pax_global_header00006660000000000000000000000064117636320170014520gustar00rootroot0000000000000052 comment=5bb1665fe4de257b527b5fb8527ed4180f34836e .gitignore000066400000000000000000000003511176363201700130530ustar00rootroot00000000000000nbproject .directory *.fam # emacs .#* # vi *.swp *~ # Build directories build build_* bld bin lib # OS X .DS_Store # CMake-generated Makefiles Makefile *.cmake CMakeFiles CMakeCache.txt install_manifest.txt /nbproject/private/ AUTHORS000066400000000000000000000001651176363201700121360ustar00rootroot00000000000000The master mind behind Falcon: Giancarlo Niccolai The contributors: Please refer to the Git logs. BUILDING000066400000000000000000000213001176363201700122000ustar00rootroot00000000000000 The Falcon Programming Language 0.9.6.8 BUILDING INSTRUCTION INTRODUCTION ============ This document refers to building Falcon source package as it distributed in bleeding edge and official releases. Starting from 9.6.6.7, the package can be configured, built and tested directly from an "unguarded prompt". It's not necessary to use script or special configuration settings to develop Falcon, even in case that there is a different development-enabled version of Falcon on the target system. PRE-REQUISITE ============= To build Falcon, it is necessary to have installed a recent version of a c/c++ compiler supporting your platform and cmake, available at http://www.cmake.org Other than that, it is optionally required to provide development files correctly installed and configured for the following well known libraries: - ZLIB - PCRE THE BUILD PROCESS ================= The build process happens under a standard CMAKE build session. Most of the times, mkdir build cd build cmake .. ccmake . (*) make install make is enough to create the core Falcon installation. The line indicated with a (*) is a manual on-screen configuration of the falcon build elements. The ccmake command will open up a menu through which you can cherry-pick the components to be built and installed. Many complex components, as SDL and DBI require a finetune configuration after their switch on. To do that, first perform a configuration (use the 'c' key) and then change the new settings that appear. Relevant variables affecting the outcome of the build and installation processes are: - CMAKE_INSTALL_PREFIX: where Falcon will be placed on target systems. It is very relevant in POSIX systems. Defaults to "/usr/local". - FALCON_BIN_DIR: Directory under prefix for binary installation. Defaults to "bin". - FALCON_INC_DIR: Where to store include files. Defaults to "include/falcon" - FALCON_LIB_DIR: Where to store library files. Defaults to "lib". - FALCON_MOD_DIR: Default location for installation modules. Defaults to "$FALCON_LIB_DIR/falcon" on POSIX system, and "bin" on others. - FALCON_WITH_MANPAGES: Creates and install also manual pages for POSIX man command. It's "ON" by default on POSIX systems, "OFF" on others. - FALCON_MAN_DIR: Where to install man pages. Defaults to "share/man/man1". - FALCON_CMAKE_DIR: Where to install cmake configuration settings that can be used to create third party Falcon modules or application embedding falcon. - FALCON_SHARE_DIR: Where README and other distribution related documentation files are to be placed. The "make install" step can be directed to place Falcon in a temporary empty location where it will be picked for separate packaging via the default "DESTDIR=" setting: cmake -DCMAKE_INSTALL_PREFIX="/opt/falcon" make install DESTDIR="/tmp/falcon" This will move the needed files in /tmp/falcon, but will internally configure falcon to run from /opt/falcon/bin *NOTE: You'll usually want to ship your Falcon version built with -DCMAKE_BUILD_TYPE=Release IDE Hints ========= If using an IDE to create Falcon, like Xcode or Visual Studio, you may want to use cmake install script "cmake_install.cmake" which is located in the root of the build directory. The script usually installs the "Release" target but you can tweak it by setting the script like the following: $ echo 'set(CMAKE_INSTALL_CONFIG_NAME "Debug" )' >> debug_install.cmake $ cat cmake_install.cmake >> debug_install.cmake $ DESTDIR=/tmp/falcon cmake -P debug_install.cmake To be able to run the install target directly from the IDE, you'll need to tweak some manual settings in the project files; keep in mind that CMAKE will destroy those project files when a setting is changed, and thus CMAKE is re-run, so you'll need to re-enter this setting manually after each re-configuration. On XCODE, just add a "DESTDIR" User-Defined setting in the "build" tab of the global project settings. On Eclipse, set the DESTDIR variable as an environment setting under the build/make project category. On Visual Studio, there isn't any specific setting, but you can easily set the DESTDIR varible in the environment prior loading the visual studio from the command line, like in the following example: set DESTDIR=c:\tmp\install\falcon vcexpress (or other command to start the visual studio) A similar approach can be applied to Eclipse, while it's more problematic with XCode. Running from a temporary directory ================================== Falcon just requires its FALCON_LOAD_PATH to be properly set to the location where modules are to be found. Also, on POSIX systems, the dynamic library loader path should be directed to load the falcon engine library file from the temporary library installation path. Finally, it's advisable to add the location where the binary falcon interpreter is to be found in front of the system PATH. All this operations are performed by a script named "falconenv.sh" or "falconenv.bat" found in devtools/ directory; just pass the temporary installation directory name to the script as a parameter and it will set the required variables accordingly. On POSIX systems remember to include the script instead of just running it, using the command: source devtools/falconenv.sh path/to/temp/install/dir Changing library directory name =============================== In some systems, common system libraries for different architectures are stored on different directories. For example, one may want to store the 64 bit version of Falcon engine in lib64. Internal libraries ================== By default, Falcon feathers build process tries to find in the system a build support for PCRE and ZLIB, falling back to build internally shipped versions if they are not found: - PCRE 7.2 - ZLIB 1.1 To change this behavior, use the following options -DFALCON_WITH_INTERNAL_PCRE=ON (to build internal PCRE) -DFALCON_WITH_INTERNAL_PCRE=OFF (to force external PCRE) -DFALCON_WITH_INTERNAL_ZLIB=ON (to build internal ZLIB) -DFALCON_WITH_INTERNAL_ZLIB=OFF (to force external ZLIB) Support for EDITLINE in interactive mode (POSIX) ================================================ The falcon command line tool can take benefit of an installed editline library, or use the internally distributed editline in case editline is not available. Currently, both those library cannot interpret international characters correctly, so they are turned off by default. To compile falcon with Editline support use: -DFALCON_WITH_EDITLINE=ON If you want to use a system-wide installation of editline, instead of the one that is shipped with Falcon, use the following option: -DFALCON_WITH_INTERNAL_EDITLINE=OFF Notice that, in this case, Falcon build system will detect if you have a system-wide installation of the edit-line library, and use that instead. Notice that WIN32 console has a built-in interface that allows to edit the commands inputed in the interactive mode. RUNNING TEST BUILDS - POSIX =========================== On POSIX systems, the script bin/falconenv.sh configures the environment so that Falcon is found on a temporary install location instead of being loaded from the standard system installation location. For example, after having installed a Falcon build configured to be installed on /usr/local and placed in /tmp/falcon, through the following commands: $ cmake -DCMAKE_INSTALL_PREFIX=/usr/local $ DESTDIR=/tmp/falcon make install it is possible to launch the following command to run that version of Falcon and have it accessing (exclusively) the modules installed with it: $ falconenv.sh The command may be any binary file in the Falcon /bin installation directory or any valid shell (i.e. /bin/bash) that will be run with the variables set to start falcon from the temporary test install directory. The script actually sets the following variables: - PATH where to find the falcon executables. - LD_LIBRARY_PATH (or equivalent) on POSIX to find the engine library. - FALCON_LOAD_PATH to the place where falcon modules are installed. * NOTE: The sole purpose of this script is that of being able to test-run Falcon during development or before a complete installation. Packagers may wish to delete it before finally packing Falcon for distribution. RUNNING TEST BUILDS - MS-WINDOWS ================================ On MS-Windows system, it's enough to run the following commands: C:\> set FALCON_LOAD_PATH=";." C:\> set PATH="%FALCON_LOAD_PATH%;%PATH%" CMakeLists.txt000066400000000000000000000116411176363201700136270ustar00rootroot00000000000000############################################################################# # The Falcon Programming language # # CMake configuration file for Core falcon ############################################################################## # ## Control Variables # # FALCON_BIN_DIR - Prefix for binary installation. Defaults to "bin" # FALCON_INC_DIR - Prefix for installation of include files. # FALCON_LIB_DIR - Prefix for installation of archives and dynamic libraries # on UNIXes. Defaults to "lib". # FALCON_MOD_DIR - Prefix for installation of modules. Defaults to # $FALCON_LIB_DIR/falcon on UNICES, and bin/ on windows. # FALCON_MAN_DIR - Where manual pages are stored (below prefix) # FALCON_SHARE_DIR - Where to install falcon share files # FALCON_CMAKE_DIR - Where to install FalconConfig.cmake & c # ## Options # # FALCON_SKIP_BISON - Turn ON to avoid using bison. # FALCON_WITH_MANPAGES - Turn ON to build and install man pages. # FALCON_BUILD_FEATHERS - Turn OFF to skip building Feathers. # FALCON_BUILD_MODULES - Turn OFF to skip building Falcon modules. # FALCON_BUILD_NATMODS - Turn OFF to skip building other binary native modules. # FALCON_BUILD_APPS - Turn ON to install installing Falcon applications. # FALCON_BUILD_FWKS - Turn OFF to skip installing Falcon frameworks. # FALCON_BUILD_DOCS - Build automatic documentation # ## Options For Native Modules # # FALCON_BUILD_*MODULE_NAME* - ON to build a given module # # List of available modules: CURL DBI DBUS DYNLIB GD2 GTK PDF SDL # cmake_minimum_required(VERSION 2.6.2) project(Falcon) ################################################################## # Falcon build environment setup set(Falcon_IN_CORE_SOURCETREE on) # Find the module for cmake that we generate before install set( Falcon_DIR "${CMAKE_CURRENT_BINARY_DIR}/devtools" ) #set( faldoc_DIR "${CMAKE_CURRENT_BINARY_DIR}/apps/faldoc") # Add source include files at those generated by the module list(APPEND Falcon_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/modules/native/feathers/include" ) include(detail.cmake) ################################################################## # CMAKE environment setup list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) #########################################################à # List of files to be installed as docs # set( doc_files AUTHORS BUILDING ChangeLog copyright LICENSE README README.editline README.mersenne TODO LICENSE_GPLv2 RELNOTES ) install( FILES ${doc_files} DESTINATION ${FALCON_SHARE_DIR} ) #########################################################à # Subdirectories # #collect the include files in FALCON_HEADER add_subdirectory(include) # add_subdirectory(engine) add_subdirectory(clt) # add_subdirectory(devtools) add_custom_target(falcon-core) ######################################################################### # Feathers? # if( FALCON_BUILD_FEATHERS ) message( STATUS "Adding FEATHERS to this build" ) #override settings for the include dirs list( APPEND Falcon_INCLUDE_DIRS "${CMAKE_CURRENT_BINARY_DIR}/include" ) add_subdirectory( modules/native/feathers ) else() message( STATUS "Not building feathers" ) endif() ######################################################################### # Modules? # if( FALCON_BUILD_NATMODS ) message( STATUS "Adding native binary modules to this build" ) add_subdirectory( modules/native ) else() message( STATUS "Not building binary native modules" ) endif() if( FALCON_BUILD_MODULES ) message( STATUS "Adding Falcon modules to this build" ) add_subdirectory( modules/falcon ) else() message( STATUS "Not building modules" ) endif() if( FALCON_BUILD_FWKS ) message( STATUS "Adding frameworks to this build" ) add_subdirectory( frameworks ) else() message( STATUS "Not building frameworks" ) endif() if( FALCON_BUILD_APPS ) message( STATUS "Adding applications to this build" ) add_subdirectory( apps ) else() message( STATUS "Not building applications" ) endif() if( FALCON_BUILD_DOCS ) message( STATUS "Adding documentation to this build" ) add_subdirectory( docs ) else() message( STATUS "Not building documentation" ) endif() #Generate make distro scripts if( FALCON_BUILD_DIST ) add_subdirectory( dist ) endif() if( FALCON_INSTALL_TESTS ) message( STATUS "Adding (source) tests, samples and demos to the distribution" ) add_subdirectory( tests ) else() message( STATUS "Not installing tests" ) endif() # CMake generated information. Is used by our falcon-config.cmake if (NOT FALCON_CMAKE_DIR) if(WIN32) set(FALCON_CMAKE_DIR cmake) else() set(FALCON_CMAKE_DIR ${FALCON_SHARE_DIR}/cmake) endif() endif() install(EXPORT falcon-core-targets DESTINATION ${FALCON_CMAKE_DIR} ) CTestConfig.cmake000066400000000000000000000003361176363201700142400ustar00rootroot00000000000000set(CTEST_PROJECT_NAME "falcon-core") set(CTEST_DROP_METHOD "http") set(CTEST_DROP_SITE "maikbeckmann.dyndns.org") set(CTEST_DROP_LOCATION "/CDash/submit.php?project=${CTEST_PROJECT_NAME}") set(CTEST_DROP_SITE_CDASH true)ChangeLog000066400000000000000000001630501176363201700126430ustar00rootroot00000000000000Falcon (0.9.6.9) * fixed: dbiloader doesn't make the specific driver the vm's main module * added: typeName method for Falcon::Item * added: AccessError now reports the type of an item for which an invalid property was requested * fixed: dbi-mysql now manages non-prepareable queries. * fixed: dbi-mysql now manages stored-procedure recordsets. * fixed: fself din't get access to the current closure. * fixed: Several doc reference missing. * fixed: Fix for broken recvAll in IMAP module. * added: Nest to version 2.0 Falcon (0.9.6.7) * fixed: pow() din't reset the numeric error on startup. * fixed: FTD not correctly escaping if using CRLF as EOL. * fixed: In WOPI, missing function Uploaded.read() * fixed: Continuations had several implementation bugs. * added: Continuation callers can now send parameters to suspended continuations. * minor: Reversed startup continuation parameter list (now the call-with-continuation parameter is the first one). * fixed: Json escaping (\uNNNN) wasn't correctly generated. * minor: Added function vmModuleLine, that returns the number of the line it has been called at. * fixed: Several problems in module loading and marking caused random crashes with complex inter-module linking. * fixed: Error traceback doesn't include Error_init anymore. * fixed: Several bugs in class inheritance calculus and class equality check. * minor: Better error reporting thanks to error boxing. * fixed: load/import with files from relative paths now working. * fixed: Fixed off-by-one error in ftrim() * added: DBI - odbc module. * added: Support for encoding IBM850 (code page 850 - western latin). * added: DBI - Firebird (fbsql) module. Falcon (0.9.6.6) * fixed: Default CoreObject clone didn't try to clone the falcon-data even if it was possible to do that. * fixed: When importing from namespaces, inheritance (derivedFrom) from classes coming from namespaces wasn't correctly detected. * fixed: In interactive mode (and whenever using the interactive compiler) modules with symbols already known in the VM couldn't be loaded even if stored in their own namespace. * fixed: CoreSlot wasn't exported so it couldn't be used in modules. * fixed: "a" * 10000 taking ages because of parser error in first allocation. Thanks maxm for this. * minor: Added option "-s" in CLT to send error output on stdout, and --cgi option to load "cgi" module and add -s option in one step. * minor: strSplit() returns an array containing all the elements if the token is not given. * fixed: toString() crashed when called without a parameter. * fixed: include() didn't take the engine encodings as the default. * fixed: language level second() was not using the new system independent api Falcon::Sys::_seconds(). As a result, some embeddings using _second() may have provided mismatched timings on windows. * minor: When used with invalid parameters, falpack points out that -h will print help text. * fixed: Sys::fal_mkdir (unix) didn't handle absolute paths proper. * added: Method Dictionary.dop (default operation). * fixed: falpack wasn't correctly storing the plugins in the app subtree. * fixed: Immediate array unpack was causing an assert in case of wrong sizes. * added: Module cache acting directly in ModuleLoader. Should be reviewed for finer control in next version. * added: Methods Dictionary.do and Array.concat * fixed: passvp() was failing if calling function had local vars (!) * fixed: First trace step of errors didn't report the target file name. * fixed: import in used the "as" instead of "in" keyword. * added: Base64 class. * fixed: String.escape() enscaped only below chr(8) and not below chr(32). * added: epoch() function returning exactly the seconds since UNIX epoch. * fixed: Comparation with timestamps and other kind of items caused an endless recursion. * fixed: strSplit(":",":") should logically return an array of two empty items, but returned ":" instead. Falcon (0.9.6.4) * fixed: returning oob(1) from a filter in comp() & single sequence mfcomp didn't discard the value. * fixed: Path::uriToWin() (used to post-normalize paths on MS-Windows system) didn't translate '+' into ' ', which meant troubles with filenames including spaces on MS-Windows. * fixed: falpack used the user-provided main script path as-is, without prior normalization, causing all the scripts to be considered non-applciation (system) when the path was given in non-canonical format. * minor: Asynchronous messages are now handled more smoothly. * fixed: future binding (named parameters) "invaded" local variables in target function symbol table. * fixed: strSplitTrimmed (and FBOM.splittr) put an extra empty string at the end of the array when multiple separators was at the end of the parsed input strings. * fixed: Failing to init the frame in new VMContexts would have caused random crashes depending on linkage or memory conditions (thanks Mordae). * added: Function.trace() to get the traceback steps. * fixed: Invalid loop condition caused fself.caller() to segfault (all thanks to fgenesis). * fixed: Fordot statement (.=) couldn't be used after for/in...: (after a colon) * added: Methodic functions strEsq, strUnesq, strEscape, strUnescape (and similarly named methods in String) helping string transformation in web modules. * fixed: URI::URLEscape needed much more escaping... * fixed: Error reporting in include() may crash. * fixed: def statement crashed if declared variables was not assigned. * fixed: Directory.descend can be called with nil as directory handler function. * fixed: URI::URLDecode was a bit too strict; there's no need to filter chars under 0x20. * added: Event model to VMSlot (children named slots). * fixed: StreamBuffer may cause hangs on partial reads in net-based streams. * fixed: clone() and *comp() didn't duplicate strings as semantic would suggest. * added: Now unknown messages are optionally marshalled to "__on_event" in objects. Falcon (0.9.6.2) * fixed: Random crash on runtime errors during object init. * fixed: fileExt() returned the file part since first dot. * added: Support for OEM - IBM473 code page. * fixed: fileType() and FileStat return values were not initialized, cusing weird values in case of file not found errors. * added: First version of falpack. * fixed: Error in class symbol de-serialization caused failure of fam loading. The default fallback to .fal re-compilation hid the problem. * fixed: In c++ API, Path::getWindowsFormat went in endless loop. * added: Path.fulloc, Path.winpath, Path.winloc and Path.winfulloc properties to the Path class. * added: falpack CLT tool. * fixed: Module loader now takes into account also disk specifiers. * fixed: Path::isAbsolute wasn't considering relative paths under disk specificators. * fixed: Local symbol tables of functions may have been broken, causing string expansion and other language elements in need of dynamic access to local symbol table to break. * added: mcomp and mfcomp methods to sequences for multiple comprehension. * fixed: Comprehension support is now not atomic (and supports continuations). * fixed: Continuation.clone wasn't correctly working -- currently disabled because causes segfaults. * major: "state" keyword removed. Now, states are declared with [statename] * fixed: Asynchronous message processing was basically broken, but the breakage could be spotted only at random conditions. * fixed: Addressing issue on sparc64 caused segfault on __leave handling in state transitions. * fixed: the check on VMContext::preParam was totally bogus and may have caused to return 0 even if proper pre-parameter existed. Falcon (0.9.6) * minor: On windows, stream functions now accept paths also in windows format. This is controlled by an engine global setting that is ON by default on windows and OFF elsewhere. * fixed: Throws to manage return frames was a bit excessive. This wasted lots of time in atomic calls. * fixed: Timestamp.dayOfWeek() wasn't working correctly. * fixed: Extra error field was always set to "_init" when raised during the link process in VM. * minor: Using libedit on unix for a more handy interactive mode. * fixed: Items generated in interactive mode may have been randomly ripped by the GC. * fixed: Calling an item referencing a callable item wasn't working. * major: overrides are PREFIXED by __, and not suffixed anymore. * fixed: Deserialization of objects didn't dereference the class item; classes coming from remote modules would crash. * major: Added states in classes. Now "state" is a keyword. * minor: Added fileExt() and fileUnit() functions for completeness. * fixed: Immediate circular dependencies in inheritance lists caused endless loop in VMachine::link * fixed: Errors in the expression part of for/in statements may have caused the compiler to lose track of context levels. This caused segfaults on some statements nearby those erroneous for/in decls. * fixed: for-to construct failed to enter the loop in 0 to 0. Also it didn't loop the correct times when starting and ending in negative ranges. * fixed: EOF in ReadFile (windows streams) was not always correctly detected. * fixed: Read of multi-byte filenames on POSIX systems caused double-utf8 encoding. * major: Added Continuation class, allowing to suspend & resume arbitrary code. * major: Added unbound variables, created with the "_" symbol, as a new "UnboundType" with relative metaclass and item type. * fixed: Garbage collection during link steps (i.e. on sub-module main(), and in instance initialization) would cause a safe crash, as it would destroy the LiveModules that are in the runtime, but not yet linked. Pitifully, this fix is useless, as LiveModules are to be removed in 0.9.6. * fixed: __getIndex() accessor was broken if the expression in the accessor was a complex expression. * fixed: Macros used in per-property reflection was possibly broken under very rare conditions. * fixed: URI reflection was a mess (and slow); cleaned and simplified. * minor: Added function passvp() that can be used to retrieve or pass variable parameters. * major: Now compare() (vm level) checks for deep equality in arrays and dictionaries. The relation [1,2] < [1,3] holds. Circular reference prevention during those deep scans is in place too. * major: Added the "eq" identity operator, so that 1 eq 1 is true, as a eq a, but b = a.clone(); a eq b is false (if a is deep). * fixed: falconeer.fal relied on the old(incorrect) behavior of regex.replace * fixed: Item compare may not work correctly if applied to ints that were too distant (> int32 range). * fixed: In falcon command line tool, fixed loading of source files not terminating with .fal, and when forcing execution of fam files via the -x options. * fixed: indirection didn't work for classes so classnames and enums couldn't be used * fixed: fallc was left behind of a version or so. * fixed: Error in array generation when small grow sizes are involved in GENA opcode. * fixed: Error in array generation when small grow sizes are involved in arrayBuffer() core function. * fixed: In ItemArray::append( Item& ) the parameter is passed as a pointer. If the array data must be relocate and the item is coming from the array itself, the old memory may be gone by the time the final copy is performed. This hit particularly hard with PUSH, which puts data on the stack and may get data from local vars and parameters (both in the stack). Falcon (0.9.4.4) * fixed: CoreTables used as sequences in for/in loops didn't mark their Falcon side Table item as owner. This caused crash with some GC patterns. * fixed: The context stack was lengthened, but not resized, when calling an array. This may have caused direct crashes when calling arrays on brand new stack frames (a rare, but possible situation). * fixed: Grammar error on def statement caused crash. * added: Object.retrieve and Object.apply BOM methods to extract and apply data to objects. * major: Added property accessors to classes and objects. * fixed: Raising a class in the error hierarchy caused segfault. * fixed: Coroutines terminating with a simple return may have killed a useful value in the A register of the swapped-in new coroutine. * fixed: StringStream didn't reset eof() at seek(0). * minor: Better interface and improved features for Tokenizer class. * fixed: In tables, Table.insert at bottom didn't work correctly. * added: Methods Table.append and Table.set * minor: Now tables support [] accessors (accessing a row in current page). * added: TableError class for generic errors in table operations. * minor: Table.find() now accepts an extra default parameter. * fixed: Cleaned and clarified several error messages in table ops. * fixed: Under windows, FileStat(".") wasn't working. Falcon (0.9.4.2) * fixed: After engine shutdown, some permanent memory object was not cleaned (Thanks Klaim). * fixed: Added method Array.NM and arrayNM function to declare "never methodic" arrays, that is, arrays meant to store data and that aren't to be seen as methods even when they are in properties. * minor: Moved VMachine::lock() (GarbageLock management) from VMachine to memPool. This allow easier moving of data across VMs. Should require recompilation of just a small subset of embedding applications. * fixed: VMachine::currentLiveModule() returned a const LiveModule by error. * fixed: VMachine::setWKS wasn't properly working on singletons, due to a glitch in the code. * minor: Added a userItems() vector in LiveModule, that can be used safely by extensions and embeddings. * fixed: Stream.readLine and Stream.grabLine were not returning the last line when it was broken by an EOF. * minor: Better return behavior of Stream.readLine: now returns false at EOF when it can't read anything. * minor: Better return behavior of Stream.grabLine: now it returns an oob(0) when hitting EOF after last read; this allows to use it directly in comprehensions and for/in loops. * minor: Added a "stream" parameter to redirect dump to another stream. * fixed: Some operations on buffered streams didn't update correctly the tell() indicator. * fixed: overloading was not working when returning defaultProperty() in synthetic classes. * added: Complex class. * fixed: Search for file stats (including modules) is now case sensitive on MS-Windows, at least in the last part of the file name. Falcon (0.9.4) * fixed: ClassMethod couldn't be used as classes for new instances, and any class in a property would have generated a class method when accessed. * fixed: exit() and other termination requests are honoured in interactive compiler. * fixed: Re-enabled falconeer and updated falcon skeleton module. * added: methods bind(), unbind(), bound() and value() to LateBind metaclass. * minor: Removed VMachine::raise*Error functions. This removal has also helped to spot many buglests in various modules that consisted in calling the old functions, now actually shortcuts for "throw", before doing some cleanup. * major: VM main loop and context (coroutine) support redesigned. * fixed: Some issues in coroutine swapping and spurious wakeups are now solved indirectly by the new VMContext system. This also greatly improves context switches times. * added: Methodic function properties() to determine properties of OOP items. * fixed: GC Marking of items stored uniquely as method "self" part was broken. * fixed: Methods of methods was not correctly dereferenced into vm->self. * major: Added support annotation attributes. * major: The CoreArray is now based on an inner element derived from Sequence. This makes possible to abstract many operations (but this is not visible at language level). * major: Added comprehension method to Array and List classes (this can be used to create lists from list definitions very similarly to Python, but still being more flexible. * fixed: Some string methods renamed: String.backFind => String.rfind (wasn't working anyhow) String.backTrim => String.rtrim String.frontTrim => String.ftrim * added: Functions argv() and argd() returning respectively the vector and the dictionary of the received parameters. * fixed: Substring primitive wasn't correctly working when switching to international strings. * fixed: Better behavior of the interactive compiler, now a bit more resistant to user error. * fixed: interactive mode didn't work with wide strings and international characters. * added: Methodic function describe() that can be used as a reflective inspect() * minor: Now using describe() instead of toString() in interactive mode. * fixed: Access to static methods in classes was broken. * fixed: In ranges, step 0 now represents the "default step"; open range is indicated with a special value. * minor: Ranged access to membuf now creates membuf slices. * minor: Added function arrayCompact and method Array.compact() for finer memory usage control. * minor: .ptr() for integer dereferenced them; this seems to be unneeded, at least in the main engine; now ptr() on integers just returns the integer itself. * fixed: Representation of memory buffers on big endian machines. * fixed: Slowness on Sparc32 bit platform. * minor: Performance issue on Big endian platforms. * minor: TypeID returned by typeOf and typeId for integer items is now NumericType. * major: Closures are not anymore transparent (arrays). From the outside, they look as functions, but they record the value of local variable. * major: A more flexible definition of methods, and a more formal definition of call points and call structures allowed to fix some weirdness in the resolution of "self" with methodic arrays. * fixed: Closures can now refer to symbols in any parent, not just in the topmost parent. * minor: Closed variables in closures are now always received by reference. This allows all the closures created in a function to share and be able to modify the same variable environment. * minor: Rationalized iterators; as a result, they won't work on Strings and MemBuf, for a few releases at least. * minor: removed List.erase() and List.insert() (now done via iterators). * major: Added comprehension to dictionaries (just as other sequences, but with pair being taken as [key, value] items for addition). * major: Now, '*' operator on strings replicates them. Unicode addition is done via '%', while '/' adds its argument to the last character of the string, so that "a" / 1 => "b" * minor: Added Set() VM assisted (sequence) class. * major: Added support for generators in for/in sequences. * fixed: Fixed error reporting in faldisass. * fixed: Fixed error reporting in falrun. Falcon (0.9.2) * fixed: using more modern throw instead of the obsolete raise() functions in many points. * fixed: "find" method in strings was erroneusly named "strFind". * minor: Added brigade() ETA function. * fixed: Directory class wasn't masked; as a result, using it caused crash. As a fix, we removed DirectoryOpen function and allowed Directory constructor to work as previously done by DirectoryOpen. * added: Directory.descend (it's meant to be a reminder for a complete do() framework). * fixed: List.first() wasn't returning a fully prepared iterator. * fixed: Oob wasn't working properly on immediate strings. * fixed: isoob() was returning 0 or 1 instead or false/true. * fixed: PostMessage() wasn't forwarding errors to the main VM loop in case of asynchronous message posting. * fixed: More correct error messages in case of problems loading submodules. * added: Method wordSize() to MemBuf metaclass. * fixed: Particular errors in class declaration may have caused error detection to segfault. * fixed: Array back-mark to its table did not deep-mark its data; as a result, the table got marked with the current generation and skipped its mark processing loop as a standard object. This exposes the need to rationalize and simplify the Garbage/mark Object/gcMark interface. * fixed: LiveModule::detach() was nilling the original (imported) values rather than the local objects. * fixed: fself wasn't working in init blocks. * fixed: Error class couldn't be inherited from scripts. * fixed: For/in loops with negative step exited immediately when start == stop * added: Added String.startsWith, String.endsWith, strStartWith(), strEndWith() * fixed: Multiline strings in MS-Win text file format sources (CRLF) couldn't be correctly parsed. Falcon (0.9.1.8) * fixed: faltest didn't output help text when called with -h/-? * fixed: Operator overriding in blessed dictionaries had many issues. * fixed: prefix inc/dec operator overriding now returns what is returned by the overriding function (formerly, it returned the item itself). * fixed: StringStream wasn't respecting stream copy semantic (copy shell, respect underlying stream). As a result, it couldn't be used to communicate across threads. * minor: StringStream is now interlocked; it can be proficiently used as an interthread safe raw data exchange device. * fixed: Obviating the need for ";" in function declared inside dictionaries. * minor: Falcon command line doesn't require full module generation to generate the debug grammar tree module. * major: Now allowing circular references in module loads. * fixed: Unpack is now working in every situation (and generally, more efficiently). * fixed: Didn't set correctly the default .ftt extension for -y options, and caused the original file to be overwritten. * fixed: Issues on multilanguage and internationalization. * minor: Added search path to the engine and to all the items involved into compilation. Also, added vmSearchPath() script function to allow scripts to get the path as set by the application. * fixed: Format wasn't correctly parsing ":0" (zero-length variable length integer formatting). * fixed: Escapes at the beginning of multiline double-quote strings were lost. * fixed: Possible reaping of table pages by an execessive GC usage. Falcon (0.9.1.6) * fixed: In loop statement, continue looped from the top skipping the loop condition (if it was present). * fixed: Function "any" was STILL returning 0 instead of false. * major: Added method ptr() to FBOM and various metaclasses. * major: Added explicit character size scaling and c-zation of Falcon::Strings. * fixed: Repeated assert() may have crashed depending on the stack status. * fixed: Due to faulty method equality check, method unsubscription was failing. * minor: Added subscription priority parameter. * fixed: getAssert() didn't get the correct default parameter when the required slot wasn't found, and VMSlot.getAssert wasn't parsing correctly its parameters. * fixed: MessageError wasn't exported to the Core module. * minor: Added CoreSlot.name() method. * fixed: VMSlot wasn't properly setting the user data, so for/in didn't know it was a sequence. * minor: Added first() and last() methods to VMSlot to allow iterators to work. * fixed: Detach() sequence was just a draft, and didn't actually free app-side data. * fixed: Respecting ThreadParams settings in POSIX thread creation. * fixed: Dind't correctly generate InterruptedError on VM interruption requests. * fixed: Function parameters were still hard-limited to 255. Now they are limited to uint32 (2^32). * fixed: std I/O blocked on Win7 (inheritance rights on DuplicateHandle). * fixed: crash in call__ callback for functors. * fixed: static initializer in classes didn't use standard definition of const_atom. * fixed: Secured deserialization on almost-last item. * major: Added support for operator overloading in poop, and Dictionary.set() /dictSet() to be able to bypass [] overloading if needed. * fixed: Interoperability of FAM and serialization across endianity. * fixed: Module unloading is now secure. * minor: Added readURI and writeURI functions. Falcon (0.9.1.4) * fixed: As now files and modules loads are URIs, and URI decoding was strict, modules with international names could not be loaded unless the name was previously URI escaped. * minor: Now it's possible to prevent parsing (un-escaping) from URI constructor. * minor: Fixed error failed DLL load error report string in Windows. * fixed: Now correctly importing symbols from modules named with strings. Falcon (0.9.1.2) * fixed: Correct parsing of octal number (0 and 7 was out...) * fixed: Crash with statements == just numbers, and allowing interactive compiler to parse non-stand alone expressions and evaluate them. * fixed: A leftover from version 0.8.14 caused tables not to properly mark their contents on garbage collection. * fixed: More flexible int() and numeric() functions (thanks Kless). * minor: Added functions vmModuleName, vmModulePath and vmRelativePath to help solving dynamic dependencies. * fixed: [DANGER]: Fixed missing GC marking of items in frame stacks (mainly self and binding registers). This was in since 0.8.0; it was less evident because 0.8 had sender which cached the last minus one self objects, saving at least the previous caller, and it was unlikely that the nth-2 caller wasn't cached somewhere else in the VM. * fixed: include() was using a cached pointer variable to create the error message; the VM stack may have been (usually is) dirtied, so the pointer was invalidated. This could have caused random crashes and errors in reporting the failing module name. * fixed: Finetuned module loader error reporting (now it indicates the module whose dependency wasn't found). * fixed: Some functional selectors (any & c) still returned 0 on false and 1 on true. * minor: String.merge() now accepts only an array as parameters, and works as String.join() does with its parameters. * fixed: Const atoms din't include negative numbers (i.e. negative numbers couldn't be used in enum declarations). * fixed: Reporting a consistent error when aliasing different modules to the same namespace. * fxied: Now the VM correctly lets imported undefined symbols not being part of namespaces to generate a proper error. Falcon (0.9.1) * fixed: String-blocks closed with " alone on a line werent' recognized. * fixed: Octal numbers weren't properly scanned. * added: Support for binary numbers (0b..., "\B...") and octal with 0c... and numeric neuter separator "_". * fixed: (possible) race condition in VM destruction and re-creation and GC VM marking. * fixed: Long existing latent bug in Lists and iterators: if the GC destroyed the list, the iterators may still refer to it. * fixed: Some latent problem in deep marking of object/classes. * major: Now lazy unload of modules on-the-fly is supported (some wip) * fixed: Missing rethrow at frame return (errors inside second level callItem()). * fixed: Error reporting from inner compilations (include etc). * fixed: Line number of errors in FTDs. * fixed: Stream::readString exchanged a success for an error... * fixed: Syntax errors in global statements caused the compiler to crash. * fixed: Indirect operator didn't methodize callable properties. * fixed: Buffered streams returned an error on EOF instead of 0. * fixed: Fixed date "%m" field in to-string. * fixed: Small memory leak in compiler re-initialization. * fixed: Loading of deep paths (due to 0.9) * fixed: Some short-to-wide chars transformation code on buffered strings (latent from 0.8). * fixed: Path class was working a bit randomly. * fixed: FileStat class wasn't correctly caching the mtime member. * major: Added detection of statements without effect (or with partial effect) at compile time; useful to detect "str1"\n"str2" left from 0.8.x * fixed: Errors in "loop" definitions may have caused a segfault. * fixed: Seek from current position wasn't working in buffered streams. * fixed: Better to always fill the error backtrace, for now. * fixed: Ops, totally forgot to add the -L load path in falcon exe. * fixed: Re-enabled copy-on-assign on strings; too dangerous without. * fixed: As Falcon didn't destroy the VM at end, items where not propery finalized. * fixed: Rare crash on static string live module owner marking. This poses a question on validity of current marking algorithm for live string (to be checked out). * fixed: Stream.readAvaliable() may have reported spurious false positive on UNIces. * fixed: Callback frame termination handler was not set if there was just one frame the stack. * major: It is now possible to launch symbols as coroutines without a FORK opcode. * fixed: Since 0.8.0, VMachine::callReturn restored caller exit value in case of context termination. This caused spurious wakeups and signal consumptions in coroutine semaphores, as the return value of the wait() function must be mangled from inside the sleeping context, and may be applied when a context dies. As 0.9 uses small coroutines (dying often) to send messages, the effect was dramatically visible. Falcon (0.9) * major: Removed "sender" from grammar. * fixed: Array bindings was not correctly accounted by the Garbage Collector. * minor: Methods can now be called directly also on numbers. * minor: Removed VMachine::compareItems; using inbound item comparators. * minor: Fallc now saves in UTF-8 in merge mode. * major: Removed "pass" and "pass/in" statements. * minor: Removed "itemCopy()" function (changed into clone() methodic FBOM). * minor: Renamed FBom method into typeId (type is too useful as property name for users). * minor: Revamped and methodized strFront and strBack (String.front()/String.back()). * major: Removed attributes. * major: Added class VMSlot, broadcast, consume, assert, retract, register and unregister message oriented functions. * major: Revamped Engine Message and Error system; now, they are centralized in eng_messages.h, where new generic messages and errors can be simply added. * minor: The engine string table is now expoerted as for any module and can be internationalized. * fixed: String Copy constructor didn't set export to false. * minor: -L option in falcon and falrun excluded automatically the system default path. * major: VFS in loading modules implemented. * major: Removed the obsolete ErrorHandler mechanism and using C++ exceptions. * minor: MemBuf now provides an interface with Java NIO Buffer semantics. * minor: Removed factory function FileReadStats(); now the constructor in FileStats() does everything. * minor: Method FileStat.readStats is renamed as FileStat.read * minor: Changed the order of parameters in RTL serialize() function (first the item, then the stream). * minor: Swapped wildcard and string parameters in strWilcardMatch. * minor: Added String.join method. * major: Methodized all Item API related functions. * minor: Removed now useless functions arrayCopy, arrayFilter and arrayMap. * minor: Removed indirect function (call, callMethod, marshal*), as they are now easily available through eval() and languge operators. * minor: Removed now useless function dictInsert(). * minor: Added methodic function strFill() / String.fill(). * minor: Added methodic function arrayFill() / Array.fill(). * minor: Added methodic function dictFill() / Dictionary.fill(). * major: Standard streams and local filesystem VFS providers are now serving buffered streams. * minor: Added getBuffering()/setBuffering() methods to Stream. * fixed: Transcoding control (it is now possible to change transcoders more than once). * minor: Added grab(), grabLine() and grabText() methods to streams. The older functions cannot return newly allocated buffers anymore. * minor: read/write functions on streams now can take advantage of NIO MemBuf interface. * fixed: abs() was mapped to fract(!) * major: Assignment does not causes strings to be automatically copied anymore. Automatic copy is performed on modify (if the string is static), and in deep copies of containers. Modify of non-static string doesn't cause the strings to be copied (explicit .clone() is required). So, a = "...", b = a, b and a will be disconnected when one is modified, while a = strMerge(...), b = a, b and a would reflect each other changes. This is a bit hybrid, but consequences are hardly visible. The point is that if you want a mutable string to be different, use an explicit .clone(), and if you want just read-only strings use "...". * major: New GC support: Added singleton GC with static members usedMem, aliveMem, items, th_normal th_active and with static functions enable() and perform(), reflecting the new engine wide GC. The old functions gcEnable, gcSetThreshold, gcPerform, gcSetTimeout, gcGetParams are removed. * major: Introduced parametric evaluation, and oob(0/1) now works at VMachine::functionalEval() level. Parametric eval is done by setting &N where N 1..parmCount() to the nth parameter. * minor: Removed xtimes() and empowered times(). Also, times() is now methodic for Integers and Ranges. Times passes the loop number via parametric evaluation (&1). * minor: Added ETA methods upto() and downto() for Integer metaclass. * major: Added { param => body } fast nameless function declaration. * major: Binary module logical names are resolved adding _fm to the searched module name. In example, "load module" searches for module_fm.dll. Fam/fal/ftd file resolution is unchanged. * major: The loop statement now supports a conditional ending. The end keyword of the loop statement block can be followed by an expression; the loop terminates when the condition is true (same as repeat/until). * fixed: Naming two times a closed variable in closures caused the parameter resolution order to be messed up. * major: Removed 'lambda' keyword. Use {=>} instead. * major: Added 'fself' keyword referring to the current symbol being executed. Won't work with methods for now. * minor: Added 'getProperty' and 'setProperty' methods to deep items meta-classes. * minor: Added 'name' and 'caller' methods to function metaclass. * fixed: Fixed crash when inserting just single constants in interactive compiler. * major: Removed dot-quoted strings (.") * major: Removed automatic joining of adjiacent double quoted string. Now, "a" "b" are two separated tokens. * minor: Added compiletime parsing of + and * operators on strings. This means that it is possible to join strings at compile time via "a" + "b" * minor: Binary operators can accept an optional EOL after them. A line can terminate with a + and the expression can continue on the next line without a \ to join them. * major: Literal strings now never escape with a \. "\" is always considered just a \; to add a single quote in a literal (single quoted) string, write a single quote twice (''). * major: Multiline strings are now initiated with a single open " or ' followed by an EOL. They terminate to the matching " or ', and the context coninues to the end of the terminator line. Double-quoted strings are non-literal, and compress new line and leading whitespace characters into a single space, while single quoted strings are literal, and insert into the string any space and line break in the source. This means that double quoted strings can be used to break a long string across multiple lines and arbitrarily indented, while single quoted strings define a verbatim block. Falcon (0.8.14) * fixed: Now asObject() raises an error if the item is not an object; this grant safety for methods that are not considered static. * fixed: Pow function (possibly to be deleted) was taking the first argument twice. * fixed: transcodeTo/transcodeFrom was actually not published correctly by the core module. * fixed: Expressions in "from" clauses (init expressions) may not resolve correctly symbols if the body of the object or class was empty. This caused assert and crash if generating expressions with symbols during "from" clauses of otherwise bodyless objects and classes. * fixed: Relevant warnings in array LDV/range (check order priority). * fixed: Relevant warnings symbol table generation (check order priority). * fixed: Generating a range from bogus values didn't raise a type error. * fixed: Import from module used "=" instead of "as" (leftover). * added: Reintroduced for/to grammar, (step now substituted by "," ), actually a candy grammar for for/in. * added: OOB opcode which manages out of band items. * added: OOB operators: ^+ (oob), ^- (deoob), ^? (isoob), ^! (invert oob status) * minor: Removed function inspectShort() and secured/empowered inspect(). * minor: Added parameter defItem to arrayBuffer to "replicate items in arrays". * fixed: Range change in arrays was taking one item too much. * fixed: Exotic range applications (as substitutions in reverse ranges with negative indices) wasn't working as expected. Part of it was due to missing or approximative definition, part to plain errors. Now the semantic of ranged access is defined and enforced through all the operators and RTL functions. * minor: VMachine::launch() is now necessary to start executing the main script even if launchAtLink() is true at link time. In other words, launchAtLink() controls only the execution of the main symbol in the non-main scripts. * minor: falrun now returns 255 on vm error. Falcon (0.8.12) * major: Support for class-driven full reflection and 0-space contstant/reflected classes. * major: Changes to FileStat interface: - FileStat.type becomes FileStat.ftype - FILE_TYPE_* constants are now static properties of FileStat. * major: Added support for module dynamic symbol creation on script request. * major: Added import [sym1, sym2, ... symN] from Module grammar. * fixed: Assigments shouldn't define symbols accessed or called. * fixed: continue dropping wasn't working for last elements in dictionaries. * minor: Now it is possible to alter static properties by their class name. * fixed: Some type error in VM should have been catchable, but was raised hard. * fixed: The compiler didn't always add module strings as symbol names. * fixed: Broadcast may have broadcast double in case of a single receiver not returning true. * minor: Added attributeByName function. * major: RTL module is removed (merged with core). * major: Now main code of modules is always executed. Added vmIsMain() function in core to be able to check if the current module is meant as main module or not. * minor: Added testAttribute() function to test for attributes. * major: Added interactive mode to Falcon command line tool. * minor: Option -D (set directive) has been moved to -d; -D sets compile time constants. * major: Macro and Meta compilation added. The escapes \[ and \] perform meta compilation, and the macro keyword and \\ escapes are candy grammar for macro compilation. * major: Added support for late binding. * major: Added local/late binding in functional constructs. * major: Added dynamic symbol resolution for arrays. * bufix: deadlocks at coroutine end when some context was waiting endless was not detected. * minor: added ETA functions let (assign) and lbind (make late binding). * minor: ',' can now be optionally used in .[] decl. * fixed: dolist wasn't breaking on oob(nil), but on just false. * major: Forward binding implemented. Currently used to resolve named parameter function calls. * minor: include() function added to Core module allowing dynamic module loading directly from base programs. * minor: added vmFalconPath() informative function. * major: Addition and self addition on strings now forces conversion of the target object into a string. I.e. "H" + 1 => "H1". Integer UNICODE operations are now delegated to the "*" and "*=" operators. So, "Hello" * 32 + "World" => "Hello World" and "H" *= 65 => "HA". * fixed: URI.encode() was actually decoding. * minor: Introduced xtimes() function which works like times(), but evaluates all the items in sequence instead of just running them. * major: Added TABULAR PROGRAMMING: - Added Item type "TabMethod" to store methods for arrays/tables - Self item is now propagated to functions called inside arrays. - Table column can now be used as properties of arrays inside the tables. * fixed: Assignments to properties and vector didn't propagate the assignand value. * fixed: Fixed compilation on Darwin. * fixed: Fixed missed mark of some deep item during GC (may have caused random crashes). * major: Added the concept of "blessed dictionary" that is seen as an instance. * minor: Much more sensible policy of EOL removal in FTD escapes. * minor: Added function valof returning the value of a variable or the return value of a callable item. * major: Added import aliasing. * major: Inheritance initialization parameter can now be any expression. * fixed: Disregarding old bytecode .fam files didn't work correctly. * minor: Added the eval unary prefix operator, first of cap-special operators: "^*" works a bit like eval() function, but much more efficient, expecially in the fast path. Falcon (0.8.10) * minor: Now classes can be stored in object properties and called to get an instance. * bugfix: Indirect operator wasn't peeking the global table after having failed in searching the local table. * bugfix: When a "module provider" was given to runtime, test for already existing modules was always positive. * bugfix: fixed ftd line count, export/load directives and reenabled \n after a "?>" * minor: Added numeric() function to core * major: Removed forall and added formiddle which runs after every element except the last one (even if "forlast" isn't provided). * minor: dirMake() has now an extra parameter; if provided and true, the function works as mkdir -p; creates the whole heirarcy, and succeeds also if the directory is already present. * major: added boolean type. Now, true/false are special compilator tokens which generate a boolean item. Every VM check results in a boolean item being returned. * bugfix: Falcon binary didn't return the VM exit code to the shell. * minor: Load now supports ".module" notation to load sibling modules. * major: Added import directive for explicit symbol import. * bugfix: Fixed deep user data GC marking in methods and classmethods. * bugfix: Parameters in FBOM function calls was not removed from stack. * minor: It is now possible to call functions stored in classes; class functions not referencing "self" can be safely called this way, which makes them "static". * bugfix: Timestamp.distance() was returning a random value instead of nil. * Minor: Added Path reflexive class to RTL. * Minor: Added URI reflexive class to RTL. * Major: Added MemBuf type and relative support. * Minor: Added functions strToMemBuf and strFromMemBuf in RTL * Minor: Added MemBuf support for Stream.read(), Stream.write(), transcodeTo() and transcodeFrom(). * Minor: Added start parameter to Stream.write() * Bugfix: size parameter was not correctly used in Stream.write() * Minor: Added internal SharedUserData class which can be used for data shared among many objects. * Minor: Added inspectShort() function that provides a more compact inspect(). * Bugfix: Switches couldn't accept negative numbers. * Major: Now "=" always assigns, and "==" is used for comparisons. * Major: Remove assignment statement(s). All assignments are now expressions. * Major: Anonymous function declaration is now an expression. * Major: Anything may be place left of an assignment. * Major: Removed for/to loop and added a step parameter to ranges. For/in loop on steps have now the same functionalities as the old for/to loop. "step" is not anymore a reserved word. * Major: Now nameless functions and lambda are "closures". * Minor: Added statement "innerfunc" creating a non-closure function. * Minor: Added "enum" statement which can create a set of constants. * Minor: Statement give/to can now give attributes to (or remove them from) a list of objects. * Minor: Better management of INC/DEC; added INCP/DECP opcodes to allow faster inc/dec. * Bugfix: Error.description wasn't correctly reflected. * Minor: Added method Error.heading() to access the heading of an error without the stack trace. * Major: VM can now be interrupted in compliant waiting operations. ATM, stream and socket readAvailable & writeAvailable have been instrumented. Interruption will raise an InterruptedError, can be catched and operations can be resumed. * Minor: RangeError renamed into AccessError. * Major: Added broadcast replies (returning from broadcast with an OOB item). * Minor: Added function vmSystemType() to access underlying target compilation system. * Minor: Aliased the function paramNumber() to parameter(). The function paramNumber() is still available but deprecated. * Minor: Added first and last BOM methods. * Minor: Added RTL function randomDice * Minor: Added Stream.flush method in RTL (ops...) * Minor: Added RFC2822 date management. * Minor: Fixed unspotted bug in "-c" option for falcon. * Major: PCODE version moved to 2.0 * Major: Added register Latch and Latcher in VM, working for LDP/LDV * Minor: Automatically disregarding outdated .fam when trying to load source modules. * Minor: Added "times" functional construct (ETA function). It's a kind of functional for/in loop. * Minor: Added pluggable support for multithreading locking scheme and atomic operation providers (rudimental, but working). * Minor: Added directive "version"=number (0x[VVV]VMMmm) and the function vmModuleVersionInfo() to store and access script and .fam version informations. * Bugfix: fixed traceback corruption in error reporting when uncatching-retrhowing from a in-between try-frame. * Major: Added support for 0 overhead string internationalization. * Major: Binary operators grammar changed. Binary and is now &&, binary or is || and binary xor is ^^; Auto assigment stays &=, |= and ^=. Binary not is now ~. (This is because we need some of those symbols for other things in the next releases). * Bugfix: strFind and strBackFind didn't work correctly and may crash when receiving wrong values. * Major: Provided FAL_MODSTR macro (family) to create module specific string tables. Falcon (0.8.8) * bugfix: TRAV opcode may destroy its parameters while setting first loop on rare conditions. Fixed. * major: VM now has a map of modules and global variables instead of two parallel arrays. This allows arbitrary module unlinking. * major: Item size is now 16 bytes. This allow space for stepped ranges. * major: Added WKS/WKI (Well Known Symbols/Items) system to obviate the need to share global items with scripts and create failsafe instances of classes. * bugfix: Fixed TimeStamp distance algorithm. * minor: List initialization is now possible also with non-atomic symbols. * bugfix: Precision in printing default flotaing point number was 6; now is 16 (maximum precision for our number format). * minor: Added strWildcardMatch in RTL for simle *.*-like wildcard matching. * minor: Added ETA function firstOf() that retruns the first non-false parameter. * bugfix: When having more than one context, each stack had a different treshold, possibly causing some of the stacks to crash on realloc. * bugfix: Path of the offending module was not shown when reporting compiler errors. * major: Load directive can now handle dot modules "a.b" * bugfix: ModLoader::addSearchPath didn't locally bufferize the path, and this may have caused problems with incoming fixed strings. * minor: Added fileCopy() function in rtl (file_ext). Falcon (0.8.6) * bugfix: incorrect format of 0.x floating point numbers. * major: Completed attribute model. * major: Improved functinal support. Added lit() and choice(). * bugfix: Fixed initial search path for faltest. * bugfix: Fixed crash in moduleloader with empty paths. * major: Added message oriented programming model. * major: Flat VM model implemented. * minor: Added round, ceil, floor, fint and abs math functions. * minor: Fixed default representation of floating point numbers, and recogninzing long integer NAN. * minor: int() core function now raises a range error in case the given fp number cannot be represented in an int64. * minor: loop statement now accepts a statement on the same line for candy grammar. * minor: added arrayHead function in rtl. * minor: strHead, strHeadTrim, strTail, strTailFind have been renamed respectively to strFront, strFrontTrim, strBack, strBackFind for coherency with nomenclature. * major: Added List class with support in for/in and CORE iterator. * major: Added support for generic sequence object and deep GC marking. * minor: Added addSingleton method to Module class for easier embedding. * minor: Added marshallCB* series to dispatch events to handlers in objects. * bugfix: fixed self/sender sequence for callable arrays used as methods. * bugfix: array and property assignment now copies strings by value. * bugfix: One line for/in statement COLON rule caused crash. * minor: Changed "<" fast print into ">>". * bugfix: In case of callitem() from toplevel, errors were not managed correctly. * bugfix: Generic array resize (to smaller size) was wrong, and caused VM stack corruption when big arrays and recursions were performed. * minor: the "_" prefix for symbols is now used for private usage in classes. * bugfix: Lexer didn't recognize unclosed contexts at end of file. * major: Added dot-square array declaration, and better management of contexts /eol in array/list declarations. * major: Added compiler directive support. Now "def" wokrs on directive. Falcon (0.8.4) * major: Old "lambda expressionss" are now "nameless functions". They are declared through variable = function(...) ... end (which was the most common pattern for the old usage of lambda expressions * major: Lambda expression grammar is much simplified, and they allow only one expression as body. * major: Added .ftd (falcon template document) file type. It's a outer-escaped document (php/asp like) where unescaped literal code and escaped code can be seamlessly merged. * bugfix: Linear dictionary didn't break search loop in case of VM error. * bugfix: VM cleared register A when comparing items. This caused weird errors in dictionaries when the objects overloaded compare(). * bugfix: Fixed try/catch sequences across internal frames * improvement: ^^^this also allowed to remove the ugly trypos structure. * bugfix: PSIN opcode extracted an unexisting second parameter; in case of dirty data that caused an assert. * design: Fixed compilation and build settings with MINGW. * bugfix: Fixed default load path of faltest. * improvement: Added a user data pointer to VM (useful for binary compatibility) * minor: Unclosed scoping errors now report where the scope was open. * improvement: added min, max, all, any, allp, anyp, map, xmap, filter, reduce, iff, cascade functions for minimal functional support. * major: callable arrays for deferred calls and functional support. * bugfix: AutoCString and AutoWString may convert incrrectly due to sign mismsatch in the return value of String::toCString()/String::toWString(). Fixed (and returing uint32). * minor: Semaphore wait() can now be timed. Falcon (0.8.3) * VM optimizations of about 20-25% * VM Stack can now automatically shrink on need * Item size reduced to 12 bytes * GENR opcode didn't work correctly when operands were float. * Added lenght recording to autocstring and autowstring * Updated REGEX to PCRE 7.2 * Added compare() and version() methods for Regex class. * in Error, added a method to stringize only the error header. * Fixed ROStream destruction sequence. * Fixed GetSystemEncoding (caused falcon cmdline to fail recognize default encodings) * Added ideographic space to whitespaces and ideographic quotes to string quotes. * Fixed some docs. Falcon (0.8.2) * String::toCString now returns immediately if space is too short. * String::toWString now returns size of converted string or -1 on error. * AutoCString and AutoWString classes were not correctly exported in Windows. * Fassert.h didn't compile under MINGW. * Now switch on objects working in every case (including compare() overload) * Now operators - and -= remove element(s) from arrays and dictionaries * More rational and elegant class interface for garbage collection objects. * More rational and elegant string classes constructors. * Cleaner class interface for module loader search path specification. * Added load module and file interface directly from Runtime * END opcode now nils the A register of the VM if quitting. * randomSeed() in RTL now randomizes on timer if called without parameters. * Added VMachine::gcLock()/gcUnlock() to allow simpler registration of callbacks. Falcon (0.8.1) * Fixed bug in VM allocation of temporary vardef that caused crash when using special memory allocator. * Added option -M to faltest to check for memory leaks * Added option -C to falcon to check for memory leaks * Removed memory leaks in some VM oprations, in maps, in compiler tree destruction, module and symbol destruction and many more. * Fixed timestamp method compare() that didn't allow for comparation against other types. * TimeStamp.toString() created a string too wide by one. * Empty loops always generate code. Optimization is to be performed by the (still unwritten) optimizer. * stdOut() &c now return a system-transcoded stream. To obtain a raw stream, use stdOutRaw() etc. * Regex.replaceAll() was broken in case of multiple submatches. * ProcessHandler last error was not zeroed in creation, causing random false negatives (process created ok, but reported error). * samples/procLoad.fal updated with correct Error class structure * Updated docs. * Added BOM serialization and item level cloning. * Added cloning support for streams. * Added cloning support for timestamps. * Items containing objects and classe were not evaluated as true; fixed * Added AutoCString and AutoWString class helpers for eaiser conversion of falcon items and strings to POD C data. * The Falcon::core::sleep function was correctly resolved when the application linked against Falcon .so in linux, but when it was loaded indirectly, in example, as a cascade load some app's plugin using falcon, it was resolved to the system sleep() instead!!!! --- so it has been renamed to Falcon::core::_f_sleep(). * Fixed VM stack push (GenericVector couldn't push to themselves). * Fixed VM context rotation for coroutines managed outside plugins. Falcon (0.8) * Prepared complete Debian packaging and fixed build issues. * Starting massive development. -- Chr. Giancarlo Niccolai 2007-04-08 LICENSE000066400000000000000000000436071176363201700121030ustar00rootroot00000000000000 Falcon Programming Language License Version 1.1, March 2008 http://www.falconpl.org/?page_id=license_1_1 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. * "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 10 of this document. * "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. * "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. * "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. * "Source" form shall mean the preferred form for making modifications, including but not limited to software source code and example code. * "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. * "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). * "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. * "Embedding Works" shall mean any work, whether in Source or Object form, that links (or binds by name) to the interface of the Work and Derivative Works. * "Applications of the Work" shall mean any work, whether in Source or Object form, that is expressed through the grammar rules which are known by the Work and that require the Work to perform its execution. * "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." * "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, prepare Embedding Works, prepare Applications of the Work, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution of Work and Derivative Works. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and 2. You must cause any modified files to carry prominent notices stating that You changed the files; and 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 4. You must state in the Source Form and in the documentation of any Derivative Work the fact that such work is a derivation of the Work, and include a copy of the Work in its Source form or provide directions on how to obtain a copy of the Work in its Source form; and 5. The Derivative Works are distributed under the terms of this License, or under terms that do not cause infringement of this License. 5. Distribution of Embedding Works and Applications of the Work. You may produce and distribute any Embedding Work or Applications of the Work thereof in any medium, in Source or Object form, provided You meet the following conditions: 1. The Embedding Works and Applications of the Work are distributed under the term of this License, or the application of another License is explicitly stated in the documentation of the Embedding Works and Applications of the Work or included in the Source form of the Embedding Works and Applications of the Work; and 2. If the Source form of Embedding Works is not distributed nor made available to the Users in any form, the Embedding Works carry a prominent notice in their documentation, or when not applicable, in any place that the Users are exposed to, about the fact that the Work is embedded, along with a general statement about the task that the Work is performing in the Embedding Works; and 3. If the Source form of Applications of the Work is not distributed nor made available to the Users in any form, the Applications of the Work carry a prominent notice in their documentation, or when not applicable, in any place that the Users are exposed to, about the fact that the Work is used by the Application of the Work. 6. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement You may have executed with Licensor regarding such Contributions. 7. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 8. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 9. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 10. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of Your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS ============================================================================== APPENDIX: How to apply the Falcon Programming Language License to your work To apply the Falcon Programming Language License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ============================================================================== APPENDIX: License commentary Dear reader, First of all, thanks for considering using The Falcon Language for Your job, or to embed it into Your application, or to use it for any other reason. The Falcon Programming Language License explains what You are allowed to do with Falcon and what You are supposed (and allowed) not to do. Since the legal jargon may be cryptic, You are provided here with a little resume of the License. It is important that You read and accept the License, as this resume hasn't any legal valence, and is provided only for the reason to express clarifications and examples that cannot find their place in the formal document. The License grants You the rights to use the Source code of Falcon and its various components in any way; You can study it, You can copy it, You can modify it, and You can even sell it, but You can't change the license under which it is distributed: even if You sell it, You have to provide the customers with the source code of Falcon and to grant them the same rights You have on it. The License also grants Your copyrights and intellectual rights for any modification or addition You may want to apply to Falcon. In case or addition and modifications, the License binds You to provide the user with the information that the original program, that is Falcon, was modified by You and what are the changes or the additions that You applied. Also, even if You can freely distribute, or even charge a fee, for Your derived work, You MUST apply the Falcon Programming Language License to Your modifications, and distribute them under the same terms. In other words, Your modifications, if made public, must be provided or available in source code. The license also grants You the right to embed Falcon in any application. Here You are granted the right to pick the terms and licenses You prefer, and to distribute Your embedding application without providing its source code. Even if significant portions of Falcon are in line functions, and even if You decide to statically link Falcon in Your application, this doesn't make it a "Derivative Work" of it: Your Embedding application is free to embed Falcon "as is" as it wish, without any requirements in terms of source distribution. You can also modify Falcon for Your specific needs and THEN embed Your modified version; the modified version of Falcon is under Falcon License, and must be made available in source code (or be proposed as a Contribution to the Falcon Committee), but Your Embedding application can still be distributed under Your preferred terms. The License has only a requirement that is demanded on Your Embedding application: in case you don't distribute your embedding application isn't made available in source code, You HAVE to state somewhere (in a place that CAN possibly be seen by the average user) that You are embedding Falcon and the reason for that. In example, You may state "ProgramX uses the Falcon Programming Language to automatize the Gamma procedure". About the scripts, which are more or less what a scripting language is for, You are granted the right to apply the license You prefer. As Falcon is also a script "compiler", You may even retain from distributing the script sources, and apply closed-source license to Your script-based application or to the scripts that are embedded in Your embedding application. However, if You don't distribute the script sources, and you use Falcon or another application covered by the same license to run them, You are again required to state somewhere in Your documentation or where the user can read it that You are using "Falcon" (or the derivative work you used), and more or less why You are doing it. For a pure Falcon application, You can just state "This application is written in Falcon". In example, if You use Falcon to drive a web site, and You don't want Your site visitors to ever see Your scripts, You have to put somewhere a reading like "Powered with Falcon". What You cannot do is to claim that any thing You learnt from Falcon is Yours: especially, You are forbidden to patent any element that is found in Falcon. Another thing that You can't do is to sell a Falcon based product as if it were "completely" Yours, forgetting to cite, even in a very small reading, the fact that Falcon is in. Finally, a thing that the License prevents You from doing is to put the blame for failures on Falcon: the product is provided as-is, without any warranty. It's up to You to test if it is suitable to solve Your problems, and if a Falcon based application can be sold and granted as "working" to Your customers. If that application breaks, whether there's a problem with Falcon or not, You can't issue any claim on the Falcon contributors. Finally, notice that this version of falcon is released under dual license. You may chose either to use this FPLLv1.1 license or the standard GNU GPLv2. GNU GPLv2 is usually shipped with Your distribution and commentaries are widely available on the Net, so we won't discuss it here. Be kind on the Open Source Community: they have already made a lot for You even if You don't know them (and even if they don't know You). Best regards, Giancarlo Niccolai LICENSE_GPLv2000066400000000000000000000431411176363201700130460ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. README000066400000000000000000000061361176363201700117520ustar00rootroot00000000000000 The Falcon Programming Language 0.9 series BUILD INSTRUCTIONS ================== WARNING: Use of SVN is now OBSOLETE. Checkout the latest source code from the Git repository from: http://git.falconpl.org Instructions on how to build the released source packages are contained in the "BUILDING" document. FALCON MINIMAL OPERATION ======================== Falcon scripts can be launched through the command falcon /path/script_name.fal parameter1 ... parameterN falcon script_module parameter1 the option -? will give some interesting help; most useful parameters, to be placed before the script name, are: -L to specify the paths where to search for modules. -p module to pump in extra modules (preload them). Repetitions of -p load other modules On UNIX systems, falcon scripts can be made executable and provide the execute instruction on the first line, like this: #! /usr/bin/falcon printl( "Hello world" ) The environment variable FALCON_LOAD_PATH can provide extra load path instruction. The priority of load paths are: - command line -L option - FALCON_LOAD_PATH - built-in directory (in config.h created from config.h.in) So it's always possible to redirect Falcon to load user-provided modules instead of system ones. OTHER NOTICES ============= Under tests/ the testsuite/ and longtests/ contains a series of scripts that are meant to be used either by faltest program or by pumping in the testsuite module. Faltest reads the directory provided with -d command line option and runs all the .fal scripts in there which provide an ID: field in the comments on top of the file. Valid IDs are numbers optionally followed by a lowercase letter. Scripts are run in numeric order, and a list of IDS separated by space can be provided to run one or more tests. Also, scripts may be categorized, and filtered by category/subcategory using -c and -C command line switches. The -l switch lists the selected scripts (or all the scripts found), and the -v switch reports verbose output errors. To know the path for a script ID, use ./faltest -d -l -v (scripts path are displayed only with -v option). Finally, -t option gathers execution time info. Other exotic/esoteric options are -m (NOT compile in memory), -s (compile via assembly), -S (do a serialization/ deserialization on the compiled module before running it, instead of using it as soon as it has been compiled). It's a good idea to launch faltest on the testsuite to verify that your environment is properly set up. The longtests/ directory has the same function as the testsuite/ directory, except for the fact that its tests are meant to run for longer times to check for stress/repetition features. The samples/ directory contains some samples and tests for those features that cannot easily be tested automatically. Those are normal falcon scripts and can be run with the "falcon" command. The three directories are a quite complete showdown of falcon language, and can be used as operating examples. README.editline000066400000000000000000000036401176363201700135430ustar00rootroot00000000000000Falcon command line interpreter uses EditLine library to assist users in command input. EditLine is not included in Falcon scripting engine. Embedding applications do not have to comply with its license. EditLine is distributed under the following BSD license: ===================================================================== Copyright (c) 1992, 1993 The Regents of the University of California. All rights reserved. This code is derived from software contributed to Berkeley by Christos Zoulas of Cornell University. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. README.mersenne000066400000000000000000000162031176363201700135610ustar00rootroot00000000000000 Falcon disclaimer for Mersenne Twister algorithm. INDRODUCTION ============ The Falcon Programming Language uses The Mersenne Twister algorithm to generate pseudo random numbers. The algorithm can be used under a BSD-LIKE license, requiring to reprint the disclaimer. Users of the Falcon Programming Language don't need to comply to this license even if embedding the Falcon Programming Language in their application, as explicitly indicated by its Author(s). LICENSE ======= Mersenne Twister random number generator -- a C++ class MTRand Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com The Mersenne Twister is an algorithm for generating random numbers. It was designed with consideration of the flaws in various other generators. The period, 2^19937-1, and the order of equidistribution, 623 dimensions, are far greater. The generator is also fast; it avoids multiplication and division, and it benefits from caches and pipelines. For more information see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html Reference M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, Copyright (C) 2000 - 2003, Richard J. Wagner All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The original code included the following notice: When you use this, send an email to: matumoto@math.keio.ac.jp with an appropriate reference to your work. It would be nice to CC: rjwagner@writeme.com and Cokus@math.washington.edu when you write. END OF LICENSE Exception ========= My name is Giancarlo Niccolai and I lead the development of the Falcon Programming Language: http://www.falconpl.org We have found the M.T. algorithm by Mr. Matsumoto and Mr. Nishimura, as modified by Mr. Wagner, to be very effective and useful for the purpose of the basic random number generator to be used in our language and provided to our users through the random() function family. However, as the code would then be part of the Falcon Scripting Engine, the BSD license may be intended as to require applications using Falcon as a scripting engine, or application written in Falcon, to add the bolireplate into their distribution. For how small this requirement may be, we cannot insert in our language code asking our users to comply particular formalities except those indicated in our license. OTOH, it's totally OK for us to comply with the BSD license; we can (and will, in case you agree) add the BSD license boilerplate with your copyright notice to our copyright statements and documentation. What I am asking you is a special exception to the BSD license to be applied to users of the Falcon programming language so that they can comply just to our license. Please, notice that Falcon is currently distributed under GPL or "The Falcon Programming Language License" (dual licensing); both those license require the final users to know what they're using and where is it coming from, and so, to have access to the original copyright statement (which will include your boilerplate) and documentation (which will indicate the source of the random number generator algorithm in the relevant sections). FYI, FPLL is meant to be an open source license, far less restrictive than GPL, aiming to resolve specific licensing issues of programming languages; we're dual licensing because of the drop in the competence of OSI and the slowness of legal offices of institutional GNU/Linux distributors in analyzing the license (yet Blastwave, AuroraUX and Slackware have adopted FPLL). TIA for your kind attention Giancarlo Niccolai =========================================================================== Reply from Mr. Wagner: Return-path: [hidden for privacy] Envelope-to: gc@falconpl.org Delivery-date: Fri, 09 Apr 2010 10:25:50 -0700 Received: from webrelay-macc.mr.itd.umich.edu ([141.211.12.74]:48408) by vps779.inmotionhosting.com with esmtp (Exim 4.68) (envelope-from ) [hidden for privacy] id 1O0Hxc-0006Kc-Ih for gc@xxxxxx; Fri, 09 Apr 2010 10:25:50 -0700 [hidden for privacy] Received: FROM jackiebrown-repl.mail.umich.edu (jackiebrown-repl.mail.umich.edu [141.211.125.75]) By webrelay-macc.mr.itd.umich.edu ID 4BBF6390.E23B8.5407 ; 9 Apr 2010 13:27:44 EDT Received: (from www@localhost) by jackiebrown-repl.mail.umich.edu () id o39HRigv029981 for gc@xxxxxxx; Fri, 9 Apr 2010 13:27:44 -0400 [hidden for privacy] Received: from hobson.nist.gov (hobson.nist.gov [129.6.153.49]) by web.mail.umich.edu (Horde Framework) with HTTP; Fri, 09 Apr 2010 13:27:44 -0400 Message-ID: <20100409132744.13544mn7qgj175a8@web.mail.umich.edu> Date: Fri, 09 Apr 2010 13:27:44 -0400 From: wagnerr To: Giancarlo Niccolai Subject: Re: Redistribution of Mersenne Twister algorithm. References: <4B4AECDF.8030107@falconpl.org> In-Reply-To: <4B4AECDF.8030107@falconpl.org> MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1; DelSp="Yes"; format="flowed" Content-Disposition: inline Content-Transfer-Encoding: 7bit User-Agent: Internet Messaging Program (IMP) H3 (4.3.5) X-Remote-Browser: Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.10 (like Gecko) (Debian) X-IMP-Server: 192.168.225.75 (jackiebrown-repl) X-Originating-IP: 129.6.153.49 X-Originating-User: wagnerr X-Spam-Status: No, score=-2.6 X-Spam-Score: -25 X-Spam-Bar: -- X-Spam-Flag: NO Giancarlo, I'm glad to hear you found my Mersenne Twister code useful. You have my permission to redistribute it under the GPL and FPLL. Rick Wagner RELNOTES000066400000000000000000000044521176363201700122470ustar00rootroot00000000000000 RELEASE NOTES for Falcon Chimera Release 0.9.6.6 "Chimera" version is an intermediate version that bridges Eagle series with the next series, in which we should put in the final compiler/module assets. This is the last bugfix release in the Chimera version, and closes the series of 0.9.6.6 releases. IMPORANT DISCLAIMER: starting from this version, we're introducing the mersenne twister algorithm for random number generation. The permission to use it has been granted by one of the authors, as described in the README.mersenne file. New Features ============ - The hash module has been added to feathers; it provides a set of popular hashing facilities as MD5, SHA256, HMAC and many more. - Message oriented programming has now an "event model" message generation, which is added to VMSlot class. - Added method Dictionary.dop (default operation) -- a facility that makes easier to create maps of complex items (i.e. maps of lists) with a one-liner. - Added methods Dictionary.do and Array.concat - Added Base64 class. - New epoch() function returning exactly the seconds since UNIX epoch. Critical bug fixes ================== - toString() crashed when called without a parameter. - Failing to init the frame in new VMContexts would have caused random crashes depending on linkage or memory conditions. - Immediate array unpack was causing an assert in case of wrong sizes. - passvp() was failing if calling function had local vars Usage tips ========== Although the new Garbage Collector is relatively efficient in determining the actual needs of applications and in reclaiming unused memory, the standard detection algorithms are still a bit naive. If your application is memory-intensive, be sure to read the Core Module Documentation about the new GC object, which provides four default collection strategies and also allows you to implement your own. A finely tuned GC strategy can boost the performance of your application and reduce its memory footprint. Meanwhile we'll continue to work towards finding collection strategies applicable to the most common usage patterns and you're invited to provide your own, should this aspect of Falcon be critical to your application. TODO000066400000000000000000000015731176363201700115620ustar00rootroot00000000000000TODO for 0.9.8 - URGENT: Change signature of interruption related functions in VM; sleep() must be fixed to respect interrruption protocol. - URGENT: New service model with service factory function. - URGENT: New GarbagePointer model to allow passing foreign types. - URGENT: virtual constructor in sequences to allow them to build an empty copy. - URGENT: Break URI::UserInfo internally in user and password fields. - URGENT: Finetune continuations: be able to change parameters after the call. - Move dictionary find methods to standard sequences. - Expansion of the Tokenizer (with custom recognizers) and deep testing. - Provide Stream interface to Sockets. - Native support for big numbers. - Type contracts. - New, more flexible compiler/module pair, and revision of the link process. - Better Parallel constructs. apps/000077500000000000000000000000001176363201700120275ustar00rootroot00000000000000apps/CMakeLists.txt000066400000000000000000000000311176363201700145610ustar00rootroot00000000000000add_subdirectory(faldoc) apps/faldoc/000077500000000000000000000000001176363201700132575ustar00rootroot00000000000000apps/faldoc/.gitignore000066400000000000000000000000071176363201700152440ustar00rootroot00000000000000faldoc apps/faldoc/AUTHORS000066400000000000000000000000451176363201700143260ustar00rootroot00000000000000Giancarlo Niccolai apps/faldoc/CMakeLists.txt000066400000000000000000000023611176363201700160210ustar00rootroot00000000000000#################################################################### # The Falcon Programming language # # CMake configuration file for Feather modules #################################################################### cmake_minimum_required(VERSION 2.6.2) project(falcon-app-faldoc) set(FALDOC_DIR "${FALCON_APP_DIR}/faldoc" ) #install startup bat if(UNIX) configure_file( faldoc.sh.in ${CMAKE_CURRENT_BINARY_DIR}/faldoc @ONLY ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/faldoc DESTINATION ${FALCON_BIN_DIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) else() configure_file( faldoc.bat.in ${CMAKE_CURRENT_BINARY_DIR}/faldoc.bat @ONLY ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/faldoc.bat DESTINATION ${FALCON_BIN_DIR} ) endif() install(FILES src/faldoc.fal DESTINATION ${FALDOC_DIR}) install(DIRECTORY src/faldoc DESTINATION ${FALDOC_DIR}) install(DIRECTORY src/resources DESTINATION ${FALDOC_DIR}) configure_file( faldoc-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/faldoc-config.cmake @ONLY ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/faldoc-config.cmake DESTINATION ${CMAKE_INSTALL_PREFIX}/share/cmake/faldoc ) apps/faldoc/ChangeLog000066400000000000000000000012031176363201700150250ustar00rootroot00000000000000faldoc(CURRENT) ! Fixed load path on windows. faldoc(2.2) ! Init block removed; Parameters are explained in the class description. ! @from command (and commands with parameters AND end-block variable data) didn't work correctly if the variable part wasn't given. ! Added Input.basdir; The directory every path in the input file refers to will be set by this variable. If not set, it defaults to the directory where the input file is located. faldoc(2.1) ! Init block is getting grammar parameters, so it's more readable. + Added a short description of inherited properties and functions in class summary. apps/faldoc/LICENSE000066400000000000000000000257771176363201700143060ustar00rootroot00000000000000 Falcon Programming Language License Version 1.0, February 2005 http://www.falconpl.org/?page_id=license TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code and example code. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Embedding Works" shall mean any work, whether in Source or Object form, that links (or binds by name) to the interface of the Work and Derivative Works. "Scripts" shall mean any work, weather in Source or Object form, that is expressed through the grammar rules which are known by the Work. "Users" shall mean any person that uses, directly or indirectly, all or any of the Work, the Derivative Works, the Embedding Works or the Scripts. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, prepare Embedding Works, prepare Scripts, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution of Work and Derivative Works. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) You must state in the Source Form and in the documentation of any Derivative Work the fact that such work is a derivation of the Work, and include a copy of the Work in its Source form or provide directions on how to obtain a copy of the Work in its Source form; and (e) The Derivative Works are distributed under the terms of this License, or under terms that do not cause infringement of this License. 5. Distribution of Embedding Works and Scripts. You may produce and distribute any Embedding Work or Scripts thereof in any medium, in Source or Object form, provided You meet the following conditions: (a) The Embedding Works and Scripts are distributed under the term of this License, or the application of another License is explicitly stated in the documentation of the Embedding Works and Scripts or included in the Source form of the Embedding Works and Scripts; and (b) The Embedding Works carry a prominent notice in their documentation, or when not applicable, in any place that the Users are exposed to, about the fact that the Work is embedded, along with a general statement about the task that the Work is performing in the Embedding Works; and (c) If the Source form of Embedding Works is distributed or made available to the Users in any medium and by any means, the portions of the Source form that causes the Work to be embedded must carry a prominent notice about this fact, along with a general statement about the task that the Work is performing in the Embedded Works; and (d) If the Source form of Scripts is not distributed nor made available by any mean to the Users, a prominent notice about the fact that the Scripts have been written in the Language must be presented in a place which the Users are exposed to. 6. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement You may have executed with Licensor regarding such Contributions. 7. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 8. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 9. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 10.Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of Your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS apps/faldoc/README000066400000000000000000000003401176363201700141340ustar00rootroot00000000000000 The Falcon Programming Language ============== Official tools ============== Falcon Auto-documentation Tool ----------------------------- This is the Falcon Auto-documentation tool. TODO: readme apps/faldoc/data/000077500000000000000000000000001176363201700141705ustar00rootroot00000000000000apps/faldoc/data/template.fd000066400000000000000000000024061176363201700163200ustar00rootroot00000000000000# # FALDOC - Falcon Documentation system # # Template fd (faldoc) file # ################################################ # Documentation generic data ################################################ Title = Template documentation Author = Name of the author Version = 0.0.1 ################################################ # Arbitrary properties ################################################ Property.coauthors = Someone Else ################################################ # Faldoc Input settings ################################################ Input.directory=/to/be/parsed Input.directory=/other/to/be/parsed... Input.wildcard=*.fdd Input.wildcard=*.fal Input.wildcard=*.ftd Input.wildcard=*.cpp Input.recursive=true # Other files may be specified here #Input.extra=/path/to/mainmain.fal ################################################ # Faldoc Output settings ################################################ Output.module=html #You may provide other dynamic output modules here. #Output.module=my_module ################################################ # Faldoc HTML Output settings # Each module can be self-configured through # Output.. configs ################################################ Output.html.directory=html Output.html.url=. apps/faldoc/faldoc-config.cmake.in000066400000000000000000000041411176363201700173610ustar00rootroot00000000000000 get_filename_component(SELF_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) find_program(Falcon_faldoc_EXECUTABLE faldoc.sh ${Falcon_BINARY_DIR}) function(faldoc_target modulename input_file output_dir) if(Falcon_IN_CORE_SOURCETREE) if(WIN32 AND CMAKE_GENERATOR MATCHES "Visual Studio.*") set(_cmd ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/falcon -L ${CMAKE_BINARY_DIR}/${FALCON_MOD_DIR}/${CMAKE_CFG_INTDIR} ${falcon-app-faldoc_SOURCE_DIR}/src/faldoc.fal ${input_file} ) else() set(_cmd ${CMAKE_COMMAND} -Dexecutable=falcon -Dworkdir=${CMAKE_CURRENT_BINARY_DIR} -Darguments="${falcon-app-faldoc_SOURCE_DIR}/src/faldoc.fal ${input_file}" -P ${CMAKE_BINARY_DIR}/${FALCON_BIN_DIR}/executable_wrapper.cmake ) endif() else() set(_cmd ${Falcon_faldoc_EXECUTABLE} "${input_file}") endif() add_custom_command( OUTPUT ${output_dir}/index.html COMMAND ${_cmd} DEPENDS ${input_file} ${ARGN} COMMENT ${output_dir}/index.html ) add_custom_target(${modulename}_module_docs DEPENDS ${output_dir}/index.html) # if(Falcon_IN_CORE_SOURCETREE) add_dependencies(${modulename}_module_docs faldoc) endif() endfunction(faldoc_target) function(faldoc_module_docs modulename input_file_in) get_filename_component(_in_ext ${input_file_in} EXT) get_filename_component(input_file ${input_file_in} NAME_WE) if(NOT _in_ext STREQUAL ".fd.in") message(FATAL_ERROR "the input_file_in file name has to match *.fd.in, it's ${input_file_in} instead.") endif() set(output_dir ${CMAKE_CURRENT_BINARY_DIR}/Falcon-docs-${modulename}) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/${input_file}.fd.in ${CMAKE_CURRENT_BINARY_DIR}/${input_file}.fd @ONLY ) faldoc_target(${modulename} ${CMAKE_CURRENT_BINARY_DIR}/${input_file}.fd ${output_dir} ${ARGN} # additional dependencies ) install( DIRECTORY ${output_dir} DESTINATION ${Falcon_DOC_DIR} OPTIONAL # if docs haven't been generated ) endfunction(faldoc_module_docs) apps/faldoc/faldoc.bat.in000066400000000000000000000002161176363201700156030ustar00rootroot00000000000000@echo off set FALDOC_APP_PATH=%~dp0/../@Falcon_APP_DIR@/faldoc/faldoc.fal "%~dp0/falcon.exe" "%FALDOC_APP_PATH%" %1 %2 %3 %4 %5 %6 %7 %8 %9 apps/faldoc/faldoc.sh.in000077500000000000000000000004731176363201700154570ustar00rootroot00000000000000#!/bin/sh if [ -z "$FALCON_ENV_DIR" ]; then FALCON_EXE=@CMAKE_INSTALL_PREFIX@/@FALCON_BIN_DIR@/falcon FALDOC_FAL=@CMAKE_INSTALL_PREFIX@/@Falcon_APP_DIR@/faldoc/faldoc.fal else # Use path FALCON_EXE=falcon FALDOC_FAL=$FALCON_ENV_DIR/@Falcon_APP_DIR@/faldoc/faldoc.fal fi $FALCON_EXE $FALDOC_FAL $* apps/faldoc/src/000077500000000000000000000000001176363201700140465ustar00rootroot00000000000000apps/faldoc/src/faldoc.fal000066400000000000000000000166051176363201700157720ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - Documentation tool FILE: faldoc.fal Autodocumentation tool - main file ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 10 Jan 2008 08:12:57 -0800 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load compiler load self.version load self.config load self.document import from parser.faldoc.txt resources: "resources/*" //================================================ // Application class //================================================ class Application version = "3.0" fdfile = nil errorCount = 0 warnCount = 0 config = nil document = nil //# file currently parsed currentFile = "" //# Txt parser used by many users. txtParser = parser.faldoc.txt.Parser() //# Application process function go() try self.processParams() self.banner() self.readConfig() self.processInput() self.processOutput() catch in e self.fatal( e.toString() ) end end //================================================================= // Process steps //================================================================= function processParams() // get minimal parameters self.fdfile = args.len() == 0 ? "faldoc.fd" : args[0] end function banner() > i"faldoc - Falcon documentation tool." > i"Part of the Falcon Programming language." > i"Version ", version.name() > "----------------------------------------" end function readConfig() self.config = Config() if not self.config.read( self.fdfile ) self.fatal( i"Errors in configuration" ) end if not self.config.inputDirs self.fatal( i"Sorry, no input dirs" ) end end function processInput() self.initDocument() cfg = self.config doc = self.document basedir = cfg.basedir for dir in cfg.inputDirs // open the directory drel = self.relativize( basedir, dir ) try hdir = Directory( drel ).descend( {=>}, {fname => for wildcard in cfg.inputWild if strWildcardMatch( fname, wildcard ) doc.addFile( fname ) end end }) catch IoError in err raise "can't read directory \""+basedir + "/"+dir+"\" in input dirs." end end // parse extra files for extra in cfg.inputExtra p = Path(extra) if not p.fulloc.startsWith("/") doc.addFile( basedir + "/" + extra ) else doc.addFile( extra ) end end // ok, time to cleanup: resolve links doc.resolveLinks() // and check for undefined entities doc.checkUndefined() end function processOutput() self.info( "=" * 60 ) // create the compiler compiler = Compiler() conf = self.config.cfile self.info( "Start processing output" ) for mod in self.config.outMods try modData = compiler.loadByName( "self.output." + mod ) faldoc_output = modData.get( "faldoc_output" ) if not isCallable( faldoc_output ) raise "module '"+ mod + "' doesn't define 'falcon_output' entry point" end // perform call faldoc_output( self.document, firstOf(conf.getCategory("Output."+mod+".*"),[=>]) ) // unload the module modData.unload() catch IoError in error // signal an abnormal exit raise "Cannot find or load module (faldoc_output_)"+mod end // let any other error through to be catched by the main try. end end function initDocument() cfg = self.config doc = Document() doc.title = cfg.title doc.author = cfg.author doc.version = cfg.version self.document = doc end //================================================================= // Utilities //================================================================= //# function to generate a fatal error function fatal( error, line, file ) self.display( 0, "FATAL - " + error, line, file ) exit(0) end //# function to generate an error function error( e, line, file ) self.display( 1, "ERROR - " + e, line, file ) self.errorCount++ end //# function to generate a warning function warn( e, line, file ) self.display( 2, "WARN - " + e, line, file ) self.warnCount++ end //# function to send an info function info( i, line, file ) self.display( 3, i, line, file ) end //# function to send an info function verbose( i, line, file ) self.display( 4, i, line, file ) end //# funciton to display an information message function display( level, msg, line, file ) if level <= self.config.loglevel if file fmarker = file elif self.currentFile fmarker = self.currentFile else fmarker = "" end if line fmarker += ":" + line end > "faldoc: ", fmarker, " ", msg end end function result() if self.warnCount or self.errorCount self.info( "="*60 ) self.info( i"Detected " + self.warnCount + " warnings." ) if self.errorCount self.info( i"Terminating with " + self.errorCount + " errors." ) return 1 else self.info( i"== Done ==" ) end else self.info( i"== Done ==" ) return 0 end end //# Eventually relativize a filename or directory name to basedir function relativize( basedir, dir ) dpath = Path( dir ) if dpath.fulloc.startsWith("/") return dir else return basedir + "/" + dir end end /*# Determine module name based on base directory and root module directories. @param file A full filename of a module, already relativized to basedir. */ function calcModName( file ) cfg = self.config basedir = cfg.basedir // find a base directory that may be the root of our module rootDir = self.findRootDir( basedir, cfg.moduleRoots, file ) // no luck? try with input dirs if not rootDir: rootDir = self.findRootDir( basedir, cfg.inputDirs, file ) if rootDir: file = file[rootDir.len()+1:] if file.endsWith( ".fal" ) or file.endsWith( ".ftd" ) file = file[0:-4] end return file.replace("/",".") end function findRootDir( basedir, roots, file ) for dir in roots dr = self.relativize( basedir, dir ) if file.startsWith( dr ) return dr end end end function copyResource( resName, dest ) p = Path( vmModulePath() ) loc = p.fulloc +"/resources/" + resName fileCopy( loc, dest + "/" + resName ) end end //================================================ // Main code //================================================ faldoc = Application() faldoc.go() return faldoc.result() // Export the global faldoc entity export faldoc /* end of faldoc */ apps/faldoc/src/faldoc.ftr000066400000000000000000000010621176363201700160120ustar00rootroot00000000000000TLTABit_IT )faldoc: Documento in ingresso non valido:-Errore nel leggere il file di configurazione:(faldoc: Error fatale -*,faldoc: Errore fatale - eccezione sollevata:,0faldoc - Strumento di documentazione per falcon.-.Parte del Linguaggio di Programmazione Falcon..Versione6,Chiave 'title' mancante dalla configurazione9-Chiave 'author' mancante dalla configurazione;/Chiave 'versione' mancante dalla configurazione><E' necessario specificare almeno una chiave 'Output.module'.apps/faldoc/src/faldoc.it_IT.ftt000066400000000000000000000035211176363201700170250ustar00rootroot00000000000000 faldoc: Invalid input file faldoc: Documento in ingresso non valido: Error in reading the configuration file: Errore nel leggere il file di configurazione: faldoc: FATAL ERROR - faldoc: Error fatale - faldoc: FATAL ERROR - Rised an exception: faldoc: Errore fatale - eccezione sollevata: faldoc - Falcon documentation tool. faldoc - Strumento di documentazione per falcon. Part of the Falcon Programming language. Parte del Linguaggio di Programmazione Falcon. Version Versione Title key missing from configuration Chiave 'title' mancante dalla configurazione Author key missing from configuration Chiave 'author' mancante dalla configurazione Version key missing from configuration Chiave 'versione' mancante dalla configurazione At least one 'Output.module' key is necessary. E' necessario specificare almeno una chiave 'Output.module'. apps/faldoc/src/faldoc/000077500000000000000000000000001176363201700152765ustar00rootroot00000000000000apps/faldoc/src/faldoc/config.fal000066400000000000000000000110251176363201700172260ustar00rootroot00000000000000/* FALCON - Documentation tool FILE: config.fal Configuration options ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 28 Sep 2010 12:53:35 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from confparser class Config title = nil author = nil version = nil properties = nil //# Base directory of the documentation tree. basedir = nil //# Input directory -- defaults to "." inputDirs = nil //# Module topmost directories moduleRoots = nil // Input file wildcard; defaults to "*.fal" inputWild = nil // Input extra files inputExtra = nil //# input recursive -- defaults to true recursive = nil //#using autobrief ? autobrief = nil //# List of output modules to be used to generate output outMods = nil //# Log level loglevel = nil //#configuration file cfile = nil //# get undocumented getundoc = nil //# get private members getprivate = nil //# Reads the configuration file. function read( fdfile ) // get config try conf = confparser.ConfParser( fdfile ) conf.read() self.cfile = conf catch IoError in error faldoc.fatal( i"Invalid input file $(fdfile)" ) return false catch in error faldoc.fatal( i"Error in reading the configuration file:\n$(error)" ) return false end r = self.configure( fdfile, conf ) return r end function configure( fdfile, conf ) self.checkPrerequisites( conf ) self.calcBaseDir( fdfile, conf ) self.getGeneralSettings( conf ) self.getInputSettings( conf ) self.getOutputSettings( conf ) return true end function checkPrerequisites( conf ) // then, we need at least one output if not conf.get("Output.module" ) faldoc.fatal( i"At least one 'Output.module' key is necessary." ) end end function calcBaseDir( fdfile, conf ) path = Path( fdfile ) basedir = conf.get( "Input.basedir" ) if not basedir: basedir = path.fulloc if basedir == nil or basedir == "": basedir ="." self.basedir = basedir end function getGeneralSettings( conf ) // get the essentials if not (self.title = conf.getOne( "Title" )) faldoc.fatal( i"Title key missing from configuration" ) end if not (self.author = conf.getOne( "Author" )) faldoc.fatal( i"Author key missing from configuration" ) end if not (self.version = conf.getOne( "Version" )) faldoc.fatal( i"Version key missing from configuration" ) end // get the properties. self.properties = conf.getCategory( "Property.*" ) // autobrief self.autobrief = self.checkBool( "AutoBrief", false ) // get undocumented self.getundoc = self.checkBool( "GetUndoc", false ) // get private members self.getprivate = self.checkBool( "GetPrivate", false ) try self.loglevel = int(firstOf( conf.getOne( "LogLevel" ), 3 )) // default info catch faldoc.fatal( i"Loglevel key must be an integer" ) end end function getInputSettings( conf ) // Input directory -- defaults to "." self.inputDirs = firstOf(conf.getMultiple( "Input.directory" ), ["."]) self.moduleRoots = firstOf(conf.getMultiple( "Input.moduleRoot" ), []) // Input file wildcard; defaults to "*.fal" self.inputWild = firstOf(conf.getMultiple( "Input.wildcard" ), ["*.fal"]) // input recursive -- defaults to true self.recursive = self.checkBool( "Input.recursive", true ) // input extra files self.inputExtra = conf.getMultiple( "Input.extra" ) end function getOutputSettings( conf ) // get the modules -- we know it's here, processConf checks it self.outMods = conf.getMultiple("Output.module") end //========================================================= // More utilities //========================================================= function checkBool( key, dflt ) value = self.cfile.getOne( key ) if not value: return dflt v = strLower( value ) if v == "on" or v == "true": return true if v == "off" or v == "false": return false faldoc.fatal( i"Invalid value for configuration key " + key ) end end export Configapps/faldoc/src/faldoc/docentity.fal000066400000000000000000000423441176363201700177730ustar00rootroot00000000000000/* FALCON - Documentation tool FILE: docentry.fal Autodocumentation tool - basic entry item. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 23 May 2009 12:19:38 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from struct.tree /*# Entity class. A document entity is a single entry in the documentation file. It can be a page, a section, a class, an object, a function, a method, or in general any entity that can be indexed and cross-linked. An entity is made of multiple paragraph and has multiple properties. It's type and name cannot always be determied at creation (actually, that is rare), and is usually assigned after the whole entity documentation text have been parsed. Entities can be hierarchically organized into different relationship groups: - parent - children; ownership relationship, like properties and methods for classes. - group - members: Multiple grouping where each group can have a list of members, but a member can be present in multiple groups.s Parent-children relationships are described through a pair of property (parent and children), while grouping is expressed through entries in the @a self.props property (with the property "in" => "". The @a self.props property contains many important elements, as the parameters. Parameters entry (under props */ class Entity( content, file, line ) //# Type of the entity type = nil /*# Local name of the entity. */ name = nil /*# Filename where the entity is coming from. */ file = file /*# First line of comment declaration of the entity. */ firstline = line /*# Line at which the entity is defined at language level (if any). */ dline = nil props = bless([=>]) //# Parent of this entity parent = nil /*# Parsed content. Documentation of the entity parsed as an infonode. */ pcontent = nil //# Textual (unparsed) content. For debugging and reference content = content //# Non-child members (for entities with grouping abilities) members = nil //# True when this entity was created because of a supposed reference automatic = false //# used to determine if an empty entity should be marked as an error hadSimple = false //# used to determine if an empty entity should be marked as an error hadComplex = false //# after -- an ID (local to the owner module) for a page-entity to be placed after. after = nil id = 0 static lid = 0 init self.id = self.lid++ end //# Copy an existing entity function declareAs( other ) self.type = other.type self.file = other.file self.firstline = other.firstline self.dline = other.dline self.props = other.props self.pcontent = other.pcontent self.content = other.content self.after = other.after if other.members if self.members self.members += other.members else self.members = other.members end end self.automatic = false end //# Calculates the unique ID of this node. function getID() if self.parent id = self.parent.getID() if id: return id + "." + self.name end return self.name == "Main module" ? "" : self.name end function getName() if "owner" in self.props return self.parent.name + "." + self.name else return self.name end end /*# Processess the commands in the parsed content block. @param onGroupingChange callback receiving line, command and name in case of grouping commands. @param entityAdder A callable that will be called back if the parser finds something to be added. @return false if this entity is actually a set of document-wide command and should not be considered a real document entity. @note Interesting commands are at top level. Everything else (inline commands) is expanded by the generic parser into DOM entities or is made to "stand out". */ function parseContent( onGroupingChange, entityAdder ) self.parseNodeContent( self.pcontent.topNode.firstChild, onGroupingChange, entityAdder ) end function parseNodeContent( node, onGroupingChange, entityAdder ) while node != nil // go deep before proceeding if node.firstChild self.parseNodeContent( node.firstChild, onGroupingChange, entityAdder ) end line = (node provides infos) ? (node.infos["line"] + self.firstline - 1) : self.firstline hadSimple = false hadComplex = false //> "Node ", node.parent.type + "." + node.type + (node provides infos and "name" in node.infos ? ": " + node.infos["name"] : "") switch node.type case "@a" l = faldoc.document.addLink( self, line, node.infos["dest"], true ) node.infos["link"] = l node = node.next case "@after" self.after = node.infos["dest"] node = node.remove() case "@raise", "@from", "@see" l = faldoc.document.addLink( self, line, node.infos["what"], node.type == "@see" ) // we trick the system a bit and send a link instead of a name here. // however, it's ok and wors as expected self.addParam( node.type[1:], l, node, line ) node = node.remove() hadComplex = true case "@brief", "@return" type = node.type[1:] if type in self.props faldoc.error( @i"Command @$type already declared for entity", line ) else self.props[ type ] = node end node = node.remove() hadComplex = true case "@param", "@optparam" self.addParam( node.type[1:], node.infos["name"], node, line ) node = node.remove() hadComplex = true case "@prop" // simple declaration of class properties prop = Entity( "", self.file, line ) prop.type = "property" prop.name = node.infos["name"] prop.pcontent = self.pcontent.baseClass()() prop.props["owner"] = self.name n1 = node.remove() node.prev = node.next = node.parent = nil node.type = "para" prop.pcontent.add( node ) prop.parseContent( onGroupingChange, entityAdder ) entityAdder( prop ) prop.makeAutoBrief() node = n1 hadComplex = true case "@inset", "@ingroup" if "inset" in self.props or "ingroup" in self.props faldoc.error( "Grouping option already declared for this entity", line ) else self.props[node.type[1:]] = node.infos["name"] end node = node.remove() hadComplex = true case "@inmodule" if self.parent faldoc.error( "Parent already declared for entity \"$name\"", line) else inmodule = node.infos["name"] self.props["inmodule"] = inmodule //faldoc.verbose( @i"Entity $name has @inmodule $inmodule" ) end node = node.remove() hadComplex = true case "@begingroup", "@beginset", "@beginmodule", "@beginignore" onGroupingChange( line, node.type, node.infos["name"] ) node = node.remove() hadSimple = true case "@endgroup", "@endset", "@endmodule", "@endignore" onGroupingChange( line, node.type, nil ) node = node.remove() hadSimple = true // we have the type defined! case "@class", "@function", "@object", "@enum", "@global" self.name = node.infos["name"] self.type = node.type[1:] node = node.remove() hadComplex = true case "@method", "@property" self.name = node.infos["name"] self.type = node.type[1:] self.props["owner"] = node.infos["class"] node = node.remove() hadComplex = true case "@page" self.name = node.infos["name"] self.type = "page" self.props[ "title" ] = node.infos["title"] node = node.remove() hadComplex = true case "@group", "@funset" self.name = node.infos["name"] self.type = node.type[1:] self.props[ "title" ] = node.infos["title"] node = node.remove() hadComplex = true case "@main" faldoc.verbose( "Found @main command" ) self.name = "#main" self.type = "main" self.props["title"] = node.infos["title"] node = node.remove() hadComplex = true case "@ignore" self.type = "ignore" hadSimple = true case "@module" name = node.infos["name"] module = faldoc.document.getModule( name ) if not module: return if not module.automatic // someone already created it. faldoc.error( @i"Module $(name) already created", line ) else module.automatic = false // if it's automatic, it can't have a main page. self.name = "#main" self.type = "main" self.props["title"] = node.infos["title"] self.props["inmodule"] = name // this will turn us in the main for the given module at return. end node = node.remove() hadComplex = true case "cmd_unrecognized" command = node.infos["cmd"] faldoc.error( @i"Unrecognized command \"$command\"", line ) node = node.remove() default node = node.next end end if hadComplex: self.hadComplex = true if hadSimple: self.hadSimple = true end /*# Indicates if this entity just contained file-level commands. If true, then it is not an error if this entity results empty after parsing. It means that it was there just as a place where to call this commands, and was not intended to create an entity that was left undefined. */ function wasASimpleCommand() return self.hadSimple and not self.hadComplex end //# Adds a parameter or similar multiple entry in properties. function addParam( type, name, content, line ) // First, a special control for parameters and optional parameters which relate to each other if type == "param" and "optparam" in self.props and arrayScan( self.props["optparam"], {elem => elem[0] == name} ) >= 0 faldoc.error( @i"Parameter \"$name\" of type already declared as optparam", line ) return elif type == "optparam" and "param" in self.props and arrayScan( self.props["param"], {elem => elem[0] == name} ) >= 0 faldoc.error( @i"Optional parameter \"$name\" of type already declared as parameter", line ) return end // the add the property if type in self.props vt = self.props[type] if arrayScan( vt, {elem => elem[0] == name} ) >= 0 faldoc.error( @i"Property \"$name\" of type \"$type\" already declared", line ) else vt.add( [name, content] ) end else self.props[type] = [[name, content]] end end //# Adds a parameter declared in the function prototype function addFoundParam( name, content, line ) vtp = "param" in self.props ? self.props["param"] : nil vto = "optparam" in self.props ? self.props["optparam"] : nil foundParam = vtp and arrayScan( vtp, {elem => elem[0] == name} ) != -1 foundOpt = vto and arrayScan( vto, {elem => elem[0] == name} ) != -1 if not foundParam and not foundOpt faldoc.warn( @i"Formal parameter \"$name\" not referenced in text block", line ) self.addParam( "param", name, nil, line ) end end //# adds an entity to this entity function addMember( entity ) if not entity.name faldoc.fatal( @i"Adding an unknown entity to its parent $self.name", entity.firstline ) end faldoc.verbose( @i"Adding entity $entity.name to $self.name", entity.firstline ) if entity.name in self.members // entity already declared; but -- is this an automatic definition? if entity.automatic // strange; we should have taken the real entity faldoc.error( @i"Entity \"$entity.name\" referenced as automatic but was already declared in \"$self.name\"", entity.firstline ) > fself.stack() else // Was the previously declared entity automatic? preve = self.members[ entity.name ] if preve.automatic // warn if found with different type if preve.type != nil and preve.type != entity.type faldoc.warn( @i"Entity \"$entity.name\" was referenced in \"$self.name\" with different type: ($preve.type)", entity.firstline ) end // but go on the same. preve.declareAs( entity ) else // a real error -- two entities explictly declared with the same name. faldoc.error( @i"Entity \"$entity.name\" already declared in \"$self.name\"", entity.firstline ) end end // in either case return end // add the entity if not self.members: self.members = [=>] self.members[entity.name] = entity end //# Returns all the members of this entity grouped by their type. function membersByType() data = [=>] for name, member in self.members if member.type in data data[member.type].add( member ) else data[member.type] = [member] end end return data end function getPropsAndMethods() props = [] methods = [] for m,v in self.members if v.type == "property" props += v else methods += v end end return [props, methods] end function prototype() pars = "" if "param" in self.props pars = ", ".merge(map( {x=> x[0]}, self.props["param"])) end if "optparam" in self.props if pars: pars += ", " pars += ",".merge( map( {x=> "[" + x[0] + "]"}, self.props["optparam"]) ) end name = self.getName() if pars return name + "( " + pars + " )" else if self.type == "function" or self.type == "method" return name + "()" else return name end end end function getDescendant( tgtname ) pt = tgtname.find( "." ) if pt > 0 name = tgtname[0:pt] rest = tgtname[pt+1:] else name = tgtname rest = nil end if name in self.members child = self.members[name] if rest return child.getDescendant( rest ) else return child end end // no luck return nil end function dump( level ) if not level: level = 0 > " " * level, self.id, " ", self.type, " ", self.name, " - ", self.file, ":", self.firstline for n, v in self.members v.dump( level + 1 ) end end //# Groups the entities by type function groupEntities() data = [=>] for e, entity in self.members if entity.type in data data[entity.type].add(entity) else data[entity.type] = [entity] end end return data end //# Create a brief paragraph generated from the first period of the content. function makeAutoBrief() if not self.pcontent: return brief = _flatten( self.pcontent.topNode ) pos = brief.find(".") if pos > 0 self.props["brief"] = struct.tree.Node(brief[0:pos+1]) else self.props["brief"] = struct.tree.Node(brief) end end //# Return false if this entity can have a subsequent source entity where it should be applied. function isStandAlone() return self.type == "module" or self.type == "main" end end //# Flattens a textual node function _flatten( textNode ) // Just text ? -- return it if textNode.content: return textNode.content // return the node as text str = "" node = textNode.firstChild while node if str: str += " " str += _flatten( node ) node = node.next end return str end export apps/faldoc/src/faldoc/docmodule.fal000066400000000000000000000162061176363201700177420ustar00rootroot00000000000000/* FALCON - Documentation tool FILE: docmodule.fal Autodocumentation tool - Module representation in a document. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 30 Sep 2010 19:07:41 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load .docentity load .link /*# Document module. A document module may represent a chapter or a module to be added to the documentation. Virtual modules are created out of module hierarcies on filesystem, and are usually flattened in documentation. For example, modules as web.alpha and web.beta belong to the "web" module, but unless there is specific content for the "web" module, it is considered virtual. This means that "web.alpha" and "web.beta" are generated directly under the parent of the "web" module. Conversely, if "web" has a main page and/or a title, or if it has any child which is not a module (i.e. a page, a class, a function etc), then it is considered a real module, and a documentation for "web" is generated. In this latter case, web.alpha and web.beta are placed at a lower level in the documentation, as "web" DocModule elements. A DocModule may also considered a separate section of the documentation, with its own pages (chapters), functions, groups and funcsets. */ class DocModule( name ) from Entity //# Title of the module title = nil //# Missing entities as local ID -> link missing = [=>] //# Main page of the module, if any main = nil //# is this a "virtual module" -- a module without contents except for other modules? isVirtual = true init self.name = name ? name : "Main module" self.type = "module" end //# Gets an entity from this module (possibly undefined) function getEntity( name, type ) if name notin self.members e = Entity( "", self.file, 0 ) e.name = name e.type = type e.automatic = true if not self.members: self.members = [=>] self.members[name] = e e.parent = self else e = self.members[name] end return e end //# Returns the meta-pages respecting their required order. function orderPages() pages = [] groups = [] funsets = [] modules = [] for name, entity in self.members switch entity.type case "main" pages[0:0] = entity case "page" pages += entity case "group" groups += entity case "funset" funsets += entity case "module" if entity.after modules += entity end end end // ok, now see if we must reorder something allpages = pages + groups + funsets + modules /*for entity in allpages forfirst: >> "Entities in ", self.name, ": " >> entity.name formiddle:>> ", " forlast: >"." end */ ppage = 0 l = allpages.len() while ppage < l entity = allpages[ppage] after = entity.after // sanitize main if after== "#main": after = "main" // adjust @after main if after == "main" // ppage starts from 1, while main a virtual page pp1 = 0 // prevent endless loop if more than one after main is found while allpages[pp1].after == "main" and pp1 < ppage ++pp1 end if pp1 < ppage allpages.remove( ppage ) allpages[pp1:pp1] = entity end ++ppage continue end // should we reorder it? if after and (ppage == 0 or allpages[ppage-1].name != after) // get the entity if after notin self.members faldoc.error( @i"Entity $(entity.name) should be after $(after), but \"$(after)\" is not in module $(self.name)", entity.firstline ) ppage ++ continue end etgt = self.members[ after ] // but where is this entity? ptgt = allpages.find( etgt ) if ptgt < 0 faldoc.error( @i"Entity $(entity.name) should be after $(entity.after), but that is not a meta-page", entity.firstline ) ppage++ continue end // and now reorder! allpages.remove( ppage ) // We must insert after the found element, which may be shifted 1 back if it's after our point ptgt_pos = (ptgt > ppage ? ptgt : ptgt+1) allpages[ptgt_pos:ptgt_pos] = entity // -- for the same reason, we may need not to increment if we have removed an element. if ptgt < ppage: ppage ++ continue end ppage ++ end // ok, pages are re-ordered return allpages end function checkUndefined() // No entites should have automatic setting turned on at end, except for modules. for name, entity in self.members if entity.type == "module" entity.checkUndefined() else if entity.automatic if entity.members mbfirst = entity.members.first().value() line = mbfirst.firstline file = mbfirst.file else line = 0 file = self.file end faldoc.error( @i"Entity \"$(entity.name)\" was indirectly invoked but never defined in $(self.name)", line, file ) end end end end /*# Adds a member to this module. If the member is a main page, it is assigned to the text and title of this module. In this case, or if the added member is not a module, this module becomes non-virtual, and will generate a new documentation level. */ function addMember( entity ) if entity.type == "main" faldoc.verbose( "Setting main page for module $(self.name)" ) self.main = entity self.title = entity.props["title"] self.after = entity.after self.isVirtual = false else self.Entity.addMember( entity ) if entity.type != "module" self.isVirtual = false end end end //# Gets the modules in this element. function getModules( alsoAfter ) mods = [=>] for name, m in self.members if m.type == "module" and (alsoAfter or not m.after) mods[ m.getID() ] = m end end return mods end //# Recursively gets all the modules stored in the document. function getAllModules() return self._getAllModules( self ) end function _getAllModules( module ) mods = [=>] for name, m in module.members if m.type == "module" mods[ m.getID() ] = m mods += self._getAllModules( m ) end end return mods end end export apps/faldoc/src/faldoc/document.fal000066400000000000000000000070601176363201700176030ustar00rootroot00000000000000/* FALCON - Documentation tool FILE: document.fal Autodocumentation tool - Output document representation. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 28 Sep 2010 15:30:16 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load .filecontext load .docmodule load .link import from parser.faldoc.generic import from parser.faldoc.falsrc class Document from DocModule title = nil author = nil version = nil _fdParser = parser.faldoc.falsrc.Parser() _gParser = parser.faldoc.generic.Parser() // links to be resolved links = [] function addFile( file ) p = Path( file ) faldoc.info( "Processing " + p.filename + " in " + p.fulloc ) try istream = InputStream( file ) // for now, suppose utf-8 encoding istream.setEncoding( "utf-8" ) if file.endsWith( ".ftd" ) or file.endsWith( ".fal" ) ctx = self._fdParser.parseStream( istream, "start" ) modname = faldoc.calcModName( file ) module = self.getModule( modname ) if not module: return faldoc.verbose( @i"File $file has module name $modname" ) else ctx = self._gParser.parseStream( istream, "start" ) module = self end // modname can be nil. fc = FileContext( file, module, modname ) faldoc.currentFile = file fc.parse( ctx ) catch in e faldoc.error( "While parsing " + file + ": " + e ) end faldoc.currentFile = nil if istream: istream.close() end function getModule( name ) if name in self.members module = self.members[name] if module.type != "module" faldoc.error( @i"Asking for module $(name), but that's already a $(module.type)") return nil end return module else // create one module at each step of the hierarcy modNames = name.split(".") topModule = self for name in modNames if name in topModule.members module = topModule.members[name] if module.type != "module" faldoc.error( @i"Asking for module $(name), but that's already a $(module.type)") return nil end else module = DocModule( name, topModule ) module.automatic = true topModule.addMember( module ) module.parent = topModule end topModule = module end return module end end function addLink( entity, line, dest, explicit ) l = Link( entity, line, dest, explicit ) self.links.add(l) return l end function resolveLinks() for l in self.links self.resolveLink( l ) end end function resolveLink( l ) src = l.source // we must find the required entity through the parent. parent = src.parent // try to reverse-find starting from the parent. while parent if (tgt = parent.getDescendant( l.destination )) // found! l.target = tgt return end parent = parent.parent end // not found? -- too bad (l.explicit ? faldoc.error : faldoc.warn)( @i"Unresolved link to \"$(l.destination)\"", l.line, l.source.file ) end end export Document apps/faldoc/src/faldoc/filecontext.fal000066400000000000000000000325461176363201700203200ustar00rootroot00000000000000/* FALCON - Documentation tool FILE: filecontext.fal Autodocumentation tool - Documentation data relative to a single input file. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 29 Sep 2010 10:05:06 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from .docentity /*# Documentation data relative to a single input file. @param File the file to be parsed @param defmod The default module where entities should go if not specified. @optparam modName The logical modue name, if any, for this file. */ class FileContext( file, defmod, modName ) file = file ctx = nil defmod = defmod modName = modName //# Parser used by this file parser = faldoc.txtParser //# Parent entity, used when parsing sub-contexts parent = nil //# Active begin/end blocks current_set = nil current_group = nil current_module = nil ignore = false //# Parsing a context into entities. function parse( ctx ) self.ctx = ctx self.parseNode( ctx.topNode ) // clear the ignore status before exiting self.ignore = false end function parseNode( parent, parentEntity ) node = parent.firstChild entity = nil getundoc = faldoc.config.getundoc getprivate = faldoc.config.getprivate while node != nil // normally, don't destroy currently examined entity clear = false if self.ignore // -- Parse texts till we get an endignore. if node.type == "faldoc_txt" entity = self.parseText( node.content, node.infos["line"] ) end node = node.next continue end switch node.type case "faldoc_txt" // see if we have a previous pending entity self.checkEntity( entity ) cnt = node.content line = node.infos["line"] // parse the content of this text entity // -- may be nil if the entity is just containing document commands. entity = self.parseText( cnt, line ) // module and main entities never refer to any subsequent entity. if entity and entity.isStandAlone() self.checkEntity( entity ) entity = nil end case "inherit" self.parseInherit( parentEntity, node.firstChild ) node = node.next continue case "params" self.parseParams( parentEntity, node.firstChild ) node = node.next continue default // have we a textual entity we want to post-process? if entity self.apply( node, entity, parentEntity ) // the text entity is discharged -- after checking for children clear = true else // skip undocumented entities if not required to take them, nodeName = node.infos["name"] if not getundoc or \ ( (nodeName.startsWith("_") or "._" in nodeName) and not getprivate) // prevent scanning for sub-nodes node = node.next continue end // create a new entity of the specified type. entity = self.createEntity( node, parentEntity ) // the entity created automatically from falcon source is discharged // -- after checking for children clear = true end end // see if we have children, and pass our entity to it. if node.firstChild: self.parseNode( node, entity ) // fullfil the promise to clear the entity. if clear: entity = nil node = node.next end // check if we have a last entity pending self.checkEntity( entity ) end function parseInherit( entity, node ) // only class and objects can have inheritance if entity.type != "class" and entity.type != "object" faldoc.error( @i"Inconsistent inheritance found for entity $entity.name", entity.firstline ) return end while node != nil line = node.infos["line"] + entity.firstline if node.type != "from" faldoc.error( @i"Invalid inheritance element in $entity.name", line ) return end l = faldoc.document.addLink( entity, line, node.infos["class"], false ) entity.addParam( "from", l, node, line ) node = node.next end end function parseParams( entity, node ) // only functions, methods and classes and objects can have parameters if entity.type != "class" and entity.type != "function" and entity.type != "method" faldoc.error( @i"Inconsistent inheritance found for entity $entity.name($entity.firstline) at line " + node.infos["line"] ) return end while node != nil if node.type != "param" and node.type faldoc.error( @i"Invalid parameter element in $entity.name($entity.firstline) at line " + node.infos["line"] ) return end line = node.infos["line"] name = node.infos["name"] entity.addFoundParam( name, node.content, line ) node = node.next end end function checkEntity( entity ) if entity if entity.name self.addEntity( entity ) else faldoc.error( @ i"Undefined entity",entity.firstline ) end end end /*# Parse a textual entity. @param cnt The text of the entry @param line The line where the text is declared in this file */ function parseText( cnt, line ) p = self.parser try ectx = p.parse( cnt ) entity = docentity.Entity( cnt, self.file, line) entity.pcontent = ectx entity.parseContent( self.onGroupingChange, self.addEntity ) // do we have a brief? if "title" notin entity.props and "brief" notin entity.props // autobrief on? if faldoc.config.autobrief // have we it some text? first_node = entity.pcontent.topNode.firstChild if first_node and first_node.type == "para" entity.props["brief"] = first_node first_node.remove() else // Discard the entity if it was a simple command if entity.wasASimpleCommand(): return nil faldoc.error( @ i"Entity not declaring a @brief paragraph", entity.firstline ) end else // Discard the entity if it was a simple command if entity.wasASimpleCommand(): return nil faldoc.error( @ i"Entity not declaring a @brief paragraph", entity.firstline ) end end // can't be "a simple command" if it had @brief return entity catch ParseError in error faldoc.error( @i"While parsing entity\n$error", line ) return nil end end function onGroupingChange( line, cmd, name ) switch cmd case "@begingroup" if self.current_group != nil faldoc.warn( @i"Beginning new group $name while group $self.current_group is not closed", line) end self.current_group = name case "@beginignore" if self.ignore faldoc.warn( @i"Beginning an ignore block, but was already ignoring", line) end faldoc.verbose( @i"Beginning ignore" ) self.ignore = true case "@beginset" if self.current_set != nil faldoc.warn( @i"Beginning new set $name while group $self.current_set is not closed", line) end self.current_set = name case "@beginmodule" if self.modName != nil faldoc.error( i"@beginmodule command invalid in source modules", line) elif self.current_module != nil faldoc.warn( @i"Beginning new module $name while group $self.current_set is not closed", line) end faldoc.verbose( @i"Changing @beginmodule to $(name)" ) self.current_module = faldoc.document.getModule( name ) case "@endgroup" if self.current_group == nil faldoc.warn( i"Parsing @endgroup, but no group open", line) end self.current_group = nil case "@endset" if self.current_set == nil faldoc.warn( i"Parsing @endset at but no set open", line) end self.current_set = nil case "@endmodule" if self.modName != nil faldoc.error( i"@endmodule command invalid in source modules", line) elif self.current_module == nil faldoc.warn( i"Parsing @endmodule at, but no module open", line) else faldoc.verbose( i"Closing module $(self.current_module.name)" ) end self.current_module = nil case "@endignore" if not self.ignore faldoc.warn( i"Parsing @endignore but wasn't ignoring.", line) end self.ignore = false end end //# Stores a new entity in the entity database. function addEntity( entity ) // if ignoring, throw it away if self.ignore or entity.type == "ignore" return end if entity.parent // nothing else needed faldoc.verbose( i"Entity " + entity.name + i" has parent " + entity.parent.name + ", ignoring." ) return end // determine the module of the entity. if "inmodule" in entity.props module = faldoc.document.getModule(entity.props["inmodule"]) faldoc.verbose( i"Applying @inmodule " + module.name + i" to entity " + entity.name ) if not module: return // error already signaled elif self.current_module module = self.current_module else module = self.defmod end // store in the current module if "owner" in entity.props // do we have an owner? -- then, it's also a parent. entity.parent = module.getEntity( entity.props["owner"] ) else // otherwise, we belong to the module entity.parent = module end // Finally, add the entity to its rightful parent entity.parent.addMember( entity ) // Ok, should we handle grouiping? if entity.type != "funset" and entity.type != "page" and entity.type != "group" and entity.type != "main" if self.current_group != nil or self.current_set!= nil if "ingroup" notin entity.props and "inset" notin entity.props and "owner" notin entity.props if self.current_group != nil entity.props["ingroup"] = self.current_group elif self.current_set!= nil entity.props["inset"] = self.current_set end end end // manage group ownership if "ingroup" in entity.props group = module.getEntity( entity.props["ingroup"] ) elif "inset" in entity.props group = module.getEntity( entity.props["inset"] ) end if group // this entity may have been neutralized if already existing // in that case, we want the already stored one to be grouped rentity = module.getEntity( entity.name ) group.addMember( rentity ) end end end //# Configures an entity with non-textual data. function apply( node, entity, parentEntity ) // if ignoring, throw it away if self.ignore return end l1 = node.infos["line"] if entity.name l2 = entity.firstline faldoc.warn( @i"Entity defined at $l2 redefined here", l1 ) end entity.name = node.infos["name"] entity.dline = l1 // if we have a parent, variables and functions must be added below them. if parentEntity and (node.type == "function" or node.type == "variable" ) // this is valid also for enums entity.type = node.type == "function" ? "method" : "property" parentEntity.addMember( entity ) entity.parent = parentEntity else entity.type = node.type self.addEntity( entity ) end end //# Creates an entity out of a non-text definition function createEntity( node, parentEntity ) if self.ignore return end entity = docentity.Entity( nil, self.file, node.infos["line"] ) entity.name = node.infos["name"] entity.dline = entity.firstline // if we have a parent, variables and functions must be added below them. if parentEntity and (node.type == "function" or node.type == "variable" ) // this is valid also for enums entity.type = node.type == "function" ? "method" : "property" parentEntity.addMember( entity ) entity.parent = parentEntity else entity.type = node.type self.addEntity( entity ) end return entity end end export FileContext apps/faldoc/src/faldoc/link.fal000066400000000000000000000042021176363201700167150ustar00rootroot00000000000000/* FALCON - Documentation tool FILE: link.fal Autodocumentation tool - Hypotetical or effective link between document entities. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 30 Sep 2010 19:07:41 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# A link between an entity and another one. @param source The entity that is searching for the link. @param line The line in file where the link is found. @param destination The target item of the link. @optparam explicit When true, the system will complain if not resolved. The link is created when a Generic Parser InfoNode of the proper type ("a") is found, or automatically when some other entities are parsed (i.e. "from" inheritance InfoNodes, "see" InfoNodes, "raise" and so on). The link is then added to the infonode as a "link" property, with one end referencing the entity where the infonode is located (source), and the other referencing a destination. If the destination exists when the link is created, it is immediately referenced. If it doesn't exist, an entry in the @a EntityDB.missing property is created, and the link is stored as the value of the missing dictionary, while the expected unique ID is set both as the destination of the link and as the key of the missing dictionary. When a new entity arrives, it is checked against the missing dictionary, and if a link is found there, the @a Link.target field gets referenced. */ class Link( source, line, destination, explicit ) source = source line = line destination = destination explicit = explicit target = nil function toString() if self.target return self.target.toString() end return @"(undef at $(self.source.file):$(self.line)) " + self.destination end function describe() return (@ "$(self.destination) at $(self.source.file):$(self.line) ") + (self.target ? "(*)" : "(-)") end end export apps/faldoc/src/faldoc/output/000077500000000000000000000000001176363201700166365ustar00rootroot00000000000000apps/faldoc/src/faldoc/output/docbook.fal000066400000000000000000000465101176363201700207500ustar00rootroot00000000000000/* FALCON - Documentation tool FILE: docbook.fal Autodocumentation tool - DOCBOOK output module ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 12 Jan 2008 12:21:30 +0100 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from parser.render.docbook in docbook // The faldoc_output function must be provided by all output modules function faldoc_output( doctree, conf ) DOCBOOKGenerator( doctree, conf ).go() end class FaldocBookRenderer( frame ) from docbook.Renderer( frame ) init self.renderers += [ "@section" => self.renderSection, "@note" => self.renderNote, "@a" => self.render_a ] end function render( context ) content = self.rcont( context.topNode ) return content end function renderSection( node ) docLevel = self.blevel front = self.open_section( docLevel, node.infos["name"], node.infos["title"] ) content = self.rcont(node) back = self.close_section( docLevel ) // the level will be closed when necessary return front + content + back end function renderNote( node ) content = self.rcont( node ) return "" + content + "\n" end function render_a( node ) l = node.infos["link"] return DOCBOOKGenerator.makeLink( l ) end end class DOCBOOKGenerator( doctree, conf ) doc = doctree conf = bless(conf) basedir = faldoc.config.basedir frame = nil file = "file" in conf ? conf["file"] : nil encoding = "encoding" in conf? conf["encoding"] : "utf-8" copyyear = "copyright.year" in conf? conf["copyright.year"] : nil copyholder = "copyright.holder" in conf? conf["copyright.holder"] : nil detailed = "detailed" in conf? conf["detailed"].lower().compare("true") == 0 : true //# Level of the document. docLevel = 0 _outfile = nil _rndr = nil _entityHandler = [ "page" => self.makePage, "variable" => self.makeVariable, "global" => self.makeVariable, "object" => self.makeObject, "class" => self.makeClass, "function" => self.makeFunction, "group" => self.makePage, "funset" => self.makePage, "enum" => self.makeEnum, "message" => self.makeMessage ] //# represents the section order _moduleOrder = .[ .["object" i"Objects"] .["class" i"Classes"] .["function" i"Functions"] .["global" i"Variables"] .["enum" i"Enumerations"] .["message" i"Messages" ] ] init if "frame" in conf switch conf["frame"] case "article" self.frame = docbook.ArticleFrame( self.encoding ) case "book" if self.file: fname = fileName( self.file ) self.frame = docbook.BookFrame( self.encoding, fname, self.doc.title, self.doc.author, self.copyyear, self.copyholder ) case "article5" if self.file: fname = fileName( self.file ) self.frame = docbook.Article5Frame( self.encoding ) case "book5" if self.file: fname = fileName( self.file ) self.frame = docbook.Book5Frame( self.encoding, fname, self.doc.title, self.doc.author, self.copyyear, self.copyholder ) default self.frame = docbook.ArticleFrame( self.encoding ) end end self._rndr = FaldocBookRenderer( self.frame ) end function go() faldoc.info( "=" * 60 ) faldoc.info( "DOCBOOK Generator module started." ) if not self.file faldoc.error( i"docbook output - Missing mandatory parameter \"file\"" ) return false end fpath = Path(self.file) if not fpath.fulloc.startsWith("/") self.file = self.basedir + "/" + self.file end faldoc.info( "docbook output - Writing to " + self.file ) try self._outfile = OutputStream( self.file ) self._outfile.setEncoding( self.encoding ) self._outfile.writeText( self.frame.open() ) // each element of the main module is a chapter self.docLevel = 0 self.makeModule( self.doc ) // other modules must be in their own chapter. for modname, module in self.doc.getModules() self.makeModule( module, true ) end self._outfile.writeText( self.frame.close() ) catch IoError in err faldoc.error( "docbook.fal: " + err ) return end if self._outfile: self._outfile.close() faldoc.info( "DOCBOOK Generator module ended." ) faldoc.info( "=" * 60 ) end function makeModule( module, isChildModule, isSubChild ) if module.isVirtual faldoc.verbose( @i"Skipping virtual module $(module.name)" ) for name, submod in module.getModules() self.makeModule( submod, true, isSubChild ) end return end faldoc.verbose( @i"Making module $(module.name)" ) docLevel = self.docLevel if module.main moduleId = module.getID() title = module.title ? module.title : i"Module " + moduleId self._outfile.writeText(self._rndr.open_section(docLevel, moduleId, title)) self.makeContent( module.main.pcontent ) if isChildModule self.docLevel ++ self._outfile.writeText("\n") else self._outfile.writeText(self._rndr.close_section(docLevel)) end end // create the module pages = module.orderPages() // do all the other pages for page in pages if page.type == "module" self.makeModule( page, true, false ) else self.makePage( page ) end end // do ungrouped entities entities = module.groupEntities() for etype in self._moduleOrder if etype.typeId() == StringType self.makeEntityList( etype, entities ) else self.makeEntityPage( etype[0], etype[1], entities ) end end if isChildModule if not isSubChild // do all the children childmods = module.getAllModules() for modname, submod in childmods self.makeModule( submod, true, true ) end end self._outfile.writeText( self._rndr.close_section(docLevel) ) end self.docLevel = docLevel end //=============================================================== // Handlers //=============================================================== function makeEntityList( etype, entities ) if etype in entities maker = self._entityHandler[ etype ] for entity in entities[ etype ] maker( entity ) //# todo - remove grouped entities. end end end function makeEntityPage( etype, chapName, entities ) done = false if etype in entities // section heading -- no need for xref here. dl = self.docLevel ++ maker = self._entityHandler[ etype ] for entity in entities[ etype ] if "ingroup" in entity.props or "inset" in entity.props: continue if not done done = true self._outfile.writeText( self._rndr.open_section( dl, "", chapName ) ) end maker( entity ) end if done self._outfile.writeText( self._rndr.close_section( dl ) ) end self.docLevel = dl end end function makeVariable( entity, prefixName ) if not prefixName: prefixName = i"Global" name = prefixName + " " + entity.name id = entity.type != "property" or self.detailed ? entity.getID() : nil self._outfile.writeText( self._rndr.open_section( self.docLevel, id, name ) ) self.makeBrief( entity ) self.makeContent( entity.pcontent ) self._outfile.writeText( self._rndr.close_section( self.docLevel ) ) end function makeContent( ctx ) if ctx data = self._rndr.render( ctx ) end self._outfile.writeText( data ? data : "\n" ) end function makeObject( entity ) self.makeObjectoid( entity, i"Object", true ) end function makeClass( entity ) self.makeObjectoid( entity, i"Class", true ) end function makeEnum( entity ) self.makeObjectoid( entity, i"Enumeration" ) end function makeObjectoid( entity, typeName, proto ) id = entity.getID() self._outfile.writeText( self._rndr.open_section( self.docLevel, id, typeName + " " + entity.name ) ) self.makeBrief( entity ) if proto: self.makePrototype( entity, true ) props, methods = entity.getPropsAndMethods() // todo - inherit list self.makePropsTable( props, methods ) // write the content self.makeContent( entity.pcontent ) if entity.members dl = self.docLevel self.docLevel ++ for property in props if property.pcontent self.makeVariable( property, i"Property" ) end end for met in methods if met.pcontent or "param" in met.props or "optparam" in met.props self.makeFunction( met, i"Method" ) end end self.docLevel = dl end self._outfile.writeText( self._rndr.close_section( self.docLevel ) ) end function makeFunction( entity, prefixName ) if not prefixName: prefixName = i"Function" name = prefixName + " " + entity.name id = entity.type != "method" or self.detailed ? entity.getID() : nil self._outfile.writeText( self._rndr.open_section( self.docLevel, id, name ) ) self.makeBrief( entity ) self.makePrototype( entity ) self.makeContent( entity.pcontent ) self._outfile.writeText( self._rndr.close_section( self.docLevel ) ) end function makePage( entity ) // If the group is undefined, it will have no title. name = ("title" in entity.props) ? entity.props["title"] : entity.name // define a chapter for xref -- can't ave prefix. self._outfile.writeText( self._rndr.open_section( self.docLevel, entity.getID(), name ) ) self.makeBrief( entity ) self.makeContent( entity.pcontent ) if entity.members dl = self.docLevel self.docLevel ++ for name, child in entity.members if child.type in self._entityHandler self._entityHandler[ child.type ]( child ) else faldoc.warn( @i"Page type $(child.type) not handled by docbook" ) end end self.docLevel = dl end self._outfile.writeText( self._rndr.close_section( self.docLevel ) ) end function makeMessage( entity ) end function makeBrief( entity ) if "brief" in entity.props node = entity.props["brief"] data = self._rndr.rcont( node ) if data self.opentag( "para" ) self.writeTag( "emphasis", data ) self.closetag( "para" ) end end end function makePrototype( entity, addType ) self.opentag( "para" ) self.opentag( "informaltable", ["frame"=>"none"] ) self.opentag( "tgroup" , ["cols"=>"3"] ) // 3 cols; self.writeTag( "colspec", "", ["colname"=>"c1", "colwidth"=>"15pt"] ) self.writeTag( "colspec", "", ["colname"=>"c2", "colwidth"=>"1*"] ) self.writeTag( "colspec", "", ["colname"=>"c3", "colwidth"=>"5*"] ) self.opentag( "tbody" ) self.opentag( "row", ["rowsep"=>"1"] ) self.opentag( "entry", ["namest"=>"c1", "nameend"=>"c3"] ) proto = entity.prototype() if addType: proto = entity.type + " " + proto if "from" in entity.props fr = entity.props["from"] proto = "" + proto + " from " for link, callexp in fr linkcal = self.makeLink( link ) cexp = callexp? "(" + self._rndr.rcont( callexp ) + ")" : "" proto += "\\ " +linkcal+ cexp end self._outfile.writeText( proto + "" ) else self.writeTag( "literal", proto ) end self.closetag( "entry" ) self.closetag( "row" ) if "param" in entity.props: self.makeParams( entity.props["param"] ) if "optparam" in entity.props: self.makeParams( entity.props["optparam"] ) if "return" in entity.props content = self._rndr.rcont( entity.props["return"] ) if content self.opentag( "row" ) self.writeTag( "entry", "" ) // make an empty col for indent self.opentag( "entry" ) self.writeTag( "emphasis", i"Returns" ) self.closetag( "entry" ) self.opentag( "entry" ) self._outfile.writeText( content ) self.closetag( "entry" ) self.closetag( "row" ) end end if "raise" in entity.props rlist = entity.props["raise"] self.makeRaise( rlist ) end self.closetag( "tbody" ) self.closetag( "tgroup" ) self.closetag( "informaltable" ) self.closetag( "para" ) end function makeParams( plist ) for name, content in plist // ignore the parameter if not documented if content self.opentag( "row" ) self.writeTag( "entry", "" ) // make an empty col for indent self.writeTag( "entry", name ) vcont = self._rndr.rcont( content ) self.opentag( "entry" ) self._outfile.writeText( vcont.trim() ) self.closetag( "entry" ) self.closetag( "row" ) end end end function makePropsTable( props, methods ) if not props and not methods: return self.opentag( "para" ) self.opentag( "informaltable", ["frame"=>"none"] ) self.opentag( "tgroup" , ["cols"=>"3"] ) // 3 cols; self.writeTag( "colspec", "", ["colname"=>"c1", "colwidth"=>"10pt"] ) self.writeTag( "colspec", "", ["colname"=>"c2", "colwidth"=>"2*"] ) self.writeTag( "colspec", "", ["colname"=>"c3", "colwidth"=>"3*"] ) self.opentag( "tbody" ) if props self.opentag( "row", ["rowsep"=>"1"] ) self.writeTag( "entry", i"Properties", ["namest"=>"c1", "nameend"=>"c3"] ) self.closetag( "row" ) for prop in props self.opentag( "row" ) self.writeTag( "entry", "" ) // make an empty col for indent self.writeTag( "entry", prop.name ) self.opentag( "entry" ) if "brief" in prop.props: self._outfile.writeText( self._rndr.rcont( prop.props["brief"] ) ) self.closetag( "entry" ) self.closetag( "row" ) end end if methods self.opentag( "row", ["rowsep"=>"1"] ) self.writeTag( "entry", i"Methods", ["namest"=>"c1", "nameend"=>"c3"] ) self.closetag( "row" ) for method in methods self.opentag( "row" ) self.writeTag( "entry", "" ) // make an empty col for indent self.writeTag( "entry", method.prototype() ) self.opentag( "entry" ) if "brief" in method.props: self._outfile.writeText( self._rndr.rcont( method.props["brief"] )) self.closetag( "entry" ) self.closetag( "row" ) end end self.closetag( "tbody" ) self.closetag( "tgroup" ) self.closetag( "informaltable" ) self.closetag( "para" ) end function makeRaise( rlist ) self.opentag( "row" ) self.writeTag( "entry", "" ) // make an empty col for indent self.opentag( "entry" ) self.writeTag( "emphasis", i"Raises" ) self.closetag( "entry" ) /* self.opentag( "entrytbl", ["cols"=>"2"] ) self.writeTag( "colspec", "", ["colname"=>"c1", "colwidth"=>"1*"] ) self.writeTag( "colspec", "", ["colname"=>"c2", "colwidth"=>"5*"] ) self.opentag( "tbody" ) for link, content in rlist self.opentag( "row" ) self.writeTag( "entry", link.destination ) // make an empty col for indent ctx = self._rndr.rcont( content ) self.writeTag( "entry", ctx ) self.closetag( "row" ) end self.closetag( "tbody" ) self.closetag( "entrytbl" ) */ self.opentag( "entry" ) for link, content in rlist self.opentag( "simpara" ) self.opentag( "emphasis", ["role"=>"bold"] ) vcont = self.makeLink( link ) self._outfile.writeText( vcont.trim() ) self.closetag( "emphasis" ) ctx = self._rndr.rcont( content ).trim() self._outfile.writeText( " - " + ctx ) self.closetag( "simpara" ) end self.closetag( "entry" ) self.closetag( "row" ) end function makeLink( l ) // ingore links to empty elements. if l.target and (l.target.pcontent or "title" in l.target.props) target = l.target if (not self.detailed) and ( target.type == "property" or target.type == "method" ) target = target.parent end if "title" in l.target.props text = target.props["title"] else text = l.destination end return ""+text+"" else return l.destination end end //=============================================================== // Utilities //=============================================================== function getAllModules( module ) mods = [=>] for name, m in module.members if m.type == "module" mods[ m.getID() ] = m mods += self.getAllModules( m ) end end return mods end function opentag( tag, attribs ) att = "" for k,v in attribs forfirst: att = " " att += k + '="' + v.replace('"', '\"') + '"' formiddle: att += " " end self._outfile.writeText( @"<$tag$att>\n" ) end function closetag( tag ) self._outfile.writeText( @"\n" ) end function write( xtag ) self._outfile.writeText( xtag.render() ) end function writeTag( tag, content, attribs ) content = docbook.Renderer.sgmlEscape(content) att = "" for k,v in attribs forfirst: att = " " att += k + '="' + v.replace('"', '\"') + '"' formiddle: att += " " end if not content self._outfile.writeText( "<"+ tag + att +"/>\n" ) else self._outfile.writeText( "<"+ tag + att +">" + content + "\n" ) end end end class Tag( tag, content, attribs ) attribs = attribs tag = tag content = content function render() if self.content.typeId() == StringType content = docbook.Renderer.sgmlEscape(self.content) else content = "" for tag in self.content content += tag.render() end end att = "" for k,v in self.attribs forfirst: att = " " att += k + '="' + v.replace('"', '\"') + '"' formiddle: att += " " end if not content self._outfile.writeText( "<"+ tag + att +"/>\n" ) else self._outfile.writeText( "<"+ tag + att +">" + content + "\n" ) end end end apps/faldoc/src/faldoc/output/html.fal000066400000000000000000000710131176363201700202700ustar00rootroot00000000000000/* FALCON - Documentation tool FILE: faldoc_output_html.fal Autodocumentation tool - HTML output module ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 12 Jan 2008 12:21:30 +0100 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from web.htmaker in ht import from parser.render.html in render import from struct.tree in tree // global location for our generator _generator = nil //# A page in our final documentation //# The content may be a single entity class Page(title, file, master_entity ) from tree.Node content = master_entity title = title file = file doc = nil stream = nil //# open this page function openDoc() file = OutputStream( _generator.directory + "/" + self.file ) file.setEncoding( _generator.encoding ) self.stream = file self.doc = _generator.htframe() self.doc.html.head = self.makeHead( _generator.doc.title, self.title ) end //#close this page function closeDoc() self.doc.render( self.stream ) self.stream.close() end function makeHead( category, itemName ) title = category + itemName ? " - " + itemName : "" return ht.Head().add( ht.Meta( nil, @"text/html;charset=\"$(_generator.encoding)\"", "Content-type" ), ht.Link( "faldoc.css", "stylesheet", "text/css"), ht.Title( title )) end //# To be overridden by subclasses function makeBody( body ) body.add( ht.P().add( "Unknown page type. You must override this page." ) ) end //# To be called to make this page function make() self.openDoc() body = ht.Body().CLASS("faldoc") navi = self.makeNavi() navi.CLASS( "navi_top" ) body.add( navi ) div = ht.Div().id("page_body") self.makeBody( div ) body.add( div ) navi = self.makeNavi() navi.CLASS( "navi_bottom" ) body.add( navi ) self.makeSignature( body ) self.doc.html.body = body self.closeDoc() end function makeSignature( body ) body.add( ht.Div().CLASS("signature").add( "Made with " ).add( ht.A( "faldoc 3.0", "http://www.falconpl.org" ) ) ) end //# Creates the navigation div function makeNavi() div = ht.UL() // top node node = self.parent while node and node.parent node = node.parent end if node self.makeNaviItem( div, node , "top", i"Top" ) end // up node if self.parent and self.parent != node self.makeNaviItem( div, self.parent, "up", i"Up" ) end // prev-next pair if self.prev self.makeNaviItem( div, self.prev, "prev", i"Previous" ) elif self.parent self.makeNaviItem( div, self.parent, "prev", i"Previous" ) else div.add( ht.LI().CLASS("prev") ) end if self.firstChild self.makeNaviItem( div, self.firstChild, "next", i"Next" ) elif self.next self.makeNaviItem( div, self.next, "next", i"Next" ) else node = self.parent while node and not node.next node = node.parent end if node self.makeNaviItem( div, node.next, "next" ) else div.add( ht.LI().CLASS("next") ) end end div.add( ht.LI().CLASS("clear") ) return div end function makeNaviItem( div, page, cls, title ) desc = page.title if desc.len() > 36: desc = desc[0:34] + "..." if title: desc = title + ": " + desc div.add( ht.LI().CLASS(cls).add(ht.A( page.file, desc ) ) ) end end class IndexPage( mainPage ) from Page( i"Table of contents", "index.html", mainPage ) function makeBody( body ) body.add( ht.H1( _generator.doc.title ) ) // the main module is not required to have a main description. if self.content body.add( ht.H2( self.content.props["title"] ) ) body.add( _generator.makeContent( self.content ) ) end body.add( ht.H2( self.title ) ) // table of contents // all the other pages must be our childern. itemList = ht.UL().CLASS("TOC") self.addChildren( self.firstChild, itemList, 0 ) body.add( itemList ) end function addChildren( node, itemList, level, pid ) id = 1 while node != nil text_id = pid ? pid + "." + id : toString(id) if node.content provides props and "html_toc_id" notin node.content.props node.content.props["html_toc_id"] = text_id end li = _generator.makeNodeLink( node ) /* li = ht.LI().add(ht.Span(text_id).CLASS("toc_number")).add( ht.A( node.file, node.title ) ) entity = node.content if entity provides props and "brief" in entity.props brief = _generator.rcont(entity.props["brief"]) if brief: li.add( " - " ).add( ht.Verbatim( brief ) ) end */ // do we have subitems? if node.firstChild il1 = ht.UL().CLASS("TOC") self.addChildren( node.firstChild, il1, level+1, text_id ) li.add( il1 ) end itemList.add( li ) node = node.next ++id end end end class GroupingPage( entity ) from Page( entity.props["title"], _generator.getFileName(entity), entity ) function makeBody( body ) entity = self.content faldoc.verbose( @i"Making page $(entity.type) $(entity.name)" ) title = self.title body.add( ht.H1().add(ht.Span(entity.props["html_toc_id"]).CLASS("toc_number")).add( title ), _generator.makeBrief( entity ), _generator.makeContent( entity ) ) if entity.members members = entity.membersByType() // Create the entities using a consistent order. for type, listName, ename in _generator.moduleOrder if type in members mlist = members[type] body.add( ht.H2( listName ) ) // if the entity is heavy, just place a link if ename elemList = ht.UL().CLASS("memberlist") for entity in mlist elemList.add( _generator.makeMemberLink( entity ) ) end body.add( elemList ) else // it's light. Add the entities. for entity in mlist _generator.makeIntraPageMember( body, entity ) end end end end end // see also... if "see" in entity.props body.add( _generator.makeSeeAlso( entity.props["see"] ) ) end end end class ObjectoidPage( entity, typeName, proto ) from Page( typeName + " " + entity.name, _generator.getFileName(entity), entity ) typeName = typeName proto = proto function makeBody( body ) entity = self.content faldoc.verbose( @i"Making objectoid $(entity.type) $(entity.name)" ) body.add( ht.H1().add( ht.Span(entity.props["html_toc_id"]).CLASS("toc_number")).add( self.title ), _generator.makeBrief( entity ) ) if self.proto _generator.makePrototype( body, entity, self.typeName ) end body.add( _generator.makeContent( entity ) ) if entity.members members = entity.membersByType() // we know we have at least a member, so we have a table. body.add( _generator.makePropsTable( members ) ) end // Generate inherited properties inherited = _generator.makeInherited( entity, [] ) if inherited: body.add( inherited ) if entity.members // then print the properties and the methods. // make the property list if "property" in members faldoc.verbose( @i"Making properties of objectoid $(entity.type) $(entity.name)" ) body.add( ht.H2( i"Properties" ) ) for prop in members["property"] _generator.makeIntraPageMember( body, prop ) end end // then print the properties and the methods. // make the property list if "method" in members faldoc.verbose( @i"Making methods of objectoid $(entity.type) $(entity.name)" ) body.add( ht.H2( i"Methods" ) ) for prop in members["method"] _generator.makeIntraPageMember( body, prop ) end end end // see also... if "see" in entity.props body.add( _generator.makeSeeAlso( entity.props["see"] ) ) end end end class ModulePage( module ) from IndexPage( module ) // override indexpage defaults. title = module.title ? module.title : "Module" + " " + module.getID() file = _generator.getFileName( module ) function makeBody( body ) module = self.content body.add( ht.H1().add(ht.Span(module.props["html_toc_id"]).CLASS("toc_number")).add( self.title ) ) // Submodules may have a description that we shoudl put here. if module.main body.add( _generator.makeContent( module.main )) end body.add( ht.H2( i"Contents of this module" ) ) // table of contents // all the other pages must be our childern. itemList = ht.UL().CLASS("TOC") self.addChildren( self.firstChild, itemList, 0 ) body.add( itemList ) end end class MetaPage( title, file, entity_list ) from Page( title, file, nil ) entlist = entity_list content = bless(["props"=>[=>]]) function makeBody( body ) entlist = self.entlist body.add( ht.H1().add(ht.Span(self.content.props["html_toc_id"]).CLASS("toc_number")).add( self.title ) ) for ent in entlist _generator.makeIntraPageMember( body, ent ) end return body end end class AlphaIndexPage( doc ) from Page( i"Alphabetical Index", "__alphaindex.html", doc ) function makeBody( body ) faldoc.info( i"Making alphabetical index page." ) dict = [=>] self.makeAlpha( self.content, dict ) for modname, module in self.content.getAllModules() self.makeAlpha( module, dict ) end body.add( ht.H1() ) // ok, for each letter of the alphabeth... for letter, entlist in dict body.add( ht.H3( letter ).CLASS("alpha") ) para = ht.P().CLASS("alpha") for name, entity in entlist // skip virtual moduls, already detected. if entity provides isVirtual and entity.isVirtual: continue para.add( ht.A( _generator.getLinkName( entity ), name) ) para.add( ", " + @i"$(entity.type)" ) try if "ingroup" in entity.props group = entity.parent.members[entity.props["ingroup"]] para.add( " " + i"in group" + " ") para.add( ht.A( _generator.getFileName( group ), group.props["title"]) ) end if "inset" in entity.props group = entity.parent.members[entity.props["inset"]] para.add( " " + i"in set" + " ") para.add( ht.A( _generator.getFileName( group ), group.props["title"]) ) end catch AccessError end para.add(ht.BR()) end body.add( para ) end end function makeAlpha( module, dict ) for name, entity in module.members // skip virtual moduls, already detected. if entity provides isVirtual and entity.isVirtual: continue if not name: continue initial = name[0].upper() if initial in dict dict[initial] += [entity.name => entity] else dict[initial] = [entity.name => entity] end end end end // The faldoc_output function must be provided by all output modules function faldoc_output( doctree, conf ) global _generator (_generator = HTMLGenerator( doctree, conf )).go() end function _htmlEscape( string ) return string.replace( "&", "&" ).replace( "<", "<").replace(">", ">").replace("\"", """) end class FaldocHtmlRenderer() from render.Renderer() level = 0 init self.renderers += [ "@section" => self.renderSection, "@note" => self.renderNote, "@a" => self.render_a ] end /* function render( context ) content = self.rcont( context.topNode ) return content end */ function renderSection( node ) hl = self.level + 2 title = _htmlEscape(node.infos["title"]) link = strEscape(node.infos["name"]) prefix = @"$(title)\n" ++self.level content = self.rcont(node) --self.level // the level will be closed when necessary return prefix + content end function renderNote( node ) content = self.rcont( node ) return "

"+i"Note"+": "+ content + "

\n" end function render_a( node ) l = node.infos["link"] if l.target target = l.target if "title" in l.target.props text = target.props["title"] else text = l.destination end return ""+ text.replace("&","&").replace("<","<").replace(">", ">") + "" else return ""+ l.destination.replace("&","&").replace("<","<").replace(">",">") +"" end end end class HTMLGenerator( doctree, conf ) doc = doctree conf = bless(conf) basedir = faldoc.config.basedir directory = "directory" in conf ? conf["directory"] : nil htmltitle = "title" in conf ? conf["title"] : doctree.title baseurl = "url" in conf ? conf["url"] : nil encoding = "encoding" in conf? conf["encoding"] : "utf-8" _rndr = FaldocHtmlRenderer() //htframe = ht.TransitionalFrame htframe = ht.XHTMLFrame //# represents the section order moduleOrder = .[ .["object" i"Objects" i"Object"] .["class" i"Classes" i"Class"] .["function" i"Functions" ""] .["global" i"Variables" "" ] .["enum" i"Enumerations" i"Enum"] .["message" i"Messages" ""] ] function go() faldoc.info( "=" * 60 ) faldoc.info( "HTML Generator module started." ) if not self.directory faldoc.error( i"html output - Missing mandatory parameter \"directory\"" ) return false end fpath = Path() fpath.fulloc = self.directory if not fpath.fulloc.startsWith("/") self.directory = self.basedir + "/" + self.directory end faldoc.info( "html output - Writing to " + self.directory ) try // prepare the directory dirMake( self.directory, true ) // copy resource files faldoc.copyResource( "faldoc.css", self.directory ) // add the index page... ip = IndexPage(self.doc.main) // and prepare the tree for the main module self.makeTree( ip, self.doc ) // then prepare the tree for the submodules for modname, module in self.doc.getModules() self.makeModule( ip, module ) end // finally create the appendix pages. ip.appendChild( AlphaIndexPage( self.doc ) ) // and make self.makePages( ip ) catch IoError in err faldoc.error( "html.fal: " + err ) return end faldoc.info( "HTML Generator module ended." ) faldoc.info( "=" * 60 ) end function makeModule( page, module ) // skip virtual modules if module.isVirtual faldoc.verbose( @"Skipping virtual module $module.name" ) for name, subModule in module.getModules() self.makeModule( page, subModule ) end return end // Else, create a module page and proceed. faldoc.verbose( @"Making module $module.name" ) ip_mod = ModulePage( module ) self.makeTree( ip_mod, module ) page.appendChild( ip_mod ) for name, subModule in module.getModules() self.makeModule( ip_mod, subModule ) end end function makeTree( topPage, module ) pages = module.orderPages() for page in pages if page.type == "module" self.makeModule( topPage, page ) else pt = GroupingPage( page ) self.makePageTree( pt, page ) topPage.appendChild( pt ) end end entities = module.groupEntities() for type, type_title, type_name in self.moduleOrder if type_name if type in entities for entity in entities[type] // skip entities in groups or sets if "ingroup" in entity.props or "inset" in entity.props: continue topPage.appendChild( ObjectoidPage( entity, type_name, type != "enum" ) ) end end else if type in entities glob_entities = [] for entity in entities[type] if "ingroup" in entity.props or "inset" in entity.props: continue // we found at least one entity that goes in the generic page glob_entities += entity end if glob_entities topPage.appendChild( MetaPage( type_title, self.getFilenameForType( type, module ), glob_entities ) ) end end end end end function makePageTree( topPage, pentity ) entities = pentity.groupEntities() for type, type_title, type_name in self.moduleOrder // only heavy types generate a sub-page if type in entities and type_name for entity in entities[type] topPage.appendChild( ObjectoidPage( entity, type_name, type != "enum" ) ) end end end end function makePages( topPage ) topPage.make() node = topPage.firstChild while node self.makePages( node ) node = node.next end end //=============================================================== // Handlers //=============================================================== function renderContent( node ) return self._rndr.rcont( node ) end function makeIntraPageMember( struct, entity ) // add the title struct.add( ht.H3().add( ht.A().set( ["name"=>entity.name] ).add(entity.name ) ) ) // add the brief struct.add( self.makeBrief( entity ) ) // add the prototype if entity.type == "function" or entity.type == "method" self.makePrototype( struct, entity ) end // finally, add the body struct.add( self.makeContent( entity ) ) // see also... if "see" in entity.props struct.add( self.makeSeeAlso( entity.props["see"] ) ) end end function makeBrief( entity ) if "brief" in entity.props content = self._rndr.rcont( entity.props["brief"] ) if content: return ht.P().CLASS("brief").add( ht.Verbatim( content ) ) end return ^+ "" end function makePrototype( struct, entity, typeName ) proto = entity.prototype() if typeName: proto = typeName + " " + proto protoEntry = ht.Pre().CLASS("prototype").add( proto ) if "from" in entity.props fr = entity.props["from"] protoEntry.add( " from " ) for link, callexp in fr linkcal = self.makeLink( link ) cexp = callexp? "(" + self._rndr.rcont( callexp ) + ")" : "" protoEntry.add( "\\\n " ) protoEntry.add(linkcal) protoEntry.add(cexp) end end struct.add( protoEntry ) table = self.makePrototable( entity ) if table struct.add(table) end end function makeInherited( entity, done, table ) if "from" notin entity.props: return fr = entity.props["from"] subclasses = [] for link, callexp in fr // Gets the members. parent = link.target // was that an unresolved link? if not parent: continue if parent in done: continue done += parent if parent.members members = parent.membersByType() if "property" in members if not table: table = ht.TABLE().CLASS( "members" ) self.makePropsTablePart( table, i"Properties", members["property"], parent ) end if "method" in members if not table: table = ht.TABLE().CLASS( "members" ) self.makePropsTablePart( table, i"Methods", members["method"], parent ) end end if "from" in parent.props subclasses += parent end end for parent in subclasses table = self.makeInherited( parent, done, table ) end return table end function makePrototable( entity ) tbody = nil if "param" in entity.props tbody = ht.TBody() for paraName, paraContent in entity.props["param"] // just ignore undocumented parameters if paraContent tbody.add( ht.TR().CLASS("param").add( ht.TD().CLASS("name").add( paraName ), ht.TD().CLASS("content").add( ht.Verbatim(self._rndr.rcont( paraContent )) ) )) end end end if "optparam" in entity.props if not tbody: tbody = ht.TBody() for paraName, paraContent in entity.props["optparam"] // as optional parameters must be explicitly declared, they cannot be undoc tbody.add( ht.TR().CLASS("optparam").add( ht.TD().CLASS("name").add( paraName ), ht.TD().CLASS("content").add( ht.Verbatim(self._rndr.rcont( paraContent )) ) )) end end if "return" in entity.props if not tbody: tbody = ht.TBody() tbody.add( ht.TR().CLASS("return").add( ht.TD().CLASS("name").add( i"Return" ), ht.TD().CLASS("content").add( ht.Verbatim(self._rndr.rcont( entity.props["return"] )) ) )) end if "raise" in entity.props if not tbody: tbody = ht.TBody() tb2 = ht.TBody() for link, content in entity.props["raise"] tb2.add( ht.TR().add( ht.TD().CLASS("name").add( self.makeLink( link ) ), ht.TD().CLASS("content").add( ht.Verbatim(self._rndr.rcont( content )) ) )) end tbody.add( ht.TR().CLASS("raise").add( ht.TD().CLASS("name").add( i"Raise" ), ht.TD().CLASS("content").add( ht.TABLE().add(tb2) ) )) end if tbody return ht.TABLE().CLASS( "prototype" ).add( tbody ) end end function makePropsTable( members ) table = nil if "property" in members table = ht.TABLE().CLASS( "members" ) self.makePropsTablePart( table, i"Properties", members["property"] ) end if "method" in members if not table: table = ht.TABLE().CLASS( "members" ) self.makePropsTablePart( table, i"Methods", members["method"] ) end return table end function makePropsTablePart( table, title, members, parent ) tbody = ht.TBody() if parent plink = ht.A( self.getLinkName( parent ), parent.name ) title = @i"$(title) inherited from class" end titlecol = ht.TD( title ).set( ["colspan" => "2", "CLASS"=>"member_type"] ) if plink titlecol.add(" ").add(plink ) end tbody.add( ht.TR().CLASS("member_type").add( titlecol ) ) for prop in members if parent tdName = ht.TD().add( ht.A( self.getLinkName( prop ), prop.name ) ) else tdName = ht.TD().add( ht.A( "#" + prop.name, prop.name ) ) end row = ht.TR().add( tdName ) if "brief" in prop.props row.add( ht.TD().add( ht.Verbatim( self._rndr.rcont( prop.props["brief"] ) ))) else tdName.set(["colspan"=>"2"]) end tbody.add(row) end table.add( tbody ) end function makeSeeAlso( seeList ) para = ht.P(i"See also" + ": ").CLASS("see_also") for link, content in seeList para.add( self.makeLink(link) ) formiddle: para.add( ", " ) forlast: para.add( "." ) end return para end function makeLink( l ) // ingore links to empty elements. if l.target target = l.target if "title" in l.target.props text = target.props["title"] else text = l.destination end return ht.A( self.getLinkName( target ), text ) else return ht.B( l.destination ) end end function makeContent( entity ) if entity.pcontent content = self._rndr.rcont( entity.pcontent.topNode ) if content: return ht.Verbatim( content ) end return ^+ "" end //=============================================================== // Utilities //=============================================================== // Gets the filename, and eventually the link position, where an entity should be placed. function getFileName( entity, forlink ) if entity.type == "property" or entity.type == "method" if entity.parent return entity.parent.getID().replace( ".", "_" ) + ".html" + (forlink ? "#" + entity.name : "") end // only functions and variables, when in groups, are hosted in the parent item. elif "ingroup" in entity.props and not self.isFat(entity) return entity.parent.members[entity.props["ingroup"]].getID().replace( ".", "_" ) + ".html" + (forlink ? "#" + entity.name : "") // function sets own their items. elif "inset" in entity.props return entity.parent.members[entity.props["inset"]].getID().replace( ".", "_" ) + ".html" + (forlink ? "#" + entity.name : "") // Unbound functions and globals have their own file. elif not self.isFat( entity ) filename = self.getFilenameForType( entity.type, entity.parent ) if forlink: filename += "#" + entity.name return filename // otherwise, the entity has its own name (but "#main" should be "main"). else return entity.getID().replace( ".", "_" ).replace("#", "") + ".html" end end //# Return the filename of pages holding types (functions, messages, variables...) function getFilenameForType( type, parent ) if parent filename = parent.getID().replace(".","_") else filename = "" end filename += "_" + type + ".html" return filename end function getLinkName( entity ) return self.getFileName( entity, true ) end //# Returns true if the entity is "massive", and should have its own page. function isFat( entity ) static fatness = [ "object" => true, "class" => true, "enum" => true, "group" => true, "page" => true, "funset" => true, "main" => true, "module" => true ] end return entity.type in fatness end //# Creates a node to be directly added in a member list (OL of class "memberlist") function makeMemberLink( member ) node = ht.LI().add( ht.Span(member.props["html_toc_id"]).CLASS("toc_number")).add( ht.A( self.getFileName(member, true ), member.name )) if "brief" in member.props brief = self._rndr.rcont(member.props["brief"]) if brief: node.add( " - " ).add( ht.Verbatim( brief ) ) end return node end //# Creates a node to be directly added in a member list (OL of class "memberlist") function makeNodeLink( node ) member = node.content li = ht.LI() if member provides props and "html_toc_id" in member.props li.add( ht.Span(member.props["html_toc_id"]).CLASS("toc_number")) end li.add(ht.A( node.file, node.title )) if member provides props and "brief" in member.props brief = self._rndr.rcont(member.props["brief"]) if brief: li.add( " - " ).add( ht.Verbatim( brief ) ) end return li end end apps/faldoc/src/faldoc/output/html_handlers.fal000066400000000000000000000502451176363201700221540ustar00rootroot00000000000000/* FALCON - Documentation tool FILE: faldoc_output_html.fal Autodocumentation tool - HTML output module - page handlers ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 14 Jun 2009 12:28:04 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ resources: "./*.html" //=========================================================== // Create the main page // function makeMain( r ) // select the main page, if any if "main" in r.doctree.entities.grouping mainpage = r.doctree.entities.grouping["main"][0] // there can be only one main. mtitle = mainpage.props["title"].renderThrough( htmlTextRender ) else mainpage = nil mtitle = r.doctree.title end if mainpage content = mainpage.content.renderThrough( htmlTextRender ) end r.setCurrent( "main" ) data = make_default_data(r) + [ "heading" => mtitle, "page_title" => mtitle, "page_content" => content ] main = r.tgdir + "/index.html" out = OutputStream( main ) r.tplHeader.render( data, out ) r.tplPage.render( data, out ) r.tplFooter.render( data, out ) out.close() end //=========================================================== // Create listing pages (pages with link to other pages) // (table member) function make_listingPage( renderer, row ) renderer.setCurrent( row.type ) list = renderer.doctree.entities.grouping[row.type] url = renderer.config["url"] + "/" items = [] for elem in list id = elem.name name = "title" in elem.props ? elem.props["title"].renderThrough( htmlTextRender ) : elem.name brief = "brief" in elem.props ? elem.props["brief"].renderThrough( htmlTextRender ) : "" item = [ "item_link" => url + (@row.detail) + ".html", "item_name" => name, "item_brief" => brief ] items += item end items.sort( {a,b => a["item_name"].compare( b["item_name"] ) } ) data = make_default_data( renderer ) + [ "heading" => row.ititle, "entity_name" => row.ititle, "items" => items ] file = renderer.tgdir + "/" + row.index + ".html" out = OutputStream( file ) renderer.tplHeader.render( data, out ) renderer.tplListing.render( data, out ) renderer.tplFooter.render( data, out ) out.close() end //=========================================================== // Create listing pages (pages with link to other pages) // (table member) function make_page( renderer ) make_listingPage( renderer, self ) list = renderer.doctree.entities.grouping[self.type] renderer.setCurrent( "" ) data = make_default_data( renderer ) for page in list id = page.name file = renderer.tgdir + "/" + (@self.detail) + ".html" title = page.props["title"].renderThrough( htmlTextRender ) content = page.content.renderThrough( htmlTextRender ) data["heading"] = data["page_title"] = title data["page_content"] = content out = OutputStream( file ) renderer.tplHeader.render( data, out ) renderer.tplPage.render( data, out ) renderer.tplFooter.render( data, out ) out.close() end end //=========================================================== // Create group pages (Main group index and all groups) // (table member) function make_group( renderer ) make_listingPage( renderer, self ) renderer.setCurrent( "" ) list = renderer.doctree.entities.grouping[self.type] url = renderer.config["url"] + "/" global_data = make_default_data( renderer ) for elem in list id = name = elem.name title = "title" in elem.props ? elem.props["title"].renderThrough( htmlTextRender ) : elem.name file = renderer.tgdir + "/" + (@ self.detail) + ".html" title = title.trim() data = [ "heading" => @ self.dtitle, "entity_name" => @ self.dtitle ] if "brief" in elem.props data["brief"] = elem.props["brief"].renderThrough( htmlTextRender ) end if elem.content data["description"] = elem.content.renderThrough( htmlTextRender ) end if "members" in elem.props items = [] for name, entity in elem.props["members"] brief = "brief" in entity.props ? entity.props["brief"].renderThrough( htmlTextRender ) : "" item = [ "item_link" => make_link_to( entity ), "item_name" => name, "item_brief" => brief ] items += item end data["items"] = items end out = OutputStream( file ) tdata = global_data + data renderer.tplHeader.render( tdata, out ) renderer.tplListing.render( tdata, out ) renderer.tplFooter.render( tdata, out ) out.close() end end //=========================================================== // Create Listing/details pages (classes, objects etc.) // (table member) function make_ld( renderer ) // first, the list make_listingPage( renderer, self ) // then the detail renderer.setCurrent( "" ) list = renderer.doctree.entities.grouping[self.type] url = renderer.config["url"] + "/" global_data = make_default_data( renderer ) for elem in list id = name = elem.name file = renderer.tgdir + "/" + (@ self.detail) + ".html" title = "title" in elem.props ? \ '"' + elem.props["title"].renderThrough( htmlTextRender ).trim() + '"' : \ elem.name data = [=>] data["heading"] = @ self.dtitle data["title"] = @ self.dtitle data["brief"] = "brief" in elem.props ? elem.props["brief"].renderThrough( htmlTextRender ) : "" if "inmodule" in elem.props l = elem.props["inmodule"] name = l.target and "title" in l.target.props ? \ l.target.props["title"].renderThrough( htmlTextRender ) : l.destination data["module_name"] = "[in " + name.trim() + "]" data["module_link"] = l.target ? make_link_to(l.target) : "#" end cpt = _makeCallPrototype( elem ) from_cpt = _makeFrom( elem ) if cpt data["grammar"] = "" + elem.type + " " + cpt + from_cpt end idata = _prepareItemEntity( elem ); if "item_params" in idata: data["item_params"] = idata["item_params"] if "item_optparams" in idata: data["item_optparams"] = idata["item_optparams"] if elem.content data["description"] = elem.content.renderThrough( htmlTextRender ) end // determine also inherited properties if "from" in elem.props inherit_props = [] inherit_methods = [] for link, callproto in elem.props["from"] dest = link.target // add the properties of this class if dest provides props // but only if dest is a real class that has been actually found props, methods = _makeInherited( dest ) inherit_props += props inherit_methods += methods end end if inherit_props: data["inherit_props"] = inherit_props if inherit_methods: data["inherit_methods"] = inherit_methods end sections = [] if "members" in elem.props methods = [] props = [] members = [] for name, prop in elem.props["members"] p = _prepareItemEntity( prop, elem.name ) switch prop.type case "method" methods += p case "property" props += p default // we'll manage init later. members += p end end if props: sections += [ "section_title" => "Properties", "items" => props ] if methods: sections += [ "section_title" => "Methods", "items" => methods ] if members: sections += [ "section_title" => "Members", "items" => members ] end if sections: data["sections"] = sections out = OutputStream( file ) tdata = global_data + data renderer.tplHeader.render( tdata, out ) renderer.tplDetails.render( tdata, out ) renderer.tplFooter.render( tdata, out ) out.close() end end function _makeInherited( elem ) brief = "brief" in elem.props ? elem.props["brief"] : "" if "members" in elem.props methods = [] props = [] for name, prop in elem.props["members"] switch prop.type case "method" p = _prepareItemEntity( prop, elem.name, "method" ) p["method_origin"] = elem.name p["method_link"] = make_link_to( prop ) methods += p case "property" p = _prepareItemEntity( prop, elem.name, "prop" ) p["prop_origin"] = elem.name p["prop_link"] = make_link_to( prop ) props += p end end return [props, methods] end return [nil, nil] end //=========================================================== // Create "all" for this type of entity (i.e. all functions). // (table member) function make_all( renderer ) list = renderer.doctree.entities.grouping[self.type] renderer.setCurrent( "" ) data = [ "charset" => "utf-8", "title" => renderer.doctree.title, "version" => version.name(), "doclinks" => renderer.doclinks ] file = renderer.tgdir + "/" + self.index + ".html" data["heading"] = data["page_title"] = self.ititle items = [] for elem in list items += _prepareItemEntity( elem ) end data["items"] = items out = OutputStream( file ) renderer.tplHeader.render( data, out ) renderer.tplFullList.render( data, out ) renderer.tplFooter.render( data, out ) out.close() end //=========================================================== // Create a modules. // (table member) function make_module( renderer ) make_listingPage( renderer, self ) renderer.setCurrent( "" ) global_data = make_default_data( renderer ) // module entries doesn't strore items. We must build the listing here. for module in renderer.doctree.entities.grouping["module"] // are there items subscribed to this module? if module.name in renderer.doctree.entities.modules members = renderer.doctree.entities.modules[module.name] mod_types = [=>] for entity in members if entity.type in mod_types mod_types[entity.type] += entity else mod_types[entity.type] = [entity] end end // now we can create the proper sections sections = [] for type, entity_list in mod_types items = [] for entity in entity_list brief = "brief" in entity.props ? entity.props["brief"].renderThrough( htmlTextRender ) : "" items += [ "item_link" => make_link_to( entity ), "item_name" => entity.name, "item_brief" => brief ] end sections += [ "section_title" => pagetable.find( "type", type, "ititle" ), "items" => items ] end end name = "title" in module.props ? module.props["title"].renderThrough( htmlTextRender ) : module.name description = module.content ? module.content.renderThrough( htmlTextRender ): "" brief = "brief" in module.props ? module.props["brief"].renderThrough( htmlTextRender ) : "" data = global_data + [ "entity_type" => "Module", "entity_name" => name, "sections" => sections, "brief" => brief, "description" => description ] id = module.name file = renderer.tgdir + "/" + (@self.detail) + ".html" out = OutputStream( file ) renderer.tplHeader.render( data, out ) renderer.tplListing.render( data, out ) renderer.tplFooter.render( data, out ) out.close() end end //====================================================================================================== // Generic utilities. // function _prepareItemEntity( elem, parentName, prefix ) if not prefix: prefix = "item" name = parentName and elem.name.find( parentName ) == 0 ? \ elem.name[parentName.len()+1:] : elem.name item_data = [ prefix + "_name" => name ] if elem.type == "method" or elem.type == "function" item_data[prefix + "_title"] = name + "()" item_data[prefix + "_grammar"] = _makeCallPrototype( elem ) else item_data[prefix + "_title"] = name if elem.type == "message" item_data[prefix + "_grammar"] = _makeCallPrototype( elem ) end end if "brief" in elem.props item_data[ prefix + "_brief" ] = elem.props["brief"].renderThrough( htmlTextRender ) else item_data[ prefix + "_brief" ] = "" end if "param" in elem.props // confirm we want a descriptior table item_data[prefix + "_descriptor_table"] = true params = [] for param in elem.props["param"] params += ["param_name" => param.name, "param_desc" => param.desc.renderThrough( htmlTextRender ) ] end item_data[ prefix + "_params"] = params end if "optparam" in elem.props item_data[prefix + "_descriptor_table"] = true params = [] for param in elem.props["optparam"] params += ["param_name" => param.name, "param_desc" => param.desc.renderThrough( htmlTextRender ) ] end item_data[ prefix + "_optparams"] = params end if "raise" in elem.props item_data[prefix + "_descriptor_table"] = true raises = [] for rname, rdesc in elem.props["raise"] raises += [ "raise_name" => rname.renderThrough( htmlTextRender ), "raise_desc" => rdesc.renderThrough( htmlTextRender ) ] end item_data[ prefix + "_raises" ] = raises end if "return" in elem.props item_data[prefix + "_descriptor_table"] = true item_data[prefix + "_return"] = elem.props["return"].renderThrough( htmlTextRender ) end if elem.content descr = elem.content.renderThrough( htmlTextRender ) item_data[prefix + "_description"] = descr end // last but not least, the module if "inmodule" in elem.props l = elem.props["inmodule"] name = l.target and "title" in l.target.props ? \ l.target.props["title"].renderThrough( htmlTextRender ) : l.destination item_data["module_name"] = "[in " + name.trim() + "]" item_data["module_link"] = l.target ? make_link_to(l.target) : "#" end return item_data end function _makeCallPrototype( elem ) // no prototypes for enum if elem.type == "enum": return elem.name if elem.type == "funset": return nil grammar = elem.type == "message" ? "...handler... " : elem.name plist = "" if "param" in elem.props for param in elem.props["param"] forfirst: plist += " " plist += param.name formiddle: plist += ", " end end if "optparam" in elem.props if "param" in elem.props: plist += "," for param in elem.props["optparam"] forfirst: plist += " " plist += "[" + param.name + "]" formiddle: plist += ", " end end if plist or elem.type == "funtion" or elem.type == "method" plist = "(" + plist + " )" grammar += plist end return grammar end function _makeFrom( elem )// have we got some from clause? grammar = "" if "from" in elem.props grammar += " \\\n" for link, callproto in elem.props["from"] grammar += " from " + link.renderThrough( htmlTextRender ) if callproto: callproto = callproto.renderThrough( htmlTextRender ) if callproto: grammar += "( " + callproto + " )" formiddle: grammar += " \\\n" end end return grammar end function htmlTextRender( elem ) switch elem.type case "text", "entity" return elem.content case "note" return '

Note: ' + elem.content +"

\n" case "b" return '' + elem.content + "" case "i" return '' + elem.content + "" case "section" ret = "" if elem.level == 1 ret+= '

' + elem.content + "

" else ret+= '

' + elem.content + "

" end return ret + "
\n" case "p" return '

' + elem.content + "

\n" case "code" fcontent = elem.content.replace( "<", "<" ).replace( ">",">" ) return "
\n" + fcontent + "
\n" case "link" if elem.target if "title" in elem.target.props title = elem.target.props["title"].renderThrough( htmlTextRender ).trim() else title = elem.destination end return ""+ title + "" else return "" + elem.destination + "" end case "href" return ""+ elem.content.trim() + "" case "list_bullet" return "
    \n"+ elem.content + "\n
\n" case "list_number" return "
    \n"+ elem.content + "\n
\n" case "number", "bullet" return "
  • "+ elem.content + "
  • " default return '
    UNKNOWN PARAGRAPH TYPE: ' + elem.type + "
    \n" end end function make_link_to( element ) try dfname = oob(pagetable.find( "type", element.type, "detail" )) end url = getAssert( "html_url" ) if dfname // it's a link to a whole page. id = element.name return url + (@dfname) + ".html" elif ^? dfname // the detail name is unexisting, but we have a master file. return url + pagetable.find( "type", element.type, "index" ) + ".html#" + element.name end // it should have a membership if "class" in element.props // or is it an object? target = element.props["class"].target // is it an unresolved link? if target // use the target type (it may be an object) dfname = pagetable.find( "type", target.type, "detail" ) id = target.name return url + (@ dfname) + ".html#" + element.name[id.len()+1:] end end // fallback to a undefined link return "#" end function make_default_data( renderer ) return [ "charset" => "utf-8", "title" => renderer.doctree.title, "version" => version.name(), "doclinks" => renderer.doclinks ] end //============================================================================== // Definition table for pages. //============================================================================== object pagetable from Table( .[ "type" "index" "detail" "handler" "ititle" "dtitle" ], // The order given in this list is the order of the tabs in the link area. .[ "module" "modules" "module_$id" make_module "Modules" "Module $title" ], .[ "page" "pages" "page_$id" make_page "Related pages" "$title" ], .[ "group" "groups" "group_$id" make_group "Groups" "Group \"$title\"" ], .[ "funset" "funset" "funset_$id" make_ld "Function sets" "Function set $title" ], .[ "class" "classes" "class_$id" make_ld "Classes" "Class $name" ], .[ "object" "objects" "object_$id" make_ld "Objects" "Object $name" ], .[ "function" "functions" nil make_all "All functions" nil ], .[ "message" "messagess" nil make_all "Messages" nil ], .[ "enum" "enum" "enum_$id" make_ld "Enumerations" "Enum $name" ], .[ "global" "globals" nil make_all "Globals" nil ] ) end export pagetable, makeMain apps/faldoc/src/faldoc/output/list.fal000066400000000000000000000113151176363201700202760ustar00rootroot00000000000000/* FALCON - Documentation tool FILE: list.fal Autodocumentation tool - list ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 12 Jan 2008 12:21:30 +0100 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ // The faldoc_output function must be provided by all output modules function faldoc_output( doctree, conf ) faldoc.info( "=" * 60 ) faldoc.info( i"List output module started" ) faldoc.info( i"Module configuration:" ) faldoc.info( "=====================" ) for k,v in conf: faldoc.info( k + " = " + v ) faldoc.info( "=====================" ) basedir = faldoc.config.basedir if "file" notin conf faldoc.error( "ERROR: Missing mandatory \"list.file\" key in configuration" ) return end filename = conf["file"] fpath = Path( filename ) if not fpath.fulloc.startsWith("/") filename = basedir + "/" + filename end faldoc.info( "Writing to " + filename ) try file = OutputStream( filename ) build_list( file, doctree, conf ) file.close() catch IoError in err if file: file.close() faldoc.error( "list.fal: ERROR while writing to " + conf["file"] + ": " + err ) return end faldoc.info( "List output module complete " ) faldoc.info( "=" * 60 ) end function build_list( file, doctree, conf ) // intestation file.setEncoding( "utf-8", SYSTEM_DETECT ) if "doctitle" in conf file.writeText( conf["doctitle"] +"\n" ) else file.writeText( doctree.title +"\n" ) end if doctree.version: file.writeText( i"Version:" + " " + doctree.version +"\n" ) if doctree.author: file.writeText( i"Author:" + " " + doctree.author +"\n" ) file.writeText( "\n" ) // all the valid documents have a main module // -- which needs not to be introduced. build_mod_list( file, doctree, doctree, conf ) for modname, module in doctree.members forfirst: file.writeText( "\n\n" ) if module.type != "module": continue // prefix the module name to the list strModName = i"Module" + " \"" + module.name +"\"" file.writeText( strReplicate( "#", strModName.len() + 4 ) + "\n" ) file.writeText( "# "+ strModName+ " #\n" ) file.writeText( strReplicate( "#", strModName.len() + 4 ) + "\n\n" ) build_mod_list( file, module, doctree, conf ) formiddle: file.writeText( "\n\n" ) end file.writeText( "\n" ) end function build_mod_list( file, module, doctree, conf ) if "bytype" in conf and conf["bytype"] data = module.groupEntities() for type, title in [ ["enum", i"Enums"], ["global", i"Globals"], ["function", i"Functions"], ["class", i"Classes"], ["messasge", i"Messages"], ["object", i"Objects"] ] todo = type in data if todo: writeData( file, conf, type, title, data[type], module ) formiddle if todo: file.writeText( "\n\n" ) end end else writeData( file, conf, type, title, doctree.entities,module ) end end function writeData( file, conf, type, title, data, module ) done = false brief = "brief" in conf and conf["brief"].lower() == "true" members = "members" in conf and conf["members"].lower() == "true" for entry in data // skip if foreign if entry.parent != module continue end if not done file.writeText( title + "\n" + ("=" * title.len() ) +"\n\n") done = true end file.writeText( entry.prototype() ) // do we want brief? if brief and "brief" in entry.props file.writeText( " - " ) file.writeText( flatten( entry.props["brief"] ) ) end file.writeText( "\n" ) if entry.type == "class" or entry.type == "object" or entry.type == "enum" if members for name,m in entry.members file.writeText( m.prototype() ) // do we want brief? if brief and "brief" in m.props file.writeText( " - " + flatten( m.props["brief"] ) ) end file.writeText( "\n" ) end end end end return done end //# Flattens a textual node function flatten( textNode ) // Just text ? -- return it if textNode.content: return textNode.content // return the node as text str = "" node = textNode.firstChild while node if str: str += " " str += flatten( node ) node = node.next end return str end apps/faldoc/src/faldoc/version.fal000066400000000000000000000012341176363201700174470ustar00rootroot00000000000000/* FALCON - Documentation tool FILE: faldoc_version.fal Autodocumentation tool - version file ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 10 Jan 2008 08:12:57 -0800 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ object version major = 3 minor = 0 revision = 0 function name() return self.major.toString() + "." +\ self.minor.toString() + "." +\ self.revision.toString() end end export version apps/faldoc/src/resources/000077500000000000000000000000001176363201700160605ustar00rootroot00000000000000apps/faldoc/src/resources/faldoc.css000066400000000000000000000104371176363201700200270ustar00rootroot00000000000000 body { margin: 25px; font-size: 12pt; font-family: verdana, helvetica, arial, sans serif, sans; } /************************************************* Navigation *************************************************/ ul.navi_top { margin: 30px; padding: 6px 15px 6px 15px; border-top: 1px solid #303060; border-bottom: 1px solid #303060; list-style-type: none; } ul.navi_bottom { margin:30px; padding: 6px 15px 6px 15px; border-top: 1px solid #303060; border-bottom: 1px solid #303060; list-style-type: none; } ul.navi_bottom li.top, ul.navi_top li.top { width:100%; margin:2px; text-align: center; background: #F0F0FF; } ul.navi_bottom li.up, ul.navi_top li.up { width:100%; margin:2px; text-align: center; background: #E0E0FF; } ul.navi_bottom li.prev, ul.navi_top li.prev { width:46%; margin: 2px; float: left; text-align: center; background: #FFC0C0; } ul.navi_bottom li.next, ul.navi_top li.next { margin: 2px; margin-right:0; padding:0; width: 46%; float: right; text-align: center; background: #C0C0FF; } ul.navi_bottom li.clear, ul.navi_top li.clear { clear:both; width:100%; } /************************************************* TOC *************************************************/ ul.TOC { list-style-type: none; font-size:14pt; } ul.TOC ul { list-style-type: none; font-size:12pt; } ul.TOC ul ul { list-style-type: none; font-size:10pt; } ul.TOC span.toc_number { font-weight: bold; padding-right: 18pt; } ul.TOC ul span.toc_number { padding-right: 14pt; } ul.TOC ul ul span.toc_number { padding-right: 12pt; } ul.memberlist { list-style-type: none; font-size:12pt; } ul.memberlist span.toc_number { padding-right: 12pt; } H1 span.toc_number { padding-right: 24pt; } /************************************************* Page *************************************************/ div#page_body { clear:both; margin-left: 20pt; } h1 { background: #F0F0FF; color: #000030; margin-bottom: 40pt; padding-top: 4pt; padding-bottom: 4pt; margin-left: -20pt; } h2 { background: #F0F0FF; color: #000030; margin-top: 35pt; margin-bottom: 25pt; padding-top: 2pt; padding-bottom: 2pt; margin-left: -20pt; } h3 { margin-top: 20pt; margin-bottom: 15pt; background: #F0F0FF; color: #000030; margin-left: -20pt; } p.note { margin:30pt; padding:8pt; background: #E0E0FF; border: 1px solid #7070A0; } pre.prototype { color: #000030; padding-top: 12pt; margin-left: 30pt; font-size: 13pt; } table.prototype { margin-left: 30pt; width: 80%; border-bottom: 1px solid #7070A0; border-collapse: collapse; } table.prototype td { margin: 0; margin-bottom: 16pt; padding: 0; border:0; padding: 1px; padding-left: 3px; padding-right: 3px; } table.prototype tr.param td.name { padding-right: 15pt; font-family: courier, courier new, helvetica, fixed; font-weight: bold; } table.prototype tr.param td.content { } table.prototype tr.optparam td.name { padding-right: 15pt; font-family: courier, courier new, helvetica, fixed; font-weight: bold; background: #F0F0F0; } table.prototype tR.optparam td.content { background: #F0F0F0; } table.prototype tr.return td.name { padding-right: 15pt; font-weight: bold; font-style: italic; background: #F0F0FF; } table.prototype tr.return td.content { background: #F0F0FF; } table.prototype tr.raise td.name { padding-right: 15pt; font-weight: bold; font-style: italic; background: #FFF0F0; } table.prototype tr.raise td.content { background: #FFF0F0; } table.members { margin-top: 21pt; margin-bottom: 12pt; } table.members td { padding-right: 30pt; padding-left: 10pt; vertical-align: top; } table.members td.member_type { font-size: 13pt; font-weight: bold; padding: 3pt; padding-top: 18pt; padding-bottom: 8pt; border-bottom: 1px solid #7070A0; } a { text-decoration: none; } a:hover { background: #E0E0FF; } a:visited { color: #A02060; } div.signature { font-family: times new roman, times, serif; font-style: italic; font-size: 10pt; margin-top: 30pt; padding-top: 10pt; } p.brief { font-size: 13pt; font-style: italic; } apps/faldoc/tests/000077500000000000000000000000001176363201700144215ustar00rootroot00000000000000apps/faldoc/tests/samples/000077500000000000000000000000001176363201700160655ustar00rootroot00000000000000apps/faldoc/tests/samples/simple.fd000066400000000000000000000015301176363201700176700ustar00rootroot00000000000000# # FALDOC - Falcon Documentation system # # Minimal sample # ################################################ # Documentation generic data ################################################ Title = Documentation minimal sample Author = Giancarlo Niccolai Version = 1.0.0 ################################################ # Arbitrary properties ################################################ Property.coauthors = Jeremy Cowgar Property.comment = A cute extra property ################################################ # Faldoc Input settings ################################################ Input.directory=simple Input.wildcard=*.fal Input.recursive=false ################################################ # Faldoc Output settings ################################################ Output.module=html Output.html.directory=sample_html Output.html.url=. apps/faldoc/tests/samples/simple/000077500000000000000000000000001176363201700173565ustar00rootroot00000000000000apps/faldoc/tests/samples/simple/testdocs.fal000066400000000000000000000033061176363201700216740ustar00rootroot00000000000000/*# * @Main This is the main page. * Some text that goes in the main page. You may write either with @code \/*# This stile \*\/ @endcode Or * @code * /*# * * * * This style * \*\/ @endcode A nice backslash '\\' will cause things to be escaped. In example you may enter a \@ in the thescription of an entry by writing "\\\@". @note Please, notice that this is only a sample. You may wish to see more of this. Back to a non-note paragraph. */ /*# @Page secondary_page A Secondary page. You may enter several paragraphs by putting some blanks line. In example, this would be another paragraph. */ /*# @File The testdocs.fal description This entry is meant to generally describe the content of this file. */ /*#*************************************** * @function test * @brief test of a function documentation. * @param t1 the first parameter. * @optparam t2 an optional parameter. * * This is the desciption of function test. * * This would be another paragraph. * @note This may be a note... * continued on more paragrahs * * And this is just the last paragraph. * * @raise FunError an error in case of xyzzing the frobozz. * @return The resutlt of the operartion. ******************************************/ /*#*************************************** @function frobniz @brief frobnizzes the xizzy @param t1 the first parameter. @optparam t2 an optional parameter. This is the desciption of function frobniz. This would be another paragraph. Note that this function has no return nor raise. ******************************************/ apps/faldoc/tests/samples/testdocs.fal000066400000000000000000000013351176363201700204030ustar00rootroot00000000000000/*# * @Main This is the main page. * * Some text that goes in the main page. You may write either with @code \/*# \* This stile \*\/ @endcode Or * @code * \/*# * * * * This style * *\/ @endcode */ /*# @Page secondary_page A Secondary page. You may enter several paragraphs by putting some blanks line. In example, this would be another paragraph. */ /*#*************************************** * @function test test of a function. * @param t1 the first parameter * @optparam t2 an optional parameter * * This is the desciption of function test. * * @return The resutlt of the operartion. ******************************************/ clt/000077500000000000000000000000001176363201700116465ustar00rootroot00000000000000clt/CMakeLists.txt000066400000000000000000000006561176363201700144150ustar00rootroot00000000000000################################################## # Falcon Programming Language # # CLT - Command Line Tools meta cmake file ################################################## ADD_SUBDIRECTORY(faltest) ADD_SUBDIRECTORY(faldisass) ADD_SUBDIRECTORY(falrun) ADD_SUBDIRECTORY(falcon) ADD_SUBDIRECTORY(falpack) configure_file( executable_wrapper.cmake.in ${CMAKE_BINARY_DIR}/${FALCON_BIN_DIR}/executable_wrapper.cmake @ONLY ) clt/executable_wrapper.cmake.in000066400000000000000000000022011176363201700171310ustar00rootroot00000000000000if(NOT executable) message(FATAL_ERROR "missing -Dexecutable=..") endif() # if(NOT arguments) message(FATAL_ERROR "missing -Darguments=\"..\"") endif() # if(NOT workdir) message(FATAL_ERROR "missing -Dworkdir=..") endif() find_program(EXECUTABLE NAMES ${executable} HINTS "@CMAKE_BINARY_DIR@/bin" NO_DEFAULT_PATH ) # if(NOT EXECUTABLE) message(FATAL_ERROR "${executable} executable not found") endif() if(WIN32) # falcon_engine.dll and the executables reside in the same directory. elseif(APPLE) set(ENV{DYLD_LIBRARY_PATH} "@CMAKE_LIBRARY_OUTPUT_DIRECTORY@:$ENV{DYLD_LIBRARY_PATH}") else() # Linux, Solaris, BSD, ... set(ENV{LD_LIBRARY_PATH} "@CMAKE_LIBRARY_OUTPUT_DIRECTORY@:$ENV{LD_LIBRARY_PATH}") endif() # set(ENV{FALCON_LOAD_PATH} "@CMAKE_BINARY_DIR@/@FALCON_MOD_DIR@\;@Falcon_SOURCE_DIR@/modules/falcon") separate_arguments(arguments) set(cmd ${EXECUTABLE} ${arguments}) separate_arguments(cmd) # execute_process( COMMAND ${cmd} WORKING_DIRECTORY ${workdir} RESULT_VARIABLE res ERROR_VARIABLE err ) if(res) if(NOT err STREQUAL "") message("error:${err}") endif() message(SEND_ERROR "${res}") endif() clt/falcon/000077500000000000000000000000001176363201700131105ustar00rootroot00000000000000clt/falcon/CMakeLists.txt000066400000000000000000000024301176363201700156470ustar00rootroot00000000000000################################################## # Falcon Programming Language # # Falcon command line ################################################## option(FALCON_WITH_EDITLINE "Switch to ON for editline support." OFF) option(FALCON_WITH_INTERNAL_EDITLINE "Switch to ON to use EDITLINE provided by falcon" ON) if(WIN32) set(readline_SRC readline_simple.cpp) set( SYSTEM_RC falcon.rc) else() if(NOT FALCON_WITH_EDITLINE) set(readline_SRC readline_simple.cpp) else() set(readline_SRC readline_editline.cpp) if(FALCON_WITH_INTERNAL_EDITLINE) add_subdirectory(editline) set(Editline_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/editline/src) set(Editline_LIBRARY edit) else() find_package(Editline REQUIRED) endif() endif() endif() include_directories( ${PROJECT_BINARY_DIR}/include ${PROJECT_SOURCE_DIR}/include ${Editline_INCLUDE_DIRS} ) add_executable( falcon falcon.cpp int_mode.cpp options.cpp ${readline_SRC} ${SYSTEM_RC} ) if(EDITLINE_FOUND AND FALCON_WITH_GPL_READLINE) set_target_properties(falcon PROPERTIES COMPILE_DEFINITIONS FALCON_WITH_GPL_READLINE ) endif() target_link_libraries(falcon falcon_engine ${Editline_LIBRARY} ) install( TARGETS falcon ${FALCON_INSTALL_DESTINATIONS}) clt/falcon/editline/000077500000000000000000000000001176363201700147055ustar00rootroot00000000000000clt/falcon/editline/CMakeLists.txt000066400000000000000000000013151176363201700174450ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.6) project(Editline) find_package(Curses REQUIRED) set(HAVE_CURSES_H ${CURSES_HAVE_CURSES_H}) set(HAVE_NCURSES_H ${CURSES_HAVE_NCURSES_H} ) add_subdirectory(src) #add_subdirectory(examples) include(CheckIncludeFile) include(CheckFunctionExists) include(CheckTypeSize) include(CheckSymbolExists) check_include_file("sys/cdefs.h" HAVE_SYS_CDEFS_H) check_include_file("term.h" HAVE_TERM_H) check_function_exists("getpwuid_r" HAVE_GETPW_R_POSIX) check_type_size("u_int32_t" U_INT32_T) check_symbol_exists(alloca alloca.h HAVE_ALLOCA) set(HAVE_ALLOCA_H ${HAVE_ALLOCA}) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h ) clt/falcon/editline/COPYING000066400000000000000000000031171176363201700157420ustar00rootroot00000000000000Copyright (c) 1992, 1993 The Regents of the University of California. All rights reserved. This code is derived from software contributed to Berkeley by Christos Zoulas of Cornell University. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. clt/falcon/editline/ChangeLog000066400000000000000000000145441176363201700164670ustar00rootroot00000000000000 * See also NetBSD changelog: http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libedit 2009-09-23 Jess Thrysoee * all: apply Apple patches from: http://opensource.apple.com/source/libedit/libedit-11/patches 2009-09-05 Jess Thrysoee * version-info: 0:33:0 * all: Use predefined macro __sun to identify Solaris * src/el.c: Ignore comment lines in .editrc 2009-07-23 Jess Thrysoee * version-info: 0:32:0 * all: sync with upstream source. 2009-06-10 Jess Thrysoee * version-info: 0:31:0 * all: sync with upstream source. 2009-05-03 Jess Thrysoee * version-info: 0:30:0 * all: sync with upstream source. 2009-04-05 Jess Thrysoee * version-info: 0:29:0 * all: sync with upstream source. 2009-01-11 Jess Thrysoee * version-info: 0:28:0 * all: sync with upstream source. MAJOR.MINOR version is now 3.0. This is due to NetBSD changing time_t and dev_t to 64 bits. It does not really effect this package. * configure.ac: Remove '--enable-debug' configure flag. The autoconf way to control flags is by specifying them when running configure, e.g. 'CFLAGS="-O0 -g" ./configure' 2008-07-12 Jess Thrysoee * version-info: 0:27:0 * configure.ac: Added '--enable-debug' configure flag, to produce debugging information. * examples/fileman.c: cast stat struct members, st_nlink and st_size, appropriately (see also 'man 2 stat'). Patch by Alex Elder. * all: sync with upstream source. MINOR version is now 11. 2007-08-31 Jess Thrysoee * version-info: 0:26:0 * libedit.pc.in,Makefile.am,configure.ac,patches/extra_dist_list.sh: Added pkg-config support for libedit. Patch by Masatake YAMATO. 2007-08-13 Jess Thrysoee * version-info: 0:25:0 * all: sync with upstream source. 2007-03-02 Jess Thrysoee * version-info: 0:24:0 * all: sync with upstream source. 2006-10-22 Jess Thrysoee * version-info: 0:23:0 * src/shlib_version: Upstream bumped minor version from 9 to 10. * all: sync with upstream source. More readline functions. 2006-10-22 Jess Thrysoee * version-info: 0:22:0 * all: sync with upstream source. 2006-08-29 Jess Thrysoee * version-info: 0:21:0 * all: License cleanup. All 4-clause advertising BSD licenses has been changed to the 3-clause version by upstream. * src/fgetln.c: use src/tools/compat/fgetln.c instead of othersrc/libexec/tnftpd/libnetbsd/fgetln.c 2006-08-16 Jess Thrysoee * version-info: 0:20:0 * all: sync with upstream source. 2006-06-03 Jess Thrysoee * version-info: 0:19:0 * COPYING: added global license file * all: sync with upstream source. 2006-02-13 Jess Thrysoee * version-info: 0:18:0 * src/readline.c: Partial rl_getc_function support, patch by Kjeld Borch Egevang. * src/readline.c: Make write_history and read_history returncode readline compatible. Upstream patch. 2006-01-03 Jess Thrysoee * version-info: 0:17:0 * patches/cvs_export.sh: strlcat.c and strlcpy.c was moved to src/common/lib/libc/string in the upstream cvs repository. * all: sync with upstream source. 2005-10-22 Jess Thrysoee * version-info: 0:16:0 * patches/*.patch, configure.ac: define SCCSID, undef LIBC_SCCS. Remove fourteen cosmetic patches. * all: sync with upstream source. 2005-09-11 Jess Thrysoee * version-info: 0:15:0 * src/Makefile.am: fix typo that meant generated files were distributes, and make generated file targets dependent on the the 'makelist' input files. * all: sync with upstream source. This is just a manpage update 2005-08-28 Jess Thrysoee * version-info: 0:14:0 * src/sys.h: include config.h to avoid "redefinition of `u_int32_t'". Patch by Norihiko Murase. * src/search.c: explicitly include sys/types.h, because regex.h on FreeBSD needs it and does not include it itself. Patch by Norihiko Murase. * acinclude.m4: added EL_GETPW_R_DRAFT test and use AC_TRY_LINK instead of AC_TRY_COMPILE. Suggested by Norihiko Murase. * all: sync with upstream source. 2005-08-16 Jess Thrysoee * version-info: 0:13:0 * all: sync with upstream source. 2005-08-05 Jess Thrysoee * version-info: 0:12:0 * all: sync with upstream source. 2005-07-24 Jess Thrysoee * version-info: 0:11:0 * histedit.h, histedit.c, readline.c, editline/readline.h: From upstream; added remove_history(). 2005-07-07 Jess Thrysoee * version-info: 0:10:0 * history.c, key.c: From upstream source; Fix memory leaks found by valgrind. 2005-06-28 Jess Thrysoee * version-info: 0:9:0 * src/readline.c: getpwent_r is not POSIX, always use getpwent. Reported by Gerrit P. Haase. * src/Makefile.am: Added libtool -no-undefined. This is needed on Cygwin to get a shared editline library. Should not affect other platforms. Suggested by Gerrit P. Haase. 2005-06-15 Jess Thrysoee * version-info: 0:8:0 * all: sync with upstream source. 2005-06-01 Jess Thrysoee * version-info: 0:7:0 * all: sync with upstream source. * src/readline.c, src/filecomplete.c: Solaris use POSIX draft versions of getpwent_r, getpwnam_r and getpwuid_r which return 'struct passwd *'. Define HAVE_GETPW_R_POSIX if these functions are (non draft) POSIX compatible. Patch by Julien Torrs. 2005-05-28 Jess Thrysoee * version-info: 0:6:0 * all: sync with upstream source. 2005-03-11 Jess Thrysoee * version-info: 0:5:0 * all: sync with upstream source. 2004-12-07 Jess Thrysoee * version-info: 0:4:0 * src/readline.c: d_namlen (in struct dirent) is not portable, always use strlen. Patch by Scott Rankin. 2004-11-27 Jess Thrysoee * version-info: 0:3:0 * src/history.c: bug #26785 fixed upstream, removed local patch. 2004-11-06 Jess Thrysoee * version-info: 0:2:0 * all: sync with upstream source. * doc/Makefile.am: If mdoc2man fails, remove empty file. Patch by Darren Tucker. 2004-10-14 Jess Thrysoee * version-info: 0:1:0 * doc/Makefile.am: 'make install' twice fails. Remove old links before trying to link the man pages. Patch by Rick Richardson. 2004-09-28 Jess Thrysoee * version-info: 0:0:0 * acinclude.m4 configure.ac src/Makefile.am: Adhere to LibTools library interface versions recommendation. http://www.gnu.org/software/libtool/manual.html#SEC32 * doc/Makefile.am: name all manpage links as el_* (e.g. el_history.3) to avoid conflicts. 2004-09-08 Jess Thrysoee * all: Initial package. clt/falcon/editline/THANKS000066400000000000000000000000651176363201700156210ustar00rootroot00000000000000Thanks to the NetBSD Project maintainers of libedit! clt/falcon/editline/config.h.cmake000066400000000000000000000017721176363201700174110ustar00rootroot00000000000000/* Define to 1 if you have `alloca', as a function or macro. */ #cmakedefine HAVE_ALLOCA /* Define to 1 if you have and it should be used (not on Ultrix). */ #cmakedefine HAVE_ALLOCA_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_CURSES_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NCURSES_H /* Define to 1 if you have getpwnam_r and getpwuid_r that are draft POSIX.1 versions. */ #cmakedefine HAVE_GETPW_R_DRAFT /* Define to 1 if you have getpwnam_r and getpwuid_r that are POSIX.1 compatible. */ #cmakedefine HAVE_GETPW_R_POSIX /* Define to 1 if you have the `issetugid' function. */ #cmakedefine HAVE_ISSETUGID /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_CDEFS_H /* Define to 1 if you have the header file. */ #cmakedefine HAVE_TERM_H /* Define to 1 if the system has the type `u_int32_t'. */ #cmakedefine HAVE_U_INT32_T #include #define SCCSID #undef LIBC_SCCS clt/falcon/editline/examples/000077500000000000000000000000001176363201700165235ustar00rootroot00000000000000clt/falcon/editline/examples/CMakeLists.txt000066400000000000000000000004731176363201700212670ustar00rootroot00000000000000include_directories(${PROJECT_SOURCE_DIR}/src) add_executable(fileman fileman.c) target_link_libraries(fileman edit) add_executable(test test.c) target_link_libraries(test edit) add_executable(maik maik.cpp) target_link_libraries(maik edit) add_executable(iohub iohub.cpp) target_link_libraries(iohub edit) clt/falcon/editline/examples/fileman.c000066400000000000000000000256701176363201700203140ustar00rootroot00000000000000/* fileman.c -- A tiny application which demonstrates how to use the GNU Readline library. This application interactively allows users to manipulate files and their modes. NOTE: this was taken from the GNU Readline documentation and ported to libedit. A commad to output the history list was added. */ #include #include #include #include #include #include #include #include #include /* GNU readline #include #include */ #include void * xmalloc (size_t size); void too_dangerous (char *caller); void initialize_readline (); int execute_line (char *line); int valid_argument (char *caller, char *arg); typedef int rl_icpfunc_t (char *); /* The names of functions that actually do the manipulation. */ int com_list (char *); int com_view (char *); int com_history (char *); int com_rename(char *); int com_stat(char *); int com_pwd(char *); int com_delete(char *); int com_help(char *); int com_cd(char *); int com_quit(char *); /* A structure which contains information on the commands this program can understand. */ typedef struct { char *name; /* User printable name of the function. */ rl_icpfunc_t *func; /* Function to call to do the job. */ char *doc; /* Documentation for this function. */ } COMMAND; COMMAND commands[] = { { "cd", com_cd, "Change to directory DIR" }, { "delete", com_delete, "Delete FILE" }, { "help", com_help, "Display this text" }, { "?", com_help, "Synonym for `help'" }, { "list", com_list, "List files in DIR" }, { "ls", com_list, "Synonym for `list'" }, { "pwd", com_pwd, "Print the current working directory" }, { "quit", com_quit, "Quit using Fileman" }, { "rename", com_rename, "Rename FILE to NEWNAME" }, { "stat", com_stat, "Print out statistics on FILE" }, { "view", com_view, "View the contents of FILE" }, { "history", com_history, "List editline history" }, { (char *)NULL, (rl_icpfunc_t *)NULL, (char *)NULL } }; /* Forward declarations. */ char *stripwhite (); COMMAND *find_command (); /* The name of this program, as taken from argv[0]. */ char *progname; /* When non-zero, this means the user is done using this program. */ int done; char * dupstr (char* s) { char *r; r = xmalloc (strlen (s) + 1); strcpy (r, s); return (r); } int main (int argc, char **argv) { char *line, *s; progname = argv[0]; initialize_readline(); /* Bind our completer. */ stifle_history(7); /* Loop reading and executing lines until the user quits. */ for ( ; done == 0; ) { line = readline ("FileMan: "); if (!line) break; /* Remove leading and trailing whitespace from the line. Then, if there is anything left, add it to the history list and execute it. */ s = stripwhite(line); if (*s) { char* expansion; int result; result = history_expand(s, &expansion); if (result < 0 || result == 2) { fprintf(stderr, "%s\n", expansion); } else { add_history(expansion); execute_line(expansion); } free(expansion); } free(line); } exit (0); return 0; } /* Execute a command line. */ int execute_line (char *line) { register int i; COMMAND *command; char *word; /* Isolate the command word. */ i = 0; while (line[i] && isspace (line[i])) i++; word = line + i; while (line[i] && !isspace (line[i])) i++; if (line[i]) line[i++] = '\0'; command = find_command (word); if (!command) { fprintf (stderr, "%s: No such command for FileMan.\n", word); return (-1); } /* Get argument to command, if any. */ while (isspace (line[i])) i++; word = line + i; /* Call the function. */ return ((*(command->func)) (word)); } /* Look up NAME as the name of a command, and return a pointer to that command. Return a NULL pointer if NAME isn't a command name. */ COMMAND * find_command (char *name) { register int i; for (i = 0; commands[i].name; i++) if (strcmp (name, commands[i].name) == 0) return (&commands[i]); return ((COMMAND *)NULL); } /* Strip whitespace from the start and end of STRING. Return a pointer into STRING. */ char * stripwhite (char *string) { register char *s, *t; for (s = string; isspace (*s); s++) ; if (*s == 0) return (s); t = s + strlen (s) - 1; while (t > s && isspace (*t)) t--; *++t = '\0'; return s; } /* **************************************************************** */ /* */ /* Interface to Readline Completion */ /* */ /* **************************************************************** */ char *command_generator(const char *, int); char **fileman_completion(const char *, int, int); /* Tell the GNU Readline library how to complete. We want to try to complete on command names if this is the first word in the line, or on filenames if not. */ void initialize_readline () { /* Allow conditional parsing of the ~/.inputrc file. */ rl_readline_name = "FileMan"; /* Tell the completer that we want a crack first. */ rl_attempted_completion_function = fileman_completion; } /* Attempt to complete on the contents of TEXT. START and END bound the region of rl_line_buffer that contains the word to complete. TEXT is the word to complete. We can use the entire contents of rl_line_buffer in case we want to do some simple parsing. Returnthe array of matches, or NULL if there aren't any. */ char ** fileman_completion (const char* text, int start, int end) { char **matches; matches = (char **)NULL; /* If this word is at the start of the line, then it is a command to complete. Otherwise it is the name of a file in the current directory. */ if (start == 0) /* TODO */ matches = completion_matches (text, command_generator); /* matches = rl_completion_matches (text, command_generator); */ return (matches); } /* Generator function for command completion. STATE lets us know whether to start from scratch; without any state (i.e. STATE == 0), then we start at the top of the list. */ char * command_generator (text, state) const char *text; int state; { static int list_index, len; char *name; /* If this is a new word to complete, initialize now. This includes saving the length of TEXT for efficiency, and initializing the index variable to 0. */ if (!state) { list_index = 0; len = strlen (text); } /* Return the next name which partially matches from the command list. */ while (name = commands[list_index].name) { list_index++; if (strncmp (name, text, len) == 0) return (dupstr(name)); } /* If no names matched, then return NULL. */ return ((char *)NULL); } /* **************************************************************** */ /* */ /* FileMan Commands */ /* */ /* **************************************************************** */ /* String to pass to system (). This is for the LIST, VIEW and RENAME commands. */ static char syscom[1024]; /* List the file(s) named in arg. */ int com_list (char *arg) { if (!arg) arg = ""; sprintf (syscom, "ls -FClg %s", arg); return (system (syscom)); } int com_view (char *arg) { if (!valid_argument ("view", arg)) return 1; sprintf (syscom, "more %s", arg); return (system (syscom)); } int com_history(char* arg) { HIST_ENTRY *he; /* rewind history */ while (next_history()) ; for (he = current_history(); he != NULL; he = previous_history()) { printf("%5d %s\n", *((int*)he->data), he->line); } return 0; } int com_rename (char *arg) { too_dangerous ("rename"); return (1); } int com_stat (char *arg) { struct stat finfo; if (!valid_argument ("stat", arg)) return (1); if (stat (arg, &finfo) == -1) { perror (arg); return (1); } printf ("Statistics for `%s':\n", arg); printf ("%s has %ld link%s, and is %lld byte%s in length.\n", arg, (long) finfo.st_nlink, (finfo.st_nlink == 1) ? "" : "s", (long long) finfo.st_size, (finfo.st_size == 1) ? "" : "s"); printf ("Inode Last Change at: %s", ctime (&finfo.st_ctime)); printf (" Last access at: %s", ctime (&finfo.st_atime)); printf (" Last modified at: %s", ctime (&finfo.st_mtime)); return (0); } int com_delete (char *arg) { too_dangerous ("delete"); return (1); } /* Print out help for ARG, or for all of the commands if ARG is not present. */ int com_help (char *arg) { register int i; int printed = 0; for (i = 0; commands[i].name; i++) { if (!*arg || (strcmp (arg, commands[i].name) == 0)) { printf ("%s\t\t%s.\n", commands[i].name, commands[i].doc); printed++; } } if (!printed) { printf ("No commands match `%s'. Possibilties are:\n", arg); for (i = 0; commands[i].name; i++) { /* Print in six columns. */ if (printed == 6) { printed = 0; printf ("\n"); } printf ("%s\t", commands[i].name); printed++; } if (printed) printf ("\n"); } return (0); } /* Change to the directory ARG. */ int com_cd (char *arg) { if (chdir (arg) == -1) { perror (arg); return 1; } com_pwd (""); return (0); } /* Print out the current working directory. */ int com_pwd (char* ignore) { char dir[1024], *s; s = (char*)getcwd(dir, sizeof(dir) - 1); if (s == 0) { printf ("Error getting pwd: %s\n", dir); return 1; } printf ("Current directory is %s\n", dir); return 0; } /* The user wishes to quit using this program. Just set DONE non-zero. */ int com_quit (char *arg) { done = 1; return (0); } /* Function which tells you that you can't do this. */ void too_dangerous (char *caller) { fprintf (stderr, "%s: Too dangerous for me to distribute.\n", caller); fprintf (stderr, "Write it yourself.\n"); } /* Return non-zero if ARG is a valid argument for CALLER, else print an error message and return zero. */ int valid_argument (char *caller, char *arg) { if (!arg || !*arg) { fprintf (stderr, "%s: Argument required.\n", caller); return (0); } return (1); } void * xmalloc (size_t size) { register void *value = (void*)malloc(size); if (value == 0) fprintf(stderr, "virtual memory exhausted"); return value; } clt/falcon/editline/examples/foo.cpp000066400000000000000000000030251176363201700200120ustar00rootroot00000000000000#include #include #include #include #include #include static char** my_completion(const char*, int ,int); char* my_generator(const char*,int); template struct FreeGuard { T* p; explicit FreeGuard(T* p) : p(p) { } ~FreeGuard() { free(p); } }; using namespace std; vector cmds; int main() { char const* cmd [] = { "hello", "world", "hell" ,"word", "quit", " "}; cmds.assign(cmd , cmd + sizeof(cmd)/sizeof(cmd[0]) ); char *buf; rl_attempted_completion_function = my_completion; while(buf = readline("\n >> ")) { FreeGuard freeGuard(buf); //enable auto-complete rl_bind_key('\t',rl_complete); printf("cmd [%s]\n",buf); if (strcmp(buf,"quit")==0) break; if (buf[0]!=0) add_history(buf); } return 0; } static int abort_rl(int,int) { return _rl_abort_internal(); } static char** my_completion( const char * text , int start, int end) { char** matches = 0; if (start == 0) matches = rl_completion_matches (text, &my_generator); else rl_bind_key('\t',abort_rl); return matches; } char* my_generator(const char* text, int state) { static int list_index, len; char* ret = 0; if (!state) { list_index = 0; len = strlen (text); } while(list_index < cmds.size()) { string const& cmd = cmds[list_index++]; if (cmd.compare(0,len,text) == 0) { ret = strdup(cmd.c_str()); break; } } return ret; } clt/falcon/editline/examples/iohub.cpp000066400000000000000000000012621176363201700203360ustar00rootroot00000000000000#include #include #include #include #include #include #include template struct FreeGuard { T* p; explicit FreeGuard(T* p) : p(p) { } ~FreeGuard() { free(p); } }; //rl_getc_function static int my_getc(FILE*) { return getchar(); } int main(int argc, char **argv) { rl_getc_function = my_getc; char *buf; while(buf = readline("\n >> ")) { FreeGuard freeGuard(buf); //enable auto-complete rl_bind_key('\t',rl_complete); printf("cmd [%s]\n",buf); if (strcmp(buf,"quit")==0) break; if (buf[0]!=0) add_history(buf); } } clt/falcon/editline/examples/test.c000066400000000000000000000160011176363201700176440ustar00rootroot00000000000000/* $NetBSD: test.c,v 1.18 2005/06/01 11:37:52 lukem Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * test.c: A little test program */ #include #include #include #include #include #include #include #include #include "histedit.h" /* from src/sys/sys/cdefs.h */ #ifndef __UNCONST # define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) #endif static int continuation = 0; volatile sig_atomic_t gotsig = 0; static unsigned char complete(EditLine *, int); int main(int, char **); static char *prompt(EditLine *); static void sig(int); static char * prompt(EditLine *el) { static char a[] = "Edit$ "; static char b[] = "Edit> "; return (continuation ? b : a); } static void sig(int i) { gotsig = i; } static unsigned char complete(EditLine *el, int ch) { DIR *dd = opendir("."); struct dirent *dp; const char* ptr; const LineInfo *lf = el_line(el); int len; /* * Find the last word */ for (ptr = lf->cursor - 1; !isspace((unsigned char)*ptr) && ptr > lf->buffer; ptr--) continue; len = lf->cursor - ++ptr; for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { if (len > strlen(dp->d_name)) continue; if (strncmp(dp->d_name, ptr, len) == 0) { closedir(dd); if (el_insertstr(el, &dp->d_name[len]) == -1) return (CC_ERROR); else return (CC_REFRESH); } } closedir(dd); return (CC_ERROR); } int main(int argc, char *argv[]) { EditLine *el = NULL; int num; const char *buf; Tokenizer *tok; #if 0 int lastevent = 0; #endif int ncontinuation; History *hist; HistEvent ev; (void) signal(SIGINT, sig); (void) signal(SIGQUIT, sig); (void) signal(SIGHUP, sig); (void) signal(SIGTERM, sig); hist = history_init(); /* Init the builtin history */ /* Remember 100 events */ history(hist, &ev, H_SETSIZE, 100); tok = tok_init(NULL); /* Initialize the tokenizer */ /* Initialize editline */ el = el_init(*argv, stdin, stdout, stderr); el_set(el, EL_EDITOR, "vi"); /* Default editor is vi */ el_set(el, EL_SIGNAL, 1); /* Handle signals gracefully */ el_set(el, EL_PROMPT, prompt); /* Set the prompt function */ /* Tell editline to use this history interface */ el_set(el, EL_HIST, history, hist); /* Add a user-defined function */ el_set(el, EL_ADDFN, "ed-complete", "Complete argument", complete); /* Bind tab to it */ el_set(el, EL_BIND, "^I", "ed-complete", NULL); /* * Bind j, k in vi command mode to previous and next line, instead * of previous and next history. */ el_set(el, EL_BIND, "-a", "k", "ed-prev-line", NULL); el_set(el, EL_BIND, "-a", "j", "ed-next-line", NULL); /* * Source the user's defaults file. */ el_source(el, NULL); while ((buf = el_gets(el, &num)) != NULL && num != 0) { int ac, cc, co; #ifdef DEBUG int i; #endif const char **av; const LineInfo *li; li = el_line(el); #ifdef DEBUG (void) fprintf(stderr, "==> got %d %s", num, buf); (void) fprintf(stderr, " > li `%.*s_%.*s'\n", (li->cursor - li->buffer), li->buffer, (li->lastchar - 1 - li->cursor), (li->cursor >= li->lastchar) ? "" : li->cursor); #endif if (gotsig) { (void) fprintf(stderr, "Got signal %d.\n", gotsig); gotsig = 0; el_reset(el); } if (!continuation && num == 1) continue; ac = cc = co = 0; ncontinuation = tok_line(tok, li, &ac, &av, &cc, &co); if (ncontinuation < 0) { (void) fprintf(stderr, "Internal error\n"); continuation = 0; continue; } #ifdef DEBUG (void) fprintf(stderr, " > nc %d ac %d cc %d co %d\n", ncontinuation, ac, cc, co); #endif #if 0 if (continuation) { /* * Append to the right event in case the user * moved around in history. */ if (history(hist, &ev, H_SET, lastevent) == -1) err(1, "%d: %s", lastevent, ev.str); history(hist, &ev, H_ADD , buf); } else { history(hist, &ev, H_ENTER, buf); lastevent = ev.num; } #else /* Simpler */ history(hist, &ev, continuation ? H_APPEND : H_ENTER, buf); #endif continuation = ncontinuation; ncontinuation = 0; if (continuation) continue; #ifdef DEBUG for (i = 0; i < ac; i++) { (void) fprintf(stderr, " > arg# %2d ", i); if (i != cc) (void) fprintf(stderr, "`%s'\n", av[i]); else (void) fprintf(stderr, "`%.*s_%s'\n", co, av[i], av[i] + co); } #endif if (strcmp(av[0], "history") == 0) { int rv; switch (ac) { case 1: for (rv = history(hist, &ev, H_LAST); rv != -1; rv = history(hist, &ev, H_PREV)) (void) fprintf(stdout, "%4d %s", ev.num, ev.str); break; case 2: if (strcmp(av[1], "clear") == 0) history(hist, &ev, H_CLEAR); else goto badhist; break; case 3: if (strcmp(av[1], "load") == 0) history(hist, &ev, H_LOAD, av[2]); else if (strcmp(av[1], "save") == 0) history(hist, &ev, H_SAVE, av[2]); break; badhist: default: (void) fprintf(stderr, "Bad history arguments\n"); break; } } else if (el_parse(el, ac, av) == -1) { switch (fork()) { case 0: execvp(av[0], (char *const *)__UNCONST(av)); perror(av[0]); _exit(1); /*NOTREACHED*/ break; case -1: perror("fork"); break; default: if (wait(&num) == -1) perror("wait"); (void) fprintf(stderr, "Exit %x\n", num); break; } } tok_reset(tok); } el_end(el); tok_end(tok); history_end(hist); return (0); } clt/falcon/editline/src/000077500000000000000000000000001176363201700154745ustar00rootroot00000000000000clt/falcon/editline/src/CMakeLists.txt000066400000000000000000000030261176363201700202350ustar00rootroot00000000000000include_directories( ${PROJECT_BINARY_DIR} # config.h ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CURSES_INCLUDE_DIR} ) set(AHDR vi.h emacs.h common.h ) set(ASRC ${CMAKE_CURRENT_SOURCE_DIR}/vi.c ${CMAKE_CURRENT_SOURCE_DIR}/emacs.c ${CMAKE_CURRENT_SOURCE_DIR}/common.c ) add_custom_command( OUTPUT ${AHDR} DEPENDS vi.c emacs.c common.c COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/makelist -h ${CMAKE_CURRENT_SOURCE_DIR}/vi.c > vi.h COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/makelist -h ${CMAKE_CURRENT_SOURCE_DIR}/emacs.c > emacs.h COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/makelist -h ${CMAKE_CURRENT_SOURCE_DIR}/common.c > common.h ) add_custom_command( OUTPUT fcns.h fcns.c help.h help.c DEPENDS ${AHDR} COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/makelist -fh ${AHDR} > fcns.h COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/makelist -bh ${ASRC} > help.h COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/makelist -fc ${AHDR} > fcns.c COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/makelist -bc ${ASRC} > help.c ) add_custom_target(makelist_target DEPENDS ${AHDR} help.h fcns.h ) set(libedit_SOURCES chared.c el.c hist.c key.c map.c parse.c prompt.c read.c refresh.c search.c sig.c term.c tty.c fgetln.c strlcat.c strlcpy.c unvis.c vis.c tokenizer.c history.c filecomplete.c readline.c ) add_library(edit STATIC ${libedit_SOURCES} ${ASRC} fcns.c help.c ) add_dependencies(edit makelist_target) target_link_libraries(edit ${CURSES_LIBRARIES} )clt/falcon/editline/src/Makefile.am000066400000000000000000000025431176363201700175340ustar00rootroot00000000000000 BUILT_SOURCES = vi.h emacs.h common.h fcns.h help.h fcns.c help.c AHDR= vi.h emacs.h common.h ASRC= $(srcdir)/vi.c $(srcdir)/emacs.c $(srcdir)/common.c vi.h: Makefile $(srcdir)/vi.c sh $(srcdir)/makelist -h $(srcdir)/vi.c > $@ emacs.h: Makefile $(srcdir)/emacs.c sh $(srcdir)/makelist -h $(srcdir)/emacs.c > $@ common.h: Makefile $(srcdir)/common.c sh $(srcdir)/makelist -h $(srcdir)/common.c > $@ fcns.h: Makefile $(AHDR) sh $(srcdir)/makelist -fh $(AHDR) > $@ help.h: Makefile $(ASRC) sh $(srcdir)/makelist -bh $(ASRC) > $@ fcns.c: Makefile $(AHDR) sh $(srcdir)/makelist -fc $(AHDR) > $@ help.c: Makefile $(ASRC) sh $(srcdir)/makelist -bc $(ASRC) > $@ CLEANFILES = $(BUILT_SOURCES) lib_LTLIBRARIES = libedit.la libedit_la_SOURCES = chared.c common.c el.c emacs.c hist.c key.c map.c parse.c \ prompt.c read.c refresh.c search.c sig.c term.c tty.c vi.c \ fgetln.c strlcat.c strlcpy.c unvis.c vis.c tokenizer.c \ history.c filecomplete.c readline.c chared.h el.h hist.h \ histedit.h key.h map.h parse.h prompt.h read.h refresh.h \ search.h sig.h sys.h el_term.h tty.h vis.h filecomplete.h \ editline/readline.h EXTRA_DIST = makelist shlib_version nobase_include_HEADERS = histedit.h editline/readline.h nodist_libedit_la_SOURCES = $(BUILT_SOURCES) libedit_la_LDFLAGS = -no-undefined -version-info $(LT_VERSION) clt/falcon/editline/src/chared.c000066400000000000000000000412771176363201700171010ustar00rootroot00000000000000/* $NetBSD: chared.c,v 1.27 2009/02/15 21:55:23 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)chared.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: chared.c,v 1.27 2009/02/15 21:55:23 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * chared.c: Character editor utilities */ #include #include "el.h" private void ch__clearmacro (EditLine *); /* value to leave unused in line buffer */ #define EL_LEAVE 2 /* cv_undo(): * Handle state for the vi undo command */ protected void cv_undo(EditLine *el) { c_undo_t *vu = &el->el_chared.c_undo; c_redo_t *r = &el->el_chared.c_redo; size_t size; /* Save entire line for undo */ size = el->el_line.lastchar - el->el_line.buffer; vu->len = size; vu->cursor = (int)(el->el_line.cursor - el->el_line.buffer); memcpy(vu->buf, el->el_line.buffer, size); /* save command info for redo */ r->count = el->el_state.doingarg ? el->el_state.argument : 0; r->action = el->el_chared.c_vcmd.action; r->pos = r->buf; r->cmd = el->el_state.thiscmd; r->ch = el->el_state.thisch; } /* cv_yank(): * Save yank/delete data for paste */ protected void cv_yank(EditLine *el, const char *ptr, int size) { c_kill_t *k = &el->el_chared.c_kill; memcpy(k->buf, ptr, (size_t)size); k->last = k->buf + size; } /* c_insert(): * Insert num characters */ protected void c_insert(EditLine *el, int num) { char *cp; if (el->el_line.lastchar + num >= el->el_line.limit) { if (!ch_enlargebufs(el, (size_t)num)) return; /* can't go past end of buffer */ } if (el->el_line.cursor < el->el_line.lastchar) { /* if I must move chars */ for (cp = el->el_line.lastchar; cp >= el->el_line.cursor; cp--) cp[num] = *cp; } el->el_line.lastchar += num; } /* c_delafter(): * Delete num characters after the cursor */ protected void c_delafter(EditLine *el, int num) { if (el->el_line.cursor + num > el->el_line.lastchar) num = (int)(el->el_line.lastchar - el->el_line.cursor); if (el->el_map.current != el->el_map.emacs) { cv_undo(el); cv_yank(el, el->el_line.cursor, num); } if (num > 0) { char *cp; for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++) *cp = cp[num]; el->el_line.lastchar -= num; } } /* c_delafter1(): * Delete the character after the cursor, do not yank */ protected void c_delafter1(EditLine *el) { char *cp; for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++) *cp = cp[1]; el->el_line.lastchar--; } /* c_delbefore(): * Delete num characters before the cursor */ protected void c_delbefore(EditLine *el, int num) { if (el->el_line.cursor - num < el->el_line.buffer) num = (int)(el->el_line.cursor - el->el_line.buffer); if (el->el_map.current != el->el_map.emacs) { cv_undo(el); cv_yank(el, el->el_line.cursor - num, num); } if (num > 0) { char *cp; for (cp = el->el_line.cursor - num; cp <= el->el_line.lastchar; cp++) *cp = cp[num]; el->el_line.lastchar -= num; } } /* c_delbefore1(): * Delete the character before the cursor, do not yank */ protected void c_delbefore1(EditLine *el) { char *cp; for (cp = el->el_line.cursor - 1; cp <= el->el_line.lastchar; cp++) *cp = cp[1]; el->el_line.lastchar--; } /* ce__isword(): * Return if p is part of a word according to emacs */ protected int ce__isword(int p) { return (isalnum(p) || strchr("*?_-.[]~=", p) != NULL); } /* cv__isword(): * Return if p is part of a word according to vi */ protected int cv__isword(int p) { if (isalnum(p) || p == '_') return 1; if (isgraph(p)) return 2; return 0; } /* cv__isWord(): * Return if p is part of a big word according to vi */ protected int cv__isWord(int p) { return (!isspace(p)); } /* c__prev_word(): * Find the previous word */ protected char * c__prev_word(char *p, char *low, int n, int (*wtest)(int)) { p--; while (n--) { while ((p >= low) && !(*wtest)((unsigned char) *p)) p--; while ((p >= low) && (*wtest)((unsigned char) *p)) p--; } /* cp now points to one character before the word */ p++; if (p < low) p = low; /* cp now points where we want it */ return (p); } /* c__next_word(): * Find the next word */ protected char * c__next_word(char *p, char *high, int n, int (*wtest)(int)) { while (n--) { while ((p < high) && !(*wtest)((unsigned char) *p)) p++; while ((p < high) && (*wtest)((unsigned char) *p)) p++; } if (p > high) p = high; /* p now points where we want it */ return (p); } /* cv_next_word(): * Find the next word vi style */ protected char * cv_next_word(EditLine *el, char *p, char *high, int n, int (*wtest)(int)) { int test; while (n--) { test = (*wtest)((unsigned char) *p); while ((p < high) && (*wtest)((unsigned char) *p) == test) p++; /* * vi historically deletes with cw only the word preserving the * trailing whitespace! This is not what 'w' does.. */ if (n || el->el_chared.c_vcmd.action != (DELETE|INSERT)) while ((p < high) && isspace((unsigned char) *p)) p++; } /* p now points where we want it */ if (p > high) return (high); else return (p); } /* cv_prev_word(): * Find the previous word vi style */ protected char * cv_prev_word(char *p, char *low, int n, int (*wtest)(int)) { int test; p--; while (n--) { while ((p > low) && isspace((unsigned char) *p)) p--; test = (*wtest)((unsigned char) *p); while ((p >= low) && (*wtest)((unsigned char) *p) == test) p--; } p++; /* p now points where we want it */ if (p < low) return (low); else return (p); } #ifdef notdef /* c__number(): * Ignore character p points to, return number appearing after that. * A '$' by itself means a big number; "$-" is for negative; '^' means 1. * Return p pointing to last char used. */ protected char * c__number( char *p, /* character position */ int *num, /* Return value */ int dval) /* dval is the number to subtract from like $-3 */ { int i; int sign = 1; if (*++p == '^') { *num = 1; return (p); } if (*p == '$') { if (*++p != '-') { *num = 0x7fffffff; /* Handle $ */ return (--p); } sign = -1; /* Handle $- */ ++p; } for (i = 0; isdigit((unsigned char) *p); i = 10 * i + *p++ - '0') continue; *num = (sign < 0 ? dval - i : i); return (--p); } #endif /* cv_delfini(): * Finish vi delete action */ protected void cv_delfini(EditLine *el) { int size; int action = el->el_chared.c_vcmd.action; if (action & INSERT) el->el_map.current = el->el_map.key; if (el->el_chared.c_vcmd.pos == 0) /* sanity */ return; size = (int)(el->el_line.cursor - el->el_chared.c_vcmd.pos); if (size == 0) size = 1; el->el_line.cursor = el->el_chared.c_vcmd.pos; if (action & YANK) { if (size > 0) cv_yank(el, el->el_line.cursor, size); else cv_yank(el, el->el_line.cursor + size, -size); } else { if (size > 0) { c_delafter(el, size); re_refresh_cursor(el); } else { c_delbefore(el, -size); el->el_line.cursor += size; } } el->el_chared.c_vcmd.action = NOP; } #ifdef notdef /* ce__endword(): * Go to the end of this word according to emacs */ protected char * ce__endword(char *p, char *high, int n) { p++; while (n--) { while ((p < high) && isspace((unsigned char) *p)) p++; while ((p < high) && !isspace((unsigned char) *p)) p++; } p--; return (p); } #endif /* cv__endword(): * Go to the end of this word according to vi */ protected char * cv__endword(char *p, char *high, int n, int (*wtest)(int)) { int test; p++; while (n--) { while ((p < high) && isspace((unsigned char) *p)) p++; test = (*wtest)((unsigned char) *p); while ((p < high) && (*wtest)((unsigned char) *p) == test) p++; } p--; return (p); } /* ch_init(): * Initialize the character editor */ protected int ch_init(EditLine *el) { c_macro_t *ma = &el->el_chared.c_macro; el->el_line.buffer = (char *) el_malloc(EL_BUFSIZ); if (el->el_line.buffer == NULL) return (-1); (void) memset(el->el_line.buffer, 0, EL_BUFSIZ); el->el_line.cursor = el->el_line.buffer; el->el_line.lastchar = el->el_line.buffer; el->el_line.limit = &el->el_line.buffer[EL_BUFSIZ - EL_LEAVE]; el->el_chared.c_undo.buf = (char *) el_malloc(EL_BUFSIZ); if (el->el_chared.c_undo.buf == NULL) return (-1); (void) memset(el->el_chared.c_undo.buf, 0, EL_BUFSIZ); el->el_chared.c_undo.len = -1; el->el_chared.c_undo.cursor = 0; el->el_chared.c_redo.buf = (char *) el_malloc(EL_BUFSIZ); if (el->el_chared.c_redo.buf == NULL) return (-1); el->el_chared.c_redo.pos = el->el_chared.c_redo.buf; el->el_chared.c_redo.lim = el->el_chared.c_redo.buf + EL_BUFSIZ; el->el_chared.c_redo.cmd = ED_UNASSIGNED; el->el_chared.c_vcmd.action = NOP; el->el_chared.c_vcmd.pos = el->el_line.buffer; el->el_chared.c_kill.buf = (char *) el_malloc(EL_BUFSIZ); if (el->el_chared.c_kill.buf == NULL) return (-1); (void) memset(el->el_chared.c_kill.buf, 0, EL_BUFSIZ); el->el_chared.c_kill.mark = el->el_line.buffer; el->el_chared.c_kill.last = el->el_chared.c_kill.buf; el->el_map.current = el->el_map.key; el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */ el->el_state.doingarg = 0; el->el_state.metanext = 0; el->el_state.argument = 1; el->el_state.lastcmd = ED_UNASSIGNED; ma->level = -1; ma->offset = 0; ma->macro = (char **) el_malloc(EL_MAXMACRO * sizeof(char *)); if (ma->macro == NULL) return (-1); return (0); } /* ch_reset(): * Reset the character editor */ protected void ch_reset(EditLine *el, int mclear) { el->el_line.cursor = el->el_line.buffer; el->el_line.lastchar = el->el_line.buffer; el->el_chared.c_undo.len = -1; el->el_chared.c_undo.cursor = 0; el->el_chared.c_vcmd.action = NOP; el->el_chared.c_vcmd.pos = el->el_line.buffer; el->el_chared.c_kill.mark = el->el_line.buffer; el->el_map.current = el->el_map.key; el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */ el->el_state.doingarg = 0; el->el_state.metanext = 0; el->el_state.argument = 1; el->el_state.lastcmd = ED_UNASSIGNED; el->el_history.eventno = 0; if (mclear) ch__clearmacro(el); } private void ch__clearmacro(EditLine *el) { c_macro_t *ma = &el->el_chared.c_macro; while (ma->level >= 0) el_free((ptr_t)ma->macro[ma->level--]); } /* ch_enlargebufs(): * Enlarge line buffer to be able to hold twice as much characters. * Returns 1 if successful, 0 if not. */ protected int ch_enlargebufs(EditLine *el, size_t addlen) { size_t sz, newsz; char *newbuffer, *oldbuf, *oldkbuf; sz = el->el_line.limit - el->el_line.buffer + EL_LEAVE; newsz = sz * 2; /* * If newly required length is longer than current buffer, we need * to make the buffer big enough to hold both old and new stuff. */ if (addlen > sz) { while(newsz - sz < addlen) newsz *= 2; } /* * Reallocate line buffer. */ newbuffer = el_realloc(el->el_line.buffer, newsz); if (!newbuffer) return 0; /* zero the newly added memory, leave old data in */ (void) memset(&newbuffer[sz], 0, newsz - sz); oldbuf = el->el_line.buffer; el->el_line.buffer = newbuffer; el->el_line.cursor = newbuffer + (el->el_line.cursor - oldbuf); el->el_line.lastchar = newbuffer + (el->el_line.lastchar - oldbuf); /* don't set new size until all buffers are enlarged */ el->el_line.limit = &newbuffer[sz - EL_LEAVE]; /* * Reallocate kill buffer. */ newbuffer = el_realloc(el->el_chared.c_kill.buf, newsz); if (!newbuffer) return 0; /* zero the newly added memory, leave old data in */ (void) memset(&newbuffer[sz], 0, newsz - sz); oldkbuf = el->el_chared.c_kill.buf; el->el_chared.c_kill.buf = newbuffer; el->el_chared.c_kill.last = newbuffer + (el->el_chared.c_kill.last - oldkbuf); el->el_chared.c_kill.mark = el->el_line.buffer + (el->el_chared.c_kill.mark - oldbuf); /* * Reallocate undo buffer. */ newbuffer = el_realloc(el->el_chared.c_undo.buf, newsz); if (!newbuffer) return 0; /* zero the newly added memory, leave old data in */ (void) memset(&newbuffer[sz], 0, newsz - sz); el->el_chared.c_undo.buf = newbuffer; newbuffer = el_realloc(el->el_chared.c_redo.buf, newsz); if (!newbuffer) return 0; el->el_chared.c_redo.pos = newbuffer + (el->el_chared.c_redo.pos - el->el_chared.c_redo.buf); el->el_chared.c_redo.lim = newbuffer + (el->el_chared.c_redo.lim - el->el_chared.c_redo.buf); el->el_chared.c_redo.buf = newbuffer; if (!hist_enlargebuf(el, sz, newsz)) return 0; /* Safe to set enlarged buffer size */ el->el_line.limit = &el->el_line.buffer[newsz - EL_LEAVE]; return 1; } /* ch_end(): * Free the data structures used by the editor */ protected void ch_end(EditLine *el) { el_free((ptr_t) el->el_line.buffer); el->el_line.buffer = NULL; el->el_line.limit = NULL; el_free((ptr_t) el->el_chared.c_undo.buf); el->el_chared.c_undo.buf = NULL; el_free((ptr_t) el->el_chared.c_redo.buf); el->el_chared.c_redo.buf = NULL; el->el_chared.c_redo.pos = NULL; el->el_chared.c_redo.lim = NULL; el->el_chared.c_redo.cmd = ED_UNASSIGNED; el_free((ptr_t) el->el_chared.c_kill.buf); el->el_chared.c_kill.buf = NULL; ch_reset(el, 1); el_free((ptr_t) el->el_chared.c_macro.macro); el->el_chared.c_macro.macro = NULL; } /* el_insertstr(): * Insert string at cursorI */ public int el_insertstr(EditLine *el, const char *s) { size_t len; if ((len = strlen(s)) == 0) return (-1); if (el->el_line.lastchar + len >= el->el_line.limit) { if (!ch_enlargebufs(el, len)) return (-1); } c_insert(el, (int)len); while (*s) *el->el_line.cursor++ = *s++; return (0); } /* el_deletestr(): * Delete num characters before the cursor */ public void el_deletestr(EditLine *el, int n) { if (n <= 0) return; if (el->el_line.cursor < &el->el_line.buffer[n]) return; c_delbefore(el, n); /* delete before dot */ el->el_line.cursor -= n; if (el->el_line.cursor < el->el_line.buffer) el->el_line.cursor = el->el_line.buffer; } /* c_gets(): * Get a string */ protected int c_gets(EditLine *el, char *buf, const char *prompt) { char ch; ssize_t len; char *cp = el->el_line.buffer; if (prompt) { len = strlen(prompt); memcpy(cp, prompt, (size_t)len); cp += len; } len = 0; for (;;) { el->el_line.cursor = cp; *cp = ' '; el->el_line.lastchar = cp + 1; re_refresh(el); if (el_getc(el, &ch) != 1) { ed_end_of_file(el, 0); len = -1; break; } switch (ch) { case 0010: /* Delete and backspace */ case 0177: if (len == 0) { len = -1; break; } cp--; continue; case 0033: /* ESC */ case '\r': /* Newline */ case '\n': buf[len] = ch; break; default: if (len >= EL_BUFSIZ - 16) term_beep(el); else { buf[len++] = ch; *cp++ = ch; } continue; } break; } el->el_line.buffer[0] = '\0'; el->el_line.lastchar = el->el_line.buffer; el->el_line.cursor = el->el_line.buffer; return (int)len; } /* c_hpos(): * Return the current horizontal position of the cursor */ protected int c_hpos(EditLine *el) { char *ptr; /* * Find how many characters till the beginning of this line. */ if (el->el_line.cursor == el->el_line.buffer) return (0); else { for (ptr = el->el_line.cursor - 1; ptr >= el->el_line.buffer && *ptr != '\n'; ptr--) continue; return (int)(el->el_line.cursor - ptr - 1); } } clt/falcon/editline/src/chared.h000066400000000000000000000114401176363201700170730ustar00rootroot00000000000000/* $NetBSD: chared.h,v 1.18 2009/02/15 21:55:23 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)chared.h 8.1 (Berkeley) 6/4/93 */ /* * el.chared.h: Character editor interface */ #ifndef _h_el_chared #define _h_el_chared #include #include #include "histedit.h" #define EL_MAXMACRO 10 /* * This is an issue of basic "vi" look-and-feel. Defining VI_MOVE works * like real vi: i.e. the transition from command<->insert modes moves * the cursor. * * On the other hand we really don't want to move the cursor, because * all the editing commands don't include the character under the cursor. * Probably the best fix is to make all the editing commands aware of * this fact. */ #define VI_MOVE typedef struct c_macro_t { int level; int offset; char **macro; } c_macro_t; /* * Undo information for vi - no undo in emacs (yet) */ typedef struct c_undo_t { ssize_t len; /* length of saved line */ int cursor; /* position of saved cursor */ char *buf; /* full saved text */ } c_undo_t; /* redo for vi */ typedef struct c_redo_t { char *buf; /* redo insert key sequence */ char *pos; char *lim; el_action_t cmd; /* command to redo */ char ch; /* char that invoked it */ int count; int action; /* from cv_action() */ } c_redo_t; /* * Current action information for vi */ typedef struct c_vcmd_t { int action; char *pos; } c_vcmd_t; /* * Kill buffer for emacs */ typedef struct c_kill_t { char *buf; char *last; char *mark; } c_kill_t; /* * Note that we use both data structures because the user can bind * commands from both editors! */ typedef struct el_chared_t { c_undo_t c_undo; c_kill_t c_kill; c_redo_t c_redo; c_vcmd_t c_vcmd; c_macro_t c_macro; } el_chared_t; #define STRQQ "\"\"" #define isglob(a) (strchr("*[]?", (a)) != NULL) #define isword(a) (isprint(a)) #define NOP 0x00 #define DELETE 0x01 #define INSERT 0x02 #define YANK 0x04 #define CHAR_FWD (+1) #define CHAR_BACK (-1) #define MODE_INSERT 0 #define MODE_REPLACE 1 #define MODE_REPLACE_1 2 #include "common.h" #include "vi.h" #include "emacs.h" #include "search.h" #include "fcns.h" protected int cv__isword(int); protected int cv__isWord(int); protected void cv_delfini(EditLine *); protected char *cv__endword(char *, char *, int, int (*)(int)); protected int ce__isword(int); protected void cv_undo(EditLine *); protected void cv_yank(EditLine *, const char *, int); protected char *cv_next_word(EditLine*, char *, char *, int, int (*)(int)); protected char *cv_prev_word(char *, char *, int, int (*)(int)); protected char *c__next_word(char *, char *, int, int (*)(int)); protected char *c__prev_word(char *, char *, int, int (*)(int)); protected void c_insert(EditLine *, int); protected void c_delbefore(EditLine *, int); protected void c_delbefore1(EditLine *); protected void c_delafter(EditLine *, int); protected void c_delafter1(EditLine *); protected int c_gets(EditLine *, char *, const char *); protected int c_hpos(EditLine *); protected int ch_init(EditLine *); protected void ch_reset(EditLine *, int); protected int ch_enlargebufs(EditLine *, size_t); protected void ch_end(EditLine *); #endif /* _h_el_chared */ clt/falcon/editline/src/common.c000066400000000000000000000467611176363201700171460ustar00rootroot00000000000000/* $NetBSD: common.c,v 1.23 2009/02/27 04:18:45 msaitoh Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)common.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: common.c,v 1.23 2009/02/27 04:18:45 msaitoh Exp $"); #endif #endif /* not lint && not SCCSID */ /* * common.c: Common Editor functions */ #include "el.h" /* ed_end_of_file(): * Indicate end of file * [^D] */ protected el_action_t /*ARGSUSED*/ ed_end_of_file(EditLine *el, int c __attribute__((__unused__))) { re_goto_bottom(el); *el->el_line.lastchar = '\0'; return (CC_EOF); } /* ed_insert(): * Add character to the line * Insert a character [bound to all insert keys] */ protected el_action_t ed_insert(EditLine *el, int c) { int count = el->el_state.argument; if (c == '\0') return (CC_ERROR); if (el->el_line.lastchar + el->el_state.argument >= el->el_line.limit) { /* end of buffer space, try to allocate more */ if (!ch_enlargebufs(el, (size_t) count)) return CC_ERROR; /* error allocating more */ } if (count == 1) { if (el->el_state.inputmode == MODE_INSERT || el->el_line.cursor >= el->el_line.lastchar) c_insert(el, 1); *el->el_line.cursor++ = c; re_fastaddc(el); /* fast refresh for one char. */ } else { if (el->el_state.inputmode != MODE_REPLACE_1) c_insert(el, el->el_state.argument); while (count-- && el->el_line.cursor < el->el_line.lastchar) *el->el_line.cursor++ = c; re_refresh(el); } if (el->el_state.inputmode == MODE_REPLACE_1) return vi_command_mode(el, 0); return (CC_NORM); } /* ed_delete_prev_word(): * Delete from beginning of current word to cursor * [M-^?] [^W] */ protected el_action_t /*ARGSUSED*/ ed_delete_prev_word(EditLine *el, int c __attribute__((__unused__))) { char *cp, *p, *kp; if (el->el_line.cursor == el->el_line.buffer) return (CC_ERROR); cp = c__prev_word(el->el_line.cursor, el->el_line.buffer, el->el_state.argument, ce__isword); for (p = cp, kp = el->el_chared.c_kill.buf; p < el->el_line.cursor; p++) *kp++ = *p; el->el_chared.c_kill.last = kp; c_delbefore(el, (int)(el->el_line.cursor - cp));/* delete before dot */ el->el_line.cursor = cp; if (el->el_line.cursor < el->el_line.buffer) el->el_line.cursor = el->el_line.buffer; /* bounds check */ return (CC_REFRESH); } /* ed_delete_next_char(): * Delete character under cursor * [^D] [x] */ protected el_action_t /*ARGSUSED*/ ed_delete_next_char(EditLine *el, int c) { #ifdef notdef /* XXX */ #define EL el->el_line (void) fprintf(el->el_errlfile, "\nD(b: %x(%s) c: %x(%s) last: %x(%s) limit: %x(%s)\n", EL.buffer, EL.buffer, EL.cursor, EL.cursor, EL.lastchar, EL.lastchar, EL.limit, EL.limit); #endif if (el->el_line.cursor == el->el_line.lastchar) { /* if I'm at the end */ if (el->el_map.type == MAP_VI) { if (el->el_line.cursor == el->el_line.buffer) { /* if I'm also at the beginning */ #ifdef KSHVI return (CC_ERROR); #else /* then do an EOF */ term_writechar(el, c); return (CC_EOF); #endif } else { #ifdef KSHVI el->el_line.cursor--; #else return (CC_ERROR); #endif } } else { if (el->el_line.cursor != el->el_line.buffer) el->el_line.cursor--; else return (CC_ERROR); } } c_delafter(el, el->el_state.argument); /* delete after dot */ if (el->el_line.cursor >= el->el_line.lastchar && el->el_line.cursor > el->el_line.buffer) /* bounds check */ el->el_line.cursor = el->el_line.lastchar - 1; return (CC_REFRESH); } /* ed_kill_line(): * Cut to the end of line * [^K] [^K] */ protected el_action_t /*ARGSUSED*/ ed_kill_line(EditLine *el, int c __attribute__((__unused__))) { char *kp, *cp; cp = el->el_line.cursor; kp = el->el_chared.c_kill.buf; while (cp < el->el_line.lastchar) *kp++ = *cp++; /* copy it */ el->el_chared.c_kill.last = kp; /* zap! -- delete to end */ el->el_line.lastchar = el->el_line.cursor; return (CC_REFRESH); } /* ed_move_to_end(): * Move cursor to the end of line * [^E] [^E] */ protected el_action_t /*ARGSUSED*/ ed_move_to_end(EditLine *el, int c __attribute__((__unused__))) { el->el_line.cursor = el->el_line.lastchar; if (el->el_map.type == MAP_VI) { if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return (CC_REFRESH); } #ifdef VI_MOVE el->el_line.cursor--; #endif } return (CC_CURSOR); } /* ed_move_to_beg(): * Move cursor to the beginning of line * [^A] [^A] */ protected el_action_t /*ARGSUSED*/ ed_move_to_beg(EditLine *el, int c __attribute__((__unused__))) { el->el_line.cursor = el->el_line.buffer; if (el->el_map.type == MAP_VI) { /* We want FIRST non space character */ while (isspace((unsigned char) *el->el_line.cursor)) el->el_line.cursor++; if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return (CC_REFRESH); } } return (CC_CURSOR); } /* ed_transpose_chars(): * Exchange the character to the left of the cursor with the one under it * [^T] [^T] */ protected el_action_t ed_transpose_chars(EditLine *el, int c) { if (el->el_line.cursor < el->el_line.lastchar) { if (el->el_line.lastchar <= &el->el_line.buffer[1]) return (CC_ERROR); else el->el_line.cursor++; } if (el->el_line.cursor > &el->el_line.buffer[1]) { /* must have at least two chars entered */ c = el->el_line.cursor[-2]; el->el_line.cursor[-2] = el->el_line.cursor[-1]; el->el_line.cursor[-1] = c; return (CC_REFRESH); } else return (CC_ERROR); } /* ed_next_char(): * Move to the right one character * [^F] [^F] */ protected el_action_t /*ARGSUSED*/ ed_next_char(EditLine *el, int c __attribute__((__unused__))) { char *lim = el->el_line.lastchar; if (el->el_line.cursor >= lim || (el->el_line.cursor == lim - 1 && el->el_map.type == MAP_VI && el->el_chared.c_vcmd.action == NOP)) return (CC_ERROR); el->el_line.cursor += el->el_state.argument; if (el->el_line.cursor > lim) el->el_line.cursor = lim; if (el->el_map.type == MAP_VI) if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return (CC_REFRESH); } return (CC_CURSOR); } /* ed_prev_word(): * Move to the beginning of the current word * [M-b] [b] */ protected el_action_t /*ARGSUSED*/ ed_prev_word(EditLine *el, int c __attribute__((__unused__))) { if (el->el_line.cursor == el->el_line.buffer) return (CC_ERROR); el->el_line.cursor = c__prev_word(el->el_line.cursor, el->el_line.buffer, el->el_state.argument, ce__isword); if (el->el_map.type == MAP_VI) if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return (CC_REFRESH); } return (CC_CURSOR); } /* ed_prev_char(): * Move to the left one character * [^B] [^B] */ protected el_action_t /*ARGSUSED*/ ed_prev_char(EditLine *el, int c __attribute__((__unused__))) { if (el->el_line.cursor > el->el_line.buffer) { el->el_line.cursor -= el->el_state.argument; if (el->el_line.cursor < el->el_line.buffer) el->el_line.cursor = el->el_line.buffer; if (el->el_map.type == MAP_VI) if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return (CC_REFRESH); } return (CC_CURSOR); } else return (CC_ERROR); } /* ed_quoted_insert(): * Add the next character typed verbatim * [^V] [^V] */ protected el_action_t ed_quoted_insert(EditLine *el, int c) { int num; char tc; tty_quotemode(el); num = el_getc(el, &tc); c = (unsigned char) tc; tty_noquotemode(el); if (num == 1) return (ed_insert(el, c)); else return (ed_end_of_file(el, 0)); } /* ed_digit(): * Adds to argument or enters a digit */ protected el_action_t ed_digit(EditLine *el, int c) { if (!isdigit((unsigned char)c)) return (CC_ERROR); if (el->el_state.doingarg) { /* if doing an arg, add this in... */ if (el->el_state.lastcmd == EM_UNIVERSAL_ARGUMENT) el->el_state.argument = c - '0'; else { if (el->el_state.argument > 1000000) return (CC_ERROR); el->el_state.argument = (el->el_state.argument * 10) + (c - '0'); } return (CC_ARGHACK); } return ed_insert(el, c); } /* ed_argument_digit(): * Digit that starts argument * For ESC-n */ protected el_action_t ed_argument_digit(EditLine *el, int c) { if (!isdigit((unsigned char)c)) return (CC_ERROR); if (el->el_state.doingarg) { if (el->el_state.argument > 1000000) return (CC_ERROR); el->el_state.argument = (el->el_state.argument * 10) + (c - '0'); } else { /* else starting an argument */ el->el_state.argument = c - '0'; el->el_state.doingarg = 1; } return (CC_ARGHACK); } /* ed_unassigned(): * Indicates unbound character * Bound to keys that are not assigned */ protected el_action_t /*ARGSUSED*/ ed_unassigned(EditLine *el, int c __attribute__((__unused__))) { return (CC_ERROR); } /** ** TTY key handling. **/ /* ed_tty_sigint(): * Tty interrupt character * [^C] */ protected el_action_t /*ARGSUSED*/ ed_tty_sigint(EditLine *el __attribute__((__unused__)), int c __attribute__((__unused__))) { return (CC_NORM); } /* ed_tty_dsusp(): * Tty delayed suspend character * [^Y] */ protected el_action_t /*ARGSUSED*/ ed_tty_dsusp(EditLine *el __attribute__((__unused__)), int c __attribute__((__unused__))) { return (CC_NORM); } /* ed_tty_flush_output(): * Tty flush output characters * [^O] */ protected el_action_t /*ARGSUSED*/ ed_tty_flush_output(EditLine *el __attribute__((__unused__)), int c __attribute__((__unused__))) { return (CC_NORM); } /* ed_tty_sigquit(): * Tty quit character * [^\] */ protected el_action_t /*ARGSUSED*/ ed_tty_sigquit(EditLine *el __attribute__((__unused__)), int c __attribute__((__unused__))) { return (CC_NORM); } /* ed_tty_sigtstp(): * Tty suspend character * [^Z] */ protected el_action_t /*ARGSUSED*/ ed_tty_sigtstp(EditLine *el __attribute__((__unused__)), int c __attribute__((__unused__))) { return (CC_NORM); } /* ed_tty_stop_output(): * Tty disallow output characters * [^S] */ protected el_action_t /*ARGSUSED*/ ed_tty_stop_output(EditLine *el __attribute__((__unused__)), int c __attribute__((__unused__))) { return (CC_NORM); } /* ed_tty_start_output(): * Tty allow output characters * [^Q] */ protected el_action_t /*ARGSUSED*/ ed_tty_start_output(EditLine *el __attribute__((__unused__)), int c __attribute__((__unused__))) { return (CC_NORM); } /* ed_newline(): * Execute command * [^J] */ protected el_action_t /*ARGSUSED*/ ed_newline(EditLine *el, int c __attribute__((__unused__))) { re_goto_bottom(el); *el->el_line.lastchar++ = '\n'; *el->el_line.lastchar = '\0'; return (CC_NEWLINE); } /* ed_delete_prev_char(): * Delete the character to the left of the cursor * [^?] */ protected el_action_t /*ARGSUSED*/ ed_delete_prev_char(EditLine *el, int c __attribute__((__unused__))) { if (el->el_line.cursor <= el->el_line.buffer) return (CC_ERROR); c_delbefore(el, el->el_state.argument); el->el_line.cursor -= el->el_state.argument; if (el->el_line.cursor < el->el_line.buffer) el->el_line.cursor = el->el_line.buffer; return (CC_REFRESH); } /* ed_clear_screen(): * Clear screen leaving current line at the top * [^L] */ protected el_action_t /*ARGSUSED*/ ed_clear_screen(EditLine *el, int c __attribute__((__unused__))) { term_clear_screen(el); /* clear the whole real screen */ re_clear_display(el); /* reset everything */ return (CC_REFRESH); } /* ed_redisplay(): * Redisplay everything * ^R */ protected el_action_t /*ARGSUSED*/ ed_redisplay(EditLine *el __attribute__((__unused__)), int c __attribute__((__unused__))) { return (CC_REDISPLAY); } /* ed_start_over(): * Erase current line and start from scratch * [^G] */ protected el_action_t /*ARGSUSED*/ ed_start_over(EditLine *el, int c __attribute__((__unused__))) { ch_reset(el, 0); return (CC_REFRESH); } /* ed_sequence_lead_in(): * First character in a bound sequence * Placeholder for external keys */ protected el_action_t /*ARGSUSED*/ ed_sequence_lead_in(EditLine *el __attribute__((__unused__)), int c __attribute__((__unused__))) { return (CC_NORM); } /* ed_prev_history(): * Move to the previous history line * [^P] [k] */ protected el_action_t /*ARGSUSED*/ ed_prev_history(EditLine *el, int c __attribute__((__unused__))) { char beep = 0; int sv_event = el->el_history.eventno; el->el_chared.c_undo.len = -1; *el->el_line.lastchar = '\0'; /* just in case */ if (el->el_history.eventno == 0) { /* save the current buffer * away */ (void) strncpy(el->el_history.buf, el->el_line.buffer, EL_BUFSIZ); el->el_history.last = el->el_history.buf + (el->el_line.lastchar - el->el_line.buffer); } el->el_history.eventno += el->el_state.argument; if (hist_get(el) == CC_ERROR) { if (el->el_map.type == MAP_VI) { el->el_history.eventno = sv_event; return CC_ERROR; } beep = 1; /* el->el_history.eventno was fixed by first call */ (void) hist_get(el); } if (beep) return CC_REFRESH_BEEP; return CC_REFRESH; } /* ed_next_history(): * Move to the next history line * [^N] [j] */ protected el_action_t /*ARGSUSED*/ ed_next_history(EditLine *el, int c __attribute__((__unused__))) { el_action_t beep = CC_REFRESH, rval; el->el_chared.c_undo.len = -1; *el->el_line.lastchar = '\0'; /* just in case */ el->el_history.eventno -= el->el_state.argument; if (el->el_history.eventno < 0) { el->el_history.eventno = 0; beep = CC_REFRESH_BEEP; } rval = hist_get(el); if (rval == CC_REFRESH) return beep; return rval; } /* ed_search_prev_history(): * Search previous in history for a line matching the current * next search history [M-P] [K] */ protected el_action_t /*ARGSUSED*/ ed_search_prev_history(EditLine *el, int c __attribute__((__unused__))) { const char *hp; int h; bool_t found = 0; el->el_chared.c_vcmd.action = NOP; el->el_chared.c_undo.len = -1; *el->el_line.lastchar = '\0'; /* just in case */ if (el->el_history.eventno < 0) { #ifdef DEBUG_EDIT (void) fprintf(el->el_errfile, "e_prev_search_hist(): eventno < 0;\n"); #endif el->el_history.eventno = 0; return (CC_ERROR); } if (el->el_history.eventno == 0) { (void) strncpy(el->el_history.buf, el->el_line.buffer, EL_BUFSIZ); el->el_history.last = el->el_history.buf + (el->el_line.lastchar - el->el_line.buffer); } if (el->el_history.ref == NULL) return (CC_ERROR); hp = HIST_FIRST(el); if (hp == NULL) return (CC_ERROR); c_setpat(el); /* Set search pattern !! */ for (h = 1; h <= el->el_history.eventno; h++) hp = HIST_NEXT(el); while (hp != NULL) { #ifdef SDEBUG (void) fprintf(el->el_errfile, "Comparing with \"%s\"\n", hp); #endif if ((strncmp(hp, el->el_line.buffer, (size_t) (el->el_line.lastchar - el->el_line.buffer)) || hp[el->el_line.lastchar - el->el_line.buffer]) && c_hmatch(el, hp)) { found++; break; } h++; hp = HIST_NEXT(el); } if (!found) { #ifdef SDEBUG (void) fprintf(el->el_errfile, "not found\n"); #endif return (CC_ERROR); } el->el_history.eventno = h; return (hist_get(el)); } /* ed_search_next_history(): * Search next in history for a line matching the current * [M-N] [J] */ protected el_action_t /*ARGSUSED*/ ed_search_next_history(EditLine *el, int c __attribute__((__unused__))) { const char *hp; int h; bool_t found = 0; el->el_chared.c_vcmd.action = NOP; el->el_chared.c_undo.len = -1; *el->el_line.lastchar = '\0'; /* just in case */ if (el->el_history.eventno == 0) return (CC_ERROR); if (el->el_history.ref == NULL) return (CC_ERROR); hp = HIST_FIRST(el); if (hp == NULL) return (CC_ERROR); c_setpat(el); /* Set search pattern !! */ for (h = 1; h < el->el_history.eventno && hp; h++) { #ifdef SDEBUG (void) fprintf(el->el_errfile, "Comparing with \"%s\"\n", hp); #endif if ((strncmp(hp, el->el_line.buffer, (size_t) (el->el_line.lastchar - el->el_line.buffer)) || hp[el->el_line.lastchar - el->el_line.buffer]) && c_hmatch(el, hp)) found = h; hp = HIST_NEXT(el); } if (!found) { /* is it the current history number? */ if (!c_hmatch(el, el->el_history.buf)) { #ifdef SDEBUG (void) fprintf(el->el_errfile, "not found\n"); #endif return (CC_ERROR); } } el->el_history.eventno = found; return (hist_get(el)); } /* ed_prev_line(): * Move up one line * Could be [k] [^p] */ protected el_action_t /*ARGSUSED*/ ed_prev_line(EditLine *el, int c __attribute__((__unused__))) { char *ptr; int nchars = c_hpos(el); /* * Move to the line requested */ if (*(ptr = el->el_line.cursor) == '\n') ptr--; for (; ptr >= el->el_line.buffer; ptr--) if (*ptr == '\n' && --el->el_state.argument <= 0) break; if (el->el_state.argument > 0) return (CC_ERROR); /* * Move to the beginning of the line */ for (ptr--; ptr >= el->el_line.buffer && *ptr != '\n'; ptr--) continue; /* * Move to the character requested */ for (ptr++; nchars-- > 0 && ptr < el->el_line.lastchar && *ptr != '\n'; ptr++) continue; el->el_line.cursor = ptr; return (CC_CURSOR); } /* ed_next_line(): * Move down one line * Could be [j] [^n] */ protected el_action_t /*ARGSUSED*/ ed_next_line(EditLine *el, int c __attribute__((__unused__))) { char *ptr; int nchars = c_hpos(el); /* * Move to the line requested */ for (ptr = el->el_line.cursor; ptr < el->el_line.lastchar; ptr++) if (*ptr == '\n' && --el->el_state.argument <= 0) break; if (el->el_state.argument > 0) return (CC_ERROR); /* * Move to the character requested */ for (ptr++; nchars-- > 0 && ptr < el->el_line.lastchar && *ptr != '\n'; ptr++) continue; el->el_line.cursor = ptr; return (CC_CURSOR); } /* ed_command(): * Editline extended command * [M-X] [:] */ protected el_action_t /*ARGSUSED*/ ed_command(EditLine *el, int c __attribute__((__unused__))) { char tmpbuf[EL_BUFSIZ]; int tmplen; tmplen = c_gets(el, tmpbuf, "\n: "); term__putc(el, '\n'); if (tmplen < 0 || (tmpbuf[tmplen] = 0, parse_line(el, tmpbuf)) == -1) term_beep(el); el->el_map.current = el->el_map.key; re_clear_display(el); return CC_REFRESH; } clt/falcon/editline/src/editline/000077500000000000000000000000001176363201700172715ustar00rootroot00000000000000clt/falcon/editline/src/editline/readline.h000066400000000000000000000164061176363201700212340ustar00rootroot00000000000000/* $NetBSD: readline.h,v 1.30 2009/09/07 21:24:34 christos Exp $ */ /*- * Copyright (c) 1997 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jaromir Dolecek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _READLINE_H_ #define _READLINE_H_ #include #include /* list of readline stuff supported by editline library's readline wrapper */ /* typedefs */ typedef int Function(const char *, int); typedef void VFunction(void); typedef void VCPFunction(char *); typedef char *CPFunction(const char *, int); typedef char **CPPFunction(const char *, int, int); typedef char *rl_compentry_func_t(const char *, int); typedef int rl_command_func_t(int, int); /* only supports length */ typedef struct { int length; } HISTORY_STATE; typedef void *histdata_t; typedef struct _hist_entry { const char *line; histdata_t data; } HIST_ENTRY; typedef struct _keymap_entry { char type; #define ISFUNC 0 #define ISKMAP 1 #define ISMACR 2 Function *function; } KEYMAP_ENTRY; #define KEYMAP_SIZE 256 typedef KEYMAP_ENTRY KEYMAP_ENTRY_ARRAY[KEYMAP_SIZE]; typedef KEYMAP_ENTRY *Keymap; #define control_character_threshold 0x20 #define control_character_bit 0x40 #ifndef CTRL #include #if !defined(__sun) && !defined(__hpux) && !defined(_AIX) #include #endif #ifndef CTRL #define CTRL(c) ((c) & 037) #endif #endif #ifndef UNCTRL #define UNCTRL(c) (((c) - 'a' + 'A')|control_character_bit) #endif #define RUBOUT 0x7f #define ABORT_CHAR CTRL('G') #define RL_READLINE_VERSION 0x0402 #define RL_PROMPT_START_IGNORE '\1' #define RL_PROMPT_END_IGNORE '\2' /* global variables used by readline enabled applications */ #ifdef __cplusplus extern "C" { #endif extern const char *rl_library_version; extern int rl_readline_version; extern char *rl_readline_name; extern FILE *rl_instream; extern FILE *rl_outstream; extern char *rl_line_buffer; extern int rl_point, rl_end; extern int history_base, history_length; extern int max_input_history; extern char *rl_basic_word_break_characters; extern char *rl_completer_word_break_characters; extern char *rl_completer_quote_characters; extern Function *rl_completion_entry_function; extern CPPFunction *rl_attempted_completion_function; extern int rl_attempted_completion_over; extern int rl_completion_type; extern int rl_completion_query_items; extern char *rl_special_prefixes; extern int rl_completion_append_character; extern int rl_inhibit_completion; extern Function *rl_pre_input_hook; extern Function *rl_startup_hook; extern char *rl_terminal_name; extern int rl_already_prompted; extern char *rl_prompt; /* * The following is not implemented */ extern KEYMAP_ENTRY_ARRAY emacs_standard_keymap, emacs_meta_keymap, emacs_ctlx_keymap; extern int rl_filename_completion_desired; extern int rl_ignore_completion_duplicates; extern int (*rl_getc_function)(FILE *); extern VFunction *rl_redisplay_function; extern VFunction *rl_completion_display_matches_hook; extern VFunction *rl_prep_term_function; extern VFunction *rl_deprep_term_function; extern int readline_echoing_p; extern int _rl_print_completions_horizontally; /* supported functions */ char *readline(const char *); int rl_initialize(void); void using_history(void); int add_history(const char *); void clear_history(void); void stifle_history(int); int unstifle_history(void); int history_is_stifled(void); int where_history(void); HIST_ENTRY *current_history(void); HIST_ENTRY *history_get(int); HIST_ENTRY *remove_history(int); /*###152 [lint] syntax error 'histdata_t' [249]%%%*/ HIST_ENTRY *replace_history_entry(int, const char *, histdata_t); int history_total_bytes(void); int history_set_pos(int); HIST_ENTRY *previous_history(void); HIST_ENTRY *next_history(void); int history_search(const char *, int); int history_search_prefix(const char *, int); int history_search_pos(const char *, int, int); int read_history(const char *); int write_history(const char *); int history_truncate_file (const char *, int); int history_expand(char *, char **); char **history_tokenize(const char *); const char *get_history_event(const char *, int *, int); char *history_arg_extract(int, int, const char *); char *tilde_expand(char *); char *filename_completion_function(const char *, int); char *username_completion_function(const char *, int); int rl_complete(int, int); int rl_read_key(void); char **completion_matches(const char *, CPFunction *); void rl_display_match_list(char **, int, int); int rl_insert(int, int); int rl_insert_text(const char *); void rl_reset_terminal(const char *); int rl_bind_key(int, rl_command_func_t *); int rl_newline(int, int); void rl_callback_read_char(void); void rl_callback_handler_install(const char *, VCPFunction *); void rl_callback_handler_remove(void); void rl_redisplay(void); int rl_get_previous_history(int, int); void rl_prep_terminal(int); void rl_deprep_terminal(void); int rl_read_init_file(const char *); int rl_parse_and_bind(const char *); int rl_variable_bind(const char *, const char *); void rl_stuff_char(int); int rl_add_defun(const char *, Function *, int); HISTORY_STATE *history_get_history_state(void); void rl_get_screen_size(int *, int *); void rl_set_screen_size(int, int); char *rl_filename_completion_function (const char *, int); int _rl_abort_internal(void); int _rl_qsort_string_compare(char **, char **); char **rl_completion_matches(const char *, rl_compentry_func_t *); void rl_forced_update_display(void); int rl_set_prompt(const char *); /* * The following are not implemented */ int rl_kill_text(int, int); Keymap rl_get_keymap(void); void rl_set_keymap(Keymap); Keymap rl_make_bare_keymap(void); int rl_generic_bind(int, const char *, const char *, Keymap); int rl_bind_key_in_map(int, rl_command_func_t *, Keymap); void rl_cleanup_after_signal(void); void rl_free_line_state(void); #ifdef __cplusplus } #endif #endif /* _READLINE_H_ */ clt/falcon/editline/src/el.c000066400000000000000000000253401176363201700162440ustar00rootroot00000000000000/* $NetBSD: el.c,v 1.55 2009/07/25 21:19:23 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)el.c 8.2 (Berkeley) 1/3/94"; #else __RCSID("$NetBSD: el.c,v 1.55 2009/07/25 21:19:23 christos Exp $"); #endif #endif /* not lint && not SCCSID */ #ifndef MAXPATHLEN #define MAXPATHLEN 4096 #endif /* * el.c: EditLine interface functions */ #include #include #include #include #include #include #include "el.h" /* el_init(): * Initialize editline and set default parameters. */ public EditLine * el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr) { EditLine *el = (EditLine *) el_malloc(sizeof(EditLine)); if (el == NULL) return (NULL); memset(el, 0, sizeof(EditLine)); el->el_infile = fin; el->el_outfile = fout; el->el_errfile = ferr; el->el_infd = fileno(fin); if ((el->el_prog = el_strdup(prog)) == NULL) { el_free(el); return NULL; } /* * Initialize all the modules. Order is important!!! */ el->el_flags = 0; if (term_init(el) == -1) { el_free(el->el_prog); el_free(el); return NULL; } (void) key_init(el); (void) map_init(el); if (tty_init(el) == -1) el->el_flags |= NO_TTY; (void) ch_init(el); (void) search_init(el); (void) hist_init(el); (void) prompt_init(el); (void) sig_init(el); (void) read_init(el); return (el); } /* el_end(): * Clean up. */ public void el_end(EditLine *el) { if (el == NULL) return; el_reset(el); term_end(el); key_end(el); map_end(el); tty_end(el); ch_end(el); search_end(el); hist_end(el); prompt_end(el); sig_end(el); el_free((ptr_t) el->el_prog); el_free((ptr_t) el); } /* el_reset(): * Reset the tty and the parser */ public void el_reset(EditLine *el) { tty_cookedmode(el); ch_reset(el, 0); /* XXX: Do we want that? */ } /* el_set(): * set the editline parameters */ public int el_set(EditLine *el, int op, ...) { va_list ap; int rv = 0; if (el == NULL) return (-1); va_start(ap, op); switch (op) { case EL_PROMPT: case EL_RPROMPT: { el_pfunc_t p = va_arg(ap, el_pfunc_t); rv = prompt_set(el, p, 0, op); break; } case EL_PROMPT_ESC: case EL_RPROMPT_ESC: { el_pfunc_t p = va_arg(ap, el_pfunc_t); char c = va_arg(ap, int); rv = prompt_set(el, p, c, op); break; } case EL_TERMINAL: rv = term_set(el, va_arg(ap, char *)); break; case EL_EDITOR: rv = map_set_editor(el, va_arg(ap, char *)); break; case EL_SIGNAL: if (va_arg(ap, int)) el->el_flags |= HANDLE_SIGNALS; else el->el_flags &= ~HANDLE_SIGNALS; break; case EL_BIND: case EL_TELLTC: case EL_SETTC: case EL_GETTC: case EL_ECHOTC: case EL_SETTY: { const char *argv[20]; int i; for (i = 1; i < 20; i++) if ((argv[i] = va_arg(ap, char *)) == NULL) break; switch (op) { case EL_BIND: argv[0] = "bind"; rv = map_bind(el, i, argv); break; case EL_TELLTC: argv[0] = "telltc"; rv = term_telltc(el, i, argv); break; case EL_SETTC: argv[0] = "settc"; rv = term_settc(el, i, argv); break; case EL_ECHOTC: argv[0] = "echotc"; rv = term_echotc(el, i, argv); break; case EL_SETTY: argv[0] = "setty"; rv = tty_stty(el, i, argv); break; default: rv = -1; EL_ABORT((el->el_errfile, "Bad op %d\n", op)); break; } break; } case EL_ADDFN: { char *name = va_arg(ap, char *); char *help = va_arg(ap, char *); el_func_t func = va_arg(ap, el_func_t); rv = map_addfunc(el, name, help, func); break; } case EL_HIST: { hist_fun_t func = va_arg(ap, hist_fun_t); ptr_t ptr = va_arg(ap, char *); rv = hist_set(el, func, ptr); break; } case EL_EDITMODE: if (va_arg(ap, int)) el->el_flags &= ~EDIT_DISABLED; else el->el_flags |= EDIT_DISABLED; rv = 0; break; case EL_GETCFN: { el_rfunc_t rc = va_arg(ap, el_rfunc_t); rv = el_read_setfn(el, rc); break; } case EL_CLIENTDATA: el->el_data = va_arg(ap, void *); break; case EL_UNBUFFERED: rv = va_arg(ap, int); if (rv && !(el->el_flags & UNBUFFERED)) { el->el_flags |= UNBUFFERED; read_prepare(el); } else if (!rv && (el->el_flags & UNBUFFERED)) { el->el_flags &= ~UNBUFFERED; read_finish(el); } rv = 0; break; case EL_PREP_TERM: rv = va_arg(ap, int); if (rv) (void) tty_rawmode(el); else (void) tty_cookedmode(el); rv = 0; break; case EL_SETFP: { FILE *fp; int what; what = va_arg(ap, int); fp = va_arg(ap, FILE *); rv = 0; switch (what) { case 0: el->el_infile = fp; el->el_infd = fileno(fp); break; case 1: el->el_outfile = fp; break; case 2: el->el_errfile = fp; break; default: rv = -1; break; } break; } case EL_REFRESH: re_clear_display(el); re_refresh(el); term__flush(el); break; default: rv = -1; break; } va_end(ap); return (rv); } /* el_get(): * retrieve the editline parameters */ public int el_get(EditLine *el, int op, ...) { va_list ap; int rv; if (el == NULL) return -1; va_start(ap, op); switch (op) { case EL_PROMPT: case EL_RPROMPT: { el_pfunc_t *p = va_arg(ap, el_pfunc_t *); char *c = va_arg(ap, char *); rv = prompt_get(el, p, c, op); break; } case EL_EDITOR: rv = map_get_editor(el, va_arg(ap, const char **)); break; case EL_SIGNAL: *va_arg(ap, int *) = (el->el_flags & HANDLE_SIGNALS); rv = 0; break; case EL_EDITMODE: *va_arg(ap, int *) = !(el->el_flags & EDIT_DISABLED); rv = 0; break; case EL_TERMINAL: term_get(el, va_arg(ap, const char **)); rv = 0; break; case EL_GETTC: { static char name[] = "gettc"; char *argv[20]; int i; for (i = 1; i < (int)(sizeof(argv) / sizeof(argv[0])); i++) if ((argv[i] = va_arg(ap, char *)) == NULL) break; switch (op) { case EL_GETTC: argv[0] = name; rv = term_gettc(el, i, argv); break; default: rv = -1; EL_ABORT((el->el_errfile, "Bad op %d\n", op)); break; } break; } #if 0 /* XXX */ case EL_ADDFN: { char *name = va_arg(ap, char *); char *help = va_arg(ap, char *); el_func_t func = va_arg(ap, el_func_t); rv = map_addfunc(el, name, help, func); break; } case EL_HIST: { hist_fun_t func = va_arg(ap, hist_fun_t); ptr_t ptr = va_arg(ap, char *); rv = hist_set(el, func, ptr); } break; #endif /* XXX */ case EL_GETCFN: *va_arg(ap, el_rfunc_t *) = el_read_getfn(el); rv = 0; break; case EL_CLIENTDATA: *va_arg(ap, void **) = el->el_data; rv = 0; break; case EL_UNBUFFERED: *va_arg(ap, int *) = (!(el->el_flags & UNBUFFERED)); rv = 0; break; case EL_GETFP: { int what; FILE **fpp; what = va_arg(ap, int); fpp = va_arg(ap, FILE **); rv = 0; switch (what) { case 0: *fpp = el->el_infile; break; case 1: *fpp = el->el_outfile; break; case 2: *fpp = el->el_errfile; break; default: rv = -1; break; } break; } default: rv = -1; break; } va_end(ap); return (rv); } /* el_line(): * Return editing info */ public const LineInfo * el_line(EditLine *el) { return (const LineInfo *) (void *) &el->el_line; } /* el_source(): * Source a file */ public int el_source(EditLine *el, const char *fname) { FILE *fp; size_t len; char *ptr; char path[MAXPATHLEN]; fp = NULL; if (fname == NULL) { static const char elpath[] = "/.editrc"; #ifdef HAVE_ISSETUGID if (issetugid()) return (-1); #endif if ((ptr = getenv("HOME")) == NULL) return (-1); if (strlcpy(path, ptr, sizeof(path)) >= sizeof(path)) return (-1); if (strlcat(path, elpath, sizeof(path)) >= sizeof(path)) return (-1); fname = path; } if (fp == NULL) fp = fopen(fname, "r"); if (fp == NULL) return (-1); while ((ptr = fgetln(fp, &len)) != NULL) { if (len > 0 && ptr[len - 1] == '\n') --len; ptr[len] = '\0'; /* loop until first non-space char or EOL */ while (*ptr != '\0' && isspace((unsigned char)*ptr)) ptr++; if (*ptr == '#') continue; /* ignore, this is a comment line */ if (parse_line(el, ptr) == -1) { (void) fclose(fp); return (-1); } } (void) fclose(fp); return (0); } /* el_resize(): * Called from program when terminal is resized */ public void el_resize(EditLine *el) { int lins, cols; sigset_t oset, nset; (void) sigemptyset(&nset); (void) sigaddset(&nset, SIGWINCH); (void) sigprocmask(SIG_BLOCK, &nset, &oset); /* get the correct window size */ if (term_get_size(el, &lins, &cols)) term_change_size(el, lins, cols); (void) sigprocmask(SIG_SETMASK, &oset, NULL); } /* el_beep(): * Called from the program to beep */ public void el_beep(EditLine *el) { term_beep(el); } /* el_editmode() * Set the state of EDIT_DISABLED from the `edit' command. */ protected int /*ARGSUSED*/ el_editmode(EditLine *el, int argc, const char **argv) { const char *how; if (argv == NULL || argc != 2 || argv[1] == NULL) return (-1); how = argv[1]; if (strcmp(how, "on") == 0) { el->el_flags &= ~EDIT_DISABLED; tty_rawmode(el); } else if (strcmp(how, "off") == 0) { tty_cookedmode(el); el->el_flags |= EDIT_DISABLED; } else { (void) fprintf(el->el_errfile, "edit: Bad value `%s'.\n", how); return (-1); } return (0); } clt/falcon/editline/src/el.h000066400000000000000000000114101176363201700162420ustar00rootroot00000000000000/* $NetBSD: el.h,v 1.18 2009/02/15 21:24:13 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)el.h 8.1 (Berkeley) 6/4/93 */ /* * el.h: Internal structures. */ #ifndef _h_el #define _h_el /* * Local defaults */ #define KSHVI #define VIDEFAULT #define ANCHOR #include #include #define EL_BUFSIZ 1024 /* Maximum line size */ #define HANDLE_SIGNALS 0x01 #define NO_TTY 0x02 #define EDIT_DISABLED 0x04 #define UNBUFFERED 0x08 typedef int bool_t; /* True or not */ typedef unsigned char el_action_t; /* Index to command array */ typedef struct coord_t { /* Position on the screen */ int h; int v; } coord_t; typedef struct el_line_t { char *buffer; /* Input line */ char *cursor; /* Cursor position */ char *lastchar; /* Last character */ const char *limit; /* Max position */ } el_line_t; /* * Editor state */ typedef struct el_state_t { int inputmode; /* What mode are we in? */ int doingarg; /* Are we getting an argument? */ int argument; /* Numeric argument */ int metanext; /* Is the next char a meta char */ el_action_t lastcmd; /* Previous command */ el_action_t thiscmd; /* this command */ char thisch; /* char that generated it */ } el_state_t; /* * Until we come up with something better... */ #define el_strdup(a) strdup(a) #define el_malloc(a) malloc(a) #define el_realloc(a,b) realloc(a, b) #define el_free(a) free(a) #include "tty.h" #include "prompt.h" #include "key.h" #include "el_term.h" #include "refresh.h" #include "chared.h" #include "common.h" #include "search.h" #include "hist.h" #include "map.h" #include "parse.h" #include "sig.h" #include "help.h" #include "read.h" struct editline { char *el_prog; /* the program name */ FILE *el_infile; /* Stdio stuff */ FILE *el_outfile; /* Stdio stuff */ FILE *el_errfile; /* Stdio stuff */ int el_infd; /* Input file descriptor */ int el_flags; /* Various flags. */ int el_errno; /* Local copy of errno */ coord_t el_cursor; /* Cursor location */ char **el_display; /* Real screen image = what is there */ char **el_vdisplay; /* Virtual screen image = what we see */ void *el_data; /* Client data */ el_line_t el_line; /* The current line information */ el_state_t el_state; /* Current editor state */ el_term_t el_term; /* Terminal dependent stuff */ el_tty_t el_tty; /* Tty dependent stuff */ el_refresh_t el_refresh; /* Refresh stuff */ el_prompt_t el_prompt; /* Prompt stuff */ el_prompt_t el_rprompt; /* Prompt stuff */ el_chared_t el_chared; /* Characted editor stuff */ el_map_t el_map; /* Key mapping stuff */ el_key_t el_key; /* Key binding stuff */ el_history_t el_history; /* History stuff */ el_search_t el_search; /* Search stuff */ el_signal_t el_signal; /* Signal handling stuff */ el_read_t el_read; /* Character reading stuff */ }; protected int el_editmode(EditLine *, int, const char **); #ifdef DEBUG #define EL_ABORT(a) do { \ fprintf(el->el_errfile, "%s, %d: ", \ __FILE__, __LINE__); \ fprintf a; \ abort(); \ } while( /*CONSTCOND*/0); #else #define EL_ABORT(a) abort() #endif #endif /* _h_el */ clt/falcon/editline/src/el_term.h000066400000000000000000000121421176363201700172740ustar00rootroot00000000000000/* $NetBSD: term.h,v 1.20 2009/03/31 17:38:27 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)term.h 8.1 (Berkeley) 6/4/93 */ /* * el.term.h: Termcap header */ #ifndef _h_el_term #define _h_el_term #include "histedit.h" typedef struct { /* Symbolic function key bindings */ const char *name; /* name of the key */ int key; /* Index in termcap table */ key_value_t fun; /* Function bound to it */ int type; /* Type of function */ } fkey_t; typedef struct { const char *t_name; /* the terminal name */ coord_t t_size; /* # lines and cols */ int t_flags; #define TERM_CAN_INSERT 0x001 /* Has insert cap */ #define TERM_CAN_DELETE 0x002 /* Has delete cap */ #define TERM_CAN_CEOL 0x004 /* Has CEOL cap */ #define TERM_CAN_TAB 0x008 /* Can use tabs */ #define TERM_CAN_ME 0x010 /* Can turn all attrs. */ #define TERM_CAN_UP 0x020 /* Can move up */ #define TERM_HAS_META 0x040 /* Has a meta key */ #define TERM_HAS_AUTO_MARGINS 0x080 /* Has auto margins */ #define TERM_HAS_MAGIC_MARGINS 0x100 /* Has magic margins */ char *t_buf; /* Termcap buffer */ int t_loc; /* location used */ char **t_str; /* termcap strings */ int *t_val; /* termcap values */ char *t_cap; /* Termcap buffer */ fkey_t *t_fkey; /* Array of keys */ } el_term_t; /* * fKey indexes */ #define A_K_DN 0 #define A_K_UP 1 #define A_K_LT 2 #define A_K_RT 3 #define A_K_HO 4 #define A_K_EN 5 #define A_K_NKEYS 6 #ifdef __sun extern int tgetent(char *, const char *); extern int tgetflag(char *); extern int tgetnum(char *); extern int tputs(const char *, int, int (*)(int)); extern char* tgoto(const char*, int, int); extern char* tgetstr(char*, char**); #endif protected void term_move_to_line(EditLine *, int); protected void term_move_to_char(EditLine *, int); protected void term_clear_EOL(EditLine *, int); protected void term_overwrite(EditLine *, const char *, size_t); protected void term_insertwrite(EditLine *, char *, int); protected void term_deletechars(EditLine *, int); protected void term_clear_screen(EditLine *); protected void term_beep(EditLine *); protected int term_change_size(EditLine *, int, int); protected int term_get_size(EditLine *, int *, int *); protected int term_init(EditLine *); protected void term_bind_arrow(EditLine *); protected void term_print_arrow(EditLine *, const char *); protected int term_clear_arrow(EditLine *, const char *); protected int term_set_arrow(EditLine *, const char *, key_value_t *, int); protected void term_end(EditLine *); protected void term_get(EditLine *, const char **); protected int term_set(EditLine *, const char *); protected int term_settc(EditLine *, int, const char **); protected int term_gettc(EditLine *, int, char **); protected int term_telltc(EditLine *, int, const char **); protected int term_echotc(EditLine *, int, const char **); protected void term_writec(EditLine *, int); protected int term__putc(EditLine *, int); protected void term__flush(EditLine *); /* * Easy access macros */ #define EL_FLAGS (el)->el_term.t_flags #define EL_CAN_INSERT (EL_FLAGS & TERM_CAN_INSERT) #define EL_CAN_DELETE (EL_FLAGS & TERM_CAN_DELETE) #define EL_CAN_CEOL (EL_FLAGS & TERM_CAN_CEOL) #define EL_CAN_TAB (EL_FLAGS & TERM_CAN_TAB) #define EL_CAN_ME (EL_FLAGS & TERM_CAN_ME) #define EL_CAN_UP (EL_FLAGS & TERM_CAN_UP) #define EL_HAS_META (EL_FLAGS & TERM_HAS_META) #define EL_HAS_AUTO_MARGINS (EL_FLAGS & TERM_HAS_AUTO_MARGINS) #define EL_HAS_MAGIC_MARGINS (EL_FLAGS & TERM_HAS_MAGIC_MARGINS) #endif /* _h_el_term */ clt/falcon/editline/src/emacs.c000066400000000000000000000302621176363201700167330ustar00rootroot00000000000000/* $NetBSD: emacs.c,v 1.22 2009/02/15 21:55:23 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)emacs.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: emacs.c,v 1.22 2009/02/15 21:55:23 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * emacs.c: Emacs functions */ #include "el.h" /* em_delete_or_list(): * Delete character under cursor or list completions if at end of line * [^D] */ protected el_action_t /*ARGSUSED*/ em_delete_or_list(EditLine *el, int c) { if (el->el_line.cursor == el->el_line.lastchar) { /* if I'm at the end */ if (el->el_line.cursor == el->el_line.buffer) { /* and the beginning */ term_writec(el, c); /* then do an EOF */ return (CC_EOF); } else { /* * Here we could list completions, but it is an * error right now */ term_beep(el); return (CC_ERROR); } } else { if (el->el_state.doingarg) c_delafter(el, el->el_state.argument); else c_delafter1(el); if (el->el_line.cursor > el->el_line.lastchar) el->el_line.cursor = el->el_line.lastchar; /* bounds check */ return (CC_REFRESH); } } /* em_delete_next_word(): * Cut from cursor to end of current word * [M-d] */ protected el_action_t /*ARGSUSED*/ em_delete_next_word(EditLine *el, int c __attribute__((__unused__))) { char *cp, *p, *kp; if (el->el_line.cursor == el->el_line.lastchar) return (CC_ERROR); cp = c__next_word(el->el_line.cursor, el->el_line.lastchar, el->el_state.argument, ce__isword); for (p = el->el_line.cursor, kp = el->el_chared.c_kill.buf; p < cp; p++) /* save the text */ *kp++ = *p; el->el_chared.c_kill.last = kp; c_delafter(el, (int)(cp - el->el_line.cursor)); /* delete after dot */ if (el->el_line.cursor > el->el_line.lastchar) el->el_line.cursor = el->el_line.lastchar; /* bounds check */ return (CC_REFRESH); } /* em_yank(): * Paste cut buffer at cursor position * [^Y] */ protected el_action_t /*ARGSUSED*/ em_yank(EditLine *el, int c __attribute__((__unused__))) { char *kp, *cp; if (el->el_chared.c_kill.last == el->el_chared.c_kill.buf) return (CC_NORM); if (el->el_line.lastchar + (el->el_chared.c_kill.last - el->el_chared.c_kill.buf) >= el->el_line.limit) return (CC_ERROR); el->el_chared.c_kill.mark = el->el_line.cursor; cp = el->el_line.cursor; /* open the space, */ c_insert(el, (int)(el->el_chared.c_kill.last - el->el_chared.c_kill.buf)); /* copy the chars */ for (kp = el->el_chared.c_kill.buf; kp < el->el_chared.c_kill.last; kp++) *cp++ = *kp; /* if an arg, cursor at beginning else cursor at end */ if (el->el_state.argument == 1) el->el_line.cursor = cp; return (CC_REFRESH); } /* em_kill_line(): * Cut the entire line and save in cut buffer * [^U] */ protected el_action_t /*ARGSUSED*/ em_kill_line(EditLine *el, int c __attribute__((__unused__))) { char *kp, *cp; cp = el->el_line.buffer; kp = el->el_chared.c_kill.buf; while (cp < el->el_line.lastchar) *kp++ = *cp++; /* copy it */ el->el_chared.c_kill.last = kp; /* zap! -- delete all of it */ el->el_line.lastchar = el->el_line.buffer; el->el_line.cursor = el->el_line.buffer; return (CC_REFRESH); } /* em_kill_region(): * Cut area between mark and cursor and save in cut buffer * [^W] */ protected el_action_t /*ARGSUSED*/ em_kill_region(EditLine *el, int c __attribute__((__unused__))) { char *kp, *cp; if (!el->el_chared.c_kill.mark) return (CC_ERROR); if (el->el_chared.c_kill.mark > el->el_line.cursor) { cp = el->el_line.cursor; kp = el->el_chared.c_kill.buf; while (cp < el->el_chared.c_kill.mark) *kp++ = *cp++; /* copy it */ el->el_chared.c_kill.last = kp; c_delafter(el, (int)(cp - el->el_line.cursor)); } else { /* mark is before cursor */ cp = el->el_chared.c_kill.mark; kp = el->el_chared.c_kill.buf; while (cp < el->el_line.cursor) *kp++ = *cp++; /* copy it */ el->el_chared.c_kill.last = kp; c_delbefore(el, (int)(cp - el->el_chared.c_kill.mark)); el->el_line.cursor = el->el_chared.c_kill.mark; } return (CC_REFRESH); } /* em_copy_region(): * Copy area between mark and cursor to cut buffer * [M-W] */ protected el_action_t /*ARGSUSED*/ em_copy_region(EditLine *el, int c __attribute__((__unused__))) { char *kp, *cp; if (!el->el_chared.c_kill.mark) return (CC_ERROR); if (el->el_chared.c_kill.mark > el->el_line.cursor) { cp = el->el_line.cursor; kp = el->el_chared.c_kill.buf; while (cp < el->el_chared.c_kill.mark) *kp++ = *cp++; /* copy it */ el->el_chared.c_kill.last = kp; } else { cp = el->el_chared.c_kill.mark; kp = el->el_chared.c_kill.buf; while (cp < el->el_line.cursor) *kp++ = *cp++; /* copy it */ el->el_chared.c_kill.last = kp; } return (CC_NORM); } /* em_gosmacs_transpose(): * Exchange the two characters before the cursor * Gosling emacs transpose chars [^T] */ protected el_action_t em_gosmacs_transpose(EditLine *el, int c) { if (el->el_line.cursor > &el->el_line.buffer[1]) { /* must have at least two chars entered */ c = el->el_line.cursor[-2]; el->el_line.cursor[-2] = el->el_line.cursor[-1]; el->el_line.cursor[-1] = c; return (CC_REFRESH); } else return (CC_ERROR); } /* em_next_word(): * Move next to end of current word * [M-f] */ protected el_action_t /*ARGSUSED*/ em_next_word(EditLine *el, int c __attribute__((__unused__))) { if (el->el_line.cursor == el->el_line.lastchar) return (CC_ERROR); el->el_line.cursor = c__next_word(el->el_line.cursor, el->el_line.lastchar, el->el_state.argument, ce__isword); if (el->el_map.type == MAP_VI) if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return (CC_REFRESH); } return (CC_CURSOR); } /* em_upper_case(): * Uppercase the characters from cursor to end of current word * [M-u] */ protected el_action_t /*ARGSUSED*/ em_upper_case(EditLine *el, int c __attribute__((__unused__))) { char *cp, *ep; ep = c__next_word(el->el_line.cursor, el->el_line.lastchar, el->el_state.argument, ce__isword); for (cp = el->el_line.cursor; cp < ep; cp++) if (islower((unsigned char)*cp)) *cp = toupper((unsigned char)*cp); el->el_line.cursor = ep; if (el->el_line.cursor > el->el_line.lastchar) el->el_line.cursor = el->el_line.lastchar; return (CC_REFRESH); } /* em_capitol_case(): * Capitalize the characters from cursor to end of current word * [M-c] */ protected el_action_t /*ARGSUSED*/ em_capitol_case(EditLine *el, int c __attribute__((__unused__))) { char *cp, *ep; ep = c__next_word(el->el_line.cursor, el->el_line.lastchar, el->el_state.argument, ce__isword); for (cp = el->el_line.cursor; cp < ep; cp++) { if (isalpha((unsigned char)*cp)) { if (islower((unsigned char)*cp)) *cp = toupper((unsigned char)*cp); cp++; break; } } for (; cp < ep; cp++) if (isupper((unsigned char)*cp)) *cp = tolower((unsigned char)*cp); el->el_line.cursor = ep; if (el->el_line.cursor > el->el_line.lastchar) el->el_line.cursor = el->el_line.lastchar; return (CC_REFRESH); } /* em_lower_case(): * Lowercase the characters from cursor to end of current word * [M-l] */ protected el_action_t /*ARGSUSED*/ em_lower_case(EditLine *el, int c __attribute__((__unused__))) { char *cp, *ep; ep = c__next_word(el->el_line.cursor, el->el_line.lastchar, el->el_state.argument, ce__isword); for (cp = el->el_line.cursor; cp < ep; cp++) if (isupper((unsigned char)*cp)) *cp = tolower((unsigned char)*cp); el->el_line.cursor = ep; if (el->el_line.cursor > el->el_line.lastchar) el->el_line.cursor = el->el_line.lastchar; return (CC_REFRESH); } /* em_set_mark(): * Set the mark at cursor * [^@] */ protected el_action_t /*ARGSUSED*/ em_set_mark(EditLine *el, int c __attribute__((__unused__))) { el->el_chared.c_kill.mark = el->el_line.cursor; return (CC_NORM); } /* em_exchange_mark(): * Exchange the cursor and mark * [^X^X] */ protected el_action_t /*ARGSUSED*/ em_exchange_mark(EditLine *el, int c __attribute__((__unused__))) { char *cp; cp = el->el_line.cursor; el->el_line.cursor = el->el_chared.c_kill.mark; el->el_chared.c_kill.mark = cp; return (CC_CURSOR); } /* em_universal_argument(): * Universal argument (argument times 4) * [^U] */ protected el_action_t /*ARGSUSED*/ em_universal_argument(EditLine *el, int c __attribute__((__unused__))) { /* multiply current argument by 4 */ if (el->el_state.argument > 1000000) return (CC_ERROR); el->el_state.doingarg = 1; el->el_state.argument *= 4; return (CC_ARGHACK); } /* em_meta_next(): * Add 8th bit to next character typed * [] */ protected el_action_t /*ARGSUSED*/ em_meta_next(EditLine *el, int c __attribute__((__unused__))) { el->el_state.metanext = 1; return (CC_ARGHACK); } /* em_toggle_overwrite(): * Switch from insert to overwrite mode or vice versa */ protected el_action_t /*ARGSUSED*/ em_toggle_overwrite(EditLine *el, int c __attribute__((__unused__))) { el->el_state.inputmode = (el->el_state.inputmode == MODE_INSERT) ? MODE_REPLACE : MODE_INSERT; return (CC_NORM); } /* em_copy_prev_word(): * Copy current word to cursor */ protected el_action_t /*ARGSUSED*/ em_copy_prev_word(EditLine *el, int c __attribute__((__unused__))) { char *cp, *oldc, *dp; if (el->el_line.cursor == el->el_line.buffer) return (CC_ERROR); oldc = el->el_line.cursor; /* does a bounds check */ cp = c__prev_word(el->el_line.cursor, el->el_line.buffer, el->el_state.argument, ce__isword); c_insert(el, (int)(oldc - cp)); for (dp = oldc; cp < oldc && dp < el->el_line.lastchar; cp++) *dp++ = *cp; el->el_line.cursor = dp;/* put cursor at end */ return (CC_REFRESH); } /* em_inc_search_next(): * Emacs incremental next search */ protected el_action_t /*ARGSUSED*/ em_inc_search_next(EditLine *el, int c __attribute__((__unused__))) { el->el_search.patlen = 0; return (ce_inc_search(el, ED_SEARCH_NEXT_HISTORY)); } /* em_inc_search_prev(): * Emacs incremental reverse search */ protected el_action_t /*ARGSUSED*/ em_inc_search_prev(EditLine *el, int c __attribute__((__unused__))) { el->el_search.patlen = 0; return (ce_inc_search(el, ED_SEARCH_PREV_HISTORY)); } /* em_delete_prev_char(): * Delete the character to the left of the cursor * [^?] */ protected el_action_t /*ARGSUSED*/ em_delete_prev_char(EditLine *el, int c __attribute__((__unused__))) { if (el->el_line.cursor <= el->el_line.buffer) return (CC_ERROR); if (el->el_state.doingarg) c_delbefore(el, el->el_state.argument); else c_delbefore1(el); el->el_line.cursor -= el->el_state.argument; if (el->el_line.cursor < el->el_line.buffer) el->el_line.cursor = el->el_line.buffer; return (CC_REFRESH); } clt/falcon/editline/src/fgetln.c000066400000000000000000000053301176363201700171200ustar00rootroot00000000000000/* $NetBSD: fgetln.c,v 1.9 2008/04/29 06:53:03 martin Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Christos Zoulas. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #if !HAVE_FGETLN #include #include #ifndef HAVE_NBTOOL_CONFIG_H /* These headers are required, but included from nbtool_config.h */ #include #include #include #include #endif char * fgetln(FILE *fp, size_t *len) { static char *buf = NULL; static size_t bufsiz = 0; char *ptr; if (buf == NULL) { bufsiz = BUFSIZ; if ((buf = malloc(bufsiz)) == NULL) return NULL; } if (fgets(buf, bufsiz, fp) == NULL) return NULL; *len = 0; while ((ptr = strchr(&buf[*len], '\n')) == NULL) { size_t nbufsiz = bufsiz + BUFSIZ; char *nbuf = realloc(buf, nbufsiz); if (nbuf == NULL) { int oerrno = errno; free(buf); errno = oerrno; buf = NULL; return NULL; } else buf = nbuf; if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL) { buf[bufsiz] = '\0'; *len = strlen(buf); return buf; } *len = bufsiz; bufsiz = nbufsiz; } *len = (ptr - buf) + 1; return buf; } #endif #ifdef TEST int main(int argc, char *argv[]) { char *p; size_t len; while ((p = fgetln(stdin, &len)) != NULL) { (void)printf("%zu %s", len, p); free(p); } return 0; } #endif clt/falcon/editline/src/filecomplete.c000066400000000000000000000343641176363201700203220ustar00rootroot00000000000000/* $NetBSD: filecomplete.c,v 1.15 2009/02/16 00:15:45 christos Exp $ */ /*- * Copyright (c) 1997 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jaromir Dolecek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* AIX requires this to be the first thing in the file. */ #if defined (_AIX) && !defined (__GNUC__) #pragma alloca #endif #include #ifdef __GNUC__ # undef alloca # define alloca(n) __builtin_alloca (n) #else # ifdef HAVE_ALLOCA_H # include # else # ifndef _AIX extern char *alloca (); # endif # endif #endif #if !defined(lint) && !defined(SCCSID) __RCSID("$NetBSD: filecomplete.c,v 1.15 2009/02/16 00:15:45 christos Exp $"); #endif /* not lint && not SCCSID */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "el.h" #include "fcns.h" /* for EL_NUM_FCNS */ #include "histedit.h" #include "filecomplete.h" static char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$', '>', '<', '=', ';', '|', '&', '{', '(', '\0' }; /********************************/ /* completion functions */ /* * does tilde expansion of strings of type ``~user/foo'' * if ``user'' isn't valid user name or ``txt'' doesn't start * w/ '~', returns pointer to strdup()ed copy of ``txt'' * * it's callers's responsibility to free() returned string */ char * fn_tilde_expand(const char *txt) { struct passwd pwres, *pass; char *temp; size_t len = 0; char pwbuf[1024]; if (txt[0] != '~') return (strdup(txt)); temp = strchr(txt + 1, '/'); if (temp == NULL) { temp = strdup(txt + 1); if (temp == NULL) return NULL; } else { len = temp - txt + 1; /* text until string after slash */ temp = malloc(len); if (temp == NULL) return NULL; (void)strncpy(temp, txt + 1, len - 2); temp[len - 2] = '\0'; } if (temp[0] == 0) { #if defined(__SUNPRO_C) pass = getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf)); #elif defined(HAVE_GETPW_R_POSIX) if (getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf), &pass) != 0) pass = NULL; #elif defined(HAVE_GETPW_R_DRAFT) pass = getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf)); #else pass = getpwuid(getuid()); #endif } else { #if defined(__SUNPRO_C) pass = getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf)); #elif defined(HAVE_GETPW_R_POSIX) if (getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf), &pass) != 0) pass = NULL; #elif defined(HAVE_GETPW_R_DRAFT) pass = getpwnam_r(temp, &pwres, pwbuf, sizeof(pwbuf)); #else pass = getpwnam(temp); #endif } free(temp); /* value no more needed */ if (pass == NULL) return (strdup(txt)); /* update pointer txt to point at string immedially following */ /* first slash */ txt += len; temp = malloc(strlen(pass->pw_dir) + 1 + strlen(txt) + 1); if (temp == NULL) return NULL; (void)sprintf(temp, "%s/%s", pass->pw_dir, txt); return (temp); } /* * return first found file name starting by the ``text'' or NULL if no * such file can be found * value of ``state'' is ignored * * it's caller's responsibility to free returned string */ char * fn_filename_completion_function(const char *text, int state) { static DIR *dir = NULL; static char *filename = NULL, *dirname = NULL, *dirpath = NULL; static size_t filename_len = 0; struct dirent *entry; char *temp; size_t len; if (state == 0 || dir == NULL) { temp = strrchr(text, '/'); if (temp) { char *nptr; temp++; nptr = realloc(filename, strlen(temp) + 1); if (nptr == NULL) { free(filename); return NULL; } filename = nptr; (void)strcpy(filename, temp); len = temp - text; /* including last slash */ nptr = realloc(dirname, len + 1); if (nptr == NULL) { free(filename); return NULL; } dirname = nptr; (void)strncpy(dirname, text, len); dirname[len] = '\0'; } else { if (*text == 0) filename = NULL; else { filename = strdup(text); if (filename == NULL) return NULL; } dirname = NULL; } if (dir != NULL) { (void)closedir(dir); dir = NULL; } /* support for ``~user'' syntax */ free(dirpath); if (dirname == NULL && (dirname = strdup("./")) == NULL) return NULL; if (*dirname == '~') dirpath = fn_tilde_expand(dirname); else dirpath = strdup(dirname); if (dirpath == NULL) return NULL; dir = opendir(dirpath); if (!dir) return (NULL); /* cannot open the directory */ /* will be used in cycle */ filename_len = filename ? strlen(filename) : 0; } /* find the match */ while ((entry = readdir(dir)) != NULL) { /* skip . and .. */ if (entry->d_name[0] == '.' && (!entry->d_name[1] || (entry->d_name[1] == '.' && !entry->d_name[2]))) continue; if (filename_len == 0) break; /* otherwise, get first entry where first */ /* filename_len characters are equal */ if (entry->d_name[0] == filename[0] /* Some dirents have d_namlen, but it is not portable. */ && strlen(entry->d_name) >= filename_len && strncmp(entry->d_name, filename, filename_len) == 0) break; } if (entry) { /* match found */ /* Some dirents have d_namlen, but it is not portable. */ len = strlen(entry->d_name); temp = malloc(strlen(dirname) + len + 1); if (temp == NULL) return NULL; (void)sprintf(temp, "%s%s", dirname, entry->d_name); } else { (void)closedir(dir); dir = NULL; temp = NULL; } return (temp); } static const char * append_char_function(const char *name) { struct stat stbuf; char *expname = *name == '~' ? fn_tilde_expand(name) : NULL; const char *rs = " "; if (stat(expname ? expname : name, &stbuf) == -1) goto out; if (S_ISDIR(stbuf.st_mode)) rs = "/"; out: if (expname) free(expname); return rs; } /* * returns list of completions for text given * non-static for readline. */ char ** completion_matches(const char *, char *(*)(const char *, int)); char ** completion_matches(const char *text, char *(*genfunc)(const char *, int)) { char **match_list = NULL, *retstr, *prevstr; size_t match_list_len, max_equal, which, i; size_t matches; matches = 0; match_list_len = 1; while ((retstr = (*genfunc) (text, (int)matches)) != NULL) { /* allow for list terminator here */ if (matches + 3 >= match_list_len) { char **nmatch_list; while (matches + 3 >= match_list_len) match_list_len <<= 1; nmatch_list = realloc(match_list, match_list_len * sizeof(char *)); if (nmatch_list == NULL) { free(match_list); return NULL; } match_list = nmatch_list; } match_list[++matches] = retstr; } if (!match_list) return NULL; /* nothing found */ /* find least denominator and insert it to match_list[0] */ which = 2; prevstr = match_list[1]; max_equal = strlen(prevstr); for (; which <= matches; which++) { for (i = 0; i < max_equal && prevstr[i] == match_list[which][i]; i++) continue; max_equal = i; } retstr = malloc(max_equal + 1); if (retstr == NULL) { free(match_list); return NULL; } (void)strncpy(retstr, match_list[1], max_equal); retstr[max_equal] = '\0'; match_list[0] = retstr; /* add NULL as last pointer to the array */ match_list[matches + 1] = (char *) NULL; return (match_list); } /* * Sort function for qsort(). Just wrapper around strcasecmp(). */ static int _fn_qsort_string_compare(const void *i1, const void *i2) { const char *s1 = ((const char * const *)i1)[0]; const char *s2 = ((const char * const *)i2)[0]; return strcasecmp(s1, s2); } /* * Display list of strings in columnar format on readline's output stream. * 'matches' is list of strings, 'len' is number of strings in 'matches', * 'max' is maximum length of string in 'matches'. */ void fn_display_match_list (EditLine *el, char **matches, size_t len, size_t max) { size_t i, idx, limit, count; int screenwidth = el->el_term.t_size.h; /* * Find out how many entries can be put on one line, count * with two spaces between strings. */ limit = screenwidth / (max + 2); if (limit == 0) limit = 1; /* how many lines of output */ count = len / limit; if (count * limit < len) count++; /* Sort the items if they are not already sorted. */ qsort(&matches[1], (size_t)(len - 1), sizeof(char *), _fn_qsort_string_compare); idx = 1; for(; count > 0; count--) { for(i = 0; i < limit && matches[idx]; i++, idx++) (void)fprintf(el->el_outfile, "%-*s ", (int)max, matches[idx]); (void)fprintf(el->el_outfile, "\n"); } } /* * Complete the word at or before point, * 'what_to_do' says what to do with the completion. * \t means do standard completion. * `?' means list the possible completions. * `*' means insert all of the possible completions. * `!' means to do standard completion, and list all possible completions if * there is more than one. * * Note: '*' support is not implemented * '!' could never be invoked */ int fn_complete(EditLine *el, char *(*complet_func)(const char *, int), char **(*attempted_completion_function)(const char *, int, int), const char *word_break, const char *special_prefixes, const char *(*app_func)(const char *), size_t query_items, int *completion_type, int *over, int *point, int *end) { const LineInfo *li; char *temp, **matches; const char *ctemp; size_t len; int what_to_do = '\t'; int retval = CC_NORM; if (el->el_state.lastcmd == el->el_state.thiscmd) what_to_do = '?'; /* readline's rl_complete() has to be told what we did... */ if (completion_type != NULL) *completion_type = what_to_do; if (!complet_func) complet_func = fn_filename_completion_function; if (!app_func) app_func = append_char_function; /* We now look backwards for the start of a filename/variable word */ li = el_line(el); ctemp = (const char *) li->cursor; while (ctemp > li->buffer && !strchr(word_break, ctemp[-1]) && (!special_prefixes || !strchr(special_prefixes, ctemp[-1]) ) ) ctemp--; len = li->cursor - ctemp; #if defined(__SSP__) || defined(__SSP_ALL__) temp = malloc(len + 1); #else temp = alloca(len + 1); #endif (void)strncpy(temp, ctemp, len); temp[len] = '\0'; /* these can be used by function called in completion_matches() */ /* or (*attempted_completion_function)() */ if (point != 0) *point = (int)(li->cursor - li->buffer); if (end != NULL) *end = (int)(li->lastchar - li->buffer); if (attempted_completion_function) { int cur_off = (int)(li->cursor - li->buffer); matches = (*attempted_completion_function) (temp, (int)(cur_off - len), cur_off); } else matches = 0; if (!attempted_completion_function || (over != NULL && !*over && !matches)) matches = completion_matches(temp, complet_func); if (over != NULL) *over = 0; if (matches) { int i; size_t matches_num, maxlen, match_len, match_display=1; retval = CC_REFRESH; /* * Only replace the completed string with common part of * possible matches if there is possible completion. */ if (matches[0][0] != '\0') { el_deletestr(el, (int) len); el_insertstr(el, matches[0]); } if (what_to_do == '?') goto display_matches; if (matches[2] == NULL && strcmp(matches[0], matches[1]) == 0) { /* * We found exact match. Add a space after * it, unless we do filename completion and the * object is a directory. */ el_insertstr(el, (*app_func)(matches[0])); } else if (what_to_do == '!') { display_matches: /* * More than one match and requested to list possible * matches. */ for(i=1, maxlen=0; matches[i]; i++) { match_len = strlen(matches[i]); if (match_len > maxlen) maxlen = match_len; } matches_num = i - 1; /* newline to get on next line from command line */ (void)fprintf(el->el_outfile, "\n"); /* * If there are too many items, ask user for display * confirmation. */ if (matches_num > query_items) { (void)fprintf(el->el_outfile, "Display all %zu possibilities? (y or n) ", matches_num); (void)fflush(el->el_outfile); if (getc(stdin) != 'y') match_display = 0; (void)fprintf(el->el_outfile, "\n"); } if (match_display) fn_display_match_list(el, matches, matches_num, maxlen); retval = CC_REDISPLAY; } else if (matches[0][0]) { /* * There was some common match, but the name was * not complete enough. Next tab will print possible * completions. */ el_beep(el); } else { /* lcd is not a valid object - further specification */ /* is needed */ el_beep(el); retval = CC_NORM; } /* free elements of array and the array itself */ for (i = 0; matches[i]; i++) free(matches[i]); free(matches); matches = NULL; } #if defined(__SSP__) || defined(__SSP_ALL__) free(temp); #endif return retval; } /* * el-compatible wrapper around rl_complete; needed for key binding */ /* ARGSUSED */ unsigned char _el_fn_complete(EditLine *el, int ch __attribute__((__unused__))) { return (unsigned char)fn_complete(el, NULL, NULL, break_chars, NULL, NULL, 100, NULL, NULL, NULL, NULL); } clt/falcon/editline/src/filecomplete.h000066400000000000000000000037051176363201700203220ustar00rootroot00000000000000/* $NetBSD: filecomplete.h,v 1.8 2009/02/16 00:15:45 christos Exp $ */ /*- * Copyright (c) 1997 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jaromir Dolecek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _FILECOMPLETE_H_ #define _FILECOMPLETE_H_ int fn_complete(EditLine *, char *(*)(const char *, int), char **(*)(const char *, int, int), const char *, const char *, const char *(*)(const char *), size_t, int *, int *, int *, int *); void fn_display_match_list(EditLine *, char **, size_t, size_t); char *fn_tilde_expand(const char *); char *fn_filename_completion_function(const char *, int); #endif clt/falcon/editline/src/hist.c000066400000000000000000000125621176363201700166150ustar00rootroot00000000000000/* $NetBSD: hist.c,v 1.15 2003/11/01 23:36:39 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)hist.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: hist.c,v 1.15 2003/11/01 23:36:39 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * hist.c: History access functions */ #include #include "el.h" /* hist_init(): * Initialization function. */ protected int hist_init(EditLine *el) { el->el_history.fun = NULL; el->el_history.ref = NULL; el->el_history.buf = (char *) el_malloc(EL_BUFSIZ); el->el_history.sz = EL_BUFSIZ; if (el->el_history.buf == NULL) return (-1); el->el_history.last = el->el_history.buf; return (0); } /* hist_end(): * clean up history; */ protected void hist_end(EditLine *el) { el_free((ptr_t) el->el_history.buf); el->el_history.buf = NULL; } /* hist_set(): * Set new history interface */ protected int hist_set(EditLine *el, hist_fun_t fun, ptr_t ptr) { el->el_history.ref = ptr; el->el_history.fun = fun; return (0); } /* hist_get(): * Get a history line and update it in the buffer. * eventno tells us the event to get. */ protected el_action_t hist_get(EditLine *el) { const char *hp; int h; if (el->el_history.eventno == 0) { /* if really the current line */ (void) strncpy(el->el_line.buffer, el->el_history.buf, el->el_history.sz); el->el_line.lastchar = el->el_line.buffer + (el->el_history.last - el->el_history.buf); #ifdef KSHVI if (el->el_map.type == MAP_VI) el->el_line.cursor = el->el_line.buffer; else #endif /* KSHVI */ el->el_line.cursor = el->el_line.lastchar; return (CC_REFRESH); } if (el->el_history.ref == NULL) return (CC_ERROR); hp = HIST_FIRST(el); if (hp == NULL) return (CC_ERROR); for (h = 1; h < el->el_history.eventno; h++) if ((hp = HIST_NEXT(el)) == NULL) { el->el_history.eventno = h; return (CC_ERROR); } (void) strlcpy(el->el_line.buffer, hp, (size_t)(el->el_line.limit - el->el_line.buffer)); el->el_line.lastchar = el->el_line.buffer + strlen(el->el_line.buffer); if (el->el_line.lastchar > el->el_line.buffer && el->el_line.lastchar[-1] == '\n') el->el_line.lastchar--; if (el->el_line.lastchar > el->el_line.buffer && el->el_line.lastchar[-1] == ' ') el->el_line.lastchar--; #ifdef KSHVI if (el->el_map.type == MAP_VI) el->el_line.cursor = el->el_line.buffer; else #endif /* KSHVI */ el->el_line.cursor = el->el_line.lastchar; return (CC_REFRESH); } /* hist_command() * process a history command */ protected int hist_command(EditLine *el, int argc, const char **argv) { const char *str; int num; HistEvent ev; if (el->el_history.ref == NULL) return (-1); if (argc == 1 || strcmp(argv[1], "list") == 0) { /* List history entries */ for (str = HIST_LAST(el); str != NULL; str = HIST_PREV(el)) (void) fprintf(el->el_outfile, "%d %s", el->el_history.ev.num, str); return (0); } if (argc != 3) return (-1); num = (int)strtol(argv[2], NULL, 0); if (strcmp(argv[1], "size") == 0) return history(el->el_history.ref, &ev, H_SETSIZE, num); if (strcmp(argv[1], "unique") == 0) return history(el->el_history.ref, &ev, H_SETUNIQUE, num); return -1; } /* hist_enlargebuf() * Enlarge history buffer to specified value. Called from el_enlargebufs(). * Return 0 for failure, 1 for success. */ protected int /*ARGSUSED*/ hist_enlargebuf(EditLine *el, size_t oldsz, size_t newsz) { char *newbuf; newbuf = realloc(el->el_history.buf, newsz); if (!newbuf) return 0; (void) memset(&newbuf[oldsz], '\0', newsz - oldsz); el->el_history.last = newbuf + (el->el_history.last - el->el_history.buf); el->el_history.buf = newbuf; el->el_history.sz = newsz; return 1; } clt/falcon/editline/src/hist.h000066400000000000000000000061261176363201700166210ustar00rootroot00000000000000/* $NetBSD: hist.h,v 1.10 2003/08/07 16:44:31 agc Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)hist.h 8.1 (Berkeley) 6/4/93 */ /* * el.hist.c: History functions */ #ifndef _h_el_hist #define _h_el_hist #include "histedit.h" typedef int (*hist_fun_t)(ptr_t, HistEvent *, int, ...); typedef struct el_history_t { char *buf; /* The history buffer */ size_t sz; /* Size of history buffer */ char *last; /* The last character */ int eventno; /* Event we are looking for */ ptr_t ref; /* Argument for history fcns */ hist_fun_t fun; /* Event access */ HistEvent ev; /* Event cookie */ } el_history_t; #define HIST_FUN(el, fn, arg) \ ((((*(el)->el_history.fun) ((el)->el_history.ref, &(el)->el_history.ev, \ fn, arg)) == -1) ? NULL : (el)->el_history.ev.str) #define HIST_NEXT(el) HIST_FUN(el, H_NEXT, NULL) #define HIST_FIRST(el) HIST_FUN(el, H_FIRST, NULL) #define HIST_LAST(el) HIST_FUN(el, H_LAST, NULL) #define HIST_PREV(el) HIST_FUN(el, H_PREV, NULL) #define HIST_SET(el, num) HIST_FUN(el, H_SET, num) #define HIST_LOAD(el, fname) HIST_FUN(el, H_LOAD fname) #define HIST_SAVE(el, fname) HIST_FUN(el, H_SAVE fname) protected int hist_init(EditLine *); protected void hist_end(EditLine *); protected el_action_t hist_get(EditLine *); protected int hist_set(EditLine *, hist_fun_t, ptr_t); protected int hist_command(EditLine *, int, const char **); protected int hist_enlargebuf(EditLine *, size_t, size_t); #endif /* _h_el_hist */ clt/falcon/editline/src/histedit.h000066400000000000000000000151151176363201700174650ustar00rootroot00000000000000/* $NetBSD: histedit.h,v 1.41 2009/09/07 21:24:33 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)histedit.h 8.2 (Berkeley) 1/3/94 */ /* * histedit.h: Line editor and history interface. */ #ifndef _HISTEDIT_H_ #define _HISTEDIT_H_ #define LIBEDIT_MAJOR 2 #define LIBEDIT_MINOR 11 #include #include #ifdef __cplusplus extern "C" { #endif /* * ==== Editing ==== */ typedef struct editline EditLine; /* * For user-defined function interface */ typedef struct lineinfo { const char *buffer; const char *cursor; const char *lastchar; } LineInfo; /* * EditLine editor function return codes. * For user-defined function interface */ #define CC_NORM 0 #define CC_NEWLINE 1 #define CC_EOF 2 #define CC_ARGHACK 3 #define CC_REFRESH 4 #define CC_CURSOR 5 #define CC_ERROR 6 #define CC_FATAL 7 #define CC_REDISPLAY 8 #define CC_REFRESH_BEEP 9 /* * Initialization, cleanup, and resetting */ EditLine *el_init(const char *, FILE *, FILE *, FILE *); void el_end(EditLine *); void el_reset(EditLine *); /* * Get a line, a character or push a string back in the input queue */ const char *el_gets(EditLine *, int *); int el_getc(EditLine *, char *); void el_push(EditLine *, const char *); /* * Beep! */ void el_beep(EditLine *); /* * High level function internals control * Parses argc, argv array and executes builtin editline commands */ int el_parse(EditLine *, int, const char **); /* * Low level editline access functions */ int el_set(EditLine *, int, ...); int el_get(EditLine *, int, ...); unsigned char _el_fn_complete(EditLine *, int); /* * el_set/el_get parameters */ #define EL_PROMPT 0 /* , el_pfunc_t); */ #define EL_TERMINAL 1 /* , const char *); */ #define EL_EDITOR 2 /* , const char *); */ #define EL_SIGNAL 3 /* , int); */ #define EL_BIND 4 /* , const char *, ..., NULL); */ #define EL_TELLTC 5 /* , const char *, ..., NULL); */ #define EL_SETTC 6 /* , const char *, ..., NULL); */ #define EL_ECHOTC 7 /* , const char *, ..., NULL); */ #define EL_SETTY 8 /* , const char *, ..., NULL); */ #define EL_ADDFN 9 /* , const char *, const char * */ /* , el_func_t); */ #define EL_HIST 10 /* , hist_fun_t, const char *); */ #define EL_EDITMODE 11 /* , int); */ #define EL_RPROMPT 12 /* , el_pfunc_t); */ #define EL_GETCFN 13 /* , el_rfunc_t); */ #define EL_CLIENTDATA 14 /* , void *); */ #define EL_UNBUFFERED 15 /* , int); */ #define EL_PREP_TERM 16 /* , int); */ #define EL_GETTC 17 /* , const char *, ..., NULL); */ #define EL_GETFP 18 /* , int, FILE **); */ #define EL_SETFP 19 /* , int, FILE *); */ #define EL_REFRESH 20 /* , void); */ #define EL_PROMPT_ESC 21 /* , el_pfunc_t, char); */ #define EL_RPROMPT_ESC 22 /* , el_pfunc_t, char); */ #define EL_BUILTIN_GETCFN (NULL) /* * Source named file or $PWD/.editrc or $HOME/.editrc */ int el_source(EditLine *, const char *); /* * Must be called when the terminal changes size; If EL_SIGNAL * is set this is done automatically otherwise it is the responsibility * of the application */ void el_resize(EditLine *); /* * User-defined function interface. */ const LineInfo *el_line(EditLine *); int el_insertstr(EditLine *, const char *); void el_deletestr(EditLine *, int); /* * ==== History ==== */ typedef struct history History; typedef struct HistEvent { int num; const char *str; } HistEvent; /* * History access functions. */ History * history_init(void); void history_end(History *); int history(History *, HistEvent *, int, ...); #define H_FUNC 0 /* , UTSL */ #define H_SETSIZE 1 /* , const int); */ #define H_GETSIZE 2 /* , void); */ #define H_FIRST 3 /* , void); */ #define H_LAST 4 /* , void); */ #define H_PREV 5 /* , void); */ #define H_NEXT 6 /* , void); */ #define H_CURR 8 /* , const int); */ #define H_SET 7 /* , int); */ #define H_ADD 9 /* , const char *); */ #define H_ENTER 10 /* , const char *); */ #define H_APPEND 11 /* , const char *); */ #define H_END 12 /* , void); */ #define H_NEXT_STR 13 /* , const char *); */ #define H_PREV_STR 14 /* , const char *); */ #define H_NEXT_EVENT 15 /* , const int); */ #define H_PREV_EVENT 16 /* , const int); */ #define H_LOAD 17 /* , const char *); */ #define H_SAVE 18 /* , const char *); */ #define H_CLEAR 19 /* , void); */ #define H_SETUNIQUE 20 /* , int); */ #define H_GETUNIQUE 21 /* , void); */ #define H_DEL 22 /* , int); */ #define H_NEXT_EVDATA 23 /* , const int, histdata_t *); */ #define H_DELDATA 24 /* , int, histdata_t *);*/ #define H_REPLACE 25 /* , const char *, histdata_t); */ /* * ==== Tokenization ==== */ typedef struct tokenizer Tokenizer; /* * String tokenization functions, using simplified sh(1) quoting rules */ Tokenizer *tok_init(const char *); void tok_end(Tokenizer *); void tok_reset(Tokenizer *); int tok_line(Tokenizer *, const LineInfo *, int *, const char ***, int *, int *); int tok_str(Tokenizer *, const char *, int *, const char ***); #ifdef __cplusplus } #endif #endif /* _HISTEDIT_H_ */ clt/falcon/editline/src/history.c000066400000000000000000000601631176363201700173470ustar00rootroot00000000000000/* $NetBSD: history.c,v 1.34 2009/09/07 21:24:33 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)history.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: history.c,v 1.34 2009/09/07 21:24:33 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * hist.c: History access functions */ #include #include #include #include #include static const char hist_cookie[] = "_HiStOrY_V2_\n"; #include "histedit.h" typedef int (*history_gfun_t)(ptr_t, HistEvent *); typedef int (*history_efun_t)(ptr_t, HistEvent *, const char *); typedef void (*history_vfun_t)(ptr_t, HistEvent *); typedef int (*history_sfun_t)(ptr_t, HistEvent *, const int); struct history { ptr_t h_ref; /* Argument for history fcns */ int h_ent; /* Last entry point for history */ history_gfun_t h_first; /* Get the first element */ history_gfun_t h_next; /* Get the next element */ history_gfun_t h_last; /* Get the last element */ history_gfun_t h_prev; /* Get the previous element */ history_gfun_t h_curr; /* Get the current element */ history_sfun_t h_set; /* Set the current element */ history_sfun_t h_del; /* Set the given element */ history_vfun_t h_clear; /* Clear the history list */ history_efun_t h_enter; /* Add an element */ history_efun_t h_add; /* Append to an element */ }; #define HNEXT(h, ev) (*(h)->h_next)((h)->h_ref, ev) #define HFIRST(h, ev) (*(h)->h_first)((h)->h_ref, ev) #define HPREV(h, ev) (*(h)->h_prev)((h)->h_ref, ev) #define HLAST(h, ev) (*(h)->h_last)((h)->h_ref, ev) #define HCURR(h, ev) (*(h)->h_curr)((h)->h_ref, ev) #define HSET(h, ev, n) (*(h)->h_set)((h)->h_ref, ev, n) #define HCLEAR(h, ev) (*(h)->h_clear)((h)->h_ref, ev) #define HENTER(h, ev, str) (*(h)->h_enter)((h)->h_ref, ev, str) #define HADD(h, ev, str) (*(h)->h_add)((h)->h_ref, ev, str) #define HDEL(h, ev, n) (*(h)->h_del)((h)->h_ref, ev, n) #define h_strdup(a) strdup(a) #define h_malloc(a) malloc(a) #define h_realloc(a, b) realloc((a), (b)) #define h_free(a) free(a) typedef struct { int num; char *str; } HistEventPrivate; private int history_setsize(History *, HistEvent *, int); private int history_getsize(History *, HistEvent *); private int history_setunique(History *, HistEvent *, int); private int history_getunique(History *, HistEvent *); private int history_set_fun(History *, History *); private int history_load(History *, const char *); private int history_save(History *, const char *); private int history_prev_event(History *, HistEvent *, int); private int history_next_event(History *, HistEvent *, int); private int history_next_string(History *, HistEvent *, const char *); private int history_prev_string(History *, HistEvent *, const char *); /***********************************************************************/ /* * Builtin- history implementation */ typedef struct hentry_t { HistEvent ev; /* What we return */ void *data; /* data */ struct hentry_t *next; /* Next entry */ struct hentry_t *prev; /* Previous entry */ } hentry_t; typedef struct history_t { hentry_t list; /* Fake list header element */ hentry_t *cursor; /* Current element in the list */ int max; /* Maximum number of events */ int cur; /* Current number of events */ int eventid; /* For generation of unique event id */ int flags; /* History flags */ #define H_UNIQUE 1 /* Store only unique elements */ } history_t; private int history_def_next(ptr_t, HistEvent *); private int history_def_first(ptr_t, HistEvent *); private int history_def_prev(ptr_t, HistEvent *); private int history_def_last(ptr_t, HistEvent *); private int history_def_curr(ptr_t, HistEvent *); private int history_def_set(ptr_t, HistEvent *, const int); private void history_def_clear(ptr_t, HistEvent *); private int history_def_enter(ptr_t, HistEvent *, const char *); private int history_def_add(ptr_t, HistEvent *, const char *); private int history_def_del(ptr_t, HistEvent *, const int); private int history_def_init(ptr_t *, HistEvent *, int); private int history_def_insert(history_t *, HistEvent *, const char *); private void history_def_delete(history_t *, HistEvent *, hentry_t *); private int history_deldata_nth(history_t *, HistEvent *, int, void **); private int history_set_nth(ptr_t, HistEvent *, int); #define history_def_setsize(p, num)(void) (((history_t *)p)->max = (num)) #define history_def_getsize(p) (((history_t *)p)->cur) #define history_def_getunique(p) (((((history_t *)p)->flags) & H_UNIQUE) != 0) #define history_def_setunique(p, uni) \ if (uni) \ (((history_t *)p)->flags) |= H_UNIQUE; \ else \ (((history_t *)p)->flags) &= ~H_UNIQUE #define he_strerror(code) he_errlist[code] #define he_seterrev(evp, code) {\ evp->num = code;\ evp->str = he_strerror(code);\ } /* error messages */ static const char *const he_errlist[] = { "OK", "unknown error", "malloc() failed", "first event not found", "last event not found", "empty list", "no next event", "no previous event", "current event is invalid", "event not found", "can't read history from file", "can't write history", "required parameter(s) not supplied", "history size negative", "function not allowed with other history-functions-set the default", "bad parameters" }; /* error codes */ #define _HE_OK 0 #define _HE_UNKNOWN 1 #define _HE_MALLOC_FAILED 2 #define _HE_FIRST_NOTFOUND 3 #define _HE_LAST_NOTFOUND 4 #define _HE_EMPTY_LIST 5 #define _HE_END_REACHED 6 #define _HE_START_REACHED 7 #define _HE_CURR_INVALID 8 #define _HE_NOT_FOUND 9 #define _HE_HIST_READ 10 #define _HE_HIST_WRITE 11 #define _HE_PARAM_MISSING 12 #define _HE_SIZE_NEGATIVE 13 #define _HE_NOT_ALLOWED 14 #define _HE_BAD_PARAM 15 /* history_def_first(): * Default function to return the first event in the history. */ private int history_def_first(ptr_t p, HistEvent *ev) { history_t *h = (history_t *) p; h->cursor = h->list.next; if (h->cursor != &h->list) *ev = h->cursor->ev; else { he_seterrev(ev, _HE_FIRST_NOTFOUND); return (-1); } return (0); } /* history_def_last(): * Default function to return the last event in the history. */ private int history_def_last(ptr_t p, HistEvent *ev) { history_t *h = (history_t *) p; h->cursor = h->list.prev; if (h->cursor != &h->list) *ev = h->cursor->ev; else { he_seterrev(ev, _HE_LAST_NOTFOUND); return (-1); } return (0); } /* history_def_next(): * Default function to return the next event in the history. */ private int history_def_next(ptr_t p, HistEvent *ev) { history_t *h = (history_t *) p; if (h->cursor == &h->list) { he_seterrev(ev, _HE_EMPTY_LIST); return (-1); } if (h->cursor->next == &h->list) { he_seterrev(ev, _HE_END_REACHED); return (-1); } h->cursor = h->cursor->next; *ev = h->cursor->ev; return (0); } /* history_def_prev(): * Default function to return the previous event in the history. */ private int history_def_prev(ptr_t p, HistEvent *ev) { history_t *h = (history_t *) p; if (h->cursor == &h->list) { he_seterrev(ev, (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST); return (-1); } if (h->cursor->prev == &h->list) { he_seterrev(ev, _HE_START_REACHED); return (-1); } h->cursor = h->cursor->prev; *ev = h->cursor->ev; return (0); } /* history_def_curr(): * Default function to return the current event in the history. */ private int history_def_curr(ptr_t p, HistEvent *ev) { history_t *h = (history_t *) p; if (h->cursor != &h->list) *ev = h->cursor->ev; else { he_seterrev(ev, (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST); return (-1); } return (0); } /* history_def_set(): * Default function to set the current event in the history to the * given one. */ private int history_def_set(ptr_t p, HistEvent *ev, const int n) { history_t *h = (history_t *) p; if (h->cur == 0) { he_seterrev(ev, _HE_EMPTY_LIST); return (-1); } if (h->cursor == &h->list || h->cursor->ev.num != n) { for (h->cursor = h->list.next; h->cursor != &h->list; h->cursor = h->cursor->next) if (h->cursor->ev.num == n) break; } if (h->cursor == &h->list) { he_seterrev(ev, _HE_NOT_FOUND); return (-1); } return (0); } /* history_set_nth(): * Default function to set the current event in the history to the * n-th one. */ private int history_set_nth(ptr_t p, HistEvent *ev, int n) { history_t *h = (history_t *) p; if (h->cur == 0) { he_seterrev(ev, _HE_EMPTY_LIST); return (-1); } for (h->cursor = h->list.prev; h->cursor != &h->list; h->cursor = h->cursor->prev) if (n-- <= 0) break; if (h->cursor == &h->list) { he_seterrev(ev, _HE_NOT_FOUND); return (-1); } return (0); } /* history_def_add(): * Append string to element */ private int history_def_add(ptr_t p, HistEvent *ev, const char *str) { history_t *h = (history_t *) p; size_t len; char *s; HistEventPrivate *evp = (void *)&h->cursor->ev; if (h->cursor == &h->list) return (history_def_enter(p, ev, str)); len = strlen(evp->str) + strlen(str) + 1; s = (char *) h_malloc(len); if (s == NULL) { he_seterrev(ev, _HE_MALLOC_FAILED); return (-1); } (void) strlcpy(s, h->cursor->ev.str, len); (void) strlcat(s, str, len); h_free((ptr_t)evp->str); evp->str = s; *ev = h->cursor->ev; return (0); } private int history_deldata_nth(history_t *h, HistEvent *ev, int num, void **data) { if (history_set_nth(h, ev, num) != 0) return (-1); /* magic value to skip delete (just set to n-th history) */ if (data == (void **)-1) return (0); ev->str = strdup(h->cursor->ev.str); ev->num = h->cursor->ev.num; if (data) *data = h->cursor->data; history_def_delete(h, ev, h->cursor); return (0); } /* history_def_del(): * Delete element hp of the h list */ /* ARGSUSED */ private int history_def_del(ptr_t p, HistEvent *ev __attribute__((__unused__)), const int num) { history_t *h = (history_t *) p; if (history_def_set(h, ev, num) != 0) return (-1); ev->str = strdup(h->cursor->ev.str); ev->num = h->cursor->ev.num; history_def_delete(h, ev, h->cursor); return (0); } /* history_def_delete(): * Delete element hp of the h list */ /* ARGSUSED */ private void history_def_delete(history_t *h, HistEvent *ev __attribute__((__unused__)), hentry_t *hp) { HistEventPrivate *evp = (void *)&hp->ev; if (hp == &h->list) abort(); if (h->cursor == hp) { h->cursor = hp->prev; if (h->cursor == &h->list) h->cursor = hp->next; } hp->prev->next = hp->next; hp->next->prev = hp->prev; h_free((ptr_t) evp->str); h_free(hp); h->cur--; } /* history_def_insert(): * Insert element with string str in the h list */ private int history_def_insert(history_t *h, HistEvent *ev, const char *str) { h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t)); if (h->cursor == NULL) goto oomem; if ((h->cursor->ev.str = h_strdup(str)) == NULL) { h_free((ptr_t)h->cursor); goto oomem; } h->cursor->data = NULL; h->cursor->ev.num = ++h->eventid; h->cursor->next = h->list.next; h->cursor->prev = &h->list; h->list.next->prev = h->cursor; h->list.next = h->cursor; h->cur++; *ev = h->cursor->ev; return (0); oomem: he_seterrev(ev, _HE_MALLOC_FAILED); return (-1); } /* history_def_enter(): * Default function to enter an item in the history */ private int history_def_enter(ptr_t p, HistEvent *ev, const char *str) { history_t *h = (history_t *) p; if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list && strcmp(h->list.next->ev.str, str) == 0) return (0); if (history_def_insert(h, ev, str) == -1) return (-1); /* error, keep error message */ /* * Always keep at least one entry. * This way we don't have to check for the empty list. */ while (h->cur > h->max && h->cur > 0) history_def_delete(h, ev, h->list.prev); return (1); } /* history_def_init(): * Default history initialization function */ /* ARGSUSED */ private int history_def_init(ptr_t *p, HistEvent *ev __attribute__((__unused__)), int n) { history_t *h = (history_t *) h_malloc(sizeof(history_t)); if (h == NULL) return -1; if (n <= 0) n = 0; h->eventid = 0; h->cur = 0; h->max = n; h->list.next = h->list.prev = &h->list; h->list.ev.str = NULL; h->list.ev.num = 0; h->cursor = &h->list; h->flags = 0; *p = (ptr_t) h; return 0; } /* history_def_clear(): * Default history cleanup function */ private void history_def_clear(ptr_t p, HistEvent *ev) { history_t *h = (history_t *) p; while (h->list.prev != &h->list) history_def_delete(h, ev, h->list.prev); h->eventid = 0; h->cur = 0; } /************************************************************************/ /* history_init(): * Initialization function. */ public History * history_init(void) { HistEvent ev; History *h = (History *) h_malloc(sizeof(History)); if (h == NULL) return NULL; if (history_def_init(&h->h_ref, &ev, 0) == -1) { h_free((ptr_t)h); return NULL; } h->h_ent = -1; h->h_next = history_def_next; h->h_first = history_def_first; h->h_last = history_def_last; h->h_prev = history_def_prev; h->h_curr = history_def_curr; h->h_set = history_def_set; h->h_clear = history_def_clear; h->h_enter = history_def_enter; h->h_add = history_def_add; h->h_del = history_def_del; return (h); } /* history_end(): * clean up history; */ public void history_end(History *h) { HistEvent ev; if (h->h_next == history_def_next) history_def_clear(h->h_ref, &ev); h_free(h->h_ref); h_free(h); } /* history_setsize(): * Set history number of events */ private int history_setsize(History *h, HistEvent *ev, int num) { if (h->h_next != history_def_next) { he_seterrev(ev, _HE_NOT_ALLOWED); return (-1); } if (num < 0) { he_seterrev(ev, _HE_BAD_PARAM); return (-1); } history_def_setsize(h->h_ref, num); return (0); } /* history_getsize(): * Get number of events currently in history */ private int history_getsize(History *h, HistEvent *ev) { if (h->h_next != history_def_next) { he_seterrev(ev, _HE_NOT_ALLOWED); return (-1); } ev->num = history_def_getsize(h->h_ref); if (ev->num < -1) { he_seterrev(ev, _HE_SIZE_NEGATIVE); return (-1); } return (0); } /* history_setunique(): * Set if adjacent equal events should not be entered in history. */ private int history_setunique(History *h, HistEvent *ev, int uni) { if (h->h_next != history_def_next) { he_seterrev(ev, _HE_NOT_ALLOWED); return (-1); } history_def_setunique(h->h_ref, uni); return (0); } /* history_getunique(): * Get if adjacent equal events should not be entered in history. */ private int history_getunique(History *h, HistEvent *ev) { if (h->h_next != history_def_next) { he_seterrev(ev, _HE_NOT_ALLOWED); return (-1); } ev->num = history_def_getunique(h->h_ref); return (0); } /* history_set_fun(): * Set history functions */ private int history_set_fun(History *h, History *nh) { HistEvent ev; if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL || nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL || nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL || nh->h_del == NULL || nh->h_ref == NULL) { if (h->h_next != history_def_next) { history_def_init(&h->h_ref, &ev, 0); h->h_first = history_def_first; h->h_next = history_def_next; h->h_last = history_def_last; h->h_prev = history_def_prev; h->h_curr = history_def_curr; h->h_set = history_def_set; h->h_clear = history_def_clear; h->h_enter = history_def_enter; h->h_add = history_def_add; h->h_del = history_def_del; } return (-1); } if (h->h_next == history_def_next) history_def_clear(h->h_ref, &ev); h->h_ent = -1; h->h_first = nh->h_first; h->h_next = nh->h_next; h->h_last = nh->h_last; h->h_prev = nh->h_prev; h->h_curr = nh->h_curr; h->h_set = nh->h_set; h->h_clear = nh->h_clear; h->h_enter = nh->h_enter; h->h_add = nh->h_add; h->h_del = nh->h_del; return (0); } /* history_load(): * History load function */ private int history_load(History *h, const char *fname) { FILE *fp; char *line; size_t sz, max_size; char *ptr; int i = -1; HistEvent ev; if ((fp = fopen(fname, "r")) == NULL) return (i); if ((line = fgetln(fp, &sz)) == NULL) goto done; if (strncmp(line, hist_cookie, sz) != 0) goto done; ptr = h_malloc(max_size = 1024); if (ptr == NULL) goto done; for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) { char c = line[sz]; if (sz != 0 && line[sz - 1] == '\n') line[--sz] = '\0'; else line[sz] = '\0'; if (max_size < sz) { char *nptr; max_size = (sz + 1024) & ~1023; nptr = h_realloc(ptr, max_size); if (nptr == NULL) { i = -1; goto oomem; } ptr = nptr; } (void) strunvis(ptr, line); line[sz] = c; if (HENTER(h, &ev, ptr) == -1) { i = -1; goto oomem; } } oomem: h_free((ptr_t)ptr); done: (void) fclose(fp); return (i); } /* history_save(): * History save function */ private int history_save(History *h, const char *fname) { FILE *fp; HistEvent ev; int i = -1, retval; size_t len, max_size; char *ptr; if ((fp = fopen(fname, "w")) == NULL) return (-1); if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1) goto done; if (fputs(hist_cookie, fp) == EOF) goto done; ptr = h_malloc(max_size = 1024); if (ptr == NULL) goto done; for (i = 0, retval = HLAST(h, &ev); retval != -1; retval = HPREV(h, &ev), i++) { len = strlen(ev.str) * 4; if (len >= max_size) { char *nptr; max_size = (len + 1024) & ~1023; nptr = h_realloc(ptr, max_size); if (nptr == NULL) { i = -1; goto oomem; } ptr = nptr; } (void) strvis(ptr, ev.str, VIS_WHITE); (void) fprintf(fp, "%s\n", ptr); } oomem: h_free((ptr_t)ptr); done: (void) fclose(fp); return (i); } /* history_prev_event(): * Find the previous event, with number given */ private int history_prev_event(History *h, HistEvent *ev, int num) { int retval; for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) if (ev->num == num) return (0); he_seterrev(ev, _HE_NOT_FOUND); return (-1); } private int history_next_evdata(History *h, HistEvent *ev, int num, void **d) { int retval; for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) if (num-- <= 0) { if (d) *d = ((history_t *)h->h_ref)->cursor->data; return (0); } he_seterrev(ev, _HE_NOT_FOUND); return (-1); } /* history_next_event(): * Find the next event, with number given */ private int history_next_event(History *h, HistEvent *ev, int num) { int retval; for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev)) if (ev->num == num) return (0); he_seterrev(ev, _HE_NOT_FOUND); return (-1); } /* history_prev_string(): * Find the previous event beginning with string */ private int history_prev_string(History *h, HistEvent *ev, const char *str) { size_t len = strlen(str); int retval; for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev)) if (strncmp(str, ev->str, len) == 0) return (0); he_seterrev(ev, _HE_NOT_FOUND); return (-1); } /* history_next_string(): * Find the next event beginning with string */ private int history_next_string(History *h, HistEvent *ev, const char *str) { size_t len = strlen(str); int retval; for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev)) if (strncmp(str, ev->str, len) == 0) return (0); he_seterrev(ev, _HE_NOT_FOUND); return (-1); } /* history(): * User interface to history functions. */ int history(History *h, HistEvent *ev, int fun, ...) { va_list va; const char *str; int retval; va_start(va, fun); he_seterrev(ev, _HE_OK); switch (fun) { case H_GETSIZE: retval = history_getsize(h, ev); break; case H_SETSIZE: retval = history_setsize(h, ev, va_arg(va, int)); break; case H_GETUNIQUE: retval = history_getunique(h, ev); break; case H_SETUNIQUE: retval = history_setunique(h, ev, va_arg(va, int)); break; case H_ADD: str = va_arg(va, const char *); retval = HADD(h, ev, str); break; case H_DEL: retval = HDEL(h, ev, va_arg(va, const int)); break; case H_ENTER: str = va_arg(va, const char *); if ((retval = HENTER(h, ev, str)) != -1) h->h_ent = ev->num; break; case H_APPEND: str = va_arg(va, const char *); if ((retval = HSET(h, ev, h->h_ent)) != -1) retval = HADD(h, ev, str); break; case H_FIRST: retval = HFIRST(h, ev); break; case H_NEXT: retval = HNEXT(h, ev); break; case H_LAST: retval = HLAST(h, ev); break; case H_PREV: retval = HPREV(h, ev); break; case H_CURR: retval = HCURR(h, ev); break; case H_SET: retval = HSET(h, ev, va_arg(va, const int)); break; case H_CLEAR: HCLEAR(h, ev); retval = 0; break; case H_LOAD: retval = history_load(h, va_arg(va, const char *)); if (retval == -1) he_seterrev(ev, _HE_HIST_READ); break; case H_SAVE: retval = history_save(h, va_arg(va, const char *)); if (retval == -1) he_seterrev(ev, _HE_HIST_WRITE); break; case H_PREV_EVENT: retval = history_prev_event(h, ev, va_arg(va, int)); break; case H_NEXT_EVENT: retval = history_next_event(h, ev, va_arg(va, int)); break; case H_PREV_STR: retval = history_prev_string(h, ev, va_arg(va, const char *)); break; case H_NEXT_STR: retval = history_next_string(h, ev, va_arg(va, const char *)); break; case H_FUNC: { History hf; hf.h_ref = va_arg(va, ptr_t); h->h_ent = -1; hf.h_first = va_arg(va, history_gfun_t); hf.h_next = va_arg(va, history_gfun_t); hf.h_last = va_arg(va, history_gfun_t); hf.h_prev = va_arg(va, history_gfun_t); hf.h_curr = va_arg(va, history_gfun_t); hf.h_set = va_arg(va, history_sfun_t); hf.h_clear = va_arg(va, history_vfun_t); hf.h_enter = va_arg(va, history_efun_t); hf.h_add = va_arg(va, history_efun_t); hf.h_del = va_arg(va, history_sfun_t); if ((retval = history_set_fun(h, &hf)) == -1) he_seterrev(ev, _HE_PARAM_MISSING); break; } case H_END: history_end(h); retval = 0; break; case H_NEXT_EVDATA: { int num = va_arg(va, int); void **d = va_arg(va, void **); retval = history_next_evdata(h, ev, num, d); break; } case H_DELDATA: { int num = va_arg(va, int); void **d = va_arg(va, void **); retval = history_deldata_nth((history_t *)h->h_ref, ev, num, d); break; } case H_REPLACE: /* only use after H_NEXT_EVDATA */ { const char *line = va_arg(va, const char *); void *d = va_arg(va, void *); const char *s; if(!line || !(s = strdup(line))) { retval = -1; break; } ((history_t *)h->h_ref)->cursor->ev.str = s; ((history_t *)h->h_ref)->cursor->data = d; retval = 0; break; } default: retval = -1; he_seterrev(ev, _HE_UNKNOWN); break; } va_end(va); return retval; } clt/falcon/editline/src/key.c000066400000000000000000000406221176363201700164340ustar00rootroot00000000000000/* $NetBSD: key.c,v 1.22 2009/02/21 23:31:56 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)key.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: key.c,v 1.22 2009/02/21 23:31:56 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * key.c: This module contains the procedures for maintaining * the extended-key map. * * An extended-key (key) is a sequence of keystrokes introduced * with a sequence introducer and consisting of an arbitrary * number of characters. This module maintains a map (the el->el_key.map) * to convert these extended-key sequences into input strs * (XK_STR), editor functions (XK_CMD), or unix commands (XK_EXE). * * Warning: * If key is a substr of some other keys, then the longer * keys are lost!! That is, if the keys "abcd" and "abcef" * are in el->el_key.map, adding the key "abc" will cause the first two * definitions to be lost. * * Restrictions: * ------------- * 1) It is not possible to have one key that is a * substr of another. */ #include #include #include "el.h" /* * The Nodes of the el->el_key.map. The el->el_key.map is a linked list * of these node elements */ struct key_node_t { char ch; /* single character of key */ int type; /* node type */ key_value_t val; /* command code or pointer to str, */ /* if this is a leaf */ struct key_node_t *next; /* ptr to next char of this key */ struct key_node_t *sibling; /* ptr to another key with same prefix*/ }; private int node_trav(EditLine *, key_node_t *, char *, key_value_t *); private int node__try(EditLine *, key_node_t *, const char *, key_value_t *, int); private key_node_t *node__get(int); private void node__free(key_node_t *); private void node__put(EditLine *, key_node_t *); private int node__delete(EditLine *, key_node_t **, const char *); private int node_lookup(EditLine *, const char *, key_node_t *, size_t); private int node_enum(EditLine *, key_node_t *, size_t); #define KEY_BUFSIZ EL_BUFSIZ /* key_init(): * Initialize the key maps */ protected int key_init(EditLine *el) { el->el_key.buf = (char *) el_malloc(KEY_BUFSIZ); if (el->el_key.buf == NULL) return (-1); el->el_key.map = NULL; key_reset(el); return (0); } /* key_end(): * Free the key maps */ protected void key_end(EditLine *el) { el_free((ptr_t) el->el_key.buf); el->el_key.buf = NULL; node__free(el->el_key.map); } /* key_map_cmd(): * Associate cmd with a key value */ protected key_value_t * key_map_cmd(EditLine *el, int cmd) { el->el_key.val.cmd = (el_action_t) cmd; return (&el->el_key.val); } /* key_map_str(): * Associate str with a key value */ protected key_value_t * key_map_str(EditLine *el, char *str) { el->el_key.val.str = str; return (&el->el_key.val); } /* key_reset(): * Takes all nodes on el->el_key.map and puts them on free list. Then * initializes el->el_key.map with arrow keys * [Always bind the ansi arrow keys?] */ protected void key_reset(EditLine *el) { node__put(el, el->el_key.map); el->el_key.map = NULL; return; } /* key_get(): * Calls the recursive function with entry point el->el_key.map * Looks up *ch in map and then reads characters until a * complete match is found or a mismatch occurs. Returns the * type of the match found (XK_STR, XK_CMD, or XK_EXE). * Returns NULL in val.str and XK_STR for no match. * The last character read is returned in *ch. */ protected int key_get(EditLine *el, char *ch, key_value_t *val) { return (node_trav(el, el->el_key.map, ch, val)); } /* key_add(): * Adds key to the el->el_key.map and associates the value in val with it. * If key is already is in el->el_key.map, the new code is applied to the * existing key. Ntype specifies if code is a command, an * out str or a unix command. */ protected void key_add(EditLine *el, const char *key, key_value_t *val, int ntype) { if (key[0] == '\0') { (void) fprintf(el->el_errfile, "key_add: Null extended-key not allowed.\n"); return; } if (ntype == XK_CMD && val->cmd == ED_SEQUENCE_LEAD_IN) { (void) fprintf(el->el_errfile, "key_add: sequence-lead-in command not allowed\n"); return; } if (el->el_key.map == NULL) /* tree is initially empty. Set up new node to match key[0] */ el->el_key.map = node__get(key[0]); /* it is properly initialized */ /* Now recurse through el->el_key.map */ (void) node__try(el, el->el_key.map, key, val, ntype); return; } /* key_clear(): * */ protected void key_clear(EditLine *el, el_action_t *map, const char *in) { if ((map[(unsigned char)*in] == ED_SEQUENCE_LEAD_IN) && ((map == el->el_map.key && el->el_map.alt[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN) || (map == el->el_map.alt && el->el_map.key[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN))) (void) key_delete(el, in); } /* key_delete(): * Delete the key and all longer keys staring with key, if * they exists. */ protected int key_delete(EditLine *el, const char *key) { if (key[0] == '\0') { (void) fprintf(el->el_errfile, "key_delete: Null extended-key not allowed.\n"); return (-1); } if (el->el_key.map == NULL) return (0); (void) node__delete(el, &el->el_key.map, key); return (0); } /* key_print(): * Print the binding associated with key key. * Print entire el->el_key.map if null */ protected void key_print(EditLine *el, const char *key) { /* do nothing if el->el_key.map is empty and null key specified */ if (el->el_key.map == NULL && *key == 0) return; el->el_key.buf[0] = '"'; if (node_lookup(el, key, el->el_key.map, 1) <= -1) /* key is not bound */ (void) fprintf(el->el_errfile, "Unbound extended key \"%s\"\n", key); return; } /* node_trav(): * recursively traverses node in tree until match or mismatch is * found. May read in more characters. */ private int node_trav(EditLine *el, key_node_t *ptr, char *ch, key_value_t *val) { if (ptr->ch == *ch) { /* match found */ if (ptr->next) { /* key not complete so get next char */ if (el_getc(el, ch) != 1) { /* if EOF or error */ val->cmd = ED_END_OF_FILE; return (XK_CMD); /* PWP: Pretend we just read an end-of-file */ } return (node_trav(el, ptr->next, ch, val)); } else { *val = ptr->val; if (ptr->type != XK_CMD) *ch = '\0'; return (ptr->type); } } else { /* no match found here */ if (ptr->sibling) { /* try next sibling */ return (node_trav(el, ptr->sibling, ch, val)); } else { /* no next sibling -- mismatch */ val->str = NULL; return (XK_STR); } } } /* node__try(): * Find a node that matches *str or allocate a new one */ private int node__try(EditLine *el, key_node_t *ptr, const char *str, key_value_t *val, int ntype) { if (ptr->ch != *str) { key_node_t *xm; for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) if (xm->sibling->ch == *str) break; if (xm->sibling == NULL) xm->sibling = node__get(*str); /* setup new node */ ptr = xm->sibling; } if (*++str == '\0') { /* we're there */ if (ptr->next != NULL) { node__put(el, ptr->next); /* lose longer keys with this prefix */ ptr->next = NULL; } switch (ptr->type) { case XK_CMD: case XK_NOD: break; case XK_STR: case XK_EXE: if (ptr->val.str) el_free((ptr_t) ptr->val.str); break; default: EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ptr->type)); break; } switch (ptr->type = ntype) { case XK_CMD: ptr->val = *val; break; case XK_STR: case XK_EXE: if ((ptr->val.str = el_strdup(val->str)) == NULL) return -1; break; default: EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype)); break; } } else { /* still more chars to go */ if (ptr->next == NULL) ptr->next = node__get(*str); /* setup new node */ (void) node__try(el, ptr->next, str, val, ntype); } return (0); } /* node__delete(): * Delete node that matches str */ private int node__delete(EditLine *el, key_node_t **inptr, const char *str) { key_node_t *ptr; key_node_t *prev_ptr = NULL; ptr = *inptr; if (ptr->ch != *str) { key_node_t *xm; for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) if (xm->sibling->ch == *str) break; if (xm->sibling == NULL) return (0); prev_ptr = xm; ptr = xm->sibling; } if (*++str == '\0') { /* we're there */ if (prev_ptr == NULL) *inptr = ptr->sibling; else prev_ptr->sibling = ptr->sibling; ptr->sibling = NULL; node__put(el, ptr); return (1); } else if (ptr->next != NULL && node__delete(el, &ptr->next, str) == 1) { if (ptr->next != NULL) return (0); if (prev_ptr == NULL) *inptr = ptr->sibling; else prev_ptr->sibling = ptr->sibling; ptr->sibling = NULL; node__put(el, ptr); return (1); } else { return (0); } } /* node__put(): * Puts a tree of nodes onto free list using free(3). */ private void node__put(EditLine *el, key_node_t *ptr) { if (ptr == NULL) return; if (ptr->next != NULL) { node__put(el, ptr->next); ptr->next = NULL; } node__put(el, ptr->sibling); switch (ptr->type) { case XK_CMD: case XK_NOD: break; case XK_EXE: case XK_STR: if (ptr->val.str != NULL) el_free((ptr_t) ptr->val.str); break; default: EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ptr->type)); break; } el_free((ptr_t) ptr); } /* node__get(): * Returns pointer to a key_node_t for ch. */ private key_node_t * node__get(int ch) { key_node_t *ptr; ptr = (key_node_t *) el_malloc((size_t) sizeof(key_node_t)); if (ptr == NULL) return NULL; ptr->ch = ch; ptr->type = XK_NOD; ptr->val.str = NULL; ptr->next = NULL; ptr->sibling = NULL; return (ptr); } private void node__free(key_node_t *k) { if (k == NULL) return; node__free(k->sibling); node__free(k->next); el_free((ptr_t) k); } /* node_lookup(): * look for the str starting at node ptr. * Print if last node */ private int node_lookup(EditLine *el, const char *str, key_node_t *ptr, size_t cnt) { size_t ncnt; if (ptr == NULL) return (-1); /* cannot have null ptr */ if (*str == 0) { /* no more chars in str. node_enum from here. */ (void) node_enum(el, ptr, cnt); return (0); } else { /* If match put this char into el->el_key.buf. Recurse */ if (ptr->ch == *str) { /* match found */ ncnt = key__decode_char(el->el_key.buf, (size_t)KEY_BUFSIZ, cnt, (unsigned char) ptr->ch); if (ptr->next != NULL) /* not yet at leaf */ return (node_lookup(el, str + 1, ptr->next, ncnt + 1)); else { /* next node is null so key should be complete */ if (str[1] == 0) { el->el_key.buf[ncnt + 1] = '"'; el->el_key.buf[ncnt + 2] = '\0'; key_kprint(el, el->el_key.buf, &ptr->val, ptr->type); return (0); } else return (-1); /* mismatch -- str still has chars */ } } else { /* no match found try sibling */ if (ptr->sibling) return (node_lookup(el, str, ptr->sibling, cnt)); else return (-1); } } } /* node_enum(): * Traverse the node printing the characters it is bound in buffer */ private int node_enum(EditLine *el, key_node_t *ptr, size_t cnt) { size_t ncnt; if (cnt >= KEY_BUFSIZ - 5) { /* buffer too small */ el->el_key.buf[++cnt] = '"'; el->el_key.buf[++cnt] = '\0'; (void) fprintf(el->el_errfile, "Some extended keys too long for internal print buffer"); (void) fprintf(el->el_errfile, " \"%s...\"\n", el->el_key.buf); return (0); } if (ptr == NULL) { #ifdef DEBUG_EDIT (void) fprintf(el->el_errfile, "node_enum: BUG!! Null ptr passed\n!"); #endif return (-1); } /* put this char at end of str */ ncnt = key__decode_char(el->el_key.buf, (size_t)KEY_BUFSIZ, cnt, (unsigned char)ptr->ch); if (ptr->next == NULL) { /* print this key and function */ el->el_key.buf[ncnt + 1] = '"'; el->el_key.buf[ncnt + 2] = '\0'; key_kprint(el, el->el_key.buf, &ptr->val, ptr->type); } else (void) node_enum(el, ptr->next, ncnt + 1); /* go to sibling if there is one */ if (ptr->sibling) (void) node_enum(el, ptr->sibling, cnt); return (0); } /* key_kprint(): * Print the specified key and its associated * function specified by val */ protected void key_kprint(EditLine *el, const char *key, key_value_t *val, int ntype) { el_bindings_t *fp; char unparsbuf[EL_BUFSIZ]; static const char fmt[] = "%-15s-> %s\n"; if (val != NULL) switch (ntype) { case XK_STR: case XK_EXE: (void) key__decode_str(val->str, unparsbuf, sizeof(unparsbuf), ntype == XK_STR ? "\"\"" : "[]"); (void) fprintf(el->el_outfile, fmt, key, unparsbuf); break; case XK_CMD: for (fp = el->el_map.help; fp->name; fp++) if (val->cmd == fp->func) { (void) fprintf(el->el_outfile, fmt, key, fp->name); break; } #ifdef DEBUG_KEY if (fp->name == NULL) (void) fprintf(el->el_outfile, "BUG! Command not found.\n"); #endif break; default: EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype)); break; } else (void) fprintf(el->el_outfile, fmt, key, "no input"); } #define ADDC(c) \ if (b < eb) \ *b++ = c; \ else \ b++ /* key__decode_char(): * Put a printable form of char in buf. */ protected size_t key__decode_char(char *buf, size_t cnt, size_t off, int ch) { char *sb = buf + off; char *eb = buf + cnt; char *b = sb; if (ch == 0) { ADDC('^'); ADDC('@'); return (int)(b - sb); } if (iscntrl(ch)) { ADDC('^'); if (ch == '\177') ADDC('?'); else ADDC(ch | 0100); } else if (ch == '^') { ADDC('\\'); ADDC('^'); } else if (ch == '\\') { ADDC('\\'); ADDC('\\'); } else if (ch == ' ' || (isprint(ch) && !isspace(ch))) { ADDC(ch); } else { ADDC('\\'); ADDC((((unsigned int) ch >> 6) & 7) + '0'); ADDC((((unsigned int) ch >> 3) & 7) + '0'); ADDC((ch & 7) + '0'); } return (size_t)(b - sb); } /* key__decode_str(): * Make a printable version of the ey */ protected size_t key__decode_str(const char *str, char *buf, size_t len, const char *sep) { char *b = buf, *eb = b + len; const char *p; b = buf; if (sep[0] != '\0') { ADDC(sep[0]); } if (*str == '\0') { ADDC('^'); ADDC('@'); if (sep[0] != '\0' && sep[1] != '\0') { ADDC(sep[1]); } goto done; } for (p = str; *p != 0; p++) { if (iscntrl((unsigned char) *p)) { ADDC('^'); if (*p == '\177') { ADDC('?'); } else { ADDC(*p | 0100); } } else if (*p == '^' || *p == '\\') { ADDC('\\'); ADDC(*p); } else if (*p == ' ' || (isprint((unsigned char) *p) && !isspace((unsigned char) *p))) { ADDC(*p); } else { ADDC('\\'); ADDC((((unsigned int) *p >> 6) & 7) + '0'); ADDC((((unsigned int) *p >> 3) & 7) + '0'); ADDC((*p & 7) + '0'); } } if (sep[0] != '\0' && sep[1] != '\0') { ADDC(sep[1]); } done: ADDC('\0'); if ((size_t)(b - buf) >= len) buf[len - 1] = '\0'; return (size_t)(b - buf); } clt/falcon/editline/src/key.h000066400000000000000000000060661176363201700164450ustar00rootroot00000000000000/* $NetBSD: key.h,v 1.12 2009/02/21 23:31:56 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)key.h 8.1 (Berkeley) 6/4/93 */ /* * el.key.h: Key macro header */ #ifndef _h_el_key #define _h_el_key typedef union key_value_t { el_action_t cmd; /* If it is a command the # */ char *str; /* If it is a string... */ } key_value_t; typedef struct key_node_t key_node_t; typedef struct el_key_t { char *buf; /* Key print buffer */ key_node_t *map; /* Key map */ key_value_t val; /* Local conversion buffer */ } el_key_t; #define XK_CMD 0 #define XK_STR 1 #define XK_NOD 2 #define XK_EXE 3 #undef key_end #undef key_clear #undef key_print protected int key_init(EditLine *); protected void key_end(EditLine *); protected key_value_t *key_map_cmd(EditLine *, int); protected key_value_t *key_map_str(EditLine *, char *); protected void key_reset(EditLine *); protected int key_get(EditLine *, char *, key_value_t *); protected void key_add(EditLine *, const char *, key_value_t *, int); protected void key_clear(EditLine *, el_action_t *, const char *); protected int key_delete(EditLine *, const char *); protected void key_print(EditLine *, const char *); protected void key_kprint(EditLine *, const char *, key_value_t *, int); protected size_t key__decode_str(const char *, char *, size_t, const char *); protected size_t key__decode_char(char *, size_t, size_t, int); #endif /* _h_el_key */ clt/falcon/editline/src/makelist000066400000000000000000000145221176363201700172340ustar00rootroot00000000000000#!/bin/sh - # $NetBSD: makelist,v 1.12 2009/02/12 13:39:49 sketch Exp $ # # Copyright (c) 1992, 1993 # The Regents of the University of California. All rights reserved. # # This code is derived from software contributed to Berkeley by # Christos Zoulas of Cornell University. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. Neither the name of the University nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # @(#)makelist 5.3 (Berkeley) 6/4/93 # makelist.sh: Automatically generate header files... AWK=awk USAGE="Usage: $0 -h|-e|-fc|-fh|-bc|-bh|-m " if [ "x$1" = "x" ] then echo $USAGE 1>&2 exit 1 fi FLAG="$1" shift FILES="$@" case $FLAG in # generate foo.h file from foo.c # -h) set - `echo $FILES | sed -e 's/\\./_/g'` hdr="_h_`basename $1`" cat $FILES | $AWK ' BEGIN { printf("/* Automatically generated file, do not edit */\n"); printf("#ifndef %s\n#define %s\n", "'$hdr'", "'$hdr'"); } /\(\):/ { pr = substr($2, 1, 2); if (pr == "vi" || pr == "em" || pr == "ed") { name = substr($2, 1, length($2) - 3); # # XXX: need a space between name and prototype so that -fc and -fh # parsing is much easier # printf("protected el_action_t\t%s (EditLine *, int);\n", name); } } END { printf("#endif /* %s */\n", "'$hdr'"); }' ;; # generate help.c from various .c files # -bc) cat $FILES | $AWK ' BEGIN { printf("/* Automatically generated file, do not edit */\n"); printf("#include \"config.h\"\n#include \"el.h\"\n"); printf("private const struct el_bindings_t el_func_help[] = {\n"); low = "abcdefghijklmnopqrstuvwxyz_"; high = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_"; for (i = 1; i <= length(low); i++) tr[substr(low, i, 1)] = substr(high, i, 1); } /\(\):/ { pr = substr($2, 1, 2); if (pr == "vi" || pr == "em" || pr == "ed") { name = substr($2, 1, length($2) - 3); uname = ""; fname = ""; for (i = 1; i <= length(name); i++) { s = substr(name, i, 1); uname = uname tr[s]; if (s == "_") s = "-"; fname = fname s; } printf(" { %-30.30s %-30.30s\n","\"" fname "\",", uname ","); ok = 1; } } /^ \*/ { if (ok) { printf(" \""); for (i = 2; i < NF; i++) printf("%s ", $i); printf("%s\" },\n", $i); ok = 0; } } END { printf("};\n"); printf("\nprotected const el_bindings_t* help__get()"); printf("{ return el_func_help; }\n"); }' ;; # generate help.h from various .c files # -bh) $AWK ' BEGIN { printf("/* Automatically generated file, do not edit */\n"); printf("#ifndef _h_help_c\n#define _h_help_c\n"); printf("protected const el_bindings_t *help__get(void);\n"); printf("#endif /* _h_help_c */\n"); }' /dev/null ;; # generate fcns.h from various .h files # -fh) cat $FILES | $AWK '/el_action_t/ { print $3 }' | \ sort | tr '[:lower:]' '[:upper:]' | $AWK ' BEGIN { printf("/* Automatically generated file, do not edit */\n"); printf("#ifndef _h_fcns_c\n#define _h_fcns_c\n"); count = 0; } { printf("#define\t%-30.30s\t%3d\n", $1, count++); } END { printf("#define\t%-30.30s\t%3d\n", "EL_NUM_FCNS", count); printf("typedef el_action_t (*el_func_t)(EditLine *, int);"); printf("\nprotected const el_func_t* func__get(void);\n"); printf("#endif /* _h_fcns_c */\n"); }' ;; # generate fcns.c from various .h files # -fc) cat $FILES | $AWK '/el_action_t/ { print $3 }' | sort | $AWK ' BEGIN { printf("/* Automatically generated file, do not edit */\n"); printf("#include \"config.h\"\n#include \"el.h\"\n"); printf("private const el_func_t el_func[] = {"); maxlen = 80; needn = 1; len = 0; } { clen = 25 + 2; len += clen; if (len >= maxlen) needn = 1; if (needn) { printf("\n "); needn = 0; len = 4 + clen; } s = $1 ","; printf("%-26.26s ", s); } END { printf("\n};\n"); printf("\nprotected const el_func_t* func__get() { return el_func; }\n"); }' ;; # generate editline.c from various .c files # -e) echo "$FILES" | tr ' ' '\012' | $AWK ' BEGIN { printf("/* Automatically generated file, do not edit */\n"); printf("#define protected static\n"); printf("#define SCCSID\n"); } { printf("#include \"%s\"\n", $1); }' ;; # generate man page fragment from various .c files # -m) cat $FILES | $AWK ' BEGIN { printf(".\\\" Section automatically generated with makelist\n"); printf(".Bl -tag -width 4n\n"); } /\(\):/ { pr = substr($2, 1, 2); if (pr == "vi" || pr == "em" || pr == "ed") { name = substr($2, 1, length($2) - 3); fname = ""; for (i = 1; i <= length(name); i++) { s = substr(name, i, 1); if (s == "_") s = "-"; fname = fname s; } printf(".It Ic %s\n", fname); ok = 1; } } /^ \*/ { if (ok) { for (i = 2; i < NF; i++) printf("%s ", $i); printf("%s.\n", $i); ok = 0; } } END { printf(".El\n"); printf(".\\\" End of section automatically generated with makelist\n"); }' ;; *) echo $USAGE 1>&2 exit 1 ;; esac clt/falcon/editline/src/map.c000066400000000000000000001233471176363201700164270ustar00rootroot00000000000000/* $NetBSD: map.c,v 1.24 2006/04/09 01:36:51 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)map.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: map.c,v 1.24 2006/04/09 01:36:51 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * map.c: Editor function definitions */ #include #include "el.h" #define N_KEYS 256 private void map_print_key(EditLine *, el_action_t *, const char *); private void map_print_some_keys(EditLine *, el_action_t *, int, int); private void map_print_all_keys(EditLine *); private void map_init_nls(EditLine *); private void map_init_meta(EditLine *); /* keymap tables ; should be N_KEYS*sizeof(KEYCMD) bytes long */ private const el_action_t el_map_emacs[] = { /* 0 */ EM_SET_MARK, /* ^@ */ /* 1 */ ED_MOVE_TO_BEG, /* ^A */ /* 2 */ ED_PREV_CHAR, /* ^B */ /* 3 */ ED_TTY_SIGINT, /* ^C */ /* 4 */ EM_DELETE_OR_LIST, /* ^D */ /* 5 */ ED_MOVE_TO_END, /* ^E */ /* 6 */ ED_NEXT_CHAR, /* ^F */ /* 7 */ ED_UNASSIGNED, /* ^G */ /* 8 */ EM_DELETE_PREV_CHAR, /* ^H */ /* 9 */ ED_UNASSIGNED, /* ^I */ /* 10 */ ED_NEWLINE, /* ^J */ /* 11 */ ED_KILL_LINE, /* ^K */ /* 12 */ ED_CLEAR_SCREEN, /* ^L */ /* 13 */ ED_NEWLINE, /* ^M */ /* 14 */ ED_NEXT_HISTORY, /* ^N */ /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */ /* 16 */ ED_PREV_HISTORY, /* ^P */ /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */ /* 18 */ ED_REDISPLAY, /* ^R */ /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */ /* 20 */ ED_TRANSPOSE_CHARS, /* ^T */ /* 21 */ EM_KILL_LINE, /* ^U */ /* 22 */ ED_QUOTED_INSERT, /* ^V */ /* 23 */ EM_KILL_REGION, /* ^W */ /* 24 */ ED_SEQUENCE_LEAD_IN, /* ^X */ /* 25 */ EM_YANK, /* ^Y */ /* 26 */ ED_TTY_SIGTSTP, /* ^Z */ /* 27 */ EM_META_NEXT, /* ^[ */ /* 28 */ ED_TTY_SIGQUIT, /* ^\ */ /* 29 */ ED_TTY_DSUSP, /* ^] */ /* 30 */ ED_UNASSIGNED, /* ^^ */ /* 31 */ ED_UNASSIGNED, /* ^_ */ /* 32 */ ED_INSERT, /* SPACE */ /* 33 */ ED_INSERT, /* ! */ /* 34 */ ED_INSERT, /* " */ /* 35 */ ED_INSERT, /* # */ /* 36 */ ED_INSERT, /* $ */ /* 37 */ ED_INSERT, /* % */ /* 38 */ ED_INSERT, /* & */ /* 39 */ ED_INSERT, /* ' */ /* 40 */ ED_INSERT, /* ( */ /* 41 */ ED_INSERT, /* ) */ /* 42 */ ED_INSERT, /* * */ /* 43 */ ED_INSERT, /* + */ /* 44 */ ED_INSERT, /* , */ /* 45 */ ED_INSERT, /* - */ /* 46 */ ED_INSERT, /* . */ /* 47 */ ED_INSERT, /* / */ /* 48 */ ED_DIGIT, /* 0 */ /* 49 */ ED_DIGIT, /* 1 */ /* 50 */ ED_DIGIT, /* 2 */ /* 51 */ ED_DIGIT, /* 3 */ /* 52 */ ED_DIGIT, /* 4 */ /* 53 */ ED_DIGIT, /* 5 */ /* 54 */ ED_DIGIT, /* 6 */ /* 55 */ ED_DIGIT, /* 7 */ /* 56 */ ED_DIGIT, /* 8 */ /* 57 */ ED_DIGIT, /* 9 */ /* 58 */ ED_INSERT, /* : */ /* 59 */ ED_INSERT, /* ; */ /* 60 */ ED_INSERT, /* < */ /* 61 */ ED_INSERT, /* = */ /* 62 */ ED_INSERT, /* > */ /* 63 */ ED_INSERT, /* ? */ /* 64 */ ED_INSERT, /* @ */ /* 65 */ ED_INSERT, /* A */ /* 66 */ ED_INSERT, /* B */ /* 67 */ ED_INSERT, /* C */ /* 68 */ ED_INSERT, /* D */ /* 69 */ ED_INSERT, /* E */ /* 70 */ ED_INSERT, /* F */ /* 71 */ ED_INSERT, /* G */ /* 72 */ ED_INSERT, /* H */ /* 73 */ ED_INSERT, /* I */ /* 74 */ ED_INSERT, /* J */ /* 75 */ ED_INSERT, /* K */ /* 76 */ ED_INSERT, /* L */ /* 77 */ ED_INSERT, /* M */ /* 78 */ ED_INSERT, /* N */ /* 79 */ ED_INSERT, /* O */ /* 80 */ ED_INSERT, /* P */ /* 81 */ ED_INSERT, /* Q */ /* 82 */ ED_INSERT, /* R */ /* 83 */ ED_INSERT, /* S */ /* 84 */ ED_INSERT, /* T */ /* 85 */ ED_INSERT, /* U */ /* 86 */ ED_INSERT, /* V */ /* 87 */ ED_INSERT, /* W */ /* 88 */ ED_INSERT, /* X */ /* 89 */ ED_INSERT, /* Y */ /* 90 */ ED_INSERT, /* Z */ /* 91 */ ED_INSERT, /* [ */ /* 92 */ ED_INSERT, /* \ */ /* 93 */ ED_INSERT, /* ] */ /* 94 */ ED_INSERT, /* ^ */ /* 95 */ ED_INSERT, /* _ */ /* 96 */ ED_INSERT, /* ` */ /* 97 */ ED_INSERT, /* a */ /* 98 */ ED_INSERT, /* b */ /* 99 */ ED_INSERT, /* c */ /* 100 */ ED_INSERT, /* d */ /* 101 */ ED_INSERT, /* e */ /* 102 */ ED_INSERT, /* f */ /* 103 */ ED_INSERT, /* g */ /* 104 */ ED_INSERT, /* h */ /* 105 */ ED_INSERT, /* i */ /* 106 */ ED_INSERT, /* j */ /* 107 */ ED_INSERT, /* k */ /* 108 */ ED_INSERT, /* l */ /* 109 */ ED_INSERT, /* m */ /* 110 */ ED_INSERT, /* n */ /* 111 */ ED_INSERT, /* o */ /* 112 */ ED_INSERT, /* p */ /* 113 */ ED_INSERT, /* q */ /* 114 */ ED_INSERT, /* r */ /* 115 */ ED_INSERT, /* s */ /* 116 */ ED_INSERT, /* t */ /* 117 */ ED_INSERT, /* u */ /* 118 */ ED_INSERT, /* v */ /* 119 */ ED_INSERT, /* w */ /* 120 */ ED_INSERT, /* x */ /* 121 */ ED_INSERT, /* y */ /* 122 */ ED_INSERT, /* z */ /* 123 */ ED_INSERT, /* { */ /* 124 */ ED_INSERT, /* | */ /* 125 */ ED_INSERT, /* } */ /* 126 */ ED_INSERT, /* ~ */ /* 127 */ EM_DELETE_PREV_CHAR, /* ^? */ /* 128 */ ED_UNASSIGNED, /* M-^@ */ /* 129 */ ED_UNASSIGNED, /* M-^A */ /* 130 */ ED_UNASSIGNED, /* M-^B */ /* 131 */ ED_UNASSIGNED, /* M-^C */ /* 132 */ ED_UNASSIGNED, /* M-^D */ /* 133 */ ED_UNASSIGNED, /* M-^E */ /* 134 */ ED_UNASSIGNED, /* M-^F */ /* 135 */ ED_UNASSIGNED, /* M-^G */ /* 136 */ ED_DELETE_PREV_WORD, /* M-^H */ /* 137 */ ED_UNASSIGNED, /* M-^I */ /* 138 */ ED_UNASSIGNED, /* M-^J */ /* 139 */ ED_UNASSIGNED, /* M-^K */ /* 140 */ ED_CLEAR_SCREEN, /* M-^L */ /* 141 */ ED_UNASSIGNED, /* M-^M */ /* 142 */ ED_UNASSIGNED, /* M-^N */ /* 143 */ ED_UNASSIGNED, /* M-^O */ /* 144 */ ED_UNASSIGNED, /* M-^P */ /* 145 */ ED_UNASSIGNED, /* M-^Q */ /* 146 */ ED_UNASSIGNED, /* M-^R */ /* 147 */ ED_UNASSIGNED, /* M-^S */ /* 148 */ ED_UNASSIGNED, /* M-^T */ /* 149 */ ED_UNASSIGNED, /* M-^U */ /* 150 */ ED_UNASSIGNED, /* M-^V */ /* 151 */ ED_UNASSIGNED, /* M-^W */ /* 152 */ ED_UNASSIGNED, /* M-^X */ /* 153 */ ED_UNASSIGNED, /* M-^Y */ /* 154 */ ED_UNASSIGNED, /* M-^Z */ /* 155 */ ED_UNASSIGNED, /* M-^[ */ /* 156 */ ED_UNASSIGNED, /* M-^\ */ /* 157 */ ED_UNASSIGNED, /* M-^] */ /* 158 */ ED_UNASSIGNED, /* M-^^ */ /* 159 */ EM_COPY_PREV_WORD, /* M-^_ */ /* 160 */ ED_UNASSIGNED, /* M-SPACE */ /* 161 */ ED_UNASSIGNED, /* M-! */ /* 162 */ ED_UNASSIGNED, /* M-" */ /* 163 */ ED_UNASSIGNED, /* M-# */ /* 164 */ ED_UNASSIGNED, /* M-$ */ /* 165 */ ED_UNASSIGNED, /* M-% */ /* 166 */ ED_UNASSIGNED, /* M-& */ /* 167 */ ED_UNASSIGNED, /* M-' */ /* 168 */ ED_UNASSIGNED, /* M-( */ /* 169 */ ED_UNASSIGNED, /* M-) */ /* 170 */ ED_UNASSIGNED, /* M-* */ /* 171 */ ED_UNASSIGNED, /* M-+ */ /* 172 */ ED_UNASSIGNED, /* M-, */ /* 173 */ ED_UNASSIGNED, /* M-- */ /* 174 */ ED_UNASSIGNED, /* M-. */ /* 175 */ ED_UNASSIGNED, /* M-/ */ /* 176 */ ED_ARGUMENT_DIGIT, /* M-0 */ /* 177 */ ED_ARGUMENT_DIGIT, /* M-1 */ /* 178 */ ED_ARGUMENT_DIGIT, /* M-2 */ /* 179 */ ED_ARGUMENT_DIGIT, /* M-3 */ /* 180 */ ED_ARGUMENT_DIGIT, /* M-4 */ /* 181 */ ED_ARGUMENT_DIGIT, /* M-5 */ /* 182 */ ED_ARGUMENT_DIGIT, /* M-6 */ /* 183 */ ED_ARGUMENT_DIGIT, /* M-7 */ /* 184 */ ED_ARGUMENT_DIGIT, /* M-8 */ /* 185 */ ED_ARGUMENT_DIGIT, /* M-9 */ /* 186 */ ED_UNASSIGNED, /* M-: */ /* 187 */ ED_UNASSIGNED, /* M-; */ /* 188 */ ED_UNASSIGNED, /* M-< */ /* 189 */ ED_UNASSIGNED, /* M-= */ /* 190 */ ED_UNASSIGNED, /* M-> */ /* 191 */ ED_UNASSIGNED, /* M-? */ /* 192 */ ED_UNASSIGNED, /* M-@ */ /* 193 */ ED_UNASSIGNED, /* M-A */ /* 194 */ ED_PREV_WORD, /* M-B */ /* 195 */ EM_CAPITOL_CASE, /* M-C */ /* 196 */ EM_DELETE_NEXT_WORD, /* M-D */ /* 197 */ ED_UNASSIGNED, /* M-E */ /* 198 */ EM_NEXT_WORD, /* M-F */ /* 199 */ ED_UNASSIGNED, /* M-G */ /* 200 */ ED_UNASSIGNED, /* M-H */ /* 201 */ ED_UNASSIGNED, /* M-I */ /* 202 */ ED_UNASSIGNED, /* M-J */ /* 203 */ ED_UNASSIGNED, /* M-K */ /* 204 */ EM_LOWER_CASE, /* M-L */ /* 205 */ ED_UNASSIGNED, /* M-M */ /* 206 */ ED_SEARCH_NEXT_HISTORY, /* M-N */ /* 207 */ ED_SEQUENCE_LEAD_IN, /* M-O */ /* 208 */ ED_SEARCH_PREV_HISTORY, /* M-P */ /* 209 */ ED_UNASSIGNED, /* M-Q */ /* 210 */ ED_UNASSIGNED, /* M-R */ /* 211 */ ED_UNASSIGNED, /* M-S */ /* 212 */ ED_UNASSIGNED, /* M-T */ /* 213 */ EM_UPPER_CASE, /* M-U */ /* 214 */ ED_UNASSIGNED, /* M-V */ /* 215 */ EM_COPY_REGION, /* M-W */ /* 216 */ ED_COMMAND, /* M-X */ /* 217 */ ED_UNASSIGNED, /* M-Y */ /* 218 */ ED_UNASSIGNED, /* M-Z */ /* 219 */ ED_SEQUENCE_LEAD_IN, /* M-[ */ /* 220 */ ED_UNASSIGNED, /* M-\ */ /* 221 */ ED_UNASSIGNED, /* M-] */ /* 222 */ ED_UNASSIGNED, /* M-^ */ /* 223 */ ED_UNASSIGNED, /* M-_ */ /* 223 */ ED_UNASSIGNED, /* M-` */ /* 224 */ ED_UNASSIGNED, /* M-a */ /* 225 */ ED_PREV_WORD, /* M-b */ /* 226 */ EM_CAPITOL_CASE, /* M-c */ /* 227 */ EM_DELETE_NEXT_WORD, /* M-d */ /* 228 */ ED_UNASSIGNED, /* M-e */ /* 229 */ EM_NEXT_WORD, /* M-f */ /* 230 */ ED_UNASSIGNED, /* M-g */ /* 231 */ ED_UNASSIGNED, /* M-h */ /* 232 */ ED_UNASSIGNED, /* M-i */ /* 233 */ ED_UNASSIGNED, /* M-j */ /* 234 */ ED_UNASSIGNED, /* M-k */ /* 235 */ EM_LOWER_CASE, /* M-l */ /* 236 */ ED_UNASSIGNED, /* M-m */ /* 237 */ ED_SEARCH_NEXT_HISTORY, /* M-n */ /* 238 */ ED_UNASSIGNED, /* M-o */ /* 239 */ ED_SEARCH_PREV_HISTORY, /* M-p */ /* 240 */ ED_UNASSIGNED, /* M-q */ /* 241 */ ED_UNASSIGNED, /* M-r */ /* 242 */ ED_UNASSIGNED, /* M-s */ /* 243 */ ED_UNASSIGNED, /* M-t */ /* 244 */ EM_UPPER_CASE, /* M-u */ /* 245 */ ED_UNASSIGNED, /* M-v */ /* 246 */ EM_COPY_REGION, /* M-w */ /* 247 */ ED_COMMAND, /* M-x */ /* 248 */ ED_UNASSIGNED, /* M-y */ /* 249 */ ED_UNASSIGNED, /* M-z */ /* 250 */ ED_UNASSIGNED, /* M-{ */ /* 251 */ ED_UNASSIGNED, /* M-| */ /* 252 */ ED_UNASSIGNED, /* M-} */ /* 253 */ ED_UNASSIGNED, /* M-~ */ /* 254 */ ED_DELETE_PREV_WORD /* M-^? */ /* 255 */ }; /* * keymap table for vi. Each index into above tbl; should be * N_KEYS entries long. Vi mode uses a sticky-extend to do command mode: * insert mode characters are in the normal keymap, and command mode * in the extended keymap. */ private const el_action_t el_map_vi_insert[] = { #ifdef KSHVI /* 0 */ ED_UNASSIGNED, /* ^@ */ /* 1 */ ED_INSERT, /* ^A */ /* 2 */ ED_INSERT, /* ^B */ /* 3 */ ED_INSERT, /* ^C */ /* 4 */ VI_LIST_OR_EOF, /* ^D */ /* 5 */ ED_INSERT, /* ^E */ /* 6 */ ED_INSERT, /* ^F */ /* 7 */ ED_INSERT, /* ^G */ /* 8 */ VI_DELETE_PREV_CHAR, /* ^H */ /* BackSpace key */ /* 9 */ ED_INSERT, /* ^I */ /* Tab Key */ /* 10 */ ED_NEWLINE, /* ^J */ /* 11 */ ED_INSERT, /* ^K */ /* 12 */ ED_INSERT, /* ^L */ /* 13 */ ED_NEWLINE, /* ^M */ /* 14 */ ED_INSERT, /* ^N */ /* 15 */ ED_INSERT, /* ^O */ /* 16 */ ED_INSERT, /* ^P */ /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */ /* 18 */ ED_INSERT, /* ^R */ /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */ /* 20 */ ED_INSERT, /* ^T */ /* 21 */ VI_KILL_LINE_PREV, /* ^U */ /* 22 */ ED_QUOTED_INSERT, /* ^V */ /* 23 */ ED_DELETE_PREV_WORD, /* ^W */ /* ED_DELETE_PREV_WORD: Only until strt edit pos */ /* 24 */ ED_INSERT, /* ^X */ /* 25 */ ED_INSERT, /* ^Y */ /* 26 */ ED_INSERT, /* ^Z */ /* 27 */ VI_COMMAND_MODE, /* ^[ */ /* [ Esc ] key */ /* 28 */ ED_TTY_SIGQUIT, /* ^\ */ /* 29 */ ED_INSERT, /* ^] */ /* 30 */ ED_INSERT, /* ^^ */ /* 31 */ ED_INSERT, /* ^_ */ #else /* !KSHVI */ /* * NOTE: These mappings do NOT Correspond well * to the KSH VI editing assignments. * On the other and they are convenient and * many people have have gotten used to them. */ /* 0 */ ED_UNASSIGNED, /* ^@ */ /* 1 */ ED_MOVE_TO_BEG, /* ^A */ /* 2 */ ED_PREV_CHAR, /* ^B */ /* 3 */ ED_TTY_SIGINT, /* ^C */ /* 4 */ VI_LIST_OR_EOF, /* ^D */ /* 5 */ ED_MOVE_TO_END, /* ^E */ /* 6 */ ED_NEXT_CHAR, /* ^F */ /* 7 */ ED_UNASSIGNED, /* ^G */ /* 8 */ VI_DELETE_PREV_CHAR, /* ^H */ /* BackSpace key */ /* 9 */ ED_UNASSIGNED, /* ^I */ /* Tab Key */ /* 10 */ ED_NEWLINE, /* ^J */ /* 11 */ ED_KILL_LINE, /* ^K */ /* 12 */ ED_CLEAR_SCREEN, /* ^L */ /* 13 */ ED_NEWLINE, /* ^M */ /* 14 */ ED_NEXT_HISTORY, /* ^N */ /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */ /* 16 */ ED_PREV_HISTORY, /* ^P */ /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */ /* 18 */ ED_REDISPLAY, /* ^R */ /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */ /* 20 */ ED_TRANSPOSE_CHARS, /* ^T */ /* 21 */ VI_KILL_LINE_PREV, /* ^U */ /* 22 */ ED_QUOTED_INSERT, /* ^V */ /* 23 */ ED_DELETE_PREV_WORD, /* ^W */ /* 24 */ ED_UNASSIGNED, /* ^X */ /* 25 */ ED_TTY_DSUSP, /* ^Y */ /* 26 */ ED_TTY_SIGTSTP, /* ^Z */ /* 27 */ VI_COMMAND_MODE, /* ^[ */ /* 28 */ ED_TTY_SIGQUIT, /* ^\ */ /* 29 */ ED_UNASSIGNED, /* ^] */ /* 30 */ ED_UNASSIGNED, /* ^^ */ /* 31 */ ED_UNASSIGNED, /* ^_ */ #endif /* KSHVI */ /* 32 */ ED_INSERT, /* SPACE */ /* 33 */ ED_INSERT, /* ! */ /* 34 */ ED_INSERT, /* " */ /* 35 */ ED_INSERT, /* # */ /* 36 */ ED_INSERT, /* $ */ /* 37 */ ED_INSERT, /* % */ /* 38 */ ED_INSERT, /* & */ /* 39 */ ED_INSERT, /* ' */ /* 40 */ ED_INSERT, /* ( */ /* 41 */ ED_INSERT, /* ) */ /* 42 */ ED_INSERT, /* * */ /* 43 */ ED_INSERT, /* + */ /* 44 */ ED_INSERT, /* , */ /* 45 */ ED_INSERT, /* - */ /* 46 */ ED_INSERT, /* . */ /* 47 */ ED_INSERT, /* / */ /* 48 */ ED_INSERT, /* 0 */ /* 49 */ ED_INSERT, /* 1 */ /* 50 */ ED_INSERT, /* 2 */ /* 51 */ ED_INSERT, /* 3 */ /* 52 */ ED_INSERT, /* 4 */ /* 53 */ ED_INSERT, /* 5 */ /* 54 */ ED_INSERT, /* 6 */ /* 55 */ ED_INSERT, /* 7 */ /* 56 */ ED_INSERT, /* 8 */ /* 57 */ ED_INSERT, /* 9 */ /* 58 */ ED_INSERT, /* : */ /* 59 */ ED_INSERT, /* ; */ /* 60 */ ED_INSERT, /* < */ /* 61 */ ED_INSERT, /* = */ /* 62 */ ED_INSERT, /* > */ /* 63 */ ED_INSERT, /* ? */ /* 64 */ ED_INSERT, /* @ */ /* 65 */ ED_INSERT, /* A */ /* 66 */ ED_INSERT, /* B */ /* 67 */ ED_INSERT, /* C */ /* 68 */ ED_INSERT, /* D */ /* 69 */ ED_INSERT, /* E */ /* 70 */ ED_INSERT, /* F */ /* 71 */ ED_INSERT, /* G */ /* 72 */ ED_INSERT, /* H */ /* 73 */ ED_INSERT, /* I */ /* 74 */ ED_INSERT, /* J */ /* 75 */ ED_INSERT, /* K */ /* 76 */ ED_INSERT, /* L */ /* 77 */ ED_INSERT, /* M */ /* 78 */ ED_INSERT, /* N */ /* 79 */ ED_INSERT, /* O */ /* 80 */ ED_INSERT, /* P */ /* 81 */ ED_INSERT, /* Q */ /* 82 */ ED_INSERT, /* R */ /* 83 */ ED_INSERT, /* S */ /* 84 */ ED_INSERT, /* T */ /* 85 */ ED_INSERT, /* U */ /* 86 */ ED_INSERT, /* V */ /* 87 */ ED_INSERT, /* W */ /* 88 */ ED_INSERT, /* X */ /* 89 */ ED_INSERT, /* Y */ /* 90 */ ED_INSERT, /* Z */ /* 91 */ ED_INSERT, /* [ */ /* 92 */ ED_INSERT, /* \ */ /* 93 */ ED_INSERT, /* ] */ /* 94 */ ED_INSERT, /* ^ */ /* 95 */ ED_INSERT, /* _ */ /* 96 */ ED_INSERT, /* ` */ /* 97 */ ED_INSERT, /* a */ /* 98 */ ED_INSERT, /* b */ /* 99 */ ED_INSERT, /* c */ /* 100 */ ED_INSERT, /* d */ /* 101 */ ED_INSERT, /* e */ /* 102 */ ED_INSERT, /* f */ /* 103 */ ED_INSERT, /* g */ /* 104 */ ED_INSERT, /* h */ /* 105 */ ED_INSERT, /* i */ /* 106 */ ED_INSERT, /* j */ /* 107 */ ED_INSERT, /* k */ /* 108 */ ED_INSERT, /* l */ /* 109 */ ED_INSERT, /* m */ /* 110 */ ED_INSERT, /* n */ /* 111 */ ED_INSERT, /* o */ /* 112 */ ED_INSERT, /* p */ /* 113 */ ED_INSERT, /* q */ /* 114 */ ED_INSERT, /* r */ /* 115 */ ED_INSERT, /* s */ /* 116 */ ED_INSERT, /* t */ /* 117 */ ED_INSERT, /* u */ /* 118 */ ED_INSERT, /* v */ /* 119 */ ED_INSERT, /* w */ /* 120 */ ED_INSERT, /* x */ /* 121 */ ED_INSERT, /* y */ /* 122 */ ED_INSERT, /* z */ /* 123 */ ED_INSERT, /* { */ /* 124 */ ED_INSERT, /* | */ /* 125 */ ED_INSERT, /* } */ /* 126 */ ED_INSERT, /* ~ */ /* 127 */ VI_DELETE_PREV_CHAR, /* ^? */ /* 128 */ ED_INSERT, /* M-^@ */ /* 129 */ ED_INSERT, /* M-^A */ /* 130 */ ED_INSERT, /* M-^B */ /* 131 */ ED_INSERT, /* M-^C */ /* 132 */ ED_INSERT, /* M-^D */ /* 133 */ ED_INSERT, /* M-^E */ /* 134 */ ED_INSERT, /* M-^F */ /* 135 */ ED_INSERT, /* M-^G */ /* 136 */ ED_INSERT, /* M-^H */ /* 137 */ ED_INSERT, /* M-^I */ /* 138 */ ED_INSERT, /* M-^J */ /* 139 */ ED_INSERT, /* M-^K */ /* 140 */ ED_INSERT, /* M-^L */ /* 141 */ ED_INSERT, /* M-^M */ /* 142 */ ED_INSERT, /* M-^N */ /* 143 */ ED_INSERT, /* M-^O */ /* 144 */ ED_INSERT, /* M-^P */ /* 145 */ ED_INSERT, /* M-^Q */ /* 146 */ ED_INSERT, /* M-^R */ /* 147 */ ED_INSERT, /* M-^S */ /* 148 */ ED_INSERT, /* M-^T */ /* 149 */ ED_INSERT, /* M-^U */ /* 150 */ ED_INSERT, /* M-^V */ /* 151 */ ED_INSERT, /* M-^W */ /* 152 */ ED_INSERT, /* M-^X */ /* 153 */ ED_INSERT, /* M-^Y */ /* 154 */ ED_INSERT, /* M-^Z */ /* 155 */ ED_INSERT, /* M-^[ */ /* 156 */ ED_INSERT, /* M-^\ */ /* 157 */ ED_INSERT, /* M-^] */ /* 158 */ ED_INSERT, /* M-^^ */ /* 159 */ ED_INSERT, /* M-^_ */ /* 160 */ ED_INSERT, /* M-SPACE */ /* 161 */ ED_INSERT, /* M-! */ /* 162 */ ED_INSERT, /* M-" */ /* 163 */ ED_INSERT, /* M-# */ /* 164 */ ED_INSERT, /* M-$ */ /* 165 */ ED_INSERT, /* M-% */ /* 166 */ ED_INSERT, /* M-& */ /* 167 */ ED_INSERT, /* M-' */ /* 168 */ ED_INSERT, /* M-( */ /* 169 */ ED_INSERT, /* M-) */ /* 170 */ ED_INSERT, /* M-* */ /* 171 */ ED_INSERT, /* M-+ */ /* 172 */ ED_INSERT, /* M-, */ /* 173 */ ED_INSERT, /* M-- */ /* 174 */ ED_INSERT, /* M-. */ /* 175 */ ED_INSERT, /* M-/ */ /* 176 */ ED_INSERT, /* M-0 */ /* 177 */ ED_INSERT, /* M-1 */ /* 178 */ ED_INSERT, /* M-2 */ /* 179 */ ED_INSERT, /* M-3 */ /* 180 */ ED_INSERT, /* M-4 */ /* 181 */ ED_INSERT, /* M-5 */ /* 182 */ ED_INSERT, /* M-6 */ /* 183 */ ED_INSERT, /* M-7 */ /* 184 */ ED_INSERT, /* M-8 */ /* 185 */ ED_INSERT, /* M-9 */ /* 186 */ ED_INSERT, /* M-: */ /* 187 */ ED_INSERT, /* M-; */ /* 188 */ ED_INSERT, /* M-< */ /* 189 */ ED_INSERT, /* M-= */ /* 190 */ ED_INSERT, /* M-> */ /* 191 */ ED_INSERT, /* M-? */ /* 192 */ ED_INSERT, /* M-@ */ /* 193 */ ED_INSERT, /* M-A */ /* 194 */ ED_INSERT, /* M-B */ /* 195 */ ED_INSERT, /* M-C */ /* 196 */ ED_INSERT, /* M-D */ /* 197 */ ED_INSERT, /* M-E */ /* 198 */ ED_INSERT, /* M-F */ /* 199 */ ED_INSERT, /* M-G */ /* 200 */ ED_INSERT, /* M-H */ /* 201 */ ED_INSERT, /* M-I */ /* 202 */ ED_INSERT, /* M-J */ /* 203 */ ED_INSERT, /* M-K */ /* 204 */ ED_INSERT, /* M-L */ /* 205 */ ED_INSERT, /* M-M */ /* 206 */ ED_INSERT, /* M-N */ /* 207 */ ED_INSERT, /* M-O */ /* 208 */ ED_INSERT, /* M-P */ /* 209 */ ED_INSERT, /* M-Q */ /* 210 */ ED_INSERT, /* M-R */ /* 211 */ ED_INSERT, /* M-S */ /* 212 */ ED_INSERT, /* M-T */ /* 213 */ ED_INSERT, /* M-U */ /* 214 */ ED_INSERT, /* M-V */ /* 215 */ ED_INSERT, /* M-W */ /* 216 */ ED_INSERT, /* M-X */ /* 217 */ ED_INSERT, /* M-Y */ /* 218 */ ED_INSERT, /* M-Z */ /* 219 */ ED_INSERT, /* M-[ */ /* 220 */ ED_INSERT, /* M-\ */ /* 221 */ ED_INSERT, /* M-] */ /* 222 */ ED_INSERT, /* M-^ */ /* 223 */ ED_INSERT, /* M-_ */ /* 224 */ ED_INSERT, /* M-` */ /* 225 */ ED_INSERT, /* M-a */ /* 226 */ ED_INSERT, /* M-b */ /* 227 */ ED_INSERT, /* M-c */ /* 228 */ ED_INSERT, /* M-d */ /* 229 */ ED_INSERT, /* M-e */ /* 230 */ ED_INSERT, /* M-f */ /* 231 */ ED_INSERT, /* M-g */ /* 232 */ ED_INSERT, /* M-h */ /* 233 */ ED_INSERT, /* M-i */ /* 234 */ ED_INSERT, /* M-j */ /* 235 */ ED_INSERT, /* M-k */ /* 236 */ ED_INSERT, /* M-l */ /* 237 */ ED_INSERT, /* M-m */ /* 238 */ ED_INSERT, /* M-n */ /* 239 */ ED_INSERT, /* M-o */ /* 240 */ ED_INSERT, /* M-p */ /* 241 */ ED_INSERT, /* M-q */ /* 242 */ ED_INSERT, /* M-r */ /* 243 */ ED_INSERT, /* M-s */ /* 244 */ ED_INSERT, /* M-t */ /* 245 */ ED_INSERT, /* M-u */ /* 246 */ ED_INSERT, /* M-v */ /* 247 */ ED_INSERT, /* M-w */ /* 248 */ ED_INSERT, /* M-x */ /* 249 */ ED_INSERT, /* M-y */ /* 250 */ ED_INSERT, /* M-z */ /* 251 */ ED_INSERT, /* M-{ */ /* 252 */ ED_INSERT, /* M-| */ /* 253 */ ED_INSERT, /* M-} */ /* 254 */ ED_INSERT, /* M-~ */ /* 255 */ ED_INSERT /* M-^? */ }; private const el_action_t el_map_vi_command[] = { /* 0 */ ED_UNASSIGNED, /* ^@ */ /* 1 */ ED_MOVE_TO_BEG, /* ^A */ /* 2 */ ED_UNASSIGNED, /* ^B */ /* 3 */ ED_TTY_SIGINT, /* ^C */ /* 4 */ ED_UNASSIGNED, /* ^D */ /* 5 */ ED_MOVE_TO_END, /* ^E */ /* 6 */ ED_UNASSIGNED, /* ^F */ /* 7 */ ED_UNASSIGNED, /* ^G */ /* 8 */ ED_DELETE_PREV_CHAR, /* ^H */ /* 9 */ ED_UNASSIGNED, /* ^I */ /* 10 */ ED_NEWLINE, /* ^J */ /* 11 */ ED_KILL_LINE, /* ^K */ /* 12 */ ED_CLEAR_SCREEN, /* ^L */ /* 13 */ ED_NEWLINE, /* ^M */ /* 14 */ ED_NEXT_HISTORY, /* ^N */ /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */ /* 16 */ ED_PREV_HISTORY, /* ^P */ /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */ /* 18 */ ED_REDISPLAY, /* ^R */ /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */ /* 20 */ ED_UNASSIGNED, /* ^T */ /* 21 */ VI_KILL_LINE_PREV, /* ^U */ /* 22 */ ED_UNASSIGNED, /* ^V */ /* 23 */ ED_DELETE_PREV_WORD, /* ^W */ /* 24 */ ED_UNASSIGNED, /* ^X */ /* 25 */ ED_UNASSIGNED, /* ^Y */ /* 26 */ ED_UNASSIGNED, /* ^Z */ /* 27 */ EM_META_NEXT, /* ^[ */ /* 28 */ ED_TTY_SIGQUIT, /* ^\ */ /* 29 */ ED_UNASSIGNED, /* ^] */ /* 30 */ ED_UNASSIGNED, /* ^^ */ /* 31 */ ED_UNASSIGNED, /* ^_ */ /* 32 */ ED_NEXT_CHAR, /* SPACE */ /* 33 */ ED_UNASSIGNED, /* ! */ /* 34 */ ED_UNASSIGNED, /* " */ /* 35 */ VI_COMMENT_OUT, /* # */ /* 36 */ ED_MOVE_TO_END, /* $ */ /* 37 */ VI_MATCH, /* % */ /* 38 */ ED_UNASSIGNED, /* & */ /* 39 */ ED_UNASSIGNED, /* ' */ /* 40 */ ED_UNASSIGNED, /* ( */ /* 41 */ ED_UNASSIGNED, /* ) */ /* 42 */ ED_UNASSIGNED, /* * */ /* 43 */ ED_NEXT_HISTORY, /* + */ /* 44 */ VI_REPEAT_PREV_CHAR, /* , */ /* 45 */ ED_PREV_HISTORY, /* - */ /* 46 */ VI_REDO, /* . */ /* 47 */ VI_SEARCH_PREV, /* / */ /* 48 */ VI_ZERO, /* 0 */ /* 49 */ ED_ARGUMENT_DIGIT, /* 1 */ /* 50 */ ED_ARGUMENT_DIGIT, /* 2 */ /* 51 */ ED_ARGUMENT_DIGIT, /* 3 */ /* 52 */ ED_ARGUMENT_DIGIT, /* 4 */ /* 53 */ ED_ARGUMENT_DIGIT, /* 5 */ /* 54 */ ED_ARGUMENT_DIGIT, /* 6 */ /* 55 */ ED_ARGUMENT_DIGIT, /* 7 */ /* 56 */ ED_ARGUMENT_DIGIT, /* 8 */ /* 57 */ ED_ARGUMENT_DIGIT, /* 9 */ /* 58 */ ED_COMMAND, /* : */ /* 59 */ VI_REPEAT_NEXT_CHAR, /* ; */ /* 60 */ ED_UNASSIGNED, /* < */ /* 61 */ ED_UNASSIGNED, /* = */ /* 62 */ ED_UNASSIGNED, /* > */ /* 63 */ VI_SEARCH_NEXT, /* ? */ /* 64 */ VI_ALIAS, /* @ */ /* 65 */ VI_ADD_AT_EOL, /* A */ /* 66 */ VI_PREV_BIG_WORD, /* B */ /* 67 */ VI_CHANGE_TO_EOL, /* C */ /* 68 */ ED_KILL_LINE, /* D */ /* 69 */ VI_END_BIG_WORD, /* E */ /* 70 */ VI_PREV_CHAR, /* F */ /* 71 */ VI_TO_HISTORY_LINE, /* G */ /* 72 */ ED_UNASSIGNED, /* H */ /* 73 */ VI_INSERT_AT_BOL, /* I */ /* 74 */ ED_SEARCH_NEXT_HISTORY, /* J */ /* 75 */ ED_SEARCH_PREV_HISTORY, /* K */ /* 76 */ ED_UNASSIGNED, /* L */ /* 77 */ ED_UNASSIGNED, /* M */ /* 78 */ VI_REPEAT_SEARCH_PREV, /* N */ /* 79 */ ED_SEQUENCE_LEAD_IN, /* O */ /* 80 */ VI_PASTE_PREV, /* P */ /* 81 */ ED_UNASSIGNED, /* Q */ /* 82 */ VI_REPLACE_MODE, /* R */ /* 83 */ VI_SUBSTITUTE_LINE, /* S */ /* 84 */ VI_TO_PREV_CHAR, /* T */ /* 85 */ VI_UNDO_LINE, /* U */ /* 86 */ ED_UNASSIGNED, /* V */ /* 87 */ VI_NEXT_BIG_WORD, /* W */ /* 88 */ ED_DELETE_PREV_CHAR, /* X */ /* 89 */ VI_YANK_END, /* Y */ /* 90 */ ED_UNASSIGNED, /* Z */ /* 91 */ ED_SEQUENCE_LEAD_IN, /* [ */ /* 92 */ ED_UNASSIGNED, /* \ */ /* 93 */ ED_UNASSIGNED, /* ] */ /* 94 */ ED_MOVE_TO_BEG, /* ^ */ /* 95 */ VI_HISTORY_WORD, /* _ */ /* 96 */ ED_UNASSIGNED, /* ` */ /* 97 */ VI_ADD, /* a */ /* 98 */ VI_PREV_WORD, /* b */ /* 99 */ VI_CHANGE_META, /* c */ /* 100 */ VI_DELETE_META, /* d */ /* 101 */ VI_END_WORD, /* e */ /* 102 */ VI_NEXT_CHAR, /* f */ /* 103 */ ED_UNASSIGNED, /* g */ /* 104 */ ED_PREV_CHAR, /* h */ /* 105 */ VI_INSERT, /* i */ /* 106 */ ED_NEXT_HISTORY, /* j */ /* 107 */ ED_PREV_HISTORY, /* k */ /* 108 */ ED_NEXT_CHAR, /* l */ /* 109 */ ED_UNASSIGNED, /* m */ /* 110 */ VI_REPEAT_SEARCH_NEXT, /* n */ /* 111 */ ED_UNASSIGNED, /* o */ /* 112 */ VI_PASTE_NEXT, /* p */ /* 113 */ ED_UNASSIGNED, /* q */ /* 114 */ VI_REPLACE_CHAR, /* r */ /* 115 */ VI_SUBSTITUTE_CHAR, /* s */ /* 116 */ VI_TO_NEXT_CHAR, /* t */ /* 117 */ VI_UNDO, /* u */ /* 118 */ VI_HISTEDIT, /* v */ /* 119 */ VI_NEXT_WORD, /* w */ /* 120 */ ED_DELETE_NEXT_CHAR, /* x */ /* 121 */ VI_YANK, /* y */ /* 122 */ ED_UNASSIGNED, /* z */ /* 123 */ ED_UNASSIGNED, /* { */ /* 124 */ VI_TO_COLUMN, /* | */ /* 125 */ ED_UNASSIGNED, /* } */ /* 126 */ VI_CHANGE_CASE, /* ~ */ /* 127 */ ED_DELETE_PREV_CHAR, /* ^? */ /* 128 */ ED_UNASSIGNED, /* M-^@ */ /* 129 */ ED_UNASSIGNED, /* M-^A */ /* 130 */ ED_UNASSIGNED, /* M-^B */ /* 131 */ ED_UNASSIGNED, /* M-^C */ /* 132 */ ED_UNASSIGNED, /* M-^D */ /* 133 */ ED_UNASSIGNED, /* M-^E */ /* 134 */ ED_UNASSIGNED, /* M-^F */ /* 135 */ ED_UNASSIGNED, /* M-^G */ /* 136 */ ED_UNASSIGNED, /* M-^H */ /* 137 */ ED_UNASSIGNED, /* M-^I */ /* 138 */ ED_UNASSIGNED, /* M-^J */ /* 139 */ ED_UNASSIGNED, /* M-^K */ /* 140 */ ED_UNASSIGNED, /* M-^L */ /* 141 */ ED_UNASSIGNED, /* M-^M */ /* 142 */ ED_UNASSIGNED, /* M-^N */ /* 143 */ ED_UNASSIGNED, /* M-^O */ /* 144 */ ED_UNASSIGNED, /* M-^P */ /* 145 */ ED_UNASSIGNED, /* M-^Q */ /* 146 */ ED_UNASSIGNED, /* M-^R */ /* 147 */ ED_UNASSIGNED, /* M-^S */ /* 148 */ ED_UNASSIGNED, /* M-^T */ /* 149 */ ED_UNASSIGNED, /* M-^U */ /* 150 */ ED_UNASSIGNED, /* M-^V */ /* 151 */ ED_UNASSIGNED, /* M-^W */ /* 152 */ ED_UNASSIGNED, /* M-^X */ /* 153 */ ED_UNASSIGNED, /* M-^Y */ /* 154 */ ED_UNASSIGNED, /* M-^Z */ /* 155 */ ED_UNASSIGNED, /* M-^[ */ /* 156 */ ED_UNASSIGNED, /* M-^\ */ /* 157 */ ED_UNASSIGNED, /* M-^] */ /* 158 */ ED_UNASSIGNED, /* M-^^ */ /* 159 */ ED_UNASSIGNED, /* M-^_ */ /* 160 */ ED_UNASSIGNED, /* M-SPACE */ /* 161 */ ED_UNASSIGNED, /* M-! */ /* 162 */ ED_UNASSIGNED, /* M-" */ /* 163 */ ED_UNASSIGNED, /* M-# */ /* 164 */ ED_UNASSIGNED, /* M-$ */ /* 165 */ ED_UNASSIGNED, /* M-% */ /* 166 */ ED_UNASSIGNED, /* M-& */ /* 167 */ ED_UNASSIGNED, /* M-' */ /* 168 */ ED_UNASSIGNED, /* M-( */ /* 169 */ ED_UNASSIGNED, /* M-) */ /* 170 */ ED_UNASSIGNED, /* M-* */ /* 171 */ ED_UNASSIGNED, /* M-+ */ /* 172 */ ED_UNASSIGNED, /* M-, */ /* 173 */ ED_UNASSIGNED, /* M-- */ /* 174 */ ED_UNASSIGNED, /* M-. */ /* 175 */ ED_UNASSIGNED, /* M-/ */ /* 176 */ ED_UNASSIGNED, /* M-0 */ /* 177 */ ED_UNASSIGNED, /* M-1 */ /* 178 */ ED_UNASSIGNED, /* M-2 */ /* 179 */ ED_UNASSIGNED, /* M-3 */ /* 180 */ ED_UNASSIGNED, /* M-4 */ /* 181 */ ED_UNASSIGNED, /* M-5 */ /* 182 */ ED_UNASSIGNED, /* M-6 */ /* 183 */ ED_UNASSIGNED, /* M-7 */ /* 184 */ ED_UNASSIGNED, /* M-8 */ /* 185 */ ED_UNASSIGNED, /* M-9 */ /* 186 */ ED_UNASSIGNED, /* M-: */ /* 187 */ ED_UNASSIGNED, /* M-; */ /* 188 */ ED_UNASSIGNED, /* M-< */ /* 189 */ ED_UNASSIGNED, /* M-= */ /* 190 */ ED_UNASSIGNED, /* M-> */ /* 191 */ ED_UNASSIGNED, /* M-? */ /* 192 */ ED_UNASSIGNED, /* M-@ */ /* 193 */ ED_UNASSIGNED, /* M-A */ /* 194 */ ED_UNASSIGNED, /* M-B */ /* 195 */ ED_UNASSIGNED, /* M-C */ /* 196 */ ED_UNASSIGNED, /* M-D */ /* 197 */ ED_UNASSIGNED, /* M-E */ /* 198 */ ED_UNASSIGNED, /* M-F */ /* 199 */ ED_UNASSIGNED, /* M-G */ /* 200 */ ED_UNASSIGNED, /* M-H */ /* 201 */ ED_UNASSIGNED, /* M-I */ /* 202 */ ED_UNASSIGNED, /* M-J */ /* 203 */ ED_UNASSIGNED, /* M-K */ /* 204 */ ED_UNASSIGNED, /* M-L */ /* 205 */ ED_UNASSIGNED, /* M-M */ /* 206 */ ED_UNASSIGNED, /* M-N */ /* 207 */ ED_SEQUENCE_LEAD_IN, /* M-O */ /* 208 */ ED_UNASSIGNED, /* M-P */ /* 209 */ ED_UNASSIGNED, /* M-Q */ /* 210 */ ED_UNASSIGNED, /* M-R */ /* 211 */ ED_UNASSIGNED, /* M-S */ /* 212 */ ED_UNASSIGNED, /* M-T */ /* 213 */ ED_UNASSIGNED, /* M-U */ /* 214 */ ED_UNASSIGNED, /* M-V */ /* 215 */ ED_UNASSIGNED, /* M-W */ /* 216 */ ED_UNASSIGNED, /* M-X */ /* 217 */ ED_UNASSIGNED, /* M-Y */ /* 218 */ ED_UNASSIGNED, /* M-Z */ /* 219 */ ED_SEQUENCE_LEAD_IN, /* M-[ */ /* 220 */ ED_UNASSIGNED, /* M-\ */ /* 221 */ ED_UNASSIGNED, /* M-] */ /* 222 */ ED_UNASSIGNED, /* M-^ */ /* 223 */ ED_UNASSIGNED, /* M-_ */ /* 224 */ ED_UNASSIGNED, /* M-` */ /* 225 */ ED_UNASSIGNED, /* M-a */ /* 226 */ ED_UNASSIGNED, /* M-b */ /* 227 */ ED_UNASSIGNED, /* M-c */ /* 228 */ ED_UNASSIGNED, /* M-d */ /* 229 */ ED_UNASSIGNED, /* M-e */ /* 230 */ ED_UNASSIGNED, /* M-f */ /* 231 */ ED_UNASSIGNED, /* M-g */ /* 232 */ ED_UNASSIGNED, /* M-h */ /* 233 */ ED_UNASSIGNED, /* M-i */ /* 234 */ ED_UNASSIGNED, /* M-j */ /* 235 */ ED_UNASSIGNED, /* M-k */ /* 236 */ ED_UNASSIGNED, /* M-l */ /* 237 */ ED_UNASSIGNED, /* M-m */ /* 238 */ ED_UNASSIGNED, /* M-n */ /* 239 */ ED_UNASSIGNED, /* M-o */ /* 240 */ ED_UNASSIGNED, /* M-p */ /* 241 */ ED_UNASSIGNED, /* M-q */ /* 242 */ ED_UNASSIGNED, /* M-r */ /* 243 */ ED_UNASSIGNED, /* M-s */ /* 244 */ ED_UNASSIGNED, /* M-t */ /* 245 */ ED_UNASSIGNED, /* M-u */ /* 246 */ ED_UNASSIGNED, /* M-v */ /* 247 */ ED_UNASSIGNED, /* M-w */ /* 248 */ ED_UNASSIGNED, /* M-x */ /* 249 */ ED_UNASSIGNED, /* M-y */ /* 250 */ ED_UNASSIGNED, /* M-z */ /* 251 */ ED_UNASSIGNED, /* M-{ */ /* 252 */ ED_UNASSIGNED, /* M-| */ /* 253 */ ED_UNASSIGNED, /* M-} */ /* 254 */ ED_UNASSIGNED, /* M-~ */ /* 255 */ ED_UNASSIGNED /* M-^? */ }; /* map_init(): * Initialize and allocate the maps */ protected int map_init(EditLine *el) { /* * Make sure those are correct before starting. */ #ifdef MAP_DEBUG if (sizeof(el_map_emacs) != N_KEYS * sizeof(el_action_t)) EL_ABORT((el->errfile, "Emacs map incorrect\n")); if (sizeof(el_map_vi_command) != N_KEYS * sizeof(el_action_t)) EL_ABORT((el->errfile, "Vi command map incorrect\n")); if (sizeof(el_map_vi_insert) != N_KEYS * sizeof(el_action_t)) EL_ABORT((el->errfile, "Vi insert map incorrect\n")); #endif el->el_map.alt = (el_action_t *)el_malloc(sizeof(el_action_t) * N_KEYS); if (el->el_map.alt == NULL) return (-1); el->el_map.key = (el_action_t *)el_malloc(sizeof(el_action_t) * N_KEYS); if (el->el_map.key == NULL) return (-1); el->el_map.emacs = el_map_emacs; el->el_map.vic = el_map_vi_command; el->el_map.vii = el_map_vi_insert; el->el_map.help = (el_bindings_t *) el_malloc(sizeof(el_bindings_t) * EL_NUM_FCNS); if (el->el_map.help == NULL) return (-1); (void) memcpy(el->el_map.help, help__get(), sizeof(el_bindings_t) * EL_NUM_FCNS); el->el_map.func = (el_func_t *)el_malloc(sizeof(el_func_t) * EL_NUM_FCNS); if (el->el_map.func == NULL) return (-1); memcpy(el->el_map.func, func__get(), sizeof(el_func_t) * EL_NUM_FCNS); el->el_map.nfunc = EL_NUM_FCNS; #ifdef VIDEFAULT map_init_vi(el); #else map_init_emacs(el); #endif /* VIDEFAULT */ return (0); } /* map_end(): * Free the space taken by the editor maps */ protected void map_end(EditLine *el) { el_free((ptr_t) el->el_map.alt); el->el_map.alt = NULL; el_free((ptr_t) el->el_map.key); el->el_map.key = NULL; el->el_map.emacs = NULL; el->el_map.vic = NULL; el->el_map.vii = NULL; el_free((ptr_t) el->el_map.help); el->el_map.help = NULL; el_free((ptr_t) el->el_map.func); el->el_map.func = NULL; } /* map_init_nls(): * Find all the printable keys and bind them to self insert */ private void map_init_nls(EditLine *el) { int i; el_action_t *map = el->el_map.key; for (i = 0200; i <= 0377; i++) if (isprint(i)) map[i] = ED_INSERT; } /* map_init_meta(): * Bind all the meta keys to the appropriate ESC- sequence */ private void map_init_meta(EditLine *el) { char buf[3]; int i; el_action_t *map = el->el_map.key; el_action_t *alt = el->el_map.alt; for (i = 0; i <= 0377 && map[i] != EM_META_NEXT; i++) continue; if (i > 0377) { for (i = 0; i <= 0377 && alt[i] != EM_META_NEXT; i++) continue; if (i > 0377) { i = 033; if (el->el_map.type == MAP_VI) map = alt; } else map = alt; } buf[0] = (char) i; buf[2] = 0; for (i = 0200; i <= 0377; i++) switch (map[i]) { case ED_INSERT: case ED_UNASSIGNED: case ED_SEQUENCE_LEAD_IN: break; default: buf[1] = i & 0177; key_add(el, buf, key_map_cmd(el, (int) map[i]), XK_CMD); break; } map[(int) buf[0]] = ED_SEQUENCE_LEAD_IN; } /* map_init_vi(): * Initialize the vi bindings */ protected void map_init_vi(EditLine *el) { int i; el_action_t *key = el->el_map.key; el_action_t *alt = el->el_map.alt; const el_action_t *vii = el->el_map.vii; const el_action_t *vic = el->el_map.vic; el->el_map.type = MAP_VI; el->el_map.current = el->el_map.key; key_reset(el); for (i = 0; i < N_KEYS; i++) { key[i] = vii[i]; alt[i] = vic[i]; } map_init_meta(el); map_init_nls(el); tty_bind_char(el, 1); term_bind_arrow(el); } /* map_init_emacs(): * Initialize the emacs bindings */ protected void map_init_emacs(EditLine *el) { int i; char buf[3]; el_action_t *key = el->el_map.key; el_action_t *alt = el->el_map.alt; const el_action_t *emacs = el->el_map.emacs; el->el_map.type = MAP_EMACS; el->el_map.current = el->el_map.key; key_reset(el); for (i = 0; i < N_KEYS; i++) { key[i] = emacs[i]; alt[i] = ED_UNASSIGNED; } map_init_meta(el); map_init_nls(el); buf[0] = CONTROL('X'); buf[1] = CONTROL('X'); buf[2] = 0; key_add(el, buf, key_map_cmd(el, EM_EXCHANGE_MARK), XK_CMD); tty_bind_char(el, 1); term_bind_arrow(el); } /* map_set_editor(): * Set the editor */ protected int map_set_editor(EditLine *el, char *editor) { if (strcmp(editor, "emacs") == 0) { map_init_emacs(el); return (0); } if (strcmp(editor, "vi") == 0) { map_init_vi(el); return (0); } return (-1); } /* map_get_editor(): * Retrieve the editor */ protected int map_get_editor(EditLine *el, const char **editor) { if (editor == NULL) return (-1); switch (el->el_map.type) { case MAP_EMACS: *editor = "emacs"; return (0); case MAP_VI: *editor = "vi"; return (0); } return (-1); } /* map_print_key(): * Print the function description for 1 key */ private void map_print_key(EditLine *el, el_action_t *map, const char *in) { char outbuf[EL_BUFSIZ]; el_bindings_t *bp, *ep; if (in[0] == '\0' || in[1] == '\0') { (void) key__decode_str(in, outbuf, sizeof(outbuf), ""); ep = &el->el_map.help[el->el_map.nfunc]; for (bp = el->el_map.help; bp < ep; bp++) if (bp->func == map[(unsigned char) *in]) { (void) fprintf(el->el_outfile, "%s\t->\t%s\n", outbuf, bp->name); return; } } else key_print(el, in); } /* map_print_some_keys(): * Print keys from first to last */ private void map_print_some_keys(EditLine *el, el_action_t *map, int first, int last) { el_bindings_t *bp, *ep; char firstbuf[2], lastbuf[2]; char unparsbuf[EL_BUFSIZ], extrabuf[EL_BUFSIZ]; firstbuf[0] = first; firstbuf[1] = 0; lastbuf[0] = last; lastbuf[1] = 0; if (map[first] == ED_UNASSIGNED) { if (first == last) { (void) key__decode_str(firstbuf, unparsbuf, sizeof(unparsbuf), STRQQ); (void) fprintf(el->el_outfile, "%-15s-> is undefined\n", unparsbuf); } return; } ep = &el->el_map.help[el->el_map.nfunc]; for (bp = el->el_map.help; bp < ep; bp++) { if (bp->func == map[first]) { if (first == last) { (void) key__decode_str(firstbuf, unparsbuf, sizeof(unparsbuf), STRQQ); (void) fprintf(el->el_outfile, "%-15s-> %s\n", unparsbuf, bp->name); } else { (void) key__decode_str(firstbuf, unparsbuf, sizeof(unparsbuf), STRQQ); (void) key__decode_str(lastbuf, extrabuf, sizeof(extrabuf), STRQQ); (void) fprintf(el->el_outfile, "%-4s to %-7s-> %s\n", unparsbuf, extrabuf, bp->name); } return; } } #ifdef MAP_DEBUG if (map == el->el_map.key) { (void) key__decode_str(firstbuf, unparsbuf, sizeof(unparsbuf), STRQQ); (void) fprintf(el->el_outfile, "BUG!!! %s isn't bound to anything.\n", unparsbuf); (void) fprintf(el->el_outfile, "el->el_map.key[%d] == %d\n", first, el->el_map.key[first]); } else { (void) key__decode_str(firstbuf, unparsbuf, sizeof(unparsbuf), STRQQ); (void) fprintf(el->el_outfile, "BUG!!! %s isn't bound to anything.\n", unparsbuf); (void) fprintf(el->el_outfile, "el->el_map.alt[%d] == %d\n", first, el->el_map.alt[first]); } #endif EL_ABORT((el->el_errfile, "Error printing keys\n")); } /* map_print_all_keys(): * Print the function description for all keys. */ private void map_print_all_keys(EditLine *el) { int prev, i; (void) fprintf(el->el_outfile, "Standard key bindings\n"); prev = 0; for (i = 0; i < N_KEYS; i++) { if (el->el_map.key[prev] == el->el_map.key[i]) continue; map_print_some_keys(el, el->el_map.key, prev, i - 1); prev = i; } map_print_some_keys(el, el->el_map.key, prev, i - 1); (void) fprintf(el->el_outfile, "Alternative key bindings\n"); prev = 0; for (i = 0; i < N_KEYS; i++) { if (el->el_map.alt[prev] == el->el_map.alt[i]) continue; map_print_some_keys(el, el->el_map.alt, prev, i - 1); prev = i; } map_print_some_keys(el, el->el_map.alt, prev, i - 1); (void) fprintf(el->el_outfile, "Multi-character bindings\n"); key_print(el, ""); (void) fprintf(el->el_outfile, "Arrow key bindings\n"); term_print_arrow(el, ""); } /* map_bind(): * Add/remove/change bindings */ protected int map_bind(EditLine *el, int argc, const char **argv) { el_action_t *map; int ntype, rem; const char *p; char inbuf[EL_BUFSIZ]; char outbuf[EL_BUFSIZ]; const char *in = NULL; char *out = NULL; el_bindings_t *bp, *ep; int cmd; int key; if (argv == NULL) return (-1); map = el->el_map.key; ntype = XK_CMD; key = rem = 0; for (argc = 1; (p = argv[argc]) != NULL; argc++) if (p[0] == '-') switch (p[1]) { case 'a': map = el->el_map.alt; break; case 's': ntype = XK_STR; break; #ifdef notyet case 'c': ntype = XK_EXE; break; #endif case 'k': key = 1; break; case 'r': rem = 1; break; case 'v': map_init_vi(el); return (0); case 'e': map_init_emacs(el); return (0); case 'l': ep = &el->el_map.help[el->el_map.nfunc]; for (bp = el->el_map.help; bp < ep; bp++) (void) fprintf(el->el_outfile, "%s\n\t%s\n", bp->name, bp->description); return (0); default: (void) fprintf(el->el_errfile, "%s: Invalid switch `%c'.\n", argv[0], p[1]); } else break; if (argv[argc] == NULL) { map_print_all_keys(el); return (0); } if (key) in = argv[argc++]; else if ((in = parse__string(inbuf, argv[argc++])) == NULL) { (void) fprintf(el->el_errfile, "%s: Invalid \\ or ^ in instring.\n", argv[0]); return (-1); } if (rem) { if (key) { (void) term_clear_arrow(el, in); return (-1); } if (in[1]) (void) key_delete(el, in); else if (map[(unsigned char) *in] == ED_SEQUENCE_LEAD_IN) (void) key_delete(el, in); else map[(unsigned char) *in] = ED_UNASSIGNED; return (0); } if (argv[argc] == NULL) { if (key) term_print_arrow(el, in); else map_print_key(el, map, in); return (0); } #ifdef notyet if (argv[argc + 1] != NULL) { bindkey_usage(); return (-1); } #endif switch (ntype) { case XK_STR: case XK_EXE: if ((out = parse__string(outbuf, argv[argc])) == NULL) { (void) fprintf(el->el_errfile, "%s: Invalid \\ or ^ in outstring.\n", argv[0]); return (-1); } if (key) term_set_arrow(el, in, key_map_str(el, out), ntype); else key_add(el, in, key_map_str(el, out), ntype); map[(unsigned char) *in] = ED_SEQUENCE_LEAD_IN; break; case XK_CMD: if ((cmd = parse_cmd(el, argv[argc])) == -1) { (void) fprintf(el->el_errfile, "%s: Invalid command `%s'.\n", argv[0], argv[argc]); return (-1); } if (key) term_set_arrow(el, in, key_map_str(el, out), ntype); else { if (in[1]) { key_add(el, in, key_map_cmd(el, cmd), ntype); map[(unsigned char) *in] = ED_SEQUENCE_LEAD_IN; } else { key_clear(el, map, in); map[(unsigned char) *in] = cmd; } } break; default: EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype)); break; } return (0); } /* map_addfunc(): * add a user defined function */ protected int map_addfunc(EditLine *el, const char *name, const char *help, el_func_t func) { void *p; int nf = el->el_map.nfunc + 1; if (name == NULL || help == NULL || func == NULL) return (-1); if ((p = el_realloc(el->el_map.func, nf * sizeof(el_func_t))) == NULL) return (-1); el->el_map.func = (el_func_t *) p; if ((p = el_realloc(el->el_map.help, nf * sizeof(el_bindings_t))) == NULL) return (-1); el->el_map.help = (el_bindings_t *) p; nf = el->el_map.nfunc; el->el_map.func[nf] = func; el->el_map.help[nf].name = name; el->el_map.help[nf].func = nf; el->el_map.help[nf].description = help; el->el_map.nfunc++; return (0); } clt/falcon/editline/src/map.h000066400000000000000000000061241176363201700164250ustar00rootroot00000000000000/* $NetBSD: map.h,v 1.8 2003/08/07 16:44:32 agc Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)map.h 8.1 (Berkeley) 6/4/93 */ /* * el.map.h: Editor maps */ #ifndef _h_el_map #define _h_el_map typedef struct el_bindings_t { /* for the "bind" shell command */ const char *name; /* function name for bind command */ int func; /* function numeric value */ const char *description; /* description of function */ } el_bindings_t; typedef struct el_map_t { el_action_t *alt; /* The current alternate key map */ el_action_t *key; /* The current normal key map */ el_action_t *current; /* The keymap we are using */ const el_action_t *emacs; /* The default emacs key map */ const el_action_t *vic; /* The vi command mode key map */ const el_action_t *vii; /* The vi insert mode key map */ int type; /* Emacs or vi */ el_bindings_t *help; /* The help for the editor functions */ el_func_t *func; /* List of available functions */ int nfunc; /* The number of functions/help items */ } el_map_t; #define MAP_EMACS 0 #define MAP_VI 1 protected int map_bind(EditLine *, int, const char **); protected int map_init(EditLine *); protected void map_end(EditLine *); protected void map_init_vi(EditLine *); protected void map_init_emacs(EditLine *); protected int map_set_editor(EditLine *, char *); protected int map_get_editor(EditLine *, const char **); protected int map_addfunc(EditLine *, const char *, const char *, el_func_t); #endif /* _h_el_map */ clt/falcon/editline/src/parse.c000066400000000000000000000126561176363201700167640ustar00rootroot00000000000000/* $NetBSD: parse.c,v 1.22 2005/05/29 04:58:15 lukem Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: parse.c,v 1.22 2005/05/29 04:58:15 lukem Exp $"); #endif #endif /* not lint && not SCCSID */ /* * parse.c: parse an editline extended command * * commands are: * * bind * echotc * edit * gettc * history * settc * setty */ #include "el.h" #include private const struct { const char *name; int (*func)(EditLine *, int, const char **); } cmds[] = { { "bind", map_bind }, { "echotc", term_echotc }, { "edit", el_editmode }, { "history", hist_command }, { "telltc", term_telltc }, { "settc", term_settc }, { "setty", tty_stty }, { NULL, NULL } }; /* parse_line(): * Parse a line and dispatch it */ protected int parse_line(EditLine *el, const char *line) { const char **argv; int argc; Tokenizer *tok; tok = tok_init(NULL); tok_str(tok, line, &argc, &argv); argc = el_parse(el, argc, argv); tok_end(tok); return (argc); } /* el_parse(): * Command dispatcher */ public int el_parse(EditLine *el, int argc, const char *argv[]) { const char *ptr; int i; if (argc < 1) return (-1); ptr = strchr(argv[0], ':'); if (ptr != NULL) { char *tprog; size_t l; if (ptr == argv[0]) return (0); l = ptr - argv[0] - 1; tprog = (char *) el_malloc(l + 1); if (tprog == NULL) return (0); (void) strncpy(tprog, argv[0], l); tprog[l] = '\0'; ptr++; l = el_match(el->el_prog, tprog); el_free(tprog); if (!l) return (0); } else ptr = argv[0]; for (i = 0; cmds[i].name != NULL; i++) if (strcmp(cmds[i].name, ptr) == 0) { i = (*cmds[i].func) (el, argc, argv); return (-i); } return (-1); } /* parse__escape(): * Parse a string of the form ^ \ \ and return * the appropriate character or -1 if the escape is not valid */ protected int parse__escape(const char **ptr) { const char *p; int c; p = *ptr; if (p[1] == 0) return (-1); if (*p == '\\') { p++; switch (*p) { case 'a': c = '\007'; /* Bell */ break; case 'b': c = '\010'; /* Backspace */ break; case 't': c = '\011'; /* Horizontal Tab */ break; case 'n': c = '\012'; /* New Line */ break; case 'v': c = '\013'; /* Vertical Tab */ break; case 'f': c = '\014'; /* Form Feed */ break; case 'r': c = '\015'; /* Carriage Return */ break; case 'e': c = '\033'; /* Escape */ break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { int cnt, ch; for (cnt = 0, c = 0; cnt < 3; cnt++) { ch = *p++; if (ch < '0' || ch > '7') { p--; break; } c = (c << 3) | (ch - '0'); } if ((c & 0xffffff00) != 0) return (-1); --p; break; } default: c = *p; break; } } else if (*p == '^') { p++; c = (*p == '?') ? '\177' : (*p & 0237); } else c = *p; *ptr = ++p; return (c); } /* parse__string(): * Parse the escapes from in and put the raw string out */ protected char * parse__string(char *out, const char *in) { char *rv = out; int n; for (;;) switch (*in) { case '\0': *out = '\0'; return (rv); case '\\': case '^': if ((n = parse__escape(&in)) == -1) return (NULL); *out++ = n; break; case 'M': if (in[1] == '-' && in[2] != '\0') { *out++ = '\033'; in += 2; break; } /*FALLTHROUGH*/ default: *out++ = *in++; break; } } /* parse_cmd(): * Return the command number for the command string given * or -1 if one is not found */ protected int parse_cmd(EditLine *el, const char *cmd) { el_bindings_t *b; for (b = el->el_map.help; b->name != NULL; b++) if (strcmp(b->name, cmd) == 0) return (b->func); return (-1); } clt/falcon/editline/src/parse.h000066400000000000000000000041051176363201700167570ustar00rootroot00000000000000/* $NetBSD: parse.h,v 1.6 2005/05/29 04:58:15 lukem Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)parse.h 8.1 (Berkeley) 6/4/93 */ /* * el.parse.h: Parser functions */ #ifndef _h_el_parse #define _h_el_parse protected int parse_line(EditLine *, const char *); protected int parse__escape(const char **); protected char *parse__string(char *, const char *); protected int parse_cmd(EditLine *, const char *); #endif /* _h_el_parse */ clt/falcon/editline/src/prompt.c000066400000000000000000000110221176363201700171550ustar00rootroot00000000000000/* $NetBSD: prompt.c,v 1.16 2009/07/17 12:26:26 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)prompt.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: prompt.c,v 1.16 2009/07/17 12:26:26 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * prompt.c: Prompt printing functions */ #include #include "el.h" private char *prompt_default(EditLine *); private char *prompt_default_r(EditLine *); /* prompt_default(): * Just a default prompt, in case the user did not provide one */ private char * /*ARGSUSED*/ prompt_default(EditLine *el __attribute__((__unused__))) { static char a[3] = {'?', ' ', '\0'}; return (a); } /* prompt_default_r(): * Just a default rprompt, in case the user did not provide one */ private char * /*ARGSUSED*/ prompt_default_r(EditLine *el __attribute__((__unused__))) { static char a[1] = {'\0'}; return (a); } /* prompt_print(): * Print the prompt and update the prompt position. * We use an array of integers in case we want to pass * literal escape sequences in the prompt and we want a * bit to flag them */ protected void prompt_print(EditLine *el, int op) { el_prompt_t *elp; char *p; int ignore = 0; if (op == EL_PROMPT) elp = &el->el_prompt; else elp = &el->el_rprompt; for (p = (*elp->p_func)(el); *p; p++) { if (elp->p_ignore == *p) { ignore = !ignore; continue; } if (ignore) term__putc(el, *p); else re_putc(el, *p, 1); } elp->p_pos.v = el->el_refresh.r_cursor.v; elp->p_pos.h = el->el_refresh.r_cursor.h; } /* prompt_init(): * Initialize the prompt stuff */ protected int prompt_init(EditLine *el) { el->el_prompt.p_func = prompt_default; el->el_prompt.p_pos.v = 0; el->el_prompt.p_pos.h = 0; el->el_prompt.p_ignore = '\0'; el->el_rprompt.p_func = prompt_default_r; el->el_rprompt.p_pos.v = 0; el->el_rprompt.p_pos.h = 0; el->el_rprompt.p_ignore = '\0'; return 0; } /* prompt_end(): * Clean up the prompt stuff */ protected void /*ARGSUSED*/ prompt_end(EditLine *el __attribute__((__unused__))) { } /* prompt_set(): * Install a prompt printing function */ protected int prompt_set(EditLine *el, el_pfunc_t prf, char c, int op) { el_prompt_t *p; if (op == EL_PROMPT || op == EL_PROMPT_ESC) p = &el->el_prompt; else p = &el->el_rprompt; if (prf == NULL) { if (op == EL_PROMPT || op == EL_PROMPT_ESC) p->p_func = prompt_default; else p->p_func = prompt_default_r; } else p->p_func = prf; p->p_ignore = c; p->p_pos.v = 0; p->p_pos.h = 0; return 0; } /* prompt_get(): * Retrieve the prompt printing function */ protected int prompt_get(EditLine *el, el_pfunc_t *prf, char *c, int op) { el_prompt_t *p; if (prf == NULL) return -1; if (op == EL_PROMPT) p = &el->el_prompt; else p = &el->el_rprompt; *prf = el->el_rprompt.p_func; if (c) *c = p->p_ignore; return 0; } clt/falcon/editline/src/prompt.h000066400000000000000000000046311176363201700171720ustar00rootroot00000000000000/* $NetBSD: prompt.h,v 1.9 2009/03/31 17:38:27 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)prompt.h 8.1 (Berkeley) 6/4/93 */ /* * el.prompt.h: Prompt printing stuff */ #ifndef _h_el_prompt #define _h_el_prompt #include "histedit.h" typedef char * (*el_pfunc_t)(EditLine*); typedef struct el_prompt_t { el_pfunc_t p_func; /* Function to return the prompt */ coord_t p_pos; /* position in the line after prompt */ char p_ignore; /* character to start/end literal */ } el_prompt_t; protected void prompt_print(EditLine *, int); protected int prompt_set(EditLine *, el_pfunc_t, char, int); protected int prompt_get(EditLine *, el_pfunc_t *, char *, int); protected int prompt_init(EditLine *); protected void prompt_end(EditLine *); #endif /* _h_el_prompt */ clt/falcon/editline/src/read.c000066400000000000000000000353441176363201700165640ustar00rootroot00000000000000/* $NetBSD: read.c,v 1.52 2009/07/22 15:57:00 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)read.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: read.c,v 1.52 2009/07/22 15:57:00 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * read.c: Clean this junk up! This is horrible code. * Terminal read functions */ #include #include #include #include #include "el.h" #define OKCMD -1 /* must be -1! */ private int read__fixio(int, int); private int read_preread(EditLine *); private int read_char(EditLine *, char *); private int read_getcmd(EditLine *, el_action_t *, char *); private void read_pop(c_macro_t *); /* read_init(): * Initialize the read stuff */ protected int read_init(EditLine *el) { /* builtin read_char */ el->el_read.read_char = read_char; return 0; } /* el_read_setfn(): * Set the read char function to the one provided. * If it is set to EL_BUILTIN_GETCFN, then reset to the builtin one. */ protected int el_read_setfn(EditLine *el, el_rfunc_t rc) { el->el_read.read_char = (rc == EL_BUILTIN_GETCFN) ? read_char : rc; return 0; } /* el_read_getfn(): * return the current read char function, or EL_BUILTIN_GETCFN * if it is the default one */ protected el_rfunc_t el_read_getfn(EditLine *el) { return (el->el_read.read_char == read_char) ? EL_BUILTIN_GETCFN : el->el_read.read_char; } #ifndef MIN #define MIN(A,B) ((A) < (B) ? (A) : (B)) #endif #ifdef DEBUG_EDIT private void read_debug(EditLine *el) { if (el->el_line.cursor > el->el_line.lastchar) (void) fprintf(el->el_errfile, "cursor > lastchar\r\n"); if (el->el_line.cursor < el->el_line.buffer) (void) fprintf(el->el_errfile, "cursor < buffer\r\n"); if (el->el_line.cursor > el->el_line.limit) (void) fprintf(el->el_errfile, "cursor > limit\r\n"); if (el->el_line.lastchar > el->el_line.limit) (void) fprintf(el->el_errfile, "lastchar > limit\r\n"); if (el->el_line.limit != &el->el_line.buffer[EL_BUFSIZ - 2]) (void) fprintf(el->el_errfile, "limit != &buffer[EL_BUFSIZ-2]\r\n"); } #endif /* DEBUG_EDIT */ /* read__fixio(): * Try to recover from a read error */ /* ARGSUSED */ private int read__fixio(int fd __attribute__((__unused__)), int e) { switch (e) { case -1: /* Make sure that the code is reachable */ #ifdef EWOULDBLOCK case EWOULDBLOCK: #ifndef TRY_AGAIN #define TRY_AGAIN #endif #endif /* EWOULDBLOCK */ #if defined(POSIX) && defined(EAGAIN) #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN case EAGAIN: #ifndef TRY_AGAIN #define TRY_AGAIN #endif #endif /* EWOULDBLOCK && EWOULDBLOCK != EAGAIN */ #endif /* POSIX && EAGAIN */ e = 0; #ifdef TRY_AGAIN #if defined(F_SETFL) && defined(O_NDELAY) if ((e = fcntl(fd, F_GETFL, 0)) == -1) return (-1); if (fcntl(fd, F_SETFL, e & ~O_NDELAY) == -1) return (-1); else e = 1; #endif /* F_SETFL && O_NDELAY */ #ifdef FIONBIO { int zero = 0; if (ioctl(fd, FIONBIO, (ioctl_t) & zero) == -1) return (-1); else e = 1; } #endif /* FIONBIO */ #endif /* TRY_AGAIN */ return (e ? 0 : -1); case EINTR: return (-1); default: return (-1); } } /* read_preread(): * Try to read the stuff in the input queue; */ private int read_preread(EditLine *el) { int chrs = 0; if (el->el_tty.t_mode == ED_IO) return (0); #ifdef FIONREAD (void) ioctl(el->el_infd, FIONREAD, (ioctl_t) & chrs); if (chrs > 0) { char buf[EL_BUFSIZ]; chrs = read(el->el_infd, buf, (size_t) MIN(chrs, EL_BUFSIZ - 1)); if (chrs > 0) { buf[chrs] = '\0'; el_push(el, buf); } } #endif /* FIONREAD */ return (chrs > 0); } /* el_push(): * Push a macro */ public void el_push(EditLine *el, const char *str) { c_macro_t *ma = &el->el_chared.c_macro; if (str != NULL && ma->level + 1 < EL_MAXMACRO) { ma->level++; if ((ma->macro[ma->level] = el_strdup(str)) != NULL) return; ma->level--; } term_beep(el); term__flush(el); } /* read_getcmd(): * Return next command from the input stream. */ private int read_getcmd(EditLine *el, el_action_t *cmdnum, char *ch) { el_action_t cmd; int num; el->el_errno = 0; do { if ((num = el_getc(el, ch)) != 1) { /* if EOF or error */ el->el_errno = num == 0 ? 0 : errno; return (num); } #ifdef KANJI if ((*ch & 0200)) { el->el_state.metanext = 0; cmd = CcViMap[' ']; break; } else #endif /* KANJI */ if (el->el_state.metanext) { el->el_state.metanext = 0; *ch |= 0200; } cmd = el->el_map.current[(unsigned char) *ch]; if (cmd == ED_SEQUENCE_LEAD_IN) { key_value_t val; switch (key_get(el, ch, &val)) { case XK_CMD: cmd = val.cmd; break; case XK_STR: el_push(el, val.str); break; #ifdef notyet case XK_EXE: /* XXX: In the future to run a user function */ RunCommand(val.str); break; #endif default: EL_ABORT((el->el_errfile, "Bad XK_ type \n")); break; } } if (el->el_map.alt == NULL) el->el_map.current = el->el_map.key; } while (cmd == ED_SEQUENCE_LEAD_IN); *cmdnum = cmd; return (OKCMD); } /* read_char(): * Read a character from the tty. */ private int read_char(EditLine *el, char *cp) { ssize_t num_read; int tried = 0; again: el->el_signal->sig_no = 0; while ((num_read = read(el->el_infd, cp, 1)) == -1) { if (el->el_signal->sig_no == SIGCONT) { sig_set(el); el_set(el, EL_REFRESH); goto again; } if (!tried && read__fixio(el->el_infd, errno) == 0) tried = 1; else { *cp = '\0'; return (-1); } } return (int)num_read; } /* read_pop(): * Pop a macro from the stack */ private void read_pop(c_macro_t *ma) { int i; el_free(ma->macro[0]); for (i = 0; i < ma->level; i++) ma->macro[i] = ma->macro[i + 1]; ma->level--; ma->offset = 0; } /* el_getc(): * Read a character */ public int el_getc(EditLine *el, char *cp) { int num_read; c_macro_t *ma = &el->el_chared.c_macro; term__flush(el); for (;;) { if (ma->level < 0) { if (!read_preread(el)) break; } if (ma->level < 0) break; if (ma->macro[0][ma->offset] == '\0') { read_pop(ma); continue; } *cp = ma->macro[0][ma->offset++] & 0377; if (ma->macro[0][ma->offset] == '\0') { /* Needed for QuoteMode On */ read_pop(ma); } return (1); } #ifdef DEBUG_READ (void) fprintf(el->el_errfile, "Turning raw mode on\n"); #endif /* DEBUG_READ */ if (tty_rawmode(el) < 0)/* make sure the tty is set up correctly */ return (0); #ifdef DEBUG_READ (void) fprintf(el->el_errfile, "Reading a character\n"); #endif /* DEBUG_READ */ num_read = (*el->el_read.read_char)(el, cp); #ifdef DEBUG_READ (void) fprintf(el->el_errfile, "Got it %c\n", *cp); #endif /* DEBUG_READ */ return (num_read); } protected void read_prepare(EditLine *el) { if (el->el_flags & HANDLE_SIGNALS) sig_set(el); if (el->el_flags & NO_TTY) return; if ((el->el_flags & (UNBUFFERED|EDIT_DISABLED)) == UNBUFFERED) tty_rawmode(el); /* This is relatively cheap, and things go terribly wrong if we have the wrong size. */ el_resize(el); re_clear_display(el); /* reset the display stuff */ ch_reset(el, 0); re_refresh(el); /* print the prompt */ if (el->el_flags & UNBUFFERED) term__flush(el); } protected void read_finish(EditLine *el) { if ((el->el_flags & UNBUFFERED) == 0) (void) tty_cookedmode(el); if (el->el_flags & HANDLE_SIGNALS) sig_clr(el); } public const char * el_gets(EditLine *el, int *nread) { int retval; el_action_t cmdnum = 0; int num; /* how many chars we have read at NL */ char ch; int crlf = 0; int nrb; #ifdef FIONREAD c_macro_t *ma = &el->el_chared.c_macro; #endif /* FIONREAD */ if (nread == NULL) nread = &nrb; *nread = 0; if (el->el_flags & NO_TTY) { char *cp = el->el_line.buffer; size_t idx; while ((num = (*el->el_read.read_char)(el, cp)) == 1) { /* make sure there is space for next character */ if (cp + 1 >= el->el_line.limit) { idx = (cp - el->el_line.buffer); if (!ch_enlargebufs(el, 2)) break; cp = &el->el_line.buffer[idx]; } cp++; if (el->el_flags & UNBUFFERED) break; if (cp[-1] == '\r' || cp[-1] == '\n') break; } if (num == -1) { if (errno == EINTR) cp = el->el_line.buffer; el->el_errno = errno; } el->el_line.cursor = el->el_line.lastchar = cp; *cp = '\0'; *nread = (int)(el->el_line.cursor - el->el_line.buffer); goto done; } #ifdef FIONREAD if (el->el_tty.t_mode == EX_IO && ma->level < 0) { long chrs = 0; (void) ioctl(el->el_infd, FIONREAD, (ioctl_t) & chrs); if (chrs == 0) { if (tty_rawmode(el) < 0) { errno = 0; *nread = 0; return (NULL); } } } #endif /* FIONREAD */ if ((el->el_flags & UNBUFFERED) == 0) read_prepare(el); if (el->el_flags & EDIT_DISABLED) { char *cp; size_t idx; if ((el->el_flags & UNBUFFERED) == 0) cp = el->el_line.buffer; else cp = el->el_line.lastchar; term__flush(el); while ((num = (*el->el_read.read_char)(el, cp)) == 1) { /* make sure there is space next character */ if (cp + 1 >= el->el_line.limit) { idx = (cp - el->el_line.buffer); if (!ch_enlargebufs(el, 2)) break; cp = &el->el_line.buffer[idx]; } cp++; crlf = cp[-1] == '\r' || cp[-1] == '\n'; if (el->el_flags & UNBUFFERED) break; if (crlf) break; } if (num == -1) { if (errno == EINTR) cp = el->el_line.buffer; el->el_errno = errno; } el->el_line.cursor = el->el_line.lastchar = cp; *cp = '\0'; goto done; } for (num = OKCMD; num == OKCMD;) { /* while still editing this * line */ #ifdef DEBUG_EDIT read_debug(el); #endif /* DEBUG_EDIT */ /* if EOF or error */ if ((num = read_getcmd(el, &cmdnum, &ch)) != OKCMD) { #ifdef DEBUG_READ (void) fprintf(el->el_errfile, "Returning from el_gets %d\n", num); #endif /* DEBUG_READ */ break; } if (el->el_errno == EINTR) { el->el_line.buffer[0] = '\0'; el->el_line.lastchar = el->el_line.cursor = el->el_line.buffer; break; } if ((unsigned int)cmdnum >= (unsigned int)el->el_map.nfunc) { /* BUG CHECK command */ #ifdef DEBUG_EDIT (void) fprintf(el->el_errfile, "ERROR: illegal command from key 0%o\r\n", ch); #endif /* DEBUG_EDIT */ continue; /* try again */ } /* now do the real command */ #ifdef DEBUG_READ { el_bindings_t *b; for (b = el->el_map.help; b->name; b++) if (b->func == cmdnum) break; if (b->name) (void) fprintf(el->el_errfile, "Executing %s\n", b->name); else (void) fprintf(el->el_errfile, "Error command = %d\n", cmdnum); } #endif /* DEBUG_READ */ /* vi redo needs these way down the levels... */ el->el_state.thiscmd = cmdnum; el->el_state.thisch = ch; if (el->el_map.type == MAP_VI && el->el_map.current == el->el_map.key && el->el_chared.c_redo.pos < el->el_chared.c_redo.lim) { if (cmdnum == VI_DELETE_PREV_CHAR && el->el_chared.c_redo.pos != el->el_chared.c_redo.buf && isprint((unsigned char)el->el_chared.c_redo.pos[-1])) el->el_chared.c_redo.pos--; else *el->el_chared.c_redo.pos++ = ch; } retval = (*el->el_map.func[cmdnum]) (el, ch); #ifdef DEBUG_READ (void) fprintf(el->el_errfile, "Returned state %d\n", retval ); #endif /* DEBUG_READ */ /* save the last command here */ el->el_state.lastcmd = cmdnum; /* use any return value */ switch (retval) { case CC_CURSOR: re_refresh_cursor(el); break; case CC_REDISPLAY: re_clear_lines(el); re_clear_display(el); /* FALLTHROUGH */ case CC_REFRESH: re_refresh(el); break; case CC_REFRESH_BEEP: re_refresh(el); term_beep(el); break; case CC_NORM: /* normal char */ break; case CC_ARGHACK: /* Suggested by Rich Salz */ /* */ continue; /* keep going... */ case CC_EOF: /* end of file typed */ if ((el->el_flags & UNBUFFERED) == 0) num = 0; else if (num == -1) { *el->el_line.lastchar++ = CONTROL('d'); el->el_line.cursor = el->el_line.lastchar; num = 1; } break; case CC_NEWLINE: /* normal end of line */ num = (int)(el->el_line.lastchar - el->el_line.buffer); break; case CC_FATAL: /* fatal error, reset to known state */ #ifdef DEBUG_READ (void) fprintf(el->el_errfile, "*** editor fatal ERROR ***\r\n\n"); #endif /* DEBUG_READ */ /* put (real) cursor in a known place */ re_clear_display(el); /* reset the display stuff */ ch_reset(el, 1); /* reset the input pointers */ re_refresh(el); /* print the prompt again */ break; case CC_ERROR: default: /* functions we don't know about */ #ifdef DEBUG_READ (void) fprintf(el->el_errfile, "*** editor ERROR ***\r\n\n"); #endif /* DEBUG_READ */ term_beep(el); term__flush(el); break; } el->el_state.argument = 1; el->el_state.doingarg = 0; el->el_chared.c_vcmd.action = NOP; if (el->el_flags & UNBUFFERED) break; } term__flush(el); /* flush any buffered output */ /* make sure the tty is set up correctly */ if ((el->el_flags & UNBUFFERED) == 0) { read_finish(el); *nread = num != -1 ? num : 0; } else { *nread = (int)(el->el_line.lastchar - el->el_line.buffer); } done: if (*nread == 0) { if (num == -1) { *nread = -1; errno = el->el_errno; } return NULL; } else return el->el_line.buffer; } clt/falcon/editline/src/read.h000066400000000000000000000037651176363201700165730ustar00rootroot00000000000000/* $NetBSD: read.h,v 1.6 2008/04/29 06:53:01 martin Exp $ */ /*- * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Anthony Mallet. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * el.read.h: Character reading functions */ #ifndef _h_el_read #define _h_el_read typedef int (*el_rfunc_t)(EditLine *, char *); typedef struct el_read_t { el_rfunc_t read_char; /* Function to read a character */ } el_read_t; protected int read_init(EditLine *); protected void read_prepare(EditLine *); protected void read_finish(EditLine *); protected int el_read_setfn(EditLine *, el_rfunc_t); protected el_rfunc_t el_read_getfn(EditLine *); #endif /* _h_el_read */ clt/falcon/editline/src/readline.c000066400000000000000000001257301176363201700174330ustar00rootroot00000000000000/* $NetBSD: readline.c,v 1.85 2009/09/07 21:24:33 christos Exp $ */ /*- * Copyright (c) 1997 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jaromir Dolecek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #if !defined(lint) && !defined(SCCSID) __RCSID("$NetBSD: readline.c,v 1.85 2009/09/07 21:24:33 christos Exp $"); #endif /* not lint && not SCCSID */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "editline/readline.h" #include "el.h" #include "fcns.h" /* for EL_NUM_FCNS */ #include "histedit.h" #include "filecomplete.h" #if !defined(SIZE_T_MAX) # define SIZE_T_MAX (size_t)(-1) #endif void rl_prep_terminal(int); void rl_deprep_terminal(void); /* for rl_complete() */ #define TAB '\r' /* see comment at the #ifdef for sense of this */ /* #define GDB_411_HACK */ /* readline compatibility stuff - look at readline sources/documentation */ /* to see what these variables mean */ const char *rl_library_version = "EditLine wrapper"; int rl_readline_version = RL_READLINE_VERSION; static char empty[] = { '\0' }; static char expand_chars[] = { ' ', '\t', '\n', '=', '(', '\0' }; static char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$', '>', '<', '=', ';', '|', '&', '{', '(', '\0' }; char *rl_readline_name = empty; FILE *rl_instream = NULL; FILE *rl_outstream = NULL; int rl_point = 0; int rl_end = 0; char *rl_line_buffer = NULL; VCPFunction *rl_linefunc = NULL; int rl_done = 0; VFunction *rl_event_hook = NULL; KEYMAP_ENTRY_ARRAY emacs_standard_keymap, emacs_meta_keymap, emacs_ctlx_keymap; int history_base = 1; /* probably never subject to change */ int history_length = 0; int max_input_history = 0; char history_expansion_char = '!'; char history_subst_char = '^'; char *history_no_expand_chars = expand_chars; Function *history_inhibit_expansion_function = NULL; char *history_arg_extract(int start, int end, const char *str); int rl_inhibit_completion = 0; int rl_attempted_completion_over = 0; char *rl_basic_word_break_characters = break_chars; char *rl_completer_word_break_characters = NULL; char *rl_completer_quote_characters = NULL; Function *rl_completion_entry_function = NULL; CPPFunction *rl_attempted_completion_function = NULL; Function *rl_pre_input_hook = NULL; Function *rl_startup1_hook = NULL; int (*rl_getc_function)(FILE *) = NULL; char *rl_terminal_name = NULL; int rl_already_prompted = 0; int rl_filename_completion_desired = 0; int rl_ignore_completion_duplicates = 0; int rl_catch_signals = 1; int readline_echoing_p = 1; int _rl_print_completions_horizontally = 0; VFunction *rl_redisplay_function = NULL; Function *rl_startup_hook = NULL; VFunction *rl_completion_display_matches_hook = NULL; VFunction *rl_prep_term_function = (VFunction *)rl_prep_terminal; VFunction *rl_deprep_term_function = (VFunction *)rl_deprep_terminal; KEYMAP_ENTRY_ARRAY emacs_meta_keymap; /* * The current prompt string. */ char *rl_prompt = NULL; /* * This is set to character indicating type of completion being done by * rl_complete_internal(); this is available for application completion * functions. */ int rl_completion_type = 0; /* * If more than this number of items results from query for possible * completions, we ask user if they are sure to really display the list. */ int rl_completion_query_items = 100; /* * List of characters which are word break characters, but should be left * in the parsed text when it is passed to the completion function. * Shell uses this to help determine what kind of completing to do. */ char *rl_special_prefixes = NULL; /* * This is the character appended to the completed words if at the end of * the line. Default is ' ' (a space). */ int rl_completion_append_character = ' '; /* stuff below is used internally by libedit for readline emulation */ static History *h = NULL; static EditLine *e = NULL; static Function *map[256]; static jmp_buf topbuf; /* internal functions */ static unsigned char _el_rl_complete(EditLine *, int); static unsigned char _el_rl_tstp(EditLine *, int); static char *_get_prompt(EditLine *); static int _getc_function(EditLine *, char *); static HIST_ENTRY *_move_history(int); static int _history_expand_command(const char *, size_t, size_t, char **); static char *_rl_compat_sub(const char *, const char *, const char *, int); static int _rl_event_read_char(EditLine *, char *); static void _rl_update_pos(void); /* ARGSUSED */ static char * _get_prompt(EditLine *el __attribute__((__unused__))) { rl_already_prompted = 1; return (rl_prompt); } /* * generic function for moving around history */ static HIST_ENTRY * _move_history(int op) { HistEvent ev; static HIST_ENTRY rl_he; if (history(h, &ev, op) != 0) return (HIST_ENTRY *) NULL; rl_he.line = ev.str; rl_he.data = (histdata_t) &(ev.num); return (&rl_he); } /* * read one key from user defined input function */ static int /*ARGSUSED*/ _getc_function(EditLine *el, char *c) { int i; i = (*rl_getc_function)(NULL); if (i == -1) return 0; *c = i; return 1; } static const char _dothistory[] = "/.history"; static const char * _default_history_file(void) { struct passwd *p; static char path[PATH_MAX]; if (*path) return path; if ((p = getpwuid(getuid())) == NULL) return NULL; strlcpy(path, p->pw_dir, PATH_MAX); strlcat(path, _dothistory, PATH_MAX); return path; } /* * READLINE compatibility stuff */ /* * Set the prompt */ int rl_set_prompt(const char *prompt) { char *p; if (!prompt) prompt = ""; if (rl_prompt != NULL && strcmp(rl_prompt, prompt) == 0) return 0; if (rl_prompt) free(rl_prompt); rl_prompt = strdup(prompt); if (rl_prompt == NULL) return -1; while ((p = strchr(rl_prompt, RL_PROMPT_END_IGNORE)) != NULL) *p = RL_PROMPT_START_IGNORE; return 0; } /* * initialize rl compat stuff */ int rl_initialize(void) { HistEvent ev; const LineInfo *li; int editmode = 1; struct termios t; if (e != NULL) el_end(e); if (h != NULL) history_end(h); if (!rl_instream) rl_instream = stdin; if (!rl_outstream) rl_outstream = stdout; /* * See if we don't really want to run the editor */ if (tcgetattr(fileno(rl_instream), &t) != -1 && (t.c_lflag & ECHO) == 0) editmode = 0; e = el_init(rl_readline_name, rl_instream, rl_outstream, stderr); if (!editmode) el_set(e, EL_EDITMODE, 0); h = history_init(); if (!e || !h) return (-1); history(h, &ev, H_SETSIZE, INT_MAX); /* unlimited */ history_length = 0; max_input_history = INT_MAX; el_set(e, EL_HIST, history, h); /* setup getc function if valid */ if (rl_getc_function) el_set(e, EL_GETCFN, _getc_function); /* for proper prompt printing in readline() */ if (rl_set_prompt("") == -1) { history_end(h); el_end(e); return -1; } el_set(e, EL_PROMPT, _get_prompt, RL_PROMPT_START_IGNORE); el_set(e, EL_SIGNAL, rl_catch_signals); /* set default mode to "emacs"-style and read setting afterwards */ /* so this can be overriden */ el_set(e, EL_EDITOR, "emacs"); if (rl_terminal_name != NULL) el_set(e, EL_TERMINAL, rl_terminal_name); else el_get(e, EL_TERMINAL, &rl_terminal_name); /* * Word completion - this has to go AFTER rebinding keys * to emacs-style. */ el_set(e, EL_ADDFN, "rl_complete", "ReadLine compatible completion function", _el_rl_complete); el_set(e, EL_BIND, "^I", "rl_complete", NULL); /* * Send TSTP when ^Z is pressed. */ el_set(e, EL_ADDFN, "rl_tstp", "ReadLine compatible suspend function", _el_rl_tstp); el_set(e, EL_BIND, "^Z", "rl_tstp", NULL); /* read settings from configuration file */ el_source(e, NULL); /* * Unfortunately, some applications really do use rl_point * and rl_line_buffer directly. */ li = el_line(e); /* a cheesy way to get rid of const cast. */ rl_line_buffer = memchr(li->buffer, *li->buffer, 1); _rl_update_pos(); if (rl_startup_hook) (*rl_startup_hook)(NULL, 0); return (0); } /* * read one line from input stream and return it, chomping * trailing newline (if there is any) */ char * readline(const char *p) { HistEvent ev; const char * volatile prompt = p; int count; const char *ret; char *buf; static int used_event_hook; if (e == NULL || h == NULL) rl_initialize(); rl_done = 0; (void)setjmp(topbuf); /* update prompt accordingly to what has been passed */ if (rl_set_prompt(prompt) == -1) return NULL; if (rl_pre_input_hook) (*rl_pre_input_hook)(NULL, 0); if (rl_event_hook && !(e->el_flags&NO_TTY)) { el_set(e, EL_GETCFN, _rl_event_read_char); used_event_hook = 1; } if (!rl_event_hook && used_event_hook) { el_set(e, EL_GETCFN, EL_BUILTIN_GETCFN); used_event_hook = 0; } rl_already_prompted = 0; /* get one line from input stream */ ret = el_gets(e, &count); if (ret && count > 0) { int lastidx; buf = strdup(ret); if (buf == NULL) return NULL; lastidx = count - 1; if (buf[lastidx] == '\n') buf[lastidx] = '\0'; } else buf = NULL; history(h, &ev, H_GETSIZE); history_length = ev.num; return buf; } /* * history functions */ /* * is normally called before application starts to use * history expansion functions */ void using_history(void) { if (h == NULL || e == NULL) rl_initialize(); } /* * substitute ``what'' with ``with'', returning resulting string; if * globally == 1, substitutes all occurrences of what, otherwise only the * first one */ static char * _rl_compat_sub(const char *str, const char *what, const char *with, int globally) { const char *s; char *r, *result; size_t len, with_len, what_len; len = strlen(str); with_len = strlen(with); what_len = strlen(what); /* calculate length we need for result */ s = str; while (*s) { if (*s == *what && !strncmp(s, what, what_len)) { len += with_len - what_len; if (!globally) break; s += what_len; } else s++; } r = result = malloc(len + 1); if (result == NULL) return NULL; s = str; while (*s) { if (*s == *what && !strncmp(s, what, what_len)) { (void)strncpy(r, with, with_len); r += with_len; s += what_len; if (!globally) { (void)strcpy(r, s); return(result); } } else *r++ = *s++; } *r = '\0'; return(result); } static char *last_search_pat; /* last !?pat[?] search pattern */ static char *last_search_match; /* last !?pat[?] that matched */ const char * get_history_event(const char *cmd, int *cindex, int qchar) { int idx, sign, sub, num, begin, ret; size_t len; char *pat; const char *rptr; HistEvent ev; idx = *cindex; if (cmd[idx++] != history_expansion_char) return(NULL); /* find out which event to take */ if (cmd[idx] == history_expansion_char || cmd[idx] == '\0') { if (history(h, &ev, H_FIRST) != 0) return(NULL); *cindex = cmd[idx]? (idx + 1):idx; return(ev.str); } sign = 0; if (cmd[idx] == '-') { sign = 1; idx++; } if ('0' <= cmd[idx] && cmd[idx] <= '9') { HIST_ENTRY *rl_he; num = 0; while (cmd[idx] && '0' <= cmd[idx] && cmd[idx] <= '9') { num = num * 10 + cmd[idx] - '0'; idx++; } if (sign) num = history_length - num + 1; if (!(rl_he = history_get(num))) return(NULL); *cindex = idx; return(rl_he->line); } sub = 0; if (cmd[idx] == '?') { sub = 1; idx++; } begin = idx; while (cmd[idx]) { if (cmd[idx] == '\n') break; if (sub && cmd[idx] == '?') break; if (!sub && (cmd[idx] == ':' || cmd[idx] == ' ' || cmd[idx] == '\t' || cmd[idx] == qchar)) break; idx++; } len = idx - begin; if (sub && cmd[idx] == '?') idx++; if (sub && len == 0 && last_search_pat && *last_search_pat) pat = last_search_pat; else if (len == 0) return(NULL); else { if ((pat = malloc(len + 1)) == NULL) return NULL; (void)strncpy(pat, cmd + begin, len); pat[len] = '\0'; } if (history(h, &ev, H_CURR) != 0) { if (pat != last_search_pat) free(pat); return (NULL); } num = ev.num; if (sub) { if (pat != last_search_pat) { if (last_search_pat) free(last_search_pat); last_search_pat = pat; } ret = history_search(pat, -1); } else ret = history_search_prefix(pat, -1); if (ret == -1) { /* restore to end of list on failed search */ history(h, &ev, H_FIRST); (void)fprintf(rl_outstream, "%s: Event not found\n", pat); if (pat != last_search_pat) free(pat); return(NULL); } if (sub && len) { if (last_search_match && last_search_match != pat) free(last_search_match); last_search_match = pat; } if (pat != last_search_pat) free(pat); if (history(h, &ev, H_CURR) != 0) return(NULL); *cindex = idx; rptr = ev.str; /* roll back to original position */ (void)history(h, &ev, H_SET, num); return rptr; } /* * the real function doing history expansion - takes as argument command * to do and data upon which the command should be executed * does expansion the way I've understood readline documentation * * returns 0 if data was not modified, 1 if it was and 2 if the string * should be only printed and not executed; in case of error, * returns -1 and *result points to NULL * it's callers responsibility to free() string returned in *result */ static int _history_expand_command(const char *command, size_t offs, size_t cmdlen, char **result) { char *tmp, *search = NULL, *aptr; const char *ptr, *cmd; static char *from = NULL, *to = NULL; int start, end, idx, has_mods = 0; int p_on = 0, g_on = 0; *result = NULL; aptr = NULL; ptr = NULL; /* First get event specifier */ idx = 0; if (strchr(":^*$", command[offs + 1])) { char str[4]; /* * "!:" is shorthand for "!!:". * "!^", "!*" and "!$" are shorthand for * "!!:^", "!!:*" and "!!:$" respectively. */ str[0] = str[1] = '!'; str[2] = '0'; ptr = get_history_event(str, &idx, 0); idx = (command[offs + 1] == ':')? 1:0; has_mods = 1; } else { if (command[offs + 1] == '#') { /* use command so far */ if ((aptr = malloc(offs + 1)) == NULL) return -1; (void)strncpy(aptr, command, offs); aptr[offs] = '\0'; idx = 1; } else { int qchar; qchar = (offs > 0 && command[offs - 1] == '"')? '"':0; ptr = get_history_event(command + offs, &idx, qchar); } has_mods = command[offs + idx] == ':'; } if (ptr == NULL && aptr == NULL) return(-1); if (!has_mods) { *result = strdup(aptr ? aptr : ptr); if (aptr) free(aptr); if (*result == NULL) return -1; return(1); } cmd = command + offs + idx + 1; /* Now parse any word designators */ if (*cmd == '%') /* last word matched by ?pat? */ tmp = strdup(last_search_match? last_search_match:""); else if (strchr("^*$-0123456789", *cmd)) { start = end = -1; if (*cmd == '^') start = end = 1, cmd++; else if (*cmd == '$') start = -1, cmd++; else if (*cmd == '*') start = 1, cmd++; else if (*cmd == '-' || isdigit((unsigned char) *cmd)) { start = 0; while (*cmd && '0' <= *cmd && *cmd <= '9') start = start * 10 + *cmd++ - '0'; if (*cmd == '-') { if (isdigit((unsigned char) cmd[1])) { cmd++; end = 0; while (*cmd && '0' <= *cmd && *cmd <= '9') end = end * 10 + *cmd++ - '0'; } else if (cmd[1] == '$') { cmd += 2; end = -1; } else { cmd++; end = -2; } } else if (*cmd == '*') end = -1, cmd++; else end = start; } tmp = history_arg_extract(start, end, aptr? aptr:ptr); if (tmp == NULL) { (void)fprintf(rl_outstream, "%s: Bad word specifier", command + offs + idx); if (aptr) free(aptr); return(-1); } } else tmp = strdup(aptr? aptr:ptr); if (aptr) free(aptr); if (*cmd == '\0' || ((size_t)(cmd - (command + offs)) >= cmdlen)) { *result = tmp; return(1); } for (; *cmd; cmd++) { if (*cmd == ':') continue; else if (*cmd == 'h') { /* remove trailing path */ if ((aptr = strrchr(tmp, '/')) != NULL) *aptr = '\0'; } else if (*cmd == 't') { /* remove leading path */ if ((aptr = strrchr(tmp, '/')) != NULL) { aptr = strdup(aptr + 1); free(tmp); tmp = aptr; } } else if (*cmd == 'r') { /* remove trailing suffix */ if ((aptr = strrchr(tmp, '.')) != NULL) *aptr = '\0'; } else if (*cmd == 'e') { /* remove all but suffix */ if ((aptr = strrchr(tmp, '.')) != NULL) { aptr = strdup(aptr); free(tmp); tmp = aptr; } } else if (*cmd == 'p') /* print only */ p_on = 1; else if (*cmd == 'g') g_on = 2; else if (*cmd == 's' || *cmd == '&') { char *what, *with, delim; size_t len, from_len; size_t size; if (*cmd == '&' && (from == NULL || to == NULL)) continue; else if (*cmd == 's') { delim = *(++cmd), cmd++; size = 16; what = realloc(from, size); if (what == NULL) { free(from); free(tmp); return 0; } len = 0; for (; *cmd && *cmd != delim; cmd++) { if (*cmd == '\\' && cmd[1] == delim) cmd++; if (len >= size) { char *nwhat; nwhat = realloc(what, (size <<= 1)); if (nwhat == NULL) { free(what); free(tmp); return 0; } what = nwhat; } what[len++] = *cmd; } what[len] = '\0'; from = what; if (*what == '\0') { free(what); if (search) { from = strdup(search); if (from == NULL) { free(tmp); return 0; } } else { from = NULL; free(tmp); return (-1); } } cmd++; /* shift after delim */ if (!*cmd) continue; size = 16; with = realloc(to, size); if (with == NULL) { free(to); free(tmp); return -1; } len = 0; from_len = strlen(from); for (; *cmd && *cmd != delim; cmd++) { if (len + from_len + 1 >= size) { char *nwith; size += from_len + 1; nwith = realloc(with, size); if (nwith == NULL) { free(with); free(tmp); return -1; } with = nwith; } if (*cmd == '&') { /* safe */ (void)strcpy(&with[len], from); len += from_len; continue; } if (*cmd == '\\' && (*(cmd + 1) == delim || *(cmd + 1) == '&')) cmd++; with[len++] = *cmd; } with[len] = '\0'; to = with; } aptr = _rl_compat_sub(tmp, from, to, g_on); if (aptr) { free(tmp); tmp = aptr; } g_on = 0; } } *result = tmp; return (p_on? 2:1); } /* * csh-style history expansion */ int history_expand(char *str, char **output) { int ret = 0; size_t idx, i, size; char *tmp, *result; if (h == NULL || e == NULL) rl_initialize(); if (history_expansion_char == 0) { *output = strdup(str); return(0); } *output = NULL; if (str[0] == history_subst_char) { /* ^foo^foo2^ is equivalent to !!:s^foo^foo2^ */ *output = malloc(strlen(str) + 4 + 1); if (*output == NULL) return 0; (*output)[0] = (*output)[1] = history_expansion_char; (*output)[2] = ':'; (*output)[3] = 's'; (void)strcpy((*output) + 4, str); str = *output; } else { *output = strdup(str); if (*output == NULL) return 0; } #define ADD_STRING(what, len, fr) \ { \ if (idx + len + 1 > size) { \ char *nresult = realloc(result, (size += len + 1));\ if (nresult == NULL) { \ free(*output); \ if (/*CONSTCOND*/fr) \ free(tmp); \ return 0; \ } \ result = nresult; \ } \ (void)strncpy(&result[idx], what, len); \ idx += len; \ result[idx] = '\0'; \ } result = NULL; size = idx = 0; tmp = NULL; for (i = 0; str[i];) { int qchar, loop_again; size_t len, start, j; qchar = 0; loop_again = 1; start = j = i; loop: for (; str[j]; j++) { if (str[j] == '\\' && str[j + 1] == history_expansion_char) { (void)strcpy(&str[j], &str[j + 1]); continue; } if (!loop_again) { if (isspace((unsigned char) str[j]) || str[j] == qchar) break; } if (str[j] == history_expansion_char && !strchr(history_no_expand_chars, str[j + 1]) && (!history_inhibit_expansion_function || (*history_inhibit_expansion_function)(str, (int)j) == 0)) break; } if (str[j] && loop_again) { i = j; qchar = (j > 0 && str[j - 1] == '"' )? '"':0; j++; if (str[j] == history_expansion_char) j++; loop_again = 0; goto loop; } len = i - start; ADD_STRING(&str[start], len, 0); if (str[i] == '\0' || str[i] != history_expansion_char) { len = j - i; ADD_STRING(&str[i], len, 0); if (start == 0) ret = 0; else ret = 1; break; } ret = _history_expand_command (str, i, (j - i), &tmp); if (ret > 0 && tmp) { len = strlen(tmp); ADD_STRING(tmp, len, 1); } if (tmp) { free(tmp); tmp = NULL; } i = j; } /* ret is 2 for "print only" option */ if (ret == 2) { add_history(result); #ifdef GDB_411_HACK /* gdb 4.11 has been shipped with readline, where */ /* history_expand() returned -1 when the line */ /* should not be executed; in readline 2.1+ */ /* it should return 2 in such a case */ ret = -1; #endif } free(*output); *output = result; return (ret); } /* * Return a string consisting of arguments of "str" from "start" to "end". */ char * history_arg_extract(int start, int end, const char *str) { size_t i, len, max; char **arr, *result = NULL; arr = history_tokenize(str); if (!arr) return NULL; if (arr && *arr == NULL) goto out; for (max = 0; arr[max]; max++) continue; max--; if (start == '$') start = (int)max; if (end == '$') end = (int)max; if (end < 0) end = (int)max + end + 1; if (start < 0) start = end; if (start < 0 || end < 0 || (size_t)start > max || (size_t)end > max || start > end) goto out; for (i = start, len = 0; i <= (size_t)end; i++) len += strlen(arr[i]) + 1; len++; result = malloc(len); if (result == NULL) goto out; for (i = start, len = 0; i <= (size_t)end; i++) { (void)strcpy(result + len, arr[i]); len += strlen(arr[i]); if (i < (size_t)end) result[len++] = ' '; } result[len] = '\0'; out: for (i = 0; arr[i]; i++) free(arr[i]); free(arr); return result; } /* * Parse the string into individual tokens, * similar to how shell would do it. */ char ** history_tokenize(const char *str) { int size = 1, idx = 0, i, start; size_t len; char **result = NULL, *temp, delim = '\0'; for (i = 0; str[i];) { while (isspace((unsigned char) str[i])) i++; start = i; for (; str[i];) { if (str[i] == '\\') { if (str[i+1] != '\0') i++; } else if (str[i] == delim) delim = '\0'; else if (!delim && (isspace((unsigned char) str[i]) || strchr("()<>;&|$", str[i]))) break; else if (!delim && strchr("'`\"", str[i])) delim = str[i]; if (str[i]) i++; } if (idx + 2 >= size) { char **nresult; size <<= 1; nresult = realloc(result, size * sizeof(char *)); if (nresult == NULL) { free(result); return NULL; } result = nresult; } len = i - start; temp = malloc(len + 1); if (temp == NULL) { for (i = 0; i < idx; i++) free(result[i]); free(result); return NULL; } (void)strncpy(temp, &str[start], len); temp[len] = '\0'; result[idx++] = temp; result[idx] = NULL; if (str[i]) i++; } return (result); } /* * limit size of history record to ``max'' events */ void stifle_history(int max) { HistEvent ev; if (h == NULL || e == NULL) rl_initialize(); if (history(h, &ev, H_SETSIZE, max) == 0) max_input_history = max; } /* * "unlimit" size of history - set the limit to maximum allowed int value */ int unstifle_history(void) { HistEvent ev; int omax; history(h, &ev, H_SETSIZE, INT_MAX); omax = max_input_history; max_input_history = INT_MAX; return (omax); /* some value _must_ be returned */ } int history_is_stifled(void) { /* cannot return true answer */ return (max_input_history != INT_MAX); } static const char _history_tmp_template[] = "/tmp/.historyXXXXXX"; int history_truncate_file (const char *filename, int nlines) { int ret = 0; FILE *fp, *tp; char template[sizeof(_history_tmp_template)]; char buf[4096]; int fd; char *cp; off_t off; int count = 0; ssize_t left = 0; if (filename == NULL && (filename = _default_history_file()) == NULL) return errno; if ((fp = fopen(filename, "r+")) == NULL) return errno; strcpy(template, _history_tmp_template); if ((fd = mkstemp(template)) == -1) { ret = errno; goto out1; } if ((tp = fdopen(fd, "r+")) == NULL) { close(fd); ret = errno; goto out2; } for(;;) { if (fread(buf, sizeof(buf), 1, fp) != 1) { if (ferror(fp)) { ret = errno; break; } if (fseeko(fp, (off_t)sizeof(buf) * count, SEEK_SET) == (off_t)-1) { ret = errno; break; } left = fread(buf, 1, sizeof(buf), fp); if (ferror(fp)) { ret = errno; break; } if (left == 0) { count--; left = sizeof(buf); } else if (fwrite(buf, (size_t)left, 1, tp) != 1) { ret = errno; break; } fflush(tp); break; } if (fwrite(buf, sizeof(buf), 1, tp) != 1) { ret = errno; break; } count++; } if (ret) goto out3; cp = buf + left - 1; if(*cp != '\n') cp++; for(;;) { while (--cp >= buf) { if (*cp == '\n') { if (--nlines == 0) { if (++cp >= buf + sizeof(buf)) { count++; cp = buf; } break; } } } if (nlines <= 0 || count == 0) break; count--; if (fseeko(tp, (off_t)sizeof(buf) * count, SEEK_SET) < 0) { ret = errno; break; } if (fread(buf, sizeof(buf), 1, tp) != 1) { if (ferror(tp)) { ret = errno; break; } ret = EAGAIN; break; } cp = buf + sizeof(buf); } if (ret || nlines > 0) goto out3; if (fseeko(fp, 0, SEEK_SET) == (off_t)-1) { ret = errno; goto out3; } if (fseeko(tp, (off_t)sizeof(buf) * count + (cp - buf), SEEK_SET) == (off_t)-1) { ret = errno; goto out3; } for(;;) { if ((left = fread(buf, 1, sizeof(buf), tp)) == 0) { if (ferror(fp)) ret = errno; break; } if (fwrite(buf, (size_t)left, 1, fp) != 1) { ret = errno; break; } } fflush(fp); if((off = ftello(fp)) > 0) (void)ftruncate(fileno(fp), off); out3: fclose(tp); out2: unlink(template); out1: fclose(fp); return ret; } /* * read history from a file given */ int read_history(const char *filename) { HistEvent ev; if (h == NULL || e == NULL) rl_initialize(); if (filename == NULL && (filename = _default_history_file()) == NULL) return errno; return (history(h, &ev, H_LOAD, filename) == -1 ? (errno ? errno : EINVAL) : 0); } /* * write history to a file given */ int write_history(const char *filename) { HistEvent ev; if (h == NULL || e == NULL) rl_initialize(); if (filename == NULL && (filename = _default_history_file()) == NULL) return errno; return (history(h, &ev, H_SAVE, filename) == -1 ? (errno ? errno : EINVAL) : 0); } /* * returns history ``num''th event * * returned pointer points to static variable */ HIST_ENTRY * history_get(int num) { static HIST_ENTRY she; HistEvent ev; int curr_num; if (h == NULL || e == NULL) rl_initialize(); /* save current position */ if (history(h, &ev, H_CURR) != 0) return (NULL); curr_num = ev.num; /* start from the oldest */ if (history(h, &ev, H_LAST) != 0) return (NULL); /* error */ /* look forwards for event matching specified offset */ if (history(h, &ev, H_NEXT_EVDATA, num, &she.data)) return (NULL); she.line = ev.str; /* restore pointer to where it was */ (void)history(h, &ev, H_SET, curr_num); return (&she); } /* * add the line to history table */ int add_history(const char *line) { HistEvent ev; if (h == NULL || e == NULL) rl_initialize(); (void)history(h, &ev, H_ENTER, line); if (history(h, &ev, H_GETSIZE) == 0) history_length = ev.num; return (!(history_length > 0)); /* return 0 if all is okay */ } /* * remove the specified entry from the history list and return it. */ HIST_ENTRY * remove_history(int num) { HIST_ENTRY *he; HistEvent ev; if (h == NULL || e == NULL) rl_initialize(); if ((he = malloc(sizeof(*he))) == NULL) return NULL; if (history(h, &ev, H_DELDATA, num, &he->data) != 0) { free(he); return NULL; } he->line = ev.str; if (history(h, &ev, H_GETSIZE) == 0) history_length = ev.num; return he; } /* * replace the line and data of the num-th entry */ HIST_ENTRY * replace_history_entry(int num, const char *line, histdata_t data) { HIST_ENTRY *he; HistEvent ev; int curr_num; if (h == NULL || e == NULL) rl_initialize(); /* save current position */ if (history(h, &ev, H_CURR) != 0) return NULL; curr_num = ev.num; /* start from the oldest */ if (history(h, &ev, H_LAST) != 0) return NULL; /* error */ if ((he = malloc(sizeof(*he))) == NULL) return NULL; /* look forwards for event matching specified offset */ if (history(h, &ev, H_NEXT_EVDATA, num, &he->data)) goto out; he->line = strdup(ev.str); if (he->line == NULL) goto out; if (history(h, &ev, H_REPLACE, line, data)) goto out; /* restore pointer to where it was */ if (history(h, &ev, H_SET, curr_num)) goto out; return he; out: free(he); return NULL; } /* * clear the history list - delete all entries */ void clear_history(void) { HistEvent ev; history(h, &ev, H_CLEAR); history_length = 0; } /* * returns offset of the current history event */ int where_history(void) { HistEvent ev; int curr_num, off; if (history(h, &ev, H_CURR) != 0) return (0); curr_num = ev.num; history(h, &ev, H_FIRST); off = 1; while (ev.num != curr_num && history(h, &ev, H_NEXT) == 0) off++; return (off); } /* * returns current history event or NULL if there is no such event */ HIST_ENTRY * current_history(void) { return (_move_history(H_CURR)); } /* * returns total number of bytes history events' data are using */ int history_total_bytes(void) { HistEvent ev; int curr_num; size_t size; if (history(h, &ev, H_CURR) != 0) return (-1); curr_num = ev.num; history(h, &ev, H_FIRST); size = 0; do size += strlen(ev.str); while (history(h, &ev, H_NEXT) == 0); /* get to the same position as before */ history(h, &ev, H_PREV_EVENT, curr_num); return (int)(size); } /* * sets the position in the history list to ``pos'' */ int history_set_pos(int pos) { HistEvent ev; int curr_num; if (pos >= history_length || pos < 0) return (-1); history(h, &ev, H_CURR); curr_num = ev.num; /* * use H_DELDATA to set to nth history (without delete) by passing * (void **)-1 */ if (history(h, &ev, H_DELDATA, pos, (void **)-1)) { history(h, &ev, H_SET, curr_num); return(-1); } return (0); } /* * returns previous event in history and shifts pointer accordingly */ HIST_ENTRY * previous_history(void) { return (_move_history(H_PREV)); } /* * returns next event in history and shifts pointer accordingly */ HIST_ENTRY * next_history(void) { return (_move_history(H_NEXT)); } /* * searches for first history event containing the str */ int history_search(const char *str, int direction) { HistEvent ev; const char *strp; int curr_num; if (history(h, &ev, H_CURR) != 0) return (-1); curr_num = ev.num; for (;;) { if ((strp = strstr(ev.str, str)) != NULL) return (int) (strp - ev.str); if (history(h, &ev, direction < 0 ? H_NEXT:H_PREV) != 0) break; } history(h, &ev, H_SET, curr_num); return (-1); } /* * searches for first history event beginning with str */ int history_search_prefix(const char *str, int direction) { HistEvent ev; return (history(h, &ev, direction < 0? H_PREV_STR:H_NEXT_STR, str)); } /* * search for event in history containing str, starting at offset * abs(pos); continue backward, if pos<0, forward otherwise */ /* ARGSUSED */ int history_search_pos(const char *str, int direction __attribute__((__unused__)), int pos) { HistEvent ev; int curr_num, off; off = (pos > 0) ? pos : -pos; pos = (pos > 0) ? 1 : -1; if (history(h, &ev, H_CURR) != 0) return (-1); curr_num = ev.num; if (history_set_pos(off) != 0 || history(h, &ev, H_CURR) != 0) return (-1); for (;;) { if (strstr(ev.str, str)) return (off); if (history(h, &ev, (pos < 0) ? H_PREV : H_NEXT) != 0) break; } /* set "current" pointer back to previous state */ history(h, &ev, (pos < 0) ? H_NEXT_EVENT : H_PREV_EVENT, curr_num); return (-1); } /********************************/ /* completion functions */ char * tilde_expand(char *name) { return fn_tilde_expand(name); } char * filename_completion_function(const char *name, int state) { return fn_filename_completion_function(name, state); } /* * a completion generator for usernames; returns _first_ username * which starts with supplied text * text contains a partial username preceded by random character * (usually '~'); state is ignored * it's callers responsibility to free returned value */ char * username_completion_function(const char *text, int state) { struct passwd *pwd; if (text[0] == '\0') return (NULL); if (*text == '~') text++; if (state == 0) setpwent(); while ((pwd = getpwent()) && pwd != NULL && text[0] == pwd->pw_name[0] && strcmp(text, pwd->pw_name) == 0); if (pwd == NULL) { endpwent(); return NULL; } return strdup(pwd->pw_name); } /* * el-compatible wrapper to send TSTP on ^Z */ /* ARGSUSED */ static unsigned char _el_rl_tstp(EditLine *el __attribute__((__unused__)), int ch __attribute__((__unused__))) { (void)kill(0, SIGTSTP); return CC_NORM; } /* * Display list of strings in columnar format on readline's output stream. * 'matches' is list of strings, 'len' is number of strings in 'matches', * 'max' is maximum length of string in 'matches'. */ void rl_display_match_list(char **matches, int len, int max) { fn_display_match_list(e, matches, (size_t)len, (size_t)max); } static const char * /*ARGSUSED*/ _rl_completion_append_character_function(const char *dummy __attribute__((__unused__))) { static char buf[2]; buf[0] = rl_completion_append_character; buf[1] = '\0'; return buf; } /* * complete word at current point */ /* ARGSUSED */ int rl_complete(int ignore __attribute__((__unused__)), int invoking_key) { if (h == NULL || e == NULL) rl_initialize(); if (rl_inhibit_completion) { char arr[2]; arr[0] = (char)invoking_key; arr[1] = '\0'; el_insertstr(e, arr); return (CC_REFRESH); } /* Just look at how many global variables modify this operation! */ return fn_complete(e, (CPFunction *)rl_completion_entry_function, rl_attempted_completion_function, rl_basic_word_break_characters, rl_special_prefixes, _rl_completion_append_character_function, (size_t)rl_completion_query_items, &rl_completion_type, &rl_attempted_completion_over, &rl_point, &rl_end); } /* ARGSUSED */ static unsigned char _el_rl_complete(EditLine *el __attribute__((__unused__)), int ch) { return (unsigned char)rl_complete(0, ch); } /* * misc other functions */ /* * bind key c to readline-type function func */ int rl_bind_key(int c, rl_command_func_t *func) { int retval = -1; if (h == NULL || e == NULL) rl_initialize(); if (func == rl_insert) { /* XXX notice there is no range checking of ``c'' */ e->el_map.key[c] = ED_INSERT; retval = 0; } return (retval); } /* * read one key from input - handles chars pushed back * to input stream also */ int rl_read_key(void) { char fooarr[2 * sizeof(int)]; if (e == NULL || h == NULL) rl_initialize(); return (el_getc(e, fooarr)); } /* * reset the terminal */ /* ARGSUSED */ void rl_reset_terminal(const char *p __attribute__((__unused__))) { if (h == NULL || e == NULL) rl_initialize(); el_reset(e); } /* * insert character ``c'' back into input stream, ``count'' times */ int rl_insert(int count, int c) { char arr[2]; if (h == NULL || e == NULL) rl_initialize(); /* XXX - int -> char conversion can lose on multichars */ arr[0] = c; arr[1] = '\0'; for (; count > 0; count--) el_push(e, arr); return (0); } int rl_insert_text(const char *text) { if (!text || *text == 0) return (0); if (h == NULL || e == NULL) rl_initialize(); if (el_insertstr(e, text) < 0) return (0); return (int)strlen(text); } /*ARGSUSED*/ int rl_newline(int count, int c) { /* * Readline-4.0 appears to ignore the args. */ return rl_insert(1, '\n'); } /*ARGSUSED*/ static unsigned char rl_bind_wrapper(EditLine *el, unsigned char c) { if (map[c] == NULL) return CC_ERROR; _rl_update_pos(); (*map[c])(NULL, c); /* If rl_done was set by the above call, deal with it here */ if (rl_done) return CC_EOF; return CC_NORM; } int rl_add_defun(const char *name, Function *fun, int c) { char dest[8]; if ((size_t)c >= sizeof(map) / sizeof(map[0]) || c < 0) return -1; map[(unsigned char)c] = fun; el_set(e, EL_ADDFN, name, name, rl_bind_wrapper); vis(dest, c, VIS_WHITE|VIS_NOSLASH, 0); el_set(e, EL_BIND, dest, name); return 0; } void rl_callback_read_char() { int count = 0, done = 0; const char *buf = el_gets(e, &count); char *wbuf; if (buf == NULL || count-- <= 0) return; if (count == 0 && buf[0] == e->el_tty.t_c[TS_IO][C_EOF]) done = 1; if (buf[count] == '\n' || buf[count] == '\r') done = 2; if (done && rl_linefunc != NULL) { el_set(e, EL_UNBUFFERED, 0); if (done == 2) { if ((wbuf = strdup(buf)) != NULL) wbuf[count] = '\0'; } else wbuf = NULL; (*(void (*)(const char *))rl_linefunc)(wbuf); //el_set(e, EL_UNBUFFERED, 1); } } void rl_callback_handler_install(const char *prompt, VCPFunction *linefunc) { if (e == NULL) { rl_initialize(); } (void)rl_set_prompt(prompt); rl_linefunc = linefunc; el_set(e, EL_UNBUFFERED, 1); } void rl_callback_handler_remove(void) { el_set(e, EL_UNBUFFERED, 0); rl_linefunc = NULL; } void rl_redisplay(void) { char a[2]; a[0] = e->el_tty.t_c[TS_IO][C_REPRINT]; a[1] = '\0'; el_push(e, a); } int rl_get_previous_history(int count, int key) { char a[2]; a[0] = key; a[1] = '\0'; while (count--) el_push(e, a); return 0; } void /*ARGSUSED*/ rl_prep_terminal(int meta_flag) { el_set(e, EL_PREP_TERM, 1); } void rl_deprep_terminal(void) { el_set(e, EL_PREP_TERM, 0); } int rl_read_init_file(const char *s) { return(el_source(e, s)); } int rl_parse_and_bind(const char *line) { const char **argv; int argc; Tokenizer *tok; tok = tok_init(NULL); tok_str(tok, line, &argc, &argv); argc = el_parse(e, argc, argv); tok_end(tok); return (argc ? 1 : 0); } int rl_variable_bind(const char *var, const char *value) { /* * The proper return value is undocument, but this is what the * readline source seems to do. */ return ((el_set(e, EL_BIND, "", var, value) == -1) ? 1 : 0); } void rl_stuff_char(int c) { char buf[2]; buf[0] = c; buf[1] = '\0'; el_insertstr(e, buf); } static int _rl_event_read_char(EditLine *el, char *cp) { int n; ssize_t num_read = 0; *cp = '\0'; while (rl_event_hook) { (*rl_event_hook)(); #if defined(FIONREAD) if (ioctl(el->el_infd, FIONREAD, &n) < 0) return(-1); if (n) num_read = read(el->el_infd, cp, 1); else num_read = 0; #elif defined(F_SETFL) && defined(O_NDELAY) if ((n = fcntl(el->el_infd, F_GETFL, 0)) < 0) return(-1); if (fcntl(el->el_infd, F_SETFL, n|O_NDELAY) < 0) return(-1); num_read = read(el->el_infd, cp, 1); if (fcntl(el->el_infd, F_SETFL, n)) return(-1); #else /* not non-blocking, but what you gonna do? */ num_read = read(el->el_infd, cp, 1); return(-1); #endif if (num_read < 0 && errno == EAGAIN) continue; if (num_read == 0) continue; break; } if (!rl_event_hook) el_set(el, EL_GETCFN, EL_BUILTIN_GETCFN); return (int)num_read; } static void _rl_update_pos(void) { const LineInfo *li = el_line(e); rl_point = (int)(li->cursor - li->buffer); rl_end = (int)(li->lastchar - li->buffer); } void rl_get_screen_size(int *rows, int *cols) { if (rows) el_get(e, EL_GETTC, "li", rows); if (cols) el_get(e, EL_GETTC, "co", cols); } void rl_set_screen_size(int rows, int cols) { char buf[64]; (void)snprintf(buf, sizeof(buf), "%d", rows); el_set(e, EL_SETTC, "li", buf); (void)snprintf(buf, sizeof(buf), "%d", cols); el_set(e, EL_SETTC, "co", buf); } char ** rl_completion_matches(const char *str, rl_compentry_func_t *fun) { size_t len, max, i, j, min; char **list, *match, *a, *b; len = 1; max = 10; if ((list = malloc(max * sizeof(*list))) == NULL) return NULL; while ((match = (*fun)(str, (int)(len - 1))) != NULL) { list[len++] = match; if (len == max) { char **nl; max += 10; if ((nl = realloc(list, max * sizeof(*nl))) == NULL) goto out; list = nl; } } if (len == 1) goto out; list[len] = NULL; if (len == 2) { if ((list[0] = strdup(list[1])) == NULL) goto out; return list; } qsort(&list[1], len - 1, sizeof(*list), (int (*)(const void *, const void *)) strcmp); min = SIZE_T_MAX; for (i = 1, a = list[i]; i < len - 1; i++, a = b) { b = list[i + 1]; for (j = 0; a[j] && a[j] == b[j]; j++) continue; if (min > j) min = j; } if (min == 0 && *str) { if ((list[0] = strdup(str)) == NULL) goto out; } else { if ((list[0] = malloc(min + 1)) == NULL) goto out; (void)memcpy(list[0], list[1], min); list[0][min] = '\0'; } return list; out: free(list); return NULL; } char * rl_filename_completion_function (const char *text, int state) { return fn_filename_completion_function(text, state); } void rl_forced_update_display(void) { el_set(e, EL_REFRESH); } int _rl_abort_internal(void) { el_beep(e); longjmp(topbuf, 1); /*NOTREACHED*/ } int _rl_qsort_string_compare(char **s1, char **s2) { return strcoll(*s1, *s2); } HISTORY_STATE * history_get_history_state(void) { HISTORY_STATE *hs; if ((hs = malloc(sizeof(HISTORY_STATE))) == NULL) return (NULL); hs->length = history_length; return (hs); } int /*ARGSUSED*/ rl_kill_text(int from, int to) { return 0; } Keymap rl_make_bare_keymap(void) { return NULL; } Keymap rl_get_keymap(void) { return NULL; } void /*ARGSUSED*/ rl_set_keymap(Keymap k) { } int /*ARGSUSED*/ rl_generic_bind(int type, const char * keyseq, const char * data, Keymap k) { return 0; } int /*ARGSUSED*/ rl_bind_key_in_map(int key, rl_command_func_t *fun, Keymap k) { return 0; } /* unsupported, but needed by python */ void rl_cleanup_after_signal(void) { } clt/falcon/editline/src/refresh.c000066400000000000000000000741501176363201700173050ustar00rootroot00000000000000/* $NetBSD: refresh.c,v 1.32 2009/07/17 12:28:27 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)refresh.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: refresh.c,v 1.32 2009/07/17 12:28:27 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * refresh.c: Lower level screen refreshing functions */ #include #include #include #include #include "el.h" private void re_nextline(EditLine *); private void re_addc(EditLine *, int); private void re_update_line(EditLine *, char *, char *, int); private void re_insert (EditLine *, char *, int, int, char *, int); private void re_delete(EditLine *, char *, int, int, int); private void re_fastputc(EditLine *, int); private void re_clear_eol(EditLine *, int, int, int); private void re__strncopy(char *, char *, size_t); private void re__copy_and_pad(char *, const char *, size_t); #ifdef DEBUG_REFRESH private void re_printstr(EditLine *, const char *, char *, char *); #define __F el->el_errfile #define ELRE_ASSERT(a, b, c) do \ if (/*CONSTCOND*/ a) { \ (void) fprintf b; \ c; \ } \ while (/*CONSTCOND*/0) #define ELRE_DEBUG(a, b) ELRE_ASSERT(a,b,;) /* re_printstr(): * Print a string on the debugging pty */ private void re_printstr(EditLine *el, const char *str, char *f, char *t) { ELRE_DEBUG(1, (__F, "%s:\"", str)); while (f < t) ELRE_DEBUG(1, (__F, "%c", *f++ & 0177)); ELRE_DEBUG(1, (__F, "\"\r\n")); } #else #define ELRE_ASSERT(a, b, c) #define ELRE_DEBUG(a, b) #endif /* re_nextline(): * Move to the next line or scroll */ private void re_nextline(EditLine *el) { el->el_refresh.r_cursor.h = 0; /* reset it. */ /* * If we would overflow (input is longer than terminal size), * emulate scroll by dropping first line and shuffling the rest. * We do this via pointer shuffling - it's safe in this case * and we avoid memcpy(). */ if (el->el_refresh.r_cursor.v + 1 >= el->el_term.t_size.v) { int i, lins = el->el_term.t_size.v; char *firstline = el->el_vdisplay[0]; for(i = 1; i < lins; i++) el->el_vdisplay[i - 1] = el->el_vdisplay[i]; firstline[0] = '\0'; /* empty the string */ el->el_vdisplay[i - 1] = firstline; } else el->el_refresh.r_cursor.v++; ELRE_ASSERT(el->el_refresh.r_cursor.v >= el->el_term.t_size.v, (__F, "\r\nre_putc: overflow! r_cursor.v == %d > %d\r\n", el->el_refresh.r_cursor.v, el->el_term.t_size.v), abort()); } /* re_addc(): * Draw c, expanding tabs, control chars etc. */ private void re_addc(EditLine *el, int c) { if (isprint(c)) { re_putc(el, c, 1); return; } if (c == '\n') { /* expand the newline */ int oldv = el->el_refresh.r_cursor.v; re_putc(el, '\0', 0); /* assure end of line */ if (oldv == el->el_refresh.r_cursor.v) /* XXX */ re_nextline(el); return; } if (c == '\t') { /* expand the tab */ for (;;) { re_putc(el, ' ', 1); if ((el->el_refresh.r_cursor.h & 07) == 0) break; /* go until tab stop */ } } else if (iscntrl(c)) { re_putc(el, '^', 1); if (c == '\177') re_putc(el, '?', 1); else /* uncontrolify it; works only for iso8859-1 like sets */ re_putc(el, (c | 0100), 1); } else { re_putc(el, '\\', 1); re_putc(el, (int) ((((unsigned int) c >> 6) & 07) + '0'), 1); re_putc(el, (int) ((((unsigned int) c >> 3) & 07) + '0'), 1); re_putc(el, (c & 07) + '0', 1); } } /* re_putc(): * Draw the character given */ protected void re_putc(EditLine *el, int c, int shift) { ELRE_DEBUG(1, (__F, "printing %3.3o '%c'\r\n", c, c)); el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_refresh.r_cursor.h] = c; if (!shift) return; el->el_refresh.r_cursor.h++; /* advance to next place */ if (el->el_refresh.r_cursor.h >= el->el_term.t_size.h) { /* assure end of line */ el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_term.t_size.h] = '\0'; re_nextline(el); } } /* re_refresh(): * draws the new virtual screen image from the current input * line, then goes line-by-line changing the real image to the new * virtual image. The routine to re-draw a line can be replaced * easily in hopes of a smarter one being placed there. */ protected void re_refresh(EditLine *el) { int i, rhdiff; char *cp, *st; coord_t cur; #ifdef notyet size_t termsz; #endif ELRE_DEBUG(1, (__F, "el->el_line.buffer = :%s:\r\n", el->el_line.buffer)); /* reset the Drawing cursor */ el->el_refresh.r_cursor.h = 0; el->el_refresh.r_cursor.v = 0; /* temporarily draw rprompt to calculate its size */ prompt_print(el, EL_RPROMPT); /* reset the Drawing cursor */ el->el_refresh.r_cursor.h = 0; el->el_refresh.r_cursor.v = 0; if (el->el_line.cursor >= el->el_line.lastchar) { if (el->el_map.current == el->el_map.alt && el->el_line.lastchar != el->el_line.buffer) el->el_line.cursor = el->el_line.lastchar - 1; else el->el_line.cursor = el->el_line.lastchar; } cur.h = -1; /* set flag in case I'm not set */ cur.v = 0; prompt_print(el, EL_PROMPT); /* draw the current input buffer */ #if notyet termsz = el->el_term.t_size.h * el->el_term.t_size.v; if (el->el_line.lastchar - el->el_line.buffer > termsz) { /* * If line is longer than terminal, process only part * of line which would influence display. */ size_t rem = (el->el_line.lastchar-el->el_line.buffer)%termsz; st = el->el_line.lastchar - rem - (termsz - (((rem / el->el_term.t_size.v) - 1) * el->el_term.t_size.v)); } else #endif st = el->el_line.buffer; for (cp = st; cp < el->el_line.lastchar; cp++) { if (cp == el->el_line.cursor) { /* save for later */ cur.h = el->el_refresh.r_cursor.h; cur.v = el->el_refresh.r_cursor.v; } re_addc(el, (unsigned char) *cp); } if (cur.h == -1) { /* if I haven't been set yet, I'm at the end */ cur.h = el->el_refresh.r_cursor.h; cur.v = el->el_refresh.r_cursor.v; } rhdiff = el->el_term.t_size.h - el->el_refresh.r_cursor.h - el->el_rprompt.p_pos.h; if (el->el_rprompt.p_pos.h && !el->el_rprompt.p_pos.v && !el->el_refresh.r_cursor.v && rhdiff > 1) { /* * have a right-hand side prompt that will fit * on the end of the first line with at least * one character gap to the input buffer. */ while (--rhdiff > 0) /* pad out with spaces */ re_putc(el, ' ', 1); prompt_print(el, EL_RPROMPT); } else { el->el_rprompt.p_pos.h = 0; /* flag "not using rprompt" */ el->el_rprompt.p_pos.v = 0; } re_putc(el, '\0', 0); /* make line ended with NUL, no cursor shift */ el->el_refresh.r_newcv = el->el_refresh.r_cursor.v; ELRE_DEBUG(1, (__F, "term.h=%d vcur.h=%d vcur.v=%d vdisplay[0]=\r\n:%80.80s:\r\n", el->el_term.t_size.h, el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v, el->el_vdisplay[0])); ELRE_DEBUG(1, (__F, "updating %d lines.\r\n", el->el_refresh.r_newcv)); for (i = 0; i <= el->el_refresh.r_newcv; i++) { /* NOTE THAT re_update_line MAY CHANGE el_display[i] */ re_update_line(el, el->el_display[i], el->el_vdisplay[i], i); /* * Copy the new line to be the current one, and pad out with * spaces to the full width of the terminal so that if we try * moving the cursor by writing the character that is at the * end of the screen line, it won't be a NUL or some old * leftover stuff. */ re__copy_and_pad(el->el_display[i], el->el_vdisplay[i], (size_t) el->el_term.t_size.h); } ELRE_DEBUG(1, (__F, "\r\nel->el_refresh.r_cursor.v=%d,el->el_refresh.r_oldcv=%d i=%d\r\n", el->el_refresh.r_cursor.v, el->el_refresh.r_oldcv, i)); if (el->el_refresh.r_oldcv > el->el_refresh.r_newcv) for (; i <= el->el_refresh.r_oldcv; i++) { term_move_to_line(el, i); term_move_to_char(el, 0); term_clear_EOL(el, (int) strlen(el->el_display[i])); #ifdef DEBUG_REFRESH term_overwrite(el, "C\b", (size_t)2); #endif /* DEBUG_REFRESH */ el->el_display[i][0] = '\0'; } el->el_refresh.r_oldcv = el->el_refresh.r_newcv; /* set for next time */ ELRE_DEBUG(1, (__F, "\r\ncursor.h = %d, cursor.v = %d, cur.h = %d, cur.v = %d\r\n", el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v, cur.h, cur.v)); term_move_to_line(el, cur.v); /* go to where the cursor is */ term_move_to_char(el, cur.h); } /* re_goto_bottom(): * used to go to last used screen line */ protected void re_goto_bottom(EditLine *el) { term_move_to_line(el, el->el_refresh.r_oldcv); term__putc(el, '\n'); re_clear_display(el); term__flush(el); } /* re_insert(): * insert num characters of s into d (in front of the character) * at dat, maximum length of d is dlen */ private void /*ARGSUSED*/ re_insert(EditLine *el __attribute__((__unused__)), char *d, int dat, int dlen, char *s, int num) { char *a, *b; if (num <= 0) return; if (num > dlen - dat) num = dlen - dat; ELRE_DEBUG(1, (__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n", num, dat, dlen, d)); ELRE_DEBUG(1, (__F, "s == \"%s\"\n", s)); /* open up the space for num chars */ if (num > 0) { b = d + dlen - 1; a = b - num; while (a >= &d[dat]) *b-- = *a--; d[dlen] = '\0'; /* just in case */ } ELRE_DEBUG(1, (__F, "re_insert() after insert: %d at %d max %d, d == \"%s\"\n", num, dat, dlen, d)); ELRE_DEBUG(1, (__F, "s == \"%s\"\n", s)); /* copy the characters */ for (a = d + dat; (a < d + dlen) && (num > 0); num--) *a++ = *s++; ELRE_DEBUG(1, (__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n", num, dat, dlen, d, s)); ELRE_DEBUG(1, (__F, "s == \"%s\"\n", s)); } /* re_delete(): * delete num characters d at dat, maximum length of d is dlen */ private void /*ARGSUSED*/ re_delete(EditLine *el __attribute__((__unused__)), char *d, int dat, int dlen, int num) { char *a, *b; if (num <= 0) return; if (dat + num >= dlen) { d[dat] = '\0'; return; } ELRE_DEBUG(1, (__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n", num, dat, dlen, d)); /* open up the space for num chars */ if (num > 0) { b = d + dat; a = b + num; while (a < &d[dlen]) *b++ = *a++; d[dlen] = '\0'; /* just in case */ } ELRE_DEBUG(1, (__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n", num, dat, dlen, d)); } /* re__strncopy(): * Like strncpy without padding. */ private void re__strncopy(char *a, char *b, size_t n) { while (n-- && *b) *a++ = *b++; } /* re_clear_eol(): * Find the number of characters we need to clear till the end of line * in order to make sure that we have cleared the previous contents of * the line. fx and sx is the number of characters inserted or deleted * int the first or second diff, diff is the difference between the * number of characters between the new and old line. */ private void re_clear_eol(EditLine *el, int fx, int sx, int diff) { ELRE_DEBUG(1, (__F, "re_clear_eol sx %d, fx %d, diff %d\n", sx, fx, diff)); if (fx < 0) fx = -fx; if (sx < 0) sx = -sx; if (fx > diff) diff = fx; if (sx > diff) diff = sx; ELRE_DEBUG(1, (__F, "re_clear_eol %d\n", diff)); term_clear_EOL(el, diff); } /***************************************************************** re_update_line() is based on finding the middle difference of each line on the screen; vis: /old first difference /beginning of line | /old last same /old EOL v v v v old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as new: eddie> Oh, my little buggy says to me, as lurgid as ^ ^ ^ ^ \beginning of line | \new last same \new end of line \new first difference all are character pointers for the sake of speed. Special cases for no differences, as well as for end of line additions must be handled. **************************************************************** */ /* Minimum at which doing an insert it "worth it". This should be about * half the "cost" of going into insert mode, inserting a character, and * going back out. This should really be calculated from the termcap * data... For the moment, a good number for ANSI terminals. */ #define MIN_END_KEEP 4 private void re_update_line(EditLine *el, char *old, char *new, int i) { char *o, *n, *p, c; char *ofd, *ols, *oe, *nfd, *nls, *ne; char *osb, *ose, *nsb, *nse; int fx, sx; size_t len; /* * find first diff */ for (o = old, n = new; *o && (*o == *n); o++, n++) continue; ofd = o; nfd = n; /* * Find the end of both old and new */ while (*o) o++; /* * Remove any trailing blanks off of the end, being careful not to * back up past the beginning. */ while (ofd < o) { if (o[-1] != ' ') break; o--; } oe = o; *oe = '\0'; while (*n) n++; /* remove blanks from end of new */ while (nfd < n) { if (n[-1] != ' ') break; n--; } ne = n; *ne = '\0'; /* * if no diff, continue to next line of redraw */ if (*ofd == '\0' && *nfd == '\0') { ELRE_DEBUG(1, (__F, "no difference.\r\n")); return; } /* * find last same pointer */ while ((o > ofd) && (n > nfd) && (*--o == *--n)) continue; ols = ++o; nls = ++n; /* * find same begining and same end */ osb = ols; nsb = nls; ose = ols; nse = nls; /* * case 1: insert: scan from nfd to nls looking for *ofd */ if (*ofd) { for (c = *ofd, n = nfd; n < nls; n++) { if (c == *n) { for (o = ofd, p = n; p < nls && o < ols && *o == *p; o++, p++) continue; /* * if the new match is longer and it's worth * keeping, then we take it */ if (((nse - nsb) < (p - n)) && (2 * (p - n) > n - nfd)) { nsb = n; nse = p; osb = ofd; ose = o; } } } } /* * case 2: delete: scan from ofd to ols looking for *nfd */ if (*nfd) { for (c = *nfd, o = ofd; o < ols; o++) { if (c == *o) { for (n = nfd, p = o; p < ols && n < nls && *p == *n; p++, n++) continue; /* * if the new match is longer and it's worth * keeping, then we take it */ if (((ose - osb) < (p - o)) && (2 * (p - o) > o - ofd)) { nsb = nfd; nse = n; osb = o; ose = p; } } } } /* * Pragmatics I: If old trailing whitespace or not enough characters to * save to be worth it, then don't save the last same info. */ if ((oe - ols) < MIN_END_KEEP) { ols = oe; nls = ne; } /* * Pragmatics II: if the terminal isn't smart enough, make the data * dumber so the smart update doesn't try anything fancy */ /* * fx is the number of characters we need to insert/delete: in the * beginning to bring the two same begins together */ fx = (int)((nsb - nfd) - (osb - ofd)); /* * sx is the number of characters we need to insert/delete: in the * end to bring the two same last parts together */ sx = (int)((nls - nse) - (ols - ose)); if (!EL_CAN_INSERT) { if (fx > 0) { osb = ols; ose = ols; nsb = nls; nse = nls; } if (sx > 0) { ols = oe; nls = ne; } if ((ols - ofd) < (nls - nfd)) { ols = oe; nls = ne; } } if (!EL_CAN_DELETE) { if (fx < 0) { osb = ols; ose = ols; nsb = nls; nse = nls; } if (sx < 0) { ols = oe; nls = ne; } if ((ols - ofd) > (nls - nfd)) { ols = oe; nls = ne; } } /* * Pragmatics III: make sure the middle shifted pointers are correct if * they don't point to anything (we may have moved ols or nls). */ /* if the change isn't worth it, don't bother */ /* was: if (osb == ose) */ if ((ose - osb) < MIN_END_KEEP) { osb = ols; ose = ols; nsb = nls; nse = nls; } /* * Now that we are done with pragmatics we recompute fx, sx */ fx = (int)((nsb - nfd) - (osb - ofd)); sx = (int)((nls - nse) - (ols - ose)); ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx)); ELRE_DEBUG(1, (__F, "ofd %d, osb %d, ose %d, ols %d, oe %d\n", ofd - old, osb - old, ose - old, ols - old, oe - old)); ELRE_DEBUG(1, (__F, "nfd %d, nsb %d, nse %d, nls %d, ne %d\n", nfd - new, nsb - new, nse - new, nls - new, ne - new)); ELRE_DEBUG(1, (__F, "xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n")); ELRE_DEBUG(1, (__F, "xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n")); #ifdef DEBUG_REFRESH re_printstr(el, "old- oe", old, oe); re_printstr(el, "new- ne", new, ne); re_printstr(el, "old-ofd", old, ofd); re_printstr(el, "new-nfd", new, nfd); re_printstr(el, "ofd-osb", ofd, osb); re_printstr(el, "nfd-nsb", nfd, nsb); re_printstr(el, "osb-ose", osb, ose); re_printstr(el, "nsb-nse", nsb, nse); re_printstr(el, "ose-ols", ose, ols); re_printstr(el, "nse-nls", nse, nls); re_printstr(el, "ols- oe", ols, oe); re_printstr(el, "nls- ne", nls, ne); #endif /* DEBUG_REFRESH */ /* * el_cursor.v to this line i MUST be in this routine so that if we * don't have to change the line, we don't move to it. el_cursor.h to * first diff char */ term_move_to_line(el, i); /* * at this point we have something like this: * * /old /ofd /osb /ose /ols /oe * v.....................v v..................v v........v * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as * ^.....................^ ^..................^ ^........^ * \new \nfd \nsb \nse \nls \ne * * fx is the difference in length between the chars between nfd and * nsb, and the chars between ofd and osb, and is thus the number of * characters to delete if < 0 (new is shorter than old, as above), * or insert (new is longer than short). * * sx is the same for the second differences. */ /* * if we have a net insert on the first difference, AND inserting the * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful * character (which is ne if nls != ne, otherwise is nse) off the edge * of the screen (el->el_term.t_size.h) else we do the deletes first * so that we keep everything we need to. */ /* * if the last same is the same like the end, there is no last same * part, otherwise we want to keep the last same part set p to the * last useful old character */ p = (ols != oe) ? oe : ose; /* * if (There is a diffence in the beginning) && (we need to insert * characters) && (the number of characters to insert is less than * the term width) * We need to do an insert! * else if (we need to delete characters) * We need to delete characters! * else * No insert or delete */ if ((nsb != nfd) && fx > 0 && ((p - old) + fx <= el->el_term.t_size.h)) { ELRE_DEBUG(1, (__F, "first diff insert at %d...\r\n", nfd - new)); /* * Move to the first char to insert, where the first diff is. */ term_move_to_char(el, (int)(nfd - new)); /* * Check if we have stuff to keep at end */ if (nsb != ne) { ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); /* * insert fx chars of new starting at nfd */ if (fx > 0) { ELRE_DEBUG(!EL_CAN_INSERT, (__F, "ERROR: cannot insert in early first diff\n")); term_insertwrite(el, nfd, fx); re_insert(el, old, (int)(ofd - old), el->el_term.t_size.h, nfd, fx); } /* * write (nsb-nfd) - fx chars of new starting at * (nfd + fx) */ len = (size_t) ((nsb - nfd) - fx); term_overwrite(el, (nfd + fx), len); re__strncopy(ofd + fx, nfd + fx, len); } else { ELRE_DEBUG(1, (__F, "without anything to save\r\n")); len = (size_t)(nsb - nfd); term_overwrite(el, nfd, len); re__strncopy(ofd, nfd, len); /* * Done */ return; } } else if (fx < 0) { ELRE_DEBUG(1, (__F, "first diff delete at %d...\r\n", ofd - old)); /* * move to the first char to delete where the first diff is */ term_move_to_char(el, (int)(ofd - old)); /* * Check if we have stuff to save */ if (osb != oe) { ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n")); /* * fx is less than zero *always* here but we check * for code symmetry */ if (fx < 0) { ELRE_DEBUG(!EL_CAN_DELETE, (__F, "ERROR: cannot delete in first diff\n")); term_deletechars(el, -fx); re_delete(el, old, (int)(ofd - old), el->el_term.t_size.h, -fx); } /* * write (nsb-nfd) chars of new starting at nfd */ len = (size_t) (nsb - nfd); term_overwrite(el, nfd, len); re__strncopy(ofd, nfd, len); } else { ELRE_DEBUG(1, (__F, "but with nothing left to save\r\n")); /* * write (nsb-nfd) chars of new starting at nfd */ term_overwrite(el, nfd, (size_t)(nsb - nfd)); re_clear_eol(el, fx, sx, (int)((oe - old) - (ne - new))); /* * Done */ return; } } else fx = 0; if (sx < 0 && (ose - old) + fx < el->el_term.t_size.h) { ELRE_DEBUG(1, (__F, "second diff delete at %d...\r\n", (ose - old) + fx)); /* * Check if we have stuff to delete */ /* * fx is the number of characters inserted (+) or deleted (-) */ term_move_to_char(el, (int)((ose - old) + fx)); /* * Check if we have stuff to save */ if (ols != oe) { ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n")); /* * Again a duplicate test. */ if (sx < 0) { ELRE_DEBUG(!EL_CAN_DELETE, (__F, "ERROR: cannot delete in second diff\n")); term_deletechars(el, -sx); } /* * write (nls-nse) chars of new starting at nse */ term_overwrite(el, nse, (size_t)(nls - nse)); } else { ELRE_DEBUG(1, (__F, "but with nothing left to save\r\n")); term_overwrite(el, nse, (size_t)(nls - nse)); re_clear_eol(el, fx, sx, (int)((oe - old) - (ne - new))); } } /* * if we have a first insert AND WE HAVEN'T ALREADY DONE IT... */ if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) { ELRE_DEBUG(1, (__F, "late first diff insert at %d...\r\n", nfd - new)); term_move_to_char(el, (int)(nfd - new)); /* * Check if we have stuff to keep at the end */ if (nsb != ne) { ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); /* * We have to recalculate fx here because we set it * to zero above as a flag saying that we hadn't done * an early first insert. */ fx = (int)((nsb - nfd) - (osb - ofd)); if (fx > 0) { /* * insert fx chars of new starting at nfd */ ELRE_DEBUG(!EL_CAN_INSERT, (__F, "ERROR: cannot insert in late first diff\n")); term_insertwrite(el, nfd, fx); re_insert(el, old, (int)(ofd - old), el->el_term.t_size.h, nfd, fx); } /* * write (nsb-nfd) - fx chars of new starting at * (nfd + fx) */ len = (size_t) ((nsb - nfd) - fx); term_overwrite(el, (nfd + fx), len); re__strncopy(ofd + fx, nfd + fx, len); } else { ELRE_DEBUG(1, (__F, "without anything to save\r\n")); len = (size_t) (nsb - nfd); term_overwrite(el, nfd, len); re__strncopy(ofd, nfd, len); } } /* * line is now NEW up to nse */ if (sx >= 0) { ELRE_DEBUG(1, (__F, "second diff insert at %d...\r\n", (int)(nse - new))); term_move_to_char(el, (int)(nse - new)); if (ols != oe) { ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n")); if (sx > 0) { /* insert sx chars of new starting at nse */ ELRE_DEBUG(!EL_CAN_INSERT, (__F, "ERROR: cannot insert in second diff\n")); term_insertwrite(el, nse, sx); } /* * write (nls-nse) - sx chars of new starting at * (nse + sx) */ term_overwrite(el, (nse + sx), (size_t)((nls - nse) - sx)); } else { ELRE_DEBUG(1, (__F, "without anything to save\r\n")); term_overwrite(el, nse, (size_t)(nls - nse)); /* * No need to do a clear-to-end here because we were * doing a second insert, so we will have over * written all of the old string. */ } } ELRE_DEBUG(1, (__F, "done.\r\n")); } /* re__copy_and_pad(): * Copy string and pad with spaces */ private void re__copy_and_pad(char *dst, const char *src, size_t width) { size_t i; for (i = 0; i < width; i++) { if (*src == '\0') break; *dst++ = *src++; } for (; i < width; i++) *dst++ = ' '; *dst = '\0'; } /* re_refresh_cursor(): * Move to the new cursor position */ protected void re_refresh_cursor(EditLine *el) { char *cp, c; int h, v, th; if (el->el_line.cursor >= el->el_line.lastchar) { if (el->el_map.current == el->el_map.alt && el->el_line.lastchar != el->el_line.buffer) el->el_line.cursor = el->el_line.lastchar - 1; else el->el_line.cursor = el->el_line.lastchar; } /* first we must find where the cursor is... */ h = el->el_prompt.p_pos.h; v = el->el_prompt.p_pos.v; th = el->el_term.t_size.h; /* optimize for speed */ /* do input buffer to el->el_line.cursor */ for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) { c = *cp; switch (c) { case '\n': /* handle newline in data part too */ h = 0; v++; break; case '\t': /* if a tab, to next tab stop */ while (++h & 07) continue; break; default: if (iscntrl((unsigned char) c)) h += 2; /* ^x */ else if (!isprint((unsigned char) c)) h += 4; /* octal \xxx */ else h++; break; } if (h >= th) { /* check, extra long tabs picked up here also */ h -= th; v++; } } /* now go there */ term_move_to_line(el, v); term_move_to_char(el, h); term__flush(el); } /* re_fastputc(): * Add a character fast. */ private void re_fastputc(EditLine *el, int c) { term__putc(el, c); el->el_display[el->el_cursor.v][el->el_cursor.h++] = c; if (el->el_cursor.h >= el->el_term.t_size.h) { /* if we must overflow */ el->el_cursor.h = 0; /* * If we would overflow (input is longer than terminal size), * emulate scroll by dropping first line and shuffling the rest. * We do this via pointer shuffling - it's safe in this case * and we avoid memcpy(). */ if (el->el_cursor.v + 1 >= el->el_term.t_size.v) { int i, lins = el->el_term.t_size.v; char *firstline = el->el_display[0]; for(i = 1; i < lins; i++) el->el_display[i - 1] = el->el_display[i]; re__copy_and_pad(firstline, "", 0); el->el_display[i - 1] = firstline; } else { el->el_cursor.v++; el->el_refresh.r_oldcv++; } if (EL_HAS_AUTO_MARGINS) { if (EL_HAS_MAGIC_MARGINS) { term__putc(el, ' '); term__putc(el, '\b'); } } else { term__putc(el, '\r'); term__putc(el, '\n'); } } } /* re_fastaddc(): * we added just one char, handle it fast. * Assumes that screen cursor == real cursor */ protected void re_fastaddc(EditLine *el) { char c; int rhdiff; c = el->el_line.cursor[-1]; if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) { re_refresh(el); /* too hard to handle */ return; } rhdiff = el->el_term.t_size.h - el->el_cursor.h - el->el_rprompt.p_pos.h; if (el->el_rprompt.p_pos.h && rhdiff < 3) { re_refresh(el); /* clear out rprompt if less than 1 char gap */ return; } /* else (only do at end of line, no TAB) */ if (iscntrl((unsigned char) c)) { /* if control char, do caret */ char mc = (c == '\177') ? '?' : (c | 0100); re_fastputc(el, '^'); re_fastputc(el, mc); } else if (isprint((unsigned char) c)) { /* normal char */ re_fastputc(el, c); } else { re_fastputc(el, '\\'); re_fastputc(el, (int)(((((unsigned int)c) >> 6) & 3) + '0')); re_fastputc(el, (int)(((((unsigned int)c) >> 3) & 7) + '0')); re_fastputc(el, (c & 7) + '0'); } term__flush(el); } /* re_clear_display(): * clear the screen buffers so that new new prompt starts fresh. */ protected void re_clear_display(EditLine *el) { int i; el->el_cursor.v = 0; el->el_cursor.h = 0; for (i = 0; i < el->el_term.t_size.v; i++) el->el_display[i][0] = '\0'; el->el_refresh.r_oldcv = 0; } /* re_clear_lines(): * Make sure all lines are *really* blank */ protected void re_clear_lines(EditLine *el) { if (EL_CAN_CEOL) { int i; term_move_to_char(el, 0); for (i = 0; i <= el->el_refresh.r_oldcv; i++) { /* for each line on the screen */ term_move_to_line(el, i); term_clear_EOL(el, el->el_term.t_size.h); } term_move_to_line(el, 0); } else { term_move_to_line(el, el->el_refresh.r_oldcv); /* go to last line */ term__putc(el, '\r'); /* go to BOL */ term__putc(el, '\n'); /* go to new line */ } } clt/falcon/editline/src/refresh.h000066400000000000000000000045321176363201700173070ustar00rootroot00000000000000/* $NetBSD: refresh.h,v 1.5 2003/08/07 16:44:33 agc Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)refresh.h 8.1 (Berkeley) 6/4/93 */ /* * el.refresh.h: Screen refresh functions */ #ifndef _h_el_refresh #define _h_el_refresh #include "histedit.h" typedef struct { coord_t r_cursor; /* Refresh cursor position */ int r_oldcv; /* Vertical locations */ int r_newcv; } el_refresh_t; protected void re_putc(EditLine *, int, int); protected void re_clear_lines(EditLine *); protected void re_clear_display(EditLine *); protected void re_refresh(EditLine *); protected void re_refresh_cursor(EditLine *); protected void re_fastaddc(EditLine *); protected void re_goto_bottom(EditLine *); #endif /* _h_el_refresh */ clt/falcon/editline/src/search.c000066400000000000000000000355361176363201700171210ustar00rootroot00000000000000/* $NetBSD: search.c,v 1.21 2009/02/15 21:55:23 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)search.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: search.c,v 1.21 2009/02/15 21:55:23 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * search.c: History and character search functions */ #include #include #if defined(REGEX) #include #elif defined(REGEXP) #include #endif #include "el.h" /* * Adjust cursor in vi mode to include the character under it */ #define EL_CURSOR(el) \ ((el)->el_line.cursor + (((el)->el_map.type == MAP_VI) && \ ((el)->el_map.current == (el)->el_map.alt))) /* search_init(): * Initialize the search stuff */ protected int search_init(EditLine *el) { el->el_search.patbuf = (char *) el_malloc(EL_BUFSIZ); if (el->el_search.patbuf == NULL) return (-1); el->el_search.patlen = 0; el->el_search.patdir = -1; el->el_search.chacha = '\0'; el->el_search.chadir = CHAR_FWD; el->el_search.chatflg = 0; return (0); } /* search_end(): * Initialize the search stuff */ protected void search_end(EditLine *el) { el_free((ptr_t) el->el_search.patbuf); el->el_search.patbuf = NULL; } #ifdef REGEXP /* regerror(): * Handle regular expression errors */ public void /*ARGSUSED*/ regerror(const char *msg) { } #endif /* el_match(): * Return if string matches pattern */ protected int el_match(const char *str, const char *pat) { #if defined (REGEX) regex_t re; int rv; #elif defined (REGEXP) regexp *rp; int rv; #else extern char *re_comp(const char *); extern int re_exec(const char *); #endif if (strstr(str, pat) != NULL) return (1); #if defined(REGEX) if (regcomp(&re, pat, 0) == 0) { rv = regexec(&re, str, 0, NULL, 0) == 0; regfree(&re); } else { rv = 0; } return (rv); #elif defined(REGEXP) if ((re = regcomp(pat)) != NULL) { rv = regexec(re, str); free((ptr_t) re); } else { rv = 0; } return (rv); #else if (re_comp(pat) != NULL) return (0); else return (re_exec(str) == 1); #endif } /* c_hmatch(): * return True if the pattern matches the prefix */ protected int c_hmatch(EditLine *el, const char *str) { #ifdef SDEBUG (void) fprintf(el->el_errfile, "match `%s' with `%s'\n", el->el_search.patbuf, str); #endif /* SDEBUG */ return (el_match(str, el->el_search.patbuf)); } /* c_setpat(): * Set the history seatch pattern */ protected void c_setpat(EditLine *el) { if (el->el_state.lastcmd != ED_SEARCH_PREV_HISTORY && el->el_state.lastcmd != ED_SEARCH_NEXT_HISTORY) { el->el_search.patlen = EL_CURSOR(el) - el->el_line.buffer; if (el->el_search.patlen >= EL_BUFSIZ) el->el_search.patlen = EL_BUFSIZ - 1; if (el->el_search.patlen != 0) { (void) strncpy(el->el_search.patbuf, el->el_line.buffer, el->el_search.patlen); el->el_search.patbuf[el->el_search.patlen] = '\0'; } else el->el_search.patlen = strlen(el->el_search.patbuf); } #ifdef SDEBUG (void) fprintf(el->el_errfile, "\neventno = %d\n", el->el_history.eventno); (void) fprintf(el->el_errfile, "patlen = %d\n", el->el_search.patlen); (void) fprintf(el->el_errfile, "patbuf = \"%s\"\n", el->el_search.patbuf); (void) fprintf(el->el_errfile, "cursor %d lastchar %d\n", EL_CURSOR(el) - el->el_line.buffer, el->el_line.lastchar - el->el_line.buffer); #endif } /* ce_inc_search(): * Emacs incremental search */ protected el_action_t ce_inc_search(EditLine *el, int dir) { static const char STRfwd[] = {'f', 'w', 'd', '\0'}, STRbck[] = {'b', 'c', 'k', '\0'}; static char pchar = ':';/* ':' = normal, '?' = failed */ static char endcmd[2] = {'\0', '\0'}; char ch, *ocursor = el->el_line.cursor, oldpchar = pchar; const char *cp; el_action_t ret = CC_NORM; int ohisteventno = el->el_history.eventno; size_t oldpatlen = el->el_search.patlen; int newdir = dir; int done, redo; if (el->el_line.lastchar + sizeof(STRfwd) / sizeof(char) + 2 + el->el_search.patlen >= el->el_line.limit) return (CC_ERROR); for (;;) { if (el->el_search.patlen == 0) { /* first round */ pchar = ':'; #ifdef ANCHOR #define LEN 2 el->el_search.patbuf[el->el_search.patlen++] = '.'; el->el_search.patbuf[el->el_search.patlen++] = '*'; #else #define LEN 0 #endif } done = redo = 0; *el->el_line.lastchar++ = '\n'; for (cp = (newdir == ED_SEARCH_PREV_HISTORY) ? STRbck : STRfwd; *cp; *el->el_line.lastchar++ = *cp++) continue; *el->el_line.lastchar++ = pchar; for (cp = &el->el_search.patbuf[LEN]; cp < &el->el_search.patbuf[el->el_search.patlen]; *el->el_line.lastchar++ = *cp++) continue; *el->el_line.lastchar = '\0'; re_refresh(el); if (el_getc(el, &ch) != 1) return (ed_end_of_file(el, 0)); switch (el->el_map.current[(unsigned char) ch]) { case ED_INSERT: case ED_DIGIT: if (el->el_search.patlen >= EL_BUFSIZ - LEN) term_beep(el); else { el->el_search.patbuf[el->el_search.patlen++] = ch; *el->el_line.lastchar++ = ch; *el->el_line.lastchar = '\0'; re_refresh(el); } break; case EM_INC_SEARCH_NEXT: newdir = ED_SEARCH_NEXT_HISTORY; redo++; break; case EM_INC_SEARCH_PREV: newdir = ED_SEARCH_PREV_HISTORY; redo++; break; case EM_DELETE_PREV_CHAR: case ED_DELETE_PREV_CHAR: if (el->el_search.patlen > LEN) done++; else term_beep(el); break; default: switch (ch) { case 0007: /* ^G: Abort */ ret = CC_ERROR; done++; break; case 0027: /* ^W: Append word */ /* No can do if globbing characters in pattern */ for (cp = &el->el_search.patbuf[LEN];; cp++) if (cp >= &el->el_search.patbuf[ el->el_search.patlen]) { el->el_line.cursor += el->el_search.patlen - LEN - 1; cp = c__next_word(el->el_line.cursor, el->el_line.lastchar, 1, ce__isword); while (el->el_line.cursor < cp && *el->el_line.cursor != '\n') { if (el->el_search.patlen >= EL_BUFSIZ - LEN) { term_beep(el); break; } el->el_search.patbuf[el->el_search.patlen++] = *el->el_line.cursor; *el->el_line.lastchar++ = *el->el_line.cursor++; } el->el_line.cursor = ocursor; *el->el_line.lastchar = '\0'; re_refresh(el); break; } else if (isglob(*cp)) { term_beep(el); break; } break; default: /* Terminate and execute cmd */ endcmd[0] = ch; el_push(el, endcmd); /* FALLTHROUGH */ case 0033: /* ESC: Terminate */ ret = CC_REFRESH; done++; break; } break; } while (el->el_line.lastchar > el->el_line.buffer && *el->el_line.lastchar != '\n') *el->el_line.lastchar-- = '\0'; *el->el_line.lastchar = '\0'; if (!done) { /* Can't search if unmatched '[' */ for (cp = &el->el_search.patbuf[el->el_search.patlen-1], ch = ']'; cp >= &el->el_search.patbuf[LEN]; cp--) if (*cp == '[' || *cp == ']') { ch = *cp; break; } if (el->el_search.patlen > LEN && ch != '[') { if (redo && newdir == dir) { if (pchar == '?') { /* wrap around */ el->el_history.eventno = newdir == ED_SEARCH_PREV_HISTORY ? 0 : 0x7fffffff; if (hist_get(el) == CC_ERROR) /* el->el_history.event * no was fixed by * first call */ (void) hist_get(el); el->el_line.cursor = newdir == ED_SEARCH_PREV_HISTORY ? el->el_line.lastchar : el->el_line.buffer; } else el->el_line.cursor += newdir == ED_SEARCH_PREV_HISTORY ? -1 : 1; } #ifdef ANCHOR el->el_search.patbuf[el->el_search.patlen++] = '.'; el->el_search.patbuf[el->el_search.patlen++] = '*'; #endif el->el_search.patbuf[el->el_search.patlen] = '\0'; if (el->el_line.cursor < el->el_line.buffer || el->el_line.cursor > el->el_line.lastchar || (ret = ce_search_line(el, newdir)) == CC_ERROR) { /* avoid c_setpat */ el->el_state.lastcmd = (el_action_t) newdir; ret = newdir == ED_SEARCH_PREV_HISTORY ? ed_search_prev_history(el, 0) : ed_search_next_history(el, 0); if (ret != CC_ERROR) { el->el_line.cursor = newdir == ED_SEARCH_PREV_HISTORY ? el->el_line.lastchar : el->el_line.buffer; (void) ce_search_line(el, newdir); } } el->el_search.patlen -= LEN; el->el_search.patbuf[el->el_search.patlen] = '\0'; if (ret == CC_ERROR) { term_beep(el); if (el->el_history.eventno != ohisteventno) { el->el_history.eventno = ohisteventno; if (hist_get(el) == CC_ERROR) return (CC_ERROR); } el->el_line.cursor = ocursor; pchar = '?'; } else { pchar = ':'; } } ret = ce_inc_search(el, newdir); if (ret == CC_ERROR && pchar == '?' && oldpchar == ':') /* * break abort of failed search at last * non-failed */ ret = CC_NORM; } if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) { /* restore on normal return or error exit */ pchar = oldpchar; el->el_search.patlen = oldpatlen; if (el->el_history.eventno != ohisteventno) { el->el_history.eventno = ohisteventno; if (hist_get(el) == CC_ERROR) return (CC_ERROR); } el->el_line.cursor = ocursor; if (ret == CC_ERROR) re_refresh(el); } if (done || ret != CC_NORM) return (ret); } } /* cv_search(): * Vi search. */ protected el_action_t cv_search(EditLine *el, int dir) { char ch; char tmpbuf[EL_BUFSIZ]; int tmplen; #ifdef ANCHOR tmpbuf[0] = '.'; tmpbuf[1] = '*'; #endif tmplen = LEN; el->el_search.patdir = dir; tmplen = c_gets(el, &tmpbuf[LEN], dir == ED_SEARCH_PREV_HISTORY ? "\n/" : "\n?" ); if (tmplen == -1) return CC_REFRESH; tmplen += LEN; ch = tmpbuf[tmplen]; tmpbuf[tmplen] = '\0'; if (tmplen == LEN) { /* * Use the old pattern, but wild-card it. */ if (el->el_search.patlen == 0) { re_refresh(el); return (CC_ERROR); } #ifdef ANCHOR if (el->el_search.patbuf[0] != '.' && el->el_search.patbuf[0] != '*') { (void) strncpy(tmpbuf, el->el_search.patbuf, sizeof(tmpbuf) - 1); el->el_search.patbuf[0] = '.'; el->el_search.patbuf[1] = '*'; (void) strncpy(&el->el_search.patbuf[2], tmpbuf, EL_BUFSIZ - 3); el->el_search.patlen++; el->el_search.patbuf[el->el_search.patlen++] = '.'; el->el_search.patbuf[el->el_search.patlen++] = '*'; el->el_search.patbuf[el->el_search.patlen] = '\0'; } #endif } else { #ifdef ANCHOR tmpbuf[tmplen++] = '.'; tmpbuf[tmplen++] = '*'; #endif tmpbuf[tmplen] = '\0'; (void) strncpy(el->el_search.patbuf, tmpbuf, EL_BUFSIZ - 1); el->el_search.patlen = tmplen; } el->el_state.lastcmd = (el_action_t) dir; /* avoid c_setpat */ el->el_line.cursor = el->el_line.lastchar = el->el_line.buffer; if ((dir == ED_SEARCH_PREV_HISTORY ? ed_search_prev_history(el, 0) : ed_search_next_history(el, 0)) == CC_ERROR) { re_refresh(el); return (CC_ERROR); } if (ch == 0033) { re_refresh(el); return ed_newline(el, 0); } return (CC_REFRESH); } /* ce_search_line(): * Look for a pattern inside a line */ protected el_action_t ce_search_line(EditLine *el, int dir) { char *cp = el->el_line.cursor; char *pattern = el->el_search.patbuf; char oc, *ocp; #ifdef ANCHOR ocp = &pattern[1]; oc = *ocp; *ocp = '^'; #else ocp = pattern; oc = *ocp; #endif if (dir == ED_SEARCH_PREV_HISTORY) { for (; cp >= el->el_line.buffer; cp--) { if (el_match(cp, ocp)) { *ocp = oc; el->el_line.cursor = cp; return (CC_NORM); } } *ocp = oc; return (CC_ERROR); } else { for (; *cp != '\0' && cp < el->el_line.limit; cp++) { if (el_match(cp, ocp)) { *ocp = oc; el->el_line.cursor = cp; return (CC_NORM); } } *ocp = oc; return (CC_ERROR); } } /* cv_repeat_srch(): * Vi repeat search */ protected el_action_t cv_repeat_srch(EditLine *el, int c) { #ifdef SDEBUG (void) fprintf(el->el_errfile, "dir %d patlen %d patbuf %s\n", c, el->el_search.patlen, el->el_search.patbuf); #endif el->el_state.lastcmd = (el_action_t) c; /* Hack to stop c_setpat */ el->el_line.lastchar = el->el_line.buffer; switch (c) { case ED_SEARCH_NEXT_HISTORY: return (ed_search_next_history(el, 0)); case ED_SEARCH_PREV_HISTORY: return (ed_search_prev_history(el, 0)); default: return (CC_ERROR); } } /* cv_csearch(): * Vi character search */ protected el_action_t cv_csearch(EditLine *el, int direction, int ch, int count, int tflag) { char *cp; if (ch == 0) return CC_ERROR; if (ch == -1) { char c; if (el_getc(el, &c) != 1) return ed_end_of_file(el, 0); ch = c; } /* Save for ';' and ',' commands */ el->el_search.chacha = ch; el->el_search.chadir = direction; el->el_search.chatflg = tflag; cp = el->el_line.cursor; while (count--) { if (*cp == ch) cp += direction; for (;;cp += direction) { if (cp >= el->el_line.lastchar) return CC_ERROR; if (cp < el->el_line.buffer) return CC_ERROR; if (*cp == ch) break; } } if (tflag) cp -= direction; el->el_line.cursor = cp; if (el->el_chared.c_vcmd.action != NOP) { if (direction > 0) el->el_line.cursor++; cv_delfini(el); return CC_REFRESH; } return CC_CURSOR; } clt/falcon/editline/src/search.h000066400000000000000000000053541176363201700171210ustar00rootroot00000000000000/* $NetBSD: search.h,v 1.8 2003/10/18 23:27:36 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)search.h 8.1 (Berkeley) 6/4/93 */ /* * el.search.h: Line and history searching utilities */ #ifndef _h_el_search #define _h_el_search #include "histedit.h" typedef struct el_search_t { char *patbuf; /* The pattern buffer */ size_t patlen; /* Length of the pattern buffer */ int patdir; /* Direction of the last search */ int chadir; /* Character search direction */ char chacha; /* Character we are looking for */ char chatflg; /* 0 if f, 1 if t */ } el_search_t; protected int el_match(const char *, const char *); protected int search_init(EditLine *); protected void search_end(EditLine *); protected int c_hmatch(EditLine *, const char *); protected void c_setpat(EditLine *); protected el_action_t ce_inc_search(EditLine *, int); protected el_action_t cv_search(EditLine *, int); protected el_action_t ce_search_line(EditLine *, int); protected el_action_t cv_repeat_srch(EditLine *, int); protected el_action_t cv_csearch(EditLine *, int, int, int, int); #endif /* _h_el_search */ clt/falcon/editline/src/sig.c000066400000000000000000000120011176363201700164140ustar00rootroot00000000000000/* $NetBSD: sig.c,v 1.15 2009/02/19 15:20:22 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)sig.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: sig.c,v 1.15 2009/02/19 15:20:22 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * sig.c: Signal handling stuff. * our policy is to trap all signals, set a good state * and pass the ball to our caller. */ #include "el.h" #include private EditLine *sel = NULL; private const int sighdl[] = { #define _DO(a) (a), ALLSIGS #undef _DO - 1 }; private void sig_handler(int); /* sig_handler(): * This is the handler called for all signals * XXX: we cannot pass any data so we just store the old editline * state in a private variable */ private void sig_handler(int signo) { int i; sigset_t nset, oset; (void) sigemptyset(&nset); (void) sigaddset(&nset, signo); (void) sigprocmask(SIG_BLOCK, &nset, &oset); sel->el_signal->sig_no = signo; switch (signo) { case SIGCONT: tty_rawmode(sel); if (ed_redisplay(sel, 0) == CC_REFRESH) re_refresh(sel); term__flush(sel); break; case SIGWINCH: el_resize(sel); break; default: tty_cookedmode(sel); break; } for (i = 0; sighdl[i] != -1; i++) if (signo == sighdl[i]) break; (void) sigaction(signo, &sel->el_signal->sig_action[i], NULL); sel->el_signal->sig_action[i].sa_handler = SIG_ERR; sel->el_signal->sig_action[i].sa_flags = 0; sigemptyset(&sel->el_signal->sig_action[i].sa_mask); (void) sigprocmask(SIG_SETMASK, &oset, NULL); (void) kill(0, signo); } /* sig_init(): * Initialize all signal stuff */ protected int sig_init(EditLine *el) { size_t i; sigset_t *nset, oset; el->el_signal = el_malloc(sizeof(*el->el_signal)); if (el->el_signal == NULL) return -1; nset = &el->el_signal->sig_set; (void) sigemptyset(nset); #define _DO(a) (void) sigaddset(nset, a); ALLSIGS #undef _DO (void) sigprocmask(SIG_BLOCK, nset, &oset); for (i = 0; sighdl[i] != -1; i++) { el->el_signal->sig_action[i].sa_handler = SIG_ERR; el->el_signal->sig_action[i].sa_flags = 0; sigemptyset(&el->el_signal->sig_action[i].sa_mask); } (void) sigprocmask(SIG_SETMASK, &oset, NULL); return 0; } /* sig_end(): * Clear all signal stuff */ protected void sig_end(EditLine *el) { el_free((ptr_t) el->el_signal); el->el_signal = NULL; } /* sig_set(): * set all the signal handlers */ protected void sig_set(EditLine *el) { size_t i; sigset_t oset; struct sigaction osa, nsa; nsa.sa_handler = sig_handler; nsa.sa_flags = 0; sigemptyset(&nsa.sa_mask); (void) sigprocmask(SIG_BLOCK, &el->el_signal->sig_set, &oset); for (i = 0; sighdl[i] != -1; i++) { /* This could happen if we get interrupted */ if (sigaction(sighdl[i], &nsa, &osa) != -1 && osa.sa_handler != sig_handler) el->el_signal->sig_action[i] = osa; } sel = el; (void) sigprocmask(SIG_SETMASK, &oset, NULL); } /* sig_clr(): * clear all the signal handlers */ protected void sig_clr(EditLine *el) { size_t i; sigset_t oset; (void) sigprocmask(SIG_BLOCK, &el->el_signal->sig_set, &oset); for (i = 0; sighdl[i] != -1; i++) if (el->el_signal->sig_action[i].sa_handler != SIG_ERR) (void)sigaction(sighdl[i], &el->el_signal->sig_action[i], NULL); sel = NULL; /* we are going to die if the handler is * called */ (void)sigprocmask(SIG_SETMASK, &oset, NULL); } clt/falcon/editline/src/sig.h000066400000000000000000000046701176363201700164360ustar00rootroot00000000000000/* $NetBSD: sig.h,v 1.8 2009/02/19 15:20:22 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)sig.h 8.1 (Berkeley) 6/4/93 */ /* * el.sig.h: Signal handling functions */ #ifndef _h_el_sig #define _h_el_sig #include #include "histedit.h" /* * Define here all the signals we are going to handle * The _DO macro is used to iterate in the source code */ #define ALLSIGS \ _DO(SIGINT) \ _DO(SIGTSTP) \ _DO(SIGQUIT) \ _DO(SIGHUP) \ _DO(SIGTERM) \ _DO(SIGCONT) \ _DO(SIGWINCH) #define ALLSIGSNO 7 typedef struct { struct sigaction sig_action[ALLSIGSNO]; sigset_t sig_set; volatile sig_atomic_t sig_no; } *el_signal_t; protected void sig_end(EditLine*); protected int sig_init(EditLine*); protected void sig_set(EditLine*); protected void sig_clr(EditLine*); #endif /* _h_el_sig */ clt/falcon/editline/src/strlcat.c000066400000000000000000000041001176363201700173070ustar00rootroot00000000000000/* $NetBSD: strlcat.c,v 1.3 2007/06/04 18:19:27 christos Exp $ */ /* $OpenBSD: strlcat.c,v 1.10 2003/04/12 21:56:39 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #if defined(LIBC_SCCS) && !defined(lint) __RCSID("$NetBSD: strlcat.c,v 1.3 2007/06/04 18:19:27 christos Exp $"); #endif /* LIBC_SCCS and not lint */ #include #include #include #ifdef _LIBC # ifdef __weak_alias __weak_alias(strlcat, _strlcat) # endif #endif #if !HAVE_STRLCAT /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). * Returns strlen(src) + MIN(siz, strlen(initial dst)). * If retval >= siz, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; size_t dlen; _DIAGASSERT(dst != NULL); _DIAGASSERT(src != NULL); /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ } #endif clt/falcon/editline/src/strlcpy.c000066400000000000000000000037201176363201700173420ustar00rootroot00000000000000/* $NetBSD: strlcpy.c,v 1.3 2007/06/04 18:19:27 christos Exp $ */ /* $OpenBSD: strlcpy.c,v 1.7 2003/04/12 21:56:39 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #if defined(LIBC_SCCS) && !defined(lint) __RCSID("$NetBSD: strlcpy.c,v 1.3 2007/06/04 18:19:27 christos Exp $"); #endif /* LIBC_SCCS and not lint */ #include #include #include #ifdef _LIBC # ifdef __weak_alias __weak_alias(strlcpy, _strlcpy) # endif #endif #if !HAVE_STRLCPY /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; _DIAGASSERT(dst != NULL); _DIAGASSERT(src != NULL); /* Copy as many bytes as will fit */ if (n != 0 && --n != 0) { do { if ((*d++ = *s++) == 0) break; } while (--n != 0); } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } #endif clt/falcon/editline/src/sys.h000066400000000000000000000110441176363201700164630ustar00rootroot00000000000000/* $NetBSD: sys.h,v 1.12 2009/08/31 00:05:43 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)sys.h 8.1 (Berkeley) 6/4/93 */ /* * sys.h: Put all the stupid compiler and system dependencies here... */ #ifndef _h_sys #define _h_sys #include #ifdef HAVE_SYS_CDEFS_H #include #endif #if !defined(__attribute__) && (defined(__cplusplus) || !defined(__GNUC__) || __GNUC__ == 2 && __GNUC_MINOR__ < 8) # define __attribute__(A) #endif #ifndef __P # define __P(x) x #endif #ifndef _DIAGASSERT # define _DIAGASSERT(x) #endif #ifndef __BEGIN_DECLS # ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } # else # define __BEGIN_DECLS # define __END_DECLS # endif #endif #ifndef public # define public /* Externally visible functions/variables */ #endif #ifndef private # define private static /* Always hidden internals */ #endif #ifndef protected # define protected /* Redefined from elsewhere to "static" */ /* When we want to hide everything */ #endif #ifndef HAVE_U_INT32_T typedef unsigned int u_int32_t; #endif #ifndef _PTR_T # define _PTR_T typedef void *ptr_t; #endif #ifndef _IOCTL_T # define _IOCTL_T typedef void *ioctl_t; #endif #include #ifndef HAVE_STRLCAT #define strlcat libedit_strlcat size_t strlcat(char *dst, const char *src, size_t size); #endif #ifndef HAVE_STRLCPY #define strlcpy libedit_strlcpy size_t strlcpy(char *dst, const char *src, size_t size); #endif #ifndef HAVE_FGETLN #define fgetln libedit_fgetln char *fgetln(FILE *fp, size_t *len); #endif #define REGEX /* Use POSIX.2 regular expression functions */ #undef REGEXP /* Use UNIX V8 regular expression functions */ #if defined(__sun) extern int tgetent(char *, const char *); extern int tgetflag(char *); extern int tgetnum(char *); extern int tputs(const char *, int, int (*)(int)); extern char* tgoto(const char*, int, int); extern char* tgetstr(char*, char**); #endif #ifdef notdef # undef REGEX # undef REGEXP # include # ifdef __GNUC__ /* * Broken hdrs. */ extern int tgetent(const char *bp, char *name); extern int tgetflag(const char *id); extern int tgetnum(const char *id); extern char *tgetstr(const char *id, char **area); extern char *tgoto(const char *cap, int col, int row); extern int tputs(const char *str, int affcnt, int (*putc)(int)); extern char *getenv(const char *); extern int fprintf(FILE *, const char *, ...); extern int sigsetmask(int); extern int sigblock(int); extern int fputc(int, FILE *); extern int fgetc(FILE *); extern int fflush(FILE *); extern int tolower(int); extern int toupper(int); extern int errno, sys_nerr; extern char *sys_errlist[]; extern void perror(const char *); # include # define strerror(e) sys_errlist[e] # endif # ifdef SABER extern ptr_t memcpy(ptr_t, const ptr_t, size_t); extern ptr_t memset(ptr_t, int, size_t); # endif extern char *fgetline(FILE *, int *); #endif #endif /* _h_sys */ clt/falcon/editline/src/term.c000066400000000000000000001164611176363201700166200ustar00rootroot00000000000000/* $NetBSD: term.c,v 1.55 2009/08/31 00:05:43 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)term.c 8.2 (Berkeley) 4/30/95"; #else __RCSID("$NetBSD: term.c,v 1.55 2009/08/31 00:05:43 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * term.c: Editor/termcap-curses interface * We have to declare a static variable here, since the * termcap putchar routine does not take an argument! */ #include #include #include #include #include #ifdef HAVE_TERMCAP_H #include #endif #ifdef HAVE_CURSES_H #include #elif HAVE_NCURSES_H #include #endif /* Solaris's term.h does horrid things. */ #if defined(HAVE_TERM_H) && !defined(__sun) #include #endif #include #include #ifdef _REENTRANT #include #endif #include "el.h" /* * IMPORTANT NOTE: these routines are allowed to look at the current screen * and the current possition assuming that it is correct. If this is not * true, then the update will be WRONG! This is (should be) a valid * assumption... */ #define TC_BUFSIZE 2048 #define GoodStr(a) (el->el_term.t_str[a] != NULL && \ el->el_term.t_str[a][0] != '\0') #define Str(a) el->el_term.t_str[a] #define Val(a) el->el_term.t_val[a] #ifdef notdef private const struct { const char *b_name; int b_rate; } baud_rate[] = { #ifdef B0 { "0", B0 }, #endif #ifdef B50 { "50", B50 }, #endif #ifdef B75 { "75", B75 }, #endif #ifdef B110 { "110", B110 }, #endif #ifdef B134 { "134", B134 }, #endif #ifdef B150 { "150", B150 }, #endif #ifdef B200 { "200", B200 }, #endif #ifdef B300 { "300", B300 }, #endif #ifdef B600 { "600", B600 }, #endif #ifdef B900 { "900", B900 }, #endif #ifdef B1200 { "1200", B1200 }, #endif #ifdef B1800 { "1800", B1800 }, #endif #ifdef B2400 { "2400", B2400 }, #endif #ifdef B3600 { "3600", B3600 }, #endif #ifdef B4800 { "4800", B4800 }, #endif #ifdef B7200 { "7200", B7200 }, #endif #ifdef B9600 { "9600", B9600 }, #endif #ifdef EXTA { "19200", EXTA }, #endif #ifdef B19200 { "19200", B19200 }, #endif #ifdef EXTB { "38400", EXTB }, #endif #ifdef B38400 { "38400", B38400 }, #endif { NULL, 0 } }; #endif private const struct termcapstr { const char *name; const char *long_name; } tstr[] = { #define T_al 0 { "al", "add new blank line" }, #define T_bl 1 { "bl", "audible bell" }, #define T_cd 2 { "cd", "clear to bottom" }, #define T_ce 3 { "ce", "clear to end of line" }, #define T_ch 4 { "ch", "cursor to horiz pos" }, #define T_cl 5 { "cl", "clear screen" }, #define T_dc 6 { "dc", "delete a character" }, #define T_dl 7 { "dl", "delete a line" }, #define T_dm 8 { "dm", "start delete mode" }, #define T_ed 9 { "ed", "end delete mode" }, #define T_ei 10 { "ei", "end insert mode" }, #define T_fs 11 { "fs", "cursor from status line" }, #define T_ho 12 { "ho", "home cursor" }, #define T_ic 13 { "ic", "insert character" }, #define T_im 14 { "im", "start insert mode" }, #define T_ip 15 { "ip", "insert padding" }, #define T_kd 16 { "kd", "sends cursor down" }, #define T_kl 17 { "kl", "sends cursor left" }, #define T_kr 18 { "kr", "sends cursor right" }, #define T_ku 19 { "ku", "sends cursor up" }, #define T_md 20 { "md", "begin bold" }, #define T_me 21 { "me", "end attributes" }, #define T_nd 22 { "nd", "non destructive space" }, #define T_se 23 { "se", "end standout" }, #define T_so 24 { "so", "begin standout" }, #define T_ts 25 { "ts", "cursor to status line" }, #define T_up 26 { "up", "cursor up one" }, #define T_us 27 { "us", "begin underline" }, #define T_ue 28 { "ue", "end underline" }, #define T_vb 29 { "vb", "visible bell" }, #define T_DC 30 { "DC", "delete multiple chars" }, #define T_DO 31 { "DO", "cursor down multiple" }, #define T_IC 32 { "IC", "insert multiple chars" }, #define T_LE 33 { "LE", "cursor left multiple" }, #define T_RI 34 { "RI", "cursor right multiple" }, #define T_UP 35 { "UP", "cursor up multiple" }, #define T_kh 36 { "kh", "send cursor home" }, #define T_at7 37 { "@7", "send cursor end" }, #define T_str 38 { NULL, NULL } }; private const struct termcapval { const char *name; const char *long_name; } tval[] = { #define T_am 0 { "am", "has automatic margins" }, #define T_pt 1 { "pt", "has physical tabs" }, #define T_li 2 { "li", "Number of lines" }, #define T_co 3 { "co", "Number of columns" }, #define T_km 4 { "km", "Has meta key" }, #define T_xt 5 { "xt", "Tab chars destructive" }, #define T_xn 6 { "xn", "newline ignored at right margin" }, #define T_MT 7 { "MT", "Has meta key" }, /* XXX? */ #define T_val 8 { NULL, NULL, } }; /* do two or more of the attributes use me */ private void term_setflags(EditLine *); private int term_rebuffer_display(EditLine *); private void term_free_display(EditLine *); private int term_alloc_display(EditLine *); private void term_alloc(EditLine *, const struct termcapstr *, const char *); private void term_init_arrow(EditLine *); private void term_reset_arrow(EditLine *); private int term_putc(int); private void term_tputs(EditLine *, const char *, int); #ifdef _REENTRANT private pthread_mutex_t term_mutex = PTHREAD_MUTEX_INITIALIZER; #endif private FILE *term_outfile = NULL; /* term_setflags(): * Set the terminal capability flags */ private void term_setflags(EditLine *el) { EL_FLAGS = 0; if (el->el_tty.t_tabs) EL_FLAGS |= (Val(T_pt) && !Val(T_xt)) ? TERM_CAN_TAB : 0; EL_FLAGS |= (Val(T_km) || Val(T_MT)) ? TERM_HAS_META : 0; EL_FLAGS |= GoodStr(T_ce) ? TERM_CAN_CEOL : 0; EL_FLAGS |= (GoodStr(T_dc) || GoodStr(T_DC)) ? TERM_CAN_DELETE : 0; EL_FLAGS |= (GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC)) ? TERM_CAN_INSERT : 0; EL_FLAGS |= (GoodStr(T_up) || GoodStr(T_UP)) ? TERM_CAN_UP : 0; EL_FLAGS |= Val(T_am) ? TERM_HAS_AUTO_MARGINS : 0; EL_FLAGS |= Val(T_xn) ? TERM_HAS_MAGIC_MARGINS : 0; if (GoodStr(T_me) && GoodStr(T_ue)) EL_FLAGS |= (strcmp(Str(T_me), Str(T_ue)) == 0) ? TERM_CAN_ME : 0; else EL_FLAGS &= ~TERM_CAN_ME; if (GoodStr(T_me) && GoodStr(T_se)) EL_FLAGS |= (strcmp(Str(T_me), Str(T_se)) == 0) ? TERM_CAN_ME : 0; #ifdef DEBUG_SCREEN if (!EL_CAN_UP) { (void) fprintf(el->el_errfile, "WARNING: Your terminal cannot move up.\n"); (void) fprintf(el->el_errfile, "Editing may be odd for long lines.\n"); } if (!EL_CAN_CEOL) (void) fprintf(el->el_errfile, "no clear EOL capability.\n"); if (!EL_CAN_DELETE) (void) fprintf(el->el_errfile, "no delete char capability.\n"); if (!EL_CAN_INSERT) (void) fprintf(el->el_errfile, "no insert char capability.\n"); #endif /* DEBUG_SCREEN */ } /* term_init(): * Initialize the terminal stuff */ protected int term_init(EditLine *el) { el->el_term.t_buf = (char *) el_malloc(TC_BUFSIZE); if (el->el_term.t_buf == NULL) return (-1); el->el_term.t_cap = (char *) el_malloc(TC_BUFSIZE); if (el->el_term.t_cap == NULL) return (-1); el->el_term.t_fkey = (fkey_t *) el_malloc(A_K_NKEYS * sizeof(fkey_t)); if (el->el_term.t_fkey == NULL) return (-1); el->el_term.t_loc = 0; el->el_term.t_str = (char **) el_malloc(T_str * sizeof(char *)); if (el->el_term.t_str == NULL) return (-1); (void) memset(el->el_term.t_str, 0, T_str * sizeof(char *)); el->el_term.t_val = (int *) el_malloc(T_val * sizeof(int)); if (el->el_term.t_val == NULL) return (-1); (void) memset(el->el_term.t_val, 0, T_val * sizeof(int)); (void) term_set(el, NULL); term_init_arrow(el); return (0); } /* term_end(): * Clean up the terminal stuff */ protected void term_end(EditLine *el) { el_free((ptr_t) el->el_term.t_buf); el->el_term.t_buf = NULL; el_free((ptr_t) el->el_term.t_cap); el->el_term.t_cap = NULL; el->el_term.t_loc = 0; el_free((ptr_t) el->el_term.t_str); el->el_term.t_str = NULL; el_free((ptr_t) el->el_term.t_val); el->el_term.t_val = NULL; el_free((ptr_t) el->el_term.t_fkey); el->el_term.t_fkey = NULL; term_free_display(el); } /* term_alloc(): * Maintain a string pool for termcap strings */ private void term_alloc(EditLine *el, const struct termcapstr *t, const char *cap) { char termbuf[TC_BUFSIZE]; size_t tlen, clen; char **tlist = el->el_term.t_str; char **tmp, **str = &tlist[t - tstr]; if (cap == NULL || *cap == '\0') { *str = NULL; return; } else clen = strlen(cap); tlen = *str == NULL ? 0 : strlen(*str); /* * New string is shorter; no need to allocate space */ if (clen <= tlen) { if (*str) (void) strcpy(*str, cap); /* XXX strcpy is safe */ return; } /* * New string is longer; see if we have enough space to append */ if (el->el_term.t_loc + 3 < TC_BUFSIZE) { /* XXX strcpy is safe */ (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc], cap); el->el_term.t_loc += (int)clen + 1; /* one for \0 */ return; } /* * Compact our buffer; no need to check compaction, cause we know it * fits... */ tlen = 0; for (tmp = tlist; tmp < &tlist[T_str]; tmp++) if (*tmp != NULL && *tmp != '\0' && *tmp != *str) { char *ptr; for (ptr = *tmp; *ptr != '\0'; termbuf[tlen++] = *ptr++) continue; termbuf[tlen++] = '\0'; } memcpy(el->el_term.t_buf, termbuf, TC_BUFSIZE); el->el_term.t_loc = (int)tlen; if (el->el_term.t_loc + 3 >= TC_BUFSIZE) { (void) fprintf(el->el_errfile, "Out of termcap string space.\n"); return; } /* XXX strcpy is safe */ (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc], cap); el->el_term.t_loc += (int)clen + 1; /* one for \0 */ return; } /* term_rebuffer_display(): * Rebuffer the display after the screen changed size */ private int term_rebuffer_display(EditLine *el) { coord_t *c = &el->el_term.t_size; term_free_display(el); c->h = Val(T_co); c->v = Val(T_li); if (term_alloc_display(el) == -1) return (-1); return (0); } /* term_alloc_display(): * Allocate a new display. */ private int term_alloc_display(EditLine *el) { int i; char **b; coord_t *c = &el->el_term.t_size; b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1))); if (b == NULL) return (-1); for (i = 0; i < c->v; i++) { b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1))); if (b[i] == NULL) { while (--i >= 0) el_free((ptr_t) b[i]); el_free((ptr_t) b); return (-1); } } b[c->v] = NULL; el->el_display = b; b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1))); if (b == NULL) return (-1); for (i = 0; i < c->v; i++) { b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1))); if (b[i] == NULL) { while (--i >= 0) el_free((ptr_t) b[i]); el_free((ptr_t) b); return (-1); } } b[c->v] = NULL; el->el_vdisplay = b; return (0); } /* term_free_display(): * Free the display buffers */ private void term_free_display(EditLine *el) { char **b; char **bufp; b = el->el_display; el->el_display = NULL; if (b != NULL) { for (bufp = b; *bufp != NULL; bufp++) el_free((ptr_t) * bufp); el_free((ptr_t) b); } b = el->el_vdisplay; el->el_vdisplay = NULL; if (b != NULL) { for (bufp = b; *bufp != NULL; bufp++) el_free((ptr_t) * bufp); el_free((ptr_t) b); } } /* term_move_to_line(): * move to line (first line == 0) * as efficiently as possible */ protected void term_move_to_line(EditLine *el, int where) { int del; if (where == el->el_cursor.v) return; if (where > el->el_term.t_size.v) { #ifdef DEBUG_SCREEN (void) fprintf(el->el_errfile, "term_move_to_line: where is ridiculous: %d\r\n", where); #endif /* DEBUG_SCREEN */ return; } if ((del = where - el->el_cursor.v) > 0) { while (del > 0) { if (EL_HAS_AUTO_MARGINS && el->el_display[el->el_cursor.v][0] != '\0') { /* move without newline */ term_move_to_char(el, el->el_term.t_size.h - 1); term_overwrite(el, &el->el_display [el->el_cursor.v][el->el_cursor.h], (size_t)(el->el_term.t_size.h - el->el_cursor.h)); /* updates Cursor */ del--; } else { if ((del > 1) && GoodStr(T_DO)) { term_tputs(el, tgoto(Str(T_DO), del, del), del); del = 0; } else { for (; del > 0; del--) term__putc(el, '\n'); /* because the \n will become \r\n */ el->el_cursor.h = 0; } } } } else { /* del < 0 */ if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up))) term_tputs(el, tgoto(Str(T_UP), -del, -del), -del); else { if (GoodStr(T_up)) for (; del < 0; del++) term_tputs(el, Str(T_up), 1); } } el->el_cursor.v = where;/* now where is here */ } /* term_move_to_char(): * Move to the character position specified */ protected void term_move_to_char(EditLine *el, int where) { int del, i; mc_again: if (where == el->el_cursor.h) return; if (where > el->el_term.t_size.h) { #ifdef DEBUG_SCREEN (void) fprintf(el->el_errfile, "term_move_to_char: where is riduculous: %d\r\n", where); #endif /* DEBUG_SCREEN */ return; } if (!where) { /* if where is first column */ term__putc(el, '\r'); /* do a CR */ el->el_cursor.h = 0; return; } del = where - el->el_cursor.h; if ((del < -4 || del > 4) && GoodStr(T_ch)) /* go there directly */ term_tputs(el, tgoto(Str(T_ch), where, where), where); else { if (del > 0) { /* moving forward */ if ((del > 4) && GoodStr(T_RI)) term_tputs(el, tgoto(Str(T_RI), del, del), del); else { /* if I can do tabs, use them */ if (EL_CAN_TAB) { if ((el->el_cursor.h & 0370) != (where & 0370)) { /* if not within tab stop */ for (i = (el->el_cursor.h & 0370); i < (where & 0370); i += 8) term__putc(el, '\t'); /* then tab over */ el->el_cursor.h = where & 0370; } } /* * it's usually cheaper to just write the * chars, so we do. */ /* * NOTE THAT term_overwrite() WILL CHANGE * el->el_cursor.h!!! */ term_overwrite(el, &el->el_display[el->el_cursor.v][el->el_cursor.h], (size_t)(where - el->el_cursor.h)); } } else { /* del < 0 := moving backward */ if ((-del > 4) && GoodStr(T_LE)) term_tputs(el, tgoto(Str(T_LE), -del, -del), -del); else { /* can't go directly there */ /* * if the "cost" is greater than the "cost" * from col 0 */ if (EL_CAN_TAB ? ((unsigned int)-del > (((unsigned int) where >> 3) + (where & 07))) : (-del > where)) { term__putc(el, '\r'); /* do a CR */ el->el_cursor.h = 0; goto mc_again; /* and try again */ } for (i = 0; i < -del; i++) term__putc(el, '\b'); } } } el->el_cursor.h = where; /* now where is here */ } /* term_overwrite(): * Overstrike num characters */ protected void term_overwrite(EditLine *el, const char *cp, size_t n) { if (n == 0) return; if (n > (size_t)el->el_term.t_size.h) { #ifdef DEBUG_SCREEN (void) fprintf(el->el_errfile, "term_overwrite: n is riduculous: %d\r\n", n); #endif /* DEBUG_SCREEN */ return; } do { term__putc(el, *cp++); el->el_cursor.h++; } while (--n); if (el->el_cursor.h >= el->el_term.t_size.h) { /* wrap? */ if (EL_HAS_AUTO_MARGINS) { /* yes */ el->el_cursor.h = 0; el->el_cursor.v++; if (EL_HAS_MAGIC_MARGINS) { /* force the wrap to avoid the "magic" * situation */ char c; if ((c = el->el_display[el->el_cursor.v][el->el_cursor.h]) != '\0') term_overwrite(el, &c, 1); else term__putc(el, ' '); el->el_cursor.h = 1; } } else /* no wrap, but cursor stays on screen */ el->el_cursor.h = el->el_term.t_size.h - 1; } } /* term_deletechars(): * Delete num characters */ protected void term_deletechars(EditLine *el, int num) { if (num <= 0) return; if (!EL_CAN_DELETE) { #ifdef DEBUG_EDIT (void) fprintf(el->el_errfile, " ERROR: cannot delete \n"); #endif /* DEBUG_EDIT */ return; } if (num > el->el_term.t_size.h) { #ifdef DEBUG_SCREEN (void) fprintf(el->el_errfile, "term_deletechars: num is riduculous: %d\r\n", num); #endif /* DEBUG_SCREEN */ return; } if (GoodStr(T_DC)) /* if I have multiple delete */ if ((num > 1) || !GoodStr(T_dc)) { /* if dc would be more * expen. */ term_tputs(el, tgoto(Str(T_DC), num, num), num); return; } if (GoodStr(T_dm)) /* if I have delete mode */ term_tputs(el, Str(T_dm), 1); if (GoodStr(T_dc)) /* else do one at a time */ while (num--) term_tputs(el, Str(T_dc), 1); if (GoodStr(T_ed)) /* if I have delete mode */ term_tputs(el, Str(T_ed), 1); } /* term_insertwrite(): * Puts terminal in insert character mode or inserts num * characters in the line */ protected void term_insertwrite(EditLine *el, char *cp, int num) { if (num <= 0) return; if (!EL_CAN_INSERT) { #ifdef DEBUG_EDIT (void) fprintf(el->el_errfile, " ERROR: cannot insert \n"); #endif /* DEBUG_EDIT */ return; } if (num > el->el_term.t_size.h) { #ifdef DEBUG_SCREEN (void) fprintf(el->el_errfile, "StartInsert: num is riduculous: %d\r\n", num); #endif /* DEBUG_SCREEN */ return; } if (GoodStr(T_IC)) /* if I have multiple insert */ if ((num > 1) || !GoodStr(T_ic)) { /* if ic would be more expensive */ term_tputs(el, tgoto(Str(T_IC), num, num), num); term_overwrite(el, cp, (size_t)num); /* this updates el_cursor.h */ return; } if (GoodStr(T_im) && GoodStr(T_ei)) { /* if I have insert mode */ term_tputs(el, Str(T_im), 1); el->el_cursor.h += num; do term__putc(el, *cp++); while (--num); if (GoodStr(T_ip)) /* have to make num chars insert */ term_tputs(el, Str(T_ip), 1); term_tputs(el, Str(T_ei), 1); return; } do { if (GoodStr(T_ic)) /* have to make num chars insert */ term_tputs(el, Str(T_ic), 1); term__putc(el, *cp++); el->el_cursor.h++; if (GoodStr(T_ip)) /* have to make num chars insert */ term_tputs(el, Str(T_ip), 1); /* pad the inserted char */ } while (--num); } /* term_clear_EOL(): * clear to end of line. There are num characters to clear */ protected void term_clear_EOL(EditLine *el, int num) { int i; if (EL_CAN_CEOL && GoodStr(T_ce)) term_tputs(el, Str(T_ce), 1); else { for (i = 0; i < num; i++) term__putc(el, ' '); el->el_cursor.h += num; /* have written num spaces */ } } /* term_clear_screen(): * Clear the screen */ protected void term_clear_screen(EditLine *el) { /* clear the whole screen and home */ if (GoodStr(T_cl)) /* send the clear screen code */ term_tputs(el, Str(T_cl), Val(T_li)); else if (GoodStr(T_ho) && GoodStr(T_cd)) { term_tputs(el, Str(T_ho), Val(T_li)); /* home */ /* clear to bottom of screen */ term_tputs(el, Str(T_cd), Val(T_li)); } else { term__putc(el, '\r'); term__putc(el, '\n'); } } /* term_beep(): * Beep the way the terminal wants us */ protected void term_beep(EditLine *el) { if (GoodStr(T_bl)) /* what termcap says we should use */ term_tputs(el, Str(T_bl), 1); else term__putc(el, '\007'); /* an ASCII bell; ^G */ } #ifdef notdef /* term_clear_to_bottom(): * Clear to the bottom of the screen */ protected void term_clear_to_bottom(EditLine *el) { if (GoodStr(T_cd)) term_tputs(el, Str(T_cd), Val(T_li)); else if (GoodStr(T_ce)) term_tputs(el, Str(T_ce), Val(T_li)); } #endif protected void term_get(EditLine *el, const char **term) { *term = el->el_term.t_name; } /* term_set(): * Read in the terminal capabilities from the requested terminal */ protected int term_set(EditLine *el, const char *term) { int i; char buf[TC_BUFSIZE]; char *area; const struct termcapstr *t; sigset_t oset, nset; int lins, cols; (void) sigemptyset(&nset); (void) sigaddset(&nset, SIGWINCH); (void) sigprocmask(SIG_BLOCK, &nset, &oset); area = buf; if (term == NULL) term = getenv("TERM"); if (!term || !term[0]) term = "dumb"; if (strcmp(term, "emacs") == 0) el->el_flags |= EDIT_DISABLED; memset(el->el_term.t_cap, 0, TC_BUFSIZE); i = tgetent(el->el_term.t_cap, term); if (i <= 0) { if (i == -1) (void) fprintf(el->el_errfile, "Cannot read termcap database;\n"); else if (i == 0) (void) fprintf(el->el_errfile, "No entry for terminal type \"%s\";\n", term); (void) fprintf(el->el_errfile, "using dumb terminal settings.\n"); Val(T_co) = 80; /* do a dumb terminal */ Val(T_pt) = Val(T_km) = Val(T_li) = 0; Val(T_xt) = Val(T_MT); for (t = tstr; t->name != NULL; t++) term_alloc(el, t, NULL); } else { /* auto/magic margins */ Val(T_am) = tgetflag("am"); Val(T_xn) = tgetflag("xn"); /* Can we tab */ Val(T_pt) = tgetflag("pt"); Val(T_xt) = tgetflag("xt"); /* do we have a meta? */ Val(T_km) = tgetflag("km"); Val(T_MT) = tgetflag("MT"); /* Get the size */ Val(T_co) = tgetnum("co"); Val(T_li) = tgetnum("li"); for (t = tstr; t->name != NULL; t++) { /* XXX: some systems' tgetstr needs non const */ term_alloc(el, t, tgetstr(strchr(t->name, *t->name), &area)); } } if (Val(T_co) < 2) Val(T_co) = 80; /* just in case */ if (Val(T_li) < 1) Val(T_li) = 24; el->el_term.t_size.v = Val(T_co); el->el_term.t_size.h = Val(T_li); term_setflags(el); /* get the correct window size */ (void) term_get_size(el, &lins, &cols); if (term_change_size(el, lins, cols) == -1) return (-1); (void) sigprocmask(SIG_SETMASK, &oset, NULL); term_bind_arrow(el); el->el_term.t_name = term; return (i <= 0 ? -1 : 0); } /* term_get_size(): * Return the new window size in lines and cols, and * true if the size was changed. */ protected int term_get_size(EditLine *el, int *lins, int *cols) { *cols = Val(T_co); *lins = Val(T_li); #ifdef TIOCGWINSZ { struct winsize ws; if (ioctl(el->el_infd, TIOCGWINSZ, (ioctl_t) & ws) != -1) { if (ws.ws_col) *cols = ws.ws_col; if (ws.ws_row) *lins = ws.ws_row; } } #endif #ifdef TIOCGSIZE { struct ttysize ts; if (ioctl(el->el_infd, TIOCGSIZE, (ioctl_t) & ts) != -1) { if (ts.ts_cols) *cols = ts.ts_cols; if (ts.ts_lines) *lins = ts.ts_lines; } } #endif return (Val(T_co) != *cols || Val(T_li) != *lins); } /* term_change_size(): * Change the size of the terminal */ protected int term_change_size(EditLine *el, int lins, int cols) { /* * Just in case */ Val(T_co) = (cols < 2) ? 80 : cols; Val(T_li) = (lins < 1) ? 24 : lins; /* re-make display buffers */ if (term_rebuffer_display(el) == -1) return (-1); re_clear_display(el); return (0); } /* term_init_arrow(): * Initialize the arrow key bindings from termcap */ private void term_init_arrow(EditLine *el) { fkey_t *arrow = el->el_term.t_fkey; arrow[A_K_DN].name = "down"; arrow[A_K_DN].key = T_kd; arrow[A_K_DN].fun.cmd = ED_NEXT_HISTORY; arrow[A_K_DN].type = XK_CMD; arrow[A_K_UP].name = "up"; arrow[A_K_UP].key = T_ku; arrow[A_K_UP].fun.cmd = ED_PREV_HISTORY; arrow[A_K_UP].type = XK_CMD; arrow[A_K_LT].name = "left"; arrow[A_K_LT].key = T_kl; arrow[A_K_LT].fun.cmd = ED_PREV_CHAR; arrow[A_K_LT].type = XK_CMD; arrow[A_K_RT].name = "right"; arrow[A_K_RT].key = T_kr; arrow[A_K_RT].fun.cmd = ED_NEXT_CHAR; arrow[A_K_RT].type = XK_CMD; arrow[A_K_HO].name = "home"; arrow[A_K_HO].key = T_kh; arrow[A_K_HO].fun.cmd = ED_MOVE_TO_BEG; arrow[A_K_HO].type = XK_CMD; arrow[A_K_EN].name = "end"; arrow[A_K_EN].key = T_at7; arrow[A_K_EN].fun.cmd = ED_MOVE_TO_END; arrow[A_K_EN].type = XK_CMD; } /* term_reset_arrow(): * Reset arrow key bindings */ private void term_reset_arrow(EditLine *el) { fkey_t *arrow = el->el_term.t_fkey; static const char strA[] = {033, '[', 'A', '\0'}; static const char strB[] = {033, '[', 'B', '\0'}; static const char strC[] = {033, '[', 'C', '\0'}; static const char strD[] = {033, '[', 'D', '\0'}; static const char strH[] = {033, '[', 'H', '\0'}; static const char strF[] = {033, '[', 'F', '\0'}; static const char stOA[] = {033, 'O', 'A', '\0'}; static const char stOB[] = {033, 'O', 'B', '\0'}; static const char stOC[] = {033, 'O', 'C', '\0'}; static const char stOD[] = {033, 'O', 'D', '\0'}; static const char stOH[] = {033, 'O', 'H', '\0'}; static const char stOF[] = {033, 'O', 'F', '\0'}; key_add(el, strA, &arrow[A_K_UP].fun, arrow[A_K_UP].type); key_add(el, strB, &arrow[A_K_DN].fun, arrow[A_K_DN].type); key_add(el, strC, &arrow[A_K_RT].fun, arrow[A_K_RT].type); key_add(el, strD, &arrow[A_K_LT].fun, arrow[A_K_LT].type); key_add(el, strH, &arrow[A_K_HO].fun, arrow[A_K_HO].type); key_add(el, strF, &arrow[A_K_EN].fun, arrow[A_K_EN].type); key_add(el, stOA, &arrow[A_K_UP].fun, arrow[A_K_UP].type); key_add(el, stOB, &arrow[A_K_DN].fun, arrow[A_K_DN].type); key_add(el, stOC, &arrow[A_K_RT].fun, arrow[A_K_RT].type); key_add(el, stOD, &arrow[A_K_LT].fun, arrow[A_K_LT].type); key_add(el, stOH, &arrow[A_K_HO].fun, arrow[A_K_HO].type); key_add(el, stOF, &arrow[A_K_EN].fun, arrow[A_K_EN].type); if (el->el_map.type == MAP_VI) { key_add(el, &strA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type); key_add(el, &strB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type); key_add(el, &strC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type); key_add(el, &strD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type); key_add(el, &strH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type); key_add(el, &strF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type); key_add(el, &stOA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type); key_add(el, &stOB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type); key_add(el, &stOC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type); key_add(el, &stOD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type); key_add(el, &stOH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type); key_add(el, &stOF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type); } } /* term_set_arrow(): * Set an arrow key binding */ protected int term_set_arrow(EditLine *el, const char *name, key_value_t *fun, int type) { fkey_t *arrow = el->el_term.t_fkey; int i; for (i = 0; i < A_K_NKEYS; i++) if (strcmp(name, arrow[i].name) == 0) { arrow[i].fun = *fun; arrow[i].type = type; return (0); } return (-1); } /* term_clear_arrow(): * Clear an arrow key binding */ protected int term_clear_arrow(EditLine *el, const char *name) { fkey_t *arrow = el->el_term.t_fkey; int i; for (i = 0; i < A_K_NKEYS; i++) if (strcmp(name, arrow[i].name) == 0) { arrow[i].type = XK_NOD; return (0); } return (-1); } /* term_print_arrow(): * Print the arrow key bindings */ protected void term_print_arrow(EditLine *el, const char *name) { int i; fkey_t *arrow = el->el_term.t_fkey; for (i = 0; i < A_K_NKEYS; i++) if (*name == '\0' || strcmp(name, arrow[i].name) == 0) if (arrow[i].type != XK_NOD) key_kprint(el, arrow[i].name, &arrow[i].fun, arrow[i].type); } /* term_bind_arrow(): * Bind the arrow keys */ protected void term_bind_arrow(EditLine *el) { el_action_t *map; const el_action_t *dmap; int i, j; char *p; fkey_t *arrow = el->el_term.t_fkey; /* Check if the components needed are initialized */ if (el->el_term.t_buf == NULL || el->el_map.key == NULL) return; map = el->el_map.type == MAP_VI ? el->el_map.alt : el->el_map.key; dmap = el->el_map.type == MAP_VI ? el->el_map.vic : el->el_map.emacs; term_reset_arrow(el); for (i = 0; i < A_K_NKEYS; i++) { p = el->el_term.t_str[arrow[i].key]; if (p && *p) { j = (unsigned char) *p; /* * Assign the arrow keys only if: * * 1. They are multi-character arrow keys and the user * has not re-assigned the leading character, or * has re-assigned the leading character to be * ED_SEQUENCE_LEAD_IN * 2. They are single arrow keys pointing to an * unassigned key. */ if (arrow[i].type == XK_NOD) key_clear(el, map, p); else { if (p[1] && (dmap[j] == map[j] || map[j] == ED_SEQUENCE_LEAD_IN)) { key_add(el, p, &arrow[i].fun, arrow[i].type); map[j] = ED_SEQUENCE_LEAD_IN; } else if (map[j] == ED_UNASSIGNED) { key_clear(el, map, p); if (arrow[i].type == XK_CMD) map[j] = arrow[i].fun.cmd; else key_add(el, p, &arrow[i].fun, arrow[i].type); } } } } } /* term_putc(): * Add a character */ private int term_putc(int c) { if (term_outfile == NULL) return -1; return fputc(c, term_outfile); } private void term_tputs(EditLine *el, const char *cap, int affcnt) { #ifdef _REENTRANT pthread_mutex_lock(&term_mutex); #endif term_outfile = el->el_outfile; (void)tputs(cap, affcnt, term_putc); #ifdef _REENTRANT pthread_mutex_unlock(&term_mutex); #endif } /* term__putc(): * Add a character */ protected int term__putc(EditLine *el, int c) { return fputc(c, el->el_outfile); } /* term__flush(): * Flush output */ protected void term__flush(EditLine *el) { (void) fflush(el->el_outfile); } /* term_writec(): * Write the given character out, in a human readable form */ protected void term_writec(EditLine *el, int c) { char buf[8]; size_t cnt = key__decode_char(buf, sizeof(buf), 0, c); buf[cnt] = '\0'; term_overwrite(el, buf, (size_t)cnt); term__flush(el); } /* term_telltc(): * Print the current termcap characteristics */ protected int /*ARGSUSED*/ term_telltc(EditLine *el, int argc __attribute__((__unused__)), const char **argv __attribute__((__unused__))) { const struct termcapstr *t; char **ts; char upbuf[EL_BUFSIZ]; (void) fprintf(el->el_outfile, "\n\tYour terminal has the\n"); (void) fprintf(el->el_outfile, "\tfollowing characteristics:\n\n"); (void) fprintf(el->el_outfile, "\tIt has %d columns and %d lines\n", Val(T_co), Val(T_li)); (void) fprintf(el->el_outfile, "\tIt has %s meta key\n", EL_HAS_META ? "a" : "no"); (void) fprintf(el->el_outfile, "\tIt can%suse tabs\n", EL_CAN_TAB ? " " : "not "); (void) fprintf(el->el_outfile, "\tIt %s automatic margins\n", EL_HAS_AUTO_MARGINS ? "has" : "does not have"); if (EL_HAS_AUTO_MARGINS) (void) fprintf(el->el_outfile, "\tIt %s magic margins\n", EL_HAS_MAGIC_MARGINS ? "has" : "does not have"); for (t = tstr, ts = el->el_term.t_str; t->name != NULL; t++, ts++) { const char *ub; if (*ts && **ts) { (void) key__decode_str(*ts, upbuf, sizeof(upbuf), ""); ub = upbuf; } else { ub = "(empty)"; } (void) fprintf(el->el_outfile, "\t%25s (%s) == %s\n", t->long_name, t->name, ub); } (void) fputc('\n', el->el_outfile); return (0); } /* term_settc(): * Change the current terminal characteristics */ protected int /*ARGSUSED*/ term_settc(EditLine *el, int argc __attribute__((__unused__)), const char **argv) { const struct termcapstr *ts; const struct termcapval *tv; const char *what, *how; if (argv == NULL || argv[1] == NULL || argv[2] == NULL) return -1; what = argv[1]; how = argv[2]; /* * Do the strings first */ for (ts = tstr; ts->name != NULL; ts++) if (strcmp(ts->name, what) == 0) break; if (ts->name != NULL) { term_alloc(el, ts, how); term_setflags(el); return 0; } /* * Do the numeric ones second */ for (tv = tval; tv->name != NULL; tv++) if (strcmp(tv->name, what) == 0) break; if (tv->name != NULL) return -1; if (tv == &tval[T_pt] || tv == &tval[T_km] || tv == &tval[T_am] || tv == &tval[T_xn]) { if (strcmp(how, "yes") == 0) el->el_term.t_val[tv - tval] = 1; else if (strcmp(how, "no") == 0) el->el_term.t_val[tv - tval] = 0; else { (void) fprintf(el->el_errfile, "%s: Bad value `%s'.\n", argv[0], how); return -1; } term_setflags(el); if (term_change_size(el, Val(T_li), Val(T_co)) == -1) return -1; return 0; } else { long i; char *ep; i = strtol(how, &ep, 10); if (*ep != '\0') { (void) fprintf(el->el_errfile, "%s: Bad value `%s'.\n", argv[0], how); return -1; } el->el_term.t_val[tv - tval] = (int) i; el->el_term.t_size.v = Val(T_co); el->el_term.t_size.h = Val(T_li); if (tv == &tval[T_co] || tv == &tval[T_li]) if (term_change_size(el, Val(T_li), Val(T_co)) == -1) return -1; return 0; } } /* term_gettc(): * Get the current terminal characteristics */ protected int /*ARGSUSED*/ term_gettc(EditLine *el, int argc __attribute__((__unused__)), char **argv) { const struct termcapstr *ts; const struct termcapval *tv; char *what; void *how; if (argv == NULL || argv[1] == NULL || argv[2] == NULL) return (-1); what = argv[1]; how = argv[2]; /* * Do the strings first */ for (ts = tstr; ts->name != NULL; ts++) if (strcmp(ts->name, what) == 0) break; if (ts->name != NULL) { *(char **)how = el->el_term.t_str[ts - tstr]; return 0; } /* * Do the numeric ones second */ for (tv = tval; tv->name != NULL; tv++) if (strcmp(tv->name, what) == 0) break; if (tv->name == NULL) return -1; if (tv == &tval[T_pt] || tv == &tval[T_km] || tv == &tval[T_am] || tv == &tval[T_xn]) { static char yes[] = "yes"; static char no[] = "no"; if (el->el_term.t_val[tv - tval]) *(char **)how = yes; else *(char **)how = no; return 0; } else { *(int *)how = el->el_term.t_val[tv - tval]; return 0; } } /* term_echotc(): * Print the termcap string out with variable substitution */ protected int /*ARGSUSED*/ term_echotc(EditLine *el, int argc __attribute__((__unused__)), const char **argv) { char *cap, *scap, *ep; int arg_need, arg_cols, arg_rows; int verbose = 0, silent = 0; char *area; static const char fmts[] = "%s\n", fmtd[] = "%d\n"; const struct termcapstr *t; char buf[TC_BUFSIZE]; long i; area = buf; if (argv == NULL || argv[1] == NULL) return (-1); argv++; if (argv[0][0] == '-') { switch (argv[0][1]) { case 'v': verbose = 1; break; case 's': silent = 1; break; default: /* stderror(ERR_NAME | ERR_TCUSAGE); */ break; } argv++; } if (!*argv || *argv[0] == '\0') return (0); if (strcmp(*argv, "tabs") == 0) { (void) fprintf(el->el_outfile, fmts, EL_CAN_TAB ? "yes" : "no"); return (0); } else if (strcmp(*argv, "meta") == 0) { (void) fprintf(el->el_outfile, fmts, Val(T_km) ? "yes" : "no"); return (0); } else if (strcmp(*argv, "xn") == 0) { (void) fprintf(el->el_outfile, fmts, EL_HAS_MAGIC_MARGINS ? "yes" : "no"); return (0); } else if (strcmp(*argv, "am") == 0) { (void) fprintf(el->el_outfile, fmts, EL_HAS_AUTO_MARGINS ? "yes" : "no"); return (0); } else if (strcmp(*argv, "baud") == 0) { #ifdef notdef int i; for (i = 0; baud_rate[i].b_name != NULL; i++) if (el->el_tty.t_speed == baud_rate[i].b_rate) { (void) fprintf(el->el_outfile, fmts, baud_rate[i].b_name); return (0); } (void) fprintf(el->el_outfile, fmtd, 0); #else (void) fprintf(el->el_outfile, fmtd, (int)el->el_tty.t_speed); #endif return (0); } else if (strcmp(*argv, "rows") == 0 || strcmp(*argv, "lines") == 0) { (void) fprintf(el->el_outfile, fmtd, Val(T_li)); return (0); } else if (strcmp(*argv, "cols") == 0) { (void) fprintf(el->el_outfile, fmtd, Val(T_co)); return (0); } /* * Try to use our local definition first */ scap = NULL; for (t = tstr; t->name != NULL; t++) if (strcmp(t->name, *argv) == 0) { scap = el->el_term.t_str[t - tstr]; break; } if (t->name == NULL) { /* XXX: some systems' tgetstr needs non const */ scap = tgetstr(strchr(*argv, **argv), &area); } if (!scap || scap[0] == '\0') { if (!silent) (void) fprintf(el->el_errfile, "echotc: Termcap parameter `%s' not found.\n", *argv); return (-1); } /* * Count home many values we need for this capability. */ for (cap = scap, arg_need = 0; *cap; cap++) if (*cap == '%') switch (*++cap) { case 'd': case '2': case '3': case '.': case '+': arg_need++; break; case '%': case '>': case 'i': case 'r': case 'n': case 'B': case 'D': break; default: /* * hpux has lot's of them... */ if (verbose) (void) fprintf(el->el_errfile, "echotc: Warning: unknown termcap %% `%c'.\n", *cap); /* This is bad, but I won't complain */ break; } switch (arg_need) { case 0: argv++; if (*argv && *argv[0]) { if (!silent) (void) fprintf(el->el_errfile, "echotc: Warning: Extra argument `%s'.\n", *argv); return (-1); } term_tputs(el, scap, 1); break; case 1: argv++; if (!*argv || *argv[0] == '\0') { if (!silent) (void) fprintf(el->el_errfile, "echotc: Warning: Missing argument.\n"); return (-1); } arg_cols = 0; i = strtol(*argv, &ep, 10); if (*ep != '\0' || i < 0) { if (!silent) (void) fprintf(el->el_errfile, "echotc: Bad value `%s' for rows.\n", *argv); return (-1); } arg_rows = (int) i; argv++; if (*argv && *argv[0]) { if (!silent) (void) fprintf(el->el_errfile, "echotc: Warning: Extra argument `%s'.\n", *argv); return (-1); } term_tputs(el, tgoto(scap, arg_cols, arg_rows), 1); break; default: /* This is wrong, but I will ignore it... */ if (verbose) (void) fprintf(el->el_errfile, "echotc: Warning: Too many required arguments (%d).\n", arg_need); /* FALLTHROUGH */ case 2: argv++; if (!*argv || *argv[0] == '\0') { if (!silent) (void) fprintf(el->el_errfile, "echotc: Warning: Missing argument.\n"); return (-1); } i = strtol(*argv, &ep, 10); if (*ep != '\0' || i < 0) { if (!silent) (void) fprintf(el->el_errfile, "echotc: Bad value `%s' for cols.\n", *argv); return (-1); } arg_cols = (int) i; argv++; if (!*argv || *argv[0] == '\0') { if (!silent) (void) fprintf(el->el_errfile, "echotc: Warning: Missing argument.\n"); return (-1); } i = strtol(*argv, &ep, 10); if (*ep != '\0' || i < 0) { if (!silent) (void) fprintf(el->el_errfile, "echotc: Bad value `%s' for rows.\n", *argv); return (-1); } arg_rows = (int) i; if (*ep != '\0') { if (!silent) (void) fprintf(el->el_errfile, "echotc: Bad value `%s'.\n", *argv); return (-1); } argv++; if (*argv && *argv[0]) { if (!silent) (void) fprintf(el->el_errfile, "echotc: Warning: Extra argument `%s'.\n", *argv); return (-1); } term_tputs(el, tgoto(scap, arg_cols, arg_rows), arg_rows); break; } return (0); } clt/falcon/editline/src/tokenizer.c000066400000000000000000000232051176363201700176540ustar00rootroot00000000000000/* $NetBSD: tokenizer.c,v 1.15 2009/02/15 21:55:23 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)tokenizer.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: tokenizer.c,v 1.15 2009/02/15 21:55:23 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * tokenize.c: Bourne shell like tokenizer */ #include #include #include "histedit.h" typedef enum { Q_none, Q_single, Q_double, Q_one, Q_doubleone } quote_t; #define IFS "\t \n" #define TOK_KEEP 1 #define TOK_EAT 2 #define WINCR 20 #define AINCR 10 #define tok_strdup(a) strdup(a) #define tok_malloc(a) malloc(a) #define tok_free(a) free(a) #define tok_realloc(a, b) realloc(a, b) struct tokenizer { char *ifs; /* In field separator */ int argc, amax; /* Current and maximum number of args */ char **argv; /* Argument list */ char *wptr, *wmax; /* Space and limit on the word buffer */ char *wstart; /* Beginning of next word */ char *wspace; /* Space of word buffer */ quote_t quote; /* Quoting state */ int flags; /* flags; */ }; private void tok_finish(Tokenizer *); /* tok_finish(): * Finish a word in the tokenizer. */ private void tok_finish(Tokenizer *tok) { *tok->wptr = '\0'; if ((tok->flags & TOK_KEEP) || tok->wptr != tok->wstart) { tok->argv[tok->argc++] = tok->wstart; tok->argv[tok->argc] = NULL; tok->wstart = ++tok->wptr; } tok->flags &= ~TOK_KEEP; } /* tok_init(): * Initialize the tokenizer */ public Tokenizer * tok_init(const char *ifs) { Tokenizer *tok = (Tokenizer *) tok_malloc(sizeof(Tokenizer)); if (tok == NULL) return NULL; tok->ifs = tok_strdup(ifs ? ifs : IFS); if (tok->ifs == NULL) { tok_free((ptr_t)tok); return NULL; } tok->argc = 0; tok->amax = AINCR; tok->argv = (char **) tok_malloc(sizeof(char *) * tok->amax); if (tok->argv == NULL) { tok_free((ptr_t)tok->ifs); tok_free((ptr_t)tok); return NULL; } tok->argv[0] = NULL; tok->wspace = (char *) tok_malloc(WINCR); if (tok->wspace == NULL) { tok_free((ptr_t)tok->argv); tok_free((ptr_t)tok->ifs); tok_free((ptr_t)tok); return NULL; } tok->wmax = tok->wspace + WINCR; tok->wstart = tok->wspace; tok->wptr = tok->wspace; tok->flags = 0; tok->quote = Q_none; return (tok); } /* tok_reset(): * Reset the tokenizer */ public void tok_reset(Tokenizer *tok) { tok->argc = 0; tok->wstart = tok->wspace; tok->wptr = tok->wspace; tok->flags = 0; tok->quote = Q_none; } /* tok_end(): * Clean up */ public void tok_end(Tokenizer *tok) { tok_free((ptr_t) tok->ifs); tok_free((ptr_t) tok->wspace); tok_free((ptr_t) tok->argv); tok_free((ptr_t) tok); } /* tok_line(): * Bourne shell (sh(1)) like tokenizing * Arguments: * tok current tokenizer state (setup with tok_init()) * line line to parse * Returns: * -1 Internal error * 3 Quoted return * 2 Unmatched double quote * 1 Unmatched single quote * 0 Ok * Modifies (if return value is 0): * argc number of arguments * argv argument array * cursorc if !NULL, argv element containing cursor * cursorv if !NULL, offset in argv[cursorc] of cursor */ public int tok_line(Tokenizer *tok, const LineInfo *line, int *argc, const char ***argv, int *cursorc, int *cursoro) { const char *ptr; int cc, co; cc = co = -1; ptr = line->buffer; for (ptr = line->buffer; ;ptr++) { if (ptr >= line->lastchar) ptr = ""; if (ptr == line->cursor) { cc = tok->argc; co = (int)(tok->wptr - tok->wstart); } switch (*ptr) { case '\'': tok->flags |= TOK_KEEP; tok->flags &= ~TOK_EAT; switch (tok->quote) { case Q_none: tok->quote = Q_single; /* Enter single quote * mode */ break; case Q_single: /* Exit single quote mode */ tok->quote = Q_none; break; case Q_one: /* Quote this ' */ tok->quote = Q_none; *tok->wptr++ = *ptr; break; case Q_double: /* Stay in double quote mode */ *tok->wptr++ = *ptr; break; case Q_doubleone: /* Quote this ' */ tok->quote = Q_double; *tok->wptr++ = *ptr; break; default: return (-1); } break; case '"': tok->flags &= ~TOK_EAT; tok->flags |= TOK_KEEP; switch (tok->quote) { case Q_none: /* Enter double quote mode */ tok->quote = Q_double; break; case Q_double: /* Exit double quote mode */ tok->quote = Q_none; break; case Q_one: /* Quote this " */ tok->quote = Q_none; *tok->wptr++ = *ptr; break; case Q_single: /* Stay in single quote mode */ *tok->wptr++ = *ptr; break; case Q_doubleone: /* Quote this " */ tok->quote = Q_double; *tok->wptr++ = *ptr; break; default: return (-1); } break; case '\\': tok->flags |= TOK_KEEP; tok->flags &= ~TOK_EAT; switch (tok->quote) { case Q_none: /* Quote next character */ tok->quote = Q_one; break; case Q_double: /* Quote next character */ tok->quote = Q_doubleone; break; case Q_one: /* Quote this, restore state */ *tok->wptr++ = *ptr; tok->quote = Q_none; break; case Q_single: /* Stay in single quote mode */ *tok->wptr++ = *ptr; break; case Q_doubleone: /* Quote this \ */ tok->quote = Q_double; *tok->wptr++ = *ptr; break; default: return (-1); } break; case '\n': tok->flags &= ~TOK_EAT; switch (tok->quote) { case Q_none: goto tok_line_outok; case Q_single: case Q_double: *tok->wptr++ = *ptr; /* Add the return */ break; case Q_doubleone: /* Back to double, eat the '\n' */ tok->flags |= TOK_EAT; tok->quote = Q_double; break; case Q_one: /* No quote, more eat the '\n' */ tok->flags |= TOK_EAT; tok->quote = Q_none; break; default: return (0); } break; case '\0': switch (tok->quote) { case Q_none: /* Finish word and return */ if (tok->flags & TOK_EAT) { tok->flags &= ~TOK_EAT; return (3); } goto tok_line_outok; case Q_single: return (1); case Q_double: return (2); case Q_doubleone: tok->quote = Q_double; *tok->wptr++ = *ptr; break; case Q_one: tok->quote = Q_none; *tok->wptr++ = *ptr; break; default: return (-1); } break; default: tok->flags &= ~TOK_EAT; switch (tok->quote) { case Q_none: if (strchr(tok->ifs, *ptr) != NULL) tok_finish(tok); else *tok->wptr++ = *ptr; break; case Q_single: case Q_double: *tok->wptr++ = *ptr; break; case Q_doubleone: *tok->wptr++ = '\\'; tok->quote = Q_double; *tok->wptr++ = *ptr; break; case Q_one: tok->quote = Q_none; *tok->wptr++ = *ptr; break; default: return (-1); } break; } if (tok->wptr >= tok->wmax - 4) { size_t size = tok->wmax - tok->wspace + WINCR; char *s = (char *) tok_realloc(tok->wspace, size); if (s == NULL) return (-1); if (s != tok->wspace) { int i; for (i = 0; i < tok->argc; i++) { tok->argv[i] = (tok->argv[i] - tok->wspace) + s; } tok->wptr = (tok->wptr - tok->wspace) + s; tok->wstart = (tok->wstart - tok->wspace) + s; tok->wspace = s; } tok->wmax = s + size; } if (tok->argc >= tok->amax - 4) { char **p; tok->amax += AINCR; p = (char **) tok_realloc(tok->argv, tok->amax * sizeof(char *)); if (p == NULL) return (-1); tok->argv = p; } } tok_line_outok: if (cc == -1 && co == -1) { cc = tok->argc; co = (int)(tok->wptr - tok->wstart); } if (cursorc != NULL) *cursorc = cc; if (cursoro != NULL) *cursoro = co; tok_finish(tok); *argv = (const char **)tok->argv; *argc = tok->argc; return (0); } /* tok_str(): * Simpler version of tok_line, taking a NUL terminated line * and splitting into words, ignoring cursor state. */ public int tok_str(Tokenizer *tok, const char *line, int *argc, const char ***argv) { LineInfo li; memset(&li, 0, sizeof(li)); li.buffer = line; li.cursor = li.lastchar = strchr(line, '\0'); return (tok_line(tok, &li, argc, argv, NULL, NULL)); } clt/falcon/editline/src/tty.c000066400000000000000000000774321176363201700164750ustar00rootroot00000000000000/* $NetBSD: tty.c,v 1.31 2009/07/22 15:58:09 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)tty.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: tty.c,v 1.31 2009/07/22 15:58:09 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * tty.c: tty interface stuff */ #include #include #include "tty.h" #include "el.h" typedef struct ttymodes_t { const char *m_name; unsigned int m_value; int m_type; } ttymodes_t; typedef struct ttymap_t { int nch, och; /* Internal and termio rep of chars */ el_action_t bind[3]; /* emacs, vi, and vi-cmd */ } ttymap_t; private const ttyperm_t ttyperm = { { {"iflag:", ICRNL, (INLCR | IGNCR)}, {"oflag:", (OPOST | ONLCR), ONLRET}, {"cflag:", 0, 0}, {"lflag:", (ISIG | ICANON | ECHO | ECHOE | ECHOCTL | IEXTEN), (NOFLSH | ECHONL | EXTPROC | FLUSHO)}, {"chars:", 0, 0}, }, { {"iflag:", (INLCR | ICRNL), IGNCR}, {"oflag:", (OPOST | ONLCR), ONLRET}, {"cflag:", 0, 0}, {"lflag:", ISIG, (NOFLSH | ICANON | ECHO | ECHOK | ECHONL | EXTPROC | IEXTEN | FLUSHO)}, {"chars:", (C_SH(C_MIN) | C_SH(C_TIME) | C_SH(C_SWTCH) | C_SH(C_DSWTCH) | C_SH(C_SUSP) | C_SH(C_DSUSP) | C_SH(C_EOL) | C_SH(C_DISCARD) | C_SH(C_PGOFF) | C_SH(C_PAGE) | C_SH(C_STATUS)), 0} }, { {"iflag:", 0, IXON | IXOFF | INLCR | ICRNL}, {"oflag:", 0, 0}, {"cflag:", 0, 0}, {"lflag:", 0, ISIG | IEXTEN}, {"chars:", 0, 0}, } }; private const ttychar_t ttychar = { { CINTR, CQUIT, CERASE, CKILL, CEOF, CEOL, CEOL2, CSWTCH, CDSWTCH, CERASE2, CSTART, CSTOP, CWERASE, CSUSP, CDSUSP, CREPRINT, CDISCARD, CLNEXT, CSTATUS, CPAGE, CPGOFF, CKILL2, CBRK, CMIN, CTIME }, { CINTR, CQUIT, CERASE, CKILL, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, CERASE2, CSTART, CSTOP, _POSIX_VDISABLE, CSUSP, _POSIX_VDISABLE, _POSIX_VDISABLE, CDISCARD, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; private const ttymap_t tty_map[] = { #ifdef VERASE {C_ERASE, VERASE, {EM_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR}}, #endif /* VERASE */ #ifdef VERASE2 {C_ERASE2, VERASE2, {EM_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR}}, #endif /* VERASE2 */ #ifdef VKILL {C_KILL, VKILL, {EM_KILL_LINE, VI_KILL_LINE_PREV, ED_UNASSIGNED}}, #endif /* VKILL */ #ifdef VKILL2 {C_KILL2, VKILL2, {EM_KILL_LINE, VI_KILL_LINE_PREV, ED_UNASSIGNED}}, #endif /* VKILL2 */ #ifdef VEOF {C_EOF, VEOF, {EM_DELETE_OR_LIST, VI_LIST_OR_EOF, ED_UNASSIGNED}}, #endif /* VEOF */ #ifdef VWERASE {C_WERASE, VWERASE, {ED_DELETE_PREV_WORD, ED_DELETE_PREV_WORD, ED_PREV_WORD}}, #endif /* VWERASE */ #ifdef VREPRINT {C_REPRINT, VREPRINT, {ED_REDISPLAY, ED_INSERT, ED_REDISPLAY}}, #endif /* VREPRINT */ #ifdef VLNEXT {C_LNEXT, VLNEXT, {ED_QUOTED_INSERT, ED_QUOTED_INSERT, ED_UNASSIGNED}}, #endif /* VLNEXT */ {-1, -1, {ED_UNASSIGNED, ED_UNASSIGNED, ED_UNASSIGNED}} }; private const ttymodes_t ttymodes[] = { #ifdef IGNBRK {"ignbrk", IGNBRK, MD_INP}, #endif /* IGNBRK */ #ifdef BRKINT {"brkint", BRKINT, MD_INP}, #endif /* BRKINT */ #ifdef IGNPAR {"ignpar", IGNPAR, MD_INP}, #endif /* IGNPAR */ #ifdef PARMRK {"parmrk", PARMRK, MD_INP}, #endif /* PARMRK */ #ifdef INPCK {"inpck", INPCK, MD_INP}, #endif /* INPCK */ #ifdef ISTRIP {"istrip", ISTRIP, MD_INP}, #endif /* ISTRIP */ #ifdef INLCR {"inlcr", INLCR, MD_INP}, #endif /* INLCR */ #ifdef IGNCR {"igncr", IGNCR, MD_INP}, #endif /* IGNCR */ #ifdef ICRNL {"icrnl", ICRNL, MD_INP}, #endif /* ICRNL */ #ifdef IUCLC {"iuclc", IUCLC, MD_INP}, #endif /* IUCLC */ #ifdef IXON {"ixon", IXON, MD_INP}, #endif /* IXON */ #ifdef IXANY {"ixany", IXANY, MD_INP}, #endif /* IXANY */ #ifdef IXOFF {"ixoff", IXOFF, MD_INP}, #endif /* IXOFF */ #ifdef IMAXBEL {"imaxbel", IMAXBEL, MD_INP}, #endif /* IMAXBEL */ #ifdef OPOST {"opost", OPOST, MD_OUT}, #endif /* OPOST */ #ifdef OLCUC {"olcuc", OLCUC, MD_OUT}, #endif /* OLCUC */ #ifdef ONLCR {"onlcr", ONLCR, MD_OUT}, #endif /* ONLCR */ #ifdef OCRNL {"ocrnl", OCRNL, MD_OUT}, #endif /* OCRNL */ #ifdef ONOCR {"onocr", ONOCR, MD_OUT}, #endif /* ONOCR */ #ifdef ONOEOT {"onoeot", ONOEOT, MD_OUT}, #endif /* ONOEOT */ #ifdef ONLRET {"onlret", ONLRET, MD_OUT}, #endif /* ONLRET */ #ifdef OFILL {"ofill", OFILL, MD_OUT}, #endif /* OFILL */ #ifdef OFDEL {"ofdel", OFDEL, MD_OUT}, #endif /* OFDEL */ #ifdef NLDLY {"nldly", NLDLY, MD_OUT}, #endif /* NLDLY */ #ifdef CRDLY {"crdly", CRDLY, MD_OUT}, #endif /* CRDLY */ #ifdef TABDLY {"tabdly", TABDLY, MD_OUT}, #endif /* TABDLY */ #ifdef XTABS {"xtabs", XTABS, MD_OUT}, #endif /* XTABS */ #ifdef BSDLY {"bsdly", BSDLY, MD_OUT}, #endif /* BSDLY */ #ifdef VTDLY {"vtdly", VTDLY, MD_OUT}, #endif /* VTDLY */ #ifdef FFDLY {"ffdly", FFDLY, MD_OUT}, #endif /* FFDLY */ #ifdef PAGEOUT {"pageout", PAGEOUT, MD_OUT}, #endif /* PAGEOUT */ #ifdef WRAP {"wrap", WRAP, MD_OUT}, #endif /* WRAP */ #ifdef CIGNORE {"cignore", CIGNORE, MD_CTL}, #endif /* CBAUD */ #ifdef CBAUD {"cbaud", CBAUD, MD_CTL}, #endif /* CBAUD */ #ifdef CSTOPB {"cstopb", CSTOPB, MD_CTL}, #endif /* CSTOPB */ #ifdef CREAD {"cread", CREAD, MD_CTL}, #endif /* CREAD */ #ifdef PARENB {"parenb", PARENB, MD_CTL}, #endif /* PARENB */ #ifdef PARODD {"parodd", PARODD, MD_CTL}, #endif /* PARODD */ #ifdef HUPCL {"hupcl", HUPCL, MD_CTL}, #endif /* HUPCL */ #ifdef CLOCAL {"clocal", CLOCAL, MD_CTL}, #endif /* CLOCAL */ #ifdef LOBLK {"loblk", LOBLK, MD_CTL}, #endif /* LOBLK */ #ifdef CIBAUD {"cibaud", CIBAUD, MD_CTL}, #endif /* CIBAUD */ #ifdef CRTSCTS #ifdef CCTS_OFLOW {"ccts_oflow", CCTS_OFLOW, MD_CTL}, #else {"crtscts", CRTSCTS, MD_CTL}, #endif /* CCTS_OFLOW */ #endif /* CRTSCTS */ #ifdef CRTS_IFLOW {"crts_iflow", CRTS_IFLOW, MD_CTL}, #endif /* CRTS_IFLOW */ #ifdef CDTRCTS {"cdtrcts", CDTRCTS, MD_CTL}, #endif /* CDTRCTS */ #ifdef MDMBUF {"mdmbuf", MDMBUF, MD_CTL}, #endif /* MDMBUF */ #ifdef RCV1EN {"rcv1en", RCV1EN, MD_CTL}, #endif /* RCV1EN */ #ifdef XMT1EN {"xmt1en", XMT1EN, MD_CTL}, #endif /* XMT1EN */ #ifdef ISIG {"isig", ISIG, MD_LIN}, #endif /* ISIG */ #ifdef ICANON {"icanon", ICANON, MD_LIN}, #endif /* ICANON */ #ifdef XCASE {"xcase", XCASE, MD_LIN}, #endif /* XCASE */ #ifdef ECHO {"echo", ECHO, MD_LIN}, #endif /* ECHO */ #ifdef ECHOE {"echoe", ECHOE, MD_LIN}, #endif /* ECHOE */ #ifdef ECHOK {"echok", ECHOK, MD_LIN}, #endif /* ECHOK */ #ifdef ECHONL {"echonl", ECHONL, MD_LIN}, #endif /* ECHONL */ #ifdef NOFLSH {"noflsh", NOFLSH, MD_LIN}, #endif /* NOFLSH */ #ifdef TOSTOP {"tostop", TOSTOP, MD_LIN}, #endif /* TOSTOP */ #ifdef ECHOCTL {"echoctl", ECHOCTL, MD_LIN}, #endif /* ECHOCTL */ #ifdef ECHOPRT {"echoprt", ECHOPRT, MD_LIN}, #endif /* ECHOPRT */ #ifdef ECHOKE {"echoke", ECHOKE, MD_LIN}, #endif /* ECHOKE */ #ifdef DEFECHO {"defecho", DEFECHO, MD_LIN}, #endif /* DEFECHO */ #ifdef FLUSHO {"flusho", FLUSHO, MD_LIN}, #endif /* FLUSHO */ #ifdef PENDIN {"pendin", PENDIN, MD_LIN}, #endif /* PENDIN */ #ifdef IEXTEN {"iexten", IEXTEN, MD_LIN}, #endif /* IEXTEN */ #ifdef NOKERNINFO {"nokerninfo", NOKERNINFO, MD_LIN}, #endif /* NOKERNINFO */ #ifdef ALTWERASE {"altwerase", ALTWERASE, MD_LIN}, #endif /* ALTWERASE */ #ifdef EXTPROC {"extproc", EXTPROC, MD_LIN}, #endif /* EXTPROC */ #if defined(VINTR) {"intr", C_SH(C_INTR), MD_CHAR}, #endif /* VINTR */ #if defined(VQUIT) {"quit", C_SH(C_QUIT), MD_CHAR}, #endif /* VQUIT */ #if defined(VERASE) {"erase", C_SH(C_ERASE), MD_CHAR}, #endif /* VERASE */ #if defined(VKILL) {"kill", C_SH(C_KILL), MD_CHAR}, #endif /* VKILL */ #if defined(VEOF) {"eof", C_SH(C_EOF), MD_CHAR}, #endif /* VEOF */ #if defined(VEOL) {"eol", C_SH(C_EOL), MD_CHAR}, #endif /* VEOL */ #if defined(VEOL2) {"eol2", C_SH(C_EOL2), MD_CHAR}, #endif /* VEOL2 */ #if defined(VSWTCH) {"swtch", C_SH(C_SWTCH), MD_CHAR}, #endif /* VSWTCH */ #if defined(VDSWTCH) {"dswtch", C_SH(C_DSWTCH), MD_CHAR}, #endif /* VDSWTCH */ #if defined(VERASE2) {"erase2", C_SH(C_ERASE2), MD_CHAR}, #endif /* VERASE2 */ #if defined(VSTART) {"start", C_SH(C_START), MD_CHAR}, #endif /* VSTART */ #if defined(VSTOP) {"stop", C_SH(C_STOP), MD_CHAR}, #endif /* VSTOP */ #if defined(VWERASE) {"werase", C_SH(C_WERASE), MD_CHAR}, #endif /* VWERASE */ #if defined(VSUSP) {"susp", C_SH(C_SUSP), MD_CHAR}, #endif /* VSUSP */ #if defined(VDSUSP) {"dsusp", C_SH(C_DSUSP), MD_CHAR}, #endif /* VDSUSP */ #if defined(VREPRINT) {"reprint", C_SH(C_REPRINT), MD_CHAR}, #endif /* VREPRINT */ #if defined(VDISCARD) {"discard", C_SH(C_DISCARD), MD_CHAR}, #endif /* VDISCARD */ #if defined(VLNEXT) {"lnext", C_SH(C_LNEXT), MD_CHAR}, #endif /* VLNEXT */ #if defined(VSTATUS) {"status", C_SH(C_STATUS), MD_CHAR}, #endif /* VSTATUS */ #if defined(VPAGE) {"page", C_SH(C_PAGE), MD_CHAR}, #endif /* VPAGE */ #if defined(VPGOFF) {"pgoff", C_SH(C_PGOFF), MD_CHAR}, #endif /* VPGOFF */ #if defined(VKILL2) {"kill2", C_SH(C_KILL2), MD_CHAR}, #endif /* VKILL2 */ #if defined(VBRK) {"brk", C_SH(C_BRK), MD_CHAR}, #endif /* VBRK */ #if defined(VMIN) {"min", C_SH(C_MIN), MD_CHAR}, #endif /* VMIN */ #if defined(VTIME) {"time", C_SH(C_TIME), MD_CHAR}, #endif /* VTIME */ {NULL, 0, -1}, }; #define tty__gettabs(td) ((((td)->c_oflag & TAB3) == TAB3) ? 0 : 1) #define tty__geteightbit(td) (((td)->c_cflag & CSIZE) == CS8) #define tty__cooked_mode(td) ((td)->c_lflag & ICANON) private int tty_getty(EditLine *, struct termios *); private int tty_setty(EditLine *, int, const struct termios *); private int tty__getcharindex(int); private void tty__getchar(struct termios *, unsigned char *); private void tty__setchar(struct termios *, unsigned char *); private speed_t tty__getspeed(struct termios *); private int tty_setup(EditLine *); #define t_qu t_ts /* tty_getty(): * Wrapper for tcgetattr to handle EINTR */ private int tty_getty(EditLine *el, struct termios *t) { int rv; while ((rv = tcgetattr(el->el_infd, t)) == -1 && errno == EINTR) continue; return rv; } /* tty_setty(): * Wrapper for tcsetattr to handle EINTR */ private int tty_setty(EditLine *el, int action, const struct termios *t) { int rv; while ((rv = tcsetattr(el->el_infd, action, t)) == -1 && errno == EINTR) continue; return rv; } /* tty_setup(): * Get the tty parameters and initialize the editing state */ private int tty_setup(EditLine *el) { int rst = 1; if (el->el_flags & EDIT_DISABLED) return (0); if (tty_getty(el, &el->el_tty.t_ed) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "tty_setup: tty_getty: %s\n", strerror(errno)); #endif /* DEBUG_TTY */ return (-1); } el->el_tty.t_ts = el->el_tty.t_ex = el->el_tty.t_ed; el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ex); el->el_tty.t_tabs = tty__gettabs(&el->el_tty.t_ex); el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ex); el->el_tty.t_ex.c_iflag &= ~el->el_tty.t_t[EX_IO][MD_INP].t_clrmask; el->el_tty.t_ex.c_iflag |= el->el_tty.t_t[EX_IO][MD_INP].t_setmask; el->el_tty.t_ex.c_oflag &= ~el->el_tty.t_t[EX_IO][MD_OUT].t_clrmask; el->el_tty.t_ex.c_oflag |= el->el_tty.t_t[EX_IO][MD_OUT].t_setmask; el->el_tty.t_ex.c_cflag &= ~el->el_tty.t_t[EX_IO][MD_CTL].t_clrmask; el->el_tty.t_ex.c_cflag |= el->el_tty.t_t[EX_IO][MD_CTL].t_setmask; el->el_tty.t_ex.c_lflag &= ~el->el_tty.t_t[EX_IO][MD_LIN].t_clrmask; el->el_tty.t_ex.c_lflag |= el->el_tty.t_t[EX_IO][MD_LIN].t_setmask; /* * Reset the tty chars to reasonable defaults * If they are disabled, then enable them. */ if (rst) { if (tty__cooked_mode(&el->el_tty.t_ts)) { tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]); /* * Don't affect CMIN and CTIME for the editor mode */ for (rst = 0; rst < C_NCC - 2; rst++) if (el->el_tty.t_c[TS_IO][rst] != el->el_tty.t_vdisable && el->el_tty.t_c[ED_IO][rst] != el->el_tty.t_vdisable) el->el_tty.t_c[ED_IO][rst] = el->el_tty.t_c[TS_IO][rst]; for (rst = 0; rst < C_NCC; rst++) if (el->el_tty.t_c[TS_IO][rst] != el->el_tty.t_vdisable) el->el_tty.t_c[EX_IO][rst] = el->el_tty.t_c[TS_IO][rst]; } tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]); if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ex) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "tty_setup: tty_setty: %s\n", strerror(errno)); #endif /* DEBUG_TTY */ return (-1); } } #ifdef notdef else tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]); #endif el->el_tty.t_ed.c_iflag &= ~el->el_tty.t_t[ED_IO][MD_INP].t_clrmask; el->el_tty.t_ed.c_iflag |= el->el_tty.t_t[ED_IO][MD_INP].t_setmask; el->el_tty.t_ed.c_oflag &= ~el->el_tty.t_t[ED_IO][MD_OUT].t_clrmask; el->el_tty.t_ed.c_oflag |= el->el_tty.t_t[ED_IO][MD_OUT].t_setmask; el->el_tty.t_ed.c_cflag &= ~el->el_tty.t_t[ED_IO][MD_CTL].t_clrmask; el->el_tty.t_ed.c_cflag |= el->el_tty.t_t[ED_IO][MD_CTL].t_setmask; el->el_tty.t_ed.c_lflag &= ~el->el_tty.t_t[ED_IO][MD_LIN].t_clrmask; el->el_tty.t_ed.c_lflag |= el->el_tty.t_t[ED_IO][MD_LIN].t_setmask; tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]); tty_bind_char(el, 1); return (0); } protected int tty_init(EditLine *el) { el->el_tty.t_mode = EX_IO; el->el_tty.t_vdisable = _POSIX_VDISABLE; (void) memcpy(el->el_tty.t_t, ttyperm, sizeof(ttyperm_t)); (void) memcpy(el->el_tty.t_c, ttychar, sizeof(ttychar_t)); return (tty_setup(el)); } /* tty_end(): * Restore the tty to its original settings */ protected void /*ARGSUSED*/ tty_end(EditLine *el __attribute__((__unused__))) { /* XXX: Maybe reset to an initial state? */ } /* tty__getspeed(): * Get the tty speed */ private speed_t tty__getspeed(struct termios *td) { speed_t spd; if ((spd = cfgetispeed(td)) == 0) spd = cfgetospeed(td); return (spd); } /* tty__getspeed(): * Return the index of the asked char in the c_cc array */ private int tty__getcharindex(int i) { switch (i) { #ifdef VINTR case C_INTR: return VINTR; #endif /* VINTR */ #ifdef VQUIT case C_QUIT: return VQUIT; #endif /* VQUIT */ #ifdef VERASE case C_ERASE: return VERASE; #endif /* VERASE */ #ifdef VKILL case C_KILL: return VKILL; #endif /* VKILL */ #ifdef VEOF case C_EOF: return VEOF; #endif /* VEOF */ #ifdef VEOL case C_EOL: return VEOL; #endif /* VEOL */ #ifdef VEOL2 case C_EOL2: return VEOL2; #endif /* VEOL2 */ #ifdef VSWTCH case C_SWTCH: return VSWTCH; #endif /* VSWTCH */ #ifdef VDSWTCH case C_DSWTCH: return VDSWTCH; #endif /* VDSWTCH */ #ifdef VERASE2 case C_ERASE2: return VERASE2; #endif /* VERASE2 */ #ifdef VSTART case C_START: return VSTART; #endif /* VSTART */ #ifdef VSTOP case C_STOP: return VSTOP; #endif /* VSTOP */ #ifdef VWERASE case C_WERASE: return VWERASE; #endif /* VWERASE */ #ifdef VSUSP case C_SUSP: return VSUSP; #endif /* VSUSP */ #ifdef VDSUSP case C_DSUSP: return VDSUSP; #endif /* VDSUSP */ #ifdef VREPRINT case C_REPRINT: return VREPRINT; #endif /* VREPRINT */ #ifdef VDISCARD case C_DISCARD: return VDISCARD; #endif /* VDISCARD */ #ifdef VLNEXT case C_LNEXT: return VLNEXT; #endif /* VLNEXT */ #ifdef VSTATUS case C_STATUS: return VSTATUS; #endif /* VSTATUS */ #ifdef VPAGE case C_PAGE: return VPAGE; #endif /* VPAGE */ #ifdef VPGOFF case C_PGOFF: return VPGOFF; #endif /* VPGOFF */ #ifdef VKILL2 case C_KILL2: return VKILL2; #endif /* KILL2 */ #ifdef VMIN case C_MIN: return VMIN; #endif /* VMIN */ #ifdef VTIME case C_TIME: return VTIME; #endif /* VTIME */ default: return -1; } } /* tty__getchar(): * Get the tty characters */ private void tty__getchar(struct termios *td, unsigned char *s) { #ifdef VINTR s[C_INTR] = td->c_cc[VINTR]; #endif /* VINTR */ #ifdef VQUIT s[C_QUIT] = td->c_cc[VQUIT]; #endif /* VQUIT */ #ifdef VERASE s[C_ERASE] = td->c_cc[VERASE]; #endif /* VERASE */ #ifdef VKILL s[C_KILL] = td->c_cc[VKILL]; #endif /* VKILL */ #ifdef VEOF s[C_EOF] = td->c_cc[VEOF]; #endif /* VEOF */ #ifdef VEOL s[C_EOL] = td->c_cc[VEOL]; #endif /* VEOL */ #ifdef VEOL2 s[C_EOL2] = td->c_cc[VEOL2]; #endif /* VEOL2 */ #ifdef VSWTCH s[C_SWTCH] = td->c_cc[VSWTCH]; #endif /* VSWTCH */ #ifdef VDSWTCH s[C_DSWTCH] = td->c_cc[VDSWTCH]; #endif /* VDSWTCH */ #ifdef VERASE2 s[C_ERASE2] = td->c_cc[VERASE2]; #endif /* VERASE2 */ #ifdef VSTART s[C_START] = td->c_cc[VSTART]; #endif /* VSTART */ #ifdef VSTOP s[C_STOP] = td->c_cc[VSTOP]; #endif /* VSTOP */ #ifdef VWERASE s[C_WERASE] = td->c_cc[VWERASE]; #endif /* VWERASE */ #ifdef VSUSP s[C_SUSP] = td->c_cc[VSUSP]; #endif /* VSUSP */ #ifdef VDSUSP s[C_DSUSP] = td->c_cc[VDSUSP]; #endif /* VDSUSP */ #ifdef VREPRINT s[C_REPRINT] = td->c_cc[VREPRINT]; #endif /* VREPRINT */ #ifdef VDISCARD s[C_DISCARD] = td->c_cc[VDISCARD]; #endif /* VDISCARD */ #ifdef VLNEXT s[C_LNEXT] = td->c_cc[VLNEXT]; #endif /* VLNEXT */ #ifdef VSTATUS s[C_STATUS] = td->c_cc[VSTATUS]; #endif /* VSTATUS */ #ifdef VPAGE s[C_PAGE] = td->c_cc[VPAGE]; #endif /* VPAGE */ #ifdef VPGOFF s[C_PGOFF] = td->c_cc[VPGOFF]; #endif /* VPGOFF */ #ifdef VKILL2 s[C_KILL2] = td->c_cc[VKILL2]; #endif /* KILL2 */ #ifdef VMIN s[C_MIN] = td->c_cc[VMIN]; #endif /* VMIN */ #ifdef VTIME s[C_TIME] = td->c_cc[VTIME]; #endif /* VTIME */ } /* tty__getchar */ /* tty__setchar(): * Set the tty characters */ private void tty__setchar(struct termios *td, unsigned char *s) { #ifdef VINTR td->c_cc[VINTR] = s[C_INTR]; #endif /* VINTR */ #ifdef VQUIT td->c_cc[VQUIT] = s[C_QUIT]; #endif /* VQUIT */ #ifdef VERASE td->c_cc[VERASE] = s[C_ERASE]; #endif /* VERASE */ #ifdef VKILL td->c_cc[VKILL] = s[C_KILL]; #endif /* VKILL */ #ifdef VEOF td->c_cc[VEOF] = s[C_EOF]; #endif /* VEOF */ #ifdef VEOL td->c_cc[VEOL] = s[C_EOL]; #endif /* VEOL */ #ifdef VEOL2 td->c_cc[VEOL2] = s[C_EOL2]; #endif /* VEOL2 */ #ifdef VSWTCH td->c_cc[VSWTCH] = s[C_SWTCH]; #endif /* VSWTCH */ #ifdef VDSWTCH td->c_cc[VDSWTCH] = s[C_DSWTCH]; #endif /* VDSWTCH */ #ifdef VERASE2 td->c_cc[VERASE2] = s[C_ERASE2]; #endif /* VERASE2 */ #ifdef VSTART td->c_cc[VSTART] = s[C_START]; #endif /* VSTART */ #ifdef VSTOP td->c_cc[VSTOP] = s[C_STOP]; #endif /* VSTOP */ #ifdef VWERASE td->c_cc[VWERASE] = s[C_WERASE]; #endif /* VWERASE */ #ifdef VSUSP td->c_cc[VSUSP] = s[C_SUSP]; #endif /* VSUSP */ #ifdef VDSUSP td->c_cc[VDSUSP] = s[C_DSUSP]; #endif /* VDSUSP */ #ifdef VREPRINT td->c_cc[VREPRINT] = s[C_REPRINT]; #endif /* VREPRINT */ #ifdef VDISCARD td->c_cc[VDISCARD] = s[C_DISCARD]; #endif /* VDISCARD */ #ifdef VLNEXT td->c_cc[VLNEXT] = s[C_LNEXT]; #endif /* VLNEXT */ #ifdef VSTATUS td->c_cc[VSTATUS] = s[C_STATUS]; #endif /* VSTATUS */ #ifdef VPAGE td->c_cc[VPAGE] = s[C_PAGE]; #endif /* VPAGE */ #ifdef VPGOFF td->c_cc[VPGOFF] = s[C_PGOFF]; #endif /* VPGOFF */ #ifdef VKILL2 td->c_cc[VKILL2] = s[C_KILL2]; #endif /* VKILL2 */ #ifdef VMIN td->c_cc[VMIN] = s[C_MIN]; #endif /* VMIN */ #ifdef VTIME td->c_cc[VTIME] = s[C_TIME]; #endif /* VTIME */ } /* tty__setchar */ /* tty_bind_char(): * Rebind the editline functions */ protected void tty_bind_char(EditLine *el, int force) { unsigned char *t_n = el->el_tty.t_c[ED_IO]; unsigned char *t_o = el->el_tty.t_ed.c_cc; unsigned char new[2], old[2]; const ttymap_t *tp; el_action_t *map, *alt; const el_action_t *dmap, *dalt; new[1] = old[1] = '\0'; map = el->el_map.key; alt = el->el_map.alt; if (el->el_map.type == MAP_VI) { dmap = el->el_map.vii; dalt = el->el_map.vic; } else { dmap = el->el_map.emacs; dalt = NULL; } for (tp = tty_map; tp->nch != -1; tp++) { new[0] = t_n[tp->nch]; old[0] = t_o[tp->och]; if (new[0] == old[0] && !force) continue; /* Put the old default binding back, and set the new binding */ key_clear(el, map, (char *)old); map[old[0]] = dmap[old[0]]; key_clear(el, map, (char *)new); /* MAP_VI == 1, MAP_EMACS == 0... */ map[new[0]] = tp->bind[el->el_map.type]; if (dalt) { key_clear(el, alt, (char *)old); alt[old[0]] = dalt[old[0]]; key_clear(el, alt, (char *)new); alt[new[0]] = tp->bind[el->el_map.type + 1]; } } } /* tty_rawmode(): * Set terminal into 1 character at a time mode. */ protected int tty_rawmode(EditLine *el) { if (el->el_tty.t_mode == ED_IO || el->el_tty.t_mode == QU_IO) return (0); if (el->el_flags & EDIT_DISABLED) return (0); if (tty_getty(el, &el->el_tty.t_ts) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "tty_rawmode: tty_getty: %s\n", strerror(errno)); #endif /* DEBUG_TTY */ return (-1); } /* * We always keep up with the eight bit setting and the speed of the * tty. But only we only believe changes that are made to cooked mode! */ el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ts); el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ts); if (tty__getspeed(&el->el_tty.t_ex) != el->el_tty.t_speed || tty__getspeed(&el->el_tty.t_ed) != el->el_tty.t_speed) { (void) cfsetispeed(&el->el_tty.t_ex, el->el_tty.t_speed); (void) cfsetospeed(&el->el_tty.t_ex, el->el_tty.t_speed); (void) cfsetispeed(&el->el_tty.t_ed, el->el_tty.t_speed); (void) cfsetospeed(&el->el_tty.t_ed, el->el_tty.t_speed); } if (tty__cooked_mode(&el->el_tty.t_ts)) { if (el->el_tty.t_ts.c_cflag != el->el_tty.t_ex.c_cflag) { el->el_tty.t_ex.c_cflag = el->el_tty.t_ts.c_cflag; el->el_tty.t_ex.c_cflag &= ~el->el_tty.t_t[EX_IO][MD_CTL].t_clrmask; el->el_tty.t_ex.c_cflag |= el->el_tty.t_t[EX_IO][MD_CTL].t_setmask; el->el_tty.t_ed.c_cflag = el->el_tty.t_ts.c_cflag; el->el_tty.t_ed.c_cflag &= ~el->el_tty.t_t[ED_IO][MD_CTL].t_clrmask; el->el_tty.t_ed.c_cflag |= el->el_tty.t_t[ED_IO][MD_CTL].t_setmask; } if ((el->el_tty.t_ts.c_lflag != el->el_tty.t_ex.c_lflag) && (el->el_tty.t_ts.c_lflag != el->el_tty.t_ed.c_lflag)) { el->el_tty.t_ex.c_lflag = el->el_tty.t_ts.c_lflag; el->el_tty.t_ex.c_lflag &= ~el->el_tty.t_t[EX_IO][MD_LIN].t_clrmask; el->el_tty.t_ex.c_lflag |= el->el_tty.t_t[EX_IO][MD_LIN].t_setmask; el->el_tty.t_ed.c_lflag = el->el_tty.t_ts.c_lflag; el->el_tty.t_ed.c_lflag &= ~el->el_tty.t_t[ED_IO][MD_LIN].t_clrmask; el->el_tty.t_ed.c_lflag |= el->el_tty.t_t[ED_IO][MD_LIN].t_setmask; } if ((el->el_tty.t_ts.c_iflag != el->el_tty.t_ex.c_iflag) && (el->el_tty.t_ts.c_iflag != el->el_tty.t_ed.c_iflag)) { el->el_tty.t_ex.c_iflag = el->el_tty.t_ts.c_iflag; el->el_tty.t_ex.c_iflag &= ~el->el_tty.t_t[EX_IO][MD_INP].t_clrmask; el->el_tty.t_ex.c_iflag |= el->el_tty.t_t[EX_IO][MD_INP].t_setmask; el->el_tty.t_ed.c_iflag = el->el_tty.t_ts.c_iflag; el->el_tty.t_ed.c_iflag &= ~el->el_tty.t_t[ED_IO][MD_INP].t_clrmask; el->el_tty.t_ed.c_iflag |= el->el_tty.t_t[ED_IO][MD_INP].t_setmask; } if ((el->el_tty.t_ts.c_oflag != el->el_tty.t_ex.c_oflag) && (el->el_tty.t_ts.c_oflag != el->el_tty.t_ed.c_oflag)) { el->el_tty.t_ex.c_oflag = el->el_tty.t_ts.c_oflag; el->el_tty.t_ex.c_oflag &= ~el->el_tty.t_t[EX_IO][MD_OUT].t_clrmask; el->el_tty.t_ex.c_oflag |= el->el_tty.t_t[EX_IO][MD_OUT].t_setmask; el->el_tty.t_ed.c_oflag = el->el_tty.t_ts.c_oflag; el->el_tty.t_ed.c_oflag &= ~el->el_tty.t_t[ED_IO][MD_OUT].t_clrmask; el->el_tty.t_ed.c_oflag |= el->el_tty.t_t[ED_IO][MD_OUT].t_setmask; } if (tty__gettabs(&el->el_tty.t_ex) == 0) el->el_tty.t_tabs = 0; else el->el_tty.t_tabs = EL_CAN_TAB ? 1 : 0; { int i; tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]); /* * Check if the user made any changes. * If he did, then propagate the changes to the * edit and execute data structures. */ for (i = 0; i < C_NCC; i++) if (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i]) break; if (i != C_NCC) { /* * Propagate changes only to the unprotected * chars that have been modified just now. */ for (i = 0; i < C_NCC; i++) { if (!((el->el_tty.t_t[ED_IO][MD_CHAR].t_setmask & C_SH(i))) && (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i])) el->el_tty.t_c[ED_IO][i] = el->el_tty.t_c[TS_IO][i]; if (el->el_tty.t_t[ED_IO][MD_CHAR].t_clrmask & C_SH(i)) el->el_tty.t_c[ED_IO][i] = el->el_tty.t_vdisable; } tty_bind_char(el, 0); tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]); for (i = 0; i < C_NCC; i++) { if (!((el->el_tty.t_t[EX_IO][MD_CHAR].t_setmask & C_SH(i))) && (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i])) el->el_tty.t_c[EX_IO][i] = el->el_tty.t_c[TS_IO][i]; if (el->el_tty.t_t[EX_IO][MD_CHAR].t_clrmask & C_SH(i)) el->el_tty.t_c[EX_IO][i] = el->el_tty.t_vdisable; } tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]); } } } if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ed) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "tty_rawmode: tty_setty: %s\n", strerror(errno)); #endif /* DEBUG_TTY */ return (-1); } el->el_tty.t_mode = ED_IO; return (0); } /* tty_cookedmode(): * Set the tty back to normal mode */ protected int tty_cookedmode(EditLine *el) { /* set tty in normal setup */ if (el->el_tty.t_mode == EX_IO) return (0); if (el->el_flags & EDIT_DISABLED) return (0); if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ex) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "tty_cookedmode: tty_setty: %s\n", strerror(errno)); #endif /* DEBUG_TTY */ return (-1); } el->el_tty.t_mode = EX_IO; return (0); } /* tty_quotemode(): * Turn on quote mode */ protected int tty_quotemode(EditLine *el) { if (el->el_tty.t_mode == QU_IO) return (0); el->el_tty.t_qu = el->el_tty.t_ed; el->el_tty.t_qu.c_iflag &= ~el->el_tty.t_t[QU_IO][MD_INP].t_clrmask; el->el_tty.t_qu.c_iflag |= el->el_tty.t_t[QU_IO][MD_INP].t_setmask; el->el_tty.t_qu.c_oflag &= ~el->el_tty.t_t[QU_IO][MD_OUT].t_clrmask; el->el_tty.t_qu.c_oflag |= el->el_tty.t_t[QU_IO][MD_OUT].t_setmask; el->el_tty.t_qu.c_cflag &= ~el->el_tty.t_t[QU_IO][MD_CTL].t_clrmask; el->el_tty.t_qu.c_cflag |= el->el_tty.t_t[QU_IO][MD_CTL].t_setmask; el->el_tty.t_qu.c_lflag &= ~el->el_tty.t_t[QU_IO][MD_LIN].t_clrmask; el->el_tty.t_qu.c_lflag |= el->el_tty.t_t[QU_IO][MD_LIN].t_setmask; if (tty_setty(el, TCSADRAIN, &el->el_tty.t_qu) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "QuoteModeOn: tty_setty: %s\n", strerror(errno)); #endif /* DEBUG_TTY */ return (-1); } el->el_tty.t_mode = QU_IO; return (0); } /* tty_noquotemode(): * Turn off quote mode */ protected int tty_noquotemode(EditLine *el) { if (el->el_tty.t_mode != QU_IO) return (0); if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ed) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "QuoteModeOff: tty_setty: %s\n", strerror(errno)); #endif /* DEBUG_TTY */ return (-1); } el->el_tty.t_mode = ED_IO; return (0); } /* tty_stty(): * Stty builtin */ protected int /*ARGSUSED*/ tty_stty(EditLine *el, int argc __attribute__((__unused__)), const char **argv) { const ttymodes_t *m; char x; int aflag = 0; const char *s, *d; const char *name; struct termios *tios = &el->el_tty.t_ex; int z = EX_IO; if (argv == NULL) return (-1); name = *argv++; while (argv && *argv && argv[0][0] == '-' && argv[0][2] == '\0') switch (argv[0][1]) { case 'a': aflag++; argv++; break; case 'd': argv++; tios = &el->el_tty.t_ed; z = ED_IO; break; case 'x': argv++; tios = &el->el_tty.t_ex; z = EX_IO; break; case 'q': argv++; tios = &el->el_tty.t_ts; z = QU_IO; break; default: (void) fprintf(el->el_errfile, "%s: Unknown switch `%c'.\n", name, argv[0][1]); return (-1); } if (!argv || !*argv) { int i = -1; size_t len = 0, st = 0, cu; for (m = ttymodes; m->m_name; m++) { if (m->m_type != i) { (void) fprintf(el->el_outfile, "%s%s", i != -1 ? "\n" : "", el->el_tty.t_t[z][m->m_type].t_name); i = m->m_type; st = len = strlen(el->el_tty.t_t[z][m->m_type].t_name); } if (i != -1) { x = (el->el_tty.t_t[z][i].t_setmask & m->m_value) ? '+' : '\0'; x = (el->el_tty.t_t[z][i].t_clrmask & m->m_value) ? '-' : x; } else { x = '\0'; } if (x != '\0' || aflag) { cu = strlen(m->m_name) + (x != '\0') + 1; if (len + cu >= (size_t)el->el_term.t_size.h) { (void) fprintf(el->el_outfile, "\n%*s", (int)st, ""); len = st + cu; } else len += cu; if (x != '\0') (void) fprintf(el->el_outfile, "%c%s ", x, m->m_name); else (void) fprintf(el->el_outfile, "%s ", m->m_name); } } (void) fprintf(el->el_outfile, "\n"); return (0); } while (argv && (s = *argv++)) { const char *p; switch (*s) { case '+': case '-': x = *s++; break; default: x = '\0'; break; } d = s; p = strchr(s, '='); for (m = ttymodes; m->m_name; m++) if ((p ? strncmp(m->m_name, d, (size_t)(p - d)) : strcmp(m->m_name, d)) == 0 && (p == NULL || m->m_type == MD_CHAR)) break; if (!m->m_name) { (void) fprintf(el->el_errfile, "%s: Invalid argument `%s'.\n", name, d); return (-1); } if (p) { int c = ffs((int)m->m_value); int v = *++p ? parse__escape((const char **) &p) : el->el_tty.t_vdisable; assert(c != 0); c--; c = tty__getcharindex(c); assert(c != -1); tios->c_cc[c] = v; continue; } switch (x) { case '+': el->el_tty.t_t[z][m->m_type].t_setmask |= m->m_value; el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value; break; case '-': el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value; el->el_tty.t_t[z][m->m_type].t_clrmask |= m->m_value; break; default: el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value; el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value; break; } } if (el->el_tty.t_mode == z) { if (tty_setty(el, TCSADRAIN, tios) == -1) { #ifdef DEBUG_TTY (void) fprintf(el->el_errfile, "tty_stty: tty_setty: %s\n", strerror(errno)); #endif /* DEBUG_TTY */ return (-1); } } return (0); } #ifdef notyet /* tty_printchar(): * DEbugging routine to print the tty characters */ private void tty_printchar(EditLine *el, unsigned char *s) { ttyperm_t *m; int i; for (i = 0; i < C_NCC; i++) { for (m = el->el_tty.t_t; m->m_name; m++) if (m->m_type == MD_CHAR && C_SH(i) == m->m_value) break; if (m->m_name) (void) fprintf(el->el_errfile, "%s ^%c ", m->m_name, s[i] + 'A' - 1); if (i % 5 == 0) (void) fprintf(el->el_errfile, "\n"); } (void) fprintf(el->el_errfile, "\n"); } #endif /* notyet */ clt/falcon/editline/src/tty.h000066400000000000000000000256031176363201700164730ustar00rootroot00000000000000/* $NetBSD: tty.h,v 1.11 2005/06/01 11:37:52 lukem Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tty.h 8.1 (Berkeley) 6/4/93 */ /* * el.tty.h: Local terminal header */ #ifndef _h_el_tty #define _h_el_tty #include "histedit.h" #include #include /* Define our own since everyone gets it wrong! */ #define CONTROL(A) ((A) & 037) /* * Aix compatible names */ # if defined(VWERSE) && !defined(VWERASE) # define VWERASE VWERSE # endif /* VWERSE && !VWERASE */ # if defined(VDISCRD) && !defined(VDISCARD) # define VDISCARD VDISCRD # endif /* VDISCRD && !VDISCARD */ # if defined(VFLUSHO) && !defined(VDISCARD) # define VDISCARD VFLUSHO # endif /* VFLUSHO && VDISCARD */ # if defined(VSTRT) && !defined(VSTART) # define VSTART VSTRT # endif /* VSTRT && ! VSTART */ # if defined(VSTAT) && !defined(VSTATUS) # define VSTATUS VSTAT # endif /* VSTAT && ! VSTATUS */ # ifndef ONLRET # define ONLRET 0 # endif /* ONLRET */ # ifndef TAB3 # ifdef OXTABS # define TAB3 OXTABS # else # define TAB3 0 # endif /* OXTABS */ # endif /* !TAB3 */ # if defined(OXTABS) && !defined(XTABS) # define XTABS OXTABS # endif /* OXTABS && !XTABS */ # ifndef ONLCR # define ONLCR 0 # endif /* ONLCR */ # ifndef IEXTEN # define IEXTEN 0 # endif /* IEXTEN */ # ifndef ECHOCTL # define ECHOCTL 0 # endif /* ECHOCTL */ # ifndef PARENB # define PARENB 0 # endif /* PARENB */ # ifndef EXTPROC # define EXTPROC 0 # endif /* EXTPROC */ # ifndef FLUSHO # define FLUSHO 0 # endif /* FLUSHO */ # if defined(VDISABLE) && !defined(_POSIX_VDISABLE) # define _POSIX_VDISABLE VDISABLE # endif /* VDISABLE && ! _POSIX_VDISABLE */ /* * Work around ISC's definition of IEXTEN which is * XCASE! */ # ifdef ISC # if defined(IEXTEN) && defined(XCASE) # if IEXTEN == XCASE # undef IEXTEN # define IEXTEN 0 # endif /* IEXTEN == XCASE */ # endif /* IEXTEN && XCASE */ # if defined(IEXTEN) && !defined(XCASE) # define XCASE IEXTEN # undef IEXTEN # define IEXTEN 0 # endif /* IEXTEN && !XCASE */ # endif /* ISC */ /* * Work around convex weirdness where turning off IEXTEN makes us * lose all postprocessing! */ #if defined(convex) || defined(__convex__) # if defined(IEXTEN) && IEXTEN != 0 # undef IEXTEN # define IEXTEN 0 # endif /* IEXTEN != 0 */ #endif /* convex || __convex__ */ /* * So that we don't lose job control. */ #ifdef __SVR4 # undef CSWTCH #endif #ifndef _POSIX_VDISABLE # define _POSIX_VDISABLE ((unsigned char) -1) #endif /* _POSIX_VDISABLE */ #if !defined(CREPRINT) && defined(CRPRNT) # define CREPRINT CRPRNT #endif /* !CREPRINT && CRPRNT */ #if !defined(CDISCARD) && defined(CFLUSH) # define CDISCARD CFLUSH #endif /* !CDISCARD && CFLUSH */ #ifndef CINTR # define CINTR CONTROL('c') #endif /* CINTR */ #ifndef CQUIT # define CQUIT 034 /* ^\ */ #endif /* CQUIT */ #ifndef CERASE # define CERASE 0177 /* ^? */ #endif /* CERASE */ #ifndef CKILL # define CKILL CONTROL('u') #endif /* CKILL */ #ifndef CEOF # define CEOF CONTROL('d') #endif /* CEOF */ #ifndef CEOL # define CEOL _POSIX_VDISABLE #endif /* CEOL */ #ifndef CEOL2 # define CEOL2 _POSIX_VDISABLE #endif /* CEOL2 */ #ifndef CSWTCH # define CSWTCH _POSIX_VDISABLE #endif /* CSWTCH */ #ifndef CDSWTCH # define CDSWTCH _POSIX_VDISABLE #endif /* CDSWTCH */ #ifndef CERASE2 # define CERASE2 _POSIX_VDISABLE #endif /* CERASE2 */ #ifndef CSTART # define CSTART CONTROL('q') #endif /* CSTART */ #ifndef CSTOP # define CSTOP CONTROL('s') #endif /* CSTOP */ #ifndef CSUSP # define CSUSP CONTROL('z') #endif /* CSUSP */ #ifndef CDSUSP # define CDSUSP CONTROL('y') #endif /* CDSUSP */ #ifdef hpux # ifndef CREPRINT # define CREPRINT _POSIX_VDISABLE # endif /* CREPRINT */ # ifndef CDISCARD # define CDISCARD _POSIX_VDISABLE # endif /* CDISCARD */ # ifndef CLNEXT # define CLNEXT _POSIX_VDISABLE # endif /* CLNEXT */ # ifndef CWERASE # define CWERASE _POSIX_VDISABLE # endif /* CWERASE */ #else /* !hpux */ # ifndef CREPRINT # define CREPRINT CONTROL('r') # endif /* CREPRINT */ # ifndef CDISCARD # define CDISCARD CONTROL('o') # endif /* CDISCARD */ # ifndef CLNEXT # define CLNEXT CONTROL('v') # endif /* CLNEXT */ # ifndef CWERASE # define CWERASE CONTROL('w') # endif /* CWERASE */ #endif /* hpux */ #ifndef CSTATUS # define CSTATUS CONTROL('t') #endif /* CSTATUS */ #ifndef CPAGE # define CPAGE ' ' #endif /* CPAGE */ #ifndef CPGOFF # define CPGOFF CONTROL('m') #endif /* CPGOFF */ #ifndef CKILL2 # define CKILL2 _POSIX_VDISABLE #endif /* CKILL2 */ #ifndef CBRK # ifndef masscomp # define CBRK 0377 # else # define CBRK '\0' # endif /* masscomp */ #endif /* CBRK */ #ifndef CMIN # define CMIN CEOF #endif /* CMIN */ #ifndef CTIME # define CTIME CEOL #endif /* CTIME */ /* * Fix for sun inconsistency. On termio VSUSP and the rest of the * ttychars > NCC are defined. So we undefine them. */ #if defined(TERMIO) || defined(POSIX) # if defined(POSIX) && defined(NCCS) # define NUMCC NCCS # else # ifdef NCC # define NUMCC NCC # endif /* NCC */ # endif /* POSIX && NCCS */ # ifdef NUMCC # ifdef VINTR # if NUMCC <= VINTR # undef VINTR # endif /* NUMCC <= VINTR */ # endif /* VINTR */ # ifdef VQUIT # if NUMCC <= VQUIT # undef VQUIT # endif /* NUMCC <= VQUIT */ # endif /* VQUIT */ # ifdef VERASE # if NUMCC <= VERASE # undef VERASE # endif /* NUMCC <= VERASE */ # endif /* VERASE */ # ifdef VKILL # if NUMCC <= VKILL # undef VKILL # endif /* NUMCC <= VKILL */ # endif /* VKILL */ # ifdef VEOF # if NUMCC <= VEOF # undef VEOF # endif /* NUMCC <= VEOF */ # endif /* VEOF */ # ifdef VEOL # if NUMCC <= VEOL # undef VEOL # endif /* NUMCC <= VEOL */ # endif /* VEOL */ # ifdef VEOL2 # if NUMCC <= VEOL2 # undef VEOL2 # endif /* NUMCC <= VEOL2 */ # endif /* VEOL2 */ # ifdef VSWTCH # if NUMCC <= VSWTCH # undef VSWTCH # endif /* NUMCC <= VSWTCH */ # endif /* VSWTCH */ # ifdef VDSWTCH # if NUMCC <= VDSWTCH # undef VDSWTCH # endif /* NUMCC <= VDSWTCH */ # endif /* VDSWTCH */ # ifdef VERASE2 # if NUMCC <= VERASE2 # undef VERASE2 # endif /* NUMCC <= VERASE2 */ # endif /* VERASE2 */ # ifdef VSTART # if NUMCC <= VSTART # undef VSTART # endif /* NUMCC <= VSTART */ # endif /* VSTART */ # ifdef VSTOP # if NUMCC <= VSTOP # undef VSTOP # endif /* NUMCC <= VSTOP */ # endif /* VSTOP */ # ifdef VWERASE # if NUMCC <= VWERASE # undef VWERASE # endif /* NUMCC <= VWERASE */ # endif /* VWERASE */ # ifdef VSUSP # if NUMCC <= VSUSP # undef VSUSP # endif /* NUMCC <= VSUSP */ # endif /* VSUSP */ # ifdef VDSUSP # if NUMCC <= VDSUSP # undef VDSUSP # endif /* NUMCC <= VDSUSP */ # endif /* VDSUSP */ # ifdef VREPRINT # if NUMCC <= VREPRINT # undef VREPRINT # endif /* NUMCC <= VREPRINT */ # endif /* VREPRINT */ # ifdef VDISCARD # if NUMCC <= VDISCARD # undef VDISCARD # endif /* NUMCC <= VDISCARD */ # endif /* VDISCARD */ # ifdef VLNEXT # if NUMCC <= VLNEXT # undef VLNEXT # endif /* NUMCC <= VLNEXT */ # endif /* VLNEXT */ # ifdef VSTATUS # if NUMCC <= VSTATUS # undef VSTATUS # endif /* NUMCC <= VSTATUS */ # endif /* VSTATUS */ # ifdef VPAGE # if NUMCC <= VPAGE # undef VPAGE # endif /* NUMCC <= VPAGE */ # endif /* VPAGE */ # ifdef VPGOFF # if NUMCC <= VPGOFF # undef VPGOFF # endif /* NUMCC <= VPGOFF */ # endif /* VPGOFF */ # ifdef VKILL2 # if NUMCC <= VKILL2 # undef VKILL2 # endif /* NUMCC <= VKILL2 */ # endif /* VKILL2 */ # ifdef VBRK # if NUMCC <= VBRK # undef VBRK # endif /* NUMCC <= VBRK */ # endif /* VBRK */ # ifdef VMIN # if NUMCC <= VMIN # undef VMIN # endif /* NUMCC <= VMIN */ # endif /* VMIN */ # ifdef VTIME # if NUMCC <= VTIME # undef VTIME # endif /* NUMCC <= VTIME */ # endif /* VTIME */ # endif /* NUMCC */ #endif /* !POSIX */ #define C_INTR 0 #define C_QUIT 1 #define C_ERASE 2 #define C_KILL 3 #define C_EOF 4 #define C_EOL 5 #define C_EOL2 6 #define C_SWTCH 7 #define C_DSWTCH 8 #define C_ERASE2 9 #define C_START 10 #define C_STOP 11 #define C_WERASE 12 #define C_SUSP 13 #define C_DSUSP 14 #define C_REPRINT 15 #define C_DISCARD 16 #define C_LNEXT 17 #define C_STATUS 18 #define C_PAGE 19 #define C_PGOFF 20 #define C_KILL2 21 #define C_BRK 22 #define C_MIN 23 #define C_TIME 24 #define C_NCC 25 #define C_SH(A) (1 << (A)) /* * Terminal dependend data structures */ #define EX_IO 0 /* while we are executing */ #define ED_IO 1 /* while we are editing */ #define TS_IO 2 /* new mode from terminal */ #define QU_IO 2 /* used only for quoted chars */ #define NN_IO 3 /* The number of entries */ #define MD_INP 0 #define MD_OUT 1 #define MD_CTL 2 #define MD_LIN 3 #define MD_CHAR 4 #define MD_NN 5 typedef struct { const char *t_name; unsigned int t_setmask; unsigned int t_clrmask; } ttyperm_t[NN_IO][MD_NN]; typedef unsigned char ttychar_t[NN_IO][C_NCC]; protected int tty_init(EditLine *); protected void tty_end(EditLine *); protected int tty_stty(EditLine *, int, const char **); protected int tty_rawmode(EditLine *); protected int tty_cookedmode(EditLine *); protected int tty_quotemode(EditLine *); protected int tty_noquotemode(EditLine *); protected void tty_bind_char(EditLine *, int); typedef struct { ttyperm_t t_t; ttychar_t t_c; struct termios t_ex, t_ed, t_ts; int t_tabs; int t_eight; speed_t t_speed; int t_mode; unsigned char t_vdisable; } el_tty_t; #endif /* _h_el_tty */ clt/falcon/editline/src/unvis.c000066400000000000000000000171161176363201700170120ustar00rootroot00000000000000/* $NetBSD: unvis.c,v 1.30 2009/02/11 13:51:59 christos Exp $ */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #if defined(LIBC_SCCS) && !defined(lint) #if 0 static char sccsid[] = "@(#)unvis.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: unvis.c,v 1.30 2009/02/11 13:51:59 christos Exp $"); #endif #endif /* LIBC_SCCS and not lint */ #include #include #include #include #include #ifdef __weak_alias __weak_alias(strunvis,_strunvis) #endif #if !HAVE_VIS /* * decode driven by state machine */ #define S_GROUND 0 /* haven't seen escape char */ #define S_START 1 /* start decoding special sequence */ #define S_META 2 /* metachar started (M) */ #define S_META1 3 /* metachar more, regular char (-) */ #define S_CTRL 4 /* control char started (^) */ #define S_OCTAL2 5 /* octal digit 2 */ #define S_OCTAL3 6 /* octal digit 3 */ #define S_HEX1 7 /* http hex digit */ #define S_HEX2 8 /* http hex digit 2 */ #define S_MIME1 9 /* mime hex digit 1 */ #define S_MIME2 10 /* mime hex digit 2 */ #define S_EATCRNL 11 /* mime eating CRNL */ #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') #define xtod(c) (isdigit(c) ? (c - '0') : ((tolower(c) - 'a') + 10)) #define XTOD(c) (isdigit(c) ? (c - '0') : ((c - 'A') + 10)) /* * unvis - decode characters previously encoded by vis */ int unvis(char *cp, int c, int *astate, int flag) { unsigned char uc = (unsigned char)c; _DIAGASSERT(cp != NULL); _DIAGASSERT(astate != NULL); if (flag & UNVIS_END) { if (*astate == S_OCTAL2 || *astate == S_OCTAL3 || *astate == S_HEX2) { *astate = S_GROUND; return UNVIS_VALID; } return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD); } switch (*astate) { case S_GROUND: *cp = 0; if (c == '\\') { *astate = S_START; return UNVIS_NOCHAR; } if ((flag & VIS_HTTPSTYLE) && c == '%') { *astate = S_HEX1; return UNVIS_NOCHAR; } if ((flag & VIS_MIMESTYLE) && c == '=') { *astate = S_MIME1; return UNVIS_NOCHAR; } *cp = c; return UNVIS_VALID; case S_START: switch(c) { case '\\': *cp = c; *astate = S_GROUND; return UNVIS_VALID; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': *cp = (c - '0'); *astate = S_OCTAL2; return UNVIS_NOCHAR; case 'M': *cp = (char)0200; *astate = S_META; return UNVIS_NOCHAR; case '^': *astate = S_CTRL; return UNVIS_NOCHAR; case 'n': *cp = '\n'; *astate = S_GROUND; return UNVIS_VALID; case 'r': *cp = '\r'; *astate = S_GROUND; return UNVIS_VALID; case 'b': *cp = '\b'; *astate = S_GROUND; return UNVIS_VALID; case 'a': *cp = '\007'; *astate = S_GROUND; return UNVIS_VALID; case 'v': *cp = '\v'; *astate = S_GROUND; return UNVIS_VALID; case 't': *cp = '\t'; *astate = S_GROUND; return UNVIS_VALID; case 'f': *cp = '\f'; *astate = S_GROUND; return UNVIS_VALID; case 's': *cp = ' '; *astate = S_GROUND; return UNVIS_VALID; case 'E': *cp = '\033'; *astate = S_GROUND; return UNVIS_VALID; case '\n': /* * hidden newline */ *astate = S_GROUND; return (UNVIS_NOCHAR); case '$': /* * hidden marker */ *astate = S_GROUND; return (UNVIS_NOCHAR); } *astate = S_GROUND; return (UNVIS_SYNBAD); case S_META: if (c == '-') *astate = S_META1; else if (c == '^') *astate = S_CTRL; else { *astate = S_GROUND; return (UNVIS_SYNBAD); } return UNVIS_NOCHAR; case S_META1: *astate = S_GROUND; *cp |= c; return UNVIS_VALID; case S_CTRL: if (c == '?') *cp |= 0177; else *cp |= c & 037; *astate = S_GROUND; return UNVIS_VALID; case S_OCTAL2: /* second possible octal digit */ if (isoctal(uc)) { /* * yes - and maybe a third */ *cp = (*cp << 3) + (c - '0'); *astate = S_OCTAL3; return UNVIS_NOCHAR; } /* * no - done with current sequence, push back passed char */ *astate = S_GROUND; return UNVIS_VALIDPUSH; case S_OCTAL3: /* third possible octal digit */ *astate = S_GROUND; if (isoctal(uc)) { *cp = (*cp << 3) + (c - '0'); return UNVIS_VALID; } /* * we were done, push back passed char */ return UNVIS_VALIDPUSH; case S_HEX1: if (isxdigit(uc)) { *cp = xtod(uc); *astate = S_HEX2; return UNVIS_NOCHAR; } /* * no - done with current sequence, push back passed char */ *astate = S_GROUND; return UNVIS_VALIDPUSH; case S_HEX2: *astate = S_GROUND; if (isxdigit(uc)) { *cp = xtod(uc) | (*cp << 4); return UNVIS_VALID; } return UNVIS_VALIDPUSH; case S_MIME1: if (uc == '\n' || uc == '\r') { *astate = S_EATCRNL; return UNVIS_NOCHAR; } if (isxdigit(uc) && (isdigit(uc) || isupper(uc))) { *cp = XTOD(uc); *astate = S_MIME2; return UNVIS_NOCHAR; } *astate = S_GROUND; return UNVIS_SYNBAD; case S_MIME2: if (isxdigit(uc) && (isdigit(uc) || isupper(uc))) { *astate = S_GROUND; *cp = XTOD(uc) | (*cp << 4); return UNVIS_VALID; } *astate = S_GROUND; return UNVIS_SYNBAD; case S_EATCRNL: switch (uc) { case '\r': case '\n': return UNVIS_NOCHAR; case '=': *astate = S_MIME1; return UNVIS_NOCHAR; default: *cp = uc; return UNVIS_VALID; } default: /* * decoder in unknown state - (probably uninitialized) */ *astate = S_GROUND; return UNVIS_SYNBAD; } } /* * strunvis - decode src into dst * * Number of chars decoded into dst is returned, -1 on error. * Dst is null terminated. */ int strunvisx(dst, src, flag) char *dst; const char *src; int flag; { char c; char *start = dst; int state = 0; _DIAGASSERT(src != NULL); _DIAGASSERT(dst != NULL); while ((c = *src++) != '\0') { again: switch (unvis(dst, c, &state, flag)) { case UNVIS_VALID: dst++; break; case UNVIS_VALIDPUSH: dst++; goto again; case 0: case UNVIS_NOCHAR: break; default: return (-1); } } if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID) dst++; *dst = '\0'; return (int)(dst - start); } int strunvis(dst, src) char *dst; const char *src; { return strunvisx(dst, src, 0); } #endif clt/falcon/editline/src/vi.c000066400000000000000000000564231176363201700162700ustar00rootroot00000000000000/* $NetBSD: vi.c,v 1.30 2009/02/21 23:31:56 christos Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Christos Zoulas of Cornell University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #include #include #include #if !defined(lint) && !defined(SCCSID) #if 0 static char sccsid[] = "@(#)vi.c 8.1 (Berkeley) 6/4/93"; #else __RCSID("$NetBSD: vi.c,v 1.30 2009/02/21 23:31:56 christos Exp $"); #endif #endif /* not lint && not SCCSID */ /* * vi.c: Vi mode commands. */ #include "el.h" private el_action_t cv_action(EditLine *, int); private el_action_t cv_paste(EditLine *, int); /* cv_action(): * Handle vi actions. */ private el_action_t cv_action(EditLine *el, int c) { if (el->el_chared.c_vcmd.action != NOP) { /* 'cc', 'dd' and (possibly) friends */ if (c != el->el_chared.c_vcmd.action) return CC_ERROR; if (!(c & YANK)) cv_undo(el); cv_yank(el, el->el_line.buffer, (int)(el->el_line.lastchar - el->el_line.buffer)); el->el_chared.c_vcmd.action = NOP; el->el_chared.c_vcmd.pos = 0; if (!(c & YANK)) { el->el_line.lastchar = el->el_line.buffer; el->el_line.cursor = el->el_line.buffer; } if (c & INSERT) el->el_map.current = el->el_map.key; return (CC_REFRESH); } el->el_chared.c_vcmd.pos = el->el_line.cursor; el->el_chared.c_vcmd.action = c; return (CC_ARGHACK); } /* cv_paste(): * Paste previous deletion before or after the cursor */ private el_action_t cv_paste(EditLine *el, int c) { c_kill_t *k = &el->el_chared.c_kill; size_t len = (size_t)(k->last - k->buf); if (k->buf == NULL || len == 0) return (CC_ERROR); #ifdef DEBUG_PASTE (void) fprintf(el->el_errfile, "Paste: \"%.*s\"\n", (int)len, k->buf); #endif cv_undo(el); if (!c && el->el_line.cursor < el->el_line.lastchar) el->el_line.cursor++; c_insert(el, (int)len); if (el->el_line.cursor + len > el->el_line.lastchar) return (CC_ERROR); (void) memcpy(el->el_line.cursor, k->buf, len); return (CC_REFRESH); } /* vi_paste_next(): * Vi paste previous deletion to the right of the cursor * [p] */ protected el_action_t /*ARGSUSED*/ vi_paste_next(EditLine *el, int c __attribute__((__unused__))) { return (cv_paste(el, 0)); } /* vi_paste_prev(): * Vi paste previous deletion to the left of the cursor * [P] */ protected el_action_t /*ARGSUSED*/ vi_paste_prev(EditLine *el, int c __attribute__((__unused__))) { return (cv_paste(el, 1)); } /* vi_prev_big_word(): * Vi move to the previous space delimited word * [B] */ protected el_action_t /*ARGSUSED*/ vi_prev_big_word(EditLine *el, int c) { if (el->el_line.cursor == el->el_line.buffer) return (CC_ERROR); el->el_line.cursor = cv_prev_word(el->el_line.cursor, el->el_line.buffer, el->el_state.argument, cv__isWord); if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return (CC_REFRESH); } return (CC_CURSOR); } /* vi_prev_word(): * Vi move to the previous word * [b] */ protected el_action_t /*ARGSUSED*/ vi_prev_word(EditLine *el, int c __attribute__((__unused__))) { if (el->el_line.cursor == el->el_line.buffer) return (CC_ERROR); el->el_line.cursor = cv_prev_word(el->el_line.cursor, el->el_line.buffer, el->el_state.argument, cv__isword); if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return (CC_REFRESH); } return (CC_CURSOR); } /* vi_next_big_word(): * Vi move to the next space delimited word * [W] */ protected el_action_t /*ARGSUSED*/ vi_next_big_word(EditLine *el, int c) { if (el->el_line.cursor >= el->el_line.lastchar - 1) return (CC_ERROR); el->el_line.cursor = cv_next_word(el, el->el_line.cursor, el->el_line.lastchar, el->el_state.argument, cv__isWord); if (el->el_map.type == MAP_VI) if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return (CC_REFRESH); } return (CC_CURSOR); } /* vi_next_word(): * Vi move to the next word * [w] */ protected el_action_t /*ARGSUSED*/ vi_next_word(EditLine *el, int c __attribute__((__unused__))) { if (el->el_line.cursor >= el->el_line.lastchar - 1) return (CC_ERROR); el->el_line.cursor = cv_next_word(el, el->el_line.cursor, el->el_line.lastchar, el->el_state.argument, cv__isword); if (el->el_map.type == MAP_VI) if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return (CC_REFRESH); } return (CC_CURSOR); } /* vi_change_case(): * Vi change case of character under the cursor and advance one character * [~] */ protected el_action_t vi_change_case(EditLine *el, int c) { int i; if (el->el_line.cursor >= el->el_line.lastchar) return (CC_ERROR); cv_undo(el); for (i = 0; i < el->el_state.argument; i++) { c = *(unsigned char *)el->el_line.cursor; if (isupper(c)) *el->el_line.cursor = tolower(c); else if (islower(c)) *el->el_line.cursor = toupper(c); if (++el->el_line.cursor >= el->el_line.lastchar) { el->el_line.cursor--; re_fastaddc(el); break; } re_fastaddc(el); } return CC_NORM; } /* vi_change_meta(): * Vi change prefix command * [c] */ protected el_action_t /*ARGSUSED*/ vi_change_meta(EditLine *el, int c __attribute__((__unused__))) { /* * Delete with insert == change: first we delete and then we leave in * insert mode. */ return (cv_action(el, DELETE | INSERT)); } /* vi_insert_at_bol(): * Vi enter insert mode at the beginning of line * [I] */ protected el_action_t /*ARGSUSED*/ vi_insert_at_bol(EditLine *el, int c __attribute__((__unused__))) { el->el_line.cursor = el->el_line.buffer; cv_undo(el); el->el_map.current = el->el_map.key; return (CC_CURSOR); } /* vi_replace_char(): * Vi replace character under the cursor with the next character typed * [r] */ protected el_action_t /*ARGSUSED*/ vi_replace_char(EditLine *el, int c __attribute__((__unused__))) { if (el->el_line.cursor >= el->el_line.lastchar) return CC_ERROR; el->el_map.current = el->el_map.key; el->el_state.inputmode = MODE_REPLACE_1; cv_undo(el); return (CC_ARGHACK); } /* vi_replace_mode(): * Vi enter replace mode * [R] */ protected el_action_t /*ARGSUSED*/ vi_replace_mode(EditLine *el, int c __attribute__((__unused__))) { el->el_map.current = el->el_map.key; el->el_state.inputmode = MODE_REPLACE; cv_undo(el); return (CC_NORM); } /* vi_substitute_char(): * Vi replace character under the cursor and enter insert mode * [s] */ protected el_action_t /*ARGSUSED*/ vi_substitute_char(EditLine *el, int c __attribute__((__unused__))) { c_delafter(el, el->el_state.argument); el->el_map.current = el->el_map.key; return (CC_REFRESH); } /* vi_substitute_line(): * Vi substitute entire line * [S] */ protected el_action_t /*ARGSUSED*/ vi_substitute_line(EditLine *el, int c __attribute__((__unused__))) { cv_undo(el); cv_yank(el, el->el_line.buffer, (int)(el->el_line.lastchar - el->el_line.buffer)); (void) em_kill_line(el, 0); el->el_map.current = el->el_map.key; return (CC_REFRESH); } /* vi_change_to_eol(): * Vi change to end of line * [C] */ protected el_action_t /*ARGSUSED*/ vi_change_to_eol(EditLine *el, int c __attribute__((__unused__))) { cv_undo(el); cv_yank(el, el->el_line.cursor, (int)(el->el_line.lastchar - el->el_line.cursor)); (void) ed_kill_line(el, 0); el->el_map.current = el->el_map.key; return (CC_REFRESH); } /* vi_insert(): * Vi enter insert mode * [i] */ protected el_action_t /*ARGSUSED*/ vi_insert(EditLine *el, int c __attribute__((__unused__))) { el->el_map.current = el->el_map.key; cv_undo(el); return (CC_NORM); } /* vi_add(): * Vi enter insert mode after the cursor * [a] */ protected el_action_t /*ARGSUSED*/ vi_add(EditLine *el, int c __attribute__((__unused__))) { int ret; el->el_map.current = el->el_map.key; if (el->el_line.cursor < el->el_line.lastchar) { el->el_line.cursor++; if (el->el_line.cursor > el->el_line.lastchar) el->el_line.cursor = el->el_line.lastchar; ret = CC_CURSOR; } else ret = CC_NORM; cv_undo(el); return (ret); } /* vi_add_at_eol(): * Vi enter insert mode at end of line * [A] */ protected el_action_t /*ARGSUSED*/ vi_add_at_eol(EditLine *el, int c __attribute__((__unused__))) { el->el_map.current = el->el_map.key; el->el_line.cursor = el->el_line.lastchar; cv_undo(el); return (CC_CURSOR); } /* vi_delete_meta(): * Vi delete prefix command * [d] */ protected el_action_t /*ARGSUSED*/ vi_delete_meta(EditLine *el, int c __attribute__((__unused__))) { return (cv_action(el, DELETE)); } /* vi_end_big_word(): * Vi move to the end of the current space delimited word * [E] */ protected el_action_t /*ARGSUSED*/ vi_end_big_word(EditLine *el, int c) { if (el->el_line.cursor == el->el_line.lastchar) return (CC_ERROR); el->el_line.cursor = cv__endword(el->el_line.cursor, el->el_line.lastchar, el->el_state.argument, cv__isWord); if (el->el_chared.c_vcmd.action != NOP) { el->el_line.cursor++; cv_delfini(el); return (CC_REFRESH); } return (CC_CURSOR); } /* vi_end_word(): * Vi move to the end of the current word * [e] */ protected el_action_t /*ARGSUSED*/ vi_end_word(EditLine *el, int c __attribute__((__unused__))) { if (el->el_line.cursor == el->el_line.lastchar) return (CC_ERROR); el->el_line.cursor = cv__endword(el->el_line.cursor, el->el_line.lastchar, el->el_state.argument, cv__isword); if (el->el_chared.c_vcmd.action != NOP) { el->el_line.cursor++; cv_delfini(el); return (CC_REFRESH); } return (CC_CURSOR); } /* vi_undo(): * Vi undo last change * [u] */ protected el_action_t /*ARGSUSED*/ vi_undo(EditLine *el, int c __attribute__((__unused__))) { c_undo_t un = el->el_chared.c_undo; if (un.len == -1) return CC_ERROR; /* switch line buffer and undo buffer */ el->el_chared.c_undo.buf = el->el_line.buffer; el->el_chared.c_undo.len = el->el_line.lastchar - el->el_line.buffer; el->el_chared.c_undo.cursor = (int)(el->el_line.cursor - el->el_line.buffer); el->el_line.limit = un.buf + (el->el_line.limit - el->el_line.buffer); el->el_line.buffer = un.buf; el->el_line.cursor = un.buf + un.cursor; el->el_line.lastchar = un.buf + un.len; return (CC_REFRESH); } /* vi_command_mode(): * Vi enter command mode (use alternative key bindings) * [] */ protected el_action_t /*ARGSUSED*/ vi_command_mode(EditLine *el, int c __attribute__((__unused__))) { /* [Esc] cancels pending action */ el->el_chared.c_vcmd.action = NOP; el->el_chared.c_vcmd.pos = 0; el->el_state.doingarg = 0; el->el_state.inputmode = MODE_INSERT; el->el_map.current = el->el_map.alt; #ifdef VI_MOVE if (el->el_line.cursor > el->el_line.buffer) el->el_line.cursor--; #endif return (CC_CURSOR); } /* vi_zero(): * Vi move to the beginning of line * [0] */ protected el_action_t vi_zero(EditLine *el, int c) { if (el->el_state.doingarg) return ed_argument_digit(el, c); el->el_line.cursor = el->el_line.buffer; if (el->el_chared.c_vcmd.action != NOP) { cv_delfini(el); return (CC_REFRESH); } return (CC_CURSOR); } /* vi_delete_prev_char(): * Vi move to previous character (backspace) * [^H] in insert mode only */ protected el_action_t /*ARGSUSED*/ vi_delete_prev_char(EditLine *el, int c __attribute__((__unused__))) { if (el->el_line.cursor <= el->el_line.buffer) return (CC_ERROR); c_delbefore1(el); el->el_line.cursor--; return (CC_REFRESH); } /* vi_list_or_eof(): * Vi list choices for completion or indicate end of file if empty line * [^D] */ protected el_action_t /*ARGSUSED*/ vi_list_or_eof(EditLine *el, int c) { if (el->el_line.cursor == el->el_line.lastchar) { if (el->el_line.cursor == el->el_line.buffer) { term_writec(el, c); /* then do a EOF */ return (CC_EOF); } else { /* * Here we could list completions, but it is an * error right now */ term_beep(el); return (CC_ERROR); } } else { #ifdef notyet re_goto_bottom(el); *el->el_line.lastchar = '\0'; /* just in case */ return (CC_LIST_CHOICES); #else /* * Just complain for now. */ term_beep(el); return (CC_ERROR); #endif } } /* vi_kill_line_prev(): * Vi cut from beginning of line to cursor * [^U] */ protected el_action_t /*ARGSUSED*/ vi_kill_line_prev(EditLine *el, int c __attribute__((__unused__))) { char *kp, *cp; cp = el->el_line.buffer; kp = el->el_chared.c_kill.buf; while (cp < el->el_line.cursor) *kp++ = *cp++; /* copy it */ el->el_chared.c_kill.last = kp; c_delbefore(el, (int)(el->el_line.cursor - el->el_line.buffer)); el->el_line.cursor = el->el_line.buffer; /* zap! */ return (CC_REFRESH); } /* vi_search_prev(): * Vi search history previous * [?] */ protected el_action_t /*ARGSUSED*/ vi_search_prev(EditLine *el, int c __attribute__((__unused__))) { return (cv_search(el, ED_SEARCH_PREV_HISTORY)); } /* vi_search_next(): * Vi search history next * [/] */ protected el_action_t /*ARGSUSED*/ vi_search_next(EditLine *el, int c __attribute__((__unused__))) { return (cv_search(el, ED_SEARCH_NEXT_HISTORY)); } /* vi_repeat_search_next(): * Vi repeat current search in the same search direction * [n] */ protected el_action_t /*ARGSUSED*/ vi_repeat_search_next(EditLine *el, int c __attribute__((__unused__))) { if (el->el_search.patlen == 0) return (CC_ERROR); else return (cv_repeat_srch(el, el->el_search.patdir)); } /* vi_repeat_search_prev(): * Vi repeat current search in the opposite search direction * [N] */ /*ARGSUSED*/ protected el_action_t vi_repeat_search_prev(EditLine *el, int c __attribute__((__unused__))) { if (el->el_search.patlen == 0) return (CC_ERROR); else return (cv_repeat_srch(el, el->el_search.patdir == ED_SEARCH_PREV_HISTORY ? ED_SEARCH_NEXT_HISTORY : ED_SEARCH_PREV_HISTORY)); } /* vi_next_char(): * Vi move to the character specified next * [f] */ protected el_action_t /*ARGSUSED*/ vi_next_char(EditLine *el, int c __attribute__((__unused__))) { return cv_csearch(el, CHAR_FWD, -1, el->el_state.argument, 0); } /* vi_prev_char(): * Vi move to the character specified previous * [F] */ protected el_action_t /*ARGSUSED*/ vi_prev_char(EditLine *el, int c __attribute__((__unused__))) { return cv_csearch(el, CHAR_BACK, -1, el->el_state.argument, 0); } /* vi_to_next_char(): * Vi move up to the character specified next * [t] */ protected el_action_t /*ARGSUSED*/ vi_to_next_char(EditLine *el, int c __attribute__((__unused__))) { return cv_csearch(el, CHAR_FWD, -1, el->el_state.argument, 1); } /* vi_to_prev_char(): * Vi move up to the character specified previous * [T] */ protected el_action_t /*ARGSUSED*/ vi_to_prev_char(EditLine *el, int c __attribute__((__unused__))) { return cv_csearch(el, CHAR_BACK, -1, el->el_state.argument, 1); } /* vi_repeat_next_char(): * Vi repeat current character search in the same search direction * [;] */ protected el_action_t /*ARGSUSED*/ vi_repeat_next_char(EditLine *el, int c __attribute__((__unused__))) { return cv_csearch(el, el->el_search.chadir, el->el_search.chacha, el->el_state.argument, el->el_search.chatflg); } /* vi_repeat_prev_char(): * Vi repeat current character search in the opposite search direction * [,] */ protected el_action_t /*ARGSUSED*/ vi_repeat_prev_char(EditLine *el, int c __attribute__((__unused__))) { el_action_t r; int dir = el->el_search.chadir; r = cv_csearch(el, -dir, el->el_search.chacha, el->el_state.argument, el->el_search.chatflg); el->el_search.chadir = dir; return r; } /* vi_match(): * Vi go to matching () {} or [] * [%] */ protected el_action_t /*ARGSUSED*/ vi_match(EditLine *el, int c) { const char match_chars[] = "()[]{}"; char *cp; size_t delta, i, count; char o_ch, c_ch; *el->el_line.lastchar = '\0'; /* just in case */ i = strcspn(el->el_line.cursor, match_chars); o_ch = el->el_line.cursor[i]; if (o_ch == 0) return CC_ERROR; delta = strchr(match_chars, o_ch) - match_chars; c_ch = match_chars[delta ^ 1]; count = 1; delta = 1 - (delta & 1) * 2; for (cp = &el->el_line.cursor[i]; count; ) { cp += delta; if (cp < el->el_line.buffer || cp >= el->el_line.lastchar) return CC_ERROR; if (*cp == o_ch) count++; else if (*cp == c_ch) count--; } el->el_line.cursor = cp; if (el->el_chared.c_vcmd.action != NOP) { /* NB posix says char under cursor should NOT be deleted for -ve delta - this is different to netbsd vi. */ if (delta > 0) el->el_line.cursor++; cv_delfini(el); return (CC_REFRESH); } return (CC_CURSOR); } /* vi_undo_line(): * Vi undo all changes to line * [U] */ protected el_action_t /*ARGSUSED*/ vi_undo_line(EditLine *el, int c) { cv_undo(el); return hist_get(el); } /* vi_to_column(): * Vi go to specified column * [|] * NB netbsd vi goes to screen column 'n', posix says nth character */ protected el_action_t /*ARGSUSED*/ vi_to_column(EditLine *el, int c) { el->el_line.cursor = el->el_line.buffer; el->el_state.argument--; return ed_next_char(el, 0); } /* vi_yank_end(): * Vi yank to end of line * [Y] */ protected el_action_t /*ARGSUSED*/ vi_yank_end(EditLine *el, int c) { cv_yank(el, el->el_line.cursor, (int)(el->el_line.lastchar - el->el_line.cursor)); return CC_REFRESH; } /* vi_yank(): * Vi yank * [y] */ protected el_action_t /*ARGSUSED*/ vi_yank(EditLine *el, int c) { return cv_action(el, YANK); } /* vi_comment_out(): * Vi comment out current command * [#] */ protected el_action_t /*ARGSUSED*/ vi_comment_out(EditLine *el, int c) { el->el_line.cursor = el->el_line.buffer; c_insert(el, 1); *el->el_line.cursor = '#'; re_refresh(el); return ed_newline(el, 0); } /* vi_alias(): * Vi include shell alias * [@] * NB: posix implies that we should enter insert mode, however * this is against historical precedent... */ #ifdef __weak_reference extern char *get_alias_text(const char *) __weak_reference(get_alias_text); #endif protected el_action_t /*ARGSUSED*/ vi_alias(EditLine *el, int c) { #ifdef __weak_reference char alias_name[3]; char *alias_text; if (get_alias_text == 0) { return CC_ERROR; } alias_name[0] = '_'; alias_name[2] = 0; if (el_getc(el, &alias_name[1]) != 1) return CC_ERROR; alias_text = get_alias_text(alias_name); if (alias_text != NULL) el_push(el, alias_text); return CC_NORM; #else return CC_ERROR; #endif } /* vi_to_history_line(): * Vi go to specified history file line. * [G] */ protected el_action_t /*ARGSUSED*/ vi_to_history_line(EditLine *el, int c) { int sv_event_no = el->el_history.eventno; el_action_t rval; if (el->el_history.eventno == 0) { (void) strncpy(el->el_history.buf, el->el_line.buffer, EL_BUFSIZ); el->el_history.last = el->el_history.buf + (el->el_line.lastchar - el->el_line.buffer); } /* Lack of a 'count' means oldest, not 1 */ if (!el->el_state.doingarg) { el->el_history.eventno = 0x7fffffff; hist_get(el); } else { /* This is brain dead, all the rest of this code counts * upwards going into the past. Here we need count in the * other direction (to match the output of fc -l). * I could change the world, but this seems to suffice. */ el->el_history.eventno = 1; if (hist_get(el) == CC_ERROR) return CC_ERROR; el->el_history.eventno = 1 + el->el_history.ev.num - el->el_state.argument; if (el->el_history.eventno < 0) { el->el_history.eventno = sv_event_no; return CC_ERROR; } } rval = hist_get(el); if (rval == CC_ERROR) el->el_history.eventno = sv_event_no; return rval; } /* vi_histedit(): * Vi edit history line with vi * [v] */ protected el_action_t /*ARGSUSED*/ vi_histedit(EditLine *el, int c) { int fd; pid_t pid; ssize_t st; int status; char tempfile[] = "/tmp/histedit.XXXXXXXXXX"; char *cp; if (el->el_state.doingarg) { if (vi_to_history_line(el, 0) == CC_ERROR) return CC_ERROR; } fd = mkstemp(tempfile); if (fd < 0) return CC_ERROR; cp = el->el_line.buffer; write(fd, cp, (size_t)(el->el_line.lastchar - cp)); write(fd, "\n", 1); pid = fork(); switch (pid) { case -1: close(fd); unlink(tempfile); return CC_ERROR; case 0: close(fd); execlp("vi", "vi", tempfile, (char *)NULL); exit(0); /*NOTREACHED*/ default: while (waitpid(pid, &status, 0) != pid) continue; lseek(fd, (off_t)0, SEEK_SET); st = read(fd, cp, (size_t)(el->el_line.limit - cp)); if (st > 0 && cp[st - 1] == '\n') st--; el->el_line.cursor = cp; el->el_line.lastchar = cp + st; break; } close(fd); unlink(tempfile); /* return CC_REFRESH; */ return ed_newline(el, 0); } /* vi_history_word(): * Vi append word from previous input line * [_] * Who knows where this one came from! * '_' in vi means 'entire current line', so 'cc' is a synonym for 'c_' */ protected el_action_t /*ARGSUSED*/ vi_history_word(EditLine *el, int c) { const char *wp = HIST_FIRST(el); const char *wep, *wsp; int len; char *cp; const char *lim; if (wp == NULL) return CC_ERROR; wep = wsp = 0; do { while (isspace((unsigned char)*wp)) wp++; if (*wp == 0) break; wsp = wp; while (*wp && !isspace((unsigned char)*wp)) wp++; wep = wp; } while ((!el->el_state.doingarg || --el->el_state.argument > 0) && *wp != 0); if (wsp == 0 || (el->el_state.doingarg && el->el_state.argument != 0)) return CC_ERROR; cv_undo(el); len = (int)(wep - wsp); if (el->el_line.cursor < el->el_line.lastchar) el->el_line.cursor++; c_insert(el, len + 1); cp = el->el_line.cursor; lim = el->el_line.limit; if (cp < lim) *cp++ = ' '; while (wsp < wep && cp < lim) *cp++ = *wsp++; el->el_line.cursor = cp; el->el_map.current = el->el_map.key; return CC_REFRESH; } /* vi_redo(): * Vi redo last non-motion command * [.] */ protected el_action_t /*ARGSUSED*/ vi_redo(EditLine *el, int c) { c_redo_t *r = &el->el_chared.c_redo; if (!el->el_state.doingarg && r->count) { el->el_state.doingarg = 1; el->el_state.argument = r->count; } el->el_chared.c_vcmd.pos = el->el_line.cursor; el->el_chared.c_vcmd.action = r->action; if (r->pos != r->buf) { if (r->pos + 1 > r->lim) /* sanity */ r->pos = r->lim - 1; r->pos[0] = 0; el_push(el, r->buf); } el->el_state.thiscmd = r->cmd; el->el_state.thisch = r->ch; return (*el->el_map.func[r->cmd])(el, r->ch); } clt/falcon/editline/src/vis.c000066400000000000000000000260611176363201700164460ustar00rootroot00000000000000/* $NetBSD: vis.c,v 1.40 2009/02/11 13:52:28 christos Exp $ */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 1999, 2005 The NetBSD Foundation, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) __RCSID("$NetBSD: vis.c,v 1.40 2009/02/11 13:52:28 christos Exp $"); #endif /* LIBC_SCCS and not lint */ #include #include #include #include #ifdef __weak_alias __weak_alias(strsvis,_strsvis) __weak_alias(strsvisx,_strsvisx) __weak_alias(strvis,_strvis) __weak_alias(strvisx,_strvisx) __weak_alias(svis,_svis) __weak_alias(vis,_vis) #endif #if !HAVE_VIS || !HAVE_SVIS #include #include #include #include static char *do_svis(char *, int, int, int, const char *); #undef BELL #define BELL '\a' #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') #define iswhite(c) (c == ' ' || c == '\t' || c == '\n') #define issafe(c) (c == '\b' || c == BELL || c == '\r') #define xtoa(c) "0123456789abcdef"[c] #define XTOA(c) "0123456789ABCDEF"[c] #define MAXEXTRAS 5 #define MAKEEXTRALIST(flag, extra, orig_str) \ do { \ const char *orig = orig_str; \ const char *o = orig; \ char *e; \ while (*o++) \ continue; \ extra = malloc((size_t)((o - orig) + MAXEXTRAS)); \ if (!extra) break; \ for (o = orig, e = extra; (*e++ = *o++) != '\0';) \ continue; \ e--; \ if (flag & VIS_SP) *e++ = ' '; \ if (flag & VIS_TAB) *e++ = '\t'; \ if (flag & VIS_NL) *e++ = '\n'; \ if ((flag & VIS_NOSLASH) == 0) *e++ = '\\'; \ *e = '\0'; \ } while (/*CONSTCOND*/0) /* * This is do_hvis, for HTTP style (RFC 1808) */ static char * do_hvis(char *dst, int c, int flag, int nextc, const char *extra) { if (!isascii(c) || !isalnum(c) || strchr("$-_.+!*'(),", c) != NULL) { *dst++ = '%'; *dst++ = xtoa(((unsigned int)c >> 4) & 0xf); *dst++ = xtoa((unsigned int)c & 0xf); } else { dst = do_svis(dst, c, flag, nextc, extra); } return dst; } /* * This is do_mvis, for Quoted-Printable MIME (RFC 2045) * NB: No handling of long lines or CRLF. */ static char * do_mvis(char *dst, int c, int flag, int nextc, const char *extra) { if ((c != '\n') && /* Space at the end of the line */ ((isspace(c) && (nextc == '\r' || nextc == '\n')) || /* Out of range */ (!isspace(c) && (c < 33 || (c > 60 && c < 62) || c > 126)) || /* Specific char to be escaped */ strchr("#$@[\\]^`{|}~", c) != NULL)) { *dst++ = '='; *dst++ = XTOA(((unsigned int)c >> 4) & 0xf); *dst++ = XTOA((unsigned int)c & 0xf); } else { dst = do_svis(dst, c, flag, nextc, extra); } return dst; } /* * This is do_vis, the central code of vis. * dst: Pointer to the destination buffer * c: Character to encode * flag: Flag word * nextc: The character following 'c' * extra: Pointer to the list of extra characters to be * backslash-protected. */ static char * do_svis(char *dst, int c, int flag, int nextc, const char *extra) { int isextra; isextra = strchr(extra, c) != NULL; if (!isextra && isascii(c) && (isgraph(c) || iswhite(c) || ((flag & VIS_SAFE) && issafe(c)))) { *dst++ = c; return dst; } if (flag & VIS_CSTYLE) { switch (c) { case '\n': *dst++ = '\\'; *dst++ = 'n'; return dst; case '\r': *dst++ = '\\'; *dst++ = 'r'; return dst; case '\b': *dst++ = '\\'; *dst++ = 'b'; return dst; case BELL: *dst++ = '\\'; *dst++ = 'a'; return dst; case '\v': *dst++ = '\\'; *dst++ = 'v'; return dst; case '\t': *dst++ = '\\'; *dst++ = 't'; return dst; case '\f': *dst++ = '\\'; *dst++ = 'f'; return dst; case ' ': *dst++ = '\\'; *dst++ = 's'; return dst; case '\0': *dst++ = '\\'; *dst++ = '0'; if (isoctal(nextc)) { *dst++ = '0'; *dst++ = '0'; } return dst; default: if (isgraph(c)) { *dst++ = '\\'; *dst++ = c; return dst; } } } if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) { *dst++ = '\\'; *dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + '0'; *dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + '0'; *dst++ = (c & 07) + '0'; } else { if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\'; if (c & 0200) { c &= 0177; *dst++ = 'M'; } if (iscntrl(c)) { *dst++ = '^'; if (c == 0177) *dst++ = '?'; else *dst++ = c + '@'; } else { *dst++ = '-'; *dst++ = c; } } return dst; } typedef char *(*visfun_t)(char *, int, int, int, const char *); /* * Return the appropriate encoding function depending on the flags given. */ static visfun_t getvisfun(int flag) { if (flag & VIS_HTTPSTYLE) return do_hvis; if (flag & VIS_MIMESTYLE) return do_mvis; return do_svis; } /* * svis - visually encode characters, also encoding the characters * pointed to by `extra' */ char * svis(char *dst, int c, int flag, int nextc, const char *extra) { char *nextra = NULL; visfun_t f; _DIAGASSERT(dst != NULL); _DIAGASSERT(extra != NULL); MAKEEXTRALIST(flag, nextra, extra); if (!nextra) { *dst = '\0'; /* can't create nextra, return "" */ return dst; } f = getvisfun(flag); dst = (*f)(dst, c, flag, nextc, nextra); free(nextra); *dst = '\0'; return dst; } /* * strsvis, strsvisx - visually encode characters from src into dst * * Extra is a pointer to a \0-terminated list of characters to * be encoded, too. These functions are useful e. g. to * encode strings in such a way so that they are not interpreted * by a shell. * * Dst must be 4 times the size of src to account for possible * expansion. The length of dst, not including the trailing NULL, * is returned. * * Strsvisx encodes exactly len bytes from src into dst. * This is useful for encoding a block of data. */ int strsvis(char *dst, const char *csrc, int flag, const char *extra) { int c; char *start; char *nextra = NULL; const unsigned char *src = (const unsigned char *)csrc; visfun_t f; _DIAGASSERT(dst != NULL); _DIAGASSERT(src != NULL); _DIAGASSERT(extra != NULL); MAKEEXTRALIST(flag, nextra, extra); if (!nextra) { *dst = '\0'; /* can't create nextra, return "" */ return 0; } f = getvisfun(flag); for (start = dst; (c = *src++) != '\0'; /* empty */) dst = (*f)(dst, c, flag, *src, nextra); free(nextra); *dst = '\0'; return (int)(dst - start); } int strsvisx(char *dst, const char *csrc, size_t len, int flag, const char *extra) { unsigned char c; char *start; char *nextra = NULL; const unsigned char *src = (const unsigned char *)csrc; visfun_t f; _DIAGASSERT(dst != NULL); _DIAGASSERT(src != NULL); _DIAGASSERT(extra != NULL); MAKEEXTRALIST(flag, nextra, extra); if (! nextra) { *dst = '\0'; /* can't create nextra, return "" */ return 0; } f = getvisfun(flag); for (start = dst; len > 0; len--) { c = *src++; dst = (*f)(dst, c, flag, len > 1 ? *src : '\0', nextra); } free(nextra); *dst = '\0'; return (int)(dst - start); } #endif #if !HAVE_VIS /* * vis - visually encode characters */ char * vis(char *dst, int c, int flag, int nextc) { char *extra = NULL; unsigned char uc = (unsigned char)c; visfun_t f; _DIAGASSERT(dst != NULL); MAKEEXTRALIST(flag, extra, ""); if (! extra) { *dst = '\0'; /* can't create extra, return "" */ return dst; } f = getvisfun(flag); dst = (*f)(dst, uc, flag, nextc, extra); free(extra); *dst = '\0'; return dst; } /* * strvis, strvisx - visually encode characters from src into dst * * Dst must be 4 times the size of src to account for possible * expansion. The length of dst, not including the trailing NULL, * is returned. * * Strvisx encodes exactly len bytes from src into dst. * This is useful for encoding a block of data. */ int strvis(char *dst, const char *src, int flag) { char *extra = NULL; int rv; MAKEEXTRALIST(flag, extra, ""); if (!extra) { *dst = '\0'; /* can't create extra, return "" */ return 0; } rv = strsvis(dst, src, flag, extra); free(extra); return rv; } int strvisx(char *dst, const char *src, size_t len, int flag) { char *extra = NULL; int rv; MAKEEXTRALIST(flag, extra, ""); if (!extra) { *dst = '\0'; /* can't create extra, return "" */ return 0; } rv = strsvisx(dst, src, len, flag, extra); free(extra); return rv; } #endif clt/falcon/editline/src/vis.h000066400000000000000000000064511176363201700164540ustar00rootroot00000000000000/* $NetBSD: vis.h,v 1.17 2009/02/10 23:06:31 christos Exp $ */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vis.h 8.1 (Berkeley) 6/2/93 */ #ifndef _VIS_H_ #define _VIS_H_ #include /* * to select alternate encoding format */ #define VIS_OCTAL 0x001 /* use octal \ddd format */ #define VIS_CSTYLE 0x002 /* use \[nrft0..] where appropiate */ /* * to alter set of characters encoded (default is to encode all * non-graphic except space, tab, and newline). */ #define VIS_SP 0x004 /* also encode space */ #define VIS_TAB 0x008 /* also encode tab */ #define VIS_NL 0x010 /* also encode newline */ #define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) #define VIS_SAFE 0x020 /* only encode "unsafe" characters */ /* * other */ #define VIS_NOSLASH 0x040 /* inhibit printing '\' */ #define VIS_HTTPSTYLE 0x080 /* http-style escape % hex hex */ #define VIS_MIMESTYLE 0x100 /* mime-style escape = HEX HEX */ /* * unvis return codes */ #define UNVIS_VALID 1 /* character valid */ #define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */ #define UNVIS_NOCHAR 3 /* valid sequence, no character produced */ #define UNVIS_SYNBAD -1 /* unrecognized escape sequence */ #define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */ /* * unvis flags */ #define UNVIS_END 1 /* no more characters */ __BEGIN_DECLS char *vis(char *, int, int, int); char *svis(char *, int, int, int, const char *); int strvis(char *, const char *, int); int strsvis(char *, const char *, int, const char *); int strvisx(char *, const char *, size_t, int); int strsvisx(char *, const char *, size_t, int, const char *); int strunvis(char *, const char *); int strunvisx(char *, const char *, int); #ifndef __LIBC12_SOURCE__ int unvis(char *, int, int *, int); #endif __END_DECLS #endif /* !_VIS_H_ */ clt/falcon/falcon.cpp000066400000000000000000000463141176363201700150660ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falcon.cpp Falcon compiler and interpreter ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 10 Sept 2004 13:15:23 +0100 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file This is the falcon command line. Consider this a relatively complex example of an embedding application. */ #include "falcon.h" #include "options.h" #include #include #include #include #include #include #include #ifndef NDEBUG #include #endif using namespace Falcon; using namespace std; /************************************************ Falcon interpreter/compiler application *************************************************/ AppFalcon::AppFalcon(): m_exitval(0), m_errors(0), m_script_pos( 0 ) { // Install a void ctrl-c handler (let ctrl-c to kill this app) //Sys::_dummy_ctrl_c_handler(); // Block all signals in the main thread. //BlockSignals(); // Prepare the Falcon engine to start. Engine::Init(); } AppFalcon::~AppFalcon() { // turn off the Falcon engine Engine::Shutdown(); } //================================================= // Utilities void AppFalcon::terminate() { if ( m_errors > 0 ) { cerr << "falcon: exiting with "; if ( m_errors > 1 ) cerr << m_errors << " errors." << endl; else cerr << "1 error." << endl; } if ( m_options.wait_after ) { cout << "Press to terminate" << endl; getchar(); } } String AppFalcon::getLoadPath() { // should we ignore system paths? if ( m_options.ignore_syspath ) { return m_options.load_path; } String envpath; String path = m_options.load_path; if( Sys::_getEnv ( "FALCON_LOAD_PATH", envpath ) ) { if ( path.size() != 0 ) path += ";"; path += envpath; } // Otherwise, get the load path. if ( path.size() > 0) return path + ";" + FALCON_DEFAULT_LOAD_PATH; else return FALCON_DEFAULT_LOAD_PATH; } String AppFalcon::getSrcEncoding() { if ( m_options.source_encoding != "" ) return m_options.source_encoding; if ( m_options.io_encoding != "" ) return m_options.io_encoding; String envenc; if ( Sys::_getEnv ( "FALCON_SRC_ENCODING", envenc ) ) return envenc; if ( Sys::_getEnv ( "FALCON_VM_ENCODING", envenc ) ) return envenc; if ( GetSystemEncoding ( envenc ) ) return envenc; // we failed. return "C"; } String AppFalcon::getIoEncoding() { // I/O encoding and source encoding priority is reversed here. if ( m_options.io_encoding != "" ) return m_options.io_encoding; if ( m_options.source_encoding != "" ) return m_options.source_encoding; String ret; if ( Sys::_getEnv ( "FALCON_VM_ENCODING", ret ) ) return ret; if( GetSystemEncoding ( ret ) ) return ret; return "C"; } void AppFalcon::applyDirectives ( Compiler &compiler ) { ListElement *dliter = m_options.directives.begin(); while ( dliter != 0 ) { String &directive = * ( ( String * ) dliter->data() ); // find "=" uint32 pos = directive.find ( "=" ); if ( pos == String::npos ) { throw String( "directive not in = syntax: \"" + directive + "\"" ); } //split the directive String dirname ( directive, 0, pos ); String dirvalue ( directive, pos + 1 ); dirname.trim(); dirvalue.trim(); // is the value a number? int64 number; bool result; if ( dirvalue.parseInt ( number ) ) result = compiler.setDirective ( dirname, number ); else result = compiler.setDirective ( dirname, dirvalue ); if ( ! result ) { throw String( "invalid directive or value: \"" + directive + "\"" ); } dliter = dliter->next(); } } void AppFalcon::applyConstants ( Compiler &compiler ) { ListElement *dliter = m_options.defines.begin(); while ( dliter != 0 ) { String &directive = * ( ( String * ) dliter->data() ); // find "=" uint32 pos = directive.find ( "=" ); if ( pos == String::npos ) { throw String( "constant not in = syntax: \"" + directive + "\"" ); } //split the directive String dirname ( directive, 0, pos ); String dirvalue ( directive, pos + 1 ); dirname.trim(); dirvalue.trim(); // is the value a number? int64 number; if ( dirvalue.parseInt ( number ) ) compiler.addIntConstant( dirname, number ); else { compiler.addStringConstant( dirname, dirvalue ); } dliter = dliter->next(); } } bool AppFalcon::setup( int argc, char* argv[] ) { m_argc = argc; m_argv = argv; m_script_pos = argc; m_options.parse( argc, argv, m_script_pos ); //======================================= // Check parameters && settings if ( ! m_options.wasJustInfo() ) { String srcEncoding = getSrcEncoding(); if ( srcEncoding != "" ) { Transcoder *tcin = TranscoderFactory ( srcEncoding, 0, false ); if ( tcin == 0 ) throw String( "unrecognized encoding '" + srcEncoding + "'." ); delete tcin; } String ioEncoding = getIoEncoding(); if ( ioEncoding != "" ) { Transcoder *tcin = TranscoderFactory ( ioEncoding, 0, false ); if ( tcin == 0 ) throw String( "unrecognized encoding '" + ioEncoding + "'." ); delete tcin; } Engine::setEncodings( srcEncoding, ioEncoding ); #ifndef NDEBUG if ( m_options.trace_file != "" ) { AutoCString trace_file(m_options.trace_file); TRACE_ON( trace_file.c_str() ); } #endif } return ! m_options.wasJustInfo(); } void AppFalcon::readyStreams() { // Function wide statics must be created here, as we may be making memory accounting later on. String ioEncoding = getIoEncoding(); // change stdandard streams to fit needs if ( ioEncoding != "" && ioEncoding != "C" ) { Transcoder* tcin = TranscoderFactory ( ioEncoding, new StreamBuffer( new StdInStream ), true ); m_stdIn = AddSystemEOL ( tcin ); Transcoder *tcout = TranscoderFactory ( ioEncoding, new StreamBuffer( new StdOutStream ), true ); m_stdOut = AddSystemEOL ( tcout ); Transcoder *tcerr = TranscoderFactory ( ioEncoding, new StreamBuffer( new StdErrStream ), true ); m_stdErr = AddSystemEOL ( tcerr ); } else { m_stdIn = AddSystemEOL ( new StreamBuffer( new StdInStream ) ); m_stdOut = AddSystemEOL ( new StreamBuffer( new StdOutStream ) ); m_stdErr = AddSystemEOL ( new StreamBuffer( new StdErrStream ) ); } } Stream* AppFalcon::openOutputStream( const String &ext ) { Stream* out; if ( m_options.output == "-" ) { out = new StdOutStream; } else if ( m_options.output == "" ) { // if input has a name, try to open there. if ( m_options.input != "" && m_options.input != "-" ) { #ifdef WIN32 Path::winToUri( m_options.input ); #endif URI uri_input( m_options.input ); uri_input.pathElement().setFilename( uri_input.pathElement().getFile() + "." + ext ); FileStream* fout = new FileStream; if ( ! fout->create( uri_input.get(), BaseFileStream::e_aUserRead | BaseFileStream::e_aUserWrite ) ) { delete fout; throw String( "can't open output file '"+ uri_input.get() +"'" ); } out = fout; } else { // no input and no output; output on stdout out = new StdOutStream; } } else { FileStream* fout = new FileStream; if ( ! fout->create( m_options.output, BaseFileStream::e_aUserRead | BaseFileStream::e_aUserWrite ) ) { delete fout; throw String( "can't open output file '"+ m_options.output +"'" ); } out = fout; } return out; } Module* AppFalcon::loadInput( ModuleLoader &ml ) { ml.sourceEncoding( getSrcEncoding() ); Module* mod; if ( m_options.input != "" && m_options.input != "-" ) { #ifdef WIN32 Path::winToUri( m_options.input ); #endif mod = ml.loadFile( m_options.input, m_options.run_only ? ModuleLoader::t_vmmod : ModuleLoader::t_defaultSource, false ); } else { String ioEncoding = getSrcEncoding(); Transcoder *tcin = TranscoderFactory ( ioEncoding == "" ? "C" : ioEncoding, new StreamBuffer( new StdInStream), true ); mod = ml.loadSource( AddSystemEOL( tcin ), "", "stdin" ); delete tcin; } return mod; } void AppFalcon::compileTLTable() { ModuleLoader ml; // the load path is not relevant, as we load by file name or stream // apply options ml.compileTemplate( m_options.parse_ftd ); ml.saveModules( false ); ml.alwaysRecomp( true ); // will throw Error* on failure Module* mod = loadInput( ml ); // try to open the oputput stream. Stream* out = 0; try { String ioEncoding = getIoEncoding(); out = AddSystemEOL( TranscoderFactory ( ioEncoding == "" ? "C" : ioEncoding, openOutputStream ( "temp.ftt" ), true ) ); // Ok, we have the output stream. if ( ! mod->saveTableTemplate( out, ioEncoding == "" ? "C" : ioEncoding ) ) throw String( "can't write on output stream." ); } catch( ... ) { delete out; mod->decref(); throw; } delete out; mod->decref(); } void AppFalcon::generateAssembly() { ModuleLoader ml; // the load path is not relevant, as we load by file name or stream // apply options ml.compileTemplate( m_options.parse_ftd ); ml.saveModules( false ); ml.alwaysRecomp( true ); // will throw Error* on failure Module* mod = loadInput( ml ); // try to open the oputput stream. Stream* out = 0; try { String ioEncoding = getIoEncoding(); out = AddSystemEOL( TranscoderFactory ( ioEncoding == "" ? "C" : ioEncoding, openOutputStream( "fas" ), true ) ); // Ok, we have the output stream. GenHAsm gasm(out); gasm.generatePrologue( mod ); gasm.generate( ml.compiler().sourceTree() ); if ( ! out->good() ) throw String( "can't write on output stream." ); } catch( const String & ) { delete out; mod->decref(); throw; } delete out; mod->decref(); } void AppFalcon::generateTree() { Compiler comp; // the load path is not relevant, as we load by file name or stream // apply options Stream* in = 0; // will throw Error* on failure if ( m_options.input != "" && m_options.input != "-" ) { #ifdef WIN32 Path::winToUri( m_options.input ); #endif FileStream* fs = new FileStream(); fs->open( m_options.input ); in = fs; } else { String ioEncoding = getSrcEncoding(); Transcoder *tcin = TranscoderFactory ( ioEncoding == "" ? "C" : ioEncoding, new StreamBuffer( new StdInStream), true ); in = tcin; } // try to open the oputput stream. Stream* out = 0; Module *mod = new Module; try { comp.compile( mod, in ); String ioEncoding = getIoEncoding(); out = AddSystemEOL( TranscoderFactory ( ioEncoding == "" ? "C" : ioEncoding, openOutputStream ( "fr" ), true ) ); // Ok, we have the output stream. GenTree gtree(out); gtree.generate( comp.sourceTree() ); if ( ! out->good() ) throw String( "can't write on output stream." ); } catch( const String & ) { delete in; delete out; mod->decref(); throw; } delete in; delete out; mod->decref(); } void AppFalcon::buildModule() { ModuleLoader ml; // the load path is not relevant, as we load by file name or stream // apply options ml.compileTemplate( m_options.parse_ftd ); ml.saveModules( false ); ml.alwaysRecomp( true ); // will throw Error* on failure Module* mod = loadInput( ml ); // try to open the oputput stream. Stream* out = 0; try { // binary out = openOutputStream ( "fam" ); // Ok, we have the output stream. GenCode gcode(mod); gcode.generate( ml.compiler().sourceTree() ); if ( ! mod->save( out ) ) throw String( "can't write on output stream." ); } catch( const String & ) { delete out; mod->decref(); throw; } delete out; mod->decref(); } void AppFalcon::makeInteractive() { m_options.version(); readyStreams(); IntMode ic( this ); ic.run(); } void AppFalcon::prepareLoader( ModuleLoader &ml ) { // 1. Ready the module loader ModuleLoader *modLoader = &ml; ml.setSearchPath( getLoadPath() ); // adds also the input path. if ( m_options.input != "" && m_options.input != "-" ) { URI in_uri( m_options.input ); in_uri.pathElement().setFilename(""); // empty path? -- add current directory (may be removed from defaults) if ( in_uri.get() == "" ) modLoader->addSearchPath ( "." ); else modLoader->addSearchPath ( in_uri.get() ); } // set the module preferred language; ok also if default ("") is used modLoader->setLanguage ( m_options.module_language ); applyDirectives( modLoader->compiler() ); applyConstants( modLoader->compiler() ); // save the main module also if compile only option is set modLoader->saveModules ( m_options.save_modules ); modLoader->compileInMemory ( m_options.comp_memory ); modLoader->alwaysRecomp ( m_options.force_recomp ); modLoader->sourceEncoding ( getSrcEncoding() ); // normally, save is not mandatory, unless we compile them our own // should be the default, but we reset it. modLoader->saveMandatory ( false ); // should we forcefully consider input as ftd? modLoader->compileTemplate ( m_options.parse_ftd ); Engine::setSearchPath( modLoader->getSearchPath() ); } void AppFalcon::runModule() { ModuleLoader ml; prepareLoader( ml ); // Create the runtime using the given module loader. Runtime runtime( &ml ); // now that we have the main module, inject other requested modules ListElement *pliter = m_options.preloaded.begin(); while ( pliter != 0 ) { Module *module = ml.loadName ( * ( ( String * ) pliter->data() ) ); runtime.addModule( module ); // abandon our reference to the injected module module->decref(); pliter = pliter->next(); } // then add the main module Module* mainMod = loadInput(ml); runtime.addModule( mainMod ); // abandon our reference to the main module mainMod->decref(); //=========================================== // Prepare the virtual machine // VMachine* vm = new VMachine( false ); VMachineWrapper vmachine(vm); //redirect the VM streams to ours. // The machine takes ownership of the streams, so they won't be useable anymore // after the machine destruction. readyStreams(); vmachine->stdIn( m_stdIn ); vmachine->stdOut( m_stdOut ); vmachine->stdErr( m_stdErr ); vmachine->init(); // I have given real process streams to the vm vmachine->hasProcessStreams( true ); // push the core module // we know we're not launching the core module. vmachine->launchAtLink( false ); Module* core = core_module_init(); #ifdef NDEBUG vmachine->link ( core ); #else LiveModule *res = vmachine->link ( core ); fassert ( res != 0 ); // should not fail #endif core->decref(); // prepare environment Item *item_args = vmachine->findGlobalItem ( "args" ); fassert ( item_args != 0 ); CoreArray *args = new CoreArray ( m_argc - m_script_pos ); String ioEncoding = getIoEncoding(); for ( int ap = m_script_pos; ap < m_argc; ap ++ ) { CoreString *cs = new CoreString; if ( ! TranscodeFromString ( m_argv[ap], ioEncoding, *cs ) ) { cs->bufferize ( m_argv[ap] ); } args->append ( cs ); } item_args->setArray ( args ); Item *script_name = vmachine->findGlobalItem ( "scriptName" ); fassert ( script_name != 0 ); *script_name = new CoreString ( mainMod->name() ); Item *script_path = vmachine->findGlobalItem ( "scriptPath" ); fassert ( script_path != 0 ); *script_path = new CoreString ( mainMod->path() ); // Link the runtime in the VM. // We'll be running the modules as we link them in. vmachine->launchAtLink( true ); if ( vmachine->link( &runtime ) ) { // Broadcast OS signals in this VM. //vmachine->becomeSignalTarget(); vmachine->launch(); if ( vmachine->regA().isInteger() ) exitval( ( int32 ) vmachine->regA().asInteger() ); } } void AppFalcon::run() { // record the memory now -- we're gonna create the streams that will be handed to the VM size_t memory = gcMemAllocated(); int32 items = memPool->allocatedItems(); // determine the operation mode if( m_options.compile_tltable ) compileTLTable(); else if ( m_options.assemble_out ) generateAssembly(); else if ( m_options.tree_out ) generateTree(); else if ( m_options.compile_only ) buildModule(); else if ( m_options.interactive ) makeInteractive(); else runModule(); memPool->performGC(); if ( m_options.check_memory ) { // be sure we have reclaimed all what's possible to reclaim. size_t mem2 = gcMemAllocated(); int32 items2 = memPool->allocatedItems(); cout << "===============================================================" << std::endl; cout << " M E M O R Y R E P O R T" << std::endl; cout << "===============================================================" << std::endl; cout << " Unbalanced memory: " << mem2 - memory << endl; cout << " Unbalanced items : " << items2 - items << endl; cout << "===============================================================" << std::endl; } } //=========================================== // Main Routine //=========================================== int main ( int argc, char *argv[] ) { AppFalcon falcon; StdErrStream serr; StdOutStream sout; try { if ( falcon.setup( argc, argv ) ) falcon.run(); } catch ( const String &fatal_error ) { serr.writeString( "falcon: FATAL - " + fatal_error + "\n" ); falcon.exitval( 1 ); } catch ( Error *err ) { String temp; err->toString( temp ); if( falcon.m_options.errOnStdout ) { sout.writeString( "falcon: FATAL - Program terminated with error.\n" ); sout.writeString( temp + "\n" ); } else { serr.writeString( "falcon: FATAL - Program terminated with error.\n" ); serr.writeString( temp + "\n" ); } err->decref(); falcon.exitval( 1 ); } falcon.terminate(); return falcon.exitval(); } /* end of falcon.cpp */ clt/falcon/falcon.h000066400000000000000000000041301176363201700145210ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falcon.h Falcon compiler and interpreter ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 23 Mar 2009 18:57:37 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Main Falcon. */ #ifndef FALCON_CLT_H #define FALCON_CLT_H #include #include "options.h" #include "int_mode.h" namespace Falcon { /** Typical embedding applications. */ class AppFalcon { int m_exitval; int m_errors; int m_script_pos; int m_argc; char** m_argv; String getSrcEncoding(); String getLoadPath(); String getIoEncoding(); Module* loadInput( ModuleLoader &ml ); void applyDirectives ( Compiler &compiler ); void applyConstants ( Compiler &compiler ); void readyStreams(); Stream* openOutputStream( const String &ext ); public: /** Prepares the application. Sets up global values and starts the falcon engine. */ AppFalcon(); /** Shuts down the Falcon application. This destroys the global data and shuts down the engine. */ ~AppFalcon(); /** Checks the parameters and determines if the run step must be performed. If the program should stop after the setup (for any non-critical error), the function sreturns false. In case of critical errors, it raises a falcon String, */ bool setup( int argc, char* argv[] ); /** Perform the operations required by the options. */ void run(); void compileTLTable(); void generateAssembly(); void generateTree(); void buildModule(); void runModule(); void makeInteractive(); void prepareLoader( ModuleLoader &ml ); void terminate(); int exitval() const { return m_exitval; } void exitval( int exitVal ) { m_exitval = exitVal; } Stream* m_stdIn; Stream* m_stdOut; Stream* m_stdErr; FalconOptions m_options; }; } // namespace Falcon #endif /* end of falcon.h */ clt/falcon/falcon.rc000066400000000000000000000026201176363201700147000ustar00rootroot00000000000000///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. MAINICON ICON "resources\\falcon-ico-base.ico" ///////////////////////////////////////////////////////////////////////////// // // Version // // for project wide versioning... #include #include // and application versioning #define VS_VERSION_INFO 1 VS_VERSION_INFO VERSIONINFO FILEVERSION FALCON_VERSION_RCINFO_N PRODUCTVERSION FALCON_VERSION_RCINFO_N FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x4L FILETYPE 0x0L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "041004b0" BEGIN VALUE "CompanyName", "Falcon Committee" VALUE "FileDescription", "The Falcon command line compiler" VALUE "FileVersion", FALCON_VERSION_RCINFO VALUE "InternalName", "falcon" VALUE "LegalCopyright", "The Falcon Programming Language License" VALUE "OriginalFilename", "falcon.exe" VALUE "ProductName", "The Falcon Programming Language" VALUE "ProductVersion", FALCON_VERSION_RCINFO END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x410, 1200 END END clt/falcon/int_mode.cpp000066400000000000000000000114661176363201700154220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: int_mode.cpp Falcon compiler and interpreter - interactive mode ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 23 Mar 2009 18:57:37 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "int_mode.h" #include #include #include using namespace Falcon; IntMode::IntMode( AppFalcon* owner ): m_owner( owner ) {} void IntMode::run() { ModuleLoader ml; m_owner->prepareLoader( ml ); VMachineWrapper intcomp_vm; Module* core_module = core_module_init(); intcomp_vm->link( core_module ); core_module->decref(); Item* describe = intcomp_vm->findGlobalItem("describe"); fassert( describe != 0 ); GarbageLock gl(*describe); InteractiveCompiler comp( &ml, intcomp_vm.vm() ); comp.setInteractive( true ); Stream *stdOut = m_owner->m_stdOut; Stream *stdIn = m_owner->m_stdIn; stdOut->writeString("\n===NOTICE===\n" ); stdOut->writeString("Interactive mode is currently UNDER DEVELOPMENT.\n" ); #ifdef FALCON_WITH_GPL_READLINE stdOut->writeString("Built with GPLed GNU-Readline. Refer to the README for alternatives.\n" ); #endif stdOut->writeString("\nWelcome to Falcon interactive mode.\n" ); stdOut->writeString("Write statements directly at the prompt; when finished press " ); #ifdef FALCON_SYSTEM_WIN stdOut->writeString("CTRL+Z" ); #else stdOut->writeString("CTRL+D" ); #endif stdOut->writeString(" to exit\n" ); stdOut->flush(); InteractiveCompiler::t_ret_type lastRet = InteractiveCompiler::e_nothing; String line, pline, codeSlice; int linenum = 1; while( stdIn->good() && ! stdIn->eof() ) { const char *prompt = ( lastRet == InteractiveCompiler::e_more ||lastRet == InteractiveCompiler::e_incomplete ) ? "... " : ">>> "; read_line(pline, prompt); if ( pline.size() > 0 ) { if( pline.getCharAt( pline.length() -1 ) == '\\' ) { lastRet = InteractiveCompiler::e_more; pline.setCharAt( pline.length() - 1, ' '); line += pline; continue; } else line += pline; InteractiveCompiler::t_ret_type lastRet1 = InteractiveCompiler::e_nothing; try { comp.lexer()->line( linenum ); lastRet1 = comp.compileNext( codeSlice + line + "\n" ); } catch( Error *err ) { String temp = err->toString(); err->decref(); stdOut->writeString( temp ); stdOut->flush(); // in case of error detected at context end, close it. line.trim(); if( line == "end" || line.endsWith( "]" ) || line.endsWith( "}" ) || line.endsWith( ")" ) ) { codeSlice.size( 0 ); lastRet = InteractiveCompiler::e_nothing; linenum = 1; } line.size(0); continue; } switch( lastRet1 ) { case InteractiveCompiler::e_more: codeSlice += line + "\n"; break; case InteractiveCompiler::e_incomplete: // is it incomplete because of '\\' at end? if ( line.getCharAt( line.length()-1 ) == '\\' ) line.setCharAt( line.length()-1, ' ' ); codeSlice += line + "\n"; break; case InteractiveCompiler::e_terminated: stdOut->writeString( "falcon: Terminated\n\n"); stdOut->flush(); return; case InteractiveCompiler::e_call: if ( comp.vm()->regA().isNil() ) { codeSlice.size(0); break; } // fallthrough case InteractiveCompiler::e_expression: { comp.vm()->pushParameter( comp.vm()->regA() ); comp.vm()->callItem( *describe, 1 ); stdOut->writeString( ": " + *comp.vm()->regA().asString() + "\n" ); stdOut->flush(); } // fallthrough default: codeSlice.size(0); linenum = 0; } // maintain previous status if having a compilation error. lastRet = lastRet1; line.size( 0 ); linenum++; } // else just continue. } stdOut->writeString( "\r \n\n"); stdOut->flush(); } /* end of int_mode.cpp */ clt/falcon/int_mode.h000066400000000000000000000015011176363201700150540ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: int_mode.h Falcon compiler and interpreter - interactive mode. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 23 Mar 2009 18:57:37 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Options storage for falcon compiler. */ #ifndef FALCON_INT_MODE_H #define FALCON_INT_MODE_H #include "falcon.h" namespace Falcon { class AppFalcon; class IntMode { void read_line(String &line, const char* prompt); AppFalcon *m_owner; public: IntMode( AppFalcon* owner ); void run(); }; } // namespace Falcon #endif /* end of int_mode.h */ clt/falcon/options.cpp000066400000000000000000000206651176363201700153200ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: options.cpp Falcon compiler and interpreter - options file ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 10 Sept 2004 13:15:23 +0100 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Falcon compiler and interpreter - options file */ #include "options.h" #include using namespace std; using namespace Falcon; FalconOptions::FalconOptions(): m_modal( false ), m_justinfo( false ), input( "" ), output( "" ), load_path( "" ), io_encoding( "" ), source_encoding( "" ), module_language( "" ), compile_only( false ), run_only( false ), tree_out( false ), assemble_out( false ), search_path( false ), force_recomp( false ), check_memory( false ), comp_memory( true ), recompile_on_load( true ), save_modules( true ), wait_after( false ), parse_ftd( false ), compile_tltable( false ), interactive( false ), ignore_syspath( false ), errOnStdout(false) {} void FalconOptions::usage( bool deep ) { cout << "Usage:\n" << endl << " falcon (-c|-S|-t) [c_opts] [-o] module" << endl << " falcon -y [-o] module" << endl << " falcon -x [c_options] module" << endl << " falcon [c_opts] [r_opts] module [script options]" << endl << " falcon -i [-p module] ...[-p module]" << endl << endl; if( deep ) { cout << "Modal options:" << endl << " -c compile the given source in a .fam module" << endl << " -i interactive mode" << endl << " -S produce an assembly output" << endl << " -t generate a syntactic tree (for logic debug)" << endl << " -y write string translation table for the module" << endl << " -x execute a binary '.fam' module" << endl << " --cgi execute in GGI mode" << endl << endl << "Compilation options (c_opts):" << endl << " -d Set directive (as =)" << endl << " -D Set constant (as =)" << endl << " -E Source files are in encoding (overrides -e)" << endl << " -f force recompilation of modules even when .fam are found" << endl << " -m do NOT compile in memory (use temporary files)" << endl << " -T consider given [module] as .ftd (template document)" << endl << endl << "Run options (r_opts):" << endl << " -C check for memory allocation correctness" << endl << " -e set given encoding as default for VM I/O" << endl #ifndef NDEBUG << " -F Output TRACE debug statements to " << endl #endif << " -l Set preferential language of loaded modules" << endl << " -L Add path for 'load' directive (start with ';' remove std paths)" << endl << " -M do NOT save the compiled modules in '.fam' files" << endl << " -p preload (pump in) given module" << endl << " -P ignore system PATH (and FALCON_LOAD_PATH envvar)" << endl << " -r do NOT recompile sources (ignore sources)" << endl << endl << "General options:" << endl << " -h/-? display usage" << endl << " -H this help" << endl << " -o output to instead of [module.xxx]" << endl << " -s Send error description to stdOut instead of stdErr" << endl << " -v print copyright notice and version and exit" << endl << " -w add an extra console wait after program exit" << endl << "" << endl << "Paths must be in falcon file name format: directory separators must be slashes [/] and" << endl << "multiple entries must be entered separed by a semicolon (';')" << endl << "File names may be set to '-' meaning standard input or output (depending on the option)" << endl << endl; } else { cout << "Use '-H' option to get more help." << endl; } } void FalconOptions::modalGiven() { if (m_modal) { throw String("multiple modal options selected"); } m_modal = true; } void FalconOptions::parse( int argc, char **argv, int &script_pos ) { // option decoding for ( int i = 1; i < argc; i++ ) { char *op = argv[i]; if ( op[0] == '-' ) { switch ( op[1] ) { case 'c': modalGiven(); compile_only = true; break; case 'C': check_memory = true; break; case 'd': if ( op[2] == 0 && i + 1< argc ) directives.pushBack ( new String ( argv[++i] ) ); else directives.pushBack ( new String ( op + 2 ) ); break; case 'D': if ( op[2] == 0 && i + 1< argc ) defines.pushBack ( new String ( argv[++i] ) ); else defines.pushBack ( new String ( op + 2 ) ); break; case 'e': if ( op[2] == 0 && i + 1 < argc ) { io_encoding = argv[++i]; } else { io_encoding = op + 2; } break; case 'E': if ( op[2] == 0 && i + 1< argc ) { source_encoding = argv[++i]; } else { source_encoding = op + 2; } break; case 'f': force_recomp = true; break; #ifndef NDEBUG case 'F': if ( op[2] == 0 && i + 1 < argc ) trace_file = argv[++i]; else trace_file = op + 2; break; #endif case 'h': case '?': usage(false); m_justinfo = true; break; case 'H': usage(true); m_justinfo = true; break; case 'i': modalGiven(); interactive = true; break; case 'L': if ( op[2] == 0 && i + 1 < argc ) load_path = argv[++i]; else load_path = op + 2; break; case 'l': if ( op[2] == 0 && i + 1 < argc ) module_language = argv[++i]; else module_language = op + 2; break; case 'm': comp_memory = false; break; case 'M': save_modules = false; break; case 'o': if ( op[2] == 0 && i + 1< argc ) output = argv[++i]; else output = op + 2; break; case 'p': if ( op[2] == 0 && i + 1< argc ) preloaded.pushBack ( new String ( argv[++i] ) ); else preloaded.pushBack ( new String ( op + 2 ) ); break; case 'P': ignore_syspath = true; break; case 'r': recompile_on_load = false; break; case 's': errOnStdout = true; break; case 'S': modalGiven(); assemble_out = true; break; case 't': modalGiven(); tree_out = true; break; case 'T': parse_ftd = true; break; case 'x': run_only = true; break; case 'v': version(); m_justinfo = true; break; case 'w': wait_after = true; break; case 'y': modalGiven(); compile_tltable = true; break; case '-': if( String( op+2 ) == "cgi" ) { errOnStdout = true; preloaded.pushBack( new String( "cgi" ) ); break; } // else just fallthrough default: cerr << "falcon: unrecognized option '" << op << "'."<< endl << endl; usage(false); m_justinfo = true; } } else { input = op; script_pos = i+1; // the other m_options are for the script. break; } } } void FalconOptions::version() { cout << "Falcon compiler and interpreter." << endl; cout << "Version " << FALCON_VERSION " (" FALCON_VERSION_NAME ")" << endl << endl; } /* options.cpp */ clt/falcon/options.h000066400000000000000000000033041176363201700147540ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: options.h Options storage for falcon compiler. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven set 10 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Options storage for falcon compiler. */ #ifndef flc_options_H #define flc_options_H #include #include namespace Falcon { /** Options storage for falcon compiler This class is just a nice place to store options for the compiler and their defaults. */ class FalconOptions { void modalGiven(); bool m_modal; bool m_justinfo; public: String input; String output; String load_path; String io_encoding; String source_encoding; String module_language; #ifndef NDEBUG String trace_file; #endif List preloaded; List directives; List defines; bool compile_only; bool run_only; bool tree_out; bool assemble_out; bool search_path; bool force_recomp; bool check_memory; bool comp_memory; bool recompile_on_load; bool save_modules; bool wait_after; bool parse_ftd; bool compile_tltable; bool interactive; bool ignore_syspath; bool errOnStdout; FalconOptions(); void parse( int argc, char **argv, int &script_pos ); void usage( bool deep = false ); void version(); /** Returns true if the parsed options required an immediate exit. */ bool wasJustInfo() const { return m_justinfo; } }; } // namespace Falcon #endif /* end of options.h */ clt/falcon/readline_editline.cpp000066400000000000000000000023331176363201700172550ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: readline_editline.cpp Falcon compiler and interpreter - interactive mode ------------------------------------------------------------------- Author: Maik Beckmann Begin: Mon, 30 Oct 2009 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "int_mode.h" #include // FILE #ifdef FALCON_WITH_GPL_READLINE # include # include #else # include #endif using namespace Falcon; namespace { // anonymous Stream* s_in = 0; extern "C" int s_getc(FILE*) { uint32 chr; s_in->get(chr); return chr; } } // anonymous void IntMode::read_line(String &line, const char* prompt) { // if(!s_in) // { // s_in = m_owner->m_stdIn; // rl_getc_function = s_getc; // } line.reserve( 1024 ); line.size(0); if( char* buf = readline(prompt) ) { if (buf[0] != 0) { line += String(buf); add_history(buf); } free(buf); } else // EOF (CTRL-D) m_owner->m_stdIn->status(Stream::t_eof); } clt/falcon/readline_simple.cpp000066400000000000000000000016051176363201700167520ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: readline_simple.cpp Falcon compiler and interpreter - interactive mode ------------------------------------------------------------------- Author: Maik Beckmann Begin: Mon, 30 Oct 2009 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "int_mode.h" using namespace Falcon; void IntMode::read_line(String &line, const char* prompt) { m_owner->m_stdOut->writeString(prompt); m_owner->m_stdOut->flush(); uint32 maxSize = 1024; line.reserve( maxSize ); line.size(0); uint32 chr; while ( line.length() < maxSize && m_owner->m_stdIn->get( chr ) ) { if ( chr == '\r' ) continue; if ( chr == '\n' ) break; line += chr; } } clt/falcon/resources/000077500000000000000000000000001176363201700151225ustar00rootroot00000000000000clt/falcon/resources/falcon-ico-base.ico000066400000000000000000000226761176363201700205550ustar00rootroot0000000000000000 %(0` -,N7_;hZ;jދx?|=m=m=m>lZ:c1U0)H8b!;izn?qAtCvEvGxyEr 6W+K9e/p>p>p?p@pAqEvK~PTR/>a$H8co?q?r@sBuExHzI{K}T]_Y.M.Q:hNpx=p=p=p=p>q>q=m=ms=p>q>q>q>q>q>r?r@s@r@r@pM=j /Fv%QX]]Zc`Q}7b?{ۨ>qq>q>q>q>q>q=o=k;>pV?r@sAtBvDxEwEu^@l-MOFW\]V[UKw6`q>q>q>q>r>r>r>r=oq>q>q>r>r>r>r?s?s?s?s?s?s?r?q>nK:e@oExqG{I}J~I{t@i EtQSQL}NIy6Z/b;kU=o;iڰ=o=q=q>q>q>r>r>r>r?s?s?s?t@t=n>qd?s@t@tAuAuAt@qf=hDt!H{I}J~I{AmBo MMK~HyHzBqU/_9f!=nq>r>r>r>r?s?s?t?t@t@u?r=mQ>n/:d.P?q(AteBvBwBwCvArIq>r>r>r>r>s?s?t?t@u@uAvAvBwBvBvBvAt@rZ=j;f BuHCwCxCwCu=iFx;G{FzDv=hDt FxDvArCt?n/R:j;=pq>r>r>r>r>s>s?t?t@u@uAvAwBwAuAuBvCxDxDyDyDxBu@n>l DxfDyCxBu>kEv2EyDwAs7` CuJBuAs@p?q;g:.Sr>r>r>s>s>s?t?t@u@vAvBwBxCy?nw9b0\:hBu'Cw`EzF{EzEyDu^:aCv2DxCwAt=j!Dv:CvAt?nn3WBt?r>o@p=l.P:i.>qr>t>s>s>s>t?t?u@u@vAwBxCyDzE{F{F|F{FyFxIvG?y>t>t?t?u@u@vAwBxCyDzE|G}H~IJKKJI}|Er=j G{VG|F{Dx?mCtCwAv?r:eCvC>q5] @q?us>t@|>u>t?t?u?u@vAwBxCyD{E|G~IJLMMNNMMK}c:]Gz.G}F{Cw=jCu%DBrt>t>t>t?u?u?v@v@wAxByD{E|G~IKMNOPQQPONLCoFy$G|FC{9a Ar3=m:7b Bp6=n8b2t>t>u?u?u?v@v@wAxByCzE|F~HJJKKvLrNPQSRQOKBmFy.HCyo:hp=r=m=s>u>u?u?u?v?v@w@wAxBzD{E}GHExb@o#o;jܡ=ru>u?u?v?v?v@w@wAxByCzD|F~F}Aq23cJ}%VNS'AzHy RROKAo?o;j=swA=m?w?w?w?w?w@x@x@yAzB{C|D|@p@ );bU[WQZQqNiK}G?ni?z>u=n?w?w?w?x?x@x@yAyAzB{C|Cz;f -M>k@kJ|DPUYYWXURDq,?mv;j>u=n?x?x?x?x@x@y@yAzAzB{C|Bx@qREz~HKOSUVTHy%v>q?w?x?y?y@y@y@yAzA{B{C|BwF~HJLOQQMK%@x!BEw@o>m;iڢ=uq>t>v@y@y@y@y@z@zAzA{B{C|BxE~KKKMLEx3O>m:gpp@z@z@z@{@{A{A{A{B|B|B{CzEFDzK*K>l݀;j.O0e?x@u?xA{A{A{A{A{A{A|B|B|C}BwC{?t$6W=l:h؍'O=t/A{>rA|A|A}A}A|A|A|B|B|B}Bz=k0>jOop;jV9`=l;i۝.OC|!IF{HHGEEDC~B}B}B|B|B{@to:b"=k;=lr>q?s>p>p>p;j4-Ip?s?s?t?s>s>r=o=o9f" ;hcnPOJLJHFECC~B}A|A{A{AzAz@w@w?t?r?t?u@u?v?u?t?t>s>r=p=kݟs>s>r>q=p;k\=p?>t;jޡ5]QVRMMJGEDB}B|A{@z@z@y@y@x@x?w?w?v?v?u?t>t>s>r>r>qr?|ީ7aRWSKKIFDC~B|A{@z@z@y@x?x?w?w?v?v?u>u>t>s>s?wA=o8e.Hu>t>t>s>s>s>s9i'7[u>t>s>s>r=o8b%:eb=mu>t>t>s>r;jv;egq>t?w?w?w?w?v>u>t=rt"@y{@y@y@v>q=o=m=n=op>r=pu[>v>u>uBA=s=r=q #include #include #include #include #include #include #include #include "faldisass.h" namespace Falcon { void gen_code( Module *mod, const FuncDef *fd, Stream *out, const t_labelMap &labels ); SymbolTable *global_symtab; SymbolTable *current_symtab; /************************************************************** * Option management ***************************************************************/ Options::Options(): m_dump_str( false ), m_dump_sym( false ), m_dump_dep( false ), m_isomorphic( false ), m_inline_str( false ), m_stdin( false ), m_lineinfo( true ), m_fname(0) {} Options options; /************************************************************** * ***************************************************************/ void write_label( Stream *output, uint32 landing ) { String temp; if ( options.m_isomorphic ) { temp = "_label_"; temp.writeNumber( (int64) landing ); } else { temp = "0x"; temp.writeNumberHex( landing, true ); } output->writeString( temp ); } void write_label_long( Stream *output, uint64 landing ) { String temp; temp.writeNumberHex( landing, true ); output->writeString( temp ); } void write_string( Stream *output, uint32 stringId, Module *mod ) { String temp; if ( ! options.m_inline_str ) { temp += "#"; temp.writeNumber( (int64) stringId ); output->writeString( temp ); } else { mod->getString( stringId )->escape( temp ); output->writeString( "\"" + temp + "\"" ); } } void write_symbol( Stream *output, const char *prefix, int32 id ) { if ( options.m_isomorphic ) { if ( current_symtab != 0 && (*prefix == 'L' || *prefix == 'P') ) { MapIterator iter = current_symtab->map().begin(); while( iter.hasCurrent() ) { const Symbol *sym = *(const Symbol **) iter.currentValue(); if ( (sym->isLocal() && *prefix == 'L' && sym->itemId() == id) || (sym->isParam() && *prefix == 'P' && sym->itemId() == id) ) { output->writeString( "$" + sym->name() ); return; } iter.next(); } } // Search for symbols in the global symbol map else if ( *prefix == 'G' ) { MapIterator iter = global_symtab->map().begin(); while( iter.hasCurrent() ) { const Symbol *sym = *(const Symbol **) iter.currentValue(); if ( sym->itemId() == id ) { output->writeString( "$" ); if ( global_symtab != current_symtab ) output->writeString( "*" ); //import output->writeString( sym->name() ); return; } iter.next(); } } } String temp = "$"; temp += prefix; temp.writeNumber( (int64) id ); output->writeString( temp ); } void write_operand( Stream *output, byte *instruction, int opnum, Module *mod ) { int offset = 4; int count = 1; String temp; while ( count < opnum ) { switch( instruction[ count ] ) { case P_PARAM_NTD32: case P_PARAM_INT32: case P_PARAM_GLOBID: case P_PARAM_LOCID: case P_PARAM_PARID: case P_PARAM_STRID: case P_PARAM_LBIND: offset += 4; break; case P_PARAM_NUM: case P_PARAM_INT64: case P_PARAM_NTD64: offset += 8; break; } count ++; } switch( instruction[ opnum ] ) { // if it's a NTD32, it may be a jump case P_PARAM_NTD32: if ( options.m_isomorphic ) { byte opcode = *instruction; if ( ( opnum == 1 && ( opcode == P_JMP || opcode == P_TRAL || opcode == P_TRY || opcode == P_JTRY || opcode == P_IFT || opcode == P_IFF || opcode == P_ONCE || opcode == P_TRAV || opcode == P_TRAN || opcode == P_SWCH ) ) || ( opnum == 2 && ( opcode == P_FORK || opcode == P_TRAN ) ) ) { write_label( output, *((int32*) (instruction + offset )) ); return; } } temp = "0x"; temp.writeNumberHex( *((int32*) (instruction + offset )) ); output->writeString( temp ); break; case P_PARAM_INT32: temp.writeNumber( (int64) *((int32*) (instruction + offset ))); output->writeString( temp ); break; case P_PARAM_NOTUSED: return; case P_PARAM_STRID: write_string( output, *((int32*) (instruction + offset )), mod ); break; case P_PARAM_LBIND: output->writeString( "$" + *mod->getString( *((int32*) (instruction + offset )))); break; case P_PARAM_NUM: temp.writeNumber( loadNum( instruction + offset )); output->writeString(temp ); break; case P_PARAM_NIL: output->writeString( "NIL" ); break; case P_PARAM_TRUE: output->writeString( "T" ); break; case P_PARAM_FALSE: output->writeString( "F" ); break; case P_PARAM_INT64: temp.writeNumber( (int64) loadInt64(instruction + offset) ); output->writeString(temp ); break; case P_PARAM_GLOBID: write_symbol( output, "G", *((int32*) (instruction + offset )) ); break; case P_PARAM_LOCID: write_symbol( output, "L", *((int32*) (instruction + offset )) ); break; case P_PARAM_PARID: write_symbol( output, "P", *((int32*) (instruction + offset )) ); break; case P_PARAM_NTD64: write_label_long( output, loadInt64((instruction + offset )) ); break; case P_PARAM_REGA: output->writeString( "A" ); break; case P_PARAM_REGB: output->writeString( "B" ); break; case P_PARAM_REGS1: output->writeString( "S1" ); break; case P_PARAM_REGL1: output->writeString( "L1" ); break; case P_PARAM_REGL2: output->writeString( "L2" ); break; } } uint32 calc_next( byte *instruction ) { uint32 offset = 4; int count = 1; while ( count < 4 ) { switch( instruction[ count ] ) { case P_PARAM_NTD32: case P_PARAM_INT32: case P_PARAM_GLOBID: case P_PARAM_LOCID: case P_PARAM_PARID: case P_PARAM_STRID: case P_PARAM_LBIND: offset += 4; break; case P_PARAM_NUM: case P_PARAM_INT64: case P_PARAM_NTD64: offset += 8; break; } count ++; } return offset; } /*************************************************************** Isomorphic symbol generators. */ void gen_function( Module *module, const Symbol *func, Stream *m_out, t_labelMap labels ) { m_out->writeString( "; ---------------------------------------------\n" ); m_out->writeString( "; Function " + func->name() + "\n" ); String tempOff; tempOff.writeNumberHex( func->getFuncDef()->basePC() ); m_out->writeString( "; Declared at offset 0x" + tempOff + "\n" ); m_out->writeString( "; ---------------------------------------------\n\n" ); m_out->writeString( ".funcdef " + func->name() ); if ( func->exported() ) m_out->writeString( " export" ); m_out->writeString( "\n" ); // generate the local symbol table. const FuncDef *fd = func->getFuncDef(); MapIterator iter = fd->symtab().map().begin(); std::vector params; params.resize( fd->symtab().size() ); while( iter.hasCurrent() ) { Symbol *sym = *(Symbol **) iter.currentValue(); switch( sym->type() ) { case Symbol::tparam: // paramters must be outputted with their original order. params[ sym->itemId() ] = sym; break; case Symbol::tlocal: m_out->writeString( ".local " + sym->name() + "\n" ); break; default: break; } iter.next(); } for ( uint32 parId = 0; parId < params.size(); parId++ ) { Symbol *sym = params[parId]; if (sym != 0 ) m_out->writeString( ".param " + sym->name() + "\n" ); } m_out->writeString( "; ---\n" ); gen_code( module, fd, m_out, labels ); m_out->writeString( ".endfunc\n\n" ); } void gen_propdef( Stream *m_out, const VarDef &def ) { String temp; switch( def.type() ) { case VarDef::t_nil: m_out->writeString( "NIL" ); break; case VarDef::t_bool: m_out->writeString( def.asBool() ? "T": "F" ); break; case VarDef::t_int: temp.writeNumber( def.asInteger() ); m_out->writeString( temp ); break; case VarDef::t_num: temp.writeNumber( def.asNumeric() ); m_out->writeString( temp ); break; case VarDef::t_string: def.asString()->escape( temp ); m_out->writeString( "\"" + temp + "\"" ); break; case VarDef::t_reference: case VarDef::t_symbol: m_out->writeString( "$" +def.asSymbol()->name() ); break; default: break; } } void gen_class( Stream *m_out, const Symbol *sym ) { m_out->writeString( "; ---------------------------------------------\n" ); m_out->writeString( "; Class " + sym->name() + "\n" ); m_out->writeString( "; ---------------------------------------------\n\n" ); m_out->writeString( ".classdef " + sym->name() ); /*if ( sym->exported() ) *m_out << " export";*/ m_out->writeString( "\n" ); const ClassDef *cd = sym->getClassDef(); // write class symbol parameters. MapIterator st_iter = cd->symtab().map().begin(); while( st_iter.hasCurrent() ) { // we have no locals. const String *ptrstr = *(const String **) st_iter.currentKey(); m_out->writeString( ".param " + *ptrstr +"\n" ); st_iter.next(); } // write all the inheritances. ListElement *it_iter = cd->inheritance().begin(); while( it_iter != 0 ) { const InheritDef *id = (const InheritDef *) it_iter->data(); const Symbol *parent = id->base(); m_out->writeString( ".inherit $" + parent->name() ); m_out->writeString( "\n" ); it_iter = it_iter->next(); } // write all the properties. MapIterator pt_iter = cd->properties().begin(); while( pt_iter.hasCurrent() ) { const VarDef *def = *(const VarDef **) pt_iter.currentValue(); if ( ! def->isBaseClass() ) { if ( def->isReference() ) m_out->writeString( ".propref " ); else m_out->writeString( ".prop " ); const String *key = *(const String **) pt_iter.currentKey(); m_out->writeString( *key + " " ); gen_propdef( m_out, *def ); m_out->writeString( "\n" ); } pt_iter.next(); } if ( cd->constructor() != 0 ) { m_out->writeString( ".ctor $" + cd->constructor()->name() + "\n" ); } m_out->writeString( ".endclass\n" ); m_out->writeString( "; ---------------------------------------------\n" ); } /************************************************** Symbols */ void gen_code( Module *module, const FuncDef *fd, Stream *out, const t_labelMap &labels ) { uint32 iPos =0; byte *code = fd->code(); byte opcode; int oldline = -1; // get the main code while( iPos < fd->codeSize() ) { opcode = code[ 0 ]; if ( options.m_isomorphic ) { int l = module->getLineAt( fd->basePC() + iPos ); if ( l != oldline ) { String temp; out->writeString( ".line " ); temp.writeNumber( (int64) l ); out->writeString( temp + "\n" ); oldline = l; } t_labelMap::const_iterator target = labels.find( iPos ); if ( target != labels.end() ) { String temp; temp.writeNumber( (int64) target->first ); out->writeString( "_label_" + temp + ":\n" ); } } else { String temp; temp.writeNumberHex( iPos ); while( temp.length() < 8 ) temp = " " + temp; out->writeString( temp ); } out->writeString( "\t" ); const char *csOpName; switch( opcode ) { case P_END : csOpName = "END "; break; case P_NOP : csOpName = "NOP "; break; case P_PSHN: csOpName = "PSHN"; break; case P_RET : csOpName = "RET "; break; case P_RETA: csOpName = "RETA"; break; // Range 2: one parameter ops case P_PTRY: csOpName = "PTRY"; break; case P_LNIL: csOpName = "LNIL"; break; case P_RETV: csOpName = "RETV"; break; case P_FORK: csOpName = "FORK"; break; case P_BOOL: csOpName = "BOOL"; break; case P_GENA: csOpName = "GENA"; break; case P_GEND: csOpName = "GEND"; break; case P_PUSH: csOpName = "PUSH"; break; case P_PSHR: csOpName = "PSHR"; break; case P_POP : csOpName = "POP "; break; case P_JMP : csOpName = "JMP "; break; case P_INC : csOpName = "INC "; break; case P_DEC : csOpName = "DEC "; break; case P_NEG : csOpName = "NEG "; break; case P_NOT : csOpName = "NOT "; break; case P_TRAL: csOpName = "TRAL"; break; case P_IPOP: csOpName = "IPOP"; break; case P_XPOP: csOpName = "XPOP"; break; case P_GEOR: csOpName = "GEOR"; break; case P_TRY : csOpName = "TRY "; break; case P_JTRY: csOpName = "JTRY"; break; case P_RIS : csOpName = "RIS "; break; case P_BNOT: csOpName = "BNOT"; break; case P_NOTS: csOpName = "NOTS"; break; case P_PEEK: csOpName = "PEEK"; break; // Range3: Double parameter ops case P_LD : csOpName = "LD "; break; case P_LDRF: csOpName = "LDRF"; break; case P_ADD : csOpName = "ADD "; break; case P_SUB : csOpName = "SUB "; break; case P_MUL : csOpName = "MUL "; break; case P_DIV : csOpName = "DIV "; break; case P_MOD : csOpName = "MOD "; break; case P_POW : csOpName = "POW "; break; case P_ADDS: csOpName = "ADDS"; break; case P_SUBS: csOpName = "SUBS"; break; case P_MULS: csOpName = "MULS"; break; case P_DIVS: csOpName = "DIVS"; break; case P_MODS: csOpName = "MODS"; break; case P_POWS: csOpName = "POWS"; break; case P_BAND: csOpName = "BAND"; break; case P_BOR : csOpName = "BOR "; break; case P_BXOR: csOpName = "BXOR"; break; case P_ANDS: csOpName = "ANDS"; break; case P_ORS : csOpName = "ORS "; break; case P_XORS: csOpName = "XORS"; break; case P_GENR: csOpName = "GENR"; break; case P_EQ : csOpName = "EQ "; break; case P_NEQ : csOpName = "NEQ "; break; case P_GT : csOpName = "GT "; break; case P_GE : csOpName = "GE "; break; case P_LT : csOpName = "LT "; break; case P_LE : csOpName = "LE "; break; case P_IFT : csOpName = "IFT "; break; case P_IFF : csOpName = "IFF "; break; case P_CALL: csOpName = "CALL"; break; case P_INST: csOpName = "INST"; break; case P_ONCE: csOpName = "ONCE"; break; case P_LDV : csOpName = "LDV "; break; case P_LDP : csOpName = "LDP "; break; case P_TRAN: csOpName = "TRAN"; break; case P_LDAS: csOpName = "LDAS"; break; // when isomorphic, switch is created through directive case P_SWCH: csOpName = options.m_isomorphic ? "" : "SWCH"; break; case P_IN : csOpName = "IN "; break; case P_NOIN: csOpName = "NOIN"; break; case P_PROV: csOpName = "PROV"; break; case P_STPS: csOpName = "STPS"; break; case P_STVS: csOpName = "STVS"; break; case P_AND : csOpName = "AND "; break; case P_OR : csOpName = "OR "; break; // Range 4: ternary opcodes case P_STP : csOpName = "STP "; break; case P_STV : csOpName = "STV "; break; case P_LDVT: csOpName = "LDVT"; break; case P_LDPT: csOpName = "LDPT"; break; case P_STPR: csOpName = "STPR"; break; case P_STVR: csOpName = "STVR"; break; case P_TRAV: csOpName = "TRAV"; break; case P_INCP: csOpName = "INCP"; break; case P_DECP: csOpName = "DECP"; break; case P_SHR : csOpName = "SHR "; break; case P_SHL : csOpName = "SHL "; break; case P_SHRS: csOpName = "SHRS"; break; case P_SHLS: csOpName = "SHLS"; break; case P_CLOS: csOpName = "CLOS"; break; case P_PSHL: csOpName = "PSHL"; break; case P_LSB : csOpName = "LSB "; break; case P_SELE: csOpName = options.m_isomorphic ? "": "SELE"; break; case P_INDI: csOpName = "INDI"; break; case P_STEX: csOpName = "STEX"; break; case P_TRAC: csOpName = "TRAC"; break; case P_WRT : csOpName = "WRT "; break; case P_STO : csOpName = "STO "; break; case P_FORB: csOpName = "FORB"; break; case P_EVAL: csOpName = "EVAL"; break; case P_OOB : csOpName = "OOB "; break; case P_TRDN: csOpName = "TRDN"; break; default: csOpName = "????"; } out->writeString( csOpName ); // if the operation is a switch, it's handled a bit specially. // now print the operators if ( ( opcode != P_SWCH && opcode != P_SELE) || ! options.m_isomorphic ) { if( code[ 1 ] != 0 ) { out->writeString( "\t" ); write_operand( out, code, 1, module ); if( code[ 2 ] != 0 ) { out->writeString( ", " ); write_operand( out, code, 2, module ); if( code[ 3 ] != 0 ) { out->writeString( ", " ); write_operand( out, code, 3, module ); } } } } // Line marker in non-ismoprphic mode if ( options.m_lineinfo && ! options.m_isomorphic ) { int l = module->getLineAt( fd->basePC() + iPos ); if ( l != oldline ) { out->writeString( " ; LINE " ); String temp; temp.writeNumber( (int64) l ); out->writeString( temp ); oldline = l; } } //special swch handling if ( opcode == P_SWCH || opcode == P_SELE ) { out->writeString( "\n" ); if ( opcode == P_SWCH ) out->writeString( ".switch " ); else out->writeString( ".select " ); write_operand( out, code, 2, module ); out->writeString( ", " ); write_operand( out, code, 1, module ); out->writeString( "\n" ); uint32 advance = calc_next( code ); iPos += advance; code += advance; uint64 sw_count = (uint64) loadInt64( code - sizeof(int64) ); uint16 sw_int = (int16) (sw_count >> 48); uint16 sw_rng = (int16) (sw_count >> 32); uint16 sw_str = (int16) (sw_count >> 16); uint16 sw_obj = (int16) sw_count; //write the nil operand if ( *reinterpret_cast(code) != 0xffffffff ) { if ( ! options.m_isomorphic ) out->writeString( "\t\t" ); out->writeString( ".case NIL, " ); write_label( out, *reinterpret_cast(code) ); out->writeString( "\n" ); } iPos += sizeof( int32 ); code += sizeof( int32 ); // write the integer table while( sw_int > 0 ) { if (! options.m_isomorphic ) out->writeString( "\t\t" ); out->writeString( ".case " ); String temp; temp.writeNumber( (int64) loadInt64( code ) ); out->writeString( temp + ", " ); code += sizeof( int64 ); iPos += sizeof( int64 ); write_label( out, *reinterpret_cast( code ) ); out->writeString( "\n" ); code += sizeof( int32 ); iPos += sizeof( int32 ); --sw_int; } // write the range table while( sw_rng > 0 ) { if ( !options.m_isomorphic ) if (! options.m_isomorphic ) out->writeString( "\t\t" ); out->writeString( ".case " ); String temp; temp.writeNumber( (int64) *reinterpret_cast(code) ); temp += ":"; temp.writeNumber( (int64) *reinterpret_cast(code+ sizeof(int32) ) ); out->writeString( temp + ", " ); code += sizeof( int64 ); iPos += sizeof( int64 ); write_label( out, *reinterpret_cast( code ) ); out->writeString( "\n" ); code += sizeof( int32 ); iPos += sizeof( int32 ); --sw_rng; } // write the string table while( sw_str > 0 ) { if ( ! options.m_isomorphic ) out->writeString( "\t\t" ); out->writeString( ".case " ); write_string( out, *reinterpret_cast(code), module ); code += sizeof( int32 ); iPos += sizeof( int32 ); out->writeString( ", " ); write_label( out, *reinterpret_cast( code ) ); out->writeString( "\n" ); code += sizeof( int32 ); iPos += sizeof( int32 ); --sw_str; } //write the symbol table while( sw_obj > 0 ) { if ( !options.m_isomorphic ) out->writeString( "\t\t" ); out->writeString( ".case " ); int32 symId = *reinterpret_cast(code); Symbol *sym = module->getSymbol( symId ); if( sym == 0 ) { String temp; temp.writeNumber( (int64) symId ); out->writeString( " [SORRY, SYMBOL " + temp + " NOT FOUND], " ); } else out->writeString( "$" + sym->name() + ", " ); code += sizeof( int32 ); iPos += sizeof( int32 ); write_label( out, *reinterpret_cast( code ) ); out->writeString( "\n" ); code += sizeof( int32 ); iPos += sizeof( int32 ); --sw_obj; } if ( !options.m_isomorphic ) out->writeString( "\t" ); out->writeString( ".endswitch\n\n" ); } else { uint32 advance = calc_next( code ); iPos += advance; code += advance; } out->writeString( "\n" ); } } /******************************************************** Analizer -- queries the landings of the various instructions. *********************************************************/ void Analizer( FuncDef *fd, t_labelMap &labels ) { byte *code, *end; byte opcode; code = fd->code(); end = code + fd->codeSize(); while( code < end ) { opcode = code[ 0 ]; // check for codes with jump lablels here. switch( opcode ) { case P_FORK: labels[ *(uint32 *)(code + sizeof(int32)*2 ) ] = 0; break; case P_TRAN: labels[ *(uint32 *)(code + sizeof(int32)*2 ) ] = 0; // do not break; fallback case P_SWCH: case P_JMP : case P_TRAL: case P_TRY : case P_JTRY: case P_IFT : case P_IFF : case P_ONCE: case P_TRAV: labels[ *(uint32 *)(code + sizeof(int32)) ] = 0; break; } if ( opcode != P_SWCH ) { code += calc_next( code ); } else { //special swch handling code += sizeof( int32 ) * 3; // remove instro and two operands uint64 sw_count = (uint64) loadInt64(code); uint16 sw_int = (int16) (sw_count >> 48); uint16 sw_rng = (int16) (sw_count >> 32); uint16 sw_str = (int16) (sw_count >> 16); uint16 sw_obj = (int16) sw_count; code += sizeof( int64 ); //write the nil operand if ( *reinterpret_cast(code) != 0xffffffff ) { labels[*reinterpret_cast(code)] = 0; } code += sizeof( int32 ); // write the integer table while( sw_int > 0 ) { code += sizeof( int64 ); labels[*reinterpret_cast(code)] = 0; code += sizeof( int32 ); --sw_int; } // write the range table while( sw_rng > 0 ) { code += sizeof( int64 ); labels[*reinterpret_cast(code)] = 0; code += sizeof( int32 ); --sw_rng; } // write the string table while( sw_str > 0 ) { code += sizeof( int32 ); labels[*reinterpret_cast(code)] = 0; code += sizeof( int32 ); --sw_str; } //write the symbol table while( sw_obj > 0 ) { code += sizeof( int32 ); labels[*reinterpret_cast(code)] = 0; code += sizeof( int32 ); --sw_obj; } } } } /******************************************************** Write the string table *********************************************************/ void write_strtable( e_tabmode mode , Stream *out, Module *mod ) { const String *str; const StringTable *strtab = &mod->stringTable(); out->writeString( ";--------------------------------------------\n" ); out->writeString( "; String table\n" ); out->writeString( ";--------------------------------------------\n" ); int32 id = 0; while( id < strtab->size() ) { str = strtab->get( id ); String temp; switch( mode ) { case e_mode_comment: temp.writeNumber( (int64) id ); out->writeString( "; " + temp + ": " ); temp.size(0); break; case e_mode_table: out->writeString( str->exported() ? ".istring " : ".string " ); break; } str->escape( temp ); out->writeString( "\"" + temp + "\"\n" ); ++id; } out->writeString( "; ------ End of string table -----------\n" ); } void write_symtable( e_tabmode mode , Stream *out, Module *mod ) { const Symbol *sym; const SymbolTable *st = &mod->symbolTable(); out->writeString( ";--------------------------------------------\n" ); out->writeString( "; Symbol table\n" ); out->writeString( ";--------------------------------------------\n" ); MapIterator iter = st->map().begin(); while( iter.hasCurrent() ) { sym = *(const Symbol **) iter.currentValue(); if ( sym->name() == "__main__" ) { iter.next(); continue; } String temp; switch( mode ) { case e_mode_comment: out->writeString( "; " ); switch( sym->type() ) { case Symbol::tundef: out->writeString( "UNDEF" ); break; case Symbol::tglobal: out->writeString( "GLOBAL" ); break; case Symbol::tlocal: out->writeString( "LOCAL" ); break; case Symbol::tparam: out->writeString( "PARAM" ); break; case Symbol::tlocalundef: out->writeString( "LU" ); break; case Symbol::tfunc: { String temp; temp.writeNumberHex( sym->getFuncDef()->basePC(), true ); out->writeString( "FUNC(0x" + temp + ")" ); break; } case Symbol::textfunc: out->writeString( "EXTFUNC" ); break; case Symbol::tclass: out->writeString( "CLASS" ); break; case Symbol::tprop: out->writeString( "PROP" ); break; case Symbol::tvar: out->writeString( "VDEF" ); break; case Symbol::tinst: out->writeString( "INST" ); break; default: break; } out->writeString( " " + sym->name() + ": " ); temp.writeNumber( (int64) sym->id() ); out->writeString( temp ); temp.size( 0 ); temp.writeNumber( (int64) sym->declaredAt() ); out->writeString( " at line "+ temp + " " ); temp.size( 0 ); temp.writeNumber( (int64) sym->itemId() ); out->writeString( "(G"+ temp + ")" ); break; case e_mode_table: // see if it's imported if ( sym->type() == Symbol::tundef ) { if ( sym->imported() ) { // see if we have an alias from where to import it uint32 dotpos = sym->name().rfind( "." ); if( dotpos != String::npos ) { String modname = sym->name().subString( 0, dotpos ); String symname = sym->name().subString( dotpos + 1 ); temp = ".import " + symname + " "; temp.writeNumber( (int64) sym->declaredAt() ); ModuleDepData *depdata = mod->dependencies().findModule( modname ); // We have created the module, the entry must be there. fassert( depdata != 0 ); if ( depdata->isFile() ) { if( depdata->moduleName() == modname ) temp += " \"" + modname+"\""; else temp += " \"" + depdata->moduleName() +"\" "+ modname; } else { if( depdata->moduleName() == modname ) temp += " " + modname; else temp += " " + depdata->moduleName() +" "+ modname; } } else { temp = ".import " + sym->name() + " "; temp.writeNumber( (int64) sym->declaredAt() ); } } else { temp = ".extern " + sym->name() + " "; temp.writeNumber( (int64) sym->declaredAt() ); } out->writeString( temp ); break; } switch( sym->type() ) { case Symbol::tglobal: out->writeString( ".global" ); break; case Symbol::tlocal: out->writeString( ".local" ); break; case Symbol::tparam: out->writeString( ".param" ); break; case Symbol::tfunc: out->writeString( ".func" ); break; case Symbol::tclass: out->writeString( ".class" ); break; case Symbol::tprop: out->writeString( ".prop" ); break; case Symbol::tundef: out->writeString( ";---(INVALID) .undef" ); break; case Symbol::tlocalundef: out->writeString( ";---(INVALID) .localundef" ); break; case Symbol::textfunc: out->writeString( ";---(INVALID) .extfunc" ); break; case Symbol::tconst: out->writeString( ";---(INVALID) .const" ); break; case Symbol::timportalias: out->writeString( ";---(INVALID) .importalias" ); break; case Symbol::tvar: out->writeString( ";---(INVALID) .var" ); break; case Symbol::tinst: out->writeString( ".instance $" ); out->writeString( sym->getInstance()->name() ); break; } out->writeString( " " + sym->name() ); if( sym->declaredAt() != 0 ) { String temp = " "; temp.writeNumber( (int64) sym->declaredAt() ); out->writeString( temp ); } break; } if ( sym->exported() ) out->writeString( " export" ); out->writeString( "\n" ); iter.next(); } out->writeString( "; ------ End of symbol table ---------------\n" ); } void write_deptable( e_tabmode mode , Stream *out, Module *mod ) { const String *str; const DependTable *deps = &mod->dependencies(); current_symtab = 0; out->writeString( ";--------------------------------------------\n" ); out->writeString( "; Dependencies table\n" ); out->writeString( ";--------------------------------------------\n" ); MapIterator iter = deps->begin(); while( iter.hasCurrent() ) { const String *alias = *(const String **) iter.currentKey(); const ModuleDepData *data = *(const ModuleDepData **) iter.currentValue(); str = &data->moduleName(); if ( data->isPrivate() ) { if ( mode == e_mode_comment ) { out->writeString( ";" ); if ( data->isFile() ) out->writeString( " \"" + *str + "\"" ); else out->writeString( " " + *str ); if ( *str != *alias ) out->writeString( " -> " + *alias ); out->writeString( " (private)" ); } } else { switch( mode ) { case e_mode_comment: out->writeString( ";" ); break; case e_mode_table: out->writeString( ".load" ); break; } if ( data->isFile() ) out->writeString( " \"" + *str + "\"" ); else out->writeString( " " + *str ); } out->writeString( "\n" ); iter.next(); } out->writeString( "; ------ End of depend table -----------\n" ); } void usage() { ::Falcon::Stream *stdOut = ::Falcon::stdOutputStream(); stdOut->writeString( "USAGE: faldisass [options] [filename]\n" ); stdOut->writeString( "\t-s: Dump the string table\n" ); stdOut->writeString( "\t-S: Write the strings inline instead of using #strid\n" ); stdOut->writeString( "\t-y: Dump the symbol table\n" ); stdOut->writeString( "\t-d: Dump the dependency table\n" ); stdOut->writeString( "\t-l: add line information\n" ); stdOut->writeString( "\t-h: show this help\n" ); stdOut->writeString( "\t-i: create an isomorphic version of the original assembly\n" ); stdOut->writeString( "\tIf 'filename' is '-' or missing, read from stdin\n" ); stdOut->flush(); } } using namespace Falcon; int main( int argc, char *argv[] ) { // initialize the engine Falcon::Engine::AutoInit autoInit; Stream *stdErr = stdErrorStream(); // option decoding for ( int i = 1; i < argc; i++ ) { char *option = argv[i]; if (option[0] == '-' ) { if ( option[1] == 0 ) { options.m_stdin = true; options.m_fname = 0; continue; } int pos = 1; while ( option[pos] != 0 ) { switch ( option[ pos ] ) { case 'i': options.m_isomorphic = true; options.m_inline_str = true; break; case 's': options.m_dump_str = true; break; case 'l': options.m_lineinfo = true; break; case 'S': options.m_inline_str = true; break; case 'y': options.m_dump_sym = true; break; case 'd': options.m_dump_dep = true; break; case 'h': case '?': usage(); return 0; break; default: stdErr->writeString( "faldisass: unrecognized option '-" ); stdErr->put( option[pos] ); stdErr->writeString( "'.\n" ); usage(); stdErr->flush(); return 1; } ++pos; } } else { options.m_fname = option; options.m_stdin = false; } } Stream *in; // file opening FileStream inf; if( options.m_fname != 0 && ! options.m_stdin ) { inf.open( options.m_fname ); if ( ! inf.good() ) { stdErr->writeString( "faldisass: can't open file " ); stdErr->writeString( options.m_fname ); stdErr->writeString( "\n" ); stdErr->flush(); return 1; } in = &inf; } else { in = stdInputStream(); } Module *module = new Module(); if( ! module->load( in, false ) ) { if ( options.m_fname == 0 || options.m_stdin ) stdErr->writeString( "faldisass: invalid module in input stream\n" ); else { stdErr->writeString( "faldisass: invalid module " ); stdErr->writeString( options.m_fname ); stdErr->writeString( "\n" ); } stdErr->flush(); return 1; } global_symtab = &module->symbolTable(); // for isomorphic we need the analysis. Stream *stdOut = DefaultTextTranscoder( new StdOutStream ); t_labelMap labels; if ( options.m_isomorphic ) { // print immediately the string table. write_strtable( e_mode_table, stdOut, module ); // and then the load table. write_deptable( e_mode_table, stdOut, module ); // and the symbol table. write_symtable( e_mode_table, stdOut, module ); } // now write MAIN, if it exists Symbol *main = module->findGlobalSymbol( "__main__" ); if ( main != 0 ) { current_symtab = global_symtab; if ( options.m_isomorphic ) { labels.clear(); Analizer( main->getFuncDef(), labels ); } gen_code( module, main->getFuncDef(), stdOut, labels ); } // also fetch the function map. MapIterator iter = module->symbolTable().map().begin(); while( iter.hasCurrent() ) { const Symbol *sym = *(const Symbol **) iter.currentValue(); if ( sym->name() == "__main__" ) { // already managed iter.next(); continue; } if ( sym->isFunction() ) { current_symtab = &sym->getFuncDef()->symtab(); if ( options.m_isomorphic ) { labels.clear(); Analizer( sym->getFuncDef(), labels ); gen_function( module, sym, stdOut, labels ); } else { String tempOff; tempOff.writeNumberHex( sym->getFuncDef()->basePC() ); stdOut->writeString( "\n; Generating function " + sym->name() + "(0x"+ tempOff + ")\n" ); gen_code( module, sym->getFuncDef(), stdOut, labels ); } } else if ( sym->isClass() && options.m_isomorphic ) { // we can generate the classess now, as the symbols have been // already declared. gen_class( stdOut, sym ); } iter.next(); } if ( options.m_dump_str ) { write_strtable( e_mode_comment, stdOut, module ); } if ( options.m_dump_sym ) { write_symtable( e_mode_comment, stdOut, module ); } if ( options.m_dump_dep ) { write_deptable( e_mode_comment, stdOut, module ); } stdOut->writeString( "\n" ); module->decref(); return 0; } /* end of faldisass.cpp */ clt/faldisass/faldisass.h000066400000000000000000000031361176363201700157440ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: faldisass.h Falcon disassembler private header file. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun set 26 2005 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Falcon disassembler private header file. This is just an header file to keep falcon disassembler private types and data ordered. */ #ifndef flc_faldisass_H #define flc_faldisass_H #include #include #include #include #include #include namespace Falcon { typedef enum { e_mode_comment, e_mode_table } e_tabmode; void write_operand( Stream *output, byte *instruction, int opnum, Module *mod ); uint32 calc_next( byte *instruction ); void disassembler( Module *module, Stream *out ); void write_strtable( e_tabmode mode , Stream *out, Module *mod ); void write_symtable( e_tabmode mode , Stream *out, const SymbolTable *st ); void write_deptable( e_tabmode mode , Stream *out, Module *mod ); void usage(); class Options { public: bool m_dump_str; bool m_dump_sym; bool m_dump_dep; bool m_isomorphic; bool m_inline_str; bool m_stdin; bool m_lineinfo; const char *m_fname; Options(); }; extern Options options; typedef std::map< int32, int32 > t_labelMap; typedef std::map< int32, const Symbol * > t_funcMap; } #endif /* end of faldisass.h */ clt/faldisass/faldisass.rc000066400000000000000000000024041176363201700161160ustar00rootroot00000000000000///////////////////////////////////////////////////////////////////////////// // // Icon // // TODO: create an icon //MAINICON ICON DISCARDABLE "resources\falcon-ico-base.ico" ///////////////////////////////////////////////////////////////////////////// // // Version // // for project wide versioning... #include #include VS_VERSION_INFO VERSIONINFO FILEVERSION FALCON_VERSION_RCINFO_N PRODUCTVERSION FALCON_VERSION_RCINFO_N FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x4L FILETYPE 0x0L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "041004b0" BEGIN VALUE "CompanyName", "Falcon Committee" VALUE "FileDescription", "The Falcon command line disassembler" VALUE "FileVersion", FALCON_VERSION_RCINFO VALUE "InternalName", "faldisass" VALUE "LegalCopyright", "The Falcon Programming Language License" VALUE "OriginalFilename", "faldisass.exe" VALUE "ProductName", "The Falcon Programming Language" VALUE "ProductVersion", FALCON_VERSION_RCINFO END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x410, 1200 END END clt/falpack/000077500000000000000000000000001176363201700132475ustar00rootroot00000000000000clt/falpack/CMakeLists.txt000066400000000000000000000012601176363201700160060ustar00rootroot00000000000000################################################## # Falcon Programming Language # # Falpack ################################################## include_directories( ${PROJECT_BINARY_DIR}/include ${CMAKE_CURRENT_SOURCE_DIRECTORY} ${PROJECT_SOURCE_DIR}/include ) IF(WIN32) SET(SYS_RC falpack.rc) SET(SYS_SOURCES falpack_sys_win.cpp ) ELSE() SET(SYS_SOURCES falpack_sys_unix.cpp ) ENDIF() # Target ADD_EXECUTABLE( falpack falpack.cpp options.cpp utils.cpp ${SYS_SOURCES} falpack_sys.h options.h utils.h ${SYS_RC} ) #Link TARGET_LINK_LIBRARIES(falpack falcon_engine) #Install INSTALL( TARGETS falpack ${FALCON_INSTALL_DESTINATIONS} ) clt/falpack/falpack.cpp000066400000000000000000000444221176363201700153620ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falpack.cpp Packager for Falcon stand-alone applications ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 22 Jan 2010 20:18:36 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include "options.h" #include "utils.h" #include "falpack_sys.h" #include #include using namespace Falcon; String load_path; String io_encoding; bool ignore_defpath = false; // Forward decl bool transferModules( Options &options, const String& mainScript ); static void version() { stdOut->writeString( "Falcon application packager.\n" ); stdOut->writeString( "Version " ); stdOut->writeString( FALCON_VERSION " (" FALCON_VERSION_NAME ")" ); stdOut->writeString( "\n" ); stdOut->flush(); } static void usage() { stdOut->writeString( "Usage: falpack [options] \n" ); stdOut->writeString( "\n" ); stdOut->writeString( "Options:\n" ); stdOut->writeString( " -b Blacklists this module (by module name)\n" ); stdOut->writeString( " --bin Specify directory where falcon binary resides\n" ); stdOut->writeString( " -e Source files encoding\n" ); stdOut->writeString( " -h, -? This help\n" ); stdOut->writeString( " --lib Specify directory where falcon runtime library resides\n" ); stdOut->writeString( " -L Redefine FALCON_LOAD_PATH\n" ); stdOut->writeString( " -M Pack also pre-compiled modules\n" ); stdOut->writeString( " -P Save the package in this directory\n" ); stdOut->writeString( " -r Install instead of \"falcon\" as interpreter\n" ); stdOut->writeString( " -R Change root for system data into (dflt: _system)\n" ); stdOut->writeString( " -s Strip sources\n" ); stdOut->writeString( " -S Do not store system files (engine + runner)\n" ); stdOut->writeString( " -v Prints version and exit\n" ); stdOut->writeString( " -V Verbose mode\n" ); stdOut->writeString( "\n" ); stdOut->flush(); } bool copyAllResources( Options& options, const Path& from, const Path& tgtPath ) { // do we have an extension filter? bool bHasExt = from.getExtension() != ""; VFSProvider* file = Engine::getVFS("file"); if( file == 0 ) { error( "Can't find FILE resource" ); return false; } DirEntry *entry = file->openDir( from.getFullLocation() ); if( entry == 0 ) { warning( "Can't open directory " + from.getFullLocation() ); return false; } String fname; while( entry->read( fname ) ) { if( fname == ".." || fname == "." ) { continue; } FileStat fs; if ( ! Sys::fal_stats( from.getFullLocation() + "/" + fname, fs ) ) { continue; } if ( fs.m_type == FileStat::t_normal || fs.m_type == FileStat::t_link ) { // do we filter the extension? if( bHasExt ) { if ( ! fname.endsWith( "." + from.getExtension(), true ) ) { continue; } } // TODO: Jail resources under modpath if ( ! copyFile( from.getFullLocation() + "/" + fname, tgtPath.getFullLocation() + "/" + fname ) ) { warning( "Cannot copy resource " + from.getFullLocation() + "/" + fname + " into " + tgtPath.getFullLocation() + "/" + fname ); entry->close(); delete entry; return false; } /* // descend Path nfrom( from ); nfrom.setFullLocation( from.getFullLocation() + "/" + fname ); if( ! copyAllResources( options, nfrom, modPath, tgtPath ) ) { return false; } */ } } entry->close(); delete entry; return true; } bool copyResource( Options& options, const String& resource, const Path& modPath, const Path& tgtPath ) { message( "Storing resource " + resource ); Path resPath( resource ); Path modResPath( modPath ); Path tgtResPath( tgtPath ); if( resPath.isAbsolute() ) { warning( "Resource " + resource + " has an absolute path." ); modResPath.setFullLocation( modPath.getFullLocation() + resPath.getFullLocation() ); tgtResPath.setFullLocation( tgtPath.getFullLocation() + resPath.getFullLocation() ); } else { modResPath.setFullLocation( modPath.getFullLocation() +"/"+ resPath.getFullLocation() ); tgtResPath.setFullLocation( tgtPath.getFullLocation() +"/"+ resPath.getFullLocation() ); } modResPath.setFilename( resPath.getFilename() ); tgtResPath.setFilename( resPath.getFilename() ); // create target path int32 fsStatus; if( ! Sys::fal_mkdir( tgtResPath.getFullLocation(), fsStatus, true ) ) { warning( "Cannot create path " + tgtResPath.getFullLocation() + " for resource " + modResPath.get() ); return false; } if( resPath.getFile() == "*" ) { Path from( resPath ); from.setFullLocation( modPath.getFullLocation() + "/" + from.getFullLocation() ); if ( ! copyAllResources( options, from, tgtResPath ) ) { return false; } } else { // TODO: Jail resources under modpath if ( ! copyFile( modResPath.get(), tgtResPath.get() ) ) { warning( "Cannot copy resource " + modResPath.get() + " into " + tgtResPath.get() ); return false; } } return true; } bool copyFtr( const Path& src, const Path &tgt ) { VFSProvider* file = Engine::getVFS("file"); fassert( file != 0 ); DirEntry *entry = file->openDir( src.getFullLocation() ); if( entry == 0 ) { warning( "Can't open directory " + src.getFullLocation() ); return false; } String fname; String module = src.getFile(); Path orig( src ); Path target( tgt ); while( entry->read( fname ) ) { if( fname.startsWith( module ) && fname.endsWith( ".ftt" ) ) { orig.setFilename( fname ); target.setFilename( fname ); if( ! copyFile( orig.get(), target.get() ) ) { warning( "Can't copy source FTT file " + orig.get() ); } } } entry->close(); delete entry; return true; } static void relativize( const Path& source, Path& target ) { if ( ! target.isAbsolute() ) { return; } String sFull1 = source.getFullLocation(); String sFull2 = target.getFullLocation(); // checking the equality, check also if the last path in sFull2 matches sFull1 completely if( sFull1.length() < sFull2.length() && sFull2.subString(0, sFull1.length()+1 ) == sFull1 + "/" ) { target.setFullLocation( sFull2.subString( sFull1.length() + 1 ) ); } else if( sFull1 == sFull2 ) { target.setFullLocation( "" ); } else { // there is nothing we can do. } } void addPlugins( const Options& options_main, const String& parentModule, const String& path ) { message( "Loading plugin \"" + path +"\" for module " + parentModule ); Path modPath( parentModule ); modPath = modPath.getFullLocation() + "/" + path; // prepare the target plugin path Path outputPath( modPath ); outputPath.setFilename(""); Path mainPath; mainPath.setFullLocation( options_main.m_sMainScriptPath ); relativize( mainPath, outputPath ); outputPath.setFullLocation( options_main.m_sTargetDir +"/"+ outputPath.getLocation() ); // topmost location of the plugin must be if( path.endsWith("*") ) { VFSProvider* file = Engine::getVFS("file"); fassert( file != 0 ); DirEntry *entry = file->openDir( modPath.getFullLocation() ); if( entry == 0 ) { warning( "Can't open plugin directory \"" + modPath.getFullLocation() + "\" for module " + parentModule ); } String fname; while( entry->read( fname ) ) { // binary? if ( fname.endsWith(".fam") ) { // do we have also a source? modPath.setFilename( fname ); modPath.setExtension( "fal" ); FileStat famStats; if( Sys::fal_stats( modPath.get(), famStats ) ) { // we have already a fal that has been transferred or will be transferred later, // so wait for that. continue; // otherwise, go on transferring the source. } // same for ftd modPath.setExtension( "ftd" ); if( Sys::fal_stats( modPath.get(), famStats ) ) { continue; } } else if( fname.endsWith( DllLoader::dllExt() ) ) { //Transfer DLLS as they are. } // source? else if( fname.endsWith( ".fal" ) || fname.endsWith(".ftd") ) { // go on, transfer the source. } else { // we don't know how to manage other plugins continue; } // copy our options, so that transferModule doesn't pollute them Options options( options_main ); options.m_sTargetDir = outputPath.get(); // ok, transfer the thing modPath.setFilename( fname ); transferModules( options, modPath.get() ); } entry->close(); delete entry; } else { // copy our options, so that transferModule doesn't pollute them Options options( options_main ); options.m_sTargetDir = outputPath.get(); transferModules( options, modPath.get() ); } } bool storeModule( Options& options, Module* mod ) { // this is the base path for the module Path modPath( mod->path() ); Path tgtPath; tgtPath.setFullLocation( options.m_sTargetDir ); // normalize module path while( modPath.getFullLocation().startsWith("./") ) { modPath.setFullLocation( modPath.getFullLocation().subString(2) ); } message( String("Processing module ").A( modPath.get() ) ); // strip the main script path from the module path. String modloc = modPath.getFullLocation(); if ( modloc.find( options.m_sMainScriptPath ) == 0 ) { // The thing came from below the main script. modloc = modloc.subString(options.m_sMainScriptPath.length() ); if ( modloc != "" && modloc.getCharAt(0) == '/' ) { modloc = modloc.subString(1); } tgtPath.setFullLocation( tgtPath.get() + "/" + modloc ); } else { // if it's coming from somewhere else in the loadpath hierarcy, // we must store it below the topmost dir. tgtPath.setFullLocation( tgtPath.get() + "/" + options.m_sSystemRoot ); // Find the path in LoadPath that caused this module to load, // strip it away and reproduce it below the SystemRoot. // For example, strip /usr/lib/falcon/ from system scripts. std::vector paths; splitPaths( options.m_sLoadPath, paths ); for( uint32 i = 0; i < paths.size(); ++i ) { if( modloc.startsWith( paths[i] ) ) { String sSysPath = modloc.subString( paths[i].size() + 1 ); if( sSysPath != "" ) { tgtPath.setFullLocation( tgtPath.get() + "/" + sSysPath ); } break; } } } // store it int fsStatus; if ( ! Sys::fal_mkdir( tgtPath.getFullLocation(), fsStatus, true ) ) { error( String("Can't create ") + tgtPath.getFullLocation() ); return false; } tgtPath.setFilename( modPath.getFilename() ); // should we store just sources, just fam or both? if( modPath.getExtension() != "fam" && modPath.getExtension() != DllLoader::dllExt() ) { // it's a source file. if ( ! options.m_bStripSources ) { if( ! copyFile( modPath.get(), tgtPath.get() ) ) { error( String("Can't copy \"") + modPath.get() + "\" into \"" + tgtPath.get() + "\"" ); return false; } } // should we save the fam? if( options.m_bStripSources || options.m_bPackFam ) { tgtPath.setExtension("fam"); FileStream famFile; if ( ! famFile.create( tgtPath.get(), (Falcon::BaseFileStream::t_attributes) 0644 ) || ! mod->save(&famFile) ) { error( "Can't create \"" + tgtPath.get() + "\"" ); return false; } famFile.flush(); famFile.close(); } } else { // just blindly copy everything else. if( ! copyFile( modPath.get(), tgtPath.get() ) ) { error( "Can't copy \"" + modPath.get() + "\" into \"" + tgtPath.get() + "\"" ); return false; } } // now copy .ftr files, if any. modPath.setExtension( "ftr" ); FileStat ftrStat; if ( Sys::fal_stats( modPath.get(), ftrStat ) ) { message( "Copying translation file " + modPath.get() ); tgtPath.setExtension( "ftr" ); // just blindly copy everything else. if( ! copyFile( modPath.get(), tgtPath.get() ) ) { warning( "Can't copy \"" + modPath.get() + "\" into \"" + tgtPath.get() + "\"\n" ); } } // Should we store .ftt as well? if ( ! options.m_bStripSources ) { copyFtr( modPath, tgtPath ); } // and now, the resources. std::vector reslist; if( getAttribute( mod, "resources", reslist ) ) { for ( uint32 i = 0; i < reslist.size(); ++i ) { copyResource( options, reslist[i], modPath, tgtPath ); } } // and finally, the dynamic libraries associated with this module. std::vector dynliblist; if( getAttribute( mod, "dynlib", dynliblist ) ) { copyDynlibs( options, mod->path(), dynliblist ); } return true; } bool transferModules( Options &options, const String& mainScript ) { ModuleLoader ml(""); ml.alwaysRecomp( true ); ml.saveModules( false ); if( options.m_sEncoding != "" ) ml.sourceEncoding( options.m_sEncoding ); // prepare the load path. if( options.m_sLoadPath != "" ) { ml.addSearchPath( options.m_sLoadPath ); } else { ml.addFalconPath(); } // add script path (always) Path scriptPath( mainScript ); if( scriptPath.getFullLocation() != "" ) ml.addSearchPath( scriptPath.getFullLocation() ); // and communicate to the rest of the program the search path we used. options.m_sLoadPath = ml.getSearchPath(); message( "Falcon load path set to: " + options.m_sLoadPath ); // Load the main script alone; It's useful for two reasons: // 1) if the main script fails to laod, we can issue a more precise error // 2) using the path() from the main script we have a normalized representation // of the topmost directory where the other sub-modules will be found. // Note -- throws on error; for now, we don't use the ability to signal // a different error. Module* mainMod = ml.loadFile( mainScript ); // update the script path with the normalized one. options.m_sMainScript = mainMod->path(); options.m_sMainScriptPath = Path(mainMod->path()).getFullLocation(); message( "Normalized main script path: " + options.m_sMainScriptPath ); // load the rest of the application. Runtime rt( &ml ); rt.addModule( mainMod ); const ModuleVector* mv = rt.moduleVector(); for( uint32 i = 0; i < mv->size(); i ++ ) { Module *mod = mv->moduleAt(i); if( options.isBlackListed(mod->name()) ) { message( "Skipping module \"" + mod->name() + "\" because it's blacklisted" ); continue; } if( options.m_bNoSysFile && options.isSysModule(mod->name()) ) { message( "Skipping module \"" + mod->name() + "\" because it's a system module" ); continue; } if( ! storeModule( options, mod ) ) return false; // add the plugins. std::vector reslist; if( getAttribute( mod, "plugins", reslist ) ) { for ( uint32 i = 0; i < reslist.size(); ++i ) { addPlugins( options, mod->path(), reslist[i] ); } } } return true; } int main( int argc, char *argv[] ) { Falcon::GetSystemEncoding( io_encoding ); if ( io_encoding != "" ) { Transcoder *trans = TranscoderFactory( io_encoding, 0, true ); if ( trans == 0 ) { stdOut = new StdOutStream(); stdOut->writeString( "Unrecognized system encoding '" + io_encoding + "'; falling back to C.\n\n" ); stdOut->flush(); stdErr = new StdErrStream; } else { stdOut = AddSystemEOL( TranscoderFactory( io_encoding, new StdOutStream, true ), true ); stdErr = AddSystemEOL( TranscoderFactory( io_encoding, new StdErrStream, true ), true ); } } Options options; if ( ! options.parse( argc-1, argv+1 ) ) { stdOut->writeString( "Fatal: invalid parameters.\nUse -h for help.\n\n" ); return 1; } if( options.m_bVersion ) { version(); } if( options.m_bHelp ) { usage(); } setVerbose( options.m_bVerbose ); if ( ! options.m_sMainScript ) { stdOut->writeString( "falpack: Nothing to do.\n\n" ); return 0; } Engine::Init(); // by default store the application in a subdirectory equal to the name of the // application. Path target( options.m_sMainScript ); if( options.m_sTargetDir == "" ) { options.m_sTargetDir = target.getFile(); } //=============================================================== // We need a runtime and a module loader to load all the modules. bool bResult; try { bResult = transferModules( options, options.m_sMainScript ); if ( bResult ) { bResult = transferSysFiles( options, options.m_bNoSysFile ); } } catch( Error* err ) { // We had a compile time problem, very probably bResult = false; error( String( "Compilation error.\n" ) + err->toString() ); err->decref(); } delete stdOut; delete stdErr; Engine::Shutdown(); return bResult ? 0 : 1; } /* end of falpack.cpp */ clt/falpack/falpack.rc000066400000000000000000000023741176363201700152040ustar00rootroot00000000000000///////////////////////////////////////////////////////////////////////////// // // Icon // // TODO: create an icon //MAINICON ICON DISCARDABLE "resources\falcon-ico-base.ico" ///////////////////////////////////////////////////////////////////////////// // // Version // // for project wide versioning... #include #include VS_VERSION_INFO VERSIONINFO FILEVERSION FALCON_VERSION_RCINFO_N PRODUCTVERSION FALCON_VERSION_RCINFO_N FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x4L FILETYPE 0x0L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "041004b0" BEGIN VALUE "CompanyName", "Falcon Committee" VALUE "FileDescription", "The Falcon application packager" VALUE "FileVersion", FALCON_VERSION_RCINFO VALUE "InternalName", "falpack" VALUE "LegalCopyright", "The Falcon Programming Language License" VALUE "OriginalFilename", "falpack.exe" VALUE "ProductName", "The Falcon Programming Language" VALUE "ProductVersion", FALCON_VERSION_RCINFO END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x410, 1200 END END clt/falpack/falpack_sys.h000066400000000000000000000014061176363201700157200ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falpack_sys.h System specific extensions for Falcon ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 31 Jan 2010 11:29:07 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALPACK_SYS_H_ #define FALPACK_SYS_H_ #include #include namespace Falcon { class Options; bool transferSysFiles( Options &options, bool bJustScript ); bool copyDynlibs( Options& options, const String& modpath, const std::vector& dynlibs ); } #endif /* end of falpack_sys.h */ clt/falpack/falpack_sys_unix.cpp000066400000000000000000000130531176363201700173170ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falpack_sys_unix.cpp System specific extensions for Falcon ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 31 Jan 2010 11:29:07 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include "falpack_sys.h" #include "options.h" #include "utils.h" #include namespace Falcon { bool transferSysFiles( Options &options, bool bJustScript ) { Path binpath, libpath; binpath.setLocation( options.m_sFalconBinDir != "" ? options.m_sFalconBinDir : FALCON_DEFAULT_BIN ); binpath.setFilename( "falcon" ); Path runnerPath( options.m_sRunner ); if( runnerPath.get() != "" && runnerPath.isValid() ) { binpath.setFilename( runnerPath.getFilename() ); } libpath.setLocation( options.m_sFalconLibDir != "" ? options.m_sFalconLibDir : FALCON_DEFAULT_LIB ); if ( ! bJustScript ) { Path tgtpath( options.m_sTargetDir + "/" + options.m_sSystemRoot +"/" ); libpath.setFile( "libfalcon_engine" ); libpath.setExtension( DllLoader::dllExt() ); tgtpath.setFilename( binpath.getFilename() ); if( ! copyFile( binpath.get(), tgtpath.get() ) ) { warning( "Can't copy system file \"" + binpath.get() + "\" into target path \""+ tgtpath.get()+ "\"" ); // but continue } Sys::fal_chmod( tgtpath.get(), 0755 ); tgtpath.setFilename( libpath.getFilename() ); if( ! copyFile( libpath.get(), tgtpath.get() ) ) { warning( "Can't copy system file \"" + libpath.get() + "\" into target path \""+ tgtpath.get()+ "\"" ); // but continue } } // now create the startup script Path mainScriptPath( options.m_sMainScript ); Path scriptPath( options.m_sTargetDir + "/" + mainScriptPath.getFile() + ".sh" ); message( "Creating startup script \"" + scriptPath.get() + "\"" ); FileStream startScript; if( ! startScript.create( scriptPath.get(), (BaseFileStream::t_attributes) 0755 ) ) { error( "Can't create startup script \"" + scriptPath.get() + "\"" ); return false; } startScript.writeString( "#!/bin/sh\n" ); startScript.writeString( "CURDIR=`dirname $0`\n" ); startScript.writeString( "cd \"$CURDIR\"\n" ); if( bJustScript ) { if ( runnerPath.isAbsolute() ) startScript.writeString( " "+ runnerPath.get() + " \\\n" ); else startScript.writeString( " "+ binpath.getFilename() + " \\\n" ); } else { startScript.writeString( "LD_LIBRARY_PATH=\"" + options.m_sSystemRoot + "\" \\\n" ); startScript.writeString( " \""+options.m_sSystemRoot + "/" + binpath.getFilename() + "\" \\\n" ); startScript.writeString( " -L \"" + options.m_sSystemRoot +";.\" \\\n" ); } // we need to discard the extension, so that the runner decides how to run the program. Path scriptName( options.m_sMainScript ); startScript.writeString( " \"" + scriptName.getFile() +"\" $*" ); startScript.flush(); return true; } bool copyDynlibs( Options& options, const String& modpath, const std::vector& dynlibs ) { // On unix, the thing is a bit more complex. // We need to find the right library via ldd and copy // it. AutoCString command( "ldd " + modpath ); FILE* ldin = popen( command.c_str(), "r" ); if( ldin == NULL ) { warning( "Cannot copy required dynlibs for " + modpath + " (Cannot start ldd)" ); return false; } char buffer[4096]; String source; // parallel vector uint32 size = dynlibs.size(); // file copy destination is always the root of the system path // where we set the LD_LIBRARY_PATH of the startup script. Path targetPath; targetPath.setFullLocation( options.m_sTargetDir + "/" + options.m_sSystemRoot ); // find our module // we must read entirely LDD output, or we may fail to close it via pclose. while ( fgets( buffer, 4096, ldin ) ) { // still something to be found? if( size > 0 ) { String line( buffer ); // search all the libs for ( uint32 i = 0; i < dynlibs.size(); ++i ) { if( line.wildcardMatch( "*" + dynlibs[i] + "*."+DllLoader::dllExt() + "*") ) { bool done = false; uint32 pos = line.find( "=>" ); uint32 pos1 = line.find( "(", pos ); if( pos != String::npos && pos1 != String::npos ) { String srcLib = line.subString( pos+2, pos1 ); srcLib.trim(); if( srcLib != "" ) { Path sourceLib( srcLib ); targetPath.setFilename( sourceLib.getFilename() ); done = copyFile( srcLib, targetPath.get() ); } } if( ! done ) { warning( "Cannot extract possible plugin from ldd line " + line ); } size--; } } } // do we have a ?name[-].*. pattern in the source list? } if ( pclose(ldin) == -1 ) { warning( "Can't close ldd process for module " + modpath ); } if( size > 0 ) { warning( "Can't resolve all the dynlibs for module " + modpath ); } return true; } } /* end of falpack_sys_unix.cpp */ clt/falpack/falpack_sys_win.cpp000066400000000000000000000143231176363201700171320ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falpack_sys_win.cpp System specific extensions for Falcon ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 31 Jan 2010 11:29:07 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include "falpack_sys.h" #include "options.h" #include "utils.h" namespace Falcon { bool copyRuntime( const Path& binpath, const Path& tgtpath ) { message( "Searching VSx CRT in " + binpath.getFullLocation() ); // open the binary path in search of "Microsoft.*.CRT" VFSProvider* provider = Engine::getVFS( "file" ); fassert( provider != 0 ); DirEntry* dir = provider->openDir( binpath.getFullLocation() ); if( dir == 0 ) { warning( "Can't search CRT in " + binpath.getFullLocation() ); return false; } String fname; while( dir->read(fname) ) { if( fname.wildcardMatch("Microsoft.*.CRT") ) { // we're done with dir. delete dir; Path source( binpath.getFullLocation() + "/" + fname + "/"); Path target( tgtpath.getFullLocation() + "/" + fname + "/"); // first, create the target path int32 fsStatus; if( ! Sys::fal_mkdir( target.getFullLocation(), fsStatus, true ) ) { warning( "Can't create CRT directory in " + target.getFullLocation() ); return false; } // then copy everything inside it. DirEntry* crtdir = provider->openDir( source.getFullLocation() ); if( crtdir == 0 ) { warning( "Can't read source CRT directory " + source.getFullLocation() ); return false; } //loop copying everything that's not a dir. String sFile; while( crtdir->read( sFile ) ) { if( sFile.startsWith(".") ) continue; source.setFilename( sFile ); target.setFilename( sFile ); if ( ! copyFile( source.get(), target.get() ) ) { delete crtdir; warning( "Can't copy CRT file " + source.get() + " into " + target.get() ); return false; } } return true; } } delete dir; return false; } bool transferSysFiles( Options &options, bool bJustScript ) { Path binpath, libpath; // Under windows, the binary path is usually stored in an envvar. String envpath; if ( ! Sys::_getEnv( "FALCON_BIN_PATH", envpath ) || envpath == "" ) envpath = FALCON_DEFAULT_BIN; binpath.setFullLocation( options.m_sFalconBinDir != "" ? options.m_sFalconBinDir: envpath ); // copy falcon or falrun if ( options.m_sRunner != "" ) binpath.setFilename( options.m_sRunner ); else binpath.setFilename( "falcon.exe" ); // our dlls are in bin, under windows. libpath.setFullLocation( options.m_sFalconLibDir != "" ? options.m_sFalconLibDir : envpath ); if ( ! bJustScript ) { Path tgtpath( options.m_sTargetDir + "/" + options.m_sSystemRoot +"/" ); libpath.setFile( "falcon_engine" ); libpath.setExtension( DllLoader::dllExt() ); tgtpath.setFilename( binpath.getFilename() ); if( ! copyFile( binpath.get(), tgtpath.get() ) ) { warning( "Can't copy system file \"" + binpath.get() + "\" into target path \""+ tgtpath.get()+ "\"" ); // but continue } tgtpath.setFilename( libpath.getFilename() ); if( ! copyFile( libpath.get(), tgtpath.get() ) ) { warning( "Can't copy system file \"" + libpath.get() + "\" into target path \""+ tgtpath.get()+ "\"" ); // but continue } // and now the visual C runtime, if any copyRuntime( binpath, tgtpath ); } // now create the startup script Path mainScriptPath( options.m_sMainScript ); Path scriptPath( options.m_sTargetDir + "/" + mainScriptPath.getFile() + ".bat" ); message( "Creating startup script \"" + scriptPath.get() + "\"" ); FileStream startScript; if( ! startScript.create( scriptPath.get(), (BaseFileStream::t_attributes) 0777 ) ) { error( "Can't create startup script \"" + scriptPath.get() + "\"" ); return false; } startScript.writeString( "@ECHO OFF\r\n" "set OLD_DIR=%CD%\r\n" "set target_dir=%~dp0\r\n" "cd %target_dir%\r\n"); if( bJustScript ) { startScript.writeString( "\"" + binpath.getFilename() + "\" " ); } else { startScript.writeString( " \""+options.m_sSystemRoot + "\\" + binpath.getFilename() + "\" " ); startScript.writeString( " -L \"" + options.m_sSystemRoot +";.\" " ); } // we need to discard the extension, so that the runner decides how to run the program. Path scriptName( options.m_sMainScript ); startScript.writeString( " \"" + scriptName.getFile() +"\" \"%*\"\r\n" ); startScript.writeString( "cd %OLD_DIR%\r\n" ); startScript.flush(); return true; } bool copyDynlibs( Options& options, const String& mp, const std::vector& dynlibs ) { // On windows, the thing is simple. // We must add the module named as .dll besides the host module if it's binary, // or in the Falcon system dir if it's a source module. Path modpath( mp ); Path targetPath; String ext = modpath.getExtension(); ext.lower(); // binary module ? if( ext == DllLoader::dllExt() ) targetPath.setFullLocation( options.m_sTargetDir + "/" + options.m_sSystemRoot ); else targetPath = modpath; bool retval = true; for ( uint32 i = 0; i < dynlibs.size(); ++i ) { modpath.setFilename( dynlibs[i] + "." + DllLoader::dllExt() ); targetPath.setFilename( dynlibs[i] + "." + DllLoader::dllExt() ); if( ! copyFile( modpath.get(), targetPath.get() ) ) { warning( "Cannot copy dynlib resource " + modpath.get() ); retval = false; } } return retval; } } /* end of falpack_sys_unix.cpp */ clt/falpack/options.cpp000066400000000000000000000073771176363201700154640ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: options.cpp Options for falcon packager - implementation. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 30 Jan 2010 12:42:48 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "options.h" #include "utils.h" #include namespace Falcon { Options::Options(): m_bPackFam( false ), m_bStripSources( false ), m_bNoSysFile( false ), m_sSystemRoot( "_system" ), m_bHelp( false ), m_bVersion( false ), m_bVerbose( false ), m_bIsValid( true ) { m_sysModules.insert( "compiler" ); m_sysModules.insert( "funcext" ); m_sysModules.insert( "confparser" ); m_sysModules.insert( "json" ); m_sysModules.insert( "logger" ); m_sysModules.insert( "mxml" ); m_sysModules.insert( "regex" ); m_sysModules.insert( "process" ); m_sysModules.insert( "socket" ); m_sysModules.insert( "threading" ); m_sysModules.insert( "zlib" ); } bool Options::parse( int argc, char* const argv[] ) { int p = 0; String* getMe = 0; String blackListItem; while( p < argc ) { const char* word = argv[ p ]; if ( word == 0 ) { // !?!?!? Malformed argv? m_bIsValid = false; return false; } if ( getMe != 0 ) { getMe->bufferize( word ); if( &blackListItem == getMe ) { m_blackList.insert( blackListItem ); } getMe = 0; } else if( word[0] == '-' ) { switch( word[1] ) { case 'M': m_bPackFam = true; break; case 's': m_bStripSources = true; break; case 'S': m_bNoSysFile = true; break; case '?': case 'h': m_bHelp = true; break; case 'v': m_bVersion = true; break; case 'V': m_bVerbose = true; break; case 'b': getMe = &blackListItem; break; case 'e': getMe = &m_sEncoding; break; case 'P': getMe = &m_sTargetDir; break; case 'L': getMe = &m_sLoadPath; break; case 'r': getMe = &m_sRunner; break; case 'R': getMe = &m_sSystemRoot; break; case '-': if( strcmp( word + 2, "bin" ) == 0 ) { getMe = &m_sFalconBinDir; break; } else if( strcmp( word + 2, "lib" ) == 0 ) { getMe = &m_sFalconLibDir; break; } // else fallthrough and raise error default: error( String("Invalid option \"").A(word).A("\"") ); m_bIsValid = false; return false; } } else { if ( m_sMainScript != "" ) { // but it's not an error -- main will tell "nothing to do" and exit. m_bIsValid = false; return false; } m_sMainScript.bufferize(word); } ++p; } // do we miss the last parameter? if ( getMe != 0 ) { error( String("Option \"").A(argv[ argc-1 ]).A("\" needs a parameter.") ); m_bIsValid = false; return false; } // do we miss both sources and fams? if( m_bStripSources && m_bPackFam ) { error( String("Options -M and -s are incompatible") ); m_bIsValid = false; return false; } return true; } bool Options::isBlackListed( const String& modname ) const { return m_blackList.find( modname ) != m_blackList.end(); } bool Options::isSysModule( const String& modname ) const { return m_sysModules.find( modname ) != m_sysModules.end(); } } /* end of options.cpp */ clt/falpack/options.h000066400000000000000000000023611176363201700151150ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: options.h Options for Falcon packager ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 30 Jan 2010 12:42:48 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include namespace Falcon { class Options { public: bool m_bPackFam; bool m_bStripSources; bool m_bNoSysFile; String m_sRunner; String m_sTargetDir; String m_sLoadPath; String m_sMainScript; String m_sEncoding; String m_sMainScriptPath; String m_sSystemRoot; String m_sFalconBinDir; String m_sFalconLibDir; bool m_bHelp; bool m_bVersion; bool m_bVerbose; Options(); bool parse( int argc, char* const argv[] ); bool isValid() const { return m_bIsValid; } bool isBlackListed( const String& modname ) const; bool isSysModule( const String& modname ) const; private: bool m_bIsValid; std::set m_blackList; std::set m_sysModules; }; } /* end of options.h */ clt/falpack/utils.cpp000066400000000000000000000062471176363201700151240ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: utils.cpp Utilities for Falcon packager ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 30 Jan 2010 12:42:48 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "utils.h" #include namespace Falcon { Stream* stdOut; Stream* stdErr; static void message_1( const String &msg ) { stdOut->writeString( "falpack: " ); stdOut->writeString( msg ); stdOut->writeString( "\n" ); stdOut->flush(); } // for non-verbose operations static void message_2( const String &msg ) {} void (*message)( const String &msg ); void error( const String &msg ) { stdErr->writeString( "falpack: ERROR - " ); stdErr->writeString( msg ); stdErr->writeString( "\n" ); stdErr->flush(); } void warning( const String &msg ) { stdOut->writeString( "falpack: WARN - " ); stdOut->writeString( msg ); stdOut->writeString( "\n" ); stdOut->flush(); } void setVerbose( bool mode ) { if ( mode ) message = message_1; else message = message_2; } void splitPaths( const String& path, std::vector& tgt ) { uint32 pos = 0, pos1; while( (pos1 = path.find( ";", pos )) != String::npos ) { String sRes = path.subString( pos, pos1 ); sRes.trim(); tgt.push_back( sRes ); pos = pos1+1; } String sRes = path.subString( pos ); sRes.trim(); tgt.push_back( sRes ); } bool copyFile( const String& source, const String& dest ) { message( String("Copying ").A( source ).A(" => ").A( dest ) ); // NOTE: streams are closed by the destructor. FileStream instream, outstream; instream.open( source, ::Falcon::BaseFileStream::e_omReadOnly ); if ( ! instream.good() ) { return false; } outstream.create( dest, (Falcon::BaseFileStream::t_attributes) 0644 ); if ( ! outstream.good() ) { return false; } byte buffer[8192]; int count = 0; while( ( count = instream.read( buffer, 8192) ) > 0 ) { if ( outstream.write( buffer, count ) < 0 ) { return false; } } return true; } bool getAttribute( Module* mod, const String& name, String& result ) { AttribMap* attributes = mod->attributes(); VarDef* resources; if( attributes != 0 && (resources = attributes->findAttrib( name ) ) != 0 ) { message( "Adding plugins for module " + mod->path() ); if( resources != 0 ) { if ( ! resources->isString() || resources->asString()->size() == 0 ) { warning( "Module \"" + mod->path() + " has an invalid \""+name+"\" attribute.\n" ); } else { result.bufferize( *resources->asString() ); return true; } } } return false; } bool getAttribute( Module* mod, const String& name, std::vector& result_list ) { String res; if( ! getAttribute( mod, name, res ) ) return false; splitPaths( res, result_list ); return true; } } /* end of utils.cpp */ clt/falpack/utils.h000066400000000000000000000020021176363201700145520ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: utils.h Utilities for Falcon packager ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 30 Jan 2010 12:42:48 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include namespace Falcon { extern Stream* stdOut; extern Stream* stdErr; extern void (*message)( const String &msg ); void error( const String &msg ); void warning( const String &msg ); void setVerbose( bool mode ); void splitPaths( const String& path, std::vector& tgt ); bool copyFile( const String& source, const String& dest ); bool getAttribute( Module* mod, const String& name, String& result ); bool getAttribute( Module* mod, const String& name, std::vector& result_list ); } /* end of utils.h */ clt/falrun/000077500000000000000000000000001176363201700131355ustar00rootroot00000000000000clt/falrun/CMakeLists.txt000066400000000000000000000007611176363201700157010ustar00rootroot00000000000000################################################## # Falcon Programming Language # # Falrun ################################################## include_directories( ${PROJECT_BINARY_DIR}/include ${CMAKE_CURRENT_SOURCE_DIRECTORY} ${PROJECT_SOURCE_DIR}/include ) IF(WIN32) SET(SYS_RC falrun.rc) ENDIF(WIN32) # Target ADD_EXECUTABLE( falrun falrun.cpp ${SYS_RC} ) #Link TARGET_LINK_LIBRARIES(falrun falcon_engine) #Install INSTALL( TARGETS falrun ${FALCON_INSTALL_DESTINATIONS} ) clt/falrun/falrun.cpp000066400000000000000000000212261176363201700151330ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falrun.cpp A simple program that uses Falcon VM to execute falcon compiled codes. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom ago 8 20:30:13 CEST 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Falcon; List preloaded; String load_path; String module_language; Stream *stdIn; Stream *stdOut; Stream *stdErr; String io_encoding; bool ignore_defpath = false; static void version() { stdOut->writeString( "FALCON runtime virtual machine.\n" ); stdOut->writeString( "Version " ); stdOut->writeString( FALCON_VERSION " (" FALCON_VERSION_NAME ")" ); stdOut->writeString( "\n" ); stdOut->flush(); } static void usage() { stdOut->writeString( "Usage: falrun [options] file.fam [script options]\n" ); stdOut->writeString( "\n" ); stdOut->writeString( "Options:\n" ); stdOut->writeString( " -e select default encoding for VM streams\n" ); stdOut->writeString( " -h this help\n" ); stdOut->writeString( " -p mod pump one or more module in the virtual machine\n" ); stdOut->writeString( " -l Set preferential language of loaded modules\n" ); stdOut->writeString( " -L set path for 'load' directive\n" ); stdOut->writeString( " -P Ignore default system paths\n" ); stdOut->writeString( " -v print copyright notice and version and exit\n" ); stdOut->writeString( "\n" ); stdOut->writeString( "Paths must be in falcon file name format: directory separatros must be slashes and\n" ); stdOut->writeString( "multiple entries must be entered separed by a semicomma (';')\n" ); stdOut->writeString( "\n" ); stdOut->flush(); } void findModuleName( const String &filename, String &name ) { uint32 pos = filename.rfind( "/" ); if ( pos == csh::npos ) { name = filename; } else { name = filename.subString( pos + 1 ); } // find the dot pos = name.rfind( "." ); if ( pos != csh::npos ) { name = name.subString( 0, pos ); } } void findModulepath( const String &filename, String &path ) { uint32 pos = filename.rfind( "/" ); if ( pos != csh::npos ) { path = filename.subString( 0, pos ); } } String get_load_path() { String envpath; bool hasEnvPath = Sys::_getEnv ( "FALCON_LOAD_PATH", envpath ); String lp; if ( load_path.size() > 0 ) { if ( ignore_defpath ) lp = load_path; else lp = FALCON_DEFAULT_LOAD_PATH ";" + load_path; } if ( ! hasEnvPath || ignore_defpath ) { return lp; } else { if ( lp == "" ) return envpath; else return lp + ";" + envpath; } return lp; // just a warning } String get_io_encoding() { if ( io_encoding != "" ) return io_encoding; String ret; if ( Sys::_getEnv("FALCON_VM_ENCODING", ret) ) return ret; return ""; } int main( int argc, char *argv[] ) { // Install a void ctrl-c handler (let ctrl-c to kill this app) Sys::_dummy_ctrl_c_handler(); Falcon::Engine::AutoInit autoInit; int script_pos = argc; char *input_file = 0; FileStream *bincode_stream; stdOut = stdOutputStream(); stdErr = stdErrorStream(); stdIn = stdInputStream(); // option decoding for ( int i = 1; i < argc; i++ ) { char *op = argv[i]; if (op[0] == '-' ) { switch ( op[1] ) { case 'e': if ( op[2] == 0 && i < argc + 1) { io_encoding = argv[++i]; } else { io_encoding = op + 2; } break; case 'h': usage(); return 0; case 'L': if ( op[2] == 0 && i < argc + 1) load_path = argv[++i]; else load_path = op + 2; break; break; case 'P': ignore_defpath = true; break; case 'l': if ( op[2] == 0 && i + 1 < argc ) module_language = argv[++i]; else module_language = op + 2; break; case 'p': if ( op[2] == 0 && i < argc + 1) preloaded.pushBack( argv[++i] ); else preloaded.pushBack( op + 2 ); break; case 'v': version(); return 0; default: stdOut->writeString( "falrun: unrecognized option '" ); stdOut->writeString( op ); stdOut->writeString( "'.\n\n" ); usage(); return 1; } } else { input_file = op; script_pos = i+1; break; } } // eventually change the encodings. io_encoding = get_io_encoding(); if ( io_encoding != "" ) { Transcoder *trans = TranscoderFactory( io_encoding, 0, true ); if ( trans == 0 ) { stdOut->writeString( "Fatal: unrecognized encoding '" + io_encoding + "'.\n\n" ); return 1; } delete stdIn ; delete stdOut; delete stdErr; trans->setUnderlying( new StdInStream ); stdIn = AddSystemEOL( trans, true ); stdOut = AddSystemEOL( TranscoderFactory( io_encoding, new StdOutStream, true ), true ); stdErr = AddSystemEOL( TranscoderFactory( io_encoding, new StdErrStream, true ), true ); } if ( input_file == 0 ) { stdOut->writeString( "falrun: missing script name.\n" ); usage(); return 1; } bincode_stream = new FileStream; bincode_stream->open( input_file ); if ( ! bincode_stream->good() ) { stdOut->writeString( "falrun: Can't open file " ); stdOut->writeString( input_file ); stdOut->writeString( "\n" ); stdOut->flush(); return 1; } String module_name; String source_path; findModuleName( input_file, module_name ); findModulepath( input_file, source_path ); //----------------------------------------- // execute the script. // if ( source_path != "" ) source_path += ";"; try { ModuleLoader *modloader = new ModuleLoader( source_path + get_load_path() ); Engine::setSearchPath( modloader->getSearchPath() ); // set the module preferred language; ok also if default ("") is used modloader->setLanguage( module_language ); Module *core = core_module_init(); Module *main_mod = modloader->loadModule( bincode_stream ); VMachine *vmachine = new VMachine(false); // change default machine streams. vmachine->stdIn( stdIn ); vmachine->stdOut( stdOut ); vmachine->stdErr( stdErr ); vmachine->init(); vmachine->link( core ); core->decref(); Runtime *runtime = new Runtime( modloader ); // preload required modules ListElement *pliter = preloaded.begin(); while( pliter != 0 ) { Module *module = modloader->loadName( * ((String *) pliter->data()) ); runtime->addModule( module ); pliter = pliter->next(); } Item *item_args = vmachine->findGlobalItem( "args" ); fassert( item_args != 0 ); CoreArray *args = new CoreArray( argc - script_pos ); for ( int ap = script_pos; ap < argc; ap ++ ) { args->append( new CoreString( argv[ap] ) ); } item_args->setArray( args ); Item *script_name = vmachine->findGlobalItem( "scriptName" ); fassert( script_name != 0 ); script_name->setString( new CoreString( module_name ) ); // the runtime will try to load the references. runtime->addModule( main_mod ); if( vmachine->link( runtime ) ) { vmachine->launch(); if ( vmachine->regA().type() == FLC_ITEM_INT ) return (int32) vmachine->regA().asInteger(); return 0; } vmachine->finalize(); } catch ( Error *err ) { String temp; err->toString( temp ); stdErr->writeString( "falcon: FATAL - Program terminated with error.\n" ); stdErr->writeString( temp + "\n" ); err->decref(); return 1; } return 255; } /* end of falrun.cpp */ clt/falrun/falrun.rc000066400000000000000000000023771176363201700147630ustar00rootroot00000000000000///////////////////////////////////////////////////////////////////////////// // // Icon // // TODO: create an icon //MAINICON ICON DISCARDABLE "resources\falcon-ico-base.ico" ///////////////////////////////////////////////////////////////////////////// // // Version // // for project wide versioning... #include #include VS_VERSION_INFO VERSIONINFO FILEVERSION FALCON_VERSION_RCINFO_N PRODUCTVERSION FALCON_VERSION_RCINFO_N FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x4L FILETYPE 0x0L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "041004b0" BEGIN VALUE "CompanyName", "Falcon Committee" VALUE "FileDescription", "The Falcon command line module runner" VALUE "FileVersion", FALCON_VERSION_RCINFO VALUE "InternalName", "falrun" VALUE "LegalCopyright", "The Falcon Programming Language License" VALUE "OriginalFilename", "falrun.exe" VALUE "ProductName", "The Falcon Programming Language" VALUE "ProductVersion", FALCON_VERSION_RCINFO END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x410, 1200 END END clt/faltest/000077500000000000000000000000001176363201700133105ustar00rootroot00000000000000clt/faltest/CMakeLists.txt000066400000000000000000000021161176363201700160500ustar00rootroot00000000000000################################################## # Falcon Programming Language # # Faltest ################################################## include_directories( ${PROJECT_BINARY_DIR}/include ${CMAKE_CURRENT_SOURCE_DIRECTORY} ${PROJECT_SOURCE_DIR}/include ) IF(WIN32) SET(SYS_RC faltest.rc) SET(SYS_RC_DLL testsuite.rc) ENDIF(WIN32) # Target ADD_EXECUTABLE( faltest faltest.cpp fal_testsuite.cpp scriptdata.cpp ${SYS_RC} ) ADD_LIBRARY( testsuite_fm MODULE fal_testsuite.cpp ${SYS_RC_DLL} ) #Link TARGET_LINK_LIBRARIES(faltest falcon_engine) TARGET_LINK_LIBRARIES(testsuite_fm falcon_engine) #faltest embeds the testsuite module SET_TARGET_PROPERTIES( faltest PROPERTIES COMPILE_FLAGS "-DFALCON_EMBED_MODULES") #while the testsuite has no prefix IF(APPLE) SET_TARGET_PROPERTIES( testsuite_fm PROPERTIES PREFIX "" SUFFIX ".dylib" ) ELSE(APPLE) SET_TARGET_PROPERTIES( testsuite_fm PROPERTIES PREFIX "") ENDIF(APPLE) #Install INSTALL( TARGETS faltest ${FALCON_INSTALL_DESTINATIONS}) INSTALL( TARGETS testsuite_fm DESTINATION ${FALCON_MOD_DIR} ) clt/faltest/fal_testsuite.cpp000066400000000000000000000102101176363201700166610ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: fal_testsuite.cpp Simple Falcon module for test suite. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun feb 13 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Simple Falcon module for test suite. */ #include #include #include #include #include #include #include #include /************************************************ Success and failure falcon functions *************************************************/ static Falcon::String s_lastFailure; static bool s_testStatus; static Falcon::String s_testPrefix; static Falcon::numeric s_totalTime; static Falcon::numeric s_totalOps; static Falcon::int64 s_timeFactor; namespace Falcon { namespace TestSuite { bool getSuccess() { return ::s_testStatus; } void setSuccess( bool status ) { ::s_testStatus = status; } const ::Falcon::String &getFailureReason() { return ::s_lastFailure; } void setTestName( const ::Falcon::String &prefix ) { ::s_testPrefix = prefix; ::s_testPrefix.reserve(0); } void getTimings( ::Falcon::numeric &tot_t, ::Falcon::numeric &tot_ops ) { tot_t = ::s_totalTime; tot_ops = ::s_totalOps; ::s_totalTime = 0.0; ::s_totalOps = 0.0; } ::Falcon::int64 getTimeFactor() { return ::s_timeFactor; } void setTimeFactor( ::Falcon::int64 ts) { ::s_timeFactor = ts; } } } FALCON_FUNC flc_testrelect ( ::Falcon::VMachine *vm ) { if ( vm->paramCount() >= 1 ) vm->retval( *vm->param( 0 ) ); else vm->retnil(); } FALCON_FUNC flc_success ( ::Falcon::VMachine *vm ) { s_testStatus = true; throw Falcon::VMEventQuit(); } FALCON_FUNC flc_failure ( ::Falcon::VMachine *vm ) { s_testStatus = false; if ( vm->paramCount() > 0 && vm->param( 0 )->isString() ) { s_lastFailure = *vm->param(0)->asString(); } else s_lastFailure = ""; throw Falcon::VMEventQuit(); } FALCON_FUNC flc_alive ( ::Falcon::VMachine *vm ) { static int pos = 0; static const char *frullo = { "-\\|/" }; ::Falcon::Stream &out = *vm->stdOut(); ::Falcon::int64 percent = 0; if ( vm->paramCount() > 0 && vm->param( 0 )->isOrdinal() ) { percent = vm->param(0)->forceInteger(); if( percent > 100 ) { percent = 100; } else if ( percent < 0 ) { percent = 0; } out.writeString( "\r" + ::s_testPrefix + ": " ); Falcon::String temp = "["; temp.append( frullo[pos++] ); temp += "]"; temp.writeNumber( percent ); temp += " "; out.writeString( temp ); } else { out.writeString( "\r" ); Falcon::String temp = "["; temp.append( frullo[pos++] ); temp += "]"; temp.writeNumber( percent ); temp += " "; out.writeString( temp ); } out.writeString( "\r" + ::s_testPrefix + ": " ); out.flush(); if ( pos == 4 ) pos = 0; } FALCON_FUNC flc_timings ( ::Falcon::VMachine *vm ) { ::Falcon::Item *i_totalTime = vm->param(0); ::Falcon::Item *i_totalOps = vm->param(1); ::s_totalOps = i_totalOps->forceNumeric(); ::s_totalTime = i_totalTime->forceNumeric(); } FALCON_FUNC flc_timeFactor( ::Falcon::VMachine *vm ) { vm->retval( ::s_timeFactor ); } #ifdef FALCON_EMBED_MODULES Falcon::Module *init_testsuite_module() { #else FALCON_MODULE_DECL { #endif s_timeFactor = 1; Falcon::Module *tsuite = new Falcon::Module(); tsuite->name( "falcon.testsuite" ); tsuite->engineVersion( FALCON_VERSION_NUM ); tsuite->version( FALCON_VERSION_NUM ); tsuite->addExtFunc( "failure", flc_failure ); tsuite->addExtFunc( "success", flc_success ); tsuite->addExtFunc( "testReflect", flc_testrelect ); tsuite->addExtFunc( "alive", flc_alive ); tsuite->addExtFunc( "timings", flc_timings ); tsuite->addExtFunc( "timeFactor", flc_timeFactor ); return tsuite; } /* end of fal_testsuite.cpp */ clt/faltest/faltest.cpp000066400000000000000000000750701176363201700154670ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: faltest.cpp Testsuite interpreter ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun feb 13 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file This program embeds the Falcon compiler and perform tests on all the files contained in a certain directory. It is meant to test Falcon features in case of changes and similar. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if FALCON_LITTLE_ENDIAN != 1 #include #endif #include "scriptdata.h" #define DEF_PREC 5 #define TIME_PRINT_FMT "%.3f" using namespace Falcon; /************************************************ Global data and options *************************************************/ bool opt_compmem; bool opt_compasm; bool opt_justlist; bool opt_verbose; bool opt_serialize; bool opt_timings; bool opt_inTimings; bool opt_checkmem; String opt_output; String opt_category; String opt_subcat; String opt_path; String opt_libpath; int opt_tf; Stream *output; int passedCount; int failedCount; int totalCount; bool testStatus; String failureReason; double total_time_compile; double total_time_generate; double total_time_link; double total_time_execute; double compTime; double genTime; double linkTime; double execTime; Falcon::List opt_testList; /** Main script dictionary */ t_idScriptMap scriptMap; /** Helper dictionary to help categorization */ t_categoryScriptMap categoryMap; /** Standard output stream */ Stream *stdOut; Stream *stdErr; /************************************************ Functions used to account memory management *************************************************/ static long s_allocatedMem = 0; static long s_totalMem = 0; static long s_outBlocks = 0; static long s_totalOutBlocks = 0; /************************************************ Typical utility functions for command lines *************************************************/ static void version() { stdOut->writeString( "Falcon unit test package.\n" ); stdOut->writeString( "Version " ); stdOut->writeString( FALCON_VERSION " (" FALCON_VERSION_NAME ")" ); stdOut->writeString( "\n" ); stdOut->flush(); } static void usage() { stdOut->writeString( "Usage: faltest [options] -d testsuite_directory [tests ids]\n" ); stdOut->writeString( "\n" ); stdOut->writeString( "Options:\n" ); stdOut->writeString( " -c only perform tests in this category\n" ); stdOut->writeString( " -C only perform test in this subcategory\n" ); stdOut->writeString( " -d tests are in specified directory\n" ); stdOut->writeString( " -l Just list available tests and exit\n" ); stdOut->writeString( " -L Changes Falcon load path.\n" ); stdOut->writeString( " -h/-? Show this help\n" ); stdOut->writeString( " -m do NOT compile in memory\n" ); stdOut->writeString( " -M Check for memory allocation correctness.\n" ); stdOut->writeString( " -f set time factor to N for benchmarks\n" ); stdOut->writeString( " -o Output report here (defaults stdout)\n" ); stdOut->writeString( " -s perform module serialization test\n" ); stdOut->writeString( " -S compile via assembly\n" ); stdOut->writeString( " -t record and display timings\n" ); stdOut->writeString( " -T read internal script timing() request\n" ); stdOut->writeString( " -v be verbose\n" ); stdOut->writeString( " -V print copyright notice and version and exit\n" ); stdOut->writeString( "\n" ); stdOut->writeString( "Path must be in falcon file name format: directory separatros must be slashes.\n" ); stdOut->writeString( "A list of space separated tests IDS to be performed may optionally be given.\n" ); stdOut->writeString( "\n" ); stdOut->flush(); } void parse_options( int argc, char *argv[] ) { opt_compmem = true; opt_checkmem = false; opt_compasm = false; opt_justlist = false; opt_verbose = false; opt_serialize = false; opt_timings = false; opt_inTimings = false; opt_tf = 1; // option decoding for ( int i = 1; i < argc; i++ ) { char *op = argv[i]; if (op[0] == '-' ) { switch ( op[1] ) { case 'c': if( op[2] != 0 ) opt_category = op + 2; else if ( i < argc - 1 ) { i++; opt_category = argv[i]; } else { stdOut->writeString( "Must specify an argument for -c\n" ); usage(); exit(1); } break; case 'C': if( op[2] != 0 ) opt_subcat = op + 2; else if ( i < argc - 1 ) { i++; opt_subcat = argv[i]; } else { stdOut->writeString( "Must specify an argument for -C\n" ); usage(); exit(1); } break; case 'd': if( op[2] != 0 ) opt_path = op + 2; else if ( i < argc - 1 ) { i++; opt_path = argv[i]; } else { stdOut->writeString( "Must specify an argument for -d\n" ); usage(); exit(1); } break; case 'L': if( op[2] != 0 ) opt_libpath = op + 2; else if ( i < argc - 1 ) { i++; opt_libpath = argv[i]; } else { stdOut->writeString( "Must specify an argument for -L\n" ); usage(); exit(1); } break; case '?': case 'h': usage(); exit(0); case 'l': opt_justlist = true; break; case 'm': opt_compmem = false; break; case 'M': opt_checkmem = true; break; case 'o': if( op[2] != 0 ) opt_output = op + 2; else if ( i < argc - 1 ) { i++; opt_output = argv[i]; } else { stdOut->writeString( "Must specify an argument for -o\n" ); usage(); exit(1); } break; case 's': opt_serialize = true; break; case 'S': opt_compasm = true; break; case 't': opt_timings = true; break; case 'f': if( op[2] != 0 ) opt_tf = atoi(op + 2); else if ( i < argc - 1 ) { i++; opt_tf = atoi(argv[i]); } else { stdOut->writeString( "Must specify an argument for -f\n" ); usage(); exit(1); } if (opt_tf <= 0) opt_tf = 1; break; case 'T': opt_inTimings = true; break; case 'v': opt_verbose = true; break; case 'V': exit(0); default: stdErr->writeString( "falcon: unrecognized option '" ); stdErr->writeString( op ); stdErr->writeString( "'.\n\n" ); usage(); exit(0); } } else { opt_testList.pushBack( new String(argv[i]) ); } } } bool readline( Stream &script, String &line ) { line = ""; char c; while( script.read( &c, 1 ) == 1 ) { if ( c == '\n' ) return true; if ( c != '\r' ) line += c; } return false; } /************************************************ Read script definitions *************************************************/ ScriptData *readProperties( const String &name, Stream &script ) { // let's get the script props. ScriptData *data = new ScriptData( name ); String line; int mode = 0; int descrMode = 0; String description; while ( readline( script, line ) ) { if ( mode == 0 ) { if ( line.find( "/*" ) == 0 ) mode = 1; } else { if ( line.find( "*/" ) == 0 ) { mode = 0; if ( descrMode != 0 ) { data->setProperty( "Description", description ); description = ""; descrMode = 0; } } else { if (line.find( "*" ) == 0 ) { if ( descrMode == 1 ) { int beg = 1; while ( line.getCharAt( beg ) == ' ' ) beg++; line = line.subString( beg ); if ( line == "[/Description]" ) { data->setProperty( "Description", description ); descrMode = 0; description = ""; } else { description += line; description += '\n'; } } else { uint32 limit = line.find( ":" ); if ( limit != String::npos ) { // ok, we have a property int beg = 1; while ( line.getCharAt( beg ) == ' ' ) beg++; String prop = line.subString( beg, limit ); limit++; while ( line.getCharAt( limit ) == ' ' ) limit++; String val = line.subString(limit); if ( prop == "ID" ) { if ( val.length() && val.getCharAt( 0 ) != '-' ) { data->id( ScriptData::IdCodeToId( val ) ); } else { data->id( -1 ); break; } } if ( prop == "Description" ) { descrMode = 1; } else data->setProperty( prop, val ); } } } } } } // invalid data? if ( data->id() == -1 ) { delete data; data = 0; } return data; } /************************************************ Script descriptions *************************************************/ void describeScript( ScriptData *data ) { String idCode; ScriptData::IdToIdCode( data->id(), idCode ); String val; output->writeString( idCode + " - " ); data->getProperty( "Short", val ); output->writeString( val ); if ( ! opt_verbose ) { output->writeString( " - " + data->filename() + " - " ); if ( data->getProperty( "Category", val ) ) { output->writeString( " (" + val ); if( data->getProperty( "Subcategory", val ) ) output->writeString( "/" + val ); output->writeString( ")" ); } output->writeString( "\n" ); } else { output->writeString( "\n" ); output->writeString( "Script file: " + opt_path + "/" ); output->writeString( data->filename() + "\n" ); String cat; if ( data->getProperty( "Category", cat ) ) output->writeString( "Category: " + cat + "\n" ); if ( data->getProperty( "Subcategory", cat ) ) output->writeString( "Subcategory: " + cat + "\n" ); data->getProperty( "Description", val ); output->writeString( val + "\n" ); } } void listScripts() { if ( opt_verbose ) { output->writeString( "-----------------------------------------------------------\n" ); } if ( opt_testList.empty() ) { t_idScriptMap::const_iterator iter = scriptMap.begin(); while( iter != scriptMap.end() ) { describeScript( iter->second ); if ( opt_verbose ) { output->writeString( "-----------------------------------------------------------\n" ); } ++iter; } } else { ListElement *iter = opt_testList.begin(); while ( iter != 0 ) { const String &name = *(String *) iter->data(); t_idScriptMap::const_iterator elem = scriptMap.find( ScriptData::IdCodeToId( name ) ); if ( elem == scriptMap.end() ) output->writeString( name + " - NOT FOUND\n" ); else { describeScript( elem->second ); if ( opt_verbose ) { output->writeString( "-----------------------------------------------------------\n" ); } } iter = iter->next(); } } } void filterScripts() { t_idScriptMap filterMap; if ( opt_testList.empty() ) { t_idScriptMap::iterator iter = scriptMap.begin(); while( iter != scriptMap.end() ) { String cat, subcat; ScriptData *script = iter->second; script->getProperty( "Category", cat ); script->getProperty( "Subcategory", subcat ); if ( ( opt_category == "" || cat == opt_category )&& ( opt_subcat == "" || subcat == opt_subcat ) ) { filterMap[iter->first] = iter->second; iter->second = 0; } ++iter; } } else { ListElement *iter = opt_testList.begin(); while ( iter != 0 ) { const String &name = *(String *) iter->data(); t_idScriptMap::iterator elem = scriptMap.find( ScriptData::IdCodeToId( name ) ); if ( elem != scriptMap.end() ) { filterMap[ elem->first ] = elem->second; elem->second = 0; } iter = iter->next(); } } t_idScriptMap::iterator liter = scriptMap.begin(); while( liter != scriptMap.end() ) { delete liter->second; ++liter; } scriptMap = filterMap; } /************************************************ Script testing *************************************************/ bool testScript( ScriptData *script, ModuleLoader *modloader, Module *core, Module *testSuite, String &reason, String &trace ) { Module *scriptModule; //--------------------------------- // 1. compile String path = opt_path + "/" + script->filename(); FileStream *source_f = new FileStream; source_f->open( path ); if( ! source_f->good() ) { reason = "Can't open source " + path; delete source_f; return false; } Stream *source = TranscoderFactory( "utf-8", source_f, true ); scriptModule = new Module(); Path scriptPath( script->filename() ); scriptModule->name( scriptPath.getFile() ); scriptModule->path( path ); Compiler compiler( scriptModule, source ); compiler.searchPath( Engine::getSearchPath() ); if ( opt_timings ) compTime = Sys::_seconds(); if ( ! compiler.compile() ) { Error* err = compiler.detachErrors(); trace = err->toString(); err->decref(); reason = "Compile step failed."; delete source; scriptModule->decref(); return false; } if ( opt_timings ) compTime = Sys::_seconds() - compTime; // we can get rid of the source here. delete source; // now compile the code. GenCode gc( compiler.module() ); if ( opt_timings ) genTime = Sys::_seconds(); gc.generate( compiler.sourceTree() ); if ( opt_timings ) genTime = Sys::_seconds() - genTime; // serialization/deserialization test if( opt_serialize ) { // create the module stream FileStream *module_stream = new FileStream; // create a temporary file module_stream->create( "temp_module.fam", FileStream::e_aUserWrite | FileStream::e_aReadOnly ); scriptModule->save( module_stream, false ); module_stream->seekBegin( 0 ); String scriptName = scriptModule->name(); scriptModule->decref(); scriptModule = new Module(); scriptModule->name( scriptName ); scriptModule->path( path ); if ( ! scriptModule->load( module_stream, false ) ) { reason = "Deserialization step failed."; delete module_stream; scriptModule->decref(); return false; } delete module_stream; } //--------------------------------- // 2. link VMachineWrapper vmachine; // so we can link them vmachine->link( core ); vmachine->link( testSuite ); Runtime runtime( modloader ); try { runtime.addModule( scriptModule ); } catch (Error *err) { trace = err->toString(); err->decref(); reason = "Module loading failed."; scriptModule->decref(); return false; } // we can abandon our reference to the script module scriptModule->decref(); //--------------------------------- // 3. execute TestSuite::setSuccess( true ); TestSuite::setTimeFactor( opt_tf ); if ( opt_timings ) execTime = Sys::_seconds(); // inject args and script name Item *sname =vmachine->findGlobalItem( "scriptName" ); *sname = new CoreString( scriptModule->name() ); sname =vmachine->findGlobalItem( "scriptPath" ); *sname = new CoreString( scriptModule->path() ); try { vmachine->link( &runtime ); } catch( Error *err ) { trace = err->toString(); err->decref(); reason = "VM Link step failed."; return false; } if ( opt_timings ) linkTime = Sys::_seconds(); // Become target of the OS signals. // vmachine->becomeSignalTarget(); try { try { vmachine->launch(); } catch( CodeError* err ) { err->decref(); trace = ""; reason = "Non executable script."; return false; } if ( opt_timings ) execTime = Sys::_seconds() - execTime; } catch( Error *err ) { trace = err->toString(); err->decref(); reason = "Abnormal script termniation"; return false; } // get timings if ( opt_timings ) { total_time_compile += compTime; total_time_execute += execTime; total_time_link += linkTime; total_time_generate += genTime; } // reset the success status reason = TestSuite::getFailureReason(); // ensure to clean memory -- do this now to ensure the VM is killed before we kill the module. modloader->compiler().reset(); return TestSuite::getSuccess(); } void gauge() { if ( opt_output != "" ) { double ratio = (passedCount + failedCount) / double( scriptMap.size() ); int gaugesize = (int) (50 * ratio); stdOut->writeString( "\r[" ); for ( int i = 0; i < gaugesize; i++ ) stdOut->writeString( "#" ); for ( int j = gaugesize; j < 50; j++ ) stdOut->writeString( " " ); String temp = "] "; temp.writeNumber( (int64)(ratio *100) ); temp += "% ("; temp.writeNumber( (int64) passedCount + failedCount ); temp += "/"; temp.writeNumber( (int64) scriptMap.size() ); temp += ")"; if ( failedCount > 0 ) stdOut->writeString( String(" fail ").A(failedCount) ); } } void executeTests( ModuleLoader *modloader ) { Module *core = Falcon::core_module_init(); Module *testSuite = init_testsuite_module(); // now we are all armed up. t_idScriptMap::const_iterator iter; ListElement *order = 0; if( opt_testList.empty() ) iter = scriptMap.begin(); else { order = opt_testList.begin(); iter = scriptMap.end(); while( iter == scriptMap.end() && order != 0 ) { const String &name = *(String *) order->data(); iter = scriptMap.find( ScriptData::IdCodeToId( name ) ); order = order->next(); } } while( iter != scriptMap.end() ) { String cat, subcat; ScriptData *script = iter->second; String reason, trace; // ... we use reason as a temporary storage... ScriptData::IdToIdCode( script->id(), reason ); TestSuite::setTestName( reason ); output->writeString( reason + ": " ); output->flush(); // ... and clear it before getting memory allocation reason = ""; if ( opt_checkmem ) { s_allocatedMem = gcMemAllocated(); s_outBlocks = memPool->allocatedItems(); } bool success = testScript( script, modloader, core, testSuite, reason, trace ); if ( success ) { passedCount++; output->writeString( "success." ); if ( opt_timings ) { String temp = "("; temp.writeNumber( compTime, TIME_PRINT_FMT ); temp += " "; temp.writeNumber( genTime, TIME_PRINT_FMT ); temp += " "; temp.writeNumber( linkTime, TIME_PRINT_FMT ); temp += " "; temp.writeNumber( execTime, TIME_PRINT_FMT ); temp += ")"; output->writeString( temp ); } if ( opt_inTimings ) { numeric tott, opNum; TestSuite::getTimings( tott, opNum ); if (opNum > 0.0 && tott >0.0) { String temp = " ( "; temp.writeNumber( tott, TIME_PRINT_FMT ); temp += " secs, "; temp.writeNumber( opNum / tott, TIME_PRINT_FMT ); temp += " ops/sec)"; output->writeString( temp ); } } if ( opt_checkmem ) { memPool->performGC(); long alloc = gcMemAllocated() - s_allocatedMem; long blocks = memPool->allocatedItems() - s_outBlocks; String temp = " (leak: "; temp.writeNumber( (int64) alloc ); temp += "/"; temp.writeNumber( (int64) blocks ); temp += ")"; output->writeString( temp ); } output->writeString( "\n" ); } else { failedCount++; if( opt_verbose ) { output->writeString( "fail.\n" ); describeScript( script ); output->writeString( "Reason: " + reason + "\n" ); if ( trace != "" ) { output->writeString( "Error trace: \n------------ \n\n" ); output->writeString( trace ); output->writeString( "\n" ); } } else output->writeString( "fail. (" + reason + ")\n" ); } if ( opt_verbose ) { output->writeString( "-------------------------------------------------------------\n\n"); } gauge(); if( opt_testList.empty() ) ++iter; else { iter = scriptMap.end(); while( iter == scriptMap.end() && order != 0 ) { const String &name = *(String *) order->data(); iter = scriptMap.find( ScriptData::IdCodeToId( name ) ); order = order->next(); } } } // clear the testname (or we'll have 1 extra block in accounts) TestSuite::setTestName( "" ); core->decref(); testSuite->decref(); } /************************************************ Main function *************************************************/ int main( int argc, char *argv[] ) { // Install a void ctrl-c handler (let ctrl-c to kill this app) Sys::_dummy_ctrl_c_handler(); /* Block signals same way falcon binary does. */ BlockSignals(); Falcon::Engine::Init(); stdOut = stdOutputStream(); stdErr = stdErrorStream(); // setting an environment var for self-configurating scripts Sys::_setEnv( "FALCON_TESTING", "1" ); version(); parse_options( argc, argv ); FileStream *fs_out = new FileStream; passedCount = 0; failedCount = 0; totalCount = 0; total_time_compile = 0.0; total_time_generate = 0.0; total_time_link = 0.0; total_time_execute = 0.0; double appTime; if( opt_timings ) appTime = Sys::_seconds(); else appTime = 0.0; ModuleLoader *modloader = new ModuleLoader( opt_libpath == "" ? "." : opt_libpath ); modloader->addFalconPath(); Engine::setSearchPath( modloader->getSearchPath() ); modloader->alwaysRecomp( true ); modloader->saveModules( false ); modloader->compileInMemory( opt_compmem ); modloader->sourceEncoding( "utf-8" ); int32 error; if ( opt_path == "" ) opt_path = "."; else modloader->addDirectoryFront( opt_path ); if ( opt_output != "" ) { fs_out->create( opt_output, FileStream::e_aUserWrite | FileStream::e_aReadOnly ); if( fs_out->bad() ) { stdErr->writeString( "faltest: FATAL - can't open output file " + opt_output + "\n" ); exit(1); } output = fs_out; } else { output = stdOut; } DirEntry *entry = Sys::fal_openDir( opt_path, error ); if ( entry == 0 ) { String err = "faltest: FATAL - can't open " + opt_path + " (error "; err.writeNumber( (int64) error ); err += "\n"; stdErr->writeString( err ); exit(1); } { String filename; while( entry->read(filename) ) { if ( filename.find( ".fal" ) == filename.length() - 4 ) { FileStream script; // TODO: use the correct transcoder. String temp = opt_path + "/" + filename; script.open( temp ); if ( script.good() ) { ScriptData *data = readProperties( filename, script ); if ( data != 0 ) { scriptMap[ data->id() ] = data; String category; if ( data->getProperty( "Category", category ) ) categoryMap[ category ].pushBack( data ); } } } } Sys::fal_closeDir( entry ); } filterScripts(); if ( opt_justlist ) { listScripts(); delete stdOut; delete stdErr; return 0; } // ensure correct accounting by removing extra data. modloader->compiler().reset(); // reset memory tests s_totalMem = gcMemAllocated(); s_totalOutBlocks = memPool->allocatedItems(); executeTests( modloader ); // in context to have it destroyed on exit { output->writeString( "\n" ); if( opt_verbose ) output->writeString( "-----------------------------------------------------------------------\n" ); String completed = "Completed "; completed.writeNumber( (int64) passedCount + failedCount ); completed += " tests, passed "; completed.writeNumber( (int64) passedCount ); completed += ", failed "; completed.writeNumber( (int64) failedCount ); completed += "\n"; output->writeString( completed ); stdOut->writeString( "\n" ); if( opt_verbose && opt_output != "" ) { stdOut->writeString( "-----------------------------------------------------------------------\n" ); output->writeString( completed ); } if( opt_timings ) { if ( opt_verbose ) { output->writeString( "Recorded timings: \n" ); String temp = "Total compilation time: "; temp.writeNumber( total_time_generate, TIME_PRINT_FMT ); temp += " (mean: "; temp.writeNumber( total_time_generate / (passedCount + failedCount), TIME_PRINT_FMT ); temp += ")\n"; temp += "Total generation time: "; temp.writeNumber( total_time_generate, TIME_PRINT_FMT ); temp += " (mean: "; temp.writeNumber( total_time_generate / (passedCount + failedCount), TIME_PRINT_FMT ); temp += ")\n"; temp += "Total link time: "; temp.writeNumber( total_time_link, TIME_PRINT_FMT ); temp += " (mean: "; temp.writeNumber( total_time_link / (passedCount + failedCount), TIME_PRINT_FMT ); temp += ")\n"; temp += "Total execution time: "; temp.writeNumber( total_time_execute, TIME_PRINT_FMT ); temp += " (mean: "; temp.writeNumber( total_time_execute / (passedCount + failedCount), TIME_PRINT_FMT ); temp += ")\n"; double actTime = total_time_compile + total_time_generate + total_time_link + total_time_execute; temp += "Total activity time: "; temp.writeNumber( actTime, TIME_PRINT_FMT ); temp += " (mean: "; temp.writeNumber( actTime / (passedCount + failedCount), TIME_PRINT_FMT ); temp += ")\n"; temp += "Total application time: "; temp.writeNumber( Sys::_seconds() - appTime, TIME_PRINT_FMT ); temp += "\n"; output->writeString( temp ); } else { output->writeString( "Recorded timings: " ); String temp; temp.writeNumber( total_time_compile, TIME_PRINT_FMT ); temp += " "; temp.writeNumber( total_time_generate, TIME_PRINT_FMT ); temp += " "; temp.writeNumber( total_time_link, TIME_PRINT_FMT ); temp += " "; temp.writeNumber( total_time_execute, TIME_PRINT_FMT ); temp += "\n"; output->writeString( temp ); double actTime = total_time_compile + total_time_generate + total_time_link + total_time_execute; temp = "Total time: "; temp.writeNumber( actTime, TIME_PRINT_FMT ); temp += "\n"; output->writeString( temp ); } } } if ( opt_checkmem ) { memPool->performGC(); long blocks = memPool->allocatedItems() - s_totalOutBlocks; long mem = gcMemAllocated() - s_totalMem; String temp = "Final memory balance: "; temp = "Final memory balance: "; temp.writeNumber( (int64) mem ); temp += "/"; temp.writeNumber( (int64) blocks ); temp += "\n"; output->writeString( temp ); } stdOut->writeString( "faltest: done.\n" ); delete stdOut; delete stdErr; if ( failedCount > 0 ) return 2; return 0; } /* end of faltest.cpp */ clt/faltest/faltest.rc000066400000000000000000000023731176363201700153050ustar00rootroot00000000000000///////////////////////////////////////////////////////////////////////////// // // Icon // // TODO: create an icon //MAINICON ICON DISCARDABLE "resources\falcon-ico-base.ico" ///////////////////////////////////////////////////////////////////////////// // // Version // // for project wide versioning... #include #include VS_VERSION_INFO VERSIONINFO FILEVERSION FALCON_VERSION_RCINFO_N PRODUCTVERSION FALCON_VERSION_RCINFO_N FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x4L FILETYPE 0x0L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "041004b0" BEGIN VALUE "CompanyName", "Falcon Committee" VALUE "FileDescription", "The Falcon unit test interface" VALUE "FileVersion", FALCON_VERSION_RCINFO VALUE "InternalName", "faltest" VALUE "LegalCopyright", "The Falcon Programming Language License" VALUE "OriginalFilename", "faltest.exe" VALUE "ProductName", "The Falcon Programming Language" VALUE "ProductVersion", FALCON_VERSION_RCINFO END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x410, 1200 END END clt/faltest/scriptdata.cpp000066400000000000000000000037431176363201700161610ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: scriptdata.cpp Simple helper to help keeping a the unit test a bit cleaner. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun feb 13 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Simple helper to help keeping a the unit test a bit cleaner. */ #include #include #include "scriptdata.h" namespace Falcon { ScriptData::ScriptData( const String &filename, int id ): m_id( id ), m_filename( filename ) { m_filename.bufferize(); } void ScriptData::IdToIdCode( int id, String &code ) { // code is just CODE * 100 + letter... int64 numcode = id /100; char letter = (id % 100); code = ""; if ( letter != 0 ) { letter += 'a'-1; code.writeNumber( numcode ); code.append( letter ); } else code.writeNumber( numcode ); } int ScriptData::IdCodeToId( const String &code ) { if ( code.length() == 0 ) return 0; uint32 lettercode = code.getCharAt( code.length() - 1 ); String copy; if ( lettercode >= (uint32)'a' && lettercode <= (uint32) 'z' ) { lettercode = lettercode - 'a' + 1; copy = code.subString( 0, code.length() - 1 ); } else { copy = code; lettercode = 0; } int64 ret; if ( copy.parseInt( ret ) ) return (int)(ret * 100 + lettercode); return 0; } void ScriptData::setProperty( const String &name, const String &value ) { m_properties[ name ] = value; } bool ScriptData::getProperty( const String &name, String &value ) const { t_stringMap::const_iterator iter = m_properties.find( name ); if( iter != m_properties.end() ) { value = iter->second; return true; } value = ""; return false; } } /* end of scriptdata.cpp */ clt/faltest/scriptdata.h000066400000000000000000000030521176363201700156170ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: scriptdata.h Defintions for script data dictionary. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun feb 13 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Header file needed for the testsuite application. Defintions for script data dictionary. */ #ifndef flc_scriptdata_H #define flc_scriptdata_H #include #include #include #include typedef std::map t_stringMap; namespace Falcon { class ScriptData { t_stringMap m_properties; int m_id; String m_filename; public: ScriptData( const String &filename, int id=-1 ); static void IdToIdCode( int id, String &code ); static int IdCodeToId( const String &code ); void setProperty( const String &name, const String &value ); bool getProperty( const String &name, String &value ) const ; const String &filename() const { return m_filename; } const int id() const { return m_id; } void id( int num ) { m_id = num; } t_stringMap properties() { return m_properties; } const t_stringMap &properties() const { return m_properties; } }; } typedef std::map< int, Falcon::ScriptData *> t_idScriptMap; typedef std::map< Falcon::String, Falcon::List> t_categoryScriptMap; #endif /* end of scriptdata.h */ clt/faltest/testsuite.rc000066400000000000000000000022301176363201700156640ustar00rootroot00000000000000///////////////////////////////////////////////////////////////////////////// // // Icon // ///////////////////////////////////////////////////////////////////////////// // // Version // // for project wide versioning... #include #include VS_VERSION_INFO VERSIONINFO FILEVERSION FALCON_VERSION_RCINFO_N PRODUCTVERSION FALCON_VERSION_RCINFO_N FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x4L FILETYPE 0x0L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "041004b0" BEGIN VALUE "CompanyName", "Falcon Committee" VALUE "FileDescription", "Unit test support module" VALUE "FileVersion", FALCON_VERSION_RCINFO VALUE "InternalName", "testsuite" VALUE "LegalCopyright", "The Falcon Programming Language License" VALUE "OriginalFilename", "testsuite.dll" VALUE "ProductName", "The Falcon Programming Language" VALUE "ProductVersion", FALCON_VERSION_RCINFO END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x410, 1200 END END cmake/000077500000000000000000000000001176363201700121445ustar00rootroot00000000000000cmake/modules/000077500000000000000000000000001176363201700136145ustar00rootroot00000000000000cmake/modules/FindEditline.cmake000066400000000000000000000013571176363201700171620ustar00rootroot00000000000000option(FALCON_WITH_GPL_READLINE "" off) if(FALCON_WITH_GPL_READLINE) set(_header readline/readline.h) set(_libname readline) else() set(_header editline/readline.h) set(_libname edit) endif() find_path(Editline_INCLUE_DIR ${_header}) find_library(Editline_LIBRARY ${_libname}) include(CheckSymbolExists) set(CMAKE_REQUIRED_INCLUDES ${Editline_INCLUE_DIR}) set(CMAKE_REQUIRED_LIBRARIES ${Editline_LIBRARY}) check_symbol_exists(rl_getc_function "stdio.h;${_header}" HAVE_RL_GETC_FUNCTION ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Editline DEFAULT_MSG Editline_LIBRARY Editline_INCLUE_DIR HAVE_RL_GETC_FUNCTION) set(Editline_INCLUDE_DIRS ${Editline_INCLUE_DIR}) set(Editline_LIBRARIES ${Editline_LIBRARY})cmake_build.sample000066400000000000000000000012141176363201700145240ustar00rootroot00000000000000#!/bin/sh # To be copied in a directory like build/ and run there cmake .. \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX="/usr/local" \ -DFALCON_BIN_DIR="bin" \ -DFALCON_LIB_DIR="lib" \ -DFALCON_WITH_MANPAGES="ON" \ -DFALCON_MAN_DIR="share/man/man1" \ -DFALCON_WITH_FEATHERS="feathers" \ -DFALCON_WITH_INTERNAL_PCRE="OFF" \ -DFALCON_WITH_INTERNAL_ZLIB="OFF" \ -DFALCON_WITH_EDITLINE="ON" \ -DFALCON_WITH_INTERNAL="OFF" # other useful settings # -DFALCON_INC_DIR="include/falcon" # -DFALCON_MOD_DIR="lib/falcon" # -DFALCON_CMAKE_DIR="share/falcon/cmake" # -DFALCON_SHARE_DIR="share/falcon" copyright000066400000000000000000000036531176363201700130260ustar00rootroot00000000000000 The Falcon Programming Language http://www.falconpl.org Falcon is currently distributed under a dual licensing scheme GPL/FPLL. The GPL licensing scheme is provided to ensure compatibilty with Open Source and Free Software products. It's a relative strict license which is not particularly adequate for virtual machine based scripting languge interpreters and engines, but it can be chosen to develop pure GPL applications or to favor integration with already existing GPL embedding applications. Falcon 0.9 and following grant the so called "OpenSSL exception to GPL" were applicable: In addition, as a special exception, the author of this program gives permission to link the code of its release with the OpenSSL project's "OpenSSL" library (or with modified versions of it that use the same license as the "OpenSSL" library), and distribute the linked executables. You must obey the GNU General Public License in all respects for all of the code used other than "OpenSSL". If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. The Falcon Programming Language License is specifically designed around the concept of an open source scripting language engine. It grants usability of the covered source in commercial applications, as it supports applying different and incompatible licenses to embedding and extension elements, while ensuring the openness of the core engine and of any work derived directly from it. Falcon 0.8.10 and followings are released under one of GPLv2+OpenSSL exception or FPLLv1.1, at your choice. The GPLv2 license can be found at: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The FPLLv1.1 license can be found at: http://www.falconpl.org?page_id=license_1_1 Copyright owners: (C) 2003-2008 Giancarlo Niccolai detail.cmake000066400000000000000000000225311176363201700133330ustar00rootroot00000000000000################################################################# # Base options option( FALCON_SKIP_BISON "Skip BISON to avoid recompiling grammar" ON ) option( FALCON_BUILD_FEATHERS "Build Falcon feathers" ON ) option( FALCON_BUILD_MODULES "Build Falcon modules" ON ) option( FALCON_BUILD_FWKS "Build Falcon frameworks" ON ) option( FALCON_BUILD_APPS "Build Falcon applications" ON ) option( FALCON_BUILD_NATMODS "Build native (binary) non-feather modules" ON ) option( FALCON_BUILD_DOCS "Build automatic documentation" ON ) option( FALCON_INSTALL_TESTS "Copy test files in the final installation (under share/)" OFF ) option( FALCON_BUILD_DIST "Prepare distribution helper scripts in dist/" OFF ) if (WIN32) set( FALCON_COMPILE_SOURCE_MODS OFF ) set( FALCON_STRIP_SOURCE_MODS OFF) else() option( FALCON_COMPILE_SOURCE_MODS "Compile source modules into .fam for faster script startup" ON ) option( FALCON_STRIP_SOURCE_MODS "Don't install source .fal/ftd modules" OFF) endif() # NOTE modules are installed via # install(FILES .. DESTINATION ${FALCON_MOD_INSTALL_DIR}) # since they are neither RUNTIME, LIBRARY nor ARCHIVE. #In windows, we normally install in c:\falcon if(WIN32) #mingw requires -mthreads global option if(CMAKE_GENERATOR STREQUAL "MinGW Makefiles") message( "MINGW make detected, adding -mthreads flag" ) list(APPEND CMAKE_EXE_LINKER_FLAGS -mthreads ) list(APPEND CMAKE_SHARED_LINKER_FLAGS -mthreads ) list(APPEND CMAKE_MODULE_LINKER_FLAGS -mthreads ) endif() endif(WIN32) # ## if(WIN32) SET( FALCON_HOST_SYSTEM "WINDOWS" ) SET( FALCON_SYSTEM_WIN 1 ) else() if(APPLE) set( FALCON_HOST_SYSTEM "MAC" ) set( FALCON_SYSTEM_MAC 1 ) elseif(UNIX) set( FALCON_HOST_SYSTEM "UNIX" ) SET( FALCON_SYSTEM_UNIX 1 ) else() message(FATAL_ERROR "Sorry, can't determine system type" ) endif() endif() ## SONAME and soversion (unix so library informations for engine) # Remember that SONAME never follows project versioning, but # uses a VERSION, REVISION, AGE format, where # VERSION: generational version of the project # REVISION: times this version has been touched # AGE: Number of version for which binary compatibility is granted # In eample, 1.12.5 means that this lib may be dynlinked against # every program using this lib versioned from 1.8 to 1.12. include(versioninfo.cmake) if(NOT FALCON_SONAME_AGE) # A couple of useful shortcuts set(FALCON_SONAME "${FALCON_SONAME_VERSION}.${FALCON_SONAME_REVISION}.${FALCON_SONAME_AGE}") set(FALCON_SONAME_REV "${FALCON_SONAME_VERSION}.${FALCON_SONAME_REVISION}") endif(NOT FALCON_SONAME_AGE) #Automatically generated version info for RC scripts and sources #CMAKE is good at this, let's use this feature set(FALCON_VERSION_RC "${FALCON_VERSION_MAJOR}, ${FALCON_VERSION_MINOR}, ${FALCON_VERSION_REVISION}, ${FALCON_VERSION_PATCH}") set(FALCON_VERSION_ID "${FALCON_VERSION_MAJOR}.${FALCON_VERSION_MINOR}.${FALCON_VERSION_REVISION}.${FALCON_VERSION_PATCH}") set(FALCON_ID "${FALCON_VERSION_MAJOR}.${FALCON_VERSION_MINOR}.${FALCON_VERSION_REVISION}") message(STATUS "Compiling Falcon ${FALCON_VERSION_ID} on ${CMAKE_SYSTEM}" ) # set( Falcon_VERSION "${FALCON_VERSION_ID}" ) ############################################################################## # Other defaults ############################################################################## include(TestBigEndian) message(STATUS "Testing endianity on ${CMAKE_SYSTEM}" ) TEST_BIG_ENDIAN(falcon_big_endian) if(falcon_big_endian) set(FALCON_LITTLE_ENDIAN 0) else(falcon_big_endian) set(FALCON_LITTLE_ENDIAN 1) endif(falcon_big_endian) # install prefix defaults, if not set if(NOT CMAKE_INSTALL_PREFIX) #In windows, we normally install in c:\falcon if(WIN32) if($ENV{PROGRAMS}) SET(CMAKE_INSTALL_PREFIX "C:\\\\$ENV{PROGRAMS}\\\\falcon" ) else() SET(CMAKE_INSTALL_PREFIX "C:\\\\Program Files\\\\falcon" ) endif() else() # unixes set(CMAKE_INSTALL_PREFIX "/usr/local" ) endif() endif() message( STATUS "Installation prefix: ${CMAKE_INSTALL_PREFIX}" ) if (NOT FALCON_LIB_DIR) set(FALCON_LIB_DIR lib) endif() # if(LIB_SUFFIX) set(FALCON_LIB_DIR "${FALCON_LIB_DIR}${LIB_SUFFIX}") endif() if( NOT FALCON_SHARE_DIR) if(WIN32) set(FALCON_SHARE_DIR "share") else() set(FALCON_SHARE_DIR "share/falcon${FALCON_ID}") endif() endif() if( NOT FALCON_DOC_DIR) if(WIN32) set(FALCON_DOC_DIR "share") else() set(FALCON_DOC_DIR "share/doc/falcon${FALCON_ID}") endif() endif() if (NOT FALCON_BIN_DIR) set(FALCON_BIN_DIR bin) endif() if (NOT FALCON_INC_DIR) if(WIN32) set(FALCON_INC_DIR "include") else() set(FALCON_INC_DIR "include/falcon${FALCON_ID}") endif() endif() if (NOT FALCON_MOD_DIR ) if(WIN32) set(FALCON_MOD_DIR bin) else() set(FALCON_MOD_DIR "${FALCON_LIB_DIR}/falcon") endif() endif() if(WIN32) set(FALCON_CMAKE_DIR cmake) else() set(FALCON_CMAKE_DIR ${FALCON_SHARE_DIR}/cmake) endif() set(FALCON_APP_DIR ${FALCON_MOD_DIR}/apps) # for install(TARGETS .. ${FALCON_INSTALL_DESTINATIONS}) set(FALCON_INSTALL_DESTINATIONS RUNTIME DESTINATION ${FALCON_BIN_DIR} LIBRARY DESTINATION ${FALCON_LIB_DIR} ARCHIVE DESTINATION ${FALCON_LIB_DIR} ) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${FALCON_BIN_DIR}" CACHE INTERNAL "Where to put the executables" ) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${FALCON_LIB_DIR}" CACHE INTERNAL "Where to put the libraries" ) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${FALCON_LIB_DIR}" CACHE INTERNAL "Where to put the archives" ) message( STATUS "Binary prefix: ${FALCON_BIN_DIR}" ) message( STATUS "Library prefix: ${FALCON_LIB_DIR}" ) message( STATUS "Include prefix: ${FALCON_INC_DIR}" ) message( STATUS "Module prefix: ${FALCON_MOD_DIR}" ) message( STATUS "Application directory: ${FALCON_APP_DIR}" ) message( STATUS "CMAKE config prefix: ${FALCON_CMAKE_DIR}" ) if ( NOT WIN32 ) if (NOT FALCON_MAN_DIR) set(FALCON_MAN_DIR "share/man/man1") endif() message( STATUS "Manual pages: ${FALCON_MAN_DIR}" ) endif() # Variable Forwarding, interally we use the conventions # introduced by find_package(Falcon). TODO: That's not elegant set(Falcon_APP_DIR "${FALCON_APP_DIR}") set(Falcon_MOD_DIR "${FALCON_MOD_DIR}") set(Falcon_BIN_DIR "${FALCON_BIN_DIR}") set(Falcon_LIB_DIR "${FALCON_LIB_DIR}") set(Falcon_MAN_DIR "${FALCON_MAN_DIR}") set(Falcon_INC_DIR "${FALCON_INC_DIR}") set(Falcon_SHARE_DIR "${FALCON_SHARE_DIR}") set(Falcon_DOC_DIR "${FALCON_DOC_DIR}") set(Falcon_CMAKE_DIR "${FALCON_CMAKE_DIR}") ######################################################################### # RPATH(Linux) and install_name(OSX) # option(DISABLE_RPATH "http://wiki.debian.org/RpathIssue" on) if(NOT DISABLE_RPATH) # Always find libfalcon_engine.so in build and install tree, without LD_LIBRARY_PATH. set(CMAKE_SKIP_BUILD_RPATH false) set(CMAKE_BUILD_WITH_INSTALL_RPATH false) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${FALCON_LIB_DIR}") set(CMAKE_INSTALL_RPATH_USE_LINK_PATH false) # Apple equivalent to RPATH is called `install_name' set(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${FALCON_LIB_DIR}") else() set(CMAKE_SKIP_RPATH on) endif() ######################################################################### # Functions # function( add_fam_target source ) file( RELATIVE_PATH source_relative "${CMAKE_SOURCE_DIR}" "${source}") get_filename_component( path_of_fal "${source_relative}" PATH) get_filename_component( name_of_fal "${source_relative}" NAME_WE) # falcon command -- on windows it if(UNIX OR APPLE) set( falcon_command "${CMAKE_BINARY_DIR}/devtools/icomp.sh" ) else() set( falcon_command "${CMAKE_BINARY_DIR}/devtools/icomp.bat" ) endif() set( output_file "${CMAKE_BINARY_DIR}/${path_of_fal}/${name_of_fal}.fam" ) set( compile_command ${falcon_command} ${source} ${output_file} ) add_custom_command( OUTPUT "${output_file}" COMMAND ${compile_command} DEPENDS ${source} ) string(REPLACE "/" "_" target_name "${source}" ) add_custom_target(${target_name} ALL DEPENDS "${output_file}" falcon falcon_engine ) #install must be relative to current source path_of_fal file( RELATIVE_PATH single_fal_relative "${CMAKE_CURRENT_SOURCE_DIR}" "${single_fal}") get_filename_component( path_of_fal "${single_fal_relative}" PATH) install(FILES "${output_file}" DESTINATION "${FALCON_MOD_DIR}/${path_of_fal}") endfunction() function( falcon_install_moddirs module_dirs ) message( "Installing top modules in ${CMAKE_CURRENT_SOURCE_DIR}" ) foreach(item ${module_dirs} ) message( "Installing falcon modules in ${item}" ) file( GLOB_RECURSE files "${item}" "*.fal" "*.ftd" ) foreach( single_fal ${files} ) file( RELATIVE_PATH single_fal_relative "${CMAKE_CURRENT_SOURCE_DIR}" "${single_fal}") get_filename_component( path_of_fal "${single_fal_relative}" PATH) #Create installation files from in files if(NOT FALCON_STRIP_SOURCE_MODS) install( FILES "${single_fal}" DESTINATION "${FALCON_MOD_DIR}/${path_of_fal}" ) endif() if(FALCON_COMPILE_SOURCE_MODS) add_fam_target( ${single_fal} ) endif() endforeach() endforeach() endfunction() # vi: set ai et sw=3 sts=3: devtools/000077500000000000000000000000001176363201700127235ustar00rootroot00000000000000devtools/.gitignore000066400000000000000000000000601176363201700147070ustar00rootroot00000000000000falcon-conf falconeer.fal falconenv.sh icomp.sh devtools/CMakeLists.txt000066400000000000000000000030651176363201700154670ustar00rootroot00000000000000# # Configurator for development tools # # CMake will find it via find_package(falcon [REQUIRED]) # It supplies include paths, libraries (via falcon-core-targets.cmake) # and convenience MACROS. # set( cmake_mod_files FalconConfig.cmake falcon-core-config.cmake falcon-core-config-version.cmake ) foreach(item ${cmake_mod_files} ) message( "Configuring ${item}" ) configure_file( ${item}.in "${CMAKE_CURRENT_BINARY_DIR}/${item}" ESCAPE_QUOTES @ONLY) #Create installation files from in files list( APPEND cmake_mod_files_inst ${CMAKE_CURRENT_BINARY_DIR}/${item} ) endforeach() # # Configure other binary/executable files # IF(WIN32) SET(sys_in_spec falconenv.bat icomp.bat) ELSE() SET(sys_in_spec falcon-conf falconenv.sh icomp.sh) ENDIF() SET(in_bin_files falconeer.fal ${sys_in_spec} ) foreach(item ${in_bin_files} ) message( "Configuring ${item}" ) configure_file( ${item}.in "${CMAKE_CURRENT_BINARY_DIR}/${item}" ESCAPE_QUOTES @ONLY) #Create installation files from in files list( APPEND inst_bin_files "${CMAKE_CURRENT_BINARY_DIR}/${item}" ) endforeach() # # Add non-configured files to be installed # list(APPEND inst_bin_files "fallc.fal") ######################################################################## # Install install( FILES ${inst_bin_files} DESTINATION ${FALCON_BIN_DIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) install( FILES ${cmake_mod_files_inst} DESTINATION ${FALCON_CMAKE_DIR} ) devtools/FalconConfig.cmake.in000066400000000000000000000005251176363201700166640ustar00rootroot00000000000000get_filename_component(SELF_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) # core find_package( falcon-core ${Falcon_FIND_VERSION} HINTS ${SELF_DIR} NO_MODULE ) # faldoc find_package( faldoc NO_MODULE HINTS ${SELF_DIR} QUIET NO_MODULE) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Falcon DEFAULT_MSG falcon-core_CONFIG) devtools/README000066400000000000000000000013251176363201700136040ustar00rootroot00000000000000 The Falcon Programming Language Falcon Development Tools 0.8 The tools in this directory are meant to be a support for development of third party modules and embedding applications. Some are also used during the distribution and packaging. Modules and embeddings that are not included in the Falcon Source Project are not bound to use Falcon Development Environment. They just require a standard installation of Falcon with development support (dev packages for debian distros, Development option turned on for windows installer and so on), which will include correctly configured tools which are present in this part of the project. devtools/compile_tree.fal000066400000000000000000000021641176363201700160610ustar00rootroot00000000000000/* FALCON - DevTools FILE: compile_tree.fal Compiles all the scripts in a tree. This script can be called after temporary or complete installation on systems with hand-made distributions to generate .fam modules out of the installed hierarcy of scripts. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load process if args.len() != 1 > "Usage: compile_tree.fal " end try dir = Directory( args[0] ) dir.descend( nil, compileFal ) dir.close() > "Done." return 0 catch IoError in e > "Can't open required directory: ", args[0] return 1 end function compileFal( fname ) if fname.endsWith( ".fal" ) or fname.endsWith( ".ftd" ) > "Making ", fname try famname = fname[0:-4]+".fam" system( @"falcon -o \"$famname\" -c \"$fname\"") catch in e > "Error while compiling ", fname > e end end end devtools/falcon-conf.in000066400000000000000000000053231176363201700154430ustar00rootroot00000000000000#!/bin/sh # # Falcon configuration script # # Useful tool to compile falcon modules and embedding applications. # # (C) Giancarlo Niccolai 2010 SYSTEM="" usage() { echo "The Falcon Programming Language" echo "" echo "Build configuration tool for Falcon @FALCON_VERSION_ID@" echo "Usage: $0 params" echo " -c, --cflags writes CFLAGS for modules" echo " -e, --embed-flags writes CFLAGS for embedding applications" echo " --cflags-only-I writes only the include part of CFLAGS" echo " -i, --include writes the FALCON inlcude directory" echo " -l, --libs write library flags for linking modules" echo " -L, --embed-libs library flags and libraries for embedding apps" echo " --libs-only-l write the libraries that must be linked" echo " --libs-only-L write the directory where library is installed" echo " -p, --ldpath Path for LD_LIBRARY_PATH" echo " -h, --help this help" echo " --moddir write the module installation directory" echo " --appdir write falcon applications installation directory" echo " --cmake Position of the CMAKE module for auto-build configurations" echo "" echo "Include this script in your makefiles with \$( $0 )" } detectSystem() { uname | grep Mac && SYSTEM='mac' || SYSTEM='unix' } if [ -z "$*" ]; then usage exit fi detectSystem FALCONLIB="-lfalcon_engine" if [ "$SYSTEM" = "mac" ]; then CFLAGS="-fPIC -dynamiclib -fno-common" LDFLAGS="-module -dload -dynamiclib" else CFLAGS="-fPIC -shared" LDFLAGS="-module -dload -shared" fi INC_PATH="@CMAKE_INSTALL_PREFIX@/@FALCON_INC_DIR@" LIB_PATH="@CMAKE_INSTALL_PREFIX@/@FALCON_LIB_DIR@" LIB_LD_PATH="@CMAKE_INSTALL_PREFIX@/@FALCON_LIB_DIR@" INC_FLAGS="-I$INC_PATH" LIB_FLAGS="-L$LIB_PATH" MODDIR="@CMAKE_INSTALL_PREFIX@/@FALCON_MOD_DIR@" APPDIR="@CMAKE_INSTALL_PREFIX@/@FALCON_APP_DIR@" CMAKEMOD="@CMAKE_INSTALL_PREFIX@/@FALCON_CMAKE_DIR@/FalconConfig.cmake" while [ -n "$*" ]; do case "$1" in "-c"| "--cflags") echo "$INC_FLAGS $CFLAGS" ;; "-e"| "--embed-flags") echo "$INC_FLAGS" ;; "--cflags-only-I") echo "$INC_FLAGS" ;; "--cflags-only-other") echo "$CFLAGS" ;; "-i"|"--include") echo "$INC_PATH";; "-l"| "--libs") echo "$LIB_FLAGS $LDFLAGS $FALCONLIB" ;; "-L"| "--embed-libs") echo "$LIB_FLAGS $FALCONLIB" ;; "--libs-only-L") echo "$LIB_PATH" ;; "-p" | "--ldpath") echo "$LIB_LD_PATH" ;; "--libs-only-l") echo "$FALCONLIB" ;; "--moddir") echo "$MODDIR";; "--appdir") echo "$APPDIR";; "--cmake") echo "$CMAKEMOD";; "-h" | "--help" | *) usage exit;; esac shift done devtools/falcon-core-config-version.cmake.in000066400000000000000000000004361176363201700214530ustar00rootroot00000000000000set(PACKAGE_VERSION @FALCON_VERSION_ID@) set(PACKAGE_VERSION_COMPATIBLE false) if(PACKAGE_FIND_VERSION VERSION_LESS @FALCON_VERSION_ID@) set(PACKAGE_VERSION_COMPATIBLE true) endif() if(PACKAGE_FIND_VERSION VERSION_EQUAL @FALCON_VERSION_ID@) set(PACKAGE_VERSION_EXACT true) endif() devtools/falcon-core-config.cmake.in000066400000000000000000000114241176363201700177670ustar00rootroot00000000000000###################################################################### # The Falcon Programming Language. # CMake module for find_package( falcon ) ###################################################################### # # Generates the following variables. # - Falcon_INCLUDE_DIRS: List of directories for inclusion # - Falcon_LIBRARY_DIRS: List of directories for library search # - Falcon_BINARY_DIR: Where the falcon binary resides # - Falcon_MODULES_DIR: Default installation directory for modules # # - Falcon_LIBRARIES: Libraries needed for link. # ###################################################################### # Notes: set -DMOD_INSTALL= to have modules installed in a # non-default directory. ###################################################################### get_filename_component(SELF_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) # OPTIONAL for the case this project is built as a subproject. if(NOT Falcon_IN_CORE_SOURCETREE) include(${SELF_DIR}/falcon-core-targets.cmake OPTIONAL) endif() set(Falcon_APP_DIR "@FALCON_APP_DIR@") set(Falcon_MOD_DIR "@FALCON_MOD_DIR@") set(Falcon_BIN_DIR "@FALCON_BIN_DIR@") set(Falcon_LIB_DIR "@FALCON_LIB_DIR@") set(Falcon_MAN_DIR "@FALCON_MAN_DIR@") set(Falcon_INC_DIR "@FALCON_INC_DIR@") set(Falcon_SHARE_DIR "@FALCON_SHARE_DIR@") set(Falcon_DOC_DIR "@FALCON_DOC_DIR@") set(Falcon_CMAKE_DIR "@FALCON_CMAKE_DIR@") # Allow flexible installation location to perform out-of-install module builds # Also, on WIN32, the target installation directory is decided during package install. string(REGEX REPLACE "[^/]+" ".." _ups "${Falcon_CMAKE_DIR}") get_filename_component(Falcon_PREFIX ${SELF_DIR}/${_ups} ABSOLUTE) set(Falcon_INCLUDE_DIR "${Falcon_PREFIX}/@FALCON_INC_DIR@") set(Falcon_BINARY_DIR "${Falcon_PREFIX}/@FALCON_BIN_DIR@") set(Falcon_LIBRARY_DIR "${Falcon_PREFIX}/@FALCON_LIB_DIR@") set(Falcon_MODULES_DIR "${Falcon_PREFIX}/@FALCON_MOD_DIR@") # If defined, prefer the source directory include files over the installed ones list(APPEND Falcon_INCLUDE_DIRS "${Falcon_INCLUDE_DIR}") #Versioning set( Falcon_VERSION "@FALCON_VERSION_ID@" ) set( Falcon_VERSION_NAME "@FALCON_VERSION_NAME@" ) # set( Falcon_MAJOR_VERSION @FALCON_VERSION_MAJOR@ ) set( Falcon_MINOR_VERSION @FALCON_VERSION_MINOR@ ) set( Falcon_SUBMINOR_VERSION @FALCON_VERSION_REVISION@ ) set( Falcon_PATCH_VERSION @FALCON_VERSION_PATCH@ ) if(NOT Falcon_IN_CORE_SOURCETREE) #Check if everything is in place. find_program(Falcon_EXECUTABLE falcon "${Falcon_BINARY_DIR}") find_program(Falcon_falpack_EXECUTABLE falpack "${Falcon_BINARY_DIR}") find_library(Falcon_ENGINE falcon_engine "${Falcon_LIBRARY_DIR}") find_file(Falcon_ENGINE_INC falcon/engine.h "${Falcon_INCLUDE_DIR}" ) endif() # Prepare Falcon in development environment. list(APPEND Falcon_INCLUDE_DIRS ${Falcon_INCLUDE_DIR}) list(APPEND Falcon_LIBRARY_DIRS ${Falcon_LIBRARY_DIR}) set(Falcon_LIBRARIES falcon_engine) # for install(TARGETS .. ${FALCON_INSTALL_DESTINATIONS}) set(FALCON_INSTALL_DESTINATIONS RUNTIME DESTINATION ${Falcon_BIN_DIR} LIBRARY DESTINATION ${Falcon_LIB_DIR} ARCHIVE DESTINATION ${Falcon_ARCH_DIR} ) # NOTE modules are installed via # install(FILES .. DESTINATION ${FALCON_MOD_DIR}) # since they are neither RUNTIME, LIBRARY nor ARCHIVE. if(WIN32) #mingw requires -mthreads global option if(CMAKE_GENERATOR STREQUAL "MinGW Makefiles") list(APPEND CMAKE_EXE_LINKER_FLAGS -mthreads ) list(APPEND CMAKE_SHARED_LINKER_FLAGS -mthreads ) list(APPEND CMAKE_MODULE_LINKER_FLAGS -mthreads ) endif() endif(WIN32) macro( falcon_define_module varname modname ) set( ${varname} "${modname}_fm" ) endmacro() function(falcon_install_module2 tgt dir ) if(NOT CMAKE_INSTALL_PREFIX STREQUAL "@CMAKE_INSTALL_PREFIX@") message("WARNING: falcon was built with the with CMAKE_INSTALL_PREFIX @CMAKE_INSTALL_PREFIX@." "The current prefix is ${CMAKE_INSTALL_PREFIX}.") endif() if(APPLE) set_target_properties(${tgt} PROPERTIES PREFIX "" SUFFIX ".dylib" ) else() set_target_properties(${tgt} PROPERTIES PREFIX "" ) endif() if( DEFINED MOD_INSTALL ) set( dest "${MOD_INSTALL}/${dir}" ) else() set( dest "${Falcon_MOD_DIR}/${dir}" ) endif() set_target_properties(${tgt} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${Falcon_MOD_DIR} ) install( TARGETS ${tgt} DESTINATION ${dest} ) endfunction() function(falcon_install_module tgt ) falcon_install_module2( "${tgt}" .) endfunction() function(falcon_finalize_module2 tgt libs) target_link_libraries(${tgt} ${Falcon_LIBRARIES} ${libs} ) falcon_install_module( ${tgt} ) endfunction() function(falcon_finalize_module tgt ) target_link_libraries(${tgt} ${Falcon_LIBRARIES} ) falcon_install_module( ${tgt} ) endfunction() devtools/falconeer.fal.in000066400000000000000000000170561176363201700157630ustar00rootroot00000000000000#!/usr/bin/env falcon /* FALCON - The Falcon Programming Language. FILE: falconeer.fal Script that configures module skeleton. ------------------------------------------------------------------- Copyright 2008 Giancarlo Niccolai Licensed under the Falcon Programming Language License, Version 1.1 (the "License") or GPLv2.0 or following, at your choice; you may not use this file except in compliance with on of the Licenses. You may obtain a copy of the Licenses at http://www.falconpl.org/?page_id=license_1_1 http://www.gnu.org/licenses/old-licenses/gpl-2.0.html Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ load regex //========================================== // Global definitions // // name used for skeleton files const skelname = "fmodskel" // using falcon string concatenation to prevent cmake // to mangle with this values. var_prjname = "@{"+skelname+"_PROJECT_NAME}@" var_desc = "@{"+skelname+"_DESCRIPTION}@" var_author = "@{"+skelname+"_AUTHOR}@" var_date = "@{"+skelname+"_DATE}@" var_year = "@{"+skelname+"_YEAR}@" var_copy = "@{"+skelname+"_COPYRIGHT}@" var_license = "@{"+skelname+"_LICENSE}@" var_mainprj = "@{"+skelname+"_MAIN_PRJ}@" const default_license = ' Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ' const default_prj = "FALCON - The Falcon Programming Language." files = [\ "src/" + skelname + ".cpp", "src/" + skelname + "_ext.cpp", "src/" + skelname + "_ext.h", "src/" + skelname + "_st.cpp", "src/" + skelname + "_st.h", "src/" + skelname + "_mod.cpp", "src/" + skelname + "_mod.h", "src/" + skelname + "_srv.cpp", "src/" + skelname + "_srv.h", "src/" + skelname + "_CMakeLists.txt:src/CMakeLists.txt", "src/" + skelname + "_version.h:src/version.h", "templates/"+ skelname + "_cpp_template", "templates/" + skelname + "_h_template", "docs/" + skelname + "_faldoc.fd.in:docs/faldoc.fd.in", "docs/" + skelname + "_CMakeLists.txt:docs/CMakeLists.txt", skelname + "_CMakeLists.txt:CMakeLists.txt" ] chgRegex = Regex( skelname ) variables = [=>] //========================================== // Configuration options parser // object MyParser from CmdlineParser prj_name = nil author = "Unknown author" description = "" date = CurrentTime() copyright = "The above AUTHOR" licplate = default_license mainprj = default_prj function onOption( option ) switch option case "?", "help": self.usage() case "a", "author", "n", "name", "d", "description", \ "l", "license", "c", "copyright", "p", "project" self.expectValue() default self.unrecognized( option ) end end function onValue( option, value ) switch option case "a", "author": self.author = value case "n", "name": self.prj_name = value case "d", "description": self.description = value case "l", "license": self.loadLicense( value ) case "c", "copyright": self.copyright = value case "p", "project": self.mainprj = value end end function onFree( param ) self.unrecognized( param ) end function loadLicense( param ) try inf = InputStream( param ) self.licplate="" while ( data = inf.read(2048) ) self.licplate +=data end inf.close() catch IoError in e > "falconeer.fal: FATAL: cannot load license plate file ", param > e exit(1) end end function unrecognized( option ) printl( "Unrecognized option \"", option, "\"\n\n" ); self.usage() exit(1) end function usage() > "Falconeer -- the skeleton module configurator.\n" + " Enter a directory where the Falcon Skeleton Module has been unpacked\n" + " and load this script setting the '-n' option.\n" + " Relevant the files in the skeleton directory will be renamed accordingly\n" + " to your settings, and configurable placeholder variables in each file\n" + " will be overwritten.\n" > " Options:\n" + " -?,--help \tPrints this help\n" + " -n/--name \tName of the project (will affect filenames)\n" + " -a/--author \tAuthor(s) of the project\n" + " -c/--copyright \tCopyright owner\n" + " -d/--description \tDescription for the project\n" + " -l/--license \tPointer to a license plate\n" + " -p/--project \tMain project to which this module belongs\n\n" + " -n option is mandatory.\n" end end //========================================== // Function that does the job // function parse_file( fileName, destName ) global variables try fin = InputStream( fileName ) catch IoError in error > "Skipping file ", fileName return end // convert the name if not destName foutName = chgRegex.replace( fileName, MyParser.prj_name ) else foutName = destName end if foutName == fileName: foutName += "_new" try // open the output file name fout = OutputStream( foutName ) line = "" // read the line loop fin.readLine( line, 512 ) // scan and substitute the variables for key, value in variables pos = strFind( line, key ) // can be more than one per line while pos >= 0 line[pos : pos + key.len()] = value pos = strFind( line, key ) end end // now we can write the line fout.write( line + "\n" ) if fin.eof(): break end fin.close() fout.close() catch in error > @"Error while performing change $fileName -> $foutName" > error return end // remove the old file // TODO: set an option to prevent this try fileRemove( fileName ) catch in error > @"Error removing $fileName" > error end end //========================================== // Main program // MyParser.parse() if not MyParser.prj_name MyParser.usage() exit( 1 ) end // create the variables variables[var_prjname] = MyParser.prj_name variables[var_desc ] = MyParser.description variables[var_author ] = MyParser.author variables[var_date ] = MyParser.date.toRFC2822() variables[var_year ] = MyParser.date.year.toString() variables[var_copy ] = MyParser.copyright variables[var_license] = MyParser.licplate variables[var_mainprj] = MyParser.mainprj for file in files if ":" in file orig, dest = strSplit( file, ":", 2 ) else orig = file dest = nil end > "Examining ", orig, "..." parse_file( orig, dest ) end /* End of falconeer.fal */ devtools/falconenv.bat.in000066400000000000000000000013601176363201700157730ustar00rootroot00000000000000@echo off rem rem Falcon development environment launcher script rem echo falconenv.bat -- Settings the environment to run Falcon test install echo echo This script launches a shell configured to run your Falcon from a temporary echo installation path. This allows to test builds before installing them, or echo while having other official builds installed. echo rem On windows, modules and falcon DLLs are in the same directory of this bat. echo set FALCON_ENV_DIR=%~dp0> %TEMP%\falconenv.bat echo echo "Falcon temporary directory set to: %%FALCON_ENV_DIR%%">> %TEMP%\falconenv.bat echo set PATH=%%FALCON_ENV_DIR%%;%%PATH%%>> %TEMP%\falconenv.bat echo set FALCON_LOAD_PATH=.;%%FALCON_ENV_DIR%%>> %TEMP%\falconenv.bat cmd /K %TEMP%\falconenv.bat devtools/falconenv.sh.in000066400000000000000000000026531176363201700156450ustar00rootroot00000000000000#!/bin/sh echo "falconenv.sh -- Settings the environment to run Falcon test install" if [ -z "$1" ]; then echo "Please, specify a command to run in shell" echo echo "To run falcon or another falcon command: falconenv.sh falcon ..." echo "To run a shell: falconenv.sh /bin/bash (or another shell)" exit 1 fi script_dir=`dirname $0` curdir=`pwd` cd $script_dir script_dir=`pwd` cd $curdir echo "Script detected in $script_dir" #discard everything past the install prefix -- retaining what comes before root_prefix=@CMAKE_INSTALL_PREFIX@ # but first escape the path separators so that next sed is not confused. root_prefix_escape=`echo $root_prefix | sed -e 's/\\//\\\\\\//g'` root_path=`echo $script_dir | sed -e "s/$root_prefix_escape.*/$root_prefix_escape/"` # We have our root path echo "Falcon root path set to $root_path" LIBDIR=@FALCON_LIB_DIR@ MODDIR=@FALCON_MOD_DIR@ BINDIR=@FALCON_BIN_DIR@ # Communicate with falcon that we're in a test environment. FALCON_ENV_DIR=$root_path PATH=$root_path/$BINDIR:$PATH echo "System path set to $PATH" LD_LIBRARY_PATH=$root_path/$LIBDIR:$LD_LIBRARY_PATH echo "System LD path set to $LD_LIBRARY_PATH" #complains with MacOSX DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH #And this is for falcon FALCON_LOAD_PATH=".;$root_path/$MODDIR" export FALCON_ENV_DIR export PATH export LD_LIBRARY_PATH export DYLD_LIBRARY_PATH export FALCON_LOAD_PATH echo echo "Starting requested command: $*" echo $* devtools/fallc.fal000066400000000000000000000272011176363201700144720ustar00rootroot00000000000000#!/usr/bin/env falcon /*********************************************************** * Falcon international translation compiler * * See LICENSE file for licensing details. ************************************************************/ directive version=0x010100, lang="en_US" load mxml load regex //============================================= // Little class for simpler regex // class XRegex( expr, pcount ) from Regex(expr) pcount = pcount function replaceAll( str, chg ) try return self.Regex.replaceAll( str, chg ) catch RegexError > @i"fallc: Warning, cannot replace variables from $(str)" return str end end function grabAll( str ) s = self.pcount try return map( {part => str[part[0] + s : part[1]]}, self.findAll( str ) ) catch RegexError > @i"fallc: Warning, cannot grab variables from $(str)" return [] end end end reVars = XRegex( '\$([^+*/;:()|^!@#[\]\s?$-]+)', 1 ) reParVars = XRegex( '\$\(([^+*/;:()|^!@#?$-]+)', 2 ) reMover = XRegex( '\s+' ) object Options from CmdlineParser output = nil inputs = [] globs = [] checkvars = true merge = nil function onOption( opt ) switch opt case 'o', 'm' self.expectValue() case 'h': usage() case 'v': version() case 'c': self.checkvars = false default usage() exit(1) end end function onValue( opt, val ) switch opt case 'o': self.output = val case 'm': self.merge = val end end function onFree( opt ) if "*" in opt or "?" in opt self.globs += opt else self.inputs += opt end end end function usage() version() > > i' Usage: fallc [options] files ... Options: -c Do NOT check for consistency in \$(varname) blocks. -m Merge mode (get original file .ftt and insert translation. -o Writes the results to this file. -h Prints this help. -v Prints version informations. ' end function version() ver = vmModuleVersionInfo() > @"Falcon Language Tables Compiler, version $(ver[0]).$(ver[1]).$(ver[2])" end //==================================================== // Class storing a filewide language table // class LanguageTable( fromLang, tglang, file, module ) fromLang = fromLang name = tglang file = file modName = module entries = [=>] originals = [=>] end //==================================================== // Parsing a language table // function parseLanguage( file ) try // read the XML document doc = MXMLDocument( "utf-8" ) doc.read( file ) // now get the root's node child, if any root = doc.root() if not root or root.name() != "translation" > @i"fallc: Warning: root node for \"$(file)\" is not \"translation\". Ignoring." return nil end attr = root.getAttribs() if "module" notin attr or "into" notin attr or "from" notin attr > @i"fallc: Warning: missing informations in root node in \"$(file)\". Ignoring." return nil end //Ok, we can start iterating. ltab = LanguageTable( attr["from"], attr["into"], file, attr["module"] ) child = root.firstChild() while child != nil if child.name() !="string" child = child.nextSibling() continue end attr = child.getAttribs() if "id" notin attr child = child.nextSibling() continue end try stringId = int( attr["id"] ) catch child = child.nextSibling() continue end // now get the original and translated subnodes. chchild = child.firstChild() translated = original = nil while chchild != nil if chchild.name() == "original" original = chchild.data() elif chchild.name() == "translated" translated = chchild.data() end chchild = chchild.nextSibling() end // warn if the transalted string has not the same variables as the original if Options.checkvars origVars = reVars.grabAll( original ) + reParVars.grabAll( original ) transVars = reVars.grabAll( translated ) + reParVars.grabAll( translated ) //normalize spaces origVars = map( {x => reMover.replaceAll( x, "" )} , origVars ) transVars = map( {x => reMover.replaceAll( x, "" )}, transVars ) for var in origVars if var notin transVars > @i"fallc: Warning: variable '$(var)' in string ID=$(stringId) has not been translated" end end for var in transVars if var notin origVars > @i"fallc: Warning: variable '$(var)' in translated string ID=$(stringId) was not in the original" end end end if translated ltab.entries[ stringId ] = translated end // should we keep the originals? if Options.merge ltab.originals[ stringId ] = original end child = child.nextSibling() end > @i"fallc: Parsed file \"$(file)\"" return ltab catch MXMLError in error > @i"fallc: Warning: Couldn't read or parse the table \"$(file)\": " > error return nil end end //==================================================== // Saving a merged language table // function saveMerge( lmerge, ltab, output ) try outstream = OutputStream( output ) outstream.setEncoding( "utf-8" ) catch IoError in error > @i"fallc: Warning: Couldn't create output file \"$(output)\": " > error return false end // put the translations done in ltab inside lmerge, // but first organize a reverese dictinary of lmerge. reverseMap = [=>] for key, value in lmerge.originals reverseMap[value] = key end transMap = [=>] for key, value in ltab.originals try id = reverseMap[ value ] transMap[id] = ltab.entries[key] catch AccessError end end // now, write the output file doc = MXMLDocument() // add our items below root. root = doc.root() root.name( "translation" ) root.setAttribute( "from", ltab.fromLang ) root.setAttribute( "into", ltab.name ) root.setAttribute( "module", ltab.modName ) for key, value in lmerge.originals node_string = MXMLNode( MXMLType.tag, "string" ) node_string.setAttribute( "id", key.toString() ) node_o = MXMLNode( MXMLType.tag, "original" ) node_o.data( value ) node_t = MXMLNode( MXMLType.tag, "translated" ) if key in transMap node_t.data( transMap[key] ) end node_string.addBelow( node_o ) node_string.addBelow( node_t ) root.addBelow( node_string ) end doc.style( MXMLStyle.INDENT ) doc.setEncoding( "utf-8" ) try doc.serialize( outstream ) catch IoError in e > @i"fallc: Warning: Couldn't create output file \"$(output)\": " > error return false end return true end //==================================================== // save the tables // // TABLE FORMAT: // 0-4: MARKER TLTAB // 5-6: FBFC endianity marker // 7-10: Number of entries (N). // 11- (11 + (N*9)): Language index table (lexically ordered): // 0-4: 5 bytes language code. // 5-8: language table + table offset. // N language tables: // 0-3: Count of entries in the table (K) // K entries: // 0-3: Id of the string in the table // 4-7: Length of the string to be read in bytes (L) // 8-8+L: String in UTF-8 format // function saveTables( languages, output ) > @i"fallc: Saving table into $(output)" // we need to know all the translations. tables = [=>] for name, lang in languages if transcodeTo( name, "utf-8" ).len() != 5 > @i"fallc: Warning; invalid language code $(lang.name). Ignoring." continue end langsize = lang.entries.len() > @"Saving $langsize entries for language \"$name\"" tempStream = StringStream() bytes = "\xFFFFFFFF" bytes[0] = langsize tempStream.write( bytes ) for id, entry in lang.entries bytes[0] = id tempStream.write( bytes ) utf8entry = transcodeTo( entry, "utf-8" ) bytes[0] = utf8entry.len() tempStream.write( bytes ) tempStream.write( utf8entry ) end tables[name] = tempStream.closeToString() end stream = OutputStream( output ) stream.write( "TLTAB" ) // This will allow the decoding program to understand what we're saying. endianMarker = "\xFBFC" stream.write( endianMarker ) // write the count of tables bytes[0] = tables.len() stream.write( bytes ) // then the table index // using the iterator avoids useless copies of big data chunks iter = tables.first() curpos = 0 while iter.hasCurrent() stream.write( iter.key() ) bytes[0] = curpos stream.write( bytes ) curpos += iter.value().len() iter.next() end // now write the tables. iter = tables.first() while iter.hasCurrent() stream.write( iter.value() ) iter.next() end stream.close() return true end //==================================================== // main code // Options.parse() // nothing to do ? if not Options.inputs and not Options.globs > i"fallc: Nothing to do." return 0 end // expand globs for glob in Options.globs // get the path part path = Path( glob ) location = path.location filename = path.filename // try to diropen the location try direnum = Directory( location ) while (file = direnum.read() ) if strWildcardMatch( glob, file ) Options.inputs += location + "/" + file end end catch IoError > @i"fallc: FATAL: can't read directory \"$(location)\"." return 1 end end // parse the language table languages = [=>] modname = nil for file in Options.inputs ltab = parseLanguage( file ) // Continue in case of errors if not ltab: continue if modname == nil modname = ltab.modName elif modname != ltab.modName > @i"fallc: Warning: ignoring file \"$(ltab.file)\"." > @i"fallc: containing infos for module \"$(ltab.module)\"." continue end if ltab.name in languages > @i"fallc: Warning: definition for language \"$(ltab.name)\" in file \"$(ltab.file)\"" > @i"fallc: was already present in file \"$(languages[ltab.name])\"." end languages[ ltab.name ] = ltab end // try to save, if we have to. if languages // time to save the table // use the last table as default name // are we in merge mode? if Options.merge Options.checkvars = false // we don't need to check vars here. lmerge = parseLanguage( Options.merge ) if lmerge for name, lang in languages output = Options.output ? Options.output : \ modname + "." + lang.name+ ".ftt" saveMerge( lmerge, lang, output ) if Options.output: break end end else output = Options.output ? Options.output : modname + ".ftr" try if saveTables( languages, output ) > @i"fallc: Saved translation for module \"$(modname)\" to \"$(output)\"" return 0 // OK end catch IoError > @i"fallc: FATAL: error while saving to \"$(output)\"" end end else > @i"fallc: No language availabe." end return 1 /* end of fallc.fal */ devtools/falmod.cmake000066400000000000000000000021511176363201700151660ustar00rootroot00000000000000########################################################àà # Falcon CMAKE suite # Useful functions to finalize modules # macro( falcon_define_module varname modname ) set( ${varname} "${modname}_fm" ) endmacro() function(falcon_install_module2 tgt dir ) if(APPLE) set_target_properties(${tgt} PROPERTIES PREFIX "" SUFFIX ".dylib" ) else() set_target_properties(${tgt} PROPERTIES PREFIX "" ) endif() set( CMAKE_INSTALL_PREFIX, "@CMAKE_INSTALL_PREFIX@" ) if( DEFINED MOD_INSTALL ) set( dest "${MOD_INSTALL}/${dir}" ) else() set( dest "${Falcon_MOD_DIR}/${dir}" ) endif() install( TARGETS ${tgt} DESTINATION ${dest} ) endfunction() function(falcon_install_module tgt ) falcon_install_module2( "${tgt}" .) endfunction() function(falcon_finalize_module2 tgt libs) target_link_libraries(${tgt} ${Falcon_LIBRARIES} ${libs} ) falcon_install_module( ${tgt} ) endfunction() function(falcon_finalize_module tgt ) target_link_libraries(${tgt} ${Falcon_LIBRARIES} ) falcon_install_module( ${tgt} ) endfunction() devtools/icomp.bat.in000066400000000000000000000002031176363201700151220ustar00rootroot00000000000000@echo off current_path=$~dp0 current_path="%current_path%/../" mkdir %~dp1 %current_path%/@FALCON_BIN_DIR@/falcon -c "%0" -o "%1" devtools/icomp.sh.in000077500000000000000000000005431176363201700150000ustar00rootroot00000000000000#!/bin/sh SOURCE=$1 TARGET=$2 current_path=`dirname $0` current_path="$current_path/../" # Enable also for MacOSX DYLD_LIBRARY_PATH="$current_path/@FALCON_LIB_DIR@" export DYLD_LIBRARY_PATH LD_LIBRARY_PATH="$current_path/@FALCON_LIB_DIR@" export LD_LIBRARY_PATH mkdir -p `dirname $TARGET` $current_path/@FALCON_BIN_DIR@/falcon -o $TARGET -c $SOURCE dist/000077500000000000000000000000001176363201700120275ustar00rootroot00000000000000dist/CMakeLists.txt000066400000000000000000000001261176363201700145660ustar00rootroot00000000000000# # Configurator for the build systems # if(WIN32) add_subdirectory( nsis ) endif() dist/nsis/000077500000000000000000000000001176363201700130035ustar00rootroot00000000000000dist/nsis/AddToPath.nsh000066400000000000000000000233371176363201700153350ustar00rootroot00000000000000!ifndef _AddToPath_nsh !define _AddToPath_nsh !verbose 3 !include "WinMessages.NSH" !verbose 4 !ifndef WriteEnvStr_RegKey !ifdef ALL_USERS !define WriteEnvStr_RegKey \ 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' !else !define WriteEnvStr_RegKey 'HKCU "Environment"' !endif !endif ; AddToPath - Adds the given dir to the search path. ; Input - head of the stack ; Note - Win9x systems requires reboot Function AddToPath Exch $0 Push $1 Push $2 Push $3 # don't add if the path doesn't exist IfFileExists "$0\*.*" "" AddToPath_done ReadEnvStr $1 PATH Push "$1;" Push "$0;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done Push "$1;" Push "$0\;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done GetFullPathName /SHORT $3 $0 Push "$1;" Push "$3;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done Push "$1;" Push "$3\;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done Call IsNT Pop $1 StrCmp $1 1 AddToPath_NT ; Not on NT StrCpy $1 $WINDIR 2 FileOpen $1 "$1\autoexec.bat" a FileSeek $1 -1 END FileReadByte $1 $2 IntCmp $2 26 0 +2 +2 # DOS EOF FileSeek $1 -1 END # write over EOF FileWrite $1 "$\r$\nSET PATH=%PATH%;$3$\r$\n" FileClose $1 SetRebootFlag true Goto AddToPath_done AddToPath_NT: ReadRegStr $1 ${WriteEnvStr_RegKey} "PATH" StrCmp $1 "" AddToPath_NTdoIt Push $1 Call Trim Pop $1 StrCpy $0 "$1;$0" AddToPath_NTdoIt: WriteRegExpandStr ${WriteEnvStr_RegKey} "PATH" $0 SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 AddToPath_done: Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd ; RemoveFromPath - Remove a given dir from the path ; Input: head of the stack Function un.RemoveFromPath Exch $0 Push $1 Push $2 Push $3 Push $4 Push $5 Push $6 IntFmt $6 "%c" 26 # DOS EOF Call un.IsNT Pop $1 StrCmp $1 1 unRemoveFromPath_NT ; Not on NT StrCpy $1 $WINDIR 2 FileOpen $1 "$1\autoexec.bat" r GetTempFileName $4 FileOpen $2 $4 w GetFullPathName /SHORT $0 $0 StrCpy $0 "SET PATH=%PATH%;$0" Goto unRemoveFromPath_dosLoop unRemoveFromPath_dosLoop: FileRead $1 $3 StrCpy $5 $3 1 -1 # read last char StrCmp $5 $6 0 +2 # if DOS EOF StrCpy $3 $3 -1 # remove DOS EOF so we can compare StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoopRemoveLine StrCmp $3 "$0$\n" unRemoveFromPath_dosLoopRemoveLine StrCmp $3 "$0" unRemoveFromPath_dosLoopRemoveLine StrCmp $3 "" unRemoveFromPath_dosLoopEnd FileWrite $2 $3 Goto unRemoveFromPath_dosLoop unRemoveFromPath_dosLoopRemoveLine: SetRebootFlag true Goto unRemoveFromPath_dosLoop unRemoveFromPath_dosLoopEnd: FileClose $2 FileClose $1 StrCpy $1 $WINDIR 2 Delete "$1\autoexec.bat" CopyFiles /SILENT $4 "$1\autoexec.bat" Delete $4 Goto unRemoveFromPath_done unRemoveFromPath_NT: ReadRegStr $1 ${WriteEnvStr_RegKey} "PATH" StrCpy $5 $1 1 -1 # copy last char StrCmp $5 ";" +2 # if last char != ; StrCpy $1 "$1;" # append ; Push $1 Push "$0;" Call un.StrStr ; Find `$0;` in $1 Pop $2 ; pos of our dir StrCmp $2 "" unRemoveFromPath_done ; else, it is in path # $0 - path to add # $1 - path var StrLen $3 "$0;" StrLen $4 $2 StrCpy $5 $1 -$4 # $5 is now the part before the path to remove StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove StrCpy $3 $5$6 StrCpy $5 $3 1 -1 # copy last char StrCmp $5 ";" 0 +2 # if last char == ; StrCpy $3 $3 -1 # remove last char WriteRegExpandStr ${WriteEnvStr_RegKey} "PATH" $3 SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 unRemoveFromPath_done: Pop $6 Pop $5 Pop $4 Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd ; AddToEnvVar - Adds the given value to the given environment var ; Input - head of the stack $0 environement variable $1=value to add ; Note - Win9x systems requires reboot Function AddToEnvVar Exch $1 ; $1 has environment variable value Exch Exch $0 ; $0 has environment variable name DetailPrint "Adding $1 to $0" Push $2 Push $3 Push $4 ReadEnvStr $2 $0 Push "$2;" Push "$1;" Call StrStr Pop $3 StrCmp $3 "" "" AddToEnvVar_done Push "$2;" Push "$1\;" Call StrStr Pop $3 StrCmp $3 "" "" AddToEnvVar_done Call IsNT Pop $2 StrCmp $2 1 AddToEnvVar_NT ; Not on NT StrCpy $2 $WINDIR 2 FileOpen $2 "$2\autoexec.bat" a FileSeek $2 -1 END FileReadByte $2 $3 IntCmp $3 26 0 +2 +2 # DOS EOF FileSeek $2 -1 END # write over EOF FileWrite $2 "$\r$\nSET $0=%$0%;$4$\r$\n" FileClose $2 SetRebootFlag true Goto AddToEnvVar_done AddToEnvVar_NT: ReadRegStr $2 ${WriteEnvStr_RegKey} $0 StrCpy $3 $2 1 -1 # copy last char StrCmp $3 ";" 0 +2 # if last char == ; StrCpy $2 $2 -1 # remove last char StrCmp $2 "" AddToEnvVar_NTdoIt StrCpy $1 "$2;$1" AddToEnvVar_NTdoIt: WriteRegExpandStr ${WriteEnvStr_RegKey} $0 $1 SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 AddToEnvVar_done: Pop $4 Pop $3 Pop $2 Pop $0 Pop $1 FunctionEnd ; RemoveFromEnvVar - Remove a given value from a environment var ; Input: head of the stack Function un.RemoveFromEnvVar Exch $1 ; $1 has environment variable value Exch Exch $0 ; $0 has environment variable name DetailPrint "Removing $1 from $0" Push $2 Push $3 Push $4 Push $5 Push $6 Push $7 IntFmt $7 "%c" 26 # DOS EOF Call un.IsNT Pop $2 StrCmp $2 1 unRemoveFromEnvVar_NT ; Not on NT StrCpy $2 $WINDIR 2 FileOpen $2 "$2\autoexec.bat" r GetTempFileName $5 FileOpen $3 $5 w GetFullPathName /SHORT $1 $1 StrCpy $1 "SET $0=%$0%;$1" Goto unRemoveFromEnvVar_dosLoop unRemoveFromEnvVar_dosLoop: FileRead $2 $4 StrCpy $6 $4 1 -1 # read last char StrCmp $6 $7 0 +2 # if DOS EOF StrCpy $4 $4 -1 # remove DOS EOF so we can compare StrCmp $4 "$1$\r$\n" unRemoveFromEnvVar_dosLoopRemoveLine StrCmp $4 "$1$\n" unRemoveFromEnvVar_dosLoopRemoveLine StrCmp $4 "$1" unRemoveFromEnvVar_dosLoopRemoveLine StrCmp $4 "" unRemoveFromEnvVar_dosLoopEnd FileWrite $3 $4 Goto unRemoveFromEnvVar_dosLoop unRemoveFromEnvVar_dosLoopRemoveLine: SetRebootFlag true Goto unRemoveFromEnvVar_dosLoop unRemoveFromEnvVar_dosLoopEnd: FileClose $3 FileClose $2 StrCpy $2 $WINDIR 2 Delete "$2\autoexec.bat" CopyFiles /SILENT $5 "$2\autoexec.bat" Delete $5 Goto unRemoveFromEnvVar_done unRemoveFromEnvVar_NT: ReadRegStr $2 ${WriteEnvStr_RegKey} $0 StrCpy $6 $2 1 -1 # copy last char StrCmp $6 ";" +2 # if last char != ; StrCpy $2 "$2;" # append ; Push $2 Push "$1;" Call un.StrStr ; Find `$1;` in $2 Pop $3 ; pos of our dir StrCmp $3 "" unRemoveFromEnvVar_done ; else, it is in path # $1 - path to add # $2 - path var StrLen $4 "$1;" StrLen $5 $3 StrCpy $6 $2 -$5 # $6 is now the part before the path to remove StrCpy $7 $3 "" $4 # $7 is now the part after the path to remove StrCpy $4 $6$7 StrCpy $6 $4 1 -1 # copy last char StrCmp $6 ";" 0 +2 # if last char == ; StrCpy $4 $4 -1 # remove last char WriteRegExpandStr ${WriteEnvStr_RegKey} $0 $4 ; delete reg value if null StrCmp $4 "" 0 +2 # if null delete reg DeleteRegValue ${WriteEnvStr_RegKey} $0 SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 unRemoveFromEnvVar_done: Pop $7 Pop $6 Pop $5 Pop $4 Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd !ifndef IsNT_KiCHiK !define IsNT_KiCHiK ########################################### # Utility Functions # ########################################### ; IsNT ; no input ; output, top of the stack = 1 if NT or 0 if not ; ; Usage: ; Call IsNT ; Pop $R0 ; ($R0 at this point is 1 or 0) !macro IsNT un Function ${un}IsNT Push $0 ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion StrCmp $0 "" 0 IsNT_yes ; we are not NT. Pop $0 Push 0 Return IsNT_yes: ; NT!!! Pop $0 Push 1 FunctionEnd !macroend !insertmacro IsNT "" !insertmacro IsNT "un." !endif ; IsNT_KiCHiK ; StrStr ; input, top of stack = string to search for ; top of stack-1 = string to search in ; output, top of stack (replaces with the portion of the string remaining) ; modifies no other variables. ; ; Usage: ; Push "this is a long ass string" ; Push "ass" ; Call StrStr ; Pop $R0 ; ($R0 at this point is "ass string") !macro StrStr un Function ${un}StrStr Exch $R1 ; st=haystack,old$R1, $R1=needle Exch ; st=old$R1,haystack Exch $R2 ; st=old$R1,old$R2, $R2=haystack Push $R3 Push $R4 Push $R5 StrLen $R3 $R1 StrCpy $R4 0 ; $R1=needle ; $R2=haystack ; $R3=len(needle) ; $R4=cnt ; $R5=tmp loop: StrCpy $R5 $R2 $R3 $R4 StrCmp $R5 $R1 done StrCmp $R5 "" done IntOp $R4 $R4 + 1 Goto loop done: StrCpy $R1 $R2 "" $R4 Pop $R5 Pop $R4 Pop $R3 Pop $R2 Exch $R1 FunctionEnd !macroend !insertmacro StrStr "" !insertmacro StrStr "un." !endif ; _AddToPath_nsh Function Trim ; Added by Pelaca Exch $R1 Push $R2 Loop: StrCpy $R2 "$R1" 1 -1 StrCmp "$R2" " " RTrim StrCmp "$R2" "$\n" RTrim StrCmp "$R2" "$\r" RTrim StrCmp "$R2" ";" RTrim GoTo Done RTrim: StrCpy $R1 "$R1" -1 Goto Loop Done: Pop $R2 Exch $R1 FunctionEnddist/nsis/CMakeLists.txt000066400000000000000000000061341176363201700155470ustar00rootroot00000000000000# # Configurator for NSIS installer # cmake_minimum_required(VERSION 2.6) # # Files to be configured # SET( config_files falcon_inst.nsi ) # Files to be copied SET( copy_files fileassoc.nsh WriteEnvStr.nsh AddToPath.nsh ) # Directories to be copied SET( copy_directories Resources ) ################################################## # Prepare configure-time variables for NSIS script # if( "${CMAKE_GENERATOR}" STREQUAL "MinGW Makefiles" ) set( FALCON_ENGINE_DLL "libFalcon_engine.dll" ) set( FALCON_ENGINE_LIB "libFalcon_engine.dll.a" ) find_library(MINGW_MD_RUNTIME mingwm10) get_filename_component(_filename ${MINGW_MD_RUNTIME} NAME) configure_file( ${MINGW_MD_RUNTIME} ${Falcon_BINARY_DIR}/${_filename} COPYONLY) set( FALCON_BUILD_NAME "MinGW" ) else() set( FALCON_ENGINE_DLL "Falcon_engine.dll" ) set( FALCON_ENGINE_LIB "Falcon_engine.lib" ) if( "${CMAKE_GENERATOR}" STREQUAL "Visual Studio 10" ) set( FALCON_BUILD_NAME "VS10" ) endif() if( "${CMAKE_GENERATOR}" STREQUAL "Visual Studio 10 Win64" ) set( FALCON_BUILD_NAME "VS10_64" ) endif() if( "${CMAKE_GENERATOR}" STREQUAL "Visual Studio 6" ) set( FALCON_BUILD_NAME "VS6" ) endif() if( "${CMAKE_GENERATOR}" STREQUAL "Visual Studio 8 2005" ) set( FALCON_BUILD_NAME "VS8" ) endif() if( "${CMAKE_GENERATOR}" STREQUAL "Visual Studio 8 2005 Win64" ) set( FALCON_BUILD_NAME "VS8_64" ) endif() if( "${CMAKE_GENERATOR}" STREQUAL "Visual Studio 9 2008" ) set( FALCON_BUILD_NAME "VS9" ) endif() if( "${CMAKE_GENERATOR}" STREQUAL "Visual Studio 9 2008 Win64" ) set( FALCON_BUILD_NAME "VS9_64" ) endif() if(NOT FALCON_BUILD_NAME ) set( FALCON_BUILD_NAME "Win32" ) endif() endif() if ( ${FALCON_VERSION_REVISION} EQUAL 0 ) if ( ${FALCON_VERSION_PATCH} EQUAL 0 ) set( FALCON_VF_NAME "${FALCON_VERSION_MAJOR}_${FALCON_VERSION_MINOR}_${FALCON_BUILD_NAME}" ) else() set( FALCON_VF_NAME "${FALCON_VERSION_MAJOR}_${FALCON_VERSION_MINOR}_${FALCON_VERSION_REVISION}_${FALCON_VERSION_PATCH}" ) endif() else() set( FALCON_VF_NAME "${FALCON_VERSION_MAJOR}_${FALCON_VERSION_MINOR}_${FALCON_VERSION_REVISION}_${FALCON_VERSION_PATCH}_${FALCON_BUILD_NAME}" ) endif() ################################################ # Execute what required # foreach( item ${config_files} ) message( "Configuring ${item}" ) configure_file( ${item}.in "${CMAKE_CURRENT_BINARY_DIR}/${item}" ESCAPE_QUOTES @ONLY) #Create installation files from in files list( APPEND inst_files "${CMAKE_CURRENT_BINARY_DIR}/${item}" ) endforeach() foreach( item ${copy_files} ) message( "Copying ${item}" ) list( APPEND inst_files "${CMAKE_CURRENT_SOURCE_DIR}/${item}" ) endforeach() ######################################################################## # Install install( FILES ${inst_files} DESTINATION dist PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE ) foreach( item ${copy_directories} ) install( DIRECTORY ${item} DESTINATION dist ) endforeach() dist/nsis/Falcon-redist.VC80-sp1.zip000066400000000000000000022075351176363201700174170ustar00rootroot00000000000000PK:!,MJ.Microsoft.VC80.CRT/Microsoft.VC80.CRT.manifest]o0'? vY͇TY*iuDٝ1&#&fi֦IzUnV9Jt:pAdU0mfOݬcTcY2$MqΤ1ʹ$#7b NaxH`%bU+ʴVee5Mm%8{M+:acm؂ e) X\yC΢!kZ9S]Z%P4ER1I b(c 5\N[OIXi̪ OK|m`wgZ U RY:&}cjGw,[`x^-퍹bTSϗ`'2z*s<#a}^K|16?S|{ms3)[v1  "G(hdoA8f &q7EL.t%mS<9YyqL> aq0%!NB4 -IzC8jט?;_@PKж5um PMicrosoft.VC80.CRT/msvcm80.dll[k\uw̎ffwV=F]zH B<4,VBh啔";BVGc;;.WB\1TRPd8jMEv-9>fwFW_{Ks|}ӧ,c{k]a,A}/Ҫ#{^ZU?r±{&_8x&N-{}r*+~}S@Ow8u޷rn*NŨܢ귩r7{4v2'bNUElƦ4PIuNpJAzNJe7|b ,fv-g^O,w[ަٻfZ p~J CkB'?pJ5)]O:pcn72t~Iƾf8uzV%&77 tWkZkZkZ뢮ϼ[ɯ=Cgn_<#]~Dݗ~ Gvw\qvgN?t<439CT>s#wv lyاuGj26sz#~7;nAS6wxxJnih/WcфtSC8PNfӀ.6uy^]}IYԯ3j$U~}yQwOP%[лxn2\&F%T[n"w6އ|YveMMO6:RƓVŰ.4ċ@ muPOD4vLy9 EjJ+O&;HaVYht8LJ\ͣϨ$6@&wA#!wMwM.XIa=^ ;~^c83=|<=Dtc< צy?CV[šNح00FK@g=!8 T{HAƖ'q7L&`3ҚXBnnXc7Xk>a +fÍZǚ]J0bU[} mUR6a+4^PwܳRn,`PqEZtbL=^Bu< )d݂M[̛o~ R'FDn?zb|SHFk})"$yXaä> ~|XUc[Hibd|\›_Jli]-!^[p< ݩv 0^WNL+MvOEeˋBVHH9޼rlxRF<,<$Q'#Ɉ/246?!ƚ`dí:m8w46$.v 7Qcc)҂rp[% >~V]0jm:dH>iCFywYVLqFWf f\G.iJzHּ[w xqDlon\pLLa[BM@&q G\}w#Otqr#>ލ${ei3qX) zNi8&iِ0xߥrxsWTs;4u s]96ye>` q Nͭhfpg#\6+Xs1|▢qt. |rz|Ugp0帖EBw=hWąJl̩k2@é4jFsw0EέKގt؁js]h^X[Gq#j;;5f߇KQEo sTI}~/}eLك>e` Y{yZUrF-M^iU8xCeL7a܄"~`U,\H ؞׃^CuǫQcSljKH?MPпDIo'r2s!OGR3LUЯVE`RYl垘WwNt،tA)3n"萑ΝW:rIlC.`KRQ @t֝ЬD̠ϕtSZ'/gX^ߝ{~KU=fZ?h7y1 4h?ըA֡RsSg{>u9W'ϰn>znJu|f>t7Azv ;&4u|nmçсO-{>v;9nQg9_d[ ocq6Uj} Z~6џR5ε[PF&ꨤElBH\.!(75mXy TNסy:NX=j s슇JTpIErbխM#Xb-J NbW-$e~o~@܁<#?0P5 U-x`ճ aJ>lU66 ap l$!#[k +gGFbF;qȍml &*kfHI6W$I&8b8𼛌-ـ uv=_4im7:mZ@'3l>3~\{L2VzƳIʑW|\|٣~j][b,7)޷(GПj1Ľf.ƳVz} 0FhXKG-[9-!Aֵ` cS8zΖZNp!8 h/n|0% f=a6~=[qx؟j][iS&ŘM`)]v IqV^7bn䪴Ӑ6Z PxM1;ۈ  n髭SD{'OaCc pYhf̟0Ots ~㔾g };~{m}_{<‘+P/{(ǽ#N?-^uSMz[z̍|=n>!lŇM0tBt0o$g8KXmzq%'^%c$ښSRզ2%^[%Q8ULcr}4/ٿh: UF su:}K9Y98 99=ZGSCuqN#Yɦ5- rf9ʎ[v嫌l#Oِ<$;P .ⲏp:{CڐI+MW0g )./A7יwȷAw9;۸w0=@,ahnDK%?}50 .Jn=V= A {D1MGwݵ@>& |f.6|9Zόγ^y4zG?'pJO? K{ W"J~hNx=Nx=7.roruGxFMYlԊLf\$t9EMr]оVZB(ji\ͫr5w` ai\Zz10xW O/zECSXN|И01+DגaN9Z9p~?k7#̠jx[Ln hK{rAC0_fJod>Qx=#Љ|s2cnqTٽ۝ۙ,ʖNIٖdlBQqTX'R1B(*H) `G*C'@NARgmv?__m: DOZ:|tP ݲrfTݦ \sEIᕪo+QLȲيʯKCv{(.[ޚ`3`P;h)mD\cCs85bU/ڦ^,-3iPs!>Hٴf:hl:mj3Ǣ@a~҄s^ˊĨvq\)mm e )>|؄q剜\} tzVpH+8 T"TYLHz~d[[ikض;2m5FzA hlWƚƚ%:?uKct1hZGFdi2F.ivf#hCHl6P<М`Cj;ݤd Qo'e R(?_S%ΗVbW.P#8qҭI7c,[b1~SnkBxewgul㴡l{+6eX}ɞB%?XX舉a% N3oB{Xt= ݗ~et@(o ǑGX5Fv\BE%,E7.|@ ݑ`YUV6(kThY Z}H{vE;`` k1&\":cM<#E̕.zC )D5UB9҆|0R `@%yJ40e 6&,R$X@v_sVd8̊ĊکQ;lI؁t K$.7gXs&zd e1!z$ wKSΣNc oϛcϛAz^Kp$lPLRc"߀\ %=ˀY3~u]+9/޽T^/֗DtE%֜vɳK)<˳c7r+Qސ9uu"xNtʼnR UٲFExDiJJjRa* ~.8|&IuSx(+v'\)ٳ?|(?[i}H~Py,m|rTҽ4)$]c&`W9Y,(yO KƲAt5RA7ioRtbϹ9]%g# Uw]1iL?^'dQHG& &j[9_9MUmSh% )g\hxa[Yir >E72t5ed/7$`'}]W^M颎EN\Nt $C]҃sq$}yj*~QesY}E/2=א),eL٘fj{=DoxK'gr__X!nm+ڑHڗ'o<\9\;g!rǖяMu?Z_hl.CձKLb, ]WkgOE n;^6;ҺyJTJ➌eGy bסBC'KwKQ Kmظ1Il"MQxrudbjK/h't`w⦑aSB?׺q &C_Nȣo3xt; KR8,o5qʪ54DǡkXtӻ2ncҞpѤse2zl!3;{Q1sF7QCXےq֤4ƺ@c/0Gg&k1Q8& OrNήl ކ*rDtD{[D؃yF}8q ;XruYsqg}D5";oT?HWs@>ym+wBCǁ(F20FEBٜƹ͐3vL$52MqrQb $wэg#PRG[GmUо~QCɧ~F6~+}{Dzu H>rwwwl{ՏƹyV?[-> PtdsȈkm{:Np.Uɺ'dp X lcfEKgg]8Xǟ5xt}Vͦ>XZ?<Wv k |wү'{A`E4>G4|ܐfnH3Ʒ6ɢ=MGͨ`ALC}Z4H~_= }c ^8#ĉ|C=}^//xQmu֎bљeE9! ֭Z(`W%oBD9bH._P_KK#Q?=bHl%)g2@i[f5_d.X6 X 4eNllY3iTǷwExBxO|zM ,6gG M#SfR_ d(JCbTJ~IeҫZu畽 Md6_Ԕ// L.6ȭj_1t& wŷ4#[0SZ8z Щ?/BCB%Q6U*.b( ϺPɏ婶{^.Ƌ! "yl߰);ЦF"5~vz =~BKGݍXݵsnKPf~ e3X#5̶EoD2.CaCQ籶1(Tb j l/>x92ctnbn?<5D_%ٞJ\?EmA\y!/%{M30NCW6Z֥ #%(,2O"G4 I O@B^#om_4#k?=UzbVx+Iڈ6T5R=IT*#ZI>'M{,~͑5|[\_JzF:٧J't2h o:VnqM|cK8ᎻBPUWSr&D>wȥ܃ Y-^X\Jcj{+bOKUks4IE_~koB(#[^}M6f NLX`џ167k{?==ecHm:,\o[')ղ#h_݂j]3㪰Ksc]`|C]:]~a GӠE|/nƽ#E, EgK8$S(>!c%" lxs+c~, |V(wC$pj ΀B!-pд n$gs8. f fMra<\ÂX粠FN?@/wM׏+_co_G p)j vr^%_+$]\x`E4QS,"U OV?:ujl(;!c+7{n}|(齀E mt)O]h7^6tߞeQїsfv6g7}g' s4@gfA)1ȹ~}N8*qz<~U7's:W3XNjwH^ziL~%yVp~su</.jTc,>)JcqBv5}q`{ǀ9n&|`} :x7ۗ]"jHJ7P荴!;?`SiI($hyo̎h~]s}IbhܘbMh$yniyt,AO.AOmdl3# z"<gރٟ> gu,NN{;QOz@[2&H&8zK7lnUQe[7 ~RHDSkW?yCA)Ly*F\yU28L@#~W# S .*t,DU0 l. [-ͶDI\CW ۴/Ճpx2 2,o[vh!bߎcP_Z,V뿿 lal0|Ѐ#;'o%?j6˺#/D7:.h7b$/.3[ FB&'\Ѽ-qYWsy7y8aߦ~ix3~ +LԹ[e'f.+|sdrmZϯem57ȷ/ANJ&WRm Q26# ɶj[7AއW\SS6'Jh j-Ai+K+-yIsWi+46 А~ aSY==}W 綬9ҟگ?'_W_GI)\"9XVdkeUUa˲_-)r2g3`ilb~B~h엑h{% _4[M'}MG"{ Ó@PNg1@تߪV7r?Oir;{14bɽWDb S 氏Qq)g|f{&qi86W4G␯pFᐟCqɋO ␏?!1<M$8#=Of :w̺a8KӲX2e)9Ϫ/{WIqggfgggYIwnt(F +0q8`p &+J \ ?8Ic~ĿJ`B(l JrH%EٙѮܝ 3ׯ_7% , 1 oYʆ,m*, g,;:mj\ ]O8c,W?Fwj1 ;ctd1 ;F1N NJУM _Vi1_Aqn]7ctu9F^]t8VѱMx"0+Ÿna]s%&wŸn9_^c?Z k%NƳ,F7.#.,(IW.;1roatG&ѝn!`t׮;}Fw;F7FwR̞t F\9!ѶFmcgZc jb+yLM4&,S 5eL1! g(3Y\|}'KqT:~5=U܀N+m׭9H[^^S&ZIll7f;V1ǩ:Jތv#zE*P%BfGd~ ҪxmN*A3zB; T_zڇ ,uH7~aH`~YnG#ƒOܿ,LRu\^U۳ rےt-0ũ(983# hhSR#TOVL%Hme1%Wĺ Q[1?2۰ʁ\|Kqcyuן3q .VCV/kG*(Jǒ_W1w/S:BϫO~R*il_I+iz$SXtk&~* L\ (_H|G%eJ|IC|v8[HCx|aU_W­>]*ۃOfkuY!?Y:A)D-!h/P8Ia]Vm}F{X2 i'i'rU {Ptݤf<`"<%:Sv}s~}l\ms͚ׄ,]>CB hIm3cTؒ̒* pdCK2ZS YTh_$k&Fdwר:_\ lR pŷb]Lh>JAnm_l};^cU!o>fle֤ut8e#q V~(L*Z+2~pp~@:qILiI0+Il M_ΆXuYx%}@TMLk,_ѫ:ZOvUѪ!] &l2yKPz}YyK”1Yg0v I=5;mQ\u/ !;N%ht;:cj7JYnf/;X{;יJjjⰫ k6Am0 )Y:άpsF|B]Kziًghi~ph3ߦ0t3|{"- $i{r?^t]U:_P7³<t]%Y)zNʶqEr8:\qPϠu$i.3Tə.`B-jnm⬂C'! t|DpM 015g$d:QtK0:$$Dt GGEP`1 <{O/C602nQރT5W#+?%gmuvJʶ WJ>I~w.2Qb{Pdt` hx%Zr¿AVPu͇ eR RT5^5'UV-I B4chP7JPOrTTcoI`Þ}&t=;kP+*MmOBGVsDӪ w74,qnP6bFTk%6`V3"'xYzU=Av'~GyVLB͙QZ#YęrL4lBIꁫz:7Bc{/$j" i,6VW kqbJk\gc2kWWը]gUYh(=6'J>-`H:mn!/|`)/^`TlڭfhvBZlAAKy\\ߕՋZʖ%8fK\`PgԚnͤ*-N!xX%y-Rw8qꓒrmMʵW.£6Śs{%qwԷ!Ḅ wd:?W9iA%ܽl֖HĤIa7U`Upl3m@ef<9%m3t9eܭ?YOsљ~Ud9(r'Q*>KD"|X N#n>j`т=Z u4ъ/+~ŀɡɁK>'Ww Ә9u<&H_v2cgT*|91+նC̢=I9+>n#8/qI >WrM`<=AK2ǜEnSLaNmh;f+k#o -o97#Z5n4Wn4n=qpLAS6Avk EA\u(̧Oj'znaŔ{?] ibZ=N &X5<8TAq4WCLAx b0Iu`4WF ٱ9;nFv;@eE⢄UV .١܀D^f7IE!6-Gcۈ8~3ܐʰr.3%9y\T;1!^L$t/E-U$tOO'97yN,G["j+a`zVt`U|N0aZҷ M2fR}#rlVA]^GdA L)^Q|erE+PSS+"Rn[Ifv!B9/ZN_ !p?J}UZ>8v["@Z*5(N jS{GP#ڹG :+_; {!cJ0_\xmlhނ4 $Ĉi֡H32.p`6#u닄2֡(ԡ jɮkH\Kpq-=S貃qs+ܨ?욕Rx5fTTAh1|R! uEL㩈~qJџl>~[.?ΪѵbV5vQeFu]qrfp Hqb>(F}^><5[>  uUPI%?Tp=%tY~*)Sf>,ZL2oq^xFΔ)ϫ KlޑYek"ti(j2 -⪸.CRvB^"uƬ0:A$>{9:;h!=j$/".@hv gpr[7hn:s \'i-G@{;YP];hX=掊O9~W; x24y%3!K G3\V~S ''#COMʢrzkHT\L1zΚuյ%uTl1-z+8:]5&욍J8L2tXmQ(eۡ\ ޖ GCW۹p5i%Ӟ]p@s9Ȯ&zhH\5U@B\]dcn-_h=DDȮ#!2]O~ieY eVC.lC̸:+Gv5 ra\OU-ZH.嗸(ZN^f:ECgǖ߀2ղ̩Ȝb.ӬuŚ@.VאUu1㋰9%dc g¿Ϩ*OɾEKJ]Hy .O=~qxc(* Ud}X] QCb۰.V'{eX!ş2aukZ|Wv`Jv /(wWg9I.Ӟ-=0CKtڙlڲ4 ,-[J?89Ml.&[jE{| K4횣cJ5*OXMs=mG~OmѶ99SZ>ҊfVNM,}xHQ?mp2Rd.hz`Yh!'(`BPAt /2J sPT)Dd%:/-, &=  %W ^U秔0kX^~m’A/NpRUR>Gw+AW3Kx .dz>],;kn󄟑 !AO~>6⺰;A&rw1vgys}۽}c'YkNM}mu\'g wlz?\/(JR`4(]('!qp µЏ$]s{I9kt3Kt'KR^uYVN/gӇ.;?q' ϶ډNbO q+sHqW  FJjL]:ʼ(o\ Vm?+ ߑ(w/h}#E] dT|{˶Y}XwJg8cB.aՀAX E%[+my)jk0 G m#J/=[ڋ3%^7D،}DŽaq/yxI9PseEI?,8{*dy)<+J߆e 6$}SwK΄뽶\+Zq$]£,|UE1rW Uxt52{^/ /ۗSu~'UoL \;=(??͟zU-Q2cu\M6sX•N;‚u)6z߉93]-e^Gǫ0]H ÁD%!K-;/5ƚoJ73:* 6)?~e|wFSxuar*@\|[b"dפ4RsvoTNEܤmY_ÞB_66CG>{FYhۊ߆U-#<:DfIYGV +';l(x޶1%?mx/ƼUl7XFa @YYnpu/83N[:@z{.vC%.lw;Ҽ!m=2-q,֯6(iɈVtqȭwrGXk8= by+-C-/h &=F=T]mW^i -؃^󪋠 *L{N˝x 1^)4FܰU*unZ>7*ʩ 7[/GCH (0Rw;@v5|x9u0gk8 Aߕ}ܤ8^3鱸7*PVIqs*³4V/-ǡD#B& W#q8JC2 %6 u `8ܲ(n)=`nnG7ص|Vg0<|OЃOУmI%6(3;8Ϛ?W?Kj" QE\G~zĕ#d,gPsb; x3BΐIF$FWK33ar FpM9=l&lm;[>\aa0`{ d 1"6",c8a"A{UQmv*֒Mi VKǟqȝ^~@u2ѫ cR^@oJ|^tx>AJ| ۉqqk]Uluܥbp"J%qbyѥXX{&tvJhZPI=܄-M8/ʮ^p6PYBWB+S߳W/a!<~gEB]BtpWy%tv)zSV%t(F ߎx+5^7c0jKJ+gT[fCzs5SjSmpbS}7]56+֔^Ьj nw~N7d~A0RA[lԊ|auHLU+ah m w2bf-(ԐvȰ5sԽzy:Uh(>>u[-`1/S; :eL!Zѻ.:V!;7odڅGVdF>֛qrӕ &82u96{g5j+=:\,}t3GI| }sjmƞ{X(P_g߷c}_avuٷ$}JtzZd\8'@Rڎ#h#2[SWFQ2<5/,yv6abhRaD9}~'6lp MqTs} ful׫*{5 ]:zfVб®%KOޤ3POIxJ{wX*jʯ/êZm h2Jp e΀Haay:B _Z1[:m$ď-|W|=pba,0b$P_1aLw## :S|Wp5s1M+_{ =cFVE5c:#u8_cUf "" #*#LHVơq"ϲx;tB[AI7kjpd Äjh(ƙ'MX"Do^xflҩX5>KfN^ޤ!r&CJ5:eEz'D#D yKKސ77@c:/e( tс/Ȇm ƲQI%^A&q< B&~PF o&S4Rjdws;uH Hf\$w\+ B+,[Qhtс´ >b<;((!`}0 &cWEk]XçC\ `Mֽе&M! _)ǎ:{i'l`{h ^n epž4lmtx-L2}_A&xC1ћ(%B"Fc FB17Z=(H(e$yU@L; Qw>OþO`On\n&$]3 !2Ӈ/)-LgS+|7%bPIԲAj#E.2Gq=HlDH=:u~ C?uM[4~g蟒= ໡+soe/*|RR0I,t?rݐTy_Sy>@Bt'4Z:_/zFo:_K*CN"'41r]1 3 3ʛbY!s<ƙJoaG3ⰷ=m$PER&uR"{.a`ޝՑ9\Zs?|>4㵶gxD0, )dl;1PR †a +7>RP/餛t$Z@)YaN4BA[Q͢LojY#_@ڮ@3_37CoL zLgyJ"-X ab!R2YR fE_ڊ>O%kYz>W%^Ty%4- DÆz܅z2LvdHp#D"U= ?Q#0NyQ8\pdΫՄJkst!2Рv,lE ʻrʊObw2e?؇TRRG!P PO7qLO" 4 K cf-xZNA1CG|q/?/3s`,dxؖ Nk?9YӚ=Z=:7變v~Ҿ %^>Hq1~*\:}>OIu]AcѾ{1ث$vZԙ"5,YPoN֦T[S$D_tF%:73JMQ&=[pQ xTeAe+< !j>5}[)ޛ[`W?H: dYgVեti qk`~P \geRRơۡ,&o [S,xw{E օ.:bI=jҶT zpCdz=sS ?>1ts6BljuVj<"jIV(т}cbQV#q{+5G3<V**`|9{9}!_mYcVw zoݩmoPK7D%PNW]-ʝNc#*d8Ww.3'q'S[C}C4}שϲugV[ޢt]>>S1wA#'+bX4YC>~V/=5p'D!1&RDD$6K*)Rݘȩl"(%+ 68JSC*Zwzcs§)Ю)Sp0'nV)܇)\̦+)rsUK 7];3t>֍{ N`8'Xb8#Ё4fn!w+l-#r&˽y$1IbZG fs~^O끎8ݻt')6Af;`, E2pR/}iJʣ)!Q 6!`Z 3y}x Sh7~`-iq盙ݝIٙo~||3;}N*%U' 7uDjJB|Fa`CH*}?˥خ#,26i-"wmyJ}rмu{X?2_G6^:=i4:tPnB/[Zz_=_(d^%u:D;wשG|VP(7J4&7M}8 >+T:ü4ty|]$-*WOWk}ܨ9C)΄Zf1}L h%-z9MKhjG%}>3kyXsiJhJh $-BvUgT~Q6ῃt~yWt.͊"5^ ;tQˤu uT/-`wن,Ӝ7 ~Z+W3*PbǪcY_j57貱DVj<-Y PqV!B2Ѵj wAˢe+EduOȓ'FۇMS)"-Mkn%[K~ڪ @:F㩄9c&C6jq.k88E'ih*wo0GT3/!PGmtܗ!wTw3J!ӫW­@z txY $Ծy3xV߿ɽ7S?}]h\şOz+u!8 ;9{9+5ℽr1qNձѡ+-aøUi _s?Z/p 4/p~[mv|Uwɘ`-kp)6:fBk޻[%>&å^>&:6kn g42!J*ٮcwܚw"]c٠:>&\c~,B-mW odƅw!½uz`ϴ(kYjN'W,|#|?w%ƒ g/4WDi`~ɦ`(?9 b|E@g:Q:휄\ H>OJA::W $t 1t&ϰΰȶVS߶_}~LgjGp:^(Xpr m7jK {SjNgިgwu(4_>Wfwzkhmzmp8]673rW 'Ir''2!]顿o2GwqoaA% a9Mg Iz~a{%Z3&MGv$}][ǽIDN2H+g.j^e"R}U[3>Y>)byG1#8q_AuA=nhx%l\<?aӲK=?ap1ʍt\,87]a3@NtN}~:ékw;!t $ F6#ߝxs^N *Megjt\S0AT#*O8u"\:k r0)s[#^D[^2N,tAu(teꃪ #>*,]}YeIfdLnXDYژ,mmL6)K_fjdi]Rf!d$2|❎t2dBO{ Q,1oaX8LylL(n,6NUw~9.&5sy7yiۂo-]+aWt9z=F U#_4244r4 j:!_Ӂv ׻w>{ +^Hoa3aۼ1-k?vJ֤;c֤ #1Hju(k^T?wĪgADΏOr~]vH+S[20W16b2I$>ФAQ*Ң2:-Cm?g|1弆l?{Oe2PI32J':9TMi$h@ 77Awҏs]D 2qR< R8nN7TpÉ͂6!Wm_o)U굏ǹ߾;7ӾJ>U.K*bgL<F3tGB:ۣt"^Flۛ)[vge<8mfVfAYd|['9l45j˖i'l5\NuuAKf v&+7wԈLB3"sČLČ4i9w4cMC b><6DcYM6UڥPiچ>Hz6}r߭{pJj~?@:ؒ*Kz 0}Z}ZB{Oߕsü[)Om8Sϥokc}L s9؜\lFXk\)|T{=\/)zy"OoW@7[>=6%tfٙN ,6?aqk-rT{AH1!ܗI"z= ,͆l$ɘlq͟qChѸ_$ Itn}S0pm|B/F=o9E$MKwa6:~ w`3>φ|x2.YYϳR>Gh vۓ:5ץ2N+B~$4L8i9 u--2`tիgu=zfX= 1sKYƔиzTH/T~ "lDrc "ymT֙}!4ۍi_5J5(*LLA]^Տq\?W1 ;K =$VEt&tFN8;PH$lEGl'\YԵ$m[u9 Nfᆧ$r6$E({Д?0|sl3r9eTAuSkKv- Wk{nyn:8ݡۄf`/ru9Y.>܂?tޚpVB_ÀiA]$f"kb&̑^P_X b#辪/" Wre~Y%z_AJv0%~/U(&8PE֘T;U# E\5:]ixFh$ @2G̕~O>D~$`F3"&"k iLu:-XmQuq[µj#. ∋KkU'&(2 c..QT3$ d$NwSu3/܊/~3B}_#`P}g5fTTuZ}qb&/fx}1CŽ2@ ֘wT_}̆3Jl 1"8gD`  !\/L_P_'K}AA#p&/6/靧mFlF$ m:$4h\8lW6ұtl̫!iD_M:5KK?mmq̡=۬^5G4 #i~_B\]`-\ϓHD|f|DbQy\u2:hδG?Ѽu?Zw^y9̀&ȸ |LZK1>Ἱ歭|J?y_蟝4+|<`2(BsX+/I=s1Bsw֊09!{>P \ҫ~Ƨ%|Ͻ=2>E>_S2>&Q]hό^U:r:chJ=%Ki`=}t"s4v?ZA@)Q_ji{}G+Nz):BLii`OQtOw|O|-|1~Bb&!CHfgoPL, 7:ſ./%덟/]F;?"ƿm},[ T=ߺŏGm=? Xmb"b_@Q//X? [߲1ib-?V&is,=D\SE??go[db, Y=+ _S:e7Ϛ8 &F/F#:ʕ3mXl<O^wRUTttј&nD146"+Y[pLo]+hEI#̦ItziDeX2?]ғd姳I(%ȣD(1afu;}֐:7(s˓r'כՏ[[:}1:T+? Mi:E޸F7~Yx ,wOfĆne$[;6HMF 95vT:r&,MOE6-~ . Kd@:!>tB娅OSb8i75 X{"X,ݾvѥ}E/7[x:??"0 wep7*.!:%ۄ3U}yyL^t^^$^^ pW˫!Ԫ FuY|U^^nojCU^^n_RX^>9yv(AKOځx"iw:= 7cS;G܋ ҿNu( O9Yg8~n-?Bˠ}ˀ#zV/[ÍΦ .Upi; ~9f vF̥i/(,"HW>mߙ,%p6Sبp);m +k9\bjϗ6#NA]W&P_P-ٰXva36md5}^X+OWf`{`wEwMcٌkg֭4 gAux8T %T}"_X7~`w_4H:~[t{h4=FғzM b1h%9$! ۉl8!JBq~⢌&!vKYXH$2`lF9޷2=azss;s GT 6fTZ|zu:`~d]+[Vtn-8V>w&% ygE|K!{}<'t?dF@znCϽ<wf‘,lM1RTRӢG)3vm0d Lqk24JKty#+fP 铼K}J}؆k@RWضP9ɼm>C`+9!f~.㘠W^,$LmcjQPV |zS@\o<p,Nn hB1x)7}TEB۝:p@h<^^Tu3ͩZ^SkV{C+j]pp]8Sד]߇o{[7Ui6`}8 6mb$@X o 9 t?>s/O{8@Z$1l1:V  \,ݜ:@9 8nwHtq `Qxw# Z[#fR`T 0i @z[ꞏӕ@&0RÁt$iOa>|@Ė>1*TH4Ўd|  QY8p+-11l=@U1iu 0"6@cT05XPM0L;0ُُ@ϴ1@M04B`Q9@xN5߻SS9b?8 : SR_{ |h.Tu-ɆB.-uZϿZ?j.$ejC8 ԒOUTҜLÜ*31¼F8zƟB4SPrGKfByJ>x<3Ofh7b{5KAT%Y~1:fJApۀdE_tO[&x+am>'YYYV *TF~.G,TMynnKXN4NΉٍXv)4nCcS^OCi=!WX\-h &_ 2FKǡ% &5Br|OJݹ)FsI FG?Yl J|i<-8r\voO4-Lŕ=.{ ZK[ f8Þ٢Ōla(U Cl5Թ4MYFpƢd_mKi+oZni9x ECNl~*a/yl{\׌rfy-gJ%lmՅFp{RWtLU%ɼJQSKÎ1ßÙ&3h:c5 p9Q H̸Ҫ\ɼžPP>yLLj0 8TR 7L*^rW2Vӊ̏beA^@.{ /%'̋'HtOR Y+j5{*A4)x/QNjK`Y,id,v1}K'ψ}L]< lyZB|`|5c!Ql,I1IR,o9ֻ2T3̊bJ:q԰L^OvW݄k\,,}*Q1U*^~=|;Z݃hWhw}.AhDW>5Vऩ|2l/ug礩|[`r҂d}7|B{du9UuN+mdXFϳ Gg, e. j8~ kkt]54"m2r839jfڱu,eFDzTUG|70ņo>7E WV䋤.v|_f^*)PX;e;CBX LEo'n;ؚXv*7Bw Qaf?Ty/V+&r8'4IqKBr& Ͳʬ9)=w譀ȽC3.ɓ*9,eYB>Yjf ߚxܕ`e=M[Q E\7:S?A㿤/Nd:7Ҡd*K iQba˰*_Dy›i>.1^l%VIey٠f/pǼpa+9yQp9\o;z/aqJeK>U䓴{|v: uvJ[2h+\;!9GdJ^?dxDDoz)[ރsk$}C8蟗ü&6!5c߀w'xU^-7ݢ.u;cv}ީk1!EqǙNȎDZ a'BbQ{w;i$O,XIIF #eչTR$M gbLpTH&i38)RH!$]<5hayҤQbP@~{nd9әh}}e{'t WԒy{'ԜLʽ ;kY_wN?ZzN(z` Ќa{''*U{' 8{'TyN\ Ϲbϳwe{'4nd{<۽[L*ޥ7Zewmg1g:496|3"WS i;Wro=N2,nuy3WR935`nO+:k}k9z_S h#*qш.(.߫Q)R]޶wۺ1゙sr&~?}Q9A9GG\/]_{eg+uۉd[KvoY[$zVY5v\baC t|N\3CمVIeq ,-`/hKx^WU.cRhm{cs'[olsNi"( ?n^6A5#$\ zB`W>,Y<I59-2ZBm/$l}Xݶi@ۑKh;{i)mB\h7֝'^o(ΓoA9?7gy|777o79q_m׾WwLؑ`uŎeN t9 pbNN>kbGc5B'"4g/U[ ,;vH< yD !!ԌaaqG5@Hl;_k|k3zazBgҁ"]'C~& oS WaOd:q]mF93`~9|4wRez&ɈE!r_Cz bwDIXS =ۂUIwH*E >59dybtѾ . CʅHV"#33-[7šlh$ 09J ANb@ИeJN*=ܘr Pgh}$`+F3 N]hmͮXnYٴEgRR\\ c)(3X1.y`}YKakW+ ^?2rgӅ{V3 emkʶ綿Kg7}z[#Mߢl݇FQU(lΞEgv=;5?mٓKg>GF*'_0{ON:kWg@]]ۮOAֻV#p8{_t'1t<FvHf>mfO_ƔߖE/ 9P]~pWaǎ҇-Uz-2||>~($. x;AZ=jYLRi6dFǘ̚DRsa>3nwo9KzMip oqGF¢0q XSNL3 С E$@9!WJ+r֨+BFE$i-1VM#\C8> )c}ϕ %Gpu)/Hss6lsKX["4Fktd e4hN13иأAȒJ+Q0d0#B=!v5xDtyẴcs d7xDo+~Q1n4P܂1OBO2:텎2/PKX7-N?{X?:#՛ZLJ® q=!"(fftEleb,mfI4!CN"e^;SW ޟȋm׶-vAZF0Nj9oɭ,i_Lh8 KF)ވG%:]~δz_8."w\莋AƹX|%\UY0krY|YG/P!AH/CNu> YJpTKs@CC7Rz5JښD%1{O);K~ُ;.|iSщ&*s#P|(I*YYm7/>?wL4VUձyb:g֫xHR&3!vbm %SSR傜%`.d=c@y>|)ѰQLźŌ K<˩ cXdheJL8ں"t\w {MQi ޛc\K (9u :KU. ]֍$d]M(P-<:e@*VWqrAxWIc*ۂczL6c_% = HW&걪z*2M*wetCAd :T,P#Qt0ee'"DH<0ε ,- PNՙv!E]ǩԿHk7 t6׾M0fV8}GXqPU[OpG$CDZj+͍j +Fߘ!p#:F}Po87qfàOg6/jepDO%E?8V4OwgTxWMxCʪ^0~ 9dߛy<_/ ܀TY֨BE %ɔAj2$4L&4z[-OѓM럡'ٓNEbU14yL^ArV$ OiB;D>nre$p>QiB\;6-[ %TKλ.99gIG:,%uo̴V{vpP?_W1.&:ysğy#Yv7k6,g>LjW+rhb9Г5@s@ @Tρkt=]Fp iܿ)L.:]3$*|p὿xIsǥf[ε̞{2$H2I@AH4\  .I0`E,Uk-V=A)R "V }Y3=yw]{k[־Ϧ,L[BnF1T  &YQǘRX=#cnWT1׆A=#,4"~4JN-E{fcLk:;"鐳b`ҴKA#hWHJ3:Aŷ_ i͗OW,f嵭Tk&n&Å4_xa཰V%/+m"HY9BepYduN׻jLR1L3Q-]+xC,_RApIG=Ê砧@&k>\Bif2 QMUfp'ʏcPʵR:#)+HLЗۏų$8*kMwL '0ƕz;$#/bRt2_vd)#KR: % n2|k:g<2ᤡxLBiE&j2s#o.F9PKBjfZQ oBiƉ.Tͬz'&b1/ >CXoNTٴ7Î6וcb֜d^p;R醔rA?FJ,N({h+o,,/__s?7ts5?K~Αk$'ulh fߝB(++<6|^q/#fZi ?JIŷZC|>Z@}u}Z;h/8c kڪ5؞1cK*qmh}VlˊpLo'.[s>>p7.(g$_cXǍl[F_[RC$V*1+y](JMFJ{84QYOXbL6V`#4Bq$7rCeG}#4m5-Q+Ane>tl}4'%B]o(u(Iɥ^ &An9\ f[ ,Mɪ\:&D~Ÿ񽅍!] L aLMxK1mMqk5g}oc[B{Ȗ J,ւO1O±e*FXt뱖݃©6a-33aY]EV.k[8|E6gqzةdUMӊET/,:3Yx'3NK;fZ#f/ӏ*Hʻ&ʧ>{ 8#)߷|<7eYo|'ə}zo LdOF4R0sD7R,6ZHHW;(Fy3h1;.ڔU.լpVQhS2)Vd8G6 ll6\ЋζLv{T2'Ѿ^?$~+' MSJOW7. >0ҙ|GR$?c+}%lҜg$eSE]H.^ZMPml zN{ &4#Q=n2rtJ)Y]'s/9R:(\=^,5u^IU/NZ d N'NG2|S?.ws)MRMjEmԊYs})mR>PضF;?5}E:޳q8Ğ N9-B:$r3Ѣ/No1Iyg.E9"E Y4*~r7j '̄{N@]Qlcީwq3Ǜ\ Qy9TO!݊|TYk{mm"U_(:}W|qJ{<ճRI fuYM RQ=b\}3PrȒKb$eVk4O\iPf] neeSsԆͨ9iԍQPf_l4w+i/msRZo=c?LmwHlfȯoYdU\҆.ЊhG&$Jm[I#U!im%ʴ%uSPuȬME[uF×]POڭsϠ!/GZ՟4'" LSۏo;Ϧ7]6׺`!ƃ͒SK]md!j%G> j4~KJET'<5\:k"wE c/mo3؈ƱW6/)!_Թq96={9jZk5s]Ɲ}S&Y)zO!Wۑ%z]uBcl5S]:u¤țJ= yVF9G_QT.lG!RQm&S| ͎fƱg&VNA^ F{NRpC8۳LTlc@q;ʛ>UW}҅MS_#u>^+/,7}S{O7}oTr -|g5v>ѠncG9p,֪y,ϿON y\>pާzSSS&g rDkMY|mےr]'KnK'u[gkR/E%D nL ^M܄H$bb=v(i9e"F-.b=3i5Fg[YΖٙMDx4[yt#obC2` !@Gh,>iɝ,(7 %¾ťk%&6$؊3;)-o"Ǡ%_K7[%fzǢQvS4k1o4mfWiME~s)F9_O_RD:!w%jcݪxcF"N#V[ol7BDcv{%xÞVҷ$BդHehDd:=P(z/tN~O= mdɰY_fxo 9~S }z?gpub<Ŵ$4X>!^!ג\KZ .:%=AI  BǠ+ⴥ8N=#=H5آh:@hzDztTe!qHHSC65]A[K Agp:OCЉ A*+mBl-V;!Q0L  ۢES-{ʸ!ra۞r }tME.ߒ=F]AGUt=.{SXHd3j)(;^XM%d䌅8n,|fGs}׾{q##G\xD] _ &My;;IVr9'2k >-*0+?bI8d^q?^{\֞)l'lN"'lpŲTq֛FI~{tXMOӥǞ| r>>Ӝt//rNszӣ NOqzӳqzӋ~eNp*GS94ӮrƩ NZ[t(t NഈbNgp:[iVsZ*N9mt8fNpmNINt{9}Npz#-NOpz3崍 ^KN/szSfi$nN8M+q4ӾYirڕDN8usɩS }i=iໜtG#]RM ̿!iqCElalD_JM7*P&؆. 'ɿNמ6`wA^A^A^yjYy jI&_M VB,=LeL^2u3gA^nvOC6C#rHVrLB&7Xn*w1#.i,%KAݠ_ zIDЯ zYToM>o"h_Ы^t hO?^v5=˽Gr'X=Uf[rU#aߩr=ٿSk,(Wkkkk!(7RPo)Ŀηj;ӌVOr\*l$Yn*ȿH"mPgc7-(#(7FPo8Aɿ%& J,(7IPoP>B|Dmg/#=Er,*j$wVQUhy>fU%rUn9uLPꇚwj:ϑ7sz.vNչZg6AIR J}BPەR߮2gٳ홥ڳRSglOd:Aɞ5= ==kS0jϏ~m'_vUn ߮ݬ3nA{%7 JvDP^A͂R/(}RT1l?~/~mW1.ujVJLPAAm?lHP}dNAR|?,(/~DС{- nZsj3nRR-()Aݿ=R+(YA{v?'(Zy0_w8JhRd@vP/-)iӃ{hD^$` ސz;yS 36K72na7? Wc7*x{'h|=؄Ҥю 4l77Jj x a"0h! 趀R]9I;vcmw 7oӃ/5?$Ȟ[LLJ>;xbtƱ☚)/VGL15SqL͔b1QbqA)vw JҏIhj nhsѦ}h vN/|NPj^A8ٳm0l `̼H4fd"ѻ^@1Rj10B Ŧ},$ Cl V#U0v,#Av g+Iz龩s8߬ ͗JE@6Ul"Y8VދM"0isn:a'AvTw39Wi_aJW:IPqׂ,8`*=sJqA)οWMZquZgD=Sl%J/5ԱJqYI9j0vcg;U5h$dykla7;u[Su[SU3A]7{;Mlz*C cӓdSM{;PISH=-dg@KU|3 db{tƱ7\Pq])b/WPq]T\WaJ7RػIPQRb/{-Zh15||Ң E$x&Y>50G;DL7vakTᠱ3'$Ǿƶ] g1T֊0MU uQq ¦ƴ~yͤoEC$-/00k[̡y_?jKJLbKuƱAP JtKXZ'(RKw JVPFA)8Zc|Km-ߪmVmq[~*(~A- Jm,('R[rjWw컠ڷ]P۪3g} J\PaVN2] 3s3#Hi0V zň<v\L_Z% \"rܛFr˩9@Ne} 5=d o5 / oAJ9y_.&?~.V-%F^{4]g%:+U9\Rt J:#tt.!ͬs?[tuF+JtEPq#*Tuu ;4IL1Q1KMTu$utUP' JD4JX4un%[֭ [ۺ5֭n uk[mlP[kBֱBۚ.(lll/ p~Oe&qj\f|w|w|w|w|)A68^1ȣ%v{ /ݠlA^A ` oA^A[u 0 oAO 6c/ Vhrsv:5eWkwAV͡yVrrA^Ayc }h` ` w Y.~FsïxnhS 6unxX ;a47BPܰMP~.(  J?ݷ*S#E?vmn m!FmwYcܖ'JPjR[v Jmy\PjcR[U >OW}=ۯؾ%^h~>6:jNcJ:+(ygYq&Z_>E窱1T߷R}FPeA_ J>c}<$}=Uؾ}5}!(wQPsAɾ%> (w^PϟJ4V/7S[R-7mmYmKƸ--R[ԏ-s1t:GN1$Hǻ4wwq:UdW}}c㨬1qLPjR?)([R?YPR?-( 66cdkj(Xǂm=l`[z,c XEm:m1dJ~((zV~|uZ ZP2j ```Ptss3:]Vd,(jlZ9[\߷opoU8u 稬ü2ڐ.6XmCX[P Щ 5jg;댷cGqtm3֑$tcQp`>p7x.]<O{ #"pplҀ(5!q`pv' O{ p 8A 9pjz`oa8 %:l~`P <<8 >[nn@ ^[*`60 2Z9M]4{ܸzđ `00 ?Qv!0p hj;ીm@=pDi.ѫ&.`Ӯρ_|O=:qe{8=G()2uwW>2BYٺ=(=N 7=JQ (.@ (%0 *6.>ῠw,|XTU@5 @X lptRu@Oz7d}~@p#0 V`"0 (&S@1PL3, s@905tz}CG7;{_sySV!u(2p\< (f_I:Z!I#k*y5pp;PF?[T EbXTKj`P,j:`Xnn;iu:06&I@0L`0fl ._Ì=ђ.VeYZuEoj"1@W9 j鱠ϟ ϶\e.p-EY_a{/6Ǭs]6:s..*tҚ*8oa37 v./M*RX{p J.SVZVYtnY¥U4jqҕUV͝WdIhߔ^ R{. ,OTX;[ 3oG0Kzk~YyXтW~9o0pPٜ揼i|I"@$bYejI}iU+HE* +K F e="5, Ђ~E2+&ZEA5\mo|)7ӵ/kY׾NRӥ\\퓨5Q>租iïk_>bdSgx<#E{yfOgV;r̋7I57/HE0sAov }TNӾ}#U>KWV/֎oÆk eokď Ѱ[r}4m݅S!/LI֎<9: Jc|oġ,)߮{S6WX 5+2uYJTes|ZRu+Tvi]u5Zʥ+iw'Wai _UpZ㒒l.uUskiY5eXŤU5)(eUwuQXX^2gy KؗKt%̓{6=w]w]>o9suQsI}cc##C~%${ԭghd\ =5  Ppi>,ޗ~}lFCFU٫LoXUd'Q֎HɅwi'ek:FA~O_h6֙L ?zP6?ŘS=U/.oziNG/?>dItLYX{,?j83%nk3`S`/Q(e X65`#eB5pΚ4 ƪ#_UAutRrF߷Q;kN#kGyJ:4.R0uͶCe,uTxWGvd;i}[\usGDe4* AUGԑv䬎zQKdutY[Y9ˣ^YhGvEvUk_ue4J?4#CrCSՑ_8IdfF?@fƶY;+™~8]o,3߱&feNN5%~T|0!8HzY`z !Z Z.0^d ?IihTFǁ3j1td}U/fQ5nj'Hb42fѮTIOFOWYO7WA3N`&I` ^__WA4ʬ4G_?H` Y]yݚ Ay 0c`P 0S/#bjidY`Z qf!0?  *JZ5g5;c"bȷZF~Ůt?Y.0ԫtQ` .3F`P?QsP{µey(0BO{jaSH™&0KٵZA$h 5률11ո M`|c\qWJ4ySnyU‰+UӪVӔ3f 3S;V[4L\_K2jsQ` Lm0A(hfF`Z23flhզ&]`T` .LoDbMڏb9!Z$p)R5-8Xͮeu5 I`>9+0yP`Z2s,tk^!0If혖)4E]}aY`JR Gӥ.,0H`[0 0Z`[2v L Lq30 sK`^FI)camiX k\s,)#ۚƮcNL;[].XA3[l%0EV`̚2L`6"y-0'_OvNCi?kXINLGeb,i?K0KɏMYK }pa(BO؛НГ: ;M8"nBq]olOΎ[K/9F.#<qq8m4n% 9& ~@??}иIl ۸F!Q_!Av_7p7. s.z${@g1 I'/ߐ'c6GɈۋpa(ǂɘw"dzN` J,}NxPKMM'%I.a狧>KO'l ׄr8?=y? +:>SZ3 u1ilĪ|l a]ń-Ȼct^4CbV4}9u%π * L/3]3+ jw%1{ς=K;0 qg^%dwBa^d/5GFg#oQglsAs+.́ ؿo^v[d5~;4]ɾyv– _yʾIoaXB؇x{_}/R}CC_gr;=gU~Xd͇.c_ `D ^݅DžЅ-B乳[~[+_d±џSnïcEpv.Boq'->m[}ynЇ~ P?|MN@G"^I mK/^K\<{KзK"}hA%rR"ڥZg"ˠ[ -˔`7a)c9.X<,g9+ˡw9s4;'fc? am!ơO 9Ⱦ8/BqN\0 Cs৷ Tc+p}αU8p"ǘpJY/DxqQjb2i~ZMkαr-$vC}}uÖ:sm4a3ףwzC>yh4YѸ¢̈́9VD+1+1=b?1Akb,~Kw|x,H ًX1NxO,|]}؈~}}ڈGq"5y}~Y8g.c<~H<" ;#$`>&r oB knmHhHzi^nFfyt)Џ蟈lND4IϲIJ>[R]IK؈G[e k}~m$Cp;=+B;knWQg?cFq 7;yb c?T竏))q:8%T1}w*gb~{*ΦIUx6JCܡiN-Hu* r!]gD:ǔt\G+ӱe*cN'y'a<Ǘ0 \/ιIخ)O"OIIS;u9-t\;}uRgy뫟A#B 9}v8>N? ,lfq[sRҩsC'_c<̱q@cV۽n`=+ O7&w>Bna~-XF] KFo#nm߼vBę[L!Tx;_y]|^w?6+ {g|SgzSknrX"c").zQ}[T>ÿ[yG>&X_?Q%?(AKqBJ+(E~GGsoA_2'ey#ުr*G_=:w?F?s#΀'s@۽FW<8zC=o_v:JLDJTbS*5 %lff+ߡ+=c=^?q?@7t> o3u~F9ABQ$ro4ХjЉP;fT~}x/ȿWyaK z;("- x= o e`/8ߩ.~xIYv]Ճ_T=ا`K!d :f 63B܁Ft'XYo1>u67| LLXʱ0 u!91Ț[ m O(4 %[-1߮!4DDBu!YA;3V߱B7V[1ְ\?C33unFӳsA6v5liXc\Xcm+EM4}= uJ-޽EQ?3; *** "EPQQQEK@E E#/iyLR JL \Kh,1,-,I3H{^y=^|z93sffggy(sb~mZqZN8ֶ+ͣBWRV)%ޢ/C"c)SNAΥlA8哔Wې;EAreGf)!)))(|zƒȱ$$(F>܀|^ʻ(RS~g?"A@[lkkp $nȱ)#"!슜H9).Y)dN'|Ljܭw7M?}6b_?.Zr: H;4YȡSј=[C?'Pf/F;gr[6sUMa-3 {3p:|x|oɏ-Ur$ J~Q\Drʂߑ{@6>Um|_6Gc}Yܖ|_Vry!³?: !U26Tx7C,!_l0 ]VbNCDe?GN%m'| cF2_Ͳns8Mx a+ M0$U1HoΓX$7Xab%G3<ÐZ?9lƗ waNtg!Ȯy,Av%8],ODfk\BZas_#l|V`:cd9nj~x'yq ;e )6ESa~ۑ[X4I%uқfY* r9mc,ffuL QKn_,%nH:թ+Sz"hR0q^__#i0'i,Y;h\ߨ!ymR)|aO)SuZI[X;pR0eƺi/O]O\O+m8'ȑEp;zk+3#INOF3I~8~S7DrLP/&^+\~R!ɇfDD>E>idK) 7&E}dorlc:w)9#]JY[p9w^z@bI.ݥ¯'byK 7س.Vmp 5oZk'ɮ}DKtyy{|eT Wgc}.A}!:Pچ&7s,Eft_/r-$ǹW}? N˅ F<3c!yMq^߽-61B}8y/y|u8HGrkR=[JnP#՛\?~q|To:{)R"7U.|<}q;@4) yb"?^ouHMf>[oT?E^od+|~a(z}֛%e#ɟכ-U¯{PFC%J8|4ݟ?$ 3}d6XI`KAv!l;NϾb).&7X,]4ؤKnx )"/h<#%lt}5R>' g[U"x=[l #+Ű0ؒ"5Vx`{WBx66NU8>& MN{g®w /'WZ/|Z 7dVJp<%0K0|ޱw*c_Ry) 4*oo~|h~AsEI8Sl p!)\ WT@k0x_};>~n"^;7X/x"9F7p)\ _++M* G$u[/-#m%-O/ʑ.Tϓ=&glpy[qn/KU {W8{ٸ>/ѨC}z8~{K_z<\ƾyԻ2o " .;M\zQAQ l^ osP6~N}υCQ*~?'7aK/H|_Yulhp~\ɽߐCju&b}y%y||ocY.ϓg56aS:c46Ya8quQWO5v3fw%!'aTd k\O.z@ O%j\_.@.]$l\H8~qp#/+sd7Ʋ;O8W[&;֟Aro&ǧ  ǒӱ)=伅)ʙ› =˨B"c~я2Vc'xTO83aw7%Ѩ߀QyF8ǏbߊzsqTO뫄0}1ygU<{lo2AE|NC}(B87?}~σP=y/ƻݞ9T~]xy12G8"9m0t:^5_!'szmg:|d?7_/{pU&k7~[g_h]v=-ޖt>b_g7dGN{:@N+qz9ӯx#_G>yDNY<ƳL<&ܔ-&^er)0QZ:WHN^ɋt]D{^Ju\΁ 4W_?^'o۩uR.|S mrI0?;*rlo.xUw$Fxm׭ٲK%rH˟lϟ#[*rb=&/ r w꯴.g_Fۻ!WrRVb_GU?> 3. ? th*+`+ِ#NfT%'2)I"%~0)1k.D}!ʬVnvy#rMU]%[H^ܪ/䵭*^)8V͕u V^Jg|Z)pȫ1 ? WWa˂:Ҫ2~'Ve/^c }dO+Aޭprl'[}qPr"|c|<]g y3'g_MTpC89]O}OSrm&K*E@ )_ Obޡy ÝĜNӜ|)l''.Vx4)Dq?.JTق33PI|+%p4= $G+4x'ӊ+F23J'|wN $+~*~{Mq;)\J ]sW6;"\ɭۼ OQOB%ñ݌hR %Oj[Y㻙<\s]V网RirEx|> g"#|_*f"z۫B}|G1 mhIT͇ޢ?S*q)ϱO'f?MObUߵ)QV?FC^Կꇕ_+Q/+Izorv^/U!꿒{*l'%溨O%QQ(qqϭ(/׏*^ ?TByLS[,~8· )/_a'|$\g)KJKlE+%?_lϐ_?V'7=7rBbn!<+9 $/9m>+™W)§oSпK)/VP''/)= 黜 c-t:1=66lyGan+[ڐQA"SB u^UCsϯFem'<ƮOam{NGc(y\~NghH9*?l; ڎ2c IV:\|uQIUT<_N$'%18yRpaJ#Gye<ߴ$lS8~C['<{]OO5:jpaX{di|[m3 Io)!3 0r hYx!s t򢐹W?߰ Bb $o$oc!?vʰWȭ-3.,sV[r5[n"|7yAU|} E8ϴ{ΐ!';d6ή>m;ۭ>߃GȎ=׻Ȏ_Ɏk\̧ A5y`N%_g'ܙR%\}P# O&g?ŝg؈y|P8F1o|~~!IϹOiXK~ߙcve9W iCGNwpPsj K^ݡz=Nt×N0/!cg`0zx4φ_rz de_'RBqlo'N!ZN%c#;:XXy;3}tQdžJ\0M& fƄ;zrNFwz1^_%<J.۰P1q?d'w wϙ΁F/9XMθ {!CxIKFKgao+eV}2Vڹ1OpZ1,]c YAƘad^DOv|Iױ~>wȎy v ?Ovv7rcp][Y8򉰡(\62&.i]D=>6넏0DEGc?p'W yx'g`{1 ^sqi ;/2llcnrnp {~y75Zo±?Oc*ƈtcLr㈇Y%O#2%:D,4/ 0Ƀ"2^1P_Na3&. ?ȸlxxCWF,6nG>2kmct0ޝ1^rfc$a<2c)^;hcda|XYh{ZؽSqxlj7;ݔW]?0VܣFÚC7f`}z/d?x(u*kIzyD7~lt*O~ڵߐӰ>|{<DuC#F>gˌϟ[v+3:V~?}j C^LOI'_ vN_Nʍ8^ Ćw;ct|-<7 |77Eח%[c&ӧ.V_e[1Ebv\}}Wn~㹑r\Z=]x5(7mT~©u$f^&\J${t+zE.@rgpz  V Y=U>uCܜ|sZ~j¯P]T6竞^:?zq<=T Տ詆`)%}^^%=Jҳzn 1/Gm X_;9nt}Qc3 j"J Il9@ PbK=;C6t3N-Dϑ)uz< Ǟ j4Y5NuHx5m&ƫ, G5A!ܾ1>zݣa|&9׽l̷5 zZОzMW M%|6K-a|^%{+1^TK/f;!j0=k:wtLVяu9jfZ_L5Cbt~XWы؟A%D~<' }w =T}LcEHv}dxX C]:5y>z9s|Õ/f`㨾=i5?yA3j>|\E*5ۻ|<~|G!}pJM{ޭ9џ>Gꝭ&IAdG=_1yu9y#7{ZjS{S/ދw^F?M{yַcsuZ~_Ux}|O#{KulUW8w7Uǿݗ|$9>;~|~9P}Ut$٥+=X)2QQ@^==(x}QQn2:&}WQ"5tg)RPߪՋՋXbux}1QoVbQSP?8dծE{wD}uB 4/NEi!t?~7!>5qQNb}t~" %6Ӻ_ݏze{^?n["~yz@=zB^?X*ϑT/~P.fH5<$=CyD[%;!u|zH A=V|zXF}VXݏ 1YϷcJTԣx:#K'9>);\b֘Oһcc>SW<%%yZ Q9/\/Ĕ|혯իm_wӅAc yA~e5|(jH7LLN$s]MG3 oW<\Gv.W2Dwaa(a/x1  {'= 8*x .F _'`׹Q}-a8 ?f9p/^8eذ@\ W/TOx <NxXxlx;2+V80CxgE9ؐ)<|6x4P o`liz}i>p)1y &KCA}ͦh*___S\WgՓ64366MK63'7zf !g*pV26q! rE&CS7ɦ~M.rr!ܡ_2̟znjhylTߎhbqjZq0w\u~M^p}rvqpSryNsiyio)S8|S)v'g:l*:_/Rc/Lfw"WhWkSɆL¯]cw[M1yÔm S9^_m3_o3-5o7i~T#žed{4,\UrwLZd$6!|N_`~;M^~8}A^ ry <}GΈ}״6粧c Mep}r^n_WQO[d䣱&sӱv*OgʇY<7ȿĖnJvPj$[5Ͱ #3ehTw|Tw%Pabh)NNpT+Dx+hl*\D>4d&|ŀ8f7SfG̻^Gtpa/r=8vd/{ے>A:)pǓ “)_BOu7|KP<?C*8`/Ὠ?sπR 7Z-w6ZԥQffn:0ޗ܉8և(ax_A=ݩ+y0Hhry+ܟLN~x uDΠz<0Y^x99{\zmr!oa?\D~އ||Srn<)W9b~"G-XhfR=If©>L9^AnC_-O+x+oQ=ܟ<< NN"{RؿPMJ[$-@G^#?N~#?G^ G_%Oc@x<6%x B>HN7:7Sr><| G.'_!lx @! o&w"ww|$/O'PTr|u2u ^fؓ\nMLX8\Dq7_ |C'Ep6<9W' >>T cnJN$-SpG"y/9 < O'o%a#F} ꉎN>B&WG'W+TO# mhX<6Fp=x1܄ܚ"36? ǟ{ _٨_<8~ N- q--_hd6k^nN093egʈH1<|}y0*Y|_ 13F3:\KuSʨK̿~5wKb7g['jq@H- Fb}3[ ianWt)#:Y>YJ ;Ghw D8?T5q7ZMk쟅l]]x*lvPK#;ʎiib4oό;q1v'G#{wٷ?_/eeemi ;ZKrm}JVW0|(b{<~ݱ~K-N[x[ǻVm??ex{]b}E5q|~CzT}2WM͓o{j?Y/YZ Z[J8\7}Ay>Zhj~c} Q}u̢b[}u|Nqsww}s>y7lC/>Ǒx8 ۞[Kbn^>~?e}s;9}Yh?7\kJM"uָ[Xu'J=x~8cx~8G}+oZ}0Uj~?&mum}|m׾]0y#|qOøW}~wm-1\w4f;z׿y/jG5We<޺_o2ΓªvqG{7)s/~L6^?'QYRu-4Nޥ߯;_+~8^i o0|IdIKuoRw fw n'j{?L%ZN;2*zc>MY`Xw1 66lrI;|`K($/RQۤg-`*` G4>̉k.(_ lKR֚r;e04.rO\&B7^TY#Q#uq~dBxtǔZvz # '" g"!3K,[ o~mD\1꿱is)Q^;"ٴ: =I3!|=$ޏŋ4Qfv@ 鄴~ Ie sʶ)BPxV(TBa,ShR@;ߝb))KR2>4r& Y YTG}RrHOki|BetLv-Bf-g^-YHd8YgPW FjA[QHiہ/?d̬moC1%n9Szμ";hE{oYnEd] |nәυsQE]ݎ3؏ @{I+ ` Kmg:OQOF;ϏKFkә߀=XvWW8cp'y5`~ Fjҥ'2]MFdmgG]`;UWqQy>O+ek <'_~/xbO#.]oo󶢟яg0h21ކ~6moC\sQE]qD [W`}WqyN#y?Uu_UhhOFdgb}6݆hՙ]`$@ W8g¼LN{"싺/nE݊U%wnC lur]Wzcf׌E?YEǓF?+ڭo5y$ 'nC ˡKV3?O~zώ~vu;1*t W:Oy? ew$O_E?_#?[Qb\I\ 1.umoC=WqB?;lGݎr@ cuE].G]qx~E?_}Q8+V[bp2ha 6ե6O]gޯ@{gjrhVԭhO֥ RonC\p._*t/S\1Wyh)[]qd 6 \]joݯ?ps ROݷ扺VWc'P.6?ݎ~v]6/WKޏ` pQY.nEdԓuLl׆vmX rQՙݎv;+6/7pts~>nx~B/nե6_KQOF 6y=W.Hma;kG Bg8 18o qUgm>p2hvl/<];_v؎۫+P@ky5/+ڭhO֥'27φ \s\ݎv;+ y4uim^Dng0KO6/ئs0osў~bu;vWBg$cnqt'ڭgE=Y~nacCR'oGW]Mpiy6`M0OWgmom'vcC lb}hE{q>QnGYnb~M1tkgE?+hOFd]6?nC\=M1?*t]4sv^Kd0R.uu+%d];oӥ'2]碞 Mݎ~Dj>x?}QE.]DK/lDj hEvǣ +i4wZ.nEV'=h]~mh(z.bv0VN[/:|nE?KF{2% rugY7UR7W8LOd0Rx]UH6/8Ygm>h݆\]#]|Pݎ  bq6/ `6OnY]dKzlhΛ.ynG;vm~h@{'~ ҧbLȉbd6,FJ,FJD;"{a\[?bO{GAt@O(흰>܀ gxd1'H.XRvRW Aq=y9)EcrboG %+D#81Xrҧ/udq,0ю|g1.uϽ&ad7rb* S0n*xd42 A!ˑCGF#da<2@ H$d2Y,GF&!39"d9xd42 A!ˑ#GF#dJ0LBf sEr$;hd2,B#aGF#d>xd42 A!ˑ$d2Y,G#=Ovi] %,}Z.$]N2eg e(-iM8Ze&-KrZ*h/_5dZ&r?-hD˯\RKLe-iMZВI FҐ洄ҝA3չ4-ulCفDQ3iɤ Zz\5dZtyђIm\N-*Ҩ`Z:2,e%--o6-b{h|O鿛գ%-Ի2*#-VZ2N.Z>sZ~ZէkA2 lm 62j -'ҒL˽]vi,pwo[v\~ŀ6zL$!-3,5=c&7E6CzBXsl m`1R7_>Rl? vbL0sEug&_miK+ZW&!@PЄn)>EίaY-?,z2Z*_XX6Kue<6|O^=D^"'rZA|$rW)Uk"Z_|S>+*) 7|QQ:Dn"rDdܬS~ e3D׷.[lo!rn'r=oUp .wm#rK[}^~"|BAs'_|H|EvwE*yM>( E{H}bc'^" IES}'ɒܕBV{N3:_(TW(R*QVwHED.aEOD^"X2NJWj/hE]z\~іE=EG"~jOI%Sgiվ=Ѿ}i۟n?_o?n{A8:wTv\ԱX1qku}[}Zu~@ow |g}eپzB_/o{ݾ}cY/?__A[1K.ҩK+6,\z;n_)ʖ+[w  GN Ao?8s- <wB })49T 5ZCWvxзB~G?pu!vp]? ?>cYEΏ\)\YqEnl7Xdoow#'"O[^|oY~}D"(,!zOc'Wdžc߈ ?? ~ZPďO'&%>S3璞E=u==`ϣ=Oһ罿}h{l}RE}3*j }ᾕ}{Ѿc_{㾂B)7KoG8wńX³¿"b͊ +n^q{W<'+[ʊpYY+\9~WWN_Yye+W~+pUUW~𪭫^_zy>^uq/\}խWvw޾zGW?/νjUW_uU\Uo^5bkZ5?Yʚckfگvm%k}}MWxWx)4\3pMѺݷu+Xw?kkzݯu7_SGl8wåj6oHlemkn8i l݃`71x>9/=xhcqC}eh%C u Cˇz 0tww==CCׇ=T0᳆0åeÕ ᫇o4R}ÿ𿆏4TigsgϪϫ/窴ؗؗj؅j"5U5VƦfK,5vPcjZE+XƮT%5֢򵩱v5uTs1AVi~5LXXXbj,zX[V5jj5N]ƮWcؠVc_Sc_Wc7-jV5v]ݡTcw{ؽj~5{P}_=Qc?Tc؏jd{J=ƞQcPcϫRi/ߨߩ߫??+*__jojj*jM5{WWcsF2ƩS(5v;C}JJ FadܾE+rVo a!@8!E]8‹/! I*@PNC{9p69 ]HkfHՑ_N )$Wfh'G@i :D|يDx%i|2&rm,i&ǁE|!GJ& l#."v2BA Γ ;f`ɮ "')CG#'cAv%@Jcr?&`q2^xC4OI Yr H#3CNOZK d#("Gd3_ '?HJ!2~K '#'@n:L@Od_$_&ygʫe R_'A9B"d/61_Δ"!ya5 H S%>P2p^T=9i`e u3W261e0UA2ocvxs*x3/aι`P\*t1f:A2˴"cʐ)E`A233O"fxZPbVL-xz1`6B*f,9 Zkcv]Ŀlg[GVPlGmHv0>eP`#|k@C^3ɒGZ&nU caƃ'e8ğ`& | }Rf̂( ӊ-HӉ~/y9g(rʯyقof3⇘V`DƇ 3ClD" Kf/3[@f=W{00U5hď0وL`#'d|iE ca-f(o3w}7*ǙϴBD`"902l-RY 2(=kb~* i쩐=4;3؃$ 1l'³m/$g3iϰ,{g (_`?݌y1' lD Ʌ1hFx[dv0ʾbv%l3KٵHNe[p:r^3أ࿘ _nC8-6Fv#Kٱ}[\cNBXnlv'JUUwaaoavCX;G 83C7Hl=8drp6{.`.dgT; hl[w=%Zyv4Nv;R]l'8z=lq/;tAn}){ؗرF,gk@Ov#\1W` p^yk!G0 6(K # آab 0(U`ÎC)6V8laYdcgn=`Axݍ-E^`p~w0 b%5 {?/%v,ŽE̞U1F,{gv7;lr{9yzP@{-l'حf rG/#,?{vC\\-GV?6#|{{#C(ǹr>a$Ore@{{R(4wrc;q3R?:"+C<7n᯸VCAo8r@xkE~1[FxOx/r!|+C W\-¿p_Z;׸@~ۉGQo: <ʍ\' |1n7yC6 ; r 7w{ܫsD>'=?v!ہ?;XB Qp?`OlGG+hD>,X/zjC*ظa9`3lBL*-(X+_wrǍwo1dpĝw.iUxPI5yJ_?YK֐ud rtjf6=p),""q8Du4W7>T3+`$ P%]Jcu<!y! WQ2/"[q_C"".(P#„ f͘0rvX(P. nAhY( c1w\dsmj[Y&ٵ5֖/"u'BqP j5uyQ խՍu6Wo"!oţ wԻB WДD8vG̦X\^|qS<u_b.tѕg." iB,F`*[Q8Bd&"# _h<?&F ϮT\@yI,!em\iUBGx5?.uon6u-z&b5)5*X, З^sIY"Hѱ/2+P3)ΐ^JxbZ쀻=G,ْ(.!h.{{F\D1w :H=q㊻H\TWڼjXZtr)\,y38 t\Ih_,xIKeԀUK_báX|DH]-pwzTq _G%ReO\-O)!^[wU!efz(~æa2 1*gN+xɯzK?%SiD \ba, L~IpI5i7Y{2c& C9M"H! #3%k`l!^[ eʍ}p&N0WT~GFX]2u?$Df~GYKs\e8z=zOٚ`pugcjZҔ3W%x;hNcryPQngA0#X:0v( ZJ3r1bard+G*/7T)/W}~lX_nf( 8Y-DJBA]ȨP2w'D\&b>T MU(~IRxҪ^^IiM-/ako^XP"w q ?[К7ͺ2J$rǕ^蕮8trWWP4q\@!ΡIsSC12)X3$7S?pH藣W"?)SQrHע_*1/IT0pT| a\UcpL|B*"*ik܀n/x1TqOU!AB}8Mj}7)5X'6y"uxcJRs2T"ODsB3/6sB߆SJ䱢G\nɮU~Ww++;F @[ԥ-NC'hM+S49Dyq?\j <HaQ&G9 QyLD"b] BX{*AhEI&My5Idd=D]Q q1jF.z*#jޔ؊>i$[#z$2_xЃG;]EXO)"+%Rj(Vn˱qX BB!n(6|\-ykJV lRd`jTjjLFҤk"+]ťVWM65,//O {1K1L.)ԙDq&ui ŵ@Dľn}r`JMif菑zn8Ej]18$XSوə/2W<44U9e>M(+*SB*S^P26Z]4jg}Gbe M¸JŲo)''.nOJ1uPin`Xwi:R +/e焒y7ꤕa, xhV_NYtDX "p"+ڝ%JeBDcnqMF#IXݧH"ZgE/kL8b\N./^DR=JTUJ$,B$EtZҕ2.f~5&~mɢ|4!=}]=Kj""Q!sU{eG N)QZ6mdOzJỄn,'/JFX$y^ae;2$Ri9oiTX~fY N➨CA}Aoh[|+`t@KI f$r%R-L ZTFuPX:KBq)nJʓ#+~Hi)0A[|?%}4= D&HĒ̠֌LP`TLW}3[ڟN :/f(dN[[re5VE:^Z㧘-uŤt"j0V_R.!4(d7Qx8vT&w6:/VY'z]Y¸I.>&s,C={/!WPa X0en4_b,y2d زɣ9dU5E1ʐ -cj 5Fk.i zR1t\D]-uc8Z3YgHc zˊI_k.sQuhdT%' b)\*(cQ&6c' MbwU "_PrhJsLxM$x%c|qX72Y\ \0!䁥^31~?G$ 5NbG`$YGH0P⶯vEty"f#xX*p]) !ΑֹQ3$q~ec(aAH|/ 0O6E0/{ e&Lےp,'8r5xc }//AHߣl!H:M#c"_v2V}͞e%n[>GJӿK۳KK7f5.f9Jf5fdeA2XI,yo!jgK{do4ql$j9G^7MjQGm20>!Pl4fDM88ɻ)4smDm4ύAՓۓ1S[֎t"Y?cmҘ&'0'˩3cMm$Dѿ0ެY ԅ #95$sU7fI'Ñ~>63&+oe7%Uaלm&Q+c x$-_xByUyL<+ 9Y@-Þ32W@/.;ʫ 6ڗSrEƷ6VnN!&-^a=S-h@V :>mZ`Ҙ[/Mʻw }NyRL\%(Ò&i.9*~ІOAʧ%GFb2^|W\_ ޘI@τGa[q- ݭxZ,>$RaAH(ںT{LK5$E3hF_%Ki"i3Wڜ!٤R|Tjޖ[hd0iHt D-)w X5(~cüoS̝7Qg-Nַ'Ip)Trkoq$,$d1?; [MaqZ4ar 1b s=)y[|~/{ꐧ7^t;"FDFIV T]PC2CzW25hP\۠a&Wv 15'H ne<QKHXUY1WiIW%z94SDE*@E3d܏fʎA5U\ZY~')&K~=׼OWClat*5-5[T*+*JiJRʽA^{59dˀ2M ˦(q=3NdzSdɯR4٫in!DR4m[`&C i4/vP8 aٝR@i4̥ju ߵYd 4Uߌڦ>ܵTOذpHCJRLcX<3*7IiMz^?35)"YM97 Jg[\z`999d>_=2pzPtw9{ ܑ=_leg`_ƳGJya#FJ瀖tGZiXFcK|5IȰ&O]Kwfd)h6MV>C;&K~=~/ULW4rEֹ`Ǚ z2Y[{34;I :,4/5 ~fjFĢԊα`d5 .]KRdɯyi3WSw9&ŘW-qm`_۶G6umrLM̐d5ՌT5%i3$/TOSO(<=)')ۖZ,T?2 ~f~ߩR{5mn7y3?5gsorhKʏ%<i|E;C;A[#Jӽ<{~-a'gGw#;۩q+̎eK6eKYH[{OvK)4Vx꬗CJC."ew|Zٴ"Ͳ(Irz45Y^%|Fv 'I l1=M~&ዦMqx)I ݄:,T& ~f~b#O+s9-Mz|?3?C}eMiKoAR%Lt{vQ<YHEF4;R^*mjp-9'c)?S)1U6E(;_5[ KW̖>i mX)y2ort9HYX (cxĸ4n$JşU[.KsktQd8vAxK|,Pi"<|a @8xIGh0naRHUSFğy )kQW(&2X-]H`roJQMσRDPأK(RQM=JR%~V "H$!yx*(K6!6WZƒ2V CN6ɐ!p oLTe.螕`;%!xS<N;_#uƨ#Cdgǣ['();NM@p l2Jn}X$"Y2ptrXKL4kIB0׵?տR+b5*ǵ;K <v:vPNf$cHՎ2`tBE>*w\G=_T~mhWqc/ Á;)(m&Nc! ɟ\&y><ɚ4gѐͣQ<4HpbCrS/mY!LZcsP<#IGi61M&9_L9vѧh֌f ꮀ Ģ{ eJu'Q>  po+fb ݮ ,(_'qeQPЦM\~OiSEpbS\/?ߕhO朅e51"KɷK#[a䈦0rHcw9br+Y>{⯺7=rLTcX0Wk#t l:MPtFb C/8at`rرJlLȠ\WP}gjXjHGz4 ^&|Qh};Sw{1wD-8 <$dҸ}LA& K08AFتu\DZ:"] U7IDbO7td[C#RόeSu'5e?'Y~ENSrFUϣl g,r,d^- ) E#ʵng[gC*өLy''|7,|R[~cc̤YSȰo 8:, !K):‰'9/UfN]5"avDdМvCւEhv|JҀzB0&5sILr R'%7N"hŃF 6tX\R0}J uEX5}TZv{wDнj\͕KeF_RW1,Ж06hPE˴9zms"pBtEҵ Y+釮r[[c9h$C#YSf9M?-:a8 0ʢ]SZ3[Z.M]Gm:zFeYڲ1YJցq. Cj8Prz!_Wufg/( 6I碎SW̫:Ou4x͉\Bkjy{4'R-PZhhZ|4bT9Fߚx--T֏,no>}|j:THi@N TW: u>5R|*{$KgQ.xAɊ%Naiu f<ڑ0[f+ wOρoPK_gXxh OA8 z-Pm40H6>Qv 4 P_Us3W1&v4,No/[YlY]M` f|dsEh݁<rp왑?qg1Q$LsN?u snyxω{Tw #qc6ڸCm۳6ڸ7lo;U{yxMcyQxR9HY\?]ⓩ:?dduդ"y='ŷsWf"r^}vr^}r^y3#/ؿeb늉%/ؿYbZ;%Ӡ-5ꜥ9k[ҏZKlڜ-AҬGJ2!;yY6,L-4L5KCfFʴ-qm電,:z.2#||e8ZL{\rɔ_0itP%s^WS< fCvՍ ܂@2)KI乜3AHV/ma#y}ձeat3s:_ʷ~TK-ѥ=ge+m%>b>}rzd)G(3oYQzS[Nmr3dgӱRgsz'U%g9FcLlq5M zM늌ǐ9CHOy\U|gɽ,598WcĹfi<=ofٻ,K3wqf6d=Ǒ۠}2ۿ FON'[ig3#C6}yY7I*Yr֬,d;Y˜\7ţgL'<R=EE)>o4hbf輬MYQT'897fDv@i~KO- 6ayD02S<ʀCaoPd\1-=ɌljaŴz#ВԶx,KthgWI1r'd\1,;lHvDX ֓>g-CίImIlsow+<{H!x?B$"D$HT;_)PBH ԗ JPP(*!\q8D*4"!}[o#WXuےرvw3QxH )RHV{6bkRKb՚6& "؇ k ~$ <L?CssꜺP,9n绝SEn{O:>&1|y0<Opu<9?|iӆ 2Y3'{@;rkkVY19G1I~ԙh&\OGm Cp?c)0ѣn[i|}D͉x- U^P+z=X<˒T`F0=} 0gLjAUYa=! tqLL<=(#8lU@E4ɡqj<7{jf\z}tmAv`BakW1JP'AdfM5dZ7P51Paf7q ㌨U{uS^x^C:;G]a8lfۚ?wWuݽ3kLSqOMH!sJZ{C9NY VX 7`PԄJ`B!1XO](V"zp9:K3^g۫+zNB0EuhLa FS?P }vI/ }ZOyۇ2C:bn |j[+ bG)+/9hmWv59;05_: O䓶#ɸmQdNxo3 l$a~|쌟() `CǛ}XNalJ|yXمsZ뱃,,F`q"*D$4k;cXL&OqvxM Aq w0 Ԣs}Wt{Fw@ًX~|&GwF.5B`۟KI#:iiMܭT^'g @ za.c(`-@|<!H/a ?Dš>,P8r7 p0j!H"üv#Ԡ1sBQ0JtQybjǮ%/NЗ'`Pa:pbtA`1:M7l]T&?}'>&䢠qX&#FP]@|DW T(H3|>;.r;u~ 1h;+0d`T`sCz<FC@qz[_ZdRnxߕAae%Wo{w{uzS[2ӄx (Y޵FG.>1;#FdYR斨jÃ!g[t8 `K]P.W~*;/=jOP ?ǢKm'4jn&MH)-92Bo*r8)zץ|o2Y{zcؑcwݶZG;I{!VYO1ΨzQQ*= \4SF}kggs x#gPnqW܃A_Hi*FFQ1SݴZs|>1sYXkyki[kmPN;]g2Zf:mgN.eRTke!;4E1?vEɭJFúcA2G ݱlXYo.bdC%qx c',b;eVDC=<|T#K44xRĝeR61ųӑڬ9*jZi{a,`]Tãr`P3ne7tak<^HvXo)j†zNm> Б~NFɶeskΊS_Z*+J+EVۛiϤ #3n(JL>ܥ1ls}8qr: EEvavl7- tvqSEVKŴ^VF.`;(aN3 hvxCqC A+D|g!:OM^MSr.Sj8)S-Es?2wXhj=3Jn*n?;U"B3T9z?]b4!qO j|MS?jZ #{T"cǓ("'=p )օ/8oty'cnɯe2GMo_hC_'fRӼV}LQWbȸuRoLGϑp0MC0f_,-`,.tV+ Y~9]1!䪂Z;r៧h!Бnn/[K .rf7il$` sf ZYfke[-q"5D'UJ _ ?|$ }I+Hw״y uRL蹔 ɾm]( ?Nz+`Sf}3Yv}t/V墙JתrksV,(}gY0UoDVf߾?QO- sfd2CyaTbq.6*, |5 Fi1Lo@rG_d?*꘱Tfw /Zj˦VDnS7 (~l'`sg|FW]ʬkdgAaP&7BZ  VJr|/Tn=t"%6[OUŠbGUMNCv&@!drNA>t"8[OUŠEbGUE =+A@Xq%:NF )r]۱v:,uy0CV{֤K"0 ( zVx!<=*<)<#b\]*(ƀX.4c!r1ܐz )1ћ)@7(1 8M_a):0,Z-dhiSVR1&N5cHc;ZҨɉRVUbs&nh":9ԈNW^VZ#@_ެTXJm+7 wTyYRwf_nv}[جTۛ0p,;NQnk{ flݽ&H9z^f1dskºuKVUR)75(n_.RV%(?PݹSaiX ,( {M'4q;eWW6/V?@il1r ʎ@A{~ #8ޅ9&%΃L26طڢ)I9ԺzF6vhB>tơ/0Ia3blr$$[܄mHNy'!K:~J' RFafb9on2 𦽝쭗z;9!< ]+ƃF8z[J{ GZ'O[LPwF_&eu2vCEgT Bu3j\puUϨO壏 (Bѐ>rzxB-5Il:keqAXOHj; ^^׈4@M6yXvȐz aIuށJtgoME1ٔ$鉽±!%bPQ*ǨOFeDQ\W-'J\1;6O6@$_v޼hs'~Y||AX}7enG|U_7lP((¿xue DQYՄ *]׺.W.`gWbFxC>9$n۳WŹHO(&䐬Z+nkFcxQ/[fSsl1iZcO]ln#F.ʧzhc&9E~iYo7rck3Ϥv[jw;/7y 7;x ʳeRe'@=@Mm/=͓4OM"9LkCcF^x x"BN Z e4\$wRr._d@PrLd{ur&Ԧ6S.NZՖ'f0F}'.GZoww-g;l&4rH4<ċhm#v!ϦJrٖUwnɕ=j3m_ ;?⢦ dV߄]%ȓ&Y@Q+W'l'ʌ6FЎD(cB%&{·f5D_#Č1 9Y,*YXM^j!J5TrbH@n^MHv U>5n /69}Og`R '3BP`˟]HDߦ Da8LNOz2r L7f"a?!*S*#n_Zr,I;/ =H%xAgR|)i?2:~nFC ͎[UޛgI1%JL8HوZ :$G6.'fD- |$*Ǝ]9:"eYRa7kf{"j6S{~oζaik6 *Ήsi oaot+u%p:@ xh6Z4x+YJCn)$=*7!b*-)OFb ]_>W f )ĈgǬG%qq}&6MƩC+Js!yQ)[i$vvwf䑇79F{{3Xz@ڌ!WK po{xL4O'Δsxij)hCoAOL MYeI] ;Nyˮ:(C? `&a Of0Wg0( mTkV;GIS`4AhHBt=ɩ5xD6`WADPɫ^u,lV <raF,0KAI-/~IH ֭n.ŵ-"lwLXs4ȷ`?)g\.g%`:WFLyCLC3ג'kGB::BxN~aE."~=4g`#0eNo+$S:&l;3ɿWuaI} 1!N[MnȇZR㵋*@@K OZ}E~Ux P Cc$[f=HҔ}oȱ}&??YS&7<8VoxW5BڀLh2#2HeZb,V +LR$BjByzi1^{]6 udB'mS=LKy;Tqo2FBn}5kVsة[C(stU^ӽW©G)fJwmk3r7x:#cIFv#|0{jg09a6]C[a3o]Zv=vFnܲ>[$ֺY*JX2N*fJ83*濊6ά?ƬmY'0>d4[X˭ellļ5)oMy$7*4tefhm;0whFl:^+o6K*VcwosVv+ΞGDi% P-SnpVy{#kݮ(.hwK5d~TڭkeW9Q8vW6P^.S G}[JC7Y,6G:z7>GC{{~␣ yu Ht5`t:p)R\Vv{k~S9y9;ЩK^g,)'_?j0z<Q@_,muW\W zch!JVzcCKޭ{;~,ڽ=W)o7ŧcktj5͢*jcGsVnoqk4dE"d(]_+Xo+g/rݾڬ h*?,mXͅ:B6ZP O)+/Ş^MYc9N?B;Ȱ>zBM\kЧOQ^#3 S CXAB6P .HQ{m@m-U4Qo(\pȧFqV,2KGp~|vɶ0`ضC6C<o;i۵ƓC_[ 4BG7 ';|!M 1 d "!_cӵni4"3mXmJܢxe#D`ih ?rh5v*Ny֟mq}| ZxxVUf!IH ŠQrQX![G5`F +:vk ؊ecxdTvT*qDiMvM[@e]dρe׃-w m_fi+^EI'3gqrhsTHpX9O%c rSG30 ;1 1k8enx ZwF,82`Bǽ 3z}kԊaȾjHb~lUeLAlXnLfl7#aۂQB8^l?\zwOR -(iZϰm:0 zz'(Ugv'0G!b$Cflk#mVdb *i@,~edC7?M"9PP{ - c2B1zXP6.\{o^W?W5'O>v['{ҿ~џ¿0߽[lrԿofJ?|i0.-.,ݸv ]\.@cѸ Wp ㍛yܾ5ҍq .MK˫hҥ|2{ [X|} b54. FPK7r,%D/-,࿯y7ww rm xIŰA,05ospҍ[s`R#/b\]sDe{L;h i>?AycozO5 w׍K(~1>YR˽+ o>?i\"B-ݨWA4H7 ,.]ځ7W`z`lq . ~]x4>XS-\_}(˯.3pkAlyYw,eP-ji5XDT0*H,,Jrݑ#uG`Qc^ ?'alߍn\w%rzOyOIcE 5ߑz8c\Q=W@# ^L:Wb@I6Z\2<^ti^bo^acB[Ɯn_3ۊ|oʺˡֻxFeqI[9Y[Wm%jL[+l %tm80u JyU[> \[$mK*@k5-\|򞠹{{q8³ƻgO 8̟$g/oo<).B[2g/6I*Л@ xq9y|逿GH~˸uvo`l_*-z%146h`Aň4JyݸC^#C[O"E˨nctP0-[0A{02?1tqݘ׈ ԱpSA/|Ǡg|] E(DjJ䪇lV/ |XyoWuqʽ̽˸w:ﮆ3333ywhJ~@G.gKISjICu \Xjo^|(RTjցz,xut2j wV0e᥁C'X9G7sv%y>5>8$'pXBbqh1'RZA4'p UVu&ɨ9֋LYDlލq^0K"XCdWDvZO_-N:S *8_*ڮߢb#hkM7;U*+1ajׄ]^zyK5)1f/R/cRK~/!%z 3M|EDPMoƹ.ҍ+rmYgy;\ C 2.EߖƉ]-+RmքNJX搡̣EEq=`USC> B5 M F`H., [L]yYX—C[[S?66۬Dy罄y~o\odHI=/mOlA5ۢYgfs*5۞YC@oBgBqV4*TQ1YcqjwIܡ0wh;'HN19D|&2'H}}֧+HH5}&M$H*DR{N4`"܇EI&23%# HHN0`"ht0+2h l$Y瀬aH)OI,Э 6hlmY\*wqQF90xmiEO9%uּB::"_z_.%`1X<1j0{w<ߏ 04:kS^{KBy\fpx sOP ul+?,TD?q;ͦ ߡ>'p9jAU@I \ 4bX/x޼Lc OӀ8%%/@l_.X<^,8 B/t!\~IL\"7"TPE+_h+WՍs_}O-r)Mbbzn$3)~.Z_J_H_J_I֯iZLܡhTwbnTnzQ-һg<[@2&!Weȍ2 @̳Dֱ/|E|iNoP}J2A6'ʲxY%#IU*1#(,u_:uB+]WN+Iϑc4ѱkcuG-9 wPoQH{vsh7zH]Ϗw|_x[lH3<'}9^5afk:g AY&Lɑu"]w4ޮ;VuǁFW}m kS>t^Nw?Ek.3s`E`_d|'q]U?ԭV+J$ADPeP&[2Ȳ"2b[z]o[xÚ)WJ  1 a \wK-dzX׬#@{=~ׯֶu߹sϽs?}PI7t5:Kӫ$o&7׿*SJ +.g(7Zm5کݬIwBRm3n.e7ECr1~gSA)sdR;G6RnGL͑ݬ9Gvirv١I;ן#7 Ae;ӶɶtMp(Qu20:&Ս9kֱx婇xz Κj;ޠN f͆>JO*)ٛj!F } jĺ ^cĺ)[)_-6oxT:5bg'shNu[G BG,戵M$,MΒ.SJX8CIe t/%on@z5XILPǤ yFBU#8'9q)1Z )F}TIׇYR9H|Y-t54`M{0nk``Ãuz.z"GA 6w6ـ L,{*ؖczi8@Umŗvml\}nDIO a ]wdǪ[5N-{rJvDIkZVu[RoKDhbPbޣ<)Q:RjE%6ePh6 ~m|9cqԃ]U3׺x^fۢFnNLYxR OG<s<6}2J,~(LzjČ"ՈRPQ2BJ;)%N/-!@v*F3;{?|^Fx b.D"v=A b"D"CA xDJTMƙ 5NUN\nA\$)šoS>8ׅSEs'sLeT?mHyboyjӤ'Cxab@\K眹et(}dZҀ\߉tZmYIڜ&մ͉8n'?~>qܑ%tT%E|NWR$壷 @wM%3֪+wQ7)y%[%" &dRl9JB&%X(Ap^T<P<#j!-O7&|\筯$^'%jL+4bNAr4|-1hƻ_P!\޼3(8^$l>*s:^NAM4EI5]E bND5t-l%SڠD`a/$}hoTSLC KBwHK R^i^aa=pw=pi΀BK؆Ad<ˁRP"[zx+ݥcBFZH']{R-lrYM<*BEh3I򇲃-xQX_ ;{*NW_G ,2̂ W`= xCI^wU\$"hah@w`pȼ|ŵ$u!,##2xQ4Px\8BKj!q $@ ST7EO@4(OP7sƩDFHzsv@<[|,,Ɣ@ɤsˁUKOpaMQSaUu36.*s.Z\]TĔ Ή !DܾrtVCzs 0')-EkcqI9RYU` jPtq(Xì5Y nsJbwew 11'Klxvgx8;1B@cnKX~^p,R !pI >VplJtt "XQEAy 6JZ{X‚.%т6 G_}H+ 裴.R?-u߆wb0hoߙ^ vXa"$$Vg);kwLb5j7m(Ocb:3fv" HpV ՟0nKom$ Zu"PpW5D8Q\S9MJbWQ6ڡZօ1(T!8.S=IRJ=yW`a͍q, (J,8;ъbPvX|d&eka&nѦ`Xu Vk N&4|#qDvC 3q]'v| EP#ӶUG+Muj3s!)s8iF<2h)_q' '*EbmS -mj1r`edi'1&Ph"왈l3XMФsxX8IrDv#-Ӳ1`:NMT.CjWbtc1** /brwCD"sEP#nCLm3~ķlk(tF_\*6ّ Ħ2<[ܯ|ChAQKW6ˊ:| F*߸ )_ETX*hoY{ww^{wH{P..b=U7ZR @'E\-nmh%Ԥ4/& ֖i\C%ԡU^?lcf3t9κ~lCE0$³ni#.t5S@g+(C_VG`Gf+>͓.hae1^ E@/ _%}NALD#F]A =atu.ÍO kbuw܆4l04,fTsH-JBQ+JܦԥR"kLl-.JЀ> a%xRtb̶*? sub|ub?"nST7[whRD`E 2F`ߦPCvG7xC8:a{SH਌p}i?jU既HE'EUuWږva Smljv05e6 %&5:@@d=Zް˛M6^oLN4L "&J4|miAaYq#̊rv$ %9 ahm|"wP>>5lcS.:[صfWz[c}ܾ&d/7^+O_ Be= { @vcNH&WCFR0*=*F%[ | Ss43c aHPujCr uܷnYnh"[M$H]7-]fdr"ߌtma;v ۉۑ'޺v*vv 8"Շ9|lSKeRH`ъC`VJX.3L=I\ғ#>\̨m zU qJ}I88So%|r-n[]B%gLTeKïAhа[(;|g\&\sJ= }RE쨘9` kEos1\NѸzCMShҎ~O4KiƝD Eж9jaT7yiж4%QX{r]3&[岒-qRCKuV|Q aUc'gYͻ)RMxHL61OBRwex(&j>%VZj6m*{XAs6U[mZ ik0ݨ8le1qyʹ 7dm؍vQMX5wP%D_F"+24 I_#I.!N?;(8>q~!@i my^,=p{^7O(IM<4"Islu=4y/Gi)nd H]oZC/CHmMt?_}fUz7pW9l{Vҋ7eUU/[UԳ)9#ῤ8.k jY]s{Y ;8iH#%ZZ/A<,;6'T kP/wbu+c@/d 's=ޛi[|05M0u4 Xi~3y9&&;n^j&HŒhY*:{>K$✕uF蚬7Bɺ#Tr1{u얡k[2LDEi-vC\ڃzoEZT[ֶD߲C0Z78^ 4O: =|a=zH<@v:4}ÏQ&)rM#%+P{*L=MKypH4 [Ea^Eq&JWOZSxr۔&LJ zebum;^"ŵT t |2/4zwI_nM͌жSt#)6+ la5oV!dqѠh+ΨYޭñ{=< _Vo\ 0|N݄Z F囫zEt[pֆks[0x Rzv{Wq)_>UGq Zj(ukq՛W݄honvh~io^~EO{{%cmS97]AZ5xջXyE-9/KHp՛TuEө IjfF 5M'ZP7m inתOn^"^l +E=]z[jijx)#ƫJO2Qg[\jelZHu)/\k#zaRAmWzCs&֟">-ŲUpHq+fz7hڌxWau`&Z2VADY3-8grc"Ew5ynD?̯,x&2^:1̫-27#4ikn!T]BhmfXB#.KB;$T\Ah?[U2;}.6hYsE6#X@יк:B3[ m2c-C[mڿe[n Bܔ!C[t=qہnf_K O:|,_?2&u aAy)HX+c-UX}:fٵ}kKu"%4,@b뎼ʞm9Zq 93D7=[srv&ni$IHkpW F5jxWM554Pl?dh~ BcwsMBټ5'+g&nr$P96Hܮ--_ =uxE b0w.Tfom JpK#"fTmϗ*[AUx+܈L;h#;Hx[KĻ$ioQ-"OX@jcZERA"E;HngWyseH8^/ *SI YPq8pYh:'*ȯ>#O#7+ۑW\Ulo.ASQ5*e [s2D[쫾,k jt_O<9tdE}r1ee[fT,խ_=}G ̵7j3:3q0h\M?Y=r_q#]q-P+b**]]xL)To5oN |c fd? %ˤqu=~N?fRct+[ WނI/Ӌ&TS_gu.L:%l_uǣt LLH<,79sÉ6!P?.țHY<œg 39S̖Q3t+Җ 9fTOsVeEuܲXٻ-V ?9ϥ_G_.򱒛ym/~~'wvoXLo[>}͟e{/׵ O狟犝^ʇ?S? gZwaz}: 2nDřcP4ANeYB>X힞)OψՀ1֐ѵのhC/ 9dUL˺#d?01;7Z&s˲ُMաcozS=SO =sSɢ e!pf5ѿֲ' OV=  ӑ'H~x#L{e8~yf %S];|oaڑ:q;|,Ea 38NVoWsPzuNY1o)g|-~, u3,3h/>-O^Zma ]aCup'-+r-o۫wwNzf|e,dOkpCLG~lĸUCe}t}ut'k6 $%(/v6}/%}|34h4geS֓>Y/5_6wb]/{ >쓷YG_Y8w< w'*]1h_cOV{ڟ|Ay3o*{vc~yVZs>YV:= 3^YY;2"O,375xŐU#k'2Z!rY|6`.e}- yd|_>l_GU;uSjYG|Nea:UGe{k]پ׀7oUe]OVo,d]! ydd>o4 [4~b֙/@ rN]3_>6Oz p'ozW}𪖅<ꕅz+䟅'}O~֐>-'kjY|А5^GGu 𓆬#udU}VOUVȺemz^Ywհ`Yf< _ W}y[<۪O%೾xkZ֥:x= }"oPÊ! k /??[\'?f||>xۇS3.n_ՠ Y- y' `MC>': ׍v_GGoޠ~6|Az[ ]%#o+uv'㞅oxe ,ˌ!oˀ "o b?a͐q/5'A _~oByYdwozĹ:e0/c]8n0)|SB B;ٻn0Sg#$ 5*o`e~@ڿ%a7)LPO?'/K~$'Ν^9}r/.%8ʷ4-sv1kSa+c.`Ap=^ .~ 5p+>n-HwcYpπ++{\7{Ĭ pw)?2g =Z pA:ow3{*=G-;sN; ~ _n\bV;0)p n&V;஀~ 2w pG}%upb]7npπ{ oG~n\ku{;=nypK>a *ρ;pS{\?np=u=~zLszFJc%09ZIJFfăݕёYJ[<־5!zӵP*YSIPk&4R.GS⌵|OG(S5+eF D'm'FX6 ,L|̲&!gr%LgƜBaɆ?d=h=2R9T.܃#̈hidX.྾z#Y{Qw(ǁ'(ΌOO?iYr>?V,WFG LIX8:e55:YUY+3ScsB?? X{R,띈)NUơ ,kA+H)C]"7CX;£c'"{Kbz{z<:STLG"CRe6}k*)ʰ[ ~8l?wp8 BmO̎B'̌ 3k{4yZ=[B.G+H-P@*>-`]}{ kى'DݎDaTU*60335mYh lY? <5ۭGPv!3`Sx)LwI q(9hXXHgWXDz ZうX$OT1Du1e[|"*ղ~%(W wCBX&P\r% I붖YT8oYOg= }hYXFzLҺqya8%zYQulY7FNJ'-^OG>;;,gdjN6dq,|5{~ iT ~]Ï|O6?x{Pa5CXetp f^DS/,״yKx^t4=z>fC`yhgуKs05`;h1z ޗDJl;,p'q{gFuI,vbB6n$$HL,ɲ%9xR&$;"ڐ؆!N PPֲ@)kJ)@hP^߽wFF|tzf=3w?3cgg9<++0FwZKμ3k%{q$`4E GzZ9g%Qoi$4>'`O3I烴ws U:bKV¥+ISp4FDgKc4G! 6N/+ U/Hűe*J! "5B_\U\>&z#C!j:g06"8ع -k0& s6 "!C Μ6u-x;WY-y,Q أQ3&.e;I9#p8[HȐߣo(}Ҋ$ې/5B(u{V9t8+Kk5]-4?1xoԋdOvb`U|<>Le6[(۽.|eA>a#+|O<¾?u1s 01|67K]?fvߥ0wz^ՓL`un ucC>"hkDǡL8޽H,-VfKtC"ˏ%2*Ѹ]xD%}D/ 0e̛";GI8MS&Htʺ bDANK@ k߄O -n\} &öCh>h[ ѹGJ\O͸hσ>sp~uH0DfH ;q/ρ`bAs$ڊ; Z4I6Qׂ:MnD-}3YGPW'BQgoj%`D/Hobl>&dv%VDg_Eq{*hX`c9 $6nyΝDt!Dq잏5qLv@ڍ8ddžK-DA6@n6!n ǁ= >.#eItx}x -w?ja| y~*қeA7~ >eoi0*p ]`Ÿ;e`?`V 6-{V* p/x>߁C?,", 潭.lw4d.| _\ 1Dp1x44,z0F< ar 8\qFpSGM}0 /.Y`3x| fn=XnO?]`(uq~π`@s`w Ղci:1,&1{~PBh "_ ήnjznLЭ؝Ft'm[MAր3Jm Q?+1 ԝ^_d)`1rA-+`lG>]'5p0ģ ~l7ew5z ͢)^L㎊tƽ>ayLCe=-&.[Iƾ0uxB]^D[4k7FEc c+k7 krq.{]=GMݍs] ѰMBV @6 5`*v؞z!`򻬌e*nPt*c&[HU0DCXT`1~"*aƨEDUA3Ho rӢުf閴$bkU"l1[ZQ5Y$ͱs:dsgSafƗd1{U)M,Qy,9pcC5XhRPO0,j/*KV3 ,h1;6%\"^?F1kYKe,V (#N!0|n.ɢ_h-g{ +C?DX)*Ց4Q'ΤUR(pf>ed?@y=!"Y>M]RllSgv4]*骽=6]%NWjJIOWbjs}BخT==̶5S`δM{3o1tu S@e:{S{ʸNu^C(<K pZR, wx73-Q)2m" cٜq,' "0|s/\xVQ+P <3R|Б#9̕kM5f}s yTeg(Ulgbxg>2fGM2aü$u}=ހZD4UMVmjgDi>&ҵNEc~X3655;Dv[]hSZfLZr~r$n=bҊr-f%dbWZq˞ŔM|[e$J~zԗ`Hfɦp?@*_.3Y"ic|VCєF>Y}H_ڞ)欉UFmD&ľes0h%wo.N=5jmX@g]iÜeǑSr8NQ,~b @CP1C1p4@)Ѐ`8` i` ƃr0@f8, "hߖdT ޸־Nf{7%z=%;2ySƭ ǧ3Y-x^/SRX^Hޒd}_̘Ox!l;w2Unw{6e7~rg)7˖^jֶs=)7{)og"MNWGoR01[|XBZ&ϖW'bs@ i`5@gN1 "5j F^]G+UhUŭ>xr DG)l;?K|\/.IeRz0>>7܉^ܯjE@ ?т3TZp֐PxlKiz-🨖LzZ-?\44eroL T\o&PڇM1l9Y9gfʠգ9K57xL\ZDZ`8:ޤAyLfk=ژlrՓm\z_sV9KXɒ?_Sml.dZ'Izcld,JIs:X`8 p!lKerp \ ׀kuz 8n[mvplw{>p?x<Կ.iH_ǮK2&_WBG/L\뒴udx <~ O?SW?Yzev}m&$0+ZPo"sR-σq|I9Y9|:?0IJH; >_/?._|gu%@PC1C1C1C1C1C1q(2bc֥3L!~Dd/򍣴W=I2~6߱R;qm /po-{^=6*z㟔e(K6/ dn2ZhdAm4MS5N\l E(aHNbq,H,R6l\*{rd,+y],, e0ubܗ?*bP3?[y),o} wlC=$lhCz: 3s4u0(d6k1R]>z"o ~!˂ޮzպ佇h6;unԴ nk.=@VڮmP1={1?X1Шdz}I=0OP P P P P P B .j˴ZY{chWTW][=QPMEɵ~Sx?ns%Ǐ^jrîS-'ΪM޺9G0OeξD3 a)*mjeuU_cy +K̐CXu"mo+^K&zm5j=$+*f-̎cuBhWjiku| %[*ʊ ^ZCe%^9t>z{ ,Oe=$"i}s"? +:6O~tΑg }3pS .~ˇꆺ]DߩO>kVƬ.\! ߍ2ª*g/_7yHGF+*2(ųyqM^t?Ř|<%R{dF~Sfw KiuYs K]uu=fNwMLʤy5Lv05fdYD0.8|//BwWA |op P~z[q_xHLw(9EF !w}d|oy"yl'I\!Ur쓗HȟIdV=Mm*h~Oyz.ү =O]Dtӗ!}7ڣS>aO 6Ƭb@:|GEE슪=/D]eDF崌#iK&R#/Ft8 #p2LyHp Bzk8NMV2/[snm=q/=%/饼%o{my޾wz-y]/, 1Rl-ʲ,lm=vZB|GxOs4_Wy&P5xbRL &bIȉX4hNC4E ɉQN1)9'\WV,ʲ=ِMy([-;r\MBS*rvTUԞ:PmS}zRO鈶Ny zQ/..]ֻ`\KuO@[&d̘MLML4L ?G}>?e?o%ׯ~o~Ϸ'e JCh -BeTAu&8gڒijdpRElW:nYG`VIlBd,Z&Yp(m oH4IXtN04Ngh.+t ;i ( m}:#iۆ g=og윝 v.;vޅYҲv{܉8q9 gI:)gYuκM9N9tZΑc;ܸkn]p3f5w'F>Lx8a|ނVd5o*`Eիy Nt,0#f(`3, v,Xg6YgMpúljd'x،<_|h&?;|hFTL@")EZ,î2#/6lƑ舁1C1/`HV %e ё]iq5,8V j ȃZ@Y:Nh)V.-Okx[D@#0!`mee`vaΜVwNa ͒r6JjX^ȋy9Ⱥ%` mH%xW! ؁gy,UUW1𳫓fɬqfYӯnk8J:\%5سtv69}6PKѶ5Ql@`Microsoft.VC80.CRT/msvcp80.dllZ{tՙLI &(DЀ{4h@HxoCؒcBg=sYXveaiZЖ-INn+G]?WHZ 5riI;#ABzzNw;iG%$I ,KKU'_?*~ ,u.u_去'6gnkozϝV-ZtA/so+p'[_wQOR'7iWӸ!I HW ڎI e&%mB<ԁG<붞*ܥ{UўH.N/[۷~6|n}- U[W ׁuKg!6lAN̸V%ݗ;Ag]u|/I[WuyGI3{:W٤ϱRYgeWuvu~b̩[չ{^:l55_5_5_g]ChǺRꉱ<ⵜ^O r4Ɵ<钚`EJ4"63=!&.6ٜMۃ<}R{P^M;SΆD@h+ St@Ǩe `ԁwu<> G\BC)dOɍn,k3ԭf%Um,bO>qdiVȬڍJ)+"4jҩ$_?cYj_3cxZvg΂_9莦3ɂAȧ Q˭z .? FO% ?s$ |q=b\ sNax +&`iC<"Ħ!i+$6 mR#qaaS7=Fr7/ kT62ÂHb1 lBez̆%9,Hӂĥ9t(0LANKe*S^ȻlFD+EY.Rw(P*gXܐ4lmժK6.bm5_~lr}O) cI/v f1衅ǽ7xgRjHRVݷ %FJٞR\cn3> Ù0;9SJYXpU擛"a'y\/g-ԶqM /pݖ_0fYT[k+[ۿuI#*KQO>ZbI=ѐYB0Fē oFjL*[b9R^R[u?&aŶV2r@:Jn+[֠b}rͦߜ`l2-lH)IP7!Õ7#|lOQȣ3o{s=ϔHJ]mT,|Sb.c!uԘoHԤC:5UfkfN8٢X0|j7v7KArQhنBmH4Í{l7^r޽Hl=R߾elj#] eZ,[cͮv5k@ӓЈUūCż{kODu/lYlm^aN#߲1QT6$\Vo z!w^~*ZBm44 $LfA Qn@R^ ѥthINn!UGor.(:o5=)_Nn5"触09gk#jԛ"VNgRH"Z}fbˈ2{+c5kH6 Kڧ&:˟sزByi$_$ w($DNgӇ@;:4R!cN:6^va.9qffl6~=mF `X*xm},K/c2S"Gw٭K0#:y̦Eġ~EV'O\^?=aFόX;p Sb1Pb.\UBJm|W?_rb=1+y  Iʭ(X[.^8<Űb*h3}*9j5TT0&OIM$F5YZۨ%] R~ Wt"X$mN,a+R[Ƕj} DzwHU~/ֆ Q/[-:  @ymj#" ,"XD7:f`H`1Q߾ 2Ə(ߎ- yhBγLnX}b 7 nSNZNiȐ,>pM5#w0A|R臄h׉ĊL!ʃʤgr&,s{*D bb#F)U~zxA5vnqMq0d/ N`1u?Zu;Hj7[! {dIƻ+kMՇTnݿ/@V04 ﱟ2rB 5qpU;2+&cP$ѬLD.Ls U9FdhI2M8>kfiZ \5Yƺ 켋 淳6G㻚+C:w2 tRwLڿ5 G ˹Pa I,9X4_\5mi%^ޞ#gqQ 7[ ֢cq_괞EBz0{P{.(l:mkJ= Og gXöPi,mTP]~i TfIព ϝsILN3TS_OT͛oo[|BS?==9Zyt\vg [/cVrʡ`Ŏ<́}N$[0ɫY<{϶wݚD5 1 7}b6v!5pb(ޘfTdIٝ ӡ ʒz=L_[:qB Ғ(*ɀ9P?8]F{ y:Q^E翄j2XEs> Ќ:,vpNCiVKRbKP#z$H@!+]Tf5&x}1G?ԚFsMOLH8 jT9ض 95EKmز1Oݛ/%-oDŽY Q6ol.SI)P馞vvB d3RTĕ,&RF7KGp+<"!ҺfHKul/hK1/ T4;-|-&>rbF8 -j[DʎzJ G}87NO\?s$N"B={1'嗰Wb;7ST⏦r{W!SGyV?ӊSq:ޥIT}6a&[acBOc)䛭D+#5оD٧q:PS4ԁn4EpXR0DMG?&&Mz' Nb+ŗiG=GR=PKziPVH^9]Xڰ/F~*^m>-L|i$܁2}uN⸥]NS1Vb^78L'I3Ȟ\)L~͕4GgBulRq(d!u\sq1@-;~gf޳,ʮUa5(aC_@w_ ټe} j>fnQ"q\SZf\}{|T. .jԨhЦZe70g T"tܮ_j[|A|aIK] RI70,dsfǯJ9sΙ3g̜VM9 ǭls\ >Gu4W. YUa&UB:UqՍjuXLp P6f';L+e@e8̉ic!np+?<p*nMǨ4ֱ`u&D%GEGiE*ÎAAe g2Uw0Mhw+*Ęu h?_SP->8[dl/NWg}KsaєZcOwu.ۇ] xK!BzG<~jU7T\MYئ(|0p90ժI [`w?( ;3˅,J(TZnDž{~y~.־021t1;}W.e è⢅C NM%lPx-ƞ(\hRb*"Zce'8+ -[:gd4'gQjS9mՁw&2Nwa޳xۄG{R4KɆc\ b_j4få#䶰һ4R~la׫",ri#iC!^(څ_Gy&6 ~{ #@"tlVҀQ<Kj:׎FkS'.i7jstq?꧱Ž|=&ߦ~)2 +x)rWa΀׼x@ٞ %/u@(0=aFPW D-&i-H)M#gTؗX2n(bW|E&6Wy5X f??3NifN/@w ќ+ׂeC0^H"3|)s;S0r?ݍbFMhʀHF>&8K`)ޣDim)Vjl0<6LJdf0@ VwcV=<CWWJY6@}5 j\b.5-+rRy#XĆ9 Kl+{PBFN>+^eEc}nЈ#ĭ&ߓž\lg#Tr7(R%8\MǕ\[ R~P 5aC&y'B26ᰉaQk) _iZ8D+BF_zgìs\jr\"ed8l04n1\El肠$XsB"[  6eĄM'ŋX7m_,"GRMOlp+68/ KйZ!YAp8X\ TCU B; ۽k!xf༴~a4(YXm߶&폮kƭ* |#sZP,Wڌj0s4s04W Jġq̘3f`ƥ@?a݊XwG =;8lc})(3bgkeF^@)0a͸ξA`SRVR(EХfƌB Wg0:9 օ40ɭ,`A|EêuE`0_'0gսP ?f, c8c=m @?Cq"0q+lfgw7\70jCvM57 M"+ZA ΌEh\b;_Ğ,!fָXa jNXaZTet]lqj#ʀŒƝ]Fc5sBMOo&hFl ٜ(<!H.gͳR gۢb t 6qT mD\.op5f\kƖB `۔BLRƻhBcuF Д1#m€5|Kn4`+ !-V34ly7BeEPJBqq+AEYU[]uffZp[b O7e~ٚ~=Wl5$=P$³ J۾3yKM*j|8*KoZLrܬёjі83ZnڋJٽQ_Xba߃߅˙hTDŏwJi "ܸc ˵'b!vh&*Dt1t:["n68DCbOt*"Q@{&!()NxMQ fge .Hv|%=\o ݦ"<9Mg,uxʻ/3@ FvkK?2-∖eXMe#ӐU|z'n6c7w/9 g_E5PI34E7K!y2/;Q_X%)?:;ۥ9Ӱ^~n- Z]XVP xxnVvK[?G8Ddh0j=6 &^h5:3Yl ښ&n 6n>K 2qo8#ޝ`MpT9l Z~O#0mb.@t0D2  0n( iy_8,t{`az݇(ò(љ.K`ϐdq[ΆEV [.7j 'h-=΄ uUK`VUlewGPNfb5dB_C7_9EP}oXoy2/Px:КvgP-<dF`RF̗s– VyzKݟN]&IfwGe6tNѠR5e90h0a/dduϱI+.[]CH4lw\\1?d7;0L#XuA8^.Y ڲ6OgY!6t!KpF)ɉYTP%<}pg2{lvr͡[qQ w$l{ql+4Plڞy+9/WF$2.\G`Ʈs[i.។i>Z4vw$BpJ#jwD 9\'[e1Q 5 T ;[3rp(`ĸsZW;:L;SZ崱^BHm>OVI Tf9ߊxmɟvva넎<};baGx3؁FpS&ocGj`5umdv£p̗*ly5/-bhge ƭ+SX%rziBe/k!V{gS Vqy||r_ZppuMnS===KvB.Ӂ!`eE7kyS v&_c\>Ty/Աn>O F6 &e9-h7Yk2nnb HCvvVLzLwx.6 ճӸ&w w YoހZi@iJ"kDI}#*n-,*G2ۣՉo"' ڣskTp THnW[׀kP WÚTTt {Oy"+8Wɶ❝l M] WU uZW+P x qeE{8}=߫ٯ֐YTHP`{- P8`;h|NC[t h7RvOG}_:u_tSme_z,Y" /0=:zF:v!vb8HC I͎'l yOLޑאx:f!݂Rd>hۏp'ű\@@lRG{_=M~4Ig"zAɚNp} D0'Nt%p=l1j7૵k9mPc4-)^ 7}~v+jSly"atK 51,Y .jWN,t0lL Ar98}}Ic'N_^<_K4!-b.%ºP8^F_~DM%v"컗U|]Z]LY|btʲ|<4oDxw˓s[-+Nd {Hq1[%zMKy/XY>`i!r=f+Zmr{̂Ьn,E#h_ P;܎LYtlp)%BJpUU%0-ad+gH9.> ThцgY Ma0o=s3y{C0HNej-oRreQ<+u𫭾||͗o_ׅ.J:O֬sQ'ӈehU^fvdd WɎ>4`?37[NJa'ɔ,}KJ.@-G ". 3~XȐǐŷ s-o^UY2; |#-}S+5W?0p_ocq+ݛ0O,nXHvj0j/x.eJ dJܻ\l?pxꑷ~٫o.`puna@S*(;JDe*e7'{B~|2N%ʺnP6Sv(en@V)["({MA o e )cE)ClH,-ew@f 58votQvЈy N[4z8~٪>iNhBfȆE&[htŪЭFr{f0.Wiq $L i r݇"l.-йSX [Н}/9`O]B; ʗ { 6JS1Φ?"Ab=p[E0! hRS\Sq}қ+zFɹd+Lfg⪭4JUfflYixdr";HͮʙN ]N.ZJWg߸$9d՞l&s6()ULD?6-+D3[Ҁւ`y)a32'+ד{6fֻԇ,emC)FhQ ʍPt@1t`Л0;]e+\AS b—6{U᳃ɣC#~!\<+>y48& ԙO>HV;.fArkL{NsyRkhВޖ/ۣ mqtq*=ns)˸~uA&J a4r{=[m.yΈ/YO!HwB`^o1)nhECO-30yM:"@CSr&Rׄvإ[qGTSB]ՎA NvȲ2Z.{0e)!)z*qb]m{^8C6{V;FFR;5D1oc7XZD)rz~Yֹ?>&OvC^AwDEO%; hh8: dQŷ1ۃzpuH{1^4.(F`z7;IDӠ<7)ʤ7k'{E= (˞MRWc7r${WTdrƸ`0p0=q0h{1s;SևhK{ܩ2tA{8=w5NNMTN7D9=uߏ8~S"VX{»8c{012=qb WUXN{.?Y>a0'Xd,cyg'8˗!˷<·{TcPtVI0gY[~$忂{@O=1й|qu M ўĢ$;H JW9LC61O-mx\)G(ْ_KԷrH^ҏSn]p0,9Qn %ɔ3:g+Ӏ}t|v`f!`W()TrST $ R?CSu{L.uBUZ\mCc&{ &'m:G~oer J>iwAMnwI¿1,!/g,vAֵyzƽG -Mz@o:Dr{/Ŗz{:xݣ 0a-4OT⼊i ݶll %Du|6x:_2¡LP0%qd}j~r霖nv[;K৭s|:(4a:"HVF` RL VUE88;rH(Q~?XNh`NB[~?h ObNQ<1Cc{ Fa2%yBK7/s2ʢU`RQx6(V,{| -9 )IRǸ J&mg'}" bҝ ;T_$|(SO~oPx"% 3H"W}R?\[KwBs$MJI:Ύy3W?o 0BG%(JeVPn7T pm)сfyYi-NQ3^z‡۪{ 7{7(r+?,?+YJ&?)aLy]J+ʓ+\G9LCLOȜď[-R)^ނ}-6H`PW3ԓ(m6Ixyf .KgǍcK1NK ;w7Rqw ~ J,#\)eT_4F[gz|+^V{:m I>|Ȓ}x2ԍ+]P`N^7S7-CK^]z|kѷU/F':F:.Q/Lzթr̃ܰΙëu۟T5ZcP (W/+JMeƗgMM3l>DLO# 6(7Ov_.}yv&~^TjY$k#鈝~&!%T$_8!ipܒhI)m<mZI#87_j_}nӪ'+Fty+u&L߼+dߒNNY] omݾ|w sW>OGq~3oOğ"}O2%m'K?hCOl2|ȓF%'V;Yj GzDRrX$}zEy+UcVet+tyUSêYo_;ͫ\U  e]^frr7ZtC_ݺ\d>jlsW]*1 +z$XINqi!4/VQ/SWm[,$xk5n{`pjFq%K5XǦ^ZB(0y ]kRZf?]vpZ.\BhrEm/_ q2ZoE%֌`M滴R,~PKXcKXA*@}l7Vr%0:F=k9O:$ޡQd\ùMm1"_Ł|9;8 )|pWyt@ nugEKѰx*Wwjo^sfR!UV)4oׇ2yJBÜY,^*69 Md&=N9\tqL]d3UΔf,kur0jmcܜFzqqUu+'n䐝ZqJsU_pt繧dM.B$\(_a?9rvFg‚*Y+@p:s WUdcEij'?$[sNǃ@cqO&7+ѝ"5tϑ'!Rq5K]CYLr /P&yԪ%qi~bYΕx,\ଢ଼sYmOL?^2l ePptn}S团rJ_O VLTq9iduapTA*Z\9uz^utU:B9gC-ye;sC;ک}@܆AoG]å?`(Px|p{2H>ڄp&d?=&&T4˭᳹)5|6IlB }V*t g*" q :=bIëwT3o*!Muvz36J+ғsҲ"psDx*t!pg&ં:cLl]& t+8#R0sߥV ^4*i:YvQvT AiLjhdcdlf 9];4!cQǥG0=c#3Yy_u~UM;@>ϸ!*\.D3MԀGB`*1upCgwLg3ZgS&-Xl ڪ EU4U}DNW|>~U PB N]igEOQgTYw:zpOI-9شڣ&$B0؀2wt3w૯wuQ^$0ju=.zaO7f}gw:%(ۑߙ?yn\癝*~dCa*V!z)AEw3C/ *W6;K;:;77rslz"];H_@sw_o~H˒=s\ǽ "Ӯߢ󾦏5H; FȮ5ώeH]w`ƾ@wg9P"ES$2 ~? ?+TP(ن,yL5/Ż BfVWpHJmDܰ?"A 6}%f(4LkgAPhp[g!HhC pNE 8eH:Th-v"H u4FAxD8;b)Pqv4/#~.eݱRv^enFkVOj >h/A}PYAM H^-gg*s_Vݘ{M\,iMb 1,+쭈.aJ6BDHkCYh,*.|;o]ђXJP@U#Oe`&cOT 4)'n Z;4YcZ8ʰ:t}cuZ!Љ0hgIOp#vΒp)MYE% ;;KH; X www,umTC҃yXG ]{ءƛ1L\ƃ:ugϯ93;RIЌ_2uT/Wj汢QIhmB]0&&@CIiI=6nT.oR6VxmCu\mO~N$Vȸv%5uιٙ'u̕XGIЎRhQ]XPDv< ږ`U{2|4x\,)giT_97 u1 q?tU}/ ˭!Ojd^~]o+y+vkhLgYgQZ3nW]t攘=BgxV9-t^d˦RWߘd ?ޥ~ɅfԂݲnZV- 0Jjԥ %F=\nuT9F9ƀ*oz |HCB?sjmB(o*PWIA'=)9yK@9M([K57d:ab_ؼ}|?5\)Kٓ7(bҪ/4+P\Joi[#,-";"2ʅnVF*OD_*Ru&W?/,P38Y K<%:տz~=x g)nv ,z8WB[Akϖ:7> h<c0_:Z lXצK O4z} 451^^a!.Ql2^C ዱFs5;r+\O )ng+.y:u^}ϟ,=u f tIw-׉b;4 AN־lNȽp[LRp6oxy.ueUo&'foܫd.%"/y[dW'yJ"ߟaH&RPf5'Sta(l7؆9+YmC tj6_hW`e-;gJ+$钹"FzI"|ꫵ :~o`of3:ot|/!CQ?  OB߮aX?9G8u~rp8rUv *q9+kt].Pji\VL?t!ᥞ+uy:y7aXaY  ßA5xR!~ݡ `vc6 B!|]_Qh?er>0U.h1  wXsz]eWufI->}v@!ɣ궻K_ĨWzSr#93kF\4ׇɇCe$I4Y>}Zb+O2y鲵n{H%X4Ssز!`ב/v H;L4 n;?2} 92ȇ+Ym}QN(Pⷨ˾j8rV-T-ƐZŶdQ#|[~5ƶAU? ,+ƏI4y|4GHf[dwLL!֘i :@d*jQMj,*"]IhB^A2 ^B*3$<ⴢL pqqqaGc1]/S/ofwWNIL@~&8.cb; >NŁ)Zp\b 6{|xGgR*F@D8-81_$O_ٯ>l$" eRx?fv%q­IIq*[7-cw 7vSvG/էʘE\+onIo.iO}~*Bchĝ99PGSsvfaO1ÖN0woo- 1@Պ\bj4ʼ2m3SNWS]η*WD5ࡺSVOW_hos)Uu/q??.3 /|(3L\ 9阳r ljL[@XWtus%z.g )\XPE)Z;MW ?w(MD68MC6s^w]1FlEhaT#jvx|ilI#ßIЈ(蕩.,pSj64mD`|+!@|8ϩwf"Q'GeٛώS` P iiltu*İv R gM&6Rטa|"pUǯ79t=Z# tu>_.{J~'Il )f4֌꒨r|tiA4>wBQiH&(ae<ם)*U&HQ*caeRV9LXdshBl]\Aeck^PD\@5WrQ eg_R x14ꦖ+DV}]^f@UUN|)W̸qMCNK-2XAp\hV.)A},D%s!t buwyErj.Bx>]qƯubgECbWE^[z}ԍ ۃ,Xу=n=UQܧSl9|vV΃ 2l`p _&f 5Rnll `w$}c0 mK6nuzg~+ʃv` Dd&稤D1i[O%"#= ]:v4< us`L!iB0)P@+<8-Hy$0~%۲;XuGRX !̎:qgeݡ883Rd @;sqy{{{sνx!4 |ѼH>N ib| =;+z62.P$-^?R(wyS&4hq#!:͔8!#>PJt,AQɍ2{E*pzQnҪ`*#oSmx$6uSj8$X6ݗea2vmx z׿4A:Ҕ%=M1ei R;f(lxeT,(wL+OY 勁u/U+A\8(X "gb>$U3"T>d|1/N#BF.rn,- Keaqm0Zr7 B;Eo!H`e EZ%i%8l/]P1]'Ŗ3l9 ^Mtt:A݂g(?gE1ښjQtXd^ F,*Ldef5NHf'͌`jO̝m&Jr-:lyQ =BM2dνTPNCη0\(\ԢtpB0hS(V'wъ $_4%Ȟ}A@IBS8c\ͱSv(9f?ߔG771y[F(?jV~3jx+b lV`Xߎ03Ltǁ;k!4~5e+~kZZ^.R-@6qq ilXGW[[cE{ _qx!9#D_y`qG†-d;?ՀuҦ -ľ/-rj&t hU SxzUi~"-azj5ҍT\/ĵ\ZQ8Т>ݱS/_>׽"1f,\7EP] ͓nk |/j 썦n@FФZ-ˍ6_ i2 {ob@NĀ: 1s艈4)S0-ԗf7EdHM.hPvwwUҎ9aiҮvn6ݣjiǪZڟ1KqP_>5,ai6.wv83W}rYgYN҆qK]4N-bjs7F>Mj4m("Fd GL# 24+ʹUxML-upJ-I>h%ɋR,c1rsj) .nU([ Q[m[B#w \%|&yhkYLzf¾x4S׭5Ƨq2p9k%}/:Um(!m+2W 7L#%䳹A&rN90)8%fɥ,u_'`ºxoON*Fe9ca5d:]rWi)GB 0݁YDG*c̛?im.Rx7Kfj??>(Z 7:~<=и:"z 4T1Avg(y P9`ś nO:Wdrky8Ε&8*WmkkAs|ޤh<#.;ױi oબ\b`֟5R c}[>'drE&b;5u_8_6IW~_[_e}fk;IIW )U D'z}\j09t2PlV}pGAmw6WdVkxut.^ z__zD[ρ<h;A6ZoA[D 7ΑSұ }EùLΕk`b ; \6ՈF(鸶(T.$'z@Jȇy<_s!.qѳ42SߑMl^ׅg_m0yF;WF֝@pej@ޮQQQ2D?c(;Bgr;CR=WeF ?zLGD$+fTR񩨳Zj *޸i4WQ[DGP֛^5MO;H+'wZ?y?g-璴}8ݷI(0zlݹoʟ9W̌Q/Ӕ*ofֵu^j_´#5(f0hZ!x +P}(  MIɖJR7X@.v;c a߰kXUAϔ&đwFL&yNxd):-'}E%Oƀ x!vV}>(& -'?#'F&*&tN=9illR]X* Zڲb˲i>MSmٛ jr"{{a*{o(~:{[oޞ^GuAW:dwTfXX]na8!.?s,_W6óz.߾nmi<C~G?՛L!khaq0-K׃saz?@]Xk/2⟝o0[ko-hY{I. .Z 0`%<ؠ hDhr5djn'YyWvB[+Dq :m栲w"VBաP*\-֦JymWiJFR!U("zK,hF^w97![b\o\ G*I;6!.V2s2MKM@}n|R.~EJ~ULP㖃̾^+._hdp;x%.J?#-y(Nq;%-n!4q|6^;GR 6]фI˱4߰x=?`=co}?Xbecc.p~O~!ءXo2){PT,؛ ^d;`m`ؕaDdbF3= Wlb,Xh̭fͧ`{ll?d`=⃭4Ma`H,'d _``01WjS0`́Ԓ=Ӏ͢)+*=x]!.Y3,Wpp = ϳzXvǂs.>= z wXxvIwb%6J6J"@yA7~ym!|Φߍ}o0yAk,7SaoUQX&Q-,ן/5H}\µ'սЬ}ki %:>;q 6̥;eBDp@$թsoSӀzP5w&t9>69YljSJ3+wp H !R2k!_6S׸ 4d#Y*$wKp`1qi8"7J9o}3Qc0DOa 4%-وw9#% ))Ka^ #[3y hk&=LފOkBr[Uvr6UKhl߽H{ :G Ns7s:0о `0d3[Qb%#̈́dgx3ee993(ڰr,3 ?d;ǖfX CLhU Yנ_‡sdMZ@mM&-&;Țh%`30>v9*L\j22At3\B5 48WkPl%# (a*p0j\\I g$dcYlso;X&8sBC:>ZI30ނ|!c!m<xP xUU+ ep[WcPC2do;d1+3kA{;4W;9L\,'@k kO&TV3ݞ%ړl+ZL( squ>]3mP)L㰃l^( 8Ó<79nx0AǪ1ZI07ڦIVm9@8b paT%XJ02%XBJ0p\y4تgL`YO !( ΂-ѕ2hd <𙦡}yg>Ƈ]Rg4v Պ61H˚ }Dj'aW'1u\< V=$+`H 5ɔ5Zi/quxGkU:Z\1Jrf0.QvC%lL;6D 9uzbV[#(91JxPrA&F}9gUϳ T▅뙟*VR}574\ 4a,/w¡‡lk 1'  +uSoP+X iuVV-*YmFEt,'V2Wj4ѽC#m6@Fc])GK( ݖ_B "Z''?G$D4QD0mol25ϻ& qvQ57P8pygI 3 E-=Cl \:k"u&H{nL#_]đC&?yosmtWYH91gY1?IX\‘ԋ:R<=FuC3.N|dMA?~f!i~?kGk.$L-8:BJS%to1F/Y&b_E1}XyZ63c * ɓLKqya]8Ӯy$n^B3lV3}A]sDH 0Ǘ} KP,'` ‡ӥU8.囋nγ tWN*e}NA1P)mz}{.Cw;bi 1o[}JoޒMo!ʂ| YRzw]JF `Ō1#qt`JM0u$_VtWi]"7)9œi5>2sŘ5sy N=S|+:˟%kB^O|BS52Nl>*g~n&}:䫟y[$_Zd7 ܰJg:z։ΰhp`Dua߫A]Uua/-lŠ .| ,jbkBv BSSK&u]7ZÖ@Y-DYEr~O`pxЌ$u"\K?[m`pdOm$yq*N``=J3.u]I)fqJRh$+>oa LGW#?Ɍ'%@Wɍi~oɡ=Z:4cz1At?[E_ˢeG;WΘ!d(|H}'Vk;Bw^|8߃M;MFNy*tz]VNb_1I]1c]\1KWBWwjF)/amB9$ѧoҦdֹuJϚ|k䋘Oq:RN-ϸw͉wѹ|a#J-Nz&yS%t;X-ɑ:MX͐˩_w=: 17М=YK(B5ǯW%+pyNC XffՓ'XZ&\; ]x2)gsrfr>M3|˖p eeϵZRvp'g&r~]`M0 ڬ:lxI Oo~)F;\?X Ip3G 3q [Jco%h,CLg8{MgupS]NF(b5eUs<} m6Y͢ʴߣZݥ'pb\)5Dn:*%;˥3>{ƣ>ɩT+lxi֋49@ms2+N s˅jm"Z 7Szw p)rЦQrݵ4wfk, {A8aXܵ֕wUS<=׾$+Je`ϗ`[N@Ǫy1\ O>u81 &ßE@p\FI^UJՁxIwqtX)u7BwMHJ._BkacߞБKA{rBi\'Le%p2VxFtd(>SId$勵^6s%89r/l7iEniA}Ʉ+ y=78oH |6? gSeO_q,}`k`B˘ccCYy$b0~aw tpM<Y܋R$̃t^$J^ %~(TЮ#V6GXvesfj,;ʲgcY3 B͋5]o/? F HKaVf5s,bA1Sh1-Kw2'kb;d1/umُD쓌Щ˞9a]bZVLH&@I o #Ȓk,#dYLEM֚/{`l>(a$+YH \:{2չrjjUq:UkCLd]rsEBvNHq!qX_| =gՒzÿևiF}moi[?< ڍFh*P6ê?T =A>[OVYSA%gtTVaT/k0⬋Bzzw~TJ%@pZօ: O"h$&uPhhήlh$QqĖ(Ϭ:9;VC #S[BVDu׼/Idٕ &CUDV=2vҾ.=epc_LJŭW?.oƫ6Fx6WݾO 1iEZ[֭^mLZZ#iIZٕ =DL\5]8{i/UMK¼jyɭhoW7WTޘ!- Һ3ֻPZFҺPV^e -+Bybe7[ݽyF&|8e;Ȏjft;DY㼓-h?K6%)%;f)Orrt,c MYvﭸ#fZOzʤ*MW-Q-̨h.cT[BL{0435KI,0XQD,f몘PVFgX/z>=p9ta`O=1\AqtCE0CC}Z`O _ɺG(ny[an9`H[ԖA|ܲ~O-dlNrE-wUD7.MZuLi[#^W++TFXѥ7E1$ A}BsbvW^dV@츋2T 5xdٓ{efTyގɬ|[,ll_m 3#e6Tav`قc1* ɶ*dTޟOަb 2{ޘ0+QQE2ˋl 3D'Sf\*f0fު0;8*ef+efTy̮1dS̞Gmުbfc(4Y,^a6&:3KLfBكvW1;eDqQ /1~xKv+v|,[;}Îͷ8c| ;nq|~|ώ??e>m(Ďc#?G#2#>˖(~&7Z-YO/ۻD˗@`5ѷCК!paV.(Ƌ XoRX&YxHQ0=;oƉuأe$9wzq͜㜉 ݼe7sFj2 AT{2ZU5q$ ǿAMEH#b^r#s,r,!.\ׂo8!>) f!eWʧz)W^a$lɩ#쇰ZڡKoT"/q3 zmU%:r#~g,uoO޴tt˔!l]7ܺlJiڏKE` l:zV^ʹFBC1V*%|SxXUXt$R i~b,s'\,i%맹̴ ,5(,/rR:Dl TS{(^~$$9<Iч @ Hv(X^J[@[7U$91ބNL@ + %ayMvbN\st] *8kfAP 60= r"gL|پ ӥ<*D oٴuz ya1r57nkt_C$40̤n2W8s)fY z|<$}@=$460Խs]C6 O tl%$2RUGF%q c޲u#s?Ɇ" drR7Ev@5ɶ3͝RQ)R^ښ ?ǽJ545RSh\UU t䥇99 Z2N ,WkeĆed7;H}OACqP #Ăծ+Dc{RT,z<Ķ-ZM=IPoqm topW;upHso72M\sCޣrҝ0u2m3B5sjjmȊ~ 8t+:Z"^[ouo8 N] _ a+< krzR#@5xvvi47+"]W頒Z B+t. ]ĸF"d! _[dTe&~`?AK4HP@٭ {{Oodz֟Пr]Kw)ɉvDڂq9d5yUiRTJkVK~Zz34DA x7 *rQƑWMRhe9椦7LVAxyZ3F{{rYZ{ o聗X#[9Y!49X[1a8g1^4OkCpx=5HyL6Kpˤme!iu!Q &5462g(0LQx+,Yp&ҜR[meZ kA0Fm܋AV,,4-b.xؓ% # R0G(n!U[8Hp,%L>4>ިď`he0;keBbPg|_&F]ty|-MҭAg#b cU&g9<޶S}!K^&<|sV]X? wO| xo; ч, xlQso>A%į ]0pw0Aj6pvo2jYaDmx4\J[C 93 lUgbMSщc\q ~v9'WUhض;Ķ! NEcn#:77%enZLoR6©Pl[欐tZ mR/K/V80Zv4| _g9jgŲ8NRKV>O]Z籙i+ P;ъ]䅣oǂyCɯ=.¨5PIËZ>N*jtd9%i_!*kDE/gY'>-ʎMe2&\D,VFz< Rh,%URDf|zK][6vQDO HE) 81U {~d[Gl_Lv6dRȋ1X$%]df~d>y>}.,`v{I|c7^;}7MNc]f~GFq7=ʄ!֑դ-4⩹t HZb#G[p]{J4(UVTO-f,J.q"&Cgyb`*lMPg*vy,w&`{Z8)K|WOUKF[âl@V,0Bֽ teC4* f1 =V`HD\L/r%$[7y<[y(xr<0STRY3pw|jСejP~uZ/|BCx `_j"D8ZGfWb b]N- te1n,:tW3|7=)nqNtMݵэvcѕ~k۩U-MPfKs-M-MI>/a" EfIx֭_P/]\5_o~qLnz0w~aBx*Śs ޡŔSo 9aY$~[€M0aFenD5m`]'J\b! y_tg=5l!1>Eladau-4 5ZǸ1Q9nSt@Jc{d4c*mq^R9oXƊ"A24 VT9~ l{BCb A8oJ:ArW[ [D 10OGnR&32y$rt99A 1δvՐ7u9@ސRxJۈR.˖n\A F{k%+/a-HQ ea>*m XUPj}'Rd[1i%f(o(Ȩ/֧~m[`\Mm[=3J13fp7tA[֮%=J\sf ^nsEэ{`X|0^'Bt>m`3ϿOHjGՎM9P;Lp|`yjU;v8eGVWA8j]vqR0Y2c?-*\:p8MҴE[A-:VR~~9ZGZ7u4iͣ:2G{:FP9k#bZG#(QrǒT0͓i)ٞKj[(V6ZZchIh}G(VThm,z9'2} ﰁ|ثBFm~p~a+2HؗI*8!863Q4QKe0Gib%3+T(] &LBLM/m( :~<zrܓ &֐ SvzpST?h{Р1ĞdDR/0DFƟ1Hd+t^Ǒ\ĮDE dŸnVPN-L1Hڭuv!8o=^ =Щo;SB ¡B y<=t)o_57,;KfpcTkҰ@$F#G-Fl%ʹx i߲gqj%ɲ,-MrlumKuƐ Fˣ8BNp\> M}>l7м&擿r##T_. 7*g"/~f%…tv-2T T (]ZV%Sh DH@iVr )͚v524h(g b8VQSH p!k"'i /SUb` aP>įT#Y)%\H1~Me kGWt0`(\Baf2W|$0+fytũY$ TC1J F{Ϯ`h0Q-+=L}W"L_f)7qx7 3}>ə+LL6<;~ZYڙ333 ׀(:DrpBgxtƩ!iv*tsAR<:M5ҙ]'bsgus&%Bn?m%1=ٺ= sa\uܩRERSC[ӫ,D:u| @raV!-J]u~:s('W!RPOMazNg!L|rud;H}(҉dꐡHD %>zmk}\nd.V!I^CRUn x0>yo[u/N)sƢo8Lj__2 `qNzo{F.ȷYo55%*_\۔otks:\'nIpv|rW0]j`- ϋ 8ߢ#g81 ҥP$~8|4FICK ݲizS9vyf%udɑx0uΪUJ.?^5 ©zKnm"pMp VaMqLE[FNuO'{u7Yq4WxCrMQ`!) QzLC$li 2}9:&Y{fΐ$6ppps '@k>̘dorӐ)`>ԘQG1d]lEEB3n[ dc@' šR 49,Z"zp$ǗaOmZr:r+ZJ8Dor-1]"Y=V#nєոK]FrPngjObع%y7hlc[@Y2lC뛺ml%d5eɛ-4ӏǧ1S1A58|lMttG>C$>QGBFBcB}^Vc?%RafSU#wW?]jTh5_G|"5@ѩK57্cx/{c9ٖ{Qmhn-m`ن|W>@}><>'^'9[>9|sJd|0~}c5im5"aBLHTZe5,=ʵDAazmx뺼AίF?xXDW#m;U#VRNH ? GXV#IS| o6.QQFH7 RcM9xZ6< V @7DzCN1^s4)]ȩt^sZT>N#(PF)R ZP7>ⷻ_@( Up~C~V]IX2ZY5*0 /s sg5 [n)gj]=mԊZ6ce*v2ιԕNGl'gh\q-%y!ƵnƭqW>Ä;er:1kNr9s8-pB9Qr ӝ3\Nsp8n scpEG횉:8Ul?\UAʄ|ݕ _ 7+8UP^e=ƳXȵD~tj+珚l ^c̺ MgS?~>qc;Xqas8{Np8}w+rZdӎS\N[9-pjC9'̍F+S[SDB19VdzlNo֊S.L+!>_.rߧ^dZ -XڱCe&CpI FDaKKl Hþ`!H7sPd4R|e(b(tve_8AغCX!?NSRƣԒRsR4CZJxoBJ#%Jg5ң섶DS|3K4075!gM.rI_.lYkLrN8]颰bȂ߸ '﹄ċ}:?qC覢ZqթGw%5m)1P ci2Q̴5(yu ?,Ӑ#I1}JoR:t#Rs:s(ZJy 71Gԣ6=>A[ic| :!8c/ɳҘkjp2ZU}L`ځw&όN=j5 x߉fX>FW'ӜL&6`3-1eRuNq񥔾RP<(PᣥtGiz]1d@uQsj989Wg8E=^Yrz/uU.fq B* )8 z%S3$2wn]+Y+KTRH bJ-[lbNΤjڜw9ϽJ4-kָLͶztFc(~ Ԯhl\x2.;h nꌥCqek#BWOPs{1o{qyGЊSI2.Ahupǣp6oCTH.rĕ6E7hfC.*t tcpmDHjjL-ԑc04pfYp)*EPK.lJ,j؟-gWா{#%:vxAI&~;TG,tx .ҕʇZɲKWRǭȡq+p~(3L!?IJ7LZZ1 sR7N3RĬgYj)kȳS?VD(VI0Tz)CW &[omno$M;,8/1[ΒųTY4?#'~pjc" |;S*(V֡8Ƕ%JwG[5 t <)L)-[Lǖ$QvVAwj/Oϴ!QмnOu7JeVVx-0lg bzX5@X5mm^o:[`'J!GxCfJŨ=G|njUow#˷q'Ԣ C+j2JZP*̊"% Bɂxj?u]wrB7cuVO'_CyLLM KL.0'BpWjD8ٻYJ hDZD@gQྶ@YB nZ3j[?fmcm XU. Bڑfg]f}~ .wOpr{h1(/(t4޵Lr{ա?,8O9VփN *YTy*b(@GP];bCUmIٖ^p&Q䚱 MA?";4|mHSZ5 ao^3dJ&"7JJB%\Ce9 j9*^X鐁%r2Ds-<FO?:z=& R?#YӵۧKOyjDpme%S\ ,`D?Ŵi,}\ | \Z5TBXI8drVX¤::`XWYC7:39!Z0”I9KM~fMA/8\[>+FKa=7vs;>8HIWLH *? iL'[cYdJ3:띦JdfR𘂑 s"ݳUt&y9)&{qbPD&DzaiPC ?")ZD$HdD:KHQ!fƪiy)IG: IU`Ƥ4EpdE:"K JOk87ΩIZa֥3r%/Su&]лcZH:Bsl\vHB)$%ͨip!$&~rmG_%O._QxdRScEf,8z]v-dS gYD%j9 [I J_$;vk%X1d\-nRo/N<%m"hq`S>0׻~|Y=΢w&0cfɑ:H/|I?,!c3fk4d'(}oą_ICΪDYEf#'1a f&C Xr}dJZYEFZb(ZiN{Tx/+I50k]uU'-tyR ={;ҢŨy;gWqK0,tx #Y C-G:J'SJqZjI$YQd4> tZ(tcc-|T=3~NجM:>BhVe#hMVy4 ak]swS"BS$ꪅт+}p7Ւwj)B3\,xD%CYyMq-*5Y~좺HboQt;S@Л6lጘc1eS/DV2xQܢ[(қZjaL5i2BTcd3Q#OpYXEo׺[%? 'G񅷉8c8!pop 7LN?ȚTqaDvL#EHd f~LdKJiADd{i<&o?LaL´i<z~&N't cxW(fsufPwo&4'F~A'h7twO6LED ,Z5Z56QZ%NI0<3xǹZ)#]ƳJ<;x-3O{<"C m%{a߃[lhgUNvϔ gfYw`x&+Zx3L<גq*HG%%=*}r 3|ٲ7E6YLfo#1lVY%1uS}ʔ$3J5855IdV/t0f%|ؓ&Ul&028%-lDNCIf+/+@5]M& iGf?-HVwNfp57ip KjGf,opdl?`d6OДd67\,(Wz+lJf{뮁*'3,Hp2m -q>~sdD2}!#Gddv%kԏi#~%2ˇj2{&~2[d3Ȭ߈'N&MTddv{zْWړqwMf|ҿ̌I];!tDfd6+L &SgwhGYCώ½óR`Yzaâ? (x)QPQ(: xR#,Ke18t9)-,DEEAEed4( Y==_{f}gZI5OtAQ"u+ L!) kG!L#Bt`äU9|NC*ym 5DW](՘WRͩ< CJ&\5IQ'riNW)~H>U UHr)2oqJ*QtT) kRS{*:+шz =7 2\N/24eX`ʰ\Ќ`(l~ QIZZi%|0>IM95ƮX*c\:AU=|ƤUUJ0;!֥ǻ^ЋK3^u9Pǫu}~]Au Ty\f[DK$T(l%**4XJcj:YdKm Wp])[%ZBdώ5,T9>T h^[>"(AMN%z-2u/ejJdUBYuLeXLwKA`(um`Li@L:^&X8FY܏V#l{AdV?POٯnVW*K_`plUn32h+m ʜbJ~LɼJżzB>d_yzmA[csnb@L|R76[&v";ƐaOçkZVZF_?'lh'Sp 5x M4tڻ͆}kP/_ m52pj>+)iBˢ}w,{d6:#wHZ:wd{d:a䎼&8r(wdܑԑ] UQݾ@B_>$ҭz.N~2HQWn7L1IJ3| VªFoz+ֹQb ϡJU&cgA5W譝tߖV#0= GP\~sǓً LtE.۠4—FusO NjxV>7HnOU+GݬNUTG3JN6f[5^ k8 B_&zj;{EM <ɠRJޠܟKuHvsT۽vd8Rk򽜈SD8c9ܙ^̝1 aW23UWN 1`ZG~:j˽aR04Uy!E@KUhL.ZKRH앬!ݒЄ iL H"dYK?X |W,fYNnɀHpva8ǁb<đ:׫27ǑʳI a,QTF]+eT E鮹I eгz,U'ݶLֺ$<5 !`n} I7`-k6ym5n)U](37fwXV'ZēЩ¼2;&KVK)7I)+A.y4g)"tD5?>?9i܆<,uf)ݤJ;$2wUۄ@?㧒dgJ›lP1()6V/u%ι8qݮtS ؟T!?γ Ew-یU3*g.k ?LA=Qɔ)Z] a.\s_ga)\1vSh)ۤ+y-dΙ 3Q ȗش5 # [QWED:hXG !]<6a.#TtO`bpd:ѫ"öTKЙ 3lDz`zltfT<,ؒV!PU3`V_s1`nR6ba߂uֈ,Ȃ=`|-LgmɎmCƶD-ٖY^eMȶ IRCaY'òu.,?XL f$f n'51E@gn9}MgJ#[,5J,Փ"m<{SJmo؛ڵ`x7[4~MFcn] 񷳎kIk-XJ Ѧ#Xw,ب|ʓ,o nׂi[hq7g /v7+:q"Wi)h KW"Es"GZ$mv?",XrL-`#m-3R;_} h+^>+{xs[r_ FjtWxvv}Uq`̦cXR/o YǴ`kX Ă%|Z@,؅fj. 63ktl *Fu3TK3y[8 ]!`ռ+Foۂ}ƂZ-ؚE" v.Kd:[S[̤ۖshmCƶd,ٖ|'yrȶ<垀e=;,;ɰʼnX\+uLjG؂og~k-+aJvݶtDcZۯ6]?Yl") -ς)ٴm+`|%m\0WRotOo0ר_!ep|/OđU+o8~4QӧtnkVYzIޘ"9=VH%) J',!_mh< t=uTm/M1T%.Yȃ(\(Yξ}0kt} u֕HxZICgSot}n2h6VXM7<4YI!w[42l jQ-28'E#[Vv)~.1@=&ۯ ΍ J x{MQ/m!II$ɷ0:&J`bRDd-& 2!&+l߈b\ 34K~xQE: b" =I\"K=m+f9zY#@#'lgMrG0BO)2c178w;aCidȻ8JA n ['!Oy מ7=)d(P7;'ԾLߘ_GFm C G@p:7 A6^=SP6]#xD1M z{MՄHEhd$s3"V3;5q4@ áɘ=3e6ܽ&p/帿b-}rw4sR9̝T:>w݆ w{o{\o?/q&anu}Ԇ{H0v4ǝfya|5i@ d\4pY)⽍a|ז+" y/U!U5YG4 v$VKwɪWQF  g< <`|D5܁|S0/g92MӴyhj6K-|J#!7C&`[LR䑅6)d, 39dc ߌt yH a9%.Wr% dm1Ɠ"FB#ȡ8dg `+̋6ߌoU1Ar ML.%A´\d6;m?wh;39'y 7E?ji.ng|ڜ' AgOǗ5W x:nP$+c@x 8Tn[|dݦ ]^0_ǺpŻ6`o_y/UU?.2$=ɲ1҃qfdcqX:yMg"G1}0/GcW%LǦ3s&אM42xa C{JL*]w3ܳtXtQQCDwe;zab*KH&"EƐ譊&#;-4 ?Ț"ZeyB (r.\Qț5cgyWYf\䆑 )@n^(@@TEթ ;ަDh.u=WkKv-Pܧ?̪ pkOi2C1kzh|(heN7 QG+O#fPQj#w@rH"XЃ:uxh^OQ_9S<5SF+9sL]Sԗ5ZUy|H =H9S`|WB9(y&y %,B>]F.\e @WɹE-Z8D"d3hm56eɩFN49\ <*LHcP7\%$H;RjZ[b\}A vhǨ`3(@/J6FqJQhC7 @]Uٔz/PRˤRZwĸfoKQmMQ .aY*P{f m;IPѯLF.TGdK#1jEL}?R(A}4O`-)1dŨqjLl ;utROgK%E=5nvWaj =WH^IDȭ5QߥApKy0DC="tKzn) zZLEJtߛAUsgqPҞj: ItZ[6jG6Լ/:B&MH&EGY$LjQ,eۤl,8yxn)>1X]lЧmHxfc n _8wȓ< U<[a5549vV ܁z gIZH̒,l%0,),=k[,Վ͒YjrFoMͥ}ϣZfrYgRb¡q6A-MN)u'd'8q)DWG I*)I~Jⱛ$]4 $܆I>JbPw!\Sߙ$3$t[d_MW=X198Lc,fv<=k.5w9IJ19Z19dEI[&IP'9Mq{teF>dԹ1Pw.fޓKb3K})u F}FB= ߴɡԥ+uR_B&H7S_KԾ@?QH=VAJ}7Fmedj.R#d7Q7xSJgV!uJʨg?ԑĨqG0um^Ԋ!lJ`nn\' @fz"4^H=Jˢ.gvzbg):B=+1jS7z|zҀ_q]gK|-FP[oQ;fba{A}"gQQq`۟Z订8 t3:]-nN̕\"r뺅2ɪҳ"Ą__% 9~vBܢott)~2D7\o&ֱЊ)ԧmWC6h+pp=hnB% E'`d~<QY#SI_PpELY[HI.!2|LcLYC$fj=G;iV4θrpqCf3>ä}zjJOKPAM Wl1. Iӷ ,1{z*J +R)BFnqC+ u(zSGGoBj IMd\k bԸ*SFkW!# )s8N5.n{){o5nBt^+vyQBH c{7Z)7 uE5nB"SoZA?˅}JP/I^*FWJQSSJ1jz]:NZN_HP/JYi\jK[)jP}/FPP@m"B=J=zRIQ; sŨq4L}g1⾪コ&3$|ULΛ_uU-̕]xv42sFLw_?vN{_u59.~*_۽'QmÝ:)_tk#4nJx~U'n$j؃ՍoI)/US%|O2/UM]euJ諦(`ahT5WɾؔUOoW]})k,w/j($`~k:oŗG%nʢG?#o D?JܵUgL_ՙƙsL LqEW=y_[:>|.|Po b>H紥q7_૞MzI_ETFFW(=)/G*7kxs/[vAU |vcK*BlHc|U^/ߒ $O#z٠ܗ@5!]ejehEZ4ʎGz{P# ?{֟Tnp7SQ`I)Ohrriϯ4:ntV᯼xB܇ SӹdQD2(ĄpdHUZU6,FUH Frye|GoUr(pS좸*'lt xQTa-M<)ҹ(/B5 MAQ}*9*l=u %Tq k!\>F7{rS ǃx1ϊ hب8|ڊҾJZC[\ѿ[Hrt6#oa.TЊUe1~E|\s +>e"|Jq?9Id~+ga{>xbO2НmS2}'$zJ?w)0e Qw8Ӎ_vp+#Q.\a#4ȅi$5rc_l 6Zʡˬ=k66-#j=TnY\e4Z sp'~%sNGƵ$E Uo,8J.ՠ~D6p!5WKY{Aj.rTB!h4+,+Rĵks蛸t9u0w+v낽f_Ż"g̵)V9xք¯像ӻPWL Qz+ٕjeAegh8g. :(w200wv; 0wLONr6l5 %l 0np6<<mX>^І{Mڒ4B߆hU\TPa#G:K6"߆yъa="q3:@ &EYuZZ. <U WɄ2VX ~_ߩqiNtC_iz]]G#ÜlQ~[hV!I%ޖJK_f_yeOvH讈4 .4C?n ֩*~X##||h}֎_U=G5ue𪡤4Vmi--(8&-`(fS)Z6z=δti-}U}Z2PZ+*3׆}{B@?w'!;ĩEQz|Nj'eNŗBS;C_}jb0Nڝ!aPvG!]h*cnAgLrj_w jߴ )Q%g?-o`j7vLP{L?w.J*BP-c.C[nE{3:WZbHm]Y085eS, -kWV6 \Fa@}<5먳6 ƒ#YddmY҈,fmpj0/0a;ᆙߵp@yY҃dufRY2;U5㑃9w4~ O,x6~W,1[ی[:ZVcGk N^BG{y};Ja7ejwrڿ }n E4f1u+XVeS&y/hoL jP]&n2aHl@7wP^+*Xreh} RK Fch܈n@o-qF l =Z:#BcYY"QL *>җ'7Q~}qUΏx*c ~xCa/T,ǤWh[e7|5(hl3Cv>9=`S@|-4 dU!N_2w~{4 .(/b־(6J1:,#ʇkeD c2|`D7N$ʣkDyhOFqbhޯ~-tv"?GHBr䓓r;OƑ<@,8}CԶG<P?Gx0⏙W[M߇S;vn |PmEo<SECx*rp#V2Zh3kӵ|X.R!MF4 mgLRdJ^^rum1귫dT(6T*hp5J]h*TqUHnШkBB)mPOMh+bo3PEmQ7q5ĘM)7w ?q6n@6BE+!}-6 c*Yؐl`ÑaS  W+dl({6+-~:7Jtal#aC(!zimVTGC0`D 0"v60歓ܝv@vXZƈqWʈ͡A ͈Z~zhF@Fp Cl1Aa 9 J4N[|d*҇lV8Y #۬>V r vkW/ U=pDŽo꫃w{rU |٫~S]QǢtwy G55#w,r/(rKo9Pj+jY+Enjl+Rj䂷mEĭ+V4e8dZPP ܊lmES n:3,ƬBlBPCLHw=R$[tB:_sIJ*)L>Y%iyP%;ÃB=__K~-'q4~[gOjg ܐgm!J'EI1b %ݓ4φGW{esOHqjE#5Z܌VR(YߒIiU!1QΌRxt f,| Yn#Mg Dq_w)̜Ze9{O%w"=VZlJfWʶ :kJ_rbNvaѮXfAa@qa > [1غ`~ +`{ V`n``#}`ؾْ`^1ر`J`eA`YSl{f &Y,Lo`W`5^V+ ˁU~kߪVd+şB}PG7eJ}Bi_IG 1ԕ3r{CgPZ4x4QibЫZиኅ9u M#r9Rϲa{[w4%%H4( )5 -|48<tsSU0PeZ2o{9Yg&u^0`6lΎs+^ҜMM-h@:Gvq':frNe[KTy z:l1ȳ'|U -VEЏ\uC.xmn=rX(t:Z" U C-ƖO9YX +(:c{}#J@"'9Tp:/D1Vvo7ϛ=h -dz#IMYF0/]"Yj`N&ܨ2Hbl ˯Vg%4 P#KS SW/G6po8 WAX'jzcӢ9uJޱ #\:̣~sB3 8>!̴wTk {KD5s-N. t] 0,Վ*uxQ0 6w_9>4(ҊZHḟk(܄3Ej\gtlHK#az[D^p`Fx<$pvB$#I[:y<;WE>>!z 7 %]XZ7Kk*2VN@SJ>Mm֘Z$LM9"=Ѫ0Lh# Ϛxaϳy}ˠ6ɳI{q yP pT;A/bq_}K!5K!:.I.Ds3qu;`"s=K ?m)UeiRA:"q ~~3鰧1.6]" woҡ:l+yDr*87$h:y U|_>a@wn8{0C8 t0,W=#MvA9.y:2qpmO(ŕ?@FjVԫaai$'rdIׇi,\N'92'q'9Mw\ kvBt=ѐ4271S`! e#{5q)S%gݚvg]ԦpuĔ )!'Ǧz0,,BAԺfpML\!^ Ѫ'FM)CaOLfo%4<xB2GvdbҒ%%r,U W HKSd&p&5yN+Ɓ8o zX4qJ1bFN xx?1إ lz(y\ 10c@hF>LP!* G*EQ0($9Ӂ&9. 6jyxaxR':;m/VO.3qeXv醸`7Ff(jqa . 'JgQ;i* 0l:<;4J{Xi'Рk>|G5q:îzc\qvV΃CܩkRaq*INBwu9w9D[/\!Lp rr<-&lORfb]wӡ-eiԢzY7LGT% M\|1rj.CSXԠayP-Z\J^8Pv,McK ?f)Ser (G TJ})ջ uUj1 xۇe&q6I`%?r\Ex#RZʕ hĢ_qaYplI1צ T}Д _̿2qf;<< sߡ/t~qLLfT/ᦈ+~X:TUCUu>u>5j+t :D ܐx \P\(ƃOMWP~n]`'g/@8pxJ-.3NNM$z͹pl'(_h#sUܖX;$Gv1O,vb@y̲ERMG$is (D}go^Qhp0( fe0Chbʃ)#)O)ǑukZYZ[޲2J RPP,sJ4Td~kϙn}^^k?}>sH\jADa@kz#mzT+Ќ7ExKRhvRhfCNGӽTŮAp*ԺoCa%f.taaKbA<-3<"JQXWIpxۤRiɀO{2`o)k6zk_1؄7<`j,ZWP ,F>H⤈1aԚHB0Q[bs|侮,?"h>r…pG…w>H [6݊ЭnCҭ,| :NT8[0yԒ9_&_ =j: ՘􄖈֒d+'ƦτeM#ҌEhXM!,lkyL-p8]XKOQ9䤙,|,+7r;* 'ix 6IZ zMJ"5e %۔/$,WŚ =&1Gg ;hqcRd:ݞdsΚr#9K7rU0kLP.M| oG(J&fYR&g~&S ƹ2':ק,-晛}FÛp6{c_q$B/ez&gv"2f^.u*U^aPk2 h t 1@?_5>UZ$(:3B+uo$=:4PNM+Kg=8,8ãm }Uh q<$xz,k-dhE h\AѴ[F6ُR`;$8  Ml2|r> $dp78k&!6ݥmG/iIR2 J*`X OӂӁᖀ @V(Zc321982B*}s1i[%ɨ:@8A BBe7QJZ50ϑl3Td814ŭњ% \ yPL.${2ߨv8tHXs9^5!(Y&(Σn Nfl`=Nci{T7{_*;+ɇ9'#/ l]aV2ܮt?%kZ"XE(=^eflq;-~EՒ[+Zw\m>!2=3n8Vd /Ph-Aine9?UYM:/ ޅϹ^g%1sʆN0@n6DH)/z CxCU^#M4{%5I 3xCkd>u׊ nl+0GsV\ @rz#Š;9vw". Xj (l `9w9lo ٓ4Ķ_sPb-\ Z7%[K ? ,M>W ":kOs^'N;Z̈́e49N_"=Hײw/MKiꮴPsphUxÜ KgKȦU5KY'Lo5}=)t0,⟣ӧK7Tje`QG|QXh ;8mS'F-l'$ .rrxGفמ^01jӊ~@01Wi*hk˜tf4JK**jQ];W[֒j:xq2hg}2H=ZSi*xr{-x)Rb/ '$[?\d3j%ʫ*hrTVC[k"&Z:a;5UIEP;x'BZUT idG#G呪 ?\^NЁ5}a *JLq/鋝xۓtiSRCO[9UlKA`>Ul%6dD#AݪT̄ȅjK5\(wfU ؖx:2ӷ*c1Bc3 Fr1Sy y5UdPt[4c9ZSyذR9pP8kP}S\)s͝ja"7V q;M.+K,e%,8x.Jwh8q-ǁF=KixWaXW}AOEzKpDiLΦ1Z'S ;9MG\].Xiqgy]cN||;6bbCb+p >G!Q: 1ڂ'*JQ0XQW^\)XXU*lٴ Ur G$7J2AWRel FA BM= *+'fp#*}$0޼v-TR[h {V=k'vZVQ' 0⪼QNB1m탵4nm!k,ܡ݉sBMDbB9mGXןM qmw.F5գ+pQI 1⦁r V0nT=~ȅP4mmz([ȃsDi,(y_gi j厳UrzX\h h Tyknkfwĉ2UW֪90ڻT](P8v#Ĝ"B{ 'Rit^ Ib@6I1SZ>Vј3 4:Xr3 *Á+?q|]gqڰ'jsTQl2`펋IBЀ7#.F-X%ч[4GWp0Xs+ewh>;p HuTl W3d7#o][W-`f Lb/шuDbydsGjQ)a!;:k>TB򴘇pg4@O:Joup-F@{B3(m ua[svMV!WYKT*꧑ x| [X\ [" ٧#Qy=fڍGw8t=Xg/pɝuhm_sV0njJkVH<D!ReKZ[B=!v^\yHrڦȣ$|HH,i:ȣox]f'͋еyMco$.*k%Iaaq蟵0meiX%fmwڬh<՚A*e=IOK#J?4ɳYijJ33l٬={my>3d:.^Uxx6Zt3jGUKgmyyFn#. =CvaQY:"y6xV+yV<1y6}ϞC nϐ]~zg\&i=ۂQ%϶1R$HՅ{ʞ!IY~PxJ-0g@ɳ$~ 9t-5DM2e2 jijѼdCdCD1!v һyq12 ]R2D%RCH*l^һyqrYu3d #_g g;z*=/,Pl ~ ,}j#bܞ!pj VOҳ>gQ;*Xl,I4ݞ<${^Գt`/8Oix䠶]N)9IB:!Q͋}4ݼVȦMz_q=PJr$60s' d/C $ c=Dc3ֻ_n5m5A$Пh.LoK&/o|rT>sX60 L '8j .cv/coN~I&!fnld%=Jw56Pj$S${lj,jjgkf&Z3%n%ur[_o趆! ? Ik&UnCE5Px[~J>* BU?O%~p_-) N*7i3=o7#kFXSYj;uѠ30wN1420?zs$/oa65w g iIoL_ԻLᕦ"/8oKk(QU/(]lF-Vآa+| Ew%4،-&ɇ3i`5ٺdm%y6U LּFؑ^/n{ũ9`.2 I>%sԏg83l>PmRH)}=34>N|LrZ\U}ODVw"3;~dx0p&9 @?9ws:Xd` ЁuOczKuQfMTeRgQiFH OO 9K{R.O;:0CLr67;Tizx>[JF5km#pHΝqI!ΞTHJⓇƖJX&UNW}K+y=;g=UBU.)gEys9pEo|q*wB|MNCw~%荐o^N|p=tN!yS6p%a9|j]ZoAV_Q^gHy}[1(Y =oig&6kUA܉D='0s8 2 2 4"8N;hdK8bzږVJqpD3l&CcQqkyX%.=UeAv---ۍe9 2y d٩ f0cUPҥXB恴cܘ,0_d) 5ݶk:֜òH1, 1, !,AXvaY&%4 lHX4o/ tKeR&,IXӰ,,Se 2),˔aYvwX,/VkTv~/\+H~d㖖N'Oj¡7|dꨩfsb$s'X{t%XqY OQyc8ڱ_soNެͽO&YȉOfX撓5xjy"?ÙDwŏZӃ'~fX0rC폖>?F3Fϝ0̝p+)Cl"ٿrzYN9笚W m4dΪYIULY "gMaJJ 7O3/5F$l0'1KsAu)عM쬿*,[]jNR0Tvhc?GDM~Tɶ ֔,cLy.R?yd[bڰ3bVo鱾~Nƛ6r9DB=G4g5|ffl atJX=P :[53Xg3^?fk7^Z_#AW],TA |(T tGL]>d?JhT}2^%N *keҵ3H\Y<+נӤn,ąQl!L0L7=)6._FϕolX PĖ^ߔ2w:0|/iWq\_fT`{BoM*[IW` 3_mv&LR2&hTu:8ר0 r|0 r% A1e:ԺXKwaQ;$Tk tAȽ: *Sad&u ?נ}$w?䳄|cSCFF0"I jˉc .?> >jG8'D~e2fxd~X?fn4 {No[{|-+,l:{D8ħ={Ds }ě3]Ԗ‚1JWdz1w'tFeh;a6(F=T&8|-h@FmbdZ31Ao&toj=tU7 7VTD~{k5 D㧬CG2$[=͝"`NuC)Ky vRP[swm82w"ot>ꪰ}ELCy!]I:5Km‘*B̀z|]= -Q;!Hay0t-l.Xzr[XDI]\070#d9sYI ek_8]8o Po~0U׈,X;n;Wj|$w]kkm4aP S8m(Ro,"x AI QgP, 1l9o2V:.[<ŵca0|uiRL&kz)^rT ȇ\"!puA;oUH]:\^oh= 4 ă#k)R3j3:#&k4^ AL- ,Ͱbh36II[ ,w! P3 7 5D3gP(~ Q9.>M[I?$}H# -Ik hHFn0jv4ߨyp)^*w RPa^t:!-`˫"om'x ڕ La _i߯øtu8|01 MķAR^ēєK>7MCYR 0Gd~;RȦAL=bO\26Iep'Fym  <ۊE ~7+>ʫL_01pz,}"N5&+@{.NWU:cHWȭeAgf_:QDPk&+YQB Rř!yT #aEB kIg1)<"Nءv~ؽ-K@l'=|${ݭp>;M䋸:AI#op&|x/"Ke+6wj؅9/  J% mdwiX%)~ !alz~yY|Gjmv(40g.>R1zAp-VL?-Uq8;Gy\LTjXE= tj، 07Lߑb/I%K|}Ǭ~^pFm`YgK/D{54?DzZc"q\o Yp?`DZO> qa>og{q ͏'E 7\3F^@H#YJ*D"/ ePFeTbdBP1L:{I)b3 vPUet "RS]ܫ lNC*ϴI#{( c!޳*HOF?A+ ۬gt ԉAt<)cx.%׿B#iGUЗBPz*ij!;$NU[NIbd/"$=P,ȅ}0fTT6Ї?],>h \Yh+@ɅaXh@;Hݛ=mKW50d!BQ|6Pswv}wE\(rToBᖫ_( s>,4 BbP⢏sq>B"d7cHẐaŶbg:*c*bwTL'H1JFJB+Ќ 3|͹nd #w3lR0[bHEld!QS0|򄊡'2F;f(kO8#CO2ղĐf+2:b" 1] Ȑ1xtzd0#ZZ M*qE˗;`!~.3ąT vdF;fX`xR0 y3<`xB d{5@̺hl\Bn- nR`,!dB*5 !;p5EqUFdX1$g@$ zlżUN?(cgZ&[#yG  N>ږlFe?N(n7dB7.D :fxͰfw,^6^zR#9K61;!$DISin8E n/R]¾FhD]N-w$g)HcLyjG"dxVvկ尜 ~.(Kн,G}\vYR-UxaB*6\Vl[_5۫ 3=@l5=j!Њ#Toέ_RUdB"$Gi≐b?F ֊s?[ aJj0Tmp?B w(y !uSI k2hB ~}E ub6W[H(Bjb+KttIԜuޓd ~)~J0E5ވg~Rw$IOA>/`~uz>/uz'U:d38!+S |C>xGe;g赩 EgN\]ZtD^dcgk_V@K:t:1 Jh{XjP';v ~5]DO{JRCE~̠ G4!AG3A݈(g[1,ڕAxB:S^9M :L ݍЃA0a?M;l#* !yA3O~Pi8BC àt!tB{20ߟh=BNQ BS ZĠ/3ʠ :CibЏ ڕAWC[OA ze>Bq;I_05tBzA2_tR,@j%'Gv SK?fp[ʠ[:AFRb5 6 3x53 Zзt Uyv4ATCG!tB0h}Y gH XЅXA G1~à;jCH@w22rA0? % @#) :tuU ˔YX1t5kw=І(`:V1B;zwʪW}A+r :b`w Bgе 5à355ƷƠ}݋R@jhBAVAg!G2Šj8W7( !dQR[:yZ̼❡_I7Fb)XhW4)x,J?D{%x@2˜TNe-DWQaPW!u^er]XHs } mN[se0`pK .'!x61懕`} A&Gp${GO܎ЈK0+!DB7g[qdcH%#V|M@`WIvsVy TzyB5 $ !k>=AjZ0R"Z @"DG ,Z(&zw3?֦{HUTEGhT JxeH5?* GvTH6(Z]waL D{-q`/yʲxQVbcp!,qП mbv[f‰:}J#D,q -*ud8Bt? rП:a4h, =ᑷ|t0= E% "i8vX*xD.XxwX0G(8:0$^Xͻ\&y!)N<2.W!z3Ps ʸND} r|x b>@"*GndP+Qs`7 nOTDՐ{|{>H庀zCQ5F@꩔@e#QQxwqӣ@=~c6Sl "jCUy, w>b`\+CT/+^=!}1CU!?KGw"qT| Tүaer rʓen@uv>9 5B_+]ID}oWQs ?E}'TϠ+u#/D>/D,nzк@= E>w"D{#}$?lxTV> ܏̍QnDk;%ޕ Z ܩ_sHz0 3Ouӣ@%/K@%oaH ØكHoG/ez@u7߀_҈F'>ߝ\ x?Xyđ'Sx:ۈ^ŹCy8'zB IͰo&sIOb)j1rs@_ɢUj{Qi084L7;D QdFIQr& o1 ) f0p4:Ѩ@ŸKZ qj1rĆG[ix-+ l<:Z@_D-[񎱕NсZj⇨ő'^D-/mHXZFbGP.B-vhBV߿߿ٿk^\4 ~⮀$ dHH(H0"ŝq d%Hph1<d {@rD/ H(P $ Y AV<eyd?!ANԂ r g/. d$H4T W!}BA*AfOddrAd e 7@Z@|꼸 #@ SAf,Y 2 Hh('@@֫` (g  ƒ,1y$dL oX mf V >ÿ 飐V!&('0̀= >8_G M A31|}Tsh ?1c+ p=ob#iQƶeӀsN tk|F _ eyׇc!O~c3WBtTGAVzϐN,AY8؄KPp%e'/DYBfdXPQp|3e9 KP,,)$arc'f/GYkLg@# e؉E(bNc_`FYSD CrqPܥ/YxI Y/]1+ǎM_jzj9tE1|Yn;ZWǶ^09uwͧ2$d`%/Y{+?s3m bXcVf0b@Q %/5XJq[;xg'5 x/HoTc٬uk?33\\a~IO_@ nFW`Xs#mG|ǀ| \߬壳\.9Ƈq Ͻ }>)-6kf;S"/,oسws폁j;yd8c'񡼑8f \`Z =x]|cQ5I>sX:<Jd'7oC8_'F75m=f`k>Bo[u|,41doX67kL=!(%6fkDX ^͋1 ck iP,(YޖcG&8T0 dMk.? zZMf>1rÏ3 Mcb 8ըM:jX).@1ȿ i!ށF#f_5Y 9. ~ms;7swzYWxy%BKr|"n/ eKW,&5U,Ϥ/1s R`o~d;?B7>57K-ٰ06/Z GC\z^xbXR4ǭȞ(۲d.jaپSq΀,1/ςt~Vv|.<> @NHC!;[! _&jH.d-ioC1F[Y_U?C''m>ܔ97>qhPNfgkZ|W3=]c"Y7OI~>?~ӣN f&HHGW1Nfa_G=7A,3=D'We;ח\\1bٕq]fwˇuϵ9݈?3N3d|ęi1'aXFWMWC0=L9y;Y^9mԹGX'ڱ C'E3zfeW-+W^ԵϯsN?NG7q6¢;8O/Sg?=2P, =xhtW%^tS1I7D.5c{.¿~s脇YQWy<Ȣe.ݻpO(oF~Oɺ8 nR3q./{0NYrA/ TI|eƾ &i<˅N* 39lͧ)*lgST\ԶkvةWjs"7WkF/r=x8OYԖq)AmRm=*mRUb+HyH@ͯG P?XTF'&fo AtTXOn 'Bek>mݕeXV./)B-ێ,{wPw ΫU|{:;+.-_l;:Sſˀ6͂s3CSV0߭CܡBL{cyk6U\0vc;Wqz-8OQpp)mRqmO+6;~ z"~<\XV[pnj[e/GF8ߖv"=>tM7D1y΅Ķ7|<g8?ĴrW*.3*:Z]Sq9 1qSq \"%4NӢaI[p>et,{Eu{(E? .Ĵ{Sq.li[p͕\+η(u^_u +\i렊km<,5.e9,køa}e.>d6.f^03 y]f( osˏq<8O>~s~1>ӜC:9a4_>^24;9=s`Pq|6ϗw3e=#8:^0+|mlXƉac.`cUb\# \X u6iCX&VՅT3V Kcxm8 m$5Xw,\X6V"0NZh#>hF]qB>مeӾoYQ- Xy.U6nBs#1N\|,p یl7c" dѾ\m9fNuh󝯑E.\ar[Qk0.wpM{pOϱs^/>k cળP88w_3\\&ty_Z]\&|$I3%g8rQacecꊄJ1X6V#l6;V ƊZz$`]NVz쑂s`\dNn: vf ,E]cqrj<Չl+ʶR9W)cLCȘX;\d%R| F H+cCIı@@@v#yO$< d o#a}d rD]1CFʐ#WD#Cꐯs!|Y r# GWC!#v伫!7#yȓȧDV";k<5Cǵd)3sC^@>GBSpl{c$ly&b5plVsQ/{<|3G9=Y߰s_AZZ0t>qcsu5/6/?/{r2/?^ ajZ%=쿝 Y柦9ܧ~|#MSXa1HI:m来B֯ҏz؜5=CQ[ngGBZ}o? c}ϘN=7/Xk~)v0g|L_i{+Ϛνyvͫws.y%]OW:=ޟ~=:׆=Fu{ uN3au|_ ,sm*mӅ9Fϑ~3 >:הc(Ϸ- ίg|m aqxqg[s 7˯79Lj{d{XҲ:t':$n8H}6E] 35nsAe~-'+mose~FοGO 5e~WXÿk}|++_a^%өϑ=%1?WxrɓYL+{*B{&j=7Ww Jlmu:RaOIPc>Y?yR~rMBm3ϥآ{&J7Y{k'ڭڣBm76nU|p)z=^ardi4<\~d\>Kj,s)s-Ṕ.j4Bm p3:6wa1i/,M"MmBmbbf0ܪzO9FԽC!ro;ouK4=S%kzSOBiMOdߟe$MY"dkzS#jzNPϯBO'z(jϫ{BO|jK4ZMma|0ki3g/f3^мÄϿ0yNs+ nͭF;}p.%ϓÅVu;ͭM[h&Disk.ާϿ0յ_>fizǏo!|5=y#5hjPO73ZO{G>ڛFԳm$[0zMgz5%5=Q]e-[ӻq5vym h[D ж[і˯q[H_h۟bkx,wcݭq=OLΎac,jzc /z,?%|alj1Ym+1|3sg+Jp {+NL~He.|6.jS!#U\WӣM^G)y*QCr ?Ky?E눬 KyY3 }ğZ'?FF狯ul{HI"?WT_tR>NF݊tk%q<~sOvg:3ՅyE|֛o]հ{L{ 㭋sz2{Kvڶt VOS;Q-WԚf}`,_|.mڡf~*Ԧijgkj' [^agHm4cg5۵63:Ľ̼Tm O9yJ4?¿ϟ"}h't53oh }sWOmj @qlsɓYI=y*J׿j乃*<~k<g8\7(<]ixpk?j_,yZ߮ԑRYp y{λDy_E%D_𔐧@L;s<ϭY>_d==Sg/*< x&%{Žq Txu7;`lޠ7q`VtL7ȧö5^'g׎%L*>7 n"(")iB+կ@%Mnl& X"P@6""("bA *"0aq%i&g9<phZi%m|B?5B"Ni8'qEE"Ĺ"✉q#wPy0r=8+EOq*E Qi~HsV_'ᐸEI 8-hm28E)Q[F'a-΅(q( c>qEÑǩqf T8ęRR=W|(;G hط#1G~O1Ÿ։&}Cҭ&_T%7s4~'i̓9>iE# h~vLmi#'2"1͏EO>;# }gY7 W#{*\?F_AXw?pR R?-e5BJ}V{wL6\/M1RwRHEQ [!!(m㿋\wH(u!uˢԝYy.]+4=PE;qVѸESjω#LejqdaREјoexE=WE`4n6;]F̉(\h䈶Va B4rhB[LZ!SpOWFhߙk2.Eq֥f-}ɉ֙Nj߫du][GDEs ږ69Gg1٬2mx/Dѥ(]syf75AzO#zwOw} b\mnL?[X΀54h+)vz ʟ~yUnσJuCpkZi[Gi3wAGt4#$o%YtA~QGgA%٠hZ~AAp^:T &i`6XV:A/`# 820L3B T"u< @7ğ ]08ڀ`L3BԂ֦h< F7(e`&Xւ`8 .{0PƂvB(n`PDbAspS纳5ĪxUm6qHJ1%uH8RrqE562<5-+՜3nF Ee=d@2;AS Pl;7Q@ r_y>>+*ެvgpzbrIRnDGdtdŇ:[QATE 1$JJ}ⴺ\HnH rtT|=NyjzvȠSzY'jr /PKv\`6]ԙ@4O~E)? ۭN_kV_ JgnZT,*kݵ)Xpxre{#0E.~>HbaUW=\?r(ÒiQ4A^wޢZcVx^mX7kkU'xmK$YQ"rekC)b/<Ǧx-y"W7yAU w^ MzsڙG,޺I!ï Fٖ&d'Hk1ya#iV#I1P{-V&cf5M!IlDst<]G0 tqn /m/y7:Z@ ̱Oŵf4Ɂz:YɗdRMv"b ~&*J-P[Iyr@nC6Яvm^oիt8rvg`;T8e\Zq ѓؙu;:KG=yy=W;;$V#> &7av -])t )|flϵrR%l@nƬŇ曕k#tÜ(w<&*&\hSc 'K SlMCXb) Ncuu[**~T>Sχeuخ=úadqo7e)~>ėɌ TT,LG`‘ 6PMR|:S؝yւ@~jB8E#{6*l)~N NhZfG,'ҸP|\޻zjNJ 8+Dg$JY_fk^ٌ.eySRb[%zvyze)}bNce#6Q\Fו3J_h"Fˠ Խm<l*8Y?uwEnޘLs0رZ?`8<= @,m 9S^˧D'6:DosX^_b0ur=Dӝ%(>$Z$0m[E~ݗopK|nv\WD)coc}(w0&yv=06~-iW_<_c`aK/JT{G@W>*N`g}I:׻DV `" P;2W`0>KEQ?ĝ@;s":/wG _A~[Do}9E KKn]1D#j ;rD]ĺh|_þ$5횶IJ Di`1- Bik$ZD@GIoD` JVTwa?|G-s%eD`-u^$ð*ǝPE%ڼcDe]vuzAITg@D\ D ;9 ugi/I(.%:Aի%Jز7-oH4tDW].ь膷$༯Fř}<(Jb[Yq6ؗǽا7qwKuBہX`25C@`#X ̫wp؀,Oߌu]5 k16&ޱQ+}̚wAcϟZiX?HA/mI}9]}ny:~ω⎘u6￁:DO2:bbTkft. ϩf0aF-dT=ѮtFUFF`_YjNF=\:z=ʨY!QQ?532ѱ01F'F1:82Z+㼎chhF0^_cF.1gd{6^#oӲH<;021dzӋތz cҀsJft__5IZub3:53`ƛ=]0O fqÊ]wUMێ&١`?"˂ا@tM><=mGۏ&_!KeA[ +>Ly1:հU:za4 qA&*9ĠNc: Gpqz!}>4hāh{U e]A|obǡD[G?/˶L[fff)effffff攙RnyIΞ=~ȲHzsfn:/}qQtG`A8}/a{4REF;NjpcAg7 ~wxe^ݲҖx<}!iU]h#ԑ!m,lߥ_{/kzZ?T6kT؛QAhĈ?!?X<_sٟ񽁍} ;\߇'\s] mhOH H\(x5Qh4bds 9yb_О}2So>Jj@/u&CLJQg,O_{'/'7@1>"M.: yIdz8^wrƗɉ+ ?%nL;Ahf M ߴDrܫ?ew& aY OL)SC$<ƴ1cQU.ք܁q8Ƴp 5o||+~:2+}P?8Cr>>9Gb4cAGk{2C UC9ø%F*b3ۭ>2ˍ;pwm\ 1n|O{WV2WaܻIx!) nA% gK\dN]G#8'HB #,iF$o̺Ք kH=MǜoϷ^XoO59.sX 8O]yl\ND6HB\4w\xx@=_q7ж-#c's)fv C?[nSyw)WQ\D23!?~z|\OO82?,19jor䇕M9тmL)-UIe_,a6>bK1w|fU><71ν!NNEsr7#<_;U,ֿfN?;|3# ) 吉r9)#PџO!kOgm.!SEʆ_=U{w%j"XS9ta.I}ɌBtcZsZwSy= )z2?gu 3jUVgOpmֻھ27K<➈Yk2k"5SWT@1~6nSd[FE)7aOJ!&PN-wP!wϿߧ~wxQ?3| mG^ޯ)Y3Чg6ۏa2 T+Ԅ?M|Ab'E c߬B?faZ7#=0”pi6 #}:}F %!.|`ch!}/O љѽ{GJ | {`Oόr]9OM\ᷞ4Q|}$R "E|F8Ő"JL ~M~|R1oKQ̑ƹRԴR514䂚H[W8-TW}VE҄_?mq>Řţ'_=+|rG \++↖FI>CG:}E `^!ǩc&ӄ܀~-zl}B-&]k@⤴>yB(D?ѽ|r}|u }ښfC?db.Lڙ5;y9Цݬ{87k~$J,HY+o];9ݡ%#o#JAn3qE'yօ|Wg+Z;9H|ן>/M[>Pvթo G-^#o(ԀwCə^B(/Ŵ%RD E'T/Z֓yo$ʒ{ugm8[S)b6j"ŠReB].gPL9dZq]p?woؼ0C,#λ=z/P&j&$4|礧| /ş=4u]]t)IeZ6M0-{oy tF?&Tw `va{ !0 P50 u0 O*J3@| H!"!MrH $M\@Z[؜a\!PkuMAh>PJ=t87*D>!*{k]ug$36k?s}z54/O &J.3Db1|bѡeHL?ߏCgз!q9a}Sgmg؞ ΫYWO t?®gwqw3\{ӳO? (Y^5Msq|!uĞۧ[/'roqm8GymTtJ &b!e8 !*QIZPgFD!NDq؋N\51qc=\AC1<FoA!jev59DBIˏTBtkc'2 {_36MAG^:^Vbh)2#fP7̛]bԡͷ2C@(;g* ?}SG\Ø.7~&o9o>^c7{AI}%}_ !ӊit?|u3jB.su? [gjƵ(qqZHq.H<[B S~oIs?Mf뛏$/GB.YW -TDm{Vķ8fkjb"a?[x:'2)$s,%?Cˎ|tz,%otח7 >wr2ޗ';8w|*b<ljb|ɤ{Y.zf_Ja '&)RH_:юEoL}mfYK>_lf6 7TkbwM)7sWв%N5i׵ Xu}i?' xX{M'lN4'r!QW?1Ӑ%o)% -S&FY L7q3q Z\J9LȚW<%/Cxv>Rkփ#(P}+歬O>WFpOwU'ɧ^|5x > s#Cr Cv|/iR|F9G<`/M_~@+Su?,aĹ|KQD S~o!lo=K;Ap,xW<߿o&mO[rB,kGo{e{>GOM4](ER=˾b9#EK$oR{ g, R\+Ec;GwTm} {OxrX' ]&'p S>7)vn k) OxOI1]4IoH/IϮH1Hmh[~0{6힉 >.5ؼc WyRZG8/ Q!6V[ClEv؅ 1 !qe:bvޢL'C,p ݻ"aWCB u3Kudayikxd 8KLExDhXo(vkE.;~ɮb=D xfF! g&cpn XyO<@;{syHszp3w6@>b1w3ɞ~ ^2_a2DTDG.m@^@i@]x7!|ADhaXkHq 7)b !"*+c/Nnlh؅g8Hkp*)Ңzb%"6EFTFgl{dJCXHm/6XhA ,c$h&XH~|C̦h5p r>d,cN,a^#cSiB\V9RmcxyM1U0geSA d+{`= }!S*@ a3(J-2Xx 8V!UK%w|BRX4񍼨X3xeh ,\"|@r ^"by 5qW~FM`"Udnb6,J,uDlxUSFcq)1w )cvªAc)JbBD1wfA(х+ٸ$ztg.bX(ؓx-;c16!z9ʠcv晸}M+oQ1 /do]4_8D٫q 1"+ʣ `&00XO(=q/wUi;ޠ p }$yzb:-9胕Hiq0lPhs=tcX󘯨#S%p"*K)hEI(aXxQ//pYq/ St%RPbn"sL7b)p?"6}Ebq CJT-|Gx6"j|%z0"%P!.hB7!ðנ%V|kr\OHB_,"IR%ʣ""A2%-&b;L3P0 S;AXcx JdE `)¥R"#;f \jڎ*5 IC=f7U$ Sb3ҥW:c5gyX ;qߐ812H>6%Reg,rP/$0"<%FP")`+RP,1z*aq h@fB{DˈWʘ{H׏ F܇zG dDH0XFXx{ka>ؠb N# SZb.j/8d{ɜ@Q#hC1ψ9Bt|Fq!x%a0v"&8i'v@M.,g8%OaA;Bԁ=1kZw{)"L%4q if'.a eN7jͣq>!$]@-d9 ,EŌ;ޡr)&",ctB_h؂ȼ>\اT9apYSwc,3/+ bG*k"9*KpO{A^Oo縅7_tl0qo6mA} bsxwX?P]0P.{{ԉ->1:P7!{$G;6r= EV@ħ a'> 3%Z`-9,b`J!Kñ<"kxHohb$6;r 4~O=EOYğ٫p )0_FbP'Ϳy>!ng7aBhF؋G0%a!n쨃~Xcx?0 (a8 ,QCpʲDzTC,!mbh؇gX"'kL-B)S^851 D ǫx '%Ң<:b2~!QDKAKFDFA_,EDH(u2-1*%R c6#?% Fb.Cft,9|C(H:^LKdAUN!s,_P?6cոq,YZ;vBģ0 '1%*a*n QڀUx wND]؆Ȟ)%9)MlɸYsM,)RZ++=؃ȗnD4c#oZαQ0%0?@K|@*L(-3#$+cxT,%;q18p9psZ" b.n z.KThEܖ؁kyL%|B%s,7 *C \EBԏ)x셹㰋wHT~b+-%֢XIt.E?f@2mZB/gT +0_~Dqe]U-qݫѮjԠ0&Z_lF:#zb!Nem?^!f=0,ϵZ5h;245aqwߌ64](ւE iMи m-%r'vCo8ϳ:p;2P%dg7t%Z`/btcP;v=XC{Z&bS`@ެU1/v\iF2wK@{l`Cq,} Ƙk|e1.0O!/n`6<^!%].ޘ;Ht2O"H|_WM|?OKf5Mjfh4K5Gs5O "h5?-YEբiѵZL-[iZB-XK%ՒiɵZJ-ZKi ZF-YˢeղiٵZN-[ˣiiZAVX+ՊiŵZIVZ+i ZEVYUժiյZMV[iZCXk5՚i͵ZKZki5Quֺh]nZwSh>Z__ i!Pm6\Fi1Xm6^M&i)Tm6]fi9\m6_[-i%Rm\[Vi5Zm^۠m6i-Vm]ۡvi=^m_;i#Qv\;Ni3Yv^].i+U-P]nh7[mvWhGcT{=^h/WkV{>hOZY}վiߵOɯI)uiHS*iI[:ҕ '2ddEFdtCƔdlGƕd|@&dbD&drBdjFdzAfdfEfdvC改dnG_2, B,"b,!KR,#r +J"j!kZ#zl Fl"fl![Vl#vdGIv]dWMv=dOK޲+r(r*r)Gr+r('r*r)gr+r\(r\*r\)Wr\+r(7r*r)wr+<(<*<)O<+(/*5y]ސ7-y[ޑw=y_>#X>O3\/+Zo;^~'$?/&%kuM]nNG#~z$=EGӣ1z,=G z"=DO'ӓ)z*=FOz&=EϪgӳ9z.=Gϫz!^D/Ӌ%z)^F/z%^EWӫ5z-^G z#Do7ӛ-z+FozIwѻzK{}z?>@C0}>BGc8}>AO'S4}>Cgs<}@_/K2}B_Wk:}Aߨo7[6}Cߩw{>}~@?G1~B?Og9~A_/Wz~Mo~O?L_N? Eӿ?/7fHC7 4aF#gD2"QF4#Èi2bqF<#Hh$2IF2#Hi2RiF:#hd22YF6#i2ryF>/#Q(h2 EF1Q(i2JeF9QhT2*UF5Qèi2juF=hh42MF3hi2ZmF;ot0:Fn0zk3 c1j 3#(c1k3$c1ŘjL33,c1ǘk3 "cXj,3+*cXk3&cjl3;.ck3!q8j3')q8k3%qŸj׌ q˸m1xl<1ό xm12>_7i2~i4u0MSi隞 oF0#~f$3ŌjF31̘f,3njk3 ̄f"3Lj&3)̔f*3Lk3ӛ̌f&3jf39̜f.3k32̂f!Y,j3%̒f)Y,k3˛̊f%YŬjV35̚f-YǬk3 ̆f#lj63-̖f+lk3ۛflv1f 0{}̾f?9h2C̡0s9i2Gc̱8s9hN2'S̩4s9Üi2gs̹sy0|n0_|o~0? j~3?̟/7IҕLl(Wy* "OERUUESU SRUWSUP%RUT%SU RRUVSUQeRUUeSUSRUWS* ª*⪄*JҪ*ʩ򪂪*ʪꪆjڪjƪj檅jZ֪jک_uPU'YuQ]U7]P=U/z>j!jjF1jƫ j&)jjf9jjZ%jZjZV5jZ֫ jڤ6-jڦjڥv=jڧ:#::N3:Ϋ ꢺ.+ TuuCTmuGU}@=TcD=UsBTkFU{A}TT~'fIK ˴eYXYV+gE"[QV4+ÊiŲb[qV<+Jh%[IV2+JiR[iV:+he2[YV6+ir[yV>/+U*h [EV1U*iJ[eV9UhU*[UV5Uêiղj[uV=jh5[MV3jiZ[mV;ou:ZVnzZk[ k5j [#(k5k[$k5ŚjM[3,k5ǚkͳ[ "kZj-[+*kZk[&kjm[;.kk[!u:j[')u:k[%uźjZ׬ u˺mݱZzl=ZϬ zmZ >[_7i~m6me[m;k{v8;hّv;͎nǰcڱv;ώo'ډv;NnSکv;Nog3ڙv;nsڹv;o څv].nKڥv].oW+ڕv]ͮnװkڵv]Ϯo7ڍvnn[ڭvnov'jw=v/;m{=l{=m{=ɞlO {=˞mϱ{^l/ {^m{lo{m}>l }>m}ɾl_ځ5}þi߲ow=~h?O3~i_o;hnڿ߶p4G:c8˱q w"8?'ىDu9ѝNL'ۉu9NB'I$u9ɝNJ'Iu9 NF'du9ٝNN'u99NAS)u9ŝNIS)u9 NES٩Tu9՝NMS۩u9NCi4u9͝NKiu9tv8]nNw8>N_ t9!Pg3tF91Xg3ޙLt&9)Tg3ݙtf99\g3Y,t9%RgYtV95Zglt69-Vgtv9=^g9t9#Qs9tN93Ys޹\t.9+U'й\wn87[ms׹w8Gcy8ON|u9ߝOJWw tk븮n7Fr#Qܨn47Írcqܸn<7M&rIܤn27MrSiܴn:7fr3Yܬn67rsyܼn>/7[-r Eܢn1[-rKeܲn9[Vr+Uܪn5[írkuܺn=m6rMܦn3mr[mܶn;vp;np{s܁ w;s#ܑ(w;sǻ܉$w;ŝNs3ܙ,w;ǝs ܅"w].s+ܕ*w]s׻܍&wns;ܝ.ws܃!{=s'ܓ){=sϻ܋%{Ž {˽q}>q }qߺ r?_ܯ7r<ᙞ,0p8S>4‡~z>*|tc ?;熟>.||'>%X8NT8΄\8.RZx$!,GcMDx2<>55|Zx[x:< >#|fsRp=</Rx{:~*-(Jǖmc׻R[o{Z_(?OT*ӥcr'!(Vgtg?," l/$J2n0G>E%eV$0MٔJxbfp= V=6466 "Ԧ4\^s|As@ˀ#"6ㄽ[y2 >_/vK̀WpGվ׌+4WpGվ׌+4WGٲW`\T^3xP\!T^3xP\dvK\f@qey־׌+4WؚG`[Kz͸As@qe5ٲW\\6zec1}e2jEFn>a )MA "h@%uU|}^g,P11kή~;4v0ꗑle$Fl7ػrHkN>5 \ \feK֊vsyX<#7և:CݒKU,[lTlgteeeiջ H+9(0#@↟ԁș lCdZ54^Rt^Za/ jdXk)(j57b7 HQe:  C l/#Hf@3jFy|^Zlu}I"[*#epMr չV-8[$ISr|e=Gc0j\z2@1i&PZ+/R=3]l~ b|lÅ'b"L`mT[c?|4,*xIƧ5H׵г)$6~=BbqH?~Vkuz J$f8`KrU7twwmK&q!]i"YȬD$(EOgPcZo,4[dԇ_$ D`' N3]_6QtS윴b']vN:tF g!8qS ҄#TV h$-7zR tԝ) g:ThB]#-5ʷ{4:QDŏ`1>'(JʎU8HWVպx;#E #cQ-ɏ`T*E\qTjL+*P (<Q)0RY2,rfXfXdʰțaai%fi+]72 +|vUk}Q+QE {X__y.lW"fLgуk 97~Ӻ 8  M#b'Kw bGR59H&@& k''KwW$k8~w&$J+Iny &XQ "Qm Y4G4[0۔\3u|iP;2WwTeVKڶu|LE8(^%Qtb}┵-"fI uTeZ:$ƔFI4k,t:>{D T3KX]VA&A2ЉS x?f_!+G2FPdRƥhJASu޸^$&kt%6CQ'BP-8n351^351n3!qz Bvu Z>#H!oXυPv1q&3(46#nB ri1Bңcfpcg=v9kSfzopLJ`6/w$& jQ6ړL8"Q$%쩶h$@y˖f=N@NXSbiF[bGCЄ>V*фܜ>WЄ>ЄV\Ν}/(!-GK*=նmܽXA.VGkZtĽmKf{%Lszlk8c͔k}LsM ұRE aNM+m5-ru_eTi8q<4ր`lI2lӛ+1ЈLQ`['4i1eᓦ0wJʼnYF͹@A1ւ$wt|Zzd$pqϻY2<8J$؎(`$pw82 rG :Eٓtٍ.TV,z09:#< <`ru=<&L7$J\Y&vy@ _YJVt" '3d B}`A' RѪb* Z☠* Zv*i6☠*it☠* Z,qAM1hYN_x1-FRѪ( N \N ^NiBNi0N dP&pጕ1YC^t25fZާ0uI]F_dm#,#7 D\S1#]rLdw| s \}WM޼d' ع˷u20Ÿ 53kW &O5fAywO2oq'u{g,)[g?vn1or5cI Pt._p2{EepH)[ennq&c -IMK:oA"3)cl8I@w8s A(_4u+FVSą*F:=e*z*i}4JUYg/zd GO8Ozڬ GOqMx*H T#E)[5?9cts^~艼Ocu\:ت-T۰b^'d.Y6{c[JqŸKΓ,n`Pb+v.߾+ (QJ R:`VQJs{Ref55])mNjrĐBD$ۈ$k+%JkeTAncujWS}Y5R #F$$A޺aDT_F~)T (۠2V V VSJX@KZ W w~ J&H;&Upy J+uJ*SͳdqdHHHtuM&Qiֳ $t"u4 > M>ύ> > ?7QTNb9@ i8 }9 u8̀ mgЉ:`K p0( 舃3H 00Bwp&[ߺ1F эDz%~Hu$Pod<(TgT*ӑֽQ[ʴhLcBpdRe51.cgHϔOkWK~cw_.?M*?VjcOEOTܮAbP#g`&uV,Rh7];yKm)RVmzͥv>qccj%&*HdҌlʎ諗]\JJc[%}L~z$#u1DU̯H/O+&N^_okT9%Y'qd^K >/[#cS/*Hη*͸hy >Us5>F&Z nV@ue42eX[+H@&uXl꒤{:,PV绍2;6ZGj׻.lOVGNfTʿWP.h!E7XĂmhh jr'Xu&OH3:Hъ 1^ՅV!Oy0۔PYP4"w,^A S96 2iLN r JUQ El/@KtP+mkV#Ruxg:I͎THH3:FkP5 xģXTo(hYrcz0_ucÉ xre56Vv:}rwlhh:m묵)KJ$e0vdD2xE:*238w!xsZI2&|qNM>B2&ѕ]swŃdţRѳRѻRQ.ptfwYcѴkqy sOsV1Iŧh"]/v]EbOۆ-&$H;#%PyslL a)+' ٷ`.4nRVt;JVݐki¾W˗ 򅑛v[E0|u7DxQ#z+6v;yҮ/#Q>VpmmA)0q@s19E0[ڋ;deT,2rLKoʥx@>0FK:4k951W8[ib$%-zWRHʨ D| +D$+X aBiXV"-4V)E2nqV9ju5!^W7{@v H(hk?e~zuaD3:Bjê k,Z|XUD7"k `M|*tSCr6%h#-\a`6v5 ιFi^[Rs}*Ȼw9TI ymTI3$u ΃e\dHجMi jGOewv>RgW6BN0KQ*cx\p.O ;h4!I& ^&M2~;h heq\d"k-QDJSj.#OR}_(=]ʨFib0hĤ{zŁ@hq \ZԎtQФ([ӤRzsezkF]4"1.:p5p6p0-x".)B{9'F8(HbMfw40fQ1Ӻ',xY&]~ v$XQ:2{F*m]\"_2}$`M$C\\ w;; n6@-P@{1~V{l$TMDekQAat3ݹ9b j V`Rz /,7;=*t,dZu;~%lX_)˷HsFVOL@+.XjL,TBӏe늑Bs_ T6f{}M- e=|h3VF4Ͱq98Td"a6_1DׇlA{,QG{}e*u$/W{[Jcv2hغ)< G“2Fp'u#|k_9\WӇG()rZX2(N @K}]D}v#Tqc\GlF"ErIgaQRXc9D:/XzȳJv"ӢӺ+8 G3˃S{n⯱B`m!  0<Bn q‰HuZtvGP_aYBU0V϶ܭSϓm{F㶑-+mC[ %dƶn'X{]+j jR,Q+W+U,EͰ@*Y @fG910b) 0H 4HwvG D_U{-q9  XkeT=Z fASڳLA3bP~|cfEKKRA3%3!/g̮闙޹Ġiߠ;;,ri5o Xヤ4I|Z/_%Gb&g`̛̮闙޹>>`ZxR>FGV* (G"  g򥣣&Yy9X;+72kE-RUG*Us,2Q8i.3h8]\}yd5GJ q{eJn N/ĕ 3Mpˆ`;S O5K1ŷSk_u#ɘ+$d<4o+f #qg%*޴-30;CvUkÃyǛkX1HMebTnPQY'+yCajA#UV',B7׎5 8D66HB.2)ZdO>9[S3oKc:QE94[ Jg4y`(vTJf&],mAX\(7P>-Α긻Α긹r3Tmەz{:UYϏD)ZHPXH ,ƭJlr8.ܙtj!5Nw!/OYR3qWUH}|QOJZ7=1H{GGB:9 ;\U$,N')N4bקiNwYZVB i5\bVӐFX!Gt AGn!V!AGi19FCnԑiM:r ivy4ѐfWHCJF$j$#S5Wy*~ 1="PhK`JDh1FPP"j 5ƄȄĄgBM0&PiaeXlIdq?cq=cq;cq9cq7gcq5Gcr3&'cr1&CWGzVr?5:!͇FhWmV,n`\_GaD^6n gܶDVHBZ4[Wrd,i.9\C$`ZQtXȌ#>BpJ}̷+7bK<,6scێcDYMh!B.x-pȸ^]_nD @6 Uy.:Jĉ8kրrDvMgtTކ+n-UEK3ֺu\ 1o 6auNUmwca0!mFƲhYW7Xwlgd"I^Гz2$ҴC -vw9 8Gp&tZ]5U2 hoy D*DًG"HGEL -8;v$28;v$-X5z' 8I%y'"Ai Z0ap*ni(-\-ڢBwuwy4Td e\Tb-nw{n ,U½'\PvmΓ, 0D$ֺ5lPIc>l? '@z7N8QE8eNci'qV1/ze*3pkvCIZ)6"nw5 >K)'RAg95s ! 3~ )@!aTK^6ÆЈJ f O'/`agI0$XA< x4Zw㚬4Qf.hňhEDEV6Η jG)`~n`(Q8lgNbdd3LC+pf@ j ya;WVjw7VM߇T_ "(r^3##b}їNm,5{Pe?m)9 op#3ңA4Iz &b2<*)m\Psi1.)ʐKܢ~b3r`zc17֒'_. KX sRMD8֊I0 @&RyZ{4gc2ZZlv{}y]R%|60G^5 Q6?8(bkd5S؀6>&. ]>RD0 dXKʃ Eh鼞k{"G m5Uuv"FL-ڢGLԞ Ga{⷇ez-YA;z+0,?iARi=čA2Z7U_B?b֪r!jIG>Vshy0V =qvQi ]j4.}3Ne<~4buV#9N"TiځC~<4[+,ٷɲ4֥& C0^cg֦*Q ,[C(l(fg֦*"f<!j܉WJ3UYb+ď-v^9c"A28{&uxĵKƇ>΄ʙkI9GvΒ"|1TO)ns/9l~'GFdLbƆضClۦ!m|mb&6!N* s[ b: q m04!؆踆ƶankhlCv\Ccㆹ:8d뽆@D6\"ph e G.#2 ^n,lwuf=mSOV1JD%J6.]g\Lᴙ^I 4liљbSkGilUZ^ϥT\J hl7{dv&t <_C_"!`X746`8!Gh<>\Pӂn+2L@l"AD:Dc}n i=B81M#HP>db_<7"WiO ˭uy +F)R̔ +$7I7yA 17؏!d(  bP"7X\Ѭו> xM$uejVǩۤڸu^EzR"YQ@Mb չVqRj$VG&*'eVd:ILJVYllj.4D7a I'b[1?Gt\,LKF"b\=!\m2lI(O9[xÕ1<%b'2Ҍ"6R}|C:gEiH;Ơ^(4x\M85x].p曚| <$( :$T!GY_^qBo+IcG$"ĺK8hVwź"Mhp0a/=:RQ*ibC͑(ZU^t$6*[ 8ۓ:$3}ɉ F` `XRZ,fc>`XZIl,k; N,&c 0tVmI0TlqH5G cƈam`ĉQF#n##n#Nb% Aq֚ |j^@"rbб(jVC($鲻:]gr& ÍXVRa6E`^BՃab^`)Z7(т(*M@UC"Ef㜀Y¸"~#~{+HiwaD+FDcBAځnw @By$~ 1r&b!H} o#!$ľُ?GhcO 2gFDi&~DCtJNp`'Xg/脰n"X󴠣kW= #z3bDegؼZ86ֲr(PGCAq?PGCAq?PGCAq? gC rf=QI<],v#Md7m^gܔC|S?0@&R"fɇ:^r}ZfQlvv1z tj2Z>Qq9&\DvYir8k$f=c0V ƒ.eoE}ݾm7)ZEDE3)~sgCYE1*D .VN<ӗʻ0"OXٻVԹ'f镽VǪhb&ae22(R_M!` i&;nÖ[0s7T- V ; ZV1,AB L!V kLpR q_#WOk_gT,⾔ In(EFR4X nwڱu%O=G?6D,~y"O=0b15X l ~M W#33؊U \#+`CK"??g\L@6&+jtpǹ"߫_8˲fЊ,:HBZA^ߚpσɳXIxIrRQ 2+HEJޤlt[UʈvqrqQ$S OV `4lB&0\i\aXl pYlw,Z/P<[Y$wS$??%QztTwO5Yɮt˼'Nav8z-7̭.p{4|4zC~#jG?h0Y%i+I7ףHQѻeImhNHPG*lhҕNx+Gɖ#do"0Ҭa{+|Vha[ꜧ$7!=:`){m:޲]:vQ>1 bQP6r22T9VrstT; n'_ I,jڗ)I; =Eb$Rչd=y;8-OqzF4vI0X)F;:lLc|"gcPJ3*yWI6V/i8qzeVyC!kM_ZlMLqY>484]̍ ]Ñ-d{Fdm4.w`.aʟ=-qpi8͚NԷi4^/ K+ݚSCT0a0kH+,dPk(RG70t 9j(Bga <'31z8%X/3Yv j$߭6V QCBr4OON~Ǥ1oz0S6uLSfٶmGyFR0;F`cQxr&7;V}^)U\o7WʣP*RnϯZCVóy/`D< O6ֿ1D Ɍ)ptx>Nga(=~F1H2cߙ K0ndg 5ݎ|""CkT " *P Aڿ BB!PD"M* v|A; g /$f>-WB'=`7a1aА|-5+ooB-(P"kܢVA;}VAhX2ʊp:8ar"Vtkج{VE{Ji6?1ȈKF)+c $!y#?{ESC]0pPMHְ1YHr -\-_K;c 8€=ȑ( s |: CK Cvc^h5*|tt :BSC|J6iݘ~ٜ_u1nqۅ ýnAK^DfqE%_ TH@)&W:?==cͮg#jfv1JKww#t ({_ӊ^$ Ko{1U04p wvAᮂq J@URޒO[}o#P ؾ@۷(`lR }[vn,ܱ@.7osiew4D45@O'YGf(M̪v'S%$jDC6_&eֿchO] YPV9I1đ "dgwz=/Y^:`XE}(rpud0s _0縷eCC+.| ǓnV Ymav4+_ܬx_oV=;7)φܜL;]o0Mɳ!7#OXcΠOS?0͖4^`0ț-iJ=`ɳE5k}QιmfwOםhJe) 4kמVWC\$'*ϊ5*)_P+ %I'$~Uh0^WxIKe2m"QҮf;.4;-NyI_Zµ|"\Q%mqۆUd#ձ5U>5|М'BY 2x5|Н/D M27$Uj~{ڍDbJKb@ThGRq[+JU{!`E+V[nX=G򮰉_!1ZQ/CQ6` fTpuirӧ5%LU0K SUq"NU0K.Sm!IKws țFj[o˗ePՒoZRпC *g ’YS\;>0AL„j]WFJZlJG` *Vv]׼ -JxQ!9 {0g2KRc|co9LZm/4>+Ζ98suC uhza!<CC}ee7f5M<.㋭JTqOsD#e*A[\ J}]ȸ,mb|eD/Cv{:;n9eg*X2j6Lo|Հv[GAO+!iʰAP֠ gBAek/Ÿ6yjlmem姳sͥ$6VR+/tӅ1_LO ]]j쬃pz6o6 ߬4|bf*bNkٿYjfg44>wv?dzn&Cj[>ٞݪ M@,VZTlտTZRW[_քj?v/ʅtN$CYT.g39=%۪gJ!F\~׾_~:yȋʍ>T/HTi>[b}/2-mk+!-}CR.]DfZ;失NoNՑ1Or8/võȫsY5BGHY\ͭH7aһ~saCXӥ͝վY`ȳzu^/`?_ѾZMsYY^}ȋ>> ~ke= ,>wFcr׫/5lwڍ=M ,‡(3pK,+5wӠ>bE$Q++`  .*(}sP"\6^\y!N}>N53\>}-lQʗ)eiFt w+Q,0َ `Ӎ&]4vmsOt>@ ە8|\ Ƿe :*΀O1Pe˙} ,-e-.阘fhyIi~yABu4ĈD9[1 T2Q@;ƕߎ]Ffͻ?"׀ūspw_7*^;㼬K$*.cZHYZ`wlLJvw|h6Qf6I֏D!';+{ͥuϟ.D=NxʺV}u=y]9h rJޱmnsu͛ם:&uݩ#[GNx~Y׸hw[]Z]noF{ݾ$4+fVf_}Kkeoz7f 7{+ŒCT+b*l}_ijB~׾_~׀~  >:mq F_|@heqs:5$#S_c-}D~ݤ]xd~jڡ%`kZ0C`>`P~gr>BCf9~XSRɍ=ґt>=>;N<}E##zf9o2g`g?fʕssW>:¥ ,[5Kǔ%4Uew~/n($o-#<+Z{\<?:qj"ܑBԁ|j$1uN NݒSYmH=! \&#F7GFύn EϢD?h(ؓcŎ+t;c})_| b+Ruiٹ?dj=O.d ¦`|yh-R~鏥K+BpN"FaAx3諢ߋ~4֋ԏRd|-s@l;y/ɽ.sGˡl(=3}|&43e^*s=駙3!Dv:+[-Qt{Idžspkd"~y=J?\|)9_B]EW&~$|Jj6S[҇g2_t=!>{6pËrwɝ)OB >XKR\ 55{b7ƮIߞe4̝3/NfdߒO^C "P7 O4"<]l.^M0K^ Xy\j=ԏS99HUa¡eMO?`GEO9zhkк5/]&\9+ZgۓO>@qGug}澖;wo|*>߅|p (/%B.M}/ԑlzG\iednr|(_fߖjqXn9v.ȕClk,S_OO++Q|EfK t5eY^z:@$, =r#@Ryrr6O^H^*Ro6ڕ1}P2<1{0l#]?"£P\-+war7~9$c&KC # K[H1b˗A>Q/O-UzV)Q:t~GKה>P)4&VbmBSxe1)vI\=,ܡ'AqT9v])t -sb'NRg~={*D(JO,}ZQ3rPHb6췳BU;ר5놮^*艱kSXrG/C'%MI/''ے䛓H^ |3R>^z?/~UؐO =ҟJzʿC?:Ϝyqf+[^K~:C0\&wfnww羜E7'Ϗ7w@QZxF!]8|? ,|]PC^+=_͙ОÍV}{;#3wK?z-] /SEo+Y]z)l>WܚX-}0 "K[H$+;"7F9$M=#Vmub;c.ITYYnг_mA:0up(.7:jt#?鯧oqZ8L1sBvCv<{Z>yٹRnCn?o[0x[>WRLvٯ )[Zܯ4R+5K+7(=XW) yw!_8]Xd1Gx[Eh^JrD B(ű@L0vsrܾaMirX B ɻuoO}(uJaca‡ 7/X_\o~v鍥O/քfnƕMRk›a$?}yt.3'v%~俒#t+3oޑ2\0Zȁw^Fe vC²ƫ@޿/<1rp )ڑ+#22]Ekѱ٠m@9,z7%_%vx37GiP&?(}JG/ޜ}V7C j\|r= qb*yu]PEWw7^LT!ˋO)pR> ™ƒ/E;:|>>8.I9}@f,weg/<Ǟ͹g/˿7 /,zԟVxryuۊ;w:G쓕.Kg@4;^OLd`&I-yB^2 P^wCK9VZZ?V*w ~%rq؇c'&NL%J>'}Tf"l>p3.RxpCO)*ו0O|s0?uC0~ğ%J^a04e?}̍H7_^;: .fYa3b$G7'^앩L?̟2Z;__j/)}RW}yX'Ӊø/M}"uwj6t4QN^k9_u,p2eҦr#BF/`{;%qcu`y#{Q.qB Rʼf?eν{`T?];Q;z;Jǁ7}:}W?|Z>vxl}@vp7/.Q^2g. WE>f#7lD4YHO^oYҟN.7UaI?9sTLzL1̫3eޖy27eNɾzr`Nr9O,TWqX++^]>w@,mRr[zq鿡_pӥCB5r=Bx0n/>W[߀="y^$s3 8h;GޚHS 3`VLτ^Q+җߒ`ҷAn}<)shfNΤeہܿFf>B0B^懙_d~ ^ GaFa٣&#Ӟ%0{M_~#{[wf=%wXn]\ 3a,xQùo=%X9yro<_:}gS { ~W8eŷo^K_.nO=!W OhH&rZE!V_RO#wGW psy C?>؆DW~QeW^{s?5x&^-W$ODa~!?L oskӇu0{)wC'xQ!Q#3ޗn*w1?9Us #L,/(yh0^Ňaz"*C*S3o` rth!:=E7FBbOohӓjO^cƓ3 3ɼ's]ù|n ^ >wlqPoX~%1̡&īa<}L™BU0"#ЯQ,*^R|k]OBS TiW.-}wH sN^³EaR5MEyF9'rYˡ7phod=̯0{_;=Q]?+1*]o# G09pIᭅw>Yf0N(F)#wasioS̉K;`~i~Ee˜d?xBY nwB~F}->Ύ) cf&߻0u鯦L?5sRh&r}_8wk .GBT2?tpsqz/_w70@0WzW#ɋw(%:&u"gdߙzXK}!7SAI0~s>4}Dt*}j Ϸ_caq{'鿥##`6slYΜٞ~*>=S~/Mr/QM4E0"G_&{~?/%“ ,hts:&~\$~i/K|/.!LRɿaH/@_1;ޒ1wO| tk |}}mvxwD0p2͑p.G߹r XκCT-22 FDرUnV F&e4nUFX=\*%c"MC}? ^ >|. ^")>υ Eg^ M|Uy*1~Z5}YczwKkSm?[毘hyga>/r/}:׹In~~]C߲bG7]r^uvp'Jt|B^z?Ǻ9w| (&9s?XS>V~W3mO;7_5>k#fִ"!>oc~#c3߆mEW,Pe!kߪsGM՟X_zBlT2} ._},IGwsx{{N?n[de4oٟbK8-7ʯ9^<:w~e#7Ot~3gG6;s翜n!XD^s鵊nuAz"H$mhYMQl -mk;ڮVZEӪp{فlNSXuvi Rj#m]n isM#zT/^Ȟ^AR׏h[~mᵾ> />ǐ5keO>{}G3ƺ0FHca6`l[F6v]d!=Jaqd ihShm870.i1qĘ3cnܒXˆQ3fu3a&͔63EoZ=[º,1 iA)pm{Ӧem{*qY{gv>cZi`S v}N xh`+8ƞ3{.Y~X>_'$ XyMP>Ent'/:kCVQFjr'Y ;wasXjsY._#Nԉ')v:t9g.٨s:tStͺk6W\p7"%EdkVwcoH;p/Kwܱ{^7.s*]/ż%^ۀUhՆW ަn{;ޮWўW wս#kxMxtMо=xޥ7FޘnXSoͽ[λ^ď1? ?B7۾GOl[~w2W}~N;~ocT,s #_Aj7ğ3wA,A"H d (؁z[A vrP j0GqЀZ)tnУË8 4upL)[qwڏ00t 7lFh ɮ,;nX a% ~xhzx{,HI]*1$Q69"sGi++}ekG՘WՄTSjZͨjf2-^C6ֵ:Qg- bg֢0ؼ g朖[ֆe>k9NӇ5rimhm8r?WrZ,];W)9#:!b| '2F-.^^~tq > &v2Mgs@ fF,]ǩ£KwHť 8Oxmwn\zU8Ix=-#%3!)ce^?NkIݞZP7qZEXmMDI~j\[z쑂=W0Sm.OvEÖ2pۖ$>S%)qɨϧSKϧ|ڴmlV&xzy)86[3b-24TUܷppAoY\zٝln C=u \sQs/NJ$XH I݄GXxBrzh]aLI(W7i7"Xe;5[DGߠ=$Թr!1.b܅M=>܀MV<풥4ѷfl #uf"k,S4&dbMlG205c|)>2Հ Ժ`=RE_dsff Ԡ>$vZ/?5Aɚ)'kN"gJ!!]B"h%QDTɑ92D{7I'g!Y 'z Tu|TW=ס lX E4?[8z4k5J^S4EN(W'x+e"JЕGWJz¦jTٴbj4a9fiޣZ t+KZvxpX +jUt;eWW&,ApўN6^ k]vVTi댾ɖY\fZ*O/H=:7AǸ ,}igR>3wAkvZ˲ˢR :6I66p@#t#Uj)ʏ;R?S >>B|ߪE{,D)dέ]zG$CQI%}MmWCkwZTC'?S^6omߞomJSvnX1Zd}?U(P␉WrUۄ EPސ^){(²@kEHmM8\©v":^ _ n^5ڂ Z(iUaj#H,ټKUgSӨWRäfaMfs %-$Ods ;'y%Ѱ~-ٜfSl`c7/e6߲r5xl2kŮnӯC"d:W,AiiGh)wVNLE&JH_dOn{ғgr(6Yzǒȅ34 Ouk_lŽmaٰB=$mJ^:\:RqM~*r֟m2!i+t eH;xMpإs#!9y e𜽙ď{ڏ1&6+ݸmD&/-O?2pCw:~ğ3俞JP)I>mfx(y,уֹ52}J'P?7+MC^;n ij<_K;| nVj|/ӑ!Ѫ0ʭ"Aj> Gpzsh,7@Uer|$n<7[덬ճzVY=gճzVY=gճzVY=gճzV{PKж5yB Microsoft.VC80.CRT/msvcr80.dllZ}pTUt:t HH 6N# ĦE>{Nt&-ϞuwZVgdj]Z]bcCJPc4ˌ B# o$Dfj-w;s=sYгI3~&mS1¯>fX5m{v|~E7رS,zdkQhێeV=s٣S4:*ԡI6RgM|i||şg dᖏSw|SmDo8^ 2dw{s ,]&>7w:~n CSx=O*%v?v[>*,$|V8$Sȿt뮴޺G_F/nm3+SghAVo$nu_O=47UAwV=eݘioݾ_+pp߃["6FHi#m6FH5uY #Z ٻS*;>v^C:k/1^ SӴ@4q`F Z#f^Έ;DfA 4/=l}f [v֐e},9Qb8WŚS(% ur^*;Dd!Mi_+HИ͛+ԨvjUU>)XՋjb0XeլLvu&T(DӢ8O{JLje7-EoE`mʦFMJB1!HaX$fϒܾT i`o+2ˋDsyY{ 7ZOgfXC8O/q+7-Z'^#G R!)•Yg;#Ӑ@c+9uzBx %"..[ࣴ,"A3H?hy 7Z=P15ݛ ZJ[2=IoH7&6&NV`=5mr5q+clwwy;aM}q>@_gRҍ(S"qlvC:z@dU0j7puNxf M!9=Q[x)y~K[uw0 YRPu򦛂N.jcuVAxsD,iiOvMnR.G ~1;5ϖ&i}ۏ1ðN r=8oS71WnL h.6mOFlz,t0c?'rdʞϦwl#GffXlVi:6b>NF{k\9UJhZD.~-ځ0熠أfM]7 wmq;o[4 趸'o;Vݷ<[4m0CnAS\:rAJH‰A HaP3gD%>{ܑVƱq7[$+k"@o4GiL:{zR+<_%,kIq4ӫ0-(P\Yc8KڌW[_HQ @bé?+!H0EP }M #iMX$ ;ix-i՛4a 2=a @ycjcƒ1ʟ`?ߏ7P>i@_7Gؓ_9hSӉ"+~ʿI1sM.&db_`%9a[ &N_[-e-y|=I~ϭNKYlnfϏs]TpG@03 :FvRct>-'O+_L!o_Og٨Ič`p5laާh oCG(QdρL S/;DOVd ^9&fҫ3$ ToM6\naO7(bT i|-ױ |F"vPaZJh}C΃8? H` ʹ8{]i>nD4H'>{vVpaO߫4vo U%Wñ|s Th2'cX,6 k3FfmY޳cW}!@vNM}/?\!r&S]ݕ9 o%e7 Bƫ؞h1I9@B]LZ f܋5| hCZ dqFbV(^Y >@n V:xJ3ZiU<}毴q3SB_ݫ=zΪ֜3ЇyM< }6. ƐoF;!^{) f/Y8.YcôlMaywQ},*|'(OǯSdh:{(ϧjabwD܅әcO^A)%PUЄo\6UPai":JXMJ vL%av,J."\>w1n `u޲U@V0M0`yd?2kBLY()I$$Q+(чK<D_VaKol}@{V.rA 7ѰJޔv-;zAwWJfXbCmT.X0~Z-U1ۯޘr& ,/%Y)`߿}b⟶8jk(4GC`=AںJc Ȇ)CrU^ș-*ՎtLMKv-O`#kīBX@w}l4+~94,e}o|(n7{F(G]._XL^$H㽬Xkh,"rBd=3bQUklճzaIRAP7Sd\5#v`_k&dj~CeKcAn%HtHh%Klf1/hZ6shdje3Ske=ʒp5/n\D,T0o/GJ_LB+.Aǥ$Þ:xM%2UҖ eE.;S覛~F!4RIO-!ow]!ؤ {PX4ȼ/*OȼlNH TP(?i>>TiZT&daRXxټiz55{^0N818@:#ʾZ Ǟ{}c'4$ ~#v6WʠpOxl#y49|gc-=i$RǶoQz:B~o/m`>0Tم[aW?^0+CTДR}TC1V=,[#W|?C^0A?S4T-`'p*'Y*R<}f=_ ]妾Lە9 A#w̥rlWwq3/w>=sQKx{lCMҚ Ū6+>3x~e^ Ľ |8$dH gPр dC1DHq] ` QbK[kاT@M@ FA6ꍃB{ΝlO6s=s=_{g>SgҘ t6KC`! M9”Y~Z %8{Ff>/3mC͇x +93Mk}-&#MDztLIgߎ3Vy ȕ rS`Ku K5|Lxcex=qpez\˻Q؊C"GM8V0i$Zm)m@pϡ H 9i)ūз߁<Vщ$<xK-GkIg SܴrI>51.ԯu3..0Be@~PLvgWzz_c J&C|0̯e>+4~VZq)R)l΍efJ7S3fLiJTZV`F(zcĬ <PndsT $626ـ@CkԭtwX;s*m2L!\ZWi$|1g'žF}ia6+/e8$R]4nl<-+08MHdtF`NՌcs+ZXǂ,L8Yb;K(XFaW;夑JVIJvsw[1;ړ#Z).BAyƇn 4lNwG0eiI4f҇ϳjq=9׃0ޔY*qNpds^9?ːs9tEҹj9nbứg ΦgV$hFhD+P-84>IK7~(Zs[ihVW,x_&cu[#gxgwU7 2Lu[9ߔϪIx%8u/ųr5YV 9[rlO:jORKEnk Mx =pjWnHb-jM8& ~CPv -6 aBc;J@H"- WpK>t$Zq|] {Z=1HnOYf+.€uG1bPH#`b eXУ@𼒵ZtO5oqU+w:nZIDN"mPUbB; ]m3p 5 W/b W O@`h 3\!.:9_<9W l~ps)eߌ]v'1H~v$2Z1 @Uh>/3Ŧ[iS uPG5˫*Ab,1GT3·gU1U;1*^m; m'fy U**TSU*Ӛ,Wf:*eV+#x&e6)$T̀ET)?ϐj!dG3;Y.>:陉bILSl4ŗHfP2Mʶ|&iI~::c@Szzⷷ[$Nkr.?oĴ:dbIjh-aZ"ry%g-1.@LT'bћQ}4"&S)bmqp6W`mA[ߵ] ڂu6. A9q 26˝x\i~k'69E|@+,{5fj^uE#I3ZaLğmh^Pn8Y;AZ7: Lݔ ̅Y,W0SlNiڋ01Ig^Ӎ %Umk%Xwl%2\ʡSR^憦Ɩu N?&a|] %Ējn>.9-%_cI^ bɿOҖ 8 i#^-4 ֥4VW귓 v*Fɬ#aV=HU?vfտ*`ziLNCC+R2hz" 1yA'QJxK~$?.C&q?D cuWc\}a91ϱA  ĵK#ą%6oFҍO㶄I?0^]L/N/+x$^@rajGQ]Vrnzg(}I+"B~@Ei[]8=+0e-e4034Uj7mꔪV:lKZ͚Ƣyi>n j  ѝdÿHR=E3< A"C2㘏fA)Xk;Ј+VٹO20'x4VUvmŲgbe-VY'z$Ogw`X_?SX]V>ߑKbZxbGY-8췋R{v{b?˫򛗶UcSH-;Mx5Z6>4؉YNy6LV"ip158}l{xZf3ȍ4-ָ ؞9r&SdYK m0Ab!}"gIG}Cz[xݽ PZ0d]Xr;Rf?@.Q/&]?~OL1|G8[1܇ElyyUd,Z7od= mE3/@iGڳLGoMA()81Y\i\IJfH鎖%c26{.k*;,+?Bee/S. rwpYh ˌ, ꋅu NVP:6}acEy]lyW ?{9M9n|24, }+S,QH4 C}]? 1Po^pS?=G:?7s"=ѤCyȮV!JCJV 1@6tq &\XLɸLnwq,v%N&7=zNRqYX^Uap!9gTcNeSt|?ɗ@Nn" ˍiXzƃkMl_Tژqشܟ ȷzPF`@ b(f.jdR7_jXj3XxRJW'gIK|UJ7AL@f>Muyz*[wPvp!~N|9 qtʻ~^z@J}t&ڡ]6!pW@1l0] -N n$Svϐ; 3s+;rI-a+r^Ba&| Ӆ/,NMOv_325]ŠpFpg[Y;^[-(莐Jm@X[T6;vGxSxrr3JC7{i'gӊ7Y8 | 2krđVǧ1ѯWB%~ݘ g=Eͷٞz;O˥1et>sAl+<7`&N?bp_rikm2cAK``Թ$Df<Ώ71m3-f\}KRa.HԖ8m˕6UvB2(9zVQ/a8]}8 mߎM8dkpgBqƱYMZ(> )T,etO]~$6Ud.t~:YBxqB#FH49XBtVOJ1V@+L 3Fct4x_pmt*}4s@mh Bj1j vuS 6tRu 1DXASl@h學4&vQ&qV*2/Z^^i0#ykp jd+/>o s3D} XflPg"&/n/L/1ՇEq^:̅q;qql1M6 we0,.ξ>Yaǣ$:֬'YG~t8#IB߹~xwobЈG >0.6C<6N6X k+.Pې;ocMSF.@fU.GK'g96[ @*U|G @6_g ܳNޜ%W"OO_cٽݨ< $ƁfW $jy=0 zb "^MKa!1~~:_u?trπ@sw H:"$}Ő [V,?.Pg[KP֎P DfKs@ܺܕ<ظ8D}(;>RZWi$LJex9G^Z٪0 ߭%%rФdAlC+1suZ@nXiAFAExbh9藆!:mbKo˼xQvl7@ym+&}o{ f ܶt,߁ ?G.s6&פ)͙b˚- !b_?!Pі}8םy- ̃P@XSjеBZޛ-NmkR1xK? HlD{09D2s:)iIpa6Z©sxm><NYD;x,^pfE#m&0gߺǓ%.I)0…P䝼 F|.I3( wLPK]` Cio]>]hGnUVK#RB+/YΉyP9gɶ1cm (ܮ43m<@S#y!ްF1Jx7ȄQ%-wbEQT.ؾҥV~ ^l^ dH$H*vl} o&JhkySae.A! 1w\Q覨ʫ QmxJUntDӻ{:O HJ5y]]ՖGaZEJlT fQZŬ#ކʤ?Lq'1L1ee^z]91;,=9O}/oi5 k%)L02#WH97#HV&u&>'z]C-ʎKQ(Y0NÌm[.K-fKᙦ9f`~A^e$ 4 [MQuê~r(Mx~tC=AT5l|guNG< Z_<`VWvVh/NZ N?a;c\ya.'د a)3!+eCIom8w ~0 oTEZZ"\]e6 r%lُE/C9£rѽ̨=k?:PF*}e󹣤;Ucpp~/rFAQU+ ژt<=KAƈ-V"R"ũq_3/Kd~k%&%*7p2G%`%4PGۅ0^Vb7/v؃dGh#ۣEx7pBՋA >o!}1tVF: ևz)}Hk=%ԣ'ZU 7{q\*50=h鿍.cCc0VYxy5߃q+dw_@b[v_}J0'gUjs4|SS8řZqȩֹb>OAn[}O:<_uybh`nhm=Ƌv|RbdM Z(5$a2*rusl!{Vpd^`'>Y r M2T DUJ: RsHR2̭ T{]9Hٶg"Zf^`biLu `Rz-gd'MV6+ߚQs.Rn|随 6RfvC?~V~anŒu9Vux:pv^e.6hW-U[" ׏HZnM-w$M˖QG=ji&e`C7#_*COOKy=9J37]})8<2;5S@Dy#λfw奺S>oc'v遖7Z-efhE 0nC4\5 pu,pvRE }cHK9o-e-) 5[&">oװsPRD1<3q[83t/uvn-7&!Pr80/|9sWCN<}Ux Bs|^^.$j$ooj|:iYS 9|>Fw//@ y(T/uÓqCtHЈۺ|^ 2qgvR}PYqQ{/naߤu0s{ѕ=w\jYq:@nm%Z䨼6=ldIxah>-5ݷqF/vdc ^ˈbR!x"qܦ8Hj(V\ib%C^{ v|\#k?a#02:almm0!)JdFȚi 0?V͍pj+iv7YUj) .NnBN=|%Tzv2pA(%mij1Y$g*% ;DpXV{qZi.ZNPy3ﰏdzKiA㉅ܜI^ &ԣߵv׬u)[&}°2QX#޺APXVRBE2"U']r$juG%n4ju 7XgCm%rū>0(-eq t( cY|B2P++6Nx-T /gcNֆtv|F/7c<5.%T^` Q M%=xgQw>c\K)72^V׹?\@'mvf7 -,jై6gڛY$Gv>h896yEx;tXм>cgdeel.4gԘklnnln c:G*k!_?2ӶcQ(vz[*(Ww;t h ݼ| *|s2(f#"=Bu,,^b2smޒ0a-IRf`@iڒŽm쁽^^mTW0r֓z1s;s 3}9>3r &hoyhHXJp޸8':B Ժ?۴1bWVK㔍l(>ɡށJ(ɿ2냠iL7lNہyAjC3yOl aA!gkX Fyb^߯sI=="ՌfzeTZT/tq? Ƹ$@ 1i/o b,"Z^ Bb]=lHNFbZZ ^~f|o-@Q3f}bHd&%{gG_[4j' #^ދG[]rl2^]@j 6w|&!~<P#KSʡH[w#2ei~o/MM77t_m%ldz{ /grf%b=WB;nd,& &3o01$?_J6J3/GSG<"ӝ>AJvy@+f< l>z?ЮB9]1iFMj34/_7CIGk.7ad2W%t˙7J3ў&\Ӳ#OB%ݸ,  '!8iWyo(jJ  Qx%(kd>m6MvjGb<-ˎ],Pm7p\[#(Uy)h^[El?Vd|ۋ3҆jim3d1RJVuf:G/@@[Y`WUD05 pfhNVڝ-=2|~-7#$Jc"! 1<Vafh<ɟ,hKCw@ ;s0NYƬ0᝾:4op%= zpZs AEױ!Fc9?By)F+#,Aٺ'Y0fU=2&>pMDQps 2\[X2^鯮)Ը\NUZPa0g8uL2w魱ԯšt$2ʬaؓf՞96ڑ`N</Wøءj>V5A<$h؟[ݕ?D_5 WF;6M}n+{!bCU|U!ۡzp?Uy=jdEV!v!c8'[Lu1 8]H ~cҀ>T㤇 FvcYakQJ"G `ݸk33Z ƥW孯!NاI*f ;p9J쯨~~N?TonmM"m6ml_4-OyVB3oVҗfnىHHJ;WSQ sr)B9?sW½ /'T*WbOk~܉C$#Eg;tx qhd݅ h99ԡj v-oŻCt"tF%gmSvi~':2)Ž֌Ц#H <_6?Aփna1͆`ūr],wS`D-h7y{Vvg T.Ft]?!)X?ÓaہĿlR˓n)tܩ8fc-vz!d qOQԇ/Yv8}2=d 4 a?=:qF}aFN{1 _S&˲pF:R*|/MGm.9#YvA]+t~\N^` @9Ubd!}qL_x8ye++{Xok[o"/v~NhusCh|BIe H~OAh{ Ҕ1 ku_Ư(W!GcZ `EEͲ>zef 떑#Jv]Q|aw/4^IA DNG2]PҠ̽>@޶Z?/K01yZ|(A#ȼhgq;1@ּvi n+4M*Qh4DhBH<+ՐbM& ԇ嘯2ጤ O)Xxk">6PSc~Z0w}\aƒ?bs.]9kt)FH2n _"CMf/a@M ydK7tB)*Z+Z" s#l,. "c08-')U4ɚW".,L^Fޑ!w0Rًہ?.6h'0nu{;T3<7wI=^ '_FnzTemZz}-7E)Jg4 ܥ'jG(UԢa=m%Ce~eQYƘA{v++a*xV7s0^Lyv >%-Y+~YA"x_pq9ۃQziI;2U adr|.1]tc)n;4"O >:\8O>?@`7  bQn mq-9]@;,5o8p|y“5ǕPCBCT_ !K>~mi,|?7P t2Sb+Ē/< >fAգ|"0b8X)PSj/wWL`?"cq/=ܽ٨X+W˻yȃ">Z8y;shI؜!I̛`,vM)5e .v2> zPvaØgepº|taAZ7fM䙆n. j NYr.F1~=C]>u X_\f#6{"@(.m-jO`jG;q' xe(۲wv⍰g!g/p j/goI˻ک}w"<re.2̝ yπ,pkHu5gOj6ʻ weT>2M_#a7@ `>oOrTXp  %-ىz2kwfXj,&ex<9ymtߟ̃odjnL.3%GY8yiqQvLoIH"ȎC*5ZR} yZnXo]9(v7 prZAH[^H`a 4~&NG=c϶UkmQ &LpW$61etw`ib:+Pm|Fɭh$ūJ*ocUV4ኇ M+؝-G+^Eg}&@6aS\=-YMeo͝KΓ;ryC?KW)瓤qTA<ɘ砏T>;IjjYضie_ $%ބZ-ЄjA,m:ʞ}E2|Ϟ;Ny3\ĸvC/ ocx{D% 6 `g(0d}xOzϟ^PSbOAX/fЈF{7~-:(E[O UO {hA6TJM! [ &iÕ ipLYm"Lnj ~Me܌lO Z^7wz DjV ghy(yz |GO 2RG4Q6nV+Ce)'ȗ)7ϭ!)Zvy$TXP}r Ի2٣@Y%Z[x\J,a5=3X8u+vDžq:vS:}0*9}3$]~+}^<DŽ{H#}V~p"7=j)okq𙕭f|Pa|,E0OQKhEy@lV*K𻞙ح#؁+~ۋr(̈ӧ3!/1wg擕6z%V@9[:9m;d;@q dAXvQnZSJH>MSh&(5*;1XGNN KfvP}>P\}`xŰO6Dw'YdCR/x /fz#1d]JVKL֖$QZGCWMh\V)R?P^X_gNڐ_[͡d~L'l(~ihzK> q )IBt?= \}fDg ;=iރa^3s|`?9ݚB#򪰻#SX@=Q+7 3bxx!j10^@j7^JEXonYr8C!7WL̢;b̄?]w?ztCcw8tKW cdAZr>- *'}e pSsM$~?vEt+lLg7'T kjovRZOhE8~\P11:9 "Gr1n>pAX%4s}ɘgiON"Ѱ{v0L aJmMFW0ΚKn1#ω.f2<Ѱ@}l8#-UǰFͫDN\(l@J8|On'Q5|A :<8 ,077W7Ɉ q'ʣtNCꪩ`;f .> ӻf𵶺nCB!4C5iS88|&U;aC~w|_@4sqKܘC$1A3rBPJkbÜ׎6M[_㬗~{$[ b+:'%574@M)<[j%55 0;TМ%-7cɹ`$@> $ Yfy^m͖&njZ-C㸔U!eΆ~ uo2UFc%&h0j;71&_mSZ;-IDmE'mW6^4d Zv{ 0 sL8\ՕKW0kKʎ(@OBNۥc?)}u[ld',`0 NY_H_wۤƼn E~6`c!Z8h)tG WibNvşR?}y<1fۣ_IFҧ.1clv 'IneM,:on]uy3h_WNO^7=y`\}Fd V !I7g9BU6o"),JǻHߡAV2 ^D^CNiO^e-2aOU"E)x3BoNqs~elM)N_p56MMS|+(R@Ԟg 8tu}6]rg/{_| jNdJZwZ9dvpExLgf][q L˝3z| =ykl+g`fpe2 <`dDgZlr\:ҵῈ@IcP/ax_~Z˰.^c">ksA\*Ot[ ӉWwn3涙KsBӲ`dv9ǫlkRp5{0f MK7!?gh\9ƍUH9 H|4 5ˉrǾVFtxiFOo^[Ϭ >sS@2MM+ʥLg//w nyb4nϰMGr4_YS=cz3ޞ|ȹ6 AI[Kkh: >{LTuTor'P_CCU調 >4N'r$4>*Q 0*Tl6O,␪7xtG.C_S1:t/ W(03Fhf9L?nil`$)yzY`O#YsqW DݞbXwefDR fVpjyܫ073Df=7sv.@? Rojd䰏<1YŝLfu8wX cG|M1֐v_Z#n(~$#g@S{ǀ/@zes 9O#62++SLDb[ $SdcىHEw2'S0X#nFPdrbox܆X+\ a[JHGɴ@l_,lGvuJstxU(SUU~KPH =sqxB -n'yUʸ[{z'Asj{}LPY3셩$!-ێ澷EE?jPh? #vֺ 9rQ_:a m{ Q1>@3P$4cs%16751Ⱥ+X2mn*b`wf6SٖΥlKԽtletO+>ȸLUƦĜe ^` yceHNv9Iy"x.N|5['T ,ˆاIbi9z' ps_2c{enU/[gU?ֽKpzU ګ)'U#u^Gwd  %h$<"IȄ N !j@ZHks0j&$a*[ljK?m/Vy# ߗُ_k_k&]k{mkYYŝsd ?_[OJ߻VT֪ܐ=ZW쐵j5r7kՀb)ɍE1tuL|IXl'k Kʾ}O=Y#䶟h'dwe??Qtnz2a?=4VJ"Jd-'A>kUjbkaPPnܦQ=ǟG t87H.-:pX]NXs}}m) gٷ);~|'{"ȭv~L))0jͧ`ݯ%R46e?6$)6)"m!G6DV;,ٿ ~qo `z/? רҫ &]һopu*8.Y<8|9Xe.I"{I̍tOS%zu3{u?6 J9X%CmU(󔠡{A{4mb45DHw`>ca{C45@XlL?1( 9Xeul^am̯ˍ%jJӄ*wӋ>FȞ24WTHtO(j5;e$Ȟks]Eo{gbfݮnH7*oVuFJzΡ Y;E'%~*Ȧi=v2qcFKl`vI 3h: 3RqD >jfMJX)WZ1]\iџ=6 L@q2inpUFPϊt;X#qKp͢<-8#2c A1Qm⭷A dB${u5pLW2*_jiŌkA6ps7_е~|7SPB6!E7M"s*Mp,q"Y*9F::*JM%W$I>:h] ^rs,yQ!؊a>Y!ōI> Q~<_Fv07 Y\,Fj*ᾌBY:=7Dİ.N=q鸠 nrER057٬3qz,`6~20?0v.Nb##Q=tDor]̌ 5ȵ~@м.oǥUK-_CG_b_U AH/*/⭜>=Ku8f(r&T?<&o"?C}_[dٍn'c^2rDfڳ`\RJ9%g%`5}ӭ?iC4$;ܚ&zK]6I:sFo2 $~2e[-Z _(#^>IM]SM E+zx9%5. *m0BtZA#-nn.AZl0i}Nk!7J7Eg}yqMAgiK=BΒ% .3S_Pn=ls`abаwKy qR(/ \G9_P 1B$U8Cd>qreRFEu^zF-#rI":%HcW˥aP \;:! vcDn߇7M=[H rÎ|Cڍhp"09P?woocă`lE_#OĽ}MC_3׮'Nҷ0>׉]P\jE&l4u$:SN ӫ%S=3+3QǂJ{~ȣ%0L%ҎfbfPp\tgo.M,28I&|,2:%_vL}x(7'QΉ8vRDw;7ަNuzo8'8>%X;-!J3͊W)3ǻNcΌ8%;qNsz#1-8~sDvwzODڜޢ{]qXGw*+eG>Jq_ ]qt)nq%{ӻ2nQwzo8JʎNon+qӛq*w~M%0Z#{j7?X.8F>^war}Q}@m^De]⾍DO&z-xi(;;9,S֛q?7G$zM1LzF=&&}W; [1s6i:w1i1vG{D߫&?{ȝAߕ6HwO,Md}א,D,rI4d?Oy|ADV $'ئeH~WiO_CqFKj~k cA?+@{#N@ S.QfvhRQvυ?iX'@w1 WK'0WL&+IN2ک l1ZfN| 3synI-+mNsAYڼͩ5a7"|K xPQT5{sxkg6ZL2|4Ə0 gqi9=E Q{u@pgd? vܹp|8&>3eJ#p4Z+K Ś5ƺ5]:.`jPx^E+w^<62 D.wq$ Z8Gbb.O{?4>xy8.҇":( :ZZ~FGM#aLքJj(# ي˥GŚ0Gq!K5!WqPvI&+.mK5lɰ-TjQ:TJrVS3;Tm[E(lF)+%mzɾ19Tbߒ)W=0HE/ 3& 3F14zr*F K'slU6u/#gٓKl{.UD# Lc,ar ?筱xJz.; Td#xՆ!/Kw2+ f Ix5SO>(u+I^uhI"O-wnr ӤE2y{P+띌R'C@buӏӌ xJHEFO<9g@HG'ӡ}Z~vFnlfMOe^ Iwt5RomwFw Z I\`ojK1ܫ8Y8E{~p7@[)XJ3ZdF1 i`O#Biw\ aT,;l@c0^p >Y k6ܹ 86 k:ݍc^[E>i}w:!Xi /:Ųnk(SAAnfj4l8ɭBo >AkMnz\EǛ4XȀ~yw€7hgTt\uIjB)0bMm!M%)ڏ 8Lh@.I)rW׹N>MvshW-  EAآOa@)bU~a\ibVg絡bǶh[7.!(_1Dv%o~_0J~}|4<}4Ld9*ިacQ@r ̳uǚ&`ly"Bnҷ>"@04OlrN6 RD0yĕg k S[Pix]bsDŁ5wDxE54Y yM rLW&Q -Z9MqEcml B9M5S[PB]~/2o57aSpomNSc⺋5 u5օ]&r kbJ{eŏ)oD^V~P55$%!pm(JMu%y"_i4iBmXdl&|an\)iۍS%=jF )~I f<~N-lx[\F A#7kZԥR#MUqQSD'zu95 Y^l)PS )FoHk8jw$֦)2BN%;UxVg3v?pErz&=-Z&%jŠ+4”CC.?UI5/,(yklH"cxikU4ӫ A8nzRhe"O*uo(O "TN?pWU86gqXZxK^F^r^ך5\6_ 7M V 5މBXC;} }T-OLրjl,# ^p8gc"4v=jM:ְ'3 wB"(E2G L ?CΠEiD rUj | zYok L!䦲fw\ F5bDa]K7%jX qymk QV\d 'C#|%fKLp&*Ohjm@B1E4{py(U(4ʷ:տs '$ߑsoe6-_s]nh=t) ]cv@IA97ډ\X\aO}u|bOD,bXJ]sNzBcI.M֋[/o2-Z)/<5Mk@e ɭb;q}9Pf-jO+ M|ʠ@7s1Ue4t^SO9ڊEm %L_8*xr?TOPIٔ}GnOy,:3wг= CX0\j2c0;r@LwtdLD+1} t"Ä^ 2b$4Zl+EU=iH?loPhxPҢص"kKr[E0Q.)X v [fmY,9S`hH]YJ3j.rO n]w04S!,_R9[S'#@$$ Fv\DrFE7=Hn lBgzh$rWζ[bqWn@N{y.rF6qwǩch II/4FGNkPi( Wy6I2E܇P$go2Ces חp:J_Y7>ȩ=>O1\NX+ @Gܠao^. Mc3,`@y9' e4h 8ggN͸c$ x<ۂ׾2N2WNH@xIz%'"7DZՍ3jE{7iZb1kaɎ< yki$ [=oMÝ<Y<lDIj =솴@>Eqoojǐ荿 a,L4 3+\L4/ Mnr ARZ{gn|U4gƧ3cۮUbUܡFF*v8tCpFy,k+ 99ud-3 l1HFYKuK`r@.R݇UW"Or/nF֮ηذc7aCխ $M܄A>yGӴpra VJjKB0֛N؍=_Dʍ%(?h Sd1]7Bt="7 `>E6.r]o'Vc]dE [ 9d\HFVf.0P0rS\d K%nV&eb7ě~Twx&?j,YUb<{͕Mؘ;Q:rxkA.{ёIߞ*C!e7( %KM2&ʠ K 2MK\Y" (yV|m R"I{=η<^ul 3=ҾST׵W(d:#+BN:l;wy<0[׍T1om Lj3[N H+T4" Y~4to&[x.䉞#;93zݠ1H0n?Q8}gC/MV]x!&(%{+'R CO1 }ИG07( "L X$L\el1W bxFv,x,fo mnF{͌ϾZ9KH9(Dς[5x_]R2Ʃd!9l{YDk5} z<__/W5|}BVq<~~N\a#ȝcr87JR̓עJ'?@ZfhxYL`oIkeP@# w.\7B/k0ml62Z)(9ʎh~6]^Ql걟ML@ &P3 q|7$x5&!4pb^k*-*2Uqw'W{.$XzH<~jh7}WY#젽I-s w lPsM@֡8" @PcgņaF,ȼ,tV4+jB=Ioﴁ'7S?70ޣ2?X~N`/މ]na# '+񿩧Adh? haz|6 4ߌZ(Xj[!4xrv`ϳZ=tzAEWmw}Gw(͈ex%7 bc`<`o m x![-G +h-]Ċ@ < cHh뼗3+)KA纅3X-"aWh#&8^ _ϓ͌}r V2j./ݤ0.%]K|S~0[!\Pff0:;ې; %OG+? CIG65priIM'J)'b2Ɨh~^Eզ;Frǃ~à fR7 mޜ_*bI^xa`cfєu@LPey[ttkmi, F=|հ6[@gٻO!w@pgdF#V Mx;KN٣cGw:4q8Be֧YǡLhk Ol^珋>t :iZzrv, .fKne6dgSrt*c:lw;ɇGA@k0€KӔom H&|\s-du^ Hl/9߰WS.4 & )@y[GY]C=Ea3 ,$pi\X@];m *oC#̀dt%5ߔ̼7F|el&;-]˾$扭2r-(h@V>vRV;;H @ YU5[Y7;Kai ՉGcPֹuXEYgTu2Q!K@_T7dVA֯RȲ#ȚYu1 mFGEXnaĒ(baf͖dxVl%Eտ"*VGG[5A`C'(pNW\+N `*c:j >pPJ$1yt âN]KEvtuPFWt`tuK,_EWCWE>WUtuTEW^]!@^] e"N&Ym,I#i6EXdG? x/W#1Ȫj)^P\Q1j੝[\uF@Q۞RB/\᭏BN2Āv)\펂Y2j]ŀCCUW2jdp\WWm1-\}: Wm^C/\ \=:\@p5k\\1\Y]Ea+gL4TP*F_MILwRA*q*ʘ1տkx2TO%f=FI<6977nٓF1901;3rĥ&ai& ͡'rh4.ZSPB\*ˈ+OJ?.|L=m;WwzLбZe "MxG:"_N琿y d,TrI5E1Qe(z\X*6Z.9eQ8+6\I{*BE4 W,b_:Rz+36Ы*DG6;3}*X+0plӉ= n*T-wQpg҃4`H~ 5-B\WD%0U eT܍l 26Dt|"Gjñ&~EgK53Aˀ+#wL/G/a&:\L 6;Ut. {l`ކV`NX@-/#߆Йȷ6t 7>&&|uca5^#uumZW9=yF,);ӏzZW ugBm 4y0~DzNjC hgѭ N04j L՚й\dpWo88j& ҆& 퀏4gx: !4H3&lKhM)ؼU*R"U(G,A9"SƠDE߫8SƧu`|)QmKurdEHu4 BV^OE ~t[/il(_AnE}%_BNFQ-1VPӏ hnU5P Dͭ E)E3zgd2OlOڮTNpU aGA)䢸v#8++ꖤ{Ay\g#|jß(DڎҀ \Qf'G#nlhy?e<B/$# ##5t4P`W YnM2Az|M|{:<%',_dU(l,ܬԨ"T) s4IPl(ÔQynW`_fn_L}jCwHWŪ/ź-Ii %rzbF(&>r,9% ABVD+<41A ?BIδ'pp)s@$ᛍF,KC rƘmMǘcP͢F7,2>SpaZm yIPϵYC@U`>$fj"v}1buLf: $G5HIobZę#'A&PN[^٭(f;dw5TԖƐU mjDċ62 kVKd bˬZ QnC֩jN͙W[HȓAPvd0UE$*:=y wX?.d٫PwR<"/YtR.b[_ Aӊ-v<V2mMU+ة elڲe\83 <0beq})nl͒E/b9Vȟf ^BBA4Tv55I1R}5 Lls ZȚ.cPI7jpa4BdsOS缙814I13YMT.r 5[530hkGK/q-C}ܫRkmLw3G~6٪Gk5^^MKk2i9k[C3T}4:x_*>c0[|ӤCfQ\}tA]kw,(ǣ-s3㛥am揣Ux^x>"aU⎓h7Pf0"G[9Ԗ|!ebyyNOE&c0ھX#sǯJ 28&$.p/AxܵuBf;ezzFZzUY{mcX+/B9R;@mD+*` [bk]r$FIІtwD>ꨭWWڏG61ފDl&RоD»B/)&QbOQ ]RFE屺KآKlnݥغK1DK6(0֍y Qx /gʑ?CQ+wmݣ9%4xE5zm T9^|_7_W>OA,nIS+'LhjYΎǃQrZw(#g#KU P7wR cq1ȁ 8Amy\W,JN&ءBJ&I oT_Еzr Nƿ@u t:E{g fB^2CU$H2Z\f1_/NW:gV;þ§KgE`4$BHj&ٛ"wA?ѧ O<]et70}Uc*ne1ƆHpL"J2-aGt+x!$m;!TWp@r]\^gBU 3DK\pn,חPpi\esz`PꗍP۲q1h3"!>B8JPuZdCT}2Z'Z*#Ac c)]ee}0*^PrʱUF]eĻY %C vE:J]͸04c{mKfp+SM~ F,0.(Wh~Q%!ƶwah],0VT٩.0S1HP9Ȯ.0HNIg/bf}Ŋ}ɴ_^pҲ\ AA /12R!#tF=WVM/*NgUAPx~Qz Y>t :6H"2Q!}!lI!rtڟ(9v:Zz("gI y,.3bA CvQENM9׌Q*Yt:1$juTSCsb(9x /f: /PQqQoj[DߩB::e0_4~D$CAH6!tTKTxXxk?'_lbfYɀdWź!I#f1Gv\䃶YZ+f~nDyc7 6Ȧ"AD\W!Yjo/L}Rnf?D0K.79x~X F_6,F_70(^| 6kޏRޏ4*~nEw\Ys@d@TҀ#ZË=yv\ׁ&ipV[._P Ɠ);'y32d[q9p[:rt[[r qd: FD+kBZPӁB+~Dw-qp@V/bJDkdcl" ّ2 `X]f2 ,N8F~A#hcNUnn9r3's29_5\ ɨ~חubG-#ckPjO*ԐCA !-w VG (5m0*lbju9sGh/NOY(,scw@-j`5D;D3o 38=6C/_&V]Aa!&^Wqv1q HF1oN]%_YkŹrt0P=R?+hVI?+{ĵq$H^& _DT5q@vS?Q;G}̶P=n]("Kc":ƈ&:M*6G4es.`mb ء=Aj*svvHlMT]m! $*TUTwǨ*CUBVUU1[UU|uUlXUžз"*>WT+ⰢhU 9T_A>5TY4)wG3#HsHUV{(P٪b[8m&HV: h+VPڊ@%d~B6> SWGugb|VQW+~+b+ȣ3W m^Uq#xĥ}?R*Uiy`UUEUFEY#FYҎU<mAkP-񼳔T{ʊ34EvEQC՝Yw QT|A(*ޏ**XBǨb= GTQ8FQ,0ZUSqbj,H@PE 8_Toq4KLK x&l^ :xv3aQ1n\j̯bxmG_a]աS6J x8]$FIϩ̀WɍU$PBȒȑHpҞ!!rD+ث2'wنKsuȟKs>mqz}:K5gdh$7'hU2z\&|.q Y >r7)",C`.h&{c<uˆOǛ{7FBB#(g}yqT?R+_ %X;\R;Ez~٨[!/ئznNP-ۨ)Cۘوe3H)ߨ5-VʰPPS,<083՜ sƿXRIW1SvU"C=-b}V Uf*4/E{@ŞT*L6c1EP_&b}6Jm ]WW "(֨jJRΦ/l 6N!wdɠ&a2XR7=h_%WO*rvHG;՗٨NYوx]Z^%qsOA?-FCxuk@]'=D Cb%0_, jWCD̃:v 6k}k)QgQoGͽ_NT(1hasoP)FE3eXSˀ#pI-DHz p)_ZEwО$;TY yjx4iIKAT ġ 2)>1M#z wڥˎ̞q:(nd*Ĺ)!ET  t*blt 7,&AWH{5-29f:ci>]-m6^Z$t֠|R\)\,1װN5jW6-Ǧl,!%)\یk"ݮфx:SU=em 0M8KEYTs6Vʢ<{UwU"WCa+]@[ zD AQ#@m8HUU$u!Vc(9 KNe)u5ި{j5u3Q[o΂wj]5;_[Uח{5HQ5 %b\usW`@-82PUK6Ob[@Ar3e~M8J7Q•qBxMHjֲ{A8Mg-jԑ4mM3d Y%G Fc+H8=x̘$Ǻ=.xtc8ccpc!zZ%$5F+4rNic'X?zmڱy):\ JLc̪  rU*Q2(U~A2HSTn9}`8񿢉3F`EK #Ue fh3w䑹z) h-cHΐ7"F;u'/RCC D_8=FH#jIq ,ɺR#FKe&UǽXEqDeQQYC*+lYY{"]1VsE_:5.&{^V9 CLUmBT[R_GT-5Q &L)a 쪅B6fP鶚 Us,T3a y154v}}=x;<67ӓ86<_~B&m\ܿmZƎX2˖1O%| lP7\PVS{Dd% h"r*FXV~Fvb9 SK9)o6gX1g %rTgu,|]|+_&;d+quP["M6Dcb5LɄ+6E^8yƃc~@VIC8c Ao :Αʛ˫pzςͦdς;5aTb?9.Yt03Ş@WkV4<NBB}N$:1Kx ]f!f{VssGz<^ 0w-mwE0Zdr|YGG,3Y?d?ZnҥGrg}swmeg7gqEOT -kL ddCRF`R-eGxgucS&5ijt@aou#ai(-'ߡPA9|bi𪆧 Jxg5xû?hb׃ӧ$&JX9QtC=nݎ9y&,?l #b#bWBPΏ0Cvs9 cClr9C1 d A1ڍ,mzaeCB6 ``,۲-l5dֹ*T5y7=3 cF@2 g•0m~/,M7\'hd`^F:ö7W{=OhD{D{#Xlap]$=8 M-߁"3ty ` ɰa^@ 6{썰7{a {=^]7TG ?m(\-HQ$]ζO7]afZNXH -rOlo}Fd<"l?vSVۡw mAfmo:Al*(7¼i6U[joTG!Z`hC ;6d ".w4ǯ(n{%L6CM6pVg4ʣ}ExDԵF.$1Dv#GxMur2G+ |W\m-l^3u̇`"3~GY" H((,N%[;X[,N"3+eLrxqںeq5",J"Nk#"[00t<ǃ(k3m,v)@vg(@vDB/DMbYeYˊ#YY/Y( QX5Yk̞C, og,Eqn87%C,`((g,"MvSG㰘2,Yhz8AqX[,r=qgg'Y(z/E8 a(: <_VO"fCX#0Xr< up-kd\HFS5.9L<8St*X%K z?uA^ ʥX 9YR{9Phݣx+ʃ(s 6,r eZ(+ʃ($G,r~,C5Admx:ѣ(r7- 9 uFey"Y=]eqO#HrwCY7(-vKG=G Y(,a7hFLHY/lķl~$Ar ߮V|Fh-ST|"OG2qǺ!b2,O:T-'d:q]- O'H=lKcezcEce%W@z /++[+\g`,t6-^ 㫍9c7+YHOtHA}+__k<\fp$H(#18W}_mɊĢE!}#G"3#x0TLU#* bu;J)p/*(wEdO`#Cv9Rme1.RNmPlVT lc`8|;rEh1|`HixfÕ'S#U#ML&19c<.\eJQ QE{~Y49-칊'|3|$gm!~S؋ꯆB=(ѹg3wUѺ܉jPLAK*~&<5V^WPc[V)9uNh8=Kg>Ypو٬l:Tl6fuzXCkuN'7x2>}8rjZf'/r]^M W]dk ~:;'~R! /t8j؂ky 3Ek1d4,A\.(8G=؏Ab?w+4h[2N-w)v^8)q9(/X٘R(F;rcif/‚SB7X߸2CelDelEe,wס2d#5#0 $$$ MB șu<8]q0'K;]"ɥdt RVnPb=qUخ=qUBG~W~x<`pɚh|NuaYħKRp~Oy%"hU_rge]:;llDnz~Y{oNeFsmyqz:u\aFڬ;/4"svXytkXe$lҊ&a^sKS>fz^hH ^$J*Mf ӀJpb!Tsg$4WVZ\Xc({s?\T#% Le-1e9N$P)M_E5LJͿ`nC%಄}'0Xgp/^jSI{1? '32 Gf8WrdP3puiJ!/$2@ڣlEh~|mcB05g)W6$TQ:?aQS4 %ɕfVw 12/HR\vh3_A}{Tfjs '׷665Z^ѶlW!R."1/Ѹ,@\Yؾľ&-YEdj_p'ipgy"lܩaC3ر(RN%pM#q?7/sB,!*O5ϸ-lx]_B ٻ^lR[VzI&z"0E" ? ϴ ,fgsjWU@%ƮHlm*S@ Q mT \+Lq\@mQ|@EoITw|E16BN޳.'\^K2q2 z"1wfSK>iރB;^%jjhjT4Y $qfC5ddciGM.Z\s`' T!kb&`+`b{p^bj>0j'΃K*Lb5󢎩/cILr`y-bwA pt58!AAX~pׂA&G LͣC~DhUQJzo6zc녰Q 2 m[]ew4e f㧧5>w ʛk߇Լ{gvIC¹j҃o0pDZ'U^:o=kB(w؛\+NISv$R5BBJcOy~W_`jI-N%%^I@:"q==D JPa?%qt'W]d~ph:ԐǕx}J 7gR^,s9ej|ߊB_\m*̞mlVkZ`/ 2OFoQH|LF1,6cdv-G_>ZJ6 $*5bcW4Y>&ń -]ֆd$6q[ۋ+xŖ@ 4*Y*i>4fs$@y8<999d1j.vJ2?af쫈HƮ+eKkJduF1GY+j ]lWItQ;HlwqIb`L*ձ bA[ǗĞAfبFx4| y6uf8B(VKa;c00=R'wD!a=`Ջi1Aev_}>^l%&0|qL|.~3+'۰ׅ;eo*{r7 ׳ ˤ1.p\-삼Pl0uAۀ/ h5U҆.j~o5ϪU5J52e>K'uav'heR!.ĄK7+aru5D`K!z.>[!lE5ǘ<j|ɂCp9¿Tza.. *o}qQMɩS֘rejIRƆe,mBf19*KV8Cg~I}>!O VߤpW7TbWErLZ}JG [q6)n;aty9(5l,rM,?r!`%[.  ty9S`Ao= }3|)s=l2J?z)]7KW6%@;/Qwȝ>x$E֬(B.΂`b[r}6'}\6CFzE{YCOZCk;][O=Jl}j@A gB%JrU|;40D ؙpj~oeV]JM8<3\ˀαA\o]*/dUzU&8[Mrɿy鐠2g.080R$C9.̿c ҭdAҝ`P'LI䗺iIXB~H^^:0"jkķH[Nn WL2*s> |>OƓS۵ߟ/ O 5\U QTH^ΰϴ\nCyLVa[L* ODK0Id'$ު+GiV%Qd6ɓo xWkUZx +U1 u}T|ijS\][r:u,0]lE|c[]]um5W'%f#ptLO#_1"ׇq64!+)RۏFHX7-"b—xKyl="|"=v J6zrT9P-OfI̶KC]UPL*ˤ(ٽ8mjDyU;CqdgAC4LjW(bex?_1N_\,z@6ҚSӚ&7OhJi4O(n.z$st\;6:D.fO&5OZɦ1[,[CXkpI|Ht2`kP(5C 34B |GkR%Y{TU*@xL[޸ ҉39s5L[(6'&fØwrǜu k7|8ɘ7Io7Y; 4St%gvt apAa1"{1`/r)Qüؚ:oiL4L\ QTS#\dƊܬp$b)k^Jz7DqN]TEZg F,c_Chʼb~-O3P)b {NM5u=,h!XWV.]*iqFOa&?eMUs<9YڋR 1x Gݚ 3*bT.X֍o0}"ہH ЕAanRK,-56Ӑ`3dmx:HzSդq&\w=MєnuF㫳meFGp)6c~h c苟\|-Fc6:@m<3;8[4F?QRb#1+t:z\tcxcݨzC |xX5ztKаL \G(֬pt8#rJ4}eGzR@/+[ot72|7yuKaigIu7 k(Gk0uГ:["l a!$T^3#@+*X;+[q&LI dSLMfW KUKhܻzjPRLHԻF%* 1Tģ'EOIjh+)+@3A&E)UU&`I\afqf*sV2jcNI.r3Pz*Nm's"sT`zc;? Yߎ4Nyu(>UXob쁹 ͩ% ani2F]<3wɌa_lU C#|"}pdػAQ.bPmjq@. |S+|U.V$Blu5fkD,EK$zi܍ ^ҪQLv5G"s_S'q\'8YXUF!K>+"'Y}Cbg$X-;# 6.G4eFxDα0tԷSBQ0oCkRsb0c;:cը #|={NZ:͜ kIaWVUӿ$&-s1͠' 5b+v>պ 2.zC9cshwGi_M:(s(7 w|eziP66%T?滥\_qq~:f|vqܦR.X]jaw WRG7 D+5 :36*,=$7~hr9:\ a Z$g\^1x -5IkS~TDU0׭orɼfa}t<b4"eK {B f__N ^z=n;>I2gk M7?0O)N|fh:ϓz'^;Y88s!s'*0vzCbx vsΩ9#_` (2>שl0:BƷk6a< TG?f 8s\/bu2.OXb&:k$׸ $y=Cxzx rrȅ nU;3{]Pb*wL$ ӿ9ů{ 45Pkt}!Ea0k)]ǯPW~@Oz\nyOa> /C*u}b'1+VBcHn rBSRAAK  U"1~HY/RϹ3 %;1bN̮$&8ʞ:֢C݀ P=3 /P@:O25DljL.'~?z\FLsnGӟ`X]; E 3:r p#,!#9 h!N | NZU?+w>!Zcw2k}Uۨ`tA7:,GLD~ J?o50oȇ6ÑE@Eո8d׺ވzPds aKB@3 hOm_2H^P7vNdRu0áD^/qDN?͊^ltx@nJBIbG9vh2*һ?݀K2h-X+ %0Ybi \@@ct ב:,WY`@lv,&Rų@&G!lz]N.-j( #2;6(1\ M e:#&u2tpNowƂntc7t6:ocdlг@PDzUxea-0;;QLHb?bۚV)r~Pl1^Si..yws#my:Gd.`4-)f#JdPZj멸zfM#\**ST.Uww9?u_W$T^<$UB 15 [GB?ViHz&eeJ[[=Azr?2+w? yL{fh!s}^˔LnKXk # `W9*棥V0t90#,s32wPAgY!} ]a=aU&]ao:w5V#[koo@+ƃGxΪ􋝣eW'79BwZ3H\IS$2P2cKT[ZZ#\6BhZli?fT Z M--FVEOf|i%ZzKyxq`G{T*` kt|֬f'= W[vwkHT=V`vߵ Wvv!:ͻ]6.=v*w1vYg,b!hԏhGv]kZZdW%5( zޕe}0$vXltkɥy8íih jZS!wwww )!k8[D073B5B:lT /W1ˁ=hRo/hTϒ˯ ӿCepgX-Li-,ϊ*qyyկ"KO{<;zC%c R^YFZɔN*q<~)~aTf 7t˛2懶ڸ XZGT@M%O\;gY^9g,ݮ3tڈHXg;} !aXGA}5O 8W߂M?)FD%CG'g1IH>kflhSdC-Pxh`&90"\6 sa,E<sxώ}XȒ봱`8pCJ GYTP|O'hሦ]s`gL_DzC |Ψ J5"` P[EykƼrxyG}F9\$13l 1XdyyyV,=_<ӨٞsVvYzϼ]Z jni?<-nxeSNFm<8,x>ȍFo(D~fKP;ƫ΅47##jO̯a~͉͞\}i{iqG"7K8BqmG/*G6՛fIyV$yk n'cݔ&yxJ <^UȂf_[<*Bp,4M`M|Kl[ P>an?b4)o n'N m>!TfuGB:`O笸1H)qT/(qf)"A'cܸGr0! þہI/*q3gu%uԶ-$ZOQfBNr6m?dCJ,Szc-)AnrteRUx% 7 d ) /.^<Û=.6 ;_KI>!KK>j 6 &LY+nWT`jðV-vJ#]V:G ԥo17`2ƘL 7ǞƇL1Z i!o@e ]w@qG7k<ڄmmu1C?޲p-uж>!72Eѱ31t:U%C5\~ plgfHKa=0Nܯ݈_jI7Fe0XD%JJB U;Ij$RO&nS:'EC^Cc2IL2IUc2IC F yvhy)f63ɳ%Qdhhɰ:F2͠ίDC 5&[u"-:-8&DM65f T$<,|@&>o si(|Ie\߮{$WYVM*%7(($7~hupXTޗ:v [ͫ;TEd&ߦMXRb$Ȓǥ,UҷUٴ6bKHoͮnP rSS0?ZRBqswtWIwY w-kcZ羅ӨM7j&/c06cp&3*x7x>w2sW+k*<ҟ!qK vV Mi@kM# R@aE53Yyn,CA5`A^=HFwc\ kwqaȥ? +v{t\szv%u#YJT2fBuFҔv:mrJ8Ch8'j^^@fg3ݸ (ܷP6  MX+9"ؘqYcXĔR7>4W嫒-o|U l68 )m3݀Wy\WTWu! ZoIW|`t 2} J_QQU(:k#c(j2RsAZ%\~e^3/~g 0vl(ԧV O D :7C R,t< ӈXbk匮΍RiJ%J"poviՈ_J$ݷKK$ҘC~J)g7PX _VXªlxn 5݀w}*vA8sA, ;AlPkd}aٻEdDu}fm"1f։u^m!ϰ_WR\^KJb\.&*X$ &{v$U ܆;qY*v2Q*A]q!֫ĸu5!++dqsMklҬf(xٌ͑U}ƓeĿ>I Mp(3BșR2SF ݃P߰S^Q"5Mfr׉4 udm\4KlKB';0Bo3)r&L*]5R7!& F/I?!R"T&5;t]qT>dT6y̧0q(FPh]}-0LzDg+d:nœǯYgC9cYP}ڥO]'=rx1$حO;AZo\5hyM5+Ĵ(pGi4!ڮVP0  ^4Ҡ2hoᮆ1zs1'gO%PTՁnJqگoM ^Vy٣/GKwcyyHʛ#1Bv8NK<j7jjAL GŜul2ލ'5h,gtŠ066ouz՛E\ WZ#.Zv30H΃RkA5 oӴ>6Mv:7aF?NI=q)Ф@>9?\fx$doL\'$ʾ/c~su _?<<@C-F6LGC8 DbDr0fXvqARǡtat\B.^478 6f+8}뜈jQhނqp'r'|#|Be~LZ~A_Q&V *_,h ]QPEMmjC*KlNVço )1?z r=iJ{|iZZI{0iFO=x)/H;KOYv5>WM%ҷ!}%250H(@k-k5vLK۷ MBE0eou4065;m@蝪][Tn#b-%+4.()`VA-aO2f co\l(xbXWO]ݖo\vȱ,6𞡫v m2.Ȼ1mE>|qghd'g(Sw|_ 2.߆u Vws)ASu14{jhm.w cM׉s!ly?㮑v܋KkP7\]f#lsTI3Yz괘ڿ` 1x&}Y K-R&*7ď̠3ƕ~6=x~k[nſ:m L=J?~u;1$h3NѡSGGQ:4~dOt}uq:ݠ'ҏTٞ54bS׫W] M  uw9[ `;&*) 5bvm%3ۭ"nDfҏ"R&@D(yWy=٣}I![%G͠MpsR\3;g7(T6iY [*4bvЍH-; v?cU얲FwhH$s2;i\)WbcZ;ۯ7fSvӍ?Be_c=#0gag{"Zri8i: E 7Ɇv9.1eHm@]f磻D.di#yۓlwk@:4Rҳ?0٣MZp1fඹFNi-p3#YrVnR yLU"*6HMn77_.CZ&OG~kkUI !ʤ$s6kQc3C>bEOk^Qo`߃G(KIUɦQ#;d[;[*6 t/T;Ȑ)":οs_h_F#}L%^<@4eF;elCDF yytxM lOzQLA$ZhkE܂[믒M^3'z&b7TϩS) L Dˤ)w&a2%L%ecNyvDYzכ + A X+rfت@wzQ&J?J(on0ZTH e*\eE;*FU,ad A's1ڒX9 ~HGf:7UbgKs*#eOxp݄=?9 W g NElWI7[/;sVTMޭrY0{c%sV~7 u0A%+q[I$I4s^&͹/\V82ui ?*ߠt~D Rݬ#z. ^:3NhX'55lvtr=aR" zR%(, Шh,PQZFפ `Ԅ-p1*XVv?>߻wwҢda|N88h)Yֻjݷנ -mt 㥌vLV Ne 'kJybY=דH}V=JmbrLN!}*T_Y˕G(aXb\l3aG) ы_ݗEx<5Dfk+쎄-Qyq87H- ; yh]2C/Cn Pkv:.nWhA*$~{[2ts`>EZ-kH-8U[Rh$_4_p3&rxbm"\Ɇ  *d8«aީXWV&1ĚϥW2HZ06JtG;/T nfavk[S3IƔLʗRk:vUbJ#˅!`+ՄS=^}g Vl5v,BpVP6Dp|n8zkt)TwР[ˤ_=?Bs8*ʼnJRٙrShQ p|p@+wܒ`˱ -a;\?k1f 8HyX1h6`i'0 yMHdԛ^*z ʼnHRLLǮ8L1Pn~\3VYg0-{ ;vid0 ޳AC 5 gQ"G @B7#{QOPh SBTt4+C%1\sjȨfza<#d%O&WXB0k056RYM=fB!&!p^兣9u$:,lUKB[dw"`&c6Bz5[s}r ln*Wˤ;9IuN)x-ldࠐY0R#٥ wB_w $@+dgQ<#k!:*ʼn;yb0Z^0N L3(T+FHf1 nܑ*\Jrj 'AD<{$8s&&*q>N,NʬL9!hʚ+4"+&VI|x{U&$M}l1R6͒fT;ӓXbo% mϓy:T$*3wmR)6:L7N71]:0f-"<.Zƶm܇rqXs1<ghU:L7va+bS2dk&Ycz"1l 'Mn%(B[%bM9Ej(Ol6lžhNpi &j@G\'4ʚNUq{3a ?QOk@AmO^v/TxRmc>3*oeo4w_?^= 0\:0ў2Gt5Yqiwr٘-2YҷմlG78lG VSGYA7[ Z9hv1;\(0F'[Wa x[ ݴ)v dhaH͡j4Mo^5آcM D0YA'7ȾXUUpIMTԠhwm]616??8/oU6/v:ތUmiT`dߓ52 E{>͙U2F?nOӉкd8sIw6>W`l.Ǹ:Sze_ro-8Ts3%xd˥#fo쭸\!}0bWI SŔ`QB(gkX 20ZДtrhM8=qMISgܦ.nK-xՆGR j eq(~y| AXXQ@r<ǝ8w^$䀈w&Ŧ48k$w@Kq9˒r,xTqiJU\z1bM 񋸰ctir1݀.RaG$ ɾ{/AyEu|T.$2 M\:CAį~Au)fեxx#3) u髐n}I_BWZw>)Um;.Oq_ UwEz)SQUM~SnxH_Jx֡,+G*êjfb.y>Yh"5Mǁbے} ,Rs;0! +x0{ řȄHYn e TFGz7vV sr>T 4< c!&~ I{j#Nڸ su(W j' {'XNRTr 9 WIoS D'7¨W+fFg4E1C̃kqM.Ã>R4M2ĔfcjB6!f%nЫMXbMdPZǼ FE<;Zl1y' TT26m,qf/ g-> [, ^1ЗҷFpv}Ves"tAf&aYdz$t~,.N.,RM?WPߡI@?PJ;B,Gse(K7hVBc[M#X"6ܰ_W%}QwLzdDE-Z:Ð7 *GP n,7^^dRh9O"/k[ ibri fi<5-堓bxT:0'iѻ ^TOE[^VQTV.(Ղ׾f԰^.A7='6á@ //=nģ,f>sω޼"wr0I'x{ȴ]qk;'za7 !ʉw7+xHqfE_S9]އL+#Ÿq|ɉwB77>@MګfyFc牡Z sԚz'7K!0<%x8Q+'zgG!7 [IZl@]^e6x;)çT<'v!:iUXW*SwWtG^j936- y~JZ` hqh E]e$*T45BC? Jpsf% O28FhC@9"ÕH8 .N7GiU.@5r ~E*9^[gT8ÿlyZ{3t X %+sD#HL ' m9u xW\c`9-fBAX0g t~(3qikOH/%M'A}T"^p_"A%hߕ%Bډt~ U!֤TV-~Z T%Xs(K;KX;A0@??\;\۳Iv@< ]1F:?F C(CnxD6 Ѝ$<X-mk&>aYdlkS΋WS^<L'x'^K;vGnr揷?ڢ-Ǧ@6rw(tX4C_~>9sss{0vcpȼ)r-ѓ'%".q&Fny)YZ. tG_5ṃH}G)xJ)p6>iFQ~([4[(ZJ+bڊE?v\o=~/\R-p'$. G,1\쒧96+ZRK0ł;5QpC5rQKrْ95p^uEiP>Z\~cg`Ib߇d@^! c~IşZsó?5]\vo,2oܿ\t ZW@.Ŗ+(N`VL ;_*'w-0m10wmykW>W 4萧uoz'ߛt }.ԔCS)ߋw?*E $;)0sVl!bȜs$0tZ](DB#HC#`,k6e`ms(>5vp9%(L>Nq48Dqiٞ#=\]쒁{_{ny]Aٞ'C\w\"<)V2t4*o ݞqo5 6gz+)V ]g@)8w% a *vv>}obxv!@kT0^hÐ{\9ٵd$#2[>=g(VZ(Fzb=% Q ,}lgNi{0ֲ[z,aPi1K 亜 $H;IyNbHfU?wD;8!` sZsi0D'Ǎ}P&نGkY[dst@24<`8:𦕣_ 4XG}JEfI/T$קּ_edn 'dnnљ3A[ g(mKO,cNWt_r;0zM9Tpmk-$ݳN6rV1 7A ~q)UF1Nb>]sBEB.,^j#B8@g"2`pâ7n7*jˌBx3 =N I=K{ָF/=:M"ō*#p]Yob෢O2P$0j,(2'nX'BZ^PG݁&t'(KJ/_uE> ОMTG6;zF!o7ҩq+ zӺ*nZ~S#J1q''꾆 ̓`Qnx}1yՙ\V;|5?ʵX)beTu]G6N޽~|w7kax\;?h$G7o_U?3 pсmTTR̗45l& DGQۭh5W0pݵ7vܶWu۶ M=Ϲ/s0 o};<<9~a?XoOh*[-gQ̞NS((oP@?C"Z4MN<VujZuD]#tiO\a ۼ㳶5,;w9ݗg 5,c*2W&z'`57H$h+U:$$LT< INW1m̏V~KWr[#=7țl4$1l>o3cl2=uxs`Oqβe&J9Og}y }in&-pLaq3т ?K$*ЌT!8K`V6~ۿar+ U}3d[zV#eZU .hoŧw*d2ca$ćOY0 (Vm'} O}7`˲[S305+W~74Y|w=W n&?M:s6(n}}d0tn zFi(aO.tCM_)'aZ\/{^:H$B4t߈k,h~{&ĭvV[x\L6.Ō0&Hz OxSg{\*ҫ V u!7@Ƀf̟c Qfޣ|+Olp܆Ou(;ӉLa!=/JE\%m7R[a٫)h7z/:F3YULGULV^vl }ŋ|ìI>Ywf~TpmXrFZ 1j@|עс;>3,(>ZIgZj=RSވ{לfW IcrlW~W#["S20g7)|0>wX82d|B0,*҉*8a.O]̄kNgG>y`Ny;lF!HJȦ^eL+ %~i~ V.sD)Oyj UdUд3Euj*fvʺ~7.F T먴%Ai-O&BiQ(y$QwM>D *1"_\z v/$Ba:s+^DabysouȮ@fWH:ã]!lZ7.??,9lJa '7/K qu)Tb,)_2~r6{* G:eUZ^ӣ0(/B['qa" 㖞< gu<+r*DͧCVCA-[ً 17sA1!ɬeA;fD&MC7Sb> L9ɻ`190WlVPw0~\lƃ*R5dxI 5 G2Ɨ5Ԉ{7(o|ƒ~dnYKȻ$Tg*TaA^/u!PYf48C4K.j<P]rȷ,6+6LO>u$<\:# 7Tg-`WߊA\i!8ŕ&C=2jebrsb2k&B~hjd;ɥe[3j3,tuN/L&;I70GI_8|ihM>2ԠTw!>7z*v`j \38L#Gr?0P1 HQk8MvwvJ Vޣ6(%U |{0%TK_fAU\Pɵoj%ϙ(]{lcT/%{>fmk.>xr7ia96rmG,p7)bWF@xl!A>nK$-]렌&ߢ3d-/J$g:4zteAXv67i/Cߠ޺rf@(,&&(rLSAX7DHg0[MUA"1Jt"oP~HLb8oP ߠfT Fޠ?\u(TAp5#`c.%N=[`7"GjPPtPPt)bJG ޙCYxe r3zޫFA)&?/"Tň^pSҴ.>s`h@R2i蹮Mqxhz2xځ񕒿k}k) ܡcKVSi{mk`ip-7=ubo,` `)xۤS'fX0J&\cs;m'@ԉԌN,5ˇ1EP)Z3vc*rdլҪ qEQ>Mg"u@ j´< k*oOl).ﳣ"9k| CH}_Mday`bIt VzZeE>bE.E1(bLMAJT Hi%B;4@1dO .j˟WJ6OispS &zY?lg`GoL>q5OK"&0z&g(RgU][ziP`<[~d\R(ryrgT/FeM3@:iDk,~򵂎ZgY~C9P-ѱ h%4} 6W,yL0-B1ڌ4 4(ƪRXi4jC֨aHt+:PX,?G9#QVǑq|blI M317; teqLPo qy!f/e>! ؆oN)e9}ZfUq* ɿ}~:;;)Zc y|F>.%B[J':dDO0LyEbe+9+&0 |_^ W_,rW EyOlHQXt>gR1&Y$-eEjyy?J*HTrSTKJ#b :#w#,njY犕6Qv.\y('D̞%XH-V"1Tn$Xp_wJ|AA~֩fWBnQ pV0e%ΑN~haǺQ~]jޱ9Vi޺h*Gs:Ea=R0lтB!:5g9iXVQYS% q>6\?i'^>IX"`N;*"l#c鸽@( hM~AES(*t-+Z_:i9=X R̹$tZ[^A;]4P>Kahi`۬@_<)бd4a/6ꤴ496TRF.~/G$3஠0b|yLXb>rUbE:~~6}OFM~+ԂH) >1O T  ԂdIh媆bU㈅r6ԉ nx9BaElԐn5Wѳmnn)!RV݌T|\cAXHiM|GqѵȹM!vŐŧLfka4c:f1< CCtWRzit߷y|m4g@)fg-&{E.-exšne6J|/Fc-JqPF) R0P\>6*FL禋HUx 4M,PA9eݖ4W*!Z0j H;|,RA  iյ₱;_Pi13ՈYuLeL5T+.<)^Wq}|ϒ~?+ }bJnq* B< wfu[lwZ'x<6F~{rfא(OSz i {+#:iW\.?Lo/)qɤ,&wm,N-R;<.XĤ_n 0n0=^h`cB2yprw

    TF5f5~5\`bۤ'5LD+QޛO jsMQpFa\}d\?_oTSv3f-FOTӒTNNntNΏN5pZNS{[ '05PЩGxp8N)8E;rNj;T##)Qrj8OkX,2>x͚6%LF7~z@L޴bEnB`C>h6r:jcxxY l궳Jxl/«FO[?δV}`&Gܳj?a\ŝy 6IQnԉcY{z/%b= 7Q-''긘"[PTo8 3AW䳷VE0}YL T\=g0J;Icd_{6%lX3t0GțÀ*Qgz) xj(ʰA$,|+֩ lW_ .R+P(pW]C_i=}\/CNGΑBA8=,laP`X7(;4'i1J{Zm8g(*L__]OmO}V|4S"S(-T}Pmh? VznEP ފwd1`N%g)pޞSJ\K*(DC^G~)Sx~z[b*AmhPѽ'Hѽb,hXPArtoA%JzR,ItcHm vs燪; #Rx;UwE254w+\^sy"[A&mSv0xNθϤ:LFl~)¸ ;[gTz(3W1ʮ$5bD m]Ŵ6+^$u&7(~ ZrƝU{ʞ6{CK+CܽNRvVq(Crrw;׳0 I!~h7cImS~~ǖ'Xpױ.n*h KC7#*?.;?,W"S׋Xk8Zkokf>`Ge|b{my@Nk/kob ֆzZWz"X{t$"&AAe񑃈û "~i{~RNI1h?OP{LA!lϬ۟cɤ(cEK}K>YYpr)ʰy {x['&x(Nm(ӨEq"OkQrݡ] __x%x'/n+(q(Ի%)B tp\hr] 8C> 0^5L5qPCDL}'LU1?H1{L=s5*Ot5 Q0e= L=׻MG#auQ0?V1Q - w5@rL65+> TFnFWcAUke_|׆2fP-ȵ %Ct*u%*/PFuɖP}'9w=*8_h>ί7ݘVn/I?zR.T9ȄC dJTjo |N O5| |f;/Vչ_*kNBsPDmTthó`./!CuSUb*؞g`P@_b{y>BնZJkٺ3Qu)\lvaͷsXes% )]: `7i 'SVȈDhF_=[l Ԃ!e du)-yn;vzR<)pI¨v֍RڃR:I-we)Ig&Kl*?I6'ke lM']dӯ{ٌ\Mdsw"uP` vՐD~ˌO~4dN|{\p ]haXZ$,IʖQb^Blj6皠3 h$q37]|MnC:Lg%RJ n̆K#7wgmGVi{}& ֠#*5 ~ʄ#6nC-u C;N;dY)(%n2?]l xDաYF~6O7%D틪)%dTboOTHtT,$P7L).s&LFSK~EaaE~8RZW-"LgG0@<@漡dyaLzCQAMb*ݏ@:)T`2F0Wଜ$ d?zܳtaQHq7_ĦUIlGammx2O&PI%zr|#PY0&#/&IsITX^kZuy-4ms_%UɴQ\ZH_z@=wX[ZIXNi yAYmchKEU3QQPaŴDQA|DIA+{D˔mz[om˶meeb/)XTg*Lyιwf@}߯>8<9yι`+5PXV9˪wһhVmoeP˳Aǂ9A!i@@@qh` x @!hJtIJDB] tu PLQ謎lV鬆v`tWZ/F`#?|ⰢabVb*|~ q<,3܈  - '4|!EDVS$47TiA&cxI-`_e%gbHj%K@`E1Xqxv=NkO+Q?4H%&Fs[oJ6,saY1H yv &UX ?[L8~$N֙ @Z{QD4My0=cN)m}kd=Un_b ny̭zN80)wF f֐>9턻̅qUk@ ̇cv‹44~]H;0Hr|o<܅ !|;L$Xµ(0Pld%=Yx)|.iT &^Tf#LPtw{:0^H'W7ԗK1@dmv2ZuPo7x:NPYxR$Y|W\K5(jq 1 VAPF^>R*6hFϺFHqv+n!.rL֓c!*0 4^+2At`bw&Qkb]J-dGFU0hR mY\՞ Sr.ɹ91ʜ#Z tXrk(ܐ5\N^S-&w*+*RVXI'oMp[ 6zZgGPkQm _ ٔ%vv^%̬MR/̥x0 __ 7 A}Pyd$k /K2uvNPFԄ7\ʂtw~&-3R(c2Z489PY7Uщ9RHL ēO"C33yԦ ɀI>#Q\ɹ$:F 茭50,BnB$alf|վc(4&H`FUzϚN]+\ ~pz|kח([a/-~ Q^"{AV6}Ոm8ơԚ\i)◐yԤe<ʹ=a|Z0ȫ/|Z;h!rOK PȘ'\K,֟ņP$C$\n)$e`HtIߋ`nWU },Iw읐PFddZ]xyx, (yQzW'h0$'$[E l'Jx}1eGKfޟ0}D^݂RЈdMА 0QF$? !xlgWN3 @Ϩz-bȹ: I}2VK$/[/~+SFW/`$Gi-f$YȤ;Di ?Z_$ӄ J/P3[_OТn/w6[~d)*{AW_!k3%(n$)/AZeprv[y oP+!^smms[;1*tS"3$pÃ}eH@$[0Gڑӡ_ Zt_r T$  @u m`(g̉ @_o bN,!V,k렘VH%IxiQ2ڵ$₶kvP3@a@2P@5YJ|.ǻ5hXõԤKҾ5JQw3#-rR&U&)U8N}Jӽ ]ʠ:kލhu.lja0ǘMie(+W٘UTU󓱁"@x]za!o v k?ZƋdz32G0҂^BSgYw\SµQ;4Fm Ɣ.)$-уAYhFΚ!ď"Rr>owЭx{C_ϣoGo[ʔi"_WEJ](nkk|c4`lkIc-b4]CN:$=ۆWdɴl7%K:Qѓ-ǶZn p'=L=Y细f/Ke{>M* l.T/{[dEMI\ |gUZ {p! uz,=yڊ96!B:Vgb#.>9LN~b%(e ~?R׹_IE9Qr=һ>"`Q>RP+/YbOM`z7'QN( ?S eQ Tq.0g Y>={KKg'FBD[>Qm(`y_&z_B_gzlg0ĞlerVx*@ vi3Sq ڳH{L%xaxӞ>|106IKn!W.wir :pc1N.Uwa6fRU B㱊Dz~17l< NNǰͶ Ѽxu*F.*zҹG0kXƉ2zo5bΈuHoD+,ʮ(He>;Ɖ%| )\A iU''#֗v(e3 p^Zgo= =EX{_҅s+eBDVV-[odFG#|]:ac2o|1_7ݩuE{e3RL(FA^4Cyo%S9&+LEx+ 'kLIHt&Ii/Mi}O7i?7c&dr*eC&tZKa ^ DvrPQ4F=њ 5 ~D3ʩAEc@{RtHI+e~qG1ɞy7Sx!уr/՗ FweXvAo%94`G׉z͑;F#zu[ׁpm3$#5Pޝk""xНkBZ%] yӮZސ@ )7ܣi$K ґa<3em' ꗋk6LaڴW3;ӂ#62^nP^=HG;+Vr7}b]VSY}lx(視ɴ7}3*Ys 7hjt?AKA{IɩiI'+fy6D } D>z`c,y]stT6I[:?p~}-UVZiOdx.hy~ 5n1%wwdX 8qScgEwn]>wsҍn7uv4%?4 զ6B,Egѐn3I9-72< 3 2oUGD&2q;,S.#Q)iunp&<h7 S3$F( W6(z Y؀q} vFBfF!<5ҩBֳb&yXdZ4c 3hg3SlN#Ԟ~,| oX|5Dއ@Xr ,&sZP$|MA ގWR! t|zW!+z[r\Z/]NG(MirZ/$H`+T6*½/I'i#ۼl@v-ʵ3k!ܸ=Bjth37hG(<w.EzV}tA_)S!j+p -8Jz=&Ijkl"\$3`x<3c>F}AjZtP=ˆgt;HKſ`q~ q=۪qs"= & t r$5Mp *ٸZ.љ.~y!E9<͡aO9GI^:zQ0OlqFQUzæ(Z: !q8Gw^:8a~ua~|"?i"&Y@I \ oZ_HbX/wq5z^)ؠ k"IFE;r~ՔF62GVD?fK;aɏH;n⣲BO 1(X.Y+fig1'q\\՟W+b/\gf&陙y|jT~J۞ 7mOD=.}fVcW$2 # Q<>]9V;t7)NNlIlg3%@4zŏ!g`? d@ϚTC[vƺ 9)u6;̓/؁! ǣz~iQڽՎQ"3-&:Wڮ&w!͒<`4 VUTvr0[C֯uS=z[!-+;V4V45jQՏS!ɿi6u;E襕_h!5r,2BR P}0l=T"sm8\>cScQJTO6pJ^Q ݔI7˜4/ʒ]j$H@{bprSḷxg[DSE)5|7H$zZgo39<_SK]˜ ^X E@S'Q8B?&3C8Cs'HhgȪ ᧶)94b7w4GmJkJ`Y$ joОˁWnjIyMgE6k#)GWʄK*k$SeqBhn!1>5gtjPR/3m}t8qHe]Ogs5CEwW,$Tt.`QtS[s $sM'Š>c(ɌsI~ W;U0iZѯ-*jAė@|s\Zt%kC{SQI(p ε+![;Ϲ:_ iG.P8uA{ X.mqjl+=Mε62i= A2Pa<9M&0yR mk\|Cqޥ)C_34Jko+Jw\MM!#I!bHt#]=&^l Dݤ6`#j/ 2m|H;GH;Aa WQX;L!n?hO\!Xq/ Vz_ q3酪ofV)\o]HldݬW?UJ7'ck d"~jsvjǠxqJ\mH/Fς$VH?(݆ F*K_苊J!8PrSS6%.HB޺Cۭ <  @׮.r4E_kC^A uw-w]M&n!ŷ] tYh$5RhJ鈖wΜwЙIt?\`ς9 Ɖ9_^x{v w-I?ol'ncC9FT~݃JWbվ3F@hëȥQq_)DN* ? B^uQV.Д^ݕmϪgO;5#ɺƞ֙%d7WS2q5֚$nsC;L߾V;AeLכT(m$M5crn'eE6onl*#GHppFРjGQ`IQ7?P{_djQͷ)z\`O`OY(H 4:X6#jC K@Gl=j2jw13C| heHp͠vzxÉ4{%,K Y81C/t/+vl-v/Qۉ,i}웠<_HMC|Glkz8 9dh%@@%i".%LlaT D#ћ(n(1\}E6sC(En\VVDH7%,:Pĩ%?C]&~Yn6Btց_;{ sf|42B ϲKi<|~CCLR8}t}VCXUxIK+yPT3#t]~;k洹6?Gfu}֯͗j\ٮ 'Q`&F \ [%j>?_rBc÷a3b$}Feq=G]h5}N^?p]ȺFԻXd+N&Ip4dm'>S6pΨ8j7Mu8bu 4RY@4O6X"~]sSJ\DSC6~o_L7B_*zuT~߷"_bQ"Vꠣ C zIt`䟠7Oނ][}}WʸKzy{fNF]LТ CwV-#]^ ўvWp[R==#ڼ7Jj̵\U(s5CH(2W3 ciW&ϣ -5+SjH( u&H*5N#"S4s!YC.iXoѷ5Q~W~MjDXАyucspT45-kXDHHqݍo>b#wU*;w4R4(}j ~R S[gsMbb"ȚCh$I8&Nձnqz湐 7]0.궻wN0Y/5@ 87}'o34qc]FW'W`/@7u@.cXEnTN jY@st**_4AW9UOv3N k??ؑ hyi<F|;T0@h A"稾qO8#0i2Ti:e0n7Pv)>Sj"?I݊|X|8l#+Y39'߇|輇QRjJi b;S*_Q_peLBs|4=AEr"$N : N82#/!ՀH+ZW *\:~ M`^=kh,3ьg4J_|2M\=%|,f"w) uS6!FWI մ>dw1JΟ uQej zHaF)@,B5,VwD1md\n{KvYZ!CD|0#Gh4h.tGavRn&Ϲ,e&js/qg?=_Ǡ;J-!@"&/Ɲ˒adh?Y 跏AGU=LTht: SnV,X -?Q?> mK/ۦa!vau< a/-@2_=qog#C/Z7Ak&hX-A>x1Q$_lAMPQ`x5-|+\{4|OY ~RJW5 xCh'GMA!g!YN/Xʛy*" / ~%I2W+ D2/΅c`1 xsa뫸d&f{%&d,AYoA=ڊBNo# Xj-Tjq0Xы%0i#v5gTmxA6ǃ- 4o# Pz,w}@z h_tcynS6S wŮ}ANSˏN\C`_Fln=Dlaf5l$C1gOҠEl[eRYCSGuYѝ={=8x.蛝%^0ndIB&o ^%) MmevxX/jԻWPwNJ:e:RDSU0ɪPKHi0>hD`b19FֆA2n*a{߯ n'ux*]BK'nd}OiAlR"v_6;S-ղ3ʾzQv Mm~_i?_g~fq:g QQ}5PI3@z}JR8;mtAu#8?TTI2DdMɗԼsԈΑڑc9tZM:[/0U|(tQA3zV+< Uf<#3YHxcXq& /4I5T9/ĆAN;=]$s%fdgIqH"ѳ6s"qlm?fc b\:li\y=ޯѫae^9A1tm6=q'J` ߂~`{0PUT_d~h@cn Jr&b`F #Ha&x@kJA LdaR?jw؈S䛏V?E 07HY+}*TxGQχPcuj;J 3H,BMבoa Y_@i2ޠFu֚*SQOЉ ܊STVDFUpuE -Qr%˨uXőP76}GcA.YBa%;&׀SǹT[Fa1tseaDן(~auXQ11bgPVհ;)E"/EaT`Z:ˤ@nG& Op7Ȭcz uUgWv>Y  (: "JMJ_C1/3 ) f &aR[E'Ø%sGUSV1-!\h~ D+zP";]5s&r=҂2LGPĤQJI p*YL^䵏h) I)Ю,TA]JDu= !nʵ!b :QΜ]@;!LĄNS:Zlm#'262iRYjNbv^fU'=>=dѝ_AKRUa-s6HQ^X'Fbw1%Y# &!4 M$ Q6~Q2 cګ !_$hx1(A9 v@Ư~+vWu }E,``IZ/U01ȣ,1 TgrA`GA.2PfL*AdNs=I.5ó+/80p+jo˽kInS=;Pglɢ<^ۋ2ic;Zhь+( }Ŕ0\ze?DQ h CDH'Ux c;~DKhlWXU_,ީ8ق!>GeAc2>a‘O|sq$!,JPa}.kMa='2 wZdjoǍ?|OG{as0b{hY ˸$ofLk"y(u0#\/Ήe;8:04k'тOiLB_^X+seK6joGuA\2}tPn1*6x_;>C4/q-~^A-s;d괐 Ajto=h.z2ctLW:XޅDU2Xv˛uظ̹PX,t 7E$qB>E7A O +h7&go{X !lYi`Vޮ>V8Q'9Tz13 /*)MHaxRIRU"ǔ0*\VU$ň=iv~D9Et%/*턲-5]tt%wd,SN @R COkYG>9Ո*ݠj09ea)&LtcW635g?y؛'QUtSD0Rs3I֎Usd$ ډ #R~L HR~ /2#cj4^mw/Ω3kxGhm/ ~r܀~7*Q\݀*rͣ'G<''*>}R bݓiWۚ˲.t@HNO]&ꔢ}LYw~8Q,|""[Me#jt[ rA{Fm4pv?ނ;͵[@unKpMx;[.BފWDHu?,&Š6: - D & Cejmez2~1=Y"7{Sg1d s`R؋D")#;^ K|3Yxs|ZG 񤥤W;}k|6+n 6@%BO3Q R]/<.6kkpX*a ?kGYs-&=?6^kk<)tK6-`hdBJ"`?ȯ_eydWyfg Ʉ؝bG.E 4]<aI OVc)(zZg``=ze;yA[!s3‰1$"8<=24&R;x#؇XX|{BzW< $mwe[u ӏ% SI; o^Vmy 1k9W%0_\(s "|HRHJ2RNs03ől-?PQҺf{#kK\5d[2"~J>$o@Gڍ:dcknK$ʑrzlCV +AE9@_W/"1kzX?KOF+CNY % mWw8f8M8&=qj_冢ޤ /݂ZK㱰x6g<KTMyƺ]s$}l1_WUiRyǮ9v~gmg<Xؒ/|g7]ov'dP_ˮhx]S&};oծZbeo\3}dG&tbj'O Ўk7}< O:`(lvRE%غB 2 rnTYT˟d r5U}vUArUi G7㸪T;]OlKQ PkejUʻUQۡ|o:~{r[Nv)+()Ln SuM Wuo)oYQuN Wu+oBJ?qRyLg‹I4/ &6?m)m'ET!*̨)b7  Yo/!GeҌ9K3Rvŕ9@2⿕ W Q7?~w{pqT| >t1r?BAs=Ǖ f 7## j^;Z?ۯZR]A+zBOi'r?/0gёOڪ珖fAoGA{8CkP=b2+zK[f|s|@[vDՄpUEOd7Ŏ\ 'Ygzz+:j]\2g{ͫ nQ=ovVn^e>PtV>5*bC_Xx5Sj݂:nP?l:pX \}eNZҶ+OGu:ɏbCHB[$q29)f)%(^)qu8)ȍ iw*tDq&,W4;vN@&Â)w)M)ü.i*#~wI:ESGъcMEM 4hPc)pøw#Ju?O)k=у02ԭ^ XL8 JӷĚ>]muҹ4ZB"l6C.!WHA]SY [Br2mRDVKϽMB$aC?ʲA HǼY ɫP iUׅއ*woYoyJkdT7QphohAxgމ_pA~.KC~j 0LoGuALW3LQ SsLWpwd~n?Dܥ}.=fTO)m(c1W<`oBt53jxbVxS<W<S<6[Qĸr{'xWSRzҋ^}Oوz%og =|d&QwݹΩ[o>SnyA6ktqGAzIéϧ7ˤvy]H~ "aP! |krCO(6HpZ̖q7 }EquB4FsF=-bmD5zugVmVz p^*/TzJκ-5Eg VV.nvL6R ^ᣴRtVh;օ#w qh$F'J Y0 6$>bi:;Olq8CSU|ЯҾmEF>VHSƹ]+`H^/R2xXHZ_Fd_wa,2y -@|\;޻ Ry&ٜ"ų*ŭ]{.?NC{aZ=˵Zڒgmm; nk/%}ZQ1GTQ+$?PVɳH)`]IWS>?*YR,pJwWSb=6y6&(/+t7cѩ-$$;8" s> eG078:#C`hL{&ե,GlvriaL+9|mEҨ>X+"XkdڡN]TbNY(>tY"'rUhp 7Kic5kKVOfKj|#4v-u_t3 uۤFi Xj:WSY>0'?X$uHH̩n縜m$ytb6bZD~3Hlq Q&㪢~UDm= Bh*[/v%{/n YWo\&Z¾kq2#\ V^3N[J {͎3a흯 fP72_@"/-M(lՂ,)L/"ESl2w&E* "xsUG='ǻzιS%| Ij(5򈍵} ~cZj>)|I:)E|JL?GPE Ee1IyeP9& R}mًsn(nA36ECQ~,98L5g`!{FxgRM[YtҪmZ΄k?UANXk`E-PE݋Ժ:;3G%p JrJY%P P1XNI1HN.A~!I 5e0hod(UPe( 绶b.E Z,NR Gwc/팜lI(Kb }[p6"; i X|;}x/.v@ғ+Ldbe*;}h"I -yF-Z^SFuS&/Wn(xRKx~wC!W-59(l/,_Ɗ>*xZYv|4CeCLʃ&o`H{wf2I;? fJYMd``G*U 'b 8e3 E9HSdwr#\ַu^]W6Ta 1^Ry^KcQ+3]}Ozf=Sdrn0Hz}zFduFk4MܯBC5:B:.bn=*ճKu?qqp?\*sCwi]F0pCTmݦURX_uW_eָy S@ɀq]`K:ANhTՇXX ࣍3H,m>2Y -]x`̩WXyԤ@g8zpvn ^2EAҹA'v.x7,t+ր8fK_՜J}S$0)nqIAd=/YMyxga]x0TA*S'ܑU*h>uѰ};W}[PsFeAd#Ƀ9*h{ h(Kx>*Sֺ3Ib3ճ( G&?LE3 75$PEn41]*D].^D6|Z{Ny7]@.G'WĊ}Rk5j sRėhmVMhwT:%?3UĘCnTl%ol:uơ8A~\TJ,MR/Q/ާ Ӹ=oyqV])y zt|0;b5 ߤ-ᢹ@ԁq[qIpNfkI)yŢ\ަ"9]P{= A;STcx8tbcqHSO,-o[ُXkY¯7 ްfڰ6u0 F98xi%AbBD{k^l VJgjR\YN|3E?1՝Wfʱvq?Ҧ D↜2lx qm[䧕v| 5Xl1W3؆Z(_ki)̔0t*2IyyL˦ U9qL5Fc})K0xn-4:4SAAW4ht5hս|Y Nl_ 6xx:2Q<#XLP4b`^˧K%i[J OLbc6l #'VhCu"*BqDOPwVxLdSstY|E zcɹD~D*=3oE_ Xk7 7 REoa6(Jhsiz_45ORZ !+> -6{aq)r.ϞSȖ]$be4KJ]1>(~Jc\ScFFd@""F0IɃ-j>JqыI [tX+3uGsO/6h3x+24|M{+36pf ]IsE6|"/ S9 F}M!-yα[I亩3HT <:I}J9%@Ƿ0^5'"2=4y!N:Z5ZH60:Q^ALM)fb@z`^[xٔ{b3&HYQ)cEoAH?=JNiZ;tA´Eo .6bFʢ+&e~,Xg"j ՘j̢VoؒIg^ʂ+20!,8a%[VxF2Sz6]W~|֪m4%ԍď!Rlߠ6BK`Hz*VU HbGv?gr'qs0Hcّy:ƙƍQF79.uuBhQdϴ(*>P_rif'iD>a;)xXYZB*_H&YՒ pa˻dR/ F-M⇎S9-jƂTKw,'t4Q-*A3䧭h(9Kq\i9Oa/ςX&b<ƈ>^8ԜDZߨzy<s^E//$!Zw$ > ġZVsA4}$s)SjSr{S^)BVu8e&{o8HR&sJ997)ߑSff`s"st'\%i=qek*uOE2ͣdH ^D";R.A&pɌ0?KhzGB ab#rv4 Ye63j^%? jەu"y16IùI)s."<2Y梩(h$5iXc/E52}t[D.z|D.*hkK=;)"٩[)nQкgByTcE pc8wF14=)=fJ2"(=sڧz|w46Fqdlq̶u:bnl̔#^5*{~ɪ<m`l'vEb1H}-lzl!6c~0+"6J.Cx"q4hh͇,(T XlmFdG$p.gEވ\h$H62CnC\jDpCkIJӢd3j4x)ѝk4i4GS|qy\5'^4?u~2~5Bn 1aiR;ښZg4K5uCMJZ6j]؃7CӤD(̐E@y^'I%5,+#eMKq1(̽-&uۖ`-bVs1L*C561 e@\̀RlL#SC `)+B|JӘЫ7w~:<NJi]a hZ_BIqGCJUk Gg>ّ{w 3P;v,x>Ті++_YY{c&lq/-tyZxcm^IHc=-fa8"\lco?c-Ua5S_j &\`٬:Czv'Du>롢6|(/_ViWYq1b\{C˱ 4G5)\ca=Q $ s1[ݬ뛄fǻ ?hMӡӑO<2p_˻ԭüJSE-O,Ĝ{|,a 9.b X%>XLj#Jz?v\J5X{<*X[X{\{R{3N >oO1=֦Ti~be ;Ԯr/dMGFM=xXLz%D&B=!B "(A0'WDЌyock\,cW3>`.ZY2Oz%.kSw+K*Yw4NʷjQx ]&%4⥗TVLWPnW5*.<{l>ͣp [Jp[CQf1Q/bg-l]l wFk`uQQ,|7z]cK#%H!`duHۗ6DR" `&1ZRI4=8É@U^)ɋVyq\Dv+`MaaӭlnQl~On56?*m1&/*+Ԡd~>:ܴi2yMKSO\5RKϑ.s *sAp5>ϱ6~*7J9l705!̣7LʸegVc;h`e`!,zpG=$-K$ԕJ_#AzFUA:JNkpo)9 vM0KJ#;4K %נ?K[ɶw0GEv:mփ*?ǺʏZf)͊;i[ؐ7%>w}_6nymz,tHAUOME+}1eJmW_XRFviCDBtlY7CӘt^/FHoxdy-;Xx@ XȤ@`:Ӝ\uLUFY&Z{--'adL|0hecfx_ƑJnss㘚O.t0/F~5 .?&S՝ʔ$&}e|g0qPgֿUbsOcӧA#x/ɲ Ioh:A>!S O܇a(zBfB_9^7xVثV,Dq=&% OXկAp|,k~n溺AJ`/\zL$VzcgE\(L D#E0jn[qCA.'(oNuS=B 7Q%M1s(41Lb 3j+ [% fy\BWVS@3G1KD_Lraz[p []ٌ֓bI0!\^_Of4vGh&H41Cu`Dnse3ob mR Fǿ#ZUm'ĞGN ? K8:-]LJnF<GMc!ʸck\MRE ;|#8sw@XHbJ-G׋/wAKaW~ mF4ޜ ERPGf$r!Jnz<2غB >I֥N!ڹD+1T#p1p[ngኼbz,l JRL%}BQ5v}81\c˭4r\Y^Xt+["eǃ4q3+k,}.kku7HY_ wחS5Ԟlu7|P‘ӻr 3-Ka.{B2V*kAer |.6>W?xL]a4U"!QG/Z =ѺO_Sɻ_Ӿ^Z-^aqbivUQ./`cKK8TztWL=P0Ⱦ"K ښ8!.ߴFI]7`5"t#[c"Yu~e&TݭQ b Υhi|T2PI6yXxrfKqb }Q'E2Ka=q74\Od5q޶yUM\c|^̴r=.Y!$}HI ߟ>S L`::Gou9ף{yNi@t@+?&^y'Wj*zX6ӛ~`ES[/~Մɼ =LK竧}:bf\KSK@@vDžBC[0s;v9N4|K9+@+~KxgF[{[TRRzweZa|$Z*Vf8/`ŋ_bLkaf4'uP#_b=m\HC"'=Dd+aZ@]h)?d|@2TP^~ .Ǎft\ф*`aA˾/~pэ;K-0M,ǂZ0ʠn /&u kmTvc#F5 j @DjV/Y>Zv q؁'\`47|QU;ya%:G|!:}ʰƯit-rt4;rt1:zXh':{sUt+XtEGh#-= s{GG/mN=wFGN2,:z3MtއcQ4uitHVVEG[? FGn1,: BWqyV٘> J8t %-QZ_h(r԰(£w4`x_{F6=m>&m|?]G&bx4Ʌ£>G׍]D/}x K/)<]qD[/9Ƭ0cr}YMvWoQln&F'(KkƸN0g{ 0ܾ~֛4U!#+h i=/d-?Tւq[1qY.thacV8 ]K l#LeC7H`3OPp04Q !j!E-A`Y'.fFBƐ!{>}8/hF }](&[JrrRX#E.} =~24hyH# ~֬ؖG񣚕YqVt}dW<ςpƀ}G5*`-ty-Bk&Bk Φ-ܹMm`nOZMޭ=jwjuO90=FSMFj͆ w;J:9tsSnҹ#ȏmcS P{| ՗UԕveۡO1scnrG$V<ߓe#npSI.22Ho4e@EʀFx0 P:Xio T=03م N8 =/N8\4ᬏޤH5o)d#>y8Tù׎N0ql6j Irx܍ʐ1i٘0dV<< P%4V!.;E i;< ɓC^l-7>{22K:^ -A̦뇀z2]ՠ߫~鷃 R#H |7`T–dcCQMqNi%[I^HVh掖:ܵf9āF4nӣ“u{iY3n +Ndup;m]e_o21atKV ;⋳r;_F>@+xn#.?hDXIxGHnK)oK}l(ЉMg0E\/v/5{ClZ'>" =Q%Cu MԯԭhQ=OYePTj3c*uU{? kVpKFP) 7#>)kQ'M}̪GSRsWʭѾ)њPt|<5֜*~qndǹUjD#rmpilt=k4  Zkj!Y<6Xı2`PTB枕|& rgSJ /D˧5vVg Oǿj[kJ[V띰Je !>W3A-ÄYGfڴ[Mn|W%hoJQ2唛Za@Hnuk%8L/+%5L٘l]\^qe5 0 )7 8ÇT='E# (5jIŠ1`T6|unyHsEiPe3ʞ ,^fɬy#m>6ycNok5  c,/W^ƥ0 gZ\VwFj19Y4+<8q +e\/uK\ϕV!ȴaKr0yڧ" \AA[&q`-D(|a9&p]zӦ9?Ҵ AP7T1l0h ~V<~KUM7H#t -VSx4!ijO4NI5heQP 5vn1 I5FD D :T bIN\ "`t/.* ˵q_=F (a:@<˥@BII?"ҿ6Ij3eB4" 5jxWbKPhY%SR-A)M:뎭Fq?8V`IC#zW4kr41/ߝTwT *g,d-=u*(0rPz?XY@)BU '0vjfx=|}TחLblFK^eRg7en_=E-p xC,nWD\ ODz?lrd5D x7U97:YB%euVZHEO\B'a yMM%A/ SH͐b5b)44}i uQ0q$QA i?O&߰|l^ e )]4%E-¥ε~g/"un[Qq)Mε) #: O;'K;zVP*EY xvs^➌a8 Ygf4|\]"b(1 jNM,gzй3]Fյ$g wvpG<`h$[$%d9)ƶ)ukdz Sɐ[/ϙ!&\&@k+KH~ʏTöqX?<'Bхƍ~f2_1,.@oPO'*L"8#K~ -KyF>5ʸXeܢ ȇZG5X7;DYb0x:GlqhųJg'Fw":(# YG݂%e4>4Z.}>#*IۡxJ{7uӻPi\m0ZpXGP͡:00,EJ2f<{T`9$[ʲ ?ʦ٣ jTBR+SHP!~i8S >Z  ŀuT$'Ѕ# dKJ7a8#1B!ha`T`q:%Ѓų~Dfvy/WGʌu01Gjgt&0TBv{Z>.`K|ܛL]1ߊkX,W]'PH ֡԰U=_gKcXS`w}!mPm ஢a+V\d1PV/FX6r hx \XwZC6;UnS=H @bdхPу1+ʟH^dW2pc ֿy5J\?քRwb8W;g{֐&dws 뚄sh.+}}wB}Ѭ*\!!%;RМ sv^^=@I}rT7T}{idL @T6Y XLũ|r3$r Sp L9r0Ї+˦ױ˞PZ}nՈ[ުL$vbP Yڇ?}>4gQ $I5{>ь䍷0&=r KK>netlh"4ďvvKpOZ.xD*-gDՄ D"͖jϐz`8~htަU[eaj*Apn*]M$+dađX9`)Hި ,6.5zmQV.u'-d d40I|YQ>>!/G.$7Wʒ 0/ o{+EVQnp}SpR^3h|p>OzRQ!r(3QQ?ء \;܁Tb';$yH*JC*/9篃P"r);j,E\jggY {o5. {6BON \l $~T\jڞXXԱ Z$Y:n.yIz_ =[`fGy(\sH Ӱ=E-:&pd t ևB_ѿ j61;/:+De@[ J@ʭeT+fZ`gX-,k 8TQǏ"Ez";_hDvІ~EaNmίE1bd'#;󃑝NȚ0}Hg!a;_ q~y1a݉хq>:R0]/%s8xTA)t &Sjjv0U RL/G \0N?q f7XM¸|y_A׀!ZwS䯃n[3QG!1SSHsV_0+L sx\3: *:S bsu~L2Wx;EG5DP^R>.p1YB^jZFχ:z\^EVNZq&@È`ZO| ƻwskq rr(SF̣ٺ4yȎAtQ$F$2d%;3#o V;Vcu)iCi1ȧC QVod`¹t{c@6C~vZ W&|_R6Ű0{fbW&Ia3sZHC>q4exYU8 ZXQ)OhY.I_SsdߐXҐZz8("$BWiqQxTRNj4rhOli ^{#r;ho1(i4+E? /AqKM==]i%hd0"xAc95{';Dd{ߒ pmW hBE%)Ť"Q.QT$I6%(@x$,@ż@QiAf1ɰB[-EjK-XQIPXDMKmBν3Hmedq9;;}1nҋ9A>3Y(vy^Ja9' h_Ͻ}"ǎ܏,Rj8/9Xi-&_¼%EOBI&zb+a(ZBGҖ w>),x9Y(gI0> jQ6-GiqܵOsG~Dp_) -2ʘg\`JG:)<+} -IKr(&lk9ӑMSMb=UEFYz59حƱՙz&[YhO}/$cBS_ul,Kumg-SӒE|2Vi j\~6Vv!'=yyl<Iwr.t#/zm5",򉾞MQm\Gi|gVpsXʙJ11Ċj5tTMK*-J ;'6ۛRXg")W,FHӻ4%>{1G.>Fs=.<?kUZuxC~Sm{]9&["ARFClyv>drYl̷u$R?x9N)jQJ `b -EXw0;%i$ؕRNbu\ ԃعxТqzLG@>9ntU.rS Q}g quJW9cVj#gd*XۻJ5Aę>섁_`.;;7Z1$JXBظyu29c6F.dل8h=]iY]^1Q)k#'fꩭxY~NP*P,,aǶ"y@J ? OhI^8Z*(Mb0 8BOz- jN9W;H[+,ܴ$p|g[-{#a62H7nl7muo#KeQ\oY\8V$@EH,P%E-kͦYptmE` ز,Î.& لwqXD;udKJǗ2BAVK)A<휭V\]f + &yƠSܘi|䖳E~ ӊvɩHգ*oh,`gF8U#hnv10tt?#J='PZ 7M؃ozP<彥dםڶqx"WYAZvd+ {Xpz @rf7x[fgl@Zi %8L=X^$ma%@|=X{caJĢjU<RT.q`ɻB{Rx7h:L 74W=WbT-,]vAwfzgM@6ruZĪ+iZS̓Cq4ktl^~?Yz1l#_[ʃG:KKiUoŒYk-Xe'Sď,!bNŮzo4_#ϋOUy ɓ3%bK!2{BK\(YXQм|XWGPnIUvD#а.:URC}U,A<´x| - bVZQ4;h47ttgʴli-=Cj{WrZMנpX}J%{ښ)KP}LhyLð<-u$*r7#dϛ 2}C*#g#}3u 7yNu!$?(ۡ,BYC?\XHEҤAq,A7Hѓࢤ.-/@pfolG!J3HDRvɻ `"M_U1P *5t_䭭`*;\BU>|H+!㈅Xp\Ϯ<<{NCUybFd_&1jcmWt_M(q ;P5w_[5Z _T'+dI+S<[ޏ x*CXJ,a'(Z[~K caT@%O;jKyBga!''r>fI&)Ǯpt+ .&e?F_84~8r @ A+NKfWu\1¥! ,Oksi"< |ib&Ws9_J&s> i\,Kz΋ӳIN-}w:7p b8%5t!cyWYҙVDz痆X~!g ȯO=DS?vl9VmQ(@C-ыiRG94]*lA2rdgTN=}1:Mn/ 8DcT,OE=`fC*&H/N uB3;|n wNRt]y{RMSk6̺l6^6LҰ Sq Xy[) I.pI0/|?[6~ڧoӤ&W8$cV^Zyk5:30dطdHST9C?B3s3ϱ;J—ŶSPyʧSul1q38S-jiN3U)Ų>$KKJzKAqaP1 0UP2ZDVҧykMc^bmqŮQ4e"ećbXB{c=""db<(|\ԔGFzqZ*s°'xBwV;~E'ߡlDʨVZlm :4] vQﺁ+^ #w4֤L@h6I>hޖG6?uSs ;aTMlr(\m(> 7HV<":Gvi%at&9 ^%绫:\P[ꭕޚǛL-g]:ba"z+3v;VG+TIj t=ץ{_F&z.h{k_Uz:s,ixMՕ~3Xuc.bx8fʮM^]ETMT@ݵ)//T5AT)j͖Y^Jd'"fuuwMjstm-~8D]seK+Bzn[Ț|PP{}W[jaurt+9aYftK7_IP>H:lt0_W~I;ؗԼ9c+0)۶_ɨzlk@dK>^$Vm. %-J&4LNȔhqΈAlo~˽@Mmto=0ɇmlo=nW/g=DtUjn qCs'dL6qzd ?%kl?Fh~[*?@7%)-#ǭX),%A*(]]ݏAgx3̀K&9̊z뙟{&܅Xْ2hy'䫿4(93֡ voHFks7W.93GE+_96|1ķ;m{wx7ZL#1:ULSd3"C%<@ⴑbTV&*yfϐ>㘖L<'T G}qX›<@XKъm5rQrYWϺZƉ}gM Lp$.ZJ]RvA XtK݆G9jt5Vq!jyF ,c>YV[ỒXavZ$&TtDL3FA=ҟ|8cfȉ]0Z)=?[ 8cSZM@P/!o/n+ -mplW0:/1m0rB)ty,*K4shϙI9sP% :ͼ\p9Tp@IR6ŭM[q`v^ X!yd7K]]HpDVCYh3lOɈw䑷1ȖNɈ% YE4/s8!Y{(@HOxRryN\8-M|'ޖO<2ڥ1+kR1I<|,dEMghZ#۠D.`5'ѽ2WYcS7+il<,_kBٮ)[Fy1rB=U[Õ.۵>($vS-&y΋w)D-qaao*0Ot j,I>y8iGD\=󔃉VyZ;Bz3)._8f؟x}\G(bN--gvuOWW_3]cdJmRMʙkWe,ŝ':r9}A/VsO M50Dt]`}WA?eއWU~ȫU)'ll76Ǜ>]4[_nlh|[tֶ mnPSjʼn/D?;"'qZ;?.Ug0.޳a Qcf4gxh=׶ ~4@ͱ|owwmej7 ΊHvU1*FDL|$wM=V.:OV9"rX"}G52orz%_TNx'ݗANtwSw+G| Q:hCM0b ݯZǩ? ; ^:[%^(kaa+0TKE 4%8`wnLDk.$$ɛxKI2jOtܒ :+UM3 g& M08x ەM{$tݫm]2G_!'?W< /?Ͷ[JDKwbk=׍Lz%m VlkhiV6XZl&VL RB0M9 ڄ=he,-H_|v : g֐Ɋ} ;9x!1F+kAVLkR=dfIơDٜHtR`z rcRLu!P\NGk3 'ő8J_>l%٧i+6_Xfjf#YfMq0m2]’;ΨģQ 2!4m}21xb>g7*%6Qbq$@J"v `֡ a=  C(擦"}/?J20I,MbDg/@$ Ů8$ؠCm7Ic\V + þtCn:tIr:<#b"z-e^.mۗ\ ̀o @UɊ]8,Ӷo7'Гrq-jKu$AQg'W.C w;ɟ{{ M,gKkؙ0H%]kyG;2G3Rrv^>UzOSvxU jcYc&O\E\,loas7S .MMrrKa6f[˦C91c;/Gm Mbrl ,4X.ėZx/ Vk;iQي;ЉbTn܌I }|Ɇږ=ONE}˻q Vicr޳&ΒNp~~kݢdin.; 0^Hc ixG`,|˃Jc/4xxc8n-%szAߌߠM;ڴ`62Qq~pSyl|̂-<1Pya& 7p[nvG>~{=NuH)0d,\(dሚi,IgI"qTV8xpx7[¬ZEEr6{s94KI_Eq(%U pa mEGH9>ZՇ|KP_/p>.] fZ(f__+(N\~} yAӌSI&VxB&88N8@@FtRMl&hX8g߂>)x&v< 7p5\%"IpP _ow~v~>?" r=]\H\|t "wEVGL4i] r/ʑzt(KŞ<jovYF˝_xҮIK.l5,漀ֱta[mydJ >J#4Vv!+4%`reA,}5i, MM]} .d}`pDY=*6A}d7f;~ ~Ga nwM&e7B;xPb00CJayIP\!гG}UsVCկ3V'[ӑ;"tnZ-{^ý9Dl0Ӧm3\:ᔡv)+-Lsl3G\3A[#no|E H|_h|Gv{:Ց&@Vɾ_;`ΤpqR\"|N8{1F%&-,vrbdzt97˫l^*|Q hLq-rxQ9q_87 EBoN Ǯ|}b_եNIEIEU01R@- *ɏ`Y Anj1]!MrgxGۚH'ÑH[g:2nKvq˫Dd }\O PW 7peb c䡇P8*γt=8USV6k0᎞RԖ6M 0/4X9_ 8Qh+Rz!oCJW¯~/ex &Cw(Rd@ ן)-V[O5[9,1{5y^;*h`):6N`Yhsu@& @#W#{O/dgs쌓 fk3TUj~tPv*TRsCyw{ \&/f:ȗcQ 3Si z=;4-AWTuH IvPR(i/C!p!u97GvQ {ֱUTXxPo놅w?oDM(RC`;'!<{vcLi 2^ x8YHC{rNC9vt y1Em_fu5W})~=7NĻ2")WYCz`geIK°Ֆ-.OKu9 V9!6ITW8 Ʃ\$+ [BVfIH<]PwD=~Cthj[QW`~G) 950&@_ru;?:UZKyˆ1Shͥ5k0|:u׋A!v:͛{&o>Fpuss(Nv,jvꋦp{8e|w]%|D̞^\04W dkҺ|(JO0{6  qt5QT@C鞤WW+TVAΧ) ybo93;t;VA t_ 1VT4>h>8 `.-k"5U+Cv3Mr&oՋ N[+~$T[Qb^LD.\4PB|^@̨[4(ꎧru<铍ru<ǔ %ug+PL㒣xMH;rNy)gҜe6kVBqBqB1nw/\{+`q/繪T7 (;>y EI/D2Ή]ᗆ"~OmHNq )5@MKOyAN j" >j8BMG knxla](&JSEFQ8F"WQdcbvÞ+4,Çv3YVfY8nd10YD& .*`j,|,&#j5 4Ldžɢ7Ŧ[,,ӌɢ0Y&{ ŝdQ Ey+E=ŠhB7YM&wuEnx]7YT,v3YL>(\&LqVAB C,M.Sa|/&dq",` ,6&"Ġ<); K,Q8O b\w Cⶆ͂,vY8S6l?@o,ve6bexewo6'lPmna؇8@넣bfq,^l-EYyyK.fQ]6~u܏0p(YGh=@Qj8N>l/qcŨap xlXE(aWb [mbRTO B\`qdS1@U{ Bn`ׇyRzW1ńqbJDO4KY o[/xR?}AL)UxSL6ML' T㗐B'gwTdk5 F MqoMNjVjRDVJY{X"s Ƶ(?Sd%`':YVzٹOvrR<'Ԛq)Gnt^."бV =8֢lewjNIGAC 4,tY1d+3< +eP'pBh^!| LR!\n WBAh?:l"\ՄF6qk/@#\71_ru#E[n,HzK+\V1M]^:E GvY#U|3Y2+{;VBy#bzyzVQ*N+(sX ;ԍuX%|]IT%Evٯto k=Й,W84|j Y:+ -Zff 74 A_`;"N1S]폋}O`pE:ɽS(zh! <as=ug Wשaލ{ɪmx"=]OUkNýxT{poEg'l*K\Nn!Zaf36|k\MEm__EZOAaG94]C盏Ck 3Bpkք ^-##lJG6UCJCvV e˄׽3cLUr3،}ܨA~^~:/N/4!1yO={nr GtzqCurq8|B)ԚM{H,)&@UhmB I dvG:HÍڤۅX& "9h$rYzwWF0F=#hs58,HxYhxJC?Ъ^~}&\աk0VE+97gt">yhb4]j] =q37%3J볨y|Ww7w[u9ͻÍЫ0:U!&[hJִ , `lrG}R v@mf3 b`u'xb", 6uP.7X{ԃ.[P>g&Ey -lX }|vǢZzx{2Ʒ*sIpV/(gd8\Y3W<=a˘qǺCC )9Oag&&;+-W3) ޟŸ2rf34&qV95q(rn d0ƷS WCamWX/PAeٺx~nG8"MgE8 +S“Z(ӄakl$.)(>#>{5vw絃|'ʔ]IK\*ōRtTɷ+I t9 r N9|rA9,/ͤs+"]MAdWU3AӞA؄ h{5-u0V  ]O*[:(Խ#-N-Z|폌ԇhkbky_|<I Yco&oYN0xJϥ \ ofǖp;q~29L_ '>S<.it<)Stן0]?/K(HIh}#<Y}.J"Շ-1/:5~/E{?xç~xci2nr{hbq1' pQ 8P7 10dWe|IvZd sL }0wP_{tJAfʼ= 5+IU 0C:[I?+=Mx@B |Iv6Q}Vp}3qd(s$~۩ M~Cy!oy(- !o?@ޯfF`B{n*w~凼B["PReOE#55QkiޖPk&geLawߠ> t4q:m oҴH]MbqDTJNjQn>?逬@R|!J-؀S(ZFoWDOj%3 Z~ѲrZ-h=LulhhBr'6qX\C,&D˝-:!CN4E1PFQ}Sl&X]hR!thce3xeET y_(CfV-H/nyțӻ z]K~lhG -c SꉺKG в d---wk&oݩ/4{O)3ѲV"Rzvk@F=s(ZFroZ^F4l`%˝uB& 6Vph'|Xo8#1,%8/΋p8qFccnIΨw^B=bl˴cB/C/9W(~B TDj`f1hxpDG`0<D1"ߣ z,1Sp?f^.EdRO:@ @ou@7qi0h/|;Z_},ɼK_>Ez6 0Kn ƌuþ_'XwF8,`t'NFPn3Q `eVT0 Ay6i=3y}B0zy o7WccR_ߡ`ti)s b>H^FALףF~TdBo 4mE5cr'^&^wDUW_z9'ϒ4*:zU1[P'`"G> 2Bo*~羰cl6Fp᳽]M 7.h]y:y4^pj7%ɀKcGvsCHlx]` $ \td28mԚYtֵhiD7tsטPV k<",34gSGꋆGnШ#a:ԉstfRzqBp+ܶVxڎ:GģvJg,eԤG z4qM[&gc ԖpgYNL@GhsgB}gٜvL gB lLO=H/ )m˩gIg lƢ憡gL5`;13MhFx6g3LHRf3.jm\}vq@cYdB蔅8 9Zvi }HoB3M'EB(v9/z|uN hv6_As nRnqw (` /R%uBU2\q x.# [9!pN;'59 $9:'ZM 6sI0f7;'\` pNp tɜ:'4{9ab wJ/ziA$՜oB =Q1q_D,؉Vӯ8_}b?N8I,:TԔTtF"\NRׄ*Na $p!zNr z"4)oM_1I:T 6~MDOSTZm O zG#\{6= 老ݭXE >A a jj1!jmҖCKZ~{">[. v>?yEw>~7 0tC6BL uLNj选Ύ ՚-=u:bc?gAiX:9F;.{E$ #v墙w?`Lk)ϜW^a0U첍ݿCatZ#dy07xRZY@`;w.š18$.RKbBqsǩFT z\H?Lg`ۘo!.iQ>T+VQQ֐!D[mؕ(mqv>ipb1tUzLƭY@KySF 3iYFrB q*C gN@OAaqAa1lŮ62(2#gb2K2R88 A3*1Q8aS<R)H>ۣC70s& L:fo< A5baU ,3x=o.0F4x GfC(R,dyP? t\oyYL)*u:qKL@hvx2ܟdp#e>^/aSpZmEY[Ѓ") ~; D;=%*^y"i-=2}-qh1CKgo/9t5w -rWwJ>3Vr{ԊJfW͈W{b_/F"*g-9)&{wJqZ)ݣ&G|u$:XqS#j [Xq;Z*DTtT|N]y4Jɍ(/l"վHp־厤t9p;r TC}|aX jO|C H?7XtE/$~#IQm5pQU3\TT,S*{1,1tQbr7ŷ{6auZwZZ5 3]2LDt9;/bϟs<>9@ V 060G)q9x#>,bv)7o cr ZK6%u@9K&OlgOƧ`*}'f *>;hBQQR@ {_хʎ^!cB{K2 3q)[H._@5xb60`&6@ِZ[A[ r_4ޯð,kڹ@ٱ@ЈabVQAM:%FIATusQd0} |?p~p{~ ̠<9:J)LqKa؃s* T/ Zw#:Ahyvw|hS3h4[{ 2?s>,]EqD FSh0): Kgm 5|T<ީ%u >/&bY PsR[]4n p0bƉSϫ`R`-d)pϨ$?^|p7z%tcjD7 xHI]/4  G…OZND8 /EBy`=*LcPa/A)mЮ| (/@A(pPx*G@L ́VB P$k77p8XEW Bb$PH@!fϧ㢰 !PQ`!d@B&ݍ3U,JB17bY}+}5u, XȘU>3mG|( _,->PNo7d·F^ؙ@9዆H|A<[T| 'X/Jrj}-ΎtlTg|1ϐ9 e|]{"(0w"4q!F7yGK[\UcfƝ5Pxꃅ&N⟃L= 5q 9jP! ;U4~86#ys"@(k0A Bur9zY4ł|t$:@%<jJvb;{@IblMK̴pۅ\*Dwo1<3 uAH"΋ m51/l<#>0ZסZfGsޑF&U9<4&0 eSi0W>O "WHbz{G33y ߆3\bAcR %;y}v⍖(24) &P[&NӨ|7t DThq\M: !2 dò2Z:`}`3UY?bWi&PM,hנF"'W"hڣ#PC-@X,P5 t yG&ГcS9P7QX>=JV%},>5MGY!)^<o`E AFa5(Luj4<ӥθ<Th$Z*Gk&.NX b b `Iø/JgnpL ܠ^JDGr8k2?cSm#H_HC9 o 'ȶSteq6"$(p$?tl݉@L PwjMLZ!Imgrnw Fxdv7/=$Ul[i\oBu+BfW7d'-r`N&4V/i ˪6rk-\BB-tvFH%)hg[T f=`嫝npI#=FwJA(弃\o0*ĀK+ܠ 2Co['1JwBDٕJyb3HW;7 +&sy:0D IBF2?{/PH%?T'k%J]sX)0`giAJkr (YHLuDr%Kw? _]0JQTww0aJS>&M._ϐ*!b'77HPTQ7FwPfs,';ɋixO)'eFLғy_w&:]ZAz>[tPJ aCrw#%, NyQe{A8Zb)PZm}ʪ+O/@.@6q7 #:b!c-Ѷcua v3`mb"8Ɇ o ؇QflBE{krj%^JPU䦗b/'[Q~m!&_Da;f^\s*'|!1ykm+ :* (ب+^7ӇZz9a@$ɢ # !?5׈}q20KoFnZ! Ro!91'OXl Og/*Bvmk _tE1*LD~WXXUSWXv/m8"nTjv}[Ntՠ„q8i,Zۆj] o>0m?dKlNhx,+.vWg[x87_Xmv/$~$xw7.Y^;blfr 3 7fuUۖD^QÙ'Wt5'p+7=O+ O-zdyz[Q$ C-`ĕ9 }Ef8CGi+!ʘ>e$B[UEK,F!޻ m'IC*lri4U괝yBٓ/̶ TX|j4!8/\'Bw*\6(E[15*.$XP ψLlZʝ"cę>%vT pz4Xn,&?|A&@"QEk U^,egq%Oʺӣw3鴱9fr(I6}+-?U~OCO\U#7w; xR4ya,+$WZaYw=Q6*p8 n%P !#fư.}$c_^.t>W9>W;u~#;D~[[_2>%9Gr\ V-}3Kxx%0p.X3+~#G㷒S¯4=4z>@$3*QQA3<Cs~?.85AA3ߡڈvEx, t/4KyńWP!ᑔMbzQYl6vAtp }ȝ6 x 8̜-gϳ0LIJ+[$6$F   .n1ȥIXU[v':ȑ'Q2ϜdHjxaMoAEDN^W-GjY 6Yx `znimR==L\Yd8K^@! %JA·s{y,ԅmĕ=T|!n &f5I.[IaEHT dBO`ۏm/!c LA7i؃i׉H_h;%A7t7i'6PLo|97aJ2Ȓ[>b&dg߉A5K#J ,5*%8'D ~գ7ɟSƎAO16lvC&͇ %UȘS*$ux+ƒxo%XWEoA3 ӊ7Fwʿ9STDVdҳˋYTU"Ǜ0%[I؇VL;yĎrvj}Ct֧G;uY֛a|$ \o P[K]R7F4à+eۙ>7B@5̰jdӮ)^b9hhPzeb5c.͠OetoO3\eEK .E  aLE;w&D˲X)^4,=5VBX&hhotF$W*/4CgoO3 2g],k+#5(.p٬l&By-a~[ȣ 6cװ ׶LڸԽA΋{"%w I -FC|?g1yA% ǍPHR R $|8nʐ |B* EfPÑϠj2.``aIڋ˞t y=@do͉ߔ7bQ#"_7DVLWh*}ioFa}j@rN1Ρ[rG zG2~Y$i7rT36k!|ݗ_Ij`Ā6rhQJ=L] 6! Pp<(4tnDA"c^*Jh$-$x!$h"g NryZ x>{)R :;vS'f?-FRUqwYJ2$I <`Yfs% t>[ӡ%ό0 #] d$5˙LhiBt^k-?. ?-;j F5[1`t9U JnjU '1gŀTߌ(NjtwƔѝq;MbZQ9z Td)Y]U-pU@g>sz8DwBfiԘg oK%&+}KS;V{ZJN7晁5z/J{19)⩉IXB ij,=n`1RER@C&1 [Y *Jl,6Qt%G Gebp #9#' f;(Hq湉$ޫKHAYé  >E{|o8xa/H7f IMQ[IWshuSя*oPZZA)pe7QoДE5\V n0|Js`ӋH"b_s).$q\(z 4u8!ԙTcB5*%,#"lB jDFI GPʥ|11i^1n}XYD>d`Ksneo1cdTXo1yَVXLJ^K,\LU ?X8g,- nh>.H+c$0?i } !46Ebl3d78Lł8z>RQhc8Y!{m 7;ZABc[l>?/ϸT,mBRUO1ZVwzAXW*ڏw!C:iA$X v ] <]Q%vIxFk((O"=/.IbvDi ; Y־}'֋uZ.6bKWGXߝK|Vdߡލv'>uEqj>2u4(t~8B'E{|(4O/vihGhY4eNG;Ȋt0Jd"l.>s x-EØJ ,a;:e| L=Ϫ@Pv5%tmUC^Lz\ pUҞ Cr裘X*TJ߉M]YK[5pKCYi4cF ymcN2d,i(Zx}ub,J\Pj RO"l0*R\I@ )gɾ6ǯ-~=0kFƄYmI>Pg[Aߎ֏]9T31ai>Mn n ?` 46\p/L?M.RBn3.5oa11j^AD~H}STޢlpᷕ9ȁ vfĠaڜ6td:|5ΗLG6nإ4erPϜ'թJ~~ ꭚS[ODޢ:՛e':[b!-q:շ~ovħڶw25R y[̯ G'0`W=EffMGf)u|E;89~Wlݽ?]/tolM=3m//>9,/O,m0q[~ r<[ϤO(<4Anf㻇Vh]US],}0(jN JgM<#/'@Oʵ- ipٴrc:4bV8s:hdžwv?>3) "6y$'&qօ,+ǝ*$vDof؄d0 蜕'3+X,$SBӆޣ3M4ysβB2}ѶxchQBWK ơn]EW x^^*aFX'M@/:MovrjYr[5J' H'}iحuk9ѮJA3!XCw_9\Փ͑8:q-5? I#,b()}#3?8NʵzRDc=BjdG}2z ֮ERvuh4/?NNp'kqy58s.`NbԉNrSD!D2, ' ֫' 9o;wj 2ᆼ#PXl8"k CS.oz[%FcT,5  mAG6^cUpZp2}C4e7VMmͼJ V'z#z-ڋjv7yF8+|tB8I׊I׊I1Enʋ#GSȏJՖj'(Vfev'GɟvlЏ0O*h6*q}|o#|` D`t#v`x.,RJ7 K1a(hx_G? zgT1X`N&1b;X+? [[nHg!MK1Fl7Ҍ孲rN 2`gֿFT6q}fax>0kY|jD!GrSL!W^S_nT5i(zn6rj9"{A0zA\\'Yй~`ߐ)" g>ι3TGpqYʽaX&фZp!y$5sD!= !/=&Ff+aNsBNrl>:зw\Llq4z9W|YGqV4M"W^]/J]fl1A\}yxBtsl n氊ΰkUV\UQh= VkկV@whX&AQ2߬8!seYp6^%ܽ]wT$6U^Ʉ(p#b6>IcvZc#pDB^)sBpǺh:Z*<_]fRя,RԇNf}d+{+Nm|'NMjg+QxJ={?H:X{6^jP9g!;cQ=`vTa!m/k.(O%2$P rCSNjO&/Χ~&-?Qj9LWNV**^ϛ &˦zâ\ rxiH%5scuNj52(X:[qO!y:t2& />Ta6Ù%\*BS:Hty,t9FE[`8u \y yOXc޵`|/-yN=.edZҜ^(S(Hqp/GO׿pw˽2tfՂa(y(j5_+TYg$Ǎ.f!K/;)_L!sIGUε_+f]ϧYt% byƎ@c*}=1w $xKZWD r:cf¢q(ZqoPՀ㷠OaDfAwqY=ֲ۽`h#Bt'+VBF9Br1V$Z<6_LJס7o2m -_'0/ڇloRт-$A?,bDtd n|MmȖjAvX /Dإ DɯTtJJp+#_ف9zH{g"%5-dr ƼRoM_ޯ%}yozj N*Qf'Ŗ/J=ʳ^;%R_)Sz4l=0 ?4YI Fš rdNR$Y16@cX✡iJu@ IBk£^? 1᪺9.7E.!taK{qzk jd5P[t>Mv+ZEq?::L=Q YCtSsWEmr@-_d1Yy}adfUEe}>'9wo B{!`VNBXnQŻ9|kvU5.Yt V?[ZއBR` fL} >qmKtYnQXMbwnQ`,嶤?Vx5hߑ  Wܛ_[ymU`V-.^[ v#up\rM@_~\%j^Xu@{*f V®RyA^ya>a+T!UYTV,V&٫kHI[A: 5XU\o͊B)+A:{_V+ jw?3QpXVhQmӻhhG5~k7WUy*9Aʖ"2 o𻜽9*&)vxGK .9E-3Am)aÍz#!D~2Pwq~a\(I"筈P{AMH2pLvw+hBV.VGZ+*P+b:i~/No0{. O`0ӓ4ӗ{v >۝OrއEKoo?X{^շ 'Cu#:>U7S攥0]ucϾB`pA9g젷5.;ЀbB^SPX6y6#1}{Bɬuq^ՠgL̯mtFH-1ۉR"nVw -z"HȐ,ƍ&W'Lܡg֎<S03`FA01Y 2zme`f/3F fth`LLP%k`&`ƌfq00c @1HW(`.6,^J -9V&wY faUi`qgw@)+'{yR8 3QzWhǼk [Wp7!:1S0clf:zUzw 1t00IsU"3f fXffhQk>!L50cDw 2I"筢`C3* |9: 6؛)ZtJUʤ4x"X8ٗ S/=j(Pj5s(qxY[&FUhU oRvF6=ۃw˽ ޜVaxc8я'H $ih+ޠ>7 6  o~a Yn3߼<*(Bh$6#ymn6WC5E{=5/#T{N|a\,djS^w~F]kx59` ?# `$|Ǭ̮BbݩÚhO74ޣ!  R8\^1ǏjDPBhuAU5`= eN3B&PvHfdX&bv2a,gҺ5P,s29 S*e>'5>2$6OFTJm bA>gBqK%Z88\[AYȩ`hI}V&RWUmG 2)c׶ dS=W*%;'_!}<&qm])w"R#r /@a8-iV\mo &A'MU< /=$4D(3vFSr\]-{}y,>"P_鷼MTޜONćW'Drl~Ő f+AOOz;|`cIAUo-41 +[Ei< A3{Jv5aJzީEn6nHq"RA8,3-̡TvNBΤfwgs'?V戉)E6c`an&e˩e sI xʬܺb\WkπCRLUTjCc~E֝h] Y- 6 `uںEa( rfQt\ș`(:V8>d}@ m_$P`n@/,8>)4tARxo~>#jf' (aOl^QXMO5i`<猢Є g3Zϑ923~$ۧŕzK= 7a(@ZЗECؗUT*WѥCC(T?Ko@  71~B?>N]Cni'8F8O~'$J{h1dTxQk0ZIaݨ/%"fWC,baMl)E }Z !7Iq]k\ !@EK=n(z00QN'@vSE]пi?1#RWKEs [j o+1ułEѺ~0i(X^_|A( +q) rw R-yEtk϶ז=anjy,`FC4y'(֙ ݗ7%!טQ5Јm'??LiJy\yU}Njv?PnP QY~r\EN^D4{+{Ξ+~+Aocأ>+A L9o": הzF4d€"xA_Y$+N"i{z˴lU#,8@ҵaP)&ًYКHA)\ѵ'>lө=[6.?E* Oh0s.iaۗ{/q(!2@p,ؾGjRA*m8U43Nr4WOjvT`/A$r5`ŊtrN2s$a -$z`Q 2`V3}WٌF:˻vrC!,ErWIX-/An9\Dn>H\m%er?w=&SiI}Ԟ-7b.t l.LuT:  asW2>n)"y_٥cs@_d΁{;]짖R`vQ?y,C@A;C 3},!çKS* $wLglrp3p?"qA]A=9tPԳPCgcXU ԱpΨN[Y8i~[݌@$]*ɨAqWs|Z6]3YyR5x^4k33龆df3\R@9[ ^!u_CkB%˥( I8dp$>`rh_? Ƌ(/2Y&?DyG}1^seB/CS!/ͳϨ5IRcJuhF4QSpԃԴwI]9z{ WΧ~ Wd5Gk$BzDž݈!/=`Pno g=`gq HO]˔2ͬNP "VXY\fhВdhɠ=jf=--Udvh/jh,t*`,SLÁw9܉KbGNV$VWH+ٌhiXޫzCKdq6mhBK!Z?R'EKf\ e9't% 1!$\`p  YфZ%3CKf-i0ԩ WՔẄ:5@&_}ICKfXՈ h"/tf__ٳCxh1t@xBcy;]Pbt]G/7T RR#-<̌=:ZKQ5I%1͕N[0B=wQh23#5ȝ@[a}|д9 h\ղ8at]2Ok ߁3[}VL joae.I༗#fu j#̑˃ AT=#=* g(j* .Q:ZtA)Q宒Ҝ!)O,ؚ0 <*A,uT{mOpSf;T~H\%Wfڷr[s :\dW @J@\RLn02h>[ٕfF]L^Y8ʶZ Rf 0JIJխ֎8W( v?M EIPC*"r8~6 TH{ 7P(MAh0i29?`p IMH*dT4DR#To,!)3(!)wbLGmpT/U7MQ/S=&0 /rҖlq\^7N& {y+/ `AC5u$֯+ "JstIQ.y=VQX=q*r cT.Ti\Hla-^H dk »hy+UL&Y0IPI]4"Wo; lAY'Yr?gf%2~[gk욨%+]5u v-=Ioq ~kaeddT,݊+#7~8A == LO.ľYPGEYobG@eXkQAly.+yTUmg; >2(Pg cNt ̗L,\.bh@4Ŝ7LnƤ(b^ gX{^k= r`('.I4lIL:L]:\h ~AnfUwJÍFpu,W3KQ~YB}-_\{IZWf'a$L1 C`RQ3vf^ŜV_EN'<јJT SiҴ.E~xΝ=peB9}oNX #HdLpqAFuԭu4*<8eLQV6٢O1mZMgM7+q$XI5tB82JPrPmp*FGNM..HXdvIr}޵{=AO"MeaCz=8-j-g? W^?!1KĐ;u#&ArZ5#4<Q-ƒ4u{I{2rg {"ĩNjr_Z8wb|ySěÛ|<4 olZX|d۞,Rx9`=vHh~rb`K;7:`A3iN2z6WAL_?~㻡ЖQyN:$l1ﱭqzě1{cz9b- ~)!ZJߨeoԂ"(Z8K8H8TxuBU#KY84>BHqwDdd:sW=%>ca[3ߠ6] $m.r0hbxR,1Sh ^)$P^)b=PAk5F41GE<VqBǬҮAqL8z,*0͈()zS~ddAgWupޤAxXIHID?I&T$!$9XKlq7(ZwH$:TxL$KQDQOfv. ڌ['V¿KN.f * qlS>\ 4/Sݫ|G9УNl9HEvptSxiG]GIerĦ|f /[fWzwg옾sKD7KNUxݿRx♎Uej||;r2n3B:,,p,XpXުS E_A\BIyCn^6tdh?ph ܤ^i ښQ'?0'+ξW3ӾML0-Qu^޳5&^0UW/ǔ']-~6 RF2!/Z < .;ǨJ5ڛ@ OPw.jf'= < p&(j--6d^6_kqm05Gnk?RbzKz­~^SVķkiw߮rQ~[k׀?K46Wk.GHz-%L)6ǣ8?kknk"m篍~z%k=Gk9yśNԛ{v7YxYhMOp*?BQlr,(#q_={1oVs\08)oV>A92lTd6`lЃ(M^CrpM뀾NoeXw9'p2SFMn~IfC\}FRu8ƕ[q^x̗A/Rd}x`dJ?u1ģī^`1`.d@ z%l֡q/=KKOvϫDT ݴ/%S) f9o"[AD.]jߠm6B6v%(®@\^onXϮtO-n^p/XQ;+}6/~25jiPf/G􏎃>xFQjmmI?'8AM C,P{o0%?q\CbZQWo-QǛzp xzBgFם#0DiRðe00P bBppΗ|ܤ}`!Mp<FXcx3MmHldh0= e)cV(N{:c80==ͣ|s+Rwj27.Qwq2)0P y`JbypUo"wVm {T[ARPuT?Ǩq]m}[6U7y|j4|j{1j]ڜUmoWmO]e.퇙UialoSo[M^a=8/N5='rE+v]]}p׸~.޹Ax ^-(&v M!m"wJJc-.u77 LqA&[[d'M@0 }< x46?P-yGp9)T iTrbu"_/{q#/ʌX2/*67-)C->c-oV'J$C ~.LL8|9:Z.9 LVesYzTA޼vsvZYKC`B遒I3eJ0%'Hg-':7N_)q:Kc2J"0Gx&SsJ_U7j|wW{N!I06ϙP[֊Bdq{ q{(Oj_tڃk_+k_e׾8:"N: 5wM2v*H]LMzR[>IuMPQhq~Yk.v .!/ 䭁Hc@ڛܴ/G)*B;w ~v%UnW8烍f쳷NIrsgp $ܗ5/k Z L3_P\¹`rC%Ǘ?ݕ%?ngg6M5hJ4GgH;xiK[^w}.S FnXf|-CCfJ QybBhR$'Pz)Ngr:.CXڕVfPe_5-N5$5wW x>usLb=xA0pkNe?j,sQXߩ OrXu8!ӗnc?K8'sYȷ>s(=`Ƥݮ$ \Z>+,mĔ.Xcٵs濼|W3d ? }6vإ=)Ob3Z"#֤v:ǺuMށmH+$+-P΁ TG4qTCu wM*$KIfpC1 yPvPN=c ,]H3DpmAc4,!+25m|gLQ:Iqݫf8H6oEW2M ٍS=2dq](@"֒[= 1B[ €rܕ㤕Ơs{_pODI} ƫGR$td YA-FhZ@hOWzP/6/ƀEmbQj\{>a/(xFA`YSVi`S#F| rw.Ysy%ZkLcgbxJ\ vNqq[0h8^w+fݠ-BrbVH.uZeY˴,y|v:qg.:=t**BJ*.r:ie!m;|_r^9E)PO|qr*oM:A^YBU Q`*46s:UU+B*Pm[H-V: V('3)-!@sWz͌] #Lefnbۀy58=qd[\(]9"ͦ3g.ޮ2Rp3laʴ!zT뜜 o|9^ +|xɽÏf EρNخ1` u7@ Ax?bd!^}/^ X(ePĈ=.~4ߪLe߽T%yb85$RK[=a(1!>8,6UI 2%3pd`:a.SnN ˄7cƷ^|˧)/wIL'Wn~'1,m)s\;[j &K}bĉI\({\\zAɣOP>WP>܂#(Ӑ?BLfAwHm~s#tvqs[4S>NvNx^ϠG\^5٨C"|m%mvEvn"i 0ޝGMѿ” ]_rڔ Nz)5\pFk]caR ο0'מ-9BjZ xa[B*xހx*9l1ɣzd=4l#% /<)Zb}ܣ.{4 (ԬGQ>4I> /b]R`ٞsr/B۳+_t[kCSt]etVlRx61e_l0f^);tU2fyY`R#5W~07'w@p|irEDcibK=jV*~O T}Yp 7+MTebϚ98`O"?1/m6M^k*[wA% e UB.&;@RpJՂl}|`~NcXQ4a3UWy5Mi¦8g]GV{ \-e+ީr47Y͓J|hIҶSo־,|T!4m_ \u  ñ([~W ڔ}`q68QFɀ%%ijgrSՄXY I|%2>.RR}/R7/tv%݂S2N:8AGg#>LK[R*ʂĈÄؕ'@JAVC C 2֣0B9 RQ&-# Rmb%MG&26&T#SƑ/|9Leש2ɫn1~h|"f`ĭZ>)Q[6Y|^I ^ !mB֑.zIZtQ'i+ IQILڤXO D8=~0'9-"%c4\%. JX )5|>wr܉RpƈlOq0)6mD*vO" xkojϘOESpYfVBlWpE΢Y0Zh+!-1eO@dODHQ6˔ H!;lyY'BARRx;>[+Pq>QK@@9#Pƹ# ƚXƊPRiWU%Q+Qo;Q/ߤBiܥVbŒ MMOa{lG7}Fk)<^:n!FIp>]/՗-ry.NTRƄl13ie,ԃ ۅXn`CI<ZОS Sad (Sż&N_00 j@yǔū2yX*(?r W<]0X020g `p홊\c}x8870F:w|1+Y M;RMi#7Zk?*S^08*o֞$SlJx-y Jhd!+-Mq>ۂn&ȅUxRU;_iFJghƽ) sOZµ@&ozV\)(O+VR}/. ,OS+σcL okZ0lwkC,+szurV}x/kSpn(:B݂;:ZjW8WN쫸-@EzN%qHf PTTR<ώ_@ WO h7Aqe p .}|6sl OuiZH-ۤt͠cE'y 9\r _N Hd t`CdmM?eڭ.єGB}6L%ƗěSIFBErmZߔ,gvT0u"{W$I5Ymw dTB]xRDWa핾y}|y m  =O"BDZƵQ▰1vs9l44 #T7ZwoP3ԉ @KXJ)&URZqɳ'L6_oc|׸,tV^ >6Ko0)I@RP%‚S81t ikơýVjkPzʦo[Cy)>k(׹P~9g ]1ooϷ+{ճ^~4I$]~F_ὦwI~q 3R/?C3J^qWB\~FʿgH/?C拉V!ط[_{]_NyQ| )0|VtSOY3=Z31U탷F?Ebe426 \$pA N OȄzz!^wfrclbcG^AY \bGۭqUr<>/ ^8/#X!1#}$8saM1k%9{<_I# NLٵ"4=+ =,=BjŔ֝pNF>t٩A1x?~Z%Q ] /ݏ :̐IX<_lĔöRa'RCךf dkm޿/$OݐlwY4K}JD&A#tۖZ9I՘4qMܻ&5ĽN~<Џ'j\hi~.3e_@>ib! QeH%3B~~!0~ d懂ؗ׆Ԯ8f sBF Ta{`ǬpzA^5F6CȒcMr35p; M;(TwqƻA;aYС+ojؗ{a籧 z^#f8N֍$Y-x쁐>ͳӊY!mS{թY#˝]?pWGel 8v3/&Va*{{q~76T:fR-䪑+= ț۰r -TZ(9$ DMʞU⦽, ^To>fHR2#gHė$%SP_v]cW[ṉݛ+K壺A۹ixM oꟃ7@ -H9?iq>V50*7'(Svs/HCWDixuۍ;D}'.AЂ_Y7!JLĴ8y." Q`%]v!_Yt[vCTY`ǁq) 4Խo){_7f݁fܩBJ f5Pa3mͯXvOD!htBAg$Xs&;jYQdcİ寒AK4xp`l5[YuG\^ɱ|YM0ܝv$>$J|L$qLiGLi@nې7k&ݻOޕT7e^UxpoU^3^v{B=SZڈڳZ[q`nЉ Fr3׽?ϙ:_:S_ߖԽL{UM~f$YuBJ.Nl椥gpZ~#蚐ia8'DnN2'C Nb*e vrsgNg ]|3My0WPYk35 -y (h|PXSg0}Klb~Ϸ؆m->i2$هj(Bs4h&FܽU]Fn]C }{р](#Uj:V[G9an~c9.~"ȱo7>~H7彨!C;=~.n5fm}E2;l6Ɋo+r*(}#??Qbo>ƻ'_zvw)Lmuf߷ x l-|n]awbo.{Ͷۇ! nZ0Z]Y_/_+ۿKd_GW >dImpƤ@Z9: p DWW Ў ~7J*bgb*X$I棷Qd㩜Hd2hKP1x u `^(8  gBnJ!sH脄 9$i&8D5:A]DƷycI l>w;Bwdro C(Q~鬨 `)@D&ؙ0[;N9+˲**o~){CU4ZzXrFfI!DVЮ[o6)4r^0KqBx}"7 ST5|]2+#t7  @\VW],v>Uk~lgkPh2޿eF D A+AC.6A/ ?;UÐ!\D}? 0c)`w$da@9rGwr=2jwQRp ƣ6*!6Ӂ(xJU}fȑprԻҀ$3;+5T^@}}(3!BJ؈(7~[P|ov)É{4far=z)_V>n\bL'ϊe[>]ԌX?v^+RO'c WQ ob_kc3-X*{ִw;z"''mH8X'~d&% -I/dz A>D.R$~NmDfcѢwptߛ)R{ RGڶ;2jG$G\`A7J ?jj+H_vm.u 1:WuBċM<*r=|R@WoST|:%S/,rr0qR} -wRd 9!ROOn40$dA ɫ."4,pw~ex _"̠_P NBp/V*+o|OaSض)@4@OGa&:r$mM0*Qp 6aϢa`Fe_y#6YU=4@4  LA@m4<VfflLvƳPE%dѭ".@qo0G$7 iGwڹ^6f֡b Ⱦ7ժ}X' iGwpǀл\Ϩ0@.8Q7s51lg`oTjCL 9 ݾ/) +KofI891 IP"CZKm{ԋ݁}J*i@>O~+h$`mKHZ:ȟ84n2 hQJP,y1 9dƓpHR8yB/N=؛L:lPIM_fbh8EA;`%_!330Jk gAf^à4T>e!,!:>l!E'z<]0\?%|=y$ 耄 RrI imM%Xkԍ K>Lt=q;SmNqxw ra 9qpKZAkA`#'$X8CLs?:G[hgPC}Z0waj@Mw jI4qL!aJTg9:kh)HUO% uĀSnX3HD(1}&+Z%kT0|BsyٗHJz9%i2$tSlF$]ڋ>$]C Tњ8\o/@"a a0W=d{$ @bqm_vjl6P5M%(@4tq̽طA0~DD[c#tG1(1Ou"T$T 6[&od\ }7@K䀬/xp _GOrLd6AOȰ I}B?C Pt!",; ?`|S(q̈S;NI3g֢v̤K{O8 ]pHH93J;Ȝ3qU0m9nAc %Jv]S?6~+ !O/KNddH̴}5y0..omND? q`x?:)z,#~HN/e.TlkU~؃%TnɯJ6We;iK1prs>wDNّ5π3&<4ZjW:bkBهU}xǚ|mwwEa@rL|C;ܓ/F! vgq81iq3ӁbtQ wd <9,T> @{ \i~H FQ Rk9:=YNe8kTr8= 62Q6/@ߢG,}7u!WuMpZu22Xn ] l@W=03Oyw`h ID#)tt$0ٗ@ωbA [AqhsA)#{ư(RUKP6ԐX Z.av9^HaP(q8JDף]S8Ly`b8werɈ3oU(N3щ8b#wA%lIs8!Fh` V TC\0lm{ AZ1N`?>S*8D |oϴLVhķ:]\(UѦd I:6U]/$ѳf6<2dRIL\: ;f 51{ĸWbt0p4~4չQ QTC\>,U / 566 Nha+ըN"La_m}F't$~550}x('7H>G11@FYЭ@t+(U$3zraܲWQ\8Ӂ9:whst`gh[qF 914h kh ˓ AjofЏE6gCNW b<9%^m'd=4zMz M!eKj\f)W.Je~|ғ)Yd@t1ʧb2K*ocڣD S%ieXᯓx'IK7JˇEZ=7|07eKZVZsqJ1毑o+=.oN3|Ga {KόYA{g<5!:EڶW&4G6.$.YN -XAacҬU:-w 1l "%|woE㤬,G#PPI*7^  U:OO \?Go $y0:Bz8 ^DÓT?Lm(ÉC 2vuK`уGY #M eq̟K^ Na+%1>\jVa]uf.0.1z|Ʒ~3տx2r1^b|xʛd1$i2L`"}#S@2!3.͇+ 9GjWGQr2f~uzz>F$Q~U6Ĺr{ȁBR|Tj{5AhtlHΝ_dG#5d,I mYT, a$tg^: JP< Jp\Or0H C<.DSj,0;A9/ԑ&>0fQ@Hqi3c!YQ?ۑLFE>#%YABѴLzݷ.LRǑ]B:ڻr1!q0y 56Gɚ+*em 2@"xG4p`YތBe҉yd׉ysżc1".qQ+v387 øKc}357p3J,pI}T.C# cniVPEv i2Fv5J=96k&-gi6gQ Y|Odf1<[U|!щtD;ۡc`߯s`o=S/ša*PK$?z;YL-nODyH{e067 WiB*GF荧)5A>#%`C蚌K 2H~R8$BFMG7eKTy*~`z^ۨ.qH[u{k<Dz޴-Q),ˠsV[%Ho2w!mNX_)MAx/o۶(9cڕ@0y)#2'ch QR8df2nP蕁v01 nL'@ ùphRMdp$ H[aA1?+q"urs]kmz9ņ[u jG%v5_x1A܆e!T' 5D-Ӿ@^ $izz7,ˣϳ<ɆaM3ԓ/VĊs'ͮLRrU}%ؒ͜._诺?1=;7x2 |w -DZ\MA5ݒ8Y"AJZ] @?uM .)5RLk%gҬ(S1|&nO@p2@C1#K#B-6.CmZ(d>W?Xזrb.P`GlhJR/A8an0Նe;X@jG8nlLbtK!%8B/=yUz%:̄^i5H,.o MqC/:tYj0l%x8}L2^W,P1W]hyVwq|ZKHd|dYIYl|hPH=B?ɥMF=#rOv}}&lA{QS.HC1 0:A}dWnfijeVzIHj)赺*Tj>n>tp{8cvoNIٸa䢜E6q}X,(1>^OnD}Rxg-'">\:q]*7GK!pJu%s^RJ7y@=IUwv*i<x2|CV.J4Ȣ+ѺWutv?JHLn.c.Ȫ0Fxᢇ $& dWn'޴ {y}8rQAn, $ -oO>4d, )-9U֠-i2w>_SzPOd\jB^lhOa;bQnR{Dk^U-RCJ+w GaIسޝNzw2'w3X<6\ ׻EDz7A1c{"* ITϿ"(;B}dIߴXYqwwt6]I\yYf<;I#[r[oݜCN\yzh|Bl.F/N ߓ޿b?--z{uJ&bCBxDh +c˜ N"$"nBɘ(g=d_h@,o\cɎn 7.Ȍ[G@t vW2S= {@Vh[gNȒ  "]L!YPW$KY0ƌdc $kc\9dca8ol;'q[h-v^'ªp V%T'!T媷$e2O~];HvtDב\IAyR\yrWы~[~; 對dϠ\i5GOOi hzuˉl'kguRfd_̒dT V@+жSa/rÿu _<-HrG6Nx7p9~xLRnf_0 LyIF!E+QpJ@IV؈%r`?& bI^!3\ Pl-'[-xp[)-j3N=|sJek같;ksokyqrw߽ĩ> :y$ $F'1Ue3[@  $XET^IRiW*(oi='.x7 )l!zCޒU^ɶֱP]\xIwxOɛn[|zbJ+1W+vx, GZ\E=".H=10U^ɶ#cTpדxOKob߼G8Ų)s-s|1:::=FG鷢|tC/?i% B0 (x?[ea_CP)h8.tc_[Kgķ{d%,L10Ab $[}9#c*,ax s$i1:{ p83㗽ekg8mW]GVWB+_ Ut)tgFvWkȌ|WإË$D@tg7( 㮯fnrCOn>+ȟE*s5L@j<*D'^8 ; 2(0R~C3KWK(ݰF'gg9D=B6ھj/~6ݠmA o"2-zI<&NUlW0.4A" Pe\*y=~0ؖlJYF( &|%=($Ct_!Bhg3 F2~;zLONsXS'fEFuuW^ij! ({O _MyI39qlM86{qfon&O@RGvW`CZ@7  CODLU(wm;7)P6FG+Z w7ԣwk8&/N t>4yqÿztиKe)t7If:Zb̳WS(vcmV4_3P'|W`.vE>B4>~LJ_B?{LyvJ3p"pQ'J*A|׮ʩ:>rWb ?Lю,EAnnT5DD.=Mk$Ǣ*9d^{}{5#WUawŸȠ̬NMu'j $m^ʲbY׬KevL]kUL?aG\i9FeB*_ð|0` :=,܈ 7bq >q9Q&lj &k 8( 5TD^ L\")B`%%U+\f!<WỬ.I'H 7>:ɇVZLu$Znޗ1Y$ ? ?!߅r&dYhqo?n"n"δ;~H,J ䷅5qrD / _5aй0qhs sD\AFhK#̅ 4mK #ZǾ,D6?uUzs֔:|Y>&*Ȕ y3K\JXjRr@Esk$\˔>o$r[K&lznƊe'3Rl L*3;@GUlSGzضʧ9,ncv8Wk,Kc՟ +1rl҇\V($1|(jKmWg/ l$B̌Nj';Pj`ҋm7?3lD +rf1j:fB (Xc{X-"%ӑຨqjuK kȸuSc}*"F|@+ֽ֧ܝet <墒>C ]‡j9FJ_vUCber?˯nYZ ks11әs—l9 O!ga6\L4hU0AdWWgB>f̩J̘sE.iژ3g$٘GjTVse2,1ՖJ5jj`&iI.4-4 i)L2$c/ -K"- #N⧮+p)RVu _b|xoXHD:e>]C0@|O>Ho0Z}`]gF)ZP5捠|n7Pr3λՇVjT560 sT|s#2=|e{X,FQH嵭q!|y}~y>"Qnܽy| HL2yhKP1͗aB=1P2L2~6Qj׍mNZ:1ĝtwRWϷ8f`o.$c- E: b g@O:k7n?*8!)T -y܏j=HV'cmkHk OKnM< )ǎAHR~OxoK%#~nB؆Xdh ԋ$YVm%~XN_]x7it݁FGgK6>olHc/zl| `{QsaIېɹ^ 4> No|dfwauIFh|2iW|/ӜqWa Z`giPbL6Ǫont'l4~DӼ>RD u6_kX' Z2Or&&# LlMK6ת- J7D|"]Pk뤦XGs(k wNLiw-\婗7+ Ikנs- }Mp-FxV^uKHp꿻p] bHO6_.`.w"s6̈́ypWn|Mcv-y@Ȗ\#+ѣ;jI lH\쥂Bdo*{lxFԉll#Ljll",M ֶ4.׸m-_IDdAɿ ç1RIW_|Z4 S X3€&cg/xCGfKv6XB92{؁Ǻ 0%qT7E0҂ -#s5-uFG9`Jtݑ-,a6 *َ[=o5ƙVc}miɠ "q%CUXGotGKbctAbB 9n'2wAJȋf $>> Y-a{#g8Ĭ *0U V $72,gVGFw[m(VB>ɪ )ƣ;XB^^@n#U=ZAQb}[SQĦdŹ8Q=blC*}L1Hzq6i,-Lw7w]Ź|&uj0-;zjߝ{m޿Jj{߯oѿWުUT|FQ!{u_#}[)Q]l XdP9YH 5.PGi*|GE4T|S(|ȿ}%llܯ2iU'ۗNK:{C{qff@%ɣ9R*$;|'C"Ug]>5[k=hXV-TAOԔ|JD:͒2O2xUfxevҏ"1x:b$h8`ѬQL0A Moa05C sH`6DCVs&2 +u&(_@Ό:y m JcR ,/a5}VG!\aQk,X"8 L (Abe4T!{> M>AX1%y?bII-pN:rh;?,ƯHiHDVh~XvZ&n 7VL0J}埱>M=>]յt{#F}t/uוszU:k;cv#B^7r_ ;Jҧ:H j"ZdbQ (?)+Gdi+a8}j5GHf|{9Gvztλ:€By>u3ƏuPAxR $# IRVx| /‹sMa;qvuMeJ]A` N0Ags!Jl+^H9H۪ЋW]lď?SG5] <)ApbIMgv3CR?֧z?hzoiMϻ4 ViR;v?~}eзh q>QO d} wӿE2XЂtA^Zb88($>=oQL} k:X*-CS^%|OyuɆjsҔeML!T!a&$J0 Wr{Ⱥ| bt_CN*W~wB`kr{dna jX95'Y#&p @TJ0 gwkh~uW!w `{K  V`{t5pm_M& ^x0Aa6lx)a=5j \H[!H:.b#9܃hoH?ay+&+ZNp_tKje5Ih"|-_5P!I0l ˊ-BMyj7>ڼel%Aoc3@fZ ╛ke'_pLӰ"Pj<-<+==r33J A)fV{d}|))))O @]Pz|0|9$82F xa%kk<УWF0a&zq>\a{(b0= Mu䞞Oex^1MyOOf-Χ< 2T a^#QT@OE&n>-5.Pm 5:ubDs >\o4iA1qnI:̫*N{[Y'}fӳPh1׿׳Ҫ[zSwBoYhjE]n^/W[̻k 7] ]k>] ŵ}( $K/(*SzMi0R.C<~ tOB!LHRl eђ~= Md6] ޼p 2uwA+.4n{BGgA}%mhWG {C6* y}BA;>T)>D<>[yv/t(^"P񳩻YjI4~)ЁOt(8Kzg!Veӷ5{q?8|x<7'ʍ0'ǘ-z7ES:u3ұPB)k Ny~GG$&qj"KTp*ɽI qi;>2uz[57ގIh??q\[3Gfg󩁨ڻ?6;t7&o`eGWVY*G)a%k9kiYlPn'fZ"g͞sK{>Ã+^Q)5{?HۋL"A QD>|9w($ՃG,%,\絸=DV[O1LEK!Ug = "=M(~JXY?^(EmK*+\4NMeIVa1gJj(u0JGƼ1:|U\$HV(A~6o R,ZcYm9kx"4cr ;L3zOsNԼݧP,ڀC?@VBT5zNY-tϸFNl0VI'*䯾A D>Ɠs2|Fsr V#$V؜6i 21ϝt@c#&i}6$hjV$!w[ޗ ]kCH XzPF b2>ؔܶ@S kDh 3?[[V }2^*>m1l yAqXh(b\0qx* c}>$D2Cl=y!;TQ˩T`<%_w=u N+T! 1r֋JRj6`Lc="ȫ|D:1z~d*b!=h  LѦ^ϲT8?aI%|%oyF|ufƱT|Mgy8&/>~C7q\2nQz̈́.. `2.q- 6ޡ3 Fy4Љ9MD݇!Y yp*4߉ONPhq~1WKL r. ֵTF ABJ Oj~"g-Ohix>{cM8]/V,Bz{}CҧQh#%#[/6qW\/PonDl/N:, o-/Ot Û?_3? RfaHyFo(5b GE@ EM=$9&74ю͐ݱ*C^Ow<V8.B{fB=k\1ن2213rT~8v:sY 7ȍJ+[x~Ob)*Ǵė`T*<UEX RH:-!h x'Be>0K,:YQ1i6,V9 (!HEF:)Z1DFlj:j@YU(74poa˸˸˸ P~B/$H=$HF]$HH$c'"AAxGYC>m: ק mȣHFjum], YspcŸjf-tBTp\GhJeDMy a,Dc:ȯҘ {gxMDQPl8*& vNi6ю%Ro61CnI[ߒC-Ș34r*N -IQ./+\\n˒U8l2dzE|"5{^YDF-*"1b,Tf3L2Ïѐt_%lBkz 7 \v#Mr#-cKaO\76_ӂ\ ],pA[l(wV+:Caz8~ W jk=~Smm0t{r{4xs'Wg6mf,}m ``ya KhIFȥ xz%ɋCXx {XYq;[]\MYępncWhӁ6zFʍЫǹ =ꖈָb2`)sz/@|,&_WˇG1`xwF<-4\F86o2pi9T-7|##B|9}_E'4PCUc~&:Z 0@g g JvP_bh=9{`>ELꥁq`t%?%y#v>H5(Dx&y6G _Uɣ[LNN(-'e&Q i߰ACn oՠi}6(4h|_bA_6(=3+ge߰z|^Q~P?:Ю;y^N=#)ie˰VTAڛ(DOċʑPC5busq&3cHHkY DyRqie5z [l ,gB]t2,>qҋYXsR9s*7q#Sɛ\bko G#:}hso|VTv<]mF\4EpU7m] qY`.dVٲKt|s">)5ۤAT<}̙nϢG3ӘL)Gݎ}³{˟$,>i~{ш1(|!dgk%#cI.q=(cjjhhJ'~[?q҉/_ L"kYjTV\1j j!\L8ue_rq7Wx6׽O͜P3z1;NL1:V)P[̞WWY84MuE0AGɏA!1|;fo7CAj'Ԙd3גp}$NC7Eis(|t[%qdb09HH|qBjTxؑLi&IF,p <~w*ML%| %P /@.կ@tض@AqY8n)*5owwhQW[4}$$#Oq^< Je 9)fbv |DWXbOO+ia>̢dJ$2BM<"#]=DŻߥG s"P(g%gVCSP"wL䒳3O#LbƴQCgg9+V Rf!nv*ˏ2i->Y-vHv@D-vh2Qnȇ.+3DN#._Y-xR85D@kKO3!x,q, on,@]@CbtÁ|hA3%JyO nIߣ2̯’[~Wg/:f)`2W}uJz<:._GVu_7p`x Q0Bl[y ʯ柛юrJA6q7QQ&y=m\0I%=V|!sІ~C 927 d*(,i3)Puf4)#x`4ac Y5X5Xfx#8#fgKMpI/z}熟A@q ͲI%TCF0 F  BC' ;|L{k kx(Se^{F6rϋ;9!G'+5ź;TVr= R?{||b9³7m܆_ﯺbMS\TjC8Iu[| %}:2ZE"NWTK%4IE4olKڿkq}9z>>=,35gB*"-4գk!659+oq3Ҹ+]`{v7?G׫fغ.[;|4q7I׫0qc~Umvp/;jjӲB`JohO?0[W42`7#AA;W;W;cP+?P >:X738{A"QvkCU>-38>OwN8bsaH/6"f?Y1$ѐHM/t؊ ˷:w$C= U>LD^bfZ*Fta%a7{W O~@NH`,Y*O8S@I`㹻M=F-"YJc'NEˬw5j_Z:(\*~`q'*+86ZO 'CV;s ?a?OU<9TɖWmY0|{NYP>:jܽqzƄI(PnHB %?P? K(JORßF/""2:8.kP΀kN&׾TΥ~p 4 Նf ISMh/\L/uIpo+ֳpI5u9Di(?%FќUb@KZ%ʈQ&%RB. h)h)UL>Z5gN3ϊ=6sC.]!ˆ'Z^5°ԱqwMG6!J3*m/GX}d3z6<q FhG/'RnkۅyBo^ ܺV@6zM\dYnS6*c[Mt7*lw~p]!@6NxIsޮA_|J|o_lDo@96w6WvNf'/k]&x]hA54PGWb#9RS&4]qncqMsUV 2cGM' +`nd"17H(ȧ {2\ZCnOaIr1F( !-i6o S0\f8KCly?wCmnΆiN&3/nNy_&/jx4Ɩu@5 ٸK6o3)O!ڙiw.iW;JZb$]F#XI֭>!RcąDf2J)Y[mRbCc @̲ߪu~ X ɑ.BϽ5Maѫt#Tab1n:Q7Q7IMɋɷI [(kq4ILdK'd צ|EIG\!Ti馂 hoBt;?6 >J7W6ɏ+r4\D P .#nNB BϼԼ)XJ`8\+Y"wrkIQU=[5NhcHϒa=i˥hyP+I7e1(K2%MƿtXT̒t'9Jjˀ ٳ;{+n;٢txBʖ01xcxuN NÁ]=t=<[I`^jHĈսZ T43)PZGvBܳ[{3Dwpq6 ːcM1B{2,HXFٸGXC>eʞiVo۶ͽNt5m_q63ֱ+]25PYR?61rȻ^,KK1Y!*K-K5JTfI;7G'>V"cUnsSy6T~#& ט*jb{7bs[ō B_E5ݴ{(]r ܒdCStM37[־=q %%MM(zBBwRBI %wN};J)0Tq ,PBw\8)I_z#xP SqpǍUbȏ/brtܩq?mzJY&ֈ!Oτ33\9SS7~'ִn4= ?F͙~I+ ng'7eUYkP 9.=+gX~, ׭MPv<`@#ZT`:DBUK-gSkBLh-D $i˦T9iTYOR2h#gܤ硚ߒj&jTӨTsgњgUD4%3 &IaaL,^]sN*kxccBڏ0;6:4zW|-4w8(03/:K6GvhT^+7_vȆvvTuՋZj{Y9FzZhM, J€I>߿~2G~2X&` @@9^#Izkя `K((xuJ:F&ff,PWx@e `#@!c~ Gc'| /xIq+6<3@%YcfF`dl(o|23<PP SC pCKحK(xWGN2X @>v/p @hPL`:@**Mp4/P< `;DrBXvy`WJޞ_rh|K׾+R`oX_ `Z2;L!cbf?3CX\8 mfvl4lm)w6Mr Aa n =vފ onTz oM2/XF%} L緮^M imGh!CZ3_1|<~"__ Ixv\wbⰉSg{[aV~cBp¸A΄NQ7- vME0A ~o(6z,('|n1B.hf]:ow~+_ ܱ_8 Sfنܭk۶n6w}#<"J[mV.P-$J0ämPx[gLZ9o„?Pxр=ydfVaѶGi[[z?db7QLXt} fmLLf3p]du ͐ x˂L:SWa\N-Y0(󶢝Y0dZF1$m39&-}^`YdR6p\2\9ԱYX֏sXDesɆ"nEkC>AKKo%7?7<}AA(\S @IzN&m&^\n-Znga. UB&)GD۽Ky'v, kS`,]vesSM֦[XeXc|Zz&x A5fA n3@x%Ҷę%zr "O36G,&yЂt\Fy(=Bb6J]S@j J213@MP&O1O.`p6k-ErG[hMpםp ~!#=[~]K̤Ĭ'mL?h(i= \f"`!FnH{Fȿbp440+)}KHs Hzr+bo |OmaؔJJ֛Ǟ傝C݆[Q> S‚;a TL4%`/9Ta-P){3Sl:cQW!}uczeSf92`ܢeCE'Y5m[!_H=%äV¬LlɼҬx]2ͺb<l5#eE6edȺ˖,Iɰp i2w]%KJ KJKG ,S{?XR{`Jm٨}l?qO:,qQǘǎcwM8iSNL͘y_ҬIN;ouEXtm􌕙Y|huΚ?acn#6`m?)*;Jvzt' aХ<&1z3Tx (  c=ti P_Է/(~BBӾ񿯝==q`ҲDŽrOwtOg/^x/^>8ʫ㟎?oco_>N仧kjO~Twܧ7x.|xW|7kJ!=(:IUJ Z3մh}$ʾKh!TS YT}y|}:\:FqS?}t @n?->ۃч_yfg3)Js-dB }ck&c?,DIe<$3~pz͏:*& Uzr}‚y! n7|wɥOB%G4'G=4ͻ wŠy<#Jax px=ѧrc֊׋ӄB5}Yzc8F8F8?~80\&`Mwo|?јx<7yp^8Λ' …vOxIݣy%+>ZHBn+KH}9 5=}$2B?r‚ü¸Q?aH3 s%~ ç/$U4=8ScxsC[y~_8׃+=7?p`^`_D=+̏^6ֹߍt^؜;(Ú4݂>~>I#x^أ!}q~Kݸ],i!̱ߋG._X$ Rna>ޡ|v6G- $`^/9taEr=quTVrw 9~pztc >nlm>nn}[@sgT#HPQƂ;V즣n%|6H=t1"%Kl4B삎\#-Q=6vI'&$Ngۣ>EJl!6VM!HU͐@ߜin$=p+T$([-A 7FIn$-p'd!ss͐sG&;wKݹCO\ sG,w퐜3oHθ~NƉOSsY樔;FE\ԑ?8cll {tm1wpԗ.Ϩ\zwWNnT/)9yyYeY[dis>UG(LrOq燝ᓅ<)/5c Y:Va`;_FMљg+!yxsPd`$dq $deeil,z-,z\*"-KXb4B::[jZ]vDžڮ beee"#o`py8xs黻3780؊53,`fq=/=ԚnʓXXv̰vDYYi{xhGIp[+9_܀Ӎi'.#+g)\0? \zDZ>k(ٷuKt|Z.-Eˡe|Q :?΃pÂ=:XIpHgVS;:=NC#v ?7IGGsqsinim:K;&q%/1vscːkV~nޘ$w]=tNkKkU qtPEZn!-7Ic"%:^@I|$In;tP$u%X4$~7sG_4|TdjaC#;^HwhKvċ{Q{/7-ӝ%~QUiH aiG@?.-`ƢhpqeC nRE{.AvtmC[5~ = fM=qVp%sUm|B,kyx}g@I8 9;$ÿh=$T%UE!#0Dh4TiÒ(VM(ޠ㼕0I'N=MAh)[ڢe?}AhR@H>CRaK#4(kpBCqoAhHrBP44 9 A}6RtA1t;F8թUvr}[L6qOdFߥx!s$XolC[fU},o!l$ӐdXs$;_xGBH`z%MÊ[G>@w17+/aW0WsDX= ~S` [NEh9?@ [WQpœ9zm|O: s{4mAyI-Zis;=^;@fuxKoyҚ/U0\?dns ӱIɎ vw\ K/}!v IMrM\60}y'L'7E17,:Zur|7bH/p(J~?SH044Xތv{K7s0dtCIw^ft[㕈9JB{^]D}c3`ڇߦ]'QAnkh_o$R1}\[sm|I=Qc3|o_HYٸLaXUn=TgB[D^@0Nk]GbƝ>ceUX>gX!./vw3PRpT93_Ţ7$Z XD"uYrU?H|0 '햭xޟQun舚l8rBeRqPIB6]: w;$*/?T [W@ gcr{$Rk,xԔUy$:[V|,LjX@zG΂YXkִ$Ԡ5X8yf.u>N*/sMGAEYJZ|p9xeϥ$*?=D<"DF'{5FDT=wX޹ںcԦ/Ht Мxdߓ{D}ojbՍ/8v{yb~D[˿SO#oIdBDK/i[lfTh1=N\8ဃ.YhGHVֱIz[-ͩ{%`q{+;FťcKUlGH9݁9xOq?q})hʁw49G)9pj؆.f$?)IK723IX߅.Bxfԃ)x>X 06\a%:}Z+2H_]DϞɼ zS۟tPDeX4hΦbm7Q~{Ԓz~߃Dg.wNJcB;sn9^> e8shK*/Q^3mSl8L\Mcؾ jG sAs ot>䂽Bj5z۵kLkv'moL>9u?8CM@$ۘRΑ^IԛV[fJ>܈dl_·EcE&y?p~#cISvH*5~z7Z^7%|v*q)Xt .7_OdKB[I^߯z\<_U1B 1W_~_k `1Y~36h-4Egom"Xנaw-$\"X)]ق5_jŹg?[O }gt㰱?} Z*/!0?:|]݄{SCA$Ϭ_]:<0 Ǟd!}@MָB8\$_ҕROKsa}Hϥa%C(42.ڒw#9Ctvtu+C>?|]-7qF }2?V:yq{7n [~_}C?kpgf1LD?s+Ӕ?3ӵ* 4s~_yG+8x>uvަjqq>Nz`yCfW[7È=#qB7|b7AW8`+3W }r#+_|8b)|l4Zn ny0Wf =_՟f{fnlaqf#5uλ}6)h9.pqJ=r`шt{0i m5p /*|"4 Kme#ܥ)i*{-X^BecXpp ?9"1 80}d=x&)ņǐOcN=ܿw4{V󝐈'`0kux>:%NL^;ڎŬC=Bsv^zx)o[e T?G'NsӻOƗw#-sO qG]lo6'>#V `}hxSCf&w5nKGv9|bq6V[z= _6UgXYߋǦD[V8˹$UrIv 59"7~[ҕ|`b\5xx?z˩'?Lt=e5?umԀ̴7͵X 50zcqܓ}/RǨak)Q?ֵZYPl~^A-,q W,8ϩ9W}^A)b}I+00m7Eq Xk_Çr*xwTހՒH:ʱo@̦ۑ1llƓ7g)s/Ohٲ¤/qΖjN-oBix}+O{0b!!0w+]i}FJ§+z?g(41&AoCb_-ZR磰 >ø^HlQs =?[D6z *'~=T@N?+Ζk+0.y*=|Q ;WhhaNO; Ɍӓ0:z1XmhAĐzQXV ڑfb^k14$\%ubz;&n>dz%E-;zaNt8V WP#%;7 Ĝ}~W/TayJ`>K1 Q8,Óә/+f&K+PBȍbБo 1@_זo֨gbx| ĽfOPioD f69X kg?T1Q&M{(x?ٛŠ~>'l^QuW FOܥTcb:=Lrk}n!haӊ[b3}YZ|byroauwJ9aw"1)*Co`2?B1x>92z+d^Dc~_1,Uah=C|v02X%+b_rz$.a>j'LCO^&̹. 5|\o>?O-;*O/CŻRŪbx?o*%Aͪ;075ed+ߌ=(RZ1,J e)q:9%bcllFcbŐv03J !ޯ@byh!tŸߗʎʻ~G3'爡tO>bZ&:ab8sF{^M؊Iܻ]_i)0y1g`{̽q,lt&J {o~saYn1vhb`{ևCGŰːTy,nQX0-T6\e1XsZQ |ۇAF $!G!,8+[-{qezf{#Mʺ?N7+zfGV6·F,6fFK,h1z/6B~0|يG3=0e}[!U-5e ڔtkō0ռ0frgl׾B#CT0䉍0kz;:ai˞ASw#!f9 Yq|6Z^QFe)k5@jK`\3/r4{x ZL34y_wlo4@iq1 0c@d'o2`6L?/%K_# WvNX7׃ucى!ysWO$-\ L}ܢ_/Zٷ`y/dڷ0EoA,m[(95L-<Ͽ aUz ]:j5f7o!x=?qh,e48v:X,N/ Mq6ԁ ϲS7ok655l=Ҷ8hkxdfNJ}t5WoW0vMV vnߚ2vZ^XN \>˯۽ Pp [ ?18?sD:<~4WdEW1e1`is^je˧ _©ti_D4dJfOɜ +j a/ 3s!*Q۟×^w wW W^Y ip)e~es5`UCݶ0w @?{`nҽ2ӥ[(tpr17њpwZ!,œpx9BN>aO^3Bu{uw=)r/?f:Kuۡ3n)Bgzf]mtȣ y/um tE ]®)-Ҏ _=@2RZcɺQg] vfr":lb_?]ala1Olc?hS[@u#!?ǔa &D4k.D!vyS}k{8'ؚDqu0"M?3dG;fe0Yv>:.b]dMfMaڇ^fLd n29p]O"mV{h%&`oȮVjMpoKh-4;l寘FwٛSG3>[wFfCQ4Mpar|fzlᨃLN )m=AKY3-}&‘ȋeL!PkZ&gy߱4} "m}sFQb3 )~~p !Md%bML$DGiζ(yۖ< DzPz6&cA/jOB6HS{"J CEVm c'k(r U&q t0z)Z5Jo 8Scs EV%v_~e2R-jCcu_i'T3Ό, \D^s+HNۓ(PP]O*ױDZ.Uy?VϤVi,}B3[!Ud )~L4ވ= 08gm өAD)f2w2ad;j߱8ϦfV1Ϛ8%o As"m%Mm!ЦTzi)jM]sԙJ_ApPNGLԗL-5ԍb$^yPOJ6StR 4З=!R&g`jE *P<` n?"<#(vD^XLJknG}?H;@(e (CZJ9 BX(j95p*[Iq5#{VS)X:J][O-jq*)ޙQQ&G K Y~(|\î0ƏF,M $/a d@zVڷq0rO7DS Nz8 >ւQ8ne!k&~HkdR)c4E9U6 2J7 N .f @J!+O*GDYyeo Mx8>4R +oMHZ>{~zш4n`bx~dXFG=0\h %%֦Xg@ex7XX%/ #8 ] qn%@UTX$@ gZ4 |2@}T Mk(LO>àx .Ux0~#⡪8 r<<;nX<)P-$ƃx]eDìx)^<Cx`Cy<1x!U%Y!z3j>8| ޙ8qp?.Әfcrq0s,LWe!#ss,UXh /º= FQ *Kђ\(c@A:lE̻|4Dþ%Ѱ3tfE&h858V|Qy% < xFAEԈ$( w"kz$lMWb u"a|$#rlx[8T);aк< J ]( pP-y!WL: )󂡲=`es Ho )F| w0f?$)Cn/|!=IB 7~1Y>0JGoȫKO/ OiAi8?ypÃaLAs.$:pk61ą+qfp K|2vps@BρSl!5+|el"fB~ 6z.3[2 rΐ`ֵxՇ3$>Ԃ-'&ÆP CM8P׎=mrT*uz8&c_xvW6KZV8gJʪjP+đ(a7)MThy<$>tUNZIݹO,Lk%llVv9yYo?=tߕN"S/#~wο%=t9W'GAi 4zأ :>KRN[Q}1) C`flcg1}Rg6 cm ,xe2ɋ tvqf>ⷣMvl]{ O̔$c6oNf.>,eo ǔ#ZE%u4bv0 &X;[D']f`7Od/"ЧV> >S é?:[)~2?ܒ=4Ata<|^YŊE:hЦ|Adt<0$ >DF7Z+B?}nxG nx%*LTeާ!3ijmItyI--2 ?}2kBxCDR7EzIO6R>tt^F7K>ل|!ZAHj?Bf)XGe>ބNTHu ^(iW a__O$ωoS $}"[32恄{TD`7>Jgtyu/4TDYU"fUQeWe3?}Le'jz`DTO9LS7πMsXe xI>Y?lX0˅H? cՃ #)M [%uL32$2xnxyBxY&aX VY7Ϗلm7O3,leWۅzM>TTVM4(xl}FŘ OK~{ ̈8O#*4oLh#2 Vfr}Vx#&6&$!{{xE>z>f{XI>>>٢_ۧuyZVy7|>Z`5]HH7^>mj}l~7Nkc*(yվqCu1MB^A Sfw{3j70 z9+0F^kmE7bÉgtCds_2i@H> '2:%D{FT&mwdcGS y۷|69sшSppTܒcOÅSܵ4Æ-,I~~| x*c5/mwnZTu 2:Dl;i-.vn qB%HaB^?uGY[\3$C'BtfQzBjy}we|y뵍BOrzwks*B)3Nj j)m 9eKpiu1Z~R.z<=wF!dOa{G.oLEvVcAz9 {JT e9K9*%p[%G(i5orx ^íjBńEgs앭y?J H R,1ϵ_%Sy˪۰#Nss//z/Å%#ޅ 5^=q?ȥ;Aɠ7Ļm%SI/?yGް Z\8\!̫Y=-qxQ H% {-| (tpl0 3=1-=9h~l& "*EkI!)Dae:3!"1:;K98je/GWr[O.[Z4 *M#Kn^X4.t]O@ijJ5>5Y*{F ET9ʺʺ>dU-6L1UW_7⼛p_뫹/%x v9%-U]k8f=z)gy<:(PpΓ\r,epN,͏n:ހ aLe/k:"+Ä\7 lܾU1Wp8yx |˔OKm% d}sWp@tFl#(3O 6tP_'W G&vU jW_KWb~=8N MOo\]l8 Y1>Uc8+z5B}'m"=CJ3mS/<LǻJ*==V^[<33=l-t0\>{x "U{l OML!M_U`m7^yx p^jYAiTϬ wj3 ;4%"7M[|Vnzc۰VqVh :l@1S$e!~UIG00Y}gfqpw&QՎ5`UC϶,N$sE3/Ψ_׎-2N}}lNlܷG䕤K˩s>cpnɉMWxmx*k{0ɝ'fĺjckc^~#Wo9e0e6'GHaGD&N6.N>B95>x#ϟy>3vLGUw.Lbz"U7\ָlYTL1?AYJ+V|;z[-Vxڶ4*RO:fBp| U? Kn.<ɹO&cjk ϽUL9]xo_Nm^~9q$i h)k'4I( '<O uҸ{ ʝkn*.)|ph3Aw0LG)a[W?7oWlyM)4h3ƬO! V.1mO֮Z'4QilA'f{ittnH+TEvoTYճkGg5 .{t/!m5`\TMd5m UpJw~ M{ܦ 6<[9I^a~Fnh^Wbf ]Ξ gM{GpO(ߐ(uiK!oq~DϿD~uݹĻbmB[g]+YǎjRɭ{](%~}/ T]iq ᏣyH#[E$״C# .`}cHاpJZ4D2߿2z'sohڗCkΑj6/͖_&3K/ew_BWXIr_ɦ褱o;&qh$6y ɼ^[a}$\{6zς{stI4mŴsp9͵z̓c$j9aDy7S$izJnkd^{7a}E`썠.%yJd Gב\+W Ťѥс*/÷=zXRI*^ov EiL0ZD\!ZzU\NQj1E뗗puݕO}_\ 'սCr דHdتkPx0ɹJ^R| ؟E1׬y_m}>+"Y1oj~1&* oI2V9zyrR!M4fI7f!cHV0v]Rrɘx0xu 6F7Y|rlzlb\"?܁ʖ n';);T^]O6;a]|oukh{p{gJ2JDQm&hHT( JchBRiF*8D(S2gm_<\u^k5kߋÇ<^?ϩ>J=%>wGmc )`fQP,i=Rd=pENokR-KȪ2puだrH;űc-||I΋u.1`Uy٤Ғ8Js"ӌCo:'z9dt~wjY+Wxjؤou8"WيKꂙ]ӡoH _}?kaԄ9+ɪ7N]Kؙ\'yEN&ĥ*"y?Ad1G'86!9Lӟ{|ǮДZt*;ki7**:w/v=4Tm l>;OJ~B˶~ pcδmc 9خM gmz[L-VGjޗUkǸVPGd.%ZU7OHÏNwsț'6=he_ nҗR ZޛH%}'ZXfA> |sY\Ф_ 8qj۷6mk"`h͕T/2+_|cリg=]ܤocK'x/5c68LRl뮱Nm5nb6x zpISg+]_~HM6:N8%h^SGf&NiQjc!jo1k]y"Ijo jo<%s5-nxkR`Y8f~v#kLoVc=Vc\Ɋ!yuӬVi6¿o/%BcG'VM˯j`( x,|{ #;y"?d_9ף>F<]s)X} ]6Oᡩm-֌HOȬ%Y}Wy6?5?Wx1/4i?=BxZ? ?^/ _x?Wo^_!?~~ ǯO8G+<>_u}.`iy}d|l9l}`֓7]>wB~$ ꇄꏄڇڏ _$Hh|!zĶffgޟm7[?lvc͒Φcaփ[Ol;v${P~*BP@ ?  Կ 4> 4~ 4ts;$4L$8{N^?-v^u˯s䴾ky?>^޿aa-|lÒ XS^MԮnɲN2/n4˩ʿ,ENܽVbEA#+b]ZNdZЙߚۗ.CNt9443h5ݝ)Q;DFGmufQ|(#H3?gzM6j1HEPIu~:9:'Ʌb>k3dʓGň؇_d! 'GbLH+{u wSbn>W1'K~x|od!%6$O%mFW N +DKH E#Z8$v,Yp2d@ cnFjG{2۵h w9~mԒ nG\IX/(AМ3:yt]J$nl$Lj r]z_D9O*d;'ITHSR:G~=3#l$Y2I<к.Fز¯9ir؃9jDYR]Nscz C7|O".9$=^|~9r#ѥ+wmO^D38W-_2,qq^c\&d릃䯑v7Ld}pTһO9x,]EG>&bVH坪'-7"Mlvm %G3iqX$)sIQM]1A9Rd2<2\ q\]/.k^! Oq{l#;YBɲޔ%$NKzL\}x,Hɝ $gU{WN/zb/Yr'%j^wԷ-mw3'`O蛧?o>6C߼k;tf:f=@ƨ^04A;k& Q1E+Oӭ'x388⽭Qy8N5@el0jy[Ҷq 2E^Ϊ=KWo|C/W= сŁG(3 ߙ 7Dq'řb\ݘ%(w8'5Rs硢}DT^ L*v?~?SRg|Ok!~ϝBb̗\(yQBԟh#Į 1?8K"BLO_!Z qg QS3V8KJBGB"! ? w qQݟBW q? ,Dk!j ȸh)P\x8 #I^*#aA.m]-K#b,86$Ug>\u֖xТOaVc)˩wV+[YUz<^ ӧ[ƒWKix$] eTy;m_A|7(;aO7PVYXT[1DO,]:`&XrN c2ʻ~2iNGu1lP7xS hj񉀂|Ħ6DG-AI T7VeiV>1{=aA1ˍqfC0JPY>X}/Tc/Cd'}!3ǚQ]-vV/@圿#$-63I +{Rt9-XP~W /.t;ҒଖY<3_*(8Vpēa|_ah Dӥ 6Z"/7'>R <2.p/'A͒HM6Ptͫ{k`_]Qj볂uIBlD- 5{.;bzr4,̺3T,QsTZ VюYo#  %Z S(,jC̹'~-~7/O38GUah><čHR=G(hTL_5mWS󗿢~$\#){|~:O? M((_jToEBтܴ#8 AkFYG~*dXcpT` e;u9 .ք*~+?s.Abjrz58-b^۴vS8tM'%>E=(Npps0oyׇ%OϽ 讕MM" #wttKAӂ-Co? j뷂 |·]OL3J&2fhR}2tj%wX*Os>( 6Ra՘-;xOJQ> !P"tL(}:, 5_C4*n3W֏5i'ڟwJji-Gyѱ?s'eq?F\ZԏrQ9jcrt3Sa?:ͮ1?~#7? X؟^QߏUp,I8ߟ[9gBMXڟa)?='_?/Yӏm؟Oџ ޟYȿПv+J?j3Qp?=ߟ~$au[%z@eq͈蹹'L Y+2V:C 1%8+= 1*o5cY)EßRwD۪MzQ`j7z(xxc>2T<|` R lZ yI ^/c9KS}.{/e*; md7>$+S]f\[$G>%4ݵއǾfMo^A\}PZoн/iLO7B_*!J4ѝE CPy;_k'4.KDdO!dmQQIٹf޽(s~}t|qjeiNK>ġbѽ n?| =xG::jV?mHtuiǗQaK047P7y9fӚQ/sI [E VGT% :}]&"%BL+ZwҾઠ"\pOʇeovnKpƭj)b02\7LY[ qoIYUJsyK < J`ݓ ÿJu&W":0>ދN:o?<-;fZ>,N; k~ݤBX[>qzN^و^E `Tozlrzvݾ>| 8}cU,^6/׫Vllσ2yww9* A=>93ҮեAM+yٟ50F'-L ;"zE ߅z?"s#BHs᭸9^H˩ͱ3dÎЛ!n}U*gCGЅUuY:4Vxi7Fg7fp#ѸLxi"gO&ۤW ̈́Awz7q@Kߑr9p.qswl4l%'>LhskƃK=OAt3 h5&a܃K׮ձ\*hpd0} er"'字W.Jdwҥ2q9FuwBvȅv}9F烮fubM#,;+qA(9)k-m%K =wjmrR)بP]f[AP5 VC V,pؑ&>z^)4.F3Wol6B%S-f䷳޶+ѧkStҐg X-qH#+d{F9gKT2pe*r'nD|zdsi[_ WYkBs,>x$|!kWF t74]*W`MDi8fZ$ x6JoEt2ḧqvV:(H.LNM}8=Өo08\IJүP )J1_vxc/0ᵤ͟~ӏA43ʝ ڄZT54A˧R:n됄FztC4Bi ԼTΛ>WW4@iՃf>ai0PG˝s<9ߣ厚u0Y||TU'XE?̮ ˜7vJ4)NhhS )^c3V6Vc}tUO]>v9O xuyUZ)ګA9pںjP#?ԧd <5WU|D"󩆐VI;ҫ Jn U/g-9¿ z_\rbӨ*CzwU7}: \;| |=ssys!~] bNÔ# ob# q>}Bmls!k|SB>r9|=N|SG>{.tuйPk qcs! L|]bpSȹm|G>{.8}C7Ϟ #g)'cja>#>9;a{v΅s8\8|m"t.4U\HB*sx| c{Z9f8rM-fN9/ȏv(`n\68Dl`y0*=w c@]m  uLJ^P]H,fT6!Os7>pd?m1MD:Z~I›࠿ѺunEΝ_ַ$E_OX)*R`n{A+Ǽ_'A{=TnKN!la$ÅGQ׏o^c磶yG׊iwn}T1[ey,19`i܈@qL&| KRGŝs`k˟ Ce~Π򟯝K6|<'IR] m?-,Gz yP8s{b.%rkz>)lj vg//_f$AvZ<bg,ygYC^*["̳YE/̳:>d.Vlse =l_ \@܏3509䰬 վ<%Pa%15(=ePqaJp)T0Z{AyM'x.G]:N2Oj<҃ރ_fM^VmI'TdSJTJn[ӊ q>9?Ӂ*P3l)vl[?wes|=կz_%6+}0P==*?;#0Rے(&LhW.< yp֡V_,`RWz2G;iN2GیKyqmi3o)KLJPAD'm(FDT1Ӓōs9{+йĐK*oikِhטL~k" Ѭ[,G8"}.\Dpd X(r2~'+hXto|'c m2Ʉതzb>pM9վs' !n<4eU2j_m-yI7#\tk3*}CP.Ve;t*kH\4N1m:栔E([Rg)uh6>j,$}U7"[3R!SEߋsPyz RO9Ӕ_*a.^[^֫gKͷꗿի3LI=n?5p-2.YK17uIк7T} um8Qe_Fhg~f8τJQKu^IB1QTqں&e(eΣ hvIأ.j|>CK'u|_H?{\ͿkB?~ٮ5 OCX?FCk]N23)]UnEGX4jæ&TqOo'GSIl.mtKoR7Was_:&r}v5oI^CW: >q9~3-:ƊI̦l9nJh3!5U7lu?^۔,q=ݿ~{كٍمىفَن 'wo_1`~lllǬYYYYYff9{2Rb"B̂>r10s1s0103130aca|KT̔>r}19&_19&_19&_19&0cr0 L `|)`|I`| `؏$0؏$0b؏$0؏$0"؏$0I`Yi0KIIOƘF넪 {)K0`>UzyR:"nߚ G#j3~a~ %> rp1YbP9.?o2|ᄀkW@@aן֙5L_ ޭ2ҿQ-g:OX3;$9³M: BTmL);a £7/YD\LN:Raj.oUI~lDg׷Gut}#+\{?8~Aҧ㫨<Ͼ4.w(u;Z)~?*Q;@}f1өz=QK;/Cl*>|\ P^e~0]~nn/[A?6nP1/~a0N8zv3ww)Ӽ ZCb:K:?vXa~EsyʂPl%ҢtPu2ˆǟJ:iȿy]~h*O 7i+|A@k{C8B}&Wxҕ0$$A 7\VMt[J_\0<914wx7fLLJ세<.]Lo6/1S) ~\Dۂ}`OTpRN)}!h Q,' :dKL*?P8]z_"v.Nis?t5v#|?(@ fE2d9=cybGCe O*\~g#9f{ZRRcQ||i?AYy?gAuݺ_(wS=6R={Cz? B -=EAfAntyGv##fލ בniYπ;V~~⦔GFQvy.t~ O.=L؉<iVKٻO\|ǖ/؉n]*Fٍvđfmo( I/EnOSLxzX3c3ޔ>ڎ|i'mHLt$GކNxvm.>>tƍ2b_ك; sCgڹast׹;G-l *Y1q` ]M毳؂V*?溨q7#WcA͍ܷ.萹3_ qA.&>ql&9P՚ѡۛo@-|}hf|6/ν2(X&::HvFO.OGd32 rH<eMZNo]5-Y:Gm3Gtgg;/mƟL?v(9PFuf|~n\xjEO:s[W %}IqtW9 ?fG ˑ;ߑil sM2m|ໞt}";EA{?e @D[dmVBwC|t}P9=msHN:+FS~ z.)d xrNC Ӛ'ܗ^e$4`|_p&l7bj]* lc +].vqL/N5 ۩Nv_t8sl?l|p8l?>wwE|}ױ} b qK8El_9.; qbr8.19`?&2cr.|a19<9|gsOccrO:&$a cr9b?&ccr}u|~o?CA|sۏ{w|]ؿw`?&_ߎcr|Wߊcrq>lؿ1ug|19k YVb?&g;`r.ח똜%ؿa?&_198B_?9 ·>'V+ H}@YU[M}u%GH߬5|@AV_KωV?\9~!K𛭤,`'i:*RF7D8 ri ՠ_v뒻74=g2ՓKS"S貲N^v[nǪJetdc%vvg<*^?v0# i:Hs@v;~}YBN>_מ{>a_XXWf)14m?O,=3yjW2iG[M:Čgc D=c7 р+V5t@w^P& 1HN`i 7q0מńco߇:iG;LF4m{_@BF&&T+2 # w-]ٸfv#QAA/DXY7K#'E&皗wok&f淧HmOG#U+w_q l _!#s@F"O 䏄 {w`$drFuOb\P/cf*HӼts!C`:Jӣu`4o^]3T4DY2{LB޿n'..Z|[ɬ 6N`+Kf\B; [ \.198Uu?y֯ [v{GՅ6zGqa9pEoOkV3qzzc.,Ճ Wl`Gl W;%C wRc}XEr(eQsj:!RL^X\D] AFiFȵLQE/M}n2*ȼ{ zW+!UxJVf }I,tTAIRVԊƫh ~C*#8ŒaD|.QFb5vlBWK"юujo a_]%ҿ@2Rhp 4i)#3I <8k;֣͟SJ䕧umpTB'_<^ļJlZShׇ^EgT6֣9'"Zo">>TDJ)*Q5VLU*"[N1Vn5f&?иtԠjhXzHoS hވL}*jҐ%?abwxT=+.s! HЩ>'*q_U0?<[6y#rkv3ӭGoQŴOP%JcިE35PU1~4h{ e~QK-Ay<gǁa_q %0K8ڑY?ӋbηJ1T;Dֳ߼Ő3D h{wC>wsf/RE6μ?Eќ1g DǭƎAlj?E`'WO>L|+4@z;n&18+͌^hnA:Aז>cWt+h(LzyOm5'Ҷӗ?<_LmWó~$)-\6QoA#)K;𔔯5x,Bi?y7+ZOu]v?{;yCr;4C'oh˹s9L|xwVX_”UɌoLko%K xMæiff_>'їCz[yv;Fd;fiw{2#vwo-&M`[ʄLzyٷTg޻<"z+y%N,e+Edwo/[~AL<+J6wo1/O7L ~dw2I7J}?f;,oy/:jTo`C7w[av)߅iqs8SnooOZ6x's+|/Y4mIL2kQ1K/`YKׅ\/dG=dS~xϛ5N>v>WSy:({&h:ү;diA*ݾH9Lv֨Th Tpn䅯&_79|d2MɆ?-JO쓪 W'ƟMvqO=a+Ǘd3$ạjj?(!"4hBݹ)2<Ҁ4M<+iDH4qҤ4jRs4N=Fq<%)!Ȇ5#0m/;L@_ Zb\:'G!FI'$NV.'ڭQ:'1"t*\2NvUytJ? F4je#_4atb@*^l(HJ9N(k=\|Z}n c_E\ɫv5cxʼQX1kr㈎B3j?*r(r:mB1:1u;]._J8c@2:/Ǎm@l L():`IK2'5TWsHO6)pj 4d<Xb"ߑ0Y~0ʱoñ b?͆Rҹ*NC=5ҕ? G'S1#W=@ph;jB1:2^]Cw}16*sqzF{m(t8HwV9d$E̽N{vZnD/qP'@&#~/Xr4='=>_4S$WYM[{9 5=Y2ZSH U s5tvG;sN–[Ց/Gu:JqCkMsB0:s©{5ͱ"$b&aS0Y0q*9{tɆZ$ a٤t ;ԺjB.9VqfؒA`u94vo2 3rA8n!SLY%^__G,Kɕ `%'<,#u0s][?`>S~0|P=]^шQ^ KTTLN72# Z5cAhoeNl셍jaPZ^X/Q9yk/=cDu|0xd4{@Q$e6S>!] 3kwKnݭ/) ܻ-OcɠNRHH3EuAdʪ]P)R)5}/z]Q!!aD}Σm m)H}Σ<;}# J">ך5>h -ԇ[Pn>B}pp:(m3~B}p ͽv^'A&V9Mt=7Wk)gb2)m{ž3>;"9#vEh`Fmݠ95ͳY0*gx9D\ĦC"ϟ7ek#e*a|i-ld/{;;7,%_)F`j[sH?.v9Re  lw Zeed5uLDNFtG*2~B_'{]7s}J=:h ﺳDzj0itcMly=ù R6\Z/{e'y?&di>~=yd'>}E&R).WAx|iòY0ݟi}vZy8"9%^! cX4Mo,K~!^{1-\ *ܷy|n}Vt,HY|8T*d޷5<3l?s;Uq'BtFn rUP.5xp;z >4]F㻚zC X3ܺy?*pT-fHf`zp~LSG|g%TB+_aT[QE\ <xmJ4(h4rܥ\\&!z4*" M!r H$ݹ羿y?_yϷsϹk]Y.Y6w^-?7lxJ/6gB;o|yK˛65\ }x8eqcu%D.v?yo g;v{,Β!r9GԨI2q{w:ݫg'Iii;<懯wc /?(ps<Ҍk7#^%S_]F ?E5]1e tn&;o^~͚L7*^xv ,#foM5]WZ6pLyod\YeE! ϗ Ո\[1n$_(g$s^_Xϑ;l:̗'KٟO>taq^&L?I5~cU6MAuW͆oȏQ^͉5zr9AW.Ϸ?U[^j4&b8o?\e};/ΖBI-:˰osP;=y_|ƗC/_= `|9z| h|i? C/_ @_>_>`8}C`8}hW}/_>C_`9C_`|y_z'`87 / @?ph@|yg3ퟁCg ??A0 )MoD=F)̱"/HE _:H_t|AC :~. ~#tF H:~#o$MAt|L I:>&$c H $?HI?_^I;~RC)!i~??+?#i4F H?#g$͟WF|y$OI'I$I~II&3/JgߣѸv}MNRn0;d@w{橍ϮOA]c?<fl~mF.Lj̸3|03 ˌ+ç|V,m}cxZF^'Չhr~Su̺bRf1B<'c_^' vLٖeEn20|5r_ 0< 0_00<8ٱY߁}}}h|a> o O?]1 M hr( cx'_dx&?dx? <O  ~n_gx&>`x&`xO`?a"/a =t?[Lx|12<1?ޣY8q.ްv3,y%<k|5>`=_/` EX, ,}>K;;;;;$'eRX'meI=$Y0meI}&YdgOf$k Y/kd_$k"WSd琉INw3~ Y7HX s|8}nӟ0wǸ8?Ż?}OOFR3桪q[0Oֲ uI{O1M_Cvߍ3(@,d?GZqmMZN҈AZ<~f(=r[(s5H4f ]zj1WԪ@݈F_,Ml@eeau`9d&;jCX^@Z<ab)z_FoZNV47ϰ@׊[fz*MݱdK,|6}mY_}YsaP e7@G6B˙;g8*(,5z@wWl1Uy-< bX{8uaZ{y;CcAbd3C:y#@KGߊ^w$r`}iSR3fN'"F' $ޱ8hV Dr|A- `;yP-IJ>5,א#6bZ\eGָl??H -De&uyQXe/laT%w/,k^|%Ӕ9cpo׊զ$:$XqFm{|퓠(x*Lbϡ/zjN%vGr=A_)_K]y' Dc8.ZނOlǰ~yx_S]4> g}/"kv'CA[I9IӝTk:g_,*%^S5.jĶ?wQ߿d@~@ <۸˦fǏ V9ִch(ҝ7f4FȄuYqc/Ro*6^87%R%79 Js:b TYϥTE?2 U?gzGYYWU] ?ۧwτD[l=+)zp7ֿE=aMyϜq<$~_hV'OՋRsES$/]rɩPi e\ q;V,˅5 SK0>_d< x 5NuC):]8 ?u yJ52;_Ce+sg[.rtus+dmǪ= |$ȹU[?Dd5> yW4Z6Ove6=2XNz R~PĶ0X. Ax~Ŭm}MRj=;F¸p0d]^Eƛ\Nz|Zs->q#7͇_?vEFhڽA>R=S=^BCH!վ0NV~||bmVTtuq#fn{CҚ_><ǩmBYXX`} Y?8RY$a.VV]i.42.N2_ a*T N*ARyG\ٯe\#$YǦhoEg k_#4!8=%?ވR%4jX,獿oېFx8MUG~z ;z;W(]~w=w&{: {ן|U?xv6 _ѳGCΓҶ.FȠy1>ۃxվ}YP])?Cue h,ܟ{6}X x^0ߋgc Ԫ?$ok] l 3oM' 8̖5B^qz SƝH40A6 BXexjZ O/SofӥP~2\{F.ohiA)<ޒ>TAoS7t5l~FOJJ7 LxA1e7AbZ>㭍ϛtˠsuS,C28r˥x~TʻbZxԈ.y;OA3$l^]}>1Ȇ$yìKa<2۽THel;\-,X;54ÅW{ 5ך}*hǷq*ZqUcB*HH,r<~͝;`ۢBڊFfgjR~ھkA5O33`rҍx<tїjp<4c&5 Hi;S~g-g6qXj/O_ZX)aKz2]'!W릞r_9%VN~^עlUN-M25%?)> ց%._ޒm/sYxĚ|\MV* Uh%x8{N7 M̯}٭{ﰸzy@#TG E:׾A >;4ªY jșX {!x|>驸5DaÌP:D+œadqW7çu7(%?V8-'xlrw}zwbKUkҞ=J`i9 w0_ bpQǮ&R 6Y5 cf3Y@hc"(c0kV x{yĮ 3ղ*)u~f3c 1M9{8jUѰCSv=pr)Ƨ??=YO "9wQ(5,C*~^'3}VW0.7{&EC+)xn_ 0j7v=OR {3LףO/SB~,긡o((YL_5YD)9yMA\ |/+@7ehWl% &" ƎKL, XuƝǩv>EE"澠`&Yg9ڏ=`@#Ayls7!^PTw|Φ`\q+<`#YlVzwt&QUhOw6HQ4 T'qc 4NJ`}DӯVs-o ]a4\4p$ns;NA~y]Q|(&)׬x PpZpQp{g#vgGޣm~Xrx_uVllEA_ (2^E +-)ڼ9~^8:q1կ, c{,ٌG9afa}$rAD`;rm;BMZڱ)k*_3)hAP߰5JhJf@Ak tß* !TYG qh0vkn'Ns…id-0§aiv=L#CjTpg3W\>XEbDDt. + ;os- $W qW0庒\ԊIyي\h0!W9rjq\^;;k?!s":˅+2~ZЊ)0⚿6͐zʅ&W`,I q6VDT|3=Z^gqri7=nacM_DAEWtH<_R\_̳ kvmZۗ@zC~VmT ,/nl79d^6wyt{ɏVԙ0i+/y K$Oor+j4J[FO_i1>B V@Y ncM>h u2[cW}*GAD+S WA皍^5_&[mښ3!-p6{8n͞z-`tgnf}֞`Vr7d3h}3Pb3< ً/85C]q%M0ׂaMpx}S̛c:lSjU֓d[FPQ2h4n4eTT#h{96ii.MIn a_RmhGe=&&CV\rzzQ^hXNC-]bu }\Vb-<qqvs&mTU. j G5Yt?L-fipٕDq?Ee?Ci1$ 5 [)R xn>&Ljʠp28Xyl_pbsJ=7x^RK{Ţ1SvIxCtϵ}{zߑ/.h IxG0ȫ_ܘAW`Ze#$ U76wZG.Z[?=fG~=ªn/B?o4uv/V./BGֆux*i̡b0ibqw=-!:}E ]w]]! Km̗"wBy8yBu 8_Z ^-!j2$+.tDF}{+ߑrF1}wrյFw8.8BױAs֛[+ ;{%N;G0]V xAb>WGƇugE4 3 ^=0Q"rMU=]WVEEO|~v]GVI錺w$+$nFI$uCgJ:M:OŠ!) g:bhR.u_ŐGđxt2]UZ$2m_đ o붊;2j>j!Ğd(4l ".T5kLxt-@lTlJ-]. $;lEaAs{n]GUXy+*QBgR7ƋĢRH?#whR8J~nZ5ҤQ̪=GJ>NCN:j͚}Pq j}гK}PLH=Z_twTwQh_Uv̎-tѾ(R;uEePQ豐N j3n 񝫼'яX]M0KC#ͤ|u!IӓX[Ou>egz:?~&="grlw=Ali{IT^}O-O/}z?@٘ 0ȷB,\)#j./Hd&{!S=Q>ܞˆ8[{ncݖ{ g'p 7Ļ,a`7ē,4e0 {a`8 szZP=1YhBEV,<¥,dꞘk,¹,TcתpfNf [+{b CYx6,B5"6?ZO+mtxe 4 *-mx0HT ofSFGPʆ+f@b"FFJR*L-1Xñ"3lJ#[ 3 nJRߪo4(,wA pvZ_ \M/(fI)͢9 u"!%4QJE4"$"sSyN4yݪ^~y=g Zk:׺9Tl-Q8_:$Jǝx^u6b'öyܙ#:~>t\5$y74y?~y2D-`2mgS&ao܇{R.pji!?`n!^͓EqQcd.&pa̯; ɞ\Hǧ t}B!vB'tw!|)'pGQҧg ?D ?CtOuU%9jVDyNscOӌ?Xtj(o׻x w>aw퍀 ?MwɁ5Vm!VE,],H%ng|]@񢄓gbꗾש†XqzΆZxQ"G˵O.Žkl\ǵnedHbLVyfv14@7x7ҁ:8xa^DH"GAlݍAd|߳) zx{g .m}lL?Sl~Mz6;> 1~XHGެV(3l_s}/\\L􇂮S}s ~7__~@%wFxwk(F䱰{FsuȩPX*O bD CP{RĤl h_=Zam|Ѽ v1) VM5G//xpPλTw;izɫaJD?mCؗ'7?|g~ҵQ|iAR,evM+g:; 괐ٿmgϸs߱7"i (9|BMG.ȘT|:DO,RIUNCPal!co%κq3C9: ړr mٺ \>rE>1?%?)Hn(EoX=4-VlĈt2aFW]Vr~gf~se7&|8薕fĂUw= ƁI|h;Lj~Cς{}gAPt̩\U6EMfw6,uI1/`Ah!-1mݾ/Aw%_.޿MFƲ\O͸G5뒔l{"4YO  c~?77"KRCg+%$[,<$,1_[Ow;h:4A|.iy mn:'eNߜb`m;;<{N|D_9!> 9bs+4,{"`E8 {QtLpVƔ?Lptx.4DH<'T? [";Ș62yE>1y~aض#{"nJ>+Mws,^ gdD"1e|׎t$K%:4WM)̩.f43SHր'rs ~ڜm<9H_,Sm硙B蕹"xT(O躓S!Ϝ.JlB8wE|Ϙ?p/ƣiE]df~w3Qh59͕c12c1U1[Ƣı({| l*CecX"EL94Ƣ珒1(xc,ltP<bY4c =ǢY_8kclXL1xX6;b:D᱈챨?EOVqXI=cQVu,rǠ屈mƢfidcFr,:kc},bca b Z0c& adOF= Ʊqvp|&"6t7a_C5 7F8MHFq+G ՏPCqv02Q #qt~'A=79> Gt ?(nBMخM% ?YHqFHDyP;3쑸07 yBis#?(rH^> Mt7aCGrGd?&(k7ICQq5Gt}.٨O. Vŝ!ꦮ}ћPN*X﫬ǨOSr}-!+R< }_;%^Z)*ѿ+>BVљ>CgF-,=T3yb?a{]~9і߃Uu_owQ36%5ZY ut%.n͚Neϱ{*qFOC&1Іx!V!޸hNػ$h;Tf9T'4prxCP1uPmB f*Ĉ.YD{ZgkΝyDa6aj5;"owu}~1C 쁡{wcy'=,xQW_LCN'sNz\@d2!'*}i[Al5qN s :# 9\O4{-aS۶̓}rdKɼ&`YOJ?IpyKeJίr1xW1|%qS$9s~ocS  wf ͲOX-y9Юn̮7$[>1 /ji:a/ƻ㾬$֛&ีS ]>31,<Ŀd=Mc/?u|]}~d&n!wí}2F?7fZM䏾e 6{b--Bp4B/3.Q|<\%G{U=)Sq[h8qgnoe0OlGk-\9Y7puBt 9ukxeF1&^WE+&k42?:btN[NȂQ8ԯqh&gc!38q9QВcLt A!E؃aN!lEA/?@7-~E؄azuk FgU?!DXaR% #a|ys !| _!|lYÈ ?{:'"#!!͏1iHCȞ1)HCCztB6/##'#=B:B !{##y!!͍1HCȞ1l.GHG!dF{ *\9Qo_ 5aV|2 @oɴwzsl !1EcRaPz)D/4Y*?kmk+ [Qs'9ܚid }:Lɿe/R ?&峇껦N[r] %EXb5}w8f&^ t:!ׯ '%7qWN\`id={t` ޹XbOv)-DĈj̸{Uq7`6~i߉ @6MCrT d7W wcBCD.3 !.u{/.Q ϯV8S{׽G ?}5g~%&LhB#ےI6>^'_O S>!6iY« _cb,j_~P9lcFȲiQ s2YF W‹mLcY~psB+_ig-X~lQ#?ΠД4Oj?HR[4 E3G#$Bh\s+ eI s"]~m kI[X0)%'w0z_uҼ`}a:=r<̞DyBe<v&NFMeSiO?Ҿmmߙ yhsb|L߸$?\/|zw?k+ KXg߮pʭoި(^*>=`Fa>_X)(6Yaez|,݇SN#K-8K&o;3w~ ۜxMZhsڋ~wL2a yW]k:_7Yz?` !3⸼oϗM$fd/;&lOC^E'sLOi#e=~7YCʜ[Ei~>[41i~g攭sH9cIws/KM-Gl߽7>$OF$囈 HƐ<$:opHCr0b=Br'Q6H(wHnBQz$!F ɟ\|H.ArJW 9ɹ(G!-_|PHFrJ|,$3 Dґɏ=D >twQHʗkHNB/廄HҝG|gQ/QHDUIO2 >%Be!=~G8GH?GH?/ }G"=BiG !=Ð#"I#@q/ҏ!}0!=BF @z?Gx(!?H#!P>H^H=GH@ܑ w#.#D]~;oCz g;!!ҷ"#oFʷ m7"=B57 !}=үCHnxK#AV#*GH@H7C<ǻNmqȅW=qdڮOn:M&36+ſjn!IWTn3j`%rȨ6}lLhMs p96qPT/_|'chK^^ ;G03>'`3{~La$8_ X$z+mm+,}2?Uuxe"~r9LawҜ"c+7%֒N=E(ьS0AF?-{WR U0{VQj@'JCqpNW=Dm~Z"?g͘Dѹ TPTȝj67 "wl'N[a2Xuϭ˝bDja5]zM4 -dGP&w2>* ɰ`6x2J'C oNG?!"fT}gqfzKcSSހ~SaO3OIS%RM&DHBf }UFR"?<VD/V @))MeKNҾ/S73վ~q xmXq_c3"ᷫ쥝dM3TB|r7+Ust ,HQJPoz/+AVʊԦ=S2cI8вʤ"juhJ΍-Ԉ2.6Sg"j1WdPJT͋\"6,|E^i`?1%8jO|wl|y k%h_9el^=OsJ010i&b׬&mr[),>u^{ I.g:lU./Ԋ98Jkݵ,NE8䣽,h)zsLtH w~ R67aJpTi-RTvY r|cy`V{ ,lB_Oiަ*dp]WŅ_*$,}`E(Kkhi0?ŵ^}%Ux{Sm{s 4D舫ezC"xzIl2I?)!MX"db9ԪE4[ Ш^#UqߣFFO QgJƋiqoGI9ƿKyiO09U)a=Uu¸eؾz aָ/U_yta+k.OXSjj/?كF!j֮}T} Mkp!^IFY# /%ڤ+.ӳTRi|Z?{t 4s&}E- +4=xNxtA|e{SC{<7x(Vb?bcNodn)N_deB~ްr}oTTsuEzIje~I~'ǗrB[BU{gx06P? MZ|?L/_$> >rxYXCٟVm?![ă.PȢyp*/Z{/O? yx6?7. ʤpu?wDa7OCmC\iF.s|1T'sB¯&,;}g?ʓle _ekSJ _Ⱒ7!3?,/3յmòJy+S?l`mRO/|* D.W~4g8[muEHoi^Dٳy*BjN:ShǻY 51z3yԲ.V̥?̥djLuK1]C!j[0n`-ge_M;-X<EDP+˪whl*> 'T`}VJC)>̳7hk,_]X/ >JG{+؅!tʿ{? kQ B7S'78?jbleɦ7~F^/P?TϺe;u?h ձg>jZVn ۗ[bP5!Pz^$U> Mc`S-c2)JY5\eS*bq=[Jg({&} }`{_߰FPr+>S|~E 4uS˘K'/ݹXN 4Ӝs/8Rp` ݺH/1f1cx;0Sj$slOV7K繻=9d:*9|.d8~OāZfQLqZ~=Π{39൦Njv$:7kRn0}~~ V9:"W̏Zn窭&c+f uN+\Zz׈fZE$ |eiвoa{y(;а]Afg0K}AȺx9<`- d ]1 gN==ǧ 6߽,bYQW%K,eK?_>!Zv6/1  Z:B -<紃DׄPtO;$. mz+vPYzͧsډmq,ӥ^X;0 ^bvSC]cfc bKs%_ei_;ZcRuM[R,+:: }7s: 3ϥ:`趂1 \8E>KQGjX ڝ>ģs:sG ??]O? nPu@FBG1L#JN|9Dp49̖׬:| ;™hi ߜ+jse<ڱOن*네Vs:aKC<]h1.M fʾ̸ O^10 c*Ruq'{oZomn{umEt?_`S}]0mꐫIouQ:ac1k738ƪPt (N78G N,` H6;ݰd[EJ$u.R7kmUd–/kv2a).qǩsA#`v&Ϭn(Y%^`!| GeNi ,I[ܝB0EszL[X"e{?g̟ԹJ=U|efKhśXد=;&J+C[[b Lk%sz\}cډҋF^xvq{Wo?lEXԹLlEfݔ|os^`y.3 3{âvܦ>.bnQ[f^Xudf>]C"摦ȥ}.s@k[]_"|>w^i9O}qnΕs.ONE ~ܧ~\\lmϪ˩saG0WLs  \b .n~slfyr`!:F:7j̆V8c`aƼΑO-&زajx_ \vwU6>pB 8f>j)d>;p(<͐=̖M;m Bk -\0齴̽fla$wXsft+D}7Mֽ5mW3^ucTL'y~iŁsgmlW|ן8 ߤp O*zoT֢-;ܪ~'U3ֆNw EZ2qѵ쥭~l_xɨ JZf~byk]X>=-ܒ^_uH?G=b;żkAROb/y]E`+}>v8Lӿ5l8[-nT'` /P!P ~/[eAbVl:(6af_~.0YiTQtυ*eMc?}]13+¼:h~WѰ'oOrVZGo V¿7?/K(ܠdCkdvkκ-DA;ZdY6:Qr}}ܥia+|vQ)7 AwwvEn㙱J6VOI8H<57hՀ4x0J*f d#:Jͷ.mc7<*z9AFAGdMyfJo")א>< edo>3OKKϿ,T_h~mie5ToϜ\ntW[&E 0W)>{\TˊXӛ`|s6ڍ),Wxu[szdC4In5g sHm8Ro٬>|:yT.j;aumt5@N4L/~GJXw2:~dDiGe3 G7|oohNG|;^wF'>N|G|;8wFw;N G3|g83qg8x3w$8I'q8O'qwH|yl_gG7|oB>l z5I)Mt:p^ {ũ1qG{lo7=N$wЃhM{'Fqs>۶a=v!.4gQ$})s^/}$H)L@"h8 B0SKs1=a /μo95r޷e ps ˡsތ\K. _pW$W&W$W&!W&"#W$W&W$W&W$W&W$Emkv~m 9swRμ8;@ , o^B5?zrf~i~e~i~eY=uԗhM1=eL\&DkJ~6!ׅ_S]'s*s3c gf48z9󺜈w, dEBVdgmǙLo=ڽO6';(+G׎$N|J~ /^y_ 8B|8̀o/_JN_gx{#b?Oo0M {dxfE|ZOm{OG2bJ]+}exHʟh=H:ʇ">$~=h}'f`(Nq%8Ľ8ȃx_/ DԪ=$ov8 L=+T=޻,r]r<Ѣ˃ӟTKN@|>'v*5N٫ ڿp*;vn #<;;qw5KSb)1S5̮RX&{W=,OC׶J8bQQTml c$=":+m8kH3 ]6UOst{ 2 PmQ~S26_MC}Rf*u.B<o~#z۩ifxRO4(]xR}&PΝ&x[^ 걋jx=U_A.QHi3I;#St^nOOEk[@Gв\)Xb >+Càx7h]t!JJah7 NZ\x59 sPz}(SJ)x~fYS:?q_N _w(o'?$P'r<ܶĈqO(;!w:I~~>S|M>+>SF~ W/E x_ UZ1{0JO)+̗1D)>pHPm.;t1(~$]^_\['gw?+Z+Q>ʯD8x3pYRJOqJoie(=?]āהG/On C~dP<]xɕA)ٮ gmxA+[fȑtmǺ\#)>rބp@H^3BҚfkv1(>I+po۲U[ªwW^:{7k﩯FPF¯sX-aM'6x%t *4@xcS|Oi] >aP3vm#jZxM{A:u#ŧ gu@|~ T>ųmwŞaO=X<+/1~d}O{:`2sYx:h쀰,׽#S $xPJ4!;1Sb ^'ū ŭPz; ςrӓG 3roĦ|^NŮꟍƝNr'˰j,VCS.NX4uNZʯ0Kڻ$yINPך,ųM;An(=-[t„f~7] 77EWm)oox^2'R|NX-ݥq?UפQP=e&@f_Wr'X>}.?pׯ/:=*N ՠN)LO1';aϬ% {}kNw`T~"7KvB1sMNVd?[Z&uBklo E ai9SX#SNX>#-x:5b/Ӂet 26Z_1(~y3⛅_u´ yMw½LZ ſ'5Jc"$Lk_]?}ե<[~$z|y~"Ow`FLx|hHǿܬqY7ԃ#WuϦ⸫)o[?^)P~e<[7vonP>}:oL 0vn)+"y蠼fF~#sBq^4Qaf: Q?wj] <]w £͟<Օl{)QS[?WDoyCM5b*<ޤc]xSS>aڼ.BҭʏsoW5$=Y"c\qo]z%{QM]xn 7(j[/WM~ߓA:Syv+H:?'{)H^.K.P.?rPЉ¼h|}U5{S{5/5.~4_c۳$)36=U:WS>{|5[<l_+ڙF;<ϟ\1uxnqP 5c-*ƹg6|2w?ˉ'd%ѰNԔ|J))_ZK969dnk(5ICs⻟/x3?u:mXAaNlj9S3G̓?hʈ~B[OD8/Ԕ݉!Ѿ%AN<5#ׯv ͵RS~^N;_AăX(?>ԉ ^_Z?EaN-HTE'uGwDO{'x̊w UW SN~:p-6,EQ_X+Tl/.Ir(E& oC.5ԫq*("u:c^a+PNiMrي`V$8/@: H=U;mU 8"W!^ vVBS ;|MVzsJa꩓c dW=JގP.?:wC(7l~–X( w"uڳuEa+ n+ 2[lۑ+U+lA!VV;+]5/-S\Rר/uBxA~vOzPW~\~wg=-^Yg?டRßK9gy=<0w=FlNq=+]Jes\zޯw"ݥ\ +s=-5ٗ~MJvnW<}d[]oRzR?l ͫ۳ɯmNWɞmޝgGβ:.l.{/wg_eOv)Lwo3; ]8w}B_5]WOZ_o5C^o޳epϹ+0?.~芻\s͓{'I|gqgd#uC.l=! >|"[/yШЕy}{@{v|&؟=طN,CF4uu`/֑AЎ=Y4<JR:/JUC_M{v|`6SЏrTOT\. -w;ZY,=>[9ʒ?e'?{5ݚ+\仄~KvХb/LS;-G,~=]oT'uzs4]ܢvוΉl?}~lw]oW+l֋^`^da?D?P[_ [İwp>qOƤ^qy]3grۏ8nenvvndɶ?{/[Gu:wv~9~sӁuz] FغO>[:[Wl}M=qr)h>,r~.+[/ :bٓ4)3ٺvWgy5K/,a8[7+/XγL;O϶l}:'ֳ~/`3{VneW},ŲK3</>Hsul'TSnYuv^vI|,l\ ,:  fz UuD^$*sN.xIрR$ۀ]3n~$@`,PX8Amz gՙEmu`/8kIg3pz-bzA6.6IkHMͦYN`SyVj$u:1hbfj2WɄM1KҵR͉͜њ]j洺dBWB`sXPÉF5f-Y)e5%/c*+RF@*Ӗ'9Qs8Eo2&c/b SZ+:EX 鋴,4DN[3?z g0Y 4Z5l&0*8 \D""8s)nFPgQ(+qh4.X7Y-$.ь,Vu%!zћ6G"DLNtIL,$hFb&UFSLO,v!оڔqqZ3[d+iK qQ@N-rQn- TKr!Zʰqt\l2C"a! b>=8.tt|${c,myJKVy<%d) _3Pg

    jbV%`Z0C~1g:cMF39"!3-!%a? FPRMQ2l]0gDRJ"k+.I70w\ zQfGYI;َ%¢*T*ך@CP,YP*2$mVۅQ*!#^ܻhZMXdJe] u;|nVԶ`vbನ!F=[T)Bdz ikkkqJKSz \̣\4A  _+nEƶ1h*Spm(Bc :]å5zWVo΂4*QXݝCݯv/K`7-j pySڭG_̑j% d˂S,n !D@kK~ ܕB nd1=Zo+7@ZL4[)͜EtOKsFo1h-"Vleyd\JlEE>4ih+qR 3VPrd3;HG*HMkҶ *˯=z宎ne|AU8*( *QFx." x \zK Y>uBu uA+v})ܵ*4{ e{(:@""HQK@-]R6ɒfdCO6H"]zGzEz#""6xvgy~^Fs3ͩ3Cz}9h0轁)$^SA{{y"ep׀t#\yf!C^$&eA_ lbz~hE-T^"I=ƀ9a2er7GL4AATHN%Fqrc?z|:V:gF0s>N29uE?Pr'YNFON]{sc8m҈-ۜ`VҠ OCSs90\BaKY?~eq;QDdQmҀrD3@DgŽ;4*!̷w2ʿ?50ECr " 墕hCǛnVQ2Eu)Le*+Ӂ8T-Q6V'0Y>[kN L{dz3؇qZ %:!]"(J@M dsF`5g 1p\%䢽nSѽhr$OɦΏ-":~@tE 4\t u\AyD ݃5&ÍіjAѮGɴ`L}d 0qLtj":5(dCs >vC$20[;{ K1t|L}&>]^? |$ [SX{Icr{5A1a#D_Մ@бvD04y}ߩF jT#M9 S ^LA?¶% KHh[0Zk=>F2֋( .;1.}]y@.~daP&-CbO-hA1BZbM^<m" L=0t'bH+Md" +6Kw0A?Y6B^(;n@\l<&l[a95(;' 7v3jV}y1-_}h/m0wJ4LɸYd }wI7K'h(Ƽ&y0xaMF9!=U_"w]Џ(ɂ|CAK 9Q) /Ⱦ`%b]AF!7^h.`e~cqz?vr  :t_oi2/>M?A>Wf5@зl trgF`:*;Ah{>4rp3 Yl$: 6pƣB2APnDwǐoMX<]BNA7SqoDȯX6CEHz%ȷ[q Ͽ)XT\,2sm]PF7bNźӟ0^CXGq̍dQ_Pp97dǚgLG(uh;݀Z*^1.̕X>Ւ%zcRZ u]&h-Q~%A(͹毞}y<ԾU=XX.w$-Jt|~ߡ0Y6{qۅXb>n^41<4}0|#(Y3$c< anR99 zKhU >80t+5)k!?I7GZn`!?aMnFPho^Hg$xEhRk rfgN1wc.u_؜Qþ矎>~u_%@_$J퐋^>Zr$#+`z #؋ 9o0`LKqGO7`C9-Q#1\ϩSC`Gp"h*5Z[`H\hNĠ4~|H0<+X7 ,yu"荭~9k(d_-Eʵ'X3pTc[XNu8$_˴uWL'?tit쩾@^*sځ9 L@gEܽ`R.CX}_:kT2e^[ueXO?'PwaA&z8N @sabdj^m a-~*dL΀> ejKx'68cvIbcQtxZ/j `_1&bp%>- scFJx ,1֬߆5|W;B`z ׽;l[`M/ֱO} 9ȥ1 bGJZ3Gݴ$*_,Iֽ cQ2EuF?d]4⅜ՌQ:v b2(c;{[c,1.aqTuNm\wo# _ ߖe^ޔQ< * 59-ɫ24k2M8c@NjЧ7ǚЁ)ƥ b$ʚj$RH7:pR\ @_%N_+VF`9 >\Bm@&6  ȳ2j`8yV.>p)}H0 lOTʃ.hZ;p ,l' XBo:h TVr ""h:?=:x @%P4B赍!=j\ aB .|`X 6<4.`0|e` s&x &\?;p 0.p \AoB A7`p06p\7mF=  3`X #"~|KAP]A2F+ lY~[w A~  Ăd 3WklGEp<Ҷ J*>h:+p`(惵0olb,j%, A&&/B؎z@IPTu@#txA Ƃy`߂&x\;1&"(jV =A`&o>p|~O؅z@)1-A' ~`急`c*xrF@1PT5@h Z ^0 ,l;Qp?=hCP 4m@$%X ւ8nG R<AKXA`:Xցn Bo" 4m@0L :-ߏT=z`X C"~J@mZ'`:`+<9"nT @ | @/p0L_e`=o9p<BǠhڀn @&y`%& a-P@C ,0Kz߁@>khZH0S<lGyp <(/(ʀ>h p`0 ` rC^,"@$8`N`  F)`Xvow><!Ǒ @EP -@[`5`Nh]j=ɠ/ 3lIp_@|v@YPA/ T0,'%@ h ڀF/"ly <)+@!PT5AC tq `Xւ\7C7uk@P*h: Hn cT0,k6p7/ *(J hZ @*x0,Fg5p< YW%G:hZΠp4Q` ,vp7OW'?@nP|0D z``8c^@PD `.@\y @ ZSA` U` 8N)u(ʃA]@ ܠ Ƃ`Xv3%*.h"A7z~`02 p<OAh (ʁAhڃ'H>0L_E`-  )( ʀ*&v+ |`$ 恥` Go %AUZ H. cT0,߀4@U@IPAg< _`- Q( jF)&y`5( ʂ11`< 5` 2 ?AkhxTAh:8} 0|`3 3* k R2֠3'1`:VM`8΂x = ?(ʃz%$`(f` S2v@1PTa hX`? ƃY`)Xv$~ B}P @K D$ 2` `78 .(R,ꀆ9hx F`` ]8 .-xA PT@m6z,0  0,+9poboPP>@-P4m@W Y`f`X{7TIG̣ Ht1R@ `Xu` ;p~? =S۠( >A- tq'p $ U`co)(JAePD | ~ 40,k߂;p<vkC ) Fi`.p| ΃] /( A5P4AWl z 0 LW`1X 608 *N$)$^W5zޠ7)7EymKP>O( R!*LE(K=*N%$*CRY*GQy@U*T>jTjPMjQmCuQ8էA 5&ԔS jI5ERjK=u(H3uԍӧdM1KqdxJDQOJ";%RIEnP*ҩ7ԏHI>ʢ/h: h0 4F(Mch,4&$LSh*M4Y4\|Z@ i-%5-eVJZEi u6FDfB[imvnC{ita:BG-StY:G]Ktwt]tnmCwGG?}Cѯ= ?O)=g3$&,' a+U{d[,{e|,?+BYAVfEXQ.+cY VbXiV}ʲr#VU`Y%VUaUǬjVfuX]VpV5`!k&)kƚ%kZHֆeX{ցE̺>eփE☕ųl'Kbv,9Y/bna,yY:,e2bgl0†al8>g#(6ac86M`$6MaS4fK6fs\l![%k-c bcmb߰l ʶlvl;;ʎdiveyv]dev}ǮnGv_o1=a?_o{ʞ3ιy\*o-wx>ࡼ / "(/K4/?ey9/+̫c^W5xM k:.x8ސ7yޔ7y ޒy$ov=xGމw]xWލwr y qyO6ޓ'q;Owq7Tƽ<}x_ޏ@3gA3>C0>||'|̧|O3L%g9|./5_ʗ|_W| _|7ofovn~~a~q~OS4?s_($ "xW⢄()J(#>eE9(/*,cQMT5DM%j:'D/P4ET4E REh#ڊv DGIt]DWMt"Eq*EH6S$ H"p T&"]}D_O@!2OdA31X C01\b-ƈb/&b,bBL3L%f9bJP,X*bX)VbX+։b(6ofElvCnG~q@aqDqqB|+NS8#Ίs⼸ .KⲸ"WqM\7MqKw]'~x(~į7X.?ğ/G<03KB$Y!B\+ҫkқRn-)WzG' HRATX*"ޕIIťRITZ*#}(II RETY"U>IեRMT[#ՕIaRT_j EH FRcTj&5ZH-VRk)Rj#IRQ$uH]nRwS")VR %J6$٥d!HNܒGJ$.H}~Ri4Pʐ2%% >KC0i4B\)FKc8i4A(M&KS4 i4C)})͒fKsWit@:(KG1tBV:)NKg9tA(].KW狼5tC)ݒnKwҏ='@z("=~~KKO??3,e!K,s!r.U5u M9G~[+#P\H.,r \J~_@.-?r\I,Wr \K-ב09\/7#r#Dn*7-r+)r9J(w;]r7l{r+V9^NeSNrSdKvn#iWN{}r?<@(gșOΒɟɃ!Py<\!.Gɣ1Xy<^ O'ɓ)Ty<]!ϔgɳ9\+y<_^ /ɋ%Ry\^!Wɫ5Zy^ o[jжFEgUX)YRꆹ=qaam׏X),,&:oF֏xyz;6oH_;)^P#Kl7%-bsħ:Gwjfv44UN0/|JYM%U ٮ󁞯0MVU^/(Nawp Vs { b2l\tR摭>tw.%t,-1nk :~Tug >Ѣ~m_IqEU`/H~tkluoYqN]_k~ߝмjXRѩ 2!p#^0)Y.T2zEo\bS-!;Z5)jrhoB)װS훶nelݴUmuPjߨ IL3گr^X)NC/}D]Xz)_#8^M5ߑ› ӝ$lz^puwd1+G+D;S3UF+I.#-^S9aMwbi:tvظ)zP햎MS|rV}p )Ϗ Wnob',hXFb TX'gU8]Vwɦ*ZZwh١=Y4Mqms헕EHT$2xtEStQ8S#POrry ۶mҴUpjPF*$])T, X5gqv#I=,nUINqx5գaG?g[;ϋBGYcUHe͔_Q]_Y*?oLv_vRMwfNSm vi4 yG͐0hnXOJũΈhXŪu v6mո}AK 8e@KA09xL~#4[oIĦyd1DX--pr#!5gSz{\?:.JEiڵ,vk MSVˡ*퉱Eʉ\);Ĉ[܆,AsYMeihS}o*8 "DӲNO39ÿ+Aɋ́5CXMbݽJy6aTڔݖd&G)R͉$[nke6d3[q=![uf%)?:@[[;)ؠ^\^/j#;ju8-r6:69{GsŦ$'k kUf)1Y~'Hq;䔏w4u4s&$;=kPf>ĺCk*7()0RUS4:?Y{cUpR9.3$mT1wc1Zsb`J$bxR #֡\cgQYwo\.MC Q?ɚSLUI1 :"Zi >iA3!)؀jqkTQ/Xc'%qWø~OU.t\OK|-͒\R9i\ݕ-Wplv(gLovZRaU0'%ױz)bѨHa3H=t=Z[]jITR;Dlb5(IVSF(6Q`TĚPljxI8ޖXJY~UPxS,h=ӯ(jNjMfOUkB- N|b7=-Vyc<J@1ꨣ8ZǢ[o|8\v&^wgs،Oxuy3X7G:@˭RN"G+[fC@C=>͍=ܾcQT棴ɥ^>GqDDI{O`z&bV\ZKnOVnRzXK k&/c -n] stR'Fxh40^v| ;q&<$ Avҿ>ݤb H˿U]x8Lv)jӡVT 7F-shb^sC߷h_LZյU>p=RFpDu%JuqUmB=Z .m7%ܲQ".!^-8bgs'ik:ŁRW]=8M~N9Uiwz4Mv_Wqso{ݺP=%TN@B M l CSJB4 MIVĊcȫ|yڒۙFFtbT Dj^C j ((ŪN umdnV&DuksGJUTgbz<mˮ}h8[ͣ hgb OuWP59nAfFm>I蒂KvDԀ]Až Ϯ.Qn.86sdqy\Su'TؠĚ'6~bի`> D+ڤIT^ѯlJVٯlք =ࣴ 1jlPƚj56Vcʿ`>cR~TӟU7|܁+6_qMWϡmnSnM3ǔt5;I7@7t:5ekʙלtS&]ÌLѫ'HODMէPS0*CufgUi&V5ȡ%iյڔ=%!F>QvWǮ \};V.e52eU u-fWnnWn˞dZYo4>]WuEo9Bu6h[{ܞ ᗬ8m gW-mt~6:U]b4UsѫŸvܦ:ӣՀD! KӐ%P QnS}18nFِf$a a'b3bsR5u2`jՁ hfZ>z6U-`uxZjd_Sv%jHT[ Q9U)[P ZV n+T*XYcMeД@yTvSуJql@30׆2h.@{q h1jTarƸns5.k!uH @.1М~Űy\-$C,zb .Q9:fS:] XTM*I5YU3IWS2i$'yTH *%:^]v _ռY7S NN4:mK;p p7IU]p;4V-C{+431.2ݩ74Îv$+YdR̮UVo՟NUkO6:2`1nrnqw lUj1k> ^^)A^ՈZ~UV5XVMI}F׮]C74Ctd/ƚfS6e 8bWh`ǁ?} =g4f=ӂfOm5o *9.92opl^stA%P*T*-TZ|9R҂?MI3e:'茠EGyՑf^v;L GZ#-#-0ϥ'.<ե纴.4MaiAsXZI,miopn1%e2L`g#O|xxݱڐkhU{ū_0 /,`dPՙ*% 6ߦN[ՙJR2vvrRrrf*]Pg*H+JӆvuAz85|nq:_i2`ѳW+hzdfx"Wk5^dd2xw ^ ^o fV3A ]6x#pڀɡio5.a46G^dbPMXxPj<$nzwpk㽁Wϙq4|i#c l.Um>W ,nki :1{ƒ_{~o`5^_{+i Z{e״n wi]_~\54ͪP6'XQy{}6t`Rx!XxYӻ"IyO}Zb8 Q.Tؔ?vMLH/kI}NY BŪAIY rC^y<^ɕ+WkMX{MדԿNHW3zᲧ(YIF7WE_ʎWӽCDߣdamkC7_-XYQ`E*BWTH-a>VG ۑZײlncܜ?'80􇄤?!$ x0HSAUQ_r6 d<#OxG>2IȮ{Rvh+ o M>_.&CVo4v5Y%jgo](PK͢ I2 h96DV]?^8b_ b}$vY{lZ2d["ϫ׫_sA[ǥ<ЄFE)b =rKmܾp^_[n&ßCݡGQ)@ѓ"MINV5v[@ډ l7߬>+C#J RؒpcMƾ]/[Z2sؑFmŸMv &`^YR(O3uQUjk}UbhBTW_+rmuCɡGQ'KOZ͜;i.4 -rۯ^zo iK`2VdTƴVocNW$MBC kC"F M2qhznoAQhې! (qCB! BV,w B|2d Ehdd%}ȿ!@>䝑v8gwW_L?<#:+]J@v2xʵ ^!̥WtWM~sonݦ<:= tw~N[N;]'b 6"-99S>?K33ulݾrjuaYnB Cu~xy<^/xy<^/xy<^/xy<^/nU# Ϳ #:&6hdOv8{ܞ4oz>x 5nҴY-[lӶ]Q;ut pK!_ ޾t49{&3bGa2Tw̽0X#&t>\؜s ҍ9t}aU .|zY.P']QڙVrlB*F k}w)(5kbš[z4M7=Dv]LH!>ʤ6cLX>Τ7{{xJ&|mچ?\@ԻC f|^  *4}Jp^JVJ8)_j}R\1hUP^MBӫv MUF %ƹ,&5!VE2sss>^ƻ/@^:Z2Z{ 5Ek/ȈkF_q=@7 AUPyAcBӟ_pUp n 0N`hj\R*cv7ʓ]WwUr&xBDA+|Bڻ^RZN_@nZ;HBB,65M^<1iJ>C/ykKUUgth'[{0s¥{fŽwԍZkى ސGY>3m"~3:ӷ1l[=knΓa){~~'@Mo,5Gگˣ1,n`ʹk)SaPs_dYk{vS~f}&kh=o 6fNM;ФeY ~jrut5b.Ϻ0>}]F+}O|mswhqņ=vJU~W^u^9;שyRK\Zȓ[]'YCYbPB޳~lW}ṋz;h\WE[:anW+5HޭӖ׻iB͆K<6^{ؼV}Ewcv|cw~9읿"ꕬ7Nyz}}s\F/w7Yf'}bK5 ;QxlKC;ZyA \75TU+o:KCnxaoH\ւ_Ft^\?VueGJ,}[t歡]:-2xVͭ1d^cBtˣ.?g~3rGWU OY<5~L=S30o5S10Geos~? fc}.}ӿ|H/)g;Kॕ/.ůO7ٝIj~+ \S!O:d9=R*a_S.bB?2f&fN9>sbf|-g̾ 2eFd~kkkkuu5l9(Ih_/} Dӗ}_s>/՗ұLl&Ӛ||F>MMMMMMMdd:3#3,6k>uGy&dc6IY ؗlo![+b6Ϸ-a |kB:ȷ/mK}r& _|oƷo|Nӷ{}{~^vg|aAqwH'}GQqw]g}'߷;Iv5w[ʮNe ;nα۾~]PϾ2{+2gsXj\ Ϝf`]3f߱JUNjA^@z,tM@mPX`3T-l{ڊ <`Ny0t=@^V4}L[ $QgHO٪ɪ[:uy&{uv=qqȖ]'٦#[@):zB9 dyO9oaxcDG2h 0 p<|^ 8@W i` .?3:@zQP=0[Nx 4@}Dx0LR3I]|`8ΏS)A2 $sg(H:!44)Q7>MN݌!|? G!||~ i {IzȊ!H`TFݐ~7_8_U/!A CG}$ _ymE0=A63Ѱ3ʋ"Gy3,%,5H_ rA5#![k^jZ(zQ~ɂFծW+9~{xRO {ޕ?,qjaq{,tgѫg ޮC.2n'fG&#V]wGS镙5ixPs=uτu|Rq<g}*lBZTZԘaK6t{2UZV6)g#|uu_b*63UNu٧~ܢ)uvU=wѶg_y'o~]ؙV=ό/E59,1g'z\-͏w_x݇٣/\}?G?I*okuj؀:g{nmשto.l+$]n.Gſy:ZU/;1,ߖmfrY_Θ8r?{WűDyAł%b7JpfvgM.vQA,׊؍ņ'B4&5Įk]"v5yz~Lڙ3;i3?0s-_f]Il;zkQĻnn*o[+zϮ'{&džnw 8{lW寛0mG#\7gOuiic\k?q5<Ѭ*Gu=*5\Iƀgm̥}7oʏ|}9J' }Ž1/~4kBXE~X4™oX>B`4;dh-^ߟ\ɢ8,ɰ1W1<ϝ Pڅ6|}=*@HW'_-\sӪeDWSpFక]Z\R4,g>gihļ桙`swv钌/BzKQVFA殰EիtӾ 9EP颕WwZcEܱ[_7^H[τť\^"+QKpeUK c/gUyp,,4RrfU ^fokJfu]*,,ga>?YW/e/SB _+[:>:^ԭKX`9v hSp!@{m?`E=_c Wk,S+N5X?`=>c9: QOB1%sxpZE%A񘤯!PNXU9=^5eoEIieΜS&7Mnrp>IT:Ngn}hG=z%$5%`-D a!DQK[¾۴ˢQsog%X qO;G0\k g}f lsm*p= =!hhMO8un %r ` u:Bj{AA%@Y%u6i_2?wJ 4vځ6p˩e0k ߨ#ڙ8썲! ^l1-Z켃7-1 )R¡ m{} y0d?]?`o w(+u9Üٙ#٘N]N+n{ /g&ԂN}_ WAN;m)g͟k l9vzK ٣Nr$azR7Mnr&7Mn{Qe5CQh<,Aeqe#p7<x)^_"^tKt!4eO VS^(gV**͔v$eWSʩԺjSUR#Qj:MTZ-HW!VNocMz0cѸmf9i sn6Y#Ϛaݱ:"$$#[H9OI>?s%Jx\E/KY=i[϶ ՕߕMy<xwIMΓ+,RV(辶V.Uר>Z%W;u4@O k=[ rFehiD1F_c1H6R+ kf<1YlbJfL0;= mZVuƺbއ Wk~g% ̟q{x뉼Qyj:1ˆ! 5 낺wh(JFg eܝFwr nCLN9& p!.M?R8'GDO!5ޗFKITiTcYS&4ny˝^PyR")˿8~&{(o+x).,rWRµZALۡ]תuzYh66Mje5#y=xa~h4ڃmB1IhKڞv}r>l ;nYH&"G1ryVNo2SZ||\T~Q( pCMQԑx-E[mվЎkZv[{+zmHC?қ6idl1Nf=3o2M݊6Yy3 _Op ʠQ(BVvOI E&yD^G1[XRxi4E%͓Ҥn#ˁ&scy0h|9M^}.{-*qz z@ l(q'ZOAcEqlfU%MLl28# zFx`>D2m=SU.+ה[2nF= x%d#e*bzqI lx9UJ"S(J+Cǖ!S[m\I{sc¸e79|3c$#sQ+jʻ$ַTAȳU,z$R.'(Q%\XQx.EI[I> H,'5r ]M(JI)iM4b-Y֏eUkMd,[r<lyB*WQ$%RIV)/;jcUV-u^=h-@m'zjT3.s|˪hհZŭVcM 3,]z]9৷ ML"!6 eL$(g@+!zkd=#E=G{FMhk,2Vi a8wS|VMh%@[N<4ÿp N3oSh^kd8A\(.7=gK4tL3Qz^hVfV d9m3V$(62N\Se1f5dkʪÃhޛM6 V -yلD@K.\T`Q[zhq%%T yQ_Z~I`&9ьeABz86bIc)l)[6m3Iv wBsfk98:dYy_gesmAp pHqb ~ul>eطHm-iϊAjFmoGL݌2׀׿}J6[ЂS*ra69̈3$)) ~4H-u14iǏ=|hj/N@O$r_VVW >[7* ̭e%p.rQ?a,D~R,fꡕj4?jA'-Kp=n v@bbxF9[˴!M'</bk4}4O=Z=k/?2 G~b5=!z1h#9&^\, 7fBߥBU`_+az~_FPP4Jf9\R@hE1E:ABWqzNc< ՙ`!4RZL[wF- 9~MV( 4! m@[Q:ĿW  l`w,b eHDDHO2%Qr@Β𤒨' [*oM)"0iXZ&Jۧ Z /I'et H>+ .Z6`[!.c =<%:A?$>4jBfЯ/Nzh >CO9 @qyjQawԿ$I`o ύƯFM bƚO6Str˜ λ඿ p: +=A/7.~' } [%!ŷp{r*0ad}8q2A/lGBX"{~CeuI!yk&[lq>/"m^76TPQxI{2>ӎB52A}UaE>2r✻g4w)x'7G>N T>O/|C '/R9FF8|m5X8dgY`D:Nj7RPP*oTvFb x?*E6R v@/sk!)"^h.({ L-'lr.W8UA1V|wIHOe\#Wᠠ3Zx`z[G_OWgC̥!gLxI ks ᚑ 78!N2ֶneo-z )^+Qir>hV"O뛽W;="JOcmAs"]N˽;~^o搼`fd2_OCH-,jZ9"MxZUN]0zkEAgưl{=ƞdOcʞcsx):Do9~KfG[AHr'>~dC2 {$XfYl}e}Z=fg; q"nbbq<[*,C&ƀƖVC_6"Q,JEh$eSȁ<{X7nl @֟ |$@pߙk$#ᵬ[v ջxTyyqq=F3YߓE!ϳ޳vYgH ]De {-8ΝO0;>I,eKvvEf-O6SŧϿ?;J"o)YtnATũx*^8TpGA/UgӆxcRoMm~/ՀƤd NvoȟDzj%r(3qjh?l0&98nSx2ᆘ!R}Zv@-1̶̆*-Cow!0ED(Jo#THF:}yW4vAku vn'yy]sO`3i:i: t&tqNdsu+r9j:uIE{Փ.[NF7|x gy{ (dzHrau *"B"o$$'I{cȨ4jnHciͥ̆oґCq\V.ˡ~:"FzV gۘ-Ke7 t!9A5FMhWƚ~H" yo0YkC9w/pƿY}d?Z@%> Ts~^?BrlŃmoóm NWZw[Yc~l[]@aR9w}bGT~yZt#R@T!9["_"Bʖ2^pr|S|Mn[eyAULSK6AU[)Gr)"G-3t*si>fA\4zAs{=sr7?(މOKy}eqL\}}rR*Lpgj@Sdd?D cv)d^!F&GM&=L} =lAn\tFQH֙f06=Z`~Ϙ(ޘ;(Wx19&yLQ:⪷U5C=]|jֽuN ͼxëBꇒ"y,7:iu{#2Wv3vֹ_!l G  dHjzV ^F|ۄ3nTE7sGV+$]x['KGE0 %^I6~CofYݭAiԟ*Vw=t02Mt+Nwr_o8}yZF˝aкKd-级lV"4-ts yV^Uzm}BW] ֥O)?˟/|!Fӕ7!T׮mq̹ٽ7yKކ߄_.fvh?M ֶ$4TT+vfD!QS3u 4/Ntr~K;#d8KT(}m?Ho*-ZZgqU  釿58%~(n#IF ?J~ ˓ {FQj1Q4L4\˴?+PܶbE#Wv0vmkCyLH$ַ.ϽƿTIF(r]DaNпh:iE3dR$[&#©d:7 {л] _fƤ`-H2dY& EOn34|ޓCWҍ||\n0kH+zrҾvvwg*zes;=ٲ2Q6=^bKArP> 1}S\FYE>G}V~(mz}5^YHW,|=t8[zwd:+YMV7lE!ep97;Bm a]ثBB[cDqHkE rr3N zdZ Y۠Gu _^)*!TMh}~J͐:PO͛qLc!hwD9:ӝY7gPq|2b8r|Wd[e"Ht o&lIm/N~zאFЉt*}ӿxs* c1$%yus\x·nv~󐳝D;*~$KW/e|ψ& hIeXk vg<n^خ_9̳f b:a/؋;%Q0'榸iH7vspT6MQak]pry5A2슕t#1Yΐ/7ZLTDZkuW RVizԟoԞCjdr#E7 ~Xt]K(Psg Z##Y?[v3s)fSOY{+3gm#܇BcIٝl8%1#~(H5Gv腜;D(Ɂ]`XR7ӇQ =SЊ^#d>PX*Vr܌|m݈h~`~l~i Ub f~ę,q:*-s2!6[^׋rC rSk#|)8*{b#^KAD?_NϔNoU dG@]PPS/Т>0Syy);|Vyf~aZRh [b>g,nk/Uc]B<{vm^9W}Ӱuȭ Gݡ.zC^=fs =8Wyü ƺjmV8qn+T9,O h!hf%]Mh1Sq~+?'@oW~H%OC5IyI^ypL0aBos]PZVxj wë'^5NgcYӹ~T__r?WP/<MbHpBpJDI*Rq^ o. Ju)'jiM\p>rULrʪL;Rd ]Ζe̕sdT>2T\.Wr$d,r+<򈬐p*Y dHEhXŨXZ!}u@JTǔV*E`tFjTSUVUlSU*PjZVuj*B(QH{U:ARRUZSzgTb_Ґq3H1VLb*3[L9Ƚs||x쿟ݗR^Q&rhr%$(-+d+/;(??\.ѺV:^w :QmiRHҽuNѩzN:>VOЙzzѹz΃J]z^7"]\Pw뽺LЕ_52Nލ^yx*#? <`{JBP47ݝ!,w7E R[,bQ,|RST6  X![JX;*)V͢yo ܂j#Xܞ MxHDVDo ~ wbmWkUpԠ_wB ,\pMPBԩUFv=Y"YN eAŊ\Э, LBCJ['T' JSu!qWGEFQcQFH:j[B+hȌ7 sYhfȊ{[HcsE ;F9".d'#ǂ r<;.D^mػ# *}Ɏux$S+i8U;.%NשpBn.O zyvW}ΈGA铠ӡ%Po$t(1*I4_Xa3Te iE:DbEz2d!Oݮ&@ne{l3 tUA.Microsoft.VC90.CRT/Microsoft.vc90.CRT.manifestn@.̀lGV.rTѸ6˹cFZ}BuĉT 49!WhU*8@Gg-߳5ՂFC_z?tPb9mms(=,5>lS[ {%J'ݜ玎6:JՔ뻡]z˴T:"i;(8ϗ(PBUW pa{ =>{:Md"!,7bb9%zmzpUOdd/BOdDRDaIǜ KR0܌MP{fky/" V+tPKiY:SدӹpMicrosoft.VC90.CRT/msvcm90.dllyXU"80Wpș'D25 bTDDTvJpgT@ELww}z?w|]a3ٻ1f ^d,ѥgU?U=#E~ȱ q#BG%E'uw踈HϚ5m'AY%QC۬LPnQ [͍>rQDMWt|=ECxpXoɒ{0@n0 ;:cqBiZm/'ȧkTYArT%$jJbVڹ2VEfԱ?,Y5uc?W@}{gXS S7.L4&B;}{dU.wB<#BC^m>U)y&MG[C) ="chPv$?8?c t-`)лyE .|h[}5G7:q X8S`06W44B#Cc74qd\,>bG%źG#bk3_]H`0Z@#Gėq 菍 Ə '5&.IǯxOc᱉ ^BcB7Z`l>6}2]/_ʿM?T17K}=t߯ߤ Yb_t>u#Y7O8U^'W_|"KyGǶS-},v>5H}K>ttpp[ \|7hX_S!hXKhEvR%>nPl^In#牵LW<~w*:lRmϚVSx#ef\sxPZj㽮/YNt7wy۱ۤv#z=8HusqPiAAUE~=}Xysd ̋=I;; m ZuȯrMQI\C~ø/kb$ƩtuQ^c6Tb}QX P%T~5IW-<T~<Щo6'4Ǘ| =5x/֞[w(;Vg]<3濑1k8 ,#'$ƃgOJ {L)~6 {`?ֶ.?DIm+ d@xŭ$j?sVfs͚v`,A~NJk1oY|Ԛh6zW-Z)Κ9ifhy;ʭV1_k}hZh]TrK䤎ڠUInyhF2:^>Z jV(rh_rtVs=49Z^r=wZelѼSZ֒\V]^kZ#֧Zk㢮j?Zy.rE=jEkqUWUmnZ+ɮrkDݨm׼Z+˕Vs$f#+.՘mɎ.8M8ݞuE9ƶY$9wv1JwZqJS(wX$)"Uv1GG#|(T 傳EpJSsV#T0\NM8Yuvreɧe1ôʻʻxLVg7wR8\Y'8ۄNUPp,uYocPu."8g;\%ByyLN3Ip Rə8*9'49g't/& 1UbdPp: Nk~`m>We 'J$ 'Bgd NǂSZeG~U:/.ښݪk&zӤ#WnUVPpVqUb5?֬Ɣk1{ac$$8ǮSsSy,8Nӫ:-<+Bp Yg16v1M';8Ǹh1ôLѲTg:l4NS 8 sYըknSC]Iǝ~TlbRWpTqQcʵ#T:n5cgrMTUg Se$ N6btmZ9ɂDp NXۛ;+83YpN'/0[9Tq7k}',>ڸo{B[ACBC;A&|-$eӠ= }/DuS_ܒ> FHJ3]]\+{f5=[?T:x+ :T t<[mҡ`NR?DEv{jHq?R AqwP\$N$.g\3wQݗ^Ir=Wݴ=# h.t9eh]R t\U]O˹w/. sKa3|74?=t%`iӸGmRI[Bs\#N庒k l\kDڔ?:4Z-~ -9B+/\W·-Z;I[Ӿ9vZQ[y/?HqR^A@_:Czn?DurogoQ~Rݑ/:xa_qõ0iX=S\\:oz>ZG~Gsf\h#)52 G(͓c4wǔ&s{NIC_t8~z:A v?OP'\y'|۝T곘Tۓ}P?y)Ү\cOZwuO3OS i'OSӔr{3 38 ho=;CAdP^\3IfRZRIY/g>G7:SVSt_nryEgѹEeQYt(s@x~W"7H.Ҽi/R皭M!4c6ZMP=} ͡/r9ْCѭݏ>,/Q|=.p_q]pO_e9R\zNɥy4]s<G<׸B:2E]c-s*}_~_ަuCB?gݦ KyH!{J!wY!ofRw9xwux1z u\kݥ~t?ܥycgzW~>~ST h(/3H{h^POqs/Ӿߧ=sWAgyP_.EWDUğ"~SLu]1,s~QLm_0!yH?/rQB֣⇖P_5J\Jޥ߷R*RjL9$Q˙~zT>w~u]}}߻7E1GSSC 7Z|ӈ 8!no|zݥmҾw?kxQNqK<>_,_OÛfп2ܠ9b`x2P<7Pٽ?m[u6b(s5=RnSbog=$#6L-b(KqŁ8~ O@|1 q&\).E\QG@qEZM_S z!F@1b/āSA1SGm_v=(Q,')-KlLej8q!fkhU@##ߤOub3}sE{߸a)~H? Oz{ g;\b}#S7)[F<1XZ.SSbc?S x@7O 5]2.rU+@%S檡x;4&E`vu+þB@> WdTDq]ʡV$b]4ڴHj|<;śJMU*N R>j.܏9x1eVC_iUS80{>\8>7sJ9_]s_{PWq[T{ӧ0µk1fv@U| rPGkar)ţko5ׇ"R;xyx)?a;R,(®G((uiT.H[_[-"m}m\MGfT7TQ]14R#K]j)|^_T!gt}|b7P=o;j[7T4R0v>Dx.-.3M)<^]KypC^Bbnw'j 'ks>^?#/j6R~jэ񛨜;ءu5QIb]ʏA/3fO=)p#l+%|{rriNUwZblQO$k={~7p8$? ?Q߮*N[䩦j$.&5-UFo1]ji>iRnqi[?-o6% ܬI~+?1U}4]a8߭Ra[yr]r \@%];z~\K J7mT g_tܥɯFGKq*|{Ņ䳎j-k+vFr G\mH7#F[9NT,滒< >QCcF($|GU,<~r8y#9 OY>9_ qv!ބ^on|=YW8\}tG)H0x~r r >I %螥Cܧ6gg>L >F<jcVۓRy<_~ɏx䪠A-y?C~ciWCD J !XJ#䵏 ɿx,W=sZQA+TzͿUr*nοTrSU*N&'yC~y@D^,VWT[UxQQ5AACNLr k%uZo*UsuA^'_V-+yw$R:z]%exy.[UC^^+9G3^~:>C;t?֡;;{qp w΅ƉgO!<<Yx~Z*i/ &g#-y}%Ge~mQ:"Z/ClxFk`ctǧ."&4,ț W|u,|ӱFJNa7.o yO>LI+C'+|[:f{3קy "iU~~KW|n*Ov>m/|VG'\~>*]x97|+y;!߼}!LIQ};_tG{K 'v+?w}{+K" o|žwl|ҷ#%^do; sȡ~u1r=$wv"w룓5~w/~ɺnEa~}uW>g'ݣGxyp-J@#/%r|7g^}|o^_Vz GTW%ޟ>rXnR}_~>e#[*7.O.]WeXn="6XgOz؟Wйpk'?sXwq8C nwsY4Oq1N).3`_x;| %Ip=\G)vs'xp2=+'w$Dj"Cz)a;YN`IWROT3Zt]7bEp +{N{6N%z-:C"o$G}뻤> ~VS^T eOгQP17&%'WPy>'N /[i8 Kgp: j0x5xz7qd=wwmp< R_CAٸ^~כ0&0rLq{Ϸ4^r][ar3r6=+@NcCW ]ճt,{ `ǽJ-.8Ft)܈Au9߶ YXaw됿oE>8tyWSGΗ|83rɿVFg=-y}FwzF܃?[3st4fr#gsWx\'t?7fRb̷?y|=% tt b6^J+(^hix  `O5%P,@!a̷;l+"l T/E;\|!$ό2!S;$xEpCśo@~i9rw!;uD}64Np4}.}fJ.O$'VWX7{Ik! 7š3;x鈵>O謚2ߑq8phX?(@%Op?S蟇ë%9ɯ]:wa&ru yba?SpoR&_-O>Ex5s8^&n: Ez2\Dr<|wK:糆]|?xp6 >FgF۬G}h熊ox8pPxy1sex=1F 67w1@3GJfv} n9OjC"M qm7ϑc73bM=rNP}&5ηIzqM88 ȿ wfEPCopP]򥛰䯂Mu\r 9=>L ֍M&bx/:9#qcJ5yN5LrMfVtm"Oj0q8[Y5[O&?_-O^Fބy[7NRjk6 M[ـ%y9?AfMt o(fpGCnhx}>:ipžc,ˤ}.J>x+If=r)xyLnɻ?)>r o@~&NnrRiO'MjrBH9 "'Qb<%ExN鿈!iFS&i_ۨlӐ/B~%ݦK~+K>zӐ? ;!y/xi|E>7i~}M7!&[q}=rɭgB7*Jv>Ln 3#=p6-^QVo?.3G)ɯ 0+$= @2Ф|)\2yF)!f/䚡X-Ї8&WfDJE:tO arOCQ']9䣨,}%##t(~֮xZ[9q3x+OgKax3|yA-~N{i/p|΃ùp0O\ \8/n?k}D7ηd"tJ#;C3TkЩ&A.) N&:Üf Dž=gGnc x'G"{Iyz@2sɏbZ"3y9 ε~̅ :%5sE [ebenb}˵ڬy < ɳV -7͹JZmEI#燽m 7֙]wL)܇N<nU Ny~raoH2L*eF㬔6l_h߶H\{~Mb|"[L "_0jguv~͆a_87?2w2>GB~~px83gyp8#-9O^D|>A.!_&&{zv_rrwzԏ7>4D}gr((1(#Dގw69򯒓#0NLr=o58^ y#z[ ׋9v0ysO(G|sV09"˿gvN#rsPt$Uʏw9;*姺 iBM]85^S%:Z5 6F+'aq?&NڻU{:zwCWo $l P-Tq_WHƕv[H߳1ZhZw#Y8:ԛHnic*_voSOzկj*G>-z}_ˮϾ$Zs)}++{ߔ~wq|jwEr溺]?G+<iK~#k絒{la{Q/MuZ xkͯe[T Ѫ ~={*_UѺ $'׏|Nw+?@HEUh_@wꢕ<ߵi\]qwyyxBkP|ǯk=O7}'ڿ1ZՌE/;5+{{QR"cltTN^|E _,#DaX7[;r܃G, uxZe߯u~:y\:kJ~sqTod:8>EFFƅmj^yb>W(.wDJ6U(vV۸n:@qڭNR| fKS|ŸHeM"?V)UM;]M({]s4(Ρxͧx5]ot$z!#(Q$MWi+#o6Jo,c-B8đ NGe+ gitDOD_[l?S ٦G۸.*Fb]W"M{EgOG!QŦ4(v؇b{op{H8 q!⛈ĜCx^?Q4Q2(;zq=zߥx&Kox)&hbuE~Z]jlX=4V xL}Gwh.zmߣDHMokYEږTfbk[29o-ydmkv4OTI)LnL.7am9|~!H[R5%^}Kad[[-1طC"'r{"פ~i3Lx?s6_};N 7mwI=n Ǹl&ƭ!\فnܞ5r[>S[5%_5%-o|x3pM"gϳTL)73==VyΜ-Bn/ox>V =ZyzږF}l"'b5Lly 9[-ϓŞf-%-#PK[g9;Zrlpe ״l"'rM"$rM gSy?\5tk[22+Vm&6:Brn/D˾^|v^b^b&Ƌϔ1\Lse"$r}"$r} פpM*re*dp63=lrpK po>/o>/o>/nnDnI> Z9{Lff%ߛ7)Ėg%R-%sy~ y=}*>ǧoeMLn>ϊ[2-<[BdVcW [pK y~<Fף[qM $~"g9ܞ-S޾>I"Mh ygټd6]h &It3%(E M HQĪE `Ţ\ԪZ\?w~sv5M|緿3svâBQ䌢t5tq䏣AaF9@ds5H9iuH91#ljNaQ9GQs#0 rΑsXFzg}f5|..AAc"F:N;D(GGQ:qNcMr8?u.\ȟ֢Zb 5WY5ȷsфR'rؐEQqNc(=#huS 1BO#g0E"&]c[Ѣ\lȨi'\TD(rF8r9Gsز1B,bs1F#Dؑc4FG:G9bT_̢ 1*p>L#gZTK0%!LjGi%̛i'J(qtj#g9s#f)ԜF4򧑟#\.ؐ^i5ȷ#ǎR'E(0qǑ?:?lD53i-48cX g J ߎyq""D =H[ɐϰq S/@QgJANJw!f_4"E]0w)| 0bvr!N!fbvzF+F0bv rLO)҈H#N!fa-H#+GNrJP1sD܅8qjFj1f}VZ4"]:.FMĺ85w!FB̶qF!]:G.Èt#w!F 0"mB>.ÈE#N!NngXr91ĉDžM =ɑq&dkNjDb$" u'ц؅88v5F8HDE qq qqt1\hCBAC@D$#G''I7#G''m]#cą6.1 ID"> qq qq> qq qq> qq qq> qq qqQц؅8888HA}Db$"ى6.1 IDA}Db& (e!uBzH??Cʢr!B. Czғ0C r!= 9H!(\HAR- sH)HAʘ(w $;fH[ )!;Czү MA?A:%e!UC&Hw(+p}ң@? HOB ~dń,oDV <;IlHs|E9h~|Fw+Ő} d)u9Y-䟋 ^牯!r}3ꨌhXV'\Ύ.ʭ뛐xZW+绎w1^;]BhmWG)Bꉖ 7-\_c3_¯r~mDk-D.ۈ$Zo%ZYv~eZXյhmt->@6U6YDg~6z7pfnoQ.,~<>wr]z7~_{ݗ }>DkD+{҃D/q=: {Gx_:#\(檯s?qD DOrW.[[[[1b =BssoL_ʽ}\PGsۜ侐7rΞ=^si' Z"¼.dUŕŗo+UW)./Prkod,,9$\b-i(q\Q* RK*9Xr䕒J-[ɿK4-1}ttarLLfz%q){S&RzNҬեJ7n->ZTwJ+Qii+.}KgsBs\c7.5fy|?ee7Vvgٷ˞-;V˲o죲UBKee2`S"ZSrҩ:uNe]t@tDJuLRSZjujN][lЩ:z:I6ԥUe^ԩm:uN][\DtʩSN[vTNtZRt*S:5Sԍ:uNҩQNݦS:yS֩{tK:uN=S_ѩtj\ Q'tޓ:).N}GSשguꇺt:SGt:SzI~S?ש_p+؜rFGñ.ll}1[upU'ZskAudjbrz0ĵk2:lu}xGv޶ XWC2`E}=F>dkS`$):&EۧI pMA=K[Md=]w=_wOnܿD?~ΗB|Azl~ 2Fah׀~G0G^.|>:ߠ OSt3䛴8Og~= 8ߣ{ c9HosCy: 4A6 E8R08 GmqvaCxSxppv=\.t^!W a5p̀W Pv @Ipuv퀲#{JY?onzW  o_+ N*$*< 0 C< z$ 87 oj~AnjD Kxp/ #սۄCo~NhCX>5np/ rKh[ / c\j7[_N'd~ e!A!!a/DŽ })`&ۣB>_̀ VD:ƀptn¼YCkv*vŽ2t2or~>Y{i"`ͽ Vܛ`6X;ʹ WxVr7s zD@Dy)UK7wm7"mumDlIFh=6=rU%jUQ7I^G5NVݒ} [ձͭ=|._*a˜@@999[m99zqD'ئ!9䫹ӻQTܽ^#@'@[Ы&9oUno]-mFGscchиh޲YtliN<>S%-7(API3ڇUZՀۻ/juo~Q# ȊBڡ~8: @ jv@+x}>O#A=۹[rKE䄍lG&2^8n @7VR*+bY-::yXGP-N㼖q!zIIQXT9[ uEՄc Mn9 }a3dS6P֝SS WP;,,z[r.|nfү8=aGDrIDԾ,iQ뵢UTѭBe@9@1Q"V_tNG$5x\%g`r{>a`@ƞ 6C uPnJW2cvUm#YWsc0v]&n'=SVf@ł>蕅uI&_ٵZI/ReȽ0RkNv}f {UҌzOX)avj֞nhPYO!q13)V Bv80d_wT-_:))i.Kj|JhRͺ6oz1wˌ5}+-BgsLSΖR(f_*U38[qczm_bBum MJ66[JUruXWsSGyYGQA+T9T H}xNevwc18um]RcGfh#%eZ(9E/ԿaTQ~S垾UxݢaS;`U0aNk+n׵2ۮYw g z8g8xvMmX:7kas5 MKq|NUūVq|7Qbry6KrXYҿӫH=VԦ]Ag{K;ݒW ~í9q'Kr߭( Rt0/NQnϰQ6ުW_bI3k"d4+[}hsO|POvpSQ 2v fU= `>;Cv#c9ȶ9.vG@~) jQQ`7WT FV`&yR7,(_;BB~q.6=r;ݪgj)@!Ur'8:6¦FucV:C&z^0U (}R\r׻uWM>j(_]r]DT  H~;/p`pFျ@\ri`?lJ5 *Zf ^t<3jEAzaJQe?{틫۶۩6tCY\O ]kءonN u.MyIhN]c,ܰ,a#YwV]vXHl! Tpx7+>1Amr7ԙRYҴa(6 GAl&~#{ E +ߓ䡶 l .n7SyZ96r QPPɽ=F|&* (ӻaSX$MڠyjQfzsF=A6 8}%̢H[JK'S[^e,(}M#o_Bݱ`.DNdoП@5wT%ab,A)/npmP GʡG0]FP׸RB޴¸c\*/ WhQ)9+*3~Qe#`M,uatUpb}>m \L٤IR`7'{t&e٥4nLwhcݎ5U.R eȻ:nVJkqsRᐷl~[ȶk,zYv~L uW@;;c»Yyy Hlڬ4^ԧB0oVR,$ cN4N6WDNH LAID훜lM1_cBDBg$>yd29rjmf݈~'A4Sdgi`D*%%2`¶o',JWe,7պޭ׃ӏ:bbm,BcUeרno5f12Z*B;|>bnyړ>ث=, wQK_įb,H \T:k-qD/%, UfQ9!rb|DxhYL1:["{*}Јp#b{:879DsBƕPN_OϜ8|7?,*jcFw@Qˈ؎?Y eb{e\) M;g c hBI\c 1#F]>JÊAKjKC" ^P,{6QD1ş4@ 2c]"ahuʴ` ;;3RЫ{RPcmn\ k9YSNy5LרTF2՝*A'4|u㍪0BL8[r!\ Ǽ83jXSr|~ { *|_3DEn4fM OxM* 抯%]z+8o┼L-SC5H[ϺUeBYdyM,j#q+Ĝ7oXM[$RthŷRl3*T@+ 2Q'AƩŶk褮 znEE4΋ԝ|S'EXxL?~9"׹R8/j[g5lS a%𞒞=ܞYB%sAk1gi7ʪwvd71 2^q3RYYY0Φ?AǸRg5r3O:~Rq|!XJCӲN2R1zH2R1O6RO4RO8R2O4R1N<#3}wi%ó7%K2wl&Dl%%f,ќħ9iaey?>$9/Iާa L9;V wY#8 'Q͙˔G˧7xUq ξ4Ff*O)FvG%a =AILS$Ah~'+@m8r6%+|Z|V9Ku8YŁGkݮ{" ȶ 9+Wr𑮭ZئYlǾp-n{ VGDx 9ik0]){;9Som1Gʾ6WC57W|sަWf)\ws/Tss7'1DQܑIMX􄆦6ldER 6g[k˻+M'eDHPL#ج2?@6dzYU^"14Orh,r}˷ǫq F7Ӿl(ꇗ++=KuC{_Wj: "*,`ay@3jY%+痽JILP]qo:d®pUvW5cUmHZsQ.I76H"-v;g]+u&nï 'hK:\g!yTsY4{Iۛ/~x 57bL19ږT8mMl}Xęi; Ip4EYe\^%YfhC5V03hC=V֋^}ι-/v0PBuQ?4vL͑ ~6op;Y$ڏڷ7Wagkz8)V*ʹN;9F(bWKU$;^1DR4ugTo4}Qc5NRwp/"fɑyZ@$C{#6+%7ӚsVfXUV͍^fiR4EYY^ۤY}#_rOȁͣ_fc3gڦ~{Mdi=FeDžcͶ>/}Fk0Vg-l"S~F0?_j9#=#6J@Es3ΐ}k/{oǑVW7 @(f(M1Fk2EH I,!P!B ȱ .훸hoe=1q7;q'|8ŅnB뛍*ocgMK^~TeVeŁ̗/{eNŻ}ۡ2omh۰!e-bOlX +UU7kWicZ] bA~5TPC:O,2-&YjTQs^= F|/Cµ9Ђc@]8BGAٕ!RAB }B :1fhT5 &##񥥄]CԦ"Oֈ/_Qʐ>ZTtm\dqֻ/sitcesKnRW_!`B x0ȑad3ۣ?X-!jÔ'kė_SЯ?ܴIPYV,ŋ]A]z(7>Hy+AR bVS+B~hm*״xH׼rJ"RvRԵSRL̚{:A{Y;-ReRe Tv"J_l+RyłwVV{&##Oֈ/ӿX!_}Bu#tblQG\!VPqm*-kT ⹑De巈?Y#LjY~e 幘L҅ZEZV iHfy.!jH5eWƟPZ$](LU*I EE毐*MX{X*b=Fr/D-A]+P+Gy F22- j1_-gACf%Oֈ/W.Я^^$Kbvל qn-t *\p.$xE`5-*evՖV/+|zVMQ%Z5e~˰j ?{-)뵸lTU jz/)Ht5" lQzB[YJA_0'"'kė7eWDWd1p\'}31 GFKV?Y#L_x)C2z#ւJ/ڊCԪ'kė+*eWW/md ) qI-sۑϭO#gOBs% ْl#[OT}=d,8z/ޟ~N~U,{G׽Y 0[n q=b +'2ۤ8-Zv3.2"mbj$ X|:>[Aj.͑ٗ\DrЧ/6NjCmj;$i?2D2EL̻ȫ 3`bB{GAI =UWY_vYd2%o}!:OⰖ IC&eHWz+(WA;J .W,u|įwUc; juj`Lʭo_Z8 ܓ `|0Id꭭B,˫: sk>)ɛ8)y2eX@G~UI,4r"ˣQ(%m -鱝1}jFm'st$#Dmdhx9XM hYNpfڜؙ/ѻ*`4\q{ 4)^ \F z X`pG@)0 m;HD :j6 + ~,?VAg2\K!x6X& БH bVE\*=+~pf}4}4š4}z9~5]Z62mIwV 9 >nuSZl..oY#W:Xpx!7h 2(pBD884p(' M LͲDI?ƶ xPM Ogdȇ׈~ `RZCo6M?,L0u7F=[~(l Dא̔u7xZE_2ob@LB:`Z'/r &9hKWK4opk%p  9A" 4Vnm^ noPsm5J=D3W`k: k[0W@T?qm2\Dh?:/ g |alCLlc?(E47LSskUs!j67FSIUk_RΎe6ϸ )_[Ex0^ tTK{vb/mϲqmeli磭nQgu?E&V$Z44#զ8b#2}K񑡁^1%pḖf4'.<áP=O=e 8pϤǒXjyXR/cy|c#LY|Y$\rfvyHWEsɳSRӳV`>xz$ \zh/N ÃK@n)3]R|ep(Og3龾l<=c'V2L_~,K 77N20=7Yz-܈2YAvk_ DHo9:s LWū'Eh``.|:+W[*D_"̰4w"֓)l%:1?˟Q@f91[I:}@ʲԎւ>nHjZ p"6OY33شlv֘-.8j'`]D-uNWSi2OOOLL/N]8?q|9>O"}~c&ixes5aHCދRA(6̂,`~Y;Ԇ-T6\qΫ(TJ&+k[e\EW/‚U;#,s&[gpesVG<Hݦs+p%|KVށ趚Cצy@#PR"J/K?],jdPOF켸RƣJl^ ȝ1w'pfXk9w߫nk%SkkDVͼb m"/t1Yȯ-\XiPnj5Wa\]̧EF"eRd<|X=iX...e6VWr[E<'_M.eg؛@@KZT%'.\ON&x~#m̀P/ГQ W"bޗJadΤvd;x|h ;@Na:ׯR92TZ+ Bqd!H*J 4*f/]VQP&OX {izK[kdd<*fQҾPF霗={<)XW`,ZcD"ۗP -gcC*֧Ʀ/$3kRNMNͫ9ofn|~b2y_MfW[-yfFfa5\!yo~fb~vj6Iy33Iya$y!hrk3OI͍%^78:;9y.D_YquMW|/ }ܨƊ!Eas64Z1&pS P h/Q=[U9^uE%5]ln-ZSI5*l7>^eeLLaQns7xh9&k/[][~0֬~O.ƥ-3ģM*AJhڕJ+:UKs[|Y[ۺJ唭P4z,Xlu#QZNYqNe<UŽ[Ze $VAkV_/󩞆[X'j߽i'LTKoI\<ɥ(U|U_ӞkܳbGGRLYN%U/Y:{\uU$QٶJ2dUzrUXx>ȥW.*h~%'m"FU=M-p88Uv0 *TU"T|.q4~[I}.Sv0U.U~/9V]r]TY{˔Ի gJQilYJz^HZ)ʪ*[r*He=b`|9IvܴWSۮF !ύ g=~畈+~U2U*7\r^U㹋W52z.ͪU+j/Zr*3*[NeV*Q(WYϽÚ4J6\v 鶘(#靵HX)6ŕ7O9mP5T0e%^գm!*8NygeݞyXr*)˹- *#[ ی 3+in;U[Ej_vv6T4a⮱/(ԫk [d18T{+DvFb8Zɔ}`pyid%_g9O$cpHRB:i )aW0޸Zg}gki [{~IwLKˎM1*Ub/w1{7T`Lj j_0kaI|T QLbĻj@n8kkQYEj^I?]RėrK#%O)-*O, "j9#S$;/`yq7OzX˒-Qb<ǣ mR$ϋw1w|zl~XaZCRUZ҇*%OXZ9@^s}P(PʵRʈT5R$s qoO]q}\`&\%}"QdUQ!ZD6[I{=hGhTe%gܹ-.ք_wo5#[vd$Br;{P!5KT$C~yej1[oT5LXY _M&`5-ʪqw3]\wxMK1vZY.MVcwvCYJCloiEר,\#ZIcQ{U i̢18Hk:jA5TMy٭(pM!jz"۶+`z~XWFk`e]&(1ÊT gMN^a̽U ǣKfwk<%\%nE+*"x UT*b.,38#Mf֤ *gZ5W.UyQYxJ5WݍQtSEӸtcV;F*XnٺT11.)Ndái*|+U؂KS]m+KdBnjHx)mUxTӺ]M+Kdnj$<ǹ"r'9n,Ai辳Scsgc<6v~hjj,~volx_D8s&?>\25tf8yOwB eu*UsjŽ5W%Ml$BX%__q/T`:݊Q5_ՅxQQ0;!v202U̧qkźSEU@AW:-#Vhe7b5Ɉ?XB\;빈T\eze.=(7Cy'%&܌x< iiqFV#rQV ЖɊ[y)TdwS(MgT1=]^ܜ(qET,;(ʗcMF=H?Qlijsj;[Fk=e;(^6GMu90~ǵv􀖵< WZbZ|lxi$ӷܯV*&ܑfߏ&769Z/>8%^! ǖ㙥>G:.W7^\pn?ٔa/I[ ҋKiF+Ɗ>[Zz NP"74鸜`9IsWb2J;IENR'T$w?IdFcN `/I;H(,c6dffIΰ>?1=?؂c$ljzg\33sG/Hs3㓓s'_V ͞=?6ksR M_8܌(جol~lfbvQ7Y 7'VO74=}anj.u-\*575vv\qahd K+ r;E:&2%7B ߾]q=.l(N '+HbC++##e~d1-1 WxC\,-f舺<440'tZ'Y8-^,d >fMEޅDL$7ܔtLl>|[I/ [/˿RY f(e{\~qyy \򓱱ȥ ۛm;cCkkhN_\#?ҷzF:J|n )wiԾwKctBU;+kl3ŌFrٝi~rLYv˛v JPĎXg:Y@oTOs~k =խMw.bR|d0unHNKiPO !F&IpQ!rYo'v6V!gr6Sk`Z G/moF ĊXTǬobSp- 8QGЩ4@( 8iΒf"4_IYDܐ$.m4Vu֤v Xzqy}$^谚V64 "($!ZGDIIJHr&e0&~(YNDz%*+!+!+!kXsNٖ{8EJKiw-"~^d +_~\3lXd X6i:mP YσA[;4IJXKJ?ҁ!tH]SH)E6"Dg%m&ՖiZsm Ym `jKAZ%,%enm97-m) i;_xȞ-v)o &% »; #"yE"]tqD(n0$@(&G:2~6xЮ,C)[ M[Ͷ7[d(*f-M.}HS#~S!mAۢo n1E!)~-moo˲沋#+RU)%^RTIJ]R?RqZ)%.K+R*+ׂz=r`ϥ%s7ilAIp0ij8L5@ 68ޝ "pY2XŠ!qɲGW@|Vl垻 G͒ݝ9kUS`$h=̲B=B)&' l Sdzå_44Ṃ RFD\a6׃BIƌ{cxX+;>{4e Dhj K+j f i iD%\ؽ˧Fڊۓ|VNө%n֪^ pI_zL%\R1mq*?K6.ejD{!ܱI9A gq -Ԥ։))-ʼ1o[-^+1ܽVdkiOM{NEtåYZmkU^h¯ [Uۄiu{ H ;6/p h@Vv͒et]L.j1sGnw_6W)K6uS^e_M<"<"<0hyD#"M #"GDe<^#s3Ze yDy#"`9d7q-daB s<ʤ{7%VN{'EDQIq?ނ:<ʆS\USqZ(݌]2g2M01%B[ : [>v @9'rPPP^"jJ*ߵp[F={ -0y(xooOC!heIBX:KZޟeN{[%goqFQPH, F_OcfO2ڻv%b :cc)b <»] l:qiC.d|dr7lM++V=3[Af#0 ح.SܻL5t`I3$B8Lh~2ړ82n]ۯi݇aaݧ\Y'A2l!b >Awaбš?#>IĖ։!bAOB0hR; TU[vEVL (|:tL!fb>Kn@7(;XE: -H2 Q&r⇺?,}NՇhlL!jGH=Ǒ:G6J 5p[ ml`Tm#E%\90^q,vMwjAtk!G-A?(T%>$D }.>!&nc\&Wss>/O y",E(O#|bq{A|`5woacXp cb,nL^XR `T74B%(DZtht^ PC,d#G(, bFf5R 6u)` YCMck&SأKa&@f AH sM}3ڦsm׬&6h~@Qz@>,5N-.Op_Q~̃Бz>[|/Z7J >m_ ~wAWokGw@7@!44hwP)j K.\wHW-.`dH B wvL5~Lu^ǝ=6sMnD.T|T4PC]uP.TneD|ݦ ++om6k A'?!d~^WNܮ{zBlwNm-ƈipj"n7v7IGY!|2mhm߀y`I|$L? 'I<@≶ծj/Q/!ضJx:ƟVà8q0@Р a 0u7I<(:N0buL1.gL2nB;vi̢ܳV<B<(x+>xOz,mBP Saf@{]nJ (M ^C@VAڗ`{ L3^z"YBVћUGV0rAM> qԣG\һIk~]KӠ:I,[B`@8 uB*LaIdWUch$z(d)n3HqfC(Dނai]zXowk]f?jW|WvuwDu)jWu0!4p VB fB,Hkv8f(H%%! oH9o3hMh4?m& 7!nN <"-m4˲FnB 6x%XOZ'D +?2,a 5vMd#H醿K?hdH2$(K2uE`[Ӎ $hG^ *0bIh O%ȑȌ[4ơ)(;}+( 9S6֔c^P_Zׄ,-+lJViŲV-+v5;ݱ[vbG%!~S b&.Y-}G _lu`gCFEεܛvudnE b?Rɀ6Fxg?n$wgivgoV}$pf](IZ'yZy`ܥ>=a q،Z8ÌmDut bm!5-}c2C"wߦн=8ПAu#k`R,q Z..KwI$T@F u842Cwɂz=p< = hqY1ߝ0[C|ZƣAdp?n`ݕGM2EZiףI< SýOQ) &N#(&f=~E3l Pc{Vb"bQ'2[c^k u:gBȝeI:LT4^6O2͎*;˖I+X&o ~e ~ÅQ&jַ8^+1ϽV/3ɵGV(mgFI`vUk0Xz-F+mZ$P82&U`Fe,VE<~MFF'Fq#P#xQF,iYR4Y",tHr/օ!^jEkyndK#GCMǵ s!yM"YAknA$lm2!E["'' f460$'c3u&R~紛~Q t؂EeD:df=[x Zʄ}c8gd[<0{x Om7^Ռ 98{tgXdIqtKHZ`nJ; ] vB$LJ0n'@<.B̀/ ٿ ԍOdw$*A2)©}V~jnâ?'$?rm+T€+žuELNF)P v?lգ'b,;id -3?w{_?7g7W_?~;~o5Noj^Ľxo{Uloזz so~WZ~|ho%iA EY[/=tL~AVЧ4F$RcO w&S>-:\oWW3k'#sslvcd[}ZDzcC>Mki!u0q]bG|aX/q0~L⎒H^V1{)]X-;+nmᯚX@* K\Od}k9_X[]ꉀn08 z"ckBɍvYo/.˽>Pf`y`o?  0~ߝy?_{uT1ݎV&0 Yvvga=,0afa,b, YE^ffYxk,da;,| _g;,|?c»,? c,LpYd8 X8.2 0«,da;,9ݧ=fY,|?aa34lga7 0X`i&Y8)βp ,\de^aaWYMYWXxX: o, `, G,|?fOX6 a, }e=^gY=XXbk,[,6 `, BS;  w߃X~k ܆C܅8*ݽ{zt]lw!!]O%ۻzw|X-= J6 xZ%H烷=hm:hѝ 0,6e0y",߉߷>y[|G7sK"5\l?)Pӿ>'Z|k~G^xβ }Cx/Ͷ_i֟ ?dg6c֥q3|*FOP7tD cp3XvUMg/aIz)HG %yQ/5"<@B&4yHez߰36408<O׏`Gy$$y ,ȯAz> 'N= ՝4 ޡ:3\{p̞5~6}Ԫ?-$_kTw BcBӂB<$>-m ng]hUjѷn9F؅;5]>G$;CD[1s!=R ڏ56ç >alB;.ʐC;z"F[7v@p?~P{&c!5=o@kf9`&Z8ZcZ1a@3Q`睚SG~c㘣X"=&!xX&c|Z'{aNw1ZVsQƏ֤$G#αUz*B0w):€RB-}0z"!ⲣ-zv=hGTQ.G=z JuEY:^]a= QrN*pQv8%Ejס{jhUmn}o;,EGbXR_ q_OOC&z(:?/ҁ@gIfR&|+:0/7{Bʼ갅IOHBXO7`AH1fN5 C .!2 $8A9tǑ@7~_G&5,6$M>Iot7epvdx)Oez&'3qwF0~9p &t*miMBMg_rSt\#g!JagG / yM1"ꈞ?p}T%}ò_Xt #7NNi|#iO1#,cɰk4j;i]OaQowp(N::t{L 6M!K# U$N 6O\t;I{ vGum3lRD'PqJe۬U6: kaL-0XR'()أV?NĠ;C pins/ 0;cCEaud؝/W(8+IL{oEKфi:Itk9rӋtm:'l Xޚ ?ʷlK=igYgNUP.vhŏkێ&5 #Ʃ vDALX~ #ƍ?F`L2?Y8UU#6gF(q2ȲB3nuIv{=I ]u4gh8}lO&| g`s7Rg#j#J51foe!RIQ3jČv!fxWLئ=5O3+~zX1q wύ, =p H壴UZIu<~8 A~nsvlo]/?SF.N3O|ixGS g8uo ~䪓6&1۬O,y88c["wq4zq: 3mcCl?]_ƹ+䏢E8N؟$)=c(6Ȥ\ (b ZׂiaQo1h:etP,!h6qe %0Q&S w)j ZH;>Njѳhu[9.=<%|0`1@Te9hg4j5j}kJ>Ϝ$g9'w6.1C008PI%8REB?㤂  gA(H!({G.$QQׯ_{{D=/&`[Yey vPf,\hV!PBdTnE/U=?7,l;=;*v;z־D/ݮ苩B{|b0bgr0Ip%@B{*2wYՀE ,S}״biF{,jTF=ؿkҾuo|o)85*MWj(t Fw8chQ[ZZo;!^m#[1Ob~y>:h ЃЀ8P6J +o +MK"y.`rUMyƒ$2g;o2dm5rvkmیc!eֲ8&aȥyf L10Q=޹"3<+xfOwRό_5O~22-@|1` f՚q(нڋ1\wA rlT:k94&{ȍGr0# x.$_ދ+oB0dmjCCScnLN ֘Q4Ԡ 11 x#fzVQF>1(x"Ȅqו92elR-AKly\%nw-9bNMƊ+a&-*c* F<2y +G f0Z&QEj>h,uy61"4p0HɌa'r#)BXFAkb. )%7w=z3CMcm`-RM@Y8޴mSzh]{  >cǒ͇rp< nR-잴qڸcnbىcqkj:/[72Wc(v,Hq&3>(3_Dwj񵯱fJm~OTq׺aw@]^ٟB ܇Y$:24*ELtڰ.܊l%Vޒ"٩c`bgdn'blY̤5e=Zq`Pg'_)\scKys0:r\}9'D=a:ےqdz|10Y]]f2ު5)d Dz>r+bZ3Fqk8q k :)5uYɹ[Czv T1>Ip_bi}sshp>0}`p˒G6(멗ĩ8R~HόGr-?8~9RX^o?yz{ϛ77"yY$-: v㪬y̶ٛyc91ɱAg? vSj2%fOi2CԳJY?uo[ / 0?l1JSNǰݙ7F!+@t_5?DnNך 3g6qhoo~w6솳oHpT3KB1gF~N/l"?Яo"?y,]l4 .p:qv5t.M9)RtB4.~ &: \MF&=W=_b̿g;=/N{p+;c&U%0YxFQ7ҵپŜdN"yjbƥF|ZA}=c 5G3HQ!w'lu>良^V8Xbۣ{n\{>{~ 8dZ' *ZMBD8`XBj.&Z.?g~X~G#~ ù~2t!C73|Fv&%oMm"V}3G/[Mo"V}+Goۛh~̿ga?4<T[ey*9&RDښ>[ґVM/M7?gޥ3ٲg^iن6rrY0B0i~E+EQ">OQcE_3}CUIXr=M kYDrIr,r,'jzt)n"1t{[|0HGP9z3*Y((PJ&g)V Z ljd쬪T&C;ďdڕ4a(dZ?R}HKo53Sc4?^O2k5^|VaP/{vUtv7uF8嘋=qe4WnUMY8v_ܠ}kc)F*=u:I:fc06ԚXDlWBC޽ee=uKҳzOgbBs(C WꁲGԾGzQ5J{J+#>8UmѮ1]9DK~_N9H' ԴqC$$Koa׋yVD,X˶|襱RcX 3=UN2&C[y ͼBs:&ǛQ .OB'uqph9Dz6)Ӊn}Ɂ]ăTɸxM؝m8wjήL^zХ!\Q*!@-){E{",ЗïϩU_Vݨ'Z^>p8|K|ϓ%KKȒII2ƹ{a_*~5 a9p1/|&|vLdv-so/Vjp|T xfy`ܜ:k~7M4*La~,p-Y3M\=#<=Cu%1{*8A(1͝2'Ew`mefU ,PtSdfI 6+|k# pfrMG@,cn3nϤlGr6ڥI046vyךS`f*9]'g)Q̍*8_ҕ! ٭?ςkF2s6e~3hqSkY ^M\q6n/R1*b2Lnx,(zg Ql34V$gVfR ތkc>VAiOćYbb`Kur`Yg΅֨n9fG:: ݑy& <>=94t8F; xF[?'"CuEgX%E~G$Y!!(˓L>ɚfMi#il=yU2e ;:~ƷvM8: dKk2B~\HFŽBh(@æ0 PD5'Ҽt#ɣ OBp;WL4]C*H4 'X/'qC62ΆjB^vQ?h>fW;@@:7Acbm6Pf/zHӬfqu:wuxf4)Q9H猲2K&4H*7R;<Qqŵu6GP(D6ȖMgI&<8 _HS{[(ݱ|0g4:>\Iτa4piɑd2*QFm1EOU-^5Tz9*G۵Q`^!Hj|rs>9og|Y[pUڨ.u*z>[py;doЄ,뵦48Fp/Tqη  ymx#WSU3w*x=|LM>ש ה jΩ:ΤS ]r|Z x</@cP ˡܝVx@SGbR=yE|hjMd5ElDZ/??U[)RmWĒ!JEbx\t e>ͦ*!=X] {cB 7]UU|!FthmV)۱1Utr)[.A|2ZyMUvƱM!@ae:V6Tp /IUNY.͝t')s'w0 fOGyve(rltJ)G0" ` NŝG3H>@m@O!Di_̬I)i`- Z$,Awj0(_$+C:paʮ96yQ^ӝǚ Ez\LIґ J9bpٜ"%װP/?be2WKN)3!PZt:q-s sfMHT=z2~\R_;X/W>z)ŸLw ߖ-ŷ:~c[ʖd }<"{'|cFTo.11cE/x-A}Pђ_G!]EHFwYBNxPcq)݁25+WcK]ku.h޵@Q;3if%Z3ه(5N-ZۻA:昆J" p`j$@N'Г@rpIy6O{ Eu@P mI@gvCZ@:ڝ{ލD1v=t!F e>>^xJ0K.*tF|hR>.ڇ=r0|F U}9hϭlOBjmX|=kiE+ iI\^I@CP&641oDGˊLc>M^N4B̾ ҆xEΤ[ vuWpDGEY U`:KI *O*8i'#AƲmȇlqHɚ[<}ʹ]N=S˨Z(='̂WkQZHAqL>Tz\=vqa#U† 6%h.Ptwϑ p_a&y;$G2z TC;9*,`V^:BJ~Xvq 8u~! Nh?a~ch3]5OvD< ' ؽ@s@sZbfA܁yj _vJyqkK)#L(a#,˂- hi8Ҍ3? ^ZX烞+}'nvZ_Y42v*Mj9;w H.,Iձ\Đ>9_ĭdEń!Tλ&]ΏK?T}ojh9| |zM/+ %g)g~\i6uxs{ś oF,KfăU#uD?? vx뎺xGhDcWM=Qޯa֫` xB߱߅r_X{" u>jO~u9у_T {?ŭE%.Tn/ޱձw4G˰bG{W&ޟbG{X9n=TǞ}zqmgگ6-N^ql 9kTU{{?/Wǽ1|<ܻ׋{.O{M &{ׇ{`S^wT|A~I.>=/N u_ <~TXă%{55`Bl`GvWx ]Σ{@_BVUQ<.ֻnXoE!ZGT {i-}Isb={Dݓ^޵9iaR6&~b{ -D}}O^>AVg+>`$'&e'l}Mج&fO'fcl0cfTfv~aS^vl//tx} cMN^;bs r6i/6f ;6T`F=g\,Pljp96b0)fp lVǑ*dQVfk0e*ٟ q+N{i݊Ҋ#apTQtLY<į13mY i7HOB D|`fyg76B5%Hj`MS+?J5J&9'Z@Ʌ}Θ"?,Cl%=0Mڗe_zhcաT|$G5)h>B 3ilx{o ,UV: r Hcob 5R*( ``6bR@+PϐlYU+Pku>\XFhV֠| 7j(B:kм-/_> 7أ SGH#8O7!Ve*]t /4oBQ5*d xE_#K̾a2'=%e⾀up8.A 7Z\LRkHP٘b8>JR #6`=軌 ;z 'f8-+ G9 U\]׹~{ ?2aJ&l(D,D0#3d"ΓIa!`gN_~09Ai{sV lG[3&v;;\8(& D:M2 Mm #"qtJY;E0m׋nMyqr |l ;0)ia]MT[H)O{*g^~ <zfP ߿ͤJ C̴^S'N Ҭejپ*/@k J. pcýfJV@tЗ,ejC9w&z2KjBL&3(wq-ʷl3Xo²iHҍ<֗Ywtd:C?8wɆ+kw],_=p}b'P[[{ʭv}سdBFnCGR2[Xct(y⠝ dr_NGf}BcS|Lj+Py%Got/7x+H0 Ei^I7g=j596|>+_D-(J.ccM\A^T'$ 979SV93$t˃]f"TPF>k2^)xyNZd̳e؂3'f}ՓIe{WЋs:3 !α`|UKt&4!ӊ%:t|SflȎKAyu Uˢtc AgС@նOǾ$e|vK]ǂl̰_C7޶~p ܧ,`1}V68G4Zq3H!DN<5#AR]w:6a!6g험ԡd |[('!Y7J}XJVƭn?AH!yq^oey&x]oxI{]%3 bN_+ VP{28 F6(0)บ(0#q՟" 6bhuW ͋N;-0loTCis^q>!1b~du xIՉ`!1j6 Ӵ5:.C넬5SzRi+ߵsz;yƞ^l9i ՃZB(.tc)2OA3Mϗ:|.VyS9Sgv"|~fvY!KY(ŢL*{ Xe5uvf `ݲ/Bak?i﫞Uߓ6#s>u. $bX`8\COM5qTz1ҋif-t(ux,~YE d *taͬJ{u؄ֹHgt6"|(=6WǃyB[#PdDh!)ӵ?ket)VxeIs :c3ܢzEo0=<'ZIU7R4qZcn1YWORևؠA'e&YLalS4eڧO1a[CEFѤ1$LHI vLm3Noԩz"gdۮε֋hK6Z`QѤ%y |f@pYz1mn6*]nEzN+=O .^ .O={3S uB/0w Ąݒ@ۄְ9Yvˀy!<8FdN_sX;<3ϬqƮ.} iQ]r}p[q &M܃j6F#h#\Ы Ƅ"gIs\~f37Q= z u@V[r-{3ԺTvZkOŪͶ~8[ by[[ph7AcJdlEwx$+ujȲ {JCl -6^j 5炳"_p?k)F~;EL{s5Aڲ }OL %vY'a[ƹ  [č'Bfݙy]À,hd>P nl~ۭoq E=ʬ`5@¿f-Q18]AO+ȝZV 9כhb6CV7nwb76H]`2yu!꓃U{ ɮ7(μ1nmf$j+XoS;4`,zOWnBA@+ NZړ$6f $J~ ǹgC0B =y{|[` B0(IT 7+Bh{?PpAM'ktvԂ_?tǰtnN./%BPEZQ*od:{}np>-+ѿ}bU%װ)wV rАg'؃ /֖wrs3WYBqE8]B(E$o6CeB)N])X1z0H$d0 z=.B6^3:5[fzm8%SxI6=L79M#7\Q2?u_'f'}d3lFτΧ>6no]ۭ6u7v=z {5kcجu]Vrk$,uS4%Y64պ 4dnv nT2ӂ_MkFV9^e&7{0K!!rd""8; =8>5N$x/9̺Rۡ;C҅M*;;־FzykgF~XJT\*9* y! [1W1|û1|1g3|i 2s3?O2{ ÷09?m dK WcV870>3Vfm22 ?3$ ?>3/"9^5NB$)ĜR9A!g 1P9s+~ 1! 1#ɹB̹6"7cH&;#c~O/E\ WP}E;K]dZ-n%X-S2"S%$h+Chjh/2"v( i )~Bgc}z\i\iO;)>-|zBi OcUOSӋIQ=($+#(DBK)D_ҫ\zBKh.{.wI]ɈB0)D_/ 7(DPB\ A!(ĵm |'F;GY{ Y/~\F$F 1bMb(OR7ScB} 1)PL!lh\l\#^f(#eY9Zdz0=ڎ?"jntcϊQ>5FyQ1ʧ(=B>e Q֯=yY|B |دOt'$F}xOX%*пNUXfݬ"2>o%qAR1=POUmQ/e|Sy'e]2'=@_!x֓gdD֓(dTzS)'[(dT\ ;PVZד?SzRO!' 8ݥ onRTJ}wN*wSqLvO%1i2g%`|.8<`|ie}[ZȫW:&Z%3?֩Ώ7n2?"o*d $3~~e~s6`ua~Ҽ݄m0oiV3l קg_[*g| `JMq@c83 fOP>0&ΫOeDg3q^}N8(g3|A:N!#_RH|؈9G3~RR W/1dG82w8Qj7KQ[Mt奰֯?}-n~J&-V?B*RuHwlaUV,5i8&/HE6-J6cCJ64Jߥ1Xm|%ii4j(pngˈPHwsv*Ź[ ...qnк8Qs{88UsnL:,>kPcʀbcw‘c>}5Ii}e͸x\tٔG}r@87H/deiMR_2ft`F}g%U7# )c*yU_bbӗEEˌo*ul )īQw]`z TYl,QA ()F*Ȇ+nWMTQ*f)Ȭ ) $LtY,[A;PYaYWYSz&J17 N-SE)!Mel 5%z=|YPϯu 6l59z=r 6šlP%l`5*cjkRz}hz+ă6kjtglS(lSyA 6P6*CٓedK:&ptF [+ [|Px ('사_,HJyE {ivEyF<#б;W>#4\Uٙ2wlqZep){RqA@r2#@@: 5}`k`Q;V@*F7JEK#Ah7ǁ*h:8 @WGC@ z-@ z@c&T h3N@'Z:z~ LlM*ry: t,9@*Z T zh3г@ہ: t#V.p o@o:E-@ہ@&'~m[Z]^r"Z*C˗T/;._⡲O4NoKksӦ:Eer\㪪. tB Wrxtܹje Oy5V$zjbUI_Z` 'F/'և%Rbvl,l5FbM۴ =9m`C7-в]SڅmidYHO$%d!KS's`I?͌d?V9?7w}w}3gT7|<.X6139K;K<-Ųen+HD$e%ƥ\:|*I+ QEQa"(+Q两(fm&ԼOe$ [H,GOsƝǡDךҒL.H%"Za/R41``50IYs5hGT0#Lau35ĔJkCB6Ap9MHV3[iɨ3*ml_Aa|{FX)ś/UT2u\/+fI#; e%FrJ6jjDٌKQSBM3IQ2&tM1ы ё2s`&lus)Euɹ"=KN}VyN bNm'iA\JJԌuF5s |Zʲ7=IѲuf#&ѯ5|}_Ӄ *H>m-DzeLEcHVR8|fb9mftD!s23R&ݤ-gY,Qeڬ7IѱX^O0OYyZ`EX^cp||}VFRJiu&Q5S1;R W85xi뽠kNTٗ[{8d񂂍`<,da̱a/\!GA$F#N Y-C/tA%jkd<\H9srLIFUzۜT%iR%s2UPBӚT% ҈$fGSƓeS}JǍա+- (yh1ҏ`^Ci. קX#mG5b#.gg+ӹ_N+SKr3j^l58 #ycƹ1rԀ\ZC.߈fz ,PZܞv6?gǰ]m DBv߲ݢKNRX3 $6u茩ȐqWR X7{B:j@vfg((pX=LblE Yq7.^MsZ/Grϫ8z  pV_ܗ9}%yKhW]|GG@c~|^1yˁ#;Cz9x#+,x/<mhq"/ptt>wv/Fii.nG8p'ǘ.СWO?s;9'G|}[}~pts?l4 |'QG~y;G}y= QKXߠ|\--mށ م3 :tlo/[LoV $~]&Z 8\jv@\Dwۀ'%>y ' `g |!p x hj3QM8 `ہ7&>Lk׎E0zNM Xπ3E؁;4G&2S#͡h.]LO%4qߥ /崀YZDJR! vzjFrnfZV d#8;8EnjUZY(Om8i)DAaZKE먛z6>4@4Dc6]9F0)A91\ޠBݶX|S$1xs(H&ּUyTZhǜG- |R'6>8pl,[u>*FV& ua#0&>SM 5trlLPW}5/qM X'"qLR%iNFcc> XNg1F퍶Zt7o[-@ZY6}*K/ۄc+T^uZէHeg㐪v\#)SOtj|RBhOؾǿ$&F[0 M,kT>Cx{09Y|ŪF:}P_w MZCo-Λd*-DW4^LƓ}FzHOԐ]k2c^\u޵>jj7uFޓQ.!Mm _T| `/ |&='f/S| `)op"e=+< sp`)caϳv>J 3<ׂo˭SxnV]ycWxwbۑ2x Gs8e1r`_-0'9"y:K`[O.~6GS>~ IӠ};مb xDɟH> (Cڳu:\Gݎ</f\Njc?n qEIeiR& ޷FÓ:i!Pb61\|8W&HBEV@;c(I!~92LK(;Zh'fcV)2hW&E^Bd LvJǷ NVK6I`.ml8=4 D:V͈ւ?}XFhT{mZ j,e|Tٍ sXymнhl>|AMd*clК ]Z{6.N HO!X`ԯYf)]1JO|c"vݝw{Ƚѝp}Ce--|زe <2qy|3{xy{ykg? {;ޭޝ]=ǽ{Gy/xhKxa-~'>?O<<<<<<|ŻDs F]iV6n^>aQ+w]Wu;ϻ7 [eUw~j\}K>N[1WXeֹ)J[6W|P|Q˝Η-h$[yupDlk/﷧((Zl<7zx G=/x~Uo{?BLQ_՗F<~kµD:={v鎯zl[{^ĽxK :d܅mnN4NsXD7|}hFY sl+lwپa{ "1&~C|F|W\jmU?_{Uq|H]Mb~ҹ3swٔPPT0q!$h a 5!$\ 1IbC  !# $%5c۶0w1 Q'崜qI'Nn㛸J.8oy;5~Wſ_78#ZDW q_<5)_Ů@@;td a>B.2&d$;!ڗq !QդVY矨BRoԪz;*:}X'ZtW􈾩^ԯ'u^g̙p;n;Nc7d"漹ḧɘI5&o̪Y7fTL KMRMB7xsނWVކW^>S֔C^TDP֠UQ(cEZY@2Sͳ<[B&fD"(m%eKiiƚ%ti*YeU I<@=3wDEBo= MO] ^vzGaeB )V8֊I/]*?Al8y2XeFIHj{Iep3L'&1N̆F& mT7KsYz({ +NE4 p.(>6;.**]@9kZY3o5%|mB- Op.AJp ~v͠ H LcśL)p`;2La %`) de @Z#< .I 8E݀&a$H7T`VNЎ1uQp  ށ< p 8 U+LBr@X j6 O@0^: l^EpςA%( Xoy?7i1X``ȿi* a7;ˀ :8&~jTA x i;] R$8>_gnu-|Nc@a$o϶c Bmvy]U>|ti`Hw:DlS4]-RS$>+gxyQ_ ARF0{zA~ l'dLg qQ $@LalE`KsphA?'{߲T9UloFI*<$B~*u21(﯄U˄r_0Wd m`zi!f"yXjE*ߢЮX&D 6"9baǓq> VwoB9CSj{T*R%1[g2,ґ{Er伾597bԋ"Ł;A.SHEM"NS[pc"JUϵA~!gMp6嬨1aaèak=nO +C, T1W aH1`W,U^d$ x)gQz6y9+˃jv|Su-C(3dGF*Kח\ {'-n{r d2GU5%eONz}YU\aKZ׻Hߖ-0{԰T[M]5r dYk~t*HBzN^5dmh9^uy_MsMiL5FԜW5H4DtX`뎮=#ү_G Uz|߿}D@Fʷ'ılo ,A(/G*L5㍪u6fmTK,^8CV.Yho>e\\9279l5mnT<&$ўG%DK@u5Jt!l;$d*5<)Uf@e(շI.R=o% X*OKS}S5vcm^h:x(iQSc/H\Ks-HP=Zs~v(O=WR$ꜥm!KNT.:"6 -4aza+awdZm21jY0Miɵ2ɵqrZs2C1iMK62mH6զeƔ! Ր|~\l4^ux۷hL1+ì;c[SCTLPY-Y8W3Xi!僌ǔ so}.ҷ+~y",XigԵ}vX6.zmjG-ldzJ|gѵݨ- ٘;lT  v:"a;{h``Î({Hk?)'`nE!6I8E5k)S$XM6ǔXKyľ_{uc)F>s*z^Nys9?P{=iEWk sʘ^)ԥwNIv] XveuEʘvM vr)vnƷoU"=W{lAthVG}ҟs(tF;Ɩ{BqmM^:'hJL{g>/)mMo3̙iƇ>wpT,sVl˜ݞ/)Xlp7깴x:U~|囲Oʱ@ { BT$/0!5^ ßke"u*s:By3ud\:t+^ ^>/S32{PwzOP9^cۼ.WVaVWSWqQk^. T6x՗m+tN]ޏq7b{~V(ʗcDbpAX=ʛ(pݰ`alao〽DE#v-| ltXhO /S?i5e-OZv;ͼ+?YgQ[=i4_˝፧Vv,g;]goX̧?}-oC&I+7ںloׂV7OuǛևSSw^a]%?=Ξ}\yم_ړ涼 /<}Il<_'gx!?pwY|Gb!0+ǫnZc?kuUux  8ZVv{m#zrޜʺUw j7Ҧi;ftQ#G`-NiM/-1euNx865 WQh ZKYפ)A';ߚ ݉X_Q_~^,`~CMm~fF~war:)aZde~:8OVgv:/:MV[.߭58#Wy:JV#RG_C6|k4n} kOS~N`L~NpZGi3:i!/.%cISRXzZV9ӫOˋW#]c5w^5rb]+W)i"%PbZ 7͍c+_:#rnXG^# N@jCIN<~VIN y)ѿg6|15zvZN#8m9zV;;\jzcyz8=3Ң.%-wXscssZAa@i"4-rZV=t16g"MJz9ڐH+M6=}-k=kv?pzkt?3zY^,=V؋ܙ==~)Ark>4'h=vgFy}cePe}~6k)NdDbjk}1kXs#NcYrb:Ny=|?Zu#/6~[׳I^]]Wz5>i@^NQ5`Aހ.%>߮b92$13Z[rbiX#5qZm;uZdiC^/@u@JYYZY-KK_Zyimߤ huyݺ[៞WXkӋ/rRbV^2|KVˋj{K:=ڲ%^bk:N!2~|ahw~?H}X485~yWr;`p>^G^3okge?5)lY-9VO=> %z am<~10Mmc֞6Nh\`+<ߝc`a1#k}Р9i[VNxN8^ˉi"/an~\3@K_sZ^Ӻ`Ui%ގ?Nk?nˉiYw2)a^Dbu*Bu^~;Lv5pZ-E 8~0/o9>I-\-ٱ߇Xu5 Myun{gnۏiPw??Z7~,?#m{~gdn=+?{)'4 վ"Yrؿ3\iq.MW;W;Zsu?sόvsrJ \ 4bK՚y?{9usZ.}husZ=KȍK53؃}ĵr&au,6ra}{iVjz!E?-N_|;!&^-KR$3V@n#W/I_WMp$aݍkKR{0vCwc;uݝ7g.o0~Н&T ڻՐ{J{CbxMЧLvAN(40p4 ] Ip4?WRաN_ rs=9 0^tO2~m~[=9[0ޡ=wzQWp;=' 20yQ@/`{A->`<v'˯xx( wD!("k@׿$ K ΗE1Y`oDa1p j(;+ @f" %ρ_ s805~ 1MQǀ`; pX;Q6;/)o*`+ \(|)0<noxDFD!v1ps` h ?N4B>$ `.t;a>P twEaPZA`AQX l݀t=@ ]`'PȀZ.Qv@7 s`Xb@s @-hT;z:`Xb@ n}88 `'Pt҇hf]G#`?xRNR lJtiRzQYE-,ee=kɠ'3juWEYۚo-E\8Qg2u-uyl)G JLb_cֺf/( /H4Ma+cv,=v1UBs2VHWSy~56|"F=^ٶ܋k[汮d[a1ÍЩ0D `|'+a:DRE 9l*/Q^r Ė۶֕TҞucS |{$"yſo!&f 2|w&jx;i!EA?n6P"IxZsj=%rؐIz̸9;Hpb|umW_ `g@+jo{ bK4iˠ:B(!mKSf&I7W e*Y-X%.F,,.6Z6]9+,݈fz],hqMNRfDK-2ܙikr۶ԄKϺH0Jx\utU| ,*m^&KyRYDim(mMv xpɁ!EءEK&sN0U )]n/x/UTuL~ R2~ dɒ›x,)͖IpmWkaoJJQtXE<á,Wa\U9BR͊#%xK'۔$WtNF|< d_9+ 'wJ0rlU[ yM lq6Ļu!YI6㇜ǚ:7TBz4UU x+yn>[nWLJދn.Lgi(WwZC3POO,s&& X?ec`M0uIZhX/yIdX=v\ǫYH$m[bL"?SŮRۆUEsKox}n}`KujDag(x1>+[7,Kyw Q☱I=]y.3o`ޯԾ Vj_Rsg\}B"&jFk%*Kӗ)`WSU&D?ď&cق>I_Km]`qkn 9- )i]kU,ԲH/xmbkWOH"جfK`X;FGߑWb0ƙ.ڲ?#ykTscyn[.QU~QB"8l Pw>xKG- SZ]k$%ɒ"rGZ'pdAS7"^ Gn {[82dCr(gxr 3ܶ*1!Bu>[EEZuX·3] K,y}C+:2y@vvʓڟK1ZC=8I{?,FQ 6MTy"WT3YnэQlAu6&*%ڄi)td %AŌN{*gA*VY@} jT/w-P?vK]?@VYyjݓ eʌ*$ZB,//k3xJGonaAgj2orCT]7Jp7fJiP΋e\|iKq aD _KB'Y8({O.nv1Qf!k=;<2RsTyՅs^G{^VjVltֈS #2>e[kGLWxqW)mb\+1bKgK-3'ܷ]&!|Ftd#M$z,L~L!o*+ϣz3, ^VYA/onb\Zf/tvPلҜ:#*'44UOjH}TĦJkK,HsЏXX#.s)Sj JOeVJ[J< V9W(젫+T>Qww|qeꩲbb +š@Wb=Y2p_ (pwR@%sTc/!(i±{X8IfSBYO3JB\ࢷ) 4.ci"rK20c[rtڵ=妏8g>BoPn3Ȋ-oWhs,ٴ#VZ4t`+:[n;]{'=u5 -5f@;[bO^1@=PnLZТ2 O?5mލF q,M愐(!Ԁ=kZFڳ } i࿗hQ!?i/ q _w; [q ,jth\R؅/ӭ4l3ߒqSg}pKIP weڰnZ& +RS)|+`btZǪZ<Ű66V}V^mrgp^?z-JjkTD#s X).VU^~cļ 7JN8ljlQb.'G^| ^;kp6'klDn~.g kc)zΓR|q5TMQK6J lvd(yup/ Kl>g3W3哺$뵌isѯRxt+=gTL<mnNf3#%%D%b/^y+8hMo%mZXyB+걪shJt;cm2XlOQ!]s/kBQmf_ݟs"8ٶ+moS;ZOr,-[T&$*Ϟ{iW˧Vh>A3-CWAO7V!6ӡMqzhO3 ٙ`X ,V\}5-8"Zu)my{͔<-{O eŐb^j~W0}xtT\ۖ sCy쌮,m$c醹)IW>QZ_JܗIwaܕ+{eX$~ TB Z„^q/J4z=k(i _G{qcN{IxG֢:N'97ya# N b'N>mb=~z[#= qx0p9 `ǽa-0/'I Lcq}~~[YvG[Y}ggtwsfF=Տ)EL0OWdʄOmP sӣs `B3ݷsI˭OD/ИCw9x`.kV*^ RwMk+t^o~eAH'K Y(*JRdqseR^_O[Ӊ^|YybMȦ]Qhxzl.fXpQƶD$.jV%]0 &O#M $Kj.ڊV^WmmEZk7IBV8q#.!{{wサ!|49s̙3gΜ933")uB7P:챑A9efTF.O'h,ݰ]ct H<k{rLw F:wn2nϗz8:>ρђr3K1T0לXDg?;uX3, ɯ .[ZLk#G>N볧G:,7OK+SzUvE=.J.jԄKjym<ayG5JW{GXLD7ɼIrarz덴sx%l^Nd׋[ݺ(Diy{QZ 1Jx9֭jXg.,?"kG|`~\%, udUSպHvw\_۱ț]=?;m'?J_];1>ϫT*)K-;C K-R4ːe_թA lȃ殦z!' :F,7du7p7s_ሙTq( sf^ tǮ$37NYN8z>݄ 1[.So$@S;`BeАM+E|fPRM4 -P+K9q*mu'^_9( r3d[ rDT m/Y-? nG]CN~ y>8]<(TRrP J!܃RDVqBEߘmBc< U6*D1g=t%Hr?0ylG/=M lGG ͝!g80wnB6L@bMok;Kpo&T 'ݛ [6O0 T7d/<,wfxB&&HV0lcy 8  &y &CdUP;bUd܀n䃛B`h}b v6Npw!h=;B7 &8ꯛI 3oQ +Q[Wϼ.6R9 @a1n\vbX܈(_tCA޺{dP?~uip]e}$v{2.fU_-UqI7;ڠF`+n~eML nǹqVD8 ~yklB?_oaL7&c Ӂ]nJ|=r@$Wss L&_M-U EOxqViUJ7!F%Pi#>x*Ǩ>~'M\˚x>ޕ89&^z饚3d_F443( b.4Ԩ h]T$@ͪa$%S7x!PQ2j2b%/9 5:d̼3=\0!.i7@"KfrᥣXrkwr{`IlA hPdeM,F]O|dԺ9MoE~L$K/u.XZ Ͱb$#ic$DNbj+ `HDҬdGt>dYOu1Sq9h{cfB\6hr^Wu9aX *_8\FV(ؗ^\T/"A% *C :DGo}sJegZrc,I,9K?K>PZ;EP~U^ ;rR7VѷǪ؟Cվ-]j- }͛0E>Y<4(W=0 # ;QX)fT:xtaLG2Vi}`o}:s-t\a9n&.;G4@ 3]93Y>@ϦRI[0^XX`Y4BXlOzƂZVMqmˢ3: ʪEotڲAqjC~55VE[٩ 4EbTw{傱BM}ɤ=8z\"Ĺp す%YzHǰe×Qᙽkd;d`)0,Ȝ0WY9܊mWr AL$ 851LHT;R} QCpW}/-;+OKk*˵ZDoKJ %z\gSiTT41pEN|4)򧋗~K]um8<(Y\Yd^5%ˢ[l~|64=C;=pui헻2 >{ds薰CxV;ev`U`][XҊ~iUX2|9Ǥ}$%sٚK~=w/Sۓ"Q/ֲU\aXœxi E.\RFsWRZrJ-$0?i#m 6+J# yCp rW'/_` [|\z򠬕L[s6$?XgPrr<Ǧ'x9/O|k!>]ډ!xė'x/_YByqMQFhȓYDWh?D ^|Ll*Qrm\;א I?h0A} ڢTk%@Gg*1))HRb$)? s!OT楁uFqk\ERzRT$x](/|`Hpnp#'Uxy铽j3epj+'5U2'?WY Uh'ʰiC\|n/8}rDvXNCP=5`KQu}ҦƲ?TvTt̄8v*"E Z u  -:Հ1$7=qlW$?8!"2rw6l2uG@fݐ;e@k4^7>u@.V,v42\ fu}86ں^lԮꅡPkOQ;\6 x^0x M'Jս & /f0)`7ᗈ?4tnDDs !(Ȯa!;0d$Z`Uζt?q.un[aH-Ԩ՜4T[{b IFxL"!o03 l6@U>t]|:lvrU mr~XJq(.pEi%=.KΧO6zI*/A%-J%!ov$rPG${iET:SJ6&OU!VIc$}5$$$*A-GʵJiCM)9^~0ݎlMC]gx˜t\EXCykr(Hk))!IIJJ:$)wxIpQ?U#xr}EY8ƫ^ C%Xr(_MR"寛%R^,f6#)=):RI9:hCuh؞aFI$nzjd,݀~]BKJ_aϚSm gq0E 0n1t@%Ejd69 ~[)j!O,_ 6|ZR°LcX K`Y²ZKxKaMT.7nݠIrJ6?Xԉm;mԶT:n(4:jyV⧐h6 &S]/&)4@a|&Ek8;`!um;ɫo7ˢUrtie7c&WSCX'VguSL$u܊,qԏ8u)E /!k *ݔy0|WHWA.d?? c+ũ89gGW#9m@͎Iv44UFvgdDž<m't|9; ;ߑf29 j YI42l5}>ip}dX(l3iM'l+bތ=qn(qBe-ȍݶsu3٬Wr7Lr+AWs#fDcTf]. NLޞUBS Mác7hrMyBS>[U7p>qH'}Ci-<uڔQTZw[Y0u<{nٛJ /|r e?a"$w^kh3ZW?) iPɣ`JCQ$Aw!vrgTȰemj;^v/"slU!vC}@cL2nxfuB# H/?([W+ާ4ॢV"ua>UY|&dJ)ULY1$34a ;]LiJ1aeUV3?~X.PbnlOLzFPG?C|Pr#áɡۣ23Iz[={zd={x8Ojxmd}vq#k?5XʍA 9f\RvMs؟EVJ-398 Q40Gmi#.*x]:Z3 9|Y^I`vӧ*CT.'u56g(Wti xV)PЩdS~k2`4ɥ %v CU=XtaQO~"9$ (r8kxgGWA`zӇ$Æw~]AE0QV,+k"п-T P+>>O.I1 `=*d,wJj&G - @4A3ݸ6K'x>¯1C /:,iSrGqW,ƲB+pjjim%wi:3_Α2'.Paڟnk&\> 3/F2Ra<3$RqYXMו[>N8_c9\/Q䒪ҟY.Lu21ӞIC6'vw2v,rlxlI>uGl{y_7bnd܃)=",ԺD-IDN\mֈCq.JGaQoē?!Ķ'ӷ}S_jGõ}y\nTEO`ۿ#4YJSSBм#E R iTRUJ2=*;&ҹj_/|NgeWF2kaB3c)z?}fno ̆WClANSۛ*|Q%3V q$=C^_ϦyA1Ib:=ڝ~?K7bދ;508-aj"ZDd"ADݼ+(;;jUD5@&#W"{Cau@Mv0/"Jz)Ew0p4Ry&"`@nw?NtAOݾB%ǢP )E{I4I/Z؂W4IRMkLSA=Mˑ{V"࿞ Z'#?55Q gꚗ]M]"Yk2,ݝE+[--{EV\ 1%qghqr;]W+^ɡ =QTلՐ…_(ExC!EѬœ86m5$q r ='B,2e#`#|dD*mۉHcGov  `!y%rq|_ #+Ƞe:2a˫H[.4('|:|J9e n3ckif;`' Fr;T˵!"h67JM^[*X㥭덺K(rPn]gvmjr>ɜr`"rmT<H,Ix,r+< @~3JS^e`)*+v`]11*ٶ!*7 21( ` 6'wy]gej]ۉ㿒&v0[Җ{op_/^𴈗/#O#%3>{:+;CM^6A]!ެ3b3-ˊřΎ낉Ky! ,H%FUtl= JݛBtZV5h~Pý&Hf@oBJpG嵹C/p}TB5ud%b0}XXrY\EboItWTGA[g05mg:Xtn_B<%Ėùe8Z ۡROm*mzg^T7jKvj#܊iSB[1N;!=TٙufnnF+`hM/3ȟJ0t&!oYfu$΄id;(W#7eろ4OlxsIz$ aD+Ԛ%jpijorP\8F|sbvLD =N_:-+0QυdC\:S?qTp~&sRTi po."b1J ~Q%M,HJ/oIWP@ 5/*_\^\O!y9 5ᤥC?gN?l>2,;g+" 7 ^'|i?5s'ɔMux_@.XFPcuY0oؽZ4aqрo#z4or@{A_&H^ò3K8 f0miש|n|dm-x кh]$E3 jѳ҉Jy`eLPxFz֋hYvMXRa/&[[[\ɱ`I Ա@^wԇح[Lvc`"Kx:IFBmk]A .>z3ȁ[fD(UGnݠER֞-I׺d]&STng3׃4K.}KZX`XXT*T8n6U[4Pݥ;|$;*w6H(;d1OpzJ~ wX$ wx+SCZIhz/1t&J}s} hOꮡpwnJJQ~=z6[yyi8rn[y+'Lñz?y`)EHLH+ *yR]'{H7м?`R^VUl3>etz%T;UTQ_/\LWa6akw xeHCf6YK|g2>RΦ8 a@E)y40]zc2euM͑Z*hE?"oB%.!+39@W׻#Hu=5@S6DxQ%&h5(x }rՍ?o?f2g5FAӮ& UO ƆJ)wwH0<ojmt' g6[ofqW黤!NFt_{#n 5Bm"v)ΟGFϹ6/( Lh/GNç&bu[$ `!- ׂTwa1RLii4E04t0OnaOzj?gf0+:O֤ *];R/M`_==/sM|z^zaJ盆Ur LZVw3t zweSRjWdO[.ωgFgg=AgG$^8]Ӻ]$%c>C0SV[U2 vPqDF; >#b3<3vsPJ%2X',mh-/`fQc+)i4EʞmW)#ZPZ 4/ SuCF@nKX1nB7[2P55=dq9[J9ffo ɵBP!KK X\^acNމW(8Va.Br4k Nˉ0z k5:AVzgDvE@ؑ aB)y4MLHbfY[eoo͞FE"MwlF3e6R{TdښMu(6 vRWE8m1`nl! (誎g,C$uhYQL45}w `mz0{g1-BF8$h͑aH;9_~ȗ' B)V]#Jw\eO |S$oZv( @;Op$_Y\u[R)FecsvR}nd .aC3?ËBkX/']ӠT*-UY߭Xzhs*-V4Ҷj$Xr6$ãcUWBs|΍MGp,Nsg-9(u0J|Ko- Tݾpe8О6 ]61ٵҶmf7]wZƺ6 ?z! 7c.43Gq2+t7Eț|MZ=oZЖ4h-#LqzS w`t|x!yU}ie8 * m&@DBȲ/˅,;f1t$ #5ܪ 2 ./ .8yyggRFE78Qmk.{̝~ؘY Sr#PmG 7k1CY;9Ȗ t-5_n 5z&ٺUT/\< ]7q~WtWvpts8d/Vz_RJTO\SOE$%}:4LVTT''dnߕ:-û}μNr^Ky.Cg<\9 XkceNeI*%u I95.:꾓:$@)Ivqq/;&H-r5(BTSSiS4IjxLkiZz-i?F7LKS/,hg KIJ5ϩ&ME%sK1յxʸdv{e٥UӗN.=}I0{t:*M,+1R>yC% IU@ ;mзFa;)?%1X_6$xobV;|+y朸Yϖz޹xHfW$¢!?hwnK@B<(efWaEh Ƌzuyq̋z`$[/1f@ σp뢓;_}؋"i/Lrk8uvD$ZcӐ@/U6**30yGI#7a!thqIy޹o/]>O4?hb\ !Ģy&y! R>.G;b8M ˦KkeFzJR::H {UT{&=Q+Y r Xg|&0M#+SH%xʴ, J9vnٸWpv⚧e^4pP+)/%*-%*-%tϫƻX@v)x4^_ktWm 24]vyZ0/+ 5L.P3> U>B=0 6NkJ0B ǹ6vavHжvo"K?Y]Y0o?W4"XZ@!AؘV^N& N@5>y.Y&Q|mVd8FY6LOݨ]htLQE01f5 _iL^сsx#GYmn1-,&a(Lk08x3{ {P>3/?H23^^_w7̷,5!w;w,'A~09+[.r;͈2O+x+[̌D"1UPH1fcB ΢akk`+{3w(ģܽM#t,+R(wj1}fG?A _"2yaVQZgjD;6FY[cFD{nyȥsc%拴-ꪖUmQU.䪤¯:KOq2-VMe/Z'FIƑYYO&?X HfݻT8 ܌mPKOE|[4Ri(r6! HDjEgl{6ziuÖeȰT-6-ZޕPCUPTU^J{0Dzf{["[g[^s27v #swUu܋ X1}WPyX|L{hh(X6}+ȶ-RKT}U*!fHcU]'Sx90+a~2QkAK5J수Jpn4ېGE-$EA̺ˆ8s9lHQ_ɹO'ЅjdP֗A$ac7Zb#XzڒH3g_Tn=gSF=TГ=dƾT]f)@TpƝj[u#.a1±=o@QMU+( 6dn2kXԀq`ey1 z"ias kI'ٛQ:{3Jga,G{-9t^&F!PSc2`%I =W[PC8&O<8fy 9k@ևX*]eUX֌j3sԓQZ`kYnqW?.7(yX0aZB8K?]]~[p*HfOG 嬹pvXe_麧 L e_zctJUt[섺uB&€mGbǐ3lb2ò^ښTDf[&_z1YS!Gڙi kW)0,W0I4#\{a)`40|r FuWc  BYVt ?:@ ໙nTjP:P4e1hd +Sh2}.eǔ^.Cǭ&ӏy"G#c#5| ,-SKt,,ӱIcŊ,&U8+{/o)OۅwD3h~dC\L7)o٫GkTE%f4ۅ?7exRpBhM!ܬBg# ~B8Mݧ{Sz.&*}gg,Rmr07``C]jcs9l/=&!]\k҇D|xh@e_󪪝,*܎!Mx@۸=!Pi b08/6Q.ˎo0Wћgy&oͅ;.X|LWTXY fvb6&9=HXm;U3"%;Ў@.aζR֔m(t`YToנݖC\*9f"CӽRKK/ RM{؃$3ϙLd*ǿYۭռFte LX{ˡ}T 3^Z%%+I# ArP76V1{9FEDnk_NOJYb1< 1O/9r¢3l3g:,>A5{3qmYפˣ8~YF]Uٸ_oQGJWTl%#$ˋ Qh]sU/<^,qT6-Mid@` NR-$M g8-0B c]DaTtPQP-F8{8P As}/ラ)~J{y/+,;rԳ YQn*w[ЪԳfX3٧p˅D.tf}S.tVNJ"Idl,gsW*3y a* cQ SmL,hW)eoPմqy{IVjZ7dMg@>"q4)g!}El➉=öf eQi+}[7e,?`5V7z?LB3[90أ6kKtjUZez;oA z_L +iXft0FKr?] HH6 [A@o+MȨ=j-γ4yvn"2Cu%a 4}35Iv7nw3 ~p3`/(6d1뚺beItw/Ţop=;=ܔpg 7~?1A歑0{Ht"ЙO c1t$L/`HL]:Fh9.Н4@?Ж#賉V" ݯdS<+AMrIF;D-P+CJe]Mp^+7PsWIn[T=*6C^*WjZD$Vt4}\@InH5E* QKĽ6' S2~YLґLUU所m,">؜jѭd'  CfBBuPGB}~6P]z{_E]Uo|UKWu [[4 e(&VVPe`nSfJAk|ݟ|K8-7tBGxq/>BNP[g"LN&Tu~%;9x:*VVrp1x18Fǫ[vrǸ ϓ[xؚo+`o@9P%S|2&~&aOILG[f:NQ5ǭ"DZ-xW/09Nyu;!l)INHSǢ#8tC'P4/Y;D3GֿDbQRc-^bW99Tr`sL7al}N dx9Lk >/RDіW(l p7몿͊B)^0P8*Ik<1L1HS]D j+qj1`p…ŻpÅ90\Љ 큒v 9/xn*ZA~mʚ_MDUN]`>FQF~sIŁO@Ȭ̓DG?p9ɲZŠb‚,a3A Q0e 1.|'o,AAE#~jk—NRCi27whyȗn@Nԃ;e/ygA8PÁ0}zS/[ܿ<(ftqBf]p=t^FeijJ40;]o9.~k\opRFx>~R ;kVK^#N񢖵kըſF֪Q\tԒjkZ>MԂٜ`ML2GZQ5eQVJh 3AxĀ!@+45A@?T@\W.;n@4j@KE/ y(}&^@sɳZ{V4A:DfK4f8Zc4_ hZS@s?qE*š_oE8vKX93RSE [u$痎*zIl1|;"mQur1Or;XxB(٤jc˴h'ȟ\dU*fDN7 תOG Dm-yTDi`(&FQOu.:M1.pds2u 4}ħDs-:v:!ۄZw"blOs zkUėE|dT / EʒX B^Y$, \(`~pn"@sp x X  讝ǽZoqH /lGo =&̌Gp@ #^  >W H  l`U$sK//:/.f7ߩB..ہ!,Tk ^'wF_S= EاL7X iJt`/ U?,KU%S?c UUNR.a牰O XfȾo@}o)PopE_{@Urr)yܥYRq@.aω,nld'Pd~f1R㌪#_ݽ$v8U*PDEu*@x.MjSs[~[+-Nim>u&.a]_&I}1W̵![5-r^+Hd:m3D121p1M/Ĭ~bI*Tv$ұPDHT<9qGhॿ#jbRFp>:v6;f l5cۆ^!C]`] CqkbîtX\fv'_|*=[/x8TMYU}A#|'T7^yfi6`ay@*c(ThC[+mJlbۏ@yY PFPF8:Ggd"%L[l-0F@F KoϹ%yIԙ{x "Ri5L sa [W_?j43%s[$R9㺏m6F5XZ.PE`m}@HsH-$K\_񢈾eqKAK -CS օ\b Q@L8LDc-kLnQ/m~ˀp-hp=X9A{LкacG>p1@R{('q0"W". *YJh$!FG-@1'PY hiy Vmtin)o(<{ $E^2R ^$Gѣ)ߝ#ܰDBBp3Vb6i W Ec2Aeox#ի(z5NэŒIWjw\sg5Ys'2e!;8Yu_ +P ?< +3P8# Y #ѓy|q;?]cF{`ӄ1zG+?e%XtF^`FQO A}2er³Bߌ l! izFq JnbfЭpXv؄Yfc K:`h!:_6k+UWW EaHkm1 ?|֣[pK _d-]NًxqU\{ EZǵAtp߱08aK\u=ץ%r1'~tД.μʸH[M~٫s ]6zU䥘1,#gDmP)#x  +YLqqRH)LJ.(HOA0B(ң>i_r"2g{=Kj4݋,l:qhv C"%?r=#i7wj v{mHY45]|WB<7 `m4z}L}%֗*o=H!C o|\(ѣk51xW]ru@24L m!.Ɖ[$|W^W~[w=NVhv4^iB?{γtr)HDuSA'TOGfQm1Dr乊{#p:e`Iکο ,0 e(:,e05)KpjL{PGA5x"&DSgRdKSdnEZzMx-)66Ӿ洛B6hqjEZSD{#x^ٌ㯤nn`yM/8/bc̆a6:6w Ol<g0$4ڄ2ѵ1^Yq68;<al<=;@6AD,,Fkw fD}eQ)Q`q[P jֲl}贍=U*5՜5՘8[W֖Mc˟ƪn$}O6C) [Vdd,}1"=b'Sȸ=NF!(30z MKeſYO4-}>Y?7|T[@=&CC!KhD7Z;]"՞>|X$Q·Ȟ?Û!O 4lMFk|w"/J?XBo1Gjjy"tgA%Aggn[NKB;x7߇_(*,Q9YFy^;V繉ޭS yneN[<ŋJ~faF9S2 wf1k!_Z׮F6Zf 6uK887vAll`\ L?kfg tp}$6!y,Żb|}UG߾8צcWz&]Iq,w1ʊS].핼MdiS88ܬ֌@4m ZMyd=讠 va"3K<ѓJr,m.QuD>G%~"%u#nۥx1NX+ HD=y`j]ue;-}OTOC,i d (Ex&"ˁ_3BbC2./VS/(Pq7Lk0+]62 31 c#m=9s,8( $txd|d1Bnat6Aa_5[`3~wE*U!UI%fpQdKh8͖l Y uτ"ȅ\t\hXNXhi?vQʅhצ^ x4-o\t%V]lJ؃5FGvK0v~| 56f .Y *!|~*%JC Ҿue oUWC&U+R  @Wܺ.p%E(FQ:Ó S=' ͡EQף;Jdi׽Fe'] AxlN!r5f].?菇d8R{0>j͉] f`0[VRY&j >Uqo ʠ;l鐵Dw*w_rЦ1&! Mt9f[;:=ֻW6x>Fd-U-:ysTz<-IUtGغ;n>RBξ~N7aǠt \L.D>zrxW=&*e8,+R0^1^I xI7Kn_B y'*H-ۣC (R: PYB"`tsI֤ L.GM!eh2%em J%lJ|L[ڼ']iJ"LiTW L/C.[* Pc \EPeDLdknn$Llt;\eor(6B F qZaer%qIP^[6Nb?+Nt'#HSb}gؚ^$π VPtRݧNQMI`Ag 8iDr}ŝ38Sx #6 "I#9).Hq*Qx0nFcl ޖlBL9 kPW\67z[q$|̃3CY^5yfYenM|&>0vYw>UQoKC}c[DYO0X#'\T3[0K4χ]I0xB;a[*oK3!&'2Ɠ3J?˘b(zFʔWlG$ g\t֕!N{/mB|ZMzVZrs^=>0j\dZp ?*Kw&> w.#pv>}ͣ3ޮ$\t|$-;-Cj#:hgZ{|ZrP?0̩A419_%"J;HS^h9nfIBXJl]{ +hه&zȿPT_>qϑ#Ќ:V`]!R8-nz]OjN1TR${D5#ǘ4uhn(VUOu?ۮlG>-B{? +U@baa6]0|J"Ϲjt& ŁQyvi/U 31$zϖ(gNBAX&JizB޴Oe;X.1 #R->59v2 j;c-_@J3 *)xjSJmru@S,Go&No%iT+hXd ׈.df/sse(pVB_6\!IRRN%)nyQ\,LP%VQn<,>&V*Qe;q7$&q).RϯTmWUga +VL-1JAÏAz$3x+\mlv;VTPQ!LLECc8|<ܙ}s<9W-w8P97Ӳ}Ϋuf@w_9ʗ1H uTZ$.-G6)A9#%XiKKASf1E">w^K"_ꋊ/,.y9SS\$Ph@r_-Rltiٰ-o?IV nw$AE۶>mGڶ_)= H&xz'HMb{-yl ZZ u&է}(}ҽbJY-j)VI>|N)5#Pj(ͅ2JsHLR.7_CG9 C+G$Lňi?"""R[iϷJ"_&J׳DS)".?W*`8qBO;a;.XMf%P0Z`MGحԈ.UbKMsT~2w2swN|^)ű nZ_&;b])p{<]E2=aI 9ǵtLP jyߓHbk [VOmynq?b-DvEtfXU![6waA<*-iʐ:^,Gz$ "}ePM>Mo4h=(Bg t[כH%"Y1 Y #2#DD>z)pQV< v怟IY/=T)MI6#_X6N/E{Q@[3 {,ΠW-#:4&-ՙ1E ~Gs%y-u>Hj޳Қ^[HRpR:'sptϷO- Ztq n"M&It]scu֭*2/dԸHn>PD~ dlY1yV#I3񆊰VTnCQrjճ&u~؊fPe I65ܖ?$RKs6u.5!hͨo8|>k1f'stjoHųiFWg_gÐёaƠ F!NG)rh s졅\ Q5r [w [C0$32[Y -,ZL·ԮZn}5Qn=x]K)Jy%\{Q-i.ʼn,,I!sPG@Q`3W2#"^V Kϒ`PmlӍ.g`{~>ŋdȻ-˷<66XMt%֐7iF̏SI `0l2X)Zl[25ۘLL?zr'C_drT,0yM5t-;Aor#" \v{H EX[DWT,.k[6NAۡ"m]mUmtRAݼG¯IG+}jŋp8!j t+Y87`..2a /S팆7,mLq3Ի* 6 rءc{NX* [P;kV6̬K"OL4X8|Ę<4Nr cDzSqar׹NMV^"X^d%Ŭ1l>rK ;qyg\N^a1-rtg5'ZL^q!/О>,S]mk#=~k2!#[)U1C~Zu$)tMU48Q+S)|cy6ϩ4)>yU YÕؚY& Z{K !@T0>ƍ.6ۉ,2^I4 c80%cpƉU4-oj]|[f58J•ߤBLǵr. -O*(f5^K }El6y#S3"T5?Ym GU= &0pʈ;  8fE#}?c3gs5UrF#QF7ɿD K8޶&>lҡkιetO8<q㜞+[yKV%Zj| WZhk(nP!68 $_ "h_@/N% w.6«m&I-4&nC;Wv6KKNks;v%ڧ ڹ-mm]8|ţ-j٬]C"w4M237I/݌f9w{qG^ml"hÚ%ܑËMr !>/]wWI^5u)dccX87 i^IJyM$Aw%%5-if1.qa3G7BFҟO7ģo)Ҏ'AEɬ5=_FjGc% Ape97(V4wc$m)# +HCk=Cbµ6^qAPPPP/ <{^68M ov_J7H?_O+S_)㯓6|cGݭ\%4\&u-׬[k 7ZB˕.k=+nϿ#OϑjKrGVɑ4#WJ9ABZ.χ'|ϫxZwW@kK6`#k\#| b}&'&<^蹣+znCjV\F湣sw<7m ܵ-WwM@K Z I^5ye5)oRN?&௑\nrM-נe$XrnPiBRI^Ut[77PG*;F#_9?#?+u䝛 㢪~_#bPa%!?=#Eoab~>QDɑb8"//y+1GtaPDwטnO筵>g 3sYYk7s0sU +˟. [[})Vy7uy*E5U简OJ #ұjn9OJ|F}F>#wU#:"r6\2?ѭywֿӷW|K?;LNU\ | K{b8szm|feβ -!r d436l(CG FgɈ$wqht'F$wggsAd^qIc/yRI'Cͺ^.oi+znJFg]D&Zd9_V70D Mwj=33Ouҷçg3rWzy <#tux'rܯ1=Ř6fcF?Tc?Ә4Oc~Әg93^Llj C?3ol|#N/ͳN/Y)vor9ݮ5ËT36j+Ξs߀F|!)Ͳlӧ_jCQ#`|Mkeu5˻eYt߂ɉ6Զ@TNsFh[zZրV-C=d!UY6V՟U[NNo?e9wjiI۪t R͇i5F%t@VZe_mϟiO[YuMO:m-^Vu̽UlOԪN{[uGK ROj'^STK-O)Ny|pVYF|U")y5KOY+>کu Y֘ݲ<9kL}LSi'=KYԳ2_l?C֎^d_hiҗnic==I6CV"cŲOb&^Y7OVxEmI-7O! >d+CuFo@ 㖖 BKU8:`YV< ͈%m)ȹ%fm OHv9*w[1RAb/`EYEH&E|Dc EF<ج Τ 6!>|@D[ 5vDܺM""*)1 bC n=:IEHEU썸IAcfX!^E lT`?">Eܳ"65y#hTS8!!>1Fȧ![4a2b|ćT""U2GC<踊-XE\$RW)b!n# *"@}ЪAtSǽ6(+J(" nDl!g5dSTDLTVˈ+:>ߨW0EE,Po]  02~F񥊈-{qA<_FFKu2^Aa:XA\kJw#b \8^F|3*ⷯ#D!.!vx+0TDldPqZS_z#8 Πi 3 3+7/o"Db ^"F@GE|LWÅ!P|qE`[ EtigɈ/F\Po"c b C\ta]{!> /TDl"b"6:PQ,;>`Y~ 8؍-~1TFFHZkGL[͚B MႶpvZS-lm6:5tj ú4M![SH) )wYStES8vUSŽBך¢o545𣦰嚦p'M!gMZW7 xMpGHmkD>`ɭ&k~qɍ6iWʇ9W&J]Xjrd1H,`W_Z 0XlKǪ-Y8=;#LB8N\p:8ۢaꍻo6hr#i^\ ƗmP!,¡WMyO4*I0rK+0_p0lq07ZX "\1x] TbI옔k2_񀅯r[*IҕWRQɼns2DmӁ`#vI* aXJ`د7uR'D^219Ix;3 Tud:P;k,-^'bsdh'󇅑3rGJ S33,}ΔvQQdQS-gUڽfB qQpK=uBRi N f W#U^PU\a"_mpQ6r6+uEF.~3O9# y|'z,*Ͳ7v&;z3i|yn+6I~!e-&bk&T'e6ΜWv-]` DԦ8;EmeE}~!Klq]|]dnr7UIz?Οi^>>P*5(ZIdrI, Ԯe-ZK%keGXbqE({dPr5V~R=yp& :V'LhSz0drɴ OH4R~2ITQ5MW#c.@`}Z=+aQ{-Q{ed[ S[pFc{ Yq$$>4]Ĥ |+3M!7C.$OK"<{9a#T:.Dkj`dw-p%} I[^(f6n;pR!Hz_8[ON@M}DC*T1e/PM.p88z=T!3FR<.GC64B@2@DZfRP֚qB{CX#Wq^`2y~PUzd*@vw 'X4U*`~!EG-/]1ıc+6H4Z2~ V6x?sBl82rU=lOӚm [KsO^5h /asTLQ8\8z [ɏM'@M#A v_Y$OsQ2\_[I|1)!? ^R@F; dÿT]ELW([#ܰk$[%|`MtXUrK-1) ZLu pF:s@\|p0&p,횜Fʕs^ްffIIo7n}n-o8V+i~49.QDA[횣pZ n3D'g>&)Ә׳@M.Bhzv8#^C3^9m/OEծQ c լϦv-dvM՜)]rծɪkz>sT(JD-M%=SV)rx`.T[I Q4߭DMbdru  ܋S=p b=TvS?Js8zC:n@s- x@S^:L"*%+!s ?8 Pfp?2t0i1qQĉAOKB\'6(6a bQCa47dV!fd)XL s}H%7y#= Η x;RvU_C#zbFlj}qd*Oք' mF&Ӎweq_ԕ >:Y&8I $+IR%wfKB , bͥ$| `9u?kn{$ ~L;ZCao5$;lQ2 / -DFli pKn8`x_AkrNLaF~^N'59.+snu(R`Kn"m73cSƏ~;qT&4M={u`@ǰjM3eC*m ĠnFV /ˣsdy%@br70z@Pn'w;mǜ*!A$-/VūM'娚 l,/π& D?ǒŘ&`iLx5bFB7.B/9_AN2 ]y+GIߝVz'fX!1<68^;1A _EZyNwt\)}@JzJ)s *Ԣoq)\^|K9W,N[N9Ēn@$4N7X<أ*ķ&|`I# .-.H͍_I6ҀA|• .‡lrfGC=wJI =LoNR?_l :qNC/r𹓋/ʼK*|u^C٣8xIǰY9UhK;EC4ķ#`EqX-y:" [pYlшEٴVN q)vCضD",Eư0Bi8Yư-Q}W3 Ő#Ͻp&$)|<8c=NSp';‹1>N#)N?vT׋5 62SѦ4Lݥe!ϻfЮ~(`Z[Wunh_8^ep"?&R[1=YOOc+x^D,OE?H A%y,bEv3{t+zf)YN+(/S^i{s| `3R0ene2x0it..؁P}\c xRoCt**U~\O{k,n,û C,Y&^?ebF Y& xUQV)Rk{ўMW1x(+ne(זJ?R_ŸkJD+E"ۍnsvui.9M܅k:$`b.dSʒP'yN#cYO%!=/R+r0Y֎r(SalESՀJ2-ܴt9Y".\.' "0G3Ek*))U/r;u+TJ.MIzđ)FH-{)$TOxSjJO"-BW~x ?ΑLa ׀è)x (Vրg5)d (FkE/NaߑX@;iE k-S經* n@fkRx@< "U@XZ I Y, u`@TyU@+e.?`:lNK2"AlyEǤQP1xORT^{^TwɋtKW{%a8 Y <&56-UFbco) Iu8B:Q3!fd8yà ` 8%ɱ! (@'r8@N6ӭR89#l'Og)' Z<|ڍ0]Ƌ4h{Sf#:p8y8jYdmoYN|d38yW{ty'o 5iKJJ?b'W9陭EOC}dhMD{=kCYvqWNSvN-|4_n8c"_VSZ~] Sxǚ{-lА(:̍# Usz{M^p18+~4el+(<&jUͲiCT%yr;y|;m\KB]z*!. 2 ѽrE-}dim_0K >HTt'z[x֩!4UzH]2r/d ..Hk$xw @MɯB?X|y\_i͗B&/_Umශ|Vd2*-|_%o4ĜEbYMQYSW8,bAepa%2o޼[ l6vNk2 G_H`;ۙ!@ ]X"]`L`P5^iݪ<{<4tl OhBY53 YZ)` SE]ج?~ZkHuEV{j@-y&CTNVVIԮ.z}Ԯ.aE*ءʳV2^l2q%Y~sU+ɼ#] Cc q.t=XV9꼐GVgAb!&z"T ; @ߴB\Pۉ֘jP VbblTMzLT5Q%Jgӹ4Q]M+EhNZZ8V0u]5-FŜD)q4VJJuJ^,۔ƅm4,>1ؐ[jiòi\XX^ǹ҉Z%>lj؁\X v`Ӱb16@dυz%J:%>D<.S viAX]ύ+Ʒ:&¯(#M0|t%*y$me89F`FwՆbY@j.Jakq66^Wl*m[LmwWm `p*Ɂ*9h /d.~w!VjQvhB8?Z !4$!*!tu1jFv(6+6Pө6XbdZ=e7vPّf Ԝ34kRƼ˕1?w혯e噷5QD.9](_P|*ť(e^6klD6)6z6PTFN6N|8\&xk;qcb#Ϣ v#^sSmp0S^̍m5c  %?dvzmEK~#ҤA,@3"?D" FӹF$ޕ7Y&ޤ"n/Q0Dn ^`?*`7I-5'05["SK`S-6ec6Lu"$qpa֘bH)f] ,*I'+ aWT^Ԓn ,lX鵢۹43mIldUQ„BΟ[:bs [-xR yWy]g؜7ޞ=C[fM\y{E"lNV3I{ѡMZ,V2_MNjbSuѸD>RmzS[j)WESu9S8G$b̛ByGE֥$6\6{ ؾxMy` >uC`oW*=ﴼ=𒮼a\E+M R$-Vxb򀬍ST%?䒅"Vˉ+HWqtT;łd\lldȖPh\cchl 1#VolGL{\`Q`ll-o KRgp~]*%1 8*%iє)`{x(Mkh<WE%F6Q=pvi4tCjԹ:)_;#7R "W'*h@Qa }qhbc"[n9c"Mbcqbce N H!ΗNKIXOU?+W@eԓRͤR_Zl\M,F*QG. >bhُ:5ԛܠ+ xܣRs8q= 'KƉ{t&6lKlYgn"0֟;7v2ƾ;+09 28i<R/u V"+qES\]dKFʋhmhޙb^'j:7]l : 3ԕxLdc ta~jH1gO:gyAoW KٳHע s!q : Z<^kq[gL/o5ߛDz|휎}}g6vR挾汱C7kkm~(N_ Ff{|#hv0z/Pd*f9w㍻Z75i=S{=*"SQ++5u^$a\$,iv?KX,>ar4oKEUm |TlH@PI-^!uS!KhF׿zf{̼V -K3- (7&Z"$yk̙@}}xZ{~9$K+K:\`n'}\riPkCsM\ڷz'MweO{j8 vw >^]auqևl6.b|a~53sZ/[!Xg3^;ade+d]YB%~Xr-*gjW&lSЄ-?4a/'9䪘7aI++>\kB8 \!JB ZMr')SՕn8]@AOSRZzxhםďVKr92QgtD7lBPdƑkPqF4O fy@6LšjQp@yb,No^ڶb azXv:x\[0Z6@'ĠHlahsb9#ie31Yы$kHa$'a__2afbs v$mt0\{Jõ[r5p%USDE^^)6-t9[9.y|jfax$yL/&ES((Ċj 飒!84) ohNO5$zO,@ZY.'.uiE鞭^Dg-5lSQC.cTλH.y0aXZ n[*X/%_˫`UtâÝy%k9-%dٻT hY>,X[ӊƈd*ۊgCiҔ [<+y!{0*:{J4~ģ6MEE\K0`يy8"9񨂭GRX~7~3Gon~M7coCp*r!F]qK0aQ*ZS ai+8QPsQ*Y ukb d ^T 5t CU)٣pj1u+"RB$|H*/ٜnGf$u,Pԟ́U#Jp)Qچ }gtNn(a?M9Z+Unt|sE$B>̅ ]l ZӤ<)FQ XK1/o J \Cyyտ$TX`w.$BofQpQ1_/еb;AW:-3T:4pdȾUz$,c7'(b9NW/!yX8V/y݃䕫_ɫ_ O^`h  w1,.g Rvի385@i#ꗭjTʲvр%k)C͝5Vg (VZ VZl6:Peuf&˙4lnEpbj4\Ln0*R$c[ڹ\ tհþb_6C(V SZ(@}$i|zfd20D!l߅0PJ!|%fJJd6vؚ5l"0KDq|˶ɠD*8g;yW $Ɗ^䐋bs}l2W/TtVBtV ` @Mg%ΰ{tW-f>A4#g,"4hVAh[(RQlfULtl¡,*h~Yo}Y[0{QⲠ6˷Ȭ(8%3/]N$tљẌDp ?}XѬKKv%lJW>_ƣU?HVgOC?ωh_ZVY~h|Ŏd{ttƅ`N4>Kr',RkE ^0{;*z赧}QxkYBnW?A{!`FIO<llӁژTIAAFQ[ ݞ-y/V2Iڐd<^ "WQ܃m @]}O(H넂tWFwcBR]ڰ+*=ՙ?[Hu%qTw>3ս0@hDulI0խo.T7OunYv6 ?F9m8NyGuCPvq[OuNfv:VCP %6gc(Vk-LuO%j OuyMn;<9*+pr3W{<;{R;?=GrկU"VLn{]TNMu_lUSװnp' k񄣺w"5%{ǰsk;1;OuTJPQ݂6O խ=Lus R]Kuh;~ThgTG}uI?۽Rc̟%{WbCcj|ˉTS]w|Q=t񿣺iG:Tݦh*;eZ⑝ǃnߎSݜCa.fQ=Q2K Iu僺Fu+?P]T7"Gu S]SKtw.Qݐ_<ᩮg Gu>$SݡTWǏ._PݸCOn)խ:E Mu[6QM.B%izSQC':MaX-שS]^m'TڮR]Sgǯ^FT'JGTy܏*TxP;|Ku|Ttřr𧋮Ե =k\f-ccKZʮiZɯL4lS>Zc)oCMЁئQfYe]Z}nџK\=F~/#ݺ2?EC<1_= ^@a0mLeѴ=n*1OKi^!]Zb/njgIb5i-='~0mMxU٧wkڜAk54y_iWx[:XL>.gўH}l֢&kZZpsJHkiZ{ZPbg֢oH ,?M׏4VbZk1!3>&'[kx֢ YdBZBBXkmNg/+k9%[~(3kр/ ԤKE&&TJ`Vb5:n6V] ]HGr-hK_p،٫F%913zuBV|&"?V BPGyye7{df77jzX{S70d)RsQCƾYB]D z^|]_` FV\c|7n/JࢸLV}ol֍P&O1[׋YWA9k@9fA3lfNV9ԈXD1hCBn*'76[6|mY$_J߫ awW$~Ɲr׸RF7cFGb݃>5~֭Q[Z냰ggʫ*4qhh(dT/h r㷅M1K\>c{Rڊdv.qih8 j07ǒ>kuU@{{ x῏bPam |UB^3g{,PIe(}BQm_g4O{Ƕ\v`p]?+Do}=?{XU_]"EaV,4#srPe۞eI M7R**ӊr\~3=;{7ǫ3׶(^mGoۃ޶E?k\ۆSZgڶih O+AFJ;x܈\۵Ӯ#p6THa*R- z$3z]fEp3F H5hxF&T]f0]rqE%C^\!zRJʄ @HYp ևP-Ce2 !'Q/hzO)0I TӷTJ!JZKP:h3L%\*{R [~UT=&N~NҷlVq*MJQL0.:=p,BXCNX94 `H)p(:LDsUh|8L77,E@y`*tּcYCl P&~ H?='dzNT|j9)\`gr|y]c/y÷ >vvM"#|2.-seTUsE,I&KuX捾.]ľA|36r'LNX=a' NwBv5^&f7~o$z>=(. hDS &W7+#.*,0 +0X@A@Y ԉ  Dr |)W|W l@ Vbb4(ߚ4Cӕ9/T"Q2ߌ:e6Y?>p1+ 0q( IAC5^ xݐqǓס魳7UZ ;!oF4 pV-ϊ;AECM j KCHݖǣYӋ¨S (/ qΝ:^rhI~ lP-EU }ʡjTtO3~э\Ylsڗ%>!v]k\q5W@W=I"'B bATd+5<9K\q'JQ1t OxХ ܰVWnlhWHKC[]-8Mc(/Qlt1) FSW;衇`՞FqC:ڌfk uYV_5* Ǫ1fȁR_h=yyPWvl\H jW)>84ܢэO%TM3c(gD9)=@ _]=!K/i?{w퐊r[Q!Os-`B92l*"0SMM9B"0X0ʣS.oŹ8G,Cqvlb@ *D| !0].Tdw0򂰫q^îuj!%eEDjUKpʃ4= -|C@,fX6jN`"dY;1Nl+.1W" +vp:zKPfV`i+ٰ8ٷ;";il4ٶh'vpZ`mt 0Kd4ٶNZu?KJaa[̔|6SS)Ỿ xW#Z'Y?ha[NyRɱ$4ۑnHV,I6}giKaRh#%ͨvR#~O>@=[FN;A{mv>v[`ZvpU ;EvpiQ >vp_,޾ea67+vXebu'6H$֮zTlMbCm]0S$6K"6'mNߔ7m$whN=}S1+גvG~H$u9ZP}GmH%JZg`sAľi2v0;87;#ab;x c^y;t`  +g~ChѦW{vq";X}ڶ;8Ej>icEi\uzm:l}vpaI0b{U46ӹ"S즾O[vUlž:A_^(Vop˻:K%O'yю9"%TItJr$_2m>fR56ؑ \$TU!jg,פK>ܞ9"hӾ>hϹ*>W۰wDvpHn: >@:ibida`aűbiqú\ɉubsXߖo~-;'){Hv[vWvpIwT~s]}2jؚ-5uStژžlwcxZBRvк|+vstW;.Y7Oqt;8fc0e: c-Y@qI{G8e7c1p3S'`w&a,1n1bsЌhLosXc̷a.a.17B$3Ru̸S fTU~03!_3TĘ; 07#̺sV ,3Xmhi;}1V?@t%;0h9A 6@N2oG{,FA`&F# @wpnǠUg-ځ@=mA7U0|YƼ>p0uߥ@6saЫg3oXlw3[|XлGl1Oьd cM16Eu: mMle@ou\Xҵ>]4^(FՂN`N:7n`2)~y!𴚔Ty%q4h0f]njMU66c OvUc@{hҫ]R6׹mqiP\jm߀~z.2m?]>072kBXDǤX/]㖄F3'B|j+㿶WD% `72a[IsWӇ8*|A UqU.ZpޭuU0U< AWo U;d{ ȴғ,I, ɶ3&k {݄!O Z2$'{:|2̅|iL״F Q& GI _l}P=pB=9 /po}TM晴t ּkY+^)o\a^V?n %/yxb]qB=(<$$ͅa$z"p$Uq!Joh/x[X.:^ Y KkD`8A)*ųms8x GDзsPH2bzFlc2To/dD`2#Z.#sQm|OL/x5xj}\AE״JR*WS$IEMT?+r|`}iaydy0IPQPDIFSPT9gaΎ||g9EE O _2 oi8:4\wOA5GiخB4 Ϲj|>rgSL=_v/PɼYm2f"'CY2^l/!Ge_E{\qLķq?'oZfo*oyc#Ќ ߦR_NQqs{LhCħbH= qF(W u1epmƹ18[ %EqIB|Bh@,!2PcĠG1Qins3/ /2DPN">2!Α8+6AP7a)Cɝb'~Xmhh71?Ӗt9|C)e^uQ^pt^%Ypǚ]ʧ)/Sgyf 5E:8jG̊ dIPHe+|cZ_Hr'K W:'ʫ ֛z5p&(o&Vvy%戊0 23 @7J'\ghv4|UG r Xo%KF?L73 GP^V ,8"x-; }@4sw"޽o3 'p  ~h.;kjXsz`qSLwӸSOsW)nwAwP坕[M>G%#g_"+pZ7wN.> }wN4.PJSܟ4<\W :j%gb)˪εP=Ap BEbaRp?g`;i ptJ(W[wp|\,S&$pmnn p%t779-UCCb%?Ix~* /y/+?( py]q&$yZV \xr \1ܷinmW~p+8~WplY ׊ Y袔ن4i( x?'H<ѫ~ZވR jؘ)f]9Ghub)JI(8 ] i5'O-KPa=72H.,-2e\n* µV1sߓ`r8aq~ oXB{wdP=~D_;G/Eɷ1Ow qRt+Z.|8 SPfÊ؟,>ڰD+?%}U6YN{`ɌO'_`ڻM7D + g{Őd;a {Rh|h%z}yl9Vr(`;X{n͢jq6t;l:ojæCOP7l{ع*hB  X!\˥QA2c_ƣOEe=lؽ*I]- +ez>J![;Y;شdUn8kԯIfߦ|Q)&ЯdtS;}ĝ$?fQӝa0J;Bs>MjO֦`ftk3}JL&2Rđ9#,GXF =3g1|}KvBi嬟x ÿ+gh5xjK66Ƒ q}!ka+g13G{*>S,sY㲼~?IN?0SzIl('%/$#6FIGpm1c;J8yPh` u#1(#1QӨ2p, I/)k (/9hdI(ߦ 0N:!!cN@5"Mi#EEK  \uXipV<+J2E/$vrw]ĭCbqˏK;;W6q7 NBrS~>‹ލpfn1 0ܭ+TmpAp9 >Dp6<\FI)*pg\^ݽb8b 8N 73NH.5k|@{p{ !ܼ$1" nev pwnI h" 7)Iw­#[yT+).8b%qp[)p xܒ={..Q sDn$m$p7-!;Epan1V+7m;+j pM~ 7 7.( ډ4p%A T(6 w";f\p^[C<\w:AA>\+\LJ  CqO("ŠkeO{,|8c+c1Z#wV2ȐЁ)c` &SRP/HALY<ַל]prd1ml'$dwmx9ǜxV z.u.>`jo OIbaۉBVdAş@F\fVi`espO.a2pؾ )5<ח]t3ļ˘J>1;,1rn}K$3Yi; 8WW=Y,.Yt\VDZ5Rg@vs!m*7 E(nn7 ]:PG*Y5Y+ gZl7~* 7K_l q2ǫ;Kn:'', IdNVE3 dOk;.wН9R\ v^cѹ H3 sgy |L H{EjpU`Zt,Tv$Rr~̝~)h/ KAvCC=a;D %'Nq1wTc_!Qm0M豪rHsmpZU󁪢P ʋ _Gr$Fo"7a{|Ib[ʱ.4\4ZVA d[̗ZC&W< IeJA#F9"4(a,%!zNDLxzR0ET^ઍnРpoF^)mPfc?5E cke p ʪU,B wq9as ^7a~/E)#sB87s. ]l(Uݺ W+Gh7{РpFj$0 nj%(pN<0JhPBMb4  ܻUGaܕpVn5n>nn8~Рpyj1ܯpC0ܼJg,tO[[B](/֜$pYL{{"<+J7 J)_.(Uڕ&8/l7ggC+\kРlp(p)pɁX9} p6-p[8;4k<ܵO &EV%KU`27kW%9R, ,X#%[nPnS!eSA -ܟaPqܡ>O> Bp妽0(kT&wq(lJD?%]. 74(a¨gCK2Qe̠|}iGֆT~͠ DJ)L7(ab"C{ O{jP 2(BA7gwvrڂzha @9$ThP^[Tt4WP A2(}7wpi}7A9rJ*A B7(AK@ ywA(X0BB%8Lwtn'1\\|ߣ`/ʠ<ݠtH0nPlA9~ QdPT2jP?àAٶ!F.vqKwA H/8 2wh'@u _!u(yBƪ :4ΘA@crޢ ?Á^_HR:KkwÔP{9HF6Ra,gvCٕyuAeEW"# ;zf+8=Ope܊t pt~;8u[n4gY-_s揙Jf5N,^'JY* v~TwDx'6biHoӇv>ރb!jBHyJQez25*kڅ]PK𰹠zCVl\E ώsB pzA:j,^[[uD*kTUp W`/ w5lJ`q:W vma_5/…aZ]KO- מּ;uW v]] G Yv;,ڽ5ޞǽ.*^pe KÌ fptFT%"ʷdWQq8X~Zj!MH0+!֥SZ<;s0~}>y99ޙ_?\7vН/Vryn̓!\>3r;ڗ۫̿Ͼ1n{q|onR^~jpnssd?|g!}wr{#>+ynZIǮS0kqh]އ= :V$R zv{\Х,e ՌvǕxmc[ Ȱ`{]\1݆;cl̰g`X7N* 0S*9UǣVK2TuѐcQ.>xۗĄGwv]SژҳM ZqR qg5TI XB|mV`[Tl; ?(tb*$+X#DR1tXi/m}'ڞ:9Y=~kBoo>c^sBnlS7r] boԮ>_ک::NK(7h":+=*neCm:;n;>`9Mz~C_DF٦Dʇ-+Ewtmat Q,XI5M鵺̈́"ޒ38u@x['B@ HtowSq7!SB0^y[B􏦄7!!āeK>.#D0_l6@:ɮ)SbT!V]jzPbqBFJa3ʪX>h}3{Ptkϗ1ʍ0@ ό[{1| ~`}z7G /KM+p8S!ɆNIfO7ɖD$!rn$$ICR4O>I}b?K}ZD$'I^KwgM,u;$zݩvpf'.K;vPЪc ?ap6*6[=nrG\ion;VVĪC&bU]4rxs&d<GK->x_N Qd!JŇ(NG::2` ~C4mrhopۼ l=OF3N J) 'g7ͣy,%yG@6@C UBL@ ~( tDX":ˈ0 ,yT1ȢO\tIAga5C2rF$0i?Fl_$?w2F̗1F1mQ FTQa_О{z$/^k2d-r2| U ӗy}пPGe s;~P>gЍ0A6~;Ƃ~=mi@4g:?iѤEۍ6Ңyz4-f UwiQU'-Zw}JNl~N:$oP_VM ';diQJԥ~ҏ Dda)K+sˌv4, PXK#s-HXZCYSZ/ܺollfHYlzy8uKe~V\h蚛\Fn5Ł\ƻfi_5ЖN~XKҜ] zr|yeJ$JhyDp}(0.jL2Riu;?yLt{OﯱM4UlY3L&2f`\A80|}{BQ"}R:/}MzX$ h/ǥP 谀K(hYVK@oh%@k}AK@*SZe_%" hFн;)z bt/J@YtNCZ4h'=ErCw?"Ǖ|Aዼ- |=pw@}S; O>XW $;`F1%vWVɘ903ƂB0Q P`@Nv"SNǶ! 1ů_ύ@|VE1O})QRTw5 T&-yWzxP=xDGQPɻƬtԽsbe@u ʈaцAؿq͚o+E3ǰWBTKcSDX^[= ۝.R#]]Glz*j+>/,.տAgEP*=!ЙШ,gvKal$`Os-0Oл7Ww9Jp<* bToLrsfϾ7mz~U0حXR9[BO7 C*ٹy\_4kde,j`΁x`uVhq./,ƹ"fVx}:Pl7B0gBYJ1YypMJ-$G[6g}n7N:>= 9RjkpfdCơ7TtvwaQiqB \*(v~ P}I>\f.V %Bӓ_\ c. ׻;Kd?[j֚1DbidMKFME*^1qk,]6;K"1\{t'@QpWxBOe~A$d3-?sau„b#IrDBHKH{nR@ MTQ>m]hTE;LJ^"qB"PL5IΨ?~xsbSǵչ:XG4~=\+-Rb墶(n&U1ę\lO4>EhLcF"@ nqY AQDC mrmiDR6ʆ% *̅ziуuHDb9SQ2&KGmҨ3B]&Ej mLԾzeGdSdǣL{G :ޗ 6F86֘C(ڋ:J'l`@#SSlJ"ѐ2ZׅE .;H!uja酹Bl\u.rxYpdQ`b-$H0f-5;O)jA[z _cUXDm9n s t6!_lUb!q/@byL.X6C5X$7-YDrxJ0#D 錏jCABFE"md\W1-%#8f3=1Tȗu :]tR%~7:% ' A<-̦elZMZ\Jyج-1Sq*#(ŤѠ\^ A&= 5Xj$ILl*bc-$Id:W& 94bSr[{H⥻g#gjY \tTƗ 3m34 f4l24":kRafdipy(Pcl\sHkBF˺P 8.%[$1$ĆF[ވL{sW\TU? Ψ74+ - m1PUtt&;xl64k-6eHALbyoCJ :Ϝ|{s}s\$C I6cЏVk pL1#.qI®:3i3}Oґ.l*ʨBOe\,9. Wz#9$$ ܯh؏2㲫^MINf7\eb.H벌cX[8z$ؙXawqKхԂapalt1J.v˶HV1ٷ՘]1P+Ä~Ń!*WKHcm&ܮ;h4n5dϴDcIW0yi)6 1 ma#F˶I!\uFFúHXuyeXn.3ha R8;gyVb)46\RxBPϦgВ.iʤnJȷr=%wt,u4faK¾4ՁI̾?3S I͎H[-dN$W9NgW'Dl/ o4lo5:2ԱV\*=8-#Jf&)ޣh&h|՟ؤK,JC}5"7)f2N(-,VZrCW+ںf%zWdڢ Ƣܰii%]deFדRѲJJLbzZ N;B4=B7M<(w8Y3l%fDBC-0т/TP^N9 ]^`S.m*Qh#ru Ưs)y{ñ"F)o8KՁm.N1=m8p&,1n4=Rdܢנ^Zrܿ$e'AOID<4& uwrgCnȦKꕍآX? Ѧu<}MdΣk7riWpr7^N1f7GP^>$W\zFlhuV}J-Vvxf({FB1-CDFrRؗ<͗"(p\ػGc<77bk*ݛ Wܫ{Ċ4d(p'hҊ'. exEgXBfx; -[w򌾰rh4B*\1ZF Gm~ wP*pOPo_\a'8}uU{VNSMҫک5Բӫ|c7vr >ҠGsCHexj +\WĕB4NktE^S3؝E|vc=2]Z0gw? 5(zt~}o׊spZqSֺb~ AWg)x,%=y.{fn:6MM*=k'JU67.|"h*FK ŖUݾd#(BQ0]G%;)4c- ?n3eR;ÊLz6kvYmI~s5t<|&VJMce$輹Fs"]+TKZk3զTDKsEkA!%- B;'ޯxdY~03'-kݴ&zXr1YJ.xK Ԃyqx5:%Dx^Cܲm3wTF x4k5uGa*2 ή+/xuPꇏugMim>kíPOzwMʼnN֦ɏdV!>53RƏildRfYLbYj`Ƌ8%wƤb e-!N2%^"hMQS7 jDt-vGr&g!vbxPmM5ݩQQD{h'WhQ" ?_66S$I!.^ximT >Go;-j*(.!?_Gc1"ZaG-ؒ(St>=D.=XsHңRIjNNp坧ş⯓tZEd4%J9n36>6ҿn&g8+7toPbEnR8 㺂myZyH &Cλ]] .1 wr|^'6vwKwa˼{Jx5PHZK$^M6>s5yԺi'jR# b FG]m3Uk[kldfϬ4nҸͷ"[PvGv!-BZK@<du|Pքa X+Ge6#k›zNⰻ3͎@ BMIEÔ Μ=ޕ7k(5k'*~ʋyjabTCJ9\ L'.zYf 3,3kY30geá߸F{R3RRguAN8$@KqX&vc%j^Fy;}r?"Bv連 C=jӯ+'59 t2p6TeĚZEި)=Qqce"q4ĵf5kx=Bb2!N9ͺLkLtR/"|ݛmݐ=:\ΰV<'?~:.C&[Eǥyʈq;KpDY|6d,z==Cu0)o"韝X&E:TwA%iLڻ$nw cCsqZwDCvo^_hsuZ;\ؗ maEzNɂJ$K_>m[^@Y}&Wՙ4ޕC9797lLwG}6k77@.7361yHGU<2`&[bp-hBgjW9&8!wjx~vNoXdҪ,؈Xg sl˲x<;֐7лev֒{eQClM[^pD+y̡ ;Ú[d+Tb1a(ģ({AٜB?X> |U]7wffW/ qdqF0LZ{Qſʢm|ґEw/8|nuDpU|]] Goԛʢs.:hnpg|вK.ACI-5>Q3RO ||Z|&_ȃS|vA3u+fcYi;c;?Xx|mvϨ;Ţ){bu5+ŕ YrabYVX9ES/kxS`;w79a̲)ЅdIؗP`k-]v) o%4?6aр%c⢛K!޳9Óf; D+!7R(k6ѝڠ&hEсrf$AԴu̍`MЄUc*"ǘZF2G1#S|xiKVhw]S?ֹs|]iAB l<{1?:T_oѭZ.ˉRN*ogY,iƷ Z %k񼏳QKݓ&hKߚ!~ʢwYc[Sr?4q@F{<,H5Slϒ4s` :/T]\U]t(.P\lr=4aA\g.n?\w'S]Lql뺸`B.po b=\X\W](.NR]D\ݝJEX3cx?0_wCōFŘ뺘P.i.‚g#\\TϹxs Tdt|0EsN]L  b}\x.6.(.R]<`j.‚88wqUs~z7U]\lR] U\q]Ѐ =.\N2tp[@F.Q\Q]t]ԉ8",WC⏧8'.wGu ت8[qqQ⏡sN] LDM'}.Ū.3 ř bek.  V\Pǹh]A[Eb !sX[.>[sĞŶǙ/r?6.㧧DP+zK՟ዑaJteU 6NTQx` 1hM{ih)J:iYoCtN-̞vEmkWDMO1QlI7O&t=6=:(W^ko&K6sHjb]As#*T dI;䘢HO#xp b1#zAi"xA!A6Zƀ:/Yy*UfUW곽IT+Jo+_bL6F6s Lq⹫ wbMmeMP3Z @c$]d{U-B& ~GN"l~Uq#j/4!L/tg =ml1-b|`:+\m0+MщD3\(sjCv.}5 2wlo*3E=r%xVzug_j(~{Y STg:\*T|Qd{OcUC$~Vju6+a(W^UP]8+{[Xߵ(lo_"Z_8+Db]&lo*z.w.Z;+2h66^wՆ1M=-~d ;}qtTmt4+aU#f&ՄuSHKT=j> ۻrI|L-ͺYebmWMOo.)lU^CrnZwu9w /{mRsO=%/I-s?,I5S{-6w׻qEWq_︮C;up E;E!b.Fj. dUu@qpz.Kߙ5aAlkaE{bAPqѤ(z=njŝ h]| s̬wHXU/*.ڮbܽ/",U:8XsJ_uq6]e?wb^7EXCs+FN6_*΃I݂k)..L!loe 9tUʸӠԕ? 92SS^7Uyu`$s:'DO"ŗTQ*GxΤ?#uu_9ƪ6^7Նc:;{_{?=>Q֦;9pOA@Ķ8C5TMֺ1u~5ߑϢQd{)pLp7xZDFp%ѻ,'~"/>y=:se7,0>ChzGh!K8=Qa;f.K#/FX '>K!{QK1 d{dº迾$GA_鏲]$e$?#U!Cl2s֞4-EXΊ~.t/@?!f]~}AJ-^^;`)~Vm).ݩn@z1^{dx{Kq~+fn?ӯv_PfBj_66hvc\#BeS,u#)6U(#JYOB*nFE=..U*:.w}$uŞtuQ7.E8Fw4Iir(vi{t| \~;";knC4,B3 ~/p?o)m)ّ qCALϴV@#\)=gAE^S= CIWհG} KBIe,dظC!(ܟ;JYvf<CsJ| ?P 78R"sBz +^aUp%8*5?BZ3<,Kpb"V|22%I9%IKY.=YN*'I%I[Z4? }X4-V0J+-AN˔ d*c|?HAPN!CG10ˌP7ѪrTR>P`6"ek%,(ړ_9{#({n/pvIHwW/e;Ы-YUfnܱ;d?ǯ3>#ɚ̹!1T0~gS3~YP;8ZG\0Af=njSM.&/lpGw-4@et,aoj;O}݉9S<x]W,L/;̽k[8hۮkv9wIQx3bHx|hBuR9a~9=#=LW=iV"[M O4_W/60QϚK,ɞ4&~#[G RXQ]?aRIo,ήol>jHo>%Ty~|tu*j."忂ɋc .ZaweOeCnGY{$uQ6!,yQ(#sx6f/>tdzAృ?p65_?\=%E$bHxLen>\:j UB nP:%chk79M1P,s(ZsJu!υ{yΡDܛY4q9=Sr}((Bpt((sosfs訣>[0~bb:#k[VOn再jj uDQV$;[Y>;:gL7/qU!EZ]3ZdEPv.\{cSp-ܩV05'{ ,bEOw<kkuҔ@ŠJ}[5'I=/\.xibݣ7<ޓtiirKȤFG=yƼ!)yISfFs4CGg~:)P G׻Cy̴tA:kH'IFW^0DTtR-!*S@{es S4m5~&q;:7{ 2X3.~tqQAoؓ/[O>β8Qǝ =?`_vVUi ,uǫG >wS3;>Qlxcw ;?k_>Ʊ#<} dlMاq,6M~a-80c6p(^6j4xp [ օul hMy冈.^S_Mxn] &ڴ11Q=icAS<i8 u42wLЪtQY&B:+D$8[{cJ^dŽax\k !9i-NSSvL%ib+]I 4S Cdž82a¸ecl\⡒H\9M-X&qat):!:=RlD̢IKB9LSX xtd.'sD'Fw^`ZhQqr : i_Slu;%iJKś+]u!@cˆLB˚^X!ܨ.Wp۫k uIManpve'G|~{@iG!=s#?&oN3BTaks&$o^FRb&*D' mO )ؘ֘QRE^tvY#ݣa{4 ̙^w5NL,Wv˰ReqC7DG-ëϕi^j h9N'5R1}Lrx!m&tk_i*4?+4U{MH9NiI+=w0Kw6shNVpX-oF-j53laT)1+Xb=~kʙR$ W@ՂPE ^mg:.:ԫN6# q^`\=\oVσz+PoS8@wX^q`O|f˶2`a3,LyVr\kNМ Uh?9|pӘQUf|מvJʛ8KgO*PyVDV*[-ǯ~7[Hm޽Y,80Yg!sR\k5@:ԜB|\CWr ˩]­PN =OqRu}\̥7$dPeO:p@&bN&2CH&2ӥj2f)v=I-deՒrLNf^x *Sj.^#q=ݛg %n ݚ)*@3un$ȴL'2򦡧Nq1$?<hKJҵa @vb|础:̘Ndp2ˆkmt{գ0Rhp ׵h Gol茿 P|8[r/d<kåtm0\sGhRth$-Ȝ Ȅ)]a>Cq:P]RM"M,@34TX&90TkCt=P|Hp1@ub'G)BSw 32+ n/Ctmp]tqN\Nf16SI3m wɺY.yzF)/V^.%w5\j̨]-*Bf,\*aҰ梢(_je犊P6XXo(hk׿قx찶jk~!Kn_/7]F?W>jD>|[Nj6.ޥ*>3*EWm&p6p8XyX ͇a |xXAݾww6_m5YPF ,Md|&~KNTA9P#vYHQ(~ ;Ϡ5xvyd>(P>>7Hxt> Cq-˥[Ob9}XGn5bTcS+_CjƟPcj<-źsQc!j&P+1B15֡FZWsPy|C\93pqEi"ok?=V*P^<Z~ŸަoI ?Ewn+5{>]%3_j _#L? ~!}7xso2^?9ޣS!< OϿ ~R,E^MOGO)}=|ieo{>]%ʳj?~?Dn$p\/=pM\ۭ\pӇ"yL=n-ym=\ʭąb=w/)zn?80A9|.Qʸ~Z[U&py#=7ۈJq8T<<>~8ڿӏS{~B^W?N?|~W?~vCxuwnWt=(ψ ?<?/ov|ۉu=y~?"n ;/OTD=9u^\>v\p8c<3z.翞n]s{\.W?8^ csP}D|'"w<[wrq~///P?Ɵ/ۡ?u]]zQ_zo_|A|_>?~WmW_? 0JB@W.)zXAɣ}~俇K 0E5+ 5J]nW.mݑkgvҖw\Ы^8w7O{zŪ!(-.푬mZ;=]|_pրqe!$wYB$HC Vq6k?;b@h1g&L> 8zN,hrPZI7(Xs3r0`V0B*}\Gƀz5 Чd;u&Q\bs2 Hd I@:arS[eȤ Ў&Q\bZHL L: 0%[`0M⒵`&`7K^BY"${@v&*K$|l%0yh2-JRSɍP&@ MR~( &u&Q\f(2yBp&7 d^]A&@-6@ Á.\UL6ZF0K9O[1kJUK1J 34[1Iwhʘ1g9NS1SI!K>aNld(,܀M~;%O+uL?>?E/a㢬=JI6*] 6n2k0.Evٖ-ؒ:6**RRQp>s9}y3|{y9 +)G s˗?T'K^Gc _iD~E9_9UmBsY#yxN8_"SN6~\2SΪZ>nAF ^tm@[wvui~_~k=owo6I|+_K>/7fa k.}]EDZ*H?X,mat;cŗ0Z_(Z=Zjt6݌Q7I⻁K|5_N>t[uF]bK/ϝ7ϫ}ls1'V_B'xa ܫDg-fH|?f|K%#7\Nܨk$ 6&]`|%71w[-e|dܷbog۞M%߇4Zf\ZZcki&frh_}~[TCks(.7':ʂ9=c?3Wޕb꡹D[Xrf|fVI|Hbیk3Jb b-TcE[%!-zg/01~Ip #Lı!u:O\Gy(:>gW1h4BɹK3mfQ;m|47/C/ĖKbl(>Up%M1LL$Eļ8%1͈bb$1ޡ1sc%1y'y1UӅGqIbxh̽LL$ >g[$o0žGY&1h̍T]6=*+lƬ3qT})gvy8$5mg_sɭE;~՟yMbAYC'I=7p~`r\[Ocܚ%s g/8*FlNILN=ZC;ӌ~$-Iiީc(K-M;u]znnIrļVKϭFɭ]{a)gn9HZwuKbۭJ #mD 9lEmn3^چ?\h6I$~&3[Q99D{K+=hY|=:IZj߁>=l^s` eb>8ڏ^ڎKzAkKh}= sKqï%j959e?Z]ZMWէS7Y=j0儿k߻bOpC?!8ZI8h*4wJwuC~u䁓>Dyqjp<:_Gxo~ʉspcn]Jo~C?QV~J}{OC)G*< _G8:_-{u abNuXQNUwJ|B߼/~f\.'y!m:g}v;n[6~ a׹mM5žEoķK|3k$u_We7n|{~9Cyp:gzxyNpз 8np9s)~ NX=ωA9^3cN-8΢]< XշBsFRi3m7Y2rҡ)pzyN8/pip9S }I9IbNg 8op9)~S  g<9rz$61;78yqbNxN=8Q~iQg#g~5ی)=]36g6Q'wx QmA>|U:_7 k@?iշLӬvO&>7ӲI|i|` jNk5Ow@|$~9K$' (~k==ޒ|UZsɴ`MRb-Z =Я@'g'~ҵ׊kQ~4U N3i'i2t8<'u2XopdNs&~sj)~W™pD]u>':̃ĜN)8p)p>s=F-Bs")[s)' k<N)p|"ωu'e $Lsnsi~SN3tcnQM9S)G* dNN 8v o#F?G_OTEN&~Am2qv Qu_sA*sx:{oۧ{>-M} ӸO#p} ZC܈<xi8; Qibз WC-OY_&~D~e:A~t8]??_f~ +$,?+ǩ~qw? SN 8oA?QqcA18Vp|NCω+f9ށ< /}N 8kxN'8p"owpy#[P}se< N:36pΌ<2- 8i3s9B#ԁV9kNM~s0Xo¾LCsVp8*Sp%W [٠2 \򳁚ȟ qb>*v9w4@"D942̼~,MYT,~}ji,ڧ>>]3Uȳ|Q1o? ]sle;Fn' +_&1oUvs8-9T?uXh^zWKwCYu/b46Ae>~qz$b|$~mY3#]?E:%owGzWDs D݈B!-FĿ5ػhK4[- c +?I5'il4ډ'u SX#V>HhTdO#|F֬u{@; !_G0O~Ӯ/7Љ[~JZP)[s\=}JAVİ[TFF7e3N({iJILi]/|n m_!x>hG~4=\0\W1ܷOfW0SmB}m\ZnLώ`G3u?;vh똊  CztM}:{32:Ce/n0(r42gW&]=zoOdoP;s<~ͼzn@YOYI2? Vc [Dd\&Lc)O>; tϻIIސe<{"27'/Y{]-_\xr%87_Wx>|εPr/ݨx"?d&v:Lΐ B\FeL?j[4+zYd @)i=Q* #lcTW2g%?[I8OǕbZRp "ZrCغ[Kiy[BKR¡% &AˊtZ.R3ٴLȮ?)Y ļYK"~h'c~wEn!\J,>hq>!HTwŔig&ԒkHKEEɉJ&s$^x;c1ٕtoŹbv!,Wsu憙JRbK,%+_1 J6U 89xJv2ѝ>MXdBZsi[}JU[Pa"3雓q+pN=DJ9Q*Ffg9aĩONwp\iHT`?=:M\]cJQ*-3k9_ZK`F-%-j?j̜a JF43'T.9c~8v"ZXJb`.&.nVQxŠ/FS2b1w^%L^@ߵ̝@#''i#ѧQv}hej¸serHڂv}EƮj 9N_s=3u֠Cڨ\l\5}qw>}- z|p|2C3qCKZJrk]-ix_KZN]E?=j}SqmrfXwk$oZo㴖tgD:ِp枋Z^k1Oj)vz9eySZ[ uH˝C!?yr2k~-MgeRo)1gEɚ=q΢^!{|OwcTKCŒ9p }r>+(=c E^Jo 8F$CF!B%qy|dB/9ϝ "x |`K&B\Rqܿ"_9V|i$p `E@bzCGYr|OSJܿ?Q"oP6L'~7@<N2un4C|w>g|&vZb38{YGN<ä"Pv$ %\;D$!|CJP|Ck}~${V/õA߄}}$>Y.q+0$qLDV֫Q\oETAt_=eFb9b[+pW8yssDkuItFLp ؓ`֟=Z=HýO"'qE#F1b9 ":¡6")Pg*2$c_/gI;l>&?Y$s|r/Y;3H,Xȩa^@/^Hԓu VZ)[4=|.y gk<󨄑CAA|`A 8zam(IN6t]u3+>HWl}(|{}cOASGh#g?^9w<6`_mOe}EĞ4Ox${ϳ޷ _߭^w_}nϞe}lgس`ϚUn2kB~jmuJEUߖ/٣ߤ[{J>x|'S.gќ(bOrGI~A4"MonTх^Dn0vd9{6hWY=.M<{D<yIpJ|CUh<>x"ַA{RQwcubֽFz7ҧGd/yXel5\XFQ'E_Zbh~H]e <.Kt3zSȋF~g'?h`2I(N;!ҕH:YFPgjgV!Hh[I~LYB;Id-;[=1?x޽_Q1V<ۀA.zi\y,DŽ o.'|N8>aum_WXg  E*J -iЌW/9ј7/~et9񉤝m\ Ҟ2B۝.v[zE3KyG=_Cx!_51fu*Xg7Aʴ=E] .#oTsltsHDd k"ڶ6Ε2|fmuʻE*S`oyZu6 y^TcW!~$Ȏ̻HI<߾k3y6fS֌OM˱]Ž|Y',k=/Y: W(F-yh{=M C2YCWO[џQ|&]dv s wd-|9v }~9cK˔8 Z& X8UC'kv_Zk!a}}QgiT|?06muY=H d eOb:w}`w܋IOᎯ1&џ'52Gmki k!s#ՕkS뙻bsI÷r5BWI5fY=>la. dWŐxDu E0(<:S|0?'z>=h%BSO$G52MxVy7988>(ﱎg9(ZХ;ߠ|ݠwNcFYOpoqZ֩!#\:{488>?KXLyja}}w)W8, HvW==q7<&M⡖BXHFodK$##K|oq ҵH/7g]́;ǵ@OwRx6Jo-p%i;+=}GZ|-WW Dp>xq'! ? 6~Wwg迗ޥm_YܟJ|GOD!v\Y#z'(ɳ]Y9=tK'oh}\wHZnD⑾_ [uN@I~rA:)8>KS S٣ \fiҳ/Q>1#_ wٗ98x-ěa!zya+.o7nF'R-`m-"Yn&竂_ǵ9 ϯka!ZSɔoc}q-}]qoui[`}_p ;uX':4kzEFV⻔#+Y;R= ;ᚮ쩡|%T(s?YGq.8TϳŞ2ޗ%sqp}i-۽wLE~@>-K%aov˟GQ)GF|pKo#8ci9#WJR_)-%CY ;W=2p*zxՐ)n/=,7^2"t]7qh+/RuO@ qVx8!Nyұ'ax͕Da^_D5T* €Pd(  os}n 䛭9};&ϸ}C"x\bkc`ipϷb@t6NLyѯ]½7Q^>an:SpڡױCƸb|D摶}{{yPZc9|m_6^%-EK.>O;/|2Wd\~pqឿ'T~#f#k!QoM}k)#YZq5?5|Z]F$ŷ*FF>b6V ˆWK/yι؄~#Y,[*; Ql[S/'-8Wזh)؏{1;q?.-ዩ؂șnXȑr#&c.$%bĴ$>""$:d؁oț:FC )Ssp SLuLM 7Q( y5IkDOgKO<@ 2؄d3|CL)2s aRCpVV[7 rey;dnK{,GgFIEސ#-3}yvJa$|A|Q4-#9 H\ЖXSoWa[aF0(}ɊQ"pߑh0,|010GɆ^'2&0?>xÙ/ KFױl$ib!(>F)n#ʆxcˈ5ɸl50Woc3pI&~,#B@Mb"4MěN2yQg2v1 'no6Px"sy'#< W$^@?L@!bJ lᗲ`~2uDZN?n\;\A'x%#*C,/TYM{ #}B/!Iy\EMXD -9NJ0 h_5nK;U|@%>0 w". gQvOP H}6#^zc C+Ϝ؏xP5Y8XIXwvG쟰/u.X{x'HzOƐ3\?⟵&Fs X(wK|=c>Exll鋳p.ŸWa_QM At$)<ɚ?P pqo1Wcf.P)E;a1W-=pQ'88؉HiX3x0Q {ATCs2Dnj'A6 @zLԎTDNFWHTˑըh;cGˑOH0GCp$ Hr,EL|@ܬFs {1 cpa #QNG9zv ŐFxM( 7e jF{ x;rf -hW\D+dn9k$iM"3q_lnøt zb1."E?35DTC/ wQm0s4t wfu:JQ g).8H= q/}c0qp# 7ao4` 6 ~ hr|A1?`X{TEgLAFq?rكYYPw}dj{(=ĠӨWZ4ڄ^_g;uv2tm>sGB<ȑYa ~jW1yV.࿚񵆶G:ʇo oh6@ķổ|nෝ[(ӑywQHi'| DCOu/eC#J؎YKא s.:0 ?#'y:ʚtrbq $(uvD3g?ڈ穃 9z~#S涫Tƚיo0##w`=nBfEm\+Ĺ|68P>.\-#L}x]{pK0Z`8^#/70P~ӈؖ:pEX?F %%ո iG`65wIxe|G ؋H@@CqH0p v'dBtDe|G|0 +q } p+m: ՍBulDN=N"Q obN Q=1zj?V~"uoTA!A>QcK#1 #V3kulWDAH]b#VP v|EaG،1omX9% sK:b#>"hc ؄;u;!s]/v' Md->X7==b TcL_?ÞHH~H8"2ʏɸ،+?11o%A3T=IDS{ O3 ϒ>jsn? (7:^]bm|G!E v+^e="^W8xoH!|Ͱ߳6b>o#1 ?"p3=7 s*&!W0{a}#H@1J$_ f҉@Y%Sy ud2^2PX K.%q I\^u0 ב؋{0G. (w %XH8HKc"U/ix>b#J$/)֘xDc)+D14r|DѨ^ yI1A C<4%ac{D%P*Gx}{I#%$/ȞKrb/$^Rd^2{#9װ?/ $H%XHxF'axIjH\DⴼI,a uCH>0 I2&؏gK>`6"fv!iV p)yv|F^⋅00aQV7a5Jk_|c6'  zhhX5؄88SF\$CjdBnFiTDuEc 10 [ p gq7q "1ɑ9%P01G7%p^"HD$%Dh]bHL%%ĕx_HBI$%$d\RHJI%%eM/$d̒EJ6.9$ܒGOK)(RLK ))RNK(TRMK )ԑRO-i$4f-ͥHKi%ŗپAtEJ7.=GJ?/d (Cd 2BF(-cd2A&$,SdL2Cf,-sd̓@"Y,Kd,BV*Y-kdA6& Uv!;e=W~9 #rTq9!'唜3rVy \+rUu!7ܖ;rW}y <'Tsy!/啼7V{ |/UwVWJ+,e+G ªp*"H*h*bX*x*JD*Jd*JRT*JҪt*ʠ2L*ʢl*ʡr\*ʣ?T>_PU!UXQEU1U\P%U)UZQeU9U^UPU%UYUQUU5U]P5U-U[QuU=U_5P U#X5QMU3孚GTTkڨOvTGIuV]TWMuW=TOKV}T_OW@5H VjjFjƩj&jjfjjZjZjZVjZ֩jڨ6jڪjکvjګ:::N:Ωꂺ.ꊺꆺnꎺzzz^zީ>~1hK.:#::c:::N:NS:N:N3:ά:ήs:έOtA]HEtQ]L%tI]JetY]NtE]IWUtU]MW5tM]Kut]]O tCH7MtSL{溅-u+Z6n;QwҝuUwuSҽuW=P҃uazGQzqz'IzizgYzyz^Ez^ez^WUz^uzޠ7Mzޢmzޡw]zޣ}z>C>c>OS>s/Kko[{~G~g~_W~w?Oo_Lhcel2aLX΄7LDD6QLTD71LL6qL\7 LB$6ILR$7)LJʤ6iLZΤ7LFd6YLVd79LN6yL^g)d ")f)eJ2)gʛ d**fej:gid&ifMsim|Mδ7Lh:Φjizަkfh& 1C03܌0#(3ڌ1c83L0$3L1S4303,31s<3,0 ",1K2ܬ0+*ڬ1k:l0&l1[60;.1{>0!s1G1sܜ0')sڜ1g9s\0%s\1W5s07-s1w=s<0#<1O3ܼ0/+ڼ1o;|0'|1_70?/~eiXe[岼0VX+ފ`E"Y(VT+݊aŴbY8V\+J`%Y$VR+JaRY4VZ+`e2Y,VV+arYVKXm-?jg:XVGbuYݭVOcY@k5 XCapk5eXcqxk5њdMXSitk5ӚeͶXsy|kZd-XKerkZeXkuzkdmX[mvkeX{}~u:dXGcqu:eXgsyuѺd]XWkuuӺeݶXw{}zd=XOgszeXow{d}X_owŶO6e۶cl/;g#Hvd;ՎfGc1Xvl;׎gǷ Dvb;Nf'S)Tvj;Ng3Lvf;fgs9\vn;g Bva].fK%Rvi].g+Jve]ծfWk5Zvm]׮g׷ Fvcnf{ne}6v[vdw]nvwe}~v{=d!P{=aG٣1X{=ޞ`O'ٓ)T{=ݞaϴgٳ9\{=^`/ً%R{^aW٫5Z{`o7ٛ-V{awٻ=^{>`ه#Q}>aO٧3Y}޾`_/ٗ+U}ݾaߴoٷ;]}~`?ُ'S~a_ٯ7[`?ٟ/WaWvc98. u9ND'ىDu9ѝNL'ۉu9NB'I$u9ɝNJ'Iu9 NF'du9ٝNN'up9NAS)u9ŝNIS)u9 NES٩Tu9՝NMS۩u9NCi4u9NstZ9_svN{tt:9.NWtz9> j"iATEz$( :!@ 6: +EP:T:XI n{v~C?s̽{}Tϩ5R^Th5FU/+j&Wku5QMRo7[mjfjU5WSC@-T'SH-VKRL-W+JJVkZN}>WQmR_jR}mjڡv]jڣVߨowjڧ:W?O:~VU~W uRRuVSO[UEuI]VWU_-Pp'(@ `PPP C ( @)( }PB9("@%x*C ՠ:ԀP jCԅzP@Cx?4hM!!@shv(Ct :C ݠ;DC zC C DH0!Ra0 0 < O < 0F "0K2`6&6  `7쁯8{~08 W ~pN)8 g,  "\pi7c ❨QPc! cb1,%$ރ4ދa,AU*VXkb-uІ~Xa}l !lG 6 fͱ1[ [ccFb| b;lQ;b']+v=0{b,8}/c? 81  LL80|$>O3,>/8 G/ 8_u |·Sp*N8g,{>8!.~৸\p9 W\3܈ ܌[K 6܎;p'ݸo[>܏ x_(_7 <4x'?/^x /mt;; I40(TJ=TJӽtTSHTTR5N5&բTlGuէԐFOazPS @ `jF!ԜZB%=J5Q8EP$Ǩ-E#uԅR7Nԃb'R/ԇR<@(h dJTLCh( i8=AOS4=Cs<Hh4L8OUz^4ޠ7-zޡ44N3h&͢.GKh>}@ZH }Jh1- ZIh5>i=mʹh+mv.M{kh/t!~':LGg1~8tN:K<!/̟𧼈^x9W^3ț [K6;x'ݼo[> |_(_7 >ɧ4|'?/_|/mr;)J@PHXDRBJ=RJJ˽rRNK(RIREJ5.5ԒRGl'uԗPF/ayDHS @ `i&!\Z]B<*IDHǤ%tEJ7.CbJ/GJ $Qd dIT,Cd eelIͲEdlCv.-{kFdr@!^~'9,GgE1U~帜rJN9+!_#(\ܦoS+ 5i֢.҅u}.⺄.ѥti}Oeu9]^W]I?+*k:ڦt]]O tCnuc~D7MuA:X7!n:TԏV:BG61Vu;Nn=tcu/{>tDA:YT6Y-fP)AHJ}cScSSCSR{^SPPPT@1))q1E~vUHi)cbcRw34>"@릧ȬӞ%niGYv%Leew pYFHlҀ qÜYw=+C41p.[3/>F8| |rGHGݾi)!҄ret ~pϮfE&^ଥA]4o3eYRG[%2aLi5&W=sa.f\y#+7[>fr1pg}tayac3>nq ;d/5loҰ= ߬a4l"G7k& ۣѰی7[-_N ;oxaX/44]g3:yHxIꂷ:|HDKIꂷ:|RgTHvQRT縘vQRTf'c8ns?+~`#1Zz⸵kӥlD7ːz3*۞lǨzk|&l'Q겎:ǡj~yi%49PCͣԥL+P3>=:$9`oW4_Zc"ʼ y:1K2o/.x+m2o.x+{Eyq-2d/=tK똭@c@ȸk7Ȗt-oxiW|muq qN/ɛלN!y=_ q-q_5f580}nx97&&6?J*c)Cey.%$I߬3^ӚkZ5oHOLJ`y b=~0YAY?2S8q.VbMNVYCR\Y굒:]kSƿZk%uVu뭗z*˭hyJ+J/|Ci:gHSpʠTҥK H1?]iOWMϐ 7Qtڴ>Yt̐H7΁lqqL90M`Dz`Gdx˴#CZFQ\K:)6>wR_8㬟-aa3Z v (; ,2:m9ڣ\'}Op̾מr6M d(CuS|G,8k23\/spow7"G'Koe''Ƨ$t!]352q-o̫1a]œj39~10u`unĺu ֽ[9eN-21hEw շxֶ\Zn잙GrZ[fƒe-3`e ֧i&IM,s;خ"0wM/=u]LTOѲ*ȹ?ZãGYp}ƌAi3[ӜlZ@'P66Sslž+:aeNy";8<]F t3 ]Ft3w ۘN:~K籪tDaQc\sr.Fɽt? ᜇt? \s $ Z]ccn¡T, &`@ !683bF"?`7 H=m_)CNцF.VەJNJ5H_A>G]5N/|Ad5%@TCAR ܼ:v cG|Ad; **̠Ϋ8N)B,3e9$\9J%FK-Q-15ZrhK Tu[Xp 79 c><-mxE` }W}@y Ui=(+0BbQ]^D ? ?S]:[ !L2o_}<ll7xe 51}$}zȑb 4z6Z\Ruǹ(k-[,\ K51 ymmGNv5v@_t0??4߮,~hge #t$7m<#Զ~YLQe]4Ugnal*1գ{ܮ6Z}$7 JIeZr| PmLau YܱW G"9+BέZ A~ YN/j'CSv;ߐ" _;='9֏.u)13W1=pTǥxdZgʏ/8 ,A!YۺHZGBr@OAgOSCBT=g.5C!a7S f:c,A!m]L~CAzXDz[Ecz\CzX3Cz[zaZ rd!Q!  {(8T֟-V+&% ?Un!ζf,cs ]bQq@: ~}=Z:2ֱ?/VArsZtEw3#Rtľ-3V b=b]햴l2d@Ȭ*\P%(trl.U_FPKu‘S!ޒˍ*Xv#.t~[.h̀Ȇ6Hil2`x-Y,θiomR$FqF:r M{XLy9ֲ%'~񼛥A;D( Ʒ#:0쟅9D'E=0dٛ$F H+MZuV5NB :'dk!&G& N78!&딅7bGQt¢&M yBǫ!zTWԴتL7+hԬA5 hKVШI' eN{Y.,A2F!@2).Bn7v7Ao뽱o>}&o2E_EO7@ zN(q:>:]qX1NX1Nc7ae8-Vvѱc+;i39-6~Fp|4>f:tUzb8.vb8/v`L0FqcXGse<Ν"0:4gpFgu25fX/~(EtmC1[Yu1cr[U~ {ѓ={l 51c\351c\3fؘ1cƌqMlؘ̞]0k'^I}̮żmU8ƎsMܞX4sbeǸ'VvbeǸ(Vv̰cƍX=;Y3+gpFk)ptKTaDŽ0d&J&oc&l1,z vˑhIF:qis%+ XuyIY@rpl`U=6>W"н~˛XVՓn= Ι96c"[FBbt:6y խnE$j%MF6uN.tEqE.oriHl|Fbܪ Λ0pS wPم~ # K%biDt(Y&vX _eÄM2D RHӹSj;`. 6VvÖ۶z*@HTb8@"ApNsZpfZO. Qv~T4_A;q4o::`-6>)  _M_M׋o _R%;3lnRR̕ːAZ% ?ړEunFrjsz`iڡveڍ\PmgO#O¦uڍOS _+]ITeFnc_W$R`TM3D.ڊS0V6Sj9v8!A!:@-:hzղp&YUkL9Eԕ/k&՘k7Z)t(59NF^1UsЄ,̍`\ kGrA"qU ͠SU#hjK-h'ue"ʈ~'pf'6熆:0`Ni&`4 tq$.O6+SYd85iQ.L/G.%?\{:~9vU˕:Id'&Q4j] Ur+`~mA*It<8)"`$4DX[/jBn1G;E /kd#-`[xԴj1>{qVV8]kL0F'FwG &^@)klEQd\'70몤EbxWQM*K& 9FQܺy" pEtoˤr!ƵBGhmeFr9u15@(J&H%`e뾈SqX x$J@7S'~m?陪h:hnjS?:q\IPDrSF?Sgǻ>-"YGD,jݱ8<R )6u)VTb@bբ:m)8`3EV7LC4ǫWљ842u|G"YiZp=W4;f$2=84߮, JFY/-' #Me dYIfP-(7~AA/x r^84_C +XҎ 'ƘQhez<\Foƙ vyX0·lW&r[ =kJi+y՞~ ZR`ARw73w(6iO1D[52(De n([x{!$[?r M9z8\q}\kuws[vlrŎVouFsJsvoS2Wo56_eĊJ^V]bʑx"M-d8QGHV`\fr['V:1Br\'3# EDZEr֑9I.ʆ is'wËg Q 0u6HlE К6`n޲SrJg Z%(ތCvgk@k/$0Fy 1)Ti4me;7vɍ+5lԦ8qOx 9mgWWW63`sRI;ZʶkVP)R ?UQ*mPB+}]eF,Y7 Lr!U&]qDD.i-wvz8<š1LhN9@Bڋ:KBs^#*c,U-g8l>i|,fXC9CY yD\)׎J8ZvZ y`kl1ړ:L U: oQa`#$ tIO u`tIX)%;4ilɘjDK%Z>5>Q2@zYfeZ>$3!3qJW;Kju+M-ڬנ^BxSf>]AIxr za] ?dJ (D8d (8dNwUJ;lT*n::J*_ ._tI@ry,ъ}ێO<4 9QAF8R2PJx@3 JU*ʍgXڛ\9ek,c,)GXƊ#{~},I/]kBE{ L;̀DtwG1\L‚9i`O8r4W=AܴW~8c6Z,n'MjavWI5!VRˋDM21mnȍשTRw!&A jDaFF*'F:xS}Xʡޓ+ ͒Iȝ%{^u^[XTk1@9Z g4~`( &<.7AnxAKCB ٹRm6] rpBڢ Iaas c8n`c,te]2'\DaG]X߱9'nO(%.BYLѼL2IX2CxSk a&ߜQ_ hRH/I;"(EƜcye{9 ʫX87j6,i!Zy G)u)WQvL_Q7n5-kdZwPԏPl(;|Zwrr Sf=;ѐ|~u+0 ~Jʥvɳ QXmbI8,'©W)6劼%uH:V: eF(>Bpzo۔^gH})%Q( evvںeCn$['AF*uP"ՀF5U*l-_EVN5KNNb3YP6d,4>QH]Qjq05.v8zg.7m=(q^cCv7G"z09L| B+YR;GMo[-ކoo6x%+`ύ[DA1GFݞ\^i XrBl'3li?N 4C)Gg륦e| `IG ]vǯ QI4EmuTY.N5}(/uԆRϪ'X1NmwumE~!g&KuF:Ʋа>+3.+FHIm4[zUlU沤p]9$ ABVZĉ 8 Pg "4j]ʽR,Du@Pd$^ꀠ ~!:|Qj~nZfJdkq$֚)iR[,[Lg; x S$Z$B-78X75C8[5y|BSj𱻴Z뭙kq9ewGjmR9{]ղ;qvd0pAIB[-T;sR(MO jjRoƱ]"S@:9ǰʏaw w"͝ˉ]J\_/J챗|zٻ y ^`Fp}zٻ ~aÊ\{{{q#TM%(qm"uAy]n ΍:R H{SHM!A061EJ=(a/r ]Wc ̺uhagvtU5/=_uz@$3/%3?.% |vcQ]3pNh$#1T4ӧKzdj(qfA4A%C>deR_{ràHDԌC0rrfjdINH ?Npe6̤ vd?>{B`ec&UNKUNKUΩRK{I/.&?$ӫSZRU7bRl8Ml.2߀}ǬfTՇe$owC1 k9|q v+s" is̠N:RR*vkϤ#(S.At8UmBPoʊG ^Lٰv. z͆tt LG=ķ {dp&~q k:GY,וQv+pGFk%í}/1|Gfvn1z9#\ ڕ8lWf7Dr=;P-g[ML ;6Ih]Zi00-jm$(JDcTBlGP~X '6PHO5wN3HKU\&D9JEQY;]8nP) l+OU&ֺKm2/MuSuȩ4Vi#€wB`GYj/'y@t6?=cFrvd\ !Ff3gǼWC>hG ^d1 < z( \!9zUMtW9aW?&yP&T<@j޳;>σZRH~a|PyϏP{0lDä>me[DzmcنX>mS˦f٧TlEA<>y[odCXCȆ0 F~SDِoCm%[/!DG 0KbDl/DDE>-ݵG&eJ0zԶ2Db+P,'ϳ~N \"e?EF=}c?)jk,c+U{}<*y9AxwWJ|9V)wkzlzD)`#:eE"P y;PQ?ɺ1WъY8 IhBuTKWkLj{Oj4ʸݶmwmO7s#MpK=E3|`{9uϔZr.?Q5?&LV cۙT:ZղUZ*VSzf#]:_ =\CYHX%pd<s@ z dPEZy٣ZV(,T\,D Q5 4U$N Ic|O@zHCvIh2\ -c{|@ ,a=ʰZ[q96HRb,bʔ$P}gaA=0gca7kG!d["c %_lJzmB]@&ْ2U[ӘUʾI&G"z[9Bl"cH$*D1kH,0sS f2*kk(fLtbcVv[l:hsA~|7[%)+X %_2KB]F@}j a M[ʼn|\)oYѧTBIVL=Rl-Ld8Vb,ЕJb,gCH;(Y'+#OcR\d1l4n9b ۥ쌮_&|՛U7&~ÅTp]u tk1Ittt60ŸV%Ch[sjVdaGCnYԸ.R(<'t]#y^9T+GGW *LG~-g^՝Gb^%ar2[@!Z9^ $QԤK<,WN1 ")%fJ#ic:RR6#9d4NofK ve> $1@@ I̧$IX$|Ib>$1o@6 I̧$ h"Y4r<'-oAm-@JY '*$B~\-SB)ꄷrRl8=(6Vyyz"ꆼ>1׬LV[FmfqOܭ@p HXxԔfgVM=t>"WzҡddR:4i9jtd&]Ng#&sLNLjMR7]Y2aҎ0h/h#IT$zCu,-lw}EaNoК)#lG/dIx̩MO"9Ic:1{HŖ]pR%;}TJgNU.+xZfmB}h8vhǙ ,d{{9$-_qN}{Ž-Z,q}?OTF)XOاj_D,A 1 bA>DΫC9OК"ChÊpj#ҵx(GQ:B䷶gBB !zK!cI)4AM Xr%v$QIlHKGrYK竕vq4]c>@X BUJBJBI'(˕+拖K櫗 kH˘H+HcRdJT. /2.A[>UJ'*'-;=)ΧYYi*]3xtV=䋔yn߭ˊt= O) :٣qީ}y:CwVARf7A'tVAR'y:FUR긎sҤק:wM ]؃^{ۃN&s*YzH&]Dzh$#uWD{P_`{ xbWf^H+G^(?l},HN\wp?Xfj{!x]sINz$_u7{ ܞ"1*͝޳qs`BsT;`hMJ-\&UR::oGsIx4OfSUGĂ`ypx<{%?:Ypx<Ё@1_H1u!rl6+&I :iJjRlu"bi7Z-72t9bp営mÕ[f&# Ki[ToDZM>ՕӒ5 Pl@BVah)n;> kaF*zIO=>2~ ]hVڤ@³fS LfTdi~z],LVچcɤz0Xvaٙgp:6Mp1Fb\mQ`@[/sLWڝJYgi9izJgSʧrer'Ċv@.'oy3~d}bA9 '4jJ)*#ZceK Zm8iV˕2yhN G9O5p\~C+JgϽ5 NeJ525uӞ},ԞTլ-F踪O"1|~A.vqW狭\fVw3` @6u hf[Vkw!CB4˴e6]k؊_g,,~-_+Ju.W ~eTFQYxEy v OX dq!KTp>0S:K*(=JqcпF\6\4G/_W~#>6zCGxOu|*ͼ,:)YFuhrDQg~d(\7Ulc!Lg \x͠q52mGb;<%$6\%Cq^{͘s:Gp8b9-4NQ r7< 7D^$^ZqڟO?4v73N|e09NX9{_9R+OWW֪zRK ]feʍ+s7pPزyk7ptm|?nVOg̋V@y}KES.lF󿏛V7nw ҡ/}҂eο!˳izco7vE>ՀUϜc0X֟rs?iq 0۾wI`}49P. 6߃:-wW$.f4,_?Mhjjr=0h͵ W\3/#1 J@)}!@F uK*SKZ ohT(O):^a%HX L(55A6D੆zFW6~$KwPAn!QeD@3p}0ȭZ 1·`5JNKw|;2e7iUMv6\ψ"]zyj#<.P9e-n r)tLO+bƷil YGGkp t?آb0k&KY|dq_/Wg+Uj>Ƌ|/F~|k=^~9?#Q????s!! l*. 5!:zppT8&nnNw w   'GS£B 8$4,<"|VxexU0±Ά HxKx<-=\τkp;<|Ex@`uÇG7o #|"|W ?>^]]-GgDű%eb+cbVǸX6V 6Fb[bmrl&Vڱ؞ؾ+bcbcŮ];;oo3Z|.ގ//_??&~(~]c[;~oO?$'$&%'V$JLJXB"D!1ؘIlI'%'ʉD-1h'{{'HOHL\8.q}pHıĭw$N$Jܝ7q_ă'$N%MKK˒˓+g%W&W%Mޘ<<<5y{]YJqxZĘ(Y q8.neqFsb[\{}~xPF<$^'^/7GcqVvxxxxxR|D<%>*RSKRKSRS+RgVVMNq)!KlHmLSRSLKS =}SWIJ]>u8u$uchXx;R'RwNݛ/@éGRR%e+ӫWt6]HG[mrz&]Kϥ+פK_>>1}4},}<}kwMߗ~ `#SGӁ̲̒̊YUs33\F2b&)d23#-̶L93e2BfOfof_k22eܘ9995s{̉]3fT|t_0/[ne mAgْe^ےۓsvr!7y0y}fw$H><\".ŞuKv[1R>u\ (a+VwceG˒ib?6,E:Eȶ[w] u[}ɲ{h%).Cޫ %uJUˠzw{;==- e}=-t@pXxD{ Y裬|u)Q>-ƞOBC8;WbLhoh@á#CBw=:z4 K2aB8KX%BVfG+̑z%>߫cT$^DJUrTU}SDj`' 99vrb#炍эёhlr%^=6rWчD*6bRkׁ;@ñGbƖWύ)njJ5F]u1DT9X3^rlFIl*fW'HJw'JfWzlܫٕOhvǺ]ucZvWrRcr.9"hf]z9FntY En! 5Z\B jT8)X6}lcrlL]?;s'K{ UK[o,m)Lx%d>Lod%!9-Bj= ]:v8t+X`eNQlm1j*FaD"lڊuÐߡƌw)ؖ32D9Eg' k=~44,?zN4D7DǢ!;X!]|do /{v|'<Ռm{7wzWC A=--#ߍ|"`_M`ŋuW׊_oL>/]L7H_xky`'7A#ǭ /6Z0w¿ #E[[{"ENE}ثc}(vS{_'\I;|ϯ$^d! ^+ T]bue$ѡ@ Qν +ėC?9=E*@/D.+ D|[Ş&~u0şH%.L:aFp#d{"L'$ǒN_zujMJC?rQ,gp/ U~!-GY|,Lb|kɸ8^Aw驗6{-@mZ~^ x[v:Y^-z_&prP)]>z{]V;3bbWħg$$ߞ.d;Myqȶ8_$fjwR>!u',M=+}vШ`:^ =֛ řge^ grb=w"8W˸ &.faDɟgc2ˁ,>< ¿*/ /CB>ۡC 0,w?p0^З 3揇Z;&~x-D]!|CZwugKa>prC xY[0\~y:-RC0^9yZj3Zt4ϙ(xA{Ip>a>t cP.4z ĻBO."$^x[M`諣狳+AC/WGTyUZᛐY(MAXO}-"=j`_/ + gû yq$ ~&(ĝܢry׉S(xrEwLe;Oۻ=|nMJdE*bŶ$K`R-~ ɩO]B+2_0s&phQ@^_\"… o -n῅9`nv do]V>Z!'DYa:7 ' 0̃FgnX/2^J<&~%Oq_N7o숮qUtL<'FT_%%~;?HI|9M^ VJѳ[Ń7de?^>%τ9M/SF0X8~FEs9߽Afp[g_%Mk-О[&|6]D(+~@_SI%R`V3>H='t,5 /oK?~jG2;3We E&rs3q*7ǽ; ý8 V %OA||;1UV(8T;C }=t'd1 yPÐZ[#sa "[oqNx+`'臢/}26_^É əd#0kr8>]=!Yj *w@nI"#e3̻237dCY8-p; <W)dÐo6!|--O ,t^hszGݡӅ94n ?΃~Ax!<)B& dK|t(:GU4;aN< Yρ'N&/|{$<[|O|xT<礆SSO})K2/ɤ`x)~[NS|ē\  ?3n 6w¼/P0 eBeS 9a6UJŒ#>/'[wj7AVߑgFτyяO9 1|l06'v?q/+͉%kbz/ŗ(7_;h;Miʼe3g[k!(M#qZ0K%0{k[y{Bo7Qa.)RzȋoR/ucπM OMJ&*> NwqbcKCWB"!d?~'pym|,sВbX.;nh5vv~xrhd.qohj:pr;&O[aVǃOs ^^(%կ+#wDC.@حM.L=> 柋R3!ِx̝1?Wz=#NDbsN\!y_L"= 2Փӫ|+1mr [aYn7o]s׈Q7 [^.|oP[GB'ymQ)*&%  3 oCOW00û~7vhT]hp4rG5X;`'+ɧyAޖz4uE` oXvҟE%w =SA~~edy h.v[O0{Ň`[o%!_xzlJ|7{a7|%mH:6k'0NW0G'/M\*˾| I@1ys?o'`U銿{U\s~\}oh +O|ryClȆԌн U/6 ۅi˅ ?<_( Dۑяs؋GA|"L|'7ىsA]OL$.歐(9k%&ǒ -'8 { 4׋7_q3a1~⏤H??,|WzD?Xyf&l `ss=;{* >?|h*aN̂^ p8Y"InFv oam }$ط' "~0~(->8Xd-y^r?/L<%fS-o c;JyS9_=Ya r& еC-e… O^qsϘ*cDŽq]Ԫ#¥^^H砖mɓey#Kg9XE~Sz j/qԆD7kǐzuKN+[ucfvټux۩=ݏC{>Jb2&*d3}P^NVpo8o_/`(x$/ďœWEqDIo[w;(}.5Hy8rە;!JG&K DΈU+*;F2RTCPs/TUUۅO}HNZv'{+#?.FNSg U='+VC|^tF:/,]'ɏ +jNnJؙC @)2KVǻ}AU{1j:Is\t2]z٦i6Q=Lg-֠ݸd04Ÿjax9̼crj7047ESs/[Bs<`En4/XExjl{"*ZKmFWX[}1}>:IKn(:Mۏ,;bRn..7N*GP'PiXIwVT =Z|xciHS]TiD`zLI2}ØdZm|I/Fo{N/A~A_EO>:c}㒁{;oiMpB/F9'|>9KD+[Tq"-O;v?#7ޅ$wy۔LO0W@6]FRs.͝(tXo"0BAƕ #'gE_$g=A(nR.D?n%P>"o""s_G5{ycr)Vi+Y}BRk4;=¯.сLe286 Zgb5`K#|!I8P2Y'$g]bXi]]E[}B-{ >sX5{?2n6E߬= H1 jĞ_F^CVm6/vjOEP<}:C}Iw<lh2z -/B=AC{` N)9[(BSjo7 ҸCp?qirܫ讫P&ב@F|:vCO-5p$HB38 JjV]- 8X%b8K'>-f[bxFzG$+i-EI ,)5sQ"USdkB"dn+QΔsxr%4JH΋do)H$T/NIPRSEJ hW|T?tajM};O}+0EP4K 8-DoBJcUZF֡y鎮9ha4GIo^W5d*NC3B8ToUFHq]fiFf8[aVAȵJ!Tt1Y9j(MJt"C!ѵfۑwTS1L%ro2glRDZc46-aMH{BDs\WUr\3:q_j$6Op !HF B'k:Ϲ\AX,kK $ 'eRB& dH9FNCv*r R(E"3n$8%-#gMJ{@7O-֔L=E&Z4c&Xt,%=:O#Y6E^~YHegUF Pl7llJfs|jQodZCQNa$T"yVA.b9Q&(VgipA2qbxbCQ&"w"XV>B0 S׭i o. G1|e \%hdL!G(E[ \[v_tA"G\ [/6b.$o!y* 8SvKfDJy=,JM?M-'r*-d6LB4<@)VŦz.5ƫejZ ~~dC"I9Uk]d *Uԋr7u7@/2J5_7>]jY`Vm譽luqgS T gkFb,}.fS,L6!/em'!d0;LUw1ۛwBB"b%H/%)5B#g 1|.%,&Sp8)EJ#ܭ"퐼$f.Q д)AƑdF+EU i)tN8[-PRmM$ޛzQk֬`8nj)u.tښSCw0S)Щfai`GQXOҨ.-R-TΑ`h\R:(ZrL&9`֥0|`XfY6'\\0g5)DVG8!  *wisPt ,1G,ĺ.me87jl%Z k/EJX9D.ZW/ljp +cT" +5-2̇V6A/gKHNt 3"hplAttmIf怛Vj6ċgB_K`%^k:pE)Q 4b.Bʄssy`5bg2 E[ .>|Z6VuPP0RU:mqv5L+K14mf x2mV B&IjRy6]N;xx fZ%.+j,AEB'$@lYg7*]3Vu4G ouսW^ݫ{uսW^ݫ{Z.mTƈ{6jUت_zd8N=<>m={>{tO/ U0ӏa+pM ig-|&W5w7/}ߘÏ+_}==u 2~CexRv/SʜC6ᅭ%ң{{z3 |m蜺`NҒKv_8JtȒ'ĂN #,Ivtr˨wE)JfٗhnvzgxxvN<^68hƔǨ^xWw쿱i(kO-PS#Um}'hgyܼИy7߽V >v݇'~ې6 [&yܷߜ0󉒌1c,N]~v}hszs #Vb͜CJKd7\(y  ʉY o/\+u;җzrʹWn~AIډVԾ\:(OTTz!T4#$m1cީko~=?qO WTxy?c~i1̶}>2zrnY.T{UK\põ<^L6GoN_PQ;>g]գőcfd9;~ u/G,qgwθrৈ|_Eu8۲r_ SZg#moua#") s{݊.7-o*^3څτ5 /.xWlVZ<5oޅٟiN8`k_*?mgݪ?Q*tU2<;U7oFo1%`A&,# Os}u{@YO<`d3v^zE̍[dqoml5sc~x\ /Ƴ:CUؿ칓 s%_GV^ OGĶmZ?9>1Fӧ'ؽv#G_*a铻&~ʰM/1K>p-;m`n޸'쵷RyOyi+6دzU?&;rW\K)jϥw83 A͛l2ex?@<}ӴwQM$[!lB, " BF0}P0"LDuPU6YdD d0*Ce_DF%8g>73gΙ99V %:>҄59~5*,ao! |Nn.;?1 B8vUl36wfkT20/aʈN:[@6m.&f8 *Q>Z_beF*cɞQVqgccR.Q›- >Dr\;Ǟcy.Xi̽%(zz+ pA\O4UzUF+heH:6?xep0oeZDx~iئX' ޣ0/s.Ce#e.Rg?*DI C/)`Մ͡{}`Lx1o1AVPuߨOQ&qԙeL mw5=bZj͒nj9Y)-C/k Dw*u@rRAp&^2düBr>q4 py !f?&L iv`{aaYByZ~yԨȽ NNJ;G {|.-U~`vPцgR@P<Ԗh:ʱK۬R]ģ8QܸdάfhN7HvvH۸f [x!'}` bIvK #"^*F[e!o܈u6n@@\׾IQ?A$tUU1'UpPS%xcY_c*ʞh*"S}@d`Zjw]Kį6>Rl";B&m΁6a=G/d|LOVl=1> spyafi47rT+>ڄ[͑"~Qj2TЌ?R|+=~rLa_m2e4*_^?|7TS&mZ~+ʋ ǐ֌Dá.ۑ!&֋-)1w:Z^n'Bp_EH޲7ӸԠw hMJѫiv<=e+TE|K堹]y9)OUq_hJgR8x N ] zސ~̡Q`7h'rkvoӔr $s߷5 V˄}Q=WhZPʓ QuO' {>DACoJ{yEy aǨ(Ag%bw=-Xuʑ`w91ȊqHAagFaڑRDZgnwGjyv=_up{,S3Mݚ'*剦X*됗ymRHWnȻw@py:w.yΚ ^9llъzL[%xu>G%bG\9Fga 1[ȴ@AhD GQL[]w9bk>ݟh.ME_@ F/k6t\@ziό1 f 4oG|~)fS=}9WT^&h>bevIⱁm5!KSяn` ǃv-ME=hA0Nc8s3}UiBC, T'L.g 8{I`VCI9+D ˵ƥDVRNOA]M=;h#z a$rݧb1''咄o숂͇i]>p|QHQ-;GF:E )2|Q^!޿&MyG[FNMlh= - InكAGA(ᗹ$x+m@l;<3Pcj}"-ob=֟n6~ƒ3d'NÑdJFWr}U{~GBAe-6G1s݈wB珅b8!Wf(`㳥.w}8|^wHXxɈAs}ǵce$3 tݕ]T 2!fc¨ܿN}5=,5kîƷ~gW ~>&7"řܝr2+ex1w)|jhf a@/c}-&!+LA@{B<˚.E%G[R1!t-*Zefr07mv9Gvw)k*E;iRӎl%|9"kmՅu`Rɯ߫"CdX'^9mj8i힅$+l_&E>*epז3t}͋}EMS9w3g68t :Z^DOUnxywv9 ɢ*کiZd(D)J[#磲MKj-K;ĸp6 ,y8|"վ,_,\+ x?5~f(;aϟWlwGLY> @5X60npx <^ɧjP @;m 0x~<A x*$P jAD`x<e0M` p^x< /}Pp".EApp rPVf `8^`h=~8^') z.0|*0x NS`:9 zvNϴglq3iluc?lwg*<7ɬ<ǰv;o ՘V9TT&M d WBሰ]9ZLT2-h pSK(+Rm؜ VUTN)";iHQٖbrDd;;cJ)G$'J5Rl]d+mS1-KU=YRUV%4YqDlN㦅o7*3-: ^*Kl556T2c(ljKOJ=U'ɺvP_ *)u%zxO +%R#A4fں]vn ݑnt NŊkJݚ/u' ETSVʰ+8hۉ!O$d=AܓB*Ga*fg) Or/n3MG^MɑMHu71lܹ2K04'jۇˏnt&>GA|'ސPm.GJ0=Gb.7ـ}Gʖ@FL5HQee'^19cMgn*,(xbAu + ھjh9!TCA ÏL$g6"l1ޞ+{2{6QnG'ZG-ԡy0qC*!6.g;4X([d"3x U ڛSZ&![zKU&YO𺜰O'x?`5%Bx^kd{EKwX: b# +pjJJbĂ%<{|aϣXQd)j]j<= j`e|Z$[R&@~ 5,/k XRE? GJbqʄSzHdE\35jE$Jj G'0Nmj$M[ZHlԫ]~-\0't_'[@%h/t ]4[ 4a̴quuuYRգHZhOKQYI;xܸ'40I3 oGcw?cxp]VymȏB=&G.B1h4?C74Tb|#Y$HH!4)M%'FL 0ADœ ƆeY X\ +Q+^/"~:YaȗdrB&,tEm8t_0DCD`x>Fr>g9tK$J<+}<~At꒰6wwVoŕTW%D2 ^]/|<':N2j. 6ׁ?0Aލټ)v,Gه?AH4S<{;.\R0nr!ˬNOǽt26i9]_>665er =$6#MqFgS]u=VrMiZDԠTڊ$Y74.+ TDRS[q@Cy.2:c!I8+f<9䐬Yvez#Y3KJJ"$<=Iv0SJZ3V|=r4}#6k%E7m==sR;͘㱭pg40>[TcPP3q08Z mur3ާ׬i7/F?kBD&ӌlfd噜YEqv|=r%W!8yClzr p? n.o/C{/D @@%(@ $p9 fi`2SW$x^)p^ip 'ap<,39{  L' Q8^GC~` NJxm` RٞM?x͝z; lnwz?~ ի%obqiF1ϓbޯe\TC eus>I9͎g,tPu]ۚq(e_Bj!<Y4I>-kqࡽ".Be5AFc13afyzTC ESu5dCb'qzGB| RDUL o>\Ҝ9mu~m5]+6Z8Z"!Lzq*KcuUu*,g24JYF\iu>=ar\1'8᳥.TɳA&NyyQ1a8[ښF:H])֯>&Br_\_U%J[ߜ&ols\GǺ:_#TTWxhSM0[ ޠ J6f$LHurde:[2ƏJK&,XGup}J5A7Rn7}~6E?p}R-p z0@]FJm륯)(spX@c)YAWVڱDp ;bynYZ_ݍo-Mk |ټh$^:H6Gmt *nN(HӉ.tpA4#I _Dt?t\b>8nӈ.J;chVڪ? 6Bbn{aýD5+v@a{ש&8p )%hET @}e(FB$N3+TRI/mqT袵;KA4hq1/ ( ,-gk[}|ᮣ(Դ 4Z;zkC];un,>PVRD5Q7aknϺt0߸6N(7TxG Xhʉ `C6QO>tjwjOS5-͍sUsKkx.Ҳ *w?ޗ+'|wN>&_&_֩4#boy@|Z%8gyyp)+M'"{ԐqZk>j 3%6tu7낍5"\7s&9 >qaWc%A{4a{ #>׾InvM/ڼ=7ycƎ?ab&Oz _8㢋/y.9WνyE,\T\RZrIe˫Yr55uoh76CMm훷lvurα=Qz07Xs7yͷPG"L$GPvȺA72mk{&wiP[s\9rܘst٧u yY7]v|Twv}˟#=<&/GA>~?瓟;̾?/>/?W?|z/W׿qy~?|g?y/_^8oW'߾>AnlmУQ؟@੭#Q\|@0jx~ŝ$eN|ߝAVFM'$ʚ.l='yTq )GQC( z#eGܙk͘_ZZܮR\`L6m/GMCOJ| 1y^Y"h=]aXnVAQQ8= 0Ir1`A0``D(JI#|5f9}vꩪB^3{D`mfLC]%gPΓX8YFM~ < \?qefڤRεo\]L,(S1uefc#mM3strvƼĿjD)#SSGM-qr䵂%4Eҁ7.bhhvToڔQSQF73M89י+բ涡3*u.K{;7eѨ~;̳vwynm!g{~!WVD\JkM g;d3W/R4mʼn'y7IL?Or?HL ;(s9+Bw.68 D#-yN[_'^wPIFܚriobmxqK㎥֑IKV^~Uy6&<[+ᶋɿRjO]{nOH?wDIG;?Xznjhy;l5j;Ǹ691H5Oژ؍ |=?wF89;f .玦i.̝SJMK}FP4:[H[9:72wD F]FuGNEah~]w>@3Wi3#k#[SMHlag͋; A3Z1{x3ʌJ}).N+Ւ7w;Z:YH[@-7|̚rwx>4^}~Ǚ9`81+l#GQaw|QhE4f4'#CFhg0pK 4Gw&ob\6ғY[.ؚ Y[BI0wcWjH#QY*iu>*G]Esu~y2F[,͌lG-la5%^8oq;՟>b:lT:'Ҝѣp⸈>EU*FOsz ].L)UmBN[5 yc %&40f/;6&-` +[$09%C|'w_ؐ fE)̬F&Y0$\?^ٺ&m($:!BjE'/sr HV{A{p@ҎY<9Fo܃ϏYj(ϤL?3 WH^]qؚSYs >}#|V\3{ )ɑI ,~4#}X}I*S,8jDQдVm>M\cWV]Քo=%0x!%avolK/mKus ntPflYvL_zCgpk2݊+OQ҅πȉ6k,spIYKVB`%wgH݇3* LQ)a}XhvXr䕛a+z~^XUs#%b=2ݳ0WPwsM`Zso= *c l _Qba+ mud#uh& >=1k ,1wN^q dv-xqk#4D. fhŻQ~F^G# vkj#Ί4E`EBҙ`TWw+kLԙgd./Ī  0jҝ8n4"Dh3lQ!,ֹ%ufjZ]$ v|$/K[nYpEp?HZ1}s׎%Qӱv7=aBc8>6#̳ kcؿfcXW!]R,|3q|; ;O{?yWu9K ܋=oTW]B.= !MglX8Vٰ2aqE)T_ǟ&d m0]l}%M5PV:8'z \o]NؿN9'*z1JTB~k>xu>gZBOov+]ar̖~^?]h[ŗ BA{B}!)T!1%SLmWt}Gy"`ﲹYcSuwc:{iI]A^1( ˊa[paz1י5 70[ x,:9c^/a L_S2%lN8uvM_e-ߵ/S^u+8B=ƫLdW ݕ`դ{@/֨zB};{53J!gcODR))l<3rQp)3WD&6_Pu)lj{OO\Zu| <}kCgĖ'^lzs-1wZk0j3,ys`чw&0Ye0MvPt9Ec\/ mu:\ZIF{}K ξwHgv͌~7`_7Mf2sIzGפ. ;3_OA&CfqsD օFuAK[_lлVՋ$}~ VZoTϠtJr9x0 /=ToT;`}>[@ .x{G)sLɨs`)a=DL^>'L]*d wMBLlWNf$|ޛU-o#~05vL%t_wwU+;<*٥ujN\u%<lɝov*͟>Ş V[%SXĉ׫`-~aFF߄):k?P~o&[ ?6PEȹTÉmmBPޱ2bUٚih|̬5Ը9ئ5 [Ǖ濷‚!5Yvr50Huꇨ}wN9xf$uf`^R39 |I@٧̀ w]#j'y7{4{uI-tW?ꟺh̏p# ha{?Bx3YK{- AC#L~m2tɪ;)vgcb?fu#y45`'@Vz=[xn\^A>C-j\]OvkV'>w_TM?ix~LʣӎY6ŦÙ Y}[K$EZ 31WT#@/Bp@=?pGe44#%Lqڵs `SY~^dF>58oPX󂏠\#MX 8m%F-F%j֜rG_:ߚڳVljcb2yMt9_5jo9ٹMb wŬfrׂ[&xj ڋ巏TEiMkc΋:P{Wh2Kkx׷Q4۔*/3[7U84L h ./J sV[A6=*:$>"[\>}r^aBڡLGz`>1d|B(\#/зn:4.&:~B9 V,* ?b6=v'|&rrm;E]ʧ b&Er=_oc; >{·̆+qNxh]$n:ΆҚH/ê0ƏY+0z/6<ԜNwgi1&5NO%Q~'ذ9샨}3GX e P2k^6`pJald'Hlz+ c"FYol`|E!Jxxߎ#Y%pb]4Άg,ɡ,W{ elPעU IvCpp[RǍ#r;aᇝcJr'^ph|'(g z> wn -*K'Qe{7;vMv.=;AYiw6lZ5\X [6L|%7DLk;F9SEO`w7EP* |ѝ[:`QYX Z~=zTtSw] :`t;6j~kvH3rH,Ev^T9}UvE:X-Iu2덳h(̜f/:C;hdX" }֒|*CeۊU+WpI hI=Yжfm1[}gdkU˟]k:U4.V1eB;r[_86 ե_Ŕ6X'z#d|ڀ^]mo\(j__-6W=N_@=X</WR ly y$3-73งYk7a'-;TAlԧƕc&<|S;Z/F.| tł@ӡXY)Z §Y/| &z}0a i~F]agx˖wgYtޑuߦ:æ:.S2Z/xx _a{{F{Y$npqG_lM5)XPj<̬u^7S^7(8&z9O dj7wn ag3K؂gw i Tw128()RI g4_fNT F1vcˢ_&N_eʊ7i+3 $lXfuRlL &Fd8>N'ϟjĕb1w$1Е-"mծuY'쬥dD <;ye \A:ZvFl*c7v:i}etjRރ+ ~:m*A*OcI3hJa&l9-QP!ŭ膄*k3NSU#{vĕ47áq?䖽tLIc4ʗ_p?-rܻxQ}?b"w M}2)nԋWcqt CrΚwxv1IkVdzMwsR E7cIE㘐9+^'fCnVؑ Bq$.ՃԜH{j˅|W2w% i.C$evX_24_VG2hxRωBǎةcd^U/K>IJθRir_x_Ey*ð#\{;Oa ErױE8&J^O6r1/oR-kE%~tÄdzo}K` ǓiSDb)_1țSSb" {2)́~\{W(!)1V+U~2Cd6Y= 9ΟN \M!^liS ȥpEDg\BA/Ibp7.ոARFo0-'cƳߓ 4Jr]X&XM~MWbQ7<'d)3g8LZl96{S.Mڵ/I4Wd7-ѳI Z{rrEK:mԋa} *7J}uACQiVF!~̙0fu_ðj20SeFL0TzcO7T=n0|4f=g/ 'E1q'bqC? ʈZs%P:7 ziA+g |+ʁA_.1 Nd.ipl:H̐d(1!%RL+B#{*3ԡcn J'2r4` wFv1vp?>iZÈI 5 bCe(m1 Y,fFy`[llvZnx $1|n7]:_ 0msV: B V&@sKF̓3 #?LV[WeW])PXAmUݒx#cn|OY4'Ġ&0*%;0k0Q 4TP;FU~Oo_(v[.pp_d h ;}A[bB/LT rзrBL_I@az}V_k|`;[>ʏ}*}(]H3}@hFyx'>3(^;}R7*%&e>->-c'Xh&o} r7X^`ox 7CiT vPZAoX 4yAs & yvo/xz zvxu^pj1:s80X ᅞЛ Pw-=!K W{B|O6HzN{8+=;̩v'`wprwton '7hwM7x(k ]ᜇ+(r]@!i|EW.Ek\6gx8oo;A.'+:B.GvqPuE{ؾL*@gفF-xiڂM ,8j!6f 7[G+Hr<>+x"*YB|Z@vP7?`fPr <L!SkL`A173FcEpi/Y~x * s>և=x~7;qiӥB=Ԁ8ӟlf辦c&n ` &lJ {U(©5'2`3r8g%q^d@bh\%`ř(ѳ |:STb$c tN^]KHm'>4֐sߒ³I9}rSRyhUr|UfCJ)bd4VLZPǙj!QTVOtT:^9}?E/G_}?E/G_}?E/Q_Gu;'uzһfe3 ,z9_x ]n'3̘cɥ̬ £>dzEfl(OŗG}4(8[K'Ԅ^'2+>&+$Jb A2O!z ]Г2 }uwi2ׇGzqLcatDŽ}^ļTF;܉i?}Jy(t _485J|^zk|2kk(89r"鱴VA}YtG}6&OZ˝%"(frfRYɣ>MpQNO$St-?a|T\%J_}W⥣>wQznta!+)5JSQց4F}*q?}$h-'ACF}ZW"dO>gwZiCF}zSR2., G}N'ܧWuB1tK=yP^_>tP}G!/}Rq ctzvz馟pɐs;C>0ڠva?JS7ߟ.|Q nHmSY'/>o£>vq?}> 3JG}~ᚣ>RH8|^Q F}ڵbC`hTa>Ɩ K>.ۧxȮz {sY8?`$ۅ򣳌%5> n?dhA_G0?z>loal!x]j(}z@?^NF3mW1Qy!'q2j{|oU0sY@] L7oA Ihz:O[q)Z̮{mɳf̈_&TV?T', q r;:*cY^z!I=l֜9fhZ^~Yr^n m3|ۺo&yއZ"^Vp.<(H|q6DdK9-N^ eYciNLrOa) ރyX&* B>/+5-8$w0XZJ)Jlq`C8de-RȦ@><|qs+hyk^ͬ|Ӂ qv&~^ϋ#&Ԝg 1LJTw0fE!U#2n}ZJGy}O\vmD;# )wVB""~\sޒ)0~ireNў;vSčV\Sx /T|lv1iȣ9۞҉Fᬍ뀕6Ɩŷ>f>muYma 0[,DZW7†>E3)Skt۶Ҭbp9@7@mMJfꭆf?K4%Dm=&o>u&SK8H}+lť6`H^rܙ!sBg%c`^EGX? 2(,]8yd}j%bw㷰+~m, }뮕îF`uϸѡGAdmwm`u95=`JF U* ce1 R XYr*!BpB*h-;&7ϗ}YO0&k3>MRRﰮ4`M03=M̝VlmdfU+v0/=àS0N *7gM. Ͻ"S6CNTƬpsaaFkNՀݳm"DKI-EGgOwYmľ[ZH+1%S>\p!6 }ޅUePvֱ W5/)QbaJgMDq,ί('zr9E8%.V0_`Z3:uHN"ݼ*>UUk~^ +g-"zΧc .&^љrzni;uzGYˍo!>X`zt$lSK>84`m!}qYNS>샻 RLoV@ YTȮD~j8TtrP ;A7Qѝh ~VLLHsXovw[~bXIcwu'o.[zߖ7|*׀`2֏;t'tӕতpKĽ*wu6+DB>Iݎ"܎v=m>-cw` լyj 'ĺ^EY3lbW]`U5Hm7Gf%喜gKkWOc^?n=vF/.wƒ24L ՞ބ }K &E/p@$Z@2= qsszTk<~aGn`.rOj>K90у|FQُv9OxgLeHqQW8T[Ytdg8T^_$k"Us8hsYqڐl( ?d|n+ʺ́KE^i}kD0ߏ˞%sS'MJ^t 6a<:I^ ޴L:=nݔv~OĊ^y:Iݙ-b3'Ł`AՀV%p{<OD:Fy ބAO 0[s{vw}vv41Öl5׾ DG+$\;m5Zwa؍u +%A%B/á-%l8) !moIyс{ }w#Okjt_*|rfc"=ifjF lt0{"m|!mR+mXPq.y_.H`T=ʼn'f5~o~ ;l~-5ƈ\ix{X yH!hć01ڏ/Dt9| BI6!Qyl@![؜ #Eֆ^?AV}O/$\=w㚧?K"&OKk]S17 ʄ ҼEEr!cxN}H1J/ ! eOfަS!mc 0s'/829}Ddb j{$ M*BE”PgNegfJ]neV'n K::DMz`%^ϋrM\F.Qj⸀@uŃ&DWmzw|*򗇉T-aG ,ynWV?*H'm,?] :zMt-*4%!68|_@jM+fE_W*k=?JF>7/ :?(}$r%]&(cV)M|g"n}w'nY O`>kߑ= 9Ptă/ź66dun)u}lʒpJ&]1g|gߚ&!]sy&(jy%h!WhM3,|㗠VLȩ=Z@D+V [~cp&pPP;9~h+1NT]ݻ[a߉_w?w₯~އ_WF-tqmbuK.Zþ =gjav!~NS I_Sػ"Ko*$;v@Jmī/, lXIݰČw]pfqb4;=DM/uS>?c2MhEz6#zbm`"uŷt 9 [Fܧn?qPue1>}6SBR1VmR%׈lw|YwLu[_"D3lu᫒ƤO$_k: TzbzXv=O^W?3Dv#bv81腼FwByzq9#+Fِ^ni!\}3IF/)& B_lr$"_<R1]'n4_{jhҼs JyK`jֻu 3AfVӗĉ/PmǓjon['l[g%AɆVMu'+er»=첐NhjF)JC˾Z<:[Qr?,\Rsy?pDzZ1doGWôࠣ_䕾 CD:ω}3 +btv0vW.w=(m{fzA|ў`8X?M8Yk&6kH5.!BA(jy;!J瞾~wnЅAY>dί_ig6v/`hQǂKnUpOfγ̘Zn/o1WiY@D>h1.o}w| hv6*HLvܤMB㬹ㄉF+RN\дIax<²W :n8=YiW.a1gpJ ?WD_ˁ)B a>cFglbMɳ8Mw"Jrs`ޖ/='/,]9p"M꺢Nz~!!yjI~}-u}IT,eLLT|N4R̡Cê3vBITo8cLYP":9yq\5%<~o?'*w\F͟C__Jk?>OOB~# GS0 S|0S/R|/R.R|.R-R|-R,R|,R+ŻR|+ųR*ūR|*ţR)śR|)œR(ŋR|(ŃR'{R|'sR&kR|&cR%[R|%SR$KR|$CR#;R|#3R"+R|"#R!R|!R R| RQ|QQ|QQ|QQ|QŻQ|ųQūQ|ţQśQ|œQŋQ|ŃQ{Q|sQkQ|cQ[Q|SQKQ|CQ;Q|3Q+Q|#QQ|Q Q|QP|PP|oC6_C4?C2C0B.B,B*B(B&_"!?9f-JۺX?Ep )DϸBي"$?+2Ɂ2۝q9GMY|e{J.@A!50Z!^qZ6/QOVU%T/Jwn~2pUo.-)ke2V,%YmoV>po **MB(E& Se,S·"4B"Q ȔYB<)'<{}as: v6t߯Ip NZM%oʽWo=nΛHkJey4Ο_ ÿ#?KK?Kn?Cѿ?//"/??}ǞO-@^*՟Noڟ֓W/쿿zdz.?G)=NN?X'8h{r _JO_?WA{G0K`O_p?_#? _)8~߿O-8~~FR/Eq_zdnJPx9M-1?dpEX/py~5C ~?7>ßQ?y='?oO.?~r/'z_!GH }$?((??h$~H}@#A#~z~zכ?/?'?oO.?~r/'z_yX@ >@@ > ~ о ?  п߳99n2t'w#}[j(-Cae9s[N߹x&R/;/}KٌC2>.4֏ZpF1XG̯ NY N-#mVv902"/{\,D<_˨q#Rx6J$1Z!+c_3RX]eAOĴ㳃gd|?$UtpZA 3"Fؿ\'*$c{_ ы7,z˘5i!c|m+E7?d`1R !xr3!$tFdE3͸qi! %^.gՍӍEsEg]b|~>{?Zښ!ik>=%fqwLo4 eXMz4&QbT0/_.nn ͑ʃ)^8Cs:_SǑhgxI,z]NӪC/f&F܋wnhFobD@ꢸ#T)FWNmbD?a L'Fd((]f| |82YFEeC8ίc#'9fԿˉ71 87_fZ%'m o%dk#;%9`]}uK2cF6<ǟsE2ʺxD)pcxT("LEr-FUn&^NГXh"Ce!٣gO8ȸ DDjƲ z$.R$,j%Ʊ3bKuŤ1{'obW9VRlap})╥\el8ft_(<=A!F'5YKZK$.jsQr֓;҄ v9m?'J?O`.<F΅aDČ] -~aYoyC6 o04Zah|A=`hиh!o_;`ȟ;$ h?Su=m$e?Kw4jfH#Ʉkϓf {I6*x(ܹI2|?7Znzߕ2͡s#򰼇~K0E9yK>w4^cR5S mzf3<>{;)KRA^UL5/^\G πᙠLǯdžk8xp `t J~Ϋ&@+6g8^yM(E`jp R#a8 pUX?"*n8 W) HR f8+@_nl {p~= QT !~ DvAx(FxFAiDrMr>K()3cyjfS6E_ 0->筸a{=u"Hϙž̏)sIE]?ˌEU+­fDr'K`nSJb<vU[JL|'d}^- 3Uvu E$M+V<#Z~A!>S3g%ild($-Oy9;{vX~h:P9'Heg+C<7KS?*V &v蚊ō A G™lVؗ.RsAң7?(̟H_]W٣hI6:I]:|~;&?X JɤB! 0/cC$r>H52|j ʻ+dυ5?Rg.y~Y U~h,JK'AʫDؤTD9_T+wv%߭q MnJG5ewӫ-nN%߁ϾWDR;f;9_!ξ?}鐳Nʃх3Fd@Ʃ|^R:;so&֑.=ԩ_bLPl'mDS8΁io[%XϥǫtE{L$ۋT72͢o_э 腴"O6F=PaLg2rR1|'V)~Kz{nh>©>!H}Ha[\ B}\*XṰ;ArȓXiTxOYəl>U{^c'78Oګ> S.m!>;gʙ$ߟI-dyBLu'm%6Ny>t4%ܱ|г%߇V GTcd9:I=xr.uZGM"!{)p|KÛg`XbBEr<_4miYUhN0 p/u9ut U3a1-L4n}elSkv}tT1=UnF]ޠ/[ 'PFqS_FƾDsu AW":,yɌ{g8vf*G]) ZzCCT {x%>WpɋA a'"UqzexJ7gÔ 0O.- O"pa>HdlHQʇܷҿ{NFF=f *t>z?ۢl @cɯ $kfDF6U 0x Uѡ9؏ڃ#sbNً}p`toli{(rG=a -[:w$@fP׉R.uu j^Yx 9b\AfЙ鐱ՙ`Z^u>:[ Cj΃>zGs,jMRm3[~&:(m+.S!r-J/mc#T3J@=MgRȌ[]+qCCbopb}ύG+V8YOSEo|D 7l5A٥LOe񕁌>ʍ3QM)x{=♯ufwH%[i1eY*H嗂dJ*\hFdvdٌov[Fi_G/ID*ȃhY'۳9З(Zn]wcIә-(M)Kl׫ ceXCԻ}Ժ}~/E8=4T'T>Y~3˾2}jwKJQ&6'Sd~~;俯Pn$s@uKIU ~>9y HO3k"Ǖ&X4nˡM3zsP V#ۧvLma#m|Dḁ3~w 0JSP'pLz+wٽ׳gց¼y $M}M} cz:@2ӗi7;k!zϊZfY }^YK7Z-[F@1mcj@nxvWT$B7:w|yث\3!Ff({/c)>ɿ.XX 2"ja7C]> ;\`\1=tüaaυG0-0%B;Cds!g|h\e|.z1c*x\:DV8>rsIXtBG\넹k aڡV7} ib(4tbV %p|.=僘[1`xjX6uCd"7ˡXys[x"bL \y 1bs! > ?)>]#fi01 *|M(]T RVT95ڼ8(|g))CęWƭ$w 6%Kuڽ w|(Fho|fTY~Vw{wSItnT:[G&nOwݹL>Q=]X?케 GmP{2R.)YtonxNO70#Y@ڇ-`]S{-&EzWNNtG|hHZ8F$پ# 2W/;4GzK6TyjFq B92_lx#`߲̈7l&9T{n"o.Y_:T 5쾄)Ns'O $SErBZsY|2R8Ph$p29(_*qv2r0xE, 2bˡQNSr}'(9;ݖs39/6Y u~^EMϘdE{Vr?G{:2~j۪mFL]{*F}V̶j³Zlܺfs,Q 7Č~@ 8Œ/݌Z;!I2h ZTkr>o\]CdQ2_xOdBn`r1`FN&ԫ6̙Ta"wxsR/8~+;^Lpm2&ju@+ca2-:U)Zh }V2orϢIEOa&MY%H)8"[ܱŸ- &夐•kLQs1rkJ2I&-QEheLIQkv׫S*Li;?F!Z/2_ћLyJudwA\93'vل_%hU?:}>fy>2Ƙ."p֞|t{Id0W/ݝ/AM'lnVy>o,30p=>~~x~f'>"n`۽f:.I㿃Le|0ɸX!LToRܟL[Jk__VzG%|R27;\!ZPx&Y]lh+G3к;,'7zq:2+`/Y;H:Y~?k.?#6"΃CiCLW1{[")iS^OCQo|%Y=HcUiyɄ!6Td:J,߼RPK^%n5qQ6]_G_Ghݗ~d~̟C9(:>mC]I9%27fgo$@,y},y5Z\W_Vťh݃.vϕ/sUkͲo#Vtgh|%07溳H*cƣkL2 u܁G cR?fW.e?}SeA@UZ󪴤 -5J*CAĕT51H}40|0|ZvTyW3i?;=@_nVE<61E(z֔K Ӳ0icf9VxvXbI?ucPpB *T6h+r2T_eT9!NR I Ob63ĨOQrx& t.bPVr#zC0!GrON|CmAT}M5 򁔝ndfX&z˫36t/\7QxKWHۤ[1MJ}GH"SRW5Wwkse~ڻh㺋NpWdM}?(TB֘P]4$$uFk1I{*w,k< o[Pae~h?4lFB jú% ><:|)&~"/ε9vjH7tEH9ة(yJ=> B)\D^va!F$u*]CHnWeHδCA 1j}O+# q<ЏZ]ݕz9J799L^=fv;ړnj|-nqyPaU̖: θV:Eﺢɳg{.qE4~n DbŶRWֆ ,;Z?Vk,V >̝iDɼ [|%pSi)HDւT'<θOu%mxf?ѧj>2[ ޳r]bɦ/?;2^M=cEt(y]t4Tf,fm:_繈Z9)v#gF[!ӔyN踉ߧߧP;h=1 П/Pj#wWI+ n܁7}WP}b)=UMavE˰ǒQ-(ꈪBڎ+햗˷+E1Z:9ߕ]>GKet)E[Qq{bX*K6 ٷ0ڹ &ٜ$5!-qJ]|6٢ʌO.'&;6 oͤS{sH\oJ/bZuw<ѷjԤ赿y.՞\6rvP`r:i2Mh\Fkmrt3҄hi%xf/Fs;Fmw3Xf?;3˧@_[ΙLYi uyljuwN {/*̩I՟B&n),vQ!2 ?DX6̰<,Ų)gfcFX6vN˺X`iXƲ4< XVv*NJXvXV'G2|\x%m5-9.<@ŵ>{p0w U_.[vRn󢁚9˨S"[5d|u˰S^SWrn3E]rKO\;h>ܯ} <6M7H2G@z~Z҄} {>v WX; K.5 ~_@2E.!$_-5I;E@fz9ھhHTo=N] fR1%!sŠ}1U%:EȔ󓮫fB>ߨMBG; .*-^ -Rw gE6Y]s#7fjX`x>JbuESzG.33#(Wz6FdƁfc/!Gɜ.iM^+qg[K ǃ/9ݴ1k<8Z_nVx~3ߘ2aNM|O)='z;5ݟ&-%gfX+Ao']cQ7rnDRD?fdR1]FKD1C_.PWf9< {R[>je(3L}XV -, Qr6׈<?weD%WY¨ZL6Ѫpcz:PG\}b,SIL_,*>0,2вB'ܒh=ۭY=-yU>媇Ԭq O+0C5P}դ~ƒ4mrMf&v~)kI/PCYRI0=Uz|OIG54gݚc:<&P}¹'|O7Cy^dpѠR͸0qj>`-0MlԾٴk@+5t~L+ 06RGRh+눜0?VqLܨ2֖e"̌n M7,"}M023鷑4aW[3hB+ۨ R&4=:˚5a?2[1~ ݵ>z?"WZM7>:~G9jZ'X6S`ɷΠһ:o ؿ[&E :}-MrTmmu3Kڞ&*1^{NrTMZ uVe ƌC?NA3*EdRxrkjXQ+t#Fڟ#$+w#I;"{EĖ0TB*b%{>o)5AtD|- U\?]7ҨuR}j)g,zM|9eP9ErMAiri즻t~Z~)s>M2}`0hOG[wE,JViξy<ڮ1rjt <+ۚJG*_rQO~<CE7ʨB[%bʜnUHtݐREd/9u /{JAr蛊1Z626؎.OeR3u>L)j;IWV7Fz4~>U(LjG4@ϥoYdNfk!:?UHV4V=6ɢ4Ώ=ٴ= Zu?06r3!Jyri:by.=9rJLo =m/G >N{ʗyn߸NNءѥk Y@φrh`;X@O)꼅`tA;gq dUwyq=RBˬnʑ<=96btlt&֑Eƽ:{Gx7s>w姝>8Qo7XytrNz<rLxz0_Fw q F*N]3*}7X]h5nr{,1Jf pϩͥ70X͜t?yqQCg07uyXy]{fꃃ){r˸Nt/9󤦈a p$ϭY_=9yvK;j=1Hgs$fg|4?_eΫkӶE0mߴ/ĕͿ@ ʷCv&ߩ=x4gh1j.em #y0kx =tU.ջwѵmlUT P`w2 <ǃc+pfﯛ`̻0v>noHsv5}OoymsnՍA{ w-B|_1W5d. [h ֩7+`M8_c-HJyd!1UC\BR'2칉8΀{OW}i;u)V(oa%2|wTq)?:GO!_m%ӒQ{2[Ȣbb|o?c>2m`-w^"5]h]/1~G] .Z 6>02ۀ-8{9E߳Jf׻bt mH67~xFX16*?,B{:ہvAƗRvXos g- O+c/UF }g;m+xʶܕ{bl}q'=wl:ZÏ_weI/&1~Hl4\Tsٗ'1n"a2;V+<=>jK gZwK#xsEnxPb؅=ti7RwK>$ћz@cђl}oNЬg-QR,G\nv}P?Mu6>ιqkOug/yS?nM[+]g#u=|R[3|dle}f3o!F6Ya'ǗϛF? 6q KZi[֍k敝e{uϣM٭gO#'o:dS_UL:4WJQu Ch\g8$hT8y>]dݴlnv(,a >/yD1x&_d)48Ntr@#ĶKCwo0$9L2I7/1,ӭ) g(p?86Fɽ'vT/m[cuAh:37=E1򉯓)ɻlhRq882韇O;d~Ht"/K} Cs2\rK1NYyDp2n N38r;DTK{ӇtS!\wK'ۚFsBiΏ$>7L%iûDDX $jj3CƐ|W&Y 5ֱ! #+lI&!۲.' "^w@ÀX4[xt8 aM$RDIcߧDiz5Is25^'Q"CDy~:)*/RJeR9kj{7~ϲ>־Ͻu}sz {iSMVnCtxQ/D,Ҟ max(6ٻ6CmtZ4w3XKeo;~kp-஡K:Mxr o`ztP#ksn]o:67[ODbpk.0Cz(U7Vz2;u`wZXRw6o6qj QvO7YDt[nXL6k@E+eɕ(^g?#ۂjvтjȫzf{v_51_ zŚ UO @SRBmGf[iPr<$U^S$>]_9S$}#ZOxCyjX$$zyb-D<1b M 6BhW STvj\%Veyj!5xm)*[CV%`5Fr~hБ~ SV~ryê`MIfJJ f/bwsl2k+{2zݽ^*ӏHiO??@`a< ~ ?#̇ _I}ay%h앨̑m*aWϛj+}9❊Mc6jIGgaK6֡)пXҺ5 `Z5@q_@+6vB;t72yئy5ֺg3sB*u *otZ}B}_ aKCWB~W7[[MձIpt38. Y# Po 6[yn`1rۖjYM/dGg{?sלWgٻvĨqd!6<ןm xo beёO0Uh9Hsħv(v?}(}{iˁv[J[2⿝W2ne3ǃ)IǩK: ^R|3rMʨ*UR`RTc2V>(5l֜'_uffs6sJ֏/_C*3"eY+.v8_b톷-?u?jˀT{r|nKz?T\6MR]3|gzYNW!swmo<8[%JY?#o^7{,ܟ"8ex9BeY1@}f )8{mNcHjzj -^х#{Mk$gJ )!Y;ď-tÌe+^n/:BW їA.Xx3 _('3HtͪGȲFu,.\KVvYδ}JjE62f.G}u\m;Z/JQfehM#`\8<ݻR_ $H[O3}.qOǍ1J|3ld?s&c}FG\8h1SWDPVフZ{J y}*ta~}(P5$&D|qBWzyD'f'Ϙ ZLuΓw\-jh[j|JwSo=h9{UfX̰ @g>qC0^#D+/϶@N\Gޥ? rNqӬJu'is| b-AinWTG$崕Ɇ[/j{!fC|0hR&Eggh 4hqߝ'o+p.ܲg+/}0nI_w/ci_/ ÿq2#~i4__/p/e+~1>0r8@c||\񗱏i4__/p/`e_8p77o4o7^c?И?1?0r~3g4ghπg >p3ur_!B+Wkcn1G}!/@BCߘGp877p}Q#\#qLj>F>fM8ƿ\w{9ġ }a}q;;1rggC?CX?CrgXd''>>8IIġO"O2ryԯϥ1:p[j^& 5( eYxX6$ǽ;Eutv͏_>)3[ofDT_1ч߉LL_W2S5?98mIeI%|+#V~GNxo @,|b=9yot/Z5h7/?=yq#>!|)GGt+{cyc݊za^Y~~o_gξ_|dMs=?99;nCf]pg9{̜vZٳf/5^}o,GM藩 >똝ͣV%.jaQϣƎ\ GKHV[~Σ޼lH'LJxIz|ɱ<5>gmL-mG%]Z:eVQ;- +$aҢ/y_ݤa֎Dכjc]2|ueUȿC"8Ì Ѯ!>z\iQ9+n Hak Ky, )]ȟGVo$%/Q*l,U£nRu&BA*̾-JVW6U^Ooʯ-f! )KĤP3XYW~Lg|u%-V:,ض候 B٧Clx |}F^Q'Ԧ˗ddeqU =wȀm+eO_rf<6_ Y((A -"$0+IQIDAA *ɒsN%#{g]k̗Uu>:ۮG} tM͆xf1gRHCO%,۽YBy-GBp s /ҽT8eJcEJV |%KgI'`JvIwȏ,1>ϡyEOp`xH!=&ڿ6 pL#Ҡr8R?i*\X|xc!-ƫ-e]r:؇{]N&jew ƥ }S>1N-A7讂7}fi Ʊum2`M{TrEd6~`c;S2홒86l( w$m3τ|[ȿz\L"3mPfL $3c^@*|㲯Ǿѳ}VY`*j(`^΂7"}XruhL$Z]cng^ />xբ i 8Î+6mbbQv)x Z8QۨoߑjckvC%qXBt2O8>cLM@=e 4(6?}1q^].hA>9mOiL;b~B͎?QكzVKKߴ[Bg%e!KSrJQ/>oW6!ۮBX΢æ_8G1]^!|"o gюB0|jsWt\8gÛQ<5NoEnhUF- t<=+dҷ/oǯãŐ-:?)b9k/ykoB'Ű6ko}Kꋡ@"ϏW%{`Tx&_6-K N#4vt+7[J`o!۔W^}Ҏ?ZI)GQR?qס0(V9ݟ9Ou&wݖGI OL_\r v剭EŲmH45[{x ]:w$rf)t>WVVo$??Eԇry>m7{#[9X*~h^Tf>)\\&rH=[|`y.3 3+|P#tB3l$LۤvtsӿGwE/'ֻr0] *aS&&_%(~ܢc3,T &%u_B/q/lXHtMc*S\zo}tB౪ :KSE$tWf4_6#AE$,.7l+۾V_$l]0~_|?D p@ &tf: RF67u [)4&Gz87}EdQyf0~J ~\{E&weRat_*Τtfmu>Xh RimTQ8 o#Lq}I]3hoHB5qţ΢L̽ 4-vn5B!MmTV*^HC(ZITjQd>vHJ=Q ypn ,? {k>b#}=b%RxHbɷ6)6j_L┺;',J%Js]tg9fN0 %Fڏnp(u˖ndQOnv{hEǕwm3˺EH"BnXm|3čnpʯ^\w٥=p.!NֹCӚO=pRC%q P^0,9a붕|NhA֛^`$эFKfLy:?,t}pb=Vz_CKCFeY}+^-Iqr`~]s~Hw=,؂#4_SԎ 9WTe-LEwub$!Zp V}(Yqg fBi㷮!.3 =o+w,oD]?a.DۅS/ \$EoC%?v<ţ+UٗjW _&_2kly-?+uG c:⯋$h[o>˒=Lh~µ#;γ6!6.H!bz\#HU!z8!{ڮ8H@PD^ '`JaDC :M_^p|ocd]aO"7x.:؁IbC> EFbI 0y2,PGL()0.6qpn%(һWQ!B-D.L$FK(gS@|$ dZ<kQ{3 lx!‡AұIn1{+0>BΘ Hig@v?a6;\V; A=NE 9z*0k~1 阐a(;8d8_=6 %oˬ03_U1`X a<h}f0 3 ҫ/ úaT![p/}< :Vٲ!0M37"apz^T5}kC}tA׭O2r kU7T1,Cez_ kOUAtij/dGlájl} u^8%-kvI/k£zVIo =P#͸ljN91tEޥ`k#p_7\誐fzoRS5.~^.=dwI|f'<]!L{;`yly}ԋW8tuHpwVñvxuTd<.EvlYiҼLW,Z!&IߵC=S IOS6[k`͊^ۍ0Ǖe?%s KR V}?-5̭o& /?*K׊g՚B3[Q 'bݾ~U<Ïs}|>1|>yb4rӕgxQ %7Kwy2|b>ƒ;ƅau֙r̝GΑ ߤW:e#wy |Y_s[ |Ba!߰oX7, |Ba!߰oX7, |Ba!߰o+0|Y <|>n࿝7999[Mo),۞te it[8bjzE;O mav*juD"ӄN ~> *;|]2۔Oct ,\뿡eg|gީNAzp;?<۵t$TY?yjK^{Bw./ƒm繖]O]:͑y%̿ynY*:H'ͬ_a>a&=&&Y_ 3s}44wN*Hijs:hezBB'מ6}ak}{/v\qP>EydjltguvG᝿=c_{Vpѥ־c3opgXvKN:XWޞevMV.&{}Y}xь^v=k٪0||F3#3>#ƀ1y?[2b|Y'bË9ZF,:>.-#&'v0:Cˈ͌f{ߚ#c<~/cRguf06}^sLj=2aGq2fsǥftb9z%3sQ撴50ˌ㴔gX0&x+ $wAcվk<5ʂUN GcZ C3:XHbǜ.++q|>ʊ2́!x׌Dec|*&]pI!P)~rFGsK &?2Ǝp><ʎ*{guر'ͧ^S3:H1 /pEXuӗYEX,GR5>ƁEU>hs1 zpfNo]f;rba,;!Y7N,;!O|V%bуĪn6fraؼ- 3:q\ۭٞ(FSI9PnYý[lYPY緿I /:y{)e '۩YAp: ӐCm,a( ihJCuJnxLxXFta KA]toY SJD1|Dy385Y%|- 8T`{,ThT3H1}]"4+l>(R3 Q9{N69+iLm[Hc͵uХe"GsTgAkhy{z۫q{5. ZktQ:K٢!є18@T>#$mNfL<P2g |9JFrD+>C$lrԲ; 8zoR cmƖuqUIBBUrsb,Ǒ}dw5ƅѩgӿI68 M~7o>^vF4*x,~^::O],WSj!u:>ۋk0lZƻm-4"SgkZuR>ޞǢ̸yZ~l35`éWkmpoٚ$|RA4^U{u۠ڵ: ۣ|zɞi#a5!::\˫EWw ϬF<oo'J6"aiJpz/t=^(ܦ잪Z+"8MjqcANP15{;O*|Ȣ$ ٻb |=.wX|4~˷r.pJdۿ'}_>d)Ӿ.8(]R~f5G\$kORX$a g<Y]j ]2Qz7 ([^qGW BȪIѿ>:qŋ +J=~npYy|II>?/7XZcMPϋ^̫uJQëJ+8*w۬4E2&PQ{ Wv_F(Ӧj4TB+!Tϵ ~ṱjjZ| B(~o˸WvGۄ㢫 ;e`3򇔰XjXW/_fC͑F*9R3Ci TtS'2IfEW Cy5'P{5ݦ>j PӪmY;Q3Q#TMM'4]7c9ǚk{So~&Mg`֋L7܋, =Ώ\E68o2ΛHƀ}My`̋&xPn΃"W7Ջ-ț?8o¼M~m1%1?X<8/6K`/9ߋ\Q$<9}R/2|V{ayV/ țt;8oz+8X?&M5M0&8,oF3l塸6רwz{iXc Fr8zXy~8o܀MX. ,c(o"+ʗ_oVB®(^&|>]}d\rJ-7ճwSCgPҧwMɦiG̥Ϭ䝹6K~yn%89G݂DyQM&;঻ʺ&>6$KMQse0h@܃ztުp 8N3/mph9~NCQ{%s%WAg5|@c4KҙT,Jz?]_ǩR _=U)Ssj~uQasQpHɥ/`3GJP۰_BqfQ"./)hsWu^ROmWipbPك|䜙o`YIcCaJza7(uP4[p1|h~LeOA%x5yP <_ruo7'_ɷdWa!mK)9.Օ2/:< \A&就ib5_qTFjjY~w!9[WW 9G<ߘVB07zx=Iȩu&-b譲^Gf_O6\f{;x%ZHwdo{WͣqJrmԍZ&q{a P{8`]eϛ|[ݟT#L6SQ:Уf[A#bGP GTÑձF~o3P?}_N-ʪwC^OON+gș/mzJՂۛONP0sf->)5fŒ-5G,3Id-,X'\C-S b;?XS%߲O FZN)^5zى:ɪGV %._TIK%s? =VDY]BzԱd$ɠg=4w h\N}l{H_Fښkk|6?5;֪üK0~xVJ.0.Q<2;xD,NV%N.6f}K|mU3z1A iwM@U~XdC5l)KogF1H1*^0S9?zA1iO.P.KTtje]hvxC;pT>U>y59{QfGyœW&O1{2jIu93'PW&%ǫ"r$lPCDD,u `MFCR`&J /x? i 6CŇ0*g}hB]*>xx\Jƚ͚BRk}aWBjsa1+ ףo(ͧ%t?Z)ҡ$2f4I{"|O]&St&|M|=LW"ge\ԣ/+kFjMr6vϹTԳsoOc9kB鿾09oߨxN`}Y7a4u>%WȃGnaUNL[xq oIH.$8ϒe|UU~Rs_iy| Κ7֔jOT?'>Bi3NmY,+;y9"7|f@GܗDj7pM:zL 70vb؎ #c+001~c#_0~ G0bX0Xc92b,X܋7_c, K/0> SO0>C0xc!Ƃ^$d{?w8^'bdb$0re##+ aX !XȕzLF$cdb$0rc=F&F#wcdb$0r%##+ ܁X \1H`?YGw V Ś f:&Eʊ^O^'sl! Bg&6g#eOҝA_-tZg@8YWHz+|W\yp7Ao[C|IV[`SuBdkgs/j^JyBە͠ow^*5%j:yG{ѿudO7tW &qM/)iIWQQrA$$K7+Zq-İNļUz}T=0hl8~\0֫6RҶRHWəC!m4 z} @~D_T+ CQUt> O^Mb G Xo^H>;u#[ph,hbgث|> dG*G l߷ (YҢ|31n {J8nR//ij+EןVM8/t|wDپ\͐5jѱ;Ϻd;Dh޿BǓQ߶햵}LWF//yDq|fOߡP|OP:>d&]y͂M\ufL$>ay!ݿZv@_G|i1a8JlZFAN䖱Q/yZ2(в)%MNjJG4D, kdG_m?7byD[w ޅĀb0L^?m/}ǿJ^`N_`~=-K8%w̧UkwjAtX} z&ZkEbyEUs<配͇Yǩ9Frvº|& ?,|do=%'dNP"uUɹ Q96\,5Vw3wȒlz1><{xatbg|(ݷ x鷽q\n~Kix`ĉ0q. !65='m1rb!LJʆPrn|N09ة9>[_KDmgS(YEDzXe39'l/ z>;rDU[NΝAWهl\NC{ȇTw-We }*¼ɌmxhJw0Zb%YcҲMݖtZA2K24/92t7=3eZ i"rmkށt,]JM8<]*2kzКN*JJT9'-v#~tLUIէz'9<琱/yjCφde$_#eu!!bV!LJ^qy/[u+w"$[7KFM@i$t5TaY.7zXvڸbYcp95\N*XFrXVIJp9Y\n8< 夰<˃.7L`Y[XrG/`l׌ ?5b˟'\uXvո,Wa+pr\o\r /s\`1rpX.r#q9XnrXXrW| \b9۝r|X>erG|ˇ!\.ۥrXNr2O8quY8.12aQ??yC\֧c=FA?yOR#s֧`}2cd|12`}"'`=Ff<10,X ܊mX~#10adnz ̯ý0Ӥ;DaTuc϶X~X.z eGT qYx8b]zCd2>?欇ǿBr c`UzJ&2?x^R;a>Ő>~k>'1c?9^ rsRVFCϺ5gLnNSׯDP-ӘҬvFIOԗx.Ct2s!},Q&S7(fԈ^D)23$֥T c~5`epEh0jו!EQ.pp^mu9j?]5fm,38~S/]bB8=iƼc"-(fIY'I| ]W&Gh }t2q`$w0DžZq*,Ij" p4S ͼ99!jx߰$չC`ʬ/!9[k(v<6!]Ma&W-;i OetZȾD)9d<1yO&p/߬7:.e,/:p+&G$/GWB0' #*U`+2 5i|e9Ȳx҉Wʃ7q(\K~yJ,zW<N\_;5grcY X_k GM"8֐H2V%Zti'+[?o.Ox =䯵S vL;:a.㲔em&W'!!J~9K6.2bmU_׆F=Hvh^YE2gb?#~ߘ7)~j\6x$(`7c)3ڣ}0f$쐹t5gQQLsF}g4(g7~z*|kk.kƥ ;r?WgƾE|掆>6p* tcպg ξF9=z a $7:&AёKw5|O CU9y ӻD 5N6a^D:_E^ਡPw `9p:X d5qA.ZhIS4}31]*_idH֢xj0~篜Ƅɚo* $ڜMf<Z<奾e45%},)ca]֘bǂSc)Ԅmc7kiSן=BZË6_}irBn<=Y>ϧP+&L/ڴX6 & x5ZЪy6ȴ3SRY/gkAmItgjxN{/c-)FV%aЅ#OCXt|v`je3z ?"A^~P?r6WvZmo(~1B]~a.,n8tn2n8J.{. {'OG(x4肴Mro 4Stw^bDdpYrv Ӑ}l 9|*#C4%z'>8Ip@\*!2Q%!OiM,r[{Q~3ZadW&knlC1@&`c:-TU#c .DwU0XZ#{1$7WF;|and_JxS.ϫJ*(?jCgjկ%wb/5L磉AuCHɅO["QS'"S])FF1)~ӳ!^AT6\}>n%{_)I|4;`8:?guI*UQ}_h Q? I#6.lPLfvo$b׬q7"Cz"x& L֕Yo$կQ}A2bO?ɪc%|6G8)z';nݿ_ɬfm Q9thd2=\5OuUx~86t7M+E_Qw 966ZBq3I=a{(zeu 9e^9V2y%<뒳[X VLa)'ԏ'(y=b;S "C>[=3xXsng sJ~V9S(/֑@Ӝa2I \G?VDއ}᝗?:9xjFd?/luJ7_ Iz,Nf lPɃ'FHfÃUya'6 x?~mt-U${0#'vetç;luH,Y*Ss  &.`>DQKnMMpsv<͐3{Gأ=87Ïg>="p꼗.p   ӻQ;1ͰM9b /\jL,o*UCf{il:qfzk;i0>FYkiR4"G&}Ɍ@zMy>pmqvs\~jiؾ[-agQv+J| cUgx_f.<'IU[΋v@KkVA>7nY>\7i͞`# Xķ\L6vL oe,?O+ugxV~m{2Vi閧ѾCPԵ4Je>Lzƽ([^Qla[ }+w_"J&fp'ò=|vȣvާϲpt{>dǮ1[ćeY[JUmU+!Y\[ץw}p :q﹙LL}$>5.8,!FE /}窂lrh P-7ͥG؟>Lpg bn)SQ!kiQv>Qy*[0ataoT7u ePZnYʲgHu;Yv[hhu7Z0|;+[64ggwKKv jݻT 2(#2=r=$Ia;&2-Z@H^}hp9EN {qo8eލ}~gXF[F;=rj {ve%ڧ7ydh7$^kId[.8k'af4s<c%况 =[ 5yP#}0&5e.˦ eDc__C-D/Kd" $מMso?w5D /w,p+H ?7,`,V!{?C~?NYV@sX~^T7mJ2V{ׇMRTo}~I{Μ vI_CravZUna BtDnKٴ3" Ɔicqt?7 vEG\Jhx2>Fj1oTMb%6H3͕w+ֽ'-lv\e8s>y_@{AiŦ0ڪ6i˫r{>n/|U$J!$MtmY7N7N,h9l {܍c8" p3YT{ ^7O_Iw)gZo=zyUS[ʗ!/]3nq<@J9"ؿP31 >޶cOEVz>8SWC*`[׎MpBщ2PJk_*0')[+]ހD_GWw3Q7Uj΍I}[?"r[`0!Cs^h铳W ]o]?~B8\*;N$RY X}5^ohlY qf(e#1r͐qS?'hږ wW_>d(W79H{$S$/;h:0O^|:|8ي1|/?Y9H^h "~+0;&6{VH ;t 5G`|mu 10>zN*4dԨD ̯Y#I.?` _okZo㭯U;Z/ D2w%SGٚP5g;KCwckX~yǚ9㲄e%f;;mkϣ8~^WӾX6k?:E->j%.9Gi.0T0\Ҕ^ 99:wrw{('cq¡SC`ͷnlSXFVص%l j:txI2Mc%j质2s !O._@#uB{J48U 099b)r_D7Ƙ'z.#圿eӑE^Nye\U;R 5g9nѺCf\D~ Vp۲lșT;_T9ܦ+VwP|pC7̖e㦠In_{QdzĥWNMr򯗜k !7j7Z}aqħ3κ;}s%1"o:|%亮|t =Jص)>p2zBOYaw}4G]>.2:'6|xdYHŽ }j]io&4]d%1-}@9U |*~*"n{1Yo|z {D?GMĸBF|)5a)׸~s+32Ktw>__e~pC<__'~8~e~pCLJ>x|~g|<>>x~__g&Yx}ܷo}} q'@O}_w'CO~;d?wΟO3gd?3~g$Οo3$d?I'~$O'I(y #E''D;fks٢tKHo%j ¿ϳYB+7P:^=4nzJ{u"^@CBinENˆdڀFڨ?Rn"~AaʛՅ71O'|u89zֈ+xxHj Cwxs QhB< 'S t PQPQKPQQ+QPQPQPQPk.Z꼊jXo3x&Z:O_4Q~:W}5}=}5}=KLSNL*qṳ_- տp~w5տogߚQodv~][8uh[Eʯ}\ۀ֞/<^z&z&y(m3Û XE}h7o2%P}y7Q_zl^lY>O/ ׋\G}oR/ǞPX^GgRg(_K}SmK%(ޗ<?;_ ⏃EIAp)?ԧ:[qoi/TøZ+u/9G6Y\qSY-σ =2_/Ӽȟ75+4n4~- 'n]!y^j{&0y;>on5+{e@S)+yE{+NT|?5zj\9(bie1zrw/ GJT0QRS"kŜhq??}*aO+0L'U"{:^7Z54/Y o|r1?M۾r ϴy; tjFGrBm ̣\ݒAwzI-̣SQC0ޛ͇zxSwmׇ7 y;J[l*{|O61jbv!,ny{aUI&?Mqw{{f'ḅF5LE /qݽ4̣vmSSrvC0ùDV#XF]+B  x@s lA>66 ]$?`|"Gl߉J!zk%_;b{ۼǃtW-]4ttC\= ēz sy!Y'6dQm/ձ}UFMaX/Qsl3o>ȼL׿im_]7O#^3 <^<4?&yb!>Fj^6 ۣ 􋚠¥!||L44;_4AOT\,4^d\Vl2)WJh^eb3̜}fg晈oϳmē1q)h$SE7[rWP3-u^ 4rg3XOirLķjF厓~Kj_l $Nf"] φLqK qWKa_ViGQ4pKho8 v<)Oij DMyL [ZrXwh+sQmO.2+Qi YeDxVet/g-h:=D90Z}Q#>M9}!/~yk+}N(4 s4:E]1׏oLtOn.A|CP Q mVgs8L?  ^)z 97[ao]f/_ r#g%n/^L]ǒѯ2DiP SlF晈3q6GOϏVSJ,\7OBӺ.Dك WU ۳i8,gķnj.[/t$|u> $;^^׏#>L:6/(~8N>y(OY85R݆Gi=N0`W`}$ /bx>BE> yu:^> Va`N\ď;V'Up xz>˚OVCl͇Fx"ph֗9pLijC,$?Ӓwao?5^;31,7C4aE83c _iGs6bEW͜r)zxY|81zVL;I!/i$ OnaQϯX_бwMpf!A4|ʇ-Ftj_w+1O(<{[>Q >e%o76UcC]gءj<>x!>fYlvRqB<+LzG`]<{{$`B"ijoiG+Xw.v.z"K3|_rEc3#9s.?Y IG;n?3C/:p(H/[Jq0-OsɌiGsӬ"/G DFLɗ^}@?glMzV8jMK$||Q4~j=gr;=Z#uە}^Lć܅RgxB|.6n=O?/I&;-' 6Rqlhxoix3tҒR Lx'hh ^1j/xϋxˈ[T<|}B|;[M'Eb8eqXW,k}ziĠ{xE|H.*H%q6Y8糫84P\/4$ +;ycx77TyEE8awQܤ{_p;T.CjX?wBz_-x ^)NN|:+R4Fm8~o }Nc-[7F,?M3H\3_mp׺ ɢA|miYਁ'fIsYeӛD6VH&'V m%^iCyJkJi=o{L l+Ss4D9c`#[; 5p lg/ɧ'O\vX uy&?5# :`RESS)RY>6 reXt[nEz C6b:$Y#d%D{1_Ї6enпEۈIٝFFjq2iM3L2pS ֈ#P k{ʺjLP023`2S_ '̑t5L!б \LOCq%oCx/rQ1xGqvK#atC8Fw 13!&`"SBB |)RAu0dP- 0|":0B"cx$4i];13ΩOx,]"S&gC[. kx_9Gk0W6[+Zow76`/_Nl>ɚdiYՀɮXT!u._7nP+re3|BnܹBLѸthw[boh.#}Mw9.gM ;zLm`@@Dz^H+`1L&5!҅꽚yp2>9Uc@IqBW:N7uc:=/z w"e?wg z0zA$WI(8#q=Q6{F@'ES EԂ{:,!Q2 bs ϐŎr"VJV6 XrX6]Ík2=G4$RE:Bͅ5%?%0ۅD=HM5XI'YNXqbw0KX :`ta͆$`Gsµ F4~M>8C#lM-YFl %RuCk0Ne9MpjyqD @vX옎\l^9x;jZkvW7+& $1Ux {S G$&"T$ʹm: ]FS*"1% 2@5hSvO3sc{;H$#AK&$H0ЙҝF`R&*"9@P%2K>Q:8l1tOq"Ǖԙ{ba}XfW9O~±}ңٟa:5>r|q$wRm1ѧ48-'xYRYN:F ed8U&tՀ;:r)-lyGˈzrA ;:it LSDk(LTXVTR,A)Y\ 6r ~$h}?wEQ Ʊ2%ToDmlG\>@gLWeխ 66 N\°տHC"\>lX0 \1P al;v%SOy;y ,> j"4WO埫_9G)p5c{KQ8 יO)N цgLF~`!0=WHLć$[gҀ =RydҐXpj8Ncqruh|7bCp[$m@!>AFhs8`^tEt\[wbܜDցUn Y`("ubZ)m`lSn6X|u|"m0[ |blW&ӷ26Y Ns0A'[TO>{R$d:f=RAaP 5ם*Z~ 8A"I"|4Eˬd1\m(r_%djq j/ xvS)oaQτBEXƎu, lV* ׸f}ʈ̈́i R4T26h] $Eƽ2xskdߴ*Zk@ߴl:t} 㘠{@@\DZ$^i\(I+a}. *Vo%q0*/jv&&JhriT>l<\bģ]Ma!9P >?,!'L~C樗}Y"SwޅXĻo+ CRߢ}tZFN\epd@-.1m'X?9!H 3/1 ) O'SێCB2 (( * !'H,LƑzQ`U P6<Cĩ)GZpiDssNl,0/ݹ0gcl+_<+< yvp܄S;WXN~Mk4]HPĀ 2ʾl2=avY!P8 /ڵH+xlsh3 >|gP'Hk ZFf3%f@]._sRq3[ С4WMizG+B>Փ(:*W d=.Hr m/V;ĵAbWvb]b]պM=Q4EJ*|?|iP`6C;w4uQ>}}x0/Hn9N-9NLpL_4xryaJCdpk0`@P#vub%>FtE􋸼.$.+gXsM6./ lc1OdV?Vw]-ocYe$G!a|9ZU4WVނ|K;٨XI[=\]beB\FAժnЬVבFX^HW/+浰/ZLe֝b,w=;s[@|=1H ;GXROqV$s]FƨV31gդ1DŠxE^]a7JK) 5iaOvnu|,`!^,$8Ȏ R!]N|Vg " %b!1Hc\:>'9K8>GY_bʀ&rr|TLb <c! ABW!>ܧeB "+,~ &Bq+O;$NHc%ƁjBVtA`BD=Gm R} g8XOZ@MG#F qabu"F1#37EUJOa||{CrP? ]aqO]80; Ǔēfzf#\2lGgf#!ƹfoZh/ev؆^nRkE.Pv)^cEU^}"t1ů&z4N#_̀9GdA]}~FuƐ"ٮ)(. }0ށzt@>C8hQ۱p;*+Q| #X$f }@O(ԖlԋDt-Ec!v6,wUL*H}@Ghh?' ^j2[\]aW9Fe6vf2v)Y3wOk#͘4RLX8XB4ktz.ΚUg{tЅ=*ّ<q< Kr m܀܈iRz`%=tR6=@y4S [V d|T\=šVldh2p}!8!L/8^t6kjlV8cHf'qF.i,D ۯ w'nie@Q:7<{o% Dh.Vny_7QysoȂ bDS |#Tl%5mn]7;_FW66mxr>Ciq`EZR׊b47&i s;BBkyWCPJr}8at\=7PC>f0LG U{ O|F!>3D,QgA;| TE=FCY顈oi^r1>sѶOLi#&!= $治5܁G @\|DJ.!`(=.R}q7&Kt8\.26>-zӠۙ΁,.WGzJ31tγKA̽tJ+iuI3ɓN0b 4bLMyfAj<3Gg_yfC*$~M=~0<3,~rYFSLGǤ9cyY%sLCsO1Ǡ%1Y9f 3yjMsEaJKqhӉ&Gmʹ64\/s ur%s͹ As m?P[su|1yA1!/2‡->6h6?fVX{|lX @?@uPqJR?:&U2 d =_2t(^}9~Gy4_Nm䲉;$@ \gMF~^Unq.w6At-o7T7Wnu: Ջx1`'o;¯;He-P0Q~)b0׸c(;kůį~FE6ifvaga6>zgHoC@dعReؙ K@$P:, "4@0HAO eY5ҲX ڰܾPnQ@`H a ^ b &<1Rg^XB[,Ć%  a bD@00<1Ro^Xn5qa ^ >,:C1WQ|#\` F @' a Ω( Q}8YL0JIℰ^%AbXb`( K` BI2J$qRXn"@铢;Wa jD@02<+GWbW( Ջ UcrLyP9*F9NOs(ap=FKkx[L3y|u#މ vaEΜԋ(2,<rTʳ*_sPyW+X9%hC܋5AcX"O@SXHoDqa|om244eie3O"/@KX<(*N9Y9,4   a ~-@QxjiEGi Zr*rDl ڀ-,r#ʓNs|z|a ]a (At%R$Έ 2tZC-,n]zj B!8Q땞bXM\|9GY'?Qw'Q  dM/ :yurΜsN\XfHD&u* 81T9+ ʬ)"t( 8(ysyYaYEf`JG?ߡ4^FqZ9-8-aU^ZËs/ݜN ,En`) <|(O9SVOgXfo̺$MI$a7}" 1SBj A Pk?m9- |:lsis?F~vXxs;s֜sVlXfɁCo9Er,‹نdI称̾j7?`^8s~ 8׷6XK(EsEYG(39pMܥ~b%Xg͜F #,[` |a ~l0"ʳ0t8&py / *=CY+58Ih(;8t=D0{)>2眢J= tDyŃ-(sY#9"39- pQ "hi yCdy~dEN*期eqG׋"/=AS^=P x. -2\P@Hxy&Qy.s. zhEf?6Syћ`H Ҍ\6\ y8Efy Y ʼnoFC :ީ7TvMG۬Y42mE5U$v.`YkeaE)Ƞ6NG*X?>"EƕmH>AEAɚW<>/%y,+yw^ yo󊫖p>ΧݓWyo^IN^qD8$;8+qh6s3gpHkQ1L[+c +:ϛ)Jga? YŒ~f0RJch:e-k'FFnq;8P>/fW 0nbqP5Rk)brȆ@?H&^9pSbj(嫌;%ƈ/sW.AS9Ɖci\!6Ȋ3A Mx)BvB.}E?78ǐ'3L\}ԕ{P|"э k%*Hœqo::͓0bagla,(-\q0HZ?i㼚j&mAt)fg0GMlPى֦b2vnz#m{]n\;k J5s8^ )W[_[juՆa=G~((ߴp ]]Tꪂ&u :k[ڻkkUiK >h`Zo0d0KWfK].EZ[>zV"w$Xz!RB,%9k8BFRӘmN1podqq4CQ[tɃ ہ퐪ɞu5r$@=D-U!6gwe᲻kulj(1 98? CYիAkMm0#jCi{4GMT ǵ/ * 1Ur UP^tUo\w5n 7E@V׻jadát0G 6Z\\80A )bߣ-* Y8ӄ̢~qfRxipfHܴB}"g)ii ΀$@D_уI}KhU&@'ӳH-p1rP0s.Qf~u-~P]z09,Bj@k%:X Qz A Pp.zL@ГNz(@>QT1 P:QcMc}47" p>C D}x0}Q(*䍆 `׺E / aSCI!bq&'eQ%9sdyp?wif'z 6*ԗ-q3-˪-Z=YzFg ω|_}k>L;65Z"t*P|ﱣ84T>Šk7%f0hWadrBkjvBұX2𱥵EhƏ_' Vrk`4=bxީs;zI;q- n~C )8-j` .fDO&|dР7>?*KUQ;]OOhgZ=:~HO;z` '] 6J> yiUK=V>_N=[+|+w%~{_YT1uIoKc)DYc&ulJzO]ݭՋ ]wCt]zVVBU/:4'5%Ҫ8.(o=I0H~kaPԏ 5 Ugӓ#(銳%OUt".|TGUFT A5\f$MrIld^416he>Wد] vs:R<$nK _yJ5įֿZ$!Ti~3R2Z} /"~F/(`^ҝޯ5=YFCJCr R:~qA8Kw׹gPZ]H`2L]qdI(iRlK! rCn(nm6OC >#ϯx ;Ak4^4;TiW5DisB\cPZ\_e-^Bަ !JWӮZSԙ6F=gAА 0F^ a_k|Jpd5_OqAtK esI̗.Qf劔BPvR4jG8fScᚆRC(5 )ԔW>.b}['IG{E:}v9GOpT>;%JjF[(찌u݊(lH(Cm8Dj¢p똁"J+ n /ڇZ/*tuBW/s-!ܜZ%}ՈJ:;LlU{Gfeln~ʸKOE.i* gN*&=tcsQAU]whlJ/}N @FO/Z wX_׉zUW ]Zu2a~BzRV?^2HCtHԽj4F(m;W }1ȸ cbP}>ZxHtd҂!Cs_^KLi"΍΅׀g0.*? *m~Kni_iB%WqQJC; Ly-`6 ++ýMY&Ŭój ֠煟C1БO5>%ӄߣYaW5׀ a]+U^2U(3FיH Bo%_T 9Sȟ(:٬Vt-h/,nqW4~a*m6G6¥d!x'0:Pc,z5DP 05v/Lo_RW,x  R):ك@]1u@B+.ODޤ|ԊQ n Cɒ}dpnCe7[C7:2!C?| =+T?P ]>`|\SnΚ⌚bov7lh+/@C˭5ڍ a;5emWӾ16|$mJ7ZZpgvX飪Q+I-m:g?*K>@kDIHѱ7˚tUsĆ:ȀnӍzs.SCށScVd[-gfX^Fq'dKO7n}RPl|0qgmn99>SPHֈXKAXTǫ;׍ tʟ36u4#7׬rэ2uw!qȲ"z$sM.otkqL6[mrӂuZmYm2`Bһ˧rϚiVlூ3.GO*of1 ijW{ylcJp>S.[W>֫ t*v0 Um{n*/1Ֆ.8趉cMp7!V~h)lѭ_B ;eE{ g1&bt&ٝ }j>;x^ʄomZwT 8c1.!tfz߬Iv0r&?0a vEbLNp˼0g׍xF_ wa{nPʹ_85E̓J;QSѥ6OR);4#a*Oƴ׎Ml}䉽*yzr.Úwтr8|ZO`c-xC*pj\_f5D:9c]'c֬ s_6@t_OE}1 "V:}TS{$R^-܉Kk-3ب7U.r7@ݜ%h]omrCqbp˰9n FyMJ&Bov7[GmļactB86kM sg\ZV:=װ;3{S{M{IUC]*[ba=|+}E~[l`I?Je$* 5ʻ/~$SdȵI2^$)}I0QJY|$yK% FQ|L&|O:,ܟt<]OqY T|.2"{Gn&+v'; Y7-]^⫐їZ9CU^v}X쫧[W_.'C}bWAUC nkSj꯫}*_C&pI0)\Vw3AUugaCk\Wǭ->|k#*^rࡒPgAR }H~mRe9{C%gJRM *ī?ë_WHRq';hm|| 40h51cjT9 ݩsQB~ qV?_؜6g0{v'SYwљJ//c1u3907fG #GͰpŌ*ŏ(_gL#4c|ot%K"B;G^*FsiyfcF'w' X0T]KL1f%l1Ubگ?c%b 0"=׏iǛӚ'WJL[Ǵt_vdaZU74? DqHӒ)1XУ FPLKYЭQ1>wmR= K@ TI1mGaڑN&0maxmZ&MJNH{ұ?; McOkm؟|w~b}:cQ~AEזsSE Gm-O["/,R"/,jpal'%L齰HC&d@齰HӇn6wba1A[G?#kl1cq>%RX!1!a rH!~Mڽ D07F $r mjJIiԖ}=(vm9Į%4 >Ic}u.B}ljiK4.9]hT7^RYZJlpކ>4Kci3ii.zH}4y/4H&ߧy:==q~rL &?]YFDICo)sg$Cw &՛bR}y4^%7a{3yuX^ΫL gfs}/b^~&L^.)54W/Pɫι0;ld3Sϔ 8A|aggpz[[檍qY?/9K3ZT fěE3ڊnZΐ يF獃R4Ljmy,MnYl45li" ElO1Q Du DyԄDXM$KzS}o1!7ROh|svǍb~{ޓd-.MN#ĮWTr0ΑFt9r_"9G^ڠZǃ ]:8c ]6 }M̔W2Ly9 mW˙1`c%-X% r'\q|6b@dоsN 委h0oS5*G_2r@k;%;E53hy0Zk+9 )pڲN jZ񹞭RPϋ:Z,$䆟P*9mAy` [Fr>c4 eڂtZgA֢hFڊNGԩ/~ZTb925Ӹ6P !c=A Na]Sb]y"u1+bMِ _H|$@)ɔ>$rm $wHFJIĄyxIJRtLkqh3d 39t "lwZJ\{_t٧]˪]v{DvٛD.cr\+5~/b+xn+mysуksgn?ˏ߿Qq_w17)ǙùDV'3t|./c9cg>0NsU{X^|ݪ챯G}vy}_}go] B]1%-IP\出c2}yBPw`{~ע}}Bہ[h(ٳsgiE3_5m;X("-Jn~<$%򢤶 @ ArQyQه$M$ɽ%)%{`_(%+َ@kZyPI lgϭgM\I@7Ԝ.pqM\^|mh=l7W9T6yK-y3?s~\v7u;blu l/WEqx-gTΘ~[Rgi|\Iw,UF6\`#a:O0fOq~)ŇiDxD Wlč!_G'K3P8BBk,C+_Z $ѰAt['(Xw Q 45ə| G(|P>OK/8f(t|J2@́yaRK/9[*­а }$|IZԩFnrxMpzX9 H> %i2PM;SĜԔ,#% Gf4 X&Jp59Сaz Z `si(LIfD HdBPYf@1npMӎǪ ^**GB0 BQVl V2կ^zzʱE6 oqF\-R6UgvmTl0ߘݓ1U[&]8ˀNQq`|\'`,։NLjr|}cgvh\,ځXsoe ,b M=ȶVv8O5̇%i刮6qw_ՙXqrrVt'T e6o[ dP78!0-bdg;mU?j]"ȏۑdAW,܅YJoA d$#E0qTn=idv>o[ÂJxtY,jAZdܮCE2s;߇ldmǭLX+i0貹b-NE޹`Vn|0^0{rE+б|+JD跐eѠн+tztt<<$i Nt {UdaV=9!atAr{Sn!Gœ2E(Cܪ!yCoӓ#mnzƒnKM;e"X~-XM5$4owɷu`Dw0F/Ewm7K  aXWoW%;K |7Ki1K3e<ܺC1jd{"&Uēލx {g@0}5zޙOfeG#PC L`Ha  ZOglCJ` \aH;N<0{Gd=Vb]2U۳7؞ꆷy=V{#HOǒ1ClP[ Q܌dP6M >f Ѵp. N4V߷͹l].*. WWsv5 3d$mp64z7d b!+"8:0=I`1k[ {aVގ||E`,7ЙᜈT{(R-\2ΈY .2ጯI'0F l:h+͞LK2v{dÁQ0P˄70Z郏g`u[V=Y׮|<hTDB8+s}\Ȝt0 {Xt\xB.@[,J^ s\g,)0lSx0-{ŀ:8ѯs:PB #4N@ûM':T&FtшnnѭPù&&P8QH%[?> CùM$`Q5 rS.†5%ù 𦁅s:RԺMH7iH_¸'/-F}FtEz!U,Z'kq`,̹-#35fϪdTdY7h04^oPVqN*a_늝Kki@o[>8A/&.8pe?8˧'̷ɱQmsv|XotՋz@JhI%d`aafM~[\E頑,2ܸN_adxr׺*81 v @@vu3*K+bD3ubQ]Ah3p#PtpmfSEir0y$aKLg F^s  {(ޭ%/cI4 ܺFƂS#ss|SqNOKXpU),`w0x1 6F +,UW`:K2'WG v *X" | [zgc:R?/ys2m1Rv$Z$uHq5܉Pv ? ~ew>PfѰ~zJ84N^Rx5e'W󅆷ޮu>WU,>Ʒ`NmP=ɋF#qrz~J,涂 Bu8kg/,~)~N!%Y5yȕci1c{{II+:dwa+޾BYmpF꩐J˕^4'nUfCxpxnǺѨTOPm @;~ZN"qvB-OG?ڒQ-m{چ6>>|-n$E dK&YvZ+Up!yL .{22Lo< 8; ;MBq7Ya#ZRZihL=!d@oRbV)M=`@]{rubqA({vմydNf()'2x^WsL?- aP^ffȪ@|h9SvS`7ξ(;kxڳGV;찯5$vWXSqQYmjgsOV0 ަ'\GTg0xn/z4|fK~U»50}m\XB]};#ɹ^>xISmKmQ# U|[b"AMq NP|];B]\ h$Ix_nT~}O;-Z}謯«TWxJ«='z:4}ίla:^gmUY?t# κϟY9 Y nߟu(o=_B:Mg}5nYOmVu֯T5ڪi S[#FMmF"ME\LHmNT<\mFTLm#wtB#ZTDe i<`t/6F.A(m+\E&L0yKͰPb| B}ڢtB)RE3 ̆cW*v_uCg!zU*J0>CPH Z^g JK猴1) N*j:QNT y2B𳢜=E*֧4Dwn(ďODx V(ԂRHQe'|mYiL 8Q\xo:MI2.1;R8izo]4{8f:Il'MyIIow'Z.Nz(qRň7Ǔd0&ᵒ:|&og1yE쒱ΘU2N#iou*! ìxٍ}CrC4E؃;]LK;ÿsK/db{̀YQ6Pڗ eNj`z1Q +C#>/tC$Ϻ Ī)Jtrj  MP6nU (` ̳@d[C]u&F |ׯXFO2_1丁!ش'6MN`CEApm;p FG*Mr)OOkd)C Z>a~Nձ$h7  %("Mj;ͲK- )yg3Ʒ{9E6Ÿ_ Z_ e<& ůl1Z {^prѢQ.qt"y}od(G'[+EBgwM?Sz?C奨+2l;=] .87]20͝ 0UUvJ;_ ܌avjaM@md,KeO"';ahk/hWjt*wj?SiwoVGtx@G'Lshc-o|jsj\S-ٿpi*ͳt̷@u@Y7IV% !UVbG(+1\2PJ4 6Ҳ [h # =5/VaJrD4Z"xqiW* xWt 6:*=9WTxY7I݊ `$;ieȊ! ~PFu{6*`E9V49}H5w,:}%0Ao %KWy=emU/9T\F~YʪtRd1yZau Jt0DECi49J Dl:^3ɰ4Zls=91K>mjX&_#ib8vOC; [ɉ˰MCnLE;û)te>1Am^_M$*kFVY"ES`-M 9"K"0 JlT{lY=Q#'VjF#\_x@ ' KmT~t!Vgѭ\ XqV0fGE~XR"Oa)k븼eQ(8e&C.a<߾2VhÇ2Ge8y X1^@QjGztT[TT.4e-r?1 -k^$_c X@c{f H3 腟ߧ+=H| ɜ+$@bf΋ۤrZKj7jqүGo.-XJ'0%kOG2E-g[e|fOIf[ositwQkj%c(cJr(]Jk~@J|֒Vd.ŕj{j(Sv_cz#-Yb)(AC:dL]Iy xqN{,Rx*fkꂮXBT1] 3C`>ũe@ՠys(k/Me_B6jݖZ./~n./Uͼ16cǨmNm/9+t"-K;WoQ]DG`4e/s}/s@x\mrZDg-cЌ{SU?eI&gwz4ˮp0m&Zb }"%O\ ؉ҿ"#Fk@z ;m4zd7%ob4L7C@yz2 {defcEKsr n6hQ&Vf0w/|W$4Yh>ӸKPXgC0j5#M*,.}IHImbP+AX QwBV9@O"KE: ̚-M"%iLBU,8&KɼJqKrPA@ˤg/PL3T"~u|&H:7 eu2B8> TE0AoKnG#u);pGڗnGy9nFE,2h,  d2h**o-_'w%j=.L 86 ̚^BjcTỲ@9p8jmgÇhs jv U~QaH3gQHa,?#g߈&/-&.|)%>R"V iI.{52U9,e{`@@!LmW*1v* ˙j廸K ) [U&W(xsz%ڟ'e"ߨHUvRNHWT~g] d.&jc'b0k(f1qoLotכ*qܩĨ7k$Ci:#^zaVlV^ۮqvFƾ^3]blgpf䎵v E.SNk@}+'YBnmn<;%d=tB9*[("Y0UTa mXK^1n@D{!,3Zfm FtH"mZ ;~ZH.oa\tz($&_,cPH> a& zɵ4:-rRNnW&I Rܡ\3;$tDkpk/=9-brf.pi],c MQ:e!PSSA%f=J{)K,N!#?[))[o9UOr^3t4s(yy:KMbnd;)h˝ÐEX-[oi蝳$o5p>)\x*VcTglkgdc]7dQUj [Y,9~vQr@@@ԩ!U&T%d2Qs&k\5 3Fɭ)rHQg=K.EIAXV /?E Z=XֲPnFTv#?`3 :kڔ b ^3Vx7osNM(Yj[ONmFwfL4cSozĖzr'NmvEدؓ,.8w",Ȯ֬CJcL2Mt6mJ?-Aeq(z-}U(jm+1"-ʼl tL{;^4n3>.vZs jʼnV0uR,Ϊ&;)Pԭ9'eoYOU!W61vZ0ԃ[C\m020U|wtʐFVuZj_jn&kBZkJA^0UJ^y,zy/`ynw!9~' m†Fx[wc\%n>1g~ByӯFĚ4m}ZW%`A|F[mb9EU0^nlB}ZtzWh@>”Rxp2Oɜ}/7iLse %!_EBoqzhC_c[Oe6^Nk >/Ж`/CwFxT |À/W A\T(h@8蜢rAh|k# oa6UͺhsF$Gz0}u3F2X:MpHu 4,NʗZ9jI ԫ!R9ت~N2hɛ};[xe=-R/{mL$: KVX d? Ax'<] vr'> /d53̊bh {!nrcl`/{Q8iTը˚&ŴGba11~F0c|Wؘv8rHjd"=Yчbg۹}Klw-y\̒ӥƊF08;Fs|FOb6bRҝKPɕtvUfl,vx2oq(5 EuC&vzDW|h®#|oҜ&)=z~} ;nX-}&Oapj-ntƤrtAZ`6Vs 3W]nѵCWF%.WE?L{ 'aaеMN-WtRb?v# xr V* +ݤy>Ɵx[7_3V,o2/+TKނQ,u;-'<c7i$0ۈW}b:Մøn2fRVA}Ѓ8%#Nbo/TA!=[ AGLO]OJX V-X=0/`sp!1Ah"x2,"XFh!=O6e?m Ov!=lNQ=76@WOR_ -XL7iVA.Gj3p }`UON_f=c}#W)W=N z=dM$*n /S:k 8w wDžbDG7߽q+{#qf#VyR diD!z#h=H'|X[JD/q܏,.\g `U5rfƬd.f5[L? Mݤ%'C[m rc>0dT^j4cvAw 0$ vK"mwk6y^<q>8w Ǖܭ(h#f?A>\o vrL}ĕt^Ě8tDJXɍ?઼-b/5k6?wRIC2&#А?Qka;7W:kאj h1&+9MyO馉|<HJ{9hcv8<DpN=/]"7O2vF2>v*J6}cnl6OJ_z PY降f(@a7ݙ7VYo8<#ȱ užSyecKkv6wbg&'&'dBf\78G!"l%+p+iT0Kvܡ#o1cಕt!K'۬n5&ۄE4w?Us͛JV=tt(q~&cu;$vRzgmVkS7,4y1(YHVݵ ( I d[/@WJ6lZHr6 DЕ ƕN.zUEC{:Zs?>a?vKÆׅ LAX+T9~цV{j>-iYE@yQ 2b]Y[w GK1wE(Tk8< R6 OY^&}9]lҫI%Zsinb1\txROT 7iS?=NjT;Z'_ ݪe4m֏(6h;n`hIyqHue_HR bB7a@m؊nRV ?y.i6%bnXL?oᛖ^pxZy4mLXiT}4wd fifz>sqEkl-=ͭܐz!oaC;}G^ ~XAA :@FeSCۑcgکA XSQvvv`fIs%V, We; b.f2?.w.5p"=2{Wd~_go5:q؆bIՏ&_6oR=fIT" ةFG^yzksqX<3.-?`:=nR kB3ڂENDse9m7񢪁;ni!č,Aw }&"܋U.Ut2ߤd6W@+}Ra/jrčJÃq2mwpw O&KI${):_ Vkc2ZSPM3Ь*.i _]lYp<5kR.~{te[-d5 !XP'&FOvp]<^r&eec:Br@yljgPtxތHSzI*\:Wh6ΚyH_fy&q:68cژ1:>xדeYL[0õYxdd)FХ8ReSFG`{014 6@g]722pOivT5 {n^&_a Y4Ў+^^FKF5wI>ژgc6_ْmc ؗ,v4+3.˗/9=fX>IÜ\,*K7&pU!X?ɢtx!kEҹNmOt5X ^d.C"j0n@t6>Jx(CMlmSw]B*)F%8xO&ez6ݏODz[LH=*n/TB/Y8Ia4g`]?f}3&!yCaxpi^B@R|Y.Wq̏Q¨ۋ*T,7c&R.dJC0k[o*6+0AXrX=Qp 2zLze .LIQ^ NʀXwD^ 6ę[:"N|X.I6uzL(* %|Aw܌W (OgoSMmf+MMW\A5*l2B\W51h&2m‹[` u݄ɍoeXA9<w֣e'^v|9k05Ra~}d{T` ^6N= S$P)u[l% /Ո5օO ut}A<[: n27KR3P9qE4 AX0J%-A#φW 9д̔"Fşh> 8q0w"CF|P\79Ghp@{&¸`u;JxZS3RJ{IF7L/ m1a;I&q3MGzÀt~Q7Dxx 3ֲ׬0We9X9k^&:5gObdD[٩\R.#g9T#Yhe4Y9HTS% ƛA{B#!VՔes,d@ZJ5GsLuJu|s.Y΍x7s#}utXFk}F:=,:YjaycLcu;~2j؁]=Ck.Zp;x%rPmàjCohaւg>@{S͋l'iA˺Cސ'iz̖<0SM?BfFCʹ*~P"':%b?gfMѩX~Ft:ȡy/܆vu6tm`3qW>ҩvrr8=u4V:ѫC?JGH^5;?.BF,F/{9Zho^.܆l6qXC>HY@#d5 \ n{GV C\L,X Fk.sr.s_X=zHChYFnuh~\*faHhq n^)GPܦ,F1F'KYeh;# Z{ɧ o4:Tmd)_)0;+Ǯ`eJ1뿩bXre~̘J-58%g?DVVޛy/gT+&FO]?"&wFɝM|/֦ZG怉uS>x 5XkCUM] $Ћr7ޔ`;~6Kɔ[Nk\rt)3pz_K:rhXy$e?. 4q|u2Oq|{j>@0fɦA`:R5`Mwt\y/qpJ̋V<5 Z2SfEzPww؁J OZB-SEP*9Y WT `8 ֓QyJ!*[{vfl*Vdtמ)GԞ}7j. !xvI0]Is> Yr+ߗaOQYq >>  s\K?xszT<0RgYVo`4䤥R'}zy=Irp)x(ZD݂Y2^o[d+K36v;h+Eֆ\JRUB IY )-Z1.CS\~xq:e[`-&6#txťz .6V~C>uLo8Yk/m^a]oOd/ tMl;.<_NZLk-Ǜ6@SՆJ>kɕoGE\jO.|(?J SbFr˰:]hfJx=M{{mxz4P;ˉNC1By)->ľ5 m1u|m2H|q%e|h($cc9c;]* [!3ٔc[]da[laUȢd d. /Q ;Puī۷|]baj0QW,ns֚_ N/V*siTӥgBAƀlŁ6/~R:MT$1hm]T^ 7P*bimlWR(Z/ EBQœ; +y&ȼGiq$P\(zI-P\aW(xqRV\a`,P)p/] nѵ^WSLXqMYqxu3=螠Mz>GIsz}N %x u++}f{=AzUO-lË+b*Z\!z#> Q2*%RQW2}toL[Ra%#O2Մ\&tcy51 B/{[蔾O|_ޓCXb-W engDJOn00Jp 3]s{+Lz[G?DW+9:N;k<z7fpĮ;S@T*j4ƱNZPO&}F;NyJC* yrXU_UhU-ߡi)[HO`UK 'p1/ +H ZUai NUߟe3'wN~L/ ila>;gU>xVUX<h@Ji.6%/VʹdEQ\ho?Og^[)Az:ݣ1ryq-FSMD@Ŭ}ȣ >NdT&*zA :1 ke|&rJJS[ܤ?|O>safBcDA/ d򂻤p4Nlt"%]d5]tFM41\LM1N{ Cf"rz1$?JL%V*#=|Z/4:Co Z㏦6K`5] o{?FkYM$KIʗ&} u|oY%,w;xS(2<z/*3w6K㬾Ӌk.K8Þu;3r<A d =y;sz]UHĖ[cnc]+RP,rQЄ7pRl/U[JKReԔv`=zԔseQS:Ke1 mbnnɕk&RuHs9j$2@IՅw䎓]K'k3BE-ځoq ZSdMNAoRK~6uFtR*}-Y̷-KH7j;g?<;*R<~܉bA8U{;ݧ\І}!|l/휕K: nN, NX#Ysǹ}nWf.F.H."^f]6bIWXȇ4c)| (BaaR'Ppϲ P:DdR#hiC^8|D4᧖6q*R܃kZ @@H(uJ^їT p2Q$oȖQ1vًD$'ԡ[0Ef.aY"9//"z}Et>=u>Tⷚ\WٹI\ⲩ G٩1 Y&q1 y%Pumѡ!3Z;tW[YZϪ |q'ޝ}6j{TspBL55l/8u^> 8ѝ*ŔUr/3״dwu#UnPj_s (yxQ}U[/eHa<+hܩx?{@55 VޫFׄX ь;Oq8wlKvh̅ tߍYpP=͠/f5>aq7g?8pByyJ*Nb8n|qk3 p] }N7f(\G)CłI/n+bƍ?kP1v耮5˪t~~`Wua{^8o2@N-.8j? оtF-YM&EՋY=tpzá G 宀>krn_i_ع:~}m03 .Y{2#d]L`&Pu3yȕ[Ѿ@Q30/f`4xo`8 iIB`nk8XL@& 7; 7PbR.&%=/B9@-E*V{Q )M ũ0yMY3BA;\F.h[F7z |عO Vd/UZ"PC„;5OT :MIaA.%JqkTbŅ+xdT7cmBMv"&+pНNjU"MH%gYU;7i>n Cuԗ:I' )uu&t'8Ԣ?ڮF\@(,6R3l.Cs^E0Tkh*U9S\96 xcgtj'y;wvL\Xj:궖7LUW8j.y`nכEUf\a`FmTD,J +ڀujA2t{cmjw\]q6m۶۾lm/۫ZmɈnXT]JLQG;3w|ssss繆j悂s_z| صՏ! 5l1n+ie&Qk Ւ(jW\leoe qE."mFb"m8'pJ^NU WD,)GЃqЦP-Hu J%-0Gp맵ǫj'SݿRЈu힓I\!c{8ok\_,p8s^]Vw^ ju}voԻqRVjZ{\&:oߴb/N̦7x4sRNglsfh+O'W%+9V"c=rW=ږGs^l}szˣypSԼRgں3<:79W̷9o״5kښԐl<ȷyzj*gbT&HX)=pJƤʑꊕM#.m^Nl/Ԩ[Qm+֓_Ӱk>_NrȄ7 |H|* ]G龠CzJK+|@0ye@$aJ"n.O~n qJbTZJKjzkZc^֕J'*Sr=IyxD?Tp4w;Z?T-}XILSk]RUZUmP"lU heU7 o5L̲M^{OP30_$g :pN-~"·H H5<}O;pߣ. ~Cjj +r,j܆rr|W T߽6qT(914]%V>,|k5j;`lPVbB}@-5H߽Xo}FNqUiYxǽwgOOFkeλZ鮮| }ڰ4@ #S~(JߣO\/%KHgt!1Uzb;CMC` Zy] I`ï&%dcXVQ9d12gqއg(A8q(*AGG1zA<W=F' 'VIƒ9[x_:*[pY]PQF U*/]f` Uk*̃22~'aV{_5Bw="xBW d8Z؊XqcTQUQ1c p( icꆇQmp[Gi^Y y婐g+S!UFZV-ǁ+ؒe4JH 䘍' *t[ivVlkyoˆu߹]5lm>t\sVVU'j[Չңys~ôi[CZ5"lR[ڝ / ܪ J--{";Y:Y` =2LXSd3z@;[ P9 ՚MfS)*hCfhi/Vg&9 =f1!ζxeHJ:xW9]yc e EC^w ~;B( O* uW :0;,SkqO$I}EQ1H$/^!GlaMRLz# ʤ$NaH_`CRp{y L8\8C#h\kU\DNR:l U +s'\i2"B5KɲY!>@[>Nqkzd^p惮p:KuT=CΝ9,f ea, fqQps6LzO4M2#[$lr&$ȳF?:SXjq =*O+geV ykUo:~ r7|o:l?劌6q/p7vZ1+ +xiYǵHP|9qؖd h\}1*btr1P&S@)( K󝉆-UA'C|ΌȊHD^u#RZvpn48JKȱU=u 6630dg\:wKFG )\8ȃ#=$>zZYOKQVkz f[e\7fs (5沐ӣƓPr\NG l_ fHڥ*UA7Ú~ʺ8yiGw܊Tn5|G EEU a"Q%b}5s\YʢNz\]Du&'D#^goW4]#i0pE{N+rRO 9LAL"k[W/x, rARq6\};ewIwl `9}aCu0) %d `9CJȴ$bqZH,(ô;0Wrp}Uy4 gխ`nَdW7z ZsgW 7׻ko[-V>k?G`>^Tek9k0b,$WNEVu^NռZ>A3G[s ͭbIZƈep'5WBtkC nqC6;j[&gh = Y{ɁUGKXډ-$?dwRbM0Ńv/6 Wu5\@n`R[8ș`b_ܪNvvz}XՑ&,QBי> ōZ2q j\\6r'ȉ ustBhѕ̵D?瓤qwdz#t*3  < gus|E&y=hi۵mFͅ`ajMڮ- >W ^MqC*3Yze~ڨpET45q Z6΋dwv묵ϡFMޫQQ$:ޫ%{.f ρJ6UVN^U7_'$cy~V|ȥsUUS 5݁=\kwfAxm9N3U`4ZdzdhܫPsUÖo:pe'ËƁ)+1eYeG5SF$V=*X(U"UÊσ\q"2>E~r8+G LFɻNMeupIܩNrn'S:O鲲2J*6-9_h\2vcN"=Ų|KttK{׶ $Kwݰeo,e_ mNhĮf,q@?$|n6<ɬ^cO,8*Rbg</Uy\Y3ƣer7ē oaV?uN$HkBA pT)~XP-|\Rs1Z wɏF`uuu,q|TzDӉ' /Vc΁r( OVCi M ^G%*_UHns%Trh)VZ9 ວnp}=p}\e: `X]h(|6c*HhK8R"5ݢ+Oވl5Kl.ayՆ15& +W(/2aL[iίMG^:BZ|`ر-PGOe0An0%A zQXq(WVqY(ng6nT]'_{<'7|~&:FT6}zֈCǔADmH_L>&Y/VGgXD37.`|ckTߘZo|xBo< j5R8d{÷8&wk!6=:JS&)[pKH($4ŜQAijQ J>:C)qd,БB*% Ӑ [RHFc12'fGFW.3~t!* EX/|Ty.14/N_e'i"B,B,b h0N .x@օD]x AU8KI))6RQg= W3 tt Y ^ԂX|oT:uJjW{PX>{IX[l@( *N*7>XRh0Yb̦\V+2ڬ3sf4^3v)iL13M꣓l3{|ivy༊U/)bTX3lK1 M|h(tJfy^(V)[UʤyՋu?-nVMuؐi4дɐ nxsCZA 0\ C'dFuV5Z&`bx~RU ZY*g~Ya!ZI|Oog 3`6qq,y26]b#2Bɴ#2{:<̐jBM{6蚺֍OӞ ߂w=JWj^8ljݰgO>L^,G{_*wn{-ZWE&K`Oxt}Ab~ > m69{w:8|вӝ=^x& Ub#j5T0eNnpf6:vHT4:*| 5JP^Ve 5"&^R;-8nN0{q<.>FGLJ02"颡UhT+Y vs:ɊL{A!ڤZ^3&H/@Ab\ȣ29bdIL2(Ї>qRޮǰX; = Dg}(SRI#P#c[? QNBn; U*<\;V#V,iUyj7(HA w'A`D . ypk3b( ~ᵙ%}8cTAMlb9_ :ȸZjfFu \pd5 8<*BENѸl=KkC-" ?Gz2.eC: [HyRfq"~U*'2MdPȘJyO ~_UoD YMg$}:v:c<j4{Kp} ENg|pJtN7Lg3>=;3Q:pw?31tƏ33L\92:olD_&oqˎ(>S~Djnru֛vr\ռW5oa܊_p)߹x} oԏy%j}UiUs=]lXռW>ϨUՆuީxO6W{+" ;Xu?( 5~fů!ĵW{_&<;*:sŋx\@qv0>Ok*) N d"ŚX_$?MxrT0> [.h u3[Y&Ҁ 9_ʧz8<9n=9N1ui)(<++eGLp|ْ 4d1Kc cbL1G$bh/}~9w1(o ֏(bV9G>{ sqN9dGQ=dqJ @蒏%X-y\։dCZBuqhwؤW FNwGbGJ ~-?= "~řwh9tx؟ZTd TGrEngEQ >ʙ dHA2sh`((Ecd$Pl Aҁ((vjㄓ=PQqߣۭmc,/qvx 7XFWb=x>zN05RݕϕxS=W2-_/dc:\;~OZ9 ~!V{~¯y7i@4j &jz2peઃN7"z?_~"Ʀ#>+!ⶋ[/Kbwc&}Lv הDv;]5v]!e]ͤ SF{/Fa~*OΕ"(7+0g\bmh瘐gnZ]\p[ ߹ʷPX[8 ?nI +[K]1⧓4R[Aš͞Vuah>뀻6keU?\s/hFs}_w<<|i}"+_Vx|ȋ; øNc:\G [D:~?]!}qݎh`!Y]hIIuRF(ߖ[NQ7_tÒL]ם٭-3VvƓTRd)P\>sDgnFYGy:֏#\ޅB? '|62'2uN@X*FT㦶 nlu-_G!]՝ݞA!{ǯ{N!}i *W :h){Fywi^'6fL6 mqj5nb#PaIxuxoڋ4êf*_"3Ubq=@Og2JvorJcSH s${'p-M/j9XjEvk0Phާ@/&ëC*^^VÑx 9 : i쥉nQ&f`+v*w5>v'`No CPv\ B~=l'9H߃X;P[oF(r=ʎO: 2c0yxշӤvq]گ9gGHB~X)Sfץ2^:W3@LS[H"+ ݝ;}ESq 6pk"\I/t`)BXf,f=}T- Zg@9!u`VFiu -S` H1W'ۉ$f 75 j6X!)F ڨ(b5<%A<^Gv7T `|hHrR$ CgCM MMnG%]ϧe(rbOȕ-S+"yZ= O:Y:ca;DGJN5\Ԓ<6*E=R܇!TiR'jV$99FPähvQ>LmHw*bN2CЗL) ۳pRRĉGQik H#&H_=L@3\p5˪hwj8QvrÉ'm㺳[I[vkO?߽dr& =DCe0=ȝx>H+(@ 3R  wDs.l;zoRl@tݹ1XA%I1Y M`sa1C.MM欠D9̈Kwx.@u@&QRl-+C@dj z˩uW#yPH*Dd亙S@z/j:r֠X}&\H ?5 r gT^~2HWj *J(FMc ehk1Ё:)F1VИoX䴢Oc 1Ёeh ǔ+b@(=ؒr]1Kz%c5}c 2[չV&Z3F~XHބqTb(i{Y#,-8k͞9NDx8>TT .1^=QB&xIA|KIߪ[w8L Ͼ$C&N+o:_1~sBK9˻&g R=c5VEo<Բ".ů]b:v\>7xF;7eR޻&o'm S7C~9u7wlI,X،o~ 15!'@Op/*0RzLOIjHjU|9~+Fy@;Ǔ@;" |va qρW /3[s4Oa nTTL݅v5؀{e%#a/P`t٭]maPAAc].*G WvSP "*qlrǶ"IHۗd[^8+ yap xbhxKy&Xh:n=B lJ2"9wXp  E{8nxwSb C.Qv V! o V~5H9,׮T#m Bi,shCE= lLE,& yMif7ڕ3~ݼ.N,t;=4&>&|jP@4 8&O 1V6Sr̮6؍5}n28*yP-@JD:. 59cX=-[;.IJJ%fH^% }UIFяhQ *mq"X{n3U7(Ròt!uONS/RNzV1^zTB<xƣ;S`Ӱ_ŵV3Ȩʊ@n5%S5T݋c6Ց+!G)p/k$cYps&>0ڿh/:@u ]%Q lB[Ř'1+nJ9s uLFsҩN>?$JeAdrL1S| dwCV&7j=Uw@"?Od"59+ծ!bh#Jf鯄{3w[iK% 3EOdc11 Q1- cEnr !O\oRmR ':;ehQ)=~%\4lhR*Η2iD@K j=@ 7PH}A%"xKo k߈Zkh^_*={»} a0 M6,QN 3f E0:*ʔ]x%vQuEc,~ `YRgУ&Pl;T;*=(9IY0$C$ fBVj-UϷpK zϣ*bՄM]x PMWNSgc=Nx̀YArK[P^S~e,/mG!5i*A05XF;0`0De5 zFE.*ՓpYy#=8JY brЈWs,jiBRl O*6,VmTx#>hHO r?j]ewϳ4aʺ$;aIob厓kz_&G%Bެ(7Fw@"8<1[JS(t7q8(!Oej:Pm&pg 4*+yU aw&]k.(GOq{gP}xwxIȜG=KEd 7n~#׺(hWkWcq?KS6t=0IS37qt;W{^za܆t}8>bהۀ EƵ,;N].ݬNeZx bj~<2q!4*n@/n855lI_J_N_1j&0MtY]6?{FXaNaO̮ 4 k b7 @3`lVgyw,x0^q O.- FVCԹQֱӟV <ׄ 0N,r຋HFaY-+"o7`-؎~{ũ#@E.U:b_3jqpѽnoDf* 49J:@ewxD_xy)&ldLF0gt~Dy!;kr eR$_J6ѲAPh:Q,`<4N/mzx7b n%Қ%sv|*K}!K;~7 )խwAҳ }e-`4@+;y>u^;ᵠ*WDEXpopP͵Ovl/[70,zP>Ptf#8 ́b y}E>f@ r^5_;M @-uf} *t5;h\2IʹK' &:붏 -UUEI\9z?.MfSI /s\Es:ڭrƏ\ t;IVH/:5<_'Lc0SHGUbg OіW%łI=f o[x->gf\H4ё(';ɏ]'J<zNS'BLb_!g-3t"_X9'g˙7K>!W"y"/'9=wdzar5VOp[TDA~"%՞QW,V̉^L(=a2ٞzG3J94ldg9bAv? EE8E: W[kM`T L\1lib5F`\6b ;?dJ OIxN{oc_9eWL[qnխh>_^/Nh7.G8 -˯s CcsVfʦ /پ_9rgTibsyb#yb 49bqyʄ<30Kwn+NU C \OԸ,}˻,UG>P}:XCPC>PsUdݜʬ'S ~<? l/yzm<E<̓o@_EI7oD<LWxi/*˃4y {E)3J׬J0>twVŰZHS/ 讗x7 w/wA9rо| &qC+MM >WY55՜5WbY5rDC;9? ~ =Zкn۹nQkv\ O,ձ;U CJ4ʄuaYn k=97~oTa8)SIi"6?+:B Φ10>"FFlgs*­F{[xV'WOck3X',<,٬2.,=}X󘂘/9k/:O u`?&>XZFWDzD7CepSܿ~Tip:92h8Fr]Ə D,fC+c jLхaSR)xdq djT/JjCs%P;j&{\G\H`W}.1|/ƏX n!@3g KF(FA3F}ȫ M8Iv}04@KIert ȴEt\1R/ƙ)rP ˩yW4 { PD E:saDr}vSiJepSJ âCx x=ew\-V B4EtIh egaEVRz\h(tA([B @zRCtM?QӑutyH`‹lB/&w7+U]W/i0dȫU^=ߝ3BgRhWexm]Mvk5sV?i~}ª kxgF|6\WV# pegDͱ6NaL/ffC32RNn8I#zFjH3%j{TExE/9uy V$?ӭXzhxzNIYXvii c FHmDmh帩P4?F&T4}T(=K]HL׆+˙2@bk}KX3e ^`ƙ2@@ TPt{D]O EȄ9n*]261qxzZHF,eWs]%WÍUn?rۑ()*"E[Ұ5,t-T_Z?NJ{{N#z# ;D ]w޹u;:-Sp!e-n^`YLl@OSV? &ͭuP G~0\kohf[=6s6R m /Vйvosm3 So´b\N::9?d׭pK CTwj\R02kU+WRG/,ZXNX6TxN,̃v 鮄~3FF5ãohe;*]GF+gãUEh0ZCUeCCfiji!-$!'׽DrrAΏr(L"\`wI7@m"<Ћwh~-ܷؓSAWHV |Us3a |*TnC51>c]"ihr p},'ǔNU ㄿ.145\JfҬR=(ojTz ^vg6;ƕK l<נRkLwe߳K s!.}1h۪өTm[':O{.`j`lgK(!3> [:9^gzcWo|g_Qݐ9mXŤn6tfwKّU+#8RE,%5PB/.l6, \47 ,5C-M_C&|'qJaY;%_P{(ӝˈT h5=@uz_S@k%2f,E)F}PӂPJBeddb)C023fDofװ$~88׸& |54FzBT");z.~})ݔ95V߰$Zݰ=7xNNMˌf#mW -l70 _ qWw5*ɠ0Jql[^baqfB%Nnmd$qGPYl*q4 9.#=ڰqYƕ e&1-ZP Y;HP8> # wFk+0(HAaH8Cm6f'Ef),Ah rp"C9'mJD/{* d{݌eMA?Abvk[7kuAb@NTD~zCh%ҡ: QLU6[(!s%{ýn|hKJc*ڰJQ'>VA5wu*qM b7ǰ0p?$2-l"K`ݗ0l רЃxܽ::1Heiw޷>T]^X_XPp={;N:!K`%0]/ M-JOkk`"9Uzo2/P{O^mhZyK^&zF%F[κiZ=bu P ^̞u"-U9RZ".؝Lȼd頡/_oFv5w p.C֜^<)!aMlRWB>2+Wq P΄mk4/pʺ< "x"}٭^`@X-Yd;uOhd$wrsZss=/&^w"4jnn9}.P;LP% qwSb2J{qy@H_ reU9p~l<~J#Q0;%FQyy\jBkOkj|6!qZDf:v{/sDul)~Ă$vh|^F .sUug2!`!  ` 2 i2&DsU 3qZ{rzWﭨ%hy#R>jЬ0߷93s&gԵ9>|ۿ"*&P U >X}9A#+y5B128 Zֿ` s"A+kƥź+l-W; >B0))ge!Mܻ7,~/hHECo 6[F@E'db ҒI؝FHQ 8 i;sͤ!]!4ܹHif *+&G7qt˪@`JGf8y2Mb"fD* QѪǟ a֒J$fVb6 aV̒$,{^%bN+11' X-r4>E_ˆ, G:2[߬7T9F!XX,ҭ3g?1'9ߖ /·_,߁(Rύ]6;%4]gkg`Jh"]鸥$dW(;i'B%~KNֵy,iml[ӆ! 5xL`Cb\5յmIc#MO+vgsw<^Eҧ#kt>%ΗwGD^F(1p|Zžp%]PtGJt l G#=vV'`zjMoϛǽ <͎nnN}"ٴex(@i569-ǚŸI' U3?>dI;vXBjb dy 9,GӬ? Y)^I Z+[`%-ľR 1'Fw~^C>3,U<.Q?URWrd.\A Pt`nP}(n2gyPS=Ah|sb"uNb2N\(C 2k d*Zda el:xX5bAU=8N3HMpڒ.P^K"-ZJ0'B~Idtw$/xowpT$3&=&8Jj4@P Kߖ̙T2q"Q]ji!ZQIil5!`ܙJ{hVI[|eF=7\^%= A6' gh N%Ҍ>p~'N*ւJct})v##W27br:0(x`:gj)4Q9K *]d dW)5J/e^& ^&szE[:x&&A@)eӱHkmvPPMEX6)A΢!-TJTg9i_$&p~KU)4tMmT4U1 'Ue:b:%`qSbBn#LtRt=KM05c{0Ev:zoǹpeW 2ڂ)NA&$rēMIM=OUJPoM 3T!D9p@ؒ}Xɐ:ȩ\^%_e샵 ޚ!쭍K !0kt|-Hnp:pNzN15ٰ|kFQt=|t,5@eݶ_-Z¶I$[%37>OdBԓ]|uC\U]4]sOR샙 (ת̷8g%4LdߴO,]y>˥(bUj\pgR{Di6=#k!d䎬1֞3Ikf@vS[^4~Qe]4HܰNCArcsU w`s}W;4Pc:՘Ȝ8\ܗY_h8&Y|{K- tRf|8FWZs)sfJqhtVa;قx<+1.az"S./C@fl]fhd"l'o&˸vSq_q;gD{8?%p| m Wv!de)|']?l!Ȣ'Y7>L\-ȴ٬#yU 9ioC1΀FN4O|ޭ~ekǃ\v΃W]NQj!m$!r}\un%=øLajN@|9dyT$pDigidbznaMvqDfq0WWk\زA|c%p e) ~OXmp+6- cKlqłac;~};~M&sٲ2 Eðy֌15tͮSbpyb0K-H1,u[DiPBlb@ ?P GNc'IEkNrcZq;Y98pwx"Œpvۨ1NId9Yl41 "+ 12S +C,bAbRZ߇+=,q͢B1.~nyAݚQ oϙ&,n썄Cy$4qWl׉j~l)6c1tea/9 Ut K$:\ONt^ކH^Pɥ1PD^JIHFPC] 8#FQˆT7FMP-\[$͘& W4+Q8Ҏ}. #2,_eY:7d݋`Z"^v2J8}&O\ lfHP(rcc$e+470AWc 8c&)ChJ[G%jg74N25#hv޵2B렽QqNofC֌>Ɩ=-v n)}lQw>Zủu.O91_ƄiHnfq9YyVZȅz+Hr.<"k~z5z$=qeჟu[t^c^('6e[U%Zm(yCyҼRj&:sdc(}$fb;Okv3PJ TdSIЪQx0}$@̴ ž}"3aЅY>%)y .{19cK[kí"w{+LEeCU2"c%dqZn)?-.~ ,vQTY;t‚8 |%H%$|?fZeenůtcS{\fٓճGVgϬ1!D1;Xj1AHR~zUW޻_T-\OSGKW6s{Z5,il-KDk=ˬx"_]6׉2]\Uf;]{QOn\$Wmq/=?`DȾW̱aKo ?Q-X:4Vi( QVK*tp™븛+{jCgLVAf*K/cA[?5c(%sY#';t\u~t:} i*GYn4pSoPK2)ihy&g||4TgB7O AX#>@Ld#l-$+eV]y 6{-_/{i5N̹ 0Qc>%:\aE Yy = q =R7l Cyk% }.Ɵ XQY9uY% LKJ6ȄFdV V WgC'UPb 5s?2$`@M]eyZE:)V.-A"ֆUX&"+t+o-v}.7~5ҍ.y?faڂ8Kr O4;$ ]IBɖ>% D4ڮ,N;g2d'e'2p<{55*\$*Kת9d#-Ww {;CMq\l,m9"ldMFDTzqͳ&R5Ttd?kWu}?o}U` S>;=eS| bҁ b m!A }~=QnXH/ dQ:yHOH͛b35l2(HZsʄjBRd;RmV4ꮕNԝz<] A);e_/F fBҹݎ4i tX8 :0‹3<$Nkyj">M[F^љ4rt~-GgbL273׫޺ErͩyqP.#FDž+4/ nX{'(z\~\.\zT3( *ly̟kS8D}Ɉ>L2s(#8R v'3&b"duV/d26;Ig_A'za`@}ۻ.Էh\ZB,yT,GG9Ċ }"޼uza/Pkn =mJ-)+alX.h9 ,p^x G\ WvFg@#XhBTR Y'몫|^"K>O?[+ɏL3+;5}Y1*GK,*G_Cz0?RZ֋)FLJPZJ^@H ? cR2*h*-^A{d?oڿ^K4A x?B{MGv~ 74ߙ m:7(Ewb;FDX'G׊+E)R/ngnܟ>CfBZ-~#Y g -(*u|LȀ? a22ϢR2Z f8S-j@?N-g<`ƣݏb>NcBZBDQh I FHa ()DT-"gUϢ%γkR ZbPr/lohEv %ǣuhȨD\7 Tonn6u ~:7Ƚ Vaa%]4:DӱUmBRxOlD;;P;#T.점9EfI~]\uqےKE_quP{Jy ̜C LGr{NaI,ߛޘ*37y;Ӆx[NՄb>~De{tTgd4/3>VDo94_[BkQbM閄̞!>*s[^^oPP-\Zg42uw֬kTEQ=SJj=vU%62??dsH牻ѸX_~B'3h8I#pDbmOn@},z6ǥ褔NDTKLI qvI'ly^W' Q-&>(;*:R_-:?Vi8CzM'7@Xȃ>ÛlaFS\/󊬣:BUC)&D7'AVX9X/ޭ5kuÕ1-yZskƯ5kgÕ]N޾Pk^/I 6 ZA|EwN82#Q.(>J-k:/٫,]5ݧ؈!W1))[c)gW!Q*U[ozOPD":1 O%GrƕY BWuMhrkI>it~TӿH9{XڹȦ،HM{=Fµ"(V &yTd;Y&&sjcR'uX /0Zs[,{KQ XiIROyܾG^ZpK] CF~Tw,q U?(fX<;ir,@:3d%8> 3Vb@N2H޺9V;O1N}2(B^8)]`g'Ԝ:=nN}v|DzhH残 C.E4bE?6fh\>{O­i @YFhEAK1M=JakFh8sroo}PQH c7iAQ4N *(DIXdz ě$1/XvFI3 E}8.2^'.cexw*XdIdK.Szm]`z7{$n'\K0 5mLk3O"nH"Bd*` N>Ncg}URk錱 #-شPB,UdF&3 AEɌ "@fY g8EdS44 3`z 3o &3H Mf"3p"w0-`Ý d>E]d9L.0ehATˆ11MSp:Gi@ҞBi$FLbw4!>+3-+! = MU 74|4eozTCPQh0t叱Rl%.iBE)7AF|FEPA*n!(m87Tg2-Y2CZ0yp9!8Cg!/hC ATss$:a[A,3&`xKhtheŇU8(6BSF!a:m*:QC,*vҩCpuQ8tYriCp돊\ݿ8X8ݭ}áCn/8VUttc4CAQC\yqChF G!*CJCȷc;f|e.ŷY&cdۗ0#WB_ (ޟYj3"7VJņXxWB|!X[-QG۴( D>hiCZlDJXru6/m%cU &$TG''/XHj3 9}ӏ&2QWLD e EQÊ$ZIJM`2)Ht =l4Ѱ\Y>\5xU~ɒBxPVv>:{:=њ)[H?QM2DYE$|1BƒH^I|;H`[xGrq=ZE>[@شel,klMzČ!`KV% ԑbKoPV0XJzkBfZ@$Uz(V|R"my3=~C4;4rrAif~M*X$Hz"@$%.cTpQr1w ^ء#u-rL7-D:)({E\(d%)+)[M7˧`RT6g*Z$vI8՛]gaF"(7ڰ+RK;n-qc}z{Gq=iKvowT7Rl6Cl(jU0bkhuȭ1Ov&,}.@8]$MDݍ=>IBN`>ۼt.Ԥ4>P%5L*L#}:=C`R0 J¤K>%I1o^x=&;~2jp}k2t t\2͕Ⲝٛ@aH!/Gc=8(fӋD !"gR1ӽ6{'-`s5,W3b p5g_\)pɶky oE =??Տ9d}lXXY~oF[R/C-iޢGnJCs'[VXy7ZΒj+^VȔDmx;ɦO3ۿi-1B;E=mJ\٧ijq F$ZK~bkSwQTi;$EaQ2(A鐝Ȫ#8T9сF:VV2gDq32#!48>QYeV(@4[y-gU}[f@g=h$5Zd0DNDNZkf. LLIre,~ ,KeNgt^+Rk s<#C i(LC85}ݍȴ^YdD6WT<*oaI͚Y%X˨AO)Q;wRRwe|ݹt&RU&n8%Ey+pȱv::\BzotǼqg[TjuET:kVTX,Llr _kȁ]x"s&Cv)_7^H̩N:*sYIS .==^@ =].?uH`-s$z5pp{nιb&9xGhcgiV_I=wbѓ4}ثjf մ1#0Zg`K?fc,aZAۭ1(#q̐tkPڿue&zٔAOm8[q]jiy~%TY!?Gf= )x8gQ 鏱.꿎[u F| gfMy2tٷ*YK rMM`z3]>{.21/݆&@0*}ZX ul] oz'(qYkӹXKKVR;àam睬b dr>G8MH&o7bB.A}} e kYGo?VU] /.cVy?A*RQeZ9LTUdֻ\e+yYu<#c_\e)wT1I3WʁzLΰk׺gvshӮTW֩fS v¦' u*9ڊtyވdSd4c2S71v(QКq|zʑ t Pz6 'F\[5ҺݤoޡOAVI޺&ߴ_^av^i-6bƧ!b\i =/F7[hM,͚/9S#Jh3-.j#>*r( ҙvޮւE\H:V\2[ 33%2~׾ȋ ߖ|Hg=K) њ4]ZxV(voHUwNw^Dwj-}TڅFc_{-t[%zE*ě$~/rBfJqg=2EӪ3|dX63Wm*D;]S)4B؄ u]QHs"fd->gF)si֡Ϙ5iZQ3qWԨ,Ƨw m٤2yo|BO\MOwJ&g#O #ôgVZ1WIt^G Ļ.𹰛 68#WĞñʯlRb,e9P<^TBСD / l6輝56ba1|?AI=+#KnXl?_F/o&,!E!+ϔVTgH ^;KfI5D:t ily͆,*|(_}ռY$|7JB~oD?W/72\г;tٝY sεfANZY۝T;)ˋVʽE5E+4O-F͘@^zګ![>|I`=>' 2@Ocq)wƮŠaV&$PL֪_|;O,% F {m !VF!]X͙I)<4!J]cҙ` υo!I`Z4}*@ ks6Ea 1C~Ww,N9P F'*7u0EX;R)R=},Ylh~sUmɪaҐ,|$7Fr4ʼniܗF7Rmo%{b"eceP]]e:MZfNkٍ 1 .LccPA:xѮa;Vbg+taM +|,L|%,o\,>wﹳo9v˨.FwпU K2,|Tүﲍ nC#KIXXPmǻ;ǁzC4rQڤ ]YH3:<~Ye$t`Uـ~[r-NJj1 -&ZL V) \9Dv+(YEc(sO&uk9I(}M1mk=ܔݔ27V#+0Luy;7+uiW=.]fzw њ" Wo{Sv?_\ɚV +sdhWWe!\F`2zfxqm%\FA\F$׎V7kr#B.n՟vmkrJ21;ӭYySNb2"TeaPtIT ϫm9m2JU:ƉW%4ba;^,\8^,\rEXQJlΣI Y%bRVwU'XtK+C/Ū / /rŪKt}iUCm/2ɋ+Eǡo)t?/UYCLx0‹EyU6>7s_G+xL-fG&&\[@1I(((Kex"ZIÏf5p#n厛ڷ:R"7[8Ŏx`8Bpb3& XK5grEv̎ÿ)7b?_8Ŗ/s;|2fp@~:3Mo$^nr zȡяQ :9 !Չ`Lw03t&r&3 #Ɂ  Ook4}u~qm6">w=!iq|=j=;>J]F|^P|GOxωOctF!Ө`b4}A+aO>*rNO/,>q=.d\>[c' >[">v۾ӝ~_`|GnG_=2ŧ_=ǧ~GN *=9iy: yNCd:ÎL#mF7A0L/(`zE *j>fǴ5ka ;Ƽ16iu=7T(sK+{|Χ|{|Q<|9*M-M`]Q6_v95vqLvkT(,?4ââ/DGEq4r%JhDND Gƛ@ uds>'%44kF̵ ?g'2k3X̐(ޅC%xu(8~6[M/ӢM4 Q4k'DzWi= k= WJ6YP7=K91gVCH5IZy2;]DM:}:H畮㕶RW.S 6S*G,]V,q Cɚ~з4Cp)kP۵`TѸڷ1mE4e@5MT7jꖯd FF TN;= .qUW7D BuKh\b)=.?5@q c)-䝭e~33EX.y4ZΫZM6GDJ=(!9.=VkEhAltC?&tN*#ChltN<yCG}GcI#_w92lt$(i t>\. UP kIh4QfܠG@qO};fإ1Qgũ{Jy~sPiSK-KAg:'3JeZ)9 'K^GeRsjCQ{ps|lMjq+ ?8>UOd3h΋3T>:מ0́( I:#O ̱?ěOŀ9 9R60;_]FSA7Frx~x(H)H9ӰMdc \/6]>?e_YQsݛ5Ǝ*j.eǽxsLP!0I%^{iZvΎxz2rߟa$~4ahstl8{?r~fqwQUY*UY &˸EA!D@@ QP_u4VRyK+.ZFq&҅0T@mExϢ4 Us޷U`Z^.{ΩwLGk|TJwDR`vgbf1{M %A&zI]PwJ ek]4MBmBZi).X3pgp9;'5$8ǚd ^_)0P $DAD% 9Lsl͠Ot~&SxZ'Kvz[J~4BُnRt"蠟`KwO`&fKM l٢=CePO>(1+znFI(P AHkY'"?mц4dBk3֜,6cƱSeMcK)uxL?_ FAseY \aXsN'A5cI[} êqR鈀8oe 8OR\xs聟Ȋw`V]F] *B!k޾fo^fo=ꤥ&̹L5ҩ ,l䑀Poā/LQ7 IUz9%ߵ_w*o@ӓʂ `_Z*ޥ*7ZQ%&\@rUfˣ(@P IZH?0eaizr4(OO/KIliY{i^PRQ6_% @'spK̂ى/zAhsջ)VkEh )1GLA?ioCpޖ&;@9ɥ^ vi3ڤ _wu;>ğى 8 `BP (-Qh;0E5'=K{7]Zz )|}ȅKW)tK]pe8q׹uٵFZA9emoz[y[1K _{v5Y:E SH:1J;5 lta3)"a{ ŠzH݆(!:z% sv풬+Q$FW NՇ2Eq2XUS;@Tiyi2]D=i8E-7 uz'UO\&AUy#Q㎵eecp?\ >Y @jN!xP=s+?Oys.cǧݤS9 :vJngb[A]i:󁣨M jsU6aQXGm.3/QNZOX;H7u-}oYp4aC'KSg(1wǸnYS]6;Pgb Ggߗe4{ҲJvӈۜ*T30G/I> r EK,Md%9^L59sQ9l mCBɅ VJ/dymV҉猟{M^<cf 33?hקWs)3l_ ]H]iC٩oס|~Nmb 'T2PRNg2 c&!em EP*F&~B4wgQnfX jvމ>ya_ 6Oegt& .\|ϜlܕKѡF9E3&VfPm:XG{B1}QҦ|g1ui ghT8b)3K)!{!f J1We3tX3K, G Wd! %.D"%7ceNgp%,ѹr\? oo:t)Xh 6'|א)˃ƕ{W\ӈ㮒PzkK(T|PGE/~;Tc:蒜(V|ڮS຾%Yܔ?9)'@^潇eewv-[ukV55<Ψ3=.Li?AP_)_KmGdnz:r7ꝷ'=mg7~7frf;GzL{G}/`̞=$5X1fZknwu4|'y.w5o胴4ﱆ;9 p\vn!FA+|lNU17wMNp I-t>jndthX\W]= u V]GZpﱻX\ Qt+.,.om.p yn89\tzpfW.g R@(cr3M-=]Hpߒ V.gR-WJĵ<]K.pguz.~\XC~ ޚ M.d8rNwoa?AlG]/hH:?x?`q| `ZSO$wЇL8g[w] p{~F)hTJhvr{DC!/׉ w'j0蟌eWvSI/_l-.^m[e=DRKv7jOr}V;Ieq}l !oW;zǤ V\oԙV͒Ɠ]߸"\P8ܮ'(l-Q<)& +'йr|Vwަ}Wg>^pBD0.KjFJO*%In 02\`M`r`ߵm9+pOh~MZ;3=i3[ uc}{ymSji.wXܵ>>9[Ԉ_x9 p:DUz]8!Ȱ46;J;I`,L>V^Eo4h+:2*=ǎ9@xdEftF ^3񮹑稣0(Q?Jǝ!AyC5r;zg*npJiZK_)j&qC{ksY(sݠ^ #u>_Sk-O5#O5͓o5yb%Es䦟{:.rϕmSㆾ!H8}!Q"HӍ7cep('x`!_jM|x5 dT*gr&) lD)K/kÂ,[C聗ʲ$p:g0}Kfo΄FRڧ1,<\އE5OzGHHl  (+IAR`&@{ *BŜZ4x#%R:h;~rR[bFywR98>zPA:Tg$A 엸3A,'NJCY:hܨc$"KGրOO Y3> f8/8' ̨Nz]*@7WPEEfj51p W:,9,yq7İS!%3Z0ꆃRrFhݮ9mջ˰hXE80D.Y) 7 cpahkL֟iKrf4d}'B|9CDu"4RsS Ofړ&13K!EDz9y@< VO:R_6!T"fSG}h"}s\)Oٖ r|3?xna;cGql~m"e}5Mlc?߯lmrIoUKS,ByǴ}YԺ ~Z t/᳾>ؓr8 eu΁8C )хZwibtƹs~c=M :5ib`>=ĸ!qkS5K9io fFQ^EF{gfaъe0 zv#|M3 Gl]UuWm>bozHZ{ZZMdoAZUFϢ uȎhimO\?ӳe!eF1sWO۹r&MNJS9xVn י4zw{)\i4=ro]Kۋ SioĶ}<0D+!njUؤxr(7EF^-I+V > I$*>w$'x[_zɛ!k|O'aSG GlvZ(a #[Ե=K[(u-mtԵ&4Pll)pGDڱfyFxJm{{J]kE@OڸdʹY12:},mq}kxr/d-3[ }` L%<;ol&KZJ,yj2YRa%o%_jhc2o!i!@؜%,eQ g ^Vͮd~u&d]i%X3) u9,4e@}R%,@a)e%r>}X>ocM`:m!! A \ D(j$LETA]E2dL^maVoG6y] 8i(@}[ 68]x|!UP]>9XxdtwlkS}(d4զjv$jTT(BSkޝQ˅pR$ڠ/ DDȧM!Ot)!3|^T WPGR:Ce2KnBiKEpySq3> |]$1xo ?<,kf%>L([M%y`_7{y[}Xz1vؒY_}!kx_MhXM7Z1D"/,l&)}ZN ')X/" uNx q%Q*/$FG0Cp!2HLqG,탕6kt,y+~ߕp2}]se:"DA ߭ޙe\ZH=3i\ڃaxag]]M_TG_#G@W{m)M]BD&Fvk> &-hĥ36Xyq3 ƔȮ~PYX ύPUؘRPz(ҴCKiT*L`{6 "=0J Jqvj 45LG\5!=E˼sY|-KJ)>w%j7Aisౘl\r~jen͌PM#x#_T.6c)?AQ7lɿ}hnyFyL2"EBh*.ԼmUjgkFt}h2Bޔ m 7`'mGn;zO`rXY}HO7 4hyf uŊ&!e[踍kl!]L;ËcwwzE+ ++ھW+KX]$mTPR5\S>od_bUHREQ=#bmV: 踃k6` /w( 0=Y6P r9?m'(|¤Kqw٩hʍ rvϝ9 qw/*km1@iM(OKtMYhYa9dVܐ݀z\j~!I.0+[=R͘}rf߁mdV&c"l^ ZH\-Ք&3Ge86s͆6vJk%.۽lo6㜂Z_Xb5bcI 41c6iWHYz7^V^;#?MF#0'+c Q B<ˆ[4ۮ'(+.IEwfM'VGkm#tSU7b0DLE;Jͷ%M_BΝ$.(>QSȁ͕acx4cޗ+Zۿ@;կP`a=ihp7VQFdF3 j4#sRd] ݆0""FNDbDtzZ>¬bGCBs@w.IS,#}K`3PY6H ÁX;Q##@'[\ЕK>Kq qdk1vOg7|RwSbvJl+"bb Me3{&I{τ>֚,snfl{@zT$ar%^݅tf1||tǪ*4A]w3yp`V|oi![EMG8ZEYydErh4oП2Ƀ+Y71y"aj//N .e [4%SnVF]† :D>6;#sO7R9P\Rs ABP\,NXh0IJ;XeТT(]v"Z[dWYsdbo,& Ej\:XUB!|r[|P.bfH#A k◛y}uڬcjh*+۔%f^IKbφ*qGYb83H_+ٲڣ-fYߝbR1~ Jl3XapcA'ɍ˭n,5wct\*Ƃ'qc!wcImD֛\Y|,̥I!s[[byVW,Ns .\|fKXԆM//O>h;?ځ 56lm \(EqKWcm}h};@H5$P@פvXC:ᬂXXtk81kc-űp=-njSZw5cuQII!kbQV_X8p 53:o;)ci.|mBʳuJ :% ;ȭ Z5^5 @#.2c`8ί_prssW33]aÄcuRvY&LO] t}([z\A+@ G=8~~D#!~2^DMNudFUEf8(fC6f.'L;&j7#W0}]>_ptl~:jtZfʜ -FSDʆ$UDY1&)0T1'Z= y>Z͵~!A*7E cc_Yc^>wZa":l|g#bܠ[Ԯb$նSRO]uvmr\me1P@Gl.'EjPHiyӾ{fi{:e־($fW/]%!rS%_S¶Rm8JMt<~h (5 _N5}8[Q X( 6ͽ9S̽˸" {47ϑiK]^I7WrA4 O}?Y:U!%r*9<g(]Z$ʎKLŎ]tR]C2sN{hx=y8$lc_A#1I>Ue.Pgc_O_~qu ɞW6,g Z9:}؏Uwic%Ead9 }s&}?ܵW9^Rq gͫƋՎedüC2x90֝ww;JLlh qu(kfxcG=(w̼r@+#+`>O]T^%[ZHg\0$]y}yUI*Bm%?|!,*J"BzBUq]-U*!y>TS+{%.iTm<*NCk\T\pʼn .tpWOqGv,+l<xXcEd]#;BYڢ6Vp KJxovH_`Ctpƅi~]oXPM|~Og$ʲԻҫ)/X"6eb[>[$yc& H6D;\\.]PX;_DØ\)sZ;r!yT !ߕZVX,~$նpyhw2wS7 Tw4 n_:r_Ả10&pЃ@+KQ&OO|KJ&x+<-K(ꝇ9M4>@…5|}D2j&}x[}2It o- ;y*_m<$@esRa ћl,7oaYKi Jk3<r|TD zP512 QB 6jpƛZƓA½%m*UR x @i;iI`BϜ3yfrXW?i+ňƷ, XIoP RǵMmS7t\Dz|#* VqEPZy-grfٔoyyҚ(6J^F]yS4+d5f0񒶀bj_Ʃ(>$ }~X)T_<ЏOn,ɍ7WT"zŞ6;U(^s.BObFIWi=@$9? (~$GRHrfd?hO&2jJMهlчC.c5Ȟ$Vstr.8Ɨ`p}k|]ϓjR/ܴ܆i)zѫèA8wA7^"h#6贈Iv)ȅ)ߍ4cТh v ̭hi+F@wx׈F0_BW@ U@I@! !U2.]-aY} ~`m_(P]hM(U o}$,;60rᘛE*4F7f^N1w>ҜCۆ4x5}  ڀnLW (x"W 3͆GpM $[A$lC$!CADyQt!B!HZuA$zHrdP~w_o?WG)Jg?䂈&&}'~$^Y vpn 7Q7RhCem=-t7m[4h κ(7 h SOwm*/M}?l m?-8_n0g m!2oک6ڲb ЖA 'A__Mx=aS>ѦgN:zok{gh Ph mmVݶґEt]|a)Жv8@&حINc+.?܅\δjc`+Y 3WR%vps%Uyg+w* Z"2la@f,| 軓s2Xi-ۇ(΀J N'+'=as=T0ˁDʮ90X5'oF Z"J)uu KʹaW|6 6SSEų :!m%KVZPf+C5Fz%U^MTBeIs24A@#6^𤊧wO!// r=!281ǐ/e:J$`R o.G[uAiF Yr"#ʪz3TX.8U7\o8( e WVՅ~-GMi`L:2 4RFkXB$OWSWmI A]>ۇ́7Y$}fz'whЯ>rdD:pE&&LY:EFXrD1ne$o={QdyiœD ֤ ɨ^/IWΪ.u8EneJ~n&zy:E275x3l˥RȀw"Hh%hFb8}KjiW|:ucgQCV9ʚx_1>ߍ`^J|PYwd+!R]7;!gmIxL: G=iTtYLm9hy J柴F X3222ز!@H>G\wK1ci;N3¥#UjeQfu<\,[0rQI"aOd#am{7]Ľ4@Td #-*b5 82]|FQԬrN_QHA l\``i lLTw T\@B ؄ҭ leŁgg;ȥ~y93 TΩYe@(vR#!U^ V`& //IF=/oҾskY,Su["dmgpCJE\R ~CzqJ LLNhҾ*e'.:uo9/jfFjgxI T}gC{/{"U@[@i_:ŀt3~{rǔ0\7Ȟ*n~~ ¶ܚP6'6w l/$ Tez6e4.G2ga}f)n&UB!lhHSJHaO1VO֐l¶Ȇ†¹I>! n'XaÁywPaC x9-ԼZ%t!H߫pEˊX#1쮙g gGRQ3g*1,0quhX4YklYxWA 1K(%"C#_K%Uu;'KXK)_<?E>e)i eLf;.;OkL/@ܾ$e/wf/*0ɋмm nI5}H[$0SetΔvEm59JCY%њ(żƓ#0|5%jj;UҌTjIC,7^Kh ; )?&;HiZO-x;dT(*nwx 0O'&7ɱBi 0333g~YhW_rw:|ܾ{Ι  `ߩBa0 !l⢚^&j.O6*ae?hwwG'{ErdҞOwso[;/ܡp3 h%"T c+m'E@a?8gaow? }n*!B~?N`BbĿ]4Q-nDŽoLjUQ Z!/g'[S;"=?k?vSJEsjk;lL4*}r1wM>iK 0)TPlA 4Ru BBRk+ 3-*tsq*>\we~hV>S@(4@sޙIg}~63~;wμ3ohCujy/ML]O.G?c?mE(%2+|鉹ںt^MLn\lLHgaS(~S$jp;H%+]-rz0jK'h* tk4 Nc*0yR|{J0~.M\?₾n|>:J Jŧv|2Ώs7i>'?Kxؐ)[ГLP0{Z%] tcPN у?BH&J0oKp(h d3 )ra*~v_h^Uْ J%b=[ _pކn]QԘ{RݨЩzPsHe|Z֍C ?oD|> Q@]Ĵ> LɼCYS4SRT 9{hx(ހ;+jR}!Fp&ӄZ㔬5) +jY7s?YNbQZWTWPP*]s撧m4OFz: z'<50buͺAY>`\if +-HvOMlJ/@${+hNn1Rp$B/b(xf OE;G8XiE\ H@8M,)5yDn2ԩ Н%\údBFpd|p>zS| 0L!:Ine&k~-쵮!ό!܏PG uK4-FQa=~;ۃ23{M+O{gr^Oo1?(qU+&n^K'R]q(yZOZ|Lco698>8%hE (UC[Sf=rB)l@Osul$==MBbg[ldSHqhiv%3ϓ#?I 1޷_K5WtßzL2 X= pۢv,y^Sc& vQ=LJ 4A!ބ!`2`<`xzG*Vt+T6GwqORЊr=F_G7OM2ȄaR IK03:ah3!8pFC&Jg0,0l&aZaR8`lQXo0JGR$03á䛆[.r dh>߽;2Htú&f,ȇ ~DkWi?&|YL͐{-0q7\sɌ4.d-4fTW>@-DrdbhÛ+S`蘶KDSImN(>%GU'R1hʇ?՝ikRƍk]j5|yw>li|8"8S Ug^ UKQA3Pd^Ƒ+62 Ħ]ۓX uE7[V p_^Оƍqb]AROfF(2"Pd/ݒ9AhF[q`b1V cCń͝1e߃% >F5b9^ңq3ͱ3Q٪z`  Ik;'Gvk PυXGU[hQX0GGӾH;G|tk$-d6l` 'q9W w|@y5M6+0Vx!c>ؖ7U*!5x!95H`MC45hTRD3y4favgmN(;Y9;w(=(iA6=8Ӄhq0jz AY9>Ew~P@<i]~W%!<~_j~ Aw0%[3]$H,ZRlTFFn#'fe]Kt} r!TEӣ{jPuMɇ O}ufW3-NTg:j5ԲOWgnn( L#HyXg5[ rQQ,I8ٽ7fPB(bg ?ME~j݃&Y'48!I M#'k UZ锟3lw);Jo<ȆFڦP1J0TBpFiVͷk̂*7tGaWZ{8 FD/]]ȢvM]FǮu$]#=]H_EWpoP+Sƶx$ǐ|zC#ɢo[>#߭VO3Un!%IO7'Sk`hPg /8:XG M0ؠ|7+Ϗc1-xAGu-0< Aś],M5hwcq3ŤmGPˣ z24ȁTIo4DӁqYχhHN([**|U<׈444S,+5[YHY9H]y6ߐ,)$K )3`Eݞ`LK8rl5%cd$wu9~mی¤KlZyy]cF͠X]X~ZHLUk_3hViy9XݫueI!dn5I$NXF}1D=)`@-1?OVӧDc6ϓ8(TF?$+xdG$~_1Y%{ (f6.a|d Ow2W]}sP`E^}V˒+2Ng)xEl6ab%*_/ t\yQh/!3Y 9< ,/M8?L1#Ŋp9r[l|)^vmrRd9L}q9k}u¦ŝ]/ N rSYTv/$iM7R f~)IL|& 9Pi_?c}e|qfZqB6>9lRd.yxX9X_Pf滌̆'1 Je6Y(䙅d|\h̨eHbJ4#q@gW mr!.l6)/pyL5ݾ:>kR:CdPd!جYJL5Y`% 2B\[plB>]dsN,;yE"g_}P WB+V`-َ"Ћl Q<ޅZ$Łbi:O[!$'*P>~ 4 /0܂.ӗqa $ߞݺƌRӛ@'k0 u[0u7 sĹxJCl~RAg~[o)Of5_C1z7і_ mJCGK(ML ZJr I{'r? _f\J_CLw8!d!/f($$BW51Ο- *j p .;z#Mg.cެF́_| uY㋣*M,j3dPH3U5Lsp20n,KeB#Vk kxe+0ի bvCl'ζ+q"` GaL+{9޾gN)8lj7zDqN//$@Ͼ[b=VVA F OU#'vy2uf֤|ߨ_h& )Rl؟BO۲"j[t <)aRibކ'uf,&ڶBzkۓeP~HW۩Nt5d_h>>0u 0|R?0LT+6 '7Sc*"'5R |cCk5|BWjFR=wuJJfd*:Ċ+fD$Iep_ C7>'>N٣/G PncnEfGJ'I&gdf" 5, 61Y"ԂnWZQf TI'mom/}[m @DXuɆ,9nvCI2ss=uOiߥ욣].Xa{/I3B@_Sr m=J+GAwIכރ7ieiSҶ܆儂".wa I~@*j@lZ1wkNBBHt\.'ތ 3F?ϐ7s: }3u9C iQ|UH*x!y҇2]rU&&4.JJʎƣoFT6蚳w&x ƉAU]_RwU[_Eミj!\5peNKz_x%E8 ҍPmbݟ N󷁱X";w$ ='J|j`*%tygv0E oNv=-r*f`mrdّuH[٢$\>+x z!h,rƎ*5,DMHܱBfB!.GL3x+x"Gg# ѝO.h6Gm,m;e(^7@A# >B*o%R'o*vȿMiT9kҧQRR%?hfe|2oQ9VPZ-w_<ײh8 AڴY-+ ܐ-I4bA-| b- ]">@"M3$t} v)<\Rįךi B`) _3l$‡V|9 )Fe;҄K)Yoh1aJxOu,70=!(/(pWۼ7X8fJV(L#X̥-뇭2 */MqMcP?KM1.zF 暼L9m_FbS0+i߆M?;/P |g2W;@Z>k*-sRj #dR^5^k`r)m;3i7/;^-&?{07ApR-4l-hW<m<;(9u@v Od"T,USKnZH;wډd']Zk7YpT۵Uװ5/@&B@Ɵ`Gk!PFNR._VTGi9Y񫫨hWo7WnR&\и8Zd%A.< M^J;:m3vmg ֵ$= 9(G`+rBnClD'CU(NqdOhP06`;4ʓK.f'-+F"3MiJ u]a̜Փ %$ WCkK&|3I/[[[º@.ӄR o QO@JQl+v$!hֶ*#ZNn4i2Low$SxRPscY[-[S4N"xZ u/(8I;,]Ov7HGOXXk"yϵ"0g# }hC qyFyJ,ߢe&pd훆ھkӸ1; qa-у (7);1\N[?j>m||NhHRWhmk,L̥oO7 TZ"((jN<p֯QN{l(RC8:1!~LP+zKrL)Eu_>bgC4L!=*2`%οAsӴ$tP ȫxGCZw8 as<]1 UρaVv(V@]Qv%oliMQ8A~)"wȋie|JUUuu9DnN#*4G'{7PBs W*\p*:iY?ؐfn\{ˮ BI,CjS6vp5npZÊf5qM=kFqA] x%IH 1D_o'XK[D{/{R}t0QR۬Ȭ>9Q>tP:s6hCź5 8Jn5ق;.U_٢s/.}I ·mfE`cVD>D:,MWp #wUIYתY7U*&FM@Xfd.'uR#Vٕ&5.Ғ;|N_ҽ5r'\ "*' `F~:=yv~EIPDNyx N 0#,IFc)QGݵ2Ri.i@b7)g+NȷzM"Kn/%/c,{v 8՝Vc mMZ\ˡ.%݈$ju=WeS1N`Uw]0vIU@_CCĺ_#N8EQ<n5YߚO>&K!gРONZkXH|2Yַѕ +:\;fHXd,su:'a@W $UTg⬖l.u9G)r *Ń{r@@Z$ {!s,`d@X#͠f:fd7b]XK :x7wCX%Kٵ 1]XMZNx4 \сpXw\8e}c0H`#emKصYminԊNtMW@*ХΌCh=%a!?G瀡Rыؼr7qP/>]uvVpgx@-Czc[JL7Q=+FZa? 21¼*`ߵ1̼rPVbd>]-gRdHwMPo4v#`FGTn }A^{6u5MJ3iY[E>)MV蚏ȯ>u&ypMQ0̖rHRo{3wIgrYzdDOAI=tj2ڹK&J 25Jwl-v9i%t9R cpD? Ne@O䯌8Lǹd}06WSzNx+/A.kS{<\\# 26X' 0Gxq/pP7uTsXd 5y>\Jݾ-2q>KoQLAQɇϽ2vl\>~ZoJ*ê}J+F^TBpo".QT!O!}sy_u0 a^\ߚo=[X-CvV Mx(eU$0GdY.|dy6$  40$ߥl 6[Nh ~Y-VqTCٻL y86e+*0œ4!ڧZYl:Ek!ka4+:-`/-Y|^ ,nNoFHҖdY63(N,~q5G+KKR (CtZAt79XN{ʴ`mZrkJAQYb̷yZAe~T..g?.H΢54FFܬPCr\Ya%r'r\^]1a<(j\a1{t}$ZT[#F;+ h=goMC77567/=r8! rran(0)Z;Fd,p&& 5;ê-mtY0[nYqH<1͓Ps,oM4G} w@ҝ %+qҙP!VP]Suvΰ)*ڭg;BvG[4E@Fd4\\4-eFԊx풁%&oY{;mF;oU SfWMr\6n)L+B qrd33t$]â]e{vrCA :Kbh٭+NBiRNCnW.4}&jvk ڭَnkk!om@aGGRk(L LQDTP~ Qgmۀƴ@R BR A*)8Y[-Bji/$DS uu'W!%D[ hwxjH~Mu[3xiG,i2sg Pf4Y;F50bΖ-ٽZU[ƃhBSJ#nPa# >O@ x;H߰V[ym>]o&xoU6-&2x >(Ҟx&MOcw3~ G>=t{%z[=*9 nꜺ.yò%i2^( [iUV %\6"oNJz?ifZk#+?اT)~!e.2Zh8lM3 Y1s RN+SU_}Y𬎛Eρr1VݒArkFEsĕL]36!6Xtebg}b+Lb!xJLn\ejqj>^]gY ݳ iN5)sju=|^< Q-: h]a*+ĻM`ҍfR")_xDvc\, x,8./i,Qd+^NangNq ej5'fbCi(vZs9Os9Kp ]FFpڐ1弦ܶaN(9nwL|[pxvvtwP?pܻ߾!r^ISI!4: 5q&+3"׹aV#8җ (}f-:e&0><|$WA*'MK dTfy8 ,bdfnNpa~U,͟ͷ2h\EFDq.MvLXmW  ÑIY"FEBpUaâ1&=I.C EŢR]V.]c6Â$ &fR2DTE<>SnF"I^ ^0Tp{wJ ٭LւroP5.Ă`{IoÂ0oo^1Bq=T=vFƞ0 0_ՖW1#p4o5.YLBXɤ]ɛvݠ,&MUB $8|õC.\t'I)^39^ ph-*2,{-([t$@2Xzn]Ϙr]Ș W)a{/ TIrRRa^I\͠bFٓn>OВ:A2*բdn!"sVq2~UN{_ݧw;?5aG"efG޳jP V5"U/YthdZyA~om#_ݜ|g X{%`HU\5 lF"+ij/7ɉ1: xdwP$NޖGJ)B T$Cjׄ˰ɩ0%۫x'Õ,)#~7}skǛ {%dٶYgSj7Q}$)8" ٍ4?'E!<ӛ315S|0Kx"X @Yï0۶&*lM(l;Bk6`L/xȞư^mLz~J8䢁ӓqxb u*ϗi5T O@k"6]KˍsEySPNnAුz- v7IrOBEyȗ(WvԳnU;XIG)yc-xt/y->UD2 6ڂhiyMp%9:ih.f#/c싲Z`nR+!;ʳ[@WTLcP[ybop`u藦#2n#) MK2M?"ٳAa56[ alGԠx 8Ƚ1Hg\ůC2fnXtuxx8i4\ƭef)b+sj(*ny;i^< _SVrXo۵B<~Rt9$o2ôK\ QHCC < 3h]oQk&O?C%x0o泚#4P`_ 7N*b X`$M5mphs 2Op" >]&@(;w㡫_v*1bd'JY? 'P6I<--K 2`:;1TZFh{Pi{bm7fu:'z <4{^ə &%j^mY ڷ0zE܌h$.69C͜mt%"5y3gj>li\W/@vV){ŔNE.-d'fzXGPo CۨSxjeo/xdaw. qp0X "t뭤Tu-u{/SuQkZI TᕏݦmSt8N^;❍4Ǔ\1E.PG7+N\[2BWyl=Җ.e'JΖxr(3`YPGQ!Fd%SD+2j^džs;%!gW[l>"9\G#aON'RÌZ 5ZpFͫ8\9Λ&!6hS|3fm l vEk xp29΅?ȡ^Ykim.Y\],t48JN [y{7JJw3xKd{GYT*R}xXn_%;j\dLfpׁ,;jC&q/J ~ lWCNCQwAЬS@;zG} ecwCmvwQ7"`w߷Fê#Hm 6 <О.s>B" њv{2P>lx ꩬ.4bɲg!}].i*eDEC^W\֍<;'Uzado"|3$;*V4P?cn{}8Kӵ9Iȳe_"gzWEvgwD=OE~_!{/1Lo3-$ ~?%4g>"HJJY%{hx!Zދx~ώ~~/ j7>n2 AnxR}m0vin[#p@]jHt?#]aF̈x5yx?R4stPpV"_u|Eź`ISqz<3@jp@vopQIs\QB X 6+SG;:e~QR !ĔXWjB+(8HVxQRMf,Ry*mvG@҃UqT쨹FNg)}c Y1*aU`/PJװǏUQp1 & >m \jsLd_Dw#۫wUfEOiڌ:;8 ?|pQ@J?3>=kk;Ws*Wk;V^Hr92j%n"D(|'LJw*m yt`$[,+[eHd~V'/ ɲ˰648ZLLJ]%y ]:н./r! ھ4/Gu_6-!w}ϊa<ʩP̨3Rh ͍ǒyP̚2ktޝ.2ˡU.$f@-c:F{TP_^$5Q@׵\^/~ FC߷Do<}]J JNw)J_wsBwH}1 m .E e ѭ[({hM=X%$v#Ϧ,z) r\ؐ}b\;UAԢ)EE=:6C22SYG YԠ$ 7!m!JJ<Xf B\7ф,MP)񱬨^2`tx~j'PcX)0F,Yꤐs" X+?fV+ty 5!gA OxOҢ͝ԖG˵5X<c$q'ߨ-)ٛ|b8W]+{IBk^>Ȏ-k-W<3ƏByVĜ̉.|cqXt#KL gu "jeȊ?Z)%p;. )2 $LŢX+zarY0=&OM2eqȵ'tŹ_W(RT \Oa[ ?OiR]qn!*L $D+CXwA{؇!bm!W)n0]@J U&Yz yT'0xdZMjq2J -2SsHDžXBJ M\;^ZurrX\"S+UwVwaV|AkgLވ'UhM3vP sg>7/H1 M-ܯFfol$ffC" {3]/hG-F@N1kMqu}Ww.$P<py1f;{t*m!s5@ɘ_9."#‡ "<֣ pNEbwp?\c7+,ʒ LēUy)` n gS4a:M&& <Wm<է&;1k!Ad=#߆Bhwk0&'fQ88 gE; V4D竪cnIEdҨ(-Lxߓƒ =:Er 䏧 2M 7("z-2q{lQ@ܮT_ङ6'!,#L3a@Z400'3ڪT@JIR=Z<B,@ wY1ҌI'@mD59-Ī}nqU'4LOg |58J8N(CeJ@o@|=bciO1=§S>6Ӓ3Zx (Qs,u({<ڸYlQ f<s.r(_iDf~ɝIq|g|M:opT#xFUM| BxWфߊHbv;<@&36;g.^,T_íh8b\;5˸@|J`'Áwh`#f=xxQD]ǧ]U )uz< x |poɣ"GKTkrj`2j݆*DcE3dݔj*#LﻓO.CO+)CXC!ΈXjXɞ>*?!xg|VqqX'q7fh+0iIJ>{nraL(MbriCvp}vY:s8l6@I:S5RXJcB#Ia;o H Xc܀Æ~ sww~PeCV >%:hT5~#?U8XI#| Yߘh>@B6E8&Lfjc|Kj=_= `(Xg X5 Y:i$d񿁵qijXc0;'3gd̻Ci͐MiNvR7RwDd|>:crΘ\β':s1f`q)#]-`h.x#2i[[(3cZ)ԇR7<1S__SഏܭU`TJVJD_ݙkxz_OtWsq8"ݗ"rثB 6,m)Pn2$9%3ps.G ԒAgvR]rpjB< K}zZu13Dj',cC^A`g Ҷ8. 2 ӈulvE Sm?. vh{yฮCї$SONBANߑw7t_D! ( /<)͎O@`1>"p^# Be 0@׃HPm?`NGa2tvQ"n| AzjZIy<֒_:x@GO$}  d'ODłjdE+s Ml21Z0:? u4<o?l 308sU39Bx<%<,=ן0OO)ǚR1ur{] d@i0[OR*D|hdSP:UiʓoR$pѻ$Mƨd%'1ve8iti j|G\dy<B^R5GaُKVBa6& Mgjur_ʡNZݱ^k 1Mq|'^dLTߠO]b tLa(hs' Nˁ!\,ySșFR@λ}vCσP0\pun4 yqabYH9H?6 !%O&X;<X!E8J?Qj~xqR{Pn7"{/^WGV>McfHW"{S>ہycvoiuDlr:@u$5+'U !,*CqB bA^6oB]2eޏiNwCqCFyDpBM_<~Ģ&/S᥀hwc}9X4\Gk6_/ uTX4 1iJoz!_lv=nJR.cGOȶ><ϻ:z]"[> aSKgaX*ZQ T֪Hs!r+rýy!<~ϙdMԱ9fxch+x9@~ěH^M`uf3^X$؍#}_'N ]w7 'Ⱥ@J; f§|jçtbq i?Ї5bOzAEſ4(^2(_]JhIr6=1 |ϤNti0hS,'aαJh<{ϳI|"R| 9=*jX |%%d5:.I Oj(G0z*w`- +tWnZB]J;KA%4"~b[}2S2a`e6f6*'ͦSfөz6!M h(:HMބ 9ѷkŒmf΃['~($M.␬b~F9A7agR[GB0.2y6&] |_sP;{m4ϝF42{bHx17i~X٢[ψMEcߜ5UT?7қL fs"qj$Ĭn*pY!Y} tcd'4S"9TIV>ĦZ0<ˠ׳ p1k.ZFn6*PZV,Yb}r:%X͌|$e$L(4Pg pWSW+֙_.+mʦkJjHֈVT2`5#y ߢK#k_@^0Fh%&z`^ԍ4njkA/}vCQ>%_[(\+uY_)4J=T㚨 *Jo ٯUENk;BB,RnS;d֛ kl\z7w`8=&D+ؔ\M䠉5 80$чBf-DlMO;*qe;32XOM_Ύ>ۮ1Q'p>(: $H# 1Crm0 6%Z _ wx<< q_\n us^*f@9W/rR6uZ~ղ`Aa5.>e9ٯ{g/!?WLaGz$.Op3(vT4#ڃtJ {-lk A~SfBGu䓃 `Hu56VNcpxmnà -<FEP^OPa[L.t2a.,3cA=Kǂ@^LėiOښz7CM6oyJp)@x-Bhc'nI{>@A8~oǽ/}in< ~ji(NWi?Hɇ"O hzwt_ǻSWhTx?ſ$}gzKvv"<<`xY)jau]ThNk9CrbrWF^ Է%0ϯO)jZEgRɂ<>jXwX~/P"!p$bɿT=H7KYv呩˥Mf3|.*j%Qtڤn}t5WutȞod;dgqYuv!-S+m<;-fWp6rƕ `<ڥ(:"'_$O#:hW`D ~DOK2iBu|| {d*%e,5M/ZSvZ (׉垷u=ދvuKy(?6C#|X"i-wkm46r@^d,30&gK~~*m3%&vk2Hbct3.QeD͞g'7D)ݓtjY̏'R1JOQwEk YjU?+p #o5Z?iS:GDF;bk*y ~6!Op)y&v{t ާδRygY*+y`s{"k'Ȏ'X^B;д&qъ ]\O'_Ě",pj0ENC -J7C;q:m4fixږӢ o4kH;ҺzBô^ ju\/>nAUWWq YxI@ԊKz{ظ"/A7+}[U:x1"*qk=6&X3 c}ocup$Nj=5AkC@l}h TO)3v4VIg5ڒX/S1 4skpV&d,9p IQ썏Չ'Ii:6ݢ4nOP 90nGjYVVB8Ui9dr­f»} /`[\2Q;r*_v%)t@``y{sC+xT0[#;lR}+ʐI}2[AJc?]c:[+W3wua P3L8Ei+i [D}PZu{ƻUwwź -xr{vQ@c=< gTLW#`\,}%ߪ4XlHs1+@v4R†%+E=ifsbFZd!=\&+[$bcVwl?UV @XNJqQ:ra;|lnXkӚ@s]g ]jJ\Z? 4~*[m`BI4R?ƽjY~ybHƀſZtddXēLŷԠ/X2 z\(0jZ̫9<j!l.W_9nhkN_X6T`p~{rKК0ElwK@MhLԎ p0(e6UuZʰ̄s7$jA$[ +0pLen._iIw34~|!"oT|],Tk13Y=h Kί9T*j%GB Av^ Be, d^3dTErvKU1Gɺ*jVQ!}0*c3%Fw ]G)dڹ.6P_f҉T@v"ĤWGav­@q"+ƐxL r Ju]je'-mUWJ̳`? W=Ly҅ч3\7fYmS~kG88Akl JhN5:VsJU|/iEN\#mUaѷGGG~.~y}|E0<_L$=@ _``LJpB` ~@#I7J 3N3Uw׻^vޫ^A\WL%D@4jF `B=g2.{ǟLNU:9NU؜'v[ͫPvќχLՅ.3rd/Y{@vY,Rx37Exm~~M.A&l?D$>$Y@fN w4GO7)1}&E=o5++xns\(605˿TT:t%ig b"XKe5(Q FZZnQdR4Kn WPw4ᴔXkfYP_oOy'%NU;kȟ`TZ!m>`oJr%!;j i{%=R*> ~qXoqZT !bqh^X3)[OO)pglGQF6pε:|Jsx-J K:a~ً:phx.͜+ `AL Qñڑ@ 1 U >%0CpW+zP&+6a@A@s;ʿC:ۙmfw4Jva5o@ ISmn|UAq1tPYm[cĺHpIۻT9ャ\Ü-W0o}F"`3 s ئfĚޣR_j*>/6tM5rRp8#2Z[6)Q>;.jc%~gD nzrβ%DVXvisa;HVH w%dqx82'}KE# /a+5,xUw]خ.:@)'$R+80jFoG 'rIv_;+6$xj)#a<_%IsRXX,PrV2&IvRH$sAɃe z'ĝ6I/ϳ)gm`O fjy9,LKA-ۃ7)_)E u!azS+F}0o%!,Q,v\dmwwX{FV^D0=OMz9!٨2n2Rm3kXq#z9(! Luܝ&z,/Lң&P\5.Y|wX$Ȑ|֮bf t jҁ]#Y_9g>:qMFNX"S$oFaݍ"6| S\IT W hlIi&-RڿvQe>|둷Oз4r!`Q:`;D ]q^>JhSfIns4=`yBy˪gpS+yK'ًڄ\ѕ# P,;$X@/LsTVQViaaw'qNnQ`0B:}An i<^DX=!9{T{Q+ јv Ǔ1,0/,bf/7-T4m];׵ZKc!+tth~C$ӂzcSYF[)Т4wMBx*LN9FR'q;& *^FޙY (U%H 1g @L` Dd1 L3&#w-m<>9GjǻhT1q $0yȯƅdc0b\0xfvJ*Wh;Y X:&_;1 _?␌<ш]),FI9Tv!'/Li1|Yb|Gr3j8~6 =pDp,#d9{M^4>\zbrw/DB. Aܞzτܑ+r $.g9% - Gml"J(G!$_ K[q Z ;5MYk7˛R&" "gȖvDژ+4f&6fMYYub]걄`WS.`<˿79$mwAnPa{sl߀[k[9z1 ZtV$;Kl`qǞ#՞׿e$l X{Bn4ѵyjs2Q ^꽪W1zHCMi σ؝6*)Gga JT.79;q{8 Toy^;[N 6vʪ|{V<vvK'đF %]آd&u1Cefqy(ro祁zy~U1ОL+ʰUvzuѸΥ:R ozP8ڄwmvgA\r>*"+zc`X҈tU,h~R"^@o<\4h޼,7)t1\'֙7OA2.`͠^PQBY$%bE D2"X5,\|_Azөl92V'd>yo(2`6zlfy{$m2dƨ}sAW3h+%򚋼Blʞeyڔa'6vExg`m7Uгpb$PN5qd>E@B0dN7U\Kr=vEX< =Ji 6(ɤ$ JN_C(D@Yo ِCEF22ee)ZDGe0t'P"<UyE qh*Ф$GlxyxVe$y2Nb^khtZ^JݭSq6ʯ>׭Wp2$/w̢klt SN0&PJ }tf`}G T4p[1׼0K%GUv`$=Q P Շ~34%yPOW)5hWBm`V4]&DFc*ggM3.٬N)_DӦ{ 8l,r_ѐybȨ|_ y6ˆZAVi3@O;|VfZ%5y?2ӥwsZ~31φ'6ߗ.C)ۍPA҆(@-b\ð<~)u}~̓Rw`j޺Fهu>517ɴGB2w̦B6'w]쿎@U>θͨ29JkgyK@P,R67=ވ$ Ɍ @D˨ 4M)P9JIŗ*] /~a>([fo퐹?Ƃ`keiUґӕ[*`)KF6^̡]d-4 +M=ql˴,>4My.e.K9%֐sKD0v[V"%eezDeeUQݯw냤ֿ[vݱt.ƾf}NwJ<냱QOBA약JāP_~V|ڞAw5=s:|U?1.Ntg`w`Fǧ(V63ʇ{{ 5[?EKG݁YDLece#bNX%K7ff ?.4&g"V{š0M#"*xR嬑tF*ɣ哱qTe;LFh9}y[ij0 o@.Y5%:T lv ]K鑑ZB-3*cV\HS FE*Z82)w#dc Mȴ .c32s`շzؚUw9mn!69Qy1RQKS)M4kTz_o8.hͣҰ =}{uY:!lMyj\/0轎Zs쥉AU2 Ê)PP8:2/]u\˲TZn׆d M$YV{Eޚԡ\J1P`uT+`1Q.ot~Kaq:ћg) &g4ƀc8Vo&N\t/g{?*LUy6:L&2cLEwp?n,Fz0OI}8 >B˸ JYFR l{6OVP Pp=;|gR+S*k.W[1]E2?@5l$`` EжY-y FG|ԙ5E=X 匬)$N"HBq6˩B xpۺ, MBTLM18qC&G=Iogɓ L:>L:atW GnS:4bqiCwjTD8v* M 鲸(#(X|4Y7=>5b&ngoUOEz $j/\>jlan&| @UD,3]AM\n0]Ҫ ;Byi]8jG/u=Zs aKf@UtD{.9K.^72[H&Dv+} Qti.Y0si5"<k" ggNwӕPWx |oԁDOܔ[yfcj9io՝|cˬ8 ? Ғ"W0"Ĵ 5!&Ċ׈Rk4,(h ʏIQ=U)1$ ? () &)>|7^6> 8њQg,;7,42IKJ"j# tPSq&3M,(VڅV>.GR/M<3Yl FbPx[Bi%6}璄:">|`DZ.bI)9q ڛ3aFh, C{7?_zY"%Vcк.h={8JhHhL #.пg*gU}ttZ:8ͩ\e \q&BK"_v+/UL6Mx&@۴+}?+ [A{5#ܖ=03j/V^Rje4fPM<Ȉ N^^PFnu5.բMܘm0I`b?cpLP\a'0ɏNk#a0`L[%&;˳" nxa֟&ģ<-̤hFFˇ^bjY 1C n| 6Ϧd+Z=n{?~sW_?L¿Sh]g{ ?]cnl+m}_Kx~%{;W^;iAۻ,d{?t1L;h{/]?j> J=D8n|9"hl6Ǡ.?L77|g ߟ^a|4$꾺=Ef7y6_o|no|`|?k@k6'r\!1#Yvg?o|4f|bӷ27׍?#{-MOY<)g4EЪD`y3ge:=ƈDryK0۩X ?(ox mh)ٜQ ,S߇, }y:Kmܞq!xrL|pͮ,jft=vrU"Z0J"q5{`KS6[JCe sp-: ]뱅B*>ߍ"Obѳ3Bbu!ZZ{fd 9"_<֔Sb0Kn}QQj%|jУ)p-*VEu7#ɞ$#Fi Q@CI5ء؛5)zWD#\\{QxD<]>c Nܮ@=NC[#S)4-+, +Q](F 8ǵN/0 P~Tؘ<.WB+Tp9K |@Lc 5^[`m4ۓwAHvypsͱ|xw. )[A"=䯗a3A$P I&XِriE TBb%>بc*M°̓]7uT3*d)2t%[)H N>9GeMM~VSu PD0#@kh7Vt7M ¨[n2_ |ց𥨁 K,V"E[,u2J{u 1vػ윲}] FRUOJJW_Ю,sUx y˽u MkooX [4<6R+:Q)bol Uw8*kg57M%9:>rCN&w1Gk ,# ͶM˜ki3ʌU!h;QiA45 1g.|:йt j ?1!g7"A2~ |Cų%[ynf`<$J丷J=ENFϥ  [+Co^Hj&k f&: Ξbh{Q'We%M\9+N Yz-Ckt ؋2Z75N!:˾jળH ½O lLb c=㢀؆e;7C|1m /rc#& YΨ NE!-Y%8%r;>-ͅ$3FJ2۟̅X_]J7jT8F0n`^L) ljP*zT$!#ς %wXuZԶQ5&SuPnglHctSw:V΢,z<.gkR Z[JEΆe}5˩/ r5re!;~d?CIt7i).1ڷ WVE `'cL,l>pXnor][M`tJ(K*I"fW2УhB Q*+_0J!Ә)nIm/# qǔmqbdOv*<<qhN3lMi ۇr81O ZY С#W[?Yk,W߾aEɥ׳ֈ /4[M2yVLJ\oIS8l&|Q0w~c|`hr)AVUhPiƲ Nޜ* o( 8@%۪ތ:XH28))Xjr\ࣉ.7PZoL>݇s4B(vqO˿!aAvM}{]RJ/OfVL =$x^xUVk1ut[ cl|=QfgLD?^a]s}Vɑ j,g3VUd_A܀dspFOJ!l`*&4A$EiO*bŝq6)_aT+ rzJD{%*6@i4-6WoDю=qN0( |W"Ys&16r&ָq os=~{6ߛUZ0iX;-^Z@d(6ЬFhB" l8!1 t%=&#XL)UaxD <06똋i sVG|K=#@h[ִxަ4Uƨ1@zg Gyy73::7$0,nڣ! ن3GYertg+8'M!ɛZx;&D1%s qw|%r1t I0CjNJ򙆜 . -?X|dq><S6F9O#[f:7<\i)CųIla!֝d^ ]ؐEe@l^<`KOFu3$6Z@nϪt^ȳ;t0'W:SUOX}I:zRhr\(3 ;'c#K"b_&)1!a]@w@'_0B"WE~^%@fPLI C<q7FnkVQ YرeST-S/ itz3}lg GSniu*h$ O0±2ن8{YӀΐUj0R 'h7*UvXwJL .9lZ6k_o QrEv!U(l# {P3SB1PjF9”` -xu_/oŸ p BV$jOGmIHIJ1V=ZwQd|$40W;FCIɫE-q_GrI~~\diOW'@bv/jx%jγN7fr](3ʗįk]f;:lGbT*je1_1\7;'8Z[7H:8[Lvd?{ RM˃g7En){ZjMtQz<{}=aEЕMڱ8sɬ3RFK^ NjuYx>M fuȸ!jFZr"v#m-V/̲۝d[|U(Ƞ36hK!0T1: ΖA'1IUb-PG+h^ ƃo7UoRqQ<i!^$K m«&g҆iC! Z T*duhIjUS;RNB**`Dʉ,eW_9VU@9.30QJTN=61l.+^1Xsݘ {^<悟#ӌ?g?um=0RNE%P0sŬj,Z2$+Z~Qsa ._C-'Ւ";w8 FLD$}u/3K/3jdā毇)ep8idt`=LSŊ*Ls~i< 4݇bܕ8 <}U9}_f҆T`;e`f8р"$wr]R2 w\3;_l-5wR,ZZRy'u 1 X˹kj S:LKo˪+㔱~Q6N0:PO!C=7d`z #*P~PWa(~?Pb?6i ;x@UJ>/3Ta:etFJv,?Y!g!gYX˔d$%H{*PxAL hqԸ.XcNE^jL5Ntq09Xcw?p{ ..qlo$zDVwX^cOݓ⻛w^6w* /]1 ^x?pw\3abk!G? >K" Tāp]KjLk:}= }x- XPX֖c2v eжYks-qEPU[1D[8o)5 $1~- ũ OɷWE;=ZO]բ]Xa1kzt -Cu7ѩjHMjbpת =|V)D+y=NHJ+>CAA:jv9G{|Бû*|4{Tl%i|l*T3#TakK - #{CJhck>/+қn>^d];D,7ZC\/)8kf^ٗE1`.R*(7=$=2u= *,xSlƑ‹UrOz0qff*hiI[\!G#%IK;W q =':-Ƭ!/c9+NIK!#ںjlRJxCu2!դVGxH5!.6[tOT9gdx.waj-}\gw*og<.y=JpҌA:hftP@hTJ#Y#-%sY=ϺM(%"I:F^ܓ^" [qS-:{[Z"ߺ;8Sܕs<4{vB$`Vw pF! (ݑp!lgU(j1rLțmC1@ҧ<C# l:zeQFz<ӗ hR9iUg'Pũ *+.NÓW"YJa䛌/][MňfZ 4nif} lg?韗Wq~브&֤SZI'~GCmp6($,q; Dž86[X}@&ޡ"R]2J.A#F}J OBXìX%d )R[/-(jYWEP#-VA|Sq/u,AUݔVzwC˟}gR naBGP mR3\.Kbԁ(U8xK׭Pg0GvMZjÿVӜ=sI+*@k %gXq9c:Oˢ.􅩖iY#(#~q]gK0+,[>eƏ=\g5q-IɉZ@]n[F ; f{-x ]R[S{g+Kη KW-:9~o3~ r] :Nް"IBLS d#8{/|:I)s2|>&w :R3=^zPڰBNGegPʹfe#.c@#vEÃXtϞvζVi7`,RvP=d},(^awK&v+lݭ7%gfYXQIwn0Suy7yUU]VcJA[t]vvwYE:H,Ŏ(lu_%?$ UB{o01WUYU+z< a3=Jx\N0~>U>XxvM ,@  2F L /";Y>t,"w.,u=QW{#:`م \r=r&bajpc}?ӎ;:rr-<0G< tKA&򾹎z"GeylGx vb!/I +3?>Wب En*Uno b/p;+sHžS$c2F#ʾV7v|Ѝ6V0T^&Qn,,-tحo;O(a$J9eT>TJ$B`m\' ^siB61qي}Iw[GW]CNbE6+w "و۴\>%;8@݈ޭ(nݍhۊ?ZPd@&n @mAnu ^#yzQjw)6UF@ٱ9$0J 1i.dwx@ a)Gp"NWN<#e=r#Q2V]@]Z;+Z=e8[d㽕Uf,_QG/j?O-G ABG0|@0;ة! rS{4ldG/C]4FTm,>W?ʘvcL9OȏCݙԭѢpmk-0yƸC.~WG\>cp+ fpE ב|h,2=f RSNH>E,V¯"4Aza>H¢X 0-c$=Ju G]9ni\ |^c5+D~PMM\sǖJzG0NG{ȔT|bT} jtlAk;k76_EPvۋNE.rM+75cjr;\o{)zlWajUj1Xy=H{)毡:AH!ğhg~pUE+IW㈬l #M}{*f=-Ͽ*}.Dx5$W}=Q3K7'(xr`j2mF="Xb)Mh,qMj8mK7@cBtE+to 1?EKg3M=hiM.r&5kou ˓a} ܍ЉU*_7'NWbOU]`90SCɾ"7%/d{<&4H]96摟>…/_ {/1ֶ٨}с!7|o( 4shuaxe3mKiyun<ȊQPbqe1^^͐+-J:3fCi7I* [&cqFn*nJo-mF>3w[߄̾uU:Poի|] S'dhZIqsV \BVP޵ maz]Uos"dt9G=?I&w)9mPQVg.+FqA] mB ۑf7r.Xf;~_kg]%>>h^+Tm=ۂk)@ >'X\#ً498ɠztSkqL\d0 ]&,^Ug';3@* lP]0!󮸎W"TC8߅,>[nWF5 Z1t+ Jez^ yM]T@3YF^FǠUY_ * CՈ1(.y@q}KZڠw>_ w_Ef6\j0l03%trSz w ']()JET]K ū(f(Lg%7x9J(e+~Z!rEk?Y-Y=ҀPzucQb Q9쾑F-Ł0 2^Ηpiٛ%e)cAW ʗU٦ wH*a¨ 6 uwAVzJo*_g$OQ}uo)J+ y֐#Q$n`VL s P-^q"oVR2̑22,#reBAƩyKxnS@gQ\ǽL:)I* myj,} 3:AK󡰇4UN4@|tPA !m$Z Pu8R k}ɟ\vCuI;*zR5bEN]qɣm.(u|L(,H^LhXX',I9.iCܤ,TOf"S0ۨ`j0#r\*/H.8wH g| VP?Iz|"CswQBɒ1$sD7YR~QBػFU'Ti+I>Z̖~_)sR"lEXUS3:9D:-\-3apDB `4 \%3սtk\ r'b}fɷ/Z|K14k+ه aj1XKGoΔ"_T ?e?Vޢ8^ =o+,wH3eX$OJaMdag6vZ~ m. TMb(i X7:&MMM<\VD(5 hט\ލ(^x覙cϨ{RKmvb,1RoI0.}tݎc MNI`HV3뼔c*xVMe:, lA,d2%Yk6E,@0. jvzsHKI>X^%8QgLdn4Trd|6xNw+G8qnqG$FNmo&긢.)( 8_:><&'rΗKu0(An]iMUGઉ+YUD,\.\E=kSQ%d=OS&7OGcSub 53nBt-AUBGM$l~ J(0̺] 9v -^H$&`v٬6+~<$ ifyIel]J,,>G ދM(B[30rx.beX2 ]^Sqxba7= W=OԎ H\YH>;D^Jn"@ˡ7!:N=TlSp9^dGĠ{kTagh yaWjpSWZzaE-鑐ÛDMW*MFQ#ןgnXPnSPW#+>+bK;=<ʌ+dK2{幫Q&'R'Aj! `JYnu!yUq5 f7Bv H!hC4Xx%D`+ijpom6$,沢ZGL) AE6HL6$.{l{3gf̜sLq`Cۋ:Q){nY3k,ҩoؕ-S6ms3y,lN')y G~"b~ aNFd%v"-w+-53LVZ/7BmNF?٭6"9K&% Xe[a|mKnW֑xp a6/l?9ߡ-υ3p[\7 ay>T;3r4v은WڻNsGHs'P/cPcZ89% ƫʎ]w*ngݼ n!CȣV9^J˝/SyrlPRKrա.6+p8|9F^We~LGʈZZȈ'?m4v!O*NƏYʌY?&s/|LjR"+@!/ϲڍx|~*B.%d'Te\vM6cÂ+vV*,6•YO!Kϟayx/qfͰdk+ hd1V*ch+#5;znrN)@V=5:`.B7B#y5H;T-㷾O֐ w$';.<yzm*ZSY/L]=_kR@a&$!/B#tM;߁ tF? )٭/5U5iݴ_u)m+ܜa6-w "(/հ7#԰7AJ)UE;߭5v׎r3tW~*N$t;:3Fv0 !3~''OE3N| Cb:_+t$\Tc1a?MVyi@V߄HO,|to [rWٸP_lǢϏ%ܟ&0WNyJ=ֻBIKq|o:vnXzr^$0ҍ+S5Nqc3΃|3S%grsea5+Rg\čYFiIV^'~-<ċjyl ??iۊvj>,e' I C$4鹿CvC}):)bUǹ67#e(v<>%Ɇ'條"-ڏܜ[~FFݼ)7dÛBi@Uk4[jd宒uA1&c6\sɣv tcP߄}Tp5^)*ӝ=떅IN#9IA z|Avp|=.GI%c_`{-mikKxnc'xEOqqԼbÛˠǭEGǖ"!8$;fGG-Z$:4fqt|?t!Tv=@Z˞-i%0x!'lm6 ;mF bgt= 8 ${u?O%"%):mE{&%_Nj$+g>;}NYKp-?a`]RrSg066#7aGo_'A|oXo.כH!f0,$&-Ԥ]d*oB=BQu oB}wRK9<$sMZPF^s$`QVOJ \;l{1l[U<\Zۊ\Ly:q."$,mlam $CjŐqO, [LNJ &i.zaL\/6L`SrX,7Կ 5yur,o<'daވJtv܇FQ0YBΣrVP&D5 *sR4xD4$nZOq%A3|УܮͰj7΀È'ڰ0Ϡw @jt4uwX6\?p6a//*Z7eOYDRW]buw1=fp2i t!::k"0QBcc-Ae)K*9@s3.5:,E}&ӳ t&w,N5ՊR+)usX˙E.%3"/PnϷ⻯?[eFwὠ]jb|#\!<%a< xd,|VGaЁ+6eQa=zYJFK`0qķ) V]hGҮ)ԉ9@\DTEZtR"Rƃ˙,t?t)>jv'Kite2k~,`n ~ ؞i?ր#M!;swA NJsW dxkd|_cs.2R+ZZiZנxLByv5wCxùiH؝{Oɷ MH耴Wэ=DdQz'pHʑ(5#uzg5DF-#Q0jL_^a }콅>3YErBjsU|@zv ,fƚ7,KIPU_/GGΣny=LjmɅѧ02x5^a;Fd=]3Tb)hXb y3ν^HME=bFf//#eZёY:i@o)1UA~m7h=P83UoD6o]:icGFN]?ژ6OQ;`l6I޳xM5^4HGR"I6y [:FNnctb `]r0?8<;Qgmsz%\ԭrLx;"C4Fvs8df!*<˃A`/D5cUy 2W\ ol)H :Ib]:ŷɏAqk/ $xlp|Ʒ jc@)UY <% qT;B%DjGYCW|2"Eh@0"C0ʥ{0<*1z:@tif4L2SGOC:t&CAt0}c;Նhf3A7TCgwe?y~O?* }+fnwn ,QɵCr dca:5w*wd%tRJO QykIKp ɩct(q3%.:Ow6 n,2pB$~χ ,Ҳm{>#|Y3e>gͬ90Rҿ&.vOkZpY,S)? V*>{$^yH/-E:!Y4 )?$՗qJJWժCݤPb'יH{uB\ zG8jL˿넗]?_ |O(a[ղJwn!o}> >s<=zn }j[π]}%H'/j~|n^^0*@JejM/p;vҟ/E xY@Ʉ`} 0mx# "5% PyGtYpQaT#8oоgS(rfy|zj{xW}<4lY;C0NpMO`Sk v ^ [L j.r<{tdE&ߓvN6OsFwa̍ h%x dβ| Y}[4"m=/L&$3{X`VWor(_i߹?Bl!+ Mhڂ&0mӽ^o'dmȬ̉Ym`hٴFP$H!*M -'UEq;'<{(DoVH"%$݉bdH$}UX[)Α|m0"$PLЬ 1Y =DTjha oWU#+a M%4`/Jv]$Љ; C9pM"֟{XV,X L2BW<`؞OzGi(Q~hV ' \I=z:YG$xR#\Ӝ]|d{LvEBC0o4؁UKj~[%TJ0jZݴ;tIӼ6~I9+4k7,X xHZ.f/k%{f-b @`ݧrc YטIepEvحiEFSno?uu_"uMՍ9!1D7mK=& n5cNCy{1ZoA WU99Tr_*ٽF.f(na1?]ڤ~%+Uoתy^rl-NkgT,'9COkIf_$|=$ډ' Ul)zd'teYY!F 9tI- t[+?OELG +bՄe†zqpmx2xC[(4r˅v1\.4TRY`FIr o䨘"dyϊRR i[vPG&TM8+Oa1[)OC6Gڀ\t@:ngO_e^9̿XWUHW".z:qޫW[h.Ўx,Ta͆WXܺ3 `XgF1_/ߧѿzFԏ'Ti497L5a2y+rd2a1|uo`V})r my_8PQ$9_A?w@ro ?2GҞ#@/>:Y?,3)D1ΚE%r4q4p})x`{CMܨ Fw'w=Ju$(3Nz(YG>P h?4ZZяAcBG%[I*+1uTL[{T,X&qb!I{4>VyQtIJ=ǦO;(@R2K+$ޥ7%m8*&tֶ8C)M/.ng|7ն8 M͊+piIZ$J?-ҽ8c2?ML~ .nmvSc:x/=,%'P2} &=RѷNd򅖲,al&[d>ڲv/=E-^@غ8Z g bAg<찔&Q,{ilڈM=RAbS_@i4x.O]aeSz!XdfsiVˢR)c+Oh3 }lHXKbo{Kڤ+eԢ)*uzI]K(mˌFîay?nJ)RaaѰ`d~, aX   55[xFɡ_$uQG,G'bݨ{9RlAVyU1# ٶX~Җ&A<&'_ U762 UlMZ>$&MWOĒɰ*rNug~+r(ءޘl~BO<6Fi7{°'xRgH G82T"-Nv'^y;@.pri {2 wƺ=Z79bEe ;_+O2ONy0~ t.e3qw֡ NLd(9R -$_ SF~̭ uc"8okH-T`1\n>y``_?ɜb* o'nץ22YJe&D .0zZ]iV X.<4Դ >/X|kۆ(ҨrѨ9i—ه ݳ*Mܳ&B4n)N%q0&I j6Un=gü7Ϥ=Jʱ )X.9t4 -AYU°0^-u[2QP.\C=¹LmU{/-*(} Z~9\ 4G[ y/]j.9ׇTT9F4 nuLC`I^+[q$T/s XzFK~fBKӁYSs)͑; |EcL`#)槊ɇ148b9/j̣w3?0g| WM>c&\Ԇ=WrQ[ʬ>ǻs>;MqfC*fA Ps&4eM?j')Ux+3 .D3-tgժ'~4ך~T(bSV$~>~Ğ4qjkigR}%)kL?[qcQM?0AӏsS踎sWL?9Iך~pFO7]{t I;X2^7Q %EPzejx%GȠoGm//Za|170gb ½jrr Wby_Hn2\ TF2 |g1zjb5Zq3j-Cܬ'*Z!-ȕk0K~^dnV^ $Y7.d,@ W@qQ$QF{V`p:K4WHh6*N`l]Ol1 /P @'& sy;Ǜ~=3|f}{|.lv(>HȜF.]\jU8 1hɦPMHZa@=xW:}#QՒh˺FCamֺ UlAH^ӂV1/RrnYw -9H~ILʒv4 EP V8'ǒMەP/F&tHLw^!%N)n:CnhЩFRRʨN F w\m$ Y0FΤi]ԉ+%{X<~8 zyTF}鈡Cj!G|dv$܃_A)8/x a&]*,S m} jb>Tc1yך$ :phRأD ~  dإ6%J }[x#g yO*(PN}l7]Oڲ81ې.zuґU8( 뙍*~a,iw'3R7,D~>"%Cv 27WeZpJVHoC1{d1 `==x rd JF1"-wtW\ xdm`6*p+9PQ\\LF0I!$u\G\ͤ7%F)sA8T& LKM2w127VP3=HqBkDfKn+RoH݈JeG!EsIt!L`#\ʊj rS GNa+,XC}jE=]cK"9kR0Bá~4aѫZpV"0;@z ž445p /o[Ӌ8 hbZz?X !b,F7-ƝCӕ҉EXQb)͟0)U1{h灙ghZ ݮm-=^}Fɛu(A/|NZU6;nK]hG-m2cNGU~IiܢV]P9LRҚqR~|tRg`S>!gp.Z3VΣӉk/L'OtF$k/UI3iGOcKTʷfZXȦr^mOŘdڃ2}77Mȕt'8&cѝ 4ȶWDwSW*w 2$KY{6O f3?NXwq w*G `K?4q@?aPDt1^ h1[%&&M:]b*1pvr߰=*(>oF́o\fAC%̣&;J~Kf+fOsz P.1lȈch|ڳqBp/}P>n|6hzPC  F>4}(a vO>xE0`A>DHa\ר[ J8w FS;b0-, 9"#&_IX [C2yWcR:,E׵7:9ň8XʌwKQt/s0Qr'o )M+1w/ 3].*Oip?ݒijwqM| ,B*%ɥĎOxx ʺ~xG@n_=ͼ5,vt°vn RT^dbU6&P*/Өp8R=I F82D .R UOH àgbPgLon[`I6E%l-G?B? &0Rtɑ-ڴR*dV t˞-K:x7x&yk~!*whשEK2Ų.y~wnK`^)es&;40[k?G!B4 m !>| wƇ R37.\@ ZWXlDtZ4cYʿ/PD;?e"CP|iu%ʷ` aѽ./W{rYv<Sy T 0YÒl ?b6 t4xpvl$~8@Ɠb|lZ4XH%h[#suP]rXAq&%lVW~1_͒aeԂ^ #N!/[oC̐厘DE rh4}ƬZ$I-UV.#DM`B%E̍$u*&.-"/B$WY)(}|st$L PH#IqMI?Ӈv]=*rwPč] %_, CoM>Хr\-]d>㏓#:\n t绲πke9 zc+@#fyFRM()<{ZALsSMy2y*(Ьs8Ϭ(p 9("YImg0x=IR:5ژLJ@z2>d8}$4+xVr$DY9ϙ}si }3܁Mg)h y> ɣ4'}$U<@kH}'>+GB|'YEr賚PXxL@(J@-Cz?( '+b@Olyg[?q.ۓa3xߖL;[VW m0vX{P"@՟hۊJ.׵_4$^ܝſ-S)F.R5ys&$*hɇHyTs>Ǯp !q]4E׉e8[6e7X6ѠBͮPȁ~QDYaf\SӻG_^~TiElSИa.@}vZV[8Lΰ \D/?v.:?"paz4S}5/]dnvC`Uht7ztmeˈ`N n @0R] }&:AO'PG`-c@0X ݓTǬSGxKLk >heǴ"Kn4fCkl `HzkIE.a&+$ݚ|Ng5C  yɴ&s ̲νHXk%R+(t.KIk?ۼCE$EբO|aeiTZ°$0P x jF9LBgkU-i `6Su*ti\̻92O.bTБX~T-P`Mp)M$o<]>&1 MgeUdlS(Wte͜7)8OqSyؓYN$hNGZ~VR~Ӵt'5wQt(ݒNKF߾C : 䓏2¤xdXji+ֶ]"n0m$I{gpd}Hф3C.s|f)WsHv{iԖcN6nsG_0\ra-ZBiޔik2}͞ukMǛWK=F?}.Avo+*f.E\+fP Vɛ3`\~uDwp+bKER8_<5[(hf7VbsKhZc̻%oNm>JEm5inBS6? ZnsSŵk{Yc *ɟq?O:]Jgj\-=U{=#'wrrF-]DCMkJu#|+tZ&C1Ifsס>z k5I*~VnU[:~2$N c%x_x =X{ޜu1TțeIKH_9'-\CcR=}٬z ~GPIH@%OJڔ41T]K 0gr /fG˷B'A_Ēoa ɥޫ䁫}Ep* :qXW,MJޙȦ,2z 0~ږF~]he&,:HtBMJ';aUՀոl4׊=۰UfyV+ז5^W+\Ίp9R``MUB&KaY&Dʢ V)P9^܏w^`$HyT6y>UG:MoK=(x틩e%W a3{k^ =Dm,OO^AnU,`:ہ|[F+NS2UFg\Ū*J`9 ynzJ0H(T-tylHI 2$ZڻGC2!?dɦ"ȥr'_Ic7,os1˭̇Bz=NE:hC9Эr!v?N9 NႮ~E⸍+Of?8S̉nGˣho[ZDQIBS;UDo=i p76 8H.f&s_k}^VO2;(. 'q8V.ԬDTg`ɭy﹂7]$7& ) )@[t1:Mwaez=%B{]v[ьvJ^C"~OŒ2֢9xŲj& I}(sC&N6pH6iJ`{;Bi>95٣"u*p: MQTՏWTu h̬H̉,NPy80ezi8JKC1cwPvz[?=SHtp3\Hz륍$N9fj 0,4Z*n _)eKC>I=R!7~4ZaQ[57c>¿gt*vJbEaO(WztB^ v*b*n70a=DGd+xZыs>@,<(xZk]*>k[*?à!OX7=&흎aFT}zQ׵*}]v}37;IzjNr)܁`ȭH}$f *JPeBkja5oeڢLxTO@S}P?Uhd7S\,_'YB"7g05rZu=jƶRR?_JM+?no*u$qU2Z }AvO60lIz"Ԓ} Y`(HUZQ'rZt7_H-F"%>QO0HڪO Cwᬫ浸~ir9J%|-zXn)4+vz(CG.)ǍNyg{_hm8rש?5'w23з-ez>2 .>Tv /Q3 'n} ^42-(tg!Q!l+.LWEmMm!F41~_;L:dYeJ<[} YI=&Y nlYӴs#i @GZ4񏧅:~ 5hWZv+rGK&;[Y<ӪM+X Q(hc΁x#7sEFc|U*j-ūN0nX"t5*xo\۴YճOqdVPVnc%H9 ^1g%ϑu xeK9&ugBAGȣ8.Z5R]jaX*d١ets&e|T恌:q GR>Nwy.,V7pU0`GVɴsVxUh"d*I86\xW6nӁ<یG+tLX!dW|3e6fB]ÝjRa (SRaa}WTF I] D;XY;6_XSrU=I/ >*:ޓ/4U++R}Mm'K>X)ڻJwcqbn*>h;!:TvEO|sܿJJQ&.~$3t1J P O|XfkS6-nTK.44l3 ? lTw}M)BD'%ca;T` YhJ/cC@#3J-Pxf;Ii?|zU 1,AOTY:xK`+غT yC2Ki%%]iD>/`AxWaj +':TZq`ւ*>ȪDaʿt*Ѧ 䍳lZ`;ichzrr4fy8[:RyYin"i}PE $Ϯ ENZRC3l{E[/9: ;< dq;nC T[4/bx=@tda?O|aU}cܴŕ뉿'U FUvF:|6PЏLp_o~8>>i2o @Zu T-a | z m_H1aWp߆)a斮}㪴S#P[%bE?ȯiпgAx{Qxc`?7e:,.BWXľ1IƵI;YhiH#]| s+ J@_`?m8{-\\DkestϷ7Hv}[? "(m'ŲŲŲŲgŲIJWŲ1RW߮ HsQFdgcR^&ڼfq(ZNcb_o[`@~iU^踷>Y0I橅dOriͰGII*movηa C4EE [R"AGz%/w35+/H˳ox$o0l<)=iA< ԰đ5ԯ7 Ґ -(_:e=p=n4y_GBP`) (RNP|^sS $fxG@ģ >cgS=ÂxcRPFZVL wOʞҠm1 Qx6a\Μ}{hFhZB*~)]L@c>0?* gЉbXI {c [F3v_p#@P{ 7 ,}KW ck(tTz Y WcOw⮋uN4yv8Yx=`QSJēgnۤ4KQ-#nжdہ݆]w 77Xe'E{;.csi4Y G [ o%q3o =/ķSG np$-˭”=C8 c-~h iP!޵'_Gqnl -fa(/OmxQ?PSXO?׉g%yqAimctg`zi#O`{G?M6r9g>wI))Ֆw.OʎQ)Y#YmH8/B_Y.χt#[NfSэMnǛ!ɰYU+oBr~*QJ coErph2xo~UtL }D!wϣC]}*l&-maݔ:E8 f>,7wKކ4rP...7''˻h|Խe-̺Ҫ4Y/v3L~%&'ͮNŲ^QO#aitq'Z$4m)%8 ;f>:g7sڀw1ڊ!$_ }u&ZuwiO{~C8 :zB~ބCeSp~|,nI()F4lHilnYP@6R4[F4&mxQa~KJotyT[}ݨ[Cix}m=ch7~.ln[܀(xRul႘maȧa.:BmhqؑO=%t0FɸŅ?f6Υq$]΅= C$@JkhiFTmhQM=cpLtCu!m?mӓ|5M6$13`&cc H}ߵ熘hϯϰ^wukL,owW@^oM2GNJ]"z3y-Ytݨږ5;"̳fK-xP!Y@ NYohӫYJn߸J^XKvs/Ń6U4J`|\xJYRzmպ"I.-^cMhA옥WkV!.V4rcV*{9z17Kl9>G~V)F#Rbkf5R]. %4l1̚Ƽ*T tS.'&|zY/:mVۃ1}F -֬aja&Cf9I٩Z#ſ=fPZྞ>~{J؉K<1.xhF5mԝv1v;u6 lѤoiul5Qc1c<(vvP)e%;fqKuZླ^>XL eqŶ~^ o.BayOZĢ*V )Slㆈuu<3zlܵµYFOǬÙ穮j׬UEt}3x4epZ*v;rVHIe>@ՉeX#@MjX }{NC<5@׶do̐,Fs *T8O0**cX(Un^ٻ!Mymjp2 G` .;UɅ^1#o2Y7RwKHo vt#m8xߤ ׍/tXŖ *@h/}u~f*~Pn_ui U]S]jr*G^dIg"4Ƃ/!"DRoح UR7A=?P7NCOFY W yz=6v28ߞ_Cׯ[6_z =E<ptk ɹ Ӫ\ 0ל!ȵ&,̃/\LЙ]|+VqP?fC|t鍏Hhft]o=>Vm3g5kP(;@%*qB\n0Ĉq9J;Zõ^^}XdK\+Meܷ!-Hc7}h}~$m.kۻdmW!40V-Wz8TƁDjU<"cڀbMX}b ѪiLkՌi=Y &P:imǑyڬCV.ذ'2q ;EGXrݗ%Xl?eӳti>d;ju8km)!ۢu'St~Kc.A p-7^Oq63#× ˒ qg̐} _df ԧ ^nq^{O4E>cDWweL]Vn)%Qq/NlL=K8ˆHrbLLS W"@' cc*q!8Mm;UGeSwMfɲ 7p-aa<$saqrS<~*5U?>o iS.4,x 7$xWW(VݎhTV's /;oq 7mg9WTN3dnHhBҧ۷KAԛ » p!Z~TVڗ0쾑Mf&LB]@' fԁ4w`SiJtҔBV]?W،(͈lFt ͈>E3QlF;8#ZkT0!w {\Bh^Fljg@v`pb201j6?:8DS~՝HHIWw.Oj8}cn~އ]CWYl4?6 +̝i˱>җ|LLէqg3N#An0elc(qXkT?pinYP,;LW)cÑwNʼt||8x"7S ,zdrXPYNmpxdt)ÙV4!Pqּ*^[ e:y֘.F|_z.ڬʌg+D^|V)zkJ]%ʎ;*CyEfe#5%o% -{:rSݵcj[vGt(2:r3}ݩ~M*T 5y ZDvփ{ei^611W-#чMՃ☰yOZ>$y@˾ Lj2yU }%K _j|@(ٷh3VR*Ƅk@5ۡI0TjqjlQ\< 1AטWG2MU*S?0q5ip1DLqĿ\pj%ihowsfrNke@kP}rZ[hC `Ք:|B5DVƨܔ;'vy9"%wYI7 Ѭ)C4s9|SݰFM|FwhTْf#(*\Hm:Tpc0H| R(y'alLޙ;_,MQwRkyrQR.8k`_xwX#7z5e#]4'g5jY$4W^,&)-l,LK>eOحa!;?~yZƘ}\O%F w@_akPVyi1N9c9ۗ^Ѵ_ 6P/v|>xxl:>G\ 6T\i-b3޷}{:6f_Y~MWs" '?;>]>:-6K!bx_ c/E 9D @8MXMґ_qԲJrPͧ9 { z:{]e|/ }1e:v-R^Tk@,?/m}{'oNW )˻ e[~иOCʧd6hiB} vT*@zo8哿Y}g?;>?e{s'pz60W r!@{sZ\?w:یw'p07I] zl7G6- ]ρώ_ w#W0&<}bo@b[R6=֖*{>;~ڭGA(#9hım>9` mp,jB{F< ?[ŎBp)|@-߰g +vgMFDS)I{ !q ;R櫍SF5Wm{iuϴ~ `KEhٮt7j!j|U4oTK7;~v(vn :||v\g0G/DƦ< BaȖ=Fݑr]-o6"fr AaAv.x8zk [/s]ov,nOcS=e)KsO1y8]cixlA4d@7]K?R{|vwX"2=25@Im ൻeA2@Le ~ 5Q8L[5܁+ҁA~z0MG]Oi".p=ےD}Gĭ6H!G+jɝɻq}h]z73.]HWOn ޒMSĥqkX¿Xu)УSd!8EZCn7c6A3ڦ6(.h Ѷ|jGLk85P ]tL@Uu ]51PsGɫ!JΠA @%v#[7`xW _ j ͨӦV!"mUe`F`H!\ c t&](ZRnLٰnÓ30'JЕSv՚5EO:'w&3f-7|u l}n>Dkj_"6_4ܝ~[1j JF-2%,2e,ZB+RZVw X$}:}I?ӄo Jw{pM`ִ}e1C/z ޅ^3b?~w$+}Քç43hȻK |=::bƤ[ͤ[2mcCU׍󯴉]P"An1DǠ}=FxBkɝ>#1 gj'iD:Y;|27$_ " 2&M*C9ף +CjixP1ڊRd7[$wRJu2!h|Ġn1P"[e:-M|ʴ)_R(J1"F?7Soas T8x;0 Fldh c C&54d)ǐn1) ڊrCpc-Y;|<ږu%v /O|&VE~VfbVC[A֥>u+ X $D`nȖi؝92/%\9: v[F܍nFkR#E])ccyCRy3!䠃dkFTBd?;lɭGpP{+=.e i }iž^QL5( X,IC ~UC3HáI$ W, ?4H13iF ihDf#W! ]yhЬ?G_=u;f+Y85!IS'pxB¡ߧ^h|^:j*c4A8±{+yĄ+;TSuK A֣.Y"dn k#vCR|IO4GCݙ#FG1\ᆺs PH"P,<:eFk౜/'m!^UWn)w~owE[ӹJ.U-xt0Tj'ӓu縋X|+zOKyi5556ėL@hI?qi Ȋ;g#!pA3PMx/jb ~zkf(? FYs `vOq)p`=Vuo kǨ!tq|Jǵ^(cy7 (f\]E˜:Qr( fTᜢΎ`8W*[<5FPJN(,x:y t[|kc P5OPC?@+E| C[Q44_s !2}b[>ƾYHͮ$wd~KQ#LQq4 Z -wzܴ*l!Cm[r5̰)ؗ˄wLxھ\ջ(l@KNߑq!XyKeJ-g86L[,׃Uc6@P)&nk˱=p#=Y-P| vEr_\b ۥ𴴁xBmzǵqoژgAY,h$ =kI\OyOsll^x$q-ټ{d%G8E'A?]$|fJIY /V40 w9g$;N sPв nH@ &*L>~ޠ%)MxGF(yZ$էy!d@W#RF|RP &j>m mSm̆ǘeGiO!p`w5 7e=@A-}܊foiD ӣ`AIND6B T ie?\ ={(UqߏxQ3/lVsXΕ3Wa**fe }*\iނ*̋%=n{Fnj@HcxAڔu6`4MA}{ Ol2,fNCt7?U "*` MQ#-~A z Sy'*BF єba GugaՎZ;a b*ٵBMmb1tB?~u*@ @Tt:rJa? ־);wv+ \&4_,]D<cXZG ԧYw8N{(i& }q <;=BB`8Uy /=I &L: NYB_,m<`} ٬CLJIi2JNzjҺݽ݇P=D@V/ {;y"kU \րg-T[R2۠t!.Rx^E ?'GC (cFEm)4SRwnr*@9Zm_Z-0d2u}\<'5 AWDP=Z̻y0Woh+ߑao/:CLfx"f|6);!L2li a^:!T7bB Ǡhѩ~ ~=-dhÓmxVa.P0*ш[Q_MѨItzf=5׀GmCcmÄV46^h!gX+ hZX`:Rb-̩,k)91E;:$ُ.$ (?J~$cF4žf-`266+xc|MdK-C?+="Eh=1 H c 9 O6x,,ywfEx7nR"%2Jy ̚3h ΅UХC.%0L-o0)C4B;PF;Ƚ!<,1Y/cfbdd^,#_>?=pEoM{z{q2gAdy99u)0UVe82IڤX =,n_3P8leZ(-8 IH@0(_ʞ6D5Ꙡ‡P٪"BBnq?_* B 0Wx996FX-GXN*G=5 G꺰/`𙖾wz-mDLŬ9gWދAe)W;=Jv7d6͘+H57A D36Qx='x>t4^sn{oI>??'ߞuaX"(fetz̎#+Aن-nCSx>h;d9S/0Be)Z7 ֨6Aض-FWcr@OtNQh?߇}&j7ЬC;gE&n/=xBmpuk^&,/6KՔgdg;EymLKb(&ԓ6QK泌%8Pkk>%j4ӑJIGcY`?UjOC& YpfHR[iZԀ2%C4-@e66_d,6ɩ 2AG- vZBb(Rf}CwG' {쥸RzBPt%# ?r@5$R MH*`3$#YZ IL)UoxϮe$=h%@]}Z6FZj`w+uޅaNE&&J(n__@9ŧաb'=ٴp~MII<BЖgEhV  @FI>$*Aad^KXnPYD`RRگrXf'2C8? ̾) (ïC\ ,u;;ZCp"XyOڷh+-)ǻІ7QEѠlrxT?\*GFkU)U < )jРz ص){ ?1JߛڿF+11XEu=M!JmKC,5ǟ p>CR{rx _ذ[ls*8.+nQ&¯i!x͝o,NH3.8PڼZ| beb0o9 f T-fUX{ǀha`@p_xm?(w2KeXgMe?8rn T#i0Ḛ e?\+*ZA1&$3tEz;h"93 7ka Y$&@š?3/'hp -rr]ӠqVƘFQp~~ZL-šm+}B97sʸd+4ga)T'+:iiS\xzExad@L 87|ŬYYVe2Xe~h3/aOe] ˘AY/e5e!,cD󰼶KهXz,qEc!eYBV? YCY O8iۘMkGe%\uV4)KO:Jg\O`d`&+ev]_&MxqVֱE0>&8}lwYGBDgڢkZ>D.mt7yB `_p-5qHKz hr`'d+HEH7Аt E n(u/ՃooB}0<6 ktࡺrl薆a;Xpz.\vJFz>J6]6 wcclfFB -u:t׆CC^Wz͡⌣eo3*ij<V m6 pZq~eSh[z~؁;|S4"'7qy}pz|K l-7 _5u](kuZ.(xLҞF@ `\q5n8R :LsRA )8i:e+g[2 80:NVgUT1@4 G;dqƅ'yV5ϽN49Vg$" 0ޣ41ld]4WL>sP'ZnT7PD6~K+2t#Z b=Xjگ S6FcLU0%Z9I7&oKǫP * uQdK -p( StY6ʊnw"9&2pAA?lj2DBWvk-MZ?ŅWJ[it !ʈe MA$CVcx|=^@ҳK3l hZ{? zfGң[p%O xb053z_W\@ 7aq,&o浬3fknmYZ,K)+Th!ju?5ёI0*Mi0De%1x1pj-Z&?R .je`[ N,['7&qt8C7wfSrEy=- _O;@*hX+)4- ^ Ɛ+ g!H@].{ye,y'AKAwܲ0P4)S xܗvEj-Z4QR @3h>)l_:ad86::>kq-"s1MDd̋= [-Ucq5 ~:`u7R=8YQy˫ŰwyVR3,P )SjAVr].aA| yMqq,K y$O^!WȵZ^B+ϷB1py#eпbr*Xc Env] !8QXrO[.3,oŸ\ny]`]WJ˹r.–koFrVܴ L( z˛X|^{Xt9 ^._ÖTH^2.L"IKʳdRҹmO3-I6p) HzZ6WdZ*MKB2DO-]s3 Ս6BAzU֋/U XZʄ]~@r3lTζ+U%Mb9J6ßq,Sb( 2vnSaI !Qki[$ Ny>qBļ,qi3+w⚟ O`xq,jLk2LJhi/-]}KK~0,DV$tƨbS%i_HK6`IB;%>%Y![jw HkvŪz OP\ nOipWJPCQ?B / (K1JR>̵qtb- M,TY'09e};D U hFrP7/bnm%GoIcV<˻BKJ2&ޒ-I'`$PwmUſ]V_߽)Znj~<7,RBmWqF|elzloOI؄J8!~oJS^0\Sح'H {c r!45]Akkd⩌Jsjz nuͽ1UTn}ty4Eָ0x\Ki8z=B-֬^o0H(EUc7-K@^  $Tǒ4!Ivkx riW.=r{Y;'}[&4<@}sV;֮r&$=.6?\r&<2<>.²%Pex|~ />YdM0QǨL|:SCW`TҪDJȾ[RP45kHQ340NкPH]!N,iUr`]Eau~Kʾe1 w엦,Ӈe.N2LM+/Ow ԴoO} ܙ?2!lKRҖ(15п(<w@5 ck\ųћ8N.rHكG^SNj/8Kj(tي[r䄑iZ'gyKt0KrAuիF7"T).y_MmĞUCaCx i!Nk?k\߀ dYAH$І Mwͤd}*OΛ+߁*.)cî|/^ИѤ1#Qv첶LsXA\cz@s\^o5hr wVUIY[3lZFN jΠ A-k hB Αn0o'xjb4Uڡܺ$445g;P[3sEPnFchP'#,X_Ջ^|pX+Lj#Q?i7a<Àن s +h¸o&;m1֢2ω&^QV\S9Skku.u \!4 ]\hHԽ-+pE$ΐkR}(+]`pd;Mv szTk|F ݨɗaD9\~א]<z9Xm$_ ]Yx"|OT kEQgG7v|]zJJNU*78F3](yL?P)nܐШ{a]dsѷU] xm Pד]C±|"=x{zw? GZљoȴd+߸J/Հ3и*a*"{ax5i[$Sa7"tTXx2{];&kEƃZ!UKu@ki°U!P ]Y'4n&HEvR x!6VBsL rk~MЪ^+Ӏ &l2CyE?t9 " 6,s 7h][B ?ۤvS..s"̼,אk7eUq:`%sU" W`M*_ OPzƛfN`ې2K)\C\Ak\UQCءpĻcv=>\6VGa_ieAE5k3`B WxZ\):Rg.6=v7h9lX>R#1W|YNCTuؾ:  !A95gqlz ^p1:V8UkCګ%Dv?gfYh1@Қ-X\aq*-WIJ ӧ, Su kT=uxC:80h2$V 1x %0)H;Z8pDV g'6* Eȅ8_8{ppDZ[8*q|pZk0rpV:Ɛ:8nq)z1J}"G% 8{LW0C!utwO<:R@}\y/b N;w2=7Ƌ\TwDU4gN>y&3{(Ÿf0EE)* T$u0IIjLR6$m~UKzQ+ણg) XTu6ȲO$ K2Wm#YΫ $b/ ,kk9NG",H>_ b oH?/_?u{%;$m䈆|/qI~r@LFlfg2$p;2cbЙA0YcOclU{二4@lyxz 9ot6Gү7Q]$߇^ƪ|/J8- 5:[>띤oE(]rX++5u RJH̽#@Nu1qN'GxccX[`lmER?{ ׌bM=-?P> T?NrW$I uERXG~]윺\Sc4E(5%ux >XX{h%s_kw".[Xx7ݩ0uy.D@8g1`S)AvvS"wAYE`C k0$05@1F$9'm`Y6z `FQ=[iV=h{qYo[U87j7կ5 :uV3T尻o[tZNT~6 ]ڍZj?O/p?swsWs?3P0Fj3~WA rX7l_4m`og&?~gu_/T/ hi;Bxx < FM_\^:wT{6j)bA|9D,Y"`X,mݰ1ll2o`h y{ @TOq84K&=޵JAoa1b%M}aCGjٴ _ +kwPka|xSЖսhKl%o%rwAB aƵ͇_fVMS#[9UamUez6u#J;EBW]e;D9ubnz|@~*wp0e ?OC"<8˥Ϯ(xƱn&|;N22$ s@F l) @Mo; Ո%IFj}|< !j /gD%r܊0@QBk<5CB6@2!2: F:8ncTevjm4r'[[`6mɀ|_VK[9O>`fKU]URS>a$~fԝa;D.;Od'Cir͒ C) ӝ@4S[tuO]=I\Oݵǰ`әfZ& Myfl wWHUR!L_aH؋O3CߡtC, H} gR36snZ>*{sWvUE1$sUKM]ݛF5w#Y"tR?z'kh8k#Waɨ(_$$#6\ Hwqӕ sxtarrziZz[9JD/b!Vi \9Bб+:eHexLdMO+: AWwK  (\7pj-&pιY߱*QކB^>{|O Uș0͔Whp}o*?i\|jDԞrזW}cu\;huݍ?m6n춤'mWhfV֎}9"B ovpAhx3 ۙ9"Yh&QNC} ?95!=g g<[쎒sXiҕ!vzY:!Ɵ86j@'h/۝&SśtQ&=;[TΐkkIF{kJQOZ]aPSO{) xal])ޟRwaQ\YinMh|5'a Uհsw,Ѭ}CPX߃pC:ؓLGp]zwb3Q#Mm۳Їjf)Һc'!Eg|fQx ]eO#D^<|7wF沎}Ŀsչ޾B Keo+GW?ɡ{m7 tteEDAd>5R(Mxs%Wwpr_lY_]_\6HLn}xófuzҋ~ڗpEZt0 +63.Lx?`3Hv)cG|qİYy6Pci+#lY b=&/6[b'H)_#R;]Ck7Ӟ⯷/\(Odaie0'H8R~ Ԛt6%D!\faǬvQEr'OWgOZ CWĄq4$LNn*:lHw]7en@uek qm,Z74uڒ 鲾Ř*ʶlew^b(TvYvFTȂs-gѝGtZ% ޼m *%>(iB(g(v@Kc$j2%!?Iv O%LZ~)Cg74|&6 @?Ao9jGfjݣN:Bhji69Yk ]92`#E,KL8 >W8LbpkG`FQtΎF2?Ccx=ޠ4.-skx{;Bq.7inWO/ARVqFDr|L8Njߔ1:`4t>I> &@$yHԭԅ GGRK'R7 +Uys1*@[ixnF eVn ’Jf2 }8,B`:O -diB䆻c~puGKrho/my3u~ ڷ4n\tkIV!dR:^ qstB H/%n^րGu%n{!T;@z$nT3.qL;P|/m M'DCn$r&\+q{T(6KJM'*ΨB!w5<=6XDn#r#qܘAgBDnV7*LbnuŤhV_tl,/*l$iV78aVp|ooO ۣ-"YXguk"V 㳺-Սt5Nb*k!^ BSdƣu{Z# "h6%zsǤukCBq"s+I, zWp[ɤwR'cՎ%e /9Me:6hS1LՖc`WjIFfҿ!BboN$jXEx=ymjh6dE9Mk;Psc 2MG(2 Cc۪{xwkzzjDu/;&mX`ď6_a{CT.UNM~JD}秅?=cqGȟJ7{%[u&U-S>E56*1|q58rO.J2G4V g>ѷ tWVSDs*%q.^17&@{GqSSI|_6sQ*VA-Р*֮O#7Wᬝp;F8<ّTΉ m\b[)ڲ.qWlv܁&Psܞlڪb xB<]y6c~αg1fH9üu] r^X Xv&J-I^ya(ӥ7,ۓ-skOx\}99i|ش>`wsJ9U94J9wcOT &cwQ1}s!V8n16ã| 6]T >^{x/RU¡:gC?NZI8d1 ,Cgm~pKZTA"|FI23ICFATE-cBr!hkòw!>?<(ʗl\_mNj桼FĽ%S#W$ڸbSiك֠Fgng Bg`pNLOBVV- 1BN Qj&!0#Eqj X] bTk)k'qܡpj)Sc|(=E}bwkSzn)ARQ] bۈU4_F9ڰq#VKg#Fd>"ths&h}1 #5i%`u*OR*\L8&̖O 4Nfka~l j<Q,@WSz:ymE]W QY.p cb@~+L$ዽE(lyY;w0Ч.i8}ͦɢ, maqVU'g h&(zN?FF,?]\itU8Vs|EOׂ#i37K6dd9Q- I6ql&L.@g;2+> LZ` `lvu~McYlOLјҖ$ r[F*ڄ,  ZD/X' Nrh)'ZS&18fHJy Za+u0re?hu5>`~-Ib>suƽ'4_-\HW◮o\O.[uMbS+|,Qdhr:B&Q{.Rl&AiEX->Q{R&=@_܃$'Wcq!~+3j|63JomX\l6–RVXU=, c`x%qvf<G[Ʋ3q; % %Q3 TW^A{Gf6K/u cgA6M`.smATqAzwVӋNh~< E,:CLN=Xz{\9C'(y]jArV-a9+>OXΊ?/,gşeH&Ѱ+  ; Gip \ !:#t(@8?*=a*Î" V*[KH`.~%`m<VD4hdڜ 8j!/˥yV(_E.;!gp3 wa`4(@^RR>q"N&1̴"k 謀o,` X笀u 0ݜ1Xef q7${] _)SEDl%Hnt1ѥݸ*aiKOc3/# p+B N !CPf 'oIsꚓe)GnI:}y;[atB@V7KQQH 3\B/R \Ops zZd eM-;1sBK.L0L.3ͻ\B2p O0z%8(8Ë[ 3{/L+[)w˼,˝R1 .s2. sI[Տ4j V]U&0E zo)ʓPwQ% qCi\[J%u2CIz@y^,/ g[{g洚Ym{+CqxwVVtflլ8&QiyF_eߍiٵ/mvli$MC뼄 +VӌG2L>Kk |t{Y,*WҦPUbq>˧DLXj107a e\\ұԫh~ ɥeT*oks_K J=ZY_8FЂ5pLE⟖ب"" Uj/*M*{=Jw}Wȍ1,I[L6]-DcMݣEhzs ~ )Q*!أY^=z9 $3fLAza\s?hH*۝wdqeF{]Ę\mw,}6N$?~OwgR\X> @џ^-0?P܂k(S}PܛWaːXLGȕw -B%t>R۩XUvs-&]&18궒Vm+J`EԤ߂5=IE(h0fAt*>~08Lo`PQ>k??ްe!\' ; AqЦ<[*J& beԱerkkLf0Ea%ӵL1;eTM6ᗞ.B3`aKOqJ ] Ӛ6M2\sxFz5%.We G'gȽY~6s ,F lhSGfq,Cš4*`>P2F3OŮ|E'JiG %I %5 %TP8NkO6Zǰ$fX)X̉}PBz$YB<^MΠ PBp& k: ˳3 J CQZ i ^Pw6\fnt&h&Da&K66a2<&ʐH . l N +YƼBŒ("]A9VXC8T"'X&V$̷<wL<┠H l.&Ah|b7hxRFhfh/kk!vm=}[e(hIט>  PtH2^]F9:CM-\Psƶ2RH#Θ;G$l/\tON}Et2v4SbwJʅZWR,#fM?EʷCt}Z֡qN-QUF_$ʔll8q+d(B-Px.vQejiXk̼ c74&Ӈ%lm8L[l` ZAkdL'bKi#lkiMD 2f#YC̍hz.+F߱=c}=eY-dÄXmB cE$ṙBuMۂjx; v]0wޅ % /jKj_.Ԗ-Z}F֮]75i !'5Ky &~mbBO-󓤑mkL_Fb/פmGyZ~ y^e]0$/V.BNd9 g,ʉUM Yei^m[6[92XG#8ȅ4N4Ys4/haL|:L-L,14Jup1%sTi\x&r^=!biRtK7aU3p7eezVQѲ'e{p޲7մzE&ƖmexS\Ѳ׫iYId1u =[ ůeYBOB{~# ᆭ̓i6kYY pOӖkf#;%KCsّL?B^bR,I*SYj$K|SUȪ]RE2ۿi*9zC_TÅn"(B/?4-{ M:E/e9Cмܻ6n"kQw]MgDc1!sG 5Mb=, t^>u.&v!Y[쑟1$+"cjH탣Ö3?.3)OS.7?:g:o: ٿiq5T)MvBs`|uc<r1滶:S9 Ѧ7'?J몋bX:&*I <غ=|Ie&݄"jUQˢOPyߨ6{Z- AX$3f^j`I^EP8յ$2 !)p8RlJh*wL|Vaņ#n4p ?Y,ID%O1.ˌXkwOH)6=M##N0V8DYNu°R!]|Sm¹^b&)穭R{umw$&B~EC"edP;=W7-1.#eխVFl o}vvYvG+ί%gZF|,?5W 6wOg#W{KZYED׺^1ӹz AnPuLH2 #=臧/cs.̗qYweKd!:ggJu盧Au d̤٬뚉wV^ lE,l^O/{ؿHMݦb|,Z/^ J^L<<5KuURFk7eLD0.P/Fәn4N<͍ `靸8v_<dz85 65~케׋.y%/#\8v4Y!Zzr =BɄvͭiM f`斗ymjV^c{J|]Xy+0-m4Amt׍ao#Xw쪍{Rz_aZ-8N#CӘhҮJ ѽ98T:Y!`?G|Qy>PzCwdW:TGvްb#)cJ=O󮶆s"&'%-9*M}BI[Mu4ƒU:M:V\m&0*-[m^ToUȚnJZ+.w[\_oӹ 4S?4lСmwgB_tXʻ؋lê谌ҙ/ehm `~e^m-;W,ՄMgtƦђ |sl6q@{sV!c:tEaӾ7"3w ,jXYɫY]!KuV?aZA4[?}eAs7ҕꞲF[ V͞~bki3zЭg >l"8LsXy/ >lE &pyZٝ~rOd1=Ym݇H"Tk~TQ*{wT`@{^eԡRplKzFhYm5ZZTD6 @ p0[mjv5UG}({E- $U]TDӼ=DJ0VTK6[&VUU06X7Җf[zce ӻotfO=وPӲTn_cleb]i3%5,G%HB%MlE4:OPs [U ђrL4Anܺ5NyTꅚҘZ xMI>Tsׄ 8aiEKa6 6eT[Q-QF.ئU'D]bzEߴ:U&SfM)YTp!7W6ǨEcH4#Z=ľE#X9;PZZsT=i.cJA(5J8lc# QIä:6O=&bm:,V𼍥!C%zrY$8]_9G2}C)Լ;@Ol6ҨȥfQD᱕܋] bwQT[EO\#Cm:*Ju7Tj)n-ӊ֏5uAjt8v;!GP/֩/1K+f҆Ф[\> ]'vRsu.eDV>e4W%J[YFTB( :_eK{M2,2q5n:l5ߩ]f|D7ffY5~:1}Ʈ7jL\:4:vbW:4S="'wr3ODOћ@'/?15?|"ыXMxP=]=|F=*TƖCCk2/xa$eCoEIҥۅnS͇b` Kla':>ӓ6:ӗw=bk]V3ӓ6+ݛ ڦUfIJͲ\|Lo&rlU^ SaA}VLV|`]ўhjfB2cPnha+UM0|Z\f~:DRm$T!Q>Y1YئkCkszYH<j8SB4:Mc~RyHV7q,k9\\>+bjTrjҺ=*_GXG h7tum1ڴM_:`P7~4k-I^9v]ki= (pLi:*+&9GBTb1cʬ$*-KY;=mGޣ%?@#ڏ2R8gY.*}FIE:IeqEѮm7{h"Y ܋XF*[7.{>݆&0!6 01$pOI}%3ΡOH[zVqv0ѢDq$ֲā-o( ֌QG*JMQo}3&0k [_#JOl'P(*Z.wuuuqy<)V_ԳrV-e*6U.;nw!> 1b.Hb_*,jT\~p}vY֒)j4әG=|,leޣf]Zcb ʓ7s5 0XsZ,Tz<5ً'5+x;cbEDwV5,ӖHyлnDװGnbH[*q⫫%fxCŅRetfkNE=fޥHgp`w|Ŭoi]W!ˇB%>@"K_Qe5_7KXִ4-?Qajݢcc>|~E5~sx%f] GZB(KRzswzz_ᶤ͋yI^|dvGUNDk=Hٟ (21\Q†{gEkW<0j{T-Sʦ3fazj,IoI-8{]dԪS̷?;x6ÇkfszKTSβpЌ{P^a}A0뎽V-<uKԳLOkfԴ c"k{]"m;/+6o G]1TCM4Bq$N"G/^gqȄek])j+ݐDQϑYv#XkCU?$_{=oXUWjW2{R$W=pFk Ynxϗ/v޼{A$y%l<ە$["B$a?7ͮjke͓٢l4q]fH~@5!pvMR6Bj=b>T-N]IK(coЧJcHCx56g twhUlbuvvu]jinW6zR߮jծh81 (+M:vJ!uפA%bUE  ;o R|BR-WmUZJ}>iڽ`秶)Quj뿩-׍[ߋfTwy.N=IchEޥY~Ƒ 擥}w:YSr)f-hɑ7ˊ6{1);&e CMb[JSbSycm3.4)J慺 ɅW5n񅕽RmyB O2aRCm{D]k$3{!ӡT1ṚYb<ahQKEpp!NLBTVK 1Y5֣!b`U*Q%ܔD/zSB_$Njvvc>]hLVC*aCLr7==&IF#Lue^kIQ~"Z S֣MFZDK-ATNH.T 깨l=ڦYv6w |k?uLw*w\XVR͢Vi0TKzj<߱[uE hA ǫ5|0 b:ktS|֨Ϳ[HⲻiGza<]mȗ嗛Pu#w.iqCIĦߤ:3vs`QR@_aS0ڽκ6gLi XaOC5PHhjV$/'EvGD"}*cr1&T+vhZ10芅*CI9fXKE'߉Zְa3꫟#̿"V<aJ`_e>5c+_sEҀ VR~'Ԋ8+һx{5 ]cdʳBۍZw*ֽr}f-)ע:&?e]WjRU՜},}5>i*|_:Sikϳh0pWW]]RO8](@z] v>v=sCMEre[~- 5yB1 &&ÿESUPFo0C7Ct-z̗ӆ\wX ړQU'a?R1>L ,LNn(,mZ~լxl&#ݙb24t1r+ҪTBn! e텯Em5/tryb^{gF(ˬ(CQ}Qz(Q._r*J&e6oЯrkť.K]5"fh%Ej=g8.QmϛZjHx3UF_7:eֽ9g.~\=Rf?a.. ̱絚s½ۅ?hW9R:>|[pί<&vw'wnx}*¬sU[༼7y;_;?trx yU?]5]wܫ^U~GMӗwF'9O?y;Z\>kK' zTKףGsV}aNû˜;Q:rKWyo\y*UqGh[o׭޳J]O;÷{9o_\cԤrxqФmo+ܯy;Vp^Ζ*gU oW|<|:Od spby*=?t•gs[9|WEs,sh{CÝ-.F軁ׅ/AاU«vprxUzcݣӻsqr'"N>4.; NFk|}?B/s*Boű+`676f߮{\g/~^;-k#П:Ǟǟ?G̹(=t딕2q㫪 /]> GU:|'_Po5eB^1nt5qU#-0?B/!)V4[]oE|Eͪ)śrJ=l;iv+ջޕëϏbwQ.MW!圷_.7_gDž zTh|VizdEywfܨ܎jkˇiU][y_;"l{Cq#*׫|?B;O:yƩ_:yx^.+W +dZ ˜Cܹ]|\X)ӿVr^~_^p/Pzy*UwBoוëڇ[?p۝AOyx[p'zog*v^ÿ:g;q?<|)w8<|9p71BurxoyFq"ϋ-byŹqގ0 K9#O?vw#i4*Wգ'T[9|Cɑ+;Qi^lFׇ5_?ï*G,',=q |ZT- -"[~p TtA`H)` 3.(nTh`8|  (䶊-@w0L4'mPj" AAHVp\6/ F @x;A.U|P0 *59p힊x't0,N΁;7b ]P Tf 2\@_E: Ls%p&E'~|HBnJl`AcUBW35MN ML;c!2Vϼi,"1g4n#fBPv>k;5"~ZLY(!"iCq}Ouر11eSf `)XĎMɄt:& ,SQ H5%GǏj'L#c&MvD!J ,]֢3xmbbB"!؛?|yg.)Sҕmg$KL8ccO `G˅U*kcx߽"QX@tҠX=O;ӏg<̇t;4ؓb;_@ɴaǎG <IՁ 8L@&SrJ-I_͝ՕG3P+4+XӒQ ?vm7)WL8FST2ŮI>k:<;i9P)w̭+ <2gY6cyŐ״-dV68^]Ufm_DmAA8-FCG'ۧ#&$aLOaDtӧ@oyjJ9PWT´i{☀2:>[761rC)æt@cgjcJ;v:zuf_f/ٙ؟DȆ':ȋFf+ngiPZ1ׄxE%ʞ M`e1D e#&&'{>nq4i~]BNA\H@&蛕,83Ifѓ b1v۵>#H)-aƃiXK/lp,';rZ5tS&MB/ ѯAVӏqh6/+~ZC[ ~c 翋sc X?g qw] YÄvq=VǍQ!0& 6,<6qjy"F?|\ 6dkRfblb(1'|d7j #<7-&lz4Qya*α>m84P 3~m& ҫzt)6cc 8f 1UAa3bb'Ŝ:?8bX F+P} n:H;лs)S^[|$5Sp" awIn}5Q1'Ir}~ģ=$'>=$O$p4Wp^ B O@}9━?l9{XNje4;Z츜LB>< O!f䤜B>3ʉ2`=^#2H,+,P{8/]I¿{Ăe\Aqh{>"']$<)_ +@k%-dr})y6Q1 ozCA峖RR'#%-sd HXIMVn<e:$+ wJIU:J$]JRtCIjR!;JrDYIŸ( %9Q$)L}1ַR; Lkf ZH3 )`#@xYMdR@&{ W w$u䈻ZI`Iw g/fU & r B.JGiïtU)1OJS*W"988Y98-i"wjH&l퉱 t"ƯK/[A4}hW0,G g8{̃%Z/%.Ѓ;/II*"\> Χ~ 1C*YϢ]HI*qk#q>rg.{1~ "MSŇ5_IR[H}xH'`9,#O/ IAn>ddd >#fq$3 gdd,z ylh\ #V>9IM_ 0f2[ tJs03 rz~!:JrCIOUW)Y"KɬRL.2LJVG٘ =G떔SJFݕ|IA8O*%iO{:!%]L)#)£oHB)!/$-[M||ܴb,/K&)C(gm DθMJ.9#॒#@*[AU #‘s8##/>H*D7UKЏ8W1# tŠem$Kr#}Bj/HRypa=z3^#7FYWI)6)K4 ww\Uj9G/Sle}BFJ~rȑzLS_\# S8m[q# <$ !X=/%PfRn-ԗVZ@  9CM>l4 qde#sΗDFt-`2+SpĄc % ?--`=hSƑ/9"Eܾpd? D&# Ă0KF^☏,0W.#`BF>K)#.ıX*yaO{WIv_cޅ8Kɿq$rv:ٌ:ő; ְݚ#6U8OnH#%㓰64SN-T$B&S0K}3#=sd.< o`ۘӡ 2P&Wo9 2s\G۵`l5yMFq$b<3'nHj zH+8d2 e:JF:@_2o-#-2NFeD '#qQF8em:F#+~Ǥ^s6(c7@k#XJzˤIJX?oޛ}i$ޭ1Wa/!'Iº \0/~ q?m+d;ÑY60F= w?!gw8rv+@\Nc1ǑI92kr*. jQIXC(o속V"o#dkH}z2SMF¾5XSbM+̭g16 29fܪ#w^;܋`gހ{7ZE=9`bq!PqG0r)g( qV0CfIo{GBa A8LCr<#[/MQ8~C9'oFpDvd5~6HI+Y>EZN2֨'k?k$dLBNnހ]`6QNb}%'{^ z&Bwf5 9#˟s$ zYch5cHz*Ɏ rܼ,%]`\W\)+7a-!%A oLH׼m?e4A,,xG!d- ?0 ޅi[ook]`NE,d#cf]gBQmJ]I9 O$cdK>A7۫HF-A$ҧ ;oS!܇;{p5 }cMMoc {+=INIIp4>G짲AgcdtT)=N/oBi=cwسZ + Pσ`~ 1l3p<})l\ (p1Rhoح-@g? 8wy`+r_Id^$YhJ27}0^An`5-qQlЧ;~܈h*IʏJr]o(1b=9 | ػd],v&cͰ>YAx茂^`l5/g%s\_b_1O|N o5S9rMg`8? ?Gs~:q #B>S5ο=| ̫t_-}0c \̑kn)'7-RwGb>NSnd H^x j7WN? `=N\p<M[I 7p-դ9 `XA+5e`; uZp l'm m&@0 g@@(h&d rA0BjP4-AZ0q#0.p䀋 _L#9 E/+h:B('`1X(8n{!xHo֠ 0$ `Up</d-AWH=O: v g%Bޠ% A( &` < ?4>` `H [䂿?_P/BA4@ X V G)p\3@Q AK s|[+8.(sz`0`.Xր`'Up<D94@ D`1X 6m`8r%pJ*mM@G`H)@fgU~h7@7B(x2%|v 䀳  ܂gA`@<f4 l?_qp3x^U04@G  LA:pW]x3@0RV#s P P-@g 80,kVdS @)P D?&-xA`SKk=s* e@Z@wCH0$`X V 1p\ x(=HK`#Ip\wA!x1wZ @0D`*R l߁=`?8 .O`Eb)h:n7Ch@2/7g Nhޠ?bdO@:XV`  g@Y h_  0| 6oNs* A P \'h ^ ` @ XV[&"P 1h T0$`)| 8΃|`b \ ^%>c@ЃS | =XA󁖠3 ߂7p ρ"hڂncA V3g5`π2 yF=A'@kVd3 b oƠz`8&Sd M<d=f@(ă$ 3lAp\>(e@=rf=PRT'9bs$bd0J!Jaꦫkj( DE1s׬W1gIUfwֿu׻jO9F X pi[_~={ 4Zy@``.`!Bewx[obØ  p``4` h., x5{/?4/^CSI@p `*w< x -_1+`c@N= 4&  8p6GO^x9`'j~ `w~aр񀩀8`: Ts . (YwV~8- 44ZY@;X80prxK?k' [p00 00p4 h`iVn )+V~$ ` v] hL$@0 p"``B2 xk_S0.zz8HcsKn<x2M/~Mv 8P HcUsK7< x" ~ G#(bBk:RG Fhc m6Ehs@[6h[z^hGvE=Оh/7CQCh: Dt(: B5 FCP@#(4A h,jDP&h:MFSQh4hfG D)ԂZQMGm(A9G3PJ<Ԏ:Lt :flt:Uh:NB'SЩh.: 3Йh>: -@ hZC Ѕ"t1-AKѥh ]@ѕ*]Aעt# ݌nAt' ݍA z=A $z =AϢ"z ^A Bowл=>}>BOЧ39}B_Uhw }G?Og ~G?_ZL0 [x-6^ x#17pw9o{x-ow=q/# wx' pop? |0>Ãp Ń\ax8<£܀F<7x'#d<SnqI-t܆38y<cqx>*O'|*Oçy |& B|6^s<|>_/K_ _+|-_o7|+ ߎw|/ߏ(~ ?O,~?_/*~ +-6~?#1?ß/+5^# ' >F,YC%dC٘t#Mf;ٜlA$=Vdk ٖlG';ّDv&]ndwٓE&}~7C~?ٟ @r9B%AԒd#C02 prIFd i cI#Gx2L$ȑd2B"G&H48I$IJd:i#%ɑ%KO"ߐow{D~&_ow?RʨEעkut=>݀nH7nt)݌v-薴݊nMtړ;ҝt+ݍN{ҽt/ݏ}h_ڏ:HC0:Z:ut(FN#(: hO'Љt=NSQh:tf )B[iNm4Cԡ93hHKL=N;Lz =GglZ>9z"=LOҹ4z:GϠg,.gEt1=Kϣ "z1.Kt^N*^Mz#Loz'M }>L $}>M"}L_ Iߢow=>~H?O39~II_U-~O?ҟ+Nҿ`nj0Zlm[g Flc֍m6elsے`[6l[۞z^lGۙvelOۛeެl6A`v;V! ep6ճl$F1elkb6MbGl ;ͦ2Mc1,,ZX+K鬍eX9,l+0YYɎaDz,v<*gU6Nd')T6NgL6l-fsy|v].f%l)-cl9]V5Zvnd7-Vvdw=^v=d#Q{=dO3Y{^d/+U{V7[m{gGc }>g_/?Wk}þe߱G~eO(}"nmnmamivzZ]]ݬݭ==}}Voo ZZY[XZYl ꬡ0k5ªFZk5Z&k5hM&[SmMbVVJY-V[mVZ `V*Ye˳ڭkuuu5:ޚmU,ߪZsSSi< LkuZhm-[XZY[XZY[XK֥22r ku5j܄!8hPovr](vN9[LԌZS;xB>55XŽٶap]hU'Ӊ5{lxN5lGuj'q>k(hnr3+MB N;![sdmC0AH&Hjky^RKwҩ6:Zr] 426vP{4V>` 0FXGs`JDkyՈLú$QҢSy(f- F=昛1r?\7 Чvm6PdMMԣ-"pbnM0Q!}b3'"Dvd툆GL/˲ lkJmxZji'sܴ[L:^T< ;+@AbU{ˈn(\JD/ mh3nS?ih8Uv/}]6DӚK%W>O:*u#Dg?gwDԎDO:ȊɀlHUvXsL}RާS-$jqquUzU8I*TT]ȶkQ˅g O,5IQT:7vQ%'ٞ2XY޽|J7:zAM$51QyjƊ5D]b;m0 Cc+v-ڗ3H+NR9Zӛ3lj (9Ua&i]?)jFz {ī3B _۱HIw2iMBd1p9Vy[vC]זr$pv]NJ־ؘ, ɃŸb!UٓzĶkKnQscۃKLbLU4" CHXPCbP]2S=x5q5$2KY:?"tC,]$}Inz6N"HJxH-|>*ti9)ZT\)^j#$2 t܄\:!u6q.f;~1#"{$>Wv&naZv @;iTY$lK;k_(aϱx1]=RH&l]hK$XE2мҹZv<9{H0)p} <}ȗ_R-3ȃ7% LTf29!ӂϹBJt兓ʸvKX WK$, Bxc(e˗Ңn @hZve,F Z+ k5vcn<\LJp^ 󴐷L-"Mujlhfω)tsvd_[D1oIS瘻D,f9@Q3:Kx 5GtwνjؑΨزb֑W3zx6qmδea#&o0$+VҐ @3LVpl,UC[bAaǙSi#fi{FHB041`RQ0er+sVHsbL#t]ڕW(:Jc Fl c  Nᅨ0n, LZHEi Q+Fsz JԍpCC>P0M9G&(bhv&Tή*m x'.. 'YŖS` lR1ќԖ\JLQS%IBTK~3: ?K hb]bbyRA()49Ef2k\.h"b Ik"] :DŅL[s*V1VYa R܅xX "N= JȜj= PVYn.ߡ {fl`MkR^%DdevEf'Գvk."7.f%IEHsCNHb=gyU*Fȼ(/ȓ1|#6KB/L3Ni [t*syraCs0&Jy+0xrQ~ɉ/ )e2ܔН `yfdtS4[}; u ҞgI*T ʊhc^S!5SRyN驍1zJe=<@S=8lWp^ǍQ^)q=@S|<[*N@ |‡Z0֞2{l)豪xx5 N oAa5b.R!c`zI!s,VbLdk Fc1ɄI:Va:f*Z L7.s7o:c)pn6)cIEFTdIy|w$#mu5"(t\[%EOmbvc}֘bNaRHZY*FtQ-XU%L9!ls 6mfpmbN`Cabe .X4bV&mFčG'.j⑒ 1 md3@0Oʒ-aQHƍRGJ5Cls.jJ|k7q׬qר=ר=dӮZ#yFzH<=R=߉=HMi>솞QQϞQ.Hg3njr #5thӡښ[y̤ I&#) Ǧ20 q@Aٵɯr%+Lř_(d bF >(F3EVyϨgD3*X[RLpͻzXW#e|ʒsn)[MϺrcڍ]*I1'G rt@q6 υQE J54Ƣ,iikAS-5Ւ\&dLk C0JK^M$HqHF,i'wfDMpJ@ ))Uڨd .:Ő : %JΔq4Hfi#t$KiΔ1^`Qyy Rh/L FލGr xiiXfi) \88aq"3J(0ͅQ4eMDGFaƠLc *I1צi(Q .SAR+ औG!dV ɬE6m F>qWrGXJUn KIMHSQ^m!˧I.Ål7z9j$XcIRHswSW,8wj]N[l˥2~!+ -r8r9rK%աB4/$BM2͂Ƚ cg 65gJy)B3DL!4`yV*RW!Gk k'[IA5'[c4?r#*d잻-75Ql`˭7Eq~$<{nܿvnk2qWev2qy iӳyM &pg`c:A=::i&/*1*XKˮC& c6F .3b(6(<'iR3Ѣ 0BI.20FH~7 ʧ_k^2Iy$PRsbV?Z{ztOŞ׽J/ Fc a@pd- rN27T`L3֊^d>t:|/@B=ӊh^{ʾ={iU3tu29_)Rf^1gE~SWDҝ'.=xzЊh.^t8H7'.r<mqR^Ɇ~ A98xNHē 㸂p,RorO]R"TJ6ay{Dʯ'29<ꮪ4h@Hܭ_{;<&0%a/'X.\$LP˵B5 k%H!A]$rآK  D[5F}*AX#u,Y8RHxn:D?>>Ct=>CD/ O(M#F=al㸦&N:r߇߼)L`^'leM;^J~.Lq̚-rp+/9@)ik?å[~tZ"@]9ueOdoW/{ gYSVVά,]IUҕEc*C*V*S+c?V^V*9ŕi~o~O)otYs~ޟ|/%{~;nrFũ$+D$dT???ӟ/g9y~\W@ToxpF&>8>z9>6^YX]/^/oFԿ_߄W | ^Jo#+5:Nrɿ;(YV|VRT51qgX;, PcHQQQ  a44T2RRUIQVEDy3 Vus퍟9s5(J%27QrP8$)vrIP!*%GDp8*|KDpCPpp'$Q/ )g$Z9qYhv+PG4 ?ׄuqC4A?nB p%(>%Фl%SSߔJ151!gU|.恖̓f hP@%P y@ d@ EHx B@@kJP΀#Vhc 3ҎS ~TuY_ uN{|KDea)[D'jxZvGS򊏢d)S}Bvf7nvf7nvf7nvf7?&{?׈1Ism6?T .6pO3XlA+jyk@Oj[G*`}#wq1O;/nwg$P u=K}ֲXet* : `7@ @ `< Hx.@ʒ H|+~p)ok:n;nww; D3N_U?|x":ԣ^XYQ:Y{ܩ_f {Nf]T5=:zBuS-7o{U_>҂V/.m(-[sun{ݫ.Xֶ&]O362C>^%X"d4^ ߁>^Fys7gDzXj ?]/zޢ_>l|+rrȮo [=EgPA6_M _Ô-{yҫ13.[{ \rT|~S楶C'n輍&w.ّT\*j(p _ `Zն^钎w6˜fRغ42ռuixrc(QPx2 MEK5dUN.:D'ׁH u.p <^F,fUV\w<,-}`9(Nׁ##$`XǁSm{h |`(08=P,@XR RP)(. Ht#" -^E]Lf;@3 à0AR5Hi²"N/C&b֦GOiw1HLؿA J`@m@/HĀsP)OAh76x9' r/~x,rY_ /XT6vY{l&Gy<|^v)m`<{5@7S-3׻r{ }oI[>޲_|lpuŕ~ӖYy}Awmo r~ʢ|3yaw6oiYti>e ڤ>Ks kG>{.yj&Yqd F8_!h%[63>(fWfä0cs&m|tg[ݩ)c[z,LG {e_'y:#[oE3\ؠqoT0kX{׮ ոl~ˋsHs{|շZ5=7骉_ cg4Ψl-yCqjY-sBԥ5~.?) 1|߃'^u9[6_qY-cs8x.&'o/M}ݽto1AL]M! \Fb4{ܒ7#K>\v2[}ݖ|X:~xu(hTu¿<wÐ7-;^ko߁'1L?~3,0YYSQ~\Q{7*OULVb_ 5ܶ|HҎEd27qéi0kaR{7&|O~.V_lڳk5gz]$YC!6ix5#}|&=O{żޗ]ңnU;}3-;>H€ki\j84l'W^VhK<~?@WrpqaM[܇{b~Q/zSUyF{;,+ж=V́gojkߧ[* ᄢ:ֵ~Ҁ<:5 PuxwP+(w,. 8RP9h=^'5mU6&P|S':FB,!d%YEV5d-YG֓&l$H3L$rLP ʕr)JI()-@RAT(AM(KSTQ*Jҩ *ʢ}>N>kZVjQ-W;.jUv',/^u^_1(e2QWi e2GY,R+*eNi3!59FFQ I1dhHaj`L{3AczC/QлDz=ʡ "2RUT UGFLPKIsaGZN;δ ]i7ڝfht D4:ѱt+\%WUs5\-Ws&k;‘\ U.*MūT**VJRU9">UʨS5T*?} XnT*! Eo2e@RC%dC2"cJ"!21dy{s=>ޓnw kv)oͻ/]'||9xC-{Mp japVx+tqC,D+j%l+xE㤯q r@͑;"F(]D77E&+D)1! ]94DO ]T iZf,pg+`[Qdv Q*po<Sq6>K\}1׆@nWpdqJ# x!U/ DŽs P#|l"B7Ecĥ#WG, )4*괶< j-|l8+-*cb6ᑾz^H"I&N $&ۑ1dr6ԧReE7 =O?f`JfQsqg]P@%l&dg[=-n1!x(p>\gn47[mp}D~:_̟ -6/S. PpFѪ υwWAO4mb3@" $ހLőC' S1YZ 6Kۥ!YtGz(I_ yMf:TƩqh=B"<XCl$Jo9$']d1YJ!IʖPnT ST7^BҶ`f8 |"T%:ؚ }^(/ѬyS+# |V(_oȷ{#B~-?e}JфIge;2TTUM&^B0*"H{҉"Ds%s!ӊٶǙx.o'8 : !Я1ò {G*I_P JB1,bʤT=>,؀ah& * EP+9W/}aFl(vɐYSJ 6$ƕ*C]>Qn:jCT_1BrH]QhY1znB?W-6s|TU~T0V৾5 R-QI0J$ǒd.IRTNtz+03C٩eAx%!=OS|{nY\l$h-oˢ[٪R hE-Th' + õ-eWw}:+xbGL"pf s퉣2%ׁ hR<,IߤP-VwՇ;k^5~t4cÞOAOvJ%r}%FT!ADQ 6?.Pp ;h2]Y&,PN(ԆjK[ƫ[CjLnj- Xb5̠͹m& [r r=\3=ŗgTV):"@u{xj-83ތszVùyO&qd*kּG;R lAusz+S x'e,~_sx'>-vI9Y;cFVO7gU>D"I#i*zA݋A@3@{Anfi~Jpw):dYTA!{j:\ vό&@'KBTl5ӛ:u$;MawpxrO> \y@4tW!Re?TG>hBa4GpD?b"I9\ׂBR>,p>m´f^l *",*Nr;FTs0, "ۈkMIF!@Me&%H.5xaP*苭'AvUkb;#(s5!ٔlM"ғ!T=ʉr6R"}#nrEΙć'׉AsbНwAaZ* g{\6F1rOƅpɠrQPoU| \h-B;$ QB0EX#l  v `R0ЩLP@(;ЋR~}RTszMO?ccVȄ U0VAk\ E1US 4sa„1u L)@{ } ljᗻ$Wi7Q>/X<:lgHG#"CCGQcn;%a ca@u%>d0Bd,y,!('=+%:PN~Ry%ـ\ڄjMQ?Ӈ3?ӟİϬ1;s8;Rޤh+"Qg>UAbٹl]'TܟG2qA #TpOCƥ\saE*jScqJԏLe:b681>BEkwǝ u%XM!@6QӨ | > ~2݅dv0b=n}pWjf^|3-4k tz?#w[h!:/cmAy/h?O7ԭarEe㽷~?(ڄEצ5tWȚ|C]i l` `7# גPm M eLץ8k.1>ϱ٢|kVD[hG;.bO/T`9\G SFn˝nt EXxO\96-G%K2eB%r\NtU/o3tkLFx]gb2R7IIV)+1LmĠh'@-sׄ>߄!X[3?Wt=VUM./] ΂ *JѧYtUZ*mUvTilGWc+V'8b?/h6mj-} St}/#] ݛ'Fu0r+ Y*p]䈻X/ՕIr4U^^`2P?Juv%F;vM7[d&y9BN k-ud5AD*;2,S-*-3 :<0d"H$z[NѶB@֬`6'9{aﳏ!ǝ.q$r~}Z|]4P+^!<'(:W9PF 3O'EAq"s4\[ZhjB2} m@@$3c!E  ؉7`v|7N.B]+ʄ-Jh~nX\ ;'^߂~$E"i44fit\GvĜ d0F'>L$4`%u=N)eI2#)Ws*-P+wAtTS`ƚRG4ГȒhA,&r Y"D{ՌO13yLYAl-+0*||k' ~/w|1o#tqZ\/n/C’,yèc>Kl/7{5L~*˲ک)1CF UGS#?@ƺyF0 j֢# 7El&@9OR{ mM; JK;b3pTV-hnW7ol}7[_Jϵ \W1>o^acsNu7zaP)Zš!.jxu:]TT9h!ڈlD,HAi]%H\hrTj28yT'ӏD3cTf> ~8Ty:ʘמvF l2;] Ng[R0{ݚ@l./CH1Z+.eT+cVoj )B_3If2/#۔ b7P-XSv=u7B;t4[Z&d{2O)RV[Xc*XZ~@R <h5ʏ pR[w.mK2*t@=P$N%af9FRK/݀i2rpGkcgtD8-|Dqs!`]0EU'c9ҍ55pZ{h)Hz[=Ї5dg*[nLwAufk2vxZ` {Bͅ9o溃ApMΟ A}N@q͡:R, |Z._>FLQo@gKOOd-9K-$i.-HRT)42'`` `vT:7 z6OfW"7-yTSèDj&( VT9uC=^SztP, 6@fL DI-c*X3hLǭb)঻ C9p,qxG3Tm"3Ak64P:g;D!B @#g//LB2n?gpcد7N쫆Qt@q|9(^*wBwR^V##i ծ>x g`J\GS\5]|*/~dzX* ,sP:, y| TK`AZG^mWEB}ȠՎ?1bz&%ȐLOjwo6yN™lE"yC5)??ZIP)<1J@PӶ-}+l ;.fWu_;cܔ\Z a(pa=>HƂ' q 2E]_Pqh,:'GdMH'0Jqq5H7G3t+-bP RIrbd4GwyHoahv9m [)40|~ؘl9{1µHǞ\/ɍ<l/ʹ 5{}!Ǯb*|FB/ݕu<ԞaެMtb)C'L_vTnv'Er{Ed[ƕ|B'Tgʯ (+]9bL}PIſwDk;dJc(TWbIdC@> %Yrڊvd ,P7 70Ѭ %r.+B ']JU3R e0W&lfQN &Dv*rՖxqpHg p뀲/S_IZIer{'8 6dž]e&~InwC;>¿ፄ x%G@c>0|xzp V&I:t:iݎ 4}6b-!ڻ/1ѭMFIdGP'P i@fLǀIOE ~b{ .|%h/ }[x 15mo>}nR?`s"> G I.`{fQ_$9oyc(mCWӺ}A%g +~Γ?CAnSt+d}hz }Cil7,[1 gؐp\c>zO  pw7),tS,ȕG VOP }0%VYfj}:HjkDwDD^iN 1H'6d(Zr3dIP2՝@Eq,]H-3C+_01;bGB7WZ ɍr+lP9=P%CcmLp' !;B@,bow&&?(5KA)60qj)>z@^e,8T*#4|"ʞN˨>P1t&~ hϸ329|c؇>L]ٺ]PpW| 93\ob$t^tHz [^P9F],)]t9ӭWEv߲6nP8^oMEO/ t`"8陴UU (IU^Px4Mh4 T%Q J{ UCtBliL[SEMc2,&qV0nvf#رl1l!5 v pZ~s7L+d!f18PZ k$ tB> ~AAJtfzKk9KmġTȋL&~ak$/NJ~أog%C0<ӛz1 Ug P:[!B(. Xx֚OGx36# +afX) k\%hA(0eR.)SyPIhEJ6|Hʌe~G1@fizP3El{zF7! E޶r*ߝA̋9~;K#>sezT5FD"n6Ʈ8'98q5; /΀"]ŕg;tdc+ՓRSJj+ v[;ȝe9P-ɡnQoX˿=è\}W`Q.4y{˴aiVd0p#m|Mu3T k>z '%Au$brЉ[JrPF3hĖd 'eҋ!;]ɡ^: FwTLHVnPeMnHe(O7ҕr b 1[-jMu"@5aGng&8=ё82 ce=. *@-!txLURہ !(hӠPޏlB7=O:]8z.^KoKugMu'2>pO{c~}dqx { ]8,w=>K*^#8 IBP"T a9b)xbSM4MʑJjTv;49xZ6՝v.j:Bmfjh8SK-#aPJ5 )BUퟡW> ]!D"Q1 r k]ndG2%edݫ z| ^ґhC@ϡrnQo=ڎ_F+菺S I:-eSPLasR5툇4Kq56ܸ0.Jmn|G~FR0$̊҅|P> 7R#Z^k[kXkUkRkOk=@]Śi 5ZesD.CI? tQLL2PbF - j~O Tʤ 4N%|6wA7Wk󘠎S$5UM4C]fBf׫9j.>а%R}}KR33O26 2D0rOz*d,j=<^JC6/S*UM=5mGӎ]h7ڝF4 ٖhO=[:!t= N,H'ѩt=N3.2,P9t.ݿ~U3%1y9\*|v ?_7@3Ok= X0kV AQ W%1p!Ta0p0K',Z-d"l5&L7_]z9aE؂BLэA"/xK|> HG҅t?/.sd9L TrAfuWH?Q_}LJ4{nljz,LR݆/#j!3cVF4ؼ^ 3Wj#W=MSv$GJ/6jTC]P9OO8;9͜wQ-Je_[sX3[l.(CD! -hZ4UwLȡB[:H`0^'"u{uiރEFMX#+M5C?#FU-*tu.>%n|re:-dIQ//V_s4K!oZi~lZ )2*kY-5ys |6,k.G$\X[E-Fm[tܸgތ/vNI:m^}qo1[%zߞ$'O>Ԗ~vIC\o9}co =~i1 lD[fR֤E`Ot)!/Vz2~k̸֗ >\ -)]##PQ[;:zz? OQzxbMMk7bO:qfv_^6߲n][)Tܹ;|,'g-5>߻৯Zݛ:k'^?5u, w6zNA^Ӌ;ds; 'xؐs);:7mslhe'; Ƿ ^F.Ý:/pk 4۵7]KMz.`5 ׿0q{u!A%N%m6uLo8miMqV:9_Yh3wm sh92<>a? xECA螒H%o2'Q6>'Оxz{a07i_L"0v,P{Zg9n:-G%T0q0ơԢ}w5\t״E˱ uGo;NQ:B]D,tۻ I@#/L1rt9WEFkQg^lfVߦ-=y^ep̞a#nV}hԓIwtJVj<Ù%c=&u5ݫpIҋ+] &WLˬ6|_5"zB3eգB8[|fHi/\Wx4)c'd?3ʟN|gZFt~1Uë4oR ".CGYھ]=%86Z46jFkh5^vO O2G[;DjNܗAEQNs|/(X׫9{ņn*+ݹai(즾Gt7skb6I5 /WrP3ʶWf?z`|hmqŮ*v 6H !j'넏x\T?Hoɟ8 K 56\zDߴ?} a.hU'v|U?ƙwk|| @hxۦK2uƵ=b8@*^_O%ol?/b߷泒63p߿?EX?Z5PM}+䌏) :Vڍ j hDf'ӌkޗ9i1z-/Lii^+ +O{F6 iPoɍWhS69佳wnVRR0%NۤEl1,R<9pa.r=Ά6-y񴹱_4w1:fuNs:>vr籛/v^bzq^Ykǩ5xw-Vt|3wQ<: 'M,;%[ilOU4Y::,o)tspc%;-Y7{} {Y(*uVgQ{ň !LjYOef}5m;^ζɌ-0Wй'fۀNƩK~Pfn\}_:䷺x`Q%y.;õs6y6~F.KƎ]ިV0{t@QS_ܳ2۶,gtxtH^cѯA)]kM{lk8!h4+/o'^J5=iиӊ%=v^DWwy}|_ѯ@ PKiY:>tUA. Microsoft.VC90.CRT/Microsoft.vc90.CRT.manifestPKiY:Sدӹp Microsoft.VC90.CRT/msvcm90.dllPKiY: 6Vf Microsoft.VC90.CRT/msvcp90.dllPKhY::Cr  (pMicrosoft.VC90.CRT/msvcr90.dllPK@ Pdist/nsis/MicrosoftVS10.zip000066400000000000000000021035161176363201700161160ustar00rootroot00000000000000PKTr=cSgp{ ~G}鷻tvMSk:]ЩJ\MS>{W*{u]'*QWu:]GL< o AiIpdvA8& | ԹJf W~|7I`?В0.H;I4?V"v"a0*9RH;õ$UpV`M?2'ׯx"q07A~L1*27?a dI{|! uv3g͜ &ŜƂ+5A) GVp=Q+_?Ly[&Z{WVaat -z@ i?~fnhuHe.91I3Yh{Rg煱σ†J3*/~(,T3YQm,ØLSf cjJs qǘ[709ǰa<^<$~ψ=#3B=2}VX|I~||( hGGu$~eb AL+c''%In0N:\6*xNlZ390w28B)5H W7c;3H6q{('9`Xa!'>kϠARLsYd5?Kb8v <~d|;0_\zȂYۘXpitZ1<oˣYuWb?sae)&|0ˮ:,K%_DTV"qVj<"!<;M<<\7_x J_TUnva?=3cM{(R=|=-u|[4M`4g'K{̖En g#a ی^yh'd&dt;o &:ۂ_/?(.t'I_N2-%HfUvemab>Hd<y@n5"3.7t+lâ` 0$ )Afv%R)50d[=!L=*%U/% a KLtjȚ\+,ѤAaeliNYn#Y _M:(Ee`gA$O:AH"(BOXAZ)4E Oc&9w   M 휼;_$j01Qkc.U=@5xH!+CG+N2p` Y_ ^蟟׃A}1VqNO0,'&ϔ!%ia@J)r?' ̧f3Eqď3p 3Cqv#f#΀G#Έt,סQ/ޔo?t_~mr 8Oew%!뗇4s@?жd0 )6kW ~q~Y/bP^# a6uR/%ek|ub@w0I~`ȴ<2tI0ÛIYPkeaWش@G,vk,&+ƥH j/NV n_.*#35aw)p86S6B0֊Az^+ A]@&E)C m&iL >K@`X2iB:DCL'}ʤyTCր[`J5=8k&}8GV ⠋R `j(@7H u%2DQI Ο]XȠht7ʠz{Tϲ`WIF1DMijGӳmt wwzI=(L~1]4hƒK6k"H܇ O4f,-KEgm)ŒȊBEǷ YhpK$'KGqSG5✭ .$.j$]-:y+,44 k`uGHДC08:kt #eϏNB<'(R GfCu0{}mVHQ'^&'V50$."z=kE,[,كa!3"'AҜ }IvhmM8;2TD -ecYJ{ #G]F6p;` e,t5? tXM{ yb__wd7Uƀpn D-x ݩ;Bb?  vTS[\|r(6a,+=џP3{`k^HLާ875$eE˜G!j):w/ě^ `Ù_#w`-9DmRl0eDE>KóY>DBB͒Ũo,#:2K d7P7(}@ Dqn}<>IVBIg_c_yڀ5k7{@A걍:.prׯ> , eE'Q(Cc<12}lAO0v=bڱe۱j,k[CLYki1,vp UFB,wYI7gK!7:e(lx/fRA:npk\$VM] 4pu4"eϤòY$pz~˄.CrT,OڦD! g7( nms2Ŭ\ȟ \䂬il؅dW6RfңTHSlߔAR5d'89Ľ6s@̏O8",v;vN/ΜKqQ36}ഒT Ηf8T찒S@~]5hdAC ڧ&}qZAL h֯&,o&mу(6w=]޿jV+j4(>w]MziU<_bPNvr%Nؼ6Ky8,0>O.Eꎂ$L/ z0zB)pNqnx/ TcDs4nCã2#wF@j R{ )dpxp]W*0f2fP@ږ<)dbM=٥&ݽyRafr>LN)4QM^>&GLq4`\vie*bH*f$'mC?0}4NrЊq'I~ m=`J˫OZBQv z] hVݡ9zdGJD10wt L"k8XPL&_h7 -fP8-ТAYtTu?սYOq_I]eP"\\ &G!O%C'-,!a Ϊj>brxf=avǞ7ӎf}pt>jw}a4\b9WU6s9b^ k9L= ^lxdW} KOM[;.(cc-B}j/7yj_nc ̈́lt#%z\`h] |JFoϓKyri=O.WKP%Wp)a@iH>)#,ey)!<?ʛq!Ewɕ9R ] NJ5Pc+5Dm& 0`p">S/#\ g6Wp1-a!E!Y809ҳWyښ˶;;W{5Z Z6(b{I4U(Pc&JEp4MtYYRRl>D96g7(T/mgJ>@[:n)y"qFP%Oa'aΧ}?Iimlfƾv_4x^imlRy3Y M9guu\mIʙwJVL6nNwӴng鰱e/sD9)ήs8G9o6p|9moh9Zg? m)H_kt)r8'l)ڿ9^kag]9ɉtw۩duNb- Tywl6؃āڜF\Ψ:gtKœk9oX*a}htmས$g%ʓ㕦$}_Sy[^I:I/@ VD'G%f<ӹ8<8V{=5e;g/Ukݗa5˾KD^9/{dR4raij8gbk:̈DuhI洇p)>>3נ3NW;B=elffGS$AaW_:7? \mrG3{-/0potm*4_$,PYD; x26q_@AǫiR|VB<"q;4z.7ޒi.eͩMEl*wnJ&,DiC%uP&Ku&ʚn>XjRzj^/pP%rJOBIɚ>J0D i*vaL@;zp2%DݏNBo}8g'ƹ9[3粁kbs$tلf.Z?K+"pY8m~dCaiJj۵U,ܴ%O`U)# ]/X`׷;Nʱ/­. L:~JJ"I;)^ӴP`UvxND%=j Voɢrsܪe|QfVd+4&B̆0'ְܙXs^ЏJj6\FI:Yz#}Z=ANTYp!ۼ |Iښ?G\;UQ|F%ĄctHO%vPM5ߎÿ$9;es_-hǃϵ湱yԭe;29$OyȴF{l:}Zf>gZu\&5]K׭;S:3GoZ}Qm5a.36 s\-7{cKZܯc?(۞FcKI% 3|H/Uֳ=NHcwHj; cìha `w+>T#ʡOso{Ax7,۩{ih&e9_"9hy +kRcgV}Eq/6 }UuHhmh ${#RFXjnogX*LL7ׂGx25re3k%?aUz?^s%@?'u{G饸>W m8/sFtyٽU@^q\CNk4T]S2ѿϹ 7=3(Еbmq[9xQarJOY5j"g7&m~ʗB֬ 5, ´՝]%g[Mk̖%ōC'؋N?vyiϱ ockckXiEҏ~M?. 61iRil~rYd_ճw)J_rMLދE#:R -->~ dO:J,mg}vϘFFu2(iOQcH>XHK6C;hɒY ɢ @:[qf *0cd8CR n|J\_z"EvvldϳX^n608 VAs^Rrn*rE?ȳgs}DaN"f? Tz2ߣJ`N7x̥ ISo Qk /mϠs*n4wT`a7 d%OVS:ٽR]>D7KK}b_xɃ@.-: žIv%?tj<'5f'ڕSb>N>^B m6i2X ˁN3BhtV ո6ai PZwjWY4]:t[Kר}Z_2RnTU *ܢ;JU-T*.Re,U~0qJ|d9wx`Muړ՞l{wQTkڰkړ Dnړ V6U{C}בxwO(1^j# p_̻]a}ejKqWBܛg}yTon_e^1ߟihI]?[mye}Mrʪ&฿o%/a? O'VG&9+i˵MZqɚEM͝cWK41;::jqp3*5glmUYМgT&m~T&γ]";gcjY=k~l"T'833C}E^ijFoQ ܜ˶?'5d0T,ɴorcհn G52ΎçH1I_ 7}=SbKg0jq ԗr|j*UѴ~Z+٘,@p.$zs;2RʫŎa9f~eH\a`H$7[udt2%~_U7d%y_TR -4yXv,^UtzUO6kxnjay-efM- ]~I3ktW\(?xQ<[/8{ (X#RAfc*qJk%x!?PYKd%vhaiT&?$XJ~UPŹ6E!3 m&NV-ZHzLl1 y,PZAO{OzXjf.>` ?8屠1t T^x-|FoI p^` BC<)N5}6o(!rDIՃF9Zmqfv4&OE W}&{1 8|۬Wᗱ>L@  vQn%`j$ɿ!M_WN#&#p/.ۥ8 gD+qs[2PDa0bfqEˈݟ[JlkFYNQ) >e٘DwߥRs ^W#~A #׿C=#^G_M~@8 j-5 jڴnh cthK U}dϝ>Mo.΄ݓJ\{9 Q JDb$hG#cDnvΉλ6Ƥ7%($Nsd4 .+1ȱO@&e](Z~kc%saYM2ڃn>uUKgK^iJ,^G3/HTsI̕*?;hBT.DͣŸi1PvIiPxoL6\si?a ^+CPݭ.$5#h^@!E:-k)`kN72Mi_hvݻ/2;>v|ݎCD|͎xTx~ȎON=)MƯ+hަE~Tל{4581C5Nu,X1+x8ĝ@0ٙ]uq4t~hlg8U#~b﬘cP##5x+w#ݫR8#vdEKPu/Zsr6R͙ݜ.j=Ȓ1MU8e :a:Vsrd˴ۿO>29}yfI^?0IO*kO 7W;m}bo߇v8>$ҙX ^Lg4_Ic{g8W?Ij1\&WߖR~jV XGg\%"9+_  }[T2-J_Y=z59 RL+1Zjg : B(4%?H 8-9ϭ;d;WP۠ax.!TZ[QfgY{F.˓ǬMB'b{[fۭеэk8]?s{JbL f-TCJ҂&@ 3 i#!<ۃՏЌg }ij(t.Ubz{PrO]\П ^bGkJ%HS\>֗O?_ĒxwG j@1zT"i5EUe>jAmCBZ]]0Y\{FpܗnAflL;+ Q^iӏ0)pj^|=$7#,caD0S-4A|,1!F:M 4%7c-D~GeKl4H⪉' jP(R77la>qB'EyYėI/\TՏdqk8h<&t~ծz.)<=&7&h;{6G}[: 7߃Ѳ7?2[1{sGTڜ LDjJs|by~e$ Ѥs93jR6 [@]QF3T\1w*H"K]ퟺ p*XZ{ n7D)x"K'w0&o<%SW7D9Tώ[<E|@a Ý%`6=]5}66$? KqA[;EFgg {m ? A_Yq~|6/$ :u67EN3SN>9O SĴjPs1$<+}0 *y`:k!E8w2 l`A-NhubmoC,iKzS޽P؏oS%'-2_1 0g܂~J0Lwʨz;iq_W  1?yƮel_Dx ˿M+Bi:2gxY8FqƱl~2}ٗvTD#okh'Q(g'a47ZN0料>Z^-ZU+s#jU~բ_Md [{.ǩϩh\ADk{aZv i ="9H"A$;G/ےy\+Ɓ}Z\P>t*:lBghWiR`PZ<K♂죘蕕Ѵ=Т `AusUt3F1Qknݢ(V ;6懡/80 E)0;OŖ BV.]_84'PCZAƕկJ!|B{!kEW,Z.ZWӃAن%2vh 5,]1>([=m_R.u$bn 0x~w\9k0eXI΢s=T{Zi5Ravװę|̈'?[gW%c+9~O?bkoUc%` }:H[_|%U҇Ra5YJm6d^j3sX;)} !DaԊBeu4w8㬢ZCE`H#v*Vqw#:]E#'`сLXI;HĢ1x[l)|+%[މ "nw ?]J|oX_>'tl(DY4-뀕CgzQ7E> ?=ӘM ?p@K9=\3[6 ):*()1N8@J&y^'q|}1̚vvro+D_+^{co]&tgț;"Pd7R̻#3#xnmWEG+*Uq& ek*:Y=7]t7Y;RP+mx4k>:sofΤyͺc =^ݓtǏYR:bZW7B  뼽S~J3a y1|ZKA)tXSi}AQl$^<n άc޿] vh1<=Os]ls'x/{Q{{8MF޻n#ON)L-{q"CHʊm41;ض y='~cKsa.g;X5T{,;B0Uq@Rd0I4܇H@(v Yb7mPPm IK\~b?-gh?z42[%9dБPT#v>>Uӏ:k>oð@hW^{o2N"Fd_ S_*uS)͑xʁp?,`~0.)K'X AܥxaؕsJ9MtOl7%"u^K3\Aݡjr=$hf bd祹.R@׌)26mjtzudZ:S4RhDJQoB)w1d A TAP?UN@uP@T1`vX*kvx0ytdY &כ d7PDO Z L BY2(1ikmm&ZK2Û0$ɜSmɤ3bE(at@HG}/m]B_k_}!!\IW >ḱҫHRea58&nm[ʤ澧!5F[iG2}' [%2.l<I}q@,gP֪ m R?EA!>[m>Ys+3F9Yۍ9@H/{ q'O5߄K׏\kr? MՏWяc/HN;׺kvS ®)QC"Ʈ?eܗ&9$dm0s|`Z/г7F{eq,C *[Bp7.8X-^߭4v˪T_|q-P&vQI'X(r-E,jCҨP 9o2I^ckpa:\kJnMf*0W}eTѾ}}_ۡn } |SUp44/,IС2C IEi(`:gtFg\Ggqam)K[*ՈVRPy9$e)Gws=s>' oŹ+Ha#wRU/yBX(/T`#mj#s G&CG͜Ώ,#[eGF(}Jv .R]Np|%^GpBxWSBC]c\Ze\eĹ(CJކyS֭{u=5Z!wE7 [&j6XA6%|Fݘ0cYW%ϖvNhFvh*vJ;h@$>vcģLFAITr^@JO7#WQ3Hky-uweu^h=?ϼu:$<͏QA/ J(P/ZAAkN'5'(s h՝TG̿sK+9>j?%8_wy䞾lMK@@slƲ.4v Pt>!OԽΙ([egy!M*B&BPϝ8 HycrX4_ЋJ,)ML;vXҐ (??(0+lwc)rj E^3/ ϼ6 dS;Ʌm@Yl@NhZ[ M {e&зɣ̛|)ZW]51x&kZT3gW\z2c z9 ^'DHcR?#^7Mu dT>4)W+A2Ig׶skA ^1כj*{xА3^j5!^; Gt*W1Xw-7°Tr)桏3{  A(^}ݭ/GCY|gL{%5СyQm@3P^"U0@F:uNN4*_/eX$Jx೪S:CI_bBE#EIJE)gOݰd$ۥ}G3J&KIiwf%#WKf}TUIK8c2[1FFf; lC&&2}a*Z2Ro(ZO'tc*#m`܇0f!(:cQ$?W$M >`f2YoVD3L|`-rrT!VARzI͂C';Ů,h(#$¨?W|̬H鏅WB M xIrO9Anrh N .;s>WP8 *A/%nS`W(өsg ,J׆ì <fLPbDx+JMl5y*sJ0oIラ+U8 T4h+QLjIzIw*σ)ܪ BrTѾ[5Ԯ"Yz)`{*0qlXeTT*'F2҃2eG(ӥ+uMj k|,:꼜oP{-bFY &HN=[W۵6E -Έ #=qH9U$7Z V6ޅ6\=pX>`DlJMB Te N#9 c,Kx['bxF.=p UܕV1(" ~8peb+ pe"ev^x`}g5~dbT%{q*0|ňj\o]Ɋ'Z XNԜ`D8#FFpFTqz#|~]|~´pWUbao|w-[y 2( ޗ*gZ鋣*F|opË`A]i^I6|0nw&-&p̏?e(`.LpWݤ-- c:H]nW[H;<:xO SVFGԯq~;n5܊P;vsDeT l[5h>Něqku7)Z/ qt T oW_A9p GPVӷ,8H!iKvif4_0`&Xtb% `Փ)Q Ԙ()֬T߿5tw(h& EG5H+ݜ*gkpz >:1 Η8l{w~0"CH 3m3 ;MIc8~3)_Q~oBRUiUJuPNBHVI`ƱJ͍T v[䩏2KSOMܨ\V=Q(Mެe%*8Y%96¬[GbJ0dJ̠kpl7|&x0` b?]9-Of#`ilBc`\>H[r .KOOZsR 9{v0 lT>/} WS(H MC`OQqsb:KJ;ˎ ;# Gԓ]Ę5^BQn)*~~`ԻѢ_C .7 0u@jʛ(@QGm!W΁DVҽZTҟ. iZA0e ,T"8.~2 %+J2K3}*o6Zw|CJؒ*[-8FeUt P\q\9t Hu6@В ?;-[,8Et]&5.:?˟ClXlz3iec4b1id#*!4Lu $t(Q_jtT]2H"=`n|rGFzdWm);voفg`jC8G=>P=#1=ޅ3a>F+{<T -o&F&.DI)N1 v ثrXz=>VL;~"Èw!׵# loWҧ@]7ȣUT%p+-C|#xb͟.Si8k2ֈY0(7]*%)F醼; uL_& -zq22R2_g,$^j"j0jfFPN$( 1,_TNQ)bM\Κ6a'yP~PG6QڤurtĦWWŦ* a7,. B“4xe<Y6 WQ̝Xv 4ԇ͟`7T#ntFWhKݶE 7+8*7qzxK@╖;]l4Uef (qg䳎ӂ9.:,^I=!E,6wqoC B0-3+e)X&݈ҝv6l`0*Q;}0ma<ә-;ZQ^G}~rUD%MBFuOJ7FtN.)@_ȯ ;de`a > ,` :0s߲+_cJs!ymM0.b& } Q`M " u{9,4<>ʂ}@\V.Ndu Ɋ&m^x+ڡf fkgE24tM4f$RtZg 2ZQK"-LBȭ3YeQ?OF)z{F3gbW`bjϛ*^gZC&n[R從X~ ?9{ > ]T/45L0 7C>񽢄hջsn^hRP$Ju|FS FrǍ_jz%>e~Bf(Ry\j{29=DC.'_E_t)8N'NFt}˜}`\#vP4zhK>PYg>~->PxH_6t%}='L<=TD{¥>Yۅq]€OQ:yv NW?DȪ>E>9r@z lWBy%.7y񲅎OӨĒ2|q0_*;3p[;լ hbl'UNf"Wa3`+"V@4v֯ ׺.=J}< bb6@z_zx/Gr"T%+Bpvsv7«䳘?ɼ4o/}7b®8of{&-(ἵ[AZy~<=3RKykc-hYvv43S =B߾ӜXh{)NKBTdD2в9MK'='duYUiAiM&O& xHj>@k.{@[lQvwhR6R h#(nQ<PE׺t! N7Hܩ gdcX8&ڐ**9Ch ䷹yvvd"eA1kc峾pS%6 Nb7=8N7 ,o$ %U̼p.[-Z)|gfgf诜9D0ӌhb J/[|k-].dҀ )re}'%9䀲+Dܼ^&@7Q NEmy[噼ޞm1v[`!xmXw ?\*:1X7&5|a]xSýY' o;녂U:0 P7}HUh. }ck(4$~&0=y`FGwq Cvд`jN[ALRPͬ:>,4$hU tR%e og{&ڔދX&T42 TB$FL<;Tz#:̥ؑ+ -4١4pXE)ߧ '3X꣌]ivTΝ8.RV^xKQn)NS^-K ڜOUp MK%vVO;sbr9)uf jiji20LRcʠqYon`ե>c+?{OPI/?e8,Ժ" r۸Q c3:@UMߙ{gC_OGž;o 威3pZ`4(+Sޅ`]p.(X]t5VI[ Kv6C,  Ml& er1/I]➧or{{3Q}:gٿ`01([PI宩sT &21Ϥj`D*@'Fa8; 0rxaƇK;QC/u0+k(@q5ռ]E_To{&h>jJ,U[`- IM;,X%1zʾt݁h.VѶ]NQGlRZ۟VGc8/0'éAvKW? `j>Es*yFG5F/JwCyuy.sAKљoHAxiAw_RqvW,\$p:ywBA;4MXx]1J-FW0^}PJZ*+[Ei*/&F {wqn^7cnLlC{Ե*kp|3׹b_o5PZgDbB</1Dhxprm8wHn4䟐ژW/?X^~e@jZ))vZ'|bDO`P>p+೘9% S,5+@.pٹ5+]0Jq`IXA@Hb/zZx ??2/'@&%46ӝ🧦1`baS|wht"wC 8X!⎸]59cTqBm:2{藢䲡LS+gM}Ϫ,6 8SRrQ \Sby/ϑނCI r'M&}3a7 a[,Q6M5MM Lk]@{v .'? ր5[+ΫIE˜pl計mtn&ͷq}M|>Z7,,C&ߦHͰ>[afg KpO(q[*-qϋC$G6{iɛ~ȃ. ~75u)ax7RNqtv/H㋃d/ԀL az2Z؛U']q$vaD`LYyGb7A KCo7ɭmPd@~*Ն E2 εM)ΊK b`V "- 3dAE (~e2% ޮ% ߺҞ )MnWbV.# OiBg$S#3=JPn>lV10j&XB6C^qt&8*x]J_&I8gel~<L 29V6u~Yf/0Do<xTУdM% wI+O& exvj?uz1N8LȎo``˝n++=(b YItw湼 h\wa%&]|`YqiV?7th:+ZvԔ8Qxq(WϨA^jBLeA^fΈ\ʌl\E+q x-uZ4~-ac܂(n:d ԸExlLݹ-hN`H܂#ty<Ɋ-3&W=[ t]/?FƬJ.BoVHBVe8(z>5* dtKaU-ߊ {+N! aE)Tj#0jxu:ŗN'u-br%J5y2MT YN4pV*g YTH|'ކ;"z=C%0 UV}*96'5FCѹ s N1ơ1@ %_࣯"G8Iq6|ӓLsl~mܿ'Jŝ >I$q7dIcVAOj+9J= 9sWr1زĕĶroY:9t!U^s=inEɣK@"P1cx ~:3W8Jrk>Ao~> Llz~%HQ**S?v!<+>6XKZ:6mc3x*6`2bh| wZƤMFi6z6\1: ӣ$Z멋E0-vR@B͊ z.|i*[1Uhh4ùk= Cc؃ KF7Z`z`WiqZ$4S3A. WE1+c#.JPZ?pjԽ|VGG}Ud|lj` 9+|4)zw\#l%rL&_0H#rJS) &oSb4s@LۍlQe>_ugQ*#ڎ{ S(xBj!*K2^-7 bna7s=_rБ<(Q+` G~24hIZb_ N]?]l;"3pu cL&M-RX$鳙Y3yz֣3|IE1x#f'S@P+;ed[E"v}fTfP5t&>& Α).sXD%emOtd|[@5FtUǣ]L(Rzf:|X`)i!:~^zg}-@W);[ #ى1=#v!sB05l|;\ #<9K31q8cM+c.fjB^M C1 xA&6e#OggF5,$ex4bDMtdj"wkS1_&[^WcU_ F'LWsY 9[ȿ2ɿx*EHTu,,J={~$̩N`9 .%8~Xpo|~O}CZ}HRKjZyn|jg_^mOc֕2VಎҪUOAT`yy\#<3PF{{uq <7%}.u}WAH1Gʘ((!+90Ħ8K'cpA]'AsAHhN|ӓNZ݁`:DゲY+#d2xvEJyB#'Gf4HV)ƔMt Yg?`4h%azp]H ;Ikk45&-CiY!_ߞ0mڠyǦ؜d]pጃ-$i k417gp!}2_9іDiPWeL]e;֘10 ?lHqT*)޺,i2$Ye>ʏUֿ+&v:7Hu.FtBSGx#8Nƣ)VWr:<>@ ONm  kD][˷.%u{lϦܣ g:y[xS`:geT?`7; ueJ\L^;}ڷ_й=g⬆"2; nmzzc¢8Ϋ=f^y7*5 D9tFPkrF:5~b6n?YD9I LlD֒n-0 `>DDOUB5-9U"w(Q)b):ͽR/\q4$³EcWs.4[jކ"Y$x X1ŅVhpTՐǸY>rί GHQm):Pp }Xp_R}__N%N+wuTݗʪ侤=74xizw2#oSb巩3  炏uy[de;s)} ?"ɦgW0~E3?Y?;I}^i ε~Fğm)UzS]AmZ%ùax$ҍC|q 5 }>}K٬HlE6 -)v:d7GM.O}-r%:褅XCڈ kUNKWu|ԥګ2ʺ@‡>8)UXGgIAUx1HX&uaC/aIC*~ͮ g5\M&׊[gAc~o& k)gGtٯ؝cNu"v!3Ns~XQ5PW§߄7 C?QR ֭<UJIjWQ^1=r '[-Kh9o={_47ʴAqeF#aDY57P-g@>]y|Q>]b8:3lM°]O$m`{@,lSI gIS~:g3(Q9M3`peW,o8 6b2m\B4w쏻;9\Gp=/\3U.Y}wN֔ެKІ'Aز/y1>A;Y>hcMt~lӃc'&ʣvGmLE/E+xS8YZpJ?Q3m)efٌoQ;SW` Q5ݢFc;/7/2_lY Zh ~_6ŀyq)Zћe6j`ܭIR6OSvZ6M5έrAMI1i7F|AmhI6Jfu6'kڼ\m_qgtr$q::qSd,Iz]h|v(|A="p_jxAxǯb_‘7 U;S829m);ƈolO%4v-N%;>V|>J"~i`3 6=a#Ae&u3w7$Ay繠m} ~ƣ2+EE:TCJl L^4tFw~Ts7MkÈ( cᙍ_=j5.c 67g5zG豳g5?)xub|yhύ)„g89'P 'snƬf6%ԏ3:6R3ŠHˆ"Gy-Uqj.,sB}ƳZ}4i;2膢ǫꌂf;jTTz Y:)ɏQ#]=MFza5RL|303 &/g>#|&=|emJ  Kߍ/u -n(?ZB8PRGJcγ).n]RhF*BvJƔІ'#e޹:צv\sŌ"ȗq\gЭ@Wvu) te:sl'Y}МF}V& s<\w2;<0-c?px^Dxr\MWp_p_ w}_Uk?' R~ܘNw!sUف+,}+`-7M*vL:\0\]p=10z|2kj\\qpp, upbz*. Z ^-\.)g4<35 :=66,[ ~rl; E4J{iE#JtV():XHsR")ZXp ZXf&Hwc:X ,2*f,mrUX@rTP˴rΰb\ԛSm#F]\KES<LrE7](S g cCgY1qIS0'95~g_V2$ωWq@e^ SfOՀ=F2ԍkxrgM9ơ.}į8)f+?3))NrFgŅNnhwYMʴ8۳JkY:{ z줴v`OBk-Lk´fpd.[Kk=?-celRiIcো1 #5F=Zl8s(M Tlg F iJgAq|zQ9ȟ> 9Xu>sF?2c ?5g? ~Ds=eAdSFkh8%i-.<9Ga1#'oVfbȣh-Me +qϯ` 3o/@#+ЎA~UQe3r7huz_G(kfa%cm٬_PEE"Q/TRb0nӶ1 q JEsl:PY;7xI-D4˷]ûK2L/m0GAKnzmi*o:E=}(JpDAL+W1V좛ԥ \=EtEɰ{(Ċ? G vg s=Wp6c{ώal,l'؉dl'ሉN/QɪAIN LA9gϯ/f}{Җנ/#g@2&| >-+D~hI*3r h'C( Gx/sSߞr[*31T[dD? 䫬c %DP< )Ls_Q 5HӇ8U|&l5YKdu7z02 g`vVYKiI קc&*~C8RHnɎg'sU4y's{ ?rh#_4{z>B7&J" "a ;ceoZ!\{(7yS/>}1{޵>fP9E+sd|pO^K;gWǂ11k(y%9"]M=TR{5{ȹ=vwao=`u&Y{'=dE" =! T u7UɛV0\=3IiųäH)*Ҷb2\m )um92mj\` aaKtKdl^ )֦kO7U#W;G D#Yde\`oA[ HYHtꎙt@5FfnPN P]bFIe]WeMZRPr$ J68~Ģ˧Wq!wb Q><M7^zjq˰M˚f+fk^PlF"iְif]4kXJj\437YY'5&4l Bx ~5{M3"{MU 5ͪGئDٺŚff$p\ڹ&/rZq}-+꿈$b\xokU:rZ+"wLvq] p5Wku5ԁkUzqM}&Zg\Mnr] ׺Fe'a\번;;\{q\Aq^s}':uɁ˱k*`f!\siT'\ԋ5 \b\.>?cnḞ~5b;׮o8p.2K8%Kד&#wZax xȊUJzH:;MugȺPsMe(Ȟ({:y? ˾_ ڿ9>FِeQ9}])xfa1Bm FͦMHoT,q܆E|sOhJUGSpI˶ m/~ڞ/iڋ mN%m/#B:x.bhEI;hv_~v'IeN>xo=8AU"] .aN:ky| S33XPź:E~*T>fde8:ʬNYX3T8/_ "h o6>~l{Uv"ӹKÀ4+^;%O'hr"x./Jf h06iBzv@<qƭ9@PWlU҂ ;`M 5 |,תA Fx bq'3 <9.5aXAoh/;NZr.{/߮-?[֒2 ꛈyLт|y)9*T|Lnxd;KNUُ%D SDos;XWiٺk_'վ9~M-;uNYDQʞ\ Z[|gj,-b8W|{$;| {SKU';G'}Ƚ-ۚxFKxd$/Jȋ|u!ybu0^ß"#ioK&`%>ۑтV^]S>H| v$ |ޠmGgpC 7'I'|^"h9ˁAJe5w7JkjRjx_.#JO[H)<)r6f .&$,d2N5]Ơ>+7RIHS8/.<j.9Z@$@z8{%43?p`#T [[aγdm6CX$.;OeTS#-LH7.G[pGH#F([\5gd~"0Q8Riz'nqc #OtEaWRQ?P5;B@qq׉g34HIezS]8WzK7 ~j,a? w۞+UQ N i}kٽ&1׎WFaeXcָRaUBZ շHo2;t^t6 ~ߋ0uZʴWG,y}sX9g3ھLOvht))j_0q مb]SZ/#P= )ׂ=J+PM-;@~VL3FP`Z< =Ӛz) 0-/gL_dׁ(R 5TEj*\p J' bN:f?6U4]&plib%aZ`1vl]b\` 7 6_ nVL3TGK \d֫kSb \g<ꮄcR]}5·\\ws}pܿ& p_B|T_7|_վ4Pgԋſ[z)]l>)o2\#VP}Jf0ejPVS(k+ي)A}(+B VC[j]R*>nܰΪol6ّLpT9={tC[6RKn E?OttDnedCg5MQίݭm}Xg'k/^+{5 B26#-2ْ5JLb3%&)(lÕHH>m'q^H8vsh?XUK:fu$6c=ia[\:*s2,Hd%gqt3[talwI8ŷmLrq$&:u+q:7L. sqålPAROͣNO/),#,KY7bÜb# Z5zH5bPZ շH+;sGH&ܝi/bb5Aq@u>)ԇ.{VF?zvz:'p=S#Eqq1_\= z_6q&x6Z YuI'd쑆cLu": [ ;toUab]=[1[g;hn?<hw\D:\r#>">qֈ>XwW>G F>#(m:xmb|RLRd>b1M⯝1I)S=I>֫X_POkX̰nN{33iNFwkX̱.b1=nuU_ѥ=c1S:ŬAO&er枍Ŕ\kftnXLh9:NߓW*1f=:b|0)XK4tN;b af)S{2Sv*fJwlXLpW1 MR[ob4ռ-b9]+\$\(؉{3Sw7~'C\b5?J7~c1? iXLKA R+ݛ>X9y+'OKf7c1כbLP{b2 q?H}셺{33sCctcôܮ.h!sMwuͅ]]TdWWzC,hק^/zPqwvwUXۺ0׵Dn9ZezS֮޲U'#8< j({C}94X!/P@ ƘV7zKY- :h=hc/#R/Y撞}p=z%ol]fes{u#8Gr\8oZ|">=}( %%\+^פ}F߳O^{[Fm\a$$Ow&27d>*AV+ϣ9Iѧ.Eg:˫:§ -$XQs+aK Q^jӔl(E]d8"y.Q>O?MY$-=,9ɟ4JޖhlMl4ͥP (LsjϒeA`/;h޼ނfay&470JqS+5_Tm辔(4HӅ3ym/eucCV)S z! Sl6.m Y|rtX4Xm+觶|boC\d8ͼaV?/GEF8f֋Q I'hoL^(be`vrRTu4l|YAIʿ1g [9ITRfJѦRhhdž5ȩ]umؤ0?*"U0 Z3^)-9AUΧ?|oV@ϳL=`-rI7Is,ffnș5 wܐ !["RE #!@0Đ5N/5kG8:1ԥPϛ(Dp9Zfɉ;Z@?Xvpk,.\2eq!^>?-"zʗNv80Rhz50 *A*0~|(|VUOU?_&7FFjD``Ϭm?h%c %Q0A/g pۜHﴈ''s3qF'O[!‡`^˱x_j6;)D>,h*&ؤl¤]fb ξ|U̙++fy\F&K6|jq]<_֎%9ڥQ#k bAlulI>Qr6d=U~̴;-7&>j\=`IKRMɖ*rgؐ597-'Հ*X^&V눵^OuZj䗊RA<`^S^u2c:hR(-02\V㵌%pO)0QD9w4b=Wr3}NRU" +KGɅƼt/ݺ7[+_H+\ь<5A+]Wƕʿܩ/oW:X7Z+SJ8&w(WOj)>?xnK|)/ynhIF,t A@spaڸf 3s/v%Lgi&X Fp*9ƑgD4'&fiYI*6O2dGF);,M+y'W[ҞaJ'G,yཎpu-#YWAj- PXFZ0Iq8ƼHI# 4Љ3yuh ,+u#>#NNVjq~HKy4X0jR 3\ )CS^KiB{2|@w@w]Xq6-&s(e\h\$& JwS+xx j FOvn3k?KNI|.J4 *i :@DP;iR|JԂ"g#q iܘ0OOKS>j* E%q 2s5,D@Q)hG3KOkڳQ&"X?ӮA۷PN~kèjlL -5[xAgAB=0 1 $>_vhhQ uɩx&':yRVN}?"|AwWϠ=ߨcU*zs-ޝw@z-֮Eř~IcձŨ-lHO /Z[ 4)]b!J-sňW[ݬ-J)A%z䷉-&Tv/E)rlu1xD[<%4uM5$> ;2\em'!V w rpGPj,'uw__`5"[,3uU kfeYg ӣRbX?ZVײɴz)~R?$O'3HYd-XYDBp+7V? ҐxIW0f9<]<; D{E{IJl)UBY X &@j]En;Iaq^.M?B,36vorqW8j|~5sT72r-n82H?K1W<317f,EH>)bbo0YsG3aC%&_Nj d o(Q{E<57ZF롕x(WyQ?2 #o]JʛHWOE׋*ͅĹ V oxv m/e Kq&WȽ.7ܖNްpss!!mw_aZ_HMR,^{ &@;K&;)o`let^=_s)"%:F:&3RL>.@bP܆8fSnav~MRِDp% yLJ!l7ѣb`Bb v4œ/b"_،zi~@y 02?@V!sTU!qoo􂚻-;cCO^cۃݭkSq DpK` -&ྛ{prwзƥzt8\ڦtr6=y4_>%xOo๗1)NQvbEIhx8]mSL]la08|_oY9yĂ̟ ś@<(jC - Oх a R"Mʜdja>=IK#~ 1n wMsEt?{b޾ _{)d|+KήM;c )DǢk^1ȊyhHƤ,ծ9z GyV 姬two_O:7)zȨ{GRLeAu`ҹ* :p嫭cڦsuDkA!l_Ё_@oa!rpdKN$g5_1 qخ[}Mi^l3El/9[uMj{^l["2~6mN!؞[7ƶhv`۰nl_~'(7~;G+6qmvۢjl_mlcۆu]`_bsOjc6l_!>A-]'b{|۾mخxJǷ5`Ͱq_;ǐRȸ[?O4nGa5iӘ;1̕p4Ua 8zh9.M>&򯠇Q%9ͅ:~O#e9u@sqklW_!.q90 PUtK/ѯVK=v&B'3Hͦ5F,iI=06I$vl=Z%hy oIbq]GB}2ɈIB.iJax|+HYI0\/A 횹2;M]?nB/@ɒuaQM=|_O0<{`|tTaX #ʶ796m@ʺ)[MaG}5 D%cj*3"bUT?6?AFg\B=qvc?ͯ?'%0o]&!%4#՛)C52/uX%a{HPi#dT- b"jjdS2Vا'^Y6ƊD IW6z[%r`ll6 G'H#mM+ K'=T*|QXB{`(_@ b8 t7Y&IvM,6I)lXjL^rQ'^Ѳ@4 Sk|DΔ^/OYF#;e<:́lA2ܐ^ `%2槐WH Dr|h_ lw@g#I္ྴ腙TLE䀈Kdl]Ղ=$ZdĘQK~`I%'`3'R P"`8R0C$Js0 ;)=F ҸΗ'Tg,l4ogM45M> Po(효k6n.굙uc^;%Qry<wv} 5sQ˔֥cnzZݴZeh^bХѩrI$"HcJ3S3$/`E,xp%>.oaJ 5e5~,2jQay4R(f6 7ܞn2V: &wp(/j|·qH~B8šX2l+8dV&YMr"twLnȚHodlۋ?n]Q':Q+А9Ѵ9jk-5MֺD GDqkcMٻ&|>iKa@WإH T.4mN *- emZڒzݫo@궊  M B[wf24I[m?O&3sw979yu0h 掵:1˓kWۨm& pw \o"!;%z1%4BFĎ|uv$/TإĈ61 ]I@QUbo݄̘"HWR$fH 9r$u{Ę="Gbkϑh/N'L`'M(A&AKr.jє"<=ۺl#?fSs'NN*Y>E ѝ"0̬oc%O6b@)@Uwg 3u("Vu!GOJ 0&w~P.ĉזMdS(K2evL7Kr*1֎TLHW$ɩ, mr %9vn.XNE w^x r*N.뎜 ^x^SQ[vYvНrn_%9]-+ !$t k% osrW/ɩX[ymh?fkr{b[NKr*Z-޳& ŋn$ɹKr*n#ـ܎X(p{qM^Sk-m|Gns{^SܜS11S13gȩ87s*~9f~{HNET2s*ET\OS7GT<S T̺TZ+s*²zNNœӻ=/>zjN{lN1c7TzvNS9"{iOΩxdJWr*Im^RmF&W&h oqL[ !9Nq7%Faח`ܔ&߬DTdD;wK dzcKm.}A8n> B>D)! RI*vM܌.>bŸVX{vJ&mPز \CĖ>e"s'"ah/-j7Ž03ci0ށmx0|yXG5"M5MGq3g:cg{N3a.ƛئW5ٱM`_n$B9GuHX3DӞIU?=L !wC=<"75Daj vsp;wL#3("q|W"jY"ͶmSWyԗ/s"0 ` y$1ys$:?%9劝g~j5[ΑyYX%&& ||qQ,4?/# Q62UFF|F}@jEM *a3gx cCLe_M"[#ز?%Y~ w{hOL "S<4^@!%x{ ~0%!%X@۳1$i1*?Kx,GEzCΒf{oxP%(݁Pe;U.S3c,^,B:%Ri]Gp YT R5o1~L<'&vWD0,UmZU"ԻCS+8Uu4 *HNQU$ۄ҆fi*a43Fl$}DP#D~* k64y=[׷u-V_@̻Š[Lw Kh? _^>H]I 5K۵٫X)S'y NeJu.zT~[iw.ʖ{{zlOI{ku+DDT,$րLI)[浕k6 ]? 2ᔁt_qĹh@VZuj,i+Ws\1aT ['*D9QB#sHm ?ϖ blGc?fī#`hn;c-OS0'3G\Md[7 OV~*JvfZvFf7;x*-~ŒpS a/>q[> :WvxQdtL|<3?3/%>SCef/EeK݊VO/)nEHt":V)6UmǍF \5dpo֐ďEZ^Cz8 "eP]fBM`cF-FJ=nw۷">Eŷʭb =h `aoQ&>$ 97<Ej1paL͈/@Vc$HdՋ#f{wPp <9А"|ڠ$dȘI-'#AzX2[ȣ$XzyYǜgLߘ[-ìVb^u1 `۱Ej+0M@ |m>1?\9W`Y+sE{0#$Ox'"x+ fI  2^Fk*xݼէ(ARUb6vv8a9q_ݡ}%K,JWwf=Y{K>!g*=Yܧf+ ܋^[iS6-hpiվ50(`sB'Л 3;e}u߫J_+?߸[Wuwʯ;ܻ)p?ON4J?Up;Fg},c8iFt8}k{:<:auhNn1`6VlbR}p)ո=/06=sHW6 t;4wy 0$-[OC,zeax. آ @PCBTwd7)'(?,K8v$8> Ǒ$@Vn ɍ`CxqoctmKS"ppb_cV)H -@#EDEn}m1čy-JcqBrfoL t ID4i[\csO<͕ p CFUݠ.t^3WvU7R vSC N.Pc-:f S$F;p9Zk&]ƾ&>X[OƵ__pӢpEp:F|gk<"1ݹcpwn;sau!P4 ԑWtTq+Ώp,pi8^f'_g&JzZx%F7dyGe!Fp2ڊ+jܟGϓN95֋ k}{M 9~uز`wpM2tw 2du >h'KiDK 3*εv& M.W @sֵdκlǮȜ7b}B8K{K-/'̺I&i }J<5y㸃=فIqM2=ii3;tAth2`l9 $Ue 1puC0q hU+>w*hڰVv&4C>#-1Pڷ!dB2Djwxx^o;wBj,KA ոĸ]u2+()l!y#c4]ޡ Qސ^{Ciu=Gmh." :/?X AZ̶bhhxW_0}zeR%b oxSz_&sۄ{ڕNF6t⃽,jV ?e>Y-}ZXqC}z &vŧ@M 8 XzchqaWdD9#'D{)VMjBp6%#ͼ7T[iPu㵿EBYh=#u65;Xз1^~>(\3 ZzmA*h܃zhT |XW aa1u`*:Z/?*' -i^A*)C\DhEIIƪ\YYũРMb<"CE?dD)vPaT@v(Hs{yi vQhߏ{9{}qhao3ѽK ֐M#c`MZa r'iD[$&~,6 %3MOyt$J4 AO(7PekGFɍ[ף6Xn @\dV Y:%6T~e#-/0]E˕dDl:>npx=T0?e3̤ͷi`. 64&p5T öL0sAV l]HP14>{GW5]EY;sYSo}U;n j1x$Z0Kűuބ?7V|Ľ.pN!wrH+;֜EZ4uh)=əT>:6kPW 6>IgI7K4h\/N1Hg!SMl~]Y@L+O?^w?m@ G&ސLN}>D#tdsTܣZ:'|:(f# uѷc F4\`\A2uy ԌΟd-Bwciw*s['&WFok]6҅RrlTta^hN.65ZP1ׂ} lU1o1"=T|s^ Уsqb6QvjtΤf-7&WQ?.p'UieO Ij"{ W|)|@a M Nh^e!kJJ+# W:tcY|7 wbzˉ)Z%m%7ă)W&=IU :f&f@$D|pY:e(rA^waa\ܮ,\p%82O/["G%Nyˎ!3).Ol1Ҥ΅*9Ȋ/',ѶiBj|z?4NX&:t2xK^b3)9,TfHIOf'UAjnM4/1=X~[؏MW/LfUOO_P{ͮ#aTӠ0 ԢR]lxrV) u[XR\g3:Rm*IuΰEDp"Zr ſt rmF2>tȸf#4R.4zV bV\5=%ǧo0MU,z_=З?^h)F,lg0Sr$fp|Hͧ`{W`u؍&=a#ޔ`Έ=S7aWDTć%{=:a\6x,ZڐUM9t{c*#Xm8 =H^$M^q\lA"O;9:M:C+d 0uzt8#튣{0$$ 8jĶ48jMLw[ V#C`5#,Fv0" !{'H?ҰMh2D LR6Qe?]4g?qݳ(i۾͐ms wd qF?52M5[%\~Qb'B@:vq 1{RPDP]QU=9 !3R#3Ɛ]Ggc?@g*هnK{Ÿy kY-zin!w%3EQ;Q)]5-e_(8x76s DiAS!hv_ H"3{E\O`oLtQ jtQ?)~5 յe7h3hTX85ŮW?N] 72Jdy,3rQ^#y;UGS.q ΨkR1z=EDl韮K]\Y}%?#t$'aKJrm{ݸrQٞg]1b^ڌs"4Sw _Ȭ_jWR!$ Y0鑷`*an--oIJ+'{/,Z)*U] La~s~BpAfe!p+|'JPq{Ar+P.yS My~©W3'-st eZU=̿,~ -'KZɂ?Y r;Vt,!PK:_ݧot7xcƳyVhh œ:a<\d-nF"~.Ӕ 4wDg8BAqMi^ SC g${`}? n/3 -c%:=8s0/SNtIq<]|s8i1!. 2X(v"vVa(!^nu"x{Xl~> M שntLc;:YMY})0$K-[-zZgOc5YX]ǂ2V'jS~k0+װk>=zD~< p}cťs\dbͪ$z EL-4̡"[$v;*G-zƐ%:5a36sTQ9tAƲƲƲ&Vn ϡ2:HvܤqyTx >'6;Hw: &l[`(c[ֿohKN3uЙ0[좥 H tb@Mߡsc\$^MpR}"x 7/@Wy33:k]OÒڜ2(3 U_ƕ&,nH`7%M1*Ro[ȃAWhA lxQ#Z0'!~޿ 0fLmӞt ł OxK88q{T{R愤C[D@ _= 6. R 66ӝ#534;pGw3bNP>ł|j5l ʺuBEYtG=ʰ,a0C4b,Q#?"' ufuY[f&eT ?݂!Ш >j0: CɃ54t]y(&]@ ӔVHOŭ?` X3LZQ: ^d&&Gu`X5xUV 1u)ҽ0DҕKGYrhE2G g%V#[ a_O֠!nw&~2)j.OOfҗ~>AߜN$6sK_z==%{ٶ&l}"Q|W 3Zoڟ9ysQv].Ba-[7>/a˴74E*s赽9z9:}UmR_&92a ܚp"= NYDa}VR r]!H8aHTɽL>N:q}C6a{R8FI})T}V,{YM3iVO EtJ@MN_9_I[[{@-fG}U/KP*@_sגtI(.GI!\Lu'pnw„ a\݃Ti%܄I=|Tm=<НrNKoC^ۃےsKzc_wg=_Tc;h 2VHw. Z[cIcIhrY'Ak^S{PD\IJP.9#2lsŬ5魍YWo}zY.٭Yߊzb֓3Un>7SuY^)J0+bNjǬ{Lm4̺bJ#u]0k5)fؔ0=j̬%u3'\¬SʬǦH1=f-v`_l伭똄`蝶]Q'ApL|)PKTplxW*K Q{ԢpҼrv}ؠSUkcLdĆPWL5ThNtN!ɰT&@R浖jUO?-h KPЪotZڳӃGWCYByZIZ\!nj鐉~ǖ km6dMWFi/?%/EBou(Fsy`nȫwl9p76rF}qFvk Y&ky׏F?/i#w4F216rv##Hȕ|c#]ƩGV{?Nhole#ߍj6yldQFnzkn#;#i#߈lh#F:"66rHXv*r*~{Vz&/1L-CjMl aoORЪk]SlGroK29MΔZFUhJ(˓rnLLN)uuxLY>G7VȨe:hc 6`Np w(] ,2}z$jlN|ݯ]Hj8r"cv 5MӕPb6dcz*T@M@J-E q8xF%v#Ŭn5A%袓 iJ2գ\I}GYt״W@ U|/#2!C2վ nFO>Qop(Cjj^bJ-6CK?j  qq ^)tn\KZ24Yz lWg8|4&$g 8V%NJ1uTzz Sj&!%~e|Y< $]߲Y^3! 9>@eI;MP|fKV%[f#.F\KVWbv*v7 ,S^KO;J>?\ܰ$oTgeڀ~"C݅w_CNsѿ`lkױ~ִ7ݳE[Az÷G?w6ʼ "e~] l jeJoN!TCumWLW߃ ^ZN#?,zKש` r}޲Gx"|[T?D^1DAYFws21͝ӎ´icڂDY$b@sfe8qQ f͆:jyu`h6t߹[Q{P3@MBnڂ*7o &fLQ[`j&l5)[n| k;+ 5=kji\5xk_uZTx"EKXU9́ ɠ u]5P3:t9*V@Tk&q$>Z5nP[XCo6#H0evFrČH1 FtXRq&l|H7$O4a7@QRiؙfn,V}DEmȲe*5xY̩0ć'.լY8x C'FǛ™P_vtiL6TCe:0/SgTzK ΖnK)aNT ŵQ{9g ]V:.*J ---˿E#=f%@;OXa?4u :/_e6X"3ZBH-%Rtp^ 7U9FZ ~9.ԍtat`l<ȁrpnn|ڝRDTHml>kt;\cGQI<탤W,駮ٯ!>W1Ù؉8F*q%fsNFfjHC!!̬-3+P^qZFY/ۡ>U);!K!s킨e77>q]-lc_nɩPD XF4ײI/z`Ȏ@եebN Ӡ3-zi}qfO*6ZnBb?"dpV(<pD+ZF_^w!nx36l ׈ @o&ʝȨRߡ n1Wym<푞D0N^"\FLG$K.jhH=FM4v@A@\M⢎ϊ'۩Gzf=0r4IMs%RF6YR"mw Qx^L)w=:΃% /0+#{C~y"& :Pw{Cexu^La{>MăLWå`tpfsql .Lpפx .ϫ\lIul5\|hBj=s^H.hqG.^[)hſq?'?ۑR.m|cF.G\w\|m Rl.~SUku\G.wzRkgW^4(,ߓd!:cF~Q " +Q2CfLшpdx@DNUuwlPT&{qJ`{UϯLP{wgzuW*'tW/OKm/^tCz/͒ /sKuǯ_*G~E?rTćLn[uN2V-I: qUK ,)XYG#M0S~pڪIqʀg?-my[9dS4OvQw.@/nU9N.ʧP}5E?I5>eutf^s]=i"Wb 6hLHAQi;~NbPˤ _-_tⷄ1ܪM8>yIVO._٪^/{0^+^!k2nc+ +bȎA>픏jxQ=ūVXX:nm]H}0\-JofqM!ʊe˨^V۩tzGz9q `_WFLd.go y&: S+G ߅Ψ?ŰJ5C`Ӗ)Hn 6]X=;9R~,ܴocT{wsSNVߛ"}9^6^_w}MWVkacx\-|y>?rloHJ9N^k~"ߣȾLXT5j6׼xM_zkc+ӱ #_S#kJ_ T iYa.>'Lxr w~u1+ ?M/ń;ńWbm.almdIVtoM)U1Xۼ/ |#' M7O G9Ti:H:An $/vVou&#nyHm$D(Q[n<!Aae?C[GJ¢f VȻՄɐw V^r0!22݋Bx;3ŀ@4Ne rʮL4'2j"d@K}r`& xN-FA}E@n/WB^խ,&;jWXҕ D1א YK@m؝ux'}XIijxrc_5HrF͋XT(sI<#M><33ZUc>ot*e⏎nr O5fQ/=X("H|/3<g?h_K}DžruUTYL#&~;D]۞F:?/`JOT^#[,mD v hWunآQ~Ea^\.< k6ZatkmU:Ny!^e9STc݄yksYbqzM։k>3-lg<+KY )*5[ ; [@EwFHŝ$KQ*.;+,N)Ea-{?QÆᡗ,8i䔏: DTf`qȇh@O|̇&'C5ӵ65bObMcƐ:9\a8gÅ\!8!Yɩf;S/m<&m:խk8ҔʺҝR1 O4)]3i>W =}p6x6 Au tf^Y(g\ޮլzZwLlڹͬj6kU\Un>lf"a4Z/i}SGlgl CuWBX숉`MŗG8>5ժ_3uZ% x.±:M֮M?i=H7lccqvPX<_|u JWiJv<ޕĭ=@V~ݸ.\z_CbZ=:o!_G+:LQ&†cGϪ#VaxrqӼQ eڎvQxָV,QԱKErG+x܏|mR M1C렓^4LdHd w7#FAx^z`)w_[V;wgxCƠ51Ė6ĦtO&g[jtW{syPS\K4픽JK_U9okeةs!Ɍ'hkϘ4$}P %P3U`ڳ_ a>`R|ԌV/ϼz-kQ9<yjk)┒!' p"b t)_3KXإ~-#x$dҪCV_ Z]pឨ{tW63 ~b)^IATw9bE*M =9?Cj3 X#EιQSPe YJ\\3ܳW{Gb 4CƁ*/ RAA˓[|tj,z.z.z.z.z.Xfl mb5][,l ܌-orL!?cd })@\V;Fݿ t#yzMֳXy50…rXLe8yh'EVafޟJRV#.,7+P?}e 3q)Su{{ [vNRoiX[s6+ ʷT2`5g9w TdJ~!Wc!,м 唯^Cc[-nq4X 8ьbv1kIv-‹T;"nE$ƴ[DGف  T[ړaBf~j@R;&*MPf KPn' 4;qSuͳzkqw hVvo_^lR?,'Ҧv1c:W()D[)*y  FA+)/ ÖsfǪ-ž&9OV# *NX XQ@I٫+t@䞌"D5_]MI'TL!OT\x32p 6ߍW`cw߂|'@6vDY@Ԍ8mvw]&m!MVy1H99AVA. ReG 30+Z-zKJno|G>#]eӷrs>3F`zZ.H +at-AkIae;qdE8qQe6UH=ԁ›bM3+Q4(C; CTa/ZV.5 iδj-0C R ʖY.E+Ac:#Y7f'l\G}B@(T?ދA趱<꺜ő[JKO-OPS(L5Ou$y|\KD]2)/1 }NA] *۱[N3N9Uzn)_]pkC s%⧁e[ i\!)`xV/e"ZG1@AMY'В!2ԕ3\ Q|21g;Y-|ɉǓBJ,U}[" bn8bIE}|Ccfŧ2!fv@I5>R4D":qGV+ D %{Z6a.. ~bI0z4q"74uR{u 7᱁A

    Wbt/߰}9 Cq'_$ Wagԅ1r(-^*3`"$DyI?q n (1F3K5U6}.67>oβ'Tl督/MP1=(BE{@j

    d6h=~ۗ 1 U95Q@j*߄3LjU*A ~q7$#?>^(|t5NZ옋Sd!{P+(% i<̾Q2uIiK)əsc^{kmjUq{&shv7m`phЭ{鞲')sa@Ϊfs֒xVEe&WD:~kF/|6ϒ'@AH#TDެE;HR<+bFsF5NbtE'.zbmv^A좹gM<~!hBb=kEgW^.B{MԈ' _;8qr6h`Πo;{(]vH:񧹔ڠC|&ڟNA 'xNH\YCEKIpb4qāX+YxSv7i{-δ?YH+-vn[JrJOL/C_xEn[~-Ca$4-ZHQg$o=,#^P㢒#>HkiXyn:Z&jVUn*湥wNǡ(=cS8sX~A$ĉ{1~!"c9 X6\4YqrG?4$x/=e'nu?Z_5OT6]1}I%w|>+nO9N4CM&%%RÉ: g0y{*|hnTj;95Μ$F6,`&9zr& sDYw ky ~&1ɘBɿLOd,RDݬ9062a ,/8 ;.YLGĢ)񎹅bQ=Ӵ>Λ̪2gRv GA+xJwQ9T@J@uY0eG?0khD.NRd$fUY h {${@{fSoBᅉgVU~{,_EQS JbsL,s`OHQ lSzWg~uVsŜ.CNW412TGqzݴ|I x[0|}iU*+q!T.!B:ހftM˦Qws!-:SU1ٖkhMl$֑c9Jv vAeDd6"rU)g}Vo+_a#}֣@n=J{ ZLc >ǥr`H&` sn Έ%!Y $_l#]FTғsUE~-kt? S$gHGڜɷz&i}ցM wMԂ4o:fԍߐaxFLT9pdUҡbʱEFjG̐CߣVNC]7:p5Eݚa(w49b@~W(Bht5W>[6=2o$>|ݰ99-qH)"}g=b=Ӕ <^ @{FZNJKoa6% rn@'mVlDdϟؙi5ѻP/Ƴ`6-u-KNc0({ PDL @EF-LqȘE0O'C} X( #T߮Qʐ-3i9I'] 2yxDvk]9ʀI2qj+G8z~ ~!8l]UX 0P VL/2^`*g\4jY!by𷦜Lq3;wf9v?low7s#R$󣜺<]rUUH&25/Ҿ'ܠ~Bח m~9eg`Gb0qr&3$V=/!rd6k?l4 @!MS4u'mu@U1´XֆÂ7Fd0(Lc~,hBAѕ$k%h1 udz=QiZ¬2z.p/ Dn<$,~8S=A(&>@bHISa<">"!]mzdY>&Q=m,x)0="ta/pL@ Zcemem%eUJ3u2\-wX. Bl;gjÑ@ߺ~\r%7<}aU'وnJ! 38i*Qxbo^km rHy AiE?/' WDB˜2 > &g]IbwpI$$iS/bJ0tc9^%9lWG1,|`zR_!08+d::p!tR?2!Y(NjҏaE?!,E_³t<~ =ȫ m3d;BiAA[XRW7=di7t5t3S+8q9sJ#T7Jzu,6wƬIjR˯4hLk"%a-`[?Q+ZWLAN$+ IgNB_9s2Cg/3ۢno&9,Ɂ)W-yD}Ie<;*11dPGC; &Bvv<ct4~nPXՉv9==E2*H' :oS@~*'O^cPe: _A9M6)y0G,r9YˈSh*vgn[#D [fr~[bON}&\SsF"/rF*b 14wn՟Sf?CBm+}1-u;qQzvArC,ub(k'&\f?iEjGjhlj6ZY&ӦMnrDÿ ܊faFq^O♿Ak`w:oOv&)S 5ю%wDGf=oRɻq!Txm n)Ӱ.x]*.oᘒH 9|NQ3Ĕ+ cv4jQW!w Ԥ(hNE-{:=PXA2'U{cϘ=&/]H^pD~6.2~_;c\MAҧ[.炰ǁ)`<8m 2@bY͢d,Ŀ[6: 9A!7 9`> n,zDԉX4ZscY}GHtshP@Z$d{ـF \DŽ"xLz=|4ؖ:y:anj ![AԋCVb79YSFHu1mSVENz_3̽M&q9XfL-ߛ^ `%l1 $8ńa{QanKzoAxwMck5~ 1:yLZ_nAKh .#3؞Ta{jT:a18+A>}'x*:Li7jEgbvCDV|W10\U"Iɘ :{aV8PDL*҇>M ~gQΏ4RNv=2 U?'~"ljNjh3A.b}yJN7נIT_ЋrwO XGt"Yg7iSh 5E.69_K„F&J+/J2`$j "XuN)5v^5Q1i/{pQ +d [ƚr 0}SRNK/VZsGLI=LĻi!oY&!&Pzb&VUf*krg7qyRp:WS׿\qUµiJu7(~\?;vٸ=Fq]4%ϖ0\#P\6bkE5P2cK7qp).??#朖q8Փf'R\?#\p-?Bp)Mqn e\k\K&E.O>FM2Sqw.qqq[#Yp}:c<ŵ>/CuxlB[ẉ-Ac ;qm"WŤucŵя.(|F0\h )+&R\s&J4˸%l&#kss:k)w?µhbWK\]ٸ&pO uake(%IkaiK`\qݛJp򣌫 \gPۡu!ul8"^/JqipudLnQI @p<)kQZ[ח/?5dhF^:gE:E:"i"N4N@QD4}YUj&xbE]5~-Ӆd9q%QR0u`0w4Vn!A iנ]`% c@y1 /MEn[hϒ6 6=#mSڴfԦkߦ請hSͳk>ڦ]?Mw}6)jӹeSIڦ!]$ +&D %d:*37q{LYGdWc|'%Yfn̫\4헙wFd eYa<Rf.Geu~˞R\J޽T״WʸKpUvum3 c2s?p%7쥸qq};\'p]5@qX ׁ#4t] Qpj>ʚ~K\kP\ΏkɞuN6/% \wP\_K&!Eu4d)ĵK`qw oq] \]n{w_6좸ppvuAyy}2"Z{.׽AדzkR _Fa(~\{l\_<.0\\}Q\}FQd\j euׇw_ f2= u)G5]Z]Cq=UumuN˩Ikf齘VC T׌ns*ђX[nIt 3:#xօ34h+b70-^vwcxq{v>ċ~;=lx,7~Ɵ۬0y"o&VrMU*txZ]ʎ /ՓLf/xgHQ|P,CKDјc,)W5F1WXyʂH0,)A[HA]ARfsH7wL~30 Ў3iXMw9r.Jtv&IWO#EBȋ'W`" %{D俁8!TJHq]#~p+E !ʎ·.G"Pµω_](=z߁j{AQ"qh;!L+M85Qc9W RH iGvዷΏI1oꂸ^".}\0._-"]čeNpy v?GxNُ%Vi.HHg+eZV2ܶ=uǿ*]̙D&&f76k4fJ!9z?` M$l=籭] W }O ZpnE`qhsa կҬY"?q6WkǨ o@HY%@A3̙׹H[W/NmU(RR, besZ4l߄vuEA` A"Ǣhw0"L^>!ݠqb`:b|3?Hƚ+7O#ȈR-tEAE'O$GyeYoiue9RR1gڂ{&+N39'ɎQ2Coo _8#qaA|gWo^Q ҽ]OR? _4Jp &n%Y{%N//@`Ĉ7އrzU{SuJESW7/dřc,;+1F/l1ۥ`@ ʟ  vaYٟٳ.\Ǚ^p`,L>BI^$pmhJkb"HѝBͽvs&>ԙ`iM!עrZ[ 4Hꥣ83lY%6%f=\L.s(̰p_Dp}AGqj!d\Juџ] q-Poe\p=4OO).Yrf]JNb"X6 Dd/)>G1Q\<¹wqMql^(,L%Mӡ)b:4)Q6ЌP ,xCzÆL@r'9d'3wmOlN2Өr'Wx3<cLPi;JO`^Ke|>K/̬o3VXWaHaՋAXU}.0*\U)R:akV4Z:pl.[yX!>6fk1qHKOC/>]iEǥ^m12usgzp{%,#Ae0eyeR+n|602Q>c601歎`f@ϋDym~H條 hypZM8I~eq6~T^Ly#p&aKۧ"g48] *uWmhv'53K&Sf/nC*,un\e[pU@UFnE*a9*(plR~±QXaB[}"nDy꣤B"G܌ (Qk4b+D SalwS:< Q 7lKLGH)܃a'`] D0uƛ\ B~Q}+W[9X} C&R[zWV.V8~i67ْ$7Y(l&a:m0jWL•`C\bcц+c,5Z@n v[Zrcs 8)ny9õhn fv݅0[J" FJ>WZ4\cX:ŒP"fMDz V2"W5Ha>(t 4V <·x\ xOg'DƯA {Bl'ZMs&WM ?k$DGz1UD)cxjޛD=Y/@X0'u?  OPXMfs+frX;gGUhN3r7BE3wN's}pycqĘ; F9zÁFG%76sAoE\W"j 'ƧԾXkfWf ƪjXU܁`i X3V"̩Nf)7iN$٘&Ms3y$+)wQVP `I(Favc}eO(q(ś r׷}]CSWr칷d؋^+vդjrƺKZ0E;5]1Xas[09Q"rI8{,& ||4[IN7݋(ω.T<n9Sb.Ő4wh_G$7kpGOxjip-wcfd(m=DrM\sT<ZƆR]`U3YĻ{CGk# ĀXLHlQ@Œ\DIru*;ۙ^[[hc ,cA4@v ؐ7QB& Nר 62xnI"3~WB]R"qE>?<ڬEd8.Out ]B><9,4Nlh~˄{~ w.nTh}QTv@Rm{X5'usja8nmVr ˒9xf\  Y[S7}rkn rc铡y`NO!./!'Bا4E<z1~nxNn.u\L (T [1VȄ_k~y_sQ}b3 wߏ&Awr%ApU"ɏDA+*2F]Ld1O& }޿'H3 OL;7l [a!h2]+lCߍ}X`n'h$ІZi6l_H:O6j@I.l-ejݫ:wݺE`ApV`GuyyGx?2 Ɏ\/@ 56/"GQ{&㓸ytb= $7./G^<(رy Ip' >6w7 \4QK ,>#";@#(r?)r^[vJ~)͸44ԌiAp+F.k6|`7&4i};[zj_IzOMj٤|oZcv':bG4CL7Q)VouTP69P-CtǷItת'24*9T(i9Pτ6ȁl~6Ɂң99}L[@ 0ZjMzf9-h}@>$?ED[YY_`K]FR I}}y@O[_j\?6f_TK/S=/eԽ~K}դT^T KGZP_,Rח \_jBP^חzS_ RO]_Jc/ re})aRRX/uDhחZ4iKT|oN})qW:] 0/ua5v#cח5__AON}RfK)e֗T`TRk/uiQ}U^zKmԗRCחn(7/5)$7K|k}0C@uoR7ӝoR'R/u{$/:L}#֗+1/Ok5SSս[xf]a/̈#3z{wc6܁8<>;RyLi;<6w✡٩2򄇆O+eSlҺ|uNZV--\`P^.-r]oɏ3ڑ^-+CSCت^V?I{A]O V"HkueZCTZ%뽖8ie/>zB#  }v9~6\7kGXSlY@FSfÜLN/Я`8$0pWUa!X pz=^S;Z:0 ^N7e7eY`t4ME D_ڄ GFN0ŸDdVL0<"]V՚R+pMR\&5pkJݟ6\=V$Sn<(wpćOXSSZ%CC)U`)^St )̪)4|M))znPS/zUOqM9o4>:Fqu8Gc4[:fe\-v\g1uWYq]y z'c!ZחQi׈Jע38ΐqBLBC+kLNW q\ZLz~+Yc\[G8 q] q\Ϟ1uEI\oOEqͨqR]C⪨q:ʸ^LhI\oc!(gӸgŵWߩ:uj!=Z+LF?~{?:w@smwP Y:.ɞfs|m3:Vk mAZ39$-j jyξ->Y=Ʊخd,Ap/x,=Sd,(c1i25VEy\Kb|k0=b,:Ƣ8oWDI&*AV H@EYEPm:Z}^y#T.m)6RZDrY*j-v93J93>OW9KɡGB5:qa W^ɤo?\?Վ'nWqu7bW\p8O\Gq]z_!\#p>qMS\G;pg.sq_k+cm*w!jC!\?'Qs)穸ߜ>qռc.w'']J@iZʛ@G[_}fĤ_Ϲ~;. ӽ^q.^w#qi4L˩\wxOٴ=@==V=J=Zs9= BWM{zA?w0C}SJZf֩໐[U1-<& Ux0 Qb] BC%8NpP!Vt(Bv'\`l rLuN_G:V^v3}*H(^õwS[-v3Q $ ,5\G<]<(2Vb칞 xZ*C;> ݩ̝Lŝw+;ggah*C=Om}?ЦU׆q$Ҡ-+ N(G#<7={y>(YLr&ʄ(ߢzvQ[C/v1[(OQEYjei"\8Na<̼ʯ^2*^D#~W(V?>f*U0[a+h"ixvPZQju-ޫ5Խj{{30\MOEښf1[Nf˦ؚ* CBxd&3$|ښ'HOH 3IPxځ$;EqO0x(wCq#uNU37d'gQ!(3#cdfkbˮx:BBh6aXܔ!C"iWM3RiNjޅ:>ft\&{P+i钃<ҙ.5\5QxXirN:iYGz4 Bo+ #E&lnOkE+ E-̮_R+W>JWp,X2߬BA|W̶s@KFʶr+<ϕe6Ca,<cA|ľTn!K1|x@`! u4X T0Z dĕvr]lB9DG"{ wv$~bĕs!r-p]8gwFiV6 %Սv~<=TȘY'PI]Ӊ6䝌w0N(|9%K^==F#8G$v|M .@&$s|!s!vlZ#|S0$^3ʖE}3r, L!_?8ڤ7i!28@ I7uV\#?g4^eFn%"#84Pt*봥Nx̲l9E]v2:+]CnfT6T5tMO)[f̿d,UnF4 @2 7JS{6nr3czv+3>)h45"hkThQ_Ǔքne ?*К(1Dfb&hcZ@vCMAhcOd7CPnRT RpMW"Bb=nN`e^ho,5WyB)w(L6I)_ S83))pBI HXƚم]tOfE8* ^JPm(>řd I@LmhX"4wϼ!~E-Q*SPb甾\D9K~O{nd.Y]|ʫP8JvJ0-wN/a خ^6$v=2sӽǖ`p&85;Tq1m.Q.iVs*:$=6!Q, R[ooBu/eNs8uz:@iw6Ĉ+^MES![佹,Y&K߃„[b'i|0}SB5tt}k脯75яj菬WɸFo5yC*3!m3maL[q:AXf:_9UKkaa>.( ΨJ5A#q^kOr|3g+[A5oѡ0~=rkF>룜K(y[vDR$*"-..7 )$saÁfERv+:S40,X(];=arM &؊0ofaOpQXP~-` -L\/ 6I X;elQ'㘾Wt%G 98R3ĞK69I)D^fKb/ ${){BI&7R#|~]\,t%H適 1/qW('g'C2{-ѐK_FNS],FFG!Y Nka.̰[E;^.SCe=[Tza&}fRn瀽?Xɝtӌۍ@<(xљ?HBX tJR[Dn썥P3ɅJ3Qb%Z I.Bps,Mظ—q;݈\BbhG5`Gyv؛-@+>)J[3 6H¥#8#rE)5;(˷$!,W]2zaJ&x[\JiWiޓMnћkT{^}iQe_`*HRWMT.ja2^w؁wGF+#d\.Pw眾t >`]K*ĊK6nk8_-Ǥ[h4if&mIkhR:A-!u+^4'/CU_04hT1\1&yw׮(WJZASlwg`SAc;QqFX݂bT@lkYe&ST9a@FI- TN"cnǟ&fE؉XȅJNJ@;Q0K}A_V2BA!,g%ںcnjkiZ&|]aG }=%pkH1+OP7dՑ?a':,J.& rmaD Ȑ9O?ZP8&=:Xv<_T0rqn''O xɣwKoE|E|&j3*L1"xE{J+h˂C rE' N!3 <ڕn< dxӥ4 ֐{f\v6yGCpo[Blc+9%n%G&,yr 9IHW œ 5J@D䎫=&I)v[fM SMri{s܃B䄠6zh|ўJ,C(@y೤R}M;03FXo[NKrNGP$VC|YG93 3"ǩo0zqU?ȩ:ht/"3DR_bi4CtH~J ̰YL-GrqREg55GyϦ[AY;@J, Cn"\ޞ1=+OA版7;1LV_f"#zLsu wa~d`Bʧ}aP ;ߗPej&#x{38f \eաo58F5> 62^ҫa~|Emk6; ԃv mo%MIfi<o7h.4K g]?e٣V0< }Rz_y3 C2"&qV)Tk=8`g,b-OGZ,j,g]Vj+80: s2Us6[:˼7 sv6pQ?^OSP3{EDPxLǢȗR 2oz=dM-3_JM 4 3"5+w%T2gfvwfgnw>vfg73sGe`<MڀS(CIA99rUA0E_ VAmBuqyt[>bA`j٨>on(j7պ'Eu[2/7\} ݠ@F| >nEAl{p*TyP_;LI~6h!mw ֎:[ Z#fpqTAtϜw$Ks13zLWsRj0s[G^torҒKrqeϢOpI%zr]Ԕkj:)==dRKbCJdV \{%\JSPZI ^"$N*XF~(| ƽHpm;p?fWh~:j_/?Ύ.7i93,+A%ԁh|/߂`m?5k\pTDzǠsp-*÷J n@G.x/%z{]~t#} `~q Tw} FugL\UO<JA$x$<h-ԕ=IcN,a'v2÷_0+aGy >ro+_}B˙ o6zaO/dW*Nldƽ+rYMū.P\B5(tӸ,rFr*6;JP^i|{5@m3g-O,!3j/@X- HH7@Q>-}0="v"eOk e0뾲q[=R=뮱9kృɿGȿ݋B؟8<۔P~ĖgbJ(NPAЁ@_xwtDTV/3/uꙘPgF@LLm_(.3|&p2QiM# z/Avݲ\[2GXGz4p&|ǰ GmD ~ۼF0khsJEh>/Dž߀в)n,䏁VP1*\~Q&T 13-+/Gn K1)R])6rtGF^aƽOg(d z:KLQJf/eD=!1qmϷ[-m611MA5xzmo]h#j4W$\Xy=~Ả\l9?J?L?Huǡ]>b gpBxPQJcHݞ "+1[P(Z 7 TgEp㯋x]A;T8)O/)H+CsB,Gi:qdo mIUs6jF ^6 q>Ki&ivjЩ# ԩ'9P*;`YFژHbx@o/{Ej1终-HU+eNF8]*c&QpL":3%BM]MBtQȊl@δ|5:0?IO(l"$Ο5 ?@ħ9}[?de{Ao&9Αa7v#Dc46ƳD1 ь`]6EaZDDb4ıDDh>Da ZDDf]ZQ&cDuvKvi4&w^~(!Z љ{}4&Ӈp &I>(! ђ9-Pit!ʈDD e|,}D"jG}V/Yhѹ>Dza:-{XQ j>ވi ߗF"/mvloL4NhsoC4.DWo,œ1Sj,M4h.,L!UY2,$}Xz>YJkXwYF켥 oGe ÒưsK5ʮ4G]i-Jj,I K%Pga ,;hQ4f]XM,PcaXp\AL[:+?=YδFs?W_FDy'Ut ?Vlbq}P4PE7{OU AĹX(%upi' O"[6"2,>$ߋ>?U4KAGZ/ca1l=f-^ob(,]TY3,,^aieQ54˩pe|GXTY2, K>,ߟT,h"_,GX>,#b'UYvwY6vYua>˰XtaBXF Y4˳c+X ˭U]X^)"ЃAC#|ÁVy \0 V-xVG) F> "TM K>U^Qt]ߣ2Y40=,E0XF~4|m I eDCj ~ aRhFUq*q>&w.Xp`d'vzl mKƌo,$w8]X^,Xv^ayp)fRіfݖ˲˺f_?ۖ֋Q,h&eϺNXScbX KO{ UeYֆfY҇er̲#5#r3f9.,V`^,hg }X$?Tci0,4ˡK,,O1,x}X!,X,Gi?²e5fiګ6oAJrՏ`'y h ~ނ7d5/y ½T{S) F5ė_G1x~oX_Z#E0?X?$i`u>wx [Mp#8FST0Ђ\Փ7`v Neǫ9ۈ*L '@~"NчdS2j9|g)nK)ɑlf g@%"%"-N0W΀@ DՆDH#e,B xwHL=R 3i3\n(ㄈW^%j!CIxCvqE3CոvR^JX9o0T)KVg)d ḩҷ(僩MQ |mO#0x5eX:&JfIyCo1H I sN̈́$-Cjʄ +B\ijY&?FYgqـ䜥]e.-}8R0۾9HEjo_ǁV3O7! |< 'Lr-;5先فO ى ;5% Lod?F[gѰs`ޤ8'/Iݙp( ß}};d-oc{p'r~VYfX)\;d#Pm6Gy[3-ZUx /7ڜ>lN?>s :q#2{ 4Ep/޲%^%%Ra+o‰|DTsa, $LQk ]MoKr?@Q^Hݣ2$j<ڳ0-Oy+=%Rl8W Co\(b_PhI"s-8 *??}:o=ʍ̐>\TþrZde tNjzԓN|NhrtЎ˛"`1bEjP?GuymhZ RVzӳlr+m]ikJ+yVA*="ϳқӕV_9"6WJ0\ ~X60_UW|ɧX}< e}m296ՇʆXYB zu< X?E5rQ=,}tegVɞ*-ۿōutC#} AW] "hwlw:k~koD:ΐ'rț,( eȖb ?크 K@ 6h,W I`K³E]> ͟ʸ\RYA--#mH_ɕgr{bQM{Z{NS6`f^b :r]թ_j%C g+e G|bQT\~?2σlTRȮ?fVFx' {F% *۔k<oE>9a ʿ5Eo:_G˿=Ă>w(0&ߟ{QwͤN?J ,ߎoI|滱f|{-76;γ|*gпzMc6SwT=|-w|ȷ،|) =xY>x-7q\zٌ ce/=w{ggAϜ3wZ Iw.bGO^l%Opu/#VGl[MPpn"tGUT-jJύ?z܈OZPytׂYnH?TkPV\B(P5R9a< չ:bF7R.\)˟\)^˭\i2b{GDe<~j.#6#ͥrr}r2?Kw\zv'-}:5RS@n.9w?u ϹZmk{ mn^lH!Rs?<9nh]OK>Ō\\>m͔%\:.e7eK-lY|9!<~RG<~\ (,ė(˱bk([R֯y스 UbTGz{x [lf2}`O8?Ro89bN?η?C[[~맘Q^c a~*D?#0zXI~N2?Ϋ?sA;->?ӵ`3qg(Y y#B۸BGc-#~A1H.:q#tƳ(Kg+}:ne3WWt1#==.{VfYJ\C 5eE[I 4[G Q=}7R׬e fuXPt]ao_筷h~|b gsrYzd~Ɗ~Oύ>?N*½g _sY!-&?3|~S@~{~f3D?{)}Ey ??}~lsL3!?;|~6D{z>㮓f3ygMq"?>?}~lg+#l~z* w[sxOsXޒϙ?1? ~Nvlc~V#`6E?(}˄٬/o:u6pTk2ha}زUԆ{swӐ/[`kԚnX XM ~ +0L`,x.p \ j}ڇvH@;8L3\,KUGp E -0f3"p!X ւkFp7;@ bOphǂ;PR/@h}A lj=`!\ }WbKA'8hX6 >ւ;@'v!u^'3"p1 qk}oloP\)Zp{;{ a=ٔ;xudˍÖb'_|睏w/頑\U~~׊u[߶R=ޤcKn8ږvw/9k8j<}$m'VԥOU{לk\ww{[=4tٱU7ҷT=!l{7gҼiqbZIySe[&ee)}))99 MPS=ƷOMaIy^ZVNfgvTa_CX()4O>_SF)R,EUN}:W1Y =h} :@+/y\ jƧS=g@on\KFANnl6dN5Ih|i|!rM`bWL!lφO#.Þ'vv*X*kcN 󷾔˲ؔ(lݮ1cʤ җAkұ|p?~4N%o4Kִ@uQDk'n&o]|,$&Y`Ę%'q_G& ] b~yPfs!p%i5]' 㺧kՃ{AB읟.o;g)l9S<&ģhFj!n#ucut[E].nNGײAw@GW8gl 6h-Tt UsnsmԎ/l$<دcc٥qϑ㩒t5:&I,*Pܞ X.k:\ 6ꋝ5FDEy)\b16A /ĚhKgƑ.gh2;SaeĂ;8R3=?JCX$.?!ګM#E]n$V !Vy% _'&KK2Q< wNbmw3V::/tֿ~ﮫk*0&7^9tMDIFq؋Ng+Оvlh|'nyt+Cp:mU?,+B|LrׯHס:W ٬3}.utL h^A{-utB k6hϯEz\G0 /F7Kc~4tZ 5)4tܯtyuu ꂶkX]nX] Z NΠŠ.h1 u::vsȯ}?*~lЯy )jvP>nRh$݄ݩl&t:.Yv.g.n7LI[[gtN_k\5|Mm]c ZHA~{t׊~:ۋEω9Q?g͔}^ܧٿ=쳥o}nۤi܏|#:@|r Ar:!]!̗:0a] Ņҙ/n7[gA~Gtw;թ_׆[:뷁:RҬc [;l'Y:1mCch{HgN[*#KGW= ']:qҥ']qm݄K8A:I$wR~P?tw A~uhWm^-*G"Rtc^OtttT ۢc}i> c?iudE^xh/S^^hBұW-vH+J;YұuFWxF^doN:MC7g`1׋.EWno9c5tZh6Õ&ȷ@iɷJɷYCKNoN+. jL#ՠ n./o]; ~w@ڵ) yq'W@$12p6.E.cvһp}sw1}Sw1.0S 7\3q=%87F60} Lߔ͸J1YE,.E~ȸF-m=qe^Mދ 3Y̸b\_}+qp~?,3u?+,e< n)>~K2[p}[¸KG$W0}C0}%\q e;_s03_G!.,.U` 2x+>.neV-oM<Ƹw1\ߐq~;츾6nc6q3qθk؎s?*$B  ہsCOB=G]pHSWB+ǯifwݧ%*W[g=݊gp}xqgg>9K<ǸsKJ@5̻ՌWC.^DY`ywN>]EEy5Υ *rӊ܂"gۅssx6Dc7M0EW2F kx&jx^Ď'O=ޖ+ޝ.$R`B!5}= 3#S_ֿ5=<7NI$N=jv}{^ۭܹ^Y2VZi-O9`% lTd%Jfg3X9XQWi|qF+Ș'uU\BUquϿMKܯ ߾F^_Z/=תqܠ"7-#>]W?6_w[>C{I=z_RǤ"ȕ5*rÊ\ 1]Ƿ,ImvG-_~5߯ 8/.#kxy G+{xC&)oYG{u-Y˷uAY>I D˴Pʩa!~{Ww!=LD!ˤ=x:eƂ>oQ%l`%-*r?S"7#"gW EnN+|Cy_)ƌ׫ޗ ~$~`3~em lA\TT|H(rC {Gof>8[DN8jjm\4Gw&iMt7p4ؔ{''ܙ7¸LNN{sjOs }O="KJk[L|yWkÂ^]jx,zz/{%[U-u0b-ak%a^lUw3QKaCH_:oMV<7[~B` v^ڧ[sΗaϚݟ䵩qE/>]P>A#2ڟx[-^qUwyTwѷRZyN#s^󱽓#$8j"w5TqIx..xWIGLsxN&_,M3*9s?XѸЅQ[(fƾ>%fXYYOk*'4vMo^^Yvz颠Lu ӕmXֶP8cmt$Ms~5TG9;~~VywO./Cu,U{POl;=<ӽ+HZz{;Hpo݃zb;Hf_@m~}){?~Tj{WX7y/7I_;˨6]OhZԱKc:C!oe%TS[bϴ[mtFg{^ KȾvhv>/i o%7b:[Jb<·i5n"u<^5^5imnz=.a;;A:%M zC`tiz=! jwG^f^߯ ׋a 7%QGqApu{NǤs{Pߕ;.\L‰vN_oK>W7Y~ƫۈە4be>a7uzDgyޭG^T|~3">@_&믓}>0𝎄FeN )r)9qvJ8Q_\Q_RM-/ z;pX?Վfrtڏ|vqYD8cUp}a!o_^^HO3hZ}sH@_rT^Dq?8 |(Q '7T8Qv.ׂD_ ^_ +pT ?N#c427pT^-MI5^eEӿW뱢8?A7p|3px9%qqL‰p:Am9ԮsNpS|}o?ޖ/9oӒjQ n%(Q?W8+rgydQ,p\J@r?}7pT&C5A‰˸K2¼QWW/D}q 'W3qTQ&GwU3e~KǙKz1?_|i>O$j2ƛ>ތ~99ό ϩXWpfHY3W9gx>u#980q.sX8x?M ᆾu`uKUIsKFW<_ܫjTRTNn&z=fou{vTl xS/{uhK]T*MѾQН/%ǣAxM)QYM%Aپ}uM rL.wTOJq7N[c.YɢE^ס~ 'sAub~Dea|l'e,Ul?蘰FNr ٭=rT͈?_&b۰GຩqS^*- G$q7µQpA,zK}#ooDe)G@R~ܔ?-TqW*m>]}jr,6tapqIIp0'>7:v#~k,2EGVi YПYI绎EGqXɏM):l7,)c%uakzyvVb+k-$?Л򓽒o~8~):J?}QSEGhI~h g_/Hsط3nBLn&c9oOJKg_>SpTZ>)3L}rϔ~:_?Ik~8y0ݏqẖ@$H~4*C_˔?O +qBeaO?Ag]h~GL87Ս(۸ʼn~sm7{Z̒2 ~8JI _- m)j F}0oq]e6/JtH稟=Ώc$bj}ۢx='_sRDѓe_t~#9I>m)Iq.Oy;v6~RLՅ].ۏ2.^vS~Tb,?aoy?+FO~ XЉ]7]%efId?LE1EGsAub~~42 $?r*o=@uab!R&@9Oe?`'-M?-?ߓ}r&LKuWrQRfOg3q@Eۆ.YO2>LvQSľ?ɟb_4;9Љ,~4;i9G)?(HO9[Κ@g).Ktk;F*^YIS^JwIw<^Oٵ@7UݒiZhTA NdЙյFf57TSN-P mq ;Eq^NFD "GeN*z?v'{9kr}g}9MK~x[ En#ܵZ/߸Z|€w֏ۨ'9_FLٸ k} kpy >E?ר''@'M~Ǜ?xPsX4bʮ= >4uqO5X~{"^x+ѫc|5u O*~j[߂>xw O~-y>|(AMw.|O?yLujnyaĖp뉃OHzn8zzztx ]+#׀p<^ܱe iqQH*ֳȿV>l] KjgFӺy]mo,pFy"}:_ Lp|D~@_.ܽIǒߤΟcN_?97~wysA͚>< ?HƟ"D p7̿cWh'@G%}4/?&I8=!C ) >2?$?k; q^b"^`X^#o?$~<&`k0yb9RǠ"63j=54Z~~V`kvZºƑۢy_"HGl8 82(N"e}D |۫m#Ƒ5~Λ3UR6 ܽMB׼U7+'eY| 5/R '!vVmdX3D~Ps<D]cXhȼpASGJg5hܘ64zGCL"'$)$5z,7&M>Qc6g3 ǥ .G{4q<R?c?hSeb>r΋Ԋ M܃_پ=z|>53jcGP OXlnݞ +5y@!)8J E"6#!`1ymqٞǘ`eQ`N^ܥ"U߻K #zx̴ǔ: 7Ugǔ~F3'x@n{V_GZQGJS8|p:xLV]G3 ԑ{ǒM~ ^^}?'<<#cXSG|+b:6.zrysNLޯ`=py Y}yc<ɛƄ _s_\;q+з|+d3;THw#7? }\G}\Ǐ:.-ow+C?좱6vq86UUzO/kW*g}evІ:"`LfC_mj]&fyQTZJۊb?yJd$slxsSIM>7`oFExǩ}d];9)-0HO>#w]K`4 lcfh70(]؁>|>Ue<:4&U2a`ųq3=N~<q*wQ.ʏsq4#w|_mw8&yifV*< wЌ?7;p|)t}sCː?yy{6Dˑ{w+RXGZs"XNLG;QIMlj2kˮ{Kar+tԛ1y?SZg:WN7vZRV?:Vd11 ^×0㵹1^mq b6Ky>3gn,ajxgcOYt=};n߀< !EcǀWw##<7c'I s9KXw93L M7D̬p_i*qW5WTkj+UMrihf+`dl9=qO~mmU%Y}yZx3ڍxL?mNd_ >}x`|:Ko6C5Q [`\(ynsVuUx_s_˼+)zƾ= }J>օe{!ג}=JϮgk;[ߣe{B_y'pNԷZS~n[ {HQ=,RsIJf뻴sDNM r7i9-G}N,G}.GK)vK'깇l?;u(uH5uAvBߔE&6ny +P wD}D)SEh[/;o΢}WP^$m+ZJw*YVB9{.DVwH}s}!{bVbTTըϾ WedZM(gb/\sq!!fY<r}Gݶt֠EdX=!._ȎOV's:~Ɉd\">.@)gً}e{U8ޫwnSO@}|-o`-J mkڧw*_T)nkD4}M\iZp;+֡~\>YGe(mg[~BG"R\Y,V&[vxTL.f?n| y'~Y,8龷޽e_[ώBhoOeGQϺ3Lo"1n}~l}>oퟦݽ^ )׶uu^Kv=Dvtd!sş9ރ_^QՄ"~ͦ|~${F]=XsO )&wz~pΏd_7_?|I3젬AA@v$Wܲn7̨ZaֵRe}\pWZuw5Tߌ97;9gΜfk:|j9F>9qo>>wd/YH U;`~rxFS@=yNNqNͷńoky͕s3&9{zRQw_\maEBm3W׬,m7h/q$r}P.GoJK:C)S5F5)}PyVR%}Nsh>&Qo|b ?:bo@ߦj#+jk5PO\ }-=tWk2t2|/Ӛo3nymZW)[wX;<nx5a|IFvI$-[_ qԟ8W /3:zb_e5ft.JY?/z|)fkOh\|Jo#_(kY +۫w,t2͡[eg6u2r_#mw„o+kPrXo r\\'gFt\e!tB6^H`~v?*Ir|={W">R;]oyMoBq۫y&q[{kAͤ޵cujϋzzu5C3}Uo"Xb-ϋwA5:u֮|s/00ߡ&5_@"d1L,}v^|փzYlo>X{2xFRG7DWyCͷфo)kȟ^oo_uRe#؈]CԖ)Z:|S|7|/YwHnO]F5Q?7Ao_&گg&Be<<߇|?ma}:|}[I i Ooߋԯo_k݌Y,yC,Yr[7vokX̷_n*V/ |[_7 o,kB"&x^${`w/JJe jjm&ֶ[>Kc_^3xv]5_#G1U }nA[7[WM}+:lk"~O bub|WR|*.?*mv׶cTC=z|/[KCͷބ/u\Ӭ,;*|adreRYHffiڵ^i54mw96zgN~&QĚ7 5ԩiX4ܿZCg-[?zf JgS Lu}a IqQr![|}?vkΐ^aUW*ղ!"wNBnmN7Rs˜)[vsnzk0ΨiVI]Xg׌=xG{ 0Xy1{yMuSNd>Ku_/Y֏{{> O1Ӥ~ZͩEݪZ=]ﻖu4nڷN~d?/ߏ8_'Żi{~ 3 >?߫_#na8ϗ7k/u`#\[n5A΋;?5_>ߎZ}?~2/Q T?&dc j`Cڠz~mr$ǹFKAXuyZB_o8૧ys9x^~:nIg|Sm|&|.ߔ\7>w<@ˁCݮ|[xނt_M>I}Vgng XaA>|N~>}r^|oS|>Z EHWhb+S1bRȕ-F;qqq۵rZ9~[Cs&u/A/AgW0g;\O, ?};hvG]yCI^N'kkU;Θ!Nr]H+=mo[]IVygyWAw]JgU̧k|cWߠUzko }&Yo5WY;{ƒKKz-u/J/J7 '9_!}Mw-t7[>f;o OS :fffot&&dj$;߫v?|RϷ]({ͤIN7Co/Y>w|WP7>Wnww{p.pGRWz s, j\X"ਧ@p^恸\z_/Sb$aRhk}_Au7k;uNqɅ􉳪O.$S'7unR?ɝ/wFr|=4\u苼 &y&zMS`-WB ||{/R[t.W O9/$i |Rxvs>j]&|KW@>ϏQq9~Eui+v+  ף\ Ə>7G*}]?BdBw#y?<۩wS55 |%FSLX š6|[00)S6C xP~Dg)○j&|_%_ˢaD5p}E}(y+运>_,uϨ8p)|ZRb~s5z4 x&owzK~O@z/Qߢ~(iPx$?y\\'op3M\u ՞-f|/kՈr#C9;YQؓ((w qO Dr\e{ld> c7c)ln[@ލ $|O #^{qnNuj h -_Tza[ #O"+l*"/{׏^/,q8Gac G:9-778 oGڵ@UU(LoIJo޲كnot|Xd#IԫiL3's<1y֜k}[kgn}U2l+b5 }|5t}0&_%a9H. q*9`QgqrPI|+ dz_7o)0#7qQ#$= /D c;81nB`B~G{>U|D0_ѷ>" U3ccK ΋3)׈6sPs4 !c66U8Op??_c#x7x!]}L%1%k߬"EU_LeG_"Xk$=Y?~#B 3㗡aV;o}UqH;O|c$-et~C#M\y--] SbSe4׌ 3FA1q-egqN fƯAcuYYqoqp@K'8xBf]T:Y%/Q?/1So'q{SM9m _c8^w4ש ~'nK~Ϗ*~{e*xopx S?yaTv\ a ӹB9Ut=WW۱97LEFM}i{i:t ZćTr7qH}~O 1eMmtIN>M/߱cI<_[56obJJxK4f;&̀'g 3xO;ߓa/3B /khǯ>*>L]8>WyqTwUY^#Dzf Їl5p3Trppq 9Ylijc699pqiX愫8qXG5>3s#b.aWw5pP5pxp5*}̮j\hcܿͅ) ~xjᝂ߭v^"8Sk5"ɯͷg&/\ gAΣM2GvS%w!5FYn$wbe]_\'Vݮv"o$ѬgZh欵ɐ-ʕlO6 - 5y䜠|Ru<|\ى֟#:}DK̀7ͳ>­{R}y.롻n"D[x_7~͑e&oB-} b}AZdVlkr;hep=6Z)r :?6R{^{"~,Cއ>&ཛ 7 ~)?}Mj5fƽsG'ٚ~/!~jo%68G?we!9+ԏwC Ļ!q^qr Rmq>,籍 T9 Ws3>#dn@n>7އxw8`׹=HN̳/eBm\?mh׹Z\6sL99h8?s]$9Qx/ps6˲mdG灋Σ#_m/Ia|qnےK[# s:)B-f,g5wyZ[ҁ@lv vzt(d;꥚[w\7km. ǂdE@AN˵<ܵH<^ -bs:ХY :amYM[Chcs*st.L@wSž8{~ wk}Yfڢv[`/K pev8X?gυs9W{ @oy \6qOh Oߑ2g=mwmk{^]k#Mޖ@uY>ײ.B׻e]u] ]e]u9nB].yC0Ǿ!v™@<*c=+sawp@ö=ԏqM\AhAqAh;8}sI]C0KJk]-r 4؛@V3{31=3݄oh#l;{hiv^:q"GeY2} XcG,#LU:yNt_-!پP|:~&TWv6<Q^i]M5~Wm+1?6R3x!QhҺ M\3z)׊nc4̓Z3aҰ~A;W|Rbyvn,eq"櫯#h=Ѝ߿͸Z Y<}SXsLߟe'2x,>YαOFwMG( [Ѽ 5ˡɜ}JJ7unxv/w~tkX{Qcxmt@;7{u $?'`O mO|;3|h.?AV>:PG]ml"V9vD@=<<7oH^*;t#_"oޢW//MVc^6$)Yj&RnSoz Vly]L7|Oy[3ϦUE$ɏ=g)$߱зj)q8 ۝%'}fv>#7><]f/*~_CoiQLktor.eh[;#yەK\\涧ިO1[x)5Nr\~ׂ_ཙx/n x\й-r_ Hu+ <4-nG\tO&;.O2κ5S'CG3ͣF8/GF)${Lܶd ϊg ]"[yN+Ywk.4RZΜh-ɖNN!4 -kJ&q+29.I"N~_1KC%}Qjx(ɬ_Q ~,gුI(G}r+%6_?q1||x,˲GsG`ϓ,BĪqi⽈ 6PB>w!,Dz p erD[oD" F wN#[FϟFnEhϦKLo"cds^ .DUowqv"ܩK8[~_e/Y_\e,=zYz)׀=^~J,ض?5twMÚ]]B}wvޗz q#]_.yJ2{^򔼜W:rM71V=U16knc>!.͚';Xcj3qU@~2ߚݒϻuukZ^q xn9{5p5قse8<EԸPg4@Wu(ֲ$o@P"vZ_ne{&b7З8;878b? N8>%/$K'%~;zZqDi)/˓zLO>E3oE3TѻrՕgyMDu9,ywsݶDDh]H$>A"@{naq҇_rsPh;. nXn_ x ^mDy/˲3M?|r\Yge[ PSWFk[ҭ#B:usYYY׹Pnu7~pU;] TUպ /a@2TT4 !l t8f,L NԵ"4Rҝy;v<]:gYk2c9ל\kεp@ЯwBjA9kSI0p8Ϻ>N:|Ý~wR ƧNR7e,Bk# TqXBr|[|^u16;#? ;჉3q+#GFCwhIqj tK$mp:}ҽT{l{UP e\Exm)re^o\~Sbz1a.Jz)ҿ2?qKzmc+Io@{t}K Ydb,E b'홃 9hiȁo`zDmy shs=I`<ٹ]&H3޳)2zLY+ÆLw2w|2G}3G}#EUQz"}y9@u#g3!LEga*M̽9 }LC 3&A*Fb%OYα6ְ]s&L'o8v?0툕!HocSD%3꾰*k.u팟)1Hr] ]}.Ϩ6y=Tծ[n;$|~g'yJouaga֡=RO"k0>[yc7e[҈'C% v0݈'3gUbG}|'}]Az6IA8ϳߧ"0s{iҭ;>,ϛm^z~m\_NCل4`\=sUHv$?\pw"z'p1wQ*T]hI@NHOCLgg"#cx;“3๒Zp I{x 9h Sr })i%]9vA߽2}!Х۴q zU!J7߆?6tTO_"LzΆu\#jd =r&K]׈gNb+ȑ \Hـy +!d|< W9& ;pkC]:^/e)I~SuSNum-_؁z}*|kQ~:sJ⸕ql ǵu׸losҀz)c7`4:^ kB熵ZX _u3VVנ5uj16SVj`Y̅/ IG`J0tj~ל;EGXIV/s|}v|OW@WąhKMG/H` \'Mi2I͋W65.M%k ǖ8G&ǂe> R5W1Eb`Å'z2}_7 r&wv^p/$n$kAb:ZEy(X 4׫kQ'R^n{~ i߉"&ojޞ8cpJy9s$Lfq^0Ro{lZFy}K4—6LC~0{fEsyh Su4ly9YU).\`[:d)6:*&J Weq{ɧyVǓ}7 :1HIַS+΂,|ǁ×y+XG[$M.we6W;a?M/mrk#$I/IQzGϸ_??MM"Mz0nڜ%Kjۗ1]?[bZzXY"9a^=?Ow3{%vu!1emty|>{g)geϞZ;WWmjԿeF|Iz ~9 -g9XwZў юHhD{ TJTH;9m>OCI\G*"ߕe^HB":. 6o70wsd}s vNb{vV= #T4ZwVx9|O:Q*i4p˟W6׸9VwI .ZYn5 }&6Kba O<9׉Ɛp/]%2zX+Sw>{>XV#ixŰa m<)URWH#qȤcqq}z-/VY0^챀>ac<qg^3ע/ 7}!/EG;A]"s|dF OB򋀝Wdmps4Iav\3s_:=~ Q@Jo$\f7L!O *HW:.DVls1Zsʾlt}gp4Yh녳;Qޛz[73p &?d Tq=iU;|!i ]?F̄3WDoL}#zMG7og v4]l%yC~waM S}sf%}X/l%LWKl-)!e iS6Oo>MuSQ'o wM5h{cB䥒]22=&lyY({$ Of,r;7溎s[BwuHYob%-Owҡҡz:tԍE]AcHЕ*i1xrH1M'5P;;*vY6U$Z܊ȳkRZ"f?fBg"ޞ?F$&&n QW$轓%=%ɽߵbs`4}{| { {:ʉ#H6?}y߹OYcM F:j]e>8Dh׿LMe\d|Ŷ'hOqm&K 2rzA_zm텶i酶^hFz4HQ}lqv|HwQqԋ"GIƻn}'7wfEq?ǣFJwx!f.r Q$V5~^ 7ڵvabbcmsM˘*P~% 6F)x_<&807v07J-ڮh+[-="UGH~V[$̶x]w.oI, I-e+ʶo1eoU6K?<_ЄMro'˶%w7-B(\;F{׉޾{x-큼O8cVk.fe64-F[\ՁXp>_uYېopiskJت]-8k9+e7sbi R[9M+q>}^H˽8/ qkI^j|E!Q61Ҏ1ݫ9nL=0o8ǽ;;9ߜ>HK;QO}&}Óaɰsdw31NǸ["gdiZ=c% +^֓U8ιOb2$=P?=MWZ4M~hز+s>8~T%&=Hύ;Xv b 7cgTT|9`K[gVG{t?|tAbkI:"*h\ygq=<.JLhga:y!?}_[o;_+xQ;Jɏ Up\ޜ`P_U]##J֗ٷT%pxnI(qD%fy6v2< :| Ci_KCaV:Ɔ苠ozig |(3!ՇR/}(/|| o?yGl86%tU>Tw|xKyCWAx6G>XogCϻ{C%/O_%W.ȿV>T1Z \,e II/0vvt|(wj5A{!o?oZIYoˤ?'}A˷ Uod{x?Bџz7c֬u~`z~ُMz~rtH89~f~fn4wso} oo};tj "郤^9O3 e~;_-r7߾oe~ռwT jHTCзA6ڑ~GfѿM$yWζy /txAauvAٙ&_җ:_[b/)mz]zUǍ+蠧ɽ67zL/\:'W=M7_?nG`xfY^{W)7}׳nWmv3 7ϩn8^/7_kd^im,@~Uz?_F~z[!v7^knck{Z p;lWEO?jqczwsνI/ EVInm?SBHRIeaH$! E|$$Q{ݳ<O{9}>^C]F^{qϲPŵ^|ދӽ/z1Ƌȋ'yqߋ?j/>i^ыy1auS./ۿx1‹M//&z1Ëc8ًӼ8Nj _j/'^ƋC#ԋQ^T/ ^׋ŕ^\ŭ^}^?5ԋ^WS^^|ދ-8c8Nj+͋g<絟x1ߋ͞{qkϋ㼘O]^jKC4/Fx{yqo/Nl/.b_/n./6Mk/n/Nb8ȋsX݋<?zq7xq{qx{ /sq(fyfҶmd0'>}Uv]-ײyee'@~i;A֮Sͣ у n/-!7.{5Tu!-%=.#H'h' ZǭKeJNvKڮchzƥn4|F{iYR)aF05~L wm6KYҶo|b0!;9/.sdt _8|˾L 'HwvpR09]/+Uj}Jq;I5$`*r{|I⒓I#Yqt2V&+-26HM ONM(;}"lR -P]/ҖEŕ^˭?z\RUݻu9|T*n,/(m;ӧK6?7CJ(}dcˊ}%!fNtKpuj7y Β'd.ԊV*A rUjB0#y c:4|bpA瑷YozRj`tFݩQdd]zNk{e f> )#+;.96#-yޕ$ӤmLoEw<(tD_PvGBsuuЍ6x@H"\urЉa)煢8^<:kxf gz?geg40!jHIJeZ/l3+@fz0>ixRቂ35WlIʒ k+w@RjRY&=7Z )!M@\J@fszIl"Y缤i  y:%}hql9t䁄#ĴK-RPJtW zcwBJt &Jtr/\nzFZcTPi[y陝~LV_`ĸ K;\S žDBWZ]9ʁ"+.ЏUV`0]6X ;7)op~k$y XfTαd%{TJ}-ޙ 75~wI,.˼q,HǤ̸a\vaqc:0t2*;1&61<-#ŝ:pu2ۭ~I# ^`*}TขLKL$>Uo~_#r gb#;>tNGݪ$ES>O% SOIOܸFI\#r*וMɊJ͛JzVS %X{X>k"nyJI"5eJ=DZ䮱yW.mx~|^-^)<ڔR9MJrHg"`_9!$,3P2j%%PHI(%rW2;ߵUIg^JD#MɚnlJS4qt2>Nٳ^^#dtS Np`ȃFZbzuyF4 Pewr?H{rwWhKܿqO&Bu.,t풀tں mKWO]wTMЎY)>F+^\ 0cA\z!}&RI+Ad"~ׄ}79%o3{r #k-ݺc8;Cѯ"V=5!Q樂O8@JKeEZeֲhalםx-k.ג|uW4%1Zvwңql%>,ǢZi@KĆ-ԲsieyZer#-5y2Nn=)~^{~x#fRMe|՟>d{8u9s sYd+J2c lt|%KrCZdtOr 掇 3=dcK r}BŖA}},_&?Ǭv%~,Bߖ-m[sQwϾe ," OY_ɘ+E ^$8/؉Smg\ozAXUl}ir3-,'N]_hCr^HPLabS{vssrnfD0Mk$},_zU[d'= d7;:$iE)=chS*x?~o={/b\ޒy~s>~273c.Ofϼ:qLٗb'/z2s uFuxm9/,i ~O_}Hm!1;@ǫEq &oD_Ґc9;$z\XG#<'Gl|RZIlJ H mQ/9e=m 2yyޝwl>_9a bD}&-[m9-mz}}>F}Ϛ0' ~75w4jK9B9׬✳ ufG"&x7[Zw{)Kvϛ<7/tJdn("MVs~ _#CQbb"q'Yׅą,b\u/Y 0NB"yI-*,ns.qYO_^]o>Ǟ@m$ghb#sm )gƭKFֹݦNTbnc\du i$}b wg m c(x#"kV!+(Txu߫"5ۨC3.c tӿ'0smٌ1~ܑ{kXϽ6#t>S,ַڤڮ2W[y:$ݭxuDk}AGRi̹"J˦R9`[[zqs$b sSwKj2_dDNP{#2Uw>Y9'- 1ң3m:\c"|6s߿ܿE X'צr؏ey\痌Yc?1/cP:+ONu?I-}GC:rHz[A ~¾sߡCVxgtHM O";􋧎=_omȒQ?EsYTP[Y^hw"ѴS3K@y&sϒ!-^_rѣ!+&3,\9y_kvOzo;5m5F>CxKy0o|;.jq:?K~.yʺ]cΔq;Ld=F|GѮ<^T# sX1 L2s7SpL=s B.G~9(?"w2P믜F#,7*U[sw|E7հgt69Y׶?Iޑ\Js0WDKv;= N4Wux0$wd!N>Ux`AG?܆8_Gr .+T>Id>'w}\n>xZ,R XO>iXZd֡> lqY5ozO?O}wаOn&OA>Z#J9ԫvqe؀}8OxkN>53 'P'_N\)XhU'X|ԫ~g?ϱ5V\'7}DAc5IS$ |>K cr6bN8'`b v ,`5;'x8yKcs7xs}2?^tGb3A蛸SW0YD]/]x H"89m Z$<42'\ԒXh{ 96NhE |K)V 2} \擛2BTkZ^ }d>i۱J3nk86mɋE ۑoמ$qXwc=N^qv'{м+ #Jdc t]q)z!.j_a)TO g\ً2`+ͱ!cp‰eXsbaCYq/DX|{u Ffl|A> bn8s0}jZcppŸӰ 2z܈9xK1 O=4~pZb(yCidqlFa;G8)qWqQuӂ>Ho1x6'/JbA E|38La i bzOZ@0"L|,IWpmx Q>{Fj<.Pk u͈sr]܍Q,yq36w3}qpޭ-~qOumqE:rl))#nh:svD_# Da #n2.wr~{8.]hz7cS|~@{kgy ,>DZq7@Ϲ=X8is ! }:C sLx ;A/h1y/ g'b/bgӏ09D\c:"Q=XrzzLkRxM܁\M81tD<`^g0 8ND#s{q_a/0^gc1 \Ɵ,c񘄧~G<,0>~Cs }X>up >Ow)EQg_Cɉ_a쯤WLJ_7i[oqCki7C_wizF~F!n~כ1қȅ63b>9YxB}f1X؏30`>ĩ1}+}?gՉ j|gnt,VDDtp܍gݮK.ty:ߡq' pC3w$Hz#X'"tab_MJ4bө~e39.zx'OK?,Ob"¾`MO!lE)fc;x.&,A`ɜȇ9',D7V4ꗮǰ~%NFa$f3ys+4Μ>H`9qLg͢!✇/x6؏Ӿx;qlCݹ1y<~})e7_^@n,çЏq}G?\x8llCBʋga@E+;pbf#T{eE(3d,fSP.<]h4/a3?yOb7Z=˘D܃阃ǰOc)^[؀O `va/cF}4…hk$c n]Xc-'c7CcD5hh шAo u"p &a*|,2ub;va/obaGnx9Aɉr,!JM%Im#uԗr! %BΔli$9WyTΗfr\(I4rK\*QrKB-tI:KMK)UGJ?/d ~z\+I@J xI (IrdITItA2$S${ђ#cFIr*6](w$,w]rL{^O2M̐2K̑2O\yTc@,<%KiyFWǩ :QNV):UTizjNWg*BRgF:GTSuj.PTjZUKuj.UQ2Z]ڨ+ԕVT{AuTTgEŨzbUjjuUש0TP W#TJR׫*YT *Ce,F*GQ7XuEݪƩj&;$5YݩRw)uOMUi5]=fjzHV9jQQ5_=BZ'SjzZ=Uy9zA-U/ejZ^R/WJzMV7jZRkjzGSj@mTjڬ>Q-3U}/ԗ+Vߨov^P?OjYRW]QOOVuPe+і}گt5}>ISt }ktm]Gt}@ u>S֍tc}>W7|L_/H\J_e\W+u[;κ]u7]=u/[tW=P҃=D_=Ta:^'GD#uNѩ:MtY:[ңuo7f}Uӷx=AOwIzSߥS=^}z~P3,s\=O?sz~L/Џ H/O~F?sy^_rB_֯U~]oMF:~Wި?&Y?[gz\o_/W:_;?ޡ?Nޥѻ7ޣ{zKGԯ6X69oN0̉$snN15̩eN3MS3Ms944Ls9429\ĜgM3s\d"Ms\lZKL+s2r\a4mMigڛd:.&t5Lw4Lok2}L_7@3 6WksPgx`faMތ4&Ť4nn0&dl3ʌ69fdƚ-V3fn73a&NsL1{}fL3A34Cfy1s<516fl1s|a4_||kfh~2;f6f5}/o/'~eiXe[q V5D$duU:ժiղNj[uV=::jhEXgZgYg[9ֹVͮmױvt aiem7Mv3B";nn/[ڗحK(2}ƾ¾nkGvlwcv7i{۱Uvoڃ5Z:;`av`;NGvj vig({coo7۷ط{}=ɞliemOﳧ {=~Ȟm?lϱG\Q{~^h?a/OOKgg<9y{^n___Wگگٯ۫7-{~~~^ooo?7ۛ'3{ηw???;]/nW7w{gemWvc98>TsNtNrNv;85SN-4Sǩs; ӝ3Nsssi4qs:;͜ H¹i\r.u˜N Js;N'ʼnq:ݜN:W9}N?3 r;W;8Ck뜀3ԉs9Nt;#D'ɹ$;)N;78Nd;N3ƹѹsnsnw;$gsss~IlmٶQ`p\Fjt2- (Y:[2,.3)>ݱFqµCz xh7p kRe]Ku\G ]Y ,r``9JvP[Q< gtvt;ZQ<4 * NoWǺ-S}RB >uݰ_XT ̈O\jvw{;jeWߝ3JwTQKṿ;{Q+pܕ\٬2.]Ǖخx$fˣ_)8QtrIyRTX*r~-bcJܮz|#{W/Ku9J+~/,ugZvRr9Ы]ʟTFsغE SSWwdJ:IYeygUxaY"Wv3IyWRW>Oj{TJg&ctAMWV*UWvtw-S^զ=rmTHYiHuMwU2RVn2:RuSiUDt2=]fOg*C)+7:3@U0Ii'ŖnV?+pCv0;XZ`T˲wk̥-,f9A؝7o]RC9yjH+zD7g^FJztGcB*M jb /Tb-3;+s7DOvjAKv}0>?PXroQ]J/~]ؿo{0=̗F)zFCR-2vh-jAV8TB?fxNda,)emz.'2aU_Ќeym^v_6Gls#mx ,ןXzE=kk{Gܲ-۶zUUƒ.aEUG2U⎦|(en*wxXʪ.;aUsY-]w6e'>mA <Җ)(ÊnzĻG h^}|/ZPz󲯻q-sۼKe\;$-iE/G-<[P₲hIK.'e.O7B[eW[ЭTˊKq?1s.grQ2UсUqUaUQ{UQsUYkUg)ǿ(+`R:mȖXC*~ ߥu&+~ ߭w=+~ ߻*zqP ShٰeJ}/%.~ Se]ДU@/Jt>wiq%vrZꪧB*sG_'\%zB%tt.S ]+[3ttЫzW:á?~W&I|*Y% *IX`٢yh&yR{+quf@0s]2[fhU CŒ d3U CIuIQ 7ו7E &,ϿuJ>?ו;|`~P0]r*#̜WaFYrffP0cB9~~Pp3`ŒTEㄛ˟`SG*φH]0ձs6Rc*ymޭrw=+ym޻r̕Mx,7pga|쉏{k?r J@\ދ0Xy73w0Xy73w0MWô\y73wJ{IwfMx,wfg'ά{ 77CIWr(f+0Yʟ dVa ´bʟ ̴V[ʧ<[VEn+p\2TzxSݥ%}@w/S ޢR,)X<o>G(G\&帮 UEWV)1Jxl<:<*<,۹oVBZETAE]/*%PFiٲ _;-)?Z=Q;^ھg6.*hv,]#AW*V;O-OK+m5(HEE%IV6E-VXAѢRJ+XiEJ}-mҊOE-ZTZ{f}t|3g̜y3Nz,9a!- glɦl#fF͡GHAǥFͬ(sxmv4UGZܬZͺl.Kv:.ܒ~Ou˖SOg{˓Wt9I c)0JB׸j8k}-_>j򵡠' uRto*F9ĥ>u\yrjշ[s'T!f(7ճ@xPPneSoeQP] {VKvުnLufҽ1Fab`Y|C,U݌L4`{TJUflc?p2(sގ.Y?#`}2 MAۚpb34-~]{W8 H^e.Z0rVYY<sghc)ebnWcmiMcmݦᓢ+x%N(H[) ?v%O93+鹕1Θw~Lex}^ˤ,@H?OkH +jy))])&9T4[fbn}-vzv3Pݟ _dVJ"EA冦ʍu cZ/T6ׯXktq[ 4Іq28+QeݧO8!LT sP˦j+h&ZTFy0E@K(8xj7Ch`ntíH42 ɯ`P7IU#m1{"z==+# 6H#$K3΅RKD$YJp Y0׌K$#2H8=/B2ɎrT8e(9׎ƒAgUTT7;†C&M}u$>$/L}ZRAרoONy禕0,GZn}EEIš-b۶PM:' RA;הhD^>ic6LPi޷D1_ 9c˄5wzSIy>'I;TrM&Gߜ$ԬR*q-+TMV((Ci]JT@Xr8'wk'^c:IZ(%b?ȕȶۛ 9ZOs%=V/댟 znO;HsOT)kl|\>CV'idZMJJ^u)*:1\)Q__Y!ˢR)takOc| A4hAq;&e^\[$垛]cIXBnDc.E;U4áfUfQF3IR;YwQ^[` _bF3cJ"Mr-kiJJiw#Yt?.O85yvr}-p2: mbJKm׹6Xdrnve|=Y0n{:TI lum& ~u5MZ]H]ȹutݽ-A?Sgw=R|*9;4nws^b3ضISS|[3R:$&5)E*T7Os?aI?'NM :3`2sy>[L¬F(fZvNĴ\XS)]yѝX b>WL=[|11MY2}]FzsWB4}>ZKR- Ruj}5Xg֧^֎s\s8x;΄/:;΄z;Ά8g8;ΉqΉv✓8'9'qNsNt'͓;)m eݶ&HRwڽ6WVZ+ZG\qwU爫W# &:k#)w;Lܓ;;sbzN ω91:'&csbjN ͑922G&+֯ .ygsYQ[Cmgf,U->"a)LuNz s*֞Ξ%n<R]6E6_*EJ BA߂\IւsX mb.;Gւspa-87OÝcpESjg"뜊s,=˩LJhYsϋ3ɇ97$kǢONXXZͳiYh*`> Yͯ 1~!Ꞡv4t娭M8_P,Ib\N^)r.6K%9Ȗ#Y]D+QLM>_6+L̢%,KfqS=gDTkt-$ؚ'w5ƣ9ޥ9ާ9ީ9ޫ9ޭ9ޯ9ޱ9޳9޵9޷9޹9_w )=,8S|(l;}~SY} 'dynIYހLfrň#1aRg/$ye"F"9wcvg>rO (}?lI1qX׸lkS~ Ƙ-GS~Ƅ3?3ywS76Lvxs;sEBa#Sv&.wMQۣ؞rIcrg&i*"Bw&Dێ32S4JTd&}etnbWoC;kp7!D|nݎ%Z),cF3Ka__n%(ǹ4c+fXj]T6cyT5TrCdI0σ9N[C]hnbƥ_qW.f\u~bƥ_qW,f\~bƥ_qW*f\uW*Ŕ/ebʗB1KYtx̐Eoa,z2dۖ!ް YVeȢ7)C=ɐEoI,z32dې!K3X1S3ۍo yL}gcq9;sY1ǜsNr91ǜnYr;ϓ۹Uk(^ƜoVks%\ASLjjlȒ2 4F,)K@cĒ2 4F,)K@cĒ2 4F,)K@cȔ:24L?輍N΀w9a#Hbqs+h^h~*_*ˑ5Nl: ;CcQmjE6kƟh6|4ad3jWj&,yDy]ӄPVcղ#0`4-j5v4M:0t؎^+|;9駱;t@R[tn!` o!y)#|570'V"2J48>)o& \Ew,ca;CQ aTg> :_rKZQ(h o]҇;fu̐lyYBP5̏)K*^}e64tR꙱Lz:L5BNWqSji5L,kqM95+YUSS+iٔzaJ7T.fq73>LƷ͜M|${B.NQי ^/h$c BUlr*6P`uslpl p4nXjٷ *03P`Ҡ7+ Ō{ͨ7+ fT[NzYUZHk!Wkz}]!ih~>{|\!mAaeapYX**m eqPy뺔4C! xOJu׵= F)> FY> F> F> F> F? FI? Fy? F? F? F @ sV)XSZ xnnI]rJ=j7偣r^VVqj͆8zpL45L9|Utˌ98y2sј/?!ƛ@PL^Z dWU ud=A-C)\se(ő]ڈS>67nU囯>^ƭ"ZupOa8+K߈rܭ:-Y%~ضdܖےU?x[oKVnɪ-Y%~d䖬C5s`n͜2[3*Jk6G\mZԨrv/ѝ3^t{w0  7bL;=^NC1'sZi7-0XK4sBTrB%#32qP+s~LOey /W=+A^ZR= bfRnd2?n yY"Jb6$3힠7gdB{-uӲ"<-'y2_jNn̖ٝΜdt:3zQu6gL}ɏ_2-ڊ YpOiP\e!ݓe:2,DXHȼsgbT}z+arK(^2vi\*e);\v_vM-C,DlajB$u*螼g&$<3!nEC]TтkdR?ہ^U΍n ~ІYchì3a0 m;6Cf ~ІYhìCaNEcOGcOIcOK}ݹfvf r3a:;Nr"(RrGy]ǯe"USg-UbHiS4:P,Q31ճԟ|e)pC嚷aZ3sHq'dѬ̧23Oѝzvswwuyz9 սn{bۗUl'nrx\H9=#yG5{>2 aa/.Dj2KFuv0{\۞dvqf,叵̴ 1_ߒy|&ol'Qi\kdTnAMPPe .*ɩ,3UOe ʟ_ Н0 S2LFg23cO(3a ex~Ij2SA_nת|>LƟ%K^$`{SЈ5<\3Z0hTwFn5_v4p: YճdՔ!=zV۬'&EtfMPOb;qx:cn_w/Kz#Wʇh'a-$8ϧlOUKpz\Rdvn95fz#wvq p#u=ݘiF8[xD7 hvOlf>M^N(s @&2#7e-cLjgm ڲ̩ELT?٥j.Ul(:&Bgx\p֙h42'եs V9f/nO̮?m{{7̍837ՔĶyWMuҌnTTor tًh_NEX6^!顖}uН I)/M8xTI컳zSSk#Qjl8T cCvCP1_wXKqqbעW㌗?c>i"hZP}<&uX'v|(2I%HBj$5?"L0տz$I"m 4J7untnTunTunQTBzTQ(Uo X"UdnU2O%:TO LBKA& OnZWπڟ:$0\H/ݪtTu]G]ڳY-dcBD)@H'"QJm񯧱}Hwژs=^w[5u7W,R)׈zfr#6_vWw5O?AOw}G|un4̑NjƦy箒Q'/=*Ҵ1~{LTMV0_NYqC&GJ3H}tegP^^8|˫(<˱߁~_#eBO'}8_bG[7u&;psp,amYyr&yr?K?G[ ydؑ 2l,/NϹðsF~;T4,0koWjG=V>ʳ~ ʛNpZySê52K?8 +oyO+nl,?~Mwq 1K9a`TF[D4נEDx+=!5RP {Fǟ;XOdy^zbazPOi :L>i'^ ]:Z>wy 'E&Ei"WvcW~N/ uٖGWTcS+:N'm>_k3\vϺm˥k3\n̯q!x+N⥋ۺ]Uʿn[}G[pcգ١fP^Oy1#FQ9jTċ׈f]L]7*yoD7_ 5U+1K+PA&=8o"DtepOʨ(L#Jf)?8-F*6@WFIqZMSuyu➫؁Au>)ruk; @*ꉊӫR1)G;:1A|'z9,Sat^ se]&lMѴҤߵ'VYoT͡3?yr=1CjN; &̹f([+yɼDmGMщ&ٰv\ȴ,饷l;c1t,BkGXW(2/a5(!\=ΏQ~o)/S.OD+"2"-V_ݡ}fu+ylҷYg92l|1r%-~v'lP-59{0J>\X,Wnk_sf#ݬe}<@Y:;ꉢK}-1&SnX=M:U l,qpPH*Zmrh>`Ht:LOa!"?nv_mdO7orA|w[qU|~?MmzZ[Z˛r9곅8#Ζ" w=1reMX2mղvy9\#Bh< qaM1Vϫt-xc_ ֌lGz4xr+\ Þ Rf#xOy5||[GO'jtp)g |=[{l(O#Cϭ~F--pz >K w}'AF׃ Ŕ 8g|.U)B|F)y |@ׂ"u $ń 4;?|!ΞF{I??]U =Π. w6AEX}v3x+z.c./46s-Wt@e 3 |(>a4iW^Nՠ {! 8Ѝ̀^(,˯uZ u^)s=[`pUEz~x?j!Z5ZP=^n:^#/hq_+V>_ O_'/wVtvH\^^~l {(D)< QkZ ~Lw/gggj \X ܥCo-$yi:u MN;𧄵 <[Z?Fpxf ol5oK^@89mAuM <M>KB6 |j)rp+cχ|Vw# ,%ueR\w7 [wkc%x= y|rvC'W]0W1|{%8^Ym;x}2`mt/uh_Z",#߄Zw> yV?y@okiaoߦK5g x`Pl %vu |Xo~p7Ya'MxLπiJgI6{4zx:'itXM?$+g/?N}@_O{W|6g~B1Oȸ3OHݶohr vx'D)g'qk'R>o/9)n#v3 zt;=# $|_y =s7G~ sgr;sp?^K|Y=_y Į qVɳܻUK %mׁ'{1FNBOL ~G_+kͅ5zv6|e| |Lx/t98ez8ܚ롗n?y2 U^GzEA๻{ʳ,㋲6|k7S`[B6|+I$o'y9}2A;wdߵ lxWΗ3>|{I9>m==Y_/?(vwxÿ)g3'tq@hb0XM|A}xھGBLH>_ #?KPd(*S X >V$w%}\\""9dwJKˊT<[V$`aCPz_y?*scc c%)B=7'u _ L{D|(xD|%.m6J: :D7b,`I}NqK&IMta֞/FpFgOܣtI/ΐtSwKgJz9x,PGit3x/a2O\@?\X$={o^$Ǡ)%]%g?x1AO]R$Jb`0 :wԍ<_#,>C7==|PNJ9rhUn 6*oqw^ɳ|x}^$C7*HاW/OV$P..p A lR/Mj]i@/69-ai/uo`CߎR.y+}N pm3ryːtbQw rCa`t#8.xWD_/ûnXEṯHL=̀F==o0~H\nA[#xȇ<õ < |bgrg%#,!-1χ ،BI |WMuB/D/bu= m^ыߔkߢN/|гGߦYI? =A/w6BE>t3\ fgҷtná3H At_s739xjQ ]T\,[IX{UX,s!>"ЏaŢX+-3/>O9? MPbJ.RXu$# nP,̼b!s{bW)7Gbn s< 8:;)srKyvA<uCCQ,A2<aWY'"o39 I xĩLN+CHb~pp,akb |huwb|ʚuF> pH:1(O`YŢM c bz3=X?ϜW,F[]"v:g nj-UʻbX 8 V!D1 U}2:7#{&s |z E`T?SeYgL+;y>h:uwwFhIݍOf7y</;{ahC=O\DySGU>lvZ,# ^~Mۋ}mb1;} >gxV]n N_PQg./V+$셔]JIg^%يWcWIk$=ܡѻqip"I\/vdୄrs7zx;aJu 6.'WD[ >LX]|G }WG/)OL[w-W< ;Ţ6 1kGwҾx^zWC>p#8CO~y>|8qn+![%.vSu-x[* -ik[,G{]G#_}O>y_>K>_|nXI%bzp&pTC;&A_1t* Κ.9n*+6iA $vwO]$C%lzD+KY,Qׂ kԅtwIݔqgx?Xt s:\( ^^pU% {}הk-Q7ˮ2WՋhk<tG]/) (cZIW yւd2$(cnpKno" [%ꙮz['F_/Ϻ~F9WnhZjLw4H=kEo&j?x2iWdLk%]θ]Ɲ .eK ])sJɿ|ɳ\\i'OKM)+4S6)yV3l"lno\)Z ~LwcwKzݚ`=n4_ׂ/jA/ߣ@n{)]Y- <(edԃ۾wx(R%btw-mFi z HA/]5aЫwBhpJh&ߠ׀O`c?&??a,P9cP"Ķ.K?y-) =M727lV7(a78hOS7'_L\gKD#t-{ۛoV`َ۞W^>$] Uc[(+l_%y'~z:8﯌/sw :c!`wKRM);Q*``T 2FcJJE;t=)++!_xB/~g#C|R =ۆ"]YMVT*~OhNR[6T,Sz|rxzh.#bt7b%򡷁Ǐ(W#(\yFpH^ >BPe}FAxYb<gwgKsJmoF'>畊wǕ2~F:=pR^ >qI"-918S*г7疊UStTTV'l:j+_l<'Kձ\{ᗖF~n3$},"lS(UǗF aT!ڹY* 迌V(\& yWGewktOfsB}da:+4a[`RѸL\>E+IhC>1a9 Kŭ;K7,/wJņWu˄*_T#t,!4LS*n!K_ 2q18q"WO8Z8Rqu2c |0ebȢRqO9D=†?7e|218FtC'zL|cqX@fxMȇ&xL M-gQ&N6va?ח.}eqiq]wSL fmQnK /?.# |Y,!^wwoGo] tSպiCZ"-0Zi)TI 2 ˠ2ZP I+)! |2Q@! bU SE(< wå؊xz;ߟ}?ݬ&o_yڨe!9 ,R:!y Gl"]cNfqc_Y5IE߽ٜ"6`S -)R LkE)(IH_ )bx뫘O;1 55X\n0ٌ9ODM/ (|Emd#vy|HjO!P}4( LBwSdYH$"-B<\ :Qx;Sd9(R >](= Jn,Ha,N6,?{Pd X@^@ =)2 */s`^lJț` ^M@ao|Nv If  #pKGn|/E΀X+(R ~Տ"?~S$q @1B󺐔{Sd8~+g7+Pcp7`o  H@wE.G=@fEg@ ^쿏"ࣼ3x6q@ "߃~!y~0EtS48mԱ;"vb3< ]Qy4x //xui^{L޾;~|!G</#$)FxPa_+hj-8qoT )gxy#x/ISU |?2yApv |k~!,.^ºXMهG&<2u3Cz`qĆ"p/SZwBmOZp=U_<G| ^3^<Pr|թ=IOtс}':~pܩN+ X{*ϣ2 |o? N8Qu|/׀0O EgWZQBEOe%\}&Xex/g'cAs#x$0{Oo7p۳5thl=+9ʺoQ#Կs7,|vtb90L'\#H#82G dx---p.^t *Hl%"2 H! Ed)ѡI&8hà3 /AofU1d0ϐFK͝gRtgغȽнՔMOĪ:.|)zWSJHk IBgLJ`t* q,vǽ##O+K3u gaFV~w4F@>qKTH=J)yWq}\,rH|EmQҙppf;O9LFhψw5HD KH6Mxqcۈ'dbd&`$Cq asXDEbZR-Ef[r, KEiQYԖ2Ƣ,z ca->XZKRg [-*Ԛj2ܚcUXJʪY5VUg[}V6Ǯٕv]m/kZή3v{=huN"!qH!s9#ϡtjGC:tq8jAqb)u:i)w*yNST;˜֩s2Nsg3uuΰI\"%qI].+ϥt\jWq. j\AW+s].►Sݴ[斻s w[V2ƭuzw]&GxTy“QzTGa<{O9 bԌԐj 2ܐcP Jʠ64!h5 u@"(1JF(3$6ILRS6LrSIa3)M*TfҘ&IobLgSTk LaSEfYbSʹYfs sYiV֬3͌5~s\ck͍#խުٴ6Moclg[Vk la["q4ƱS9aG}QsS:[挸֐.2"b6MjK6Mn˱2VjYge}l4l-bX9ƢA{&nɟF(f>p׸Zw]wbV)h<8h:^kA^U1Ǩ0FQmdgcXk actV8+m*Vf4ǀRJt})si\:x\={W{T/y›UzU^̫j:xY5ޠyz/QđbD0R&#gr(*1F=0,cLaL-b0S 64 v ?JZrʌ֨3~/VY`5ɷƕ+,X9*T:dat ݩ}<=eNgLiЈc $2e0B%LMO^[<2chn)#f&̞5huD;ֱYs`U6tG VZû1ٶdٱ[GdWz 8s1Cy~'̼!fϘڑ?{nZl[Ԉjǚ{=gАWgnK:o8_=]ZiNguqΠ{]'fj__^qeSվeVų{D_wep liHX69 +^=ͪ~1;Fȕ3~]U3 mQܖ<սzœtQ^ )$@T~.L'b:Sz[-("8.Yl,]nZtzxjPcAk?տ}d([Eky.;[{ס/wZN(&cAt א l'LڟrpQOޱO3 -˭BQeԉ")Q=9ԘwOi[+u^[F|9ɂK/ an\nN~XWhk \k>Z=XNټOnXA׾ ;2/\^zE&-\u/ }"u6ٚPTk<1ݱmNJ>'kNߪ>qoɌK=2̖3l.K:|~Ҋg*Z{nkmGҷv&9УS Qy<Z(5IԘ|!8[7̣8ԙŬ NjL:#+8t˟[|$p1֞jz#J'^[9t :2x ^պ6$䊄);/]/g߾r3bw3-3A?`jnM^%"Zqfkϣ+}vS&v9~UUekGzXbe/Z qVhSËJRw[r+b~h^3$X֖{h }פ GsGEѽONI19yѹ+G~9-_{򟚮=7XVW{rw}/5HfHH&4MpNp0=b2*JK_R6S?QSZ_߰\^=}k:/opM]R*R}Rcq&]Cs }vgo>z[\-9{SKニGUkP{gƞ0FFɾfl)􋐲F)[ʾ_n%ɒIeI%=suhiL"y؛bpa!&Hӈo3jW‚Qb5 dle7*A_Jzvy"ۣJ -/3B΅{8=;+x[/$=ZTQ&]pv% ¥Q0O'Jh{ƞm=nw 2r:|A26[܏zsyb91_=#}Hإ+]Q p˸y{\NA*똢<}|m=L{nwUElIYNu-ʌƋVp3?\[kqv(4WUQ۳ŚCwV@LۿCt|re9##o!'7?S\% 0t(/  TVs#~]4w^Fv60C s.ZצÙ+bݗﰭu3G0}퉁w 6ʒH굛#nzI.jL΅vy#%}_vA4_; C(/%G^oH/T: r[v IGZhoB?i/uӀ׳eR3Ơb}Lh(kL<ftS1UJ7 KM/P( h/Wx`j25Q] ~ʈ#NƵ4``Q9ns_`5YM;\eR`s[aQ1v?!L"hHi_:MQ&Sz&Dm1+SY*5W8p-H.)X Sτ3?+Z><;/Vi:.TfĜսGx?Ո h=4Lq7#|Se` ֙ px?F^DO.0pL3Ж,XE7Rt($PJtf]m.:( TD )0g>,ujs?-Ǭ̢^u1tj䝣LoLmo?1?kX?P˯hŠ`}C\8%e8q9%:VGk07`Y#z VK!{=JlgJ\ aG,F^d#$¡NFS_mr,M>a#rGqdŞ[t5N.eiq=h l aSEFk8MfP6Nv]4Oώ2+V asn4-85k2TAmDȇFejǂ<6`@M,aödC-4=jgH[(xD,?99l{[]؜P@N#w2BAƷ@()ꋛ+&rY>YRГM6+_"_s/?p#'LjJ "HN6}_Z( 227Գn<%HF5M"<oW80XD=ƞ8|ufBpձМ/jq53epſ+{M>2 dzuAڷ$bl>mwK'zbF)&+Vݱ}=_KeSI5 %&>Am=>~Z^1)yJX2WL 88=_'}2װeZA8O.9PBH%"Ue`0;P5]&"TXI9&E#}v"dݮѤww*dz `GY' e4^|n/=sԨexhY5z\t tq'6:‘/тg.#/FTrATjO=)-47mB XmO>I0|Sfr v+xbPKTrpo9%M6)$Ap⥔ϐd?~yzQi5b?ǛRA"dT^?L#6 gbRK <1d0gIC}֓$xSm~|k±[87o$S%l¶8a0~|IHc w|yS̘)Ta'`o"1 SjS 7۽zqu`R:H. 4G/_79b$Ȋhݦ'+s\6Y N}'n)4qr"#c!im~QcԾs2ʑYЕ'79u_m4;fÄ"EfXHId3tvR[+QqldwL<,.hȼ6Q-pzRش_T0zQЮv7EElj^6"k783k>6ėV:\-hRjYr#rsl:c+yCrZZY38Kd\vf/Vnfa_[eh?LCaRw\cDr$rBJ"E kMChfm5ęzr4 paq P98DDrʣ:ƒYY I:LC2W;O>kf C5QWˠ ]Ur ti7V=Z{o>Mk?UgC`E٪[eN<$6[F7&ljLHlqVVp:i =[jFeVWw Poo0 < ㏔8n?UVr  GHL*S瞃ݻT΅G]m,?[S*3q8W>:j=Ju, Dܬ'A P9}|j/餾{oeʗ+X\Q'q:3HgiS\M63^bd+?*p*>sW~ X4OWF,n*f$4`ї#$F3;Sp(V=$|Ǎ&aBs^AM+dUN%ѣTT~ωr,W7>1Ӵ(, kءݙmD֬{Ɠ<)/On[̪:ne2X@v2Q+r2[Y/;̺+tû:(1 \% )+3 iC|ӋRúhP(v̮ H 9\y6(l{-t$8 ~3 I!qLڞ@d֣n">(+`sh dc n@G*qL˃G/R.C̈́7 rE։UXFHW?#e$} ܾdCwР4" T4A3u6fEyob?UGlƺ$ֺ3W ;ʹ2M&:? '30@'W$q|Mey%W7!&0 J_d,u:F0?&}BՁUyQ!99F_CoUl%x ;SVyH+O]QLB٦U$\[K@ԝ(52g֭yO-bCP#nMc&b7Pb(+Ez'!HU2~I Hx*5ߝ@%Q7ϛ's!/_ktP4$_^?Z,(xl`ntH9[deRG$J% =? U Tp ^g4uB2nJˌuOrտg+iru4 +A3*3#hi)':q}:0 X^d guHGY$τkdbRt?w=5f=[Yb7]e8Ht}S-lc#}gتg{VlǂLCNfA9Ǒ7t*&#塄:GX%F6b~P;}OOOxt(z]]Gpq"Gt5$ Y,H"7P䅚 63pũec-7@"eeƸ 8nBT׋0>tott` L(!ߢcR^!w+/Yyر^7M[E ,nq H*&j8oN̦hz Jah0; 2E >eoQ"0E`x-[8ފCo`x/_ BN!c-B Cexߵrԃϲjl*=oJص tȷt|UhLnQeYjk45f )]B"Va ܋K+H]J-'hJ0Rl!Z.eⱕ>V^ C/0;X?Pc>NY7g% 7$U#m"&ڲ# IcVM4Iَ3lXnҕL+J٬*[K/ 6Ϭ#̇ŦoEVFng;d,ۀYd"% ۄP TZ(WL-}EQ'~ZT)vP?b6d"%k(! 6!,;@v}@(Z)\a';GQy@=[hm sABu⌗:÷f|:dlފ6~/-^\6/]ϼwZXfe"(6K{zevX%y .wDžrH\~\Q\`ڳd%o &+~6 -2l W{ňD4 qM5N>3;);)*)u Ofc<](6ìV%.V Yqɸٌ2l+(No43ަbS3r)aBf}%ڻSi۶I,/YÄݴvEL7F㞖G} R,_,JO{X\ ͭv~6l3mcNOh-^#r@%X PH[hTl=(۽-vRy Mfjة%3n?WJl " !Pj Q]6@'fo T4R_y!F|ZX*l{2 cBeb\&Ȣ~JN>}@ʐg S5,ꑆ%q08S-883f`FM8'6ύ7p w V݆T@#z^VV},~"k@&UU ]:5Q&V !}hsϬ¶QeL2})dbO8/d@6SMz=l>$;쟂^ղ*ӨXK"|4\];+b3r#(9 X9q nQ>myE" m:@Bį oŨr۱FD}wox* n!Kv]kg!ih!ΒqSŗ`B"e&c<:W–1 ^s|QҐDz֐Dզgr2 /}RBbIÁ ,^E<$YCi㢜2^UM{gl=OQ^F;zz›JÑߢCAp6LdS,Cf;ElI-F @qOELPϘ="0OЊMaݭ9Ce!g/Kl$2Hp<NNHOZTA^5OrK*BX> ώ,X ,GJ@Q t9]fp^yz6h͡/ Ӱkp OdgGbv× O7#R|G4j.U4jZ: |++ \Cg![ |vt8cw%`UK>4P"{[ b}Y"/l_ a M[lѤr@ `"#BW,.Kp[Jp3csr(2{h;Q=4R#ԧ9wz|11^&/| &i5|V:0Dv 0>_>ϯNGu> a$ >m= _2P lS&* Vaz~WҬ7a"ۥAӺK}6l${6VŊ+^ÿЫT^qTG= +@Vi he^3A@:-$jM|i0bfZ#6%X릴r#˚ANfLb?,![ ۄ8LBp.aHW@ET„RnЕ]^!x)!S ?3A1|xV\U|vߏ~ms:%A7vt؀i bG5z7UP^7vdZb'mZ,m걷dM@pE]ִ`dRz^/TuBp-q"FF,ǩX_7ӟ]Jӿ·@z3ؗ ʶ!;a3c?` PXA&R!rxMG- /zLUH*扯SQW-՛pt8序ק\3oӖX+}Mڄbݒ%ʉ6$*%Ց LHJrt)&֘/n1k ,9e KUrvN=;yR@'mIXJhlo`4[@iLߍe\j7,(u %pQecZƸ͸aAM!ڷw1>CĊo&| l&>ZAD@E.Ab'_`,ő@;EӴFGtMD@}]cB8?pWp7_82g ݏ&4vϖ L|qtMsM{SX@Rglo&Pd08MYU{?Qk-OP&lm/ðq84QihzNs|cBs4@[i}}H>52`%)yS6=-nfyMVG.x='b;Aft8*K.7.zd>>lX܃4y"E|sDN*(2u_A}nm|h#zb^ze- &f ΌXw/V^ˍX=kž͈>Y0uߩEc@{)y߿x;<_lx@;?Uf;yNgrv󧜺eڹsӳS˭<:h;}gɹwKN\8ٿZtJ8 [_n;};Wr~f;xN=πv._xvs5tvn^nm3n<1oSMDz9) U3f.=g7@ d[کTaszgn9W)h}6;N{?ysҙY})衄-|Ѥ: o]=7G? Y\zJvn۝gN(I I9"gA"f5t;|9 ϙm &5쀽R4hk>,8^z&zwCfA}]-Կғ52bK, h+x~Hr9u% .Zxu_ e.g&@W zAO2{w\}/w=Ð3e&ԄRd F/ ˌJadrGU*6?B Je^P<-Gm?g9lqVܱAéa75Lٽ45qy^mZ/aSY,OF(nJ@Cio?вCy%;&ixѴBlDD)U|Ԅ?T}&MP[:H*FiYrGB)*e^B2R705=mPNQcyV UP*+"LK<siFux扣gfU3? dA/ø` ;p eB7, !?(|Ob@Xt{,|Ч:ȇ]n/SE]/vaPW܋.r7p/{ݽD=_{&(JdupߑRjc吂[PIq2ER4{_v`xƙjN ooyjȀ5^W)<:_?Y~ڨi)qHp;\;h.0xűm'O\GG h_4W5ӂG~x\TgI^c ?har$_A[GS!)>ݿm$v뿰F6s~9V݆):qh9\:Hkgl1#'~e_gU9(?H=yAt2̙}g*"ַxPO8:L\B[嚳{mۀșc]?K`8Hf{i"Q#3lp$201V$ =MkQu2 3a 3} w|0^ok=OG.ZoהR9Ifa+@1_cS}c[KlaDMhmb,/ #r/rU~?G"e4&/\&Qᄗ_GadQ|-qgh_UY Pj] AM7zFZI˰>m#`_j;t5'r:k !rK5i;p ,N)Ŏ(8I%&b dS:WUTVI|%BU_ r\IϔRPYx\(~ZLժP^ߋk3VcZ?5{[˛{6 O ~Ykfb_'̲Մ)$)?[Nt~`r.V?)@=7hT:(k%D? 5`!\ʼn|,yaq{bbQD 5"I1ŋw }N*"7<,pK 8&ovœ\%`=`;"NJԣнK{a+ w {$-{/2F|7R-t!_ Տ_"Gns#o\gJ#ORŝf+JB((_S?5g/_GOoso*~.܀%]z jQs(xBX(3fD{5Cu_=w! 'W;w9<O#rYޠ'_a sAvqh>x+!9 91S1=DʵtTc' BԁN- $cZi F22 R9" +DXe\_ފiL7yV1k ` ]"GD AݢqtkQP9~J\bS%~bLXĿut n?] {x<k'x:Gc"V'#,FZiy}CqLbcGy \PZNR`$-j$%ٻ~Y\"/2N $L-až;Dm-piAx@p:LtHͱ`ގyʩ)սp; Wgr!V6tpՔH$V^3ZU_kz (u ={bЂVJJY)QKۉy0+l$Og:9D:''A'IciSNsw(ࡢj{޼H7D"Mخ`":Y9Zmi::lMGϋ`J;\5BE 9S:΄:TDv d8VV$"dU'=_}pmIe΋%%B5^=%n f카euM=x3(.Gd祸.@igXTPjҡ?i&3Aٟ$eEW@YqHC| '8EQ[ѽIK#E&,DzZjdl<0%)OKc_JʛT5 S@x<҈<$FqϾÊVQ3Z932W5օ1ЄhdًqcYeyzCFi-5 r&I'4_O HW$S>i,l0 |[ϖɱhemܯN{]3*G=ga})+ҨS`1 SDP&8x!lgwIb2ma^_їzs8"K:Qa\J;l:o紭i3#I po] QrZKcZ?m4&6wxǸܟhd.b,}ZKfVƏa21'NF1W%H6;{'aÕg!w g='}|b{.$i\+юf7l:0 Y_ TW |Rov?6#/|g I{'D}}}\C{nO%ۢ^k0HeV?b9O&w^0 -߉1>~LOyL'(1Kg |L?[09/NU Pfgp:T' kS' khjVW6ۥ5ص=MҲzW^qCVza$WU!a eɕmGlE A: t~rsd^lZD^r 19EYGvG.t2z ?ih~K_hTdLL}Q|2LT\zm'Cؓ"Tp`mkngԪ_*<ĒǪF+jJX]1" zE9b- I&?B_leƋ`,UAR-!'?ii:];(;eZgp:~:t_q\ "μO0rp`hšA_hdj̶>B504VgOӪxA̢'?ѩǶJÞ z:s[S͖y5^6oR&}=FCK3E|ӪPHFdrB3G_f/ssqAC㝖+z7-t?!(ePxG/0h !_xB~84.DȀ=8DfYXǰ- }2#3٘sKřD =b1z9@9BJCg!qG{ ZL2 WhNf$xєH 8-'ĜgoA'wQpmyx㚽E !;,Rf~gt V&wctY)v=G '?r2[#-NrXE1:UqzLRGcCT =Jܑ\֭OB~f?3}#4#x'V6qgZNp֬"\lˍwd{BD^w6[SKN2'ooJ+NqƳ5fhzpdc,&7P g2'ɝ11.k~wlj?9/7sHؠ.Mq(Buq,m~{Y2CmEw?cОF:y\WP*b}gm0Yg P49"j D;؎׶'t *yOdw39aឯŏGĕ,?W0">gF}Ftϳ՜A0E9J  X#UKD45ZTstvfWqHTcbҎI S_M~[1{жmơFY-@1ug%_`Lu5=Y\tltAՌ$B9Ze y#w $/cܗ=<Dkn1]oEiΗq7G\7S|k/:z2/VkDۚ́|NU$% +os~XQ+ٽ/ingӂr;%d["_~!j-H$d[WƼ65L`b2Bt_]N6UvҬHl>=-=hy'#]#ǛF"{; ?]G;l2qxvRé>dFY~MX/+K@Z\R_%G5_R#z0yoqA\YY73g ՞kdh0cn,WW/z[omopḑYd9;`woхo0v".ՄƏ!] {Z~\Zgl<gcg(0C' M;ɉ!Hf"+El ;F[ =.I@?':q%H$|jlZP ZP/*r鷜| Dv ]I3jҸ$5^/$WHy:p0z<[iդ~'ǎr({IX0xN-y ݵaE췀~gy 0=w!8fG_#@?;?}evKwIxE92Èn~`0db:eL5<(c:>f6 ]#*wzӭ"QN(_˲JXE_e!,mVeLW6~P..~3ls{Ŀe,b<'Mߍxj&u7d|6l;4(*B@[ޓ:֥wCe,֯?h\] h?^~ xbC*=>wnl?+v޺fkͽ"+H>3ZzMʱД9IЌx(Y#1_Ao +ƫ@<ɬ 쎱b/E=#*ioHY4RIgF[X[. YZ ֧)̸jTAN2̰thN416ԯ x՟*tHN<XN7hA;/*uk0JUI˂I𞓯^ZVz. (3 <{<>;0ڊqˬI' *5 p^oaڂATǜ>NNg,j_b:@-Cym\: oKFpD1kRtRooAmN@W!kх1Oͽ_ofnaTblbl^-?/-bۍQ!lₐ=К=Sc;Ts 2|S\A/]ac: @6mH)@u~pdپ#MNܯk@Š:;"{zeV` &'ȑYZs"Mj/ByXU&ZFA9=qHZe`uxkaCV*os}Kf8RV8;G7`J[f?P=SVNbn l@vIҡ׼ h8n:JA&C \SQ")j fjπc$ ߰;`ENם۔| [mvF~s:<jI[MoY,"JӽxȤL?K=eOV3IIn/ZEp()J1+bD.Z}k>+va/jN-zYvC^Z3 _;em P(}5jTz&y/\frguqżT >k^IY~ A[я}rZ>G>'_X,fgM,h ;jX{>pW$ԯFmErƸhk͜)x2cG|l[TրY kZVbULè~ Q?8Qj'0~+gh/(eiI"ykN^ b.C"eʳ? =&zNNBJ;h,f=YQgt;ӂCda:Ҏ|R+0og >wЕZ3j8%ϏDRDD̦((?RPWPg *$ LWjЗ#l3v8l-+~,&H܄%{ۯBGxm/et#GM Z"S5`u [ԈT8xB Kh`{J$3q[Oh]d}-x-e2K³>H : dtO!ce:BQԮYrZjřȓ#mN0٢%8״CF¸M4K`vOvLW% ݗݮ> ?ErsP[KA탞=k7>"ˀ6}cG%ԚR6(״La8jPs7vQ?&yVoǰ*S7pm_!7~4{}kn:Jz vnKz{}\MHc3{ wVIo²~{qWIhoTD{@Su@i=ܲPS.ւG6ٳoE)!CM\@m*WB3KxjF'ٷJ :@io'9Zw=Ce &4(~AFR&ſpz?&~mV'> g'w}l!ɕGOFa|4LZZeG2(%g;I6Zb^U 7C{%6sG6v4?-39ZgH -Υ=?1^X `Br_jsa9\k AOiHݓfJY3 '%HbTOAypkhZda QW)"St{1ac_LN5h~G\X쾗Ih~6oE&GVBHE0 6l6{d.d DE t&?lxԪ>sSbϓ;wUJk2kJ62K"8mRV3./@~Y(]\(7g8v4#ɼwE ݾ&Urʊ--T ~8UX/M5xmn}[DZPsnfZ#CCK%PלM-)UX}M%zAQL>!DV>}i@ 0VGM~;[m5n(^S,̠ZD2?Ű+I/3ϭV*^NT@G)0z_1#"۞NI&-7|#b| />Bw^o]Y3Q-3Gj&6X`DV~xMYnF8ėL"?³4{/^lR%eIY!<$#`.(opF@Q/s{Bk5 2~^f1: Y`Sn@]PWXS@QleB']c ZyLOA|'&*,tʑZ[ [ (#cO!uΕ#G jw= J'@fV&]fC&nbU$IHE%2{mKkMD Tg֜'Eq@9{P]j#xfV PF~ߡq/JV6Y^u*ցu}Qc2rMVh#eEcXW~:Û[j0`KHq͊耷#*Uת)Sod8Bˑ%Gp|tRPe'y>! >&G,:"[h/37=}_ >y^<|ʗWVE+ˑLiz^hxyy0I9%4P*ysL7SEhZ>/y`ݠB*#J~o ~c+@% @$7=`%]|f!vPs?b`Ar H~CpI\'焮^KqƳJ" 031 d? mxG=ە=}P s\D*A\xdwaM]Q} 0uAf1s&nDd.+].U7\(wz-ߢ%&0 .`o} ~[KB{~ ^u^:|OPB8|4ԞmhPk6dq8<$ D5vK!@K2گoU956lb30vgnjQ\߹- ~'HEK|^':S lN[5?Hk(#1VF :z^V"7sD~i7L&O7Y XMD Aމ' ;4SK4FGoY+PjňWSmJeh[~M%j&YZ{d1rHeB=o~&򗶼׌y Bk6.>d7QOt4-o>If?pg-tBS\"0g -{칮Mln4#eL~a ? G=ՙ0fɞL{6, eH$\d?C<~K[*HViLh) Uu QgGpzM5S8Ȑ#/aA?Ag[8?8}%ɥxddM}OH6$of S֞I#xH}c]}vt-^%Bl5Gnr;qt}azwY|l<~K6ٕnk[bt#m~ίDK9ȚPx /N“T֩L> k hN5}e~>R$RKhLX2d>bIsJ>g_]Z4+eWPmbE!PKo]+`~f2("^Rf\(7$xNxx\ r#pk3$)+NRozg5.Lngmt7{%(uTr-o~& {DO%/u4OD(r˿ u_,ŬC $EqiR(VSvY>G#)"Xz[ɺ]n'z,f.!],{AH|LygmH&̇zDbW)PtV3-z3񀠓\>χAh OJE%9_ <%,Tg dxkRvB*<*-6X5#\1:/zo--arہ<<1떻ʅ`XBW+4i-"!ZZ RSU^~3"oHɌi0 ϣO%AW8SGb0,?.`f~I'|0@;D{_1^NFKibv/[)+ An1Uө:5ҢBRs{1)TׂyJRǑC\lիNYVR BgMy!wb2ݢu`A'yӽ04;+G-75<-mk< &wފC۪I&:a|4*;h hI)d2O(*zAB"C)mW4:@E|$<|灃+hőԵ l8򡩂1-jsWZȴr׌FʹT:Dm&>{htn_{ؽ&pq7S*~<-\l!D|3e_{)>d)Շx_t+`.-H \oN6-NjEOvx.CxqKȕPcɳrJ6>w5< bF*ថyqY-l| ܺs}C X78Ioo\NAxU @Ln}__Mv*L4yD컈lV^Neˮ_',M.?^>ÐÄv$x#=4~3('(vtha1Zt}$z? `85 "KX~SY+V4E-y$X啃hkJ7-2 LZ4xFzk9מd w#wn#,4*|ڐ3=>PǎKHKn=?!\hԇv a!t_ɛx t3PWra_{!vA7B!Ԭ,h;.ԬDs\ 9^X2:/;R`|Wpj_jP#("}_1 ]:%Ng5+vgɣz :H!cF|w&;V4ި>gIߡ+M65Iv!3֦Zꋾh>i!Bdy'M#7}p^1PD\HT?@\kfkJ'2ay"L[0OfW'[jW~t;Z%Ϟw!SDev7I :'+Nn6Uq2K/Anv5H]3(ϔGjyp}- J6>9"\ABV&0,Y93@Զ^*yGB"-t*Zoa9jY3h q% Xb%wqV-2_3믐YYfCmBD@4!Y/2Xm>+"Lx2pX=d1"nyY!fhts.^C 1b `=wL5# ĈG n`{=tX XؾRNJ^!!aBC8Mqp^hp~ko& %It1BV߄NJrEx=fswK:%`w1)w HAh@4#/*/LBLʴ} ,Q+d3vt=@_8rBxxՂ V{.Gsj 86g{ ΢fy)I=$j;z }%-sK}DosNNlPcW%A2NQۄ'>ϗ՞ jy U~j!%6?:p5=wq~Ѳ ozI,;F bk_c[4>G&EY@k%d{$(?isy޳4P}Aw;k>TsHlt( %~T3CFm0D8\Kak4 7D&y6V'usA_6aA@j@͂ERk9 D:orpك 0;7O]c;O";^@^<5i1tk\B5WHsoMAs/ZRK1RE5({Abh/ 8յ#$&$튬U H Yhc1M2G]laFJiO~Nd[D֒%{sOb=:T-e9çq[ζdCu}%x4}Qehm3^i;m*h"{sl"6`slxhD~%L9zFgNR:R$YxS+MїfՏE_.(D}7ھ:se3.OPQxUPs mu+5ٽƐT=@BZm0MXD=4C_ A1,MRj'Z H+os'o$mmYDhz}j 8h t/[@?vbv\/;B5 ^ LYY7vgǵ>X3~uT.7:VFC> sNs ҈,2Q-!殅ݿnڠ|KV2 0Ңφ䞖i_{)x\(s[ P+:ХRR/e=8X, 0ihEL-y EqKj7چ%A{40YށKᐲU^JЯ  k[WC#!Y2{ &Y@_nI ĸ[%ϝkL%O34R)$i:` OAylh>w3whbI]@+Z#Hd{'1B~qdLβ gR½I IVGGj˽#Ts\ Tjd[`\#:0;@Vj_AA/kq~HGk5|UP(3˸Ιj92J`9j`{1JjR7xb* :9 >Srhwjڑ}g p|T`op\b` /Q m&ց~x-^FaQ{7.sJUbͭqgp9G(VK6vآa0AbM`EW4c4ܩNn/?رĀYx9$gBBzц(&)0IrCkW#P,t$/w_/O@Qr^LLEl"cD.ͯ;e T! $PILrSr`Q\q:V§_~JceG'nsbT~gEAѼCJQPV<]|B@y +y-)O~6$u; s(}"^Gi /w}äSGHb0yةӞ;) ,S0<*NCu.2Jh<#&aOBNӵ<),[l淅F-ЊA`:&yl/vAЛ8)6ӱ\v7^O$4z/`)}̓\isimcѭƟ:|?|8]47PsY!{z5,bͅFץd"@S!@}DwOn<|UR,ohdqqrk :zk@"2= 3 gO*GbWfǁ9(i1PD= zu ~mSН5"逹I~7x=DV,42 rEtX&knѻM GFkU[3*x:xm^?[b?XJf$d $~(osڴ%O)!zCdnˆ)1 qS}3b'%HW[|6~3t҂3\J1rO2-{6fҥ|!EHΓ=wcC 'dj7U:ѻ*W*[.g8(sW:BbEF !z̍.L{YU};p eRɅAv7Qϼ Z"+mL9F K!KcA}>b %7Ɛ`ݏ,~md.?/~ ES02Y}`Peϐ# QQ[qdz?՗Z˞w)q!NClu j 'Ǒ69|6Gs2ۏb_ @OЎWGXdeA(w5{~}tO )mu2qy̋.C\>:O;}[^l&(zOH]7rd2|VwzQo7oUdGSL YwǨ{NvnbTl9^"Gvvu 0sR~.6?w 827ʏo~(9Vzy{,)ìs畮ÃCggh+J}zC}S:`+]ϵrX=E]N8ro$XF_:K8ŷr$3,T#޾d>I|#q0 `1N}<6M нs1>o;[rx' G'JX^`rwg,,a-pʌX&YU=%wt;bӷrkdZU6ҽ]/npZ1üT/ .`q-PZjDKCJT3;:e&+z*ZW<- 4ζ}W 0uW>/;d6x(@7-xWwl%~ek̮\Uv+ʷw]j\,]h Y:gs STK.+Ʊ8ϰ@յzjS$1PGT}ܸbe _YUMcnցko| (1=+ jy XKvk.@h4 v& lk |E,?Q?7b!i^m\#dX1O0>LX}gox\ ],; .ڮu]+bS{1@[7{,)7¿i_ohhoD%FteΞO1  qK@eOd颠~3 =г`a|2f!J=؝8K{24seDSr-4xUiz<֌6cJ#+9SՆJ,BF8<$;.n?qƯMym"?2 iL^pZ9䉵o}m7bO^g)uȂv'I񅎺'PAf;4nao#M-$i+([T0i s6_etHd(DxLdmyeIy ܙF^3Wd[Pc,m7bL M‷_1<S44|`Ep獀ydS}.Ox*_÷yfbit4GŎ}>XA"cѐ"t715Zn=QxG>>T۠- ϡ+#/:P}P)oR IlΜV ܒJ&jB%(I^ؽyd`-%,uw$x6&E~#*ȆoH_@OOKŎfyY.;7+ "YZ*q/]Ye*GW:P9h~ṊH4F5y{@3UŞc^vzƹv)Ih ՚9rF/JcS&]v&QUҟb^ܯ+zڨҋ!   䍿D];M#oVQz8YnY/bDϺe,p5vvNö_H ARx܉:xY=u)$]JTO(ރ໿}r}{]? xGpX5Kg#"a7aUa\ӱe7+a4`\a? 7sxRA'@E$CSG~ՁʆAWQ.? =Up w;D 9OxtYV!ry'veC-4F2_P4dpfRُ s!9>n-6;]0p~^K0Rd T$ i08:HdҼ!*Q-B)t^Uc} BGʁ:\Yu>]H?.,~de#.dfj#=vn58/%W KAe,K.FΪ& pڠl\XV7.{oǂ|={s~N'MRI$fwΞV W+fJg{Ch5j%{;) h97O7v&ZF `tAٷt4$X=a4%N1Kz'.>FXt]MzhQ EHQ]FY/#7 .u?; E|棞vRC~0muY;:.b{8vgsB1JyƐ R&q+@~ :XW\gcPMyܯE~Y?Ӏe5nYQm-Dj.Um+W'&dT97\{c=4YZF_$^!2!An D u52NK[VH^SL98&kȨz[PF2:TVM_dJ˾oG%OVVn{{a)_vSflGF,}!zMz5^vUnCwQ~?5ddSqFD:*~!Ċ0V}RЬ=ݢo3֝'7>LW˯;6'6W<>,5O ,l*G5f 5jPM6@+`g/*u78M${eo 㑗H;]jLYhMFy(>8e {Xdjb}M| S]͔ٝ * ^u+d!B4p4˱3$et+W3Sx 7FI.hwC, nZ) :Lmtډ=oI.Oe9*kz(ug94uq >^cnSG; ~B׏sO*Zmiq{pD]} ͰiGl'ضtmYʱ䄽if,xy@?gzH@uIצZ;-ar=`A0F T0K~Slf8N1E RFxsh~+jxsl%y:VsǽShs0vL6I7Ya \b 1M z4>:pၹ`7:A$g-֣VV;4N^~K~֩@ `Z WNfӺM,tfd ȷp}դ;w& uN8^eCf%!q=¯{:J嬑"7;Z&Wp/ae32F#>1!U=qowkjlpC?=A߯UT_m|YyDK~Eča᝚1j$swwG&Σ- KG'H 㩹7u٠h: +t&:_ CX8ck&9 |=Bߖv CY.gT"<Å,Hz'hT2!ˆOPF$._Ѕš!7İSgB8GDSLv6 :>aӯp$x}xEXY{}ʋLeT[q $.Y?P[xQbSo;kDy^.+:DZ٪^J%>|2F}ӐKjm ^Tŏיb*fTUZ{c|l뵜v~8gu@/]@BR3c=rB/ Kj,UX%HL^ZF5_'rBJBY,O+k9V#:.aƿ_dG8#2 `u dS lמg=k^:?eW$3 qlz7K`g-Β0ؙG h^YwwƓǔM%B.FoEa #ky;ʾ:if( &O7 hM,r~iU.ep0Quަc3uUn;ͺ.|*YЏmpVGbeX?+W64sf/PMNmH1ˉai|>'[.@(0(5ғɚI 8AmJ5$/ T$* w9O ϯޡ? Y1iy|ן;ݤ !/{ YJ{l59%^€T-ګ56o:jԻ-($4Rkl̅J=~mc-~W =6 h(ު6کyzZxzŝ*àJ^fU%SWC4h!*Y^KRw!^meD^lv-P/ýq<=M_}lwx_fU-QTAw:N W)X6omr)k \F3Me/> .U"2>x˜Y%b +xf[rgXK š)Xa7w%^ =X9W@S 2\>'%F _@-c[8Ř€D߀H_sg_p2>t+:rڳ vQ We)u_Y;||~H / f3n8ۯ_j~{O܈R,7ָ+P7na;PcLumdm/dk'_9ԋ u=f Mv{h2h=wHԪj>>fǬ+ P74쎎-DG[n O^>9|>.B0]}&;9큿6=q-AއW:kŦ_}GM  G@_qKt"yG_cerxYgxћյ݌VI?CrҸ(~ԩ xzU5[l4>+8_n (stGynPS롿< »9I!=.MSw$L^3&(ࡄM0αRIWp׭xߦ-ЫPއ|/ɂn-L!CY*zKzP8`tvTb~i,{:SC}$jO);e-Ef[dţ=h:P4RģE "1;JOvhC4oQrRv)0B$3Wn}SѴ|֠ZܬA^d y!`1cC Nel˲a*/P$ 71Q 7;Υ_(WM*{h?Yl'IV8l sؐf؇en~:wN|̍2#7hܬC1JC?u}W}v[~MG"Bɴ2l- E΁y O3 _n*>J&~SѴv`B֗W&y d!ztB ,w ^tpjy%]bd;S:5x 8ZޡwԻgD9ni&* ;!= Dl0e!r "6= +D]re*#~ Ը1]!y{a"ÓL&K)YH%/@b2eVOȬMMצWBE(v ]9˜d7H=Ne* Kmܧk(|#V U7ߌ8T`]ZݿIQ< ٿKfD43~1Ae4Y@&/vfHS%) { zòH\7lɍltfhPڨ X۹ }UBc8F&˫`Pk嶓蕸[dRX߰W(+ßl'q`Kr B"w=VӢWB20|fb,3_rBKqAg:[_ODZ.JׯFJMV*bh\~­H6XɅCp193 +uL,x΁yC)BI) cZ)E2y;cve<p&*6w2%S\G|+vȚ.p/yk}dzǾ^]&q/ w EteQ#x -n)}"o)8΀+I;-p"Jgn,}%6{?dX?iހ΢O/d8S-]iPc?:'Mk3ql|JK  I֡PJ4I!] G"7ڙ#-/֒V ZIvmGz t}7 (DSTJo[j⽣My$F)F}7 g(kNvC]g"6씢NJc7B3wJ;cl X;/E};u* &^ "/`lU[g b2/yYzVHH dsVL`q6- t`v9M)f͡KӁML/^PGZ7'X `JKgr;x%;QRڋp7-UoE 7^X{)볕m?TGZ/?ErNCe%Mr#GfXW[ 'tI0`1 &_w#t{9W9pd:ڸv<`sWFXJ1c^mx!gk?FKIJ0I#@kbc+,ߔR@cCQJpCԤ61i{߮Țelݜ7gqB2KM6eBF h&r;!&_{uUOVSn`zܠL wm@kI` %Eɶ*b{vo 2lbBb^#NQW,.>CI(U?>~\1wK.^ U{0ŒT N4KuN|F ]!Q8PoUB]mxqK0ӷ:^{,=ޮ,z߆Πn)Hg&":J@2KIތ%1DHۀm5aUʒ&Go]| *X{$f[y\QbHC%J[)m16;m_!Ck&7bI&Z@ Nö8f@)U xqu}~C$5ҝ֞-"ho>-]Ko;*J^Nv0˕\si1K1,0Y\ J/|(_5vᓲsk{1,DU/%*KT,Iݾ΅Jfe6?K#] TDdڀΞFX_urz`l@mJ͍EecEe.ăX"9܆*I)æɡ,/Y Fvi~ UÛϾy!gk /P0o2}F{z{`%q:pԗx*^Jv`Ұ䖰lD?"jJTsB{-cfYgy6$40Wo6)$b Ž~V#҂~c/P]څtdV7Ӹt|y|xzepT3uL]L?i}!!A}RʾK W~n|F>־a"o߄2eۄX8"bV"bުaWFgS؜8g}^,n mކ}v_dԴ ] Խpf- ' CxwQd}4K*oo[1u_(n>4vkpJ>ClEl^-:&~)̷%1On{ߣGmwEr7On9$GJ!,I?Tj7P$S@m$p36#bQqiE78Wq_̝3F.gjuH 4)"GvLd$y}fl"bI{(PqB/ D t$T^NB&dg/+͍kfъ3;8v&w*lDg<c[cF O(uS #8y `鑉Rbg%(ϧFBz,kne G:3A5Kpo45M'x3bF<곌xtQ)BRHHGABR]n>V%$Ngy8IH?'$Xĺ?6)?&y8ev7E$"ȿm[־=쭒~6ڦ`ڀµBhz;QMC^0l pDv/Mu]`@!SkJN'7BNpD_$l k$bKlF)+)f)]qKc$OB{;rx\~\Q\`.Β *#`ٛ @g#py[i2rNXuK9D:5\vvpe2N{2גNA Otݹx>P/*jŠ1g1JSER/ʍvtz?N?L8d{R'2܃\(Jsŝt XJ srpݡ>3)&l8\X5ЇHF~sp̖:#j428dw ,{5v_cͿ Ά#+CbG=o?V=>SJ*jd}$ w.#8Q\Q(y.g\-pgNr<ގv'ᙱrfEܛ7QuIӴ)PR@Et"TEA_\@يia྾/HK *ƀҲtsΝLy>G3w;sv1_ )z_: 6džIP;А4)nc>C)NF7h":Ó2- %7zmiq!z_nRG7!UaVp|亖tli^y!9hm9Zߞ"QM8AsJvm1&`"n;Ysg|7n3icww%CQ8,3f7v#,7 vCr{;=39YņGvTPڎv(w&)[2uwHv3#R}{$ `Aֳ/g.gy3;C^jFoXO ﻭ>4{ZST\kux|Z?2u4-i?gBI~K:?x=[ a0X(<nE$!pVF,tT{u"#lՄyՂkqF WDXwX3qnk iPJ9۟.uP_@n"cB' 4_?;ɬRiŅv{F].p̱2b%GH خ{gH=TGKfD cuzp~P+=} Z"W Y雜A[6>-W< Ƹɍ^.7Ҩ&MׇbUIISMf[Pbx *(*x# Vk#`!Wa5Bdejj~# "zwnDT@_j+SHN)ȟf+pR3$ H* ^Nrڇv#|sHDs+ ol J\z+Q(;E!J<Zȁ=t<)|v&֣x@~0c.;H%4 dn]'DMG4a44>;=_kf>WniGG7o|>rwzې99'6;?\˨9t|x$w"VQ,%R@)&̔\/Sb`gx;%D`y3S^]$oE R쎂8"Dmi7/)cO˔-[mӹzS?~'`@N1#vlFՇ2o735]P/gVfgꁧ0etw)陞@}7,%= Te[Jo }2IFWR3saIΣuwm ?2Մߥ1w`8Z\Zc8$eHX7 xn~n >ߗ42&)*;<Dr sGq! G5<'|4l.!1SNz>(a>oI3GT[Lp}Re EE*蛎>v%L7qb9]ٽ ^H~լIs :+c}) zg/..]?7>`gu 6]֟=Y+ r$(('pKnIҥ"7lKajERoܲi|/nKe[, |V5lzv߿ +KAkߘu%֔OZ-o>OPcI{X֓Ƃz+rwon/{xLly8_4!I+5pV@rIcWDjiVږ0pa~7vX8J*bRH 09rqÞހfV6<= 5~f)"v?kp-BMq~iɝ.G聎4n4EZQPؒOGL:WAXըةuvS<= ͑G@+ky4X o i^wt#{J^7FVoWѝ2:N8tqudN6k3 ~Fp|]7Nz5mm~`2 UڿMvXZ]r=w&qXZl@fLA p X J.>Cu(VwhİE}gz|_8U% mn3y H-]n9h AF'21{W7979],8q!dw<TeKyeeK_&I!o|YA꧕b7o _֊||p&amxƾBx2I6 [͆FPR'lx"2J:=%%?it  Y>lZGwP7gc1յYm%Bzo>%> }ɋͼnB 7R,-PfaZ"Oi]S zuY' YѺu /ovTϯ%D!H/9#DZA^Gib1-^Y"/cȾN|$"(:17Pt"~"`GE?(:Pt⤪l܇QɬG~DxEcjw3/(}q8Nf]"05pLݿIBᄃ/A@v__( U(rmql/h?AF+77)Yɸ۽>KVQ-'þ|ؗ4~[~ ɥq 4B M}[\|v˵]Ϸ^*)QjHDž`+sB| D6x/GFhL26* BMbҠ.>[Bh: {&24i|%χ?]q*gMD7"`mO#o ^)xK|Gd 0o#vq: {/buQ`'t5t »bua`&~ DV9N Fz=˞鋬XPG?<Ҏ`UЅMdkptgwhP0Nd3 z\a>2G(# KMчJ{[Y5y~!):<"Ò i}:)`߭HSYpA|ˋ$t>Θdgŝm,mvZyڬB, 5yab˂;l]jwk@Iz~jQDia/a"f0\s "s e6c4vS'DkwY)fV x"ߥw6])=*ȷ{~ x1Sv5]Cl:TIaQh;Fm1M(4vb q0ihgHN71ZG ]H݊L"?Wl$⬤FܢTCQ(QjV$ E <ãOW7Q{|tPu؉;ieP9ԪsѨsxb) \XP8WJʎ%'qť&+IA*>iȺ_A2$vz , thDyU-)7ffkwTitEEi!d;c#'ýaNmA|0˝~}"q%@s[uX@3SHT,j/W{Bu2hRd-- PٺZ#tae Z GMCSg6@"F2 Ns802Z7X%xGi11[{f?3YyL[](ޫn-C ݎ3v_+17tM X?$YL!DRI\r`R}5ea (S ;,6/ޠ+E6޼o[~Ӻ/v#ǮǙ|L\ܹ(&KcB'5)٤1{}zxJ{3$W蹘:(D*.BmB h(BIɣWUc6MxRTgq-RR$[KBs-N~F5:E7E::2cXoᶄ9i敧AFj(.3URT 1V`__A&88?>oI,埼- 03NEiL Jqcݢ;n)m$8R*|(2jft4kO! ɳUF{;6ʖΑx #DRg+KOHA*$e.0~d@oP z^[wς 2Dm컳[r .9Y{/Q+hGدd|E\l|`Β)2E;5ybۑ]A \V"%o K̪OIC7l#JW[Ik0^{qGMBFtQM@ !ƯS<&),a-}6[tkWI=Й;e{`6fq@@q%cfԬ8|kޣi쳳CV1,l;Ac LI\3ZDKwשWlKww,Htӗp68sz1$' Q,ڏ gGZ3xexݠ/fv&'î,7р/f GGGQ?%]x!NJG@N-8"2nЛ#/6EWw4nſGhz73P#~ǒwоPTL ;`t%myY&Nn:[JvیȤ&6O䁪 CvTs4leO e|a^;؍^&ބ/NHx$M$z{mR& m7m }݋o8}zNStm0@3p4v?z:*NfBsmiOмj.8 DV$ d ŁOI%Y+8k3:IO6 ' Jה}yqd3F7By'b >A,*L$֋Ӳ \>1?oW|/c8ySF*v6U3o"T{`}\rWT$"Q' J<s8 ƛj Dh[PDYGع6Uu-UQwr|Ѡ E'YuO`J= >xHE-rz;klIJgY_d3&5O\&PԴSSgšBH*!фixl\J،^Ntē2x 1|1rӸux_a:u <l` I|=J|ߜIM4?geTһn3Cv*UZt|ÓH]:0^W47Yg.Ζ[&Y+\؝$eP^9/=<D_Qػ؜dy^,1;ؓbu`oҞw$MpƎUt]b|,/bp.{Wx*Ʃ--K&E>`YP\a6g,"g ,xe #~*ј* $'u[ ߚ/`BR_?^2 ~NKѠBv`aOk 'Xs WÉ 䦱)^P<="i=T$؏}GoQ=My0+,KʏhiyC#FluԀ1 dw"wݼI]]ƻY&%CxgxY@$ˢIyBd)dz4^-a_&dGmT<3' d'&(CSj-"Җu:b*{Ӟ/U{t/'Kxd;⢙#unb[TMȳE_ [ݞ)o kfg&-\ #iN$3T%~+k@ ZxIr#uZXVB_0b ,+ǐ8b~ 8yO]Vu,Xeˬʱj`Ӭ*iy~lH+/7ItҔY,Kcs)!K#4,v-)coKcS|\xqiQutߒsG9 " 9'k*s6aB'!wojŝ#'A ;cn iҖR=@mK_W/Npm-5#j[up oK` Dd; ][ʹu&xH 4t<^x& ER}>l1=_Hd:GI$5gc :.ۮ4Di*nѣ /^BB9]V#~xy+,oU$d-|+t*HPn:A<R/3o|y_}y?C{>THA1w` a'.ry[+_::?֨+|p ND@_a@؈!wo*EƯ辰Hr1(7#\b.!C)/Hsq̅ |_1إ9Wjлw4Q@)RD., ͏'@N]NO9 ;Js 95+qi{a Ǭ쮱hˆ]Y d"٦;ABqm_|n ϖ6Qr~s 4 >o%|P}k:B0֑R>PunMl& ?÷>+t?ܗ9X(w蔵oExn~ *7ƕT"I[nN5_S!cxۭ.9Im0oXsT7wOf$3~s^ m0}kdLV|@<@n7*|Xd"m_.蜳~X9a\(n$~YA- yf;w>6^kxcc^<~*UaR&As(~2IF(AF#ҁ%gi€ZA4S++AXs?RnC$tl7@'dU Me_yGw*styM78:׊6Бy9UD$ =0rmIύI@^5SBhBR8R3L~IB -VͧFSl+Yq-NBX3=QDb9G>EB>U ]"OԻ{:ů1w<w^g}΂M7bi&8ǥ_s)Y<({oC'S-R$dTRA}m1`zߗ~E&K;Ra&=[A۹7.RH, $/)#_ަVr+_rvb1YT?CJyd~B)B x?`پք'OdIc$Qd''xQMR/v:Nxf=eISk4@eGi5K C_ZWFv*"y/ɲAmb`~9E{ar B|J w[3FgDh@R`P^=|6gOb!*4O,AtXRtDDPo=!JU911ot*0U^^ -6%䖏)@|~ɂd032. -Vev';Ŧ}qp֪|&7w|Ͽy]i!a|:o HgWTSGq5NAE8:jSl!ojBMXCMծŦ^ fF k1.j ]`$qՑ64"s1!ɫ՜ţHGa]'";8B|xI/f UlC UP6izg~JRl47k˽n7|T\&fH!)ƭtylSn5v-6XDLt%(A:X: +:i7Y]Gb/^^J9py6EHVCKxPV)L׼~+G]jTCApYaD)e* $`4E䊼ZNϯ*H֜(+\Ǹi0m]Jƕd(Xzq6u<ιjF mZ [4i'C,AۨҵCMw!DTSMkuYeվ4#U4֧燍u}>9% (aD *]wx $+yZɓ-]NJul9˹F(? <ӕÒ]Yo$Dޫ+SPq P&[ǩEJ| Ezc9WBqFPvb1|%b|:{2Gb@|?||W wCms39 4=NC|<ϴ8:ÔCАIۖd1[ZpK%Ûx2[oA8lK/D蟸]{kɳخsȷWSR>c/gBQ_4&˄o%ߏ2*Ql(Y-q 30]z}{ )Dc)Q7ֈo΋hYӓ$~Qs}dԠAGMGF^>;VGhop&\A]?Mٕ|dw#ζ |΋uh$xA'+tzFr =LO}孌jc.Aw%{,\%3cg=Q+8 Y忴rRX+1+l ,`Qvg 1&ٟF14>Q|d :W~gQu-L[ݪAؘ`أ/SQɺ$UcVyUP1({&EP!׼YP}xu\-a0TMeLedDw{a -ي|PuN8}##䠈z+4}ݦ4#w#Y %!-Mixv!Umo(Ȧdʾ.I/S(7b :iD~1Cɝ2uQMNǸ8ΔCOVCKWJR \YDL0^HļYuˡjwU$(]ډǯnW<9,zJ"8vOP/űQ8v?l箘U2RDf~)<&)Cʚ9vCơI`006^#⊺)d:T60ِ0[M#UAd9냜Cy%mčND-x%%[dJm S>l.@ԁ}E˜hObmfB7#~WK (@?)RD57z.ʌM"nȹ8~! =\:& x>+GսH 2$(پR8sQl?MAFtoJeuʽ$S(a,sꟍq.BCȸj+ч]z7Ú1;jծqKfD?w`7hjgu˔V}!|Cl2| ;nnbv_l4T'8(;8q UuF0k͟@sd=i~=O9cAwOxVFqۯ9Į}LI:$Y s)Z[@ N" !v 569pz7Vq` ;T[ Ch}FT2%KдCA bdem' ڴSTˋ-Y]E~xusvh3po%+u~-b"p,JDr-lXŕDY7>Fsᝐۊ%*yiWK H5.qBoC &h-mOC)>Tj0dSYY'[~ʛJƋpJLRGSRۂJ5V(8/I5Z^/9tΗ$#REVH h]L.V{y M/é'?J/KEvQ[H]$YX@m@ojY (Gt1w$Xe^>Xxy/_Wʽ?n:Ih== zVzփWa|D+~z0*^C7a}* (=Hծ?nxYϗh5lo_[XQKzD-^_7mn~<ۯxOaxMӾ_۟BXz~\/g{?m4{i1 z~S@] M{W=6ʽ{mC2)Vr/RNLὼ2y?kŻwAq߻0PS(lc1t"y 4~ VTyaޚ[POO"7'R/zX^I`rIr_mfc퉉;gz 1ݍ&Uӈo^02 XIHuK)N*Ic=Glp3K\{ <(&9jIu8 q!ʚzs&j wyE3Yc3ݳ1'|LdpN`Ў'I$)-&oVj ,tL^TdWcrA vۂzs:T>G>Jl6A AL|eYW{5uĜt"FζISUơBy4aocd +-yçN@'lv®S ]nE<`&\i;C]&,F %KaӺM GMg#Y(Ɯ{:lc f,OPV}g&WI!e^WCg0]aNv1@aH9j:Vp+yfݗQ,:XRJa=pd>*hN2Nrv!ɨA+IdII;ٮѵa~E˼jHl Ȣ_ut, TA-suRb :lN$ O@ U0)4D sȐN'=ZN<nx .W:=lSYR3ݏ8E~{R.m4K<&&bl7ϋM |$ JG_RSS+c0#Y:5 Ws+\Gg!%@E}]PԁӪɃ*x/ty妰>86GF?uUJr>e=.)t~ H?.v~D|Ef.'Q0Y ü٧C/}E9Gު(}z>q~ʔusw[:_m 2Mp?T1w4)N`{,m~~Z%,mx`Alb-V;CL;эo᮲<Xըі‡4.ɻj)hhXՄNYHkFeތzF|viM-^ ջ?-|lCv5{^nŲ^9ZbwӜ)OH^Ixt(o7ᯞE(G pQ}ԮNZ04¤:əMLY ձMܒHІ-sImAV&؞@fLw| &R .|zm݄Zl;nl_w.%9I^oD6yݜF]l_ɛ$fw() ;w0 ^Qo} L\E=; #* [~]MR͙IpJq&sѫzۃfQ"w 'S0s&gSh3yNㅂ.1 DgA &PaRe  <p]y>9gf<=%K_w F'1Q{tg7tO:ѿ#緬qk)RtU@M+2m`r2vv_ LXNo[URff}A:Xwy՞xM:}N]NϙO=QVÿlb#!p Hɥ_Ddr0 E])~zh w|:N`oJ*Ƅ9V&Ξ&d'N1+4w.ilǍAh_Bt?UX" _.ng%+9WE^Q*M-G=eb?}_#bd>)xχTFBd},Hc!4٢G{\3'21 n|9%Ϟ"~NvR,[E֜^J΂"# ; )t%hN$oӠg`;x؇D<$c;hܰVW`rmI^5x>qGgͰ/c73c6fK_/J$O:Vt\`len} q7QqRx9)rGdњRѠvF\-Ӯ%|k57I &gqGs1I(KjY{THTKė ] ٴ&˝se&ڃ fr4WoWcq q Q1@*Fy@(8t%i13H|<@Xe Nx6 Sq<8)Q>F,˝W(")DM"{w΂Z =ь!]񖧸Tحw&i|9:"рP<0ԳU@30Zso .Pɱ?p)Rtvw7l ܫ\\-i:7!ǡzɏ%KV_ICb7CNOx#|Ȗ>$ 5,0yz5B:OP7xerBn#eƝ(*BB盒* 1"({x^B EqɱE , V[xg̚eqL3=qDϬa':Kc#kȀ '_]$!D%&WBgF"#Bkp9:vv&2T\&vyWj}%$;ls22CF59mh9sa 'y %C?Fw:';Bq^ CwdƌD*ÍضNkV:3 :̈́*}j{2?}_ LIJ9გQ.M#5^Ɯh@9Et;c+XG}s\A' $rȈ<|w' wq8 pـur},ߝzVa1S0MKP}n^IT|nxf9ZB GTrhOlVgCyΆ"r ;g ~S\c95Q\_%m& L&~:i NV7 9r@0tUZ'i\3X7٧M5'-?Y-TrW3= 8] e|~ y|rh 04 .i i(f`_: (S<@T~P9piʹ:)HPO*=aT{a\=ɒ DC*^qf)VWe#=d ~iw? 8`M:eހ_Q;@]YFhyQ{=0 P~|brK} itQ~ 1$uҍ&=Cl[$sd(cG7S·<$OM;"djPP/[uƟލrMƕc,ٳPQ#=S#Eep|8M wPt‘1@J70*FRA"(ZJ.A%'b)~2/+oxvjorԢ])Cݙ\o~% [#E ?"u 9⦁͏SK4%!`.d[E&T[2 +>Ֆ̈.wV*FJ:S@(,8Dλ;> yG^KzM@NwQ yEJ>~#?''UʥCVLļCfGK|np_d,`a[ xPNpLekn$})n $2Rw';d&K 2ؿ0pb1"|qq4~bC,o S7hL_)I1ñ0mE }G̕C8C{G#6 VN|ihr"H* !ƨ.NM)jF} P/%٣J}k4ۗyγ(o;0dΏGZfH0ebqgQHVɡ&: aD9,鼽%pOcå{9K=*1qXT:||[ H7Žثuv\Dǧi^*:)o) l{bD!uR|o^~=Ǡ)}{xQB;mEM)87:~P-aBiGF}` `\ESƢ # oth?69}cu8`:%;&c8k7{Ի eVņ`k4lX+LjJbG]%c`o%Ta Cep( $ ?ce}Cǿ "щk3#zI+xj@Lqd%UI@gO[}xpֻ]XdaHb*K!SɋWqLDR  m ]lW 5叭 QgQqP~.eAe7%aT xXBeFS<Xoev._ӻGk }Scڿc^W繨dXI@cĝv%Md5_Bqגݞe7'5co Flg3?YqVC[W0<>@6qo_=V!̂E6%p8 ~ 6c_$7*-@ ?F&+7,F08qX&`)WjHWNjbp&zcAi*Bk7V@ })PQ։]Rۯ4V`;A4nO ~RU3<{bZw\XwU Oǡ^?JhsX'ˁmMׁ{[X+txi!tWnUHML;].C}^f~Nʖ<aZfĨSm^Qf*BW0n;5J3?W2=ׄ1N; : ~9J{??o/)'#ݹ]u䄅9x[Do3Y\b=IQ.ɉ/$`6=:.BI/ Qn7~K{d=Ur)y:<z.+su"tOy,)qa[Q.2.ϖ@r; >; NrPt.op(r/q~8y݁5Bqm[p6Wf,pGË,;m%@ Iކ(^:ejȘr$|CG]F*{Mvϒm$>'!%ӯV k;HZlD65˻SP|1H޺[wm?Zdz<f?+W h; 8mDK%EXK`4Ba\,h &r b&aWЧNtWv3]^_s T%tnV&p8сwtڬ%#[h C.N9Fa^pq/QX^j*p68q1NaG /S32~9UXFHTj|!7k"!ΔL4"?;G’nA,tXu)R0DnvQu)-qh8|@'rRF&wIthmDΗ~dK#TЅyш~8<1sԘM_@1lǬ Mww:_WѸ_Eи~BAvHT 瑩I.Qo.Agjf,b:`R@stggE0l!p| y41!?9¤kbtXP6cF {ֳd->}8ox┙Z3v05vJĞz xv; q1<Az{6;/S1'tbbP#1bEw_cq 엎6(m y 3IFxe )Bi}FOmϛ"<F V>33elcQXژۘakކNq~2<s cF~ׇ~xY $sN%Sx{31i#ߓ,QN=cegrgsMzϥBAkJU`HOB6Ղ"c0/JSg> a6B(3ܛ࡭c S$iN<)}0KL#o,GGGS H힯PI5k{$K~O(O!4y16bSz-N+{jH;FIԍ< N>-$` V2`;,2bep=f{0m* ^0tk 깒efW[ ?χ1gu86˃IL:,B7b5#a9BxXiǎ74v]=NamҾgnTa+ 1VIHcE֞ve%`ȂYvRIRTgH>E~oQ'إJ &2dYUVT ZI%XcRz~WSߣq:b󐊴ٵ } f_(RǷ.eVmT|#Hq&<@, ,`*6Q6:zw Phݽ5|x'ǍAC, Տe:rye#|ek`2_OOիkۗ+k;?V2䈃}wAI2/77lzy&-K A6@#&Օń_4[nȔ^Bǻ9|& &DmHM|r$G/zH]u%M%ZulfQEufIbb{w~dMAIC}ԜMFD; qw|Λ%1;cg.\$Ƅ\ "jY>0? OS4?]ʟ&d4?'J CܧxVZ G|=.jߴr"ݴi=I ;L:Pq1TrP^]YYwD_ ]wJw[`wCJ7ɭ.L!$&;<~?CrǸo}ڥX&%䢹x9z Vngm{wTCyɞݵ~0+o^>fߜE5@ lzݍ9%?KkY=ǗNQ( l_Lv96VGRao ׁ1&ˣXud m"xZ..fAroe_/A#xގ!Ys[s #]1w~VNq.b8B/D[/yhC>Cf_l-`vz Rq`;]kzF0-,BhvzN8,w>c[I-$N;bWkZ2Β^7 2zAYθe(Z Ɲ?S\J7ȴ&6]< ! Z  fJq?Y\iZ~0FA 5X j12h6x86xS\EmmyKsagzOe1D,).۾Sՙ :xjSIh(c[5f:4|׸MqPx 2X5tk,h}G˼#_?[kɎ56dӹE63Z5ޤg/8y Kb6.cj"]\KC:".c1P2;:\o?Ho䘹%*1֚gb~O6p'`N1_z8H"w5ow^_ oVO'&$k`8EKN {Nv*ñˮ21|X5>"5gMh1E"9G9IqFU7N~c!3 G+ mb8 t:f8ܡe8'v101a E _atp2HSSgR~KlX< juuy~NQSg>"Z{$C*^93erGb;=pW=+az\"Fx7&57'a?"d:TZݱ:"Xf~9%ev2LxQֆ]U{ nñnd^Nj60'yIR541Qy8O-V"-3ӺO[c/B H@LRV *U(4Y&i6BĖ0(Q/rgQ _)Iք6lF `WtO*CUXւ l z^h ނoB z 2ÚpstfҒ6aW:0:JK~¿ ۷o`kb:<&7*yИhK hѢAH4"l2IJ'-(֒(GpgOtꞨt*l>[O?TƱf]BG Un(]n%:m|?$[voF~G;#=0' <9 I1~T7Ftsӣ(%z%zDKuPRߋC b-ĺ^Qɒ!*i*yZR-0f1ZUׁޣ*/E>OH؎dՠ>2V6CƋ[*&5D>E 7N# +lJM8;S4: (حD.[˹i yr%2t1ym0L`r6wwc;:*=}lCGŧ10CNϨ [Cf"A@_n,{T~prM>zqcVRڗ"%9wtaɷ800&(y3:6 G&T<: Uk`E,M ez RUx]̖XTx-aO[xf%X2XX&U mbVz $'WFQqcK8MZC64ǹ*.窾 ù_swvMqso%!$:`'eыɕʏ@A2T0T`*`tY6G Q;,6vjq#-܋T-kmG|lZ.ipE_tn|@BN 9@<r 7 7, N(3 r׫Ps-k;|6˱0{SmK{dY;u5oG*.x&l\f\.|\2 a=R)s?9=/QVu[oa_ܟBu/Y싔- Cf_ԛ5"`> -gUhSTeOhs UQ e];-%2zwms ƻ>ە-m8޽z ?pO }yHœXj0. XZ2o5Q5ĿV(ϛ{ 77o ԲAm@mX2b@Ua|{ \@i%5f^B돚"B~ᰜA6=mT*Jk8/9o< ç[kp8}kjp?.~n(x+a` \ث+UU'oO[e xoje΍Nh}B8ODyڸ-2ՙW4ǹ.ZsoOc 8Ĺ^{^iznE;e&k?2PM^ W#~~488+M֞֞3B\<Ns\ܾoZ*9>>q0|찬|<`?࿴/->\?8p2u>/#X`g-`Qˍxnl jqg&?^vn%kUUysk0{nn ǻ{b>9{{՜?u:7Җ6[ *D;,k>G[?i Π ƿ44Γ:dFgσGl zT Ǥ&LdyG\0PA1>MilHht} ,mCQR22걼Z7WqN? wleOWٸ1aUM.|fuުӚF[9`T0BSZz14;jxVuh]R2>8[Q1Bv~ g%)CgG$tfމ.^2ꡨWwfe^ݏf20xYJʂهoKAg3+)O|#-T|omQio~poso\u؟m+D_!_f1by0>Z+sCI3oP>0N0;cҐYlmBAR(o$=Ňut5AxޥKod>k`έDfSGAR5"C]g=KV밒bu7ZAKl"0[n>ep}{A8p?בYINJ(`~A]ɾҫ!W!PuRZW@_ܶ2.\<  qO]F̬y}0$.F.gl.c=O+)uǼBwC6~߇%sS 4`ƣc9:يlH![g+j c*6to$˭–;[76"fO)ǦX+ipnmdf?,4~r mGe^rm~Y+g*YoT.eڼI&%Ka9$#9H(S'%KgT)XDcO!E0X3! K#d nJttƸ_Ie:M:@1Hs[ v[莉\Zˢ/ \cýQH?ݍF岊sL/ofOsI.62% ΨAH,JxIDChang߇I u͏cN,1_Ȍs2;K]0ToP6 `.F1A@Y*:EkŘ>ڝ8`;Aʰ#3gZW[ԏqIzݔEzUY+m>: N +ŽbL?\Lƀw p5f9J= $q}Yv&rS\GhYD%ixn׼@I_d崯ݦpLlN 9&Rnq,mclpd솮YT'L9|Ӻ yHh .,sM1/l$u0AO3cs Ԏ,A';N{y";kO?qvrW#X񸵞ӭ7Tˍs|pdb.ت\c}Ó`2AAI/0/U_opEHkHwEnm/ }ݒ;H2*ۼ^}TR˩^2>]u 7o_rL] {7Λg-ތ$o%" <_%:i6 j.YƩKڞ'5Ȅ#Rci*SnIUJQLؗoq𡥃'UgBWK$|z}|8P?CT/^'s$N!9$S=ϕg-PLs Ճߥzz}Quԛy68=N6Wj:iMvRYHf/vu0W_v*q|?<&CZxPIh  wBF:s PFmHɟ蹯#Dfq7 ɞ73-8AK_U3KQ(_Kk9yߓ?{GZޛB8S%r~3 xKYeG9ޮ?4^Q{LnF<G$Q޽3{NhF{;5F >m{F?/~^:Jy>ݢ2h G&G8ezAg07Yu|do*j?5c\i)s<A?g_8w]:%rOnJ8]Oc.5WS2חȹȮuXAN?_8g>\sg^|_D ߷w9c* ]~Oeo }2jŨ}]t3SNxG>mkr_};}_oƯZo|+}d[#oU_]3o Sa|-}Ois|M*z|V ueOL_/h13};p}-=Xj!~$BwO2d$HZ=c z@E(vL}ycW^iji.O>#RR'/>r^VBKǼ۹:GOsT,sTyAn2]ퟠ ,оV6ՍAS6B0La EWh͎q[9l)I^5\Y-hT?7ſvH@3.d*,&,>,&-rDz$`:s🅪p>g>7-'SnCБSf[T,v AfS@DTj8%:iLwԪϸnlmH_MwBtW(67 pZtwtMwgkMwSdEBWG@m?Uw=kɸvOqmaոaո&-_=t(rw.&kk4ţ%nJ**dsb4,seKale6 s1+gW*KG*&zF9yP@PH:JP:cCG >`}N% TEG:v5M[uz%4hـ!zM[0lvs=}=Tɔ4m]^ݴPM[ôݴM[2m]nצoP@@2mxzq&?Lq:F'1Q {\Vq 5|GB~T'ܯ?+Fk0<٩a1Dcgtq'Y20,f/֜Fj\nFom͖odZYL+qmɴWH!Э>"[Z T.$Mݺq-z&dǵjE6ů[Њ+j^]Hi>,Z Ռ-w׌\פь\5#;/W }]4'993=>'99n3͉ne3]Y9ll\cB氂 P>(يzDg<scOvr~ ?V(͐~,mi9cO97?vŬp~*Nc-RƜcΏ=\&Rw4f96rp~,.3?Ndf['Hdfdu3Tiέl|TsL@* _/ _z[ٻZka|MkVMk\i?k+q|ͩ9Ukܪk~l jl=hyZ;4-O3\iĘ iv"L=Д M&4~ M8C3HФ 7M%1϶?oSϕSz{SϮ?taߛznԓnw#ZL=CLc&rfˤ8O^tr{\0qzFS@Jio^Rf~s~R<840Gb }9\9zAԻ|90C/.tf 0&l/BL4 W⡂LG1(f(<|7#ժⷩZQtc6yq!99)~ͮxw#ϺMD0ǃy'پ둍) QK v~=ZA2_2c\n-)*C3hnؙ`kZē_ rdp_ F7pwgvN&i},ږ9YN/ vnPP-63o60+,řݛ[{J9|wfOu&Xl:X7t-argݧ`C;fgٱ05@ J]'Ld /&2Cw bNC|^1r&ϱ &}#3%JXg1ţꂷ$`0!ly=VXp+VR?@KxO3Z/we ۝ M:gaXәn850=.~ʎ4ұLsTv+ i4`uO#kas (0cRaOiwQDf]bjr]8^M+0K }i8 ^(U~b`̈́t Ubb:ƒT2tuQ @XH@*b| &^i!NSlCwfMϳ~1N]W ]}%7;K.$AD $̂7G`8!42:8+Up4\tΘQ#ho0MG`:XppQn;ݺmNړ`_`?@P +33= zwR"fy+2v;Yd]!v`B,ŸC#$` {\"752gL8i}!s˰杞x$HTQ(,5]ɱFi=-%ާG^( @zbؒCsnqN4`b !oyCL'yZb Jo+ CqKw)x6׻٭ߩ8̮`Nɱ9kb*U{#ibOn{g2 /rV asbL*\$#,Tqg2䐧N6N{xLͯOc:'O/AQ /Y.ç[ļk ,/DW4% 饠6j;C>sH6t^p$ n+Wl!3p~+` [l1r*(s/M-.sQ.gnuZMjF貌}ݔ+&//7dAjs>+ ?Wd#xˀfo Xo㡔M$\9BijwWӛ8f!7fzsMuً]b%Rv"_@Ye'VҪ]|op)v#/Gma?TzOfNfT10I*O4|k;;'(r=t~ xtD˻Ey Mrޯ΁?3T. "d<4/AŎht,#@#{sE'RX7XOaVg%f{N9ƜeSx.>8qw?Wc;puI ` .x#ZN㽦x2dzy_D]`I&y5އ |$dW;{G7;]TOh{5!_eo h,Ȼ@s]*;(h O!n6:7O=Ye*^xCm[H^]\L{~> dul$>k7dtFO\]ÜkL W?dMz ":""I[5\]!4~ h43C1/g`Q]B3%=0^a}ƙ[H,ny,m̎ɐϸ0v%3s=)WVGKuhRScb[?%'G'$-PBQx9f%ĝ {.XqFiE@ɫś(8+γ9Ö}pØ"fODԢNtT-ZkX^%\c;?ӊUүDOO^ ~VU8m?=M<+u?~0;}~xS(%X SM3L*yr4$%շU8O2 Rt`78$J6Uf[q_kkl~X.գܟ˦y1MD٦@#'s8E88|9y\8ZrfÇvn|LŴIG>>̖Ay:jK8e˝-Z6r^RwpT`.>78f7yR>|~j &.>+M2t"e uEi6OCysZo +&*Γ\A}#!s#5ʸc{dS}z̈LO=;=Ɲ QGIJo.H.|K^uqƺ95uCZߍ.ͫh/D/DMkL  31x0_kbܘߒ&!w̞*#Z6Խ[WaV?٤Tu?n[1Gآ᱃-cx?Y 6~-hi5˗_pA{T |7j6ޝo-˰-{4 1̎p [5IKFnwaw<|z}q¾WAp90~]206BQ+nyX SJg;-_蚩|Z{᪩T+_D\UM6ByyW1U>z~,!V6#L#7ÍYۤuCimVQer(J&Ա;5ZYqL+1e:E+ßZV**ze1 AxB9}8=Yf0+ޓ[5<ox9(f_"pnCx=–M{3M}ަ=5V)&XIWC)f]!T[N&2\:&[<> qq~~u g\'=)>"[q~xeuyX<'\ky|_WBv {p=vhK8GPG$88bl_k+y_lqud C};ܶ8҄mG)gj4b9Xy6gy>׷(,5[<]nUY?dy%IhG*eg*˃8 Vqd~qƜ#N=9y54Z.Fq| _({j @&_?WasE&9Mrhb~Rp1CA`H4|H~<$MTszLgv}9EٲY瀮h]=qϑaN1ˡ>ecA}Nf^Z~|OFR|1"dh8 Veb!WB+7!L7VqH% uX'.vܦ1U \Maq^z#k2R-{_eq}?y[⌇fnϪf7ns%BV7#3:?-n] lO^#=6ehFc0`HGla ǘz%gikŒicjØe{ћ#i>=msF~-9=mdOާB4Ӳ=iŞ6iŞӲ=͑=mHij|6k]4S[(1eŰ(auT+v3U2YunU!)y.5YЁ{J% ;,x1TX;P/c=CP_a'|UmHFyt:;=N1ͫW-cw`U覣=o/i^:T'ckH-@m}Ňc A8ўmEߧZs G(ݏf䧲Ƭ-N xmatFކ&wof[pFuwv:T qcL`;q+]B~H\OolblBWQ7Zzڔ31!4 `vDyy b nӡp#G{ИwڀIT[gXu'] i>JĉWGE&)?,t.1Ț. wb Th 􀝕QV^-{7] ? 2mݡ |'%$)HGJ} **@Y 63Ķͳ*W&K۠wojZQc+wLp26XG"U0dރ6nҙK> ]Zt6p`P~ZCoxIC ] \̹p\2x 'km1pS+;;^@\\ݠ|ޣG&QCALبG£XCMEt45H 2 sq`^^Æ$dW&TA?i¤ 'ł݅wTxv['"]mi,5 ޲&_lYz[#Ab 6tH(X'CTX-M+; :+4,hֽh!B*kJ+k9|#mdgBΦ*_'"7I=rt or( 3,1Xir*ꀘlIZtˇ~ќЕ#  GW;ښCQmSγuKP{VyEe& ^n*~)S4 zcRkh&.YSR=-Z?c^ѨJ28T5;&,WYg-̫NJL@xk.6wkouϞ8C?C= ijў^mqGfߖa_a g(Ɛ*=nmc >8Ƹ_{J 1l˹ ]ƻ*tL|c4~$9qT@z3-0di. (8ͰEdKL ҙ,)!7{I'n ?] pE2\[L3׫:O\Լ0}R`vRC',x `oIvlbI-//t=&jriЭ@ w?iI |}Ԍo>vmSq;9i[*Խ |SU8$H -@"u*>  Z00(iᙑqft\Q}FGTlZVBƀh[ι//k߿i޻{BE-pxk (%]5)Smw :STRRV1lǦLbdEzKqtYkc:HvMĞ[-Bx|nY+¯uoKl^gudAf#ĩ]}:sEwFǖ. [vvq(y\n[(ѿ6L[C/Etnܸl~_Lxpȁav=b\c`?$Xz0ꕸJqL2g#v5|EtPx`Oy3TlrӱgY*67zX^#QUyhЕhŐ gW<{xJ˽P+JSL LwsbCű(i-lA`wG?ɘka~7MMf"!3YaװChQO8)L2ޫRCHDJ[X*5m9o9GaE ȬS0#ShFvby+"gs38'8'V(srZj99Ls29yll:~9y,fN4|NL5)'Iĭ.$9$=nRCr&@ߏ:M $ޙ#Em|P nn%A`p%Vv"_xLLy؄@0{/:[ m̄SUTWM pEo$ mWζymf}\`Xmvڲ9xe'Ae'4uxZ =K Oǽ]u޵rRQ"WTP(o U˜qOuN68sΡnεe*p/I{;Mz?K;R!YrpsνScxL*rͥJss44y9}Œ1TUW km I;›}|i#K[o= OcH r hC @EKm7nBX5pՖƊՐ.u5M-)P%,pd ,)E/g9A"{2V#gMf%57T^/lR 9`0zX2 = kv³E,h[=V/ɮnS]{tg$tEDU)lu@sN - ҝ6hAytC~F ^v)휼1ᲛRL2k2TO}r-,WmpL-f@C#  ] JdQA<),;!+bEKQ̡(a Y:Ƞ>8fC#1iCvʱĬBe1I%on}fE -g0ިf#~zh^hPK,=idƜP9hviľ̹K0%}6sUٌ!&RX)Vɟ 5/s; ʋ:#ŠsP7Q<:mк#}Kii} jW7/X꜃|N☫du56>ɟO ѻ6'wòZ|'#gF07]g'{b}#XϜ Qس 5Ho?~8weGd tzRe>|d ]tyS:#X=uJmd 6N+jT;Y7P aWa}]{dO:HJCH<2CpE3b&owSl}SM'̃)Qz,D %d;UwhXJ\f (}Uзo~3j~rX\ij!7>)C#2/]n|@bE)bn tczHM33Ls 16G9X6vVm<9˗A\&mbnT̉g̓=ڙ ð|@ UۤdQb~HG6z[r?rsvilMK'r\ UA?j&W kCJn֊é<OWw/Y w7/QɵLRwGS7^@ {` U,R9I8$R-5*(fMjHgol+5THNw}؁m66BpPTIH椁NFNy}Ǎ c?B~OGѿ {Ru-90nW)"tVVy~||pl1$:| {KĽ# 吶Z%R_ ~}A1{FW*=/"lP3yJ,4'6XՂrt${iOqogTdਲ਼U~uu()K< UjL3>o5b^3,'Ў*:R{]u#m]ZFmҌ>Bqe{ ;V-N?d*H?i2}I$݉f$hmyhv!RP'E' bʱCUf=bp@ ŤNPMNٺԲ 涡[e$֋]bwݡLZ#\oVtX>(kÝTXD>|dT1$1OV*)"1%- gVh= a8i jY2LAb8Lr ҔlNNkRU8;Uh( b.Z:-*1wajcF gr(:BGSӋ`pNyG<›*Gp E:3ND(@G6c])Z.LweARex F 1n*vp0tK[Z"QףcpWG1tߞd3RQ6բ",ks hJ+ݚm;ȳ^ȳMvH^%CѠ/IkerƗɤ0|N6i=//ThJATF|o) 6S - B"׊cu=kfإEvih${|uՃQՃi]g_Wª=K(”8}2D7Ɨ}%NV Yp ҝģa9}% 7M|E󏍽Oc/wtN?2ZFyA \#ճH=%$/TXQ~ KP'NAa1@ՒMv_ƷB ;ؤ(.*xyIH7QJ-~8`oX̐YWWf CJ 8V-Ew"&E.I2vJ2rB1,mKl3C'lsIK}k#t+Q9~{ej<&jY$,_yԎ+Gp('KW7LeNғT=C#ug( P:^` \dOՓ&yu`K6')i`6xo- ;ճ;;=c=cdž؃wiNf(vtNrE_fŽ8=S{Ѧ}׏{#sճ?[I:oDw0}:Xt"*S) +TZ^rK7)-ˋaP&Tvpg `rD_ UDUoënݭT:0B](`9Z='4 ike'NR(a5m7@u7+&T`]ۄ`rF5]ɇu|/@.a A} [6bFl?5#" >h YB@|f,R#]jB"*j[Q^QoVVI1O讄i>S`d?~粺>ڞUߢD7;Pp ٠Z4' ָh+MXyC[ڑn>͇ nfj=t,œ|5 ~w_1RR҃cRwHWm,ZU,LqdZ#Y$Jiu-R E6I\EK⫭^UG ^eK=CE$3 A][y҃T1ZiO { Jh[l@Q<0;LQ _MBFQ '{BIvOmfn"WMeyB亴{ RI0 p~ 8]Sj6VCV'72F2I:VN (]wL 030<>Am, vBN>AC哙< s;]RC)'"OG/6u#9b8pe9O)8s.8;#'\<6n| swq]RUXpړ2D9 %a@`j"7Yc]#w >j[P `owY%r-`-Zb'{0i =Mst_'#\}J:df3jCD7ORgjyKB y0s9T%l(z:z6 Nt3,1p"]CE\C`2f>0)˜8;x]CsmMs;"ܶùuJwkfc]Ccylޖ*] ;9ЫL$zt="KKď!0M\>) ^mpg(sfPfSpyA383HN7â\ŏ8B 4!fEu,҉AXu'[ 'ׇC[z!UڞDo4xcSo9hufŽ= %k4iwU; i؜(cHJbe9J:'̇4- UdC'HxR %I8.7ZqfF"|);8ٻap_h}e_p57p>4m:Gv?#%i* i,dLR;V;4݀(oPRJa_`0t+74T  胿;}5R2G;,E]OkՓ3&ŎېFibfފD,8*OhTR;;ȋS,{1ۈC6`NN̦3% 9)#O^J/'7kZ/E3kTY *Um> Fѓzg&7@;)<p>#ϸbU.vުm6g|[J>߮J~ќ{:c8X ?˸U eNdZ-{9CQ̼ţ|\_K vcX$ժS`Q^;{ EX-r"ʔva7Fu;; dsGx$j(ͮòVnG(9PςYnC<^4QJ]ۥR$OͶZaȍ<9ҷHN@d?&'ƟoD$``]$$?K諾4}-c`UZYX5>l^s^K6 9+0R;^{,\ݶw Jrv0k1>̯ea҃ y;@S٠rCwBx"liCEQ.%iQ{OYuda^с"}s5+$5^5 v3?2ȵc+tc\j lv0WMbM5S0bкYtxdoOrPlZ9,Ld6UIhd,qxz"jBk3"Nܝ }W=Z0h0[~Zxg'2 e^I(v=Uo e%pF_xmЂs#Rؔz`Uvif7ظ%!~\f)=Yd~V#Kgk;7! KfE#Į~FqO+NnIR^ k5G(*Y0'DW3e?䮫x$p/iGo?X{gE^{m댮~8(vVs)&֘&kީ.M7,"]/Xh!``o{xj kU"9C+ W4Ep-h{!Nf8MBDnZ?m d9=~H _!#5P2g(?CMc^`>_-ģ!`I[<'T xձ1jmUSU8Tw _ Kۯ^%/q_OL~*Z73lG,nBA>;ۏK__ͯ'7*ΨҪAپJn,g$]i&pVB_t$kegH8KѶO-(iMߋ0;b-#6{wWčgZպԻQ" #[3]81>#g|FDC*v^ltf]ɢw*AJ6>7u4x+ķ*V}(z} GLjP3˙2fK~sUj΀V0/+jT0(Pa:{ c$@"؏*m|;~|TnڏPM$LBa?lHdIZvN|\Tz㕸 P-$k-ǻT-Z:y"Khځg۲eq) Wo 5u1^ PMgf6B: >O4iʂkuzG*z.1V=@ehޞ5?(Q*@3NyPj{,4-E館 ջk_E Agɰ &܌K8;XeG8j&c8Awd;\hh)u#!*2-^*\7 UsopHw@M6vUq sz(x2艠nQHI0k-58;%lW. hS1[f!ʻbZB&Փ!7YO>d%QUXUp:fSE*@mM^dyn .Fa8w2$NI//>L3B'>0+mN蔡;T넏ѢIj fFxP+r%0S(׭:Mz"Խ6i͂ kC!p~nL=NJr=b.?o[tФ:n?ٓ@%DD;ir=kc.4q  ٺ)>rnB(RX?ab>6f%fVhG^ gbxu&?rH946=yѺ*MH!Fuĝ.F4C&+Kqp6xv8{ڥv&26*s[UͲ6d^9j_ٟ5UiVtIWZjW]<5!{FվcՊb+{?:b0=*7gzQAp\ʂ`]j>dV߯ xEh(>V~ʏ"^^HbN+ax[pi7O>c_2XoV-+WWAbQ"Ezr5ïܳpY r vm3#LDl3Q>[@@["}!63/r}˹;4B+ZsUõ%+9 0,k`kw8{)HLbQ$\ս aeIΪ;EOOA Ybи֚9L 6R8U/1Ѭ6~%p ե rEمKrAW$n}BTQeÜ1G/@9%P@V] UggX \lsfmd/qXc $`^E@N/_J\k pO<8lI YiN!'doz͛r/.`E^W"`F~GHd #ds#[>cߚ:}'Da'_;gQrm¦I?_Ovj&MU+<}yʦ_A>S+'#:k̘\A#&]x]t 7~斳js,8^Û:kV2nM@wk9?wa8p_"+Ƅ%6Nh Orn9B6oU˲88ۀ.f;bm!*VO#4%tV< !p sk7q?PR5]":$[C9 &u5yl-m OҬ\QE]k. w 6Xlx]z(0^LH0h8S݀S%+G$_u R^,Nʒ'SEs Or0T\^@K_kioLy1Kx.9bFwtYxnimClvyHqnYc["炸;1E^U 9]V0NsUeV'IqNUWo=Wć5}FIJZB!F^nc!͜'o%Mssj]ZDD |³ё֖p 8$q<-A,Kkp,m)_!2%o7_(5碑R_pm ' IvfcGxwN+?h`9uO oOF؞{fu'a, wS]YY&"?xI_fB]cỔ{A2[v_A\'M1tʳۭNV_n#[mwltwe5[-ܭ*un˻^ndQMRцЭ +\vgNGj7c]͞w!He=-vS ' u p,&fo~NjޯfOYۯbFf:Ƹc iJ-mpoU7x:Th3 =x#F6Iq7GhR(GlaG<^xgDwZj@nݾșn_L/B/ V&mI풋w(R~ܔ?r:, ȐpC bÀ€X݁yisPua dٰGcM_#\+ :R(!A"_M~{imyjn"ͣJЮxzhzA+tp|;Yqڷ|;]GaX#;p9!Ncvi8vFv-5E})Ɯ Vi&&wtR=Wo"_LqD֨9_|NeV|[{rޠ[J IqWL/G Ř>A m츤{:L^2:0mqCwObNЏl HDoFؠ, ٠T ٠@#hƒaxO {Ez}L6(HA9ؠL鮢JeXͦ^%;9H! xg?A&kt-cl;ҙ c1Z|ypƍ قd()jS':ɛFy!Ws]}ЀdlG[X G\i6=cUh@2?$<)*n@ HI&|:.v V̭wHU!;=vۑ 4 hpog_!HQ9wcGu@c.[,d3;|ۓǕ!yg IiHQ=6$?T;~0yD@+4g0ٵ̽ f]_uN<c1 Oc @TmaCqv%GuJvډ]WY\IՋ+Q]JMJӨB3d]BB ?e"jDƇh9⏳UU¤7Dl^<13JaȧҐGge3.9$)莿V&OXn{kxM]ZPz?7ib[t,Nnа%cM-DϢbw!P<$}NN*ImBK+pĠwdokT}9>&MP\{3ޡHHcnet{vVr֝Koqi >[q(fTVCj*0ʙd6vtE&-o;k-1Oc޶P_`&6g.Huyj}5!*ɻEFk5K`Ǟ@%hv_#]C61`48u 2F,l/IZ*jw3R *JzPb_KՑ/D0hZca40BB R(iФT@D(B!qd>bڭ `G.XoRˡ!o_1'C#qGja*ܫrw1 E %aY p3Z025Pk :bAĉe&6%`Sc gו~1P7L|lUUFv#ɚRR(6%6XVZ-Ԝl3  kF9jD IT]U4#;`(kN RUA["D9̐5tw}b/Vgfvh| #И8<` _JΩqx;I'+^XCDQIS)`J`K2\dNsHYbmhP9^J,%+]r,K63Ҕ h%#/Dq/ddn2't,LLOIB04H0 2ۑ?PVTˏLp,_O/2K5w 1L#g!A!Mbnc WT-$@ynuŎ1kx32ȪaduJmxranN3jQ9v  cqrk)[t<筢r2xi9cbLL1O_@kU@@vi` v^eFq~M@6 QOopN8GPNEK:䁴ϖyz߇'(&L(4;qVCNT_dPדg=0AVN LB05 C_WFH|bf)L עq`T~ _F.h3$OD)!?aL.NJ<$aCzYKes!<=01#qbzRc :)gN}E LFw;'hΑ;`r kmh\2 /4e8 ć^BZ YL+N!1Hyc/>>!dpqAOqỾ 21%D8}hTSvegPoRF[(49#‹ 8@s ug ׏'4y*))8HR  LtCHuzN G`Lvv'+GH2p6?$%/U5Θ= @*N22[z @4 *Ea>Qm0Jsa.(ajJ:҅Hq]# wf?YfمJ#qrʚUesP(ӝ\FJUXC {9ʹMZ-Um[>4I5-y؎xS/NN/wHMdtn nB29\l# Ѱv&`6Veע7zuvHQ\׍@K1G\vVD|{!dU@%>w${B!"N_Xt(w^0}a(oəMI ʿw{?2ZUQNb||>=Bi4h/~ RWJ-"(GRI!Zj }].HTnj$I>6@۴NKk7C5FcE6VPn3>Z$q^f{boZÛ$WW~J}NM7xmDHZ]P!yY5ո1V-Ԏe =<û;;u*':TkÑFU걨!>!Adȟ2&ȟ!r| TLB^;'pTLXۈ4j}VMr|7$dR$((5`QtW:b^WAeBU;z0`66gLَ-=W;^;xPъ8ؾs-NVte۾Pj`=QOW7C:Ǎg߶@/`D/$4GJ :ӏ<rU%Qtv1lՐ͆N6)g{DXKM KrˌN 2*%!Cл]ˌǫEp{rE"C Rǯ㵝zzgN?zVȳ}zcZs֡SgA$~^IdtO9D'[7Bo|NW,΢ʎ7=9J)UrsNJ{Gm,+j8yKr ji }앐 {m<{ 4ƊG8:\o[pV\ha֦=6gzY= yJ>bW *6MNRˍJhfTmgVϊ&mVoNN6T!V;՚V[=^׮O[oZLֲa%UK]zʹ^tpX nCZ튏C. K j[lD1[B˄DJNR_IKiiD҈CJIWӇ*ҞNXװ)(6Σ\TLvi1#sj77Acu=\vONvjN^C<ӻI[ eޤL߈mP'C?()Y5^hݮq)+|&shÙ  ֽ{j^zU/„3ǀ _oG~e/>jo;'1ZDmc*:Uzj7!z3?gO|NRtba =<=@ "]$k0f lԉoUhRr[D_o1*5EӃA{”q 2jo柮V\nV_H CUj|OUU?U$^oM/~ RM3&~ћt'${J3o^(Eh ^.%;O'?B3dzftfB5[],w_f}n#t3L}I }D,L1o>q2u6)cuȯ#~' 'rJ.4R >y)zƥiˀLrRda-^xݧٳ`kF-(>xe٢ٱ"%2E/OƢ>l˧a,aP""iwG&8օ vA=R19Oc ?ᆡ'jܱRϭZqK-ONoزA98BU[UMXf5P[pRfqw.]W%R{l_(2h%X'~y^({X๿v k30^͊w)k\%{6z=%Ug|{ˇۥ?2u4mlxFPwPnEixOP#TDKy^%e>IR A5V{S|OY:c*5XUlWS4ЎUs#57  \O mvO>[Ra7]AC)PW}2`ȊeJ>ۂ1p12>v:>J4  \D*A-*kZ]܀FiP S}>§a|:@f+8:a$@S[#EͿoz*ܰU; o> P.?Y?ؘZ.<9ad5gV5WЉӝdi 4tSpPΰ7^nGRY/s6\c u0"̑:|j;WbjP{~٥lYlU. )dzX"鎒N"6 ݃~ =1M ȼI?K_Ƽώ/mQXV Z= 0chzM@="nK40'>9rHd[Q=OΠ3ccM_h*Brc9޻Z Isi2rPkc=)ҋIly }u W@bg ԟr@GdA:;Y UO K%ɏ)ü4 9 )!jAՁu5|)ήT_1VL,%ͅ_zqkr:af١Bf?'26.1mhft9}nv:IC򛥭,$4ʺR0rḀ?Wk"ׯTyRۿ**S)/iH>!`c7BaTWzۀK axD+زT罹d[0|iǍpki'{]`yEՏ5|jtѵߡFK/{e%TCV%C5+~RWʥRXVq̙*}7n.P FIa=@ f~ v]fdX[zo?_=3Df2d_\RxU1Y~jeI,E[L5<,˻XGxjjxн0o|B\ M=3Llw+'N]}xt. .-.}Ωd Derʕf &|teAz.$>jz*9v9EݕS~Goҷx`? g^emڏ`vsT2aБJa?~F!{cJc҄L`l(9i}}O A,_}эJ2Ix%[ pw,Xa mؼ!ddc jjC[gQ1!= 3~x:/%tc_ҴbX20H ,+`yqqxXicks,4L$/n|F gj?^l-#,9 ]p[y%}aZ*k?7uo({J fV w3A81䈺k&0I wE?;X(B ~qk?zAR(ΉFXVAm,Բ;g[GĀ/[=u$h6Cg(jL;2A/E<e[|=XW[5<~)6on f-GԳPPɫ;x$(f/| jnc$5WM&S˂$Zϛ SM(ʞ%o;7|r0ԝah6ή jbif*oّ֜lZ9C׍WGE†"'@ly x:>x~G'~<; w\oG7hܬPꈢ1Ѷ= gyQ['Pu=|6b!reB%g`QA6)k<8xY#/i|2!+-?R".[?B(a= d={MjY_rF͐Z?Կyb)` ^,%rW6K7cR[M;f 8Ʈ=?&׮@ EVM $~^,-HԞF bu-n3#Qⵘg1B-ѲN2d_q!B:W֙Nfx^NFJ0+ dQfi2qiJzFZc2je ӿ%ZƢ;"s2_ʹr}wrÚhUs}iY9c{ DUG8ioXmU6Wa>6΁5z8;y'NPw5BQ  -ڈk^@siMjC۰lNM&#S41t#Ώpte˧(X74TqQB.v/i6S&=8Eߙtr5L+H̀Q. :*+zAی*? \MEUzӠ^JSQ)V_Ot(}.OZ~7o%ҥ:t3 J:J+)Zkj8wHUXRcQz, ~NEܩG䩸IvEޜtOuOD|=\*=؈UG؟On 2b<H^]4|  0 t0M`֗7efi%BnGvv'nÌ2dv$o܅Z]ygu\96RVz$h Hf+:<db%h ^Yv:_F.: 3 ~aNl7gY)i-/Z$hݱIjK.ɺQצFIn$h9m ڿ_U$hnIЮE6(u*AK(CUe25Q27"dh=_mA#νrafBaݛV湊[Y~ayuvN k0>!l/3>Rض)J:ŴiCa&kBiO,dx lطr2HfM:C7AEvv5,ylVJ56oqE,,bwXS.VK5en~Yrur3,}y3cޱXbPx߄PF^_c̚+#} 'l|A `ޱˢ;fͭ򫰪aB'/Obr$4S Un2i0ѫL ǭ2Z nc,[dbȄ aX 5&Kux9\a7 $gЅI`9\`u2XTZVmVWW7v  0ESnߥD~N`¼iAm=yo%=v$7|x!vV |s_Σ+#$n7F|;ټvpp5¡Dp~E\{=6{l`I`p#epo \pDsCo R;Dy)7[!S9_5)$tsxU0Ox/y2=$}GR)O׉:ԅϛ1}Sاkc>u>?y?1wnX4էwf[Kq]=C8-}~knF=MpR̯Sn ֬L絢-c( bM1hGk,d\&z3֜i7.(A~Kk oDСmW+j@ `ʛC[E&x=g#v`ChhНd$ANMOo@ RE4qcMiq0&LefÓgq0f4?'%|~`.VGN_80 '0 `Zf&S4g\h~6vD\&$fifŢEŶ}CzgGODm=΄8.B@Юe'-ZZiD/?\D*o ;Hp鄈k%t C 0Caj0 QZPD=sHM !Wm nӒ]݈@>bȋ%(1)pJO~x`ڄ RTCx \5 Mm=v7;˫-PKו91F u%ڨ˕toMI61N]hAއo@LBC{Ϸ[U65b$8PD&o@uch  )bJ^`IaGaY$ܜW@oև:(`*C(>xP&hz܍Bx)?kUko=O߳ =Mk:?SI*`[`#dqGaSS ֧$zŇPqqhLOQ4|πh"CE {i =N@<7!e${BUHORTʽp`ƓkK= BhEozz}9z +ܪW i2:6SZXlpbTI?lɬS+ՠqe)ج&)*c!1[,vOC =H"K[z=BKz s 3Xt?-Y/"qHIW7VmhR1Bofu\#Iďȸu+7gj}D} mNxU7T}6:pshԡxqtw#f;iHzg44Dwx59wn 3{;iHzGt4޻_oxuqOEf':  %NP A3͐n d7vYgVgvV}C@$!df0 H;SM 98RWU]DL}{+.c_F M4:ox MqsyA wE?ӥZGGp y&:ci: 2bu?D?&x[k;|e,ˈ2p~D|vJ!w&~fשQ馁-/tşn3=tn?wɣˢx!ӍeO7 MK5F=sBbb/PY7$׻Z|D묈\52p{y[{(ٖ3fXB^;1%^yܟ\>,uዔknY)u ZA\Ì W1" HB #5wgʯnDcq?4Bz.OŪeoW=(?m!$B,8zK2FWnXmN~OEΫ 4:Hx|I W}8d =?l)3 <`Qo7m,?dÇ)( 8ĢB=OG~;^fxq7 botS#ьE~ F:Ŋ.+؝]Omzŋwwm Ά QJ}pO +X7#.?+_U%Ëkq?xwㅧs5wQѷ8<6}@\/^fߚr?)(b\/( ~;hfak&OZ쀐hρP,qs22AG%9^v' r2lYP5+ӛ8=!*9oP5|^ ) yq-)N=gZZ#w^|jkz;UwAtLjΗ.ܒ:Ԥ[otv~R8m! X %C'+μd8覸]m7D'd]OO!2L6duG`X\b.;5#|91WWʧERSl*^\ݏk{BLrp 4K <эԎ1sF'moݏ3DC]fMtHoƣ"dߠӓt路Lt{_"ae%g\SnIO!U(:ڔi1ɎG{:ik|b$#ZCK(O@:" Ezh3+n#5ޥhFO n˽l@!VځK?/kƴty 鉠EWu]J'Dڣ$/<.<|XmAk`irO j-e[jz?c7d$bZ:!x8ěX%呚&'Y Mf/ yjdy;VkcUe!=*cA*@Gߡ]ͮ[!\_m)s7.)ܻCd@*; mɭ\_Cj>Xh;sc}it wHtNcI XS r =AK.A#cE \xkΆQ NQH]d;䂈5' O~olBNy!@AlQRc1]w)6&dbC}2X+pI^ XL^muf&\lb_Q|u?ݪ?*:cw= VAbxm~s|ptD<= w;9bM~y}3тH=&'֔jݑ Fzq%?dq00rJhzX .R7@E^:l[|d:i\\dvx12C~‡CXf\NkڍNߺ)riqdcev<./:q4,Ni0^_^Q|]"8׹q/DA~ld>/@+6M?(`<[؋Ln)lQ)x('+?r "HV]ti jC'P>2vH$pVd$"fA|5 ֔nU.OSx=h)N-X]wjjpdD% ;.ΐxZlꔌ FS2EBV|Yp\X;e2%\W~0G.7}VZ*ٙek@ %eύ oj5ވK͹8 DDR.ZdC}EK$)%hь)1( @kРvU^|.^“x,BW` ![4 YEO"pwSq*oI SZ1$NL#%U )":%pIT:Yj8 iX Rh34e_/V4KkCIc `SjL1'JJP.Qw,R8+Q?Hxߙ δ?H#z Լ!BЎQ+xJvˋ- 5bC&ԃd) Fn/L^j)TG絳!sQag5l ?R7FtLWx78|$ tXУ/ &DKE%^WG 3' c(<qu!yy'>QNOpXsz+}hf!EE XVS?f4a3RO6wʼn-e/X|A` Y7Det o" y6F`/l}W@ċh cq_CJؘ7)iyB %u&9!5z`7 <^|mnN 00#?Z>:b#)Ł\,nRtMo,s: r56%pc]jX#S5syE /Ŭ;PvUg?/>nR2F7e+iB!uY}aNw+uZƖ9n윛jOwܙlQ2 ͔׊ۛ^  wےap5?2ҁ< SYa14kn).ȵI4}Vo^o B(a\_4|CyZbI7{qwTdV0տZg@}ْ'xJϾ[\U/z?YlMTRv*r Lm[ρs}iTn/sl +s,+e ~TSp6>O%S~XAQ 6$z嵃㻾uIpnXb^: ~s\&~k;vg;^*ĺH5 ֵ$&ٔτ_OZYs;Y{44$o$}i[`A xu܄ţ2YՔ1^:[a8R/tXVjVwjq)gn)MVhNrKԨ> ?HG?=k6 CoP{n݆aӸa\X3̀ƍxLjnr[$NqR8#ԸPpO܎l<ۊz J7{K#_巠BYZq0UĄi9&dH3I*h659rD?A}?.%$4{Ih(4>|,2ΪMdjpޢcPH. %g.9Nnql2NYE",%⑴|T>QsD'D1 +9QtNVlGAY{4E@[2!jLOF"Nma wTDd1"0ٜU(LxU:-X{#Ԙ )vpV"R ,5Q2_e*Wc觮[fǓ~S23U"%ve#Ff|,ͶEp,jEJVA\M4lQhcA %eT'1OBOj&ҥgK5|4T(\msVnNor~ %C.]R,۶&w:-%6O)njn`ndr)mH" V P^.:, ;.6&&1W4<(veIu y_V?ȇ["M48j'hy>3 B"#BFX5 ÈD+:C]1DxYC7@ E J2bȋzk4خ Kz bU!q(*k-b2X"nWa5L&ɗ+).MO H2S".XTB )/ 2& NV?;tX<:gGHO81fXh= 7 0wo-.]Oxwc`| rIL6ra;WH}d)VyN;zs@ Eޔ~JI"Nܜ~+`U nY$1僶%P+4!2 KF4 %YZ?#e$ QdGgG2Jbl߁KL\C![ ^d]FXXs;hK!edLgoD56J=o+r?jxyV->Ay䪡Ǔ1*^f]]{xUGBZBRW 4$@gfc-C*0E>@W@;ZIMϪ~>puOEoJ`8J8"TۢHB{DSUs==t͗M48YP릟f{3i--(i8D68jr;q5#ޡr4CR2n}J=NqΥ2ICV\ t{+aɀK\g&nrť.Weu+9W[I(HFׁBR+{ U9̹>nddQf:R!gVLނ_\枳)ž KD';4DOw~UG^2\9W[Ak-];o#0%;,I] K uIHYlu?3rs %1A tSR/]Yrsenv19YGSv4hhlI]֢wa[} -x2Df#ؼ fK nQtʐ*O> \8-7c2`&Kbщ I?y>;-+l4d9;|S!n֕WHqfK`3T 1zt_<;4?f#^`^1o0o]w( }yW*y[g|Dd)."-K,A?/S?.gk: Oz-`kWcFт{xLF-7^1INf&=̖~QG(fN Nx.: P]Wde¿ |ɏ×eLTN#0 >$1]bEĎ姍cR]ԤF5ʎLPyx&jVcZY:g0#Vo>vM3v ֌?A7UkZ0V Yߞoҷ lVJWə]bT-usW,7d/^E:XW'{@fz8Tc `8:pBH̯ )A?ỠKoZj)W˼[_фB= m%*Zrh b{^݊GoKÈ#f+P㗫(ؑG|ICS$)qN}C@:2FW:ҙǾwU16-_5d:vP7K.xGZµSɖY-N1܇tfcU 1vUŝרD3MF񋇐OTy{`*}KIb1)[Wá)sed[ ڬ<`RS1ѓL⒦P/λbOˑ8d'1>H^a w>ȦUŪ1Kы"vWȗ%b|[2V1ji$ 6lMٲU}qj%)=GݲZHXlIGg⏐.yDS *g!%lïUC>5kn}ߜ#w+/'8G_<0UUs F(Mb=*1fҰ)Yo~t56nM1;B P0='ѿ;,i ū)M!)y@\\К"½Xmm,lUdzҷfq7akϜ)#{'o/Hk,g=D^D+MYR^mS[wU0 "Gڜmfk3<=`G*Wu6+Ա:KTR`wTWWa'ڞA3-s0YHgJßpw#QzOjԮ+mzI6v66MgMks326ahNA&M0{ mIMR[ږV2%GX;ж$M_ iqc[J/ɆόzoG74ǍMh<=6tʮa)*"YP| l>qRx3= eeY#Y$wu?,oN*VjRrDRdB|ή˟̂ř,X&$ ޣ42Z*PĴ7Mѵp5k{ɶXH(nsZ7D%{?Fdond*vsMbWٹtOR$(n_7}~*eK ծ>b_|8dp=kC}y3b_zMQx0bGב]raM#Wuy8p00_ wںXȣиfuc?~p2O&>yY}hzG0uk.,_ݒmtyt@5FLIK{ڜ#Il=' Ҥ-ɞTPK^yK_,D} Mcˎwڽ`! d=oaݧ)6SvW}/y>z_>םkiNj Gv掞hР3/(8م] q6OMZAõ\HJiO?Xݸl':W=PȖ' ~=zx5\k:͈ߑU#8]W#H}U27Bzzy$Tj`!C.䡑uza!"e.*Le<?/9$VZ#+ב3}$?|UhsoR[ քVܨ^wٺZ3u%lP0(aCG8fjh]u01Hߍ58S)}ԗ䯀5o%n?Am!8Yp+UB4w>VBXqlL@7JzJbQP#q{]dlL|Ă_V0E f +<# yKkX(.=5y0,zdF#M#,o[ JS@.◠P$Be h$Kfi6 OM(3'HH]LN.ʴdI詝+ǴcN3Q-صіθ@3v;c`gtU~%tK iNRSc9ޫjھT:q+rPmC;mB[ c$0@;UC*4Tx&rx0v -Щ|MBݕ|SS6pQ]߸_) 6n 7Gm)X&ǐmP*3ʦnה[?(nHZ&ĐO0xs 8j2R:*U e u~dI5$CU3 7m lԯG@ 4']o0Qj}mGmwv6m26vm?:u>5~[*ۀQS2%6T_|AU\UOQU8?"(26(ӕqDɗRS(U%AGS %h*_V; ;4X(o&MVx`ǸB+=lv~y;+"AN0U{.ߝɄjbh> )7Y*21ϪV~LMOw%QJ )@n P? nkC‰?h'|zUS<ǩ>|]VsQ@7D&j+/dEPMMXiۄ4X WZ겶/un/ԍOФC !{gz2]]&#Sc- Q8& p%H,_=EsKXofW^qif0|ogwCEBf',&,D2Bb>K8P%Xj6pljjbX%,*-݄:Ks -r2= 0\'~`4:98(cA,sF`qo00)mr%mHwbYH$tx}NXic6]`(͏i l3+?i/,ڝÝҋ? DMp:f_$W3{˃DO.tde߷$"b \oL{b9m@,^29@>5`F<`- [Hp.\F.jFDY|\9/=yIzz\m?jn#Tٙ*8_4$yS*t9״77?QddO \V0\@ƖܰT' h7qV 8UNsWzr?e o |Imo> bޱ q(ڐ[_{=ޖ+4u{jS2:ZR`_\.晚Ok)| V$3|KvЉ0g\wx ht+$wŵqvo-sIG)a:6abtB;V^\Dv)46[v~tBKh%o1prx?fhCIɥ\G3, ]M]q8QL9@ﲜ(e " x\cpM?cUZ*~2*`Y5]a*X`+a?^rnUTb!T0* Rb\n;wq_W AYW9,\S ]ttƇKoNqrO',-=z6P&۹,mY},%l^s8ۇ?G0jhbm3STV^'L Au|:v<~J`8"zrؐ{ :.ҡ$wG# oũKTU,~sS S~C!aB67F.@!a-9ZݓY XrX2~s}&S5pUඏQ->$ faN(PQ&תG "uP5MnuE_4S$)g2UӮ󺘿5cT7DObgelK%]AНnդ;W^݉N$itծxM+ؕ+kHW&oAS,C\NKiXkt=dx+f.Ŀ)4˜qGĿ/P;?J-i04xFu`sK.%#%R?7Ofhq5}:G҃]Iv]D KSPx5oQvܑ'k;5ةq?@zZ5RL~ݰ4$![*bb*9KF]O<7+UZk:t?4@IÿaO5%uE~bY+:QrafQJ:<.ːBldC@XO% 6.4P吮9.+/FMmi,ncK<=oo3~ڊs(=A7 ˬ eQ\}n1my7"7SY)-,C|L.HJ!;,+7<'1;3ɍV14 iPUB+b40ؠn5J&PLEN4Lh0x[>G6`[WnShGŒKgz!oe_a${J;q>42ѼjYl=xFGeb t6}q'3d)ܵ( :>\IN1Zԍʍg-.a4 ^{hMGm ( ^LK s&{! zazEBr!t󛀛XGV'-O 6 8 Z'PF+6t̾^N^@겶SÞ,ƞWG蔿i [rdi'7frtEtEs$ELM}K18 1/S%JR'M{lc-0{ j+w(gJY7G = ՙSLgY-U C?S}GƋZE{Q6D O87zFSB{&7ƨk }Ӎ$TA=7Tڍ?c֚G :|lVF9^}ǧ`͂3PQ.:m`%0CfUZ}Iw#ֵZ+?{P%Zrh}dB^l[a\5Nsb\ W8_FT'E#PPwFf)ӎZ>ɋVi.@\DB6Mg8ɪm#8)4 ^ʲ7n;U(궩C-$ H4)T`C>a.WArNoXFX(#K.Ho*G+,n[z`)v ;˺;SNIi7~ĖQ$.xuV*!>%[D:ϓc{{1FW&dR&r4yȤG 2e26E&;M& \&}\8,*.LIuS-9A9 ? +Xn("-Q͑,?Y+ʙwTL"rf"VsXGIz=Y#?Ч6=?KC7"?s`oğBOf8\֠'i[e"RrJ^Բ4>p b䗍}{p$xm($T"%+0/Xߥ,H%$ oQrf6&mـ ~lL>5.Tan=*bLݨ'|n%X4}Ht#)0]|F*9j%kdj-,ղ饳\7TgWa&0 D*<Z>" 1L 4a3A[la+L3q-pK.J ̆bq1L՚dzPXE*I|F>m#O'm63( 2)9SQ8 ?&>'qRǛ^7`|"ƞ}ƈƜ+= ,HgX}-bNt?%  4⮽+ӭ8XP4(BLaʾhL4 SsáI;AF,tLP"^> ]*:9|Ot"Mfw s'Ma:7sg`.fZKEtOcWR(ߣ'wǣ)攬.)\ `W[Ήd'^N튅x"SeƘ)&m 6i63hcAkg„:]3q$vtA3,Tw`iwzZ&/#b~NapL[!Ǡ5*v0 .SU8^+dɆ82lkmuǜpR>Ү^@<^8 9"-W⬨YRqV1/Adq[j3'3dr/^h$8MRH&ؠG_P*Hn}9*stR4 ,CrҜsR"!bIX$3hipV˙DFwէd<}3})YFEKO kDD-dQ }2Q苿rYtԥKOb}FӲO46"Ci5ɛ;EHPx^Au}mOVfiFsyfQnrۯfu\,K >/Vv'D bGA}4n <呈5Pj+Q"/Ӓ*\=QH"^f_{IDp ^@}SEbm;i 9>IbwLגm&۹jt3vrsv+g"}ɔ+pܪ-$4U4;ԀUoS$F4ޏLaII\S)v$WN h`xj C[#Q[NQҍl]w%{7DD4ԄAͅ3%hD LuXmK[퇪Z.&dwqAQq !9sI&@~/ٟykƠX3fg' "vv7Boǫt')Yc1A ?:KroA7 .~ 9^uj>i[y'Bw?%_7D'~"6H֓DO~" l2W/EVUΕl3q/v>}Z(o,kJQ1u2-3?uf090O lg/69L3389x}z.wǴ7zs}?w9T Ui4Št*skxްH5VYmnix$ߔcd< |%Z^Yg8I8G8Ip&uSk/7m [.:@1絊:/s'm2=skdXAX(*[_76}or65s[?FX +s;ΞsZp9O}$h,_뻍;qܑܙԺΨ2 τt# u~_|Ʊ ?p[^j>e)C\ (~Ec<`|}`9w1&S#Ra^pGnh[t %Vg]gЗiݣS >@}&WEXi.uE>}pnl,/ .LV9 g _+~axashnr+RC@IG)oi7ӌp&aS_E(iѷt$VIatxBe L6Dp@V&ފpCqnCޓm<7w9%p6nVnK9ovP43 X,Fbӕwa1T NG<XHOqtkGvjvOhznVbk+S\B`Tbk;;aɊ<Ђ=x*I&yr 뜹%Om=Nk? LE"xHXgG]dMrپ ߋynu'Y]oY5w^闡z`EŁv! F{: 8tyr|L3WT<# ldr-w>G&.r]rx}rh\W&/b`@1ymo! .w_k i_/rD{*[%_/%fh?tSq$`m~ *Q!0*b6b' D!A}/# XwxNߦ `9 b\xK,gPa|v! d0 ŹpZbXѰ!2%ߞn#'Id+j7*SWgxl˙;[T>++$yTI=~ gIۘFAqFnP!DU|b)gǃ5ngtx\,~*C^Shb'))u#I=,1sb c1^^5n q<6hv+2U/_?y9}??/"9zt`ܕREIzqJ1(V*[֣}ŗbn\)GX)64y'<6Sx %&WHHa=oַb+I@c跋D¤\!8J??Ɗi YNLP TnTgOѨΣb\Pi[k =F^H$n:Z|Si(1nI2i|U7tCt0Jr#0[lm|u rt@(xBR W &|u`'{AtԎ toQJI$@NwbHȵt6ER>lB.)8Cݞ9E}pRLɾ^ e60gcYۻN^tZЋlr3h]T+uuttS>"m> Mff>Y ͼUSκK9LkC ދ³rI3OHk*WO.ONҞvjcSHqC OŠ&v&"^{h◅κ-KM~A#p ^C'7ur9tr1f:9*yJ~Q%yӶiNȽuɺoZJqTqu?S3QfݴqV醨ɵQL/ӵqWx%H`HwI_k'Az~ѣ{y{2N2ք @k4R߅8tbT9>J90 }g$q\A%`:ԬkbWH#;n)-6DGL܄j,L.怆JDѤQ2P%g#g0)57%^etI͐w9܎£OAqXĔդ/ e_NCe=]=SWؙ8**ͮ}V𚇓w!pi(HaqtQ*4ewi/O ?x*kJװuՎMC҇~YKSp~UM*R4QtO{̏e R7k;f*3t1'U&D&|]Yy>5]s@]4y\PǗrEZ5}Ikk5'Vӓi)1zk؆8Fӓpe 4Qrq2h6 0ή-(DZʠ^_  UC_nt2`%6_FͿ4W4v1qCBū*kObM VOr5u%.Acb>Pơ;]rƮ/M8naD3nKTf" “*#<VW #*ST6qi !\J۩Uf@+@Wdѣr)IerLgƕ 4B&]&nh#ayf?c0SDC6NRv+j`{;G/ڐ X)V[zmL1errxqdaa";)hpfЀءݝwyB(^ߋ5axN!>J!& a CA5$10a&@e$\Z̝O#ɝ 7By|3"͖X뉰]]L7> N`?t,py Op!&u2쎁 (4)Bm XM5+ԊU1 P9aշ,<歡vZFNev>\e~zC!9N0La F& RUkArf80cXͯnī +Lr܈hj7b> G񁱼 U@: {ts4Z= ~{z;Û{$qT1ytJYBE80cZ1DEVF=#S# r-.4Й Y1q?c«GT-A>WY xsqſ9>yd@U}W. ʝ8AOVnT'#)22x !(o7ۄ}q]D#<@8C!"Wz_,vk H=._ck@3P)]"ب~+ꕨr*`D Ѿ'lł:.s\F%wNqXUc&j U2Xm#& ǦᒎsGW׀eU;Y|2UIgf'&d HBeG8$<1< !? Q|s:0t;bOϥ1B@ (+6^ DIBCD90|8 M3]|th;P!wjOe2mX}Sբ-LlGL-:3LcaGvza|5TTjR! ;V6(@&WR( 0*0 :nedvlj/lvD2VZe`~Н83inLE^'K#) wwLc[sR&Y 8W OXR"XY݄L\6;P^'V;Վ6߽ծwLx"%kӡ=H0MMµFïM!j[H|x nd_5[F3FInR4VLH7Cw'w?{]HBu:%\ _% îlS65ai`hE,CC!Ybq*X{ƾ_r;'RmU~IeJG^7L15tNu lX8ƧT1)KѦ 1ԌV۔#DaSYXRLF~q=eT_v+F7BapMj[|gh>'~zHCu/9DʴC 4`-m<0p1S m]*“BHM[@.F|bؐ"_ 'm@[A#ҏRSnYޓ\%+ 9`_qZ@F'2HdbUFע vݮ8]G$.$QIhkH>pM+|$SQN ;2Mw\[o' F2[vZX'# % 5Ħ>^Źs;﨟i=2U {N\[(LX ~H.iz$4 .@ž,<Z94PTj#vڰ wmkH.+A+ 3Sg5 00 f+mdIdn*Tˢ!x<<y36<ןdžgyxĆg1|ѹ!\x%2ytg::9([Qu|`lOl\.q6.2.ǑEN9Hz"Ț$R9czCq:mD'Pyُĝke\f[Vp I[qL-FuICs έD vN-<fB%\"TRj;ڢQcr1_Ԋ6^ſC.5j!ƍ,\).)gE D N #z':B`h;o,Zvù8S9rv܉ہI][;vx ;{;q 8Lx0 C;a*uf)a1]4Ubƴ10~4E` :_R0. ?gӲ:_743ܡEЕ[/&d,1@fOa7@f D(S*Lʈi{G^)\*b=:g]9l;3N?$w:*Lp62%T?`eLqO{2ze^JT".l{cLpU[ 5p~ut;ϋ#TFDa2 X˼ mʴ**u*ut9Je Й533?d+e)`}WV@iLs)^l0hCS B@w~'>ia:#3I|&s|.1§ s|^# 0zi{:NqU1:|r o{@0==j{,1=FҺُ];܃ፈ=ANJFwa͛npiB|X7w1{V\DP/FFbDBاhoc#w/ 9nE}ִfMu)"| +3lrBThUt0ȗҞϳB?.EnQ+G5 Yl]a jG28[=9OG~| Dv feEࠅmu8DCKpFҀ6 H['$TFЕLʥvRܠዲ` :lQQҡ#,qPB 4B>*ntPWWf8hU_AOxZM ʨvlc r'=HPc|ONJpP̟"8 U+ko#}bMtgɥCOGG\E.0ơu-g-)= I8^"= qH"ޠ"~ *E6 k#oԄю1VGnZЈPCFܑM& +hQZaƁGb^Vx/bk5r7Y?G-,JraBt򍡂GcISzbIX KZ+$ q?vk7 Ec?l\ᠱաdRϞC&J%OWi!AYtSW9sؾ! #ؒ';إL 1}qP;.ԗ&A}vǯVPS vm0BvCuCl5yP^ TXzLi&:@cF:HNDP4VGqbl?!д: b ^t:h2=d`) XʝRӌň3-xb38Sy<0Q PH\?2[Y:TX믣LƦdo$ceo̒:2:T.p"<>3}lf(JXS)bM.IXA&Ao6/80ɻtn#M4R-ϽR*O3Uё f 3UxZ& tIOQ `sFw[̜ "C0m^h1D1G'U6={>PJF'£`1@1E m 4ZE 1;@[NRWY~\0ڳNoC~bW Xnk2 -9S1ހ-lB DLc˝ ?w%ј+VҗlgP{)h=tԹFp>6hkB,6)oZصF)~D20x?9U6ZʰلսЀGȭi^swkup2-r*ʌDt+Wp^Mtz!w58j#HT,@ K-Kť@E4MX@[,zw&vDN l⭂I.ebz^NPAα`TZ!LaqDCD1VQs`x9}Y)wBǮ.Ѿ4j #v:ohU Kp2H55f]?! Wm2=QC3o:ynO@$DT{g h[|DɃoYyAoFހ.}3˭d`pF` uʴZqoșKiGpq25`a> bAF8ْҪZl`tba8dL(DAL#F]sH149„B;SΒqhbm,_5"<@32r :F]n7~4P7]\@T:y d,Fe,Xtyz^&#'En8@VSiLj`GGrDr)H[Ay*g@=@L˩=,f6PsȇO7%=UK8t{^̑E;B/ X i(^v6bՆT/dӒ@g:)?xҿ"̲J ՚lu)I8A0xXZŪ)@Zlko0d+1`n+^uڜs$ ~&P\Swӣ6^U]T{?UF?ZnV~&ʎ[QpX(HZXFIzKU[h$7£( mNq9 K$Y2duY,uLR̙(/hp*?8GZqѴ*nB aQp݁!Բ5v@Nũ|4Ђ墿꣘b/ D˼{\IGg5RoZ ubp:!V_?eLٞ>uL/a@I/P ,Nz6jH4|>+Wga-ʌ̅Qٸ/ L0ΑMEپ졅S-W6 8 )ID(Bo]X(3g<{lv?e g 468"`kבRJ> kK .01L-Dy~W8՝ Oد\U 2[& Ա.kYPctriFFܐ67,;xA{gZO{G))IYɊnU/UF[p;zkm aj^^).mpf[zP25Rnמ?ۘW:=~1folc֐p V:C[(i OWY 6L Y\]ԐP&M=AiCnr8Ԃ"pߠ׫}S;c}(-;9Bq |BSںX-WW44nD'~naX(L\}OIF]A1rX{ND3CEQ/Z´-ꢕہފ^r@RӉKEӖJ}ȵ'_r{ q cz-OU㇃Q?h?SPG bFp{17Uz_i&NeFjeq}9ӌm)L/G>T~rL}lAK2IG>|g)Ӷj%Ϫ;12_s#>yW9ƚ3I?jO2my xar@<8ZNd;SΒ߹B(vYH)@MvkU Mnf_r4(TXyVd+b}cw׾OwUFחh ^%*$(i;_;[Q-Ap6]JCh_lXUL0A簤"#+q(,L>~4b9?#).݉ϸYmtP͑#7O΃aFy3_NJKjUYLjV6٘(B\"|/.Q(Rt3# "Ѕԉ}Au4Rd`)~F1Ĩ[j#B&o5YfT>m0u*RuE6r:.=nN!?[-Ujjd. (e*Q =Jj+*=4tD˧ÿA_W ]6uu4-܄yI8u*PǶ-QfD] qBc~y9kA냠GScyLKJaG3xpY7bL8w6]MpBg\PQaestE @#_3O{ka⍌NeO+?tQN42J _7 t+bLVƃ25д%dd\ōU=q:yBnc\gc$]([' +&5 P) UrՅd nL??Cs,Ff2:>ҍuㅺqDQ3:ЍO;2:u7:~ΌLLhN>QY8cV}qS:^ꈆn|Itc7>n&Gؠg|-qa|,;׉#|?c}ta}һ1 ~5/C#U(*NUf@oz} tEk= iQpsokgT:x K޴jrMe3Wi |S+ `5 =Օjj1omi )7Itg=)\Oژ^9s˥滙ɍνM,&ESX Ix IЃ\ޢKSuۺnfڹ w+.PM%Heij}r3|2bͶҡ+ShP'/=7f:bWovT _htt]o7?c 9BN98ա+" l"g$o֍Doq#.۷^#E8Ys~XT ]a@=c])E#Y6%0g;,:} c/.&ssC=$L? 8ht*IN+ cdЀ=Qgc[V?Q93\}}/|wtÝHt3]4TژB[H:a1!OU3IsG5MEK7I'9|BFfÊ4nYh:H^+j0`ۛ vamv /k } U\y?8~Pj`0V0diN$_©qry|)bFhW#1I㌔V 6.V}5ղHh=Hhun1:폼"9ᩤL'&l̓@[ޒ2Z0~3;{R pk"]WNlh# >Ց1Q`,ud$n)cq ~TciHZ,JYt}6AyHÀVDEQccΊIt]O~Rfa4:{FOI!Ϡ5 %K{ٕ;4!+tu\a;4l>0k  yRFFҨv=V tl +]GN0sS#g8RT1r4JIfRqb%)"X+Ow^&q4φ/Ci8Bp5)Ӵ/I/V )pR%y$̕Y JATh%?\җ?>WI3tOK DҾ77D=ѹ/Oͨ6DP2+M}c}x)4]?:Ky oŨ"K\'u5hOퟶߎl5t O\6z Q>ˌu./!Hނ W ^uvI;Owd@ ʬj#-L[cћyEy].ȖPZdM r'*m^KhcPB +TA+_tWNr5\DafPpݬ3Żf'(> MQy_%_gϖ[!<;S93۴~pgЙ{P;ߧSb&ÒQQEEı:ÕAr9ԵIRr7zi's( Da-݈_u/)vy+e\|),\ԝK:c]GC ZMk=j,洓Jj6ydވ5Պވ`…DE{ި* XCm7* JKr F¸7]J65sLobuzNBվΔNewQ\h,?%5G֢'6–^'`G1>Pdg9)κ;^0h0 &w_=a+LY6 %泸|3I #Ӹe=8:DRb.Ԥ?nec82D%xVߊN8IRbdbtb܊vJ391{3b0%bJw} 3p9 4( nfz5ays a \/0YQ-GqMNA0O  {B p]$ /(` ᳹,t /1| >Z0ZX k& ݿ> 8pIgwi)Jqp} arx l2옭>_<ʋ>eZ )lL/A~]03{$#vbn*"A}Ѯ㺮%oy>&!SfgS1UQT]EVXenGN_S :L[ElUtŠ=B MD6>5f2--^q`D0%换Ld*W.eZH8kO(/dΓ_@2+k\W>a]o5"0Pg]MW~0dHخStu Sv)˙7%rXq2]*Ih:edᶺWZlSe6ܩfr\i8U>ZcrRa :lq[ r9vt]}I"S.S*A"puԑR13& [pA•G5*O@C_[-q*kVlGsx7; +zըj-ܹPmdNڵ7Yt2_[[]3U M=Gÿ ߹lwM>CŃNS--|"YXXRY q;~\:]οS˨ML܉> QQu4< =5B`)r,5͆ܚvuwdpIArxfQZAޒKvrB|iu* _w&kK< D-{c k޽&Oىf}NީlKd sLv_4OLleKv_ۀx(NT"ɪQ3?ʫ]~㹅W,_?/-dO83%V-%%6f KCL2,%Z/W,'\,t+7K,u0P<YE%>i >ߵ?뤖WPMd ^jXl ^rd9qn`dLs,ڽYVΎ5S̤h]Q>)`w]ynrLTAó^ֈ?&eIR6lі[3)ߧ!c>+- +rߡ[?3Sh@?W,1X hV!4Ya}*:&E QAK1+*9*7P[}F"ވuKRx3vD)UIuN7? },Tٝ\ |L>-tھ.~zӏڅ>b=ZC>ň=PXY@%ab:r`wk-^4f2s}?vR_ܡHa˟K5>q5qe&#ܵ~v²`KmCmĈXfNj '/A')91<"]h)4Au_g0IT-fT|N nz)*0U2fY<d{LuB+#!n~:P|ʅzn|.,/b3p q5A9:Z <X;C(;W;>vBV0.Z2 54,gciEŠ=Br9ϵA 0]:mvаVjjcSқdCV"aE(f!5#f#?DDWPPr1}ɠ)>É-LWoŷLf]kKsGaۜ9ꑗ v]g׾ՙa>nW3goαW&g组@Sl,/6xlΧH<))L'K 4Y_ =ø !}k( 4<8B} sZ䘯tVhoӮpbǨkS'09SYas7VyvPHK~FNn!&߬AF6̏;C'l9K ~QPcM ^뉹]sdKˆ@[!í[a;' Ӝxڿ1 1&StcƋՈ5i +b%HX q,Q#oDڏ5mXH/03J:}" Z rf%xC %*'ps}-c۾>F݋lLc]yLw|< [Y "+BÞ`GӡQ^|΢U**]vsڿԒtėRIF_/x/ig lfENFP!DVZjÎvdԝViaE6ձFRդ׏GʚNM' irN;z!8zYP_2x#Ӯ05_1⿲{PN;cf@)6\n4劯J1,Cb_)1'i-6q$xU3BAlKBqϫ @QuVUKٍ/*Ѯ;`[B( QpU NЗHƺ8Pe noV3pBW E3BA.M.toOgQrЉ we88B LQgw:2h +̲V'W;Ddޞ&3XH.WX*QILF"RI+|ҋGQw^φ%X'DL(hr[2CUK{Q[Y/D0~i~zOV'/ΤWđu*(*dϩw/F40e8㰓;Ⱃ`'uq={a'y]wu!v0!vc{q+:#q#< OfvO3T!#ѓǏ0-Z|S"ҏj\<='c6z 1daӠ|bT g2X6D^ dǸѾ]B=(g(LNY/!`s7'90SfNwG%LY! @ZKttK>$*Kx Cy'} ^K6%el^bkh$K$Kfx PyFMx.TQBCiu\%/p9Dl;>AHzHK 윳PO`SkC+i3V>JK2Hm`%?5z˱ItDAŷ +IcX FXJZ'1Zj^'<ʌFbvog9$)M,(EJD;~t6 9bJΌ|őQ߄QX́ft]/Bacp/GPy%T2Tlųby\[#(W?R=z9X{)jCSn3ǣfdn!FlL 99儗D8ڳpOςVG'Gh 5tiu"-֎r]]N2wy?}+FV w˦9327sB/s5A2$gg8V1zn9 b$S\xQ;Eϭ!=G5Gϭҝp[x%l]E6=z.e/sK=7} V$;=6asL-ưr5qݯzK<TMè]~*AsiaܖAs'LAs''F\̍x7'q߱&{b+1#˿1ScN "榚".Ghږ<`.Y#`Ls}mI;?';.ݻ$ʅ֞rKrK-1-#-.fnNKI[8qu+Lqu䡔ququi'kعI;?T>[c;6̐|뭖BͲ'КEerԣk;g4b-F3Bֽ ï|@NL./xaM$FZ.xt\KvO2y<]>9 6m}r듳o|wS)9XP*e"P)2RQ(JǢdݶ;I*Mk7d`c{SoXTڀbv6(i zgT&>Z_f5cz=Vڔ5~Stz WGG(kh]XQI3G')v*9@M -$#2E=x:tzqAՇabԭ_G6[_2|srz\~b'>K򙒎gJi,i9B ̦xdZX.f,A'O{nƕ?)^LF;D'\ym~u<56Q;ȮBJM̶|>߻6bf~6hG62gܙ婤ðӰzB R t:hzZRGVR[,koye_ZP>RHW?*z=x>Q$z֡RO&P( ciL :A"ֽ0+=aDZ!(FhJLGw#:Cł_y H|#0_tz2i[R=M{1||D<˻=A9Y0+Q\Or=mRYMe}㱝}_e=c{MzZVl~c{վ2==V~)T+Xlӈ[KY?ՠ]F>|/|壵?cm-!XBV*.AB *c̙:CQDs{.֫{buI`.b=mkhL[y4#{z]2֕jډ ĊrXL58>=R.4PTTgb|3O) j{.֜#+$X&b9iSp>1%#kS/)X/=3)\~0.kí\Zy7-<]z ۭ\HBl`Fg u| V8k>L\ H1oG\xXW,_u“jK_xe.zr̼Z{Ahjd߁qWH°h#ZW?aW=qW\:S:.*|nHUƻ:Qˍ^<M;<cWSjθz=66r+cu5:(`+;?tO[*ȸQ۩e\Zs͘rf^05~ƺ\-\-ҕzUv0>,*zUv<ݷ,Qey ;owdLiWaeN "2geצ$ku/^IQOTϏT?k@quV?anV*[Ub FӶVgl[}jY2\-7Й:SЙ2[Li61I )g،rg52a3tB4wfx8h'9Opr^%:a} Y"Od^5fd-XCMu!}b&֭,u+.1#&{)DoJKRT>?S\8䙉@R4?[=@hŔrzD9'IR扥Nody. "D9buOx9b_9b_EyRN#IlqKq1o-ąF$Rך_MRː b'3qGtHw_{}Yb6/;7E*ljE^%J!k Z/_A$ kVڸ\Z֫Eb/\S!-'6rS_ŕ;Rkq\j1~~Z!fr~O[=ܛ҉P9r; zK~ Ri+)Ӗ#S7pYa` J(I;JuehMu/ZjAdcf(sz);+PDT{x*7PlT,ȹkjHȾMXIVW朔ngiܲTm?Oؒ}dKS16ݖx[rr-\~O['%oSfz-g aR\?6ΖÍ j-YǪі07k nY%S\|n4Sryo\aS&g}Ǹl?e\Ga\g\?l\{ڸtu?K`\>0͸}L17Č˥Xf7Nn\OOzr>s^g\qyq9A̲-3kGtcsNnlu136{udƉN%1E|77̓/77pssWWBsGn\nL {17/0g'67d67{z37<ǹTus󇆹=ܼTͿAsǏ"U^شhSDS鳏G;X(r?BH ʵ6[|ŧ%"ܻŌ6<*){B3PYBVmbk3ăx"x6;1VU(SvgLaBJŀ؁𖸚sֹV nWҏ{OEzt偒FMt`6 7Gz Geh"Pķhg3]s)V6!_Q](*[EvV-tO3`)$5ҁ¥9A,(?;\{@wþ]e +s%3| +6CʨGWXB?Q\,6 kz(ΞbTnD!}q7;=5B`3])_ҏ1wx(JVd3#MsپPrھwi22^X o;. uIA6 qQKt@E{1׊SVEaos`/BEۑHhkqnyh8gݬBҍXT,mXRh¶֕Ax, ?v\_QK7%l{sqHeCgQAZ + S6ho+= sg9Վf!0/_q8<\N+\v/“Fӂ?9ڄ'>ea^@Hx:hG{Ye: LF<,(,gP ]Mn[hf!Yu(7HV:1έE2XPGoq逨+ SuiÀX#!z'͍7q8!=lͼ@(,f[sZJ աy -vۻVnV6gUH>M)t͆4e@!k=ʌ:][+4FxX4$JQ*G[D,8(SìOE{u|E}bբM@=.FA4>Yu]%5J7B$Nk jRXitH<Dn=Rԯk#KXmKr2âÆ ZLZs\eH}%lY(ZC GpWpu)Ya! __1w,"YE$siؑsq_`dj%J9u-';9s,MR ;;o,[:q }`w+ޥաK Bޥ)y< 6 fns ''*X_`([-IUKT:hPȆiqc7e1 zk-e2I BB!BVZ!JаYR](V!@:v/ EOحyY|&Ѻ|,HdZ\Fqc='wG-_LX j!JM3$],<Z95PT:X.) Փ[W%oŒĒa}%Nle[!ЍD1&o (P-_*Xvw4dhC&ƺb7DNɾ]5#e,X)!U͠!v$ףVLu݇/\nߊOt;==)wcU ]֮Aˋj7|QbcP%@=襝 mE6#mn{j{eAd0d`R{"qKWq!aƠ!Qi.)S3„!0j-e7paC1ǰVĆUplf@R돰C q`Ui.)-*V|Kj`BykaBg͍aB8D|m*ǃf3<I J c2[)Xblo> !_QJƈ:rWґm BT"TN p̄rEf#"qD"'Ef!*Tu""mF  b@(C!B Zk B : D& (g&2e()VFǃѰ a֊v4{士:88$۽S^g}t(hK JƎQ(  Ejt+=4B\`]}NWJt*MC,Jw$p`dptiGJH.gb]F!OxZ aj6!/ԁًV2:Hb]] ]w/0ruy_wE*BXFvgxu*Z9|TRe(naQ> |D{\vkyE]H~WL GufR@>iRDnz"hi\$ ̄--xkMnBf Dgfc-EGh'pu:Z]:*Mmɟw)tAG/՛GmT9ן :;t6nGOaܖAG4n&ܨpR5drƗŘ=x28v7ʂZ/GV;SÕobhv 5{8RI P;#!؁3GBPcA~Fkr) nCch|ձ  1Ià^+bX);EQr\V$ ؒlIF2ew^,'\j>BSQ#Y )\`tufZY"__2^_?]8wa푯7 =ծ D ŧ"ܬs4SNxa_J4ٟ~ ,Vsun:P ӪQ̓a˽g?q?4FJ Ҁ,1g8NC-zo 2ǔ[2Ngge 9jjpst?>ٿpr;!m,LstϿcEOdބgӍvh`?n ҍש+KNniVƧG (~3btXjӝ ҍ7 ҍJ7>tҍzjƏ8Ӊҍ/!ݸ(ÿnK7ӍfgӍ7ƽIta٩_O<ԝ8ҍ!5qcttk񍽥oҍ'J7^H:z' r JGiDCM`"?iP<>S:e> k|*v#x#$ݓϮ2%?!tWvDF7J+[?{hKCLJ68>U/>2ѩV迕S960;x fggZ\xp,;:9[ʊtj_xo0|X<$V,_Xo^jl%VnjڀɆ]O<ϻ)Sӏ묬B`Is2ӆm:Tr`hNQus( s *~ | |9eF )}jJ'}ߑf% %9SzXidFwL#Na~Deߚԋa%A>8|dq}ŵkT3AMa:ʂFrt\~vQQiB PAl0s͂Zv/OTOPRmUb߯?)%dLd '*QW yX}*w9hRu);e MrWT;`YnghcHs7%LJ@&vK1 B#VC@e;+z-*<\5<*HR`9 V!.YZ]Q_3\̺c3A8Ehr=EKX`9)\e]Q&"ga!]**:K^ @ݶp ։W*Z~ !nptѱ]ED%”58̑G{9:57k?Gy̹{g97ǚ[чqFѱFx'5kQ=뺪7[5wk)% xs7E7lUL Wa>]WЯ+K7T`Vv`:"U/@k#UL%G"1|1@ަ>L'qڟ5k( 03$rm'ֹ$%f7sjAwE`K٫}KyHΜ$]߾ g4ZťcZa``X\{msU @GlH*:Xt:n+@$]Cm'r=6J bW< κ'2 `~$uM(TB (~\mLNbxp֋(-1uBК W,VS48 R,Ԅ3zvo:nh 34OtM6IefFiv|bD1v5̊CxSOatt4Cپv2щŸT:0?~1xZSX!?S=)B-Q8]?^dqbwL.ffмpͻݬyy#B4Pv;wuȻ 0/i[ba4qvm6}1<7vg4<55ZjWiَ BȜf:/;4ԘQqM7Ʃð<@%:WwƕpN(Fޓz?u:1Sm,.ae&#W܃5[qc`>ڍ0rF"bD={V\2e{ (nR ըw<?3ʫ,+^t_xE)I+$;8*L9)ſ /nKr%v.LvzBh¬}J2A&p3joFF)͇Bh%>$^&)XO&\:qtxe#y" )CW]IA.1 #o8MOƑ 6"ofy[țpD0L79s. |N "΄=xs,f b\t0v:i)H"F,h5/BGB(KTY8 W R#/0LzA/?] eLkV};.GK&lb$Hyb$$0+5.˄ Z[9[m:WDyw@ W r ḪC*is:Ţt#YX;T.a_rRK<E+uKRY@y Ti^Do\EbKʗ8UE }NZ:Lb:mGd[r{ a؃J]@M#4+^ A$iu)`DyL'5` ÈNaNS@890J oET/DPpG{U['[b-ggV: Tx@L$Frx+PdonYoGdbZzoak1LԤ3ALxM t%BHF`P*#N̽'mbNtaNU9Ulp12ݥ"Bq2ȱn\kLk;7 }fuzREp7;iT =\*pԙxjJR{koM\w6t19՛Ggrm-C܎)*ؤhye75o}YMjYp'!)aLcgPz}Y(alPñDyJՏE6a0wm u>S87p~H0Hbr۩%G8ܗ߮4&۹aR hFL~i\xf}ts3'6ȃ'0y6,]prVI7xg&{S0Sw{wn.2ٻǚ: .F ݷь h aVJd4Of~xk7Sq&u4g/ mK9cY1s[32?Ka-LoR ?M%o;.aJJOn>5-.ۉh8Rf ,۰ycA LdosI\OzQ=v}ז{f {5{?[؛͡ '@%2| ߃bog΋{p'}MdnWNW2w5Ld)ھelwjހN=mߥY?^sic7}O]c,a۾ﳘm)33_w4en÷ëzo0݆=o&Al/N9L SxĦ?Ĵ4c>)LדZ2%cI-ӋIٸ`8{cz9įTߕ=MkL&wOhW!z~vsbSienɓ]͐/="S{iŠ&cNc9B؛fPSN$9|@֠dᲘhs2 pIذj=ne[Y-hWۆwE|Qї7SHQ7(=E&V#p3N/q6swG/OVxdأ_YKpGUKJ;]aыAv9Y ٠~Lgߎ_9o=̕ )7G de ;r-qGNj(=av+QnA>x^ VuYgiKբwލGO)Ɇ&`tN6GrP^[P܎mPA4Rx%u΍)[Zt̹HaQMs3('H=mEǢR)<<%`}vX>~[#Z$?'IqP[5"&) d=apQQ=/gNC(э15E[{CT޳n*ӤB0##*hHFuⅹ4z4c,۳Y?$Y< i7CK99~^; zEEM> Tn|SbG\:xQtҽwdQV~W{\drpG׈%DԃA@%g|\j3o4-FU3yNRԖ(穋dވVK3.ouc*`BpmQX7Y) >tuq X X;y yЇnL3S3h?_ߒ bWfY, úwZaE {E6ƚ^` c=mr HLdMI:~ə0b SA&u+d5Jm5㍏2'wo<4gNyc_;x?gxch7G.<1o? 7^y?%h7.Sy'y/&qj=x[1wvoN7\No`yo Ц뢨lG܎/PbM%0[i&Xg#m5^JexD6 WЋ߰7Zb#$7ԣ4pn~J3iv'[^e,$MdQzw=ǾFLF\POpDb8(OӔ 1tVP4!͚ #JjTzD:pI->o Fw"Hٰt%Ţ=9-oCq|9(7Mx=U&=FAYzC;nq1=.>{Yٕv<ٷ+Z5jVJ-dEx+h@؅ծvUSĈ7MVV;R]jgƓtbcbJR X1#Jxo'%}k 6aerm^ѸD2Ӎo3)GY,J367I=F֓AV&R?BХ&he#I \/lkbE[w;-T ڙ4g6L >ZVx)?g3{6mG.՗5~gs"q6d\o^UnÌ?/w;K>_8ks9',xB3$<^m:vŝ>Kb<;3&`H_FvBܑ,ƒm^h_h^;އ:QjgQo՟,$(?>0kV.T0)NcSv-p9 1')3=CLbݭt19_r$,|`@(LeeX giኗi< 5Wx b3P lΙ@$i 3 msКO5_"7UaN3nk|>;BK_5f-/%W볶^؊"M_\}ҿƎfg]OT6, mrJ$!PJ;r23j8~ -$-1ˠϛa2QD~Sd|FyɹǙ6yw_8ƻ߾RopRmȍ'bxĻBjw^|Wou'ݗ6vF#Ļi5h N{6(o9,֜Z-D:L%nܳ$L $ <0`?aMQ`K7O,{yVcQQ [͏\Md$ ToPF<1JV$ ա)C"!VX{L#Ξ$AonrHtB,ĜWxތ+nCM;=Ψ}AԫPFϱBcX`@ÍrUݻ؂“^%GrڂåXq3CxAb9 eA^kW?;y!CX{pwM*Osl723!jcSt.^\u*ri< r+ 5S*$"9[`Ph\a>1>#(yK{lۯ.=bXmXqofteY .7 -_O01?w6(pb>*T_}#gog:F32Ø;;L3IBds瓫N~"fH5kMxݪfJ x!<6/iŖFwFܧU CvOkC/jR\RG=Ö2S׆];*haW0.x{ ܸ⪛^Ow*̟?Վ+<5)&G9dS}xgnU#V鮧;[;w;qFmQſ#www|?<6숓UxنvGW$xN}]4Ž.n4юmHxxY8 %-5@1(ݥoy9Ӧ9?7w^%;Rpguux8tԦiR۷֯ic ge;GkpG5N)nun[ 6>Cա1~D"7IUk᛭=^*IX[U:놥J-эPok=_2'y,h,}?zDۘAGI*9~$BIg߆7l1<9ىȟ')-^{m vEecwP 5ٙ7WwM_$֭C,2Yos`_(VCgkDKEhyC)b4DxJK-m-V^\n4[wgӭ4핤X^L O<9^~QmG셓ockm@/8[OzZ^μmB=6t6^(oUdv s_@~{K2 OR^H*qIP ="K42ԓ!p}.f=8OldO R5<~R<թQ>1ՙt4ͷϔ %,tO,ij|},*ԗ#w7?:(ƢO+<6ac&atx``:hQuЀO{'^ B`=*F7..\l6pR*@E|/:Fd|6QPx Ktl92\x7F ֒-tLg4ZkPAqwHW<"Րk岀5ʞW4[r<+Ѭ\z m?hl#kMfl*-nS?Zc; |;Uk܎ڭJd&2ɥ#ۭ8Z#%f6 >E4ޕǣnQ6 4%hv*kьo:LkuyTQ>_i0_|6 ?h=n1.Je|ȝIOx< _W_=4xҴaUelkA16۸V71[vbREs,* Lں44l | a5 ւ'wRdݼ&jÏ۵_[YkaLxN~,җ!pњ x5xQ?$8k7i^Mn|}!@KՅHXChgCOnbY!/QfIzQe~bIP&]auإHtむ熚EUQsgRarU cn豘slq+c[}(f5+ϰrbrd xj|xG"TIPm(@ Y] Ii SieEޝ.DrXm^ reJ\gY.hoGΕov ׫3) Z؊uEU$p[TZj#|iDw:vmHWi0CtU`j]~WE >LڌnD)Ks W#݃-sIy : uT5u\m~ S.piNWpaɤxVmA$Ib V y?y` lDZu_;<tn\T #Ze~:G?R\ ¹yc+ kv^ ͈T=fF::^ w`A{ǟ&lg_Jҹܔ&GF.JqB5xc([˳II1fn<ЮySO֍/QE u4Knx^=91<О;1"[JR 6V3u@q V @^.U}(޵Qi)<+r 嶏zf;2&[8zZ`s,fVJJpp.b De|>$4\JegIXۤdU|74R+9RG'e[yDiJjA]r\WY'x2o7?yx>y9wIG#H1|>&I,E~\(47!\ww!>QZ 采g 3=أ~ ˅wgS'/~Ea XXd5*gh}/>Md}qމϝŞpL1;n`츹+\sr34WuºQ 1d9,YnE*G9ye M8=8HYH1oѯ"m5MtT%-tߴG&:8P2{S5!۩\;1w6H`, 5rfl@ѱ~wFȃ#ϣ ڑĜ ^~{5،:xl;8, qՅQTuS,(6hqXKF`1ƾMpIZ옓+ۉcR=6TI(VR,4n8^?PLQy;(uETB3~c_NF $ J@b[[]C- ;/u uD ?WPbrAhJIBamb3;.Aoy(A;GqQ0 SXcj)̵'$xBŰJQʂCWʥ=pOUe(d=Js%%Sn3\('em/אTusQsR_KV&K'NøQW e+4$[%UMɖI*[ A{1n3]Sw >>*n /).)``An+ \Q'г`-6mj|@jR7`@?%xz)/@d(oc0遃β[Ժ`SA| $i0"_g +Ŕ1rnb\$#BߊVJ "rRt+K|;ϐ}+'|6,Ëq\? L{`;UMGP{ݤajy< UMRǚod ߱Գ0U${[.)yeTJwdGmIkwԞ@N7n-5۞&/'Z&&$YIrR UN~i6Uco]Tx {sz g/O=/ j x~Yf#WnsN㬂@+JG9ꁕ^C|5ʭr($%: c,wad8Ƹد>/VMN&Gg%Ek$)莋6dz2fQޓ'5'KIOӺL_YI;Ew ]RU'PDh| 97ϑ7|mK0DT&Jf'<9ǩLܐQ>UW*Z,X.]P' u2$eH9eeY/@*m`$J1$kG)Hވc!ã| [`KB5[}4 '[bshsqq&!)-͗uZ ;"dà%$`4p6 5`T/tm*`Th$2p[PY;4e\FGfQM@ְT(Ajܿ% HsZNU:uJ ݋PWbǠcԍ{ܐeh)=hoKv04t K}+[Z1Zu:~S;g|72=?tY.f o(~BKlR`'FHxT7=Ι ;IYN *SنYFC; q) ʮr^Gȓ c\9,q$OGWƝ Գ*)g@Ji!}*j>PXVrlVa^D܎ͼ"U o|'=rih&w9= ȷ L/6BJZBӫ ok֛}%+ aY}D(12Xʝ,gqp4>+`PBJa.lQěhc-JVF#` եsؽ:7>h&anb !q-4®nB]0O_ש\Khʕh#|uLT^L 5&:0<br`+܃ԓYLg)xobT+>I39ga5i%h$'HFkґأ!L֖G.#lZTUXt3 =pt{4{ ).bF=@87R5q X7M_rsENud= R3لн0pg~#IBa#L ֻ-H*ěܼ532Q?{|Ydeq܅_0}@O=Iصʂt3bO Mq@7P[,CSgD܉݌כL(ѰE(@_zMZ @0H͞-E܅˖-AHa}A?=(txq=?yy08J { #C(a, BF/k,"M?hZ|1KL?eGf_eZg jev%֓ACXXm#=́\h4o& /i CPd"Emءwj+›]OHG$Q㥔C YJ㬹ݝ=탄S33t/Dq_V5Ԙ$ھ\҅t/$)jipx63QL7s0("'F]3żO~1~ўK[k0nK^˷ {&!ӗe+r F]2Jw vP\q zƒ@qZ%D &M|#CS$uؼQ=qcL1,mCHw 7:Yt Q6܃UbQ&X'7)x,y3Kqx*~gڽl_>O8b%|]FbxD`sMgQ^<mͻ׌"C.2f7YRmvY y/\!I7z4'(Q䈻@s2fkgsb^%dw ?M8/ iTޥz6h0Qڃ]ƒf0@^iŁ4ܐ?] v#:qD!D ~"Ng h] 6 PBg`8FLXFL9E 8F w1bi{iN|upл#A=c UwFV;2ՠ̮!*ףr &^k ֬UWQտQ[_i^/jq^o%Xe'Ke_ӎu[cʃKRi TrY!r:I=.^OKMؕҾ j)(&X.lHF ) As$w3y*d|/H{ i?Ga$5(ɭlsWҲV RB.88L-!Ť̟ȎaSZDRəZD6Jbo6 9!k & ws+)2zC :udp gijLg;ID)36,NUѻ[u/HTuNVTjq]pr)ebvh+{)cqd?fջt0#|ʡ,0IJC9E0L"op)Uq;c ~$L@ :@c&IQ9(*t*?iLɩě`}/U7豙t*::\a>CL)vOض=y6ahjt5~w.`7r+L mµVPxEϏqsAGƽҼ”NTLԡqaV(܁) ; ,ǺPOVH:qGg.WE>x+BRԤԉo$@ϏUJ$`۟j,A?̘ʙ&c݄)!.P^P1 v`dTW4[cI :+|Nq@;:+!iG3_4(ԻՂgg]ιVI;avXekPƛPMi@)=> OXSOéxŚUBdp} .3;4pL\OLT )e/B+'cBa[Wr>O rG, 2mm'ᨋ8Ad4 /ĉދ ~b~8 l$# 9/wب:]3<ͻMy-FO51**lf1POl &R1 ;-UvB|wVk Pv܎oU]< 60 ߌeaztEYL5: wQaªɿ楲Q /_ wꀄ˵KԊhߩ{{ <^/:gqB:<SNڐ,UXQ7 1v[K>G{Yj[3,8 -~;,:{ͦ[N`{yLB` XWM]lftBov84If~ 68*]X=RO9- HBI _y!{M")0t E0y~ñ 9 AJ p!5.9M LClC3f7Kpli>Fex:6_Vq}xMo\R? -H H)j\8CoK_O2}dY>{V EI&A%v چqҁwHѫ:ia)V@{%=\ pE .r񕊤pb |OЧ(7p7gOD3Fܼ1~ R ~ܼ Ѽ?/wh\؅Ԓd,)|~xJQ>0^}4+(nJ[O$FOuY$t@{2ʭ۲ x_r:;|'Kf$;8;|q. c ?Ny8<8|)loUL0t\(:,PRI[ CCNl*DK@@.%o Fa[7Jս몒E,AR]hXTwkckyR{H uEem3[.0FQ*g1Yt Q~:x^9Ll5` bFI!I9 t *`7BP Pk c8l'&&۞^)p .%U`_ 6hnì+t) 3;HyQwN\ `oFT(A (ʑn a/]bP!'FX!1bt<`>A9j\vs soq@RO,2RIW_}gFg2(4He#^ sKqvls* -.NwR"0u(B5 yg$7![uJ_ JXah W03h^әXHaqMm ytɛ EMzNv2~De]J9D<{Er>sVb Ź|guTL0ߍ5k1zNNޡ Lu+:8AW$A}3p ȸP@-uiOx}I,d G_G\$RpDo8ցڂa^1tWr$  KdV#ɣfɣF%F7UN5D<ŦQO/M4ꞥ}@5XmT|Ө+O֛?A_08=C=Ya){~GCD yXtxx;@i6\|t$;-MQ?mnPorê v e W}Tr}AW,c-PTM!˙R&kx΃pدu<ԯuQ4#f犃S@rWD* e!#=~R@7d–;,;m$Hx q0ZstAg ӥ֕ ͓OJL{QŽgD`OCb'a9R4hw#_k&)%Y}Wȟƣ |E+wSB Pman3pB#)f p=NsxԺRNƜWRH-iNяId\'} sp8_V.p sh ܕvz}X8k÷;p$Ae3x@K.ŰKe5q*o8u(  D, b#[(Mqr%dЀcبe)h4^6 2gC %f"Oud=8X~VQSАZD)B2MqdD&B/ Hšt$yl=`7b;ʬh5oщI9(Н죋,@οŐu٥RvF"xGn7K#ͺ:.V1*mQo!@/>* Q7]2J/,/$Zo?C\o!;C\o á΋b2c-j@{l6ar;!YEh-? aA}CNvkw!s]Y&dhriPOidbk(F#}FRg1 < w~-w՚wzc @|3I7seu4KpVȕt.* Q4^W|QQoHm{-ؙg]ϳ ة_Z-:\)c77xqѪ!lͬ1pST X"? WsmX(0V+_B([R_6XʘY%'pfs\*t7uvy,:c B^߹l9ј7` 2Zήґu&n11[:"Ri.# t(-/!N\2EwNjYJ[ ~Nc14Yq)q،ғSװ7D#_3`Zux0j#&/[.QCuksk&KV2}EK['az'eQ1hܰZo =8̑>z h-YGX|C%έFq!Y*}k :kpR(*p܊0bWkع4tKr)FmsixkN\)q=!+q 4[ʙ3 JN3Y8+[蘮ӛNTG.驠:i')ݡ0 JDZ?5yK/Kqgx=Dmz y 9Hp"bk e kik~0ޑdY|> Q6p' {@q/!kL"b`|]w/2[/d~",$akȀ#[̌cE!ޔD "8ZIfj4CaMTQxkim5"kԵL;s7vlwƍB^h}ejT5 >Rei4fP]kq&n-%gi" ?s< (Qc hV,e81l'\ͅ]%M[m$jZTΣ݉F\#PVR($]#%v( ]ͽ'ubԲ|YP<(t$ăj_Qr>v%ORy(m S$à8bbe@HO3 -d8E%x0= /B d8BQI;A"f؁Wxu ˝y φ=A1+i[0Nd+$Bm/)RxJ.Zm!&L;0jgwq&鱞ArY->Mfg5?4Sm\"4?g#,vk4#3wCQλ (_{8N܌orQ==.$`BV<}C]1ze WZdl'W`*:ks 1Ӭ4 T:h<2@5wЙq M6wT 5wJ*CqAey{!)Va $כ6uik鋸LLlR o*qmU=s+ Sʠca@Zײygy 7_þ0JU93̀`1g* L_%j}Glh503x9M%¿Lw=Խ!}xIˊ Fb}Ssl1Xα&I="K$T4LZU Y3miu~{h\'sK7H8R w0A+G!1ݡP@S$Gt,)PR>iE) Uog4!;ѢyÚ e;Q =21/|Kw!cdD[h/=ޣgRSQ{`ytJ< r2'X32X=y(k=rx GNFz2xr#>) :6hul`:6Dl/]]u^-nO LBL& \6p)9ʦȮ6l`ben1ċ̖dv^!safN_Plyw=Hׂ(l1nΫ3ꟹs6 7 .k;6 VmE'Kgy2cTa˝0izk#^aOqLW coÔ{Ww4Rޱ?ǣzwnH1TGa]vH\nV'2VOG.%qm[YO|S؀s2uuf==AxouH bxH +4`yh3pO2uv)yv;GK\ӗW$>іJ>RHJ׵|5]ކB&\riVU $Pt޵h_,֓ߩC?IMs< w;5n'7[mLDK_{WZ- ir806*F +O9FN6ڗ2]2ΓPgo?Bq$аې*w$./׸ n|m*^Jw ǰ;QC6NN?޽Bb/P*_Đ-{ۊ:A E+h͂7))*hg;ɧ6~ RA^@E7zBN!α1&N  Ek/s, %깯3?s[78 +Sg?茱Rk%~pqŰa9 ;WW矡Y m&z~ ]ªr 5^{e/X0~oDauH3ͩ11AN~M? csx Z/dO 9Ir4E>Ɛ}|}:BFU6Dokܷ^|o-1rW<:rQ̙h9V| (C("}Px/P|7wJğx+bÍ} 2}ͻ3]>GJqћŔ'$Wg,s yF:.׫ *!3ʘYOi?sH*t$ ) W|eKMG /Ts"JT|Uk,@6r+Xa6YP5nJ 5ay^35NGCkb*WZE,ilcBx[{ZsEx< ĥCZ OYn[?E{b^;wrO1BJ5s(;7/pt_4VW@1uxM蚽*ō]8sY ^[,^Rit rrBp[c:ŮY}5j&:L sǹ>/arBŸٛ\[PU~Ş9-ʹ _E;gdf| \Az$Q `0cp,L7TuZ\kgwXu('ևKѴ^TXހb9^2^M,olo훆) ˡnQ<۬LFBΉ&w%1:+a6Tr{)PaQo΢c*;0Dw,z ԁd윖i76@iV7 f> ʳX,aXl^6Ώ!4Iuf ϟ]$(ln>kOS*?Vÿ1n>uvؗ p!v_"Gޗ|,R ʡ]~.mk2=rLr6ѿqv(ɠtM{=%u'agv 9Ү+]SۿIKO$Z<؇!MC-}y Ui}sƎz; =QdGD8hFaТx\U]%M*;-xBn_\FHl^'Z'p<(~W]6_Dw"1 3O'TVJ  cнXCF6aF)e>! /;~Rg*rY꛹᛽ oBWʮlۄcB {} :c >DMyN/S=̺C$`$=t YeMOD{H-g_[\L3 heKFg"~iWarV"/NI xk<,N(xSO H@7?jvfHLLbsG41[ǯ<*waȺu#6<Uqn 9D#ؠ9o7;Klk+M |g58| H|d0G{/8p^Wy/?=?~A40"t# 0 hOTJ4 U%waXY9PŨ>%ޝ5yN{sX6¬eE8̭`Pf"Vg3?xcoV\gʳE /r8ٛա'=:bONLj^]p0dOP3 TꝐ8)G H',n\[d"Cf`#QrO7P7V@f_v F;PD~ lq$"!TlU]0$`d8&##C[PRR_/oGmĮo7a].Jr&OEpΗ#;t${eg9wsɳ9 %6F3m,8p??no宵5^VMԕA&'qa0$m/3^H (+X l>-c~m:4@qP}x}6zߵ٬#l1K֛!#q վnpTmeW,$,ƜqF/ikFV5 /e±iF8W CŜOj+%Bҍإ% d ^˾:;sxtB4P=sr7ցnOE(a$,̲hc& P~Ȃ(.o,| r0;=MHe5#d/:$?uaaH*rnCyk a ?$)QBvo*o>4" e>iy s6xuh{>L['<=I|$laGNRc@:V$53`aqHuGT<7ϳ_,7&'wDa gD3H鏴&8ĴM:{{>z3o+peͯb2:-9Md[U :Lƙ5D`V3mEn}ii23ʣqlٲ-m2{$=غsɧ:z';Q4ǢYNܾѥ~rR -צKA*L;^,&J8ЦR7nc,x1KIB0?,R~=87z:+'_'ϫ HiGxy= Naa@Ftχeh~G>{s :aм 9jҹo7#d*) gv;RagC\a Ћ GLfx>QX7 f4j C]G lJᷧ( Q,?mCJCi(^[HVʽ$)!BxBaBx?@ OKem=r#ۏdKvȦr6.??j>#>|(Wr@yIAM3趰0xhWtSP@xvsb=HxTag[#xtM,w Z =qgBKm%3=^z'Oa3U3Uol^ K݉7@.&Qk#elxB, b;  `0G5$&x[a+R \?K&W',mֽIκVUљH1(b^MPeZ٨YU~5nTIPvY-tㅂC ϲitu%.X{i}) A&N3OfE@XGW3 =TVO{BmXI-_l%3@fW wB~ӍOChGT[:DŽ翚R-"Z)G{kbGzf!}[6!ҮxȪotOub`>lc-}st PLOņ5lhfŶ 1(\,.Nxnd Z.Fbڧ(x6;::Ԫ︁JQô_pQ َ[ބ7|8ҁfaW※ /"5 <@ |/8pG+cڧc[8b77~w{s3OCm-_Hq ꑏ1/^o}5dc~o$k* -dƊ\)ض<;{-)!Ჿsc'Lx*A((" V6h*y̓ࣁ=tzțnV@z'MR@GΐS3WoIAy"Q'}W.sψ!׶T=sԯ OR|:pPd!΢ w0 nxwަ%uj] O#ȿ}*%aX,}%3^f8OGB(ߦD+DS"/DyfYS"} dj^btLv"g_1^P'''0I̻ #oeTet\hr4=8Jކ KGz\K{&-ې)&w'>8C ?<5z& &WUQ;U%G{}*ͧ/ڔ)F-ߢ ˞6_(y!I-۠uк:n7b J{ l%+mvR'N+f8jP.AZ$B@fc>n<x݊*[<' Љx'vtQi\m<ſ#ڗ:l8 wd^ yQoԛhY°7?Ao~m?{޴u Ք3woux:d wh'CgO:h)vHMBږJƚzRX) NT6&ēF4Ȧ'F,A$Ӹ)Qv@_|7<3̓ޝyrI ٤b|o&C.6 ok)U>XA3 hNTbUlc"c즠b6` bIDA'{2ĎhÛO@..HbSG^w6.?ERS; Gt{g#<_~sy" H,w$"PORd)nZQߣyyP{a$n?V6{myG، `gODpw$V6ؑOxUFn~)J˳%?V+? X D ܙ`<\/,َ.v+]P$َH„RS ;jA8V^BV\/#d,K)ȖS}w7lI."tg;nP&)X;n|[#hF>d ^Ùrok*\Džkpzq* D l>mcxnY>CH'\~GoTP[VC;.qȁ0BT'2L\HJV(0 Ř$IڤV k͂˯5 es ~5 n&lSJ *i$u>}gL8_l;eSg+w@ȅ)w\ ;.\tĮl 8eCQrZW`ڋ9=q,mG܄!+PTJ&<]]~w_ȼC2ꫤ yOk3R-s``ۇ\Vב}ԭ]=mA<{}m=0x Lfo?Fe+^ Gw0%|Tݝ6dl{zaiAg)M+J+~.utU_n&Cs% WS-{1p$xBb 0}/K1 F6^[Z.X Ia̼xa/!پZx#Ȅ^`-~D|lr,19qrmUCKBhMAhэxۺ ǑoECzDt j]O8ڒGxm=?8`cV+ZޯFAt&G(:X<5a?{hQq@a2}Η't:tz΅ZnK ܊/MD!l`׸=I 6P6 =e!s=0FHy\CQ^]م0ۋ)6ʍi e{C!z֔G 6Ix?)8h_bP'a;閒a^xxXde u7w'++*q.9 ' $ BF Y%Q<`tXS*gsNG| w4/@?ޤg'k'u00眃zl3]Z`]]3a:KdhQMYKmj`l0(lH c+\ |V=č(ȡKyږA4Mwb6jAΜ#΄3A+| 4?:HK_DGpэa 3-&MP,<\{7vjcaD 'ӽ/CsmysLB <آ:9,17P؂őq, yP`՟G!LIQ]d,GY=x̤O0U'3;z5$}쓻q|](r+7>/mQ/E]!Z6g @GLy fQmD%fYPc/٪8%wb[>VupiC6Q4f=x2WJ\1|udIzcHH6@N"9L ;K/~ SQ{nWU9Qګ(f|1/|ֻ1V@Sx׬ө:Ӻ+b;(Nkz| =~SbT`'mc~>>Wʃ g`r|Il!Wp9V[~}NTėRy.`Bk6xcH2 ɝ$.+W p^waPt~m7?k rN)P]%NOA_eMRL?H%Zv&+ %@(y08@oij6MTb9 ͵h+ Vƭ/6݁Xa|~Z>´'Kt t*7ooa|÷Вe fJ߬nE N y\A!g֕&w G¬P#UMA湷hDž 9 l9&.I] 2f$ɞw >>e(V7oy(,I3†I_wRSc>tTrèsZ|ڝ1+ '/u}DK@kChAjwowFUk_f,J($oB7a3~YAw ;4؋0>0A.y@#ȕv!pmw) q*:N8Z*~'X%@޷^$K-u-% `71ёZO|}3ղᲲW܄#s.V'߫]!{;<}  pr);v5C וYd 4&ω[ =ht*Jf.~NC ^⟞G6=N[rDxi[ƊQ94(GMx.7b` t4mr2phrYf4gS9B?ȝl%r@Jg(~M8_fXX+w_a^_lU py//惄`dv9K6i@:J|*vA;/|vx<{# #, x:,e/xTtWiw(-9.VޣI͠0Q9LxTlm:,&Zl`3_WP+g@JauڬZFU)tto;JM^U,Ajp/100&mQ_Oī>+QɬQCRq?1*' ѶNgGQIVLA4K-=nI;9lQķmv&=O\Q$9 @|T ~eם*{w(Ls7B'O:^tKD-)fHyK{y⥜i!Y'.U*C}`3&\^)<烆0Ǎ[Kg@g7Q ؤsa:8fMm8! _I]9%·0A)'DX2%Ahb&l%B3;m;m?/z/BL\df9xrn ,PG@,M wi<B-nDIp 01 Q%tۯ'hrzEUlOi QaJ7!eM2VpU`Ma[.ìw 6ߩ*-O>Znz0-r*.!Zs0]+_-8򁖴ߠ5'Ђ3" =e(Z~ ZBh#˥cdY}gs#KM99!1YԴ Z@gMȉ y‰lybGd']rJ+ i*np_2T_7V2Ŷ0~21 PI.la(hPZ1tBU~PZCkږP? CillP mJ+P֑- w U/J4 a^E#竿i(hoJa(2/vÝ7O;^??U;]mIztjRlˈ,7biA(;V<6<]$cXuY6 b7Q)PdyO^q+ZVX*9tf>@EA%S"SϺPэx4dR@CrfC/.尸sZ$^Y! o܌%_øAIr%Y~W8m.מc.3vhK `_cUB0W#cr2X `&^`{ ߡt[ 4٥s3y,'1;wP^4-2ۄƱjv ; FޕGR.ض &3M^{/w^a S3V#`WOܠ8s+s)_geWRPځMˀgp:l9U^Ilo>Qu.\4~[*#.nFrŌل# b.89m"m'+K yX"vfJz !7#>_jxbMrn#ZG) 2HjfLP[+G^3Be =\_*|>5.(wLoTPzLv(³;lp@xAAQ& :e(8TJwVs/~]*lQgtm\I9?| 邽Xp1E:1"? fѾD3RNW5rԚv$h^H;k$^i=?*v+.rg=9#ѡa p2O7v*4Cd.e t&C%s$Zrg1 (I?ցf}.i/%"bma,I&7ث|):^:d΃ޅ(^(v+!{WwYE|vf"5/PC2:qn/sLB36s JC+na'm_)1<.)'ʏz|R=H`h4':GCCM=p3޲ħ sчz%uYvff'B솿7Rvfv*Hrf/L2$ 0{.jyO&:{N k?s"r[9 lbUr,zOָ}&M|TWBZPgbϻb_\X,r\e{&bxw!;)ē LTL5=%@2!塚2CRI1twwÞ>%/\YRq=+u;| Ӏf:tL+k|wC+bWՈ=Dqȝt?El |"c{%ldwGnr)eRJ˸C/PyQ@wY%y~S)Bnve@H:fJM'(EO;rHvT#7k˩Pp-]xK7<+0H T^i$\682LV͠1+`:7n GѬb>6պF^w +GC->8Cc\#l0:Ӿ@Kisx}$Xl܀ƏŢv0Ći(Svb0=tj9 [!-1S:#h$̺FFPg]u IcK$UφόLo!ve:+}nd`z&!dz#̀|N֍4`>>i(^߶xxf<# ϑ \^.[u\[ lQάZw~ɋ𓡿a=./wUx4#LcKq t|R ttoP>b%L Cu1ncR^Rcvk& `]0t~PvF<ژX0*,誰@9C)T5AK8`J/޾7;б8j.9mF[O=bz`}a+4ft4I=.qAwY6(L$ javx ~fɪZw?OQ^~]/a,-\@*EaK bOU5ohRtC :r7U͈oރ2^'#>j5DFv-hb*:'tV!w0oy K|#A  be]a[@N;Rv*r8ucS)ncĺbwLs,nT d<jB1 ^CL4I~SIځxٜ^1O{[:57ЩH9Q.w+fDRS`zb:TRjD<Xn?kt`tH PQ $s3d!xg$Rxߝ4Bd~"O>{eBZu*އ 3H9tkD01 t7n:؏&uM DfFB̈́dɷ$ e>7; 0l* tǃˁ\oF`WKus>`޵-~ӂSY08իl|v|rQne ,[ iҚxu-`ۢ(#S(7߆l`axݪhI-cs}!!pSbrRCFaCy`]oOTM̀U^= @Ź0c9_ OM-뺔K5! 'Fa ڑ7CNaC_"t WumJeQj-%?~5 *h](o+nF^M;7,d8w`E/yϺ}o'e oRqPFI27QeW/#lАy^ذM.+UHʐQHid@WGum :xۋ6b4` <ಞv.7=KX_)K!{Eaw!ڋEq;1Ί܍M%} G [Z}z]*m֒rC<^D$o t4k ٱDq]C(;Gm1jsQ=c !:˩T:̆Zw'yΝsCeSyK۸O["R3;}nG~!6\J4!*;6D(&8M#tI>("l(~-hIlp0]PX}Ov Hw(5"p>SH26-۹Sh6J;B"3ͧWew's hx9/{s.50 </W+@fXG*cn k-%ύYAEZH`QCT9Oq.EiL/&Eb# tt?-HM5BP0yPאH T`1W}ԻRsG|9#ô>;L>T:A9y$.=c IEVN Y^ h/^Qry̒k*yzEPyr#<>ۨt7b;1h1m :+qe`czŠ zVҳ}Iew-߀Jw%E Zwo z '|%'0ãa|z"i3G%o#es97LILѶFZgTWγ_vO8 i@HBhIJJFYUBP>]Ipb\ =xCpv#aGӴG=52]%$? MDRoU?,7>1ՠC.oJ+vQC_XWbXxKb*51a2't Sz6؛&݂RVNsCؐo {zOR-T3^׸c99bdϪT }xAHDBr0@r'Q'jUȶZ|Z_Kc`7Y(hl66(LwX6-&3K'ZNu8S! )^OBIK Ҽ Qr"=J^ ~eߌ Yם)Gm%;Qx^ܢ.C:yC99 %9N *qi7#f%3`w+`J-'4+U/l:~?m8Dq BS؃rS *x=ڕuδ:4mw+A҄~<ϑa Gbo3cu83hb=O.JYUf0e@8*BfhJ:֔ Dq䟧Jj{` n:(*TvH*i0o`XY–47?B5I1mz$6x8v;9]9=eb*eR/|,r)-vgރ-WaM?gDwFTH= ;>b3,fvxK_k;pH!hvg7gy<.iC`7:>οcYϛEa\p+'O耊:ާ}Ws ~ 0_ _UtCGY/OdNi(yB(FAUB8@Έ񽆑#{n t]N`ȝ^?bzR᧭t"xƽ6Xˏjyz,&5D Q)M9fuAaz+&5 rZ]J v@+ PWy"ư6F)o+ hËom?w\{gm[\>SbOPD,H Ǫq}a߆)EPAWojkZߣgRډ/ح<-S jq1UUʂظI{v;FH̉BD=;<;5MyݲBHSxm{/!8p>ytxw!^8" 7:3҄]Ml$jM`;Q`w;AYE|.5qlp}dAk/!d^#Dڬzm_0 ;ܚS/qaqFH=v.`L>/Z ]"6+ YvO P /0PnJmc}Ui{vKx,}mx,~m3x cG$Tm*nz*)c/`43<҄߷t+?cč%hhpH`e* GAZҊ>}~b(O"/v[/qo9 ~!Ux'A''IϮy]H x[Cc>=-1cE3vfڧo]ص~]3#,&v6oYWZliP}Pmk͛h@,Y*9A]<n29>yu `mA0XX;̑(=mjWM'r5+[>W/Uv0";VےQ^(-؜'8{^p*{@<j>$56G_aҮћ@2t:H鄍6=7%6CeXc0o͹d=mҥnKt?Eڗnyȗ(_ܯ5yF2vM_-IU(YGGxllTQ{ym*Kg :@<27&;K+C6ebw%g+J ߈ySE#}x'P֓,؃ Y&wB(M.Yh>z<#wP˂XӊCʌ g!:c]b]`wL92tԖe674 ūf3f2ub#w|g9X'/ ##_e}G]vFWgwWEOGd7$tx=B!tXC1^ĀwuxH&Z,֟ 6l-!5V:R\xG"gX!,?CU<={:!5dpY2G#C#2 l06̜NЃҧLg߅mC0>F |$ ~p_&D d@/FHWk2xצ8-ǢZoqfmU%۲kmm=~jJ|qGٗx Lf؁o5xHbf9y#7b F]0=O;vxo6E1 -苻a]bgO)W"'RAW,bCUP2GR -__!9 F-$Y9T.RiP^x$:@`<&]lkqS)BtdsڛZ}=<4*[g~e}'¯굯CM=Lb/(~5 SvcpQt1vm݊ٶ_4;ML&hncfJlV2|[ PJJž{_K?@fW#T(C{JѕbJJ.@n_5Z\ev~uOf|4nɣ{23|>fDhq myT!xg!KI'j$M| 1m/͋ʗN|0uo(哢S<2u ;48U$Ty8W:/g_7gigдq4MŭǴ:eg3&yL;S]izU k=DT}V҆ɵwI\ #{?vmҮ斔asL wD0u4ɚލZoT'rO,8PZv/kG\/<D5݅l8"w6uA,51x^898حlA6":P#o)"|3xm?ϊKnrvJJIy}¢SԧĭLlf&wBg[ԭNx?%*/feNG.. xGC3S/ju87gYZByوs#~7HN(fWMʳ((>Q]4S[szy׉ڊ=) nwuRWyhH NkP}xQ`N&Q:}n%s_ǛJ 7fU8~OMJ6З4BT*',+K1pg"߭|kqMp$|w7۷T4\M.̔L ϡ(T:]mGg,3cq Y$We%3&ݦ{*FRp*B/r̭[}O;| Ē I=u$$+LZ9k t˝&wN&&cBxĭ<71exG@a@JL;L;R^"nexf?٢01~3? +&nh$Ajݣe>/2)e {V͉5s09;ဉYy{TMօP:qǑw5O'X~ssȸ,#i|$EԨg_^'? +ؠ]Oº#xWw P0,܌~r[lHԇ~H)')E=h'w&#|m_0UUY_^oW>(MVB,eS2g^,ؑ*ȔsȻ8rVKJ~$%XB%_F,qc;b7֚XÌ)2!CB]Ԡc%e%TR+<,Vˆ%z+Z}TMnb={j^ 2V%fꦖ3O(dI:&'SS'm\Iq7]!) Eݛtfi ( st)VGѳ7 X +H<I[QROxw+V^YzMqH'nBp`>YI} l.f?\>hYp,p,f|e2%'[(K3Km &bJgs_ED| 0m+=^gf " YQw="Hލ(N(}_).K~KL:v/⟔Ȓ/\$sat։v#^rw%e0I v}KK0^hj ƿZX K2QjZGs#j-jWqkE/nISuοȐx2Y:ܾIֽ\ RElvU:])kʽWP,?yJUGLh>bTƁ gX@Љ&xC~'H v/)囱c)/MhG# v@@).H>KЕUۍH*<1JE LvBR#*P{\~Tm.埱VJSCۡL3t퀁tļoswx[G'p;yASTڐuٖtkwW#U_@xg/_CCvH(:q KJ+y^w[ 7*cG.b8P Vw료l!x4Ӎ%lRˣ6K*h|^L7*GNb5E7ԵXW߯H^W T<,<;ו|KĒד| S1Q=CjTРL*0^̮OPTeFm_D=-̼P6 Go|ol mPEIf9Cir(?9Pڌ (&}LgC K; n2Yp M^42 Ee7*h4leyk` ( hWjّ9*PB ;`=B#P'o@WJ2ɸw4_|A$yxoIrʖK<Ֆ=Dajxxl)?bUϗ?]~?6!p5O5q&Ɲҝi )wuv OG\~4Q=ygV|UZQq#ޫH ?D?تX4kXt{ ~/!'MYAH E7HG s'P/c+ă;^`^q2JFS⟢q%\9j{H6V!TZ-2[b%B.3<_M›ޖQ:O(9ɾMLm{~1~2/*_#}~r"oQECs[Zp-$m$M"Xo$7;g$#\;v>Xy?1צGq xG36{ p1TÈ @%B j?܌q޸OmW-09sd_p@ 3BQ<D@㬪szKD&,+_0Yp>9}Q݊C*gUն4wE,,^D4ٸFnIp yþM#IZj4oJ[36GpYcF$at?/y6ySmGtżt .lm:y=ozf d?m REV`JTr;/Q_̙ʥ`1٩:hzzn C n r[Ї¥()UR~,DK ߪ</na)ao91Z 72oZFJgn۸/ら67cf$MEk[sZ(q:j˪U;ekM쭬TZmStb9͖z%6jćYc%-d +w`AI06tI ":?WOysBHO[aP= 0ب1OR)A`tP>t'W`Tb2ѵ K)K:J 7o| Z,-;Mt !oBEhwmڸE29wZ&qUR #%cYj3OΗvٚUtMuICc>ރy'pH4ZRٚ$]nhlͰ *45xIlxOf6Jh^q9KT-K`'dX+[]!`n 9z:`wM9j|BA;2 ~ɱZa˶rOz)~I[)S`Z<>iQ$[ikQdB^~Щ)g!P) _+-ULKb؛pC&d6WXy~foe~w1#)EǓ̐+Pp=$p=cH(^|l4˻^՚'cM+B`tYx֟OҕK\&>ݾ8 ^y;ޥ#KK@:W:/q;  zwQǬsAlK.F>wށ7Q3? V_T1H>cA4mlց8XjӚE?LBRw2jugޞԡ\,CjGL'phNO`ƢLe;p4xt$Sj" tvrld}PD_!0:# P\Ҁa` LgLJĢ{ΙQ(eY Yvώ Z^$@yH޿_9s;;w=Sp-BlQffw* iKop(]JP,<ŋmjԟ_lPEJ=jo J!,񘧈n3'V}х1QD RQY(T܈s6#yhCڵVg9h5j5A=R>e`U\j„b' Ϯ0t"^ǣ{Ô1fGy=^4!~Wshr qtuH#.lmFBD'GCr+~yaj4eZ i#a\\PߧE,L']dPaHQKDis4J(${]ʂrrV}Ku唀\z*\Xo߽::8R%z]KRo79n'+Qvs2P[)qf[D:V.zr[']r JI6c 9-D1)0yY*]q! "b_'~_RLx(BAuvGJ5ћ_"H%,apE=0p|X^;ijjvנ䮡}_qdi>Y/7nQnܯ","xϐOh 0~ ^wx')wq,JhlQ&!;xM 'эs&dҠp>ɯza_وi8&H-7ko&7R0J~ Ynhi^eSݥ /jOm cOC.ޥ]rSVMK=RJ.+zfВIS9'^pw{;{hG?b6[9q΅pK?q8mk4Edo\1#YA)D#Fڈqp]Aܾb!"4wJjs(`";xO\Gr 2TŨw,6Ykhe䵴m)~e3)u1rͷǘ睋A/ ] $MJ8}"'bpŋ2ruM1Xo/[s_E?$dD"1`PtF'ڃ3܀EMfXD24+Oq򮨈"IXC@VEqX–$_s{z:w~? 5NWsT}Nl=nv [bX W 5 +XMºƒ-!U-_XYm]2J>p06va)ndf]ií*ipՕSXΝBv'zcU܇VJױ*٨P^獥3e4\fYDérh,vߙ,n4Ns͢\0jhOͥqW)Yxvx9G]O*3F]|K{~!Wp"/-sבSDo_!)M/^V傂t@Vs>EB"a=HbQtɖ~f-;682(-D"IQY}?[ ~~I}җq;/2V^{<7ۓ/Tx +Fᖭ kϯE[ݰֵW@sw%/Or57X\>c-| l\>뫯R&C7DPW_:ݠ8,2la8B GiJ@<DV;A΂2Og`^*b؅u 2׌ 6<.;:^/e_k 7? Œ 6 z[7lkL2UْKNěK@bɱl;ͥf퐰d-8y%<ҼP6OUEP\'|7%mEɢy':KE^$C,aW\y"B4:7f(aHsq>\vEtJL:sq>Cd7ܓrҳAĬI)950c#GSr8ck{ߴ@p{q2j\zUIyN/_gs| "&dy۴AT'I% V{NFO?}W>qI^zٹ,ϽN~3q:{@ɛDi:'ee(F닰G3QlDSupHTh3BҼ寑yw!ik¥m㇦~O ݥwڡӧ '{E|#ԝRB /ݶД_v^QQQeEO6/]S& E 8&i3+>(8/s1O<bRl8&Aɹa>/%tOX/R rND{T|Y6^e:0վ {v;%@Rf=ՋJh(x%OXM؞i*6jc@hlc_il^n\uQ-~V)Z>WwɹWDGo~僥p?Wざ2cPKQ} Vz-(Ddž}fx^îolm pB /ɑvM l.+z.΢b/~vSY[1mPd'ҕޭP}.V{bY|R;Sж7jn50Ga~[D9rQp[_|th+{h'j0W:rE+?g AZcS*חf'1VInhMM72ׂpWj:Ik ,qF8{Š գ1= !%0}>Py=*>wa%{KpyM G8#6`rmXw΁!  F3ye5uFC[xbj8Y3{x*062vaBdjg,ѓDqˁzC^glRׅ&ɼk]uW n6PBQtZ#>STIZ簔M>IEݙ0[4MV6Py:,:E|-2qx"LÕƕ!W:cD@jaB=>Q) &B&?D,VyV0+L<Ȋ௿M&{븥qKﭻݪ,n^V%chfqנog/::Z`̝`MKWfWTO`U)N{_ǍңX٠G}94 -ȓPSV& 3r{6x?ƍb#ml0Pը_`2tUvYרaSO_٥\γ>T[̍bi[N{ru{|n{JfB~}Io6w:Mwg:}SNl9}xy`]l0:)ENi9'et>cK,mx]5\1AW}=O1^{!+f x) o]') ^ɳ ^D>ّe*<Yn";uf["8w4bz.?^jܵJ~BXߚ&.x,is8j`_8Xp!#'F̎J഻ 9δJg(JZ5PsgiN *`0owvDzb}Ntkɹ,X`̢3n@.blRK%{%>qwq@ԶBCԯ41iBsAp¯l/(3苓JT! §8$5BX!ѕu.Ê`\;Z.N+Ry+oyw8HF},8 _NOo鉋OTgN"UU!M65M]?Ԕ OED *YɕH2 e8t X-`;@טՋcIs@k ,S5BnU쭕AskAsV iNwtΙMsBsUze9 viBד@{.0747%bmi/t,7]z; z+j~>P7Uзqm:/FзK_=r 3˲p&ˇX<౱e^~w34n:lB@1-tˢ&a'0:cHvhv?huӎÖDy PӧUHa amnMkܹ-ә8i^^i]w/MڒA=}m_l"eQ;8S@mݎGUVt;/ !w7c7k0=_[~Sˋc -4BEBp_*_~lP~ [Ԁ̈́.TlֲV@V4ˁݡڃAlya*z`\~} ČcN'D**hyVr*I:+-[!Ιv pOLFT*Yط J@^%S7 4P3 ܎$W \}0 w7-iߙ ~x2au{~vIig+ɂxҶ"D6:v,]"zPFO!qPayp7"/T,T!x% I%ds(5"~^d@;~Qi\\&҅;#H Î ־x.z[n:7QmJE1Q~4asHר.7DIHbZ"{#R_ ujlʟz+uVv[Cd\F[ÕVWyk|u z\y/<W\ն}?W-H8A+[q%e W^N' G-ū+Ã\y=v[)r mg>!:ʙ(tWN@5\-u-"vWΎVpx"k+^4H7#OG"DG"D616zq⏑ez"n$Ea i(?ʇǁ˿5f{X 1FUš笁W hO_R AD^X6,vcFL{ 2kz1DyGL{T)E]1mpQ~wz.g׫ ,V[MD ygT{%{ɽ5|<[V{C.ur>/\(˟rxKA8H;;ih(ʖekrwRU@[&;SR`^I 32ܽOh\(R:CY阊Uv+O ͈FC`ecS2j8lU(Y!9|r+w/Ʊ7n}!Aw!WxI!znMNpWJxOӚN{ ͇_|ϞMpng4 ·sҝ9a0:9/}vD7U)Jr ~kWЗxmro\ZjڝaI##[@Xi鈦%Œ+vAIR!BZ=$ο$Lm /FKx:͙>ÎUV ;n<@C*vĜĴlo&>`K41T>;kai86plTD;Ϧgf?#ۀH_> x>9𼷭 ꕮ[1<H0R T Almh{CϷT0p@bzk%ri8P T[ ۩m %4Je+w+{Uy~.C CK}/ o#Jјp9Ә=SӘNcS4HS XyWRa 2~,4DV[/F0Mz FR\4聽"k-Ƃ)-V ξE v€rP g4-%ŨDlKK$*'7V$+C~9H \/Et֟`,8`qee\Vّ~ul3P=D~Cuo-f3biZS%=`"E!S%|CAQ܉sHlyKoD Ҧ8 fC9CH,WH?_k}{߃XvS_%m×/ ˿[&WtvJR[m-na,FD(瞥Rq"_U "3N̢\j/,QܼNiQOs4_Kɥ@aoa ZyFyL/2Yl0xnYkZ٥N`Yz\#ߺ5 pn/5QZx6wRc)k ,}-OCNi)|qidgȡH+k/)Q)nfOZ3g ʸ\)yuC$wciy# UgDKs \t?+6^ƝoqGSi ڞVho-pdA5,ܩ.o ;yt%oh0:|XoBo{xԅ++[OͿoxK=KA\nyDDM}7WCcRrH 9)e  wIFiZ-t>:-n_`/a7L twP.\P1DHݔmDRh#pQ1 9|Y`X3>賨s#Q?JID}~)_|ω0H++.h7bqWW#8_DT,() sm5WzݤD{|Mn[{s(teitC%ۄnH_F3Eܭ;i!}^Q e]Qͼژg6ܹ  uAw2ixQe#]<ݔ :W]凞+w3\k$PMm3=׶t޵{U!P&t%x$x R'0R1 9i awI_/Fv DN.7_l_ݞ&إ$?8bS%pˍ~&q}KM*~`[>s+^O۝ N#F!@d֣44zi7qُ~ذUX~0kBP%!ZLOX{_bފRTye. r5DPۑ! SvEn;,v.@L䖢qHr ˒a,oTBQ9J4`+ZPpNjv;<2g5"-^Oo\ Bf ŵ`yV]k?R!Ib8P4Wdofmk& G:]NsMXm-biJJC8-]%̥Ni[oIjePct tl۾:NÕVg3+h YSJXڝcQ5嚆|&q\saPv`6g,* ;F96jP.\!"rLȇ >yYE68)w&-_ɰsO\!['Y+gӸ$B b%78W*@yvtQіraP-NK}Sp{+E ݴ |qX65)eۃwag=mhB˞9ۤ7|*E"ޛg$rB9<ڏ*׾U7E>;5fk/ b=j"[WS'VO.~̮N{@u-mq;;?~Q-]vY ]7#)g]1sAޣD{!~6 /MҗKT}n6ޢso[ٖrz3Vۜ7i*Mrs_\<%AAܙW+y>wr3ʽ[˝ϽrZ,}<s\b8B h upư1ÛgR-X06CT`e>a!Cdpi 0Dc00"4׉!Sco`N Y10b]c`S[2JPg(dd1vC3bSc70,"Ɛh`H 6!0R F!G Cק/8wli,{K覕;n(Ji|j[9dB K+e4 ct? yh}Tc^ߑtBCn`z*^/Xl`XT^委vG>o1 S/Pztp`SV|AI;'e ]x!oRQ_p=IiIPsI^m(<` '&L;l3L48RbpaDiO40<2`ړ &L{DiO704L _ AҚ<8`ڭ&Lh`xsfN01502`:M0) }axig`FCߘu/60z axi_j`C ׇ HtmC ]Cr1Tpu.1k9t(9?9}}J$7)#%RًHga5&QBdaQb$JL8v'%2٭PbKK4v c&J.48iQ0vrm48Rl gb) [JiR6Ұ)=NipPHJ/œY) @ip&X*o`](SCipXO](QFdaeƣQR0l( #${0(Eqͣ4 qMi8JFS(Ci0LJ zSDiBX'J(( ks9kT] ڥ oy7DmcM-q6iSn_YjFyC mprn'-^;S _b#](Zqv!/QX٤`} 8eayPs6i?{18\Ow3 T. 3#?"ցr*X11, nn!m!t?/>m{F;?d^ x.8RBk䐋=?R,{ =lj\9v8DjhCuiõaΏlEj~zxۊ7F-AZ0-]ߵu3?t:lF1AH.tmHЀʒ ,ʒ؉\[7輙Qgp;,.\nsU(IwΩ- 2_n-N:TթE~X"RH>bz/E4L@wmcLT<˓Gtg6!]?rO>J}hS-wUĝGĝ3 䂠gd`EbUۈw>~燾 @|x Ҙ~o&^N|f/~ׅ^x8/5Ma'K&Cĭ/?ۇtD8Sq6J_f9sV~9}nu}}t\x+]|qGˌvP_꾜%ZyH} $Q7ݛfmVz:. ;hil{d%^2:}YQ5=}ueUFNc1ED9/ŗ:lѮz5kVpJ퓣18=O  hSsA?hb@(Vs6wv$ۀŷgoL˴\yjk%w1zʂ17/2`]Za^B@}EȦ%7|la}` yW# 3#0  z1TL:LE=2Mg5.?Ep\~Z~&'roUi>ܞ@=2鍹[l[ %02r,NRdXkZGNR@{u^D*Ӻ(r@R^"Y{m~ƞ պ`vKQ4NY#}̢. oEbjڛ|:5Op=9֐G8Hη]Brû@Guvo KND45~$TbXw#܃Q<ĞnanrfuK8uGg[zpM*ߋy0&(k/2fxlGkNYYh8OѸf Oc9}a G$Oܪմ0`1fcBN@*fގ,ue 6|K:J*"GwVy>:hT+1*EF_g{‹ hO0b*R i峌twH,5À@3/w6Vlcg^j9Z*,_S]5|ǍrtK6ٻs!(^p}o/x@AS4`(XBP?r8+A'Kq0fQ*^ e>vi!%jQ`(>:_⿔/LÁ ]XFH {=.BHB]5dή?d¾Qޤ[0Fn>LE,v' ~xtb`GN^:b%%kOA(7pag.ߋ> VGu>3MayY]>F68ڶqv}n0'&Bs}Dƾm~\CvHJçcKWb.O,;'Gв -{m riܘ1>P"/@I65S58q^n)`\5ÚW֜֜rMkN4!|[kΒpkΨZn͹[k/^Xsw۴'? rGk6̋R~2rK{4Ii%m%UИs1gÍ9Vfڀ1߻1w7,c| 31>>i ֯*-g C>BS\S pYd1?ֺh-F/]r;FjzGfxwK-jp-j~寯1W ~YWY6X/HDAk", )U16J^ԛ3U,|W> 5UM-m +P~twtGF].E( pzºtҝḫ+޿Fg7gLw †#-0o7ஓ;Gk5, "O6ejj E&Ѧ~02obeTez[xy2]*Weׁݾ/ !)/ Lymi嫒 h;`s[$Wn,_rsxS@_ufYiƜvD\tp#&4ֺ,e:=f9#6e|hݏE6rhnvd yOFtGom-AOwm5>R Zn݇Q[xzvl{ \>C|0vZX:O tw_CvC^n\%Ν'4`&[Pu-& 직>OkK~YBh> /!^~J%b El:SԚnzkǑ6:{ZgAW*=C:TO'?aʆa@%2eF;%TWڜ[_toLʕֶfkvddO+Z ̲qY_ T=}1ua#lV|/Df P܆0ty8PV8xWWi;^TQՙ3~zy!uAP2sGֿ K=Ǒ [>2GTЙ]Qnۊ֏٬M*xMWJ*KB7KzcqѬYܮb'Nۣ$9:ۍ:;3pImԀ焆{dI6/,kkq;'rL9uXI[9e3E]\]#$aCM]̰*8ƞVvqLZvѲ9Rqr/).|3û+?@ uPMF7Ru/lræO"ocF0LJ8bXׇ^`,_?9q>)Tia\W2$#Q "CJ1BE2y2}׺%,} ?֤bm7%Zs f@4 Y&`ә؉eㄭG!a%53o*Iٷ2_P[}8櫺EPc`-e V3V1Ӯ vM< (hW@W6cbN*ogTr錄fbCn&!_K Wj4[XM>W$y+Kd 6Ƃw$:)kS[.Lo,r3%ᄷa8pH"Nh?i_GA򦨶Eȩ9hCX,4kV[ay|n=iAL" oǀqF,^onE5(] @hahYeud|fvq[v[2>-r+(cS ㍝QMa=ߏMlf<*ʍaa]=h2^0 h[MB9WhۘqK->B=f(\ N]aFl&6 bisB5$"lǁJ? ob1a_6@޶eF/Пb _;4eZ#O3:&ʖ!.עCGƦ]kX2vuYS{-cxf'r1j@iC{t|*#[ -.%SmGE{Rk2DM o#zMQ ;4B>Û*@B*;ѴE`}qbѕ/Wc]RZ}L[2V?gժ{"ANmI]Zu8(0oÀ6_Xph>OC5_7>R6l&;9/q~r۬Qܪ\"SKhݐ"~Ċސ%g kύ=@4郮 ʶ%0Lr%5#Qt;V,k)O 9`Mʇ^+12e]?໕:qMn'(m7lKLuloخo|]gܛ\ ü}RRqۣ̓X$8G+/>MMRdObQ 3x@Yv=°];QׂEVmô6M\TL+NGTN_V/28\YhNV̮AOݯy6d`:#Ձd-lVnU/3o]JQ [&hU^~|"3pew~Iy!y4^W<.̮vzm-e:$1Thߥ+0y|w;;Ytg*pj  yK~kᇒ͝t*ږK~gGlM}AۧkyeBEW|VͭzV3)*\t$Q)Չ[S#"Ɲ`E4M̴gubŶ1}A;Um Tya͜G|C)AT0PkfD_OzVMt^ťP Isn ,s Wg)n77).>ʛ~W2<D>AuI{إcYƴ&aGh |cvߩz HgDv}MFE!b4~Vޭ@'ɇjh#μ_f Wy/Fϋu5e:]1mR%N}s|o"H F*[p"ځȹ曭#$:Ҕ9un{?.N "?mTX'e] z*B'yRW Q_θ*9q,VM0WtF)f09bBŻd]%j#V^We'ZʼJQJ)cqU`޶^}x&RCZcPAALJp)׼mX[3Rښ[ : e:O8O@amF$gNJyF\E,殨[0&C?"z˶*H KK8 ʡ“b 0]r8(@5ՃE4)V;'몶ZAbѹ@n]39 kow&etR$5wc#j6wջ͠ mmO{?G)'.A,uA!Ik.:ɽ`~7 $5wi3QjƠOj;jkRy(ۘ%<6o$$EPI:>)0B[%~kv0߱tXig‘ߔL?̨k1/1~cDl%F0O׾M0o_c*8Jٌ<Wwo/ue<-)Y (,av3_"}yEc 8Bو\ z 3h{ :XZ@A%l+GF# i+m= D2H}0N1;A8TCe;Ouv&Ñ8 ;2 _agޙ*Q5"Q  \ vzB@ 5=؊46 o'h ӄDվ%0$Xǩ&R r5d1ՋgDzL:z66m&ͤ+١ͤf4ͤAͤ]NIz3@ͤb16΂ؒ7T/8W$ͣ|3J:fRUh3ivfRuOI6r3fEn$ cLfRM$Cw7TM;JՁjQbێRbKBM7[Hy9n Dn]<:6kTt#3:X *5VC 5yݍa8utPkiRj-Kӊ]3Y3nf1o8 Tl br~Q0{kg贙 Hj8۱} :>IvZzmsc: DwHbscfw_iǙL8$82` G;$M4{pDLFM8t*qgy\`2rcd"-ctsuQqQꎾ,ºJsvlo㖬/|X4q DW|ԢM>Pcǝ-U6 K=Q<> #@ 7O1Wt"o50 DpGOM(,G\}ߕapUP 0_+%+6*eX % ^kFkqh@ʐ !֟d‘]$ @AA; P:P|s! V]6t{((Np>edJy$$&)>潀#B]odq"z#Vv| ʕjj gDc5<6"/rX2$߅v @HyDϺ"G})JZaG9mphhXR;Dgb;Vs]:`@}pqOG@2Kw$'䈁6|Ԣ@OY-R!t6|^%v y4\-a͠هͮ{= dV^P5ˎ-Qq5;dbPΖmU@%yluu@8 j08 0>a^eàA"WF~6Dq-(r(i<ǩȿ*D!/[?)D7u-AXԕfP)*6AU[AM㔏\gC¼Sef. >ER喾œrӄsu)I0{}p:{fg218cn1Λ-"Hwu*9eZR$xf49j0Pt 2"M4t t%{̈[NJǣmV>M/ 5:,7 > ¸ :&rBF&)Fd 9ڬz 5:НͲx<VwiR%5hD6/`!@LKp#URљmQ&|6mG QIJɓbMT&ٲb!A@&*_e6Gg޶"%Z$dPߴL>?oPsFs _$[+'Ӥce>mswFeiKagZe 4gs saD&BjP @dk|U]D5-.mq1 -54x~oV=Y[bAL+0ޔ屸oY?,xNޢSYtq ˝a5BU"MVtwtTvҒ]:(c}?hPyV7h-2#1^oQ/0=ipj@#[Y*A63@:¾G#FKX7eO|e=-&?r5ң97@nsմne1=VI +,IT*w { F k BNAcKЉkKˬ5ܗI <* Kklw\.B-PGO̜V` Q û{6ΐDa=ax0ʞY,|%}3杭(R늳@%~F㯳/ &.-{y ЄϤ5ʟC,ڬ9݄0UFMoP`z LisYu˜BWC"j2=Y ۉ$rB2 jpFA#BY^O18C0yJ X|DBl<@a i[.x<`uj@7b?:Q&tQha'N"zp<`-@j<Vs`ѓ@;cStҺ'rY~qw2*=-#KT2LOůŝV6S*мSV<~hO6~pSBĄU'&ݝr!Tsrճ:/8z5VS@[cj]0%y8(XbmUGF43G/>g ||wVi^{1ƞ8}Ąx &arS E제@Ϯ,!<+z\zsm>̖y[J݅qC]I~}:pD|~T[S:9g&{4T&V9Ƌ:Č敠"|wy|\ \ڐ |Dz'@VNJ= ep,Os+ 6DOhܜU0x`bڝSVן+FCpEb\' .uPn37Fo ڎI0koBl <)"&ܒڤ0Hʨ'CoƗruäA7>,˯j4 Z R~ﵦV,ً cq8Nl M О1Kxh6lmzkyڦy E&xslhqyDyХel̡_E3Dܡ,fܩ_g>S8d>gj&ԟs񡌤zR}Zg78 0`/^J[REmDz7ht^CO(DzO{d.'y`~/ypϊه7'@Ž pmbj cLcnZh&T[zl5͌x_~){*L35joNZp96TF)M.9k ᮜ8ԗl6?geR* [M޿ƪF N>Ƒ 9hY y>1Ń^U;_NmMhs|+ݑ߉`Cpi9Lè{6J!~ 4Z+.v ;z OJ6@:&9LVwtJ B Yjj`wfO C [mN K(엄:ޚլӅ4eNao {{ڙؔ07)@H]htRX/ `x)UY\.5k߄mbmɣ. gqN `8ቀa1L>.A0ᩐbw R R+z7%rvŽVКK4%j4ƙղzv%*QQQ4 rG ?yt_\~Ak5"d(݈cl@&/2bHN@a |9J{1֭g7BL LI? L0{}3 !1P1aq3+f<կ4 xOz5$` FM\Pc 0(w1Ѱ'Y%cqBUae6LPX̥LuWkE5ątie5Т[b;~ :SbIMJv&b24 Z\+7!Mƅt4ei7LPd3U%UV<.'px1żH\3k>>Sh )1И.MAz.!)ߊU煯=%r{a<[k5xJ~-=Y咙KuJ7]N [c 'nnmcL//*ܐFnLn;:[pNnfuGnbV44#^7!u7GrQ2P.`0:ŭմ<qʕ~o:*9(z\?aƔ~51cA[,`q2U1?TqFrz86H{8=^G_>c_F0zI}>qP8=*.H0U "ce."@rPǴa7(uG-;H5ͨD5zwC5sz(ޜRw7:w:=TV^Ps+sZ0J-3uKgpJ5܉RcoPHdؙn4q^(ήd*̮4$(*U=ZZؐv}՜/ V BUPV[uEZR5VQ8i&G)Z PHi~F |ꍟ14͇\>y*mK#L{ӡu~q'ͥw/hg?NBuLS8'sti4w\FzBƽ nĶT[v7u[̃q^ql mH^᥇v[%x֞ղ0@*FtɿȲ@Ua'_?W/yz qg4(._FQ`@nؘ༸eKtdbhQj^XA6:_Ew_n\SSu! x"۹ s;}80l2 Vlڀb=T/'`9=b wU`- tWCs+hW^\HQ)~bST֯_W,շnjKU,<7:L|$]z\Nbj+`+3/UЎ\8;¿y*=P䐅L@'/ޯ1%qRb^raU`d @ {$[\\| P)"If;5a+F}Y2Ǽaǵ}aUkAI*? 7-Sqmi4NK8R u7{DH@@ޣW cfBqw5, iSo/i: X Y/CN@ ywDMO=v^Xy/%5@js|L=!Bk Poe [!X?QAA8.nEy ْ tRce??T/^$mL~3=S&? ߟP&k=&yK 'nP?1R%e&Pз 8B0YB6gg2_խ;y'@JiD_n $`ĻҪ}1acE?AƯY^N=߳ {N~o}=M=/$|3cOXzJq72<$Fn)8\If8cG" g~k0I_lTPXEŸRܽ臿U?葤=!ӽ=za ZUȋ.+ x0ux3τLzw]0xj o2\R ˌO6ueL>ANS |cO 2aNII/ 2`"9![+T*8>dataVh++ֈ~}7 ,TًDiy@d8LjBfiV™&"%zիh@ t9h[ۨ쪵3q-# [`B 7oؼT^v*븤jdtlʄ"qr-%hgjLPr'4 yN`" {,3橡=sc_O7aװc f1cdT-LN 8b`i`)` /^";,?{,9 ט (Ȏ! z ;<|ap3)|0Od\D~:e)}6ySaTNapYŬPl w2* ;cqψ>?kXp}mMc<=c+>UxmR3-\Mm?UVd75Yb2VTnZ+ {6؃3uwZM4W޼h<X+ۧ&#sc'װ7+k=2V㻇n1;Kt4:b6fďhl…a7w]8f{!&</'@ƜSl 'm.փz"D6;QUPJ]1,ޘxaj##,.=3^Lq,eF?q(zK F1sfK=,S PoULF3tr?0ygQ-vlOwEMpm'KXb~oieRJYzO)ae"{0*w)Jm[npKM$7[!I*隭 ͒@=hqG$?ul58_)p\d aAx%Jy_Ədʣ!_zh ׎\qzI'GĠ7=`M2OIpg}fD@Pux&8Hƍ1|1x/"{|HIPG%˓688VGҲ$|$7!}M+<fFϜ7|pqp='oK[KYCBzq'3C>&t@K78nrJr$ D]kMm}?XSXSYg>Nx1chyI?tì+Aj76gYU@|`x¡ M#0uےTP ~-G@}V5~ =LG@޼L'w1%hEPm c1ddX !:|yy yƃi>4- 7#;/?1sϠ:t$@`DŽܟD;Ob7̃lcq-uh,g'!Y߇z3-}DEmGQy @bzS? j|'洸M= zS>d tBlOWQ]@}FdLn-arO CďYh+=0M׶hU&7(2j $q/L r~# L} {VYu 'gχ^pvw B;q N}Aۻn r LmH 8L`7W>Y pD#Gdu q٦A@ yFf!NVJ 29jъx6O~1[9cah&fႋ!"ԋ⡋nӆOgΕt$aI_ʦpޥ'È<+y!#~.], Ē6,/?Mޤ8lL`uDF<yNI ث'vUbëFĎb]gĸϊρD.1=ߌ ۸͙!e7h0[(!^d!<ɲ3`Z<zb,!} ]]]Rc(~Vߥa6}F, r }EIm 8(BЖ? hn@z#Ns܄'r.q2$*һba-݉LtHT?*ƠL[ M<#,YjW r^k^S!*tInCÞJ԰NdZڸ0cف,F#8,Ҵz-WݼWr':Vy'ILj]RU8\9҅ tf*o^lkvi~dlB XB?p3 s v2N#5Rq~rlI.!,lLY>F?ctK`1 0D{.h X1e@+a….⒏pyق!K Lg"GnՓSgRvuiڻ oq@Jx*vSgR'0!"qJoN\R ܉|7`%3i`.wJ V"|aTk5p)J$qr|aL׍inU\U~XS@RMԅlLۥ:sFJP襙2rgs\#i@nh(CT8كvА#Ah0-[Fh̏h x&7riL(" գBF\cLEpa<|mfbVef?N"nhvtG#+\ 4n:_;~BۤpϠy_}l2X4'ꋖ5ю?V,ƒObϚ/ ;lH@=| aԍ; p5.Sl6{TDԙdI-*TWO '"О?ݞmlϭ==̂"ڒumi2!l72 c1@Y|oWHE<У{6P5W 잹ltX(G6…ҲQ(DCkqm@'Y9}[6 [Dsv;H]Hbv4sF=*ngLVLgXXX w- %kOй_c:g=׹=gtn#I  / 1tWZ{^m.ˑ [luZG-6'`G^-g(? P0pwA}RbQ˭Gi?4?'{Hf"}Dz{.1tF}"to ׼gä4ybnZvS mtY|*Ml{p`2*o8v< Eu\+V14 27(Ne.- ~3׸[)."AC K\K ^ EQB V ÿ1[_4Ƨi--m @"pO>?y({ $?_$Vn6=ݬ\t:EsG ~-}s onu/;_ ?m3n/\=pa@&銊`eo"}k\G1 vu;): ls@c,󂍱̻q!֙c1_O1 순*5 ՉPSnB6cIQ716yΞ䫳dYM2S, Ma0'䷟/Eu-el"WgD7V,u7 vC;hlw,{rmM[m,nv*xwL>¢-f Hü<[צG#y"U!DϽO [z  tYa7a@i\[rŪsEcd(a^b~:-X%2$З8<_HŐ._=.m!/%@ы[ר ]{[u g?RFX+1e6y~d&NB:qoNs5?k̈́}|$VOZ{|oJ=%aWm;%c-G)cs/ Ϯ[H`re59w@=L Vo}!5+Y<Wp_?hꐎ)U0;f{5ujlpWТT`2Hn GZs [?jON&8ި|5#'߆$p*tIP'#B==5=weѨ<ߣNvޟ|` ]79~+tNN~9;}[R,oŠU8 &9Mj;\1L|G$Ea-("H @zs|#s͝s\`g/:[thKiѢUpѳy5fSm|B SCHf,3ŪU*䤌w7W:}K =m t hFW/'d5F136Yˈj (jwNzDl}+:hߦ [^;pG{[͢[ =37kfskڭ:_5uk)jZ09)|%u#NUCI[+ )IHXS?3MF{2͚ 5vϥh_x֠yR^*,M'P@VcnX3枛cYe0>iImp<$4@ϡ&6ټ7BTKEWw҈({N\J0"Poc5SwV@ ҜWc̺%akǥ?"vŌc&%t%p[Wt/Җ 4 3OZ}0ޱǚ^ o欳))<0{3 Y[Ɏe*iԚ刱FsQվ2!_lH, Ss{}G(oDّ*4jȻ=+eL!Tm_b*(PāE҅"8'Uqf2CW Od1s@t?0^.PR1Q#D*1N\O~M6h:NLETobKhLܖ:?<[4Kf.@wƒ!c9ynr1`0mr//X]IAs M,!ڙN؁?4Bދ>1+?5U}j`hP :&,31/{}A%Tf /vgJc_R H ȹ)MgUk 8 na'NE=&|ELs Ө@B">=cO٪q*ǎhN2 x}Z2~.<5e0[?jKJ1#%+qHT6p9Z$ gaS{0j?Z| z ;t!.t5.Y;LkVTU|ʈ&$;{N}*o0v;Oӣ9ޒdܚx3NɹP5mz+^ .>=~eJqB׉kvTRKG<e ;赠<~\r/1O$sɝYZs6^p_?) Րs*\-17PXy=Թe˛xrYuY4˖dnD6+s[J$78n7M!+0Z~B EHJDдǮi͵4-OXpv;qP@s2E-MRߴ).8@+oDnXE9) owߢ3=x_Ƿ Ag" "9ne ;O9taHHsxǂxfeВQWg=c=z=ӈEv#>`XQt(-rΫcNPGEk6&FI-.&s6d~jaVV\vjLR63gc+oV.l&Z]@qv_6ҥװ P$ Q&er2[+J\MK2̓oJâ|uO$[<s ]=)EkU)£I -9 h4 2@=˂@ya%!K48[PDE# ޖ(( *yЉLudd+Т;<KS%lEqED,RDyFyzO6ՠS^u5ӵ!d"tQ0`AK9ҥzpH^jB Ed=RY0ӨXC ߍ>[F#\:=c`,ŒZ m.*U3t9Ž}> ̮GLgwgnK2FXhcwᾭz+pXvgH`t:B*'DI!C4 Z1 RZ};) RG-Ydub7haNQF_GMz$1{ 7php1J- P-8Qw#S"#xs[feH&f+W(YuH&+!RewrSx>-.+gq[XF}iV9VX( lg(hx@v(kF,,蜐*ҏuSЈ+oV Q S:-l)Tfky$/"^~ A;^љPyH7@5˵Y>҆W=C:ЧS@x$t6[^F6\h$f|R:%U-xN Y_TS wo7HәbjC:IvrZjрz:8f, /P^ְ֬*ʿiH~qa* /ck "o%BсO'9~C,N1iSj6wS^xT. 80]gpe¡ eZ-F9li)CQUJ47B#+ةWT3ΖirG1yj#^Cq}t?WH0:]W-e~j]ꥄC̲2ͱA_qy=Sot fեu^㈵jP^缘axJ5B^sr]-.`u.1j'E#:&U֊=&G.TT¯5Tt߳RF '|k]Bvc`uY~QcdW(qs1%Jغjn߀FH{XgT77oxvFWΦ`~1?62?jG [J}@\Fԩ㏄?OX%2X䪸~^b?ߦ!UD|_0Mء'O9GTͩ*j {`8K30F +;Qe`4BBj%GC}!+7 QA%Bz!0, [<ˁ Vr !_Cc&4XVxe^BvvL{Sw,=G옼vcAΎ'P|I|ga+l>鯷__Py;?4x5Ι-`~-GGXXN5!T㝐IriI R&:KY;<`yHy:S1=qlY]I?dX ̃4EB +fTCnYwV_} l]P"bu`}_ e%:,WT⣼#kQk\V}`ɢdM>juvy2|4+>7§`DЈڎ S|VOnP9;Tvw݁g4xE b%M}=t|Ph$1%Cњ Ap0 N0*m=g{lϰ0EzR\Vu^_%Vl:ݡ %s'Lğ3z&pQ'z xpmnu5EgcQl j zOtzG<4V:IWxlю~\ Qh%y̍:B=v܁?s#şFjx}KzcP&gZ%H4C"#n;Clb]/b vr@b$*Y#@. ?r rw!9sQ]k=zZO֛=z[Xa0jJڼɒ W\1mmw18MClN#<+$n8[tޓ[b;s ?A=572Afere0Ze+.#[e3"9W!=9,s\wt21zp\ 8 ^,]kIt^ڍe:#3hE/|QӒO+ɇsqw̗ Y '݃]2$wY.Р\&_ku3H}l'.]i^zOWK p!*F-'pAޘVbɺ n( >c+E_uxd +Po.;ksİq{ӵ1m"+ʕIr(Hgk?^|h]IYRބGmNQs$׬|%3%9Ry2vmx2Ҳa̅.)čMՉLpd9ߤ5kkN>+0,'qXhv׈'?l,?pʪnʫ$iaI-9S5~6dXilhd@hH^chw~tj6T~+\ƭ|?rUe%ɒ}YgҒ ?em:n'Zsn`q?mn˭_-M*inC[+ H"fm`]X]ec馡Ύc:n Ύ;nlݤ)nѼmEZ ~}}. Ccíꍖ_a[TcgRh?~fkg9Y߀xx ](-dEIP>$rgSos+N~Q߿O~7QKŗBa(Q׃U4g+).,T-pbTT'G"5DQ9[ 55ӝ# N '1#f{b%|oKugس?*Ѡ` EEGjQ$g xgJ8g!)vއ+Qϛ-LM"&0PR1P4EzxϲE 6=#X%(j*]wh]柪ijY x0p?nvj`ךpwv̰;;PHX1NeC==r,*j%.MNupƣSb&@.9nt\l&G՝j +TArO>l*?{lcԚd3 ZMzrח<ջ᯷qs-ېavo@@$D N!:OL#_(ʳ|s^~Zb[ΖiD<Ҩؑ3C-K@rI~yI*vJ#ȗY BEo#wl"v4ͮ1[J=twr]OBhR=Sk u|{Cល;~O}WxQrE/fyGsV6$ꎴ2Z%4ȃ ]̏R\x1 TvC“W>K [jPqՁrŻ>XPN}kaKqKd%PYΓLfU;[ "r^|8^?JS|K Ybg_ [yaɨ[U3B%6(TK'dBh)2'  oDUP?Dw'ٗyK!T0:+iOcӀg4~F?3bB5l/üDE͘8O'±P+|P'DOeϡ1Ì1c2v # ;q>UNYf#)-ߠ਑vFCjLGy*hB1l@iyf90[_knC\U|J:]~f?:&ME*̖8j·;I4K`BFJb } b'Ls4#bF5` &>rs9&ޱ\'S/m(5O{QDj:iDJmf#f~cZem#GGqW'4^tvq= yá3.6"bY=^#-%iRbx[S%wy𮟄*:z$ ^{~,:] Bo;>O?x֯P5Dy_G_$3O+, AQd€FȔWfX^!]=a%yQĢjTB@o,$G9幑 [,[N^EufϮ&I*C UV`րF_1Z̮v OAK[ȈBBQZZد+/}D{4_BCX0'(E'Ƌj}7YS_6k|Uj:#G@ݳE=.-4gǐ"p.<͵Nq ǛkGKQT; $w4V#-䗫,^ Ib u")6\/ Grt.&{|8BOvvE ma04~6!Ylky@T'cnbb e4RצkPeP-֢)\G:f< _G|t܇ @K=tyb&pmWfܬѼO(tY$|3Bfjƒfڔ'fҌ[⭠*'0EE栥0Y Pl_$l< NW?ģ~9[frH;~*h败Ej;!½hrĤ 0{ 5yQ7dJE)PL4g̵8mk;^g]FQ[+P9j\i5Ye/#Ѯs̡θN=* @|ZP>l*z3hO?nEVegzr]}Rq> in":g% oD}xobAU9WR yWFC-WŶ ‚D j v}cx6ڞ>j +@%Pa-~ GwDAucQh5ZLoq!14ל)~G<z1c{/܇9 }Na]B%jx[* }EX=*8[*ҴkX2P 1/7y1QWX)?:~@, k}u UeɃncT)(._A}֞b~.&|,*.ߔn,je6_v ǏAU.T ܥ@pS_^a١D8aMq..B%[<3MϔUcy^>!!⭗.1O`MGsHXkpeلO_-2Ѵ=:{ QgY+\6шD}JX缀&+ ^FsGX~b&9xD(2 uM&]^VI25bB-R5)URNDY"XԚ@FVa! G0^a{rȹ1Qt?B$IVH"@b*/*>fw2yձev% kHƥNր2Q>.L5 4Na,1^ZiP&K %1xEp.b BU?6ki CHqMAhqVl;.7;U*P#l`À>Vyl7G:G;`9x8=, JtnHRS#R$)ZViGIR:Jl@sd"vSX!ϧak~d+C']C^(!x[|c8z}/ۂWD_ 8˦By K>&qHNZO`v xHS A6FT| o?QP׌fwgP-h/|D@x/&Q+7hPu0L{]sI VgbM|pN4- c戩lWp7i`eLn0jr4:b,c0736z[@ *鋻mΊhFl ď@˃=j"SQu/EMSg=h8 9N숢GF9f]*PM֛S-.^J,e8; 6|נXpjMt::ƎF ŢAƣqba>|?ߎ n GcpE{:_{n[he+DG\ëIHjr0* 047Ç01I4$UֆC_4h 4+<rW]+I9H-eKJ?kgK,  MJ =UH;Jl fݔxJLe?`RbpR)٦/ DNrF !u@ywX`O2˅Pgx[%N薮.3zZ!MB`ObAy3>?]~y0?zwMz] Nl‡/e~'yhHlH,@F.j%"=!}F }/]!eQ\!B8]brJHȪ-[Ɇ7]& [j3ɖ93[Aq7VnC?eBÄJ4ZUjP7KAzУW zG1;B2[!˻oUA @[' O'g ?L CŷATXB@J6Mх7 hI6pl^( Q.{W8Avq|Ӕ,O|Y$]Q^N7`0m,(g#>>PndKf`Plq4Rz K=AuPfpH#h6-1e6HD_}o۞v:oСu5&Sr5S <0)#3{VGA5p7(iŭLHN$ %>iꪛ 3u팣QC&RMX䯚Y~ff ^0ίt͌ nd$w#::/ڋSWӴru7 {AwMcBE Z4SCeO Z*.Ko'm汕 d93K=@a@wzT%$?C+hE :#i%@^JM;q\=QF%y@2!*8]!Fӕ8sBeRwZ*=حbvv<ߨ|9t U(-Pb3W.qh/\i+@v핾9"?Ʃ !fL(Ω߳(A5\>5U Ɋk"V` \!{|p#R(;t_]iŸo| X9`~,0{Ӝp 7nA:5 o= ~u7ڞ z4πX]PbWˮjOE}/&苍h05m_{ro/J$6hx?bN =roOsr$lVQZ2@Y&SQ"m6)fl} +ɟH, :{rK5?:K$as0&bp7]B.'wWYE$9wy\IdȖ-`>Y*d(QHG>܎`|bJ~gzGDɝ;Qq%B p{Dw)+4׍(֭SX w1 6/Fl^%In7:ʫ 'wFSn趡u} G;{qScO`^ Źi!8Nc{N+'R%F+ɍA=ٕZֹRDQi[ekOT.^+>_ʻg%m~I3|q6Cϩv!q=Y`R{w5Tkcrr4G_%uxzlգ9`!GY{G,Ajenш 25x.`X3K b /2A&yn 4gm?},Br[r˹2S| u*PCIY]O_(ۊ2 w6NN5 ̹7}sӆkuD)BuɐjO([Lj2W`l2}=oQ9Ҙ9*oN9U–6Ǒ$f՗O9Gu  n\"DR!&vͭ{ybh}U-5:P`T'~L 9\ L9^nP lʡe>X~xa'a8wP^JAz̔L Oy)z@"ߺ/TٳDόxr>9>ϿaU:R7U)!o{Hh:#AMyk@c7@zN4' y7< :ݾ?U#t6WP5tw6q* !twBP'OntGճ[s*'&`S;_ęSomߐAȝ`>N M9ME= FJݖ>.5KWDxٷՉ|[w\D?ֺ5)ԗB~ٍ;šE+ v;S(a( `kB[xoj 59|?45uEMNjo\лyaMt)j05gs\$ Y:2HJ2C(}sw`D+Jw/:Ԓ"O&g݃&R&|'8+u7fFJN~)2*5qpg ~9h-/a/ a}D^9$fv{' 1ɴeD{g娅Vz6}#^_N6;Hts%"xRkE"j1gA8`(Onm&{V * eK4hETISGJnUE|DVnlTHW AAcB%yc(x'tUJ[ $Tr&a MLЁ 05#%ti-i|$v"S8 jKT" T}gd{fR7b^@Ƀ;M#{Kh|MҚ}[Xau 1aȪ=Q(M+$|]lֳ~#T ^1O`mvuǽwmbE`=0 $|@m%2j;MOm*ϲ[5h@5[ZI|DzzmM_J >_1{2*-ñnt7>ve{ AcpЧ 2%lbv(BJurWk!?`U_3jrX]eTds*-qw& wH}_wPMq@d1$;74\bSu5HMP89hΓCOwЂ?zz6Y=m6bR^t"l\n" Z}.aP6B^OI[?]MЪ|ҿ*|#ԝ4(7$s'`ԟ+ MsǣB9Vi28Ψ|oDG3 Aޭw٤;>;dvgu_?xr󹻌qi=~݋܍gc;ws۸ wwrs[^n|Mܽ3{wwp}{ ۗ#}ݿpw?w?܍~)MDJܽ+$w_~s5,dPfr7ws+[O-w/r")ܝ|ڹw{_s*w3w wot. >w mE&>{7wFn/;~s܍)ݛ;wswwrOpwwݝ/{WgC-F>nwOrQr;kbnwwp wkoXn&ws݇"w7frw&wrw3w+w9w;\y|S27KOq~ݿqW>vPxɆb6ݧOӲgG黅a5NoC4kqvr~ |뚈y,<^z-Yຢ%>Xb/e%[ O˞Hp]đb68.'uMKYo!߆(ݔkT#UNw]tNlX]jYܞWhffPZS)c5K5+Uf9(ERJ$ s56l&pn]=B}!wFT=jndj?ƌ"9VjJ 3AP}sduK)`ˠek~ d)(Uk@,%!Xb@xX tK᷐x&(A1宦R1`BZɠ5).])PLZn"x+(=k*916rHSu68+(RDaN\-VQ wJ.9T\Ȼ@UN40%@B޿ pYk=bVa''k}40c}t[f:] [5pq^  g8Zzq]yC]_,oB1җB.n$fbB$ETq\ͤkj(ט<Zʢ.?Vj4v% Vr.Fb)A_XN~"KZJ_ҩq^MtNv&EA3*yfS0>]In > )tӣ!~_L}FX% HPH)}j,킣(zz3= wPk߁oٷU`7WwÿSwC/=ȿ%q?P>ʿ {XDm 8߁I̋1_%;ǿsi엒^}$\oy2fiC">A_#)Jo0Qu{\%8ʇh _cj4TW;^ܢE[6:PoOrb}#* Y4 8ٰ^իXHiĹR T3}Ϙ;'2#p5B W:%H 0A 5COHo?+\71y>r3nf4/Cn_x(N9Mwފ)D52BǪի:/Z:wsf.YugCKJŒ%f:Vt)*]h{Zs5p/Aw^XbT87'XF.FeX,z7!I7Mˏ5hn̼?f&zIc(XV`H dQMlOŔ=YV^,{򉲾z=PYY ̲m:}ļ ?6m{,[`]_R$qW+Aj]x,!w%,ٺ>b]`ުGW^*bΏv~w?{n}: _O.Nw_VO~~-yk?wB._b 4ƴj޹sKKl^3}RSi^^TS:^ԴdPR|iuy6[ V-Y%kW(f?HQd[]ľtd0~tIi &sњ%$KDbi ԡ? #ZT~ݴ(B閯Zn_dNJ"R-33U K͔V]K V¦""W\]b!,iaU:m}C+"02HM 0WXjdwZ ,肭N"",](kRZtV.)n{ߟ5%;SaXEK+=.T~Y:bRn}h S[]7| 8wM2UsS5#?_4!Mb  n&:ao5ddhHV0bCS>q珌;<>X([^Uq/-yh9~(EThC>h_|M҈%K0Z.\ZT\쯥%mØ<+n w fr7&k2sۇ} e¿ sS6s#qw/w=};7?r5Ww_>ݧ-F箝%]e].n>wr76p7M&!ܝ]#w[s|s5qyܕL{yz ws7m7Ma`$`nwwrE[>'spK&bxpww28sYp]M3x1荛W^5y!hLɒ%+rZCL.[By,[]jsj]͇a"=hŊ%k~u37G`@E[.]3 @VSb%W![^ g$% +W;֘@*ZSpѪ" ݵ1ɓQLt 8F"^P:X2Aj4E+ W!L.)l)ZL7θ<`_%-WxhA8% 1k >"gE+*ˊVyXG]wz mk,p-̓VRgS DġS'4u(~20ԃx֌a~f SfEbÜ=>HpbBo#ϠNs BcQnP ϿCA$7Gayx[Ӎ Нuv(:P5M!6 MC=peaV# t<eFݝ+G$nAi5B(Ē<2}+h2/ †2Q{x8S_"KS""G#)pT5Gbe(D3~t,EH ;FBY#BȄ=FQ qϸ'y l !H\` zg77BnJ KI؝ H@2vs"*G<M/(m>y0q'~pC!}t?i(dك3'!7 '7fn B^÷-޹yGy>}D lOlg$2] 6m`Kl0oBaYKT`m5؋#!Sto|iDY>/a~@yV>+CB}~R}623l n,ђ |D|[Q?5B@d]n|RQ խ85K0K! $KԘV8?هlG!5[#yZ5u -f!7?omq+ۗ`i~L= ķB|k\}=?]]WvUVoz[BfΚМϛȣ='.zg]vs/[bV極/ ~g˻ソuvܵ{}q'~v ǎhY(:I@?.-D|NeC* .|d\RsYWX& e9;~Jqwȑ0^SLULRWq3p;qcMۖCV(rNђ<6e;knN|<ql}(A _ X" Y qC04I3<#sN% |###ξ`_o F@FM]oE}©ZU;g_o¾\R;*/HdIgx9f:7Cs(δg- ۲>L'R[z<ՊjEZ8\o]HnY7nF9'l7Ϟm_1 ~}<)7? 'cG. t)@e K+Db&ь/~EV덯.kt|k Jmr%_Ai#Gc3PXs(#TE`ŗZv6f㋌"P 4V2e[೛v7g]UYtZEo6ѕ$Ըs݊=zrZ=54tǠ0x;vn]6 =_/5Vxgp8'|CqNVɛf/sSӸ'S+̧L]cGn") ~Tx3 i]I5M郐$+UhɃwVr7HU4 Bk{&"NKf%eV -Q>p ~SuT;np_`UIF_@]x٥Ľl4~ HǽW,-v٥+$>ͫ4Zv7=Ip)Y=:w@KX=h2;]nXv@ԅyB0oR~])-R=0% j@u2GAZ:(^մ NJϣ!huA/󩞙5%q)k^ȇi 44If>h4E:MQ] ]\~s4 I4WM MI?jc>ש:+;j: |O ?4'jt6ա\t2T5 V?4 VkSihMC{4HMCj74ĕ LRݠBCg۫k! EU6J=vA}W1]W =O8UJXz߉A:' \hH},A]U]djjޔ~HW7&m߾[֡C[oCCh I=Yg1 jo cbߢgL!ИɔyhbIv.6娘L.Uwlz[? H]r"؞f5:9T~ &".33kP>{LY}M")qv_36E q-fzG½q;Q[M;;>@\ąw.4+]7gĞ _;WcϾwT|h1~z<~ %贏ڂa~vUo|&Aw1ȗg8!:iωNF3dM(W+Nx |QO?N'7ZI\vUvooK3mX;yҟuGy@k6FwsxJ..~g'[ѿ&!}V+'P3#g}^Wt57J]͟I&\~~ ʻl\yM.m-ކcq=n4|.ރ||rw+emNޑ(X(_=;(J;)5<qny]M{(g~m#?.ݐz(g#֭N2)?_(gPx eE`؃S>tOO#Axǀ, V X=k䧔Uˀ~F&X4x~ ՘# "лpNQ^&o{甭ˁ{K?k%GO6x1Dž#;\xc>!⁋es 0psF(`A%кw x')_KL8 ;NQ o7Oud"p=\v2n;qp5 xY`׀a{ \4%87xX瀍o[^6u8~N ogg6w3_Rv^H9aˀo_f:Kl;H\D x 0(G/~ \PLpK} xN#G{~tCoqSi Er;L}xe08,(NFHvӛrWcu7/Oץہ1N ߶B/NҬ[O[t!=:GF-뷯i;~>k9o\^-u^uH.yit= .Jr)_'>&_e4V{_k=QWyJ|?L㛖9݀AVxtvm(%3tO떦{!U\ ;9M@i{ϋQ[tq[gؑ~QUu_;uY}mg{~&>x=lG_*սΩ.s/LqQD[kiVȵV'ybGVrb[b%{ʥSX3""$LWE*aP[B&҅JQJB#/qB_{=w}sgyg]wӬ}vYi>ul/}48aڽ/d;VK##l<6xwi0pUtw peZdxe >UH1rTm{Z$) qX]# X@?PW\D{c,|\pz3/ߜ"&>yvqRF-nVot>̹h;QsVn+Q kvsf1)Gn\_qΞj}ʓoBke>I||-rN1:*35|dN C _$?Vq.A /Ͽq'-g:;PJhTS ZnI05[ZUY']ݜ˖"h2/NRlk!vа swNJ( ?aכסKB}"_#/@^čD/"Z*>Y6Gcm]ˌhWf3]Tju0[$^7)LNwCu̝FW7ۀN2~U ~⥧3*CDC6t1wO20 D]eYϹqhJoǡ9w>n4c4s`x4*urÔi઒.[Pa7#%{گMeP,ݎ\;=%[.3ɶjd%*:395JxGw`nbTPO:zCqs08|[kP=0trC]E>7FtUGYqHm}aTڳdݜ(Xyu/fm'nPZ.(rVᤌDtTr:U^s쇝(±ّ[+QΒ>v$"yi \!]7z.ے힝{_Mm(T:E?2)hI2EcvlrE;gVM2_Ev}|hk,6i[ZE#= CY*wm_ BwMߋv&x4{<:D; ILyS6xkg۾j:Hk*K\YxrˠLïu<x 𸅖7Î<>θw EDž-,8N]/zxg~ F:ǻLwxxO<^1bZMʎo:=+7L\Sx|Q C+Dqx\7r D}c"_84lN=}x|ϞqǧxuBO㿉xWGg}ZL<.xӫb'u/47 D 㑯t_]17[> xjj ikN<~yрǗvxxܬALJ/ 8x\Qǻ_* 5~鳢f_Qyx ts8xܳ˕SǫxYSZf_~c+_<^e7hv)̓G7k}e# c%ow.<.//x|bѭRsqox|L!7V7]}x9xxs t'٩t8axތWoP]ERqW=3 *r=K7PYIהThݞ|nYDPi;zeThl8oy#&T= *]ۿ8ASG&(ޒKf$.bB]?FR}ODv~$hD"}#tgq;bg?@Sx@Kg ۉzlj/8{8 "y ox(u}?229ThuX~}P7@ :ܪ-^:0RVDmx~M^^on0n5[N̯JOί!W걗_J87;ya3'HJЍ!9.|E^GϦT>l O 򗡇ɐ?K[4#74+xVi|qܥx.>f}A||ZaptWP߄*?0z mš޽xY ws^OLy dTp3ȿauP|p_`} a]x>Y:xtX$wO_GEr >^tՁجo=j~񯑹>f:5ǴX>}ݣw%ZV><0E>2 `}tۘLj6e^·wA>ɸ 9wB>D>9kO/|D'm1!|2ХZD{, u,"^]rjvWRGpdlA/FJ 鑅s_шhqhƫL|5:>w\F|uFrڷgƯѾ1~fW\Gj>Մg)_K4<9{[C͗gQX'(~4;.umZ<=5w|CͤU?߳˽Ԕ >?|=-za1uO{}#]^̎S+jϏ컅Ȅgg,q+C§fow]̩9_4=ONԣ4T6~aTKyFY+O괹Ŕ]~Ϙ_\G-WBdsıHpY&\mx<^>GpSGK{W[d?$¹~EZޘx9oY-dDlgO^n✰?a;ޟ2fJܲ'3-t*dr0߰|wFѺ^:U L'hs+zG5 RV"'ivIgd ڬry+sg}sEt.r=&*81G{kf|;+z|ŬXTGz!@';O"a P:oخQ41]v KQ-m"|B9Y?%7_6}Ƒ|2S^q_^R//`g_agȳXY,,,,K,,yzy`'/?>rjc&rR Iq*s~^c^߮;a:Iz;\BS2;[lŷ l|3uuh zԛI]zК7wꏦνݨwVJ+R!AHPTH IZ>W_*T@qRSr i}kǓ^Cd?v7:k vQ0>n _s=` !Ğ 89]' !AWlq @śأPqL`iCBpΡ^ON{m@x5ι>!4D*{y|vUuB4,!\x9O`U|i>"Z$HQE#+U__>v]_}57~&(\qJDmMHAQfTmNm܂_΃ Ba]ܛF+䳩&=7 jG-2ϧxD lȝ}Hw۱QZ}yDMQjĤΝp#'b9GPL+b:ƃaSa{;dEm؅|Cߗ1(zғ&i?̂l'hE)*˜? <. n}գI[R^p#viA<8>R\pI{Ɨ"ǥ7qbaboB^K.d72=.uӸp"% l\f\\p㒄K6.r^ pkQ7縈a~\p\ĸd/"%xA66o)l)lS$ ĸ ǥ.?@qKg\࢏KW\ҝCŮ'."/MGz=VREψS;/!!k?%ϠxRvҿ{bGwˎ>CsτpP꽊!_q8#ݐ~<;@ozb3um<[$'X?O/SNL]}+>Jρ׵Cf{d@.L\&4KROR?Ź~*&0*fE3JoESsYߖo2P|SIG3W[q}rQ!Q_Di(N>a*_쓬/OپEӂ o 0 bbiX{Rq UP|u1g7$ęoswUf˱K RG}Z#=AE&J/=ƪǪ$A(t6U~p?W:R%YС8v̗i@kgʊXM19~܃i|y1]x6~zA}ݾ ibC?M7UU'm*pRT9= q\)GI]K\DxWϡ rϗw,}u4^j~qǿY#_GhRYB^ڟЏlOY'M<-a7) Ji/,,Z^{j"U*!uBT-e' o4s#Yשc]v*%4T8`[pUإr\A| RYP| K?T*!uBT[o n#|M? 4GBT*!uJXd.'둰؄y_Z Ui`+<14x%ƟS ʇ7ȱM8h|[:kr:^Jw/ﮱq.c(\2 YQNF262Q q68gK6EOJc/W< ġ9+s?чIw棁93|lw3/,Ct2l3eςZ=ckmtcsYdz4m;1g/cx%଱?oy >[S|whZ`4o_t7m=Bu,sCvQ,x;Ao;pm/1^+9[`NU9ۓwKrv o e\Ɠx< BEs޶~;6r>㚇65Y`.69ؿ-]xLl{wîskoy氳2_ak0h3^5wyc-uh_G>+uoKN>\w[ <; In;zĜ>xE;6;6X7Mƛm~2[ﰦi .AǺ oҴY?ik _ ܉WFOG`O|zޏI\+–Gџ&֟oR;vu;x(nv ]~Yof0g1 G{ngu5l$.u' 9{EyCߍ]ﳽ˟o~ l[ۻ=Qg0's_}3qߢ;TI3{c)@ +bg._^w|g+ƺ|\k1FXH!V!5XG^sF+A7!`[8k`uoSrtmo=?guOn9mg-޶g8OgkeS~*?5h<"zwغl#%! 6_ɛeӌ=қ:xXG;xƌjg$ƻ?{~ӵd_cRT[L[3G7wk܇lbUU}!g|osbl;dU4iU%>t'~|be1|h5>JO)wQj*A :: ],(7EE&ؠ>!Bjfl7b.*{PN}DŽ$O* Oy)UC+{FXSoJ+*DC9^)*JKI䔏d1Za$J' wؐ^)SQf 9b~7$90s㹄[M`1sN9`Pw  M+)peR9t]! ЋT+lq䄇Uy+9 TOgbZ Rj!ъ5#  L0JWbhq'k(() JB 5אv@!}CGJՓ̘NM᮸JjWJK4趟BhJvK I#PgT6ڊy7 -+.J h΁WL5$/U>kKXnFUaum}u9'ϩ,ke %s*ZQ' Îai%=ҏO~?ÿf1mEkda!ێtE\|؋Dž}T_x;׺iy%tLEl=s8]f!UzJc+ =xɟP ',oMTkqleT)3*7o꜒D]A^VC_nǔvl~-UzNtdmQiC[A33٩S: *=`"8e=rJ_}?儺t*.ԥ_5᧣P?7ঁIq gir<+%Px!~ЩIs^D՟Xd?<O,zS >j'7Q)) 8Sq ?kU[ TS-=poR\mUn4u v䧦,.4dibE&E[O".s?dcaGLx6:9⢟Lˢ.M0q|KOsѦm[- ݅r{y$1ܣSȷO ԰I#%PHNGB %ȏ&s>j!лsACƺG|2k]_Y?)X~VX>*ϋں|=¤*T=iV}s.:Eu@g<{Ak,S_5cjXdENbQG0?iʋ?g~9=\KCz+4E=y :'_\?]mzeOZ9sru>;< g!s EE-\\qF<-{ٴ~FruGnӟWwcXg _t-_4}ٺZoa?9)o$/hﵦDeMϦ͗=uZY|.\ӐkX:}EM}^,cwƐEwrǫY/s r9䉰$%?C w,e>[ME3{fbה%v!鸛8e7*!3/ӈ%*6`Si|87C{m:w*#tgА y iN[I+^'7>e;wmEn!xeG8awWAg-'tn6{~\,66偿X.Wbgl/ejOq6`,%`7kZݐ kN&A/zqqmCQ䐡U#Fn6# Zq>d~{h z ~""+)n=qst:XsNv; ,pOSws}jɿ֐r5xki͏Zf6<;TOuᾌrv^!_^_A_mwk]y׉ 4n蒏[yqxa5Дt2W}:Goxy/yCD_H g}ڏF?Gu'gqgHX΅ߑvԓ4ڣi}.~=NMntcvҽƫѨO5o{iĉ)懣3rxb;-R <.6wӻr'Ԣxc]UeD5Цc>l5[j{ ힷ}W oL9hIܳؽ?ۖh `&޶qFgLzUwo\m[Yf:8}F-_ǻ' 81?Ac6\G#fHTX$1zw#K4Z6zŧo/gX>#~: [qz! q?HBSka? GBS׳oJ O>dn3fkK|6հ\q|KF)tz"Diyȯ %Gf6/  /;7?Eh[.ΟGCqg[0r07/^{γ234[,wy>Fc C$_Qk2Qlv25L]{}F7I}= cj?a B&/nܖ)._1DP>ENnSUjE] =Q Ic~ĺ8 ~Mvc?#ƾ Љ! $VdU/ѶwĜ|.JD,F޽3D.q~1bC˼a>/F=9Oît8M}Lc>)%Z{說c@n~$(}.NEVaQ܋\R')P~۫Ÿ{ y'{h߻g`e22Txs6~W:.jߏ?Wn,Dj|2>]W}`C*,g~6U4@6Spj20 > |~o [0m?Ĵ ;>SY4]cstq9'=.@"L|¢/ƴғ ܟU(7)cOͱ'_p?UتZ[! .?!b.b~7Ø3aez q3D6YMo>+Aм>US̓*c w&>ԫ S{^z O4KkO>?x7Va({ud8sιog#; noj/J~k;5@~R3m6AOn؆$"W4'(G&tCW^Z;՚Fۅ]D Ho;Q>Ə+676qlzB6GK;w 7ߊ#|g 4r]ԧlғ%6p|{!ђh'|=cfT̾KIPP\\l}ރ6kkoS?L6|X7w?>zm&a|\'|J4_G׉PyLc[犄E!PnҔ<1N LY,u'1>ob^mt7A:]Z'Q' ?J޶x:}!O1{95"z"?kq[~u^;i)vQ$kEbunqcmpF7Vd97EOLN'ꕸ}E{< kS UEy~n]75M6اg7F-e*ohk} q^7&.EG&WOtw677OK_93}+ b4C~a5ʰ eU 6 ~۞.[;E;ok^K7[!ҮBQ F5Xo@;:6qzanG !7R kA]mj,$,G1(zُZIKCZk4ͦCBX;lx\ڄmBb3/,;B'n[HV^NQ޴l"=^X_X_j%Lq؇{cǮ2. y"\cV,GXnr?K˿ ePY6nAo%d\GBpP5Zw.Ѿ:X1k߀mk6qDhmQ}&4?fG}لaWTFpX\J[qpa=-ښ41:]N9fARe}Z;ds뗄/.A⺒v [^dc[P9;MPuvFyNy2*ϝN P7 qUeP=ɖ~Y4ϋ5Sҹ>3|ݡ|2 LD Q(e!ÆňrCPǐ8jc"!xZ_c)vv`Ptx#lş0$bq| 5=z m<\ j<]eDzwtPYtP7l%'Voi& -~9˖Mˉvr:s [9]q\(ڕsg q.#ZpWZجĹaڽ =|cT?,_! 2of`sDwUp%Y<&|ta{Tl{$Lr¿CN^lS8ge++LxeSn/1>B6*[˸eĺk2憍8N1Cلw0<9L'c2q=&L"n2(qy]e'4^hV}z@;+X9.G^OZ&"~+ƞٰ(C*寭G8>S;)֞hP PnܖhJE[JgM:m2m~T+l43]:UGnX_iV9eN^9Q_yc~ kjƫ)W!?!q'!(9gzo=za~> ;~E؂$W&k31֍I/~<܏s+\P}9- *gB2 (2}xh?n2]eZ_g^rz*t'x5.旎Slj^]k@H}ư>mw'#?F=Fr~pr 2_Bt^r%V([%3.[<:Lg_>27欛CuSqHXJlށr`{s۟<Ƃ*m l&D?y YCk6GN$V[EiC.XmčgVw 'yՄhՔo6'M#h!|x`8/Ww$ū^]oD퍸8cY8L4 w$\Kn h0-ouц9g/m:K|pVJq=; -: Dy<ޝt0;LjgǨ|~#z8 j[9~_'c1A}?6뾼}n92{NT7?Em)K1RϴHL5;J%;\ ŏ/\g :AG&<`#83/lgw1l!X=Nmϝ5QV1'!n%X8{O\Kߺw3;}ai74@^r~M'>~t%'\OOc'HN! N7D:fYCO G Ğ{v b?Kw4LG)w~ 3 E*sH~h̼bUB8Wi9/=Iu6$ {m'略oKme/ oQ3|^NKz._&2FX<7xv3;  r. *`0tr~s˸k4#k7N_gG>gN;=7Ewį{X'i\wSg)w1)#ttFGS>}wGw#\:6K-,v+Q%ҵ?!kWDLI۱ WokwQ76?Ans{kO<;aE>|&ߛGttc\ 7;6gG%&'9g#Wq@." |սp^$ELl.Hwҍ[2 x+H szԍ6m<U=OM#څoZK)J pŠ>:\5kzpww. p]&rA#*وn#i˹znG-Ψs=Wj}L{i{Rs*Rk NA4o4Q>wO9G39ݸNB>V69T|k..Zks}aA# NXndXq7,.냘ֺc:>ރgw^.nY2 {kh佮}__CiΩ>C'1-v.\g1Ĩt{b϶5fڞ 0D}XP;y| + /9!o'1iטgL1sa IO?Ny_ӶB!tBڴVZ43;̘n]7B!t KB!t !93sf~?}+JSk%Q2‘=%7*ٹJdJ)YdT)JVQ%W() dÔ *yW*9WG\J_ѪX0T GSxI׋сp24*OcQ!4G"eUSÑPI($PjD, IבPcxju- DV*`0J&8@e?k|"Su2TЉ,K׃P4U^@CBI " !q^]aLjדBi8Jf4s)_CT8 D@dLJ6AQ48&5DSu+J5#N2JC;ڛvmîS~!FAXRȓ5. $S=ѡdC} H BIeO=5 昜+Z3 ]ĪGR DvD9NjowK-NCd,xd0L.ZήDȥH(ljR2"T"\ِ %)Djӊ6h=3.*e,Yq8AqъZ O;ZxKRjklqOjL`ST{sh53z\$V+ub|۲(a/b-ےެF]Լ2LY _lŧlr ӓD5>NK Gk"Q.T0}Q 0+j5izZ O#6xM#!Rp;EHX444ZKQc=`hPm QINNDv#g8,.gl.o-y)cc~b6 bqf1dJo~F\㴆Pbr1+r>'zy(Kd*p5񞲀5E6B]ŵ/t8UU[sg1h ӷ4,OLnu2鯪:g‰Tm.w\($eq*Kc5YlqcygTKX¹Yb"JMHֱr7%]ܔ-ANo2UxE1ΎY9x`ugMvָkB26r?TZ29JAx!!b)^1Cp4L qsf] 9"jųq?KGqE$A#'Be &CPu9tbNǹbMmw&~CVvvgI j q~92-P-Gs:`k!Μ|ǮUBtFPTHMm$iKfz^Ϣ]|%ČcDF^^ _s/h0v%:ٰt(jy] dz%%ĦS5Qx&E*NCc^-5p=/ha/fVZ#i7/~{74P <@7lMtm-,Llv`K膭--1HN]5#Z "\Hi۞>avyj6A]R$2M]r|jD~ u0v\øNRy)9y&R^Io.%|6?Ē}J 7gl)})[io-Y/b[/dB_OL 0{as.3i!]Hc1WRȞgKtoez5廹>2=rUͦ,`wHm-m%mLZ1ޏ$ X%fv VR>6}wA, aԫܻ^i: %N( 7Āw;8ٽw1#;YZNIi:C KoPQθz!u VE+ <@ӠQrHq-)UIs曺{ cR~x'14{A6Bﮤ 0 BXkAٝp!`MPN}r,s .mDwCSCXeAHS7r> ^N(~h8F 1 mĬrk&q}O,~09hL|5} aNԭ!6b1,|=tVm66ͺb C L%Z#Of_ >g1.f|ɜk,s:cpa5_D3Ooo`t[&ܺt`9LQ=1Ŗ5)V\ &r"9alS8Ni9ҍ(>ˡLD/;[bNX)cKLIStby\o{ v>2DdؗOǒ /b[H$u j=t. C,H?! 틺Xyu?ч\O&StQ8{-"|1[H^%3HN-&wX܎yǮ-}QwXA(y|+R\Z"w 5a-vIKIfֻ{}b>QeL\h󭆈bنM[όGo+iüO vX=rxyy htb:mXDx [`~c)qҦ?FvD6'зgчg2JyoE,Boڍb"y}=zb5,.bLQM36b%Īcis"L~KiKʺzd]*=: 1s!2^ne^Xoڗo0ߦ=WbX ]ĞeeRH? @ඞp3 a>ý ;m)w5 Lf/9VȮ`IyL19 K) D_?q 'A@maR 3{^ ^-Vb/b%l^]ii\E00 *^c'bIy"Wظw,X˼7e\;)` 3K w#?[DoYVt[VĚ䵀 Kه7Qϴ)~t ,Ph }2҃~a풄LXҝJYIbC]O?3!CLk}^Z͠'@a0p}S$z7@9Vqa [E6E-({97ǒ6z8GO'MiȞӉ> SA_s&uF߀w=O<ȉ}X&=)~ʀr"io b-Vda?VRfh ryzOa&e^G=jS!Gj>3LϚDg>YvFi^c\AVs1 %oa3AzrNpgcXbݖ3?-s+Ӕ}oA\Oz5>=us(o=鈭/jbZ&s|7b؈>SsMmڙreZa~rSKX҅=ڮw_"̇@ph;Y=d/5?S7U̿s(:ʼJS@ehg֋ {;5])ȵ$=?̃um/eZkл|ӡng.} \Mb6ֲ;tQ [{O>ɧwޏgu50/5Jq2v3w 8oZ -(~%6W<"g5зw^|Km/`3 [bc% j.a؍}\hAjK-{2'ߞ{d' \ `^E?SAҏÆ_!>o&8 Xg>1_g\|ٟr$b81<1GuGoJd>h ={Rxe'+3O7Bgѧ/,@/`o}S?X%tacȕ/>t9~BtKD$¿'Ss2z111A*–wk d'4-8[Q!Թ{ '-G9NbK:BΙP',I5K-A 爋>?2KtR.(1nasGocۉ p6k9LΒ-(hg]w6>ECGYd}10ka}}5̥ a*e6 ጭф (/`P;L} kI`΍=:i sSs(ϚR좜Ka9zMX='sVF B,E'qr><}F?BӹQG/e{=A?:OL9<1d$g9c}t}Sǯٯ* əCڄ::8䉩iK8uҙ{gѤsV\T^&]8oCF[9% Jyb<1Sa|SWH5趑sAQr69 R'+!_ )<-=k6٠Vs{٧__>}tSC٧bߺ}=VgYgӭi7y?`KGYܿXz ۳XFO菕0se;X%X|>;ۑ۳Ӱќ/ Yv]+te+ϐ:s߷/_bֿwf ]Ga{M0\`sq"e?P~uwkg'j'&\՝utsSɺzYtЩTܖvivy9RUSY X'>.8뿣M5̻ˡ sgkTֺc>?O,2y4xa]*#ak\ k\ k\YX rMe֐S/օMgtQK `=YR sy:0-BBz&0Wo<3lb{>jd9=g/f^3W׮`@Rݘf1 8|Q7j0#a,\Gb\ k;8U#yW8p< .H*wca Zx ^_{xo{'{8le zߗ/p/| UOxC`8w\/jCp+Cc|1ޅ.ϓ.\g8҄=< []x6Q+- |Q7js%1Km|Ip<| 7pЫ!8ë*_B%\ `z-_ z/J`\tx t[M<3|.oRN8aaEOa&a |qِLx >|?SL.+zxVПp EP )fm6t|7_tp8EP g@nqx^ h?Gq0,.x۰?`5}0 a>3f`9 - bW8 ` .NV1lͰð' }a D S`:\-p<X=7>;AaUp n;>XK&|_f1w> ` qzha&wC$</k|Om@7z‘p 0I`\ 7m <+Mx>- >fn pL 7p?,e2O`>@8NR 18. #;}:Qp 0.Kj sX UXװH08 B&\ |o1 BO8N0ΆIja9+ ?Ǟ`(0 Ap\a<OJX·;tzp' gCxl%}Q0p@.F` 6bGp }8pc $HEp%iXo;>'п}`wh8ip&@΃KJ S56B/*  8p̃އ ` 0F8sa\ 7oW6Ao 'P(hip5\sNx^!|?k(A?#L0!x `)#NJ4\a^l?OS}dZd̀Yp Xo%l} v{p8a( 183Fs273X?3~p(C9! I.Yp+,E,!_/# }` q0!M0p# <GC_ =p(N ]a #[I^p,i0",'a% 7:àj! S*wbX:[!wz0C Up# Rx ބ ~)8J` a8΃?F 'a9 - $v` C`4a$` \WMp< O‹|wLaO0΄ lex>GN"!ppP)8.+zSzߠIp<0(! ?&RxބK4:@WzQp b `p.+_ṕXam> euaW z Ca$0bS`\p3Bxak6 ,_' 8` rPI8.p\7|Xa),U|_+hàN0,L? fp74jx>gOp 'B1 2gA<ÕpwyxVû1|;2F`' 8PC D(4p \ mp/<2X 70;N  a3A  s'yx ރN/p,(gA"%p\]5x> Fglvp _FxI@~o KD':|ά;]DM.=Ğb.Dq88D*q r8Z>8FNJ?ʉ$1@U,NH D,Ne)C01\b%i?cD+ƉB.g ,q6DJEHTQ+¬Su""ETD\Ú) EZ4\qkq*.iiť2q!WLOֹYqN׋čb+1W*nyq'+|qG+P<(bu\$boxB,a|J<-gij9LGJ/OGcr}>NWggu>QzCz^a}^Gz=~ГzJoޤOӧSЧ3++?kk &f}~~~>OCSK߭ߣ߫/GG'%SRY9}y}BI___Woookww:#c}_S3}QJZFߤoҷ?[?5C7 4,gt0Fldlbt1v5v3v7{{{݌}}~F@ `qqq888m88(0}c~ƱqFD$cWd(2bcq1(1ÌF1i2ʌӌk3ƙ,loL4FQeQmFؘdވ1#nc$2F#m4s)ƅT"o4%?ƥe J*cOj_,Z:cqqq1ǸɸٸŘkjfn3042w ƃC"Q1coq cxxxXfxxXnh0^2V/׌׍7ƛ[]=cXo`|n|a|il42616-/ƯV7w~M4M־Vwk?kuuu:::enaibemX}1V?X8xuuu5uuUhYAV5:bXCapaFYeihkUnY t Lkuu&ZҪVȪjZ+lM꬈UoEαVJY Vֹy|Bku7bkwtR2rkuuu5u/kuuu5ۺ޺њcdlb͵nnnYwXwZwY󭻭{{}BA!akzzzZj=c=k=g-c=o`-^VX/Y+WWUkjM-mkzZg}d}l}bk}j}fm>6Z_Y_[Xo֏OgWk~ͧ |>__/ٷog..]}vuӷoo>}}};w`_O!C}z;Q}>c||3n>ڧ?jlyrq)˯_Rس0J‘dXɅ .**,;3Z**lg1U! X$LWЧ+? ;d,::T y/BF*Z,++m_$ ] SDɘX<B`[ .ק1SsSc0<D~u/"j=PlJET*ߠSL[3m,< UIYOGLPz_l8{E6ð&Դ2E7BϞ]mGV=1۱sap{ݰx;[]_ZTXC 臨K:Rs도&{gRD1wxBuS,T,dmpmc$26p̨]] {8!QY?y1o9jp^N8~EY9e~P4?KepM4soJW"#?R)h-L|vO ۧJMfCz[U?iCD,neAhM3#hhU,74rz}vbѸI )(3ٔ}aHyؗo\1hpYQ#e,䯊{]^ϸW@SkN#(U:6Dk`(hO_Y@0 $jhDTXH(.crdv2=x$S^r~>gGZ0Um,V:f+U ̰a[%2DeTe8WG~vk2dɟEK<5gK: 5Te}u)Q#?Zʖ *&'ɪp֪3J3ni1žѕv\SjRE[M T#QG; i97\TgP{Gb3ҠWJO{n "eL{ jĮ.Kz|nOR ?ڟ {n1|WQцHD^]ΨҸ3x(Fyպj UE)wrhT½vɁTmT.OSe@.p*puP@VGH@U9iT6T2 E1hn R֭ R') ^]7rLU Uԧ>U9SeWxZ*jÉG͸Rqd[uQn@3>Vi*OVjU2 yXEƯ!ϔ]dǓOzz/h IO=˓YiO=Ͼ!Q]=;ӞO{;i۴\iOҞEk$ѝۜ͝MkI$ڣVfDnA"1XnV/ܹsDQr\(::jX?#JN$b#;\Vb'$[J؛]ψgĮgDճ>PrAfrxǰ&N\-so *>ٕSjɬ\'ؤd΅W//ɜ ;HG*T̙2jݙ2j]ݬZY-YVw}TSdʒ̴Nm$peE֔k Ie5 *e]uuYw~p6Ӱ7װ'pNռl4 bʧd;ɶN$[;9QL9&])o&g{֗!sg1LkG݂xuynpWw|=fxܜ^f;9hڛ|:7t&'' }+uo:xKn0z/=w?JjоiP N0Ք}Qvf\dUՔ}rdE>;QOh&'h67/gU^8WGnJK=sk$5d4xC4A͑YQfECoNGr{39;oc9}},o8;v 6+c6g9cds?{~uoε-א/ޘkޙkp} 95'@N :sչ(׵Y%_1{Vv^s:fgn7fF2YsFƜ'ѻ6z1_LLcS8ތs(4>Rhɉ>;hn BgИEh#46$4fFZט5zWFrט5zƜU1gkl5&!-mnOVioӹe)ENc4k"yycFʉv_HgHUjyw"Hw: wG׭['ޗN3(%f/&\uˑYbfݲhqcG=٬޼uK)*{B^YFٚ=OZ58;٫lo7z92뢊Y+ye!yg!yi!yk!m{RsN#kw~}v3~}v3t#tg震OffOnv3]n&]ܴ/ynO:ieoBꠜvO8r=æOf&L9@4RSj:=9;I2_Ҟc\:֩i3Og>ܧ}:Og?siwwl<٠3qWS5١,Ď,ٛS,%_I͐a:;U7̐Ӿr76%ٺL.>aYȗU1ZzvJ$QT ju>iI/ɏJu2s߾#]P8bT E1uVcA.G_).ղΛ=ZJTX.?$Wm漾"1\~YW6?w]임FiBhߐq_B#3Up^R*~!E~:ŵKd`yC7D=aW}oI%Įzi̋L;B=1Pϣz-Oz -c9 @Nq}'pb>37>p6>js6>d6>^gk}&"T"AiHY~;)p>yFj?Q3m}5m=us"b)d҈d%dP=*mZ= vL%c(^ٶj歫sr&? G .Y5ի>):E/U٩\o{k9ŞR?PU3a*I`dpO9[ upQd"s$áhtݲ.nf:s.dnҕ|2}Cԉ–#T(AB* \4^TUID3iDUk^em.՜*CΆuJ8dz5~-vp6otfHgg=XMhu{M>ꔢq\Ceҡ@-|a*Gm3]2)OŸ3p\6ϱ|-STRhϸT,kXL[&w&pMoz@6V)E P*Z" YTQ@Uj[((*"ފHⅭg~gg# }:~fg2;;n2ċm9~]v͵78L?uQ GM[V8#1}R# 2C!2 }(hh3ж`c@ B81 ʇA) |áU81tg3KоBȿ}={KswMBPy՜co‡!g;f^ G'Cpq/q|.8q89458qTeqQJ>Yl?qlzwv=uPo0ßۮ*{=%>R?|F@`bfpȬlbK_Yیo;~Sv_u?|HdB[0sB ?G I,e/aUL7o4P?% ">\tbybw;S?4jkPu\S<~sK˞xBYKh*b(OJBPk952#[4!{4MBrD1MQT8eIR.!wy!o$A>I!$G3P DAHf`{׏ZIɨJLQ!h!Z"b-ErDhZP4AkP,Z֡o?F)[|BNBD.Œ0wb4Co%,齄?Ϸ|[ ߎ?O|':?Mg]4ӹ|1}E(C#||BJ'j2 @->LͦO"">Cg >G zAp{b}XHo'L,Kbb}XNW۾N&V׈(y}D }XCN" h#6fHB{dHA{Tߊߣ4tߍ2{!~te@YC(҈lA#(#Q8:F@It(@'NjE@:v]ATD\CgRt#6't'n D9ND[q]&+]TLT=TdB ”J$&b&QIJc(n<˻/v"6r5W,j\95Xo_S_cAk&}\x߱~g{{ qpx z{cSH`0 ('-@8888 {5@q?]9y`>0 \ F@`78B-HG5z0/xZ`$0 ʁ@#h $J}F@4%Pl vҮ}[>k/"҇0߰v=ut$#S^](_3{ ̃ D7<>-@?{"^ 'x?O;rakv-2kl'RPsn)P~ABM}5bڷ\/-|o q%#/$~cyH9hٲaJe,c7y\ӌk+Jmxq/t[3o<N}iW7<7ro~;;洅ت4k[P1󸥍iyJ]ۤܜn7xij7ӓ Iˆyie鋟]SH'310ocKv.GG?Up /3L>IW)t0qXYG0uǮ<u>Fk̶B2yD&o^μx[UkFprԟlu\Tߤ KFZ|>^8h ޙaG3ZHso-*w7Y I'C\q-!{E]gƾRTq"/oEҡrrruP Γk˵m%*F[ӝ\],A9!&ӷW^]_qa&gv^xe~4Ԭ; ^$wۏ2.}zG(-8b1מzc\2How妱䮧vpX]Ӵ1dC =,ZqCuu6W`8(۴LCUh僗R_ ]FzL6 2s=c_dzt[fJ?{Qr4LS>G&;D:5k{u}ronWJΗy3k7pcOyG+QQ)5Ll=7IuBŜz xF,$V{yrlr(R`"6kC~EGEg6dm}WxWLy[&ݭuQ\u22yws:ˈ%?n+…I9I&n;sB9 L,wI0|r杀m`#5݀/ [nxy'tAzAS.rVsY[/bu{#Ʊgo=-Aϵfg=#͠hVO)Xz:t7@k :ׅk/w rнAKA Z&Qu0%\RUݏ_AWV@[1A/53VXe6eU^ֆ'.kӿ1'a9^֟R_:ؿum_um쿾?C[ 񾂡`SSӕB̩#mu;:;cN|{b:ؿ1`q-o_ CoqJpZ` lW=gYwgYwzOaҷW/)U6^c{/l/5K<ϗ~Y+sQpV^}*ƑwWZk*ԟ酣RSgssNKX#V<dmo #%`Wz_a+`e:ص; {ܐoL qoncpC! G[g@ H?z;ԗ\@q} MP@%Ӡ{_#K(Љ@~<[BӏP+PuP!o4r@r+!W@CPk`,]ݿˀP;qȿʍG!{1 5/v۠sHYgR 6Z"&T4o/9rE;VfC}Ϫpb_s_䚡6?I`D\IP~״z_^swWirF%62 gנ W&A[DYUk7A;v6bq di4wY6& #^ ?f9w*ύ}/Y'\v*$nb}\/Hqp:ÀûIcՏ-(d \pf{7}6@/ς5 UUgZJz|ųrqo#9Q*n2K2Bji}gnTwgA@Y!sS1RfVPUbOW7spTu|={X=׼k{_G`ԏڌ=>?hC:&*dؿ wBߔ- DğNoCѸխm]N1?[rWVacqB;[~_[f]/{[[//KuN?F 84 *?50dGՊo).tĿ;M?m:?1U{1.$|࿋y,؋[=[>]ܟ:,uR8qBR9 Ը@1-%.^Ԑ@:<к\yNHr&8ā?| 7 !qzp{A(nb*D: 4'A(>PpH#vM#S:'Ʌ%Irh֘>Ef DW7^kI4AM>lTLس)3tv4C[`=b*#\!r+'Ty< $<!~r@OE^?"[x_yրVv`0a> ̑(N^\O%]C߼m^%G AHEypW{Z8ǒ>nv`\ΐ1Iкہ]Zq} 8Cǀ%|!5`uo^?`/onyOGUzH .!`@.ۀ}g{b:78' >:v-g(/С ?rЬ/ǘ\X V/~z\6`X7bb3ĸ r8XkϏ#8g'|=Q!8p M,0Ank*8ˡPeLgd{`%[ 8iIyoeZc@Xp]8o$k<5|:iy` \MsP(8p2PA]Nm"feC[O9)ceBlP;xn{{ \칈{/ @?9@v^dàRC0Fـ:L-g >nDsVƃ B$hӉAmܻ`FUMzlB_@{۶O*o+vx\:$;z/% $j"<;C/GӐ?8 ru6@sLB4$F./O[@A+P8tQvcp;3y>c1O`2`gE5!`po&capҊ?݅Lfa=nxot0!!/顼')>}E`O7H}_aSV9bm}0 s;_~uu +[$+N `p`Wl"u~)$ez:B ud$gu@Q6K0zѼ Jr6DG(qGy:l CtXy>:lZ[~jy$YɤtX^u ./5:ƒW5tèp?ZEGPf:B8c _k:T`8_{iaK:Bm[ xIlyI_leqYk `wK4{!mίd).σ#KEDwxxٓ0)\+cn`G3l.G}"~/0[gKf3KMudup3le6C ' ?XWg˥+\NuChhpTsno >ՠ yW{ChHtlaI Bo#!-[G0cNkƷhyk^%`__A۶+=x4a*x9aZGa:=9X>@ :B$:]'1.Y#xO0Fi /W -AT0 JX}n:~R<v8G?{Qhh;]d8> uf5x~?xYUgt޳> L4*atN Veg\]:X.v~*a|h~U Pvԛ 4^mgzV]G@$#Egt7\ [ǟ`%O$xm s]o XV[t{2^~eRucEpKwo\ &*+pB&p m >P?,XC=Ul !6~%ш\$/7aND]k7$Q[foe's V.|g18}; cZ 4׃) <@\ ]GCy}A4xFI}]`\vpYNHm4syZ\i k!W(+>y{ޠQ?r[x p/3|:hT2fσ[ Jsx .,@N˾qVc<Xrl6W%,)YI6uG~Ko,p'i).%!P"];%y/3Q#?KbNdՠL\WW|xEpKXQ ۀnp~,8n0!!`c]a:LtH*MxPp$'/%k˞apͦ<)cI3X\WH@؊`KsOKe(k-Z$.[Q[=WЊ]Kz\頭#o:85F.ɧ"rLut B8YAWMCN[r כލےح)uPK=x< IOJE0f`IIO.dʽugv`4/8p +@6{A%$9 e+k_i+|1a!/!\.Fr8#2e/xF_+cdr\4{`r.'+ K2&`'^Ic]&u5]9a G4~0@ 0f`$gxXp+l?~uyt؊:G4vze_ z'I o\ p^S`}`A! 5I=x.H66;SfCOxyS $98e+$3$9.{FWp:eo0 [8FC*x,Jr1O8T* g.?E6|-ɅeY#[+ؠo+mAī7٫Ҽ ּ8UR<~Ʒ%%@[xw8F#7G~+:xS9Xfas?c.d_^tb6O4~ [q] 8gHm =|y.DFIr [HLD碌/1#oK>wyߡq 38ׇ3$9Ly`.skAZgy.k/#o>u`|n!`BN#'c|o%A#,pY15IVż@\/?px#E'?P.0r1T . %}2x[g_. zBdpprm G! 7̊`l'x@A>>cp",C%>n' 0zY=a/dBO gc,)!Gx<2Y>SyFpo 08"!.BN{Xr}08Ւؽw98?$Uz+ce`'Z8VK||Wm hke{ /Kh`l {$-@zukޫ˕ rvܿ3ݎ%TJp xɎ:V5xYd I'Ujr_)so!G]=zopM׃- }AhKZn%9l'd HlPx 4AC !'5rQ'5`f<-H/8BOJE$ .h_ĩOH~7\ Vu=Fq dh&ؾ9͹0;lr8ߝ뗂$9==[ɞz@*xٓ믃" z8p}I> d6zB-;1ŗ- [V[6 , fwL-+`Wxu L'O94GOG<dWqt,NEq<lgU磌kvzX 5tU kW'AO@΁3Fpyp pj`5n5nU@D^V`F=6ր͒xxݷz p}2sz%Xm;{ )E1q'Uc;y>A=7Gyu18wHz?ہq~)2 kVȅ`Ã\vr$0FGxIIE=TcU =!ϟڋ`)?;]B/;st092Rs!sK,JBpE.Gw%9p!Iv@$e7I> 6QSW,KU.; $y)XْZ\8EAR<`NHI[D p->קoXdJƘw#$Y N9~Uw tp}-;hen^0!8m#pP/ȡ`G\jJ k`\ ;?65_Āg^Ksrp!1 CiwT˸>lUN_tA* vG3*r9`U{#+}A?*Ek}?gK U?II6 62w5?hZ_0FA6 /Cma'Ocq{ v/8R`\G,0; M0R.`q^BW+`nCF =xX8'\ ^Jo?, p}և#Z}y~<8T0?[V<^p 6Ta3C1VP=f(.e88# =H.d41\>c)1>Aq`q] %9 \;Op/˃Γ2B(Ġ= Gׂ*' Xk%vX 2۸`m܏xc4s`#ܿ=s]Ϣ g>\xOy 3a7Unsv`0n)Tu{ἭA`_VprN@H9pԏ?8iq@<8A%mp!zXa:my <lT<$by8aZznaOGr ́WPi3m;y'vM5 [8% * P N/% d!8 c/kg(Bրklܺ<~ ˕ z3$;$YVs^P8ـJ܏ M8q?A''/ FBD#G T/~`ݷs?2scG.~ր>M5W$9l?Mx`WC}AE!plJ% Ox|^ۊ=`h_S]${y*\8Uz>C!X~a x" y y>Uࡅ)9]fG0dۀy31qZҧ \ 9 c8K>ѯ0VVy |YZ1?EB4^૷HBX>҅ ϊ{PG׋0C>y~)Ypo>J=C$]>A@УĐ=k}ƚ zGp>E*}W#' GdUuXڃ:#=>g$YzWIe\<G灼oj@^ptN9oy v>WCaewWH z̈ya)(?˥.?2^quڷ: .4p]+ZԹ18 tzegp]D;g/T3~ =T{ 6<(@?Ʌ=A~` bp\!/W$+YXX 9k,hj}`=cmh/}c! }462ҩ 2;cF܏cf,\d0˜}_;.e_p.,b?RX:̮x ʪƂ3,ph5?XAk uj8e\PX`=c!\ɠa#c!8r4XH<l* !{󽌅BK6A~h#rc+p c"~ ,q}B~h kB?`X.!~̏Kp.$gp;]KHr6q2~nS^4~Bp 8SiS?7 }>KU [EL`\r}sp$$yV7+^H2%)KWJz$_?K^P-$y\#I^J~~`WO=p$G^2k|,|Z5L<Lm< `7<~<"QJI^IhG4\_`IFIl0Q'$9|qol ~.H){LIY`,/.]n* aX'ňNaʨk p \6(pcG.7K^jKp韸s`$ RZ mp )I`\qlŘHX(ɲC9E&3˛ X/c:GffVLkpg 3AIO tob& k#-=?x'?||b|rrXP/b :{݀•}ՎW˜, "]@9УC,Q͜Уhv2;|-~MoA(xvqXbq2b#߳j:otJ<?[^y8h)Ay,8ד\ 4&|}`mpO}s7)2Nz[t<u#g4H j_ws/O'›:;^>^hF{rѬiӐli-u7qs>M Cm궷m-y/m|"'զ;EoC 9sG|4? 3鱹1Z:k)9|Cyq?2S]!ȩPͷ<LLiսX P^YLq6ou="+-AeC"VowZxp;Q{%C ]?דʛJPC d :2t}R?cm5<<_'&Nep`"T)@'5?΄ˢJt ;w2 '$)Dn]Ӣ毭pcx =@b9f&`kkѴuVi4's?ұ'^,vŬz :C͏W3FۧҌ_jginWߙ)"hvB–L ѴWsm. =h9{{>R-5G[.жִcaS*׽OIegMNS d >HƉ$r)=xxYpA6-j,9_[@L4e'9ցj?a@q<t̠<7ۙ G{8˩֙nECǛ' І'8fMG|S`Km(ܤ4RM5궱tqёFmJ?Rㅥ1XG7ޣGזr|-oe:0gg=M'R.f ~?V܃7'o7Q3M==[o7mgHBa>k'@/;kbs|P^MC,F"kŸkδ͡dK׃8z+sX85aFNElpkǖAO=[ HdtyQ[sQCvBnNC:=8 K4K:=ͨAZڌ(ԘÛ^HƟ6Ee9?[Q?\ ?GߓFN f^}Mk56 ^}=&;ZQZa d(ov s>>_ێIˠ=wUXn~ <5y4Lmn8{ϻ\'_j%}wO~)=mnK . *\-cޝ7j O|rL?nܞwTnܞs{>ܝ|5ݘ=o'{>CFC(=w Ces{4k{.p?szӼ0ۙc_о(2'۷&'s[훶96򤴆so`ini+sN4% Z$H}g{}Rԅ@K1?{Mr_3r0?/3"x/)`dCX'hy$$_Baʍ(K=z$-mPӧXZZ6#ȼ6ʣzi@8|~E}'*t92ZϭG!d}Xm:,gD!X=b 9)e<tc;pc'1)n&֗a2¾ؠ1L` 7~ØMr&,m_`z6hfikl;a˕?o+*ϊavӄ_?{z//ֲe toݠf[҃XWDz g 'e>lCv5fԩLmu"KөzVo07fwkdG7mʈ7w-'EKI4l)W˂͉l^1WmXxNbA☢|Y{A,ݳ$fAs=+h9/b,d[I+=BTx: | ( v?jP_8Fy}>_AY޶*%N+a 2Rw<ǰJmP],@[% ,k6*S {h?LɖJjz#LH+RI|.KKW4tuT@7#A2`.&Lۡ}}`b[=?0yQb{`rz #OLoKMFm8kWO&xMMӊ_<4qYMԑ> 09}g'twJx(=C QI=AxGp>9IOo(ӕm˃C?_;˿=_{P?Jso~D[rk[h?=O|vpU_:C#Un*w'yta.Sw|c͝0ٵ¯e:ƌeMuZZDaC=ƞ ۡY,Rsop3;V}Q'،~X;1vf;V;LГV4s? 0q t^奌e~xϥ$cf߇ڼ*m/C۲ql[;k4c;{t>Y~[A ~I zE ThuxYceRS%ZV:T=FlpNgDZ߇LT\|ƒ{jwscKj;haF82bH0Z&ReMb3aZô72$o5/Y\LeT1$-'¨9a&3D2րy/[EENԒ>Ѳ)rGk{8Fq?ru/<c `hY --*+`{A;n4+A hҰPwIN}vG!EHd_f_z u7h85 caSuXW: 'gxXx~E6f9,|2-f9B`kyv^G`sbV76܌:)"/j㹮ԞDoY,ԮGNWkZ@dVr[@P,cx+%? jDP9";~Okݠ͉AWJm6`52.p?Xښj`ˑf: aD%YM&f^2%뗚թOy~HI`=dO~~o762{&{=H8`m|="T+1&?+vnoI֟i3s$[fDŽȑ>qtNi\zWat`w毂~EP :?Wj]ԶonDwD72~ [+[xnZkmdSm~-[: $7E[z}W[} ~Gߺ}cP[Ѵ#ݏ-_~6h]kc?Dh(q_n?R=\Kk+t \f-V{9};~%.|_o-_ĦuﯵuY:K ={z_!jC Q~ r~^b`:߉뷝l~[g]hN]x_VvD}|)EB.&.Xڻ|X:" ,? (~?(isP\~_iO[7Ɠʥ#xQ 1?X?c8cѩd>S}$}j{}$>O6bGZs>O64D?a'rxj?5]f_]ңۡ%dX{vKH\vNFɜO?YM=[=銒Ϥ>o܉Oڰ+ ۫/Q'%뢎oJ?r`mPF Oi*TO۟mzLTiSi>Ri)i^A;4?m`mП? O۟OC۟OG۟G۟OA۟C۟K۟OG۟Ǵi:ik%t^Sˋ ~t˻!#CCM?KČv*!-;~Mrf8-G{=K ^Krpq5f.M-">j}k7)F%u1 $q-[oY{{s1B(q޼uׯ9=?G"㟗{Y->fGb*_/3yQ|ƷU+jW~_z,Ʒg"ߎw~kToǚ}ҝ>*Sh6?Ϛ6y2YIpU`zjvzz6NMdغX[ngGAbhxV,YE]yVXe g\0O>'X~[n?\|ŗq{ gTcϫa̳v*~x`RgM4h᧺\MGx]) ј}Myw.& VaܢkVo]03>|^]Gmt`r}Q>->vpڴN'J4QSqF9|yK7bow]O^i!bE7w>?( q).z3cht`mªmy';gyvO.糷Nmv(+mG%)ncՊnά'p!CbZ3KE_/qQ@cͼ>2}ŻIʍcmxz+mԤMF˵o䴲W8YibۣZ}Wx-afWm⦷hXqpeu/6>T{} Kڱ)|ķ&xW \+3̺$9E$UJ(_~?yk@ND; N WoM>%jY,k`[q ޲>b& x*ލl;aˡ;^g$ķXEY3+L%;L=nnE,^b[NO;h^V5[RS 5;^\VT)9Hk-vKeHGYsbh-:Ewϵ?aY/ۈݺGNxUs 3E׏'Oyѷu UjRooeUǿDcT3]&++^컫J Jƴln7MkyJGj}% |~!<ߌ.~!/uNVBlo _: sy/dI!/d'7~&mUt&wL̛^V1-ɞ)AX.X3 WZ39s;hT~ )ZonGKȢG} ~K*!-}U'؜%dv[+KKB۾:%$KgTΟzOҹ "SĘO}"WjՠڲO$ЩOD:Ul rDjnqؕxxvG7~Iհa?/48WN| wd^@zyY4d'e} Y7KR}?gq@=hSj q};˳brJ>_Z1iub`ŁbfNze1i]tv19X}nx1IEÈBbr+<*iN8>*XcXL|6(|Ol;\{Rh0E$Rߎ''kxOb~m{7=1?dV{CߓuN'{2K5##*'aQetߓYV#?*"ӣlRD"iѴ"?"Έif·DU *"Cu\֏)"jv׿ll>S"2F-Mk;VV 2("%N#-3z?G6=;94![ ;c3#^:# Rޑmky% 6|Gx5y%_|qY6KB_wAr/lߑӧ=icGV4ڒ9;*U{NBhBX]΄7{-+$J_1dJŝmM.$C{:_YHVBr~ǷO .n]H:.ۼ̩~Bҹg: IEAe0 DS1ڵDwkamn( ϷOIZvtWvR}wfd2pxȵq^UAU@^{|5zP0X)>xDO6] H Y)Z={n^@>Ԩ5hVenDESż랪S@B%.'|I2PW'*'cTOI9^ >'^ tv' l'gk.lO=vI>S>5&t854?.չ[H>yXVo>3qѴ]bOzW^_'OWԩO">Ո_=<櫜dOn)O> ~|/mGa[F<~Kk8][baoI,ߒzZ{;<9?4r[|u[ޒu岪{KlSv]m?3u[R*U[}jyߒIZ54)?-1;_oɹ7_2--o<,yKʥ7$Ejn[Ty`*rkgD)9ǚ/2 PiHg#iH 5dyvw5fݽjÝƜӐ1-bgoQkHykȾcҐ-Ө?>#/_w7v}v_kفpv?j)>*~ؘ/y2o,?5o"vj߾~KY];(MR؇wX'Wa }7s*Ah{^qh *+FNZKW1=ǒhoY=R'c1oΞoelo(:Ȟwϟт=R9 c99N4c'rV치{>&ҏ1+*{^(vQ {~!.dzɟMl?FF/Υ=s5M˲kBa:'m6AoY]-~Q3T?y_ cXC|.#{>|n6׋y]"FY3FTNycQuܝ=KϨڞ͟칞ryŨ:Q?Ԋ1H(gQ<(èiY=SY5gTf `˟ >Q?gR3#S.2^pBjG>acاF͑2칩rZCzUG1s4F/wgasV-sW@FM,T-f9-'ζ-aqm31 ]+]92 D0Ew+dX{QS|\R?y?f ɘ屆0=ƌr%\*{ҝQղ7dưƚ&ŌYePQ߀Q69A;\9'$1uR5_ûoϡb ξϛ=aT7Z^]d=3֝<3{^:r'-SA1 kՕWmngYL(+,2lsk#ϊ;Tc({Z9je&guas7=;! ebQ^ -]~9rozN/ ۥ 8NJbnb+)yS ;[&&c-}>_f+>?0f⺇`C-<}ˊ\ "V^^l>@yyh">Xj{E^QlZ([,fN%}uz=szSuރOͨs^`/K1䥱Wi[#QYo1U{44i$6銕U}r83:kϞt8Q-$ߜq|Юˇ\bZ=*7)Վ|kܯ}އťf53ʋ&O/~y{Ł]ě]aQy&vL8<|Ԯ˲N [qRE1|鬚Of?r+pvŤF:S 'S7ى+z )jVϲm,>V(-VPbCМ Mڎ](>'/3Pz_yH4龠绮nţ-j^7ta=n]k-rL.f=^Q/jbѰGs֥ %ءl@4juF5DwnwrKcDWv}tv@1tp3[{툷 sbQa<\_<7jFz V&geƟ~٦ڴ!%RU1S,o$V :8*Mەzk|9x1TZ_^ԏ\vћX#`i1gEYn8薭d`sAgt7ޣh~|q1Oyҿϓ}'g%A ǻpLJ 3_ oA~ =%QZWm J+e@sP-ʠ5hX :9@C 7 2 w{B&[qk;\gB 8h/  ApCBg8\((0pp"&EMCX%\ ,8RsbpbF|.n0]?-epV|ʟlW^0e.+pYkv=_>\jōÕMk*Rv )vjrsͮuc\f)+ʙ+|cHrP~繢ɳ׊r&Gc 湪.UȌ(9u1YuFُ+V,nHyO ד'—)B2+WTv8+b^ŞGʹXUms1Is7*E޵VZ_Yhfhn'Pdػ}5Ys]Ytl[ \An2n:t\ɭFQ:ɌNn۶Q y}ۍ$p["0N#S&n6M{lBTxoQ|O"vΆnW؞nuDEz=f-ܢ9|,+t7yr+KT{fhJ^f䶲zŅ7܆r[=\?d[IS䭷V pkޭr[/Ƹإ%ĩG"\D-$7`ѐ qoG^IJ/H3P)q[D<",z xy C&.Ko7ɔ׉y5R*QA̫t\x8̹@b|Γ;F, I|]Of'ZMc㌣D60y."q ^4rb?_wdiQf/hiUi^}7)J4EZ$ wR O.}+!9$4`yLNuLMDؚDLom$} 7H$Q"ɐruܵQrd [MrWZV+IzHwi鴜V-#&KIR2{RbRbw"rs&b^baü&|浀_^0W1G ~7"M"t#H{7_,܏ H<,ΟGqyuo!;sBFW!.'ƀvFD2=Ae }͈ + eDPù8WYF2V ZOUkCT[Fkj-g0\]퀰5@:pNp _#89R)KR9%lU/ kRe}dW_x:sLקZ,j[],t۸kH+[*,"),dT$>yUaX;K3(oE|E[3׷}^[)g^z2E!x.pb֥ۘŊ[%7T ;nxYxcCߥ׊\[}tZ1W9Ny _<|8X]W`[YEO/VpPTYaPV ~EjDZr┉:N]]ެxvғOn)i(f\w6V8Z+bW? "6)+Ws~y۸;UKXΝW^=3mg4 âb{#;y4r\EŖ#1q7a;eܶ s6ZhrɭVaȷ>q{S-Obeqơn3+^lZHKv(عt3hY+\1ϿOvׇ33 P?O.d{}:vW\Q ݠ9MYP"OkI-~N周?~;,.e'Oת>C*_U}U}ʿWa*_~߯U}ʿW*o~pDD`^E /Q%1^bDr2ĬR<U%I%N QS™!1UJRbDrvOR|U%I %Q.N QQWZJbD`$II$f}◨/Q)1LD/rvb)S%$KTJ ,K\DDA潔 U%*%I /K\DDAHJWbDT*$K%Q.NL QNJ_bDT*&1XD/rveRJ!Q-1U%*%I .E^JQ&Q)USKQ%1^$K/E/rv((&_g)fHTKL-ERTJ +`%Q^ve(歔R̐.RTI/Ee)I .ER(/ER QYQRL*RTbX))zJQVBi~Hߏcm6UKߏ!dg&&-L^'C~L?r_&Vp )RbB2n腿F eY7%|?sy 2sZAă}BLjĦㅽ{^ƽR J !/fuݗzB9L~'9,=AdH~mڏǶ",]&]Zx7yϞby я~1$ E9zFaʃ{Iߏ % \Zt'_;T1}[+~zH){{d'q>-Aֺd I|]7G~LrϞ&?mM1TF>ܲ|ћrA霹M;ΪzIwzlHߏiNzͳBf:>[~Ls|Pӽ}"GfƵ'kMvm(i>%z.HT+t;ڽߜ4#5zQiy53ziS/;f0I?^áÉ޾iv&^MSG|nEnqa$,6 &'9-<1fjUpKD=b{?:/.{ԛףA9p2ž Iu?* ܼ%Eu!{Um$y\-țJ҃\޷@bK=LJKߏiJڽ|@F"T ӪgxmFw#=@&C~}y^g=ɉv>_s`q4+u'zMx:^)3<zL^qϭK+[w IOE$1tk 3Gibn;K v6~ƕ |=,)ͣn()6ndܾvFKߏiLeFrKn}]¼5{{.z1ķRtO4_s!d)k$3r\Ð+{ҡ婫o;jߋPܩ~v ԅCz O'o*%!M.TQ|ňa+F$g^f{HZ7uy;c’c$BmOR]\L}ۿ%'OO\Ǥp)[oeaN1K&*^O_qܡtZ^ ۟Vu #/iDY-ȗtZɽ&Etٱ1U}gcM3rrXoyt?YՍo}dokN:{!7,(_Ћ> 2"0F7辰Z` }`զ^{}_חlalVyNyܖ] v/\Cs :UpLّߺNm\,ݯPte/w$ݏ"cRItՍv;?}We7oN[}Z98g5dT>:HSuz3?eloD_tTbetS?*Oe/I1jtԮOى }OF~ mNN7Uvkl)}S6tRlV(o/Xݯdz)ۧx*?iDD&t_HdK}HU:츴;w,U3RFզOקFZ}D1snq}C wR>}B "A =龠6@Wl]Ky= j4щYv6w,g:%辞7 <+3}< KS Kھ ݧ[t΅ 龜疧qbSO|pxoBt]辛NM~1>k֬k~ofWc]5V׶tͧc3-G_fvmC 1'!X#;a k_X`kX_jW{۫^}~tO`vA ̄Rk[ӣ|.tᯏR[,;-y|f Zj ӥ! Xa0J؆ `2ۅYU]C dczw37fm٨XQH2N%Y%١]hY/Tm{4)"0jlwYbq0"5ģТ*_FPBJV6QcJԊ1JD~Uxy@r6YWǠog0 c2Z'Š0|`ܶLGTȂ xأ %avTQ }Ͽ?~??t󓮮 Qԟ;jFe9T::M ӵ6! uufzMXk* Ք6InHk~#u=KFyWnWW8Q|~|JOWGW״rֆE73_BV&64$a`=nnv ;z!vF kظ܆{Çո:=W.G۵2zIjrE|67Uټq6^Ian]WL{2lp$#J F]9z mL*c5xƬ%;4v՟;3͕9N2pmCً]ٶ UkU〕Tٴ]~kTd;ѷ-ǹW8I/u}7 ՙ= 5?m?gU;}yYxgٯBݶ4t.p"Ig>{Wxv*[Γ혓]\??ffǧ2seNj 5ʺvuGsvՅyl(z/괭o&754ɣ6P~*y wF؛ac< ^HͰ 1守Ez-h+|7r[(_IE޻9qUgjei }}t?t=jOTB*E* U0'%ҋ EKP"[TDD(*E |Ti4QPDQ) 7(v|ws6}vg\P/F3˵/Xp 8p,#t K. XNt)5-˔jƬC#E'__CUλ!G:yamFfAu.'bRl[^;Yj|*:MI`M/lC3Fs ZLv2F{v[q3#Kl?C/ /7*lI{u.JnFJveNNE3U4z}hc^%i Ztq.R9/1vN1.%GXzdˮCzZĐh&Z= B#\j.?1>)L674cH`CmA ܟg> S@ctybUHkFw{>iF^n|=|~$3OM"j ,vuq>.[< ;@EnD]Sw iZvV䀟Ll )p/6[*Zq w<g#w_%,L',]ޟ [b;Nq8/=??ZhŬhsFe44WiWݪ>Q@e4m"Z4y)e$\σh"\jY 0aX)t?q1)<w(2Dx}ڒ>;0UaW+/$ Ub|6WܲQPߞ&'-^w )w5ΌjBhfvC *k-)69e`p/xSTU-A](;rB,0[6ߎy1PØ?N[cE׆Eꙺe\ĖmϞVR ŵeqrvk%6u&@ӖQ^g&MW8{aKw'jD}sLF(e>M'lb۟N7 yĠ"dQԇ &Ռ%5[ =M`!n)> vs#{.zDeیoѢ* B WJȏJ_ګn=RRL~lv"vI 77uc+/e'BA9ﱀO:xLG5YUx?|=m=m=m>lZ:c1U0)H8b!;izn?qAtCvEvGxyEr 6W+K9e/p>p>p?p@pAqEvK~PTR/>a$H8co?q?r@sBuExHzI{K}T]_Y.M.Q:hNpx=p=p=p=p>q>q=m=ms=p>q>q>q>q>q>r?r@s@r@r@pM=j /Fv%QX]]Zc`Q}7b?{ۨ>qq>q>q>q>q>q=o=k;>pV?r@sAtBvDxEwEu^@l-MOFW\]V[UKw6`q>q>q>q>r>r>r>r=oq>q>q>r>r>r>r?s?s?s?s?s?s?r?q>nK:e@oExqG{I}J~I{t@i EtQSQL}NIy6Z/b;kU=o;iڰ=o=q=q>q>q>r>r>r>r?s?s?s?t@t=n>qd?s@t@tAuAuAt@qf=hDt!H{I}J~I{AmBo MMK~HyHzBqU/_9f!=nq>r>r>r>r?s?s?t?t@t@u?r=mQ>n/:d.P?q(AteBvBwBwCvArIq>r>r>r>r>s?s?t?t@u@uAvAvBwBvBvBvAt@rZ=j;f BuHCwCxCwCu=iFx;G{FzDv=hDt FxDvArCt?n/R:j;=pq>r>r>r>r>s>s?t?t@u@uAvAwBwAuAuBvCxDxDyDyDxBu@n>l DxfDyCxBu>kEv2EyDwAs7` CuJBuAs@p?q;g:.Sr>r>r>s>s>s?t?t@u@vAvBwBxCy?nw9b0\:hBu'Cw`EzF{EzEyDu^:aCv2DxCwAt=j!Dv:CvAt?nn3WBt?r>o@p=l.P:i.>qr>t>s>s>s>t?t?u@u@vAwBxCyDzE{F{F|F{FyFxIvG?y>t>t?t?u@u@vAwBxCyDzE|G}H~IJKKJI}|Er=j G{VG|F{Dx?mCtCwAv?r:eCvC>q5] @q?us>t@|>u>t?t?u?u@vAwBxCyD{E|G~IJLMMNNMMK}c:]Gz.G}F{Cw=jCu%DBrt>t>t>t?u?u?v@v@wAxByD{E|G~IKMNOPQQPONLCoFy$G|FC{9a Ar3=m:7b Bp6=n8b2t>t>u?u?u?v@v@wAxByCzE|F~HJJKKvLrNPQSRQOKBmFy.HCyo:hp=r=m=s>u>u?u?u?v?v@w@wAxBzD{E}GHExb@o#o;jܡ=ru>u?u?v?v?v@w@wAxByCzD|F~F}Aq23cJ}%VNS'AzHy RROKAo?o;j=swA=m?w?w?w?w?w@x@x@yAzB{C|D|@p@ );bU[WQZQqNiK}G?ni?z>u=n?w?w?w?x?x@x@yAyAzB{C|Cz;f -M>k@kJ|DPUYYWXURDq,?mv;j>u=n?x?x?x?x@x@y@yAzAzB{C|Bx@qREz~HKOSUVTHy%v>q?w?x?y?y@y@y@yAzA{B{C|BwF~HJLOQQMK%@x!BEw@o>m;iڢ=uq>t>v@y@y@y@y@z@zAzA{B{C|BxE~KKKMLEx3O>m:gpp@z@z@z@{@{A{A{A{B|B|B{CzEFDzK*K>l݀;j.O0e?x@u?xA{A{A{A{A{A{A|B|B|C}BwC{?t$6W=l:h؍'O=t/A{>rA|A|A}A}A|A|A|B|B|B}Bz=k0>jOop;jV9`=l;i۝.OC|!IF{HHGEEDC~B}B}B|B|B{@to:b"=k;=lr>q?s>p>p>p;j4-Ip?s?s?t?s>s>r=o=o9f" ;hcnPOJLJHFECC~B}A|A{A{AzAz@w@w?t?r?t?u@u?v?u?t?t>s>r=p=kݟs>s>r>q=p;k\=p?>t;jޡ5]QVRMMJGEDB}B|A{@z@z@y@y@x@x?w?w?v?v?u?t>t>s>r>r>qr?|ީ7aRWSKKIFDC~B|A{@z@z@y@x?x?w?w?v?v?u>u>t>s>s?wA=o8e.Hu>t>t>s>s>s>s9i'7[u>t>s>s>r=o8b%:eb=mu>t>t>s>r;jv;egq>t?w?w?w?w?v>u>t=rt"@y{@y@y@v>q=o=m=n=op>r=pu[>v>u>uBA=s=r=qi/TRL,;fΓW^;oG~vY8l OМ$Cac0<3wJ8Vm`{c]f(G~v䓟ԃ}iW_l.?am4 )S´_s\`rd?afKG⫺} a~c˾ԞO 恹]E֣^қ;idSmp |@"De>Ik_޿}FŠpP~?P` ߻Bkݳ"y\nɤ63·IzDBN( ~9joT2&bqWiiPuܵXj7䧛?z ث l"XDIDŶk}x#~tՃB޿&vn/ozFڔ>/IBi0>hF.c8-]U'5n__VŊ0&עo]|)~՜^_7mmE%=rHhLVઐ%&Ye>2ĪSzGc+4Q(+^xAH=yOXiv6jc {W۪b߾v^ + y'(qTЂnN׀{*p>p)OmYXgVN{fkM\nyFuFqe]Fm *A/#T® 3rKU#hё*O'o=l:?{fH{RzMӖ+j!R닃r.2ނ HkU6=Gw'}5,W'qضO0BJ,hvdM= 0 @c>HFk/J`O]O :k+"pH%Sy4 ;I1%m%^}ڗ3"ys;2G[s)qQ` ,њ÷$AF mx~0V.㎝<@$X (Ii.) 8{>C+AP~@ $D$\헖l:$r,1Q E4,꜓3 d踆▄Feeo43˷Of_(GՏ.-S> _%h F_Gs tvgʣ1kM}g5Lk $ -;'xYCnЗ8e]s;tş UTwZo֗rǤN[=e=$CDJ"A{϶fey?vň0JWjLB|(V )ܫ&|GJPկ1}%( ~e3<ًϜHdQ"邰;`1UaAҝ-do瓋9Xu51xݻZI10r U]^t%w}\h/}x{7YBؖW\5˴1gkC 1)Y!2ݟ ,mP""B3Np䎱AE!6:J"=]ҩDԯ3}FIKtsidQ7M)> tgF0Qkp E,[PyIo/ÛDo[@=<ݔe \8GcKg\#Z&',vA%MndzU+e'VHBh8"OJQ +BlKɵ1i{ú’* ċ^]՘o:[2}*M~u_M4@ Te;E0K[@;րaL3V6o- rWƴev=_M{퉏0,:Mj{K#՚a4C ~1 fp&Wm%rȘvvvj˞k~0,?Gb2<<o9?(,?]A= _ui;O9S #@~3~fk6Y!_*/(%A 8LJN#PAcC},.m`5:sn%v,,bꤷXȓxRf4+R!k*]q4^ 7 >t%hտzz@3np_m=1b ;vgAtѠZ\Fke~}r1:=Khh@a. ?KCS4fVF;F Wɲj7:*q̟|0؋ڶOޚ~ט?lܮ ]Y3ØXmU7B{. ?6UZf&yS^HWR1r oIFAa]ԮE p15ö;n\-_wB ()6(2`"@ IނP-A2ͳ1=|?߉8SIP%*{2< %1k~G2&`=䀴w:9_mi>p-,3hν M^Izջk_*bO$a Ǵ#{*Ƌ'k2sH VXsҞ>a|(*6񷎴%ۏ? +dc69 q6FfdMr p%QpA;7Bk'Rhؗ3A@d5?},#!E_n?aѦ5$LiHo>z0kdOo=ޚoӥsWmv5J=y锷-€uu@ZBDIO=YG^?B|qa%&'o:Rw׬gwօ#ea/3zw/ʂh^CA杮G<} OoL7+ H'%2#ݐI48-w))?OtZ4YtV1\ʈEݕKdE[ 0Jx?|=m=m=m>lZ:c1U0)H8b!;izn?qAtCvEvGxyEr 6W+K9e/p>p>p?p@pAqEvK~PTR/>a$H8co?q?r@sBuExHzI{K}T]_Y.MT|NymY>o=p=o=o=p=p=p=p>pcOMSST`eaMInT~pmAr=o=oD>x=p=p=p=p>q>qq]VZY]gcR} `\?p=p=p=pA~>s=p>q>q>q>qGx@s?r@sRQX]]Zc`Q}nZIxCt=p=p=p>q>q>q>q>q>qP}eL|BvFyk-MOFW\]V[UKww`TM|Fw@s>q>q>q>q>r>r>r>rd_G{J~nGw;0OJ{UXXPSPBl&Q(I7s.U2\4`6e9i;l=p>r>r>r>r?s?s?s?s?sBu]`I}J~I{t@i EtQSQL}NIy6Zvha\VOIzCu>r?s?s?s?t@tlQAuCwgI}J~I{AmBo MMK~HyHzBqU/_rkf_YRL|Gy@t?t?t@t@uK{qH{DxmGzZH|H|Gy@lBpI|H{EwEvCtkEv2EyDwAs7` CuJBuAs@p?q;g:~xqke_YSNH{BxCy{NEzafCwAt=j!Dv:CvAt?nn3WBt?r>o@p=l.P|uoid]XRMH}E{T^qJJ~DxBv@s:gDvWAu@spBvF|H~IJKOf\G|wCtCwAv?r:eCvC>q5] @q?umBs:e+I";qfSRQP[Cyo:hp}wsmhr|SPN8b >o;jܡ{vq}fROn?o;j"?$C'H)M,R0W1X0^q?ov;k~{?ni>w[?ni?zag{ZZ?mv;j"I&Q*Z.b3l7t;}!?$D'I /"=v/R=hDsIzJ}/N"8bc}?m;j%B}yuq >m;iڢ)K|y3O>m:gp@k6l݀;j.O6W=l:h؍'O>jOTMd>Y>[>\:;::::::8`;gS 6iX>Z:;::>op;jV9`=l;i۝.O;;Yp>p>p;j4-IN>V>W>_>Z]Iz?t?s>s>r=o=o9f" ;hcQ>S>U>W>^>_%E$C%C(J-T5b8i:ks>r=p=kݟTC[>]=^|vojc\VPJ|Cw>s>r>q=p;k\=p?>t;jޡ5];;;;=E>Scz=[=]yslg`ZTM~Gy@s>r>qr?|ީ7a;;;;;;;;;;?I=E>LIZk~=Y=\'K)N+R-V0Z2]3a5d7gJ{DzA=o8e.Ha>]ysmf`ZTN~Fx>s9i'7[_}wqjd]WTx8b#:eb=mTGZup &hŷu30흈۷Kr"( PIP BF`*#$!1!dAlz_%ܼp!٬ynqG「*Gohj:2z= .̂6""QL.F._"ηSڄo;26},w<7ڟޢceUߎ߱بf{ *b䯻o~ Plhر7ZWݷ>L߻7MK,]2Hy`je;9xoqEM?ku=ϱ`jvmgl(f5gc׮(e[u;֯Gjc_5z29!(,Z@L?߉}D5C[0 P)w4&is}|}աKPWV>N?PMn>cӇЁ򸦕.Ο ^aABs?Ǔ%-D]ȭIBG$'8B9MC՛nW/[|v8a b bͯ9q b 3kv΃c^mmƫ qC삨Ҟ`ޥF N׻I"Sr3 w䙒 C.0s!RcUF.rOz}g~ǞgdeRu}/&h`(&P_߻%9TDm"Kǟ̘/#F0>(پ06մD52#Թ u=zG9o;C^6o#H)4d$U}(!wU{CycG%gM1%()]Ȕ.“G|wS*$fB3:BsVo=QF-DM0]RoQ}0aq}b@XX,R((  JL)KWP< ^VGߵk^2H|j&⾀Huj.@a䒋PPT>të#2kOQ_fRqPS _t*BZmjjjTk5jjJOX_ZR;ڼ|ڧ+ȕ'(hJP'K$z Iԧx4h뺹zzz&џ]sz;Vn{ǿw^yǴ=}oݿɦ5 #ǘz*w i 哌O##-4HQk=.o3{k^ ]% u6Nk;tAE7(zz*MR$MÏpnd߽^8T"{du-qL]h6 \kכMȩ;{]G1 MtUӾJXW߽1rȏF޷Hkwi7ѰYp !2Ϭϛi@h#**j~Hl2EۢҢ|s@R'W'VF&Z'Ly z/si&W-h_*ZKo _|o3tB9nzZ+7U8z?(y{H+%ߢB&tߖ h x? E39afS9 fo|tH"_!\E8{V0Ln3R٬քdCHccp1$iOkr Ƈq#C1I s&~l-W y<~^B2qG-/eJX 98iN61"6w.]!ӁA{קMC{6F DTL F6*O\zns>g7x}>S/;qژ`|o:fθӀEȥO#a3l >A\%ws3Qp?.C\GGA~"Q%?D`*F,8pͪ pIFIaSe  PRp,#0Dv:ɏ(~tvߥ'A&G0rYl̷ua@!4e4ڲ!mِ|&=;ʾ[z $?>n)S| ƇЁ{FNoWhnqo9Hʯ: 8Flt4ՅƑҋ$?!Y/ b.e[p;G[1wj~&67X_:;OWȁ]`E''gR(LzEGśiNm k[jA˥]cR9\G7+N>zo f^Okcgb%3Eô,׾C͡HLFv18P󣻾߂M> (d]Vg7$ƴ`\,Ё=HTNIPXTOd&ݞ_s5cЎ2"̜Kw+q7(gyW".jA$A'+Am9yr̼3Yt}N2۞DS.\!䕭trGEG*gO-;TCy]ӡGbu hu;Q\֦PQ>X 6A\0b5W:0I )o}l س js,?^{}l><z+Jg=yٯgɈ3;|Pu넰N"$d0hCU7qVTnzArL=en,ܙcޱ#ȣߣ86DTOoxT;{ws ^rNW{6xZuD[޵ &o /B{{>?a,GJݛ bv9.:ѧζ%.[/KD_VicLw=<;rUh[\w7n'z.\pA4$A]I6 Ue WL8}6EǾ^|ٷq7w rՌcg3Q=k°j#bxP5!(u._߰x_Ż`o]k*Sk[JS  W:ze1Y0_JI"yx?"wg- 2p=- = f,"}uٳ;rUꇣO l_i)N^oɰ:!J kVa[X8ۯ?~9lٲd!{7Sxp1iǑ vx?b ^=]$g]?L8:@nhmW,l#Jdԫz73Ztբ+Κo*u\ B~p՘[U"12y#7d @LP"bc ŶS¶uj> WlFo]zbp;'2F.W֝>`C\یȂP6$X]EjL3L}cOksou?*mUK;M PDcXj !BH&l`(fsPkVe|Ʊ9mé"#r%SPF8HT {j]5odIENDB`dist/nsis/Resources/falcon-ico-fam.ico000066400000000000000000000226761176363201700202410ustar00rootroot0000000000000000 %(0` -,N7_;hZ;jދx?|=m=m=m>lZ:c1U0)H8b!;izn?qAtCvEvGxyEr 6W+K9e/p>p>p?p@pAqEvK~PTR/>a$H8co?q?r@sBuExHzI{K}T]_Y.MT|NymY>o=p=o=o=p=p=p=p>pcOMSST`eaMInS}nlAr=o=oD>x=p=p=p=p>q>qp]VZY]gcR} _[?p=p=p=pA~>s=p>q>q>q>qGx@s?r@sRQX]]Zc`Q}!,K}4Z7aHx;m=p=p=p>q>q>q>q>q>q9f$5]-I7`>nBvCvk-MOFW\]V[UKwu_SM{Fv?s>q>q>q>q>r>r>r>rc^G{J~nGw;0OJ{UXXPSPBlzh]WPKzEv>q>r>r>r>r?s?s?s?s?sBu\`I}J~I{t@i EtQSQL}NIy6Z(A.N(>u1T4\`7bU:i;lr?s?s?s?t@t&:i-J6^kEv2EyDwAs7` CuJBuAs@p?q;g:{void^XRMH{BxCyzNEz`fCwAt=j!Dv:CvAt?nn3WBt?r>o@p=l.Pysnhb\WRLH|E{S^pJ~J~DxBv@s:gDvWAu@sq5] @q?uo:hpzuqlgp{SPN8b >o;jܡytp{fROm?o;j$6d%:l'=s(?{+G.M0Q2U|7^x *B"/]&4O?fBm?ov;k{z?ni>w[?ni?z!1V$7f'>u)A}*E,H/P1T.L""0HxDmUgHtVR&7?mv;jtnkjb|?m;j{wtp}>m;iڢ&;"2W#5_%9g(@w)C~-J/N.K2T:gh *3O>m:gp@k6l݀;j.O#3%7 ,I#4Z%:h'=q*D.L,G(>o6W=l:h؍'O>jOop;jV9`=l;i۝.O5M5NN9zz::;84L4L4L4MT?s>p>p>p;j4-Is>r=o=o9f" ;hc=~L~Fy@t>s>r=p=kݟs>r>q=p;k\=p?>t;jޡ5]5M5M:@::<SL~Gy@s>r>qr?|ީ7a5M5M8^Ⱦ:b::`m;>TlZs9i'7[_01"u<0owɲPn4hǽ4]Z ZSq8$ 2mxHc_ xnD((ZI$+(Xwہݷ.^gWTUE4vCo_y).;xmڷX*' i#i+"V#ƀ2!~^;Sݲ-a= -$N5[wƄZ;㲙,W4Y\@Wv<(*ՙ% J"Qit"p:ąk4]+fIzL&Ysz_z{{]8h< zo`^dGt#7DŽcܽreL5Erp:[pQ&,E7re{lvbUG|1?Z2~'w!tߘ,K>?@{\^y9 UΞռob b |B,ژUq2$06gא=4$'126k"AFExKF>sek~{`TEo]2cL@oO 3>& eBŧ[ؕoBkh< <'x 2w3Q|8X 4NCLq)ӫ}o-]HB8xX7FD9llAv \=̍ (B 9c{[9\e-a;J+@mғܣk=f䟆ǿG> |h<(EOϦgwք< 8%(QAf1f硉!"_XwWq+ ajZ<`D g'2ttebH_8sݐdڲ/~ej|)*<0Dj-4ebt=o޷΀3UgM?(LϏfg,::$wλ.b0X"2NN/L<\p.[>rԇ 5 뚾̭O OX}HnችKi+j4i.z6"…Ou]5uz{.0Ѧ>P0 z{w#g>͹L1Ĉ"d"BP:"T!7|* jQ_ [sۥC% Y6K&-^^{/Uz‘KV,@NM9g\m> 7b""^)xP;@%'%X'=f?˷ ]K۽(sU[[Sy謩~FERX Q{ѥwǎ< 啾бmd9oޱ~\?*[3) &N;4Θs e~\kG V{? A0a" `?@}8@7n\n|ڱ?iW0D$˹'ĂDEܛHCkĄ 1#f1r1ߑ ?Omh1fWw6(1aALTv[xkqhRe" :H~ٸgt)Nr\z:G_IݤA驾D9Ξدⲯ􄤯-?'\oq=N̿b>/^Jˁ:l\A~4̨iHbHU-i"'477@ʹcp֠L&blQGH%&B5#@Ix s'q3Q.mYië/"rU IBS$pq]wp<$}EW|y}ĺAcc#58=Y8+[n4+«| xB`󻮧iEXJbM"Jb%f^W3 /ln#7<(OtZݧ }wZ}gcH}p˘SBtz>X䋋W\^u9DpͫNb3xڒrPr$[ (\R[o\ůn1?Hx¥ϓsΜHd)iijb:V5`,;3墟/W=0 ցs8mH#{,7~<ةZz}:]kJ;7~/p!X]Q. ]d~ %:x IC۰؆] lpNfWMל4=iwd.YW8eK0@ >V:_ >9tE-ѱ6:v݉{ ]5h߽5ePd@e˷N~ۏiZwڊd:m2=#n׺:_Upӿ^1*7.`폐FHtѩ'+HTLcF D#Xyc0K~wrGcQ's${87Jn3BmneĿo^HLMU=̦DQ5/"ڌ5^'JRZ"V8NUB ]^c"&1ܞ߸hIȩ&F;/]QMo6,Gm[ aDśz}TlVDHAE5v4L؆9!NqFUFNŸ:o䢯~U;bE:rݺe_TY9SJ] { s徚+DGs};:(˕? VA{M|E/O{Re:yk,f|B,6;G}G~])%cՎ!H鑄q2%RHJٕWw)NExL&_QXUX_SY; <c+N}ƍ +%Z z0Ybס&%(wfQN=tzWar.p~*Q>&ww.:'C>Y~e W"m'չġ\-|6dϝtw%ѾȶbqX^@ʫgz}i/ӟZ~"g:~dIcwIŠ'xB6zɷ: DI'$HR)=Q^V,jM\|ﵸO?ooF]唪͘ ƭr}q1Jd/n!Mi"d˒^,VxSםkӚC 3g7jDu;/9cƹf]#Ĕb{jPR+vv6ڝGcLRu3Fz )D0Ԕrfּ^ғכ̚=/ p^@WIENDB`dist/nsis/Resources/falcon-ico-inst.ico000066400000000000000000000226761176363201700204530ustar00rootroot0000000000000000 %(0` B@>OtY?eK1643|u|oyyyXRLMIDPKFthjbY333A@>Oɾ333nd[xg_V{uzl333A?>Oǿ???333tj`ue^Vzuzl333A?>O;;;666333ĮkaXxmbh`W|tzl333.MA?>O·^^^333Įî·vk`uzl333aMInA?>O333ĮĮ¬zl333gcR} B@?O333Įzl333KVYWߤBdHHHO333Įzl333~k\H:3| Bm HHHO333Įzl333u~Qj@'B,GIMR333Įzl333f`qtU3l#g/bBW333Įzl333~v]fyf?z 29f!@a$*$333Įzl333|trYk~ŽF(T  .Sqs?f28S\YYY======;;;777333eeeNE##^yyyxyS9:j3>rt@g{{{============:::QQQNNNJJJVVV%%%{{{n^&#]0&fϻQ=tBlLLL===============888444TTTWWWlllQQQ FFFqr{ncpI==r=m=s>uCpppp==================;;;777333///+++777^^^yyyXXX#"#WLxof~]tPdFQQIҿ8:v=ru>uFuLLL============:::666222...***&&&"""BBBpppHHH:/xZfVnNbHUAAFK^wǺ}u-B=s=DGWmxv=3f4]@=m?v?v>t=tQfiz|'V=vA=m?w?w>v>v>u>u=su=n?w?w?w?x>w?w?x?v>v>t>r~~~%%%!!! ?;W4,rPejz7)N  '?mv;j>u=n?x?x?x?x@x@y@y@y@y@yAx>q4\^ 75E70^tye`$5  ?m;j>v>q?w?x?y?y@y@y@yAzA{AzB{@tDzE{E|v &&&PPQ..KA:\\Sk|ST)M @>m;iڢ=uq>t>v@y@y@y@y@z@zAzA{B{C|AwD}JIHIe 2/w,&nK?VIfTVG@4p'J#G 3O>m:gpp@z@z@z@{@{A{A{A{B|B|B{CzEFDzK8q $殮ۿ# >l݀;j.O0e?x@u?xA{A{A{A{A{A{A|B|B|C}BwC{?t$>6W=l:h؍'O=t/A{>rA|A|A}A}A|A|A|B|B|B}Bz=k0>jOop;jV9`=l;i۝.OC|!IF{HHGEEDC~B}B}B|B|B{@to:b"=k;=lr>q?s>p>p>p;j4-Ip?s?s?t?s>s>r=o=o9f" ;hcnPOJLJHFECC~B}A|A{A{AzAz@w@w?t?r?t?u@u?v?u?t?t>s>r=p=kݟs>s>r>q=p;k\=p?>t;jޡ5]QVRMMJGEDB}B|A{@z@z@y@y@x@x?w?w?v?v?u?t>t>s>r>r>qr?|ީ7aRWSKKIFDC~B|A{@z@z@y@x?x?w?w?v?v?u>u>t>s>s?wA=o8e.Hu>t>t>s>s>s>s9i'7[u>t>s>s>r=o8b%:eb=mu>t>t>s>r;jv;egq>t?w?w?w?w?v>u>t=rt"@y{@y@y@v>q=o=m=n=op>r=pu[>v>u>uBA=s=r=qs3'dB0(c(WyXd( R}uYmai<C(j>ĂPe( !Mr/= XϽYw9~!>E!XM,` 0,jTgUףzeFv4ؾb4;;>7N_r 34.&8 PeJ̔T)dj?wEٖc_uKyd43QlT%AB.AHd2  !1.jLz5A@2iph\ligǝ/!jJ8򅍶j1L@(.~\wZh:SM?m)иx+bẶW\FS) U!~VXHVBC .k{zI,ĶZ]Is2,5{V_e|j؞aW }NT x/X.'RP?bfԫѫ68(P /x(^y)N&Kx)]WI#ڲ4u^,DƘ Jg& 02 LR9퍁YvS]!8TG7k 1Oӕ71/O\It0~ƅ^ABI^"Ј%$$Y>T`TZ*}ݒLb"Al~sqwB!"D%Lb$dqwOzH7R @ۇ:X-EĴ4Ъqo*V9=/{gR(fXyۘr\K&G9 z p !]:%+&[`?>r)/ˮ>y&y?&f-#/y43G8x\*{4q᯷K^,k+e ;E*S|,@̔TBĴC HJH*ѝoiݵ+3l2y!ɢ\jR?tqf0K1B 'l~G&׺uWln5YZ$/n2dU3%CL\DTLW}dd/T`+l̟D e<=7a}a )t )W-loZB]4=ކN=&np~%2.ilMޯ˶_{ivx U;*>016 \,GsV F M#~;ALxJuuMTy!h#LT߼v /$86ui<3'^*ʔyGaCi37[9.zdiqu7~te/cU@SgloUZ XCFD:k{M'$ |`>ӟHA+HP_p=37_?4mKuF> M *]0GJ2 , Jɤ~~]**l<_ROT^{-vڵӧ[^2$>In{' UJ-)Jxd &9Ħ8%7UnhIό~[׮];<~衇N:thieeeơC+?=>dlGHrvx HQ!3eJ2$[Ǔw< E*ѹCBЭ.*¹U !]×]f]6#YUUU60 {|sƍkh׻O>u8LbKpEHWhpZ4WRښc)cS/!tEyMN7 Ҽ Zֆ˗/cݺu}cW~~>ƍ dL]`X&"q{pzK3*kO^t~1H O 撉@Ƅ|8~y<@SM7&}ů(EtssK+q%lذa2v*'"Z⻐2r8Ը$(fH 頪 M°\ 2}~]:qm5oE Jp @I{th.~r%hkks]χsa˖-NIQpݳٰ!pdX P*Pd}-7%`M$@f= .s ,DTuz**`֯_:J+>^U4'Z'dFt5ٖn'n JPawK2! ]&L;@O1=9ʊ % Cm*DQl޼8!ȓclfnb!)IP A# jC'AqA [,dto[G.Y1B8L+Qe4\lh@J3t][o^&6mn?29Yf$z%xPM*8xR*lk:4_\3 YL (TV@ܿ^ጶpDf!l|u5d7o~#81cPRRH\1 l2<ףuA`(d JtDg&D2PSSH~3ۛd\Qչ)I ;o=܃7̙3C_CNLLDaa!P__-[n c }L.x$ C aBGCڔq Ű<^,bEC01#e6:gJj(\!eRzt쨨ܹsQTT]v @'-(($IHMMEkkk_D0a}vFAMI7 Mj /Eڔp]!9-% ѲgMQ"a0pf@3ؤD~I6?vEyw su GuŎN =qȑA$rrri À.\?/È`hݛ!葤H&1F$&E≴O3H(SI@CУTt.Ɏkc 0] oB<X\ 0&MBٳ---}ft:a&OhZu?$A[!@zB^ήHDζ0DHTW  \r]"%&T*@Ǟy*OMѺ,^طo^/Ұb T]kq B!~r&?d|LSuOuZ{&!` C 5|{b$|{5y鴟l#G{G݆N(F-KOLþ v`R8~?x);~U-~آmw绐 _ #8O\E㡣pA0:~/o2Tfe;y!Vf.|eD;:PǷQcs?o$l#C+@ˍU=b0N4@'TL[H{੾KjNA-VH* Nqel46U*q@plk@ pi[ $b0)rO9_tX~)G:lqLv 0 I"l%W]W0ԙꐑ?ƛ C} 0W@ 1(.'<(i?\&jh4Y[ ]58dC@&I DEߍmo{mƥf"sC$RXGG)h@ /߉hPSSs[BA1; 9?hAe=xu!+f mP, @眱VTTtK# 0kPk< $#|phtCB #4bQfq}<'.{ Z&@AzR'29"𭿺HfK%0݃wS}{ZydaHB>-T99wHC(c֣  4ގkpm:Pqg*S;y( L&=z+Wę3gPRR2@1ܿ !ߪ576b:wXsu>*"hCii)RLM)f7#p̈D"|Sg;*v{yLC ׻gF}}=vލ 16H҂3`# yR%ГQ* [q_vQX3~Qe?G>V'*Ãٓ x0b$1sA3l@FV"l6#==vo?ٚFf #7W=wsty9HNNcun@mm-6m4psFE}olzrbܘB>,V8x >$oMVRDu}'.!͇Y߈dk}l 9= MMM}Ed= vV6Ot(1fBz\/:aYӆX#~ذa5 93c ^'G$ os=0zpy|xoq; q> \jt+&cwAՇ@ ӊϟB8Fߍ;@L4Y 3IENDB`dist/nsis/Resources/falcon-ico.ico000066400000000000000000000042761176363201700174740ustar00rootroot00000000000000 ( @ !!""##$$%%&&''(())**,,--..////001122556677778899::;;<<<<==>>>>????@@@@AAAABBCCDDDDEEFFGGKKNNOOPPQQTTUUUUWWYYYY\\]]^^____````bbbbeeiijjllmmmmnnppqqrrssuuvvvvwwxxzz{{||||}}~~oH*)Gn|6#0/-'&*,0!4|4+1 2A 734p5_e:pV%Ud )VV,vfP.Vo%*mt$p^ kB|46=xn 5Y(4|+ yM~4S [!|62']XRA8* #4! ; C2;cu0n0}_hnG.@sw7G'+Wu`[q)&Ir&j>QZ'*vyb< n`*F*9Hl*aio 63*5{'QK|01mJ44Pg?$l"<\bNL`DoT) OVT"zDVl.r$o0'*pK4{3 #&&'.25)5|lE''Gndist/nsis/Resources/falcon-ico.png000066400000000000000000000031031176363201700174720ustar00rootroot00000000000000PNG  IHDR szzbKGDC pHYs  tIMETIDATxڭkligf(ZEC-ْnj5$P!tHv7>l _VXXB/%D\#nZ+.y~8vT=Ly9#(ʝ;p4R^uu qqп?&P@n@np .v#oߢ:@^LڴH~>f=x6'%& <@ׯO lݺ[qѵkaP$?3^VB!d2ekxI P^ .iu_YYHpRRb$TWLϞAZZbbmPWgtI~}ɓhz{૯ <#e}.4/bb*a.akm,ZI8\šO]հj8NZU7z5r%|Ysg\D;`lrŨ9'ݻ)Sؒ51ΟO#XQє ʌRE{M !FsspG]bDF:EPɸAl,2k:~<#99x;;w"ǎAFѱzZtڷ 56/,;ٳad"u =|˃zt^dXHl,z42|F];ㄶ-~ǡ[7Gf̀Ǐ} m)^Um֐BO?Y\nycG5ʼ0y2"AP\l%%hI';MJBbb'4~i@UԹpAZuΝS]Ru@>})*>`,=vUfgа$'  е+<޺sEC!2]f΄cǐ4Dd4!:99amzxzѪ*T$ZTdPM 9CM :s&^ W/tXjYt }&rƳϭ4ge Bޖp>11ȑQ).F[Bf/##tr|9 ۍTT%9w6Sc͓"9}C쾿|i%qih0o5f˪j\y拇]r ;C~5m!=`IENDB`dist/nsis/Resources/license.rtf000066400000000000000000000266151176363201700171260ustar00rootroot00000000000000{\rtf1\ansi\ansicpg1252\deff0\deftab709{\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}{\f1\fnil\fcharset0 StarSymbol;}} {\*\generator Msftedit 5.41.15.1507;}\viewkind4\uc1\pard\keepn\sb240\sa120\lang1040\f0\fs20 Falcon is currently distributed under a dual licensing scheme GPL/FPLL.\par The Falcon Programming Language License is specifically designed around the concept of an open source scripting language engine. It grants usability of the covered source in commercial applications, as it supports applying different and incompatible licenses to embedding and extension elements, while ensuring the openness of the core engine and of any work derived directly from it.\par Falcon 0.8.10 and later are released under one of GPLv2 or FPLLv1.1, at your choice.\par The GPLv2 license can be found at http://www.gnu.org/licenses/old-licenses/gpl-2.0.html\b\fs36\par \pard\keepn\sb240\sa283\qc\lang1023 Falcon Programming Language License\par \pard\sa283\qc\b0\fs24 Version 1.1, March 2008 \line http://www.falconpl.org/?page_id=license_1_1 \par \pard\keepn\sb240\sa283\qj\b\fs28 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\par \pard\fi-283\li707\qj\b0\fs24 1.\tab\b Definitions\b0 . \par \pard\fi-283\li1414\qj\f1\fs18\bullet\tab\f0\fs24 "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 10 of this document. \par \pard\fi-283\li707\qj 2.\tab "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. \par 3.\tab "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. \par 4.\tab "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. \par 5.\tab "Source" form shall mean the preferred form for making modifications, including but not limited to software source code and example code. \par 6.\tab "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. \par 7.\tab "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). \par 8.\tab "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. \par 9.\tab "Embedding Works" shall mean any work, whether in Source or Object form, that links (or binds by name) to the interface of the Work and Derivative Works. \par 10.\tab "Applications of the Work" shall mean any work, whether in Source or Object form, that is expressed through the grammar rules which are known by the Work and that require the Work to perform its execution. \par 11.\tab "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." \par 12.\tab "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. \par 13.\tab\b Grant of Copyright License\b0 . Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, prepare Embedding Works, prepare Applications of the Work, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. \par 14.\tab\b Grant of Patent License\b0 . Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. \par 15.\tab\b Redistribution of Work and Derivative Works\b0 . You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:\par \pard\fi-283\li1414\qj\f1\fs18\bullet\tab\f0\fs24 You must give any other recipients of the Work or Derivative Works a copy of this License; and \par \pard\fi-283\li707\qj 16.\tab You must cause any modified files to carry prominent notices stating that You changed the files; and \par 17.\tab You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and \par 18.\tab You must state in the Source Form and in the documentation of any Derivative Work the fact that such work is a derivation of the Work, and include a copy of the Work in its Source form or provide directions on how to obtain a copy of the Work in its Source form; and \par 19.\tab The Derivative Works are distributed under the terms of this License, or under terms that do not cause infringement of this License. \par 20.\tab\b Distribution of Embedding Works and Applications of the Work\b0 . You may produce and distribute any Embedding Work or Applications of the Work thereof in any medium, in Source or Object form, provided You meet the following conditions: \par \pard\fi-283\li1414\qj\f1\fs18\bullet\tab\f0\fs24 The Embedding Works and Applications of the Work are distributed under the term of this License, or the application of another License is explicitly stated in the documentation of the Embedding Works and Applications of the Work or included in the Source form of the Embedding Works and Applications of the Work; and \par \pard\fi-283\li707\qj 21.\tab If the Source form of Embedding Works is not distributed nor made available to the Users in any form, the Embedding Works carry a prominent notice in their documentation, or when not applicable, in any place that the Users are exposed to, about the fact that the Work is embedded, along with a general statement about the task that the Work is performing in the Embedding Works; and \par 22.\tab If the Source form of Applications of the Work is not distributed nor made available to the Users in any form, the Applications of the Work carry a prominent notice in their documentation, or when not applicable, in any place that the Users are exposed to, about the fact that the Work is used by the Application of the Work. \par 23.\tab\b Submission of Contributions\b0 . Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement You may have executed with Licensor regarding such Contributions. \par 24.\tab\b Trademarks\b0 . This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. \par 25.\tab\b Disclaimer of Warranty\b0 . Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. \par 26.\tab\b Limitation of Liability\b0 . In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. \par \pard\fi-283\li707\sa283\qj 27.\tab\b Accepting Warranty or Additional Liability\b0 . While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of Your accepting any such warranty or additional liability. \par \pard\sa283\qj\b END OF TERMS AND CONDITIONS\b0 \par \pard\sa120\qj\lang1040\par } dist/nsis/WriteEnvStr.nsh000066400000000000000000000056701176363201700157610ustar00rootroot00000000000000!ifndef _WriteEnvStr_nsh !define _WriteEnvStr_nsh !include WinMessages.nsh !ifndef WriteEnvStr_RegKey !ifdef ALL_USERS !define WriteEnvStr_RegKey \ 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' !else !define WriteEnvStr_RegKey 'HKCU "Environment"' !endif !endif # # WriteEnvStr - Writes an environment variable # Note: Win9x systems requires reboot # # Example: # Push "HOMEDIR" # name # Push "C:\New Home Dir\" # value # Call WriteEnvStr # Function WriteEnvStr Exch $1 ; $1 has environment variable value Exch Exch $0 ; $0 has environment variable name Push $2 Call IsNT Pop $2 StrCmp $2 1 WriteEnvStr_NT ; Not on NT StrCpy $2 $WINDIR 2 ; Copy drive of windows (c:) FileOpen $2 "$2\autoexec.bat" a FileSeek $2 0 END FileWrite $2 "$\r$\nSET $0=$1$\r$\n" FileClose $2 SetRebootFlag true Goto WriteEnvStr_done WriteEnvStr_NT: WriteRegExpandStr ${WriteEnvStr_RegKey} $0 $1 SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} \ 0 "STR:Environment" /TIMEOUT=5000 WriteEnvStr_done: Pop $2 Pop $0 Pop $1 FunctionEnd # # un.DeleteEnvStr - Removes an environment variable # Note: Win9x systems requires reboot # # Example: # Push "HOMEDIR" # name # Call un.DeleteEnvStr # Function un.DeleteEnvStr Exch $0 ; $0 now has the name of the variable Push $1 Push $2 Push $3 Push $4 Push $5 Call un.IsNT Pop $1 StrCmp $1 1 DeleteEnvStr_NT ; Not on NT StrCpy $1 $WINDIR 2 FileOpen $1 "$1\autoexec.bat" r GetTempFileName $4 FileOpen $2 $4 w StrCpy $0 "SET $0=" SetRebootFlag true DeleteEnvStr_dosLoop: FileRead $1 $3 StrLen $5 $0 StrCpy $5 $3 $5 StrCmp $5 $0 DeleteEnvStr_dosLoop StrCmp $5 "" DeleteEnvStr_dosLoopEnd FileWrite $2 $3 Goto DeleteEnvStr_dosLoop DeleteEnvStr_dosLoopEnd: FileClose $2 FileClose $1 StrCpy $1 $WINDIR 2 Delete "$1\autoexec.bat" CopyFiles /SILENT $4 "$1\autoexec.bat" Delete $4 Goto DeleteEnvStr_done DeleteEnvStr_NT: DeleteRegValue ${WriteEnvStr_RegKey} $0 SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} \ 0 "STR:Environment" /TIMEOUT=5000 DeleteEnvStr_done: Pop $5 Pop $4 Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd !ifndef IsNT_KiCHiK !define IsNT_KiCHiK # # [un.]IsNT - Pushes 1 if running on NT, 0 if not # # Example: # Call IsNT # Pop $0 # StrCmp $0 1 +3 # MessageBox MB_OK "Not running on NT!" # Goto +2 # MessageBox MB_OK "Running on NT!" # !macro IsNT UN Function ${UN}IsNT Push $0 ReadRegStr $0 HKLM \ "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion StrCmp $0 "" 0 IsNT_yes ; we are not NT. Pop $0 Push 0 Return IsNT_yes: ; NT!!! Pop $0 Push 1 FunctionEnd !macroend !insertmacro IsNT "" !insertmacro IsNT "un." !endif ; IsNT_KiCHiK !endif ; _WriteEnvStr_nshdist/nsis/falcon_inst.nsi.in000066400000000000000000000250321176363201700164240ustar00rootroot00000000000000;---------------------------------------------------------------------------- ; The Falcon Programming Language ; NSI script for windows installer ; ; IMPORTANT: Before to run this script, copy the relevant CRT to ; this directory (see README) ;-------------------------------- !define ALL_USERS !include WriteEnvStr.nsh !include FileAssoc.nsh !include AddToPath.nsh ;---------------------------------------------------------------------------------------------- ; Main script ; The name of the installer Name "Falcon @Falcon_VERSION@ (@FALCON_BUILD_NAME@)" ; The file to write OutFile "Falcon_@FALCON_VF_NAME@.exe" ; The default installation directory InstallDir $PROGRAMFILES\Falcon ; Registry key to check for directory (so if you install again, it will ; overwrite the old one automatically) InstallDirRegKey HKLM "Software\Falcon" "Install_Dir" ;-------------------------------- ; Pages Page license Page directory Page components Page instfiles UninstPage uninstConfirm UninstPage instfiles Icon Resources/falcon-ico-inst.ico UninstallIcon Resources/falcon-ico-inst.ico ;-------------------------------- ; License options LicenseData Resources/license.rtf LicenseForceSelection checkbox ;------------------------------------------------------------------- ; License Statements and basic docs ; Mandatory section ; Section "License Statements and basic docs" SectionIn RO ; Set output path to the installation directory. SetOutPath $INSTDIR\share ; Put file there File "..\share\*.*" SectionEnd ;------------------------------------------------------------------- ; Main binary files ; Mandatory section ; Section "Falcon binaries" SectionIn RO SetOutPath $INSTDIR\bin File "..\bin\*.exe" File "..\bin\fallc.fal" File "..\bin\@FALCON_ENGINE_DLL@" ; Do not install the runtime library if the runtime VC8 environment is already installed ;Call CheckVCRedist ;StrCmp $R0 "-1" +1 +3 ; Do only if not compiled with mingw !if "@CMAKE_GENERATOR@" == "Visual Studio 8 2005" SetOutPath $INSTDIR\bin\Microsoft.VC80.CRT File "..\bin\Microsoft.VC80.CRT\*.*" !endif !if "@CMAKE_GENERATOR@" == "Visual Studio 9 2008" SetOutPath $INSTDIR\bin\Microsoft.VC90.CRT File "..\bin\Microsoft.VC90.CRT\*.*" !endif !if "@CMAKE_GENERATOR@" == "Visual Studio 10 Win64" SetOutPath $INSTDIR\bin File "..\bin\MicrosoftVS10\msvcp100.dll" File "..\bin\MicrosoftVS10\msvcr100.dll" !endif SetOutPath $INSTDIR\share\icons File "Resources\*.ico" SetOutPath $INSTDIR ; Write the installation path into the registry WriteRegStr HKLM "SOFTWARE\Falcon" "Install_Dir" "$INSTDIR" WriteRegStr HKLM "SOFTWARE\Falcon" "Modules_Dir" "$INSTDIR\bin" ; Write the uninstall keys for Windows WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Falcon" "DisplayName" "Falcon" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Falcon" "UninstallString" '"$INSTDIR\uninstall.exe"' WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Falcon" "NoModify" 1 WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Falcon" "NoRepair" 1 WriteUninstaller "uninstall.exe" SectionEnd ;------------------------------------------------------- ; System wide settings ; File associations, program icons Section "System wide settings" ; System Register Path Push "$INSTDIR\bin" Call AddToPath Push "FALCON_LOAD_PATH" Push ".;$INSTDIR\bin" Call WriteEnvStr ; register application !insertmacro APP_ASSOCIATE "fal" "falcon.source" "Falcon script" "$INSTDIR\share\icons\falcon-ico-fal.ico" \ "Run" "$INSTDIR\bin\falcon.exe -w $\"%1$\" %*" !insertmacro APP_ASSOCIATE "fam" "falcon.module" "Falcon module" "$INSTDIR\share\icons\falcon-ico-fam.ico" \ "Run" "$INSTDIR\bin\falcon.exe -x -w $\"%1$\" %*" SectionEnd ;------------------------------------------------------- ; Start menu shortcut ; Section "Start Menu Shortcuts" CreateDirectory "$SMPROGRAMS\Falcon" CreateShortCut "$SMPROGRAMS\Falcon\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 CreateShortCut "$SMPROGRAMS\Falcon\Falcon.lnk" "$INSTDIR\bin\Falcon" "" "$INSTDIR\bin" 0 SectionEnd ;------------------------------------------------------------------- ; Feather modules ; Section "Feathers modules" SetOutPath $INSTDIR\bin File "..\bin\bufext_fm.dll" File "..\bin\compiler_fm.dll" File "..\bin\confparser_fm.dll" File "..\bin\funcext_fm.dll" File "..\bin\hash_fm.dll" File "..\bin\json_fm.dll" File "..\bin\logging_fm.dll" File "..\bin\math_extra_fm.dll" File "..\bin\mxml_fm.dll" File "..\bin\process_fm.dll" File "..\bin\regex_fm.dll" File "..\bin\socket_fm.dll" File "..\bin\threading_fm.dll" File "..\bin\zlib_fm.dll" SectionEnd ;------------------------------------------------------------------- ; Falcon modules ; Section "General Falcon source modules" SetOutPath $INSTDIR\bin\img File /r "..\bin\img\*.fal" File /r "..\bin\img\*.fam" SetOutPath $INSTDIR\bin\net File /r "..\bin\net\*.fal" File /r "..\bin\net\*.fam" SetOutPath $INSTDIR\bin\parser File /r "..\bin\parser\*.fal" File /r "..\bin\parser\*.fam" SetOutPath $INSTDIR\bin\struct File /r "..\bin\struct\*.fal" File /r "..\bin\struct\*.fam" SetOutPath $INSTDIR\bin\web File /r "..\bin\web\*.fal" File /r "..\bin\web\*.fam" SectionEnd ;------------------------------------------------------------------- ; NEST ; Section "Nest web application framework" SetOutPath $INSTDIR\bin File /r "..\bin\nest.fal" SetOutPath $INSTDIR\bin\nest File /r "..\bin\nest\*.*" SectionEnd ;------------------------------------------------------------------- ; Faldoc ; Section "Faldoc - auto documentation tool" SetOutPath $INSTDIR\bin File /r "..\bin\faldoc.bat" SetOutPath $INSTDIR\bin\apps\faldoc File /r "..\bin\apps\faldoc\*.*" SectionEnd ;------------------------------------------------------------------- ; WOPI ; Section "Web Oriented API" SetOutPath $INSTDIR\lib\falcon\wopi\cgi-bin File "..\lib\falcon\wopi\cgi-bin\*" SetOutPath $INSTDIR\bin File "..\bin\cgi_fm.dll" File "..\bin\falhttpd.exe" SectionEnd ;------------------------------------------------------------------- ; GD library ; Section "GD2 image library binding" SetOutPath $INSTDIR\bin File "..\bin\gd2_fm.dll" File "..\bin\bgd.dll" File "..\bin\jpeg-b6.dll" File "..\bin\libpng14.dll" File "..\bin\zlib1.dll" SectionEnd ;------------------------------------------------------------------- ; Dynlib ; Section "Dynlib - dynamic loader module" SetOutPath $INSTDIR\bin File "..\bin\dynlib_fm.dll" SectionEnd ;------------------------------------------------------------------- ; Curl ; Section "Curl library binding" SetOutPath $INSTDIR\bin File "..\bin\curl_fm.dll" File "..\bin\curllib.dll" File "..\bin\openldap.dll" File "..\bin\ssleay32.dll" File "..\bin\libeay32.dll" SectionEnd ;------------------------------------------------------------------- ; DBI ; Section "DBI - Database interface" SetOutPath $INSTDIR\bin File "..\bin\dbi_fm.dll" File "..\bin\libmysql.dll" SetOutPath $INSTDIR\bin\dbi File /r "..\bin\dbi\*.*" SectionEnd ;------------------------------------------------------------------- ; GTK ; Section "GTK - Widget library binding" SetOutPath $INSTDIR\bin File "..\bin\gtk_fm.dll" File "..\bin\intl.dll" File "..\bin\libasprintf-0.dll" File "..\bin\libatk-1.0-0.dll" File "..\bin\libcairo-2.dll" File "..\bin\libcairo-gobject-2.dll" File "..\bin\libcairo-script-interpreter-2.dll" File "..\bin\libexpat-1.dll" File "..\bin\libfontconfig-1.dll" File "..\bin\libgailutil-18.dll" File "..\bin\libgcc_s_dw2-1.dll" File "..\bin\libgdk_pixbuf-2.0-0.dll" File "..\bin\libgdk-win32-2.0-0.dll" File "..\bin\libgio-2.0-0.dll" File "..\bin\libglib-2.0-0.dll" File "..\bin\libgmodule-2.0-0.dll" File "..\bin\libgobject-2.0-0.dll" File "..\bin\libgthread-2.0-0.dll" File "..\bin\libgtk-win32-2.0-0.dll" File "..\bin\libpango-1.0-0.dll" File "..\bin\libpangocairo-1.0-0.dll" File "..\bin\libpangocairo-1.0-0.dll" File "..\bin\libpangoft2-1.0-0.dll" File "..\bin\libpangowin32-1.0-0.dll" File "..\bin\libpng14-14.dll" File "..\bin\freetype6.dll" SectionEnd ;------------------------------------------------------------------- ; MONGO ; Section "Mongo DB - non-sql DB driver" SetOutPath $INSTDIR\bin File "..\bin\mongo_fm.dll" SectionEnd ;------------------------------------------------------------------- ; SDL ; Section "SDL - Bindings for SDL and akin modules" SetOutPath $INSTDIR\bin File "..\bin\sdl_fm.dll" File /r "..\bin\sdl\*.*" File "..\bin\SDL.dll" File "..\bin\SDL_image.dll" File "..\bin\jpeg.dll" File "..\bin\libtiff-3.dll" File "..\bin\libpng12-0.dll" File "..\bin\zlib1.dll" File "..\bin\SDL_mixer.dll" File "..\bin\libogg-0.dll" File "..\bin\libvorbis-0.dll" File "..\bin\libvorbisfile-3.dll" File "..\bin\SDL_mixer.dll" File "..\bin\smpeg.dll" File "..\bin\SDL_ttf.dll" File "..\bin\libfreetype-6.dll" SectionEnd ;------------------------------------------------------- ; Tests and samples ; Section "Tests and samples" SetOutPath $INSTDIR\tests File /r "..\share\tests\*.*" SectionEnd ;------------------------------------------------------- ; Development files ; Section "Development files" SetOutPath $INSTDIR\include File /r "..\include\*.*" SetOutPath $INSTDIR\lib File /r "..\lib\*.lib" SetOutPath $INSTDIR\cmake File /r "..\cmake\*.*" SetOutPath $INSTDIR\bin File "..\bin\testsuite_fm.dll" File "..\bin\falconeer.fal" SectionEnd ;-------------------------------- ; Uninstaller Section "Uninstall" ; Remove registry keys DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Falcon" DeleteRegKey HKLM SOFTWARE\Falcon Push "FALCON_LOAD_PATH" Call un.DeleteEnvStr Push "FALCON_BIN_PATH" Call un.DeleteEnvStr Push "FALCON_INC_PATH" Call un.DeleteEnvStr Push "FALCON_LIB_PATH" Call un.DeleteEnvStr ;remove ourselves from path Push "$INSTDIR\bin" Call un.RemoveFromPath ; unregistre associations !insertmacro APP_UNASSOCIATE "fal" "falcon.source" !insertmacro APP_UNASSOCIATE "fam" "falcon.module" ; Remove files and uninstaller RMDir /r $INSTDIR\bin RMDir /r $INSTDIR\tests RMDir /r $INSTDIR\lib RMDir /r $INSTDIR\share RMDir /r $INSTDIR\include RMDir /r $INSTDIR\cmake Delete $INSTDIR\uninstall.exe ; Remove directories used RMDir "$INSTDIR" SectionEnd ;------------------------------- ; Test if Visual Studio Redistributables 2005+ SP1 installed ; Returns -1 if there is no VC redistributables intstalled Function CheckVCRedist Push $R0 ClearErrors ReadRegDword $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{7299052b-02a4-4627-81f2-1818da5d550d}" "Version" ; if VS 2005+ redist SP1 not installed, install it IfErrors 0 VSRedistInstalled StrCpy $R0 "-1" VSRedistInstalled: Exch $R0 FunctionEnd dist/nsis/fileassoc.nsh000066400000000000000000000100331176363201700154620ustar00rootroot00000000000000; fileassoc.nsh ; File association helper macros ; Written by Saivert ; ; Features automatic backup system and UPDATEFILEASSOC macro for ; shell change notification. ; ; |> How to use <| ; To associate a file with an application so you can double-click it in explorer, use ; the APP_ASSOCIATE macro like this: ; ; Example: ; !insertmacro APP_ASSOCIATE "txt" "myapp.textfile" "$INSTDIR\myapp.exe,0" \ ; "Open with myapp" "$INSTDIR\myapp.exe $\"%1$\"" ; ; Never insert the APP_ASSOCIATE macro multiple times, it is only ment ; to associate an application with a single file and using the ; the "open" verb as default. To add more verbs (actions) to a file ; use the APP_ASSOCIATE_ADDVERB macro. ; ; Example: ; !insertmacro APP_ASSOCIATE_ADDVERB "myapp.textfile" "edit" "Edit with myapp" \ ; "$INSTDIR\myapp.exe /edit $\"%1$\"" ; ; To have access to more options when registering the file association use the ; APP_ASSOCIATE_EX macro. Here you can specify the verb and what verb is to be the ; standard action (default verb). ; ; And finally: To remove the association from the registry use the APP_UNASSOCIATE ; macro. Here is another example just to wrap it up: ; !insertmacro APP_UNASSOCIATE "txt" "myapp.textfile" ; ; |> Note <| ; When defining your file class string always use the short form of your application title ; then a period (dot) and the type of file. This keeps the file class sort of unique. ; Examples: ; Winamp.Playlist ; NSIS.Script ; Photoshop.JPEGFile ; ; |> Tech info <| ; The registry key layout for a file association is: ; HKEY_CLASSES_ROOT ; = <"description"> ; shell ; = <"menu-item text"> ; command = <"command string"> ; !macro APP_ASSOCIATE EXT FILECLASS DESCRIPTION ICON COMMANDTEXT COMMAND ; Backup the previously associated file class ReadRegStr $R0 HKCR ".${EXT}" "" WriteRegStr HKCR ".${EXT}" "${FILECLASS}_backup" "$R0" WriteRegStr HKCR ".${EXT}" "" "${FILECLASS}" WriteRegStr HKCR "${FILECLASS}" "" `${DESCRIPTION}` WriteRegStr HKCR "${FILECLASS}\DefaultIcon" "" `${ICON}` WriteRegStr HKCR "${FILECLASS}\shell" "" "open" WriteRegStr HKCR "${FILECLASS}\shell\open" "" `${COMMANDTEXT}` WriteRegStr HKCR "${FILECLASS}\shell\open\command" "" `${COMMAND}` !macroend !macro APP_ASSOCIATE_EX EXT FILECLASS DESCRIPTION ICON VERB DEFAULTVERB SHELLNEW COMMANDTEXT COMMAND ; Backup the previously associated file class ReadRegStr $R0 HKCR ".${EXT}" "" WriteRegStr HKCR ".${EXT}" "${FILECLASS}_backup" "$R0" WriteRegStr HKCR ".${EXT}" "" "${FILECLASS}" StrCmp "${SHELLNEW}" "0" +2 WriteRegStr HKCR ".${EXT}\ShellNew" "NullFile" "" WriteRegStr HKCR "${FILECLASS}" "" `${DESCRIPTION}` WriteRegStr HKCR "${FILECLASS}\DefaultIcon" "" `${ICON}` WriteRegStr HKCR "${FILECLASS}\shell" "" `${DEFAULTVERB}` WriteRegStr HKCR "${FILECLASS}\shell\${VERB}" "" `${COMMANDTEXT}` WriteRegStr HKCR "${FILECLASS}\shell\${VERB}\command" "" `${COMMAND}` !macroend !macro APP_ASSOCIATE_ADDVERB FILECLASS VERB COMMANDTEXT COMMAND WriteRegStr HKCR "${FILECLASS}\shell\${VERB}" "" `${COMMANDTEXT}` WriteRegStr HKCR "${FILECLASS}\shell\${VERB}\command" "" `${COMMAND}` !macroend !macro APP_ASSOCIATE_REMOVEVERB FILECLASS VERB DeleteRegKey HKCR `${FILECLASS}\shell\${VERB}` !macroend !macro APP_UNASSOCIATE EXT FILECLASS ; Backup the previously associated file class ReadRegStr $R0 HKCR ".${EXT}" `${FILECLASS}_backup` WriteRegStr HKCR ".${EXT}" "" "$R0" DeleteRegKey HKCR `${FILECLASS}` !macroend !macro APP_ASSOCIATE_GETFILECLASS OUTPUT EXT ReadRegStr ${OUTPUT} HKCR ".${EXT}" "" !macroend ; !defines for use with SHChangeNotify !ifdef SHCNE_ASSOCCHANGED !undef SHCNE_ASSOCCHANGED !endif !define SHCNE_ASSOCCHANGED 0x08000000 !ifdef SHCNF_FLUSH !undef SHCNF_FLUSH !endif !define SHCNF_FLUSH 0x1000 !macro UPDATEFILEASSOC ; Using the system.dll plugin to call the SHChangeNotify Win32 API function so we ; can update the shell. System::Call "shell32::SHChangeNotify(i,i,i,i) (${SHCNE_ASSOCCHANGED}, ${SHCNF_FLUSH}, 0, 0)" !macroend ;EOFdocs/000077500000000000000000000000001176363201700120145ustar00rootroot00000000000000docs/.gitignore000066400000000000000000000000231176363201700137770ustar00rootroot00000000000000Doxyfile faldoc.fd docs/CMakeLists.txt000066400000000000000000000025461176363201700145630ustar00rootroot00000000000000# Maybe useful for C++ application and module writers, thus a reusable function function(generate_doxygen_documentation _target_name _doxygen_config_file) find_package(Doxygen) if(NOT DOXYGEN_FOUND ) message(STATUS "Doxygen not found - Doxygen based docs won't be generated") return() endif() if(NOT EXISTS ${_doxygen_config_file}) message( STATUS "Doxygen configuration file not found - Doxygen based docs won't be generated") return() endif() add_custom_target(${_target_name} COMMAND ${DOXYGEN_EXECUTABLE} "${DOXYGEN_CONFIG_FILE}" ) endfunction(generate_doxygen_documentation) set(output_dir ${CMAKE_CURRENT_BINARY_DIR}/Falcon-docs-engine) configure_file( Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY ) # generate_doxygen_documentation( engine_docs # make target ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile ) # install( DIRECTORY ${output_dir} DESTINATION ${Falcon_DOC_DIR} OPTIONAL # if docs haven't been generated ) message(STATUS "Configuring faldoc.fd") configure_file( faldoc.fd.in # input ${CMAKE_CURRENT_BINARY_DIR}/faldoc.fd @ONLY ) if(WIN32) option( FALCON_WITH_MANPAGES "Build and install also manpages" OFF ) else() # Manpages are mandatory on unix option( FALCON_WITH_MANPAGES "Build and install also manpages" ON ) endif() # if (FALCON_WITH_MANPAGES) add_subdirectory(manpages) endif() docs/Doxyfile.in000066400000000000000000000245301176363201700141330ustar00rootroot00000000000000# Doxyfile 1.5.3 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = Falcon_Core PROJECT_NUMBER = @FALCON_VERSION_ID@ OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@ CREATE_SUBDIRS = YES OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class " \ "The $name widget " \ "The $name file " \ is \ provides \ specifies \ contains \ represents \ a \ an \ the ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = @Falcon_SOURCE_DIR@ STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO DETAILS_AT_TOP = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 8 ALIASES = OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = YES SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_DIRECTORIES = NO FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text " WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = @Falcon_SOURCE_DIR@/include INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.d \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.idl \ *.odl \ *.cs \ *.php \ *.php3 \ *.inc \ *.m \ *.mm \ *.dox \ *.py \ *.C \ *.CC \ *.C++ \ *.II \ *.I++ \ *.H \ *.HH \ *.H++ \ *.CS \ *.PHP \ *.PHP3 \ *.M \ *.MM \ *.PY \ *.C \ *.H \ *.tlh \ *.diff \ *.patch \ *.moc \ *.xpm \ *.dox RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = NO INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES REFERENCES_LINK_SOURCE = YES USE_HTAGS = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = @output_dir@ HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO HTML_DYNAMIC_SECTIONS = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO BINARY_TOC = NO TOC_EXPAND = NO DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = NO TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = NO USE_PDFLATEX = NO LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = Falcon_Core.tag ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES CLASS_GRAPH = YES COLLABORATION_GRAPH = NO GROUP_GRAPHS = YES UML_LOOK = NO TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = NO DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 1000 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = YES docs/faldoc.fd.in000066400000000000000000000037411176363201700141710ustar00rootroot00000000000000# # FALDOC - Falcon Documentation system # # Faldoc file for core module. # AutoBrief = true GetUndoc = true GetPrivate = false ################################################ # Documentation generic data ################################################ Title = Falcon Core Module Author = The Falcon Committee Version = @FALCON_VERSION_ID@ ################################################ # Faldoc Input settings ################################################ Input.basedir=@PROJECT_SOURCE_DIR@ Input.directory=engine/core_module Input.directory=modules/native Input.directory=modules/falcon Input.wildcard=*.cpp Input.wildcard=*.fd Input.wildcard=*.fal Input.recursive=true # Other files may be specified here Input.extra=docs/main.fd ################################################ # Faldoc Output settings ################################################ Output.module=html Output.module=list Output.module=docbook ################################################ # Faldoc HTML Output settings # Each module can be self-configured through # Output.. configs ################################################ Output.html.directory=@output_dir@/@FALCON_VERSION_ID@-html Output.html.url=. Output.html.doctitle=Falcon Function Reference ################################################ # Faldoc listing Output settings ################################################ Output.list.file=@output_dir@/@FALCON_VERSION_ID@.lst Output.list.doctitle=Falcon Core Module - Function List Output.list.bytype=true Output.list.members=true Output.list.brief=true ################################################ # Faldoc listing Output settings ################################################ Output.docbook.file = @output_dir@/@FALCON_VERSION_ID@-docbook.xml Output.docbook.encoding = utf-8 Output.docbook.copyright.year = 2010 Output.docbook.copyright.holder = The Falcon Committee #Can be "book", "article", "book5", "article5" Output.docbook.frame = book Output.docbook.detailed = falsedocs/main.fd000066400000000000000000000071701176363201700132600ustar00rootroot00000000000000/*# @main The Falcon Programming Language Library Reference This guide contains the reference to the standard library (@a core module and @a feathers modules), the reference to the standard extensions (the other modules) as well as some general advices an information about the Falcon language functions, object and classes. This guide doesn't contain a Language reference nor tutorials related to the Falcon language in general. */ /*# @page interrupt_protocol Falcon Virtual Machine Preemptibility Falcon Virtual Machine supports bidirectional communication with the embedding applications and with extension modules that allows external code to ask for the VM to suspend or terminate its execution, and allows the VM to request for the embedding application to perform idle operations. Namely, this communication is currently supported through three mechanisms: - Wait Interruption Protocol - Suspension on sleep request - Synchronous periodic callback @section wait_interrupt Wait Interruption Protocol Blocking operations performed by the Virtual Machine can be asynchronously interrupted from another thread of the application where the virtual machine runs. Whenever an interruption request is issued, if the VM is already engaged in or is approaches a compliant blocking operation, it will raise an @a InterruptedError. Scripts may intercept this error to for provide some cleanup, or just let it unfiltered. @note Unfiltered errors doesn't reach the script layer and cause immediate termination of the signaled VM. Also, a full Falcon error instance is not created for them, and embedding application can access the exception that terminated the execution through a standard Falcon::Error C++ interface. Not all the blocking waits are instrumented to respect interrupt requests and raise this error, as some waiting operations may be caused by external module that are not required to adopt the vm interruption protocol. Compliant waiting functions are mainly @a sleep, @a Stream.readAvailable, @a Stream.writeAvailable and a few others. Compliance to interruption protocol is specified in the item descriptions. @section suspend_on_sleep Suspension on sleep request. Embedding applications may also require the VM to suspend its execution on sleep requests coming from the scripts or from extension modules. It is useful to have the VM instruct the embedding application on how much sleep time has been required, and let the embedding application manage this idle time. The Embedding application may also decide to ignore the request, to reset the VM and start a new execution or to destroy it. After a return in sleep request state, the VM is clear for inspection, modification, concurrent execution and termination. @section periodic_callback Synchronous periodic callback. The VM provides a callback hook that will be periodically called. This hook can be used by embedding applications to check for program status change, and it may inject in the VM quit or suspension requests. In example, a Falcon VM may run in a thread of an embedding application; an asynchronous request to stop the VM may be posted to the runner thread, and the VM may peek this request through the periodic callback. This hook may also be used to dequeue incoming interthread requests that may then be safely injected in the VM and possibly dispatched to coroutines that may handle them without alter the state of the running VM. Frequency of the periodic callback is expressed in count of virtual instructions executed, and can be regulated and changed live. */ /*# @group general_purpose General purpose @inmodule core @brief Generic language and type oriented functions. */ docs/manpages/000077500000000000000000000000001176363201700136075ustar00rootroot00000000000000docs/manpages/.gitignore000066400000000000000000000000071176363201700155740ustar00rootroot00000000000000*.1.gz docs/manpages/CMakeLists.txt000066400000000000000000000015631176363201700163540ustar00rootroot00000000000000# # Install all the manual files. # find_program(GZIP_TOOL NAMES gzip PATHS /bin /usr/bin /usr/local/bin ) if(NOT GZIP_TOOL) message(FATAL_ERROR "Unable to find 'gzip' program") endif(NOT GZIP_TOOL) # Use default man dir if (NOT FALCON_MAN_DIR) set( FALCON_MAN_DIR "share/man/man1" ) endif() file(GLOB SRC "*.1" ) foreach(man ${SRC}) get_filename_component( mantarget ${man} NAME ) add_custom_command( OUTPUT ${mantarget}.gz COMMAND ${GZIP_TOOL} --best -c ${man} > ${mantarget}.gz DEPENDS ${man} COMMENT "Building ${mantarget}.gz" ) add_custom_target( ${mantarget} ALL DEPENDS ${mantarget}.gz ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${mantarget}.gz DESTINATION ${FALCON_MAN_DIR} PERMISSIONS OWNER_READ GROUP_READ WORLD_READ RENAME ) list( APPEND targets ${mantarget}.gz ) endforeach() docs/manpages/falcon-conf.1000066400000000000000000000020501176363201700160530ustar00rootroot00000000000000.\" Process this file with .\" groff -man -Tascii falcon-conf.1 .\" .TH falcon\-conf 1 "July 2007" "Falcon toolset" "Falcon User Manuals" .SH NAME falcon\-conf \- Falcon makefile settings helper .SH SYNOPSIS .B falcon\-conf [options] .SH DESCRIPTION The .B falcon\-conf script can be used to determine automatically Falcon development file installation path and settings for include and library options in compilers. .B falcon\-conf is written mainly to work with GNU gcc and make, but it provides also options to determine solely the paths or the files to be included or linked, stripped of GNU options. .SH OPTIONS Please, refer to the inline option list, which is available calling .B falcon\-conf without any parameter. .SH BUGS None known .SH AUTHOR Giancarlo Niccolai .SH "SEE ALSO" .BR falcon (1) .SH LICENSE This document is released under the "GNU Free Documentation License, version 1.2". On Debian systems, the complete text of the Free Documentation License, version 1.2, can be found in /usr/share/common\-licenses/. docs/manpages/falcon.1000066400000000000000000000215631176363201700151420ustar00rootroot00000000000000.\" Process this file with .\" groff -man -Tascii falcon.1 .\" .TH falcon 1 "April 2007" "Falcon toolset" "Falcon User Manuals" .SH NAME falcon \- The Falcon Programming Language command line interpreter .SH SYNOPSIS .B falcon [options] [main_script] [script_options] .SH DESCRIPTION The .B falcon command line interpreter is used to launch, compile, assemble or debug scripts written in the Falcon Programming Language. The command line interpreter pushes the .B core module and the .B rtl module in the script load table, so they are available by default to all the scripts. The default operation is that of launching the given script, or read it from the standard input if the script name is not given. By default, .B falcon saves also the compiled module of the script in the same directory where it is found, changing the extension to ".fam". The .B main_script can be a "logical" module name, a relative path or an absolute path. In case it's a logical module name, that is, a script name without extension nor leading path, it is searched through the load path. The default load path is determined by the compilation options of the interpreter, and usually it includes the current directory. The environment variable .B FALCON_LOAD_PATH and the command line option .B \-L can change the default module search path. When the main module is found, its path is added to the module search path; in other words, there isn't any need to specify the path containing the main module to have other modules in its same directory to get loaded. The main module and other source Falcon module it loads can be stored in a directory that is not listed in the module search path; indicating an absolute or relative path as the .B main_script parameter will add that path on top of the active search path. If not differently specified, falcon will search for .fam modules newer than the relative .fal source scripts and will load those ones instead of compiling the sources. Options past the script name will be directly passed in the .B args[] global variable of the script. The interpreter is compatible with the UNIX script execution directive "#!". A main script can have on the very first line of the code the directive .I #!/path/to/falcon to declare to the shell that the falcon command line is to be loaded. If falcon command line interpreter is also in the system PATH environment variable, which is usually the case of a normal installation, then the interpreter directive may also be simply .I #!/bin/env falcon It is then simply necessary to make the main script executable with .I "chmod 744 script_name" to be able to call the script directly. Scripts executed in this way will add their path to the falcon module load path as soon as they are loaded, so other modules referenced by them will be searched in the directory where they resides before being searched elsewhere. Options to the falcon compiler may be passed normally by writing them after the execution directive in the main script. Since version 0.8.12, the falcon command line interpreter has also an interactive mode which accepts statements and provide results as the expressions are evaluated. .SH OPTIONS .IP \-c Compile but do not execute. This makes falcon to compile the given module into a .fam file and then terminate. By default, the .fam file is written to a file with the same name as the input one, with the .fam extension. .IP \-C Check for memory leaks in VM execution. Like the \-M option of faltest, this function sets the falcon engine memory allocators to slower functions that checks for memory to be allocated and deallocated correctly during the execution of a module. If the script is executed, before Falcon exits it writes a small report to the standard output. .IP "\-d =" Sets the given directive to the desired value. Compilation directives and their values are the ones that scripts can set through the directive statement. .IP "\-D =" Sets the given constant to the desired value. Constants are made available at compile time, and can be employed in macro and meta compilation. .IP \-e\ Set given encoding as default for VM I/O. Unless the scripts select a different I/O encoding, the streams that are provided to the falcon VM (like the output stream for printf) are encoded using the given ISO encoding. This overrides the default encoding that is detected by reading the environment settings. In example, if your system uses iso\-8859\-1 encoding by default, but you want your script to read and write utf\-8 files, use the option .I "\-e utf\-8" The .B \-e option also determines the default encoding of the source files. To override this, use .B \-E . .IP \-E\ Set source script encoding. As .B \-e , but this determines only the encoding used by falcon when loading the source scripts. This options overrides .B \-e values, so it can be used to set the script encoding when they have to read and write from different encodings. .IP \-f Force recompilation of modules even when .fam are found. .IP "\-h or \-?" Prints a brief help on stdout and exits. .IP \-i Interactive mode. Falcon interpreter reads language statements from a prompt and present evaluation results to the user. .IP "\-l " Select a different language code for internationalized programs. This option loads an alternate string table for all the modules loaded. If the table doesn't exist or if the modules doesn't have a .B \.ftr file containing the translation, the operation silently files and the original strings are used instead. Language codes should be in the international ISO format of five characters with a language name, an underscore and the regional code, like in .B en_US . .IP "\-L " Changes the default load path. This overrides both the internal built in settings and the contents of environment variable FALCON_LOAD_PATH. Each directory in the path should be separated by ";" and use forward slashes, like this: .I "falcon \-L \"./;/usr/share/falcon_mod;./myapp\"" .IP \-m Use temporary files for intermediate steps. By default compilation is completely performed in memory; this option makes falcon to use temporary files instead. .IP \-M Do NOT save the compiled modules in '.fam' files. .IP \-o\ Redirects output to . This is useful to control the output of falcon when using options as \-c, \-a, \-S etc. If .B is a dash (\-) the output is sent to stdout. .IP \-p\ Preloads the given module as if it were loaded by the main script. .IP \-P Ignore default load paths and uses only the paths set via the \-L switch. .IP \-r Ignore source files and only use available .fam. This does not affects the main script; use the \-x option if also the main script is a pre\-compiled .fam module and source script must be ignored. .IP \-S Produce an assembly output. Writes an assembly representation of the given script to the standard output and the exit. Use \-o to change file destination. .IP \-t Generates a syntactic tree of the source and writes it to the standard output, then exits. The syntactic tree is a representation of the script that is known by the compiler and used by the generators to create the final code. This option is useful when debugging the compiler and to test for the correct working of optimization algorithm. .IP \-T Force input parsing as .ftd (Falcon Template Document). Normally, only files ending with ".ftd" (case sensitive) are parsed as template document; when this switch is selected, the input is treated as a template document regardless of its name. .IP \-v Prints copyright notice and version and exits. .IP \-w After execution, requires the user to confirm program termination by pressing . This helps in point & click environments, where Falcon window is closed as soon as the program terminates. .IP \-x Executes a pre\-compiled .fam module. .IP \-y Creates a template file for internationalization. This option creates a single .ftt file from a single source, .fam module or binary module. By default, the name of the template is the same as the module plus ".temp.ftt" added at the end; it is possible to change the destination template file using the .B "\-o" option. .SH FILES .I /usr/lib/libfalcon_engine.so .RS Default location of the Falcon Engine loadable module. .RE .I /usr/lib/falcon .RS Default directory containing Falcon binary modules. .RE .SH ENVIRONMENT .IP FALCON_LOAD_PATH Default search path for modules loaded by the scripts. .IP FALCON_SRC_ENCODING Default encoding for the source scripts loaded by falcon (when different from the system default). .IP FALCON_VM_ENCODING Default encoding for the VM I/O streams (when different from system default). .SH AUTHOR Giancarlo Niccolai .SH "SEE ALSO" .BR falrun (1) .BR faldisass (1) .BR fallc.fal (1) .SH LICENSE This document is released under the "GNU Free Documentation License, version 1.2". On Debian systems, the complete text of the Free Documentation License, version 1.2, can be found in /usr/share/common\-licenses/. docs/manpages/falconeer.fal.1000066400000000000000000000052301176363201700163700ustar00rootroot00000000000000.\" Process this file with .\" groff -man -Tascii falconeer.fal.1 .\" .TH falconeer.fal 1 "July 2007" "Falcon toolset" "Falcon User Manuals" .SH NAME falconeer.fal \- Falcon skeleton module startup configurator script .SH SYNOPSIS .B falconeer.fal \-n moduleName [options] .SH DESCRIPTION The .B falconeer.fal script configures a directory containing the Falcon Skeleton Module so that it becomes ready for compilation under all the systems supported by Falcon. Although not mandatory, a developer willing to write native modules for Falcon may use this facility to speed up the startup phase and begin with an already compilable module. The configuration consists in the renaming of the module files into the module name specified in the command line, and in the update of the makefiles and project files provided for the various development platform Falcon can be built on. Other than the project name, the script allows one to configure other options, that will affect the template variables that will be substituted in the modified files. Once configured and built, the skeleton module provides already a .B skeleton() symbol that is exported to the VM, and a service that exports that function (defined in the fskelmod_mod.cpp file) to C++ applications. .SH OPTIONS .IP \-a\ Specifies the author name. .IP \-c\ "" Indicates the copyright owner, if different from the author, to be set on the copyright line, right beside the copyright year. .IP \-d\ Sets a brief description of the project. .IP \-l\ Loads a license plate (a long statement indicating the license under which the files are distributed) from a template file. If not given, the standard FPLL license plate is applied to the generated files. .IP \-n\ Sets the (short) name of the project. Files will be renamed accordingly to this value, and also the final module name will be configured using this setting. .IP \-p\ Sets the main project hood under which the file is created. Usually, modules are part of wider projects, or can be distributed as sets. If not set, the text "The Falcon Programming Language" will be used instead. .SH BUGS The file .B version.h cannot currently be properly configured. Edit it by hand. .SH NOTES On some systems, falconeer.fal script can be "proxied" with a falconeer pseudo command (shell script, batch file and so on). .SH AUTHOR Giancarlo Niccolai .SH "SEE ALSO" .BR falcon (1) .SH LICENSE This document is released under the "GNU Free Documentation License, version 1.2". On Debian systems, the complete text of the Free Documentation License, version 1.2, can be found in /usr/share/common\-licenses/. docs/manpages/faldisass.1000066400000000000000000000033761176363201700156530ustar00rootroot00000000000000.\" Process this file with .\" groff -man -Tascii faldisass.1 .\" .TH faldissass 1 "April 2007" "Falcon toolset" "Falcon User Manuals" .SH NAME faldissass \- The Falcon disassembler .SH SYNOPSIS .B faldissass [options] .I module_file.fam .SH DESCRIPTION The .B faldissass command line tool disassembles a compiled .fam Falcon module. The output human\-readable Falcon Virtual Machine assembly is sent to the standard output. The tool has mainly two working modes. The standard mode procures an assembly dump containing the PC counter address that will be associated with each instruction in the VM. This allows one to see exactly on which VM instruction an error was raised (as the PC at error raisal is always shown in error dumps), or to debug the VM by following the PC register in step\-by\-step mode. The isomorphic mode creates a compilable assembly source that can the be feed into the falcon assembler to obtain a compiled module. In example, this can be used for VM level hand\-made finetuning optimizations. .SH OPTIONS .IP \-d Dump the dependency table (list of load directives). .IP \-h Show version and a short help. .IP \-i Create an isomorphic version of the original assembly. .IP \-l add line information. .IP \-s Dump the string table. .IP \-S Write the strings inline instead of using #strid .IP \-y Dump the symbol table. .SH FILES .I /usr/lib/libfalcon_engine.so .RS Default location of the Falcon Engine loadable module. .RE .SH AUTHOR Giancarlo Niccolai .SH "SEE ALSO" .BR falcon (1) .BR falrun (1) .SH LICENSE This document is released under the "GNU Free Documentation License, version 1.2". On Debian systems, the complete text of the Free Documentation License, version 1.2, can be found in /usr/share/common\-licenses/. docs/manpages/fallc.fal.1000066400000000000000000000051331176363201700155150ustar00rootroot00000000000000.\" Process this file with .\" groff -man -Tascii fallc.fal.1 .\" .TH fallc.fal 1 "May 2008" "Falcon toolset" "Falcon User Manuals" .SH NAME falconeer.fal \- Falcon Language table compiler .SH SYNOPSIS .B fallc.fal [\-m merge_file] [...options...] ftt_list... .SH DESCRIPTION The Falcon language table compiler is used to compile .ftt files (Falcon translation tables) into a binary .ftr file (Falcon translation). The ftr file is a collection of translations relative to a single module (source file, .fam pre\-compiled Falcon file or binary shared object/dynamic load library), that can be shipped side by side with the module and providing a translation to one of the compiled-in languages on request. Ftt files are generated by falcon command line compiler using the \-y option. The program operates on the list of ftt files provided on the command li .SH OPTIONS .IP \-c Do NOT check for inline variables consistency. If not given, fallc will check for all the variables indicated with $varname or $(varname[:...]) to be both in the original strings and in the translation, and will warn if this doesn't happens. .IP \-h Prints an help screen with command synopsis. .IP "\-m " Merges a new source table with previously performed translations. If the source program that must be translated changes after that some work has been performed on the translation table, it is necessary to integrate the changes into the work files. In this mode, a new template source table generated by falcon can be integrated in already existing translations, which will be modified directly on place. It is possible to provide a different output file for the merged result using the \-o option, but in this case it will be possible to merge only one file at a time. .IP "\-o " By default, fallc sends the binary translation table to a file named after the original module with the .ftr extension added. This option allows one to specify a different destination for the compiled translation table. The option can be used in conjunction with the \-m option to send the result of a merged table to a different file. .IP \-v Prints program version and exits. .SH NOTES On some systems, fallc.fal script can be "proxied" with a fallc pseudo command (shell script, batch file and so on). .SH AUTHOR Giancarlo Niccolai (remove "_") .SH "SEE ALSO" .BR falcon (1) .SH LICENSE This document is released under the "GNU Free Documentation License, version 1.2". On Debian systems, the complete text of the Free Documentation License, version 1.2, can be found in /usr/share/common\-licenses/. docs/manpages/falpack.1000066400000000000000000000212751176363201700153010ustar00rootroot00000000000000.\" Process this file with .\" groff -man -Tascii falpack.1 .\" .TH falpack 1 "January 2010" "Falcon toolset" "Falcon User Manuals" .SH NAME falpack \- The falcon application packaging tool .SH SYNOPSIS .B falpak [options] .I main_script .SH DESCRIPTION The .B falpack command line tool is meant to copy all the modules and other dependencies needed for a standalone falcon application to run into a target directory. Falpack can copy also the system files needed to run the application, making the final application totally stand-alone, or just store the needed modules so that a local falcon installation can be used to run the application. .B falpack searches for special attributes in the parsed modules to store resources data files that an application may require. It copies also needed internationalization translation tables, and eventually compiles sources into binary .fam modules. It can also remove sources from the final installation, so that pre-compiled applications only can be shipped. Finally, it creates a script that can be used to run the application with a "single click" operation on the host platform. System relevant modules (as i.e. feathers or system-wide installed modules) are also stored together with the application. Anyhow, installation of falcon system files (which include feather modules) is optional. System files are copied into a fake root subdirectory (normally called .B _system ). All the modules not lying under the same directory of the application "main script" or in deeper trees, are considered "system-wide" installed, and it is supposed that the script access them via \-L option or via FALCON_LOAD_PATH environment variable. They are then stored in the system directory and a directory tree is re-created so that the simple addition of the fake root to the load path (i.e. "falcon \-L _system") is enough to access all of them. This method allows the application to access modules by logical name and by filename, as the filename is made relative to the load path. However, modules loaded with absolute filenames must be separately installed on the target system so that their position matches the required absolute path. .SH OPTIONS .IP "-b " Blacklists this module (by module name). Using this option it is possible to prevent default action on the given module (copy into the application tree or the fake root tree). It is possible to specify more blacklisted modules repeating this option. .IP "--bin

    " Specify directory where falcon binary resides. Useful if falpack is required to read an interpreter from a non-default installation, or if it can't find it. .B NOTE: falpack uses falcon build environment settings. If used under the build environment, it will copy files from the active build tree ignoring system wide installations. .IP "-e " Source files encoding. In case the source file text encoding is different from the system default encoding, the module loader may detect syntax errors or fail to load the sources; also, if .fam generation is required, the strings in the pre-compiled modules. .IP "-h" Usage help. A short in-line summary of options. .IP "--lib " Specify directory where falcon engine dynamic link library resides. Useful if falpack is required to read an interpreter from a non-default installation, or if it can't find it. .B NOTE: falpack uses falcon build environment settings. If used under the build environment, it will copy files from the active build tree ignoring system wide installations. .IP "-L " Redefine FALCON_LOAD_PATH. In case the main module needs a special load path which is not the system default or the one stored in the FALCON_LOAD_PATH environment variable. Notice that this setting overwrites system and environment variable settings. Also, notice that this load path is intended for local loading and compiling of the modules in the application; the modules are then arranged so that this setting is not needed in the final application. .IP "-M" Pack also pre-compiled modules. If given, this option will cause .fam modules to be stored beside their source files (.fal or .ftd). It is incompatible with the .B \-s option. .IP "-P " Save the package in this directory. By default, the package is stored in a directory with the same name of the main module (its extension stripped), under the current work directory. This option overrides the default and allows one to store the package at an arbitrary location. .IP "-r " Install instead of "falcon" as interpreter. Useful in case only pre\-compiled modules are packaged, where .B falrun may be employed, or in case of special system-specific interpreter build, as with .B sdl_falcon MacOSX framework compatible interpreter. .IP "-R " Change fake root for system data into . If the default name for storage of system specific apparels (as binary modules, falcon engine library etc.), which is "_system", is not satisfactory, it can be overridden through this option. .IP "-s" Strip sources. Using this option, source falcon files are not copied in the package; only the pre-compiled bytecode .fam modules are stored. .IP "-S" Do not store system files. Prevent copying of the falcon interpreter, falcon engine dynamic library and feather modules. .IP "-v" Prints version and exit. .IP "-V" Verbose mode. Prints verbose messages of what .B falpack is doing. .SH SPECIAL\ ATTRIBUTES .B falpack automatically packages needed ancillary files required by modules, as the translation table files (.ftr). However, the modules may specify other files to be included through a set of special attributes which are interpreted by .B falpack as it loads and analyzes them. .IP resources .RS This attribute can indicate a list of resources that should be copied together with the module. The attribute needs to be a string, and different resources can be separated through a semi comma (;). It is possible also to specify file masks using the "*.ext" pattern; whole subdirectories can be stored using the "dir/*" pattern. Please, notice that this attribute doesn't work recursively; to include sub-directories in resource directories, specify all of them. For example: resources: "images/*; images/icons/*" .RE .IP plugins .RS Similar to the .B resources attribute, the plugins attribute indicates a single module or a directory containing more modules that may be used by the applications as dynamic plugins. Differently from the data considered in the .B resources attribute, the files indicated in the plugins attribute are loaded by the loader, and their dependencies, if any, are further resolved and become part of the installed application. Also, they are treated as any other module in any respect; for example associated translation files are copied, and in case .B falpack is required to strip sources, only the pre-compiled fam modules will be saved. It is possible to store all the modules found in a certain directory using the "*" wildcard. Similarly to the resources attribute, the plugin attribute doesn't descend recursively in sub-directories; to include other modules laying below the required directory, that subdirectory must be explicitly specified, as in this example: plugins: "output/*; output/helpers/*" .RE .IP dynlib .RS This attribute stores one or more system dynamic library needed for the falcon module to run. Depending on the host system, the target location may be in the fake root directory or besides the module (depending on how the system module loader tries to find the modules). More dependencies may be separated with a semi comma (;). .RE .SH BUGS Relative paths are not jailed; if the resource attribute or the relative path of a loaded module indicates a position above the main script directory or above any position in the FALCON_LOAD_PATH specification, the files may be copied outside the target package location. Be careful when writing the application you want to package so that all the needed files can be reached at the same level or below the main script, or at the same level or below directories in the load path. At the moment, it's quite complex to create cross platform packages (except for script-only applications which use local falcon installation to run). It is planned to provide this support in future via the ability to download pre-compiled modules from a central repository. In this version, .B dynlib attribute is not working for MacOSX style framework libraries. However, it will work for MacOSX dylib that can be found via the .I ldd command. .SH AUTHOR Giancarlo Niccolai .SH "SEE ALSO" .BR falcon (1) .BR falrun (1) .SH LICENSE This document is released under the "GNU Free Documentation License, version 1.2". On Debian systems, the complete text of the Free Documentation License, version 1.2, can be found in /usr/share/common\-licenses/. docs/manpages/falrun.1000066400000000000000000000046421176363201700151660ustar00rootroot00000000000000.\" Process this file with .\" groff -man -Tascii falrun.1 .\" .TH flcrun 1 "April 2007" "Falcon toolset" "Falcon User Manuals" .SH NAME flcrun \- The Falcon command line execution tool .SH SYNOPSIS .B falrun [options] [main_script] [script_options] .SH DESCRIPTION The .B falrun command line tool is a subset of the .BR falcon(1) command line interpreter which only executes pre\-compiled falcon scripts in ".fam" module files. This is meant to launch Falcon based application which were compiled on a different system, and whose source is not shipped. .SH OPTIONS .IP \-e\ Set given encoding as default for VM I/O. Unless the scripts select a different I/O encoding, the streams that are provided to the falcon VM (like the output stream for printf) are encoded using the given ISO encoding. This overrides the default encoding that is detected by reading the environment settings. In example, if your system uses iso\-8859\-1 encoding by default, but you want your script to read and write utf\-8 files, use the option .B \-e\ utf\-8 .IP \-h\ or\ \-? Prints a brief help on stdout and exits. .IP \-L\ Changes the default load path. This overrides both the internal built in settings and the contents of environment variable FALCON_LOAD_PATH. Each directory in the path should be separated by ";" and use forward slashes, like this: .B falrun \-L "./;/usr/share/falcon_mod;./myapp" .IP \-p\ Preloads the given module as if it were loaded by the main script. .IP \-P Ignore default load paths and uses only the paths set via the \-L switch. .IP \-v Prints copyright notice and version and exits. .SH FILES .I /usr/lib/libfalcon_engine.so .RS Default location of the Falcon Engine loadable module. .RE .I /usr/lib/falcon .RS Default directory containing Falcon binary modules. .RE .SH ENVIRONMENT .IP FALCON_LOAD_PATH Default search path for modules loaded by the scripts. .IP FALCON_SRC_ENCODING Default encoding for the source scripts loaded by falcon (when different from the system default). .IP FALCON_VM_ENCODING Default encoding for the VM I/O streams (when different from system default). .SH AUTHOR Giancarlo Niccolai .SH "SEE ALSO" .BR falcon (1) .BR faldisass (1) .SH LICENSE This document is released under the "GNU Free Documentation License, version 1.2". On Debian systems, the complete text of the Free Documentation License, version 1.2, can be found in /usr/share/common\-licenses/. docs/manpages/faltest.1000066400000000000000000000207231176363201700153370ustar00rootroot00000000000000.\" Process this file with .\" groff -man -Tascii faltest.1 .\" .TH faltest 1 "April 2007" "Falcon toolset" "Falcon User Manuals" .SH NAME faltest \- Falcon unit test interface. .SH SYNOPSIS .B faltest [\-d unit_test_dir] [options] [unit_list] .I module_file.fam .SH DESCRIPTION The .B faltest command line tool is a powerful interface to the Falcon unit testing system. The basic working principle of faltest is that of taking all the .fal script files contained in a directory, compile and execute them, eventually keeping track of errors, elapsed times and execution failures. After running all the scripts, .B faltest may print a report on what happened if requested to do so. A list of one or more unit test may be indicated in the .B faltest command line after the options. Also, the executed tests can be limited to named subsets. The unit test directory is added to the module load path, so .B load directives will be resolved searching the required scripts in the test path. .SH UNIT TEST SCRIPTS Scripts being part of unit test have to start with a common header indicating some information about them. The header is actually a formatted Falcon comment which is read by the faltest utility. This is a typical header: .nf /********************************************** * Falcon test suite * * ID: 10a * Category: cat\-name * Subcategory: subcat\-name * Short: The short name * Description: * Long description of this test * Spanning on many lines... * [/Description] **********************************************/ .fi The header has a free form; faltest recognizes the following fields, being inside a comment and eventually preceded by a "*". .B ID: .RS The only mandatory field, it declares the ID under which this unit test is known. It will be used in listing the tests and in selecting them as argument of the .B faltest command line. The id must be an integer number, eventually followed by a single lowercase letter. Similar tests should be filed under the same ID with a different specification letter. Scripts not having this field will be ignored by faltest. .RE .B Category: .RS The name of the category under which this test is filed. Faltest can select a subset of scripts to be executed to a certain category. .RE .B Subcategory: .RS The name of the subcategory under which this test is filed. Faltest can select a subset of scripts to be executed to a certain subcategory. .RE .B Short: .RS Short description (or symbolic name) for this unit test. .RE .B Description: .RS A longer description explaining what this test is supposed to do. The description always spans on more lines, and is closed by a .B [/Description] tag. .RE .SH THE TESTSUITE MODULE Falcon system provides a loadable module called "testsuite". The module is actually embedded in .B faltest , and is provided to all the scripts it runs. The module provides the following functions: .B success() .RS The script is terminated and recorded as a success. .RE .B failure( reason ) .RS The script is terminated and recorded as a failure. An optional parameter containing a description of the failure condition may be optionally provided; it will be written as part of the report and may be used to track which part of the test wasn't working. .RE .B testReflect( item ) .RS Returns the passed item. This is used to test for engine responsiveness to external calls and item copy through external functions. .RE .B alive( percent ) .RS In tests running for some human sensible time, this function should be called periodically to inform the system and the user that the test is proceeding. An optional "percent" parameter can be provided. It will be interpreted as a value between 1 and 100 representing the amount of test that has been performed up to this moment. .RE .B timings( total_time, performed_ops ) .RS In case the execution time is relevant for the test, like in benchmarks, this function can be used to communicate back to .B faltest the time elapsed in the operations being tested and the number of operations performed. Those parameters will be recorded and eventually saved in the report file, to be used as benchmarks against falcon engine modifications. .RE .B timeFactor() .RS Lengthy tests are often performed by looping over the operation to be tested for a certain time. Benchmarks and performance tests should be written so that they can normally complete in a reasonable time, between one and ten seconds. In case the user wants the tests to perform longer, in order to obtain better statistical data, it can pass the \-f (time factor) option to .B faltest command line. The time factor will be a number greater than 1, and should be used by tests that may perform lengthy operation to customize the number of performed tests. This function returns as an integer value of the \-f option given to .B faltest or 1 if the \-f option was not given. .RE .SH OPTIONS .IP "\-c " Select this category and ignore the rest. .IP "\-C " Select this subcategory and ignore the rest. .IP \-d Directory from where to load the unit tests. If not specified, it will be the current directory. .IP "\-f " Set time factor to N. Some scripts may use this information to perform more loops or lengthy operations. .IP \-h Show version and a short help. .IP \-l List the selected tests and exit. Combine with .B \-v to have the path of the tests, as .B \-l only lists the script ID, its short name and the category/subcategory pair. .IP \-m Do .B not compile in memory. Use disk temporary files. .IP \-M Checks for memory leaks. This option makes faltest to report unclaimed memory after each script execution, and a final report at the end of the tests. The check is extended to all the engine operations, so errors in the engine are detected too. .IP "\-o " Write final report to the given output file. .IP \-s Perform module serialization test. Other than compiling the file, the module is also saved and then restored before being executed. This allows one to check for errors in module serialization (that is, loading of .fam files). The operation is performed in memory, unless the option .B \-m is also specified. .IP \-S Compile via assembly. This test the correct behavior of the assembler generator and compiler instead of the binary module generator. .IP \-t Records and display timings. The statistics of compilation, linking and execution overall times are recorded and written as part of the report. .IP \-T Records timings() calls from scripts. This allows the scripts to declare their own performance ratings, and collects the results in the final report. .IP \-v Be verbose. Normally, execution and failures are sparsely reported. This is because the normal execution mode is meant for automated runs. Tests can be executed by automated utilities and errors can be reported to system administrator by simple checks on the output data. A developer willing to fix a broken test must run that test alone with the .B \-v enabled. A more complete error report (including compilation or execution errors, if they were the cause for the failure) will be then visualized. The \-v options also allows one to see the path of the original script, which is otherwise hidden (masked by the testsuite ID). .IP \-V Prints version number and exits. .SH SAMPLE This is a simple and complete example from the Falcon benchmark suite. .nf /******************************************* * Falcon direct benchmarks * * ID: 2a * Category: benchmark * Subcategory: calls * Short: Benchmark on function calls * Description: * Performing repeated function calls and returns. * This test calls a function without parameters. * * [/Description] ******************************************/ loops = 1000000 * timeFactor() each = int(loops/10) function toBeCalled() end // getting time time = seconds() for i = 1 to loops // perform the call toBeCalled() if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() \- time // subtract alive time timeAlive = seconds() for i = 1 to loops if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() \- timeAlive time \-= timeAlive timings( time, loops ) /* end of file */ .fi .SH FILES .I /usr/lib/libfalcon_engine.so .RS Default location of the Falcon Engine loadable module. .RE .SH AUTHOR Giancarlo Niccolai .SH "SEE ALSO" .BR falcon (1) .SH LICENSE This document is released under the "GNU Free Documentation License, version 1.2". On Debian systems, the complete text of the Free Documentation License, version 1.2, can be found in /usr/share/common\-licenses/. engine/000077500000000000000000000000001176363201700123315ustar00rootroot00000000000000engine/CMakeLists.txt000066400000000000000000000136011176363201700150720ustar00rootroot00000000000000################################################## # Falcon Programming Language # ################################################## #Engine specific define add_definitions(-DFALCON_ENGINE_EXPORTS) include_directories( ${PROJECT_BINARY_DIR}/include ${PROJECT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/core_module ) message(STATUS "Compiling Falcon core on ${CMAKE_SYSTEM_NAME}" ) #Specific system files if(UNIX OR APPLE) set(SYS_SPECIFIC baton_posix.cpp dir_sys_unix.cpp fstream_sys_unix.cpp mt_posix.cpp stdstreams_unix.cpp time_sys_unix.cpp vm_sys_posix.cpp vfs_file_unix.cpp signals_posix.cpp ) #for solaris system flavor, add sys_solaris.cpp if(CMAKE_SYSTEM_NAME STREQUAL "SunOS" ) list(APPEND SYS_SPECIFIC sys_solaris.cpp) else() list(APPEND SYS_SPECIFIC sys_unix.cpp) endif() #DL is different for unix and mac if(APPLE) list(APPEND SYS_SPECIFIC heap_bsd.cpp dll_mac.cpp ) elseif(CMAKE_SYSTEM_NAME STREQUAL "BSD") list(APPEND SYS_SPECIFIC heap_bsd.cpp dll_dl.cpp ) else() list(APPEND SYS_SPECIFIC heap_posix.cpp dll_dl.cpp ) endif() option(FALCON_SKIP_BISON "Don't perform grammar recompilation" on) if(NOT FALCON_SKIP_BISON) find_program(BISON_EXECUTABLE bison) if(NOT BISON_EXECUTABLE) message(FATAL_ERROR "BISON not found - Turn on FALCON_SKIP_BISON to not perform grammar recompilation") endif(NOT BISON_EXECUTABLE) # Builds out-of-source. Copy into the source tree and commit to svn, if desired. add_custom_command( DEPENDS ${PROJECT_SOURCE_DIR}/include/falcon/syntree.h ${CMAKE_CURRENT_SOURCE_DIR}/src_parser.yy COMMAND ${BISON_EXECUTABLE} -y ${CMAKE_CURRENT_SOURCE_DIR}/src_parser.yy -o ${CMAKE_CURRENT_SOURCE_DIR}/src_parser.cpp OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src_parser.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src_parser.hpp COMMENT "Creating src_parser.cpp" ) endif(NOT FALCON_SKIP_BISON) elseif(WIN32) set(SYS_SPECIFIC baton_win.cpp dir_sys_win.cpp dll_win.cpp fstream_sys_win.cpp heap_win.cpp mt_win.cpp stdstreams_win.cpp sys_win.cpp time_sys_win.cpp vm_sys_win.cpp vfs_file_win.cpp falcon_engine.rc signals_win.cpp ) endif() ################# set(CORE_MODULE_SOURCES core_module/array_ext.cpp core_module/attrib_ext.cpp core_module/cmdlineparser.cpp core_module/complex_ext.cpp core_module/continuation_ext.cpp core_module/core_module.cpp core_module/coroutine_ext.cpp core_module/dict_ext.cpp core_module/dir_ext.cpp core_module/envvars.cpp core_module/error_ext.cpp core_module/fal_include.cpp core_module/file_ext.cpp core_module/format_ext.cpp core_module/function_ext.cpp core_module/functional_ext.cpp core_module/gc_ext.cpp core_module/input.cpp core_module/inspect.cpp core_module/item_ext.cpp core_module/iterator_ext.cpp core_module/list_ext.cpp core_module/math_ext.cpp core_module/membuf_ext.cpp core_module/message_ext.cpp core_module/oob_ext.cpp core_module/pagedict_ext.cpp core_module/param_ext.cpp core_module/path_ext.cpp core_module/poopseq_ext.cpp core_module/print.cpp core_module/random.cpp core_module/seconds.cpp core_module/sequence_ext.cpp core_module/serialize.cpp core_module/set_ext.cpp core_module/string_ext.cpp core_module/state_ext.cpp core_module/stringstream_ext.cpp core_module/sys_ext.cpp core_module/table.cpp core_module/time_ext.cpp core_module/transcode_ext.cpp core_module/tokenizer_ext.cpp core_module/uri_ext.cpp core_module/vminfo_ext.cpp core_module/webhelp.cpp ) add_library(falcon_engine SHARED attribmap.cpp autocstring.cpp autoucsstring.cpp autowstring.cpp base64.cpp basealloc.cpp cacheobject.cpp crobject.cpp compiler.cpp complex.cpp continuation.cpp corearray.cpp coreclass.cpp coredict.cpp corefunc.cpp coreobject.cpp coreslot.cpp coretable.cpp debug.cpp deptab.cpp error.cpp falcondata.cpp falconobject.cpp fassert.cpp filestat.cpp format.cpp garbageable.cpp gcalloc.cpp gencode.cpp generatorseq.cpp genericlist.cpp genericmap.cpp genericvector.cpp genhasm.cpp gentree.cpp globals.cpp intcomp.cpp item.cpp item_co.cpp itemarray.cpp itemlist.cpp itemserial.cpp itemset.cpp itemtraits.cpp iterator.cpp lineardict.cpp linemap.cpp livemodule.cpp ltree.cpp membuf.cpp memhash.cpp memory.cpp mempool.cpp modloader.cpp module.cpp modulecache.cpp pagedict.cpp path.cpp pcode.cpp poopseq.cpp proptable.cpp rampmode.cpp rangeseq.cpp reflectobject.cpp rosstream.cpp runtime.cpp sequence.cpp service.cpp smba.cpp src_lexer.cpp stackframe.cpp stream.cpp streambuffer.cpp string.cpp stringitem.cpp stringstream.cpp strtable.cpp symbol.cpp symtab.cpp syntree.cpp timestamp.cpp trace.cpp traits.cpp transcoding.cpp tokenizer.cpp uri.cpp vfsprovider.cpp vmcontext.cpp vm.cpp vmmaps.cpp vmmsg.cpp vm_run.cpp vmsema.cpp # src_parser.hpp src_parser.cpp # ${FALCON_HEADERS} ${CORE_MODULE_SOURCES} ${SYS_SPECIFIC} ) set_target_properties(falcon_engine PROPERTIES VERSION "${FALCON_SONAME_VERSION}.${FALCON_SONAME_REVISION}.${FALCON_SONAME_AGE}" SOVERSION "${FALCON_SONAME_VERSION}" ) #In unix and mac we have to add extra libraries if(UNIX OR APPLE) #also dl for linux and solaris # and RT only for solaris and non-bsd systems set(SYS_LIBS pthread m) if(APPLE) list(APPEND SYS_LIBS dl) else() IF(CMAKE_SYSTEM_NAME MATCHES ".*BSD") else() list(APPEND SYS_LIBS dl rt) endif() endif() target_link_libraries(falcon_engine ${SYS_LIBS}) endif() install(TARGETS falcon_engine EXPORT falcon-core-targets ${FALCON_INSTALL_DESTINATIONS} ) engine/attribmap.cpp000066400000000000000000000045301176363201700150220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: attribmap.cpp Attribute Map - specialized string - vardef map. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 11 Jul 2009 20:42:43 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include namespace Falcon { AttribMap::AttribMap(): Map( &traits::t_string(), &traits::t_voidp() ) {} AttribMap::AttribMap( const AttribMap& other ): Map( &traits::t_string(), &traits::t_voidp() ) { MapIterator iter = other.begin(); while( iter.hasCurrent() ) { insertAttrib( *(String *) iter.currentKey(), *(VarDef**) iter.currentValue() ); iter.next(); } } AttribMap::~AttribMap() { MapIterator iter = begin(); while( iter.hasCurrent() ) { delete *(VarDef**) iter.currentValue(); iter.next(); } } void AttribMap::insertAttrib( const String& name, VarDef* vd ) { void *found = find( &name ); if ( found ) { VarDef* old = *(VarDef**) found; *old = *vd; delete vd; } else insert( &name, vd ); } VarDef* AttribMap::findAttrib( const String& name ) { void *found = find( &name ); if ( ! found ) return 0; return *(VarDef**) found; } bool AttribMap::save( const Module* mod, Stream *out ) const { uint32 sz = endianInt32( size() ); out->write( &sz, sizeof( sz ) ); MapIterator iter = begin(); while( iter.hasCurrent() ) { String* key = (String*) iter.currentKey(); VarDef* value = *(VarDef**) iter.currentValue(); key->serialize( out ); value->save( mod, out ); iter.next(); } return out->good(); } bool AttribMap::load( const Module* mod, Stream *in ) { uint32 sz; in->read( &sz, sizeof( sz ) ); sz = endianInt32( sz ); for( uint32 i = 0; i < sz; ++i ) { String k; VarDef* temp = new VarDef; if ( ! k.deserialize( in ) || ! temp->load( mod, in ) ) { delete temp; return false; } insertAttrib( k, temp ); } return true; } } /* end of attribmap.cpp */ engine/autocstring.cpp000066400000000000000000000061241176363201700154020ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: autocstring.cpp Utility to convert falcon items and strings into C Strings. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab ago 4 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Utility to convert falcon items and strings into C Strings. */ #include #include #include namespace Falcon { AutoCString::AutoCString(): m_len(0) { m_pData = m_buffer; m_buffer[3] = '\0'; } AutoCString::AutoCString( const Item &itm ): m_pData(0) { set(itm); } AutoCString::AutoCString( const String &str ): m_pData(0) { set(str); } void AutoCString::init_vm_and_format( VMachine *vm, const Item &itm, const String &fmt ) { String *str; String tempStr; str = &tempStr; vm->itemToString( tempStr, &itm, fmt ); if ( (m_len = str->toCString( m_buffer+3, AutoCString_BUF_SPACE-3 ) ) != String::npos ) { m_pData = m_buffer; return; } Falcon::uint32 len = str->length() * 4 + 4; m_pData = (char *) memAlloc( len+3 ); m_len = str->toCString( m_pData+3, len ); } const char* AutoCString::bom_str() { m_pData[0] = (char)0xEF; m_pData[1] = (char)0xBB; m_pData[2] = (char)0xBF; return m_pData; } AutoCString::~AutoCString() { if ( m_pData != 0 && m_pData != m_buffer ) memFree( m_pData ); } void AutoCString::set( const Falcon::String &str ) { // remove m_pData if( m_pData != 0 && m_pData != m_buffer ) memFree( m_pData ); if ( (m_len = str.toCString( m_buffer+3, AutoCString_BUF_SPACE-3 ) ) != String::npos ) { m_pData = m_buffer; return; } Falcon::uint32 len = str.length() * 4 + 4; m_pData = (char *) memAlloc( len+3 ); m_len = str.toCString( m_pData+3, len ); } void AutoCString::set( const Falcon::Item &itm ) { // remove m_pData if( m_pData != 0 && m_pData != m_buffer ) memFree( m_pData ); String *str; String tempStr; if ( itm.isString() ) { str = itm.asString(); } else { str = &tempStr; itm.toString( tempStr ); } //if the size is large, we already know we can't try the conversion if ( (m_len = str->toCString( m_buffer+3, AutoCString_BUF_SPACE ) ) != String::npos ) { m_pData = m_buffer; return; } Falcon::uint32 len = str->length() * 4 + 4; m_pData = (char *) memAlloc( len+3 ); m_len = str->toCString( m_pData+3, len ); } void AutoCString::set( Falcon::VMachine *vm, const Falcon::Item &itm ) { // remove m_pData if( m_pData != 0 && m_pData != m_buffer ) memFree( m_pData ); init_vm_and_format( vm, itm, "" ); } void AutoCString::set( Falcon::VMachine *vm, const Falcon::Item &itm, const Falcon::String &fmt ) { // remove m_pData if( m_pData != 0 && m_pData != m_buffer ) memFree( m_pData ); init_vm_and_format( vm, itm, fmt ); } } /* end of autocstring.cpp */ engine/autoucsstring.cpp000066400000000000000000000030131176363201700157440ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: autoucsstring.cpp Utility to convert falcon items and strings into UCS-2 Strings. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 12 Sep 2010 12:53:18 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Utility to convert falcon items and strings into UCS-2 Strings. */ #include #include #include namespace Falcon { AutoUCSString::AutoUCSString(): m_len(0) { m_pData = m_buffer; m_buffer[0] = 0; } AutoUCSString::AutoUCSString( const String &str, uint16 defChar ): m_pData(0) { set( str, defChar ); } AutoUCSString::~AutoUCSString() { if ( m_pData != 0 && m_pData != m_buffer ) memFree( m_pData ); } void AutoUCSString::set( const Falcon::String &str, uint16 defChar ) { if ( m_pData != 0 && m_pData != m_buffer ) memFree( m_pData ); m_len = str.length(); if ( m_len + 1 <= AutoUCSString_BUF_SPACE ) { m_pData = m_buffer; } else { Falcon::uint32 size = (m_len + 1) * sizeof(uint16); m_pData = (uint16 *) memAlloc( size ); } for( uint32 i = 0; i < m_len; ++i ) { uint32 chr = str.getCharAt(i); m_pData[i] = chr <= 0xFFFF ? chr : defChar; } m_pData[m_len] = 0; } } /* end of autoucsstring.cpp */ engine/autowstring.cpp000066400000000000000000000055071176363201700154320ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: autowstring.cpp Utility to convert falcon items and strings into Wide C Strings. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab ago 4 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Utility to convert falcon items and strings into Wide C Strings. */ #include #include #include namespace Falcon { AutoWString::AutoWString(): m_len(0) { m_pData = m_buffer; m_buffer[0] = 0; } AutoWString::AutoWString( const String &str ): m_pData(0) { set( str ); } AutoWString::AutoWString( const Item &itm ): m_pData(0) { set( itm ); } AutoWString::~AutoWString() { if ( m_pData != 0 && m_pData != m_buffer ) memFree( m_pData ); } void AutoWString::init_vm_and_format( VMachine *vm, const Item &itm, const String &fmt ) { String *str; String tempStr; str = &tempStr; vm->itemToString( tempStr, &itm, fmt ); if ( ( m_len = str->toWideString( m_buffer, AutoWString_BUF_SPACE ) ) != String::npos ) { m_pData = m_buffer; return; } Falcon::uint32 len = str->length() * 2 + 2; m_pData = (wchar_t *) memAlloc( len ); m_len = str->toWideString( m_pData, len ); } void AutoWString::set( const Falcon::String &str ) { if ( m_pData != 0 && m_pData != m_buffer ) memFree( m_pData ); if ( (m_len = str.toWideString( m_buffer, AutoWString_BUF_SPACE ) ) != String::npos ) { m_pData = m_buffer; return; } Falcon::uint32 len = str.length() * 2 + 2; m_pData = (wchar_t *) memAlloc( len ); m_len = str.toWideString( m_pData, len ); } void AutoWString::set( const Falcon::Item &itm ) { if ( m_pData != 0 && m_pData != m_buffer ) memFree( m_pData ); String *str; String tempStr; if ( itm.isString() ) { str = itm.asString(); } else { str = &tempStr; itm.toString( tempStr ); } if ( (m_len = str->toWideString( m_buffer, AutoWString_BUF_SPACE ) ) != String::npos ) { m_pData = m_buffer; return; } Falcon::uint32 len = str->length() * 2 + 2; m_pData = (wchar_t *) memAlloc( len ); m_len = str->toWideString( m_pData, len ); } void AutoWString::set( Falcon::VMachine *vm, const Falcon::Item &itm ) { if ( m_pData != 0 && m_pData != m_buffer ) memFree( m_pData ); init_vm_and_format( vm, itm, "" ); } void AutoWString::set( Falcon::VMachine *vm, const Falcon::Item &itm, const Falcon::String &fmt ) { if ( m_pData != 0 && m_pData != m_buffer ) memFree( m_pData ); init_vm_and_format( vm, itm, fmt ); } } /* end of autowstring.cpp */ engine/base64.cpp000066400000000000000000000124251176363201700141250ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: base64.cpp Base64 encoding as per rfc3548 ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 20 Jun 2010 15:47:05 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include namespace Falcon { bool Base64::encode( byte* data, uint32 dsize, byte* target, uint32& tgsize ) { static const char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // go the integer division point uint32 dsize_pair = (dsize/3) * 3; uint32 padCount = dsize - dsize_pair; if( tgsize < (dsize_pair / 3 * 4 + padCount) ) { // not enough space return false; } // perform everything up to the par uint32 rsize = 0; for (uint32 dp = 0; dp < dsize_pair; dp += 3) { // these three 8-bit (ASCII) characters become one 24-bit number uint32 b64val = data[dp] << 16; b64val += data[dp+1] << 8; b64val += data[dp+2]; // this 24-bit number gets separated into four 6-bit numbers target[rsize++] = base64chars[(b64val >> 18) & 63]; target[rsize++] = base64chars[(b64val >> 12) & 63]; target[rsize++] = base64chars[(b64val >> 6) & 63]; target[rsize++] = base64chars[b64val & 63]; } // now add the last semi-four word if( padCount != 0 ) { uint32 b64val = data[dsize_pair] << 16; // get enough bits if( (dsize_pair + 1) < dsize ) b64val += data[dsize_pair+1] << 8; if( (dsize_pair + 2) < dsize ) b64val += data[dsize_pair+2]; // the first two semi-bytes are always in target[rsize++] = base64chars[(b64val >> 18) & 63]; target[rsize++] = base64chars[(b64val >> 12) & 63]; // then, the third only if we have an extra byte ... if( (dsize_pair + 1) < dsize ) { target[rsize++] = base64chars[(b64val >> 6) & 63]; // and the fourth only if we have 2 if( (dsize_pair + 2) < dsize ) target[rsize++] = base64chars[b64val & 63]; } // fill the 1 or 2 missing chars with '=' for (; padCount < 3; padCount++) target[rsize++] = '='; } // shrink the input buffer tgsize = rsize; return true; } bool Base64::decode( const String& data, byte* target, uint32& tgsize ) { uint32 tgpos = 0; uint32 dsize = data.size(); uint32 i = 0; while( i < dsize ) { uint32 value = 0; uint32 n = 0; while( n < 4 && i < dsize ) { uint32 chr = data.getCharAt(i); // whitespace? -- ignore. if( chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r' ) { ++i; continue; } // padding char? if( chr == '=' ) { ++i; while( i < dsize ) { chr = data.getCharAt(i); if( ! (chr == '=' || chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r') ) { // we had a "=" not at the end, this is not a base64 string. return false; } ++i; } // ok, finish this off. break; } uint32 bits = getBits( chr ); if( bits == 0xFF ) return false; // create the value value |= bits << ((3-n)*6); // advance in the string and in the status. ++i; ++n; } // get the chars if( (tgpos+n-1) >= tgsize ) return false; // have we to fix something? if( n > 0 ) { target[tgpos++] = (byte)((value>>16) &0xFF); if( n > 1 ) { target[tgpos++] = (byte)((value>>8) &0xFF); if( n > 2 ) target[tgpos++] = (byte)(value &0xFF); } } } tgsize = tgpos; return true; } byte Base64::getBits( uint32 chr ) { if( chr >= 'A' && chr <= 'Z' ) { return (byte)( chr - 'A'); } else if ( chr >= 'a' && chr <= 'z' ) { return (byte)(chr - 'a' + 26); } else if ( chr >= '0' && chr <= '9' ) { return (byte)(chr - '0' + 52); } else if ( chr == '+' ) { return 62; } else if( chr == '/' ) { return 63; } else { // It's not a valid base64 encoding return 0xFF; } } void Base64::encode( byte* data, uint32 dsize, String& target ) { uint32 tgsize = dsize/3*4+4; target.reserve( tgsize ); encode( data, dsize, target.getRawStorage(), tgsize ); target.size( tgsize ); } void Base64::encode( const String& data, String& target ) { AutoCString cdata( data ); encode( (byte*) cdata.c_str(), cdata.length(), target ); } bool Base64::decode( const String& data, String& target ) { // enough space to store a correctly encoded base64 string. String tg2; uint32 tgsize = data.size()/4*3+4; tg2.reserve( tgsize ); if( decode( data, tg2.getRawStorage(), tgsize ) ) { tg2.size( tgsize ); tg2.c_ize(); return target.fromUTF8( (char*)tg2.getRawStorage() ); } return false; } } engine/basealloc.cpp000066400000000000000000000015141176363201700147630ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: basealloc.cpp Base allocation declaration for engine classes ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar dic 5 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Base allocation declaration for engine classes */ #include #include #include #include namespace Falcon { void *BaseAlloc::operator new( size_t size ) { return memAlloc( size ); } void BaseAlloc::operator delete( void *mem, size_t /* currenty unused */ ) { memFree( mem ); } } /* end of basealloc.cpp */ engine/baton_posix.cpp000066400000000000000000000102641176363201700153650ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: baton_posix.cpp Baton synchronization structure. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 14 Mar 2009 00:03:28 +0100 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include namespace Falcon { typedef struct tag_POSIX_BATON_DATA { bool m_bBusy; bool m_bBlocked; pthread_t m_thBlocker; pthread_mutex_t m_mtx; pthread_cond_t m_cv; } POSIX_BATON_DATA; Baton::Baton( bool bBusy ) { m_data = (POSIX_BATON_DATA *) memAlloc( sizeof( POSIX_BATON_DATA ) ); POSIX_BATON_DATA &p = *(POSIX_BATON_DATA *)m_data; p.m_bBusy = bBusy; p.m_bBlocked = false; #ifdef NDEBUG pthread_mutex_init( &p.m_mtx, 0 ); pthread_cond_init( &p.m_cv, 0 ); #else int res = pthread_mutex_init( &p.m_mtx, 0 ); fassert( res == 0 ); res = pthread_cond_init( &p.m_cv, 0 ); fassert( res == 0 ); #endif } Baton::~Baton() { POSIX_BATON_DATA &p = *(POSIX_BATON_DATA *)m_data; #ifdef NDEBUG pthread_mutex_destroy( &p.m_mtx ); pthread_cond_destroy( &p.m_cv ); #else int res = pthread_mutex_destroy( &p.m_mtx ); fassert( res == 0 ); res = pthread_cond_destroy( &p.m_cv ); fassert( res == 0 ); #endif memFree( m_data ); } void Baton::acquire() { POSIX_BATON_DATA &p = *(POSIX_BATON_DATA *)m_data; mutex_lock( p.m_mtx ); if( p.m_bBlocked && ! pthread_equal( pthread_self(), p.m_thBlocker ) ) { mutex_unlock( p.m_mtx ); // no problem if we release the mutex here: it's ok, as the semantic is just that of firing this // notify if we're blocked. onBlockedAcquire(); mutex_lock( p.m_mtx ); } while( p.m_bBusy || (p.m_bBlocked && ! pthread_equal( pthread_self(), p.m_thBlocker ) ) ) { cv_wait( p.m_cv, p.m_mtx ); } p.m_bBusy = true; p.m_bBlocked = false; mutex_unlock( p.m_mtx ); } bool Baton::busy() { POSIX_BATON_DATA &p = *(POSIX_BATON_DATA *)m_data; mutex_lock( p.m_mtx ); bool bbret = p.m_bBusy; mutex_unlock( p.m_mtx ); return bbret; } bool Baton::tryAcquire() { POSIX_BATON_DATA &p = *(POSIX_BATON_DATA *)m_data; mutex_lock( p.m_mtx ); if( p.m_bBusy || (p.m_bBlocked && ! pthread_equal( pthread_self(), p.m_thBlocker ) ) ) { mutex_unlock( p.m_mtx ); return false; } p.m_bBusy = true; p.m_bBlocked = false; mutex_unlock( p.m_mtx ); return true; } void Baton::release() { POSIX_BATON_DATA &p = *(POSIX_BATON_DATA *)m_data; mutex_lock( p.m_mtx ); p.m_bBusy = false; cv_broadcast( p.m_cv ); mutex_unlock( p.m_mtx ); } void Baton::checkBlock() { POSIX_BATON_DATA &p = *(POSIX_BATON_DATA *)m_data; mutex_lock( p.m_mtx ); p.m_bBusy = false; cv_broadcast( p.m_cv ); bool bb = (p.m_bBlocked && ! pthread_equal( pthread_self(), p.m_thBlocker ) ); if ( bb ) { mutex_unlock( p.m_mtx ); onBlockedAcquire(); mutex_lock( p.m_mtx ); } while( p.m_bBusy || (p.m_bBlocked && ! pthread_equal( pthread_self(), p.m_thBlocker ) ) ) { cv_wait( p.m_cv, p.m_mtx ); } p.m_bBusy = true; p.m_bBlocked = false; mutex_unlock( p.m_mtx ); } bool Baton::block() { POSIX_BATON_DATA &p = *(POSIX_BATON_DATA *)m_data; mutex_lock( p.m_mtx ); if ( (!p.m_bBusy) || (p.m_bBlocked && ! pthread_equal( pthread_self(), p.m_thBlocker )) ) { mutex_unlock( p.m_mtx ); return false; } if ( ! p.m_bBlocked ) { p.m_bBlocked = true; p.m_thBlocker = pthread_self(); } mutex_unlock( p.m_mtx ); return true; } bool Baton::unblock() { POSIX_BATON_DATA &p = *(POSIX_BATON_DATA *)m_data; mutex_lock( p.m_mtx ); if ( p.m_bBlocked && pthread_equal( pthread_self(), p.m_thBlocker ) ) { p.m_bBlocked = false; cv_broadcast( p.m_cv ); mutex_unlock( p.m_mtx ); return true; } mutex_unlock( p.m_mtx ); return false; } void Baton::onBlockedAcquire() { } } /* end of baton_posix.cpp */ engine/baton_win.cpp000066400000000000000000000154671176363201700150320ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: baton_win.cpp Baton synchronization structure. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 14 Mar 2009 00:03:28 +0100 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include namespace Falcon { typedef struct tag_WIN_BATON_DATA { HANDLE hEvtIdle; HANDLE hEvtUnblocked; DWORD nBlockerId; CRITICAL_SECTION cs; } WIN_BATON_DATA; Baton::Baton( bool bBusy ) { m_data = (WIN_BATON_DATA *) memAlloc( sizeof( WIN_BATON_DATA ) ); WIN_BATON_DATA &p = *(WIN_BATON_DATA *)m_data; p.nBlockerId = 0; // bBusy is reverse with respect to hEvtIdle p.hEvtIdle = CreateEvent( NULL, FALSE, bBusy ? FALSE : TRUE , NULL ); // Unblocker is manual reset (let's everyone go in). p.hEvtUnblocked = CreateEvent( NULL, TRUE, TRUE, NULL ); InitializeCriticalSection( &p.cs ); } Baton::~Baton() { WIN_BATON_DATA &p = *(WIN_BATON_DATA *)m_data; CloseHandle( p.hEvtIdle ); CloseHandle( p.hEvtUnblocked ); DeleteCriticalSection( &p.cs ); memFree( m_data ); } void Baton::acquire() { WIN_BATON_DATA &p = *(WIN_BATON_DATA *)m_data; EnterCriticalSection( &p.cs ); if( p.nBlockerId != GetCurrentThreadId() ) { if ( p.nBlockerId != 0 ) { LeaveCriticalSection( &p.cs ); // we're sure we hit the block onBlockedAcquire(); // we must wait both for block to be free and idle to be set. HANDLE won[] = { p.hEvtIdle, p.hEvtUnblocked }; DWORD nRes; nRes = WaitForMultipleObjects( 2, won, TRUE, INFINITE ); fassert( nRes == WAIT_OBJECT_0 ); } else { // Currently not blocked LeaveCriticalSection( &p.cs ); // we must ignore blocking requests incoming now, or we won't be able to signal that we're blocked. // In case we receive, the blocker will have to wait for next acquire. Let's say that now, // it's already too late to block us. DWORD nRes = WaitForSingleObject( p.hEvtIdle, INFINITE ); fassert( nRes == WAIT_OBJECT_0 ); } // we have acquired the IDLE event, which is auto reset, so we can proceed. return; } // we are the blocker; must wait only on the idle event. LeaveCriticalSection( &p.cs ); DWORD nRes; nRes = WaitForSingleObject( p.hEvtIdle, INFINITE ); fassert( nRes == WAIT_OBJECT_0 ); EnterCriticalSection( &p.cs ); p.nBlockerId = 0; LeaveCriticalSection( &p.cs ); // no more blocked. SetEvent( p.hEvtUnblocked ); } bool Baton::busy() { WIN_BATON_DATA &p = *(WIN_BATON_DATA *)m_data; // we must hold the mutex to avoid block to think that the baton is // busy, while we're actually keeping it busy here for a short time. EnterCriticalSection( &p.cs ); bool bRet = ! (WaitForSingleObject( p.hEvtIdle, 0 ) == WAIT_OBJECT_0); // if it's not busy, we have changed the idle setting; reset it. if( ! bRet ) { SetEvent(p.hEvtIdle); } LeaveCriticalSection( &p.cs ); return bRet; } bool Baton::tryAcquire() { WIN_BATON_DATA &p = *(WIN_BATON_DATA *)m_data; EnterCriticalSection( &p.cs ); if( p.nBlockerId != GetCurrentThreadId() ) { LeaveCriticalSection( &p.cs ); // we must wait both for block to be free and idle to be set. HANDLE won[] = { p.hEvtIdle, p.hEvtUnblocked }; DWORD nRes; nRes = WaitForMultipleObjects( 2, won, TRUE, 0 ); return (nRes == WAIT_OBJECT_0); } // we are the blocker; must wait only on the idle event. LeaveCriticalSection( &p.cs ); DWORD nRes; nRes = WaitForSingleObject( p.hEvtIdle, 0 ); if ( nRes == WAIT_OBJECT_0 ) { EnterCriticalSection( &p.cs ); p.nBlockerId = 0; LeaveCriticalSection( &p.cs ); // no more blocked. SetEvent( p.hEvtUnblocked ); return true; } return false; } void Baton::release() { WIN_BATON_DATA &p = *(WIN_BATON_DATA *)m_data; // Use sync section to be sure I am not idling the baton while someone // is trying to block it. EnterCriticalSection( &p.cs ); SetEvent( p.hEvtIdle ); LeaveCriticalSection( &p.cs ); } void Baton::checkBlock() { WIN_BATON_DATA &p = *(WIN_BATON_DATA *)m_data; EnterCriticalSection( &p.cs ); if( p.nBlockerId == GetCurrentThreadId() ) { LeaveCriticalSection( &p.cs ); // I am the blocker; there's nothing we should do now. return; } else { if( p.nBlockerId != 0 ) { // Idle in a sync section to avoid idling after blockers block. SetEvent( p.hEvtIdle ); LeaveCriticalSection( &p.cs ); onBlockedAcquire(); // wait for the unblocked event to be on; We just set idle, but we won't steal it // as we know for sure that EvtUnblocked is on. If it's not on (i.e. because the // blocker has forfaited in the meanwhile) we may re-acquire evtIdle, but that's ok. HANDLE won[] = { p.hEvtIdle, p.hEvtUnblocked }; DWORD nRes; nRes = WaitForMultipleObjects( 2, won, TRUE, INFINITE ); fassert(nRes == WAIT_OBJECT_0); } else { LeaveCriticalSection( &p.cs ); } } } bool Baton::block() { WIN_BATON_DATA &p = *(WIN_BATON_DATA *)m_data; // first, issue a block try EnterCriticalSection( &p.cs ); // already blocked? -- we can't do anything. if( p.nBlockerId != GetCurrentThreadId() && p.nBlockerId != 0 ) { LeaveCriticalSection( &p.cs ); return false; } // Noone can release the baton while we lock the mutex. // This gives us the chance to see if the baton is idle. if ( WaitForSingleObject( p.hEvtIdle, 0 ) == WAIT_OBJECT_0 ) { // Yes, the baton was idle (we jave just acquired it). // This means that noone can reply our block, so we can't perform that. // re-idle the baton and go away. SetEvent( p.hEvtIdle ); LeaveCriticalSection( &p.cs ); return false; } // Ok, we can block. p.nBlockerId = GetCurrentThreadId(); ResetEvent( p.hEvtUnblocked ); LeaveCriticalSection( &p.cs ); return true; } bool Baton::unblock() { WIN_BATON_DATA &p = *(WIN_BATON_DATA *)m_data; EnterCriticalSection( &p.cs ); if( p.nBlockerId != GetCurrentThreadId() ) { LeaveCriticalSection( &p.cs ); return false; } else { p.nBlockerId = 0; SetEvent( p.hEvtUnblocked ); LeaveCriticalSection( &p.cs ); } return true; } void Baton::onBlockedAcquire() { } } /* end of baton_win.cpp */ engine/cacheobject.cpp000066400000000000000000000111031176363201700152630ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: cacheobject.cpp Cache Object - Standard instance of classes in script - base ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom dic 5 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Falcon Object - Standard instance of classes in script - base */ #include #include #include #include #include namespace Falcon { CacheObject::CacheObject( const CoreClass *generator, bool bSeralizing ): CoreObject( generator ), m_cache( 0 ) { const PropertyTable &pt = m_generatedBy->properties(); m_cache = (Item *) memAlloc( sizeof( Item ) * pt.added() ); // We don't need to copy the default values if we're going to overwrite them. if ( ! bSeralizing ) { // TODO: Check if a plain copy would do. for ( uint32 i = 0; i < pt.added(); i ++ ) { const Item &itm = *pt.getValue(i); m_cache[i] = itm.isString() ? new CoreString( *itm.asString() ) : itm; } } } CacheObject::CacheObject( const CacheObject &other ): CoreObject( other.generator() ), m_cache( 0 ) { const PropertyTable &pt = m_generatedBy->properties(); m_cache = (Item *) memAlloc( sizeof( Item ) * pt.added() ); for( uint32 i = 0; i < pt.added(); i++ ) { const Item &itm = other.m_cache[i]; //TODO: GARBAGE m_cache[i] = itm.isString() ? new CoreString( *itm.asString() ) : itm; } } CacheObject::~CacheObject() { memFree( m_cache ); } void CacheObject::gcMark( uint32 mk ) { if( mk != mark() ) { CoreObject::gcMark( mk ); const PropertyTable &props = m_generatedBy->properties(); for ( uint32 i = 0; i < props.added(); i ++ ) { memPool->markItem( m_cache[i] ); } } } bool CacheObject::setProperty( const String &propName, const Item &value ) { fassert( m_generatedBy != 0 ); register uint32 pos; const PropertyTable &pt = m_generatedBy->properties(); if ( pt.findKey( propName, pos ) ) { if ( value.isReference() ) m_cache[ pos ] = value; else *m_cache[ pos ].dereference() = value; return true; } return false; } bool CacheObject::getProperty( const String &propName, Item &ret ) const { fassert( m_generatedBy != 0 ); register uint32 pos; const PropertyTable &pt = m_generatedBy->properties(); if ( pt.findKey( propName, pos ) ) { ret = *m_cache[pos].dereference(); return true; } return false; } bool CacheObject::serialize( Stream *stream, bool bLive ) const { uint32 len = m_generatedBy->properties().added(); uint32 elen = endianInt32( m_generatedBy->properties().added() ); stream->write((byte *) &elen, sizeof(elen) ); for( uint32 i = 0; i < len; i ++ ) { if ( m_cache[i].serialize( stream, bLive ) != Item::sc_ok ) return false; } CoreObject::serialize( stream, bLive ); return stream->good(); } bool CacheObject::deserialize( Stream *stream, bool bLive ) { VMachine *vm = VMachine::getCurrent(); // Read the class property table. uint32 len; stream->read( (byte *) &len, sizeof( len ) ); len = endianInt32( len ); if ( len != m_generatedBy->properties().added() ) { return false; } for( uint32 i = 0; i < len; i ++ ) { if( ! stream->good() ) { return false; } Item temp; Item::e_sercode sc = m_cache[i].deserialize( stream, vm ); if ( sc != Item::sc_ok ) { return false; } } CoreObject::deserialize( stream, bLive ); return true; } void CacheObject::reflectFrom( void *user_data ) { const PropertyTable &pt = m_generatedBy->properties(); for ( uint32 i = 0; i < pt.added(); i ++ ) { const PropEntry &entry = pt.getEntry(i); if( entry.m_eReflectMode != e_reflectNone ) { entry.reflectFrom( this, user_data, m_cache[i] ); } } } void CacheObject::reflectTo( void *user_data ) const { const PropertyTable &pt = m_generatedBy->properties(); for ( uint32 i = 0; i < pt.added(); i ++ ) { const PropEntry &entry = pt.getEntry(i); if( entry.m_eReflectMode != e_reflectNone ) { entry.reflectTo( const_cast(this), user_data, m_cache[i] ); } } } } /* end of cacheobject.cpp */ engine/citerator.cpp000066400000000000000000000025061176363201700150340ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: citerator.cpp Base abstract class for generic collection iterators. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 27 Mar 2009 01:45:22 -0700 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Base abstract class for generic collection iterators. Implementation of non virtual functions */ #include #include #include #include namespace Falcon { CoreIterator::CoreIterator(): m_creator(0), m_creatorSeq(0) { } CoreIterator::CoreIterator( const CoreIterator& other ): m_creator(0), m_creatorSeq(0) { m_creator = other.m_creator; m_creatorSeq = other.m_creatorSeq; } CoreIterator::~CoreIterator() { } void CoreIterator::setOwner( Garbageable *owner ) { m_creator = owner; } void CoreIterator::setOwner( Sequence *owner ) { m_creatorSeq = owner; } void CoreIterator::gcMark( uint32 mark ) { // AARRG if ( m_creator != 0 ) m_creator->mark( mark ); if ( m_creatorSeq != 0 ) m_creatorSeq->gcMark( mark ); } } /* end of citerator.cpp */ engine/compiler.cpp000066400000000000000000001045131176363201700146530ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: compiler.cpp Core language compiler. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 01-08-2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "core_module/core_module.h" #include namespace Falcon { AliasMap::AliasMap(): Map( &traits::t_stringptr(), &traits::t_voidp() ) { } //=============================================================== // Compiler //=============================================================== Compiler::Compiler( Module *mod, Stream* in ): m_constants( &traits::t_string(), &traits::t_voidp() ), m_namespaces( &traits::t_string(), &traits::t_voidp() ), m_root(0), m_errors(0), m_optLevel(0), m_lexer(0), m_stream( in ), m_module(0), m_enumId(0), m_staticPrefix(0), m_lambdaCount(0), m_closureContexts(0), m_tempLine(0), m_strict( false ), m_language( "C" ), m_modVersion( 0 ), m_defContext( false ), m_bParsingFtd(false), m_bInteractive( false ), m_rootError( 0 ), m_metacomp( 0 ), m_serviceVM( 0 ), m_serviceLoader( 0 ) { init(); m_module = mod; m_module->engineVersion( (FALCON_VERSION_NUM) ); addPredefs(); // reset FTD parsing mode parsingFtd(false); } Compiler::Compiler(): m_constants( &traits::t_string(), &traits::t_voidp() ), m_namespaces( &traits::t_string(), &traits::t_voidp() ), m_root(0), m_errors(0), m_optLevel(0), m_lexer(0), m_stream( 0 ), m_module( 0 ), m_enumId( 0 ), m_staticPrefix(0), m_lambdaCount(0), m_closureContexts(0), m_tempLine(0), m_strict( false ), m_language( "C" ), m_modVersion( 0 ), m_defContext( false ), m_bParsingFtd(false), m_bInteractive( false ), m_rootError( 0 ), m_metacomp(0), m_serviceVM( 0 ), m_serviceLoader( 0 ) { addPredefs(); } Compiler::~Compiler() { // clear doesn't clear the constants MapIterator iter = m_constants.begin(); while( iter.hasCurrent() ) { delete *(Value **) iter.currentValue(); iter.next(); } clear(); } void Compiler::init() { m_lexer = new SrcLexer( this ); if ( m_bParsingFtd ) m_lexer->parsingFtd( true ); m_root = new SourceTree; // context is empty, but the root context set must be placed. pushContextSet( &m_root->statements() ); // and an empty list for the local function undefined values List *l = new List; m_statementVals.pushBack( l ); m_errors = 0; m_enumId = 0; m_staticPrefix = 0; m_lambdaCount = 0; m_closureContexts = 0; m_tempLine = 0; // reset the require def status m_defContext = false; } void Compiler::reset() { if ( m_module != 0 ) { clear(); } m_namespaces.clear(); m_alias.clear(); m_context.clear(); m_func_ctx.clear(); m_contextSet.clear(); m_loops.clear(); m_errors = 0; m_enumId = 0; m_staticPrefix = 0; m_lambdaCount = 0; m_closureContexts = 0; m_tempLine = 0; // reset FTD parsing mode parsingFtd(false); } void Compiler::clear() { if( m_serviceVM != 0 ) m_serviceVM->finalize(); delete m_serviceLoader; delete m_metacomp; m_serviceVM = 0; m_serviceLoader = 0; m_metacomp = 0; delete m_root; delete m_lexer; m_root = 0; m_lexer= 0; m_functions.clear(); ListElement *valListE = m_statementVals.begin(); while( valListE != 0 ) { delete (List *) valListE->data(); valListE = valListE->next(); } m_statementVals.clear(); if ( m_rootError != 0 ) { m_rootError->decref(); m_rootError = 0; } m_module = 0; m_stream = 0; } bool Compiler::compile( Module *mod, Stream *in ) { if ( m_module != 0 ) { clear(); } // m_stream must be configured at init time. m_stream = in; init(); m_module = mod; m_module->engineVersion( FALCON_VERSION_NUM ); return compile(); } bool Compiler::compile() { if ( m_module == 0 || m_stream == 0 ) { raiseError( e_cmp_unprep ); return false; } // save directives bool bSaveStrict = m_strict; String savedLanguage = m_language; int64 modver = m_modVersion; // parse m_lexer->input( m_stream ); flc_src_parse( this ); if ( ! m_stream->good() ) { raiseError( new IoError( ErrorParam( e_io_error, __LINE__ ) .origin( e_orig_compiler ) .sysError( (uint32) m_stream->lastError() ) ) ); } m_module->language( m_language ); m_module->version( (uint32) m_modVersion ); // restore directives m_strict = bSaveStrict; m_language = savedLanguage; m_modVersion = modver; // If the context is not empty, then we have something unclosed. if ( ! m_context.empty() ) { Statement *stmt = getContext(); String temp = "from line "; temp.writeNumber( (int64) stmt->line() ); raiseError( e_unclosed_cs, temp ); } // leadout sequence if ( m_errors == 0 ) { if ( m_root->isExportAll() ) { // export all the symbol in the global module table m_module->symbolTable().exportUndefined(); } return true; } // we had errors. return false; } void Compiler::raiseError( int code, int line ) { raiseError( code, "", line ); } void Compiler::raiseContextError( int code, int line, int startLine ) { if ( line != startLine ) { m_lexer->resetContexts(); String temp = "from line "; temp.writeNumber( (int64) startLine ); raiseError( code, temp, line ); } else { raiseError( code, line ); } } void Compiler::raiseError( int code, const String &errorp, int line ) { if ( line == 0 ) line = lexer()->line()-1; SyntaxError *error = new SyntaxError( ErrorParam(code, line) .origin( e_orig_compiler ) ); error->extraDescription( errorp ); error->module( String(m_module->path()).bufferize() ); raiseError( error ); } void Compiler::raiseError( Error *error ) { if ( m_rootError == 0 ) { m_rootError = error; } else { error->module( m_module->path() ); m_rootError->appendSubError( error ); error->decref(); } m_errors++; } void Compiler::pushFunction( FuncDef *f ) { m_functions.pushBack( f ); m_statementVals.pushBack( new List ); m_alias.pushBack( new AliasMap ); } void Compiler::popFunction() { m_functions.popBack(); List *l = (List *) m_statementVals.back(); delete l; m_statementVals.popBack(); DeclarationContext *dc = new DeclarationContext; delete dc; AliasMap *temp = (AliasMap *) m_alias.back(); delete temp; m_alias.popBack(); } void Compiler::defineVal( ArrayDecl *val ) { ListElement *iter = val->begin(); while( iter != 0 ) { Value *val = (Value *) iter->data(); defineVal( val ); iter = iter->next(); } } void Compiler::defineVal( Value *val ) { // raise error for read-only expressions if ( val->isExpr() ) { // byte accessors cannot receive values if ( val->asExpr()->type() == Expression::t_array_byte_access ) { raiseError( e_byte_access, lexer()->previousLine() ); // but proceed } // assignments to accessors and function returns doesn't define anything. else if ( ! (val->asExpr()->type() == Expression::t_obj_access || val->asExpr()->type() == Expression::t_array_access || val->asExpr()->type() == Expression::t_funcall ) ) { Expression *expr = val->asExpr(); defineVal( expr->first() ); if( expr->second() != 0 ) { if ( expr->second()->isExpr() && expr->second()->asExpr()->type() == Expression::t_assign ) { defineVal( expr->second() ); } else if ( expr->isBinaryOperator() ) { raiseError( e_assign_const, lexer()->previousLine() ); } } } } else if ( val->isArray() ) { ListElement *it_s = val->asArray()->begin(); while( it_s != 0 ) { Value *t = (Value *) it_s->data(); defineVal( t ); it_s = it_s->next(); } } else if ( val->isImmediate() ) { raiseError( e_assign_const, lexer()->previousLine() ); } else if ( val->isSymdef() ) { if ( staticPrefix() == 0 ) { Symbol *sym; // are we in a function that misses that symbol? if ( getFunction() != 0 ) { // we're creating the local symbol // addlocal must also define the symbol sym = addLocalSymbol( *val->asSymdef(), false ); } else { // globals symbols that have been added as undefined must stay so. sym = m_module->findGlobalSymbol( *val->asSymdef() ); if ( sym == 0 ) { // values cannot be defined in this way if def is required AND we are not in a def context if ( m_strict && ! m_defContext ) { raiseError( e_undef_sym, "", lexer()->previousLine() ); } sym = addGlobalSymbol( *val->asSymdef() ); sym->declaredAt( lexer()->previousLine() ); sym->setGlobal(); } } val->setSymbol( sym ); } else { String symname = *staticPrefix() + "#" + *val->asSymdef(); Symbol *gsym = addGlobalSymbol( symname ); AliasMap &map = *(AliasMap*)m_alias.back(); map.insert( val->asSymdef(), gsym ); if( gsym->isUndefined() ) { if ( m_strict && ! m_defContext ) { raiseError( e_undef_sym, "", lexer()->previousLine() - 1); } gsym->setGlobal(); gsym->declaredAt( lexer()->previousLine() - 1); } val->setSymbol( gsym ); } } } Symbol *Compiler::addLocalSymbol( const String &symname, bool parameter ) { // fallback to add global if not in a local table FuncDef *func = getFunction(); if ( func == 0 ) return addGlobalSymbol( symname ); SymbolTable &table = func->symtab(); Symbol *sym = table.findByName( symname ); if( sym == 0 ) { // now we can add the symbol. As we have the string from // the module already, we keep it. sym = new Symbol( m_module, symname ); m_module->addSymbol( sym ); sym->declaredAt( lexer()->previousLine() ); // this flag marks closure forward definitions bool taken = false; if ( parameter ) { sym = func->addParameter( sym ); } else { // If we're in a closure, we may wish to add // a local undefined that will be filled at closure ending if ( m_closureContexts && searchLocalSymbol( symname, true ) != 0 ) { taken = true; sym = func->addUndefined( sym ); } else sym = func->addLocal( sym ); } // in strict mode raise an error if we're not in def, but not if // we taken the symbol from some parent. if ( !taken && m_strict && !m_defContext ) { raiseError( e_undef_sym, "", lexer()->previousLine() ); } } return sym; } bool Compiler::checkLocalUndefined() { List *l = (List *) m_statementVals.back(); while( ! l->empty() ) { Value *val = (Value *) l->front(); if ( val->isSymdef() ) { Symbol *sym = 0; if ( m_closureContexts > 0 ) { // still undefined after some loop? // in case of undef == x[undef.len()] we have double // un-definition of undef on the same line. fassert( m_functions.end() != 0 ); ListElement* le = m_functions.end(); const FuncDef *fd_current = reinterpret_cast( le->data() ); // try to find it locally -- may have been defined in a previous loop sym = fd_current->symtab().findByName( *val->asSymdef() ); if ( sym == 0 ) { le = le->prev(); while( le != 0 ) { const FuncDef *fd_parent = reinterpret_cast( le->data() ); if( fd_parent->symtab().findByName( *val->asSymdef() ) != 0 ) { sym = m_module->addSymbol(*val->asSymdef()); sym->declaredAt( lexer()->previousLine() ); getFunction()->addUndefined( sym ); break; } le = le->prev(); } } } // still nothing? if (sym == 0) { // --- import the symbol sym = addGlobalSymbol( *val->asSymdef() ); } val->setSymbol( sym ); } l->popFront(); } return true; } Symbol *Compiler::searchLocalSymbol( const String &symname, bool recurse ) { if( m_functions.empty() ) return searchGlobalSymbol( symname ); // first search the local symbol aliases AliasMap *map = (AliasMap *) m_alias.back(); Symbol **sympp = (Symbol **) map->find( &symname ); if ( sympp != 0 ) return *sympp; // then try in the local symtab or just return 0. ListElement* lastFunc = m_functions.end(); while( lastFunc != 0 ) { FuncDef *fd = (FuncDef *)lastFunc->data(); Symbol *found; if( (found = fd->symtab().findByName( symname )) ) return found; if ( ! recurse ) return 0; lastFunc = lastFunc->prev(); } // definitely not found. return 0; } Symbol *Compiler::searchOuterSymbol( const String &symname ) { ListElement *aliasIter = m_alias.end(); ListElement *funcIter = m_functions.end(); while( aliasIter != 0 && funcIter != 0 ) { AliasMap *map = (AliasMap *) aliasIter->data(); // first search the local symbol aliases Symbol **sympp = (Symbol **) map->find( &symname ); if ( sympp != 0 ) return *sympp; // then try in the local symtab or just return 0. FuncDef *fd = (FuncDef *) funcIter->data(); Symbol *sym = fd->symtab().findByName( symname ); if ( sym != 0 ) return sym; aliasIter = aliasIter->prev(); funcIter = funcIter->prev(); } return searchGlobalSymbol( symname ); } Symbol *Compiler::searchGlobalSymbol( const String &symname ) { return module()->findGlobalSymbol( symname ); } Symbol *Compiler::addGlobalSymbol( const String &symname ) { // is the symbol already defined? Symbol *sym = m_module->findGlobalSymbol( symname ); bool imported = false; if( sym == 0 ) { // check if it is authorized. // Unauthorized symbols are namespaced symbol not declared in import all clauses uint32 dotpos; if( (dotpos = symname.rfind ( "." ) ) != String::npos ) { // Namespaced symbol String nspace = symname.subString( 0, dotpos ); // change self into our name void **mode = (void **) m_namespaces.find( &nspace ); // if it's not a namespace, then it's an hard-built symbol as i.e. class._init if ( mode != 0 ) { // we wouldn't have a namespaced symbol if the lexer didn't find it was already in if( *mode == 0 ) { // if we were authorized, the symbol would have been created by // the clauses itself. raiseError( e_undef_sym, symname ); // but add it anyhow. } // namespaced symbols are always imported imported = true; } } sym = new Symbol( m_module, symname ); m_module->addGlobalSymbol( sym ); sym->declaredAt( lexer()->previousLine() ); sym->imported( imported ); } return sym; } Symbol *Compiler::addGlobalVar( const String &symname, VarDef *value ) { Symbol *sym = addGlobalSymbol( symname ); sym->declaredAt( lexer()->previousLine() ); sym->setVar( value ); return sym; } Symbol *Compiler::globalize( const String &symname ) { if ( ! isLocalContext() ) { // an error should be raised elsewhere. return 0; } // already alaised? raise an error AliasMap &map = *(AliasMap *) m_alias.back(); Symbol **ptr = (Symbol **) map.find( &symname ); if( ptr != 0 ) { raiseError( e_global_again, symname ); return *ptr; } // search for the global symbol that will be aliased Symbol *global = m_module->findGlobalSymbol( symname ); if ( global == 0 ) { global = m_module->addGlobal( symname, false ); global->declaredAt( lexer()->line() ); // it's defined in the module, the reference will be overwritten with // defineVal() -- else it will be searched outside the module. // (eventually causing a link error if not found). } map.insert( &symname, global ); return global; } StmtFunction *Compiler::buildCtorFor( StmtClass *cls ) { Symbol *sym = cls->symbol(); // Make sure we are not overdoing this. fassert( sym->isClass() ); fassert( sym->getClassDef()->constructor() == 0 ); // creates a name for the constructor ClassDef *def = sym->getClassDef(); String cname = sym->name() + "._init"; // creates an empty symbol Symbol *funcsym = addGlobalSymbol( cname ); //def->addProperty( addString( "_init" ) , new VarDef( funcsym ) ); // creates the syntree entry for the symbol; we are using the same line as the class. StmtFunction *stmt_ctor = new StmtFunction( cls->line(), funcsym ); addFunction( stmt_ctor ); // fills the symbol to be a valid constructor FuncDef *fdef = new FuncDef( 0, 0 ); funcsym->setFunction( fdef ); def->constructor( funcsym ); // now we must copy the parameter of the class in the parameters of the constructor. MapIterator iter = def->symtab().map().begin(); GenericVector params( &traits::t_voidp() ); while( iter.hasCurrent() ) { Symbol *symptr = *(Symbol **) iter.currentValue(); if ( symptr->isParam() ) { Symbol *p = m_module->addSymbol( symptr->name() ); fdef->addParameter( p ); p->itemId( symptr->itemId() ); } iter.next(); } cls->ctorFunction( stmt_ctor ); stmt_ctor->setConstructorFor( cls ); return stmt_ctor; } void Compiler::addPredefs() { addIntConstant( "NilType", FLC_ITEM_NIL ); addIntConstant( "BooleanType", FLC_ITEM_BOOL ); //addIntConstant( "IntegerType", FLC_ITEM_INT ); addIntConstant( "NumericType", FLC_ITEM_NUM ); addIntConstant( "RangeType", FLC_ITEM_RANGE ); addIntConstant( "FunctionType", FLC_ITEM_FUNC ); addIntConstant( "StringType", FLC_ITEM_STRING ); addIntConstant( "LBindType", FLC_ITEM_LBIND ); addIntConstant( "MemBufType", FLC_ITEM_MEMBUF ); addIntConstant( "ArrayType", FLC_ITEM_ARRAY ); addIntConstant( "DictionaryType", FLC_ITEM_DICT ); addIntConstant( "ObjectType", FLC_ITEM_OBJECT ); addIntConstant( "ClassType", FLC_ITEM_CLASS ); addIntConstant( "MethodType", FLC_ITEM_METHOD ); addIntConstant( "ClassMethodType", FLC_ITEM_CLSMETHOD ); addIntConstant( "UnboundType", FLC_ITEM_UNB ); } void Compiler::addAttribute( const String &name, Value *val, uint32 line ) { String n = name; FuncDef* fd = getFunction(); if( fd == 0 ) { m_module->addAttribute( name, val->genVarDef() ); } else { fd->addAttrib(name, val->genVarDef() ); } } void Compiler::addIntConstant( const String &name, int64 value, uint32 line ) { addConstant( name, new Value( value ), line ); } void Compiler::addNilConstant( const String &name, uint32 line ) { addConstant( name, new Value(), line ); } void Compiler::addStringConstant( const String &name, const String &value, uint32 line ) { if ( m_module != 0 ) addConstant( name, new Value( m_module->addString( value ) ), line ); else { // we'll leak, but oh, well... //TODO: fix the leak addConstant( name, new Value( new String( value ) ), line ); } } void Compiler::addNumConstant( const String &name, numeric value, uint32 line ) { addConstant( name, new Value( value ), line ); } void Compiler::addConstant( const String &name, Value *val, uint32 line ) { // is a constant with the same name defined? if ( m_constants.find( &name ) != 0 ) { raiseError( e_already_def, name, line ); return; } // is a symbol with the same name defined ? // Module may be zero (i.e. for pre-defined Falcon constants) if ( m_module != 0 && m_module->findGlobalSymbol( name ) != 0 ) { raiseError( e_already_def, name, line ); return; } if( ! val->isImmediate() ) { raiseError( e_assign_const, name, line ); return; } // create a global const symbol /* It's a thing I must think about Symbol *sym = new Symbol( m_module->addString( name ) ); sym->setConst( val->genVarDef() ); m_module->addGlobalSymbol( sym ); */ // add the constant to the compiler. String temp( name ); m_constants.insert( &temp, val ); } void Compiler::closeFunction() { StmtFunction *func = static_cast( getContext() ); Symbol *fsym = func->symbol(); FuncDef *def = fsym->getFuncDef(); // has this function a static block? if ( func->hasStatic() ) { def->onceItemId( m_module->addGlobal( "_once_" + fsym->name(), false )->itemId() ); } popContext(); popFunctionContext(); popContextSet(); popFunction(); } bool Compiler::parsingFtd() const { return m_bParsingFtd; } void Compiler::parsingFtd( bool b ) { m_bParsingFtd = b; if ( m_lexer != 0 ) m_lexer->parsingFtd( b ); } bool Compiler::setDirective( const String &directive, const String &value, bool bRaise ) { bool bWrongVal = false; if ( directive == "strict" ) { if ( value == "on" ) { m_strict = true; return true; } else if ( value == "off" ) { m_strict = false; return true; } bWrongVal = true; } else if ( directive == "lang" ) { m_language = value; return true; } // ... // if we're here we have either a wrong directive or a wrong value. if ( bRaise ) { if ( bWrongVal ) raiseError( e_directive_value, directive + "=" + value, m_lexer->line() ); else raiseError( e_directive_unk, directive, m_lexer->line() ); } return true; } bool Compiler::setDirective( const String &directive, int64 value, bool bRaise ) { bool bWrongVal = false; if ( directive == "strict" || directive == "lang" ) { bWrongVal = true; } else if ( directive == "version" ) { m_modVersion = value; return true; } // if we're here we have either a wrong directive or a wrong value. if ( bRaise ) { if ( bWrongVal ) { String temp = directive; temp += "="; temp.writeNumber( value ); raiseError( e_directive_value, temp, m_lexer->line() ); } else raiseError( e_directive_unk, directive, m_lexer->line() ); } return true; } Value *Compiler::closeClosure() { // first, close it as a normal function, but without mangling with the // local function table. StmtFunction *func = static_cast( getContext() ); FuncDef *fd = func->symbol()->getFuncDef(); // has this function a static block? if ( func->hasStatic() ) { fd->onceItemId( m_module->addGlobal( "_once_" + func->symbol()->name(), false )->itemId() ); } popContext(); popFunctionContext(); popContextSet(); popFunction(); decClosureContext(); // we're going to need a lambda call Value *lambda_call = new Value( new Falcon::Expression( Expression::t_lambda , new Value( func->symbol() ) ) ); // Is there some undefined? if( fd->undefined() > 0 ) { // we have to find all the local variables that exist in the upper context and // transform them in the first part of the local array. SymbolTable &funcTable = fd->symtab(); ArrayDecl *closureDecl = new ArrayDecl; //First; put parameters away, so that we can reorder them. const Map &symbols = funcTable.map(); MapIterator iter = symbols.begin(); int moved = 0; while( iter.hasCurrent() ) { Symbol *sym = *(Symbol **) iter.currentValue(); if ( sym->isLocalUndef() ) { // ok, this must become a local symbol... sym->setLocal(); sym->itemId( moved ); moved ++; // and now let's find the father's having this local undefined. fassert( m_functions.end() != 0 ); ListElement* le = m_functions.end(); Symbol *parentSym = 0; while( le != 0 ) { FuncDef *fd_parent = ( FuncDef *) le->data(); // when we find it... if( ( parentSym = fd_parent->symtab().findByName( sym->name() )) != 0 ) { // we must now force the declaration of local undefined for this // symbol up to this closure, so that it sent down the stack on runtime. le = le->next(); while( le != 0 ) { fd_parent = (FuncDef *) le->data(); parentSym = fd_parent->addUndefined( m_module->addSymbol( sym->name() ) ); le = le->next(); } //... and the parent symbol must be stored in parametric array... closureDecl->pushBack( new Value( parentSym ) ); break; } le = le->prev(); } if ( parentSym == 0 ) { // closures can't have undefs. raiseError( e_undef_sym, "", sym->declaredAt() ); } } else if ( sym->isLocal() ) { // push forward all the locals sym->itemId( fd->undefined() + sym->itemId() ); } iter.next(); } // no more undefs -- now they are locals fd->locals( fd->locals() + fd->undefined() ); fd->undefined( 0 ); // ... put as third element of the lambda call. lambda_call->asExpr()->second( new Value( closureDecl ) ); } // just create create a lambda call for this function return lambda_call; } void Compiler::addEnumerator( const String &str, Value *val ) { StmtClass *stmt = static_cast< StmtClass *>( getContext() ); ClassDef *cd = stmt->symbol()->getClassDef(); if ( cd->hasProperty( str ) ) { raiseError( e_already_def, str, lexer()->previousLine() ); } else { VarDef *vd = &m_module->addClassProperty( stmt->symbol(), str ); switch( val->type() ) { case Value::t_nil : // nothing to do break; case Value::t_imm_integer: vd->setInteger( val->asInteger() ); m_enumId = val->asInteger() + 1; break; case Value::t_imm_num: vd->setNumeric( val->asNumeric() ); m_enumId = int( val->asNumeric() ) + 1; break; case Value::t_imm_string: vd->setString( m_module->addString( *val->asString() ) ); break; case Value::t_imm_bool: vd->setBool( val->asBool() ); break; default: break; } } } void Compiler::addEnumerator( const String &str ) { Value dummy( (int64) m_enumId ); addEnumerator( str, &dummy ); } bool Compiler::isNamespace( const String &symName ) { return m_namespaces.find( &symName ) != 0; } void Compiler::addNamespace( const String &nspace, const String &alias, bool full, bool isFilename ) { // Purge special namespace names String nselfed; if ( alias.size() == 0 ) { // if the namespace starts with self, add also the namespace // with the same name of the module if( isFilename ) { // we got to convert "/" into "." Path fconv( nspace ); nselfed = fconv.getLocation() + "." + fconv.getFile(); uint32 pos = 0; if ( nselfed.getCharAt(0) == '/' ) nselfed = nselfed.subString( 1 ); while( (pos = nselfed.find( "/", pos ) ) != String::npos ) { nselfed.setCharAt( pos, '.' ); } } else if ( nspace.getCharAt(0) == '.' ) { nselfed = nspace.subString( 1 ); } else if ( nspace.find( "self." ) == 0 ) { nselfed = nspace.subString(5); } else { nselfed = nspace; } } else { nselfed = alias; } // Already added? void **res = (void **) m_namespaces.find( &nselfed ); if ( res == 0 ) { // yes? -- add it m_namespaces.insert( &nselfed, full ? (void *)1 : (void *) 0 ); // we have to insert in the namespaces all the sub-namespaces. uint32 dotpos = nselfed.find( "." ); while( dotpos != String::npos ) { String subSpace = nselfed.subString( 0, dotpos ); // don't overwrite in case we already imported something from it. void *oldNs = m_namespaces.find( &subSpace ); if ( oldNs == 0 ) { // we set a default of 0 for them as we normally import nothing. m_namespaces.insert( &subSpace, 0 ); } dotpos = nselfed.find( ".", dotpos+1 ); } m_module->addDepend( nselfed, nspace, true, isFilename ); } // no? else { // -- eventually change to load all. if ( *res == 0 && full ) *res = (void *) 1; // -- and check if this is a mis-redefinition // raise an error if you try to alias a different module with an alredy existing namespace ModuleDepData* mdPrev = m_module->dependencies().findModule( nselfed ); if ( mdPrev != 0 && mdPrev->moduleName() != nspace ) { raiseError( e_ns_clash, nselfed, lexer()->previousLine() ); } } } Symbol *Compiler::importAlias( const String &symName, const String &fromMod, const String &alias, bool filename ) { // add the dependency m_module->addDepend( fromMod, true, filename ); // add the alias Falcon::Symbol *sym = new Symbol( m_module, alias ); m_module->addGlobalSymbol( sym ); sym->declaredAt( lexer()->previousLine() ); // sets the alias sym->setImportAlias( symName, fromMod, filename ); return sym; } void Compiler::importSymbols( List *lst, const String& pre, const String& alias, bool isFilename ) { String prefix = pre; // add the namespace if not previously known if ( prefix.size() != 0 ) { if( alias.size() != 0 ) { addNamespace( prefix, alias, false, isFilename ); prefix = alias; } else { addNamespace( prefix, "", false, isFilename ); } } String fprefix; if( isFilename && alias.size() == 0 && prefix.size() != 0 ) { // we got to convert "/" into "." Path fconv( prefix ); fprefix = fconv.getLocation() + "." + fconv.getFile(); uint32 pos = 0; if ( fprefix.getCharAt(0) == '/' ) fprefix = fprefix.subString(1); while( (pos = fprefix.find( "/", pos ) ) != String::npos ) { fprefix.setCharAt( pos, '.' ); } } Falcon::ListElement *li = lst->begin(); while( li != 0 ) { Falcon::String& symName = *(String *) li->data(); if( prefix.size() != 0 ) { if( isFilename && alias.size() == 0 ) { symName = fprefix + "." + symName; } else if ( prefix.getCharAt(0) == '.' ) { symName = prefix.subString( 1 ) + "." + symName; } else if ( prefix.find( "self." ) == 0 ) { symName = prefix.subString(5) + "." + symName; } else symName = prefix + "." + symName; } Falcon::Symbol *sym = new Symbol( m_module, symName ); m_module->addGlobalSymbol( sym ); sym->declaredAt( lexer()->previousLine() ); sym->imported(true); delete &symName; li = li->next(); } delete lst; } void Compiler::metaCompile( const String &data, int startline ) { String ioEnc, srcEnc; Engine::getEncodings( srcEnc, ioEnc ); // evantually turn on the meta-compiler if ( m_metacomp == 0 ) { if ( m_serviceVM == 0 ) { m_serviceVM = new VMachine; m_serviceVM->appSearchPath( searchPath() ); Module*cm = core_module_init(); m_serviceVM->link( cm ); cm->decref(); } if ( m_serviceLoader == 0 ) { m_serviceLoader = new ModuleLoader( searchPath() ); m_serviceLoader->sourceEncoding( srcEnc ); } m_metacomp = new InteractiveCompiler( m_serviceLoader, m_serviceVM ); m_metacomp->searchPath( searchPath() ); // not incremental... m_metacomp->lexer()->incremental( false ); // do not confuse our user... m_metacomp->module()->path( module()->path() ); m_metacomp->module()->name( "[meta] " + module()->name() ); // transfer the constants. MapIterator iter = m_constants.begin(); while( iter.hasCurrent() ) { if ( m_metacomp->m_constants.find( iter.currentKey() ) == 0 ) { m_metacomp->addConstant( *(String *)iter.currentKey(), new Value( **(Value **) iter.currentValue()) ); } iter.next(); } } StringStream* output = new StringStream; // set the same encoding as the source that we're parsing. m_serviceVM->stdOut( TranscoderFactory( srcEnc, output, true ) ); // set current line in lexer of the meta compiler m_metacomp->tempLine( startline ); try { m_metacomp->compileAll( data ); // something has been written if ( output->length() != 0 ) { // pass it to the lexer output->seekBegin(0); StringStream *transferred = new StringStream(); transferred->transfer( *output ); m_lexer->appendStream( transferred ); } } catch( Error *e ) { raiseError( e ); } } } /* end of compiler.cpp */ engine/complex.cpp000066400000000000000000000020441176363201700145040ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: complex.cpp Complex class for Falcon Interface extension functions ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 09 Sep 2009 23:44:53 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Complex class for Falcon Internal logic functions - implementation. */ #include #include #include #include namespace Falcon { void Complex::throw_div_by_zero() { throw new MathError( ErrorParam( e_div_by_zero, __LINE__ ) .origin( e_orig_runtime ) .extra( "Complex number division by zero") ); } numeric Complex::abs() const { return sqrt( m_real * m_real + m_imag * m_imag ); } Complex Complex::conj() const { return Complex( m_real, m_imag * -1 ); } } /* end of complex.cpp */ engine/continuation.cpp000066400000000000000000000126241176363201700155540ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: continuation.h Continuation object. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 05 Dec 2009 17:04:27 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include namespace Falcon { Continuation::Continuation( VMachine* vm ): m_vm( vm ), m_tgtSymbol(0), m_tgtLModule(0), m_callingFrame(0), m_top(0), m_bottom(0), m_bComplete( false ) { m_context = vm->currentContext(); } Continuation::Continuation( const Continuation& e ): m_vm( e.m_vm ), m_tgtSymbol( e.m_tgtSymbol ), m_tgtLModule( e.m_tgtLModule ), m_bComplete( e.m_bComplete ) { if( e.m_top != 0 ) { m_top = e.m_top->copyDeep( &m_bottom ); } else { m_top = 0; m_bottom = 0; } m_callingFrame = e.m_callingFrame; m_context = e.m_context; m_stackLevel = e.m_stackLevel; m_tgtPC = e.m_tgtPC; } Continuation::~Continuation() { StackFrame* frame = m_top; while( frame != 0 ) { StackFrame* f = frame; frame = frame->prev(); delete f; } } bool Continuation::jump( const Item &jump_param ) { if ( m_vm->currentContext()->atomicMode() ) { throw new CodeError( ErrorParam( e_cont_atomic, __LINE__ ) .origin( e_orig_vm ) ); } m_callingFrame = m_vm->currentFrame(); m_context = m_vm->currentContext(); if ( m_tgtSymbol != 0 ) { fassert( m_tgtSymbol->isFunction() || m_tgtSymbol->isExtFunc() ); if( m_bComplete ) { throw new CodeError( ErrorParam( e_cont_out, __LINE__ ) .origin( e_orig_vm ) ); } m_bComplete = true; // engage the previous frame m_bottom->prev( m_callingFrame ); for ( uint32 i = 0; i < m_params.length(); ++i ) m_callingFrame->stack().append( m_params[i] ); m_bottom->prepareParams( m_callingFrame, m_params.length() ); // Set the new frame m_context->setFrames( m_top ); // jump -- by modifying the current FRAME stack, as it will be popped as we return m_top->m_symbol = m_tgtSymbol; m_top->m_module = m_tgtLModule; m_top->m_ret_pc = m_tgtPC; m_bottom = m_top = 0; // and set the return value that must be seen there. m_context->regA() = jump_param; return true; } m_bComplete = true; return false; } void Continuation::suspend( const Item& retval ) { if ( m_vm->currentContext()->atomicMode() ) { throw new CodeError( ErrorParam( e_cont_atomic, __LINE__ ) .origin( e_orig_vm ) ); } // find the calling frame. StackFrame* frame = m_vm->currentFrame(); while( frame->prev() != m_callingFrame ) { frame = frame->prev(); } // save the original parameters m_params.clear(); for( uint32 i = 0; i < frame->m_param_count; i++ ) { m_params.append( frame->m_params[i] ); } // disengage the stack. frame->prev(0); m_bottom = frame; m_top = m_vm->currentFrame(); // and remove the parameters m_callingFrame->pop( frame->m_param_count ); m_context->setFrames( m_callingFrame ); // prepare the resume values m_tgtSymbol = m_top->m_symbol; m_tgtLModule = m_top->m_module; m_tgtPC = m_top->m_ret_pc; m_vm->regA() = retval; // for sure, we need more call m_bComplete = false; // PC, module and symbol are in our return frame, which is invoked as we return. } bool Continuation::updateSuspendItem( const Item& itm ) { if ( m_top == 0 ) return false; StackFrame* t = m_top; while( t->prev() != m_bottom ) { t = t->prev(); } if( t->m_param_count ) t->m_params[0] = itm; return true; } //============================================================= // Continuation Carrier ContinuationCarrier::ContinuationCarrier( const CoreClass* cc ): CoreObject( cc ), m_cont(0), m_mark(0) {} ContinuationCarrier::ContinuationCarrier( const ContinuationCarrier& other ): CoreObject( other ), m_citem( other.m_citem ), m_mark(0) { if( other.m_cont != 0 ) m_cont = new Continuation( *other.m_cont ); else m_cont = 0; getMethod("_suspend", suspendItem() ); m_cont->updateSuspendItem( suspendItem() ); } ContinuationCarrier::~ContinuationCarrier() { delete m_cont; } ContinuationCarrier *ContinuationCarrier::clone() const { // for now, uncloneable return 0; //return new ContinuationCarrier( *this ); } bool ContinuationCarrier::setProperty( const String &prop, const Item &value ) { uint32 pos; if( m_generatedBy->properties().findKey( prop, pos ) ) readOnlyError( prop ); return false; } bool ContinuationCarrier::getProperty( const String &prop, Item &value ) const { return defaultProperty( prop, value ); } void ContinuationCarrier::gcMark( uint32 mark ) { if( mark == m_mark ) return; m_mark = mark; memPool->markItem( m_citem ); if ( m_cont != 0 ) { m_cont->params().gcMark( mark ); StackFrame* sf = m_cont->frames(); while( sf != 0 ) { sf->gcMark( mark ); sf = sf->prev(); } } } CoreObject* ContinuationCarrier::factory( const CoreClass* cls, void* , bool ) { return new ContinuationCarrier( cls ); } } engine/core_module/000077500000000000000000000000001176363201700146265ustar00rootroot00000000000000engine/core_module/array_ext.cpp000066400000000000000000001310331176363201700173310ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: array_ext.cpp Array specialized operation ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom nov 7 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @funset core_array_funcs Array support @brief Array related functions. @beginset core_array_funcs @inmodule core Functions in this set are meant to provide functional support to arrays. Some of them replicate operator or @a Array class methods. */ #include #include #include #include #include #include #include #include namespace Falcon { namespace core { /*# @method comp Array @brief Appends elements to this array through a filter. @param source A sequence, a range or a callable generating items. @optparam filter A filtering function receiving one item at a time. @return This array. Please, see the description of @a Sequence.comp. @see Sequence.comp */ FALCON_FUNC Array_comp ( ::Falcon::VMachine *vm ) { if ( vm->param(0) == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "R|A|C|Sequence, [C]" ) ); } // Save the parameters as the stack may change greatly. CoreArray* arr = vm->self().asArray(); Item i_gen = *vm->param(0); Item i_check = vm->param(1) == 0 ? Item(): *vm->param(1); arr->items().comprehension_start( vm, vm->self(), i_check ); vm->pushParam( i_gen ); } /*# @method mcomp Array @brief Appends multiple elements to this array. @param ... One or more sequences or item generators. @return This array. Please, see the description of @a Sequence.mcomp. @see Sequence.mcomp */ FALCON_FUNC Array_mcomp ( ::Falcon::VMachine *vm ) { CoreArray* arr = vm->self().asArray(); StackFrame* current = vm->currentFrame(); arr->items().comprehension_start( vm, vm->self(), Item() ); for( uint32 i = 0; i < current->m_param_count; ++i ) { vm->pushParam( current->m_params[i] ); } } /*# @method mfcomp Array @brief Appends multiple elements to this array through a filter. @param filter A filter function receiving each element before its insertion, or nil. @param ... One or more sequences or item generators. @return This array. Please, see the description of @a Sequence.mfcomp. @see Sequence.mfcomp */ FALCON_FUNC Array_mfcomp ( ::Falcon::VMachine *vm ) { if ( vm->param(0) == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "C, ..." ) ); } CoreArray* arr = vm->self().asArray(); Item i_check = *vm->param(0); StackFrame* current = vm->currentFrame(); arr->items().comprehension_start( vm, vm->self(), i_check ); for( uint32 i = 1; i < current->m_param_count; ++i ) { vm->pushParam( current->m_params[i] ); } } /*# @method concat Array @brief Concatenate all the elements of an array in a string. @optparam sep Separator to be put between the elements. @return A single string. */ FALCON_FUNC Array_concat ( ::Falcon::VMachine *vm ) { Item* i_sep = vm->param(0); if ( i_sep != 0 && ! i_sep->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "[S]" ) ); } CoreArray* arr = vm->self().asArray(); CoreString *str = new CoreString; uint32 len = arr->length(); for( uint32 i = 0; i < len ; ++i ) { const Item& item = arr->at(i); if ( item.isString() ) { str->append( *item.asString() ); } else { String temp; vm->itemToString( temp, &item ); str->append( temp ); } if( (i + 1)< len && i_sep ) str->append( *i_sep->asString() ); } vm->retval( str ); } /*# @method front Array @brief Returns and eventually extracts the first element in the array. @optparam remove true to remove the front item. @return The extracted item. @raise AccessError if the array is empty. */ FALCON_FUNC Array_front ( ::Falcon::VMachine *vm ) { CoreArray *array = vm->self().asArray(); if ( array->length() == 0 ) { throw new AccessError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->moduleString( rtl_emptyarr ) ) ); return; } vm->retval( array->at(0) ); if ( vm->param(0) != 0 && vm->param(0)->isTrue() ) array->remove(0); } /*# @method back Array @brief Returns and eventually extracts the last element in the array. @optparam remove true to remove the front item. @return The extracted item. @raise AccessError if the array is empty. */ FALCON_FUNC Array_back ( ::Falcon::VMachine *vm ) { CoreArray *array = vm->self().asArray(); if ( array->length() == 0 ) { throw new AccessError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ).extra( vm->moduleString( rtl_emptyarr ) ) ); } vm->retval( array->at( array->length() - 1 ) ); if ( vm->param(0) != 0 && vm->param(0)->isTrue() ) array->remove( array->length() - 1 ); } /*# @method table Array @brief Returns the table related with this array. @raise AccessError if the item is not an array. @return The table of which this item is a row. This array method retrieves the table that is related with the item, provided the item is an array being part of a table. In case the item is an array, but it doesn't belong to any table, nil is returned. */ FALCON_FUNC Array_table( VMachine *vm ) { CoreArray *array = vm->self().asArray(); if ( array->table() != 0 ) { vm->retval( array->table() ); return; } vm->retnil(); } /*# @method tabField Array @brief Returns one of the items in the array, given the field name. @param field The field name or position to be retrieved. @raise AccessError if the item is not an array. @return An item in the array or the default column value. If this item is an array and is part of a table, the field with the given name or ID (number) is searched in the table definition, and if found, it is returned. If the corresponding item in the array is nil, then the table column data (default data) is returned instead, unless the item is also an OOB item. In that case, nil is returned and the default column value is ignored. */ FALCON_FUNC Array_tabField( VMachine *vm ) { Item *i_field = vm->param( 0 ); if ( i_field == 0 || ! ( i_field->isString() || i_field->isOrdinal() )) { throw new ParamError( ErrorParam( e_inv_params ). extra( "(N)" ) ); return; } CoreArray *array = vm->self().asArray(); if ( array->table() != 0 ) { // if a field parameter is given, return its value uint32 num = (uint32) CoreTable::noitem; CoreTable *table = reinterpret_cast(array->table()->getFalconData()); if ( i_field->isString() ) { num = table->getHeaderPos( *i_field->asString() ); } else { // we already checked, must be a field num = (uint32) i_field->forceInteger(); } if ( num < array->length() ) { Item &data = (*array)[num]; if ( ! data.isNil() ) { vm->retval( data ); } else { if ( data.isOob() ) vm->retnil(); else vm->retval( table->columnData( num ) ); } return; } throw new AccessError( ErrorParam( e_param_range ) ); return; } vm->retnil(); } /*# @method tabRow Array @brief Returns the row ID of this element. @raise AccessError if the item is not a table row (array). @return A number indicating the position of this row in the table, or nil if this item is an array, but it's not stored in a table. This method returns the position of this element in a table. This number gets valorized only after a @a Table.get or @a Table.find method call, so that it is possible to know what index had this element in the owning table. If the table is changed by inserting or removing a row, the number returned by this function becomes meaningless. */ FALCON_FUNC Array_tabRow( VMachine *vm ) { CoreArray *array = vm->self().asArray(); if ( array->table() != 0 ) vm->retval( (int64) array->tablePos() ); else vm->retnil(); } /*# @method first Array @brief Returns an iterator to the head of this array. @return An iterator. */ FALCON_FUNC Array_first( VMachine *vm ) { Item *itclass = vm->findWKI( "Iterator" ); fassert( itclass != 0 ); CoreObject *iterator = itclass->asClass()->createInstance(); // we need to set the FalconData flag iterator->setUserData( new Iterator( &vm->self().asArray()->items() ) ); vm->retval( iterator ); } /*# @method last Array @brief Returns an iterator to the last element in this array. @return An iterator. */ FALCON_FUNC Array_last( VMachine *vm ) { Item *itclass = vm->findWKI( "Iterator" ); fassert( itclass != 0 ); CoreObject *iterator = itclass->asClass()->createInstance(); // we need to set the FalconData flag iterator->setUserData( new Iterator( &vm->self().asArray()->items(), true ) ); vm->retval( iterator ); } /*# @function arrayIns @brief Inserts an item into an array. @param array The array where the item should be placed. @param itempos The position where the item should be placed. @param item The item to be inserted. The item is inserted before the given position. If pos is 0, the item is inserted in the very first position, while if it's equal to the array length, it is appended at the array tail. */ /*# @method ins Array @brief Inserts an item into this array array. @param itempos The position where the item should be placed. @param item The item to be inserted. The item is inserted before the given position. If pos is 0, the item is inserted in the very first position, while if it's equal to the array length, it is appended at the array tail. */ FALCON_FUNC mth_arrayIns ( ::Falcon::VMachine *vm ) { Item *array_x; Item *item_pos; Item *item; if ( vm->self().isMethodic() ) { array_x = &vm->self(); item_pos = vm->param(0); item = vm->param(1); } else { array_x = vm->param(0); item_pos = vm->param(1); item = vm->param(2); } if ( array_x == 0 || ! array_x->isArray() || item_pos == 0 || ! item_pos->isOrdinal() || item == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra( vm->self().isMethodic() ? "N,X" : "A,N,X" ) ); } CoreArray *array = array_x->asArray(); int32 pos = (int32) item_pos->forceInteger(); if ( ! array->insert( *item, pos ) ) { // array access error throw new AccessError( ErrorParam( e_arracc, __LINE__ ) .origin( e_orig_runtime ) ); } } /*# @function arrayDel @brief Deletes the first element matching a given item. @param array The array that is to be changed. @param item The item that must be deleted. @return true if at least one item has been removed, false otherwise. The function scans the array searching for an item that is considered equal to the given one. If such an item can be found, it is removed and the function returns true. If the item cannot be found, false is returned. Only the first item matching the given one will be deleted. */ /*# @method del Array @brief Deletes the first element matching a given item. @param item The item that must be deleted. @return true if at least one item has been removed, false otherwise. The function scans the array searching for an item that is considered equal to the given one. If such an item can be found, it is removed and the function returns true. If the item cannot be found, false is returned. Only the first item matching the given one will be deleted. */ FALCON_FUNC mth_arrayDel ( ::Falcon::VMachine *vm ) { Item *array_x; Item *item_rem; if ( vm->self().isMethodic() ) { array_x = &vm->self(); item_rem = vm->param(0); } else { array_x = vm->param(0); item_rem = vm->param(1); } if ( array_x == 0 || ! array_x->isArray() || item_rem == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "X" : "A,X" ) ); } // find the element CoreArray *array = array_x->asArray(); const ItemArray& elements = array->items(); for( uint32 i = 0; i < array->length(); i++ ) { if ( elements[i] == *item_rem ) { array->remove( i ); vm->regA().setBoolean(true); return; } } vm->regA().setBoolean(false); } /*# @function arrayDelAll @brief Deletes all the occurrences of a given item in an array. @param array The array that is to be changed. @param item The item that must be deleted. @return true if at least one item has been removed, false otherwise. This function removes all the elements of the given array that are considered equivalent to the given item. If one or more elements have been found and deleted, the function will return true, else it will return false. */ /*# @method delAll Array @brief Deletes all the occurrences of a given item in this array. @param item The item that must be deleted. @return true if at least one item has been removed, false otherwise. This function removes all the elements in this array that are considered equivalent to the given item. If one or more elements have been found and deleted, the function will return true, else it will return false. @see filter */ FALCON_FUNC mth_arrayDelAll ( ::Falcon::VMachine *vm ) { Item *array_x; Item *item_rem; if ( vm->self().isMethodic() ) { array_x = &vm->self(); item_rem = vm->param(0); } else { array_x = vm->param(0); item_rem = vm->param(1); } if ( array_x == 0 || ! array_x->isArray() || item_rem == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "X" : "A,X" ) ); } // find the element bool done = false; CoreArray *array = array_x->asArray(); const ItemArray& elements = array->items(); uint32 i = 0; while( i < array->length() ) { if ( elements[i] == *item_rem ) { array->remove( i ); done = true; } else i++; } vm->regA().setBoolean( done ); } /*# @function arrayAdd @brief Adds an element to an array. @param array The array where to add the new item. @param item The item to be added. The element will be added at the end of the array, and its size will be increased by one. */ /*# @method add Array @brief Adds an element to this array. @param item The item to be added. The element will be added at the end of this array, and its size will be increased by one. */ FALCON_FUNC mth_arrayAdd ( ::Falcon::VMachine *vm ) { Item *array_x; Item *item; if ( vm->self().isMethodic() ) { array_x = &vm->self(); item = vm->param(0); } else { array_x = vm->param(0); item = vm->param(1); } if ( array_x == 0 || ! array_x->isArray() || item == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "X" : "A,X" ) ); } CoreArray *array = array_x->asArray(); array->append( *item ); } /*# @function arrayFill @brief Fills the array with the given element. @param array The array where to add the new item. @param item The item to be replicated. @return The same @b array passed as parameter. This method allows to clear a whole array, resetting all the elements to a default value. */ /*# @method fill Array @brief Fills the array with the given element. @param item The item to be replicated. @return This array. This method allows to clear a whole array, resetting all the elements to a default value. */ FALCON_FUNC mth_arrayFill ( ::Falcon::VMachine *vm ) { Item *i_array; Item *i_item; if ( vm->self().isMethodic() ) { i_array = &vm->self(); i_item = vm->param(0); } else { i_array = vm->param(0); i_item = vm->param(1); } if ( i_array == 0 || ! i_array->isArray() || i_item == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "X" : "A,X" ) ); } CoreArray *array = i_array->asArray(); for ( uint32 i = 0; i < array->length(); i ++ ) { if ( i_item->isString() ) (*array)[i] = new CoreString( *i_item->asString() ); else (*array)[i] = *i_item; } vm->retval( array ); } /*# @function arrayResize @brief Changes the size of the array. @param array The array that will be resize. @param newSize The new size for the array. If the given size is smaller than the current size, the array is shortened. If it's larger, the array is grown up to the desired size, and the missing elements are filled by adding nil values. */ FALCON_FUNC mth_arrayResize ( ::Falcon::VMachine *vm ) { Item *array_x; Item *item_size; if ( vm->self().isMethodic() ) { array_x = &vm->self(); item_size = vm->param(0); } else { array_x = vm->param(0); item_size = vm->param(1); } if ( array_x == 0 || array_x->type() != FLC_ITEM_ARRAY || item_size == 0 || !item_size->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "N" : "A,N" ) ); } CoreArray *array = array_x->asArray(); int64 size = item_size->forceInteger(); if ( size < 0 ) size = 0; array->resize( (uint32) size ); } /*# @function arrayRemove @brief Removes one or more elements in the array. @param array The array from which items must be removed. @param itemPos The position of the item to be removed, or the first of the items to be removed. @optparam lastItemPos The last item to be removed, in range semantic. Remove one item or a range of items. The size of the array is shortened accordingly. The semantic of @b lastItemPos is the same as ranged access to the array. */ /*# @method remove Array @brief Removes one or more elements in the array. @param itemPos The position of the item to be removed, or the first of the items to be removed. @optparam lastItemPos The last item to be removed, in range semantic. Remove one item or a range of items. The size of the array is shortened accordingly. The semantic of @b lastItemPos is the same as ranged access to the array. */ FALCON_FUNC mth_arrayRemove( ::Falcon::VMachine *vm ) { Item *array_x; Item *item_start; Item *item_end; if ( vm->self().isMethodic() ) { array_x = &vm->self(); item_start = vm->param(0); item_end = vm->param(1); } else { array_x = vm->param(0); item_start = vm->param(1); item_end = vm->param(2); } if ( array_x == 0 || !array_x->isArray() || item_start == 0 || !item_start->isOrdinal() || (item_end != 0 && !item_end->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "N,[N]" : "A,N,[N]" ) ); } CoreArray *array = array_x->asArray(); int32 pos = (int32) item_start->forceInteger(); bool res; if ( item_end != 0 ) { int32 posLast = (int32) item_end->forceInteger(); res = array->remove( pos, posLast ); } else res = array->remove( pos ); if ( ! res ) { throw new AccessError( ErrorParam( e_arracc, __LINE__ ). origin( e_orig_runtime ) ); } } /*# @function arrayBuffer @brief Creates an array filled with nil items. @param size The length of the returned array. @optparam defItem The default item to be that will fill the array. @return An array filled with @b size nil elements. This function is useful when the caller knows the number of needed items. In this way, it is just necessary to set the various elements to their values, rather than adding them to the array. This will result in faster operations. If @b defItem is not given, the elements in the returned array will be set to nil, otherwise each element will be a flat copy of @b item. */ FALCON_FUNC arrayBuffer ( ::Falcon::VMachine *vm ) { Item *item_size = vm->param(0); Item *i_item = vm->param(1); if ( item_size == 0 || ! item_size->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "N,[X]" ) ); } uint32 nsize = (uint32) item_size->forceInteger(); CoreArray *array = new CoreArray; array->resize( nsize ); if( i_item != 0 ) { for ( uint32 i = 0; i < nsize; i++ ) { array->items()[i] = *i_item; } } vm->retval( array ); } /*# @function arrayCompact @brief Reduces the memory used by an array. @param array The array itself. @return Itself Normally, array operations, as insertions, additions and so on cause the array to grow to accommodate items that may come in the future. Also, reducing the size of an array doesn't automatically dispose of the memory held by the array to store its elements. This function grants that the memory used by the array is strictly the memory needed to store its data. */ /*# @method compact Array @brief Reduces the memory used by an array. @return Itself Normally, array operations, as insertions, additions and so on cause the array to grow to accommodate items that may come in the future. Also, reducing the size of an array doesn't automatically dispose of the memory held by the array to store its elements. This method grants that the memory used by the array is strictly the memory needed to store its data. */ FALCON_FUNC mth_arrayCompact ( ::Falcon::VMachine *vm ) { Item *array_x; if ( vm->self().isMethodic() ) { array_x = &vm->self(); } else { array_x = vm->param(0); if ( array_x == 0 || !array_x->isArray() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "A" ) ); } } array_x->asArray()->items().compact(); vm->retval( *array_x ); } /*# @function arrayCopy @brief Copies an array into another array. @param dest The destination array. @param from Starting position in the destination array. @param src The source array. @param first Starting position in the source array. @optparam amount Optional The number of items to be copied. @raise AccessError @b from is greater than the length of the destination array or @b first is greater than the length of the source array. Copies the source array, starting from the specified position, to the specified position of the destination array. */ /*# @method copyFrom Array @brief Copies an array into another array. @param from Starting position in the array. @param src The source array. @param first Starting position in the source array. @optparam amount Optional The number of items to be copied. @raise AccessError @b from is greater than the length of the array or @b first is greater than the length of the source array. Copies the source array, starting from the specified position, to the specified position of the array. */ FALCON_FUNC mth_arrayCopy( ::Falcon::VMachine *vm ) { Item *dest_i; Item *from_i; Item *src_i; Item *first_i; Item *amount_i; if( vm->self().isMethodic() ) { dest_i = &vm->self(); from_i = vm->param( 0 ); src_i = vm->param( 1 ); first_i = vm->param( 2 ); amount_i = vm->param( 3 ); } else { dest_i = vm->param( 0 ); from_i = vm->param( 1 ); src_i = vm->param( 2 ); first_i = vm->param( 3 ); amount_i = vm->param( 4 ); } if ( dest_i == 0 || !dest_i->isArray() || from_i == 0 || !from_i->isOrdinal() || src_i == 0 || !src_i->isArray() || first_i == 0 || !first_i->isOrdinal() || ( amount_i != 0 && !amount_i->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "N,A,N,[N]" : "A,N,A,N,[N]" ) ); } CoreArray *src = src_i->asArray(); if ( src->length() == 0 ) return; CoreArray *dest = dest_i->asArray(); int32 from = (int32) ( from_i->asInteger() ); int32 first = (int32) ( first_i->asInteger() ); int32 amount = (int32) ( amount_i == 0 ? src->length() : amount_i->asInteger() ); if ( ! dest->items().copyOnto( from, src->items(), first, amount ) ) { throw new AccessError( ErrorParam( e_arracc, __LINE__ ) .origin( e_orig_runtime ) ); } } /*# @function arrayHead @brief Extracts the first element of an array and returns it. @param array The array that will be modified. @return The extracted item. @raise AccessError if the array is empty. This function removes the first item of the array and returns it. If the original array is empty, AccessError is raised. @see Array.front @see Array.head */ /*# @method head Array @brief Extracts the first element from this array and returns it. @return The extracted item. @raise AccessError if the array is empty. This function removes the first item from the array and returns it. If the original array is empty, AccessError is raised. @see Array.front */ FALCON_FUNC mth_arrayHead ( ::Falcon::VMachine *vm ) { Item *array_x; if( vm->self().isMethodic() ) { array_x = &vm->self(); } else { array_x = vm->param(0); if ( array_x == 0 || ! array_x->isArray() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ).extra( "A" ) ); } } CoreArray *array = array_x->asArray(); if ( array->length() == 0 ) { throw new AccessError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ).extra( vm->moduleString( rtl_emptyarr ) ) ); return; } vm->retval( array->at(0) ); array->remove(0); } /*# @function arrayTail @brief Extracts the last element of an array and returns it. @param array The array that will be modified. @return The extracted item. This function removes the last item of the array and returns it. If the original array is empty, AccessError is raised. */ /*# @method tail Array @brief Extracts the last element of an array and returns it. @return The extracted item. This function removes the last item of the array and returns it. If the original array is empty, AccessError is raised. */ FALCON_FUNC mth_arrayTail ( ::Falcon::VMachine *vm ) { Item *array_x; if( vm->self().isMethodic() ) { array_x = &vm->self(); } else { array_x = vm->param(0); if ( array_x == 0 || ! array_x->isArray() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ).extra( "A" ) ); } } CoreArray *array = array_x->asArray(); if ( array->length() == 0 ) { throw new AccessError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ).extra( vm->moduleString( rtl_emptyarr ) ) ); return; } vm->retval( array->at(array->length()-1) ); array->remove(array->length()-1); } /*# @function arrayFind @brief Searches for a given item in an array. @param array The array that will be searched. @param item The array that will be searched. @optparam start Optional first element to be searched. @optparam end Optional last element +1 to be searched. @return The position of the searched item in the array, or -1 if not found. This function searches the array for a given item (or for an item considered equivalent to the given one). If that item is found, the item position is returned, else -1 is returned. An optional range may be specified to limit the search in the interval start included, end excluded. */ /*# @method find Array @brief Searches for a given item in an array. @param item The array that will be searched. @optparam start Optional first element to be searched. @optparam end Optional last element +1 to be searched. @return The position of the searched item in the array, or -1 if not found. This function searches the array for a given item (or for an item considered equivalent to the given one). If that item is found, the item position is returned, else -1 is returned. An optional range may be specified to limit the search in the interval start included, end excluded. */ FALCON_FUNC mth_arrayFind ( ::Falcon::VMachine *vm ) { Item *array_x; Item *item; Item *start; Item *end; if ( vm->self().isMethodic() ) { array_x = &vm->self(); item = vm->param(0); start = vm->param(1); end = vm->param(2); } else { array_x = vm->param(0); item = vm->param(1); start = vm->param(2); end = vm->param(3); } if ( array_x == 0 || ! array_x->isArray() || item == 0 || (start != 0 && ! start->isOrdinal()) || ( end != 0 && ! end->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "X,[N],[N]" : "A,X,[N],[N]" ) ); } CoreArray *array = array_x->asArray(); // can find nothing in an empty array if ( array->length() == 0 ) { vm->retval( -1 ); return; } int32 pos_start = (int32) (start == 0 ? 0 : start->asInteger()); int32 pos_end = (int32) (end == 0 ? array->length() : end->asInteger()); if ( pos_start > pos_end ) { int32 temp = pos_start; pos_start = pos_end; pos_end = temp; } if ( pos_start < 0 || pos_start >= (int32) array->length() || pos_end > (int32) array->length()) { throw new AccessError( ErrorParam( e_arracc, __LINE__ ). origin( e_orig_runtime ) ); return; } const ItemArray& elements = array->items(); for( int32 i = pos_start; i < pos_end; i++ ) { if ( elements[i] == *item ) { vm->retval(i); return; } } vm->retval(-1); } /*# @function arrayScan @brief Searches an array for an item satisfying arbitrary criteria. @param array The array that will be searched. @param func Function that verifies the criterion. @optparam start Optional first element to be searched. @optparam end Optional upper end of the range.. @return The position of the searched item in the array, or -1 if not found. With this function, it is possible to specify an arbitrary search criterion. The items in the array are fed one after another as the single parameter to the provided scanFunc, which may be any Falcon callable item, including a method. If the scanFunc returns true, the scan is interrupted and the index of the item is returned. If the search is unsuccessful, -1 is returned. An optional start parameter may be provided to begin searching from a certain point on. If also an end parameter is given, the search is taken between start included and end excluded (that is, the search terminates when at the element before the position indicated by end). Scan function is called in atomic mode. The called function cannot be interrupted by external kind requests, and it cannot sleep or yield the execution to other coroutines. */ /*# @method scan Array @brief Searches an array for an item satisfying arbitrary criteria. @param func Function that verifies the criterion. @optparam start Optional first element to be searched. @optparam end Optional upper end of the range. @return The position of the searched item in the array, or -1 if not found. @see arrayScan */ FALCON_FUNC mth_arrayScan ( ::Falcon::VMachine *vm ) { Item *array_x; Item *func_x; Item *start; Item *end; if ( vm->self().isMethodic() ) { array_x = &vm->self(); func_x = vm->param(0); start = vm->param(1); end = vm->param(2); } else { array_x = vm->param(0); func_x = vm->param(1); start = vm->param(2); end = vm->param(3); } if ( array_x == 0 || ! array_x->isArray() || func_x == 0 || ! func_x->isCallable() || (start != 0 && ! start->isOrdinal()) || ( end != 0 && ! end->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "C,[N],[N]" : "A,C,[N],[N]" ) ); } CoreArray *array = array_x->asArray(); // can find nothing in an empty array if ( array->length() == 0 ) { vm->retval( -1 ); return; } int32 pos_start = (int32) (start == 0 ? 0 : start->asInteger()); int32 pos_end = (int32) (end == 0 ? array->length() : end->asInteger()); if ( pos_start > pos_end ) { int32 temp = pos_start; pos_start = pos_end; pos_end = temp; } if ( pos_start < 0 || pos_start >= (int32) array->length() || pos_end > (int32) array->length()) { throw new AccessError( ErrorParam( e_arracc, __LINE__ ). origin( e_orig_runtime ) ); } const ItemArray& elements = array->items(); // fetching as we're going to change the stack Item func = *func_x; for( int32 i = pos_start; i < pos_end; i++ ) { vm->pushParam( elements[i] ); vm->callItemAtomic( func, 1 ); if ( vm->regA().isTrue() ) { vm->retval( i ); return; } } vm->retval(-1); } #define MINMAL_QUICKSORT_THRESHOLD 4 inline void arraySort_swap( Item *a, int i, int j) { Item T = a[i]; a[i] = a[j]; a[j] = T; } static void arraySort_quickSort( Item *array, int32 l, int32 r ) { int32 i; int32 j; Item v; if ( ( r-l ) > MINMAL_QUICKSORT_THRESHOLD ) { i = ( r + l ) / 2; if ( array[l] > array[i] ) arraySort_swap( array, l , i ); if ( array[l] > array[r] ) arraySort_swap( array , l, r ); if ( array[i] > array[r] ) arraySort_swap( array, i, r ); j = r - 1; arraySort_swap( array , i, j ); i = l; v = array[j]; for(;;) { while( array[++i] < v ); while( array[--j] > v ); if ( j < i ) break; arraySort_swap ( array , i , j ); } arraySort_swap( array , i, r - 1 ); arraySort_quickSort( array, l , j ); arraySort_quickSort( array, i + 1 , r ); } } static void arraySort_insertionSort( VMachine *vm, Item *array, int lo0, int hi0) { int32 i; int32 j; Item v; for ( i = lo0 + 1 ; i <= hi0 ; i++ ) { v = array[i]; j = i; while ( ( j > lo0 ) && ( array[j-1] > v ) ) { array[j] = array[j-1]; j--; } array[j] = v; } } static void arraySort_quickSort_flex( VMachine *vm, Item *comparer, Item *array, int32 l, int32 r ) { int32 i; int32 j; Item v; if ( ( r-l ) > MINMAL_QUICKSORT_THRESHOLD ) { i = ( r + l ) / 2; vm->pushParam( array[l] ); vm->pushParam( array[i] ); vm->callItemAtomic( *comparer, 2 ); if ( vm->regA().asInteger() > 0 ) arraySort_swap( array, l , i ); vm->pushParam( array[l] ); vm->pushParam( array[r] ); vm->callItemAtomic( *comparer, 2 ); if ( vm->regA().asInteger() > 0 ) arraySort_swap( array , l, r ); vm->pushParam( array[i] ); vm->pushParam( array[r] ); vm->callItemAtomic( *comparer, 2 ); if ( vm->regA().asInteger() > 0 ) arraySort_swap( array, i, r ); j = r - 1; arraySort_swap( array, i, j ); i = l; v = array[j]; for(;;) { do { vm->pushParam( array[++i] ); vm->pushParam( v ); vm->callItemAtomic( *comparer, 2 ); } while( vm->regA().asInteger() < 0 ); do { vm->pushParam( array[--j] ); vm->pushParam( v ); vm->callItemAtomic( *comparer, 2 ); } while( vm->regA().asInteger() > 0 ); if ( j < i ) break; arraySort_swap ( array , i , j ); } arraySort_swap( array , i, r - 1 ); arraySort_quickSort( array, l , j ); arraySort_quickSort( array, i + 1 , r ); } } static void arraySort_insertionSort_flex( VMachine *vm, Item *comparer, Item *array, int lo0, int hi0) { int32 i; int32 j; Item v; for ( i = lo0 + 1 ; i <= hi0 ; i++ ) { v = array[i]; j = i; while ( j > lo0 ) { vm->pushParam( array[j-1] ); vm->pushParam( v ); vm->callItemAtomic( *comparer, 2 ); if ( vm->regA().asInteger() <= 0 ) break; array[j] = array[j-1]; j--; } array[j] = v; } } /*# @function arraySort @brief Sorts an array, possibly using an arbitrary ordering criterion. @param array The array that will be searched. @optparam sortingFunc A function used to compare two items. The function sorts the contents of the array so that the first element is the smaller one, and the last element is the bigger one. String sorting is performed lexicographically. To sort the data based on an arbitrary criterion, or to sort complex items, or objects, based on some of their contents, the caller may provide a sortFunc that will receive two parameters. The sortFunc must return -1 if the first parameter is be considered greater than the second, 0 if they are equal and 1 if the second parameter is to be considered greater. Sort function is called in atomic mode. The called function cannot be interrupted by external kind requests, and it cannot sleep or yield the execution to other coroutines. */ /*# @method sort Array @brief Sorts an array, possibly using an arbitrary ordering criterion. @optparam sortingFunc A function used to compare two items. @see arraySort */ FALCON_FUNC mth_arraySort( ::Falcon::VMachine *vm ) { Item *array_itm; Item *sorter_itm; if( vm->self().isMethodic() ) { array_itm = &vm->self(); sorter_itm = vm->param( 0 ); } else { array_itm = vm->param( 0 ); sorter_itm = vm->param( 1 ); } if ( array_itm == 0 || ! array_itm->isArray() || (sorter_itm != 0 && ! sorter_itm->isCallable()) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra( vm->self().isMethodic() ? "C" : "A,C" ) ); } CoreArray *array = array_itm->asArray(); Item *vector = array->items().elements(); if ( sorter_itm == 0 ) { arraySort_quickSort( vector, 0, array->length() - 1 ); arraySort_insertionSort( vm, vector, 0, array->length() -1 ); } else { // fetching as we're going to change the stack Item sorter = *sorter_itm; arraySort_quickSort_flex( vm, &sorter, vector, 0, array->length() - 1 ); arraySort_insertionSort_flex( vm, &sorter, vector, 0, array->length() -1 ); } } /*# @function arrayMerge @brief Merges two arrays. @param array1 Array containing the first half of the merge, that will be modified. @param array2 Array containing the second half of the merge, read-only @optparam insertPos Optional position of array 1 at which to place array2 @optparam start First element of array2 to merge in array1 @optparam end Last element - 1 of array2 to merge in array1 The items in array2 are appended to the end of array1, or in case an mergePos is specified, they are inserted at the given position. If mergePos is 0, they are inserted at beginning of array1, while if it's equal to array1 size they are appended at the end. An optional start parameter may be used to specify the first element in the array2 that must be copied in array1; if given, the parameter end will specify the last element that must be copied plus 1; that is elements are copied from array2 in array1 from start to end excluded. The items are copied shallowly. This means that if an object is in array2 and it's modified thereafter, both array2 and array1 will grant access to the modified object. */ /*# @method merge Array @brief Merges the given array into this one. @param array Array containing the second half of the merge, read-only @optparam insertPos Optional position of array 1 at which to place array2 @optparam start First element of array to merge in this array @optparam end Last element - 1 of array2 to merge in this array @see arrayMerge */ FALCON_FUNC mth_arrayMerge( ::Falcon::VMachine *vm ) { Item *first_i, *second_i, *from_i, *start_i, *end_i; if( vm->self().isMethodic() ) { first_i = &vm->self(); second_i = vm->param( 0 ); from_i = vm->param( 1 ); start_i = vm->param( 2 ); end_i = vm->param( 3 ); } else { first_i = vm->param( 0 ); second_i = vm->param( 1 ); from_i = vm->param( 2 ); start_i = vm->param( 3 ); end_i = vm->param( 4 ); } if ( first_i == 0 || ! first_i->isArray() || second_i == 0 || ! second_i->isArray() || ( from_i != 0 && ! from_i->isOrdinal() ) || ( start_i != 0 && ! start_i->isOrdinal() ) || ( end_i != 0 && ! end_i->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "A,[N],[N],[N]" : "A,A,[N],[N],[N]" ) ); } CoreArray *first = first_i->asArray(); CoreArray *second = second_i->asArray(); int64 from = from_i == 0 ? first->length(): from_i->forceInteger(); int64 start = start_i == 0 ? 0 : start_i->forceInteger(); int64 end = end_i == 0 ? second->length() : end_i->forceInteger(); bool val; if( start == 0 && end == second->length() ) { val = first->change( *second, (int32) from, (int32) from ); } else { //TODO: partition on an array CoreArray *third = second->partition( (int32)start, (int32)end ); third->mark( vm->generation() ); if ( third == 0 ) { throw new AccessError( ErrorParam( e_arracc, __LINE__ ). origin( e_orig_runtime ) ); } val = first->change( *third, (int32) from, (int32) from ); } if ( ! val ) { throw new AccessError( ErrorParam( e_arracc, __LINE__ ). origin( e_orig_runtime ).extra( Engine::getMessage( rtl_start_outrange ) ) ); } } /*# @function arrayNM @brief Force an array to be non-method. @param array the array that should not be transformed into a method. @return The same array passed as parameter. Callable arrays stored in objects become methods when they are accessed. At times, this is not desirable. Suppose it is necessary to store a list of items, possibly functions, in an object, and that random access is needed. In this case, an array is a good storage, but it's useful to tell Falcon that this array is never to be considered a method for the host object. Consider the following case: @code class WithArrays( a ) asMethod = a asArray= arrayNM( a.clone() ) // or a.clone().NM() end wa = WithArrays( [ printl, 'hello' ] ) // this is ok for item in wa.asArray: > "Item in this array: ", item // this will raise an error for item in wa.asMethod: > "Item in this array: ", item @endcode The array is cloned in this example as the function modifies the array itself, which becomes non-methodic, and returns it without copying it. So, if not copied, in this example also asMethod would have been non-methodic. */ /*# @method NM Array @brief Force an array to be non-method. @return The This array @see arrayNM */ FALCON_FUNC mth_arrayNM( ::Falcon::VMachine *vm ) { Item *i_array; if( vm->self().isMethodic() ) { i_array = &vm->self(); } else { i_array = vm->param(0); if ( i_array == 0 || ! i_array->isArray() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "A" ) ); } } CoreArray *array = i_array->asArray(); array->canBeMethod(false); vm->retval( array ); } }} /* end of array_ext.cpp */ engine/core_module/attrib_ext.cpp000066400000000000000000000110641176363201700175010ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: attrib_ext.cpp Facilities handling attributes. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 11 Jul 2009 23:26:40 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include "core_module.h" /*# @beginmodule core */ namespace Falcon { namespace core { static void inner_make_item( VarDef* vd, Item& itm ) { switch( vd->type() ) { case VarDef::t_bool: itm.setBoolean( vd->asBool() ); break; case VarDef::t_int: itm.setInteger( vd->asInteger() ); break; case VarDef::t_num: itm.setNumeric( vd->asNumeric() ); break; case VarDef::t_string: { itm.setString( new CoreString( *vd->asString() ) ); } break; default: itm.setNil(); } } static CoreDict* interal_make_attrib_dict( Map* attr ) { CoreDict* cd = new CoreDict( new LinearDict( attr->size() ) ); MapIterator iter = attr->begin(); while( iter.hasCurrent() ) { VarDef* vd = *(VarDef**) iter.currentValue(); Item temp; inner_make_item( vd, temp ); cd->put( new CoreString( *(String*) iter.currentKey() ), temp ); iter.next(); } return cd; } /*# @function attributes @brief Returns a dictionary containing annotation attributes of the current module. @return Nil if the current module has no attributes, or a string-indexed dictionary. @see Function.attributes @see Class.attributes @see Object.attributes */ FALCON_FUNC attributes ( ::Falcon::VMachine *vm ) { // we want to know the attributes of the module calling us. StackFrame* cf = vm->currentFrame(); const Module* mod = cf->m_module->module(); Map* attr = mod->attributes(); if( attr != 0 ) { vm->retval( interal_make_attrib_dict( attr ) ); } } /*# @method attributes Class @brief Returns a dictionary containing annotation attributes of the given class. @return Nil if the class has no attributes, or a string-indexed dictionary. @see attributes */ FALCON_FUNC Class_attributes ( ::Falcon::VMachine *vm ) { Map* attr = vm->self().asClass()->symbol()->getClassDef()->attributes(); if( attr != 0 ) { vm->retval( interal_make_attrib_dict( attr ) ); } } /*# @method attributes Object @brief Returns a dictionary containing annotation attributes of the given object. @return Nil if the object has no attributes, or a string-indexed dictionary. If the object is a class instance, this method will return the attributes of the generator class. @see attributes */ FALCON_FUNC Object_attributes ( ::Falcon::VMachine *vm ) { Map* attr = vm->self().asObject()->generator()->symbol()->getClassDef()->attributes(); if( attr != 0 ) { vm->retval( interal_make_attrib_dict( attr ) ); } } /*# @method attributes Function @brief Returns a dictionary containing annotation attributes of the given function. @return Nil if the function has no attributes, or a string-indexed dictionary. @see attributes */ FALCON_FUNC Function_attributes ( ::Falcon::VMachine *vm ) { const Symbol* sym = vm->self().asFunction()->symbol(); // currently, extfunc are not supported; let the VM return nil if ( sym->isExtFunc() ) return; Map* attr = sym->getFuncDef()->attributes(); if( attr != 0 ) { vm->retval( interal_make_attrib_dict( attr ) ); } } /*# @method attributes Method @brief Returns the attributes associated with the method function. @return Nil if the function has no attributes, or a string-indexed dictionary. @see attributes */ /*# @method attributes ClassMethod @brief Returns the attributes associated with the method function. @return Nil if the function has no attributes, or a string-indexed dictionary. @see attributes */ FALCON_FUNC Method_attributes ( ::Falcon::VMachine *vm ) { if ( ! vm->self().asMethodFunc()->isFunc() ) return; const Symbol* sym = static_cast(vm->self().asMethodFunc())->symbol(); // currently, extfunc are not supported; let the VM return nil if ( sym->isExtFunc() ) return; Map* attr = sym->getFuncDef()->attributes(); if( attr != 0 ) { vm->retval( interal_make_attrib_dict( attr ) ); } } } } /* end of attrib_ext.cpp */ engine/core_module/cmdlineparser.cpp000066400000000000000000000455261176363201700201760ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: cmdlineparser.cpp The command line parser class ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2007-11-30 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file The command line parser class */ /*# @beginmodule core */ #include #include #include #include #include #include #include "core_module.h" #include /*# @class CmdlineParser @brief Provides simple and powerful parsing of the command line options. Command line options are the simplest and most immediate mean to provide a stand-alone script with basic configuration, or to ask it to do something a bit more specific than just "operate". Some embedding applications may provide the scripts with a command line too; for example, a "scanner" script in a FPS game may be provided with the objects to search for in a "command line", that may be actually the string that represents its configuration in the user interface. Often, this important feature is neglected in scripts because bringing up a decent option parser is a bit of a nuisance, boring and repetitive, and above anything it may be considered a huge piece of code with respect to the needs of a simple script. The CmdlineParser class, that is declared directly in the RTL module, provides a simple, efficient and flexible mean to implement command line option parsing that let on the script the essential duty to grab the given values and store them for later usage. The command line parser knows the following option categories: - @b short @b options: options consisting of a single character, case sensitive, following a single "-". For example, "-a", "-B", "-x". Short options may be chained in sequences of characters as, for example "-aBx" which is equivalent to "-a -B -x". Short options may have also the special "switch off" semantic; if they are followed by a "-" sign, the parser interprets it as a will to turn off some feature; for example, the sequence "-B-" means that the "B" option should be turned off. The semantic can be expressed also in chained options as "-aB-x". - @b long @b options: they consists of two minus followed by a word of any length, as for example "--parameter", "--config", "--elements". Long options are usually (but not necessarily) meant to receive a parameter, for example "--debug off". - @b free @b options: they are strings not leaded by any "-" sign. Usually the semantic of a command gives free options a special meaning; for example the "cp" unix command accept an arbitrary amount of free options, where the first N-1 options are the name of the files to copy, and the Nth option is the copy destination. A single "-" not followed by any letter is considered a free option (i.e. it often means "stdout/stdin" in many UNIX commands). - @b option @b parsing @b terminator: The special sequence "--" followed by a whitespace is considered as the terminator of option parsing; after that element, all the other options are considered free options and given to the parser "as is". If you want to pass a free parameter starting with a "-", (i.e. a file named "-strangename"), it must follow the "--" sign. Short and long options may be parametric. The word (or string) following parametric option is considered the parameter of that option, and is not subject to parsing. For example, if "--terminator" is a parametric option, it is possible to write "./myprg.fal --terminator -opt". The parameter "-opt" will be passed as-is to the script as "terminator" option parameter. In case of short option chaining, if more than one chained option is parametric, the parameter following the chained options will be considered applied only to the last option, and the other ones will be ignored. If the sequence of parameters ends while waiting for the parameter of an option, the incomplete option is ignored. On the script point of view, the parser can be configured by implementing callbacks in the CmdlineParser class. The parser will call the methods of the subclasses as it finds options in the argument vector; the callbacks will configure the application, report errors and mis-usage, terminate the program on fatal errors and communicate with the parser through member functions. For example, it is not necessary to declare in advance which are the parametric options; it's done on a per-option basis by calling the expectParam() method and returning to the parser. To use this feature, it is just necessary to declare a subclass of CmdlineParser and instance it, or derive an object from it, and call the parse() method. The CmdlineParser class is meant to be overloaded by scripts, so that the callbacks provided in the class can be called by the parse() method. Once called, parse() will scan the line and will call in turn onOption(), onFree() and onSwitchOff() callbacks, depending on what kind of arguments it finds in the argument list. If the parsed option should provide some value, the script implementing onOption() should call expectValue() and then return. The next element on the command line will be then passed to onValue(), which will receive the previously parsed option and the parsed value as parameters. Calling the terminate() method from any callback routine will force parse() to terminate and pass the control back to the application. The last parsed element will be stored in the property lastParsed, that can be used as an index to read the args vector from the last parsed parameter. Here is a working sample of implementation. @code object MyParser from CmdlineParser function onOption( option ) switch option case "?", "help" self.usage() case "d", "m", "l", "long" // those options require a parameter; signal it self.expectValue() case "k" // set switch k ON case "n" // set switch n ON case "v", "version" // show version case "z", "sleep" self.terminate() default self.unrecognized( option ) end end function onValue( option, value ) switch option case "d" // set value for option d case "m" // set value for option m case "l", "long" // set value for option l end // can't be anything else, as this function call must // be authorized from onOption end function onFree( param ) // record the free parameter end function onSwitchOff( sw ) switch sw case "k" // set switch k OFF case "n" // set switch n OFF default self.unrecognized( sw ) end end function unrecognized( option ) // Signal some error end function usage() // say something relevant end end // And in the main program: MyParser.parse() @endcode @b Notice: Callback methods in the instance are called in Virtual Machine atomic mode. The called methods cannot be interrupted by external kind requests, they won't honor periodic callback requests and they will be forbidden to sleep or yield the execution to other coroutines. Parsing of the whole command line happens in an atomic context, so it's not possible to wait for other coroutines in anyone of the callback methods. It is also advisable that methods are simple and straight to the point to minimize the time in which the VM is unresponsive to kind requests and time scheduling. @prop lastParsed An integer representing the last item parsed in the argument array before exit the parsing loop. */ namespace Falcon{ namespace core { /*# @method parse CmdlineParser @brief Starts command line parsing. @optparam args A specific string array that will be used as arguments to be parsed. @return true if the parsing is complete, false on error. Start the parsing process. If args parameter is not provided, the method gets the content of the @a args global vector defined in the Core module. Returns true if the parsing was complete, and false on error (for example, if some element in the array wasn't a string). */ FALCON_FUNC CmdlineParser_parse( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Item *i_params = vm->param( 0 ); if ( i_params == 0 ) { // get the parameters from the VM args object i_params = vm->findGlobalItem( "args" ); if ( i_params == 0 || ! i_params->isArray() ) { throw new CodeError( ErrorParam( e_undef_sym ).extra( "args" ).hard() ); } } else if ( ! i_params->isArray() ) { throw new ParamError( ErrorParam( e_inv_params ).extra( "( A )" ) ); } CoreArray *args = i_params->asArray(); // zero request. self->setProperty( "_request", Item((int64) 0) ); self->setProperty( "lastParsed", Item((int64) 0) ); // status. typedef enum { t_none, t_waitingValue, t_allFree } t_states; t_states state = t_none ; String currentOption; Item i_method; Item i_passMM; self->getProperty( "passMinusMinus", i_passMM ); bool passMM = i_passMM.isTrue(); Item _request; String subParam; uint32 i; for ( i = 0; i < args->length(); i++ ) { Item &i_opt = args->at( i ); if ( !i_opt.isString() ) { throw new ParamError( ErrorParam( e_param_type ). extra( vm->moduleString( rtl_cmdp_0 ) ) ); } String &opt = *i_opt.asString(); // if we were expecting a value, we MUST consider ANYTHING as it was a value. if ( state == t_waitingValue ) { self->getProperty( "onValue", i_method ); if ( i_method.methodize( self ) ) { vm->pushParam( new CoreString(currentOption) ); vm->pushParam( i_opt ); vm->callItemAtomic( i_method, 2 ); state = t_none; } else { vm->retval( false ); self->setProperty( "lastParsed", i ); return; } } else if( opt.length() == 0 || (opt.getCharAt( 0 ) != '-' || opt.length() == 1) || state == t_allFree ) { self->getProperty( "onFree", i_method ); if ( i_method.methodize( self ) ) { vm->pushParam( i_opt ); vm->callItemAtomic( i_method, 1 ); } else { vm->retval( false ); self->setProperty( "lastParsed", i ); return; } } else if ( opt == "--" && ! passMM ) { state = t_allFree; continue; // to skip return value. } else { // we have at least one '-', and length > 1 if ( opt.getCharAt( 1 ) == (uint32) '-' ) { self->getProperty( "onOption", i_method ); if ( i_method.methodize( self ) ) { if ( passMM && opt.size() == 2 ) vm->pushParam( i_opt ); else { //Minimal optimization; reuse the same string and memory subParam = opt.subString( 2 ); vm->pushParam( new CoreString( subParam ) ); } vm->callItemAtomic( i_method, 1 ); self->getProperty( "_request", _request ); // value requested? if ( _request.asInteger() == 1 ) { currentOption = subParam; } } else { vm->retval( false ); self->setProperty( "lastParsed", i ); return; } } else { // we have a switch set. for( uint32 chNum = 1; chNum < opt.length(); chNum++ ) { //Minimal optimization; reuse the same string and memory subParam.size( 0 ); subParam.append( opt.getCharAt( chNum ) ); if ( chNum < opt.length() -1 && opt.getCharAt( chNum +1 ) == (uint32) '-' ) { // switch turnoff. self->getProperty( "onSwitchOff", i_method ); if ( i_method.methodize( self ) ) { vm->pushParam( new CoreString(subParam) ); vm->callItemAtomic( i_method, 1 ); } else { vm->retval( false ); self->setProperty( "lastParsed", (int64) i ); return; } chNum++; } else { self->getProperty( "onOption", i_method ); if ( i_method.methodize( self ) ) { vm->pushParam( new CoreString(subParam) ); vm->callItemAtomic( i_method, 1 ); } else { vm->retval( false ); self->setProperty( "lastParsed", (int64) i ); return; } } self->getProperty( "_request", _request ); // value requested? if ( _request.asInteger() == 1 ) { currentOption = subParam; } } } self->getProperty( "_request", _request ); // value requested? if ( _request.asInteger() == 1 ) { state = t_waitingValue; self->setProperty( "_request", Item(0) ); } // or request to terminate? else if ( _request.asInteger() == 2 ) { self->setProperty( "_request", Item(0) ); vm->retval( true ); self->setProperty( "lastParsed", (int64) i ); return; } } } self->setProperty( "lastParsed", (int64) i ); vm->retval( true ); } /*# @method expectValue CmdlineParser @brief Declares that current option needs a value. This method is to be called only from the onOption callback. When called, it suggests the parser that the received option requires a parameter, that should immediately follow. As the same option received by onOption() will be reported later on to onValue(), it is not necessary for the application to take note of the event. Simply, when receiving an option that needs a parameter, the application should call self.expectValue() and return. */ FALCON_FUNC CmdlineParser_expectValue( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); self->setProperty( "_request", (int64) 1 ); } /*# @method terminate CmdlineParser @brief Requests the parser to terminate parsing. This method should be called from inside one of the CmdlineParser class callbacks. Once called, the parser will immediately return true. The calling application can know the position of the last parsed parameter by accessing the lastParsed property, and handle the missing parameters as it prefers. */ FALCON_FUNC CmdlineParser_terminate( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); self->setProperty( "_request", (int64) 2 ); } FALCON_FUNC CmdlineParser_usage( ::Falcon::VMachine *vm ) { vm->stdErr()->writeString( "The stub for \"CmdlineParser.usage()\" has been called.\n" ); vm->stdErr()->writeString( "This class should be derived and the method usage() overloaded.\n" ); } /*# @method onFree CmdlineParser @brief Called when the parser finds a free option. @param opt The free option being read. This callback method gets called by parse() when a command line parameter not being bound with any option is found. The overloaded method should check for this value respecting the host program command line semantic. In case the free option cannot be accepted, the method should either signal error and exit the application, ignore it or set an error indicator and request the parser to terminate by calling terminate(). The parser won't signal error to the calling application, so, in case this free value cannot be accepted, an error state should be set in the application or in the parser object. */ /*# @method onOption CmdlineParser @brief Called when the parser finds an option. @param opt The option being read. This callback method gets called by parse() when an option is found. The overloaded method should check for the option being valid; in case it is not valid, it may either signal error and exit the application, ignore it or set an error indicator and request the parser to terminate by calling terminate(). The parser won't signal error to the calling application, so, in case an invalid option is received, an error state should be set in the application or in the parser object. If the incoming option requires a parameter, this callback should call expectOption() before returning. */ /*# @method onSwitchOff CmdlineParser @brief Called when the parser finds a switch being turned off. @param opt The switch being turned off. This callback method gets called by parse() when a short option is immediately followed by a "-", indicating a "switch off" semantic. The overloaded method should check for the option being valid and being possibly target of a "switch off"; if not, it may either signal error and exit the application, ignore it or set an error indicator and request the parser to terminate by calling terminate(). The parser won't signal error to the calling application, so, in case an invalid option is received, an error state should be set in the application or in the parser object. */ /*# @method onValue CmdlineParser @brief Called when the parser finds a value for a given option. @param opt The option being parsed. @param value The given value for that option. This callback method gets called by parse() when the parameter for a parametric option is read. The overloaded method should check for the option being valid; in case it is not valid, it may either signal error and exit the application, ignore it or set an error indicator and request the parser to terminate by calling terminate(). The parser won't signal error to the calling application, so, in case an invalid option is received, an error state should be set in the application or in the parser object. */ }} /* end of cmdlineparser.cpp */ engine/core_module/complex_ext.cpp000066400000000000000000000120371176363201700176640ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: complex_ext.cpp Complex class for Falcon Interface extension functions ------------------------------------------------------------------- Author: Enrico Lumetti Begin: Sat, 05 Sep 2009 21:04:31 +0000 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Complex class for Falcon Interface extension functions */ /*# @beginmodule core */ #include "core_module.h" namespace Falcon { namespace core { CoreObject *Complex_Factory( const CoreClass *cls, void *, bool ) { return new CoreComplex ( cls ); } CoreComplex::~CoreComplex ( void ) { } bool CoreComplex::hasProperty( const String &key ) const { uint32 pos = 0; return generator()->properties().findKey( key, pos ); } bool CoreComplex::setProperty( const String &key, const Item &item ) { if (key == "real") { m_complex.real( item.forceNumeric() ); return true; } if (key == "imag") { m_complex.imag( item.forceNumeric() ); return true; } // found but read only? uint32 pos; if( generator()->properties().findKey( key, pos ) ) throw new AccessError( ErrorParam( e_prop_ro, __LINE__ ) .origin( e_orig_runtime ) .extra( key ) ); // no, not found. return false; } bool CoreComplex::getProperty( const String &key, Item &ret ) const { if (key == "real") { ret = m_complex.real(); return true; } if (key == "imag") { ret = m_complex.imag(); return true; } return defaultProperty( key, ret ); } FALCON_FUNC Complex_init( ::Falcon::VMachine *vm ) { Item *i_real = vm->param(0); Item *i_imag = vm->param(1); if ( ( i_real != 0 && ! i_real->isOrdinal() ) || ( i_imag != 0 && ! i_imag->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "[N,N]" ) ); } CoreComplex *c = dyncast ( vm->self().asObject() ); c->complex() = Complex( ( i_real != 0 ) ? i_real->forceNumeric() : 0 , ( i_imag != 0 ) ? i_imag->forceNumeric() : 0 ); } FALCON_FUNC Complex_toString( ::Falcon::VMachine *vm ) { CoreComplex *self = dyncast( vm->self().asObject() ); String res,real,imag; Item(self->complex().real() ).toString(real); Item(self->complex().imag()).toString(imag); res=real+" , "+imag+"i"; vm->retval(res); } FALCON_FUNC Complex_abs( ::Falcon::VMachine *vm ) { CoreComplex *self = dyncast( vm->self().asObject() ); vm->retval( self->complex().abs() ); } static void s_operands( VMachine* vm, Complex* &one, Complex& two, const CoreClass* &gen ) { Item *i_obj = vm->param( 0 ); bool is_ordinal = i_obj->isOrdinal(); if ( i_obj == 0 || ! ( i_obj->isOfClass( "Complex" ) || is_ordinal ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "Complex" ) ); } CoreComplex *self = dyncast( vm->self().asObject() ); if ( is_ordinal ) two.real( i_obj->forceNumeric() ); else two = (dyncast( i_obj->asObject() ))->complex(); one = &self->complex(); gen = self->generator(); } FALCON_FUNC Complex_add__( ::Falcon::VMachine *vm ) { Complex *one, two; const CoreClass* gen; s_operands( vm, one, two, gen ); vm->retval( new CoreComplex( (*one) + two, gen ) ); } FALCON_FUNC Complex_sub__( ::Falcon::VMachine *vm ) { Complex *one, two; const CoreClass* gen; s_operands( vm, one, two, gen ); vm->retval( new CoreComplex( (*one) - two, gen ) ); } FALCON_FUNC Complex_mul__( ::Falcon::VMachine *vm ) { Complex *one, two; const CoreClass* gen; s_operands( vm, one, two, gen ); vm->retval( new CoreComplex( (*one) * two, gen ) ); } FALCON_FUNC Complex_div__( ::Falcon::VMachine *vm ) { Complex *one, two; const CoreClass* gen; s_operands( vm, one, two, gen ); vm->retval( new CoreComplex( (*one) / two, gen ) ); } FALCON_FUNC Complex_compare( ::Falcon::VMachine *vm ) { Item* i_other = vm->param( 0 ); Item& i_self = vm->self(); if( i_other == 0 || !( i_other->isOrdinal() || i_other->isOfClass( "Complex" ) )) { vm->retval( i_self.type() - i_other->type() ); return; } Complex& self = dyncast(i_self.asObject())->complex(); Complex other; if( i_other->isOrdinal() ) { other.real( i_other->forceNumeric() ); } else { other = (dyncast( i_other->asObject() ))->complex(); } if( self < other ) { vm->retval( -1 ); } else if( self > other ) { vm->retval( 1 ); } else { vm->retval( 0 ); } } FALCON_FUNC Complex_conj( ::Falcon::VMachine *vm ) { CoreComplex *self = dyncast( vm->self().asObject() ); vm->retval( new CoreComplex( self->complex().conj(), self->generator() ) ); } } } /* end of complex_ext.cpp */ engine/core_module/continuation_ext.cpp000066400000000000000000000147431176363201700207350ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: continuation.h Continuation object. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 05 Dec 2009 17:04:27 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "core_module.h" #include #include #include /*# @beginmodule core */ namespace Falcon { namespace core { //=========================================================== // /*# @class Continuation @brief Intra-context suspension and resume control device. @param callable A function or any callable item. This instances of this class can be used to execute some code that can be interrupted at any time and resumed later from the point they were interrupted. The function or other callable item set as the parameter in the constructor of this class will receive a special copy of the instance when this instance is called as a @i functor. For example: @code function callable( cont ) > cont.toString() end c = Continuation( callable ) c() // will call callable( c ) @endcode The special instance which is passed to the @b callable item is itself a functor. Calling it causes immediate suspension and return to the original frame where the continuation instance was first called. An optional value can be returned to the caller by passing it as a parameter of the continuation instance. For example; the following code returns to the continuation first caller if the random number matches a dividend of a "secret" number. @code c = Continuation( { c, secret => while true r = random( 2, secret ) if secret % r == 0 c(r) // return "r" to our caller end end }) > "A random factor of 136: ", c(136) @endcode Other than returning immediately to the first caller of the continuation, the current state of the called sequence is recorded and restored when subsequent calls are performed. The following code returns the position where a given element is found in an array: @code finder = Continuation( { c, elem, array => for n in [0: array.len()] if array[n] == elem: c(n) end }) pos = finder(10, [1,"a",10,5,10] ) while pos >= 0 > "Found a '10' at pos ", pos pos = finder() end @endcode Parameters passed in subsequent calls are returned inside the continuation function. For example, the following code adds a parameter that is passed from the caller to the callee and then displayed: @code finder = Continuation( { c, elem, array => for n in [0: array.len()] if array[n] == elem > "Found ", c(n), " elements." end end }) pos = finder(10, [1,"a",10,5,10] ) count = 0 while pos >= 0 > "Found a '10' at pos ", pos pos = finder(++count) end @endcode @note It is not possible to use continuations in atomic calls; for/in generators are called as atomically, so it's not possible to use continuations as generators in for/in loops. Use standard \b while loops instead. Separate continuations calling the same functions have a completely different state. Also, the @a Continuation.reset method clears any previously existing state of a continuation, so that it can be called anew again without the need to recreate it. */ FALCON_FUNC Continuation_init ( ::Falcon::VMachine *vm ) { Item* i_cc = vm->param(0); if ( i_cc == 0 || ! i_cc->isCallable() ) { throw new ParamError( ErrorParam( e_inv_params ). extra("C") ); } ContinuationCarrier* cc = dyncast( vm->self().asObject() ); cc->cont( new Continuation(vm) ); cc->ccItem( *i_cc ); cc->getMethod("_suspend", cc->suspendItem() ); } /*# @method __call Continuation @brief Enters into or exit from a continuation. @param ... parameters passed to the callable stored in the instance, or to the return value. @return The parameter passed to this method from inside the continuation. For the complete usage pattern, see the @a Continuation class. */ FALCON_FUNC Continuation_call ( ::Falcon::VMachine *vm ) { ContinuationCarrier* cc = dyncast( vm->self().asObject() ); // eventually, prepare the parameter to be returned in case of jump Item iContPass; if ( vm->paramCount() > 0 ) iContPass = *vm->param(0); // call in passive phase calls the desired item. if( cc->cont()->jump(iContPass) ) { // the jump happened, we just must return. return; } vm->pushParam( cc->suspendItem() ); for( int32 i = 0; i < vm->paramCount(); i++) { vm->pushParam( *vm->param(i) ); } // otherwise, we have to call the item from here. vm->callFrame( cc->ccItem(), 1 + vm->paramCount() ); } /*# @method reset Continuation @brief Clears the continuation state. Allows to recycle a continuation after it is terminated, or at any moment. This must be called when the continuation has returned the control to its caller: it has no effect otherwise. */ FALCON_FUNC Continuation_reset ( ::Falcon::VMachine *vm ) { ContinuationCarrier* cc = dyncast( vm->self().asObject() ); cc->cont()->reset(); } /*# @method complete Continuation @brief Indicates if the continuation has been completely executed or not. @return True if the continuation code has exited through any mean except calling the continuation. When the code inside the continuation calls the continuation, it means it has more operations to perform; the calling code may then decide to call the continuation to let it continue, or to reset it, or just to discard it. */ FALCON_FUNC Continuation_complete ( ::Falcon::VMachine *vm ) { ContinuationCarrier* cc = dyncast( vm->self().asObject() ); cc->cont()->complete(); } FALCON_FUNC Continuation__suspend ( ::Falcon::VMachine *vm ) { ContinuationCarrier* susp = dyncast( vm->self().asObject() ); susp->cont()->suspend( vm->param(0) == 0 ? Item(): *vm->param(0) ); } } } engine/core_module/core_module.cpp000066400000000000000000003243121176363201700176340ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: core_module.cpp Falcon core module ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 14 Aug 2008 00:17:31 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "core_module.h" #include #include #include /*# @module core The core module @after main The core module interacts with the virtual machine, to the point that, at times, the virtual machine itself refers to it. For example, while the exceptions can be of any types, the exceptions internally generated by the machine are instance of the Error class, which is part of the core module. However, the core module is not automatically linked in the VM. Every embedding application must create an instance of the core module, and then link it to the VM it uses; this allow to override also the functions that are in this module to, in example, trace their calls or change their behavior. Scripts launched from the falcon command line tools are granted to have access to the core module contents as they are described in this manual. Some of the functions that are provided in the core module, as printl, are a common hook for embedders. In the case of printl, which provides a common and comfortable simple output for scripts, embedders usually redirect it to their own functions to intercept script output request and redirect them. In some cases, embedders may may find useful to disable some functionalities, i.e. removing file/directory functions. The core module also provides many functions that duplicate operator functionalities. Array partitioning and string concatenations are some example. Those duplicate functions are meant both because they are usually more flexible than the pre-defined operators and because it is then possible to pass those functions as function parameters, or to save those functions in variables; of course, operators cannot be used that way. However, operators are faster than function calls, so, when possible, use the operators rather than the functions. Functions duplicating operators are mean to be used when operators cannot possibly be employed, or when the limited functionalities of the operators are not enough. */ /*# @beginmodule core */ /*# @funset core_basic_io Basic I/O @brief Functions providing basic interface. RTL Basic I/O functions are mainly meant to provide scripts with a very basic interface to interact with the outside world. */ /*# @group core_syssupport System Support @brief Function and classes supporting OS and environment. This group of functions and classes is meant to provide OS and environmental basic support to Falcon scripts. */ /*# @group memory_manipulation Raw memory manipulation functions. @brief DANGEROUS group of functions and methods manipulating memory. This group of functions is meant to manipulate directly memory. It is useful to support raw bindings from external C modules returning raw memory areas, or requiring raw memory areas to be passed to them. In Falcon, raw memory pointers are just represented as 64-bit integers, eventually cast down to proper sizes on different architectures. In short, they are integers that can be directly modified by the Falcon script and fed in the library. Messing with functions in this group is the fastest and safest way to find troubles. Avoid them unless it is necessary to deal with raw pointers coming from outer bindings. */ namespace Falcon { /**************************************** Module initializer ****************************************/ Module* core_module_init() { Module *self = new Module(); self->name( "falcon.core" ); #define FALCON_DECLARE_MODULE self self->language( "en_US" ); self->engineVersion( FALCON_VERSION_NUM ); self->version( FALCON_VERSION_NUM ); //======================================================================= // Message setting //======================================================================= StringTable *st = new StringTable(*engineStrings); self->adoptStringTable( st, true ); //======================================================================= // Metaclasses //======================================================================= self->addExtFunc( "len", &Falcon::core::mth_len )-> addParam("item"); self->addExtFunc( "toString", &Falcon::core::mth_ToString )-> addParam("item")->addParam("format"); self->addExtFunc( "compare", &Falcon::core::mth_compare )-> addParam("first")->addParam("second"); self->addExtFunc( "typeId", &Falcon::core::mth_typeId )-> addParam("item"); self->addExtFunc( "clone", &Falcon::core::mth_clone )-> addParam("item"); self->addExtFunc( "serialize", &Falcon::core::mth_serialize )-> addParam("item")->addParam("stream"); self->addExtFunc( "isCallable", &Falcon::core::mth_isCallable )-> addParam("item"); self->addExtFunc( "className", &Falcon::core::mth_className )-> addParam("item"); self->addExtFunc( "baseClass", &Falcon::core::mth_baseClass )-> addParam("item"); self->addExtFunc( "derivedFrom", &Falcon::core::mth_derivedFrom )-> addParam("item")->addParam("cls"); self->addExtFunc( "metaclass", &Falcon::core::mth_metaclass )-> addParam("item"); self->addExtFunc( "describe", &Falcon::core::mth_describe )-> addParam("item")->addParam("depth")->addParam("maxLen"); self->addExtFunc( "isBound", &Falcon::core::mth_bound )-> addParam("item"); /*# @group bom_classes Basic Object Model @brief Generic item reflection classes The classes in this group reflect the underlying Falcon item types. Methods declared by this classes can be applied to every item (if they are in the @a BOM class) or to some specific item type. The methods can be applied both to symbols containing some data of a certain types, or directly to the constants specifying them. For example, to obtain the length of the "Hello world" string, it is possible to do either: @code > "Hello world".len() // or item = "Hello world" > item.len() @endcode */ /*# @class BOM @ingroup bom_classes @brief Basic Object Model metaclass This class contains the methods that can be applied to every falcon item; the method themselves are not shown in the item list, and the @b provides keyword won't detect their availability unless they have been explicitly re-declared (overloaded) by objects, instances, classes, array bindings or blessed dictionaries. Nevertheless, the method listed in the BOM metaclass can be applied to any item, while methods defined in specific item metaclasses derived from BOM, as i.e. the @a Dictionary metaclass, can be applied only to items of the reflected type. @note The method @a BOM.compare is meant to overload the behavior of generic VM comparisons, including relational operators (<, >, <=, >=, ==, !=) and generic ordering criterion, for example in @a Dictionary insertions and @a arraySort. */ /*# @method __add BOM @brief Overrides binary addition operand. @param operand The second operand in the expression. @return The value of @b self + @b operand. If an object or instance re-defines this method, when a "+" operation is performed on this object, the method gets called. This includes self-target operations as +=, -= and so on; in this latter case, the return value of the function will also be immediately assigned to this object. @note There is no guarantee that the @b operand type is the same of this item. */ /*# @method __sub BOM @brief Overrides binary subtraction operand. @param operand The second operand in the expression. @return The value of @b self - @b operand. If an object or instance re-defines this method, when a "-" operation is performed on this object, the method gets called. This includes self-target operations as +=, -= and so on; in this latter case, the return value of the function will also be immediately assigned to this object. @note There is no guarantee that the @b operand type is the same of this item. */ /*# @method __mul BOM @brief Overrides binary multiplication operand. @param operand The second operand in the expression. @return The value of @b self * @b operand. If an object or instance re-defines this method, when a "*" operation is performed on this object, the method gets called. This includes self-target operations as +=, -= and so on; in this latter case, the return value of the function will also be immediately assigned to this object. @note There is no guarantee that the @b operand type is the same of this item. */ /*# @method __div BOM @brief Overrides binary division operand. @param operand The second operand in the expression. @return The value of @b self / @b operand. If an object or instance re-defines this method, when a "/" operation is performed on this object, the method gets called. This includes self-target operations as +=, -= and so on; in this latter case, the return value of the function will also be immediately assigned to this object. @note There is no guarantee that the @b operand type is the same of this item. */ /*# @method __mod BOM @brief Overrides modulo operand. @param operand The second operand in the expression. @return The value of @b self % @b operand. If an object or instance re-defines this method, when a "%" operation is performed on this object, the method gets called. This includes self-target operations as +=, -= and so on; in this latter case, the return value of the function will also be immediately assigned to this object. @note There is no guarantee that the @b operand type is the same of this item. */ /*# @method __pow BOM @brief Overrides power operand. @param operand The second operand in the expression. @return The value of @b self ** @b operand. If an object or instance re-defines this method, when a "**" operation is performed on this object, the method gets called. This includes self-target operations as +=, -= and so on; in this latter case, the return value of the function will also be immediately assigned to this object. @note There is no guarantee that the @b operand type is the same of this item. */ /*# @method __inc BOM @brief Overrides increment unary prefix operand. @param operand The second operand in the expression. @return The value of ++ @b self. If an object or instance re-defines this method, when a "++" prefix operation is performed on this object, the method gets called. The implementation should modify the object, and return itself modified (or value representing this object after modification). */ /*# @method __dec BOM @brief Overrides decrement unary prefix operand. @param operand The second operand in the expression. @return The value of -- @b self. If an object or instance re-defines this method, when a "--" prefix operation is performed on this object, the method gets called. The implementation should modify the object, and return itself modified (or value representing this object after modification). */ /*# @method __incpost BOM @brief Overrides increment unary postifx operand. @param operand The second operand in the expression. @return The value of @b self ++. If an object or instance re-defines this method, when a "++" postfix operation is performed on this object, the method gets called. The implementation should keep an unmodified copy of this object, modify this instance and return the previous one, or a value representing the previous status of this object. */ /*# @method __decpost BOM @brief Overrides decrement unary postfix operand. @param operand The second operand in the expression. @return The value of @b self --. If an object or instance re-defines this method, when a "--" postfix operation is performed on this object, the method gets called. The implementation should keep an unmodified copy of this object, modify this instance and return the previous one, or a value representing the previous status of this object. */ /*# @method __getIndex BOM @brief Overrides array access operator [] @param index The index of the desired item. @return The value of @b self [@b index]. If an object or instance re-defines this method, when a "[]" array access operator is called to read a value from a given index, this method gets called. The @b index value may be of any type. The function should return a value or throw an AccessError if the @b index is considered invalid. */ /*# @method __setIndex BOM @brief Overrides array write operator [] @param index The index of the desired item. @param value The value that must be set. @return The value of (@b self [@b index] = @b value). If an object or instance re-defines this method, when a "[]" array access operator is called to write a value to a given index, this method gets called. The @b index value may be of any type. The function should return the same value that is being assigned, but the return value can be freely changed. */ /*# @method __call BOM @brief Overrides call operator "self()". @param ... The parameters passed to the original call. @return The value of @b self( ... ). If an object or instance re-defines this method, when this object is accessed through the call operator, then this method is called instead. This allows to create functors, as in the following example: @code object sum function __call( a, b ) return a+b end end > sum( 2, 2 ) // 4 @endcode */ Falcon::Symbol *bom_meta = self->addClass( "%FBOM" ); bom_meta->exported( false ); self->addClassMethod( bom_meta, "len", &Falcon::core::mth_len ); self->addClassMethod( bom_meta, "toString", &Falcon::core::mth_ToString ).asSymbol()-> addParam("format"); self->addClassMethod( bom_meta, "compare", &Falcon::core::mth_compare ).asSymbol()-> addParam("other"); self->addClassMethod( bom_meta, "typeId", &Falcon::core::mth_typeId ); self->addClassMethod( bom_meta, "clone", &Falcon::core::mth_clone ); self->addClassMethod( bom_meta, "serialize", &Falcon::core::mth_serialize ).asSymbol()-> addParam("stream"); self->addClassMethod( bom_meta, "isCallable", &Falcon::core::mth_isCallable ); self->addClassMethod( bom_meta, "className", &Falcon::core::mth_className ); self->addClassMethod( bom_meta, "baseClass", &Falcon::core::mth_baseClass ); self->addClassMethod( bom_meta, "derivedFrom", &Falcon::core::mth_derivedFrom ); self->addClassMethod( bom_meta, "metaclass", &Falcon::core::mth_metaclass ); self->addClassMethod( bom_meta, "ptr", &Falcon::core::BOM_ptr ); self->addClassMethod( bom_meta, "describe", &Falcon::core::mth_describe ).asSymbol() ->addParam("depth")->addParam("maxLen"); self->addClassMethod( bom_meta, "bound", &Falcon::core::mth_bound ); Falcon::Symbol *nil_meta = self->addClass( "Nil" ); nil_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); nil_meta->exported( false ); nil_meta->getClassDef()->setMetaclassFor( FLC_ITEM_NIL ); Falcon::Symbol *umb_meta = self->addClass( "Unbound" ); umb_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); umb_meta->exported( false ); umb_meta->getClassDef()->setMetaclassFor( FLC_ITEM_UNB ); Falcon::Symbol *bool_meta = self->addClass( "Boolean" ); bool_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); bool_meta->exported( false ); bool_meta->getClassDef()->setMetaclassFor( FLC_ITEM_BOOL ); /*# @class Integer @from BOM @ingroup bom_classes @brief Integer type basic object model metaclass. */ Falcon::Symbol *int_meta = self->addClass( "Integer" ); int_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); int_meta->exported( false ); int_meta->getClassDef()->setMetaclassFor( FLC_ITEM_INT ); self->addClassMethod( int_meta, "times", &Falcon::core::core_times ).asSymbol()->setEta( true )-> addParam("count")->addParam("sequence"); self->addClassMethod( int_meta, "upto", &Falcon::core::core_upto ).asSymbol()->setEta( true )-> addParam("limit")->addParam("sequence"); self->addClassMethod( int_meta, "downto", &Falcon::core::core_downto ).asSymbol()->setEta( true )-> addParam("limit")->addParam("sequence"); self->addClassMethod( int_meta, "ptr", &Falcon::core::Integer_ptr ); /*# @class Numeric @from BOM @ingroup bom_classes @brief Generic number type basic object model metaclass. */ Falcon::Symbol *num_meta = self->addClass( "Numeric" ); num_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); num_meta->exported( false ); num_meta->getClassDef()->setMetaclassFor( FLC_ITEM_NUM ); /*# @class Range @from BOM @ingroup bom_classes @brief Metaclass for Falcon range type. This class holds the methods that can be applied to Falcon range items. Ranges are created through the @b [:] operator, like this: @code r1 = [1:10] // 1 to 10 r2 = [-1:0] // reverse sequence r3 = [0:10:2] // stepping range > "Hello world"[r2] // reverses the string @endcode */ Falcon::Symbol *range_meta = self->addClass( "Range" ); range_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); range_meta->exported( false ); range_meta->getClassDef()->setMetaclassFor( FLC_ITEM_RANGE ); self->addClassMethod( range_meta, "times", &Falcon::core::core_times ).asSymbol()->setEta( true )-> addParam("count")->addParam("sequence"); /*# @class LateBinding @from BOM @ingroup bom_classes @brief Metaclass for LateBinding type. Late bindings are special items treated as "symbols", that can receive an associated value at runtime. */ Falcon::Symbol *lbind_meta = self->addClass( "LateBinding" ); lbind_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); lbind_meta->exported( false ); lbind_meta->getClassDef()->setMetaclassFor( FLC_ITEM_LBIND ); self->addClassMethod( lbind_meta, "value", &Falcon::core::LateBinding_value ); self->addClassMethod( lbind_meta, "symbol", &Falcon::core::LateBinding_symbol ); self->addClassMethod( lbind_meta, "bound", &Falcon::core::LateBinding_bound ); self->addClassMethod( lbind_meta, "bind", &Falcon::core::LateBinding_bind ); self->addClassMethod( lbind_meta, "unbind", &Falcon::core::LateBinding_unbind ); /*# @class Function @from BOM @ingroup bom_classes @brief Metaclass for Falcon function type. */ Falcon::Symbol *func_meta = self->addClass( "Function" ); func_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); func_meta->exported( false ); func_meta->getClassDef()->setMetaclassFor( FLC_ITEM_FUNC ); self->addClassMethod( func_meta, "attributes", &Falcon::core::Function_attributes ); self->addClassMethod( func_meta, "name", &Falcon::core::Function_name ); self->addClassMethod( func_meta, "caller", &Falcon::core::Function_caller ).asSymbol()-> addParam("level"); //static self->addClassMethod( func_meta, "trace", &Falcon::core::Function_trace ).asSymbol()-> addParam("level"); //static self->addClassMethod( func_meta, "stack", &Falcon::core::Function_stack ); //static /*# @class GarbagePointer @from BOM @ingroup bom_classes @brief Metaclass for internal and application-wide garbage sensible data. GarbagePointer is a class used to reflect opaque data which can be automatically destroyed by the Falcon Garbage Collector when it goes out of scope. */ Falcon::Symbol *gcptr_meta = self->addClass( "GarbagePointer" ); gcptr_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); gcptr_meta->exported( false ); gcptr_meta->getClassDef()->setMetaclassFor( FLC_ITEM_GCPTR ); self->addClassMethod( gcptr_meta, "ptr", &Falcon::core::GarbagePointer_ptr ); //================================================================== // String class // self->addExtFunc( "strFront", &Falcon::core::mth_strFront )-> addParam("str")->addParam("count")->addParam("remove")->addParam("numeric"); self->addExtFunc( "strBack", &Falcon::core::mth_strBack )-> addParam("str")->addParam("count")->addParam("remove")->addParam("numeric"); self->addExtFunc( "strFrontTrim", &Falcon::core::mth_strFrontTrim )-> addParam("str")->addParam("trimSet"); self->addExtFunc( "strBackTrim", &Falcon::core::mth_strBackTrim )-> addParam("str")->addParam("trimSet"); self->addExtFunc( "strTrim", &Falcon::core::mth_strTrim )-> addParam("str")->addParam("trimSet"); /*# @class String @from BOM @ingroup bom_classes @brief Metaclass for string items. This is the set of methods that can be applied to string items. */ Falcon::Symbol *string_meta = self->addClass( "String" ); string_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); string_meta->exported( false ); string_meta->getClassDef()->setMetaclassFor( FLC_ITEM_STRING ); self->addClassMethod( string_meta, "front", &Falcon::core::mth_strFront ).asSymbol()-> addParam("count")->addParam("remove")->addParam("numeric"); self->addClassMethod( string_meta, "fill", &Falcon::core::mth_strFill ).asSymbol()-> addParam("chr"); self->addClassMethod( string_meta, "back", &Falcon::core::mth_strBack ).asSymbol()-> addParam("count")->addParam("remove")->addParam("numeric"); self->addClassMethod( string_meta, "ftrim", &Falcon::core::mth_strFrontTrim ).asSymbol()-> addParam("trimSet"); self->addClassMethod( string_meta, "rtrim", &Falcon::core::mth_strBackTrim ).asSymbol()-> addParam("trimSet"); self->addClassMethod( string_meta, "trim", &Falcon::core::mth_strTrim ).asSymbol()-> addParam("trimSet"); //self->addClassMethod( string_meta, "first", &Falcon::core::String_first ); //self->addClassMethod( string_meta, "last", &Falcon::core::String_last ); self->addClassMethod( string_meta, "split", &Falcon::core::mth_strSplit ).asSymbol() ->addParam( "token" )->addParam( "count" ); self->addClassMethod( string_meta, "splittr", &Falcon::core::mth_strSplitTrimmed ).asSymbol() ->addParam( "token" )->addParam("count"); self->addClassMethod( string_meta, "merge", &Falcon::core::mth_strMerge ).asSymbol() ->addParam("array")->addParam("mergeStr")->addParam("count"); self->addClassMethod( string_meta, "join", &Falcon::core::String_join ); self->addClassMethod( string_meta, "find", &Falcon::core::mth_strFind ).asSymbol() ->addParam("needle")->addParam("start")->addParam("end"); self->addClassMethod( string_meta, "rfind", &Falcon::core::mth_strBackFind ).asSymbol() ->addParam("needle")->addParam("start")->addParam("end"); self->addClassMethod( string_meta, "replace", &Falcon::core::mth_strReplace ).asSymbol() ->addParam("substr")->addParam("repstr")->addParam("start")->addParam("end"); self->addClassMethod( string_meta,"replicate", &Falcon::core::mth_strReplicate ).asSymbol() ->addParam("times"); self->addClassMethod( string_meta,"esq", &Falcon::core::mth_strEsq ).asSymbol() ->addParam("inplace"); self->addClassMethod( string_meta,"unesq", &Falcon::core::mth_strUnesq ).asSymbol() ->addParam("inplace"); self->addClassMethod( string_meta,"escape", &Falcon::core::mth_strEscape ).asSymbol() ->addParam("full"); self->addClassMethod( string_meta,"unescape", &Falcon::core::mth_strUnescape ).asSymbol() ->addParam("inplace"); self->addClassMethod( string_meta, "upper", &Falcon::core::mth_strUpper ); self->addClassMethod( string_meta, "lower", &Falcon::core::mth_strLower ); self->addClassMethod( string_meta, "cmpi", &Falcon::core::mth_strCmpIgnoreCase ).asSymbol() ->addParam("string2"); self->addClassMethod( string_meta, "wmatch", &Falcon::core::mth_strWildcardMatch ).asSymbol() ->addParam("wildcard")->addParam("ignoreCase"); self->addClassMethod( string_meta, "toMemBuf", &Falcon::core::mth_strToMemBuf ).asSymbol() ->addParam("wordWidth"); self->addClassMethod( string_meta, "ptr", &Falcon::core::String_ptr ); self->addClassMethod( string_meta, "charSize", &Falcon::core::String_charSize ).asSymbol() ->addParam("bpc"); self->addClassMethod( string_meta, "startsWith", &Falcon::core::mth_strStartsWith ).asSymbol() ->addParam("token")->addParam("icase"); self->addClassMethod( string_meta, "endsWith", &Falcon::core::mth_strEndsWith ).asSymbol() ->addParam("token")->addParam("icase"); //================================================================== // Array class // Falcon::Symbol *array_meta = self->addClass( "Array" ); array_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); array_meta->exported( false ); array_meta->getClassDef()->setMetaclassFor( FLC_ITEM_ARRAY ); /*# @class Array @from BOM @ingroup bom_classes @brief MetaClass for Falcon arrays. This is the class reflecting the base array classes. */ self->addClassMethod( array_meta, "front", &Falcon::core::Array_front ).asSymbol()-> addParam("remove"); self->addClassMethod( array_meta, "back", &Falcon::core::Array_back ).asSymbol()-> addParam("remove"); self->addClassMethod( array_meta, "first", &Falcon::core::Array_first ); self->addClassMethod( array_meta, "last", &Falcon::core::Array_last ); self->addClassMethod( array_meta, "table", &Falcon::core::Array_table ); self->addClassMethod( array_meta, "tabField", &Falcon::core::Array_tabField ).asSymbol()-> addParam("field"); self->addClassMethod( array_meta, "tabRow", &Falcon::core::Array_tabRow ); self->addClassMethod( array_meta, "ins", &Falcon::core::mth_arrayIns ).asSymbol() ->addParam("itempos")->addParam("item"); self->addClassMethod( array_meta, "del", &Falcon::core::mth_arrayDel ).asSymbol() ->addParam("item"); self->addClassMethod( array_meta, "delAll", &Falcon::core::mth_arrayDelAll ).asSymbol() ->addParam("item"); self->addClassMethod( array_meta, "add", &Falcon::core::mth_arrayAdd ).asSymbol() ->addParam("item"); self->addClassMethod( array_meta, "resize", &Falcon::core::mth_arrayResize ).asSymbol()-> addParam("newSize"); self->addClassMethod( array_meta, "find", &Falcon::core::mth_arrayFind ).asSymbol()-> addParam("item")->addParam("start")->addParam("end"); self->addClassMethod( array_meta, "scan", &Falcon::core::mth_arrayScan ).asSymbol()-> addParam("func")->addParam("start")->addParam("end"); self->addClassMethod( array_meta, "sort", &Falcon::core::mth_arraySort ).asSymbol()-> addParam("sortingFunc"); self->addClassMethod( array_meta, "remove", &Falcon::core::mth_arrayRemove ).asSymbol()-> addParam("itemPos")->addParam("lastItemPos"); self->addClassMethod( array_meta, "merge", &Falcon::core::mth_arrayMerge ).asSymbol()-> addParam("array"); self->addClassMethod( array_meta, "fill", &Falcon::core::mth_arrayFill ).asSymbol()-> addParam("item"); self->addClassMethod( array_meta, "head", &Falcon::core::mth_arrayHead ); self->addClassMethod( array_meta, "tail", &Falcon::core::mth_arrayTail ); self->addClassMethod( array_meta, "getProperty", &Falcon::core::mth_getProperty ).asSymbol()-> addParam("propName"); self->addClassMethod( array_meta, "setProperty", &Falcon::core::mth_setProperty ).asSymbol()-> addParam("propName")->addParam("value"); self->addClassMethod( array_meta, "properties", &Falcon::core::mth_properties ); self->addClassMethod( array_meta, "comp", &Falcon::core::Array_comp ).asSymbol()-> addParam("source")->addParam("filter"); self->addClassMethod( array_meta, "mcomp", &Falcon::core::Array_mcomp ); self->addClassMethod( array_meta, "mfcomp", &Falcon::core::Array_mfcomp ).asSymbol()-> addParam("filter"); self->addClassMethod( array_meta, "compact", &Falcon::core::mth_arrayCompact ); self->addClassMethod( array_meta, "copyFrom", &Falcon::core::mth_arrayCopy ).asSymbol()-> addParam("from")->addParam("src")->addParam("first")->addParam("amount"); self->addClassMethod( array_meta, "NM", &Falcon::core::mth_arrayNM ); self->addClassMethod( array_meta, "concat", &Falcon::core::Array_concat ).asSymbol()-> addParam( "sep" ); //================================================================== // Dict class // self->addExtFunc( "dictFront", &Falcon::core::mth_dictFront )-> addParam("dict")->addParam("remove")->addParam("key"); self->addExtFunc( "dictBack", &Falcon::core::mth_dictBack )-> addParam("dict")->addParam("remove")->addParam("key"); Falcon::Symbol *dict_meta = self->addClass( "Dictionary" ); dict_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); dict_meta->exported( false ); dict_meta->getClassDef()->setMetaclassFor( FLC_ITEM_DICT ); /*# @class Dictionary @from BOM @ingroup bom_classes @brief Metaclass for Falcon dictionary types. This class holds the methods that can be applied to Falcon dictionary items. */ self->addClassMethod( dict_meta, "front", &Falcon::core::mth_dictFront ).asSymbol()-> addParam("remove")->addParam("key"); self->addClassMethod( dict_meta, "back", &Falcon::core::mth_dictBack ).asSymbol()-> addParam("remove")->addParam("key"); self->addClassMethod( dict_meta, "first", &Falcon::core::Dictionary_first ); self->addClassMethod( dict_meta, "last", &Falcon::core::Dictionary_last ); self->addClassMethod( dict_meta, "comp", &Falcon::core::Dictionary_comp ).asSymbol()-> addParam( "source" )->addParam( "filter" ); self->addClassMethod( dict_meta, "mcomp", &Falcon::core::Dictionary_mcomp ); self->addClassMethod( dict_meta, "mfcomp", &Falcon::core::Dictionary_mfcomp ).asSymbol()-> addParam( "filter" ); self->addClassMethod( dict_meta, "do", &Falcon::core::Dictionary_do ).asSymbol()-> addParam( "func" ); self->addClassMethod( dict_meta, "merge", &Falcon::core::mth_dictMerge ).asSymbol()-> addParam("sourceDict"); self->addClassMethod( dict_meta, "keys", &Falcon::core::mth_dictKeys ); self->addClassMethod( dict_meta, "values", &Falcon::core::mth_dictValues ); self->addClassMethod( dict_meta, "get", &Falcon::core::mth_dictGet ).asSymbol()-> addParam("key"); self->addClassMethod( dict_meta, "set", &Falcon::core::mth_dictSet ).asSymbol()-> addParam("key")->addParam("value"); self->addClassMethod( dict_meta, "find", &Falcon::core::mth_dictFind ).asSymbol()-> addParam("key"); self->addClassMethod( dict_meta, "best", &Falcon::core::mth_dictBest ).asSymbol()-> addParam("key"); self->addClassMethod( dict_meta, "remove", &Falcon::core::mth_dictRemove ).asSymbol()-> addParam("key"); self->addClassMethod( dict_meta, "fill", &Falcon::core::mth_dictFill ).asSymbol()-> addParam("item"); self->addClassMethod( dict_meta, "clear", &Falcon::core::mth_dictClear ); self->addClassMethod( dict_meta, "getProperty", &Falcon::core::mth_getProperty ).asSymbol()-> addParam("propName"); self->addClassMethod( dict_meta, "setProperty", &Falcon::core::mth_setProperty ).asSymbol()-> addParam("propName")->addParam("value"); self->addClassMethod( dict_meta, "properties", &Falcon::core::mth_properties ); self->addClassMethod( dict_meta, "dop", &Falcon::core::Dictionary_dop ).asSymbol()-> addParam("key")->addParam("dflt")->addParam("orig"); //================================================================== // Object class // /*# @class Object @from BOM @ingroup bom_classes @brief Object (class instance) type basic object model metaclass. */ Falcon::Symbol *object_meta = self->addClass( "Object" ); object_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); object_meta->exported( false ); object_meta->getClassDef()->setMetaclassFor( FLC_ITEM_OBJECT ); self->addClassMethod( object_meta, "attributes", &Falcon::core::Object_attributes ); self->addClassMethod( object_meta, "getProperty", &Falcon::core::mth_getProperty ).asSymbol()-> addParam("propName"); self->addClassMethod( object_meta, "setProperty", &Falcon::core::mth_setProperty ).asSymbol()-> addParam("propName")->addParam("value"); self->addClassMethod( object_meta, "properties", &Falcon::core::mth_properties ); self->addClassMethod( object_meta, "comp", &Falcon::core::Object_comp ).asSymbol()-> addParam("source")->addParam("filter"); self->addClassMethod( object_meta, "mcomp", &Falcon::core::Object_mcomp ); self->addClassMethod( object_meta, "mfcomp", &Falcon::core::Object_mfcomp ).asSymbol()-> addParam("filter"); self->addClassMethod( object_meta, "apply", &Falcon::core::Object_apply ); self->addClassMethod( object_meta, "getState", &Falcon::core::Object_getState ); self->addClassMethod( object_meta, "setState", &Falcon::core::Object_setState ); self->addClassMethod( object_meta, "retrieve", &Falcon::core::Object_retrieve ); //================================================================== // MemoryBuffer class // /* Docs for this class are in membuf_ext.cpp */ Falcon::Symbol *membuf_meta = self->addClass( "MemoryBuffer" ); membuf_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); membuf_meta->exported( false ); membuf_meta->getClassDef()->setMetaclassFor( FLC_ITEM_MEMBUF ); self->addClassMethod( membuf_meta, "front", &Falcon::core::MemoryBuffer_front ); self->addClassMethod( membuf_meta, "back", &Falcon::core::MemoryBuffer_back ); self->addClassMethod( membuf_meta, "first", &Falcon::core::MemoryBuffer_first ); self->addClassMethod( membuf_meta, "last", &Falcon::core::MemoryBuffer_last ); self->addClassMethod( membuf_meta, "put", &Falcon::core::MemoryBuffer_put ).asSymbol()-> addParam( "data" ); self->addClassMethod( membuf_meta, "get", &Falcon::core::MemoryBuffer_get ); self->addClassMethod( membuf_meta, "rewind", &Falcon::core::MemoryBuffer_rewind ); self->addClassMethod( membuf_meta, "reset", &Falcon::core::MemoryBuffer_reset ); self->addClassMethod( membuf_meta, "flip", &Falcon::core::MemoryBuffer_flip ); self->addClassMethod( membuf_meta, "limit", &Falcon::core::MemoryBuffer_limit ).asSymbol()-> addParam( "pos" ); self->addClassMethod( membuf_meta, "mark", &Falcon::core::MemoryBuffer_mark ); self->addClassMethod( membuf_meta, "position", &Falcon::core::MemoryBuffer_position ).asSymbol()-> addParam( "pos" ); self->addClassMethod( membuf_meta, "clear", &Falcon::core::MemoryBuffer_clear ); self->addClassMethod( membuf_meta, "compact", &Falcon::core::MemoryBuffer_compact ); self->addClassMethod( membuf_meta, "remaining", &Falcon::core::MemoryBuffer_remaining ); self->addClassMethod( membuf_meta, "fill", &Falcon::core::MemoryBuffer_fill ).asSymbol()-> addParam("value"); self->addClassMethod( membuf_meta, "wordSize", &Falcon::core::MemoryBuffer_wordSize ); self->addClassMethod( membuf_meta, "ptr", &Falcon::core::MemoryBuffer_ptr ); /*# @class ClassMethod @from BOM @ingroup bom_classes @brief Metaclass of ClassMethod items. ClassMethods are special methods that, once applied to objects or methods, resolve in a new method taking the original object, but the function/code of one of the base classes. */ Falcon::Symbol *clsmethod_meta = self->addClass( "ClassMethod" ); clsmethod_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); clsmethod_meta->exported( false ); clsmethod_meta->getClassDef()->setMetaclassFor( FLC_ITEM_CLSMETHOD ); self->addClassMethod( clsmethod_meta, "attributes", &Falcon::core::Method_attributes ); /*# @class Method @from BOM @ingroup bom_classes @brief Metaclass of method items. This is the class reflecting falcon method items. A method is a set of an item and a function applied to that. The item can be a method again, as it is possible to apply methods to method items. */ Falcon::Symbol *method_meta = self->addClass( "Method" ); method_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); method_meta->exported( false ); method_meta->getClassDef()->setMetaclassFor( FLC_ITEM_METHOD ); self->addClassMethod( method_meta, "source", &Falcon::core::Method_source ); self->addClassMethod( method_meta, "base", &Falcon::core::Method_base ); self->addClassMethod( method_meta, "attributes", &Falcon::core::Method_attributes ); /*# @class Class @from BOM @ingroup bom_classes @brief Metaclass of Class items. This is the class reflecting falcon class items. A class is a callable item that can generate instances. */ Falcon::Symbol *class_meta = self->addClass( "Class" ); class_meta->getClassDef()->addInheritance( new Falcon::InheritDef( bom_meta ) ); class_meta->exported( false ); class_meta->getClassDef()->setMetaclassFor( FLC_ITEM_CLASS ); self->addClassMethod( class_meta, "attributes", &Falcon::core::Class_attributes ); self->addClassMethod( class_meta, "properties", &Falcon::core::mth_properties ); //======================================================================= // Module declaration body //======================================================================= /*# @global args @brief Script arguments @ingroup general_purpose A global variable holding an array that contains the strings passed as argument for the script. Embedders may change the convention, and pass any Falcon item as arguments; however, falcon command line and the other standard tools pass only an array of strings. */ self->addGlobal( "args", true ); /*# @global scriptName @brief Logical module name of current module @ingroup general_purpose It's a global variable that is usually filled with the script name. It's the logical script name that the VM has assigned to this module, mainly used for debugging. */ self->addGlobal( "scriptName", true ); /*# @global scriptPath @brief Complete path used to load the script @ingroup general_purpose It's a global variable that is usually filled with the location from which the script has been loaded. It's semantic may vary among embedding applications, but it should usually receive the complete path to the main script, in Falcon file convention (forward slashes to separate directories), or the complete URI where applicable. */ self->addGlobal( "scriptPath", true ); self->addExtFunc( "attributes", &Falcon::core::attributes ); self->addExtFunc( "argv", &Falcon::core::core_argv ); self->addExtFunc( "argd", &Falcon::core::core_argd ); self->addExtFunc( "passvp", &Falcon::core::core_passvp ); self->addExtFunc( "chr", &Falcon::core::chr )-> addParam("number"); self->addExtFunc( "ord", &Falcon::core::ord )-> addParam("string"); self->addExtFunc( "getProperty", &Falcon::core::mth_getProperty )-> addParam("obj")->addParam("propName"); self->addExtFunc( "setProperty", &Falcon::core::mth_setProperty )-> addParam("obj")->addParam("propName")->addParam("value"); self->addExtFunc( "properties", &Falcon::core::mth_properties )-> addParam("obj"); self->addExtFunc( "yield", &Falcon::core::yield ); self->addExtFunc( "yieldOut", &Falcon::core::yieldOut )-> addParam("retval"); self->addExtFunc( "sleep", &Falcon::core::_f_sleep )-> addParam("time"); self->addExtFunc( "beginCritical", &Falcon::core::beginCritical ); self->addExtFunc( "endCritical", &Falcon::core::endCritical ); self->addExtFunc( "int", &Falcon::core::val_int )-> addParam("item"); self->addExtFunc( "numeric", &Falcon::core::val_numeric )-> addParam("item"); self->addExtFunc( "typeOf", &Falcon::core::mth_typeId )-> addParam("item"); self->addExtFunc( "exit", &Falcon::core::core_exit )-> addParam("value"); self->addExtFunc( "paramCount", &Falcon::core::paramCount ); self->addExtFunc( "paramNumber", &Falcon::core::_parameter ); self->addExtFunc( "parameter", &Falcon::core::_parameter )-> addParam("pnum"); self->addExtFunc( "paramIsRef", &Falcon::core::paramIsRef )-> addParam("number"); self->addExtFunc( "paramSet", &Falcon::core::paramSet )-> addParam("number")->addParam("value"); self->addExtFunc( "PageDict", &Falcon::core::PageDict )-> addParam("pageSize"); self->addExtFunc( "MemBuf", &Falcon::core::Make_MemBuf )-> addParam("size")->addParam("wordSize"); self->addExtFunc( "MemBufFromPtr", &Falcon::core::Make_MemBufFromPtr )-> addParam("data")->addParam("size")->addParam("wordSize"); // Creating the TraceStep class: // ... first the constructor /*Symbol *ts_init = self->addExtFunc( "TraceStep._init", &Falcon::core::TraceStep_init ); //... then the class Symbol *ts_class = self->addClass( "TraceStep", ts_init ); // then add var props; flc_CLSYM_VAR is 0 and is linked correctly by the VM. self->addClassProperty( ts_class, "module" ); self->addClassProperty( ts_class, "symbol" ); self->addClassProperty( ts_class, "pc" ); self->addClassProperty( ts_class, "line" ); // ... finally add a method, using the symbol that this module returns. self->addClassMethod( ts_class, "toString", self->addExtFunc( "TraceStep.toString", &Falcon::core::TraceStep_toString ) );*/ // Creating the Error class class Symbol *error_init = self->addExtFunc( "Error._init", &Falcon::core::Error_init ); Symbol *error_class = self->addClass( "Error", error_init ) ->addParam( "code" )->addParam( "description")->addParam( "extra" ); error_class->getClassDef()->factory( &Falcon::core::ErrorObjectFactory ); error_class->setWKS( true ); self->addClassMethod( error_class, "toString", self->addExtFunc( "Error.toString", &Falcon::core::Error_toString ) ).setReadOnly( true ); self->addClassMethod( error_class, "heading", &Falcon::core::Error_heading ).setReadOnly( true ); // separated property description to test for separate @property faldoc command /*# @property code Error @brief Error code associated with this error. */ self->addClassProperty( error_class, "code" ). setReflectFunc( Falcon::core::Error_code_rfrom, &Falcon::core::Error_code_rto ); self->addClassProperty( error_class, "description" ). setReflectFunc( Falcon::core::Error_description_rfrom, &Falcon::core::Error_description_rto ); self->addClassProperty( error_class, "message" ). setReflectFunc( Falcon::core::Error_message_rfrom, &Falcon::core::Error_message_rto ); self->addClassProperty( error_class, "systemError" ). setReflectFunc( Falcon::core::Error_systemError_rfrom, &Falcon::core::Error_systemError_rto ); /*# @property origin Error @brief String identifying the origin of the error. This code allows to determine what element of the Falcon engine has raised the error (or eventually, if this error has been raised by a script or a loaded module). The error origin is a string; when an error gets displayed through a standard rendering function (as the Error.toString() method), it is indicated by two letters in front of the error code for better readability. The origin code may be one of the following: - @b compiler - (represented in Error.toString() as CO) - @b assembler - (AS) - @b loader - that is, the module loader (LD) - @b vm - the virtual machine (when not running a script, short VM) - @b script - (that is, a VM running a script, short SS) - @b runtime - (core or runtime modules, RT) - @b module - an extension module (MD). - */ self->addClassProperty( error_class, "origin" ). setReflectFunc( Falcon::core::Error_origin_rfrom, &Falcon::core::Error_origin_rto ); self->addClassProperty( error_class, "module" ). setReflectFunc( Falcon::core::Error_module_rfrom, &Falcon::core::Error_module_rto ); self->addClassProperty( error_class, "symbol" ). setReflectFunc( Falcon::core::Error_symbol_rfrom, &Falcon::core::Error_symbol_rto ); self->addClassProperty( error_class, "line" ). setReflectFunc( Falcon::core::Error_line_rfrom, &Falcon::core::Error_line_rto ); self->addClassProperty( error_class, "pc" ). setReflectFunc( Falcon::core::Error_pc_rfrom, &Falcon::core::Error_pc_rto ); self->addClassProperty( error_class, "subErrors" ). setReflectFunc( Falcon::core::Error_subErrors_rfrom ); self->addClassProperty( error_class, "boxed" ). setReflectFunc( Falcon::core::Error_boxed_rfrom ,&Falcon::core::Error_boxed_rto ); self->addClassMethod( error_class, "getSysErrorDesc", &Falcon::core::Error_getSysErrDesc ).setReadOnly( true ); // Other derived error classes. Falcon::Symbol *synerr_cls = self->addClass( "SyntaxError", &Falcon::core::SyntaxError_init ) ->addParam( "code" )->addParam( "description")->addParam( "extra" ); synerr_cls->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); synerr_cls->setWKS( true ); Falcon::Symbol *genericerr_cls = self->addClass( "GenericError", &Falcon::core::GenericError_init ) ->addParam( "code" )->addParam( "description")->addParam( "extra" ); genericerr_cls->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); genericerr_cls->setWKS( true ); Falcon::Symbol *codeerr_cls = self->addClass( "CodeError", &Falcon::core::CodeError_init ) ->addParam( "code" )->addParam( "description")->addParam( "extra" ); codeerr_cls->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); codeerr_cls->setWKS( true ); Falcon::Symbol *rangeerr_cls = self->addClass( "AccessError", &Falcon::core::AccessError_init ) ->addParam( "code" )->addParam( "description")->addParam( "extra" ); rangeerr_cls->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); rangeerr_cls->setWKS( true ); Falcon::Symbol *matherr_cls = self->addClass( "MathError", &Falcon::core::MathError_init ) ->addParam( "code" )->addParam( "description")->addParam( "extra" ); matherr_cls->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); matherr_cls->setWKS( true ); Falcon::Symbol *ioerr_cls = self->addClass( "IoError", &Falcon::core::IoError_init ) ->addParam( "code" )->addParam( "description")->addParam( "extra" ); ioerr_cls->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); ioerr_cls->setWKS( true ); Falcon::Symbol *typeerr_cls = self->addClass( "TypeError", &Falcon::core::TypeError_init ) ->addParam( "code" )->addParam( "description")->addParam( "extra" ); typeerr_cls->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); typeerr_cls->setWKS( true ); Falcon::Symbol *paramerr_cls = self->addClass( "ParamError", &Falcon::core::ParamError_init ) ->addParam( "code" )->addParam( "description")->addParam( "extra" ); paramerr_cls->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); paramerr_cls->setWKS( true ); Falcon::Symbol *parsererr_cls = self->addClass( "ParseError", &Falcon::core::ParseError_init ) ->addParam( "code" )->addParam( "description")->addParam( "extra" ); parsererr_cls->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); parsererr_cls->setWKS( true ); Falcon::Symbol *cloneerr_cls = self->addClass( "CloneError", &Falcon::core::CloneError_init ) ->addParam( "code" )->addParam( "description")->addParam( "extra" ); cloneerr_cls->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); cloneerr_cls->setWKS( true ); Falcon::Symbol *interr_cls = self->addClass( "InterruptedError", &Falcon::core::IntrruptedError_init ) ->addParam( "code" )->addParam( "description")->addParam( "extra" ); interr_cls->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); interr_cls->setWKS( true ); Falcon::Symbol *msgerr_cls = self->addClass( "MessageError", &Falcon::core::MessageError_init ) ->addParam( "code" )->addParam( "description")->addParam( "extra" ); msgerr_cls->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); msgerr_cls->setWKS( true ); Falcon::Symbol *tableerr_cls = self->addClass( "TableError", &Falcon::core::TableError_init ) ->addParam( "code" )->addParam( "description")->addParam( "extra" ); tableerr_cls->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); tableerr_cls->setWKS( true ); //========================================= // Creating the semaphore class -- will be a FalconObject Symbol *semaphore_init = self->addExtFunc( "Semaphore._init", &Falcon::core::Semaphore_init ); Symbol *semaphore_class = self->addClass( "Semaphore", semaphore_init ); self->addClassMethod( semaphore_class, "post", &Falcon::core::Semaphore_post ).asSymbol()-> addParam("count"); self->addClassMethod( semaphore_class, "wait", &Falcon::core::Semaphore_wait ).asSymbol()-> addParam("timeout"); // GC support Symbol *gcsing = self->addSingleton( "GC" ); Symbol *gc_cls = gcsing->getInstance(); gc_cls->getClassDef()->factory(&Falcon::core::GC_Factory); self->addClassProperty( gc_cls, "usedMem" ). setReflectFunc( &Falcon::core::GC_usedMem_rfrom ); self->addClassProperty( gc_cls, "aliveMem" ). setReflectFunc( &Falcon::core::GC_aliveMem_rfrom ); self->addClassProperty( gc_cls, "items" ). setReflectFunc( &Falcon::core::GC_items_rfrom ); self->addClassProperty( gc_cls, "th_normal" ). setReflectFunc( &Falcon::core::GC_th_normal_rfrom, &Falcon::core::GC_th_normal_rto ); self->addClassProperty( gc_cls, "th_active" ). setReflectFunc( &Falcon::core::GC_th_active_rfrom, &Falcon::core::GC_th_active_rto ); self->addClassMethod( gc_cls, "enable", &Falcon::core::GC_enable ).setReadOnly(true).asSymbol()-> addParam("mode"); self->addClassMethod( gc_cls, "perform", &Falcon::core::GC_perform ).setReadOnly(true).asSymbol()-> addParam("wcoll"); self->addClassMethod( gc_cls, "adjust", &Falcon::core::GC_adjust ).setReadOnly(true).asSymbol()-> addParam("mode"); self->addClassProperty( gc_cls, "ADJ_NONE" ).setInteger(RAMP_MODE_OFF).setReadOnly(true); self->addClassProperty( gc_cls, "ADJ_STRICT" ).setInteger(RAMP_MODE_STRICT_ID).setReadOnly(true); self->addClassProperty( gc_cls, "ADJ_LOOSE" ).setInteger(RAMP_MODE_LOOSE_ID).setReadOnly(true); self->addClassProperty( gc_cls, "ADJ_SMOOTH_SLOW" ).setInteger(RAMP_MODE_SMOOTH_SLOW_ID).setReadOnly(true); self->addClassProperty( gc_cls, "ADJ_SMOOTH_FAST" ).setInteger(RAMP_MODE_SMOOTH_FAST_ID).setReadOnly(true); // VM support self->addExtFunc( "vmVersionInfo", &Falcon::core::vmVersionInfo ); self->addExtFunc( "vmVersionName", &Falcon::core::vmVersionName ); self->addExtFunc( "vmSystemType", &Falcon::core::vmSystemType ); self->addExtFunc( "vmModuleVersionInfo", &Falcon::core::vmModuleVersionInfo ); self->addExtFunc( "vmIsMain", &Falcon::core::vmIsMain ); self->addExtFunc( "vmFalconPath", &Falcon::core::vmFalconPath ); self->addExtFunc( "vmSearchPath", &Falcon::core::vmSearchPath ); self->addExtFunc( "vmModuleName", &Falcon::core::vmModuleName ); self->addExtFunc( "vmModuleLine", &Falcon::core::vmModuleLine ); self->addExtFunc( "vmModulePath", &Falcon::core::vmModulePath ); self->addExtFunc( "vmRelativePath", &Falcon::core::vmRelativePath ); // Format Symbol *format_class = self->addClass( "Format", &Falcon::core::Format_init ); //format_class->getClassDef()->setObjectManager( &core_falcon_data_manager ); self->addClassMethod( format_class, "format", &Falcon::core::Format_format ).asSymbol()-> addParam("item")->addParam("dest"); self->addClassMethod( format_class, "parse", &Falcon::core::Format_parse ).asSymbol()-> addParam("fmtspec"); self->addClassMethod( format_class, "toString", &Falcon::core::Format_toString ); self->addClassProperty( format_class,"size" ); self->addClassProperty( format_class, "decimals" ); self->addClassProperty( format_class, "paddingChr" ); self->addClassProperty( format_class, "groupingChr" ); self->addClassProperty( format_class, "decimalChr" ); self->addClassProperty( format_class, "grouiping" ); self->addClassProperty( format_class, "fixedSize" ); self->addClassProperty( format_class, "rightAlign" ); self->addClassProperty( format_class, "originalFormat" ); self->addClassProperty( format_class, "misAct" ); self->addClassProperty( format_class, "convType" ); self->addClassProperty( format_class, "nilFormat" ); self->addClassProperty( format_class, "negFormat" ); self->addClassProperty( format_class, "numFormat" ); // Iterators Symbol *iterator_class = self->addClass( "Iterator", &Falcon::core::Iterator_init ); iterator_class->setWKS( true ); iterator_class->addParam("collection")->addParam( "atEnd" ); self->addClassMethod( iterator_class, "hasCurrent", &Falcon::core::Iterator_hasCurrent ); self->addClassMethod( iterator_class, "hasNext", &Falcon::core::Iterator_hasNext ); self->addClassMethod( iterator_class, "hasPrev", &Falcon::core::Iterator_hasPrev ); self->addClassMethod( iterator_class, "next", &Falcon::core::Iterator_next ); self->addClassMethod( iterator_class, "prev", &Falcon::core::Iterator_prev ); self->addClassMethod( iterator_class, "value", &Falcon::core::Iterator_value ).asSymbol()-> addParam("subst"); self->addClassMethod( iterator_class, "key", &Falcon::core::Iterator_key ); self->addClassMethod( iterator_class, "erase", &Falcon::core::Iterator_erase ); self->addClassMethod( iterator_class, "compare", &Falcon::core::Iterator_compare ).asSymbol()-> addParam("item"); self->addClassMethod( iterator_class, "clone", &Falcon::core::Iterator_clone ); self->addClassMethod( iterator_class, "find", &Falcon::core::Iterator_find ).asSymbol()-> addParam("key"); self->addClassMethod( iterator_class, "insert", &Falcon::core::Iterator_insert ).asSymbol()-> addParam("key")->addParam("value"); // ================================================ // Functional extensions // //ETA functions self->addExtFunc( "all", &Falcon::core::core_all )->setEta( true )-> addParam("sequence"); self->addExtFunc( "any", &Falcon::core::core_any )->setEta( true )-> addParam("sequence"); self->addExtFunc( "allp", &Falcon::core::core_allp )->setEta( true ); self->addExtFunc( "anyp", &Falcon::core::core_anyp )->setEta( true ); self->addExtFunc( "eval", &Falcon::core::core_eval )->setEta( true )-> addParam("sequence"); self->addExtFunc( "choice", &Falcon::core::core_choice )->setEta( true )-> addParam("selector")->addParam("whenTrue")->addParam("whenFalse"); self->addExtFunc( "xmap", &Falcon::core::core_xmap )->setEta( true )-> addParam("mfunc")->addParam("sequence"); self->addExtFunc( "iff", &Falcon::core::core_iff )->setEta( true )-> addParam("cfr")->addParam("whenTrue")->addParam("whenFalse"); self->addExtFunc( "lit", &Falcon::core::core_lit )->setEta( true )-> addParam("item"); self->addExtFunc( "cascade", &Falcon::core::core_cascade )->setEta( true )-> addParam("callList"); self->addExtFunc( "brigade", &Falcon::core::core_brigade )->setEta( true )-> addParam("fl"); self->addExtFunc( "dolist", &Falcon::core::core_dolist )->setEta( true )-> addParam("processor")->addParam("sequence"); self->addExtFunc( "floop", &Falcon::core::core_floop )->setEta( true )-> addParam("sequence"); self->addExtFunc( "firstOf", &Falcon::core::core_firstof )->setEta( true ); self->addExtFunc( "times", &Falcon::core::core_times )->setEta( true )-> addParam("count")->addParam("sequence"); self->addExtFunc( "let", &Falcon::core::core_let )->setEta( true )-> addParam("dest")->addParam("source"); // other functions self->addExtFunc( "valof", &Falcon::core::core_valof )->addParam("item"); self->addExtFunc( "min", &Falcon::core::core_min ); self->addExtFunc( "max", &Falcon::core::core_max ); self->addExtFunc( "map", &Falcon::core::core_map )-> addParam("mfunc")->addParam("sequence"); self->addExtFunc( "filter", &Falcon::core::core_filter )-> addParam("ffunc")->addParam("sequence"); self->addExtFunc( "reduce", &Falcon::core::core_reduce )-> addParam("reductor")->addParam("sequence")->addParam("initial_value"); self->addExtFunc( "oob", &Falcon::core::core_oob )-> addParam("item"); self->addExtFunc( "deoob", &Falcon::core::core_deoob )-> addParam("item"); self->addExtFunc( "isoob", &Falcon::core::core_isoob )-> addParam("item"); self->addExtFunc( "lbind", &Falcon::core::core_lbind )-> addParam("name")->addParam("value"); //======================================================================= // RTL basic functionality //======================================================================= self->addExtFunc( "print", &Falcon::core::print ); self->addExtFunc( "inspect", &Falcon::core::inspect )-> addParam("item")->addParam( "depth" )->addParam( "maxLen" )->addParam( "stream" ); self->addExtFunc( "input", &Falcon::core::input ); self->addExtFunc( "printl", &Falcon::core::printl ); self->addExtFunc( "seconds", &Falcon::core::seconds ); self->addExtFunc( "epoch", &Falcon::core::epoch ); //======================================================================= // RTL random api //======================================================================= self->addExtFunc( "random", &Falcon::core::flc_random ); self->addExtFunc( "randomChoice", &Falcon::core::flc_randomChoice ); self->addExtFunc( "randomPick", &Falcon::core::flc_randomPick )-> addParam("series"); self->addExtFunc( "randomWalk", &Falcon::core::flc_randomWalk )-> addParam("series")->addParam("size"); self->addExtFunc( "randomGrab", &Falcon::core::flc_randomGrab )-> addParam("series")->addParam("size"); self->addExtFunc( "randomSeed", &Falcon::core::flc_randomSeed )-> addParam("seed"); self->addExtFunc( "randomDice", &Falcon::core::flc_randomDice )-> addParam("dices"); //======================================================================= // RTL math //======================================================================= self->addConstant("PI", 3.1415926535897932384626433832795); self->addConstant("E", 2.7182818284590452353602874713527); self->addExtFunc( "log", &Falcon::core::flc_math_log )-> addParam("x"); self->addExtFunc( "log10", &Falcon::core::flc_math_log )-> addParam("x"); self->addExtFunc( "exp", &Falcon::core::flc_math_exp )-> addParam("x"); self->addExtFunc( "sqrt", &Falcon::core::flc_math_sqrt )-> addParam("x"); self->addExtFunc( "mod", &Falcon::core::flc_math_mod )-> addParam("x")->addParam("y"); self->addExtFunc( "pow", &Falcon::core::flc_math_pow )-> addParam("x")->addParam("y"); self->addExtFunc( "sin", &Falcon::core::flc_math_sin )-> addParam("x"); self->addExtFunc( "cos", &Falcon::core::flc_math_cos )-> addParam("x"); self->addExtFunc( "tan", &Falcon::core::flc_math_tan )-> addParam("x"); self->addExtFunc( "asin", &Falcon::core::flc_math_asin )-> addParam("x"); self->addExtFunc( "acos", &Falcon::core::flc_math_acos )-> addParam("x"); self->addExtFunc( "atan", &Falcon::core::flc_math_atan )-> addParam("x"); self->addExtFunc( "atan2", &Falcon::core::flc_math_atan2 )-> addParam("x")->addParam("y"); self->addExtFunc( "rad2deg", &Falcon::core::flc_math_rad2deg )-> addParam("x"); self->addExtFunc( "deg2rad", &Falcon::core::flc_math_deg2rad )-> addParam("x"); self->addExtFunc( "fract", &Falcon::core::flc_fract )-> addParam("x"); self->addExtFunc( "fint", &Falcon::core::flc_fint )-> addParam("x"); self->addExtFunc( "round", &Falcon::core::flc_round )-> addParam("x"); self->addExtFunc( "floor", &Falcon::core::flc_floor )-> addParam("x"); self->addExtFunc( "ceil", &Falcon::core::flc_ceil )-> addParam("x"); self->addExtFunc( "abs", Falcon::core::flc_abs )-> addParam("x"); self->addExtFunc( "factorial", &Falcon::core::flc_math_factorial )-> addParam("x"); self->addExtFunc( "permutations", &Falcon::core::flc_math_permutations )-> addParam("x")->addParam("y"); self->addExtFunc( "combinations", &Falcon::core::flc_math_combinations )-> addParam("x")->addParam("y"); //======================================================================= // RTL complex api //======================================================================= Falcon::Symbol *c_complex = self->addClass( "Complex", &Falcon::core::Complex_init ); c_complex->getClassDef()->factory( Falcon::core::Complex_Factory ); self->addClassProperty( c_complex, "real" ); self->addClassProperty( c_complex, "imag" ); self->addClassMethod( c_complex, OVERRIDE_OP_ADD, &Falcon::core::Complex_add__ ).asSymbol()-> addParam( "complex" ); self->addClassMethod( c_complex, OVERRIDE_OP_SUB, &Falcon::core::Complex_sub__ ).asSymbol()-> addParam( "complex" ); self->addClassMethod( c_complex, OVERRIDE_OP_MUL, &Falcon::core::Complex_mul__ ).asSymbol()-> addParam( "complex" ); self->addClassMethod( c_complex, OVERRIDE_OP_DIV, &Falcon::core::Complex_div__ ).asSymbol()-> addParam( "complex" ); self->addClassMethod( c_complex, "compare", &Falcon::core::Complex_compare ).asSymbol()-> addParam( "complex" ); self->addClassMethod( c_complex, "abs", &Falcon::core::Complex_abs ); self->addClassMethod( c_complex, "conj", &Falcon::core::Complex_conj ); self->addClassMethod( c_complex, "toString", &Falcon::core::Complex_toString ); //======================================================================= // RTL string api //======================================================================= self->addExtFunc( "strFill", &Falcon::core::mth_strFill ) ->addParam( "string" )->addParam( "chr" ); self->addExtFunc( "strStartsWith", &Falcon::core::mth_strStartsWith ) ->addParam( "string" )->addParam( "chr" )->addParam( "icase" ); self->addExtFunc( "strEndsWith", &Falcon::core::mth_strEndsWith ) ->addParam( "string" )->addParam( "token" )->addParam( "icase" ); self->addExtFunc( "strSplit", &Falcon::core::mth_strSplit ) ->addParam( "string" )->addParam( "token" )->addParam( "count" ); self->addExtFunc( "strSplitTrimmed", &Falcon::core::mth_strSplitTrimmed )-> addParam("string")->addParam("token")->addParam("count"); self->addExtFunc( "strMerge", &Falcon::core::mth_strMerge)-> addParam("array")->addParam("mergeStr")->addParam("count"); self->addExtFunc( "strFind", &Falcon::core::mth_strFind )-> addParam("string")->addParam("needle")->addParam("start")->addParam("end"); self->addExtFunc( "strBackFind", &Falcon::core::mth_strBackFind )-> addParam("string")->addParam("needle")->addParam("start")->addParam("end"); self->addExtFunc( "strReplace", &Falcon::core::mth_strReplace )-> addParam("string")->addParam("substr")->addParam("repstr")->addParam("start")->addParam("end"); self->addExtFunc( "strReplicate", &Falcon::core::mth_strReplicate )-> addParam("string")->addParam("times"); self->addExtFunc( "strEsq", &Falcon::core::mth_strEsq )-> addParam("string")->addParam("inplace"); self->addExtFunc( "strUnesq", &Falcon::core::mth_strUnesq )-> addParam("string")->addParam("inplace"); self->addExtFunc( "strEscape", &Falcon::core::mth_strEscape )-> addParam("string")->addParam("full"); self->addExtFunc( "strUnescape", &Falcon::core::mth_strUnesq )-> addParam("string")->addParam("inplace"); self->addExtFunc( "strBuffer", &Falcon::core::strBuffer )-> addParam("size"); self->addExtFunc( "strUpper", &Falcon::core::mth_strUpper )-> addParam("string"); self->addExtFunc( "strLower", &Falcon::core::mth_strLower )-> addParam("string"); self->addExtFunc( "strCmpIgnoreCase", &Falcon::core::mth_strCmpIgnoreCase )-> addParam("string1")->addParam("string2"); self->addExtFunc( "strWildcardMatch", &Falcon::core::mth_strWildcardMatch ) ->addParam("string")->addParam("wildcard")->addParam("ignoreCase"); self->addExtFunc( "strToMemBuf", &Falcon::core::mth_strToMemBuf )-> addParam("string")->addParam("wordWidth"); self->addExtFunc( "strFromMemBuf", &Falcon::core::strFromMemBuf )-> addParam("membuf"); //======================================================================= // RTL array API //======================================================================= self->addExtFunc( "arrayIns", &Falcon::core::mth_arrayIns )-> addParam("array")->addParam("itempos")->addParam("item"); self->addExtFunc( "arrayDel", &Falcon::core::mth_arrayDel )-> addParam("array")->addParam("item"); self->addExtFunc( "arrayDelAll", &Falcon::core::mth_arrayDelAll )-> addParam("array")->addParam("item"); self->addExtFunc( "arrayAdd", &Falcon::core::mth_arrayAdd )-> addParam("array")->addParam("item"); self->addExtFunc( "arrayResize", &Falcon::core::mth_arrayResize )-> addParam("array")->addParam("newSize"); self->addExtFunc( "arrayScan", &Falcon::core::mth_arrayScan )-> addParam("array")->addParam("func")->addParam("start")->addParam("end"); self->addExtFunc( "arrayFind", &Falcon::core::mth_arrayFind )-> addParam("array")->addParam("item")->addParam("start")->addParam("end"); self->addExtFunc( "arraySort", &Falcon::core::mth_arraySort )-> addParam("array")->addParam("sortingFunc"); self->addExtFunc( "arrayRemove", &Falcon::core::mth_arrayRemove )-> addParam("array")->addParam("itemPos")->addParam("lastItemPos"); self->addExtFunc( "arrayMerge", &Falcon::core::mth_arrayMerge )-> addParam("array1")->addParam("array2")->addParam("insertPos")->addParam("start")->addParam("end"); self->addExtFunc( "arrayHead", &Falcon::core::mth_arrayHead )-> addParam("array"); self->addExtFunc( "arrayTail", &Falcon::core::mth_arrayTail )-> addParam("array"); self->addExtFunc( "arrayBuffer", &Falcon::core::arrayBuffer )-> addParam("size")->addParam("defItem"); self->addExtFunc( "arrayFill", &Falcon::core::mth_arrayFill )-> addParam("array")->addParam("item"); self->addExtFunc( "arrayCompact", &Falcon::core::mth_arrayCompact )-> addParam("array"); self->addExtFunc( "arrayCopy", &Falcon::core::mth_arrayCopy )-> addParam("dest")->addParam("from")->addParam("src")->addParam("first")->addParam("amount"); self->addExtFunc( "arrayNM", &Falcon::core::mth_arrayNM )-> addParam("array"); //======================================================================= // RTL dictionary //======================================================================= self->addExtFunc( "bless", &Falcon::core::bless )-> addParam("dict")->addParam("mode"); self->addExtFunc( "dictMerge", &Falcon::core::mth_dictMerge )-> addParam("destDict")->addParam("sourceDict"); self->addExtFunc( "dictKeys", &Falcon::core::mth_dictKeys )-> addParam("dict"); self->addExtFunc( "dictValues", &Falcon::core::mth_dictValues )-> addParam("dict"); self->addExtFunc( "dictGet", &Falcon::core::mth_dictGet )-> addParam("dict")->addParam("key"); self->addExtFunc( "dictSet", &Falcon::core::mth_dictSet )-> addParam("dict")->addParam("key")->addParam("value"); self->addExtFunc( "dictFind", &Falcon::core::mth_dictFind )-> addParam("dict")->addParam("key"); self->addExtFunc( "dictBest", &Falcon::core::mth_dictBest )-> addParam("dict")->addParam("key"); self->addExtFunc( "dictRemove", &Falcon::core::mth_dictRemove )-> addParam("dict")->addParam("key"); self->addExtFunc( "dictClear", &Falcon::core::mth_dictClear )-> addParam("dict"); self->addExtFunc( "dictFill", &Falcon::core::mth_dictFill )-> addParam("dict")->addParam("item"); self->addExtFunc( "fileType", &Falcon::core::fileType )-> addParam("filename")->addParam("df"); self->addExtFunc( "fileNameMerge", &Falcon::core::fileNameMerge )-> addParam("spec")->addParam("path")->addParam("filename")->addParam("ext"); self->addExtFunc( "fileNameSplit", &Falcon::core::fileNameSplit )-> addParam("path"); self->addExtFunc( "fileName", &Falcon::core::fileName )-> addParam("path"); self->addExtFunc( "filePath", &Falcon::core::filePath )-> addParam("fullpath"); self->addExtFunc( "fileExt", &Falcon::core::fileExt )-> addParam("fullpath"); self->addExtFunc( "fileUnit", &Falcon::core::fileUnit )-> addParam("fullpath"); self->addExtFunc( "fileMove", &Falcon::core::fileMove )-> addParam("sourcePath")->addParam("destPath"); self->addExtFunc( "fileRemove", &Falcon::core::fileRemove )-> addParam("filename"); self->addExtFunc( "fileChown", &Falcon::core::fileChown )-> addParam("path")->addParam("ownerId"); self->addExtFunc( "fileChmod", &Falcon::core::fileChmod )-> addParam("path")->addParam("mode"); self->addExtFunc( "fileChgroup", &Falcon::core::fileChgroup )-> addParam("path")->addParam("groupId"); self->addExtFunc( "fileCopy", &Falcon::core::fileCopy )-> addParam("source")->addParam("dest"); self->addExtFunc( "dirMake", &Falcon::core::dirMake )-> addParam("dirname")->addParam("bFull"); self->addExtFunc( "dirChange", &Falcon::core::dirChange )-> addParam("newDir"); self->addExtFunc( "dirCurrent", &Falcon::core::dirCurrent ); self->addExtFunc( "dirRemove", &Falcon::core::dirRemove )-> addParam("dir"); self->addExtFunc( "dirReadLink", &Falcon::core::dirReadLink )-> addParam("linkPath"); self->addExtFunc( "dirMakeLink", &Falcon::core::dirMakeLink )-> addParam("source")->addParam("dest"); self->addExtFunc( "deserialize", &Falcon::core::deserialize )-> addParam("stream"); self->addExtFunc( "include", &Falcon::core::fal_include )-> addParam("file")->addParam("inputEnc")->addParam("path")->addParam("symDict"); //============================================== // Transcoding functions self->addExtFunc( "transcodeTo", &Falcon::core::transcodeTo )-> addParam("string")->addParam("encoding"); self->addExtFunc( "transcodeFrom", &Falcon::core::transcodeFrom )-> addParam("string")->addParam("encoding"); self->addExtFunc( "getSystemEncoding", &Falcon::core::getSystemEncoding ); //============================================== // Environment variable functions self->addExtFunc( "getenv", &Falcon::core::falcon_getenv )-> addParam("varName"); self->addExtFunc( "setenv", &Falcon::core::falcon_setenv )-> addParam("varName")->addParam("value"); self->addExtFunc( "unsetenv", &Falcon::core::falcon_unsetenv )-> addParam("varName"); self->addExtFunc( "getEnviron", &Falcon::core::falcon_getEnviron ); //======================================================================= // Messaging API //======================================================================= self->addExtFunc( "broadcast", &Falcon::core::broadcast )-> addParam("msg"); self->addExtFunc( "subscribe", &Falcon::core::subscribe )-> addParam("msg")->addParam("handler")->addParam("prio"); self->addExtFunc( "unsubscribe", &Falcon::core::unsubscribe )-> addParam("msg")->addParam("handler"); self->addExtFunc( "getSlot", &Falcon::core::getSlot )-> addParam("msg")->addParam( "make" ); self->addExtFunc( "consume", &Falcon::core::consume ); self->addExtFunc( "assert", &Falcon::core::assert )-> addParam("msg")->addParam("data"); self->addExtFunc( "retract", &Falcon::core::retract )-> addParam("msg"); self->addExtFunc( "getAssert", &Falcon::core::getAssert )-> addParam("msg")->addParam( "default" ); Falcon::Symbol *vmslot_class = self->addClass( "VMSlot", &Falcon::core::VMSlot_init )-> addParam("name"); vmslot_class->setWKS( true ); vmslot_class->getClassDef()->factory( &Falcon::CoreSlotFactory ); // methods -- the first example is equivalent to the following. self->addClassMethod( vmslot_class, "name", &Falcon::core::VMSlot_name ); self->addClassMethod( vmslot_class, "broadcast", &Falcon::core::VMSlot_broadcast ); self->addClassMethod( vmslot_class, "subscribe", &Falcon::core::VMSlot_subscribe ).asSymbol()-> addParam("handler")->addParam("prio"); self->addClassMethod( vmslot_class, "unsubscribe", &Falcon::core::VMSlot_unsubscribe ).asSymbol()-> addParam("handler"); self->addClassMethod( vmslot_class, "prepend", &Falcon::core::VMSlot_prepend ).asSymbol()-> addParam("handler"); self->addClassMethod( vmslot_class, "assert", &Falcon::core::VMSlot_assert ).asSymbol()-> addParam("data"); self->addClassMethod( vmslot_class, "retract", &Falcon::core::VMSlot_retract ); self->addClassMethod( vmslot_class, "getAssert", &Falcon::core::VMSlot_getAssert ).asSymbol()-> addParam("default"); self->addClassMethod( vmslot_class, "first", &Falcon::core::VMSlot_first ); self->addClassMethod( vmslot_class, "last", &Falcon::core::VMSlot_last ); self->addClassMethod( vmslot_class, "send", &Falcon::core::VMSlot_send ).asSymbol()-> addParam("event"); self->addClassMethod( vmslot_class, "register", &Falcon::core::VMSlot_register ).asSymbol()-> addParam("event")->addParam("callback"); self->addClassMethod( vmslot_class, "getEvent", &Falcon::core::VMSlot_getEvent ).asSymbol()-> addParam("event"); //======================================================================= // RTL CLASSES //======================================================================= //============================================== // Stream class // Factory functions self->addExtFunc( "InputStream", &Falcon::core::InputStream_creator )-> addParam("fileName")->addParam("shareMode"); self->addExtFunc( "OutputStream", &Falcon::core::OutputStream_creator )-> addParam("fileName")->addParam("createMode")->addParam("shareMode"); self->addExtFunc( "IOStream", &Falcon::core::IOStream_creator )-> addParam("fileName")->addParam("createMode")->addParam("shareMode"); self->addExtFunc( "readURI", &Falcon::core::readURI )-> addParam("uri")->addParam("encoding"); self->addExtFunc( "writeURI", &Falcon::core::writeURI )-> addParam("uri")->addParam("data")->addParam("encoding"); // create the stream class (without constructor). Falcon::Symbol *stream_class = self->addClass( "Stream" ); stream_class->setWKS(true); //stream_class->getClassDef()->setObjectManager( &core_falcon_data_manager ); self->addClassMethod( stream_class, "close", &Falcon::core::Stream_close ); self->addClassMethod( stream_class, "flush", &Falcon::core::Stream_flush ); self->addClassMethod( stream_class, "read", &Falcon::core::Stream_read ).asSymbol()-> addParam("buffer")->addParam("size"); self->addClassMethod( stream_class, "grab", &Falcon::core::Stream_grab ).asSymbol()-> addParam("size"); self->addClassMethod( stream_class, "grabLine", &Falcon::core::Stream_grabLine ).asSymbol()-> addParam("size"); self->addClassMethod( stream_class, "readLine", &Falcon::core::Stream_readLine ).asSymbol()-> addParam("buffer")->addParam("size"); self->addClassMethod( stream_class, "write", &Falcon::core::Stream_write ).asSymbol()-> addParam("buffer")->addParam("size")->addParam("start"); self->addClassMethod( stream_class, "seek", &Falcon::core::Stream_seek ).asSymbol()-> addParam("position"); self->addClassMethod( stream_class, "seekEnd", &Falcon::core::Stream_seekEnd ).asSymbol()-> addParam("position"); self->addClassMethod( stream_class, "seekCur", &Falcon::core::Stream_seekCur ).asSymbol()-> addParam("position"); self->addClassMethod( stream_class, "tell", &Falcon::core::Stream_tell ); self->addClassMethod( stream_class, "truncate", &Falcon::core::Stream_truncate ).asSymbol()-> addParam("position"); self->addClassMethod( stream_class, "lastMoved", &Falcon::core::Stream_lastMoved ); self->addClassMethod( stream_class, "lastError", &Falcon::core::Stream_lastError ); self->addClassMethod( stream_class, "errorDescription", &Falcon::core::Stream_errorDescription ); self->addClassMethod( stream_class, "eof", &Falcon::core::Stream_eof ); self->addClassMethod( stream_class, "isOpen", &Falcon::core::Stream_errorDescription ); self->addClassMethod( stream_class, "readAvailable", &Falcon::core::Stream_readAvailable ).asSymbol()-> addParam("seconds"); self->addClassMethod( stream_class, "writeAvailable", &Falcon::core::Stream_writeAvailable ).asSymbol()-> addParam("seconds"); self->addClassMethod( stream_class, "readText", &Falcon::core::Stream_readText ).asSymbol()-> addParam("buffer")->addParam("size"); self->addClassMethod( stream_class, "grabText", &Falcon::core::Stream_grabText ).asSymbol()-> addParam("size"); self->addClassMethod( stream_class, "writeText", &Falcon::core::Stream_writeText ).asSymbol()-> addParam("buffer")->addParam("start")->addParam("end"); self->addClassMethod( stream_class, "setEncoding", &Falcon::core::Stream_setEncoding ).asSymbol()-> addParam("encoding")->addParam("EOLMode"); self->addClassMethod( stream_class, "setBuffering", &Falcon::core::Stream_setBuffering ).asSymbol()-> addParam("size"); self->addClassMethod( stream_class, "getBuffering", &Falcon::core::Stream_getBuffering ); self->addClassMethod( stream_class, "clone", &Falcon::core::Stream_clone ); // Specialization of the stream class to manage the closing of process bound streams. Falcon::Symbol *stdstream_class = self->addClass( "StdStream" ); stdstream_class->setWKS(true); self->addClassMethod( stdstream_class, "close", &Falcon::core::StdStream_close ); self->addClassProperty( stdstream_class, "_stdStreamType" ); stdstream_class->getClassDef()->addInheritance( new Falcon::InheritDef( stream_class ) ); self->addConstant( "FILE_EXCLUSIVE", (Falcon::int64) Falcon::BaseFileStream::e_smExclusive ); self->addConstant( "FILE_SHARE_READ", (Falcon::int64) Falcon::BaseFileStream::e_smShareRead ); self->addConstant( "FILE_SHARE", (Falcon::int64) Falcon::BaseFileStream::e_smShareFull ); self->addConstant( "CR_TO_CR", (Falcon::int64) CR_TO_CR ); self->addConstant( "CR_TO_CRLF", (Falcon::int64) CR_TO_CRLF ); self->addConstant( "SYSTEM_DETECT", (Falcon::int64) SYSTEM_DETECT ); //============================================== // StringStream class Falcon::Symbol *sstream_ctor = self->addExtFunc( "StringStream._init", Falcon::core::StringStream_init, false ); Falcon::Symbol *sstream_class = self->addClass( "StringStream", sstream_ctor ); sstream_class->setWKS(true); // inherits from stream. sstream_class->getClassDef()->addInheritance( new Falcon::InheritDef( stream_class ) ); // add methods self->addClassMethod( sstream_class, "getString", &Falcon::core::StringStream_getString ); self->addClassMethod( sstream_class, "closeToString", &Falcon::core::StringStream_closeToString ); //============================================== // The TimeStamp class -- declaration functional equivalent to // the one used for StringStream class (there in two steps, here in one). Falcon::Symbol *tstamp_class = self->addClass( "TimeStamp", &Falcon::core::TimeStamp_init ); tstamp_class->setWKS( true ); // methods -- the first example is equivalent to the following. self->addClassMethod( tstamp_class, "currentTime", self->addExtFunc( "TimeStamp.currentTime", &Falcon::core::TimeStamp_currentTime, false ) ).setReadOnly(true); self->addClassMethod( tstamp_class, "dayOfYear", &Falcon::core::TimeStamp_dayOfYear ).setReadOnly(true); self->addClassMethod( tstamp_class, "dayOfWeek", &Falcon::core::TimeStamp_dayOfWeek ).setReadOnly(true); self->addClassMethod( tstamp_class, "toString", &Falcon::core::TimeStamp_toString ).setReadOnly(true).asSymbol()-> addParam("format"); self->addClassMethod( tstamp_class, "add", &Falcon::core::TimeStamp_add ).setReadOnly(true).asSymbol()-> addParam("timestamp"); self->addClassMethod( tstamp_class, "distance", &Falcon::core::TimeStamp_distance ).setReadOnly(true).asSymbol()-> addParam("timestamp"); self->addClassMethod( tstamp_class, "isValid", &Falcon::core::TimeStamp_isValid ).setReadOnly(true); self->addClassMethod( tstamp_class, "isLeapYear", &Falcon::core::TimeStamp_isLeapYear ).setReadOnly(true); self->addClassMethod( tstamp_class, "toLongFormat", &Falcon::core::TimeStamp_toLongFormat ).setReadOnly(true); self->addClassMethod( tstamp_class, "fromLongFormat", &Falcon::core::TimeStamp_fromLongFormat ).setReadOnly(true); self->addClassMethod( tstamp_class, "compare", &Falcon::core::TimeStamp_compare ).setReadOnly(true).asSymbol()-> addParam("timestamp"); self->addClassMethod( tstamp_class, "fromRFC2822", &Falcon::core::TimeStamp_fromRFC2822 ).setReadOnly(true).asSymbol()-> addParam("sTimestamp"); self->addClassMethod( tstamp_class, "toRFC2822", &Falcon::core::TimeStamp_toRFC2822 ).setReadOnly(true); self->addClassMethod( tstamp_class, "changeZone", &Falcon::core::TimeStamp_changeZone ).setReadOnly(true).asSymbol()-> addParam("zone"); // properties TimeStamp ts_dummy; self->addClassProperty( tstamp_class, "year" ).setReflective( e_reflectShort, &ts_dummy, &ts_dummy.m_year ); self->addClassProperty( tstamp_class, "month" ).setReflective( e_reflectShort, &ts_dummy, &ts_dummy.m_month ); self->addClassProperty( tstamp_class, "day" ).setReflective( e_reflectShort, &ts_dummy, &ts_dummy.m_day ); self->addClassProperty( tstamp_class, "hour" ).setReflective( e_reflectShort, &ts_dummy, &ts_dummy.m_hour ); self->addClassProperty( tstamp_class, "minute" ).setReflective( e_reflectShort, &ts_dummy, &ts_dummy.m_minute ); self->addClassProperty( tstamp_class, "second" ).setReflective( e_reflectShort, &ts_dummy, &ts_dummy.m_second ); self->addClassProperty( tstamp_class, "msec" ).setReflective( e_reflectShort, &ts_dummy, &ts_dummy.m_msec ); self->addClassProperty( tstamp_class, "timezone" ). setReflectFunc( Falcon::core::TimeStamp_timezone_rfrom, &Falcon::core::TimeStamp_timezone_rto ); Falcon::Symbol *c_timezone = self->addClass( "TimeZone" ); self->addClassMethod( c_timezone, "getDisplacement", &Falcon::core::TimeZone_getDisplacement ).asSymbol()-> addParam("tz"); self->addClassMethod( c_timezone, "describe", &Falcon::core::TimeZone_describe ).asSymbol()-> addParam("tz"); self->addClassMethod( c_timezone, "getLocal", &Falcon::core::TimeZone_getLocal ); self->addClassProperty( c_timezone, "local" ).setInteger( Falcon::tz_local ); self->addClassProperty( c_timezone, "UTC" ).setInteger( Falcon::tz_UTC ); self->addClassProperty( c_timezone, "GMT" ).setInteger( Falcon::tz_UTC ); self->addClassProperty( c_timezone, "E1" ).setInteger( Falcon::tz_UTC_E_1 ); self->addClassProperty( c_timezone, "E2" ).setInteger( Falcon::tz_UTC_E_2 ); self->addClassProperty( c_timezone, "E3" ).setInteger( Falcon::tz_UTC_E_3 ); self->addClassProperty( c_timezone, "E4" ).setInteger( Falcon::tz_UTC_E_4 ); self->addClassProperty( c_timezone, "E5" ).setInteger( Falcon::tz_UTC_E_5 ); self->addClassProperty( c_timezone, "E6" ).setInteger( Falcon::tz_UTC_E_6 ); self->addClassProperty( c_timezone, "E7" ).setInteger( Falcon::tz_UTC_E_7 ); self->addClassProperty( c_timezone, "E8" ).setInteger( Falcon::tz_UTC_E_8 ); self->addClassProperty( c_timezone, "E9" ).setInteger( Falcon::tz_UTC_E_9 ); self->addClassProperty( c_timezone, "E10" ).setInteger( Falcon::tz_UTC_E_10 ); self->addClassProperty( c_timezone, "E11" ).setInteger( Falcon::tz_UTC_E_11 ); self->addClassProperty( c_timezone, "E12" ).setInteger( Falcon::tz_UTC_E_12 ); self->addClassProperty( c_timezone, "W1" ).setInteger( Falcon::tz_UTC_W_1 ); self->addClassProperty( c_timezone, "W2" ).setInteger( Falcon::tz_UTC_W_2 ); self->addClassProperty( c_timezone, "W3" ).setInteger( Falcon::tz_UTC_W_3 ); self->addClassProperty( c_timezone, "W4" ).setInteger( Falcon::tz_UTC_W_4 ); self->addClassProperty( c_timezone, "EDT" ).setInteger( Falcon::tz_UTC_W_4 ); self->addClassProperty( c_timezone, "W5" ).setInteger( Falcon::tz_UTC_W_5 ); self->addClassProperty( c_timezone, "EST" ).setInteger( Falcon::tz_UTC_W_5 ); self->addClassProperty( c_timezone, "CDT" ).setInteger( Falcon::tz_UTC_W_5 ); self->addClassProperty( c_timezone, "W6" ).setInteger( Falcon::tz_UTC_W_6 ); self->addClassProperty( c_timezone, "CST" ).setInteger( Falcon::tz_UTC_W_6 ); self->addClassProperty( c_timezone, "MDT" ).setInteger( Falcon::tz_UTC_W_6 ); self->addClassProperty( c_timezone, "W7" ).setInteger( Falcon::tz_UTC_W_7 ); self->addClassProperty( c_timezone, "MST" ).setInteger( Falcon::tz_UTC_W_7 ); self->addClassProperty( c_timezone, "PDT" ).setInteger( Falcon::tz_UTC_W_7 ); self->addClassProperty( c_timezone, "W8" ).setInteger( Falcon::tz_UTC_W_8 ); self->addClassProperty( c_timezone, "PST" ).setInteger( Falcon::tz_UTC_W_8 ); self->addClassProperty( c_timezone, "W9" ).setInteger( Falcon::tz_UTC_W_9 ); self->addClassProperty( c_timezone, "W10" ).setInteger( Falcon::tz_UTC_W_10 ); self->addClassProperty( c_timezone, "W11" ).setInteger( Falcon::tz_UTC_W_11 ); self->addClassProperty( c_timezone, "W12" ).setInteger( Falcon::tz_UTC_W_12 ); self->addClassProperty( c_timezone, "NFT" ).setInteger( Falcon::tz_NFT ); self->addClassProperty( c_timezone, "ACDT" ).setInteger( Falcon::tz_ACDT ); self->addClassProperty( c_timezone, "ACST" ).setInteger( Falcon::tz_ACST ); self->addClassProperty( c_timezone, "HAT" ).setInteger( Falcon::tz_HAT ); self->addClassProperty( c_timezone, "NST" ).setInteger( Falcon::tz_NST ); self->addClassProperty( c_timezone, "NONE" ).setInteger( Falcon::tz_NST ); // A factory function that creates a timestamp already initialized to the current time: self->addExtFunc( "CurrentTime", &Falcon::core::CurrentTime ); self->addExtFunc( "ParseRFC2822", &Falcon::core::ParseRFC2822 ); //======================================================================= // Directory class //======================================================================= Falcon::Symbol *cont_class = self->addClass( "Continuation", &Falcon::core::Continuation_init ) ->addParam("item"); cont_class->getClassDef()->factory( &Falcon::ContinuationCarrier::factory ); self->addClassMethod( cont_class, "__call", &Falcon::core::Continuation_call ); self->addClassMethod( cont_class, "reset", &Falcon::core::Continuation_reset ); self->addClassMethod( cont_class, "complete", &Falcon::core::Continuation_complete ); self->addClassMethod( cont_class, "_suspend", &Falcon::core::Continuation__suspend ); //======================================================================= // Directory class //======================================================================= Falcon::Symbol *dir_class = self->addClass( "Directory", &Falcon::core::Directory_init ) ->addParam("path"); dir_class->setWKS(true); self->addClassMethod( dir_class, "read", &Falcon::core::Directory_read ); self->addClassMethod( dir_class, "descend", &Falcon::core::Directory_descend ).asSymbol()-> addParam("dfunc")->addParam("ffunc"); self->addClassMethod( dir_class, "close", &Falcon::core::Directory_close ); self->addClassMethod( dir_class, "error", &Falcon::core::Directory_error ); // Add the directory constants //======================================================================= // FileStat class //======================================================================= Falcon::Symbol *fileStats_class = self->addClass( "FileStat", Falcon::core::FileStat_init ) ->addParam( "path" ); fileStats_class->setWKS( true ); fileStats_class->getClassDef()->factory( &Falcon::core::FileStatObjectFactory ); // properties core::FileStatObject::InnerData id; self->addClassProperty( fileStats_class, "ftype" ). setReflectFunc( Falcon::core::FileStats_type_rfrom ); // read only, we have no set. self->addClassProperty( fileStats_class, "size" ).setReflective( e_reflectLL, &id, &id.m_fsdata.m_size ); self->addClassProperty( fileStats_class, "owner" ).setReflective( e_reflectUInt, &id, &id.m_fsdata.m_owner ); self->addClassProperty( fileStats_class, "group" ).setReflective( e_reflectUInt, &id, &id.m_fsdata.m_group ); self->addClassProperty( fileStats_class, "access" ).setReflective( e_reflectUInt, &id, &id.m_fsdata.m_access ); self->addClassProperty( fileStats_class, "attribs" ).setReflective( e_reflectUInt, &id, &id.m_fsdata.m_attribs ); self->addClassProperty( fileStats_class, "mtime" ). setReflectFunc( Falcon::core::FileStats_mtime_rfrom ); self->addClassProperty( fileStats_class, "ctime" ). setReflectFunc( Falcon::core::FileStats_ctime_rfrom ); self->addClassProperty( fileStats_class, "atime" ). setReflectFunc( Falcon::core::FileStats_atime_rfrom ); self->addClassProperty( fileStats_class, "NORMAL" ).setInteger( (Falcon::int64) Falcon::FileStat::t_normal ). setReadOnly( true ); self->addClassProperty( fileStats_class, "DIR" ).setInteger( (Falcon::int64) Falcon::FileStat::t_dir ). setReadOnly( true ); self->addClassProperty( fileStats_class, "PIPE" ).setInteger( (Falcon::int64) Falcon::FileStat::t_pipe ). setReadOnly( true ); self->addClassProperty( fileStats_class, "LINK" ).setInteger( (Falcon::int64) Falcon::FileStat::t_link ). setReadOnly( true ); self->addClassProperty( fileStats_class, "DEVICE" ).setInteger( (Falcon::int64) Falcon::FileStat::t_device ). setReadOnly( true ); self->addClassProperty( fileStats_class, "SOCKET" ).setInteger( (Falcon::int64) Falcon::FileStat::t_socket ). setReadOnly( true ); self->addClassProperty( fileStats_class, "UNKNOWN" ).setInteger( (Falcon::int64) Falcon::FileStat::t_unknown ). setReadOnly( true ); self->addClassProperty( fileStats_class, "NOTFOUND" ).setInteger( (Falcon::int64) Falcon::FileStat::t_notFound ). setReadOnly( true ); // methods - set read only to have full reflection self->addClassMethod( fileStats_class, "read", Falcon::core::FileStat_read ).setReadOnly(true).asSymbol()-> addParam("path"); //======================================================================= // The Random class //======================================================================= Falcon::Symbol *random_class = self->addClass( "Random", &Falcon::core::flc_Random_init ) ->addParam("seed"); random_class->setWKS( true ); self->addClassMethod( random_class, "random", &Falcon::core::flc_random ); self->addClassMethod( random_class, "randomChoice", &Falcon::core::flc_randomChoice ); self->addClassMethod( random_class, "randomPick", &Falcon::core::flc_randomPick ).asSymbol()-> addParam("series"); self->addClassMethod( random_class, "randomWalk", &Falcon::core::flc_randomWalk ).asSymbol()-> addParam("series")->addParam("size"); self->addClassMethod( random_class, "randomGrab", &Falcon::core::flc_randomGrab ).asSymbol()-> addParam("series")->addParam("size"); self->addClassMethod( random_class, "randomSeed", &Falcon::core::flc_randomSeed ).asSymbol()-> addParam("seed"); self->addClassMethod( random_class, "randomDice", &Falcon::core::flc_randomDice ).asSymbol()-> addParam("dices"); //======================================================================= // The sequence class //======================================================================= Falcon::Symbol *sequence_class = self->addClass( "Sequence" ); sequence_class->exported(false); self->addClassMethod( sequence_class, "comp", &Falcon::core::Sequence_comp ).asSymbol()-> addParam("source")->addParam("filter"); self->addClassMethod( sequence_class, "mcomp", &Falcon::core::Sequence_mcomp ); self->addClassMethod( sequence_class, "mfcomp", &Falcon::core::Sequence_mfcomp ).asSymbol()-> addParam("filter"); self->addClassMethod( sequence_class, "front", &Falcon::core::Sequence_front ); self->addClassMethod( sequence_class, "back", &Falcon::core::Sequence_back ); self->addClassMethod( sequence_class, "last", &Falcon::core::Sequence_last ); self->addClassMethod( sequence_class, "first", &Falcon::core::Sequence_first ); self->addClassMethod( sequence_class, "empty", &Falcon::core::Sequence_empty ); self->addClassMethod( sequence_class, "clear", &Falcon::core::Sequence_clear ); self->addClassMethod( sequence_class, "append", &Falcon::core::Sequence_append ).asSymbol()-> addParam("item"); self->addClassMethod( sequence_class, "prepend", &Falcon::core::Sequence_prepend ).asSymbol()-> addParam("item"); //======================================================================= // The list class //======================================================================= Falcon::Symbol *list_class = self->addClass( "List", &Falcon::core::List_init ); list_class->setWKS(true); // inherits from stream. list_class->getClassDef()->addInheritance( new Falcon::InheritDef( sequence_class ) ); self->addClassMethod( list_class, "push", &Falcon::core::List_push ).asSymbol()-> addParam("item"); self->addClassMethod( list_class, "pop", &Falcon::core::List_pop ); self->addClassMethod( list_class, "pushFront", &Falcon::core::List_pushFront ).asSymbol()-> addParam("item"); self->addClassMethod( list_class, "popFront", &Falcon::core::List_popFront ); self->addClassMethod( list_class, "len", &Falcon::core::List_len ); //======================================================================= // The Set class //======================================================================= Falcon::Symbol *set_class = self->addClass( "Set", &Falcon::core::Set_init ); set_class->setWKS(true); // inherits from stream. set_class->getClassDef()->addInheritance( new Falcon::InheritDef( sequence_class ) ); self->addClassMethod( set_class, "insert", &Falcon::core::Set_insert ).asSymbol()-> addParam("item"); self->addClassMethod( set_class, "remove", &Falcon::core::Set_remove ).asSymbol()-> addParam("item"); self->addClassMethod( set_class, "contains", &Falcon::core::Set_contains ).asSymbol()-> addParam("item"); self->addClassMethod( set_class, "find", &Falcon::core::Set_find ).asSymbol()-> addParam("item"); self->addClassMethod( set_class, "len", &Falcon::core::Set_len ); //======================================================================= // The path class //======================================================================= Falcon::Symbol *path_class = self->addClass( "Path", &Falcon::core::Path_init ) ->addParam( "path" ); path_class->getClassDef()->factory( Falcon::core::PathObjectFactory ); path_class->setWKS(true); self->addClassProperty( path_class, "path" ). setReflectFunc( &Falcon::core::Path_path_rfrom, &Falcon::core::Path_path_rto ); self->addClassProperty( path_class, "unit" ). setReflectFunc( &Falcon::core::Path_unit_rfrom, &Falcon::core::Path_unit_rto ); self->addClassProperty( path_class, "location" ). setReflectFunc( &Falcon::core::Path_location_rfrom, &Falcon::core::Path_location_rto ); self->addClassProperty( path_class, "fulloc" ). setReflectFunc( &Falcon::core::Path_fullloc_rfrom, &Falcon::core::Path_fullloc_rto ); self->addClassProperty( path_class, "file" ). setReflectFunc( &Falcon::core::Path_file_rfrom, &Falcon::core::Path_file_rto ); self->addClassProperty( path_class, "extension" ). setReflectFunc( &Falcon::core::Path_extension_rfrom, &Falcon::core::Path_extension_rto ); self->addClassProperty( path_class, "filename" ). setReflectFunc( &Falcon::core::Path_filename_rfrom, &Falcon::core::Path_filename_rto ); self->addClassProperty( path_class, "winpath" ). setReflectFunc( &Falcon::core::Path_winpath_rfrom ); self->addClassProperty( path_class, "winloc" ). setReflectFunc( &Falcon::core::Path_winloc_rfrom ); self->addClassProperty( path_class, "winfulloc" ). setReflectFunc( &Falcon::core::Path_winfulloc_rfrom ); //======================================================================= // The path class //======================================================================= Falcon::Symbol *uri_class = self->addClass( "URI", &Falcon::core::URI_init ) ->addParam( "path" )->addParam("decode"); uri_class->getClassDef()->factory( &Falcon::core::UriObject::factory ); uri_class->setWKS(true); self->addClassProperty( uri_class, "scheme" ); self->addClassProperty( uri_class, "userInfo" ); self->addClassProperty( uri_class, "host" ); self->addClassProperty( uri_class, "port" ); self->addClassProperty( uri_class, "path" ); self->addClassProperty( uri_class, "query" ); self->addClassProperty( uri_class, "fragment" ); self->addClassProperty( uri_class, "uri" ); self->addClassMethod( uri_class, "toString", &Falcon::core::URI_toString ); self->addClassMethod( uri_class, "encode", &Falcon::core::URI_encode ).asSymbol()-> addParam("string"); self->addClassMethod( uri_class, "decode", &Falcon::core::URI_decode ).asSymbol()-> addParam("enc_string"); self->addClassMethod( uri_class, "getFields", &Falcon::core::URI_getFields ); self->addClassMethod( uri_class, "setFields", &Falcon::core::URI_setFields ).asSymbol()-> addParam("fields"); //======================================================================= // The command line parser class //======================================================================= Falcon::Symbol *cmdparser_class = self->addClass( "CmdlineParser", true ); self->addClassMethod( cmdparser_class, "parse", &Falcon::core::CmdlineParser_parse ).asSymbol()-> addParam("args"); self->addClassMethod( cmdparser_class, "expectValue", &Falcon::core::CmdlineParser_expectValue ); self->addClassMethod( cmdparser_class, "terminate", &Falcon::core::CmdlineParser_terminate ); // private property internally used to communicate between the child classes and // the base parse. self->addClassProperty( cmdparser_class, "_request" ); // Properties that will hold callbacks self->addClassProperty( cmdparser_class, "onOption" ); self->addClassProperty( cmdparser_class, "onFree" ); self->addClassProperty( cmdparser_class, "onValue" ); self->addClassProperty( cmdparser_class, "onSwitchOff" ); self->addClassProperty( cmdparser_class, "passMinusMinus" ); self->addClassProperty( cmdparser_class, "lastParsed" ); self->addClassMethod( cmdparser_class, "usage", &Falcon::core::CmdlineParser_usage ); //======================================================================= // SYSTEM API //======================================================================= self->addExtFunc( "stdIn", &Falcon::core::_stdIn ); self->addExtFunc( "stdOut", &Falcon::core::_stdOut ); self->addExtFunc( "stdErr", &Falcon::core::_stdErr ); self->addExtFunc( "stdInRaw", &Falcon::core::stdInRaw ); self->addExtFunc( "stdOutRaw", &Falcon::core::stdOutRaw ); self->addExtFunc( "stdErrRaw", &Falcon::core::stdErrRaw ); self->addExtFunc( "systemErrorDescription", &Falcon::core::systemErrorDescription )-> addParam("errorCode"); //======================================================================= // Tokenizer class //======================================================================= Falcon::Symbol *tok_class = self->addClass( "Tokenizer", Falcon::core::Tokenizer_init ) ->addParam( "seps" )->addParam( "options" )->addParam( "tokLen" )->addParam( "source" ); // properties self->addClassProperty( tok_class, "_source" ); self->addClassProperty( tok_class, "groupsep" ).setInteger( TOKENIZER_OPT_GRROUPSEP ); self->addClassProperty( tok_class, "bindsep" ).setInteger( TOKENIZER_OPT_BINDSEP ); self->addClassProperty( tok_class, "trim" ).setInteger( TOKENIZER_OPT_TRIM ); self->addClassProperty( tok_class, "retsep" ).setInteger( TOKENIZER_OPT_RSEP ); self->addClassProperty( tok_class, "wsAsToken" ).setInteger( TOKENIZER_OPT_WSISTOK ); self->addClassMethod( tok_class, "parse", &Falcon::core::Tokenizer_parse ).asSymbol()-> addParam("source"); self->addClassMethod( tok_class, "rewind", &Falcon::core::Tokenizer_rewind ); self->addClassMethod( tok_class, "next", &Falcon::core::Tokenizer_next ); self->addClassMethod( tok_class, "nextToken", &Falcon::core::Tokenizer_nextToken ); self->addClassMethod( tok_class, "token", &Falcon::core::Tokenizer_token ); self->addClassMethod( tok_class, "hasCurrent", &Falcon::core::Tokenizer_hasCurrent ); //======================================================================= // Web coders - Base64 //======================================================================= Falcon::Symbol *base64_class = self->addClass( "Base64", Falcon::core::Tokenizer_init ); // properties self->addClassMethod( base64_class, "encode", &Falcon::core::Base64_encode ).asSymbol()-> addParam("data"); self->addClassMethod( base64_class, "decode", &Falcon::core::Base64_decode ).asSymbol()-> addParam("data"); self->addClassMethod( base64_class, "decmb", &Falcon::core::Base64_decmb ).asSymbol()-> addParam("data"); //======================================================================= // Table class - tabular programming //======================================================================= Falcon::Symbol *table_class = self->addClass( "Table", &Falcon::core::Table_init ); table_class->setWKS(true); self->addClassMethod( table_class, "setHeader", &Falcon::core::Table_setHeader ).asSymbol()-> addParam("header"); self->addClassMethod( table_class, "getHeader", &Falcon::core::Table_getHeader ).asSymbol()-> addParam("id"); self->addClassMethod( table_class, "getColData", &Falcon::core::Table_getColData ).asSymbol()-> addParam("id"); self->addClassMethod( table_class, "order", &Falcon::core::Table_order ); self->addClassMethod( table_class, "len", &Falcon::core::Table_len ); self->addClassMethod( table_class, "front", &Falcon::core::Table_front ); self->addClassMethod( table_class, "back", &Falcon::core::Table_back ); self->addClassMethod( table_class, "first", &Falcon::core::Table_first ); self->addClassMethod( table_class, "last", &Falcon::core::Table_last ); self->addClassMethod( table_class, "get", &Falcon::core::Table_get ).asSymbol()-> addParam("row")->addParam("tcol"); self->addClassMethod( table_class, OVERRIDE_OP_GETINDEX, &Falcon::core::Table_get ).asSymbol()-> addParam("row"); self->addClassMethod( table_class, "set", &Falcon::core::Table_set ).asSymbol()-> addParam("row")->addParam("element"); self->addClassMethod( table_class, OVERRIDE_OP_SETINDEX, &Falcon::core::Table_set ).asSymbol()-> addParam("row")->addParam("element"); self->addClassMethod( table_class, "columnPos", &Falcon::core::Table_columnPos ).asSymbol()-> addParam("column"); self->addClassMethod( table_class, "columnData", &Falcon::core::Table_columnData ).asSymbol()-> addParam("column")->addParam("data"); self->addClassMethod( table_class, "find", &Falcon::core::Table_find ).asSymbol()-> addParam("column")->addParam("value")->addParam("tcol")->addParam("dflt"); self->addClassMethod( table_class, "insert", &Falcon::core::Table_insert ).asSymbol()-> addParam("row")->addParam("element"); self->addClassMethod( table_class, "remove", &Falcon::core::Table_remove ).asSymbol()-> addParam("row"); self->addClassMethod( table_class, "append", &Falcon::core::Table_append ).asSymbol()-> addParam("element"); self->addClassMethod( table_class, "setColumn", &Falcon::core::Table_setColumn ).asSymbol()-> addParam("column")->addParam("name")->addParam("coldata"); self->addClassMethod( table_class, "insertColumn", &Falcon::core::Table_insertColumn ).asSymbol()-> addParam("column")->addParam("name")->addParam("coldata")->addParam("dflt"); self->addClassMethod( table_class, "removeColumn", &Falcon::core::Table_removeColumn ).asSymbol()-> addParam("column"); self->addClassMethod( table_class, "choice", &Falcon::core::Table_choice ).asSymbol()-> addParam("func")->addParam("offer")->addParam("rows"); self->addClassMethod( table_class, "bidding", &Falcon::core::Table_bidding ).asSymbol()-> addParam("column")->addParam("offer")->addParam("rows"); self->addClassMethod( table_class, "resetColumn", &Falcon::core::Table_resetColumn ).asSymbol()-> addParam("column")->addParam("resetVal")->addParam("row")->addParam("value"); self->addClassMethod( table_class, "pageCount", &Falcon::core::Table_pageCount ); self->addClassMethod( table_class, "setPage", &Falcon::core::Table_setPage ).asSymbol()-> addParam("pageId"); self->addClassMethod( table_class, "curPage", &Falcon::core::Table_curPage ); self->addClassMethod( table_class, "insertPage", &Falcon::core::Table_insertPage ).asSymbol()-> addParam("pageId")->addParam("data"); self->addClassMethod( table_class, "removePage", &Falcon::core::Table_removePage ).asSymbol()-> addParam("pageId"); self->addClassMethod( table_class, "getPage", &Falcon::core::Table_getPage ).asSymbol()-> addParam("pageId"); return self; } } // Fake an engine-wide module, for external users. FALCON_MODULE_DECL { return Falcon::core_module_init(); } /* end of core_module.cpp */ engine/core_module/core_module.h000066400000000000000000001001051176363201700172710ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: core_module.h Header for Falcon Core Module ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 14 Aug 2008 00:37:13 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_CORE_MODULE_H #define FALCON_CORE_MODULE_H #include #include #include namespace Falcon { class CoreObject; class Error; /* Utility function to generate RTL oriented errors */ Error *rtlError( int t, const String &desc ); namespace core { FALCON_FUNC BOM_ptr( VMachine *vm ); FALCON_FUNC Integer_ptr( VMachine *vm ); FALCON_FUNC GarbagePointer_ptr( VMachine *vm ); FALCON_FUNC core_argv( VMachine *vm ); FALCON_FUNC core_argd( VMachine *vm ); FALCON_FUNC core_passvp( VMachine *vm ); // Methodic functions FALCON_FUNC mth_ToString ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_bound( ::Falcon::VMachine *vm ); FALCON_FUNC mth_len ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_typeId ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_compare ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_clone( ::Falcon::VMachine *vm ); FALCON_FUNC mth_serialize( ::Falcon::VMachine *vm ); FALCON_FUNC mth_isCallable ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_className ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_derivedFrom ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_baseClass ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_metaclass ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_getProperty( ::Falcon::VMachine *vm ); FALCON_FUNC mth_setProperty( ::Falcon::VMachine *vm ); FALCON_FUNC mth_properties( ::Falcon::VMachine *vm ); //FALCON_FUNC mth_hasProperty( ::Falcon::VMachine *vm ); FALCON_FUNC Function_name ( ::Falcon::VMachine *vm ); FALCON_FUNC Function_caller ( ::Falcon::VMachine *vm ); FALCON_FUNC Function_trace ( ::Falcon::VMachine *vm ); FALCON_FUNC Function_stack ( ::Falcon::VMachine *vm ); FALCON_FUNC Function_attributes( ::Falcon::VMachine *vm ); // Iterator class FALCON_FUNC Iterator_init( ::Falcon::VMachine *vm ); FALCON_FUNC Iterator_hasCurrent( ::Falcon::VMachine *vm ); FALCON_FUNC Iterator_hasNext( ::Falcon::VMachine *vm ); FALCON_FUNC Iterator_hasNext( ::Falcon::VMachine *vm ); FALCON_FUNC Iterator_hasPrev( ::Falcon::VMachine *vm ); FALCON_FUNC Iterator_next( ::Falcon::VMachine *vm ); FALCON_FUNC Iterator_prev( ::Falcon::VMachine *vm ); FALCON_FUNC Iterator_value( ::Falcon::VMachine *vm ); FALCON_FUNC Iterator_key( ::Falcon::VMachine *vm ); FALCON_FUNC Iterator_compare( ::Falcon::VMachine *vm ); FALCON_FUNC Iterator_clone( ::Falcon::VMachine *vm ); FALCON_FUNC Iterator_erase( ::Falcon::VMachine *vm ); FALCON_FUNC Iterator_find( ::Falcon::VMachine *vm ); FALCON_FUNC Iterator_insert( ::Falcon::VMachine *vm ); FALCON_FUNC LateBinding_value( ::Falcon::VMachine *vm ); FALCON_FUNC LateBinding_symbol( ::Falcon::VMachine *vm ); FALCON_FUNC LateBinding_bound( ::Falcon::VMachine *vm ); FALCON_FUNC LateBinding_bind( ::Falcon::VMachine *vm ); FALCON_FUNC LateBinding_unbind( ::Falcon::VMachine *vm ); FALCON_FUNC Error_init ( ::Falcon::VMachine *vm ); FALCON_FUNC Error_toString ( ::Falcon::VMachine *vm ); FALCON_FUNC Error_heading ( ::Falcon::VMachine *vm ); FALCON_FUNC Error_getSysErrDesc ( ::Falcon::VMachine *vm ); FALCON_FUNC SyntaxError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC GenericError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC CodeError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC IoError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC TypeError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC AccessError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC MathError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC ParamError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC ParseError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC CloneError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC IntrruptedError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC MessageError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC TableError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC val_int ( ::Falcon::VMachine *vm ); FALCON_FUNC val_numeric ( ::Falcon::VMachine *vm ); FALCON_FUNC attributes ( ::Falcon::VMachine *vm ); FALCON_FUNC chr ( ::Falcon::VMachine *vm ); FALCON_FUNC ord ( ::Falcon::VMachine *vm ); FALCON_FUNC paramCount ( ::Falcon::VMachine *vm ); FALCON_FUNC _parameter ( ::Falcon::VMachine *vm ); FALCON_FUNC paramIsRef ( ::Falcon::VMachine *vm ); FALCON_FUNC paramSet ( ::Falcon::VMachine *vm ); FALCON_FUNC yield ( ::Falcon::VMachine *vm ); FALCON_FUNC yieldOut ( ::Falcon::VMachine *vm ); FALCON_FUNC _f_sleep ( ::Falcon::VMachine *vm ); FALCON_FUNC beginCritical ( ::Falcon::VMachine *vm ); FALCON_FUNC endCritical ( ::Falcon::VMachine *vm ); FALCON_FUNC Semaphore_init ( ::Falcon::VMachine *vm ); FALCON_FUNC Semaphore_post ( ::Falcon::VMachine *vm ); FALCON_FUNC Semaphore_wait ( ::Falcon::VMachine *vm ); FALCON_FUNC Format_parse ( ::Falcon::VMachine *vm ); FALCON_FUNC Format_init ( ::Falcon::VMachine *vm ); FALCON_FUNC Format_format ( ::Falcon::VMachine *vm ); FALCON_FUNC Format_toString ( ::Falcon::VMachine *vm ); FALCON_FUNC broadcast( ::Falcon::VMachine *vm ); FALCON_FUNC subscribe( ::Falcon::VMachine *vm ); FALCON_FUNC unsubscribe( ::Falcon::VMachine *vm ); FALCON_FUNC getSlot( ::Falcon::VMachine *vm ); FALCON_FUNC consume( ::Falcon::VMachine *vm ); FALCON_FUNC assert( ::Falcon::VMachine *vm ); FALCON_FUNC retract( ::Falcon::VMachine *vm ); FALCON_FUNC getAssert( ::Falcon::VMachine *vm ); FALCON_FUNC VMSlot_init( ::Falcon::VMachine *vm ); FALCON_FUNC VMSlot_name( ::Falcon::VMachine *vm ); FALCON_FUNC VMSlot_prepend( ::Falcon::VMachine *vm ); FALCON_FUNC VMSlot_broadcast( ::Falcon::VMachine *vm ); FALCON_FUNC VMSlot_subscribe( ::Falcon::VMachine *vm ); FALCON_FUNC VMSlot_unsubscribe( ::Falcon::VMachine *vm ); FALCON_FUNC VMSlot_assert( ::Falcon::VMachine *vm ); FALCON_FUNC VMSlot_retract( ::Falcon::VMachine *vm ); FALCON_FUNC VMSlot_getAssert( ::Falcon::VMachine *vm ); FALCON_FUNC VMSlot_first( ::Falcon::VMachine *vm ); FALCON_FUNC VMSlot_last( ::Falcon::VMachine *vm ); FALCON_FUNC VMSlot_send( ::Falcon::VMachine *vm ); FALCON_FUNC VMSlot_register( ::Falcon::VMachine *vm ); FALCON_FUNC VMSlot_getEvent( ::Falcon::VMachine *vm ); FALCON_FUNC core_exit ( ::Falcon::VMachine *vm ); FALCON_FUNC PageDict( ::Falcon::VMachine *vm ); FALCON_FUNC Make_MemBuf( ::Falcon::VMachine *vm ); FALCON_FUNC Make_MemBufFromPtr( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_first( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_last( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_front( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_back( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_put( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_get( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_rewind( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_reset( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_flip( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_limit( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_mark( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_position( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_clear( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_fill( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_compact( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_remaining( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_wordSize( ::Falcon::VMachine *vm ); FALCON_FUNC MemoryBuffer_ptr( VMachine *vm ); FALCON_FUNC Method_source( ::Falcon::VMachine *vm ); FALCON_FUNC Method_base( ::Falcon::VMachine *vm ); FALCON_FUNC core_any ( ::Falcon::VMachine *vm ); FALCON_FUNC core_all ( ::Falcon::VMachine *vm ); FALCON_FUNC core_anyp ( ::Falcon::VMachine *vm ); FALCON_FUNC core_allp ( ::Falcon::VMachine *vm ); FALCON_FUNC core_eval ( ::Falcon::VMachine *vm ); FALCON_FUNC core_valof ( ::Falcon::VMachine *vm ); FALCON_FUNC core_min ( ::Falcon::VMachine *vm ); FALCON_FUNC core_max ( ::Falcon::VMachine *vm ); FALCON_FUNC core_map ( ::Falcon::VMachine *vm ); FALCON_FUNC core_dolist ( ::Falcon::VMachine *vm ); FALCON_FUNC core_times ( ::Falcon::VMachine *vm ); FALCON_FUNC core_upto ( ::Falcon::VMachine *vm ); FALCON_FUNC core_downto ( ::Falcon::VMachine *vm ); FALCON_FUNC core_xmap ( ::Falcon::VMachine *vm ); FALCON_FUNC core_filter ( ::Falcon::VMachine *vm ); FALCON_FUNC core_reduce ( ::Falcon::VMachine *vm ); FALCON_FUNC core_iff ( ::Falcon::VMachine *vm ); FALCON_FUNC core_choice ( ::Falcon::VMachine *vm ); FALCON_FUNC core_lit ( ::Falcon::VMachine *vm ); FALCON_FUNC core_cascade ( ::Falcon::VMachine *vm ); FALCON_FUNC core_floop ( ::Falcon::VMachine *vm ); FALCON_FUNC core_firstof ( ::Falcon::VMachine *vm ); FALCON_FUNC core_let ( ::Falcon::VMachine *vm ); FALCON_FUNC core_brigade ( ::Falcon::VMachine *vm ); FALCON_FUNC core_oob( ::Falcon::VMachine *vm ); FALCON_FUNC core_deoob( ::Falcon::VMachine *vm ); FALCON_FUNC core_isoob( ::Falcon::VMachine *vm ); FALCON_FUNC core_lbind( ::Falcon::VMachine *vm ); reflectionFuncDecl GC_usedMem_rfrom; reflectionFuncDecl GC_aliveMem_rfrom; reflectionFuncDecl GC_items_rfrom; reflectionFuncDecl GC_th_normal_rfrom; reflectionFuncDecl GC_th_normal_rto; reflectionFuncDecl GC_th_active_rfrom; reflectionFuncDecl GC_th_active_rto; CoreObject* GC_Factory( const CoreClass *cls, void *user_data, bool ); FALCON_FUNC GC_adjust( ::Falcon::VMachine *vm ); FALCON_FUNC GC_enable( ::Falcon::VMachine *vm ); FALCON_FUNC GC_perform( ::Falcon::VMachine *vm ); FALCON_FUNC gcEnable( ::Falcon::VMachine *vm ); FALCON_FUNC gcSetThreshold( ::Falcon::VMachine *vm ); FALCON_FUNC gcSetTimeout( ::Falcon::VMachine *vm ); FALCON_FUNC gcPerform( ::Falcon::VMachine *vm ); FALCON_FUNC gcGetParams( ::Falcon::VMachine *vm ); FALCON_FUNC vmVersionInfo( ::Falcon::VMachine *vm ); FALCON_FUNC vmModuleVersionInfo( ::Falcon::VMachine *vm ); FALCON_FUNC vmVersionName( ::Falcon::VMachine *vm ); FALCON_FUNC vmSystemType( ::Falcon::VMachine *vm ); FALCON_FUNC vmIsMain( ::Falcon::VMachine *vm ); FALCON_FUNC vmFalconPath( ::Falcon::VMachine *vm ); FALCON_FUNC vmSearchPath( ::Falcon::VMachine *vm ); FALCON_FUNC vmModuleName( ::Falcon::VMachine *vm ); FALCON_FUNC vmModuleLine( ::Falcon::VMachine *vm ); FALCON_FUNC vmModulePath( ::Falcon::VMachine *vm ); FALCON_FUNC vmRelativePath( ::Falcon::VMachine *vm ); FALCON_FUNC print ( ::Falcon::VMachine *vm ); FALCON_FUNC printl ( ::Falcon::VMachine *vm ); FALCON_FUNC inspect ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_describe ( ::Falcon::VMachine *vm ); FALCON_FUNC seconds ( ::Falcon::VMachine *vm ); FALCON_FUNC epoch ( ::Falcon::VMachine *vm ); FALCON_FUNC input ( ::Falcon::VMachine *vm ); FALCON_FUNC falcon_getenv( ::Falcon::VMachine *vm ); FALCON_FUNC falcon_setenv( ::Falcon::VMachine *vm ); FALCON_FUNC falcon_unsetenv( ::Falcon::VMachine *vm ); FALCON_FUNC falcon_getEnviron( ::Falcon::VMachine *vm ); FALCON_FUNC InputStream_creator ( ::Falcon::VMachine *vm ); FALCON_FUNC OutputStream_creator ( ::Falcon::VMachine *vm ); FALCON_FUNC IOStream_creator ( ::Falcon::VMachine *vm ); FALCON_FUNC systemErrorDescription ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_close ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_flush ( ::Falcon::VMachine *vm ); FALCON_FUNC StdStream_close ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_read ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_grab ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_readLine ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_grabLine ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_readText ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_grabText ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_write ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_writeText ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_setEncoding ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_clone ( ::Falcon::VMachine *vm ); FALCON_FUNC readURI ( ::Falcon::VMachine *vm ); FALCON_FUNC writeURI ( ::Falcon::VMachine *vm ); #define CR_TO_CR 0 #define CR_TO_CRLF 1 #define SYSTEM_DETECT -1 FALCON_FUNC Stream_seek ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_seekEnd ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_seekCur ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_tell ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_truncate ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_lastError ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_lastMoved ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_errorDescription ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_eof ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_isOpen ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_writeAvailable ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_readAvailable ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_getBuffering ( ::Falcon::VMachine *vm ); FALCON_FUNC Stream_setBuffering ( ::Falcon::VMachine *vm ); FALCON_FUNC _stdIn ( ::Falcon::VMachine *vm ); FALCON_FUNC _stdOut ( ::Falcon::VMachine *vm ); FALCON_FUNC _stdErr ( ::Falcon::VMachine *vm ); FALCON_FUNC stdInRaw ( ::Falcon::VMachine *vm ); FALCON_FUNC stdOutRaw ( ::Falcon::VMachine *vm ); FALCON_FUNC stdErrRaw ( ::Falcon::VMachine *vm ); FALCON_FUNC StringStream_init ( ::Falcon::VMachine *vm ); FALCON_FUNC StringStream_getString ( ::Falcon::VMachine *vm ); FALCON_FUNC StringStream_closeToString ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strFront ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strBack ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strStartsWith ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strEndsWith ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strFill ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strSplit ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strSplitTrimmed ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strMerge ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strFind ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strBackFind ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strTrim ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strFrontTrim ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strBackTrim ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strReplace ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strReplicate ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strEsq ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strUnesq ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strEscape ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strUnescape ( ::Falcon::VMachine *vm ); FALCON_FUNC strBuffer ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strUpper ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strLower ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strCmpIgnoreCase ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strWildcardMatch ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_strToMemBuf ( ::Falcon::VMachine *vm ); FALCON_FUNC strFromMemBuf ( ::Falcon::VMachine *vm ); //FALCON_FUNC String_first ( ::Falcon::VMachine *vm ); //FALCON_FUNC String_last ( ::Falcon::VMachine *vm ); FALCON_FUNC String_join ( ::Falcon::VMachine *vm ); FALCON_FUNC String_ptr( VMachine *vm ); FALCON_FUNC String_charSize( VMachine *vm ); FALCON_FUNC mth_arrayIns ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_arrayDel ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_arrayDelAll ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_arrayAdd ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_arrayResize ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_arrayFind ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_arrayScan ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_arraySort( ::Falcon::VMachine *vm ); FALCON_FUNC mth_arrayRemove( ::Falcon::VMachine *vm ); FALCON_FUNC mth_arrayMerge( ::Falcon::VMachine *vm ); FALCON_FUNC mth_arrayHead ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_arrayTail ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_arrayFill ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_arrayNM( ::Falcon::VMachine *vm ); FALCON_FUNC arrayBuffer ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_arrayCompact ( ::Falcon::VMachine *vm ); FALCON_FUNC mth_arrayCopy ( ::Falcon::VMachine *vm ); FALCON_FUNC Array_comp ( ::Falcon::VMachine *vm ); FALCON_FUNC Array_mcomp ( ::Falcon::VMachine *vm ); FALCON_FUNC Array_mfcomp ( ::Falcon::VMachine *vm ); FALCON_FUNC Array_front ( ::Falcon::VMachine *vm ); FALCON_FUNC Array_back ( ::Falcon::VMachine *vm ); FALCON_FUNC Array_table ( ::Falcon::VMachine *vm ); FALCON_FUNC Array_tabField ( ::Falcon::VMachine *vm ); FALCON_FUNC Array_tabRow ( ::Falcon::VMachine *vm ); FALCON_FUNC Array_first ( ::Falcon::VMachine *vm ); FALCON_FUNC Array_last ( ::Falcon::VMachine *vm ); FALCON_FUNC Array_concat ( ::Falcon::VMachine *vm ); FALCON_FUNC bless( ::Falcon::VMachine *vm ); FALCON_FUNC mth_dictMerge( ::Falcon::VMachine *vm ); FALCON_FUNC mth_dictKeys( ::Falcon::VMachine *vm ); FALCON_FUNC mth_dictValues( ::Falcon::VMachine *vm ); FALCON_FUNC mth_dictGet( ::Falcon::VMachine *vm ); FALCON_FUNC mth_dictSet( ::Falcon::VMachine *vm ); FALCON_FUNC mth_dictFind( ::Falcon::VMachine *vm ); FALCON_FUNC mth_dictBest( ::Falcon::VMachine *vm ); FALCON_FUNC mth_dictRemove( ::Falcon::VMachine *vm ); FALCON_FUNC mth_dictClear( ::Falcon::VMachine *vm ); FALCON_FUNC mth_dictFront( ::Falcon::VMachine *vm ); FALCON_FUNC mth_dictBack( ::Falcon::VMachine *vm ); FALCON_FUNC mth_dictFill ( ::Falcon::VMachine *vm ); FALCON_FUNC Dictionary_first( ::Falcon::VMachine *vm ); FALCON_FUNC Dictionary_last( ::Falcon::VMachine *vm ); FALCON_FUNC Dictionary_comp ( ::Falcon::VMachine *vm ); FALCON_FUNC Dictionary_mcomp ( ::Falcon::VMachine *vm ); FALCON_FUNC Dictionary_mfcomp ( ::Falcon::VMachine *vm ); FALCON_FUNC Dictionary_dop( ::Falcon::VMachine *vm ); FALCON_FUNC Dictionary_do( ::Falcon::VMachine *vm ); FALCON_FUNC fileType( ::Falcon::VMachine *vm ); FALCON_FUNC fileNameSplit ( ::Falcon::VMachine *vm ); FALCON_FUNC fileNameMerge ( ::Falcon::VMachine *vm ); FALCON_FUNC Directory_init ( ::Falcon::VMachine *vm ); FALCON_FUNC Directory_read ( ::Falcon::VMachine *vm ); FALCON_FUNC Directory_descend ( ::Falcon::VMachine *vm ); FALCON_FUNC Directory_close ( ::Falcon::VMachine *vm ); FALCON_FUNC Directory_error ( ::Falcon::VMachine *vm ); FALCON_FUNC dirChange ( ::Falcon::VMachine *vm ); FALCON_FUNC dirCurrent ( ::Falcon::VMachine *vm ); FALCON_FUNC dirMake ( ::Falcon::VMachine *vm ); FALCON_FUNC dirRemove ( ::Falcon::VMachine *vm ); FALCON_FUNC dirReadLink( ::Falcon::VMachine *vm ); FALCON_FUNC dirMakeLink( ::Falcon::VMachine *vm ); FALCON_FUNC fileMove ( ::Falcon::VMachine *vm ); FALCON_FUNC fileRemove ( ::Falcon::VMachine *vm ); FALCON_FUNC fileName ( ::Falcon::VMachine *vm ); FALCON_FUNC filePath ( ::Falcon::VMachine *vm ); FALCON_FUNC fileExt ( ::Falcon::VMachine *vm ); FALCON_FUNC fileUnit ( ::Falcon::VMachine *vm ); FALCON_FUNC fileChmod ( ::Falcon::VMachine *vm ); FALCON_FUNC fileChown ( ::Falcon::VMachine *vm ); FALCON_FUNC fileChgroup ( ::Falcon::VMachine *vm ); FALCON_FUNC fileCopy ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_Random_init( ::Falcon::VMachine *vm ); FALCON_FUNC flc_random ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_randomChoice ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_randomPick ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_randomWalk ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_randomDice ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_randomSeed ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_randomGrab ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_fract ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_fint ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_round ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_floor ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_ceil ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_abs ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_log ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_log10 ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_exp ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_sqrt ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_mod ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_pow ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_sin ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_cos ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_tan ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_asin ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_acos ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_atan ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_atan2 ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_deg2rad ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_rad2deg ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_factorial ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_permutations ( ::Falcon::VMachine *vm ); FALCON_FUNC flc_math_combinations ( ::Falcon::VMachine *vm ); FALCON_FUNC deserialize ( ::Falcon::VMachine *vm ); // Transcoder functions FALCON_FUNC transcodeTo ( ::Falcon::VMachine *vm ); FALCON_FUNC transcodeFrom ( ::Falcon::VMachine *vm ); FALCON_FUNC getSystemEncoding ( ::Falcon::VMachine *vm ); /* Timestamp class */ FALCON_FUNC TimeStamp_init ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeStamp_currentTime ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeStamp_dayOfYear ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeStamp_dayOfWeek ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeStamp_toString ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeStamp_add ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeStamp_distance ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeStamp_isValid ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeStamp_isLeapYear ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeStamp_toLongFormat ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeStamp_fromLongFormat ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeStamp_compare ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeStamp_toRFC2822 ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeStamp_fromRFC2822 ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeStamp_changeZone ( ::Falcon::VMachine *vm ); FALCON_FUNC CurrentTime ( ::Falcon::VMachine *vm ); FALCON_FUNC ParseRFC2822 ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeZone_getDisplacement ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeZone_describe ( ::Falcon::VMachine *vm ); FALCON_FUNC TimeZone_getLocal ( ::Falcon::VMachine *vm ); extern reflectionFuncDecl TimeStamp_timezone_rfrom; extern reflectionFuncDecl TimeStamp_timezone_rto; FALCON_FUNC Sequence_comp ( ::Falcon::VMachine *vm ); FALCON_FUNC Sequence_mcomp ( ::Falcon::VMachine *vm ); FALCON_FUNC Sequence_mfcomp ( ::Falcon::VMachine *vm ); FALCON_FUNC Sequence_front ( ::Falcon::VMachine *vm ); FALCON_FUNC Sequence_back ( ::Falcon::VMachine *vm ); FALCON_FUNC Sequence_first ( ::Falcon::VMachine *vm ); FALCON_FUNC Sequence_last ( ::Falcon::VMachine *vm ); FALCON_FUNC Sequence_empty( ::Falcon::VMachine *vm ); FALCON_FUNC Sequence_clear ( ::Falcon::VMachine *vm ); FALCON_FUNC Sequence_append ( ::Falcon::VMachine *vm ); FALCON_FUNC Sequence_prepend ( ::Falcon::VMachine *vm ); FALCON_FUNC Continuation_init ( ::Falcon::VMachine *vm ); FALCON_FUNC Continuation_call ( ::Falcon::VMachine *vm ); FALCON_FUNC Continuation_reset ( ::Falcon::VMachine *vm ); FALCON_FUNC Continuation_complete ( ::Falcon::VMachine *vm ); FALCON_FUNC Continuation__suspend ( ::Falcon::VMachine *vm ); FALCON_FUNC List_init ( ::Falcon::VMachine *vm ); FALCON_FUNC List_push ( ::Falcon::VMachine *vm ); FALCON_FUNC List_pop ( ::Falcon::VMachine *vm ); FALCON_FUNC List_pushFront ( ::Falcon::VMachine *vm ); FALCON_FUNC List_popFront ( ::Falcon::VMachine *vm ); FALCON_FUNC List_len( ::Falcon::VMachine *vm ); FALCON_FUNC Set_init( ::Falcon::VMachine *vm ); FALCON_FUNC Set_remove( ::Falcon::VMachine *vm ); FALCON_FUNC Set_insert( ::Falcon::VMachine *vm ); FALCON_FUNC Set_contains( ::Falcon::VMachine *vm ); FALCON_FUNC Set_find( ::Falcon::VMachine *vm ); FALCON_FUNC Set_len( ::Falcon::VMachine *vm ); FALCON_FUNC CmdlineParser_parse( ::Falcon::VMachine *vm ); FALCON_FUNC CmdlineParser_expectValue( ::Falcon::VMachine *vm ); FALCON_FUNC CmdlineParser_terminate( ::Falcon::VMachine *vm ); FALCON_FUNC CmdlineParser_usage( ::Falcon::VMachine *vm ); FALCON_FUNC FileStat_init ( ::Falcon::VMachine *vm ); FALCON_FUNC FileStat_read ( ::Falcon::VMachine *vm ); reflectionFuncDecl FileStats_type_rfrom; reflectionFuncDecl FileStats_mtime_rfrom; reflectionFuncDecl FileStats_ctime_rfrom; reflectionFuncDecl FileStats_atime_rfrom; FALCON_FUNC fal_include( ::Falcon::VMachine *vm ); FALCON_FUNC Object_attributes( ::Falcon::VMachine *vm ); FALCON_FUNC Object_setState( ::Falcon::VMachine *vm ); FALCON_FUNC Object_getState( ::Falcon::VMachine *vm ); FALCON_FUNC Object_apply( ::Falcon::VMachine *vm ); FALCON_FUNC Object_retrieve( ::Falcon::VMachine *vm ); FALCON_FUNC Method_attributes( ::Falcon::VMachine *vm ); FALCON_FUNC Class_attributes( ::Falcon::VMachine *vm ); FALCON_FUNC Object_comp( ::Falcon::VMachine *vm ); FALCON_FUNC Object_mcomp( ::Falcon::VMachine *vm ); FALCON_FUNC Object_mfcomp( ::Falcon::VMachine *vm ); /** Path class */ FALCON_FUNC Path_init ( ::Falcon::VMachine *vm ); reflectionFuncDecl Path_path_rfrom; reflectionFuncDecl Path_filename_rfrom; reflectionFuncDecl Path_unit_rfrom; reflectionFuncDecl Path_location_rfrom; reflectionFuncDecl Path_fullloc_rfrom; reflectionFuncDecl Path_file_rfrom; reflectionFuncDecl Path_extension_rfrom; reflectionFuncDecl Path_winpath_rfrom; reflectionFuncDecl Path_winloc_rfrom; reflectionFuncDecl Path_winfulloc_rfrom; reflectionFuncDecl Path_path_rto; reflectionFuncDecl Path_filename_rto; reflectionFuncDecl Path_unit_rto; reflectionFuncDecl Path_location_rto; reflectionFuncDecl Path_fullloc_rto; reflectionFuncDecl Path_file_rto; reflectionFuncDecl Path_extension_rto; CoreObject* PathObjectFactory( const CoreClass *cr, void *path, bool ); /** URI class */ FALCON_FUNC URI_init ( ::Falcon::VMachine *vm ); FALCON_FUNC URI_encode ( ::Falcon::VMachine *vm ); // static FALCON_FUNC URI_decode ( ::Falcon::VMachine *vm ); // static FALCON_FUNC URI_toString ( ::Falcon::VMachine *vm ); FALCON_FUNC URI_getFields ( ::Falcon::VMachine *vm ); FALCON_FUNC URI_setFields ( ::Falcon::VMachine *vm ); /** Table class */ FALCON_FUNC Table_init ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_setHeader ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_getHeader ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_getColData ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_order ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_len ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_front ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_back ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_first ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_last ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_get ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_set ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_columnPos ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_columnData ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_find ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_insert ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_remove ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_append ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_setColumn ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_insertColumn ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_removeColumn ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_choice ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_bidding ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_resetColumn ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_pageCount ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_setPage ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_curPage ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_insertPage ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_removePage ( ::Falcon::VMachine *vm ); FALCON_FUNC Table_getPage ( ::Falcon::VMachine *vm ); FALCON_FUNC Base64_encode ( ::Falcon::VMachine *vm ); FALCON_FUNC Base64_decode ( ::Falcon::VMachine *vm ); FALCON_FUNC Base64_decmb ( ::Falcon::VMachine *vm ); FALCON_FUNC Tokenizer_init ( ::Falcon::VMachine *vm ); FALCON_FUNC Tokenizer_parse ( ::Falcon::VMachine *vm ); FALCON_FUNC Tokenizer_rewind ( ::Falcon::VMachine *vm ); FALCON_FUNC Tokenizer_nextToken ( ::Falcon::VMachine *vm ); FALCON_FUNC Tokenizer_next ( ::Falcon::VMachine *vm ); FALCON_FUNC Tokenizer_token ( ::Falcon::VMachine *vm ); FALCON_FUNC Tokenizer_hasCurrent ( ::Falcon::VMachine *vm ); #define TOKENIZER_OPT_GRROUPSEP 1 #define TOKENIZER_OPT_BINDSEP 2 #define TOKENIZER_OPT_TRIM 4 #define TOKENIZER_OPT_RSEP 8 #define TOKENIZER_OPT_WSISTOK 16 class UriObject: public CoreObject { public: UriObject( const CoreClass *gen ): CoreObject( gen ) {} UriObject( const CoreClass *gen, const URI& uri ): CoreObject( gen ), m_uri( uri ) {} UriObject( const UriObject &other ); virtual ~UriObject(); virtual UriObject *clone() const; virtual bool setProperty( const String &prop, const Item &value ); virtual bool getProperty( const String &prop, Item &value ) const; const URI& uri() const { return m_uri; } URI& uri() { return m_uri; } static CoreObject* factory( const CoreClass *cr, void *uri, bool ); private: URI m_uri; }; /** Class used to manage the file stats. The FileStat reflection object provides three reflected timestamps in its object body. Although it is perfectly legal to: # Create an instance of TimeStamp falcon core object each time a reflected timestamp property is read; or # Derive the manager from FalconManager setting needCacheData() to return true, and then creating a TimeStamp reflected falcon object instance only the first time a TimeStamp property is asked for; or # Use the above method, but instead of reflecting the TimeStamp properties, setting them read only and creating the cached items at object creation. Those methods are sub-optimal, as they waste space. This object manager creates a class meant to store the fsdata and the cached TimeStamp instances that will be served on request. The cached TimeStamp items will hold a copy of Falcon::TimeStamp in them, so the FileStat instance can be destroyed and its TimeStamp will be destroyed with it. Notice that it would be possible to create object managers to use that instances directly, and keep alive their owner, but TimeStamp is too small to bother about this. */ class FileStatObject: public ReflectObject { public: class InnerData { public: InnerData() {} InnerData( const InnerData &other ); FileStat m_fsdata; Item m_cache_atime; Item m_cache_mtime; Item m_cache_ctime; }; FileStatObject( const CoreClass* generator ); FileStatObject( const FileStatObject &other ); virtual ~FileStatObject(); virtual void gcMark( uint32 mark ); virtual FileStatObject* clone() const; InnerData* getInnerData() const { return (InnerData*) m_user_data; } }; CoreObject* FileStatObjectFactory( const CoreClass *cls, void *user_data, bool bDeserializing ); CoreObject* PathObjectFactory( const CoreClass *me, void *uri, bool dyn ); /********************************************** * Carrier of complex number objects. */ class CoreComplex : public CoreObject { Complex m_complex; public: CoreComplex ( const CoreClass *cls ): CoreObject( cls ) { } CoreComplex ( const Complex &origin, const CoreClass* cls ): CoreObject( cls ), m_complex( origin ) {} CoreComplex ( const CoreComplex &other ): CoreObject( other ), m_complex( other.m_complex ) {} virtual ~CoreComplex (); virtual void gcMark( uint32 ) { } virtual CoreObject *clone( void ) const { return new CoreComplex( *this ); } virtual bool hasProperty( const String &key ) const; virtual bool setProperty( const String &key, const Item &ret ); virtual bool getProperty( const String &key, Item &ret ) const; const Complex &complex() const { return m_complex; } Complex &complex() { return m_complex; } }; CoreObject *Complex_Factory( const CoreClass *cls, void *, bool ); FALCON_FUNC Complex_init( ::Falcon::VMachine *vm ); FALCON_FUNC Complex_toString( ::Falcon::VMachine *vm ); FALCON_FUNC Complex_abs( ::Falcon::VMachine *vm ); FALCON_FUNC Complex_conj( ::Falcon::VMachine *vm ); FALCON_FUNC Complex_add__( ::Falcon::VMachine *vm ); FALCON_FUNC Complex_sub__( ::Falcon::VMachine *vm ); FALCON_FUNC Complex_mul__( ::Falcon::VMachine *vm ); FALCON_FUNC Complex_div__( ::Falcon::VMachine *vm ); FALCON_FUNC Complex_compare( ::Falcon::VMachine *vm ); }} #endif /* end of core_module.h */ engine/core_module/coroutine_ext.cpp000066400000000000000000000225571176363201700202340ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: coroutine_ext.cpp Coroutine support, sleep functions, kind request functions. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 14 Aug 2008 00:17:31 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "core_module.h" #include "../vmsema.h" #include /*# @beginmodule core */ namespace Falcon { namespace core { /*# @group coro_sup Coroutine support The functions in this group allows to interact with the coroutine support that is provided by the Virtual Machine. Most of them translate in requests to the virtual machine. Also, functions in this group are meant to permeate the embedding applications. Requests generate by coroutining are delivered to the application for approval and control. @begingroup coro_sup */ /*# @function yield @ingroup coroutine_support @brief gives up the rest of the coroutine time slice. This signals the VM that the current coroutine is temporarily done, and that another coroutine may be executed. If no other coroutines can be executed, current coroutine is resumed immediately (actually, it is never swapped out). */ FALCON_FUNC yield ( ::Falcon::VMachine *vm ) { vm->rotateContext(); } /*# @function yieldOut @brief Terminates current coroutine. @ingroup coroutine_support @param retval a return value for the coroutine. The current coroutine is terminated. If this is the last coroutine, the VM exits. Calling this function has the same effect of the END virtual machine PCODE. In multithreading context, exiting from the last coroutine causes a clean termination of the current thread. @see exit */ FALCON_FUNC yieldOut ( ::Falcon::VMachine *vm ) { Item *ret = vm->param(0); if ( ret != 0 ) vm->retval( *ret ); else vm->retnil(); vm->terminateCurrentContext(); } /*# @function sleep @brief Put the current coroutine at sleep for some time. @param time Time, in seconds and fractions, that the coroutine wishes to sleep. @return an item posted by the embedding application. @ingroup coroutine_support @raise InterruptedError in case of asynchronous interruption. This function declares that the current coroutines is not willing to proceed at least for the given time. The VM will swap out the coroutine until the time has elapsed, and will make it eligible to run again after the given time lapse. The parameter may be a floating point number if a pause shorter than a second is required. The @b sleep() function can be called also when the VM has not started any coroutine; this will make it to be idle for the required time. In embedding applications, the Virtual Machine can be instructed to detect needed idle time and return to the calling application instead of performing a system request to sleep. In this way, embedding applications can use the idle time of the virtual machine to perform background operations. Single threaded applications may continue their execution and schedule continuation of the Virtual Machine at a later time, and multi-threaded applications can perform background message processing. This function complies with the interrupt protocol. The Virtual Machine may be asynchronously interrupted from the outside (i.e. from another thread). In this case, @b sleep will immediately raise an @a InterruptedError instance. The script may just ignore this exception and let the VM to terminate immediately, or it may honor the request after a cleanup it provides in a @b catch block, or it may simply ignore the request and continue the execution by discarding the error through an appropriate @b catch block. @see interrupt_protocol */ FALCON_FUNC _f_sleep ( ::Falcon::VMachine *vm ) { Item *amount = vm->param(0); numeric pause; if( amount == 0 ) pause = 0.0; else { pause = amount->forceNumeric(); if ( pause < 0.0 ) pause = 0.0; } vm->yield( pause ); } /*# @function beginCritical @brief Signals the VM that this coroutine must not be interrupted. @ingroup coroutine_support After this call the VM will abstain from swapping this coroutine out of the execution context. The coroutine can then alter a set of data that must be prepare and readied for other coroutines, and then call @a endCritical or @a yield to pass the control back to the other coroutines. This function is not recursive. Successive calls to @b beginCritical are not counted, and have actually no effect. The first call to @a yield will swap out the coroutine, and the first call to @a endCritical will signal the availability of the routine to be swapped out, no matter how many times @a beginCritical has been called. */ FALCON_FUNC beginCritical ( ::Falcon::VMachine *vm ) { vm->allowYield( false ); } /*# @function endCritical @brief Signals the VM that this coroutine can be interrupted. @ingroup coroutine_support After this call, the coroutine may be swapped. This will happen only if/when the timeslice for this coroutine is over. This function is not recursive. Successive calls to @a beginCritical are not counted, and have actually no effect. The first call to @a yield will swap out the coroutine, and the first call to @a endCritical will signal the availability of the routine to be swapped out, no matter how many times @a beginCritical has been called. */ FALCON_FUNC endCritical ( ::Falcon::VMachine *vm ) { vm->allowYield( true ); } /*# @class Semaphore @brief Simple coroutine synchronization device. @ingroup coroutine_support @optparam initValue Initial value for the semaphore; if not given, 0 will be assumed. The semaphore is a simple synchronization object that is used by coroutines to communicate each others about relevant changes in the status of the application. Decrements the value of the semaphore, and eventually waits for the value to be > 0. When a @a Semaphore.wait method is called on a semaphore, two things may happen: if the value of the semaphore is greater than zero, the value is decremented and the coroutine can proceed. If it's zero, the coroutine is swapped out until the semaphore gets greater than zero again. When this happens, the coroutine decrements the value of the semaphore and proceeds. If a timeout parameter is given, in case the semaphore wasn't posted before the given timeout the function will return false. The order by which coroutines are resumed is the same by which they asked to wait on a semaphore. In this sense, @a Semaphore.wait method is implemented as a fair wait routine. The @a Semaphore.post method will raise the count of the semaphore by the given parameter (1 is the default if the parameter is not given). However, the calling coroutine won't necessarily be swapped out until a @a yield is called. By default, the semaphore is initialized to zero; this means that the first wait will block the waiting coroutine, unless a @a Semaphore.post is issued first. */ FALCON_FUNC Semaphore_init ( ::Falcon::VMachine *vm ) { Item *qty = vm->param(0); int32 value = 0; if ( qty != 0 ) { if ( qty->type() == FLC_ITEM_INT ) value = (int32) qty->asInteger(); else if ( qty->type() == FLC_ITEM_NUM ) value = (int32) qty->asNumeric(); else { throw new ParamError( ErrorParam( e_param_range ).extra( "( N )" ) ); } } VMSemaphore *sem = new VMSemaphore( value ); vm->self().asObject()->setUserData( sem ); } /*# @method post Semaphore @brief Increments the count of the semaphore. @optparam count The amount by which the semaphore will be incremented (1 by default). This method will increment the count of the semaphore by 1 or by a specified amount, allowing the same number of waiting coroutines to proceed. However, the calling coroutine won't necessarily be swapped out until a @a yield is called. */ FALCON_FUNC Semaphore_post ( ::Falcon::VMachine *vm ) { VMSemaphore *semaphore = dyncast< VMSemaphore *>(vm->self().asObject()->getFalconData()); Item *qty = vm->param(0); int32 value = 1; if ( qty != 0 ) { if ( qty->type() == FLC_ITEM_INT ) value = (int32)qty->asInteger(); else if ( qty->type() == FLC_ITEM_NUM ) value = (int32) qty->asNumeric(); else { throw new ParamError( ErrorParam( e_inv_params ).extra( "( N )" ) ); } if (value <= 0) value = 1; } semaphore->post( vm, value ); } /*# @method wait Semaphore @brief Waits on a semaphore. @optparam timeout Optional maximum wait in seconds. Decrements the value of the semaphore, and eventually waits for the value to be greater than zero. */ FALCON_FUNC Semaphore_wait ( ::Falcon::VMachine *vm ) { VMSemaphore *semaphore = dyncast< VMSemaphore *>(vm->self().asObject()->getFalconData()); Item *i_wc = vm->param( 0 ); if ( i_wc == 0 ) semaphore->wait( vm ); else { if ( ! i_wc->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params ).extra( "(N)" ) ); } semaphore->wait( vm, i_wc->forceNumeric() ); } } } } /* end of coroutine_ext.cpp */ engine/core_module/dict_ext.cpp000066400000000000000000000705041176363201700171430ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dict.cpp Dictionary api ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: gio mar 16 2006 ------------------------------------------------------------------- (C) Copyright 2006: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Dictionary api */ /*# @beginmodule core */ #include #include #include #include #include #include #include #include #include #include #include namespace Falcon { namespace core { static void process_dictFrontBackParams( VMachine *vm, CoreDict* &dict, bool &bKey, bool &bRemove ) { if ( vm->self().isMethodic() ) { dict = vm->self().asDict(); bRemove = vm->param(0) != 0 && vm->param(0)->isTrue(); bKey = vm->param(1) != 0 && vm->param(1)->isTrue(); } else { Item *i_dict = vm->param(0); if( i_dict == 0 || ! i_dict->isDict() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).extra( "S,[N,B,B]" ) ); } dict = i_dict->asDict(); bRemove = vm->param(1) != 0 && vm->param(1)->isTrue(); bKey = vm->param(2) != 0 && vm->param(2)->isTrue(); } } /**************************************** Support for dictionaries ****************************************/ /*# @method front Dictionary @brief Returns the first item in the dictionary. @optparam remove If true, remove the dictionary entry too. @optparam key If true, return the key instead of the value. @return The first value (or key) in the dictionary. @raise AccessError if the dictionary is empty */ /*# @method back Dictionary @brief Returns the last item in the dictionary. @optparam remove If true, remove the dictionary entry too. @optparam key If true, return the key instead of the value. @return The last value (or key) in the dictionary. @raise AccessError if the dictionary is empty. */ /*# @funset core_dict_funcs Dictionary support @brief Dictionary related functions. @beginset core_dict_funcs */ /*# @function dictFront @brief Returns the first item in the dictionary. @param dict The dictionary on which to operate. @optparam remove If true, remove the dictionary entry too. @optparam key If true, return the key instead of the value. @return The first value (or key) in the dictionary. @raise AccessError if the dictionary is empty */ FALCON_FUNC mth_dictFront( ::Falcon::VMachine *vm ) { CoreDict* dict; bool bKey; bool bRemove; process_dictFrontBackParams( vm, dict, bKey, bRemove ); Iterator iter( &dict->items() ); if ( bKey ) vm->retval( iter.getCurrentKey() ); else vm->retval( iter.getCurrent() ); if ( bRemove ) iter.erase(); } /*# @function dictBack @brief Returns the last item in the dictionary. @param dict The dictionary on which to operate. @optparam remove If true, remove the dictionary entry too. @optparam key If true, return the key instead of the value. @return The last value (or key) in the dictionary. @raise AccessError if the dictionary is empty */ FALCON_FUNC mth_dictBack( ::Falcon::VMachine *vm ) { CoreDict* dict; bool bKey; bool bRemove; process_dictFrontBackParams( vm, dict, bKey, bRemove ); Iterator iter( &dict->items(), true ); if ( bKey ) vm->retval( iter.getCurrentKey() ); else vm->retval( iter.getCurrent() ); if ( bRemove ) iter.erase(); } /*# @method first Dictionary @brief Returns an iterator to the head of this dictionary. @return An iterator. */ FALCON_FUNC Dictionary_first( VMachine *vm ) { Item *itclass = vm->findWKI( "Iterator" ); fassert( itclass != 0 ); CoreObject *iterator = itclass->asClass()->createInstance(); // we need to set the FalconData flag iterator->setUserData( new Iterator( &vm->self().asDict()->items() ) ); vm->retval( iterator ); } /*# @method last Dictionary @brief Returns an iterator to the head of this dictionary. @return An iterator. */ FALCON_FUNC Dictionary_last( VMachine *vm ) { Item *itclass = vm->findWKI( "Iterator" ); fassert( itclass != 0 ); CoreObject *iterator = itclass->asClass()->createInstance(); // we need to set the FalconData flag iterator->setUserData( new Iterator( &vm->self().asDict()->items(), true ) ); vm->retval( iterator ); } /*# @function bless @brief Blesses a dictionary, making it an OOP instance. @param dict A dictionary to be blessed. @optparam mode True (default) to bless the dictionary, false to unbless it. @return The same dictonary passed as @b dict. Blessed dictionaries become sensible to OOP operators: dot accessors and "provides" keyword behave as if the dictionary was an object instance, with its string entries being properties. */ FALCON_FUNC bless ( ::Falcon::VMachine *vm ) { Item *i_dict = vm->param(0); Item *i_mode = vm->param(1); if( i_dict == 0 || ! i_dict->isDict() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra( "D,[B]" ) ); } bool mode = i_mode == 0 ? true: i_mode->isTrue(); i_dict->asDict()->bless( mode ); vm->regA() = *i_dict; } /*# @function dictRemove @brief Removes a given key from the dictionary. @param dict A dictionary. @param key The key to be removed @return True if the key is found and removed, false otherwise. If the given key is found, it is removed from the dictionary, and the function returns true. If it's not found, it returns false. */ /*# @method remove Dictionary @brief Removes a given key from the dictionary. @param key The key to be removed @return True if the key is found and removed, false otherwise. If the given key is found, it is removed from the dictionary, and the function returns true. If it's not found, it returns false. */ FALCON_FUNC mth_dictRemove ( ::Falcon::VMachine *vm ) { Item *dict, *key; if( vm->self().isMethodic() ) { dict = &vm->self(); key = vm->param(0); } else { dict = vm->param(0); key = vm->param(1); } if( dict == 0 || ! dict->isDict() || key == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "X" : "D,X" ) ); } CoreDict *d = dict->asDict(); vm->regA().setBoolean( d->remove( *key ) ); } /*# @function dictClear @brief Removes all the items from a dictionary. @param dict The dictionary to be cleared. */ /*# @method clear Dictionary @brief Removes all the items from this dictionary. */ FALCON_FUNC mth_dictClear ( ::Falcon::VMachine *vm ) { Item *dict; if( vm->self().isMethodic() ) { dict = &vm->self(); } else { dict = vm->param(0); if( dict == 0 || ! dict->isDict() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "D" ) ); } } CoreDict *d = dict->asDict(); d->clear(); } /*# @function dictMerge @brief Merges two dictionaries. @param destDict The dictionary where the merge will take place. @param sourceDict A dictionary that will be inserted in destDict The function allows to merge two dictionaries. */ /*# @method merge Dictionary @brief Merges a dictionary into this one. @param sourceDict A dictionary that will be inserted in destDict */ FALCON_FUNC mth_dictMerge ( ::Falcon::VMachine *vm ) { Item *dict1, *dict2; if( vm->self().isMethodic() ) { dict1 = &vm->self(); dict2 = vm->param(0); } else { dict1 = vm->param(0); dict2 = vm->param(1); } if( dict1 == 0 || ! dict1->isDict() || dict2 == 0 || ! dict2->isDict() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "D" : "D,D" ) ); } CoreDict *d1 = dict1->asDict(); CoreDict *d2 = dict2->asDict(); d1->merge( *d2 ); } /*# @function dictKeys @brief Returns an array containing all the keys in the dictionary. @param dict A dictionary. @return An array containing all the keys. The returned keyArray contains all the keys in the dictionary. The values in the returned array are not necessarily sorted; however, they respect the internal dictionary ordering, which depends on a hashing criterion. If the dictionary is empty, then an empty array is returned. */ /*# @method keys Dictionary @brief Returns an array containing all the keys in this dictionary. @return An array containing all the keys. The returned keyArray contains all the keys in the dictionary. The values in the returned array are not necessarily sorted; however, they respect the internal dictionary ordering, which depends on a hashing criterion. If the dictionary is empty, then an empty array is returned. */ FALCON_FUNC mth_dictKeys( ::Falcon::VMachine *vm ) { Item *i_dict; if( vm->self().isMethodic() ) { i_dict = &vm->self(); } else { i_dict = vm->param(0); if( i_dict == 0 || ! i_dict->isDict() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "D" ) ); } } CoreDict *dict = i_dict->asDict(); CoreArray *array = new CoreArray; array->reserve( dict->length() ); Iterator iter( &dict->items() ); while( iter.hasCurrent() ) { array->append( iter.getCurrentKey() ); iter.next(); } vm->retval( array ); } /*# @function dictValues @brief Extracts all the values in the dictionary. @param dict A dictionary. @return An array containing all the values. The returned array contains all the value in the dictionary, in the same order by which they can be accessed traversing the dictionary. If the dictionary is empty, then an empty array is returned. */ /*# @method values Dictionary @brief Extracts all the values in this dictionary. @return An array containing all the values. The returned array contains all the value in the dictionary, in the same order by which they can be accessed traversing the dictionary. If the dictionary is empty, then an empty array is returned. */ FALCON_FUNC mth_dictValues( ::Falcon::VMachine *vm ) { Item *i_dict; if( vm->self().isMethodic() ) { i_dict = &vm->self(); } else { i_dict = vm->param(0); if( i_dict == 0 || ! i_dict->isDict() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "D" ) ); } } CoreDict *dict = i_dict->asDict(); CoreArray *array = new CoreArray; array->reserve( dict->length() ); Iterator iter( &dict->items() ); while( iter.hasCurrent() ) { array->append( iter.getCurrent() ); iter.next(); } vm->retval( array ); } /*# @function dictFill @brief Fills the dictionary values with the given item. @param dict The array where to add the new item. @param item The item to be replicated. @return The same @b dict passed as parameter. This method allows to clear all the values in this dictionary, resetting all the elements to a default value. */ /*# @method fill Dictionary @brief Fills the array with the given element. @param item The item to be replicated. @return This dictionary. This method allows to clear all the values in this dictionary, resetting all the elements to a default value. */ FALCON_FUNC mth_dictFill ( ::Falcon::VMachine *vm ) { Item *i_dict; Item *i_item; if ( vm->self().isMethodic() ) { i_dict = &vm->self(); i_item = vm->param(0); } else { i_dict = vm->param(0); i_item = vm->param(1); } if ( i_dict == 0 || ! i_dict->isDict() || i_item == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "X" : "D,X" ) ); } CoreDict *dict = i_dict->asDict(); Iterator iter( &dict->items() ); while( iter.hasCurrent() ) { if ( i_item->isString() ) iter.getCurrent() = new CoreString( *i_item->asString() ); else iter.getCurrent() = *i_item; iter.next(); } vm->retval( dict ); } /*# @function dictGet @brief Retreives a value associated with the given key @param dict A dictionary. @param key The key to be found. @return The value associated with a key, or an out-of-band nil if not found. Return the value associated with the key, if present, or one of the values if more than one key matching the given one is present. If not present, the value returned will be nil. Notice that nil may be also returned if the value associated with a given key is exactly nil. In case the key cannot be found, the returned value will be marked as OOB. @see oob */ /*# @method get Dictionary @brief Retrieves a value associated with the given key @param key The key to be found. @return The value associated with a key, or an out-of-band nil if not found. Return the value associated with the key, if present, or one of the values if more than one key matching the given one is present. If not present, the value returned will be nil. Notice that nil may be also returned if the value associated with a given key is exactly nil. In case the key cannot be found, the returned value will be marked as OOB. @note This method bypasses getIndex__ override in blessed (POOP) dictionaries. @see oob */ FALCON_FUNC mth_dictGet( ::Falcon::VMachine *vm ) { Item *i_dict, *i_key; if( vm->self().isMethodic() ) { i_dict = &vm->self(); i_key = vm->param(0); } else { i_dict = vm->param(0); i_key = vm->param(1); } if( i_dict == 0 || ! i_dict->isDict() || i_key == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "X" : "D,X" ) ); } CoreDict *dict = i_dict->asDict(); Item *value = dict->find( *i_key ); if ( value == 0 ) { vm->retnil(); vm->regA().setOob(); } else vm->retval( *value ); } /*# @function dictSet @brief Stores a value in a dictionary @param dict A dictionary. @param key The key to be found. @param value The key to be set. @return True if the value was overwritten, false if it has been inserted anew. @note This method bypasses setIndex__ override in blessed (POOP) dictionaries. @see oob */ /*# @function set Dictionary @brief Stores a value in a dictionary @param key The key to be found. @param value The key to be set. @return True if the value was overwritten, false if it has been inserted anew. @note This method bypasses setIndex__ override in blessed (POOP) dictionaries. @see oob */ FALCON_FUNC mth_dictSet( ::Falcon::VMachine *vm ) { Item *i_dict, *i_key, *i_value; if( vm->self().isMethodic() ) { i_dict = &vm->self(); i_key = vm->param(0); i_value = vm->param(1); } else { i_dict = vm->param(0); i_key = vm->param(1); i_value = vm->param(2); } if( i_dict == 0 || ! i_dict->isDict() || i_key == 0 || i_value == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "X,X" : "D,X,X" ) ); } CoreDict *dict = i_dict->asDict(); Item *value = dict->find( *i_key ); if ( value == 0 ) { vm->regA().setBoolean( false ); dict->put( *i_key, *i_value ); } else { vm->regA().setBoolean( true ); *value = *i_value; } } /*# @function dictFind @brief Returns an iterator set to a given key. @param dict The dictionary. @param key The key to be found. @return An iterator to the found item, or nil if not found. If the key is found in the dictionary, an iterator pointing to that key is returned. It is then possible to change the value of the found item, insert one item after or before the returned iterator or eventually delete the key. If the key is not found, the function returns nil. */ /*# @method find Dictionary @brief Returns an iterator set to a given key. @param key The key to be found. @return An iterator to the found item, or nil if not found. If the key is found in the dictionary, an iterator pointing to that key is returned. It is then possible to change the value of the found item, insert one item after or before the returned iterator or eventually delete the key. If the key is not found, the function returns nil. */ FALCON_FUNC mth_dictFind( ::Falcon::VMachine *vm ) { Item *i_dict, *i_key; if( vm->self().isMethodic() ) { i_dict = &vm->self(); i_key = vm->param(0); } else { i_dict = vm->param(0); i_key = vm->param(1); } if( i_dict == 0 || ! i_dict->isDict() || i_key == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "X" : "D,X" ) ); } CoreDict *dict = i_dict->asDict(); Iterator iter( &dict->items() ); if ( ! dict->findIterator( *i_key, iter ) ) vm->retnil(); else { // find the iterator class, we'll need it Item *i_iclass = vm->findWKI( "Iterator" ); fassert( i_iclass != 0 ); CoreObject *ival = i_iclass->asClass()->createInstance( new Iterator( iter ) ); ival->setProperty( "_origin", *i_dict ); vm->retval( ival ); } } /*# @function dictBest @brief Returns an iterator set to a given key, or finds the best position for its insertion. @param dict The dictionary. @param key The key to be found. @return An iterator to the best possible position. If the key is found in the dictionary, an iterator pointing to that key is returned. It is then possible to change the value of the found item, insert one item after or before the returned iterator or eventually delete the key. If the key is not found, an iterator pointing to the first key greater than the searched one is returned. The position is so that an insertion there would place the key in the right order. If the key is not found, the returned iterator is marked as out-of-band (see oob() at page 14). The method insert() of the Iterator class is optimized so that if the iterator is already in a valid position where to insert its key, the binary search is not performed again. Compare: @code d = [ "a" => 1, "c"=>2 ] // two searches if "b" notin d d["b"] = 0 else d["b"]++ end // one search iter = dictBest( dict, "b" ) isoob(iter) ? iter.insert( "b", 0 ) : iter.value( iter.value() + 1 ) @endcode In the first case, the insertion of a special value in a dictionary where the value is still not present has required a first search then a second one at insertion or modify. In the second case, the iterator can use the position information it has stored to avoid a second search. This function can also be used just to know what is the nearest key being present in the dictionary. The searched key is greater than the one that can be reached with Iterator.prev(), and less or equal than the one pointed. If Iterator.hasPrev() is false, then the searched key is smaller than any other in the collection, and if Iterator.hasCurrent() is false, then the key is greater than any other. */ /*# @method best Dictionary @brief Returns an iterator set to a given key, or finds the best position for its insertion. @param key The key to be found. @return An iterator to the best possible position. @see dictBest */ FALCON_FUNC mth_dictBest( ::Falcon::VMachine *vm ) { Item *i_dict, *i_key; if( vm->self().isMethodic() ) { i_dict = &vm->self(); i_key = vm->param(0); } else { i_dict = vm->param(0); i_key = vm->param(1); } if( i_dict == 0 || ! i_dict->isDict() || i_key == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "X" : "D,X" ) ); } // find the iterator class, we'll need it Item *i_iclass = vm->findWKI( "Iterator" ); fassert( i_iclass != 0 ); CoreDict *dict = i_dict->asDict(); Iterator* iter = new Iterator( &dict->items() ); CoreObject *ival = i_iclass->asClass()->createInstance( iter ); ival->setProperty( "_origin", *i_dict ); vm->regA() = ival; if ( ! dict->findIterator( *i_key, *iter ) ) { vm->regA().setOob(); } } /*# @method comp Dictionary @brief Appends elements to this dictionary through a filter. @param source A sequence, a range or a callable generating items. @optparam filter A filtering function receiving one item at a time. @return This dictionary. Please, see the description of @a Sequence.comp. When the target sequence (this item) is a dictionary, each element that is to be appended must be exactly an array with two items; the first will be used as a key, and the second as the relative value. For example: @code dict = [=>].comp( // the source .[ 'bananas' 'skip me' 'apples' 'oranges' '' 'melons' ], // the filter { element, dict => if " " in element: return oob(1) if "" == element: return oob(0) return [ "A" / len(dict), element ] // (1) } ) @endcode The element generated by the filter is a 2 element array, which is then stored in the dictionary as a pair of key-value items. @note In the example, the expression marked with (1) "A"/len(dict) causes the current number of elements in the dictionary to be added to the UNICODE value of the "A" letter; so the generated key will be "A" when the dictionary has zero elements, "B" when it has one element and so on. If the dictionary is blessed, then it is treated as an object, and instead of adding directly the pair of key/value items, it's @b append method is repeatedly called with the generated item as its parameter. In this case, the type and length of each element is not relevant. @see Sequence.comp */ FALCON_FUNC Dictionary_comp ( ::Falcon::VMachine *vm ) { if ( vm->param(0) == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "R|A|C|Sequence, [C]" ) ); } // Save the parameters as the stack may change greatly. CoreDict* dict = vm->self().asDict(); Item i_gen = *vm->param(0); Item i_check = vm->param(1) == 0 ? Item(): *vm->param(1); // if this is a blessed dictionary, we must use the append method. if ( dict->isBlessed() ) { // this will throw if dict has not "append" PoopSeq *seq = new PoopSeq( vm, dict ); vm->pushParam( new GarbagePointer( seq ) ); seq->comprehension_start( vm, dict, i_check ); } else { dict->items().comprehension_start( vm, dict, i_check ); } vm->pushParam( i_gen ); } /*# @method mcomp Dictionary @brief Appends elements to this dictionary through a filter. @param ... One or more sequences, ranges or callables generating items. @optparam filter A filtering function receiving one item at a time. @return This dictionary. Please, see the description of @a Sequence.comp, and the general @a Dictionary.comp for dictionary-specific notes. @see Sequence.mcomp */ FALCON_FUNC Dictionary_mcomp ( ::Falcon::VMachine *vm ) { // Save the parameters as the stack may change greatly. CoreDict* dict = vm->self().asDict(); StackFrame* current = vm->currentFrame(); // if this is a blessed dictionary, we must use the append method. if ( dict->isBlessed() ) { // this will throw if dict has not "append" PoopSeq *seq = new PoopSeq( vm, dict ); vm->pushParam( new GarbagePointer( seq ) ); seq->comprehension_start( vm, dict, Item() ); } else { dict->items().comprehension_start( vm, dict, Item() ); } for( uint32 i = 0; i < current->m_param_count; ++i ) { vm->pushParam( current->m_params[i] ); } } /*# @method mfcomp Dictionary @brief Appends elements to this dictionary through a filter. @param filter A filter function receiving each element before its insertion, or nil. @param ... One or more sequences, ranges or callables generating items. @return This dictionary. Please, see the description of @a Sequence.comp, and the general @a Dictionary.comp for dictionary-specific notes. @see Sequence.mfcomp */ FALCON_FUNC Dictionary_mfcomp ( ::Falcon::VMachine *vm ) { Item* i_func = vm->param(0); if ( i_func == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "C, ..." ) ); } // Save the parameters as the stack may change greatly. CoreDict* dict = vm->self().asDict(); StackFrame* current = vm->currentFrame(); Item i_check = *i_func; // if this is a blessed dictionary, we must use the append method. if ( dict->isBlessed() ) { // this will throw if dict has not "append" PoopSeq *seq = new PoopSeq( vm, dict ); vm->pushParam( new GarbagePointer( seq ) ); seq->comprehension_start( vm, dict, i_check ); } else { dict->items().comprehension_start( vm, dict, i_check ); } for( uint32 i = 1; i < current->m_param_count; ++i ) { vm->pushParam( current->m_params[i] ); } } static bool Dictionary_do_next( VMachine* vm ) { Iterator* iter = dyncast(vm->local(0)->asGCPointer()); bool advance = true; if( vm->regA().isOob() && vm->regA().isInteger() ) { int64 value = vm->regA().asInteger(); if ( value == 0 ) { // we're done Item* ip = vm->param(1); if ( ip ) vm->retval( *ip ); return false; } if( value == 1 ) { iter->erase(); advance = false; } } else { Item* ip = vm->param(1); if ( ip != 0 && ! ip->isNil() ) { ip->add( vm->regA(), *ip ); } } if( advance ) { iter->next(); } if( ! iter->hasCurrent() ) { // get the last return value and exit Item* ip = vm->param(1); if ( ip ) vm->retval( *ip ); return false; } vm->pushParam( iter->getCurrentKey() ); vm->pushParam( iter->getCurrent() ); vm->callFrame( *vm->param(0), 2 ); return true; } /*# @method do Dictionary @brief Repeats a function for each key/value pair in the dictionary. @param func The function to be repeated. @optparam acc Accumulator item. @return The accumulator item, if provided, or nil. The function is called repeatedly for each key/value pair in the dictionary, with the key passed as the first parameter and the value in the second. If an accumulator item is given in the @b acc parameter, each returned element is plainly added to the accumulator item with the standard add semantic (equivalent to "+" operator). Objects overriding the __add operator will receive the required callback. If the function returns an oob(0), the loop is broken, while if it returns an oob(1), the current item is dropped. Any other out of band parameter is ignored. For example: @code dict = [ "first" => "v1", "second" => "v2", "third" => "v3" ] dsum = dict.do( { k, v => if k == "second": return oob(1) return k + "=" + v }, [] ) > dsum.describe() @endcode */ FALCON_FUNC Dictionary_do ( ::Falcon::VMachine *vm ) { Item* i_func = vm->param(0); if ( i_func == 0 || ! i_func->isCallable() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "C, ..." ) ); } // Save the parameters as the stack may change greatly. CoreDict* dict = vm->self().asDict(); if ( dict->empty() ) { vm->retnil(); return; } Iterator* iter = new Iterator(&dict->items()); vm->addLocals(1); *vm->local(0) = new GarbagePointer(iter); vm->returnHandler( Dictionary_do_next ); // do the first call. vm->pushParam( iter->getCurrentKey() ); vm->pushParam( iter->getCurrent() ); vm->callFrame( *vm->param(0), 2 ); } } } /* end of dict.cpp */ engine/core_module/dir_ext.cpp000066400000000000000000001170001176363201700167670ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dir.cpp Directory management api. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom nov 7 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Directory management api */ #include #include #include #include #include #include #include #include #include #include #include "core_module.h" #include #include /*# @beginmodule core */ /*# @group core_dir_funcs Directory functions @brief Directory and file names functions. Directory functions are currently under development. The general principle is that they should be, where possible, multiplatform and portable, with some extensions working only on specific OSes. Also, the general idea on failure is that they should raise an error when success is expected, and return an error value when failure is likely. However, the behavior of the function listed in this section is subject to sudden change, as the contribution of the community (even if just in form of suggestion) is vital. @begingroup core_dir_funcs */ namespace Falcon { namespace core { FileStatObject::FileStatObject( const CoreClass *cls ): ReflectObject( cls, new InnerData ) { } FileStatObject::FileStatObject( const FileStatObject &other ): ReflectObject( other.generator(), new InnerData( *other.getInnerData() ) ) { } FileStatObject::~FileStatObject() { delete getInnerData(); } void FileStatObject::gcMark( uint32 mark ) { memPool->markItem( getInnerData()->m_cache_mtime ); memPool->markItem( getInnerData()->m_cache_atime ); memPool->markItem( getInnerData()->m_cache_ctime ); } FileStatObject* FileStatObject::clone() const { return new FileStatObject( *this ); } CoreObject* FileStatObjectFactory( const CoreClass *cls, void *, bool ) { return new FileStatObject( cls ); } FileStatObject::InnerData::InnerData( const InnerData &other ): m_fsdata( other.m_fsdata ) { fassert( m_cache_atime.asObjectSafe() ); other.m_cache_mtime.clone( m_cache_mtime ); other.m_cache_atime.clone( m_cache_atime ); other.m_cache_ctime.clone( m_cache_ctime ); } void FileStats_type_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { FileStatObject::InnerData *id = static_cast(user_data); property = (int64) id->m_fsdata.m_type; } void FileStats_ctime_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { FileStatObject::InnerData *id = static_cast(user_data); // is read only if ( id->m_fsdata.m_ctime != 0 ) { if ( id->m_cache_ctime.isNil() ) { VMachine* vm = VMachine::getCurrent(); fassert( vm != 0 ); Item *ts_class = vm->findWKI( "TimeStamp" ); //if we wrote the std module, can't be zero. fassert( ts_class != 0 ); id->m_cache_ctime = ts_class->asClass()->createInstance( new TimeStamp( *id->m_fsdata.m_ctime) ); } else { TimeStamp *ts = dyncast( id->m_cache_ctime.asObject()->getFalconData()); *ts = *id->m_fsdata.m_ctime; } } else { id->m_cache_ctime.setNil(); } property = id->m_cache_ctime; } void FileStats_mtime_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { FileStatObject::InnerData *id = static_cast< FileStatObject::InnerData *>(user_data); // is read only if ( id->m_fsdata.m_mtime != 0 ) { if ( id->m_cache_mtime.isNil() ) { VMachine* vm = VMachine::getCurrent(); fassert( vm != 0 ); Item *ts_class = vm->findWKI( "TimeStamp" ); //if we wrote the std module, can't be zero. fassert( ts_class != 0 ); id->m_cache_mtime = ts_class->asClass()->createInstance( new TimeStamp( *id->m_fsdata.m_mtime) ); } else { TimeStamp *ts = dyncast( id->m_cache_mtime.asObject()->getFalconData()); *ts = *id->m_fsdata.m_mtime; } } else { id->m_cache_mtime.setNil(); } property = id->m_cache_mtime; } void FileStats_atime_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { FileStatObject::InnerData *id = static_cast(user_data); // is read only if ( id->m_fsdata.m_atime != 0 ) { if ( id->m_cache_atime.isNil() ) { VMachine* vm = VMachine::getCurrent(); fassert( vm != 0 ); Item *ts_class = vm->findWKI( "TimeStamp" ); //if we wrote the std module, can't be zero. fassert( ts_class != 0 ); id->m_cache_atime = ts_class->asClass()->createInstance( new TimeStamp( *id->m_fsdata.m_atime) ); } else { TimeStamp *ts = dyncast( id->m_cache_atime.asObject()->getFalconData()); *ts = *id->m_fsdata.m_atime; } } else { id->m_cache_atime.setNil(); } property = id->m_cache_atime; } /*# @class FileStat @optparam path If given, the filestats will be initialized with stats of the given file. @raise IoError if @b path is given but not found. @brief Class holding informations on system files. The FileStat class holds informations on a single directory entry. It is possible to pass a @b path parameter, in which case, if the given file is found, the contents of this class is filled with the stat data from the required file, otherwise an IoError is raised. The @a FileStat.read method would search for the required file without raising in case it is not found, so if it preferable not to raise on failure (i.e. because searching the most fitting of a list of possibly existing files), it is possiblo to create the FileStat object without parameters and the use the @b read method iteratively. @prop access POSIX access mode @prop atime Last access time, expressed as a @a TimeStamp instance. @prop attribs DOS Attributes @prop ctime Creation time or last attribute change time, expressed as a @a TimeStamp instance. @prop group Group ID of the given file. @prop mtime Last modify time, expressed as a @a TimeStamp instance. @prop owner Owner ID of the given file. @prop size File size. Both access and attribs properties are given a value respectively only on POSIX or MS-Windows systems; their value is the underlying numeric value the system provides. The ctime property has a different meaning in MS-Windows and POSIX system. In the former, is the time at which the file has been created; in the latter is the time when the file ownership flags have been last changed, which may or may not be the same as file creation time. Times are returned as a @a TimeStamp class instance; the time is always expressed as local system time. */ /*# @property ftype FileStat @brief Type of the file. Can be one of the following constants (declared in this class): - NORMAL - DIR - PIPE - LINK - DEVICE - SOCKET - UNKNOWN */ FALCON_FUNC FileStat_init( ::Falcon::VMachine *vm ) { // we're initialized with consistent data from the class factory function. if ( vm->paramCount() > 0 ) { // would throw on param error... FileStat_read( vm ); // we must raise from the constructor, if we didn't found the file. if( ! vm->regA().isTrue() ) { throw new IoError( ErrorParam( e_nofile, __LINE__ ) .origin( e_orig_runtime ) .extra( *vm->param(0)->asString() ) ); } } } /*# @method read FileStat @brief Fills the data in this instance reading them from a system file. @param filename Relative or absolute path to a file for which stats must be read @return True on success, false if the file cannot be queried. Fills the contents of this object with informations on the given file. If the stats of the required file can be read, the function returns true. */ FALCON_FUNC FileStat_read ( ::Falcon::VMachine *vm ) { Item *name = vm->param(0); if ( name == 0 || ! name->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra("S") ); } FileStatObject *self = dyncast(vm->self().asObject()); FileStatObject::InnerData *id = self->getInnerData(); vm->regA().setBoolean( Sys::fal_stats( *name->asString(), id->m_fsdata ) ); } /*# @function fileType @brief Determines the type of a file. @param filename Relative or absolute path to a file. @optparam nf Don't follow symlinks @return A valid file type or FileStat.NOTFOUND if not found. This function is useful to know what of what kind of system entry is a certain file, or if it exists at all, without paying the overhead for a full FileStat object being instantiated. Returned values may be: - FileStat.NORMAL - FileStat.DIR - FileStat.PIPE - FileStat.LINK - Only if @b nf parameter is true. - FileStat.DEVICE - FileStat.SOCKET - FileStat.UNKNOWN or FileStat.NOTFOUND if the file doesn't exist. */ FALCON_FUNC fileType( ::Falcon::VMachine *vm ) { Item *name = vm->param(0); Item *i_df = vm->param(1); if ( name == 0 || ! name->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S" ) ); return; } FileStat::e_fileType type = FileStat::t_notFound; bool follow = !( i_df != 0 && i_df->isTrue() ); int count = 99; String sName = *name->asString(); while( true ) { Sys::fal_fileType( sName, type ); if ( follow && type == FileStat::t_link ) { String temp; if ( ! Sys::fal_readlink( sName, temp ) || --count == 0 ) { type = FileStat::t_notFound; break; } sName = temp; } else { break; } } // will already be -1 if not found vm->retval( type ); } /*# @function dirReadLink @brief On systems supporting symbolic links, returns the linked file. @param linkPath A path to a symbolic link. If the target file in the linkPath parameter is a symbolic link and can be read, the return value will be a string containing the file the link is pointing to. Otherwise, the function will return nil. Function will return nil also on system where symbolic links are not supported. */ FALCON_FUNC dirReadLink( ::Falcon::VMachine *vm ) { Item *name = vm->param(0); if ( name == 0 || ! name->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) ); return; } String temp; if ( ! Sys::fal_readlink( *name->asString(), temp ) ) { vm->retnil(); } else { CoreString *ret = new CoreString; ret->bufferize( temp ); vm->retval( ret ); } } /*# @function dirMakeLink @brief Creates a soft link to a file. @param source The original file path. @param dest The path to the link file. The path of both source and dest parameter is to be expressed in Falcon convention (forward slashes), and can be both relative to the working directory or absolute. Currently, the function works only on UNIX systems; Windows platforms have recently added this functionality, but they are still not supported by this function at the moment. On success, the function returns true. On failure, it returns false. In case the function is not supported, it just returns false. (Comments are welcome). */ FALCON_FUNC dirMakeLink( ::Falcon::VMachine *vm ) { Item *name = vm->param(0); Item *dest = vm->param(1); if ( name == 0 || ! name->isString() || dest == 0 || ! dest->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) ); } if ( ! Sys::fal_readlink( *name->asString(), *dest->asString() ) ) { vm->regA().setBoolean( false ); } else { vm->regA().setBoolean( true ); } } /* @function fileNameSplit @brief Splits a filename in four elements. @param path A string containing a path. @return An array of four elements containing the split string. This function analyzes the given filename and separates it in disk/server specification, path, file name and extension, returning them in a 4 string element array. If one of the elements is not present in the filename, the corresponding location is set to nil. The extension dot, the disk/server specification colon and the last slash of the path are removed from the returned strings. @note This function is an internal shortcut to the @a Path class. */ FALCON_FUNC fileNameSplit ( ::Falcon::VMachine *vm ) { // a filename is always in this format: // [DISK_SPEC or SERVER:][/path/to/file/]file.part[.ext] // returns an array of 4 elements. ALWAYS. Item *name = vm->param(0); if ( name == 0 || ! name->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } Path path( *name->asString() ); if ( ! path.isValid() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra( vm->moduleString( rtl_invalid_path ) ) ); } CoreArray *parts = new CoreArray( 4 ); parts->length( 4 ); CoreString *res = new CoreString; CoreString *loc = new CoreString; CoreString *file = new CoreString; CoreString *ext = new CoreString; path.split( *res, *loc, *file, *ext ); parts->at(0) = res; parts->at(1) = loc; parts->at(2) = file; parts->at(3) = ext; vm->retval( parts ); } /*# @function fileNameMerge @brief Merges a filename split up in four elements. @param spec The disk or server specification, an array containing all the elements of the file path, or nil. @param path Path to the file, or nil. @param filename Filename, or nil. @param ext extension, or nil. @return A complete absolute path. The final path is composed by adding a colon after the disk/server specification, a slash after the path and a dot before the extension. It is also possible to pass all the four elements in an array, in place of the @b spec parameter. @note This function is an internal shortcut to the @a Path class. */ FALCON_FUNC fileNameMerge ( ::Falcon::VMachine *vm ) { const String *unitspec = 0; const String *fname = 0; const String *fpath = 0; const String *fext = 0; String sDummy; Item *p0 = vm->param(0); if ( p0 != 0 && p0->isArray() ) { const CoreArray &array = *p0->asArray(); if( array.length() >= 0 && array[0].isString() ) { unitspec = array[0].asString(); } else unitspec = &sDummy; if( array.length() >= 1 && array[1].isString() ) { fpath = array[1].asString(); } else fpath = &sDummy; if( array.length() >= 2 && array[2].isString() ) { fname = array[2].asString(); } else fname = &sDummy; if( array.length() >= 3 && array[3].isString() ) { fext = array[3].asString(); } else fext = &sDummy; } else { Item *p1 = vm->param(1); Item *p2 = vm->param(2); Item *p3 = vm->param(3); unitspec = p0 != 0 && p0->isString() ? p0->asString() : &sDummy; fpath = p1 != 0 && p1->isString() ? p1->asString() : &sDummy; fname = p2 != 0 && p2->isString() ? p2->asString() : &sDummy; fext = p3 != 0 && p3->isString() ? p3->asString() : &sDummy; } Path p; p.join( *unitspec, *fpath, *fname, *fext ); if ( ! p.isValid() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra( vm->moduleString( rtl_invalid_path ) ) ); } vm->retval( new CoreString( p.get() ) ); } /*# @function fileName @brief Determines the name of a file in a complete path. @param path A string containing a path. @return The filename part in the path. The function determines the filename part of a complete path name. The returned filename includes the extension, if present. The filename does not need to represent a file actually existing in the system. If the filename part cannot be determined, an empty string is returned. */ FALCON_FUNC fileName ( ::Falcon::VMachine *vm ) { Item *filename = vm->param(0); if ( filename == 0 || ! filename->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } String *name = filename->asString(); int32 len = name->length()-1; int32 pos = len; while( pos >= 0 ) { if ( name->getCharAt( pos ) == '/' ) { vm->retval( new CoreString( *name, pos + 1 ) ); return; } pos--; } // shallow copy vm->retval( *filename ); } /*# @function filePath @brief Return the path specification part in a complete filename. @param fullpath A string containing a path. @return The path part. The function determines the filename part of a complete path name. The returned filename includes the host or disk specification, if present. The filename does not need to represent a file actually existing in the system. @note The filename should be in Falcon format (URI convention with forward slashes). @see Path */ FALCON_FUNC filePath ( ::Falcon::VMachine *vm ) { Item *filename = vm->param(0); if ( filename == 0 || ! filename->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } String *name = filename->asString(); int32 len = name->length(); int32 pos = len-1; while( pos > 0 ) { if ( name->getCharAt( pos ) == '/' ) { vm->retval( new CoreString( *name, 0, pos ) ); return; } pos--; } if ( name->getCharAt( pos ) == '/' ) vm->retval( new CoreString( "/" ) ); else vm->retval( new CoreString ); } /*# @function fileExt @brief Return the extension in a complete filename. @param fullpath A string containing a path. @return The extension part. The function determines the element past a dot after the filename part. Filenames can start with a "."; they are not considered extensions. @note The filename should be in Falcon format (URI convention with forward slashes). @see Path */ FALCON_FUNC fileExt ( ::Falcon::VMachine *vm ) { Item *filename = vm->param(0); if ( filename == 0 || ! filename->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } String *name = filename->asString(); uint32 pos = name->rfind( "/" ); uint32 len = name->length(); if ( pos + 2 < len ) { // there may be an extension. ++pos; // discard initial "." while( pos < len && name->getCharAt( pos ) != '.' ) { ++pos; } // find a dot here. uint32 pdot = name->rfind( ".", pos ); if ( pdot + 1 < len ) { vm->retval( new CoreString( *name, pdot+1 ) ); return; } } // else return "" vm->retval( new CoreString ); } /*# @function fileUnit @brief Return the unit specificator in a complete filename. @param fullpath A string containing a path. @return The unit specificator part. Returns the unit specification in a complete path. @see Path */ FALCON_FUNC fileUnit ( ::Falcon::VMachine *vm ) { Item *filename = vm->param(0); if ( filename == 0 || ! filename->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } String *name = filename->asString(); uint32 pos = name->find( ":" ); if ( pos != String::npos && pos > 0 ) { uint32 start = 0; while( start < pos && name->getCharAt(start) == '/' ) { ++start; } vm->retval( new CoreString( *name, start, pos ) ); return; } // else return "" vm->retval( new CoreString ); } /*# @class Directory @brief Special iterator to access directory listings. @param dirname A relative or absolute path to a directory @raise IoError on failure. The Directory class allow to iterate through the contents of a local file directory. The caller should repeatedly call the read() method until nil is returned. In case an error is raised, the error() method may be called to get informations on the cause that raised the error. After the read is complete, the caller should call close() to free the resources associated with the object. The garbage collector will eventually take care of it, but it is better to close the object as soon as possible. */ FALCON_FUNC Directory_init ( ::Falcon::VMachine *vm ) { Item *name = vm->param(0); if ( name == 0 || ! name->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S" ) ); return; } int32 fsError; DirEntry *dir = Sys::fal_openDir( *name->asString(), fsError ); if( dir != 0 ) { CoreObject *self = vm->self().asObjectSafe(); self->setUserData( dir ); } else { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .origin( e_orig_runtime ) .extra( *name->asString() ) .sysError( (uint32) Sys::_lastError() ) ); } } /*# @method read Directory @brief Returns the next entry in the directory. @return A string representing the next entry, or oob(0) when no new entries are left. The usage is @code dir = Directory( "." ) while entry = dir.read(): > entry dir.close() @endcode or @code dir = Directory( "." ) for entry in dir.read: > entry dir.close() @endcode */ FALCON_FUNC Directory_read ( ::Falcon::VMachine *vm ) { DirEntry *dir = dyncast(vm->self().asObject()->getFalconData()); String reply; if ( dir->read( reply ) ) { CoreString *ret = new CoreString; ret->bufferize( reply ); vm->retval( ret ); } else { if ( dir->lastError() != 0 ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .origin( e_orig_runtime ) .sysError( (uint32) Sys::_lastError() ) ); } vm->retval( (int64)0 ); vm->regA().setOob( true ); } } /*# @method close Directory @brief Closes the directory object. This method should be called when the item is not needed anymore to free system resources. However, the directory listing is closed at garbage collecting. */ FALCON_FUNC Directory_close ( ::Falcon::VMachine *vm ) { DirEntry *dir = dyncast(vm->self().asObject()->getFalconData()); dir->close(); if ( dir->lastError() != 0 ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .origin( e_orig_runtime ) .sysError( (uint32) Sys::_lastError() ) ); } } static bool Directory_descend_next_descend ( ::Falcon::VMachine *vm ) { if( vm->regA().isOob() && vm->regA().isInteger() ) { // in case of 0 or 1 we should break. if ( vm->regA().asInteger() == 0 || vm->regA().asInteger() == 1 ) return false; } // we're in a directory. descend. int32 fsError; DirEntry *dir = Sys::fal_openDir( *vm->param(0)->asString(), fsError ); vm->regB().setNil(); if( dir != 0 ) { Item* i_dir = vm->findWKI( "Directory" ); fassert( i_dir != 0 && i_dir->isClass() ); // we don't want to be called anymore. // when this frame returns, resume previous frame. vm->returnHandler( 0 ); CoreClass* dircls = i_dir->asClass(); CoreObject *self = dircls->createInstance( dir ); vm->param(1)->asArray()->append( self ); // and be sure that the VM will execute this last time return false; } throw new IoError( ErrorParam( e_io_error, __LINE__ ) .origin( e_orig_runtime ) .extra( *vm->param(0)->asString() ) .sysError( (uint32) Sys::_lastError() ) ); } static bool Directory_descend_next ( ::Falcon::VMachine *vm ) { // get the real self CoreArray* pushed = vm->local(0)->asArray(); fassert( pushed->length() > 0 ); Item& self = pushed->at( pushed->length() -1 ); DirEntry *dir = dyncast(self.asObjectSafe()->getFalconData()); if( vm->regA().isOob() && vm->regA().isInteger() && vm->regA().asInteger() == 0 ) { // we're out of here. return false; } String fnext; // skip this and parent dir while( fnext == "" || fnext == "." || fnext == ".." ) { if( ! dir->read( fnext ) ) { // pop this level. dir->close(); pushed->length( pushed->length()-1); // please, repeat us if this was not the last level return pushed->length() != 0; } } // is this a directory? if ( dir->path().size() != 0 ) fnext.prepend( dir->path() + "/" ); FileStat fs; Sys::fal_stats( fnext, fs ); if( fs.m_type == FileStat::t_dir ) { // yes? -- prepare the new callback frame if ( vm->param(0) != 0 && ! vm->param(0)->isNil() ) { vm->pushParameter( (new CoreString( fnext ))->bufferize() ); vm->pushParameter( pushed ); vm->callFrame( *vm->param(0), 2 ); // prepare the descent vm->returnHandler( &Directory_descend_next_descend ); } else { // we're in a directory. descend. int32 fsError; DirEntry *dir = Sys::fal_openDir( fnext, fsError ); vm->regB().setNil(); if( dir != 0 ) { Item* i_dir = vm->findWKI( "Directory" ); fassert( i_dir != 0 && i_dir->isClass() ); CoreClass* dircls = i_dir->asClass(); CoreObject *self = dircls->createInstance( dir ); pushed->append( self ); return true; } throw new IoError( ErrorParam( e_io_error, __LINE__ ) .origin( e_orig_runtime ) .extra( *vm->param(0)->asString() ) .sysError( (uint32) Sys::_lastError() ) ); } } else { // should we call the file handler? Item* i_ffunc = vm->param(1); if ( i_ffunc != 0 && ! i_ffunc->isNil() ) { vm->pushParameter( (new CoreString( fnext ))->bufferize() ); vm->callFrame( *i_ffunc, 1 ); // no need for extra params } } return true; } /*# @method descend Directory @brief Descends into subdirectories, iteratively calling a function. @optparam dfunc Function to be called upon directories. @optparam ffunc Function to be called upon files. This function calls iteratively a function on directory entries. If an entry is detected to be a directory, it is passed to @b dfunc as the only parameter. If @b ffunc is also provided, then it will receive all the non-directory entries. Entries corresponding to the current directory and the parent directory will never be sent to the handler functions. @note The parameters for @b dfunc and @b ffunc will always be relative to the directory on which this object has been created. Returning an out of band 0, any of the callbacks involved may stop the processing and return immediately. An out of band 1 will skip the currently processed item and proceed. The @b dfunc handler is called before descending into the found subdirectory; this gives the handlers the chance to skip directories or interrupt the search. @note After a complete descend, this directory will be closed and won't be usable anymore. */ FALCON_FUNC Directory_descend ( ::Falcon::VMachine *vm ) { Item *i_dfunc = vm->param(0); Item *i_ffunc = vm->param(1); if ( ( i_dfunc != 0 && ! i_dfunc->isCallable() && ! i_dfunc->isNil() ) || ( i_ffunc != 0 && ! i_ffunc->isCallable() && ! i_dfunc->isNil() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "[C],[C]" ) ); return; } // nothing to do? if( (i_dfunc == 0 || i_dfunc->isNil() ) && (i_ffunc == 0 || i_ffunc->isNil() )) { // should we signal an error? --- I don't think return; } vm->addLocals(1); *vm->local(0) = new CoreArray(1); vm->local(0)->asArray()->append( vm->self() ); // be sure we won't loop out vm->regA().setNil(); vm->returnHandler( &Directory_descend_next ); } /*# @method error Directory @brief Returns the last system error code that the directory operation causes. @return A system error code. The error code may be rendered into a string using the @a systemErrorDescription function. */ FALCON_FUNC Directory_error( ::Falcon::VMachine *vm ) { DirEntry *dir = dyncast(vm->self().asObject()->getFalconData()); vm->retval( (int)dir->lastError() ); } /*# @function dirMake @brief Creates a directory. @param dirname The name of the directory to be created. @optparam bFull Create also the full pat to the given directory. @raise IoError on system error. On success, this function creates the given directory with normal attributes. It is possible to specify both a relative or absolute path; both the relative and absolute path can contain a subtree specification, that is, a set of directories separated by forward slashes, which lead to the directory that should be created. For example: @code dirMake( "top/middle/bottom" ) @endcode instructs @b dirMake to create the directory bottom in a subdirectory "middle", which should already exist. Passing @b true as second parameter, dirMake will also try to create directories leading to the final destination, if they are missing. */ FALCON_FUNC dirMake ( ::Falcon::VMachine *vm ) { Item *name = vm->param(0); if ( name == 0 || ! name->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } const String &strName = *name->asString(); bool descend = vm->param(1) == 0 ? false : vm->param(1)->isTrue(); int32 fsError = 0; if ( ! Sys::fal_mkdir( strName, fsError, descend ) ) { throw new IoError( ErrorParam( 1011, __LINE__ ). origin( e_orig_runtime ).desc( "Cannot create directory" ).extra( strName ). sysError( (uint32) Sys::_lastError() ) ); } } /*# @function fileCopy @ingroup core_syssupport @param source Source file to be copied @param dest Destination file. @brief Copies a whole file from one position to another. @raise IoError on system error. This function performs a file copy. The function is still experimental and needs addition of VM interruption protocol compliancy, as well as the possibility to preserve or change the system attributes in the target copy. */ FALCON_FUNC fileCopy ( ::Falcon::VMachine *vm ) { Item *filename = vm->param(0); Item *filedest = vm->param(1); if ( filename == 0 || ! filename->isString() || filedest == 0 || ! filedest->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ). extra("S,S") ); } const String &source = *filename->asString(); const String &dest = *filedest->asString(); ::Falcon::BaseFileStream::t_shareMode shMode = ::Falcon::BaseFileStream::e_smShareFull; FileStream instream, outstream; instream.open( source, ::Falcon::BaseFileStream::e_omReadOnly, shMode ); if ( ! instream.good() ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ). extra( source ). sysError( (uint32) instream.lastError() ) ); } outstream.create( dest, (Falcon::BaseFileStream::t_attributes) 0644, shMode ); if ( ! outstream.good() ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ). extra( dest ). sysError( (uint32) outstream.lastError() ) ); } // Declaring the VM idle from now on. VMachine::Pauser pauser( vm ); byte buffer[4096]; int count = 0; while( ( count = instream.read( buffer, 4096) ) > 0 ) { if ( outstream.write( buffer, count ) < 0 ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ). sysError( (uint32) outstream.lastError() ) ); } } if ( count < 0 ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ). sysError( (uint32) instream.lastError() ) ); } } /*# @function dirRemove @brief Removes an empty directory. @param dir The path to the directory to be removed. @raise IoError on system error. The function removes an empty directory. On failure an IoError with code 1012 will be raised. */ FALCON_FUNC dirRemove ( ::Falcon::VMachine *vm ) { Item *name = vm->param(0); if ( name == 0 || ! name->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } String *strName = name->asString(); int32 fsError; if( ! Sys::fal_rmdir( *strName, fsError ) ) { throw new IoError( ErrorParam( 1012, __LINE__ ). origin( e_orig_runtime ).desc( "Cannot remove directory" ).extra( *strName ). sysError( (uint32) Sys::_lastError() ) ); } } /*# @function dirChange @brief Changes the current working directory. @param newDir The new working directory. @raise IoError on system error. On success, the working directory is changed to the specified one. The path must be indicated in Falcon conventions (forward slashes separating directories). On failure, an IoError is raised. */ FALCON_FUNC dirChange ( ::Falcon::VMachine *vm ) { Item *name = vm->param(0); if ( name == 0 || ! name->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } String *strName = name->asString(); int32 fsError; if( ! Sys::fal_chdir( *strName, fsError ) ) { throw new IoError( ErrorParam( 1013, __LINE__ ). origin( e_orig_runtime ).desc( "Cannot change working directory" ).extra( *strName ). sysError( (uint32) Sys::_lastError() ) ); } else vm->retnil(); } /*# @function dirCurrent @brief Returns the current working directory. @return A string representing the current working directory. @raise IoError on system error. On success, a string containing the current working directory is returned. The path is in Falcon convention (forward slashes). If any system error occurs, an IoError is raised. */ FALCON_FUNC dirCurrent ( ::Falcon::VMachine *vm ) { int32 fsError; CoreString *ret = new CoreString; if( ! Sys::fal_getcwd( *ret, fsError ) ) { throw new IoError( ErrorParam( 1014, __LINE__ ). origin( e_orig_runtime ).desc( "Cannot read current working directory" ). sysError( (uint32) Sys::_lastError() ) ); } else vm->retval( ret ); } /*# @function fileRemove @brief Removes a file from the system. @param filename The name, relative or absolute path of the file to be removed. @raise IoError on system error. This function tries to remove a file from the filesystem. If unsuccessful, an error with code 1015 is raised. */ FALCON_FUNC fileRemove ( ::Falcon::VMachine *vm ) { Item *name = vm->param(0); if ( name == 0 || ! name->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ) ); return; } String *strName = name->asString(); int32 fsError; if( ! Sys::fal_unlink( *strName, fsError ) ) { throw new IoError( ErrorParam( 1015, __LINE__ ). origin( e_orig_runtime ).desc( "Cannot remove target file" ).extra( *strName ). sysError( (uint32) Sys::_lastError() ) ); } } /*# @function fileMove @brief Renames a file locally. @param sourcePath The path of the file to be moved. @param destPath The path of the destination file. @raise IoError on system error. This function actually renames a file. Usually, it will file if the caller tries to move the file across filesystems (i.e. on different discs). On failure, an IoError is raised. */ FALCON_FUNC fileMove ( ::Falcon::VMachine *vm ) { Item *name = vm->param(0); Item *dest = vm->param(1); if ( name == 0 || ! name->isString() || dest == 0 || ! dest->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ) ); return; } String *strName = name->asString(); String *strDest = dest->asString(); int32 fsError; if( ! Sys::fal_move( *strName, *strDest, fsError ) ) { throw new IoError( ErrorParam( 1016, __LINE__ ). origin( e_orig_runtime ).desc( "Cannot move target file" ).extra( *strName + " -> " + *strDest ). sysError( (uint32) Sys::_lastError() ) ); } } /*# @function fileChmod @brief Changes UNIX access right to a directory entry. @param path A file or otherwise valid directory entry. @param mode The new access mode. @raise IoError on system error. This function will work only on POSIX systems. The file access mode will be set along the octal access right defined by POSIX. See man 3 chmod for details. */ FALCON_FUNC fileChmod ( ::Falcon::VMachine *vm ) { Item *name = vm->param(0); Item *mode = vm->param(1); if ( name == 0 || ! name->isString() || mode == 0 || ! mode->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ) ); } if( ! Sys::fal_chmod( *name->asString(), (uint32) mode->forceInteger() ) ) { throw new IoError( ErrorParam( 1016, __LINE__ ). origin( e_orig_runtime ).desc( "Cannot change target file mode" ).extra( *name->asString() ). sysError( (uint32) Sys::_lastError() ) ); } } /*# @function fileChown @brief Changes UNIX owner to a directory entry. @param path A file or otherwise valid directory entry. @param ownerId The new ownerId. @raise IoError on system error. This function changes the user ownership of the given file on POSIX systems. */ FALCON_FUNC fileChown ( ::Falcon::VMachine *vm ) { Item *name = vm->param(0); Item *mode = vm->param(1); if ( name == 0 || ! name->isString() || mode == 0 || ! mode->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ) ); return; } if( Sys::fal_chown( *name->asString(), (int32) mode->forceInteger() ) ) { throw new IoError( ErrorParam( 1017, __LINE__ ). origin( e_orig_runtime ).desc( "Cannot change target file owner" ).extra( *name->asString() ). sysError( (uint32) Sys::_lastError() ) ); } } /*# @function fileChgroup @brief Changes UNIX group to a directory entry. @param path A file or otherwise valid directory entry. @param groupId The new group id. @raise IoError on system error. This function changes the group ownership of the given file on POSIX systems. */ FALCON_FUNC fileChgroup ( ::Falcon::VMachine *vm ) { Item *name = vm->param(0); Item *mode = vm->param(1); if ( name == 0 || ! name->isString() || mode == 0 || ! mode->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ) ); } if( Sys::fal_chgrp( *name->asString(), (int32) mode->forceInteger() ) ) { throw new IoError( ErrorParam( 1018, __LINE__ ). origin( e_orig_runtime ).desc( "Cannot change target file owner" ).extra( *name->asString() ). sysError( (uint32) Sys::_lastError() ) ); } } } } /* end of dir_ext.cpp */ engine/core_module/envvars.cpp000066400000000000000000000116041176363201700170200ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: envvars.cpp Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer dic 27 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Short description */ /*# @beginmodule core */ #include #include #include #include #include "core_module.h" /*# @funset core_environ Environment support @brief Functions used to access the process environment variables. Environment variables are an handful way to provide system wide configuration. Falcon RTL getenv(), setenv() and unsetenv() functions peek and manipulates environment variables. Variables set with "setenv()" will be available to child processes in case they are launched with the utilities in the Process module. @begingroup core_syssupport @beginset core_environ */ namespace Falcon { namespace core { /*# @function getenv @brief Get environment variable value. @param varName Environment variable name (as a string) @return The value of the environment variable or nil if it is not present. This function returns a string containing the value set for the given environment variable by the operating system before starting the Falcon process or or by a previous call to setenv(). If the given variable name is not declared, the function will return nil. On some systems (e.g. MS-Windows), setting a variable to an empty string is equivalent to unsetting it, so getenv() will never return an empty string. On other systems, environment variables may be set to empty strings, that may be returned by getenv(). */ FALCON_FUNC falcon_getenv( ::Falcon::VMachine *vm ) { Item *i_var = vm->param( 0 ); if ( i_var == 0 || ! i_var->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); return; } String retVal; if ( Sys::_getEnv( *i_var->asString(), retVal ) ) { vm->retval( retVal ); // will garbage this } else { vm->retnil(); } } /*# @function setenv @brief Set environment variable value. @param varName Environment variable name (as a string) @param value a value for the given variable. @raise IoError on failure. This function sets the given value for the given environment variable. The varName parameter must be a string, while value may be any Falcon value. If the value is not a string, it will be converted using the toString() function. If the variable was previously set to a different value, its value is changed; if it doesn't existed, it is created. The function may fail if the system cannot perform the operation; this may happen if the space that the system reserves for environment variables is exhausted. In this case, the function raises an error. */ FALCON_FUNC falcon_setenv( ::Falcon::VMachine *vm ) { Item *i_var = vm->param( 0 ); Item *i_value = vm->param( 1 ); if ( i_var == 0 || ! i_var->isString() || i_value == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } String *value; String localVal; if ( i_value->isString() ) value = i_value->asString(); else { value = &localVal; vm->itemToString( *value, i_value ); } if ( ! Sys::_setEnv( *i_var->asString(), *value ) ) { throw new IoError( ErrorParam( 1000, __LINE__ ). origin( e_orig_runtime ).desc( "Environment variable set failed." ). extra( *i_var->asString() ). sysError( (uint32) Sys::_lastError() ) ); } } /*# @function unsetenv @brief Clear environment variable value. @param varName Environment variable name (as a string) This function removes a given variable setting, causing subsequents getenv( varName ) to return nil. */ FALCON_FUNC falcon_unsetenv( ::Falcon::VMachine *vm ) { Item *i_var = vm->param( 0 ); if ( i_var == 0 || ! i_var->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } Sys::_unsetEnv( *i_var->asString() ); } static void _falcon_getEnviron_cb( const String& key, const String& value, void* data ) { LinearDict* ret = (LinearDict*) data; ret->put( new CoreString( key ), new CoreString( value ) ); } /*# @function getEnviron() @brief Return a dictionary containing all the environment variables. @return A dictionary where each key is an environment variable. */ FALCON_FUNC falcon_getEnviron( ::Falcon::VMachine *vm ) { LinearDict* ret = new LinearDict; Sys::_enumerateEnvironment( &_falcon_getEnviron_cb, ret ); vm->retval( new CoreDict( ret ) ); } } } /* end of envvars.cpp */ engine/core_module/error_ext.cpp000066400000000000000000000462011176363201700173460ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: error_ext.cpp Falcon error class and core subclasses ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 14 Aug 2008 00:17:31 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "core_module.h" #include /*# @beginmodule core */ namespace Falcon { namespace core { /*# @group errors The Falcon Error System. @brief Falcon classes reflecting internal errors. This is the list of classes used by falcon core module to report the scripts (or embedding applications) about runtime errors. */ /*# @class Error @brief Internal VM and runtime error reflection class. @ingroup errors @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. In case the error code is a well known code (i.e. one of the codes known by the engine), the description of the error will be automatically provided. To provide an error message without setting the code description, use directly the @a Error.message property after having created the object. The Error class is used by the virtual machine and by the Falcon Feathers functions to communicate to the scripts, and eventually to the embedding application, about error conditions. It is also available to extension modules, and to the script themselves, that can create error instances that can be cached internally or returned to the embedder. A complete error code is formed by two letters indicating the error origin, and a numeric code specifying the correct error name. By convention, one and only one error description may be associated with one error code. The error @i message is free to be used to carry more specific informations about the error conditions. Use the comment parameter when the error message is generic, and/or the error may be reported because of various reasons, or to give an hint about how to avoid the error. Error codes below 5000 are reserved to Falcon engine and officially recognized modules. Extension modules should issue errors above 5001, unless raising well known error codes that are encoded and described directly by the Falcon Engine (i.e. a very common error code is 901 - invalid parameters when a user makes a mistake in calling a script function). All the elements in the error class are automatically initialized by the constructor, except for the code, the message and the description. As some error are created by binary modules, which are not executed by the VM, the informations about the line and the program counter that generated the error may not always be available. The toString() method returns a string representation of the error, which includes all the available informations (except for system error description). In this version, access to the TraceBack class has been removed from scripts. @prop description Textual description for the error code of this error. @prop message Arbitrary text used to better explain and define the error conditions. Consider this as a "free text". @prop systemError If the error was caused by a failure during an OS operation, this this property contains the error code indicating the cause of the failure. Otherwise, it will be 0. @prop module Name of the module where the error has been generated. @prop symbol Symbol name (function or method) where the error has been raised. @prop line Line at which the error happened. If not applicable (i.e. because the error is not generated by a Falcon script) is 0. @prop pc Program counter of the instruction that raised the error. If not applicable (i.e. if the VM wasn't running when the error has been raised) the number will be 0. @prop subErrors Array of sub-errors. Some error generating facilities may delay error reporting to complete some operations, and then report all the errors at once, encapsulated in a top-level failure signaling error. It's the case of the reflexive compiler, which, in case of errors during compilation of source code, would record all the errors and store them in a generic "syntax error" exception. This property stores a vector of the single sub-errors that have caused operation failure. @prop boxed Boxed error. Similar to subError, but this is a single error that was separately raised, and caused an higher-priority error to be raised next. For example, included script may raise an error that generates then a "failed to include" error at top-level. The inner error generated by the included script is available as the boxed error. */ // Separate "code" property to test for @property command FALCON_FUNC Error_init ( ::Falcon::VMachine *vm ) { core::ErrorObject *einst = static_cast(vm->self().asObject()); // subclasses may have already given a value to the userdata. Falcon::Error *err = einst->getError(); if( err == 0 ) { err = new Falcon::Error( ErrorParam( 0, __LINE__ ). module( "core" ) ); einst->setUserData( err ); } // declare that the script has created it err->origin( e_orig_script ); // filling properties Item *param = vm->param( 0 ); if ( param != 0 && param->type() != FLC_ITEM_NIL ) err->errorCode( (int) param->forceInteger() ); param = vm->param( 1 ); if ( param != 0 && param->isString() ) err->errorDescription( *param->asString() ); param = vm->param( 2 ); if ( param != 0 && param->isString() ) err->extraDescription( *param->asString() ); } /*# @method toString Error @brief Creates a textual representation of the error. This method uses the standard Falcon error representation to render the error codes, descriptions and stack traces into a string. Suberrors are also considered. To get only a descriptive string of the error without its stack trace, use the @a Error.heading method. */ FALCON_FUNC Error_toString ( ::Falcon::VMachine *vm ) { core::ErrorObject *einst = static_cast(vm->self().asObject()); Falcon::Error *err = einst->getError(); if ( err != 0 ) { CoreString *cs = new CoreString; err->toString( *cs ); vm->retval( cs ); } else vm->retnil(); } /*# @method heading Error @brief Creates a short textual representation of the error. This method will only render the essential informations of the error, without printing the stack trace and without checking for other sub errors in the @a Error.subErrors array. @see Error.toString */ FALCON_FUNC Error_heading ( ::Falcon::VMachine *vm ) { ErrorObject *einst = static_cast(vm->self().asObject()); Falcon::Error *err = static_cast(einst->getUserData()); if ( err != 0 ) { CoreString *cs = new CoreString; err->heading( *cs ); vm->retval( cs ); } else vm->retnil(); } /*# @method getSysErrDesc Error @brief returns system specific error description. @return System specific error description or nil if not available. If the error was generated by the underlying system (that is, if systemError > 0) returns a system and locale dependent error description. The description is obtained by querying the relevant OS error description API/SDK. */ FALCON_FUNC Error_getSysErrDesc ( ::Falcon::VMachine *vm ) { ErrorObject *einst = static_cast(vm->self().asObject()); Falcon::Error *err = static_cast(einst->getUserData()); if ( err != 0 ) { String temp; ::Falcon::Sys::_describeError( err->systemError(), temp ); vm->retval( temp ); } else vm->retnil(); } /*# @class SyntaxError @brief Syntax error descriptor. @ingroup errors @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. @from Error code, description, extra This errors are generated by the compiler or the assembler when a compilation fails. Usually, scripts won't receive this unless they are using the compiler to compile themselves modules on the fly. */ FALCON_FUNC SyntaxError_init ( ::Falcon::VMachine *vm ) { ErrorObject *einst = static_cast(vm->self().asObject()); if( einst->getFalconData() == 0 ) einst->setUserData( new Falcon::SyntaxError ); Error_init( vm ); } /*# @class GenericError @brief Generic undefined failure. @ingroup errors @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. @from Error code, description, extra This error reports a generic failure, usually not recoverable, which cannot be classified under a more specific class. Examples are failure in VM linking of deserialized objects, once the de-serialization is successful both on the stream side (correct load) and on the code side (correct conversion into Falcon items). */ FALCON_FUNC GenericError_init ( ::Falcon::VMachine *vm ) { ErrorObject *einst = static_cast(vm->self().asObject()); if( einst->getFalconData() == 0 ) einst->setUserData( new Falcon::GenericError ); Error_init( vm ); } /*# @class CodeError @brief VM and internal coded related error descriptor. @ingroup errors @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. @from Error code, description, extra This hard errors are usually generated by the VM when it finds some corruption of the code, or some illegal instruction parameter. Scripts can hardly receive it, if not as a notification of something bad happened to another controlled script. */ FALCON_FUNC CodeError_init ( ::Falcon::VMachine *vm ) { ErrorObject *einst = static_cast(vm->self().asObject()); if( einst->getUserData() == 0 ) einst->setUserData( new Falcon::CodeError ); Error_init( vm ); } /*# @class IoError @brief Error on I/O operations. @ingroup errors @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. @from Error code, description, extra This error is raised when an I/O operation fails on the underlying system stream. It may also be generated by functions accessing streams at high abstraction level, as the functions used to serialize items or to read XML files. */ FALCON_FUNC IoError_init ( ::Falcon::VMachine *vm ) { ErrorObject *einst = static_cast(vm->self().asObject()); if( einst->getUserData() == 0 ) einst->setUserData( new Falcon::IoError ); Error_init( vm ); } /*# @class TypeError @brief Type mismatch in a typed operation. @ingroup errors @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. @from Error code, description, extra This error is generated when some operations requiring items of a certain type fail because of type mismatch. */ FALCON_FUNC TypeError_init ( ::Falcon::VMachine *vm ) { ErrorObject *einst = static_cast(vm->self().asObject()); if( einst->getUserData() == 0 ) einst->setUserData( new Falcon::TypeError ); Error_init( vm ); } /*# @class AccessError @from Error code, description, extra @brief Error accessing an indexed item. @ingroup errors @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. This error is generated when an array was accessed beyond its size, or a key was not found in a dictionary, or a requested property was not found in an object. In addition, core, RTL and extension functions may raise this error when the semantic is appropriate (i.e. when providing them nonexisting property names, or when requesting to access an nonexisting range in an array). */ FALCON_FUNC AccessError_init ( ::Falcon::VMachine *vm ) { ErrorObject *einst = static_cast(vm->self().asObject()); if( einst->getUserData() == 0 ) einst->setUserData( new Falcon::AccessError ); Error_init( vm ); } /*# @class MathError @brief Mathematical calculation error. @ingroup errors @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. @from Error code, description, extra This class is generated when a mathematical operation caused an error; it may be a domain error, as trying to extract the square root of a negative number, an overflow error or a division by zero. The error code will detail what kind of math error has happened. */ FALCON_FUNC MathError_init ( ::Falcon::VMachine *vm ) { ErrorObject *einst = static_cast(vm->self().asObject()); if( einst->getUserData() == 0 ) einst->setUserData( new Falcon::MathError ); Error_init( vm ); } /*# @class ParamError @brief Incongruent paremeter error. @ingroup errors @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. @from Error code, description, extra This error is generated when a function is called with insufficient parameters, or with parameters of the wrong types; this error may also be raised if the function determines the parameters not to be valid for other reasons (i.e. if the function requires not just a string as a parameter, but also a string of a minimum length). This error usually indicates a problem in the script code. Normally, it is to be considered a runtime error that has to be resolved by fixing an incorrect script, which should pass correct parameter to the functions it calls. */ FALCON_FUNC ParamError_init ( ::Falcon::VMachine *vm ) { ErrorObject *einst = static_cast(vm->self().asObject()); if( einst->getUserData() == 0 ) einst->setUserData( new Falcon::ParamError ); Error_init( vm ); } /*# @class ParseError @brief Generic input parsing error. @ingroup errors @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. @from Error code, description, extra This error is generated when a parsing operation fails. It is used by RTL and extension modules when an input that should respect a given format is malformed. In example, the core module throws this error when the command line parser is told to search for a named parameter, but the command line parameters have been exhausted. It is also used by the mxml module to report problems while parsing XML files. Finally, this error is available for script to describe an error status due to invalid inputs. */ FALCON_FUNC ParseError_init ( ::Falcon::VMachine *vm ) { ErrorObject *einst = static_cast(vm->self().asObject()); if( einst->getUserData() == 0 ) einst->setUserData( new Falcon::ParseError ); Error_init( vm ); } /*# @class CloneError @brief Item cannot be cloned. @ingroup errors @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. @from Error code, description, extra Items containing external data, provided by extension modules or embedding application, must respect a "clone" protocol that allows to share, or duplicate, the inner data between different Falcon instances of the cloned item. If the inner core of Falcon item coming from a non-falcon source does not respect this protocol, the item cannot be cloned. When this error is raised, it is usually because the script tried to explicitly duplicate a "very special object" (for example, as an external resource handle created by a module). */ FALCON_FUNC CloneError_init ( ::Falcon::VMachine *vm ) { ErrorObject *einst = static_cast(vm->self().asObject()); if( einst->getUserData() == 0 ) einst->setUserData( new Falcon::CloneError); Error_init( vm ); } /*# @class InterruptedError @brief Wait operation interrupted. @ingroup errors @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. @from Error code, description, extra This error is raised when a wait interrupt request has been received by the VM during a blocking wait. @see interrupt_protocol */ FALCON_FUNC IntrruptedError_init ( ::Falcon::VMachine *vm ) { ErrorObject *einst = static_cast(vm->self().asObject()); if( einst->getUserData() == 0 ) einst->setUserData( new Falcon::InterruptedError ); Error_init( vm ); } /*# @class MessageError @brief Error in the messaging system. @ingroup errors @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. @from Error code, description, extra This error is raised when a wait interrupt request has been received by the VM during a blocking wait. @see interrupt_protocol */ FALCON_FUNC MessageError_init ( ::Falcon::VMachine *vm ) { ErrorObject *einst = static_cast(vm->self().asObject()); if( einst->getUserData() == 0 ) einst->setUserData( new Falcon::MessageError ); Error_init( vm ); } /*# @class TableError @brief Error in Table class core operations. @ingroup errors @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. @from Error code, description, extra This error is raised when an logic or constraint error is found in access or when modifying tables. It is also the result for some common table operations in case of failure (i.e. @a Table.find). @see Table */ FALCON_FUNC TableError_init ( ::Falcon::VMachine *vm ) { ErrorObject *einst = static_cast(vm->self().asObject()); if( einst->getUserData() == 0 ) einst->setUserData( new Falcon::TableError ); Error_init( vm ); } } } /* end of error_ext.cpp */ engine/core_module/fal_include.cpp000066400000000000000000000112721176363201700176020ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: fal_include.cpp Include function - Dynamic module loading. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 31 Aug 2008 19:01:59 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include /*# @beginmodule core */ namespace Falcon { namespace core { /*# @function include @brief Dynamically loads a module as a plugin. @param file The relative filename of the module. @optparam inputEnc Input encoding. @optparam path A string of ';' separated search paths. @optparam symDict Symbols to be queried (or nil). @raise IoError if the module cannot be found or loaded. A module indicated by filename is compiled, loaded and linked in the running Virtual Machine. The inclusion is relative to the current path, be it set in the script, in the current embedding application or in the falcon command line interpreter. It is possible to use a path relative to the current script path by using the scriptPath variable. If a dictionary of symbols to be queried is @b not provided, the module is loaded and its main code, if present, is executed. If @b symDict is provided, its keys are strings which refer to symbol names to be searched in the loaded module. If present, the entry are filled with symbols coming from the loaded module. When @b symDict is provided, the linked module won't have its main code executed (it is possible to execute it at a later time adding "__main__" to the searched symbols). If a symbol is not found, its entry in the dictionary will be set to nil. When loaded this way, the export requests in the loaded module are @b not honored (import/from semantic). The @b compiler Feather module provides a more complete interface to dynamic load and compilation, but this minimal dynamic load support is provided at base level for flexibility. */ FALCON_FUNC fal_include( Falcon::VMachine *vm ) { Falcon::Item *i_file = vm->param(0); Falcon::Item *i_enc = vm->param(1); Falcon::Item *i_path = vm->param(2); Falcon::Item *i_syms = vm->param(3); if( i_file == 0 || ! i_file->isString() || (i_syms != 0 && ! (i_syms->isDict() || i_syms->isNil()) ) || (i_enc != 0 && !(i_enc->isString() || i_enc->isNil()) ) || (i_path != 0 && !(i_path->isString() || i_path->isNil()) ) ) { throw new Falcon::ParamError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ) .origin(e_orig_runtime) .extra( "S,[S],[S],[D]" ) ); } // create the loader/runtime pair. ModuleLoader cpl( i_path == 0 || i_path->isNil() ? vm->appSearchPath() : String(*i_path->asString()) ); cpl.delayRaise(true); Runtime rt( &cpl, vm ); rt.hasMainModule( false ); // minimal config if ( i_enc != 0 && ! i_enc->isNil() ) { cpl.sourceEncoding( *i_enc->asString() ); } else { String sEnc, sIoEnc; Engine::getEncodings( sEnc, sIoEnc ); cpl.sourceEncoding( sEnc ); } bool execAtLink = vm->launchAtLink(); //! Copy the filename so to be sure to display it correctly in an eventual error. String fileName = *i_file->asString(); fileName.bufferize(); // load and link try { rt.loadFile( fileName, true ); vm->launchAtLink( i_syms == 0 || i_syms->isNil() ); LiveModule *lmod = vm->link( &rt ); // shall we read the symbols? if( lmod != 0 && ( i_syms != 0 && i_syms->isDict() ) ) { CoreDict *dict = i_syms->asDict(); // traverse the dictionary Iterator iter( &dict->items() ); while( iter.hasCurrent() ) { // if the key is a string and a corresponding item is found... Item *ival; if ( iter.getCurrentKey().isString() && ( ival = lmod->findModuleItem( *iter.getCurrentKey().asString() ) ) != 0 ) { // copy it locally iter.getCurrent() = *ival; } else { iter.getCurrent().setNil(); } iter.next(); } } // reset launch status vm->launchAtLink( execAtLink ); } catch(Error* err) { CodeError *ce = new CodeError( ErrorParam( e_loaderror, __LINE__ ). extra( fileName ) ); ce->appendSubError(err); err->decref(); // reset launch status vm->launchAtLink( execAtLink ); throw ce; } } } } engine/core_module/file_ext.cpp000066400000000000000000002141751176363201700171430ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: file.cpp File api ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun nov 1 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Falcon file api. */ /*# @beginmodule core */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core_module.h" namespace Falcon { namespace core { // raises the correct error depending on the problem on the file static void s_breakage( Stream *file ) { if ( file->unsupported() ) throw new IoError( ErrorParam( e_io_unsup ) .origin( e_orig_runtime ) ); else if ( file->invalid() ) throw new IoError( ErrorParam( e_io_invalid ) .origin( e_orig_runtime ) ); throw new IoError( ErrorParam( e_io_error ) .origin( e_orig_runtime ) .sysError( (uint32) file->lastError() ) ); } /*# @class Stream @brief Stream oriented I/O class. @ingroup core_syssupport Stream class is a common interface for I/O operations. The class itself is to be considered "abstract". It should never be directly instantiated, as factory functions, subclasses and embedding applications will provide fully readied stream objects. Stream I/O is synchronous, but it's possible to wait for the operation to be nonblocking with the readAvailable() and writeAvailable() methods. Generally, all the methods in the stream class raise an error in case of I/O failure. Streams provide also a character encoding layer; readText() and writeText() are meant to decode and encode falcon strings based on character encoding set with setEncoding(). Method as read() and write() are not affected, and seek operations works bytewise regardless the character conversion being used. @prop encoding Name of the set encoding, if given, for text operations @prop eolMode Mode of EOL conversion in text operations. */ /*# @method close Stream @brief Closes the stream. All the operations are flushes and system resources are freed. This method is also called automatically at garbage collection, if it has not been called before. */ FALCON_FUNC Stream_close ( ::Falcon::VMachine *vm ) { Stream *file = static_cast( vm->self().asObject()->getFalconData() ); // declaring the VM idle from now on. VMachine::Pauser pauser( vm ); if ( ! file->close() ) { s_breakage( file ); } } /*# @method flush Stream @brief Flushes a stream. Ensures that the operations on the stream are correctly flushed. */ FALCON_FUNC Stream_flush ( ::Falcon::VMachine *vm ) { Stream *file = static_cast( vm->self().asObject()->getFalconData() ); // declaring the VM idle from now on. VMachine::Pauser pauser( vm ); if ( ! file->flush() ) { s_breakage( file ); } } /** Close a standard stream. */ FALCON_FUNC StdStream_close ( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Stream *file = static_cast( self->getFalconData() ); // declaring the VM idle from now on. VMachine::Pauser pauser( vm ); if ( file->close() ) { if ( vm->hasProcessStreams() ) { Item mode; if( self->getProperty( "_stdStreamType", mode ) && mode.isInteger() ) { switch( mode.asInteger() ) { case 0: vm->stdIn()->close(); break; case 1: vm->stdOut()->close(); break; case 2: vm->stdErr()->close(); break; } } } } } /*# @method read Stream @brief Reads binary data from the stream. @param buffer A string or MemBuf that will be filled with read data. @optparam size Optionally, a maximum size to be read. @return Amount of data actually read. @raise IoError on system errors. This method uses an already existing and pre-allocated string or Memory Buffer, filling it with at maximum @b size bytes. If @b size is not provided, the method tries to read enough data to fill the given buffer. A string may be pre-allocated with the @a strBuffer function. If @b size is provided but it's larger than the available space in the given buffer, it is ignored. If there isn't any available space in the target buffer, a ParamError is raised. If the buffer is a string, each read fills the string from the beginning. If it is a MemBuffer, the space between @a MemoryBuffer.limit and @a len is filled. This allow for partial reads in slow (i.e. network) streams. */ FALCON_FUNC Stream_read ( ::Falcon::VMachine *vm ) { Stream *file = dyncast( vm->self().asObject()->getFalconData() ); Item *i_target = vm->param(0); Item *i_size = vm->param(1); if ( i_target == 0 || ! ( i_target->isString() || i_target->isMemBuf() ) || ( i_size != 0 && ! i_size->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S|M,[N]" ) ); } int32 size; byte *memory; // first, extract the maximum possible size if ( i_target->isString() ) { String *str = i_target->asString(); if ( str->allocated() == 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->moduleString( rtl_string_empty ) ) ); } memory = str->getRawStorage(); size = str->allocated(); } else { MemBuf* mb = i_target->asMemBuf(); size = mb->remaining(); if( size <= 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->moduleString( rtl_buffer_full ) ) ); } memory = mb->data() + (mb->position() * mb->wordSize()); } if( i_size != 0 ) { int32 nsize = (int32) i_size->forceInteger(); if( nsize <= 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->moduleString( rtl_zero_size ) ) ); } if ( nsize < size ) size = nsize; } // declare the VM idle during the I/O vm->idle(); size = file->read( memory, size ); vm->unidle(); if ( size < 0 ) { s_breakage( file ); } if ( i_target->isString() ) { i_target->asString()->size( size ); } else { MemBuf* mb = i_target->asMemBuf(); mb->position( i_target->asMemBuf()->position() + size ); fassert( mb->limit() <= mb->length() ); } vm->retval((int64) size ); } /*# @method grab Stream @brief Grabs binary data from the stream. @param size Maximum size to be read. @return A string containing binary data from the stream. @raise IoError on system errors. This method creates a string wide enough to read size bytes, and then tries to fill it with binary data coming from the stream. */ FALCON_FUNC Stream_grab ( ::Falcon::VMachine *vm ) { Stream *file = dyncast( vm->self().asObject()->getFalconData() ); Item *i_size = vm->param(0); if ( i_size == 0 || ! i_size->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "N" ) ); } int32 nsize = (int32) i_size->forceInteger(); if( nsize <= 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->moduleString( rtl_zero_size ) ) ); } CoreString* str = new CoreString; str->reserve( nsize ); vm->idle(); int32 size = file->read( str->getRawStorage(), nsize ); vm->unidle(); if ( size < 0 ) { s_breakage( file ); } str->size( size ); vm->retval( str ); } /*# @method readText Stream @brief Reads text encoded data from the stream. @param buffer A string that will be filled with read data. @optparam size Optionally, a maximum size to be read. @return Amount of data actually read. @raise IoError on system errors. This method reads a string from a stream, eventually parsing the input data through the given character encoding set by the @a Stream.setEncoding method. The number of bytes actually read may vary depending on the decoding rules. If the size parameter is given, the function will try to read at maximum @b size characters, enlarging the string if there isn't enough room for the operation. If it is not given, the current allocated memory of buffer will be used instead. @note This differ from Stream.read, where the buffer is never grown, even when it is a string. If the function is successful, the buffer may contain @b size characters or less if the stream hadn't enough characters to read. In case of failure, an IoError is raised. Notice that this function is meant to be used on streams that are known to have available all the required data. For streams that may perform partial updates (i.e. network streams), a combination of binary reads and @a transcodeFrom calls should be used instead. */ FALCON_FUNC Stream_readText ( ::Falcon::VMachine *vm ) { Stream *file = dyncast( vm->self().asObject()->getFalconData() ); Item *i_target = vm->param(0); Item *i_size = vm->param(1); if ( i_target == 0 || ! i_target->isString() || ( i_size != 0 && ! i_size->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S,[N]" ) ); } String *str = i_target->asString(); int32 size; if ( i_size != 0 ) { size = (int32) i_size->forceInteger(); if ( size <= 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->moduleString( rtl_zero_size ) ) ); return; } str->reserve( size ); } else { size = str->allocated(); if ( size <= 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->moduleString( rtl_string_empty ) ) ); return; } } vm->idle(); if ( ! file->readString( *str, size ) ) { vm->unidle(); s_breakage( file ); } else { vm->unidle(); } vm->retval( (int64) str->length() ); } /*# @method grabText Stream @brief Grabs text encoded data from the stream. @param size Optionally, a maximum size to be read. @return A string containing the read text. @raise IoError on system errors. This method reads a string from a stream, eventually parsing the input data through the given character encoding set by the @a Stream.setEncoding method, and returns it in a newly allocated string. If the function is successful, the buffer may contain @b size characters or less if the stream hadn't enough characters to read. In case of failure, an IoError is raised. Notice that this function is meant to be used on streams that are known to have available all the required data. For streams that may perform partial updates (i.e. network streams), a combination of binary reads and @a transcodeFrom calls should be used instead. */ FALCON_FUNC Stream_grabText ( ::Falcon::VMachine *vm ) { Stream *file = dyncast( vm->self().asObject()->getFalconData() ); Item *i_size = vm->param(0); if ( i_size == 0 || ! i_size->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "N" ) ); } CoreString *str = new CoreString; int32 size = (int32) i_size->forceInteger(); if ( size <= 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->moduleString( rtl_zero_size ) ) ); return; } str->reserve( size ); vm->idle(); if ( ! file->readString( *str, size ) ) { vm->unidle(); s_breakage( file ); // will throw } else { vm->unidle(); } vm->retval( str ); } /*# @method readLine Stream @brief Reads a line of text encoded data. @param buffer A string that will be filled with read data. @optparam size Maximum count of characters to be read before to return anyway. @return True if a line was read, false otherwise. @raise IoError on system errors. This function works as @a Stream.readText, but if a new line is encountered, the read terminates. Returned string does not contain the EOL sequence. Also, the returned string may be empty if the line was empty. At EOF, the function returns false. Example: @code s = InputStream( "file.txt" ) line = strBuffer(4096) while s.readLine( line ): > "LINE: ", line s.close() @endcode @note It is possible to obtain a newly allocated line instead of having to provide a target buffer through the @a Stream.grabLine method. @see Stream.grabLine */ FALCON_FUNC Stream_readLine ( ::Falcon::VMachine *vm ) { Stream *file = dyncast( vm->self().asObject()->getFalconData() ); Item *i_target = vm->param(0); Item *i_size = vm->param(1); if ( i_target == 0 || ! i_target->isString() || ( i_size != 0 && ! i_size->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S,[N]" ) ); } String *str = i_target->asString(); int32 size; if ( i_size != 0 ) { size = (int32) i_size->forceInteger(); if ( size <= 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->moduleString( rtl_zero_size ) ) ); return; } str->reserve( size ); } else { size = str->allocated(); if ( size <= 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->moduleString( rtl_string_empty ) ) ); return; } } // anyhow, reset the string. str->size(0); // well, if we're eof before starting, then we can't read. if ( file->eof() ) { vm->regA().setBoolean(false); return; } // we're idling while using VM memory, but we know we're keeping the data structure constant. vm->idle(); uint32 c = 0, c1 = 0; int pos = 0; bool getOk = file->get( c ); while( getOk && pos < size ) { if ( c == (uint32)'\r' ) { c1 = c; getOk = file->get( c ); continue; } else if ( c == (uint32)'\n' ) { break; } else if ( c1 != 0 ) { c1 = 0; str->append( c1 ); ++pos; } str->append( c ); ++pos; getOk = file->get( c ); } vm->unidle(); if ( ! getOk && ! file->eof() ) { s_breakage( file ); } // even if we read an empty line AND then we hit eof, we must return true, // so the program knows that the last line is empty. vm->regA().setBoolean( getOk || pos > 0 ); } /*# @method grabLine Stream @brief Grabs a line of text encoded data. @optparam size Maximum count of characters to be read before to return anyway. @return A string containing the read line, or oob(0) when the file is over. @raise IoError on system errors. This function works as @a Stream.grabText, but if a new line is encountered, the read terminates. Returned string does not contain the EOL sequence. At EOF, the function returns an oob(0), which in normal tests is translated as "false", and that can be used to build sequences. @note An empty line is returned as an empty string. Please, notice that empty lines are returned as empty strings, and that empty strings, in Falcon, assume "false" value when logically evaluated. On loops where not checking for an explicit EOF to be hit in the stream, you will need to check for the returned value to be != 0, or not out of band. For example, a normal loop may look like: @code s = InputStream( "file.txt" ) while (l = s.grabLine()) != 0 > "LINE: ", l end s.close() @endcode But it is possible to use the fuinction also in for/in loops: @code s = InputStream( "file.txt" ) for line in s.grabLine: > "LINE: ", line s.close() @endcode Or even comprehensions: @code s = InputStream( "file.txt" ) lines_in_file = List().comp( s.grabLine ) s.close() @endcode @note As @a Stream.readLine recycles a pre-allocated buffer provided as parameter it is more efficient than grabLine, unless you need to store each line for further processing later on. */ FALCON_FUNC Stream_grabLine ( ::Falcon::VMachine *vm ) { Stream *file = dyncast( vm->self().asObject()->getFalconData() ); Item *i_size = vm->param(0); if ( i_size != 0 && ! i_size->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "[N]" ) ); } int32 size = i_size == 0 ? -1 : (int32) i_size->forceInteger(); if ( size == 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->moduleString( rtl_zero_size ) ) ); } // if the stream is at eof, we must return oob(0). if ( file->eof() ) { vm->retval( (int64) 0 ); vm->regA().setOob( true ); return; } CoreString *str = new CoreString; // put it in the VM now, so that it can be inspected vm->retval( str ); if ( size > 0 ) str->reserve( size ); vm->idle(); uint32 c = 0, c1 = 0; int pos = 0; bool getOk = file->get( c ); while( getOk && (size < 0 || pos < size) ) { if ( c == (uint32)'\r' ) { c1 = c; getOk = file->get( c ); continue; } else if ( c == (uint32)'\n' ) { break; } else if ( c1 != 0 ) { c1 = 0; str->append( c1 ); ++pos; } str->append( c ); ++pos; getOk = file->get( c ); } vm->unidle(); if ( ! getOk ) { if ( ! file->eof() ) { s_breakage( file ); } // a last line with some data? else if( pos == 0 ) { // no? -- consider it null vm->retval( (int64) 0 ); vm->regA().setOob( true ); } } // otherwise, let the returned string to go. } /*# @method write Stream @brief Write binary data to a stream. @param buffer A string or a MemBuf containing the data to be written. @optparam size Number of bytes to be written. @optparam start A position from where to start writing. @return Amount of data actually written. @raise IoError on system errors. Writes bytes from a buffer on the stream. The write operation is synchronous, and will block the VM until the stream has completed the write; however, the stream may complete only partially the operation. The number of bytes actually written on the stream is returned. When the output buffer is a string, a size parameter can be given; otherwise the whole binary contents of the stream are written. A start position may optionally be given too; this allows to iterate through writes and send part of the data that couldn't be send previously without extracting substrings or copying the memory buffers. MemBuf items can participate to stream binary writes through their internal position pointers. The buffer is written from @a MemoryBuffer.position up to @a MemoryBuffer.limit, and upon completion @a MemoryBuffer.position is advanced accordingly to the number of bytes effectively stored on the stream. When a MemBuf is used, @b size and @b start parameters are ignored. */ FALCON_FUNC Stream_write ( ::Falcon::VMachine *vm ) { Item *i_source = vm->param(0); if ( i_source == 0 || ! ( i_source->isString() || i_source->isMemBuf() )) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ). extra( "S|M, [N, N]" ) ); } uint32 size, start; byte *buffer; if ( i_source->isString() ) { Item *i_count = vm->param(1); Item *i_start = vm->param(2); if ( ( i_count != 0 && ! i_count->isOrdinal() ) || ( i_start != 0 && ! i_start->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ). extra( "S|M, [N, N]" ) ); } String* str = i_source->asString(); if ( i_start == 0 ) { start = 0; } else { start = (uint32) i_start->forceInteger(); if ( start > str->size() ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ).origin( e_orig_runtime ) ); } } if( i_count == 0 ) { size = str->size(); } else { size = (uint32) i_count->forceInteger(); if ( size > str->size() - start ) { size = str->size() - start; } } buffer = str->getRawStorage(); } else { MemBuf* mb = i_source->asMemBuf(); start = mb->position(); size = mb->limit() - start; buffer = mb->data(); } Stream *file = dyncast( vm->self().asObject()->getFalconData() ); vm->idle(); int64 written = file->write( buffer + start, size ); vm->unidle(); if ( written < 0 ) { s_breakage( file ); } if ( i_source->isMemBuf() ) i_source->asMemBuf()->position( (uint32) (i_source->asMemBuf()->position() + written) ); vm->retval( written ); } /*# @method writeText Stream @brief Write text data to a stream. @param buffer A string containing the text to be written. @optparam start The character count from which to start writing data. @optparam end The position of the last character to write. @raise IoError on system errors. Writes a string to a stream using the character encoding set with @a Stream.setEncoding method. The begin and end optional parameters can be provided to write a part of a wide string without having to create a temporary substring. In case of failure, an IoError is raised. */ FALCON_FUNC Stream_writeText ( ::Falcon::VMachine *vm ) { Stream *file = static_cast( vm->self().asObject()->getFalconData() ); Item *source = vm->param(0); Item *begin = vm->param(1); Item *end = vm->param(2); uint32 iBegin, iEnd; if ( source == 0 || source->type() != FLC_ITEM_STRING || (begin != 0 && begin->type() != FLC_ITEM_INT ) || (end != 0 && end->type() != FLC_ITEM_INT ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S,[N],[N]" ) ); } iBegin = begin == 0 ? 0 : (uint32) begin->asInteger(); iEnd = end == 0 ? source->asString()->length() : (uint32) end->asInteger(); vm->idle(); if ( ! file->writeString( *(source->asString()), iBegin, iEnd ) ) { vm->unidle(); s_breakage( file ); } else { vm->unidle(); } } /*# @method seek Stream @brief Moves the file pointer on seekable streams. @param position Position in the stream to seek. @return Position in the stream after seek. @raise IoError on system errors. Changes the position in the stream from which the next read/write operation will be performed. The position is relative from the start of the stream. If the stream does not support seeking, an IoError is raised; if the position is greater than the stream size, the pointer is set to the end of the file. On success, it returns the actual position in the file. */ FALCON_FUNC Stream_seek ( ::Falcon::VMachine *vm ) { Stream *file = static_cast( vm->self().asObject()->getFalconData() ); Item *position = vm->param(0); if ( position== 0 || ! position->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } vm->idle(); int64 pos = file->seekBegin( position->forceInteger() ); vm->unidle(); if ( file->bad() ) { s_breakage( file ); } vm->retval( pos ); } /*# @method seekCur Stream @brief Moves the file pointer on seekable streams relative to current position. @param position Position in the stream to seek. @return Position in the stream after seek. @raise IoError on system errors. Changes the position in the stream from which the next read/write operation will be performed. The position is relative from the current position in the stream, a negative number meaning "backward", and a positive meaning "forward". If the stream does not support seeking, an IoError is raised. If the operation would move the pointer past the end of the file size, the pointer is set to the end; if it would move the pointer before the beginning, it is moved to the beginning. On success, the function returns the position where the pointer has been moved. */ FALCON_FUNC Stream_seekCur ( ::Falcon::VMachine *vm ) { Stream *file = static_cast( vm->self().asObject()->getFalconData() ); Item *position = vm->param(0); if ( position== 0 || ! position->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } vm->idle(); int64 pos = file->seekCurrent( position->forceInteger() ); vm->unidle(); if ( file->bad() ) { s_breakage( file ); } vm->retval( pos ); } /*# @method seekEnd Stream @brief Moves the file pointer on seekable streams relative to end of file. @param position Position in the stream to seek. @return Position in the stream after seek. @raise IoError on system errors. Changes the position in the stream from which the next read/write operation will be performed. The position is relative from the end of the stream. If the stream does not support seeking, an error is raised. If the operation would move the pointer before the beginning, the pointer is set to the file begin. On success, the function returns the position where the pointer has been moved. Use seekEnd( 0 ) to move the pointer to the end of the stream. On success, the function returns the position where the pointer has been moved. */ FALCON_FUNC Stream_seekEnd ( ::Falcon::VMachine *vm ) { Stream *file = static_cast( vm->self().asObject()->getFalconData() ); Item *position = vm->param(0); if ( position== 0 || ! position->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } vm->idle(); int64 pos = file->seekEnd( position->forceInteger() ); vm->unidle(); if ( file->bad() ) { s_breakage( file ); } vm->retval( pos ); } /*# @method tell Stream @brief Return the current position in a stream. @return Position in the stream. @raise IoError on system errors. Returns the current read/write position in the stream. If the underlying stream does not support seeking, the operation raises an IoError. */ FALCON_FUNC Stream_tell ( ::Falcon::VMachine *vm ) { Stream *file = static_cast( vm->self().asObject()->getFalconData() ); vm->idle(); int64 pos = file->tell(); vm->unidle(); if ( file->bad() ) { s_breakage( file ); } vm->retval( pos ); } /*# @method truncate Stream @brief Resizes a file. @optparam position If given, truncate at given position. @return Position in the stream. @raise IoError on system errors. Truncate stream at current position, or if a position parameter is given, truncate the file at given position relative from file start. To empty a file, open it and then truncate it, or pass 0 as parameter. If the underlying stream does not support seek operation, this function will raise an error. */ FALCON_FUNC Stream_truncate ( ::Falcon::VMachine *vm ) { Stream *file = static_cast( vm->self().asObject()->getFalconData() ); Item *position = vm->param(0); int64 pos; // declaring the VM idle from now on. VMachine::Pauser pauser( vm ); if ( position == 0 ) pos = file->tell(); else if ( ! position->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } else pos = position->forceInteger(); if ( pos == -1 || ! file->truncate( pos ) ) { s_breakage( file ); } } /*# @method lastError Stream @brief Return the last system error. @return An error code. Returns a system specific low level error code for last failed I/O operation on this stream, or zero if the last operation was successful. */ FALCON_FUNC Stream_lastError ( ::Falcon::VMachine *vm ) { Stream *file = static_cast( vm->self().asObject()->getFalconData() ); vm->retval( (int64) file->lastError() ); } /*# @method lastMoved Stream @brief Return the amount of data moved by the last operation. @return An amount of bytes. Returns the amount of bytes moved by the last write or read operation, in bytes. This may differ from the count of characters written or read by text-oriented functions. */ FALCON_FUNC Stream_lastMoved ( ::Falcon::VMachine *vm ) { Stream *file = static_cast( vm->self().asObject()->getFalconData() ); vm->retval( (int64) file->lastMoved() ); } /*# @method eof Stream @brief Checks if the last read operation hit the end of the file. @return True if file pointer is at EOF. Returns true if the last read operation hit the end of file. */ FALCON_FUNC Stream_eof ( ::Falcon::VMachine *vm ) { Stream *file = static_cast( vm->self().asObject()->getFalconData() ); vm->retval( file->eof() ? 1 : 0 ); } /*# @method isOpen Stream @brief Checks if the stream is currently open. @return True if the file is open. Return true if the stream is currently open. */ FALCON_FUNC Stream_isOpen ( ::Falcon::VMachine *vm ) { Stream *file = static_cast( vm->self().asObject()->getFalconData() ); vm->retval( file->open() ? 1 : 0 ); } /*# @method readAvailable Stream @brief Checks if data can be read, or wait for available data. @optparam seconds Maximum wait in seconds and fraction (defaults to infinite). @return True if data is available, false otherwise. @raise IoError On stream error. @raise InterruptedError if the Virtual Machine is asynchronously interrupted. This function checks if data is available on a stream to be read immediately, or if it becomes available during a determined time period. The @b seconds parameter may be set to zero to perform just a check, or to a positive value to wait for incoming data. If the parameter is not given, or if it's set to a negative value, the wait will be infinite. A read after readAvailable has returned successfully is granted not to be blocking (unless another coroutine or thread reads data from the same stream in the meanwhile). Performing a read after that readAvailable has returned false will probably block for an undefined amount of time. This method complies with the @a interrupt_protocol of the Virtual Machine. */ FALCON_FUNC Stream_readAvailable ( ::Falcon::VMachine *vm ) { Stream *file = static_cast( vm->self().asObject()->getFalconData() ); Item *secs_item = vm->param(0); int32 msecs = secs_item == 0 ? -1 : (int32) (secs_item->forceNumeric()*1000); if ( msecs != 0 ) vm->idle(); int32 avail = file->readAvailable( msecs, &vm->systemData() ); if ( msecs != 0 ) vm->unidle(); if ( file->interrupted() ) { vm->interrupted( true, true, true ); return; } if ( avail > 0 ) vm->regA().setBoolean( true ); else if ( avail == 0 ) vm->regA().setBoolean( false ); else if ( file->lastError() != 0 ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .origin( e_orig_runtime ) .sysError( (uint32) file->lastError() ) ); } else vm->regA().setBoolean( false ); } /*# @method writeAvailable Stream @brief Checks if data can be written, or wait until it's possible to write. @optparam seconds Maximum wait in seconds and fraction (defaults to infinite). @return True if data can be written, false otherwise. @raise IoError On stream error. @raise InterruptedError if the Virtual Machine is asynchronously interrupted. This function checks if the stream is available for immediate write, or if it becomes available during a determined time period. The @b seconds parameter may be set to zero to perform just a check, or to a positive value to wait for the line being cleared. If the @b seconds is not given, or if it's set to a negative value, the wait will be infinite. A write operation after writeAvailable has returned successfully is granted not to be blocking (unless another coroutine or thread writes data to the same stream in the meanwhile). Performing a read after that readAvailable has returned false will probably block for an undefined amount of time. This method complies with the @a interrupt_protocol of the Virtual Machine. */ FALCON_FUNC Stream_writeAvailable ( ::Falcon::VMachine *vm ) { Stream *file = static_cast( vm->self().asObject()->getFalconData() ); Item *secs_item = vm->param(0); int32 msecs = secs_item == 0 ? -1 : (int32) (secs_item->forceNumeric()*1000); if ( msecs != 0 ) vm->idle(); int32 available = file->writeAvailable( msecs, &vm->systemData() ); if ( msecs != 0 ) vm->unidle(); if ( available <= 0 ) { if ( file->interrupted() ) { vm->interrupted( true, true, true ); return; } if ( file->lastError() != 0 ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .origin( e_orig_runtime ) .sysError( (uint32) file->lastError() ) ); } vm->regA().setBoolean( false ); } else { vm->regA().setBoolean( true ); } } /*# @method clone Stream @brief Clone the stream handle. @return A new copy of the stream handle. @raise IoError On stream error. The resulting stream is interchangeable with the original one. From this point on, write and read operations are not reflected on the cloned object, so two stream objects can be effectively used to read and write at different places in the same resource, unless the underlying stream is not seekable (in which case, reads are destructive). The underlying resource remains open until all the instances of the streams are closed. */ FALCON_FUNC Stream_clone ( ::Falcon::VMachine *vm ) { Stream *file = static_cast( vm->self().asObject()->getFalconData() ); // create a new stream instance. Item *clstream = vm->findWKI( "Stream" ); fassert( clstream != 0 ); Stream *nstream = static_cast( file->clone() ); // in case of filesystem error, we get 0 and system error properly set. if ( nstream == 0 ) { throw new CodeError( ErrorParam( e_uncloneable ) .origin( e_orig_runtime ) ); } CoreObject *obj = clstream->asClass()->createInstance( nstream ); vm->retval( obj ); } /*# @method errorDescription Stream @brief Returns a system specific textual description of the last error. @return A string describing the system error. Returns a system specific textual description of the last error occurred on the stream. */ FALCON_FUNC Stream_errorDescription ( ::Falcon::VMachine *vm ) { Stream *file = static_cast( vm->self().asObject()->getFalconData() ); CoreString *str = new CoreString; file->errorDescription( *str ); vm->retval( str ); } /*# @funset core_stream_factory Stream factory functions @brief Function creating or opening file and system streams. Stream factory functions create a stream object bound to a system file. As the open or create operation may fail, using a factory function is more appropriate, as they can avoid creating the underlying stream object when not needed. Also, they can setup the proper stream subclass to manage special files. Stream factory function often accept a parameter to determine the sharing mode. Only some systems implements correctly this functionality, so use with caution. Predefined constant that can be used as share mode are: - @b FILE_EXCLUSIVE: The calling scripts own the shared file; other processes trying to open the file, or even the same process, should receive an error. - @b FILE_SHARE_READ: A file opened in this way can be opened by other processes, but only for read operations. - @b FILE_SHARE: Any process may open and overwrite the file. Function creating files, as IOStream() and OutputStream() accepts also a creation ownership mode. This is actually an octal number that is directly passed to the POSIX systems for directory ownership creation. It has currently no meaning for MS-Windows systems. @beginset core_stream_factory */ /*# @function InputStream @brief Open a system file for reading. @ingroup core_syssupport @param fileName A relative or absolute path to a file to be opened for input @optparam shareMode If given, the share mode. @return A new valid @a Stream instance on success. If the function is successful, it opens the given fileName and returns a valid stream object by which the underlying file may be read. Calling write methods on the returned object will fail, raising an error. If the optional share parameter is not given, the maximum share publicity available on the system will be used. If the file cannot be open, an error containing a valid fsError code is raised. See @a core_stream_factory for a description of the shared modes. */ FALCON_FUNC InputStream_creator ( ::Falcon::VMachine *vm ) { Item *fileName = vm->param(0); Item *fileShare = vm->param(1); if ( fileName == 0 || ! fileName->isString() || ( fileShare != 0 && ! fileShare->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S,[N]" ) ); } URI furi( *fileName->asString() ); if ( !furi.isValid() ) { throw new ParamError( ErrorParam( e_malformed_uri, __LINE__ ) .origin( e_orig_runtime ) .extra( *fileName->asString() ) ); } // find the appropiate provider. VFSProvider* vfs = Engine::getVFS( furi.scheme() ); if ( vfs == 0 ) { throw new ParamError( ErrorParam( e_unknown_vfs, __LINE__ ) .origin( e_orig_runtime ) .extra( *fileName->asString() ) ); } ::Falcon::BaseFileStream::t_shareMode shMode = fileShare != 0 ? (::Falcon::BaseFileStream::t_shareMode) fileShare->forceInteger() : ::Falcon::BaseFileStream::e_smShareFull; VFSProvider::OParams params; params.rdOnly(); if ( shMode == BaseFileStream::e_smExclusive ) params.shNone(); else if ( shMode == BaseFileStream::e_smShareRead ) params.shNoWrite(); vm->idle(); Stream *in = vfs->open( furi, params ); vm->unidle(); if ( in == 0 ) { throw vfs->getLastError(); } Item *stream_class = vm->findWKI( "Stream" ); //if we wrote the std module, can't be zero. fassert( stream_class != 0 ); ::Falcon::CoreObject *co = stream_class->asClass()->createInstance( in ); vm->retval( co ); } /*# @function OutputStream @brief Creates a stream for output only. @ingroup core_syssupport @param fileName A relative or absolute path to a file to be opened for input @optparam createMode If given, the ownership of the created file. @optparam shareMode If given, the share mode. @return A new valid @a Stream instance on success. If the function is successful, it creates the given fileName and returns a valid stream object by which the underlying file may be read. If an already existing file name is given, then the file is truncated and its access right are updated. Calling read methods on the returned object will fail, raising an error. If the file can be created, its sharing mode can be controlled by providing a shareMode parameter. In case the shareMode is not given, then the maximum publicity is used. If the file cannot be created, an error containing a valid fsError code is raised. See @a core_stream_factory for a description of the shared modes. */ FALCON_FUNC OutputStream_creator ( ::Falcon::VMachine *vm ) { Item *fileName = vm->param(0); if ( fileName == 0 || ! fileName->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } URI furi( *fileName->asString() ); if ( !furi.isValid() ) { throw new ParamError( ErrorParam( e_malformed_uri, __LINE__ ) .origin( e_orig_runtime ) .extra( *fileName->asString() ) ); } // find the appropriate provider. VFSProvider* vfs = Engine::getVFS( furi.scheme() ); if ( vfs == 0 ) { throw new ParamError( ErrorParam( e_unknown_vfs, __LINE__ ) .origin( e_orig_runtime ) .extra( *fileName->asString() ) ); } Item *osMode = vm->param(1); int mode; if ( osMode == 0 ) { mode = 0666; } else { if ( ! osMode->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } mode = (int) osMode->forceInteger(); } Item *fileShare = vm->param(2); if ( fileShare != 0 && ! fileShare->isInteger() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } ::Falcon::BaseFileStream::t_shareMode shMode = fileShare != 0 ? (::Falcon::BaseFileStream::t_shareMode) fileShare->forceInteger() : ::Falcon::BaseFileStream::e_smShareFull; VFSProvider::CParams params; params.truncate(); params.wrOnly(); if ( shMode == BaseFileStream::e_smExclusive ) params.shNone(); else if ( shMode == BaseFileStream::e_smShareRead ) params.shNoWrite(); params.createMode( mode ); vm->idle(); Stream *stream = vfs->create( furi, params ); vm->unidle(); if ( stream == 0 ) { throw vfs->getLastError(); } Item *stream_class = vm->findWKI( "Stream" ); //if we wrote the std module, can't be zero. fassert( stream_class != 0 ); CoreObject *co = stream_class->asClass()->createInstance(stream); vm->retval( co ); } /*# @function IOStream @brief Creates a stream for input and output. @ingroup core_syssupport @param fileName A relative or absolute path to a file to be opened for input @optparam createMode If given, the ownership of the created file. @optparam shareMode If given, the share mode. @return A new valid @a Stream instance on success. If the function is successful, it creates the given fileName and returns a valid stream object by which the underlying file may be read. If an already existing file name is given, then the file is truncated and its access right are updated. Calling read methods on the returned object will fail, raising an error. If the file can be created, its sharing mode can be controlled by providing a shareMode parameter. In case the shareMode is not given, then the maximum publicity is used. If the file cannot be created, an error containing a valid fsError code is raised. See @a core_stream_factory for a description of the shared modes. */ FALCON_FUNC IOStream_creator ( ::Falcon::VMachine *vm ) { Item *fileName = vm->param(0); if ( fileName == 0 || ! fileName->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } URI furi( *fileName->asString() ); if ( !furi.isValid() ) { throw new ParamError( ErrorParam( e_malformed_uri, __LINE__ ) .origin( e_orig_runtime ) .extra( *fileName->asString() ) ); } // find the appropriate provider. VFSProvider* vfs = Engine::getVFS( furi.scheme() ); if ( vfs == 0 ) { throw new ParamError( ErrorParam( e_unknown_vfs, __LINE__ ) .origin( e_orig_runtime ) .extra( *fileName->asString() ) ); } Item *osMode = vm->param(1); int mode; if ( osMode == 0 ) { mode = 0666; } else { if ( ! osMode->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } mode = (int) osMode->forceInteger(); } Item *fileShare = vm->param(2); if ( fileShare != 0 && ! fileShare->isInteger() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } ::Falcon::BaseFileStream::t_shareMode shMode = fileShare != 0 ? (::Falcon::BaseFileStream::t_shareMode) fileShare->forceInteger() : ::Falcon::BaseFileStream::e_smShareFull; VFSProvider::CParams params; params.rdwr(); if ( shMode == BaseFileStream::e_smExclusive ) params.shNone(); else if ( shMode == BaseFileStream::e_smShareRead ) params.shNoWrite(); params.createMode( mode ); vm->idle(); Stream *stream = vfs->open( furi, params ); vm->unidle(); if ( stream == 0 ) { stream = vfs->create( furi, params ); if ( stream == 0 ) throw vfs->getLastError(); } Item *stream_class = vm->findWKI( "Stream" ); //if we wrote the std module, can't be zero. fassert( stream_class != 0 ); ::Falcon::CoreObject *co = stream_class->asClass()->createInstance( stream ); vm->retval( co ); } static CoreObject *internal_make_stream( VMachine *vm, FalconData *clone, int userMode ) { // The clone stream may be zero if the embedding application doesn't want // to share a virtual standard stream with us. if ( clone == 0 ) { throw new CloneError( ErrorParam( e_uncloneable, __LINE__ ).origin( e_orig_runtime ) ); } Item *stream_class; if ( userMode < 0 ) stream_class = vm->findWKI( "Stream" ); else stream_class = vm->findWKI( "StdStream" ); //if we wrote the RTL module, can't be zero. fassert( stream_class != 0 ); CoreObject *co = stream_class->asClass()->createInstance(clone); if ( userMode >= 0 ) co->setProperty( "_stdStreamType", userMode ); vm->retval(co); return co; } /*# @function stdIn @brief Creates an object mapped to the standard input of the Virtual Machine. @ingroup core_syssupport @return A new valid @a Stream instance on success. The returned read-only stream is mapped to the standard input of the virtual machine hosting the script. Read operations will return the characters from the input stream as they are available. The readAvailable() method of the returned stream will indicate if read operations may block. Calling the read() method will block until some character can be read, or will fill the given buffer up the amount of currently available characters. The returned stream is a clone of the stream used by the Virtual Machine as standard input stream. This means that every transcoding applied by the VM is also available to the script, and that, when running in embedding applications, the stream will be handled by the embedder. As a clone of this stream is held in the VM, closing it will have actually no effect, except that of invalidating the instance returned by this function. Read operations will fail raising an I/O error. */ FALCON_FUNC _stdIn ( ::Falcon::VMachine *vm ) { if( vm->paramCount() == 0 ) { internal_make_stream( vm, vm->stdIn()->clone(), -1 ); } else { // verify streamability of parameter Item *p1 = vm->param(0); if( ! p1->isObject() || ! p1->asObject()->derivedFrom("Stream") ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } //keep the stream internal_make_stream( vm, vm->stdIn()->clone(), -1 ); // this also returns the old stream Stream *orig = (Stream *) p1->asObject()->getFalconData(); Stream *clone = (Stream *) orig->clone(); if ( clone == 0 ) { throw new CloneError( ErrorParam( e_uncloneable, __LINE__ ).origin( e_orig_runtime ) ); } // but change it vm->stdIn( clone ); } } /*# @function stdOut @brief Creates an object mapped to the standard output of the Virtual Machine. @ingroup core_syssupport @return A new valid @a Stream instance on success. The returned stream maps output operations on the standard output stream of the process hosting the script. The returned stream is a clone of the stream used by the Virtual Machine as standard output stream. This means that every transcoding applied by the VM is also available to the script, and that, when running in embedding applications, the stream will be handled by the embedder. As a clone of this stream is held in the VM, closing it will have actually no effect, except that of invalidating the instance returned by this function. Read operations will fail raising an I/O error. */ FALCON_FUNC _stdOut ( ::Falcon::VMachine *vm ) { if( vm->paramCount() == 0 ) { internal_make_stream( vm, vm->stdOut()->clone(), -1 ); } else { // verify streamability of parameter Item *p1 = vm->param(0); if( ! p1->isObject() || ! p1->asObject()->derivedFrom("Stream") ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } //keep the stream internal_make_stream( vm, vm->stdOut()->clone(), -1 ); Stream *orig = (Stream *) p1->asObject()->getFalconData(); Stream *clone = (Stream *) orig->clone(); if ( clone == 0 ) { throw new CloneError( ErrorParam( e_uncloneable, __LINE__ ).origin( e_orig_runtime ) ); } // but change it vm->stdOut( clone ); } } /*# @function stdErr @brief Creates an object mapped to the standard error of the Virtual Machine. @ingroup core_syssupport @return A new valid @a Stream instance on success. The returned stream maps output operations on the standard error stream of the virtual machine hosting the script. The returned stream is a clone of the stream used by the Virtual Machine as standard error stream. This means that every transcoding applied by the VM is also available to the script, and that, when running in embedding applications, the stream will be handled by the embedder. As a clone of this stream is held in the VM, closing it will have actually no effect, except that of invalidating the instance returned by this function. Read operations will fail raising an I/O error. */ FALCON_FUNC _stdErr ( ::Falcon::VMachine *vm ) { if( vm->paramCount() == 0 ) { internal_make_stream( vm, vm->stdErr()->clone(), -1 ); } else { // verify streamability of parameter Item *p1 = vm->param(0); if( ! p1->isObject() || ! p1->asObject()->derivedFrom("Stream") ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } //keep the stream internal_make_stream( vm, vm->stdErr()->clone(), -1 ); Stream *orig = (Stream *) p1->asObject()->getFalconData(); Stream *clone = (Stream *) orig->clone(); if ( clone == 0 ) { throw new CloneError( ErrorParam( e_uncloneable, __LINE__ ).origin( e_orig_runtime ) ); } // but change it vm->stdErr( clone ); } } /*# @function stdInRaw @brief Creates a stream that interfaces the standard input stream of the host process. @ingroup core_syssupport @return A new valid @a Stream instance on success. The returned stream maps input operations on the standard input of the process hosting the script. The returned stream is bound directly with the process input stream, without any automatic transcoding applied. @a Stream.readText will read the text as stream of binary data coming from the stream, unless @a Stream.setEncoding is explicitly called on the returned instance. Closing this stream has the effect to close the standard input stream of the process running the script (if the operation is allowed by the embedding application). Applications trying to write data to the script process will be notified that the script has closed the stream and is not willing to receive data anymore. The stream is read only. Write operations will cause an I/O to be raised. */ FALCON_FUNC stdInRaw ( ::Falcon::VMachine *vm ) { internal_make_stream( vm, new RawStdInStream(), 0 ); } /*# @function stdOutRaw @brief Creates a stream that interfaces the standard output stream of the host process. @ingroup core_syssupport @return A new valid @a Stream instance on success. The returned stream maps output operations on the standard output stream of the process hosting the script. The returned stream is bound directly with the process output, without any automatic transcoding applied. @a Stream.writeText will write the text as stream of bytes to the stream, unless @a Stream.setEncoding is explicitly called on the returned instance. Closing this stream has the effect to close the standard output of the process running the script (if the operation is allowed by the embedding application). Print functions, fast print operations, default error reporting and so on will be unavailable from this point on. Applications reading from the output stream of the process running the scripts, in example, piped applications, will recognize that the script has completed its output, and will disconnect immediately, while the script may continue to run. The stream is write only. Read operations will cause an IoError to be raised. */ FALCON_FUNC stdOutRaw ( ::Falcon::VMachine *vm ) { internal_make_stream( vm, new RawStdOutStream(), 1 ); } /*# @function stdErrRaw @brief Creates a stream that interfaces the standard error stream of the host process. @ingroup core_syssupport @return A new valid @a Stream instance on success. The returned stream maps output operations on the standard error stream of the process hosting the script. The returned stream is bound directly with the process error stream, without any automatic transcoding applied. @a Stream.writeText will write the text as stream of bytes to the stream, unless @a Stream.setEncoding is explicitly called on the returned instance. Closing this stream has the effect to close the standard error stream of the process running the script (if the operation is allowed by the embedding application). Applications reading from the error stream of the script will be notified that the stream has been closed, and won't be left pending in reading this stream. The stream is write only. Read operations will cause an I/O to be raised. */ FALCON_FUNC stdErrRaw ( ::Falcon::VMachine *vm ) { internal_make_stream( vm, new RawStdErrStream(), 2 ); } /*# @endset */ /*# @function systemErrorDescription @ingroup general_purpose @brief Returns a system dependent message explaining an integer error code. @param errorCode A (possibly) numeric error code that some system function has returned. @return A system-specific error description. This function is meant to provide the users (and the developers) with a minimal support to get an hint on why some system function failed, without having to consult the system manual pages. The fsError field of the Error class can be fed directly inside this function. */ FALCON_FUNC systemErrorDescription ( ::Falcon::VMachine *vm ) { Item *number = vm->param(0); if ( ! number->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } CoreString *str = new CoreString; ::Falcon::Sys::_describeError( number->forceInteger(), *str ); vm->retval( str ); } /*# @method setBuffering Stream @brief Set the buffering state of this stream. @param size Buffering size; pass 0 to disable. This method activates or unactivates I/O buffering on this stream. When buffering is active, every read/write operation is first cached in memory, provided the size of the memory buffer is wide enough to store the data being written or to provide the data being read. Seek operations invalidate the buffer, that is automatically flushed when necessary. Local filesystem providers and standard I/O streams are buffered by default; other streams may be created with buffering enabled or not, buffered or not depending on their and common usage patterns (network streams are usually unbuffered). You may want to disable buffering when preparing binary data in memory, or parsing big chunks of binary data at once via block (MemBuf) read/write operations. However, notice that buffering is always optimizing when chunk width is 1/4 of the buffer size or less, and causes only minor overhead in the other cases. */ FALCON_FUNC Stream_setBuffering ( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Stream *file = dyncast( self->getFalconData() ); Item *i_size = vm->param(0); if ( i_size == 0 || ! i_size->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "N") ); } int64 size = i_size->forceInteger(); // Buffering lies between transcoders and the final stream. // We need to find the first non-transcoder in the hierarchy. Stream *sub = file; Transcoder* ts = 0; // lowermost transcoder. while( sub->isTranscoder() ) { ts = dyncast( sub ); sub = ts->underlying(); } // detach the lowermost transcoder. if ( ts != 0 ) ts->detach(); // Ok, the sub stream may be a buffering stream or the final stream itself. if ( sub->isStreamBuffer() ) { StreamBuffer* sb = dyncast( sub ); // do we want to disable it? if( size <= 0 ) { sub = sb->underlying(); sb->flush(); sb->detach(); delete sb; // re-attach the unbuffered stream to the transcoders, // or set it as the new user data. if( ts != 0 ) { // reflected streams are always owned. ts->setUnderlying( sub, true ); } else self->setUserData( sub ); } else { // we just want to resize it. if ( ! sb->resizeBuffer( (uint32) size ) ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .origin( e_orig_runtime ) .sysError( (uint32) sb->underlying()->lastError() ) ); } } } else { // if we want to enable it, we need to create a bufferer. if ( size > 0 ) { StreamBuffer* sb = new StreamBuffer( sub, true, (uint32) size ); // attach the newly buffered stream to the transcoders, // or set it as the new user data. if( ts != 0 ) { // reflected streams are always owned. ts->setUnderlying( sb, true ); } else self->setUserData( sb ); } } } /*# @method getBuffering Stream @brief Returns the size of I/O buffering active on this stream. @return 0 if the stream is unbuffered or a positive number if it is buffered. @see Stream.setBuffering */ FALCON_FUNC Stream_getBuffering ( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Stream *file = dyncast( self->getFalconData() ); // Buffering lies between transcoders and the final stream. // We need to find the first non-transcoder in the hierarchy. Stream *sub = file; while( sub->isTranscoder() ) { Transcoder* ts = dyncast( sub ); sub = ts->underlying(); } // if it's a streambuffer, we have buffering enabled if( sub->isStreamBuffer() ) { StreamBuffer* sb = dyncast( sub ); vm->retval( (int64) sb->bufferSize() ); } else { // buffering is disabled. vm->retval( 0 ); } } /*# @method setEncoding Stream @brief Set the text encoding and EOL mode for text-based operations. @param encoding Name of the encoding that is used for the stream. @optparam EOLMode How to treat end of line indicators. This method sets an encoding that will affect readText() and writeText() methods. Provided encodings are: - "utf-8" - "utf-16" - "utf-16BE" - "utf-16LE" - "iso8859-1" to "iso8859-15" - "gbk" (Chinese simplified) - "cp1252" - "C" (byte oriented -- writes byte per byte) As EOL manipulation is also part of the text operations, this function allows to chose how to deal with EOL characters stored in Falcon strings when writing data and how to parse incoming EOL. Available values are: - CR_TO_CR: CR and LF characters are untranslated - CR_TO_CRLF: When writing, CR ("\n") is translated into CRLF, when reading CRLF is translated into a single "\n" - SYSTEM_DETECT: use host system policy. If not provided, this parameter defaults to SYSTEM_DETECT. If the given encoding is unknown, a ParamError is raised. */ FALCON_FUNC Stream_setEncoding ( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Stream *file = dyncast( self->getFalconData() ); Item *i_encoding = vm->param(0); Item *i_eolMode = vm->param(1); if ( i_encoding == 0 || ! i_encoding->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } int mode = ( i_eolMode == 0 ? SYSTEM_DETECT : (int) i_eolMode->forceInteger()); if( mode != SYSTEM_DETECT && mode != CR_TO_CR && mode != CR_TO_CRLF ) { mode = SYSTEM_DETECT; } // find the first non-transcoder stream entity while( file->isTranscoder() ) { Transcoder* tc = dyncast(file); file = tc->underlying(); tc->detach(); delete tc; } // just in case, set the stream back in place. self->setUserData( file ); Transcoder *trans = TranscoderFactory( *(i_encoding->asString()), file, true ); if ( trans == 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ).origin( e_orig_runtime ) ); } Stream *final; if ( mode == SYSTEM_DETECT ) { final = AddSystemEOL( trans ); } else if( mode == CR_TO_CRLF ) { final = new TranscoderEOL( trans, true ); } else final = trans; self->setUserData( final ); self->setProperty( "encoding", *i_encoding ); self->setProperty( "eolMode", (int64) mode ); } /*# @function readURI @brief Reads fully data from a given file or URI source. @param uri The item to be read (URI or string) @optparam encoding The encoding. @return A string containing the whole contents of the given file. @raise IoError in case of read error. This function reads as efficiently as possible a file from the given source. If encoding isn't given, the file is read as binary data. Provided encodings are: - "utf-8" - "utf-16" - "utf-16BE" - "utf-16LE" - "iso8859-1" to "iso8859-15" - "gbk" (Chinese simplified) - "cp1252" - "C" (byte oriented -- writes byte per byte) @note The maximum size of the data that can be read is limited to 2 Gigabytes. */ FALCON_FUNC readURI ( ::Falcon::VMachine *vm ) { #define READURI_READ_BLOCK_SIZE 2048 Item *i_uri = vm->param(0); Item *i_encoding = vm->param(1); URI uri; if ( i_encoding != 0 && ! ( i_encoding->isString()|| i_encoding->isNil()) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S|URI, [S]" ) ); } if ( i_uri->isString() ) { uri.parse( *i_uri->asString() ); if( ! uri.isValid() ) { throw new ParamError( ErrorParam( e_malformed_uri, __LINE__ ) .extra( *i_uri->asString() ) ); } } else if ( i_uri->isOfClass( "URI" ) ) { uri = dyncast( i_uri->asObjectSafe() )->uri(); } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S|URI, [S]" ) ); } // find the appropriate provider. VFSProvider* vfs = Engine::getVFS( uri.scheme() ); if ( vfs == 0 ) { throw new ParamError( ErrorParam( e_unknown_vfs, __LINE__ ) .extra( uri.scheme() ) ); } // Idling the VM here; we're starting to access the system. vm->idle(); Stream *in = vfs->open( uri, VFSProvider::OParams().rdOnly() ); if ( in == 0 ) { vm->unidle(); throw vfs->getLastError(); } FileStat fs; int64 len; if( ! vfs->readStats( uri, fs ) ) { // we know the file exists; this means that the vfs doesn't provide file length. len = -1; } else { len = fs.m_size; } String *ret = new CoreString(); // direct read? if ( i_encoding == 0 || i_encoding->isNil() ) { if ( len >= 0 && len < 2000000000 ) ret->reserve( (uint32) len ); int64 pos = 0; while( ! in->eof() ) { int rin = 0; if ( len >= 0 && len < 2000000000 ) { rin = in->read( ret->getRawStorage() + pos, (int32)(len+1) ); // so we hit immediately EOF } else { ret->reserve( (int32) pos + READURI_READ_BLOCK_SIZE ); rin = in->read( ret->getRawStorage() + pos, READURI_READ_BLOCK_SIZE ); } if ( rin < 0 ) { vm->unidle(); int64 fsError = in->lastError(); delete in; throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra( uri.get() ) .sysError( (int32) fsError ) ); } pos += rin; } ret->size( (uint32) pos ); delete in; } else { // text read. Stream* tin = TranscoderFactory( *i_encoding->asString(), in, true ); String temp; while( ! tin->eof() ) { bool res; if ( len >= 0 && len < 2000000000 ) { res = tin->readString( temp, (uint32) len ); } else { res = tin->readString( temp, READURI_READ_BLOCK_SIZE ); } if ( ! res ) { vm->unidle(); int64 fsError = tin->lastError(); delete in; throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra( uri.get() ) .sysError( (uint32) fsError ) ); } ret->append( temp ); } delete tin; } vm->unidle(); vm->retval( ret ); } /*# @function writeURI @brief Writes fully data to a given file or URI source. @param uri The item to be read (URI or string) @param data A string or membuf containing the data to be written. @optparam encoding The encoding. @raise IoError in case of write errors. This function writes all the data contained in the string or memory buffer passed in the @b data parameter into the @b uri output resource. If @b encoding is not given, the data is treated as binary data and written as-is. Provided encodings are: - "utf-8" - "utf-16" - "utf-16BE" - "utf-16LE" - "iso8859-1" to "iso8859-15" - "gbk" (Chinese simplified) - "cp1252" - "C" (byte oriented -- writes byte per byte) */ FALCON_FUNC writeURI ( ::Falcon::VMachine *vm ) { #define WRITEURI_WRITE_BLOCK_SIZE 2048 Item *i_uri = vm->param(0); Item *i_data = vm->param(1); Item *i_encoding = vm->param(2); URI uri; if ( i_data == 0 || ! ( i_data->isString() ) || (i_encoding != 0 && ! (i_encoding->isString() || i_encoding->isNil())) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S|URI, S, [S]" ) ); } if ( i_uri->isString() ) { uri.parse( *i_uri->asString() ); if( ! uri.isValid() ) { throw new ParamError( ErrorParam( e_malformed_uri, __LINE__ ) .extra( *i_uri->asString() ) ); } } else if ( i_uri->isOfClass( "URI" ) ) { uri = dyncast( i_uri->asObjectSafe() )->uri(); } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S|URI, S, [S]" ) ); } // find the appropriate provider. VFSProvider* vfs = Engine::getVFS( uri.scheme() ); if ( vfs == 0 ) { throw new ParamError( ErrorParam( e_unknown_vfs, __LINE__ ) .extra( uri.scheme() ) ); } // Idling the VM here; we're starting to access the system. vm->idle(); VFSProvider::CParams params; params.truncate(); params.wrOnly(); Stream *out = vfs->create( uri, params ); if ( out == 0 ) { vm->unidle(); throw vfs->getLastError(); } // direct read? if ( i_encoding == 0 || i_encoding->isNil() ) { byte* data = i_data->asString()->getRawStorage(); uint32 size = i_data->asString()->size(); uint32 pos = 0; while( pos < size ) { int wout = out->write( data + pos, size - pos ); // so we hit immediately EOF if ( wout < 0 ) { vm->unidle(); int64 fsError = out->lastError(); delete out; throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra( uri.get() ) .sysError( (uint32) fsError ) ); } pos += wout; } delete out; } else { // text read. Stream* tout = TranscoderFactory( *i_encoding->asString(), out, true ); if ( ! tout->writeString( *i_data->asString() ) ) { vm->unidle(); int64 fsError = tout->lastError(); delete tout; throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra( uri.get() ) .sysError( (uint32) fsError ) ); } delete tout; } vm->unidle(); } }} /* end of file.cpp */ engine/core_module/format_ext.cpp000066400000000000000000000312201176363201700175000ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: format_ext.cpp The Format pretty print formatter class. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 14 Aug 2008 02:06:31 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "core_module.h" #include #include #include /*# @beginmodule core */ namespace Falcon { namespace core { /*# @class Format @brief Controls the rendering of items into strings. @ingroup general_purpose @optparam fmtspec If provided, must be a valid format specifier which is immediately parsed. In case of invalid format, a ParseError is raised. Format class is meant to provide an efficient way to format variables into strings that can then be sent to output streams. Internally, the format class is used in string expansion (the '\@' operator), but while string expansion causes a string parsing to be initiated and an internal temporary Format object to be instantiated each time an expansion is performed, using a prebuilt Format object allows to optimize repeated formatting operations. Also, Format class instances may be used as other objects properties, applied directly to strings being written on streams, modified after being created and are generally more flexible than the string expansion. The format specifier is a string that may contain various elements indicating how the target variable should be rendered as a string. @section format_specification Format specification @b Size: The minimum field length; it can be just expressed by a number. if the formatted output is wide as or wider than the allocated size, the output will not be truncated, and the resulting string may be just too wide to be displayed where it was intended to be. The size can be mandatory by adding '*' after it. In this case, the format() method will return false (and eventually raise an error) if the conversion caused the output to be wider than allowed. @b Padding: the padding character is appended after the formatted size, or it is put in front of it if alignment is to the right. To define padding character, use 'p' followed by the character. In example, p0 to fill the field with zeros. Of course, the character may be any Unicode character (the format string accepts standard Falcon character escapes). In the special case of p0, front sign indicators are placed at the beginning of the field; in example "4p0+" will produce "+001" "-002" and so on, while "4px+" will produce "xx+1", "xx-2" etc. @b Numeric @b base: the way an integer should be rendered. It may be: - Decimal: as it's the default translation, no command is needed; a 'N' character may be added to the format to specify that we are actually expecting a number. - Hexadecimal: Command may be 'x' (lowercase hex), 'X' (uppercase Hex), 'c' (0x prefixed lowercase hex) or 'C' (0x prefixed uppercase hex). Binary: 'b' to convert to binary, and 'B' to convert to binary and add a "b" after the number. - Octal: 'o' to display an octal number, or '0' to display an octal with "0" prefix. - Scientific: 'e' to display a number in scientific notation W.D+/-eM. Format of numbers in scientific notation is fixed, so thousand separator and decimal digit separator cannot be set, but decimals cipher setting will still work. @b Decimals: a dot '.' followed by a number indicates the number of decimal to be displayed. If no decimal is specified, floating point numbers will be displayed with all significant digits digits, while if it's set to zero, decimal numbers will be rounded. @b Decimal @b separator: a 'd' followed by any non-cipher character will be interpreted as decimal separator setting. For example, to use central European standard for decimal numbers and limit the output to 3 decimals, write ".3d,", or "d,.3". The default value is '.'. @b (Thousands) @b Grouping: actually it's the integer part group separator, as it will be displayed also for hexadecimal, octal and binary conversions. It is set using 'g' followed by the separator character, it defaults to ','. Normally, it is not displayed; to activate it set also the integer grouping digit count; normally is 3, but it's 4 in Japanese and Chinese locales, while it may be useful to set it to 2 or 4 for hexadecimal, 3 for octal and 4 or 8 for binary. In example 'g4-' would group digits 4 by 4, grouping them with a "-". Zero would disable grouping. @b Grouping @b Character: If willing to change only the grouping character and not the default grouping count, use 'G'. @b Alignment: by default the field is aligned to the left; to align the field to the right use 'r'. @b Negative @b display @b format: By default, a '-' sign is appended in front of the number if it's negative. If the '+' character is added to the format, then in case the number is positive, '+' will be appended in front. '--' will postpend a '-' if the number is negative, while '++' will postpend either '+' or '-' depending on the sign of the number. To display a parenthesis around negative numbers, use '[', or use ']' to display a parenthesis for negative numbers and use the padding character in front and after positive numbers. Using parenthesis will prevent using '+', '++' or '--' formats. Format '-^' will add a - in front of padding space if the number is negative, while '+^' will add plus or minus depending on number sign. In example, "5+" would render -12 as " -12", while "5+^" will render as "- 12". If alignment is to the right, the sign will be added at the other side of the padding: "5+^r" would render -12 as "12 -". If size is not mandatory, parenthesis will be wrapped around the formatted field, while if size is mandatory they will be wrapped around the whole field, included padding. In example "5[r" on -4 would render as " (4)", while "5*[r" would render as "( 4)". @b Nil @b format: How to represent a nil. It may be one of the following: - 'nn': nil is not represented (mute). - 'nN': nil is represented by "N" - 'nl': nil is rendered with "nil" - 'nL': nil is rendered with "Nil". This is also the default. - 'nu': nil is rendered with "Null" - 'nU': nil is rendered with "NULL" - 'no': nil is rendered with "None" - 'nA': nil is rendered with "NA" @b Action @b on @b error: Normally, if trying to format something different from what is expected, the method format() will simply return false. In example, to format a string in a number, a string using the date formatter, a number in a simple pad-and-size formatter etc. To change this behavior, use '/' followed by one of the following: - 'n': act as the wrong item was nil (and uses the defined nil formatter). - '0': act as if the given item was 0, the empty string or an invalid date, or anyhow the neuter item of the expected type. - 'r': raise a type error. A 'c' letter may be added after the '/' and before the specifier to try a basic conversion into the expected type before triggering the requested effect. In example, if the formatted item is an object and the conversion type is string (that is, no numeric related options are set), this will cause the toString() method of the target object to be called, or if not available, the toString() function to be applied on the target object. In example "6/cr" tries to convert the item to a 6 character long string, and if it fails (i.e. because toString() method returns nil) an TypeError is raised. @b Object @b specific @b format: Objects may accept an object specific formatting as parameter of the standard toString() method. A pipe separator '|' will cause all the following format to be passed unparsed to the toString() method of objects eventually being formatted. If the object does not provides a toString() method, or if it's not an object at all, an error will be raised. The object is the sole responsible for parsing and applying its specific format. @note Ranges will be represented as [n1:n2:n3] or [n1:] if they are open. Size, alignment and padding will work on the whole range, while numeric formatting will be applied to each end of the range. Example: the format specifier "8*Xs-g2" means to format variables in a field of 8 characters, size mandatory (i.e. truncated if wider), Hexadecimal uppercase, grouped 2 by 2 with '-' characters. A result may be "0A-F1-DA". Another example: "12.3'0r+/r" means to format a number in 12 ciphers, of which 3 are fixed decimals, 0 padded, right aligned; a '+' is always added in front of positive numbers. In case the formatted item is not a number, a type error is raised. Format class instances may be applied on several variables; in example, a currency value oriented numeric format may be applied on all the currency values of a program, and changing the default format would just be a matter of changing just one format object. */ /*# @method parse Format @brief Initializes the Format instance with an optional value. @param fmtspec Format specifier @raise ParseError if the format specifier is not correct. Sets or changes the format specifier for this Format instance. If the format string is not correct, a ParseError is raised. */ FALCON_FUNC Format_parse ( ::Falcon::VMachine *vm ) { CoreObject *einst = vm->self().asObject(); Format *fmt = (Format *) einst->getFalconData(); Item *param = vm->param( 0 ); if ( param != 0 ) { if( ! param->isString() ) { throw new ParamError( ErrorParam( e_inv_params ).extra( "[S]" ) ); } else { fmt->parse( *param->asString() ); if( ! fmt->isValid() ) { throw new ParseError( ErrorParam( e_param_fmt_code ) ); } } } } FALCON_FUNC Format_init ( ::Falcon::VMachine *vm ) { FalconObject *einst = static_cast( vm->self().asObject() ); Format *fmt = new Format; einst->setUserData( fmt ); Format_parse( vm ); } /*# @method format Format @brief Performs desired formatting on a target item. @param item The item to be formatted @optparam dest A string where to store the formatted data. @return A formatted string @raise ParamError if a format specifier has not been set yet. @raise TypeError if the format specifier can't be applied the item because of incompatible type. Formats the variable as per the given format descriptor. If the class has been instantiated without format, and the parse() method has not been called yet, a ParamError is raised. If the type of the variable is incompatible with the format descriptor, the method returns nil; a particular format specifier allows to throw a TypeError in this case. On success, the method returns a string containing a valid formatted representation of the variable. It is possible to provide a pre-allocated string where to store the formatted result to improve performance and spare memory. */ FALCON_FUNC Format_format ( ::Falcon::VMachine *vm ) { CoreObject *einst = vm->self().asObject(); Format *fmt = dyncast( einst->getFalconData() ); Item *param = vm->param( 0 ); Item *dest = vm->param( 1 ); if( param == 0 || ( dest != 0 && ! dest->isString() ) ) { throw new ParamError( ErrorParam( e_inv_params ).extra( "X,[S]" ) ); } else { CoreString *tgt; if( dest != 0 ) { tgt = dest->asCoreString(); } else { tgt = new CoreString; } if( ! fmt->format( vm, *param, *tgt ) ) vm->retnil(); else vm->retval( tgt ); } } /*# @method toString Format @brief Represents this format as a string. @return The format specifier. Returns a string representation of the format instance. */ FALCON_FUNC Format_toString ( ::Falcon::VMachine *vm ) { FalconObject *einst = dyncast< FalconObject*>( vm->self().asObject() ); Format *fmt = dyncast< Format*>( einst->getFalconData() ); vm->retval( new CoreString( fmt->originalFormat()) ); } } } /* end of format_ext.fal */ engine/core_module/function_ext.cpp000066400000000000000000000116501176363201700200420ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: function_ext.cpp Methods of the function class. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 22 Mar 2009 00:12:42 +0100 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "core_module.h" #include /*# @beginmodule core */ namespace Falcon { namespace core { /*# @method name Function @brief Gets the symbolic name of the given function. @return A string containing the function name This is useful if the function symbol or has been re-assigned to temporary variables, or if it is applied to the @b fself keyword. */ FALCON_FUNC Function_name ( ::Falcon::VMachine *vm ) { if ( vm->self().isFunction() ) { vm->retval( vm->self().asFunction()->symbol()->name() ); } else { vm->retnil(); } } static void s_caller_internal( VMachine* vm, bool mode ) { uint32 level; Item *i_level = vm->param(0); if( i_level != 0 ) { if( i_level->isOrdinal() || i_level->isNil() ) { int64 i64level = i_level->forceInteger(); if( i64level < 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin(e_orig_runtime) .extra( ">=0" ) ); } level = (uint32)i64level+1; } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin(e_orig_runtime) .extra( "[N]" ) ); } } else { level = 1; } if( mode ) { Item caller; if ( vm->getCallerItem( caller, level ) ) { vm->retval( caller ); return; } } else { const Symbol *sym; uint32 line; uint32 pc; if( vm->getTraceStep( level, sym, line, pc ) ) { CoreArray* arr = new CoreArray( 3 ); arr->append( sym->name() ); arr->append( sym->module()->name() ); arr->append( sym->module()->path() ); arr->append( (int64) line ); arr->append( (int64) pc ); vm->retval( arr ); return; } } // we're not called. vm->retnil(); } /*# @method caller Function @brief Gets the direct caller or one of the calling ancestors. @optparam level Caller level (starting from zero, the default). @return The item having performed the nth call. This function returns the n-th caller (zero based) that caused this function to be called. It may be a function, a method or another callable item from which the call has originated. @note The method can also be called statically on the Function metaclass. */ FALCON_FUNC Function_caller ( ::Falcon::VMachine *vm ) { s_caller_internal( vm, true ); } /*# @method trace Function @brief Gets a trace step in the call stack. @optparam level Caller level (starting from zero, the default). @return An array containing the data relative to the given trace level. The returned data is organized as follows: @code [ 'symbol name', 'module name', 'module path', line_in_module, PC_in_vm] @endcode @note The method can also be called statically on the Function metaclass. */ FALCON_FUNC Function_trace ( ::Falcon::VMachine *vm ) { s_caller_internal( vm, false ); } /*# @method stack Function @brief Gets a string representation of the call stack. @return A string containing all the stack trace up to this point. The returned string looks like the stack trace printed by errors, but is referred to the call stack up to the function from which this method is called. @note The method can also be called statically on the Function metaclass. */ FALCON_FUNC Function_stack ( ::Falcon::VMachine *vm ) { const Symbol *sym; uint32 line; uint32 pc; int level = 0; CoreString& target = *(new CoreString()); while( vm->getTraceStep( level, sym, line, pc ) ) { if ( target.size() ) target += "\n"; const Module* mod = sym->module(); if ( mod->path().size() ) { target += "\"" + mod->path() + "\" "; } target += mod->name() + "." + sym->name() + ":"; target.writeNumber( (int64) line ); target += "(PC:"; switch( pc ) { case VMachine::i_pc_call_external: target += "ext"; break; case VMachine::i_pc_call_external_return: target += "ext.r"; break; case VMachine::i_pc_redo_request: target += "redo"; break; case VMachine::i_pc_call_external_ctor: target += "ext.c"; break; case VMachine::i_pc_call_external_ctor_return: target += "ext.cr"; break; default: target.writeNumber( (int64) pc ); } target += ")"; ++level; } vm->retval( &target ); } } } /* end of functional_ext.cpp */ engine/core_module/functional_ext.cpp000066400000000000000000001761051176363201700203660ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: functional_ext.cpp Functional programming support ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 14 Aug 2008 02:10:57 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "core_module.h" /*# @beginmodule core */ namespace Falcon { namespace core { /*# @funset functional_support Functional programming support @brief ETA functions and functional constructs. Falcon provides some special functional programming constructs that are known to the VM to have special significance. The vast majority of them starts a "functional evaluation" chain on their parameters before their value is evaluated. A functional evaluation is a recursive evaluation (reduction) of list structures into atoms. At the moment, the only list structure that can be evaluated this way is the array. Evaluating a parameter in functional context means that the given parameter will be recursively scanned for callable arrays or symbols that can be reduced to atoms. A callable array is reduced by calling the function and substituting it with its return value. When all the contents of the list are reduced, the higher level is evaluated. Consider this example: @code function func0( p0, p1 ): ... function func1( p0 ): ... list = [func0, [func1, param1], param2] @endcode Calling @b list as a callable array, func0 will be called with the array [func1, param1] as the first parameter, and param2 as the second parameter. On the other hand, evaluating the above list in a functional context, first func1 will be called with param1, then func0 will be called with the return value of the previous evaluation as the first parameter, and with param2 as the second parameter. The functions in this section are considered "special constructs" as the VM knows them and treats them specially. Their definition overrides the definition of a functional evaluation, so that when the VM finds a special construct in its evaluation process, it ceases using the default evaluation algorithm and passes evaluation control to the construct. For example, the iff construct selects one of its branches to be evaluated only if the first parameter evaluates to true: @code list = [iff, someValueIsTrue, [func0, [func1, param1]], [func1, param2] ] @endcode If this list had to be evaluated in a functional context, then before iff had a chance to decide what to do, the two arrays [func0, ...] and [func1,...] would have been evaluated. As iff is a special construct, the VM doesn't evaluate its parameters and lets iff perform its operations as it prefer. In the case of iff, it first evaluates the first parameter, then evaluates in functional context the second on the third parameter, leaving unevaluated the other one. Not all constructs evaluates everything it is passed to them in a functional context. Some of them are meant exactly to treat even a callable array (or anything else that should be reduced) as-is, stopping the evaluation process as the VM meets them. The description of each construct explains its working principles, and whether if its parameters are evaluated or not. Please, notice that "callable" doesn't necessarily mean "evaluable". To evaluate in functional context a callable symbol without parameter, it must be transformed into a single-element array. For example: @code function func0(): ... result = [iff, shouldEval, [func0], func0] @endcode This places in result the value returned by func0 if shouldEval is true, while it returns exactly the function object func0 as-is if shouldEval is false. A more formal definition of the functional programming support in Falcon is provided in the Survival Guide. */ static bool core_any_next( ::Falcon::VMachine *vm ) { // was the elaboration succesful? if ( vm->regA().isTrue() ) { vm->regA().setBoolean( true ); return false; } // repeat checks. CoreArray *arr = vm->param(0)->asArray(); uint32 count = (uint32) vm->local(0)->asInteger(); while( count < arr->length() ) { Item *itm = &arr->at(count); *vm->local(0) = (int64) count+1; if ( vm->functionalEval( *itm ) ) { return true; } else if ( vm->regA().isTrue() ) { vm->regA().setBoolean( true ); return false; } count++; } vm->regA().setBoolean( false ); return false; } /*# @function any @inset functional_support @brief Returns true if any of the items in a given collection evaluate to true. @param sequence A sequence of arbitrary items. @return true at least one item in the collection is true, false otherwise. Items in @b sequence are evaluated in functional context for truth value. This means that, if they are sigmas, they get sigma-reduced and their return value is evaluated, otherwise they are evaluated directly. Truth value is determined using the standard Falcon truth check (nil is false, numerics are true if not zero, strings and collections are true if not empty, object and classes are always true). The check is short circuited. This means that elements are evaluated until an element considered to be true (or sigma-reduced to a true value) is found. If the collection is empty, this function returns false. */ FALCON_FUNC core_any ( ::Falcon::VMachine *vm ) { Item *i_param = vm->param(0); if( i_param == 0 || !i_param->isArray() ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "A" ) ); } CoreArray *arr = i_param->asArray(); uint32 count = arr->length(); vm->returnHandler( &core_any_next ); vm->addLocals(1); for( uint32 i = 0; i < count; i ++ ) { Item *itm = &arr->at(i); *vm->local(0) = (int64) i+1; if ( vm->functionalEval( *itm ) ) { return; } else if ( vm->regA().isTrue() ) { vm->returnHandler( 0 ); vm->regA().setBoolean( true ); return; } } vm->returnHandler( 0 ); vm->regA().setBoolean( false ); } static bool core_all_next( ::Falcon::VMachine *vm ) { // was the elaboration succesfull? if ( ! vm->regA().isTrue() ) { vm->regA().setBoolean( false ); return false; } // repeat checks. CoreArray *arr = vm->param(0)->asArray(); uint32 count = (uint32) vm->local(0)->asInteger(); while( count < arr->length() ) { Item *itm = &arr->at(count); *vm->local(0) = (int64) count+1; if ( vm->functionalEval( *itm ) ) { return true; } else if ( ! vm->regA().isTrue() ) { vm->regA().setBoolean( false ); return false; } count++; } vm->regA().setBoolean( true ); return false; } /*# @function all @inset functional_support @brief Returns true if all the items in a given collection evaluate to true. @param sequence A sequence of arbitrary items. @return true if all the items are true, false otherwise Items in @b sequence are evaluated in functional context for truth value. This means that, if they are sigmas, they get sigma-reduced and their return value is evaluated, otherwise they are evaluated directly. Truth value is determined using the standard Falcon truth check (nil is false, numerics are true if not zero, strings and collections are true if not empty, object and classes are always true). The check is short circuited. This means that the processing of parameters is interrupted as an element is evaluated into false. If the collection is empty, this function returns false. */ FALCON_FUNC core_all ( ::Falcon::VMachine *vm ) { Item *i_param = vm->param(0); if( i_param == 0 || !i_param->isArray() ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "A" ) ); } CoreArray *arr = i_param->asArray(); uint32 count = arr->length(); if ( count == 0 ) { vm->regA().setBoolean( false ); return; } vm->returnHandler( &core_all_next ); vm->addLocals(1); for( uint32 i = 0; i < count; i ++ ) { Item *itm = &arr->at(i); *vm->local(0) = (int64) i+1; if ( vm->functionalEval( *itm ) ) { return; } else if ( ! vm->regA().isTrue() ) { vm->returnHandler( 0 ); vm->regA().setBoolean( false ); return; } } vm->returnHandler( 0 ); vm->regA().setBoolean( true ); } static bool core_anyp_next( ::Falcon::VMachine *vm ) { // was the elaboration succesfull? if ( vm->regA().isTrue() ) { vm->regA().setBoolean( true ); return false; } // repeat checks. int32 count = (uint32) vm->local(0)->asInteger(); while( count < vm->paramCount() ) { Item *itm = vm->param( count ); *vm->local(0) = (int64) count+1; if ( vm->functionalEval( *itm ) ) { return true; } else if ( vm->regA().isTrue() ) { vm->regA().setBoolean( true ); return false; } count++; } vm->regA().setBoolean( false ); return false; } /*# @function anyp @inset functional_support @brief Returns true if any one of the parameters evaluate to true. @param ... A list of arbitrary items. @return true at least one parameter is true, false otherwise. This function works like @a any, but the sequence may be specified directly in the parameters rather being given in a separate array. This make easier to write anyp in callable arrays. For example, one may write @code [anyp, 1, k, n ...] @endcode while using any one should write @code [any, [1, k, n ...]] @endcode Parameters are evaluated in functional context. This means that, if they are sigmas, they get sigma-reduced and their return value is evaluated, otherwise they are evaluated directly. Truth value is determined using the standard Falcon truth check (nil is false, numerics are true if not zero, strings and collections are true if not empty, object and classes are always true). If called without parameters, this function returns false. */ FALCON_FUNC core_anyp ( ::Falcon::VMachine *vm ) { uint32 count = vm->paramCount(); vm->returnHandler( &core_anyp_next ); vm->addLocals(1); for( uint32 i = 0; i < count; i ++ ) { Item *itm = vm->param(i); *vm->local(0) = (int64) i+1; if ( vm->functionalEval( *itm ) ) { return; } else if ( vm->regA().isTrue() ) { vm->returnHandler( 0 ); vm->regA().setBoolean( true ); return; } } vm->returnHandler( 0 ); vm->regA().setBoolean( false ); } static bool core_allp_next( ::Falcon::VMachine *vm ) { // was the elaboration succesfull? if ( ! vm->regA().isTrue() ) { vm->regA().setBoolean( false ); return false; } // repeat checks. int32 count = (uint32) vm->local(0)->asInteger(); while( count < vm->paramCount() ) { Item *itm = vm->param(count); *vm->local(0) = (int64) count+1; if ( vm->functionalEval( *itm ) ) { return true; } else if ( ! vm->regA().isTrue() ) { vm->regA().setBoolean( false ); return false; } count++; } vm->regA().setBoolean( true ); return false; } /*# @function allp @inset functional_support @brief Returns true if all the parameters evaluate to true. @param ... An arbitrary list of items. @return true if all the items are true, false otherwise This function works like @a all, but the collection may be specified directly in the parameters rather being given in a separate array. This make easier to write allp in callable arrays. For example, one may write @code [allp, 1, k, n ...] @endcode while using all one should write @code [all, [1, k, n ...]] @endcode Parameters are evaluated in functional context. This means that, if they are sigmas, they get sigma-reduced and their return value is evaluated, otherwise they are evaluated directly. Truth value is determined using the standard Falcon truth check (nil is false, numerics are true if not zero, strings and collections are true if not empty, object and classes are always true). If called without parameters, this function returns false. */ FALCON_FUNC core_allp ( ::Falcon::VMachine *vm ) { uint32 count = vm->paramCount(); vm->returnHandler( &core_allp_next ); vm->addLocals(1); if ( count == 0 ) { vm->retval(0); return; } for( uint32 i = 0; i < count; i ++ ) { Item *itm = vm->param(i); *vm->local(0) = (int64) i+1; if ( vm->functionalEval( *itm ) ) { return; } else if ( ! vm->regA().isTrue() ) { vm->returnHandler( 0 ); vm->regA().setBoolean( false ); return; } } vm->returnHandler( 0 ); vm->retval( 1 ); } /*# @function eval @inset functional_support @brief Evaluates a sequence in functional context. @param sequence A sequence to be evaluated. @return The sigma-reduction (evaluation) result. The parameter is evaluated in functional context; this means that if the parameter is a sequence starting with a callable item, that item gets called with the rest of the sequence passed as parameters, and the result it returns is considered the "evaluation result". This is performed recursively, inner-to-outer, on every element of the sequence before the call to the first element is actually performed. The description of the functional evaluation algorithm is included in the heading of this section. */ FALCON_FUNC core_eval ( ::Falcon::VMachine *vm ) { Item *i_param = vm->param(0); if( i_param == 0 ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "X" ) ); } uint32 pcount = vm->paramCount() - 1; for ( uint32 i = pcount; i > 0; i-- ) { vm->pushParameter( *vm->param(i) ); } vm->functionalEval( *i_param, pcount ); } /*# @function valof @inset functional_support @brief Calls callable items or returns non callable ones. @param item The item to be checked. @return The item if it is not callable, or the call return value. The name function is a short for @i extended @i value. It is meant to determine if the passed item is a non-callable value or if it should be called to determine a value. Performing this check at script level time consuming and often clumsy, and this function is easily used in functional sequences. */ FALCON_FUNC core_valof ( ::Falcon::VMachine *vm ) { if ( vm->paramCount() == 0 ) { vm->retnil(); return; } Item *elem = vm->param( 0 ); if( elem->isCallable() ) vm->callFrame( *elem, 0 ); else vm->retval( *elem ); } /*# @function min @inset functional_support @brief Picks the minimal value among its parameters. @param ... The items to be checked. @return The smallest item in the sequence. This function performs a lexicographic minority check on each element passed as a parameter, returning the smallest of them. A standard VM comparison is performed, so the standard ordering rules apply. This also means that objects overloading the @a BOM.compare method may provide specialized ordering rules. If more than one item is found equal and lesser than all the others, the first one is returned. If the function is called without parameters, it returns @b nil. */ FALCON_FUNC core_min ( ::Falcon::VMachine *vm ) { if ( vm->paramCount() == 0 ) { vm->retnil(); return; } Item *elem = vm->param( 0 ); for ( int32 i = 1; i < vm->paramCount(); i++) { if ( *vm->param(i) < *elem ) { elem = vm->param(i); } } vm->retval( *elem ); } /*# @function max @inset functional_support @brief Picks the maximum value among its parameters. @param ... The items to be checked. @return The greatest item in the sequence. This function performs a lexicographic majority check on each element passed as a parameter, returning the greater of them. A standard VM comparison is performed, so the standard ordering rules apply. This also means that objects overloading the @a BOM.compare method may provide specialized ordering rules. If more than one item is found equal and greater than all the others, the first one is returned. If the function is called without parameters, it returns @b nil. */ FALCON_FUNC core_max ( ::Falcon::VMachine *vm ) { if ( vm->paramCount() == 0 ) { vm->retnil(); return; } Item *elem = vm->param( 0 ); int32 count = vm->paramCount(); for ( int32 i = 1; i < count; i++) { if ( *vm->param(i) > *elem ) { elem = vm->param(i); } } vm->retval( *elem ); } /*# @function map @inset functional_support @brief Creates a new vector of items transforming each item in the original array through the mapping function. @param mfunc A function or sigma used to map the array. @param sequence A sequence of arbitrary items. @return The parameter unevaluated. mfunc is called iteratively for every item in the collection; its return value is added to the mapped array. In this way it is possible to apply an uniform transformation to all the item in a collection. If mfunc returns an out of band nil item, map skips the given position in the target array, actually acting also as a filter function. For example: @code function mapper( item ) if item < 0: return oob(nil) // discard negative items return item ** 0.5 // perform square root end inspect( map( mapper, [ 100, 4, -12, 9 ]) ) // returns [10, 2, 3] @endcode @see oob */ static bool core_map_next( ::Falcon::VMachine *vm ) { // callable in first item CoreArray *origin = vm->param(1)->asArray(); uint32 count = (uint32) vm->local(0)->asInteger(); CoreArray *mapped = vm->local(1)->asArray(); if ( ! vm->regA().isOob() ) mapped->append( vm->regA() ); if ( count < origin->length() ) { *vm->local(0) = (int64) count + 1; vm->pushParameter( origin->at(count) ); vm->callFrame( *vm->param(0), 1 ); return true; } vm->retval( mapped ); return false; } FALCON_FUNC core_map ( ::Falcon::VMachine *vm ) { Item *callable = vm->param(0); Item *i_origin = vm->param(1); if( callable == 0 || !callable->isCallable() || i_origin == 0 || !i_origin->isArray() ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "C,A" ) ); } CoreArray *origin = i_origin->asArray(); CoreArray *mapped = new CoreArray( origin->length() ); if ( origin->length() > 0 ) { vm->returnHandler( &core_map_next ); vm->addLocals( 2 ); *vm->local(0) = (int64)1; *vm->local(1) = mapped; vm->pushParameter( origin->at(0) ); // do not use pre-fetched pointer vm->callFrame( *vm->param(0), 1 ); return; } vm->retval( mapped ); } static bool core_dolist_next ( ::Falcon::VMachine *vm ) { CoreArray *origin = vm->param(1)->asArray(); uint32 count = (uint32) vm->local(0)->asInteger(); // done -- let A stay as is. if ( count >= origin->length() ) return false; //if we called if ( vm->local(1)->asInteger() == 1 ) { // not true? -- exit if ( vm->regA().isOob() && vm->regA().isNil() ) { return false; } // prepare for next loop *vm->local(1) = (int64)0; if ( vm->functionalEval( origin->at(count) ) ) { return true; } } *vm->local(0) = (int64) count + 1; *vm->local(1) = (int64) 1; vm->pushParameter( vm->regA() ); vm->callFrame( *vm->param(0), 1 ); return true; } /*# @function dolist @inset functional_support @brief Repeats an operation on a list of parameters. @param processor A callable item that will receive data coming from the sequence. @param sequence A list of items that will be fed in the processor one at a time. @optparam ... Optional parameters to be passed to the first callable item. @return The return value of the last callable item. Every item in @b sequence is passed as parameter to the processor, which must be a callable item. Items are also functionally evaluated, one by one, but the parameter @b sequence is not functionally evaluated as a whole; to do that, use the explicit evaluation: @code dolist( processor, eval(array) ) @endcode This method is equivalent to @a xmap, but it has the advantage that it doesn't create an array of evaluated results. So, when it is not necessary to transform a sequence in another through a mapping function, but just to run repeatedly over a collection, this function is to be preferred. */ FALCON_FUNC core_dolist ( ::Falcon::VMachine *vm ) { Item *callable = vm->param(0); Item *i_origin = vm->param(1); if( callable == 0 || !callable->isCallable() || i_origin == 0 || !i_origin->isArray() ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "C,A" ) ); } CoreArray *origin = i_origin->asArray(); if ( origin->length() != 0 ) { vm->returnHandler( &core_dolist_next ); vm->addLocals( 2 ); // count *vm->local(0) = (int64) 0; //exiting from an eval or from a call frame? -- 0 eval *vm->local(1) = (int64) 0; if ( vm->functionalEval( origin->at(0) ) ) { return; } // count *vm->local(0) = (int64) 1; //exiting from an eval or from a call frame? -- 1 callframe *vm->local(1) = (int64) 1; vm->pushParameter( vm->regA() ); vm->callFrame( *vm->param(0), 1 ); } } static bool core_times_next_( ::Falcon::VMachine *vm ) { int64 end = vm->local(0)->asInteger(); int64 step = vm->local(1)->asInteger(); int64 start = vm->local(2)->asInteger(); while ( (step > 0 && start >= end) || (step < 0 && start < end ) || ( vm->regA().isOob() && vm->regA().isInteger() && vm->regA().asInteger() == 0 ) ) { vm->retval( start ); return false; } vm->pushParameter( start ); *vm->local(2) = start + step; Item *i_sequence = vm->local(3); if( i_sequence->isArray() ) { vm->functionalEval( *i_sequence, 1, false ); } else { vm->callFrame( *i_sequence, 1 ); } return true; } /*# @function times @inset functional_support @brief Repeats a sequence a determined number of times. @param count Count of times to be repeated or non-open range. @param sequence A function or a Sigma sequence. @return Last index processed. This function is very similar to a functional for/in loop. It repeats a sequence of callable items in the @b sequence parameter a determined number of times. If the @b sequence parameter is a sequence, parametric evaluation is performed and the **&1** late binding is filled with the value of the index; if it's a function, then it is called with the counter value added as the last parameter. If the evaluated parameter is a sequence, full deep sigma evaluation is performed at each loop. The loop index count will be given values from 0 to the required index-1 if @b count is numeric, or it will act as the for/in loop if @b count is a range. For example: @code function printParam( var ) > "Parameter is... ", var end // the followings are equivalent times( 10, [printParam, &1] ) times( 10, printParam ) @endcode The following example prints a list of pair numbers between 2 and 10: @code times( [2:11:2], // range 2 to 10+1, with step 2 .[ printl "Index is now..." &1 ] ) @endcode Exactly like @a floop, the flow of calls in @b times can be altered by the functions in sequence returning an out-of-band 0 or 1. If any function in the sequence returns an out-of-band 0, @b times terminates and return immediately (performing an operation similar to "break"). If a function returns an out-of-band 1, the rest of the items in @b sequence are ignored, and the loop is restarted with the index updated; this is equivalent to a functional "continue". For example: @code times( 10, // skip numbers less than 5 .[ .[(function(x); if x < 5: return oob(1); end) &1] .[printl &1] // print the others ] ) @endcode The @b times function return the last generated value for the index. A natural termination of @b times can be detected thanks to the fact that the index is equal to the upper bound of the range, while an anticipated termination causes @b times to return a different index. For example, if @b count is 10, the generated index (possibly received by the items in @b sequence) will range from 0 to 9 included, and if the function terminates correctly, it will return 10. If a function in @b sequence returns an out-of-band 0, causing a premature termination of the loop, the value returned by times will be the loop index at which the out-of-band 0 was returned. @note Ranges [m:n] where m > n (down-ranges) terminate at n included; in that case, a succesful completion of @b times return one-past n. */ /*# @method times Integer @brief repeats a sequence a given number of times. @param sequence Function or sequence to be repeated. @return Last index processed. This method works exactly as the @b times function when the first parameter is a number. @see times */ /*# @method times Range @brief repeats a sequence a given number of times. @param sequence Function or sequence to be repeated. @return Last index processed. This method works exactly as the @b times function when the first parameter is a range. @see times */ FALCON_FUNC core_times ( ::Falcon::VMachine *vm ) { Item *i_count; Item *i_sequence; if ( vm->self().isMethodic() ) { i_count = &vm->self(); i_sequence = vm->param(0); } else { i_count = vm->param(0); i_sequence = vm->param(1); } if( i_count == 0 || ! ( i_count->isRange() || i_count->isOrdinal() ) || i_sequence == 0 || ! ( i_sequence->isArray() || i_sequence->isCallable() ) ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "N|R, A|C" ) ); } int64 start, end, step; if( i_count->isRange() ) { if ( i_count->asRangeIsOpen() ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "open range" ) ); } start = i_count->asRangeStart(); end = i_count->asRangeEnd(); step = i_count->asRangeStep(); if ( step == 0 ) step = start > end ? -1 : 1; } else { start = 0; end = i_count->forceInteger(); step = end < 0 ? -1 : 1; } // check ranges and steps. if ( start == end || ( start < end && ( step < 0 || start + step > end ) ) || ( start > end && ( step > 0 || start + step < end ) ) ) { // no loop to be done. vm->retval( start ); return; } // ok, we must do at least a loop vm->returnHandler( &core_times_next_ ); // 0: shifting range // 1: position in the sequence calls. // 2: should evaluate ? 0 = no 1 = yes, 2 = already evaluating. vm->addLocals( 4 ); // count *vm->local(0) = end; *vm->local(1) = step; *vm->local(2) = start; *vm->local(3) = *vm->param( vm->self().isMethodic()? 0 : 1 ); // prevent dirty A to mess our break/continue system. vm->regA().setNil(); // start the loop core_times_next_(vm); } /*# @method upto Integer @brief Repeats a function or a sequence until the upper limit is reached. @param llimit The upper limit of the loop. @param sequence The sequence or function to be repeated. @return The last index processed. This method repeats a loop from this integer down to the limit value included. If the limit is less than this integer, the function returns immediately. If the sequence is a function, then it is called iteratively with the current index value as last parameter. If it is a sequence, it is functionally evaluated and the &1 parametric binding is set to the index. @code 2.upto( 5, printl ) 2.downto(5, .[printl "Value number " &1]) @endcode In both cases, returning an oob(0) will cause the loop to terminate, while returning an oob(1) from any evaluation in the sequence makes the rest of the evaluation to be skipped and the loop to be restarted. */ FALCON_FUNC core_upto ( ::Falcon::VMachine *vm ) { Item* i_count = vm->param(0); Item* i_sequence = vm->param(1); if( i_count == 0 || ! i_count->isOrdinal() || i_sequence == 0 || ! ( i_sequence->isArray() || i_sequence->isCallable() ) ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "N|R, A|C" ) ); } int64 start = vm->self().asInteger(); int64 end = i_count->forceInteger(); // check ranges and steps. if ( start > end ) { // no loop to be done. vm->retval( start ); return; } // ok, we must do at least a loop vm->returnHandler( &core_times_next_ ); // 0: shifting range // 1: position in the sequence calls. // 2: should evaluate ? 0 = no 1 = yes, 2 = already evaluating. vm->addLocals( 4 ); // count *vm->local(0) = end+1; *vm->local(1) = (int64) 1; *vm->local(2) = start; *vm->local(3) = *vm->param( 1 ); // prevent dirty A to mess our break/continue system. vm->regA().setNil(); // start the loop core_times_next_(vm); } /*# @method downto Integer @brief Repeats a function or a sequence until the lower limit is reached. @param llimit The lower limit of the loop. @param sequence The sequence or function to be repeated. @return The last index processed. This method repeats a loop from this integer down to the limit value included. If the limit is greater than this integer, the function returns immediately. If the sequence is a function, then it is called iteratively with the current index value as last parameter. If it is a sequence, it is functionally evaluated and the &1 parametric binding is set to the index. @code 5.downto( 2, printl ) 3.downto(0, .[printl "Value number " &1]) @endcode In both cases, returning an oob(0) will cause the loop to terminate, while returning an oob(1) from any evaluation in the sequence makes the rest of the evaluation to be skipped and the loop to be restarted. */ FALCON_FUNC core_downto ( ::Falcon::VMachine *vm ) { Item* i_count = vm->param(0); Item* i_sequence = vm->param(1); if( i_count == 0 || ! i_count->isOrdinal() || i_sequence == 0 || ! ( i_sequence->isArray() || i_sequence->isCallable() ) ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "N|R, A|C" ) ); } int64 start = vm->self().asInteger(); int64 end = i_count->forceInteger(); // check ranges and steps. if ( start <= end ) { // no loop to be done. vm->retval( start ); return; } // ok, we must do at least a loop vm->returnHandler( &core_times_next_ ); // 0: shifting range // 1: position in the sequence calls. // 2: should evaluate ? 0 = no 1 = yes, 2 = already evaluating. vm->addLocals( 4 ); // count *vm->local(0) = end; *vm->local(1) = (int64) -1; *vm->local(2) = start; *vm->local(3) = *vm->param( 1 ); // prevent dirty A to mess our break/continue system. vm->regA().setNil(); // start the loop core_times_next_(vm); } static bool core_xmap_next( ::Falcon::VMachine *vm ) { // in vm->param(0) there is "callable". CoreArray *origin = vm->param(1)->asArray(); uint32 count = (uint32) vm->local(0)->asInteger(); CoreArray *mapped = vm->local(1)->asArray(); if ( count < origin->length() ) { if ( vm->local(2)->asInteger() == 1 ) { if ( ! vm->regA().isOob() ) mapped->append( vm->regA() ); // prepare for next loop *vm->local(0) = (int64) count + 1; *vm->local(2) = (int64) 0; if ( vm->functionalEval( origin->at(count) ) ) { return true; } } *vm->local(2) = (int64) 1; vm->pushParameter( vm->regA() ); vm->callFrame( *vm->param(0), 1 ); return true; } else { if ( ! vm->regA().isOob() ) mapped->append( vm->regA() ); } vm->retval( mapped ); return false; } /*# @function xmap @inset functional_support @brief Creates a new vector of items transforming each item in the original array through the mapping function, applying also filtering on undesired items. @param mfunc A function or sigma used to map the array. @param sequence A sequence to be mapped. @return The mapped sequence. @b mfunc is called iteratively for every item in the collection; its return value is added to the mapped array. Moreover, each item in the collection is functionally evaluated before being passed to mfunc. The filter function may return an out of band nil item to signal that the current item should not be added to the final collection. For example: @code mapper = { item => item < 0 ? oob(nil) : item ** 0.5 } add = { a, b => a+b } // a block that will be evaluated inspect( xmap( mapper, [ [add, 99, 1], 4, -12, 9 ]) ) // returns [10, 2, 3] @endcode @see oob @see dolist */ FALCON_FUNC core_xmap ( ::Falcon::VMachine *vm ) { Item *callable = vm->param(0); Item *i_origin = vm->param(1); if( callable == 0 || !callable->isCallable() || i_origin == 0 || !i_origin->isArray() ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "C,A" ) ); } CoreArray *origin = i_origin->asArray(); CoreArray *mapped = new CoreArray( origin->length() ); if ( origin->length() > 0 ) { vm->returnHandler( &core_xmap_next ); vm->addLocals( 3 ); *vm->local(0) = (int64)1; *vm->local(1) = mapped; *vm->local(2) = (int64) 0; if ( vm->functionalEval( origin->at(0) ) ) { return; } *vm->local(2) = (int64) 1; vm->pushParameter( vm->regA() ); vm->callFrame( *vm->param(0), 1 ); return; } vm->retval( mapped ); } static bool core_filter_next ( ::Falcon::VMachine *vm ) { CoreArray *origin = vm->param(1)->asArray(); CoreArray *mapped = vm->local(0)->asArray(); uint32 count = (uint32) vm->local(1)->asInteger(); if ( vm->regA().isTrue() ) mapped->append( origin->at(count -1) ); if( count == origin->length() ) { vm->retval( mapped ); return false; } *vm->local(1) = (int64) count+1; vm->pushParameter( origin->at(count) ); vm->callFrame( *vm->param(0), 1 ); return true; } /*# @function filter @inset functional_support @brief Filters sequence using a filter function. @param ffunc A callable item used to filter the array. @param sequence A sequence of arbitrary items. @return The filtered sequence. ffunc is called iteratively for every item in the collection, which is passed as a parameter to it. If the call returns true, the item is added to the returned array; if it returns false, the item is not added. Items in the collection are treated literally (not evaluated). */ FALCON_FUNC core_filter ( ::Falcon::VMachine *vm ) { Item *callable = vm->param(0); Item *i_origin = vm->param(1); if( callable == 0 || !callable->isCallable() || i_origin == 0 || !i_origin->isArray() ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "C,A" ) ); } CoreArray *origin = i_origin->asArray(); CoreArray *mapped = new CoreArray( origin->length() / 2 ); if( origin->length() > 0 ) { vm->returnHandler( &core_filter_next ); vm->addLocals(2); *vm->local(0) = mapped; *vm->local(1) = (int64) 1; vm->pushParameter( origin->at(0) ); vm->callFrame( *vm->param(0), 1 ); return; } vm->retval( mapped ); } static bool core_reduce_next ( ::Falcon::VMachine *vm ) { // Callable in param 0 CoreArray *origin = vm->param(1)->asArray(); // if we had enough calls, return (the return value of the last call frame is // already what we want to return). uint32 count = (uint32) vm->local(0)->asInteger(); if( count >= origin->length() ) return false; // increment count for next call vm->local(0)->setInteger( count + 1 ); // call next item vm->pushParameter( vm->regA() ); // last returned value vm->pushParameter( origin->at(count) ); // next element vm->callFrame( *vm->param(0), 2 ); return true; } /*# @function reduce @inset functional_support @brief Uses the values in a given sequence and iteratively calls a reductor function to extract a single result. @param reductor A function or Sigma to reduce the array. @param sequence A sequence of arbitrary items. @optparam initial_value Optional startup value for the reduction. @return The reduced result. The reductor is a function receiving two values as parameters. The first value is the previous value returned by the reductor, while the second one is an item iteratively taken from the origin array. If a startup value is given, the first time the reductor is called that value is provided as its first parameter, otherwise the first two items from the array are used in the first call. If the collection is empty, the initial_value is returned instead, and if is not given, nil is returned. If a startup value is not given and the collection contains only one element, that element is returned. Some examples: @code > reduce( {a,b=> a+b}, [1,2,3,4]) // sums 1 + 2 + 3 + 4 = 10 > reduce( {a,b=> a+b}, [1,2,3,4], -1 ) // sums -1 + 1 + 2 + 3 + 4 = 9 > reduce( {a,b=> a+b}, [1] ) // never calls lambda, returns 1 > reduce( {a,b=> a+b}, [], 0 ) // never calls lambda, returns 0 > reduce( {a,b=> a+b}, [] ) // never calls lambda, returns Nil @endcode Items in the collection are treated literally (not evaluated). */ FALCON_FUNC core_reduce ( ::Falcon::VMachine *vm ) { Item *callable = vm->param(0); Item *i_origin = vm->param(1); Item *init = vm->param(2); if( callable == 0 || !callable->isCallable()|| i_origin == 0 || !i_origin->isArray() ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "C,A,[X]" ) ); } CoreArray *origin = i_origin->asArray(); vm->addLocals(1); // local 0: array position if ( init != 0 ) { if( origin->length() == 0 ) { vm->retval( *init ); return; } vm->returnHandler( &core_reduce_next ); vm->pushParameter( *init ); vm->pushParameter( origin->at(0) ); *vm->local(0) = (int64) 1; //WARNING: never use pre-cached item pointers after stack changes. vm->callFrame( *vm->param(0), 2 ); return; } // if init == 0; if there is only one element in the array, return it. if ( origin->length() == 0 ) vm->retnil(); else if ( origin->length() == 1 ) vm->retval( origin->at(0) ); else { vm->returnHandler( core_reduce_next ); *vm->local(0) = (int64) 2; // we'll start from 2 // the first call is between the first and the second elements in the array. vm->pushParameter( origin->at(0) ); vm->pushParameter( origin->at(1) ); //WARNING: never use pre-cached item pointers after stack changes. vm->callFrame( *vm->param(0), 2 ); } } static bool core_iff_next( ::Falcon::VMachine *vm ) { // anyhow, we don't want to be called anymore vm->returnHandler( 0 ); if ( vm->regA().isTrue() ) { if ( vm->functionalEval( *vm->param(1) ) ) return true; } else { Item *i_ifFalse = vm->param(2); if ( i_ifFalse != 0 ) { if ( vm->functionalEval( *i_ifFalse ) ) return true; } else vm->retnil(); } return false; } /*# @function iff @inset functional_support @brief Performs a functional if; if the first parameter evaluates to true, the second parameter is evaluated and then returned, else the third one is evaluated and returned. @param cfr A condition or a callable item. @param whenTrue Value to be called and/or returned in case cfr evaluates to true. @optparam whenFalse Value to be called and/or returned in case cfr evaluates to false. @return The evaluation result of one of the two branches (or nil). Basically, this function is meant to return the second parameter or the third (or nil if not given), depending on the value of the first parameter; however, every item is evaluated in a functional context. This means that cfr may be a callable item, in which case its return value will be evaluated for truthfulness, and also the other parameters may. For example: @code > iff( 0, "was true", "was false" ) // will print "was false" iff( [{a=>a*2}, 1] , [printl, "ok!"] ) // will print "ok!" and return nil @endcode In the last example, we are not interested in the return value (printl returns nil), but in executing that item only in case the first item is true. The first item is a callable item too, so iff will first execute the given block, finding a result of 2 (true), and then will decide which element to pick, and eventually execute. Notice that: @code iff( 1 , printl( "ok!" ), printl( "no" ) ) @endcode This would have forced Falcon to execute the two printl calls before entering the iff function; still, iff would have returned printl return values (which is nil in both cases). */ FALCON_FUNC core_iff ( ::Falcon::VMachine *vm ) { Item *i_cond = vm->param(0); Item *i_ifTrue = vm->param(1); Item *i_ifFalse = vm->param(2); if( i_cond == 0 || i_ifTrue == 0 ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "X,X,[X]" ) ); } // we can use pre-fetched values as we have stack unchanged on // paths where we use item pointers. vm->returnHandler( &core_iff_next ); if ( vm->functionalEval( *i_cond ) ) { return; } vm->returnHandler( 0 ); if ( vm->regA().isTrue() ) { vm->functionalEval( *i_ifTrue ); } else { if ( i_ifFalse != 0 ) vm->functionalEval( *i_ifFalse ); else vm->retnil(); } } static bool core_choice_next( ::Falcon::VMachine *vm ) { if ( vm->regA().isTrue() ) { vm->retval( *vm->param(1) ); } else { Item *i_ifFalse = vm->param(2); if ( i_ifFalse != 0 ) vm->retval( *i_ifFalse ); else vm->retnil(); } return false; } /*# @function choice @inset functional_support @brief Selects one of two alternatives depending on the evaluation of the first parameter. @param selector The item to be evaluated. @param whenTrue The item to return if selector evaluates to true. @optparam whenFalse The item to be returned if selector evaluates to false @optparam ... Optional parameters to be passed to the first callable item. @return The return value of the last callable item. The selector parameter is evaluated in functional context. If it's a true atom or if it's a callable array which returns a true value, the ifTrue parameter is returned as-is, else the ifFalse parameter is returned. If the ifFalse parameter is not given and the selector evaluates to false, nil is returned. The choice function is equivalent to iff where each branch is passed through the @a lit function: @code choice( selector, a, b ) == iff( selector, [lit, a], [lit, b] ) @endcode In case a literal value is needed, choice is more efficient than using iff and applying lit on the parameters. */ FALCON_FUNC core_choice ( ::Falcon::VMachine *vm ) { Item *i_cond = vm->param(0); Item *i_ifTrue = vm->param(1); Item *i_ifFalse = vm->param(2); if( i_cond == 0 || i_ifTrue == 0 ) { throw new ParamError( ErrorParam( e_inv_params ). extra( "X,X,[X]" ) ); } vm->returnHandler( &core_choice_next ); if ( vm->functionalEval( *i_cond ) ) { return; } vm->returnHandler( 0 ); if ( vm->regA().isTrue() ) { vm->retval( *i_ifTrue ); } else { if ( i_ifFalse != 0 ) vm->retval( *i_ifFalse ); else vm->retnil(); } } /*# @function lit @inset functional_support @brief Return its parameter as-is @param item A condition or a callable item. @return The parameter unevaluated. This function is meant to interrupt functional evaluation of lists. It has the same meaning of the single quote literal ' operator of the LISP language. For example, the following code will return either a callable instance of printl, which prints a "prompt" before the parameter, or a callable instance of inspect: @code iff( a > 0, [lit, [printl, "val: "] ], inspect)( param ) @endcode as inspect is a callable token, but not an evaluable one, it is already returned literally; however, [printl, "val:"] would be considered an evaluable item. To take its literal value and prevent evaluation in functional context, the lit construct must be used. */ FALCON_FUNC core_lit ( ::Falcon::VMachine *vm ) { Item *i_cond = vm->param(0); if( i_cond == 0 ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "X" ) ); } vm->regA() = *i_cond; // result already in A. } static bool core_cascade_next ( ::Falcon::VMachine *vm ) { // Param 0: callables array // local 0: counter (position) // local 1: last accepted result CoreArray *callables = vm->param(0)->asArray(); uint32 count = (uint32) vm->local(0)->asInteger(); // Done? if ( count >= callables->length() ) { // if the last result is not accepted, return last accepted if ( vm->regA().isOob() ) { // reset OOB, that may be set on first unaccepted parameter. vm->local(1)->resetOob(); vm->retval( *vm->local(1) ); } // else, just keep return false; } uint32 pc; // still some loop to do // accept result? if ( vm->regA().isOob() ) { // not accepted. // has at least one parameter been accepted? if ( vm->local(1)->isOob() ) { // no? -- replay initial params pc = vm->paramCount(); for ( uint32 pi = 1; pi < pc; pi++ ) { vm->pushParameter( *vm->param(pi) ); } pc--; //first param is our callable } else { // yes? -- reuse last accepted parameter pc = 1; vm->pushParameter( *vm->local(1) ); } } else { *vm->local(1) = vm->regA(); pc = 1; vm->pushParameter( vm->regA() ); } // prepare next call vm->local(0)->setInteger( count + 1 ); // perform call vm->callFrame( callables->at(count), pc ); // will throw noncallable in case of noncallable item. //throw new ParamError( ErrorParam( e_non_callable ).origin(e_orig_runtime) ); return true; } /*# @function cascade @inset functional_support @brief Concatenate a set of callable items so to form a single execution unit. @param callList Sequence of callable items. @optparam ... Optional parameters to be passed to the first callable item. @return The return value of the last callable item. This function executes a set of callable items passing the parameters it receives beyond the first one to the first item in the list; from there on, the return value of the previous call is fed as the sole parameter of the next call. In other words, @code cascade( [F1, F2, ..., FN], p1, p2, ..., pn ) @endcode is equivalent to @code FN( ... F2( F1( p1, p2, ..., pn ) ) ... ) @endcode A function may declare itself "uninterested" to insert its value in the cascade by returning an out-of-band item. In that case, the return value is ignored and the same parameter it received is passed on to the next calls and eventually returned. Notice that the call list is not evaluated in functional context; it is just a list of callable items. To evaluate the list, or part of it, in functional context, use the eval() function. A simple example usage is the following: @code function square( a ) return a * a end function sqrt( a ) return a ** 0.5 end cascade_abs = [cascade, [square, sqrt] ] > cascade_abs( 2 ) // 2 > cascade_abs( -4 ) // 4 @endcode Thanks to the possibility to prevent insertion of the return value in the function call sequence, it is possible to program "interceptors" that will catch the progress of the sequence without interfering: @code function showprog( v ) > "Result currently ", v return oob(nil) end // define sqrt and square as before... cascade_abs = [cascade, [square, showprog, sqrt, showprog] ] > "First process: ", cascade_abs( 2 ) > "Second process: ", cascade_abs( -4 ) @endcode If the first function of the list declines processing by returning an oob item, the initial parameters are all passed to the second function, and so on till the last call. For example: @code function whichparams( a, b ) > "Called with ", a, " and ", b return oob(nil) end csq = [cascade, [ whichparams, {a,b=> a*b} ] > csq( 3, 4 ) @endcode Here, the first function in the list intercepts the parameters but, as it doesn't accepts them, they are both passed to the second in the list. @see oob */ FALCON_FUNC core_cascade ( ::Falcon::VMachine *vm ) { Item *i_callables = vm->param(0); if( i_callables == 0 || !i_callables->isArray() ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "A,..." ) ); } // for the first callable... CoreArray *callables = i_callables->asArray(); if( callables->length() == 0 ) { vm->retnil(); return; } // we have at least one callable. // Prepare the local space // 0: array counter // 1: saved previous value // saved previous value is initialized to oob until // someone accepts the first parameters. vm->addLocals(2); vm->local(0)->setInteger( 1 ); // we'll start from 1 vm->local(1)->setOob(); // echo the parameters to the first call uint32 pcount = vm->paramCount(); for ( uint32 pi = 1; pi < pcount; pi++ ) { vm->pushParameter( *vm->param(pi) ); } pcount--; // install the handler vm->returnHandler( &core_cascade_next ); // perform the real call vm->callFrame( callables->at(0), pcount ); //throw new ParamError( ErrorParam( e_non_callable ).origin(e_orig_runtime) ); } static bool core_floop_next ( ::Falcon::VMachine *vm ) { // Param 0: callables array CoreArray *callables = vm->param(0)->asArray(); // local 0: counter (position) uint32 count = (uint32) vm->local(0)->asInteger(); // next item. ++count; // still some loop to do if ( vm->regA().isInteger() && vm->regA().isOob() ) { if ( vm->regA().asInteger() == 0 ) { // we're done. vm->returnHandler( 0 ); // ensure we're not called after first loop vm->retnil(); return false; } else if ( vm->regA().asInteger() == 1 ) { // continue count = 0; } } if ( count >= callables->length() ) { count = 0; } // save the count *vm->local(0) = (int64) count; // find a callable in the array if ( (*callables)[count].isCallable() ) { vm->callFrame( (*callables)[count], 0 ); } else { // set the item as A and recall ourself for evaluation vm->regA() = (*callables)[count]; vm->recallFrame(); return true; } // else, just return true return true; } /*# @function floop @inset functional_support @brief Repeats indefinitely a list of operations. @param sequence A sequence of callable items that gets called one after another. Every item in @b sequence gets executed, one after another. When the last element is executed, the first one is called again, looping indefinitely. Any function in the sequence may interrupt the loop by returning an out-of-band 0; if a function returns an out of band 1, all the remaining items in the list are ignored and the loop starts again from the first item. Items in the array are not functionally evaluated. */ FALCON_FUNC core_floop ( ::Falcon::VMachine *vm ) { Item *i_callables = vm->param(0); if( i_callables == 0 || !i_callables->isArray() ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "A" ) ); return; } // for the first callable... CoreArray *callables = i_callables->asArray(); if( callables->length() == 0 ) { return; } // we have at least one callable. // Prepare the local space // 0: array counter vm->addLocals(1); vm->local(0)->setInteger( callables->length() ); // we'll start from 0 from the first loop // install the handler vm->returnHandler( &core_floop_next ); // call it directly vm->regA().setNil(); // zero to avoid false signals to next handler vm->callFrameNow( &core_floop_next ); } /*# @function firstOf @inset functional_support @brief Returns the first non-false of its parameters. @param ... Any number of arbitrary parameters. @return The first non-false item. This function scans the paraters one at a time. Sigma evaluation is stopped, or in other words, every parameters is considered as-is, as if @a lit was used on each of them. The function returns the first parameter being non-false in a standard Falcon truth check. Nonzero numeric values, non empty strings, arrays and dictionaries and any object is considered true. If none of the parameters is true, of is none of the parameter is given, the function returns nil (which is considered false). */ FALCON_FUNC core_firstof ( ::Falcon::VMachine *vm ) { int count = 0; Item *i_elem = vm->param(0); while( i_elem != 0 ) { if ( i_elem->isTrue() ) { vm->retval( *i_elem ); return; } i_elem = vm->param( ++count ); } vm->retnil(); } /*# @function lbind @inset functional_support @brief Creates a dynamic late binding symbol. @param name A string representing a late binding name. @optparam value A future binding value. @return A newly created late binding name. This function create a late binding item which can be used in functional sequences as if the parameter was written in the source code prefixed with the amper '&' operator. The following lines are equivalent: @code bound = lbind( "count" ) ctx = &count @endcode The return value of this function, both used directly or pre-cached, can be seamlessly merged with the & operator in functional sequences. For example, it is possible to write the following loop: @code eval( .[ .[ times 10 &count .[ .[eval .[ printl 'Counting...' .[lbind 'count'] ] ] ] ]] ) @endcode It is also possible cache the value and use it afterwards: @code x = lbind( 'count' ) eval( .[ .[ times 10 &count .[ .[ printl 'Counting...' x] ] ]] ) @endcode The @b value parameter initializes a future (forward) binding. Future bindings are bindings with a potential value, which is applied during function calls and symbol resolution to pre-existing symbolic entities. In practice, they allow calling fucntions with named parameters. When mixing forward bindings and normal parameters, forward bindings are as placed directly at the position of the parameter they refer to, and they doesn't count during parameter expansion of non-named parameters. Also, they always overwrite the positional parameters, as they are considered after all the positional parameters have been placed on their spots. @code function test( par1, par2, par3 ) >> "Par1: ", par1 >> ", Par2: ", par2 > ", Par3: ", par3 end x = lbind( "par2", "Hello" ) test( x ) // nil->par1, "Hello"->par2, nil->par3 test( x, "Yo!" ) // "Yo!"->par1, "Hello"->par2, nil->par3 test( "Yo!", x ) // as above test( "Yo!", "Yo! again", x ) // "Hello" overwrites "Yo again" test( x, "Yo!", "Yo! again", "end" ) // "Yo!"->par1, "Hello"->par2, "end"->par3 @endcode @note lbind is @b not an ETA function. */ FALCON_FUNC core_lbind ( ::Falcon::VMachine *vm ) { Item *i_name = vm->param(0); Item *i_value = vm->param(1); if( i_name == 0 || !i_name->isString() ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "S" ) ); } GarbageItem *itm = i_value == 0 ? 0 : new GarbageItem( *i_value ); vm->regA().setLBind( new CoreString( *i_name->asString() ), itm ); } static bool core_let_next( ::Falcon::VMachine *vm ) { *vm->param(0) = vm->regA(); return false; } /*# @function let @inset functional_support @brief Assigns a value to another in a functional context. @param dest Destination value (passed by reference). @param source Source value. @return The assigned (source) value. This function assigns a literal value given in @b source into @b dest, provided dest is a late binding or is passed by referece. This function is an ETA and prevents evaluation of its first parameter. In other words, the first parameter is treadted as if passed through @a lit. */ FALCON_FUNC core_let ( ::Falcon::VMachine *vm ) { Item *i_dest = vm->param(0); Item *i_source = vm->param(1); if( i_dest == 0 || ! vm->isParamByRef( 0 ) || i_source == 0 ) { throw new ParamError( ErrorParam( e_inv_params ) .origin(e_orig_runtime) .extra( "$X,X" ) ); } vm->returnHandler( &core_let_next ); if ( vm->functionalEval( *i_source ) ) { return; } vm->returnHandler( 0 ); *vm->param(0) = *vm->param(1); vm->regA() = *vm->param(0); } static bool core_brigade_next( ::Falcon::VMachine *vm ) { int64 next = vm->local(0)->asInteger(); // Has the previous call returned something interesting? if ( vm->regA().isOob() ) { if( vm->regA().isInteger() ) { // break request? if( vm->regA().asInteger() == 0 ) { vm->retnil(); return false; } else if( vm->regA().asInteger() == 1 ) { // loop from start next = 0; } } else if ( vm->regA().isArray() ) { CoreArray* newParams = vm->regA().asArray(); *vm->local(1) = newParams; // add a space for the calls newParams->prepend( Item() ); } } CoreArray* list = vm->param(0)->asArray(); // are we done? if( next >= list->length() ) return false; // prepare the local call vm->local(0)->setInteger( next + 1 ); // anyhow, prepare the call //-- have we changed parameters? if ( vm->local(1)->isArray() ) { CoreArray* callarr = vm->local(1)->asArray(); callarr->at(0) = list->at((int32)next); vm->callFrame( callarr, 0 ); } else { // no? -- use our original paramters. for( int32 i = 1; i < vm->paramCount(); ++i ) { vm->pushParameter( *vm->param(i) ); } vm->callFrame( list->at((int32)next), vm->paramCount()-1 ); } return true; // call me again } /*# @function brigade @inset functional_support @brief Process a list of functions passing the same parameters to them. @param fl The sequence of callable items to be called. @param ... Arbitrary parameters used by the brigade functions. @return The return value of the last function in fl. This function process a sequence of functions passing them the same set of parameters. The idea is that of a "brigate" of functions operating all on the same parameters so that it is possible to put common code into separate functions. Items in the list are not functionally evaluated; they are simply called, passing to them the same parameters that the brigade group receives. Brigate is an ETA funcion, and this means that ongoing functional evaluation is interrupted as soon as a brigade is encountered. @code function mean( array ) value = 0 for elem in array: value += elem return value / len( array ) end function dbl( array ) for i in [0:len(array)]: array[i] *= 2 end doubleMean = .[ brigade .[ dbl mean ]] > "Mean: ", mean( [1,2,3] ) > "Double mean: ", doubleMean( [1,2,3] ) @endcode The above example brigades a "prefix" function to double the values in an array that must be processed by the main function. Using out of band return values, the functions in the sequence can also control the parameters that the following functions will receive, terminate immediately the evaluation or restart it, forming a sort of iterative functional loop. An oob 0 causes the sequence to be interrutped (and return oob(0) itself), an out of band 1 will cause the first element of the sequence to be called again, and an out of band array will permanently change the parameters as seen by the functions. The following brigate performs a first step using the given parameters, and another one using modified ones: @code looper = .[brigade .[ { val, text => printl( text, ": ", val ) } // do things { val, text => oob( [val+1, "Changed"] ) } // change params { val, text => val >= 10 ? oob(0) : oob(1)} // loop control ]] looper( 1, "Original" ) @endcode */ FALCON_FUNC core_brigade ( ::Falcon::VMachine *vm ) { Item *i_fl = vm->param(0); if( i_fl == 0 || ! i_fl->isArray() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin(e_orig_runtime) .extra( "A" ) ); } // nothing to do? if ( i_fl->asArray()->length() == 0 ) { vm->retnil(); return; } vm->returnHandler( &core_brigade_next ); vm->addLocals(2); vm->local(0)->setInteger(1); vm->local(1)->setNil(); // anyhow, prepare the call for( int32 i = 1; i < vm->paramCount(); ++i ) { vm->pushParameter( *vm->param(i) ); } vm->callFrame( vm->param(0)->asArray()->at(0), vm->paramCount()-1 ); //throw new ParamError( ErrorParam( e_non_callable,__LINE__ ).origin(e_orig_runtime) ); } } } /* end of functional_ext.cpp */ engine/core_module/gc_ext.cpp000066400000000000000000000166141176363201700166130ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: gc_ext.cpp Garbage control from scripts ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 14 Aug 2008 02:10:57 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "core_module.h" #include /*# @beginmodule core */ namespace Falcon { namespace core { /*# @page gc_control About the garbage collector. The standard collector strategy (be it set up by the Falcon interpreter or by embedding applications) is adequate for average scripts. However, some script meant to start from command line and dealing with time critical data may find the action of the garbage collector too intrusive. For example the GC may occuur at the wrong time. Other times, calculation intensive programs may generate a lot of data that they know in advance can be never garbaged during some period. In those case, having GC to scan periodically the allocated memory for released blocks is evidently a useless waste of time. Finally, some complex scripts may even provide their own collection strategy, based on memory pattern usage that they know in advance. Starting the collection loop at time intervals, provided the memory allocation has grown at a certain rate, or hasn't grown for a certain time, may be a fitting strategy for some scripts. A sensible usage of the garbage collection feature may boost performance of calculation and memory intensive scripts by order of degrees, and may be essential in time critical applications where some part of the process has to be performed as fast as possible. Consider that some of the functions listed in this section may not be always available. Some embedding application may decide to turn some or all of them off for security reasons, as a malevolent script may crash an application very fast by turning off automatic GC check-and-reclaim feature and then creating a great amount of garbage. Also, the loop maximum execution time control is not present by default in Falcon command line, as the time-deterministic version of the garbage collector is sensibly slower, and it would be useless to the vast majority of the scripts. */ /*# @object GC @brief Support for script-based garbage collection strategies. @prop usedMem Memory used by the Falcon engine. @prop items Single GC sensible items currently allocated. @prop th_normal Threshold of occupied memory above which the GC will enter the normal mode. @prop th_active Threshold of occupied memory above which the GC will enter the active mode. @see gc_control */ CoreObject* GC_Factory( const CoreClass *cls, void *user_data, bool ) { // just to mark our user data. return new ReflectObject( cls, (void*)1 ); } /*# @method enable GC @brief Turns automatic GC feature on or off. @param mode true to turn automatic GC on, false to turn it off. Virtual machines and some heavy garbage generating functions call periodically a function that checks for the level of allocated memory to have reached a critical point. When there is too much allocated memory of uncertain status, a garbage collecting loop is started. By setting this property to false, this automatic control is skipped, and allocated memory can grow up to physical process limits (or VM memory limit constraints, if provided). Setting this value to true will cause VM to perform memory control checks again with the usual strategy. In case the script is sure to have generated a wide amount of garbage, it is advisable to call explicitly @a GC.perform() before turning automatic GC on, as the "natural" collection loop may start at any later moment, also after several VM loops. */ FALCON_FUNC GC_enable( ::Falcon::VMachine *vm ) { if( vm->param(0) == 0 ) vm->retval( vm->isGcEnabled() ? 1 : 0 ); else vm->gcEnable(vm->param(0)->isTrue()); } /*# @method perform GC @brief Requests immediate check of garbage. @optparam wcoll Set to true to wait for the collection of free memory to be complete. @return true if the gc has been actually performed, false otherwise. Suspends the activity of the calling Virtual Machine, waiting for the garbage collector to complete a scan loop before proceeding. */ FALCON_FUNC GC_perform( ::Falcon::VMachine *vm ) { if ( vm->paramCount() > 0 ) vm->performGC( vm->param(0)->isTrue() ); else vm->performGC(); } /*# @method adjust GC @brief Sets or gets the automatic threshold levels adjust algorithm. @optparam mode The adjust mode used by the GC. @return The mode currently set. Mode can be one of: - GC.ADJ_NONE: No adjust. All adjusting must be done manually. - GC.ADJ_STRICT: Aggressive adjustment strategy, forcing active collection whenever the memory grows. - GC.ADJ_LOOSE: Permissive adjustment strategy, forcing active collection only when memory grows promptly. - GC.ADJ_SMOOTH_FAST: Adjustment following the memory allocation status with some delay and a smooth asymptotic curve (fast adaption). - GC.ADJ_SMOOTH_SLOW: Adjustment following the memory allocation status with some delay and a smooth asymptotic curve (slow adaption). */ FALCON_FUNC GC_adjust( ::Falcon::VMachine *vm ) { Item *i_setting = vm->param(0); vm->retval( memPool->rampMode() ); if ( i_setting != 0 ) { if ( ! i_setting->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin(e_orig_runtime) .extra( "N" ) ); } else if ( ! memPool->rampMode( (int) i_setting->forceInteger() ) ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin(e_orig_runtime) ); } } } // Reflective path method void GC_usedMem_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { property = (int64) gcMemAllocated(); } void GC_aliveMem_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { property = (int64) gcMemAllocated(); } void GC_items_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { property = (int64) memPool->allocatedItems(); } void GC_th_normal_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { property = (int64) memPool->thresholdNormal(); } void GC_th_normal_rto(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { if ( ! property.isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ).extra( "N" ) ); } memPool->thresholdNormal( (size_t) property.forceInteger() ); } void GC_th_active_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { property = (int64) memPool->thresholdActive(); } void GC_th_active_rto(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { if ( ! property.isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ).extra( "[N]" ) ); } memPool->thresholdActive( (size_t) property.forceInteger() ); } } } /* end of gc_ext.cpp */ engine/core_module/input.cpp000066400000000000000000000040601176363201700164710ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: input.cpp Read a line from the input stream ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom set 19 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Read a line from the input stream. */ /*# @beginmodule core */ #include #include #include #include #include #include #include #include #ifndef WIN32 #include #else #include #ifndef STDIN_FILENO #define STDIN_FILENO 0 #endif #endif namespace Falcon { namespace core { /*# @function input @inset core_basic_io @brief Get some text from the user (standard input stream). Reads a line from the standard input stream and returns a string containing the read data. This is mainly meant as a test/debugging function to provide the scripts with minimal console based user input support. When in need of reading lines from the standard input, prefer the readLine() method of the input stream object. This function may also be overloaded by embedders to provide the scripts with a common general purpose input function, that returns a string that the user is queried for. */ FALCON_FUNC input ( ::Falcon::VMachine *vm ) { char mem[512]; int size = 0; while( size < 511 ) { if ( ::read( STDIN_FILENO, mem + size, 1 ) != 1 ) { throw new IoError( ErrorParam( e_io_error ) .origin( e_orig_runtime ) .sysError( errno ) ); } if( mem[size] == '\n' ) { mem[ size ] = 0; break; } else if( mem[size] != '\r' ) { size++; } } mem[size] = 0; CoreString *str = new CoreString; str->bufferize( mem ); vm->retval( str ); } }} /* end of input.cpp */ engine/core_module/inspect.cpp000066400000000000000000000507761176363201700170160ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: inspect.cpp Deep inspect function. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 15 Jul 2009 07:03:13 -0700 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ #include #include #include #include #include #include #include #include #include #include namespace Falcon { namespace core { void inspect_internal( VMachine *vm, const Item *elem, int32 level, int32 maxLevel, int32 maxSize, Item* i_stream, bool add = true, bool addLine=true ); void inspect_internal( VMachine *vm, const Item *elem, int32 level, int32 maxLevel, int32 maxSize, Item* i_stream, bool add, bool addline ) { uint32 count; int32 i; Stream *stream = i_stream != 0 ? dyncast(i_stream->asObjectSafe()->getFalconData()) : vm->stdErr(); // return if we reached the maximum level. if ( maxLevel >= 0 && level > maxLevel ) { stream->writeString( "..." ); if ( addline ) stream->writeString( "\n" ); return; } if ( stream == 0 ) { stream = vm->stdOut(); if ( stream == 0 ) return; } if ( add ) for ( i = 0; i < level*3; i ++ ) { stream->put( 0x20 ); // space } if ( elem == 0 ) { stream->writeString( "Nothing" ); if ( addline ) stream->writeString( "\n" ); return; } String temp; switch( elem->type() ) { case FLC_ITEM_NIL: stream->writeString( "Nil" ); break; case FLC_ITEM_UNB: stream->writeString( "_" ); break; case FLC_ITEM_BOOL: stream->writeString( elem->asBoolean() ? "true" : "false" ); break; case FLC_ITEM_INT: temp.writeNumber( elem->asInteger() ); stream->writeString( "int(" ); stream->writeString( temp ); stream->writeString( ")" ); break; case FLC_ITEM_NUM: temp.writeNumber( elem->asNumeric(), "%g" ); stream->writeString( "num(" ); stream->writeString( temp ); stream->writeString( ")" ); break; case FLC_ITEM_RANGE: elem->toString(temp); stream->writeString( temp ); break; case FLC_ITEM_STRING: stream->writeString( "\"" ); if ( maxSize < 0 || elem->asString()->length() < (uint32) maxSize ) { stream->writeString( *elem->asString() ); stream->writeString( "\"" ); } else { stream->writeString( elem->asString()->subString(0, maxSize ) ); stream->writeString( " ... \"" ); } break; case FLC_ITEM_LBIND: stream->writeString( "&" ); stream->writeString( *elem->asLBind() ); break; case FLC_ITEM_MEMBUF: { MemBuf *mb = elem->asMemBuf(); temp = "MemBuf("; temp.writeNumber( (int64) mb->length() ); temp += ","; temp.writeNumber( (int64) mb->wordSize() ); temp += ")"; if ( maxSize == 0 ) stream->writeString( temp ); else { temp += " [\n"; String fmt; int limit = 0; switch ( mb->wordSize() ) { case 1: fmt = "%02" LLFMT "X"; limit = 24; break; case 2: fmt = "%04" LLFMT "X"; limit = 12; break; case 3: fmt = "%06" LLFMT "X"; limit = 9; break; case 4: fmt = "%08" LLFMT "X"; limit = 6; break; } int written = 0; uint32 max = maxSize < 0 || mb->length() < (uint32) maxSize ? mb->length() : (uint32) maxSize; for( count = 0; count < max; count++ ) { temp.writeNumber( (int64) mb->get( count ), fmt ); temp += " "; written ++; if ( written == limit ) { temp += "\n"; written = 0; } stream->writeString( temp ); temp = ""; } if ( count == (uint32) maxSize ) stream->writeString( " ... " ); stream->writeString( "]" ); } } break; case FLC_ITEM_ARRAY: { CoreArray *arr = elem->asArray(); temp = "Array["; temp.writeNumber( (int64) arr->length() ); temp += "]"; stream->writeString( temp ); if ( level == maxLevel ) { stream->writeString( "{...}" ); break; } stream->writeString( "{\n" ); for( count = 0; count < arr->length(); count++ ) { inspect_internal( vm, & ((*arr)[count]), level + 1, maxLevel, maxSize, i_stream, true, true ); } for ( i = 0; i < level; i ++ ) { stream->writeString( " " ); } stream->writeString( "}" ); } break; case FLC_ITEM_DICT: { CoreDict *dict = elem->asDict(); temp = "Dict["; temp.writeNumber( (int64) dict->length() ); temp += "]"; stream->writeString( temp ); if ( level == maxLevel ) { stream->writeString( "{...}" ); break; } stream->writeString( "{\n" ); Iterator iter( &dict->items() ); while( iter.hasCurrent() ) { inspect_internal( vm, &iter.getCurrentKey(), level + 1, maxLevel, maxSize, i_stream, true, false ); stream->writeString( " => " ); inspect_internal( vm, &iter.getCurrent(), level + 1, maxLevel, maxSize, i_stream, false, true ); iter.next(); } for ( i = 0; i < level; i ++ ) { stream->writeString(" "); } stream->writeString( "}" ); } break; case FLC_ITEM_OBJECT: { CoreObject *arr = elem->asObjectSafe(); stream->writeString( "Object of class " + arr->generator()->symbol()->name() ); if ( level == maxLevel ) { stream->writeString( "{...}" ); break; } stream->writeString( " {\n" ); const PropertyTable &pt = arr->generator()->properties(); for( count = 0; count < pt.added() ; count++ ) { for ( i = 0; i < (level+1); i ++ ) { stream->writeString(" "); } const String &propName = *pt.getKey( count ); if ( pt.getEntry(count).isWriteOnly() ) { stream->writeString( "(" ); stream->writeString( propName + ")\n" ); } else { stream->writeString( propName + " => " ); Item dummy; arr->getProperty( propName, dummy); inspect_internal( vm, &dummy, level + 1, maxLevel, maxSize, i_stream, false, true ); } } for ( i = 0; i < level; i ++ ) { stream->writeString(" "); } stream->writeString( "}" ); } break; case FLC_ITEM_CLASS: stream->writeString( "Class " + elem->asClass()->symbol()->name() ); break; case FLC_ITEM_METHOD: { temp = "Method "; temp += "->" + elem->asMethodFunc()->name(); stream->writeString( temp ); Item itemp; elem->getMethodItem( itemp ); inspect_internal( vm, &itemp, level + 1, maxLevel, maxSize, i_stream, true, true ); for ( i = 0; i < level; i ++ ) { stream->writeString(" "); } stream->writeString( "}" ); } break; case FLC_ITEM_CLSMETHOD: temp = "Cls.Method 0x"; temp.writeNumberHex( (uint64) elem->asMethodClassOwner() ); temp += "->" + elem->asMethodClass()->symbol()->name(); stream->writeString( temp ); break; case FLC_ITEM_FUNC: { const Symbol *funcSym = elem->asFunction()->symbol(); if ( funcSym->isExtFunc() ) { stream->writeString( "Ext. Function " + funcSym->name() ); } else { stream->writeString( "Function " + funcSym->name() ); FuncDef *def = funcSym->getFuncDef(); uint32 itemId = def->onceItemId(); if ( itemId != FuncDef::NO_STATE ) { if ( elem->asFunction()->liveModule()->globals()[ itemId ].isNil() ) stream->writeString( "{ not called }"); else stream->writeString( "{ called }"); } } } break; case FLC_ITEM_REFERENCE: stream->writeString( "Ref to " ); inspect_internal( vm, elem->dereference(), level + 1, maxLevel, maxSize, i_stream, false, true ); break; default: stream->writeString( "Invalid type"); } if ( addline ) stream->writeString( "\n" ); stream->flush(); } /*# @function inspect @inset core_basic_io @param item The item to be inspected. @optparam depth Maximum inspect depth. @optparam maxLen Limit the display size of possibly very long items as i.e. strings or membufs. @optparam stream Different stream where to send the dump. @brief Displays the deep contents of an item. This is mainly a debugging function that prints all the available informations on the item on the auxiliary stream. This function should not be used except for testing scripts and checking what they put in arrays, dictionaries, objects, classes or simple items. Output is sent to the VM auxiliary stream; for stand-alone scripts, this translates into the "standard error stream". Embedders may provide simple debugging facilities by overloading and intercepting the VM auxiliary stream and provide separate output for that. This function traverses arrays and items deeply; there isn't any protection against circular references, which may cause endless loop. However, the default maximum depth is 3, which is a good depth for debugging (goes deep, but doesn't dig beyond average interesting points). Set to -1 to have infinite depth. By default, only the first 60 characters of strings and elements of membufs are displayed. You may change this default by providing a @b maxLen parameter. You may create personalized inspect functions using forward bindings, like the following: @code compactInspect = .[inspect depth|1 maxLen|15] @endcode And then, you may inspect a list of item with something like: @code linsp = .[ dolist _compactInspect x ] linsp( ["hello", "world"] ) @endcode */ FALCON_FUNC inspect ( ::Falcon::VMachine *vm ) { Item *i_item = vm->param(0); Item *i_depth = vm->param(1); Item *i_maxLen = vm->param(2); Item *i_stream = vm->param(3); if ( i_item == 0 || ( i_depth != 0 && ! i_depth->isNil() && ! i_depth->isOrdinal() ) || ( i_maxLen != 0 && ! i_maxLen->isNil() && ! i_maxLen->isOrdinal() ) || ( i_stream != 0 && ! i_stream->isNil() && ! i_stream->isOfClass("Stream") ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra("X,[N],[N],[Stream]") ); } int32 depth = (int32) (i_depth == 0 || i_depth->isNil() ? 3 : i_depth->forceInteger()); int32 maxlen = (int32) (i_maxLen == 0 || i_maxLen->isNil() ? 60 : i_maxLen->forceInteger()); inspect_internal( vm, i_item, 0, depth, maxlen, i_stream ); } static void describe_internal( VMachine *vm, String &tgt, const Item *elem, int32 level, int32 maxLevel, int32 maxSize ) { uint32 count; // return if we reached the maximum level. if ( maxLevel >= 0 && level > maxLevel ) { tgt += "..."; return; } switch( elem->type() ) { case FLC_ITEM_NIL: tgt += "Nil"; break; case FLC_ITEM_UNB: tgt += "_"; break; case FLC_ITEM_BOOL: tgt += elem->asBoolean() ? "true" : "false"; break; case FLC_ITEM_INT: tgt.writeNumber( elem->asInteger() ); break; case FLC_ITEM_NUM: tgt.writeNumber( elem->asNumeric(), "%g" ); break; case FLC_ITEM_RANGE: elem->toString(tgt); break; case FLC_ITEM_STRING: tgt += "\""; if ( maxSize < 0 || elem->asString()->length() < (uint32) maxSize ) { tgt += *elem->asString(); tgt += "\""; } else { tgt += elem->asString()->subString(0, maxSize ); tgt += " ... \""; } break; case FLC_ITEM_LBIND: tgt += "&"; tgt += *elem->asLBind(); if (elem->isFutureBind()) { tgt +="|"; describe_internal( vm, tgt, &elem->asFutureBind(), level+1, maxLevel, maxSize ); } break; case FLC_ITEM_MEMBUF: { MemBuf *mb = elem->asMemBuf(); tgt += "MB("; tgt.writeNumber( (int64) mb->length() ); tgt += ","; tgt.writeNumber( (int64) mb->wordSize() ); tgt += ")"; tgt += " ["; String fmt; int limit = 0; switch ( mb->wordSize() ) { case 1: fmt = "%02" LLFMT "X"; limit = 24; break; case 2: fmt = "%04" LLFMT "X"; limit = 12; break; case 3: fmt = "%06" LLFMT "X"; limit = 9; break; case 4: fmt = "%08" LLFMT "X"; limit = 6; break; } uint32 max = maxSize < 0 || mb->length() < (uint32) maxSize ? mb->length() : (uint32) maxSize; for( count = 0; count < max; count++ ) { tgt.writeNumber( (int64) mb->get( count ), fmt ); tgt += " "; } if ( count == (uint32) maxSize ) tgt += " ..."; tgt += "]"; } break; case FLC_ITEM_ARRAY: { CoreArray *arr = elem->asArray(); tgt += "["; if ( level == maxLevel ) { tgt += "...]"; break; } for( count = 0; count < arr->length(); count++ ) { if ( count == 0 ) tgt += " "; describe_internal( vm, tgt, & ((*arr)[count]), level + 1, maxLevel, maxSize ); if ( count + 1 < arr->length() ) tgt += ", "; } tgt +="]"; } break; case FLC_ITEM_DICT: { CoreDict *dict = elem->asDict(); if( dict->isBlessed() ) tgt += "*"; tgt += "["; if ( level == maxLevel ) { tgt += "...=>...]"; break; } if ( dict->length() == 0 ) { tgt += "=>]"; break; } Item key, value; Iterator iter( &dict->items() ); // separate the first loop to be able to add ", " describe_internal( vm, tgt, &iter.getCurrentKey(), level + 1, maxLevel, maxSize ); tgt += " => "; describe_internal( vm, tgt, &iter.getCurrent(), level + 1, maxLevel, maxSize ); iter.next(); while( iter.hasCurrent() ) { tgt += ", "; describe_internal( vm, tgt, &iter.getCurrentKey(), level + 1, maxLevel, maxSize ); tgt += " => "; describe_internal( vm, tgt, &iter.getCurrent(), level + 1, maxLevel, maxSize ); iter.next(); } tgt += "]"; } break; case FLC_ITEM_OBJECT: { CoreObject *arr = elem->asObjectSafe(); tgt += arr->generator()->symbol()->name() + "(){ "; if ( level == maxLevel ) { tgt += "...}"; break; } const PropertyTable &pt = arr->generator()->properties(); for( count = 0; count < pt.added() ; count++ ) { const String &propName = *pt.getKey( count ); // write only? if ( pt.getEntry( count ).isWriteOnly() ) { tgt.A( "(" ).A( propName ).A(")"); } else { Item dummy; arr->getProperty( propName, dummy ); // in describe skip methods. if ( dummy.isFunction() || dummy.isMethod() ) continue; tgt += propName + " = "; describe_internal( vm, tgt, &dummy, level + 1, maxLevel, maxSize ); } if (count+1 < pt.added()) { tgt += ", "; } } tgt += "}"; } break; case FLC_ITEM_CLASS: tgt += "Class " + elem->asClass()->symbol()->name(); break; case FLC_ITEM_METHOD: { tgt += "("; Item itemp; elem->getMethodItem( itemp ); describe_internal( vm, tgt, &itemp, level + 1, maxLevel, maxSize ); tgt += ")."; tgt += elem->asMethodFunc()->name() + "()"; } break; case FLC_ITEM_CLSMETHOD: tgt += "Class "; tgt += elem->asMethodClassOwner()->generator()->symbol()->name(); tgt += "." + elem->asMethodClass()->symbol()->name() + "()"; break; case FLC_ITEM_FUNC: { const Symbol *funcSym = elem->asFunction()->symbol(); tgt += funcSym->name() + "()"; } break; case FLC_ITEM_REFERENCE: tgt += "->"; describe_internal( vm, tgt, elem->dereference(), level + 1, maxLevel, maxSize ); break; default: tgt += "?"; } } /*# @function describe @param item The item to be inspected. @optparam depth Maximum inspect depth. @optparam maxLen Limit the display size of possibly very long items as i.e. strings or membufs. @brief Returns the deep contents of an item on a string representation. This function returns a string containing a representation of the given item. If the item is deep (an array, an instance, a dictionary) the contents are also passed through this function. This function traverses arrays and items deeply; there isn't any protection against circular references, which may cause endless loop. However, the default maximum depth is 3, which is a good depth for debugging (goes deep, but doesn't dig beyond average interesting points). Set to -1 to have infinite depth. By default, only the first 60 characters of strings and elements of membufs are displayed. You may change this default by providing a @b maxLen parameter. You may create personalized inspect functions using forward bindings, like the following: @code compactDescribe = .[inspect depth|1 maxLen|15] @endcode */ /*# @method describe BOM @optparam depth Maximum inspect depth. @optparam maxLen Limit the display size of possibly very long items as i.e. strings or membufs. @brief Returns the deep contents of an item on a string representation. This method returns a string containing a representation of this item. If the item is deep (an array, an instance, a dictionary) the contents are also passed through this function. This method traverses arrays and items deeply; there isn't any protection against circular references, which may cause endless loop. However, the default maximum depth is 3, which is a good depth for debugging (goes deep, but doesn't dig beyond average interesting points). Set to -1 to have infinite depth. By default, only the first 60 characters of strings and elements of membufs are displayed. You may change this default by providing a @b maxLen parameter. */ FALCON_FUNC mth_describe ( ::Falcon::VMachine *vm ) { Item *i_item; Item *i_depth; Item *i_maxLen; if( vm->self().isMethodic() ) { i_item = &vm->self(); i_depth = vm->param(0); i_maxLen = vm->param(1); } else { i_item = vm->param(0); i_depth = vm->param(1); i_maxLen = vm->param(2); } if ( i_item == 0 || ( i_depth != 0 && ! i_depth->isNil() && ! i_depth->isOrdinal() ) || ( i_maxLen != 0 && ! i_maxLen->isNil() && ! i_maxLen->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra( vm->self().isMethodic() ? "[N],[N]" : "X,[N],[N]") ); } int32 depth = (int32) (i_depth == 0 || i_depth->isNil() ? 3 : i_depth->forceInteger()); int32 maxlen = (int32) (i_maxLen == 0 || i_maxLen->isNil() ? 60 : i_maxLen->forceInteger()); String temp; describe_internal( vm, temp, i_item, 0, depth, maxlen ); CoreString* res = new CoreString(temp); vm->retval( res ); } }} /* end of inspect.cpp */ engine/core_module/item_ext.cpp000066400000000000000000001336111176363201700171550ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: item_ext.cpp Generic item handling ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 14 Aug 2008 00:17:31 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ #include "core_module.h" #include #include namespace Falcon { namespace core { /*# @funset generic_item_api Type mangling @brief Functions managing item conversions, type detection, item structure management and generically manipulating Falcon items. @beginset generic_item_api */ /*# @function len @brief Retrieves the length of a collection @param item an item of any kind @return the count of items in the sequence, or 0. The returned value represent the "size" of the item passed as a parameter. The number is consistent with the object type: in case of a string, it represents the count of characters, in case of arrays or dictionaries it represents the number of elements, in all the other cases the returned value is 0. */ /*# @method len BOM @brief Retrieves the length of a collection @return the count of items in the sequence, or 0. The returned value represent the "size" of this item. @see len */ FALCON_FUNC mth_len ( ::Falcon::VMachine *vm ) { Item *elem; if ( ! vm->self().isMethodic() ) elem = vm->param( 0 ); else elem = &vm->self(); if ( elem == 0 ) { vm->retval( 0 ); return; } switch( elem->type() ) { case FLC_ITEM_STRING: vm->retval( (int64) elem->asString()->length() ); break; case FLC_ITEM_ARRAY: vm->retval( (int64) elem->asArray()->length() ); break; case FLC_ITEM_MEMBUF: vm->retval( (int64) elem->asMemBuf()->length() ); break; case FLC_ITEM_DICT: vm->retval( (int64) elem->asDict()->length() ); break; case FLC_ITEM_RANGE: vm->retval( 3 ); break; default: vm->retval( 0 ); } } /*# @function isBound @param item @brief Determines if an item is bound or not. @return True if the item is bound. @see BOM.bound */ /*# @method bound BOM @brief Determines if an item is bound or not. @return True if the item is bound. @see isBound */ FALCON_FUNC mth_bound( ::Falcon::VMachine *vm ) { Item *elem; if ( ! vm->self().isMethodic() ) { elem = vm->param( 0 ); if ( elem == 0 ) { vm->regA().setBoolean( false ); return; } } else elem = &vm->self(); vm->regA().setBoolean( ! elem->isUnbound() ); } /*# @function int @brief Converts the given parameter to integer. @param item The item to be converted @return An integer value. @raise ParseError in case the given string cannot be converted to an integer. @raise MathError if a given floating point value is too large to be converted to an integer. Integer values are just copied. Floating point values are converted to long integer; in case they are too big to be represented a RangeError is raised. Strings are converted from base 10. If the string cannot be converted, or if the value is anything else, a MathError instance is raised. */ FALCON_FUNC val_int ( ::Falcon::VMachine *vm ) { if ( vm->paramCount() == 0 ) { vm->retnil(); return; } Item *to_int = vm->param(0); switch( to_int->type() ) { case FLC_ITEM_INT: vm->retval( to_int->asInteger() ); break; case FLC_ITEM_NUM: { numeric num = to_int->asNumeric(); if ( num > 9.223372036854775808e18 || num < -9.223372036854775808e18 ) { throw new MathError( ErrorParam( e_domain, __LINE__ ).origin( e_orig_runtime ) ); } vm->retval( (int64)num ); } break; case FLC_ITEM_STRING: { String *cs = to_int->asString(); int64 val; if ( ! cs->parseInt( val ) ) { numeric nval; if ( cs->parseDouble( nval ) ) { if ( nval > 9.223372036854775808e18 || nval < -9.223372036854775808e18 ) { throw new MathError( ErrorParam( e_domain, __LINE__ ).origin( e_orig_runtime ) ); } vm->retval( (int64) nval ); return; } throw new ParseError( ErrorParam( e_numparse, __LINE__ ).origin( e_orig_runtime ) ); } vm->retval( val ); } break; default: throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ).extra( "N|S" ) ); } } /*# @function numeric @brief Converts the given parameter to numeric. @param item The item to be converted @return A numeric value. @raise ParseError in case the given string cannot be converted to an integer. @raise MathError if a given floating point value is too large to be converted to an integer. Floating point values are just copied. Integer values are converted to floating point; in case of very large integers, precision may be lost. Strings are converted from base 10. If the string cannot be converted, or if the value is anything else, a MathError instance is raised. */ FALCON_FUNC val_numeric ( ::Falcon::VMachine *vm ) { if ( vm->paramCount() == 0 ) { vm->retnil(); return; } Item *to_numeric = vm->param(0); switch( to_numeric->type() ) { case FLC_ITEM_NUM: vm->retval( to_numeric->asNumeric() ); break; case FLC_ITEM_INT: { int64 num = to_numeric->asInteger(); vm->retval( (numeric)num ); } break; case FLC_ITEM_STRING: { String *cs = to_numeric->asString(); numeric value; if ( ! cs->parseDouble( value ) ) { throw new ParseError( ErrorParam( e_numparse, __LINE__ ).origin( e_orig_runtime ) ); } vm->retval( value ); } break; default: throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra( "(N|S)" ) ); } } /*# @function typeOf @param item An item of any kind. @brief Returns an integer indicating the type of an item. @return A constant indicating the type of the item. The typeId returned is an integer; the Falcon compiler is fed with a set of compile time constants that can be used to determine the type of an item. Those constants are always available at Falcon sources. The value returned may be one of the following: - @b NilType - the item is NIL - @b BooleanType - the item is true or false - @b NumericType - the item is a number - @b RangeType - the item is a range (a pair of two integers) - @b FunctionType - the item is a function - @b StringType - the item is a string - @b LBindType - the item is a late binding symbol - @b MemBufType - the item is a Memory Buffer Table - @b ArrayType - the item is an array - @b DictionaryType - the item is a dictionary - @b ObjectType - the item is an object - @b ClassType - the item is a class - @b MethodType - the item is a method - @b ClassMethodType - the item is a method inside a class */ /*# @method typeId BOM @brief Returns an integer indicating the type of this item. @return A constant indicating the type of the item. See @a typeOf() function for details. */ FALCON_FUNC mth_typeId ( ::Falcon::VMachine *vm ) { byte type; if ( vm->self().isMethodic() ) type = vm->self().dereference()->type(); else { if ( vm->paramCount() > 0 ) type = vm->param(0)->type(); else throw new ParamError( ErrorParam( e_inv_params ).origin( e_orig_runtime ).extra( "X" ) ); } vm->regA() = (int64) ( type == FLC_ITEM_INT ? FLC_ITEM_NUM : type ); } /*# @function isCallable @brief Determines if an item is callable. @inset generic_item_api @param item The item to be converted @return true if the item is callable, false otheriwse. If the function returns true, then the call operator can be applied. If it returns false, the item is not a callable one, and trying to call it would cause an error. */ /*# @method isCallable BOM @brief Determines if an item is callable. @return true if the item is callable, false otheriwse. If the function returns true, then the call operator can be applied. If it returns false, the item is not a callable one, and trying to call it would cause an error. */ FALCON_FUNC mth_isCallable ( ::Falcon::VMachine *vm ) { if ( vm->self().isMethodic() ) vm->regA().setBoolean( vm->self().isCallable() ); else { if ( vm->paramCount() > 0 ) vm->regA().setBoolean( vm->param( 0 )->isCallable() ? 1 : 0 ); else throw new ParamError( ErrorParam( e_inv_params ).origin( e_orig_runtime ).extra( "X" ) ); } } /*# @function getProperty @brief Returns the value of a property in an object. @param obj the source object, array or (blessed) dictionary. @param propName A string representing the name of a property or a method inside the object. @return the property @raise AccessError if the property can't be found. An item representing the property is returned. The returned value is actually a copy of the property; assigning a new value to it won't have any effect on the original object. If the property is a method, a callable method item is returned. If the property is not found, an error of class RangeError is raised. */ /*# @method getProperty Object @brief Returns the value of a property in an object. @param propName A string representing the name of a property or a method inside the object. @return the property @raise AccessError if the property can't be found. An item representing the property is returned. The returned value is actually a copy of the property; assigning a new value to it won't have any effect on the original object. If the property is a method, a callable method item is returned. If the property is not found, an error of class RangeError is raised. */ FALCON_FUNC mth_getProperty( ::Falcon::VMachine *vm ) { Item *obj_x, *prop_x; if( vm->self().isMethodic() ) { obj_x = &vm->self(); prop_x = vm->param(0); } else { obj_x= vm->param(0); prop_x = vm->param(1); } if ( obj_x == 0 || ! obj_x->isDeep() || prop_x == 0 || ! prop_x->isString() ) { throw new ParamError( ErrorParam( e_inv_params ) .origin( e_orig_runtime ).extra( "O,S" ) ); } obj_x->asDeepItem()->readProperty( *prop_x->asString(), vm->regA() ); if ( vm->regA().isCallable() ) { vm->regA().methodize( obj_x->asObjectSafe() ); } } /*# @function setProperty @brief Sets the value of a proprety in a given object @param obj The source object. @param propName A string representing the name of a property or a method inside the object. @param value The property new value. @raise AccessError If the property can't be found. Alters the value of the property in the given object. If the required property is not present, an AccessError is raised. */ /*# @method setProperty Object @brief Sets the value of a proprety in this object @param propName A string representing the name of a property or a method inside the object. @param value The property new value. @raise AccessError If the property can't be found. Alters the value of the property in the given object. If the required property is not present, an AccessError is raised. */ /*# @method setProperty Array @brief Sets a binding (as a property) in the array. @param propName A string representing the name of a property or a method inside the array. @param value The property new value. @raise AccessError If the property can't be found. Alters the value of the property in the given array. If the required property is not present, an AccessError is raised. */ /*# @method setProperty Dictionary @brief Sets a property in dictionary based instances. @param propName A string representing the name of a property or a method inside the dictionary. @param value The property new value. @raise AccessError If the property can't be found. Alters the value of the property in the given dictionary. If the required property is not present, an AccessError is raised. */ FALCON_FUNC mth_setProperty( ::Falcon::VMachine *vm ) { Item *obj_x, *prop_x, *new_item; if( vm->self().isMethodic() ) { obj_x = &vm->self(); prop_x = vm->param(0); new_item = vm->param(1); } else { obj_x= vm->param(0); prop_x = vm->param(1); new_item = vm->param(2); } if ( obj_x == 0 || ! obj_x->isDeep() || prop_x == 0 || ! prop_x->isString() || new_item == 0) { throw new ParamError( ErrorParam( e_inv_params ) .origin( e_orig_runtime ).extra( "O,S" ) ); } obj_x->asDeepItem()->writeProperty( *prop_x->asString(), *new_item ); } /*# @method properties Array @brief Returns an array of properties (bindings) in the array. @return An array with 0 or more strings. This methods returns all the properties in the given array, which represents the list of array bindings. If the array has no bindings, this method returns an empty array. The property list includes properties that refer to any kind of data, including functions (that is, methods), but it doesn't include properties in the metaclass of this item (FBOM properties). The returned list is ordered by UNICODE value of the property names. */ /*# @method properties Dictionary @brief Returns all the properties in the dictionary. @return An array of strings representing property names. This method returns all the property name in this dictionary. If the dictionary is not blessed, returns an empty array. The returned list contains all those keys that are suitable to be directly accessed as properties (that is, strings without spaces, puntaction and so on). You may use @a Dictionary.keys instead if you know that all the keys can be used as properties. The property list includes properties that refer to any kind of data, including functions (that is, methods), but it doesn't include properties in the metaclass of this item (FBOM properties). The returned list is ordered by UNICODE value of the property names. */ /*# @method properties Object @brief Returns all the properties in the object. @return An array of strings representing property names. This method returns all the properties in this object. The property list includes properties that refer to any kind of data, including functions (that is, methods), but it doesn't include properties in the metaclass of this item (FBOM properties). The returned list is ordered by UNICODE value of the property names. @note Subclasses are seen as properties, so they will returned in the list too. */ /*# @method properties Class @brief Returns all the properties in the class. @return An array of strings representing property names. This method returns all the properties in this class. The property list includes properties that refer to any kind of data, including functions (that is, methods), but it doesn't include properties in the metaclass of this item (FBOM properties). The returned list is ordered by UNICODE value of the property names. @note Subclasses are seen as properties, so they will returned in the list too. */ /*# @function properties @brief Returns all the properties in the given item. @param item An item that can be accessed via dot accessor. @return An array of strings representing property names. This function returns the properties offered by an item as a list of strings in an array. FBOM methods (item metaclass methods) are not returned; only explicitly declared properties are taken into account. The item susceptible of returning an array of properties are: - Objects (see @a Object.properties) - Dictionaries (if blessed, see @a Dictionary.properties) - Arrays (see @a Array.properties) - Classes (see @a Class.properties) This function, applied to any other item type, returns @b nil. */ FALCON_FUNC mth_properties( ::Falcon::VMachine *vm ) { Item *obj_x; if( vm->self().isMethodic() ) { obj_x = &vm->self(); } else { obj_x= vm->param(0); if ( obj_x == 0 ) { throw new ParamError( ErrorParam( e_inv_params ) .origin( e_orig_runtime ).extra( "X" ) ); } } switch( obj_x->type() ) { case FLC_ITEM_OBJECT: { CoreObject *obj = obj_x->asObjectSafe(); const PropertyTable &pt = obj->generator()->properties(); CoreArray *ret = new CoreArray(pt.added()); for( uint32 count = 0; count < pt.added() ; count++ ) { const String &propName = *pt.getKey( count ); ret->append( new CoreString( propName ) ); } vm->retval( ret ); } break; case FLC_ITEM_CLASS: { CoreClass *cls= obj_x->asClass(); const PropertyTable &pt = cls->properties(); CoreArray *ret = new CoreArray(pt.added()); for( uint32 count = 0; count < pt.added() ; count++ ) { const String &propName = *pt.getKey( count ); ret->append( new CoreString( propName ) ); } vm->retval( ret ); } break; case FLC_ITEM_DICT: { CoreDict *dict = obj_x->asDict(); if ( dict->isBlessed() ) { Iterator iter( &dict->items() ); CoreArray *ret = new CoreArray( dict->length() ); while( iter.hasCurrent() ) { const Item& itm = iter.getCurrentKey(); if ( itm.isString() ) { String* str = itm.asString(); //TODO Skip impossible strings. ret->append( *str ); } iter.next(); } vm->retval( ret ); } } break; case FLC_ITEM_ARRAY: { CoreArray *arr = obj_x->asArray(); if ( arr->bindings() != 0 ) { CoreDict* dict = arr->bindings(); Iterator iter( &dict->items() ); CoreArray *ret = new CoreArray( dict->length() ); while( iter.hasCurrent() ) { const Item& itm = iter.getCurrentKey(); if ( itm.isString() ) { String* str = itm.asString(); //TODO Skip impossible strings. ret->append( *str ); } iter.next(); } vm->retval( ret ); } } break; } } static bool dop_internal( VMachine* vm ) { vm->self().asDict()->put( *vm->param(0), vm->regA() ); // in A we already have the value return false; } /*# @method dop Dictionary @brief Dictionary default operation. @param key The key to be defaulted. @param dflt The default value to be applied. @optparam oper The operation to be applied. @return The value associated with @b key after the application of the operation. Given the @b key, @b dflt and @b oper parameters, this method inserts a default value on a dictionary, eventually performing a default operation. In short, if the @b key is not present in the dictionary, a new key is created and the @b dflt value is assigned to it. If a @b oper callable item (function) is given, then the current value associated with the key is passed to it as a parameter; in case that the key still doesn't exist, the @b dflt value is passed instead. In both case, the key is then associated with the return value of the @b oper function. Finally, this method return the value that has just been associated with the dictionary. More coinciserly the method works along the following pseudocode rules: @code function dop of dict, key, dflt and oper if key exists in dict if oper is a callable entity value = oper( dict[key] ) dict[key] = value else value = dict[key] end else if oper is a callable entity value = oper( dflt ) dict[key] = value else value = oper( dflt ) end end return value end @endcode This function comes extremely convenient when in need to do some complex operations on a possibly uninitialized dictionary value. Suppose that an application stores the list of currently logged in-users in an array under the "users" key of a given prog_data dictionary. Then, @code // a new user comes in... newcomer = ... users = prog_data.dop( "users", [], { v => v += newcomer } ) @endcode In one line, this code creates a "users" entry in prog_data, if it doesn't exists, which is initialized to an empty array. The empty array is then lenghtened and also returned, so that the program has already it handy without having to scan for it in program_data again. */ FALCON_FUNC Dictionary_dop ( ::Falcon::VMachine *vm ) { Item *i_key = vm->param(0); Item *i_dflt = vm->param(1); Item *i_oper = vm->param(2); if( i_key == 0|| i_dflt == 0 || ( i_oper != 0 && ! i_oper->isCallable() ) ) { throw new ParamError( ErrorParam( e_inv_params ) .origin( e_orig_runtime ).extra( "X,X,[C]" ) ); } CoreDict *cd = vm->self().asDict(); // find our key -- will be 0 if not found Item *i_val = cd->find(*i_key); if( i_oper == 0 ) { // if we have no operations, we're done here if( i_val == 0 ) { // if the value was not found, set the default cd->put( *i_key, *i_dflt ); vm->retval( *i_dflt ); } else { // otherwise, don't do anything, but return the found value vm->retval( *i_val ); } } else { // we have to call our function vm->returnHandler( &dop_internal ); vm->pushParam( i_val == 0 ? *i_dflt : *i_val ); vm->callItem( *vm->param(2), 1 ); // stack may change -- use vm->param } } /*# @function chr @brief Returns a string containing a single character that corresponds to the given number. @inset generic_item_api @param number Numeric code of the desired character @return a single-char string. This function returns a single character string whose only character is the UNICODE equivalent for the given number. The number must be a valid UNICODE character, so it must be in range 0-0xFFFFFFFF. */ FALCON_FUNC chr ( ::Falcon::VMachine *vm ) { uint32 val; Item *elem = vm->param(0); if ( elem == 0 ) return; if ( elem->type() == FLC_ITEM_INT ) val = (uint32) elem->asInteger(); else if ( elem->type() == FLC_ITEM_NUM ) val = (uint32) elem->asNumeric(); else { throw new ParamError( ErrorParam( e_inv_params ) .origin( e_orig_runtime ).extra( "N" ) ); } CoreString *ret = new CoreString; ret->append( val ); vm->retval( ret ); } /*# @function ord @brief Returns the numeric UNICODE ID of a given character. @inset generic_item_api @param string The character for which the ID is requested. @return the UNICODE value of the first element in the string. The first character in string is taken, and it's numeric ID is returned. @see chr */ FALCON_FUNC ord ( ::Falcon::VMachine *vm ) { Item *elem = vm->param(0); if ( elem == 0 || ! elem->isString() || elem->asString()->size() == 0 ) { throw new ParamError( ErrorParam( e_inv_params).origin( e_orig_runtime ).extra( "S" ) ); return; } vm->retval( (int64) elem->asString()->getCharAt(0) ); } /*# @function toString @brief Returns a string representation of the item. @param item The item to be converted to string. @optparam format Specific object format. @return the string representation of the item. This function is useful to convert an unknown value in a string. The item may be any kind of Falcon item; the following rules apply: - Nil items are represented as "" - Integers are converted in base 10. - Floating point values are converted in base 10 with a default precision of 6; numprec may be specified to change the default precision. - Array and dictionaries are represented as "Array of 'n' elements" or "Dictionary of 'n' elements". - Strings are copied. - Objects are represented as "Object 'name of class'", but if a toString() method is provided by the object, that one is called instead. - Classes and other kind of opaque items are rendered with their names. This function is not meant to provide complex applications with pretty-print facilities, but just to provide simple scripts with a simple and consistent output facility. If a @b format parameter is given, the format will be passed unparsed to toString() methods of underlying items. @see Format */ /*# @method toString BOM @brief Coverts the object to string. @optparam format Optional object-specific format string. Calling this BOM method is equivalent to call toString() core function passing the item as the first parameter. Returns a string representation of the given item. If applied on strings, it returns the string as is, while it converts numbers with default internal conversion. Ranges are represented as "[N:M:S]" where N and M are respectively lower and higher limits of the range, and S is the step. Nil values are represented as "Nil". The format parameter is not a Falcon format specification, but a specific optional object-specific format that may be passed to objects willing to use them. In example, the TimeStamp class uses this parameter to format its string representation. */ FALCON_FUNC mth_ToString ( ::Falcon::VMachine *vm ) { Item *elem; Item *format; // methodic? if ( vm->self().isMethodic() ) { elem = &vm->self(); format = vm->param(0); } else { elem = vm->param(0); format = vm->param(1); } if(elem == 0) { throw new ParamError( ErrorParam( e_inv_params ) .origin( e_orig_runtime ).extra( vm->self().isMethodic() ? "[S]" : "X,[S]" ) ); } CoreString *target = 0; if ( format != 0 ) { if ( format->isString() ) { Format fmt( *format->asString() ); if ( ! fmt.isValid() ) { throw new ParamError( ErrorParam( e_param_fmt_code ). extra( *format->asString() ) ); } else { target = new CoreString; fmt.format( vm, *elem, *target ); } } else { throw new ParamError( ErrorParam( e_inv_params ) .origin( e_orig_runtime ).extra( vm->self().isMethodic() ? "[S]" : "X,[S]" ) ); } } else { target = new CoreString; if ( vm->self().isMethodic() ) { elem->toString( *target ); } else { vm->itemToString( *target, elem ); } } vm->retval( target ); } /*# @method compare BOM @brief Performs a lexicographical comparison. @param item The item to which this object must be compared. @return -1, 0 or 1 depending on the comparation result. Performs a lexicographical comparison between the self item and the item passed as a parameter. If the item is found smaller than the parameter, it returns -1; if the item is greater than the parameter, it returns 1. If the two items are equal, it returns 0. The compare method, if overloaded, is used by the Virtual Machine to perform tests on unknown types (i.e. objects), and to sort dictionary keys. Item different by type are ordered by their type ID, as indicated in the documentation of the @a typeOf core function. By default, string comparison is performed in UNICODE character order, and objects, classes, vectors, and dictionaries are ordered by their internal pointer address. */ /*# @function compare @brief Performs a lexicographical comparison. @param operand1 The item to which this object must be compared. @param operand2 The item to which this object must be compared. @return -1, 0 or 1 depending on the comparation result. Performs a lexicographical comparison between the self item and the item passed as a parameter. If the item is found smaller than the parameter, it returns -1; if the item is greater than the parameter, it returns 1. If the two items are equal, it returns 0. The compare method, if overloaded, is used by the Virtual Machine to perform tests on unknown types (i.e. objects), and to sort dictionary keys. Item different by type are ordered by their type ID, as indicated in the documentation of the @a typeOf core function. By default, string comparison is performed in UNICODE character order, and objects, classes, vectors, and dictionaries are ordered by their internal pointer address. */ FALCON_FUNC mth_compare( VMachine *vm ) { Item *first; Item *second; if( vm->self().isMethodic() ) { first = &vm->self(); second = vm->param(0); } else { first = vm->param(0); second = vm->param(1); } if( first == 0 || second == 0 ) { throw new ParamError( ErrorParam( e_inv_params ) .origin( e_orig_runtime ).extra( vm->self().isMethodic() ? "X" : "X,X" ) ); } vm->retval( (int64) first->compare(*second) ); } /*# @method clone BOM @brief Performs a deep copy of the item. @return A copy of the item. @raise CloneError if the item is not cloneable. Returns an item equal to the current one, but phisically separated. If the item is a sequence, only the first level of the item gets actually cloned: vectors and dictionaries gets cloned, but the items they store are just copied. This means that the new copy of the collection itself may change, and the older version will stay untouched, but if a deep item in the collection (as an object) is changed, its change will be reflected also in the original collection. Cloning objects causes each of their properties to be cloned. If they store an internal user data which is provided by extension modules or embedding applications, that data is cloned too. Behavior of user data is beyond the control of the script, and the data may actually be just referenced or it may also refuse to be cloned. In that case, this method will raise a CloneError, which indicates that a deep user data provided by an external module or application doesn't provide a cloning feature. @note Cloning objects that stores other objects referencing themselves in their properties may cause an endless loop in this version. To provide a safe duplicate of objects that may be organized in circular hierarcies, overload the clone method so that it creates a new instance of the item and just performs a flat copy of the properties. */ /*# @function clone @brief Performs a deep copy of the item. @param item The item to be copied. @return A copy of the item. @raise CloneError if the item is not cloneable. Returns an item equal to the @b item, but phisically separated. If the item is a sequence, only the first level of the item gets actually cloned: vectors and dictionaries gets cloned, but the items they store are just copied. This means that the new copy of the collection itself may change, and the older version will stay untouched, but if a deep item in the collection (as an object) is changed, its change will be reflected also in the original collection. Cloning objects causes each of their properties to be cloned. If they store an internal user data which is provided by extension modules or embedding applications, that data is cloned too. Behavior of user data is beyond the control of the script, and the data may actually be just referenced or it may also refuse to be cloned. In that case, this method will raise a CloneError, which indicates that a deep user data provided by an external module or application doesn't provide a cloning feature. @note Cloning objects that stores other objects referencing themselves in their properties may cause an endless loop in this version. To provide a safe duplicate of objects that may be organized in circular hierarcies, overload the clone method so that it creates a new instance of the item and just performs a flat copy of the properties. */ FALCON_FUNC mth_clone( VMachine *vm ) { bool result; if( vm->self().isMethodic() ) { result = vm->self().clone( vm->regA() ); } else { if( vm->paramCount() == 0 ) { throw new ParamError( ErrorParam( e_inv_params ).origin( e_orig_runtime ).extra("X") ); } else { result = vm->param(0)->clone( vm->regA() ); } } if( ! result ) throw new CloneError( ErrorParam( e_uncloneable, __LINE__ ) .hard() .origin( e_orig_runtime ) ); } /*# @method className BOM @brief Returns the name of the class an instance is instantiated from. @return The class name of an object (a string) or nil. If applied to objects, returns the name of the class of which the object is an instance. When applied to classes, it return the class symbolic name. In all other cases, return nil. @see className */ /*# @function className @brief Returns the name of the class an instance is instantiated from. @param The item to be checked. @return The class name of an object (a string) or nil. If applied to objects, returns the name of the class of which the object is an instance. When applied to classes, it return the class symbolic name. In all other cases, return nil. @see BOM.className */ FALCON_FUNC mth_className( VMachine *vm ) { Item *self; if ( vm->self().isMethodic() ) { self = &vm->self(); } else { self = vm->param(0); if ( self == 0 ) { throw new ParamError( ErrorParam( e_inv_params ) .origin( e_orig_runtime ).extra("X") ); return; } } switch( self->type() ) { case FLC_ITEM_OBJECT: vm->retval( new CoreString( vm->self().asObject()->generator()->symbol()->name() ) ); break; case FLC_ITEM_CLASS: vm->retval( new CoreString( vm->self().asClass()->symbol()->name() ) ); break; default: vm->retnil(); } } /*# @method baseClass BOM @brief Returns the class item from which an object has been instantiated. @return A class item or nil. If applied on objects, returns the class item that has been used to instantiate an object. Calling the returned item is equivalent to call the class that instantiated this object. The returned item can be used to create another instance of the same class, or for comparisons on @b select branches. If the item on which this method is applied is not an object, it returns nil. @see baseClass */ /*# @function baseClass @brief Returns the class item from which an object has been instantiated. @param item @return A class item or nil. If applied on objects, returns the class item that has been used to instantiate an object. Calling the returned item is equivalent to call the class that instantiated this object. The returned item can be used to create another instance of the same class, or for comparisons on @b select branches. If the item on which this method is applied is not an object, it returns nil. @see BOM.baseClass */ FALCON_FUNC mth_baseClass( VMachine *vm ) { Item *self; if ( vm->self().isMethodic() ) { self = &vm->self(); } else { self = vm->param(0); if ( self == 0 ) { throw new ParamError( ErrorParam( e_inv_params ) .origin( e_orig_runtime ).extra("X") ); } } if( self->isObject() ) { CoreClass* cls = const_cast(self->asObject()->generator()); if ( cls != 0 ) { vm->retval( cls ); return; } } vm->retnil(); } /*# @method derivedFrom BOM @brief Checks if this item has a given parent. @param cls A symbolic class name or a class instance. @return true if the given class is one of the ancestors of this item. If applied on objects, returns true if the given parameter is the name of the one of the classes that compose the class hierarchy of the object. If applied on class instances, it returns true if the parameter is its name or the name of one of its ancestors. In all the other cases, it return false. It is also possible to use directly the class instance as a parameter, instead of a class name. In example: @code object MyError from Error //... end > "Is MyError derived from 'Error' (by name)?: ", \ MyError.derivedFrom( "Error" ) > "Is MyError derived from 'Error' (by class)?: ", \ MyError.derivedFrom( Error ) @endcode @see derivedFrom */ /*# @function derivedFrom @brief Checks if this item has a given parent. @param item The item to be checked. @param cls A symbolic class name or a class instance. @return true if the given class is one of the ancestors of this item. If applied on objects, returns true if the given parameter is the name of the one of the classes that compose the class hierarchy of the object. If applied on class instances, it returns true if the parameter is its name or the name of one of its ancestors. In all the other cases, it return false. It is also possible to use directly the class instance as a parameter, instead of a class name. In example: @code object MyError from Error //... end > "Is MyError derived from 'Error' (by name)?: ", \ derivedFrom( MyError, "Error" ) > "Is MyError derived from 'Error' (by class)?: ", \ derivedFrom( MyError, Error ) @endcode @see BOM.derivedFrom */ FALCON_FUNC mth_derivedFrom( VMachine *vm ) { Item *i_clsName; Item *self; if( vm->self().isMethodic() ) { self = &vm->self(); i_clsName = vm->param( 0 ); } else { self = vm->param(0); i_clsName = vm->param(1); } if( i_clsName == 0 || ! (i_clsName->isString() || i_clsName->isClass()) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ).extra( "S|C" ) ); } if ( i_clsName->isString() ) { const String *name = i_clsName->asString(); switch( self->type() ) { case FLC_ITEM_OBJECT: vm->regA().setBoolean( (bool)self->asObjectSafe()-> derivedFrom( *name ) ); break; case FLC_ITEM_CLASS: vm->regA().setBoolean( (bool)self->asClass()->derivedFrom( *name ) ); break; default: vm->regA().setBoolean( false ); } } else { switch( self->type() ) { case FLC_ITEM_OBJECT: vm->regA().setBoolean( (bool)self->asObjectSafe()->generator()->derivedFrom( i_clsName->asClass()->symbol() ) ); break; case FLC_ITEM_CLASS: vm->regA().setBoolean( (bool)self->asClass()->derivedFrom( i_clsName->asClass()->symbol() ) ); break; default: vm->regA().setBoolean( false ); } } } /*# @method source Method @brief Returns the object associated with this method. @return The object from which this method was created. Returns an object (or an item, in case this is a BOM method) that gave birth to this method. */ FALCON_FUNC Method_source( VMachine *vm ) { Item *self = vm->self().dereference(); vm->retval( self->isMethod() ? self->asMethodItem() : *self ); } /*# @method base Method @brief Returns the function or the array associated with this method. @return The function or array that is applied on the associated object. Returns a function or a callable array that is associated with the object in this method. This method cannot return external functions; only function generated by Falcon native modules can be returned. This is because externa functions are not suited to be extracted from methods and eventually re-associated with other objects. This method will raise an error in case it refers to an external function. However, it can be safely used to extract base callable arrays. */ FALCON_FUNC Method_base( VMachine *vm ) { Item *self = vm->self().dereference(); if ( self->isMethod() ) { if ( ! self->asMethodFunc()->isFunc() ) { // an array vm->retval( dyncast(self->asMethodFunc()) ); return; } else { CoreFunc* func = dyncast(self->asMethodFunc()); if ( ! func->symbol()->isExtFunc() ) { vm->regA().setFunction( func ); return; } } } throw new AccessError( ErrorParam( e_acc_forbidden, __LINE__ ) .origin( e_orig_runtime ) .extra( "External function ") ); } /*# @method metaclass BOM @brief Returns the metaclass associated with this item. @return The metaclass of this item. */ /*# @function metaclass @brief Returns the metaclass associated with the given item. @param item The item of which the metaclass must be found. @return The metaclass of this item. */ FALCON_FUNC mth_metaclass( VMachine *vm ) { Item *self; if ( vm->self().isMethodic() ) { self = &vm->self(); } else { self = vm->param(0); if ( self == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("X") ); } } CoreClass* cls = vm->getMetaClass( self->type() ); fassert( cls ); vm->retval( cls ); } /*# @method ptr BOM @brief Returns a raw memory pointer out of this data (as an integer). @return An integer containing a pointer to this data. The default behavior of this method is to return the value of the memory location where inner data is stored, if the data is deep, and 0 otherwise. The Integer metaclass overrides this method so that it returns a dereferenced pointer, that is, the pointer value at the location indicated by the integer value, and String items overload this so that a pointer to the raw character sequence is returned. */ FALCON_FUNC BOM_ptr( VMachine *vm ) { if ( vm->self().isDeep() ) { vm->retval( (int64) vm->self().asDeepItem() ); } vm->retval( (int64) 0 ); } /*# @method ptr Integer @brief Returns the value itself. @return The value in this integer. Falcon integers can be used to store memory locations, as the are granted to be wide at least as the widest pointer on the target platform. For this reason, they can be used to transport raw pointers coming from external libraries. This function override ensures that .ptr() applied to an integer returns the original integer value (and doesn't get mangled as with other ptr overrides). */ FALCON_FUNC Integer_ptr( VMachine *vm ) { vm->retval( vm->self().asInteger() ); } /*# @method ptr GarbagePointer @brief Returns the inner data stored in this pointer. @return Deep data (as a pointer). This function returns a pointer value (stored in a Falcon integer) pointing to the inner FalconData served this garbage pointer. */ FALCON_FUNC GarbagePointer_ptr( VMachine *vm ) { vm->retval( (int64) vm->self().asGCPointer() ); } /*# @method ptr String @brief Returns a pointer to raw data contained in this string. @return A string pointer. This function returns a pointer value (stored in a Falcon integer) pointing to the raw data in the string. The string is not encoded in any format, and its character size can be 1, 2 or 4 bytes per character depending on the values previusly stored. The string is granted to be terminated by an appropriate "\\0" value of the correct size. The value exists and is valid only while the original string (this item) stays unchanged. */ FALCON_FUNC String_ptr( VMachine *vm ) { String *str = vm->self().asString(); str->c_ize(); vm->retval( (int64) str->getRawStorage() ); } /*# @method ptr MemoryBuffer @brief Returns the pointer to the raw memory stored in this memory buffer. @return A memory pointer. This function returns a pointer (as a Falcon integer) to the memory area managed by this memory buffer. */ FALCON_FUNC MemoryBuffer_ptr( VMachine *vm ) { vm->retval( (int64) vm->self().asMemBuf()->data() ); } /*# @method value LateBinding @brief Returns the value associated with a late binding. @return A value or nil if no value is associated. To determine if this binding has a "nil" value associated, use @a LateBinding.bound. */ FALCON_FUNC LateBinding_value( VMachine *vm ) { if ( vm->self().isFutureBind() ) vm->retval( vm->self().asFutureBind() ); else vm->retnil(); } /*# @method symbol LateBinding @brief Returns the symbol name associated with a late binding. @return The symbol name */ FALCON_FUNC LateBinding_symbol( VMachine *vm ) { vm->retval( vm->self().asLBind() ); } /*# @method bound LateBinding @brief Checks if the late binding is bound. @return True if this late binding has a bound value. */ FALCON_FUNC LateBinding_bound( VMachine *vm ) { vm->regA().setBoolean( vm->self().isFutureBind() ); } FALCON_FUNC LateBinding_bind( VMachine *vm ) { Item* i_item = vm->param(0); if( i_item == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).extra( "X" ) ); } vm->self().setLBind( vm->self().asLBind(), new GarbageItem( *i_item ) ); vm->regA() = vm->self(); } FALCON_FUNC LateBinding_unbind( VMachine *vm ) { vm->self().setLBind( vm->self().asLBind() ); } } } engine/core_module/iterator_ext.cpp000066400000000000000000000356101176363201700200500ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: iterator_ext.cpp Support for language level iterators. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 14 Aug 2008 00:17:31 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ #include "core_module.h" #include #include #include #include namespace Falcon { namespace core { /*# @class Iterator @brief Indirect pointer to sequences. @ingroup general_purpose @param collection The collection on which to iterate. @optparam atEnd Indicator for start position. An iterator is an object meant to point to a certain position in a collection (array, dictionary, string, or eventually user defined types), and to access iteratively the elements in that collection. Iterators may be used to alter a collection by removing the item they are pointing to, or by changing its value. They can be stored to be used at a later moment, or they can be passed as parameters. Most notably, they hide the nature of the underlying collection, so that they can be used as an abstraction layer to access underlying data, one item at a time. Altering the collection may cause an iterator to become invalid; only performing write operations through an iterator it is possible to guarantee that it will stay valid after the modify. A test for iterator validity is performed on each operation, and in case the iterator is not found valid anymore, an error is raised. Iterators supports equality tests and provide an equal() method. Two iterators pointing to the same element in the same collection are considered equal; so it is possible to iterate through all the items between a start and an end. @section init Initialization The iterator is normally created at the begin of the sequence. If items in the collection can be directly accessed If @b position is given and true, the iterator starts from the end of the sequence (that is, pointing to the last valid element in the sequence), otherwise it points at the first valid element. */ FALCON_FUNC Iterator_init( ::Falcon::VMachine *vm ) { Item *collection = vm->param(0); if( collection == 0 ) { throw new ParamError( ErrorParam( e_inv_params ) .origin( e_orig_runtime ) .extra( "Sequence,[B]" ) ); return; } Iterator* iter; bool fromEnd = vm->param(1) != 0 && vm->param(1)->isTrue(); switch( collection->type() ) { case FLC_ITEM_ARRAY: iter = new Iterator( &collection->asArray()->items(), fromEnd ); break; case FLC_ITEM_DICT: iter = new Iterator( &collection->asDict()->items(), fromEnd ); break; case FLC_ITEM_OBJECT: { CoreObject* obj = collection->asObjectSafe(); Sequence* seq = obj->getSequence(); if( seq != 0 ) { iter = new Iterator( seq, fromEnd ); } else { throw new ParamError( ErrorParam( e_inv_params ) .origin( e_orig_runtime ) .extra( "Sequence,[B]" ) ); } } break; default: throw new ParamError( ErrorParam( e_inv_params ) .origin( e_orig_runtime ) .extra( "Sequence,[B]" ) ); } FalconObject *self = dyncast( vm->self().asObject() ); self->setUserData( iter ); } /*# @method hasCurrent Iterator @brief Check if the iterator is valid and can be used to access the underlying collection. @return true if the iterator is valid and can be used to access a current item. */ FALCON_FUNC Iterator_hasCurrent( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Iterator* iter = dyncast( self->getFalconData() ); vm->regA().setBoolean( iter->hasCurrent() ); } /*# @method hasNext Iterator @brief Check if the iterator is valid and a @a Iterator.next operation would still leave it valid. @return true if there is an item past to the current one. */ FALCON_FUNC Iterator_hasNext( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Iterator* iter = dyncast( self->getFalconData() ); vm->regA().setBoolean( iter->hasNext() ); } /*# @method hasPrev Iterator @brief Check if the iterator is valid and a @a Iterator.prev operation would still leave it valid. @return true if there is an item before to the current one. */ FALCON_FUNC Iterator_hasPrev( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Iterator* iter = dyncast( self->getFalconData() ); vm->regA().setBoolean( iter->hasPrev() ); } /*# @method next Iterator @brief Advance the iterator. @return true if the iterator is still valid after next() has been completed. Moves the iterator to the next item in the collection. If the iterator is not valid anymore, or if the current element was the last in the collection, the method returns false. If the iterator has successfully moved, it returns true. */ FALCON_FUNC Iterator_next( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Iterator* iter = dyncast( self->getFalconData() ); if( ! iter->isValid() ) throw new ParamError( ErrorParam( e_invalid_iter ) .origin( e_orig_runtime ) ); vm->regA().setBoolean( iter->next() ); } /*# @method prev Iterator @brief Move the iterator back. @return true if the iterator is still valid after prev() has been completed. Moves the iterator to the previous item in the collection. If the iterator is not valid anymore, or if the current element was the first in the collection, the method returns false. If the iterator has successfully moved, it returns true. */ FALCON_FUNC Iterator_prev( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Iterator* iter = dyncast( self->getFalconData() ); if( ! iter->isValid() ) throw new ParamError( ErrorParam( e_invalid_iter ) .origin( e_orig_runtime ) ); vm->regA().setBoolean( iter->prev() ); } /*# @method value Iterator @brief Retreives the current item in the collection. @optparam subst New value for the current item. @return The current item. @raise AccessError if the iterator is not valid. If the iterator is valid, the method returns the value of the item being currently pointed by the iterator. If a parameter @b subst is given, the current value in the sequence is changed to @b substs. */ FALCON_FUNC Iterator_value( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Iterator* iter = dyncast( self->getFalconData() ); if( ! iter->isValid() ) throw new ParamError( ErrorParam( e_invalid_iter ) .origin( e_orig_runtime ) ); Item* i_subst = vm->param(0); if( i_subst != 0 ) iter->getCurrent() = *i_subst; else vm->regA() = iter->getCurrent(); } /*# @method key Iterator @brief Retreives the current key in the collection. @return The current key. @raise AccessError if the iterator is not valid, or if the collection has not keys. If this iterator is valid and is pointing to a collection that provides key ordering (i.e. a dictionary), it returns the current key; otherwise, it raises an AccessError. */ FALCON_FUNC Iterator_key( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Iterator* iter = dyncast( self->getFalconData() ); if( ! iter->isValid() ) throw new ParamError( ErrorParam( e_invalid_iter ) .origin( e_orig_runtime ) ); vm->regA() = iter->getCurrentKey(); } /*# @method equal Iterator @param item The item to which this iterator must be compared. @brief Check if this iterator is equal to the provided item. @return True if the item matches this iterator. This method overrides the FBOM equal method, and overloads the equality check of the VM. */ FALCON_FUNC Iterator_compare( ::Falcon::VMachine *vm ) { Item *i_other = vm->param(0); CoreObject *self = vm->self().asObject(); Iterator* iter = dyncast( self->getFalconData() ); if( i_other == 0 || ! i_other->isOfClass( "Iterator" ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).extra( "Iterator" ).origin( e_orig_runtime ) ); } Iterator* other = static_cast( i_other->asObjectSafe()->getUserData() ); if( ! iter->isValid() || ! other->isValid() ) throw new ParamError( ErrorParam( e_invalid_iter ) .origin( e_orig_runtime ) ); if( other->equal(*iter) ) vm->retval( (int64) 0 ); // declare them equal else // let the standard algorithm to decide. vm->retnil(); } /*# @method clone Iterator @brief Returns an instance of this iterator pointing to the same item. @return A new copy of this iterator. Creates an iterator equivalent to this one. In this way, it is possible to record a previous position and use it later. Using a normal assignment wouldn't work, as the assignand would just be given the same iterator, and its value would change accordingly with the other image of the iterator. */ FALCON_FUNC Iterator_clone( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Iterator *iter = dyncast( self->getFalconData() ); if( ! iter->isValid() ) throw new ParamError( ErrorParam( e_invalid_iter ) .origin( e_orig_runtime ) ); Item* i_itercls = vm->findWKI( "Iterator" ); fassert( i_itercls != 0 && i_itercls->isClass() ); CoreClass* Itercls = i_itercls->asClass(); CoreObject* io = Itercls->createInstance(); io->setUserData( iter->clone() ); vm->retval( io ); } /*# @method erase Iterator @brief Erase current item in the underlying sequence. @raise AccessError if the iterator is invalid. If the iterator is valid, this method removes current item. The iterator is moved to the very next item in the collection, and this may invalidate it if the removed element was the last one. To remove element while performing a scanning from the last element to the first one, remember to call the prev() method after every remove(); in forward scans, a successful remove() implies that the caller must not call next() to continue the scan. */ FALCON_FUNC Iterator_erase( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Iterator *iter = dyncast( self->getFalconData() ); if( ! iter->isValid() ) throw new ParamError( ErrorParam( e_invalid_iter ) .origin( e_orig_runtime ) ); iter->erase(); } /*# @method find Iterator @param key the key to be searched. @brief Moves this iterator on the searched item. @return true if the key was found, false otheriwse. @raise AccessError if the iterator is invalid or if the sequence doesn't provide keys. This method searches for an key in the underlying sequence, provided it offers search keys support. This is the case of the various dictionaries. This search is optimizied so that the subtree below the current position of the iterator is searched first. If the iterator is pointing to an item that matches the required key, this method returns immediately. After a succesful search, the iterator is moved to the position of the searched item. After a failed search, the iterator is moved to the smallest item in the sequence greater than the desired key; it's the best position for an insertion of the searched key. For example, to traverse all the items in a dictionary starting with 'C', the following code can be used: @code dict = [ "Alpha" => 1, "Beta" => 2, "Charlie" => 3, "Columbus" => 4, "Delta" => 5 ] iter = Iterator( dict ) iter.find( "C" ) // we don't care if it succeeds while iter.hasCurrent() and iter.key()[0] == "C" > iter.key(), " => ", iter.value() iter.next() end @endcode Also, a failed search gives anyhow a useful hint position for a subsequent insertion, which may avoid performing the search again. */ FALCON_FUNC Iterator_find( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Iterator *iter = dyncast( self->getFalconData() ); Item* i_key = vm->param(0); if( i_key == 0 ) { throw new ParamError( ErrorParam( e_inv_params ) .origin( e_orig_runtime ) .extra( "X" ) ); } if( ! iter->isValid() ) throw new ParamError( ErrorParam( e_invalid_iter ) .origin( e_orig_runtime ) ); if( iter->sequence()->isDictionary() ) { ItemDict* dict = static_cast(iter->sequence()); vm->regA().setBoolean( dict->findIterator( *i_key, *iter ) ); } else { throw new ParamError( ErrorParam( e_non_dict_seq ) .origin( e_orig_runtime ) ); } } /*# @method insert Iterator @param key Item to be inserted (or key, if the underlying sequence is keyed). @optparam value A value associated with the key. @brief Insert an item, or a pair of key values, in an underlying sequence. @raise AccessError if the iterator is invalid. Inserts an item at current position. In case the underlying sequence is an ordered sequence of key-value pairs, a correct position for insertion is first searched, and then the iterator is moved to the position of the inserted key. In this second case, if the iterator already points to a valid position for insertion of the given key, the search step is skipped. @see Iterator.find */ FALCON_FUNC Iterator_insert( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Iterator *iter = dyncast( self->getFalconData() ); if( ! iter->isValid() ) throw new ParamError( ErrorParam( e_invalid_iter ) .origin( e_orig_runtime ) ); if( iter->sequence()->isDictionary() ) { Item *i_key = vm->param(0); Item *i_value = vm->param(1); if( i_key == 0 || i_value == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "X,X" ) ); } static_cast( iter->sequence() )->put( *i_key, *i_value ); } else { Item *i_key = vm->param(0); if( vm->param(1) != 0 ) { throw new ParamError( ErrorParam( e_non_dict_seq ) .origin( e_orig_runtime ) ); } if( i_key == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "X" ) ); } iter->insert( *i_key ); } } } } /* end of iterator_ext.cpp */ engine/core_module/list_ext.cpp000066400000000000000000000107111176363201700171650ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: list_ext.cpp Implementation of the RTL List Falcon class. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2007-12-01 ------------------------------------------------------------------- (C) Copyright 2007: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Implementation of the RTL List Falcon class. */ /*# @beginmodule core */ #include #include #include #include #include #include "core_module.h" #include namespace Falcon { namespace core { /*# @class List @from Sequence ... @brief Fast growable double linked list. @param ... An arbitrary list of parameters. The list class implements a double linked list of generic Falcon items that has some hooking with the falcon VM. Particularly, instances of the List class can be used as parameters for the Iterator constructor, or an iterator can be generated for them using first() and last() BOM methods. Also, instances of the List class can be used as any other sequence in for/in loops. For example, the following code: @code descr = List("blue", "red", "gray", "purple") for color in descr forfirst >> "Grues are ", color continue end formiddle: >> ", ", color forlast: > " and ", color, "." end @endcode prints: @code Grues are blue, red, gray and purple. @endcode */ FALCON_FUNC List_init ( ::Falcon::VMachine *vm ) { ItemList *list = new ItemList; int32 pc = vm->paramCount(); for( int32 p = 0; p < pc; p++ ) { list->push_back( *vm->param(p) ); } list->owner( vm->self().asObject() ); vm->self().asObject()->setUserData( list ); } /*# @method push List @brief Appends given item to the end of the list. @param item The item to be pushed. */ FALCON_FUNC List_push ( ::Falcon::VMachine *vm ) { Item *data = vm->param( 0 ); if( data == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra("X") ); return; } ItemList *list = static_cast( vm->self().asObject()->getUserData() ); list->push_back( *data ); } /*# @method pop List @brief Removes the last item from the list (and returns it). @raise AccessError if the list is empty. @return The last item in the list. Removes the last item from the list (and returns it). If the list is empty, an access exception is raised. */ FALCON_FUNC List_pop ( ::Falcon::VMachine *vm ) { ItemList *list = static_cast( vm->self().asObject()->getUserData() ); if( list->size() == 0 ) //empty() is virtual { throw new AccessError( ErrorParam( e_arracc, __LINE__ ). origin( e_orig_runtime ) ); return; } vm->retval( list->back() ); list->pop_back(); } /*# @method pushFront List @brief Pushes an item in front of the list. @param item The item to be pushed. */ FALCON_FUNC List_pushFront ( ::Falcon::VMachine *vm ) { Item *data = vm->param( 0 ); if( data == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra("X") ); return; } ItemList *list = static_cast( vm->self().asObject()->getUserData() ); list->push_front( *data ); } /*# @method popFront List @brief Removes the first item from the list (and returns it). @raise AccessError if the list is empty. @return The first item in the list. Removes the first item from the list (and returns it). If the list is empty, an access exception is raised. */ FALCON_FUNC List_popFront ( ::Falcon::VMachine *vm ) { ItemList *list = static_cast( vm->self().asObject()->getUserData() ); if( list->size() == 0 ) //empty() is virtual { throw new AccessError( ErrorParam( e_arracc, __LINE__ ). origin( e_orig_runtime ) ); return; } vm->retval( list->front() ); list->pop_front(); } /*# @method len List @brief Returns the number of items stored in the Sequence. @return Count of items in the Sequence. */ FALCON_FUNC List_len( ::Falcon::VMachine *vm ) { ItemList *list = dyncast( vm->self().asObject()->getSequence() ); vm->retval( (int64) list->size() ); } } } /* end of list_ext.cpp */ engine/core_module/math_ext.cpp000066400000000000000000000533671176363201700171610ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: math.cpp Mathematical basic function for basic rtl. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom apr 16 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Mathematical basic function for basic rtl. */ /*# @beginmodule core */ #include #include #include #include /*# @funset core_math Math functions. @brief Functions providing math support to Falcon. This group includes mathematical, trigonometrical and floating point conversion functions. @beginset core_math */ namespace Falcon { namespace core { /*# @function log @brief Returns the natural logarithm of the argument. @param x Argument. @return The natural logarithm of the argument. @raise MathError If the argument is out of domain. The function may raise an error if the value cannot be computed because of domain or overflow errors. */ FALCON_FUNC flc_math_log( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); if ( num1 == 0 || ! num1->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); } errno = 0; numeric res = log( num1->forceNumeric() ); if ( errno != 0 ) { throw new MathError( ErrorParam( e_domain, __LINE__).origin( e_orig_runtime ) ); } else { vm->retval( res ); } } /*# @function log10 @brief Returns the common (base 10) logarithm of the argument. @param x Argument. @return The common logarithm of the argument. @raise MathError If the argument is out of domain. The function may raise an error if the value cannot be computed because of domain or overflow errors. */ FALCON_FUNC flc_math_log10( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); if ( num1 == 0 || ! num1->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); } errno = 0; numeric res = log10( num1->forceNumeric() ); if ( errno != 0 ) { throw new MathError( ErrorParam( e_domain, __LINE__).origin( e_orig_runtime ) ); } else { vm->retval( res ); } } /*# @function exp @brief Returns exponential (e^x) of the argument. @param x Argument. @return The exponential of the argument. @raise MathError If the argument is out of domain. The function may raise an error if the value cannot be computed because of domain or overflow errors. */ FALCON_FUNC flc_math_exp( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); if ( num1 == 0 || ! num1->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); return; } errno = 0; numeric res = exp( num1->forceNumeric() ); if ( errno != 0 ) { throw new MathError( ErrorParam( e_domain, __LINE__).origin( e_orig_runtime ) ); } else { vm->retval( res ); } } /*# @function sqrt @brief Returns the square root of the argument. @param x Argument. @return The square root of the argument. @raise MathError If the argument is out of domain. The function may raise an error if the value cannot be computed because of domain or overflow errors. */ FALCON_FUNC flc_math_sqrt( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); if ( num1 == 0 || ! num1->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); return; } errno = 0; numeric res = sqrt( num1->forceNumeric() ); if ( errno != 0 ) { throw new MathError( ErrorParam( e_domain, __LINE__).origin( e_orig_runtime ) ); } else { vm->retval( res ); } } /*# @function mod @brief Returns the modulo of two arguments. @param x Argument. @param y Argument. @return The modulo of the two argument; x mod y. @raise MathError If the argument is out of domain. The function may raise an error if the value cannot be computed because of domain or overflow errors. */ FALCON_FUNC flc_math_mod( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); Item *num2 = vm->param( 1 ); if ( num2 == 0 || ! num1->isOrdinal() || ! num2->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N,N") ); return; } errno = 0; numeric res = fmod( num1->forceNumeric(), num2->forceNumeric() ); if ( errno != 0 ) { throw new MathError( ErrorParam( e_domain, __LINE__).origin( e_orig_runtime ) ); } else { vm->retval( res ); } } /*# @function pow @brief Returns the first argument elevated to the second one (x^y) @param x Base. @param y Exponent. @return x^y @raise MathError If the argument is out of domain. The function may raise an error if the value cannot be computed because of domain or overflow errors. */ FALCON_FUNC flc_math_pow( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); Item *num2 = vm->param( 1 ); if ( num1 == 0 || ! num1->isOrdinal() || num2 == 0 || ! num2->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N,N") ); return; } errno = 0; numeric res = pow( num1->forceNumeric(), num2->forceNumeric() ); if ( errno != 0 ) { throw new MathError( ErrorParam( e_domain, __LINE__).origin( e_orig_runtime ) ); } else { vm->retval( res ); } } /*# @function sin @brief Returns the sine of the argument. @param x Argument. @return The sine of the argument. @raise MathError If the argument is out of domain. The return value is expressed in radians. The function may raise an error if the value cannot be computed because of domain or overflow errors. */ FALCON_FUNC flc_math_sin( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); if ( num1 == 0 || ! num1->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); } errno = 0; numeric res = sin( num1->forceNumeric() ); if ( errno != 0 ) { throw new MathError( ErrorParam( e_domain, __LINE__).origin( e_orig_runtime ) ); } else { vm->retval( res ); } } /*# @function cos @brief Returns the cosine of the argument. @param x Argument. @return The cosine of the argument. @raise MathError If the argument is out of domain. The return value is expressed in radians. The function may raise an error if the value cannot be computed because of domain or overflow errors. */ FALCON_FUNC flc_math_cos( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); if ( num1 == 0 || ! num1->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra( "N" ) ); } errno = 0; numeric res = cos( num1->forceNumeric() ); if ( errno != 0 ) { throw new MathError( ErrorParam( e_domain, __LINE__).origin( e_orig_runtime ) ); } else { vm->retval( res ); } } /*# @function tan @brief Returns the tangent of the argument. @param x Argument. @return The tangent of the argument. @raise MathError If the argument is out of domain. The return value is expressed in radians. The function may raise an error if the value cannot be computed because of domain or overflow errors. */ FALCON_FUNC flc_math_tan( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); if ( num1 == 0 || ! num1->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); } errno = 0; numeric res = tan( num1->forceNumeric() ); if ( errno != 0 ) { throw new MathError( ErrorParam( e_domain, __LINE__).origin( e_orig_runtime ) ); } else { vm->retval( res ); } } /*# @function asin @brief Returns the arc sine of the argument. @param x Argument. @return The arc sine of the argument. @raise MathError If the argument is out of domain. The return value is expressed in radians. The function may raise an error if the value cannot be computed because of domain or overflow errors. */ FALCON_FUNC flc_math_asin( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); if ( num1 == 0 || ! num1->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); } errno = 0; numeric res = asin( num1->forceNumeric() ); if ( errno != 0 ) { throw new MathError( ErrorParam( e_domain, __LINE__).origin( e_orig_runtime ) ); } else { vm->retval( res ); } } /*# @function acos @brief Returns the arc cosine of the argument. @param x Argument. @return The arc cosine of the argument. @raise MathError If the argument is out of domain. This function computes the principal value of the arc cosine of its argument x. The value of x should be in the range [-1,1]. The return value is expressed in radians. The function may raise a Math error if the value cannot be computed because of domain or overflow errors. */ FALCON_FUNC flc_math_acos( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); if ( num1 == 0 || ! num1->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); return; } errno = 0; numeric res = acos( num1->forceNumeric() ); if ( errno != 0 ) { throw new MathError( ErrorParam( e_domain, __LINE__).origin( e_orig_runtime ) ); } else { vm->retval( res ); } } /*# @function atan @brief Returns the arc tangent of the argument. @param x Argument. @return The arc tangent of the argument. @raise MathError If the argument is out of domain. This function computes the principal value of the arc tangent of its argument x. The value of x should be in the range [-1,1]. The return value is expressed in radians. The function may raise a Math error if the value cannot be computed because of domain or overflow errors. */ FALCON_FUNC flc_math_atan( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); if ( num1 == 0 || ! num1->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); return; } errno = 0; numeric res = atan( num1->forceNumeric() ); if ( errno != 0 ) { throw new MathError( ErrorParam( e_domain, __LINE__).origin( e_orig_runtime ) ); } else { vm->retval( res ); } } /*# @function atan2 @brief Returns the arc tangent of x / y. @param x First argument. @param y Second argument. @return The arc tangent of the x / y. @raise MathError If the argument is out of domain. This function computes the principal value of the arc tangent of x/y, using the signs of both arguments to determine the quadrant of the return value. The return value is expressed in radians. The function may raise a Math error if the value cannot be computed because of domain or overflow errors. */ FALCON_FUNC flc_math_atan2( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); Item *num2 = vm->param( 1 ); if ( num1 == 0 || ! num1->isOrdinal() || num2 == 0 || ! num2->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); return; } errno = 0; numeric res = atan2( num1->forceNumeric(), num2->forceNumeric() ); if ( errno != 0 ) { throw new MathError( ErrorParam( e_domain, __LINE__).origin( e_orig_runtime ) ); } else { vm->retval( res ); } } #define PI 3.1415926535897932384626433832795 #define E 2.7182818284590452353602874713527 /*# @function rad2deg @brief Converts an angle expressed in radians into degrees. @param x An angle expressed in radians. @return The angle converted in degrees. */ FALCON_FUNC flc_math_rad2deg( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); if ( num1 == 0 || ! num1->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); return; } vm->retval( 180.0 / ( PI * num1->forceNumeric() ) ); } /*# @function deg2rad @brief Converts an angle expressed in degrees into radians. @param x An angle expressed in degrees. @return The angle converted in radians. */ FALCON_FUNC flc_math_deg2rad( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); if ( num1 == 0 || ! num1->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); return; } vm->retval( num1->forceNumeric() * PI / 180.0 ); } /*# @function fract @brief Returns the fractional part of a number. @param x Argument. @return The fractional part of a number. This function returns the non-integer part of a number. For example, @code > fract( 1.234 ) @endcode would print 0.234. */ FALCON_FUNC flc_fract ( ::Falcon::VMachine *vm ) { Item *num = vm->param( 0 ); if ( num->type() == FLC_ITEM_INT ) { vm->retval( (int64) 0 ); } else if ( num->type() == FLC_ITEM_NUM ) { numeric intpart; vm->retval( modf( num->asNumeric(), &intpart ) ); } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); } } /*# @function fint @brief Returns the integer part of a floating point number as a floating point number. @param x Argument. @return A floating point number with fractional part zeroed. Fint function works like the core @a int function, but it returns a floating point number. For example, @b fint applied on 3.58e200 will return the same number, while @a int would raise a math error, as the number cannot be represented in a integer number that can store numbers up to +-2^63. */ FALCON_FUNC flc_fint( ::Falcon::VMachine *vm ) { Item *num = vm->param( 0 ); if ( num->type() == FLC_ITEM_INT ) { vm->retval( *num ); } else if ( num->type() == FLC_ITEM_NUM ) { numeric n = num->asNumeric(); numeric intpart; modf(n, &intpart ); vm->retval( intpart ); } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); } } /*# @function round @brief Rounds a floating point to the nearest integer. @param x Argument. @return Nearest integer to x. Round returns the nearest integer value of a given floating point number. If the fractional part of the number is greater or equal to 0.5, the number is rounded up to the nearest biggest integer in absolute value, while if it's less than 0.5 the number is rounded down to the mere integer part. For example, 1.6 is rounded to 2, -1.6 is rounded to -2, 1.2 is rounded to 1 and -1.2 is rounded to -1. */ FALCON_FUNC flc_round ( ::Falcon::VMachine *vm ) { Item *num = vm->param( 0 ); if ( num->type() == FLC_ITEM_INT ) { vm->retval( *num ); } else if ( num->type() == FLC_ITEM_NUM ) { // Or windows or solaris, use a simple round trick. #if defined(_MSC_VER) || ( defined (__SVR4) && defined (__sun) ) numeric n = num->asNumeric(); numeric intpart; numeric fractpart = modf(n, &intpart ); if ( fractpart >= 0.5 ) vm->retval( intpart + 1 ); else if ( fractpart <= -0.5 ) vm->retval( intpart - 1 ); else vm->retval( intpart ); #else vm->retval( llround( num->asNumeric() ) ); #endif } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); } } /*# @function floor @brief Returns the smallest integer near to the given value. @param x Argument. @return The smallest integer near to the given value. Floor function returns the smallest integer near to a given floating point number. For example, floor of 1.9 is 1, and floor of -1.9 is -2. If an integer number is given, then the function returns the same number. This is similar to fint(), but in case of negative numbers @a fint would return the integer part; in case of -1.9 it would return -1. */ FALCON_FUNC flc_floor ( ::Falcon::VMachine *vm ) { Item *num = vm->param( 0 ); if ( num->type() == FLC_ITEM_INT ) { vm->retval( *num ); } else if ( num->type() == FLC_ITEM_NUM ) { vm->retval( (int64) floor( num->asNumeric() ) ); } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); } } /*# @function ceil @brief Returns the greatest integer near to the given value. @param x Argument. @return The ceil value. Ceil function returns the highest integer near to a given floating point number. For example, ceil of 1.1 is 2, and ceil of -1.1 is -1. If an integer number is given, then the function returns the same number. */ FALCON_FUNC flc_ceil ( ::Falcon::VMachine *vm ) { Item *num = vm->param( 0 ); if ( num->type() == FLC_ITEM_INT ) { vm->retval( *num ); } else if ( num->type() == FLC_ITEM_NUM ) { vm->retval( (int64) ceil( num->asNumeric() ) ); } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); } } /*# @function abs @brief Returns the absolute value of a number. @param x A number. @return The absolute value of the parameter. If the argument is an integer, then an integer is returned, otherwise the return value will be a floating point number. */ FALCON_FUNC flc_abs ( ::Falcon::VMachine *vm ) { Item *num = vm->param( 0 ); if ( num->type() == FLC_ITEM_INT ) { int64 n = num->asInteger(); vm->retval( n < 0 ? -n : n ); } else if ( num->type() == FLC_ITEM_NUM ) { numeric n = num->asNumeric(); vm->retval( fabs( n ) ); } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); } } static numeric fact( numeric n ) { numeric res = 1.0; while( n > 0 ) { res *= n; n = n-1.0; } return res; } /*# @function factorial @brief Returns the factorial of the argument. @param x Argument. @return The factorial of the argument. The return value is expressed as a floating point value. @note For high values of @b x, the function may require exponential computational time and power. */ FALCON_FUNC flc_math_factorial( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); if ( num1 == 0 || ! num1->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); } numeric num = num1->forceNumeric(); if ( num < 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ).origin( e_orig_runtime ) ); } errno = 0; numeric res = fact( num ); if ( errno != 0 ) { throw new MathError( ErrorParam( e_domain, __LINE__).origin( e_orig_runtime ) ); } else { vm->retval( res ); } } /*# @function permutations @brief Returns the permutation of the arguments. @param x First argument. @param y Second arguments. @return The permutation of the arguments. The return value is expressed as a floating point value. @note For high values of @b x, the function may require exponential computational time and power. */ FALCON_FUNC flc_math_permutations( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); Item *num2 = vm->param( 1 ); if ( num1 == 0 || ! num1->isOrdinal() || num2 == 0 || ! num2->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N,N") ); return; } numeric n = num1->forceNumeric(); numeric r = num2->forceNumeric(); // n must be > 0, but r may be zero. if ( n <= 0 || r < 0) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ).origin( e_orig_runtime ) ); } errno = 0; // check to make sure numbers aren't the same double res = 1.0; double from = r == 0 ? 1 : n - r + 1; while ( from <= n && errno == 0 ) { res *= from; from += 1.0; } if ( errno != 0 ) { throw new MathError( ErrorParam( e_domain, __LINE__).origin( e_orig_runtime ) ); } else { vm->retval( res ); } } /*# @function combinations @brief Returns the combination of the arguments. @param x First argument. @param y Second arguments. @return The combination of the arguments. The return value is expressed as a floating point value. @note For high values of @b x, the function may require exponential computational time and power. */ FALCON_FUNC flc_math_combinations( ::Falcon::VMachine *vm ) { Item *num1 = vm->param( 0 ); Item *num2 = vm->param( 1 ); if ( num1 == 0 || ! num1->isOrdinal() || num2 == 0 || ! num2->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N,N") ); } numeric n = num1->forceNumeric(); numeric r = num2->forceNumeric(); // check to make sure numbers aren't the same if ( n <= 0 || r < 0) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ).origin( e_orig_runtime ) ); } if ( n == r ) { vm->retval( n ); } else { errno = 0; numeric res = fact( n ) / (fact( r ) * fact(n-r)); if ( errno != 0 ) { throw new MathError( ErrorParam( e_domain, __LINE__).origin( e_orig_runtime ) ); } else { vm->retval( res ); } } } } } /* end of math.cpp */ engine/core_module/membuf_ext.cpp000066400000000000000000000344261176363201700174760ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: membuf_ext.cpp Memory buffer functions. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 14 Aug 2008 00:17:31 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "core_module.h" /*# @beginmodule core */ namespace Falcon { namespace core { FALCON_FUNC Make_MemBuf( ::Falcon::VMachine *vm ) { Item *i_size = vm->param(0); Item *i_wordSize = vm->param(1); if( ( i_size == 0 || ! i_size->isOrdinal() ) || ( i_wordSize != 0 && ! i_wordSize->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ).extra( "N,[N]" ) ); } int64 wordSize = i_wordSize == 0 ? 1: i_wordSize->forceInteger(); int64 size = i_size->forceInteger(); if ( wordSize < 1 || wordSize > 4 || size <= 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) ); } MemBuf *mb = 0; switch( wordSize ) { case 1: mb = new MemBuf_1( (uint32) size ); break; case 2: mb = new MemBuf_2( (uint32) size ); break; case 3: mb = new MemBuf_3( (uint32) size ); break; case 4: mb = new MemBuf_4( (uint32) size ); break; } fassert( mb != 0 ); vm->retval( mb ); } /*# @function MemBufFromPtr @ingroup memory_manipulation @brief Creates a memory buffer to store raw memory coming from external providers. @param data A pointer to the raw memory (as an integer value). @param size The maximum size of the memory. @optparam wordSize Size of each word in bytes (1, 2, 3 or 4). Intentionally left undocumented. Don't use if you don't know what you're doing. */ FALCON_FUNC Make_MemBufFromPtr( ::Falcon::VMachine *vm ) { Item *i_ptr = vm->param(0); Item *i_size = vm->param(1); Item *i_wordSize = vm->param(2); if( ( i_ptr == 0 || ! i_ptr->isInteger() ) || ( i_size == 0 || ! i_size->isOrdinal() ) || ( i_wordSize != 0 && ! i_wordSize->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ).extra( "N,[N]" ) ); } int64 wordSize = i_wordSize == 0 ? 1: i_wordSize->forceInteger(); int64 size = i_size->forceInteger(); if ( wordSize < 1 || wordSize > 4 || size <= 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) ); } MemBuf *mb = 0; byte *data = (byte*) i_ptr->asInteger(); switch( wordSize ) { case 1: mb = new MemBuf_1( data, (uint32) size, 0 ); break; case 2: mb = new MemBuf_2( data, (uint32) size, 0 ); break; case 3: mb = new MemBuf_3( data, (uint32) size, 0 ); break; case 4: mb = new MemBuf_4( data, (uint32) size, 0 ); break; } fassert( mb != 0 ); vm->retval( mb ); } /*# @class MemoryBuffer @from BOM @ingroup bom_classes @brief Metaclass for MemBuf items. The Memory Buffers have a set of internal pointers and sequence methods useful to parse binary streams read in variable size chunks. Initially, allocate a memory buffer wide enough to store enough data. The maximum possible amount of data units (generally bytes) that can be stored in a memory buffer is its @b length, returned by the BOM @b len method or @a len function. After having read some contents from a stream, the buffer @b limit will be moved to the amount of incoming data (which may be also the same as the @b length if the buffer is completely filled). The application may get one item at a time via the @a MemoryBuffer.get() method, or process blocks of data transferring them to other membufs or arrays via ranged operators. Each get() moves a @b position indicator forward up to the @b limit. At the same time, it is possible to put data in the buffer, moving forward the @b position pointer up to the limit. The buffer has a @b marker, that can be set at current @b position, and that can be later used to to return to a previously marked position. The following invariants always hold: @code 0 <= [mark] <= position <= limit <= length @endcode The limit is usually set to the buffer length, unless it is explicitly set to a lower position via explicit calls, or the last read didn't bear enough data to fill the buffer. The following operations are meant to simplify read and partial parsing of binary data: - @a MemoryBuffer.reset: return the position at last mark (raises if a mark is not defined). - @a MemoryBuffer.rewind: discards the mark and returns the position to 0. - @a MemoryBuffer.clear: rewinds and set limit to the buffer length. - @a MemoryBuffer.flip: sets the limit to the current position, the position to zero and discards the mark. - @a MemoryBuffer.compact: removes already parsed data and prepares for an incremental read. All the members in this group not explicitly returning data or sizes return the MemPool itself, so that it is possible to concatenate calls like this: @code mb.clear() mb.position(3) // equivalent: mb.clear().position(3) @endcode */ /*# @method first MemoryBuffer @brief Returns an iterator to the first element of this buffer. @return An iterator. */ FALCON_FUNC MemoryBuffer_first( VMachine *vm ) { Item *itclass = vm->findWKI( "Iterator" ); fassert( itclass != 0 ); CoreObject *iterator = itclass->asClass()->createInstance(); iterator->setProperty( "_pos", Item((int64) 0) ); iterator->setProperty( "_origin", vm->self() ); vm->retval( iterator ); } /*# @method last MemoryBuffer @brief Returns an iterator to the last element of this buffer. @return An iterator. */ FALCON_FUNC MemoryBuffer_last( VMachine *vm ) { Item *itclass = vm->findWKI( "Iterator" ); fassert( itclass != 0 ); CoreObject *iterator = itclass->asClass()->createInstance(); MemBuf* orig = vm->self().asMemBuf(); iterator->setProperty( "_pos", Item(orig->length() == 0 ? 0 : (int64) orig->length() - 1) ); iterator->setProperty( "_origin", vm->self() ); vm->retval( iterator ); } /*# @method front MemoryBuffer @brief Returns the first element in this memory buffer. @return A number representing the first element in this buffer. @raise AccessError if this buffer is empty. */ FALCON_FUNC MemoryBuffer_front( VMachine *vm ) { MemBuf* self = vm->self().asMemBuf(); if ( self->length() == 0 ) { throw new AccessError( ErrorParam( e_arracc, __LINE__ ).origin( e_orig_runtime ) ); } else { vm->retval( (int64) self->get(0) ); } } /*# @method back MemoryBuffer @brief Returns the last element in this memory buffer. @return A number representing the last element in this buffer. @raise AccessError if this buffer is empty. */ FALCON_FUNC MemoryBuffer_back( VMachine *vm ) { MemBuf* self = vm->self().asMemBuf(); if ( self->length() == 0 ) { throw new AccessError( ErrorParam( e_arracc, __LINE__ ).origin( e_orig_runtime ) ); } else { vm->retval( (int64) self->get(self->length() - 1) ); } } /*# @method put MemoryBuffer @brief Puts a value in the memory buffer. @return The buffer itself. @raise AccessError if the buffer is full (limit() == position()) Sets the vaulue at current position and advances it. */ FALCON_FUNC MemoryBuffer_put( ::Falcon::VMachine *vm ) { Item *i_data = vm->param(0); if ( i_data == 0 || ! i_data->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra( "N" ) ); return; } MemBuf* self = vm->self().asMemBuf(); self->put( (uint32) i_data->forceInteger() ); vm->retval( self ); } /*# @method get MemoryBuffer @brief Gets a value in the memory buffer. @return the retreived value. @raise AccessError if the buffer is full (limit() == position()) Gets the vaulue at current position and advances it. */ FALCON_FUNC MemoryBuffer_get( ::Falcon::VMachine *vm ) { MemBuf* self = vm->self().asMemBuf(); vm->regA().setInteger( self->get() ); } /*# @method rewind MemoryBuffer @brief Rewinds the buffer and discards the mark. @return The buffer itself Returns the position at 0 and discards the mark. Limit is unchanged. */ FALCON_FUNC MemoryBuffer_rewind( ::Falcon::VMachine *vm ) { MemBuf* self = vm->self().asMemBuf(); self->rewind(); vm->retval( self ); } /*# @method reset MemoryBuffer @brief Returns the position to the last mark. @return The buffer itself @raise AccessError if the mark is not defined. Returns the position at the value previously marked with @a MemoryBuffer.mark. The mark itself is not discarded. */ FALCON_FUNC MemoryBuffer_reset( ::Falcon::VMachine *vm ) { MemBuf* self = vm->self().asMemBuf(); self->reset(); vm->retval( self ); } /*# @method remaining MemoryBuffer @brief Determines how many items can be read. @return Count of remanining items. Returns the count of times get and put can be called on this memory buffer before raising an error. */ FALCON_FUNC MemoryBuffer_remaining( ::Falcon::VMachine *vm ) { MemBuf* self = vm->self().asMemBuf(); vm->retval( (int64) self->remaining() ); } /*# @method compact MemoryBuffer @brief Discards processed data and prepares to a new read. @return The buffer itself This operation moves all the bytes between the position (included) and the limit (escluded) to the beginning of the buffer. Then, the position is moved to the previous value of the limit, and the limit is reset to the length of the buffer. As read is performed by filling the data between the current position and the limit, this operation allows to trying get more data when a previously imcomplete input was taken. @note Just, remember to move the position back to the first item that was still to be decoded before compact */ FALCON_FUNC MemoryBuffer_compact( ::Falcon::VMachine *vm ) { MemBuf* self = vm->self().asMemBuf(); self->compact(); vm->retval( self ); } /*# @method flip MemoryBuffer @brief Sets the limit to the current position, and the position to zero. @return The buffer itself This is useful to write some parsed or created contents back on a stream. Once filled the buffer with data, the position is on the last element; then, using flip and writing the buffer, the prepared data is sent to the output. */ FALCON_FUNC MemoryBuffer_flip( ::Falcon::VMachine *vm ) { MemBuf* self = vm->self().asMemBuf(); self->flip(); vm->retval( self ); } /*# @method mark MemoryBuffer @brief Places the mark at current position. @return The buffer itself This position sets the mark at current position; calling @a MemoryBuffer.reset later on will move the position back to this point. */ FALCON_FUNC MemoryBuffer_mark( ::Falcon::VMachine *vm ) { MemBuf* self = vm->self().asMemBuf(); self->placeMark(); vm->retval( self ); } /*# @method position MemoryBuffer @brief Sets or get the current position in the buffer. @optparam pos The new position @return The buffer itself or the requested position. @raise AccessError if the @b pos parameter is > limit. This method can be used to get the current postition in the memory buffer, or to set a new position. In the first case, the current value of the position is returned, in the second case the current buffer is returned. As the position can never be greater that the limit, an error is raised in case a value too high has been set. */ FALCON_FUNC MemoryBuffer_position( ::Falcon::VMachine *vm ) { MemBuf* self = vm->self().asMemBuf(); Item *i_pos = vm->param(0); if ( i_pos == 0 ) { vm->regA().setInteger( self->position() ); } else if ( ! i_pos->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).extra( "N" ) ); } else { self->position( (uint32) i_pos->forceInteger() ); vm->retval( self ); } } /*# @method clear MemoryBuffer @brief Clears the buffer resetting it to initial status. @return The buffer itself. Removes the mark, sets the position to zero and the limit to the length. This makes the buffer ready for a new set of operations. */ FALCON_FUNC MemoryBuffer_clear( ::Falcon::VMachine *vm ) { MemBuf* self = vm->self().asMemBuf(); self->clear(); vm->retval( self ); } /*# @method limit MemoryBuffer @brief Gets or sets current filled data size. @optparam pos The value to be applied to the memory buffer. @return current limit. This method can be used to get the current postition in the memory buffer, or to set a new position. In the first case, the current value of the position is returned, in the second case the current buffer is returned. */ FALCON_FUNC MemoryBuffer_limit( ::Falcon::VMachine *vm ) { MemBuf* self = vm->self().asMemBuf(); Item *i_limit = vm->param(0); if ( i_limit == 0 ) { vm->regA().setInteger( self->limit() ); } else if ( ! i_limit->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params ).origin( e_orig_runtime ).extra( "N" ) ); } else { self->limit( (uint32) i_limit->forceInteger() ); vm->retval( self ); } } /*# @method wordSize MemoryBuffer @brief Returns the number of bytes used to store each entry of this Memory Buffer. @return Size of each memory buffer entity in bytes. */ FALCON_FUNC MemoryBuffer_wordSize( ::Falcon::VMachine *vm ) { vm->retval( (int64) vm->self().asMemBuf()->wordSize() ); } /*# @method fill MemoryBuffer @brief Fills all the elements in the memory buffer with a given value. @param value The value to be applied to the memory buffer. @return This memory buffer. */ FALCON_FUNC MemoryBuffer_fill( ::Falcon::VMachine *vm ) { Item *i_item = vm->param(0); MemBuf* self = vm->self().asMemBuf(); if ( i_item == 0 || ! i_item->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "X" ) ); } uint32 value = (uint32) i_item->forceInteger(); for( uint32 i = 0; i < self->length(); i ++ ) self->set( i, value ); vm->retval( self ); } } } /* end of membuf_ext.cpp */ engine/core_module/message_ext.cpp000066400000000000000000000610311176363201700176370ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: message_ext.cpp Attribute support functions for scripts. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 14 Aug 2008 02:33:46 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "core_module.h" /*# @beginmodule core */ namespace Falcon { namespace core { static void check_assertion( VMachine *vm, CoreSlot *cs, const Item &itm ) { if ( cs->hasAssert() ) { vm->pushParameter( cs->assertion() ); if ( ! itm.isCallable() ) { if( itm.isComposed() ) { // may throw Item tmp; itm.asDeepItem()->readProperty( "on_" + cs->name(), tmp ); tmp.readyFrame( vm, 1 ); } else { throw new CodeError( ErrorParam( e_non_callable, __LINE__ ).extra( "broadcast" ) ); } } else itm.readyFrame( vm, 1 ); } } /*# @funset set_message_model Message oriented model functions @brief Functions supporting Message Oriented Programming (MOP) @see message_model This is the list of functions working together to implement the message oriented model. Other than this functions, it is possible to use the @a VMSlot class for direct access to reflected virtual machine slots. On this regards, see the @a message_model group. */ /*# @group message_model Message oriented model @brief Functions and classes supporting Message Oriented Programming (MOP) @begingroup message_model */ /*# @function broadcast @param msg A message (string) to be broadcast. @optparam ... Zero or more data to be broadcaset. @brief Sends a message to every callable item subscribed to a message. @return true if @b msg is found, false if it doesn't exist. @inset set_message_model @see VMSlot @see getSlot Broadcast function implicitly searches for a Virtual Machine Message Slot (@a VMSlot) with the given @b msg name, and if it finds it, it emits a broadcast on that. If the message is not found, the broadcast is silently dropped (no error is raised), but the function returns false. As calling this function requires a scan in the virtual machine message slot table, in case of repeated operations it is preferable to explicitly search for a slot with @a getSlot, or to create it as an @a VMSlot instance. On the other hand, if the reference to a VMSlot is not needed, this function allows to broadcast on the slot without adding the overhead required by the creation of the @a VMSlot wrapper. */ FALCON_FUNC broadcast( ::Falcon::VMachine *vm ) { Item *i_msg = vm->param( 0 ); if ( ! i_msg->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S,..." ) ); } CoreSlot* cs = vm->getSlot( *i_msg->asString(), false ); if ( cs ) { cs->prepareBroadcast( vm->currentContext(), 1, vm->paramCount() - 1 ); vm->regA().setBoolean(true); } else vm->regA().setBoolean(false); // otherwise, we're nothing to do here. } /*# @function subscribe @inset set_message_model @brief Registers a callback to a message slot. @param msg A string with the message name on which the item should be registered. @param handler A callable item or instance providing callback support. @optparam prio Set to true to insert this subscription in front of the subscription list. */ FALCON_FUNC subscribe( ::Falcon::VMachine *vm ) { Item *i_msg = vm->param( 0 ); Item *i_handler = vm->param( 1 ); Item *i_prio = vm->param(2); if ( i_msg == 0 || ! i_msg->isString() || i_handler == 0 || ! ( i_handler->isCallable() || i_handler->isComposed() ) ) { throw new ParamError( ErrorParam( e_inv_params ) .origin( e_orig_runtime ) .extra( "S,C" ) ); } String *sub = i_msg->asString(); CoreSlot* cs = vm->getSlot( *sub, true ); if ( i_prio != 0 && i_prio->isTrue() ) cs->push_front( *i_handler ); else cs->push_back( *i_handler ); check_assertion( vm, cs, *i_handler ); } /*# @function unsubscribe @inset set_message_model @brief Unregisters a registered callback from a slot. @param msg A string with the message name on which the item should be registered. @param handler A callable item or instance providing callback support. @raise CodeError if the @b handler is not registered with this slot. @raise AccessError if the named message slot doesn't exist. */ FALCON_FUNC unsubscribe( ::Falcon::VMachine *vm ) { Item *i_msg = vm->param( 0 ); Item *i_handler = vm->param( 1 ); if ( i_msg == 0 || ! i_msg->isString() || i_handler == 0 || ! ( i_handler->isCallable() || i_handler->isComposed() ) ) { throw new ParamError( ErrorParam( e_inv_params ). extra( "S,C" ) ); } CoreSlot* cs = vm->getSlot( *i_msg->asString(), false ); if ( cs == 0 ) { throw new AccessError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( *i_msg->asString() ) ); } if( ! cs->remove( *i_handler ) ) { throw new CodeError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ).extra( "unsubscribe" ) ); } if ( cs->empty() && ! cs->hasAssert() ) { vm->removeSlot( cs->name() ); } } /*# @function getSlot @inset set_message_model @brief Retreives a MOP Message slot. @param msg The message slot that must be taken or created. @optparam make If true (default) create the slot if it doesn't exist. @return The message slot coresponding with this name. */ FALCON_FUNC getSlot( ::Falcon::VMachine *vm ) { Item *i_msg = vm->param( 0 ); if ( i_msg == 0 || ! i_msg->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S" ) ); } CoreSlot* cs = vm->getSlot( *i_msg->asString(), (vm->param(1) == 0 || vm->param(1)->isTrue()) ); if ( cs == 0 ) { throw new MessageError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( *i_msg->asString() ) ); } else { Item* cc_slot = vm->findWKI( "VMSlot" ); fassert( cc_slot != 0 ); // the factory function takes care of increffing cs CoreObject *obj = cc_slot->asClass()->createInstance( cs ); cs->decref(); vm->retval( obj ); } } /*# @function consume @inset set_message_model @brief Consumes currently being broadcasted signal. */ FALCON_FUNC consume( ::Falcon::VMachine *vm ) { // TODO: report error. vm->consumeSignal(); } /*# @function assert @inset set_message_model @brief Creates a message assertion on a certain message slot. @param msg The message to be asserted. @param data The value of the assertion. If there are already subscribed callbacks for this message a broadcast on them is performed now. */ FALCON_FUNC assert( ::Falcon::VMachine *vm ) { // TODO: report error. Item *i_msg = vm->param( 0 ); Item *i_data = vm->param( 1 ); if ( i_msg == 0 || ! i_msg->isString() || i_data == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S,X" ) ); } CoreSlot* cs = vm->getSlot( *i_msg->asString(), true ); cs->setAssertion( vm, *i_data ); } /*# @function retract @inset set_message_model @brief Removes a previous assertion on a message. @param msg The message slot to be retracted. */ FALCON_FUNC retract( ::Falcon::VMachine *vm ) { Item *i_msg = vm->param( 0 ); if ( i_msg == 0 || ! i_msg->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S" ) ); } CoreSlot* cs = vm->getSlot( *i_msg->asString(), true ); if( cs != 0 ) { cs->retract(); if( cs->empty() ) { vm->removeSlot( cs->name() ); } } // TODO: report error if the slot is not found } /*# @function getAssert @inset set_message_model @brief Returns the given assertion, if it exists. @param msg The message slot on which the assertion is to be ckeched. @optparam default If given, instead of raising in case the assertion is not found, return this item. @raise MessageError if the given message is not asserted. */ FALCON_FUNC getAssert( ::Falcon::VMachine *vm ) { Item *i_msg = vm->param( 0 ); Item *i_defalut = vm->param(1); if ( i_msg == 0 || ! i_msg->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S" ) ); } CoreSlot* cs = vm->getSlot( *i_msg->asString(), true ); if ( cs == 0 ) { if ( i_defalut != 0 ) { vm->regA() = *i_defalut; } else { throw new MessageError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( *i_msg->asString() ) ); } } else { if( cs->hasAssert() ) { vm->regA() = cs->assertion(); } else { if ( i_defalut != 0 ) { vm->regA() = *i_defalut; } else { throw new MessageError( ErrorParam( e_inv_params ) .origin( e_orig_runtime ) .extra( *i_msg->asString() ) ); } } } } //================================================================================ // VMSlot wrapper class. // /*# @class VMSlot @brief VM Interface for message oriented programming operations. @optparam name The name of the mesasge managed by this VMSlot. The VMSlot instance is a direct interface to the messaging facility of the VM creating it. It is implicitly created by the @a getSlot function, but it can be directly created by the user. If a slot with the given name didn't previously exist, a new message slot is created in the virtual machine, otherwise the already existing slot is wrapped in the returned instance. @code // create a message slot x = VMSlot( "message" ) x.subscribe( handler ) ... y = VMSlot( "message" ) y.broadcast( "value" ) // handler is called. @endcode Same happens if the VMSlot is created via @a getSlot, or implicitly referenced via @a subscribe function. Slots are considered unique by name, so that comparisons on slots are performed on their names. If the @b name parameter is not given, the slot will be created as "anonymous", and won't be registered with this virtual machine. It will be possible to use it only through its methods. @section ev_anonym_slots Anonymous slots An anonymous slot can be created using an empty call to the VMSlot class constructor. This will make the slot private for the users that can access it; in other words, the slot won't be published to the VM and it won't be possible to broadcast on it using the standard functions @a broadcast or @a assert functions. @section ev_slots Automatic broadcast marshaling If a listener subscribed to a slot is a callable item, then it's called in case of broadcasts. If it's a non-callable property provider (class instances, blessed dictionaries, non-callable arrays) then a callable property named like "on_" is searched; if found, it's called (as a method of the host entity), otherwise a catch all method named "__on_event" is searched. If the "__on_event" method is found, it's called with the first parameter containing the broadcast name. Otherwise, an access error is raised. @section ev_events Events Events are "named broadcasts". They are issued on slots and excite all the listeners that are subscribed to that slot. The main difference is that automatic marshalling is directed to the name of the @i event rather to the name of the @i slot. See the following code: @code object Receiver _count = 0 function display(): > "Count is now ", self._count function on_add(): self._count++ function on_sub(): self._count-- function __on_event( evname ): > "Received an unknown event: ", evname end s = VMSlot() // creates an anonymous slot s.subscribe( Receiver ) s.send( "add" ) // Instead of sending a broadcast ... s.send( "add" ) // ... generate some events via send() s.send( "A strange event", "some param" ) // will complain Receiver.display() // show the count @endcode The @a VMSlot.send method works similarly to the @a VMSlot.broadcast method, but it allows to specify an arbitrary event name. Callbacks subscribed to this slot would be called for @i every event, be it generated through a broadcast or via a send call. @section ev_subslots Registering to events and Sub-slots While callbacks subscribed to the slot will be excited no matter what kind of event is generated, it is possible to @i register callbacks to respond only to @i particular @i events via the @a VMSlot.register method. See the following example: @code slot = VMSlot() // creates an anonymous slot slot.register( "first", { param => printl( "First called with ", param ) } ) slot.register( "second", { param => printl( "Second called with ", param ) } ) // send "first" and "second" events slot.send( "first", "A parameter" ) slot.send( "second", "Another parameter" ) // this will actually do nothing slot.broadcast( "A third parameter" ) @endcode As no callback is @i subscribed to the slot, but some are just @i register to some events, a generic @i broadcast will have no effect. An interesting thing about registering to events is that a slot keeps tracks of callbacks and items registered to a specific event via a named slot. For example, to know who is currently subscribed to the "first" event, it's possible to call the @a VMSlot.getEvent method and inspect the returned slot. Any change in that returned slot will cause the event registration to change. For example, continuing the above code... @code //... fevt = slot.getEvent( "first" ) // display each subscribed item for elem in fevt: > elem.toString() // and broadcast on the event first fevt.broadcast( "The parameter" ) @endcode As seen, a broadcast on a sub-slot is equivalent to an event send on the parent slot. @note It is then possible to cache repeatedly broadcast slots, so that the virtual machine is not forced to search across the subscribed events. This structure can be freely replicated at any level. In the above example, @b fevt may be subject of send() and register() method application, and its own events can be retrieved trough its @a VMSlot.getEvent method. */ /*# @endgroup */ FALCON_FUNC VMSlot_init( ::Falcon::VMachine *vm ) { Item *i_msg = vm->param( 0 ); if ( i_msg != 0 && ! (i_msg->isString()||i_msg->isNil()) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "[S]" ) ); } CoreSlot* vms; if ( i_msg != 0 && i_msg->isString() ) { vms = vm->getSlot( *i_msg->asString(), true ); fassert( vms != 0 ); } else { vms = new CoreSlot(""); } CoreSlotCarrier* self = dyncast( vm->self().asObjectSafe() ); self->setSlot( vms ); vms->decref(); } /*# @method broadcast VMSlot @brief Performs broadcast on this slot. @param ... Extra parameters to be sent to listeners. */ FALCON_FUNC VMSlot_broadcast( ::Falcon::VMachine *vm ) { CoreSlot* cs = (CoreSlot*) vm->self().asObject()->getUserData(); cs->prepareBroadcast( vm->currentContext(), 0, vm->paramCount() ); } /*# @method send VMSlot @brief Performs an event generation on this slot. @param event Event name. @param ... Extra parameters to be sent to listeners. The send method works as broadcast, with two major differences; - In case of objects being subscribed to this slot, the name of the broadcast message is that specified as a parameter, and not that of this slot. This means that automatic marshaling will be performed on methods named like on_, and not on_. - Items registered with @a VMSlot.register gets activated only if the @b event name is the same to which they are registered to. */ FALCON_FUNC VMSlot_send( ::Falcon::VMachine *vm ) { CoreSlot* cs = (CoreSlot*) vm->self().asObject()->getUserData(); Item *i_msg = vm->param( 0 ); if ( i_msg == 0 || ! i_msg->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S" ) ); } // better to cache the name Item iName = *i_msg; cs->prepareBroadcast( vm->currentContext(), 1, vm->paramCount()-1, 0, iName.asString() ); } /*# @method register VMSlot @brief Registers a listener for a specific event. @param event A string representing the event name. @param handler Handler to associate to this event. This function associates the given @b handler to a sub-slot named after the @b event parameter. This operation is equivalent to call @a VMSlot.getEvent() to create the desired sub-slot, and then call @a VMSlot.subscribe() on that named slot. @see VMSlot.send */ FALCON_FUNC VMSlot_register( ::Falcon::VMachine *vm ) { CoreSlot* cs = (CoreSlot*) vm->self().asObject()->getUserData(); Item *i_event = vm->param( 0 ); Item *i_handler = vm->param( 1 ); if ( i_event == 0 || ! i_event->isString() || i_handler == 0 || ! (i_handler->isComposed() || i_handler->isCallable() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S,C|O" ) ); } // Get the child core slot CoreSlot* child = cs->getChild( *i_event->asString(), true ); child->append( *i_handler ); } /*# @method getEvent VMSlot @brief Returns an event (as a child VMSlot) handled by this slot. @param event A string representing the event name. @optparam force Pass true to create the event if it is not existing. @return a VMSlot representing the given event in this slot, or @b nil if not found. This method returns a named VMSlot that will be excited by @a VMSlot.send applied on this slot with the same @b event name. In other words, subscribing or unsubscribing items from the returned slot would add or remove listeners for a @a VMSlot.send call on this slot. Also, a broadcast on the returned VMSlot has the same effect of a @a VMSlot.send with the same name as the @b event passed. @see VMSlot.send */ FALCON_FUNC VMSlot_getEvent( ::Falcon::VMachine *vm ) { CoreSlot* cs = (CoreSlot*) vm->self().asObject()->getUserData(); Item *i_event = vm->param( 0 ); Item *i_force = vm->param( 1 ); if ( i_event == 0 || ! i_event->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S" ) ); } // Get the child core slot CoreSlot* child = cs->getChild( *i_event->asString(), i_force != 0 && i_force->isTrue() ); // force creation if required if( child != 0 ) { // found or to be created. Item* icc = vm->findWKI("VMSlot"); fassert( icc != 0 ); fassert( icc->isClass() ); vm->retval( icc->asClass()->createInstance(child) ); child->decref(); } else { vm->retnil(); } } /*# @method name VMSlot @brief Returns the name of this slot @return The name of the event bind to this slot (as a string). */ FALCON_FUNC VMSlot_name( ::Falcon::VMachine *vm ) { CoreSlot* cs = (CoreSlot*) vm->self().asObject()->getUserData(); vm->retval( new CoreString(cs->name()) ); } /*# @method subscribe VMSlot @brief Registers a callback handler on this slot. @param handler A callable item or instance providing callback support. @optparam prio Set to true to have this handler called before the previous ones. */ FALCON_FUNC VMSlot_subscribe( ::Falcon::VMachine *vm ) { Item *callback = vm->param(0); Item *i_prio = vm->param(1); if ( callback == 0 || ! ( callback->isCallable() || callback->isComposed() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "C" )); } CoreSlot* cs = (CoreSlot*) vm->self().asObject()->getUserData(); if( i_prio != 0 && i_prio->isTrue() ) cs->push_front( *callback ); else cs->push_back( *callback ); check_assertion( vm, cs, *callback ); } /*# @method unsubscribe VMSlot @brief Unregisters a callback handler from this slot. @param handler The callable that must be unregistered. @raise CodeError if the @b handler is not registered with this slot. */ FALCON_FUNC VMSlot_unsubscribe( ::Falcon::VMachine *vm ) { Item *callback = vm->param(0); if ( callback == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S,C" ) ); } CoreSlot* cs = (CoreSlot*) vm->self().asObject()->getUserData(); if( ! cs->remove( *callback ) ) { throw new CodeError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( "unregister" ) ); } if ( cs->empty() && ! cs->hasAssert() ) { vm->removeSlot( cs->name() ); } } /*# @method prepend VMSlot @brief Registers a callback handler that will be called before the others. @param handler The callable that must be unregistered. */ FALCON_FUNC VMSlot_prepend( ::Falcon::VMachine *vm ) { Item *callback = vm->param(0); if ( callback == 0 || ! ( callback->isCallable() || callback->isComposed() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "C" ) ); } CoreSlot* cs = (CoreSlot*) vm->self().asObject()->getUserData(); cs->push_front( *callback ); check_assertion( vm, cs, *callback ); } /*# @method assert VMSlot @brief Creates a message assertion on this certain message slot. @param data The value of the assertion. If there are already subscribed callbacks for this message a broadcast on them is performed now. */ FALCON_FUNC VMSlot_assert( ::Falcon::VMachine *vm ) { Item *i_data = vm->param( 0 ); if ( i_data == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "X" ) ); } CoreSlot* cs = (CoreSlot*) vm->self().asObject()->getUserData(); cs->setAssertion( vm, *i_data ); } /*# @method retract VMSlot @brief Removes a previous assertion on a message. */ FALCON_FUNC VMSlot_retract( ::Falcon::VMachine *vm ) { CoreSlot* cs = (CoreSlot*) vm->self().asObject()->getUserData(); cs->retract(); if( cs->empty() ) { vm->removeSlot( cs->name() ); } } /*# @method getAssert VMSlot @brief Gets the item asserted for this slot. @optparam default If given, instead of raising in case the essartion is not found, return this item. @raise MessageError if the item has not an assertion and a default is not given. */ FALCON_FUNC VMSlot_getAssert( ::Falcon::VMachine *vm ) { CoreSlot* cs = (CoreSlot*) vm->self().asObject()->getUserData(); if( cs->hasAssert() ) { vm->regA() = cs->assertion(); } else { if ( vm->paramCount() > 0 ) { vm->regA() = *vm->param(0); } else { throw new MessageError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "..." ) ); } } } /*# @method first VMSlot @brief Gets an iterator to the first subscriber. @return An iterator to the first subscriber of this message slot. */ FALCON_FUNC VMSlot_first( ::Falcon::VMachine *vm ) { CoreSlot* cs = (CoreSlot*) vm->self().asObject()->getUserData(); Item* cc = vm->findWKI( "Iterator" ); fassert( cc != 0 ); CoreObject *oi = cc->asClass()->createInstance( new Iterator( cs ) ); vm->retval( oi ); } /*# @method last VMSlot @brief Gets an iterator to the last subscriber. @return An iterator to the last subscriber of this message slot. */ FALCON_FUNC VMSlot_last( ::Falcon::VMachine *vm ) { CoreSlot* cs = (CoreSlot*) vm->self().asObject()->getUserData(); Item* cc = vm->findWKI( "Iterator" ); fassert( cc != 0 ); CoreObject *oi = cc->asClass()->createInstance( new Iterator( cs, true) ); vm->retval( oi ); } } } /* end of message_ext.cpp */ engine/core_module/oob_ext.cpp000066400000000000000000000104631176363201700167750ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: functional_ext.cpp Functional programming support ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 14 Aug 2008 02:10:57 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "core_module.h" /*# @beginmodule core */ namespace Falcon { namespace core { /*# @funset oob_support Out of band items support @brief Handle out of band items. Out-of-band items are normal items which can be tested for the out-of-band quality through the @a isoob function to perform special tasks. Some core and RTL functions can check for the item being out-of-band to take special decisions about the item, or to modify their behavior. For example, the @a map function drops the item (acting like @a filter ), if it is out-of-band. This feature is available also to scripts; functions accepting any kind of items from callbacks they are using to generate data may wish to receive special instructions through out of band data. In the next example, a data producer returns a set of items one at a time, and notifies the caller to switch to another producer via an out-of-band notification. @code function firstSeries() static: vals = [1, 2, 3, 4 ] if vals: return arrayHead( vals ) // notify the next function return oob( secondSeries ) end function secondSeries() static: vals = [ "a", nil, "b", 4 ] if vals: return arrayHead( vals ) // notify we're done with an nil OOB return oob() end function consumer( producer ) loop item = producer() if isoob( item ) // An OOB means we have something special. If it's nil, we're done... if item == nil: return // else it's the notification of a new producer producer = item else // if it's not an OOB, then we must process it > "Received item: ", item end end end consumer( firstSeries ) @endcode Marking an item as out-of-band allows the creation of @i monads in functional evaluations. More automatism will be introduced in future, but scripters can have monads by assigning the oob status to complex objects and perform out-of-band processing on them. */ /*# @function oob @brief Generates an out-of-band item. @inset oob_support @optparam item The item to be declared out of band. @return An oob version of the item, or an oob @b nil if no item is given. This function returns an out-of-band nil item, or if a parameter is given, an out-of-band version of that item. */ FALCON_FUNC core_oob( ::Falcon::VMachine *vm ) { Item *obbed = vm->param(0); if ( ! obbed ) { vm->regA().setNil(); } else { vm->regA() = *obbed; } vm->regA().setOob(); } /*# @function deoob @brief Turns an out-of-band item in a normal item. @inset oob_support @param item The out of band item to be turned into a normal item. @return An the non-out-of-band version version of the item. The function returns a flat copy of the item without the out-of-band status set. If the item was initially not OOB, then deoob() does nothing. See @a oob for a deeper explanation of OOB items. */ FALCON_FUNC core_deoob( ::Falcon::VMachine *vm ) { Item *obbed = vm->param(0); if ( ! obbed ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "X" ) ); return; } vm->regA() = *obbed; vm->regA().resetOob(); } /*# @function isoob @brief Checks for the out-of-band status of an item. @inset oob_support @param item The item to be checked. @return True if the item is out of band, false otherwise. This function can be used to check if a certain item is an out of band item. */ FALCON_FUNC core_isoob( ::Falcon::VMachine *vm ) { Item *obbed = vm->param(0); if ( ! obbed ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "X" ) ); return; } vm->regA().setBoolean( obbed->isOob() ); } } } /* end of oob_ext.cpp */ engine/core_module/pagedict_ext.cpp000066400000000000000000000054301176363201700177740ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: pagedict_ext.cpp Page dictionary extensions. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 14 Aug 2008 00:17:31 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "core_module.h" #include /*# @beginmodule core */ namespace Falcon { namespace core { /*# @function PageDict @ingroup general_purpose @brief Creates a paged dictionary (which is internally represented as a B-Tree). @param pageSize size of pages expressed in maximum items. @return A new dictionary. The function returns a Falcon dictionary that can be handled exactly as a normal dictionary. The difference is only in the internal management of memory allocation and tree balance. Default Falcon dictionaries (the ones created with the "[=>]" operator) are internally represented as paired linear vectors of ordered entries. They are extremely efficient to store a relatively small set of data, whose size, and possibly key order, is known in advance. As this is exactly the condition under which source level dictionary are created, this way to store dictionary is the default in Falcon. The drawback is that if the data grows beyond a critical mass linear dictionary may become sluggishly slow and hang down the whole VM processing. This function, which is actually a class factory function (this is the reason why its name begins in uppercase), returns an empty Falcon dictionary that is internally organized as a B-Tree structure. At a marginal cost in term of memory with respect to the mere storage of falcon items, which is used as spare and growth area, this structure offer high performances on medium to large amount of data to be ordered and searched. Empirical tests in Falcon language showed that this structure can scale up easily to several millions items. In general, if a Falcon dictionary is meant to store large data, above five to ten thousands elements, or if the size of stored data is not known in advance, using this structure instead of the default Falcon dictionaries is highly advisable. */ FALCON_FUNC PageDict( ::Falcon::VMachine *vm ) { Item *i_pageSize = vm->param(0); if( i_pageSize != 0 && ! i_pageSize->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params ).origin( e_orig_runtime ).extra( "[N]" ) ); } uint32 pageSize = (uint32)( i_pageSize == 0 ? 33 : (uint32)i_pageSize->forceInteger() ); CoreDict *cd = new CoreDict( new ::Falcon::PageDict( pageSize ) ); vm->retval( cd ); } } } /* end of pagedict_ext.cpp */ engine/core_module/param_ext.cpp000066400000000000000000000267031176363201700173220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: param_ext.cpp Variable parameter management support. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 14 Aug 2008 01:54:37 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "core_module.h" #include /*# @beginmodule core */ namespace Falcon { namespace core { /*# @funset varparams_support Variable parameters support @brief Functions giving informations on variable parameters. Falcon supports variable parameter calling; a function or method may access the items that have been used in the parameter call by counting them and accessing them one by one. Parameter passed by reference may be modified with the appropriate function. This functions may be used whether the calling function provides a list of formal parameters or not. The first formal parameter will be treated as the variable parameter number zero, and the parameter count may be the same as, more than or less than the number of formal parameters. So, part of the parameters may be accessible via parameter names, and the others may be accessed with the functions in this group. */ /*# @function paramCount @return The parameter count. @inset varparams_support @brief Returns number of parameter that have been passed to the current function or method. The return value is the minimum value between the formal parameters declared for the current function and the number of actual parameters the caller passed. Formal parameters which are declared in the function header, but for which the caller didn't provide actual parameters, are filled with nil. */ FALCON_FUNC paramCount ( ::Falcon::VMachine *vm ) { StackFrame *thisFrame = vm->currentFrame(); if( thisFrame == 0 || thisFrame->prev() == 0 ) throw new GenericError( ErrorParam( e_stackuf, __LINE__ ).origin( e_orig_runtime ) ); vm->retval( (int64) thisFrame->prev()->m_param_count ); } /*# @function parameter @brief Gets the Nth parameter @inset varparams_support @param pnum The ordinal number of the paremeter, zero based @return The nth paramter (zero based) or NIL if the parameter is not given. @raise AccessError if @b pnum is out of range. This function returns the required parameter, being the first one passed to the function indicated as 0, the second as 1 and so on. Both formally declared parameters and optional parameters can be accessed this way. If the given parameter number cannot be accessed, a AccessError is raised. @note This function used to be called "paramNumber", and has been renamed in version 0.8.10. The function is still aliased throught the old function name for compatibility reason, but its usage is deprecated. Use @b parameter instead. */ FALCON_FUNC _parameter ( ::Falcon::VMachine *vm ) { Item *number = vm->param(0); if ( number == 0 || ! number->isOrdinal() ) { throw new ParamError( ErrorParam( e_param_range ).origin( e_orig_runtime ).extra( "(N)" ) ); } StackFrame *thisFrame = vm->currentFrame(); if ( thisFrame == 0 || thisFrame->prev() == 0 ) throw new GenericError( ErrorParam( e_stackuf, __LINE__ ).origin( e_orig_runtime ) ); // ...but we want the parameter count of our caller. StackFrame *prevFrame = thisFrame->prev(); // ...while the parameters are below our frame's base. uint32 val = (uint32) number->forceInteger(); if( val >= 0 && val < prevFrame->m_param_count ) { vm->retval( prevFrame->m_params[val] ); } else { vm->retnil(); } } /*# @function paramIsRef @inset varparams_support @brief Checks whether the nth parameter has been passed by reference or not. @param number The paramter that must be checked (zero based) @return true if the parameter has been passed by reference, false otherwise. @raise AccessError if @b number is out of range. Both assigning a value to a certain parameter and using the paramSet() function will change locally the value of the parameter, but this value won't be reflected in the actual parameter that was used to call the function, unless the parameter was explicitly passed by reference. In some contexts, it may be useful to know if this is the case. If the given parameter number cannot be accessed, a AccessError is raised. */ FALCON_FUNC paramIsRef ( ::Falcon::VMachine *vm ) { Item *number = vm->param(0); if ( number == 0 || ! number->isOrdinal() ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ).origin( e_orig_runtime ).extra( "N" ) ); } StackFrame *thisFrame = vm->currentFrame(); if ( thisFrame == 0 || thisFrame->prev() == 0 ) throw new GenericError( ErrorParam( e_stackuf, __LINE__ ).origin( e_orig_runtime ) ); // ...but we want the parameter count of our caller. StackFrame *prevFrame = thisFrame->prev(); uint32 val = (uint32) number->forceInteger(); if( val >= 0 && val < prevFrame->m_param_count ) { vm->regA().setBoolean( prevFrame->m_params[val].isReference() ? true: false ); } else { vm->regA().setBoolean( false ); } } /*# @function paramSet @inset varparams_support @brief Changes the nth paramter if it has been passed by reference. @param number the paramter to be changed (zero based) @param value the new value for the parameter @raise AccessError if @b number is out of range. The function is equivalent to assigning the value directly to the required parameter; of course, in this way also optional parameter may be accessed. If the required parameter was passed by reference, also the original value in the caller is changed. If the given parameter number cannot be accessed, an AccessError is raised. */ FALCON_FUNC paramSet ( ::Falcon::VMachine *vm ) { Item *number = vm->param(0); Item *value = vm->param(1); if ( number == 0 || ! number->isOrdinal() || value == 0) { throw new ParamError( ErrorParam( e_param_range ).origin( e_orig_runtime ).extra( "N,X" ) ); } StackFrame *thisFrame = vm->currentFrame(); if ( thisFrame == 0 || thisFrame->prev() == 0 ) throw new GenericError( ErrorParam( e_stackuf, __LINE__ ).origin( e_orig_runtime ) ); // ...but we want the parameter count of our caller. StackFrame *prevFrame = thisFrame->prev(); uint32 val = (uint32) number->forceInteger(); if( val >= 0 && val < prevFrame->m_param_count ) { prevFrame->m_params[val].dereference()->copy( *value ); } } /*# @function argv @inset varparams_support @brief Returns all the parameters of the current function as a vector. If the current function doesn't receive any parameter, it returns nil. */ FALCON_FUNC core_argv( VMachine *vm ) { StackFrame *thisFrame = vm->currentFrame(); if ( thisFrame == 0 || thisFrame->prev() == 0 ) throw new GenericError( ErrorParam( e_stackuf, __LINE__ ).origin( e_orig_runtime ) ); // ...but we want the parameter count of our caller. StackFrame *prevFrame = thisFrame->prev(); // ...while the parameters are below our frame's base. if( prevFrame->m_param_count > 0 ) { CoreArray* arr = new CoreArray(prevFrame->m_param_count); Item* first = prevFrame->m_params; memcpy( arr->items().elements(), first, arr->items().esize( prevFrame->m_param_count ) ); arr->length( prevFrame->m_param_count ); vm->retval( arr ); } } /*# @function argd @inset varparams_support @brief Returns a dictionary containing all the parameters passed to the current function. The dictionary contains the parameter names associated with the value passed by the caller. Parameters received beyond the officially declared ones aren't returned in this dictionary. If the function doesn't declare any parameter, returns nil. */ FALCON_FUNC core_argd( VMachine *vm ) { StackFrame *thisFrame = vm->currentFrame(); if ( thisFrame == 0 || thisFrame->prev() == 0 ) throw new GenericError( ErrorParam( e_stackuf, __LINE__ ).origin( e_orig_runtime ) ); // ...but we want the parameter count of our caller. StackFrame *prevFrame = thisFrame->prev(); // get the caller function symbol --- it holds the declared parameters const Symbol* sym = thisFrame->m_symbol; const Map* st = sym->isFunction()? &sym->getFuncDef()->symtab().map() : &sym->getExtFuncDef()->parameters()->map(); CoreDict* ret = 0; Item* first = prevFrame->m_params; // ...while the parameters are below our frame's base. MapIterator iter = st->begin(); while( iter.hasCurrent() ) { Symbol *p = (*(Symbol**)iter.currentValue()); if( p->isParam() ) { if( ret == 0 ) ret = new CoreDict( new LinearDict ); ret->put( Item(new CoreString( p->name() )), first[p->itemId()] ); } iter.next(); } if ( ret != 0 ) vm->retval( ret ); } /*# @function passvp @inset varparams_support @brief Returns all the undeclared parameters, or passes them to a callable item @optparam citem Callable item on which to pass the parameters. @return An array containing unnamed parameters, or the return value \b citem. This function returns all the parameters passed to this function but not declared in its prototype (variable parameters) in an array. If the host function doesn't receive any extra parameter, this function returns an empty array. This is useful in case the array is immediately added to a direct call. For example: @code function receiver( a, b ) > "A: ", a > "B: ", b > "Others: ", passvp().describe() end receiver( "one", "two", "three", "four" ) @endcode If @b citem is specified, the function calls citem passing all the extra parameters to it. For example: @code function promptPrint( prompt ) passvp( .[printl prompt] ) end promptPrint( "The prompt: ", "arg1", " ", "arg2" ) @endcode */ FALCON_FUNC core_passvp( VMachine *vm ) { StackFrame *thisFrame = vm->currentFrame(); if ( thisFrame == 0 || thisFrame->prev() == 0 ) throw new GenericError( ErrorParam( e_stackuf, __LINE__ ).origin( e_orig_runtime ) ); // ok, do we have an item to call? Item* i_citem = vm->param(0); if ( i_citem != 0 && ! i_citem->isCallable() ) throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).extra("[C]").origin( e_orig_runtime ) ); // ...but we want the parameter count of our caller. StackFrame *prevFrame = thisFrame->prev(); // get the caller function symbol --- it holds the declared parameters const Symbol* sym = thisFrame->m_symbol; unsigned size = sym->isFunction()? sym->getFuncDef()->params() : sym->getExtFuncDef()->parameters()->size(); Item* first = prevFrame->m_params; int pcount = prevFrame->m_param_count - size; if ( pcount < 0 ) pcount = 0; if ( i_citem ) { while( size < prevFrame->m_param_count ) { vm->pushParam( first[size] ); ++size; } // size may be > param count in ext funcs. vm->callFrame(*i_citem, pcount ); } else { CoreArray* arr = new CoreArray( pcount ); while( size < prevFrame->m_param_count ) { arr->append( first[size] ); ++size; } vm->retval( arr ); } } } } /* end of param_ext.cpp */ engine/core_module/path_ext.cpp000066400000000000000000000346111176363201700171530ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dir.cpp Directory management api. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 28 Jan 2009 07:53:27 -0800 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ /** \file Path - script interface API */ #include #include #include #include #include #include #include #include #include "core_module.h" namespace Falcon { namespace core { class PathObject: public CRObject { public: PathObject( const CoreClass *genr, Path* path, bool bSerial ): CRObject( genr, bSerial ) { if ( path == 0 ) path = new Path; setUserData( path ); } PathObject( const PathObject &other ); virtual ~PathObject(); virtual PathObject *clone() const; Path* getPath() const { return static_cast( m_user_data ); } }; CoreObject* PathObjectFactory( const CoreClass *me, void *path, bool dyn ) { return new PathObject( me, static_cast( path ), dyn ); } PathObject::PathObject( const PathObject &other ): CRObject( other ) { setUserData( new Path( *getPath() ) ); } PathObject::~PathObject() { delete getPath(); } PathObject *PathObject::clone() const { return new PathObject( *this ); } /*# @class Path @brief Interface to local filesystem path definition. @optparam path The path that will be used as initial path. @raise ParamError in case the inital path is malformed. This class offers an object oriented interface to access path elements given a complete path, or to build a path from its elements. Builds the path object, optionally using the given parameter as a complete path constructor. If the parameter is an array, it must have at least four string elements, and it will be used to build the path from its constituents. For example: @code unit = "C" location = "/a/path/to" file = "somefile" ext = "anext" p = Path( [ unit, location, file, ext ] ) @endcode @b nil can be passed if some part of the specification is not used. The path (or any part of it) may be specified both in RFC3986 format or in MS-Windows path format. @note Use the fileNameMerge() function to simply merge elements of a path specification into a string. @see fileNameMerge */ /*# @property unit Path @brief Unit specificator. @raise ParamError if assigned to a value that makes the path invalid. This is the unit specificator (disk name) used in some filesystems. It is separated by the rest of the path via a ":". According to RFC 3986 it always starts with a "/", which is automatically added if absent. */ /*# @property location Path @brief Location specificator. @raise ParamError if assigned to a value that makes the path invalid. This is the "path to file". It can start with a "/" or not; if it starts with a "/" it is considered absolute. */ /*# @property fulloc Path @brief Unit specificator and location. @raise ParamError if assigned to a value that makes the path invalid. This property contains the location of this path, including the unit specificator, if present. So, in a path like "/C:/path/to/me.txt", the @b fulloc property (notice the two 'l' characters in the name) will have the value of "/C:/path/to", while in a relative path like "relative/file.txt" it will take the same value of @a Path.location. Assigning a value to this property means to change the value of both the unit specificator and location at the same time. */ /*# @property file Path @brief File part. @raise ParamError if assigned to a value that makes the path invalid. This element coresponds to the first part of the file element, if it is divided into a filename and an extension by a "." dot. @note If an extension is given, then @b filename is the same as @b file + "." + @b extension */ /*# @property filename Path @brief File name part. @raise ParamError if assigned to a value that makes the path invalid. This is the part of the path that identifies an element in a directory. It includes everything after the last "/" path separator. @note If an extension is given, then @b filename is the same as @b file + "." + @b extension */ /*# @property extension Path @brief File extension part. @raise ParamError if assigned to a value that makes the path invalid. This element coresponds to the first last of the file element, if it is divided into a filename and an extension by a "." dot. @note If an extension is given, then @b filename is the same as @b file + "." + @b extension */ /*# @property path Path @brief Complete path. @raise ParamError if assigned to a value that makes the path invalid. This is the complete path referred by this object. */ /*# @property winpath Path @brief Complete path in MS-Windows format. This is the complete path referred by this object, given in MS-Windows format. Use this if you need to produce scripts or feed it into external process on windows platforms. Normally, all the I/O functions used by Falcon on any platform understand the RFC3986 format. @note The property is read-only; you can anyhow assign a path in MS-Windows format to the @a Path.path property. */ /*# @property winloc Path @brief Complete path in MS-Windows format. This is the location element in the complete path, given in MS-Windows format. Use this if you need to produce scripts or feed it into external process on windows platforms. Normally, all the I/O functions used by Falcon on any platform understand the RFC3986 format. @note The property is read-only; you can anyhow assign a location in MS-Windows format to the @a Path.location property. */ /*# @property winfulloc Path @brief Complete path in MS-Windows format. This is the full location element in this path (unit specificator + location), given in MS-Windows format. Use this if you need to produce scripts or feed it into external process on windows platforms. Normally, all the I/O functions used by Falcon on any platform understand the RFC3986 format. @note The property is read-only; you can anyhow assign a full location in MS-Windows format to the @a Path.fulloc property. */ // Reflective URI method void Path_path_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); if ( ! path.isValid() ) { VMachine* vm = VMachine::getCurrent(); throw new ParamError( ErrorParam( e_inv_params ). origin( e_orig_runtime ). extra( vm != 0 ? vm->moduleString( rtl_invalid_path ) : "" ) ); } // And now set the property if ( property.isString() ) property.asString()->bufferize( path.get() ); else property = new CoreString( path.get() ); } void Path_path_rto(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); // We're setting the URI, that is, the property has been written. FALCON_REFLECT_STRING_TO( (&path), set ); if ( ! path.isValid() ) { VMachine* vm = VMachine::getCurrent(); throw new ParamError( ErrorParam( e_inv_params ). origin( e_orig_runtime ). extra( vm != 0 ? vm->moduleString( rtl_invalid_path ) : "" ) ); } } // Reflective URI method void Path_filename_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); FALCON_REFLECT_STRING_FROM( (&path), getFilename ); } void Path_filename_rto(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); // We're setting the path, that is, the property has been written. FALCON_REFLECT_STRING_TO( (&path), setFilename ); if ( ! path.isValid() ) { VMachine* vm = VMachine::getCurrent(); throw new ParamError( ErrorParam( e_inv_params ). origin( e_orig_runtime ). extra( vm != 0 ? vm->moduleString( rtl_invalid_path ) : "" ) ); } } // Reflective path method void Path_file_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); FALCON_REFLECT_STRING_FROM( (&path), getFile ); } void Path_file_rto(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); // We're setting the path, that is, the property has been written. FALCON_REFLECT_STRING_TO( (&path), setFile ); if ( ! path.isValid() ) { VMachine* vm = VMachine::getCurrent(); throw new ParamError( ErrorParam( e_inv_params ). origin( e_orig_runtime ). extra( vm != 0 ? vm->moduleString( rtl_invalid_path ) : "" ) ); } } // Reflective path method void Path_location_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); FALCON_REFLECT_STRING_FROM( (&path), getLocation ); } void Path_location_rto(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); // We're setting the path, that is, the property has been written. FALCON_REFLECT_STRING_TO( (&path), setLocation ); if ( ! path.isValid() ) { VMachine* vm = VMachine::getCurrent(); throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra( vm != 0 ? vm->moduleString( rtl_invalid_path ) : "" ) ); } } // Reflective full location method void Path_fullloc_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); FALCON_REFLECT_STRING_FROM( (&path), getFullLocation ); } void Path_fullloc_rto(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); // We're setting the path, that is, the property has been written. FALCON_REFLECT_STRING_TO( (&path), setFullLocation ); if ( ! path.isValid() ) { VMachine* vm = VMachine::getCurrent(); throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra( vm != 0 ? vm->moduleString( rtl_invalid_path ) : "" ) ); } } // Reflective path method void Path_unit_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); FALCON_REFLECT_STRING_FROM( (&path), getResource ); } void Path_unit_rto(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); // We're setting the path, that is, the property has been written. FALCON_REFLECT_STRING_TO( (&path), setResource ); if ( ! path.isValid() ) { VMachine* vm = VMachine::getCurrent(); throw new ParamError( ErrorParam( e_inv_params ). origin( e_orig_runtime ). extra( vm != 0 ? vm->moduleString( rtl_invalid_path ) : "" ) ); } } // Reflective path method void Path_extension_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); FALCON_REFLECT_STRING_FROM( (&path), getExtension ); } void Path_extension_rto(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); // We're setting the path, that is, the property has been written. FALCON_REFLECT_STRING_TO( (&path), setExtension ); if ( ! path.isValid() ) { VMachine* vm = VMachine::getCurrent(); throw new ParamError( ErrorParam( e_inv_params ). origin( e_orig_runtime ). extra( vm != 0 ? vm->moduleString( rtl_invalid_path ) : "" ) ); } } // Reflective windows method void Path_winpath_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); FALCON_REFLECT_STRING_FROM( (&path), getWinFormat ); } // Reflective windows method void Path_winloc_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); FALCON_REFLECT_STRING_FROM( (&path), getWinLocation ); } // Reflective windows method void Path_winfulloc_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { Path &path = *static_cast( user_data ); FALCON_REFLECT_STRING_FROM( (&path), getFullWinLocation ); } FALCON_FUNC Path_init ( ::Falcon::VMachine *vm ) { Item *p0 = vm->param(0); // no parameter? -- ok, we have an empty path if ( p0 == 0 ) return; // extract the path instance created by the factory function if ( ( ! p0->isString() && ! p0->isArray() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ).extra( "[S|A]" ) ); } PathObject *self = dyncast( vm->self().asObject() ); Path *path = self->getPath(); if ( p0->isString() ) { path->set( *p0->asString() ); } else { const String *unitspec = 0; const String *fname = 0; const String *fpath = 0; const String *fext = 0; String sDummy; const CoreArray &array = *p0->asArray(); if( array.length() >= 0 && array[0].isString() ) unitspec = array[0].asString(); else unitspec = &sDummy; if( array.length() >= 1 && array[1].isString() ) fpath = array[1].asString(); else fpath = &sDummy; if( array.length() >= 2 && array[2].isString() ) fname = array[2].asString(); else fname = &sDummy; if( array.length() >= 3 && array[3].isString() ) fext = array[3].asString(); else fext = &sDummy; path->join( *unitspec, *fpath, *fname, *fext ); } if ( ! path->isValid() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra( vm->moduleString( rtl_invalid_path ) ) ); } } } } /* end of path_ext.cpp */ engine/core_module/poopseq_ext.cpp000066400000000000000000000076301176363201700177060ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: poopcomp_ext.cpp Prototype oop oriented sequence interface. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 10 Aug 2009 11:19:09 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ #include #include #include #include #include #include #include "core_module.h" namespace Falcon { namespace core { /*# @method comp Object @brief Appends elements to this object through a filter. @param source One sequence, range or callable generating items. @optparam filter A filtering function receiving one item at a time. @return This object. This method extracts one item at a time from the source, and calls repeatedly the @b append method of this object. Please, see the description of @a Sequence.comp. @see Sequence.comp */ FALCON_FUNC Object_comp ( ::Falcon::VMachine *vm ) { if ( vm->param(0) == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "R|A|C|Sequence, [C]" ) ); } // Save the parameters as the stack may change greatly. CoreObject* obj = vm->self().asObject(); Item i_gen = *vm->param(0); Item i_check = vm->param(1) == 0 ? Item(): *vm->param(1); PoopSeq* seq = new PoopSeq( vm, Item(obj) ); // may throw vm->pushParam( new GarbagePointer( seq ) ); seq->comprehension_start( vm, vm->self(), i_check ); vm->pushParam( i_gen ); } /*# @method mcomp Object @brief Appends elements to this object through a filter. @param ... One or more sequences, ranges or callables generating items. @return This object. This method sends the data generated from multiple comprehension, to the append method of this object. Please, see the description of @a Sequence.comp. @see Sequence.mcomp */ FALCON_FUNC Object_mcomp ( ::Falcon::VMachine *vm ) { // Save the parameters as the stack may change greatly. CoreObject* obj = vm->self().asObject(); StackFrame* current = vm->currentFrame(); PoopSeq* seq = new PoopSeq( vm, Item(obj) ); // may throw vm->pushParam( new GarbagePointer( seq ) ); seq->comprehension_start( vm, vm->self(), Item() ); for( uint32 i = 0; i < current->m_param_count; ++i ) { vm->pushParam( current->m_params[i] ); } } /*# @method mfcomp Object @brief Appends elements to this object through a filter. @param filter A filter function receiving each element before its insertion, or nil. @param ... One or more sequences, ranges or callables generating items. @return This object. This method performs a filtered multiple comprehension and and calls repeatedly the @b append method of this object, passing the output of the filter function to it each time. If the filter function returns an oob(1), the step is skipped and the @b append method is not called. Please, see the description of @a Sequence.comp. @see Sequence.mfcomp */ FALCON_FUNC Object_mfcomp ( ::Falcon::VMachine *vm ) { if ( vm->param(0) == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "C, ..." ) ); } // Save the parameters as the stack may change greatly. CoreObject* obj = vm->self().asObject(); StackFrame* current = vm->currentFrame(); Item i_check = vm->param(0) == 0 ? Item(): *vm->param(0); PoopSeq* seq = new PoopSeq( vm, Item(obj) ); // may throw vm->pushParam( new GarbagePointer( seq ) ); seq->comprehension_start( vm, vm->self(), i_check ); for( uint32 i = 1; i < current->m_param_count; ++i ) { vm->pushParam( current->m_params[i] ); } } } } /* end of poopseq_ext.cpp */ engine/core_module/print.cpp000066400000000000000000000054301176363201700164700ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: print.cpp Basic module ------------------------------------------------------------------- Author: $AUTHOR Begin: $DATE ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ #include #include #include #include #include namespace Falcon { namespace core { /*# @function print @inset core_basic_io @param ... An arbitrary list of parameters. @brief Prints the contents of various items to the standard output stream. This function is the default way for a script to say something to the outer world. Scripts can expect print to do a consistent thing with respect to the environment they work in; stand alone scripts will have the printed data to be represented on the VM output stream. The stream can be overloaded to provide application supported output; by default it just passes any write to the process output stream. The items passed to print are just printed one after another, with no separation. After print return, the standard output stream is flushed and the cursor (if present) is moved past the last character printed. The function @a printl must be used, or a newline character must be explicitly placed among the output items. The print function has no support for pretty print (i.e. numeric formatting, space padding and so on). Also, it does NOT automatically call the toString() method of objects. @see printl */ FALCON_FUNC print ( ::Falcon::VMachine *vm ) { Stream *stream = vm->stdOut(); if ( stream == 0 ) { return; } for (int i = 0; i < vm->paramCount(); i ++ ) { Item *elem = vm->param(i); String temp; switch( elem->type() ) { case FLC_ITEM_STRING: stream->writeString( *elem->asString() ); break; default: elem->toString( temp ); stream->writeString( temp ); } } stream->flush(); } /*# @function printl @inset core_basic_io @param ... An arbitrary list of parameters. @brief Prints the contents of various items to the VM standard output stream, and adds a newline. This functions works exactly as @a print, but it adds a textual "new line" after all the items are printed. The actual character sequence may vary depending on the underlying system. @see print */ FALCON_FUNC printl ( ::Falcon::VMachine *vm ) { Stream *stream = vm->stdOut(); if ( stream == 0 ) { return; } print( vm ); stream->writeString( "\n" ); stream->flush(); } }} /* end of print.cpp */ engine/core_module/random.cpp000066400000000000000000000321231176363201700166130ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: random.cpp Random number related functions. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun nov 8 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ /** \file Random number generator related functions. */ #include #include #include #include #include #include #include #include #include /*# @funset core_random Random functions @brief Functions providing random numbers and sequences. @beginset core_random */ namespace Falcon { namespace core { class RandomCarrier : public FalconData { public: RandomCarrier() {}; inline MTRand &getRNG(void) { return mtrand; } virtual RandomCarrier *clone() const { return NULL; } // not cloneable virtual void gcMark( uint32 mark ) {} virtual bool serialize( Stream *stream, bool bLive ) const { return false; } virtual bool deserialize( Stream *stream, bool bLive ) { return false; } private: MTRand mtrand; }; FALCON_FUNC flc_Random_init( ::Falcon::VMachine *vm ) { RandomCarrier *rc = new RandomCarrier(); if ( vm->paramCount() ) { // throw to indicate there is no other type yet we can use to seed the RNG rc->getRNG().seed( (uint32)vm->param(0)->forceIntegerEx() ); } vm->self().asObject()->setUserData(rc); } /*# @function random @brief Returns a pseudo random number. @param ... See below. @return A pseudo random number or a random item picked from parameters. This function has actually several functionalities that are selected depending on the parameters. Without parameters, the function returns a floating point number in the range [0,1). With a signle numeric parameter, the function returns an integer between 0 and the number, included. The following functions are equivalent: @code > random( x ) > int( random() * (x + 1) ) @endcode With two numeric parameters, the function returns an integer in the range [x, y]. The following functions are equivalent: @code > random( x, y ) > int( x + (random() * (y + 1)) ) @endcode With more than two parameters, or when at least one of the first two parameters it not a number, one of the parameter is picked at random and then returned. The function @a randomChoice returns unambiguously one of the parameters picked at random. */ FALCON_FUNC flc_random ( ::Falcon::VMachine *vm ) { int32 pcount = vm->paramCount(); Item *elem1, *elem2; CoreObject *selfobj = vm->self().isNil() ? NULL : vm->self().asObject(); MTRand &rng = selfobj ? ((RandomCarrier*)selfobj->getUserData())->getRNG() : vm->getRNG(); switch( pcount ) { case 0: vm->retval( (numeric) rng.rand() ); break; case 1: elem1 = vm->param(0); if ( elem1->isOrdinal() ) { int64 num = elem1->forceInteger(); if ( num < 0 ) vm->retval( -((int64) rng.randInt64( (-num) & ~(UI64LIT(1) << 63) )) ); // mask out sign bit and make result negative else if ( num == 0 ) vm->retval( 0 ); else vm->retval( (int64) rng.randInt64( num & ~(UI64LIT(1) << 63) ) ); // mask out sign bit, result always positive } else throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ).extra( "[N],[N]" ) ); break; case 2: elem1 = vm->param(0); elem2 = vm->param(1); if ( elem1->isOrdinal() && elem2->isOrdinal() ) { int64 num1 = elem1->forceInteger(); int64 num2 = elem2->forceInteger(); if ( num1 == num2 ) vm->retval( num1 ); else if ( num2 < num1 ) { int64 temp = num2; num2 = num1; num1 = temp; } num2 ++; vm->retval( (int64) (num1 + rng.randInt64(num2 - num1 - 1)) ); } else vm->retval( *vm->param( rng.randInt(1) ) ); break; default: vm->retval( *vm->param( rng.randInt(pcount - 1) ) ); } } /*# @function randomChoice @brief Selects one of the arguments at random and returns it. @param ... At least two items of any kind. @return One of the parameters, picked at random. This function works as @a random when it receives more than two parameters, but its usage is not ambiguous in case there are two items from which to choice. The function raises an error if less than two parameters are passed. */ FALCON_FUNC flc_randomChoice( ::Falcon::VMachine *vm ) { int32 pcount = vm->paramCount(); switch( pcount ) { case 0: case 1: throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ).extra( "X,X,..." ) ); break; default: { CoreObject *selfobj = vm->self().isNil() ? NULL : vm->self().asObject(); MTRand &rng = selfobj ? ((RandomCarrier*)selfobj->getUserData())->getRNG() : vm->getRNG(); vm->retval( *vm->param( rng.randInt(pcount - 1) ) ); } } } /*# @function randomPick @brief Grabs repeatedly random elements from an array. @param series An array containing one or more items. @return One of the items in the array. @raise ParamError if the @b series is empty. This function choices one of the items contained in the @b series array at random. If the array is empty, a ParamError error is raised. */ FALCON_FUNC flc_randomPick ( ::Falcon::VMachine *vm ) { Item *series = vm->param(0); if ( series == 0 || ! series->isArray() || series->asArray()->length() == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "A" ) ); } CoreObject *selfobj = vm->self().isNil() ? NULL : vm->self().asObject(); MTRand &rng = selfobj ? ((RandomCarrier*)selfobj->getUserData())->getRNG() : vm->getRNG(); CoreArray &source = *series->asArray(); vm->retval( source[ rng.randInt(source.length() - 1) ] ); } /*# @function randomWalk @brief Performs a random walk in an array. @param series An array containing one or more items. @optparam size Desire size of the walk. @return An array built from randomly picked items. This function picks one or more elements from the given array, and stores them in a new array without removing them from the old one. Elements can be picked up repeatedly, and the size of the target array may be larger than the size of the original one. If the requested target size is zero, or if the original array is empty, an empty array is returned. If @b size is not given, 1 is assumed; if it's less than zero, then an the function will create an array of the same size of the @b series array, but the target array can contain multiple copies of the items in @b series, or it may be missing some of them. */ FALCON_FUNC flc_randomWalk ( ::Falcon::VMachine *vm ) { Item *series = vm->param(0); Item *qty = vm->param(1); if ( series == 0 || ! series->isArray() || (qty != 0 && ! qty->isOrdinal()) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra( "A,[N]" ) ); } CoreObject *selfobj = vm->self().isNil() ? NULL : vm->self().asObject(); MTRand &rng = selfobj ? ((RandomCarrier*)selfobj->getUserData())->getRNG() : vm->getRNG(); int32 number = qty == 0 ? 1 : (int32)qty->forceInteger(); if( number < 0 ) number = series->asArray()->length(); CoreArray *array = new CoreArray( number ); CoreArray &source = *series->asArray(); int32 slen = (int32) source.length(); if ( slen > 0 ) { while( number > 0 ) { array->append( source[ rng.randInt(slen - 1) ] ); number--; } } vm->retval( array ); } /*# @function randomGrab @brief Grabs repeatedly random elements from an array. @param series An array from which items will be extracted. @optparam size Count of extracted items. @return An array with some or all of the items grabbed from the original elements. This function extracts a desired amount of items from the elements array, putting them in a new array that will be returned. Items left in the elements array have a fair chance to be selected and removed at every step. If the size parameter is greater or equal than the size of the elements array, the array is eventually emptied and all the items are moved to the new array, actually performing a complete fair shuffling of the original. If @b size is not given, 1 is assumed; if it's zero or less than zero, then all the elements in the @b series array will be taken. This function is suitable to emulate card shuffling or other random extraction events. */ FALCON_FUNC flc_randomGrab ( ::Falcon::VMachine *vm ) { Item *series = vm->param(0); Item *qty = vm->param(1); if ( series == 0 || ! series->isArray() || (qty != 0 && ! qty->isOrdinal()) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra( "A,[N]" ) ); } CoreObject *selfobj = vm->self().isNil() ? NULL : vm->self().asObject(); MTRand &rng = selfobj ? ((RandomCarrier*)selfobj->getUserData())->getRNG() : vm->getRNG(); int32 number = qty == 0 ? 1 : (int32)qty->forceInteger(); if( number < 1 ) number = series->asArray()->length(); CoreArray *array = new CoreArray( number ); CoreArray &source = *series->asArray(); int32 slen = (int32) source.length(); while( number > 0 && slen > 0 ) { uint32 pos = rng.randInt(slen - 1); array->append( source[ pos ] ); source.remove( pos ); slen--; number--; } vm->retval( array ); } /*# @function randomDice @brief Performs a virtual dice set trow. @param dices Number of dices to be thrown. @optparam sides Number of faces in the virtual dices. @return A random value which is the sum of the virtual throws. This function generates a series of successive @b dices throws, each one being integer value in the range [1, @b sides]. If @b sides is not given, 6 is assumed. It would be easy to obtain the same result with simple instructions in Falcon, but this function spares several wasted VM cycles. The @b dices parameter must be greater than zero, and the and @b sides parameter must be greater than one. */ FALCON_FUNC flc_randomDice( ::Falcon::VMachine *vm ) { Item *i_dices = vm->param(0); Item *i_sides = vm->param(1); if ( i_dices == 0 || ! i_dices->isOrdinal() || ( i_sides != 0 && ! i_sides->isOrdinal()) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra( "N,N") ); } CoreObject *selfobj = vm->self().isNil() ? NULL : vm->self().asObject(); MTRand &rng = selfobj ? ((RandomCarrier*)selfobj->getUserData())->getRNG() : vm->getRNG(); int64 dices = i_dices->forceInteger(); int64 sides = i_sides == 0 ? 6 : i_sides->forceInteger(); if( dices < 1 || sides < 2 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ). origin( e_orig_runtime ). extra( ">0,>1" ) ); } int64 result = 0; for( int64 i = 0; i < dices; i ++ ) { result += 1 + rng.randInt64(sides - 1); } vm->retval( result ); } /*# @function randomSeed @brief Seeds the random number generator. @optparam seed An integer number being used as random seed. The random seed should be set once per program, possibly using a number that is likely to vary greatly among different executions. A good seed may be the return of the seconds() function, eventually multiplied by 1000 to make the milliseconds to take part in the seeding. If called without parameters, a number based on the current system timer value will be used. Repeated calls to random(), and calls based on random function as randomChoice, randomPick and so on, will produce the same sequences if randomSeed() is called with the same seed. Using a constant number as random seed may be a good strategy to produce predictable debug sequences. */ FALCON_FUNC flc_randomSeed ( ::Falcon::VMachine *vm ) { Item *num = vm->param( 0 ); uint32 value; if ( num == 0 ) { value = (uint32) (Sys::_seconds() * 1000); } else { if ( ! num->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("N") ); } value = (uint32) num->forceInteger(); } CoreObject *selfobj = vm->self().isNil() ? NULL : vm->self().asObject(); MTRand &rng = selfobj ? ((RandomCarrier*)selfobj->getUserData())->getRNG() : vm->getRNG(); rng.seed( value ); } } } /* end of random.cpp */ engine/core_module/seconds.cpp000066400000000000000000000025711176363201700167750ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: seconds.cpp Time function ------------------------------------------------------------------- Author: $AUTHOR Begin: $DATE ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ #include #include #include namespace Falcon { namespace core { /*# @function seconds @ingroup general_purpose @brief Returns the number of seconds and milliseconds from day, activity or program start. @return The number of seconds and fractions of seconds in a floating point value. Actually, this function returns a floating point number which represents seconds and fraction of seconds elapse since a conventional time. This function is mainly meant to be used to take intervals of time inside the script, with a millisecond precision. */ FALCON_FUNC seconds ( ::Falcon::VMachine *vm ) { vm->retval( Sys::_seconds() ); } /*# @function epoch @ingroup general_purpose @brief Returns the number of seconds since the "epoch" (1 Jan 1970). @return An integer number of seconds. */ FALCON_FUNC epoch ( ::Falcon::VMachine *vm ) { vm->retval( Sys::_epoch() ); } }} /* end of seconds.cpp */ engine/core_module/sequence_ext.cpp000066400000000000000000000327451176363201700200350ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: sequence_ext.cpp Implementation of the RTL Sequence abstract Falcon class. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 08 Aug 2009 11:07:57 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ /** \file Implementation of the RTL Sequence Falcon class. */ #include #include #include #include #include #include "core_module.h" namespace Falcon { namespace core { /*# @class Sequence @brief Base abstract class for VM assisted sequences. This class is meant to provide common methods to VM-assisted (that is, language level) sequence classes. You can't derive directly from this class unless you're writing a native module, but you can derive from script-visible classes children of this one. */ /*# @method comp Sequence @brief Appends elements to this sequence through a filter. @param source A sequence, a range or a callable generating items. @optparam filter A filtering function receiving one item at a time. @return This sequence. This method adds one item at a time to this Sequence. If @b source is a range, (must not be open), all the values generated by the range will be appended, considering range direction and step. If @b source is a sequence (array, dictionary, or any other object providing the sequence interface), all the values in the item will be appended to this Sequence. If @b source is a callable item, it is called repeatedly to generate the sequence. All the items it returns will be appended, until it declares being terminated by returning an oob(0). Continuation objects are also supported. If a @b filter callable is provided, all the items that should be appended are first passed to to it; the item returned by the callable will be used instead of the item provided in @b source. The @b filter may return an out of band 1 to skip the item from @b source, or an out of band 0 to stop the processing altogether. The @b filter callable receives also the forming sequence as the second parameter so that it account for it or manage it dynamically during the filter step. For example, the following comprehension creates a dictionary associating a letter of the alphabet to each element in the source sequence, discarding elements with spaces and terminating when a "" mark is found. The @b filter function uses the second parameter to determine how many items have been added, and return a different element each time. @code dict = [=>].comp( // the source .[ 'bananas' 'skip me' 'apples' 'oranges' '' 'melons' ], // the filter { element, dict => if " " in element: return oob(1) if "" == element: return oob(0) return [ "A" / len(dict), element ] // (1) } ) // (1): "A" / number == chr( ord("A") + number ) @endcode This method actually adds each item in the comprehension to the sequence or sequence-compatible item in self. This means that comprehension needs not to be performed on a new, empty sequence; it may be also used to integrate more data in already existing sequences. @see Array.comp @see Dictionary.comp @see Object.comp */ FALCON_FUNC Sequence_comp ( ::Falcon::VMachine *vm ) { if ( vm->param(0) == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "R|A|C|Sequence, [C]" ) ); } // Save the parameters as the stack may change greatly. Sequence* seq = vm->self().asObject()->getSequence(); fassert( seq != 0 ); Item i_gen = *vm->param(0); Item i_check = vm->param(1) == 0 ? Item() : *vm->param(1); seq->comprehension_start( vm, vm->self(), i_check ); vm->pushParam( i_gen ); } /*# @method mcomp Sequence @brief Appends elements to this sequence from multiple sources. @param ... One or more sequences, ranges or callables generating items. @return This sequence. This method works as @a Sequence.comp but it's possible to specify more items and sequences. Each element in the result set is an array of items in which each element extracted from a source or returned by a generator is combined with all the others. For example, the following operation: @code [].mcomp( [1,2], [3,4] ) @endcode results in: @code [ [1,3], [1,4], [2,3], [2,4] ] @endcode Generators are called repeatedly until they exhaust all the items they can generate, in the same order as they are declared in the @b mcomp call. For example: @code [].mcomp( alphagen, betagen ) @endcode will first call alphagen until it returns an oob(0), and then call betagen repeatedly. @note Calls in this context are not atomic. Suspension, sleep, I/O, and continuations are allowed and supported. After all the generators are called, the collected data is mixed with static data coming from other sources. For example: @code function make_gen( count, name ) i = 0 return {=> if i == count: return oob(0) > name, ": ", i return i++ } end > [].mcomp( ["a","b"], make_gen(2, "first"), make_gen(2, "second") ).describe() @endcode will generate the following output: @code first: 0 first: 1 second: 0 second: 1 [ [ "a", 0, 0], [ "a", 0, 1], [ "a", 1, 0], [ "a", 1, 1], [ "b", 0, 0], [ "b", 0, 1], [ "b", 1, 0], [ "b", 1, 1]] @endcode @note The @a Sequence.mfcomp provides a more flexible approach. @see Array.mcomp @see Dictionary.mcomp @see Object.mcomp */ FALCON_FUNC Sequence_mcomp ( ::Falcon::VMachine *vm ) { // Save the parameters as the stack may change greatly. Sequence* seq = vm->self().asObject()->getSequence(); fassert( seq != 0 ); StackFrame* current = vm->currentFrame(); seq->comprehension_start( vm, vm->self(), Item() ); for( uint32 i = 0; i < current->m_param_count; ++i ) { vm->pushParam( current->m_params[i] ); } } /*# @method mfcomp Sequence @brief Appends elements to this sequence from multiple sources through a filter. @param filter A filtering function receiving one item at a time. @param ... One or more sequences, ranges or callables generating items. @return This sequence. This function works exactly as @a Sequence.mcomp, with the difference that the elements generated are passed to a filter function for final delivery to the target sequence. @note The @b filter parameter is optional; if @b nil is passed to it, this method works exactly as @a Sequence.mcomp. For example, the math set operation @code { x*y for x in 1,2,3 and y in 4,5,6 } @endcode can be written using mfcomp like the following: @code [].mfcomp( {x, y => x*y}, .[1 2 3], .[4 5 6] ) @endcode which results in @code [ 4, 5, 6, 8, 10, 12, 12, 15, 18] @endcode The as for @a Sequence.comp, filter receives an extra parameter which is the sequence itself. For example, the following code: @code > [].mfcomp( {x, y, seq => printl( "Seq is now long: " + seq.len() ) return [seq.len(), x*y] }, .[1 2 3], .[4 5 6] ).describe() @endcode generates this output: @code Seq is now long: 0 Seq is now long: 1 Seq is now long: 2 Seq is now long: 3 Seq is now long: 4 Seq is now long: 5 Seq is now long: 6 Seq is now long: 7 Seq is now long: 8 [ [ 0, 4], [ 1, 5], [ 2, 6], [ 3, 8], [ 4, 10], [ 5, 12], [ 6, 12], [ 7, 15], [ 8, 18]] @endcode Notice that it is possible to modify the sequence inside the filter, in case it's needed. The filter may return an oob(1) to skip the value, and an oob(0) to terminate the operation. For example, the following code s @note The call Sequence.mfcomp( filter, seq ) is equivalent to Sequence.comp( seq, filter ). @see Array.mfcomp @see Dictionary.mfcomp @see Object.mfcomp */ FALCON_FUNC Sequence_mfcomp ( ::Falcon::VMachine *vm ) { if ( vm->param(0) == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "C, ..." ) ); } // Save the parameters as the stack may change greatly. Sequence* seq = vm->self().asObject()->getSequence(); fassert( seq != 0 ); Item i_check = *vm->param(0); StackFrame* current = vm->currentFrame(); seq->comprehension_start( vm, vm->self(), i_check ); for( uint32 i = 1; i < current->m_param_count; ++i ) { vm->pushParam( current->m_params[i] ); } } /*# @method front Sequence @brief Returns the first item in the Sequence. @raise AccessError if the Sequence is empty. @return The first item in the Sequence. This method overloads the BOM method @b front. If the Sequence is not empty, it returns the first element. */ FALCON_FUNC Sequence_front ( ::Falcon::VMachine *vm ) { Sequence* seq = vm->self().asObject()->getSequence(); fassert( seq != 0 ); if( seq->empty() ) { throw new AccessError( ErrorParam( e_arracc, __LINE__ ). origin( e_orig_runtime ) ); return; } vm->retval( seq->front() ); } /*# @method back Sequence @brief Returns the last item in the Sequence. @raise AccessError if the Sequence is empty. @return The last item in the Sequence. This method overloads the BOM method @b back. If the Sequence is not empty, it returns the last element. */ FALCON_FUNC Sequence_back ( ::Falcon::VMachine *vm ) { Sequence* seq = vm->self().asObject()->getSequence(); fassert( seq != 0 ); if( seq->empty() ) { throw new AccessError( ErrorParam( e_arracc, __LINE__ ). origin( e_orig_runtime ) ); return; } vm->retval( seq->back() ); } /*# @method first Sequence @brief Returns an iterator to the first element of the Sequence. @return An iterator. Returns an iterator to the first element of the Sequence. If the Sequence is empty, an invalid iterator will be returned, but an insertion on that iterator will succeed and append an item to the Sequence. */ FALCON_FUNC Sequence_first ( ::Falcon::VMachine *vm ) { Sequence* seq = vm->self().asObject()->getSequence(); fassert( seq != 0 ); Item *i_iclass = vm->findWKI( "Iterator" ); fassert( i_iclass != 0 ); CoreObject *iobj = i_iclass->asClass()->createInstance(); // we need it separated to activate the FalconData bit iobj->setUserData( new Iterator( seq ) ); vm->retval( iobj ); } /*# @method last Sequence @brief Returns an iterator to the last element of the Sequence. @return An iterator. Returns an iterator to the last element of the Sequence. If the Sequence is empty, an invalid iterator will be returned, but an insertion on that iterator will succeed and append an item to the Sequence. */ FALCON_FUNC Sequence_last ( ::Falcon::VMachine *vm ) { Sequence* seq = vm->self().asObject()->getSequence(); fassert( seq != 0 ); Item *i_iclass = vm->findWKI( "Iterator" ); fassert( i_iclass != 0 ); CoreObject *iobj = i_iclass->asClass()->createInstance(); // we need it separated to activate the FalconData bit iobj->setUserData( new Iterator( seq, true ) ); vm->retval( iobj ); } /*# @method empty Sequence @brief Checks if the Sequence is empty or not. @return True if the Sequence is empty, false if contains some elements. */ FALCON_FUNC Sequence_empty( ::Falcon::VMachine *vm ) { Sequence* seq = vm->self().asObject()->getSequence(); fassert( seq != 0 ); vm->regA().setBoolean( seq->empty() ); } /*# @method clear Sequence @brief Removes all the items from the Sequence. */ FALCON_FUNC Sequence_clear( ::Falcon::VMachine *vm ) { Sequence* seq = vm->self().asObject()->getSequence(); fassert( seq != 0 ); seq->clear(); } /*# @method prepend Sequence @brief Adds an item in front of the sequence @param item The item to be added. If the sequence is sorted, the position at which the @b item is inserted is determined by the internal ordering; otherwise the @b item is prepended in front of the sequence. */ FALCON_FUNC Sequence_prepend ( ::Falcon::VMachine *vm ) { Item *data = vm->param( 0 ); if( data == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra("X") ); return; } Sequence* seq = vm->self().asObject()->getSequence(); fassert( seq != 0 ); seq->prepend( *data ); } /*# @method append Sequence @brief Adds an item at the end of the sequence. @param item The item to be added. If the sequence is sorted, the position at which the @b item is inserted is determined by the internal ordering; otherwise the @b item is appended at the end of the sequence. */ FALCON_FUNC Sequence_append ( ::Falcon::VMachine *vm ) { Item *data = vm->param( 0 ); if( data == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra("X") ); return; } Sequence* seq = vm->self().asObject()->getSequence(); fassert( seq != 0 ); seq->append( *data ); } } } /* end of sequence_ext.cpp */ engine/core_module/serialize.cpp000066400000000000000000000150221176363201700173210ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: serialize.cpp Serialization support ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab nov 13 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Serialization support */ #include #include #include #include #include #include #include #include /*# @beginmodule core */ /*# @funset core_serialization Serialization functions @brief Function used to store items persistently. Serialization functions allow to flatten a Falcon item, or a sequence of items, on a stream for later retrieval, storage or transmission. At the moment, custom serialization is not supported. This means that all the basic items, as strings and numbers, plus arrays and dictionaries are supported. Objects are partially supported: when they are fully derived from Falcon classes, or declared as "object" by the scripts, the serialization and de-serialization are successful. However, there is no mechanism to support creation of user-specific data, as the "load" that objects can carry internally in behalf of embedding applications. Nevertheless, if there is the need, objects may be serialized/deserialized with the provided functions, and after the de-serialization step, a custom mechanism may be used to re-create application specific data. However, it is necessary that the deserializing application has access to the same classes that were used to create the serialized object. Notice that also functions are correctly serialized and deserialized. Also, static block is not re-executed in case the function is re-entered after a de-serialization. @beginset core_serialization */ namespace Falcon { namespace core { /*# @method serialize BOM @brief Serialize the item on a stream for persistent storage. @param stream The stream on which to perform serialization. @raise IoError on stream errors. The item is stored on the stream so that a deserialize() call on the same position in the stream where serialization took place will create an exact copy of the serialized item. The application must ensure that the item does not contains circular references, or the serialization will enter an endless loop. In case the underlying stream write causes an i/o failure, an error is raised. */ /*# @function serialize @brief Serializes an item on a stream. @param item The item to be serialized. @param stream An instance of the Stream (or derived) class. @raise IoError on underlying stream error. The item is stored on the stream so that a deserialize() call on the same position in the stream where serialization took place will create an exact copy of the serialized item. The application must ensure that the item does not contains circular references, or the serialization will enter an endless loop. In case the underlying stream write causes an i/o failure, an error is raised. The BOM method @a BOM.serialize is available for all the Falcon items, and is equivalent to call this function. */ FALCON_FUNC mth_serialize ( ::Falcon::VMachine *vm ) { Item *fileId; Item *source; if ( vm->self().isMethodic() ) { source = &vm->self(); fileId = vm->param(0); } else { source = vm->param(0); fileId = vm->param(1); } if( fileId == 0 || source == 0 || ! fileId->isObject() || ! fileId->asObjectSafe()->derivedFrom( "Stream" ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ). extra( vm->self().isMethodic() ? "Stream" : "X,Stream" ) ); } Stream *file = (Stream *) fileId->asObject()->getUserData(); vm->idle(); Item::e_sercode sc = source->serialize( file ); vm->unidle(); switch( sc ) { case Item::sc_ok: vm->retval( 1 ); break; case Item::sc_ferror: throw new IoError( ErrorParam( e_modio, __LINE__ ).origin( e_orig_runtime ) ); default: vm->retnil(); // VM may already have raised an error. //TODO: repeat error. } } /*# @function deserialize @brief Deserialize an item from a stream. @param stream An instance of the Stream (or derived) class. @raise IoError on underlying stream error. @raise GenericError If the data is correctly de-serialized, but it refers to external symbols non defined by this script. @raise ParseError if the format of the input data is invalid. The returned item is a new copy of the item that has been previously serialized on the given stream. After the read, the stream pointer is left ready for a new read, so that items that are serialized in sequence may be deserialized in the same order. If the underlying stream read causes an i/o failure, an error is raised. Also, an error is raised if the function cannot deserialize from the stream because the data format is invalid. */ FALCON_FUNC deserialize ( ::Falcon::VMachine *vm ) { Item *fileId = vm->param(0); if( fileId == 0 || ! fileId->isObject() || ! fileId->isObject() || ! fileId->asObjectSafe()->derivedFrom( "Stream" ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ). extra( "O:Stream" ) ); return; } // deserialize rises it's error if it belives it should. Stream *file = (Stream *) fileId->asObject()->getUserData(); Item::e_sercode sc = vm->regA().deserialize( file, vm ); switch( sc ) { case Item::sc_ok: return; // ok, we've nothing to do case Item::sc_eof: throw new IoError( ErrorParam( e_deser_eof, __LINE__ ).origin( e_orig_runtime ) ); case Item::sc_ferror: throw new IoError( ErrorParam( e_io_error, __LINE__ ).origin( e_orig_runtime ) ); case Item::sc_misssym: throw new GenericError( ErrorParam( e_undef_sym, __LINE__ ).origin( e_orig_runtime ) ); case Item::sc_missclass: throw new GenericError( ErrorParam( e_undef_sym, __LINE__ ).origin( e_orig_runtime ) ); case Item::sc_invformat: throw new ParseError( ErrorParam( e_invformat, __LINE__ ).origin( e_orig_runtime ) ); case Item::sc_vmerror: default: vm->retnil(); // VM may already have raised an error. //TODO: repeat error. } } }} /* end of serialize.cpp */ engine/core_module/set_ext.cpp000066400000000000000000000120571176363201700170120ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: set_ext.cpp Implementation of the RTL Set Falcon class. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 08 Aug 2009 14:55:46 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ /** \file Implementation of the RTL List Falcon class. */ #include #include #include #include #include #include "core_module.h" #include namespace Falcon { namespace core { /*# @class Set @from Sequence ... @brief Storage for uniquely defined items (and ordering criterion). @param ... An arbitrary list of parameters. The Set class implements a binary tree, uniquely and orderly storing a set of generic Falcon items. Instances of the Set class can be used as parameters for the Iterator constructor, and an iterator can be generated for them using first() and last() BOM methods. Also, instances of the Set class can be used as any other sequence in for/in loops. Items in the set are ordered using the Falcon standard comparison algorithm; if they are instances of classes (or blessed dictionaries) implementing the compare() method, that method is used as a comparison criterion. If the set constructor is given some parameters, it will be initially filled with those items; if some of them is duplicated, only one item will be then found in the set. */ FALCON_FUNC Set_init ( ::Falcon::VMachine *vm ) { ItemSet *set = new ItemSet; int32 pc = vm->paramCount(); for( int32 p = 0; p < pc; p++ ) { set->insert( *vm->param(p) ); } set->owner( vm->self().asObject() ); vm->self().asObject()->setUserData( set ); } /*# @method insert Set @brief Adds an item to the set. @param item The item to be added. If an item considered equal to the added one exists, the previously set item is destroyed. */ FALCON_FUNC Set_insert ( ::Falcon::VMachine *vm ) { Item *data = vm->param( 0 ); if( data == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra("X") ); return; } ItemSet *set = dyncast( vm->self().asObject()->getFalconData() ); set->insert( *data ); } /*# @method remove Set @brief Removes an item from a set. @param item The item to be removed. @return True if the item was removed, false if it wasn't found. */ FALCON_FUNC Set_remove ( ::Falcon::VMachine *vm ) { Item *data = vm->param( 0 ); if( data == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra("X") ); return; } ItemSet *set = dyncast( vm->self().asObject()->getFalconData() ); ItemSetElement* elem = set->find( *data ); if( elem == 0 ) { vm->regA().setBoolean( false ); } else { set->erase( elem ); vm->regA().setBoolean( true ); } } /*# @method contains Set @brief Checks if a certain item is in the set. @param item The item to be found. @return True if the item is in the set, false otherwise. */ FALCON_FUNC Set_contains ( ::Falcon::VMachine *vm ) { Item *data = vm->param( 0 ); if( data == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra("X") ); return; } ItemSet *set = dyncast( vm->self().asObject()->getFalconData() ); ItemSetElement* elem = set->find( *data ); vm->regA().setBoolean( elem != 0 ); } /*# @method find Set @brief Checks if a certain item is in the set. @param item The item to be found. @return True if the item is in the set, false otherwise. */ FALCON_FUNC Set_find ( ::Falcon::VMachine *vm ) { Item *data = vm->param( 0 ); if( data == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra("X") ); return; } ItemSet *set = dyncast( vm->self().asObject()->getFalconData() ); ItemSetElement* elem = set->find( *data ); if( elem != 0 ) { Iterator* iter = new Iterator(set); set->getIteratorAt( *iter, elem ); Item *i_iclass = vm->findWKI( "Iterator" ); fassert( i_iclass != 0 && i_iclass->isClass() ); CoreObject* citer = i_iclass->asClass()->createInstance(); // we need this to declare the iterator as a falcon data citer->setUserData( iter ); vm->retval( citer ); } else vm->retnil(); } /*# @method len Set @brief Returns the number of items stored in this set. @return Count of items in the set. */ FALCON_FUNC Set_len( ::Falcon::VMachine *vm ) { ItemSet *set = dyncast( vm->self().asObject()->getFalconData() ); vm->retval( (int64) set->size() ); } } } /* end of set_ext.cpp */ engine/core_module/state_ext.cpp000066400000000000000000000127441176363201700173420ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: attrib_ext.cpp Facilities handling attributes. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 15 Nov 2009 11:17:19 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ #include #include #include #include "core_module.h" namespace Falcon { namespace core { /*# @method setState Object @param nstate The new state into which the object is moved. @brief Change the current active state of an object. @return Return value of the __leave -> __enter sequence, if any, or nil @raise CodeError if the state is not part of the object state. This method changes the state of the object, applying a new set of function described in the state section. */ FALCON_FUNC Object_setState ( ::Falcon::VMachine *vm ) { Item* nstate = vm->param(0); if ( nstate == 0 || ! nstate->isString() ) throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin(e_orig_runtime) .extra( "S") ); // will raise in case of problems. vm->self().asObject()->setState( *nstate->asString(), vm ); } /*# @method getState Object @brief Return the current state of an object. @return A string representing the current state of an object, or nil if the object is stateless. This function returns the current state in which an object is operating. */ FALCON_FUNC Object_getState( ::Falcon::VMachine *vm ) { CoreObject* obj = vm->self().asObject(); if( obj->hasState() ) vm->retval( new CoreString( obj->state() ) ); else vm->retnil(); } /*# @method apply Object @brief Applies the values in a dictionary to the corresponding properties. @param dict A "stamp" dictionary, or a sequence of named values. @raise AccessError if some property listed in the dictionary is not defined. @return This same object. This method applies a "stamp" on this object. The idea is that of copying the contents of all the items in the dictionary into the properties of this object. Dictionaries are more flexible than objects, at times they are preferred for i.e. network operations and key/value databases. With this method, you can transfer data from a dictionary in an object with a single VM step, paying just the cost of the copy; in other words, sparing the VM operations needed for looping over the dictionary and searching dynamically the required properties. @note Non-string keys in @b dict are simply skipped. @see Object.retrieve */ FALCON_FUNC Object_apply( ::Falcon::VMachine *vm ) { Item* i_dict = vm->param( 0 ); if ( i_dict == 0 || ! (i_dict->isDict() || i_dict->isArray()) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin(e_orig_runtime) .extra( "D|A") ); } CoreObject* self = vm->self().asObject(); if ( i_dict->isDict() ) { self->apply( i_dict->asDict()->items(), true ); vm->retval( self ); } else { ItemArray& arr = i_dict->asArray()->items(); for( uint32 i = 0; i < arr.length() ; ++i ) { const Item& item = arr[i]; if ( item.isFutureBind() ) { if( ! self->setProperty( *item.asLBind(), item.asFutureBind() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin(e_orig_runtime) .extra( "Missing property: " + *item.asLBind() ) ); } } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin(e_orig_runtime) .extra( "D|A") ); } } } } /*# @method retrieve Object @brief Gets the values stored in the properties of this object. @optparam dict A "stamp" dictionary. @raise AccessError if some property listed in the dictionary is not defined. @return A dictionary containing the contents of each property (stored as a key in the dictionary). This method takes all the data values stored in the properties of this object (ignoring methods), and places them in a dictionary. Property names are used as keys under which to store flat copies of the property values. If a @b dict parameter is passed, this method will take only the properties stored as keys, and eventually raise an AccessError if some of them are not found. Otherwise, a new dictionary will be filled with all the properties in this object. @note In case of repeated activity, the same dictionary can be used to fetch new values to spare memory and CPU. @see Object.apply */ FALCON_FUNC Object_retrieve( ::Falcon::VMachine *vm ) { Item* i_dict = vm->param( 0 ); CoreDict* dict; bool bFillDict; if( i_dict == 0 ) { bFillDict = true; dict = new CoreDict( new LinearDict ); } else { if ( ! i_dict->isDict() ) { throw new AccessError( ErrorParam( e_inv_params, __LINE__ ) .origin(e_orig_runtime) .extra( "[D]" ) ); } dict = i_dict->asDict(); bFillDict = false; } CoreObject* self = vm->self().asObject(); self->retrieve( dict->items(), true, bFillDict, true ); vm->retval( dict ); } } } engine/core_module/string_ext.cpp000066400000000000000000002016561176363201700175320ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: string.cpp Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven nov 5 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ /** \file Short description */ #include #include #include #include #include #include #include /*# @funset core_string_functions String functions @brief Functions manipulating strings @beginset core_string_functions */ namespace Falcon { namespace core { static void process_strFrontBackParams( VMachine *vm, String* &str, bool &bNumeric, bool &bRemove, int32 &len ) { Item *i_count; if ( vm->self().isMethodic() ) { str = vm->self().asString(); i_count = vm->param(0); bRemove = vm->param(1) != 0 && vm->param(1)->isTrue(); bNumeric = vm->param(2) != 0 && vm->param(2)->isTrue(); } else { Item *i_str = vm->param(0); if( i_str == 0 || ! i_str->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).extra( "S,[N,B,B]" ) ); } str = i_str->asString(); i_count = vm->param(1); bRemove = vm->param(2) != 0 && vm->param(2)->isTrue(); bNumeric = vm->param(3) != 0 && vm->param(3)->isTrue(); } if ( i_count == 0 || i_count->isNil() ) { len = 1; } else if ( ! i_count->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).extra( "S,[N,B,B]" ) ); } else { len = (int32) i_count->forceInteger(); } } /*# @method charSize String @brief Returns or changes the size in bytes in this string. @optparam bpc New value for bytes per character (1, 2 or 4). @return This string if @b bpc is given, or the current character size value if not given. */ FALCON_FUNC String_charSize( VMachine *vm ) { Item *i_bpc = vm->param(0); String* str = vm->self().asString(); if ( i_bpc == 0 ) { // no parameters -- just read us. vm->retval( (int64) str->manipulator()->charSize() ); return; } else if( ! i_bpc->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "[N]" ) ); } uint32 bpc = (uint32) i_bpc->forceInteger(); if ( ! str->setCharSize( bpc ) ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) ); } vm->retval( str ); } /*# @method front String @brief Returns the first character in a string. @optparam count Number of characters to be returned (defaults to 1). @optparam numeric If true, returns a character value instead of a string. @optparam remove If true, also remove the character. @return The first element or nil if the string is empty. This method returns a string containing one character from the beginning of the string, or eventually more characters in case a number > 1 is specified in @b count. If @b remove is true, then the character is removed and the string is shrunk. @see strFront If @b numeric is true, the UNICODE value of the string character will be returned, otherwise the caller will receive a string containing the desired character. In this case, @b count parameter will be ignored and only one UNICODE value will be returned. */ /*# @function strFront @brief Returns the first character(s) in a string. @param str The string on which to operate. @optparam count Number of characters to be taken (defaults to 1). @optparam remove If true, also remove the character. @optparam numeric If true, returns a character value instead of a string. @return The first element or nil if the string is empty. This function returns a string containing one character from the beginning of @b str, or eventually more characters in case a number > 1 is specified in @b count. If @b remove is true, then the character is removed and the string is shrunk. @see String.front If @b numeric is true, the UNICODE value of the string character will be returned, otherwise the caller will receive a string containing the desired character. In this case, @b count parameter will be ignored and only one UNICODE value will be returned. */ FALCON_FUNC mth_strFront( VMachine *vm ) { String *str; bool bNumeric; bool bRemove; int32 len; process_strFrontBackParams( vm, str, bNumeric, bRemove, len ); if ( bNumeric ) { if ( str->size() == 0 ) { vm->retnil(); } else { vm->retval( (int64) str->getCharAt( 0 ) ); if( bRemove ) str->remove( 0, 1 ); } } else { if ( len <= 0 ) { vm->retval( new CoreString( "" ) ); } else if ( len > (int32) str->length() ) { vm->retval( new CoreString( *str ) ); } else { vm->retval( new CoreString( *str, 0, len ) ); } if( bRemove ) str->remove(0, len ); } } /*# @method back String @brief Returns the last character in a string. @optparam count Number of characters to be taken (defaults to 1). @optparam numeric If true, returns a character value instead of a string. @optparam remove If true, remove also the character. @return The last element or nil if the string is empty. This function returns a string containing one character from the end of the string, or eventually more characters in case a number > 1 is specified in @b count. If @b remove is true, then the character is removed and the string is shrunk. @see strFront If @b numeric is true, the UNICODE value of the string character will be returned, otherwise the caller will receive a string containing the desired character. In this case, @b count parameter will be ignored and only one UNICODE value will be returned. */ /*# @function strBack @brief Returns the last character(s) in a string. @param str The string on which to operate. @optparam count Number of characters to be taken (defaults to 1). @optparam remove If true, also remove the character. @optparam numeric If true, returns a character value instead of a string. @return The last element or nil if the string is empty. This function returns a string containing one character from the end of @b str, or eventually more characters in case a number > 1 is specified in @b count. If @b remove is true, then the characters are removed and the string is shrunk. @see String.back If @b numeric is true, the UNICODE value of the string character will be returned, otherwise the caller will receive a string containing the desired character. In this case, @b count parameter will be ignored and only one UNICODE value will be returned. */ FALCON_FUNC mth_strBack( VMachine *vm ) { String *str; bool bNumeric; bool bRemove; int32 len; process_strFrontBackParams( vm, str, bNumeric, bRemove, len ); int32 strLen = str->length(); if ( bNumeric ) { if ( str->size() == 0 ) { vm->retnil(); } else { vm->retval( (int64) str->getCharAt( strLen-1 ) ); if( bRemove ) str->remove( strLen-1, 1 ); } } else { if ( len <= 0 ) { vm->retval( new CoreString( "" ) ); } else if ( len >= strLen ) { vm->retval( new CoreString( *str ) ); } else { vm->retval( new CoreString( *str, strLen-len ) ); } if( bRemove ) str->remove( strLen-len, len ); } } /** @method first String @brief Returns an iterator to the head of this string. @return An iterator. */ /*FALCON_FUNC String_first( VMachine *vm ) { Item *itclass = vm->findWKI( "Iterator" ); fassert( itclass != 0 ); CoreObject *iterator = itclass->asClass()->createInstance(); iterator->setProperty( "_pos", Item( 0 ) ); iterator->setProperty( "_origin", vm->self() ); vm->retval( iterator ); }*/ /** @method last String @brief Returns an iterator to the tail of this string. @return An iterator. */ /*FALCON_FUNC String_last( VMachine *vm ) { Item *itclass = vm->findWKI( "Iterator" ); fassert( itclass != 0 ); CoreObject *iterator = itclass->asClass()->createInstance(); String *orig = vm->self().asString(); iterator->setProperty( "_pos", Item( orig->size() == 0 ? 0 : (int64) orig->length() - 1 ) ); iterator->setProperty( "_origin", vm->self() ); vm->retval( iterator ); }*/ /*# @function strSplitTrimmed @brief Subdivides a string in an array of substrings given a token substring. @param string The string that must be split. @param token The token by which the string should be split. @optparam count Optional maximum split count. @return An array of strings containing the split string. This function returns an array of strings extracted from the given parameter. The array is filled with strings extracted from the first parameter, by dividing it based on the occurrences of the token substring. A count parameter may be provided to limit the splitting, so to take into consideration only the first relevant tokens. Adjacent matching tokens will be ignored. If no matches are possible, the returned array contains at worst a single element containing a copy of the whole string passed as a parameter. Contrarily to @a strSplit, this function will "eat up" adjacent tokens. While @a strSplit is more adequate to parse field-oriented strings (as i.e. colon separated fields in configuration files) this function is best employed in word extraction. @note this function is equivalent to the FBOM method String.splittr @note See @a Tokenizer for a more adequate function to scan extensively wide strings. */ /*# @method splittr String @brief Subdivides a string in an array of substrings given a token substring. @param token The token by which the string should be split. @optparam count Optional maximum split count. @return An array of strings containing the split string. @see strSplitTrimmed */ FALCON_FUNC mth_strSplitTrimmed ( ::Falcon::VMachine *vm ) { Item *target; Item *splitstr; Item *count; // Parameter checking; if( vm->self().isMethodic() ) { target = &vm->self(); splitstr = vm->param(0); count = vm->param(1); } else { target = vm->param(0); splitstr = vm->param(1); count = vm->param(2); } uint32 limit; if ( target == 0 || ! target->isString() || (splitstr != 0 && ! (splitstr->isString() || splitstr->isNil())) || ( count != 0 && ! count->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "S, [N]" : "S, S, [N]" ) ); } limit = count == 0 ? 0xffffffff: (int32) count->forceInteger(); // Parameter extraction. String *tg_str = target->asString(); uint32 tg_len = target->asString()->length(); // split in chars? if( splitstr == 0 || splitstr->isNil() || splitstr->asString()->size() == 0) { // split the string in an array. if( limit > tg_len ) limit = tg_len; CoreArray* ca = new CoreArray( limit ); for( uint32 i = 0; i < limit - 1; ++i ) { CoreString* elem = new CoreString(1); elem->append( tg_str->getCharAt(i) ); ca->append( elem ); } // add remains if there are any if(limit <= tg_len) ca->append(tg_str->subString(limit - 1)); vm->retval( ca ); return; } String *sp_str = splitstr->asString(); uint32 sp_len = splitstr->asString()->length(); // return item. CoreArray *retarr = new CoreArray; // if the split string is empty, return empty string if ( sp_len == 0 ) { retarr->append( new CoreString() ); vm->retval( retarr ); return; } // if the token is wider than the string, just return the string if ( tg_len < sp_len ) { retarr->append( new CoreString( *tg_str ) ); vm->retval( retarr ); return; } uint32 pos = 0; uint32 last_pos = 0; bool lastIsEmpty = false; // scan the string while( limit > 1 && pos <= tg_len - sp_len ) { uint32 sp_pos = 0; // skip matching pattern- while ( tg_str->getCharAt( pos ) == sp_str->getCharAt( sp_pos ) && sp_pos < sp_len && pos <= tg_len - sp_len ) { sp_pos++; pos++; } // a match? if ( sp_pos == sp_len ) { // put the item in the array. uint32 splitend = pos - sp_len; retarr->append( new CoreString( String( *tg_str, last_pos, splitend ) ) ); lastIsEmpty = (last_pos >= splitend); last_pos = pos; limit--; // skip matching pattern while( sp_pos == sp_len && pos <= tg_len - sp_len ) { sp_pos = 0; last_pos = pos; while ( tg_str->getCharAt( pos ) == sp_str->getCharAt( sp_pos ) && sp_pos < sp_len && pos <= tg_len - sp_len ) { sp_pos++; pos++; } } pos = last_pos; } else pos++; } // Residual element? // -- but only if we didn't already put a "" in the array if ( limit >= 1 && ! lastIsEmpty ) { retarr->append( new CoreString( String( *tg_str, last_pos, (uint32) tg_len ) ) ); } vm->retval( retarr ); } /*# @function strSplit @brief Subdivides a string in an array of substrings given a token substring. @param string The string that must be split. @optparam token The token by which the string should be split. @optparam count Optional maximum split count. @return An array of strings containing the split string. This function returns an array of strings extracted from the given parameter. The array is filled with strings extracted from the first parameter, by dividing it based on the occurrences of the token substring. A count parameter may be provided to limit the splitting, so to take into consideration only the first relevant tokens. Adjacent matching tokens will cause the returned array to contains empty strings. If no matches are possible, the returned array contains at worst a single element containing a copy of the whole string passed as a parameter. For example, the following may be useful to parse a INI file where keys are separated from values by "=" signs: @code key, value = strSplit( string, "=", 2 ) @endcode This code would return an array of 2 items at maximum; if no "=" signs are found in string, the above code would throw an error because of unpacking size, a thing that may be desirable in a parsing code. If there are more than one "=" in the string, only the first starting from left is considered, while the others are returned in the second item, unparsed. If the @b token is empty or not given, the string is returned as a sequence of 1-character strings in an array. @note This function is equivalent to the fbom method @a String.split. The above example can be rewritten as: @code key, value = string.split( "=", 2 ) @endcode */ /*# @method split String @brief Subdivides a string in an array of substrings given a token substring. @optparam token The token by which the string should be split. @optparam count Optional maximum split count. @return An array of strings containing the split string. @see strSplit */ FALCON_FUNC mth_strSplit ( ::Falcon::VMachine *vm ) { Item *target; Item *splitstr; Item *count; // Parameter checking; if( vm->self().isMethodic() ) { target = &vm->self(); splitstr = vm->param(0); count = vm->param(1); } else { target = vm->param(0); splitstr = vm->param(1); count = vm->param(2); } if ( (target == 0 || ! target->isString()) || (splitstr != 0 && ! (splitstr->isString() || splitstr->isNil())) || ( count != 0 && ! count->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "S, [N]" : "S, S, [N]" ) ); } // Parameter extraction. String *tg_str = target->asString(); uint32 tg_len = target->asString()->length(); uint32 limit = count == 0 ? 0xffffffff: (int32) count->forceInteger(); // split in chars? if( splitstr == 0 || splitstr->isNil() || splitstr->asString()->size() == 0) { // split the string in an array. if( limit > tg_len ) limit = tg_len; CoreArray* ca = new CoreArray( limit ); for( uint32 i = 0; i < limit - 1; ++i ) { CoreString* elem = new CoreString(1); elem->append( tg_str->getCharAt(i) ); ca->append( elem ); } // add remains if there are any if(limit <= tg_len) ca->append(tg_str->subString(limit - 1)); vm->retval( ca ); return; } String *sp_str = splitstr->asString(); uint32 sp_len = sp_str->length(); // return item. CoreArray *retarr = new CoreArray; // if the split string is empty, return empty string if ( sp_len == 0 ) { retarr->append( new CoreString() ); vm->retval( retarr ); return; } // if the token is wider than the string, just return the string if ( tg_len < sp_len ) { retarr->append( new CoreString( *tg_str ) ); vm->retval( retarr ); return; } uint32 pos = 0; uint32 last_pos = 0; // scan the string while( limit > 1 && pos <= tg_len - sp_len ) { uint32 sp_pos = 0; // skip matching pattern- while ( tg_str->getCharAt( pos ) == sp_str->getCharAt( sp_pos ) && sp_pos < sp_len && pos <= tg_len - sp_len ) { sp_pos++; pos++; } // a match? if ( sp_pos == sp_len ) { // put the item in the array. uint32 splitend = pos - sp_len; retarr->append( new CoreString( *tg_str, last_pos, splitend ) ); last_pos = pos; limit--; } else pos++; } // Residual element? if ( limit >= 1 || last_pos < tg_len ) { uint32 splitend = tg_len; retarr->append( new CoreString( *tg_str, last_pos, splitend ) ); } vm->retval( retarr ); } /*# @function strMerge @brief Merges an array of strings into a string. @param array An array of strings to be merged. @optparam mergeStr A string placed between each merge. @optparam count Maximum count of merges. @return The merged string. The function will return a new string containing the concatenation of the strings inside the array parameter. If the array is empty, an empty string is returned. If a mergeStr parameter is given, it is added to each pair of string merged; mergeStr is never added at the end of the new string. A count parameter may be specified to limit the number of elements merged in the array. The function may be used in this way: @code a = strMerge( [ "a", "string", "of", "words" ], " " ) printl( a ) // prints "a string of words" @endcode If an element of the array is not a string, an error is raised. */ /*# @method merge String @brief Merges an array of strings into one. @param array An array of strings to be merged. @return This string. This method works as strMerge, using this string as separator between the strings in the array. The function may be used in this way: @code a = " ".merge( [ "a", "string", "of", "words" ] ) printl( a ) // prints "a string of words" @endcode If an element of the array is not a string, an error is raised. */ FALCON_FUNC mth_strMerge ( ::Falcon::VMachine *vm ) { // Parameter checking; Item *source = vm->param(0); Item *mergestr = vm->param(1); Item *count = vm->param(2); uint64 limit; if ( source == 0 || ! source->isArray() || ( mergestr != 0 && ! mergestr->isString() ) || ( count != 0 && ! count->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "A" : "A,[S],[N]" ) ); } // Parameter estraction. limit = count == 0 ? 0xffffffff: count->forceInteger(); String *mr_str; if( mergestr != 0 ) { mr_str = mergestr->asString(); } else mr_str = vm->self().isMethodic() ? vm->self().asString() : 0; Item *elements = source->asArray()->items().elements(); uint32 len = source->asArray()->length(); if ( limit < len ) len = (uint32) limit; CoreString *ts = new CoreString; // filling the target. for( uint32 i = 1; i <= len ; i ++ ) { Item* item = elements+(i-1); if ( item->isString() ) *ts += *item->asString(); else { String temp; vm->itemToString( temp, item ); *ts += temp; } if ( mr_str != 0 && i < len ) ts->append( *mr_str ); ts->reserve( len/i * ts->size() ); } vm->retval( ts ); } /*# @method join String @brief Joins the parameters into a new string. @param ... @return A new string created joining the parameters, left to right. If this string is not empty, it is copied between each joined string. For example, the next code separates each value with ", " @code > ", ".join( "have", "a", "nice", "day" ) @endcode If the parameters are not string, a standard @a toString conversion is tried. */ FALCON_FUNC String_join ( ::Falcon::VMachine *vm ) { // Parameter extraction. CoreString *ts = new CoreString; String *self = vm->self().asString(); uint32 pc = vm->paramCount(); if ( pc > 0 ) { Item *head = vm->param(0); if ( head->isString() ) *ts = *head->asString(); else vm->itemToString( *ts, head ); ts->bufferize(); for( uint32 i = 1; i < pc; i++ ) { if( self->size() != 0 ) *ts += *self; Item *item = vm->param(i); if ( item->isString() ) *ts += *item->asString(); else { String temp; vm->itemToString( temp, item ); *ts += temp; } ts->reserve( pc/i * ts->size() ); } } vm->regA().setString( ts ); } /*# @function strFind @brief Finds a substring. @param string String where the search will take place. @param needle Substring to search for. @optparam start Optional position from which to start the search in string. @optparam end Optional position at which to end the search in string. @return The position where the substring is found, or -1. Returns the index in string were needle begins, or -1 if not present. Giving a start parameter will cause the search to start from the given position up to the end of the string; if a match can be made at start position, then the the returned value will be the same as start, so when repeating searches in a string for all the possible matches, repeat until the result is -1 by adding one to the returned value and using it as start position for the next search. If an end position is given, it is used as upper limit for the search, so that the search is in the interval [start, end-1]. @note This function is equivalent to the fbom method String.find */ /*# @method find String @brief Finds a substring. @param needle Substring to search for. @optparam start Optional position from which to start the search in string. @optparam end Optional position at which to end the search in string. @return The position where the substring is found, or -1. @see strFind */ FALCON_FUNC mth_strFind ( ::Falcon::VMachine *vm ) { // Parameter checking; Item *target = vm->param(0); Item *needle = vm->param(1); Item *start_item = vm->param(2); Item *end_item = vm->param(3); if ( vm->self().isMethodic() ) { target = &vm->self(); needle = vm->param(0); start_item = vm->param(1); end_item = vm->param(2); } else { target = vm->param(0); needle = vm->param(1); start_item = vm->param(2); end_item = vm->param(3); } if ( target == 0 || ! target->isString() || needle == 0 || ! needle->isString() || (start_item != 0 && ! start_item->isOrdinal()) || (end_item != 0 && ! end_item->isOrdinal()) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "S,[N],[N]" : "S,S,[N],[N]" ) ); } int64 start = start_item == 0 ? 0 : start_item->forceInteger(); int64 end = end_item == 0 ? csh::npos : end_item->forceInteger(); String *sTarget = target->asString(); // negative? -- fix if ( start < 0 ) end = sTarget->length() + start +1; // out of range? if ( start < 0 || start >= sTarget->length() ) { vm->retval( -1 ); return; } if ( end < 0 ) end = sTarget->length() + end+1; // again < than 0? -- it's out of range. if ( end < 0 ) { vm->retval( -1 ); return; } uint32 pos = target->asString()->find( *needle->asString(), (uint32) start, (uint32) end ); if ( pos != csh::npos ) vm->retval( (int64)pos ); else vm->retval( -1 ); } /*# @function strBackFind @brief Finds a substring backwards. @param string String where the search will take place. @param needle Substring to search for. @optparam start Optional position from which to start the search in string. @optparam end Optional position at which to end the search in string. @return The position where the substring is found, or -1. Works exactly as @a strFind, except for the fact that the last match in the string (or in the specified interval) is returned. */ /*# @method rfind String @brief Finds a substring backwards. @param needle Substring to search for. @optparam start Optional position from which to start the search in string. @optparam end Optional position at which to end the search in string. @return The position where the substring is found, or -1. @see strBackFind */ FALCON_FUNC mth_strBackFind ( ::Falcon::VMachine *vm ) { // Parameter checking; Item *target = vm->param(0); Item *needle = vm->param(1); Item *start_item = vm->param(2); Item *end_item = vm->param(3); if ( vm->self().isMethodic() ) { target = &vm->self(); needle = vm->param(0); start_item = vm->param(1); end_item = vm->param(2); } else { target = vm->param(0); needle = vm->param(1); start_item = vm->param(2); end_item = vm->param(3); } if ( target == 0 || ! target->isString() || needle == 0 || ! needle->isString() || (start_item != 0 && ! start_item->isOrdinal()) || (end_item != 0 && ! end_item->isOrdinal()) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "S,[N],[N]" : "S,S,[N],[N]" ) ); } int64 start = start_item == 0 ? 0 : (int64) start_item->forceInteger(); int64 end = end_item == 0 ? csh::npos : (int64) end_item->forceInteger(); String *sTarget = target->asString(); // negative? -- fix if ( start < 0 ) end = sTarget->length() + start +1; // out of range? if ( start < 0 || start >= sTarget->length() ) { vm->retval( -1 ); return; } if ( end < 0 ) end = sTarget->length() + end+1; // again < than 0? -- it's out of range. if ( end < 0 ) { vm->retval( -1 ); return; } uint32 pos = target->asString()->rfind( *needle->asString(), (uint32) start, (uint32) end ); if ( pos != csh::npos ) vm->retval( (int)pos ); else vm->retval( -1 ); } /*# @method rtrim String @brief Trims trailing whitespaces in a string. @optparam trimSet A set of characters that must be removed. @return The trimmed version of the string. @raise AccessError if the given item is not a string. A new string, which is a copy of the original one with all characters in @b trimSet at the end of the string removed, is returned. If @b trimSet is not supplied, it defaults to space, tabulation characters, new lines and carriage returns. The original string is unmodified. */ /*# @method ftrim String @brief Trims front whitespaces in a string. @optparam trimSet A set of characters that must be removed. @return The trimmed version of the string. @raise AccessError if the given item is not a string. A new string, which is a copy of the original one with all characters in @b trimSet at the beginning of the string removed, is returned. If @b trimSet is not supplied, it defaults to space, tabulation characters, new lines and carriage returns. The original string is unmodified. @see strFrontTrim */ /*# @method trim String @brief Trims whitespaces from both ends of a string. @optparam trimSet A set of characters that must be removed. @return The trimmed version of the string. @raise AccessError if the given item is not a string. A new string, which is a copy of the original one with all characters in @b trimSet at both ends of the string removed, is returned. If @b trimSet is not supplied, it defaults to space, tabulation characters, new lines and carriage returns. The original string is unmodified. @see strTrim */ /*# @function strTrim @brief Removes the white spaces at the beginning and at the end of a string. @param string The string to be trimmed. @optparam trimSet A set of characters that must be removed. @return The trimmed substring. A new string, which is a copy of the original one with all characters in @b trimSet at the end of the string removed, is returned. If @b trimSet is not supplied, it defaults to space, tabulation characters, new lines and carriage returns. The original string is unmodified. */ FALCON_FUNC mth_strTrim ( ::Falcon::VMachine *vm ) { String *self; Item *trimChars; if ( vm->self().isMethodic() ) { self = vm->self().asString(); trimChars = vm->param(0); } else { Item *i_str = vm->param( 0 ); if ( i_str == 0 || ! i_str->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } self = i_str->asString(); trimChars = vm->param(1); } CoreString *cs = new CoreString( *self ); cs->garbage().mark( vm->generation() ); if ( trimChars == 0 || trimChars->isNil() ) { cs->trim(); vm->retval( cs ); } else if ( ! trimChars->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } else { String *trim = trimChars->asString(); int32 tLen = trim->length(); int32 len = cs->length(); int32 start = 0; int32 end = len; uint32 chr; int found = 0; while( start < len ) { found = 0; chr = cs->getCharAt( start ); for ( int32 tIdx=0; tIdx < tLen; tIdx++ ) if ( chr == trim->getCharAt( tIdx ) ) found = 1; if ( found == 0 ) break; start++; } while( end > start ) { found = 0; chr = cs->getCharAt( end - 1 ); for ( int32 tIdx=0; tIdx < tLen; tIdx++ ) if ( chr == trim->getCharAt( tIdx ) ) found = 1; if ( found == 0 ) break; end--; } // an empty string if set is empty vm->retval( cs->subString( start, end ) ); } } /*# @function strFrontTrim @brief Removes white spaces from the front of the string. @param string The string to be trimmed. @optparam trimSet A set of characters that must be removed. @return The trimmed substring. A new string, which is a copy of the original one with all characters in @b trimSet at the beginning of the string removed, is returned. If @b trimSet is not supplied, it defaults to space, tabulation characters, new lines and carriage returns. The original string is unmodified. */ FALCON_FUNC mth_strFrontTrim ( ::Falcon::VMachine *vm ) { String *self; Item *trimChars; if ( vm->self().isMethodic() ) { self = vm->self().asString(); trimChars = vm->param(0); } else { Item *i_str = vm->param( 0 ); if ( i_str == 0 || ! i_str->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } self = i_str->asString(); trimChars = vm->param(1); } if (trimChars == 0 ) { CoreString *cs = new CoreString( *self ); cs->frontTrim(); vm->retval( cs ); } else if ( ! trimChars->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } else { int pos = 0; int32 len = self->length(); String *trim = trimChars->asString(); int32 tLen = trim->length(); while( pos < len ) { uint32 chr = self->getCharAt( pos ); int found = 0; for ( int32 tIdx = 0; tIdx < tLen; tIdx++ ) if ( chr == trim->getCharAt( tIdx ) ) found = 1; if ( found == 0 ) break; pos++; } // has something to be trimmed? if ( pos < len ) vm->retval( new CoreString( self->subString( pos, len ) ) ); else vm->retval( new CoreString ); } } /*# @function strBackTrim @brief Removes white spaces at both the beginning and the end of the string. @param string The string to be trimmed. @optparam trimSet A set of characters that must be removed. @return The trimmed substring. A new string, which is a copy of the original one with all characters in @b trimSet at the beginning and at the end of the string removed, is returned. If @b trimSet is not supplied, it defaults to space, tabulation characters, new lines and carriage returns. The original string is unmodified. */ FALCON_FUNC mth_strBackTrim ( ::Falcon::VMachine *vm ) { String *self; Item *trimChars; if ( vm->self().isMethodic() ) { self = vm->self().asString(); trimChars = vm->param(0); } else { Item *i_str = vm->param( 0 ); if ( i_str == 0 || ! i_str->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } self = i_str->asString(); trimChars = vm->param(1); } if ( trimChars == 0 ) { CoreString *cs = new CoreString( *self ); cs->backTrim(); vm->retval( cs ); } else if ( ! trimChars->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } else { int32 pos = self->length()-1; String *trim = trimChars->asString(); int32 tLen = trim->length(); while ( pos >= 0 ) { uint32 chr = self->getCharAt( pos ); int found = 0; for ( int32 tIdx=0; tIdx < tLen; tIdx++ ) if ( chr == trim->getCharAt( tIdx ) ) found = 1; if ( found == 0 ) break; pos--; } // has something to be trimmed? if ( pos >= 0) vm->retval( new CoreString( self->subString( 0, pos + 1 ) ) ); else vm->retval( new CoreString ); } } /*# @function strReplace @brief Replaces the all the occurrences of a substring with another one. @param string The string where the replace will take place. @param substr The substring that will be replaced. @param repstr The string that will take the place of substr. @optparam start Optional start position in the string. @optparam end Optional end position in the string. @return A copy of the string with the occourences of the searched substring replaced. This is a flexible function that allows to alter a string by changing all the occurrences of a substring into another one. If the start parameter is given, the search and replacement will take place only starting at the specified position up to the end of the string, and if the end parameter is also provided, the search will take place in the interval [start, end-1]. */ /*# @method replace String @brief Replaces the all the occurrences of a substring with another one. @param substr The substring that will be replaced. @param repstr The string that will take the place of substr. @optparam start Optional start position in the string. @optparam end Optional end position in the string. @return A copy of the string with the occourences of the searched substring replaced. @see strReplace */ FALCON_FUNC mth_strReplace ( ::Falcon::VMachine *vm ) { // Parameter checking; Item *target; Item *needle; Item *replacer; Item *start_item; Item *end_item; if( vm->self().isMethodic() ) { target = &vm->self(); needle = vm->param(0); replacer = vm->param(1); start_item = vm->param(2); end_item = vm->param(3); } else { target = vm->param(0); needle = vm->param(1); replacer = vm->param(2); start_item = vm->param(3); end_item = vm->param(4); } if ( target == 0 || ! target->isString() || needle == 0 || ! needle->isString() || replacer == 0 || ! replacer->isString() || (start_item != 0 && ! start_item->isOrdinal()) || (end_item != 0 && ! end_item->isOrdinal()) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "S,S,[N],[N]" : "S,S,S,[N],[N]" ) ); } // Parameter estraction. String *tg_str = target->asString(); uint32 tg_len = target->asString()->length(); String *ned_str = needle->asString(); int32 ned_len = (int32) needle->asString()->length(); // Is the needle is empty if ( ned_len == 0 ) { // shallow copy the target vm->retval( *target ); return; } String *rep_str = replacer->asString(); uint32 rep_len = replacer->asString()->length(); int32 start = start_item ? (int32) start_item->forceInteger(): 0; if ( start < 0 ) start = 0; int32 end = end_item ? (int32) end_item->forceInteger(): tg_len-1; if ( end >= (int32) tg_len ) end = tg_len-1; CoreString *ret = new CoreString; if ( start > 0 ) ret->append( String( *tg_str, 0, start ) ); int32 old_start = start; while ( start <= end ) { int32 ned_pos = 0; int32 pos = 0; // skip matching pattern while ( tg_str->getCharAt( start + pos ) == ned_str->getCharAt( ned_pos ) && ned_pos < (int32) ned_len && start + ned_pos <= end ) { ned_pos++; pos++; } // a match? if ( ned_pos == ned_len ) { if ( start > old_start ) { ret->append( String( *tg_str, old_start, start ) ); } if ( rep_len > 0 ) { ret->append( *rep_str ); } start += ned_len; old_start = start; } else start++; } if ( old_start < (int32)tg_len ) ret->append( String( *tg_str, old_start ) ); vm->retval( ret ); } /*# @function strReplicate @brief Returns a new string that is created by replicating the original one. @param string The string to be replicaeted. @param times Number of times the string must be replicated. @return The new string. A nice shortcut. Also, this function performs the work efficiently, preallocating the needed space in one shot and avoiding the need to grow the string while replicating the original value. */ /*# @method replicate String @brief Returns a new string that is created by replicating this one. @param times Number of times the string must be replicated. @return The new string. A nice shortcut. Also, this function performs the work efficiently, preallocating the needed space in one shot and avoiding the need to grow the string while replicating the original value. */ FALCON_FUNC mth_strReplicate ( ::Falcon::VMachine *vm ) { // Parameter checking; Item *strrep; Item *qty; if ( vm->self().isMethodic() ) { strrep = &vm->self(); qty = vm->param(0); } else { strrep = vm->param(0); qty = vm->param(1); } if ( strrep == 0 || ! strrep->isString() || qty == 0 || ! qty->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "N" : "S,N" ) ); } int32 repl = (int32) qty->forceInteger(); String *replicated = strrep->asString(); int32 len = replicated->size() * repl; if ( len <= 0 ) { vm->retval( new CoreString("") ); return; } CoreString *target = new CoreString; target->reserve( len ); int pos = 0; while ( pos < len ) { memcpy( target->getRawStorage() + pos, replicated->getRawStorage(), replicated->size() ); pos+= replicated->size(); } target->manipulator( const_cast(replicated->manipulator()->bufferedManipulator()) ); target->size( len ); vm->retval( target ); } /*# @function strBuffer @brief Pre-allocates an empty string. @param size Size of the pre-allocated string. @return The new string. The returned string is an empty string, and equals to "". However, the required size is pre-allocated, and addition to this string (i.e. += operators) takes place in a fraction of the time otherwise required, up tho the filling of the pre-allocated buffer. Also, this string can be fed into file functions, the pre-allocation size being used as the input read size. */ FALCON_FUNC strBuffer ( ::Falcon::VMachine *vm ) { // Parameter checking; Item *qty = vm->param(0); if ( qty == 0 || ! qty->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra("N") ); } int32 size = (int32) qty->forceInteger(); if ( size <= 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ).origin( e_orig_runtime ) ); } vm->retval( new CoreString( String( size ) ) ); } /*# @function strUpper @brief Returns an upper case version of the string. @param string String that must be uppercased. @return The uppercased string. All the Latin characters in the string are turned uppercase. Other characters are left untouched. */ /*# @method upper String @brief Returns an upper case version of this string. @return The uppercased string. All the Latin characters in the string are turned uppercase. Other characters are left untouched. */ FALCON_FUNC mth_strUpper ( ::Falcon::VMachine *vm ) { Item *source; // Parameter checking; if ( vm->self().isMethodic() ) { source = &vm->self(); } else { source = vm->param(0); if ( source == 0 || ! source->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S" ) ); } } String *src = source->asString(); if ( src->size() == 0 ) { vm->retval( new CoreString ); } else { CoreString *target = new CoreString( *src ); target->upper(); vm->retval( target ); } } /*# @function strLower @brief Returns a lowercase version of the given string. @param string String that must be lowercased. @return The lowercased string. All the Latin characters in the string are turned lowercase. Other characters are left untouched. */ /*# @method lower String @brief Returns a lowercase version of this string. @return The lowercased string. All the Latin characters in the string are turned lowercase. Other characters are left untouched. */ FALCON_FUNC mth_strLower ( ::Falcon::VMachine *vm ) { Item *source; // Parameter checking; if ( vm->self().isMethodic() ) { source = &vm->self(); } else { source = vm->param(0); if ( source == 0 || ! source->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S" ) ); } } String *src = source->asString(); if ( src->size() == 0 ) { vm->retval( new CoreString ); } else { CoreString *target = new CoreString( *src ); target->lower(); vm->retval( target ); } } /*# @method startsWith String @brief Check if a strings starts with a substring. @param token The substring that will be compared with this string. @optparam icase If true, pefroms a case neutral check @return True if @b token matches the beginning of this string, false otherwise. This method performs a comparation check at the beginning of the string. If this string starts with @b token, the function returns true. If @b token is larger than the string, the method will always return false, and if @b token is an empty string, it will always match. The optional parameter @b icase can be provided as true to have this method to perform a case insensitive match. */ /*# @function strStartsWith @brief Check if a strings starts with a substring. @param string The string that is going to be tested for the given token. @param token The substring that will be compared with this string. @optparam icase If true, pefroms a case neutral check @return True if @b token matches the beginning of @b string, false otherwise. This functioin performs a comparation check at the beginning of the @b string. If this string starts with @b token, the function returns true. If @b token is larger than the string, the function will always return false, and if @b token is an empty string, it will always match. The optional parameter @b icase can be provided as true to have this function to perform a case insensitive match. */ FALCON_FUNC mth_strStartsWith ( ::Falcon::VMachine *vm ) { Item* source; Item* i_token; Item* i_icase; // Parameter checking; if ( vm->self().isMethodic() ) { source = &vm->self(); i_token = vm->param(0); i_icase = vm->param(1); } else { source = vm->param(0); i_token = vm->param(1); i_icase = vm->param(2); } if ( source == 0 || ! source->isString() || i_token == 0 || ! i_token->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "S,[B]" : "S,S,[B]" ) ); } String *src = source->asString(); vm->regA().setBoolean( src->startsWith( *i_token->asString(), i_icase ? i_icase->isTrue():false ) ); } /*# @method endsWith String @brief Check if a strings ends with a substring. @param token The substring that will be compared with this string. @optparam icase If true, pefroms a case neutral check @return True if @b token matches the end of this string, false otherwise. This method performs a comparation check at the end of the string. If this string ends with @b token, the function returns true. If @b token is larger than the string, the method will always return false, and if @b token is an empty string, it will always match. The optional parameter @b icase can be provided as true to have this method to perform a case insensitive match. */ /*# @function strEndsWith @brief Check if a strings ends with a substring. @param string The string that is going to be tested for the given token. @param token The substring that will be compared with this string. @optparam icase If true, pefroms a case neutral check @return True if @b token matches the end of @b string, false otherwise. This functioin performs a comparation check at the end of the @b string. If this string ends with @b token, the function returns true. If @b token is larger than the string, the function will always return false, and if @b token is an empty string, it will always match. The optional parameter @b icase can be provided as true to have this function to perform a case insensitive match. */ FALCON_FUNC mth_strEndsWith ( ::Falcon::VMachine *vm ) { Item* source; Item* i_token; Item* i_icase; // Parameter checking; if ( vm->self().isMethodic() ) { source = &vm->self(); i_token = vm->param(0); i_icase = vm->param(1); } else { source = vm->param(0); i_token = vm->param(1); i_icase = vm->param(2); } if ( source == 0 || ! source->isString() || i_token == 0 || ! i_token->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "S,[B]" : "S,S,[B]" ) ); } String *src = source->asString(); vm->regA().setBoolean( src->endsWith( *i_token->asString(), i_icase ? i_icase->isTrue() : false ) ); } /*# @function strCmpIgnoreCase @brief Performs a lexicographic comparation of two strings, ignoring character case. @param string1 First string to be compared. @param string2 Second string to be compared. @return -1, 0 or 1. The two strings are compared ignoring the case of latin characters contained in the strings. If the first string is greater than the second, the function returns a number less than 0. If it's smaller, it returns a positive number. If the two strings are the same, ignoring the case of the characters, 0 is returned. */ /*# @method cmpi String @brief Performs a lexicographic comparation of two strings, ignoring character case. @param string Second string to be compared with this one. @return <0, 0 or >0, respectively if this string is less, equal or greater than the @b string parameter. The two strings are compared ignoring the case of latin characters contained in the strings. If the first string is greater than the second, the function returns a number less than 0. If it's smaller, it returns a positive number. If the two strings are the same, ignoring the case of the characters, 0 is returned. */ FALCON_FUNC mth_strCmpIgnoreCase ( ::Falcon::VMachine *vm ) { Item *s1_itm; Item *s2_itm; // Parameter checking; if( vm->self().isMethodic() ) { s1_itm = &vm->self(); s2_itm = vm->param(0); } else { s1_itm = vm->param(0); s2_itm = vm->param(1); } if ( s1_itm == 0 || ! s1_itm->isString() || s2_itm == 0 || !s2_itm->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "S" : "S,S" ) ); } String *str1 = s1_itm->asString(); String *str2 = s2_itm->asString(); int32 len1 = str1->length(); int32 len2 = str2->length(); int32 minlen = len1 > len2 ? len2 : len1; for( int32 i = 0; i < minlen; i ++ ) { int32 elem1 = str1->getCharAt( i ); int32 elem2 = str2->getCharAt( i ); if ( elem1 >= 'A' && elem1 <= 'Z' ) elem1 |= 0x20; if ( elem2 >= 'A' && elem2 <= 'Z' ) elem2 |= 0x20; if ( elem1 > elem2 ) { vm->retval( 1 ); return; } if ( elem1 < elem2 ) { vm->retval( -1 ); return; } } if ( len1 > len2 ) { vm->retval( 1 ); return; } if ( len1 < len2 ) { vm->retval( -1 ); return; } // same!!! vm->retval( 0 ); } inline void internal_escape( int mode, VMachine* vm ) { Item *i_string; Item *i_onplace; // Parameter checking; if( vm->self().isMethodic() ) { i_string = &vm->self(); i_onplace = vm->param(0); } else { i_string = vm->param(0); i_onplace = vm->param(1); if ( i_string == 0 || ! i_string->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S,B" ) ); } } String* str; if ( mode != 2 && i_onplace != 0 && i_onplace->isTrue() ) { str = i_string->asString(); } else { str = new CoreString( *i_string->asString() ); } switch( mode ) { case 0: //esq str->escapeQuotes(); break; case 1: //unesq str->unescapeQuotes(); break; case 2: //escape { CoreString* other = new CoreString( str->size() ); if ( i_onplace != 0 && i_onplace->isTrue() ) // actually it means "complete" str->escapeFull( *other ); else str->escape( *other ); vm->retval( other ); } return; case 3: //unescape str->unescape(); break; } vm->retval( str ); } /*# @function strEsq @brief Escape quotes in the given string. @param string the String to be escaped. @optparam inplace if true, the source string is modified, saving memory. @return A new escaped string, if @b inplace is not given, or the @b string parameter if @b inplace is true. @see String.esq @see strUnesq */ /*# @method esq String @brief Escapes the quotes in this string. @optparam inplace if true, the source string is modified, saving memory. @return A new escaped string, if @b inplace is not given, or this same string if @b inplace is true. @see String.unesq @see strEsq */ FALCON_FUNC mth_strEsq ( ::Falcon::VMachine *vm ) { internal_escape( 0, vm ); } /*# @function strUnesq @brief Unescape the quotes in given string. @param string the String to be unescaped. @optparam inplace if true, the source string is modified, saving memory. @return A new unescaped string, if @b inplace is not given, or the @b string parameter if @b inplace is true. This function transforms all the occourences of '\\"' and '\\'' into a double or single quote, leaving all the other special escape sequences untouched. @see String.unesq @see strEsq */ /*# @method unesq String @brief Escapes the quotes in this string. @optparam inplace if true, the source string is modified, saving memory. @return A new escaped string, if @b inplace is not given, or this same string if @b inplace is true. @see String.esq @see strUnesq */ FALCON_FUNC mth_strUnesq ( ::Falcon::VMachine *vm ) { internal_escape( 1, vm ); } /*# @function strEscape @brief Escape quotes and special characters in the string @param string the String to be escaped. @optparam full If true, characters above UNICODE 127 are escaped as well. @return A new escaped string. @see String.esq @see strUnesq */ /*# @method escape String @brief Escapes all the special characters in the string. @optparam full If true, characters above UNICODE 127 are escaped as well. @return A new escaped string. @see String.esq @see strEsq @see strUnescape */ FALCON_FUNC mth_strEscape ( ::Falcon::VMachine *vm ) { internal_escape( 2, vm ); } /*# @function strUnescape @brief Unescape quotes and special characters in the string @param string the String to be escaped. @optparam inplace if true, the source string is modified, saving memory. @return A new unescaped string, if @b inplace is not given, or the @b string parameter if @b inplace is true. @see String.esq @see strUnesq @see String.unescape */ /*# @method unescape String @brief Unescapes all the special characters in the string. @optparam inplace if true, the source string is modified, saving memory. @return A new unescaped string, if @b inplace is not given, or the @b string parameter if @b inplace is true. @see String.esq @see strEsq @see strEscape */ FALCON_FUNC mth_strUnescape ( ::Falcon::VMachine *vm ) { internal_escape( 3, vm ); } /*# @function strWildcardMatch @brief Perform an old-style file-like jolly-based wildcard match. @param string String that must match the wildcard. @param wildcard A wildcard, possibly but not necessarily including a jolly character. @optparam ignoreCase If true, the latin 26 base letters case is ignored in matches. @return True if the string matches, false otherwise. This function matches a wildcard that may contain jolly "*" or "?" characters against a string, eventually ignoring the case of the characters. This is a practical function to match file names against given patterns. A "?" in the wildcard represents any single character, while a "*" represents an arbitrary sequence of characters. The wildcard must match completely the given string for the function to return true. For example: - "*" matches everything - "a?b" matches "aab", "adb" and so on - "a*b" matches "ab", "annnb" and so on @see String.wmatch */ /*# @method wmatch String @brief Perform an old-style file-like jolly-based wildcard match. @param wildcard A wildcard, possibly but not necessarily including a jolly character. @optparam ignoreCase If true, the latin 26 base letters case is ignored in matches. @return True if the string matches, false otherwise. This function matches a wildcard that may contain jolly "*" or "?" characters against a string, eventually ignoring the case of the characters. This is a practical function to match file names against given patterns. A "?" in the wildcard represents any single character, while a "*" represents an arbitrary sequence of characters. The wildcard must match completely the given string for the function to return true. For example: - "*" matches everything - "a?b" matches "aab", "adb" and so on - "a*b" matches "ab", "annnb" and so on @see strWildcardMatch */ FALCON_FUNC mth_strWildcardMatch ( ::Falcon::VMachine *vm ) { // Parameter checking; Item *s1_itm = vm->param(0); Item *s2_itm = vm->param(1); Item *i_bIcase = vm->param(2); if ( vm->self().isMethodic() ) { s1_itm = &vm->self(); s2_itm = vm->param(0); i_bIcase = vm->param(1); } else { s1_itm = vm->param(0); s2_itm = vm->param(1); i_bIcase = vm->param(2); } if ( s1_itm == 0 || ! s1_itm->isString() || s2_itm == 0 || !s2_itm->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "S,[B]" : "S,S,[B]") ); } // Ignore case? bool bIcase = i_bIcase == 0 ? false : i_bIcase->isTrue(); // The first is the wildcard, the second is the matched thing. String *cfr = s1_itm->asString(); String *wcard = s2_itm->asString(); vm->regA().setBoolean( cfr->wildcardMatch( *wcard, bIcase ) ); } /*# @function strToMemBuf @brief Convets a string into a Memory Buffer @param string String to be converted in a membuf. @optparam wordWidth The memory buffer word width (defaults to string character size). @return The resulting membuf. This function creates a membuf from a string. The resulting membuf has the same word width of the original string, which may be 1, 2 or 4 byte wide depending on the size needed to store its contents. It is possible to specify a different word width; in that case the function will be much less efficient (each character must be copied). If wordWidth is set to zero, the resulting memory buffer will have 1 byte long elements, but the content of the string will be copied as-is, bytewise, regardless of its character size. */ /*# @method toMemBuf String @brief Convets this string into a Memory Buffer @optparam wordWidth The memory buffer word width (defaults to string character size). @return The resulting membuf. This function creates a membuf from a string. The resulting membuf has the same word width of the original string, which may be 1, 2 or 4 byte wide depending on the size needed to store its contents. It is possible to specify a different word width; in that case the function will be much less efficient (each character must be copied). If wordWidth is set to zero, the resulting memory buffer will have 1 byte long elements, but the content of the string will be copied as-is, bytewise, regardless of its character size. */ FALCON_FUNC mth_strToMemBuf ( ::Falcon::VMachine *vm ) { Item *i_string; Item *i_wordWidth; // Parameter checking; if ( vm->self().isMethodic() ) { i_string = &vm->self(); i_wordWidth = vm->param(0); } else { i_string = vm->param(0); i_wordWidth = vm->param(1); } if( i_string == 0 || ! i_string->isString() || ( i_wordWidth != 0 && ! i_wordWidth->isOrdinal() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "[N]" : "S,[N]" ) ); } String *string = i_string->asString(); int charSize = string->manipulator()->charSize(); int64 ww = i_wordWidth == 0 ? charSize : i_wordWidth->forceInteger(); MemBuf *result; if ( ww == 0 ) { result = new MemBuf_1( string->size() ); memcpy( result->data(), string->getRawStorage(), string->size() ); } else { result = MemBuf::create( charSize, string->length() ); if ( result == 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra("0-4") ); } if ( ww == charSize ) { memcpy( result->data(), string->getRawStorage(), string->size() ); } else { uint32 size = string->size(); for( uint32 p = 0; p < size; p++ ) { result->set( p, string->getCharAt(p) ); } } } vm->retval( result ); } /*# @function strFromMemBuf @brief Convets a MemBuf to a string. @param membuf A MemBuf that will be converted to a string. @return The resulting string. This string takes each element of the membuf and converts it into a character in the final string. The contents of the buffer are not transcoded. It is appropriate to say that this function considers each element in the MemBuf as an Unicode value for the character in the final string. To create a string from a buffer that may come from an encoded source (i.e. a file), use directly Transcode functions. */ FALCON_FUNC strFromMemBuf ( ::Falcon::VMachine *vm ) { // Parameter checking; Item *i_membuf = vm->param(0); if( i_membuf == 0 || ! i_membuf->isMemBuf() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra("M") ); } MemBuf *mb = i_membuf->asMemBuf(); // preallocating size instead of len, we won't have to resize the memory even // if resizing character sizes. CoreString *result = new CoreString( mb->size() ); uint32 size = mb->length(); for( uint32 p = 0; p < size; p++ ) { result->append( mb->get( p ) ); } vm->retval( result ); } /*# @function strFill @brief Fills a string with a given character or substring. @param string The string to be filled. @param chr The character (unicode value) or substring used to refill the @b string. @return The string itself. This function fills the physical storage of the given string with a single character or a repeated substring. This can be useful to clean a string used repeatedly as input buffer. The function returns the same string that has been passed as the parameter. */ /*# @method fill String @brief Fills a string with a given character or substring. @param chr The character (unicode value) or substring used to refill this string. @return The string itself. This method fills the physical storage of the given string with a single character or a repeated substring. This can be useful to clean a string used repeatedly as input buffer. */ FALCON_FUNC mth_strFill ( ::Falcon::VMachine *vm ) { Item *i_string; Item *i_chr; // Parameter checking; if ( vm->self().isMethodic() ) { i_string = &vm->self(); i_chr = vm->param(0); } else { i_string = vm->param(0); i_chr = vm->param(1); } if( i_string == 0 || ! i_string->isString() || i_chr == 0 || ( ! i_chr->isOrdinal() && !i_chr->isString()) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->self().isMethodic() ? "N|S" : "S,N|S" ) ); } CoreString *string = i_string->asCoreString(); if ( i_chr->isOrdinal() ) { uint32 chr = (uint32) i_chr->forceInteger(); for( uint32 i = 0; i < string->length(); i ++ ) { string->setCharAt( i, chr ); } } else { String* rep = i_chr->asString(); if ( rep->length() == 0 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( vm->moduleString( rtl_string_empty ) ) ); } uint32 pos = 0; uint32 pos2 = 0; while( pos < string->length() ) { string->setCharAt( pos++, rep->getCharAt( pos2++ ) ); if ( pos2 >= rep->length() ) { pos2 = 0; } } } vm->retval( string ); } }} /* end of string.cpp */ engine/core_module/stringstream_ext.cpp000066400000000000000000000075071176363201700207450ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: sstream.cpp Falcon module interface for string streams. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom mar 5 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ /** \file Falcon module interface for string streams. */ #include #include #include #include #include namespace Falcon { namespace core { /*# @class StringStream @brief Memory based stream. @optparam buffer The buffer that will be used as stream @from Stream The StringStream class inherits from stream. It can be used to provide functions that are supposed to write to streams with a memory buffer; for example, variables may be serialized on a string stream which can be then written completely on a physical stream, or sent over the network, or written in a database blob field. The reverse is of course possible: a string can be read from any source and then used to construct a StringStream, that can be then fed to function expecting streams as parameters. Of course, all the methods listed in the Stream class are available also here. The StringStream is always available for read and write operations, and supports seek operations. Writing past the end of the stream will cause the StringStream to grow. If the parameter @b buffer is a numeric value, the constructor preallocates the given size. Writes up to buffer size won't require re-allocation, and the size will be used as a hint to grow the stream buffer sensibly. If a string is provided, it is used as initial contents of the StringStream; subsequent reads will return the data contained in the string. */ FALCON_FUNC StringStream_init ( ::Falcon::VMachine *vm ) { // check the paramenter. Item *size_itm = vm->param( 0 ); Stream *stream; if ( size_itm != 0 ) { if ( size_itm->isString() ) { stream = new StringStream ( *size_itm->asString() ); } else if ( size_itm->isOrdinal() ) { stream = new StringStream ( (int32) size_itm->forceInteger() ); } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra( "S|N" ) ); } } else stream = new StringStream (); // get the self object CoreObject *self = vm->self().asObject(); // create the string stream self->setUserData( stream ); } /*# @method getString StringStream @brief Returns the data currently stored in the stream. @return A copy of the contents of this stream. The data currently held in the stream is left untouched, and a new copy of the data is returned. */ FALCON_FUNC StringStream_getString ( ::Falcon::VMachine *vm ) { // get the self object CoreObject *self = vm->self().asObject(); StringStream *ss = (StringStream *)self->getUserData(); vm->retval( ss->getCoreString() ); } /*# @method closeToString StringStream @brief Close the stream and returns its contents. @return The stream contents. Closes the stream and returns the contents of the stream as a string. The object is internally destroyed, and the whole content is transformed into the returned string. In this way, an extra allocation and copy can be spared. */ FALCON_FUNC StringStream_closeToString ( ::Falcon::VMachine *vm ) { // get the self object CoreObject *self = vm->self().asObject(); StringStream *ss = (StringStream *)self->getUserData(); CoreString *rets = new CoreString; ss->closeToString( *rets ); vm->retval( rets ); } }} /* end of sstream.cpp */ engine/core_module/sys_ext.cpp000066400000000000000000000026661176363201700170420ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: sys_ext.cpp System relevant extensions. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 14 Aug 2008 00:17:31 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ #include "core_module.h" #include namespace Falcon { namespace core { /*# @function exit @ingroup core_syssupport @param value an item representing VM exit code. @brief Requires immediate termination of the program. The VM execution will be interrupted, with normal state, and the item will be passed in the A register of the VM, where embedding application expects to receive the exit value. Semantic of the exit value will vary depending on the embedding application. The Falcon command line tools (and so, stand alone scripts) will pass this return value to the system if it is an integer, else they will terminate passing 0 to the system. This function terminates the VM also when there are more coroutines running. */ FALCON_FUNC core_exit ( ::Falcon::VMachine *vm ) { Item *ret = vm->param(0); if ( ret != 0 ) vm->retval( *ret ); else vm->retnil(); throw VMEventQuit(); } } } /* end of sys_ext.cpp */ engine/core_module/table.cpp000066400000000000000000001306011176363201700164220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: table.cpp Table support Iterface for Falcon. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 14 Sep 2008 15:55:59 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ #include #include #include #include #include namespace Falcon { namespace core { /*# @class Table @brief Home of tabular programming. @optparam heading The heading of the table (array) */ FALCON_FUNC Table_init( VMachine* vm ) { // the first parameter is the heading Item *i_heading = vm->param( 0 ); if ( i_heading != 0 && ! i_heading->isArray() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( Engine::getMessage(rtl_no_tabhead) ) ); } CoreTable* table = new CoreTable(); if( i_heading != 0 ) { if (! table->setHeader( i_heading->asArray() ) ) { throw new ParamError( ErrorParam( e_param_type, __LINE__ ) .origin( e_orig_runtime ) .extra( Engine::getMessage(rtl_invalid_tabhead) ) ); } } uint32 order = table->order(); // create the first table CoreArray *page = new CoreArray( vm->paramCount() ); table->insertPage( vm->self().asObject(), page ); table->setCurrentPage(0); CoreObject *self = vm->self().asObject(); self->setUserData( table ); table->owner( self ); // now we can safely add every other row that has been passed. for (int i = 1; i < vm->paramCount(); i ++ ) { Item *vi = vm->param(i); if( ! vi->isArray() || vi->asArray()->length() != order ) { String tempString = Engine::getMessage(rtl_invalid_tabrow); tempString += ": ["; tempString.writeNumber( (int64)( i-1 ) ); tempString += "]"; throw new ParamError( ErrorParam( e_param_type, __LINE__ ) .origin( e_orig_runtime ) .extra( tempString ) ); } vi->asArray()->table( self ); table->insertRow( vi->asArray() ); } } /*# @method setHeader Table @brief Sets the header of an empty table. @param header An array of strings or future bindings that will be used as table header @raise CodeError if the table has already a valid header. This method allows to set the header of an uninitialized table after its creation. */ FALCON_FUNC Table_setHeader( VMachine* vm ) { // the first parameter is the heading Item *i_heading = vm->param( 0 ); if ( i_heading != 0 && ! i_heading->isArray() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( Engine::getMessage(rtl_no_tabhead) ) ); } CoreObject* self = vm->self().asObject(); CoreTable* table = reinterpret_cast(self->getUserData()); if ( table->order() != CoreTable::noitem ) { throw new TableError( ErrorParam( e_table_aconf, __LINE__ ) .origin( e_orig_runtime ) .extra( Engine::getMessage(rtl_tabhead_given) ) ); } if (! table->setHeader( i_heading->asArray() ) ) { throw new TableError( ErrorParam( e_param_type, __LINE__ ) .origin( e_orig_runtime ) .extra( Engine::getMessage(rtl_invalid_tabhead) ) ); } } /*# @method getHeader Table @brief Gets the name of one header, or the list of header names. @optparam id If given, a number indicating the column of which to get the name. @return A string (if @b id is given) or the vector of ordered column names. */ FALCON_FUNC Table_getHeader( VMachine* vm ) { CoreObject* self = vm->self().asObject(); CoreTable* table = reinterpret_cast(self->getUserData()); Item *i_pos = vm->param( 0 ); if ( i_pos != 0 && ! i_pos->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "(N)" ) ); } if( i_pos != 0 ) { uint32 pos = (uint32) i_pos->forceInteger(); if( pos > table->order() ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) ); } vm->retval( new CoreString( table->heading(pos) ) ); } else { CoreArray *ret = new CoreArray( table->order() ); ret->resize( table->order() ); for( uint32 i = 0; i < table->order(); i ++ ) { ret->at(i) = new CoreString( table->heading( i ) ); } vm->retval( ret ); } } /*# @method getColData Table @brief Gets the clumn wide data associated with this table. @optparam id If given, a number indicating the column of which to get the data. @return An item (if @b id is given) or the vector of ordered column names. */ FALCON_FUNC Table_getColData( VMachine* vm ) { CoreObject* self = vm->self().asObject(); CoreTable* table = reinterpret_cast(self->getUserData()); Item *i_pos = vm->param( 0 ); if ( i_pos != 0 && ! i_pos->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "(N)" ) ); } if( i_pos != 0 ) { uint32 pos = (uint32) i_pos->forceInteger(); if( pos > table->order() ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) ); } vm->retval( table->columnData(pos) ); } else { CoreArray *ret = new CoreArray( table->order() ); for( uint32 i = 0; i < table->order(); i ++ ) { ret->append( table->columnData(i) ); } vm->retval( ret ); } } /*# @method order Table @brief Returns the order of the table (column count). @return The number of the columns, and of the length of every array in the table. */ FALCON_FUNC Table_order( VMachine* vm ) { CoreObject* self = vm->self().asObject(); CoreTable* table = reinterpret_cast(self->getUserData()); vm->retval( (int64) table->order() ); } /*# @method len Table @brief Returns the length of the table (the number of rows). @return The rows in the current page of the table. Tables may have multiple pages, each of which having the same order (column count), but different length (rows). This method returns the length of the currently active page. */ FALCON_FUNC Table_len( VMachine* vm ) { CoreObject* self = vm->self().asObject(); CoreTable* table = reinterpret_cast(self->getUserData()); if ( table->currentPage() == 0 ) { vm->retval( 0 ); } else { vm->retval( (int64) table->currentPage()->length() ); } } /*# @method front Table @brief Returns the first item in the table. @raise AccessError if the table is empty. @return The first item in the table. If the table is not empty, it returns the first element (row) in the table. */ FALCON_FUNC Table_front ( ::Falcon::VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); if( table->empty() ) // empty() is virtual { throw new AccessError( ErrorParam( e_table_empty, __LINE__ ). origin( e_orig_runtime ) ); } vm->retval( table->front() ); } /*# @method back Table @brief Returns the last item in the table. @raise AccessError if the table is empty. @return The last item in the table. If the table is not empty, it returns the last element (row) in the table. */ FALCON_FUNC Table_back ( ::Falcon::VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); if( table->empty() ) // empty() is virtual { throw new AccessError( ErrorParam( e_table_empty, __LINE__ ). origin( e_orig_runtime ) ); } vm->retval( table->back() ); } static void internal_first_last( VMachine *vm, bool mode ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); if( table->empty() ) // empty() is virtual { throw new AccessError( ErrorParam( e_table_empty, __LINE__ ). origin( e_orig_runtime ) ); } Item *i_iclass = vm->findWKI( "Iterator" ); fassert( i_iclass != 0 ); CoreObject *iobj = i_iclass->asClass()->createInstance(); iobj->setUserData( new Iterator( table, mode) ); vm->retval( iobj ); } /*# @method first Table @brief Returns an iterator to the first element of the table. @return An iterator. Returns an iterator to the first element of the table. If the table is empty, an invalid iterator will be returned, but an insertion on that iterator will succeed and append an item to the table. */ FALCON_FUNC Table_first ( ::Falcon::VMachine *vm ) { internal_first_last( vm, false ); } /*# @method last Table @brief Returns an iterator to the last element of the table. @return An iterator. Returns an iterator to the last element of the table. If the table is empty, an invalid iterator will be returned, but an insertion on that iterator will succeed and append an item to the table. */ FALCON_FUNC Table_last ( ::Falcon::VMachine *vm ) { internal_first_last( vm, true ); } static uint32 internal_col_pos( CoreTable *table, VMachine *vm, Item *i_column ) { uint32 colPos; if( i_column->isString() ) { colPos = table->getHeaderPos( *i_column->asString() ); if ( colPos == CoreTable::noitem ) { // there isn't such field throw new AccessError( ErrorParam( e_tabcol_acc, __LINE__ ) .origin( e_orig_runtime ) .extra( *i_column->asString() ) ); } } else { colPos = (uint32) i_column->forceInteger(); if ( colPos >= table->order() ) { String temp; temp = "col "; temp.writeNumber( (int64) colPos ); throw new AccessError( ErrorParam( e_tabcol_acc, __LINE__ ) .origin( e_orig_runtime ) .extra( temp ) ); } } return colPos; } static void internal_get_item( CoreTable *table, CoreArray *row, VMachine *vm, Item *i_column ) { if ( i_column->isInteger() && (((uint32)i_column->asInteger()) == CoreTable::noitem) ) { vm->retval( row ); return; } uint32 colPos = internal_col_pos( table, vm, i_column ); // otherwise we have already an error risen. if ( colPos != CoreTable::noitem ) { Item ret = (*row)[colPos]; if ( ret.isNil() && ! ret.isOob() ) ret = *table->getHeaderData(colPos); // eventually methodize. if ( ret.isFunction() ) ret.setMethod( row, ret.asFunction() ); vm->retval( ret ); } } /*# @method get Table @brief Gets a row in a table. @param row a Row number. @optparam tcol The name of the column to be extracted (target column; either name or 0 based number). @return An array (if the column is not specified) or an item. The returned array is a "table component", and as such, its size cannot be changed; also, it inherits all the table clumns, that can be accessed as bindings with the dot accessor and will resolve in one of the element in the array. */ FALCON_FUNC Table_get ( ::Falcon::VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); Item* i_pos = vm->param(0); Item* i_column = vm->param(1); if ( i_pos == 0 || ! i_pos->isOrdinal() || ( i_column != 0 && ! (i_column->isString() || i_column->isOrdinal()) ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "N, [S|N]" ) ); } CoreArray* page = table->currentPage(); // Get the correct row. uint32 pos = (uint32) i_pos->forceInteger(); if ( pos >= page->length() ) { throw new AccessError( ErrorParam( e_arracc, __LINE__ ) .origin( e_orig_runtime ) .extra( Engine::getMessage( rtl_row_out_of_bounds ) ) ); } // Should we also get a single item? if( i_column == 0 ) { Item &itm = (*page)[pos]; fassert( itm.isArray() ); itm.asArray()->tablePos( pos ); vm->retval( itm ); } else { internal_get_item( table, (*page)[pos].asArray(), vm, i_column ); } } /*# @method set Table @brief Changes the value of a whole row. @param row a Row number. @param element The new data to be placed at the given row. This method changes an existing row placing a completely new value over it. */ FALCON_FUNC Table_set ( ::Falcon::VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); Item* i_pos = vm->param(0); Item* i_element = vm->param(1); if ( i_pos == 0 || ! i_pos->isOrdinal() || i_element == 0 || ! i_element->isArray() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "N,A" ) ); } CoreArray* element = i_element->asArray(); if ( element->length() != table->order() ) { throw new ParamError( ErrorParam( e_param_type, __LINE__ ) .origin( e_orig_runtime ) .extra( Engine::getMessage( rtl_invalid_tabrow ) ) ); } CoreArray* page = table->currentPage(); uint32 pos = (uint32)(i_pos->forceInteger()); if ( pos >= page->length() ) { throw new AccessError( ErrorParam( e_arracc, __LINE__ ) .origin( e_orig_runtime ) .extra( Engine::getMessage( rtl_row_out_of_bounds ) ) ); } page->at(pos) = element; element->table( vm->self().asObject() ); vm->retval( element ); } /*# @method columnPos Table @brief Returns the number of a given column name. @param column The column header name. @return The numeric position of the column or -1 if not found. */ FALCON_FUNC Table_columnPos ( ::Falcon::VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); Item* i_column = vm->param(0); if ( i_column != 0 && ! i_column->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S" ) ); } uint32 colpos = table->getHeaderPos( *i_column->asString() ); if ( colpos == CoreTable::noitem ) vm->regA().setInteger( -1 ); else vm->regA().setInteger( colpos ); } /*# @method columnData Table @brief Returns the column data bound with a certain column @param column The column header name or numeric position. @optparam data New data to be stored as column data. @return The column data for the given column, or nil is not found. If the @b data parameter is specified, then the value of the given column data is changed. Anyhow, the previous value is returned. Notice that the column data of an existing column may be nil; to know if a column with a given name exists, use the @a Table.columnPos method. */ FALCON_FUNC Table_columnData ( ::Falcon::VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); Item* i_column = vm->param(0); Item* i_data = vm->param(1); if ( i_column != 0 && ! i_column->isString() && ! i_column->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S" ) ); } uint32 colpos = internal_col_pos( table, vm, i_column ); if ( colpos == CoreTable::noitem ) vm->regA().setNil(); else vm->regA() = table->columnData( colpos ); if ( i_data != 0 ) { table->columnData( colpos, *i_data ); } } /*# @method find Table @brief Finds an element in a table. @param column The column where to perform the search (either name or 0 based number). @param value The value to be found. @optparam tcol The name of the column to be extracted (target column; either name or 0 based number). @optparam dflt A default value to be returned if the row is not found. @return An array (if the column is not specified) or an item. @raise TableError if the item is not found. The returned array is a "table component", and as such, its size cannot be changed; also, it inherits all the table columns, that can be accessed as bindings with the dot accessor and will resolve in one of the element in the array. In case of success, through the BOM method @a Array.tabRow it is possible to retrieve the table row position of the returned array. In case of failure, a TableError is raised, unless a @b dflt parameter is specified. */ FALCON_FUNC Table_find ( ::Falcon::VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); Item* i_column = vm->param(0); Item* i_value = vm->param(1); Item* i_tcol = vm->param(2); if ( i_column == 0 || ! ( i_column->isString() || i_column->isOrdinal() ) || i_value == 0 || ( i_tcol != 0 && ! (i_tcol->isString()|| i_tcol->isOrdinal() || i_tcol->isNil()) ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S|N,X,[S|N]" ) ); } CoreArray* page = table->currentPage(); uint32 pos = CoreTable::noitem; // the easy way // must be a future binding. find it. uint32 col = internal_col_pos( table, vm, i_column ); if ( col == CoreTable::noitem ) { // error already risen. return; } Item value = *i_value; // cache the item, as we may loose the stack for ( uint32 i = 0; i < page->length(); i++ ) { if( page->at(i).asArray()->at(col) == value ) { pos = i; break; } } if ( pos == CoreTable::noitem ) { // there isn't such field Item* i_dflt = vm->param(3); if( i_dflt != 0 ) { vm->retval( *i_dflt ); return; } throw new TableError( ErrorParam( e_search_eof, __LINE__ ) .origin( e_orig_runtime ) ); } // re-get the parameter, as it may has been changed. i_tcol = vm->param(2); // we know we have a valid pos here. if( i_tcol == 0 || i_tcol->isNil() ) { Item &itm = (*page)[pos]; fassert( itm.isArray() ); itm.asArray()->tablePos( pos ); vm->retval( itm ); } else { internal_get_item( table, (*page)[pos].asArray(), vm, i_tcol ); } } /*# @method insert Table @brief Insert a row in the table. @param row The position where to insert the row. @param element The row to be inserted. @raise AccessError if the position is out of range. @raise ParamError if the row is not an array with the same length of the table order. The element is inserted before the given position. If @b pos is greater or equal to the length of the table, the row will be inserted at end (added). If @b pos is negative, the row will be accessed backward, -1 being the last element (the row will be inserted before the last one). */ FALCON_FUNC Table_insert ( ::Falcon::VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); Item* i_pos = vm->param(0); Item* i_element = vm->param(1); if ( i_pos == 0 || ! ( i_pos->isOrdinal() || i_pos->isNil() ) || i_element == 0 || ! i_element->isArray() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "N,A" ) ); } CoreArray* element = i_element->asArray(); if ( element->length() != table->order() ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( Engine::getMessage( rtl_invalid_tabrow ) ) ); } CoreArray* page = table->currentPage(); uint32 pos; if ( i_pos->isNil() ) pos = page->length(); else { pos = (uint32) i_pos->forceInteger(); // insert a bottom? if( pos > page->length() ) pos = page->length(); } page->insert( Item(element), pos ); element->table( vm->self().asObject() ); } /*# @method append Table @brief Adds a row at the end of the current page in the table. @param element The row to be inserted. @raise ParamError if @b element is not an array with the same length of the table order. This method adds the element at the end of the page currently selected in this table. @note This method is equivalent to Table.insert with @b row set as nil. */ FALCON_FUNC Table_append ( ::Falcon::VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); Item* i_element = vm->param(0); if ( i_element == 0 || ! i_element->isArray() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "N,A" ) ); } CoreArray* element = i_element->asArray(); if ( element->length() != table->order() ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( Engine::getMessage( rtl_invalid_tabrow ) ) ); } CoreArray* page = table->currentPage(); page->insert( Item(element), page->length() ); element->table( vm->self().asObject() ); } /*# @method remove Table @brief Remove a row from the table. @param row The number of the row to be removed. @raise AccessError if the position is out of range. @return The removed array. This method removes one of the rows from the table. However, the array still remember the table from which it came from, as the table may have multiple reference to the array. So, the array stays bound with the table, and cannot be modified. */ FALCON_FUNC Table_remove ( ::Falcon::VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); Item* i_row = vm->param(0); if (i_row == 0 || ! i_row->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "N" ) ); } uint32 pos = (uint32) i_row->forceInteger(); CoreArray* page = table->currentPage(); if ( pos < 0 ) pos = page->length() - pos; if ( pos >= page->length() ) { throw new AccessError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) ); } CoreArray *rem = (*page)[pos].asArray(); //rem->table(0); page->remove( pos ); vm->retval(rem); } /*# @method setColumn Table @brief Change the title or column data of a column. @param column The number of name of the column to be renamed. @param name The new name of the column, or nil to let it unchanged. @optparam coldata The new column data. This method changes the column heading in a table. It may be also used to change the column-wide data. */ FALCON_FUNC Table_setColumn ( ::Falcon::VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); Item* i_column = vm->param(0); Item* i_name = vm->param(1); Item* i_value = vm->param(2); if (i_column == 0 || ! ( i_column->isOrdinal()|| i_column->isString()) || i_name == 0 || ! ( i_name->isNil() || i_name->isString()) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "N|S, Nil|S, [X]" ) ); } uint32 colpos = internal_col_pos( table, vm, i_column ); if ( colpos == CoreTable::noitem ) { return; } if ( ! i_name->isNil() ) { table->renameColumn( colpos, *i_name->asString() ); } if ( i_value != 0 ) { table->columnData( colpos ) = *i_value; } } /*# @method insertColumn Table @brief Inserts a column in a table. @param column The column name or position where to insert the column. @param name The name of the new column. @optparam coldata The column data for the new column. @optparam dflt Default value for the newly inserted columns. This method creates a new column in the table, inserting or adding a new heading, a new column data and an item in the corresponding column position of each array in the table. If @b dflt parameter is specified, that value is used to fill the newly created columns in the table rows, otherwise the new items will be nil. */ FALCON_FUNC Table_insertColumn ( ::Falcon::VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); Item* i_column = vm->param(0); Item* i_name = vm->param(1); Item* i_data = vm->param(2); Item* i_dflt = vm->param(3); if ( i_column == 0 || ! ( i_column->isOrdinal()|| i_column->isString()) || i_name == 0 || ! i_name->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "N|S, S, [X], [X]" ) ); } uint32 colpos; if ( i_column->isOrdinal() ) { colpos = i_column->forceInteger() < 0 ? (uint32) (table->order() + i_column->forceInteger()) : (uint32) (i_column->forceInteger()); if ( colpos > table->order() ) colpos = table->order(); } else { colpos = internal_col_pos( table, vm, i_column ); if ( colpos == CoreTable::noitem ) { // already raised. return; } } Item data, dflt; if ( i_data != 0 ) data = *i_data; if ( i_dflt != 0 ) dflt = *i_dflt; table->insertColumn( colpos, *i_name->asString(), data, dflt ); } /*# @method removeColumn Table @brief Removes a column from a table. @param column The column name or position to be removed. @raise AccessError if the column is not found. This method removes column in the table, removing also the item at corresponding position in all the rows of all the pages in this table. */ FALCON_FUNC Table_removeColumn ( ::Falcon::VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); Item* i_column = vm->param(0); if ( i_column == 0 || ! ( i_column->isOrdinal() || i_column->isString()) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "N|S" ) ); } if ( i_column->isOrdinal() && i_column->forceInteger() < 0 ) { i_column->setInteger( table->order() + i_column->forceInteger()); } uint32 colpos = internal_col_pos( table, vm, i_column ); if ( colpos == CoreTable::noitem ) { // already raised. return; } table->removeColumn( colpos ); } static bool table_choice_next( Falcon::VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); CoreArray &page = *table->currentPage(); uint32 start = (uint32) vm->local(0)->forceInteger(); uint32 row = (uint32) vm->local(1)->forceInteger(); uint32 end = (uint32) vm->local(2)->forceInteger(); // has the evaluation function just asked us to stop? if( vm->regA().isOob() ) { // in case of just row end, A can't be nil as the function has if( vm->regA().isNil() ) { vm->regA().setOob(false); } else { // get the winner CoreArray *winner = page[row-1].asArray(); winner->tablePos( row-1 ); internal_get_item( table, winner, vm, vm->local(3) ); } // we're done, don't call us anymore return false; } else if ( start != row ) { // we have a bidding uint32 pos = row - start; // a bit paranoid, but users may really screw up the table. if ( pos > table->biddingsSize() ) { throw new AccessError( ErrorParam( e_continue_out, __LINE__ ) .origin( e_orig_runtime ) .extra( Engine::getMessage( rtl_broken_table ) ) ); } numeric *biddings = table->biddings(); biddings[pos-1] = vm->regA().forceNumeric(); // is the bidding step over? if( pos == end - start) { uint32 maxrow = CoreTable::noitem; // our threshold is 0. If no item meets the requirement, we return nil numeric maxval = 0.0; // but take also the first row in the loop, to update maxval for ( pos = 0; pos < end - start; pos ++ ) { if( biddings[pos] > maxval ) { maxrow = pos + start; maxval = biddings[pos]; } } // no winner? if ( maxrow == CoreTable::noitem ) { vm->retnil(); } else { // we have a winner. CoreArray *winner = page[row-1].asArray(); winner->tablePos( row-1 ); internal_get_item( table, page[maxrow].asArray(), vm, vm->local(3) ); } // we're done, don't call us anymore return false; } } // nothing special to do: just call the desired function with our row. Item &rowItem = page[row]; rowItem.asArray()->tablePos(row); // save the position, in case is needed by our evaluator // update counter for next loop vm->local(1)->setInteger( row + 1 ); // do we have to call a function or an item in the row? Item &calling = *vm->local(4); if( calling.isInteger() ) { // colunn in the table. Item ret = page[row].asArray()->at( (uint32) calling.asInteger() ); if ( ret.isNil() && ! ret.isOob() ) ret = *table->getHeaderData((uint32) calling.asInteger()); // eventually methodize. if ( ret.isFunction() ) { ret.setMethod( page[row].asArray(), ret.asFunction() ); vm->callFrame( ret, 0 ); return true; } if ( ret.isCallable() ) { vm->pushParameter( page[row] ); vm->callFrame( ret, 1 ); return true; } // else, the item is not callable! Raise an error. // (it's ok also if we pushed the parameter; stack is unwinded). throw new AccessError( ErrorParam( e_non_callable, __LINE__ ) .origin( e_orig_runtime ) .extra( Engine::getMessage( rtl_uncallable_col ) ) ); } else { // function provided externally vm->pushParameter( page[row] ); vm->callFrame( calling, 1 ); } // call us again when the frame is done. return true; } static void internal_bind_or_choice( VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); Item* i_offer = vm->param(1); Item* i_rows = vm->param(2); CoreArray *cp = table->currentPage(); // if the table is empty, just return. if ( cp->length() == 0 ) { vm->retnil(); return; } uint32 start, end; if( i_rows == 0 ) { start = 0; end = cp->length(); } else { start = (uint32) (i_rows->asRangeStart() < 0 ? cp->length() + i_rows->asRangeStart() : i_rows->asRangeStart()); end = i_rows->asRangeIsOpen() ? cp->length() : (uint32) (i_rows->asRangeEnd() < 0 ? cp->length() + i_rows->asRangeEnd() : i_rows->asRangeEnd()); } if ( start == cp->length() || start >= end || start < 0 ) { vm->retnil(); return; } // performs also a preliminary check of column pos uint32 colpos; if( i_offer == 0 || i_offer->isNil() ) { colpos = CoreTable::noitem; } else { colpos = internal_col_pos( table, vm, i_offer ); if ( colpos == CoreTable::noitem ) { // error already raised return; } } // locals already allocated. vm->local(0)->setInteger( (int64) start ); // we need a copy vm->local(1)->setInteger( (int64) start ); vm->local(2)->setInteger( (int64) end ); vm->local(3)->setInteger( (int64) colpos ); table->reserveBiddings( end - start + 1); // returning from this frame will call table_choice_next vm->returnHandler( table_choice_next ); } /*# @method choice Table @brief Performs a choice between rows. @param func Choice function or callable. @optparam offer Offer column (number or name). @optparam rows Range of rows in which to perform the bidding. @return The winning row, or the corresponding value in the offer column. This method sends all the rows in the table as the sole parameter of a function which has to return a numeric value for each row. After all the rows have been processed, the row for which the called function had the highest value is returned. If the optional parameter @b offer is specified, then the item specified by that column number or name is returned instead. If two or more rows are equally evaluated by the choice function, only the first one is returned. The evaluation function may return a number lower than 0 to have the row effectively excluded from the evaluation. If all the rows are evaluated to have a value lower than zero, the function returns nil (as if the table was empty). The function may force an immediate selection by returning an out of band item. An out of band nil will force this method to return nil, and an out of band number will force the selection of the current row. If an @b offer parameter is specified, then the item in the corresponding column (indicated by a numeric index or by column name) is returned. A @b row range can be used to iterate selectively on one part of the table. If the table is empty, or if the given row range is empty, the function returns nil. @note Except for OOB nil, every return value coming from the choice function will be turned into a floating point numeric value. Non-numeric returns will be evaluated as 0.0. */ FALCON_FUNC Table_choice ( ::Falcon::VMachine *vm ) { Item* i_func = vm->param(0); Item* i_offer = vm->param(1); Item* i_rows = vm->param(2); if ( i_func == 0 || ! i_func->isCallable() || (i_offer != 0 && (! i_offer->isNil() && ! i_offer->isString() && ! i_offer->isOrdinal() )) || (i_rows != 0 && ! i_rows->isRange() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "C,[N|S],[R]" ) ); } // prepare local stack and function pointer vm->addLocals(5); *vm->local(4) = *vm->param(0); internal_bind_or_choice( vm ); } /*# @method bidding Table @brief Performs a bidding between rows. @param column Betting column (number or name). @optparam offer Offer column (number or name). @optparam rows Range of rows in which to perform the bidding. @return The winning row, or the corresponding value in the offer column. @raise AccessError if the table or ranges are empty. This method calls iteratively all the items in a determined column of the table, recording their return value, which must be numeric. It is allowed also to have numeric and nil values in the cells in the betting column. Numeric values will be considered as final values and will participate in the final auction, while row having nil values will be excluded. After the bidding is complete, the row offering the highest value is selected and returned, or if the @b offer parameter is specified, the corresponding value in the given column will be returned. @note If the bidding element is a plain function, it will be called as a method of the array, so "self" will be available. In all the other cases, the array will be passed as the last parameter. A @b rows range can be specified to limit the bidding to a part smaller part of the table. */ FALCON_FUNC Table_bidding ( ::Falcon::VMachine *vm ) { Item* i_column = vm->param(0); Item* i_offer = vm->param(1); Item* i_rows = vm->param(2); if ( i_column == 0 || ( ! i_column->isOrdinal() && ! i_column->isString() ) || (i_offer != 0 && (! i_offer->isNil() && ! i_offer->isString() && ! i_offer->isOrdinal() )) || (i_rows != 0 && ! i_rows->isRange() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "[N|S],[N|S],[R]" ) ); } CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); uint32 colpos = internal_col_pos( table, vm, i_column ); // wrong position? if( colpos == CoreTable::noitem ) return; // prepare local stack and data pointer vm->addLocals(5); vm->local(4)->setInteger( colpos ); internal_bind_or_choice( vm ); } /*# @method pageCount Table @brief Gets the number of pages in this table. @return Number of pages stored in this table. */ FALCON_FUNC Table_pageCount ( ::Falcon::VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); vm->retval( (int64) table->pageCount() ); } /*# @method setPage Table @brief Sets current active page. @param pageId The number of the selected page. All the tables are created with at least one page having ID = 0. */ FALCON_FUNC Table_setPage ( ::Falcon::VMachine *vm ) { Item* i_page = vm->param(0); if ( i_page == 0 || ! i_page->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "N" ) ); } CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); uint32 pcount = table->pageCount(); int64 reqPid = i_page->forceInteger(); uint32 pid = (uint32)(reqPid < 0 ? pcount + reqPid : reqPid); if ( ! table->setCurrentPage( pid ) ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( Engine::getMessage( rtl_no_page ) ) ); } } /*# @method curPage Table @brief Gets the current table page. @return The currently active table page. All the tables are created with at least one page having ID = 0. */ FALCON_FUNC Table_curPage ( ::Falcon::VMachine *vm ) { CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); vm->retval( (int64) table->currentPageId() ); } /*# @method insertPage Table @brief Inserts a new table page. @optparam pageId The position at which to insert the page. @optparam data an array of rows (arrays), each of which having length equal to table order. If @b pos is greater than the number of pages in the table, or not given, the page will be appended at the end. */ FALCON_FUNC Table_insertPage ( ::Falcon::VMachine *vm ) { Item* i_pos = vm->param(0); Item* i_data = vm->param(1); if ( (i_pos != 0 && ! ( i_pos->isOrdinal() || i_pos->isNil() )) || ( i_data != 0 && ! i_data->isArray() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "[N],[A]" ) ); } uint32 pos = i_pos == 0 || i_pos->isNil() ? CoreTable::noitem : (uint32) i_pos->forceInteger(); CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); if ( i_data == 0 ) { CoreArray* page = new CoreArray; table->insertPage( vm->self().asObject(), page, pos ); } else { CoreArray* page = i_data->asArray()->clone(); if ( ! table->insertPage( vm->self().asObject(), page, pos ) ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( Engine::getMessage( rtl_invalid_tabrow ) ) ); } } } /*# @method removePage Table @brief Removes a page. @param pageId The page to be removed. The table cannot exist without at least one page, and if the deleted page is the current one, the page 0 is selected. */ FALCON_FUNC Table_removePage ( ::Falcon::VMachine *vm ) { Item* i_pos = vm->param(0); if ( i_pos == 0 || ! i_pos->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "N" ) ); } CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); if ( ! table->removePage( (uint32) i_pos->forceInteger() ) ) { throw new AccessError( ErrorParam( e_arracc, __LINE__ ) .origin( e_orig_runtime ) .extra( Engine::getMessage( rtl_no_page ) ) ); } } /*# @method getPage Table @brief Returns a copy of a given page in the table. @optparam pageId The page to be copied (defaults to the current page). @return An array containing all the rows in the page. While the returned item is a copy, and modifying it doesn't cause the change the be reflected on the table, each row returned with it is the actual item stored in the table. So, modifying the Nth element of one of the arrays in the returned one will affect the page. */ FALCON_FUNC Table_getPage ( ::Falcon::VMachine *vm ) { Item* i_pos = vm->param(0); if ( i_pos != 0 && ! i_pos->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "[N]" ) ); } CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); uint32 pos = i_pos == 0 ? table->currentPageId() : (uint32) i_pos->forceInteger(); CoreArray* page = table->page( pos ); if ( page == 0 ) { throw new AccessError( ErrorParam( e_arracc, __LINE__ ) .origin( e_orig_runtime ) .extra( Engine::getMessage( rtl_no_page ) ) ); } vm->retval( page->clone() ); } /*# @method resetColumn Table @brief Applies a value on a column, eventually setting one or more rows to a different value. @param column Column name or number where the value must be applied. @optparam resetVal The value that must be set to clear the rows (defaults to nil). @optparam row The row (or rows, if a range is given) to be set to @b value. @optparam value The value that must be set in the given row(s) (defaults to true). This method takes a column in the current page and sets all the values in all the rows to @b resetVal (or nil if not given). Optionally, a single row or a range of rows where to apply a different value can be given. In example, this allows to create a diagonal matrix like the following: @code x = Table( ["a","b","c"] ) x.insert( 0, arrayBuffer( 3 ) ) x.insert( 0, arrayBuffer( 3 ) ) x.insert( 0, arrayBuffer( 3 ) ) for i in [0:3] x.resetColumn( i, 0, i, 1 ) end @endcode Tables are not meant for linear algebra calculus, but it is often useful to clear a whole column, and it can come useful to set just one row in a table to stand out, especially in decision support systems and program logic control, which is what Falcon tables are specifically designed. */ FALCON_FUNC Table_resetColumn ( ::Falcon::VMachine *vm ) { Item* i_col = vm->param(0); Item* i_value = vm->param(1); Item* i_row = vm->param(2); Item* i_resVal = vm->param(3); if ( i_col == 0 || !(i_col->isOrdinal() || i_col->isString() ) || (i_row != 0 && !(i_row->isOrdinal() || i_row->isRange() )) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "N|S,[X],[N|R],[X]" ) ); } CoreTable *table = static_cast( vm->self().asObject()->getUserData() ); uint32 colpos = internal_col_pos( table, vm, i_col ); // wrong position? if( colpos == CoreTable::noitem ) return; // first, reset all rows Item dummyNil; if ( i_value == 0 ) i_value = &dummyNil; CoreArray &page = *table->currentPage(); for ( uint32 i = 0; i < page.length(); i++ ) { page[i].asArray()->at(colpos) = *i_value; } if ( i_row == 0 ) return; // then, eventually sets the rows // doing this AFTER may seem a waste, but usually it's just a row being touched int32 start, end, step; if ( i_row->isRange() ) { start = (int32) i_row->asRangeStart(); if ( start < 0 ) start = page.length() + start; end = (int32)( i_row->asRangeIsOpen() ? page.length() : i_row->asRangeEnd()); if ( end < 0 ) end = page.length() + end; if ( end < start ) { int32 temp = start; start = end; // to compensate different closure semantics end = temp + 1; } step = (int32) i_row->asRangeStep(); if ( step == 0 ) step = 1; } else { start = (int32) i_row->forceInteger(); if ( start < 0 ) start = page.length() + start; end = start + 1; step = 1; } if ( ((uint32)start) >= page.length() || ((uint32)end) > page.length() ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) ); } Item dummyTrue; dummyTrue.setBoolean( true ); if ( i_resVal == 0 ) i_resVal = &dummyTrue; while( start < end ) { page[start].asArray()->at(colpos) = *i_resVal; start += step; } } } } /* end of table.cpp */ engine/core_module/time_ext.cpp000066400000000000000000000502421176363201700171530ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: time_ext.cpp Date and time support for RTL ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven nov 12 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ /** \file Date and time support for RTL. */ #include #include #include #include #include #include #include #include #include namespace Falcon { namespace core { /*# @class TimeStamp @brief Representation of times in the system. @param date Other TimeStamp instance from which to copy the new instance. The TimeStamp class can be used to retrieve the system time and date. It is also used by other entities in the RTL to return informations about the date (i.e. the @a FileStat). The instance is created empty and unset, unless the @b date parameter is provided. In that case, the new instance is copied from a previously created one. The provided parameter may also be a "long opaque format" generated by the @a TimeStamp.toLongFormat method. To update the instance with the current system time, use @a TimeStamp.currentTime @prop year Timestamp year, absolute value. @prop month Month of the year, starting from 1. @prop day Day of month, starting from 1. @prop hour Hour in range 0 - 23. @prop minute Minute in range 0 - 59. @prop second Second in range 0 - 59. @prop msec Millisecond in range 0 - 999. @prop timezone A timezone code (see @a TimeZone class). */ FALCON_FUNC TimeStamp_init ( ::Falcon::VMachine *vm ) { Item *date = vm->param(0); CoreObject *self = vm->self().asObject(); if ( date != 0 ) { if ( date->isInteger() ) { self->setUserData( new TimeStamp( date->asInteger() ) ); } else if ( date->isObject() ) { CoreObject *other = date->asObject(); if( !other->derivedFrom( "TimeStamp" ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "Parameter is not a TimeStamp" ) ); } self->setUserData( new TimeStamp( * static_cast( other->getUserData() ) ) ); } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "TimeStamp class init requires an integer or TimeStamp parameter" ) ); } } else self->setUserData( new TimeStamp ); } /*# @method currentTime TimeStamp @brief Fills this item with current time. Fills the value of the date with the current local time on the system. Timezone management is still not implemented. */ FALCON_FUNC TimeStamp_currentTime ( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); TimeStamp *ts = (TimeStamp *) self->getUserData(); Falcon::Sys::Time::currentTime( *ts ); vm->retval(self); } /*# @method toString TimeStamp @brief Converts the current timestamp to a string. @optparam format Date formatting pattern in @b strftime format. @return The formatted timestamp. Returns a string representation of the time stamp. The returned format is YYYY-MM-DD HH:MM:SS.mmm. This function is just meant as a basic way to represent timestamps on output. @note In version 0.8.x, The extra format parameter is internally passed to strftime() C standard function. Some compiler/C libraries may abort the program if the given format string is malformed. An internal re-implementation of the method will be available in the next versions; it will be granted to be compatible with strftime() and will offer falcon-specific formattings. @note Some specific extra formats available in 0.8.x: %q (milliseconds), %Q (zero-padded milliseconds) and %i (Internet format, RFC-2822). */ FALCON_FUNC TimeStamp_toString ( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); TimeStamp *ts = (TimeStamp *) self->getUserData(); Item *format = vm->param( 0 ); CoreString *str = new CoreString; if( format != 0 ) { if( ! format->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ).extra( "[S]" ) ); return; } if( ! ts->toString( *str, *format->asString() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ). extra( "Invalid TimeStamp format" ) ); return; } } else { ts->toString( *str ); } vm->retval( str ); } static void internal_add_dist( ::Falcon::VMachine *vm, int mode ) { CoreObject *self = vm->self().asObject(); TimeStamp *ts1, *ts2; ts1 = (TimeStamp *) self->getUserData(); Item *date = vm->param( 0 ); if ( date->isObject() ) { CoreObject *other = date->asObject(); if( !other->derivedFrom( "TimeStamp" ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ). extra( "not a TimeStamp" ) ); return; } ts2 = (TimeStamp *) date->asObject()->getUserData(); if ( mode == 0 ) ts1->add( *ts2 ); else ts1->distance( *ts2 ); vm->retval( self ); } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ). extra( "Not a TimeStamp" ) ); } } /*# @method add TimeStamp @brief Adds a date value to this value (altering it). @param timestamp A timestamp to add @return itself. Alters this timestamp by adding another one to itself. Years, months, days, hours, minutes, seconds and milliseconds from the parameters are added, and the target date is re-validated. To use this functionality without changing the contents of this instance, use the clone semantic: @code added = orig.clone().add(addend) @endcode */ FALCON_FUNC TimeStamp_add ( ::Falcon::VMachine *vm ) { internal_add_dist( vm, 0 ); } /*# @method distance TimeStamp @brief Determines the distance between this item and a target timestamp. @param timestamp The timestamp from which to calculate the distance. @return itself The parameter is subtracted from the current object; the number of days, hours, minutes, seconds and milliseconds between the two dates is stored in the current object. The values may be negative if the given timestamp parameter is greater than this object. To use this functionality without changing the contents of this instance, use the clone semantic: @code distance = currentDate.clone().distance( baseDate ) @endcode */ FALCON_FUNC TimeStamp_distance ( ::Falcon::VMachine *vm ) { internal_add_dist( vm, 1 ); } /*# @method isValid TimeStamp @brief Checks the validity of this TimeStamp. @return True if this timestamp represents a valid moment in time. Returns true if the data in the timestamp represents a valid date. The function takes into consideration leap years. */ FALCON_FUNC TimeStamp_isValid ( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); TimeStamp *ts = (TimeStamp *) self->getUserData(); vm->regA().setBoolean( ts->isValid() ); } /*# @method isLeapYear TimeStamp @brief Checks if the year in this TimeStamp is a LeapYear. @return True if this timestamp is in a leap year, false otherwise. Returns true if year member of this timestamp is leap, false otherwise. Calculation is reliable only for years past 1700. */ FALCON_FUNC TimeStamp_isLeapYear ( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); TimeStamp *ts = (TimeStamp *) self->getUserData(); vm->retval( ts->isLeapYear() ); } /*# @method dayOfWeek TimeStamp @brief Returns the weekday in which this TimeStamp falls. @return A number representing a day in the week. Returns the day of week calculated on this object. The returned number is in range 0 to 6 included, 0 being Sunday and 6 being Saturday. The function is reliable only for dates past January the first 1700. */ FALCON_FUNC TimeStamp_dayOfWeek ( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); TimeStamp *ts = (TimeStamp *) self->getUserData(); vm->retval( ts->dayOfWeek() ); } /*# @method dayOfYear TimeStamp @brief Returns the days passed since the beginning of the year in this TimeStamp @return A number of days in the year. Returns the day in the year represented by the current object. The returned number will range between 1 for January the first and 365 or 366 (if the current year is leap) for December the 31th. */ FALCON_FUNC TimeStamp_dayOfYear ( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); TimeStamp *ts = (TimeStamp *) self->getUserData(); vm->retval( ts->dayOfYear() ); } /*# @method toLongFormat TimeStamp @brief Returns a compressed date representation. @return A date in an opaque "long format". Returns a Falcon integer which contains packed binary data representing this object. The returned data is opaque and should not be used to compare different dates. */ FALCON_FUNC TimeStamp_toLongFormat ( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); TimeStamp *ts = (TimeStamp *) self->getUserData(); vm->retval( ts->toLongFormat() ); } /*# @method fromLongFormat TimeStamp @brief Sets this date using a compressed opaque "long format" data. Fills the data in this object using a binary packed data which can be stored in a Falcon integer value (64 bits). A valid value can be only obtained with the toLongFormat() method. This two methods are just meant for easier serialization; timestamps in long format cannot be compared or summed. */ FALCON_FUNC TimeStamp_fromLongFormat ( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); TimeStamp *ts = (TimeStamp *) self->getUserData(); Item *data = vm->param( 0 ); if ( ! data->isInteger() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ). extra( "Only integer parameter allowed" ) ); } ts->fromLongFormat( data->asInteger() ); } /*# @method compare TimeStamp @brief Compare another TimeStamp against this one. @param timestamp The TimeStamp to be compared. @return -1, 0 or 1. The given timestamp is compared to this object. If this object is greater than the target timestamp, 1 is returned; if it's smaller (before), -1 is returned. If the two timestamp are exactly the same, 0 is returned. */ FALCON_FUNC TimeStamp_compare ( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); TimeStamp *ts1, *ts2; ts1 = (TimeStamp *) self->getUserData(); Item *date = vm->param( 0 ); if ( date->isObject() ) { CoreObject *other = date->asObject(); if( other->derivedFrom( "TimeStamp" ) ) { ts2 = (TimeStamp *) date->asObject()->getUserData(); vm->retval( ts1->compare( *ts2 ) ); } else { // let the VM use the default algo. vm->retnil(); } } else { // let the VM use the default algo. vm->retnil(); } } /*# @method fromRFC2822 TimeStamp @brief Sets this date from a RFC 2822 string. @param sTimestamp A string containing a date in RFC 2822 format. @return True on success, false on failure. RFC 2822 format is the textual descriptive format used in Internet transactions. It's composed with: - Day of the week signature - Month signature - Day in the current month - 4 digits year - Time in HH:MM:SS format - Timezone name or displacement. A sample looks like: @code Thu, 01 May 2008 23:52:34 +0200 @endcode If the given string is not a valid timestamp in the RFC 2822 format, the function will return false. @note Part of this timestamp may be corrupted after a faulty try; be sure to save this TimeStamp before trying the conversion, if it is needed. */ FALCON_FUNC TimeStamp_fromRFC2822 ( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); Item *i_string = vm->param(0); if( i_string == 0 || ! i_string->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ). extra( "S" ) ); return; } TimeStamp *ts1 = (TimeStamp *) self->getUserData(); vm->regA().setBoolean( TimeStamp::fromRFC2822( *ts1, *i_string->asString() ) ); } /*# @method toRFC2822 TimeStamp @brief Format this TimeStamp in RFC 2822 format. @return A string with this timestamp converted, or nil if this TimeStamp is not valid. @see TimeStamp.fromRFC2822 */ FALCON_FUNC TimeStamp_toRFC2822 ( ::Falcon::VMachine *vm ) { CoreObject *self = vm->self().asObject(); TimeStamp *ts1 = (TimeStamp *) self->getUserData(); if ( ts1->isValid() ) { CoreString *str = new CoreString( String(32) ); ts1->toRFC2822( *str ); vm->retval( str ); } else vm->retnil(); } /*# @method changeZone TimeStamp @brief Change the time zone in this timestamp, maintaing the same absolute value. @param zone The new time zone. This methods shifts forward or backward this timestamp according with the relative shift between the @a TimeStamp.timezone member and the @b zone parameter. After the shift is performed, the new zone is set in the timezone property of this object. For example, to convert the local time in GMT: @code now = CurrentTime() > "Local time: ", now now.changeZone( TimeZone.GMT ) > "GMT: ", now @endcode As assigning a new time zone to the @b timezone property is not subject to any control, it is possible to set an arbitrary time and timezone by normal assignment, and then convert it to another time zone using this method. For example: @code a_gmt_time = decodeTime( "..." ) // let's say we know the timestamp is GMT. a_gmt_time.timezone = TimeZone.GMT // to convert in local time: localTime = a_gmt_time localTime.changeZone( TimeZone.local ) @endcode The "local" zone is a special zone which is automatically converted in the system timezone; it can also be directly assigned to a timestamp, but it's preferable to determine the system timezone through @a TimeZone.getLocal. @see TimeZone */ FALCON_FUNC TimeStamp_changeZone ( ::Falcon::VMachine *vm ) { Item *i_tz = vm->param(0); if( i_tz == 0 || ! i_tz->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ). extra( "N" ) ); return; } int tz = (int) i_tz->forceInteger(); if ( tz < 0 || tz >= 32 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ).origin( e_orig_runtime ). extra( "Invalid timezone" ) ); return; } CoreObject *self = vm->self().asObject(); TimeStamp *ts1 = (TimeStamp *) self->getUserData(); ts1->changeTimezone( (TimeZone) tz ); } /*# @function CurrentTime @brief Returns the current system local time as a TimeStamp instance. @return A new TimeStamp instance. Returns the current system local time as a TimeStamp instance. The function can never fail. */ FALCON_FUNC CurrentTime ( ::Falcon::VMachine *vm ) { // create the timestamp Item *ts_class = vm->findWKI( "TimeStamp" ); //if we wrote the std module, can't be zero. fassert( ts_class != 0 ); CoreObject *self = ts_class->asClass()->createInstance(); TimeStamp *ts = new TimeStamp; Falcon::Sys::Time::currentTime( *ts ); self->setUserData( ts ); vm->retval( self ); } /*# @function ParseRFC2822 @brief Parses a RFC2822 formatted date and returns a timestamp instance. @return A valid @a TimeStamp instance or nil if the format is invalid. @see TimeStamp.fromRFC2822 */ FALCON_FUNC ParseRFC2822 ( ::Falcon::VMachine *vm ) { // verify that the string is valid Item *i_string = vm->param(0); if( i_string == 0 || ! i_string->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ). extra( "S" ) ); return; } TimeStamp *ts1 = new TimeStamp; if( ! TimeStamp::fromRFC2822( *ts1, *i_string->asString() ) ) { delete ts1; vm->retnil(); return; } // create the timestamp Item *ts_class = vm->findWKI( "TimeStamp" ); //if we wrote the std module, can't be zero. fassert( ts_class != 0 ); CoreObject *self = ts_class->asClass()->createInstance(); self->setUserData( ts1 ); vm->retval( self ); } //================================================================== // Timezone /*# @class TimeZone @brief TimeZone enumeration and services. This class proves the list of managed timezones and various services needed to handle them. The enumerative part contains the following constants, representing west, east and some named timezones: - local: Special local zone (unassigned, but relative to current location). - UTC or GMT - E1 to E12 (+1h to +12h) - W1 to W12 (-1h to -12h) - EDT - EST - CDT - CST - MDT - MST - PDT - PST - NFT - ACDT - ACST - HAT - NST - NONE: No/neutral/unknown timezone. */ /*# @method getDisplacement TimeZone @brief Returns the distance in minutes from the GMT time of a given timezone @param tz A time zone code. @return A relative time distance in minutes. This static method, callable directly on the TimeZone class, returns the time displacement of a determined time zone with respect to GMT. */ FALCON_FUNC TimeZone_getDisplacement ( ::Falcon::VMachine *vm ) { // verify that the string is valid Item *i_tz = vm->param(0); if( i_tz == 0 || ! i_tz->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ). extra( "N" ) ); return; } int tz = (int) i_tz->forceInteger(); if ( tz < 0 || tz >= 32 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ).origin( e_orig_runtime ). extra( "Invalid timezone" ) ); return; } int16 hours, minutes; TimeStamp::getTZDisplacement( (TimeZone) tz, hours, minutes ); vm->retval( (int64) (hours *60 + minutes) ); } /*# @method describe TimeZone @brief Returns a descriptive string naming the required timezone. @param tz A time zone code. @return A timezone name. This static method, callable directly on the TimeZone class, returns a RFC 2822 compliant timezone name, given a timezone code. The "name" is in the format "+/-hhmm". */ FALCON_FUNC TimeZone_describe ( ::Falcon::VMachine *vm ) { Item *i_tz = vm->param(0); if( i_tz == 0 || ! i_tz->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ). extra( "N" ) ); return; } int tz = (int) i_tz->forceInteger(); if ( tz < 0 || tz >= 32 ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ).origin( e_orig_runtime ). extra( "Invalid timezone" ) ); return; } vm->retval( new CoreString( TimeStamp::getRFC2822_ZoneName( (TimeZone) tz ) ) ); } /*# @method getLocal TimeZone @brief Return local time zone code. @return A time zone code coresponding to the system local timezone. To get a descriptive name of local timezone, use: @code TimeZone.describe( TimeZone.getLocal() ) @endcode */ FALCON_FUNC TimeZone_getLocal ( ::Falcon::VMachine *vm ) { vm->retval( (int64) Sys::Time::getLocalTimeZone() ); } //================================================ // Reflection // void TimeStamp_timezone_rfrom(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { TimeStamp *ts = static_cast(user_data); property = (int64) ts->m_timezone; } void TimeStamp_timezone_rto(CoreObject *instance, void *user_data, Item &property, const PropEntry& ) { TimeStamp *ts = static_cast(user_data); ts->m_timezone = (TimeZone)(property.forceInteger()%32); } }} /* end of time_ext.cpp */ engine/core_module/tokenizer_ext.cpp000066400000000000000000000212161176363201700202260ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: tokenizer_ext.cpp Tokenizer class support. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 03 Feb 2009 22:58:56 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ #include "core_module.h" #include namespace Falcon { namespace core { /*# @class Tokenizer @brief Simple stream-oriented parser for efficient basic recognition of incoming data. @optparam seps A string representing the separators. @optparam options Tokenization options. @optparam tokLen Maximum length of returned tokens. @optparam source The string to be tokenized, or a stream to be read for tokens. The tokenizer class is meant to provide simple and efficient logic to parse incoming data (mainly, incoming from string). The source can also be set at a second time with the @a Tokenizer.parse method. @b seps defaults to " " if not given. The @b options parameter can be a binary combinations of the following values: - @b Tokenizer.groupsep: Groups different tokens into one. If not given, when a token immediately follows another, an empty field is returned. - @b Tokenizer.bindsep: Return separators inbound with their token. - @b Tokenizer.trim: trim whitespaces away from returned tokens. - @b Tokenizer.wsAsToken: Treat a sequence of whitespaces as a single token. - @b Tokenizer.retsep: Return separators as separate tokens. */ FALCON_FUNC Tokenizer_init ( ::Falcon::VMachine *vm ) { Item* i_separators = vm->param(0); Item* i_options = vm->param(1); Item* i_len = vm->param(2); Item* i_source = vm->param(3); if ( ( i_separators != 0 && ! ( i_separators->isString() || i_separators->isNil())) || ( i_options != 0 && !( i_options->isInteger() || i_options->isNil() ) ) || ( i_len != 0 && ! ( i_len->isNumeric() || i_len->isNil() ) ) || ( i_source != 0 && ( ! i_source->isString() && ( ! i_source->isObject() || ! i_source->asObjectSafe()->derivedFrom( "Stream" ) ))) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "[S],[I],[N],[S|Stream]" ) ); } // we must have separators String seps; if( i_separators && i_separators->isString() ) seps = *i_separators->asString(); // Then, we may have options: TokenizerParams params; if( i_options != 0 && i_options->isInteger() ) { uint32 opts = (uint32) i_options->asInteger(); if( (opts & TOKENIZER_OPT_GRROUPSEP) != 0 ) params.groupSep(); if( (opts & TOKENIZER_OPT_BINDSEP) != 0 ) params.bindSep(); if( (opts & TOKENIZER_OPT_TRIM) != 0 ) params.trim(); if( (opts & TOKENIZER_OPT_RSEP) != 0 ) params.returnSep(); if( (opts & TOKENIZER_OPT_WSISTOK) != 0 ) params.wsIsToken(); } // ... and a maximum length? if ( i_len != 0 && i_len->isNumeric() ) { params.maxToken( (int32) i_len->forceInteger() ); } CoreObject* self = vm->self().asObject(); // Finally determine the source. if ( i_source != 0 ) { // save it from GC ripping self->setProperty( "_source", *i_source ); if( i_source->isString() ) { self->setUserData( new Tokenizer( params, seps, *i_source->asString() ) ); } else { Stream* source = dyncast(i_source->asObject()->getFalconData() ); self->setUserData( new Tokenizer( params, seps, source, false ) ); if ( ! source->good() ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .origin( e_orig_runtime ) .sysError( (uint32) source->lastError() ) ); } } } else { self->setUserData( new Tokenizer( params, seps ) ); } } /*# @method parse Tokenizer @raise IoError on errors on the underlying stream. @brief Changes or set the source data for this tokenizer. @param source A string or a stream to be used as a source for the tokenizer. The first token is immediately read and set as the current token. If it's not empty, that is, if at least a token can be read, @a Tokenizer.hasCurrent returns true, and @a Tokenizer.token returns its value. */ FALCON_FUNC Tokenizer_parse ( ::Falcon::VMachine *vm ) { Item* i_source = vm->param(0); if ( i_source == 0 || ( ! i_source->isString() && ( ! i_source->isObject() || ! i_source->asObjectSafe()->derivedFrom( "Stream" ) )) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "S|Stream" ) ); } CoreObject* self = vm->self().asObject(); self->setProperty( "_source", *i_source ); Tokenizer* tzer = dyncast( self->getFalconData() ); if( i_source->isString() ) { tzer->parse( *i_source->asString() ); } else { Stream* source = dyncast(i_source->asObjectSafe()->getFalconData()); tzer->parse( source, false ); if ( ! source->good() ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .origin( e_orig_runtime ) .sysError( (uint32) source->lastError() ) ); } } } /*# @method rewind Tokenizer @brief Resets the status of the tokenizer. @raise IoError if the tokenizer is tokenizing a non-rewindable stream. */ FALCON_FUNC Tokenizer_rewind ( ::Falcon::VMachine *vm ) { Tokenizer* tzer = dyncast( vm->self().asObject()->getFalconData() ); tzer->rewind(); } /*# @method nextToken Tokenizer @brief Returns the next token from the tokenizer @return A string or nil at the end of the tokenization. @raise IoError on errors on the underlying stream. @raise CodeError if called on an unprepared Tokenizer. This method is actually a combination of @a Tokenizer.next followed by @a Tokenizer.token. Sample usage: @code t = Tokenizer( source|"A string to be tokenized" ) while (token = t.nextToken()) != nil > "Token: ", token end @endcode @note When looping, remember to check the value of the returned token against nil, as empty strings can be legally returned multiple times, and they are considered false in logic checks. */ FALCON_FUNC Tokenizer_nextToken ( ::Falcon::VMachine *vm ) { Tokenizer* tzer = dyncast( vm->self().asObject()->getFalconData() ); if( tzer->hasCurrent() ) { CoreString* ret = new CoreString( tzer->getToken() ); tzer->next(); ret->bufferize(); vm->retval( ret ); } else vm->retnil(); } /*# @method next Tokenizer @brief Advances the tokenizer up to the next token. @return True if a new token is now available, false otherwise. @raise IoError on errors on the underlying stream. @raise CodeError if called on an unprepared Tokenizer. For example: @code t = Tokenizer( source|"A string to be tokenized" ) while t.hasCurrent() > "Token: ", t.token() t.next() end @endcode @see Tokenizer.token */ FALCON_FUNC Tokenizer_next ( ::Falcon::VMachine *vm ) { Tokenizer* tzer = dyncast( vm->self().asObject()->getFalconData() ); vm->regA().setBoolean( tzer->next() ); } /*# @method token Tokenizer @brief Get the current token. @return True if a new token is now available, false otherwise. @raise IoError on errors on the underlying stream. @raise CodeError if called on an unprepared Tokenizer, or before next(). This method returns the current token. @see Tokenizer.nextToken @see Tokenizer.next */ FALCON_FUNC Tokenizer_token ( ::Falcon::VMachine *vm ) { Tokenizer* tzer = dyncast( vm->self().asObject()->getFalconData() ); if( ! tzer->empty() ) { CoreString* ret = new CoreString( tzer->getToken() ); ret->bufferize(); vm->retval( ret ); } else vm->retnil(); } /*# @method hasCurrent Tokenizer @brief Return true if the tokenizer has a current token. @return True if a token is now available, false otherwise. Contrarily to iterators, it is necessary to call this @a Tokenizer.next at least once before calling this method. @see Tokenizer.nextToken @see Tokenizer.next */ FALCON_FUNC Tokenizer_hasCurrent ( ::Falcon::VMachine *vm ) { Tokenizer* tzer = dyncast( vm->self().asObject()->getFalconData() ); vm->regA().setBoolean( tzer->hasCurrent() ); } } } /* end of iterator_ext.cpp */ engine/core_module/transcode_ext.cpp000066400000000000000000000116751176363201700202060ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: transcode_ext.cpp Transcoder api for rtl. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun ott 2 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ /** \file Transcoder api for rtl. */ #include #include #include #include #include #include #include "core_module.h" namespace Falcon { namespace core { /*# @funset core_transcoding_functions Transcoding functions @brief Functions needed to transcode texts into various character sets. Transcoding functions turns binary strings encoded in a format into Falcon strings, or conversely, they turn Falcon strings into binary encoded buffers. Used in combination with binary stream reads and write, this function allows full internationalization of script input and output. However, if the target stream is known to support safe reads and writes and to provide immediate access to the needed data, the @a Stream.setEncoding method is more efficient, as it doesn't need a temporary buffer to store the binary read data, or the binary data that has to be written. @beginset core_transcoding_functions */ /*# @function getSystemEncoding @brief Returns the "official" system encoding, if it matches with one known by Falcon. @return The system encoding name. This function will return the name under which Falcon knows the default system encoding. Using returned value, the program is able to create encoders that should be able to parse the data provided by system functions as directory scanning, or that is probably used as the main encoding for system related text files (i.e. configuration files). */ FALCON_FUNC getSystemEncoding ( ::Falcon::VMachine *vm ) { CoreString *res = new CoreString; GetSystemEncoding( *res ); vm->retval( res ); } /*# @function transcodeTo @brief Returns a binary buffer containing an encoded representation of a Falcon string. @param string Falcon string to be encoded. @param encoding Name of the encoding (as a string). @return On success, the transcoded string. @raise ParamError if the encoding is not known. In case the encoding name is not known, the function will raise a ParamError. */ FALCON_FUNC transcodeTo ( ::Falcon::VMachine *vm ) { Item *i_source = vm->param( 0 ); Item *i_encoding = vm->param( 1 ); if ( i_source == 0 || ( ! i_source->isString() && ! i_source->isMemBuf() ) || i_encoding == 0 || ! i_encoding->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra("S|M,[M]") ); } CoreString *res = new CoreString; String *source; String dummy; if ( i_source->isMemBuf() ) { source = &dummy; // using 0 as allocated, the buffer is considered static. dummy.adopt( (char *) i_source->asMemBuf()->data(), i_source->asMemBuf()->size(), 0 ); } else { source = i_source->asString(); } if ( ! TranscodeString( *source, *(i_encoding->asString()), *res ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } vm->retval( res ); } /*# @function transcodeFrom @brief Returns a Falcon string created by parsing the given one as a binary sequence of bytes. @param string Falcon string or MemBuf to be encoded. @param encoding Name of the encoding (as a string). @return On success, the transcoded string. @raise ParamError if the encoding is not known. In case the encoding name is not known, the function will raise a ParamError. The transcoding may also fail if the source data is not a valid sequence under the given encoding, and cannot be decoded. */ FALCON_FUNC transcodeFrom ( ::Falcon::VMachine *vm ) { Item *i_source = vm->param( 0 ); Item *i_encoding = vm->param( 1 ); if ( i_source == 0 || ( ! i_source->isString() && !i_source->isMemBuf() ) || i_encoding == 0 || ! i_encoding->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ).extra( "S|M,S" ) ); } CoreString *res = new CoreString; String *source; String dummy; if ( i_source->isMemBuf() ) { source = &dummy; // using 0 as allocated, the buffer is considered static. dummy.adopt( (char *) i_source->asMemBuf()->data(), i_source->asMemBuf()->size(), 0 ); } else { source = i_source->asString(); } if ( ! TranscodeFromString( *source, *(i_encoding->asString()), *res ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).origin( e_orig_runtime ) ); } vm->retval( res ); } } } /* end of transcode_ext.cpp */ engine/core_module/uri_ext.cpp000066400000000000000000000230731176363201700170160ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: uri_ext.cpp Falcon class reflecting URI class ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 27 Feb 2008 22:39:35 +0100 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ /** \file Falcon class reflecting URI class. */ #include #include #include #include #include "core_module.h" #include #include namespace Falcon { namespace core { CoreObject* UriObject::factory( const CoreClass *me, void *uri, bool ) { if ( uri != 0 ) return new UriObject( me, *static_cast( uri ) ); else return new UriObject( me ); } /*# @class URI @brief Interface to RFC3986 Universal Resource Indicator. @optparam path The URI that will be used as initial data. @optparam decode True if the path is URI encoded, and must be decoded (default). @raise ParamError in case the inital URI is malformed. This class offers an object oriented interface to access URI elements. Setting the properties in this class immediately reflects on the related fields; for example setting the value of the @b uri property causes a complete re-parse of the item; setting a field as the query string will cause the uri to change. Each update is subject to RFC3986 compliance checks, and will raise a ParseError if conformance of the URI object is broken. @prop scheme URI scheme. @prop userInfo User, password or account specification preceding '\@' host. @prop host Host specificator. @prop port Optional port specificator (following the host after a ':'). @prop path Path specificator. @prop query Query string in the URI. @prop fragment Fragment string in the uri (following path and query after a '#'). @prop uri Complete URI. */ UriObject::UriObject( const UriObject &other ): CoreObject( other ), m_uri( other.uri() ) { } UriObject::~UriObject() { } UriObject *UriObject::clone() const { return new UriObject( *this ); } bool UriObject::getProperty( const String &prop, Item &value ) const { if ( prop == "scheme" ) { value = new CoreString( uri().scheme() ); } else if ( prop == "userInfo" ) { value = new CoreString( uri().userInfo() ); } else if ( prop == "path" ) { value = new CoreString( uri().path() ); } else if ( prop == "host" ) { value = new CoreString( uri().host() ); } else if ( prop == "port" ) { value = new CoreString( uri().port() ); } else if ( prop == "query" ) { value = new CoreString( uri().query() ); } else if ( prop == "fragment" ) { value = new CoreString( uri().fragment() ); } else if ( prop == "uri" ) { value = new CoreString( uri().get(true) ); } else { return defaultProperty( prop, value ); } return true; } bool UriObject::setProperty( const String &prop, const Item &value ) { if ( prop == "scheme" ) { if ( ! value.isString() ) goto complain; uri().scheme( *value.asString() ); } else if ( prop == "userInfo" ) { if ( ! value.isString() ) goto complain; uri().userInfo( *value.asString() ); } else if ( prop == "path" ) { if ( ! value.isString() ) goto complain; uri().path( *value.asString() ); } else if ( prop == "host" ) { if ( ! value.isString() ) goto complain; uri().host( *value.asString() ); } else if ( prop == "port" ) { if ( ! value.isString() ) goto complain; uri().port( *value.asString() ); } else if ( prop == "query" ) { if ( ! value.isString() ) goto complain; uri().query( *value.asString() ); } else if ( prop == "fragment" ) { if ( ! value.isString() ) goto complain; uri().fragment( *value.asString() ); } else if ( prop == "uri" ) { if ( ! value.isString() ) goto complain; uri().parse( *value.asString() ); } else return false; if ( ! uri().isValid() ) { VMachine* vm = VMachine::getCurrent(); throw new AccessError( ErrorParam( e_param_range, __LINE__) .origin( e_orig_runtime ) .extra( vm != 0 ? vm->moduleString( rtl_invalid_uri ) : "" ) ); } return true; complain: throw new AccessError( ErrorParam( e_param_type, __LINE__) .origin( e_orig_runtime ) .extra( "S" ) ); } FALCON_FUNC URI_init ( ::Falcon::VMachine *vm ) { Item *p0 = vm->param(0); Item *i_parse = vm->param(1); // nothing to do if ( p0 == 0 ) return; UriObject *self = dyncast(vm->self().asObject()); // take the URI generated by the factory (can be empty). if ( ! p0->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ).extra( "[S]" ) ); } else { self->uri().parse( *p0->asString(), false, (i_parse == 0 || i_parse->isTrue()) ); if ( !self->uri().isValid() ) { throw new ParamError( ErrorParam( e_inv_params ). origin( e_orig_runtime ). extra( vm->moduleString( rtl_invalid_uri ) ) ); } } } /*# @method encode URI @brief Encode a string to URL encoding (static). @param string The string to be encoded. @return the URL/URI encoded string. */ FALCON_FUNC URI_encode ( ::Falcon::VMachine *vm ) { Item *p0 = vm->param(0); if ( ( p0 == 0 ) || ( ! p0->isString() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ).extra( "S" ) ); return; } CoreString *str = new CoreString; URI::URLEncode( *p0->asString(), *str ); vm->retval( str ); } /*# @method decode URI @brief Decode a string to from URL encoding (static). @param enc_string The URI/URL encoded string. @return The decoded string. @raise ParamError if the string is not a valid URI/URL encoded string. */ FALCON_FUNC URI_decode ( ::Falcon::VMachine *vm ) { Item *p0 = vm->param(0); if ( ( p0 == 0 ) || ( ! p0->isString() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ).extra( "S" ) ); return; } CoreString *str = new CoreString; if ( ! URI::URLDecode( *p0->asString(), *str ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ).extra( vm->moduleString( rtl_invalid_uri ) ) ); return; } vm->retval( str ); } /*# @method toString URI @brief Returns a string representing this URI. @return An URI encoded string containing a valid URI. Renders the URI into a valid string representation. @see URI.uri */ FALCON_FUNC URI_toString ( ::Falcon::VMachine *vm ) { UriObject *self = dyncast( vm->self().asObject() ); URI &uri = self->uri(); vm->retval( new CoreString( uri.get( true ) ) ); } /*# @method getFields URI @brief Returns fields contained in the query element into a dictionary. @return The fields as a dictionary of nil if the query part contains no element. @raise ParamError if the string is not a valid URI/URL encoded string. */ FALCON_FUNC URI_getFields ( ::Falcon::VMachine *vm ) { UriObject *self = dyncast( vm->self().asObject() ); URI &uri = self->uri(); if ( uri.query().size() == 0 ) { vm->retnil(); return; } if( uri.fieldCount() == 0 ) { // we have a query but no fields; this means we still have to parse it. if ( ! uri.parseQuery( true ) ) { // todo: better signalation throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ).extra( vm->moduleString( rtl_invalid_uri ) ) ); return; } // really nothing to parse? if ( uri.fieldCount() == 0 ) { vm->retnil(); return; } } // ok, build our dictionary uint32 count = uri.fieldCount(); CoreDict *dict = new CoreDict( new LinearDict( count ) ); CoreString *key = new CoreString; CoreString *value = new CoreString; uri.firstField( *key, *value ); count--; dict->put( key, value ); while( count > 0 ) { key = new CoreString; value = new CoreString; uri.nextField( *key, *value ); count --; dict->put( key, value ); } vm->retval( dict ); } /*# @method setFields URI @brief Sets query fields for this uri. @param fields A dictionary of fields or nil to clear the query. @raise ParamError if the input dictionary contains non-string values. */ FALCON_FUNC URI_setFields ( ::Falcon::VMachine *vm ) { UriObject *self = dyncast( vm->self().asObject() ); URI &uri = self->uri(); Item *p0 = vm->param(0); if ( ( p0 == 0 ) || ( ! p0->isDict() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ).extra( "S" ) ); return; } CoreDict *dict = p0->asDict(); Iterator iter( &dict->items() ); while( iter.hasCurrent() ) { if ( ( !iter.getCurrentKey().isString()) || (! iter.getCurrent().isString() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ).extra( "S" ) ); return; } uri.setField( *iter.getCurrentKey().asString(), *iter.getCurrent().asString() ); iter.next(); } uri.makeQuery(); } } } /* end of uri_ext.cpp */ engine/core_module/vminfo_ext.cpp000066400000000000000000000203141176363201700175100ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vminfo_ext.cpp ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 14 Aug 2008 00:31:21 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "core_module.h" #include #include /*# @beginmodule core */ namespace Falcon { namespace core { /*# @funset vminfo Virtual Machine Informations @brief Generic informations on the Virtual Machine. This functions are meant to provide minimal informations about the virtual machine and its configuration. For example, they provide the VM version number and target architectures. */ /*# @function vmVersionInfo @inset vminfo @brief Returns an array containing VM version informations. @return Major, minor and revision numbers of the running virtual machine in a 3 elements array. */ FALCON_FUNC vmVersionInfo( ::Falcon::VMachine *vm ) { CoreArray *ca = new CoreArray( 3 ); ca->append( (int64) ((FALCON_VERSION_NUM >> 16)) ); ca->append( (int64) ((FALCON_VERSION_NUM >> 8) & 0xFF) ); ca->append( (int64) ((FALCON_VERSION_NUM ) & 0xFF) ); vm->retval( ca ); } /*# @function vmModuleVersionInfo @inset vminfo @brief Returns an array containing current module version informations. @return Major, minor and revision numbers of the curerntly being executed module, in a 3 elements array. */ FALCON_FUNC vmModuleVersionInfo( ::Falcon::VMachine *vm ) { CoreArray *ca = new CoreArray( 3 ); int major=0, minor=0, revision=0; // we don't want our current (core) module version info... StackFrame *thisFrame = vm->currentFrame(); if( thisFrame->prev() != 0 ) { StackFrame *prevFrame = thisFrame->prev(); if ( prevFrame->m_module != 0 ) { prevFrame->m_module->module()->getModuleVersion( major, minor, revision ); } } ca->append( (int64) major ); ca->append( (int64) minor ); ca->append( (int64) revision ); vm->retval( ca ); } /*# @function vmVersionName @inset vminfo @brief Returns the nickname for this VM version. @return A string containing the symbolic name of this VM version. */ FALCON_FUNC vmVersionName( ::Falcon::VMachine *vm ) { CoreString *str = new CoreString( FALCON_VERSION " (" FALCON_VERSION_NAME ")" ); vm->retval( str ); } /*# @function vmSystemType @inset vminfo @brief Returns a descriptive name of the overall system architecture. @return A string containing a small descriptiuon of the system architecture. Currently, it can be "WIN" on the various MS-Windows flavours and POSIX on Linux, BSD, Solaris, Mac-OSX and other *nix based systems. */ FALCON_FUNC vmSystemType( ::Falcon::VMachine *vm ) { CoreString *str = new CoreString( Sys::SystemData::getSystemType() ); vm->retval( str ); } /*# @function vmIsMain @inset vminfo @brief Returns true if the calling module is the main module of the application. @return True if the calling module is the main module. This function checks if the current module has been added as the last one right before starting an explicit execution of the virtual machine from the outside. This function is useful for those modules that have a main code which is meant to be executed at link time and a part that is menat to be executed only if the module is directly loaded and executed. For example: @code // executes this at link time prtcode = printl // executes this from another module on request function testPrint() prtcode( "Success." ) end export testPrint // performs a test if directly loaded if vmIsMain() > "Testing the testPrint function" testPrint() end @endcode */ FALCON_FUNC vmIsMain( ::Falcon::VMachine *vm ) { StackFrame *thisFrame = vm->currentFrame(); if ( thisFrame == 0 ) { throw new GenericError( ErrorParam( e_stackuf, __LINE__ ).origin( e_orig_runtime ) ); } else { // get the calling symbol module vm->retval( (bool) (thisFrame->m_module == vm->mainModule() ) ); } } /*# @function vmFalconPath @inset vminfo @brief Returns default system path for Falcon load requests. @return The default compiled-in load path, or the value of the environemnt variable FALCON_LOAD_PATH if defined. */ FALCON_FUNC vmFalconPath( ::Falcon::VMachine *vm ) { String envpath; bool hasEnvPath = Sys::_getEnv( "FALCON_LOAD_PATH", envpath ); if ( hasEnvPath ) { vm->retval( new CoreString( envpath ) ); } else { vm->retval( new CoreString( FALCON_DEFAULT_LOAD_PATH ) ); } } /*# @function vmSearchPath @inset vminfo @brief Returns the application specific load path. @return A module search path as set by the application when creating the virtual machine. This string is at disposal of the embeddign application (or of the Falcon command line interpreter) to communicate to scripts and underlying users the search path set at applicaiton level. It is used by internal services, the @a include function, the compiler Feather module and similar facilities. */ FALCON_FUNC vmSearchPath( ::Falcon::VMachine *vm ) { vm->retval( new CoreString( vm->appSearchPath() ) ); } /*# @function vmModuleName @inset vminfo @brief Returns the logical name of this module. @return Logical name of this module. Every module has a logical name inside the application. There is a loose mapping between the underlying module providers and the logical name of the module, so knowing it may be helpful. */ FALCON_FUNC vmModuleName( ::Falcon::VMachine *vm ) { const Symbol* sym; const Module* mod; vm->getCaller( sym, mod ); vm->retval( new CoreString( mod->name() )); } /*# @function vmModuleLine @inset vminfo @brief Returns the number of the line it has been called at. @return An integer that represents a line number. */ FALCON_FUNC vmModuleLine( ::Falcon::VMachine *vm ) { const Symbol* sym; uint32 line; uint32 pc; vm->getTraceStep( 0, sym, line, pc ); vm->retval( (int64)line ); } /* @function vmModulePath @inset vminfo @brief Returns the phisical path (complete URI) from which this module was loaded. @return A string representing the load URI that individuates this module. */ FALCON_FUNC vmModulePath( ::Falcon::VMachine *vm ) { const Symbol* sym; const Module* mod; vm->getCaller( sym, mod ); vm->retval( new CoreString( mod->path() )); } /* @function vmRelativePath @inset vminfo @param path A relative (or absolute) path to a target file. @brief Relativize the URI of a given file to the path of the current module. @return A string containing the relativized URI. This function is meant to simplify the task of loading dynamic components in falcon libraries. The static loader has enough information to form a correct script dependency tree, but a library may be placed in unexpected loactions at target site, and then it would be complex to dynamcally load components. Suppose a library has a module called plugin.fal in the subdirectory modules/ of its directory structure. To load it, this function can be used to virtualize the load path so that @code modpath = vmRelativePath( "modules/plugin.fal" ) include( modpath ) @endcode */ FALCON_FUNC vmRelativePath( ::Falcon::VMachine *vm ) { Item *i_path = vm->param(0); if( i_path == 0 || ! i_path->isString()) { throw new Falcon::ParamError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ). extra( "S" ) ); } const Symbol* sym; const Module* mod; Path path( *i_path->asString() ); vm->getCaller( sym, mod ); URI uri( mod->path() ); if ( path.isAbsolute() ) uri.pathElement().setLocation( path.getLocation() ); else if ( path.getLocation() != "" ) uri.pathElement().extendLocation( path.getLocation() ); uri.pathElement().setFilename( path.getFilename() ); CoreString* ret = new CoreString( uri.get(false) ); ret->bufferize(); vm->retval( ret ); } } } /* end of vminfo_ext.cpp */ engine/core_module/webhelp.cpp000066400000000000000000000100301176363201700167520ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: webhelp.cpp Helpers for web applications ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 20 Jun 2010 17:12:56 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginmodule core */ #include "core_module.h" #include namespace Falcon { namespace core { /*# @class Base64 @brief Collection of methods to handle rfc3548 Base64 encoding. */ /*# @method encode Base64 @brief Transforms an input string or MemBuf into a Base64 encoded string. @param data The data to be encoded. @return The encoded string. This @b static method encodes the contents of the incoming data as a @b base64 encoded string. The rfc3548 doesn't define the encoding of the input data, as base64 is a method to encode generic binary data and send them across the Internet. However, it's common practice to encode textual contents as utf-8 strings and then apply the base64 encoding. This method automatically uses utf-8 encoding to transform strings with international characters. If this is not desired, provide a MemBuf as the parameter. */ FALCON_FUNC Base64_encode( VMachine* vm ) { Item* i_data = vm->param(0); if( i_data == 0 || ! (i_data->isMemBuf() || i_data->isString()) ) { throw new ParamError( ErrorParam( e_inv_params ). extra("S|M") ); } CoreString* cs = new CoreString; if( i_data->isString() ) { Base64::encode( *i_data->asString(), *cs ); } else { Base64::encode( i_data->asMemBuf()->data(), i_data->asMemBuf()->size(), *cs ); } vm->retval(cs); } /*# @method decode Base64 @brief Decodes a previously encoded text data. @param data The data to be decoded. @raise ParseError if the incoming data is not a correct base64 string. @return The original string (as an international text). This @b static method decodes the contents of the incoming data as a @b base64 encoded string into a Falcon text-oriented String. The rfc3548 doesn't define the encoding of the input data, as base64 is a method to encode generic binary data and send them across the Internet. However, it's common practice to encode textual contents as utf-8 strings and then apply the base64 encoding. So, this method supposes that the data, to be transformed in a string, is actually an utf-8 representation of a text. If this is not desired, use the Base64.decmb method. */ FALCON_FUNC Base64_decode( VMachine* vm ) { Item* i_data = vm->param(0); if( i_data == 0 || ! i_data->isString() ) { throw new ParamError( ErrorParam( e_inv_params ). extra("S") ); } CoreString* cs = new CoreString; if( ! Base64::decode( *i_data->asString(), *cs ) ) { cs->mark(1); throw new ParseError( ErrorParam( e_parse_format, __LINE__ ) ); } vm->retval(cs); } /*# @method decmb Base64 @brief Decodes a previously encoded binary data. @param data The data to be decoded. @raise ParseError if the incoming data is not a correct base64 string. @return The origianl data, as a binary sequence of bytes. This @b static method decodes the contents of the incoming data as a @b base64 encoded string into a binary buffer. */ FALCON_FUNC Base64_decmb( VMachine* vm ) { Item* i_data = vm->param(0); if( i_data == 0 || ! i_data->isString() ) { throw new ParamError( ErrorParam( e_inv_params ). extra("S") ); } const String& s = *i_data->asString(); uint32 tgtsize = s.length() / 4*3+3; byte* tgt = (byte*) memAlloc( tgtsize ); if ( ! Base64::decode( *i_data->asString(), tgt, tgtsize ) ) { memFree( tgt ); throw new ParseError( ErrorParam( e_parse_format, __LINE__ ) ); } MemBuf_1* mb = new MemBuf_1( tgt, tgtsize, memFree ); vm->retval(mb); } } } /* end of webhelp.cpp */ engine/corearray.cpp000066400000000000000000000245161176363201700150340ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: corearray.cpp Language level array implementation ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab dic 4 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Implementation of core arrays. Core arrays are meant to hold item pointers. */ #include #include #include #include #include #include #include namespace Falcon { CoreArray::CoreArray(): m_bindings(0), m_table(0), m_tablePos(0) { m_itemarray.owner( this ); } CoreArray::CoreArray( const CoreArray& other ): m_itemarray( other.m_itemarray ), m_table( other.m_table ), m_tablePos( other.m_tablePos ) { m_itemarray.owner( this ); if ( other.m_bindings != 0 ) { m_bindings = static_cast( other.m_bindings->clone() ); m_bindings->gcMark( mark() ); } else m_bindings = 0; } CoreArray::CoreArray( uint32 prealloc ): m_itemarray( prealloc ), m_bindings(0), m_table(0), m_tablePos(0) { m_itemarray.owner( this ); } CoreArray::CoreArray( Item *buffer, uint32 size, uint32 alloc ): m_itemarray( buffer, size, alloc ), m_bindings(0), m_table(0), m_tablePos(0) { m_itemarray.owner( this ); } CoreArray::~CoreArray() { } const String& CoreArray::name() const { static String name( "Array" ); return name; } void CoreArray::readyFrame( VMachine* vm, uint32 paramCount ) { vm->prepareFrame( this, paramCount ); vm->currentContext()->fself() = this; } CoreDict *CoreArray::makeBindings() { if ( m_bindings == 0 ) { m_bindings = new CoreDict( new LinearDict( ) ); m_bindings->put( new CoreString( "self" ), this ); m_bindings->gcMark( mark() ); } return m_bindings; } Item* CoreArray::getProperty( const String &name ) { Item *found = 0; if ( m_bindings != 0 ) { found = m_bindings->find( name ); if ( found ) return found->dereference(); } // we didn't find it. if ( m_table ) { CoreTable *table = reinterpret_cast( m_table->getFalconData() ); uint32 pos = table->getHeaderPos( name ); if ( pos != CoreTable::noitem ) { found = (*this)[pos].dereference(); if ( found->isNil() && ! found->isOob() ) found = table->getHeaderData( pos )->dereference(); } } return found; } void CoreArray::setProperty( const String &name, const Item &data ) { if ( m_bindings != 0 ) { Item* found = m_bindings->find( name ); if ( found != 0 ) { *found = data; return; } } if ( m_table != 0 ) { CoreTable *table = reinterpret_cast( m_table->getFalconData() ); uint32 pos = table->getHeaderPos( name ); if ( pos != CoreTable::noitem ) { *(*this)[pos].dereference() = data; return; } } m_bindings = makeBindings(); Item ref; VMachine* vm = VMachine::getCurrent(); if ( vm ) vm->referenceItem( ref, *const_cast(&data) ); m_bindings->put( new CoreString( name ), ref ); } void CoreArray::readProperty( const String &prop, Item &item ) { Item *p = getProperty( prop ); if ( p == 0 ) { // try to find a generic method VMachine* vm = VMachine::getCurrent(); fassert( vm != 0); CoreClass* cc = vm->getMetaClass( FLC_ITEM_ARRAY ); uint32 id; if ( cc == 0 || ! cc->properties().findKey( prop, id ) ) { String extra( "Array." ); extra.A( prop ); throw new AccessError( ErrorParam( e_prop_acc, __LINE__ ).extra( extra ) ); } p = cc->properties().getValue( id ); fassert( ! p->isReference() ); } item = *p->dereference(); item.methodize( this ); // may fail but it's ok } void CoreArray::writeProperty( const String &prop, const Item &item ) { setProperty( prop, *item.dereference() ); } void CoreArray::readIndex( const Item &index, Item &target ) { switch ( index.type() ) { case FLC_ITEM_INT: { register int32 pos = (int32) index.asInteger(); if ( pos < 0 ) { if ( -pos <= (int32) length() ) { target = m_itemarray[length()+pos]; return; } } else { if( pos < (int) length() ) { target = m_itemarray[pos]; return; } } } break; case FLC_ITEM_NUM: { register int32 pos = (int32) index.asNumeric(); if ( pos < 0 ) { if ( -pos <= (int32) length() ) { target = m_itemarray[length()+pos]; return; } } else { if( pos < (int) length() ) { target = m_itemarray[pos]; return; } } } break; case FLC_ITEM_RANGE: { // open ranges? if ( index.asRangeIsOpen() && ((index.asRangeStart() >= 0 && (int) length() <= index.asRangeStart() ) || (index.asRangeStart() < 0 && (int) length() < -index.asRangeStart() )) ) { target = new CoreArray(); return; } register int32 end = (int32)(index.asRangeIsOpen() ? length() : index.asRangeEnd()); CoreArray* array = partition( (int32) index.asRangeStart(), end ); if ( array != 0 ) { target = array; return; } } break; case FLC_ITEM_REFERENCE: readIndex( index.asReference()->origin(), target ); return; } throw new AccessError( ErrorParam( e_arracc, __LINE__ ).extra( "LDP" ) ); } CoreArray* CoreArray::partition( int32 start, int32 end ) const { int32 size; Item *buffer; if ( start < 0 ) start = m_itemarray.m_size + start; if ( start < 0 || start >= (int32) m_itemarray.m_size ) return 0; if ( end < 0 ) end = m_itemarray.m_size + end; if ( end < 0 || end > (int32) m_itemarray.m_size ) return 0; if( end < start ) { size = start - end + 1; buffer = (Item *) memAlloc( m_itemarray.esize( size ) ); for( int i = 0; i < size; i ++ ) buffer[i] = m_itemarray.m_data[start - i]; } else { if( end == start ) { return new CoreArray; } size = end - start; buffer = (Item *) memAlloc( m_itemarray.esize( size ) ); memcpy( buffer, m_itemarray.m_data + start, m_itemarray.esize( size ) ); } return new CoreArray( buffer, size, size ); } CoreArray *CoreArray::clone() const { return new CoreArray( *this ); } void CoreArray::writeIndex( const Item &index, const Item &target ) { switch ( index.type() ) { case FLC_ITEM_INT: { register int32 pos = (int32) index.asInteger(); if ( pos < 0 ) { if ( -pos <= (int32) length() ) { if ( target.isString() ) m_itemarray[length()+pos] = new CoreString( *target.asString() ); else m_itemarray[length()+pos] = target; return; } } else { if( pos < (int) length() ) { if ( target.isString() ) m_itemarray[pos] = new CoreString( *target.asString() ); else m_itemarray[pos] = target; return; } } } break; case FLC_ITEM_NUM: { register int32 pos = (int32) index.asNumeric(); if ( pos < 0 ) { if ( -pos <= (int32) length() ) { if ( target.isString() ) m_itemarray[length()+pos] = new CoreString( *target.asString() ); else m_itemarray[length()+pos] = target; return; } } else { if( pos < (int) length() ) { if ( target.isString() ) m_itemarray[pos] = new CoreString( *target.asString() ); else m_itemarray[pos] = target; return; } } } break; case FLC_ITEM_RANGE: { int32 end = (int32)(index.asRangeIsOpen() ? length() : index.asRangeEnd()); int32 start = (int32) index.asRangeStart(); const Item *tgt = target.dereference(); if( tgt->isArray() ) { if( change( *target.asArray(), (int32)start, (int32)end ) ) return; } else { // if it's not a plain insert... if ( start != end ) { // before it's too late. if ( start < 0 ) start = length() + start; if ( end < 0 ) end = length() + end; if( ! remove( start, end ) ) throw new AccessError( ErrorParam( e_arracc, __LINE__ ).extra( "STP" ) ); if ( start > end ) start = end; } if ( tgt->isString() ) { if ( insert( new CoreString( *tgt->asString() ), start ) ) return; } else { if( insert( *tgt, start ) ) return; } } } break; case FLC_ITEM_REFERENCE: writeIndex( index.asReference()->origin(), target ); return; } throw new AccessError( ErrorParam( e_arracc, __LINE__ ).extra( "STP" ) ); } void CoreArray::gcMark( uint32 gen ) { CoreArray *array = this; if( array->mark() != gen ) { array->mark(gen); array->items().gcMark(gen); // mark also the bindings if ( array->bindings() != 0 ) { array->bindings()->gcMark( gen ); } // and also the table if ( array->table() != 0 && array->table()->mark() != gen ) { array->table()->gcMark( gen ); } } } } /* end of corearray.cpp */ engine/coreclass.cpp000066400000000000000000000063411176363201700150170ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: coreclass.cpp Core Class implementation ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu Jan 20 2005 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file CoreClass implementation */ #include #include #include #include namespace Falcon { CoreClass::CoreClass( const Symbol *sym, LiveModule *lmod, PropertyTable *pt ): Garbageable(), m_lmod( lmod ), m_sym( sym ), m_properties( pt ), m_factory(sym->getClassDef()->factory()), m_states( 0 ), m_initState( 0 ), m_bHasInitEnter( false ) { } bool CoreClass::derivedFrom( const String &className ) const { // is this class? if ( m_sym->name() == className ) return true; // else, try with the properties/inheritance for( uint32 i = 0; i < properties().added(); ++i ) { const Item& p = *properties().getValue(i)->dereference(); if( p.isClass() && p.asClass()->derivedFrom( className ) ) { return true; } } return false; } bool CoreClass::derivedFrom( const Symbol *sym ) const { // is this class? /*if ( m_sym == sym || m_sym->name() == sym->name() ) return true; */ if ( m_sym == sym ) return true; // else, try with the properties/inheritance for( uint32 i = 0; i < properties().added(); ++i ) { const Item& p = *properties().getValue(i)->dereference(); if( p.isClass() && p.asClass()->derivedFrom( sym ) ) { return true; } } return false; } CoreClass::~CoreClass() { delete m_properties; delete m_states; } void CoreClass::gcMark( uint32 gen ) { // first, mark ourselves. if ( gen != mark() ) { mark( gen ); // then mark our items, memPool->markItem( m_constructor ); for( uint32 i = 0; i < properties().added(); i++ ) { // ancestors are in the property table as classItems memPool->markItem( *properties().getValue(i) ); } // and our states if( m_states != 0 ) { m_states->gcMark( gen ); } // and our module m_lmod->gcMark( gen ); } } void CoreClass::states( ItemDict* sd, ItemDict* is ) { delete m_states; m_states = sd; // have we got an init state? m_initState = is; String name("__enter"); if( is != 0 && is->find( &name ) ) m_bHasInitEnter = true; } CoreObject *CoreClass::createInstance( void *userdata, bool bDeserial ) const { if ( m_sym->isEnum() ) { throw new CodeError( ErrorParam( e_noninst_cls, __LINE__ ) .extra( m_sym->name() ) ); // anyhow, flow through to allow user to see the object } // The core object will self configure, // eventually calling the user data constructor and creating the property vector. CoreObject *instance = m_factory( this, userdata, bDeserial ); if( m_initState != 0 ) { instance->setState( "init", m_initState ); } return instance; } } /* end of coreclass.cpp */ engine/coredict.cpp000066400000000000000000000125541176363201700146400ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: cdict.cpp Core dictionary common functions. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 04 Jan 2009 09:49:26 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include namespace Falcon { bool CoreDict::getMethod( const String &name, Item &mth ) { if ( m_blessed ) { Item* found = find( name ); if ( found != 0 ) { mth = *found; return mth.methodize( SafeItem( this ) ); } } return false; } Item *CoreDict::find( const String &key ) const { return find( const_cast(&key) ); } void CoreDict::readProperty( const String &prop, Item &item ) { if( m_blessed ) { Item *method; if ( ( method = find( prop ) ) != 0 ) { item = *method->dereference(); item.methodize( this ); // may fail but it's ok return; } } // try to find a generic method VMachine *vm = VMachine::getCurrent(); if( vm != 0 ) { CoreClass* cc = vm->getMetaClass( FLC_ITEM_DICT ); uint32 id; if ( cc == 0 || ! cc->properties().findKey( prop, id ) ) { String extra( "Dictionary." ); extra.A( prop ); throw new AccessError( ErrorParam( e_prop_acc, __LINE__ ).extra( extra ) ); } item = *cc->properties().getValue( id ); item.methodize( this ); } } void CoreDict::writeProperty( const String &prop, const Item &item ) { if( m_blessed ) { Item *method; if ( ( method = find( prop ) ) != 0 ) { *method = *item.dereference(); return; } } String extra( "Dictionary." ); extra.A( prop ); throw new AccessError( ErrorParam( e_prop_acc, __LINE__ ).extra( extra ) ); } void CoreDict::readIndex( const Item &pos, Item &target ) { if( m_blessed ) { Item *method; if ( (method = find( OVERRIDE_OP_GETINDEX ) ) != 0 ) { Item mth = *method; if ( mth.methodize(this) ) { VMachine* vm = VMachine::getCurrent(); if( vm != 0 ) { vm->pushParam( pos ); vm->callItemAtomic( mth, 1 ); } return; } } } if( ! find( *pos.dereference(), target ) ) { throw new AccessError( ErrorParam( e_arracc, __LINE__ ) ); } } void CoreDict::writeIndex( const Item &pos, const Item &target ) { if( m_blessed ) { Item *method; if ( (method = find( OVERRIDE_OP_SETINDEX ) ) != 0 ) { Item mth = *method; if ( mth.methodize(this) ) { VMachine* vm = VMachine::getCurrent(); if( vm != 0 ) { vm->pushParam( pos ); vm->pushParam( target ); vm->callItemAtomic( mth, 2 ); return; } } } } const Item *tgt = target.dereference(); if( tgt->isString() ) { //TODO: Methodize put( *pos.dereference(), new CoreString( *tgt->asString() ) ); } else { put( *pos.dereference(), *tgt ); } } void CoreDict::gcMark( uint32 gen ) { if ( gen != mark() ) { mark( gen ); m_dict->gcMark( gen ); } } bool CoreDict::find( const Item &key, Item &value ) { Item *itm; if( ( itm = find( key ) ) != 0 ) { value = *itm; return true; } return false; } //TODO - move in another file int ItemDict::compare( const ItemDict& other, ItemDict::Parentship* parent ) const { // really the same. if (&other == this) return 0; // Create the new parentship Parentship current( this, parent ); Iterator ithis( const_cast(this) ); Iterator iother( const_cast(&other) ); while( ithis.hasCurrent() ) { // is the other shorter? if ( ! iother.hasCurrent() ) { // we're bigger return 1; } const Item& tkey = ithis.getCurrentKey(); const Item& okey = iother.getCurrentKey(); int v = checkValue( tkey, okey, current ); if ( v != 0 ) return v; const Item& tvalue = ithis.getCurrent(); const Item& ovalue = iother.getCurrent(); v = checkValue( tvalue, ovalue, current ); if ( v != 0 ) return v; ithis.next(); iother.next(); } if( iother.hasCurrent() ) return -1; // ok, we're the same return 0; } int ItemDict::checkValue( const Item& first, const Item& second, ItemDict::Parentship& current ) const { // different dictionaries? if ( first.isDict() && first.isDict() ) { const ItemDict* dict = &first.asDict()->items(); Parentship *p1 = current.m_parent; // If it is not, we should scan it too. bool bDescend = true; while( p1 != 0 ) { if( p1->m_dict == dict ) { bDescend = false; break; } p1 = p1->m_parent; } if ( bDescend ) { return dict->compare( second.asDict()->items(), ¤t ); } return 0; } return first.compare( second ); } } /* end of cdict.cpp */ engine/corefunc.cpp000066400000000000000000000020601176363201700146370ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: corefunc.cpp Abstract live function object. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 07 Jan 2009 14:54:36 +0100 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Abstract live function object. */ #include #include #include #include namespace Falcon { CoreFunc::~CoreFunc() { delete m_closure; } void CoreFunc::readyFrame( VMachine* vm, uint32 paramCount ) { vm->prepareFrame( this, paramCount ); vm->currentContext()->fself() = this; } void CoreFunc::gcMark( uint32 gen ) { if( mark() != gen ) { mark( gen ); liveModule()->gcMark( gen ); // mark also closed items if ( closure() != 0 ) closure()->gcMark( gen ); } } } /* end of corefunc.cpp */ engine/coreobject.cpp000066400000000000000000000264311176363201700151620ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: coreobject.cpp Core object implementation ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 27 Jan 2009 19:46:05 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Core object implementation. */ #include #include #include #include #include #include #include namespace Falcon { CoreObject::CoreObject( const CoreClass *parent ): Garbageable(), m_user_data( 0 ), m_bIsFalconData( false ), m_bIsSequence( false ), m_generatedBy( parent ), m_state( 0 ) { } CoreObject::CoreObject( const CoreObject &other ): Garbageable( other ), m_user_data( 0 ), m_bIsFalconData( other.m_bIsFalconData ), m_bIsSequence( other.m_bIsFalconData ), m_generatedBy( other.m_generatedBy ), m_state( 0 ) { if ( m_bIsFalconData ) { fassert( other.m_user_data != 0 ); m_user_data = other.getFalconData()->clone(); } else { fassert( other.m_user_data == 0 ); } } CoreObject::~CoreObject() { delete m_state; if ( m_bIsFalconData ) delete static_cast( m_user_data ); } void CoreObject::gcMark( uint32 gen ) { // our class const_cast(m_generatedBy)->gcMark( gen ); if( gen != mark() ) { // mark ourseleves mark( gen ); // and possibly our inner falcon data if ( m_bIsFalconData ) { fassert( m_user_data != 0 ); static_cast(m_user_data)->gcMark( gen ); } } } bool CoreObject::serialize( Stream *stream, bool bLive ) const { if( bLive ) { void* data = 0; if( m_bIsFalconData ) { data = getFalconData()->clone(); } if ( data == 0 ) { data = m_user_data; } stream->write( (byte *) &data, sizeof( m_user_data ) ); return true; } return false; } bool CoreObject::hasProperty( const String &key ) const { fassert( m_generatedBy != 0 ); register uint32 pos; const PropertyTable &pt = m_generatedBy->properties(); return pt.findKey( key, pos ); } bool CoreObject::defaultProperty( const String &key, Item &prop ) const { fassert( m_generatedBy != 0 ); register uint32 pos; const PropertyTable &pt = m_generatedBy->properties(); if ( pt.findKey( key, pos ) ) { prop = *pt.getValue(pos); return true; } return false; } void CoreObject::readOnlyError( const String &key ) const { fassert( m_generatedBy != 0 ); register uint32 pos; const PropertyTable &pt = m_generatedBy->properties(); if( pt.findKey( key, pos ) ) { throw new AccessError( ErrorParam( e_prop_ro, __LINE__ ) .extra( key ) ); } else { String extra( generator()->symbol()->name() ); extra.A( '.' ).A( key ); throw new AccessError( ErrorParam( e_prop_acc, __LINE__ ) .extra( extra ) ); } } bool CoreObject::deserialize( Stream *stream, bool bLive ) { if( bLive ) { if ( stream->read( (byte *) &m_user_data, sizeof( m_user_data ) ) != sizeof( m_user_data ) ) return false; return true; } return false; } bool CoreObject::derivedFrom( const String &className ) const { return m_generatedBy->derivedFrom( className ); } bool CoreObject::getMethodDefault( const String &name, Item &mth ) const { const Falcon::Item* pmth = generator()->properties().getValue( name ); if ( pmth != 0 && pmth->isFunction() ) { // yes, a valid method mth = *pmth; mth.methodize( SafeItem( const_cast(this) ) ); return true; } return false; } bool CoreObject::apply( const ItemDict& dict, bool bRaiseOnError ) { Iterator iter( const_cast(&dict) ); bool bRes = true; while( iter.hasCurrent() ) { const Item& key = iter.getCurrentKey(); if ( key.isString() ) { if ( ! setProperty( *key.asString(), iter.getCurrent() ) ) { if( bRaiseOnError ) { String extra( generator()->symbol()->name() ); extra.A( '.' ).A( *key.asString() ); throw new AccessError( ErrorParam( e_prop_acc, __LINE__ ) .origin( e_orig_runtime ) .extra( extra ) ); } else bRes = false; } } iter.next(); } return bRes; } bool CoreObject::retrieve( ItemDict& dict, bool bRaiseOnError, bool bFillDict, bool bIgnoreMethods ) const { if ( bFillDict ) { const PropertyTable& names = m_generatedBy->properties(); for ( uint32 p = 0; p < names.added(); ++p ) { Item prop; if( getProperty( *names.getKey(p), prop ) ) { if ( bIgnoreMethods && prop.canBeMethod() ) continue; dict.put( *names.getKey(p), prop ); } } return true; } else { Iterator iter(&dict); bool bRes = true; while( iter.hasCurrent() ) { const Item& key = iter.getCurrentKey(); if ( key.isString() ) { Item value; if ( getProperty( *key.asString(), value ) ) { iter.data( &value ); } else { if( bRaiseOnError ) { String extra( generator()->symbol()->name() ); extra.A( '.' ).A( *key.asString() ); throw new AccessError( ErrorParam( e_prop_acc, __LINE__ ) .origin( e_orig_runtime ) .extra( extra ) ); } else bRes = false; } } iter.next(); } return bRes; } } //======================================================================= // Deep item overloading //======================================================================= bool CoreObject::setProperty( const String &propName, const String &value ) { return setProperty( propName, new CoreString( value ) ); } void CoreObject::readIndex( const Item &pos, Item &target ) { Item mth; if ( getMethod( OVERRIDE_OP_GETINDEX, mth ) ) { VMachine* vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->pushParam( pos ); vm->callItemAtomic( mth, 1 ); target = vm->regA(); return; } } throw new AccessError( ErrorParam( e_arracc, __LINE__ ).extra( OVERRIDE_OP_GETINDEX ) ); } void CoreObject::writeIndex( const Item &pos, const Item &target ) { Item method; if ( getMethod( OVERRIDE_OP_SETINDEX, method ) ) { VMachine* vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->pushParam( pos ); vm->pushParam( target ); vm->callItemAtomic( method, 2 ); return; } } throw new AccessError( ErrorParam( e_arracc, __LINE__ ).extra( "setIndex__" ) ); } void CoreObject::readProperty( const String &prop, Item &target ) { Item *p; if ( ! getProperty( prop, target ) ) { // try to find a generic method VMachine* vm = VMachine::getCurrent(); fassert( vm != 0 ); CoreClass* cc = vm->getMetaClass( FLC_ITEM_OBJECT ); uint32 id; if ( cc == 0 || ! cc->properties().findKey( prop, id ) ) { String extra( generator()->symbol()->name() ); extra.A( '.' ).A( prop ); throw new AccessError( ErrorParam( e_prop_acc, __LINE__ ).extra( extra ) ); } p = cc->properties().getValue( id ); } else p = target.dereference(); switch( p->type() ) { case FLC_ITEM_CLASS: if ( derivedFrom( p->asClass()->symbol()->name() ) ) target.setClassMethod( this, p->asClass() ); else target.setClass( p->asClass() ); break; default: target = *p; target.methodize( this ); } } void CoreObject::writeProperty( const String &prop, const Item &target ) { if ( ! setProperty( prop, target ) ) { String extra( generator()->symbol()->name() ); extra.A( '.' ).A( prop ); throw new AccessError( ErrorParam( e_prop_acc, __LINE__ ).extra( extra ) ); } } static bool __leave_handler( VMachine* vm ) { // clear enter-leave to grant a correct apply CoreObject* self = vm->self().asObject(); self->setProperty("__leave", Item()); self->setProperty("__enter", Item()); String* state = vm->param(0)->asString(); bool wasInState = self->hasState(); String oldStateName; if( wasInState ) { oldStateName = self->state(); } // enter the state self->setState( *state, &vm->preParam(0)->asDict()->items() ); // Does the new state have a "__enter" ? Item enterItem; if( self->getMethod("__enter", enterItem ) ) { if( wasInState ) vm->pushParam( new CoreString( oldStateName ) ); else vm->pushParam( Item() ); vm->pushParam( vm->regA() ); // never return here vm->returnHandler(0); vm->callFrame( enterItem, 2 ); // but respect this frame. return true; } // else, we're done, just remove this frame return false; } void CoreObject::setState( const String& state, VMachine* vm ) { ItemDict *states = m_generatedBy->states(); Item* stateDict; if ( states == 0 || ( stateDict = states->find( Item(const_cast(&state)) ) ) == 0 ) { throw new CodeError( ErrorParam( e_undef_state, __LINE__ ) .origin( e_orig_runtime ) .extra( state ) ); } // do we have an active __leave property? Item leaveItem; if( getMethod("__leave", leaveItem ) ) { CoreString* csState = new CoreString( state ); vm->pushParam(*stateDict); // pre-param 0 vm->pushParam( csState ); vm->callFrame( leaveItem, 1, &__leave_handler ); return; } // no leave? -- apply and see if we have an enter. setProperty("__enter", Item()); fassert( stateDict->isDict() ); bool hasOldState; String sOldState; if ( m_state == 0 ) { hasOldState = false; m_state = new String( state ); } else { hasOldState = true; sOldState = *m_state; m_state->bufferize( state ); } // shouldn't raise if all is ok apply( stateDict->asDict()->items(), true ); Item enterItem; if( getMethod("__enter", enterItem ) ) { if( hasOldState ) vm->pushParam( sOldState ); else vm->pushParam( Item() ); vm->pushParam( Item() ); vm->callFrame( enterItem, 2 ); } } void CoreObject::setState( const String& state, ItemDict* stateDict ) { if ( m_state == 0 ) m_state = new String( state ); else m_state->bufferize( state ); apply( *stateDict, true ); } void CoreObject::setUserData( FalconData* fdata ) { fdata->gcMark( mark() ); m_bIsSequence = false; m_bIsFalconData = true; m_user_data = fdata; } void CoreObject::setUserData( Sequence* sdata ) { sdata->gcMark( mark() ); m_bIsSequence = true; m_bIsFalconData = true; m_user_data = sdata; } } /* end of coreobject.cpp */ engine/coreslot.cpp000066400000000000000000000236421176363201700146760ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: coreslot.cpp Core Slot - Messaging slot system. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 11 Jan 2009 18:28:35 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include namespace Falcon { bool coreslot_broadcast_internal( VMachine *vm ) { Iterator *ci = dyncast< Iterator *>( vm->local(0)->asGCPointer() ); VMMessage* msg = 0; Item *msgItem = vm->local(4); if( msgItem->isInteger() ) { msg = (VMMessage*) msgItem->asInteger(); } if ( ! ci->hasCurrent() ) { // child call? if ( vm->local(5)->isNil() ) { // were we called after a message? if( msg != 0 ) { msg->onMsgComplete( true ); delete msg; } // let the GC pointer to take care of the ietrator. return false; } // we have a child that wants to get the message too. ci = dyncast< Iterator *>( vm->local(5)->asGCPointer() ); *vm->local(0) = *vm->local(5); vm->local(5)->setNil(); } Item current = ci->getCurrent(); int baseParamCount = 0; if ( ! current.isCallable() ) { const String& name = *vm->local(3)->asString(); if( name.size() != 0 ) { if( current.isComposed() ) { // may throw try { current.asDeepItem()->readProperty( "on_" + name, current ); } catch( Error* err ) { // see if we can get "__on_event" try { current.asDeepItem()->readProperty( "__on_event", current ); // yes? -- ignore the previous error. err->decref(); // and push the event name vm->pushParam( new CoreString(name) ); baseParamCount = 1; } catch( Error* err1 ) { // no? -- throw the original error err1->decref(); throw err; } } } else { throw new CodeError( ErrorParam( e_non_callable, __LINE__ ).extra( "broadcast" ) ); } } else { // ignore this item. ci->next(); return true; } } int64 pfirst = vm->local(1)->asInteger(); if( pfirst < 0 ) { Item cache = *vm->local( 2 ); vm->pushParam( cache ); vm->callFrame( current, 1 + baseParamCount ); } else { int64 paramCount = 0; if( msg == 0 ) { paramCount = vm->local(2)->asInteger(); for( int32 i = 0; i < paramCount; i++ ) { // we don't want to dereference the parameter, so we // access it directly instead of use vm->param() Item cache = vm->currentFrame()->m_params[ i + pfirst ]; vm->pushParam( cache ); } } else { paramCount = msg->paramCount(); for ( uint32 i = 0; i < paramCount; ++i ) { vm->pushParam( *msg->param(i) ); } } vm->callFrame( current, (int32)paramCount + baseParamCount ); } ci->next(); return true; } CoreSlot::~CoreSlot() { if( m_children != 0 ) { // to avoid double deletion, we remove the child from the map /* I disable it because I am not sure it's possible to make loops, and the deletor takes already care of the items. while( ! m_children->empty() ) { MapIterator iter = m_children->begin(); CoreSlot* sl = iter.currentValue(); m_children->erase( iter ); sl->decref(); }*/ delete m_children; m_children = 0; } } void CoreSlot::prepareBroadcast( VMContext *vmc, uint32 pfirst, uint32 pcount, VMMessage *msg, String* msgName ) { // no listeners? if( empty() ) { // have we receiving a named message and do we have a child? if( msgName != 0 ) { CoreSlot* child = getChild( *msgName, false ); if( child != 0 ) { child->prepareBroadcast( vmc, pfirst, pcount, msg, msgName ); } } return; } Iterator* iter = new Iterator( this ); // we don't need to set the slot as owner, as we're sure it stays in range // (slots are marked) on themselves. vmc->addLocals( 6 ); vmc->local(0)->setGCPointer( iter ); *vmc->local(1) = (int64) pfirst; *vmc->local(2) = (int64) pcount; *vmc->local(3) = new CoreString( msgName == 0 ? m_name : *msgName ); if ( msg != 0 ) { // store it as an opaque pointer. vmc->local(4)->setInteger( (int64) msg ); } // have we received a named message and do we have a child? if( msgName != 0 ) { CoreSlot* child = getChild( *msgName, false ); if( child != 0 ) { vmc->local(5)->setGCPointer( new Iterator(child) ); } } vmc->returnHandler( &coreslot_broadcast_internal ); } bool CoreSlot::remove( const Item &subscriber ) { Iterator iter( this ); while( iter.hasCurrent() ) { if ( iter.getCurrent() == subscriber ) { erase( iter ); return true; } iter.next(); } return false; } CoreSlot *CoreSlot::clone() const { incref(); return const_cast(this); } CoreSlot* CoreSlot::getChild( const String& name, bool create ) { if ( m_children == 0 ) { if ( ! create ) return 0; // create the children map m_children = new Map( &traits::t_string(), &traits::t_coreslotptr() ); } // we have already a map. void* vchild = m_children->find( &name ); if( vchild == 0 ) { if ( ! create ) return 0; CoreSlot* child = new CoreSlot( name ); m_children->insert( &name, child ); return child; } // no need to incref it if it's already in the map! return *(CoreSlot**) vchild; } void CoreSlot::gcMark( uint32 mark ) { if ( m_bHasAssert ) memPool->markItem( m_assertion ); ItemList::gcMark( mark ); } void CoreSlot::setAssertion( VMachine* vm, const Item &a ) { setAssertion( a ); if ( ! empty() ) { vm->addLocals( 6 ); // Warning -- we add 5 to nil the msg ptr callback at local(4). Iterator* iter = new Iterator( this ); // we don't need to set the slot as owner, as we're sure it stays in range // (slots are marked) on themselves. vm->local(0)->setGCPointer( iter ); *vm->local(1) = (int64) -1; *vm->local(2) = m_assertion; *vm->local(3) = new CoreString( m_name ); // local(4) must stay nil; it's used by broadcast_internal as msg callback pointer. // local(4) must stay nil; it's used by broadcast_internal as msg callback pointer. vm->returnHandler( &coreslot_broadcast_internal ); } } void CoreSlot::incref() const { m_mtx.lock(); m_refcount++; m_mtx.unlock(); } void CoreSlot::decref() { m_mtx.lock(); bool bdel = --m_refcount == 0; m_mtx.unlock(); if (bdel) { delete this; } } void CoreSlot::getIterator( Iterator& tgt, bool tail ) const { ItemList::getIterator( tgt, tail ); const_cast(this)->incref(); } void CoreSlot::copyIterator( Iterator& tgt, const Iterator& source ) const { ItemList::copyIterator( tgt, source ); const_cast(this)->incref(); } void CoreSlot::disposeIterator( Iterator& tgt ) const { ItemList::disposeIterator( tgt ); const_cast(this)->decref(); } //============================================================= // Carrier for VM // CoreSlotCarrier::CoreSlotCarrier( const CoreClass* generator, CoreSlot* cs, bool bSeralizing ): FalconObject( generator, bSeralizing ) { if( cs != 0 ) { cs->incref(); setUserData( cs ); } } CoreSlotCarrier::CoreSlotCarrier( const CoreSlotCarrier &other ): FalconObject( other ) { // FalconObject takes care of cloning (increffing) the inner data. } CoreSlotCarrier::~CoreSlotCarrier() { CoreSlot* cs = (CoreSlot*) m_user_data; cs->decref(); // sterilize downward destructors m_user_data = 0; } void CoreSlotCarrier::setSlot( CoreSlot* cs ) { CoreSlot* old_cs = (CoreSlot*) m_user_data; if ( old_cs != 0 ) old_cs->decref(); cs->incref(); setUserData( cs ); } CoreSlotCarrier *CoreSlotCarrier::clone() const { return new CoreSlotCarrier( *this ); } CoreObject* CoreSlotFactory( const CoreClass *cls, void *user_data, bool bDeserial ) { return new CoreSlotCarrier( cls, (CoreSlot *) user_data ); } //============================================================= // Traits for maps // namespace traits { CoreSlotPtrTraits &t_coreslotptr() { static CoreSlotPtrTraits dt; return dt; } } uint32 CoreSlotPtrTraits::memSize() const { return sizeof( CoreSlot * ); } void CoreSlotPtrTraits::init( void *itemZone ) const { itemZone = 0; } void CoreSlotPtrTraits::copy( void *targetZone, const void *sourceZone ) const { CoreSlot **target = (CoreSlot **) targetZone; CoreSlot *source = (CoreSlot *) sourceZone; *target = source; if( source != 0 ) source->incref(); } int CoreSlotPtrTraits::compare( const void *firstz, const void *secondz ) const { if ( sizeof(int) == sizeof(void*)) return (int)(((int64)firstz) - ((int64)secondz)); else return (((int64)firstz) > ((int64)secondz)) ? -1 : (((int64)firstz) < ((int64)secondz)) ? 1 : 0; } void CoreSlotPtrTraits::destroy( void *item ) const { CoreSlot *ptr = *(CoreSlot **) item; ptr->decref(); } bool CoreSlotPtrTraits::owning() const { return true; } } /* end of coreslot.cpp */ engine/coretable.cpp000066400000000000000000000254631176363201700150070ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: coretable.cpp Table support Interface for Falcon. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Sep 2008 15:15:56 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include namespace Falcon { //=============================================================== CoreTable::CoreTable(): m_currentPage(0), m_pages(&traits::t_voidp()), m_headerData( &traits::t_item() ), m_heading( &traits::t_string(), &traits::t_int() ), m_currentPageId(noitem), m_order(noitem), m_biddingVals(0), m_biddingSize(0) { } CoreTable::CoreTable( const CoreTable& other ): m_currentPage( other.m_currentPage ), m_pages(other.m_pages), m_headerData( other.m_headerData ), m_heading( other.m_heading ), m_currentPageId( other.m_currentPageId ), m_order( other.m_order ), m_biddingVals(0), m_biddingSize(0) { } CoreTable::~CoreTable() { if ( m_biddingVals != 0 ) { memFree( m_biddingVals ); m_biddingVals = 0; } } bool CoreTable::setHeader( CoreArray *header ) { return setHeader( header->items() ); } bool CoreTable::setHeader( const ItemArray& header ) { if ( m_order != noitem ) { if( m_order != header.length() ) return false; } uint32 len = header.length(); m_headerData.reserve( len ); m_headerData.resize(0); m_heading.clear(); for( uint32 i = 0; i < len; i++ ) { const Item &itm = header[i]; // we accept only strings and future bindings if ( itm.isFutureBind() ) { // string + value m_heading.insert( itm.asLBind(), &i ); m_headerData.push( &itm.asFBind()->origin() ); } else if ( itm.isString() ) { Item nil; m_heading.insert( itm.asString(), &i ); m_headerData.push( &nil ); } else return false; } m_order = len; return true; } bool CoreTable::insertRow( CoreArray *ca, uint32 pos, uint32 page ) { if ( m_order == 0 || m_order != ca->length() ) return false; CoreArray *tgt; if ( page == noitem ) { tgt = currentPage(); if ( tgt == 0 ) return false; } else { if( m_pages.size() <= page ) return false; tgt = *(CoreArray **) m_pages.at(page); } if ( pos < ca->length() ) tgt->insert( ca, pos ); else tgt->append( ca ); return true; } bool CoreTable::removeRow( uint32 pos, uint32 page ) { CoreArray *tgt; if ( page == noitem ) { tgt = currentPage(); if ( tgt == 0 ) return false; } else { if( m_pages.size() <= page ) return false; tgt = *(CoreArray **) m_pages.at(page); } if ( pos >= tgt->length() ) return false; tgt->remove( pos ); return true; } const String &CoreTable::heading( uint32 pos ) const { fassert( pos < order() ); MapIterator mi = m_heading.begin(); while( mi.hasCurrent() ) { uint32* p = (uint32*) mi.currentValue(); if ( *p == pos ) return *(String *) mi.currentKey(); mi.next(); } fassert( false ); // have a nice crash... return *(String *) 0; } uint32 CoreTable::getHeaderPos( const String &name ) const { uint32* pos = (uint32*) m_heading.find( &name ); if ( pos == 0 ) return noitem; return *pos; } Item *CoreTable::getHeaderData( uint32 pos ) const { if ( pos >= m_headerData.size() ) return 0; return (Item *) m_headerData.at(pos); } bool CoreTable::insertPage( CoreObject *self, CoreArray *data, uint32 pos ) { uint32 i; // may be long zero; it's ok for( i = 0; i < data->length(); i ++ ) { if ( ! (*data)[i].isArray() || (*data)[i].asArray()->length() != m_order ) return false; } for( i = 0; i < data->length(); i ++ ) { (*data)[i].asArray()->table( self ); } // ok we have a good page to add. if( pos < m_pages.size() ) m_pages.insert( data, pos ); else m_pages.push( data ); if ( m_currentPageId >= pos ) m_currentPageId++; return true; } bool CoreTable::removePage( uint32 pos ) { if ( m_pages.size() == 1 || pos >= m_pages.size() ) { // can't delete the only page left. return false; } // declare the page dead page(pos)->gcMark(1); // are we going to remove the current page? if ( m_currentPageId == pos ) { m_pages.remove(pos); m_currentPage = *(CoreArray **) m_pages.at(0); m_currentPageId = 0; } else { m_pages.remove(pos); } // If it was equal, it became 0 if( m_currentPageId > pos ) m_currentPageId--; return true; } const Item &CoreTable::front() const { static Item fake; fassert( m_currentPage != 0 ); fassert( m_currentPage->length() != 0 ); fake = (*m_currentPage)[0]; return fake; } void CoreTable::append( const Item &data ) { if ( ! data.isArray() ) throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "A" ) ); insertRow( data.asArray() ); } /** Prepend an item at the beginning of the sequence. */ void CoreTable::prepend( const Item &data ) { if ( ! data.isArray() ) throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "A" ) ); insertRow( data.asArray(), 0 ); } const Item &CoreTable::back() const { static Item fake; fassert( m_currentPage != 0 ); fassert( m_currentPage->length() != 0 ); fake = (*m_currentPage)[m_currentPage->length()-1]; return fake; } CoreTable *CoreTable::clone() const { return new CoreTable( *this ); } void CoreTable::clear() { if ( m_currentPage != 0 ) m_currentPage->resize(0); } void CoreTable::gcMark( uint32 gen ) { uint32 i; // mark the header data... for ( i = 0; i < m_headerData.size(); i ++ ) { memPool->markItem( *((Item *) m_headerData.at(i)) ); } // and all the tables. for( i = 0; i < m_pages.size(); i++ ) { CoreArray* page = *(CoreArray**)m_pages.at(i); page->gcMark( gen ); } } void CoreTable::reserveBiddings( uint32 size ) { if ( size > m_biddingSize ) { if ( m_biddingVals != 0 ) memFree( m_biddingVals ); m_biddingSize = size; m_biddingVals = (numeric *) memAlloc( size * sizeof( numeric ) ); } } void CoreTable::renameColumn( uint32 pos, const String &name ) { fassert( pos < order() ); MapIterator mi = m_heading.begin(); while( mi.hasCurrent() ) { uint32* p = (uint32*) mi.currentValue(); if ( *p == pos ) { m_heading.erase( mi ); m_heading.insert( &name, &pos ); return; } mi.next(); } fassert( false ); } void CoreTable::insertColumn( uint32 pos, const String &name, const Item &data, const Item &dflt ) { // if pos >= order, we're lucky. Append. if ( pos >= m_order ) { pos = m_order; m_heading.insert( &name, &pos ); } else { // first, move all following elements 1 forward MapIterator mi = m_heading.begin(); while( mi.hasCurrent() ) { uint32* p = (uint32*) mi.currentValue(); if ( *p >= pos ) { *p = *p+1; } mi.next(); } // then insert the new entry m_heading.insert( &name, &pos ); } m_order++; // add the column data. m_headerData.insert( (void *) &data, pos ); // now, for each page, for each row, insert the default item. for( uint32 pid = 0; pid < m_pages.size(); pid++ ) { CoreArray *pg = page( pid ); for( uint32 rowid = 0; rowid < pg->length(); rowid++ ) { CoreArray *row = pg->at(rowid).asArray(); // modify is forbidden if the array has a table. CoreObject *save = row->table(); row->table(0); row->insert( dflt, pos ); row->table(save); } } } bool CoreTable::removeColumn( uint32 pos ) { // if pos >= order, we're lucky. Append. if ( pos >= m_order ) { return false; } // first, move all following elements 1 forward MapIterator mi = m_heading.begin(); while( mi.hasCurrent() ) { uint32* p = (uint32*) mi.currentValue(); // when we find the foobar'd column, remove it. if ( *p == pos ) { m_heading.erase( mi ); continue; } // else, take other columns back if ( *p > pos ) { *p = *p-1; } mi.next(); } // add the column data. m_headerData.remove( pos ); m_order--; // now, for each page, for each row, insert the default item. for( uint32 pid = 0; pid < m_pages.size(); pid++ ) { CoreArray *pg = page( pid ); for( uint32 rowid = 0; rowid < pg->length(); rowid++ ) { CoreArray *row = pg->at(rowid).asArray(); // modify is forbidden if the array has a table. CoreObject *save = row->table(); row->table(0); row->remove( pos ); row->table(save); } } return true; } //============================================ // Iterator implementation //============================================ void CoreTable::getIterator( Iterator& tgt, bool tail ) const { // give up the ownership of the iterator to the current page. tgt.sequence( &m_currentPage->items() ); m_currentPage->items().getIterator( tgt, tail ); } void CoreTable::copyIterator( Iterator& tgt, const Iterator& source ) const { // actually never called } void CoreTable::insert( Iterator &iter, const Item &data ) { // actually never called } void CoreTable::erase( Iterator &iter ) { // actually never called } bool CoreTable::hasNext( const Iterator &iter ) const { // actually never called return false; } bool CoreTable::hasPrev( const Iterator &iter ) const { // actually never called return false; } bool CoreTable::hasCurrent( const Iterator &iter ) const { // actually never called return false; } bool CoreTable::next( Iterator &iter ) const { // actually never called return false; } bool CoreTable::prev( Iterator &iter ) const { // actually never called return false; } Item& CoreTable::getCurrent( const Iterator &iter ) { // actually never called throw new CodeError( ErrorParam( e_invalid_iter, __LINE__ ) ); } Item& CoreTable::getCurrentKey( const Iterator &iter ) { // actually never called throw new CodeError( ErrorParam( e_invalid_iter, __LINE__ ) ); } bool CoreTable::equalIterator( const Iterator &first, const Iterator &second ) const { // actually never called return false; } } /* end of coretable.cpp */ engine/crobject.cpp000066400000000000000000000063651176363201700146420ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: crobject.cpp Core object implementation ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom dic 5 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Core object implementation. */ #include #include #include namespace Falcon { CRObject::CRObject( const CoreClass *generator, bool bDeserial ): CacheObject( generator, bDeserial ) {} CRObject::CRObject( const CRObject &other ): CacheObject( other ) {} bool CRObject::setProperty( const String &propName, const Item &value ) { register uint32 pos; const PropertyTable &pt = m_generatedBy->properties(); if ( pt.findKey( propName, pos ) ) { // to be optimized const PropertyTable &pt = m_generatedBy->properties(); //Ok, we found the property, but what should we do with that? const PropEntry &entry = pt.getEntry( pos ); // can we write it? if ( entry.m_bReadOnly ) { throw new AccessError( ErrorParam( e_prop_ro, __LINE__ ).extra( propName ) ); } if ( entry.m_eReflectMode != e_reflectNone ) { fassert( m_user_data != 0 || entry.m_eReflectMode == e_reflectSetGet ); entry.reflectTo( this, m_user_data, value ); // remember to cache the value. } if ( value.isReference() ) m_cache[ pos ] = value; else *m_cache[ pos ].dereference() = value; return true; } return false; } bool CRObject::getProperty( const String &propName, Item &ret ) const { fassert( m_generatedBy != 0 ); register uint32 pos; const PropertyTable &pt = m_generatedBy->properties(); if ( pt.findKey( propName, pos ) ) { Item &cached = *m_cache[pos].dereference(); const PropEntry &entry = pt.getEntry(pos); if ( entry.m_eReflectMode != e_reflectNone ) { fassert( m_user_data != 0 || entry.m_eReflectMode == e_reflectSetGet ); // this code allows to modify our cached value. entry.reflectFrom( const_cast( this ), m_user_data, cached ); } ret = cached; // already assigned, if possible return true; } return false; } CRObject *CRObject::clone() const { return new CRObject( *this ); } //============================================ CoreObject* CROpaqueFactory( const CoreClass *cls, void *user_data, bool bDeserial ) { CRObject* cro = new CRObject( cls, bDeserial ); cro->setUserData( user_data ); return cro; } CoreObject* CRFalconFactory( const CoreClass *cls, void *user_data, bool bDeserial ) { CRObject* cro = new CRObject( cls, bDeserial ); if( user_data != 0 ) cro->setUserData( static_cast(user_data) ); return cro; } CoreObject* CRSequenceFactory( const CoreClass *cls, void *user_data, bool bDeserial ) { CRObject* cro = new CRObject( cls, bDeserial ); if( user_data != 0 ) cro->setUserData( static_cast(user_data) ); return cro; } } // namespace Falcon /* end of crobject.cpp */ engine/debug.cpp000066400000000000000000000030331176363201700141220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: debug.cpp Falcon debugging system. ------------------------------------------------------------------- Author: Paul Davey Begin: Thur, 26 Nov 2009 06:08:00 +1200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include namespace Falcon { DebugVMachine::DebugVMachine() : VMachine(), m_breakPoints( Map( &traits::t_voidp(), &traits::/*t_MapPtr*/t_voidp() ) ), m_watches( Map( &traits::t_string(), &traits::t_voidp() ) ), m_step(false), m_stepInto(false), m_stepOut(false) { callbackLoops(1); } void DebugVMachine::setBreakPoint(Symbol* func, int32 lineNo) { MapIterator iter; Map *lines; if ( !m_breakPoints.find( static_cast( func ), iter) ) { lines = new Map(&traits::t_int(),&traits::t_voidp()); m_breakPoints.insert(static_cast( func ),static_cast( lines )); } else { lines = static_cast( iter.currentValue() ); } //lines. } const Map& DebugVMachine::watches() const { return m_watches; } const ItemArray& DebugVMachine::stackTrace() const { return stack(); } void DebugVMachine::periodicCallback() { m_opNextCheck = m_opCount + 1; } } //end namespace Falcon engine/deptab.cpp000066400000000000000000000044531176363201700143020ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: deptab.cpp Dependency table support for modules. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom ago 21 2005 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Dependency table support for modules. */ #include #include #include #include #include namespace Falcon { DependTable::DependTable(): Map( &traits::t_stringptr_own(), &traits::t_voidp() ) { } DependTable::~DependTable() { MapIterator iter = begin(); while( iter.hasCurrent() ) { delete *(ModuleDepData **) iter.currentValue(); iter.next(); } } bool DependTable::save( Stream *out ) const { uint32 s = endianInt32(size()); out->write( &s, sizeof( s ) ); MapIterator iter = begin(); while( iter.hasCurrent() ) { const String *name = *(const String **) iter.currentKey(); const ModuleDepData *data = *(const ModuleDepData **) iter.currentValue(); name->serialize( out ); data->moduleName().serialize( out ); s = endianInt32( data->isPrivate() ? 1 : 0 ); out->write( &s, sizeof( s ) ); s = endianInt32( data->isFile() ? 1 : 0 ); out->write( &s, sizeof( s ) ); iter.next(); } return true; } bool DependTable::load( Module *mod, Stream *in ) { uint32 s; in->read( &s, sizeof( s ) ); s = endianInt32( s ); while( s > 0 ) { uint32 id; String alias, modname; if ( ! alias.deserialize( in, false ) || ! modname.deserialize( in, false ) ) { return false; } in->read( &id, sizeof( id ) ); bool isPrivate = endianInt32(id) != 0; in->read( &id, sizeof( id ) ); bool isFile = endianInt32(id) != 0; insert( new String(alias), new ModuleDepData( modname, isPrivate, isFile ) ); --s; } return true; } void DependTable::addDependency( const String &alias, const String& name, bool bPrivate, bool bFile ) { insert( new String(alias), new ModuleDepData( name, bPrivate, bFile ) ); } } /* end of deptab.cpp */ engine/dir_sys_unix.cpp000066400000000000000000000166361176363201700155700ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dir_unix.cpp Implementation of directory system support for unix. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom nov 7 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Implementation of directory system support for unix. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Falcon { namespace Sys { bool fal_fileType( const String &fname, FileStat::e_fileType &st ) { AutoCString filename(fname ); struct stat fs; if ( lstat( filename.c_str(), &fs ) != 0 ) { st = FileStat::t_notFound; return false; } if( S_ISREG( fs.st_mode ) ) st = FileStat::t_normal; else if( S_ISDIR( fs.st_mode ) ) st = FileStat::t_dir; else if( S_ISFIFO( fs.st_mode ) ) st = FileStat::t_pipe; else if( S_ISLNK( fs.st_mode ) ) st = FileStat::t_link; else if( S_ISBLK( fs.st_mode ) || S_ISCHR( fs.st_mode ) ) st = FileStat::t_device; else if( S_ISSOCK( fs.st_mode ) ) st = FileStat::t_socket; else st = FileStat::t_unknown; return true; } bool fal_stats( const String &f, FileStat &sts ) { AutoCString filename( f ); struct stat fs; if ( lstat( filename.c_str(), &fs ) != 0 ) { return false; } sts.m_size = fs.st_size; if( S_ISREG( fs.st_mode ) ) sts.m_type = FileStat::t_normal; else if( S_ISDIR( fs.st_mode ) ) sts.m_type = FileStat::t_dir; else if( S_ISFIFO( fs.st_mode ) ) sts.m_type = FileStat::t_pipe; else if( S_ISLNK( fs.st_mode ) ) sts.m_type = FileStat::t_link; else if( S_ISBLK( fs.st_mode ) || S_ISCHR( fs.st_mode ) ) sts.m_type = FileStat::t_device; else if( S_ISSOCK( fs.st_mode ) ) sts.m_type = FileStat::t_socket; else sts.m_type = FileStat::t_unknown; sts.m_access = fs.st_mode; sts.m_owner = fs.st_uid; /* user ID of owner */ sts.m_group = fs.st_gid; /* group ID of owner */ UnixSystemTime mtime( fs.st_mtime ); if (sts.m_atime == 0 ) sts.m_atime = new TimeStamp(); mtime.m_time_t = fs.st_atime; sts.m_atime->fromSystemTime( mtime ); /* time of last access */ if (sts.m_ctime == 0 ) sts.m_ctime = new TimeStamp(); mtime.m_time_t = fs.st_ctime; sts.m_ctime->fromSystemTime( mtime ); /* time of last change */ // copy last change time to last modify time if (sts.m_mtime == 0 ) sts.m_mtime = new TimeStamp(); sts.m_mtime->fromSystemTime( mtime ); return true; } bool fal_mkdir( const String &f, int32 &fsStatus ) { AutoCString filename( f ); if ( ::mkdir( filename.c_str(), 0744 ) == 0 ) { fsStatus = 0; return true; } fsStatus = errno; return false; } bool fal_mkdir( const String &strName, int32 &fsError, bool descend ) { if ( descend ) { // find /.. sequences uint32 pos = strName.find( "/" ); if(pos == 0) pos = strName.find( "/", 1 ); // an absolute path while( true ) { String strPath( strName, 0, pos ); // stat the file FileStat fstats; // if the file exists... if ( (! Sys::fal_stats( strPath, fstats )) || fstats.m_type != FileStat::t_dir ) { // if it's not a directory, try to create the directory. if ( ! Sys::fal_mkdir( strPath, fsError ) ) return false; } // last loop? if ( pos == String::npos ) break; pos = strName.find( "/", pos + 1 ); } } else { // Just one try; succeed or fail return Sys::fal_mkdir( strName, fsError ); } return true; } bool fal_rmdir( const String &f, int32 &fsStatus ) { AutoCString filename( f ); if ( ::rmdir( filename.c_str() ) == 0 ) { fsStatus = 0; return true; } fsStatus = errno; return false; } bool fal_unlink( const String &f, int32 &fsStatus ) { AutoCString filename( f ); if ( ::unlink( filename.c_str() ) == 0 ) { fsStatus = 0; return true; } fsStatus = errno; return false; } bool fal_move( const String &f, const String &d, int32 &fsStatus ) { AutoCString filename( f ); AutoCString dest( d ); if ( ::rename( filename.c_str(), dest.c_str() ) == 0 ) { fsStatus = 0; return true; } fsStatus = errno; return false; } bool fal_chdir( const String &f, int32 &fsStatus ) { AutoCString filename( f ); if ( ::chdir( filename.c_str() ) == 0 ) { fsStatus = 0; return true; } fsStatus = errno; return false; } bool fal_getcwd( String &fname, int32 &fsError ) { char buf[256]; uint32 pwdSize = 256; char *buffer = buf; char *bufret; while ( (bufret = ::getcwd( buffer, pwdSize )) == 0 && errno == ERANGE ) { pwdSize += 256; if ( buffer != buf ) memFree( buffer ); buffer = ( char * ) memAlloc( pwdSize ); } fsError = errno; bool val; if ( bufret != 0 ) { val = true; fname.fromUTF8( bufret ); } else val = false; if ( buffer != buf ) memFree( buffer ); return val; } bool fal_chmod( const String &fname, uint32 mode ) { AutoCString filename( fname ); bool ret = ::chmod( filename.c_str(), mode ) == 0; return ret; } bool fal_chown( const String &fname, int32 owner ) { AutoCString filename( fname ); bool ret = ::chown( filename.c_str(), owner , (gid_t) -1 ) == 0; return ret; } bool fal_readlink( const String &fname, String &link ) { char buf[1024]; int len; AutoCString filename( fname ); if ( ( len = readlink( filename.c_str(), buf, sizeof(buf) - 1 ) ) != -1) { buf[len] = '\0'; link.fromUTF8( buf ); return true; } return false; } bool fal_writelink( const String &fname, const String &link ) { AutoCString filename( fname ); AutoCString linkname( link ); if ( ! symlink( filename.c_str(), linkname.c_str() ) ) { return false; } return true; } bool fal_chgrp( const String &fname, int32 owner ) { AutoCString filename( fname ); bool ret = ::chown( filename.c_str(), (uid_t) -1, owner ) == 0; return ret; } ::Falcon::DirEntry *fal_openDir( const String &p, int32 &fsStatus ) { AutoCString filename( p ); DIR *dir = ::opendir( filename.c_str() ); if ( dir == 0 ) { fsStatus = errno; return 0; } return new DirEntry_unix( p, dir ); } void fal_closeDir( ::Falcon::DirEntry *entry ) { delete entry; } } // SYS namespace bool DirEntry_unix::read( String &res ) { // Glibc doesn't perform that check if( m_raw_dir == 0) { return false; } struct dirent *d; errno = 0; d = readdir( m_raw_dir ); m_lastError = errno; if ( d == 0 ) { return false; } res.fromUTF8( d->d_name ); return true; } void DirEntry_unix::close() { if ( m_raw_dir != 0 ) { errno = 0; closedir( m_raw_dir ); m_lastError = errno; } m_raw_dir = 0; } } /* end of dir_unix.cpp */ engine/dir_sys_win.cpp000066400000000000000000000267211176363201700153760ustar00rootroot00000000000000/* FALCON - Falcon advanced simple text evaluator. FILE: dir_win.cpp Implementation of directory system support for unix. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom nov 7 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Implementation of directory system support for unix. */ #include #include #include #include #include #include #include #include #include #include #include #include namespace Falcon { namespace Sys { bool fal_fileType( const String &filename, FileStat::e_fileType &st ) { String fname = filename; Path::uriToWin( fname ); AutoWString wstrBufName( fname ); DWORD attribs = GetFileAttributesW( wstrBufName.w_str() ); if( attribs == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) { AutoCString cstrBufName( fname ); attribs = GetFileAttributes( cstrBufName.c_str() ); } if( attribs == INVALID_FILE_ATTRIBUTES ) { return false; } if( (attribs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY ) st = FileStat::t_dir; else st = FileStat::t_normal; return true; } bool fal_stats( const String &filename, FileStat &sts ) { String fname = filename; Path::uriToWin( fname ); AutoWString wBuffer( fname ); // First, determine if the file exists if( filename.size() > 0 && filename.getCharAt(filename.length()-1) != '.' ) { WIN32_FIND_DATAW wFindData; HANDLE hFound = FindFirstFileW( wBuffer.w_str(), &wFindData ); if( hFound == INVALID_HANDLE_VALUE ) { if( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) { WIN32_FIND_DATAA aFindData; AutoCString cBuffer( fname ); hFound = FindFirstFileA( cBuffer.c_str(), &aFindData ); if ( hFound == INVALID_HANDLE_VALUE ) return false; FindClose( hFound ); // check case sensitive String ffound(aFindData.cFileName); if( fname.subString( fname.length() - ffound.length() ) != ffound ) return false; } else return false; } FindClose( hFound ); // Then, see if the case matches. String ffound(wFindData.cFileName); if( fname.subString( fname.length() - ffound.length() ) != ffound ) return false; } // ok, file exists and with matching case HANDLE temp = CreateFileW( wBuffer.w_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); if( (temp == INVALID_HANDLE_VALUE || temp == 0) && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) { AutoCString cBuffer( fname ); temp = CreateFile( cBuffer.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL ); } if( temp == INVALID_HANDLE_VALUE ) { // on win 95/98, we can't normally access directory data. DWORD attribs = GetFileAttributesW( wBuffer.w_str() ); if( attribs == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) { AutoCString cBuffer( fname ); attribs = GetFileAttributes( cBuffer.c_str() ); } if( attribs == INVALID_FILE_ATTRIBUTES ) { return false; } if( (attribs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY ) { sts.m_type = FileStat::t_dir; sts.m_attribs = attribs; sts.m_size = 0; sts.m_mtime = new TimeStamp(); sts.m_atime = new TimeStamp(); sts.m_ctime = new TimeStamp(); sts.m_owner = 0; /* user ID of owner */ sts.m_group = 0; /* group ID of owner */ return true; } return false; } BY_HANDLE_FILE_INFORMATION info; memset( &info, 0, sizeof( info ) ); GetFileInformationByHandle( temp, &info ); if( info.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY ) sts.m_type = FileStat::t_dir; else sts.m_type = FileStat::t_normal; FILETIME local_timing; SYSTEMTIME timing; FileTimeToLocalFileTime( &info.ftCreationTime, &local_timing ); FileTimeToSystemTime( &local_timing, &timing ); WinSystemTime mtime( timing ); if ( sts.m_ctime == 0 ) sts.m_ctime = new TimeStamp(); sts.m_ctime->fromSystemTime( mtime ); FileTimeToLocalFileTime( &info.ftLastAccessTime, &local_timing ); FileTimeToSystemTime( &local_timing, &mtime.m_time ); if ( sts.m_atime == 0 ) sts.m_atime = new TimeStamp(); sts.m_atime->fromSystemTime( mtime ); FileTimeToLocalFileTime( &info.ftLastWriteTime, &local_timing ); FileTimeToSystemTime( &local_timing, &mtime.m_time ); if ( sts.m_mtime == 0 ) sts.m_mtime = new TimeStamp(); sts.m_mtime->fromSystemTime( mtime ); sts.m_size = info.nFileSizeHigh; sts.m_size = sts.m_size << 32 | info.nFileSizeLow; sts.m_attribs = info.dwFileAttributes; sts.m_owner = 0; /* user ID of owner */ sts.m_group = 0; /* group ID of owner */ CloseHandle( temp ); return true; } bool fal_mkdir( const String &filename, int32 &fsStatus ) { String fname = filename; Path::uriToWin( fname ); AutoWString wBuffer( fname ); BOOL res = CreateDirectoryW( wBuffer.w_str(), NULL ); if( ! res && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) { AutoCString cBuffer( fname ); res = CreateDirectory( cBuffer.c_str(), NULL ); } if ( res == TRUE ) { return true; } fsStatus = GetLastError(); return false; } bool fal_mkdir( const String &strName, int32 &fsError, bool descend ) { if ( descend ) { // find /.. sequences uint32 pos = strName.find( "/" ); while( true ) { String strPath( strName, 0, pos ); // stat the file FileStat fstats; // if the file exists... if ( (! Sys::fal_stats( strPath, fstats )) || fstats.m_type != FileStat::t_dir ) { // if it's not a directory, try to create the directory. if ( ! Sys::fal_mkdir( strPath, fsError ) ) return false; } // last loop? if ( pos == String::npos ) break; pos = strName.find( "/", pos + 1 ); } } else { // Just one try; succeed or fail return Sys::fal_mkdir( strName, fsError ); } return true; } bool fal_rmdir( const String &filename, int32 &fsStatus ) { String fname = filename; Path::uriToWin( fname ); AutoWString wBuffer( fname ); BOOL res = RemoveDirectoryW( wBuffer.w_str() ); if( ! res && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) { AutoCString cBuffer( fname ); res = RemoveDirectory( cBuffer.c_str() ); } if ( res == TRUE ) { return true; } fsStatus = GetLastError(); return false; } bool fal_unlink( const String &filename, int32 &fsStatus ) { String fname = filename; Path::uriToWin( fname ); AutoWString wBuffer( fname ); BOOL res = DeleteFileW( wBuffer.w_str() ); if( ! res && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) { AutoCString cBuffer( fname ); res = DeleteFile( cBuffer.c_str() ); } if ( res == TRUE ) { return true; } fsStatus = GetLastError(); return false; } bool fal_move( const String &filename, const String &dest, int32 &fsStatus ) { String fname1 = filename; Path::uriToWin( fname1 ); String fname2 = dest; Path::uriToWin( fname2 ); AutoWString wBuffer1( fname1 ); AutoWString wBuffer2( fname2 ); BOOL res = MoveFileW( wBuffer1.w_str(), wBuffer2.w_str() ); if( ! res && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) { AutoCString cBuffer1( fname1 ); AutoCString cBuffer2( fname2 ); res = MoveFile( cBuffer1.c_str(), cBuffer2.c_str() ); } if ( res == TRUE ) { return true; } fsStatus = GetLastError(); return false; } bool fal_chdir( const String &filename, int32 &fsStatus ) { String fname = filename; Path::uriToWin( fname ); AutoWString wBuffer( fname ); BOOL res = SetCurrentDirectoryW( wBuffer.w_str() ); if( ! res && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) { AutoCString cBuffer( fname ); res = SetCurrentDirectory( cBuffer.c_str() ); } if ( res == TRUE ) { return true; } fsStatus = GetLastError(); return false; } bool fal_getcwd( String &cwd, int32 &fsError ) { DWORD size = GetCurrentDirectory( 0, NULL ); if( size == 0 ) { fsError = GetLastError(); return 0; } int bufSize = size * sizeof( wchar_t ) + sizeof( wchar_t ); wchar_t *buffer = (wchar_t *) memAlloc( bufSize ); size = GetCurrentDirectoryW( bufSize, buffer ); if( size == 0 && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) { char *buffer_c = (char *) buffer; size = GetCurrentDirectory( bufSize, buffer_c ); if( size == 0 ) { memFree( buffer ); fsError = GetLastError(); return false; } cwd.adopt( buffer_c, size, bufSize ); Path::winToUri( cwd ); return true; } if( size == 0 ) { memFree( buffer ); fsError = GetLastError(); return false; } cwd.adopt( buffer, size, bufSize ); Path::winToUri( cwd ); return true; } bool fal_chmod( const String &fname, uint32 mode ) { return false; } bool fal_chown( const String &fname, int32 owner ) { return false; } bool fal_chgrp( const String &fname, int32 owner ) { return false; } bool fal_readlink( const String &fname, String &link ) { /** TODO: implement on windows */ return false; } bool fal_writelink( const String &fname, String &link ) { /** TODO: implement on windows */ return false; } ::Falcon::DirEntry *fal_openDir( const String &path, int32 &fsError ) { String fname = path + "\\*"; Path::uriToWin( fname ); AutoWString wBuffer( fname ); WIN32_FIND_DATAW dir_data; HANDLE handle = FindFirstFileW( wBuffer.w_str(), &dir_data ); if( handle == INVALID_HANDLE_VALUE && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) { AutoCString cBuffer( fname ); handle = FindFirstFile( cBuffer.c_str(), (WIN32_FIND_DATA*) &dir_data ); } if ( handle != INVALID_HANDLE_VALUE ) return new DirEntry_win( path, handle, dir_data ); fsError = GetLastError(); return 0; } void fal_closeDir( ::Falcon::DirEntry *entry ) { delete entry; } } // Namespace Srv bool DirEntry_win::read( String &str ) { if( m_handle == INVALID_HANDLE_VALUE ) return 0; bool bWideChar = true; if ( m_first ) { m_first = false; } else { if ( ! FindNextFileW( m_handle, &m_raw_dir ) ) { if( GetLastError() != ERROR_CALL_NOT_IMPLEMENTED ) return false; bWideChar = false; if ( ! FindNextFile( m_handle, (WIN32_FIND_DATA*) &m_raw_dir ) ) return false; } } if( bWideChar ) str.bufferize( m_raw_dir.cFileName ); else str.bufferize( ((WIN32_FIND_DATA*) &m_raw_dir)->cFileName ); Path::winToUri( str ); return true; } void DirEntry_win::close() { if ( m_handle != INVALID_HANDLE_VALUE ) { if ( ! FindClose( m_handle ) ) { m_lastError = GetLastError(); } else m_lastError = 0; } m_handle = INVALID_HANDLE_VALUE; } } /* end of dir_win.cpp */ engine/dll_dl.cpp000066400000000000000000000032101176363201700142630ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dll_win.cpp Implementation of windows specific DLL system ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar ago 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include namespace Falcon { DllLoader_dl::~DllLoader_dl() { close(); } bool DllLoader_dl::open( const String &dll_name ) { AutoCString name( dll_name ); if( m_module != 0 ) if ( ! dlclose( m_module ) ) return false; m_module = dlopen( name.c_str(), RTLD_NOW ); if ( m_module == 0 ) return false; return true; } bool DllLoader_dl::close() { if ( m_module != 0 ) { if ( dlclose( m_module ) ) { m_module = 0; return true; } } return false; } void DllLoader_dl::assign( DllLoader_dl &other ) { if ( m_module != 0 ) close(); m_module = other.m_module; other.m_module = 0; } DllFunc DllLoader_dl::getSymbol( const String &sym_name ) const { AutoCString name( sym_name ); if ( m_module != 0 ) return DllFunc( dlsym( m_module, name.c_str() ) ); return DllFunc( 0 ); } bool DllLoader_dl::isDllMark( char ch1, char ch2 ) { if ( ch1 == 0x7f && ch2 == 'E' ) return true; return false; } void DllLoader_dl::getErrorDescription( String &descr ) const { const char *le = dlerror(); if ( le == 0 ) return; descr.bufferize( le ); } } /* end of dll_dl.cpp */ engine/dll_mac.cpp000066400000000000000000000034371176363201700144370ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dll_mac.cpp Implementation of darwin specific DLL system ------------------------------------------------------------------- Author: Giancarlo Niccolai: Modified for darwin by: Francesco Guerra Begin: mar ago 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include namespace Falcon { DllLoader_Mac::~DllLoader_Mac() { close(); } bool DllLoader_Mac::open( const String &dll_name ) { if( m_module != 0 ) if ( ! dlclose( m_module ) ) return false; AutoCString name( dll_name ); m_module = dlopen( name.c_str(), RTLD_NOW ); if ( m_module == 0 ) return false; return true; } bool DllLoader_Mac::close() { if ( m_module != 0 ) { if ( dlclose( m_module ) ) { m_module = 0; return true; } } return false; } void DllLoader_Mac::assign( DllLoader_Mac &other ) { if ( m_module != 0 ) close(); m_module = other.m_module; other.m_module = 0; } DllFunc DllLoader_Mac::getSymbol( const String &sym_name ) const { AutoCString name(sym_name); if ( m_module != 0 ) return DllFunc( dlsym( m_module, name.c_str() ) ); return DllFunc( 0 ); } bool DllLoader_Mac::isDllMark( unsigned char ch1, unsigned char ch2 ) { if ( ch1 == 0xfe && ch2 == 0xed || ch1 == 0xca && ch2 == 0xfe ) return true; // Magic for Mach-O and Mach-O Fat binaries return false; } void DllLoader_Mac::getErrorDescription( String &descr ) const { const char *le = dlerror(); if ( le == 0 ) return; descr.bufferize( le ); } } /* end of dll_mac.cpp */ engine/dll_win.cpp000066400000000000000000000052261176363201700144720ustar00rootroot00000000000000/* FALCON - Falcon advanced simple text evaluator. FILE: dll_win.cpp Implementation of windows specific DLL system ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar ago 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ // Must include also windows.h #include #include #include #include namespace Falcon { DllLoader_win::~DllLoader_win() { close(); } bool DllLoader_win::open( const String &dll_name_fal ) { if( m_module != NULL ) if ( ! FreeLibrary( m_module ) ) return false; String dll_name = dll_name_fal; Path::uriToWin( dll_name ); uint32 bufsize = dll_name.length() * sizeof( wchar_t ) + sizeof( wchar_t ); wchar_t *dll_name_wc = (wchar_t *) memAlloc( bufsize ); dll_name.toWideString( dll_name_wc, bufsize ); m_module = LoadLibraryW( dll_name_wc ); if ( m_module == NULL ) { m_error = GetLastError(); if ( m_error == ERROR_CALL_NOT_IMPLEMENTED ) { char *dll_name_c = (char *) dll_name_wc; if( dll_name.toCString( dll_name_c, bufsize ) > 0 ) m_module = LoadLibrary( dll_name_c ); } } memFree( dll_name_wc ); if ( m_module == NULL ) { m_error = GetLastError(); return false; } return true; } bool DllLoader_win::close() { if ( m_module != NULL ) { if ( FreeLibrary( m_module ) ) { m_module = NULL; return true; } } return false; } void DllLoader_win::assign( DllLoader_win &other ) { if ( m_module != NULL ) close(); m_module = other.m_module; other.m_module = NULL; } DllFunc DllLoader_win::getSymbol( const char *sym_name ) const { if ( m_module != NULL ) return DllFunc( (void*)GetProcAddress( m_module, (LPCSTR) sym_name ) ); return DllFunc( 0 ); } bool DllLoader_win::isDllMark( char ch1, char ch2 ) { if ( ch1 == 'M' && ch2 == 'Z' ) return true; return false; } void DllLoader_win::getErrorDescription( String &descr ) const { LPVOID lpMsgBuf; DWORD res = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, m_error, LANG_USER_DEFAULT, (LPTSTR) &lpMsgBuf, 0, NULL ); if ( res == 0 ) { descr = "Impossible to retreive error description"; } else { descr = (char *) lpMsgBuf; // force to copy descr.bufferize(); } LocalFree(lpMsgBuf); } } /* end of dll_win.cpp */ engine/error.cpp000066400000000000000000000357211176363201700141760ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: error.cpp Error management. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun ago 28 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Error management. */ #include #include #include #include #include #include namespace Falcon { const String &errorDesc( int code ) { switch( code ) { #define FLC_MAKE_ERROR_MESSAGE_SELECTOR #include } return Engine::getMessage( msg_unknown_error ); } String &TraceStep::toString( String &target ) const { if ( m_modpath.size() ) { target += "\"" + m_modpath + "\" "; } target += m_module + "." + m_symbol + ":"; target.writeNumber( (int64) m_line ); target += "(PC:"; switch( m_pc ) { case VMachine::i_pc_call_external: target += "ext"; break; case VMachine::i_pc_call_external_return: target += "ext.r"; break; case VMachine::i_pc_redo_request: target += "redo"; break; case VMachine::i_pc_call_external_ctor: target += "ext.c"; break; case VMachine::i_pc_call_external_ctor_return: target += "ext.cr"; break; default: target.writeNumber( (int64) m_pc ); } target += ")"; return target; } //================================================== // Error //================================================== Error::Error( const Error &e ): m_nextError( 0 ), m_LastNextError( 0 ), m_boxed( 0 ) { m_errorCode = e.m_errorCode; m_line = e.m_line; m_pc = e.m_pc; m_character = e.m_character; m_sysError = e.m_sysError; m_origin = e.m_origin; m_catchable = e.m_catchable; m_description = e.m_description; m_extra = e.m_extra; m_module = e.m_module; m_symbol = e.m_symbol; m_raised = e.m_raised; m_className = e.m_className; if ( e.m_boxed != 0 ) { boxError(m_boxed); } m_refCount = 1; ListElement *step_i = m_steps.begin(); while( step_i != 0 ) { TraceStep *step = (TraceStep *) step_i->data(); addTrace( step->module(), step->symbol(), step->line(), step->pcounter() ); step_i = step_i->next(); } } Error::~Error() { ListElement *step_i = m_steps.begin(); while( step_i != 0 ) { TraceStep *step = (TraceStep *) step_i->data(); delete step; step_i = step_i->next(); } Error *ptr = m_nextError; while( ptr != 0 ) { Error *ptrnext = ptr->m_nextError; ptr->m_nextError = 0; ptr->decref(); ptr = ptrnext; } if ( m_boxed != 0 ) { m_boxed->decref(); } } void Error::incref() { atomicInc( m_refCount ); } void Error::decref() { if( atomicDec( m_refCount ) <= 0 ) { delete this; } } String &Error::toString( String &target ) const { if ( m_boxed != 0 ) { target += m_boxed->toString(); target += " =====================================================\n"; target += " Boxed in "; } heading( target ); target += "\n"; if ( ! m_steps.empty() ) { target += " Traceback:\n"; ListElement *iter = m_steps.begin(); while( iter != 0 ) { target += " "; TraceStep *step = (TraceStep *) iter->data(); step->toString( target ); target += "\n"; iter = iter->next(); } } // recursive stringation if ( m_nextError != 0 ) { m_nextError->toString( target ); } return target; } String &Error::heading( String &target ) const { target += m_className; target += " "; switch( m_origin ) { case e_orig_compiler: target += "CO"; break; case e_orig_assembler: target += "AS"; break; case e_orig_loader: target += "LD"; break; case e_orig_vm: target += "VM"; break; case e_orig_runtime: target += "RT"; break; case e_orig_mod: target += "MD"; break; case e_orig_script: target += "SS"; break; default: target += "??"; } uint32 ecode = (uint32) m_errorCode; for ( int number = 1000; number > 0; number /= 10 ) { int64 cipher = ecode / number; ecode %= number; target.writeNumber( cipher ); } if ( m_sysError != 0 ) { target += "(sys: "; target.writeNumber( (int64) m_sysError ); String temp; Sys::_describeError( m_sysError, temp ); target += " " + temp; target += ")"; } if( m_line != 0 || m_module.size() != 0 ) target += " at "; if ( m_module.size() != 0 ) { target += m_module; if ( m_symbol.size() != 0 ) target += "." + m_symbol; target += ":"; } if ( m_line != 0 ) target.writeNumber( (int64) m_line ); if ( m_character != 0 ) { target += "/"; target.writeNumber( (int64) m_character ); } if ( m_pc != 0 ) { target += "(PC:"; switch( m_pc ) { case VMachine::i_pc_call_external: target += "ext"; break; case VMachine::i_pc_call_external_return: target += "ext.r"; break; case VMachine::i_pc_redo_request: target += "redo"; break; case VMachine::i_pc_call_external_ctor: target += "ext.c"; break; case VMachine::i_pc_call_external_ctor_return: target += "ext.cr"; break; default: target.writeNumber( (int64) m_pc ); } target += ")"; } if ( m_description.size() > 0 ) { target += ": " + m_description; } else { target += ": " + errorDesc( m_errorCode ); } if ( m_extra.size() > 0 ) { target += " (" + m_extra + ")"; } if ( ! m_raised.isNil() ) { String temp; m_raised.toString( temp ); target += "\n"+ temp; } return target; } void Error::addTrace( const String &module, const String &symbol, uint32 line, uint32 pc ) { m_steps.pushBack( new TraceStep( module, symbol, line, pc ) ); m_stepIter = m_steps.begin(); } void Error::addTrace( const String &module, const String &modpath, const String &symbol, uint32 line, uint32 pc ) { m_steps.pushBack( new TraceStep( module, modpath, symbol, line, pc ) ); m_stepIter = m_steps.begin(); } void Error::appendSubError( Error *error ) { if ( m_LastNextError == 0 ) { m_LastNextError = m_nextError = error; } else { m_LastNextError->m_nextError = error; m_LastNextError = error; } error->incref(); } void Error::boxError( Error *error ) { if ( m_boxed != 0 ) { m_boxed->decref(); } m_boxed = error; if ( error != 0 ) { error->incref(); } } bool Error::nextStep( String &module, String &symbol, uint32 &line, uint32 &pc ) { if ( m_steps.empty() || m_stepIter == 0 ) return false; TraceStep *step = (TraceStep *) m_stepIter->data(); module = step->module(); symbol = step->symbol(); line = step->line(); pc = step->pcounter(); m_stepIter = m_stepIter->next(); return true; } void Error::rewindStep() { if ( ! m_steps.empty() ) m_stepIter = m_steps.begin(); } CoreObject *Error::scriptize( VMachine *vm ) { Item *error_class = vm->findWKI( m_className ); // in case of 0, try with global items if ( error_class == 0 ) error_class = vm->findGlobalItem( m_className ); if ( error_class == 0 || ! error_class->isClass() ) { throw new GenericError( ErrorParam( e_undef_sym, __LINE__) .origin(e_orig_vm) .module( "core.Error" ). symbol( "Error::scriptize" ) .extra( m_className ).hard() ); } // CreateInstance will use ErrorObject, which increfs to us. CoreObject *cobject = error_class->asClass()->createInstance( this ); return cobject; } Error *Error::clone() const { return new Error( *this ); } //============================================================================== // Reflections namespace core { void Error_code_rfrom( CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); FALCON_REFLECT_INTEGER_FROM( error, errorCode ); } void Error_description_rfrom(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); FALCON_REFLECT_STRING_FROM( error, errorDescription ); } void Error_message_rfrom(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); FALCON_REFLECT_STRING_FROM( error, extraDescription ); } void Error_systemError_rfrom(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); FALCON_REFLECT_INTEGER_FROM( error, systemError ); } void Error_origin_rfrom(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); String origin; switch( error->origin() ) { case e_orig_compiler: origin = "compiler"; break; case e_orig_assembler: origin = "assembler"; break; case e_orig_loader: origin = "loader"; break; case e_orig_vm: origin = "vm"; break; case e_orig_script: origin = "script"; break; case e_orig_runtime: origin = "runtime"; break; case e_orig_mod: origin = "module"; break; default: origin = "unknown"; } property = new CoreString( origin ); } void Error_module_rfrom(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); FALCON_REFLECT_STRING_FROM( error, module ); } void Error_symbol_rfrom(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); FALCON_REFLECT_STRING_FROM( error, symbol ); } void Error_line_rfrom(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); FALCON_REFLECT_INTEGER_FROM( error, line ); } void Error_pc_rfrom(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); FALCON_REFLECT_INTEGER_FROM( error, pcounter ); } void Error_boxed_rfrom(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); if ( error->getBoxedError() != 0 ) { VMachine* vm = VMachine::getCurrent(); fassert( vm != 0 ); property = error->getBoxedError()->scriptize(vm); } else { property.setNil(); } } void Error_subErrors_rfrom(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); VMachine* vm = VMachine::getCurrent(); fassert( vm != 0 ); // scriptize sub-errors Error *ptr = error->subError(); if ( ptr != 0) { CoreArray *errorList = new CoreArray(); do { // CreateInstance will use ErrorObject, which increfs ptr. CoreObject *subobject = ptr->scriptize( vm ); errorList->append( subobject ); ptr = ptr->subError(); } while( ptr != 0 ); property = errorList; } else property.setNil(); } void Error_code_rto( CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); FALCON_REFLECT_INTEGER_TO( error, errorCode ); } void Error_description_rto(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); FALCON_REFLECT_STRING_TO( error, errorDescription ); } void Error_message_rto(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); FALCON_REFLECT_STRING_TO( error, extraDescription ); } void Error_systemError_rto(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); FALCON_REFLECT_INTEGER_TO( error, systemError ); } void Error_origin_rto(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); if ( property.isString() ) { String &origin = *property.asString(); if( origin == "compiler" ) { error->origin( e_orig_compiler ); } else if( origin == "assembler" ) { error->origin( e_orig_assembler ); } else if( origin == "loader" ) { error->origin( e_orig_loader ); } else if( origin == "vm" ) { error->origin( e_orig_vm ); } else if( origin == "script" ) { error->origin( e_orig_script ); } else if( origin == "runtime" ) { error->origin( e_orig_runtime ); } else if( origin == "module" ) { error->origin( e_orig_mod); } else { throw new ParamError( ErrorParam( e_param_range ) ); } return; } throw new ParamError( ErrorParam( e_inv_params ).extra( "S" ) ); } void Error_module_rto(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); FALCON_REFLECT_STRING_TO( error, module ); } void Error_symbol_rto(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); FALCON_REFLECT_STRING_TO( error, symbol ); } void Error_line_rto(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); FALCON_REFLECT_INTEGER_TO( error, line ); } void Error_pc_rto(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); FALCON_REFLECT_INTEGER_TO( error, pcounter ); } void Error_boxed_rto(CoreObject *instance, void *userData, Item &property, const PropEntry& ) { Error *error = static_cast(userData); if ( property.isObject() && property.asObject()->derivedFrom("Error") ) { error->boxError( static_cast(property.asObject()->getUserData()) ); } } //============================================================ // Reflector ErrorObject::ErrorObject( const CoreClass* cls, Error *err ): CRObject( cls ) { if ( err != 0 ) { err->incref(); setUserData( err ); } } ErrorObject::~ErrorObject() { if ( m_user_data != 0 ) getError()->decref(); } void ErrorObject::gcMark( uint32 mark ) { Error* error = getError(); if ( error != 0 ) memPool->markItem( const_cast(error->raised()) ); } ErrorObject *ErrorObject::clone() const { return new ErrorObject( m_generatedBy, getError() ); } //======================================================== // Factory function // CoreObject* ErrorObjectFactory( const CoreClass *cls, void *user_data, bool ) { return new ErrorObject( cls, (Error *) user_data ); } }} // namespace Falcon::core /* end of error.cpp */ engine/falcon_engine.rc000066400000000000000000000020741176363201700154510ustar00rootroot00000000000000#define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // for project wide versioning... #include #include VS_VERSION_INFO VERSIONINFO FILEVERSION FALCON_VERSION_RCINFO_N PRODUCTVERSION FALCON_VERSION_RCINFO_N FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x0L FILETYPE 0x0L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "041004b0" BEGIN VALUE "CompanyName", "Falcon Committee" VALUE "FileDescription", "Main Falcon engine" VALUE "FileVersion", FALCON_VERSION VALUE "InternalName", "engine" VALUE "LegalCopyright", "The Falcon Programming Language License" VALUE "OriginalFilename", "falcon_engine.dll" VALUE "ProductName", "The Falcon Programming Language" VALUE "ProductVersion", FALCON_VERSION END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x410, 1200 END END engine/falcondata.cpp000066400000000000000000000015331176363201700151330ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falcondata.cpp Falcon common object reflection architecture. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 22 Jun 2008 11:09:16 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Falcon common object reflection architecture. */ #include #include #include namespace Falcon { Destroyable::~Destroyable() {} bool FalconData::serialize( Stream *stream, bool bLive ) const { return false; } bool FalconData::deserialize( Stream *stream, bool bLive ) { return false; } } /* end of falcondata.cpp */ engine/falconobject.cpp000066400000000000000000000037261176363201700154760ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falconobject.cpp Falcon Object - Standard instance of classes in script ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom dic 5 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Falcon Object - Standard instance of classes in script */ #include #include #include #include namespace Falcon { FalconObject::FalconObject( const CoreClass *generator, bool bSeralizing ): CacheObject( generator, bSeralizing ) {} FalconObject::FalconObject( const FalconObject &other ): CacheObject( other ) {} FalconObject::~FalconObject() {} FalconObject *FalconObject::clone() const { // create and ask not to create the base copy of the item table. FalconObject* fo = new FalconObject( *this ); // was the inner data uncloneable? -- this is a rare case. if( m_user_data != 0 && fo->m_user_data == 0 ) return 0; // and let the gc take care of the Falcon Object return fo; } CoreObject* OpaqueObjectFactory( const CoreClass *cls, void *data, bool bDeserializing ) { CoreObject* co = new FalconObject( cls, bDeserializing ); co->setUserData( data ); return co; } CoreObject* FalconObjectFactory( const CoreClass *cls, void *data, bool bDeserializing ) { CoreObject* co = new FalconObject( cls, bDeserializing ); if ( data != 0 ) co->setUserData( static_cast(data) ); return co; } CoreObject* FalconSequenceFactory( const CoreClass *cls, void *data, bool bDeserializing ) { CoreObject* co = new FalconObject( cls, bDeserializing ); if ( data != 0 ) co->setUserData( static_cast(data) ); return co; } } // namespace Falcon /* end of falconobject.cpp */ engine/fassert.cpp000066400000000000000000000020131176363201700145000ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: fassert.cpp Falcon specific assertion raising ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab nov 4 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Falcon specific assertion raising */ #include #include #include extern "C" void _perform_FALCON_assert_func( const char *expr, const char *filename, int line, const char *funcName ) { printf( "FALCON ASSERTION FALIED.\n%s:%d in function %s ---\n%s\nProgram ending.\n", filename, line, funcName, expr ); abort(); } extern "C" void _perform_FALCON_assert( const char *expr, const char *filename, int line ) { printf( "FALCON ASSERTION FALIED.\n%s:%d ---\n%s\nProgram ending.\n", filename, line, expr ); abort(); } /* end of fassert.cpp */ engine/filestat.cpp000066400000000000000000000032041176363201700146470ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: filestats.cpp Directory and file specific statistic accounting ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: gio giu 21 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Directory and file specific statistic accounting */ #include #include #include #include #include namespace Falcon { FileStat::FileStat(): m_type(t_notFound), m_size(0), m_owner(0), m_group(0), m_access(0), m_attribs(0), m_ctime( 0 ), m_mtime( 0 ), m_atime( 0 ) {} FileStat::FileStat( const FileStat &other ): m_type( other.m_type ), m_size( other.m_size ), m_owner( other.m_owner ), m_group( other.m_group ), m_access( other.m_access ), m_attribs( other.m_attribs ) { if( other.m_ctime != 0 ) m_ctime = (TimeStamp *) other.m_ctime->clone(); else m_ctime = 0; if( other.m_atime != 0 ) m_atime = (TimeStamp *) other.m_atime->clone(); else m_atime = 0; if( other.m_mtime != 0 ) m_mtime = (TimeStamp *) other.m_mtime->clone(); else m_mtime = 0; } FileStat::~FileStat() { delete m_ctime; delete m_atime; delete m_mtime; } //=================================== // Reflection // FileStat *FileStat::clone() const { FileStat *other = new FileStat( *this ); return other; } } /* end of filestat.cpp */ engine/format.cpp000066400000000000000000000652051176363201700143350ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: format.cpp Format base class ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar apr 17 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Format base class */ #include #include #include #include #include #include #include #include #include #include namespace Falcon { void Format::reset() { m_convType = e_tStr; m_misAct = e_actNoAction; m_fixedSize = false; m_size = 0; m_paddingChr = ' '; m_thousandSep = ','; m_decimalSep = '.'; m_grouping = 0; m_rightAlign = false; m_decimals = 0; m_negFormat = e_minusFront; m_numFormat = e_decimal; m_nilFormat = e_nilNil; m_posOfObjectFmt = String::npos; } bool Format::parse( const String &fmt ) { String tmp; uint32 pos = 0; uint32 len = fmt.length(); typedef enum { e_sInitial, e_sSize, e_sDecimals, e_sPadding, e_sDecSep, e_sGroupSep, e_sGroupSep2, e_sErrorEffect, e_sErrorEffect2, e_sNilMode, e_sNegFmt, e_sNegFmt2 } t_state; t_state state = e_sInitial; while( pos < len ) { uint32 chr = fmt.getCharAt( pos ); switch( state ) { //============================= // Basic state. // case e_sInitial: if( chr >= '0' && chr <= '9' ) { // size already given if ( m_size != 0 ) return false; state = e_sSize; tmp.size(0); tmp += chr; break; } // else: switch( chr ) { case 'N': // it should be an octal. m_convType = e_tNum; numFormat( e_decimal ); break; case '.': // it should be an octal. m_convType = e_tNum; state = e_sDecimals; tmp.size(0); break; case 'b': // it should be an octal. m_convType = e_tNum; numFormat( e_binary ); break; case 'B': // it should be an octal. m_convType = e_tNum; numFormat( e_binaryB ); break; case 'd': m_convType = e_tNum; state = e_sDecSep; break; case 'p': state = e_sPadding; break; case 'g': m_convType = e_tNum; state = e_sGroupSep; break; case 'G': m_convType = e_tNum; state = e_sGroupSep2; break; case '0': // it should be an octal. m_convType = e_tNum; numFormat( e_octalZero ); break; case 'o': // it should be an octal. m_convType = e_tNum; numFormat( e_octal ); break; case 'x': // it should be an octal. m_convType = e_tNum; numFormat( e_hexLower ); break; case 'X': // it should be an octal. m_convType = e_tNum; numFormat( e_hexUpper ); break; case 'c': // it should be an octal. m_convType = e_tNum; numFormat( e_cHexLower ); break; case 'C': // it should be an octal. m_convType = e_tNum; numFormat( e_cHexUpper ); break; case 'e': // it should be in scientific format m_convType = e_tNum; numFormat( e_scientific ); break; case '/': // it should be an octal. state = e_sErrorEffect; break; case 'n': state = e_sNilMode; break; case '|': m_posOfObjectFmt = pos; m_convType = e_tStr; // complete parsing pos = len; break; case '+': m_negFormat = e_plusMinusFront; state = e_sNegFmt; break; case '-': m_negFormat = e_minusFront; state = e_sNegFmt2; break; case '[': m_negFormat = e_parenthesis; break; case ']': m_negFormat = e_parpad; break; case 'r': m_rightAlign = true; break; default: // unrecognized character m_convType = e_tError; return false; } break; //============================= // Parse padding // case e_sDecSep: m_decimalSep = chr; state = e_sInitial; break; case e_sPadding: m_paddingChr = chr; state = e_sInitial; break; case e_sGroupSep: if( chr >= '0' && chr <='9' ) { m_grouping = chr - '0'; state = e_sGroupSep2; } else { m_thousandSep = chr; state = e_sInitial; } break; case e_sGroupSep2: m_thousandSep = chr; state = e_sInitial; break; //============================= // Size parsing state // case e_sSize: if( chr >= '0' && chr <= '9' ) { tmp += chr; // size too wide if ( tmp.length() > 4 ) { m_convType = e_tError; return false; } } else { int64 tgt; tmp.parseInt( tgt ); fieldSize( (uint16) tgt ); if( chr == '*' ) { fixedSize( true ); } else { // reparse current char --pos; } state = e_sInitial; } break; //============================= // Decimals parsing state // case e_sDecimals: if( chr >= '0' && chr <= '9' ) { tmp += chr; // size too wide if ( tmp.length() > 2 ) { m_convType = e_tError; return false; } } else { int64 tgt; tmp.parseInt( tgt ); decimals( (uint8) tgt ); // reparse current char --pos; state = e_sInitial; } break; //=============================================== // Parsing what should be done in case of error. // case e_sErrorEffect: if ( chr == 'c' ) { state = e_sErrorEffect2; break; } // else switch( chr ) { case 'n': mismatchAction( e_actNil ); break; case '0': mismatchAction( e_actZero ); break; case 'r': mismatchAction( e_actRaise ); break; default: // invalid choiche m_convType = e_tError; return false; } state = e_sInitial; break; case e_sErrorEffect2: switch( chr ) { case 'n': mismatchAction( e_actConvertNil ); break; case '0': mismatchAction( e_actConvertZero ); break; case 'r': mismatchAction( e_actConvertRaise ); break; default: // invalid choiche m_convType = e_tError; return false; } state = e_sInitial; break; //================================= // parsing what do to with a Nil // case e_sNilMode: switch( chr ) { case 'n': m_nilFormat = e_nilEmpty; break; case 'N': m_nilFormat = e_nilN; break; case 'l': m_nilFormat = e_nilnil; break; case 'L': m_nilFormat = e_nilNil; break; case 'u': m_nilFormat = e_nilNull; break; case 'U': m_nilFormat = e_nilNULL; break; case 'o': m_nilFormat = e_nilNone; break; case 'A': m_nilFormat = e_nilNA; break; default: m_convType = e_tError; return false; } state = e_sInitial; break; //================================= // Parsing neg format case e_sNegFmt: switch( chr ) { case '+': m_negFormat = e_plusMinusBack; break; case '^': m_negFormat = e_plusMinusEnd; break; default: pos--; } state = e_sInitial; break; //================================= // Parsing neg format 2 case e_sNegFmt2: switch( chr ) { case '-': m_negFormat = e_minusBack; break; case '^': m_negFormat = e_minusEnd; break; default: pos--; } state = e_sInitial; break; } ++pos; } // end main loop // verify output status switch( state ) { case e_sInitial: // ok case e_sNegFmt: break; case e_sSize: { int64 tgt; tmp.parseInt( tgt ); fieldSize( (uint8) tgt ); } break; case e_sDecimals: { int64 tgt; tmp.parseInt( tgt ); decimals( (uint8) tgt ); } break; // any other state means we're left in the middle of something default: m_convType = e_tError; return false; } // if everything goes fine... m_originalFormat = fmt; return true; } bool Format::format( VMachine *vm, const Item &source, String &target ) { String sBuffer; switch( source.type() ) { case FLC_ITEM_NIL: switch( m_nilFormat ) { case e_nilEmpty: break; case e_nilNil: sBuffer = "Nil"; break; case e_nilN: sBuffer = "N"; break; case e_nilnil: sBuffer = "nil"; break; case e_nilNA: sBuffer = "N/A"; break; case e_nilNone: sBuffer = "None"; break; case e_nilNULL: sBuffer = "NULL"; break; case e_nilNull: sBuffer = "Null"; break; case e_nilPad: sBuffer.append( m_paddingChr ); } applyPad( sBuffer ); break; case FLC_ITEM_UNB: sBuffer = "_"; applyPad( sBuffer ); break; //================================================== // Parse an integer // case FLC_ITEM_INT: { int64 num = source.asInteger(); // number formats are compatible with string formats if ( m_convType != e_tNum && m_convType != e_tStr ) { return processMismatch( vm, source, target ); } formatInt( num, sBuffer, true ); // minus sign must be added AFTER padding with parentesis/fixed size or with *End signs, // else it must be added before. if ( negBeforePad() ) { applyNeg( sBuffer, num ); applyPad( sBuffer ); } else { applyPad( sBuffer, negPadSize( num ) ); applyNeg( sBuffer, num ); } } break; //================================================== // Parse double format // case FLC_ITEM_NUM: { numeric num = source.asNumeric(); // number formats are compatible with string formats if ( m_convType != e_tNum && m_convType != e_tStr ) { return processMismatch( vm, source, target ); } if( m_numFormat == e_scientific ) { formatScientific( num, sBuffer ); } else { double intPart, fractPart; bool bNeg, bIntIsZero; fractPart = modf( num, &intPart ); if ( intPart < 0.0 ) { intPart = -intPart; fractPart = -fractPart; bNeg = true; bIntIsZero = false; } else { bIntIsZero = intPart > 0.0 ? false : true; if ( fractPart < 0.0 ) { fractPart = -fractPart; // draw neg sign only if < 0 but int bNeg = true; } else bNeg = false; } String precPart; int base = 10; switch( m_numFormat ) { case e_binary: case e_binaryB: base = 2; break; case e_octalZero: case e_octal: base = 8; break; case e_cHexUpper: case e_hexUpper: case e_cHexLower: case e_hexLower: base = 16; break; default: break; } while( intPart > 9e14 ) { intPart /= base; precPart.append( '0' ); } // manual round if( (pow((double)10.0,-(m_decimals+1)) *5)+fractPart >=1.0) { intPart++; bIntIsZero = false; } uint8 decs = m_decimals; m_decimals = 0; formatInt( (int64) intPart, sBuffer, false ); sBuffer.append( precPart ); // now we can add the grouping if ( m_grouping > 0 ) { String token; token.append( m_thousandSep ); uint32 pos = sBuffer.size(); while( pos > m_grouping ) { pos -= m_grouping; sBuffer.insert( pos, 0, token ); } } // finally add decimals m_decimals = decs; if( base == 10 && m_decimals > 0 ) { char bufFmt[32]; char buffer[255]; sprintf( bufFmt, "%%.%df", m_decimals ); sprintf( buffer, bufFmt, fractPart ); sBuffer.append( m_decimalSep ); sBuffer.append( buffer + 2 ); } else if ( bIntIsZero ) { // do not print -0! bNeg = false; } // we must fix the number. num = bNeg ? -1.0 : 1.0; } // minus sign must be added AFTER padding with parentesis/fixed size or with *End signs, // else it must be added before. if ( negBeforePad() ) { applyNeg( sBuffer, (int64) num ); applyPad( sBuffer ); } else { applyPad( sBuffer, negPadSize( (int64) num ) ); applyNeg( sBuffer, (int64) num ); } } break; case FLC_ITEM_RANGE: { // number formats are compatible with string formats if ( m_convType != e_tNum && m_convType != e_tStr ) { return processMismatch( vm, source, target ); } int64 begin = source.asRangeStart(); String sBuf1, sBuf2, sBuf3; formatInt( begin, sBuf1, true ); //apply negative format now. applyNeg( sBuf1, (int64) begin ); if ( ! source.asRangeIsOpen() ) { int64 end = source.asRangeEnd(); formatInt( end, sBuf2, true ); applyNeg( sBuf2, (int64) end ); int64 step = source.asRangeStep(); if ( (begin <= end && step != 1) || (begin > end && step != -1 ) ) { formatInt( step, sBuf3, true ); applyNeg( sBuf3, (int64) step ); sBuffer = "[" + sBuf1 + ":" + sBuf2 + ":" + sBuf3 + "]"; } else sBuffer = "[" + sBuf1 + ":" + sBuf2 + "]"; } else sBuffer = "[" + sBuf1 + ":" + sBuf2 + "]"; applyPad( sBuffer ); } break; case FLC_ITEM_STRING: { // number formats are compatible with string formats if ( m_convType != e_tStr ) { return processMismatch( vm, source, target ); } sBuffer = *source.asString(); applyPad( sBuffer ); } break; case FLC_ITEM_OBJECT: { // try to format the object if( vm != 0 ) { if( m_posOfObjectFmt != String::npos ) { vm->itemToString( sBuffer, &source, m_originalFormat.subString( m_posOfObjectFmt + 1 ) ); } else { vm->itemToString( sBuffer, &source ); } } else { return processMismatch( vm, source, target ); } applyPad( sBuffer ); } break; default: return processMismatch( vm, source, target ); } // out of bounds? if ( m_size > 0 && m_fixedSize && sBuffer.length() > m_size ) { return false; } target += sBuffer; return true; } void Format::formatScientific( numeric num, String &sBuffer ) { char buffer[36]; bool bNeg; if( num < 0 ) { num = - num; bNeg = true; } else { bNeg = false; } sprintf( buffer, "%35e", num ); sBuffer += buffer; } bool Format::processMismatch( VMachine *vm, const Item &source, String &target ) { Item dummy; switch( m_misAct ) { case e_actNoAction: return false; case e_actConvertNil: if( tryConvertAndFormat( vm, source, target ) ) return true; // else fallthrouhg case e_actNil: dummy.setNil(); return format( vm, dummy, target ); break; case e_actConvertZero: if( tryConvertAndFormat( vm, source, target ) ) return true; // else fallthrouhg case e_actZero: { dummy = (int64) 0; // also, force conversion m_misAct = e_actConvertZero; bool ret = format( vm, source, target ); m_misAct = e_actZero; return ret; } case e_actConvertRaise: if( tryConvertAndFormat( vm, source, target ) ) return true; // else fallthrouhg case e_actRaise: if( vm != 0 ) { throw new TypeError( ErrorParam( e_fmt_convert, __LINE__ ) .origin( e_orig_runtime ) ); } return false; } // should not happen return false; } bool Format::tryConvertAndFormat( VMachine *vm, const Item &source, String &target ) { // first convert to string, then try to convert to number // try a basic string conversion String temp; if( vm != 0 ) { if( m_posOfObjectFmt != String::npos ) { vm->itemToString( temp, &source, originalFormat().subString( m_posOfObjectFmt + 1 ) ); } else vm->itemToString( temp, &source ); } else { source.toString( temp ); } // If conversion was numeric, try to to reformat the thing into a number if( m_convType == e_tNum ) { numeric num; if( ! temp.parseDouble( num ) ) return false; return format( vm, num, target ); } return format( vm, &temp, target ); } void Format::formatInt( int64 number, String &target, bool bUseGroup ) { if ( m_numFormat == e_scientific ) { formatScientific( (numeric) number, target ); return; } if ( number == 0 ) { target += "0"; return; } // prepare the buffer const int bufSize = 132; // int64 binary size + separator per each bit + minus format + zero uint32 buffer[ bufSize ]; uint32 pos = bufSize - 2; buffer[ pos+1 ] = 0; // allow room for post formatters if ( number < 0 ) { number = - number; } // init grouping int grp = m_grouping; // init base int base = 10; uint32 baseHexChr = (uint32) 'X'; switch( m_numFormat ) { case e_decimal: base = 10; break; case e_binary: case e_binaryB: base = 2; break; case e_octalZero: target.append( '0' ); // fallthrough case e_octal: base = 8; break; case e_cHexLower: target.append( '0' ); target.append( 'x' ); // fallthrough case e_hexLower: base = 16; baseHexChr = 'a'; break; case e_cHexUpper: target.append( '0' ); target.append( 'x' ); // fallthrough case e_hexUpper: base = 16; baseHexChr = 'A'; break; default: break; } while( number != 0 ) { uint32 cipher =(uint32) (number % base); if ( cipher < 10 ) { buffer[pos--] = (char) ( cipher + 0x30 ); } else { buffer[pos--] = (char) ( (cipher-10) + baseHexChr ); } number /= base; if( number != 0 && bUseGroup && m_grouping != 0 ) { if( --grp == 0 ) { buffer[pos--] = (char) m_thousandSep; grp = m_grouping; } } } pos++; // unroll the parsed buffer while( buffer[pos] != 0 ) { target += buffer[pos]; ++pos; } if( m_numFormat == e_decimal ) { if( m_decimals != 0 ) { target.append( m_decimalSep ); for( int d = 0; d < m_decimals; d ++ ) target.append( '0' ); } } else if ( m_numFormat == e_binaryB ) { target.append( 'b' ); } } void Format::applyNeg( String &target, int64 number ) { // apply negative format if ( number < 0 ) { switch( m_negFormat ) { case e_plusMinusFront: case e_minusFront: target.prepend( '-' ); break; case e_plusMinusBack: case e_minusBack: target.append( '-' ); break; case e_minusEnd: case e_plusMinusEnd: if( m_rightAlign ) target.prepend( '-' ); else target.append( '-' ); break; case e_parpad: case e_parenthesis: target.append( ')' ); target.prepend( '(' ); break; } } else { switch( m_negFormat ) { case e_plusMinusFront: target.prepend( '+' ); break; case e_plusMinusBack: target.append( '+' ); break; case e_plusMinusEnd: if( m_rightAlign ) target.prepend( '+' ); else target.append( '+' ); break; case e_parpad: target.prepend( m_paddingChr ); target.append( m_paddingChr ); break; default: break; } } } int Format::negPadSize( int64 number ) { if ( number < 0 ) { switch( m_negFormat ) { case e_minusFront: return 1; case e_plusMinusFront: return 1; case e_minusBack: return 1; case e_plusMinusBack: return 1; case e_parenthesis: return 2; case e_parpad: return 2; case e_minusEnd: return 1; case e_plusMinusEnd: return 1; } } else { switch( m_negFormat ) { case e_minusFront: return 0; case e_plusMinusFront: return 1; case e_minusBack: return 0; case e_plusMinusBack: return 1; case e_parenthesis: return 0; case e_parpad: return 2; case e_minusEnd: return 0; case e_plusMinusEnd: return 1; } } // should never happen return 0; } bool Format::negBeforePad() { // if the format requires it explicitly... if ( m_negFormat == e_plusMinusEnd || m_negFormat == e_minusEnd ) return false; // else, if padding char is 0... if ( m_paddingChr == '0' ) return false; // finally, if size is fixed and parenthesis are requested if ( m_fixedSize && ( m_negFormat == e_parenthesis || m_negFormat == e_parpad ) ) return false; return true; } void Format::applyPad( String &target, uint32 extraSize ) { uint32 tgSize = m_size; uint32 strSize = target.length() + extraSize; if( tgSize <= strSize ) return; String strBuffer; strBuffer.reserve( tgSize - strSize ); while( strSize < tgSize ) { strBuffer.append( m_paddingChr ); ++strSize; } if( m_rightAlign ) { target.prepend( strBuffer ); } else { target += strBuffer; } } /* void Format::getProperty( const String &propName, Item &prop ) { if( propName == "size" ) { prop = (int64) m_size; } else if( propName == "decimals" ) { prop = (int64) m_decimals; } else if( propName == "paddingChr" ) { prop = (int64) m_paddingChr; } else if( propName == "groupingChr" ) { prop = (int64) m_thousandSep; } else if( propName == "decimalChr" ) { prop = (int64) m_decimalSep; } else if( propName == "grouiping" ) { prop = (int64) m_grouping; } else if( propName == "fixedSize" ) { prop = (int64) (m_fixedSize ? 1:0); } else if( propName == "rightAlign" ) { prop = (int64) (m_rightAlign ? 1:0); } else if( propName == "originalFormat" ) { prop = &m_originalFormat; } else if( propName == "convType" ) { prop = (int64) m_convType; } else if( propName == "misAct" ) { prop = (int64) m_misAct; } else if( propName == "nilFormat" ) { prop = (int64) m_nilFormat; } else if( propName == "negFormat" ) { prop = (int64) m_negFormat; } else if( propName == "numFormat" ) { prop = (int64) m_numFormat; } } void Format::setProperty( const String &propName, Item &prop ) { // read only } */ Format *Format::clone() const { return new Format( this->originalFormat() ); } } /* end of format.cpp */ engine/fstream_sys_unix.cpp000066400000000000000000000257221176363201700164470ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: fstream_sys_unix.cpp Unix system specific FILE service support ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom mar 12 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Unix system specific FILE service support. */ #include #include #include #include #include #include #include #include #include #include #include #include namespace Falcon { FileSysData *UnixFileSysData::dup() { int fd2 = ::dup( m_handle ); if ( fd2 >= 0 ) { UnixFileSysData *other = new UnixFileSysData( fd2, m_lastError ); return other; } else return 0; } BaseFileStream::BaseFileStream( const BaseFileStream &other ): Stream( other ) { m_fsData = other.m_fsData->dup(); } BaseFileStream::~BaseFileStream() { close(); delete m_fsData; } bool BaseFileStream::close() { UnixFileSysData *data = static_cast< UnixFileSysData *>( m_fsData ); if ( m_status & Stream::t_open ) { if( ::close( data->m_handle ) == -1 ) { data->m_lastError = errno; m_status = t_error; return false; } } data->m_lastError = 0; m_status = m_status & (~ Stream::t_open); return true; } int32 BaseFileStream::read( void *buffer, int32 size ) { UnixFileSysData *data = static_cast< UnixFileSysData *>( m_fsData ); int32 result = ::read( data->m_handle, buffer, size ); if ( result < 0 ) { data->m_lastError = errno; m_status = Stream::t_error; m_status = t_error; return -1; } if ( result == 0 ) { m_status = m_status | Stream::t_eof; } data->m_lastError = 0; m_lastMoved = result; return result; } int32 BaseFileStream::write( const void *buffer, int32 size ) { UnixFileSysData *data = static_cast< UnixFileSysData *>( m_fsData ); int32 result = ::write( data->m_handle, buffer, size ); if ( result < 0 ) { data->m_lastError = errno; m_status = Stream::t_error; return -1; } data->m_lastError = 0; m_lastMoved = result; return result; } bool BaseFileStream::put( uint32 chr ) { /** \TODO optimize */ byte b = (byte) chr; return write( &b, 1 ) == 1; } bool BaseFileStream::get( uint32 &chr ) { /** \TODO optimize */ if( popBuffer( chr ) ) return true; byte b; if ( read( &b, 1 ) == 1 ) { chr = (uint32) b; return true; } return false; } int64 BaseFileStream::seek( int64 pos, e_whence whence ) { UnixFileSysData *data = static_cast< UnixFileSysData *>( m_fsData ); int from; switch( whence ) { case ew_begin: from = SEEK_SET; break; case ew_cur: from = SEEK_CUR; break; case ew_end: from = SEEK_END; break; default: from = SEEK_SET; } pos = (int64) lseek( data->m_handle, pos, from ); if( pos < 0 ) { data->m_lastError = errno; m_status = Stream::t_error; return -1; } else m_status = m_status & ~Stream::t_eof; setError( 0 ); return pos; } int64 BaseFileStream::tell() { UnixFileSysData *data = static_cast< UnixFileSysData *>( m_fsData ); int64 pos = (int64) lseek( data->m_handle, 0, SEEK_CUR ); if( pos < 0 ) { setError( errno ); return -1; } setError( 0 ); return pos; } bool BaseFileStream::truncate( int64 pos ) { UnixFileSysData *data = static_cast< UnixFileSysData *>( m_fsData ); if ( pos < 0 ) { pos = tell(); if ( pos < 0 ) return false; } int32 res = ftruncate( data->m_handle, pos ); if( res < 0 ) { setError( errno ); return false; } setError( 0 ); return true; } bool BaseFileStream::errorDescription( ::Falcon::String &description ) const { if ( Stream::errorDescription( description ) ) return true; UnixFileSysData *data = static_cast< UnixFileSysData *>( m_fsData ); if ( data->m_lastError == 0 ) return false; const char *error; if ( data->m_lastError == -1 ) error = "Out of memory"; error = strerror( data->m_lastError ); if( error == 0 ) { return false; } description.bufferize( error ); return true; } int64 BaseFileStream::lastError() const { UnixFileSysData *data = static_cast< UnixFileSysData *>( m_fsData ); return (int64) data->m_lastError; } void BaseFileStream::setError( int64 errorCode ) { UnixFileSysData *data = static_cast< UnixFileSysData *>( m_fsData ); data->m_lastError = errorCode; if ( errorCode != 0 ) status( t_error ); else status( status() & ~Stream::t_error ); } bool BaseFileStream::writeString( const String &content, uint32 begin, uint32 end ) { UnixFileSysData *data = static_cast< UnixFileSysData *>( m_fsData ); uint32 done = begin; uint32 stop = content.size(); uint32 charSize = content.manipulator()->charSize(); if ( end < stop / charSize ) stop = end * charSize; while ( done < stop ) { int32 written = ::write( data->m_handle, (const char *) (content.getRawStorage() + done), content.size() - done ); if ( written < 0 ) { setError( errno ); m_lastMoved = done; return false; } done += written; } setError( 0 ); m_lastMoved = done - begin; return true; } bool BaseFileStream::readString( String &content, uint32 size ) { // TODO OPTIMIZE uint32 chr; content.size( 0 ); //UnixFileSysData *data = static_cast< UnixFileSysData *>( m_fsData ); while( size > 0 && get( chr ) ) { size--; content.append( chr ); } if ( size == 0 || eof() ) { setError( 0 ); return true; } setError( errno ); return false; } int32 BaseFileStream::readAvailable( int32 msec, const Sys::SystemData *sysData ) { UnixFileSysData *data = static_cast< UnixFileSysData *>( m_fsData ); /* Temporarily turned off because a darwin flaw struct pollfd poller; poller.fd = data->m_handle; poller.events = POLLIN | POLLPRI; if ( poll( &poller, 1, msec) == 1 ) { data->m_lastError = 0; if( (poller.revents & (POLLIN | POLLPRI | POLLHUP )) != 0 ) return 1; } else { if ( errno == EINPROGRESS ) { data->m_lastError = 0; return 0; } data->m_lastError = errno; return -1; } return 0; */ struct timeval tv, *tvp; fd_set set; int last; FD_ZERO( &set ); FD_SET( data->m_handle, &set ); if( sysData != 0 ) { last = sysData->m_sysData->interruptPipe[0]; FD_SET( last, &set ); if( last < data->m_handle ) last = data->m_handle; } else last = data->m_handle; if ( msec >= 0 ) { tvp = &tv; tv.tv_sec = msec / 1000; tv.tv_usec = (msec % 1000 ) * 1000; } else tvp = 0; switch( select( last + 1, &set, 0, 0, tvp ) ) { case 1: case 2: if ( sysData != 0 && FD_ISSET( sysData->m_sysData->interruptPipe[0], &set ) ) { m_status = t_interrupted; return -1; } return FD_ISSET( data->m_handle, &set ) ? 1 : 0; case -1: if( errno == EINPROGRESS ) { setError( 0 ); return 0; } setError( errno ); return -1; } return 0; } int32 BaseFileStream::writeAvailable( int32 msec, const Sys::SystemData *sysData ) { UnixFileSysData *data = static_cast< UnixFileSysData *>( m_fsData ); struct pollfd poller[2]; int fds; poller[0].fd = data->m_handle; poller[0].events = POLLOUT; if ( sysData != 0 ) { fds = 2; poller[1].fd = sysData->m_sysData->interruptPipe[0]; poller[1].events = POLLIN; } else fds = 1; int res; while( ( res = poll( poller, fds, msec ) ) == EAGAIN ); if ( res > 0 ) { setError( 0 ); if( sysData != 0 && (poller[1].revents & POLLIN) != 0 ) { m_status = t_interrupted; return -1; } if( (poller[0].revents & ( POLLOUT | POLLHUP ) ) != 0 ) return 1; } else { setError( errno ); return -1; } return 0; } BaseFileStream *BaseFileStream::clone() const { BaseFileStream *ge = new BaseFileStream( *this ); if ( ge->m_fsData == 0 ) { delete ge; return 0; } return ge; } //========================================= // File Stream //========================================= FileStream::FileStream(): BaseFileStream( t_file, new UnixFileSysData( -1, 0 ) ) { status( t_none ); } bool FileStream::open( const String &filename, t_openMode mode, t_shareMode share ) { UnixFileSysData *data = static_cast< UnixFileSysData * >( m_fsData ); int omode = 0; if ( data->m_handle > 0 ) ::close( data->m_handle ); if ( mode == e_omReadWrite ) omode = O_RDWR; else if ( mode == e_omReadOnly ) omode = O_RDONLY; else omode = O_WRONLY; // todo: do something about share mode AutoCString cfilename( filename ); int handle; errno = 0; handle = ::open( cfilename.c_str(), omode ); data->m_handle = handle; if ( handle < 0 ) { setError( errno ); status( t_error ); return false; } status( t_open ); data->m_lastError = 0; return true; } bool FileStream::create( const String &filename, t_attributes mode, t_shareMode share ) { UnixFileSysData *data = static_cast< UnixFileSysData * >( m_fsData ); if ( data->m_handle > 0 ) ::close( data->m_handle ); //TODO: something about sharing AutoCString cfilename( filename ); errno=0; data->m_handle = ::open( cfilename.c_str(), O_CREAT | O_RDWR | O_TRUNC, static_cast( mode ) ); if ( data->m_handle < 0 ) { data->m_lastError = errno; status( t_error ); return false; } status( t_open ); data->m_lastError = 0; return true; } void FileStream::setSystemData( const FileSysData &fsData ) { const UnixFileSysData *data = static_cast< const UnixFileSysData *>( &fsData ); UnixFileSysData *myData = static_cast< UnixFileSysData *>( m_fsData ); myData->m_handle = data->m_handle; myData->m_lastError = data->m_lastError; } StdInStream::StdInStream(): InputStream( new UnixFileSysData( dup(STDIN_FILENO), 0 ) ) { } StdOutStream::StdOutStream(): OutputStream( new UnixFileSysData( dup(STDOUT_FILENO), 0 ) ) { } StdErrStream::StdErrStream(): OutputStream( new UnixFileSysData( dup(STDERR_FILENO), 0 ) ) { } RawStdInStream::RawStdInStream(): InputStream( new UnixFileSysData( STDIN_FILENO, 0 ) ) { } RawStdOutStream::RawStdOutStream(): OutputStream( new UnixFileSysData( STDOUT_FILENO, 0 ) ) { } RawStdErrStream::RawStdErrStream(): OutputStream( new UnixFileSysData( STDERR_FILENO, 0 ) ) { } } /* end of fstream_sys_unix.cpp */ engine/fstream_sys_win.cpp000066400000000000000000000427731176363201700162660ustar00rootroot00000000000000/* FALCON - Falcon advanced simple text evaluator. FILE: fstream_sys_win.cpp Unix system specific FILE service support ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom mar 12 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Widnows system specific FILE service support. */ #include #include #include #include #include #include #ifndef INVALID_SET_FILE_POINTER #define INVALID_SET_FILE_POINTER ((DWORD)-1) #endif namespace Falcon { FileSysData *WinFileSysData::dup() { HANDLE dupped; HANDLE curProc = GetCurrentProcess(); if (! DuplicateHandle( curProc, // handle to the source process m_handle, // handle to duplicate curProc, // handle to process to duplicate to &dupped, // pointer to duplicate handle 0, // access for duplicate handle TRUE, // handle inheritance flag DUPLICATE_SAME_ACCESS // optional actions ) ) { return 0; } return new WinFileSysData( dupped, m_lastError, m_isConsole, m_direction, m_isPipe ); } BaseFileStream::BaseFileStream( const BaseFileStream &gs ): Stream( gs ) { m_fsData = gs.m_fsData->dup(); } BaseFileStream::~BaseFileStream() { close(); delete m_fsData; } bool BaseFileStream::close() { WinFileSysData *data = static_cast< WinFileSysData *>( m_fsData ); if ( open() ) { if( ! CloseHandle( data->m_handle ) ) { data->m_lastError = GetLastError(); m_status = Stream::t_error; return false; } } data->m_lastError = 0; m_status = m_status & static_cast(~ static_cast(Stream::t_open)); return true; } int32 BaseFileStream::read( void *buffer, int32 size ) { WinFileSysData *data = static_cast< WinFileSysData *>( m_fsData ); DWORD result; if ( ! ReadFile( data->m_handle, buffer, size, &result, NULL ) ) { data->m_lastError = GetLastError(); if( data->m_lastError == ERROR_NOACCESS || data->m_lastError == ERROR_HANDLE_EOF || data->m_lastError == ERROR_BROKEN_PIPE ) { // ReadFile returns ERROR_NOACCESS at EOF data->m_lastError = 0; m_status = m_status | Stream::t_eof; return 0; } m_status = m_status | Stream::t_error; return -1; } if ( result == 0 ) { m_status = m_status | Stream::t_eof; } data->m_lastError = 0; m_lastMoved = result; return result; } int32 BaseFileStream::write( const void *buffer, int32 size ) { WinFileSysData *data = static_cast< WinFileSysData *>( m_fsData ); DWORD result; if ( ! WriteFile( data->m_handle, buffer, size, &result, NULL ) ) { data->m_lastError = GetLastError(); m_status = m_status | Stream::t_error; return -1; } data->m_lastError = 0; m_lastMoved = result; return result; } bool BaseFileStream::put( uint32 chr ) { byte b = (byte) chr; return write( &b, 1 ) == 1; } bool BaseFileStream::get( uint32 &chr ) { if( popBuffer( chr ) ) return true; byte b; if ( read( &b, 1 ) == 1 ) { chr = (uint32) b; return true; } return false; } int64 BaseFileStream::seek( int64 pos, e_whence whence ) { WinFileSysData *data = static_cast< WinFileSysData *>( m_fsData ); DWORD from; switch(whence) { case 0: from = FILE_BEGIN; break; case 1: from = FILE_CURRENT; break; case 2: from = FILE_END; break; default: from = FILE_BEGIN; } LONG posLow = (LONG)(pos & 0xFFFFFFFF); LONG posHI = (LONG) (pos >> 32); DWORD npos = (int32) SetFilePointer( data->m_handle, posLow, &posHI, from ); if( npos == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR ) { data->m_lastError = GetLastError(); m_status = m_status | Stream::t_error; return -1; } else { #ifdef _MSC_VER m_status = (Stream::t_status)(((uint32)m_status) & ~((uint32)Stream::t_eof)); #else m_status = m_status & ~Stream::t_eof; #endif } data->m_lastError = 0; return npos | ((int64)posHI) << 32; } int64 BaseFileStream::tell() { WinFileSysData *data = static_cast< WinFileSysData *>( m_fsData ); LONG posHI = 0; DWORD npos = (int32) SetFilePointer( data->m_handle, 0, &posHI, FILE_CURRENT ); if( npos == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR ) { data->m_lastError = GetLastError(); m_status = m_status | Stream::t_error; return -1; } data->m_lastError = 0; return npos | ((int64)posHI) << 32; } bool BaseFileStream::truncate( int64 pos ) { WinFileSysData *data = static_cast< WinFileSysData *>( m_fsData ); if ( pos > 0 ) { LONG posLow = (LONG)(pos & 0xFFFFFFFF); LONG posHI = (LONG) (pos >> 32); SetFilePointer( data->m_handle, posLow, &posHI, FILE_BEGIN ); if( GetLastError() != NO_ERROR ) { data->m_lastError = GetLastError(); m_status = m_status | Stream::t_error; return false; } } SetEndOfFile( data->m_handle ); if( GetLastError() != NO_ERROR ) { data->m_lastError = GetLastError(); m_status = m_status | Stream::t_error; return false; } SetFilePointer( data->m_handle, 0, 0, FILE_END ); data->m_lastError = 0; return true; } bool BaseFileStream::errorDescription( ::Falcon::String &description ) const { if ( Stream::errorDescription( description ) ) return true; WinFileSysData *data = static_cast< WinFileSysData *>( m_fsData ); if ( data->m_lastError == 0 ) return false; LPVOID lpMsgBuf; DWORD res = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, data->m_lastError, LANG_USER_DEFAULT, (LPTSTR) &lpMsgBuf, 0, NULL ); if ( res == 0 ) { description = "Impossible to retreive error description"; return false; } else { description = (char *) lpMsgBuf; description.bufferize(); LocalFree(lpMsgBuf); return true; } } int64 BaseFileStream::lastError() const { WinFileSysData *data = static_cast< WinFileSysData *>( m_fsData ); return (int64) data->m_lastError; } int BaseFileStream::readAvailable( int32 msec, const Sys::SystemData *sysData ) { WinFileSysData *data = static_cast< WinFileSysData *>( m_fsData ); if ( data->m_isConsole || data->m_isPipe ) { if ( data->m_direction == WinFileSysData::e_dirOut ) { if (msec > 0 ) ::Sleep( msec ); return false; } DWORD waitTime; DWORD size = 0; if ( data->m_isPipe ) { if ( msec < 0 ) { while ( size == 0 ) { Sleep(10); if ( ! PeekNamedPipe( data->m_handle, NULL, 0, NULL, &size, NULL ) ) return -1; } return 1; } else { waitTime = (DWORD) msec; DWORD result = PeekNamedPipe( data->m_handle, NULL, 0, NULL, &size, NULL ); while ( result && size == 0 && waitTime > 0 ) { // Interrupted? if( WaitForSingleObject( sysData->m_sysData->evtInterrupt, 1 ) == WAIT_OBJECT_0 ) return -2; waitTime -= 1; result = PeekNamedPipe( data->m_handle, NULL, 0, NULL, &size, NULL ); } if ( ! result ) return -1; return (size > 0) ? 1 : 0; } } else { waitTime = msec < 0 ? INFINITE : msec; HANDLE waiting[2]; waiting[0] = data->m_handle; waiting[1] = sysData->m_sysData->evtInterrupt; DWORD res = WaitForMultipleObjects( 2, waiting, FALSE,waitTime ); if ( res == WAIT_OBJECT_0 ) return 1; // Interrupted? if ( res == WAIT_OBJECT_0 + 1 ) return -2; } return 0; } // on windows, disk files are always available. return 1; } int32 BaseFileStream::writeAvailable( int32 msec, const Sys::SystemData *sysData ) { WinFileSysData *data = static_cast< WinFileSysData *>( m_fsData ); if ( data->m_isConsole || data->m_isPipe ) { if ( data->m_direction == WinFileSysData::e_dirIn ) { if (msec > 0 ) { if ( sysData->sleep( msec ) ) return 0; return -2; // interrupted } } //DWORD waitTime; DWORD size = 0; if ( data->m_isPipe ) { // no way to know, by now } else { /* Always ready on windows. waitTime = msec < 0 ? INFINITE : msec; if ( WaitForSingleObject( data->m_handle, waitTime ) == WAIT_OBJECT_0 ) { return 1; }*/ } if ( sysData->interrupted() ) return -2; return 1; } // on windows, disk files are always available. return 1; } bool BaseFileStream::writeString( const String &content, uint32 begin, uint32 end ) { WinFileSysData *data = static_cast< WinFileSysData *>( m_fsData ); uint32 done = begin; uint32 stop = content.size(); uint32 charSize = content.manipulator()->charSize(); if ( end < stop / charSize ) stop = end * charSize; while ( done < stop ) { DWORD result; if (! WriteFile( data->m_handle, content.getRawStorage() + done, content.size() - done, &result, NULL ) ) { setError( GetLastError() ); m_lastMoved = done; return false; } done += result; } setError( 0 ); m_lastMoved = done - begin; return true; } bool BaseFileStream::readString( String &content, uint32 size ) { // TODO OPTIMIZE uint32 chr; content.size( 0 ); WinFileSysData *data = static_cast< WinFileSysData *>( m_fsData ); while( size > 0 && get( chr ) ) { size--; content.append( chr ); } if ( size == 0 || eof() ) { setError( 0 ); return true; } setError( errno ); return false; } void BaseFileStream::setError( int64 errorCode ) { WinFileSysData *data = static_cast< WinFileSysData *>( m_fsData ); data->m_lastError = (uint32) errorCode; if ( errorCode != 0 ) status( status() | t_error ); else status( (t_status) (((int)status()) & ~(int)Stream::t_error )); } BaseFileStream *BaseFileStream::clone() const { BaseFileStream *gs = new BaseFileStream( *this ); if ( gs->m_fsData == 0 ) { delete gs; return 0; } return gs; } //======================================================== // File stream // FileStream::FileStream(): BaseFileStream( t_file, new WinFileSysData( INVALID_HANDLE_VALUE, 0 ) ) { status( t_none ); } bool FileStream::open( const String &filename_flc, t_openMode mode, t_shareMode share ) { WinFileSysData *data = static_cast< WinFileSysData * >( m_fsData ); DWORD omode = 0; DWORD shmode = 0; /*if ( data->m_handle != 0 ) CloseHandle( data->m_handle );*/ if ( mode == e_omReadWrite ) omode = GENERIC_READ | GENERIC_WRITE; else if ( mode == e_omReadOnly ) omode = GENERIC_READ; else omode = GENERIC_WRITE; if( share == e_smShareRead ) shmode = FILE_SHARE_READ; else if ( share == e_smShareFull ) shmode = FILE_SHARE_READ | FILE_SHARE_WRITE; // todo: do something about share mode String filename = filename_flc; Path::uriToWin( filename ); uint32 bufsize = filename.length() * sizeof( wchar_t ) + sizeof( wchar_t ); wchar_t *buffer = ( wchar_t *) memAlloc( bufsize ); if (buffer == 0) { setError( -2 ); return false; } filename.toWideString( buffer, bufsize ); HANDLE handle = CreateFileW( buffer, omode, shmode, NULL, OPEN_EXISTING, 0, NULL ); DWORD dwError = GetLastError(); if ( handle == 0 || handle == INVALID_HANDLE_VALUE ) { if ( dwError == ERROR_CALL_NOT_IMPLEMENTED ) { char *charbuf = (char *) buffer; if( filename.toCString( charbuf, bufsize ) > 0 ) { handle = CreateFile( charbuf, omode, shmode, NULL, OPEN_EXISTING, 0, NULL ); } } } memFree( buffer ); data->m_handle = handle; if ( handle == 0 || handle == INVALID_HANDLE_VALUE) { setError( GetLastError() ); status( t_error ); return false; } status( t_open ); data->m_lastError = 0; return true; } bool FileStream::create( const String &filename_flc, t_attributes mode, t_shareMode share ) { WinFileSysData *data = static_cast< WinFileSysData * >( m_fsData ); DWORD shmode = 0; if ( data->m_handle > 0 ) CloseHandle( data->m_handle ); // for now ignore attributes if( share == e_smShareRead ) shmode = FILE_SHARE_READ; else if ( share == e_smShareFull ) shmode = FILE_SHARE_READ | FILE_SHARE_WRITE; // todo: do something about share mode String filename = filename_flc; Path::uriToWin( filename ); uint32 bufsize = filename.length() * sizeof( wchar_t ) + sizeof( wchar_t ); wchar_t *buffer = ( wchar_t *) memAlloc( bufsize ); if (buffer == 0) { setError( -2 ); return false; } filename.toWideString( buffer, bufsize ); HANDLE handle = CreateFileW( buffer, GENERIC_READ | GENERIC_WRITE, shmode, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL ); if ( handle == 0 || handle == INVALID_HANDLE_VALUE ) { if ( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) { char *charbuf = (char *) buffer; if( filename.toCString( charbuf, bufsize ) > 0 ) { handle = CreateFile( charbuf, GENERIC_READ | GENERIC_WRITE, shmode, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL ); } } } memFree( buffer ); data->m_handle = handle; if ( handle == 0 || handle == INVALID_HANDLE_VALUE ) { setError( GetLastError() ); status( t_error ); return false; } status( t_open ); data->m_lastError = 0; return true; } void FileStream::setSystemData( const FileSysData &fsData ) { const WinFileSysData *data = static_cast< const WinFileSysData *>( &fsData ); WinFileSysData *myData = static_cast< WinFileSysData *>( m_fsData ); myData->m_handle = data->m_handle; myData->m_lastError = data->m_lastError; myData->m_direction = data->m_direction; myData->m_isConsole = data->m_isConsole; myData->m_isPipe = data->m_isPipe; } StdInStream::StdInStream(): InputStream( 0 ) { HANDLE dupped; HANDLE curProc = GetCurrentProcess(); if (! DuplicateHandle( curProc, // handle to the source process GetStdHandle( STD_INPUT_HANDLE ), // handle to duplicate curProc, // handle to process to duplicate to &dupped, // pointer to duplicate handle GENERIC_READ, // access for duplicate handle FALSE, // handle inheritance flag 0 // optional actions ) ) { dupped = GetStdHandle( STD_INPUT_HANDLE ); if( dupped != INVALID_HANDLE_VALUE ) { SetConsoleMode( dupped, 0 ); /* SetConsoleMode( dupped, ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT ); */ } } else { SetConsoleMode( dupped, 0 ); // remove the readline mode, as it breaks the WaitForXX functions. /*SetConsoleMode( dupped, ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT );*/ } m_fsData = new WinFileSysData( dupped, 0, true, WinFileSysData::e_dirIn ); } StdOutStream::StdOutStream(): OutputStream( 0 ) { HANDLE dupped; HANDLE curProc = GetCurrentProcess(); if (! DuplicateHandle( curProc, // handle to the source process GetStdHandle( STD_OUTPUT_HANDLE ), // handle to duplicate curProc, // handle to process to duplicate to &dupped, // pointer to duplicate handle GENERIC_WRITE, // access for duplicate handle FALSE, // handle inheritance flag 0 // optional actions ) ) { dupped = GetStdHandle( STD_OUTPUT_HANDLE ); } m_fsData = new WinFileSysData( dupped, 0, true, WinFileSysData::e_dirOut ); } StdErrStream::StdErrStream(): OutputStream( 0 ) { HANDLE dupped; HANDLE curProc = GetCurrentProcess(); if (! DuplicateHandle( curProc, // handle to the source process GetStdHandle( STD_ERROR_HANDLE ), // handle to duplicate curProc, // handle to process to duplicate to &dupped, // pointer to duplicate handle GENERIC_WRITE, // access for duplicate handle FALSE, // handle inheritance flag 0 // optional actions ) ) { dupped = GetStdHandle( STD_ERROR_HANDLE ); } m_fsData = new WinFileSysData( dupped, 0, true, WinFileSysData::e_dirOut ); } RawStdInStream::RawStdInStream(): InputStream( new WinFileSysData( GetStdHandle( STD_INPUT_HANDLE ), 0, true, WinFileSysData::e_dirIn ) ) { } RawStdOutStream::RawStdOutStream(): OutputStream( new WinFileSysData( GetStdHandle( STD_OUTPUT_HANDLE ), 0, true, WinFileSysData::e_dirOut ) ) { } RawStdErrStream::RawStdErrStream(): OutputStream( new WinFileSysData( GetStdHandle( STD_ERROR_HANDLE ), 0, true, WinFileSysData::e_dirOut ) ) { } } /* end of file_srv_unix.cpp */ engine/garbageable.cpp000066400000000000000000000023531176363201700152540ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: garbageable.cpp Garbageable objects, declaration. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun gen 22 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Garbageable items definition */ #include #include #include #include namespace Falcon { GarbageableBase::GarbageableBase( const GarbageableBase &other ): m_gcStatus( other.m_gcStatus ) {} GarbageableBase::~GarbageableBase() {} bool GarbageableBase::finalize() { delete this; return true; } uint32 GarbageableBase::occupation() { return 0; } //======================================================================== Garbageable::Garbageable() { memPool->storeForGarbage(this); } Garbageable::Garbageable( const Garbageable &other ): GarbageableBase( other ) { memPool->storeForGarbage(this); } Garbageable::~Garbageable() {} void Garbageable::gcMark( uint32 mk ) { mark( mk ); } } /* end of garbageable.cpp */ engine/gcalloc.cpp000066400000000000000000000015161176363201700144440ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: gcalloc.cpp Base allocation declaration for engine classes (gc sensible) ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar dic 5 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Base allocation declaration for engine classes */ #include #include #include #include namespace Falcon { void *GCAlloc::operator new( size_t size ) { return gcAlloc( size ); } void GCAlloc::operator delete( void *mem, size_t /* currenty unused */ ) { gcFree( mem ); } } /* end of gcalloc.cpp */ engine/gencode.cpp000066400000000000000000002023201176363201700144400ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: gencode.cpp Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include namespace Falcon { #define JMPTAG_UNDEFINED 0xFFFFFFFF GenCode::c_jmptag::c_jmptag( Stream *stream, uint32 offset ): m_elifDefs( &traits::t_int() ), m_elifQueries( &traits::t_List() ), m_tries( 0 ), m_offset( offset ), m_bIsForLast( false ), m_ForinLoop(0), m_stream( stream ) { m_defs[0] = m_defs[1] = m_defs[2] = m_defs[3] = JMPTAG_UNDEFINED; m_elifQueries.resize(12); } uint32 GenCode::c_jmptag::addQuery( uint32 id, uint32 blocks ) { if ( m_defs[id] == JMPTAG_UNDEFINED ) { uint32 pos = (uint32) m_stream->tell(); m_queries[id].pushBack( pos + (blocks * 4) ); } return m_defs[id]; } uint32 GenCode::c_jmptag::addQueryElif( uint32 id, uint32 blocks ) { if ( m_elifDefs.size() <= id ) { uint32 size = m_elifDefs.size(); m_elifDefs.resize( id + 1 ); uint32 undef = JMPTAG_UNDEFINED; while (size <= id ) m_elifDefs.set( &undef, size ++ ); if ( m_elifQueries.size() <= id ) m_elifQueries.resize( id + 1 ); } if ( *(uint32 *)m_elifDefs.at( id ) == JMPTAG_UNDEFINED ) { uint32 pos = (uint32) m_stream->tell(); ((List *)m_elifQueries.at(id))->pushBack( pos + (blocks * 4) ); } return *(uint32 *) m_elifDefs.at(id); } void GenCode::c_jmptag::define( uint32 id ) { if ( m_defs[id] != JMPTAG_UNDEFINED ) return; uint32 current_pos = (uint32) m_stream->tell(); m_defs[id] = current_pos + m_offset; uint32 value = current_pos + m_offset; ListElement *iter = m_queries[id].begin(); while( iter != 0 ) { m_stream->seekBegin( iter->iData() ); m_stream->write( &value, sizeof( value ) ); iter = iter->next(); } m_queries[id].clear(); m_stream->seekBegin( current_pos ); } void GenCode::c_jmptag::defineElif( uint32 id ) { if ( m_elifDefs.size() <= id ) { uint32 size = m_elifDefs.size(); m_elifDefs.resize( id + 1 ); int32 undef = JMPTAG_UNDEFINED; while (size <= id ) m_elifDefs.set( &undef, size ++ ); if ( m_elifQueries.size() <= id ) m_elifQueries.resize( id + 1 ); } if ( *(uint32 *)m_elifDefs.at( id ) != JMPTAG_UNDEFINED ) return; uint32 current_pos = (uint32) m_stream->tell(); uint32 cp = current_pos + m_offset; m_elifDefs.set( &cp, id ); uint32 value = current_pos + m_offset; List *lst = (List *) m_elifQueries.at(id); ListElement *iter = lst->begin(); while( iter != 0 ) { m_stream->seekBegin( iter->iData() ); m_stream->write( reinterpret_cast< const char *>( &value ), sizeof( value ) ); iter = iter->next(); } lst->clear(); m_stream->seekBegin( current_pos ); } //=============================================================== // Code generator implementation //=============================================================== GenCode::GenCode( Module *mod ): Generator( 0 ), m_pc(0), m_outTemp( new StringStream ), m_module( mod ) {} GenCode::~GenCode() { delete m_outTemp; } void GenCode::generate( const SourceTree *st ) { // generates the main program if ( ! st->statements().empty() ) { // entry point gen_block( &st->statements() ); gen_pcode( P_RET ); uint32 codeSize = m_outTemp->length(); // create the main function. m_module->addFunction( "__main__", m_outTemp->closeToBuffer(), codeSize, false ); m_pc += codeSize; delete m_outTemp; m_outTemp = new StringStream; } // No need to generate the classes, as they are just definitions in the // module symbols // generate functions if ( ! st->functions().empty() ) { const StmtFunction *func = static_cast(st->functions().front()); while( func != 0 ) { gen_function( func ); func = static_cast(func->next()); } } else { // generate at least an valid VM stub if ( st->statements().empty() ) { gen_pcode( P_RET ); } } } void GenCode::gen_pcode( byte pcode, const c_varpar &first, const c_varpar &second, const c_varpar &third ) { byte pdefs[4]; // first, ensure the third parameter is not complex. fassert( third.m_type != e_parVAL || third.m_content.value->isSimple() ); c_varpar f1, s1; // then check for the need of a complex generation. if ( first.m_type == e_parVAL && ! first.m_content.value->isSimple() && second.m_type == e_parVAL && ! second.m_content.value->isSimple() ) { // generate first the first operand. gen_value( first.m_content.value ); gen_pcode( P_PUSH, e_parA ); // then generate the second operand. gen_value( second.m_content.value ); gen_pcode( P_POP, e_parB ); // and tell the rest of func to use B and A. f1 = e_parB; s1 = e_parA; } else if ( first.m_type == e_parVAL && ! first.m_content.value->isSimple() ) { gen_value( first.m_content.value ); f1 = e_parA; s1 = second; } else if ( second.m_type == e_parVAL && ! second.m_content.value->isSimple() ) { gen_value( second.m_content.value ); f1 = first; s1 = e_parA; } else { f1 = first; s1 = second; } // write the instruction header pdefs[0] = pcode; pdefs[1] = gen_pdef( f1 ); pdefs[2] = gen_pdef( s1 ); pdefs[3] = gen_pdef( third ); m_outTemp->write( reinterpret_cast< const char * >( pdefs ), 4 ); // now write the contents. f1.generate( this ); s1.generate( this ); third.generate( this ); } void GenCode::c_varpar::generate( GenCode *owner ) const { switch( m_type ) { case e_parVAL: owner->gen_operand( m_content.value ); break; case e_parVAR: owner->gen_var( *m_content.vd ); break; case e_parSYM: { uint32 out = m_content.sym->itemId(); owner->m_outTemp->write( &out, sizeof(out) ); } break; case e_parSTRID: case e_parNTD32: case e_parINT32: { uint32 out = m_content.immediate; owner->m_outTemp->write( &out, sizeof(out) ); } break; case e_parNTD64: { uint64 out = m_content.immediate64; owner->m_outTemp->write( &out, sizeof(out) ); } break; default: break; } // else we should not generate anything } void GenCode::gen_operand( const Value *stmt ) { switch( stmt->type() ) { case Value::t_symbol: { uint32 symid; const Symbol *sym = stmt->asSymbol(); symid = sym->itemId(); m_outTemp->write( &symid, sizeof( symid ) ); } break; case Value::t_imm_integer: { int64 ival = stmt->asInteger(); m_outTemp->write( &ival, sizeof( ival ) ); } break; case Value::t_imm_num: { numeric dval = stmt->asNumeric(); m_outTemp->write( &dval, sizeof( dval ) ); } break; case Value::t_imm_string: { uint32 strid = (uint32) m_module->stringTable().findId( *stmt->asString() ); m_outTemp->write( &strid, sizeof( strid ) ); } break; case Value::t_lbind: { uint32 strid = (uint32) m_module->stringTable().findId( *stmt->asLBind() ); m_outTemp->write( &strid, sizeof( strid ) ); } break; case Value::t_imm_bool: case Value::t_self: case Value::t_fself: // do nothing break; default: // can't be fassert(false); } } byte GenCode::gen_pdef( const c_varpar &elem ) { switch( elem.m_type ) { case e_parND: return P_PARAM_NOTUSED; case e_parNIL: return P_PARAM_NIL; case e_parUND: return P_PARAM_UNB; case e_parVAL: { const Value *val = elem.m_content.value; switch( val->type() ) { case Value::t_nil: return P_PARAM_NIL; case Value::t_imm_bool: return val->asBool() ? P_PARAM_TRUE : P_PARAM_FALSE; case Value::t_imm_integer: return P_PARAM_INT64; case Value::t_imm_string: return P_PARAM_STRID; case Value::t_imm_num: return P_PARAM_NUM; case Value::t_self: return P_PARAM_REGS1; case Value::t_fself: return P_PARAM_FSELF; case Value::t_lbind: return P_PARAM_LBIND; case Value::t_symbol: if ( val->asSymbol()->isLocal() ) return P_PARAM_LOCID; if ( val->asSymbol()->isParam() ) return P_PARAM_PARID; // valid for undefined, extern and globals: return P_PARAM_GLOBID; default: break; } } // should not get here fassert( false ); return P_PARAM_NOTUSED; case e_parVAR: { const VarDef *vd = elem.m_content.vd; switch( vd->type() ) { case VarDef::t_nil: return P_PARAM_NIL; case VarDef::t_bool: return vd->asBool() ? P_PARAM_TRUE : P_PARAM_FALSE; case VarDef::t_int: return P_PARAM_INT64; case VarDef::t_num: return P_PARAM_NUM; case VarDef::t_string: return P_PARAM_STRID; case Value::t_lbind: return P_PARAM_LBIND; case VarDef::t_symbol: if ( vd->asSymbol()->isLocal() ) return P_PARAM_LOCID; if ( vd->asSymbol()->isParam() ) return P_PARAM_PARID; // valid for undefined, extern and globals: return P_PARAM_GLOBID; default: break; } } // should not get here fassert( false ); return P_PARAM_NOTUSED; case e_parSYM: { const Symbol *sym = elem.m_content.sym; if ( sym->isLocal() ) return P_PARAM_LOCID; if ( sym->isParam() ) return P_PARAM_PARID; // valid for undefined, extern and globals: } return P_PARAM_GLOBID; case e_parINT32: return P_PARAM_INT32; case e_parNTD32: return P_PARAM_NTD32; case e_parNTD64: return P_PARAM_NTD64; case e_parA: return P_PARAM_REGA; case e_parB: return P_PARAM_REGB; case e_parL1: return P_PARAM_REGL1; case e_parL2: return P_PARAM_REGL2; case e_parS1: return P_PARAM_REGS1; case e_parLBIND: return P_PARAM_LBIND; case e_parTRUE: return P_PARAM_TRUE; case e_parFALSE: return P_PARAM_FALSE; case e_parSTRID: return P_PARAM_STRID; } // should not get here fassert( false ); return P_PARAM_NOTUSED; } void GenCode::gen_var( const VarDef &def ) { switch( def.type() ) { case VarDef::t_int: { int64 ival = def.asInteger(); m_outTemp->write( reinterpret_cast< const char *>( &ival ), sizeof( ival ) ); } break; case VarDef::t_num: { numeric num = def.asNumeric(); m_outTemp->write( reinterpret_cast< const char *>( &num ), sizeof( num ) ); } break; case VarDef::t_string: { int32 id = m_module->stringTable().findId( *def.asString() ); m_outTemp->write( reinterpret_cast< const char *>( &id ), sizeof( id ) ); } break; case VarDef::t_reference: case VarDef::t_symbol: { const Symbol *sym = def.asSymbol(); int32 ival = sym->itemId(); m_outTemp->write( reinterpret_cast< const char *>( &ival ), sizeof( ival ) ); } break; default: break; } } void GenCode::gen_function( const StmtFunction *func ) { m_functions.pushBack( func->symbol() ); const StmtClass *ctorFor = func->constructorFor(); byte ret_mode = ctorFor == 0 ? P_RET : P_RETV; // get the offset of the function const Symbol *funcsym = func->symbol(); // generates INIT for constructors if ( ctorFor != 0 ) { ListElement *it_iter = ctorFor->initExpressions().begin(); while( it_iter != 0 ) { const Value *value = (const Value *) it_iter->data(); gen_value( value ); it_iter = it_iter->next(); } } // picks a return mode, either ret/retv or state jump byte end_type = ret_mode; // Generates statements if ( ! func->staticBlock().empty() ) { // push a label that is needed for the static block c_jmptag tag( m_outTemp ); // tag is destroed after pop m_labels.pushBack( &tag ); gen_pcode( P_ONCE, c_param_fixed( tag.addQueryBegin() ), func->symbol() ); gen_block( &func->staticBlock() ); tag.defineBegin(); // removes the static block tag m_labels.popBack(); } if ( ! func->statements().empty() ) gen_block( &func->statements() ); if ( func->statements().empty() || func->statements().back()->type() != Statement::t_return ) { if ( end_type == P_RETV ) gen_pcode( end_type, e_parS1 ); else gen_pcode( end_type ); } uint32 codeSize = m_outTemp->length(); funcsym->getFuncDef()->codeSize( codeSize ); funcsym->getFuncDef()->code( m_outTemp->closeToBuffer() ); funcsym->getFuncDef()->basePC( m_pc ); m_pc += codeSize; delete m_outTemp; m_outTemp = new StringStream; m_functions.popBack(); } void GenCode::gen_block( const StatementList *slist ) { const Statement *stmt = slist->front(); while( stmt != 0 ) { gen_statement( stmt ); stmt = static_cast(stmt->next()); } } void GenCode::gen_statement( const Statement *stmt ) { static uint32 last_line=0; if ( stmt->line() != last_line ) { last_line = stmt->line(); m_module->addLineInfo( m_pc + (uint32) m_outTemp->tell(), last_line ); } switch( stmt->type() ) { case Statement::t_none: // ignore this statement. break; case Statement::t_break: case Statement::t_continue: { fassert( ! m_labels_loop.empty() ); // first, eventually pop the needed tries. c_jmptag &tag = *(c_jmptag *) m_labels_loop.back(); if ( tag.tryCount() > 0 ) gen_pcode( P_PTRY, c_param_fixed( tag.tryCount() ) ); // now jump to the loop begin or end. int32 jmppos; if ( stmt->type() == Statement::t_continue ) { if( tag.isForIn() ) { const StmtContinue *cont = static_cast( stmt ); // last element has a special continue semantic. if( tag.isForLast() ) { // on dropping, just drop the last and go away if( cont->dropping() ) { gen_pcode( P_TRDN, c_param_fixed( tag.addQueryEnd() ), c_param_fixed( 0 ), c_param_fixed( 0 ) ); } else { // exactly as a break jmppos = tag.addQueryEnd(); gen_pcode( P_JMP, c_param_fixed( jmppos ) ); } } else { if( cont->dropping() ) { const StmtForin* fi = tag.ForinLoop(); uint32 nv = fi->dest()->size(); gen_pcode( P_TRDN, c_param_fixed( tag.addQueryBegin() ), c_param_fixed( tag.addQueryEnd( 2 ) ), c_param_fixed( nv ) ); ListElement* ite = fi->dest()->begin(); while( ite != 0 ) { Value* v = (Value*) ite->data(); fassert( v->isSimple() ); gen_pcode( P_NOP, v ); ite = ite->next(); } } else { // a normal continue -> next jmppos = tag.addQueryNext(); gen_pcode( P_JMP, c_param_fixed( jmppos ) ); } } } else { jmppos = tag.addQueryNext(); gen_pcode( P_JMP, c_param_fixed( jmppos ) ); } } else { jmppos = tag.addQueryEnd(); gen_pcode( P_JMP, c_param_fixed( jmppos ) ); } } break; case Statement::t_launch: { const StmtLaunch *launch = static_cast( stmt ); const Value *call = launch->value(); fassert( call->isExpr() ); const Expression *expr = call->asExpr(); fassert( expr->type() == Expression::t_funcall ); gen_funcall( expr, true ); } break; case Statement::t_autoexp: { const StmtExpression *val = static_cast( stmt ); if ( ! val->value()->isSimple() ) { gen_complex_value( val->value() ); } } break; case Statement::t_return: { const StmtReturn *ret = static_cast( stmt ); if ( ret->value() == 0 ) { gen_pcode( P_RET ); } else { gen_pcode( P_RETV, ret->value() ); } } break; case Statement::t_raise: { const StmtRaise *op = static_cast< const StmtRaise *>( stmt ); gen_pcode( P_RIS, op->value() ); } break; case Statement::t_fordot: { const StmtFordot *op = static_cast< const StmtFordot *>( stmt ); gen_pcode( P_TRAC, op->value() ); } break; case Statement::t_global: { // nothing to generate ?? } break; case Statement::t_self_print: { const StmtSelfPrint *sp = static_cast( stmt ); const ArrayDecl *attribs = sp->toPrint(); ListElement *iter = attribs->begin(); while( iter != 0 ) { const Value *val = (const Value *) iter->data(); gen_pcode( P_WRT, val ); iter = iter->next(); } } break; case Statement::t_unref: { const StmtUnref *ass = static_cast(stmt); if( ass->symbol()->isSimple() ) { gen_pcode( P_LDRF, ass->symbol(), 0 ); } else { gen_complex_value( ass->symbol() ); gen_pcode( P_LDRF, e_parA, 0 ); } } break; case Statement::t_if: { const StmtIf *elem = static_cast( stmt ); if ( elem->children().empty() && elem->elifChildren().empty() && elem->elseChildren().empty() ) { if ( ! elem->condition()->isSimple() ) gen_complex_value( elem->condition() ); // generate & discard value break; // nothing more needed } // create a new label c_jmptag tag( m_outTemp ); m_labels.pushBack( &tag ); gen_condition( elem->condition() ); if( ! elem->children().empty() ) { gen_block( &elem->children() ); } // do we need to jump away? if( ! (elem->elifChildren().empty() && elem->elseChildren().empty() ) ) { gen_pcode( P_JMP, c_param_fixed( tag.addQueryIfEnd() ) ); } // this is the position for the failure of the condition. tag.defineIfElse(); if ( ! elem->elifChildren().empty() ) { const StmtElif *selif = static_cast(elem->elifChildren().front()); int elifcount = 0; while( selif != 0 ) { gen_condition( selif->condition(), elifcount ); if( ! selif->children().empty() ) { gen_block( &selif->children() ); // do we need to jump away? if( selif->next() != 0 || ! elem->elseChildren().empty() ) { gen_pcode( P_JMP, c_param_fixed( tag.addQueryIfEnd() ) ); } } // this is the end for this elif tag.defineElif( elifcount ); elifcount++; selif = static_cast(selif->next()); } } if ( ! elem->elseChildren().empty() ) { gen_block( &elem->elseChildren() ); } // this is the position for the end of the if. tag.defineIfEnd(); m_labels.popBack(); } break; case Statement::t_switch: case Statement::t_select: { const StmtSwitch *elem = static_cast( stmt ); // check for the item to be not empty. In that case, just // eventually generate tyhe switch expression. if ( elem->intCases().empty() && elem->rngCases().empty() && elem->strCases().empty() && elem->objCases().empty() && elem->defaultBlock().empty() && elem->nilBlock() == -1 ) { if ( ! elem->switchItem()->isSimple() ) gen_complex_value( elem->switchItem() ); break; } // first, build the switch descriptor int64 sizeInt = (int16) elem->intCases().size(); int64 sizeRng = (int16) elem->rngCases().size(); int64 sizeStr = (int16) elem->strCases().size(); int64 sizeObj = (int16) elem->objCases().size(); c_varpar sizes_par; sizes_par.m_type = e_parNTD64; sizes_par.m_content.immediate64 = sizeInt << 48 | sizeRng << 32 | sizeStr << 16 | sizeObj; // prepare the jump out of the switch c_jmptag tag( m_outTemp ); m_labels.pushBack( &tag ); // gencode for the switch byte pCode = stmt->type() == Statement::t_switch ? P_SWCH : P_SELE; if( elem->switchItem()->isSimple() ) { // the content doesn't matter. NTD32 will be fine. gen_pcode( pCode, c_param_fixed( tag.addQueryEnd() ), elem->switchItem(), sizes_par ); } else { gen_value( elem->switchItem() ); gen_pcode( pCode, c_param_fixed( tag.addQueryEnd() ), e_parA, sizes_par ); } // prepare the nil block. May stay the dummy value if undefined. // and that is fine... int32 dummy = 0xFFFFFFFF; if ( elem->nilBlock() != -1 ) tag.addQuerySwitchBlock( elem->nilBlock(),0 ); m_outTemp->write( &dummy, sizeof( dummy ) ); // write the int cases map MapIterator iter = elem->intCases().begin(); while( iter.hasCurrent() ) { Value *first = *(Value **) iter.currentKey(); uint32 second = *(int32 *) iter.currentValue(); int64 value = first->asInteger(); m_outTemp->write( &value, sizeof(value ) ); tag.addQuerySwitchBlock( second, 0 ); m_outTemp->write( &dummy, sizeof( dummy ) ); iter.next(); } // write the range cases map iter = elem->rngCases().begin(); while( iter.hasCurrent() ) { Value *first = *(Value **) iter.currentKey(); uint32 second = *(int32 *) iter.currentValue(); int32 start = (uint32) first->asRange()->rangeStart()->asInteger(); int32 end = (uint32) first->asRange()->rangeEnd()->asInteger(); m_outTemp->write( &start, sizeof( start ) ); m_outTemp->write( &end, sizeof( end ) ); tag.addQuerySwitchBlock( second, 0 ); m_outTemp->write( &dummy, sizeof( dummy ) ); iter.next(); } // write the string cases map iter = elem->strCases().begin(); while( iter.hasCurrent() ) { Value *first = *(Value **) iter.currentKey(); uint32 second = *(int32 *) iter.currentValue(); int32 strid = m_module->stringTable().findId( *first->asString() ); m_outTemp->write( &strid, sizeof( strid ) ); tag.addQuerySwitchBlock( second, 0 ); m_outTemp->write( &dummy, sizeof( dummy ) ); iter.next(); } // we must put the objects in the same order they were declared. ListElement *obj_iter = elem->objList().begin(); while( obj_iter != 0 ) { Value *val = (Value *) obj_iter->data(); if( elem->objCases().find( val, iter ) ) { uint32 second = *(int32 *) iter.currentValue(); Symbol *sym = val->asSymbol(); int32 objid = sym->id(); m_outTemp->write( &objid, sizeof( objid ) ); tag.addQuerySwitchBlock( second, 0 ); m_outTemp->write( &dummy, sizeof( dummy ) ); } else { fassert( false ); } obj_iter = obj_iter->next(); } // generate the case blocks stmt = elem->blocks().front(); int case_id = 0; while( stmt != 0 ) { const StmtCaseBlock *elem_case = static_cast( stmt ); // skip default block. tag.defineSwitchCase( case_id ); gen_block( &elem_case->children() ); // jump away, but only if needed. if ( elem_case->next() != 0 || ! elem->defaultBlock().empty() ) { // ask for the postEnd in case of default block if ( elem->defaultBlock().empty() ) tag.addQueryEnd(); else tag.addQueryPostEnd(); gen_pcode( P_JMP, e_parNTD32 ); } case_id++; stmt = static_cast(elem_case->next()); } // end of the switch tag.defineEnd(); // but eventually generate the default block if ( ! elem->defaultBlock().empty() ) { gen_block( &elem->defaultBlock() ); tag.definePostEnd(); } m_labels.popBack(); } break; case Statement::t_while: { const StmtWhile *elem = static_cast( stmt ); c_jmptag tag( m_outTemp ); m_labels_loop.pushBack( &tag ); // this is the place for next and begin tag.defineBegin(); tag.defineNext(); // generate condition check only if present and if not always true if ( elem->condition() != 0 && ! elem->condition()->isTrue() ) { if ( elem->condition()->isSimple() ) { gen_pcode( P_IFF, c_param_fixed( tag.addQueryEnd() ), elem->condition() ); } else { gen_complex_value( elem->condition() ); gen_pcode( P_IFF, c_param_fixed( tag.addQueryEnd() ), e_parA ); } } gen_block( &elem->children() ); gen_pcode( P_JMP, c_param_fixed( tag.addQueryBegin() ) ); // end of the loop tag.defineEnd(); m_labels_loop.popBack(); } break; case Statement::t_loop: { const StmtLoop *elem = static_cast( stmt ); c_jmptag tag( m_outTemp ); m_labels_loop.pushBack( &tag ); // this is the place for begin tag.defineBegin(); // if we don't have a check, continue can land at loop begin. if ( elem->condition() == 0 ) tag.defineNext(); gen_block( &elem->children() ); // generate condition check only if present and if not always true if ( elem->condition() == 0 ) { // endless loop gen_pcode( P_JMP, c_param_fixed( tag.addQueryBegin() ) ); } else { tag.defineNext(); if ( ! elem->condition()->isTrue() ) { if ( elem->condition()->isSimple() ) { gen_pcode( P_IFF, c_param_fixed( tag.addQueryBegin() ), elem->condition() ); } else { gen_complex_value( elem->condition() ); gen_pcode( P_IFF, c_param_fixed( tag.addQueryBegin() ), e_parA ); } } } // if it's true, terminate immediately // end of the loop tag.defineEnd(); m_labels_loop.popBack(); } break; case Statement::t_propdef: { const StmtVarDef *pdef = static_cast( stmt ); if ( pdef->value()->isSimple() ) { gen_pcode( P_STP, e_parS1, c_param_str( m_module->stringTable().findId( *pdef->name() )), pdef->value() ); } else { gen_value( pdef->value() ); gen_pcode( P_STP, e_parS1, c_param_str( m_module->stringTable().findId( *pdef->name())), e_parA ); } } break; case Statement::t_forin: { const StmtForin *loop = static_cast( stmt ); // begin a new loop c_jmptag tag( m_outTemp ); tag.setForIn( loop ); m_labels_loop.pushBack( &tag ); uint32 neededVars = loop->dest()->size(); if( loop->source()->isSimple() ) { gen_pcode( P_TRAV, c_param_fixed( tag.addQueryPostEnd() ), c_param_fixed( neededVars ), loop->source() ); } else { gen_value( loop->source() ); gen_pcode( P_TRAV, c_param_fixed( tag.addQueryPostEnd() ), c_param_fixed( neededVars ), e_parA ); } // generate all the NOPs ListElement* ite = loop->dest()->begin(); while( ite != 0 ) { Value* v = (Value*) ite->data(); fassert( v->isSimple() ); gen_pcode( P_NOP, v ); ite = ite->next(); } // have we got a "first" block? if ( ! loop->firstBlock().empty() ) { gen_block( &loop->firstBlock() ); } // begin of the main loop; tag.defineBegin(); if( ! loop->children().empty() ) { gen_block( &loop->children() ); } // generate the formiddle block if( ! loop->middleBlock().empty() ) { // skip it for the last element gen_pcode( P_TRAL, c_param_fixed( tag.addQueryElif( 0 ) ) ); gen_block( &loop->middleBlock() ); } // after the formiddle block we have the next element pick tag.defineNext(); gen_pcode( P_TRAN, c_param_fixed( tag.addQueryBegin() ), c_param_fixed( neededVars ) ); ite = loop->dest()->begin(); while( ite != 0 ) { Value* v = (Value*) ite->data(); fassert( v->isSimple() ); gen_pcode( P_NOP, v ); ite = ite->next(); } // and the last time... tag.defineElif( 0 ); if( ! loop->lastBlock().empty() ) { tag.setForLast( true ); gen_block( &loop->lastBlock() ); } // create break landing code: tag.defineEnd(); gen_pcode( P_IPOP, c_param_fixed(1) ); tag.definePostEnd(); m_labels_loop.popBack(); } break; case Statement::t_try: { const StmtTry *op = static_cast< const StmtTry *>( stmt ); // if the try block is empty we have nothing to do if( op->children().empty() ) break; // also increment current loop try count if any. if ( ! m_labels_loop.empty() ) ((c_jmptag *) m_labels_loop.back())->addTry(); // we internally manage try landings, as they can be referenced only here. uint32 tryRefPos = ((uint32)m_outTemp->tell()) + 4; gen_pcode( P_TRY, c_param_fixed( 0xFFFFFFFF ) ); // MUST maintain the current branch level to allow inner breaks. gen_block( &op->children() ); // When the catcher is generated, the TRY cannot be broken anymore // by loop controls, as the catcher pops the TRY context from the VM if ( ! m_labels_loop.empty() ) ((c_jmptag *) m_labels_loop.back())->removeTry(); // start generating the blocks uint32 tryEndRefPos; if ( op->handlers().empty() && ! op->defaultGiven() ) { // we have no handlers gen_pcode( P_PTRY, c_param_fixed( 1 ) ); } else { tryEndRefPos = ((uint32)m_outTemp->tell()) + 4; gen_pcode( P_JTRY, c_param_fixed( 0xFFFFFFFF ) ); } // the landing is here uint32 curpos = (uint32) m_outTemp->tell(); m_outTemp->seekBegin( tryRefPos ); tryRefPos = curpos; m_outTemp->write( reinterpret_cast< const char *>(&tryRefPos), sizeof( tryRefPos ) ); m_outTemp->seekBegin( curpos ); // prepare the jump out of the switch, ok also if not used. c_jmptag tag( m_outTemp ); m_labels.pushBack( &tag ); if ( ! op->handlers().empty() || op->defaultGiven() ) { // we have some handlers. If we have only the default, we don't create a select. if ( ! op->handlers().empty() ) { // we have to generate a SELE on m_regB // first, build the switch descriptor int64 sizeInt = (int16) op->intCases().size(); int64 sizeObj = (int16) op->objCases().size(); c_varpar sizes_par; sizes_par.m_type = e_parNTD64; sizes_par.m_content.immediate64 = sizeInt << 48 | sizeObj; // gencode for the switch gen_pcode( P_SELE, c_param_fixed( tag.addQueryEnd() ), e_parB, sizes_par ); // prepare the nil block -- used in select B to determine if we should re-raise int32 dummy = op->defaultGiven() ? 1:0; m_outTemp->write( &dummy, sizeof( dummy ) ); // write the int cases map MapIterator iter = op->intCases().begin(); while( iter.hasCurrent() ) { Value *first = *(Value **) iter.currentKey(); uint32 second = *(int32 *) iter.currentValue(); int64 value = first->asInteger(); m_outTemp->write( &value, sizeof(value ) ); tag.addQuerySwitchBlock( second, 0 ); m_outTemp->write( &dummy, sizeof( dummy ) ); iter.next(); } // we must put the objects in the same order they were declared. ListElement *obj_iter = op->objList().begin(); while( obj_iter != 0 ) { Value *val = (Value *) obj_iter->data(); if( op->objCases().find( val, iter ) ) { uint32 second = *(int32 *) iter.currentValue(); Symbol *sym = val->asSymbol(); int32 objid = sym->id(); m_outTemp->write( &objid, sizeof( objid ) ); tag.addQuerySwitchBlock( second, 0 ); m_outTemp->write( &dummy, sizeof( dummy ) ); } else { fassert( false ); } obj_iter = obj_iter->next(); } // generate the case blocks stmt = op->handlers().front(); int case_id = 0; while( stmt != 0 ) { const StmtCatchBlock *elem_case = static_cast( stmt ); // skip default block. tag.defineSwitchCase( case_id ); // if we have a catch into, we have to move B here. if ( elem_case->intoValue() != 0 ) gen_pcode( P_LD, elem_case->intoValue(), e_parB ); gen_block( &elem_case->children() ); // jump away, but only if needed. if ( elem_case->next() != 0 || op->defaultGiven() ) { // ask for the postEnd in case of default block if ( ! op->defaultGiven() ) tag.addQueryEnd(); else tag.addQueryPostEnd(); gen_pcode( P_JMP, e_parNTD32 ); } case_id++; stmt = static_cast(elem_case->next()); } } // end of the switch tag.defineEnd(); // tell the news to the other branches // but eventually generate the default block if ( op->defaultGiven() ) { if ( op->defaultHandler()->intoValue() != 0 ) gen_pcode( P_LD, op->defaultHandler()->intoValue(), e_parB ); gen_block( &op->defaultHandler()->children() ); // even if we don't use it, no one will care. tag.definePostEnd(); } m_labels.popBack(); // tell the TRY jumper where to jump when we are out of TRY curpos = (uint32) m_outTemp->tell(); m_outTemp->seekBegin( tryEndRefPos ); tryEndRefPos = curpos; m_outTemp->write( reinterpret_cast< const char *>(&tryEndRefPos), sizeof( tryEndRefPos ) ); m_outTemp->seekBegin( curpos ); } } break; default: break; } } void GenCode::gen_inc_prefix( const Value *val ) { if ( val->isSimple() ) { gen_pcode( P_INC, val ); } else { t_valType xValue = l_value; gen_complex_value( val, xValue ); gen_pcode( P_INC, e_parA ); if ( xValue == p_value ) gen_pcode( P_STP, e_parL1, e_parL2, e_parA ); else if ( xValue == v_value ) gen_pcode( P_STV, e_parL1, e_parL2, e_parA ); } } void GenCode::gen_inc_postfix( const Value *val ) { if ( val->isSimple() ) { gen_pcode( P_INCP, val ); } else { t_valType xValue = l_value; gen_complex_value( val, xValue ); gen_pcode( P_INCP, e_parA ); if ( xValue == p_value ) gen_pcode( P_STP, e_parL1, e_parL2, e_parB ); else if ( xValue == v_value ) gen_pcode( P_STV, e_parL1, e_parL2, e_parB ); } } void GenCode::gen_dec_prefix( const Value *val ) { if ( val->isSimple() ) { gen_pcode( P_DEC, val ); } else { t_valType xValue = l_value; gen_complex_value( val, xValue ); gen_pcode( P_DEC, e_parA ); if ( xValue == p_value ) gen_pcode( P_STP, e_parL1, e_parL2, e_parA ); else if ( xValue == v_value ) gen_pcode( P_STV, e_parL1, e_parL2, e_parA ); } } void GenCode::gen_dec_postfix( const Value *val ) { if ( val->isSimple() ) { gen_pcode( P_DECP, val ); } else { t_valType xValue = l_value; gen_complex_value( val, xValue ); gen_pcode( P_DECP, e_parA ); if ( xValue == p_value ) gen_pcode( P_STP, e_parL1, e_parL2, e_parB ); else if ( xValue == v_value ) gen_pcode( P_STV, e_parL1, e_parL2, e_parB ); } } void GenCode::gen_autoassign( byte opcode, const Value *target, const Value *source ) { if( target->isSimple() && source->isSimple() ) { gen_pcode( opcode, target, source ); } else if ( target->isSimple() ) { gen_complex_value( source ); gen_pcode( opcode, target, e_parA ); } else if ( source->isSimple() ) { t_valType xValue = l_value; gen_complex_value( target, xValue ); gen_pcode( opcode, e_parA, source ); if ( xValue == p_value ) gen_pcode( P_STP, e_parL1, e_parL2, e_parA ); else if ( xValue == v_value ) gen_pcode( P_STV, e_parL1, e_parL2, e_parA ); } else { gen_complex_value( source ); gen_pcode( P_PUSH, e_parA ); t_valType xValue = l_value; gen_complex_value( target, xValue ); gen_pcode( P_POP, e_parB ); gen_pcode( opcode, e_parA, e_parB ); if ( xValue == p_value ) gen_pcode( P_STP, e_parL1, e_parL2, e_parA ); else if ( xValue == v_value ) gen_pcode( P_STV, e_parL1, e_parL2, e_parA ); } } void GenCode::gen_condition( const Value *stmt, int mode ) { c_jmptag &tag = *(c_jmptag*) m_labels.back(); if ( !stmt->isSimple() ) { gen_complex_value( stmt ); if ( mode >= 0 ) { gen_pcode( P_IFF, c_param_fixed( tag.addQueryElif( mode ) ), e_parA ); } else { gen_pcode( P_IFF, c_param_fixed( tag.addQueryIfElse() ), e_parA ); } } else { if ( mode >= 0 ) { gen_pcode( P_IFF, c_param_fixed( tag.addQueryElif( mode ) ), stmt ); } else { gen_pcode( P_IFF, c_param_fixed( tag.addQueryIfElse() ), stmt ); } } } void GenCode::gen_value( const Value *stmt ) { if ( stmt->isSimple() ) { gen_pcode( P_STO, e_parA, stmt ); } else { gen_complex_value( stmt ); } } void GenCode::gen_complex_value( const Value *stmt, t_valType &xValue ) { switch( stmt->type() ) { // catch also reference taking in case it's not filtered before // this happens when we have not a specific opcode to handle references case Value::t_byref: if ( stmt->asReference()->isSymbol() ) gen_pcode( P_LDRF, e_parA, stmt->asReference()->asSymbol() ); else { gen_value( stmt->asReference() ); // won't do a lot, but we need it gen_pcode( P_LDRF, e_parA, e_parA ); } break; case Value::t_array_decl: gen_array_decl( stmt->asArray() ); break; case Value::t_dict_decl: gen_dict_decl( stmt->asDict() ); break; case Value::t_expression: gen_expression( stmt->asExpr(), xValue ); break; case Value::t_range_decl: gen_range_decl( stmt->asRange() ); break; default: fassert( false ); } } void GenCode::gen_push( const Value *val ) { if( val->isSimple() ) { gen_pcode( P_PUSH, val ); } else if ( val->isReference() ) { if( val->asReference()->isSymbol() ) gen_pcode( P_PSHR, val->asReference()->asSymbol() ); else { gen_value( val->asReference() ); gen_pcode( P_PSHR, e_parA ); } } else { gen_complex_value( val ); gen_pcode( P_PUSH, e_parA ); } } void GenCode::gen_expression( const Expression *exp, t_valType &xValue ) { byte opname = 0; int mode = 0; // 1 = unary, 2 = binary // first, deterime the operator name and operation type switch( exp->type() ) { // optimized away operations case Expression::t_optimized: gen_value( exp->first() ); // nothing else needed, going out return; // logical connectors & shortcuts case Expression::t_and: case Expression::t_or: { xValue = l_value; char ifmode; if ( exp->type() == Expression::t_or ) { opname = P_OR; ifmode = P_IFT; } else { opname = P_AND; ifmode = P_IFF; } if( exp->first()->isSimple() && exp->second()->isSimple() ) { gen_pcode( opname, exp->first(), exp->second() ); } else { c_jmptag tag( m_outTemp ); m_labels.pushBack( &tag ); if( exp->first()->isSimple() ) { gen_pcode( P_LD, e_parA, exp->first() ); gen_pcode( ifmode, c_param_fixed( tag.addQueryIfEnd() ), e_parA ); // if A that is the first op is false, we generate this one. gen_value( exp->second() ); } else if( exp->second()->isSimple() ) { gen_value( exp->first() ); gen_pcode( ifmode, c_param_fixed( tag.addQueryIfEnd() ), e_parA ); // if A that is the first op is false, we generate this one. gen_pcode( P_LD, e_parA, exp->second() ); } else { gen_value( exp->first() ); gen_pcode( ifmode, c_param_fixed( tag.addQueryIfEnd() ), e_parA ); // if A that is the first op is false, we generate this one. gen_value( exp->second() ); } tag.defineIfEnd(); m_labels.popBack(); } } return; // unary operators case Expression::t_neg: mode = 1; opname = P_NEG; break; case Expression::t_not: mode = 1; opname = P_NOT; break; case Expression::t_bin_not: mode = 1; opname = P_BNOT; break; case Expression::t_strexpand: mode = 1; opname = P_STEX; break; case Expression::t_indirect: mode = 1; opname = P_INDI; break; case Expression::t_eval: mode = 1; opname = P_EVAL; break; case Expression::t_bin_and: mode = 2; opname = P_BAND; break; case Expression::t_bin_or: mode = 2; opname = P_BOR; break; case Expression::t_bin_xor: mode = 2; opname = P_BXOR; break; case Expression::t_shift_left: mode = 2; opname = P_SHL; break; case Expression::t_shift_right: mode = 2; opname = P_SHR; break; case Expression::t_plus: mode = 2; opname = P_ADD; break; case Expression::t_minus: mode = 2; opname = P_SUB; break; case Expression::t_times: mode = 2; opname = P_MUL; break; case Expression::t_divide: mode = 2;opname = P_DIV; break; case Expression::t_modulo: mode = 2; opname = P_MOD; break; case Expression::t_power: mode = 2; opname = P_POW; break; case Expression::t_gt: mode = 2; opname = P_GT; break; case Expression::t_ge: mode = 2; opname = P_GE; break; case Expression::t_lt: mode = 2; opname = P_LT; break; case Expression::t_le: mode = 2; opname = P_LE; break; case Expression::t_eq: mode = 2; opname = P_EQ; break; case Expression::t_exeq: mode = 2; opname = P_EXEQ; break; case Expression::t_neq: mode = 2; opname = P_NEQ; break; case Expression::t_in: mode = 2; opname = P_IN; break; case Expression::t_notin: mode = 2; opname = P_NOIN; break; case Expression::t_provides: mode = 2; opname = P_PROV; break; case Expression::t_fbind: mode = 2; opname = P_FORB; break; // it is better to handle the rest directly here. case Expression::t_iif: { xValue = l_value; c_jmptag tag( m_outTemp ); m_labels.pushBack( &tag ); // condition if ( exp->first()->isSimple() ) { tag.addQueryIfElse(); gen_pcode( P_IFF, e_parNTD32, exp->first() ); } else { gen_value( exp->first() ); tag.addQueryIfElse(); gen_pcode( P_IFF, e_parNTD32, e_parA ); } // success case if( exp->second()->isSimple() ) { gen_pcode( P_STO, e_parA, exp->second() ); } else { gen_value( exp->second() ); } tag.addQueryIfEnd(); gen_pcode( P_JMP, e_parNTD32 ); tag.defineIfElse(); //failure case // success case if( exp->third()->isSimple() ) { gen_pcode( P_STO, e_parA, exp->third() ); } else { gen_value( exp->third() ); } tag.defineIfEnd(); m_labels.popBack(); } return; case Expression::t_assign: // handle it as a load... // don't change x-value gen_load( exp->first(), exp->second() ); return; case Expression::t_aadd: xValue = l_value; gen_autoassign( P_ADDS, exp->first(), exp->second() ); return; case Expression::t_asub: xValue = l_value; gen_autoassign( P_SUBS, exp->first(), exp->second() ); return; case Expression::t_amul: xValue = l_value; gen_autoassign( P_MULS, exp->first(), exp->second() ); return; case Expression::t_adiv: xValue = l_value; gen_autoassign( P_DIVS, exp->first(), exp->second() ); return; case Expression::t_amod: xValue = l_value; gen_autoassign( P_MODS, exp->first(), exp->second() ); return; case Expression::t_aband: xValue = l_value; gen_autoassign( P_ANDS, exp->first(), exp->second() ); return; case Expression::t_abor: xValue = l_value; gen_autoassign( P_ORS, exp->first(), exp->second() ); return; case Expression::t_abxor: xValue = l_value; gen_autoassign( P_XORS, exp->first(), exp->second() ); return; case Expression::t_apow: xValue = l_value; gen_autoassign( P_POWS, exp->first(), exp->second() ); return; case Expression::t_ashl: xValue = l_value; gen_autoassign( P_SHLS, exp->first(), exp->second() ); return; case Expression::t_ashr: xValue = l_value; gen_autoassign( P_SHRS, exp->first(), exp->second() ); return; case Expression::t_pre_inc: gen_inc_prefix( exp->first() ); return; case Expression::t_pre_dec: gen_dec_prefix( exp->first() ); return; case Expression::t_post_inc: gen_inc_postfix( exp->first() ); return; case Expression::t_post_dec: gen_dec_postfix( exp->first() ); return; case Expression::t_obj_access: xValue = p_value; gen_load_from_deep( P_LDP, exp->first(), exp->second() ); return; case Expression::t_array_access: xValue = v_value; gen_load_from_deep( P_LDV, exp->first(), exp->second() ); return; case Expression::t_array_byte_access: xValue = l_value; gen_load_from_deep( P_LSB, exp->first(), exp->second() ); return; case Expression::t_funcall: case Expression::t_inherit: { xValue = l_value; gen_funcall( exp, false ); } // funcall is complete here return; case Expression::t_lambda: xValue = l_value; if( exp->second() != 0 ) { // we must create the lambda closure int size = 0; ListElement *iter = exp->second()->asArray()->begin(); while( iter != 0 ) { const Value *val = (Value *) iter->data(); // push the reference; we want a reference in the closure. gen_pcode( P_PSHR, val ); size++; iter = iter->next(); } gen_pcode( P_CLOS, c_param_fixed(size), e_parA, exp->first()->asSymbol() ); } else { gen_pcode( P_STO, e_parA, exp->first()->asSymbol() ); } return; case Expression::t_oob: xValue = l_value; gen_pcode( P_OOB, c_param_fixed(1), exp->first() ); return; case Expression::t_deoob: xValue = l_value; gen_pcode( P_OOB, c_param_fixed(0), exp->first() ); return; case Expression::t_xoroob: xValue = l_value; gen_pcode( P_OOB, c_param_fixed(2), exp->first() ); return; case Expression::t_isoob: xValue = l_value; gen_pcode( P_OOB, c_param_fixed(3), exp->first() ); return; default: break; } // post-processing unary and binary operators. // ++, -- and accessors are gone; safely change the l-value status. xValue = l_value; // then, if there is still something to do, put the operands in place. if ( mode == 1 ) { // unary? if ( exp->first()->isSimple() ) { gen_pcode( opname, exp->first() ); } else { gen_complex_value( exp->first() ); gen_pcode( opname, e_parA ); } } else { if ( exp->first()->isSimple() && exp->second()->isSimple() ) { gen_pcode( opname, exp->first(), exp->second() ); } else if ( exp->first()->isSimple() ) { gen_complex_value( exp->second() ); gen_pcode( opname, exp->first(), e_parA ); } else if ( exp->second()->isSimple() ) { gen_complex_value( exp->first() ); gen_pcode( opname, e_parA, exp->second() ); } else { gen_complex_value( exp->first() ); gen_pcode( P_PUSH, e_parA ); gen_complex_value( exp->second() ); gen_pcode( P_POP, e_parB ); gen_pcode( opname, e_parB, e_parA ); } } } void GenCode::gen_dict_decl( const DictDecl *dcl ) { int size = 0; ListElement *iter = dcl->begin(); while( iter != 0 ) { DictDecl::pair *pair = (DictDecl::pair *) iter->data(); const Value *key = pair->first; const Value *value = pair->second; gen_push( key ); gen_push( value ); size++; iter = iter->next(); } gen_pcode( P_GEND, c_param_fixed( size ) ); } void GenCode::gen_array_decl( const ArrayDecl *dcl ) { int size = 0; ListElement *iter = dcl->begin(); while( iter != 0 ) { const Value *val = (Value *) iter->data(); gen_push( val ); size++; iter = iter->next(); } gen_pcode( P_GENA, c_param_fixed( size ) ); } void GenCode::gen_range_decl( const RangeDecl *dcl ) { if ( dcl->isOpen() ) { if ( dcl->rangeStart()->isSimple() ) { gen_pcode( P_GEOR, dcl->rangeStart() ); } else { gen_complex_value( dcl->rangeStart() ); gen_pcode( P_GEOR, e_parA ); } } else { Value dummy; // defaults to nil Value *rangeStep; if ( dcl->rangeStep() == 0 ) { dummy.setInteger( 0 ); rangeStep = &dummy; } else if ( dcl->rangeStep()->isSimple() ) { rangeStep = dcl->rangeStep(); } else { gen_complex_value( dcl->rangeStep() ); gen_pcode( P_PUSH, e_parA ); // we'll instruct GENR to get it via NIL as parameter rangeStep = &dummy; } if ( dcl->rangeStart()->isSimple() && dcl->rangeEnd()->isSimple() ) { gen_pcode( P_GENR, dcl->rangeStart(), dcl->rangeEnd(), rangeStep ); } else if ( dcl->rangeStart()->isSimple() ) { gen_complex_value( dcl->rangeEnd() ); gen_pcode( P_GENR, dcl->rangeStart(), e_parA, rangeStep ); } else if ( dcl->rangeEnd()->isSimple() ) { gen_complex_value( dcl->rangeStart() ); gen_pcode( P_GENR, e_parA, dcl->rangeEnd(), rangeStep ); } else { gen_complex_value( dcl->rangeStart() ); gen_pcode( P_PUSH, e_parA ); gen_complex_value( dcl->rangeEnd() ); gen_pcode( P_POP, e_parB ); gen_pcode( P_GENR, e_parB, e_parA, rangeStep ); } } } void GenCode::gen_load( const Value *target, const Value *source ) { if ( target->isSimple() && source->isSimple() ) { gen_pcode( P_LD, target, source ); } else if ( target->isSimple() ) { if( source->isReference() ) { if ( source->asReference() == 0 ) gen_pcode( P_LDRF, target, 0 ); else { if( source->asReference()->isSymbol() ) { gen_pcode( P_LDRF, target, source->asReference()->asSymbol() ); } else { gen_value( source->asReference() ); gen_pcode( P_LDRF, target, e_parA ); } } } else { gen_complex_value( source ); gen_pcode( P_LD, target, e_parA ); } } else { // target is NOT simple. If it's an expression ... if( target->type() == Value::t_expression ) { const Expression *exp = target->asExpr(); // ... then it may be an array assignment... if( exp->type() == Expression::t_array_access ) { gen_store_to_deep( P_STV, exp->first(), exp->second(), source ); } else if ( exp->type() == Expression::t_obj_access ) { gen_store_to_deep( P_STP, exp->first(), exp->second(), source ); } } else if ( target->type() == Value::t_array_decl ) { const ArrayDecl *tarr = target->asArray(); // if the source is also an array, fine, we have a 1:1 assignment. if ( source->type() == Value::t_array_decl ) { const ArrayDecl *sarr = source->asArray(); ListElement *it_s = sarr->begin(); ListElement *it_t = tarr->begin(); while( it_s != 0 && it_t != 0 ) { const Value *t = (const Value *) it_t->data(); const Value *s = (const Value *) it_s->data(); gen_load( t, s ); it_s = it_s->next(); it_t = it_t->next(); } // the compiler takes care to provide us with parallel arrays, // but we set a trap here in case there is a bug in the compiler. fassert( it_s == 0 && it_t == 0 ); } // we must generate an unpack request else { // then unpack the source in the array. if ( source->isSimple() ) { gen_pcode( P_LDAS, c_param_fixed( tarr->size() ), source ); } else { gen_complex_value( source ); gen_pcode( P_LDAS, c_param_fixed( tarr->size() ), e_parA ); } ListElement *it_t = tarr->end(); while( it_t != 0 ) { const Value *t = (const Value *) it_t->data(); if( t->isSimple() ) gen_pcode( P_POP, t ); else { gen_pcode( P_POP, e_parB ); gen_load_from_reg( t, e_parB ); } it_t = it_t->prev(); } } } } } int GenCode::gen_refArray( const ArrayDecl *tarr, bool bGenArray ) { ListElement *it_t = tarr->begin(); int size = 0; // first generates an array of references while( it_t != 0 ) { // again, is the compiler that must make sure of this... const Value *val = (const Value *) it_t->data(); fassert( val->isSimple() ); gen_pcode( P_PSHR, val ); ++size; it_t = it_t->next(); } if( bGenArray ) gen_pcode( P_GENA, c_param_fixed( size ) ); return size; } void GenCode::gen_store_to_deep( byte type, const Value *first, const Value *second, const Value *source ) { // first we must generate the assignands. if( source->isSimple() ) { if ( first->isSimple() && second->isSimple() ) { gen_pcode( type, first, second, source ); } else if ( second->isSimple() ) { gen_complex_value( first ); gen_pcode( type, e_parA, second, source ); } else if ( first->isSimple() ) { gen_complex_value( second ); gen_pcode( type, first, e_parA, source ); } else { gen_complex_value( first ); gen_pcode( P_PUSH, e_parA ); gen_complex_value( second ); gen_pcode( P_POP, e_parB ); gen_pcode( type, e_parB, e_parA, source ); } } else { if ( first->isSimple() && second->isSimple() ) { gen_complex_value( source ); gen_pcode( type, first, second, e_parA ); } else { if( source->isReference() ) { type == P_STP ? type = P_STPR : P_STVR; source = source->asReference(); } else { type = type == P_STP ? P_STPS : P_STVS; } if ( second->isSimple() ) { gen_complex_value( first ); gen_pcode( P_PUSH, e_parA ); gen_complex_value( source ); gen_pcode( P_XPOP, e_parA ); gen_pcode( type, e_parA, second ); } else if ( first->isSimple() ) { gen_complex_value( second ); gen_pcode( P_PUSH, e_parA ); gen_complex_value( source ); gen_pcode( P_XPOP, e_parA ); gen_pcode( type, first, e_parA ); } else { gen_complex_value( first ); gen_pcode( P_PUSH, e_parA ); gen_complex_value( second ); gen_pcode( P_PUSH, e_parA ); gen_complex_value( source ); gen_pcode( P_POP, e_parB ); gen_pcode( P_XPOP, e_parA ); gen_pcode( type, e_parA, e_parB ); } } } } void GenCode::gen_load_from_deep( byte type, const Value *first, const Value *second ) { // first we must generate the assignands. if( first->isSimple() ) { if ( second->isSimple() ) { gen_pcode( type, first, second ); } else { gen_complex_value( second ); gen_pcode( type, first, e_parA ); } } else { if ( second->isSimple() ) { gen_complex_value( first ); gen_pcode( type, e_parA, second ); } else { gen_complex_value( first ); gen_pcode( P_PUSH, e_parA ); gen_complex_value( second ); gen_pcode( P_POP, e_parB ); gen_pcode( type, e_parB, e_parA ); } } } void GenCode::gen_load_from_A( const Value *target ) { gen_load_from_reg( target, e_parA ); } void GenCode::gen_load_from_reg( const Value *target, t_paramType reg ) { if ( target->isSimple() ) { gen_pcode( P_LD, target, reg ); } else { // target is NOT simple. If it's an expression ... if( target->type() == Value::t_expression ) { const Expression *exp = target->asExpr(); // ... then it may be an array assignment... if( exp->type() == Expression::t_array_access ) { gen_store_to_deep_reg( P_STV, exp->first(), exp->second(), reg ); } else if ( exp->type() == Expression::t_obj_access ) { gen_store_to_deep_reg( P_STP, exp->first(), exp->second(), reg ); } else { gen_pcode( P_PUSH, reg ); gen_expression( exp ); gen_pcode( P_POP, e_parB ); gen_pcode( P_LD, e_parA, e_parB ); } } else if ( target->type() == Value::t_array_decl ) { // if the source is also an array, fine, we have a 1:1 assignment. const ArrayDecl *tarr = target->asArray(); // push the source array on the stack. gen_pcode( P_LDAS, c_param_fixed( tarr->size() ), reg ); // and load each element back in the array ListElement *it_t = tarr->end(); // Now load each element by popping it. while( it_t != 0 ) { const Value *val = (const Value *) it_t->data(); if( val->isSimple() ) { gen_pcode( P_POP, val ); } else { gen_pcode( P_POP, e_parB ); gen_load_from_reg( val, e_parB ); } it_t = it_t->prev(); } } } } void GenCode::gen_store_to_deep_A( byte type, const Value *first, const Value *second ) { gen_store_to_deep_reg( type, first, second, e_parA ); } void GenCode::gen_store_to_deep_reg( byte type, const Value *first, const Value *second, t_paramType reg ) { // first we must generate the assignands. if ( first->isSimple() && second->isSimple() ) { gen_pcode( type, first, second, reg ); } else { type = type == P_STP ? P_STPS : P_STVS; gen_pcode( P_PUSH, reg ); if ( second->isSimple() ) { gen_complex_value( first ); gen_pcode( type, e_parA, second ); } else if ( first->isSimple() ) { gen_complex_value( second ); gen_pcode( type, first, reg ); } else { gen_complex_value( first ); gen_pcode( P_PUSH, e_parA ); gen_complex_value( second ); gen_pcode( P_POP, e_parB ); gen_pcode( type, e_parB, e_parA ); } } } void GenCode::gen_funcall( const Expression *exp, bool fork ) { int size = 0; byte functor = exp->type() == Expression::t_inherit ? P_INST : P_CALL; if( exp->second() != 0 ) { const ArrayDecl *dcl = exp->second()->asArray(); ListElement *iter = (ListElement *) dcl->begin(); while( iter != 0 ) { const Value *val = (const Value *) iter->data(); gen_push( val ); size++; iter = iter->next(); } } uint32 fork_pos; if ( fork ) { // push the function call if ( exp->first()->isSimple() ) { gen_pcode( P_PUSH, exp->first() ); } else { gen_complex_value( exp->first() ); gen_pcode( P_PUSH, e_parA ); } fork_pos = ((uint32)m_outTemp->tell()) + 8; gen_pcode( P_FORK, c_param_fixed( size + 1 ), e_parNTD32 ); // call the topmost stack item. gen_pcode( P_POP, e_parA ); gen_pcode( functor, c_param_fixed( size ), e_parA ); gen_pcode( P_END ); // landing for fork uint32 curpos = (uint32) m_outTemp->tell(); m_outTemp->seekBegin( fork_pos ); fork_pos = curpos; m_outTemp->write( &fork_pos, sizeof( fork_pos ) ); m_outTemp->seekBegin( curpos ); } else { if( exp->first()->isSimple() ) { gen_pcode( functor, c_param_fixed( size ), exp->first() ); } else { gen_complex_value( exp->first() ); gen_pcode( functor, c_param_fixed( size ), e_parA ); } } } } /* end of gencode.cpp */ engine/generatorseq.cpp000066400000000000000000000135371176363201700155450ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: generatorseq.cpp Virtual sequence that can be used to iterate over generators. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 06 Aug 2009 22:10:00 +020 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include namespace Falcon { GeneratorSeq::GeneratorSeq( VMachine* runEvn, const Item& callable ): m_vm( runEvn ), m_callable( callable ), m_bHasCachedCur( false ), m_bHasCachedNext( false ), m_bComplete( false ) {} GeneratorSeq::GeneratorSeq( const GeneratorSeq& other ): m_vm( other.m_vm ), m_callable(other.m_callable), m_cache_cur( other.m_cache_cur ), m_cache_next( other.m_cache_next ), m_bHasCachedCur( other.m_bHasCachedCur ), m_bHasCachedNext( other.m_bHasCachedNext ), m_bComplete( other.m_bComplete ) {} GeneratorSeq::~GeneratorSeq() {} GeneratorSeq* GeneratorSeq::clone() const { return new GeneratorSeq( *this ); } const Item &GeneratorSeq::front() const { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ) .extra( "GeneratorSeq::front" ) ); } const Item &GeneratorSeq::back() const { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ) .extra( "GeneratorSeq::back" ) ); } void GeneratorSeq::clear() { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ) .extra( "GeneratorSeq::clear" ) ); } bool GeneratorSeq::empty() const { if ( m_bComplete ) return true; if ( m_bHasCachedCur ) return false; return ! fillCurrentValue(); } void GeneratorSeq::gcMark( uint32 gen ) { memPool->markItem( m_callable ); if( m_bHasCachedCur ) memPool->markItem( m_cache_cur ); if ( m_bHasCachedNext ) memPool->markItem( m_cache_next ); Sequence::gcMark( gen ); } bool GeneratorSeq::fillCurrentValue() const { if( m_bComplete ) return false; m_vm->callItemAtomic( m_callable, 0 ); m_cache_cur = m_vm->regA(); if( m_cache_cur.isOob() && m_cache_cur.isInteger() && m_cache_cur.asInteger() == 0 ) { m_bComplete = true; m_bHasCachedCur = false; return false; } m_bHasCachedCur = true; return true; } bool GeneratorSeq::fillNextValue() const { if( m_bComplete ) return false; m_vm->callItemAtomic( m_callable, 0 ); m_cache_next = m_vm->regA(); if( m_cache_next.isOob() && m_cache_next.isInteger() && m_cache_next.asInteger() == 0 ) { m_bHasCachedNext = false; m_bComplete = true; return false; } m_bHasCachedNext = true; return true; } void GeneratorSeq::append( const Item &data ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ) .extra( "GeneratorSeq::append" ) ); } void GeneratorSeq::prepend( const Item &data ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ) .extra( "GeneratorSeq::prepend" ) ); } void GeneratorSeq::getIterator( Iterator& tgt, bool tail ) const { Sequence::getIterator( tgt, tail ); } void GeneratorSeq::copyIterator( Iterator& tgt, const Iterator& source ) const { Sequence::copyIterator( tgt, source ); } void GeneratorSeq::insert( Iterator &, const Item & ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "GeneratorSeq::insert" ) ); } void GeneratorSeq::erase( Iterator & ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "GeneratorSeq::erase" ) ); } bool GeneratorSeq::hasNext( const Iterator &iter ) const { if( ! m_bHasCachedCur ) { if( ! fillCurrentValue() ) return false; } if ( ! m_bHasCachedNext ) { if( ! fillNextValue() ) return false; } return true; } bool GeneratorSeq::hasPrev( const Iterator &iter ) const { return true; // will eventually raise on prev. } bool GeneratorSeq::hasCurrent( const Iterator &iter ) const { if( ! m_bHasCachedCur ) { if( ! fillCurrentValue() ) return false; } return true; } bool GeneratorSeq::next( Iterator &iter ) const { // if we don't have a current value anymore, cache it. if( ! m_bHasCachedCur ) { if( ! fillCurrentValue() ) return false; m_bHasCachedNext = false; return true; } else { // we have a current value; but have we got a next value? if( m_bHasCachedNext ) { // Yes -- demote it to current. m_cache_cur = m_cache_next; m_bHasCachedNext = false; return true; } else { // no? -- absorb a next value. return fillCurrentValue(); } } } bool GeneratorSeq::prev( Iterator &iter ) const { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ) .extra( "GeneratorSeq::prev" ) ); } Item& GeneratorSeq::getCurrent( const Iterator &iter ) { if( ! m_bHasCachedCur ) { fillCurrentValue(); // TODO: raise if terminated? -- one should call next() or hasCurrent(), but.. } return m_cache_cur; } Item& GeneratorSeq::getCurrentKey( const Iterator &iter ) { throw new CodeError( ErrorParam( e_non_dict_seq, __LINE__ ) .origin( e_orig_runtime ).extra( "GeneratorSeq::getCurrentKey" ) ); } bool GeneratorSeq::equalIterator( const Iterator &first, const Iterator &second ) const { return true; } } /* end of generatorseq.cpp */ engine/genericlist.cpp000066400000000000000000000125121176363201700153460ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: List.cpp a list holding generic values. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom ott 15 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file a list holding generic values. */ #include #include #include #include #include #include namespace Falcon { void List::clear() { ListElement *elem = m_head; if( m_deletor != 0 ) { while( elem != 0 ) { ListElement *toBeDeleted = elem; elem = elem->next(); m_deletor( (void *) toBeDeleted->data() ); memFree( toBeDeleted ); } } else { while( elem != 0 ) { ListElement *toBeDeleted = elem; elem = elem->next(); memFree( toBeDeleted ); } } m_head = m_tail = 0; m_size = 0; } void List::pushFront( const void *data ) { ListElement *element = (ListElement *) memAlloc( sizeof( ListElement ) ); element->data( data ); if ( m_head == 0 ) { m_head = m_tail = element; element->next(0); } else { element->next( m_head ); m_head->prev( element ); m_head = element; } m_size++; element->prev( 0 ); } void List::pushBack( const void *data ) { ListElement *element = (ListElement *) memAlloc( sizeof( ListElement ) ); element->data( data ); if ( m_head == 0 ) { m_head = m_tail = element; element->prev(0); } else { element->prev( m_tail ); m_tail->next( element ); m_tail = element; } m_size++; element->next( 0 ); } void List::pushFront( uint32 data ) { ListElement *element = (ListElement *) memAlloc( sizeof( ListElement ) ); element->iData( data ); if ( m_head == 0 ) { m_head = m_tail = element; element->next(0); } else { element->next( m_head ); m_head->prev( element ); m_head = element; } m_size++; element->prev( 0 ); } void List::pushBack( uint32 data ) { ListElement *element = (ListElement *) memAlloc( sizeof( ListElement ) ); element->iData( data ); if ( m_head == 0 ) { m_head = m_tail = element; element->prev(0); } else { element->prev( m_tail ); m_tail->next( element ); m_tail = element; } m_size++; element->next( 0 ); } void List::popFront() { if ( m_head == 0 ) return; ListElement *element = m_head; m_head = m_head->next(); if( m_head != 0 ) m_head->prev( 0 ); else m_tail = 0; if( m_deletor != 0 ) { m_deletor( (void *) element->data() ); } m_size--; memFree( element ); } void List::popBack() { if ( m_tail == 0 ) return; ListElement *element = m_tail; m_tail = m_tail->prev(); if( m_tail == 0 ) m_head = 0; else m_tail->next(0); if( m_deletor != 0 ) { m_deletor( (void *) element->data() ); } m_size--; memFree( element ); } void List::insertAfter( ListElement *position, const void *data ) { ListElement *element = (ListElement *) memAlloc( sizeof( ListElement ) ); element->data( data ); element->next( position->next() ); element->prev( position ); if( position->next() != 0 ) { position->next()->prev( element ); } position->next( element ); m_size++; if ( position == m_tail ) m_tail = element; } void List::insertBefore( ListElement *position, const void *data ) { ListElement *element = (ListElement *) memAlloc( sizeof( ListElement ) ); element->data( data ); element->next( position ); element->prev( position->prev() ); if( position->prev() != 0 ) { position->prev()->next( element ); } position->prev( element ); m_size++; if ( position == m_head ) m_head = element; } ListElement *List::erase( ListElement *position ) { ListElement *ret = position->next(); if( position == m_head ) { // could be 0 if m_head == m_tail m_head = ret; if ( ret != 0 ) { ret->prev(0); } else m_tail = 0; } else if ( position == m_tail ) { // here, ret is 0 for sure. m_tail = position->prev(); m_tail->next(0); } else { // normal case position->prev()->next( ret ); ret->prev( position->prev() ); } if( m_deletor != 0 ) m_deletor( (void *) position->data() ); m_size--; memFree( position ); return ret; } uint32 ListTraits::memSize() const { return sizeof( List ); } void ListTraits::init( void *itemZone ) const { List *list = (List *) itemZone; list->m_head = list->m_tail = 0; list->m_deletor = 0; } void ListTraits::copy( void *targetZone, const void *sourceZone ) const { memcpy( targetZone, sourceZone, sizeof( List ) ); } int ListTraits::compare( const void *first, const void *second ) const { return -1; } void ListTraits::destroy( void *item ) const { List *list = (List *) item; list->clear(); } bool ListTraits::owning() const { return true; } namespace traits { FALCON_DYN_SYM ListTraits &t_List() { static ListTraits lt; return lt; } } } /* end of List.cpp */ engine/genericmap.cpp000066400000000000000000000722011176363201700151510ustar00rootroot00000000000000 /* FALCON - The Falcon Programming Language. FILE: genericmap.cpp Generic map - a map holding generic values. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun ago 23 21:55:38 CEST 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #define PLAT_ALIGN 4 namespace Falcon { Map::Map( ElementTraits *keyt, ElementTraits *valuet, uint16 order ): m_keyTraits( keyt ), m_valueTraits( valuet ), m_treeOrder( order ), m_size(0), m_treeTop(0) { if ( order % 2 == 0 ) m_treeOrder = order + 1; // pre-cache key and value sizes m_keySize = keyt->memSize(); // force 4bytes alignment if ( m_keySize % PLAT_ALIGN != 0 ) { m_keySize = ((m_keySize / PLAT_ALIGN ) + 1) * PLAT_ALIGN; } m_valueSize = valuet->memSize(); // force 4bytes alignment if ( m_valueSize % PLAT_ALIGN != 0 ) { m_valueSize = ((m_valueSize / PLAT_ALIGN) + 1) * PLAT_ALIGN; } // create the first page. m_treeTop = allocPage(); } Map::~Map() { if ( m_treeTop != 0 ) { destroyPage( m_treeTop ); m_treeTop = 0; } else { throw "Double destruction"; } } //====================================== // MAP_PAGE *Map::allocPage() const { fassert( sizeof(MAP_PAGE) % 4 == 0 ); uint32 sz = (m_keySize + m_valueSize + sizeof( MAP_PAGE * ) ) * m_treeOrder + sizeof( MAP_PAGE ); MAP_PAGE *page = (MAP_PAGE *) memAlloc( sz ); page->m_count = 0; page->m_parent = 0; page->m_higher = 0; page->m_allocated = 0; page->m_dummy = 0; page->m_parentElement = 0; return page; } MAP_PAGE **Map::ptrsOfPage( const MAP_PAGE *ptr ) const { char *page = (char *) ptr; return (MAP_PAGE **) ( page + sizeof( MAP_PAGE ) ); } void *Map::keysOfPage( const MAP_PAGE *ptr ) const { char *page = (char *) ptr; return page + sizeof( MAP_PAGE ) + ( m_treeOrder * sizeof( MAP_PAGE * ) ); } void *Map::valuesOfPage( const MAP_PAGE *ptr ) const { char *page = (char *) ptr; return page + sizeof( MAP_PAGE ) + ( m_treeOrder * (sizeof( MAP_PAGE * ) + m_keySize ) ); } MAP_PAGE *Map::ptrInPage( const MAP_PAGE *ptr, uint16 count ) const { char *page = (char *) ptr; MAP_PAGE **pageVect = (MAP_PAGE **) ( page + sizeof( MAP_PAGE ) ); return pageVect[count]; } void *Map::keyInPage( const MAP_PAGE *ptr, uint16 count ) const { char *page = (char *) ptr; page += sizeof( MAP_PAGE ) + ( m_treeOrder * sizeof( MAP_PAGE * ) ); return page + (count * m_keySize); } void *Map::valueInPage( const MAP_PAGE *ptr, uint16 count ) const { char *page = (char *) ptr; page += sizeof( MAP_PAGE ) + ( m_treeOrder * (sizeof( MAP_PAGE * ) + m_keySize ) ); return page + (count * m_valueSize); } //====================================== // void *Map::find( const void *key ) const { MapIterator iter; if( find( key, iter ) ) return iter.currentValue(); return 0; } bool Map::find( const void *key, MapIterator &iter ) const { iter.m_map = this; // if the top page has zero element, this is the find if( m_treeTop->m_count == 0 ) { iter.m_pagePosition = 0; iter.m_page = m_treeTop; return false; } return subFind( key, iter, m_treeTop ); } bool Map::subFind( const void *key, MapIterator &iter, MAP_PAGE *currentPage ) const { register int count = currentPage->m_count; // By design, this page cannot have zero elements uint16 pos; bool found = scanPage( key, currentPage, count, pos ); if ( found ) { // FOUND!!! iter.m_pagePosition = pos; iter.m_page = currentPage; return true; } // greater than the greatest element? if( pos >= count ) { // if there is a greater page, search there if ( currentPage->m_higher != 0 ) { return subFind( key, iter, currentPage->m_higher ); } // else, insert past last iter.m_pagePosition = count; iter.m_page = currentPage; return false; } // if the item has a smaller page, go there MAP_PAGE *page = ptrInPage( currentPage, pos ); if ( page != 0 ) { return subFind( key, iter, page ); } // we should insert in this position iter.m_pagePosition = pos; iter.m_page = currentPage; return false; } bool Map::scanPage( const void *key, MAP_PAGE *currentPage, uint16 higher, uint16 &ret_pos ) const { // by design, higher can't be zero. register uint16 lower = 0, point; higher --; point = higher / 2; void *cfrKey; int cmp; while ( true ) { // get the table element cfrKey = keyInPage( currentPage, point ); cmp = m_keyTraits->compare( cfrKey, key ); if( cmp == 0 ) { ret_pos = point; return true; } else { if ( lower == higher ) // not found { break; } // last try. In pair sized dictionaries, it can be also in the other node else if ( point == lower && point == higher - 1 ) { // if it's lower than the lower, we must insert before the lower ) if ( cmp > 0 ) { ret_pos = point; return false; } // being integer math, ulPoint is rounded by defect and has // already looked at the ulLower position point = lower = higher; // try again continue; } if ( cmp < 0 ) { lower = point; } else { higher = point; } point = ( lower + higher ) / 2; } } // entry not found, but signal the best match anyway ret_pos = cmp < 0 ? higher + 1: higher; return false; } void Map::insertSpaceInPage( MAP_PAGE *page, uint16 pos ) { // by design, count in page cannot be greater than page order if( pos < page->m_count ) { char *mp_pos = (char *) page; mp_pos += sizeof( MAP_PAGE ) + (sizeof( MAP_PAGE *) * pos); memmove( mp_pos + sizeof( MAP_PAGE * ), mp_pos, sizeof( MAP_PAGE *) * (page->m_count - pos ) ); char *key_pos = (char *) keyInPage( page, pos ); memmove( key_pos + m_keySize, key_pos, m_keySize * (page->m_count - pos ) ); char *val_pos = (char *) valueInPage( page, pos ); memmove( val_pos + m_valueSize, val_pos, m_valueSize * (page->m_count - pos ) ); } page->m_count++; } void Map::removeSpaceFromPage( MAP_PAGE *page, uint16 pos ) { // The last element does not need refitting if( pos < page->m_count - 1 ) { char *mp_pos = (char *) page; mp_pos += sizeof( MAP_PAGE ) + (sizeof( MAP_PAGE *) * pos); memmove( mp_pos, mp_pos + sizeof( MAP_PAGE *), sizeof( MAP_PAGE *) * (page->m_count - pos -1) ); char *key_pos = (char *) keyInPage( page, pos ); memmove( key_pos, key_pos + m_keySize, m_keySize * (page->m_count - pos -1) ); char *val_pos = (char *) valueInPage( page, pos ); memmove( val_pos, val_pos + m_valueSize, m_valueSize * (page->m_count - pos -1) ); page->m_count --; if ( ptrInPage( page, pos ) != 0 ) { while ( pos < page->m_count ) { ptrInPage( page, pos )->m_parentElement = pos; ++pos; } } } else page->m_count --; } bool Map::insert( const void *key, const void *value ) { MapIterator iter; if ( find( key, iter ) ) { m_valueTraits->destroy( iter.currentValue() ); m_valueTraits->copy( iter.currentValue(), value ); return false; } // fix page situation. insertSpaceInPage( iter.m_page, iter.m_pagePosition ); // put data in space m_keyTraits->copy( iter.currentKey(), key ); m_valueTraits->copy( iter.currentValue(), value ); ptrsOfPage( iter.m_page )[ iter.m_pagePosition ] = 0; // as this is a leaf, no extra management is needed // signal we have an element more m_size++; // if the page is full, balance. if ( iter.m_page->m_count == m_treeOrder ) { splitPage( iter.m_page ); } return true; } bool Map::erase( const void *key ) { MapIterator iter; if ( find( key, iter ) ) { erase( iter ); return true; } return false; } MapIterator Map::erase( const MapIterator &iter ) { void *key = keyInPage( iter.m_page, iter.m_pagePosition ); void *value = valueInPage( iter.m_page, iter.m_pagePosition ); m_keyTraits->destroy( key ); m_valueTraits->destroy( value ); MAP_PAGE *child = ptrInPage( iter.m_page, iter.m_pagePosition ); MapIterator retIter = iter; // if we have no children, we must shrink the page. if ( child == 0 ) { removeSpaceFromPage( iter.m_page, iter.m_pagePosition ); // if we are too small, we must re-balance the tree // but the tree-top is an exception if( iter.m_page->m_count < m_treeOrder / 2 && iter.m_page != m_treeTop ) { rebalanceNode( iter.m_page, &retIter ); } } else { // we'll promote the highest of our children to our position. MAP_PAGE *child_child = child->m_higher; // and promote one from each child up to the leaves. while( child_child != 0 ) { child = child_child; child_child = child->m_higher; } child->m_count--; memcpy( key, keyInPage( child, child->m_count ), m_keySize ); memcpy( value, valueInPage( child, child->m_count ), m_valueSize ); // in case a leaf child is unbalanced, we'll start rebalance algorithm. if( child->m_count < m_treeOrder / 2 ) rebalanceNode( child, &retIter ); } m_size --; return retIter; } MAP_PAGE *Map::getLeftSibling( const MAP_PAGE *page ) const { uint16 parentElem = page->m_parentElement; MAP_PAGE *parent = page->m_parent; // No parent, no sibling. if ( parent == 0 || parentElem == 0 ) return 0; if ( parentElem >= parent->m_count ) return ptrInPage( parent, parent->m_count - 1 ); return ptrInPage( parent, parentElem - 1 ); } MAP_PAGE *Map::getRightSibling( const MAP_PAGE *page ) const { uint16 parentElem = page->m_parentElement; MAP_PAGE *parent = page->m_parent; // No parent, no sibling. if ( parent == 0 ) return 0; // we are the higher; of course we don't have siblings. if ( parentElem >= parent->m_count ) return 0; // ok, we have a sibling in the parent parentElem++; if( parentElem >= parent->m_count ) return parent->m_higher; return ptrInPage( parent, parentElem ); } void Map::reshapeChildPointers( MAP_PAGE *page, uint16 startFrom ) { while ( startFrom < page->m_count ) { MAP_PAGE *child = ptrInPage( page, startFrom ); child->m_parent = page; child->m_parentElement = startFrom; ++ startFrom; } page->m_higher->m_parentElement = m_treeOrder + 1; page->m_higher->m_parent = page; } void Map::rebalanceNode( MAP_PAGE *page, MapIterator *scanner ) { MAP_PAGE *left, *right; MAP_PAGE *parent; parent = page->m_parent; int limit = m_treeOrder / 2; // no rebalancing for the root if ( parent == 0 || page->m_count >= limit ) return; // identify left sibling. left = getLeftSibling( page ); right = getRightSibling( page ); // nonroot element must have at least a sibling. fassert( left != 0 || right != 0 ); // decide which has the larger count. if ( (right != 0 && right->m_count > limit ) && ( left == 0 || right->m_count > left->m_count ) ) { // rotate right elements int elems = (right->m_count - limit) / 2; // move our parent here at limit position memcpy( keyInPage( page, page->m_count ), keyInPage( parent, page->m_parentElement), m_keySize ); memcpy( valueInPage( page, page->m_count ), valueInPage( parent, page->m_parentElement), m_valueSize ); // whose child is our higher ptrsOfPage(page)[ page->m_count ] = page->m_higher; // if the scanner was at our parent, move it to limit in this page // now move elems items from the page on the right. // elems may be zero if the other page is just limit + 1 items. if ( elems > 0 ) { memcpy( keyInPage( page, page->m_count + 1), keyInPage( right, 0 ), m_keySize * elems ); memcpy( valueInPage( page, page->m_count + 1 ), valueInPage( right, 0 ), m_valueSize * elems ); memcpy( &ptrsOfPage(page)[ page->m_count + 1 ], &ptrsOfPage( right )[ 0 ] , sizeof( MAP_PAGE *) * elems ); } // now rotate the elems item in place of our old parent; its' child are our new higher memcpy( keyInPage( parent, page->m_parentElement), keyInPage( right, elems ), m_keySize ); memcpy( valueInPage( parent, page->m_parentElement), valueInPage( right, elems ), m_valueSize ); page->m_higher = ptrInPage( right, elems ); // set our new count page->m_count = page->m_count + elems + 1; // finally, shift left the right pages of elems + 1 items. elems ++; int rcount = right->m_count - elems; memmove( keyInPage( right, 0 ), keyInPage( right, elems ), m_keySize * rcount ); memmove( valueInPage( right, 0), valueInPage( right, elems ), m_valueSize * rcount ); memmove( ptrsOfPage( right ), &ptrsOfPage( right )[elems], sizeof( MAP_PAGE *) * rcount ); right->m_count = rcount; // fix backpointers to changed pages. if ( ptrInPage( page, 0 ) != 0 ) { reshapeChildPointers( page ); reshapeChildPointers( right ); } // if the scanner was in the moved elements, move it too if ( scanner != 0 ) { if ( scanner->m_page == parent && scanner->m_pagePosition == page->m_parentElement ) { scanner->m_page = page; scanner->m_pagePosition = limit; } else if ( scanner->m_page == right ) { // elems has been grown if ( scanner->m_pagePosition == elems - 1) { scanner->m_page = parent; scanner->m_pagePosition = page->m_parentElement; } else if ( scanner->m_pagePosition < elems -1 ) { scanner->m_page = page; scanner->m_pagePosition = limit + 1 + scanner->m_pagePosition; } else { scanner->m_pagePosition -= elems; } } } return; } if ( left != 0 && left->m_count > limit ) { // rotate left elements int elems = (left->m_count - limit) / 2; // shift this page elements on the right to make room (elems plus the rotated parent memmove( keyInPage( page, elems + 1 ), keyInPage( page, 0 ), m_keySize * page->m_count ); memmove( valueInPage( page, elems + 1 ), valueInPage( page, 0 ), m_valueSize * page->m_count ); memmove( &ptrsOfPage( page )[elems + 1], ptrsOfPage( page ), sizeof( MAP_PAGE *) * page->m_count ); // move left page parent's to elems position. memcpy( keyInPage( page, elems ), keyInPage( parent, left->m_parentElement), m_keySize ); memcpy( valueInPage( page, elems ), valueInPage( parent, left->m_parentElement), m_valueSize ); // whose child left's higher ptrsOfPage(page)[elems] = left->m_higher; // now move elems items from the page on the left. int lcount = left->m_count - elems; // elems may be zero if the other page is just limit + 1 items. if ( elems > 0 ) { memcpy( keyInPage( page, 0 ), keyInPage( left, lcount ), m_keySize * elems); memcpy( valueInPage( page, 0 ), valueInPage( left, lcount ), m_valueSize * elems ); memcpy( ptrsOfPage(page), &ptrsOfPage( left )[lcount], sizeof( MAP_PAGE *) * elems ); } // now rotate the elems item in place of left's old parent; its' child are left's higher lcount--; memcpy( keyInPage( parent, left->m_parentElement), keyInPage( left, lcount ), m_keySize ); memcpy( valueInPage( parent, left->m_parentElement), valueInPage( left, lcount ), m_valueSize ); left->m_higher = ptrInPage( left, lcount ); // set our new count page->m_count += elems + 1; // finally, shift left the right pages of elems + 1 items. left->m_count = lcount; // fix backpointers to changed pages. if ( ptrInPage( page, 0 ) != 0 ) { reshapeChildPointers( page ); // only the higher is changed in the left page left->m_higher->m_parent = left; left->m_higher->m_parentElement = m_treeOrder; } // if the scanner was in the moved elements, move it too if ( scanner != 0 ) { // if the scanner was at our parent, move it to limit in this page if ( scanner->m_page == parent && scanner->m_pagePosition == left->m_parentElement ) { scanner->m_page = page; scanner->m_pagePosition = elems; } else if ( scanner->m_page == left ) { // lcount has already been shrunk if ( scanner->m_pagePosition == lcount ) { scanner->m_page = parent; scanner->m_pagePosition = left->m_parentElement; } else if ( scanner->m_pagePosition > lcount ) { scanner->m_page = page; scanner->m_pagePosition = scanner->m_pagePosition - lcount - 1; } } else if ( scanner->m_page == page ) { scanner->m_pagePosition = elems + scanner->m_pagePosition; } } return; } // if here, we can only perform a complete merge. // If right is not zero, excange us with left and right with page, and act as for left. if ( left == 0 ) { left = page; page = right; } // we need a bit of space on the left. memcpy( keyInPage( page, left->m_count + 1), keysOfPage( page ), m_keySize * page->m_count ); memcpy( valueInPage( page, left->m_count + 1), valuesOfPage( page ), m_valueSize * page->m_count ); memcpy( &ptrsOfPage( page )[ left->m_count + 1], ptrsOfPage( page ), sizeof(MAP_PAGE *) * page->m_count ); // nowy copy the keys on the left (0 to left->m_count -1) memcpy( keysOfPage( page ), keysOfPage( left ), m_keySize * left->m_count ); memcpy( valuesOfPage( page ) , valuesOfPage( left ), m_valueSize * left->m_count ); memcpy( ptrsOfPage( page ), ptrsOfPage( left ), sizeof(MAP_PAGE *) * left->m_count ); memcpy( keyInPage( page, left->m_count ), keyInPage( parent, left->m_parentElement ), m_keySize ); memcpy( valueInPage( page, left->m_count ) , valueInPage( parent, left->m_parentElement ), m_valueSize ); ptrsOfPage( page )[ left->m_count ] = left->m_higher; page->m_count = left->m_count + page->m_count + 1; // page should be full now except for 1 removeSpaceFromPage( parent, left->m_parentElement ); // if the scanner was in the moved elements, move it too if ( scanner != 0 ) { // if the scanner was at our parent, move it to limit in this page if ( scanner->m_page == parent ) { if( scanner->m_pagePosition == left->m_parentElement ) { scanner->m_page = page; scanner->m_pagePosition = limit; } else if ( scanner->m_pagePosition > left->m_parentElement ) { scanner->m_pagePosition--; } } else if ( scanner->m_page == left ) { scanner->m_page = page; } else if ( scanner->m_page == page ) { scanner->m_pagePosition = limit + 1 + scanner->m_pagePosition; } } memFree( left ); if ( ptrInPage( page, 0 ) != 0 ) reshapeChildPointers( page ); if ( parent->m_count < limit ) { // treetop? if( parent == m_treeTop ) { if ( parent->m_count == 0 ) { // page was the higher of treetop... memFree( m_treeTop ); m_treeTop = page; page->m_parent = 0; } } else rebalanceNode( parent, scanner ); } return; } void Map::splitPage( MAP_PAGE *page ) { // splitting a page requires to insert the median element in the upper page. MAP_PAGE *parent = page->m_parent; void *key; void *value; int i; // this is the splitted node uint16 splitPos = page->m_count / 2; key = keyInPage( page, splitPos ); value = valueInPage( page, splitPos ); MAP_PAGE *selected_child = ptrInPage( page, splitPos ); // create a new page that will be added to the left of this page MAP_PAGE *new_left = allocPage(); memcpy( ptrsOfPage( new_left ), ptrsOfPage( page ), sizeof( MAP_PAGE *) * splitPos ); memcpy( keysOfPage( new_left ), keysOfPage( page ), m_keySize * splitPos ); memcpy( valuesOfPage( new_left ), valuesOfPage( page ), m_valueSize * splitPos ); new_left->m_count = splitPos; // if we don't have a parent (if we are the treetop), we must create a new treetop if( parent == 0 ) { fassert( page == m_treeTop ); // we must create a new treetop whose higher pointer is the splitted page. parent = allocPage(); parent->m_parent = 0; memcpy( keysOfPage( parent ), key , m_keySize ); memcpy( valuesOfPage( parent ), value, m_valueSize ); ptrsOfPage( parent ) [ 0 ] = new_left; parent->m_higher = page; parent->m_count = 1; new_left->m_parent = parent; new_left->m_parentElement = 0; page->m_parent = parent; page->m_parentElement = m_treeOrder; m_treeTop = parent; } else { fassert( page != m_treeTop ); // Now save the element in the previous page // place the inserted item uint16 parentPos = page->m_parentElement; // insert the splitted item if( parentPos < parent->m_count ) insertSpaceInPage( parent, parentPos ); else { parentPos = parent->m_count; parent->m_count ++; } memcpy( keyInPage( parent, parentPos ), key , m_keySize ); memcpy( valueInPage( parent, parentPos ), value, m_valueSize ); // the page maintain the same parent, that is moved forward by one for( uint16 childPos = parentPos + 1; childPos < parent->m_count; childPos ++ ) { ptrInPage( parent, childPos )->m_parentElement = childPos; } // the child of the inserted splitted element is the new left page ptrsOfPage( parent ) [ parentPos ] = new_left; new_left->m_parent = parent; new_left->m_parentElement = parentPos; } // the old child of the splitted element becomes the new higher of the left page if ( selected_child != 0 ) { selected_child->m_parent = new_left; selected_child->m_parentElement = m_treeOrder; new_left->m_higher = selected_child; } else new_left->m_higher = 0; // now that we're done with the key, we can scroll back the original page. splitPos++; int scrollSize = page->m_count - splitPos; memcpy( ptrsOfPage( page ), ptrsOfPage( page ) + splitPos, sizeof( MAP_PAGE *) * scrollSize ); memcpy( keysOfPage( page ), keyInPage( page, splitPos ) , m_keySize * scrollSize ); memcpy( valuesOfPage( page ), valueInPage( page, splitPos ), m_valueSize * scrollSize ); page->m_count = scrollSize; // we have to update all the children page to point to the new page positions. MAP_PAGE *child = ptrInPage( new_left, 0 ); if ( child != 0 ) { fassert( new_left->m_count == page->m_count ); // a little check for( i = 0; i < page->m_count; i++ ) // we've just set it to page count { child = ptrInPage( new_left, i ); fassert( child != 0 ); child->m_parent = new_left; child->m_parentElement = i; child = ptrInPage( page, i ); fassert( child != 0 ); // parent was already page. child->m_parentElement = i; } } // higher elements have already been updated correctly. // the only thing left to do is to see if the parent has overgrown. if( parent->m_count == m_treeOrder ) { splitPage( parent ); } } void Map::clear() { destroyPage( m_treeTop ); m_treeTop = allocPage(); m_size = 0; } void Map::destroyPage( MAP_PAGE *page ) { for ( uint16 i = 0; i < page->m_count; i++ ) { m_keyTraits->destroy( keyInPage( page, i ) ); m_valueTraits->destroy( valueInPage( page, i ) ); MAP_PAGE *child = ptrInPage( page, i ); if ( child != 0 ) destroyPage( child ); } if ( page->m_higher != 0 ) destroyPage( page->m_higher ); memFree( page ); } MapIterator Map::begin() const { MAP_PAGE *page = m_treeTop; if ( m_size == 0 ) { return MapIterator( this, 0, 0 ); } MAP_PAGE *next = ptrInPage( page, 0 ); while( next != 0 ) { page = next; next = ptrInPage( page, 0 ); } MapIterator iter( this, page, 0 ); return iter; } MapIterator Map::end() const { MAP_PAGE *page = m_treeTop; if ( m_size == 0 ) { return MapIterator( this, 0, 0 ); } while( page->m_higher != 0 ) page = page->m_higher; // will generate an invalid iterator. prev() must be used. MapIterator iter( this, page, page->m_count ); return iter; } bool MapIterator::next() { // if it's the same page, go to the left. m_pagePosition++; MAP_PAGE *page = m_page; // if the current page is over... if ( m_pagePosition >= page->m_count ) { // if we have a higher page, use that if( page->m_higher != 0 ) { m_page = page->m_higher; page = m_map->ptrInPage( m_page, 0 ); while( page != 0 ) { m_page = page; page = m_map->ptrInPage( page, 0 ); } m_pagePosition = 0; return true; } // get parent's sibling. int16 parentPos = page->m_parentElement; page = page->m_parent; while( page != 0 ) { if ( parentPos < page->m_count ) { // return our parent m_pagePosition = parentPos; m_page = page; return true; } // we were from an higher, so we can't get again in an higher. parentPos = page->m_parentElement; page = page->m_parent; } // we're off return false; } else { // get the child of our next sibling page = m_map->ptrInPage( page, m_pagePosition ); if ( page == 0 ) { // return our sibling ( as we already did m_pagePosition++) return true; } else { // descend to the bottom of the hyerarcy m_page = page; page = m_map->ptrInPage( page, 0 ); while( page != 0 ) { m_page = page; page = m_map->ptrInPage( page, 0 ); } m_pagePosition = 0; return true; } } // we never get here fassert( false ); } bool MapIterator::prev() { // has this element a child ? - in this case, get the leftmost child element. if( m_pagePosition < m_page->m_count ) { MAP_PAGE *child = m_map->ptrInPage( m_page, m_pagePosition ); if( child != 0 ) { while( child->m_higher != 0 ) { child = child->m_higher; } m_page = child; m_pagePosition = child->m_count - 1; return true; } // if we have no children, proceed as usual } //are there other elements in this page? if( m_pagePosition > 0 ) { m_pagePosition--; return true; } // else, we must get the previous element in the parent page. // we need to scan a parent page until we have a position which is greater than 0 MAP_PAGE *page = m_page->m_parent; uint16 ppos = m_page->m_parentElement; while( page != 0 && ppos == 0 ) { ppos = page->m_parentElement; page = page->m_parent; } // if the page is zero, we can't do anything more if( page == 0 ) { // invalidate the iterator m_pagePosition = m_map->m_treeOrder; return false; } // if the PPOS is >= count, it means we was in an "higher page" if( ppos >= page->m_count ) ppos = page->m_count - 1; else ppos--; m_page = page; m_pagePosition = ppos; return true; } bool MapIterator::hasNext() const { return m_page != 0 && ( m_page->m_count > m_pagePosition + 1 || m_map->ptrInPage( m_page, m_page->m_count - 1 ) != 0 ); } bool MapIterator::hasPrev() const { if ( m_page == 0 ) return false; if( m_pagePosition > 0 ) return true; uint16 ppos = m_page->m_parentElement; MAP_PAGE *page = m_page->m_parent; while( page != 0 && ppos == 0 ) { ppos = page->m_parentElement; page = page->m_parent; } return page != 0; } bool MapIterator::equal( const MapIterator &other ) const { return m_map == other.m_map && m_page == other.m_page && m_pagePosition == other.m_pagePosition; } //======================================================= // Map traits uint32 MapPtrTraits::memSize() const { return sizeof( Map * ); } void MapPtrTraits::init( void *itemZone ) const { Map **map = (Map **) itemZone; *map = 0; } void MapPtrTraits::copy( void *targetZone, const void *sourceZone ) const { Map **tgt = (Map **) targetZone; Map *src = (Map *) sourceZone; *tgt = src; } int MapPtrTraits::compare( const void *first, const void *second ) const { return -1; } void MapPtrTraits::destroy( void *item ) const { // do nothing } bool MapPtrTraits::owning() const { return false; } void MapPtrOwnTraits::destroy( void *item ) const { Map **ptr = (Map**) item; delete (*ptr); } bool MapPtrOwnTraits::owning() const { return true; } namespace traits { FALCON_DYN_SYM MapPtrTraits &t_MapPtr(); FALCON_DYN_SYM MapPtrOwnTraits &t_MapPtrOwn(); } } /* end of genericmap.cpp */ engine/genericvector.cpp000066400000000000000000000111011176363201700156660ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: genericvector.cpp Generic vector - a generic vector of elements ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven oct 27 11:02:00 CEST 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include namespace Falcon { GenericVector::GenericVector( const ElementTraits *traits, uint32 prealloc ): m_data( 0 ), m_size( 0 ), m_allocated( prealloc < alloc_block ? alloc_block : prealloc ), m_threshold_size( 0 ), m_traits(traits) { fassert( traits != 0 ); m_itemSize = traits->memSize(); if ( m_itemSize % 4 != 0 ) m_itemSize = (m_itemSize/4 + 1) * 4; if ( m_allocated > 0 ) m_data = (byte *) memAlloc( m_allocated * m_itemSize ); } void GenericVector::init( const ElementTraits *traits, uint32 prealloc ) { fassert( traits != 0 ); m_size = 0; m_allocated = prealloc < alloc_block ? alloc_block : prealloc; m_traits = traits; m_itemSize = traits->memSize(); if ( m_itemSize % 4 != 0 ) m_itemSize = (m_itemSize/4 + 1) * 4; if( m_allocated > 0 ) m_data = (byte *) memAlloc( m_allocated * m_itemSize ); } GenericVector::~GenericVector() { if( m_traits->owning() ) { for ( uint32 i = 0; i < m_size; i ++ ) { m_traits->destroy( at( i ) ); } } memFree( m_data ); } void GenericVector::insert( void *data, uint32 pos ) { if ( pos > m_size ) { return; } m_size ++; if ( m_size >= m_allocated ) { m_allocated = m_size + alloc_block; byte *target_data = (byte *) memRealloc( m_data, m_allocated * m_itemSize ); if ( target_data != 0 ) { m_data = target_data; } } else { if ( pos < m_size ) memmove( m_data + ( m_itemSize * (pos+1) ), m_data + ( m_itemSize * pos ), ( (m_size - pos) * m_itemSize ) ); } m_traits->copy( m_data + ( m_itemSize * pos ), data ); } bool GenericVector::remove( uint32 pos ) { if ( pos >= m_size ) return false; m_traits->destroy( m_data + ( m_itemSize * pos ) ); if ( pos < m_size-1 ) { memmove( m_data + ( m_itemSize * pos ), m_data + ( m_itemSize * (pos+1) ), ( m_itemSize * (m_size - pos ) ) ); } m_size --; return true; } void GenericVector::set( void *data, uint32 pos ) { if ( pos >= m_size ) return; byte *target = m_data + ( m_itemSize * pos ); m_traits->destroy( target ); m_traits->copy( target, data ); } void GenericVector::push( void *data ) { m_traits->copy( m_data + ( m_itemSize * m_size ), data ); m_size ++; if ( m_size >= m_allocated ) { m_allocated = m_size + alloc_block; byte *target_data = (byte *) memRealloc( m_data, m_allocated * m_itemSize ); m_data = target_data; } } void GenericVector::reserve( uint32 s ) { if ( m_allocated >= s + 1 ) return; byte *mem = (byte *) memAlloc( (s+1) * m_itemSize ); if( m_allocated > 0 ) { if ( m_size > 0 ) memcpy( mem, m_data, m_size * m_itemSize ); memFree( m_data ); } m_data = mem; m_allocated = s+1; } void GenericVector::resize( uint32 s ) { if ( s == m_size ) return; if( s > m_size ) { if ( s >= m_allocated ) { m_allocated = ((s/alloc_block) + 1) * alloc_block; byte *mem = (byte *) memRealloc( m_data, m_allocated * m_itemSize ); m_data = mem; if ( mem == 0 ) { // atm, memRealloc should take care of this. } } for( uint32 i = m_size; i <= s; i ++ ) { m_traits->init( at( i ) ); } } else { if ( m_traits->owning() ) { for( uint32 i = s+1; i < m_size; i ++ ) { m_traits->destroy( at( i ) ); } } if ( m_threshold_size > 0 ) { if ( s + m_threshold_size < m_size ) { m_allocated = ((s/m_threshold_size) + 1 ) * m_threshold_size; m_data = (byte *) memRealloc( m_data, m_allocated * m_itemSize ); m_allocated = m_allocated; } } else { // without threshold size, always resize. m_data = (byte *) memRealloc( m_data, (s+1) * m_itemSize ); m_allocated = s+1; } } m_size = s; } } /* end of genericvector.cpp */ engine/genhasm.cpp000066400000000000000000002270261176363201700144700ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: genhasm.cpp Generate Falcon Assembly from a Falcon syntactic tree. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include namespace Falcon { GenHAsm::GenHAsm( Stream *out ): Generator( out ), m_branch_id(1), m_loop_id(1), m_try_id(1) {} void GenHAsm::generatePrologue( const Module *mod ) { m_out->writeString( "; -------------------------------------------------------------\n" ); m_out->writeString( "; Falcon ASM source for module " + mod->name() + "\n" ); m_out->writeString( "; -------------------------------------------------------------\n\n" ); m_out->writeString( "; -------------\n" ); m_out->writeString( "; String table\n\n" ); // generates the string table. gen_stringTable( mod ); m_out->writeString( "\n" ); m_out->writeString( "; -------------\n" ); m_out->writeString( "; Dependecy table\n\n" ); // generates the string table. gen_depTable( mod ); m_out->writeString( "\n" ); // generates the string table. m_out->writeString( "; -------------\n" ); m_out->writeString( "; Symbol table\n\n" ); gen_symbolTable( mod ); m_out->writeString( "\n" ); } void GenHAsm::generate( const SourceTree *st ) { // generates the main program if ( ! st->statements().empty() ) { m_out->writeString( "; -------------\n" ); m_out->writeString( "; Entry point\n\n" ); m_out->writeString( ".entry\n" ); gen_block( &st->statements() ); m_out->writeString( "\tRET\n" ); } // generate functions if ( ! st->classes().empty() ) { m_out->writeString( "; -------------------------------------------------------------\n" ); m_out->writeString( "; Classes\n" ); m_out->writeString( "; -------------------------------------------------------------\n" ); const StmtClass *cls = static_cast(st->classes().front()); while( cls != 0 ) { gen_class( cls ); cls = static_cast(cls->next()); } m_out->writeString( "\n" ); } // generate functions if ( ! st->functions().empty() ) { m_out->writeString( "; -------------------------------------------------------------\n" ); m_out->writeString( "; Routines\n" ); m_out->writeString( "; -------------------------------------------------------------\n" ); const StmtFunction *func = static_cast(st->functions().front()); while( func != 0 ) { gen_function( func ); func = static_cast(func->next()); } m_out->writeString( "\n" ); } m_out->writeString( "\n; -------------------------------------------------------------\n" ); m_out->writeString( "; End of listing\n" ); m_out->writeString( "; -------------------------------------------------------------\n" ); } void GenHAsm::gen_class( const StmtClass *cls ) { const Symbol *sym = cls->symbol(); m_out->writeString( "; -------------------------------------------------------------\n" ); m_out->writeString( "; Class\n" ); m_out->writeString( "; -------------------------------------------------------------\n" ); m_out->writeString( ".classdef " + sym->name() ); if ( sym->exported() ) m_out->writeString( " export" ); m_out->writeString( "\n" ); const ClassDef *cd = sym->getClassDef(); // write class symbol parameters. MapIterator st_iter = cd->symtab().map().begin(); int16 count = 0; while( st_iter.hasCurrent() && count < cd->symtab().size() ) { // we have no locals. const Symbol *param = *( const Symbol **) st_iter.currentValue(); if( param->itemId() == count ) { m_out->writeString( ".param " + param->name() + "\n" ); count++; st_iter = cd->symtab().map().begin(); continue; } st_iter.next(); } // write all the inheritances. ListElement *it_elem = cd->inheritance().begin(); while( it_elem != 0 ) { const InheritDef *id = (const InheritDef *) it_elem->data(); const Symbol *parent = id->base(); m_out->writeString( ".inherit $" + parent->name() ); m_out->writeString( "\n" ); it_elem = it_elem->next(); } // write all the properties. MapIterator pt_iter = cd->properties().begin(); while( pt_iter.hasCurrent() ) { const VarDef *def = *(const VarDef **) pt_iter.currentValue(); if ( ! def->isBaseClass() ) { if ( def->isReference() ) m_out->writeString( ".propref " ); else m_out->writeString( ".prop " ); const String *key = *( const String **) pt_iter.currentKey(); m_out->writeString( *key + " " ); gen_propdef( *def ); m_out->writeString( "\n" ); } pt_iter.next(); } if ( cd->constructor() != 0 ) { m_out->writeString( ".ctor $" + cd->constructor()->name() + "\n" ); } MapIterator state_iter = cd->states().begin(); while( state_iter.hasCurrent() ) { const StateDef *def = *(const StateDef **) state_iter.currentValue(); m_out->writeString( ".state " ); const String *key = *( const String **) state_iter.currentKey(); m_out->writeString( *key ); m_out->writeString( "\n" ); MapIterator s_iter = def->functions().begin(); while( s_iter.hasCurrent() ) { m_out->writeString( ".stateitem " ); const String *key = *( const String **) s_iter.currentKey(); const Symbol *func = *( const Symbol **) s_iter.currentValue(); m_out->writeString( *key + " $" + func->name() + "\n" ); s_iter.next(); } state_iter.next(); } m_out->writeString( ".endclass\n" ); } void GenHAsm::gen_propdef( const VarDef &def ) { switch( def.type() ) { case VarDef::t_nil: m_out->writeString( "NIL" ); break; case VarDef::t_int: { String str; str.writeNumber( def.asInteger() ); m_out->writeString( str ); } break; case VarDef::t_num: { String str; str.writeNumber( def.asNumeric() ); m_out->writeString( str ); } break; case VarDef::t_string: { String temp; def.asString()->escape( temp ); m_out->writeString( "\"" + temp + "\""); } break; case VarDef::t_reference: case VarDef::t_symbol: m_out->writeString( "$" + def.asSymbol()->name() ); break; default: break; } } void GenHAsm::gen_depTable( const Module *mod ) { MapIterator iter = mod->dependencies().begin(); while( iter.hasCurrent() ) { const ModuleDepData *depdata = *(const ModuleDepData **) iter.currentValue(); // only generate non-private data if ( ! depdata->isPrivate() ) m_out->writeString( ".load " + depdata->moduleName() + "\n" ); iter.next(); } } void GenHAsm::gen_stringTable( const Module *mod ) { uint32 count = 0; const String *str = mod->getString( count ); while( str != 0 ) { // for now all cstrings. String temp; str->escape( temp ); if ( str->exported() ) m_out->writeString( ".istring \"" ); else m_out->writeString( ".string \"" ); m_out->writeString( temp + "\"\n" ); ++count; str = mod->getString( count); } } void GenHAsm::gen_symbolTable( const Module *mod ) { const SymbolTable *symtab = &mod->symbolTable(); MapIterator iter = symtab->map().begin(); String temp; while( iter.hasCurrent() ) { const Symbol *sym = *(const Symbol **) iter.currentValue(); switch( sym->type() ) { case Symbol::tundef: // see if it's imported if ( sym->imported() ) { // see if we have a namespace into which import it uint32 dotpos = sym->name().rfind( "." ); if( dotpos != String::npos ) { String modname = sym->name().subString( 0, dotpos ); String symname = sym->name().subString( dotpos + 1 ); temp = ".import " + symname + " "; temp.writeNumber( (int64) sym->declaredAt() ); ModuleDepData *depdata = mod->dependencies().findModule( modname ); // We have created the module, the entry must be there. fassert( depdata != 0 ); if ( depdata->isFile() ) { if( depdata->moduleName() == modname ) temp += " \"" + modname+"\""; else temp += " \"" + depdata->moduleName() +"\" "+ modname; } else { if( depdata->moduleName() == modname ) temp += " " + modname; else temp += " " + depdata->moduleName() +" "+ modname; } } else { temp = ".import " + sym->name() + " "; temp.writeNumber( (int64) sym->declaredAt() ); } } else { temp = ".extern " + sym->name() + " "; temp.writeNumber( (int64) sym->declaredAt() ); } m_out->writeString( temp ); break; case Symbol::tglobal: temp = ".global " + sym->name() + " "; temp.writeNumber( (int64) sym->declaredAt() ); m_out->writeString( temp ); break; case Symbol::tvar: m_out->writeString( ".var " + sym->name() + " " ); gen_propdef( *sym->getVarDef() ); temp = " "; temp.writeNumber( (int64) sym->declaredAt() ); m_out->writeString( temp ); break; case Symbol::tconst: m_out->writeString( ".const " + sym->name() + " " ); gen_propdef( *sym->getVarDef() ); temp = " "; temp.writeNumber( (int64) sym->declaredAt() ); m_out->writeString( temp ); break; case Symbol::tfunc: temp = ".func " + sym->name() + " "; temp.writeNumber( (int64) sym->declaredAt() ); m_out->writeString( temp ); break; case Symbol::tclass: temp = ".class " + sym->name() + " "; temp.writeNumber( (int64) sym->declaredAt() ); m_out->writeString( temp ); break; case Symbol::tinst: m_out->writeString( ".instance $" + sym->getInstance()->name() + " " + sym->name() ); temp = " "; temp.writeNumber( (int64) sym->declaredAt() ); m_out->writeString( temp ); break; case Symbol::timportalias: { ImportAlias* ia = sym->getImportAlias(); temp = ".alias " + ia->name() + " "; temp.writeNumber( (int64) sym->declaredAt() ); temp.append( " " ); if ( ia->isOrigFileName() ) temp.append( "\"" ); temp.append( ia->origModule() ); if ( ia->isOrigFileName() ) temp.append( "\"" ); temp.append( " " ); temp.append( sym->name() ); } m_out->writeString( temp ); break; default: break; } if ( sym->exported() && ! sym->isUndefined() ) m_out->writeString( " export" ); m_out->writeString( "\n" ); iter.next(); } } void GenHAsm::gen_function( const StmtFunction *func ) { m_functions.pushBack( func->symbol() ); const StmtClass *ctorFor = func->constructorFor(); const char *ret_mode = ctorFor != 0 ? "\tRETV\tS1\n" : "\tRET\n"; m_out->writeString( "; ---------------------------------------------\n" ); m_out->writeString( "; Function " + func->symbol()->name() + "\n" ); m_out->writeString( "; ---------------------------------------------\n\n" ); m_out->writeString( ".funcdef " + func->symbol()->name() ); if ( func->symbol()->exported() ) m_out->writeString( " export" ); m_out->writeString( "\n" ); // generate the local symbol table. const FuncDef *fd = func->symbol()->getFuncDef(); GenericVector params( &traits::t_voidp() ); GenericVector locals( &traits::t_voidp() ); params.resize( fd->symtab().size() ); locals.resize( fd->symtab().size() ); MapIterator iter = fd->symtab().map().begin(); while( iter.hasCurrent() ) { Symbol *sym = *(Symbol **) iter.currentValue(); switch( sym->type() ) { case Symbol::tparam: // paramters must be outputted with their original order. params.set( sym, sym->itemId() ); break; case Symbol::tlocal: locals.set( sym, sym->itemId() ); break; default: break; } iter.next(); } for ( uint32 parId = 0; parId < params.size(); parId++ ) { Symbol *sym = *(Symbol **) params.at( parId ); if (sym != 0 ) m_out->writeString( ".param " + sym->name() + "\n" ); } for ( uint32 locId = 0; locId < locals.size(); locId++ ) { Symbol *sym = *(Symbol **) locals.at( locId ); if (sym != 0 ) m_out->writeString( ".local " + sym->name() + "\n" ); } // generates INIT for constructors if ( ctorFor != 0 ) { ListElement *it_iter = ctorFor->initExpressions().begin(); while( it_iter != 0 ) { const Value *value = (const Value *) it_iter->data(); gen_value( value ); it_iter = it_iter->next(); } } const char *end_type = ret_mode; if ( func->statements().empty() ) { if ( func->staticBlock().empty() ) { m_out->writeString( end_type ); } else { m_out->writeString( "\tONCE\t _" + func->name() + "_once_end, $" + func->name() + "\n" ); gen_block( &func->staticBlock() ); m_out->writeString( "_" + func->name() + "_once_end:" + "\n" ); if( func->staticBlock().back()->type() != Statement::t_return ) { m_out->writeString( end_type ); } } } else { if ( ! func->staticBlock().empty() ) { m_out->writeString( "\tONCE\t _" + func->name() + "_once_end, $" + func->name() + "\n" ); gen_block( &func->staticBlock() ); // ok also if empty m_out->writeString( "_" + func->name() + "_once_end:" + "\n" ); } gen_block( &func->statements() ); if( func->statements().back()->type() != Statement::t_return ) { m_out->writeString( end_type ); } } m_out->writeString( ".endfunc\n\n" ); m_functions.popBack(); } void GenHAsm::gen_block( const StatementList *slist ) { const Statement *stmt = slist->front(); while( stmt != 0 ) { gen_statement( stmt ); stmt = static_cast(stmt->next()); } } void GenHAsm::gen_statement( const Statement *stmt ) { static uint32 last_line=0; if ( stmt->line() > 0 && last_line != stmt->line() ) { last_line = stmt->line(); String linestr; linestr.writeNumber( (int64) last_line ); m_out->writeString( ".line " + linestr + "\n" ); } switch( stmt->type() ) { case Statement::t_none: // this must be ignored. break; case Statement::t_break: case Statement::t_continue: { fassert( ! m_loops.empty() ); LoopInfo* loop = (LoopInfo*) m_loops.back(); int target_pos = loop->m_id; // there is a TRY statement above this one. If the TRY level is "inner" // ( try is inside the loop we are going to break ) we have to also pop // the TRY value while restarting the loop. If the TRY is outside the loop, // then everything stays the same. ListElement *trypos = m_trys.end(); int steps = 0; while( trypos != 0 ) { if ( ((int) trypos->iData()) >= target_pos ) steps ++; trypos = trypos->prev(); } String stepsStr; stepsStr.writeNumber( (int64) steps ); if ( steps > 0 ) m_out->writeString( "\tPTRY\t" + stepsStr + "\n" ); String loopStr; loopStr.writeNumber( (int64) target_pos ); if ( stmt->type() == Statement::t_continue ) { const StmtContinue *cont = static_cast( stmt ); // is a continue dropping? -- we must generate a TRDN opcode. if( cont->dropping() ) { fassert( loop->m_loop->type() == Statement::t_forin ); // when generating the last element, TRDN is simpler. if( loop->m_isForLast ) { m_out->writeString( "\tTRDN\t_loop_end_" + loopStr + ", 0, 0\n" ); } else { StmtForin* fin = (StmtForin* ) loop->m_loop; String svars; svars.writeNumber((int64) fin->dest()->size() ); m_out->writeString( "\tTRDN\t_loop_begin_" + loopStr + ", " + "_loop_end_" + loopStr + ", "+ svars +"\n" ); ListElement *it_t = fin->dest()->begin(); // first generates an array of references while( it_t != 0 ) { // again, is the compiler that must make sure of this... const Value *val = (const Value *) it_t->data(); fassert( val->isSimple() ); m_out->writeString( "\tNOP \t" ); gen_operand( val ); m_out->writeString( "\n" ); it_t = it_t->next(); } } } else { // when generating the last element, continue == break. if( loop->m_isForLast ) m_out->writeString( "\tJMP \t_loop_end_" + loopStr + "\n" ); else m_out->writeString( "\tJMP \t_loop_next_" + loopStr + "\n" ); } } else m_out->writeString( "\tJMP \t_loop_end_" + loopStr + "\n" ); } break; case Statement::t_launch: { const StmtLaunch *launch = static_cast( stmt ); const Value *call = launch->value(); fassert( call->isExpr() ); const Expression *expr = call->asExpr(); fassert( expr->type() == Expression::t_funcall ); gen_funcall( expr, true ); } break; case Statement::t_autoexp: { const StmtExpression *val = static_cast( stmt ); if ( ! val->value()->isSimple() ) { gen_complex_value( val->value() ); } } break; case Statement::t_return: { const StmtReturn *ret = static_cast( stmt ); if ( ret->value() == 0 ) { m_out->writeString( "\tRET \t" ); } else if ( ret->value()->isSimple() ) { m_out->writeString( "\tRETV\t" ); gen_operand( ret->value() ); } else { gen_complex_value( ret->value() ); m_out->writeString( "\tRETA\t" ); } m_out->writeString( "\n" ); } break; case Statement::t_raise: { const StmtRaise *op = static_cast< const StmtRaise *>( stmt ); if ( op->value()->isSimple() ) { m_out->writeString( "\tRIS \t" ); gen_operand( op->value() ); } else { gen_complex_value( op->value() ); m_out->writeString( "\tRIS \tA" ); } m_out->writeString( "\n" ); } break; case Statement::t_fordot: { const StmtFordot *op = static_cast< const StmtFordot *>( stmt ); if ( op->value()->isSimple() ) { m_out->writeString( "\tTRAC\t" ); gen_operand( op->value() ); } else { gen_complex_value( op->value() ); m_out->writeString( "\tTRAC\tA" ); } m_out->writeString( "\n" ); } break; case Statement::t_global: { // ignore the statement. } break; case Statement::t_self_print: { const StmtSelfPrint *sp = static_cast( stmt ); const ArrayDecl *attribs = sp->toPrint(); ListElement *iter = attribs->begin(); while( iter != 0 ) { const Value *val = (const Value *) iter->data(); if ( val->isSimple() ) { m_out->writeString( "\tWRT \t" ); gen_operand( val ); } else { gen_complex_value( val ); m_out->writeString( "\tWRT \tA" ); } m_out->writeString( "\n" ); iter = iter->next(); } } break; case Statement::t_unref: { const StmtUnref *ass = static_cast(stmt); if( ass->symbol()->isSimple() ) { m_out->writeString( "\tLDRF\t" ); gen_operand( ass->symbol() ); m_out->writeString( ", 0\n" ); } else { gen_complex_value( ass->symbol() ); m_out->writeString( "\tLDRF\tA, 0\n" ); } } break; case Statement::t_if: { const StmtIf *elem = static_cast( stmt ); if ( elem->children().empty() && elem->elifChildren().empty() && elem->elseChildren().empty() ) { if ( ! elem->condition()->isSimple() ) gen_complex_value( elem->condition() ); // generate & discard value break; // nothing more needed } int branch = m_branch_id++; String branchStr; branchStr.writeNumber( (int64) branch ); m_branches.pushBack( (void *) branch ); gen_condition( elem->condition() ); if( ! elem->children().empty() ) { gen_block( &elem->children() ); // do we need to jump away? } if( ! (elem->elifChildren().empty() && elem->elseChildren().empty() ) ) { m_out->writeString( "\tJMP \t_branch_end_" + branchStr + "\n" ); } m_out->writeString( "_branch_fail_" + branchStr + ":\n" ); if ( ! elem->elifChildren().empty() ) { const StmtElif *selif = static_cast(elem->elifChildren().front()); int elifcount = 1; while( selif != 0 ) { gen_condition( selif->condition(), elifcount ); if( ! selif->children().empty() ) { gen_block( &selif->children() ); // do we need to jump away? if( selif->next() != 0 || ! elem->elseChildren().empty() ) { m_out->writeString( "\tJMP \t_branch_end_" + branchStr + "\n" ); } String elifStr; elifStr.writeNumber( (int64) elifcount ); m_out->writeString( "_branch_" + branchStr + "_elif_"); m_out->writeString( elifStr + ":\n" ); } elifcount++; selif = static_cast(selif->next()); } } if ( ! elem->elseChildren().empty() ) { gen_block( &elem->elseChildren() ); } // have we any block to jump across? m_out->writeString( "_branch_end_" + branchStr + ":\n" ); m_branches.popBack(); } break; case Statement::t_switch: case Statement::t_select: { const StmtSwitch *elem = static_cast( stmt ); // just check for the item to be not empty if ( elem->intCases().empty() && elem->rngCases().empty() && elem->strCases().empty() && elem->objCases().empty() && elem->defaultBlock().empty() && elem->nilBlock() == -1 ) { if ( ! elem->switchItem()->isSimple() ) gen_complex_value( elem->switchItem() ); break; } const char *oper = stmt->type() == Statement::t_switch ? ".switch " : ".select "; if ( elem->switchItem()->isSimple() ) { m_out->writeString( oper ); gen_operand( elem->switchItem() ); } else { gen_complex_value( elem->switchItem() ); m_out->writeString( oper ); m_out->writeString( "A" ); } int branch = m_branch_id++; String branchStr; branchStr.writeNumber( (int64) branch ); if ( elem->defaultBlock().empty() ) m_out->writeString( ", _switch_" + branchStr + "_end" ); else m_out->writeString( ", _switch_" + branchStr + "_default" ); m_out->writeString( "\n" ); if ( elem->nilBlock() != -1 ) { String caseStr; caseStr.writeNumber( (int64) elem->nilBlock() ); m_out->writeString( ".case NIL, _switch_" + branchStr + "_case_"); m_out->writeString( caseStr + "\n" ); } dump_cases( branch, elem->intCases().begin() ); dump_cases( branch, elem->rngCases().begin() ); dump_cases( branch, elem->strCases().begin() ); // we must put the objects in the same order they were declared. ListElement *iter = elem->objList().begin(); while( iter != 0 ) { Value *val = (Value *) iter->data(); MapIterator case_iter; if( elem->objCases().find( val, case_iter ) ) { String caseStr; int64 cn = (int64) *(uint32*) case_iter.currentValue(); caseStr.writeNumber( cn ); m_out->writeString( ".case " ); gen_operand( val ); m_out->writeString( ", _switch_" + branchStr + "_case_" ); m_out->writeString( caseStr + "\n" ); } iter = iter->next(); } m_out->writeString( ".endswitch\n\n" ); // generate the code blocks stmt = elem->blocks().front(); int id = 0; while( stmt != 0 ) { const StmtCaseBlock *elem_case = static_cast( stmt ); // skip default block. String idStr; idStr.writeNumber( (int64) id ); m_out->writeString( "_switch_" + branchStr + "_case_" ); m_out->writeString( idStr + ":\n" ); gen_block( &elem_case->children() ); // jump away, but only if needed. if ( elem_case->next() != 0 || ! elem->defaultBlock().empty() ) m_out->writeString( "\tJMP \t_switch_" + branchStr + "_end\n" ); id++; stmt = static_cast(elem_case->next()); } if ( ! elem->defaultBlock().empty() ) { m_out->writeString( "_switch_" + branchStr + "_default:\n" ); gen_block( &elem->defaultBlock() ); } m_out->writeString( "_switch_" + branchStr + "_end:\n" ); } break; case Statement::t_while: { const StmtWhile *elem = static_cast( stmt ); int branch = m_loop_id++; LoopInfo loop( branch, elem ); m_loops.pushBack( (void *) &loop ); String branchStr; branchStr.writeNumber( (int64) branch ); m_out->writeString( "_loop_next_" + branchStr + ":\n" ); m_out->writeString( "_loop_begin_" + branchStr + ":\n" ); // Generate the condition only if present and not always true. if ( elem->condition() != 0 && ! elem->condition()->isTrue() ) { if ( elem->condition()->isSimple() ) { m_out->writeString( "\tIFF \t_loop_end_" + branchStr + ", " ); gen_operand( elem->condition() ); } else { gen_complex_value( elem->condition() ); m_out->writeString( "\tIFF \t_loop_end_" + branchStr + ", A" ); } m_out->writeString( "\n" ); } gen_block( &elem->children() ); m_out->writeString( "\tJMP \t_loop_begin_" + branchStr + "\n" ); m_out->writeString( "_loop_end_" + branchStr + ":\n" ); m_loops.popBack(); } break; case Statement::t_loop: { const StmtLoop *elem = static_cast( stmt ); int branch = m_loop_id++; LoopInfo loop( branch, elem ); m_loops.pushBack( (void *) &loop ); String branchStr; branchStr.writeNumber( (int64) branch ); m_out->writeString( "_loop_begin_" + branchStr + ":\n" ); if ( elem->condition() == 0 ) m_out->writeString( "_loop_next_" + branchStr + ":\n" ); gen_block( &elem->children() ); if ( elem->condition() == 0 ) { // endless loop m_out->writeString( "\tJMP \t_loop_begin_" + branchStr + "\n" ); } else { m_out->writeString( "_loop_next_" + branchStr + ":\n" ); if ( ! elem->condition()->isTrue() ) { if ( elem->condition()->isSimple() ) { m_out->writeString( "\tIFF \t_loop_begin_" + branchStr + ", " ); gen_operand( elem->condition() ); } else { gen_complex_value( elem->condition() ); m_out->writeString( "\tIFF \t_loop_begin_" + branchStr + ", A" ); } m_out->writeString( "\n" ); } } // if it's true, terminate immediately m_out->writeString( "_loop_end_" + branchStr + ":\n" ); m_loops.popBack(); } break; case Statement::t_propdef: { const StmtVarDef *pdef = static_cast( stmt ); if ( pdef->value()->isSimple() ) { m_out->writeString( "\tSTP \tS1, \"" + *pdef->name() + "\", " ); gen_operand( pdef->value() ); } else { gen_value( pdef->value() ); m_out->writeString( "\tSTP \tS1, \"" + *pdef->name() + "\", A" ); } m_out->writeString( "\n" ); } break; case Statement::t_forin: { const StmtForin *loop = static_cast( stmt ); int loopId = m_loop_id++; LoopInfo li( loopId, loop ); m_loops.pushBack( (void *) &li ); String loopStr; loopStr.writeNumber( (int64) loopId ); String snv; snv.writeNumber( (int64) loop->dest()->size() ); if ( loop->source()->isSimple() ) { m_out->writeString( "\tTRAV\t_p_loop_end_" + loopStr + ", " ); m_out->writeString( snv + ", " ); gen_operand( loop->source() ); m_out->writeString( "\n" ); } else { gen_value( loop->source() ); m_out->writeString( "\tTRAV\t_p_loop_end_" + loopStr + ", " ); m_out->writeString( snv + ", A\n" ); } ListElement* it_t = loop->dest()->begin(); // first generates an array of references while( it_t != 0 ) { // again, is the compiler that must make sure of this... const Value *val = (const Value *) it_t->data(); fassert( val->isSimple() ); m_out->writeString( "\tNOP \t" ); gen_operand( val ); m_out->writeString( "\n" ); it_t = it_t->next(); } // have we got a "first" block? if ( ! loop->firstBlock().empty() ) { gen_block( &loop->firstBlock() ); } // begin of the main loop; m_out->writeString( "_loop_begin_" + loopStr + ":\n" ); if( ! loop->children().empty() ) { gen_block( &loop->children() ); } // generate the middle block if( ! loop->middleBlock().empty() ) { // skip it for the last element m_out->writeString( "\tTRAL\t_loop_tral_" + loopStr + "\n" ); gen_block( &loop->middleBlock() ); } m_out->writeString( "_loop_next_" + loopStr + ":\n" ); m_out->writeString( "\tTRAN\t_loop_begin_" + loopStr + ", "+ snv +",\n"); it_t = loop->dest()->begin(); // first generates an array of references while( it_t != 0 ) { // again, is the compiler that must make sure of this... const Value *val = (const Value *) it_t->data(); fassert( val->isSimple() ); m_out->writeString( "\tNOP \t" ); gen_operand( val ); m_out->writeString( "\n" ); it_t = it_t->next(); } // generate the last block m_out->writeString( "_loop_tral_" + loopStr + ":\n" ); if( ! loop->lastBlock().empty() ) { // and the last time... li.m_isForLast = true; gen_block( &loop->lastBlock() ); } // create break landing code: m_out->writeString( "_loop_end_" + loopStr + ":\n" ); m_out->writeString( "\tIPOP\t1\n" ); // internal loop out used by TRAV, TRAN and TRAL m_out->writeString( "_p_loop_end_" + loopStr + ":\n" ); m_loops.popBack(); } break; case Statement::t_try: { const StmtTry *op = static_cast< const StmtTry *>( stmt ); // if the try block is empty we have nothing to do if( op->children().empty() ) break; // push the LOOP id that may cause a TRY breaking. if ( m_loops.empty() ) m_trys.pushBack( (void *) -1 ); else m_trys.pushBack( ((LoopInfo*) m_loops.back())->m_id ); int branch = m_try_id++; String branchStr; branchStr.writeNumber( (int64) branch ); // as TRY does not have a condition, and everyone is on // its own, we don't have to push it as a branch. m_out->writeString( "\tTRY \t_branch_try_" + branchStr + "\n" ); // MUST maintain the current branch level to allow inner breaks. gen_block( &op->children() ); // When the catcher is generated, the TRY cannot be broken anymore // by loop controls, as the catcher pops the TRY context from the VM m_trys.popBack(); // now generate the catch blocks // if we have no default nor specific blocs, we're done if ( op->handlers().empty() && ! op->defaultGiven() ) { m_out->writeString( "\tPTRY \t1\n" ); m_out->writeString( "_branch_try_" + branchStr + ":\n" ); break; } // if we have only the default block, we don't have a to generate a select. if ( op->handlers().empty() ) { m_out->writeString( "\tJTRY \t_branch_try_end_" + branchStr + "\n" ); m_out->writeString( "_branch_try_" + branchStr + ":\n" ); if ( op->defaultHandler()->intoValue() != 0 ) gen_load_from_reg( op->defaultHandler()->intoValue(), "B" ); gen_block( &op->defaultHandler()->children() ); m_out->writeString( "_branch_try_end_" + branchStr + ":\n" ); break; } // great, we have to create a select B statement. m_out->writeString( "\tJTRY \t_branch_try_end_" + branchStr + "\n" ); m_out->writeString( "_branch_try_" + branchStr + ":\n" ); m_out->writeString( ".select B" ); int branch_select = m_branch_id++; String branch_selectStr; branch_selectStr.writeNumber( (int64) branch_select ); if ( op->defaultHandler() == 0 ) m_out->writeString( ", _switch_" + branch_selectStr + "_end" ); else m_out->writeString( ", _switch_" + branch_selectStr + "_default" ); m_out->writeString( "\n" ); dump_cases( branch_select, op->intCases().begin() ); // we must put the objects in the same order they were declared. ListElement *iter = op->objList().begin(); while( iter != 0 ) { Value *val = (Value *) iter->data(); MapIterator case_iter; if( op->objCases().find( val, case_iter ) ) { String caseStr; int64 cn = (int64) *(uint32*) case_iter.currentValue(); caseStr.writeNumber( cn ); m_out->writeString( ".case " ); gen_operand( val ); m_out->writeString( ", _switch_" + branch_selectStr + "_case_" ); m_out->writeString( caseStr + "\n" ); } iter = iter->next(); } m_out->writeString( ".endswitch\n\n" ); // generate the code blocks stmt = op->handlers().front(); int id = 0; while( stmt != 0 ) { const StmtCatchBlock *elem_catch = static_cast( stmt ); // skip default block. String idStr; idStr.writeNumber( (int64) id ); m_out->writeString( "_switch_" + branch_selectStr + "_case_" ); m_out->writeString( idStr + ":\n" ); if ( elem_catch->intoValue() != 0 ) gen_load_from_reg( elem_catch->intoValue(), "B" ); gen_block( &elem_catch->children() ); // jump away, but only if needed. if ( elem_catch->next() != 0 || op->defaultHandler() != 0 ) m_out->writeString( "\tJMP \t_switch_" + branch_selectStr + "_end\n" ); id++; stmt = static_cast(elem_catch->next()); } if ( op->defaultHandler() != 0 ) { m_out->writeString( "_switch_" + branch_selectStr + "_default:\n" ); if ( op->defaultHandler() ->intoValue() != 0 ) gen_load_from_reg( op->defaultHandler()->intoValue(), "B" ); gen_block( &op->defaultHandler()->children() ); } m_out->writeString( "_switch_" + branch_selectStr + "_end:\n" ); m_out->writeString( "_branch_try_end_" + branchStr + ":\n" ); } break; default: break; } } void GenHAsm::dump_cases( int branch, const MapIterator &begin1 ) { MapIterator begin = begin1; while( begin.hasCurrent() ) { Value *val = *(Value **) begin.currentKey(); uint32 id = *(uint32 *) begin.currentValue(); m_out->writeString( ".case " ); if ( val->isRange() ) { String start, end; start.writeNumber( val->asRange()->rangeStart()->asInteger() ); end.writeNumber( val->asRange()->rangeEnd()->asInteger() ); m_out->writeString( start + ":" + end ); } else gen_operand( val ); String branchStr, idStr; branchStr.writeNumber( (int64) branch ); idStr.writeNumber( (int64) id ); m_out->writeString( ", _switch_" + branchStr + "_case_" ); m_out->writeString( idStr + "\n" ); begin.next(); } } void GenHAsm::gen_inc_prefix( const Value *val ) { if ( val->isSimple() ) { m_out->writeString( "\tINC \t" ); gen_operand( val ); m_out->writeString( "\n" ); } else { t_valType xValue = l_value; gen_complex_value( val, xValue ); m_out->writeString( "\tINC \tA\n" ); if ( xValue == p_value ) m_out->writeString( "\tSTP \tL1, L2, A\n" ); else if ( xValue == v_value ) m_out->writeString( "\tSTV \tL1, L2, A\n" ); } } void GenHAsm::gen_inc_postfix( const Value *val ) { if ( val->isSimple() ) { m_out->writeString( "\tINCP\t" ); gen_operand( val ); m_out->writeString( "\n" ); } else { t_valType xValue = l_value; gen_complex_value( val, xValue ); m_out->writeString( "\tINCP\tA\n" ); if ( xValue == p_value ) m_out->writeString( "\tSTP \tL1, L2, B\n" ); else if ( xValue == v_value ) m_out->writeString( "\tSTV \tL1, L2, B\n" ); } } void GenHAsm::gen_dec_prefix( const Value *val ) { if ( val->isSimple() ) { m_out->writeString( "\tDEC \t" ); gen_operand( val ); m_out->writeString( "\n" ); } else { t_valType xValue = l_value; gen_complex_value( val, xValue ); m_out->writeString( "\tDEC \tA\n" ); if ( xValue == p_value ) m_out->writeString( "\tSTP \tL1, L2, A\n" ); else if ( xValue == v_value ) m_out->writeString( "\tSTV \tL1, L2, A\n" ); } } void GenHAsm::gen_dec_postfix( const Value *val ) { if ( val->isSimple() ) { m_out->writeString( "\tDECP\t" ); gen_operand( val ); m_out->writeString( "\n" ); } else { t_valType xValue = l_value; gen_complex_value( val, xValue ); m_out->writeString( "\tDECP\tA\n" ); if ( xValue == p_value ) m_out->writeString( "\tSTP \tL1, L2, B\n" ); else if ( xValue == v_value ) m_out->writeString( "\tSTV \tL1, L2, B\n" ); } } void GenHAsm::gen_autoassign( const char *op, const Value *target, const Value *source ) { String opstr = op; if( target->isSimple() && source->isSimple() ) { m_out->writeString( "\t" + opstr + "\t" ); gen_operand( target ); m_out->writeString( ", " ); gen_operand( source ); m_out->writeString( "\n" ); } else if ( target->isSimple() ) { gen_complex_value( source ); m_out->writeString( "\t" + opstr + "\t" ); gen_operand( target ); m_out->writeString( ", A\n" ); } else if ( source->isSimple() ) { t_valType xValue = l_value; gen_complex_value( target, xValue ); m_out->writeString( "\t" + opstr + "\tA, " ); gen_operand( source ); m_out->writeString( "\n" ); if ( xValue == p_value ) m_out->writeString( "\tSTP \tL1, L2, A\n" ); else if ( xValue == v_value ) m_out->writeString( "\tSTV \tL1, L2, A\n" ); } else { gen_complex_value( source ); m_out->writeString( "\tPUSH\tA\n" ); t_valType xValue = l_value; gen_complex_value( target, xValue ); m_out->writeString( "\tPOP \tB\n" ); m_out->writeString( "\t" + opstr + "\tA, B\n" ); if ( xValue == p_value ) m_out->writeString( "\tSTP \tL1, L2, A\n" ); else if ( xValue == v_value ) m_out->writeString( "\tSTV \tL1, L2, A\n" ); } } void GenHAsm::gen_condition( const Value *stmt, int mode ) { if ( !stmt->isSimple() ) { gen_complex_value( stmt ); } String branchStr; branchStr.writeNumber( (int64) m_branches.back() ); m_out->writeString( "\tIFF \t" ); if ( mode > 0 ) { String modeStr; modeStr.writeNumber( (int64) mode ); m_out->writeString( "_branch_" + branchStr + "_elif_" + modeStr ); } else m_out->writeString( "_branch_fail_" + branchStr ); if ( stmt->isSimple() ) { m_out->writeString( ", " ); gen_operand( stmt ); } else { m_out->writeString( ", A" ); } m_out->writeString( "\n" ); } void GenHAsm::gen_value( const Value *stmt, const char *prefix, const char *cpl_post ) { if ( prefix == 0 ) prefix = "\tLD \tA, "; if ( stmt->isSimple() ) { m_out->writeString( prefix ); gen_operand( stmt ); } else { gen_complex_value( stmt ); if ( cpl_post != 0 ) m_out->writeString( cpl_post ); m_out->writeString( "\n" ); } } void GenHAsm::gen_complex_value( const Value *stmt, t_valType &xValue ) { switch( stmt->type() ) { // catch also reference taking in case it's not filtered before // this happens when we have not a specific opcode to handle references case Value::t_byref: if ( stmt->asReference()->isSymbol() ) m_out->writeString( "\tLDRF\tA, $" + stmt->asReference()->asSymbol()->name() + "\n" ); else { gen_value( stmt->asReference() ); // won't do a lot, but we need it m_out->writeString( "\tLDRF\tA, A\n" ); } break; case Value::t_array_decl: gen_array_decl( stmt->asArray() ); break; case Value::t_dict_decl: gen_dict_decl( stmt->asDict() ); break; case Value::t_expression: gen_expression( stmt->asExpr(), xValue ); break; case Value::t_range_decl: gen_range_decl( stmt->asRange() ); break; default: m_out->writeString( "; can't generate this\n" ); } } void GenHAsm::gen_push( const Value *val ) { if( val->isSimple() ) { m_out->writeString( "\tPUSH\t" ); gen_operand( val ); } else if ( val->isReference() ) { if( val->asReference()->isSymbol() ) m_out->writeString( "\tPSHR\t$" + val->asReference()->asSymbol()->name() ); else { gen_value( val->asReference() ); m_out->writeString( "\tPSHR\tA" ); } } else { gen_complex_value( val ); m_out->writeString( "\tPUSH\tA" ); } m_out->writeString( "\n" ); } void GenHAsm::gen_operand( const Value *stmt ) { switch( stmt->type() ) { case Value::t_nil: m_out->writeString( "NIL" ); break; case Value::t_unbound: m_out->writeString( "UNB" ); break; case Value::t_symbol: { const Symbol *sym = stmt->asSymbol(); m_out->writeString( "$" ); if ( ! m_functions.empty() && sym->isGlobal() ) m_out->writeString( "*" ); m_out->writeString( sym->name() ); } break; case Value::t_lbind: m_out->writeString( "&"+ *stmt->asLBind() ); break; case Value::t_imm_bool: m_out->writeString( stmt->asBool() ? "T" : "F" ); break; case Value::t_imm_integer: { String intStr; intStr.writeNumber( stmt->asInteger() ); m_out->writeString( intStr ); } break; case Value::t_imm_num: { String numStr; numStr.writeNumber( stmt->asNumeric(), "%.12g" ); m_out->writeString( numStr ); } break; case Value::t_imm_string: { String temp; stmt->asString()->escape( temp ); m_out->writeString( "\"" + temp + "\"" ); } break; case Value::t_self: m_out->writeString( "S1" ); break; case Value::t_fself: m_out->writeString( "FSELF" ); break; default: m_out->writeString( "???" ); } } void GenHAsm::gen_expression( const Expression *exp, t_valType &xValue ) { String opname; int mode = 0; // 1 = unary, 2 = binary // first, deterime the operator name and operation type switch( exp->type() ) { case Expression::t_none: return; // optimized away operations case Expression::t_optimized: gen_value( exp->first() ); // nothing else needed, going out return; // logical connectors & shortcuts case Expression::t_and: case Expression::t_or: { xValue = l_value; String opname, ifmode; if ( exp->type() == Expression::t_or ) { opname = "OR "; ifmode = "IFT "; } else { opname = "AND "; ifmode = "IFF "; } if( exp->first()->isSimple() && exp->second()->isSimple() ) { m_out->writeString( "\t" + opname ); gen_operand( exp->first() ); m_out->writeString( ", " ); gen_operand( exp->second() ); m_out->writeString( "\n" ); } else if( exp->first()->isSimple() ) { int branch = m_branch_id++; String branchStr; branchStr.writeNumber( (int64) branch ); m_out->writeString( "\tSTO \tA, " ); gen_operand( exp->first() ); m_out->writeString( "\n" ); m_out->writeString( "\t" + ifmode + "\t_branch_orand_" ); m_out->writeString( branchStr + ", A\n" ); // if A that is the first op is false, we generate this one. gen_value( exp->second() ); m_out->writeString( "_branch_orand_" + branchStr + ":\n" ); } else if( exp->second()->isSimple() ) { int branch = m_branch_id++; String branchStr; branchStr.writeNumber( (int64) branch ); gen_value( exp->first() ); m_out->writeString( "\t" + ifmode + "\t_branch_orand_" + branchStr + ", A" + "\n" ); // else we have to load the second in A m_out->writeString( "\tSTO \tA, " ); gen_operand( exp->second() ); m_out->writeString( "\n" ); m_out->writeString( "_branch_orand_" + branchStr + ":\n" ); } else { int branch = m_branch_id++; String branchStr; branchStr.writeNumber( (int64) branch ); gen_value( exp->first() ); m_out->writeString( "\t" + ifmode + "\t_branch_orand_"); m_out->writeString( branchStr + ", A\n" ); // else we have to load the second in A gen_value( exp->second() ); m_out->writeString( "_branch_orand_" + branchStr + ":\n" ); } } return; // unary operators case Expression::t_neg: mode = 1; opname = "NEG "; break; case Expression::t_not: mode = 1; opname = "NOT "; break; case Expression::t_bin_not: mode = 1; opname = "BNOT"; break; case Expression::t_strexpand: mode = 1; opname = "STEX"; break; case Expression::t_indirect: mode = 1; opname = "INDI"; break; case Expression::t_eval: mode = 1; opname = "EVAL"; break; case Expression::t_bin_and: mode = 2; opname = "BAND"; break; case Expression::t_bin_or: mode = 2; opname = "BOR "; break; case Expression::t_bin_xor: mode = 2; opname = "BXOR"; break; case Expression::t_shift_left: mode = 2; opname = "SHL "; break; case Expression::t_shift_right: mode = 2; opname = "SHR "; break; case Expression::t_plus: mode = 2; opname = "ADD "; break; case Expression::t_minus: mode = 2; opname = "SUB "; break; case Expression::t_times: mode = 2; opname = "MUL "; break; case Expression::t_divide: mode = 2;opname = "DIV "; break; case Expression::t_modulo: mode = 2; opname = "MOD "; break; case Expression::t_power: mode = 2; opname = "POW "; break; case Expression::t_gt: mode = 2; opname = "GT "; break; case Expression::t_ge: mode = 2; opname = "GE "; break; case Expression::t_lt: mode = 2; opname = "LT "; break; case Expression::t_le: mode = 2; opname = "LE "; break; case Expression::t_eq: mode = 2; opname = "EQ "; break; case Expression::t_exeq: mode = 2; opname = "EXEQ"; break; case Expression::t_neq: mode = 2; opname = "NEQ "; break; case Expression::t_has: mode = 2; opname = "HAS "; break; case Expression::t_hasnt: mode = 2; opname = "HASN"; break; case Expression::t_in: mode = 2; opname = "IN "; break; case Expression::t_notin: mode = 2; opname = "NOIN"; break; case Expression::t_provides: mode = 2; opname = "PROV"; break; // it is better to handle the rest directly here. case Expression::t_iif: { xValue = l_value; int32 branch = m_branch_id++; String branchStr; branchStr.writeNumber( (int64) branch ); // condition if ( exp->first()->isSimple() ) { m_out->writeString( "\tIFF \t_iif_fail_" + branchStr + ", " ); gen_operand( exp->first() ); m_out->writeString( "\n" ); } else { gen_value( exp->first() ); m_out->writeString( "\tIFF \t_iif_fail_" + branchStr + ", A\n" ); } // success case if( exp->second()->isSimple() ) { m_out->writeString( "\tSTO \tA, " ); gen_operand( exp->second() ); m_out->writeString( "\n" ); } else { gen_value( exp->second() ); } m_out->writeString( "\tJMP \t_iif_end_" + branchStr + "\n" ); m_out->writeString( "_iif_fail_" + branchStr + ":\n" ); //failure case // success case if( exp->third()->isSimple() ) { m_out->writeString( "\tSTO \tA, " ); gen_operand( exp->third() ); m_out->writeString( "\n" ); } else { gen_value( exp->third() ); } m_out->writeString( "_iif_end_" + branchStr + ":\n" ); } return; case Expression::t_fbind: mode = 2; opname = "FORB"; break; case Expression::t_assign: // handle it as a load... // don't change x-value gen_load( exp->first(), exp->second() ); return; case Expression::t_aadd: xValue = l_value; gen_autoassign( "ADDS", exp->first(), exp->second() ); return; case Expression::t_asub: xValue = l_value; gen_autoassign( "SUBS", exp->first(), exp->second() ); return; case Expression::t_amul: xValue = l_value; gen_autoassign( "MULS", exp->first(), exp->second() ); return; case Expression::t_adiv: xValue = l_value; gen_autoassign( "DIVS", exp->first(), exp->second() ); return; case Expression::t_amod: xValue = l_value; gen_autoassign( "MODS", exp->first(), exp->second() ); return; case Expression::t_apow: xValue = l_value; gen_autoassign( "POWS", exp->first(), exp->second() ); return; case Expression::t_aband: xValue = l_value; gen_autoassign( "ANDS", exp->first(), exp->second() ); return; case Expression::t_abor: xValue = l_value; gen_autoassign( "ORS", exp->first(), exp->second() ); return; case Expression::t_abxor: xValue = l_value; gen_autoassign( "XORS", exp->first(), exp->second() ); return; case Expression::t_ashl: xValue = l_value; gen_autoassign( "SHLS", exp->first(), exp->second() ); return; case Expression::t_ashr: xValue = l_value; gen_autoassign( "SHRS", exp->first(), exp->second() ); return; case Expression::t_pre_inc: xValue = l_value; gen_inc_prefix( exp->first() ); return; case Expression::t_pre_dec: xValue = l_value; gen_dec_prefix( exp->first() ); return; case Expression::t_post_inc: xValue = l_value; gen_inc_postfix( exp->first() ); return; case Expression::t_post_dec: xValue = l_value; gen_dec_postfix( exp->first() ); return; case Expression::t_obj_access: xValue = p_value; gen_load_from_deep( "LDP ", exp->first(), exp->second() ); return; case Expression::t_array_access: xValue = v_value; gen_load_from_deep( "LDV ", exp->first(), exp->second() ); return; case Expression::t_array_byte_access: xValue = l_value; gen_load_from_deep( "LSB ", exp->first(), exp->second() ); return; case Expression::t_funcall: case Expression::t_inherit: { xValue = l_value; gen_funcall( exp, false ); } // funcall is complete here return; case Expression::t_lambda: { xValue = l_value; if( exp->second() != 0 ) { // we must create the lambda closure int size = 0; ListElement *iter = exp->second()->asArray()->begin(); while( iter != 0 ) { const Value *val = (Value *) iter->data(); if( val->isSimple() ) { m_out->writeString( "\tPSHR\t" ); gen_operand( val ); } else { gen_complex_value( val ); m_out->writeString( "\tPSHR\tA" ); } m_out->writeString( "\n" ); size++; iter = iter->next(); } String temp; temp.writeNumber((int64) size); m_out->writeString( "\tCLOS\t" + temp + ", A, $" + exp->first()->asSymbol()->name() + "\n" ); } else { m_out->writeString( "\tSTO \tA, $" + exp->first()->asSymbol()->name() + "\n" ); } } return; case Expression::t_oob: xValue = l_value; m_out->writeString( "\tOOB \t1, " ); gen_operand( exp->first() ); m_out->writeString( "\n" ); return; case Expression::t_deoob: xValue = l_value; m_out->writeString( "\tOOB \t0, " ); gen_operand( exp->first() ); m_out->writeString( "\n" ); return; case Expression::t_xoroob: xValue = l_value; m_out->writeString( "\tOOB \t2, " ); gen_operand( exp->first() ); m_out->writeString( "\n" ); return; case Expression::t_isoob: xValue = l_value; m_out->writeString( "\tOOB \t3, " ); gen_operand( exp->first() ); m_out->writeString( "\n" ); return; } // post-processing unary and binary operators. // ++, -- and accessors are gone; safely change the l-value status. xValue = l_value; // then, if there is still something to do, put the operands in place. if ( mode == 1 ) { // unary? if ( exp->first()->isSimple() ) { m_out->writeString( "\t" + opname + "\t" ); gen_operand( exp->first() ); } else { gen_complex_value( exp->first() ); m_out->writeString( "\t" + opname + "\tA" ); } } else { if ( exp->first()->isSimple() && exp->second()->isSimple() ) { m_out->writeString( "\t" + opname + "\t" ); gen_operand( exp->first() ); m_out->writeString( ", " ); gen_operand( exp->second() ); } else if ( exp->first()->isSimple() ) { gen_complex_value( exp->second() ); m_out->writeString( "\t" + opname + "\t" ); gen_operand( exp->first() ); m_out->writeString( ", A" ); } else if ( exp->second()->isSimple() ) { gen_complex_value( exp->first() ); m_out->writeString( "\t" + opname + "\tA, " ); gen_operand( exp->second() ); } else { gen_complex_value( exp->first() ); m_out->writeString( "\tPUSH\tA\n" ); gen_value( exp->second() ); m_out->writeString( "\tPOP \tB\n" ); m_out->writeString( "\t" + opname + "\tB, A" ); } } m_out->writeString( "\n" ); } void GenHAsm::gen_dict_decl( const DictDecl *dcl ) { int size = 0; ListElement *iter = dcl->begin(); while( iter != 0 ) { DictDecl::pair *pair = (DictDecl::pair *) iter->data(); const Value *key = pair->first; const Value *value = pair->second; gen_push( key ); gen_push( value ); size++; iter = iter->next(); } String sizeStr; sizeStr.writeNumber( (int64) size ); m_out->writeString( "\tGEND\t" + sizeStr + "\n" ); } void GenHAsm::gen_array_decl( const ArrayDecl *dcl ) { int size = 0; ListElement *iter = dcl->begin(); while( iter != 0 ) { const Value *val = (const Value *) iter->data(); gen_push( val ); size++; iter = iter->next(); } String sizeStr; sizeStr.writeNumber( (int64) size ); m_out->writeString( "\tGENA\t" + sizeStr + "\n" ); } void GenHAsm::gen_range_decl( const RangeDecl *dcl ) { if ( dcl->isOpen() ) { if ( dcl->rangeStart()->isSimple() ) { m_out->writeString( "\tGEOR\t" ); gen_operand( dcl->rangeStart() ); m_out->writeString( "\n" ); } else { gen_complex_value( dcl->rangeStart() ); m_out->writeString( "\tGEOR\tA\n" ); } } else { Value dummy; // defaults to nil Value *rangeStep; if ( dcl->rangeStep() == 0 ) { dummy.setInteger( 0 ); rangeStep = &dummy; } else if ( dcl->rangeStep()->isSimple() ) { rangeStep = dcl->rangeStep(); } else { gen_complex_value( dcl->rangeStep() ); m_out->writeString( "\tPUSH\tA\n" ); // we'll instruct GENR to get it via NIL as parameter rangeStep = &dummy; } if ( dcl->rangeStart()->isSimple() && dcl->rangeEnd()->isSimple() ) { m_out->writeString( "\tGENR\t" ); gen_operand( dcl->rangeStart() ); m_out->writeString( ", " ); gen_operand( dcl->rangeEnd() ); m_out->writeString( ", " ); gen_operand( rangeStep ); m_out->writeString( "\n" ); } else if ( dcl->rangeStart()->isSimple() ) { gen_complex_value( dcl->rangeEnd() ); m_out->writeString( "\tGENR\t" ); gen_operand( dcl->rangeStart() ); m_out->writeString( ", A, " ); gen_operand( rangeStep ); m_out->writeString( "\n" ); } else if ( dcl->rangeEnd()->isSimple() ) { gen_complex_value( dcl->rangeStart() ); m_out->writeString( "\tGENR\tA, " ); gen_operand( dcl->rangeEnd() ); m_out->writeString( ", " ); gen_operand( rangeStep ); m_out->writeString( "\n" ); } else { gen_complex_value( dcl->rangeStart() ); m_out->writeString( "\tPUSH\tA\n" ); gen_complex_value( dcl->rangeEnd() ); m_out->writeString( "\tPOP \tB\n" ); m_out->writeString( "\tGENR\tB, A, "); gen_operand( rangeStep ); m_out->writeString( "\n" ); } } } void GenHAsm::gen_load( const Value *target, const Value *source ) { if ( target->isSimple() && source->isSimple() ) { m_out->writeString( "\tLD \t" ); gen_operand( target ); m_out->writeString( ", " ); gen_operand( source ); m_out->writeString( "\n" ); } else if ( target->isSimple() ) { if( source->isReference() ) { if ( source->asReference() == 0 ) { m_out->writeString( "\tLDRF\t" ); gen_operand( target ); m_out->writeString( ", 0\n" ); } else { if( source->asReference()->isSymbol() ) { m_out->writeString( "\tLDRF\t" ); gen_operand( target ); m_out->writeString( ", $" + source->asReference()->asSymbol()->name() + "\n" ); } else { gen_value( source->asReference() ); m_out->writeString( "\tLDRF\t" ); gen_operand( target ); m_out->writeString( ", A\n" ); } } } else { gen_complex_value( source ); m_out->writeString( "\tLD \t" ); gen_operand( target ); m_out->writeString( ", A\n" ); } } else { // target is NOT simple. If it's an expression ... if( target->type() == Value::t_expression ) { const Expression *exp = target->asExpr(); // ... then it may be an array assignment... if( exp->type() == Expression::t_array_access ) { gen_store_to_deep( "STV", exp->first(), exp->second(), source ); } else if ( exp->type() == Expression::t_obj_access ) { gen_store_to_deep( "STP", exp->first(), exp->second(), source ); } } else if ( target->type() == Value::t_array_decl ) { const ArrayDecl *tarr = target->asArray(); // if the source is also an array, fine, we have a 1:1 assignment. if ( source->type() == Value::t_array_decl ) { const ArrayDecl *sarr = source->asArray(); ListElement *it_s = sarr->begin(); ListElement *it_t = tarr->begin(); while( it_s != 0 && it_t != 0 ) { const Value *t = (const Value *) it_t->data(); const Value *s = (const Value *) it_s->data(); gen_load( t, s ); it_s = it_s->next(); it_t = it_t->next(); } // the compiler takes care to provide us with parallel arrays, // but we set a trap here in case there is a bug in the compiler. fassert( it_s == 0 && it_t == 0 ); } // we must generate an unpack request else { String ssize; ssize.writeNumber( (int64) tarr->size() ); // then unpack the source in the array. if ( source->isSimple() ) { m_out->writeString( "\tLDAS\t" + ssize + ", " ); gen_operand( source ); m_out->writeString( "\n" ); } else { gen_complex_value( source ); m_out->writeString( "\tLDAS\t" + ssize + ", A\n" ); } ListElement *it_t = tarr->end(); while( it_t != 0 ) { const Value *t = (const Value *) it_t->data(); if( t->isSimple() ) { m_out->writeString( "\tPOP \t" ); gen_operand( t ); m_out->writeString( "\n" ); } else { m_out->writeString( "\tPOP \tB\n" ); gen_load_from_reg( t, "B" ); } it_t = it_t->prev(); } } } } } int GenHAsm::gen_refArray( const ArrayDecl *tarr, bool bGenArray ) { ListElement *it_t = tarr->begin(); int size = 0; // first generates an array of references while( it_t != 0 ) { // again, is the compiler that must make sure of this... const Value *val = (const Value *) it_t->data(); fassert( val->isSimple() ); m_out->writeString( "\tPSHR\t" ); gen_operand( val ); m_out->writeString( "\n" ); ++size; it_t = it_t->next(); } String sizeStr; sizeStr.writeNumber( (int64) size ); if ( bGenArray ) m_out->writeString( "\tGENA\t" + sizeStr + "\n" ); return size; } void GenHAsm::gen_store_to_deep( const char *type, const Value *first, const Value *second, const Value *source ) { String operation; String typeStr = type; // first we must generate the assignands. if( source->isSimple() ) { if ( first->isSimple() && second->isSimple() ) { m_out->writeString( "\t" + typeStr + " \t" ); gen_operand( first ); m_out->writeString( ", " ); gen_operand( second ); m_out->writeString( ", " ); gen_operand( source ); m_out->writeString( "\n" ); } else if ( second->isSimple() ) { gen_complex_value( first ); m_out->writeString( "\t" + typeStr + " \tA, " ); gen_operand( second ); m_out->writeString( ", " ); gen_operand( source ); m_out->writeString( "\n" ); } else if ( first->isSimple() ) { gen_complex_value( second ); m_out->writeString( "\t" + typeStr + " \t" ); gen_operand( first ); m_out->writeString( ",A , " ); gen_operand( source ); m_out->writeString( "\n" ); } else { gen_complex_value( first ); m_out->writeString( "\tPUSH\tA\n" ); gen_complex_value( second ); m_out->writeString( "\tPOP \tB\n" ); m_out->writeString( "\t" + typeStr + " \tB, A, " ); gen_operand( source ); m_out->writeString( "\n" ); } } else { if ( first->isSimple() && second->isSimple() ) { gen_complex_value( source ); m_out->writeString( "\t" + typeStr + " \t" ); gen_operand( first ); m_out->writeString( ", " ); gen_operand( second ); m_out->writeString( ", A\n" ); } else { if( source->isReference() ) { operation = "R"; // store to vector by reference source = source->asReference(); } else { operation = "S"; // store to vector from stack top } if ( second->isSimple() ) { gen_complex_value( first ); m_out->writeString( "\tPUSH\tA\n" ); gen_complex_value( source ); m_out->writeString( "\tXPOP\tA\n" ); m_out->writeString( "\t" + typeStr + operation + "\tA, " ); gen_operand( second ); m_out->writeString( "\n" ); } else if ( first->isSimple() ) { gen_complex_value( second ); m_out->writeString( "\tPUSH\tA\n" ); gen_complex_value( source ); m_out->writeString( "\tXPOP\tA\n" ); m_out->writeString( "\t" + typeStr + operation + "\t" ); gen_operand( first ); m_out->writeString( ", A\n" ); } else { gen_complex_value( first ); m_out->writeString( "\tPUSH\tA\n" ); gen_complex_value( second ); m_out->writeString( "\tPUSH\tA\n" ); gen_complex_value( source ); m_out->writeString( "\tPOP \tB\n" ); m_out->writeString( "\tXPOP\tA\n" ); m_out->writeString( "\t" + typeStr + operation + "\tA, B\n" ); } } } } void GenHAsm::gen_load_from_deep( const char *type, const Value *first, const Value *second ) { String typeStr = type; // first we must generate the assignands. if( first->isSimple() ) { if ( second->isSimple() ) { m_out->writeString( "\t" + typeStr + "\t" ); gen_operand( first ); m_out->writeString( ", " ); gen_operand( second ); m_out->writeString( "\n" ); } else { gen_complex_value( second ); m_out->writeString( "\t" + typeStr + "\t" ); gen_operand( first ); m_out->writeString( ", A\n" ); } } else { if ( second->isSimple() ) { gen_complex_value( first ); m_out->writeString( "\t" + typeStr + " \t" ); m_out->writeString( "A, " ); gen_operand( second ); m_out->writeString( "\n" ); } else { gen_complex_value( first ); m_out->writeString( "\tPUSH\tA\n" ); gen_complex_value( second ); m_out->writeString( "\tPOP \tB\n" ); m_out->writeString( "\t" + typeStr + "\tB, A\n" ); } } } void GenHAsm::gen_load_from_A( const Value *target ) { gen_load_from_reg( target, "A" ); } void GenHAsm::gen_load_from_reg( const Value *target, const char *reg ) { String regStr = reg; if ( target->isSimple() ) { m_out->writeString( "\tLD \t" ); gen_operand( target ); m_out->writeString( ", " + regStr + "\n" ); } else { // target is NOT simple. If it's an expression ... if( target->type() == Value::t_expression ) { const Expression *exp = target->asExpr(); // ... then it may be an array assignment... if( exp->type() == Expression::t_array_access ) { gen_store_to_deep_reg( "STV", exp->first(), exp->second(), reg ); } else if ( exp->type() == Expression::t_obj_access ) { gen_store_to_deep_reg( "STP", exp->first(), exp->second(), reg ); } else { m_out->writeString( "\tPUSH\t" + regStr + "\n" ); gen_expression( exp ); m_out->writeString( "\tPOP \tB\n" ); m_out->writeString( "\tLD \tA,B\n" ); } } else if ( target->type() == Value::t_array_decl ) { // if the source is also an array, fine, we have a 1:1 assignment. const ArrayDecl *tarr = target->asArray(); String ssize; ssize.writeNumber((int64) tarr->size() ); // push the source array on the stack. m_out->writeString( "\tLDAS\t" + ssize + ", " + regStr + "\n" ); // and load each element back in the array ListElement *it_t = tarr->end(); // Now load each element by popping it. while( it_t != 0 ) { const Value *val = (const Value *) it_t->data(); if( val->isSimple() ) { m_out->writeString( "\tPOP \t" ); gen_operand( val ); m_out->writeString( "\n" ); } else { m_out->writeString( "\tPOP \tB\n" ); gen_load_from_reg( val, "B" ); } it_t = it_t->prev(); } } } } void GenHAsm::gen_store_to_deep_A( const char *type, const Value *first, const Value *second ) { gen_store_to_deep_reg( type, first, second, "A" ); } void GenHAsm::gen_store_to_deep_reg( const char *type, const Value *first, const Value *second, const char *reg ) { String typeStr = type; // first we must generate the assignands. if ( first->isSimple() && second->isSimple() ) { m_out->writeString( "\t" + typeStr + " \t" ); gen_operand( first ); m_out->writeString( ", " ); gen_operand( second ); m_out->writeString( ", " ); m_out->writeString( reg ); m_out->writeString( "\n" ); } else { m_out->writeString( "\tPUSH\t" + typeStr + "\n" ); if ( second->isSimple() ) { gen_complex_value( first ); m_out->writeString( "\t" + typeStr + "S\tA, " ); gen_operand( second ); m_out->writeString( "\n" ); } else if ( first->isSimple() ) { gen_complex_value( second ); m_out->writeString( "\t" + typeStr + "S\t" ); gen_operand( first ); m_out->writeString( ", A\n" ); } else { gen_complex_value( first ); m_out->writeString( "\tPUSH\tA\n" ); gen_complex_value( second ); m_out->writeString( "\tPOP \tB\n" ); m_out->writeString( "\t" + typeStr + "S\tB, A\n" ); } } } void GenHAsm::gen_funcall( const Expression *exp, bool fork ) { int size = 0; int branch; String functor = exp->type() == Expression::t_inherit ? "INST" : "CALL"; if( exp->second() != 0 ) { const ArrayDecl *dcl = exp->second()->asArray(); ListElement *iter = dcl->begin(); while( iter != 0 ) { const Value *val = (const Value *) iter->data(); gen_push( val ); size++; iter = iter->next(); } } String sizeStr; sizeStr.writeNumber( (int64) size ); if ( fork ) { branch = m_branch_id++; String branchStr; branchStr.writeNumber( (int64) branch ); if( exp->first()->isSimple() ) { m_out->writeString( "\tPUSH\t" ); gen_operand( exp->first() ); m_out->writeString( "\n" ); } else { gen_complex_value( exp->first() ); m_out->writeString( "\tPUSH\t A\n" ); } String sizeOne; sizeOne.writeNumber( (int64) size + 1 ); m_out->writeString( "\tFORK\t" + sizeOne + ", _branch_fork_" ); m_out->writeString( branchStr + "\n" ); m_out->writeString( "\tPOP \tA\n" ); m_out->writeString( "\t" + functor + "\t" ); m_out->writeString( sizeStr + ", A\n" ); m_out->writeString( "\tEND \t\n" ); m_out->writeString( "_branch_fork_" + branchStr + ":\n" ); } else { if( exp->first()->isSimple() ) { m_out->writeString( "\t" + functor + "\t" ); m_out->writeString( sizeStr + ", " ); gen_operand( exp->first() ); m_out->writeString( "\n" ); } else { gen_complex_value( exp->first() ); m_out->writeString( "\t" + functor + "\t" ); m_out->writeString( sizeStr + ", A\n" ); } } } } /* end of genhasm.cpp */ engine/gentree.cpp000066400000000000000000000571341176363201700145000ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: gentree.cpp Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: $DATE Last modified because ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include namespace Falcon { void GenTree::generate( const SourceTree *st ) { // generate functions if ( ! st->functions().empty() ) { m_out->writeString( "FUNCTIONS: \n" ); const StmtFunction *func = static_cast( st->functions().front() ); while ( func != 0 ) { m_out->writeString( "Function " + func->name() + "(" ); const Symbol *fsym = func->symbol(); const FuncDef *fdef = fsym->getFuncDef(); if( fdef->params() != 0) { MapIterator iter = fdef->symtab().map().begin(); bool first = true; while( iter.hasCurrent() ) { const Symbol *param = *(const Symbol **) iter.currentValue(); if( param->isParam() ) { if ( first ) { first = false; m_out->writeString( " " ); } else m_out->writeString( ", " ); m_out->writeString( param->name() ); } iter.next(); } } m_out->writeString( " )\n" ); if( func->hasStatic() ) { m_out->writeString( "STATIC:\n" ); gen_block( func->staticBlock(), 1 ); } gen_block( func->statements(), 1 ); func = static_cast(func->next()); } m_out->writeString( "\n" ); } m_out->writeString( "CODE:\n" ); gen_block( st->statements(), 0 ); } void GenTree::generate( const Statement *cmp, const char *specifier, bool sameline, int depth ) { if ( ! sameline ) { String line; line.writeNumber( (int64) cmp->line() ); int pos = 0; while (pos + line.length() < 5 ) { pos ++; m_out->writeString( " " ); } m_out->writeString( line + " : " ); for (int i = 0; i < depth; i++ ) m_out->writeString( " " ); } if ( specifier != 0 ) { m_out->writeString( specifier ); m_out->writeString( " " ); } switch( cmp->type() ) { case Statement::t_none: m_out->writeString( "(placeholder none statement)\n" ); break; case Statement::t_break: m_out->writeString( "BREAK\n" ); break; case Statement::t_continue: m_out->writeString( "CONTINUE" ); if( static_cast< const StmtContinue *>( cmp )->dropping() ) m_out->writeString( " DROPPING" ); m_out->writeString( "\n" ); break; case Statement::t_launch: m_out->writeString( "LAUNCH " ); gen_value( static_cast< const StmtExpression *>( cmp )->value() ); m_out->writeString( "\n" ); break; case Statement::t_autoexp: m_out->writeString( "AUTOEXPR " ); gen_value( static_cast< const StmtExpression *>( cmp )->value() ); m_out->writeString( "\n" ); break; case Statement::t_return: m_out->writeString( "RETURN " ); gen_value( static_cast< const StmtExpression *>( cmp )->value() ); m_out->writeString( "\n" ); break; case Statement::t_fordot: m_out->writeString( "FORDOT " ); gen_value( static_cast< const StmtExpression *>( cmp )->value() ); m_out->writeString( "\n" ); break; case Statement::t_raise: m_out->writeString( "RAISE " ); gen_value( static_cast< const StmtExpression *>( cmp )->value() ); m_out->writeString( "\n" ); break; case Statement::t_give: { const StmtGive *give = static_cast< const StmtGive *>( cmp ); m_out->writeString( "GIVE " ); gen_array( give->attributes() ); m_out->writeString( " to " ); gen_array( give->objects() ); m_out->writeString( "\n" ); } break; case Statement::t_self_print: { const StmtSelfPrint *sp = static_cast< const StmtSelfPrint *>( cmp ); m_out->writeString( "FAST PRINT " ); gen_array( sp->toPrint() ); m_out->writeString( "\n" ); } break; case Statement::t_if: { m_out->writeString( "IF " ); const StmtIf *sif = static_cast< const StmtIf *>( cmp ); gen_value( sif->condition() ); m_out->writeString( "\n" ); gen_block( sif->children(), depth ); const Statement *stmt = sif->elifChildren().front(); while( stmt != 0 ) { generate( stmt, 0, false, depth + 1 ); stmt = static_cast(stmt->next()); } gen_block( sif->elseChildren(), depth, "ELSE" ); } break; case Statement::t_select: case Statement::t_switch: { if ( cmp->type() == Statement::t_switch ) m_out->writeString( "SWITCH " ); else m_out->writeString( "SELECT " ); const StmtSwitch *sw = static_cast< const StmtSwitch *>( cmp ); gen_value( sw->switchItem() ); m_out->writeString( "\n" ); // generates the switch lists MapIterator iter; // generatest the switch integer list if ( !sw->intCases().empty() ) { m_out->writeString( "INT cases " ); iter = sw->intCases().begin(); while( iter.hasCurrent() ) { Value *first = *(Value **) iter.currentKey(); uint32 second = *(uint32 *) iter.currentValue(); String temp; temp.writeNumber( (int64) second ); gen_value( first ); m_out->writeString( "->" + temp + "; " ); iter.next(); } m_out->writeString( "\n" ); } if ( !sw->rngCases().empty() ) { m_out->writeString( "RANGE cases " ); iter = sw->rngCases().begin(); while( iter.hasCurrent() ) { Value *first = *(Value **) iter.currentKey(); uint32 second = *(uint32 *) iter.currentValue(); String temp; temp.writeNumber( (int64) second ); gen_value( first ); m_out->writeString( "->" + temp + "; " ); iter.next(); } m_out->writeString( "\n" ); } if ( !sw->strCases().empty() ) { m_out->writeString( "STRING cases " ); iter = sw->strCases().begin(); while( iter.hasCurrent() ) { Value *first = *(Value **) iter.currentKey(); uint32 second = *(uint32 *) iter.currentValue(); String temp; temp.writeNumber( (int64) second ); gen_value( first ); m_out->writeString( "->" + temp + "; " ); iter.next(); } m_out->writeString( "\n" ); } if ( !sw->objCases().empty() ) { m_out->writeString( "Symbol cases " ); iter = sw->objCases().begin(); while( iter.hasCurrent() ) { Value *first = *(Value **) iter.currentKey(); uint32 second = *(uint32 *) iter.currentValue(); String temp; temp.writeNumber( (int64) second ); gen_value( first ); m_out->writeString( "->" + temp + "; " ); iter.next(); } m_out->writeString( "\n" ); } // generates the blocks int blockId = 0; const Statement *stmt = sw->blocks().front(); while( stmt != 0 ) { String blockStr; blockStr.writeNumber( (int64) blockId ); if( blockId == sw->nilBlock() ) m_out->writeString( "CASE BLOCK (NIL)" + blockStr + "\n" ); else m_out->writeString( "CASE BLOCK " + blockStr + "\n" ); generate( stmt, 0, false, depth + 1 ); stmt = static_cast(stmt->next()); blockId ++ ; } if ( ! sw->defaultBlock().empty() ) { m_out->writeString( "DEFAULT BLOCK\n" ); gen_block( sw->defaultBlock(), depth + 1 ); } } break; case Statement::t_case: { //m_out->writeString( "CASE \n" ); const StmtCaseBlock *scase = static_cast< const StmtCaseBlock *>( cmp ); gen_block( scase->children(), depth ); } break; case Statement::t_catch: { //m_out->writeString( "CASE \n" ); const StmtCatchBlock *scase = static_cast< const StmtCatchBlock *>( cmp ); if ( scase->intoValue() != 0 ) { m_out->writeString( "CATCH into " ); gen_value( scase->intoValue() ); m_out->writeString( "\n" ); } else m_out->writeString( "CATCH witout into\n" ); gen_block( scase->children(), depth ); } break; case Statement::t_elif: { m_out->writeString( "ELIF " ); const StmtElif *selif = static_cast< const StmtElif *>( cmp ); gen_value( selif->condition() ); m_out->writeString( "\n" ); gen_block( selif->children(), depth ); } break; case Statement::t_while: { const StmtWhile *wh = static_cast< const StmtWhile *>( cmp ); m_out->writeString( "WHILE " ); gen_value( wh->condition() ); m_out->writeString( "\n" ); gen_block( wh->children(), depth ); } break; case Statement::t_loop: { const StmtLoop *wh = static_cast< const StmtLoop *>( cmp ); m_out->writeString( "LOOP " ); m_out->writeString( "\n" ); gen_block( wh->children(), depth ); if( wh->condition() != 0 ) { m_out->writeString( "END LOOP WHEN " ); gen_value( wh->condition() ); m_out->writeString( "\n" ); } else m_out->writeString( "END\n" ); } break; case Statement::t_global: { m_out->writeString( "GLOBAL " ); const StmtGlobal *sglobal = static_cast< const StmtGlobal *>( cmp ); ListElement *iter = sglobal->getSymbols().begin(); while ( iter != 0 ) { Symbol *sym = (Symbol *) iter->data(); m_out->writeString( sym->name() + ", " ); iter = iter->next(); } m_out->writeString( "\n" ); } break; case Statement::t_forin: { m_out->writeString( "FOR-IN " ); const StmtForin *sfor = static_cast< const StmtForin *>( cmp ); gen_array( sfor->dest() ); m_out->writeString( " IN " ); gen_value( sfor->source() ); m_out->writeString( "\n" ); gen_block( sfor->children(), depth ); gen_block( sfor->firstBlock(), depth, "FORFIRST" ); gen_block( sfor->middleBlock(), depth, "FORMIDDLE" ); gen_block( sfor->lastBlock(), depth, "FORLAST" ); } break; case Statement::t_try: { m_out->writeString( "TRY\n" ); const StmtTry *stry = static_cast< const StmtTry *>( cmp ); gen_block( stry->children(), depth ); // generatest the switch integer list if ( ! stry->intCases().empty() ) { m_out->writeString( "TYPE ID CATCHES " ); MapIterator iter = stry->intCases().begin(); while( iter.hasCurrent() ) { Value *first = *(Value **) iter.currentKey(); uint32 second = *(uint32 *) iter.currentValue(); String temp; temp.writeNumber( (int64) second ); gen_value( first ); m_out->writeString( "->" + temp + "; " ); iter.next(); } m_out->writeString( "\n" ); } // Generates the switch symbol list if ( ! stry->objCases().empty() ) { m_out->writeString( "SYMBOL CATCHES " ); MapIterator iter = stry->objCases().begin(); while( iter.hasCurrent() ) { Value *first = *(Value **) iter.currentKey(); uint32 second = *(uint32 *) iter.currentValue(); String temp; temp.writeNumber( (int64) second ); gen_value( first ); m_out->writeString( "->" + temp + "; " ); iter.next(); } m_out->writeString( "\n" ); } // generates the blocks int blockId = 0; const Statement *stmt = stry->handlers().front(); while( stmt != 0 ) { String blockStr; blockStr.writeNumber( (int64) blockId ); m_out->writeString( "HANDLER BLOCK " + blockStr + "\n" ); generate( stmt, 0, false, depth + 1 ); stmt = static_cast( stmt->next() ); blockId ++ ; } if ( stry->defaultHandler() != 0 ) { m_out->writeString( "DEFAULT HANDLER" ); if ( stry->defaultHandler()->intoValue() != 0 ) { m_out->writeString( " into " ); gen_value( stry->defaultHandler()->intoValue() ); } m_out->writeString( "\n" ); gen_block( stry->defaultHandler()->children(), depth + 1 ); } } break; case Statement::t_propdef: { m_out->writeString( "PROPDEF " ); const StmtVarDef *spd = static_cast< const StmtVarDef *>( cmp ); m_out->writeString( *spd->name() ); m_out->writeString( "=" ); gen_value( spd->value() ); m_out->writeString( "\n" ); } break; default: m_out->writeString( "????\n" ); } } void GenTree::gen_block( const StatementList &list, int depth, const char *prefix ) { if( list.empty() ) return; if ( prefix != 0 ) { String line; line.writeNumber( (int64) list.front()->line() ); int pos = 0; while( pos + line.length() < 5 ) { m_out->writeString( " " ); pos++; } m_out->writeString( line + " : " ); for (int i = 0; i < depth; i++ ) m_out->writeString( " " ); m_out->writeString( prefix ); m_out->writeString( "\n" ); } const Statement *stmt = list.front(); while( stmt != 0 ) { generate( stmt, 0, false, depth + 1 ); stmt = static_cast(stmt->next()); } } void GenTree::gen_value( const Value *val ) { if ( val == 0 ) { m_out->writeString( "(null)" ); return; } switch( val->type() ) { case Value::t_nil: m_out->writeString( "nil" ); break; case Value::t_unbound: m_out->writeString( "unbound" ); break; case Value::t_imm_bool: m_out->writeString( val->asBool() ? "true": "false" ); break; case Value::t_imm_integer: { String intStr; intStr.writeNumber( val->asInteger() ); m_out->writeString( intStr ); } break; case Value::t_imm_string: { String temp; val->asString()->escape( temp ); m_out->writeString( "\"" + temp + "\"" ); } break; case Value::t_lbind: { m_out->writeString( "&" + *val->asLBind() ); } break; case Value::t_imm_num: { String numStr; numStr.writeNumber( val->asInteger() ); m_out->writeString( numStr ); } break; case Value::t_range_decl: m_out->writeString( "[" ); gen_value( val->asRange()->rangeStart() ); m_out->writeString( ":" ); if ( ! val->asRange()->isOpen() ) { gen_value( val->asRange()->rangeEnd() ) ; if ( val->asRange()->rangeStep() != 0 ) { m_out->writeString( ":" ); gen_value( val->asRange()->rangeStep() ); } } m_out->writeString( "]" ); break; case Value::t_symbol: m_out->writeString( val->asSymbol()->name() ); break; case Value::t_self: m_out->writeString( "self" ); break; case Value::t_fself: m_out->writeString( "fself" ); break; case Value::t_array_decl: m_out->writeString( "Array: [ " ); gen_array( val->asArray() ); m_out->writeString( " ]" ); break; case Value::t_dict_decl: m_out->writeString( "Dict: [" ); gen_dict( val->asDict() ); m_out->writeString( " ]" ); break; case Value::t_byref: m_out->writeString( "$" ); gen_value( val->asReference() ); break; case Value::t_expression: gen_expression( val->asExpr() ); break; default: break; } } void GenTree::gen_expression( const Expression *exp ) { String name; int type = 0; switch( exp->type() ) { case Expression::t_optimized: gen_value( exp->first() ); return; case Expression::t_neg: type = 0; name = "-"; break; case Expression::t_bin_not: type = 0; name = "!"; break; case Expression::t_strexpand: type = 0; name = "@"; break; case Expression::t_indirect: type = 0; name = "#"; break; case Expression::t_not: type = 0; name = "not"; break; case Expression::t_pre_inc: type = 0; name = "++"; break; case Expression::t_pre_dec: type = 0; name = "--"; break; case Expression::t_eval: type = 0; name = "^*"; break; case Expression::t_oob: type = 0; name = "^+"; break; case Expression::t_deoob: type = 0; name = "^-"; break; case Expression::t_isoob: type = 0; name = "^?"; break; case Expression::t_xoroob: type = 0; name = "^!"; break; case Expression::t_post_inc: type = 1; name = "++"; break; case Expression::t_post_dec: type = 1; name = "--"; break; case Expression::t_bin_and: type = 2; name = "&"; break; case Expression::t_bin_or: type = 2; name = "|"; break; case Expression::t_bin_xor: type = 2; name = "^"; break; case Expression::t_shift_left: type = 2; name = "<<"; break; case Expression::t_shift_right: type = 2; name = ">>"; break; case Expression::t_plus: type = 2; name = "+"; break; case Expression::t_minus: type = 2; name = "-"; break; case Expression::t_times: type = 2; name = "*"; break; case Expression::t_divide: type = 2; name = "/"; break; case Expression::t_modulo: type = 2; name = "%"; break; case Expression::t_power: type = 2; name = "**"; break; case Expression::t_gt: type = 2; name = ">"; break; case Expression::t_ge: type = 2; name = ">="; break; case Expression::t_lt: type = 2; name = "<"; break; case Expression::t_le: type = 2; name = "<="; break; case Expression::t_eq: type = 2; name = "="; break; case Expression::t_exeq: type = 2; name = "eq"; break; case Expression::t_neq: type = 2; name = "!="; break; case Expression::t_has: type = 2; name = "has"; break; case Expression::t_hasnt: type = 2; name = "hasnt"; break; case Expression::t_in: type = 2; name = "in"; break; case Expression::t_notin: type = 2; name = "notin"; break; case Expression::t_provides: type = 2; name = "provides"; break; case Expression::t_or: type = 2; name = "or"; break; case Expression::t_and: type = 2; name = "and"; break; case Expression::t_iif: type = 3; break; case Expression::t_assign: type = 4; name = " = "; break; case Expression::t_fbind: type = 4; name = " | "; break; case Expression::t_aadd: type = 4; name = " += "; break; case Expression::t_asub: type = 4; name = " -= "; break; case Expression::t_amul: type = 4; name = " *= "; break; case Expression::t_adiv: type = 4; name = " /= "; break; case Expression::t_amod: type = 4; name = " %= "; break; case Expression::t_apow: type = 4; name = " *= "; break; case Expression::t_aband: type = 4; name = " &= "; break; case Expression::t_abor: type = 4; name = " |= "; break; case Expression::t_abxor: type = 4; name = " ^= "; break; case Expression::t_ashl: type = 4; name = " <<= "; break; case Expression::t_ashr: type = 4; name = " >>= "; break; case Expression::t_array_access: type = 5; break; case Expression::t_array_byte_access: type = 10; break; case Expression::t_obj_access: type = 6; break; case Expression::t_funcall: case Expression::t_inherit: type = 7; break; case Expression::t_lambda: type = 8; break; default: return; } switch( type ) { case 0: m_out->writeString( " " + name + " " ); gen_value( exp->first() ); break; case 1: gen_value( exp->first() ); m_out->writeString( " " + name + " " ); break; case 2: m_out->writeString( "(" ); gen_value( exp->first() ); m_out->writeString( " " + name + " " ); gen_value( exp->second() ); m_out->writeString( " )" ); break; case 3: m_out->writeString( "(" ); gen_value( exp->first() ); m_out->writeString( " ? (" ); gen_value( exp->second() ); m_out->writeString( " ):(" ); gen_value( exp->third() ); m_out->writeString( " ))" ); break; case 4: m_out->writeString( "( " ); gen_value( exp->first() ); m_out->writeString( name ); gen_value( exp->second() ); m_out->writeString( " )" ); break; case 5: gen_value( exp->first() ); m_out->writeString( "[ " ); gen_value( exp->second() ); m_out->writeString( " ]" ); break; case 6: gen_value( exp->first() ); m_out->writeString( "." ); gen_value( exp->second() ); break; case 7: gen_value( exp->first() ); m_out->writeString( "(" ); if( exp->second() != 0 ) gen_array( exp->second()->asArray() ); m_out->writeString( " )" ); break; case 8: m_out->writeString( exp->first()->asSymbol()->name() ); if( exp->second() != 0 ) { m_out->writeString( " closure (" ); gen_array( exp->second()->asArray() ); m_out->writeString( " )" ); } break; case 10: gen_value( exp->first() ); m_out->writeString( "[ *" ); gen_value( exp->second() ); m_out->writeString( " ]" ); break; } } void GenTree::gen_array( const ArrayDecl *ad ) { ListElement *iter = ad->begin(); while( iter != 0 ) { const Value *val = (const Value *) iter->data(); gen_value( val ); iter = iter->next(); if( iter != 0 ) m_out->writeString( ", " ); } } void GenTree::gen_dict( const DictDecl *ad ) { if( ad->empty() ) { m_out->writeString( " =>" ); return; } ListElement *iter = ad->begin(); while( iter != 0 ) { DictDecl::pair *pair = (DictDecl::pair *) iter->data(); const Value *key = pair->first; const Value *value = pair->second; gen_value( key ); m_out->writeString( "=>" ); gen_value( value ); iter = iter->next(); if( iter != 0 ) m_out->writeString( ", " ); } } } /* end of gentree.cpp */ engine/globals.cpp000066400000000000000000000132551176363201700144660ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: globals.cpp Engine static/global data setup and initialization ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 02 Mar 2009 20:33:22 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Engine static/global data setup and initialization */ #include #include #include #include #include #include #include #include namespace Falcon { #define FLC_DECLARE_ENGINE_MSG #include #undef FLC_DECLARE_ENGINE_MSG StringTable * engineStrings = 0; namespace Engine { static Mutex s_mtx; static Map *s_serviceMap = 0; static String* s_sIOEnc = 0; static String* s_sSrcEnc = 0; static String* s_searchPath = 0; static ModuleCache* s_moduleCache = 0; #ifdef FALCON_SYSTEM_WIN static bool s_bWindowsNamesConversion = true; #else static bool s_bWindowsNamesConversion = false; #endif /** Release language data. */ void releaseLanguage(); /** Release encoding data. */ void releaseEncodings(); bool addVFS( VFSProvider *prv ) { fassert( prv != 0 ); return addVFS(prv->protocol(),prv); } bool addVFS( const String &protocol, VFSProvider *prv ) { fassert( prv != 0 ); s_mtx.lock(); if ( s_serviceMap == 0 ) { s_serviceMap = new Map( &traits::t_string(), &traits::t_voidp() ); } else { void *oldPrv = s_serviceMap->find( &protocol ); if( oldPrv != 0 ) { s_mtx.unlock(); return false; } } s_serviceMap->insert( &protocol, prv ); s_mtx.unlock(); return true; } VFSProvider* getVFS( const String &name ) { s_mtx.lock(); if ( s_serviceMap == 0 ) { s_mtx.unlock(); return 0; } void *prv = s_serviceMap->find( &name ); s_mtx.unlock(); if ( prv != 0 ) return *(VFSProvider**) prv; return 0; } void Init() { // Default language setLanguage( "C" ); setEncodings( "C", "C" ); // create the default mempool. memPool = new MemPool; memPool->start(); // create the default file VSF addVFS( "file", new VFSFile ); addVFS( "", new VFSFile ); // Ok, we're ready } void PerformGC() { memPool->performGC(); } void Shutdown() { if( s_searchPath != 0 ) delete s_searchPath; delete memPool; memPool = 0; delete s_moduleCache; s_moduleCache = 0; releaseLanguage(); releaseEncodings(); // clear all the service ( and VSF ); s_mtx.lock(); if( s_serviceMap ) { MapIterator mi = s_serviceMap->begin(); while ( mi.hasCurrent() ) { delete *(VFSProvider**) mi.currentValue(); mi.next(); } delete s_serviceMap; s_serviceMap = 0; } s_mtx.unlock(); traits::releaseTraits(); gcMemShutdown(); } const String &getMessage( uint32 id ) { const String *data = engineStrings->get( id ); fassert( data != 0 ); return *data; } bool setTable( StringTable *tab ) { if ( engineStrings == 0 || engineStrings->size() == tab->size() ) { engineStrings = tab; return true; } return false; } bool setLanguage( const String &language ) { delete engineStrings; engineStrings = new StringTable; if( language == "C" ) { #define FLC_REALIZE_ENGINE_MSG #include #undef FLC_REALIZE_ENGINE_MSG return true; } // ... signal that we didn't found the language. return false; } void releaseLanguage() { delete engineStrings; engineStrings = 0; } void setEncodings( const String &sSrcEnc, const String &sIOEnc ) { s_mtx.lock(); releaseEncodings(); s_sSrcEnc = new String(sSrcEnc); s_sIOEnc = new String(sIOEnc); s_sSrcEnc->bufferize(); s_sIOEnc->bufferize(); s_mtx.unlock(); } void releaseEncodings() { delete s_sSrcEnc; delete s_sIOEnc; } void getEncodings( String &sSrcEnc, String &sIOEnc ) { s_mtx.lock(); sSrcEnc = *s_sSrcEnc; sIOEnc = *s_sIOEnc; s_mtx.unlock(); } void setSearchPath( const String &str ) { s_mtx.lock(); if( s_searchPath != 0 ) delete s_searchPath; s_searchPath = new String( str ); s_searchPath->bufferize(); s_mtx.unlock(); } String getSearchPath() { s_mtx.lock(); if( s_searchPath != 0 ) { String temp( *s_searchPath ); temp.bufferize(); s_mtx.unlock(); return temp; } s_mtx.unlock(); return ""; } void setWindowsNamesConversion( bool s ) { s_bWindowsNamesConversion = s; } bool getWindowsNamesConversion() { return s_bWindowsNamesConversion; } void cacheModules( bool tmode ) { s_mtx.lock(); if ( tmode ) { if ( s_moduleCache == 0 ) s_moduleCache = new ModuleCache; } else { delete s_moduleCache; s_moduleCache = 0; } s_mtx.unlock(); } ModuleCache* getModuleCache() { return s_moduleCache; } } } /* end of globals.cpp */ engine/heap_bsd.cpp000066400000000000000000000021731176363201700146050ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: heap_bsd.cpp Class for heap management - system specific for BSD compliant systems. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mer 14 Gen 2009 20:26:53 CET ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Class for heap management - system specific for POSIX compliant systems. */ #include #include #include #include namespace Falcon { static int s_pageSize; HeapMem Heap; HeapMem::HeapMem() { s_pageSize = getpagesize(); } HeapMem::~HeapMem() { } namespace Sys { int sys_pageSize() { return s_pageSize; } void *sys_allocPage() { void *ret = mmap(((void *)0), s_pageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); fassert( ret != MAP_FAILED ); return ret; } void sys_freePage( void *page ) { munmap( page, s_pageSize ); } } } /* end of heap_bsd.cpp */ engine/heap_posix.cpp000066400000000000000000000023441176363201700151770ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: heap_posix.cpp Class for heap management - system specific for POSIX compliant systems. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 13 Dec 2008 13:45:59 +0100 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Class for heap management - system specific for POSIX compliant systems. */ #include #include #include #include // For BSD #ifndef MAP_ANONYMOUS # define MAP_ANONYMOUS MAP_ANON #endif namespace Falcon { static int s_pageSize; HeapMem Heap; HeapMem::HeapMem() { s_pageSize = sysconf( _SC_PAGESIZE ); } HeapMem::~HeapMem() { } namespace Sys { int sys_pageSize() { return s_pageSize; } void *sys_allocPage() { void *ret = mmap(((char *)0), s_pageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); fassert( ret != MAP_FAILED ); return ret; } void sys_freePage( void *page ) { munmap( (char*) page, s_pageSize ); } } } /* end of heap_posix.cpp */ engine/heap_win.cpp000066400000000000000000000022271176363201700146320ustar00rootroot00000000000000/* FALCON - Falcon advanced simple text evaluator. FILE: heap_win.cpp Initialization of heap_windows variables. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2004-11-01 10:20+0100UTC ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Implementation of windows specific heap. */ #include #include #include namespace Falcon { #define WIN_PAGE_SIZE 4096 static int s_pageSize; static HANDLE s_heapHandle = 0; #if 0 HeapMem Heap; HeapMem::HeapMem() { int x = 0, y = 1, z = 2; s_pageSize = WIN_PAGE_SIZE; //s_heapHandle = GetProcessHeap(); s_heapHandle = 0; } HeapMem::~HeapMem() { } namespace Sys { int sys_pageSize() { return s_pageSize; } void *sys_allocPage() { void *ret = HeapAlloc( s_heapHandle, 0, s_pageSize ); fassert( ret != 0 ); return ret; } void sys_freePage( void *page ) { HeapFree( s_heapHandle, 0, page ); } } #endif } /* end of heap_win.cpp */ engine/intcomp.cpp000066400000000000000000000235561176363201700145210ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: intcomp.cpp Complete encapsulation of an incremental interactive compiler. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 17 Aug 2008 11:10:24 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include "core_module/core_module.h" namespace Falcon { InteractiveCompiler::InteractiveCompiler( ModuleLoader *l, VMachine *vm ): Compiler(), m_lmodule( 0 ), m_loader( l ), m_interactive( true ) { m_vm = vm; m_vm->incref(); m_modVersion = 0x010000; m_language = "en_EN"; m_module = new Module; m_module->name( "falcon.intcomp" ); m_module->engineVersion( FALCON_VERSION_NUM ); // link this as main and private. m_lmodule = m_vm->link( m_module, true, true ); // as the module is currently empty, we expect the link to work. fassert( m_lmodule ); // set incremental mode for the lexer. m_tempLine = 1; init(); } InteractiveCompiler::~InteractiveCompiler() { m_module->decref(); m_vm->decref(); } InteractiveCompiler::t_ret_type InteractiveCompiler::compileNext( const String &input ) { // we split the thing for performance // and use stack based object to protect against exception raisal if( input.manipulator()->charSize() == 1 ) { ROStringStream ss( input ); return compileNext( &ss ); } else { String temp; TranscodeString(input, "utf-16", temp ); ROStringStream ss( temp ); TranscoderUTF16 tr( &ss, false ); return compileNext( &tr ); } } InteractiveCompiler::t_ret_type InteractiveCompiler::compileAll( const String &input ) { t_ret_type ret = e_nothing; // we split the thing for performance // and use stack based object to protect against exception raisal if( input.manipulator()->charSize() == 1 ) { ROStringStream ss( input ); while ( ! ss.eof() ) { // on error, we'll throw. ret = compileNext( &ss ); } } else { String temp; TranscodeString(input, "utf-16", temp ); ROStringStream ss( temp ); TranscoderUTF16 tr( &ss, false ); while ( ! tr.eof() ) { // on error, we'll throw. ret = compileNext( &tr ); } } return ret; } InteractiveCompiler::t_ret_type InteractiveCompiler::compileNext( Stream *input ) { // ensure we're talking the same language... m_stream = input; // reset if last code was executed. m_contextSet.clear(); m_context.clear(); m_loops.clear(); m_functions.clear(); m_func_ctx.clear(); delete m_root; m_root = new SourceTree; pushContextSet( &m_root->statements() ); // and an empty list for the local function undefined values while( ! m_statementVals.empty() ) { delete (List*) m_statementVals.back(); m_statementVals.popBack(); } List* l = new List; m_statementVals.pushBack( l ); // reset compilation m_enumId = 0; m_staticPrefix = 0; m_closureContexts = 0; // anyhow reset error count m_errors = 0; // reset the lexer m_lexer->reset(); m_lexer->line( m_tempLine ); m_lexer->input( m_stream ); m_lexer->incremental( m_interactive ); // prepare to unroll changes in the module uint32 modSymSize = m_module->symbols().size(); // parse flc_src_parse( this ); // TODO: move this directly where changed... m_module->language( m_language ); m_module->version( (uint32) m_modVersion ); // some errors during compilation? if( m_errors != 0 ) { // but do not reset errors, the owner may want to know. Error *err = m_rootError; m_rootError = 0; m_module->rollBackSymbols( modSymSize ); throw err; } if ( m_lexer->hasOpenContexts() ) { m_module->rollBackSymbols( modSymSize ); return e_incomplete; } // If the context is not empty, then we have a partial data. // more is needed if ( !m_context.empty() || m_contextSet.size() != 1 ) { m_module->rollBackSymbols( modSymSize ); return e_more; } // now we have to decide what to do with the beast. // if it's a directive, we already handled it. m_tempLine += m_lexer->previousLine(); t_ret_type ret = e_nothing; // empty? if ( m_root->classes().empty() && m_root->functions().empty() && m_root->statements().empty() ) { m_module->rollBackSymbols( modSymSize ); return e_nothing; } // if we have some statements, we may need to change the first expression // into a return, if we are in interactive mode. if( ! m_root->statements().empty() ) { // was it an expression? if( m_interactive && m_root->statements().back()->type() == Statement::t_autoexp ) { // wrap it around a return, so A is not nilled. StmtAutoexpr *ae = static_cast( m_root->statements().pop_back() ); m_root->statements().push_back( new StmtReturn( 1, ae->value()->clone() ) ); // what kind of expression is it? -- if it's a call, we're interested if( ae->value()->isExpr() && ae->value()->asExpr()->type() == Expression::t_funcall ) ret = e_call; else ret = e_expression; // we don't need the expression anymore. delete ae; } else ret = e_statement; } // generate the module GenCode gencode( module() ); gencode.generate( m_root ); m_lmodule->globals().resize( module()->symbolTable().size() + 1 ); while ( ! m_root->classes().empty() ) { StmtClass *cls = static_cast( m_root->classes().front() ); // in case of classes, we have to link the constructor, // the class itself and eventually the singleton. StmtFunction *init = cls->ctorFunction(); if ( init != 0 ) { ListElement *from_iter = cls->symbol()->getClassDef()->inheritance().begin(); while( from_iter != 0 ) { const InheritDef *def = (const InheritDef *) from_iter->data(); const Symbol *parent = def->base(); // it's just an import m_vm->linkSymbol( parent, m_lmodule ); from_iter = from_iter->next(); } m_vm->linkCompleteSymbol( init->symbol(), m_lmodule ); } m_vm->linkCompleteSymbol( cls->symbol(), m_lmodule ); Symbol *singleton = cls->singleton(); if ( singleton != 0 ) { m_vm->linkCompleteSymbol( singleton, m_lmodule ); } if ( ret == e_nothing ) ret = e_decl; delete m_root->classes().pop_front(); } // if it's a function while ( ! m_root->functions().empty() ) { StmtFunction *func = static_cast( m_root->functions().front() ); m_vm->linkCompleteSymbol( func->symbol(), m_lmodule ); if ( ret == e_nothing ) ret = e_decl; delete m_root->functions().pop_front(); } // now, the symbol table must be traversed. MapIterator iter = m_module->symbolTable().map().begin(); bool success = true; while( iter.hasCurrent() ) { Symbol *sym = *(Symbol **) iter.currentValue(); // try to link undefined symbols. if ( sym->isUndefined() ) { try { m_vm->linkSymbol( sym, m_lmodule ); } catch( Error *e ) { // but continue to expose other errors as well. success = false; // prevent searching again sym->setGlobal(); raiseError( e ); } } // next symbol iter.next(); } // re-link failed? if ( ! success ) { Error *e = m_rootError; m_rootError = 0; m_module->rollBackSymbols( modSymSize ); throw e; } // finally, link main if ( ! m_root->statements().empty() ) { Symbol* msym = module()->findGlobalSymbol("__main__" ); vm()->linkCompleteSymbol( msym, m_lmodule ); } // launch the vm. if ( ret == e_statement || ret == e_call || ret == e_expression ) { try { Item* i_main = m_vm->mainModule()->findModuleItem("__main__"); if( i_main == 0 ) { throw new CodeError( ErrorParam( e_undef_sym, __LINE__ ) .origin( e_orig_compiler ) .extra( "__main__ (check modules link order/mode)" ) ); } m_vm->reset(); m_vm->callItem( *i_main, 0 ); } catch( VMEventQuit & ) { ret = e_terminated; } } return ret; } void InteractiveCompiler::addNamespace( const String &nspace, const String &alias, bool full, bool filename ) { loadNow( nspace, filename, true ); // create also the standard namespace. if ( m_errors == 0 ) { Compiler::addNamespace( nspace, alias, full, filename ); } } void InteractiveCompiler::addLoad( const String &name, bool isFilename ) { loadNow( name, isFilename, false ); if ( m_errors == 0 ) { Compiler::addLoad( name, isFilename ); } } void InteractiveCompiler::loadNow( const String &name, bool isFilename, bool bPrivate ) { Module *mod; if ( isFilename ) mod = m_loader->loadFile( name ); else mod = m_loader->loadName( name, m_module->name() ); if ( mod == 0 ) { // we had an error. m_errors++; return; } // perform a complete linking Runtime rt( m_loader, m_vm ); rt.hasMainModule( false ); try { rt.addModule( mod, bPrivate ); m_vm->link( &rt ); } catch( Error* ) { m_errors++; mod->decref(); throw; } mod->decref(); } } /* end of intcomp.cpp */ engine/item.cpp000066400000000000000000000407011176363201700137750ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: item.cpp Item API implementation ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar ott 12 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Item API implementation This File contains the non-inlined access to items. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Falcon { inline void assignToVm( Garbageable *gcdata ) { gcdata->gcMark( memPool->generation() ); } //================================================================= // Garbage markers Item::Item( byte t, Garbageable *dt ) { type( t ); content( dt ); assignToVm( dt ); } void Item::setRange( CoreRange *r ) { type( FLC_ITEM_RANGE ); all.ctx.data.content = r; assignToVm( r ); } void Item::setString( String *str ) { type( FLC_ITEM_STRING ); all.ctx.data.ptr.voidp = str; all.ctx.data.ptr.extra = 0; if ( str->isCore() ) assignToVm( &static_cast(str)->garbage() ); } /* void Item::setString( String *str, LiveModule *lm ) { type( FLC_ITEM_STRING ); // must not be a non-garbage string fassert( ! str->isCore() ); all.ctx.data.ptr.voidp = str; all.ctx.data.ptr.extra = lm; } */ void Item::setArray( CoreArray *array ) { type( FLC_ITEM_ARRAY ); all.ctx.data.ptr.voidp = array; assignToVm( array ); } void Item::setObject( CoreObject *obj ) { type( FLC_ITEM_OBJECT ); all.ctx.data.ptr.voidp = obj; assignToVm( obj ); } void Item::setDict( CoreDict *dict ) { type( FLC_ITEM_DICT ); all.ctx.data.ptr.voidp = dict; assignToVm( dict ); } void Item::setMemBuf( MemBuf *b ) { type( FLC_ITEM_MEMBUF ); all.ctx.data.ptr.voidp = b; assignToVm( b ); } void Item::setReference( GarbageItem *ref ) { type( FLC_ITEM_REFERENCE ); all.ctx.data.ptr.voidp = ref; assignToVm( ref ); } void Item::setFunction( CoreFunc* cf ) { type( FLC_ITEM_FUNC ); all.ctx.data.ptr.extra = cf; assignToVm( cf ); } void Item::setLBind( String *lbind, GarbageItem *val ) { type( FLC_ITEM_LBIND ); all.ctx.data.ptr.voidp = lbind; all.ctx.data.ptr.extra = val; if ( lbind->isCore() ) assignToVm( &static_cast(lbind)->garbage() ); if ( val != 0 ) assignToVm( val ); } void Item::setMethod( const Item &data, CallPoint *func ) { *this = data; all.ctx.base.bits.oldType = all.ctx.base.bits.type; all.ctx.method = func; type( FLC_ITEM_METHOD ); assignToVm( func ); } void Item::setClassMethod( CoreObject *obj, CoreClass *cls ) { type( FLC_ITEM_CLSMETHOD ); all.ctx.data.ptr.voidp = obj; all.ctx.data.ptr.extra = cls; assignToVm( obj ); assignToVm( cls ); } void Item::setClass( CoreClass *cls ) { type( FLC_ITEM_CLASS ); // warning: class in extra to be homologue to methodClass() all.ctx.data.ptr.extra = cls; assignToVm( cls ); } void Item::setGCPointer( FalconData *ptr ) { type( FLC_ITEM_GCPTR ); all.ctx.data.content = new GarbagePointer( ptr ); assignToVm( all.ctx.data.content ); // Done in assingToVM //ptr->gcMark( memPool->generation() ); } void Item::setGCPointer( GarbagePointer *shell ) { type( FLC_ITEM_GCPTR ); all.ctx.data.content = shell; assignToVm( shell ); // Done in assingToVM //shell->ptr()->gcMark( memPool->generation() ); } FalconData *Item::asGCPointer() const { return static_cast(all.ctx.data.content)->ptr(); } GarbagePointer *Item::asGCPointerShell() const { return static_cast(all.ctx.data.content); } //==================================================== // Safe items. // SafeItem::SafeItem( byte t, Garbageable *dt ) { type( t ); content( dt ); } void SafeItem::setRange( CoreRange *r ) { type( FLC_ITEM_RANGE ); all.ctx.data.content = r; } void SafeItem::setString( String *str ) { type( FLC_ITEM_STRING ); all.ctx.data.ptr.voidp = str; all.ctx.data.ptr.extra = 0; } void SafeItem::setArray( CoreArray *array ) { type( FLC_ITEM_ARRAY ); all.ctx.data.ptr.voidp = array; } void SafeItem::setObject( CoreObject *obj ) { type( FLC_ITEM_OBJECT ); all.ctx.data.ptr.voidp = obj; } void SafeItem::setDict( CoreDict *dict ) { type( FLC_ITEM_DICT ); all.ctx.data.ptr.voidp = dict; } void SafeItem::setMemBuf( MemBuf *b ) { type( FLC_ITEM_MEMBUF ); all.ctx.data.ptr.voidp = b; } void SafeItem::setReference( GarbageItem *ref ) { type( FLC_ITEM_REFERENCE ); all.ctx.data.ptr.voidp = ref; } void SafeItem::setFunction( CoreFunc* cf ) { type( FLC_ITEM_FUNC ); all.ctx.data.ptr.extra = cf; } void SafeItem::setLBind( String *lbind, GarbageItem *val ) { type( FLC_ITEM_LBIND ); all.ctx.data.ptr.voidp = lbind; all.ctx.data.ptr.extra = val; if ( val != 0 ) assignToVm( val ); } void SafeItem::setMethod( const Item &data, CallPoint *func ) { copy( data ); all.ctx.base.bits.oldType = all.ctx.base.bits.type; all.ctx.method = func; type( FLC_ITEM_METHOD ); assignToVm( func ); } void SafeItem::setClassMethod( CoreObject *obj, CoreClass *cls ) { type( FLC_ITEM_CLSMETHOD ); all.ctx.data.ptr.voidp = obj; all.ctx.data.ptr.extra = cls; } void SafeItem::setClass( CoreClass *cls ) { type( FLC_ITEM_CLASS ); // warning: class in extra to be omologue to methodClass() all.ctx.data.ptr.extra = cls; } void SafeItem::setGCPointer( FalconData *ptr ) { type( FLC_ITEM_GCPTR ); all.ctx.data.content = new GarbagePointer( ptr ); } void SafeItem::setGCPointer( GarbagePointer *shell ) { type( FLC_ITEM_GCPTR ); all.ctx.data.content = shell; } //=========================================================================== // Generic item manipulators bool Item::isTrue() const { switch( dereference()->type() ) { case FLC_ITEM_BOOL: return asBoolean() != 0; case FLC_ITEM_INT: return asInteger() != 0; case FLC_ITEM_NUM: return asNumeric() != 0.0; case FLC_ITEM_RANGE: return asRangeStart() != asRangeEnd() || asRangeIsOpen(); case FLC_ITEM_STRING: return asString()->size() != 0; case FLC_ITEM_ARRAY: return asArray()->length() != 0; case FLC_ITEM_DICT: return asDict()->length() != 0; case FLC_ITEM_FUNC: case FLC_ITEM_OBJECT: case FLC_ITEM_CLASS: case FLC_ITEM_METHOD: case FLC_ITEM_MEMBUF: case FLC_ITEM_LBIND: // methods are always filled, so they are always true. return true; } return false; } /* static int64 s_atoi( const String *cs ) { if ( cs->size() == 0 ) return 0; const char *p = (const char *)cs->getRawStorage() + ( cs->size() -1 ); uint64 val = 0; uint64 base = 1; while( p > (const char *)cs->getRawStorage() ) { if ( *p < '0' || *p > '9' ) { return 0; } val += (*p-'0') * base; p--; base *= 10; } if ( *p == '-' ) return -(int64)val; return (int64)(val*base); } */ int64 Item::forceInteger() const { switch( type() ) { case FLC_ITEM_INT: return asInteger(); case FLC_ITEM_NUM: return (int64) asNumeric(); case FLC_ITEM_RANGE: return (int64)asRangeStart(); case FLC_ITEM_STRING: { int64 tgt; if ( asString()->parseInt( tgt ) ) return tgt; return 0; } } return 0; } int64 Item::forceIntegerEx() const { switch( type() ) { case FLC_ITEM_INT: return asInteger(); case FLC_ITEM_NUM: return (int64) asNumeric(); } throw new TypeError( ErrorParam( e_param_type, __LINE__ ) ); // to make some dumb compiler happy return 0; } numeric Item::forceNumeric() const { switch( type() ) { case FLC_ITEM_INT: return (numeric) asInteger(); case FLC_ITEM_NUM: return asNumeric(); case FLC_ITEM_RANGE: return (numeric) asRangeStart(); case FLC_ITEM_STRING: { double tgt; if ( asString()->parseDouble( tgt ) ) return tgt; return 0.0; } } return 0.0; } bool Item::isOfClass( const String &className ) const { switch( type() ) { case FLC_ITEM_OBJECT: // objects may be classless or derived from exactly one class. return asObjectSafe()->derivedFrom( className ); case FLC_ITEM_CLASS: return className == asClass()->symbol()->name() || asClass()->derivedFrom( className ); } return false; } void Item::toString( String &target ) const { target.size(0); switch( this->type() ) { case FLC_ITEM_NIL: target = "Nil"; break; case FLC_ITEM_UNB: target = "_"; break; case FLC_ITEM_BOOL: target = asBoolean() ? "true" : "false"; break; case FLC_ITEM_INT: target.writeNumber( this->asInteger() ); break; case FLC_ITEM_RANGE: target = "["; target.writeNumber( (int64) this->asRangeStart() ); target += ":"; if ( ! this->asRangeIsOpen() ) { target.writeNumber( (int64) this->asRangeEnd() ); if ( this->asRangeStep() != 0 ) { target += ":"; target.writeNumber( (int64) this->asRangeStep() ); } } target += "]"; break; case FLC_ITEM_NUM: { target.writeNumber( this->asNumeric(), "%.16g" ); } break; case FLC_ITEM_MEMBUF: target = "MemBuf( "; target.writeNumber( (int64) this->asMemBuf()->length() ); target += ", "; target.writeNumber( (int64) this->asMemBuf()->wordSize() ); target += " )"; break; case FLC_ITEM_STRING: target = *asString(); break; case FLC_ITEM_LBIND: if ( isFutureBind() ) { String temp; asFutureBind().toString(temp); target = *asLBind() + "|" + temp; } else target = "&" + *asLBind(); break; case FLC_ITEM_REFERENCE: dereference()->toString( target ); break; case FLC_ITEM_OBJECT: target = "Object from " + asObjectSafe()->generator()->symbol()->name(); break; case FLC_ITEM_ARRAY: target = "Array"; break; case FLC_ITEM_DICT: target = "Dictionary"; break; case FLC_ITEM_FUNC: target = "Function " + this->asFunction()->symbol()->name(); break; case FLC_ITEM_CLASS: target = "Class " + this->asClass()->symbol()->name(); break; case FLC_ITEM_METHOD: { Item orig; this->getMethodItem( orig ); String temp; orig.dereference()->toString( temp ); target = "Method (" + temp + ")." + this->asMethodFunc()->name(); } break; case FLC_ITEM_CLSMETHOD: target = "ClsMethod " + this->asMethodClass()->symbol()->name(); break; default: target = ""; } } void Item::typeName( String &target ) const { target.size(0); switch( this->type() ) { case FLC_ITEM_NIL: target = "Nil"; break; case FLC_ITEM_UNB: target = "Unbound"; break; case FLC_ITEM_BOOL: target = "Bool"; break; case FLC_ITEM_INT: target = "Int"; break; case FLC_ITEM_RANGE: target = "Range"; break; case FLC_ITEM_NUM: target = "Numeric"; break; case FLC_ITEM_MEMBUF: target = "MemBuf"; break; case FLC_ITEM_STRING: target = "String"; break; case FLC_ITEM_LBIND: target = "LBind"; break; case FLC_ITEM_REFERENCE: target = "Reference"; break; case FLC_ITEM_OBJECT: target = asObjectSafe()->generator()->symbol()->name(); break; case FLC_ITEM_ARRAY: target = "Array"; break; case FLC_ITEM_DICT: target = "Dictionary"; break; case FLC_ITEM_FUNC: target = "Function"; break; case FLC_ITEM_CLASS: target = this->asClass()->symbol()->name(); break; case FLC_ITEM_METHOD: target = "Method"; break; case FLC_ITEM_CLSMETHOD: target = "ClsMethod"; break; default: target = ""; } } bool Item::methodize( const Item &self ) { Item *data = dereference(); switch( data->type() ) { case FLC_ITEM_FUNC: { data->setMethod( self, data->asFunction() ); } return true; case FLC_ITEM_ARRAY: { CoreArray& arr = *asArray(); // even if arr[0] is not an array, the check is harmless, as we check by ptr value. if ( arr.canBeMethod() && arr.length() > 0 && arr[0].asArray() != &arr && arr[0].isCallable() ) { data->setMethod( self, &arr ); return true; } } return false; } return false; } bool Item::isCallable() const { if ( isClass() || isFunction() || isMethod() ) return true; if( isObject() ) { return asObjectSafe()->hasProperty( OVERRIDE_OP_CALL ); } //a bit more complex: a callable array... if( type() == FLC_ITEM_ARRAY ) { CoreArray& arr = *asArray(); if ( arr.length() > 0 ) { // avoid infinite recursion. // even if arr[0] is not an array, the check is harmless, as we check by ptr value. return arr[0].asArray() != &arr && arr[0].isCallable(); } } // in all the other cases, the item is not callable return false; } bool Item::canBeMethod() const { if ( isFunction() || isMethod() ) return true; //a bit more complex: a callable array... if( type() == FLC_ITEM_ARRAY ) { CoreArray& arr = *asArray(); if ( ! arr.canBeMethod() ) return false; if ( arr.length() > 0 ) { // avoid infinite recursion. // even if arr[0] is not an array, the check is harmless, as we check by ptr value. return arr[0].asArray() != &arr && arr[0].isCallable(); } } // in all the other cases, the item is not callable return false; } const Item &Item::asFutureBind() const { return ((GarbageItem*)all.ctx.data.ptr.extra)->origin(); } Item &Item::asFutureBind() { return ((GarbageItem*)all.ctx.data.ptr.extra)->origin(); } CoreObject *Item::asObject() const { if ( ! isObject() ) throw new CodeError( ErrorParam( e_static_call, __LINE__ ) ); return (CoreObject *) all.ctx.data.ptr.voidp; } bool Item::exactlyEqual( const Item& other ) const { if ( type() != other.type() ) { return false; } switch( type() ) { case FLC_ITEM_NIL: case FLC_ITEM_UNB: return true; case FLC_ITEM_INT: return asInteger() == other.asInteger(); case FLC_ITEM_NUM: return asNumeric() == other.asNumeric(); case FLC_ITEM_RANGE: if( asRangeIsOpen() != other.asRangeIsOpen() ) return false; if ( asRangeStart() != other.asRangeStart() ) return false; if ( asRangeStep() != other.asRangeStep() ) return false; if ( ! asRangeIsOpen() && (asRangeEnd() != other.asRangeEnd() ) ) return false; return true; case FLC_ITEM_STRING: return *asString() == *other.asString(); case FLC_ITEM_METHOD: if ( asMethodFunc() == other.asMethodFunc() ) { return asMethodItem().exactlyEqual(other.asMethodItem() ); } return false; case FLC_ITEM_CLSMETHOD: if ( asObjectSafe() != other.asObjectSafe() ) return false; // fallthrough case FLC_ITEM_FUNC: case FLC_ITEM_CLASS: return asClass() == other.asClass(); } // the default is to check for the voidp element in data return asObjectSafe() == other.asObjectSafe(); } } /* end of item.cpp */ engine/item_co.cpp000066400000000000000000001721311176363201700144610ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: item_co.cpp Item Common Operations support. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 02 Jan 2009 21:03:39 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Falcon { // Generic fail void co_fail() { throw new TypeError( ErrorParam( e_invop ) ); } void co_fail_unb() { throw new TypeError( ErrorParam( e_invop_unb ) ); } //============================================================= // Add // void co_int_add( const Item& first, const Item& second, Item& third ) { switch( second.type() ) { case FLC_ITEM_INT: third = first.asInteger() + second.asInteger(); return; case FLC_ITEM_NUM: third = first.asInteger() + second.asNumeric(); return; case FLC_ITEM_REFERENCE: co_int_add( first, second.asReference()->origin(), third ); return; } throw new TypeError( ErrorParam( e_invop ).extra( "ADD" ) ); } void co_num_add( const Item& first, const Item& second, Item& third ) { switch( second.type() ) { case FLC_ITEM_INT: third = first.asNumeric() + second.asInteger(); return; case FLC_ITEM_NUM: third = first.asNumeric() + second.asNumeric(); return; case FLC_ITEM_REFERENCE: co_num_add( first, second.asReference()->origin(), third ); return; } throw new TypeError( ErrorParam( e_invop ).extra( "ADD" ) ); } void co_string_add( const Item& first, const Item& second, Item& third ) { String *sf = first.asString(); CoreString *dest = new CoreString( *sf ); // Clones also garbage status const Item *op2 = second.dereference(); if( op2->isString() ) { dest->append( *op2->asString() ); } else { String tgt; VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) vm->itemToString( tgt, op2 ); // may raise else op2->toString( tgt ); dest->append( tgt ); } third = dest; } void co_dict_add( const Item& first, const Item& second, Item& third ) { CoreDict *source = first.asDict(); const Item *op2 = second.dereference(); Item mth; if ( source->getMethod( OVERRIDE_OP_ADD, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->pushParam( *second.dereference() ); vm->callItemAtomic( mth, 1 ); third = vm->regA(); return; } } if ( op2->isDict() ) { // adjust for self-operations (do not clone if target == source) if( third.isDict() && third.asDict() == source ) { source->merge( *op2->asDict() ); } else { LinearDict *dict = new LinearDict( source->length() + op2->asDict()->length() ); dict->merge( source->items() ); dict->merge( op2->asDict()->items() ); third = new CoreDict( dict ); } return; } throw new TypeError( ErrorParam( e_invop ).extra( "ADD" ) ); } void co_array_add( const Item& first, const Item& second, Item& third ) { // adjust for self-operations (do not clone if target == source) CoreArray *array = third.isArray() && third.asArray() == first.asArray() ? first.asArray() : first.asArray()->clone(); const Item *op2 = second.dereference(); if ( op2->isArray() ) { array->merge( *op2->asArray() ); } else { if ( op2->isString() ) array->append( new CoreString( *op2->asString() ) ); else array->append( *op2 ); } third = array; } void co_object_add( const Item& first, const Item& second, Item& third ) { CoreObject *self = first.asObjectSafe(); Item mth; if ( self->getMethod( OVERRIDE_OP_ADD, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->pushParam( *second.dereference() ); vm->callItemAtomic( mth, 1 ); third = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_ADD ) ); } void co_ref_add( const Item& first, const Item& second, Item& third ) { Item& ref = first.asReference()->origin(); ref.add( second, third ); } void co_lbind_add( const Item& first, const Item& second, Item& third ) { Item& ref = first.asReference()->origin(); ref.add( second, third ); } void co_method_add( const Item& first, const Item& second, Item& third ) { if ( ! first.asMethodFunc()->isFunc() ) { co_array_add( dyncast(first.asMethodFunc()), second, third ); } else throw new TypeError( ErrorParam( e_invop ).extra( "ADD" ) ); } //============================================================= // Sub // void co_int_sub( const Item& first, const Item& second, Item& third ) { switch( second.type() ) { case FLC_ITEM_INT: third.setInteger( first.asInteger() - second.asInteger() ); return; case FLC_ITEM_NUM: third.setNumeric( first.asInteger() - second.asNumeric() ); return; case FLC_ITEM_REFERENCE: co_int_sub( first, second.asReference()->origin(), third ); return; } throw new TypeError( ErrorParam( e_invop ).extra("SUB") ); } void co_num_sub( const Item& first, const Item& second, Item& third ) { switch( second.type() ) { case FLC_ITEM_INT: third.setNumeric( first.asNumeric() - second.asInteger() ); return; case FLC_ITEM_NUM: third.setNumeric( first.asNumeric() - second.asNumeric() ); return; case FLC_ITEM_REFERENCE: co_num_sub( first, second.asReference()->origin(), third ); return; } throw new TypeError( ErrorParam( e_invop ).extra("SUB") ); } void co_array_sub( const Item& first, const Item& second, Item& third ) { // remove any element from an array CoreArray *source = first.asArray(); // accomodate for selfsub CoreArray *dest = third.isArray() && third.asArray() == source ? source : source->clone(); const Item *op2 = second.dereference(); // if we have an array, remove all of it if( op2->isArray() ) { CoreArray *removed = op2->asArray(); for( uint32 i = 0; i < removed->length(); i ++ ) { int32 rem = dest->find( removed->at(i) ); if( rem >= 0 ) dest->remove( rem ); } } else { int32 rem = dest->find( *op2 ); if( rem >= 0 ) dest->remove( rem ); } third = dest; } void co_dict_sub( const Item& first, const Item& second, Item& third ) { // remove various keys from arrays CoreDict *source = first.asDict(); // accomodate for selfsub CoreDict *dest = third.isDict() && third.asDict() == source ? source : source->clone(); const Item *op2 = second.dereference(); Item mth; if ( source->getMethod( OVERRIDE_OP_SUB, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->pushParam( *second.dereference() ); vm->callItemAtomic( mth, 1 ); third = vm->regA(); return; } } // if we have an array, remove all of it if( op2->isArray() ) { CoreArray *removed = op2->asArray(); for( uint32 i = 0; i < removed->length(); i ++ ) { dest->remove( removed->at(i) ); } } else { dest->remove( *op2 ); } // never raise. third = dest; } void co_object_sub( const Item& first, const Item& second, Item& third ) { CoreObject *self = first.asObjectSafe(); Item mth; if ( self->getMethod( OVERRIDE_OP_SUB, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->pushParam( *second.dereference() ); vm->callItemAtomic( mth, 1 ); third = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_SUB ) ); } void co_ref_sub( const Item& first, const Item& second, Item& third ) { Item& ref = first.asReference()->origin(); ref.sub( second, third ); } void co_method_sub( const Item& first, const Item& second, Item& third ) { if ( ! first.asMethodFunc()->isFunc() ) { co_array_sub( dyncast(first.asMethodFunc()), second, third ); } else throw new TypeError( ErrorParam( e_invop ).extra( "ADD" ) ); } //============================================================= // MUL // void co_int_mul( const Item& first, const Item& second, Item& third ) { switch( second.type() ) { case FLC_ITEM_INT: third = first.asInteger() * second.asInteger(); return; case FLC_ITEM_NUM: third = first.asInteger() * second.asNumeric(); return; case FLC_ITEM_REFERENCE: co_int_mul( first, second.asReference()->origin(), third ); return; } throw new TypeError( ErrorParam( e_invop ).extra( "MUL" ) ); } void co_num_mul( const Item& first, const Item& second, Item& third ) { switch( second.type() ) { case FLC_ITEM_INT: third = first.asNumeric() * second.asInteger(); return; case FLC_ITEM_NUM: third = first.asNumeric() * second.asNumeric(); return; case FLC_ITEM_REFERENCE: co_num_mul( first, second.asReference()->origin(), third ); return; } throw new TypeError( ErrorParam( e_invop ).extra( "MUL" ) ); } void co_string_mul( const Item& first, const Item& second, Item& third ) { switch( second.type() ) { case FLC_ITEM_INT: case FLC_ITEM_NUM: { String *str = first.asString(); int64 chr = second.forceInteger(); if ( chr >= 0 && chr <= (int64) 0xFFFFFFFF ) { CoreString *gcs = new CoreString( str->length() ); for ( int i = 0; i < chr; i ++ ) { gcs->append( *str ); } third = gcs; return; } break; } case FLC_ITEM_REFERENCE: co_string_mul( first, second.asReference()->origin(), third ); return; } throw new TypeError( ErrorParam( e_invop ).extra( "MUL" ) ); } void co_dict_mul( const Item& first, const Item& second, Item& third ) { CoreDict *self = first.asDict(); Item mth; if ( self->getMethod( OVERRIDE_OP_MUL, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->pushParam( *second.dereference() ); vm->callItemAtomic( mth, 1 ); third = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_MUL ) ); } void co_object_mul( const Item& first, const Item& second, Item& third ) { CoreObject *self = first.asObjectSafe(); Item mth; if ( self->getMethod( OVERRIDE_OP_MUL, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->pushParam( *second.dereference() ); vm->callItemAtomic( mth, 1 ); third = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_MUL ) ); } void co_ref_mul( const Item& first, const Item& second, Item& third ) { Item& ref = first.asReference()->origin(); ref.mul( second, third ); } //============================================================= // DIV // void co_int_div( const Item& first, const Item& second, Item& third ) { switch( second.type() ) { case FLC_ITEM_INT: { int64 val2 = second.asInteger(); if ( val2 == 0 ) { throw new MathError( ErrorParam( e_div_by_zero ) ); } third.setNumeric( first.asInteger() / (numeric)val2 ); return; } case FLC_ITEM_NUM: { numeric val2 = second.asNumeric(); if ( val2 == 0.0 ) { throw new MathError( ErrorParam( e_div_by_zero ) ); return; } third.setNumeric( first.asInteger() / val2 ); return; } case FLC_ITEM_REFERENCE: co_int_div( first, second.asReference()->origin(), third ); return; } throw new TypeError( ErrorParam( e_invop ).extra( "DIV" ) ); } void co_num_div( const Item& first, const Item& second, Item& third ) { switch( second.type() ) { case FLC_ITEM_INT: { int64 val2 = second.asInteger(); if ( val2 == 0 ) { throw new MathError( ErrorParam( e_div_by_zero ) ); } third.setNumeric( first.asNumeric() / (numeric)val2 ); return; } case FLC_ITEM_NUM: { numeric val2 = second.asNumeric(); if ( val2 == 0.0 ) { throw new MathError( ErrorParam( e_div_by_zero ) ); } third.setNumeric( first.asNumeric() / val2 ); return; } case FLC_ITEM_REFERENCE: co_num_div( first, second.asReference()->origin(), third ); return; } throw new TypeError( ErrorParam( e_invop ).extra( "DIV" ) ); } void co_string_div( const Item& first, const Item& second, Item& third ) { switch( second.type() ) { case FLC_ITEM_INT: case FLC_ITEM_NUM: { String *str = first.asString(); int64 chr = second.forceInteger(); uint32 len = str->length(); if ( chr >= -(int64) 0xFFFFFFFF && chr <= (int64) 0xFFFFFFFF && len > 0 ) { CoreString *gcs = new CoreString( *str ); gcs->setCharAt( len-1, gcs->getCharAt(len-1) + (uint32)chr ); third = gcs; return; } break; } case FLC_ITEM_REFERENCE: co_string_div( first, second.asReference()->origin(), third ); return; } throw new TypeError( ErrorParam( e_invop ).extra( "DIV" ) ); } void co_dict_div( const Item& first, const Item& second, Item& third ) { CoreDict *self = first.asDict(); Item mth; if ( self->getMethod( OVERRIDE_OP_DIV, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->pushParam( *second.dereference() ); vm->callItemAtomic( mth, 1 ); third = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_DIV ) ); } void co_object_div( const Item& first, const Item& second, Item& third ) { CoreObject *self = first.asObjectSafe(); Item mth; if ( self->getMethod( OVERRIDE_OP_DIV, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->pushParam( *second.dereference() ); vm->callItemAtomic( mth, 1 ); third = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_DIV ) ); } void co_ref_div( const Item& first, const Item& second, Item& third ) { Item& ref = first.asReference()->origin(); ref.div( second, third ); } //============================================================= // MOD // void co_int_mod( const Item& first, const Item& second, Item& third ) { switch( second.type() ) { case FLC_ITEM_INT: if ( second.asInteger() == 0 ) throw new MathError( ErrorParam( e_div_by_zero ).extra("MOD") ); third.setInteger( first.asInteger() % second.asInteger() ); return; case FLC_ITEM_NUM: if ( second.asNumeric() == 0.0 ) throw new MathError( ErrorParam( e_div_by_zero ).extra("MOD") ); third.setInteger( first.asInteger() % (int64) second.asNumeric() ); return; case FLC_ITEM_REFERENCE: co_int_mod( first, second.asReference()->origin(), third ); } throw new TypeError( ErrorParam( e_invop ).extra( "MOD" ) ); } void co_num_mod( const Item& first, const Item& second, Item& third ) { switch( second.type() ) { case FLC_ITEM_INT: if ( second.asInteger() == 0 ) throw new MathError( ErrorParam( e_div_by_zero ).extra("MOD") ); third.setInteger( ((int64)first.asNumeric()) % second.asInteger() ); return; case FLC_ITEM_NUM: if ( second.asNumeric() == 0.0 ) throw new MathError( ErrorParam( e_div_by_zero ).extra("MOD") ); third.setInteger( ((int64)first.asNumeric()) % (int64) second.asNumeric() ); return; case FLC_ITEM_REFERENCE: co_num_mod( first, second.asReference()->origin(), third ); } throw new TypeError( ErrorParam( e_invop ).extra( "MOD" ) ); } void co_string_mod( const Item& first, const Item& second, Item& third ) { String *str = first.asString(); switch( second.type() ) { case FLC_ITEM_INT: { int64 chr = second.asInteger(); if ( chr >= 0 && chr <= (int64) 0xFFFFFFFF ) { CoreString *gcs = new CoreString( *str ); gcs->append( (uint32) chr ); third = gcs; return; } break; } case FLC_ITEM_NUM: { numeric chr = second.asNumeric(); if ( chr >= 0 && chr <= (numeric) 0xFFFFFFFF ) { CoreString *gcs = new CoreString( *str ); gcs->append( (uint32) chr ); third = gcs; return; } break; } case FLC_ITEM_REFERENCE: co_string_mod( first, second.asReference()->origin(), third ); return; } throw new TypeError( ErrorParam( e_invop ).extra( "MOD" ) ); } void co_dict_mod( const Item& first, const Item& second, Item& third ) { CoreDict *self = first.asDict(); Item mth; if ( self->getMethod( OVERRIDE_OP_MOD, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->pushParam( *second.dereference() ); vm->callItemAtomic( mth, 1 ); third = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_MOD ) ); } void co_object_mod( const Item& first, const Item& second, Item& third ) { CoreObject *self = first.asObjectSafe(); Item mth; if ( self->getMethod( OVERRIDE_OP_MOD, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->pushParam( *second.dereference() ); vm->callItemAtomic( mth, 1 ); third = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_MOD ) ); } void co_ref_mod( const Item& first, const Item& second, Item& third ) { Item& ref = first.asReference()->origin(); ref.mod( second, third ); } //============================================================= // pow // void co_int_pow( const Item& first, const Item& second, Item& third ) { numeric powval; switch( second.type() ) { case FLC_ITEM_INT: if ( second.asInteger() == 0 ) { third = (int64) 1; return; } powval = pow( (double)first.asInteger(), (numeric) second.asInteger() ); break; case FLC_ITEM_NUM: if ( second.asNumeric() == 0.0 ) { third = (int64) 1; return; } powval = pow( (double)first.asInteger(), (numeric) second.asNumeric() ); break; case FLC_ITEM_REFERENCE: co_int_pow( first, second.asReference()->origin(), third ); return; default: throw new TypeError( ErrorParam( e_invop ).extra( "POW" ) ); } if ( errno != 0 ) { errno = 0; throw new MathError( ErrorParam( e_domain ).extra( "POW" ) ); } third = powval; } void co_num_pow( const Item& first, const Item& second, Item& third ) { numeric powval; switch( second.type() ) { case FLC_ITEM_INT: if ( second.asInteger() == 0 ) { third = (int64) 1; return; } powval = pow( first.asNumeric(), (numeric) second.asInteger() ); break; case FLC_ITEM_NUM: if ( second.asNumeric() == 0.0 ) { third = (int64) 1; return; } powval = pow( first.asNumeric(), (numeric) second.asNumeric() ); break; case FLC_ITEM_REFERENCE: co_num_pow( first, second.asReference()->origin(), third ); return; default: throw new TypeError( ErrorParam( e_invop ).extra( "POW" ) ); } if ( errno != 0 ) { errno = 0; throw new MathError( ErrorParam( e_domain ).extra( "POW" ) ); } third = powval; } void co_dict_pow( const Item& first, const Item& second, Item& third ) { CoreDict *self = first.asDict(); Item mth; if ( self->getMethod( OVERRIDE_OP_POW, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->pushParam( *second.dereference() ); vm->callItemAtomic( mth, 1 ); third = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_POW ) ); } void co_object_pow( const Item& first, const Item& second, Item& third ) { CoreObject *self = first.asObjectSafe(); Item mth; if ( self->getMethod( OVERRIDE_OP_POW, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->pushParam( *second.dereference() ); vm->callItemAtomic( mth, 1 ); third = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_POW ) ); } void co_ref_pow( const Item& first, const Item& second, Item& third ) { Item& ref = first.asReference()->origin(); ref.pow( second, third ); } //============================================================= // NEG // void co_int_neg( const Item& first, Item& tgt ) { tgt = -first.asInteger(); } void co_num_neg( const Item& first, Item& tgt ) { tgt = -first.asNumeric(); } void co_dict_neg( const Item& first, Item& tgt ) { CoreDict *self = first.asDict(); Item mth; if ( self->getMethod( OVERRIDE_OP_NEG, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->callItemAtomic( mth, 0 ); tgt = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_NEG ) ); } void co_object_neg( const Item& first, Item& third ) { CoreObject *self = first.asObjectSafe(); Item mth; if ( self->getMethod( OVERRIDE_OP_NEG, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->callItemAtomic( mth, 0 ); third = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_NEG ) ); } void co_ref_neg( const Item& first, Item &tgt ) { Item& ref = first.asReference()->origin(); ref.neg( tgt ); } //============================================================= // INC (prefix) // void co_int_inc( Item& first, Item &target ) { first = first.asInteger() + 1; target = first; } void co_num_inc( Item& first, Item &target ) { first = first.asNumeric() + 1.0; target = first; } void co_dict_inc( Item& first, Item &target ) { CoreDict *self = first.asDict(); Item mth; if ( self->getMethod( OVERRIDE_OP_INC, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->callItemAtomic( mth, 0 ); target = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_INC ) ); } void co_object_inc( Item& first, Item &target ) { CoreObject *self = first.asObjectSafe(); Item mth; if ( self->getMethod( OVERRIDE_OP_INC, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->callItemAtomic( mth, 0 ); target = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_INC ) ); } void co_ref_inc( Item& first, Item &target ) { Item& ref = first.asReference()->origin(); ref.inc(target); } //============================================================= // DEC (prefix) // void co_int_dec( Item& first, Item &target ) { first = first.asInteger() - 1; target = first; } void co_num_dec( Item& first, Item &target ) { first = first.asNumeric() - 1.0; target = first; } void co_dict_dec( Item& first, Item &target ) { CoreDict *self = first.asDict(); Item mth; if ( self->getMethod( OVERRIDE_OP_DEC, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->callItemAtomic( mth, 0 ); target = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_DEC ) ); } void co_object_dec( Item& first, Item &target ) { CoreObject *self = first.asObjectSafe(); Item mth; if ( self->getMethod( OVERRIDE_OP_DEC, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->callItemAtomic( mth, 0 ); target = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_DEC ) ); } void co_ref_dec( Item& first, Item &target ) { Item& ref = first.asReference()->origin(); ref.dec( target ); } //============================================================= // INC (postfix) // void co_int_incpost( Item& first, Item& tgt ) { tgt = first; first = first.asInteger() + 1; } void co_num_incpost( Item& first, Item& tgt ) { tgt = first; first = first.asNumeric() + 1.0; } void co_dict_incpost( Item& first, Item& third ) { CoreDict *self = first.asDict(); Item mth; if ( self->getMethod( OVERRIDE_OP_INCPOST, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->callItemAtomic( mth, 0 ); third = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_INCPOST ) ); } void co_object_incpost( Item& first, Item& third ) { CoreObject *self = first.asObjectSafe(); Item mth; if ( self->getMethod( OVERRIDE_OP_INCPOST, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->callItemAtomic( mth, 0 ); third = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_INCPOST ) ); } void co_ref_incpost( Item& first, Item &tgt ) { Item& ref = first.asReference()->origin(); ref.incpost( tgt ); } //============================================================= // DEC (postfix) // void co_int_decpost( Item& first, Item& tgt ) { tgt = first; first = first.asInteger() - 1; } void co_num_decpost( Item& first, Item& tgt ) { tgt = first; first = first.asNumeric() - 1.0; } void co_dict_decpost( Item& first, Item& third ) { CoreDict *self = first.asDict(); Item mth; if ( self->getMethod( OVERRIDE_OP_DECPOST, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->callItemAtomic( mth, 0 ); third = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_DECPOST ) ); } void co_object_decpost( Item& first, Item& third ) { CoreObject *self = first.asObjectSafe(); Item mth; if ( self->getMethod( OVERRIDE_OP_DECPOST, mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->callItemAtomic( mth, 0 ); third = vm->regA(); return; } } throw new TypeError( ErrorParam( e_invop ).extra( OVERRIDE_OP_DECPOST ) ); } void co_ref_decpost( Item& first, Item &tgt ) { Item& ref = first.asReference()->origin(); ref.decpost( tgt ); } //============================================================= // Compare // int co_nil_compare( const Item& first, const Item& second ) { switch( second.type() ) { case FLC_ITEM_REFERENCE: return co_nil_compare( first, second.asReference()->origin() ); case FLC_ITEM_UNB: const_cast(&second)->copy( first ); return 0; } return first.type() - second.type(); } int co_bool_compare( const Item& first, const Item& second ) { // true > false switch( second.type() ) { case FLC_ITEM_NIL: return 1; case FLC_ITEM_BOOL: return (first.asBoolean() ? 1 : 0) - (second.asBoolean() ? 1: 0); case FLC_ITEM_REFERENCE: return co_bool_compare( first, second.asReference()->origin() ); case FLC_ITEM_UNB: const_cast(&second)->copy( first ); return 0; } // bool is always lower return -1; } int co_int_compare( const Item& first, const Item& second ) { switch ( second.type() ) { case FLC_ITEM_NIL: case FLC_ITEM_BOOL: return 1; case FLC_ITEM_INT: { // converting the int to a 32 bit integer is not a simple matter int64 v = (first.asInteger() - second.asInteger()) ; if (v==0) return 0; if ( v > 0 ) return 1; return -1; } case FLC_ITEM_NUM: return (int)(((numeric)first.asInteger()) - second.asNumeric()); case FLC_ITEM_REFERENCE: return co_int_compare( first, second.asReference()->origin() ); case FLC_ITEM_UNB: const_cast(&second)->copy( first ); return 0; } // lower than the others return -1; } int co_num_compare( const Item& first, const Item& second ) { switch ( second.type() ) { case FLC_ITEM_NIL: case FLC_ITEM_BOOL: return 1; case FLC_ITEM_INT: if( first.asNumeric() < ((numeric)second.asInteger()) ) return -1; if( first.asNumeric() > ((numeric)second.asInteger()) ) return 1; return 0; case FLC_ITEM_NUM: if (first.asNumeric() < second.asNumeric()) return -1; if( first.asNumeric() > second.asNumeric()) return 1; return 0; case FLC_ITEM_REFERENCE: return co_num_compare( first, second.asReference()->origin() ); case FLC_ITEM_UNB: const_cast(&second)->copy( first ); return 0; } // lower than the others return -1; } int co_range_compare( const Item& first, const Item& second ) { switch ( second.type() ) { case FLC_ITEM_NIL: case FLC_ITEM_BOOL: case FLC_ITEM_INT: case FLC_ITEM_NUM: return 1; case FLC_ITEM_RANGE: { int64 diff = first.asRangeStart() - second.asRangeStart(); if ( diff != 0 ) return (int)(diff>>32); // always greater diff = (first.asRangeIsOpen() ? 1: 0) - (second.asRangeIsOpen() ? 1: 0); if ( diff != 0 ) return (int)(diff); if ( first.asRangeIsOpen() ) return 0; diff = first.asRangeEnd() - second.asRangeEnd(); if ( diff != 0 ) return (int)(diff>>32); return (int)((first.asRangeStep() - second.asRangeStep())>>32); } break; case FLC_ITEM_REFERENCE: return co_range_compare( first, second.asReference()->origin() ); case FLC_ITEM_UNB: const_cast(&second)->copy( first ); return 0; } return -1; } int co_lbind_compare( const Item& first, const Item& second ) { switch( second.type() ) { case FLC_ITEM_REFERENCE: return co_lbind_compare( first, second.asReference()->origin() ); case FLC_ITEM_LBIND: return (int)(first.asLBind() - second.asLBind()); case FLC_ITEM_UNB: const_cast(&second)->copy( first ); return 0; } return first.type() - second.type(); } int co_func_compare( const Item& first, const Item& second ) { switch( second.type() ) { case FLC_ITEM_REFERENCE: return co_func_compare( first, second.asReference()->origin() ); case FLC_ITEM_FUNC: return first.asFunction()->name().compare(second.asFunction()->name()); case FLC_ITEM_UNB: const_cast(&second)->copy( first ); return 0; } return first.type() - second.type(); } int co_gcptr_compare( const Item& first, const Item& second ) { if ( second.isReference() ) { return co_gcptr_compare( first, second.asReference()->origin() ); } else if ( second.isGCPointer() ) { return (int)(first.asGCPointer() - second.asGCPointer()); } else if ( second.isUnbound() ) { const_cast(&second)->copy( first ); return 0; } return first.type() - second.type(); } int co_string_compare( const Item& first, const Item& second ) { if ( second.isReference() ) { return co_string_compare( first, second.asReference()->origin() ); } else if ( second.isString() ) { return first.asString()->compare( *second.asString() ); } else if ( second.isUnbound() ) { const_cast(&second)->copy( first ); return 0; } return first.type() - second.type(); } int co_array_compare( const Item& first, const Item& second ) { if ( second.isReference() ) { return co_array_compare( first, second.asReference()->origin() ); } else if ( second.isArray() ) { return first.asArray()->compare( *second.asArray() ); } else if ( second.isUnbound() ) { const_cast(&second)->copy( first ); return 0; } return (first.type() - second.type()); } int co_dict_compare( const Item& first, const Item& second ) { CoreDict *self = first.asDict(); const Item* psecond = second.dereference(); if ( second.isUnbound() ) { const_cast(&second)->copy( first ); return 0; } Item mth; if ( self->getMethod( "compare", mth ) ) { VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { vm->pushParam( *psecond ); vm->callItemAtomic( mth, 1 ); if ( ! vm->regA().isNil() ) return (int) vm->regA().forceInteger(); } } if ( psecond->isDict() ) { return first.asDict()->compare(psecond->asDict()); } return first.type() - psecond->type(); } int co_object_compare( const Item& first, const Item& second ) { if ( second.isReference() ) { return co_object_compare( first, second.asReference()->origin() ); } else if ( second.isUnbound() ) { const_cast(&second)->copy( first ); return 0; } else { CoreObject *self = first.asObjectSafe(); // we're possibly calling a deep method, // and second may come from the stack. Item temp = second; // do we have an active VM? VMachine *vm = VMachine::getCurrent(); if ( vm != 0 ) { // first provides a less operator? Item mth; if ( self->getMethod( "compare", mth ) ) { vm->pushParam( temp ); vm->callItemAtomic( mth, 1 ); if ( vm->regA().isInteger() ) { return (int)vm->regA().asInteger(); } } } // by fallback -- use normal ordering. if( temp.isObject() ) return (int)(self - temp.asObjectSafe()); } return first.type() - second.type(); } int co_membuf_compare( const Item& first, const Item& second ) { if ( second.isReference() ) { return co_membuf_compare( first, second.asReference()->origin() ); } else if ( second.isMemBuf() ) { return (int)(first.asMemBuf() - second.asMemBuf()); } else if ( second.isUnbound() ) { const_cast(&second)->copy( first ); return 0; } return (int)(first.type() - second.type()); } int co_ref_compare( const Item& first, const Item& second ) { const Item &ref = first.asReference()->origin(); return ref.compare( second ); } int co_clsmethod_compare( const Item& first, const Item& second ) { if ( second.isReference() ) { return co_clsmethod_compare( first, second.asReference()->origin() ); } else if ( second.isClassMethod() ) { return (int)(first.asMethodFunc() - second.asMethodFunc()); } else if ( second.isUnbound() ) { const_cast(&second)->copy( first ); return 0; } return first.type() - second.type(); } int co_method_compare( const Item& first, const Item& second ) { if ( second.isReference() ) { return co_method_compare( first, second.asReference()->origin() ); } else if ( second.isMethod() ) { int cp = first.asMethodItem().compare(second.asMethodItem()); if ( cp == 0 ) return (int)(first.asMethodFunc() - second.asMethodFunc()); return cp; } else if ( second.isUnbound() ) { const_cast(&second)->copy( first ); return 0; } return first.type() - second.type(); } int co_class_compare( const Item& first, const Item& second ) { if ( second.isReference() ) { return co_class_compare( first, second.asReference()->origin() ); } else if ( second.isClass() ) { return (int)(first.asClass()->symbol()->name().compare(second.asClass()->symbol()->name())); } else if ( second.isUnbound() ) { const_cast(&second)->copy( first ); return 0; } return (int)(first.type() - second.type()); } int co_unb_compare( const Item& first, const Item& second ) { if ( second.isReference() ) return co_unb_compare( first, second.asReference()->origin() ); const_cast(&first)->copy( second ); return 0; } //============================================================= // Get Index // void co_range_getindex( const Item &item, const Item &idx, Item &result ) { int32 pos; switch( idx.type() ) { case FLC_ITEM_INT: pos = (int32) idx.asInteger(); break; case FLC_ITEM_NUM: pos = (int32) idx.asNumeric(); break; case FLC_ITEM_REFERENCE: co_range_getindex( item, idx.asReference()->origin(), result ); return; default: throw new AccessError( ErrorParam( e_arracc ).extra( "LDV" ) ); } switch( pos ) { case -3: case 0: result = (int64) item.asRangeStart(); return; case 1: case -2: if( item.asRangeIsOpen() ) result.setNil(); else result = (int64) item.asRangeEnd(); return; case 2: case -1: if( item.asRangeIsOpen() ) result.setNil(); else result = (int64) item.asRangeStep(); return; } throw new AccessError( ErrorParam( e_arracc ).extra( "LDV" ) ); } void co_string_getindex( const Item &item, const Item &idx, Item &result ) { item.asString()->readIndex( idx, result ); } void co_deep_getindex( const Item &item, const Item &idx, Item &result ) { item.asDeepItem()->readIndex( idx, result ); } void co_method_getindex( const Item &item, const Item &idx, Item &result ) { if ( ! item.asMethodFunc()->isFunc() ) { // an array dyncast(item.asMethodFunc())->readIndex( idx, result ); } else throw new AccessError( ErrorParam( e_arracc ).extra( "LDV" ) ); } void co_ref_getindex( const Item &item, const Item &idx, Item &result ) { item.asReference()->origin().getIndex( idx, result ); } //============================================================= // Set Index // void co_range_setindex( Item &item, const Item &idx, const Item &nval ) { int32 pos; switch( idx.type() ) { case FLC_ITEM_INT: pos = (int32) idx.asInteger(); break; case FLC_ITEM_NUM: pos = (int32) idx.asNumeric(); break; case FLC_ITEM_REFERENCE: co_range_setindex( item, idx.asReference()->origin(), nval ); return; default: throw new AccessError( ErrorParam( e_arracc ).extra( "STV" ) ); } int32 value; switch( nval.type() ) { case FLC_ITEM_NIL: if ( pos == -1 || pos == 2 ) item.asRange()->setOpen(); // no more things to do, let's return. return; case FLC_ITEM_INT: value = (int32) nval.asInteger(); break; case FLC_ITEM_NUM: value = (int32) nval.asNumeric(); break; case FLC_ITEM_REFERENCE: co_range_setindex( item, idx, nval.asReference()->origin() ); return; default: throw new TypeError( ErrorParam( e_param_type ).extra( "STV" ) ); } CoreRange *cr = item.asRange(); switch( pos ) { case -3: case 0: item.setRange( new CoreRange( value, cr->end(), cr->step() ) ); return; case 1: case -2: item.setRange( new CoreRange( cr->start(), value, cr->step() ) ); return; case 2: case -1: item.setRange( new CoreRange( cr->start(), cr->end(), value ) ); return; } throw new AccessError( ErrorParam( e_arracc ).extra( "STV" ) ); } void co_string_setindex( const Item &item, const Item &idx, Item &result ) { // alter the item so that it is now a core string. if( ! item.asString()->isCore() ) { const_cast(&item )->setString( new CoreString( *item.asString() ) ); } item.asString()->writeIndex( idx, result ); } void co_deep_setindex( Item &item, const Item &idx, Item &result ) { item.asDeepItem()->writeIndex( idx, result ); } void co_method_setindex( const Item &item, const Item &idx, Item &result ) { if ( ! item.asMethodFunc()->isFunc() ) { // an array dyncast(item.asMethodFunc())->writeIndex( idx, result ); } else throw new AccessError( ErrorParam( e_arracc ).extra( "STV" ) ); } void co_ref_setindex( Item &item, const Item &idx, Item &result ) { item.asReference()->origin().setIndex( idx, result ); } //============================================================= // Get Property // void co_generic_getproperty( const Item &item, const String &prop, Item &result ) { VMachine* vm = VMachine::getCurrent(); fassert( vm != 0 ); CoreClass* cs = vm->getMetaClass( item.type() ); if ( cs != 0 ) { uint32 pos; if( cs->properties().findKey( prop, pos ) ) { Item *prop = cs->properties().getValue( pos ); fassert( prop->isFunction() ); result.setMethod( item, prop->asFunction() ); return; } } String extra; item.typeName( extra ); extra.A( '.' ).A( prop ); throw new AccessError( ErrorParam( e_prop_acc, __LINE__ ).extra( extra ) ); } void co_string_getproperty( const Item &item, const String &prop, Item &result ) { item.asString()->readProperty( prop, result ); } void co_deep_getproperty( const Item &item, const String &prop, Item &result ) { item.asDeepItem()->readProperty( prop, result ); } void co_ref_getproperty( const Item &item, const String &idx, Item &result ) { item.asReference()->origin().getProperty( idx, result ); } void co_method_getproperty( const Item &item, const String &prop, Item &result ) { VMachine* vm = VMachine::getCurrent(); fassert( vm != 0 ); CoreClass* cs = vm->getMetaClass( FLC_ITEM_METHOD ); if ( cs != 0 ) { uint32 pos; if( cs->properties().findKey( prop, pos ) ) { Item *prop = cs->properties().getValue( pos ); fassert( prop->isFunction() ); result.setMethod( new GarbageItem( item ), prop->asFunction() ); return; } } String extra; item.typeName( extra ); extra.A( '.' ).A( prop ); throw new AccessError( ErrorParam( e_prop_acc, __LINE__ ).extra( extra ) ); } void co_classmeth_getproperty( const Item &item, const String &idx, Item &target ) { CoreClass* sourceClass = item.asMethodClass(); CoreObject *self = item.asMethodClassOwner(); uint32 pos; if( sourceClass->properties().findKey( idx, pos ) ) { Item *prop = sourceClass->properties().getValue( pos ); // now, accessing a method in a class means that we want to call the base method in a // self item: switch( prop->type() ) { case FLC_ITEM_FUNC: // the function may be a dead function; by so, the method will become a dead method, // and it's ok for us. target.setMethod( self, prop->asFunction() ); break; case FLC_ITEM_CLASS: target.setClassMethod( self, prop->asClass() ); break; default: target = *prop; } return; } // try to find a generic method VMachine* vm = VMachine::getCurrent(); fassert( vm != 0 ); CoreClass* cs = vm->getMetaClass( FLC_ITEM_CLSMETHOD ); if ( cs != 0 ) { uint32 pos; if( cs->properties().findKey( idx, pos ) ) { Item *prop = cs->properties().getValue( pos ); fassert( prop->isFunction() ); target.setMethod( new GarbageItem( item ), prop->asFunction() ); return; } } String extra; item.typeName( extra ); extra.A( '.' ).A( idx ); throw new AccessError( ErrorParam( e_prop_acc, __LINE__ ).extra( extra ) ); } void co_class_getproperty( const Item &item, const String &idx, Item &result ) { CoreClass *sourceClass = item.asClass(); uint32 pos; if( sourceClass->properties().findKey( idx, pos ) ) { Item *prop = sourceClass->properties().getValue( pos ); switch( prop->type() ) { // it is legal to get methods in classes; We can have static methods. case FLC_ITEM_FUNC: { result.setMethod( item, prop->asFunction() ); } break; // getting a class in a class creates a classMethod, as getting a class in // an object. case FLC_ITEM_CLASS: { VMachine* vm = VMachine::getCurrent(); fassert( vm != 0 ); if ( vm->self().isObject() ) result.setClassMethod( vm->self().asObjectSafe(), prop->asClass() ); else result = *prop; } break; default: result = *prop; } return; } // try to find a generic method VMachine* vm = VMachine::getCurrent(); fassert( vm != 0 ); CoreClass* cc = vm ->getMetaClass( FLC_ITEM_CLASS ); uint32 id; if ( cc != 0 && cc->properties().findKey( idx, id ) ) { Item* p = cc->properties().getValue( id ); fassert( ! p->isReference() ); result.setMethod( sourceClass, p->asFunction() ); return; } String extra; item.typeName( extra ); extra.A( '.' ).A( idx ); throw new AccessError( ErrorParam( e_prop_acc, __LINE__ ).extra( extra ) ); } //============================================================= // Write Property // void co_deep_setproperty( Item &item, const String &idx, const Item &result ) { item.asDeepItem()->writeProperty( idx, result ); } void co_ref_setproperty( Item &item, const String &idx, const Item &result ) { item.asReference()->origin().setProperty( idx, result ); } void co_class_setproperty( Item &item, const String &idx, const Item &result ) { // is the target property a static property -- it must be a reference. PropertyTable &pt = item.asClass()->properties(); uint32 propId; if ( pt.findKey( idx, propId ) ) { Item *prop = pt.getValue( propId ); if ( prop->isReference() ) { if ( result.isString() ) { *prop->dereference() = new CoreString( *result.asString() ); } else *prop->dereference() = *result.dereference(); return; } else { throw new AccessError( ErrorParam( e_prop_ro ).origin( e_orig_vm ) .extra( idx ) ); return; } } String extra; item.typeName( extra ); extra.A( '.' ).A( idx ); throw new AccessError( ErrorParam( e_prop_acc ).origin( e_orig_vm ). extra( extra ) ); } //============================================================= // Call // void co_call_uncallable( VMachine *vm, uint32 paramCount ) { //TODO: Useful? -- on throw we either unroll or close the VM... /* if ( paramCount != 0 ) vm->currentStack().resize( vm->currentStack().size() - paramCount ); */ // TODO: correct error. throw new TypeError( ErrorParam( e_invop ).extra("CALL") ); } void co_call_function( const Item &itm, VMachine *vm, uint32 paramCount ) { // fill - in the missing parameters. vm->prepareFrame( itm.asFunction(), paramCount ); vm->currentContext()->fself() = itm; // leave self as is. } void co_call_reference( const Item &itm, VMachine *vm, uint32 paramCount ) { itm.asReference()->origin().readyFrame( vm, paramCount ); } void co_call_array( const Item &itm, VMachine *vm, uint32 paramCount ) { CoreArray *arr = itm.asArray(); if ( arr->length() != 0 ) { const Item &carr = arr->at(0); if ( carr.asArray() != arr && carr.isCallable() ) { vm->prepareFrame( arr, paramCount ); vm->currentContext()->fself() = itm; // the prepare for array already checks for self -- tables if ( arr->table() != 0 ) vm->self() = arr->table(); return; } } // TODO: correct error. throw new TypeError( ErrorParam( e_invop ).extra("CALL") ); } void co_call_dict( const Item &itm, VMachine *vm, uint32 paramCount ) { // find the call__ member, if it exists. CoreDict *self = itm.asDict(); Item *mth; if ( (mth = self->find( OVERRIDE_OP_CALL ) ) != 0 && mth->isCallable() ) { mth->readyFrame( vm, paramCount ); vm->self() = self; return; } // TODO: correct error. throw new TypeError( ErrorParam( e_invop ).extra("CALL") ); } void co_call_object( const Item &itm, VMachine *vm, uint32 paramCount ) { // find the call__ member, if it exists. CoreObject *self = itm.asObjectSafe(); Item mth; if ( self->getProperty( OVERRIDE_OP_CALL, mth ) && mth.isCallable() ) { mth.readyFrame( vm, paramCount ); vm->self() = self; return; } // TODO: correct error. throw new TypeError( ErrorParam( e_invop ).extra("CALL") ); } void co_call_method( const Item &itm, VMachine *vm, uint32 paramCount ) { // fill - in the missing parameters. itm.asMethodFunc()->readyFrame( vm, paramCount ); itm.getMethodItem( vm->self() ); } static bool __call_init_enter_last( VMachine *vm ) { vm->regA() = vm->self(); return false; } static bool __call_init_enter( VMachine *vm ) { Item initEnter; if( vm->self().asObject()->getMethod( "__enter", initEnter ) ) { vm->returnHandler(0); vm->callFrame( initEnter, 0 ); return true; } vm->regA() = vm->self(); return false; } void co_call_class( const Item &itm, VMachine *vm, uint32 paramCount ) { CoreClass *cls = itm.asClass(); CoreObject* inst = cls->createInstance(); fassert( inst != 0 ); // if the class has not a constructor, we just set the item in A // and return if ( cls->constructor().isNil() ) { // we are sure it's a core object vm->regA().setObject( inst ); // pop the stack vm->stack().resize( vm->stack().length() - paramCount ); if( cls->hasInitEnter() ) { Item initEnter; if( inst->getProperty( "__enter", initEnter ) && initEnter.isFunction() ) { vm->prepareFrame( initEnter.asFunction(), 0 ); vm->currentContext()->fself() = itm; vm->self() = inst; inst->gcMark( memPool->generation() ); vm->returnHandler( __call_init_enter_last ); } } return; } vm->prepareFrame( cls->constructor().asFunction(), paramCount ); vm->currentContext()->fself() = itm; vm->self() = inst; inst->gcMark( memPool->generation() ); // also return self; we need to tell it to the VM vm->requestConstruct(); if ( cls->hasInitEnter() ) { vm->returnHandler( __call_init_enter ); } } //============================================================= // Tables declaration // void* NilCommOpsTable[] = { (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // neg (void*) co_fail, // Inc/dec (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // cfr (void*) co_nil_compare, // set deep (void*) co_fail, (void*) co_fail, (void*) co_generic_getproperty, (void*) co_fail, //call (void*) co_call_uncallable }; void* IntCommOpsTable[] = { (void*) co_int_add, (void*) co_int_sub, (void*) co_int_mul, (void*) co_int_div, (void*) co_int_mod, (void*) co_int_pow, (void*) co_int_neg, // Inc/dec (void*) co_int_inc, (void*) co_int_dec, (void*) co_int_incpost, (void*) co_int_decpost, // cfr (void*) co_int_compare, // set deep (void*) co_fail, (void*) co_fail, (void*) co_generic_getproperty, (void*) co_fail, //call (void*) co_call_uncallable }; void* NumCommOpsTable[] = { (void*) co_num_add, (void*) co_num_sub, (void*) co_num_mul, (void*) co_num_div, (void*) co_num_mod, (void*) co_num_pow, (void*) co_num_neg, // Inc/dec (void*) co_num_inc, (void*) co_num_dec, (void*) co_num_incpost, (void*) co_num_decpost, // cfr (void*) co_num_compare, // set deep (void*) co_fail, (void*) co_fail, (void*) co_generic_getproperty, (void*) co_fail, //call (void*) co_call_uncallable }; void* RangeCommOpsTable[] = { (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // Inc/dec (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // cfr (void*) co_range_compare, // set deep (void*) co_range_getindex, (void*) co_range_setindex, (void*) co_generic_getproperty, (void*) co_fail, //call (void*) co_call_uncallable }; void* BoolCommOpsTable[] = { (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // Inc/dec (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // cfr (void*) co_bool_compare, // set deep (void*) co_fail, (void*) co_fail, (void*) co_generic_getproperty, (void*) co_fail, //call (void*) co_call_uncallable }; void* LBindCommOpsTable[] = { (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // Inc/dec (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // cfr (void*) co_lbind_compare, // set deep (void*) co_fail, (void*) co_fail, (void*) co_generic_getproperty, (void*) co_fail, //call (void*) co_call_uncallable }; void* FuncCommOpsTable[] = { (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // Inc/dec (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // cfr (void*) co_func_compare, // set deep (void*) co_fail, (void*) co_fail, (void*) co_generic_getproperty, (void*) co_fail, //call (void*) co_call_function }; void* GCPTRCommOpsTable[] = { (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // Inc/dec (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // cfr (void*) co_gcptr_compare, // set deep (void*) co_fail, (void*) co_fail, (void*) co_generic_getproperty, (void*) co_fail, //call (void*) co_call_uncallable }; void* StringCommOpsTable[] = { (void*) co_string_add, (void*) co_fail, (void*) co_string_mul, (void*) co_string_div, (void*) co_string_mod, (void*) co_fail, (void*) co_fail, // Inc/dec (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // cfr (void*) co_string_compare, // set deep (void*) co_string_getindex, (void*) co_string_setindex, (void*) co_string_getproperty, (void*) co_fail, //call (void*) co_call_uncallable }; void* ArrayCommOpsTable[] = { (void*) co_array_add, (void*) co_array_sub, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // Inc/dec (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // cfr (void*) co_array_compare, // set deep (void*) co_deep_getindex, (void*) co_deep_setindex, (void*) co_deep_getproperty, (void*) co_deep_setproperty, //call (void*) co_call_array }; void* DictCommOpsTable[] = { (void*) co_dict_add, (void*) co_dict_sub, (void*) co_dict_mul, (void*) co_dict_div, (void*) co_dict_mod, (void*) co_dict_pow, (void*) co_dict_neg, // Inc/dec (void*) co_dict_inc, (void*) co_dict_dec, (void*) co_dict_incpost, (void*) co_dict_decpost, // cfr (void*) co_dict_compare, // set deep (void*) co_deep_getindex, (void*) co_deep_setindex, (void*) co_deep_getproperty, (void*) co_deep_setproperty, //call (void*) co_call_dict }; void* ObjectCommOpsTable[] = { (void*) co_object_add, (void*) co_object_sub, (void*) co_object_mul, (void*) co_object_div, (void*) co_object_mod, (void*) co_object_pow, (void*) co_object_neg, // Inc/dec (void*) co_object_inc, (void*) co_object_dec, (void*) co_object_incpost, (void*) co_object_decpost, // cfr (void*) co_object_compare, // set deep (void*) co_deep_getindex, (void*) co_deep_setindex, (void*) co_deep_getproperty, (void*) co_deep_setproperty, //call (void*) co_call_object }; void* MembufCommOpsTable[] = { (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // Inc/dec (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // cfr (void*) co_membuf_compare, // set deep (void*) co_deep_getindex, (void*) co_deep_setindex, (void*) co_deep_getproperty, (void*) co_deep_setproperty, //call (void*) co_call_uncallable }; void* ReferenceCommOpsTable[] = { (void*) co_ref_add, (void*) co_ref_sub, (void*) co_ref_mul, (void*) co_ref_div, (void*) co_ref_mod, (void*) co_ref_pow, (void*) co_ref_neg, (void*) co_ref_inc, (void*) co_ref_dec, (void*) co_ref_incpost, (void*) co_ref_decpost, // cfr (void*) co_ref_compare, // set deep (void*) co_ref_getindex, (void*) co_ref_setindex, (void*) co_ref_getproperty, (void*) co_ref_setproperty, //call (void*) co_call_reference }; void* ClsMethodCommOpsTable[] = { (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // Inc/dec (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // cfr (void*) co_clsmethod_compare, // set deep (void*) co_fail, (void*) co_fail, (void*) co_classmeth_getproperty, (void*) co_fail, //call -- act as a class // this is possible as long as asMethodClass() === asClass() (void*) co_call_class }; void* MethodCommOpsTable[] = { (void*) co_method_add, (void*) co_method_sub, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // Inc/dec (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // cfr (void*) co_method_compare, // set deep (void*) co_method_getindex, (void*) co_method_setindex, (void*) co_method_getproperty, (void*) co_fail, //call (void*) co_call_method }; void* ClassCommOpsTable[] = { (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // Inc/dec (void*) co_fail, (void*) co_fail, (void*) co_fail, (void*) co_fail, // cfr (void*) co_class_compare, // set deep (void*) co_fail, (void*) co_fail, (void*) co_class_getproperty, (void*) co_class_setproperty, //call (void*) co_call_class }; void* UnbCommOpsTable[] = { (void*) co_fail_unb, (void*) co_fail_unb, (void*) co_fail_unb, (void*) co_fail_unb, (void*) co_fail_unb, (void*) co_fail_unb, (void*) co_fail_unb, // Inc/dec (void*) co_fail_unb, (void*) co_fail_unb, (void*) co_fail_unb, (void*) co_fail_unb, // cfr (void*) co_unb_compare, // set deep (void*) co_fail_unb, (void*) co_fail_unb, (void*) co_generic_getproperty, (void*) co_fail_unb, //call (void*) co_fail_unb }; CommOpsTable CommOpsDict[] = { NilCommOpsTable, BoolCommOpsTable, IntCommOpsTable, NumCommOpsTable, RangeCommOpsTable, LBindCommOpsTable, FuncCommOpsTable, GCPTRCommOpsTable, StringCommOpsTable, ArrayCommOpsTable, DictCommOpsTable, ObjectCommOpsTable, MembufCommOpsTable, ReferenceCommOpsTable, ClsMethodCommOpsTable, MethodCommOpsTable, ClassCommOpsTable, UnbCommOpsTable }; } /* end of item_co.cpp */ engine/itemarray.cpp000066400000000000000000000411571176363201700150420ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: itemarray.cpp Basic item array structure (sequence). ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 27 Jul 2009 20:48:24 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Implementation of core arrays. Core arrays are meant to hold item pointers. */ #include #include #include #include #include #include #include #include #include namespace Falcon { ItemArray::ItemArray(): m_alloc(0), m_size(0), m_data(0), m_growth( flc_ARRAY_GROWTH ), m_owner(0) {} ItemArray::ItemArray( const ItemArray& other ): m_growth( other.m_growth ), m_owner( 0 ) { if( other.m_size != 0 ) { m_alloc = other.m_size; m_size = other.m_size; m_data = (Item *) memAlloc( esize(other.m_size) ); memcpy( m_data, other.m_data, esize(other.m_size) ); // duplicate strings for ( uint32 i = 0; i < m_size; ++i ) { Item& item = m_data[i]; if( item.isString() && item.asString()->isCore() ) { item = new CoreString( *item.asString() ); } } } else { m_alloc = 0; m_size = 0; m_data = 0; } } ItemArray::ItemArray( uint32 prealloc ): m_growth( prealloc == 0 ? flc_ARRAY_GROWTH : prealloc ), m_owner( 0 ) { if( m_growth < 4 ) m_growth = 4; m_data = (Item *) memAlloc( esize(m_growth) ); m_alloc = m_growth; m_size = 0; } ItemArray::ItemArray( Item *buffer, uint32 size, uint32 alloc ): m_alloc(alloc), m_size(size), m_data(buffer), m_growth( flc_ARRAY_GROWTH ), m_owner( 0 ) {} ItemArray::~ItemArray() { if ( m_data != 0 ) memFree( m_data ); } void ItemArray::gcMark( uint32 mark ) { Sequence::gcMark( mark ); for( uint32 pos = 0; pos < m_size; pos++ ) { memPool->markItem( m_data[pos] ); } } void ItemArray::append( const Item &ndata ) { // create enough space to hold the data if ( m_alloc <= m_size ) { // we don't know where the item is coming from; it may come from also from our thing. Item copy = ndata; m_alloc = m_size + m_growth; m_data = (Item *) memRealloc( m_data, esize( m_alloc ) ); m_data[ m_size ] = copy; } else { m_data[ m_size ] = ndata; } m_size++; } void ItemArray::merge( const ItemArray &other ) { if ( other.m_size == 0 ) return; if ( m_alloc < m_size + other.m_size ) { m_alloc = m_size + other.m_size; m_data = (Item *) memRealloc( m_data, esize( m_alloc ) ); } memcpy( m_data + m_size, other.m_data, esize( other.m_size ) ); m_size += other.m_size; } void ItemArray::prepend( const Item &ndata ) { // create enough space to hold the data Item *mem = (Item *) memAlloc( esize(m_size + 1) ); m_alloc = m_size + 1; if ( m_size != 0 ) memcpy( mem + 1, m_data, esize( m_size ) ); mem[0] = ndata; if ( m_size != 0 ) memFree( m_data ); m_data = mem; m_size++; invalidateAllIters(); } void ItemArray::merge_front( const ItemArray &other ) { if ( other.m_size == 0 ) return; if ( m_alloc < m_size + other.m_size ) { m_alloc = m_size + other.m_size; Item *mem = (Item *) memAlloc( esize( m_alloc ) ); memcpy( mem , other.m_data, esize( other.m_size ) ); if ( m_size > 0 ) { memcpy( mem + other.m_size, m_data, esize( m_size ) ); memFree( m_data ); } m_size = m_alloc; m_data = mem; } else { memmove( m_data + other.m_size, m_data, esize( m_size ) ); memcpy( m_data, other.m_data, esize( other.m_size ) ); m_size += other.m_size; } invalidateAllIters(); } bool ItemArray::insert( const Item &ndata, int32 pos ) { if ( pos < 0 ) pos = m_size + pos; if ( pos < 0 || pos > (int32) m_size ) return false; if ( m_alloc <= m_size ) { m_alloc = m_size + flc_ARRAY_GROWTH; Item *mem = (Item *) memAlloc( esize( m_alloc ) ); if ( pos > 0 ) memcpy( mem , m_data, esize( pos ) ); if ( pos < (int32)m_size ) memcpy( mem + pos + 1, m_data + pos , esize(m_size - pos) ); mem[ pos ] = ndata; m_size++; memFree( m_data ); m_data = mem; } else { if ( pos < (int32)m_size ) memmove( m_data + pos + 1, m_data+pos, esize( m_size - pos) ); m_data[pos] = ndata; m_size ++; } if ( m_iterList != 0 ) { m_invalidPoint = pos; invalidateIteratorOnCriterion(); } return true; } bool ItemArray::insert( const ItemArray &other, int32 pos ) { if ( other.m_size == 0 ) return true; if ( pos < 0 ) pos = m_size + pos; if ( pos < 0 || pos > (int32)m_size ) return false; if ( m_alloc < m_size + other.m_size ) { m_alloc = m_size + other.m_size; Item *mem = (Item *) memAlloc( esize( m_alloc ) ); if ( pos > 0 ) memcpy( mem , m_data, esize( pos ) ); if ( pos < (int32)m_size ) memcpy( mem + pos + other.m_size, m_data + pos , esize(m_size - pos) ); memcpy( mem + pos , other.m_data, esize( other.m_size ) ); m_size = m_alloc; memFree( m_data ); m_data = mem; } else { if ( pos < (int32)m_size ) memmove( m_data + other.m_size + pos, m_data + pos, esize(m_size - pos ) ); memcpy( m_data + pos , other.m_data, esize( other.m_size ) ); m_size += other.m_size; } if ( m_iterList != 0 ) { m_invalidPoint = pos; invalidateIteratorOnCriterion(); } return true; } bool ItemArray::remove( int32 first, int32 last ) { if ( first < 0 ) first = m_size + first; if ( first < 0 || first >= (int32)m_size ) return false; if ( last < 0 ) last = m_size + last; if ( last < 0 || last > (int32)m_size ) return false; if( first > last ) { int32 temp = first; first = last; // last can't be < first if it was == size. last = temp+1; } uint32 rsize = last - first; if ( last < (int32)m_size ) memmove( m_data + first, m_data + last, esize(m_size - last) ); m_size -= rsize; if ( m_iterList != 0 ) { m_invalidPoint = first; invalidateIteratorOnCriterion(); } return true; } int32 ItemArray::find( const Item &itm ) const { for( uint32 i = 0; i < m_size; i ++ ) { if ( itm == m_data[ i ] ) return (int32) i; } return -1; } bool ItemArray::remove( int32 first ) { if ( first < 0 ) first = m_size + first; if ( first < 0 || first >= (int32)m_size ) return false; if ( first < (int32)m_size - 1 ) memmove( m_data + first, m_data + first + 1, esize(m_size - first) ); m_size --; if ( m_iterList != 0 ) { m_invalidPoint = first; invalidateIteratorOnCriterion(); } return true; } bool ItemArray::change( const ItemArray &other, int32 begin, int32 end ) { if ( begin < 0 ) begin = m_size + begin; if ( begin < 0 || begin > (int32)m_size ) return false; if ( end < 0 ) end = m_size + end; if ( end < 0 || end > (int32)m_size ) return false; if( begin > end ) { int32 temp = begin; begin = end; end = temp+1; } int32 rsize = end - begin; // we're considering end as "included" from now on. // this considers also negative range which already includes their extreme. if ( m_size - rsize + other.m_size > m_alloc ) { m_alloc = m_size - rsize + other.m_size; Item *mem = (Item *) memAlloc( esize( m_alloc ) ); if ( begin > 0 ) memcpy( mem, m_data, esize( begin ) ); if ( other.m_size > 0 ) memcpy( mem + begin, other.m_data, esize( other.m_size ) ); if ( end < (int32) m_size ) memcpy( mem + begin + other.m_size, m_data + end, esize(m_size - end) ); memFree( m_data ); m_data = mem; m_size = m_alloc; } else { if ( end < (int32)m_size ) memmove( m_data + begin + other.m_size, m_data + end, esize(m_size - end) ); if ( other.m_size > 0 ) memcpy( m_data + begin, other.m_data, esize( other.m_size ) ); m_size = m_size - rsize + other.m_size; } if ( m_iterList != 0 ) { m_invalidPoint = begin; invalidateIteratorOnCriterion(); } return true; } bool ItemArray::insertSpace( uint32 pos, uint32 size ) { if ( size == 0 ) return true; if ( pos < 0 ) pos = m_size + pos; if ( pos < 0 || pos > m_size ) return false; if ( m_alloc < m_size + size ) { m_alloc = ((m_size + size)/m_growth+1)*m_growth; Item *mem = (Item *) memAlloc( esize( m_alloc ) ); if ( pos > 0 ) memcpy( mem , m_data, esize( pos ) ); if ( pos < m_size ) memcpy( mem + pos + size, m_data + pos , esize(m_size - pos) ); for( uint32 i = pos; i < pos + size; i ++ ) m_data[i] = Item(); m_size += size; memFree( m_data ); m_data = mem; } else { if ( pos < m_size ) memmove( m_data + size + pos, m_data + pos, esize(m_size - pos) ); for( uint32 i = pos; i < pos + size; i ++ ) m_data[i] = Item(); m_size += size; } if ( m_iterList != 0 ) { m_invalidPoint = pos; invalidateIteratorOnCriterion(); } return true; } ItemArray *ItemArray::partition( int32 start, int32 end ) const { int32 size; Item *buffer; if ( start < 0 ) start = m_size + start; if ( start < 0 || start >= (int32)m_size ) return 0; if ( end < 0 ) end = m_size + end; if ( end < 0 || end > (int32)m_size ) return 0; if( end < start ) { size = start - end + 1; buffer = (Item *) memAlloc( esize( size ) ); for( int i = 0; i < size; i ++ ) buffer[i] = m_data[start - i]; } else { if( end == start ) { return new ItemArray(); } size = end - start; buffer = (Item *) memAlloc( esize( size ) ); memcpy( buffer, m_data + start, esize( size ) ); } return new ItemArray( buffer, size, size ); } ItemArray *ItemArray::clone() const { return new ItemArray( *this ); } void ItemArray::resize( uint32 size ) { // use this request also to force size in shape with alloc. if ( size > m_alloc ) { m_alloc = (size/m_growth + 1) *m_growth; m_data = (Item *) memRealloc( m_data, esize( m_alloc ) ); memset( m_data + m_size, 0, esize( m_alloc - m_size ) ); } else if ( size > m_size ) memset( m_data + m_size, 0, esize( size - m_size ) ); else if ( size == m_size ) { return; } else { if( m_iterList != 0 ) { m_invalidPoint = size; invalidateIteratorOnCriterion(); } } m_size = size; } void ItemArray::compact() { if ( m_size == 0 ) { if ( m_data != 0 ) { memFree( m_data ); m_data = 0; } m_alloc = 0; } else if ( m_size < m_alloc ) { m_alloc = m_size; m_data = (Item *) memRealloc( m_data, esize( m_alloc ) ); memset( m_data + m_size, 0, esize( m_alloc - m_size ) ); } } void ItemArray::reserve( uint32 size ) { if ( size == 0 ) { if( m_iterList != 0 ) { m_invalidPoint = size; invalidateIteratorOnCriterion(); } if ( m_data != 0 ) { memFree( m_data ); m_data = 0; } m_alloc = 0; m_size = 0; } else if ( size > m_alloc ) { m_data = (Item *) memRealloc( m_data, esize( size ) ); m_alloc = size; } } bool ItemArray::copyOnto( uint32 from, const ItemArray& src, uint32 first, uint32 amount ) { if( first > src.length() ) return false; if ( first + amount > src.length() ) amount = src.length() - first; if ( from > length() ) return false; if ( from + amount > length() ) resize( from + amount ); memcpy( m_data + from, src.m_data + first, esize( amount ) ); if( m_iterList != 0 ) { m_invalidPoint = from; invalidateIteratorOnCriterion(); } return true; } //============================================================ // Iterator management. //============================================================ void ItemArray::getIterator( Iterator& tgt, bool tail ) const { Sequence::getIterator( tgt, tail ); tgt.position( tail ? (length()>0? length()-1: 0) : 0 ); } void ItemArray::copyIterator( Iterator& tgt, const Iterator& source ) const { Sequence::copyIterator( tgt, source ); tgt.position( source.position() ); } void ItemArray::insert( Iterator &iter, const Item &data ) { if ( ! iter.isValid() ) throw new CodeError( ErrorParam( e_invalid_iter, __LINE__ ) .origin( e_orig_runtime ).extra( "ItemArray::insert" ) ); insert( data, (int32) iter.position() ); m_invalidPoint = (uint32) iter.position()+1; invalidateIteratorOnCriterion(); // the iterator inserted before this position, so the element has moved forward iter.position( iter.position() + 1 ); } void ItemArray::erase( Iterator &iter ) { if ( iter.position() >= length() ) throw new AccessError( ErrorParam( e_iter_outrange, __LINE__ ) .origin( e_orig_runtime ).extra( "ItemArray::erase" ) ); uint32 first = (uint32) iter.position(); if ( first+1 < m_size ) memmove( m_data + first, m_data + first + 1, esize(m_size - first) ); m_size --; invalidateAnyOtherIter( &iter ); } bool ItemArray::hasNext( const Iterator &iter ) const { return iter.position()+1 < length(); } bool ItemArray::hasPrev( const Iterator &iter ) const { return iter.position() > 0; } bool ItemArray::hasCurrent( const Iterator &iter ) const { return iter.position() < length(); } bool ItemArray::next( Iterator &iter ) const { if ( iter.position() < length()) { iter.position( iter.position() + 1 ); return iter.position() < length(); } return false; } bool ItemArray::prev( Iterator &iter ) const { if ( iter.position() > 0 ) { iter.position( iter.position() - 1 ); return true; } iter.position( length() ); return false; } Item& ItemArray::getCurrent( const Iterator &iter ) { if ( iter.position() < length() ) return m_data[ iter.position() ]; throw new AccessError( ErrorParam( e_iter_outrange, __LINE__ ) .origin( e_orig_runtime ).extra( "ItemArray::getCurrent" ) ); } Item& ItemArray::getCurrentKey( const Iterator &iter ) { throw new CodeError( ErrorParam( e_non_dict_seq, __LINE__ ) .origin( e_orig_runtime ).extra( "ItemArray::getCurrentKey" ) ); } bool ItemArray::equalIterator( const Iterator &first, const Iterator &second ) const { return first.position() == second.position(); } bool ItemArray::onCriterion( Iterator* elem ) const { return elem->position() >= m_invalidPoint; } int ItemArray::compare( const ItemArray& other, ItemArray::Parentship* parent ) const { // really the same. if (&other == this) return 0; // use the size + 1 element to store the parent list Parentship current( this, parent ); for ( uint32 i = 0; i < m_size; i ++ ) { // is the other shorter? if ( i >= other.m_size ) { // we're bigger return 1; } // different arrays? if ( m_data[i].isArray() && other.m_data[i].isArray() ) { // check if m_data[i] is in the list of parents. ItemArray* ia = &m_data[i].asArray()->items(); Parentship *p1 = parent; // If it is not, we should scan it too. bool bDescend = true; while( p1 != 0 ) { if( p1->m_array == ia ) { bDescend = false; break; } p1 = p1->m_parent; } if ( bDescend ) { int cval = ia->compare( other.m_data[i].asArray()->items(), ¤t ); // if the things below us aren't equal, we're not equal if ( cval != 0 ) return cval; // else, check other items. } } else { int cval = m_data[i].compare( other.m_data[i] ); // if the things below us aren't equal, we're not equal if ( cval != 0 ) return cval; // else, check other items. } } if( m_size < other.m_size ) return -1; // ok, we're the same return 0; } } /* end of itemarray.cpp */ engine/itemlist.cpp000066400000000000000000000200221176363201700146630ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: itemlist.cpp List of Falcon Items ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2007-12-01 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file List of Falcon Items implementation */ #include #include #include namespace Falcon { ItemList::ItemList( const ItemList &l ): m_erasingIter(0), m_disposingElem(0) { ItemListElement *h = l.m_head; if ( h == 0 ) { m_head = 0; m_tail = 0; m_size = 0; return; } m_head = new ItemListElement( h->item(), 0, 0 ); ItemListElement *h1 = m_head; h = h->next(); while( h != 0 ) { h1->next( new ItemListElement( h->item(), h1, 0 ) ); h1 = h1->next(); h = h->next(); } m_tail = h1; m_size = l.m_size; } ItemList *ItemList::clone() const { return new ItemList( *this ); } const Item &ItemList::front() const { return m_head->item(); } const Item &ItemList::back() const { return m_tail->item(); } ItemListElement *ItemList::first() const { return m_head; } ItemListElement *ItemList::last() const { return m_tail; } void ItemList::push_back( const Item &itm ) { if ( m_tail == 0 ) { m_head = m_tail = new ItemListElement( itm ); m_size = 1; } else { m_tail->next( new ItemListElement( itm, m_tail, 0 ) ); m_tail = m_tail->next(); m_size++; } } void ItemList::pop_back() { ItemListElement* elem = m_tail; if( elem != 0 ) { m_tail = m_tail->prev(); // was the only element? if ( m_tail == 0 ) { // delete it and update head. m_head = 0; } else { m_tail->next()->prev(0); m_tail->next(0); } m_size--; if ( m_iterList != 0) { m_disposingElem = elem; invalidateIteratorOnCriterion(); m_disposingElem = 0; } delete elem; } } void ItemList::push_front( const Item &itm ) { if ( m_head == 0 ) { m_head = m_tail = new ItemListElement( itm ); m_size = 1; } else { m_head->prev( new ItemListElement( itm, 0, m_head ) ); m_head = m_head->prev(); m_size++; } } void ItemList::pop_front() { ItemListElement* elem = m_head; if( m_head != 0 ) { m_head = m_head->next(); // was the only element? if ( m_head == 0 ) { // delete it and update head. m_tail = 0; } else { m_head->prev()->next(0); m_head->prev(0); } m_size--; if ( m_iterList != 0) { m_disposingElem = elem; invalidateIteratorOnCriterion(); m_disposingElem = 0; } delete elem; } } ItemListElement *ItemList::erase( ItemListElement *elem ) { if ( m_head == 0 ) { //? return 0; } if ( elem == m_head ) { m_head = m_head->next(); // was the last element? if ( m_head == 0 ) m_tail = 0; else m_head->prev( 0 ); } else if ( elem == m_tail ) { // cannot be also == head, so the list is not empty m_tail = m_tail->prev(); m_tail->next(0); } else { // we know we have a valid prev and next elem->prev()->next( elem->next() ); elem->next()->prev( elem->prev() ); } ItemListElement *retval = elem->next(); // invalidate all the iterators on this element. if( m_iterList ) { m_disposingElem = elem; invalidateIteratorOnCriterion(); m_disposingElem = 0; } delete elem; m_size--; return retval; } void ItemList::insert( ItemListElement *elem, const Item &item ) { if ( elem == 0 ) { push_back( item ); return; } // we have a valid element. // it may be head, tail or both. if ( elem == m_head ) { m_head->prev( new ItemListElement( item, 0, m_head ) ); m_head = m_head->prev(); } // but we don't have to move the tail which stays where it is. else { ItemListElement *en = new ItemListElement( item, elem->prev(), elem ); elem->prev()->next( en ); elem->prev( en ); } m_size++; } void ItemList::clear() { invalidateAllIters(); ItemListElement *h = m_head; while( h != 0 ) { ItemListElement *nx = h->next(); h->next(h); h->prev(0); delete h; h = nx; } m_head = 0; m_tail = 0; m_size = 0; } void ItemList::gcMark( uint32 gen ) { Sequence::gcMark( gen ); // we don't have to record the mark byte, as we woudln't have been called // if the coreobject holding us had the right mark. ItemListElement *h = m_head; while( h != 0 ) { memPool->markItem( h->item() ); h = h->next(); } } //======================================================== // Iterator implementation. //======================================================== void ItemList::getIterator( Iterator& tgt, bool tail ) const { Sequence::getIterator( tgt, tail ); ItemListElement* elem = tail ? m_tail : m_head; // may legally be 0 (for insertion at end) tgt.data( elem ); } void ItemList::copyIterator( Iterator& tgt, const Iterator& source ) const { Sequence::copyIterator( tgt, source ); tgt.data( source.data() ); } void ItemList::insert( Iterator &tgt, const Item &data ) { ItemListElement* ptr = (ItemListElement*) tgt.data(); if ( ptr == 0 ) append( data ); else { insert( ptr, data ); tgt.prev(); } } void ItemList::erase( Iterator &tgt ) { ItemListElement* ptr = (ItemListElement*) tgt.data(); if ( ptr == 0 ) { throw new AccessError( ErrorParam( e_invalid_iter ) .origin( e_orig_runtime ).extra( "ItemList::erase" ) ); } m_erasingIter = &tgt; ItemListElement* next = erase( ptr ); m_erasingIter = 0; tgt.data( next ); } bool ItemList::hasNext( const Iterator &iter ) const { ItemListElement* ptr = (ItemListElement*) iter.data(); return ptr != 0 && ptr->next() != 0; } bool ItemList::hasPrev( const Iterator &iter ) const { ItemListElement* ptr = (ItemListElement*) iter.data(); // ptr == 0 => at end return ( ptr == 0 && m_size > 0 ) || (ptr!= 0 && ptr->prev() != 0); } bool ItemList::hasCurrent( const Iterator &iter ) const { ItemListElement* ptr = (ItemListElement*) iter.data(); return ptr != 0; } bool ItemList::next( Iterator &iter ) const { ItemListElement* ptr = (ItemListElement*) iter.data(); if ( ptr == 0 ) return false; ItemListElement* next = ptr->next(); // change even if next == 0 (it's a valid iterator at end). iter.data( next ); return next != 0; } bool ItemList::prev( Iterator &iter ) const { ItemListElement* ptr = (ItemListElement*) iter.data(); // zero means "at end", so the previous element is the tail if ( ptr == 0 ) { ptr = m_tail; if ( ptr == 0 ) { return false; } iter.data( ptr ); return true; } ItemListElement* prev = ptr->prev(); iter.data( prev ); return prev != 0; } Item& ItemList::getCurrent( const Iterator &iter ) { ItemListElement* ptr = (ItemListElement*) iter.data(); if ( ptr == 0 ) { throw new AccessError( ErrorParam( e_iter_outrange, __LINE__ ) .origin( e_orig_runtime ).extra( "ItemList::getCurrent" ) ); } return ptr->item(); } Item& ItemList::getCurrentKey( const Iterator &iter ) { throw new CodeError( ErrorParam( e_non_dict_seq, __LINE__ ) .origin( e_orig_runtime ).extra( "ItemList::getCurrentKey" ) ); } bool ItemList::equalIterator( const Iterator &first, const Iterator &second ) const { return first.data() == second.data(); } // Deletion criterion. bool ItemList::onCriterion( Iterator* elem ) const { return elem != m_erasingIter && elem->data() == m_disposingElem; } } /* end of itemlist.cpp */ engine/itemserial.cpp000066400000000000000000000457751176363201700152150ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: itemserial.cpp Item serialization methods ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom gen 28 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Item serialization methods. The serialization methods have been put in this file as they require also linking with the stream class. */ #include #include #include #include #include #include #include namespace Falcon { bool Item::serialize_symbol( Stream *file, const Symbol *sym ) const { const Module *mod= sym->module(); // write the module name mod->name().serialize( file ); // write the symbol name sym->name().serialize( file ); if( ! file->good() ) return false; return true; } bool Item::serialize_function( Stream *file, const CoreFunc *func, bool bLive ) const { byte type = FLC_ITEM_FUNC; if ( bLive ) type |= 0x80; file->write( &type, 1 ); // we don't serialize the module ID because it is better to rebuild it on deserialization serialize_symbol( file, func->symbol() ); if ( func->symbol()->isFunction() ) { // language function ? -- serialize the state. FuncDef *fdef = func->symbol()->getFuncDef(); // write the called status uint32 itemId = fdef->onceItemId(); if ( itemId != FuncDef::NO_STATE ) { byte called = func->liveModule()->globals()[ itemId ].isNil() ? 0 : 1; file->write( &called, 1 ); } // and we may need to serialize also it's closure. ItemArray* closed = func->closure(); if( closed != 0 ) { int32 len = endianInt32( closed->length() ); file->write( (byte *) &len, sizeof( len ) ); for( uint32 i = 0; i < closed->length(); i ++ ) { (*closed)[i].serialize( file, bLive ); if( ! file->good() ) return false; } } else { int32 len = 0; file->write( (byte *) &len, sizeof( len ) ); } } if ( ! file->good() ) return false; return true; } bool Item::serialize_object( Stream *file, CoreObject *obj, bool bLive ) const { byte type = FLC_ITEM_OBJECT; if ( bLive ) type |= 0x80; file->write( &type, 1 ); // write the class symbol so that it can be rebuilt back. serialize_symbol( file, obj->generator()->symbol() ); // then serialzie the object itself return obj->serialize( file, bLive ); } bool Item::serialize_class( Stream *file, const CoreClass *cls ) const { byte type = FLC_ITEM_CLASS; file->write( &type, 1 ); LiveModule *lmod = cls->liveModule(); // Write the live module name lmod->name().serialize( file ); if ( ! file->good() ) return false; // and the class name cls->symbol()->name().serialize( file ); if ( ! file->good() ) return false; return true; } Item::e_sercode Item::serialize( Stream *file, bool bLive ) const { if( file->bad() ) return sc_ferror; switch( this->type() ) { case FLC_ITEM_BOOL: { byte type = FLC_ITEM_BOOL; file->write((byte *) &type, 1 ); byte bval = this->asBoolean() ? 1 : 0; file->write( (byte *) &bval, sizeof( bval ) ); } break; case FLC_ITEM_INT: { byte type = FLC_ITEM_INT; file->write((byte *) &type, 1 ); int64 val = endianInt64( this->asInteger() ); file->write( (byte *) &val, sizeof( val ) ); } break; case FLC_ITEM_RANGE: { byte type = FLC_ITEM_RANGE; file->write((byte *) &type, 1 ); int64 val1 = endianInt64(this->asRangeStart()); int64 val2 = endianInt64(this->asRangeEnd()); int64 val3 = endianInt64(this->asRangeStep()); //byte isOpen = this->asRangeIsOpen() ? 1 : 0; file->write( (byte *) &val1, sizeof( val1 ) ); file->write( (byte *) &val2, sizeof( val2 ) ); file->write( (byte *) &val3, sizeof( val3 ) ); //file->write( (byte *) &isOpen, sizeof( isOpen ) ); } break; case FLC_ITEM_NUM: { byte type = FLC_ITEM_NUM; file->write((byte *) &type, 1 ); numeric val = endianNum( this->asNumeric() ); file->write( (byte *) &val, sizeof( val ) ); } break; case FLC_ITEM_STRING: { byte type = FLC_ITEM_STRING; file->write((byte *) &type, 1 ); this->asString()->serialize( file ); } break; case FLC_ITEM_LBIND: { byte type = FLC_ITEM_LBIND; file->write((byte *) &type, 1 ); // Future bindings are temporary items; as such, their future // value is not to be serialized. asLBind()->serialize( file ); } break; case FLC_ITEM_MEMBUF: { byte type = FLC_ITEM_MEMBUF; if ( bLive ) { type |= 0x80; file->write( &type, 1 ); MemBuf* mb = asMemBuf(); file->write( &mb, sizeof(mb) ); } else { file->write( &type, 1 ); this->asMemBuf()->serialize( file, bLive ); } } break; case FLC_ITEM_ARRAY: { byte type = FLC_ITEM_ARRAY; file->write((byte *) &type, 1 ); CoreArray &array = *this->asArray(); int32 len = endianInt32( array.length() ); file->write( (byte *) &len, sizeof( len ) ); for( uint32 i = 0; i < array.length(); i ++ ) { array[i].serialize( file, bLive ); if( ! file->good() ) return sc_ferror; } } break; case FLC_ITEM_DICT: { byte type = FLC_ITEM_DICT; file->write( &type, 1 ); CoreDict *dict = this->asDict(); type = dict->isBlessed() ? 1:0; file->write( &type, 1 ); int32 len = endianInt32( dict->length() ); file->write( (byte *) &len, sizeof( len ) ); Iterator iter( &dict->items() ); while( iter.hasCurrent() ) { iter.getCurrentKey().serialize( file, bLive ); if( ! file->good() ) return sc_ferror; iter.getCurrent().serialize( file, bLive ); if( ! file->good() ) return sc_ferror; iter.next(); } } break; case FLC_ITEM_FUNC: serialize_function( file, this->asFunction(), bLive ); break; case FLC_ITEM_METHOD: { byte type = FLC_ITEM_METHOD; file->write( &type, 1 ); e_sercode sc = asMethodItem().serialize( file, bLive ); if( sc != sc_ok ) return sc; CallPoint* cp = this->asMethodFunc(); if ( cp->isFunc() ) { serialize_function( file, static_cast(cp), bLive ); } else { SafeItem arr( static_cast(cp) ); arr.serialize(file, bLive ); } } break; case FLC_ITEM_OBJECT: serialize_object( file, this->asObjectSafe(), bLive ); break; case FLC_ITEM_REFERENCE: asReference()->origin().serialize( file, bLive ); break; case FLC_ITEM_CLASS: serialize_class( file, this->asClass() ); break; case FLC_ITEM_CLSMETHOD: serialize_class( file, this->asMethodClass() ); serialize_function( file, this->asFunction(), bLive ); break; case FLC_ITEM_UNB: { byte type = FLC_ITEM_UNB; file->write((byte *) &type, 1 ); } break; default: { byte type = FLC_ITEM_NIL; file->write((byte *) &type, 1 ); } } return file->bad() ? sc_ferror : sc_ok; } Item::e_sercode Item::deserialize_symbol( Stream *file, VMachine *vm, Symbol **tg_sym, LiveModule **livemod ) { if ( vm == 0 ) return sc_missvm; // read the module name String name; if ( ! name.deserialize( file ) ) return sc_ferror; LiveModule *lmod = vm->findModule( name ); *livemod = lmod; if ( lmod == 0 ) { return sc_misssym; } const Module *mod = lmod->module(); // read the symbol name if ( ! name.deserialize( file ) ) return file->bad() ? sc_ferror : sc_invformat; // find the name in the module Symbol *sym = mod->findGlobalSymbol( name ); if ( sym == 0 ) { return sc_misssym; } *tg_sym = sym; return sc_ok; } Item::e_sercode Item::deserialize_function( Stream *file, VMachine *vm ) { if ( vm == 0 ) return sc_missvm; Symbol *sym; LiveModule *lmod; e_sercode sc = deserialize_symbol( file, vm, &sym, &lmod ); if ( sc != sc_ok ) return sc; // read the function called status if ( ! sym->isFunction() ) { // external function setFunction( new CoreFunc( sym, lmod ) ); return sc_ok; } // internal function. FuncDef *def = sym->getFuncDef(); // read the once status uint32 itemId = def->onceItemId(); if( itemId != FuncDef::NO_STATE ) { byte called; file->read( &called, 1 ); if( called ) lmod->globals()[ itemId ].setInteger( 1 ); else lmod->globals()[ itemId ].setNil(); } CoreFunc* function = new CoreFunc( sym, lmod ); // read the closure state. uint32 closlen; file->read( &closlen, sizeof( closlen ) ); if( closlen != 0 ) { ItemArray* closure = new ItemArray( closlen ); closure->resize( closlen ); // put the state in now, so we can destroy it in case of error. function->closure( closure ); // deserialize all the items. for( uint32 i = 0; i < closlen; i++ ) { Item::e_sercode res = (*closure)[i].deserialize(file, vm ); if ( res != sc_ok ) return res; } } setFunction( function ); return sc_ok; } Item::e_sercode Item::deserialize_class( Stream *file, VMachine *vm ) { if ( vm == 0 ) return sc_missvm; String modName, className; if ( ! modName.deserialize( file ) || ! className.deserialize( file ) ) return file->bad() ? sc_ferror : sc_invformat; // find the module in the vm LiveModule *origMod = vm->findModule( modName ); if( origMod == 0 ) { return sc_misssym; } // find the class item in the module Item *clitem = origMod->findModuleItem( className ); if ( clitem == 0 ) return sc_misssym; if ( clitem->isReference() ) { if( ! clitem->dereference()->isClass() ) return sc_misssym; } else if ( ! clitem->isClass() ) return sc_misssym; *this = *clitem; return sc_ok; } Item::e_sercode Item::deserialize( Stream *file, VMachine *vm ) { byte type = FLC_ITEM_NIL; if ( file->read((byte *) &type, 1 ) == 0 ) return sc_eof; if( ! file->good() ) return sc_ferror; switch( type ) { case FLC_ITEM_NIL: setNil(); return sc_ok; case FLC_ITEM_UNB: setUnbound(); return sc_ok; case FLC_ITEM_BOOL: { byte bval; file->read( (byte *) &bval, sizeof( bval ) ); if ( file->good() ) { setBoolean( bval != 0 ); return sc_ok; } return sc_ferror; } return sc_ok; case FLC_ITEM_INT: { int64 val; file->read( (byte *) &val, sizeof( val ) ); if ( file->good() ) { setInteger(endianInt64(val) ); return sc_ok; } return sc_ferror; } break; case FLC_ITEM_RANGE: { int64 val1; int64 val2; int64 val3; //byte isOpen; file->read( (byte *) &val1, sizeof( val1 ) ); file->read( (byte *) &val2, sizeof( val2 ) ); file->read( (byte *) &val3, sizeof( val3 ) ); //file->read( (byte *) &isOpen, sizeof( isOpen ) ); val1 = endianInt64( val1 ); val2 = endianInt64( val2 ); val3 = endianInt64( val3 ); if ( file->good() ) { setRange( new CoreRange( val1, val2, val3 ) ); return sc_ok; } return sc_ferror; } break; case FLC_ITEM_NUM: { numeric val; file->read( (byte *) &val, sizeof( val ) ); if ( file->good() ) { setNumeric( endianNum( val ) ); return sc_ok; } return sc_ferror; } break; case FLC_ITEM_LBIND: { int32 id; file->read( (byte*) &id, sizeof(id) ); String name; if ( ! name.deserialize( file ) ) return file->bad() ? sc_ferror : sc_invformat; setLBind( new CoreString( name ) ); } break; case FLC_ITEM_STRING: { CoreString *cs = new CoreString; setString( cs ); if ( ! cs->deserialize( file ) ) { return file->bad() ? sc_ferror : sc_invformat; } if ( file->good() ) { return sc_ok; } return sc_ferror; } break; case FLC_ITEM_MEMBUF |0x80: { // get the function pointer in the stream /*MemBuf *(*deserializer)( VMachine *, Stream * ); file->read( &deserializer, sizeof( deserializer ) ); if ( ! file->good() ) { return sc_ferror; } MemBuf *mb = deserializer( vm, file ); if( mb == 0 ) { return sc_invformat; }*/ MemBuf* mb; if( file->read( &mb, sizeof( mb ) ) == sizeof(mb) ) { setMemBuf( mb ); return sc_ok; } return sc_eof; } case FLC_ITEM_MEMBUF: { MemBuf *mb = MemBuf::deserialize( vm, file ); if ( file->good() && mb != 0 ) { setMemBuf( mb ); return sc_ok; } return sc_ferror; } break; case FLC_ITEM_ARRAY: { int32 val; file->read( (byte *) &val, sizeof( val ) ); e_sercode retval = sc_ok; if ( file->good() ) { val = endianInt32(val); CoreArray *array = new CoreArray(); array->resize(val); for( int i = 0; i < val; i ++ ) { retval = array->items()[i].deserialize( file, vm ); if( retval != sc_ok ) { break; } } if ( retval == sc_ok ) { setArray( array ); return sc_ok; } return retval; } } break; case FLC_ITEM_DICT: { byte blessed; file->read( &blessed, 1 ); int32 val; file->read( (byte *) &val, sizeof( val ) ); if ( file->good() ) { val = endianInt32(val); LinearDict *dict = new LinearDict( val ); LinearDictEntry *elems = dict->entries(); e_sercode retval = sc_ok; for( int i = 0; i < val; i ++ ) { LinearDictEntry *entry = elems + i; retval = entry->key().deserialize( file, vm ); if( retval == sc_ok ) retval = entry->value().deserialize( file, vm ); if ( retval != sc_ok ) break; dict->length( i + 1 ); } if( retval == sc_ok ) { CoreDict* cdict = new CoreDict( dict ); cdict->bless( blessed ? true : false ); setDict( cdict ); return sc_ok; } else delete dict; return retval; } } break; case FLC_ITEM_FUNC | 0x80: case FLC_ITEM_FUNC: { if( vm == 0 ) return sc_missvm; return deserialize_function( file, vm ); } break; case FLC_ITEM_METHOD: { if( vm == 0 ) return sc_missvm; Item obj; Item func; e_sercode sc; sc = obj.deserialize( file, vm ); if ( sc != sc_ok ) return sc; sc = func.deserialize( file, vm ); if ( sc != sc_ok ) return sc; if ( func.isFunction() ) setMethod( obj, func.asMethodFunc() ); else if ( func.isArray() && func.isCallable() ) { setMethod( obj, func.asArray() ); } else return sc_invformat; return sc_ok; } case FLC_ITEM_OBJECT | 0x80: case FLC_ITEM_OBJECT: { bool bLive = type != FLC_ITEM_OBJECT; if( vm == 0 ) return sc_missvm; // read the module name Symbol *sym; LiveModule *lmod; e_sercode sc = deserialize_symbol( file, vm, &sym, &lmod ); if ( sc != sc_ok ) return sc; Item *clitem = &lmod->globals()[ sym->itemId() ]; // Create the core object, but don't fill attribs. CoreObject *object = clitem->dereference()->asClass()->createInstance(0, true); if ( ! object->deserialize( file, bLive ) ) { return sc_missclass; } setObject( object ); return file->good() ? sc_ok : sc_ferror; } break; case FLC_ITEM_CLASS: return deserialize_class( file, vm ); case FLC_ITEM_CLSMETHOD: { e_sercode sc = deserialize_class( file, vm ); if ( sc != sc_ok ) return sc; return deserialize_function( file, vm ); } break; default: return sc_invformat; } return sc_ferror; } //TODO:Move in another file. bool Item::clone( Item &target) const { const Item *item = this->dereference(); switch( item->type() ) { case FLC_ITEM_STRING: target = new CoreString( *item->asString() ); break; case FLC_ITEM_ARRAY: target = item->asArray()->clone(); break; case FLC_ITEM_DICT: target = item->asDict()->clone(); break; case FLC_ITEM_OBJECT: { CoreObject *obj = item->asObjectSafe()->clone(); if ( obj == 0 ) { return false; } target = obj; } break; case FLC_ITEM_METHOD: { target = *this; } break; default: target = *this; } return true; } /* void Item::copy( const Item &other ) { if ( isLBind() ) { VMachine* current = VMachine::getCurrent(); if ( current != 0 ) { current->bindItem( *asLBind(), other ); return; } } if( other.isLBind() ) { VMachine* current = VMachine::getCurrent(); if ( current != 0 ) { current->unbindItem( *other.asLBind(), *this ); return; } } all = other.all; } */ } /* end of itemserial.cpp */ engine/itemset.cpp000066400000000000000000000170151176363201700145130ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: itemset.cpp (Ordered) set of falcon items. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 07 Aug 2009 18:36:22 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include namespace Falcon { ItemSet::ItemSet( const ItemSet &l ) { m_root = duplicateSubTree( 0, l.m_root ); m_size = l.m_size; } ItemSetElement* ItemSet::duplicateSubTree( ItemSetElement* parent, const ItemSetElement* source ) { if ( source == 0 ) return 0; ItemSetElement* ret = new ItemSetElement( source->item(), parent ); ret->left( duplicateSubTree( ret, source->left() ) ); ret->right( duplicateSubTree( ret, source->right() ) ); return ret; } ItemSet *ItemSet::clone() const { return new ItemSet(*this); } const Item &ItemSet::front() const { fassert( ! empty() ); return first()->item(); } const Item &ItemSet::back() const { fassert( ! empty() ); return last()->item(); } ItemSetElement *ItemSet::first() const { if ( m_root == 0 ) return 0; return smallestInTree( m_root ); } ItemSetElement *ItemSet::smallestInTree( ItemSetElement *e ) { while( e->left() != 0 ) { e = e->left(); } return e; } ItemSetElement *ItemSet::last() const { if ( m_root == 0 ) return 0; return largestInTree( m_root ); } ItemSetElement *ItemSet::largestInTree( ItemSetElement *e ) { while( e->right() != 0 ) { e = e->right(); } return e; } void ItemSet::clear() { invalidateAllIters(); clearSubTree( m_root ); m_size = 0; } void ItemSet::clearSubTree( ItemSetElement *e ) { if( e != 0 ) { clearSubTree( e->left() ); clearSubTree( e->right() ); delete e; } } void ItemSet::erase( ItemSetElement *elem ) { // the new subtree root is the highest element in the left subtree. ItemSetElement *newRoot = 0; if ( elem->left() ) { newRoot = largestInTree( elem->left() ); newRoot->parent()->right( newRoot->left() ); } else if( elem->right() ) { newRoot = smallestInTree( elem->right() ); newRoot->parent()->left( newRoot->right() ); } // was this element the main root? if ( elem->parent() == 0 ) { m_root = newRoot; } else { // disengage the element from the parent if( elem->parent()->left() == elem ) elem->parent()->left( newRoot ); else elem->parent()->right( newRoot ); } // assign the new children newRoot->left( elem->left() ); newRoot->right( elem->right() ); // the element is disengaged. // invalidate the iterators pointing here. m_disposingElem = elem; invalidateIteratorOnCriterion(); m_disposingElem = 0; // time to get rid of the element. delete elem; --m_size; } ItemSetElement* ItemSet::find( const Item &item ) { if( m_root == 0 ) return 0; return findInTree( m_root, item ); } ItemSetElement* ItemSet::findInTree( ItemSetElement* elem, const Item &item ) { int c = elem->item().compare( item ); if ( c < 0 ) { if ( elem->right() != 0 ) return findInTree( elem->right(), item ); } else if ( c > 0 ) { if ( elem->left() != 0 ) return findInTree( elem->left(), item ); } else // == return elem; return 0; // not found, } void ItemSet::getIteratorAt( Iterator &tgt, ItemSetElement* elem ) { Sequence::getIterator( tgt, false ); tgt.data( elem ); } void ItemSet::insert( const Item &item ) { if( m_root == 0 ) { m_root = new ItemSetElement( item ); ++m_size; } else { if( insertInSubtree( m_root, item ) ) ++m_size; } } bool ItemSet::insertInSubtree( ItemSetElement* elem, const Item& item ) { int result = elem->item().compare( item ); if( result == 0 ) { elem->item() = item; return false; } if( result < 0 ) { if ( elem->right() != 0 ) return insertInSubtree( elem->right(), item ); else elem->right( new ItemSetElement( item, elem ) ); } else { if ( elem->left() != 0 ) return insertInSubtree( elem->left(), item ); else elem->left( new ItemSetElement( item, elem ) ); } // if we arrived here, it means we added a new node. return true; } ItemSetElement* ItemSet::nextElem( ItemSetElement* e ) { if( e->right() != 0 ) return smallestInTree( e->right() ); while( e->parent() != 0 && e->parent()->right() == e ) e = e->parent(); return e->parent(); } ItemSetElement* ItemSet::prevElem( ItemSetElement* e ) { if( e->left() != 0 ) return largestInTree( e->left() ); while( e->parent() != 0 && e->parent()->left() == e ) e = e->parent(); return e->parent(); } void ItemSet::gcMark( uint32 m ) { if( m_mark != m ) { m_mark = m; Sequence::gcMark( m ); markSubTree( m_root ); } } void ItemSet::markSubTree( ItemSetElement* e ) { if( e != 0 ) { memPool->markItem( e->item() ); markSubTree( e->left() ); markSubTree( e->right() ); } } bool ItemSet::onCriterion( Iterator* elem ) const { return elem->data() == m_disposingElem && elem != m_erasingIter; } void ItemSet::getIterator( Iterator& tgt, bool tail ) const { Sequence::getIterator( tgt, tail ); if ( m_root == 0 ) tgt.data(0); else if( tail ) tgt.data( largestInTree( m_root ) ); else tgt.data( smallestInTree( m_root ) ); } void ItemSet::copyIterator( Iterator& tgt, const Iterator& source ) const { Sequence::copyIterator( tgt, source ); tgt.data( source.data() ); } void ItemSet::insert( Iterator &iter, const Item &data ) { insert( data ); } void ItemSet::erase( Iterator &iter ) { m_erasingIter = &iter; ItemSetElement* elem = (ItemSetElement*) iter.data(); next( iter ); erase( (ItemSetElement*) elem ); m_erasingIter = 0; } bool ItemSet::hasNext( const Iterator &iter ) const { ItemSetElement* elem = (ItemSetElement*) iter.data(); return elem != 0 && nextElem( elem ) != 0; } bool ItemSet::hasPrev( const Iterator &iter ) const { ItemSetElement* elem = (ItemSetElement*) iter.data(); return elem == 0 || prevElem( elem ) != 0; } bool ItemSet::hasCurrent( const Iterator &iter ) const { return iter.data() != 0; } bool ItemSet::next( Iterator &iter ) const { ItemSetElement* elem = (ItemSetElement*) iter.data(); if ( elem == 0 ) return false; iter.data( nextElem( elem ) ); return iter.data() != 0; } bool ItemSet::prev( Iterator &iter ) const { ItemSetElement* elem = (ItemSetElement*) iter.data(); if ( elem == 0 ) { if ( m_root ) iter.data( largestInTree( m_root ) ); // otherwise, it stays 0 } else iter.data( prevElem( elem ) ); return iter.data() != 0; } Item& ItemSet::getCurrent( const Iterator &iter ) { ItemSetElement* elem = (ItemSetElement*) iter.data(); fassert( elem != 0 ); return elem->item(); } Item& ItemSet::getCurrentKey( const Iterator &iter ) { throw new CodeError( ErrorParam( e_non_dict_seq, __LINE__ ) .origin( e_orig_runtime ).extra( "ItemSet::getCurrentKey" ) ); } bool ItemSet::equalIterator( const Iterator &first, const Iterator &second ) const { return first.data() == second.data(); } } /* end of itemset.cpp */ engine/itemtraits.cpp000066400000000000000000000025361176363201700152300ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: itemtraits.cpp Traits for putting items in generic containers ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom ott 29 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Traits for putting items in generic containers */ #include #include namespace Falcon { uint32 ItemTraits::memSize() const { return sizeof( Item ); } void ItemTraits::init( void *itemZone ) const { Item *itm = (Item *) itemZone; itm->setNil(); } void ItemTraits::copy( void *targetZone, const void *sourceZone ) const { Item *target = (Item *) targetZone; const Item *source = (Item *) sourceZone; target->copy( *source ); } int ItemTraits::compare( const void *first, const void *second ) const { const Item *f = (Item *) first; const Item *s = (Item *) second; return f->compare( *s ); } void ItemTraits::destroy( void *item ) const { // do nothing } bool ItemTraits::owning() const { return false; } namespace traits { ItemTraits &t_item() { static ItemTraits *dt = new ItemTraits(); return *dt; } } } /* end of itemtraits.cpp */ engine/iterator.cpp000066400000000000000000000017171176363201700146740ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: iterator.cpp Implementation of virtual functions for iterators. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 02 Aug 2009 13:00:35 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include namespace Falcon { Iterator::~Iterator() { // give the owner a chance to de-account us if( m_owner != 0 ) m_owner->disposeIterator( *this ); // Then, get rid of deep data, if we have to if ( m_deletor != 0 ) m_deletor( this ); } void Iterator::gcMark( uint32 mark ) { if( m_owner ) { m_owner->gcMark( mark ); } } Iterator *Iterator::clone() const { return new Iterator( *this ); } } /* end of iterator.cpp */ engine/lineardict.cpp000066400000000000000000000253361176363201700151640ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: lineardict.cpp Linear dictionary ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun ago 23 21:55:38 CEST 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include namespace Falcon { LinearDict::LinearDict(): m_size(0), m_alloc(0), m_data(0), m_mark( 0xFFFFFFFF ) {} LinearDict::LinearDict( uint32 size ): m_mark( 0xFFFFFFFF ) { m_data = (LinearDictEntry *) memAlloc( esize( size ) ); length(0); allocated( size ); } LinearDict::~LinearDict() { if ( m_data != 0 ) memFree( m_data ); } uint32 LinearDict::length() const { return m_size; } bool LinearDict::empty() const { return m_size == 0; } const Item &LinearDict::front() const { if( m_size == 0 ) throw new AccessError( ErrorParam( e_iter_outrange, __LINE__ ) .origin( e_orig_runtime ).extra( "LinearDict::front" ) ); return m_data[0].value(); } const Item &LinearDict::back() const { if( m_size == 0 ) throw new AccessError( ErrorParam( e_iter_outrange, __LINE__ ) .origin( e_orig_runtime ).extra( "LinearDict::back" ) ); return m_data[m_size-1].value(); } void LinearDict::append( const Item& item ) { if( item.isArray() ) { ItemArray& pair = item.asArray()->items(); if ( pair.length() == 2 ) { put( pair[0], pair[1] ); return; } } throw new AccessError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "LinearDict::append" ) ); } void LinearDict::prepend( const Item& item ) { append( item ); invalidateAllIters(); } Item *LinearDict::find( const Item &key ) const { uint32 posHint; // Insert supports substitution semantics. if ( findInternal( key, posHint ) ) { return &m_data[ posHint ].value(); } return 0; } bool LinearDict::findIterator( const Item &key, Iterator &iter ) { uint32 posHint; // Insert supports substitution semantics. bool val = findInternal( key, posHint ); iter.position( posHint ); return val; } bool LinearDict::remove( const Item &key ) { uint32 posHint; // Insert supports substitution semantics. if ( findInternal( key, posHint ) ) { removeAt( posHint ); m_invalidPos = posHint; invalidateIteratorOnCriterion(); return true; } return false; } void LinearDict::put( const Item &key, const Item &value ) { uint32 posHint; // Insert supports substitution semantics. if ( findInternal( key, posHint ) ) { m_data[ posHint ].value( value ); return; } // Entry not found, must be added addInternal( posHint, key, value ); m_invalidPos = posHint; invalidateIteratorOnCriterion(); } void LinearDict::smartInsert( const Iterator &iter, const Item &key, const Item &value ) { if ( m_size == 0 ) { addInternal( 0, key, value ); return; } if ( iter.hasCurrent() ) { uint32 posHint = (uint32) iter.position(); // right position? if ( key == m_data[posHint].key() ) { m_data[ posHint ].value( value ); return; } // not right, but good for insertion? if ( ( posHint == 0 || key > m_data[posHint-1].key() ) && ( posHint == m_size || key < m_data[posHint].key() ) ) { addInternal( posHint, key, value ); m_invalidPos = posHint; invalidateIteratorOnCriterion(); return; } } // nothing to do, perform a full search put( key, value ); } void LinearDict::merge( const ItemDict &dict ) { if ( dict.length() > 0 ) { m_alloc = m_size + dict.length(); m_data = (LinearDictEntry*) memRealloc( m_data, sizeof( LinearDictEntry )*m_alloc ); Iterator iter( const_cast( &dict ) ); while( iter.hasCurrent() ) { put( iter.getCurrentKey(), iter.getCurrent() ); iter.next(); } } invalidateAllIters(); } bool LinearDict::addInternal( uint32 pos, const Item &key, const Item &value ) { if ( pos > m_size ) return false; // haven't we got enough space? if ( m_alloc <= m_size ) { m_alloc = m_size + flc_DICT_GROWTH; LinearDictEntry *mem = (LinearDictEntry *) memAlloc( esize( m_alloc ) ); if ( pos > 0 ) memcpy( mem, m_data, esize( pos ) ); LinearDictEntry *entry = (LinearDictEntry *) (mem + pos); entry->key( key ); entry->value( value ); if ( pos < m_size ) memcpy( mem+pos + 1, m_data + pos, esize( m_size - pos ) ); if ( m_data != 0 ) memFree( m_data ); m_data = mem; } else { if ( pos < m_size ) memmove( m_data + pos + 1, m_data + pos, esize( m_size - pos ) ); LinearDictEntry *entry = (LinearDictEntry *) (m_data + pos); entry->key( key ); entry->value( value ); } length( m_size + 1 ); return true; } bool LinearDict::removeAt( uint32 pos ) { if ( pos >= m_size ) return false; if ( pos < m_size - 1 ) memmove( m_data + pos, m_data + pos + 1, esize( m_size - pos ) ); // otherwise, there's nothing to move... length( m_size - 1 ); // for now, do not reallocate. m_invalidPos = pos; invalidateIteratorOnCriterion(); return true; } bool LinearDict::findInternal( const Item &key, uint32 &ret_pos ) const { uint32 lower = 0, higher, point; higher = m_size; if ( higher == 0 ) { ret_pos = 0; return false; } higher --; point = higher / 2; LinearDictEntry *current; int comparation; while ( true ) { // get the table row current = m_data + point; comparation = key.compare( current->key() ); if ( comparation == 0 ) { ret_pos = point; return true; } else { if ( lower == higher ) // not found { break; } // last try. In pair sized dictionaries, it can be also in the other node else if ( lower == higher -1 ) { // key is EVEN less than the lower one if ( comparation < 0 ) { ret_pos = lower; return false; } // being integer math, ulPoint is rounded by defect and has // already looked at the ulLower position point = lower = higher; // try again continue; } if ( comparation > 0 ) { lower = point; } else { higher = point; } point = ( lower + higher ) / 2; } } // entry not found, but signal the best match anyway ret_pos = comparation > 0 ? higher + 1 : higher; return false; } LinearDict *LinearDict::clone() const { LinearDict *ret; if ( m_size == 0 ) { ret = new LinearDict(); } else { ret = new LinearDict( m_size ); ret->length( m_size ); memcpy( ret->m_data, m_data, esize( m_size ) ); // duplicate strings for ( uint32 i = 0; i < m_size; ++i ) { Item& item = m_data[i].m_value; if( item.isString() && item.asString()->isCore() ) { item = new CoreString( *item.asString() ); } } } return ret; } void LinearDict::clear() { memFree( m_data ); m_data = 0; m_alloc = 0; m_size = 0; invalidateAllIters(); } void LinearDict::gcMark( uint32 gen ) { if ( m_mark != gen ) { m_mark = gen; Sequence::gcMark( gen ); for( uint32 i = 0; i < length(); ++i ) { memPool->markItem( m_data[i].key() ); memPool->markItem( m_data[i].value() ); } } } //============================================================ // Iterator management. //============================================================ void LinearDict::getIterator( Iterator& tgt, bool tail ) const { Sequence::getIterator( tgt, tail ); tgt.position( tail ? (length()>0? length()-1: 0) : 0 ); } void LinearDict::copyIterator( Iterator& tgt, const Iterator& source ) const { Sequence::copyIterator( tgt, source ); tgt.position( source.position() ); } void LinearDict::insert( Iterator &iter, const Item &data ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "LinearDict::insert" ) ); } void LinearDict::erase( Iterator &iter ) { if ( iter.position() >= length() ) throw new AccessError( ErrorParam( e_iter_outrange, __LINE__ ) .origin( e_orig_runtime ).extra( "LinearDict::erase" ) ); uint32 pos = (uint32)iter.position(); if ( pos < m_size - 1 ) memmove( m_data + pos, m_data + pos + 1, esize( m_size - pos ) ); // otherwise, there's nothing to move... length( m_size - 1 ); // the next item has automatically moved on the position pointed by us // but the other iterators should be killed. invalidateAnyOtherIter( &iter ); } bool LinearDict::hasNext( const Iterator &iter ) const { return iter.position()+1 < length(); } bool LinearDict::hasPrev( const Iterator &iter ) const { return iter.position() > 0; } bool LinearDict::hasCurrent( const Iterator &iter ) const { return iter.position() < length(); } bool LinearDict::next( Iterator &iter ) const { if ( iter.position() < length() ) { iter.position( iter.position() + 1 ); return iter.position() < length(); } return false; } bool LinearDict::prev( Iterator &iter ) const { if ( iter.position() > 0 ) { iter.position( iter.position() - 1 ); return true; } iter.position( length() ); return false; } Item& LinearDict::getCurrent( const Iterator &iter ) { if ( iter.position() < length() ) return m_data[ iter.position() ].value(); throw new AccessError( ErrorParam( e_iter_outrange, __LINE__ ) .origin( e_orig_runtime ).extra( "LinearDict::getCurrent" ) ); } Item& LinearDict::getCurrentKey( const Iterator &iter ) { if ( iter.position() < length() ) return m_data[ iter.position() ].key(); throw new AccessError( ErrorParam( e_iter_outrange, __LINE__ ) .origin( e_orig_runtime ).extra( "LinearDict::getCurrent" ) ); } bool LinearDict::equalIterator( const Iterator &first, const Iterator &second ) const { return first.position() == second.position(); } bool LinearDict::onCriterion( Iterator* elem ) const { return elem->position() < m_invalidPos; } } /* end of dict.cpp */ engine/linemap.cpp000066400000000000000000000027641176363201700144730ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: linemap.cpp Line map with module serialization support. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom ago 21 2005 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Line map with module serialization support. */ #include #include #include #include namespace Falcon { LineMap::LineMap(): Map( &traits::t_int(), &traits::t_int() ) {} bool LineMap::save( Stream *out ) const { uint32 s = endianInt32(size()); out->write( &s , sizeof( s ) ); MapIterator iter = begin(); while( iter.hasCurrent() ) { s = endianInt32( *(uint32 *) iter.currentKey() ); out->write( &s , sizeof( s ) ); s = endianInt32( *(uint32 *) iter.currentValue() ); out->write( &s , sizeof( s ) ); iter.next(); } return true; } bool LineMap::load( Stream *in ) { uint32 s; in->read( &s , sizeof( s ) ); s = endianInt32( s ); while( s > 0 ) { uint32 pos; uint32 line; in->read( &pos , sizeof( pos ) ); pos = endianInt32( pos ); in->read( &line , sizeof( line ) ); line = endianInt32( line ); insert( &pos, &line ); --s; } return true; } } /* end of linemap.cpp */ engine/livemodule.cpp000066400000000000000000000106361176363201700152100ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: livemodule.cpp The Representation of module live data once linked in a VM ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 03 Apr 2009 23:27:53 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file MThe Representation of module live data once linked in a VM */ #include #include #include #include #include namespace Falcon { //================================================================================= // The live module class. //================================================================================= LiveModule::LiveModule( Module *mod, bool bPrivate ): Garbageable(), m_module( mod ), m_aacc( 0 ), m_iacc( 0 ), m_bPrivate( bPrivate ), m_bAlive(true), m_needsCompleteLink( true ), m_initState( init_none ) { m_module->incref(); m_strCount = mod->stringTable().size(); if( m_strCount > 0 ) { m_strings = static_cast( memAlloc( sizeof(CoreString*) * m_strCount ) ); memset( m_strings, 0, sizeof(CoreString*) * m_strCount ); } else { m_strings = 0; } } LiveModule::~LiveModule() { fassert( m_module != 0 ); if ( m_strings != 0 ) memFree( m_strings ); m_module->decref(); memPool->accountItems( m_iacc ); gcMemAccount( m_aacc ); } void LiveModule::detachModule() { // disengage all the items. uint32 i; // TODO: memset to 0 for ( i = 0; i < m_globals.length(); ++i ) { // disengage but not dereferece; we want to nil the globals here, // not to destroy the imported symbols. m_globals[ i ].setNil(); } for ( i = 0; i < m_wkitems.length(); ++i ) { wkitems()[i].dereference()->setNil(); } m_bAlive = false; } Item *LiveModule::findModuleItem( const String &symName ) const { if ( ! isAlive() ) return 0; const Symbol *sym = m_module->findGlobalSymbol( symName ); if ( sym == 0 ) return 0; return const_cast(&m_globals[ sym->itemId() ]); } bool LiveModule::finalize() { // resist early destruction return false; } void LiveModule::gcMark( uint32 mk ) { if( mk != mark() ) { mark( mk ); for( uint32 i = 0; i < m_strCount; ++i ) { if( m_strings[i] != 0 ) m_strings[i]->mark( mk ); } globals().gcMark( mk ); wkitems().gcMark( mk ); userItems().gcMark( mk ); } } String* LiveModule::getString( uint32 stringId ) const { fassert( stringId < (uint32) m_module->stringTable().size() ); if( stringId >= m_strCount ) { m_strings = static_cast(memRealloc( m_strings, sizeof( CoreString* ) * (stringId+1) )); memset( m_strings + m_strCount, 0, sizeof( CoreString* ) * ( stringId - m_strCount +1) ); m_strCount = stringId+1; } if( m_strings[stringId] == 0 ) { CoreString* dest = new CoreString( *m_module->stringTable().get( stringId ) ); m_strings[stringId] = dest; dest->bufferize(); gcMemUnaccount( sizeof( CoreString ) + dest->allocated() ); memPool->accountItems( -1 ); m_aacc += sizeof( CoreString ) + dest->allocated(); m_iacc++; return dest; } return m_strings[stringId]; } //================================================================================= // Live module related traits //================================================================================= uint32 LiveModulePtrTraits::memSize() const { return sizeof( LiveModule * ); } void LiveModulePtrTraits::init( void *itemZone ) const { itemZone = 0; } void LiveModulePtrTraits::copy( void *targetZone, const void *sourceZone ) const { LiveModule **target = (LiveModule **) targetZone; LiveModule *source = (LiveModule *) sourceZone; *target = source; } int LiveModulePtrTraits::compare( const void *firstz, const void *secondz ) const { // never used as key return 0; } void LiveModulePtrTraits::destroy( void *item ) const { /* Owned by GC LiveModule *ptr = *(LiveModule **) item; delete ptr; */ } bool LiveModulePtrTraits::owning() const { /* Owned by GC */ return false; } } /* end of livemodule.cpp */ engine/ltree.cpp000066400000000000000000000035641176363201700141600ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: ltree.cpp Strong list definition ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat Feb 26 2005 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Strong list definitions */ #include #include namespace Falcon { void StrongList::push_front( SLElement *elem ) { elem->next( m_head ); elem->prev(0); if( m_tail == 0 ) m_tail = elem; else m_head->prev( elem ); m_head = elem; elem->owner( this ); m_size++; } void StrongList::push_back( SLElement *elem ) { elem->next( 0 ); elem->prev( m_tail ); if( m_head == 0 ) m_head = elem; else m_tail->next( elem ); m_tail = elem; elem->owner( this ); m_size++; } SLElement *StrongList::pop_front() { SLElement *h = m_head; if (m_head != 0 ) { m_head = m_head->next(); if ( m_head != 0 ) m_head->prev(0); else m_tail = 0; m_size--; h->owner( 0 ); } return h; } SLElement *StrongList::pop_back() { SLElement *t = m_tail; if (m_tail != 0 ) { m_tail = m_tail->prev(); if ( m_tail != 0 ) m_tail->next(0); else m_head = 0; m_size--; t->owner( 0 ); } return t; } void StrongList::remove( SLElement *elem ) { if ( m_size == 0 || elem->owner() != this ) return; if ( elem == m_head ) { m_head = elem->next(); } else { elem->prev()->next( elem->next() ); } if ( elem == m_tail ) { m_tail = elem->prev(); } else { elem->next()->prev( elem->prev() ); } m_size--; elem->owner( 0 ); } } /* end of ltree.cpp */ engine/membuf.cpp000066400000000000000000000245631176363201700143220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: membuf.cpp Core memory buffer. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 17 Mar 2008 23:07:21 +0100 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Memory buffer - Pure memory for Falcon. */ #include #include #include #include #include #include namespace Falcon { MemBuf::MemBuf( uint32 ws, uint32 length ): Garbageable(), m_length( length ), m_mark( INVALID_MARK ), m_limit( length ), m_position( 0 ), m_wordSize( ws ), m_byteOrder(0xFEFF), m_dependant(0) { m_memory = (byte *) memAlloc( length * ws ); m_deletor = memFree; } MemBuf::MemBuf( uint32 ws, byte *data, uint32 length ): Garbageable(), m_memory( data ), m_length( length ), m_mark( INVALID_MARK ), m_limit( length ), m_position( 0 ), m_wordSize( ws ), m_dependant(0), m_deletor( 0 ) { } MemBuf::MemBuf( uint32 ws, byte *data, uint32 length, tf_deletor deletor ): Garbageable(), m_memory( data ), m_length( length ), m_mark( INVALID_MARK ), m_limit( length ), m_position( 0 ), m_wordSize( ws ), m_dependant(0), m_deletor( deletor ) { } MemBuf::~MemBuf() { if ( m_deletor != 0 && m_memory != 0 ) m_deletor( m_memory ); } void MemBuf::setData( byte *data, uint32 size, tf_deletor deletor ) { if ( m_deletor != 0 && m_memory != 0 ) m_deletor( m_memory ); m_memory = data; m_length = size/m_wordSize; m_deletor = deletor; } void MemBuf::resize( uint32 newSize ) { uint32 nsize = newSize * m_wordSize; m_memory = (byte*) memRealloc( m_memory, nsize ); if ( m_limit > newSize ) { m_limit = newSize; if ( m_position > newSize ) { m_position = newSize; if ( m_mark > newSize ) { m_mark = newSize; } } } m_length = newSize; } bool MemBuf::serialize( Stream *stream, bool bLive ) const { if ( bLive ) { // write the live serializer MemBuf *(*funcptr)( VMachine *vm, Stream *stream ) = MemBuf::deserialize; stream->write( &funcptr, sizeof( funcptr ) ); } uint32 wSize = endianInt32( wordSize() ); stream->write( &wSize, sizeof( wSize ) ); if ( ! stream->good() ) return false; wSize = endianInt32( m_length ); stream->write( &wSize, sizeof( wSize ) ); int16 ws = endianInt16( m_wordSize ); stream->write( &ws, sizeof( ws ) ); if ( ! stream->good() ) return false; if ( m_length > 0 ) { stream->write( m_memory, m_length * m_wordSize ); if ( ! stream->good() ) return false; } return true; } MemBuf *MemBuf::deserialize( VMachine *vm, Stream *stream ) { uint32 nWordSize; if ( stream->read( &nWordSize, sizeof( nWordSize ) ) != sizeof( nWordSize ) ) return 0; nWordSize = endianInt32( nWordSize ); if ( nWordSize < 1 || nWordSize > 4 ) return 0; uint32 nWS; if ( stream->read( &nWS, sizeof( nWS ) ) != sizeof( nWS ) ) return 0; uint32 nSize; if ( stream->read( &nSize, sizeof( nSize ) ) != sizeof( nSize ) ) return 0; nSize = (uint32) endianInt32( nSize ); byte *mem = (byte *) memAlloc( nSize * nWS ); if ( mem == 0 ) return 0; if ( stream->read( mem, nSize*nWS ) != (int32) (nSize*nWS) ) { memFree( mem ); return 0; } switch( nWordSize ) { case 1: return new MemBuf_1( mem, nSize, memFree ); case 2: return new MemBuf_2( mem, nSize, memFree ); case 3: return new MemBuf_3( mem, nSize, memFree ); case 4: return new MemBuf_4( mem, nSize, memFree ); } return 0; // impossible } MemBuf *MemBuf::create( int bpp, uint32 nSize ) { switch( bpp ) { case 1: return new MemBuf_1( nSize ); case 2: return new MemBuf_2( nSize ); case 3: return new MemBuf_3( nSize ); case 4: return new MemBuf_4( nSize ); } return 0; } MemBuf *MemBuf::clone() const { byte* data; if( m_length != 0 ) { data = (byte*) memAlloc( m_length * m_wordSize ); memcpy( data, m_memory, m_length * m_wordSize ); } else data = 0; switch( m_wordSize ) { case 1: return new MemBuf_1( data, m_length, memFree ); case 2: return new MemBuf_2( data, m_length, memFree ); case 3: return new MemBuf_3( data, m_length, memFree ); case 4: return new MemBuf_4( data, m_length, memFree ); } return 0; } uint32 MemBuf_1::get( uint32 pos ) const { return m_memory[ pos ]; } void MemBuf_1::set( uint32 pos, uint32 value ) { m_memory[pos] = (byte) value; } uint32 MemBuf_2::get( uint32 pos ) const { return endianInt16(((uint16 *)m_memory)[pos]); } void MemBuf_2::set( uint32 pos, uint32 value ) { ((uint16 *)m_memory)[pos] = endianInt16((uint16) value); } uint32 MemBuf_3::get( uint32 pos ) const { byte *p = m_memory + (pos * 3); // always act as little endian return p[0] | p[1] << 8 | p[2] << 16; } void MemBuf_3::set( uint32 pos, uint32 value ) { byte *p = m_memory + (pos * 3); // always act as little endian p[0] = value & 0xff; p[1] = (value >> 8) & 0xff; p[2] = (value >> 16) & 0xff; } uint32 MemBuf_4::get( uint32 pos ) const { return endianInt32(((uint32 *)m_memory)[pos]); } void MemBuf_4::set( uint32 pos, uint32 value ) { ((uint32 *)m_memory)[pos] = endianInt32( (uint32)value); } void MemBuf::readProperty( const String &prop, Item &item ) { VMachine *vm = VMachine::getCurrent(); fassert( vm != 0 ); // try to find a generic method CoreClass* cc = vm->getMetaClass( FLC_ITEM_MEMBUF ); if ( cc != 0 ) { uint32 id; if( cc->properties().findKey( prop, id ) ) { item = *cc->properties().getValue( id ); item.methodize( this ); return; } } String extra; item.typeName( extra ); extra.A( '.' ).A( prop ); throw new AccessError( ErrorParam( e_prop_acc, __LINE__ ).extra( extra ) ); } void MemBuf::writeProperty( const String &prop, const Item &item ) { throw new AccessError( ErrorParam( e_prop_ro, __LINE__ ).extra( prop ) ); } void MemBuf::readIndex( const Item &index, Item &target ) { switch( index.type() ) { case FLC_ITEM_INT: { int64 pos = (int64) index.asInteger(); uint32 uPos = (uint32) (pos >= 0 ? pos : length() + pos); if ( uPos < length() ) { target = (int64) get( uPos ); return; } } break; case FLC_ITEM_NUM: { int64 pos = (int64) index.asNumeric(); uint32 uPos = (uint32) (pos >= 0 ? pos : length() + pos); if ( uPos < length() ) { target = get( uPos ); return; } } break; case FLC_ITEM_RANGE: { int64 pos = (int64) index.asRangeStart(); int64 end; int64 step; if( index.asRangeIsOpen() ) { if( pos == 0 ) { target = clone(); return; } end = m_length; } else end = index.asRangeEnd(); if ( pos < 0 ) pos += m_length; if ( end < 0 ) end += m_length; if( pos >= m_length || end > m_length ) break; step = index.asRangeStep(); MemBuf* mb; if ( pos <= end ) { if ( step < 0 ) { mb = create( m_wordSize, 0 ); } else { if ( step == 0 ) step = 1; uint32 size = (uint32) ((end - pos)/step); mb = create( m_wordSize, size ); for (uint32 i = 0; pos < end; i++, pos += step ) { mb->set(i, get((uint32)pos)); } } } else { if ( step > 0 ) { mb = create( m_wordSize, 0 ); } else { if ( step == 0 ) step = -1; uint32 size = (uint32) ((pos-end)/(-step))+1; mb = create( m_wordSize, size ); for (uint32 i = 0; pos >= end; i++, pos += step ) { mb->set(i, get((uint32)pos)); } } } target = mb; return; } break; case FLC_ITEM_REFERENCE: readIndex( index.asReference()->origin(), target ); return; } throw new AccessError( ErrorParam( e_arracc, __LINE__ ).extra( "LDV" ) ); } void MemBuf::writeIndex( const Item &index, const Item &target ) { uint32 data; switch( target.type() ) { case FLC_ITEM_INT: data = (uint32) target.asInteger(); break; case FLC_ITEM_NUM: data = (uint32) target.asNumeric(); break; case FLC_ITEM_REFERENCE: writeIndex( index, target.asReference()->origin() ); return; default: throw new TypeError( ErrorParam( e_param_type, __LINE__ ).extra( "STV" ) ); } switch( index.type() ) { case FLC_ITEM_INT: { int64 pos = (int64) index.asInteger(); uint32 uPos = (uint32) (pos >= 0 ? pos : length() + pos); if ( uPos < length() ) { set( uPos, data ); return; } } break; case FLC_ITEM_NUM: { int64 pos = (int64) index.asNumeric(); uint32 uPos = (uint32) (pos >= 0 ? pos : length() + pos); if ( uPos < length() ) { set( uPos, data ); return; } } break; case FLC_ITEM_REFERENCE: writeIndex( index.asReference()->origin(), target ); return; } throw new AccessError( ErrorParam( e_arracc, __LINE__ ).extra( "STV" ) ); } void MemBuf::compact() { uint32 count = remaining(); if ( count > 0 ) { memmove( m_memory, m_memory + (m_position * m_wordSize), count * m_wordSize ); } m_mark = INVALID_MARK; m_position = count; m_limit = m_length; } void MemBuf::gcMark( uint32 gen ) { if ( mark() != gen ) { mark( gen ); // small optimization; resolve the problem here instead of looping again. if( dependant() != 0 && dependant()->mark() != gen ) { dependant()->gcMark( gen ); } } } } /* end of membuf.cpp */ engine/memhash.cpp000066400000000000000000000023521176363201700144610ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: memhash.cpp Calculates checksum + hash value of a memory range ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun ago 23 23:01:46 CEST 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include namespace Falcon { uint32 calcMemHash( const char *memory, uint32 size ) { uint32 sum = flc_HASH_SEED; int i = 2; for( const char *p = memory; p < memory + size; p++, i++ ) { sum += static_cast( *p ) * i; } return sum; } uint32 calcCstrHash( const char *cstring ) { uint32 sum = flc_HASH_SEED; int i = 2; for( const char *p = cstring; *p != 0; p++, i++ ) { sum += static_cast( *p ) * i; } return sum; } uint32 calcStringHash( const String &str ) { uint32 sum = flc_HASH_SEED; int i = 2; uint32 len = str.length(); for( uint32 pos = 0; pos < len ; pos++, i++ ) { sum += str.getCharAt( pos ) * i; } return sum; } } /* end of memhash.cpp */ engine/memory.cpp000066400000000000000000000076041176363201700143540ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_memory.cpp Basic memory manager functions and function pointers. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2004-08-01 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #if defined(__SUNPRO_CC) #include #elif defined(__BORLANDC__) #include #include #else #include #include #endif #ifdef FALCON_SYSTEM_WIN #include #else #include #endif namespace Falcon { /* #ifdef _MSC_VER const builder_t Builder; #else const builder_t Builder = {}; #endif */ void * DflMemAlloc( size_t amount ) { void *ret = malloc( amount + sizeof(size_t) ); if ( ret == 0 ) { printf( "Falcon: fatal allocation error when allocating %d bytes\n", (int) amount ); exit(1); } return ret; } void DflMemFree( void *mem ) { free( mem ); } void * DflMemRealloc( void *mem, size_t amount ) { void *ret = realloc( mem, amount ); if ( ret == 0 && amount != 0 ) { printf( "Falcon: fatal reallocation error when allocating %d bytes\n", (int) amount ); exit(1); } return ret; } void * DflAccountMemAlloc( size_t amount ) { size_t *ret = (size_t*) malloc( amount + sizeof(size_t) ); if ( ret == 0 ) { printf( "Falcon: fatal allocation error when allocating %d bytes\n", (int) amount ); exit(1); } gcMemAccount( amount ); *ret = amount; return ret+1; } void DflAccountMemFree( void *mem ) { if ( mem != 0 ) { size_t *smem = (size_t*) mem; gcMemUnaccount( smem[-1] ); free( smem-1 ); } } void * DflAccountMemRealloc( void *mem, size_t amount ) { if ( amount == 0 ) { DflAccountMemFree( mem ); return 0; } if ( mem == 0 ) return DflAccountMemAlloc( amount ); size_t *smem = (size_t*) mem; smem--; size_t oldalloc = *smem; size_t *nsmem = (size_t*) realloc( smem, amount + sizeof( size_t ) ); if ( nsmem == 0 ) { printf( "Falcon: fatal reallocation error when allocating %d bytes\n", (int) amount ); exit(1); } *nsmem = amount; if( amount > oldalloc ) gcMemAccount( amount - oldalloc ); else gcMemUnaccount( oldalloc - amount ); return nsmem+1; } //=================================================================================== // Account functions // static Mutex *s_gcMutex = 0; static size_t s_allocatedMem = 0; void gcMemAccount( size_t mem ) { if( s_gcMutex == 0 ) s_gcMutex = new Mutex; s_gcMutex->lock(); s_allocatedMem += mem; s_gcMutex->unlock(); } void gcMemUnaccount( size_t mem ) { if( s_gcMutex == 0 ) s_gcMutex = new Mutex; s_gcMutex->lock(); s_allocatedMem -= mem; s_gcMutex->unlock(); } size_t gcMemAllocated() { if( s_gcMutex == 0 ) s_gcMutex = new Mutex; s_gcMutex->lock(); register uint32 val = s_allocatedMem; s_gcMutex->unlock(); return val; } void gcMemShutdown() { delete s_gcMutex; s_gcMutex = 0; } //============================================================ // Global function pointers. // void * (*gcAlloc) ( size_t ) = DflAccountMemAlloc; void (*gcFree) ( void * ) = DflAccountMemFree; void * (*gcRealloc) ( void *, size_t ) = DflAccountMemRealloc; /* void * (*memAlloc) ( size_t ) = DflMemAlloc; void (*memFree) ( void * ) = DflMemFree; void * (*memRealloc) ( void *, size_t ) = DflMemRealloc; */ /* In phase 1, we're accounting everything to verify the basic solidity of our GC system. */ void * (*memAlloc) ( size_t ) = DflAccountMemAlloc; void (*memFree) ( void * ) = DflAccountMemFree; void * (*memRealloc) ( void *, size_t ) = DflAccountMemRealloc; } /* end of flc_memory.cpp */ engine/mempool.cpp000066400000000000000000000576611176363201700145240ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: mempool.cpp Memory management system ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2004-08-03 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define GC_IDLE_TIME 250 // default 1M #define GC_THREAD_STACK_SIZE 1024*1024 // By default, 1MB #define TEMP_MEM_THRESHOLD 1000000 namespace Falcon { MemPool* memPool = 0; MemPool::MemPool(): m_mingen( 0 ), m_bNewReady( true ), m_olderVM( 0 ), m_vmRing(0), m_vmCount(0), m_vmIdle_head( 0 ), m_vmIdle_tail( 0 ), m_generation( 0 ), m_allocatedItems( 0 ), m_allocatedMem( 0 ), m_th(0), m_bLive(false), m_bRequestSweep( false ), m_lockGen( 0 ) { m_vmRing = 0; // use a ring for garbage items. m_garbageRoot = new GarbageableBase; m_garbageRoot->nextGarbage( m_garbageRoot ); m_garbageRoot->prevGarbage( m_garbageRoot ); // Use a ring also for the garbageLock system. m_lockRoot = new GarbageLock( true ); m_lockRoot->next( m_lockRoot ); m_lockRoot->prev( m_lockRoot ); // separate the newly allocated items to allow allocations during sweeps. m_newRoot = new GarbageableBase; m_newRoot->nextGarbage( m_newRoot ); m_newRoot->prevGarbage( m_newRoot ); m_thresholdNormal = TEMP_MEM_THRESHOLD; m_thresholdActive = TEMP_MEM_THRESHOLD*3; // fill the ramp algorithms m_ramp[RAMP_MODE_OFF] = new RampNone; m_ramp[RAMP_MODE_STRICT_ID] = new RampStrict; m_ramp[RAMP_MODE_LOOSE_ID] = new RampLoose; m_ramp[RAMP_MODE_SMOOTH_SLOW_ID] = new RampSmooth( 2.6 ); m_ramp[RAMP_MODE_SMOOTH_FAST_ID] = new RampSmooth( 6.5 ); // force initialization in rampMode by setting a different initial value; m_curRampID = DEFAULT_RAMP_MODE+1; rampMode( DEFAULT_RAMP_MODE ); } MemPool::~MemPool() { // ensure the thread is down. stop(); clearRing( m_newRoot ); clearRing( m_garbageRoot ); delete m_newRoot; delete m_garbageRoot; // delete the garbage lock ring. GarbageLock *ge = m_lockRoot->next(); while( ge != m_lockRoot ) { GarbageLock *gnext = ge->next(); delete ge; ge = gnext; } delete ge; // VMs are not mine, and they should be already dead since long. for( uint32 ri = 0; ri < RAMP_MODE_COUNT; ri++ ) delete m_ramp[ri]; } bool MemPool::rampMode( int mode ) { if( mode >= 0 && mode < RAMP_MODE_COUNT ) { m_mtx_ramp.lock(); if ( m_curRampID != mode ) { m_curRampID = mode; m_curRampMode = m_ramp[mode]; m_curRampMode->reset(); } m_mtx_ramp.unlock(); return true; } return false; } int MemPool::rampMode() const { m_mtx_ramp.lock(); int mode = m_curRampID; m_mtx_ramp.unlock(); return mode; } void MemPool::safeArea() { m_mtx_newitem.lock(); m_bNewReady = false; m_mtx_newitem.unlock(); } void MemPool::unsafeArea() { m_mtx_newitem.lock(); m_bNewReady = true; m_mtx_newitem.unlock(); } void MemPool::registerVM( VMachine *vm ) { // already registered if( vm->m_nextVM != 0 || vm->m_prevVM ) return; vm->m_idlePrev = vm->m_idleNext = 0; vm->incref(); m_mtx_vms.lock(); vm->m_generation = ++m_generation; // rollover detection in run() ++m_vmCount; if ( m_vmRing == 0 ) { m_vmRing = vm; vm->m_nextVM = vm; vm->m_prevVM = vm; m_mingen = vm->m_generation; vm->incref(); m_olderVM = vm; } else { vm->m_prevVM = m_vmRing; vm->m_nextVM = m_vmRing->m_nextVM; m_vmRing->m_nextVM->m_prevVM = vm; m_vmRing->m_nextVM = vm; // also account for older VM. if ( m_mingen == vm->m_generation ) { vm->incref(); m_olderVM->decref(); m_olderVM = vm; } } m_mtx_vms.unlock(); } void MemPool::unregisterVM( VMachine *vm ) { m_mtx_vms.lock(); // disengage vm->m_nextVM->m_prevVM = vm->m_prevVM; vm->m_prevVM->m_nextVM = vm->m_nextVM; // was this the ring top? if ( m_vmRing == vm ) { m_vmRing = m_vmRing->m_nextVM; // still the ring top? -- then the ring is empty if( m_vmRing == vm ) m_vmRing = 0; } --m_vmCount; // is this the oldest VM? -- then we got to elect a new one. if( vm == m_olderVM ) { electOlderVM(); } m_mtx_vms.unlock(); // ok, the VM is not held here anymore. vm->decref(); } // WARNING -- this must be called with m_mtx_vms locked void MemPool::electOlderVM() { // Nay, we don't have any VM. if ( m_vmRing == 0 ) { if ( m_olderVM != 0 ) { m_olderVM->decref(); m_olderVM = 0; } } else { VMachine *vmc = m_vmRing; m_mingen = vmc->m_generation; VMachine* vmmin = vmc; vmc = vmc->m_nextVM; while( vmc != m_vmRing ) { if ( vmc->m_generation < m_mingen ) { m_mingen = vmc->m_generation; vmmin = vmc; } vmc = vmc->m_nextVM; } vmmin->incref(); if( m_olderVM != 0 ) m_olderVM->decref(); m_olderVM = vmmin; } } void MemPool::clearRing( GarbageableBase *ringRoot ) { TRACE( "Entering sweep %ld, allocated %ld", (long)gcMemAllocated(), (long)m_allocatedItems ); // delete the garbage ring. int32 killed = 0; GarbageableBase *ring = m_garbageRoot->nextGarbage(); // live modules must be killed after all their data. For this reason, we must put them aside. GarbageableBase *later_ring = 0; while( ring != m_garbageRoot ) { if ( ring->mark() < m_mingen ) { ring->nextGarbage()->prevGarbage( ring->prevGarbage() ); ring->prevGarbage()->nextGarbage( ring->nextGarbage() ); GarbageableBase *dropped = ring; ring = ring->nextGarbage(); // a module? -- do it later if( ! dropped->finalize() ) { dropped->nextGarbage(later_ring); dropped->prevGarbage( 0 ); later_ring = dropped; } else killed++; } else { ring = ring->nextGarbage(); } } TRACE( "Sweeping step 1 complete %ld", (long)gcMemAllocated() ); // deleting persistent finalized items. while( later_ring != 0 ) { GarbageableBase *current = later_ring; later_ring = later_ring->nextGarbage(); delete current; killed++; } TRACE( "Sweeping step 2 complete %ld", (long)gcMemAllocated() ); m_mtx_newitem.lock(); fassert( killed <= m_allocatedItems ); m_allocatedItems -= killed; m_mtx_newitem.unlock(); TRACE( "Sweeping done, allocated %ld (killed %ld)", (long)m_allocatedItems, (long)killed ); } void MemPool::storeForGarbage( Garbageable *ptr ) { // We mark newly created items as the maximum possible value // so they can't be reclaimed until marked at least once. ptr->mark( MAX_GENERATION ); m_mtx_newitem.lock(); m_allocatedItems++; ptr->nextGarbage( m_newRoot ); ptr->prevGarbage( m_newRoot->prevGarbage() ); m_newRoot->prevGarbage()->nextGarbage( ptr ); m_newRoot->prevGarbage( ptr ); m_mtx_newitem.unlock(); } void MemPool::accountItems( int itemCount ) { m_mtx_newitem.lock(); m_allocatedItems += itemCount; m_mtx_newitem.unlock(); } bool MemPool::markVM( VMachine *vm ) { // mark all the messaging system. vm->markSlots( generation() ); // mark the global symbols // When generational gc will be on, this won't be always needed. MapIterator iter = vm->liveModules().begin(); while( iter.hasCurrent() ) { LiveModule *currentMod = *(LiveModule **) iter.currentValue(); // We must mark the current module. currentMod->gcMark( generation() ); iter.next(); } // mark all the items in the coroutines. ListElement *ctx_iter = vm->getCtxList()->begin(); uint32 pos; while( ctx_iter != 0 ) { VMContext *ctx = (VMContext *) ctx_iter->data(); markItem( ctx->regA() ); markItem( ctx->regB() ); markItem( ctx->latch() ); markItem( ctx->latcher() ); markItem( ctx->self() ); markItem( vm->regBind() ); markItem( vm->regBindP() ); StackFrame* sf = ctx->currentFrame(); while( sf != 0 ) { Item* stackItems = sf->stackItems(); uint32 sl = sf->stackSize(); markItem( sf->m_self ); markItem( sf->m_binding ); for( pos = 0; pos < sl; pos++ ) { markItem( stackItems[ pos ] ); } sf = sf->prev(); } ctx_iter = ctx_iter->next(); } return true; } void MemPool::markItem( const Item &item ) { uint32 gen = generation(); switch( item.type() ) { case FLC_ITEM_RANGE: item.asRange()->gcMark( gen ); break; case FLC_ITEM_GCPTR: item.asGCPointerShell()->gcMark( gen ); break; case FLC_ITEM_ARRAY: item.asArray()->gcMark( gen ); break; case FLC_ITEM_DICT: item.asDict()->gcMark( gen ); break; case FLC_ITEM_OBJECT: item.asObject()->gcMark( gen ); break; case FLC_ITEM_MEMBUF: item.asMemBuf()->gcMark( gen ); break; case FLC_ITEM_CLASS: item.asClass()->gcMark( gen ); break; case FLC_ITEM_FUNC: item.asFunction()->gcMark( gen ); break; case FLC_ITEM_REFERENCE: { GarbageItem *gi = item.asReference(); if( gi->mark() != gen ) { gi->mark( gen ); markItem( gi->origin() ); } } break; case FLC_ITEM_LBIND: if ( item.asFBind() != 0 ) { item.asFBind()->gcMark( gen ); } // fallthrough case FLC_ITEM_STRING: { String* str = item.asString(); if( str->isCore() ) { StringGarbage &gs = static_cast(str)->garbage(); gs.mark( gen ); } } break; case FLC_ITEM_METHOD: { // if the item isn't alive, give it the death blow. if( item.asMethodFunc()->mark() != gen ) { CallPoint* cp = item.asMethodFunc(); cp->gcMark( gen ); } Item self; item.getMethodItem( self ); markItem( self ); } break; case FLC_ITEM_CLSMETHOD: { CoreObject *co = item.asMethodClassOwner(); co->gcMark( gen ); CoreClass *cls = item.asMethodClass(); // if the class is the generator of the method, we have already marked it. cls->gcMark( gen ); } break; // all the others are shallow items; already marked } } void MemPool::gcSweep() { TRACE( "Sweeping %ld (mingen: %d, gen: %d)", (long)gcMemAllocated(), m_mingen, m_generation ); m_mtx_ramp.lock(); // ramp mode may change while we do the lock... RampMode* rm = m_curRampMode; rm->onScanInit(); m_mtx_ramp.unlock(); clearRing( m_garbageRoot ); m_mtx_ramp.lock(); rm->onScanComplete(); m_thresholdActive = rm->activeLevel(); m_thresholdNormal = rm->normalLevel(); m_mtx_ramp.unlock(); } int32 MemPool::allocatedItems() const { m_mtx_newitem.lock(); int32 size = m_allocatedItems; m_mtx_newitem.unlock(); return size; } void MemPool::performGC() { m_mtxRequest.lock(); m_bRequestSweep = true; m_eRequest.set(); m_mtxRequest.unlock(); m_eGCPerformed.wait(); } //=================================================================================== // MT functions // void MemPool::idleVM( VMachine *vm, bool bPrio ) { // ok, we're givin the VM to the GC, so we reference it. m_mtx_idlevm.lock(); if ( bPrio ) { vm->m_bPirorityGC = true; } if ( vm->m_idleNext != 0 || vm->m_idlePrev != 0 || vm == m_vmIdle_head) { // already waiting m_mtx_idlevm.unlock(); return; } vm->incref(); vm->m_idleNext = 0; if ( m_vmIdle_head == 0 ) { m_vmIdle_head = vm; m_vmIdle_tail = vm; vm->m_idlePrev = 0; } else { m_vmIdle_tail->m_idleNext = vm; vm->m_idlePrev = m_vmIdle_tail; m_vmIdle_tail = vm; } m_mtx_idlevm.unlock(); // wake up if we're waiting. m_eRequest.set(); } void MemPool::start() { if ( m_th == 0 ) { m_bLive = true; m_th = new SysThread( this ); m_th->start( ThreadParams().stackSize( GC_THREAD_STACK_SIZE ) ); } } void MemPool::stop() { if ( m_th != 0 ) { m_bLive = false; m_eRequest.set(); void *dummy; m_th->join( dummy ); m_th = 0; } } void* MemPool::run() { uint32 oldGeneration = m_generation; uint32 oldMingen = m_mingen; bool bMoreWork; while( m_bLive ) { bMoreWork = false; // in case of sweep request, loop without pause. // first, detect the operating status. size_t memory = gcMemAllocated(); int state = m_bRequestSweep || memory >= m_thresholdActive ? 2 : // active mode memory >= m_thresholdNormal ? 1 : // normal mode 0; // dormient mode TRACE( "Working %ld (in mode %d)", (long)gcMemAllocated(), state ); // put the new ring in the garbage ring m_mtx_newitem.lock(); // Are we in a safe area? if ( m_bNewReady ) { GarbageableBase* newRingFront = m_newRoot->nextGarbage(); if( newRingFront != m_newRoot ) { GarbageableBase* newRingBack = m_newRoot->prevGarbage(); // disengage the chain from the new garbage thing m_newRoot->nextGarbage( m_newRoot ); m_newRoot->prevGarbage( m_newRoot ); // we can release the chain m_mtx_newitem.unlock(); // and now, store the disengaged ring in the standard reclaimable garbage ring. MESSAGE( "Storing the garbage new ring in the normal ring" ); newRingBack->nextGarbage( m_garbageRoot ); newRingFront->prevGarbage( m_garbageRoot->prevGarbage() ); m_garbageRoot->prevGarbage()->nextGarbage( newRingFront ); m_garbageRoot->prevGarbage( newRingBack ); } else m_mtx_newitem.unlock(); } else { m_mtx_newitem.unlock(); MESSAGE( "Skipping new ring inclusion due to safe area lock." ); } // if we're in active mode, send a block request to all the enabled vms. if ( state == 2 ) { m_mtx_vms.lock(); VMachine *vm = m_vmRing; if ( vm != 0 ) { if ( vm->isGcEnabled() ) { TRACE( "Activating blocking request vm %p", vm ); vm->baton().block(); } vm = vm->m_nextVM; while( vm != m_vmRing ) { if ( vm->isGcEnabled() ) { TRACE( "Activating blocking request vm %p", vm ); vm->baton().block(); } vm = vm->m_nextVM; } } m_mtx_vms.unlock(); } VMachine* vm = 0; bool bPriority = false; // In all 3 the modes, we must clear the idle queue, so let's do that. m_mtx_idlevm.lock(); if( m_vmIdle_head != 0 ) { // get the first VM to be processed. vm = m_vmIdle_head; m_vmIdle_head = m_vmIdle_head->m_idleNext; if ( m_vmIdle_head == 0 ) m_vmIdle_tail = 0; else bMoreWork = true; vm->m_idleNext = 0; vm->m_idlePrev = 0; // dormient or not, we must work this VM on priority scans. bPriority = vm->m_bPirorityGC; vm->m_bPirorityGC = false; // if we're dormient, just empty the queue. if ( state == 0 && ! bPriority ) { // this to discard block requets. vm->baton().unblock(); // we're done with this VM here vm->decref(); m_mtx_idlevm.unlock(); TRACE( "Discarding idle vm %p", vm ); continue; } // mark the idle VM if we're not dormient. // ok, we need to reclaim some memory. // (try to acquire only if this is not a priority scan). if ( ! bPriority && ! vm->baton().tryAcquire() ) { m_mtx_idlevm.unlock(); // we're done with this VM here vm->decref(); TRACE( "Was going to mark vm %p, but forfaited", vm ); // oh damn, we lost the occasion. The VM is back alive. continue; } m_mtx_idlevm.unlock(); m_mtx_vms.lock(); // great; start mark loop -- first, set the new generation. advanceGeneration( vm, oldGeneration ); m_mtx_vms.unlock(); TRACE( "Marking idle vm %p at %d", vm, m_generation ); // and then mark markVM( vm ); // should notify now? if ( bPriority ) { if ( ! vm->m_bWaitForCollect ) { bPriority = false; // disable the rest. vm->m_eGCPerformed.set(); vm->decref(); } } else { // the VM is now free to go -- but it is not declared idle again. vm->baton().releaseNotIdle(); // we're done with this VM here vm->decref(); } } else { m_mtx_idlevm.unlock(); } m_mtx_vms.lock(); // Mark of idle VM complete. See if it's useful to promote the last vm. if ( state > 0 && ( m_generation - m_mingen > (unsigned) m_vmCount ) ) { if ( m_olderVM != 0 ) { if( m_olderVM->baton().tryAcquire() ) { VMachine *vm = m_olderVM; // great; start mark loop -- first, set the new generation. advanceGeneration( vm, oldGeneration ); m_mtx_vms.unlock(); TRACE( "Marking oldest vm %p at %d", vm, m_generation ); // and then mark markVM( vm ); // the VM is now free to go. vm->baton().releaseNotIdle(); } else { m_mtx_vms.unlock(); } } else { m_mtx_vms.unlock(); } } else m_mtx_vms.unlock(); // if we have to sweep (we can claim something only if the lower VM has moved). if ( oldMingen != m_mingen || bPriority || m_bRequestSweep ) { bool signal = false; m_mtxRequest.lock(); m_mtx_vms.lock(); if ( m_bRequestSweep ) { if ( m_vmCount == 0 ) { // be sure to clear the garbage oldMingen = m_mingen; m_mingen = SWEEP_GENERATION; m_bRequestSweep = false; signal = true; } else { // HACK: we are not able to kill correctly VMS in multithreading in 0.9.1, // so we just let the request go; // we'll clean them during 0.9.1->0.9.2 //m_bRequestSweep = true; //signal = true; TRACE( "Priority with %d", m_vmCount ); } } m_mtx_vms.unlock(); m_mtxRequest.unlock(); // before sweeping, mark -- eventually -- the locked items. markLocked(); // all is marked, we can sweep gcSweep(); // should we notify about the sweep being complete? if ( bPriority ) { fassert( vm != 0 ); vm->m_eGCPerformed.set(); vm->decref(); } if ( signal ) { m_mingen = oldMingen; m_eGCPerformed.set(); } // no more use for this vm } oldGeneration = m_generation; // spurious read is ok here (?) oldMingen = m_mingen; // if we have nothing to do, we shall wait a bit. if( ! bMoreWork ) { MESSAGE( "Waiting GC idle time" ); m_eRequest.wait(GC_IDLE_TIME); } } TRACE( "Stopping %ld", (long)gcMemAllocated() ); return 0; } // to be called with m_mtx_vms locked void MemPool::advanceGeneration( VMachine* vm, uint32 oldGeneration ) { uint32 curgen = ++m_generation; // detect here rollover. if ( curgen < oldGeneration || curgen >= MAX_GENERATION ) { curgen = m_generation = m_vmCount+1; // perform rollover rollover(); // re-mark everything remark( curgen ); // as we have remarked everything, there's nothing we can do // but wait for the next occasion to do some collection. return; } vm->m_generation = curgen; // Now that we have marked it, if this was the oldest VM, we need to elect the new oldest vm. if ( vm == m_olderVM ) { // calling it with mtx_vms locked electOlderVM(); } } // WARNING: Rollover is to be called with m_mtx_vms locked. void MemPool::rollover() { // Sets the minimal VM. m_mingen = 1; m_vmRing->incref(); if ( m_olderVM != 0 ) m_olderVM->decref(); m_olderVM = m_vmRing; m_olderVM->m_generation = 1; // ramp up the other VMS uint32 curgen = 1; VMachine* vm = m_vmRing->m_nextVM; while( vm != m_vmRing ) { vm->m_generation = ++curgen; vm = vm->m_nextVM; } } void MemPool::remark( uint32 curgen ) { GarbageableBase* gc = m_garbageRoot->nextGarbage(); while( gc != m_garbageRoot ) { // Don't mark objects that are still unassigned. if( gc->mark() != MAX_GENERATION ) gc->mark( curgen ); gc = gc->nextGarbage(); } } void MemPool::promote( uint32 oldgen, uint32 curgen ) { GarbageableBase* gc = m_garbageRoot->nextGarbage(); while( gc != m_garbageRoot ) { if( gc->mark() == oldgen ) gc->mark( curgen ); gc = gc->nextGarbage(); } } void MemPool::addGarbageLock( GarbageLock* ptr ) { m_mtx_lockitem.lock(); ptr->next( m_lockRoot->next() ); ptr->prev( m_lockRoot ); m_lockRoot->next()->prev( ptr ); m_lockRoot->next( ptr ); m_mtx_lockitem.unlock(); markItem( ptr->item() ); } void MemPool::removeGarbageLock( GarbageLock *ptr ) { // frirst: remove the item from the availability pool m_mtx_lockitem.lock(); ptr->next()->prev( ptr->prev() ); ptr->prev()->next( ptr->next() ); m_mtx_lockitem.unlock(); } void MemPool::markLocked() { fassert( m_lockRoot != 0 ); // is there any VM keeping the locked items alive? if ( m_mingen <= m_lockGen ) return; m_lockGen = m_generation; // Lock root never changes. GarbageLock *rlock = this->m_lockRoot; GarbageLock *lock = rlock; Item itm; do { // The root item never needs to be marked m_mtx_lockitem.lock(); lock = lock->next(); itm = lock->item(); m_mtx_lockitem.unlock(); // if a new item is inserted now, NP: // it gets marked with current generation. // If it gets deleted, it will just get an extra mark // and live for an extra turn. memPool->markItem( itm ); } while( lock != rlock ); } //======================================================================= // Garbage Lock //======================================================================= GarbageLock::GarbageLock( bool ) { } GarbageLock::GarbageLock() { memPool->addGarbageLock( this ); } GarbageLock::GarbageLock( const Item &itm ): m_item(itm) { memPool->addGarbageLock( this ); } GarbageLock::~GarbageLock() { memPool->removeGarbageLock( this ); } } /* end of mempool.cpp */ engine/modloader.cpp000066400000000000000000000566351176363201700150220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_modloader.cpp Module loader ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2004-08-20 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BINMODULE_EXT "_fm" namespace Falcon { ModuleLoader::ModuleLoader(): m_alwaysRecomp( false ), m_compMemory( true ), m_saveModule( true ), m_saveMandatory( false ), m_detectTemplate( true ), m_forceTemplate( false ), m_delayRaise( false ), m_ignoreSources( false ), m_saveRemote( false ), m_compileErrors(0) { m_path.deletor( string_deletor ); setSearchPath( "." ); } ModuleLoader::ModuleLoader( const String &path ): m_alwaysRecomp( false ), m_compMemory( true ), m_saveModule( true ), m_saveMandatory( false ), m_detectTemplate( true ), m_forceTemplate( false ), m_delayRaise( false ), m_ignoreSources( false ), m_saveRemote( false ), m_compileErrors(0) { m_path.deletor( string_deletor ); setSearchPath( path ); } ModuleLoader::ModuleLoader( const ModuleLoader &other ): m_alwaysRecomp( other.m_alwaysRecomp ), m_compMemory( other.m_compMemory ), m_saveModule( other.m_saveModule ), m_saveMandatory( other.m_saveMandatory ), m_detectTemplate( other.m_detectTemplate ), m_forceTemplate( other.m_forceTemplate ), m_delayRaise( other.m_delayRaise ), m_ignoreSources( other.m_ignoreSources ), m_saveRemote( other.m_saveRemote ), m_compileErrors( other.m_compileErrors ) { setSearchPath( other.getSearchPath() ); } ModuleLoader::~ModuleLoader() { } ModuleLoader *ModuleLoader::clone() const { return new ModuleLoader( *this ); } void ModuleLoader::getModuleName( const String &path, String &modName ) { // .../modname.xxx // we need at least a char. if ( path.length() == 0 ) { modName = ""; return; } int32 dotpos = path.rfind( "." ); int32 slashpos = path.rfind( "/" ); if ( dotpos == -1 || dotpos < slashpos ) dotpos = path.length(); // legal also if slashpos < 0 modName = path.subString( slashpos + 1, dotpos ); } void ModuleLoader::addFalconPath() { String envpath; bool hasEnvPath = Sys::_getEnv( "FALCON_LOAD_PATH", envpath ); if ( hasEnvPath ) { addSearchPath( envpath ); } else { addSearchPath( FALCON_DEFAULT_LOAD_PATH ); } } void ModuleLoader::setSearchPath( const String &path ) { m_path.clear(); addSearchPath( path ); } void ModuleLoader::addSearchPath( const String &path ) { // subdivide the path by ';' int32 pos = 0, pos1 = 0; // nothing to add ? if ( path == "" ) return;; while( true ) { String *tmp; pos1 = path.find( ";", pos ); if ( pos1 == -1 ) { tmp = new String( path, pos ); tmp->bufferize(); m_path.pushBack( tmp ); break; } if ( pos1 == -1 ) { tmp = new String( path, pos ); tmp->bufferize(); m_path.pushBack( tmp ); break; } if ( pos1 > pos ) { tmp = new String( path, pos, pos1 ); tmp->bufferize(); m_path.pushBack( tmp ); } pos = pos1+1; } } void ModuleLoader::getSearchPath( String &tgt ) const { tgt.size(0); ListElement *path_elem = m_path.begin(); while ( path_elem != 0 ) { String *pathp = (String *) path_elem->data(); tgt += *pathp; path_elem = path_elem->next(); if ( path_elem != 0 ) tgt += ";"; } } Stream *ModuleLoader::openResource( const String &path, t_filetype type ) { // Get the uri URI furi( path ); if ( !furi.isValid() ) { raiseError( e_malformed_uri, path ); // upstream will fill the module } // find the appropriage provider. VFSProvider* vfs = Engine::getVFS( furi.scheme() ); if ( vfs == 0 ) { raiseError( e_unknown_vfs, path ); // upstream will fill the module } Stream *in = vfs->open( furi, VFSProvider::OParams().rdOnly() ); if ( in == 0 ) { throw vfs->getLastError(); } if ( type == t_source || type == t_ftd ) { if ( m_srcEncoding != "" ) { // set input encoding Stream *inputStream = TranscoderFactory( m_srcEncoding, in, true ); if( inputStream != 0 ) return AddSystemEOL( inputStream ); delete in; raiseError( e_unknown_encoding, m_srcEncoding ); // upstream will fill the module } else return AddSystemEOL( in ); } return in; } ModuleLoader::t_filetype ModuleLoader::fileType( const String &fext ) { String ext = fext; ext.lower(); if ( ext == "fal" ) { return t_source; } else if ( ext == "ftd" ) { return t_ftd; } else if ( ext == DllLoader::dllExt() ) { return t_binmod; } else if ( ext == "fam" ) { return t_vmmod; } return t_none; } Module *ModuleLoader::loadName( const String &module_name, const String &parent_name ) { String nmodName; // prevent doing a crashy thing. if ( module_name.length() == 0 ) throw new CodeError( ErrorParam( e_modname_inv ).extra( module_name ).origin( e_orig_loader ). module( "(loader)" ) ); Module::absoluteName( module_name, parent_name, nmodName ); String expName = nmodName; // expand "." names into "/" uint32 pos = expName.find( "." ); while( pos != String::npos ) { expName.setCharAt( pos, '/' ); pos = expName.find( ".", pos + 1 ); } Module *mod = loadFile( expName, t_none, true ); mod->name( nmodName ); return mod; } bool ModuleLoader::scanForFile( URI &origUri, VFSProvider* vfs, t_filetype &type, FileStat &fs ) { // loop over the possible extensions and pick the newest. const char *exts[] = { "ftd", "fal", "fam", DllLoader::dllExt(), 0 }; const t_filetype ftypes[] = { t_ftd, t_source, t_vmmod, t_binmod, t_none }; TimeStamp tsNewest; const char **ext = exts; const t_filetype *ptf = ftypes; // skip source exensions if so required. if ( ignoreSources() ) { ext++; ext++; ptf++; ptf++; } while( *ext != 0 ) { origUri.pathElement().setExtension( *ext ); // did we find it? if ( *ptf == t_binmod ) { // for binary module, add the extra module identifier. URI copy(origUri); copy.pathElement().setFile( copy.pathElement().getFile() + BINMODULE_EXT ); if( vfs->readStats( copy, fs ) ) { origUri.pathElement().setFile( copy.pathElement().getFile() ); type = t_binmod; return true; } } else if ( vfs->readStats( origUri, fs ) ) { type = *ptf; return true; } // get next extension and file type ext++; ptf++; } return false; } Module *ModuleLoader::loadFile( const URI& uri, t_filetype type, bool scan ) { URI origUri = uri; String file_path; t_filetype tf = t_none; VFSProvider *vfs = Engine::getVFS( origUri.scheme() ); if ( vfs == 0 ) { throw new CodeError( ErrorParam( e_unknown_vfs ) .extra( uri.scheme() ) .origin( e_orig_loader ) ); } // Check wether we have absolute files or files to be searched. FileStat foundStats; bool bFound = false; // If we don't have have an absolute path, if ( ! origUri.pathElement().isAbsolute() ) { // ... and if scan is false, we must add our relative path to absolutize it. if ( ! scan ) { String curdir; int32 error; if ( ! Sys::fal_getcwd( curdir, error ) ) { throw new IoError( ErrorParam( e_io_error ) .extra( Engine::getMessage( msg_io_curdir ) ) .origin( e_orig_loader ) .sysError( error ) ); } origUri.pathElement().setFullLocation( curdir + "/" + origUri.pathElement().getFullLocation() ); } } else { // absolute path force scan to be off, just to be sure. scan = false; } // we are interested in knowing if a default extension was given, // in that case we won't go searching for that. bool bHadExtension = origUri.pathElement().getExtension() != ""; // if we don't have to scan in the path... if ( ! scan ) { // ... if type is t_none, we may anyhow scan for a proper extension. if( (type == t_none || type == t_defaultSource) && ! bHadExtension ) { URI saveUri = origUri; bFound = scanForFile( origUri, vfs, tf, foundStats ); if ( ! bFound && type == t_defaultSource ) { origUri = saveUri; // scanforfile may bFound = vfs->readStats( origUri, foundStats ); if ( bFound ) tf = t_source; } } else { // just check if the file exists. tf = type == t_none ? fileType(origUri.pathElement().getExtension()) : type; bFound = vfs->readStats( origUri, foundStats ); } } // shall we scan for a file? else { // ready to scan the list of directories; ListElement *path_elem = m_path.begin(); String oldPath = origUri.pathElement().getLocation(); while ( (! bFound) && path_elem != 0 ) { String *pathp = (String *) path_elem->data(); origUri.pathElement().setFullLocation( *pathp ); origUri.pathElement().extendLocation( oldPath ); // If we originally had an extension, we must not add it. if ( bHadExtension ) { // if the thing exists... if( ( bFound = vfs->readStats( origUri, foundStats ) ) ) { // ... set the file type, either on our default or on the found extension. tf = (type == t_none || type == t_defaultSource ) ? fileType( origUri.pathElement().getExtension() ) : type; } } else { // we must can the possible extensions in this directory bFound = scanForFile( origUri, vfs, tf, foundStats ); } path_elem = path_elem->next(); } } Module *mod = 0; // did we found a file? if( bFound ) { // Ok, TF should be the file type. switch( tf ) { case t_source: case t_ftd: case t_defaultSource: { FileStat fs; // should we load a .fam instead? URI famUri = origUri; famUri.pathElement().setExtension( "fam" ); if ( ! alwaysRecomp() && ( ignoreSources() || (vfs->readStats( famUri, fs ) && *fs.m_mtime >= *foundStats.m_mtime) ) ) { try { mod = loadModule( famUri.get() ); } catch( Error *e ) { // well, our try wasn't cool. try with the source. e->decref(); mod = loadSource( origUri.get() ); } } else { mod = loadSource( origUri.get() ); } } break; case t_vmmod: mod = loadModule( origUri.get() ); break; case t_binmod: mod = loadBinaryModule( URI::URLDecode( origUri.get() ) ); break; default: // we have not been able to find it // -- report the actual file that caused the problem. raiseError( e_unrec_file_type, origUri.get() ); } } else { String expl = URI::URLDecode(uri.get()); if( scan ) expl += String(" in path ") + getSearchPath(); raiseError( e_nofile, expl ); } // in case of errors, we already raised String modName; getModuleName( origUri.get(), modName ); mod->name( modName ); mod->path( origUri.get() ); // try to load the language table. if ( m_language != "" && mod->language() != m_language ) { // the function may fail, but it won't raise. loadLanguageTable( mod, m_language ); } // in case of errors, we already raised return mod; } Module *ModuleLoader::loadFile( const String &module_path, t_filetype type, bool scan ) { // Preliminary filtering -- get the URI and the filesystem. URI origUri; origUri.parse( module_path, true, false ); if ( ! origUri.isValid() ) { throw new CodeError( ErrorParam( e_malformed_uri ) .extra( module_path ) .origin( e_orig_loader ) ); } return loadFile( origUri, type, scan ); } bool ModuleLoader::loadLanguageTable( Module *module, const String &language ) { String langFileName; // try to find the .ftr file uint32 posDot = module->path().rfind( "." ); uint32 posSlash = module->path().rfind( "/" ); if ( posDot == String::npos || ( posSlash != String::npos && posDot < posSlash ) ) { langFileName = module->path() + ".ftr"; } else { langFileName = module->path().subString(0, posDot ); langFileName += ".ftr"; } if( applyLangTable( module, langFileName ) ) { module->language( language ); return true; } return false; } inline int32 xendianity( bool sameEndianity, int32 val ) { return sameEndianity ? val : (val >> 24) | ((val & 0xFF0000) >> 8) | ((val & 0xFF00 ) << 8) | (val << 24); } //TODO: add some diags. bool ModuleLoader::applyLangTable( Module *mod, const String &file_path ) { URI fsuri( file_path ); fassert( fsuri.isValid() ); VFSProvider* vfs = Engine::getVFS( fsuri.scheme() ); fassert( vfs != 0 ); // try to open the required file table. Stream *fsin_p = vfs->open( fsuri, VFSProvider::OParams().rdOnly() ); if( fsin_p == 0 ) return false; std::auto_ptr fsin( fsin_p ); // check if this is a regular tab file. char buf[16]; buf[5] = 0; if ( fsin->read( buf, 5 ) != 5 || String( "TLTAB" ) != buf ) { return false; } uint16 endianity; if( fsin->read( &endianity, 2 ) != 2 ) { return false; } bool sameEndianity = endianity == 0xFBFC; // read the language table index. int32 sizeField; if( fsin->read( &sizeField, 4 ) != 4 ) return false; int32 tableSize = xendianity( sameEndianity, sizeField ); int32 tablePos = -1; for( int32 i = 0; i < tableSize; i++ ) { // read language code and position in file if( fsin->read( buf, 5 ) != 5 || fsin->read( &sizeField, 4 ) != 4 ) return false; // is this our language code? if( m_language == buf ) { tablePos = xendianity( sameEndianity, sizeField ); break; } } // entry not found? if( tablePos < 0 ) return false; uint32 headerSise = 5 + 2 + 4 + (tableSize * 9); uint32 filePos = headerSise + tablePos; fsin->seekBegin( filePos ); // read the number of strings to be decoded. if( fsin->read( &sizeField, 4 ) != 4 ) return false; int32 stringCount = xendianity( sameEndianity, sizeField ); // read table and alter module. int32 allocated = 256; char *memBuf = (char *) memAlloc( allocated ); // the most intelligent thing is that to modify the strings as they are in memory. // In this way, we don't have to alter already allocated string structures, and // we don't have to scan the map for the correct string entry. while ( stringCount > 0 ) { // read ID if( fsin->read( &sizeField, 4 ) != 4 ) break; int32 stringID = xendianity( sameEndianity, sizeField ); if ( stringID < 0 || stringID >= mod->stringTable().size() ) break; // read the string size if( fsin->read( &sizeField, 4 ) != 4 ) break; int32 stringSize = xendianity( sameEndianity, sizeField ); if ( stringSize < 0 ) break; if ( stringSize == 0 ) continue; // if the string size exeeds the allocated amount, fix it. if( stringSize >= allocated ) { memFree( memBuf ); allocated = stringSize + 1; memBuf = (char *) memAlloc( allocated ); } // read the string if( fsin->read( memBuf, stringSize ) != stringSize ) break; // zero the end so we have an utf8 string memBuf[ stringSize ] = 0; // finally, place it in the right place if ( ! mod->stringTable().getNonConst( stringID )->fromUTF8( memBuf ) ) break; stringCount --; } memFree( memBuf ); return stringCount == 0; } Module *ModuleLoader::loadModule( const String &path ) { ModuleCache* mc = Engine::getModuleCache(); if( mc != 0 ) { Module* mod = mc->find( path ); if( mod != 0 ) return mod; } // May throw on error. Module *mod = 0; { // loadModule may throw, so we need an autoptr not to leak in case of errors. std::auto_ptr in( openResource( path, t_vmmod )); mod = loadModule( in.get() ); if( mc != 0 ) mod = mc->add( path, mod ); } fassert( mod != 0 ); String modName; getModuleName( path, modName ); mod->name( modName ); mod->path( path ); if ( m_language != "" && mod->language() != m_language ) { // This should not throw. loadLanguageTable( mod, m_language ); } return mod; } Module *ModuleLoader::loadBinaryModule( const String &path ) { ModuleCache* mc = Engine::getModuleCache(); if( mc != 0 ) { Module* mod = mc->find( path ); if( mod != 0 ) return mod; } DllLoader dll; if ( ! dll.open( path ) ) { String error; dll.getErrorDescription( error ); error.prepend( path + ": " ); raiseError( e_binload, error ); return 0; } DllFunc dlfunc = dll.getSymbol( "falcon_module_init" ); ext_mod_init func = (ext_mod_init) dlfunc.data(); if ( func == 0 ) { raiseError( e_binstartup, path ); return 0; } Module *mod = func(); if ( mod == 0 ) { raiseError( e_bininit, path); return 0; } // Now I can pass the DLL instance to the module. mod->dllLoader().assign( dll ); // and give the module its names. String modName; getModuleName( path, modName ); mod->name( modName ); mod->path( path ); if ( mc ) mod = mc->add( path, mod ); // as dll instance has been emptied, the DLL won't be closed till the // module lifetime comes to an end. return mod; } Module *ModuleLoader::loadModule( Stream *in ) { // try to open the file char c1, c2; in->read( &c1, 1 ); in->read( &c2, 1 ); if(c1 =='F' && c2 =='M') { Module *ret = loadModule_select_ver( in ); return ret; } return 0; } Module *ModuleLoader::loadModule_select_ver( Stream *in ) { char c1, c2; in->read( &c1, 1 ); in->read( &c2, 1 ); // C1 and c2 now contain the version. // for now we can load only format PCODE if( c1 == FALCON_PCODE_VERSION && c2 == FALCON_PCODE_MINOR ) { return loadModule_ver_1_0( in ); } raiseError( e_modver, "" ); return 0; } Module *ModuleLoader::loadModule_ver_1_0( Stream *in ) { Module *mod = new Module(); if ( ! mod->load( in, true ) ) { raiseError( e_modformat, "" ); } return mod; } void ModuleLoader::raiseError( int code, const String &expl, int fsError ) { throw new IoError( ErrorParam( code ) .extra( expl ) .origin( e_orig_loader ) .sysError( fsError ) ); } Module *ModuleLoader::loadSource( const String &file ) { ModuleCache* mc = Engine::getModuleCache(); if( mc != 0 ) { Module* mod = mc->find( file ); if( mod != 0 ) return mod; } // we need it later int32 dotpos = file.rfind( "." ); // Ok, if we're here we have to load the source. // should we force loading through Falcon Template Document parsing? bool bOldForceFtd = m_forceTemplate; if ( m_detectTemplate && ! m_forceTemplate ) { if ( file.subString( dotpos ) == ".ftd" ) m_forceTemplate = true; } // use the base load source routine // ask for detection, but default to falcon source // will throw on error std::auto_ptr in( openResource( file, t_source ) ); String modName; getModuleName( file, modName ); Module *mod = 0; try { mod = loadSource( in.get(), file, modName ); m_forceTemplate = bOldForceFtd; in->close(); if( mc != 0 ) { mod = mc->add( file, mod ); } } catch (Error *) { // reset old forcing method m_forceTemplate = bOldForceFtd; throw; } return mod; } Module *ModuleLoader::loadSource( Stream *fin, const String &path, const String &name ) { Module *module; m_compileErrors = 0; // the temporary binary file for the pre-generated module Stream *temp_binary; module = new Module(); module->name( name ); module->path( path ); m_compiler.reset(); m_compiler.searchPath( getSearchPath() ); if ( m_forceTemplate ) m_compiler.parsingFtd( true ); // the compiler can never throw if( ! m_compiler.compile( module, fin ) ) { module->decref(); throw m_compiler.detachErrors(); } // we have compiled it. Now we need a file or a memory stream for // saving data. if ( m_compMemory ) { temp_binary = new StringStream; } else { String tempFileName; Sys::_tempName( tempFileName ); FileStream *tb= new FileStream; tb->create( tempFileName + "_1", Falcon::FileStream::e_aReadOnly | Falcon::FileStream::e_aUserWrite ); if ( ! tb->good() ) { int fserr = (int)tb->lastError(); delete tb; module->decref(); raiseError( e_file_output, tempFileName, fserr ); } temp_binary = new StreamBuffer( tb ); } GenCode codeOut( module ); codeOut.generate( m_compiler.sourceTree() ); // import the binary stream in the module; delete temp_binary; module->name( name ); module->path( path ); // if the base load source worked, save the result (if configured to do so). if ( m_saveModule ) { URI tguri( path ); fassert( tguri.isValid() ); VFSProvider* vfs = Engine::getVFS( tguri.scheme() ); fassert( vfs != 0 ); // don't save on remote systems if saveRemote is false if ( vfs->protocol() == "file" || m_saveRemote ) { tguri.pathElement().setExtension( "fam" ); // Standard creations params are ok. Stream *temp_binary = vfs->create( tguri, VFSProvider::CParams() ); if ( temp_binary == 0 || ! module->save( temp_binary ) ) { if ( m_saveMandatory ) { int fserr = (int) temp_binary->lastError(); delete temp_binary; module->decref(); raiseError( e_file_output, tguri.get(), fserr ); } } delete temp_binary; } } return module; } } /* end of flc_modloader.cpp */ engine/module.cpp000066400000000000000000000336631176363201700143350ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: module.cpp Falcon module manager ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2004-08-01 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Falcon { const uint32 Module::c_noEntry = 0xFFffFFff; Module::Module(): m_refcount(1), m_language( "C" ), m_modVersion( 0 ), m_engineVersion( 0 ), m_lineInfo( 0 ), m_loader(0), m_serviceMap( &traits::t_string(), &traits::t_voidp() ), m_attributes(0) { m_strTab = new StringTable; m_bOwnStringTable = true; } Module::~Module() { // The depend table points to the string table // so there is no need to check it. // the services in the service table are usually static, // ... in case they need to be dynamic, the subclass of // ... the module will take care of them. delete m_lineInfo; delete m_attributes; if ( m_bOwnStringTable ) delete m_strTab; } DllLoader &Module::dllLoader() { if ( m_loader == 0 ) m_loader = new DllLoader; return *m_loader; } void Module::addDepend( const String &name, const String &module, bool bPrivate, bool bFile ) { m_depend.addDependency( name, module, bPrivate, bFile ); } void Module::addDepend( const String &name, bool bPrivate, bool bFile ) { m_depend.addDependency( name, name, bPrivate, bFile ); } Symbol *Module::addGlobal( const String &name, bool exp ) { if ( m_symtab.findByName( name ) != 0 ) return 0; Symbol *sym = new Symbol( this, m_symbols.size(), name, exp ); sym->setGlobal( ); m_symbols.push( sym ); sym->itemId( m_symtab.size() ); m_symtab.add( sym ); return sym; } void Module::addSymbol( Symbol *sym ) { sym->id( m_symbols.size() ); sym->module( this ); m_symbols.push( sym ); } Symbol *Module::addSymbol() { Symbol *sym = new Symbol( this ); sym->id( m_symbols.size() ); m_symbols.push( sym ); return sym; } Symbol *Module::addSymbol( const String &name ) { Symbol *sym = new Symbol( this, m_symbols.size(), name, false ); m_symbols.push( sym ); return sym; } Symbol *Module::addGlobalSymbol( Symbol *sym ) { if ( sym->id() >= m_symbols.size() || m_symbols.symbolAt( sym->id() ) != sym ) { sym->id( m_symbols.size() ); m_symbols.push( sym ); } sym->itemId( m_symtab.size() ); sym->module( this ); m_symtab.add( sym ); return sym; } void Module::adoptStringTable( StringTable *st, bool bOwn ) { if ( m_bOwnStringTable ) delete m_strTab; m_strTab = st; m_bOwnStringTable = bOwn; } Symbol *Module::addConstant( const String &name, int64 value, bool exp ) { Symbol *sym = new Symbol( this, name ); sym->exported( exp ); sym->setConst( new VarDef( value ) ); return addGlobalSymbol( sym ); } Symbol *Module::addConstant( const String &name, numeric value, bool exp ) { Symbol *sym = new Symbol( this, name ); sym->exported( exp ); sym->setConst( new VarDef( value ) ); return addGlobalSymbol( sym ); } Symbol *Module::addConstant( const String &name, const String &value, bool exp ) { Symbol *sym = new Symbol( this, name ); sym->exported( exp ); sym->setConst( new VarDef( addString( value ) ) ); return addGlobalSymbol( sym ); } Symbol *Module::addExtFunc( const String &name, ext_func_t func, void *extra, bool exp ) { Symbol *sym = findGlobalSymbol( name ); if ( sym == 0 ) { sym = addSymbol(name); addGlobalSymbol( sym ); } sym->setExtFunc( new ExtFuncDef( func, extra ) ); sym->exported( exp ); return sym; } Symbol *Module::addFunction( const String &name, byte *code, uint32 size, bool exp ) { Symbol *sym = findGlobalSymbol( name ); if ( sym == 0 ) { sym = addSymbol(name); addGlobalSymbol( sym ); } sym->setFunction( new FuncDef( code, size ) ); sym->exported( exp ); return sym; } Symbol *Module::addClass( const String &name, Symbol *ctor_sym, bool exp ) { if ( m_symtab.findByName( name ) != 0 ) return 0; Symbol *sym = new Symbol( this, m_symbols.size(), name, exp ); sym->setClass( new ClassDef( ctor_sym ) ); m_symbols.push( sym ); sym->itemId( m_symtab.size() ); m_symtab.add( sym ); return sym; } Symbol *Module::addSingleton( const String &name, Symbol *ctor_sym, bool exp ) { String clName = "%" + name; // symbol or class symbol already present? if ( m_symtab.findByName( name ) != 0 || m_symtab.findByName( clName ) != 0 ) return 0; // create the class symbol (never exported) Symbol *clSym = addClass( clName, ctor_sym, false ); // create a singleton instance of the class. Symbol *objSym = new Symbol( this, name ); objSym->setInstance( clSym ); objSym->exported( exp ); addGlobalSymbol( objSym ); return objSym; } Symbol *Module::addSingleton( const String &name, ext_func_t ctor, bool exp ) { if( ctor != 0 ) { String ctor_name = name + "._init"; Symbol *sym = addExtFunc( ctor_name, ctor, false ); if ( sym == 0 ) return 0; return addSingleton( name, sym, exp ); } else { return addSingleton( name, (Symbol*)0, exp ); } } Symbol *Module::addClass( const String &name, ext_func_t ctor, bool exp ) { String ctor_name = name + "._init"; Symbol *sym = addExtFunc( ctor_name, ctor, false ); if ( sym == 0 ) return 0; return addClass( name, sym, exp ); } VarDef& Module::addClassProperty( Symbol *cls, const String &prop ) { ClassDef *cd = cls->getClassDef(); VarDef *vd = new VarDef(); cd->addProperty( addString( prop ), vd ); return *vd; } VarDef& Module::addClassMethod( Symbol *cls, const String &prop, Symbol *method ) { ClassDef *cd = cls->getClassDef(); VarDef *vd = new VarDef( method ); cd->addProperty( addString( prop ), vd ); return *vd; } VarDef& Module::addClassMethod( Symbol *cls, const String &prop, ext_func_t method_func ) { String name = cls->name() + "." + prop; Symbol *method = addExtFunc( name, method_func, false ); return addClassMethod( cls, prop, method ); } String *Module::addCString( const char *pos, uint32 size ) { String *ret = stringTable().find( pos ); if ( ret == 0 ) { char *mem = (char *)memAlloc(size+1); memcpy( mem, pos, size ); mem[size] = 0; ret = new String(); ret->adopt( mem, size, size + 1 ); stringTable().add( ret ); } return ret; } String *Module::addString( const String &st ) { String *ret = stringTable().find( st ); if ( ret == 0 ) { ret = new String( st ); ret->bufferize(); ret->exported( st.exported() ); stringTable().add( ret ); } return ret; } String *Module::addString( const String &st, bool exported ) { String *ret = stringTable().find( st ); if ( ret == 0 ) { ret = new String( st ); ret->exported( exported ); ret->bufferize(); stringTable().add( ret ); } else { ret->exported( exported ); } return ret; } uint32 Module::addStringID( const String &st, bool exported ) { uint32 ret = stringTable().size(); String* s = new String( st ); s->exported( exported ); s->bufferize(); stringTable().add( s ); return ret; } bool Module::save( Stream *out, bool skipCode ) const { // save header informations: const char *sign = "FM"; out->write( sign, 2 ); char ver = pcodeVersion(); out->write( &ver, 1 ); ver = pcodeSubVersion(); out->write( &ver, 1 ); // serializing module and engine versions uint32 iver = endianInt32( m_modVersion ); out->write( &iver, sizeof( iver ) ); iver = endianInt32( m_engineVersion ); out->write( &iver, sizeof( iver ) ); // serialize the module tables. //NOTE: all the module tables are saved 32 bit alinged. if ( ! stringTable().save( out ) ) return false; if ( ! m_symbols.save( out ) ) return false; if ( ! m_symtab.save( out ) ) return false; if ( ! m_depend.save( out ) ) return false; if( m_attributes != 0 ) { iver = endianInt32(1); out->write( &iver, sizeof( iver ) ); if ( ! m_attributes->save( this, out ) ) return false; } else { iver = endianInt32(0); out->write( &iver, sizeof( iver ) ); } if ( m_lineInfo != 0 ) { int32 infoInd = endianInt32( 1 ); out->write( &infoInd, sizeof( infoInd ) ); if ( ! m_lineInfo->save( out ) ) return false; } else { int32 infoInd = endianInt32( 0 ); out->write( &infoInd, sizeof( infoInd ) ); } return out->good(); } bool Module::load( Stream *is, bool skipHeader ) { // verify module version informations. if ( ! skipHeader ) { char c; is->read( &c, 1 ); if( c != 'F' ) return false; is->read( &c, 1 ); if( c != 'M' ) return false; is->read( &c, 1 ); if ( c != pcodeVersion() ) return false; is->read( &c, 1 ); if ( c != pcodeSubVersion() ) return false; } // Load the module and engine version. uint32 ver; is->read( &ver, sizeof( ver ) ); m_modVersion = endianInt32( ver ); is->read( &ver, sizeof( ver ) ); m_engineVersion = endianInt32( ver ); /*TODO: see if there is a localized table around and use that instead. If so, skip our internal strtable. */ if ( ! stringTable().load( is ) ) return false; if ( ! m_symbols.load( this, is ) ) return false; if ( ! m_symtab.load( this, is ) ) return false; if ( ! m_depend.load( this, is ) ) return false; is->read( &ver, sizeof( ver ) ); if( ver != 0 ) { m_attributes = new AttribMap; if ( ! m_attributes->load( this, is ) ) return false; } // load lineinfo indicator. int32 infoInd; is->read( &infoInd, sizeof( infoInd ) ); infoInd = endianInt32( infoInd ); if( infoInd != 0 ) { m_lineInfo = new LineMap; if ( ! m_lineInfo->load( is ) ) return false; } else m_lineInfo = 0; return is->good(); } uint32 Module::getLineAt( uint32 pc ) const { if ( m_lineInfo == 0 || m_lineInfo->empty() ) return 0; MapIterator iter; if( m_lineInfo->find( &pc, iter ) ) { return *(uint32 *) iter.currentValue(); } else { iter.prev(); if( iter.hasCurrent() ) return *(uint32 *) iter.currentValue(); return 0; } } void Module::addLineInfo( uint32 pc, uint32 line ) { if ( m_lineInfo == 0 ) m_lineInfo = new LineMap; m_lineInfo->addEntry( pc, line ); } void Module::setLineInfo( LineMap *infos ) { delete m_lineInfo; m_lineInfo = infos; } Service *Module::getService( const String &name ) const { Service **srv = (Service **) m_serviceMap.find( &name ); if ( srv != 0 ) { return *srv; } return 0; } bool Module::publishService( Service *sp ) { Service **srv = (Service **) m_serviceMap.find( &sp->getServiceName() ); if ( srv != 0 ) { return false; } else { m_serviceMap.insert( &sp->getServiceName(), sp ); } return true; } char Module::pcodeVersion() const { return FALCON_PCODE_VERSION; } char Module::pcodeSubVersion() const { return FALCON_PCODE_MINOR; } void Module::getModuleVersion( int &major, int &minor, int &revision ) const { major = (m_modVersion ) >> 16; minor = (m_modVersion & 0xFF00 ) >> 8; revision = m_modVersion & 0xFF; } void Module::getEngineVersion( int &major, int &minor, int &revision ) const { major = (m_engineVersion ) >> 16; minor = (m_engineVersion & 0xFF00 ) >> 8; revision = m_engineVersion & 0xFF; } DllLoader *Module::detachLoader() { DllLoader *ret = m_loader; m_loader = 0; return ret; } void Module::incref() const { atomicInc( m_refcount ); } void Module::decref() const { if( atomicDec( m_refcount ) <= 0 ) { Module *deconst = const_cast(this); DllLoader *loader = deconst->detachLoader(); delete deconst; delete loader; } } bool Module::saveTableTemplate( Stream *stream, const String &encoding ) const { stream->writeString( "writeString( encoding ); stream->writeString( "\"?>\n" ); return stringTable().saveTemplate( stream, name(), language() ); } void Module::absoluteName( const String &module_name, const String &parent_name, String& result ) { if ( module_name.getCharAt(0) == '.' ) { // notation .name if ( parent_name.size() == 0 ) result = module_name.subString( 1 ); else { // remove last part of parent name uint32 posDot = parent_name.rfind( "." ); // are there no dot? -- we're at root elements if ( posDot == String::npos ) result = module_name.subString( 1 ); else result = parent_name.subString( 0, posDot ) + module_name; // "." is included. } } else if ( module_name.find( "self." ) == 0 ) { if ( parent_name.size() == 0 ) result = module_name.subString( 5 ); else result = parent_name + "." + module_name.subString( 5 ); } else result = module_name; } void Module::addAttribute( const String &name, VarDef* vd ) { if( m_attributes == 0 ) m_attributes = new AttribMap; m_attributes->insertAttrib( name, vd ); } void Module::rollBackSymbols( uint32 nsize ) { uint32 size = symbols().size(); for (uint32 pos = nsize; pos < size; pos ++ ) { Symbol *sym = symbols().symbolAt( pos ); symbolTable().remove( sym->name() ); delete sym; } symbols().resize(nsize); } } /* end module.cpp */ engine/modulecache.cpp000066400000000000000000000067671176363201700153260ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: modulecache.cpp Cache-sensible module loader. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 30 May 2010 15:12:30 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include namespace Falcon { class CacheEntry: public BaseAlloc { public: CacheEntry( Module* mod, const TimeStamp& tsDate ): m_module( mod ), m_ts( tsDate ) {} ~CacheEntry() { m_module->decref(); } void change( Module* mod, const TimeStamp& tsDate ) { m_module->decref(); mod->incref(); m_module = mod; m_ts = tsDate; } Module* m_module; TimeStamp m_ts; }; ModuleCache::ModuleCache(): m_modMap( &traits::t_string(), &traits::t_voidp() ) {} ModuleCache::~ModuleCache() { MapIterator iter = m_modMap.begin(); while( iter.hasCurrent() ) { CacheEntry* mod = *(CacheEntry**) iter.currentValue(); delete mod; iter.next(); } } Module* ModuleCache::add( const String& muri, Module* module ) { FileStat fm; bool gotStats = Sys::fal_stats( muri, fm ); m_mtx.lock(); void* data = m_modMap.find( &muri ); if( data != 0 ) { CacheEntry* mod_cache = *(CacheEntry**) data; // New module? // -- if I can't get the stats, in doubt, change it if( ! gotStats ) { // inserts a null timestamp so any future entry will change it. mod_cache->change( module, TimeStamp() ); m_mtx.unlock(); return module; } else if( fm.m_mtime->compare( mod_cache->m_ts ) > 0 ) { mod_cache->change( module, *fm.m_mtime ); m_mtx.unlock(); return module; } else { Module* mod1 = mod_cache->m_module; mod1->incref(); m_mtx.unlock(); module->decref(); return mod1; } } else { // had we been able to get the stats? if( gotStats ) { m_modMap.insert( &muri, new CacheEntry( module, *fm.m_mtime ) ); } else { // insert the module with a null timestamp; any other timestamp // read later from the system m_modMap.insert( &muri, new CacheEntry( module, TimeStamp() ) ); } module->incref(); m_mtx.unlock(); return module; } } bool ModuleCache::remove( const String& muri ) { MapIterator iter; m_mtx.lock(); if( m_modMap.find( &muri, iter ) ) { CacheEntry* mod = *(CacheEntry**) iter.currentValue(); m_modMap.erase( iter ); m_mtx.unlock(); delete mod; return true; } m_mtx.unlock(); return false; } Module* ModuleCache::find( const String& muri ) { FileStat fm; bool gotStats = Sys::fal_stats( muri, fm ); m_mtx.lock(); void* data = m_modMap.find( &muri ); if( data != 0 ) { CacheEntry* emod = *(CacheEntry**) data; if ( !gotStats || fm.m_mtime->compare( emod->m_ts ) > 0 ) { // ignore the find m_mtx.unlock(); return 0; } Module* mod = emod->m_module; mod->incref(); m_mtx.unlock(); return mod; } m_mtx.unlock(); return 0; } } /* end of modulecache.cpp */ engine/mt_posix.cpp000066400000000000000000000146101176363201700147010ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: mt_posix.cpp Multithreaded extensions - POSIX specific. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 17 Jan 2009 17:06:47 +0100 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #define _XOPEN_SOURCE_EXTENDED #include // this for gettimeofday on macosx #include #include #include namespace Falcon { static Mutex s_cs; ThreadSpecific::ThreadSpecific( void (*destructor)(void*) ) { #ifndef NDEBUG int value = pthread_key_create( &m_key, destructor ); fassert( value == 0 ); #else pthread_key_create( &m_key, destructor ); #endif } /** Performs an atomic thread safe increment. */ int32 atomicInc( volatile int32 &data ) { s_cs.lock(); register int32 res = ++data; s_cs.unlock(); return res; } /** Performs an atomic thread safe decrement. */ int32 atomicDec( volatile int32 &data ) { s_cs.lock(); register int32 res = --data; s_cs.unlock(); return res; } void Event::set() { #ifdef NDEBUG pthread_mutex_lock( &m_mtx ); m_bIsSet = true; if ( m_bAutoReset ) pthread_cond_signal( &m_cv ); else pthread_cond_broadcast( &m_cv ); pthread_mutex_unlock( &m_mtx ); #else int result = pthread_mutex_lock( &m_mtx ); fassert( result == 0 ); m_bIsSet = true; if ( m_bAutoReset ) result = pthread_cond_signal( &m_cv ); else result = pthread_cond_broadcast( &m_cv ); fassert( result == 0 ); result = pthread_mutex_unlock( &m_mtx ); fassert( result == 0 ); #endif } bool Event::wait( int32 to ) { pthread_mutex_lock( &m_mtx ); // are we lucky? if( m_bIsSet ) { if ( m_bAutoReset ) m_bIsSet = false; pthread_mutex_unlock( &m_mtx ); return true; } // No? -- then are we unlucky? if ( to == 0 ) { pthread_mutex_unlock( &m_mtx ); return false; } // neither? -- then let's wait. How much? if ( to < 0 ) { do { pthread_cond_wait( &m_cv, &m_mtx ); } while( ! m_bIsSet ); } else { // release the mutex for a while pthread_mutex_unlock( &m_mtx ); struct timespec ts; #if _POSIX_TIMERS > 0 clock_gettime(CLOCK_REALTIME, &ts); #else struct timeval tv; gettimeofday(&tv, NULL); ts.tv_sec = tv.tv_sec; ts.tv_nsec = tv.tv_usec*1000; #endif ts.tv_sec += to/1000; ts.tv_nsec += (to%1000) * 1000000; if( ts.tv_nsec >= 1000000000 ) { ++ts.tv_sec; ts.tv_nsec -= 1000000000; } pthread_mutex_lock( &m_mtx ); while( ! m_bIsSet ) { int res; if( (res = pthread_cond_timedwait( &m_cv, &m_mtx, &ts )) == ETIMEDOUT ) { // wait failed pthread_mutex_unlock( &m_mtx ); return false; } // be sure that we haven't got other reasons to fail. fassert( res == 0 ); } } // here, m_bIsSet is set... if ( m_bAutoReset ) m_bIsSet = false; pthread_mutex_unlock( &m_mtx ); return true; } //================================================================================== // System threads. // SysThread::~SysThread() { pthread_mutex_destroy( &m_sysdata->m_mtxT ); memFree( m_sysdata ); } SysThread::SysThread( Runnable* r ): m_runnable( r ) { m_sysdata = ( struct SYSTH_DATA* ) memAlloc( sizeof( struct SYSTH_DATA ) ); m_sysdata->m_bDetached = false; m_sysdata->m_bDone = false; m_sysdata->m_lastError = 0; pthread_mutex_init( &m_sysdata->m_mtxT, NULL ); } void SysThread::attachToCurrent() { m_sysdata->pth = pthread_self(); } void* SysThread::RunAThread( void *data ) { SysThread* sth = (SysThread*) data; return sth->run(); } bool SysThread::start( const ThreadParams ¶ms ) { pthread_attr_t attr; pthread_attr_init( &attr ); if( params.stackSize() != 0 ) { if( (m_sysdata->m_lastError = pthread_attr_setstacksize( &attr, params.stackSize() ) ) != 0 ) { pthread_attr_destroy( &attr ); return false; } } if ( params.detached() ) { if( (m_sysdata->m_lastError = pthread_attr_setdetachstate( &attr, params.detached() ? 1:0 ) ) != 0 ) { pthread_attr_destroy( &attr ); return false; } } // time to increment the reference count of our thread that is going to run if ( (m_sysdata->m_lastError = pthread_create( &m_sysdata->pth, &attr, &SysThread::RunAThread, this ) ) != 0 ) { pthread_attr_destroy( &attr ); return false; } if ( params.detached() ) { detach(); } pthread_attr_destroy( &attr ); return true; } void SysThread::disengage() { delete this; } void SysThread::detach() { // are we already done? pthread_mutex_lock( &m_sysdata->m_mtxT ); if ( m_sysdata->m_bDone ) { pthread_mutex_unlock( &m_sysdata->m_mtxT ); // disengage system joins and free system data. pthread_detach( m_sysdata->pth ); // free app data. delete this; } else { // tell the run function to dispose us when done. m_sysdata->m_bDetached = true; pthread_mutex_unlock( &m_sysdata->m_mtxT ); } } bool SysThread::join( void* &result ) { if ( pthread_join( m_sysdata->pth, &result ) == 0 ) { delete this; return true; } return false; } uint64 SysThread::getID() { return (uint64) m_sysdata->pth; } uint64 SysThread::getCurrentID() { return (uint64) pthread_self(); } bool SysThread::isCurrentThread() { return pthread_equal( m_sysdata->pth, pthread_self() ) != 0; } bool SysThread::equal( const SysThread *th1 ) const { return pthread_equal( m_sysdata->pth, th1->m_sysdata->pth ) != 0; } void *SysThread::run() { fassert( m_runnable != 0 ); void* data = m_runnable->run(); // have we been detached in the meanwhile? -- we must dispose our data now. pthread_mutex_lock( &m_sysdata->m_mtxT ); if( m_sysdata->m_bDetached ) { pthread_mutex_unlock( &m_sysdata->m_mtxT ); delete this; } else { m_sysdata->m_bDone = true; pthread_mutex_unlock( &m_sysdata->m_mtxT ); } return data; } } /* end of mt_posix.cpp */ engine/mt_win.cpp000066400000000000000000000134361176363201700143410ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: mt_win.cpp Multithreaded extensions - MS-Windows specific. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 17 Jan 2009 17:06:47 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include namespace Falcon { static DWORD s_nxd = 0; //================================================================================= // Thread Specific destruction sequence. // ThreadSpecific::ThreadSpecific( void (*destructor)(void*) ) { if ( s_nxd == 0 ) s_nxd = TlsAlloc(); m_key = TlsAlloc(); m_destructor = destructor; } ThreadSpecific* ThreadSpecific::clearAndNext() { ThreadSpecific* nextd = m_nextDestructor; void* value = TlsGetValue( m_key ); if( value != 0 ) m_destructor( value ); return nextd; } void ThreadSpecific::set( void *value ) { void* data = TlsGetValue( m_key ); #ifndef NDEBUG BOOL res = TlsSetValue( m_key, value ); fassert( res ); #else TlsSetValue( m_key, value ); #endif if( data == 0 && value != 0 && m_destructor != 0 ) { m_nextDestructor = (ThreadSpecific*) TlsGetValue( s_nxd ); TlsSetValue( s_nxd, this ); } } //================================================================================== // System threads. // SysThread::~SysThread() { CloseHandle( m_sysdata->hEvtDetach ); DeleteCriticalSection( &m_sysdata->m_csT ); memFree( m_sysdata ); } SysThread::SysThread( Runnable* r ): m_runnable( r ) { m_sysdata = ( struct SYSTH_DATA* ) memAlloc( sizeof( struct SYSTH_DATA ) ); // The event isactually a barrier. m_sysdata->hEvtDetach = CreateEvent( 0, TRUE, FALSE, 0 ); m_sysdata->retval = 0; m_sysdata->m_bDone = false; m_sysdata->m_bDetached = false; m_sysdata->m_bJoining = false; InitializeCriticalSection( &m_sysdata->m_csT ); } void SysThread::disengage() { delete this; } void SysThread::attachToCurrent() { m_sysdata->hThread = GetCurrentThread(); m_sysdata->nThreadID = GetCurrentThreadId(); } extern "C" { static unsigned int __stdcall run_a_thread( void *data ) { return (unsigned int) SysThread::RunAThread( data ); } } void* SysThread::RunAThread( void *data ) { SysThread* sth = (SysThread*) data; void* ret = sth->run(); // fire the destruction sequence ThreadSpecific* tdnext = (ThreadSpecific *) TlsGetValue( s_nxd ); while( tdnext != 0 ) { tdnext = tdnext->clearAndNext(); } return ret; } bool SysThread::start( const ThreadParams ¶ms ) { m_sysdata->hThread = (HANDLE) _beginthreadex( 0, params.stackSize(), &run_a_thread, this, 0, &m_sysdata->nThreadID ); if ( m_sysdata->hThread == INVALID_HANDLE_VALUE ) return false; if ( params.detached() ) detach(); return true; } void SysThread::detach() { EnterCriticalSection( &m_sysdata->m_csT ); if( m_sysdata->m_bDone ) { // if we're done, either we or the joiner must destroy us. if ( m_sysdata->m_bJoining ) { m_sysdata->m_bDetached = true; SetEvent( m_sysdata->hEvtDetach ); LeaveCriticalSection( &m_sysdata->m_csT ); } else { // this prevents joiners to succed while we destroy ourself m_sysdata->m_bJoining = true; LeaveCriticalSection( &m_sysdata->m_csT ); delete this; } } else { m_sysdata->m_bDetached = true; SetEvent( m_sysdata->hEvtDetach ); LeaveCriticalSection( &m_sysdata->m_csT ); } } bool SysThread::join( void* &result ) { // ensure just one thread can join. EnterCriticalSection( &m_sysdata->m_csT ); if ( m_sysdata->m_bJoining || m_sysdata->m_bDetached ) { LeaveCriticalSection( &m_sysdata->m_csT ); return false; } else { m_sysdata->m_bJoining = true; LeaveCriticalSection( &m_sysdata->m_csT ); } HANDLE hs[] = { m_sysdata->hEvtDetach, m_sysdata->hThread }; DWORD wres = WaitForMultipleObjects( 2, hs, FALSE, INFINITE ); if ( wres == WAIT_OBJECT_0 ) { // The thread was detached -- if it's also done, we must destroy it. EnterCriticalSection( &m_sysdata->m_csT ); if( m_sysdata->m_bDone ) { LeaveCriticalSection( &m_sysdata->m_csT ); delete this; return false; } m_sysdata->m_bJoining = false; LeaveCriticalSection( &m_sysdata->m_csT ); return false; // can't join anymore. } else if ( wres == WAIT_OBJECT_0 + 1 ) { // Ok, we joined the thread -- and it terminated. result = m_sysdata->retval; delete this; return true; } // wait failed. return false; } uint64 SysThread::getID() { return (uint64) m_sysdata->nThreadID; } uint64 SysThread::getCurrentID() { return (uint64) GetCurrentThreadId(); } bool SysThread::isCurrentThread() { return GetCurrentThreadId() == m_sysdata->nThreadID; } bool SysThread::equal( const SysThread *th1 ) const { return m_sysdata->nThreadID == th1->m_sysdata->nThreadID; } void *SysThread::run() { fassert( m_runnable != 0 ); void* data = m_runnable->run(); m_sysdata->retval = data; // if we're detached and not joined, we must destroy ourself. EnterCriticalSection( &m_sysdata->m_csT ); if( m_sysdata->m_bDetached && (! m_sysdata->m_bJoining) ) { LeaveCriticalSection( &m_sysdata->m_csT ); delete this; } else { // otherwise, just let joiners or detachers to the dirty job. m_sysdata->m_bDone = true; LeaveCriticalSection( &m_sysdata->m_csT ); } return data; } } /* end of mt_win.cpp */ engine/pagedict.cpp000066400000000000000000000152041176363201700146170ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dict.cpp Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun ago 23 21:55:38 CEST 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #if defined(__BORLANDC__) #include #else #include #endif namespace Falcon { void PageDict::PageDictIterDeletor( Iterator* iter ) { MapIterator* mi = (MapIterator*) iter->data(); delete mi; } //======================================================= // Iterator // PageDict::PageDict(): m_map( &m_itemTraits, &m_itemTraits ), m_mark( 0xFFFFFFFF ) {} PageDict::PageDict( uint32 pageSize ): m_map( &m_itemTraits, &m_itemTraits, (uint16) pageSize ), m_mark( 0xFFFFFFFF ) { } PageDict::~PageDict() { } uint32 PageDict::length() const { return m_map.size(); } Item *PageDict::find( const Item &key ) const { return (Item *) m_map.find( &key ); } bool PageDict::findIterator( const Item &key, Iterator &di ) { MapIterator *mi = (MapIterator *) di.data(); bool ret = m_map.find( &key, *mi ); di.data( mi ); return ret; } bool PageDict::remove( const Item &key ) { if( m_map.erase( &key ) ) { invalidateAllIters(); return true; } return false; } void PageDict::put( const Item &key, const Item &value ) { if( m_map.insert( &key, &value ) ) invalidateAllIters(); } void PageDict::smartInsert( const Iterator &iter, const Item &key, const Item &value ) { // todo put( key, value ); } void PageDict::merge( const ItemDict &dict ) { Iterator iter( const_cast(&dict) ); while( iter.hasCurrent() ) { Item& current = iter.getCurrent(); put( iter.getCurrentKey(), current.isString() && current.asString()->isCore() ? new CoreString( *current.asString() ) : current ); iter.next(); } invalidateAllIters(); } PageDict *PageDict::clone() const { PageDict *ret; if ( m_map.size() == 0 ) { ret = new PageDict; } else { ret = new PageDict( m_map.order() ); ret->merge( *this ); } return ret; } const Item &PageDict::front() const { if( m_map.empty() ) throw new AccessError( ErrorParam( e_iter_outrange, __LINE__ ) .origin( e_orig_runtime ).extra( "PageDict::front" ) ); return *(Item*) m_map.begin().currentValue(); } const Item &PageDict::back() const { if( m_map.empty() ) throw new AccessError( ErrorParam( e_iter_outrange, __LINE__ ) .origin( e_orig_runtime ).extra( "PageDict::back" ) ); return *(Item*) m_map.end().currentValue(); } void PageDict::append( const Item& item ) { if( item.isArray() ) { ItemArray& pair = item.asArray()->items(); if ( pair.length() == 2 ) { m_map.insert( &pair[0], &pair[1] ); return; } } throw new AccessError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "PageDict::append" ) ); invalidateAllIters(); } void PageDict::prepend( const Item& item ) { append( item ); invalidateAllIters(); } void PageDict::clear() { m_map.clear(); invalidateAllIters(); } bool PageDict::empty() const { return m_map.empty(); } void PageDict::gcMark( uint32 gen ) { if( m_mark != gen ) { m_mark = gen; Sequence::gcMark( gen ); MapIterator iter = m_map.begin(); while( iter.hasCurrent() ) { memPool->markItem( *(Item*)iter.currentKey() ); memPool->markItem( *(Item*)iter.currentValue() ); iter.next(); } } } //============================================================ // Iterator management. //============================================================ void PageDict::getIterator( Iterator& tgt, bool tail ) const { Sequence::getIterator( tgt, tail ); MapIterator* mi = (MapIterator *) tgt.data(); if ( mi == 0 ) { mi = new MapIterator; tgt.data( mi ); tgt.deletor( &PageDictIterDeletor ); } *mi = tail ? m_map.end() : m_map.begin(); } void PageDict::copyIterator( Iterator& tgt, const Iterator& source ) const { Sequence::copyIterator( tgt, source ); MapIterator* mi = (MapIterator *) tgt.data(); if ( mi == 0 ) { mi = new MapIterator; tgt.data( mi ); tgt.deletor( &PageDictIterDeletor ); } *mi = *(MapIterator *)source.data(); } void PageDict::insert( Iterator &iter, const Item &data ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "PageDict::insert" ) ); } void PageDict::erase( Iterator &iter ) { MapIterator* mi = (MapIterator*) iter.data(); if ( ! mi->hasCurrent() ) throw new AccessError( ErrorParam( e_iter_outrange, __LINE__ ) .origin( e_orig_runtime ).extra( "PageDict::erase" ) ); // map erase grants the validity of mi m_map.erase( *mi ); invalidateAnyOtherIter( &iter ); } bool PageDict::hasNext( const Iterator &iter ) const { MapIterator* mi = (MapIterator*) iter.data(); return mi->hasNext(); } bool PageDict::hasPrev( const Iterator &iter ) const { MapIterator* mi = (MapIterator*) iter.data(); return mi->hasPrev(); } bool PageDict::hasCurrent( const Iterator &iter ) const { MapIterator* mi = (MapIterator*) iter.data(); return mi->hasCurrent(); } bool PageDict::next( Iterator &iter ) const { MapIterator* mi = (MapIterator*) iter.data(); return mi->next(); } bool PageDict::prev( Iterator &iter ) const { MapIterator* mi = (MapIterator*) iter.data(); return mi->prev(); } Item& PageDict::getCurrent( const Iterator &iter ) { MapIterator* mi = (MapIterator*) iter.data(); if ( mi->hasCurrent() ) return *(Item*) mi->currentValue(); throw new AccessError( ErrorParam( e_iter_outrange, __LINE__ ) .origin( e_orig_runtime ).extra( "PageDict::getCurrent" ) ); } Item& PageDict::getCurrentKey( const Iterator &iter ) { MapIterator* mi = (MapIterator*) iter.data(); if ( mi->hasCurrent() ) return *(Item*) mi->currentKey(); throw new AccessError( ErrorParam( e_iter_outrange, __LINE__ ) .origin( e_orig_runtime ).extra( "PageDict::getCurrent" ) ); } bool PageDict::equalIterator( const Iterator &first, const Iterator &second ) const { return first.position() == second.position(); } } /* end of dict.cpp */ engine/path.cpp000066400000000000000000000310451176363201700137740ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: path.cpp RFC 3986 compliant path-parth - implementation ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 27 Feb 2008 22:05:33 +0100 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include namespace Falcon { Path::Path(): m_bValid( true ), m_owner(0) { } Path::Path( URI *owner ): m_bValid( true ), m_owner( owner ) { } void Path::copy( const Path &other ) { if ( m_owner != 0 ) m_owner->m_encoded.size(0); m_path = other.m_path; m_bValid = other.m_bValid; m_device = other.m_device; m_location = other.m_location; m_file = other.m_file; m_extension = other.m_extension; } bool Path::set( const String &path ) { uint32 len = path.length(); m_device.size(0); m_location.size(0); m_file.size(0); m_extension.size(0); if ( len == 0 ) { m_bValid = true; m_path = ""; if ( m_owner ) m_owner->m_encoded.size(0); return true; } // a single element should be considered as the file. bool bColon = false; uint32 p = 0; while( p < len ) { uint32 chr = path.getCharAt( p ); if ( chr == '\\' ) { chr = '/'; } switch( chr ) { case ':': if ( bColon ) { m_bValid = false; if ( m_owner ) m_owner->m_encoded.size(0); return false; } bColon = true; m_location.size(0); // prevent storing the intial "/" m_device = m_file; m_file.size(0); break; case '/': if ( m_file.size() == 0 ) { if( m_location.size() == 0 ) m_location = "/"; // otherwise ignore; treat // as just / } else { if ( m_location.size() != 0 && m_location.getCharAt( m_location.length() - 1 ) != '/' ) m_location.append( '/' ); m_location += m_file; m_file.size(0); } break; default: m_file.append( chr ); } p++; } // detect if we have an extension uint32 pos = m_file.rfind( "." ); if ( m_file.size() > 0 && pos > 0 && pos < m_file.length() - 1 ) { m_extension = m_file.subString( pos + 1 ); m_file.remove( pos, m_file.length() ); } m_bValid = true; compose(); return true; } void Path::compose() { m_path.size(0); if ( m_device.size() > 0 ) m_path = "/" + m_device + ":"; if ( m_location.size() > 0 ) { m_path += m_location; if ( m_location.getCharAt( m_location.length() - 1 ) != '/' && m_file.size() > 0 ) m_path += "/"; } if ( m_file.size() != 0 ) { m_path += m_file; if ( m_extension.size() != 0 ) m_path += "." + m_extension; } if ( m_owner ) m_owner->m_encoded.size(0); } void Path::getWinFormat( String &str ) const { if ( m_path.size() == 0 ) { str.size(0); return; } str.reserve( m_path.size() ); // if we have a resource specifier, we know we have a leading / uint32 startPos = m_path.getCharAt(0) == '/' && m_device.size() != 0 ? 1: 0; uint32 endPos = m_path.length(); while( startPos < endPos ) { uint32 chr = m_path.getCharAt( startPos ); if( chr != '/' ) str.append( chr ); else str.append( '\\' ); ++startPos; } } bool Path::getResource( String &str ) const { str = m_device; return str.size() != 0; } bool Path::getLocation( String &str ) const { str = m_location; return str.size() != 0; } bool Path::getFullLocation( String &str ) const { if( m_device.size() != 0 ) str = "/" + m_device + ":" + m_location; else str = m_location; return str.size() != 0; } bool Path::setFullLocation( const String &str ) { // full location is disk + path + filename; // we don't know if we have the disk, but we // know we should have at least the path (unless it's ""). // It's simpler to add the filename and reparse everything. if( str == "" ) { m_location = ""; m_device = ""; return true; } else { String loc; // only the resource? if( str.getCharAt( str.length() - 1 ) == ':' ) { // then, don't add a slash which would translate in an absolute location. loc = str + getFilename(); } else { // ok, add the slash after the path // (notice that "//" is correctly parsed into "/") loc = str + "/" + getFilename(); } return set( loc ); } } bool Path::getWinLocation( String &str ) const { if ( ! getLocation( str ) ) return false; uint32 len = str.length(); for( uint32 i = 0; i < len; i ++ ) { if ( str.getCharAt( i ) == '/' ) str.setCharAt( i, '\\' ); } return true; } bool Path::getFullWinLocation( String &str ) const { if ( m_device != "" ) { String loc; if ( getWinLocation( loc ) ) { str = m_device + ":" + loc; return true; } } else if ( getWinLocation( str ) ) { return true; } // str has been cleaned by getWinLocation() return false; } bool Path::getFile( String &str ) const { str = m_file; return str.size() != 0; } bool Path::getFilename( String &str ) const { if ( m_file.size() != 0 ) { if ( m_extension.size() != 0 ) str = m_file + "." + m_extension; else str = m_file; return true; } return false; } bool Path::getExtension( String &str ) const { str = m_extension; return str.size() != 0; } void Path::setResource( const String &res ) { if ( res != m_device ) { m_device = res; if ( m_device.find( ":" ) != String::npos || m_device.find( "/" ) != String::npos ) m_bValid = false; compose(); } } void Path::extendLocation( const String &npath ) { if ( m_location.size() == 0 ) m_location = npath; else { if( npath.size() != 0 ) { if( npath.getCharAt(0) != '/' ) m_location += "/"; m_location += npath; } } // remove trailing "/". if ( npath.size() > 0 && m_location.getCharAt( m_location.length() - 1 ) == '/' ) m_location.remove( m_location.length() - 1, 1 ); compose(); } void Path::setLocation( const String &loc ) { uint32 pos = loc.find( ":" ); if ( pos != String::npos ) { if( loc.find( ":", pos + 1 ) != String::npos ) { m_bValid = false; } else { setResource( loc.subString( 0, pos ) ); setLocation( loc.subString( pos + 1 ) ); } } else { if( m_location != loc ) { m_location = loc; uint32 pos1 = m_location.find( "\\" ); while( pos1 != String::npos ) { m_location.setCharAt( pos1, '/' ); pos1 = m_location.find( "\\", pos1 ); } // remove trailing "/" if ( m_location.size() > 1 && m_location.getCharAt( m_location.length() - 1 ) == '/' ) m_location.remove( m_location.length() - 1, 1 ); compose(); } } } void Path::setFile( const String &file ) { if ( file.find( "/" ) != String::npos || file.find( "\\" ) != String::npos || file.find( ":" ) != String::npos ) { m_bValid = false; } else { if ( m_file != file ) { m_file = file; compose(); } } } void Path::setExtension( const String &ext ) { if ( ext.find( "/" ) != String::npos || ext.find( "\\" ) != String::npos || ext.find( ":" ) != String::npos || ext.find( "." ) != String::npos ) { m_bValid = false; } else { if ( m_extension != ext ) { m_extension = ext; if ( m_owner ) m_owner->m_encoded.size(0); compose(); } } } void Path::setFilename( const String &fname ) { if ( fname.find( "/" ) != String::npos || fname.find( "\\" ) != String::npos || fname.find( ":" ) != String::npos ) { m_bValid = false; } else { uint32 posdot = fname.rfind( "." ); if ( posdot != String::npos && posdot != 0 && posdot != fname.length() - 1 ) { m_file = fname.subString( 0, posdot ); m_extension = fname.subString( posdot + 1 ); } else { m_file = fname; m_extension = ""; } if ( m_owner ) m_owner->m_encoded.size(0); compose(); } } bool Path::isAbsolute() const { return m_location.size() > 0 && m_location.getCharAt(0) == '/'; } bool Path::isLocation() const { return (m_file.size() == m_extension.size()) == 0; } void Path::split( String &loc, String &name, String &ext ) { if ( m_device.size() > 0 ) { loc = m_device + ":" + m_location; } else loc = m_location; name = m_file; ext = m_extension; } void Path::split( String &res, String &loc, String &name, String &ext ) { res = m_device; loc = m_location; name = m_file; ext = m_extension; } void Path::splitWinFormat( String &res, String &loc, String &name, String &ext ) { split( res, loc, name, ext ); uint32 len = loc.length(); for( uint32 i = 0; i < len; i ++ ) { if ( loc.getCharAt( i ) == '/' ) loc.setCharAt( i, '\\' ); } } void Path::join( const String &loc, const String &name, const String &ext ) { if ( m_owner ) m_owner->m_encoded.size(0); m_path = loc; if( loc.length() != 0 ) { if( loc.getCharAt( loc.length() - 1 ) != '/' ) m_path += '/'; } if ( name.length() != 0 ) { m_path += name; if( ext.length() != 0 ) { if( name.getCharAt( name.length() - 1 ) != '.' ) m_path.append( '.' ); m_path += ext; } } set( m_path ); } void Path::join( const String &res, const String &loc, const String &name, const String &ext ) { if ( m_owner ) m_owner->m_encoded.size(0); m_path = res; if ( res.length() != 0 && res.getCharAt( res.length() - 1 ) != ':' ) m_path.append( ':' ); m_path += loc; if( loc.length() != 0 ) { if( loc.getCharAt( loc.length() - 1 ) != '/' ) m_path += '/'; } if ( name.length() != 0 ) { m_path += name; if( ext.length() != 0 ) { if( name.getCharAt( name.length() - 1 ) != '.' ) m_path.append( '.' ); m_path += ext; } } set(m_path); } void Path::winToUri( String &ret ) { int size = ret.length(); ret.bufferize(); bool prefix = false; for( int i = 0; i < size; i ++ ) { int chr = ret.getCharAt( i ); if( chr == '\\' ) ret.setCharAt( i, '/' ); else if ( chr == ':' ) prefix = true; } if( prefix && ret.getCharAt(0) != '/' ) ret.prepend('/'); } void Path::uriToWin( String &result ) { result.bufferize(); bool bRem = false; unsigned int i = 0; while( i < result.length() ) { int chr = result.getCharAt( i ); switch ( chr ) { case '/': // "/C:" disk specificator? if ( i == 0 ) bRem = true; else bRem = false; result.setCharAt( i, '\\' ); break; case ':': // was the first "/" to be removed? if ( bRem ) { result.remove(0,1); // get I back. i--; } break; case '+': result.setCharAt( i, ' ' ); break; // hex escape? case '%': { // have we got enough space for 2 chars? if( result.length() < i + 2 ) { return; } char n1 = result.getCharAt( i+1 ); char n2 = result.getCharAt( i+2 ); uint32 r = 0; if( n1 >= 'a' && n1 <= 'f' ) r += (n1-'a')*0x10; else if( n1 >= 'A' && n1 <= 'F' ) r += (n1-'A')*0x10; else if( n1 >= '0' && n1 <= '9' ) r += (n1-'0')*0x10; if( n2 >= 'a' && n2 <= 'f' ) r += (n2-'a'); else if( n1 >= 'A' && n2 <= 'F' ) r += (n2-'A'); else if( n2 >= '0' && n2 <= '9' ) r += (n2-'0'); // remove extra chars result.remove( i, i+2 ); // change the transformed one. result.setCharAt( i, r ); } break; } i ++; } } } engine/pcode.cpp000066400000000000000000000162251176363201700141350ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: pcode.cpp Utilities to manage the Falcon Virtual Machine pseudo code. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 24 Jul 2009 19:42:40 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include namespace Falcon { void PCODE::convertEndianity( uint32 paramType, byte* targetArea, bool into ) { switch( paramType ) { case P_PARAM_INT32: case P_PARAM_STRID: case P_PARAM_LBIND: case P_PARAM_GLOBID: case P_PARAM_LOCID: case P_PARAM_PARID: case P_PARAM_NTD32: *reinterpret_cast(targetArea) = endianInt32(*reinterpret_cast(targetArea) ); break; case P_PARAM_INT64: case P_PARAM_NTD64: { // high part - low part if(into) { // load from our straight int uint64 value64 = loadInt64( targetArea ); // endianize each subpart, and invert their position. *reinterpret_cast(targetArea+sizeof(uint32)) = endianInt32((uint32)(value64 >> 32)); *reinterpret_cast(targetArea) = (uint32) endianInt32((uint32)(value64 & 0xFFFFFFFF)); } else { // load from different endianity uint64 value64 = grabInt64( targetArea ); *reinterpret_cast(targetArea) = (uint32)(value64 >> 32); *reinterpret_cast(targetArea+sizeof(uint32)) = (uint32) value64; } } break; case P_PARAM_NUM: { union t_unumeric { struct t_integer { uint32 high; uint32 low; } integer; numeric number; } unumeric; unumeric.number = grabNum( targetArea ); // high part - low part *reinterpret_cast(targetArea) = unumeric.integer.high; *reinterpret_cast(targetArea+sizeof(uint32)) = unumeric.integer.low; } break; } } uint32 PCODE::advanceParam( uint32 paramType ) { uint32 offset; switch( paramType ) { case P_PARAM_NIL: case P_PARAM_NOTUSED: case P_PARAM_TRUE: case P_PARAM_FALSE: case P_PARAM_REGA: case P_PARAM_REGB: case P_PARAM_REGS1: case P_PARAM_FSELF: case P_PARAM_REGL1: case P_PARAM_REGL2: offset = 0; break; case P_PARAM_NUM: case P_PARAM_INT64: case P_PARAM_NTD64: offset = 8; break; default: offset = 4; } return offset; } void PCODE::deendianize( byte* code, uint32 codeSize, bool into ) { uint32 iPos =0; byte opcode; while( iPos < codeSize ) { opcode = code[ iPos ]; uint32 iStart = iPos; // get the options iPos += 4; if( code[ iStart + 1 ] != 0 ) { convertEndianity( code[ iStart + 1 ], code + iPos, into ); iPos += advanceParam( code[iStart + 1] ); if( code[ iStart + 2 ] != 0 ) { convertEndianity( code[iStart + 2], code + iPos ); iPos += advanceParam( code[iStart + 2] ); if( code[ iStart + 3 ] != 0 ) { convertEndianity( code[iStart + 3], code + iPos ); iPos += advanceParam( code[iStart + 3] ); } } } // if the operation is a switch, it's handled a bit specially. if ( opcode == P_SWCH || opcode == P_SELE ) { // get the switch table (aready de-endianized in the above step) iPos -= sizeof(int64); uint16 sw_int, sw_rng, sw_str, sw_obj; uint64 value64; if ( into ) { // we have just destroyed our int64 switch value, which must be inverted value64 = grabInt64( code+iPos ); } else { // we have just correctly decoded our integer from the stream. value64 = loadInt64( code + iPos ); } sw_int = (int16) (value64 >> 48); sw_rng = (int16) ((value64 >> 32) & 0xFFFF); sw_str = (int16) ((value64 >> 16) & 0xFFFF); sw_obj = (int16) (value64 & 0xFFFF); iPos += sizeof( uint64 ); // Endianize the nil landing *reinterpret_cast(code+iPos) = endianInt32( *reinterpret_cast(code+iPos) ); iPos += sizeof( uint32 ); // endianize the integer table while( sw_int > 0 ) { if( into ) { // the int64 value uint64 value64 = loadInt64( code+iPos ); // high part - low part *reinterpret_cast(code+iPos+sizeof(uint32)) = endianInt32((uint32)(value64 >> 32)); *reinterpret_cast(code+iPos) = endianInt32((uint32) value64); } else { uint64 value64 = grabInt64( code+iPos ); *reinterpret_cast(code+iPos) = (uint32)(value64 >> 32); *reinterpret_cast(code+iPos+sizeof(uint32)) = (uint32) value64; } iPos += sizeof( int64 ); // and the landing *reinterpret_cast(code+iPos) = endianInt32( *reinterpret_cast(code+iPos) ); iPos += sizeof( int32 ); --sw_int; } // endianize the range table while( sw_rng > 0 ) { // the int32 value -- start *reinterpret_cast(code+iPos) = endianInt32( *reinterpret_cast(code+iPos) ); iPos += sizeof( int32 ); // the int32 value -- end *reinterpret_cast(code+iPos) = endianInt32( *reinterpret_cast(code+iPos) ); iPos += sizeof( int32 ); // and the landing *reinterpret_cast(code+iPos) = endianInt32( *reinterpret_cast(code+iPos) ); iPos += sizeof( int32 ); --sw_rng; } // endianize the string table while( sw_str > 0 ) { // the int32 string index *reinterpret_cast(code+iPos) = endianInt32( *reinterpret_cast(code+iPos) ); iPos += sizeof( int32 ); // and the landing *reinterpret_cast(code+iPos) = endianInt32( *reinterpret_cast(code+iPos) ); iPos += sizeof( int32 ); --sw_str; } // endianize the object table while( sw_obj > 0 ) { // the int32 symbol index *reinterpret_cast(code+iPos) = endianInt32( *reinterpret_cast(code+iPos) ); iPos += sizeof( int32 ); // and the landing *reinterpret_cast(code+iPos) = endianInt32( *reinterpret_cast(code+iPos) ); iPos += sizeof( int32 ); --sw_obj; } } } } } /* end of pcode.cpp */ engine/poopseq.cpp000066400000000000000000000120011176363201700145150ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: poopseq.h Virtual sequence that can be used to iterate over poop providers. *AT THE MOMENT* providing just "append" method to re-use sequence comprehension in OOP and POOP contexts. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 10 Aug 2009 10:53:29 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include namespace Falcon { Item m_appendMth; uint32 m_mark; PoopSeq::PoopSeq( VMachine* vm, const Item &iobj ): m_mark( vm->generation() ), m_vm( vm ) { switch( iobj.type() ) { case FLC_ITEM_OBJECT: if( ! iobj.asObjectSafe()->getMethod( "append", m_appendMth ) ) { throw new CodeError( ErrorParam( e_miss_iface, __LINE__ ) .origin( e_orig_runtime ) .extra( "append") ); } break; case FLC_ITEM_DICT: if( ! iobj.asDict()->getMethod( "append", m_appendMth ) ) { throw new CodeError( ErrorParam( e_miss_iface, __LINE__ ) .origin( e_orig_runtime ) .extra( "append") ); } break; default: throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "O|D" ) ); } } PoopSeq::PoopSeq( const PoopSeq& other ): m_appendMth( other.m_appendMth ), m_mark( other.m_vm->generation() ), m_vm( other.m_vm ) {} PoopSeq::~PoopSeq() { } bool PoopSeq::empty() const { return true; } void PoopSeq::gcMark( uint32 gen ) { if ( m_mark != gen ) { m_mark = gen; Sequence::gcMark( gen ); memPool->markItem( m_appendMth ); } } PoopSeq* PoopSeq::clone() const { return new PoopSeq( *this ); } const Item &PoopSeq::front() const { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ) .extra( "PoopSeq::front" ) ); } const Item &PoopSeq::back() const { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ) .extra( "PoopSeq::back" ) ); } void PoopSeq::clear() { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ) .extra( "PoopSeq::clear" ) ); } void PoopSeq::append( const Item& itm ) { m_vm->pushParam( itm ); m_vm->callItemAtomic( m_appendMth, 1 ); } void PoopSeq::prepend( const Item& itm ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ).origin( e_orig_runtime ).extra( "PoopSeq::prepend" ) ); } //============================================================ // Iterator management. //============================================================ void PoopSeq::getIterator( Iterator& tgt, bool tail ) const { Sequence::getIterator( tgt, tail ); // nothing to do } void PoopSeq::copyIterator( Iterator& tgt, const Iterator& source ) const { Sequence::copyIterator( tgt, source ); // nothing to do } void PoopSeq::insert( Iterator &iter, const Item &data ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "PoopSeq::insert" ) ); } void PoopSeq::erase( Iterator &iter ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "PoopSeq::erase" ) ); } bool PoopSeq::hasNext( const Iterator &iter ) const { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "PoopSeq::erase" ) ); } bool PoopSeq::hasPrev( const Iterator &iter ) const { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "PoopSeq::erase" ) ); } bool PoopSeq::hasCurrent( const Iterator &iter ) const { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "PoopSeq::next" ) ); } bool PoopSeq::next( Iterator &iter ) const { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "PoopSeq::next" ) ); } bool PoopSeq::prev( Iterator &iter ) const { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "PoopSeq::prev" ) ); } Item& PoopSeq::getCurrent( const Iterator &iter ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "PoopSeq::prev" ) ); } Item& PoopSeq::getCurrentKey( const Iterator &iter ) { throw new CodeError( ErrorParam( e_non_dict_seq, __LINE__ ) .origin( e_orig_runtime ).extra( "PoopSeq::getCurrentKey" ) ); } bool PoopSeq::equalIterator( const Iterator &first, const Iterator &second ) const { return false; } } /* end of poopseq.cpp */ engine/proptable.cpp000066400000000000000000000223661176363201700150360ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: proptable.cpp Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven ott 14 2005 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Implementation of the property table for live object and classes. */ #include #include #include #include #include #include #include namespace Falcon { PropertyTable::PropertyTable( uint32 size ): m_bReflective( false ), m_bStatic( true ) { m_size = size; m_added = 0; m_entries = (PropEntry *) memAlloc( sizeof( PropEntry ) * size ); // Luckily, 0 is the neuter value for all our representations. memset( m_entries, 0, sizeof( PropEntry ) * size ); } PropertyTable::PropertyTable( const PropertyTable &other ): m_bReflective( other.m_bReflective ), m_bStatic( other.m_bStatic ) { m_size = other.m_size; m_added = other.m_added; m_entries = (PropEntry *) memAlloc( sizeof( PropEntry ) * m_size ); memcpy( m_entries, other.m_entries, sizeof( PropEntry ) * m_size ); } PropertyTable::~PropertyTable() { memFree( m_entries ); } void PropertyTable::checkProperties() { m_bStatic = true; m_bReflective = false; for( uint32 i = 0; i < m_added; i++ ) { const PropEntry &e = m_entries[i]; if ( e.m_eReflectMode != e_reflectNone ) { m_bReflective = true; } else if ( ! e.m_bReadOnly ) { m_bStatic = false; } } } bool PropertyTable::findKey( const String &key, uint32 &pos ) const { uint32 lower = 0, higher, point; higher = m_size; const String *current; if ( higher == 0 ) { pos = 0; return false; } higher --; point = higher / 2; while ( true ) { // get the table row current = m_entries[point].m_name; if( *current == key ) { pos = point; return true; } else { if ( lower == higher -1 ) { // key is EVEN less than the lower one if ( key < *current ) { pos = lower; return false; } // being integer math, ulPoint is rounded by defect and has // already looked at the ulLower position if ( key == *m_entries[higher].m_name ) { pos = higher; return true; } // we cannot find it break; } else if ( lower == higher ) { // again, can't find it. break; } if ( key > *current ) { lower = point; } else { higher = point; } point = ( lower + higher ) / 2; } } // entry not found, but signal the best match anyway pos = key > *current ? higher + 1 : higher; return false; } PropEntry &PropertyTable::append( const String *key ) { if( m_added >= m_size ) return m_entries[0]; if ( m_added != 0 ) { uint32 pos; if ( !findKey( *key, pos ) ) { memmove( m_entries + pos, m_entries + pos + 1, sizeof( PropEntry ) * (m_added - pos) ); m_entries[pos].m_name = key; } m_added++; return m_entries[pos]; } else { return appendSafe( key ); } } void PropEntry::reflectTo( CoreObject *instance, void *user_data, const Item &prop ) const { byte *ud = (byte *) user_data; switch( m_eReflectMode ) { case e_reflectBool: ((bool *) user_data)[ m_reflection.offset ] = prop.isTrue(); break; case e_reflectByte: ud[ m_reflection.offset ] = (byte) prop.forceInteger(); break; case e_reflectChar: ((char *) user_data)[ m_reflection.offset ] = (char) prop.forceInteger(); break; case e_reflectShort: *(int16 *) (ud + m_reflection.offset) = (int16) prop.forceInteger(); break; case e_reflectUShort: *(uint16 *)(ud + m_reflection.offset) = (uint16) prop.forceInteger(); break; case e_reflectInt: *(int32 *) (ud + m_reflection.offset) = (int32) prop.forceInteger(); break; case e_reflectUInt: *(uint32 *)(ud + m_reflection.offset) = (uint32) prop.forceInteger(); break; case e_reflectLong: *(long *)(ud + m_reflection.offset) = (long) prop.forceInteger(); break; case e_reflectULong: *(unsigned long *)(ud + m_reflection.offset) = (unsigned long) prop.forceInteger(); break; case e_reflectLL: *(int64 *)(ud + m_reflection.offset) = prop.forceInteger(); break; case e_reflectULL: *(uint64 *)(ud + m_reflection.offset) = (uint64) prop.forceInteger(); break; case e_reflectFloat: *(float *)(ud + m_reflection.offset) = (float) prop.forceNumeric(); break; case e_reflectDouble: *(double *)(ud + m_reflection.offset) = (double) prop.forceNumeric(); break; case e_reflectFunc: // We should not have been called if "to" was zero; we're read only. fassert( m_reflection.rfunc.to != 0 ); m_reflection.rfunc.to( instance, user_data, *const_cast(&prop), *this ); break; case e_reflectSetGet: // reflection is via a private setter function. // We should not have been called if "to" was NO_OFFSET; we're read only. fassert( m_reflection.gs.m_setterId != PropEntry::NO_OFFSET ); { VMachine* vm = VMachine::getCurrent(); const Item* call = instance->generator()->properties() .getValue( m_reflection.gs.m_setterId ); fassert( call->isCallable() ); if( vm->currentSymbol() == call->asFunction()->symbol() ) { // can't be throw new AccessError( ErrorParam( e_prop_loop, __LINE__ ) .origin( e_orig_vm ) .extra( *m_name )); } Item method = *call; method.methodize( instance ); vm->pushParam( prop ); VMachine::getCurrent()->callItemAtomic( method, 1 ); } break; default: fassert( false ); break; } } void PropEntry::reflectFrom( CoreObject *instance, void *user_data, Item &prop ) const { byte *ud = (byte *) user_data; switch( m_eReflectMode ) { case e_reflectBool: prop.setBoolean( ((bool *) user_data)[ m_reflection.offset ] ); break; case e_reflectByte: prop = (int64) ud[ m_reflection.offset ]; break; case e_reflectChar: prop = (int64) ((char *) user_data)[ m_reflection.offset ]; break; case e_reflectShort: prop = (int64) *(int16 *)(ud + m_reflection.offset); break; case e_reflectUShort: prop = (int64) *(uint16 *)(ud + m_reflection.offset); break; case e_reflectInt: prop = (int64) *(int32 *)(ud + m_reflection.offset); break; case e_reflectUInt: prop = (int64) *(uint32 *)(ud + m_reflection.offset); break; case e_reflectLong: prop = (int64) *(long *)(ud + m_reflection.offset); break; case e_reflectULong: prop = (int64) *(unsigned long *)(ud + m_reflection.offset); break; case e_reflectLL: prop = *(int64 *)(ud + m_reflection.offset); break; case e_reflectULL: prop = (int64) *(uint64 *)(ud + m_reflection.offset); break; case e_reflectFloat: prop = (numeric) *(float *)(ud + m_reflection.offset); break; case e_reflectDouble: prop = (numeric) *(double *)(ud + m_reflection.offset); break; case e_reflectFunc: m_reflection.rfunc.from( instance, user_data, prop, *this ); break; case e_reflectSetGet: // reflection is via a private setter function. // Write only properties are allowed. if( m_reflection.gs.m_getterId == PropEntry::NO_OFFSET ) { throw new AccessError( ErrorParam( e_prop_wo, __LINE__ ) .origin( e_orig_vm ) .extra( *m_name ) ); } else { VMachine* vm = VMachine::getCurrent(); const Item* call = instance->generator()->properties() .getValue( m_reflection.gs.m_getterId ); if( vm->currentSymbol() == call->asFunction()->symbol() ) { // can't be throw new AccessError( ErrorParam( e_prop_loop, __LINE__ ) .origin( e_orig_vm ) .extra( *m_name )); } Item method = *call; method.methodize( instance ); VMachine::getCurrent()->callItemAtomic( method, 0 ); prop = vm->regA(); } break; default: fassert( false ); break; } } } /* end of proptable.cpp */ engine/rampmode.cpp000066400000000000000000000044531176363201700146470ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: rampmode.cpp Ramp mode - progressive GC limits adjustment algoritmhs ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 19 Mar 2009 08:23:59 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include namespace Falcon { RampMode::~RampMode() {} void RampMode::reset() { } //========================================================= // A void ramp mode. RampNone::~RampNone() { } void RampNone::onScanInit() { m_active = memPool->thresholdActive(); m_normal = memPool->thresholdNormal(); } void RampNone::onScanComplete() { } //========================================================= // RampStrict::~RampStrict() { } void RampStrict::onScanInit() { } void RampStrict::onScanComplete() { m_active = gcMemAllocated(); m_normal = m_active/2; } //========================================================= // RampLoose::~RampLoose() { } void RampLoose::onScanInit() { m_active = (size_t)(gcMemAllocated() ); m_normal = (size_t)(gcMemAllocated() / 2 ); } void RampLoose::onScanComplete() { } //========================================================= // RampSmooth::RampSmooth( numeric factor ): RampMode(), m_pNormal(0), m_pActive(0), m_factor( factor ) { if ( m_factor < 1.0 ) m_factor = 1.0; } RampSmooth::~RampSmooth() { } void RampSmooth::reset() { m_pNormal = 0; } void RampSmooth::onScanInit() { // on the first loop, we setup the waiting loops. if ( m_pNormal == 0 ) { m_pNormal = gcMemAllocated(); m_normal = m_pNormal; m_active = (size_t)(m_normal * m_factor); } } void RampSmooth::onScanComplete() { // size_t is usually unsigned. size_t allocated = gcMemAllocated(); if( m_pNormal > allocated ) { // we're getting smaller m_pNormal -= (size_t)((m_pNormal - allocated) / m_factor); } else { m_pNormal += size_t((allocated-m_pNormal) / m_factor); } m_normal = m_pNormal; m_active = (size_t)(m_normal * m_factor); } } /* end of rampmode.cpp */ engine/rangeseq.cpp000066400000000000000000000111431176363201700146420ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: rangeseq.cpp Virtual sequence that can be used to iterate over ranges. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 06 Aug 2009 22:10:00 +020 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include namespace Falcon { RangeSeq::RangeSeq( const CoreRange &rng ) { if ( rng.isOpen() ) throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( "open range") ); m_start = rng.start(); m_end = rng.end(); m_step = rng.step(); // force the step to be non-default if( m_step == 0 ) m_step = m_start <= m_end ? 1 : -1; } RangeSeq::RangeSeq( int64 s, int64 e, int64 step ): m_start(s), m_end(e), m_step(step) { // force the step to be non-default if( m_step == 0 ) m_step = m_start <= m_end ? 1 : -1; } RangeSeq::~RangeSeq() {} RangeSeq* RangeSeq::clone() const { return new RangeSeq( *this ); } const Item &RangeSeq::front() const { m_number = m_start; return m_number; } const Item &RangeSeq::back() const { m_number = m_end; return m_number; } void RangeSeq::clear() { m_start = m_end; m_step = 1; } bool RangeSeq::empty() const { return m_step > 0 ? m_start >= m_end : m_start < m_end; } void RangeSeq::append( const Item &data ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "RangeSeq::append" ) ); } void RangeSeq::prepend( const Item &data ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "RangeSeq::prepend" ) ); } void RangeSeq::getIterator( Iterator& tgt, bool tail ) const { Sequence::getIterator( tgt, tail ); tgt.position( m_start ); } void RangeSeq::copyIterator( Iterator& tgt, const Iterator& source ) const { Sequence::copyIterator( tgt, source ); tgt.position( source.position() ); } void RangeSeq::insert( Iterator &, const Item & ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "RangeSeq::insert" ) ); } void RangeSeq::erase( Iterator & ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "RangeSeq::erase" ) ); } bool RangeSeq::hasNext( const Iterator &iter ) const { return m_step > 0 ? iter.position() + m_step < m_end : iter.position() + m_step >= m_end; } bool RangeSeq::hasPrev( const Iterator &iter ) const { return m_step > 0 ? iter.position() - m_step > m_start : iter.position() - m_step < m_end; } bool RangeSeq::hasCurrent( const Iterator &iter ) const { return m_step > 0 ? iter.position() < m_end : iter.position() >= m_end; } bool RangeSeq::next( Iterator &iter ) const { if( m_step > 0 ) { if ( iter.position() < m_end ) iter.position( iter.position() + m_step ); return iter.position() < m_end; } else { if ( iter.position() >= m_end ) iter.position( iter.position() + m_step ); return iter.position() >= m_end; } } bool RangeSeq::prev( Iterator &iter ) const { if( m_step > 0 ) { if ( iter.position() > m_start ) { iter.position( iter.position() - m_step ); return true; } else { // move past end if( (m_end - m_start) % m_step == 0 ) iter.position( m_end ); else iter.position( ((m_end - m_start)/ m_step + 1 )* m_step + m_start); return false; } } else { if ( iter.position() < m_start ) { iter.position( iter.position() + m_step ); return true; } else { iter.position( ((m_start - m_end)/ m_step - 1 )* m_step + m_start); return false; } } } Item& RangeSeq::getCurrent( const Iterator &iter ) { m_number = iter.position(); return m_number; } Item& RangeSeq::getCurrentKey( const Iterator &iter ) { throw new CodeError( ErrorParam( e_non_dict_seq, __LINE__ ) .origin( e_orig_runtime ).extra( "RangeSeq::getCurrentKey" ) ); } bool RangeSeq::equalIterator( const Iterator &first, const Iterator &second ) const { return first.position() == second.position(); } } /* end of rangeseq.cpp */ engine/reflectobject.cpp000066400000000000000000000063161176363201700156560ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: cobject.cpp Core object implementation ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom dic 5 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Core object implementation. */ #include #include #include #include #include #include namespace Falcon { ReflectObject::ReflectObject( const CoreClass *generator, void *user_data ): CoreObject( generator ) { if( user_data != 0 ) setUserData( user_data ); // In some controlled case, it may be the _init method to set the object. } ReflectObject::ReflectObject( const CoreClass* generator, FalconData* fdata ): CoreObject( generator ) { if( fdata != 0 ) setUserData( fdata ); } ReflectObject::ReflectObject( const CoreClass* generator, Sequence* seq ): CoreObject( generator ) { if( seq != 0 ) setUserData( seq ); } ReflectObject::ReflectObject( const ReflectObject& other ): CoreObject( other ) {} ReflectObject::~ReflectObject() {} bool ReflectObject::setProperty( const String &propName, const Item &value ) { register uint32 pos; const PropertyTable &pt = m_generatedBy->properties(); if ( pt.findKey( propName, pos ) ) { const PropEntry &entry = pt.getEntry( pos ); if ( entry.m_bReadOnly ) { throw new AccessError( ErrorParam( e_prop_ro, __LINE__ ).extra( propName ) ); } fassert( m_user_data != 0 ); fassert( entry.m_eReflectMode != e_reflectNone ); entry.reflectTo( this, m_user_data, value ); return true; } return false; } bool ReflectObject::getProperty( const String &propName, Item &ret ) const { fassert( m_generatedBy != 0 ); register uint32 pos; const PropertyTable &pt = m_generatedBy->properties(); if ( pt.findKey( propName, pos ) ) { //Ok, we found the property, but what should we do with that? const PropEntry &entry = pt.getEntry( pos ); fassert( entry.m_bReadOnly || entry.m_eReflectMode != e_reflectNone ); if ( entry.m_bReadOnly && entry.m_eReflectMode == e_reflectNone ) { ret = entry.m_value; return true; } fassert( m_user_data != 0 ); entry.reflectFrom( const_cast(this), m_user_data, ret ); return true; } return false; } ReflectObject* ReflectObject::clone() const { return new ReflectObject( *this ); } //============================================ CoreObject* ReflectOpaqueFactory( const CoreClass *cls, void *user_data, bool ) { return new ReflectObject( cls, user_data ); } CoreObject* ReflectFalconFactory( const CoreClass *cls, void *user_data, bool ) { return new ReflectObject( cls, static_cast(user_data) ); } CoreObject* ReflectSequenceFactory( const CoreClass *cls, void *user_data, bool ) { return new ReflectObject( cls, static_cast(user_data) ); } } // namespace Falcon /* end of cobject.cpp */ engine/rosstream.cpp000066400000000000000000000026751176363201700150660ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: rosstream.cpp Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab ago 19 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Short description */ #include #include #include namespace Falcon { ROStringStream::ROStringStream( const String &source ): StringStream( -1 ) { setBuffer( source ); } ROStringStream::ROStringStream( const char *source, int size ): StringStream( -1 ) { setBuffer( source, size ); } ROStringStream::ROStringStream( const ROStringStream& other ): StringStream( other ) { } bool ROStringStream::close() { return detachBuffer(); } int32 ROStringStream::write( const void *buffer, int32 size ) { status( t_unsupported ); return -1; } int32 ROStringStream::write( const String &source ) { status( t_unsupported ); return -1; } int32 ROStringStream::writeAvailable( int32 msecs, const Falcon::Sys::SystemData* ) { status( t_unsupported ); return -1; } bool ROStringStream::truncate( int64 pos ) { status( t_unsupported ); return false; } ROStringStream *ROStringStream::clone() const { return new ROStringStream( *this ); } } /* end of rosstream.cpp */ engine/runtime.cpp000066400000000000000000000137711176363201700145310ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_runtime.cpp Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer ago 18 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include namespace Falcon { ModuleMap::ModuleMap(): Map( &traits::t_stringptr(), &traits::t_voidp() ) { } ModuleVector::ModuleVector(): GenericVector( &traits::t_voidp() ) { } Runtime::Runtime(): m_loader( 0 ), m_provider( 0 ), m_modPending( &traits::t_stringptr(), &traits::t_int() ), m_hasMainModule( true ) {} Runtime::Runtime( ModuleLoader *loader, VMachine *prov ): m_loader( loader ), m_provider( prov ), m_modPending( &traits::t_stringptr(), &traits::t_int() ), m_hasMainModule( true ) {} /** Declared here to avoid inlining of destructor. */ Runtime::~Runtime() { for ( uint32 i = 0; i < m_modvect.size(); i++ ) { delete m_modvect.moduleDepAt( i ); } } void Runtime::addModule( Module *mod, bool isPrivate ) { if ( m_modules.find( &mod->name() ) != 0 ) return; // already in.. ModuleDep *dep = new ModuleDep( mod, isPrivate ); m_modules.insert( &mod->name(), dep ); if ( m_loader != 0 && ! mod->dependencies().empty() ) { MapIterator deps = mod->dependencies().begin(); while( deps.hasCurrent() ) { const ModuleDepData *depdata = *(const ModuleDepData **) deps.currentValue(); const String &moduleName = depdata->moduleName(); // if we have a provider, skip this module if already found VM LiveModule *livemod; ModuleDep **olddep; if( m_provider != 0 && (livemod = m_provider->findModule( moduleName )) != 0 ) { if ( livemod->isPrivate() && ! depdata->isPrivate() ) { // just insert the module with the request to extend its privacy. m_modvect.push( new ModuleDep( const_cast(livemod->module()), true ) ); } // anyhow, we don't need to perform another load. deps.next(); continue; } else { // ... or do we have already loaded the module? if( (olddep = (ModuleDep **) m_modules.find( &moduleName )) != 0 ) { // already in? -- should we broaden the publishing? if( (*olddep)->isPrivate() && ! depdata->isPrivate() ) { (*olddep)->setPrivate( false ); } // anyhow, we don't need to perform another load. deps.next(); continue; } } Module *l = 0; try { if( depdata->isFile() ) { // if the path is relative, then it's relative to the parent module path. Path p(moduleName); if ( !p.isAbsolute() ) { l = m_loader->loadFile( Path(mod->path()).getFullLocation() + "/" + moduleName ); } else { l = m_loader->loadFile( moduleName ); } } else { l = m_loader->loadName( moduleName, mod->name() ); } } catch( Error* e) { if ( e->module() == "" ) e->module( moduleName ); m_modules.erase( &mod->name() ); delete dep; CodeError* ce = new CodeError( ErrorParam( e_loaderror ) .module( mod->name() ) .extra( "loading " + moduleName ) .origin( e_orig_loader ) ); ce->appendSubError( e ); e->decref(); throw ce; } fassert( l != 0 ); try { addModule( l, depdata->isPrivate() ); } catch( Error* ) { l->decref(); m_modules.erase( &mod->name() ); delete dep; throw; } l->decref(); deps.next(); } m_modvect.push( dep ); } else { int insertAt = m_modvect.size(); // re-sort the array if we had some previous dependency. int *pending = (int *) m_modPending.find( &mod->name() ); if ( pending != 0 ) { insertAt = *pending; m_modvect.insert( dep, (uint32) insertAt ); } else m_modvect.push( dep ); // then, record pending modules for THIS module, if any. MapIterator deps = mod->dependencies().begin(); while( deps.hasCurrent() ) { const ModuleDepData *depdata = *(const ModuleDepData **) deps.currentValue(); const String &moduleName = depdata->moduleName(); // if the module is missing both from our module list and dependency list // add a dependency here if ( m_modules.find( &moduleName ) == 0 && m_modPending.find( &moduleName ) == 0 ) { m_modPending.insert( &moduleName, &insertAt ); } deps.next(); } } } void Runtime::loadName( const String &name, const String &parent, bool bIsPrivate ) { Module *l = m_loader->loadName( name, parent ); try { addModule( l, bIsPrivate ); l->decref(); } catch( Error* ) { l->decref(); throw; } } void Runtime::loadFile( const String &file, bool bIsPrivate ) { Module *l = m_loader->loadFile( file, ModuleLoader::t_none, true ); try { if( bIsPrivate ) { // mangle the file name... String name = l->name(); l->name( name.A("-").N(rand()).N(rand())); } addModule( l, bIsPrivate ); l->decref(); } catch( Error* ) { l->decref(); throw; } } } /* end of flc_runtime.cpp */ engine/sequence.cpp000066400000000000000000000624271176363201700146600ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: sequence.cpp Definition of abstract sequence class. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 13 Jul 2009 23:00:10 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include namespace Falcon { class FALCON_DYN_CLASS GarbagePointer2: public GarbagePointer { FalconData *m_ptr; public: GarbagePointer2( FalconData *p ): GarbagePointer( p ) { } virtual ~GarbagePointer2() {} virtual bool finalize() { return false; } }; //=================================================================== // TODO Remvoe at first major release // inline bool s_appendMe( VMachine *vm, Sequence* me, const Item &source, const Item &filter ) { if( filter.isNil() ) { me->append( source ); } else { vm->pushParam( source ); vm->pushParam( vm->self() ); vm->callItemAtomic(filter,2); if ( ! vm->regA().isOob() ) me->append( vm->regA().isString() ? new CoreString( *vm->regA().asString() ) : vm->regA() ); else if ( vm->regA().isInteger() && vm->regA().asInteger() == 0 ) return false; } return true; } void Sequence::comprehension( VMachine* vm, const Item& cmp, const Item& filter ) { if( cmp.isRange() ) { { if ( cmp.asRangeIsOpen() ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( "open range" ) ); } int64 start = cmp.asRangeStart(); int64 end = cmp.asRangeEnd(); int64 step = cmp.asRangeStep(); if ( start == end ) { if ( step < 0 ) { s_appendMe( vm, this, start, filter ); } return; } if( start < end ) { if ( step < 0 ) return; if ( step == 0 ) step = 1; while( start < end ) { if ( ! s_appendMe( vm, this, start, filter ) ) break; start += step; } } else { if ( step > 0 ) return; if ( step == 0 ) step = -1; while( start >= end ) { if ( ! s_appendMe( vm, this, start, filter ) ) break; start += step; } } } } else if ( cmp.isCallable() ) { while( true ) { vm->callItemAtomic( cmp, 0 ); if( vm->regA().isOob() && vm->regA().isInteger() && vm->regA().asInteger() == 0 ) { return; } Item temp = vm->regA(); if( ! s_appendMe( vm, this, temp, filter ) ) break; } } // todo --- remove this as soon as we have iterators on ItemArrays else if ( cmp.isArray() ) { const CoreArray& arr = *cmp.asArray(); for( uint32 i = 0; i < arr.length(); i ++ ) { if ( ! s_appendMe( vm, this, arr[i], filter ) ) break; } } else if ( (cmp.isObject() && cmp.asObjectSafe()->getSequence() ) ) { //Sequence* seq = cmp.isArray() ? &cmp.asArray()->items() : cmp.asObjectSafe()->getSequence(); Sequence* seq = cmp.asObjectSafe()->getSequence(); Iterator iter( seq ); while( iter.hasCurrent() ) { if ( ! s_appendMe( vm, this, iter.getCurrent(), filter ) ) break; iter.next(); } } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "A|C|R|Sequence, [C]" ) ); } } //========================================================================== // static bool multi_comprehension_generic_single_loop( VMachine* vm ); static bool multi_comprehension_callable_multiple_loop( VMachine* vm ); static bool multi_comprehension_filtered_loop( VMachine* vm ); static bool comp_get_all_items_callable_next( VMachine *vm ) { // STACK LOCAL : // 0 - Global counter // 1 - Sequence GC pointer // 2 - filter (nil) // 3 - Source (callable) if( vm->regA().isOob() && vm->regA().isInteger() && vm->regA().asInteger() == 0 ) { // we're done. vm->retval(vm->self()); return false; } // add the data. dyncast(vm->local(1)->asGCPointer())->append( vm->regA().isString() ? new CoreString( *vm->regA().asString() ) : vm->regA() ); // iterate. vm->callFrame( *vm->local(3), 0 ); return true; } // gets all the items that it can from a comprehension static bool comp_get_all_items( VMachine *vm, const Item& cmp ) { Sequence* sequence = dyncast(vm->local(1)->asGCPointer()); if ( cmp.isRange() ) { if ( cmp.asRangeIsOpen() ) { throw new ParamError( ErrorParam( e_param_range, __LINE__ ) .origin( e_orig_runtime ) .extra( "open range" ) ); } int64 start = cmp.asRangeStart(); int64 end = cmp.asRangeEnd(); int64 step = cmp.asRangeStep(); if ( start == end ) { if ( step < 0 ) { sequence->append( start ); } return false; // all done. } if( start < end ) { if ( step < 0 ) return true; if ( step == 0 ) step = 1; while( start < end ) { sequence->append( start ); start += step; } } else { if ( step > 0 ) return false; if ( step == 0 ) step = -1; while( start >= end ) { sequence->append( start ); start += step; } } } else if ( cmp.isCallable() ) { // change the frame handler, so that instead of having vm->returnHandler( comp_get_all_items_callable_next ); vm->callFrame( cmp, 0 ); // need more calls. return true; } else if ( cmp.isArray() ) { const CoreArray& arr = *cmp.asArray(); for( uint32 i = 0; i < arr.length(); i ++ ) { sequence->append( arr[i].isString() ? new CoreString( *arr[i].asString() ) : arr[i] ); } } else if ( (cmp.isObject() && cmp.asObjectSafe()->getSequence() ) ) { Sequence* origseq = cmp.asObjectSafe()->getSequence(); Iterator iter( origseq ); while( iter.hasCurrent() ) { sequence->append( iter.getCurrent().isString() ? new CoreString( *iter.getCurrent().asString() ) : iter.getCurrent() ); iter.next(); } } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "A|C|R|Sequence, [C]" ) ); } // we have processed all the sequence. vm->retval( vm->self() ); return false; } #if 0 static bool multi_comprehension_generic_multiple_loop_post_filter( VMachine* vm ) { if( vm->regA().isOob() && vm->regA().isInteger() && vm->regA().asInteger() == 0 ) { // we're done. vm->retval( vm->self() ); return false; } // TODO: Change in a generic sequence dyncast(vm->local(1)->asGCPointer())->append( vm->regA() ); vm->returnHandler( &multi_comprehension_generic_multiple_loop ); return true; } static bool multi_comprehension_generic_multiple_loop_post_call( VMachine* vm ) { // STACK LOCAL : // 0 - Global counter // 1 - GC Ptr to the sequence // 2 - filter // 3 - first iterator or callable // 4 - second iterator or callable // N-3 - ... nth iterator or callable. // if( vm->regA().isOob() && vm->regA().isInteger() && vm->regA().asInteger() == 0 ) { // we're done. vm->retval( vm->self() ); return false; } if( vm->local(1)->isNil() ) { // no filter vm->local( vm->currentFrame()->stackSize()-1 )->asArray()->append( vm->regA() ); vm->returnHandler( &multi_comprehension_generic_multiple_loop_post_filter ); } else { vm->returnHandler( &multi_comprehension_generic_multiple_loop_post_filter ); Item filter = *vm->local(2); vm->pushParam( vm->regA() ); vm->pushParam( vm->self() ); vm->callFrame( filter, 2 ); } return true; } static bool multi_comprehension_generic_multiple_loop( VMachine* vm ) { // STACK LOCAL : // 0 - Global counter // 1 - GCPointer to the sequence // 2 - filter // 3 - first iterator // 4 - second iterator // N-3 - ... nth iterator // uint32 ssize = vm->currentFrame()->stackSize()-4; int64 current = vm->local(0)->asInteger(); // prepare next loop int64 next = current + 1; if( next > (int64) ssize ) { Item item = *vm->local(ssize + 3); *vm->local(ssize + 3) = new CoreArray( ssize ); // do we have a filter? if( ! vm->local(2)->isNil() ) { // prepare for the next loop *vm->local(0) = (int64) 0; // call the filter Item filter = *vm->local(2); vm->returnHandler( &multi_comprehension_generic_multiple_loop_post_filter ); vm->pushParam( item ); vm->pushParam( vm->self() ); vm->callFrame( filter, 2 ); return true; } else { dyncast(vm->local(1)->asGCPointer())->append( item ); } next = 1; current = 0; } *vm->local(0) = next; // get the element Item* src = vm->local(current+3); if ( src->isOob() ) { // it's a callable -- we must call it. // change the next loop vm->returnHandler( &multi_comprehension_generic_multiple_loop_post_call ); vm->callFrame( *src, 0 ); } else { Iterator* iter = dyncast( src->asGCPointer() ); if ( iter->hasCurrent() ) { vm->local(ssize + 3)->asArray()->append( iter->getCurrent() ); iter->next(); } else { // we're done; we must discard this work-in-progress vm->retval( vm->self() ); return false; } } // continue return true; } static bool multi_comprehension_generic_multiple_loop( VMachine* vm ) { // STACK LOCAL : // 0 - Global counter // 1 - GCPointer to the sequence // 2 - filter // 3 - first iterator or callable // 4 - second iterator or callable // N-3 - ... nth iterator pr callable // uint32 ssize = vm->currentFrame()->stackSize()-4; int64 current = vm->local(0)->asInteger(); // prepare next loop int64 next = current + 1; if( next > (int64) ssize ) { Item item = *vm->local(ssize + 3); *vm->local(ssize + 3) = new CoreArray( ssize ); // do we have a filter? if( ! vm->local(2)->isNil() ) { // prepare for the next loop *vm->local(0) = (int64) 0; // call the filter Item filter = *vm->local(2); vm->returnHandler( &multi_comprehension_generic_multiple_loop_post_filter ); vm->pushParam( item ); vm->pushParam( vm->self() ); vm->callFrame( filter, 2 ); return true; } else { dyncast(vm->local(1)->asGCPointer())->append( item ); } next = 1; current = 0; } *vm->local(0) = next; // get the element Item* src = vm->local(current+3); Iterator* iter = dyncast( src->asGCPointer() ); if ( iter->hasCurrent() ) { vm->local(ssize + 3)->asArray()->append( iter->getCurrent() ); iter->next(); } else { // we're done; we must discard this work-in-progress vm->retval( vm->self() ); return false; } // continue return true; } #endif static bool multi_comprehension_filtered_loop_next( VMachine* vm ) { Sequence* self = dyncast(vm->local(1)->asGCPointer()); Item& regA = vm->regA(); // do the last operation was an oob? if( regA.isOob() ) { // is it an integer? if( regA.isInteger() ) { // Request to stop? if ( regA.asInteger() == 0 ) { vm->retval( vm->self() ); return false; } else if ( regA.asInteger() != 1 ) { self->append( vm->regA() ); } } else self->append( vm->regA() ); } else self->append( vm->regA().isString() ? new CoreString( *vm->regA().asString() ) : vm->regA() ); return multi_comprehension_filtered_loop( vm ); } static bool multi_comprehension_filtered_loop( VMachine* vm ) { // STACK LOCAL : // 0 - Global counter // 1 - GCPointer to the sequence // 2 - filter // 3 - first iterator // 4 - second iterator // N-3 - ... nth iterator uint32 ssize = vm->currentFrame()->stackSize()-4; // create the object to be added. for( uint32 elem = 0; elem < ssize; ++elem ) { Iterator* seq = dyncast(vm->local(elem+3)->asGCPointer()); if( ! seq->hasCurrent() ) { vm->retval( vm->self() ); return false; } vm->pushParam( seq->getCurrent() ); } // advance uint32 pos = ssize; while( pos > 0 ) { Iterator* seq = dyncast(vm->local(pos-1+3)->asGCPointer()); // can we advance? if( seq->next() ) break; //--- and advance the previous element. --pos; //--- no? reset this element, if( pos > 0 ) { // but only if it's not the first. Then, we leave this set. seq->goTop(); // leaving the first element set at bottom, we'll terminate at next loop } } vm->pushParam( vm->self() ); vm->returnHandler( multi_comprehension_filtered_loop_next ); vm->callFrame( *vm->local(2), ssize + 1); // continue return true; } static bool multi_comprehension_generic_single_loop_post_filter( VMachine* vm ) { if( vm->regA().isOob() && vm->regA().isInteger() ) { if( vm->regA().asInteger() == 0 ) { // we're done. vm->retval( vm->self() ); return false; } else if ( vm->regA().asInteger() == 1 ) { // don't append vm->returnHandler( multi_comprehension_generic_single_loop ); return true; } } dyncast(vm->local(1)->asGCPointer())->append( vm->regA().isString() ? new CoreString( *vm->regA().asString() ) : vm->regA() ); vm->returnHandler( multi_comprehension_generic_single_loop ); return true; } static bool multi_comprehension_generic_single_loop_post_call( VMachine* vm ) { if( vm->regA().isOob() && vm->regA().isInteger() && vm->regA().asInteger() == 0 ) { // we're done. vm->retval( vm->self() ); return false; } // we know we have a filter, or we'd be in the simpler get all case. Item filter = *vm->local(2); vm->returnHandler( multi_comprehension_generic_single_loop_post_filter ); vm->pushParam( vm->regA() ); vm->pushParam( vm->self() ); vm->callFrame( filter, 2 ); return true; } static bool multi_comprehension_generic_single_loop( VMachine* vm ) { // STACK LOCAL : // 0 - Global counter // 1 - GCPointer of the sequence // 2 - filter // 3 - iterator or callable // get the element Item* src = vm->local(3); if ( src->isOob() ) { // it's a callable -- we must call it. // change the next loop vm->returnHandler( &multi_comprehension_generic_single_loop_post_call ); vm->callFrame( *src, 0 ); } else { Iterator* iter = dyncast( src->asGCPointer() ); if ( iter->hasCurrent() ) { Item item = iter->getCurrent(); iter->next(); // call the filter -- we know we have it or we'd be in the simpler get all case Item filter = *vm->local(2); vm->returnHandler( &multi_comprehension_generic_single_loop_post_filter ); vm->pushParam( item ); vm->pushParam( vm->self() ); vm->callFrame( filter, 2 ); } else { // we're done; we must discard this work-in-progress vm->retval( vm->self() ); return false; } } // continue return true; } static bool multi_comprehension_callable_multiple_loop_next( VMachine* vm ) { uint32 ssize = vm->currentFrame()->stackSize(); Item* array = vm->local( ssize-1 ); CoreArray* ca = array->asArray(); int64 current = vm->local(0)->asInteger(); if( vm->regA().isOob() && vm->regA().isInteger() && vm->regA().asInteger() == 0 ) { // we're done with this callable. We must store the result in the right place vm->local( current + 3 )->setGCPointer( new GarbagePointer2( new Iterator( &ca->items()) ) ); vm->local(0)->setInteger( current+1 ); vm->returnHandler( multi_comprehension_callable_multiple_loop ); } else { ca->append( vm->regA().isString() ? new CoreString( *vm->regA().asString() ) : vm->regA() ); vm->callFrame( *vm->local(current+3), 0 ); } return true; } static void multi_comprehension_generate_all( VMachine* vm ) { uint32 ssize = vm->currentFrame()->stackSize()-4; Sequence* self = dyncast(vm->local(1)->asGCPointer()); while( true ) { // create the object to be added. CoreArray* cret = new CoreArray( ssize ); for( uint32 elem = 0; elem < ssize; ++elem ) { Iterator* seq = dyncast(vm->local(elem+3)->asGCPointer()); if( ! seq->hasCurrent() ) return; cret->append( seq->getCurrent().isString() ? new CoreString( *seq->getCurrent().asString() ) : seq->getCurrent() ); } // append it self->append( cret ); // advance uint32 pos = ssize; while( pos > 0 ) { Iterator* seq = dyncast(vm->local(pos-1+3)->asGCPointer()); // can we advance? if( seq->next() ) break; //--- no? reset this element, seq->goTop(); //--- and advance the previous element. --pos; } // did we reset the topmost element? if ( pos == 0) break; } vm->retval( vm->self() ); } static bool multi_comprehension_callable_multiple_loop( VMachine* vm ) { uint32 ssize = vm->currentFrame()->stackSize()-4; int64 current = vm->local(0)->asInteger(); while( (! vm->local(current+3)->isOob()) && current < ssize ) { ++current; } if( current == ssize ) { // No filter ? if( vm->local(2)->isNil() ) { multi_comprehension_generate_all( vm ); return false; } // we have run all the runnable generators. Now it's time to pack this up vm->returnHandler( multi_comprehension_filtered_loop ); // it's useless to wait -- also call it now return multi_comprehension_filtered_loop( vm ); } Item callable = *vm->local(current+3); // prepare for the next loop vm->local(0)->setInteger( current ); // ready to accept the reutrn of the function vm->local( ssize + 3 )->setArray( new CoreArray ); vm->returnHandler( multi_comprehension_callable_multiple_loop_next ); // call it vm->callFrame( callable, 0 ); return true; } static bool multi_comprehension_first_loop( VMachine* vm ) { // STACK LOCAL : // 0 - Global counter // 1 - GCPointer of the sequence // 2 - filter // 3 - first comp source // 4 - second cmp source // N-3 - ... nth source. uint32 ssize = vm->currentFrame()->stackSize(); if ( ssize < 4 ) return false; uint32 sources = ssize - 3; if( sources == 1 && vm->local(2)->isNil() ) { // we can use the simplified single comprehension system. return comp_get_all_items( vm, *vm->local(3) ); } bool hasCallable = false; // No luck; let's proceed with next loop // we must transform all the sources in their iterator, // For callable elements, we just set OOB. for ( uint32 nSrc = 0; nSrc < sources; nSrc ++ ) { Item* src = vm->local( 3 + nSrc ); if( src->isCallable() ) { src->setOob(true); hasCallable = true; } else { src->setOob(false); // just in case if ( src->isRange() ) { // the iterator will keep alive the range sequence as long as it exists. src->setGCPointer( new GarbagePointer2( new Iterator( new RangeSeq( *src->asRange() ) ) ) ); } else if ( src->isArray() ) { src->setGCPointer( new GarbagePointer2( new Iterator( &src->asArray()->items() ) ) ); } else if ( (src->isObject() && src->asObjectSafe()->getSequence() ) ) { src->setGCPointer( new GarbagePointer2( new Iterator( src->asObjectSafe()->getSequence() ) ) ) ; } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .origin( e_orig_runtime ) .extra( "A|C|R|Sequence, [C]" ) ); } } } if( sources == 1 ) { vm->returnHandler( &multi_comprehension_generic_single_loop ); // it's useless to wait -- also call it now return multi_comprehension_generic_single_loop( vm ); } else { // Todo: add an empty copy of the sequence in self instead. vm->pushParam( Item() ); if( ! hasCallable ) { // No filter ? if( vm->local(2)->isNil() ) { multi_comprehension_generate_all( vm ); return false; } vm->returnHandler( &multi_comprehension_filtered_loop ); // it's useless to wait -- also call it now return multi_comprehension_filtered_loop( vm ); } else { vm->returnHandler( multi_comprehension_callable_multiple_loop ); // it's useless to wait -- also call it now return multi_comprehension_callable_multiple_loop( vm ); } } } bool Sequence::comprehension_start( VMachine* vm, const Item& self, const Item& filter ) { if( ! (filter.isNil() || filter.isCallable()) ) { throw new ParamError( ErrorParam( e_param_type, __LINE__ ) .origin( e_orig_runtime ) .extra( "filter" ) ); } // local copy before changing the stack. Item copyFilter = filter; Item selfCpy = self; // Ask for a new stack frame, with immediate invocation of the return frame. vm->invokeReturnFrame(multi_comprehension_first_loop); // prepare data stub vm->addLocals( 3 ); vm->self() = selfCpy; *vm->local(0) = (int64) 0; // global counter vm->local(1)->setGCPointer( new GarbagePointer2( this ) ); *vm->local(2) = filter; // filter (may be nil) return true; } void Sequence::gcMark( uint32 gen ) { if ( m_owner != 0 && m_owner->mark() != gen ) m_owner->gcMark( gen ); } void Sequence::invalidateAllIters() { while( m_iterList != 0 ) { m_iterList->invalidate(); m_iterList = m_iterList->nextIter(); } } void Sequence::invalidateAnyOtherIter( Iterator* iter ) { // is the iterator really in our list? bool foundMe = false; while( m_iterList != 0 ) { if ( m_iterList != iter ) { m_iterList->invalidate(); } else foundMe = true; m_iterList = m_iterList->nextIter(); } //... then save it and set it at the only iterator. fassert( foundMe ); // actually, it should be... if ( foundMe ) { iter->nextIter( 0 ); m_iterList = iter; } } void Sequence::getIterator( Iterator& tgt, bool tail ) const { tgt.sequence( const_cast(this) ); if ( &tgt != m_iterList ) tgt.nextIter( m_iterList ); m_iterList = &tgt; } void Sequence::copyIterator( Iterator& tgt, const Iterator& source ) const { tgt.sequence( const_cast(this) ); tgt.nextIter( m_iterList ); m_iterList = &tgt; } void Sequence::disposeIterator( Iterator& tgt ) const { Iterator *iter = m_iterList; Iterator *piter = 0; while( iter != 0 ) { if ( iter == &tgt ) { // found! if ( piter == 0) { // was the first one! m_iterList = iter->nextIter(); } else { piter->nextIter( iter->nextIter() ); } iter->invalidate(); return; } piter = iter; iter = iter->nextIter(); } // we should have found an iterator of ours fassert( false ); } void Sequence::invalidateIteratorOnCriterion() const { Iterator *iter = m_iterList; Iterator *piter = 0; while( iter != 0 ) { if ( onCriterion( iter ) ) { // found! if ( piter == 0) { // was the first one! m_iterList = iter->nextIter(); } else { piter->nextIter( iter->nextIter() ); } iter->invalidate(); Iterator* old = iter; iter = iter->nextIter(); old->nextIter( 0 ); continue; } piter = iter; iter = iter->nextIter(); } } } /* end of sequence.cpp */ engine/service.cpp000066400000000000000000000012341176363201700144750ustar00rootroot00000000000000/* FALCON - Falcon advanced simple text evaluator. FILE: service.cpp Service virtual function implementation. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #define FALCON_EXPORT_SERVICE #include #include #include namespace Falcon { Service::Service( const String & name ): m_name(name) {} Service::~Service() {} } /* end of service.cpp */ engine/signals_posix.cpp000066400000000000000000000077041176363201700157270ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: signals_posix.cpp POSIX-specific signal handling ------------------------------------------------------------------- Author: Jan Dvorak Begin: Fri, 12 Feb 2010 11:23:13 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #if !defined(__linux__) || !defined(__sun) # define __BSD__ #endif namespace Falcon { SignalReceiver *signalReceiver = 0; void signal_handler(int signum, siginfo_t *siginfo, void *ctx) { signalReceiver->deliver(signum, siginfo); } SignalReceiver::SignalReceiver(VMachine *targetVM): m_thread(0), m_shallRun(false) { m_targetVM = targetVM; } SignalReceiver::~SignalReceiver() { stop(); } void SignalReceiver::start() { if (0 != m_thread) return; m_thread = new SysThread(this); m_shallRun = true; m_thread->start(); } void SignalReceiver::stop() { if (0 == m_thread) return; void *dummy; m_shallRun = false; wakeup(); m_thread->join(dummy); m_thread = 0; } void *SignalReceiver::run() { sigset_t sigset; /* * Allow all signals in this thread. */ sigemptyset(&sigset); sigaddset(&sigset, SIGCONT); pthread_sigmask(SIG_SETMASK, &sigset, NULL); /* Wait... and wait... and wait. */ while (m_shallRun) { #ifndef __APPLE__ sigwaitinfo(&sigset, NULL); #else sigwait(&sigset, NULL); #endif } return 0; } bool SignalReceiver::trap(int signum) { struct sigaction sigaction; sigaction.sa_sigaction = signal_handler; sigfillset(&sigaction.sa_mask); sigaction.sa_flags = SA_SIGINFO; return 0 == ::sigaction(signum, &sigaction, 0); } bool SignalReceiver::reset(int signum) { return 0 == ::signal(signum, SIG_DFL); } void SignalReceiver::deliver(int signum, siginfo_t *siginfo) { VMMessage *m = new VMMessage("os.signal"); LinearDict *ld = new LinearDict(); CoreDict *cd = new CoreDict(ld); ld->put(new CoreString("signo"), (int32)signum); ld->put(new CoreString("errno"), (int32)siginfo->si_errno); ld->put(new CoreString("code"), (int32)siginfo->si_code); #ifndef __APPLE__ if (SIGCHLD == signum || (signum >= SIGRTMIN && signum <= SIGRTMAX)) { ld->put(new CoreString("pid"), (int32)siginfo->si_pid); ld->put(new CoreString("uid"), (int32)siginfo->si_uid); #ifndef __BSD__ ld->put(new CoreString("utime"), (int64)siginfo->si_utime); ld->put(new CoreString("stime"), (int64)siginfo->si_stime); #endif } if (signum >= SIGRTMIN && signum <= SIGRTMAX) { #ifdef _POSIX_SOURCE ld->put(new CoreString("overrun"), (int32)siginfo->si_overrun); ld->put(new CoreString("timerid"), (int32)siginfo->si_timerid); ld->put(new CoreString("int"), (int32)siginfo->si_int); #endif } if (SIGCHLD == signum) { ld->put(new CoreString("status"), (int32)siginfo->si_status); } #ifndef __BSD__ if (SIGPOLL == signum) { ld->put(new CoreString("band"), (int32)siginfo->si_band); ld->put(new CoreString("fd"), (int32)siginfo->si_fd); } #endif #else /* MACOSX */ ld->put(new CoreString("pid"), (int32)siginfo->si_pid); ld->put(new CoreString("uid"), (int32)siginfo->si_uid); ld->put(new CoreString("status"), (int32)siginfo->si_status); ld->put(new CoreString("band"), (int32)siginfo->si_band); #endif cd->bless(true); m->addParam(SafeItem(cd)); m_targetVM->postMessage(m); } void SignalReceiver::wakeup(void) { kill(getpid(), SIGCONT); } void BlockSignals() { sigset_t sigset; sigfillset(&sigset); pthread_sigmask(SIG_SETMASK, &sigset, NULL); } void UnblockSignals() { sigset_t sigset; sigemptyset(&sigset); pthread_sigmask(SIG_SETMASK, &sigset, NULL); } } // vim: et ts=3 sw=3 : /* end of signals_posix.cpp */ engine/signals_win.cpp000066400000000000000000000011321176363201700153470ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: signals_win.cpp Windows-specific signal handling ------------------------------------------------------------------- Author: Jan Dvorak Begin: 2010-02-19 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include namespace Falcon { void BlockSignals() { /* no-op */ } void UnblockSignals() { /* no-op */ } } // vim: et ts=3 sw=3 : /* end of signals_win.cpp */ engine/smba.cpp000066400000000000000000000130031176363201700137540ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: smba.cpp Small Memory Block Allocator. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 13 Dec 2008 14:42:24 +0100 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include namespace Falcon { int32 s_pageSize; int32 s_pageMask; #if 0 SmallMemBlockAlloc SMBA; SmallMemBlockAlloc::SmallMemBlockAlloc() { for( int i = 0; i < page_list_size; i ++ ) { PAGE_HEADER* p = newPage( 8 << i ); page_lists[i] = p; page_free_lists[i] = 0; } m_mtx = new Mutex; // we don't really expect this to change. s_pageSize = Heap.pageSize(); // create the page mask, which is all bit off past the page size. s_pageMask = 0; for ( uint32 bit = 0; bit < sizeof( int32 ); bit++ ) { int32 mask = 1 << bit; if( mask >= s_pageSize ) s_pageMask |= mask; } } SmallMemBlockAlloc::PAGE_HEADER* SmallMemBlockAlloc::newPage( int blockSize ) { PAGE_HEADER* p = (PAGE_HEADER*) Heap.allocPage(); p->next = 0; p->prev = 0; p->firstFree = p + (s_pageSize - blockSize); p->allocated = 0; if ( blockSize <= 8 ) { p->pageArea = 0; } else if ( blockSize <= 16 ) { p->pageArea = 1; } else if ( blockSize <= 32 ) { p->pageArea = 2; } else if( blockSize <= 64 ) { p->pageArea = 3; } else fassert( false ); return p; } SmallMemBlockAlloc::~SmallMemBlockAlloc() { delete m_mtx; for( int i = 0; i < page_list_size; i ++ ) { PAGE_HEADER* p = page_lists[i]; while( p != 0 ) { PAGE_HEADER* next = p->next; Heap.freePage( p ); p = next; } } } void* SmallMemBlockAlloc::alloc( unsigned int bytes ) { register int index; if ( bytes <= 8 ) { index = 0; } else if ( bytes <= 16 ) { index = 1; } else if ( bytes <= 32 ) { index = 2; } else if( bytes <= 64 ) { index = 3; } else { return 0; } // got a free node in the list? m_mtx->lock(); void* freeNode = page_free_lists[index]; if( freeNode != 0 ) { // cool, allocate it PAGE_HEADER* pageNode = (PAGE_HEADER*) ( ((int)freeNode) & s_pageMask ); // advance our record page_free_lists[index] = *( (void**) freeNode ); // and account the page pageNode->allocated++; m_mtx->unlock(); } else { register int size = (8 << index); // see if we have some space on the last page. PAGE_HEADER* pageNode = page_lists[index]; if ( ((uint32)pageNode->firstFree - (uint32)pageNode) > sizeof( PAGE_HEADER ) + size ) { // yay, we got some space freeNode = pageNode->firstFree; // go back on the page where next free data is located. pageNode->firstFree = (void*) ( ((int)pageNode->firstFree) - size ); // account pageNode->allocated++; m_mtx->unlock(); } else { // no luck, we need a new page. // We're doing a lot of system job here. Free the mutex. m_mtx->unlock(); pageNode = newPage( size ); freeNode = pageNode->firstFree; // go back on the page where next free data is located. pageNode->firstFree = (void*) ( ((int)pageNode->firstFree) - size ); // account pageNode->allocated++; // Reachieve the lock to store the page we just bought. // It doesn't matter if some space has become available by now, // we just got some extra stuff to be used in future. // But we can't relay on cached values. m_mtx->lock(); pageNode->prev = page_lists[index]; page_lists[index]->next = pageNode; page_lists[index] = pageNode; m_mtx->unlock(); } } // we got a valid node and the mutex unlocked here. return freeNode; } void SmallMemBlockAlloc::free( void* bytes ) { // determine the pagine from which this block // is coming from. PAGE_HEADER* pageNode = (PAGE_HEADER*) ( ((int)bytes) & s_pageMask ); bool bRelease = false; // account for the data being free m_mtx->lock(); if( --pageNode->allocated == 0 ) { // disengage the page from the list, unless it's the only one. if( pageNode->next != 0 ) { // we'll release this page when we're out from the mutex bRelease = true; pageNode->next->prev = pageNode->prev; // can't be top page if next != 0 } if ( pageNode->prev != 0 ) { // we'll release this page when we're out from the mutex bRelease = true; if ( pageNode->next == 0 ) { // the first page. We must update our pointer pageNode->prev->next = 0; page_lists[ pageNode->pageArea ] = pageNode->prev; } else { // just update the previous node pageNode->prev->next = pageNode->next; } } } m_mtx->unlock(); // eventually free the page if( bRelease ) Heap.freePage( pageNode ); } #endif } /* end of smba.h */ engine/src_lexer.cpp000066400000000000000000001446451176363201700150410ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: src_lexer.cpp Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab ago 26 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Short description */ #include #include #include #include #include #include #include #include // Token internally used #define ITOK_SEMICOMMA 0x7FFFFF01 #define VALUE ((YYSTYPE*)value()) // Get the bison generated token defs #include "src_parser.hpp" namespace Falcon { SrcLexer::SrcLexer( Compiler *comp ): m_value(0), m_line( 1 ), m_previousLine( 1 ), m_character( 0 ), m_prevStat(0), m_firstEq( false ), m_done( false ), m_addEol( false ), m_lineFilled( false ), m_bIsDirectiveLine(false), m_incremental( false ), m_lineContContext( false ), m_graphAgain( false ), m_chrEndString(0), m_in( 0 ), m_compiler( comp ), m_state( e_line ), m_mode( t_mNormal ), m_bParsingFtd(false), m_bWasntEmpty(false), m_topCtx(0) {} SrcLexer::~SrcLexer() { reset(); } void SrcLexer::reset() { resetContexts(); m_addEol = false; // revert what's done by resetContext m_prevStat = 0; m_character = 0; m_state = e_line; m_bIsDirectiveLine = false; m_bWasntEmpty = false; m_whiteLead = ""; while( ! m_streams.empty() ) { Stream *s = (Stream *) m_streams.back(); m_streams.popBack(); // all the streams except the first are to be deleted. if ( !m_streams.empty() ) delete s; } } void SrcLexer::input( Stream *i ) { m_in = i; m_streams.pushBack(i); m_streamLines.pushBack( (uint32) m_line ); m_done = false; m_addEol = false; m_lineFilled = false; } int SrcLexer::lex() { switch( m_mode ) { case t_mNormal: return lex_normal(); case t_mOutscape: return lex_outscape(); case t_mEval: return lex_eval(); } return 0; } int SrcLexer::lex_outscape() { enum { e_leadIn, e_normal, e_esc1, e_esc2, e_escF, e_escA, e_escL } state; // clear string m_string.size( 0 ); // in outscape mode, everything up to an escape (or at EOF) is a "fast print" statement. uint32 chr; state = e_leadIn; m_state = e_line; m_bIsDirectiveLine = false; while( m_mode == t_mOutscape && m_in->get( chr ) ) { if ( chr == '\n' ) { m_previousLine = m_line; m_line++; m_character = 0; m_bIsDirectiveLine = false; if ( state == e_normal ) state = e_leadIn; } else { m_character++; } switch( state ) { case e_leadIn: if( isWhiteSpace(chr) ) { m_whiteLead.append( chr ); break; } else state = e_normal; // fall through, don't break case e_normal: if ( chr == '<' ) { state = e_esc1; } else { if ( m_whiteLead.size() > 0 ) { m_string.append( m_whiteLead ); m_whiteLead.size(0); } m_string.append( chr ); } break; case e_esc1: if ( chr == '?' ) state = e_esc2; else { if ( m_whiteLead.size() > 0 ) { m_string.append( m_whiteLead ); m_whiteLead.size(0); } m_string.append( '<' ); m_string.append( chr ); state = e_normal; } break; case e_esc2: if ( chr == '=' ) { // reset lead chars and eventually remove last \n m_whiteLead.size(0); // but don't remove extra EOL; we want another line. m_bWasntEmpty = true; // we enter now in the eval mode m_mode = t_mEval; // and break from the loop so to return the string to print. break; } else if ( isWhiteOrEOL(chr) ) { // reset lead chars and eventually remove last \n m_whiteLead.size(0); m_bWasntEmpty = m_string.size() > 0; if ( m_string.size() > 0 && m_string.getCharAt( m_string.length() -1 ) == '\n' ) m_string.size( m_string.size() - m_string.manipulator()->charSize() ); // we enter now the normal mode; we start to consider a standard program. m_mode = t_mNormal; // and break from the loop so to return the string to print. break; } else if ( chr == 'f' ) { state = e_escF; } else { if ( m_whiteLead.size() > 0 ) { m_string.append( m_whiteLead ); m_whiteLead.size(0); } state = e_normal; m_string.append( '<' ); m_string.append( '?' ); m_string.append( chr ); } break; case e_escF: if ( chr == 'a' ) { state = e_escA; } else { if ( m_whiteLead.size() > 0 ) { m_string.append( m_whiteLead ); m_whiteLead.size(0); } state = e_normal; m_string.append( '<' ); m_string.append( '?' ); m_string.append( 'f' ); m_string.append( chr ); } break; case e_escA: if ( chr == 'l' ) { state = e_escL; } else { if ( m_whiteLead.size() > 0 ) { m_string.append( m_whiteLead ); m_whiteLead.size(0); } state = e_normal; m_string.append( '<' ); m_string.append( '?' ); m_string.append( 'f' ); m_string.append( 'a' ); m_string.append( chr ); } break; case e_escL: if ( isWhiteOrEOL(chr) ) { // reset lead chars and eventually remove last \n m_whiteLead.size(0); m_bWasntEmpty = m_string.size() > 0; if ( m_string.size() > 0 && m_string.getCharAt( m_string.length() -1 ) == '\n' ) m_string.size( m_string.size() - m_string.manipulator()->charSize() ); // we enter now the normal mode; we start to consider a standard program. m_mode = t_mNormal; // and break from the loop so to return the string to print. break; } else { if ( m_whiteLead.size() > 0 ) { m_string.append( m_whiteLead ); m_whiteLead.size(0); } state = e_normal; m_string.append( '<' ); m_string.append( '?' ); m_string.append( 'f' ); m_string.append( 'a' ); m_string.append( 'l' ); m_string.append( chr ); } break; } } if( m_string.size() != 0 ) { VALUE->stringp = m_compiler->addString( m_string ); m_string.size( 0 ); m_string.exported( false ); return OUTER_STRING; } // else proceed with normal evaluation as we can't return nothing if( m_in->good() ) { switch( m_mode ) { case t_mNormal: return lex_normal(); case t_mEval: return lex_eval(); case t_mOutscape: return 0; } } return 0; } int SrcLexer::lex_eval() { // prepare for a normal scan m_mode = t_mNormal; // returns a SHR, which will be interpreted as a fast print return SHR; } int SrcLexer::lex_normal() { // generate an extra eol? if ( m_addEol ) { m_addEol = false; // ignore in incremental mode with open contexts if ( ! (m_done && incremental() && hasOpenContexts()) ) { m_lineFilled = false; if ( ! inParCtx() ) { m_bIsDirectiveLine = false; return EOL; } } } if( m_graphAgain ) { m_graphAgain = false; return CLOSE_GRAPH; } if ( m_done ) { // raise error if there is some lexer context open // in incremental mode ignore completely end of file errors if ( ! m_incremental ) checkContexts(); return 0; } // check for shell directive if ( m_line == 1 && m_character == 0 ) { uint32 ch1, ch2; if( m_in->get( ch1 ) ) { if( m_in->get( ch2 ) ) { if ( ch1 == '#' && ch2 == '!' ) { while( ch1 != '\n' ) if( ! m_in->get( ch1 ) ) { checkContexts(); return 0; } m_line++; } else { m_in->unget( ch2 ); m_in->unget( ch1 ); } } else m_in->unget( ch1 ); } else { checkContexts(); return 0; } } // reset previous token m_lineContContext = false; m_string.size(0); m_string.manipulator( &csh::handler_buffer ); String tempString; uint32 chr; bool next_loop = true; while( next_loop ) { next_loop = m_in->get( chr ); if ( ! next_loop ) { // if this is the last stream, ask also an extra EOL m_streams.popBack(); if ( m_streams.empty() ) { // fake an empty terminator at the end of input. chr = '\n'; if ( ! m_incremental || ! hasOpenContexts() ) m_addEol = true; m_done = true; } else { delete m_in; // all the streams except first are to be disposed. m_in = (Stream *) m_streams.back(); m_line = (uint32)(int64) m_streamLines.back(); m_streamLines.popBack(); m_previousLine = m_line-1; next_loop = true; continue; } } m_character++; // Totally ignore '\r' if ( chr == '\r' ) continue; switch ( m_state ) { case e_line: // in none status, we have to discard blanks and even ignore '\n'; // we enter in line or string status depending on what we find. if( ! isWhiteSpace( chr ) ) { m_previousLine = m_line; if( m_bIsDirectiveLine && chr == '.' ) { // just ignore it m_string.append( chr ); break; } // whitespaces and '\n' can't follow a valid symbol, // as since they are token limiters, and they would be read // ahead after token begin, valid symbols and token has already // been returned. int token = state_line( chr ); if ( token != 0 ) { // we have a token in this line //m_lineFilled = true; return token; } } break; case e_symbol: m_lineFilled = true; if ( isTokenLimit( chr ) ) { // end of symbol // special x" notation? if( m_string.size() == 1 && (chr == '"' || chr == '\'')) { // recognize only the i" for now if ( m_string[0] == 'i' ) { uint32 nextChr; // we'll begin to read a string. if ( readAhead( nextChr ) && nextChr == '\n' ) { m_mlString = true; m_line++; m_character = 0; m_in->discardReadAhead( 1 ); m_state = chr == '"' ? e_stringRunning : e_litString; } else { m_state = chr == '"' ? e_string : e_litString; } pushContext( ct_string, m_line ); m_string.size(0); // remove first char. m_string.exported( true ); m_chrEndString = chr; //... up to the matching " break; } // else, just let it through. } // unless we have a dot in a load directive or namespace if( chr == '.' ) { if ( m_bIsDirectiveLine || ( m_string.size() != 0 && m_compiler->isNamespace( m_string )) ) { // just ignore it m_string.append( chr ); break; } } // push this chr back; we want to read it again in line state if( ! isWhiteSpace( chr ) ) // save a loop m_in->unget( chr ); m_state = e_line; // it may be a named token int token = checkLimitedTokens(); if ( token != 0 ) { return token; } // internally parsed? else if ( m_string.size() == 0 ) { // reset m_state = e_line; continue; } // see if we have named constants const Value *cval = m_compiler->getConstant( m_string ); if ( cval != 0 ) { switch( cval->type() ) { case Falcon::Value::t_nil: return NIL; case Falcon::Value::t_imm_integer: VALUE->integer = cval->asInteger(); return INTNUM; case Falcon::Value::t_imm_num: VALUE->numeric = cval->asNumeric(); return DBLNUM; case Falcon::Value::t_imm_string: VALUE->stringp = cval->asString(); return STRING; default: return NIL; // signal error? } } // we have a symbol VALUE->stringp = m_compiler->addString( m_string ); return SYMBOL; } else m_string.append( chr ); break; case e_operator: { // have we a token? int token = checkUnlimitedTokens( chr ); if ( token > 0 ) { // great, we have a token m_lineFilled = true; m_state = e_line; m_in->unget( chr ); return token; } else if ( token < 0 || m_string.length() == 3 ) { // We have aknowledged this can't be a valid token. m_in->unget( chr ); m_state = e_line; m_string = m_string.subString( 0, 1 ); m_compiler->raiseError( e_inv_token, "'" + m_string + "'", m_line ); // do not return. } // if we have been switched to another state, we have aknowledged something if ( m_state != e_operator ) { m_in->unget( chr ); m_string.size( 0 ); } else m_string.append( chr ); } break; case e_eolComment: if ( chr == '\n' ) { m_previousLine = m_line; m_line ++; m_character = 0; m_bIsDirectiveLine = false; m_state = e_line; // a real EOL has been provided here. if ( m_state == e_line && ! inParCtx() ) { m_bIsDirectiveLine = false; if ( m_lineFilled ) { m_lineFilled = false; return EOL; } } } break; case e_blockComment: if ( chr == '\n' ) { // previous line stays the same m_line ++; m_character = 0; m_bIsDirectiveLine = false; } else if ( chr == '*' ) { uint32 nextChr; readAhead( nextChr ); if ( nextChr == '/' ) { m_in->discardReadAhead( nextChr ); m_state = e_line; } } break; case e_intNumber: m_lineFilled = true; if ( chr == '.' ) { // a method on a number? uint32 nextChr; if ( readAhead( nextChr ) && (nextChr < '0' || nextChr > '9') ) { // end m_in->unget( chr ); int64 retval; if ( ! m_string.parseInt( retval ) ) m_compiler->raiseError( e_inv_num_format, m_line ); VALUE->integer = retval; m_state = e_line; return INTNUM; } // no. m_string.append( chr ); m_state = e_floatNumber; } else if ( chr == 'e' ) { m_state = e_floatNumber_e; m_string.append( chr ); } else if ( chr >= '0' && chr <= '9' ) { m_string.append( chr ); } else if ( chr != '_' ) { // end m_in->unget( chr ); int64 retval; if ( ! m_string.parseInt( retval ) ) m_compiler->raiseError( e_inv_num_format, m_line ); VALUE->integer = retval; m_state = e_line; return INTNUM; } break; case e_floatNumber: m_lineFilled = true; if ( chr == 'e' ) { m_state = e_floatNumber_e; m_string.append( chr ); } else if ( chr >= '0' && chr <= '9' ) { m_string.append( chr ); } else if ( chr != '_' ) { // end m_in->unget( chr ); // "0." ? if( m_string == "0." ) { m_in->unget( '.' ); VALUE->integer = 0; m_state = e_line; return INTNUM; } numeric retval; if ( ! m_string.parseDouble( retval ) ) m_compiler->raiseError( e_inv_num_format, m_line ); VALUE->numeric = retval; m_state = e_line; return DBLNUM; } break; case e_floatNumber_e: m_lineFilled = true; if ( (chr < '0' || chr > '9' ) && chr != '+' && chr != '-' ) { m_in->unget( chr ); m_compiler->raiseError( e_inv_num_format, m_line ); m_state = e_line; VALUE->numeric = 0.0; return DBLNUM; } m_state = e_floatNumber_e1; m_string.append( chr ); break; case e_floatNumber_e1: m_lineFilled = true; if ( chr < '0' || chr > '9' ) { // end m_in->unget( chr ); numeric retval; if ( ! m_string.parseDouble( retval ) ) m_compiler->raiseError( e_inv_num_format, m_line ); m_state = e_line; VALUE->numeric = retval; return DBLNUM; } m_string.append( chr ); break; case e_zeroNumber: m_lineFilled = true; if( chr == 'x' || chr == 'X' ) { m_string.size( 0 ); m_state = e_hexNumber; } else if ( chr == 'b' || chr == 'B' ) { m_string.size( 0 ); m_state = e_binNumber; } else if ( chr == 'c' || chr == 'C' ) { m_string.size( 0 ); m_state = e_octNumber; } else if ( chr >= '0' && chr <= '7' ) { m_string.size( 0 ); m_string.append( chr ); m_state = e_octNumber; } else if ( chr == '.' ) { m_string = "0."; m_state = e_floatNumber; } else if ( isTokenLimit( chr ) ) { m_state = e_line; VALUE->integer = 0; if( ! isWhiteSpace( chr ) ) m_in->unget( chr ); return INTNUM; } else { m_compiler->raiseError( e_inv_num_format, m_line ); m_state = e_line; } break; case e_octNumber: m_lineFilled = true; if ( chr >= '0' && chr <= '7' ) { m_string.append( chr ); } else if ( chr != '_' ) { m_in->unget( chr ); uint64 retval; if ( ! m_string.parseOctal( retval ) ) m_compiler->raiseError( e_inv_num_format, m_line ); VALUE->integer = retval; m_state = e_line; return INTNUM; } break; case e_binNumber: m_lineFilled = true; if ( chr == '0' || chr == '1' ) m_string.append( chr ); else if ( chr != '_' ) { m_in->unget( chr ); uint64 retval; if ( ! m_string.parseBin( retval ) ) m_compiler->raiseError( e_inv_num_format, m_line ); VALUE->integer = retval; m_state = e_line; return INTNUM; } break; case e_hexNumber: m_lineFilled = true; if ( (chr >= '0' && chr <= '9') || (chr >= 'a' && chr <= 'f') || (chr >= 'A' && chr <= 'F') ) { m_string.append( chr ); } else if ( chr != '_' ) { m_in->unget( chr ); uint64 retval; if ( ! m_string.parseHex( retval ) ) m_compiler->raiseError( e_inv_num_format, m_line ); VALUE->integer = retval; m_state = e_line; return INTNUM; } break; case e_litString: m_lineFilled = true; if ( chr == '\n' ) { if( ! m_mlString ) { m_compiler->raiseError( e_nl_in_lit, m_line ); m_lineFilled = false; m_character = 0; m_bIsDirectiveLine = false; m_compiler->raiseError( e_nl_in_lit, m_previousLine ); m_state = e_line; popContext(); } m_string.append( chr ); m_previousLine = m_line; m_line++; } else if ( chr == '\'' ) { uint32 nextChar; if ( readAhead( nextChar ) && nextChar == '\'' ) { m_string.append( '\'' ); m_in->discardReadAhead(1); } else { m_state = e_line; popContext(); VALUE->stringp = m_compiler->addString( m_string ); m_string.exported( false ); return STRING; } } else m_string.append( chr ); break; case e_string: m_lineFilled = true; // an escape ? if ( chr == '\\' ) { uint32 nextChar; readAhead( nextChar ); switch ( nextChar ) { case '\\': nextChar = '\\'; break; case 'n': nextChar = '\n'; break; case 'b': nextChar = '\b'; break; case 't': nextChar = '\t'; break; case 'r': nextChar = '\r'; break; case 's': nextChar = 1; break; case 'x': case 'X': nextChar = 1; tempString.size(0); m_state = e_stringHex; break; case 'B': nextChar = 1; tempString.size(0); m_state = e_stringBin; break; case '0': case 'c': case 'C': nextChar = 1; tempString.size(0); m_state = e_stringOctal; break; // when none of the above, just keep nextchar as is. } if ( nextChar != 0 ) { m_in->discardReadAhead( 1 ); if ( nextChar != 1 ) m_string.append( nextChar ); } } else if ( chr == '\n' ) { if( ! m_mlString ) { m_compiler->raiseError( e_nl_in_lit, m_line ); m_lineFilled = false; m_bIsDirectiveLine = false; m_compiler->raiseError( e_nl_in_lit, m_previousLine ); m_state = e_line; popContext(); } else m_state = e_stringRunning; m_previousLine = m_line; m_line++; m_character = 0; m_bIsDirectiveLine = false; } else if ( chr == m_chrEndString ) { popContext(); m_state = e_line; VALUE->stringp = m_compiler->addString( m_string ); m_string.exported( false ); return STRING; } else m_string.append( chr ); break; case e_stringRunning: if ( ! isWhiteSpace( chr ) ) { if ( chr == '\n' ) { m_previousLine = m_line; m_line++; m_character = 0; } else if ( chr == m_chrEndString ) { popContext(); m_state = e_line; VALUE->stringp = m_compiler->addString( m_string ); m_string.exported( false ); return STRING; } else { m_state = e_string; if ( m_string.size() != 0 ) m_string.append( ' ' ); m_in->unget( chr ); } } break; case e_stringHex: if ( (chr >= '0' && chr <= '9') || (chr >= 'a' && chr <= 'f') || (chr >= 'A' && chr <= 'F') ) { tempString.append( chr ); } else if ( chr != '_' ) { m_in->unget( chr ); uint64 retval; if ( ! tempString.parseHex( retval ) || retval > 0xFFFFFFFF ) m_compiler->raiseError( e_inv_esc_sequence, m_line ); m_string.append( (uint32) retval ); m_state = e_string; } break; case e_stringBin: if ( chr == '0' || chr == '1' ) { tempString.append( chr ); } else if ( chr != '_' ) { m_in->unget( chr ); uint64 retval; if ( ! tempString.parseBin( retval ) || retval > 0xFFFFFFFF ) m_compiler->raiseError( e_inv_esc_sequence, m_line ); m_string.append( (uint32) retval ); m_state = e_string; } break; case e_stringOctal: if ( (chr >= '0' && chr <= '7') ) { tempString.append( chr ); } else if ( chr != '_' ) { m_in->unget( chr ); uint64 retval; if ( ! tempString.parseOctal( retval ) || retval > 0xFFFFFFFF ) m_compiler->raiseError( e_inv_esc_sequence, m_line ); m_string.append( (uint32) retval ); m_state = e_string; } break; default: break; } } if ( ! m_incremental ) checkContexts(); return 0; } void SrcLexer::checkContexts() { t_contextType ct = currentContext(); if ( ct == ct_round ) m_compiler->raiseContextError( e_par_unbal, m_line, contextStart() ); else if ( ct == ct_square ) m_compiler->raiseContextError( e_square_unbal, m_line, contextStart() ); else if ( ct == ct_graph ) m_compiler->raiseContextError( e_graph_unbal, m_line, contextStart() ); else if ( ct == ct_string ) m_compiler->raiseContextError( e_unclosed_string, m_line, contextStart() ); } int SrcLexer::state_line( uint32 chr ) { if ( chr == '\n' ) { m_previousLine = m_line; m_line ++; m_character = 0; // a real EOL has been provided here. m_bIsDirectiveLine = false; if ( m_lineFilled && ! inParCtx() ) { m_lineFilled = false; return EOL; } } else if ( chr == '\\' ) { // don't return at next eol: uint32 nextChr; if ( ! readAhead( nextChr ) ) { // end of file; if we're in incremental mode, // declare the opening of a temporary context if ( m_incremental ) { m_lineContContext = true; } } else if ( nextChr == '\n' ) { m_previousLine = m_line; m_line ++; m_character = 0; m_in->discardReadAhead( 1 ); } else if ( nextChr == '\\' ) { m_in->discardReadAhead( 1 ); parseMacroCall(); } else if ( nextChr == '[' ) { int startline = m_line; m_in->discardReadAhead( 1 ); // create meta data up to \] String temp; temp.reserve( 512 ); uint32 chr; bool waiting = false; while( m_in->get( chr ) ) { if ( chr == '\\' ) { waiting = true; } else { if ( waiting ) { // done? if ( chr == ']' ) break; temp.append( '\\' ); waiting = false; } if ( chr == '\n' ) { m_previousLine = m_line; m_line ++; m_character = 0; waiting = false; } temp.append( chr ); } } temp.append( '\n' ); m_compiler->metaCompile( temp, startline ); } } else if ( chr < 0x20 ) { // only invalid characters are in this range. String value; value.writeNumberHex( chr, true ); m_compiler->raiseError( e_charRange, value, m_line ); m_state = e_eolComment; // ignore the rest of the line } else if ( chr == '"' ) { uint32 nextChr; // we'll begin to read a string. if ( readAhead( nextChr ) && nextChr == '\n' ) { m_mlString = true; m_line++; m_character = 0; m_in->discardReadAhead( 1 ); m_state = e_stringRunning; } else { m_mlString = false; m_state = e_string; } pushContext( ct_string, m_line ); m_chrEndString = '"'; //... up to the matching " } else if ( chr == 0x201C ) { uint32 nextChr; // we'll begin to read a string. if ( readAhead( nextChr ) && nextChr == '\n' ) { m_mlString = true; m_line++; m_character = 1; m_in->discardReadAhead( 1 ); } else m_mlString = false; // we'll begin to read a string. m_state = e_string; pushContext( ct_string, m_line ); m_chrEndString = 0x201D; //... up to the matching close quote } else if ( chr == 0x300C ) { uint32 nextChr; // we'll begin to read a string. if ( readAhead( nextChr ) && nextChr == '\n' ) { m_mlString = true; m_line++; m_character = 1; m_in->discardReadAhead( 1 ); } else m_mlString = false; // we'll begin to read a string. m_state = e_string; pushContext( ct_string, m_line ); m_chrEndString = 0x300D; //... up to the matching close japanese quote } else if ( chr == '\'' ) { uint32 nextChr; // we'll begin to read a string. if ( readAhead( nextChr ) && nextChr == '\n' ) { m_mlString = true; m_line++; m_character = 1; m_in->discardReadAhead( 1 ); } else m_mlString = false; // we'll begin to read a non escaped string pushContext( ct_string, m_line ); m_state = e_litString; } else if ( chr == '0' ) { // we'll begin to read a 0 based number. m_state = e_zeroNumber; } else if ( chr >= '1' && chr <= '9' ) { // reading a std number m_string.append( chr ); m_state = e_intNumber; } else { // store this character. m_string.append( chr ); // if it's an operator character, enter in operator mode. if ( isTokenLimit( chr ) ) m_state = e_operator; else m_state = e_symbol; } return 0; } int SrcLexer::checkUnlimitedTokens( uint32 nextChar ) { switch( m_string.length() ) { case 1: { uint32 chr = m_string.getCharAt( 0 ); if ( chr == ';' ) { m_bIsDirectiveLine = false; // but not first sym if ( m_lineFilled && ! inParCtx() ) { m_lineFilled = false; return EOL; } } else if ( chr == '+' && nextChar != '=' && nextChar != '+' ) return PLUS; else if ( chr == '-' && nextChar != '=' && nextChar != '-' ) return MINUS; else if ( chr == '*' && nextChar != '=' && nextChar != '*' ) return STAR; else if ( chr == '/' && nextChar != '=' && nextChar != '/' && nextChar != '*' ) return SLASH; else if ( chr == '%' && nextChar != '=' ) return PERCENT; else if ( chr == '&' && nextChar != '=' && nextChar != '&' ) return AMPER; else if ( chr == '~' && nextChar != '=' ) return TILDE; else if ( chr == '|' && nextChar != '=' && nextChar != '|' ) return VBAR; /* else if ( chr == '^' && nextChar != '=' && nextChar != '^' ) return CAP; */ /*else if ( chr == '!' && nextChar != '=' ) return BANG; */ else if ( chr == '$' ) { return DOLLAR; } else if ( chr == ':' ) { // but they don't reset first sym return COLON; } else if( chr == ',' ) return COMMA; else if( chr == ';' ) return ITOK_SEMICOMMA; else if ( chr == '.' && nextChar != '=' && nextChar != '[' && nextChar != '"' ) return DOT; else if ( chr == '?' ) { if ( ! parsingFtd() || nextChar != '>' ) return QUESTION; } else if ( chr == '>' && nextChar != '=' && nextChar != '>' ) return GT; else if ( chr == '<' && nextChar != '=' && nextChar != '<' ) return LT; else if ( chr == '=' && nextChar != '=' && nextChar != '>' ) { return OP_EQ; } else if ( chr == '(' || chr == 0xff08 ) { pushContext( ct_round, m_line ); return OPENPAR; } else if ( chr == ')' || chr == 0xff09 ) { if ( currentContext() != ct_round ) m_compiler->raiseError( e_par_close_unbal, m_line ); else { popContext(); return CLOSEPAR; } } else if ( chr == '[' ) { pushContext( ct_square, m_line ); return OPENSQUARE; } else if ( chr == ']' ) { if ( currentContext() != ct_square ) { m_compiler->raiseError( e_square_close_unbal, m_line ); } else { popContext(); return CLOSESQUARE; } } else if ( chr == '{' ) { pushContext( ct_graph, m_line ); return OPEN_GRAPH; } else if ( chr == '}' ) { if ( currentContext() == ct_graph ) { m_graphAgain = true; popContext(); return EOL; } else m_compiler->raiseError( e_graph_close_unbal, m_line ); } else if ( chr == '@' ) { return ATSIGN; } else if ( chr == '#' ) { return DIESIS; } } break; case 2: // EXTENDED CAP OPERATORS if ( m_string == "^=" ) return ASSIGN_BXOR; else if ( m_string == "^^" ) return CAP_CAP; else if ( m_string == "^*" ) return CAP_EVAL; else if ( m_string == "^!" ) return CAP_XOROOB; else if ( m_string == "^?" ) return CAP_ISOOB; else if ( m_string == "^-" ) return CAP_DEOOB; else if ( m_string == "^+" ) return CAP_OOB; //==== else if ( m_string == "=>" ) return ARROW; else if ( m_string == "==" ) return EEQ; else if ( m_string == "!=" ) return NEQ; else if ( m_string == ">=" ) return GE; else if ( m_string == "<=" ) return LE; else if ( m_string == "+=" ) return ASSIGN_ADD; else if ( m_string == "-=" ) return ASSIGN_SUB; else if ( m_string == "*=" ) return ASSIGN_MUL; else if ( m_string == "/=" ) return ASSIGN_DIV; else if ( m_string == "%=" ) return ASSIGN_MOD; else if ( m_string == "&=" ) return ASSIGN_BAND; else if ( m_string == "|=" ) return ASSIGN_BOR; else if ( m_string == "&&" ) return AMPER_AMPER; else if ( m_string == "||" ) return VBAR_VBAR; else if ( m_string == ">>" && nextChar != '=' ) return SHR; else if ( m_string == "<<" && nextChar != '=' ) return SHL; else if ( m_string == "++" ) return INCREMENT; else if ( m_string == "--" ) return DECREMENT; else if ( m_string == "**" && nextChar != '=' ) return POW; else if ( m_string == "//" ) m_state = e_eolComment; else if ( m_string == "/*" ) m_state = e_blockComment; else if ( m_string == ".=" ) return FORDOT; else if ( m_string == ".[" ) { pushContext( ct_square, m_line ); return LISTPAR; } else if ( parsingFtd() && m_string == "?>" && (nextChar != '\n' || m_in->eof() )) { m_mode = t_mOutscape; m_bIsDirectiveLine = false; return EOL; } break; case 3: if ( parsingFtd() && m_string == "?>\n" ) { m_mode = t_mOutscape; if ( m_bWasntEmpty ) m_whiteLead = "\n"; m_bIsDirectiveLine = false; m_previousLine = m_line; m_line++; m_character = 0; return EOL; } else if ( m_string == ">>=" ) return ASSIGN_SHR; else if ( m_string == "<<=" ) return ASSIGN_SHL; else if ( m_string == "**=" ) return ASSIGN_POW; break; } return 0; } int SrcLexer::checkLimitedTokens() { switch( m_string.length() ) { case 1: if ( m_string == "_" ) return UNB; break; case 2: if ( m_string == "or" ) return OR; else if ( m_string == "in" ) return OP_IN; else if ( m_string == "if" ) return IF; else if ( m_string == "to" ) return OP_TO; else if ( m_string == "as" ) return OP_AS; else if ( m_string == "eq" ) return OP_EXEQ; break; case 3: if ( m_string == "not" ) return NOT; if ( m_string == "try" ) return TRY; else if ( m_string == "nil" ) return NIL; else if ( m_string == "for" ) return FOR; else if ( m_string == "and" ) return AND; else if ( m_string == "and" ) return AND; else if ( m_string == "end" ) return END; else if ( m_string == "def" ) return DEF; break; case 4: if ( m_string == "load" ) // directive { m_bIsDirectiveLine = true; return LOAD; } if ( m_string == "init" ) return INIT; if ( m_string == "else" ) return ELSE; if ( m_string == "elif" ) return ELIF; if ( m_string == "from" ) return FROM; if ( m_string == "self" ) return SELF; if ( m_string == "case" ) return CASE; if ( m_string == "loop" ) return LOOP; if ( m_string == "true" ) return TRUE_TOKEN; if ( m_string == "enum" ) return ENUM; break; case 5: if ( m_string == "catch" ) return CATCH; if ( m_string == "break" ) return BREAK; if ( m_string == "raise" ) return RAISE; if ( m_string == "class" ) return CLASS; if ( m_string == "notin" ) return OP_NOTIN; if ( m_string == "const" ) return CONST_KW; if ( m_string == "while" ) return WHILE; if ( m_string == "false" ) return FALSE_TOKEN; if ( m_string == "fself" ) return FSELF; if( m_string == "macro" ) { m_string.size(0); parseMacro(); return 0; } break; case 6: if ( m_string == "switch" ) return SWITCH; if ( m_string == "select" ) return SELECT; if ( m_string == "global" ) return GLOBAL; if ( m_string == "launch" ) return LAUNCH; if ( m_string == "object" ) return OBJECT; if ( m_string == "return" ) return RETURN; if ( m_string == "export" ) // directive { m_bIsDirectiveLine = true; return EXPORT; } if ( m_string == "import" ) // directive { m_bIsDirectiveLine = true; return IMPORT; } if ( m_string == "static" ) return STATIC; break; case 7: if ( m_string == "forlast" ) return FORLAST; if ( m_string == "default" ) return DEFAULT; break; case 8: if ( m_string == "provides" ) return PROVIDES; if ( m_string == "function" ) return FUNCDECL; if ( m_string == "continue" ) return CONTINUE; if ( m_string == "dropping" ) return DROPPING; if ( m_string == "forfirst" ) return FORFIRST; break; case 9: if ( m_string == "directive" ) { // No assigments in directive. m_bIsDirectiveLine = true; return DIRECTIVE; } if ( m_string == "innerfunc" ) return INNERFUNC; if ( m_string == "formiddle" ) return FORMIDDLE; break; } return 0; } void SrcLexer::parsingFtd( bool b ) { if ( b ) { m_bParsingFtd = true; m_mode = t_mOutscape; } else { m_bParsingFtd = false; m_mode = t_mNormal; } } void SrcLexer::resetContexts() { // clear contexts while( m_topCtx != 0 ) { Context* ctx = m_topCtx->m_prev; delete m_topCtx; m_topCtx = ctx; } // force to generate a fake eol at next loop m_addEol = true; m_bIsDirectiveLine = false; m_state = e_line; m_lineFilled = false; m_string = ""; } void SrcLexer::appendStream( Stream *s ) { m_in = s; m_streams.pushBack( s ); m_streamLines.pushBack( (uint32) m_line ); } void SrcLexer::parseMacro() { // macros are in the form // macro decl( params ) (content) // they must be passed to the compiler as // function decl( params ); > content; end int startline = m_line; typedef enum { s_decl, s_params, s_endparams, s_content, s_done } macro_state; macro_state state = s_decl; String sDecl; String sContent; uint32 ctx = 0; uint32 chr; while( state != s_done && m_in->get( chr ) ) { if ( chr == '\n' ) { m_previousLine = m_line; m_line++; } switch(state) { case s_decl: if ( chr == '(' ) state = s_params; sDecl += chr; break; case s_params: if ( chr == ')' ) state = s_endparams; sDecl += chr; break; case s_endparams: if ( chr == '(' ) { state = s_content; ctx = 1; } break; case s_content: if ( chr == '(' ) ctx++; else if( chr == ')' ) { ctx--; if ( ctx == 0 ) { state = s_done; // last ) must not be included break; } } sContent += chr; break; default: break; } } // if we're done, pass the thing to the compiler for metacompilation if ( s_done ) { // escape \ and " String sContEsc; sContent.escape( sContEsc ); String sFunc = "function " + sDecl + "\n>>@\"" + sContEsc + "\"\nend\n"; m_compiler->metaCompile( sFunc, startline ); } else { // raise an error. m_compiler->raiseError( e_syn_macro, sDecl, startline ); } } void SrcLexer::parseMacroCall() { // macros are in the form // \\decl( param1, param2 ) // they must be passed to the compiler as // decl( "param1", param2 ) int startline = m_line; typedef enum { s_decl, s_params, s_done } macro_state; macro_state state = s_decl; String sDecl; String sParam; String sFinal; uint32 ctx=0; uint32 chr; while( state != s_done && m_in->get( chr ) ) { if ( chr == '\n' ) { m_previousLine = m_line; m_line++; } switch(state) { case s_decl: if ( chr == '(' ) { state = s_params; ctx = 1; } sFinal += chr; sDecl += chr; break; case s_params: if ( chr == '(' ) { ctx++; sParam += chr; } else if ( chr == ')' ) { ctx--; if ( ctx == 0 ) { state = s_done; if ( sParam.size() > 0 ) { String temp; sParam.trim(); sParam.escape( temp ); sFinal += '"'; sFinal += temp; sFinal += '"'; } sFinal += chr; break; } else sParam += chr; } else if( chr == ',' && ctx == 1 ) { String temp; sParam.trim(); sParam.escape( temp ); sFinal += '"'; sFinal += temp; sFinal += '"'; sFinal += ','; sParam.size(0); } else sParam += chr; break; default: break; } } // if we're done, pass the thing to the compiler for metacompilation if ( s_done ) { m_compiler->metaCompile( sFinal+"\n", startline ); } else { // raise an error. m_compiler->raiseError( e_syn_macro_call, sDecl, startline ); } } void SrcLexer::pushContext( t_contextType ct, int startLine ) { m_topCtx = new Context( ct, startLine, m_topCtx ); } bool SrcLexer::popContext() { if( m_topCtx == 0 ) return false; Context* current = m_topCtx; m_topCtx = m_topCtx->m_prev; delete current; return true; } SrcLexer::t_contextType SrcLexer::currentContext() { if( m_topCtx == 0 ) return ct_top; return m_topCtx->m_ct; } int SrcLexer::contextStart() { if( m_topCtx == 0 ) return 0; return m_topCtx->m_oline; } bool SrcLexer::inParCtx() { return m_topCtx != 0 && ( m_topCtx->m_ct == ct_round || m_topCtx->m_ct == ct_square ); } bool SrcLexer::readAhead( uint32 &chr ) { bool res; while( (res = m_in->readAhead( chr )) && chr == '\r' ) { m_in->discardReadAhead( 1 ); } return res; } } /* end of src_lexer.cpp */ engine/src_parser.cpp000066400000000000000000011653031176363201700152110ustar00rootroot00000000000000 /* A Bison parser, made by GNU Bison 2.4.1. */ /* Skeleton implementation for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "2.4.1" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 1 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Using locations. */ #define YYLSP_NEEDED 0 /* Substitute the variable and function names. */ #define yyparse flc_src_parse #define yylex flc_src_lex #define yyerror flc_src_error #define yylval flc_src_lval #define yychar flc_src_char #define yydebug flc_src_debug #define yynerrs flc_src_nerrs /* Copy the first part of user declarations. */ /* Line 189 of yacc.c */ #line 17 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" #include #include #include #include #include #include #include #include #include #include #include #define YYMALLOC Falcon::memAlloc #define YYFREE Falcon::memFree #define COMPILER ( reinterpret_cast< Falcon::Compiler *>(yyparam) ) #define CTX_LINE ( COMPILER->lexer()->contextStart() ) #define LINE ( COMPILER->lexer()->previousLine() ) #define CURRENT_LINE ( COMPILER->lexer()->line() ) #define YYPARSE_PARAM yyparam #define YYLEX_PARAM yyparam int flc_src_parse( void *param ); void flc_src_error (const char *s); inline int flc_src_lex (void *lvalp, void *yyparam) { return COMPILER->lexer()->doLex( lvalp ); } /* Cures a bug in bison 1.8 */ #undef __GNUC_MINOR__ /* Line 189 of yacc.c */ #line 124 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.cpp" /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 0 #endif /* Enabling the token table. */ #ifndef YYTOKEN_TABLE # define YYTOKEN_TABLE 0 #endif /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { EOL = 258, INTNUM = 259, DBLNUM = 260, SYMBOL = 261, STRING = 262, NIL = 263, UNB = 264, END = 265, DEF = 266, WHILE = 267, BREAK = 268, CONTINUE = 269, DROPPING = 270, IF = 271, ELSE = 272, ELIF = 273, FOR = 274, FORFIRST = 275, FORLAST = 276, FORMIDDLE = 277, SWITCH = 278, CASE = 279, DEFAULT = 280, SELECT = 281, SELF = 282, FSELF = 283, TRY = 284, CATCH = 285, RAISE = 286, CLASS = 287, FROM = 288, OBJECT = 289, RETURN = 290, GLOBAL = 291, INIT = 292, LOAD = 293, LAUNCH = 294, CONST_KW = 295, EXPORT = 296, IMPORT = 297, DIRECTIVE = 298, COLON = 299, FUNCDECL = 300, STATIC = 301, INNERFUNC = 302, FORDOT = 303, LISTPAR = 304, LOOP = 305, ENUM = 306, TRUE_TOKEN = 307, FALSE_TOKEN = 308, OUTER_STRING = 309, CLOSEPAR = 310, OPENPAR = 311, CLOSESQUARE = 312, OPENSQUARE = 313, DOT = 314, OPEN_GRAPH = 315, CLOSE_GRAPH = 316, ARROW = 317, VBAR = 318, ASSIGN_POW = 319, ASSIGN_SHL = 320, ASSIGN_SHR = 321, ASSIGN_BXOR = 322, ASSIGN_BOR = 323, ASSIGN_BAND = 324, ASSIGN_MOD = 325, ASSIGN_DIV = 326, ASSIGN_MUL = 327, ASSIGN_SUB = 328, ASSIGN_ADD = 329, OP_EQ = 330, OP_AS = 331, OP_TO = 332, COMMA = 333, QUESTION = 334, OR = 335, AND = 336, NOT = 337, LE = 338, GE = 339, LT = 340, GT = 341, NEQ = 342, EEQ = 343, OP_EXEQ = 344, PROVIDES = 345, OP_NOTIN = 346, OP_IN = 347, DIESIS = 348, ATSIGN = 349, CAP_CAP = 350, VBAR_VBAR = 351, AMPER_AMPER = 352, MINUS = 353, PLUS = 354, PERCENT = 355, SLASH = 356, STAR = 357, POW = 358, SHR = 359, SHL = 360, CAP_XOROOB = 361, CAP_ISOOB = 362, CAP_DEOOB = 363, CAP_OOB = 364, CAP_EVAL = 365, TILDE = 366, NEG = 367, AMPER = 368, DECREMENT = 369, INCREMENT = 370, DOLLAR = 371 }; #endif /* Tokens. */ #define EOL 258 #define INTNUM 259 #define DBLNUM 260 #define SYMBOL 261 #define STRING 262 #define NIL 263 #define UNB 264 #define END 265 #define DEF 266 #define WHILE 267 #define BREAK 268 #define CONTINUE 269 #define DROPPING 270 #define IF 271 #define ELSE 272 #define ELIF 273 #define FOR 274 #define FORFIRST 275 #define FORLAST 276 #define FORMIDDLE 277 #define SWITCH 278 #define CASE 279 #define DEFAULT 280 #define SELECT 281 #define SELF 282 #define FSELF 283 #define TRY 284 #define CATCH 285 #define RAISE 286 #define CLASS 287 #define FROM 288 #define OBJECT 289 #define RETURN 290 #define GLOBAL 291 #define INIT 292 #define LOAD 293 #define LAUNCH 294 #define CONST_KW 295 #define EXPORT 296 #define IMPORT 297 #define DIRECTIVE 298 #define COLON 299 #define FUNCDECL 300 #define STATIC 301 #define INNERFUNC 302 #define FORDOT 303 #define LISTPAR 304 #define LOOP 305 #define ENUM 306 #define TRUE_TOKEN 307 #define FALSE_TOKEN 308 #define OUTER_STRING 309 #define CLOSEPAR 310 #define OPENPAR 311 #define CLOSESQUARE 312 #define OPENSQUARE 313 #define DOT 314 #define OPEN_GRAPH 315 #define CLOSE_GRAPH 316 #define ARROW 317 #define VBAR 318 #define ASSIGN_POW 319 #define ASSIGN_SHL 320 #define ASSIGN_SHR 321 #define ASSIGN_BXOR 322 #define ASSIGN_BOR 323 #define ASSIGN_BAND 324 #define ASSIGN_MOD 325 #define ASSIGN_DIV 326 #define ASSIGN_MUL 327 #define ASSIGN_SUB 328 #define ASSIGN_ADD 329 #define OP_EQ 330 #define OP_AS 331 #define OP_TO 332 #define COMMA 333 #define QUESTION 334 #define OR 335 #define AND 336 #define NOT 337 #define LE 338 #define GE 339 #define LT 340 #define GT 341 #define NEQ 342 #define EEQ 343 #define OP_EXEQ 344 #define PROVIDES 345 #define OP_NOTIN 346 #define OP_IN 347 #define DIESIS 348 #define ATSIGN 349 #define CAP_CAP 350 #define VBAR_VBAR 351 #define AMPER_AMPER 352 #define MINUS 353 #define PLUS 354 #define PERCENT 355 #define SLASH 356 #define STAR 357 #define POW 358 #define SHR 359 #define SHL 360 #define CAP_XOROOB 361 #define CAP_ISOOB 362 #define CAP_DEOOB 363 #define CAP_OOB 364 #define CAP_EVAL 365 #define TILDE 366 #define NEG 367 #define AMPER 368 #define DECREMENT 369 #define INCREMENT 370 #define DOLLAR 371 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union /* Line 214 of yacc.c */ #line 61 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" lex_value_t { /* Line 214 of yacc.c */ #line 61 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" Falcon::int64 integer; Falcon::numeric numeric; char * charp; Falcon::String *stringp; Falcon::Symbol *symbol; Falcon::Value *fal_val; Falcon::Expression *fal_exp; Falcon::Statement *fal_stat; Falcon::ArrayDecl *fal_adecl; Falcon::DictDecl *fal_ddecl; Falcon::SymbolList *fal_symlist; Falcon::List *fal_genericList; /* Line 214 of yacc.c */ #line 412 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.cpp" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 #endif /* Copy the second part of user declarations. */ /* Line 264 of yacc.c */ #line 424 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.cpp" #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #elif (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) typedef signed char yytype_int8; #else typedef short int yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(msgid) dgettext ("bison-runtime", msgid) # endif # endif # ifndef YY_ # define YY_(msgid) msgid # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(e) ((void) (e)) #else # define YYUSE(e) /* empty */ #endif /* Identity function, used to suppress warnings about constant conditions. */ #ifndef lint # define YYID(n) (n) #else #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int YYID (int yyi) #else static int YYID (yyi) int yyi; #endif { return yyi; } #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's `empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined _STDLIB_H \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef _STDLIB_H # define _STDLIB_H 1 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss_alloc; YYSTYPE yyvs_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(To, From, Count) \ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) # else # define YYCOPY(To, From, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ while (YYID (0)) # endif # endif /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (YYID (0)) #endif /* YYFINAL -- State number of the termination state. */ #define YYFINAL 3 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 6811 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 117 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 167 /* YYNRULES -- Number of rules. */ #define YYNRULES 470 /* YYNRULES -- Number of states. */ #define YYNSTATES 852 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 371 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116 }; #if YYDEBUG /* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in YYRHS. */ static const yytype_uint16 yyprhs[] = { 0, 0, 3, 5, 6, 9, 11, 14, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 43, 47, 51, 55, 57, 59, 61, 65, 69, 73, 76, 79, 84, 91, 93, 95, 97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127, 131, 133, 137, 141, 145, 146, 152, 155, 159, 163, 167, 171, 172, 180, 184, 188, 189, 191, 192, 199, 202, 206, 210, 214, 218, 219, 221, 222, 226, 229, 233, 234, 239, 243, 247, 248, 251, 254, 258, 261, 265, 269, 274, 279, 285, 289, 290, 295, 296, 303, 308, 313, 317, 318, 321, 324, 325, 328, 330, 332, 334, 336, 340, 344, 348, 351, 355, 358, 362, 366, 368, 369, 376, 380, 384, 385, 392, 396, 400, 401, 408, 412, 416, 417, 424, 428, 432, 433, 436, 440, 442, 443, 449, 450, 456, 457, 463, 464, 470, 471, 472, 476, 477, 479, 482, 485, 488, 490, 494, 496, 498, 500, 504, 506, 507, 514, 518, 522, 523, 526, 530, 532, 533, 539, 540, 546, 547, 553, 554, 560, 562, 566, 567, 569, 571, 575, 576, 583, 586, 590, 591, 593, 595, 598, 601, 604, 609, 613, 619, 623, 625, 629, 631, 633, 637, 641, 647, 650, 656, 657, 665, 669, 675, 676, 683, 686, 687, 689, 693, 695, 696, 697, 703, 704, 708, 711, 715, 718, 722, 726, 730, 736, 742, 746, 749, 753, 757, 759, 763, 767, 773, 779, 787, 795, 803, 811, 816, 821, 826, 831, 838, 845, 849, 854, 859, 861, 865, 869, 873, 875, 879, 883, 887, 891, 892, 900, 904, 907, 908, 912, 913, 919, 920, 923, 925, 929, 932, 933, 936, 940, 941, 944, 946, 948, 950, 952, 954, 956, 957, 965, 971, 976, 981, 986, 991, 992, 995, 997, 999, 1000, 1008, 1009, 1012, 1014, 1019, 1021, 1024, 1026, 1028, 1029, 1037, 1040, 1043, 1044, 1047, 1049, 1051, 1053, 1055, 1057, 1058, 1063, 1065, 1067, 1070, 1074, 1078, 1080, 1083, 1087, 1091, 1093, 1095, 1097, 1099, 1101, 1103, 1105, 1107, 1109, 1111, 1113, 1115, 1117, 1119, 1121, 1123, 1125, 1127, 1128, 1130, 1132, 1134, 1137, 1140, 1143, 1147, 1151, 1155, 1158, 1162, 1167, 1172, 1177, 1182, 1187, 1192, 1197, 1202, 1207, 1212, 1217, 1220, 1224, 1227, 1230, 1233, 1236, 1240, 1244, 1248, 1252, 1256, 1260, 1264, 1268, 1271, 1275, 1279, 1283, 1286, 1289, 1292, 1295, 1298, 1301, 1304, 1307, 1310, 1312, 1314, 1316, 1318, 1320, 1322, 1325, 1327, 1332, 1338, 1342, 1344, 1346, 1350, 1356, 1360, 1364, 1368, 1372, 1376, 1380, 1384, 1388, 1392, 1396, 1400, 1404, 1408, 1413, 1418, 1424, 1432, 1437, 1441, 1442, 1449, 1450, 1457, 1458, 1465, 1470, 1474, 1477, 1480, 1483, 1486, 1487, 1494, 1500, 1506, 1511, 1515, 1518, 1522, 1526, 1529, 1533, 1537, 1541, 1545, 1550, 1552, 1556, 1558, 1562, 1563, 1565, 1567, 1571, 1575 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int16 yyrhs[] = { 118, 0, -1, 119, -1, -1, 119, 120, -1, 121, -1, 10, 3, -1, 24, 1, 3, -1, 123, -1, 222, -1, 202, -1, 225, -1, 248, -1, 243, -1, 124, -1, 216, -1, 217, -1, 219, -1, 4, -1, 98, 4, -1, 38, 6, 3, -1, 38, 7, 3, -1, 38, 1, 3, -1, 125, -1, 220, -1, 3, -1, 45, 1, 3, -1, 34, 1, 3, -1, 32, 1, 3, -1, 1, 3, -1, 263, 3, -1, 279, 75, 263, 3, -1, 279, 75, 263, 78, 279, 3, -1, 128, -1, 129, -1, 133, -1, 150, -1, 166, -1, 181, -1, 136, -1, 147, -1, 148, -1, 192, -1, 201, -1, 257, -1, 253, -1, 215, -1, 157, -1, 158, -1, 159, -1, 127, -1, 126, 78, 127, -1, 260, -1, 260, 75, 263, -1, 11, 126, 3, -1, 11, 1, 3, -1, -1, 131, 130, 146, 10, 3, -1, 132, 124, -1, 12, 263, 3, -1, 12, 1, 3, -1, 12, 263, 44, -1, 12, 1, 44, -1, -1, 50, 3, 134, 146, 10, 135, 3, -1, 50, 44, 124, -1, 50, 1, 3, -1, -1, 263, -1, -1, 138, 137, 146, 140, 10, 3, -1, 139, 124, -1, 16, 263, 3, -1, 16, 1, 3, -1, 16, 263, 44, -1, 16, 1, 44, -1, -1, 143, -1, -1, 142, 141, 146, -1, 17, 3, -1, 17, 1, 3, -1, -1, 145, 144, 146, 140, -1, 18, 263, 3, -1, 18, 1, 3, -1, -1, 146, 124, -1, 13, 3, -1, 13, 1, 3, -1, 14, 3, -1, 14, 15, 3, -1, 14, 1, 3, -1, 19, 282, 92, 263, -1, 19, 260, 75, 153, -1, 19, 282, 92, 1, 3, -1, 19, 1, 3, -1, -1, 149, 44, 151, 124, -1, -1, 149, 3, 152, 155, 10, 3, -1, 263, 77, 263, 154, -1, 263, 77, 263, 1, -1, 263, 77, 1, -1, -1, 78, 263, -1, 78, 1, -1, -1, 156, 155, -1, 124, -1, 160, -1, 162, -1, 164, -1, 48, 263, 3, -1, 48, 1, 3, -1, 104, 279, 3, -1, 104, 3, -1, 86, 279, 3, -1, 86, 3, -1, 104, 1, 3, -1, 86, 1, 3, -1, 54, -1, -1, 20, 3, 161, 146, 10, 3, -1, 20, 44, 124, -1, 20, 1, 3, -1, -1, 21, 3, 163, 146, 10, 3, -1, 21, 44, 124, -1, 21, 1, 3, -1, -1, 22, 3, 165, 146, 10, 3, -1, 22, 44, 124, -1, 22, 1, 3, -1, -1, 168, 167, 169, 175, 10, 3, -1, 23, 263, 3, -1, 23, 1, 3, -1, -1, 169, 170, -1, 169, 1, 3, -1, 3, -1, -1, 24, 179, 3, 171, 146, -1, -1, 24, 179, 44, 172, 124, -1, -1, 24, 1, 3, 173, 146, -1, -1, 24, 1, 44, 174, 124, -1, -1, -1, 177, 176, 178, -1, -1, 25, -1, 25, 1, -1, 3, 146, -1, 44, 124, -1, 180, -1, 179, 78, 180, -1, 8, -1, 122, -1, 7, -1, 122, 77, 122, -1, 6, -1, -1, 183, 182, 184, 175, 10, 3, -1, 26, 263, 3, -1, 26, 1, 3, -1, -1, 184, 185, -1, 184, 1, 3, -1, 3, -1, -1, 24, 190, 3, 186, 146, -1, -1, 24, 190, 44, 187, 124, -1, -1, 24, 1, 3, 188, 146, -1, -1, 24, 1, 44, 189, 124, -1, 191, -1, 190, 78, 191, -1, -1, 4, -1, 6, -1, 29, 44, 124, -1, -1, 194, 193, 146, 195, 10, 3, -1, 29, 3, -1, 29, 1, 3, -1, -1, 196, -1, 197, -1, 196, 197, -1, 198, 146, -1, 30, 3, -1, 30, 92, 260, 3, -1, 30, 199, 3, -1, 30, 199, 92, 260, 3, -1, 30, 1, 3, -1, 200, -1, 199, 78, 200, -1, 4, -1, 6, -1, 31, 263, 3, -1, 31, 1, 3, -1, 203, 210, 146, 10, 3, -1, 205, 124, -1, 207, 56, 208, 55, 3, -1, -1, 207, 56, 208, 1, 204, 55, 3, -1, 207, 1, 3, -1, 207, 56, 208, 55, 44, -1, -1, 207, 56, 1, 206, 55, 44, -1, 45, 6, -1, -1, 209, -1, 208, 78, 209, -1, 6, -1, -1, -1, 213, 211, 146, 10, 3, -1, -1, 214, 212, 124, -1, 46, 3, -1, 46, 1, 3, -1, 46, 44, -1, 46, 1, 44, -1, 39, 265, 3, -1, 39, 1, 3, -1, 40, 6, 75, 259, 3, -1, 40, 6, 75, 1, 3, -1, 40, 1, 3, -1, 41, 3, -1, 41, 218, 3, -1, 41, 1, 3, -1, 6, -1, 218, 78, 6, -1, 42, 221, 3, -1, 42, 221, 33, 6, 3, -1, 42, 221, 33, 7, 3, -1, 42, 221, 33, 6, 76, 6, 3, -1, 42, 221, 33, 7, 76, 6, 3, -1, 42, 221, 33, 6, 92, 6, 3, -1, 42, 221, 33, 7, 92, 6, 3, -1, 42, 6, 1, 3, -1, 42, 221, 1, 3, -1, 42, 33, 6, 3, -1, 42, 33, 7, 3, -1, 42, 33, 6, 92, 6, 3, -1, 42, 33, 7, 92, 6, 3, -1, 42, 1, 3, -1, 6, 44, 259, 3, -1, 6, 44, 1, 3, -1, 6, -1, 221, 78, 6, -1, 43, 223, 3, -1, 43, 1, 3, -1, 224, -1, 223, 78, 224, -1, 6, 75, 6, -1, 6, 75, 7, -1, 6, 75, 122, -1, -1, 32, 6, 226, 227, 234, 10, 3, -1, 228, 230, 3, -1, 1, 3, -1, -1, 56, 208, 55, -1, -1, 56, 208, 1, 229, 55, -1, -1, 33, 231, -1, 232, -1, 231, 78, 232, -1, 6, 233, -1, -1, 56, 55, -1, 56, 279, 55, -1, -1, 234, 235, -1, 3, -1, 202, -1, 238, -1, 239, -1, 236, -1, 220, -1, -1, 37, 3, 237, 210, 146, 10, 3, -1, 46, 6, 75, 259, 3, -1, 6, 75, 263, 3, -1, 240, 241, 10, 3, -1, 58, 6, 57, 3, -1, 58, 37, 57, 3, -1, -1, 241, 242, -1, 3, -1, 202, -1, -1, 51, 6, 244, 3, 245, 10, 3, -1, -1, 245, 246, -1, 3, -1, 6, 75, 259, 247, -1, 220, -1, 6, 247, -1, 3, -1, 78, -1, -1, 34, 6, 249, 250, 251, 10, 3, -1, 230, 3, -1, 1, 3, -1, -1, 251, 252, -1, 3, -1, 202, -1, 238, -1, 236, -1, 220, -1, -1, 36, 254, 255, 3, -1, 256, -1, 1, -1, 256, 1, -1, 255, 78, 256, -1, 255, 78, 1, -1, 6, -1, 35, 3, -1, 35, 263, 3, -1, 35, 1, 3, -1, 8, -1, 9, -1, 52, -1, 53, -1, 4, -1, 5, -1, 7, -1, 8, -1, 9, -1, 52, -1, 53, -1, 122, -1, 5, -1, 7, -1, 6, -1, 260, -1, 27, -1, 28, -1, -1, 3, -1, 258, -1, 261, -1, 113, 6, -1, 113, 4, -1, 113, 27, -1, 113, 59, 6, -1, 113, 59, 4, -1, 113, 59, 27, -1, 98, 263, -1, 6, 63, 263, -1, 263, 99, 262, 263, -1, 263, 98, 262, 263, -1, 263, 102, 262, 263, -1, 263, 101, 262, 263, -1, 263, 100, 262, 263, -1, 263, 103, 262, 263, -1, 263, 97, 262, 263, -1, 263, 96, 262, 263, -1, 263, 95, 262, 263, -1, 263, 105, 262, 263, -1, 263, 104, 262, 263, -1, 111, 263, -1, 263, 87, 263, -1, 263, 115, -1, 115, 263, -1, 263, 114, -1, 114, 263, -1, 263, 88, 263, -1, 263, 89, 263, -1, 263, 86, 263, -1, 263, 85, 263, -1, 263, 84, 263, -1, 263, 83, 263, -1, 263, 81, 263, -1, 263, 80, 263, -1, 82, 263, -1, 263, 92, 263, -1, 263, 91, 263, -1, 263, 90, 6, -1, 116, 260, -1, 116, 4, -1, 94, 263, -1, 93, 263, -1, 110, 263, -1, 109, 263, -1, 108, 263, -1, 107, 263, -1, 106, 263, -1, 267, -1, 269, -1, 273, -1, 265, -1, 275, -1, 277, -1, 263, 264, -1, 276, -1, 263, 58, 263, 57, -1, 263, 58, 102, 263, 57, -1, 263, 59, 6, -1, 278, -1, 264, -1, 263, 75, 263, -1, 263, 75, 263, 78, 279, -1, 263, 74, 263, -1, 263, 73, 263, -1, 263, 72, 263, -1, 263, 71, 263, -1, 263, 70, 263, -1, 263, 64, 263, -1, 263, 69, 263, -1, 263, 68, 263, -1, 263, 67, 263, -1, 263, 65, 263, -1, 263, 66, 263, -1, 56, 263, 55, -1, 58, 44, 57, -1, 58, 263, 44, 57, -1, 58, 44, 263, 57, -1, 58, 263, 44, 263, 57, -1, 58, 263, 44, 263, 44, 263, 57, -1, 263, 56, 279, 55, -1, 263, 56, 55, -1, -1, 263, 56, 279, 1, 266, 55, -1, -1, 45, 268, 271, 210, 146, 10, -1, -1, 60, 270, 272, 210, 146, 61, -1, 56, 208, 55, 3, -1, 56, 208, 1, -1, 1, 3, -1, 208, 62, -1, 208, 1, -1, 1, 62, -1, -1, 47, 274, 271, 210, 146, 10, -1, 263, 79, 263, 44, 263, -1, 263, 79, 263, 44, 1, -1, 263, 79, 263, 1, -1, 263, 79, 1, -1, 58, 57, -1, 58, 279, 57, -1, 58, 279, 1, -1, 49, 57, -1, 49, 280, 57, -1, 49, 280, 1, -1, 58, 62, 57, -1, 58, 283, 57, -1, 58, 283, 1, 57, -1, 263, -1, 279, 78, 263, -1, 263, -1, 280, 281, 263, -1, -1, 78, -1, 260, -1, 282, 78, 260, -1, 263, 62, 263, -1, 283, 78, 263, 62, 263, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { 0, 197, 197, 200, 202, 206, 207, 208, 212, 213, 214, 219, 224, 229, 234, 239, 240, 241, 245, 246, 250, 256, 262, 269, 270, 271, 272, 273, 274, 275, 280, 291, 308, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 342, 343, 347, 355, 365, 367, 372, 372, 386, 394, 395, 399, 400, 404, 404, 419, 425, 432, 433, 437, 437, 452, 462, 463, 467, 468, 472, 474, 475, 475, 484, 485, 490, 490, 502, 503, 506, 508, 514, 523, 531, 541, 550, 558, 563, 571, 576, 586, 585, 606, 605, 627, 633, 637, 644, 645, 646, 649, 651, 655, 662, 663, 664, 668, 681, 689, 693, 699, 705, 712, 717, 726, 736, 736, 750, 759, 763, 763, 776, 785, 789, 789, 805, 814, 818, 818, 835, 836, 843, 845, 846, 850, 852, 851, 862, 862, 874, 874, 886, 886, 902, 905, 904, 917, 918, 919, 922, 923, 929, 930, 934, 943, 955, 966, 977, 998, 998, 1015, 1016, 1023, 1025, 1026, 1030, 1032, 1031, 1042, 1042, 1055, 1055, 1067, 1067, 1085, 1086, 1089, 1090, 1102, 1123, 1130, 1129, 1148, 1149, 1152, 1154, 1158, 1159, 1163, 1168, 1186, 1206, 1216, 1227, 1235, 1236, 1240, 1252, 1275, 1276, 1283, 1293, 1302, 1303, 1303, 1307, 1311, 1312, 1312, 1319, 1441, 1443, 1444, 1448, 1463, 1466, 1465, 1477, 1476, 1491, 1492, 1496, 1497, 1506, 1510, 1518, 1528, 1533, 1545, 1554, 1561, 1569, 1574, 1582, 1587, 1592, 1597, 1617, 1636, 1641, 1646, 1651, 1665, 1670, 1675, 1680, 1685, 1694, 1699, 1706, 1712, 1724, 1729, 1737, 1738, 1742, 1746, 1750, 1764, 1763, 1826, 1829, 1835, 1837, 1838, 1838, 1844, 1846, 1850, 1851, 1855, 1879, 1880, 1881, 1888, 1890, 1894, 1895, 1898, 1917, 1937, 1938, 1942, 1942, 1976, 1998, 2026, 2038, 2046, 2059, 2061, 2066, 2067, 2079, 2078, 2122, 2124, 2128, 2129, 2133, 2134, 2141, 2141, 2150, 2149, 2216, 2217, 2223, 2225, 2229, 2230, 2233, 2252, 2253, 2262, 2261, 2279, 2280, 2285, 2290, 2291, 2298, 2314, 2315, 2316, 2324, 2325, 2326, 2327, 2328, 2329, 2330, 2334, 2335, 2336, 2337, 2338, 2339, 2340, 2344, 2362, 2363, 2364, 2374, 2376, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2387, 2388, 2389, 2390, 2416, 2417, 2437, 2461, 2478, 2479, 2480, 2481, 2482, 2483, 2484, 2485, 2486, 2487, 2488, 2489, 2490, 2491, 2492, 2493, 2494, 2495, 2496, 2497, 2498, 2499, 2500, 2501, 2502, 2503, 2504, 2505, 2506, 2507, 2508, 2509, 2510, 2511, 2512, 2513, 2514, 2515, 2516, 2518, 2523, 2527, 2532, 2538, 2547, 2548, 2550, 2555, 2562, 2563, 2564, 2565, 2566, 2567, 2568, 2569, 2570, 2571, 2572, 2573, 2578, 2581, 2584, 2587, 2590, 2596, 2602, 2607, 2607, 2617, 2616, 2659, 2658, 2710, 2711, 2715, 2722, 2723, 2727, 2735, 2734, 2783, 2788, 2795, 2802, 2812, 2813, 2817, 2825, 2826, 2830, 2839, 2840, 2841, 2849, 2850, 2854, 2855, 2858, 2859, 2862, 2868, 2875, 2876 }; #endif #if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "EOL", "INTNUM", "DBLNUM", "SYMBOL", "STRING", "NIL", "UNB", "END", "DEF", "WHILE", "BREAK", "CONTINUE", "DROPPING", "IF", "ELSE", "ELIF", "FOR", "FORFIRST", "FORLAST", "FORMIDDLE", "SWITCH", "CASE", "DEFAULT", "SELECT", "SELF", "FSELF", "TRY", "CATCH", "RAISE", "CLASS", "FROM", "OBJECT", "RETURN", "GLOBAL", "INIT", "LOAD", "LAUNCH", "CONST_KW", "EXPORT", "IMPORT", "DIRECTIVE", "COLON", "FUNCDECL", "STATIC", "INNERFUNC", "FORDOT", "LISTPAR", "LOOP", "ENUM", "TRUE_TOKEN", "FALSE_TOKEN", "OUTER_STRING", "CLOSEPAR", "OPENPAR", "CLOSESQUARE", "OPENSQUARE", "DOT", "OPEN_GRAPH", "CLOSE_GRAPH", "ARROW", "VBAR", "ASSIGN_POW", "ASSIGN_SHL", "ASSIGN_SHR", "ASSIGN_BXOR", "ASSIGN_BOR", "ASSIGN_BAND", "ASSIGN_MOD", "ASSIGN_DIV", "ASSIGN_MUL", "ASSIGN_SUB", "ASSIGN_ADD", "OP_EQ", "OP_AS", "OP_TO", "COMMA", "QUESTION", "OR", "AND", "NOT", "LE", "GE", "LT", "GT", "NEQ", "EEQ", "OP_EXEQ", "PROVIDES", "OP_NOTIN", "OP_IN", "DIESIS", "ATSIGN", "CAP_CAP", "VBAR_VBAR", "AMPER_AMPER", "MINUS", "PLUS", "PERCENT", "SLASH", "STAR", "POW", "SHR", "SHL", "CAP_XOROOB", "CAP_ISOOB", "CAP_DEOOB", "CAP_OOB", "CAP_EVAL", "TILDE", "NEG", "AMPER", "DECREMENT", "INCREMENT", "DOLLAR", "$accept", "input", "body", "line", "toplevel_statement", "INTNUM_WITH_MINUS", "load_statement", "statement", "base_statement", "assignment_def_list", "assignment_def_list_element", "def_statement", "while_statement", "$@1", "while_decl", "while_short_decl", "loop_statement", "$@2", "loop_terminator", "if_statement", "$@3", "if_decl", "if_short_decl", "elif_or_else", "$@4", "else_decl", "elif_statement", "$@5", "elif_decl", "statement_list", "break_statement", "continue_statement", "forin_header", "forin_statement", "$@6", "$@7", "for_to_expr", "for_to_step_clause", "forin_statement_list", "forin_statement_elem", "fordot_statement", "self_print_statement", "outer_print_statement", "first_loop_block", "$@8", "last_loop_block", "$@9", "middle_loop_block", "$@10", "switch_statement", "$@11", "switch_decl", "case_list", "case_statement", "$@12", "$@13", "$@14", "$@15", "default_statement", "$@16", "default_decl", "default_body", "case_expression_list", "case_element", "select_statement", "$@17", "select_decl", "selcase_list", "selcase_statement", "$@18", "$@19", "$@20", "$@21", "selcase_expression_list", "selcase_element", "try_statement", "$@22", "try_decl", "catch_statements", "catch_list", "catch_statement", "catch_decl", "catchcase_element_list", "catchcase_element", "raise_statement", "func_statement", "func_decl", "$@23", "func_decl_short", "$@24", "func_begin", "param_list", "param_symbol", "static_block", "$@25", "$@26", "static_decl", "static_short_decl", "launch_statement", "const_statement", "export_statement", "export_symbol_list", "import_statement", "attribute_statement", "import_symbol_list", "directive_statement", "directive_pair_list", "directive_pair", "class_decl", "$@27", "class_def_inner", "class_param_list", "$@28", "from_clause", "inherit_list", "inherit_token", "inherit_call", "class_statement_list", "class_statement", "init_decl", "$@29", "property_decl", "state_decl", "state_heading", "state_statement_list", "state_statement", "enum_statement", "$@30", "enum_statement_list", "enum_item_decl", "enum_item_terminator", "object_decl", "$@31", "object_decl_inner", "object_statement_list", "object_statement", "global_statement", "$@32", "global_symbol_list", "globalized_symbol", "return_statement", "const_atom_non_minus", "const_atom", "atomic_symbol", "var_atom", "OPT_EOL", "expression", "range_decl", "func_call", "$@33", "nameless_func", "$@34", "nameless_block", "$@35", "nameless_func_decl_inner", "nameless_block_decl_inner", "innerfunc", "$@36", "iif_expr", "array_decl", "dotarray_decl", "dict_decl", "expression_list", "listpar_expression_list", "listpar_comma", "symbol_list", "expression_pair_list", 0 }; #endif # ifdef YYPRINT /* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to token YYLEX-NUM. */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint16 yyr1[] = { 0, 117, 118, 119, 119, 120, 120, 120, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 122, 122, 123, 123, 123, 124, 124, 124, 124, 124, 124, 124, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 126, 126, 127, 127, 128, 128, 130, 129, 129, 131, 131, 132, 132, 134, 133, 133, 133, 135, 135, 137, 136, 136, 138, 138, 139, 139, 140, 140, 141, 140, 142, 142, 144, 143, 145, 145, 146, 146, 147, 147, 148, 148, 148, 149, 149, 149, 149, 151, 150, 152, 150, 153, 153, 153, 154, 154, 154, 155, 155, 156, 156, 156, 156, 157, 157, 158, 158, 158, 158, 158, 158, 159, 161, 160, 160, 160, 163, 162, 162, 162, 165, 164, 164, 164, 167, 166, 168, 168, 169, 169, 169, 170, 171, 170, 172, 170, 173, 170, 174, 170, 175, 176, 175, 177, 177, 177, 178, 178, 179, 179, 180, 180, 180, 180, 180, 182, 181, 183, 183, 184, 184, 184, 185, 186, 185, 187, 185, 188, 185, 189, 185, 190, 190, 191, 191, 191, 192, 193, 192, 194, 194, 195, 195, 196, 196, 197, 198, 198, 198, 198, 198, 199, 199, 200, 200, 201, 201, 202, 202, 203, 204, 203, 203, 205, 206, 205, 207, 208, 208, 208, 209, 210, 211, 210, 212, 210, 213, 213, 214, 214, 215, 215, 216, 216, 216, 217, 217, 217, 218, 218, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 224, 226, 225, 227, 227, 228, 228, 229, 228, 230, 230, 231, 231, 232, 233, 233, 233, 234, 234, 235, 235, 235, 235, 235, 235, 237, 236, 238, 238, 239, 240, 240, 241, 241, 242, 242, 244, 243, 245, 245, 246, 246, 246, 246, 247, 247, 249, 248, 250, 250, 251, 251, 252, 252, 252, 252, 252, 254, 253, 255, 255, 255, 255, 255, 256, 257, 257, 257, 258, 258, 258, 258, 258, 258, 258, 259, 259, 259, 259, 259, 259, 259, 260, 261, 261, 261, 262, 262, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 264, 264, 264, 264, 264, 265, 265, 266, 265, 268, 267, 270, 269, 271, 271, 271, 272, 272, 272, 274, 273, 275, 275, 275, 275, 276, 276, 276, 277, 277, 277, 278, 278, 278, 279, 279, 280, 280, 281, 281, 282, 282, 283, 283 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 1, 0, 2, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 1, 1, 1, 3, 3, 3, 2, 2, 4, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 3, 3, 0, 5, 2, 3, 3, 3, 3, 0, 7, 3, 3, 0, 1, 0, 6, 2, 3, 3, 3, 3, 0, 1, 0, 3, 2, 3, 0, 4, 3, 3, 0, 2, 2, 3, 2, 3, 3, 4, 4, 5, 3, 0, 4, 0, 6, 4, 4, 3, 0, 2, 2, 0, 2, 1, 1, 1, 1, 3, 3, 3, 2, 3, 2, 3, 3, 1, 0, 6, 3, 3, 0, 6, 3, 3, 0, 6, 3, 3, 0, 6, 3, 3, 0, 2, 3, 1, 0, 5, 0, 5, 0, 5, 0, 5, 0, 0, 3, 0, 1, 2, 2, 2, 1, 3, 1, 1, 1, 3, 1, 0, 6, 3, 3, 0, 2, 3, 1, 0, 5, 0, 5, 0, 5, 0, 5, 1, 3, 0, 1, 1, 3, 0, 6, 2, 3, 0, 1, 1, 2, 2, 2, 4, 3, 5, 3, 1, 3, 1, 1, 3, 3, 5, 2, 5, 0, 7, 3, 5, 0, 6, 2, 0, 1, 3, 1, 0, 0, 5, 0, 3, 2, 3, 2, 3, 3, 3, 5, 5, 3, 2, 3, 3, 1, 3, 3, 5, 5, 7, 7, 7, 7, 4, 4, 4, 4, 6, 6, 3, 4, 4, 1, 3, 3, 3, 1, 3, 3, 3, 3, 0, 7, 3, 2, 0, 3, 0, 5, 0, 2, 1, 3, 2, 0, 2, 3, 0, 2, 1, 1, 1, 1, 1, 1, 0, 7, 5, 4, 4, 4, 4, 0, 2, 1, 1, 0, 7, 0, 2, 1, 4, 1, 2, 1, 1, 0, 7, 2, 2, 0, 2, 1, 1, 1, 1, 1, 0, 4, 1, 1, 2, 3, 3, 1, 2, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 2, 1, 4, 5, 3, 1, 1, 3, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 5, 7, 4, 3, 0, 6, 0, 6, 0, 6, 4, 3, 2, 2, 2, 2, 0, 6, 5, 5, 4, 3, 2, 3, 3, 2, 3, 3, 3, 3, 4, 1, 3, 1, 3, 0, 1, 1, 3, 3, 5 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state STATE-NUM when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const yytype_uint16 yydefact[] = { 3, 0, 0, 1, 0, 25, 336, 337, 346, 338, 332, 333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 348, 349, 0, 0, 0, 0, 0, 321, 0, 0, 0, 0, 0, 0, 0, 446, 0, 0, 0, 0, 334, 335, 121, 0, 0, 438, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 8, 14, 23, 33, 34, 56, 0, 35, 39, 69, 0, 40, 41, 0, 36, 47, 48, 49, 37, 134, 38, 165, 42, 187, 43, 10, 221, 0, 0, 46, 15, 16, 17, 24, 9, 11, 13, 12, 45, 44, 352, 347, 353, 461, 412, 403, 400, 401, 402, 404, 407, 405, 411, 0, 29, 0, 0, 6, 0, 346, 0, 50, 52, 0, 346, 436, 0, 0, 88, 0, 90, 0, 0, 0, 0, 467, 0, 0, 0, 0, 0, 0, 0, 189, 0, 0, 0, 0, 265, 0, 310, 0, 329, 0, 0, 0, 0, 0, 0, 0, 403, 0, 0, 0, 235, 238, 0, 0, 0, 0, 0, 0, 0, 0, 260, 0, 216, 0, 0, 0, 0, 455, 463, 0, 0, 63, 0, 300, 0, 0, 452, 0, 461, 0, 0, 0, 387, 0, 118, 461, 0, 394, 393, 360, 0, 116, 0, 399, 398, 397, 396, 395, 373, 355, 354, 356, 0, 378, 376, 392, 391, 86, 0, 0, 0, 58, 86, 71, 99, 97, 138, 169, 86, 0, 86, 222, 224, 208, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 350, 377, 375, 406, 0, 0, 0, 18, 344, 345, 339, 340, 341, 342, 0, 343, 0, 361, 55, 54, 0, 0, 60, 62, 59, 61, 89, 92, 91, 73, 75, 72, 74, 96, 0, 0, 0, 137, 136, 7, 168, 167, 190, 186, 206, 205, 28, 0, 27, 0, 331, 330, 324, 328, 0, 0, 22, 20, 21, 231, 230, 234, 0, 237, 236, 0, 253, 0, 0, 0, 0, 240, 0, 0, 259, 0, 258, 0, 26, 0, 217, 221, 221, 114, 113, 457, 456, 466, 0, 66, 86, 65, 0, 426, 427, 0, 458, 0, 0, 454, 453, 0, 459, 0, 0, 220, 0, 218, 221, 120, 117, 119, 115, 358, 357, 359, 0, 0, 0, 0, 0, 0, 0, 0, 226, 228, 0, 86, 0, 212, 214, 0, 433, 0, 0, 0, 410, 420, 424, 425, 423, 422, 421, 419, 418, 417, 416, 415, 413, 451, 0, 386, 385, 384, 383, 382, 381, 374, 379, 380, 390, 389, 388, 351, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 462, 255, 19, 254, 51, 53, 94, 0, 468, 0, 93, 0, 217, 281, 273, 0, 0, 0, 314, 322, 0, 325, 0, 0, 239, 247, 249, 0, 250, 0, 248, 0, 0, 257, 262, 263, 264, 261, 442, 0, 86, 86, 464, 0, 302, 429, 428, 0, 469, 460, 0, 445, 444, 443, 0, 86, 0, 87, 0, 0, 0, 78, 77, 82, 0, 0, 0, 109, 0, 0, 110, 111, 112, 98, 0, 141, 0, 0, 139, 0, 151, 0, 172, 0, 0, 170, 0, 0, 192, 193, 86, 227, 229, 0, 0, 225, 0, 210, 0, 434, 432, 0, 408, 0, 450, 0, 370, 369, 368, 363, 362, 366, 365, 364, 367, 372, 371, 31, 0, 0, 95, 268, 0, 0, 0, 313, 278, 274, 275, 312, 0, 327, 326, 233, 232, 0, 0, 241, 0, 0, 242, 0, 0, 441, 0, 0, 0, 67, 0, 0, 430, 0, 219, 0, 57, 0, 80, 0, 0, 0, 86, 86, 0, 122, 0, 0, 126, 0, 0, 130, 0, 0, 108, 140, 0, 164, 162, 160, 161, 0, 158, 155, 0, 0, 171, 0, 184, 185, 0, 181, 0, 0, 196, 203, 204, 0, 0, 201, 0, 194, 0, 207, 0, 0, 0, 209, 213, 0, 409, 414, 449, 448, 0, 103, 0, 271, 270, 283, 0, 0, 0, 0, 0, 0, 284, 288, 282, 287, 285, 286, 296, 267, 0, 277, 0, 316, 0, 317, 320, 319, 318, 315, 251, 252, 0, 0, 0, 0, 440, 437, 447, 0, 68, 304, 0, 0, 306, 303, 0, 470, 439, 81, 85, 84, 70, 0, 0, 125, 86, 124, 129, 86, 128, 133, 86, 132, 100, 146, 148, 0, 142, 144, 0, 135, 86, 0, 152, 177, 179, 173, 175, 183, 166, 200, 0, 198, 0, 0, 188, 223, 215, 0, 435, 32, 102, 0, 101, 0, 0, 266, 289, 0, 0, 0, 0, 279, 0, 276, 311, 243, 245, 244, 246, 64, 308, 0, 309, 307, 301, 431, 83, 0, 0, 0, 86, 0, 163, 86, 0, 159, 0, 157, 86, 0, 86, 0, 182, 197, 202, 0, 211, 106, 105, 272, 0, 221, 0, 0, 0, 298, 0, 299, 297, 280, 0, 0, 0, 0, 0, 149, 0, 145, 0, 180, 0, 176, 199, 292, 86, 0, 294, 295, 293, 305, 123, 127, 131, 0, 291, 0, 290 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { -1, 1, 2, 64, 65, 300, 66, 518, 68, 126, 127, 69, 70, 227, 71, 72, 73, 375, 712, 74, 232, 75, 76, 521, 621, 522, 523, 622, 524, 401, 77, 78, 79, 80, 404, 403, 467, 767, 529, 530, 81, 82, 83, 531, 729, 532, 732, 533, 735, 84, 236, 85, 405, 539, 798, 799, 795, 796, 540, 644, 541, 747, 640, 641, 86, 237, 87, 406, 546, 805, 806, 803, 804, 649, 650, 88, 238, 89, 548, 549, 550, 551, 657, 658, 90, 91, 92, 665, 93, 557, 94, 391, 392, 240, 412, 413, 241, 242, 95, 96, 97, 172, 98, 99, 176, 100, 179, 180, 101, 332, 474, 475, 768, 478, 588, 589, 694, 584, 687, 688, 816, 689, 690, 691, 775, 823, 102, 377, 609, 718, 788, 103, 334, 479, 591, 702, 104, 160, 339, 340, 105, 106, 301, 107, 108, 449, 109, 110, 111, 668, 112, 183, 113, 201, 366, 393, 114, 184, 115, 116, 117, 118, 119, 189, 373, 142, 200 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ #define YYPACT_NINF -575 static const yytype_int16 yypact[] = { -575, 77, 869, -575, 93, -575, -575, -575, 24, -575, -575, -575, 118, 23, 3715, 94, 280, 3787, 296, 3859, 87, 3931, -575, -575, 222, 4003, 370, 373, 3499, -575, 342, 4075, 511, 362, 372, 526, 283, -575, 4147, 5585, 270, 180, -575, -575, -575, 5945, 5418, -575, 5945, 3571, 5945, 5945, 5945, 3643, 5945, 5945, 5945, 5945, 5945, 5945, 360, 5945, 5945, 264, -575, -575, -575, -575, -575, -575, -575, -575, 3427, -575, -575, -575, 3427, -575, -575, 323, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, 156, 3427, 26, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, 4952, -575, -575, -575, -575, -575, -575, -575, -575, -575, 276, -575, 46, 5945, -575, 210, -575, 139, -575, 147, 385, 168, -575, 4775, 271, -575, 301, -575, 337, 407, 4848, 413, 356, -17, 446, 5004, 475, 486, 5056, 502, -575, 3427, 503, 5108, 520, -575, 523, -575, 540, -575, 5160, 532, 547, 557, 560, 561, 6496, 562, 565, 460, 566, -575, -575, 151, 568, 101, 453, 158, 570, 507, 186, -575, 575, -575, 27, 27, 580, 5212, -575, 6496, 58, 593, -575, 3427, -575, 6182, 5657, -575, 541, 6006, 142, 149, 100, 6696, 597, -575, 6496, 194, 294, 294, 303, 598, -575, 197, 303, 303, 303, 303, 303, 303, -575, -575, -575, 188, 303, 303, -575, -575, -575, 601, 605, 125, -575, -575, -575, -575, -575, -575, -575, -575, 341, -575, -575, -575, -575, 606, 140, -575, 5729, 5513, 604, 5945, 5945, 5945, 5945, 5945, 5945, 5945, 5945, 5945, 5945, 5945, 5945, 4219, 5945, 5945, 5945, 5945, 5945, 5945, 5945, 5945, 5945, 607, 5945, 5945, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, -575, -575, -575, 5945, 5945, 619, -575, -575, -575, -575, -575, -575, -575, 620, -575, 622, 6496, -575, -575, 621, 5945, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, 5945, 621, 4291, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, 521, -575, 420, -575, -575, -575, -575, 200, 184, -575, -575, -575, -575, -575, -575, 123, -575, -575, 627, -575, 625, 31, 57, 632, -575, 461, 630, -575, 65, -575, 633, -575, 634, 636, 156, 156, -575, -575, -575, -575, -575, 5945, -575, -575, -575, 635, -575, -575, 6234, -575, 5801, 5945, -575, -575, 583, -575, 5945, 581, -575, 134, -575, 156, -575, -575, -575, -575, -575, -575, -575, 1919, 1571, 991, 3427, 515, 631, 1687, 427, -575, -575, 2035, -575, 3427, -575, -575, 138, -575, 152, 5945, 6068, -575, 6496, 6496, 6496, 6496, 6496, 6496, 6496, 6496, 6496, 6496, 6496, 232, -575, 4702, 6646, 6696, 489, 489, 489, 489, 489, 489, 489, -575, 294, 294, -575, 5945, 5945, 5945, 5945, 5945, 5945, 5945, 5945, 5945, 5945, 5945, 4900, 6546, -575, -575, -575, -575, 6496, -575, 6286, -575, 641, 6496, 643, 636, -575, 614, 654, 652, 656, -575, -575, 543, -575, 658, 659, -575, -575, -575, 670, -575, 671, -575, 8, 53, -575, -575, -575, -575, -575, -575, 204, -575, -575, 6496, 2151, -575, -575, -575, 6130, 6496, -575, 6340, -575, -575, -575, 636, -575, 675, -575, 431, 4363, 669, -575, -575, -575, 381, 421, 425, -575, 673, 991, -575, -575, -575, -575, 681, -575, 72, 449, -575, 676, -575, 682, -575, 154, 677, -575, 116, 679, 672, -575, -575, -575, -575, 710, 2267, -575, 661, -575, 444, -575, -575, 6392, -575, 5945, -575, 4435, 399, 399, 516, 549, 549, 405, 405, 405, 299, 303, 303, -575, 5945, 4507, -575, -575, 208, 484, 714, -575, 662, 642, -575, -575, 335, -575, -575, -575, -575, 716, 718, -575, 717, 719, -575, 720, 721, -575, 727, 2383, 2499, 5945, 531, 5945, -575, 5945, -575, 2615, -575, 728, -575, 729, 5264, 730, -575, -575, 732, -575, 3427, 734, -575, 3427, 735, -575, 3427, 737, -575, -575, 471, -575, -575, -575, 645, 223, -575, -575, 738, 492, -575, 508, -575, -575, 225, -575, 739, 740, -575, -575, -575, 621, 55, -575, 742, -575, 1803, -575, 743, 680, 693, -575, -575, 694, -575, 674, -575, 6596, 201, -575, 4640, -575, -575, -575, 37, 747, 748, 749, 751, 319, -575, -575, -575, -575, -575, -575, -575, -575, 5873, -575, 652, -575, 755, -575, -575, -575, -575, -575, -575, -575, 756, 757, 758, 759, -575, -575, -575, 760, 6496, -575, 221, 761, -575, -575, 6444, 6496, -575, -575, -575, -575, -575, 2731, 1571, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, 10, -575, -575, 15, -575, -575, 3427, -575, -575, -575, -575, -575, 551, -575, -575, 762, -575, 552, 621, -575, -575, -575, 763, -575, -575, -575, 4579, -575, 699, 5945, -575, -575, 692, 711, 712, 174, -575, 133, -575, -575, -575, -575, -575, -575, -575, -575, 85, -575, -575, -575, -575, -575, 2847, 2963, 3079, -575, 3427, -575, -575, 3427, -575, 3195, -575, -575, 3427, -575, 3427, -575, -575, -575, 767, -575, -575, 6496, -575, 5316, 156, 85, 769, 770, -575, 771, -575, -575, -575, 207, 772, 775, 777, 1107, -575, 1223, -575, 1339, -575, 1455, -575, -575, -575, -575, 778, -575, -575, -575, -575, -575, -575, -575, 3311, -575, 779, -575 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { -575, -575, -575, -575, -575, -357, -575, -2, -575, -575, 478, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, 59, -575, -575, -575, -575, -575, 60, -575, -575, -575, -575, -575, -575, -575, -575, 254, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, 379, -575, -575, -575, -575, 44, -575, -575, -575, -575, -575, -575, -575, -575, -575, -575, 36, -575, -575, -575, -575, -575, 241, -575, -575, 34, -575, -574, -575, -575, -575, -575, -575, -240, 278, -336, -575, -575, -575, -575, -575, -575, -575, -575, -575, -304, -575, -575, -575, 434, -575, -575, -575, -575, -575, 324, -575, 103, -575, -575, -575, 209, -575, 212, -575, -575, -575, -575, -575, -575, -575, -575, -24, -575, -575, -575, -575, -575, -575, -575, -575, 325, -575, -575, -338, -11, -575, 389, -13, 268, 776, -575, -575, -575, -575, -575, 624, -575, -575, -575, -575, -575, -575, -575, -33, -575, -575, -575, -575 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -466 static const yytype_int16 yytable[] = { 67, 132, 128, 497, 139, 416, 144, 141, 147, 484, 685, 598, 152, 199, 292, 159, 206, 698, 165, 292, 212, 636, 637, 638, 124, 186, 188, 244, 364, 125, 501, 502, 194, 198, 487, 202, 205, 207, 208, 209, 205, 213, 214, 215, 216, 217, 218, 291, 223, 224, 292, 293, 226, 294, 295, 296, 601, 516, 756, 370, 489, 320, -465, -465, -465, -465, -465, -465, 121, 292, 231, 495, 496, 635, 233, 321, 292, 3, 636, 637, 638, 121, 245, 365, 599, -465, -465, 122, 145, 292, 293, 243, 294, 295, 296, 133, 120, 134, 297, 298, 600, 389, 352, -465, -256, -465, 390, -465, 299, 302, -465, -465, 769, 299, -465, 371, -465, 652, -465, 653, 654, 123, 655, 488, 483, 500, 181, 292, 293, 602, 294, 295, 296, 757, -256, 513, 372, 297, 298, 558, -465, 415, 304, 384, 299, 603, 390, 758, 328, 490, 386, -465, -465, 560, 349, 646, -465, -183, 647, 355, 648, 356, -217, 299, -465, -465, -465, -465, -465, -465, 299, -465, -465, -465, -465, 297, 298, 820, -217, -256, 639, -436, 380, 299, 821, 482, 193, -323, 824, 361, 376, 357, 398, 559, 399, -217, 514, 395, -183, 385, 397, 822, 239, 480, 764, 604, 387, 561, 656, 676, 785, 290, 515, 303, 418, 400, 515, 305, -217, 682, 290, 299, 306, 148, 785, 149, 741, 388, 750, 350, 290, 122, -183, 583, 205, 420, 358, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 605, 446, 447, -323, 677, 362, 121, 150, 742, 225, 751, 125, 190, 290, 191, 311, 290, 460, 461, 481, 290, 686, 135, 515, 136, 181, 787, 515, 699, 247, 182, 248, 249, 402, 466, 128, 137, 786, 140, 407, 787, 411, 743, 125, 752, 312, 717, 468, 261, 471, 469, 564, 262, 263, 264, 192, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 773, 234, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 696, -436, 313, 679, 408, 161, 409, 697, 286, 287, 162, 163, 247, 289, 248, 249, 290, 247, 774, 248, 249, 247, 503, 248, 249, 169, 219, 170, 220, 235, 171, 508, 509, 153, 681, 173, 155, 511, 154, 288, 174, 156, 682, 683, 623, 797, 624, 410, 639, 221, 307, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 288, 528, 534, 284, 285, 175, 562, 288, 286, 287, 314, 556, 288, 286, 287, 288, 318, 286, 287, 222, 288, 476, 626, -273, 627, 625, 629, 288, 630, 308, 552, 319, 616, 288, 617, 504, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 666, 825, 322, 642, 315, -154, 477, 288, 247, 288, 248, 249, 353, 354, 247, 288, 248, 249, 628, 288, 492, 493, 631, 288, 553, 555, 288, 738, 288, 288, 288, 324, 840, 839, 288, 288, 288, 288, 288, 288, 678, 667, 325, 679, 288, 288, -154, 680, 745, 277, 278, 279, 280, 281, 282, 283, 284, 285, 327, 329, 619, 283, 284, 285, 748, 167, 286, 287, 739, 535, 168, 536, 286, 287, 681, 472, 331, -269, -150, 333, 177, 528, 682, 683, 670, 178, 337, 714, 347, 746, 715, 338, 537, 538, 716, 684, 335, 592, 247, 673, 248, 249, 338, 341, 205, 749, 672, -269, 647, 654, 648, 655, -153, 342, 606, 607, 343, 344, 345, 205, 675, 346, 348, 288, 351, 247, 359, 248, 249, 614, 473, 363, 272, 273, 274, 360, 368, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 713, 374, 719, 381, 720, 394, 396, 153, 286, 287, 247, 155, 248, 249, 414, 421, 661, 448, 445, 278, 279, 280, 281, 282, 283, 284, 285, 462, 730, 463, 464, 733, 125, 486, 736, 286, 287, 542, 485, 543, 491, 494, 499, 505, 178, 510, -150, 390, 512, 581, 755, 582, 477, 288, 280, 281, 282, 283, 284, 285, 544, 538, 586, 587, 590, 777, 594, 595, 286, 287, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, -153, 596, 597, 615, 620, 205, 726, 727, 632, 634, 645, 643, 651, 288, 659, 288, 288, 288, 288, 288, 288, 288, 288, 288, 288, 288, 288, 547, 288, 288, 288, 288, 288, 288, 288, 288, 288, 288, 662, 288, 288, 664, 692, 693, 703, 695, 704, 740, 705, 761, 706, 707, 708, 288, 288, 709, 722, 723, 725, 288, 728, 288, 731, 734, 288, 737, 744, 753, 754, 802, 759, 760, 810, 762, 763, 770, 771, 290, 813, 814, 182, 815, 772, 779, 780, 781, 782, 783, 784, 789, 808, 811, 817, 818, 819, 837, 288, 841, 842, 843, 845, 288, 288, 846, 288, 847, 849, 851, 465, 633, 545, 791, 800, 807, 792, 660, 809, 793, 613, 830, 794, 498, 832, 778, 585, 700, 844, 834, 701, 836, 801, 593, 166, 367, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 288, 0, 0, 0, 0, 288, 288, 288, 288, 288, 288, 288, 288, 288, 288, 288, 0, 0, 0, 0, 0, 0, 0, 0, 0, 829, 0, 0, 831, 0, 0, 0, 0, 833, 0, 835, 0, 0, 0, -2, 4, 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 17, 0, 288, 18, 0, 0, 0, 19, 20, 0, 21, 22, 23, 24, 848, 25, 26, 0, 27, 28, 29, 0, 30, 31, 32, 33, 34, 35, 0, 36, 0, 37, 38, 39, 40, 41, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 288, 0, 0, 288, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 288, 60, 61, 62, 63, 0, 288, 288, 0, 0, 0, 4, 0, 5, 6, 7, 8, 9, 10, 11, -107, 13, 14, 15, 16, 0, 17, 0, 0, 18, 525, 526, 527, 19, 0, 0, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 288, 0, 288, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, -147, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, -147, -147, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, -147, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, -143, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, -143, -143, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, -143, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, -178, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, -178, -178, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, -178, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, -174, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, -174, -174, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, -174, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, -76, 13, 14, 15, 16, 0, 17, 519, 520, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, -191, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, 547, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, -195, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, -195, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, 517, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, 554, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, 608, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, 663, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, 710, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, 711, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, 0, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 721, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, -79, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, 826, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, 827, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, 828, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, -156, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, 850, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 4, 0, 5, 6, 7, 8, 9, 10, 11, 0, 13, 14, 15, 16, 0, 17, 0, 0, 18, 0, 0, 0, 19, 0, 0, 21, 22, 23, 24, 0, 25, 228, 0, 229, 28, 29, 0, 0, 31, 0, 0, 0, 0, 0, 230, 0, 37, 38, 39, 40, 0, 42, 43, 44, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 157, 0, 158, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 53, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, 0, 204, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 210, 0, 211, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 138, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 143, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 146, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 151, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 185, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 434, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 470, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 618, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 671, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 674, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 812, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 765, 0, -104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, -104, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 247, 0, 248, 249, 0, 0, 0, 565, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 766, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 566, 0, 0, 0, 0, 0, 0, 0, 286, 287, 0, 0, 247, 0, 248, 249, 0, 0, 0, 0, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 309, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 0, 0, 0, 0, 0, 0, 0, 286, 287, 0, 310, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 247, 0, 248, 249, 0, 0, 0, 0, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 316, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 0, 0, 0, 0, 0, 0, 0, 286, 287, 0, 317, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 578, 247, 0, 248, 249, 0, 0, 0, 0, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 246, 247, 0, 248, 249, 0, 0, 286, 287, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 579, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 323, 247, 0, 248, 249, 0, 0, 286, 287, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 326, 247, 0, 248, 249, 0, 0, 286, 287, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 330, 247, 0, 248, 249, 0, 0, 286, 287, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 336, 247, 0, 248, 249, 0, 0, 286, 287, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 369, 247, 0, 248, 249, 0, 0, 286, 287, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 724, 247, 0, 248, 249, 0, 0, 286, 287, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 838, 247, 0, 248, 249, 0, 0, 286, 287, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 0, 247, 0, 248, 249, 0, 0, 286, 287, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 6, 7, 130, 9, 10, 11, 0, 0, 286, 287, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 195, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 196, 46, 0, 47, 0, 197, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 6, 7, 130, 9, 10, 11, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 0, 0, 0, 0, 0, 22, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 195, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 419, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 187, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 379, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 417, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 507, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 776, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 130, 9, 10, 11, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 22, 23, 0, 0, 0, 0, 0, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 131, 0, 37, 0, 39, 0, 0, 42, 43, 0, 0, 45, 0, 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 382, 54, 55, 56, 57, 58, 59, 0, 60, 61, 62, 63, 247, 0, 248, 249, 0, 0, 383, 0, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 382, 0, 0, 0, 0, 0, 0, 0, 286, 287, 0, 0, 247, 563, 248, 249, 0, 0, 0, 0, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 610, 0, 0, 0, 0, 0, 0, 0, 286, 287, 0, 0, 247, 611, 248, 249, 0, 0, 0, 0, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 378, 247, 0, 248, 249, 0, 0, 286, 287, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 0, 247, 506, 248, 249, 0, 0, 286, 287, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 0, 247, 0, 248, 249, 0, 0, 286, 287, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 580, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 0, 0, 0, 247, 0, 248, 249, 286, 287, 612, 0, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 0, 247, 669, 248, 249, 0, 0, 286, 287, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 0, 247, 790, 248, 249, 0, 0, 286, 287, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 0, 247, 0, 248, 249, 0, 0, 286, 287, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 247, 0, 248, 249, 0, 0, 0, 0, 286, 287, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 247, 0, 248, 249, 0, 0, 0, 0, 286, 287, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 263, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 247, 0, 248, 249, 0, 0, 0, 0, 286, 287, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 264, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 247, 0, 248, 249, 0, 0, 0, 0, 286, 287, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 0, 0, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 0, 0, 0, 0, 0, 0, 0, 0, 286, 287 }; static const yytype_int16 yycheck[] = { 2, 14, 13, 360, 17, 245, 19, 18, 21, 347, 584, 3, 25, 46, 4, 28, 49, 591, 31, 4, 53, 6, 7, 8, 1, 38, 39, 1, 1, 6, 366, 367, 45, 46, 3, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 1, 61, 62, 4, 5, 63, 7, 8, 9, 3, 393, 3, 1, 3, 78, 4, 5, 6, 7, 8, 9, 44, 4, 72, 6, 7, 1, 76, 92, 4, 0, 6, 7, 8, 44, 56, 56, 76, 27, 28, 63, 1, 4, 5, 93, 7, 8, 9, 1, 3, 3, 52, 53, 92, 1, 1, 45, 3, 47, 6, 49, 98, 122, 52, 53, 75, 98, 56, 57, 58, 1, 60, 3, 4, 3, 6, 92, 1, 365, 1, 4, 5, 76, 7, 8, 9, 78, 33, 1, 78, 52, 53, 1, 82, 1, 3, 1, 98, 92, 6, 92, 150, 92, 1, 93, 94, 1, 3, 1, 98, 3, 4, 1, 6, 3, 62, 98, 106, 107, 108, 109, 110, 111, 98, 113, 114, 115, 116, 52, 53, 3, 78, 78, 537, 56, 195, 98, 10, 1, 6, 3, 55, 3, 192, 33, 4, 55, 6, 55, 62, 3, 44, 57, 3, 775, 46, 3, 3, 1, 57, 55, 92, 1, 3, 78, 78, 3, 247, 27, 78, 78, 78, 45, 78, 98, 75, 1, 3, 3, 3, 78, 3, 78, 78, 63, 78, 473, 247, 248, 78, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 55, 273, 274, 78, 55, 78, 44, 44, 44, 4, 44, 6, 1, 78, 3, 3, 78, 289, 290, 78, 78, 584, 1, 78, 3, 1, 78, 78, 591, 56, 6, 58, 59, 232, 306, 305, 15, 75, 1, 238, 78, 240, 78, 6, 78, 3, 609, 319, 75, 321, 320, 78, 79, 80, 81, 44, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 6, 3, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 3, 56, 3, 6, 1, 1, 3, 10, 114, 115, 6, 7, 56, 75, 58, 59, 78, 56, 37, 58, 59, 56, 373, 58, 59, 1, 4, 3, 6, 44, 6, 382, 383, 1, 37, 1, 1, 388, 6, 109, 6, 6, 45, 46, 1, 740, 3, 44, 743, 27, 3, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 132, 403, 404, 104, 105, 33, 419, 139, 114, 115, 3, 413, 144, 114, 115, 147, 3, 114, 115, 59, 152, 1, 1, 3, 3, 44, 1, 159, 3, 44, 3, 75, 1, 165, 3, 375, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 3, 786, 3, 1, 44, 3, 33, 186, 56, 188, 58, 59, 6, 7, 56, 194, 58, 59, 44, 198, 6, 7, 44, 202, 44, 412, 205, 3, 207, 208, 209, 3, 817, 816, 213, 214, 215, 216, 217, 218, 3, 44, 3, 6, 223, 224, 44, 10, 3, 97, 98, 99, 100, 101, 102, 103, 104, 105, 3, 3, 520, 103, 104, 105, 3, 1, 114, 115, 44, 1, 6, 3, 114, 115, 37, 1, 3, 3, 10, 3, 1, 530, 45, 46, 564, 6, 1, 3, 75, 44, 6, 6, 24, 25, 10, 58, 3, 1, 56, 579, 58, 59, 6, 3, 564, 44, 566, 33, 4, 4, 6, 6, 44, 3, 501, 502, 3, 3, 3, 579, 580, 3, 3, 302, 3, 56, 3, 58, 59, 516, 56, 3, 90, 91, 92, 75, 3, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 608, 3, 610, 57, 612, 3, 3, 1, 114, 115, 56, 1, 58, 59, 3, 6, 551, 3, 6, 98, 99, 100, 101, 102, 103, 104, 105, 3, 625, 4, 3, 628, 6, 3, 631, 114, 115, 1, 6, 3, 3, 6, 3, 3, 6, 57, 10, 6, 62, 3, 656, 3, 33, 380, 100, 101, 102, 103, 104, 105, 24, 25, 3, 6, 3, 693, 3, 3, 114, 115, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 44, 6, 6, 3, 10, 693, 621, 622, 10, 3, 3, 10, 10, 420, 10, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 30, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 3, 446, 447, 55, 3, 56, 3, 78, 3, 77, 6, 44, 6, 6, 6, 460, 461, 3, 3, 3, 3, 466, 3, 468, 3, 3, 471, 3, 3, 3, 3, 746, 3, 3, 758, 55, 55, 3, 3, 78, 766, 55, 6, 769, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 75, 57, 57, 3, 503, 3, 3, 3, 3, 508, 509, 3, 511, 3, 3, 3, 305, 530, 406, 727, 743, 752, 729, 549, 757, 732, 515, 796, 735, 362, 799, 695, 475, 591, 825, 804, 591, 806, 745, 481, 31, 184, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 562, -1, -1, -1, -1, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, -1, -1, -1, -1, -1, -1, -1, -1, -1, 795, -1, -1, 798, -1, -1, -1, -1, 803, -1, 805, -1, -1, -1, 0, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, 619, 19, -1, -1, -1, 23, 24, -1, 26, 27, 28, 29, 839, 31, 32, -1, 34, 35, 36, -1, 38, 39, 40, 41, 42, 43, -1, 45, -1, 47, 48, 49, 50, 51, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 672, -1, -1, 675, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, 713, 113, 114, 115, 116, -1, 719, 720, -1, -1, -1, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, 20, 21, 22, 23, -1, -1, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, 813, -1, 815, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, 24, 25, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, 44, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, 24, 25, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, 44, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, 24, 25, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, 44, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, 24, 25, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, 44, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, 17, 18, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, 30, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, 30, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, -1, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, 61, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 1, -1, 3, 4, 5, 6, 7, 8, 9, -1, 11, 12, 13, 14, -1, 16, -1, -1, 19, -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, -1, 31, 32, -1, 34, 35, 36, -1, -1, 39, -1, -1, -1, -1, -1, 45, -1, 47, 48, 49, 50, -1, 52, 53, 54, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 3, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, 86, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, 104, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 3, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 3, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, 1, -1, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, -1, 44, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 56, -1, 58, 59, -1, -1, -1, 1, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, 78, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 44, -1, -1, -1, -1, -1, -1, -1, 114, 115, -1, -1, 56, -1, 58, 59, -1, -1, -1, -1, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 3, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, -1, -1, -1, -1, -1, -1, -1, 114, 115, -1, 44, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 56, -1, 58, 59, -1, -1, -1, -1, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 3, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, -1, -1, -1, -1, -1, -1, -1, 114, 115, -1, 44, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 56, -1, 58, 59, -1, -1, -1, -1, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, 3, 56, -1, 58, 59, -1, -1, 114, 115, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, 78, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, 3, 56, -1, 58, 59, -1, -1, 114, 115, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, 3, 56, -1, 58, 59, -1, -1, 114, 115, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, 3, 56, -1, 58, 59, -1, -1, 114, 115, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, 3, 56, -1, 58, 59, -1, -1, 114, 115, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, 3, 56, -1, 58, 59, -1, -1, 114, 115, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, 3, 56, -1, 58, 59, -1, -1, 114, 115, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, 3, 56, -1, 58, 59, -1, -1, 114, 115, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, -1, 56, -1, 58, 59, -1, -1, 114, 115, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 4, 5, 6, 7, 8, 9, -1, -1, 114, 115, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 27, 28, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 44, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, 57, 58, -1, 60, -1, 62, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 4, 5, 6, 7, 8, 9, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, -1, -1, -1, -1, -1, 27, 28, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 44, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, 102, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, 57, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, 57, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, 55, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, 57, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, 55, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, 27, 28, -1, -1, -1, -1, -1, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 45, -1, 47, -1, 49, -1, -1, 52, 53, -1, -1, 56, -1, 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, 94, -1, -1, -1, 98, -1, -1, -1, -1, -1, -1, 44, 106, 107, 108, 109, 110, 111, -1, 113, 114, 115, 116, 56, -1, 58, 59, -1, -1, 62, -1, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 44, -1, -1, -1, -1, -1, -1, -1, 114, 115, -1, -1, 56, 57, 58, 59, -1, -1, -1, -1, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 44, -1, -1, -1, -1, -1, -1, -1, 114, 115, -1, -1, 56, 57, 58, 59, -1, -1, -1, -1, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, 55, 56, -1, 58, 59, -1, -1, 114, 115, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, -1, 56, 57, 58, 59, -1, -1, 114, 115, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, -1, 56, -1, 58, 59, -1, -1, 114, 115, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, 77, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, -1, -1, -1, 56, -1, 58, 59, 114, 115, 62, -1, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, -1, 56, 57, 58, 59, -1, -1, 114, 115, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, -1, 56, 57, 58, 59, -1, -1, 114, 115, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, -1, 56, -1, 58, 59, -1, -1, 114, 115, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 56, -1, 58, 59, -1, -1, -1, -1, 114, 115, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 79, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 56, -1, 58, 59, -1, -1, -1, -1, 114, 115, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 80, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 56, -1, 58, 59, -1, -1, -1, -1, 114, 115, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 81, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 56, -1, 58, 59, -1, -1, -1, -1, 114, 115, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, -1, -1, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, -1, -1, -1, -1, -1, -1, -1, -1, 114, 115 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint16 yystos[] = { 0, 118, 119, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 19, 23, 24, 26, 27, 28, 29, 31, 32, 34, 35, 36, 38, 39, 40, 41, 42, 43, 45, 47, 48, 49, 50, 51, 52, 53, 54, 56, 58, 60, 82, 86, 93, 94, 98, 104, 106, 107, 108, 109, 110, 111, 113, 114, 115, 116, 120, 121, 123, 124, 125, 128, 129, 131, 132, 133, 136, 138, 139, 147, 148, 149, 150, 157, 158, 159, 166, 168, 181, 183, 192, 194, 201, 202, 203, 205, 207, 215, 216, 217, 219, 220, 222, 225, 243, 248, 253, 257, 258, 260, 261, 263, 264, 265, 267, 269, 273, 275, 276, 277, 278, 279, 3, 44, 63, 3, 1, 6, 126, 127, 260, 1, 6, 45, 263, 1, 3, 1, 3, 15, 1, 263, 1, 260, 282, 1, 263, 1, 1, 263, 1, 3, 44, 1, 263, 1, 6, 1, 6, 1, 3, 263, 254, 1, 6, 7, 1, 263, 265, 1, 6, 1, 3, 6, 218, 1, 6, 33, 221, 1, 6, 223, 224, 1, 6, 268, 274, 1, 263, 57, 263, 280, 1, 3, 44, 6, 263, 44, 57, 62, 263, 279, 283, 270, 263, 1, 3, 263, 279, 263, 263, 263, 1, 3, 279, 263, 263, 263, 263, 263, 263, 4, 6, 27, 59, 263, 263, 4, 260, 130, 32, 34, 45, 124, 137, 124, 3, 44, 167, 182, 193, 46, 210, 213, 214, 124, 1, 56, 3, 56, 58, 59, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 79, 80, 81, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 114, 115, 264, 75, 78, 1, 4, 5, 7, 8, 9, 52, 53, 98, 122, 259, 263, 3, 3, 78, 75, 3, 44, 3, 44, 3, 3, 3, 3, 44, 3, 44, 3, 75, 78, 92, 3, 3, 3, 3, 3, 3, 124, 3, 3, 3, 226, 3, 249, 3, 3, 1, 6, 255, 256, 3, 3, 3, 3, 3, 3, 75, 3, 3, 78, 3, 1, 6, 7, 1, 3, 33, 78, 3, 75, 3, 78, 3, 1, 56, 271, 271, 3, 3, 1, 57, 78, 281, 3, 134, 124, 244, 55, 57, 263, 57, 44, 62, 1, 57, 1, 57, 78, 1, 6, 208, 209, 272, 3, 3, 3, 3, 4, 6, 27, 146, 146, 152, 151, 169, 184, 146, 1, 3, 44, 146, 211, 212, 3, 1, 208, 55, 279, 102, 263, 6, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 1, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 6, 263, 263, 3, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 262, 263, 263, 3, 4, 3, 127, 263, 153, 263, 260, 1, 263, 1, 56, 227, 228, 1, 33, 230, 250, 3, 78, 1, 1, 259, 6, 3, 3, 92, 3, 92, 3, 6, 7, 6, 6, 7, 122, 224, 3, 208, 210, 210, 263, 146, 3, 57, 57, 263, 263, 57, 263, 62, 1, 62, 78, 210, 10, 124, 17, 18, 140, 142, 143, 145, 20, 21, 22, 124, 155, 156, 160, 162, 164, 124, 1, 3, 24, 25, 170, 175, 177, 1, 3, 24, 175, 185, 30, 195, 196, 197, 198, 3, 44, 10, 146, 124, 206, 1, 55, 1, 55, 263, 57, 78, 1, 44, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 3, 78, 77, 3, 3, 208, 234, 230, 3, 6, 231, 232, 3, 251, 1, 256, 3, 3, 6, 6, 3, 76, 92, 3, 76, 92, 1, 55, 146, 146, 10, 245, 44, 57, 62, 209, 146, 3, 1, 3, 1, 263, 10, 141, 144, 1, 3, 44, 1, 3, 44, 1, 3, 44, 10, 155, 3, 1, 6, 7, 8, 122, 179, 180, 1, 10, 176, 3, 1, 4, 6, 190, 191, 10, 1, 3, 4, 6, 92, 199, 200, 10, 197, 146, 3, 10, 55, 204, 3, 44, 266, 57, 279, 1, 263, 279, 1, 263, 1, 55, 3, 6, 10, 37, 45, 46, 58, 202, 220, 235, 236, 238, 239, 240, 3, 56, 233, 78, 3, 10, 202, 220, 236, 238, 252, 3, 3, 6, 6, 6, 6, 3, 10, 10, 135, 263, 3, 6, 10, 220, 246, 263, 263, 61, 3, 3, 3, 3, 146, 146, 3, 161, 124, 3, 163, 124, 3, 165, 124, 3, 3, 44, 77, 3, 44, 78, 3, 3, 44, 178, 3, 44, 3, 44, 78, 3, 3, 260, 3, 78, 92, 3, 3, 44, 55, 55, 3, 1, 78, 154, 229, 75, 3, 3, 6, 6, 37, 241, 55, 279, 232, 3, 3, 3, 3, 3, 3, 3, 75, 78, 247, 3, 57, 140, 146, 146, 146, 173, 174, 122, 171, 172, 180, 146, 124, 188, 189, 186, 187, 191, 3, 200, 260, 3, 1, 263, 55, 263, 237, 75, 57, 57, 3, 10, 202, 242, 55, 259, 10, 10, 10, 146, 124, 146, 124, 146, 124, 146, 124, 3, 3, 210, 259, 3, 3, 3, 247, 3, 3, 3, 146, 3, 10, 3 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. Once GCC version 2 has supplanted version 1, this can go. */ #define YYFAIL goto yyerrlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY && yylen == 1) \ { \ yychar = (Token); \ yylval = (Value); \ yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ else \ { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (YYID (0)) #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #define YYRHSLOC(Rhs, K) ((Rhs)[K]) #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (YYID (N)) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (YYID (0)) #endif /* YY_LOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT # if YYLTYPE_IS_TRIVIAL # define YY_LOCATION_PRINT(File, Loc) \ fprintf (File, "%d.%d-%d.%d", \ (Loc).first_line, (Loc).first_column, \ (Loc).last_line, (Loc).last_column) # else # define YY_LOCATION_PRINT(File, Loc) ((void) 0) # endif #endif /* YYLEX -- calling `yylex' with the right arguments. */ #ifdef YYLEX_PARAM # define YYLEX yylex (&yylval, YYLEX_PARAM) #else # define YYLEX yylex (&yylval) #endif /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (YYID (0)) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value); \ YYFPRINTF (stderr, "\n"); \ } \ } while (YYID (0)) /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) #else static void yy_symbol_value_print (yyoutput, yytype, yyvaluep) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; #endif { if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # else YYUSE (yyoutput); # endif switch (yytype) { default: break; } } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) #else static void yy_symbol_print (yyoutput, yytype, yyvaluep) FILE *yyoutput; int yytype; YYSTYPE const * const yyvaluep; #endif { if (yytype < YYNTOKENS) YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); else YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); yy_symbol_value_print (yyoutput, yytype, yyvaluep); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) #else static void yy_stack_print (yybottom, yytop) yytype_int16 *yybottom; yytype_int16 *yytop; #endif { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (YYID (0)) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yy_reduce_print (YYSTYPE *yyvsp, int yyrule) #else static void yy_reduce_print (yyvsp, yyrule) YYSTYPE *yyvsp; int yyrule; #endif { int yynrhs = yyr2[yyrule]; int yyi; unsigned long int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) ); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyvsp, Rule); \ } while (YYID (0)) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static YYSIZE_T yystrlen (const char *yystr) #else static YYSIZE_T yystrlen (yystr) const char *yystr; #endif { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static char * yystpcpy (char *yydest, const char *yysrc) #else static char * yystpcpy (yydest, yysrc) char *yydest; const char *yysrc; #endif { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into YYRESULT an error message about the unexpected token YYCHAR while in state YYSTATE. Return the number of bytes copied, including the terminating null byte. If YYRESULT is null, do not copy anything; just return the number of bytes that would be copied. As a special case, return 0 if an ordinary "syntax error" message will do. Return YYSIZE_MAXIMUM if overflow occurs during size calculation. */ static YYSIZE_T yysyntax_error (char *yyresult, int yystate, int yychar) { int yyn = yypact[yystate]; if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) return 0; else { int yytype = YYTRANSLATE (yychar); YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); YYSIZE_T yysize = yysize0; YYSIZE_T yysize1; int yysize_overflow = 0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; int yyx; # if 0 /* This is so xgettext sees the translatable formats that are constructed on the fly. */ YY_("syntax error, unexpected %s"); YY_("syntax error, unexpected %s, expecting %s"); YY_("syntax error, unexpected %s, expecting %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); # endif char *yyfmt; char const *yyf; static char const yyunexpected[] = "syntax error, unexpected %s"; static char const yyexpecting[] = ", expecting %s"; static char const yyor[] = " or %s"; char yyformat[sizeof yyunexpected + sizeof yyexpecting - 1 + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) * (sizeof yyor - 1))]; char const *yyprefix = yyexpecting; /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yycount = 1; yyarg[0] = yytname[yytype]; yyfmt = yystpcpy (yyformat, yyunexpected); for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; yyformat[sizeof yyunexpected - 1] = '\0'; break; } yyarg[yycount++] = yytname[yyx]; yysize1 = yysize + yytnamerr (0, yytname[yyx]); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; yyfmt = yystpcpy (yyfmt, yyprefix); yyprefix = yyor; } yyf = YY_(yyformat); yysize1 = yysize + yystrlen (yyf); yysize_overflow |= (yysize1 < yysize); yysize = yysize1; if (yysize_overflow) return YYSIZE_MAXIMUM; if (yyresult) { /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ char *yyp = yyresult; int yyi = 0; while ((*yyp = *yyf) != '\0') { if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyf += 2; } else { yyp++; yyf++; } } } return yysize; } } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ /*ARGSUSED*/ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) #else static void yydestruct (yymsg, yytype, yyvaluep) const char *yymsg; int yytype; YYSTYPE *yyvaluep; #endif { YYUSE (yyvaluep); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); switch (yytype) { default: break; } } /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); #else int yyparse (); #endif #else /* ! YYPARSE_PARAM */ #if defined __STDC__ || defined __cplusplus int yyparse (void); #else int yyparse (); #endif #endif /* ! YYPARSE_PARAM */ /*-------------------------. | yyparse or yypush_parse. | `-------------------------*/ #ifdef YYPARSE_PARAM #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void *YYPARSE_PARAM) #else int yyparse (YYPARSE_PARAM) void *YYPARSE_PARAM; #endif #else /* ! YYPARSE_PARAM */ #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) int yyparse (void) #else int yyparse () #endif #endif { /* The lookahead symbol. */ int yychar; /* The semantic value of the lookahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: `yyss': related to states. `yyvs': related to semantic values. Refer to the stacks thru separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; YYSIZE_T yystacksize; int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ int yytoken; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; yytoken = 0; yyss = yyssa; yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ yyssp = yyss; yyvsp = yyvs; goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yyn == 0 || yyn == YYTABLE_NINF) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token. */ yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: `$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; YY_REDUCE_PRINT (yyn); switch (yyn) { case 6: /* Line 1455 of yacc.c */ #line 207 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_lone_end ); } break; case 7: /* Line 1455 of yacc.c */ #line 208 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_case_outside ); } break; case 8: /* Line 1455 of yacc.c */ #line 212 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_stat)=0; } break; case 10: /* Line 1455 of yacc.c */ #line 215 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if( (yyvsp[(1) - (1)].fal_stat) != 0 ) COMPILER->addFunction( (yyvsp[(1) - (1)].fal_stat) ); } break; case 11: /* Line 1455 of yacc.c */ #line 220 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if ( (yyvsp[(1) - (1)].fal_stat) != 0 ) COMPILER->addClass( (yyvsp[(1) - (1)].fal_stat) ); } break; case 12: /* Line 1455 of yacc.c */ #line 225 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if ( (yyvsp[(1) - (1)].fal_stat) != 0 ) COMPILER->addClass( (yyvsp[(1) - (1)].fal_stat) ); } break; case 13: /* Line 1455 of yacc.c */ #line 230 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if ( (yyvsp[(1) - (1)].fal_stat) != 0 ) COMPILER->addClass( (yyvsp[(1) - (1)].fal_stat) ); } break; case 14: /* Line 1455 of yacc.c */ #line 235 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if( (yyvsp[(1) - (1)].fal_stat) != 0 ) COMPILER->addStatement( (yyvsp[(1) - (1)].fal_stat) ); } break; case 19: /* Line 1455 of yacc.c */ #line 246 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.integer) = - (yyvsp[(2) - (2)].integer); } break; case 20: /* Line 1455 of yacc.c */ #line 251 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if ( COMPILER->getContext() != 0 ) COMPILER->raiseError(Falcon::e_toplevel_load ); COMPILER->addLoad( *(yyvsp[(2) - (3)].stringp), false ); } break; case 21: /* Line 1455 of yacc.c */ #line 257 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if ( COMPILER->getContext() != 0 ) COMPILER->raiseError(Falcon::e_toplevel_load ); COMPILER->addLoad( *(yyvsp[(2) - (3)].stringp), true ); } break; case 22: /* Line 1455 of yacc.c */ #line 263 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_load ); } break; case 23: /* Line 1455 of yacc.c */ #line 269 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->checkLocalUndefined(); (yyval.fal_stat) = (yyvsp[(1) - (1)].fal_stat); } break; case 24: /* Line 1455 of yacc.c */ #line 270 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" {(yyval.fal_stat)=0;} break; case 25: /* Line 1455 of yacc.c */ #line 271 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_stat) = 0; } break; case 26: /* Line 1455 of yacc.c */ #line 272 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_toplevel_func ); (yyval.fal_stat) = 0; } break; case 27: /* Line 1455 of yacc.c */ #line 273 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_toplevel_obj ); (yyval.fal_stat) = 0; } break; case 28: /* Line 1455 of yacc.c */ #line 274 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_toplevel_class ); (yyval.fal_stat) = 0; } break; case 29: /* Line 1455 of yacc.c */ #line 275 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syntax ); (yyval.fal_stat) = 0;} break; case 30: /* Line 1455 of yacc.c */ #line 280 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if( (! COMPILER->isInteractive()) && ((!(yyvsp[(1) - (2)].fal_val)->isExpr()) || (!(yyvsp[(1) - (2)].fal_val)->asExpr()->isStandAlone()) ) ) { Falcon::StmtFunction *func = COMPILER->getFunctionContext(); if ( func == 0 || ! func->isLambda() ) COMPILER->raiseError(Falcon::e_noeffect ); } (yyval.fal_stat) = new Falcon::StmtAutoexpr( LINE, (yyvsp[(1) - (2)].fal_val) ); } break; case 31: /* Line 1455 of yacc.c */ #line 292 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if ( (yyvsp[(3) - (4)].fal_val)->type() == Falcon::Value::t_array_decl ) { Falcon::ArrayDecl* ad = (yyvsp[(3) - (4)].fal_val)->asArray(); if ( (yyvsp[(1) - (4)].fal_adecl)->size() != ad->size() ) { COMPILER->raiseError(Falcon::e_unpack_size ); } } Falcon::Value *first = new Falcon::Value( (yyvsp[(1) - (4)].fal_adecl) ); COMPILER->defineVal( first ); (yyval.fal_stat) = new Falcon::StmtAutoexpr( LINE, new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_assign, first, (yyvsp[(3) - (4)].fal_val) ) ) ); } break; case 32: /* Line 1455 of yacc.c */ #line 308 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if ( (yyvsp[(1) - (6)].fal_adecl)->size() != (yyvsp[(5) - (6)].fal_adecl)->size() + 1 ) { COMPILER->raiseError(Falcon::e_unpack_size ); } Falcon::Value *first = new Falcon::Value( (yyvsp[(1) - (6)].fal_adecl) ); COMPILER->defineVal( first ); (yyvsp[(5) - (6)].fal_adecl)->pushFront( (yyvsp[(3) - (6)].fal_val) ); Falcon::Value *second = new Falcon::Value( (yyvsp[(5) - (6)].fal_adecl) ); (yyval.fal_stat) = new Falcon::StmtAutoexpr( LINE, new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_assign, first, second ) ) ); } break; case 52: /* Line 1455 of yacc.c */ #line 348 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->defContext( true ); COMPILER->defineVal( (yyvsp[(1) - (1)].fal_val) ); COMPILER->addStatement( new Falcon::StmtAutoexpr(CURRENT_LINE, new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_assign, (yyvsp[(1) - (1)].fal_val), new Falcon::Value() ) ) ) ); } break; case 53: /* Line 1455 of yacc.c */ #line 356 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->defContext( true ); COMPILER->defineVal( (yyvsp[(1) - (3)].fal_val) ); COMPILER->addStatement( new Falcon::StmtAutoexpr(CURRENT_LINE, new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_assign, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ) ) ); } break; case 54: /* Line 1455 of yacc.c */ #line 366 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->defContext( false ); (yyval.fal_stat)=0; } break; case 55: /* Line 1455 of yacc.c */ #line 368 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError( Falcon::e_syn_def ); } break; case 56: /* Line 1455 of yacc.c */ #line 372 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtWhile *w = new Falcon::StmtWhile( LINE, (yyvsp[(1) - (1)].fal_val) ); COMPILER->pushLoop( w ); COMPILER->pushContext( w ); COMPILER->pushContextSet( &w->children() ); } break; case 57: /* Line 1455 of yacc.c */ #line 379 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtWhile *w = static_cast(COMPILER->getContext()); COMPILER->popLoop(); COMPILER->popContext(); COMPILER->popContextSet(); (yyval.fal_stat) = w; } break; case 58: /* Line 1455 of yacc.c */ #line 386 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtWhile *w = new Falcon::StmtWhile( LINE, (yyvsp[(1) - (2)].fal_val) ); if ( (yyvsp[(2) - (2)].fal_stat) != 0 ) w->children().push_back( (yyvsp[(2) - (2)].fal_stat) ); (yyval.fal_stat) = w; } break; case 59: /* Line 1455 of yacc.c */ #line 394 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = (yyvsp[(2) - (3)].fal_val); } break; case 60: /* Line 1455 of yacc.c */ #line 395 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_while ); (yyval.fal_val) = 0; } break; case 61: /* Line 1455 of yacc.c */ #line 399 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = (yyvsp[(2) - (3)].fal_val); } break; case 62: /* Line 1455 of yacc.c */ #line 400 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_while, "", CURRENT_LINE ); (yyval.fal_val) = 0; } break; case 63: /* Line 1455 of yacc.c */ #line 404 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtLoop *w = new Falcon::StmtLoop( LINE ); COMPILER->pushLoop( w ); COMPILER->pushContext( w ); COMPILER->pushContextSet( &w->children() ); } break; case 64: /* Line 1455 of yacc.c */ #line 411 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtLoop *w = static_cast(COMPILER->getContext()); w->setCondition((yyvsp[(6) - (7)].fal_val)); COMPILER->popLoop(); COMPILER->popContext(); COMPILER->popContextSet(); (yyval.fal_stat) = w; } break; case 65: /* Line 1455 of yacc.c */ #line 419 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtLoop *w = new Falcon::StmtLoop( LINE ); if ( (yyvsp[(3) - (3)].fal_stat) != 0 ) w->children().push_back( (yyvsp[(3) - (3)].fal_stat) ); (yyval.fal_stat) = w; } break; case 66: /* Line 1455 of yacc.c */ #line 425 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError( Falcon::e_syn_loop ); (yyval.fal_stat) = 0; } break; case 67: /* Line 1455 of yacc.c */ #line 432 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val)=0; } break; case 68: /* Line 1455 of yacc.c */ #line 433 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = (yyvsp[(1) - (1)].fal_val); } break; case 69: /* Line 1455 of yacc.c */ #line 437 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtIf *stmt = new Falcon::StmtIf( LINE, (yyvsp[(1) - (1)].fal_val) ); COMPILER->pushContext( stmt ); COMPILER->pushContextSet( &stmt->children() ); } break; case 70: /* Line 1455 of yacc.c */ #line 445 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtIf *stmt = static_cast(COMPILER->getContext()); COMPILER->popContext(); COMPILER->popContextSet(); (yyval.fal_stat) = stmt; } break; case 71: /* Line 1455 of yacc.c */ #line 452 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { // use LINE as statement includes EOL Falcon::StmtIf *stmt = new Falcon::StmtIf( LINE, (yyvsp[(1) - (2)].fal_val) ); if( (yyvsp[(2) - (2)].fal_stat) != 0 ) stmt->children().push_back( (yyvsp[(2) - (2)].fal_stat) ); (yyval.fal_stat) = stmt; } break; case 72: /* Line 1455 of yacc.c */ #line 462 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = (yyvsp[(2) - (3)].fal_val); } break; case 73: /* Line 1455 of yacc.c */ #line 463 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_if ); (yyval.fal_val) = 0; } break; case 74: /* Line 1455 of yacc.c */ #line 467 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = (yyvsp[(2) - (3)].fal_val); } break; case 75: /* Line 1455 of yacc.c */ #line 468 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_if, "", CURRENT_LINE ); (yyval.fal_val) = 0; } break; case 78: /* Line 1455 of yacc.c */ #line 475 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtIf *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); COMPILER->pushContextSet( &stmt->elseChildren() ); } break; case 81: /* Line 1455 of yacc.c */ #line 485 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_else ); } break; case 82: /* Line 1455 of yacc.c */ #line 490 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtIf *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtElif *elif = new Falcon::StmtElif( LINE, (yyvsp[(1) - (1)].fal_val) ); stmt->elifChildren().push_back( elif ); COMPILER->pushContextSet( &elif->children() ); } break; case 84: /* Line 1455 of yacc.c */ #line 502 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = (yyvsp[(2) - (3)].fal_val); } break; case 85: /* Line 1455 of yacc.c */ #line 503 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_elif ); (yyval.fal_val) = 0; } break; case 87: /* Line 1455 of yacc.c */ #line 508 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addStatement( (yyvsp[(2) - (2)].fal_stat) ); } break; case 88: /* Line 1455 of yacc.c */ #line 515 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if ( COMPILER->getLoop() == 0 ) { COMPILER->raiseError(Falcon::e_break_out ); (yyval.fal_stat) = 0; } else (yyval.fal_stat) = new Falcon::StmtBreak( LINE ); } break; case 89: /* Line 1455 of yacc.c */ #line 524 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_break ); (yyval.fal_stat) = 0; } break; case 90: /* Line 1455 of yacc.c */ #line 532 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if ( COMPILER->getLoop() == 0 ) { COMPILER->raiseError(Falcon::e_continue_out ); (yyval.fal_stat) = 0; } else (yyval.fal_stat) = new Falcon::StmtContinue( LINE ); } break; case 91: /* Line 1455 of yacc.c */ #line 542 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if ( COMPILER->getLoop() == 0 ) { COMPILER->raiseError(Falcon::e_continue_out ); (yyval.fal_stat) = 0; } else (yyval.fal_stat) = new Falcon::StmtContinue( LINE, true ); } break; case 92: /* Line 1455 of yacc.c */ #line 551 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_continue ); (yyval.fal_stat) = 0; } break; case 93: /* Line 1455 of yacc.c */ #line 559 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_stat) = new Falcon::StmtForin( LINE, (yyvsp[(2) - (4)].fal_adecl), (yyvsp[(4) - (4)].fal_val) ); } break; case 94: /* Line 1455 of yacc.c */ #line 564 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->defineVal( (yyvsp[(2) - (4)].fal_val) ); Falcon::ArrayDecl *decl = new Falcon::ArrayDecl(); decl->pushBack( (yyvsp[(2) - (4)].fal_val) ); (yyval.fal_stat) = new Falcon::StmtForin( LINE, decl, (yyvsp[(4) - (4)].fal_val) ); } break; case 95: /* Line 1455 of yacc.c */ #line 572 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { delete (yyvsp[(2) - (5)].fal_adecl); COMPILER->raiseError( Falcon::e_syn_forin ); (yyval.fal_stat) = 0; } break; case 96: /* Line 1455 of yacc.c */ #line 577 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError( Falcon::e_syn_forin ); (yyval.fal_stat) = 0; } break; case 97: /* Line 1455 of yacc.c */ #line 586 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtForin *f = static_cast((yyvsp[(1) - (2)].fal_stat)); COMPILER->pushLoop( f ); COMPILER->pushContext( f ); COMPILER->pushContextSet( &f->children() ); } break; case 98: /* Line 1455 of yacc.c */ #line 593 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if( (yyvsp[(4) - (4)].fal_stat) != 0 ) { COMPILER->addStatement( (yyvsp[(4) - (4)].fal_stat) ); } COMPILER->popLoop(); COMPILER->popContext(); COMPILER->popContextSet(); (yyval.fal_stat) = (yyvsp[(1) - (4)].fal_stat); } break; case 99: /* Line 1455 of yacc.c */ #line 606 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtForin *f = static_cast((yyvsp[(1) - (2)].fal_stat)); COMPILER->pushLoop( f ); COMPILER->pushContext( f ); COMPILER->pushContextSet( &f->children() ); } break; case 100: /* Line 1455 of yacc.c */ #line 617 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtForin *f = static_cast(COMPILER->getContext()); COMPILER->popLoop(); COMPILER->popContext(); COMPILER->popContextSet(); (yyval.fal_stat) = f; } break; case 101: /* Line 1455 of yacc.c */ #line 628 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::RangeDecl* rd = new Falcon::RangeDecl( (yyvsp[(1) - (4)].fal_val), new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_oob, (yyvsp[(3) - (4)].fal_val))), (yyvsp[(4) - (4)].fal_val) ); (yyval.fal_val) = new Falcon::Value( rd ); } break; case 102: /* Line 1455 of yacc.c */ #line 634 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::RangeDecl( (yyvsp[(1) - (4)].fal_val), (yyvsp[(3) - (4)].fal_val), 0 ) ); } break; case 103: /* Line 1455 of yacc.c */ #line 638 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::RangeDecl( (yyvsp[(1) - (3)].fal_val), 0, 0 ) ); } break; case 104: /* Line 1455 of yacc.c */ #line 644 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val)=0; } break; case 105: /* Line 1455 of yacc.c */ #line 645 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val)=new Falcon::Value( (yyvsp[(2) - (2)].fal_val) ); } break; case 106: /* Line 1455 of yacc.c */ #line 646 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val)=0; } break; case 109: /* Line 1455 of yacc.c */ #line 655 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if ( (yyvsp[(1) - (1)].fal_stat) != 0 ) { Falcon::StmtForin *f = static_cast(COMPILER->getContext()); f->children().push_back( (yyvsp[(1) - (1)].fal_stat) ); } } break; case 113: /* Line 1455 of yacc.c */ #line 669 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtForin *f = static_cast(COMPILER->getLoop()); if ( f == 0 || f->type() != Falcon::Statement::t_forin ) { COMPILER->raiseError( Falcon::e_syn_fordot ); delete (yyvsp[(2) - (3)].fal_val); (yyval.fal_stat) = 0; } else { (yyval.fal_stat) = new Falcon::StmtFordot( LINE, (yyvsp[(2) - (3)].fal_val) ); } } break; case 114: /* Line 1455 of yacc.c */ #line 682 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError( Falcon::e_syn_fordot ); (yyval.fal_stat) = 0; } break; case 115: /* Line 1455 of yacc.c */ #line 690 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_stat) = new Falcon::StmtSelfPrint( LINE, (yyvsp[(2) - (3)].fal_adecl) ); } break; case 116: /* Line 1455 of yacc.c */ #line 694 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError( Falcon::e_syn_self_print ); (yyval.fal_stat) = 0; } break; case 117: /* Line 1455 of yacc.c */ #line 700 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyvsp[(2) - (3)].fal_adecl)->pushBack( new Falcon::Value( COMPILER->addString( "\n" ) ) ); (yyval.fal_stat) = new Falcon::StmtSelfPrint( LINE, (yyvsp[(2) - (3)].fal_adecl) ); } break; case 118: /* Line 1455 of yacc.c */ #line 706 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::ArrayDecl *adecl = new Falcon::ArrayDecl(); adecl->pushBack( new Falcon::Value( COMPILER->addString( "\n" ) ) ); (yyval.fal_stat) = new Falcon::StmtSelfPrint( LINE, adecl ); } break; case 119: /* Line 1455 of yacc.c */ #line 713 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError( Falcon::e_syn_self_print ); (yyval.fal_stat) = 0; } break; case 120: /* Line 1455 of yacc.c */ #line 718 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError( Falcon::e_syn_self_print ); (yyval.fal_stat) = 0; } break; case 121: /* Line 1455 of yacc.c */ #line 727 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::ArrayDecl *adecl = new Falcon::ArrayDecl(); adecl->pushBack( new Falcon::Value( (yyvsp[(1) - (1)].stringp) ) ); (yyval.fal_stat) = new Falcon::StmtSelfPrint( LINE, adecl ); } break; case 122: /* Line 1455 of yacc.c */ #line 736 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtForin *f = static_cast(COMPILER->getContext()); if( ! f->firstBlock().empty() ) { COMPILER->raiseError( Falcon::e_already_forfirst ); } COMPILER->pushContextSet( &f->firstBlock() ); // Push anyhow an empty item, that is needed for to check again for thio blosk f->firstBlock().push_back( new Falcon::StmtNone( LINE ) ); } break; case 123: /* Line 1455 of yacc.c */ #line 748 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->popContextSet(); } break; case 124: /* Line 1455 of yacc.c */ #line 750 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtForin *f = static_cast(COMPILER->getContext()); if( ! f->firstBlock().empty() ) { COMPILER->raiseError( Falcon::e_already_forfirst ); } if ( (yyvsp[(3) - (3)].fal_stat) != 0 ) f->firstBlock().push_back( (yyvsp[(3) - (3)].fal_stat) ); } break; case 125: /* Line 1455 of yacc.c */ #line 759 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_forfirst ); } break; case 126: /* Line 1455 of yacc.c */ #line 763 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtForin *f = static_cast(COMPILER->getContext()); if( ! f->lastBlock().empty() ) { COMPILER->raiseError( Falcon::e_already_forlast ); } // Push anyhow an empty item, that is needed for empty last blocks f->lastBlock().push_back( new Falcon::StmtNone( LINE ) ); COMPILER->pushContextSet( &f->lastBlock() ); } break; case 127: /* Line 1455 of yacc.c */ #line 775 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->popContextSet(); } break; case 128: /* Line 1455 of yacc.c */ #line 776 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtForin *f = static_cast(COMPILER->getContext()); if( ! f->lastBlock().empty() ) { COMPILER->raiseError( Falcon::e_already_forlast ); } if ( (yyvsp[(3) - (3)].fal_stat) != 0 ) f->lastBlock().push_back( (yyvsp[(3) - (3)].fal_stat) ); } break; case 129: /* Line 1455 of yacc.c */ #line 785 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_forlast ); } break; case 130: /* Line 1455 of yacc.c */ #line 789 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtForin *f = static_cast(COMPILER->getContext()); if( ! f->middleBlock().empty() ) { COMPILER->raiseError( Falcon::e_already_formiddle ); } // Push anyhow an empty item, that is needed for empty last blocks // Apparently you get a segfault without it. // (Note that the formiddle: version below does *not* need it f->middleBlock().push_back( new Falcon::StmtNone( LINE ) ); COMPILER->pushContextSet( &f->middleBlock() ); } break; case 131: /* Line 1455 of yacc.c */ #line 803 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->popContextSet(); } break; case 132: /* Line 1455 of yacc.c */ #line 805 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtForin *f = static_cast(COMPILER->getContext()); if( ! f->middleBlock().empty() ) { COMPILER->raiseError( Falcon::e_already_formiddle ); } if ( (yyvsp[(3) - (3)].fal_stat) != 0 ) f->middleBlock().push_back( (yyvsp[(3) - (3)].fal_stat) ); } break; case 133: /* Line 1455 of yacc.c */ #line 814 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_formiddle ); } break; case 134: /* Line 1455 of yacc.c */ #line 818 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtSwitch *stmt = new Falcon::StmtSwitch( LINE, (yyvsp[(1) - (1)].fal_val) ); COMPILER->pushContext( stmt ); COMPILER->pushContextSet( &stmt->blocks() ); } break; case 135: /* Line 1455 of yacc.c */ #line 826 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); COMPILER->popContext(); COMPILER->popContextSet(); (yyval.fal_stat) = stmt; } break; case 136: /* Line 1455 of yacc.c */ #line 835 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = (yyvsp[(2) - (3)].fal_val); } break; case 137: /* Line 1455 of yacc.c */ #line 837 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_switch_decl ); (yyval.fal_val) = 0; } break; case 140: /* Line 1455 of yacc.c */ #line 846 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_switch_body ); } break; case 142: /* Line 1455 of yacc.c */ #line 852 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtCaseBlock *lst = new Falcon::StmtCaseBlock( LINE ); COMPILER->pushContextSet( &lst->children() ); stmt->addBlock( lst ); } break; case 144: /* Line 1455 of yacc.c */ #line 862 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtCaseBlock *lst = new Falcon::StmtCaseBlock( CURRENT_LINE ); COMPILER->pushContextSet( &lst->children() ); stmt->addBlock( lst ); } break; case 145: /* Line 1455 of yacc.c */ #line 870 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addStatement( (yyvsp[(5) - (5)].fal_stat) ); } break; case 146: /* Line 1455 of yacc.c */ #line 874 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_case_decl ); Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtCaseBlock *lst = new Falcon::StmtCaseBlock( LINE ); COMPILER->pushContextSet( &lst->children() ); stmt->addBlock( lst ); } break; case 148: /* Line 1455 of yacc.c */ #line 886 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_case_decl ); Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtCaseBlock *lst = new Falcon::StmtCaseBlock( CURRENT_LINE ); COMPILER->pushContextSet( &lst->children() ); stmt->addBlock( lst ); } break; case 149: /* Line 1455 of yacc.c */ #line 896 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addStatement( (yyvsp[(5) - (5)].fal_stat) ); } break; case 151: /* Line 1455 of yacc.c */ #line 905 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); if ( ! stmt->defaultBlock().empty() ) { COMPILER->raiseError(Falcon::e_switch_default, "", CURRENT_LINE ); } COMPILER->pushContextSet( &stmt->defaultBlock() ); } break; case 155: /* Line 1455 of yacc.c */ #line 919 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_default_decl ); } break; case 157: /* Line 1455 of yacc.c */ #line 923 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addStatement( (yyvsp[(2) - (2)].fal_stat) ); } break; case 160: /* Line 1455 of yacc.c */ #line 935 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); // todo: correct error if ( stmt->nilBlock() != -1 ) COMPILER->raiseError(Falcon::e_switch_clash, "nil entry", CURRENT_LINE ); stmt->nilBlock( stmt->currentBlock() ); } break; case 161: /* Line 1455 of yacc.c */ #line 944 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); // todo: correct error Falcon::Value *val = new Falcon::Value( (yyvsp[(1) - (1)].integer) ); if ( ! stmt->addIntCase( val ) ) { COMPILER->raiseError(Falcon::e_switch_clash, "", CURRENT_LINE ); delete val; } } break; case 162: /* Line 1455 of yacc.c */ #line 956 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); Falcon::Value *val = new Falcon::Value( (yyvsp[(1) - (1)].stringp) ); if ( ! stmt->addStringCase( val ) ) { COMPILER->raiseError(Falcon::e_switch_clash, "", CURRENT_LINE ); delete val; } } break; case 163: /* Line 1455 of yacc.c */ #line 967 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); Falcon::Value *val = new Falcon::Value( new Falcon::RangeDecl( new Falcon::Value( (yyvsp[(1) - (3)].integer) ), new Falcon::Value( (yyvsp[(3) - (3)].integer) ) ) ); if ( ! stmt->addRangeCase( val ) ) { COMPILER->raiseError(Falcon::e_switch_clash, "", CURRENT_LINE ); delete val; } } break; case 164: /* Line 1455 of yacc.c */ #line 978 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); Falcon::Symbol *sym = COMPILER->searchLocalSymbol( *(yyvsp[(1) - (1)].stringp) ); if( sym == 0 ) sym = COMPILER->addGlobalSymbol( *(yyvsp[(1) - (1)].stringp) ); Falcon::Value *val = new Falcon::Value( sym ); if ( ! stmt->addSymbolCase( val ) ) { COMPILER->raiseError(Falcon::e_switch_clash, "", CURRENT_LINE ); delete val; } } break; case 165: /* Line 1455 of yacc.c */ #line 998 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtSelect *stmt = new Falcon::StmtSelect( LINE, (yyvsp[(1) - (1)].fal_val) ); COMPILER->pushContext( stmt ); COMPILER->pushContextSet( &stmt->blocks() ); } break; case 166: /* Line 1455 of yacc.c */ #line 1006 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtSelect *stmt = static_cast(COMPILER->getContext()); COMPILER->popContext(); COMPILER->popContextSet(); (yyval.fal_stat) = stmt; } break; case 167: /* Line 1455 of yacc.c */ #line 1015 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = (yyvsp[(2) - (3)].fal_val); } break; case 168: /* Line 1455 of yacc.c */ #line 1017 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_select_decl ); (yyval.fal_val) = 0; } break; case 171: /* Line 1455 of yacc.c */ #line 1026 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_select_body ); } break; case 173: /* Line 1455 of yacc.c */ #line 1032 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtSelect *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtCaseBlock *lst = new Falcon::StmtCaseBlock( LINE ); COMPILER->pushContextSet( &lst->children() ); stmt->addBlock( lst ); } break; case 175: /* Line 1455 of yacc.c */ #line 1042 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtSelect *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtCaseBlock *lst = new Falcon::StmtCaseBlock( CURRENT_LINE ); COMPILER->pushContextSet( &lst->children() ); stmt->addBlock( lst ); } break; case 176: /* Line 1455 of yacc.c */ #line 1051 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addStatement( (yyvsp[(5) - (5)].fal_stat) ); } break; case 177: /* Line 1455 of yacc.c */ #line 1055 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_case_decl ); Falcon::StmtSelect *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtCaseBlock *lst = new Falcon::StmtCaseBlock( LINE ); COMPILER->pushContextSet( &lst->children() ); stmt->addBlock( lst ); } break; case 179: /* Line 1455 of yacc.c */ #line 1067 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_case_decl ); Falcon::StmtSelect *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtCaseBlock *lst = new Falcon::StmtCaseBlock( CURRENT_LINE ); COMPILER->pushContextSet( &lst->children() ); stmt->addBlock( lst ); } break; case 180: /* Line 1455 of yacc.c */ #line 1077 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addStatement( (yyvsp[(5) - (5)].fal_stat) ); } break; case 184: /* Line 1455 of yacc.c */ #line 1091 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); // todo: correct error Falcon::Value *val = new Falcon::Value( (yyvsp[(1) - (1)].integer) ); if ( ! stmt->addIntCase( val ) ) { COMPILER->raiseError(Falcon::e_switch_clash, "", CURRENT_LINE ); delete val; } } break; case 185: /* Line 1455 of yacc.c */ #line 1103 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); Falcon::Symbol *sym = COMPILER->searchLocalSymbol( *(yyvsp[(1) - (1)].stringp) ); if( sym == 0 ) sym = COMPILER->addGlobalSymbol( *(yyvsp[(1) - (1)].stringp) ); Falcon::Value *val = new Falcon::Value( sym ); if ( ! stmt->addSymbolCase( val ) ) { COMPILER->raiseError(Falcon::e_switch_clash, "", CURRENT_LINE ); delete val; } } break; case 186: /* Line 1455 of yacc.c */ #line 1123 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtTry *t = new Falcon::StmtTry( CURRENT_LINE ); if ( (yyvsp[(3) - (3)].fal_stat) != 0 ) t->children().push_back( (yyvsp[(3) - (3)].fal_stat) ); (yyval.fal_stat) = t; } break; case 187: /* Line 1455 of yacc.c */ #line 1130 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtTry *t = new Falcon::StmtTry( LINE ); COMPILER->pushContext( t ); COMPILER->pushContextSet( &t->children() ); } break; case 188: /* Line 1455 of yacc.c */ #line 1140 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_stat) = COMPILER->getContext(); COMPILER->popContext(); COMPILER->popContextSet(); } break; case 190: /* Line 1455 of yacc.c */ #line 1149 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_try ); } break; case 196: /* Line 1455 of yacc.c */ #line 1169 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->popContextSet(); // popping previous catch Falcon::StmtTry *t = static_cast( COMPILER->getContext() ); // if we have already a default, raise an error if( t->defaultHandler() != 0 ) { COMPILER->raiseError(Falcon::e_catch_adef ); } // but continue by pushing this new context Falcon::StmtCatchBlock *lst = new Falcon::StmtCatchBlock( LINE, 0 ); t->defaultHandler( lst ); // will delete the previous one COMPILER->pushContextSet( &lst->children() ); } break; case 197: /* Line 1455 of yacc.c */ #line 1187 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->popContextSet(); // popping previous catch Falcon::StmtTry *t = static_cast( COMPILER->getContext() ); // if we have already a default, raise an error if( t->defaultHandler() != 0 ) { COMPILER->raiseError(Falcon::e_catch_adef ); } // but continue by pushing this new context COMPILER->defineVal( (yyvsp[(3) - (4)].fal_val) ); Falcon::StmtCatchBlock *lst = new Falcon::StmtCatchBlock( LINE, (yyvsp[(3) - (4)].fal_val) ); t->defaultHandler( lst ); // will delete the previous one COMPILER->pushContextSet( &lst->children() ); } break; case 198: /* Line 1455 of yacc.c */ #line 1207 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->popContextSet(); // popping previous catch Falcon::StmtTry *t = static_cast( COMPILER->getContext() ); Falcon::StmtCatchBlock *lst = new Falcon::StmtCatchBlock( LINE, 0 ); COMPILER->pushContextSet( &lst->children() ); t->addHandler( lst ); } break; case 199: /* Line 1455 of yacc.c */ #line 1217 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->popContextSet(); // popping previous catch Falcon::StmtTry *t = static_cast( COMPILER->getContext() ); COMPILER->defineVal( (yyvsp[(4) - (5)].fal_val) ); Falcon::StmtCatchBlock *lst = new Falcon::StmtCatchBlock( LINE, (yyvsp[(4) - (5)].fal_val) ); COMPILER->pushContextSet( &lst->children() ); t->addHandler( lst ); } break; case 200: /* Line 1455 of yacc.c */ #line 1228 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError( Falcon::e_syn_catch ); } break; case 203: /* Line 1455 of yacc.c */ #line 1241 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtTry *stmt = static_cast(COMPILER->getContext()); Falcon::Value *val = new Falcon::Value( (yyvsp[(1) - (1)].integer) ); if ( ! stmt->addIntCase( val ) ) { COMPILER->raiseError(Falcon::e_catch_clash, "", CURRENT_LINE ); delete val; } } break; case 204: /* Line 1455 of yacc.c */ #line 1253 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtTry *stmt = static_cast(COMPILER->getContext()); Falcon::Symbol *sym = COMPILER->searchLocalSymbol( *(yyvsp[(1) - (1)].stringp) ); if( sym == 0 ) { sym = COMPILER->addGlobalSymbol( *(yyvsp[(1) - (1)].stringp) ); } Falcon::Value *val = new Falcon::Value( sym ); if ( ! stmt->addSymbolCase( val ) ) { COMPILER->raiseError(Falcon::e_catch_clash, "", CURRENT_LINE ); delete val; } } break; case 205: /* Line 1455 of yacc.c */ #line 1275 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_stat) = new Falcon::StmtRaise( LINE, (yyvsp[(2) - (3)].fal_val) ); } break; case 206: /* Line 1455 of yacc.c */ #line 1276 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_raise ); (yyval.fal_stat) = 0; } break; case 207: /* Line 1455 of yacc.c */ #line 1288 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_stat) = COMPILER->getContext(); COMPILER->closeFunction(); } break; case 208: /* Line 1455 of yacc.c */ #line 1294 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addStatement( (yyvsp[(2) - (2)].fal_stat) ); (yyval.fal_stat) = COMPILER->getContext(); COMPILER->closeFunction(); } break; case 210: /* Line 1455 of yacc.c */ #line 1303 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->tempLine( CURRENT_LINE ); } break; case 211: /* Line 1455 of yacc.c */ #line 1304 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseContextError(Falcon::e_syn_funcdecl, COMPILER->tempLine(), CTX_LINE ); } break; case 212: /* Line 1455 of yacc.c */ #line 1307 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_funcdecl ); } break; case 214: /* Line 1455 of yacc.c */ #line 1312 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->tempLine( CURRENT_LINE ); } break; case 215: /* Line 1455 of yacc.c */ #line 1313 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseContextError(Falcon::e_syn_funcdecl, COMPILER->tempLine(), CTX_LINE ); } break; case 216: /* Line 1455 of yacc.c */ #line 1320 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::FuncDef *def = new Falcon::FuncDef( 0, 0 ); // the SYMBOL which names the function goes in the old symbol table, while the parameters // will go in the new symbol table. // if we are in a class, I have to create the symbol classname.functionname Falcon::Statement *parent = COMPILER->getContext(); Falcon::String func_name; if ( parent != 0 ) { switch( parent->type() ) { case Falcon::Statement::t_class: { Falcon::StmtClass *stmt_cls = static_cast< Falcon::StmtClass *>( parent ); func_name = stmt_cls->symbol()->name() + "." + *(yyvsp[(2) - (2)].stringp); } break; case Falcon::Statement::t_state: { Falcon::StmtState *stmt_state = static_cast< Falcon::StmtState *>( parent ); func_name = stmt_state->owner()->symbol()->name() + "." + * stmt_state->name() + "#" + *(yyvsp[(2) - (2)].stringp); } break; case Falcon::Statement::t_function: { Falcon::StmtFunction *stmt_func = static_cast< Falcon::StmtFunction *>( parent ); func_name = stmt_func->name() + "##" + *(yyvsp[(2) - (2)].stringp); } break; default: func_name = *(yyvsp[(2) - (2)].stringp); } } else func_name = *(yyvsp[(2) - (2)].stringp); // find the global symbol for this. Falcon::Symbol *sym = COMPILER->searchGlobalSymbol( func_name ); // Not defined? if( sym == 0 ) { sym = COMPILER->addGlobalSymbol( func_name ); } else if ( sym->isFunction() || sym->isClass() ) { COMPILER->raiseError(Falcon::e_already_def, sym->name() ); } // anyhow, also in case of error, destroys the previous information to allow a correct parsing // of the rest. sym->setFunction( def ); // and eventually add it as a class property if ( parent != 0 ) { if( parent->type() == Falcon::Statement::t_class ) { Falcon::StmtClass *stmt_cls = static_cast< Falcon::StmtClass *>( parent ); Falcon::ClassDef *cd = stmt_cls->symbol()->getClassDef(); if ( cd->hasProperty( *(yyvsp[(2) - (2)].stringp) ) ) { COMPILER->raiseError(Falcon::e_prop_adef, *(yyvsp[(2) - (2)].stringp) ); } else { cd->addProperty( (yyvsp[(2) - (2)].stringp), new Falcon::VarDef( sym ) ); // is this a setter/getter? if( ( (yyvsp[(2) - (2)].stringp)->find( "__set_" ) == 0 || (yyvsp[(2) - (2)].stringp)->find( "__get_" ) == 0 ) && (yyvsp[(2) - (2)].stringp)->length() > 6 ) { Falcon::String *pname = COMPILER->addString( (yyvsp[(2) - (2)].stringp)->subString( 6 )); Falcon::VarDef *pd = cd->getProperty( *pname ); if( pd == 0 ) { pd = new Falcon::VarDef; cd->addProperty( pname, pd ); pd->setReflective( Falcon::e_reflectSetGet, 0xFFFFFFFF ); } else if( ! pd->isReflective() ) { COMPILER->raiseError(Falcon::e_prop_adef, *pname ); } } } } else if ( parent->type() == Falcon::Statement::t_state ) { Falcon::StmtState *stmt_state = static_cast< Falcon::StmtState *>( parent ); if( ! stmt_state->addFunction( (yyvsp[(2) - (2)].stringp), sym ) ) { COMPILER->raiseError(Falcon::e_sm_adef, *(yyvsp[(2) - (2)].stringp) ); } else { stmt_state->state()->addFunction( *(yyvsp[(2) - (2)].stringp), sym ); } // eventually add a property where to store this thing Falcon::ClassDef *cd = stmt_state->owner()->symbol()->getClassDef(); if ( ! cd->hasProperty( *(yyvsp[(2) - (2)].stringp) ) ) cd->addProperty( (yyvsp[(2) - (2)].stringp), new Falcon::VarDef ); } } Falcon::StmtFunction *func = new Falcon::StmtFunction( COMPILER->lexer()->line(), sym ); // prepare the statement allocation context COMPILER->pushContext( func ); COMPILER->pushFunctionContext( func ); COMPILER->pushContextSet( &func->statements() ); COMPILER->pushFunction( def ); } break; case 220: /* Line 1455 of yacc.c */ #line 1449 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::Symbol *sym = COMPILER->searchLocalSymbol( *(yyvsp[(1) - (1)].stringp) ); if ( sym != 0 ) { COMPILER->raiseError(Falcon::e_already_def, sym->name() ); } else { Falcon::FuncDef *func = COMPILER->getFunction(); Falcon::Symbol *sym = new Falcon::Symbol( COMPILER->module(), *(yyvsp[(1) - (1)].stringp) ); COMPILER->module()->addSymbol( sym ); func->addParameter( sym ); } } break; case 222: /* Line 1455 of yacc.c */ #line 1466 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtFunction *func = static_cast(COMPILER->getContext()); COMPILER->pushContextSet( &func->staticBlock() ); COMPILER->staticPrefix( &func->symbol()->name() ); } break; case 223: /* Line 1455 of yacc.c */ #line 1472 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->popContextSet(); COMPILER->staticPrefix(0); } break; case 224: /* Line 1455 of yacc.c */ #line 1477 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtFunction *func = static_cast(COMPILER->getContext()); COMPILER->pushContextSet( &func->staticBlock() ); COMPILER->staticPrefix( &func->symbol()->name() ); } break; case 225: /* Line 1455 of yacc.c */ #line 1483 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addStatement( (yyvsp[(3) - (3)].fal_stat) ); COMPILER->popContextSet(); COMPILER->staticPrefix(0); } break; case 227: /* Line 1455 of yacc.c */ #line 1492 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_static ); } break; case 229: /* Line 1455 of yacc.c */ #line 1497 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_static, "", CURRENT_LINE ); } break; case 230: /* Line 1455 of yacc.c */ #line 1507 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_stat) = new Falcon::StmtLaunch( LINE, (yyvsp[(2) - (3)].fal_val) ); } break; case 231: /* Line 1455 of yacc.c */ #line 1510 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_launch ); (yyval.fal_stat) = 0; } break; case 232: /* Line 1455 of yacc.c */ #line 1519 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { // TODO: evalute const expressions on the fly. Falcon::Value *val = (yyvsp[(4) - (5)].fal_val); //COMPILER->exprSimplify( $4 ); // will raise an error in case the expression is not atomic. COMPILER->addConstant( *(yyvsp[(2) - (5)].stringp), val, LINE ); // we don't need the expression anymore // no other action: (yyval.fal_stat) = 0; } break; case 233: /* Line 1455 of yacc.c */ #line 1529 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_inv_const_val ); (yyval.fal_stat) = 0; } break; case 234: /* Line 1455 of yacc.c */ #line 1534 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_const ); (yyval.fal_stat) = 0; } break; case 235: /* Line 1455 of yacc.c */ #line 1546 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if ( COMPILER->sourceTree()->isExportAll() ) COMPILER->raiseError(Falcon::e_export_all ); else COMPILER->sourceTree()->setExportAll(); // no effect (yyval.fal_stat)=0; } break; case 236: /* Line 1455 of yacc.c */ #line 1555 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if ( COMPILER->sourceTree()->isExportAll() ) COMPILER->raiseError(Falcon::e_export_all ); // no effect (yyval.fal_stat) = 0; } break; case 237: /* Line 1455 of yacc.c */ #line 1562 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_export ); (yyval.fal_stat) = 0; } break; case 238: /* Line 1455 of yacc.c */ #line 1570 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::Symbol *sym = COMPILER->addGlobalSymbol( *(yyvsp[(1) - (1)].stringp) ); sym->exported(true); } break; case 239: /* Line 1455 of yacc.c */ #line 1575 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::Symbol *sym = COMPILER->addGlobalSymbol( *(yyvsp[(3) - (3)].stringp) ); sym->exported(true); } break; case 240: /* Line 1455 of yacc.c */ #line 1583 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->importSymbols( (yyvsp[(2) - (3)].fal_genericList) ); (yyval.fal_stat) = 0; } break; case 241: /* Line 1455 of yacc.c */ #line 1588 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->importSymbols( (yyvsp[(2) - (5)].fal_genericList), *(yyvsp[(4) - (5)].stringp), "", false ); (yyval.fal_stat) = 0; } break; case 242: /* Line 1455 of yacc.c */ #line 1593 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->importSymbols( (yyvsp[(2) - (5)].fal_genericList), *(yyvsp[(4) - (5)].stringp), "", true ); (yyval.fal_stat) = 0; } break; case 243: /* Line 1455 of yacc.c */ #line 1598 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { // destroy the list to avoid leak Falcon::ListElement *li = (yyvsp[(2) - (7)].fal_genericList)->begin(); int counter = 0; while( li != 0 ) { Falcon::String *symName = (Falcon::String *) li->data(); if ( counter == 0 ) COMPILER->importAlias( *symName, *(yyvsp[(4) - (7)].stringp), *(yyvsp[(6) - (7)].stringp), false ); delete symName; li = li->next(); counter++; } delete (yyvsp[(2) - (7)].fal_genericList); if ( counter != 1 ) COMPILER->raiseError(Falcon::e_syn_import ); (yyval.fal_stat) = 0; } break; case 244: /* Line 1455 of yacc.c */ #line 1618 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { // destroy the list to avoid leak Falcon::ListElement *li = (yyvsp[(2) - (7)].fal_genericList)->begin(); int counter = 0; while( li != 0 ) { Falcon::String *symName = (Falcon::String *) li->data(); if ( counter == 0 ) COMPILER->importAlias( *symName, *(yyvsp[(4) - (7)].stringp), *(yyvsp[(6) - (7)].stringp), true ); delete symName; li = li->next(); counter++; } delete (yyvsp[(2) - (7)].fal_genericList); if ( counter != 1 ) COMPILER->raiseError(Falcon::e_syn_import ); (yyval.fal_stat) = 0; } break; case 245: /* Line 1455 of yacc.c */ #line 1637 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->importSymbols( (yyvsp[(2) - (7)].fal_genericList), *(yyvsp[(4) - (7)].stringp), *(yyvsp[(6) - (7)].stringp), false ); (yyval.fal_stat) = 0; } break; case 246: /* Line 1455 of yacc.c */ #line 1642 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->importSymbols( (yyvsp[(2) - (7)].fal_genericList), *(yyvsp[(4) - (7)].stringp), *(yyvsp[(6) - (7)].stringp), true ); (yyval.fal_stat) = 0; } break; case 247: /* Line 1455 of yacc.c */ #line 1647 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_import ); (yyval.fal_stat) = 0; } break; case 248: /* Line 1455 of yacc.c */ #line 1652 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { // destroy the list to avoid leak Falcon::ListElement *li = (yyvsp[(2) - (4)].fal_genericList)->begin(); while( li != 0 ) { Falcon::String *symName = (Falcon::String *) li->data(); delete symName; li = li->next(); } delete (yyvsp[(2) - (4)].fal_genericList); COMPILER->raiseError(Falcon::e_syn_import ); (yyval.fal_stat) = 0; } break; case 249: /* Line 1455 of yacc.c */ #line 1666 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addNamespace( *(yyvsp[(3) - (4)].stringp), "", true, false ); (yyval.fal_stat) = 0; } break; case 250: /* Line 1455 of yacc.c */ #line 1671 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addNamespace( *(yyvsp[(3) - (4)].stringp), "", true, true ); (yyval.fal_stat) = 0; } break; case 251: /* Line 1455 of yacc.c */ #line 1676 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addNamespace( *(yyvsp[(3) - (6)].stringp), *(yyvsp[(5) - (6)].stringp), true, false ); (yyval.fal_stat) = 0; } break; case 252: /* Line 1455 of yacc.c */ #line 1681 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addNamespace( *(yyvsp[(3) - (6)].stringp), *(yyvsp[(5) - (6)].stringp), true, true ); (yyval.fal_stat) = 0; } break; case 253: /* Line 1455 of yacc.c */ #line 1686 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_import ); (yyval.fal_stat) = 0; } break; case 254: /* Line 1455 of yacc.c */ #line 1695 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addAttribute( *(yyvsp[(1) - (4)].stringp), (yyvsp[(3) - (4)].fal_val), LINE ); } break; case 255: /* Line 1455 of yacc.c */ #line 1700 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_attrdecl ); } break; case 256: /* Line 1455 of yacc.c */ #line 1707 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::List *lst = new Falcon::List; lst->pushBack( new Falcon::String( *(yyvsp[(1) - (1)].stringp) ) ); (yyval.fal_genericList) = lst; } break; case 257: /* Line 1455 of yacc.c */ #line 1713 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyvsp[(1) - (3)].fal_genericList)->pushBack( new Falcon::String( *(yyvsp[(3) - (3)].stringp) ) ); (yyval.fal_genericList) = (yyvsp[(1) - (3)].fal_genericList); } break; case 258: /* Line 1455 of yacc.c */ #line 1725 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { // no effect (yyval.fal_stat)=0; } break; case 259: /* Line 1455 of yacc.c */ #line 1730 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_directive ); (yyval.fal_stat)=0; } break; case 262: /* Line 1455 of yacc.c */ #line 1743 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->setDirective( *(yyvsp[(1) - (3)].stringp), *(yyvsp[(3) - (3)].stringp) ); } break; case 263: /* Line 1455 of yacc.c */ #line 1747 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->setDirective( *(yyvsp[(1) - (3)].stringp), *(yyvsp[(3) - (3)].stringp) ); } break; case 264: /* Line 1455 of yacc.c */ #line 1751 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->setDirective( *(yyvsp[(1) - (3)].stringp), (yyvsp[(3) - (3)].integer) ); } break; case 265: /* Line 1455 of yacc.c */ #line 1764 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::ClassDef *def = new Falcon::ClassDef; // the SYMBOL which names the function goes in the old symbol table, while the parameters // will go in the new symbol table. // find the global symbol for this. Falcon::Symbol *sym = COMPILER->searchGlobalSymbol( *(yyvsp[(2) - (2)].stringp) ); // Not defined? if( sym == 0 ) { sym = COMPILER->addGlobalSymbol( *(yyvsp[(2) - (2)].stringp) ); } else if ( sym->isFunction() || sym->isClass() ) { COMPILER->raiseError(Falcon::e_already_def, sym->name() ); } // anyhow, also in case of error, destroys the previous information to allow a correct parsing // of the rest. sym->setClass( def ); Falcon::StmtClass *cls = new Falcon::StmtClass( COMPILER->lexer()->line(), sym ); // prepare the statement allocation context COMPILER->pushContext( cls ); // We don't have a context set here COMPILER->pushFunction( def ); } break; case 266: /* Line 1455 of yacc.c */ #line 1796 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_stat) = COMPILER->getContext(); // check for expressions in from clauses COMPILER->checkLocalUndefined(); Falcon::StmtClass *cls = static_cast((yyval.fal_stat)); // if the class has no constructor, create one in case of inheritance. if( cls != 0 ) { if ( cls->ctorFunction() == 0 ) { Falcon::ClassDef *cd = cls->symbol()->getClassDef(); if ( ! cd->inheritance().empty() ) { COMPILER->buildCtorFor( cls ); // COMPILER->addStatement( func ); should be done in buildCtorFor // cls->ctorFunction( func ); idem } } COMPILER->popContext(); //We didn't pushed a context set COMPILER->popFunction(); } } break; case 268: /* Line 1455 of yacc.c */ #line 1830 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_class ); } break; case 271: /* Line 1455 of yacc.c */ #line 1838 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->tempLine( CURRENT_LINE ); } break; case 272: /* Line 1455 of yacc.c */ #line 1839 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseContextError(Falcon::e_syn_class, COMPILER->tempLine(), CTX_LINE ); } break; case 277: /* Line 1455 of yacc.c */ #line 1856 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtClass *cls = static_cast( COMPILER->getContext() ); // creates or find the symbol. Falcon::Symbol *sym = COMPILER->addGlobalSymbol(*(yyvsp[(1) - (2)].stringp)); Falcon::ClassDef *clsdef = cls->symbol()->getClassDef(); Falcon::InheritDef *idef = new Falcon::InheritDef(sym); if ( clsdef->addInheritance( idef ) ) { cls->addInitExpression( new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_inherit, new Falcon::Value( sym ), (yyvsp[(2) - (2)].fal_val) ) ) ); } else { COMPILER->raiseError(Falcon::e_prop_adef ); delete idef; delete (yyvsp[(2) - (2)].fal_val); } } break; case 278: /* Line 1455 of yacc.c */ #line 1879 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = 0; } break; case 279: /* Line 1455 of yacc.c */ #line 1880 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val)=0; } break; case 280: /* Line 1455 of yacc.c */ #line 1882 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = (yyvsp[(2) - (3)].fal_adecl) == 0 ? 0 : new Falcon::Value( (yyvsp[(2) - (3)].fal_adecl) ); } break; case 284: /* Line 1455 of yacc.c */ #line 1895 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addFunction( (yyvsp[(1) - (1)].fal_stat) ); } break; case 285: /* Line 1455 of yacc.c */ #line 1898 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtClass *cls = static_cast( COMPILER->getContext() ); if ( cls->initGiven() ) { COMPILER->raiseError(Falcon::e_prop_pinit ); } // have we got a complex property statement? if ( (yyvsp[(1) - (1)].fal_stat) != 0 ) { // as we didn't push the class context set, we have to do it by ourselves // see if the class has already a constructor. Falcon::StmtFunction *ctor_stmt = cls->ctorFunction(); if ( ctor_stmt == 0 ) { ctor_stmt = COMPILER->buildCtorFor( cls ); } ctor_stmt->statements().push_back( (yyvsp[(1) - (1)].fal_stat) ); // this goes directly in the auto constructor. } } break; case 286: /* Line 1455 of yacc.c */ #line 1918 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtState* ss = static_cast((yyvsp[(1) - (1)].fal_stat)); Falcon::StmtClass *cls = static_cast( COMPILER->getContext() ); if( ! cls->addState( static_cast((yyvsp[(1) - (1)].fal_stat)) ) ) { const Falcon::String* name = ss->name(); // we're not using the state we created, afater all. delete ss->state(); delete (yyvsp[(1) - (1)].fal_stat); COMPILER->raiseError( Falcon::e_state_adef, *name ); } else { // ownership passes on to the classdef cls->symbol()->getClassDef()->addState( ss->name(), ss->state() ); cls->symbol()->getClassDef()->addProperty( ss->name(), new Falcon::VarDef( ss->name() ) ); } } break; case 289: /* Line 1455 of yacc.c */ #line 1942 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtClass *cls = static_cast( COMPILER->getContext() ); if( cls->initGiven() ) { COMPILER->raiseError(Falcon::e_init_given ); } else { cls->initGiven( true ); Falcon::StmtFunction *func = cls->ctorFunction(); if ( func == 0 ) { func = COMPILER->buildCtorFor( cls ); } // prepare the statement allocation context COMPILER->pushContext( func ); COMPILER->pushFunctionContext( func ); COMPILER->pushContextSet( &func->statements() ); COMPILER->pushFunction( func->symbol()->getFuncDef() ); } } break; case 290: /* Line 1455 of yacc.c */ #line 1967 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->popContext(); COMPILER->popContextSet(); COMPILER->popFunction(); COMPILER->popFunctionContext(); } break; case 291: /* Line 1455 of yacc.c */ #line 1977 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->checkLocalUndefined(); Falcon::StmtClass *cls = static_cast( COMPILER->getContext() ); Falcon::ClassDef *clsdef = cls->symbol()->getClassDef(); Falcon::VarDef *def = (yyvsp[(4) - (5)].fal_val)->genVarDef(); if ( def != 0 ) { Falcon::String prop_name = cls->symbol()->name() + "." + *(yyvsp[(2) - (5)].stringp); Falcon::Symbol *sym = COMPILER->addGlobalVar( prop_name, def ); if( clsdef->hasProperty( *(yyvsp[(2) - (5)].stringp) ) ) COMPILER->raiseError(Falcon::e_prop_adef, *(yyvsp[(2) - (5)].stringp) ); else clsdef->addProperty( (yyvsp[(2) - (5)].stringp), new Falcon::VarDef( Falcon::VarDef::t_reference, sym) ); } else { COMPILER->raiseError(Falcon::e_static_const ); } delete (yyvsp[(4) - (5)].fal_val); // the expression is not needed anymore (yyval.fal_stat) = 0; // we don't add any statement } break; case 292: /* Line 1455 of yacc.c */ #line 1999 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->checkLocalUndefined(); Falcon::StmtClass *cls = static_cast( COMPILER->getContext() ); Falcon::ClassDef *clsdef = cls->symbol()->getClassDef(); Falcon::VarDef *def = (yyvsp[(3) - (4)].fal_val)->genVarDef(); if ( def != 0 ) { if( clsdef->hasProperty( *(yyvsp[(1) - (4)].stringp) ) ) COMPILER->raiseError(Falcon::e_prop_adef, *(yyvsp[(1) - (4)].stringp) ); else clsdef->addProperty( (yyvsp[(1) - (4)].stringp), def ); delete (yyvsp[(3) - (4)].fal_val); // the expression is not needed anymore (yyval.fal_stat) = 0; // we don't add any statement } else { // create anyhow a nil property if( clsdef->hasProperty( *(yyvsp[(1) - (4)].stringp) ) ) COMPILER->raiseError(Falcon::e_prop_adef, *(yyvsp[(1) - (4)].stringp) ); else clsdef->addProperty( (yyvsp[(1) - (4)].stringp), new Falcon::VarDef() ); // but also prepare a statement to be executed by the auto-constructor. (yyval.fal_stat) = new Falcon::StmtVarDef( LINE, (yyvsp[(1) - (4)].stringp), (yyvsp[(3) - (4)].fal_val) ); } } break; case 293: /* Line 1455 of yacc.c */ #line 2031 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_stat) = COMPILER->getContext(); COMPILER->popContext(); } break; case 294: /* Line 1455 of yacc.c */ #line 2039 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtClass* cls = static_cast( COMPILER->getContext() ); COMPILER->pushContext( new Falcon::StmtState( (yyvsp[(2) - (4)].stringp), cls ) ); } break; case 295: /* Line 1455 of yacc.c */ #line 2047 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtClass* cls = static_cast( COMPILER->getContext() ); Falcon::StmtState* state = new Falcon::StmtState( COMPILER->addString( "init" ), cls ); cls->initState( state ); COMPILER->pushContext( state ); } break; case 299: /* Line 1455 of yacc.c */ #line 2068 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addFunction( (yyvsp[(1) - (1)].fal_stat) ); } break; case 300: /* Line 1455 of yacc.c */ #line 2079 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::ClassDef *def = new Falcon::ClassDef; // the SYMBOL which names the function goes in the old symbol table, while the parameters // will go in the new symbol table. // find the global symbol for this. Falcon::Symbol *sym = COMPILER->searchGlobalSymbol( *(yyvsp[(2) - (2)].stringp) ); // Not defined? if( sym == 0 ) { sym = COMPILER->addGlobalSymbol( *(yyvsp[(2) - (2)].stringp) ); sym->setEnum( true ); } else if ( sym->isFunction() || sym->isClass() ) { COMPILER->raiseError(Falcon::e_already_def, sym->name() ); } // anyhow, also in case of error, destroys the previous information to allow a correct parsing // of the rest. sym->setClass( def ); Falcon::StmtClass *cls = new Falcon::StmtClass( COMPILER->lexer()->line(), sym ); // prepare the statement allocation context COMPILER->pushContext( cls ); // We don't have a context set here COMPILER->pushFunction( def ); COMPILER->resetEnum(); } break; case 301: /* Line 1455 of yacc.c */ #line 2113 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_stat) = COMPILER->getContext(); COMPILER->popContext(); //We didn't pushed a context set COMPILER->popFunction(); } break; case 305: /* Line 1455 of yacc.c */ #line 2130 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addEnumerator( *(yyvsp[(1) - (4)].stringp), (yyvsp[(3) - (4)].fal_val) ); } break; case 307: /* Line 1455 of yacc.c */ #line 2135 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addEnumerator( *(yyvsp[(1) - (2)].stringp) ); } break; case 310: /* Line 1455 of yacc.c */ #line 2150 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::ClassDef *def = new Falcon::ClassDef; // the SYMBOL which names the function goes in the old symbol table, while the parameters // will go in the new symbol table. // we create a special symbol for the class. Falcon::String cl_name = "%"; cl_name += *(yyvsp[(2) - (2)].stringp); Falcon::Symbol *clsym = COMPILER->addGlobalSymbol( cl_name ); clsym->setClass( def ); // find the global symbol for this. Falcon::Symbol *sym = COMPILER->searchGlobalSymbol( *(yyvsp[(2) - (2)].stringp) ); // Not defined? if( sym == 0 ) { sym = COMPILER->addGlobalSymbol( *(yyvsp[(2) - (2)].stringp) ); } else if ( sym->isFunction() || sym->isClass() ) { COMPILER->raiseError(Falcon::e_already_def, sym->name() ); } // anyhow, also in case of error, destroys the previous information to allow a correct parsing // of the rest. sym->setInstance( clsym ); Falcon::StmtClass *cls = new Falcon::StmtClass( COMPILER->lexer()->line(), clsym ); cls->singleton( sym ); // prepare the statement allocation context COMPILER->pushContext( cls ); //Statements here goes in the auto constructor. //COMPILER->pushContextSet( &cls->autoCtor() ); COMPILER->pushFunction( def ); } break; case 311: /* Line 1455 of yacc.c */ #line 2190 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_stat) = COMPILER->getContext(); Falcon::StmtClass *cls = static_cast((yyval.fal_stat)); // check for expressions in from clauses COMPILER->checkLocalUndefined(); // if the class has no constructor, create one in case of inheritance. if( cls->ctorFunction() == 0 ) { Falcon::ClassDef *cd = cls->symbol()->getClassDef(); if ( !cd->inheritance().empty() ) { COMPILER->buildCtorFor( cls ); // COMPILER->addStatement( func ); should be done in buildCtorFor // cls->ctorFunction( func ); idem } } COMPILER->popContext(); //COMPILER->popContextSet(); COMPILER->popFunction(); } break; case 313: /* Line 1455 of yacc.c */ #line 2218 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_object ); } break; case 317: /* Line 1455 of yacc.c */ #line 2230 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->addFunction( (yyvsp[(1) - (1)].fal_stat) ); } break; case 318: /* Line 1455 of yacc.c */ #line 2233 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtClass *cls = static_cast( COMPILER->getContext() ); if ( cls->initGiven() ) { COMPILER->raiseError(Falcon::e_prop_pinit ); } COMPILER->checkLocalUndefined(); // have we got a complex property statement? if ( (yyvsp[(1) - (1)].fal_stat) != 0 ) { // as we didn't push the class context set, we have to do it by ourselves // see if the class has already a constructor. Falcon::StmtFunction *ctor_stmt = cls->ctorFunction(); if ( ctor_stmt == 0 ) { ctor_stmt = COMPILER->buildCtorFor( cls ); } ctor_stmt->statements().push_back( (yyvsp[(1) - (1)].fal_stat) ); // this goes directly in the auto constructor. } } break; case 321: /* Line 1455 of yacc.c */ #line 2262 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StmtGlobal *glob = new Falcon::StmtGlobal( CURRENT_LINE ); COMPILER->pushContext( glob ); } break; case 322: /* Line 1455 of yacc.c */ #line 2267 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { // raise an error if we are not in a local context if ( ! COMPILER->isLocalContext() ) { COMPILER->raiseError(Falcon::e_global_notin_func, "", LINE ); } (yyval.fal_stat) = COMPILER->getContext(); COMPILER->popContext(); } break; case 324: /* Line 1455 of yacc.c */ #line 2281 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError( Falcon::e_syn_global ); } break; case 325: /* Line 1455 of yacc.c */ #line 2286 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError( Falcon::e_syn_global ); } break; case 327: /* Line 1455 of yacc.c */ #line 2292 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError( Falcon::e_syn_global ); } break; case 328: /* Line 1455 of yacc.c */ #line 2299 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { // we create (or retrieve) a globalized symbol Falcon::Symbol *sym = COMPILER->globalize( *(yyvsp[(1) - (1)].stringp) ); // then we add the symbol to the global statement (it's just for symbolic asm generation). Falcon::StmtGlobal *glob = static_cast( COMPILER->getContext() ); glob->addSymbol( sym ); } break; case 329: /* Line 1455 of yacc.c */ #line 2314 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_stat) = new Falcon::StmtReturn(LINE, 0); } break; case 330: /* Line 1455 of yacc.c */ #line 2315 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_stat) = new Falcon::StmtReturn( LINE, (yyvsp[(2) - (3)].fal_val) ); } break; case 331: /* Line 1455 of yacc.c */ #line 2316 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_return ); (yyval.fal_stat) = 0; } break; case 332: /* Line 1455 of yacc.c */ #line 2324 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value(); } break; case 333: /* Line 1455 of yacc.c */ #line 2325 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value(); (yyval.fal_val)->setUnbound(); } break; case 334: /* Line 1455 of yacc.c */ #line 2326 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( true ); } break; case 335: /* Line 1455 of yacc.c */ #line 2327 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( false ); } break; case 336: /* Line 1455 of yacc.c */ #line 2328 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( (yyvsp[(1) - (1)].integer) ); } break; case 337: /* Line 1455 of yacc.c */ #line 2329 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( (yyvsp[(1) - (1)].numeric) ); } break; case 338: /* Line 1455 of yacc.c */ #line 2330 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( (yyvsp[(1) - (1)].stringp) ); } break; case 339: /* Line 1455 of yacc.c */ #line 2334 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value(); } break; case 340: /* Line 1455 of yacc.c */ #line 2335 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value(); (yyval.fal_val)->setUnbound(); } break; case 341: /* Line 1455 of yacc.c */ #line 2336 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( true ); } break; case 342: /* Line 1455 of yacc.c */ #line 2337 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( false ); } break; case 343: /* Line 1455 of yacc.c */ #line 2338 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( (yyvsp[(1) - (1)].integer) ); } break; case 344: /* Line 1455 of yacc.c */ #line 2339 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( (yyvsp[(1) - (1)].numeric) ); } break; case 345: /* Line 1455 of yacc.c */ #line 2340 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( (yyvsp[(1) - (1)].stringp) ); } break; case 346: /* Line 1455 of yacc.c */ #line 2345 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::Value *val; Falcon::Symbol *sym = COMPILER->searchLocalSymbol( *(yyvsp[(1) - (1)].stringp) ); if( sym == 0 ) { val = new Falcon::Value(); val->setSymdef( (yyvsp[(1) - (1)].stringp) ); // warning: the symbol is still undefined. COMPILER->addSymdef( val ); } else { val = new Falcon::Value( sym ); } (yyval.fal_val) = val; } break; case 348: /* Line 1455 of yacc.c */ #line 2363 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value(); (yyval.fal_val)->setSelf(); } break; case 349: /* Line 1455 of yacc.c */ #line 2364 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value(); (yyval.fal_val)->setFself(); } break; case 354: /* Line 1455 of yacc.c */ #line 2382 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value(); (yyval.fal_val)->setLBind( (yyvsp[(2) - (2)].stringp) ); /* do not add the symbol to the compiler */ } break; case 355: /* Line 1455 of yacc.c */ #line 2383 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { char space[32]; sprintf(space, "%d", (int)(yyvsp[(2) - (2)].integer) ); (yyval.fal_val) = new Falcon::Value(); (yyval.fal_val)->setLBind( COMPILER->addString(space) ); } break; case 356: /* Line 1455 of yacc.c */ #line 2384 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value(); (yyval.fal_val)->setLBind( COMPILER->addString("self") ); /* do not add the symbol to the compiler */ } break; case 357: /* Line 1455 of yacc.c */ #line 2385 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value(); (yyvsp[(3) - (3)].stringp)->prepend( "." ); (yyval.fal_val)->setLBind( (yyvsp[(3) - (3)].stringp) ); /* do not add the symbol to the compiler */ } break; case 358: /* Line 1455 of yacc.c */ #line 2386 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { char space[32]; sprintf(space, ".%d", (int)(yyvsp[(3) - (3)].integer) ); (yyval.fal_val) = new Falcon::Value(); (yyval.fal_val)->setLBind( COMPILER->addString(space) ); } break; case 359: /* Line 1455 of yacc.c */ #line 2387 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value(); (yyval.fal_val)->setLBind( COMPILER->addString(".self") ); /* do not add the symbol to the compiler */ } break; case 360: /* Line 1455 of yacc.c */ #line 2388 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_neg, (yyvsp[(2) - (2)].fal_val) ) ); } break; case 361: /* Line 1455 of yacc.c */ #line 2389 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_fbind, new Falcon::Value((yyvsp[(1) - (3)].stringp)), (yyvsp[(3) - (3)].fal_val)) ); } break; case 362: /* Line 1455 of yacc.c */ #line 2390 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { // is this an immediate string sum ? if ( (yyvsp[(1) - (4)].fal_val)->isString() ) { if ( (yyvsp[(4) - (4)].fal_val)->isString() ) { Falcon::String str( *(yyvsp[(1) - (4)].fal_val)->asString() ); str += *(yyvsp[(4) - (4)].fal_val)->asString(); (yyvsp[(1) - (4)].fal_val)->setString( COMPILER->addString( str ) ); delete (yyvsp[(4) - (4)].fal_val); (yyval.fal_val) = (yyvsp[(1) - (4)].fal_val); } else if ( (yyvsp[(4) - (4)].fal_val)->isInteger() ) { Falcon::String str( *(yyvsp[(1) - (4)].fal_val)->asString() ); str.writeNumber( (yyvsp[(4) - (4)].fal_val)->asInteger() ); (yyvsp[(1) - (4)].fal_val)->setString( COMPILER->addString( str ) ); delete (yyvsp[(4) - (4)].fal_val); (yyval.fal_val) = (yyvsp[(1) - (4)].fal_val); } else (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_plus, (yyvsp[(1) - (4)].fal_val), (yyvsp[(4) - (4)].fal_val) ) ); } else (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_plus, (yyvsp[(1) - (4)].fal_val), (yyvsp[(4) - (4)].fal_val) ) ); } break; case 363: /* Line 1455 of yacc.c */ #line 2416 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_minus, (yyvsp[(1) - (4)].fal_val), (yyvsp[(4) - (4)].fal_val) ) ); } break; case 364: /* Line 1455 of yacc.c */ #line 2417 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if ( (yyvsp[(1) - (4)].fal_val)->isString() ) { if ( (yyvsp[(4) - (4)].fal_val)->isInteger() ) { Falcon::String str( (yyvsp[(1) - (4)].fal_val)->asString()->length() * (yyvsp[(4) - (4)].fal_val)->asInteger() ); for( int i = 0; i < (yyvsp[(4) - (4)].fal_val)->asInteger(); ++i ) { str.append( *(yyvsp[(1) - (4)].fal_val)->asString() ); } (yyvsp[(1) - (4)].fal_val)->setString( COMPILER->addString( str ) ); delete (yyvsp[(4) - (4)].fal_val); (yyval.fal_val) = (yyvsp[(1) - (4)].fal_val); } else (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_times, (yyvsp[(1) - (4)].fal_val), (yyvsp[(4) - (4)].fal_val) ) ); } else (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_times, (yyvsp[(1) - (4)].fal_val), (yyvsp[(4) - (4)].fal_val) ) ); } break; case 365: /* Line 1455 of yacc.c */ #line 2437 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if ( (yyvsp[(1) - (4)].fal_val)->isString() ) { if( (yyvsp[(1) - (4)].fal_val)->asString()->length() == 0 ) { COMPILER->raiseError( Falcon::e_invop ); } else { if ( (yyvsp[(4) - (4)].fal_val)->isInteger() ) { Falcon::String str( *(yyvsp[(1) - (4)].fal_val)->asString() ); str.setCharAt( str.length()-1, str.getCharAt(str.length()-1) + (yyvsp[(4) - (4)].fal_val)->asInteger() ); (yyvsp[(1) - (4)].fal_val)->setString( COMPILER->addString( str ) ); delete (yyvsp[(4) - (4)].fal_val); (yyval.fal_val) = (yyvsp[(1) - (4)].fal_val); } else (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_divide, (yyvsp[(1) - (4)].fal_val), (yyvsp[(4) - (4)].fal_val) ) ); } } else (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_divide, (yyvsp[(1) - (4)].fal_val), (yyvsp[(4) - (4)].fal_val) ) ); } break; case 366: /* Line 1455 of yacc.c */ #line 2461 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { if ( (yyvsp[(1) - (4)].fal_val)->isString() ) { if ( (yyvsp[(4) - (4)].fal_val)->isInteger() ) { Falcon::String str( *(yyvsp[(1) - (4)].fal_val)->asString() ); str.append( (Falcon::uint32) (yyvsp[(4) - (4)].fal_val)->asInteger() ); (yyvsp[(1) - (4)].fal_val)->setString( COMPILER->addString( str ) ); delete (yyvsp[(4) - (4)].fal_val); (yyval.fal_val) = (yyvsp[(1) - (4)].fal_val); } else (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_modulo, (yyvsp[(1) - (4)].fal_val), (yyvsp[(4) - (4)].fal_val) ) ); } else (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_modulo, (yyvsp[(1) - (4)].fal_val), (yyvsp[(4) - (4)].fal_val) ) ); } break; case 367: /* Line 1455 of yacc.c */ #line 2478 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_power, (yyvsp[(1) - (4)].fal_val), (yyvsp[(4) - (4)].fal_val) ) ); } break; case 368: /* Line 1455 of yacc.c */ #line 2479 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_bin_and, (yyvsp[(1) - (4)].fal_val), (yyvsp[(4) - (4)].fal_val) ) ); } break; case 369: /* Line 1455 of yacc.c */ #line 2480 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_bin_or, (yyvsp[(1) - (4)].fal_val), (yyvsp[(4) - (4)].fal_val) ) ); } break; case 370: /* Line 1455 of yacc.c */ #line 2481 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_bin_xor, (yyvsp[(1) - (4)].fal_val), (yyvsp[(4) - (4)].fal_val) ) ); } break; case 371: /* Line 1455 of yacc.c */ #line 2482 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_shift_left, (yyvsp[(1) - (4)].fal_val), (yyvsp[(4) - (4)].fal_val) ) ); } break; case 372: /* Line 1455 of yacc.c */ #line 2483 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_shift_right, (yyvsp[(1) - (4)].fal_val), (yyvsp[(4) - (4)].fal_val) ) ); } break; case 373: /* Line 1455 of yacc.c */ #line 2484 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_bin_not, (yyvsp[(2) - (2)].fal_val) ) ); } break; case 374: /* Line 1455 of yacc.c */ #line 2485 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_neq, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 375: /* Line 1455 of yacc.c */ #line 2486 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_post_inc, (yyvsp[(1) - (2)].fal_val) ) ); } break; case 376: /* Line 1455 of yacc.c */ #line 2487 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_pre_inc, (yyvsp[(2) - (2)].fal_val) ) ); } break; case 377: /* Line 1455 of yacc.c */ #line 2488 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_post_dec, (yyvsp[(1) - (2)].fal_val) ) ); } break; case 378: /* Line 1455 of yacc.c */ #line 2489 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_pre_dec, (yyvsp[(2) - (2)].fal_val) ) ); } break; case 379: /* Line 1455 of yacc.c */ #line 2490 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_eq, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 380: /* Line 1455 of yacc.c */ #line 2491 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_exeq, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 381: /* Line 1455 of yacc.c */ #line 2492 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_gt, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 382: /* Line 1455 of yacc.c */ #line 2493 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_lt, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 383: /* Line 1455 of yacc.c */ #line 2494 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_ge, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 384: /* Line 1455 of yacc.c */ #line 2495 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_le, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 385: /* Line 1455 of yacc.c */ #line 2496 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_and, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 386: /* Line 1455 of yacc.c */ #line 2497 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_or, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 387: /* Line 1455 of yacc.c */ #line 2498 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_not, (yyvsp[(2) - (2)].fal_val) ) ); } break; case 388: /* Line 1455 of yacc.c */ #line 2499 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_in, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 389: /* Line 1455 of yacc.c */ #line 2500 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_notin, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 390: /* Line 1455 of yacc.c */ #line 2501 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_provides, (yyvsp[(1) - (3)].fal_val), new Falcon::Value( (yyvsp[(3) - (3)].stringp) ) ) ); } break; case 391: /* Line 1455 of yacc.c */ #line 2502 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( (yyvsp[(2) - (2)].fal_val) ); } break; case 392: /* Line 1455 of yacc.c */ #line 2503 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( (Falcon::Value *) 0 ); } break; case 393: /* Line 1455 of yacc.c */ #line 2504 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_strexpand, (yyvsp[(2) - (2)].fal_val) ) ); } break; case 394: /* Line 1455 of yacc.c */ #line 2505 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_indirect, (yyvsp[(2) - (2)].fal_val) ) ); } break; case 395: /* Line 1455 of yacc.c */ #line 2506 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_eval, (yyvsp[(2) - (2)].fal_val) ) ); } break; case 396: /* Line 1455 of yacc.c */ #line 2507 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_oob, (yyvsp[(2) - (2)].fal_val) ) ); } break; case 397: /* Line 1455 of yacc.c */ #line 2508 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_deoob, (yyvsp[(2) - (2)].fal_val) ) ); } break; case 398: /* Line 1455 of yacc.c */ #line 2509 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_isoob, (yyvsp[(2) - (2)].fal_val) ) ); } break; case 399: /* Line 1455 of yacc.c */ #line 2510 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_xoroob, (yyvsp[(2) - (2)].fal_val) ) ); } break; case 406: /* Line 1455 of yacc.c */ #line 2518 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::Expression *exp = new Falcon::Expression( Falcon::Expression::t_array_access, (yyvsp[(1) - (2)].fal_val), (yyvsp[(2) - (2)].fal_val) ); (yyval.fal_val) = new Falcon::Value( exp ); } break; case 407: /* Line 1455 of yacc.c */ #line 2523 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( (yyvsp[(1) - (1)].fal_adecl) ); } break; case 408: /* Line 1455 of yacc.c */ #line 2527 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::Expression *exp = new Falcon::Expression( Falcon::Expression::t_array_access, (yyvsp[(1) - (4)].fal_val), (yyvsp[(3) - (4)].fal_val) ); (yyval.fal_val) = new Falcon::Value( exp ); } break; case 409: /* Line 1455 of yacc.c */ #line 2532 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::Expression *exp = new Falcon::Expression( Falcon::Expression::t_array_byte_access, (yyvsp[(1) - (5)].fal_val), (yyvsp[(4) - (5)].fal_val) ); (yyval.fal_val) = new Falcon::Value( exp ); } break; case 410: /* Line 1455 of yacc.c */ #line 2538 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::Expression *exp = new Falcon::Expression( Falcon::Expression::t_obj_access, (yyvsp[(1) - (3)].fal_val), new Falcon::Value( (yyvsp[(3) - (3)].stringp) ) ); if ( (yyvsp[(3) - (3)].stringp)->getCharAt(0) == '_' && ! (yyvsp[(1) - (3)].fal_val)->isSelf() ) { COMPILER->raiseError(Falcon::e_priv_access, COMPILER->tempLine() ); } (yyval.fal_val) = new Falcon::Value( exp ); } break; case 413: /* Line 1455 of yacc.c */ #line 2550 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->defineVal( (yyvsp[(1) - (3)].fal_val) ); (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_assign, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 414: /* Line 1455 of yacc.c */ #line 2555 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->defineVal( (yyvsp[(1) - (5)].fal_val) ); (yyvsp[(5) - (5)].fal_adecl)->pushFront( (yyvsp[(3) - (5)].fal_val) ); Falcon::Value *second = new Falcon::Value( (yyvsp[(5) - (5)].fal_adecl) ); (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_assign, (yyvsp[(1) - (5)].fal_val), second ) ); } break; case 415: /* Line 1455 of yacc.c */ #line 2562 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_aadd, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 416: /* Line 1455 of yacc.c */ #line 2563 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_asub, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 417: /* Line 1455 of yacc.c */ #line 2564 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_amul, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 418: /* Line 1455 of yacc.c */ #line 2565 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_adiv, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 419: /* Line 1455 of yacc.c */ #line 2566 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_amod, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 420: /* Line 1455 of yacc.c */ #line 2567 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_apow, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 421: /* Line 1455 of yacc.c */ #line 2568 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_aband, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 422: /* Line 1455 of yacc.c */ #line 2569 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_abor, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 423: /* Line 1455 of yacc.c */ #line 2570 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_abxor, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 424: /* Line 1455 of yacc.c */ #line 2571 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_ashl, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 425: /* Line 1455 of yacc.c */ #line 2572 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_ashr, (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ) ); } break; case 426: /* Line 1455 of yacc.c */ #line 2573 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" {(yyval.fal_val)=(yyvsp[(2) - (3)].fal_val);} break; case 427: /* Line 1455 of yacc.c */ #line 2578 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::RangeDecl( new Falcon::Value( (Falcon::int64) 0 ) ) ); } break; case 428: /* Line 1455 of yacc.c */ #line 2581 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::RangeDecl( (yyvsp[(2) - (4)].fal_val) ) ); } break; case 429: /* Line 1455 of yacc.c */ #line 2584 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::RangeDecl( new Falcon::Value( (Falcon::int64) 0 ), (yyvsp[(3) - (4)].fal_val) ) ); } break; case 430: /* Line 1455 of yacc.c */ #line 2587 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::RangeDecl( (yyvsp[(2) - (5)].fal_val), (yyvsp[(4) - (5)].fal_val) ) ); } break; case 431: /* Line 1455 of yacc.c */ #line 2590 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::RangeDecl( (yyvsp[(2) - (7)].fal_val), (yyvsp[(4) - (7)].fal_val), (yyvsp[(6) - (7)].fal_val) ) ); } break; case 432: /* Line 1455 of yacc.c */ #line 2597 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_funcall, (yyvsp[(1) - (4)].fal_val), new Falcon::Value( (yyvsp[(3) - (4)].fal_adecl) ) ) ); } break; case 433: /* Line 1455 of yacc.c */ #line 2603 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_funcall, (yyvsp[(1) - (3)].fal_val), 0 ) ); } break; case 434: /* Line 1455 of yacc.c */ #line 2607 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->tempLine( CURRENT_LINE ); } break; case 435: /* Line 1455 of yacc.c */ #line 2608 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { delete (yyvsp[(3) - (6)].fal_adecl); COMPILER->raiseContextError(Falcon::e_syn_funcall, COMPILER->tempLine(), CTX_LINE ); (yyval.fal_val) = new Falcon::Value; } break; case 436: /* Line 1455 of yacc.c */ #line 2617 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::FuncDef *def = new Falcon::FuncDef( 0, 0 ); // set the def as a lambda. COMPILER->incLambdaCount(); COMPILER->incClosureContext(); int id = COMPILER->lambdaCount(); // find the global symbol for this. char buf[48]; sprintf( buf, "_lambda#_id_%d", id ); Falcon::String name( buf, -1 ); // Not defined? fassert( COMPILER->searchGlobalSymbol( name ) == 0 ); Falcon::Symbol *sym = COMPILER->addGlobalSymbol( name ); // anyhow, also in case of error, destroys the previous information to allow a correct parsing // of the rest. sym->setFunction( def ); Falcon::StmtFunction *func = new Falcon::StmtFunction( COMPILER->lexer()->line(), sym ); COMPILER->addFunction( func ); func->setLambda( id ); // prepare the statement allocation context COMPILER->pushContext( func ); COMPILER->pushFunctionContext( func ); COMPILER->pushContextSet( &func->statements() ); COMPILER->pushFunction( def ); COMPILER->lexer()->pushContext( Falcon::SrcLexer::ct_inner, COMPILER->lexer()->line() ); } break; case 437: /* Line 1455 of yacc.c */ #line 2651 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->lexer()->popContext(); (yyval.fal_val) = COMPILER->closeClosure(); } break; case 438: /* Line 1455 of yacc.c */ #line 2659 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::FuncDef *def = new Falcon::FuncDef( 0, 0 ); // set the def as a lambda. COMPILER->incLambdaCount(); COMPILER->incClosureContext(); int id = COMPILER->lambdaCount(); // find the global symbol for this. char buf[48]; sprintf( buf, "_lambda#_id_%d", id ); Falcon::String name( buf, -1 ); Falcon::Symbol *sym = COMPILER->searchGlobalSymbol( name ); // Not defined? fassert( sym == 0 ); sym = COMPILER->addGlobalSymbol( name ); // anyhow, also in case of error, destroys the previous information to allow a correct parsing // of the rest. sym->setFunction( def ); Falcon::StmtFunction *func = new Falcon::StmtFunction( COMPILER->lexer()->line(), sym ); COMPILER->addFunction( func ); func->setLambda( id ); // prepare the statement allocation context COMPILER->pushContext( func ); COMPILER->pushFunctionContext( func ); COMPILER->pushContextSet( &func->statements() ); COMPILER->pushFunction( def ); } break; case 439: /* Line 1455 of yacc.c */ #line 2693 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::StatementList *stmt = COMPILER->getContextSet(); if( stmt->size() == 1 && stmt->back()->type() == Falcon::Statement::t_autoexp ) { // wrap it around a return, so A is not nilled. Falcon::StmtAutoexpr *ae = static_cast( stmt->pop_back() ); stmt->push_back( new Falcon::StmtReturn( 1, ae->value()->clone() ) ); // we don't need the expression anymore. delete ae; } (yyval.fal_val) = COMPILER->closeClosure(); } break; case 441: /* Line 1455 of yacc.c */ #line 2712 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseContextError(Falcon::e_syn_funcdecl, LINE, CTX_LINE ); } break; case 442: /* Line 1455 of yacc.c */ #line 2716 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_funcdecl ); } break; case 444: /* Line 1455 of yacc.c */ #line 2724 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseContextError(Falcon::e_syn_funcdecl, LINE, CTX_LINE ); } break; case 445: /* Line 1455 of yacc.c */ #line 2728 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseError(Falcon::e_syn_funcdecl ); } break; case 446: /* Line 1455 of yacc.c */ #line 2735 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { Falcon::FuncDef *def = new Falcon::FuncDef( 0, 0 ); // set the def as a lambda. COMPILER->incLambdaCount(); int id = COMPILER->lambdaCount(); // find the global symbol for this. char buf[48]; sprintf( buf, "_lambda#_id_%d", id ); Falcon::String name( buf, -1 ); // Not defined? fassert( COMPILER->searchGlobalSymbol( name ) == 0 ); Falcon::Symbol *sym = COMPILER->addGlobalSymbol( name ); // anyhow, also in case of error, destroys the previous information to allow a correct parsing // of the rest. sym->setFunction( def ); Falcon::StmtFunction *func = new Falcon::StmtFunction( COMPILER->lexer()->line(), sym ); COMPILER->addFunction( func ); func->setLambda( id ); // prepare the statement allocation context COMPILER->pushContext( func ); COMPILER->pushFunctionContext( func ); COMPILER->pushContextSet( &func->statements() ); COMPILER->pushFunction( def ); COMPILER->lexer()->pushContext( Falcon::SrcLexer::ct_inner, COMPILER->lexer()->line() ); } break; case 447: /* Line 1455 of yacc.c */ #line 2768 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->lexer()->popContext(); Falcon::StmtFunction *func = static_cast(COMPILER->getContext()); (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_lambda , new Falcon::Value( func->symbol() ) ) ); // analyze func in previous context. COMPILER->closeFunction(); } break; case 448: /* Line 1455 of yacc.c */ #line 2784 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_iif, (yyvsp[(1) - (5)].fal_val), (yyvsp[(3) - (5)].fal_val), (yyvsp[(5) - (5)].fal_val) ) ); } break; case 449: /* Line 1455 of yacc.c */ #line 2789 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { delete (yyvsp[(1) - (5)].fal_val); delete (yyvsp[(3) - (5)].fal_val); COMPILER->raiseError(Falcon::e_syn_iif, CURRENT_LINE ); (yyval.fal_val) = new Falcon::Value; } break; case 450: /* Line 1455 of yacc.c */ #line 2796 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { delete (yyvsp[(1) - (4)].fal_val); delete (yyvsp[(3) - (4)].fal_val); COMPILER->raiseError(Falcon::e_syn_iif, CURRENT_LINE ); (yyval.fal_val) = new Falcon::Value; } break; case 451: /* Line 1455 of yacc.c */ #line 2803 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { delete (yyvsp[(1) - (3)].fal_val); COMPILER->raiseError(Falcon::e_syn_iif, CURRENT_LINE ); (yyval.fal_val) = new Falcon::Value; } break; case 452: /* Line 1455 of yacc.c */ #line 2812 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_adecl) = new Falcon::ArrayDecl(); } break; case 453: /* Line 1455 of yacc.c */ #line 2814 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_adecl) = (yyvsp[(2) - (3)].fal_adecl); } break; case 454: /* Line 1455 of yacc.c */ #line 2818 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseContextError( Falcon::e_syn_arraydecl, CURRENT_LINE, CTX_LINE ); (yyval.fal_adecl) = (yyvsp[(2) - (3)].fal_adecl); } break; case 455: /* Line 1455 of yacc.c */ #line 2825 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::ArrayDecl() ); } break; case 456: /* Line 1455 of yacc.c */ #line 2827 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( (yyvsp[(2) - (3)].fal_adecl) ); } break; case 457: /* Line 1455 of yacc.c */ #line 2831 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseContextError( Falcon::e_syn_arraydecl, CURRENT_LINE, CTX_LINE ); (yyval.fal_val) = new Falcon::Value( (yyvsp[(2) - (3)].fal_adecl) ); } break; case 458: /* Line 1455 of yacc.c */ #line 2839 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( new Falcon::DictDecl() ); } break; case 459: /* Line 1455 of yacc.c */ #line 2840 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_val) = new Falcon::Value( (yyvsp[(2) - (3)].fal_ddecl) ); } break; case 460: /* Line 1455 of yacc.c */ #line 2842 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->raiseContextError( Falcon::e_syn_dictdecl, LINE, CTX_LINE ); (yyval.fal_val) = new Falcon::Value( (yyvsp[(2) - (4)].fal_ddecl) ); } break; case 461: /* Line 1455 of yacc.c */ #line 2849 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_adecl) = new Falcon::ArrayDecl(); (yyval.fal_adecl)->pushBack( (yyvsp[(1) - (1)].fal_val) ); } break; case 462: /* Line 1455 of yacc.c */ #line 2850 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyvsp[(1) - (3)].fal_adecl)->pushBack( (yyvsp[(3) - (3)].fal_val) ); (yyval.fal_adecl) = (yyvsp[(1) - (3)].fal_adecl); } break; case 463: /* Line 1455 of yacc.c */ #line 2854 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_adecl) = new Falcon::ArrayDecl(); (yyval.fal_adecl)->pushBack( (yyvsp[(1) - (1)].fal_val) ); } break; case 464: /* Line 1455 of yacc.c */ #line 2855 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyvsp[(1) - (3)].fal_adecl)->pushBack( (yyvsp[(3) - (3)].fal_val) ); (yyval.fal_adecl) = (yyvsp[(1) - (3)].fal_adecl); } break; case 467: /* Line 1455 of yacc.c */ #line 2862 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->defineVal( (yyvsp[(1) - (1)].fal_val) ); Falcon::ArrayDecl *ad = new Falcon::ArrayDecl(); ad->pushBack( (yyvsp[(1) - (1)].fal_val) ); (yyval.fal_adecl) = ad; } break; case 468: /* Line 1455 of yacc.c */ #line 2868 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { COMPILER->defineVal( (yyvsp[(3) - (3)].fal_val) ); (yyvsp[(1) - (3)].fal_adecl)->pushBack( (yyvsp[(3) - (3)].fal_val) ); } break; case 469: /* Line 1455 of yacc.c */ #line 2875 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyval.fal_ddecl) = new Falcon::DictDecl(); (yyval.fal_ddecl)->pushBack( (yyvsp[(1) - (3)].fal_val), (yyvsp[(3) - (3)].fal_val) ); } break; case 470: /* Line 1455 of yacc.c */ #line 2876 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" { (yyvsp[(1) - (5)].fal_ddecl)->pushBack( (yyvsp[(3) - (5)].fal_val), (yyvsp[(5) - (5)].fal_val) ); (yyval.fal_ddecl) = (yyvsp[(1) - (5)].fal_ddecl); } break; /* Line 1455 of yacc.c */ #line 7581 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.cpp" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*------------------------------------. | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else { YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) { YYSIZE_T yyalloc = 2 * yysize; if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) yyalloc = YYSTACK_ALLOC_MAXIMUM; if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yyalloc); if (yymsg) yymsg_alloc = yyalloc; else { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; } } if (0 < yysize && yysize <= yymsg_alloc) { (void) yysyntax_error (yymsg, yystate, yychar); yyerror (yymsg); } else { yyerror (YY_("syntax error")); if (yysize != 0) goto yyexhaustedlab; } } #endif } if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; /* Do not reclaim the symbols of the rule which action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (yyn != YYPACT_NINF) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct ("Error: popping", yystos[yystate], yyvsp); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } *++yyvsp = yylval; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #if !defined(yyoverflow) || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEMPTY) yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval); /* Do not reclaim the symbols of the rule which action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif /* Make sure YYID is used. */ return YYID (yyresult); } /* Line 1675 of yacc.c */ #line 2880 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" /* c code */ void flc_src_error (const char *s) /* Called by yyparse on error */ { /* do nothing: manage it in the action */ } /* end of src_parser.yy */ engine/src_parser.hpp000066400000000000000000000156461176363201700152210ustar00rootroot00000000000000 /* A Bison parser, made by GNU Bison 2.4.1. */ /* Skeleton interface for Bison's Yacc-like parsers in C Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { EOL = 258, INTNUM = 259, DBLNUM = 260, SYMBOL = 261, STRING = 262, NIL = 263, UNB = 264, END = 265, DEF = 266, WHILE = 267, BREAK = 268, CONTINUE = 269, DROPPING = 270, IF = 271, ELSE = 272, ELIF = 273, FOR = 274, FORFIRST = 275, FORLAST = 276, FORMIDDLE = 277, SWITCH = 278, CASE = 279, DEFAULT = 280, SELECT = 281, SELF = 282, FSELF = 283, TRY = 284, CATCH = 285, RAISE = 286, CLASS = 287, FROM = 288, OBJECT = 289, RETURN = 290, GLOBAL = 291, INIT = 292, LOAD = 293, LAUNCH = 294, CONST_KW = 295, EXPORT = 296, IMPORT = 297, DIRECTIVE = 298, COLON = 299, FUNCDECL = 300, STATIC = 301, INNERFUNC = 302, FORDOT = 303, LISTPAR = 304, LOOP = 305, ENUM = 306, TRUE_TOKEN = 307, FALSE_TOKEN = 308, OUTER_STRING = 309, CLOSEPAR = 310, OPENPAR = 311, CLOSESQUARE = 312, OPENSQUARE = 313, DOT = 314, OPEN_GRAPH = 315, CLOSE_GRAPH = 316, ARROW = 317, VBAR = 318, ASSIGN_POW = 319, ASSIGN_SHL = 320, ASSIGN_SHR = 321, ASSIGN_BXOR = 322, ASSIGN_BOR = 323, ASSIGN_BAND = 324, ASSIGN_MOD = 325, ASSIGN_DIV = 326, ASSIGN_MUL = 327, ASSIGN_SUB = 328, ASSIGN_ADD = 329, OP_EQ = 330, OP_AS = 331, OP_TO = 332, COMMA = 333, QUESTION = 334, OR = 335, AND = 336, NOT = 337, LE = 338, GE = 339, LT = 340, GT = 341, NEQ = 342, EEQ = 343, OP_EXEQ = 344, PROVIDES = 345, OP_NOTIN = 346, OP_IN = 347, DIESIS = 348, ATSIGN = 349, CAP_CAP = 350, VBAR_VBAR = 351, AMPER_AMPER = 352, MINUS = 353, PLUS = 354, PERCENT = 355, SLASH = 356, STAR = 357, POW = 358, SHR = 359, SHL = 360, CAP_XOROOB = 361, CAP_ISOOB = 362, CAP_DEOOB = 363, CAP_OOB = 364, CAP_EVAL = 365, TILDE = 366, NEG = 367, AMPER = 368, DECREMENT = 369, INCREMENT = 370, DOLLAR = 371 }; #endif /* Tokens. */ #define EOL 258 #define INTNUM 259 #define DBLNUM 260 #define SYMBOL 261 #define STRING 262 #define NIL 263 #define UNB 264 #define END 265 #define DEF 266 #define WHILE 267 #define BREAK 268 #define CONTINUE 269 #define DROPPING 270 #define IF 271 #define ELSE 272 #define ELIF 273 #define FOR 274 #define FORFIRST 275 #define FORLAST 276 #define FORMIDDLE 277 #define SWITCH 278 #define CASE 279 #define DEFAULT 280 #define SELECT 281 #define SELF 282 #define FSELF 283 #define TRY 284 #define CATCH 285 #define RAISE 286 #define CLASS 287 #define FROM 288 #define OBJECT 289 #define RETURN 290 #define GLOBAL 291 #define INIT 292 #define LOAD 293 #define LAUNCH 294 #define CONST_KW 295 #define EXPORT 296 #define IMPORT 297 #define DIRECTIVE 298 #define COLON 299 #define FUNCDECL 300 #define STATIC 301 #define INNERFUNC 302 #define FORDOT 303 #define LISTPAR 304 #define LOOP 305 #define ENUM 306 #define TRUE_TOKEN 307 #define FALSE_TOKEN 308 #define OUTER_STRING 309 #define CLOSEPAR 310 #define OPENPAR 311 #define CLOSESQUARE 312 #define OPENSQUARE 313 #define DOT 314 #define OPEN_GRAPH 315 #define CLOSE_GRAPH 316 #define ARROW 317 #define VBAR 318 #define ASSIGN_POW 319 #define ASSIGN_SHL 320 #define ASSIGN_SHR 321 #define ASSIGN_BXOR 322 #define ASSIGN_BOR 323 #define ASSIGN_BAND 324 #define ASSIGN_MOD 325 #define ASSIGN_DIV 326 #define ASSIGN_MUL 327 #define ASSIGN_SUB 328 #define ASSIGN_ADD 329 #define OP_EQ 330 #define OP_AS 331 #define OP_TO 332 #define COMMA 333 #define QUESTION 334 #define OR 335 #define AND 336 #define NOT 337 #define LE 338 #define GE 339 #define LT 340 #define GT 341 #define NEQ 342 #define EEQ 343 #define OP_EXEQ 344 #define PROVIDES 345 #define OP_NOTIN 346 #define OP_IN 347 #define DIESIS 348 #define ATSIGN 349 #define CAP_CAP 350 #define VBAR_VBAR 351 #define AMPER_AMPER 352 #define MINUS 353 #define PLUS 354 #define PERCENT 355 #define SLASH 356 #define STAR 357 #define POW 358 #define SHR 359 #define SHL 360 #define CAP_XOROOB 361 #define CAP_ISOOB 362 #define CAP_DEOOB 363 #define CAP_OOB 364 #define CAP_EVAL 365 #define TILDE 366 #define NEG 367 #define AMPER 368 #define DECREMENT 369 #define INCREMENT 370 #define DOLLAR 371 #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union /* Line 1676 of yacc.c */ #line 61 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" lex_value_t { /* Line 1676 of yacc.c */ #line 61 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.yy" Falcon::int64 integer; Falcon::numeric numeric; char * charp; Falcon::String *stringp; Falcon::Symbol *symbol; Falcon::Value *fal_val; Falcon::Expression *fal_exp; Falcon::Statement *fal_stat; Falcon::ArrayDecl *fal_adecl; Falcon::DictDecl *fal_ddecl; Falcon::SymbolList *fal_symlist; Falcon::List *fal_genericList; /* Line 1676 of yacc.c */ #line 304 "/home/gian/Progetti/FalconRepo/falcon_master/engine/src_parser.hpp" } YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 #endif engine/src_parser.yy000066400000000000000000002532141176363201700150660ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: src_parser.yy Bison grammar definition for falcon. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven mag 21 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ %{ #include #include #include #include #include #include #include #include #include #include #include #define YYMALLOC Falcon::memAlloc #define YYFREE Falcon::memFree #define COMPILER ( reinterpret_cast< Falcon::Compiler *>(yyparam) ) #define CTX_LINE ( COMPILER->lexer()->contextStart() ) #define LINE ( COMPILER->lexer()->previousLine() ) #define CURRENT_LINE ( COMPILER->lexer()->line() ) #define YYPARSE_PARAM yyparam #define YYLEX_PARAM yyparam int flc_src_parse( void *param ); void flc_src_error (const char *s); inline int flc_src_lex (void *lvalp, void *yyparam) { return COMPILER->lexer()->doLex( lvalp ); } /* Cures a bug in bison 1.8 */ #undef __GNUC_MINOR__ %} %union lex_value_t { Falcon::int64 integer; Falcon::numeric numeric; char * charp; Falcon::String *stringp; Falcon::Symbol *symbol; Falcon::Value *fal_val; Falcon::Expression *fal_exp; Falcon::Statement *fal_stat; Falcon::ArrayDecl *fal_adecl; Falcon::DictDecl *fal_ddecl; Falcon::SymbolList *fal_symlist; Falcon::List *fal_genericList; } /*%debug*/ %pure_parser %defines %name-prefix="flc_src_" %token EOL %token INTNUM %token DBLNUM %token SYMBOL %token STRING %token NIL %token UNB %token END %token DEF %token WHILE BREAK CONTINUE DROPPING %token IF ELSE ELIF %token FOR %token FORFIRST FORLAST FORMIDDLE %token SWITCH CASE DEFAULT %token SELECT %token SELF FSELF %token TRY CATCH RAISE %token CLASS FROM OBJECT %token RETURN %token GLOBAL %token INIT %token LOAD %token LAUNCH %token CONST_KW %token EXPORT %token IMPORT %token DIRECTIVE %token COLON %token FUNCDECL STATIC %token INNERFUNC %token FORDOT %token LISTPAR %token LOOP %token ENUM %token TRUE_TOKEN %token FALSE_TOKEN /* Special token used by the parser to generate a parse print */ %token OUTER_STRING %token CLOSEPAR OPENPAR CLOSESQUARE OPENSQUARE DOT OPEN_GRAPH CLOSE_GRAPH /* Assigning rule precendence: immediate operations have maximum precedence, being resolved immediately, then the assignment gets more precendece than the expressions (OP_EQ is preferibily parsed as an assignment request where ambiguity arises). */ %left ARROW %right VBAR %right ASSIGN_ADD ASSIGN_SUB ASSIGN_MUL ASSIGN_DIV ASSIGN_MOD ASSIGN_BAND ASSIGN_BOR ASSIGN_BXOR ASSIGN_SHR ASSIGN_SHL ASSIGN_POW %right OP_EQ %right COMMA OP_TO OP_AS %left QUESTION %right COLON %left OR %left AND %right NOT %left OP_EXEQ EEQ NEQ GT LT GE LE %left OP_IN OP_NOTIN PROVIDES %right ATSIGN DIESIS %left VBAR_VBAR CAP_CAP %left AMPER_AMPER %left PLUS MINUS %left STAR SLASH PERCENT %left POW %left SHL SHR %right NEG TILDE CAP_EVAL CAP_OOB CAP_DEOOB CAP_ISOOB CAP_XOROOB %right DOLLAR INCREMENT DECREMENT AMPER %type INTNUM_WITH_MINUS %type expression_list listpar_expression_list array_decl %type symbol_list %type expression_pair_list %type import_symbol_list %type expression func_call nameless_func innerfunc nameless_block iif_expr %type for_to_expr for_to_step_clause loop_terminator %type switch_decl select_decl while_decl while_short_decl %type if_decl if_short_decl elif_decl %type const_atom const_atom_non_minus var_atom atomic_symbol /* atom */ %type dotarray_decl dict_decl %type inherit_call %type break_statement continue_statement %type toplevel_statement statement while_statement if_statement %type forin_statement switch_statement fordot_statement forin_header %type select_statement %type try_statement raise_statement return_statement global_statement %type base_statement %type launch_statement %type func_statement %type self_print_statement %type enum_statement %type class_decl object_decl property_decl state_decl export_statement directive_statement %type import_statement %type def_statement %type loop_statement %type outer_print_statement %type const_statement %type range_decl %% /**************************************************** * Rules for falcon. *****************************************************/ /* input: {yydebug = 1; } body ;*/ input: body ; body: /*empty */ | body line ; line: toplevel_statement | END EOL { COMPILER->raiseError(Falcon::e_lone_end ); } | CASE error EOL { COMPILER->raiseError(Falcon::e_case_outside ); } ; toplevel_statement: load_statement { $$=0; } | directive_statement | func_statement { if( $1 != 0 ) COMPILER->addFunction( $1 ); } | class_decl { if ( $1 != 0 ) COMPILER->addClass( $1 ); } | object_decl { if ( $1 != 0 ) COMPILER->addClass( $1 ); } | enum_statement { if ( $1 != 0 ) COMPILER->addClass( $1 ); } | statement { if( $1 != 0 ) COMPILER->addStatement( $1 ); } | const_statement /* no action */ | export_statement /* no action */ | import_statement /* no action */ ; INTNUM_WITH_MINUS: INTNUM | MINUS INTNUM %prec NEG { $$ = - $2; } ; load_statement: LOAD SYMBOL EOL { if ( COMPILER->getContext() != 0 ) COMPILER->raiseError(Falcon::e_toplevel_load ); COMPILER->addLoad( *$2, false ); } | LOAD STRING EOL { if ( COMPILER->getContext() != 0 ) COMPILER->raiseError(Falcon::e_toplevel_load ); COMPILER->addLoad( *$2, true ); } | LOAD error EOL { COMPILER->raiseError(Falcon::e_syn_load ); } ; statement: base_statement { COMPILER->checkLocalUndefined(); $$ = $1; } | attribute_statement {$$=0;}/* no action */ | EOL { $$ = 0; } | FUNCDECL error EOL { COMPILER->raiseError(Falcon::e_toplevel_func ); $$ = 0; } | OBJECT error EOL { COMPILER->raiseError(Falcon::e_toplevel_obj ); $$ = 0; } | CLASS error EOL { COMPILER->raiseError(Falcon::e_toplevel_class ); $$ = 0; } | error EOL { COMPILER->raiseError(Falcon::e_syntax ); $$ = 0;} ; base_statement: expression EOL { if( (! COMPILER->isInteractive()) && ((!$1->isExpr()) || (!$1->asExpr()->isStandAlone()) ) ) { Falcon::StmtFunction *func = COMPILER->getFunctionContext(); if ( func == 0 || ! func->isLambda() ) COMPILER->raiseError(Falcon::e_noeffect ); } $$ = new Falcon::StmtAutoexpr( LINE, $1 ); } | expression_list OP_EQ expression EOL { if ( $3->type() == Falcon::Value::t_array_decl ) { Falcon::ArrayDecl* ad = $3->asArray(); if ( $1->size() != ad->size() ) { COMPILER->raiseError(Falcon::e_unpack_size ); } } Falcon::Value *first = new Falcon::Value( $1 ); COMPILER->defineVal( first ); $$ = new Falcon::StmtAutoexpr( LINE, new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_assign, first, $3 ) ) ); } | expression_list OP_EQ expression COMMA expression_list EOL { if ( $1->size() != $5->size() + 1 ) { COMPILER->raiseError(Falcon::e_unpack_size ); } Falcon::Value *first = new Falcon::Value( $1 ); COMPILER->defineVal( first ); $5->pushFront( $3 ); Falcon::Value *second = new Falcon::Value( $5 ); $$ = new Falcon::StmtAutoexpr( LINE, new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_assign, first, second ) ) ); } | def_statement /* no action -- at the moment def_statement always returns 0*/ | while_statement | loop_statement | forin_statement | switch_statement | select_statement | if_statement | break_statement | continue_statement | try_statement | raise_statement | return_statement | global_statement | launch_statement | fordot_statement | self_print_statement | outer_print_statement ; assignment_def_list: assignment_def_list_element | assignment_def_list COMMA assignment_def_list_element ; assignment_def_list_element: atomic_symbol { COMPILER->defContext( true ); COMPILER->defineVal( $1 ); COMPILER->addStatement( new Falcon::StmtAutoexpr(CURRENT_LINE, new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_assign, $1, new Falcon::Value() ) ) ) ); } | atomic_symbol OP_EQ expression { COMPILER->defContext( true ); COMPILER->defineVal( $1 ); COMPILER->addStatement( new Falcon::StmtAutoexpr(CURRENT_LINE, new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_assign, $1, $3 ) ) ) ); } ; def_statement: DEF assignment_def_list EOL { COMPILER->defContext( false ); $$=0; } | DEF error EOL { COMPILER->raiseError( Falcon::e_syn_def ); } ; while_statement: while_decl { Falcon::StmtWhile *w = new Falcon::StmtWhile( LINE, $1 ); COMPILER->pushLoop( w ); COMPILER->pushContext( w ); COMPILER->pushContextSet( &w->children() ); } statement_list END EOL { Falcon::StmtWhile *w = static_cast(COMPILER->getContext()); COMPILER->popLoop(); COMPILER->popContext(); COMPILER->popContextSet(); $$ = w; } | while_short_decl statement { Falcon::StmtWhile *w = new Falcon::StmtWhile( LINE, $1 ); if ( $2 != 0 ) w->children().push_back( $2 ); $$ = w; } while_decl: WHILE expression EOL { $$ = $2; } | WHILE error EOL { COMPILER->raiseError(Falcon::e_syn_while ); $$ = 0; } ; while_short_decl: WHILE expression COLON { $$ = $2; } | WHILE error COLON { COMPILER->raiseError(Falcon::e_syn_while, "", CURRENT_LINE ); $$ = 0; } ; loop_statement: LOOP EOL { Falcon::StmtLoop *w = new Falcon::StmtLoop( LINE ); COMPILER->pushLoop( w ); COMPILER->pushContext( w ); COMPILER->pushContextSet( &w->children() ); } statement_list END loop_terminator EOL { Falcon::StmtLoop *w = static_cast(COMPILER->getContext()); w->setCondition($6); COMPILER->popLoop(); COMPILER->popContext(); COMPILER->popContextSet(); $$ = w; } | LOOP COLON statement { Falcon::StmtLoop *w = new Falcon::StmtLoop( LINE ); if ( $3 != 0 ) w->children().push_back( $3 ); $$ = w; } | LOOP error EOL { COMPILER->raiseError( Falcon::e_syn_loop ); $$ = 0; } ; loop_terminator: { $$=0; } | expression { $$ = $1; } ; if_statement: if_decl { Falcon::StmtIf *stmt = new Falcon::StmtIf( LINE, $1 ); COMPILER->pushContext( stmt ); COMPILER->pushContextSet( &stmt->children() ); } statement_list elif_or_else END EOL { Falcon::StmtIf *stmt = static_cast(COMPILER->getContext()); COMPILER->popContext(); COMPILER->popContextSet(); $$ = stmt; } | if_short_decl statement { // use LINE as statement includes EOL Falcon::StmtIf *stmt = new Falcon::StmtIf( LINE, $1 ); if( $2 != 0 ) stmt->children().push_back( $2 ); $$ = stmt; } ; if_decl: IF expression EOL { $$ = $2; } | IF error EOL { COMPILER->raiseError(Falcon::e_syn_if ); $$ = 0; } ; if_short_decl: IF expression COLON { $$ = $2; } | IF error COLON { COMPILER->raiseError(Falcon::e_syn_if, "", CURRENT_LINE ); $$ = 0; } ; elif_or_else: /* nothing */ | elif_statement | else_decl { Falcon::StmtIf *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); COMPILER->pushContextSet( &stmt->elseChildren() ); } statement_list ; else_decl: ELSE EOL | ELSE error EOL { COMPILER->raiseError(Falcon::e_syn_else ); } ; elif_statement: elif_decl { Falcon::StmtIf *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtElif *elif = new Falcon::StmtElif( LINE, $1 ); stmt->elifChildren().push_back( elif ); COMPILER->pushContextSet( &elif->children() ); } statement_list elif_or_else ; elif_decl: ELIF expression EOL { $$ = $2; } | ELIF error EOL { COMPILER->raiseError(Falcon::e_syn_elif ); $$ = 0; } ; statement_list: /* empty */ | statement_list statement { COMPILER->addStatement( $2 ); } ; break_statement: BREAK EOL { if ( COMPILER->getLoop() == 0 ) { COMPILER->raiseError(Falcon::e_break_out ); $$ = 0; } else $$ = new Falcon::StmtBreak( LINE ); } | BREAK error EOL { COMPILER->raiseError(Falcon::e_syn_break ); $$ = 0; } ; continue_statement: CONTINUE EOL { if ( COMPILER->getLoop() == 0 ) { COMPILER->raiseError(Falcon::e_continue_out ); $$ = 0; } else $$ = new Falcon::StmtContinue( LINE ); } | CONTINUE DROPPING EOL { if ( COMPILER->getLoop() == 0 ) { COMPILER->raiseError(Falcon::e_continue_out ); $$ = 0; } else $$ = new Falcon::StmtContinue( LINE, true ); } | CONTINUE error EOL { COMPILER->raiseError(Falcon::e_syn_continue ); $$ = 0; } ; forin_header: FOR symbol_list OP_IN expression { $$ = new Falcon::StmtForin( LINE, $2, $4 ); } | FOR atomic_symbol OP_EQ for_to_expr { COMPILER->defineVal( $2 ); Falcon::ArrayDecl *decl = new Falcon::ArrayDecl(); decl->pushBack( $2 ); $$ = new Falcon::StmtForin( LINE, decl, $4 ); } | FOR symbol_list OP_IN error EOL { delete $2; COMPILER->raiseError( Falcon::e_syn_forin ); $$ = 0; } | FOR error EOL { COMPILER->raiseError( Falcon::e_syn_forin ); $$ = 0; } ; forin_statement: forin_header COLON { Falcon::StmtForin *f = static_cast($1); COMPILER->pushLoop( f ); COMPILER->pushContext( f ); COMPILER->pushContextSet( &f->children() ); } statement { if( $4 != 0 ) { COMPILER->addStatement( $4 ); } COMPILER->popLoop(); COMPILER->popContext(); COMPILER->popContextSet(); $$ = $1; } | forin_header EOL { Falcon::StmtForin *f = static_cast($1); COMPILER->pushLoop( f ); COMPILER->pushContext( f ); COMPILER->pushContextSet( &f->children() ); } forin_statement_list END EOL { Falcon::StmtForin *f = static_cast(COMPILER->getContext()); COMPILER->popLoop(); COMPILER->popContext(); COMPILER->popContextSet(); $$ = f; } ; for_to_expr: expression OP_TO expression for_to_step_clause { Falcon::RangeDecl* rd = new Falcon::RangeDecl( $1, new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_oob, $3)), $4 ); $$ = new Falcon::Value( rd ); } | expression OP_TO expression error { $$ = new Falcon::Value( new Falcon::RangeDecl( $1, $3, 0 ) ); } | expression OP_TO error { $$ = new Falcon::Value( new Falcon::RangeDecl( $1, 0, 0 ) ); } ; for_to_step_clause: /* nothing */ { $$=0; } | COMMA expression { $$=new Falcon::Value( $2 ); } | COMMA error { $$=0; } ; forin_statement_list: /* nothing */ | forin_statement_elem forin_statement_list ; forin_statement_elem: statement { if ( $1 != 0 ) { Falcon::StmtForin *f = static_cast(COMPILER->getContext()); f->children().push_back( $1 ); } } | first_loop_block | last_loop_block | middle_loop_block ; fordot_statement: FORDOT expression EOL { Falcon::StmtForin *f = static_cast(COMPILER->getLoop()); if ( f == 0 || f->type() != Falcon::Statement::t_forin ) { COMPILER->raiseError( Falcon::e_syn_fordot ); delete $2; $$ = 0; } else { $$ = new Falcon::StmtFordot( LINE, $2 ); } } | FORDOT error EOL { COMPILER->raiseError( Falcon::e_syn_fordot ); $$ = 0; } ; self_print_statement: SHR expression_list EOL { $$ = new Falcon::StmtSelfPrint( LINE, $2 ); } | SHR EOL { COMPILER->raiseError( Falcon::e_syn_self_print ); $$ = 0; } | GT expression_list EOL { $2->pushBack( new Falcon::Value( COMPILER->addString( "\n" ) ) ); $$ = new Falcon::StmtSelfPrint( LINE, $2 ); } | GT EOL { Falcon::ArrayDecl *adecl = new Falcon::ArrayDecl(); adecl->pushBack( new Falcon::Value( COMPILER->addString( "\n" ) ) ); $$ = new Falcon::StmtSelfPrint( LINE, adecl ); } | SHR error EOL { COMPILER->raiseError( Falcon::e_syn_self_print ); $$ = 0; } | GT error EOL { COMPILER->raiseError( Falcon::e_syn_self_print ); $$ = 0; } ; outer_print_statement: OUTER_STRING { Falcon::ArrayDecl *adecl = new Falcon::ArrayDecl(); adecl->pushBack( new Falcon::Value( $1 ) ); $$ = new Falcon::StmtSelfPrint( LINE, adecl ); } ; first_loop_block: FORFIRST EOL { Falcon::StmtForin *f = static_cast(COMPILER->getContext()); if( ! f->firstBlock().empty() ) { COMPILER->raiseError( Falcon::e_already_forfirst ); } COMPILER->pushContextSet( &f->firstBlock() ); // Push anyhow an empty item, that is needed for to check again for thio blosk f->firstBlock().push_back( new Falcon::StmtNone( LINE ) ); } statement_list END EOL { COMPILER->popContextSet(); } | FORFIRST COLON statement { Falcon::StmtForin *f = static_cast(COMPILER->getContext()); if( ! f->firstBlock().empty() ) { COMPILER->raiseError( Falcon::e_already_forfirst ); } if ( $3 != 0 ) f->firstBlock().push_back( $3 ); } | FORFIRST error EOL { COMPILER->raiseError(Falcon::e_syn_forfirst ); } ; last_loop_block: FORLAST EOL { Falcon::StmtForin *f = static_cast(COMPILER->getContext()); if( ! f->lastBlock().empty() ) { COMPILER->raiseError( Falcon::e_already_forlast ); } // Push anyhow an empty item, that is needed for empty last blocks f->lastBlock().push_back( new Falcon::StmtNone( LINE ) ); COMPILER->pushContextSet( &f->lastBlock() ); } statement_list END EOL { COMPILER->popContextSet(); } | FORLAST COLON statement { Falcon::StmtForin *f = static_cast(COMPILER->getContext()); if( ! f->lastBlock().empty() ) { COMPILER->raiseError( Falcon::e_already_forlast ); } if ( $3 != 0 ) f->lastBlock().push_back( $3 ); } | FORLAST error EOL { COMPILER->raiseError(Falcon::e_syn_forlast ); } ; middle_loop_block: FORMIDDLE EOL { Falcon::StmtForin *f = static_cast(COMPILER->getContext()); if( ! f->middleBlock().empty() ) { COMPILER->raiseError( Falcon::e_already_formiddle ); } // Push anyhow an empty item, that is needed for empty last blocks // Apparently you get a segfault without it. // (Note that the formiddle: version below does *not* need it f->middleBlock().push_back( new Falcon::StmtNone( LINE ) ); COMPILER->pushContextSet( &f->middleBlock() ); } statement_list END EOL { COMPILER->popContextSet(); } | FORMIDDLE COLON statement { Falcon::StmtForin *f = static_cast(COMPILER->getContext()); if( ! f->middleBlock().empty() ) { COMPILER->raiseError( Falcon::e_already_formiddle ); } if ( $3 != 0 ) f->middleBlock().push_back( $3 ); } | FORMIDDLE error EOL { COMPILER->raiseError(Falcon::e_syn_formiddle ); } ; switch_statement: switch_decl { Falcon::StmtSwitch *stmt = new Falcon::StmtSwitch( LINE, $1 ); COMPILER->pushContext( stmt ); COMPILER->pushContextSet( &stmt->blocks() ); } case_list default_statement END EOL { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); COMPILER->popContext(); COMPILER->popContextSet(); $$ = stmt; } ; switch_decl: SWITCH expression EOL { $$ = $2; } |SWITCH error EOL { COMPILER->raiseError(Falcon::e_switch_decl ); $$ = 0; } ; case_list: /* nothing */ | case_list case_statement | case_list error EOL { COMPILER->raiseError(Falcon::e_switch_body ); } ; case_statement: EOL | CASE case_expression_list EOL { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtCaseBlock *lst = new Falcon::StmtCaseBlock( LINE ); COMPILER->pushContextSet( &lst->children() ); stmt->addBlock( lst ); } statement_list | CASE case_expression_list COLON { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtCaseBlock *lst = new Falcon::StmtCaseBlock( CURRENT_LINE ); COMPILER->pushContextSet( &lst->children() ); stmt->addBlock( lst ); } statement { COMPILER->addStatement( $5 ); } | CASE error EOL { COMPILER->raiseError(Falcon::e_case_decl ); Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtCaseBlock *lst = new Falcon::StmtCaseBlock( LINE ); COMPILER->pushContextSet( &lst->children() ); stmt->addBlock( lst ); } statement_list | CASE error COLON { COMPILER->raiseError(Falcon::e_case_decl ); Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtCaseBlock *lst = new Falcon::StmtCaseBlock( CURRENT_LINE ); COMPILER->pushContextSet( &lst->children() ); stmt->addBlock( lst ); } statement { COMPILER->addStatement( $5 ); } ; default_statement: /* noting */ | default_decl { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); if ( ! stmt->defaultBlock().empty() ) { COMPILER->raiseError(Falcon::e_switch_default, "", CURRENT_LINE ); } COMPILER->pushContextSet( &stmt->defaultBlock() ); } default_body ; default_decl: | DEFAULT | DEFAULT error { COMPILER->raiseError(Falcon::e_default_decl ); } default_body: EOL statement_list | COLON statement { COMPILER->addStatement( $2 ); } ; case_expression_list: case_element | case_expression_list COMMA case_element ; case_element: NIL { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); // todo: correct error if ( stmt->nilBlock() != -1 ) COMPILER->raiseError(Falcon::e_switch_clash, "nil entry", CURRENT_LINE ); stmt->nilBlock( stmt->currentBlock() ); } | INTNUM_WITH_MINUS { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); // todo: correct error Falcon::Value *val = new Falcon::Value( $1 ); if ( ! stmt->addIntCase( val ) ) { COMPILER->raiseError(Falcon::e_switch_clash, "", CURRENT_LINE ); delete val; } } | STRING { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); Falcon::Value *val = new Falcon::Value( $1 ); if ( ! stmt->addStringCase( val ) ) { COMPILER->raiseError(Falcon::e_switch_clash, "", CURRENT_LINE ); delete val; } } | INTNUM_WITH_MINUS OP_TO INTNUM_WITH_MINUS { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); Falcon::Value *val = new Falcon::Value( new Falcon::RangeDecl( new Falcon::Value( $1 ), new Falcon::Value( $3 ) ) ); if ( ! stmt->addRangeCase( val ) ) { COMPILER->raiseError(Falcon::e_switch_clash, "", CURRENT_LINE ); delete val; } } | SYMBOL { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); Falcon::Symbol *sym = COMPILER->searchLocalSymbol( *$1 ); if( sym == 0 ) sym = COMPILER->addGlobalSymbol( *$1 ); Falcon::Value *val = new Falcon::Value( sym ); if ( ! stmt->addSymbolCase( val ) ) { COMPILER->raiseError(Falcon::e_switch_clash, "", CURRENT_LINE ); delete val; } } ; /***************************************************** select statement ******************************************************/ select_statement: select_decl { Falcon::StmtSelect *stmt = new Falcon::StmtSelect( LINE, $1 ); COMPILER->pushContext( stmt ); COMPILER->pushContextSet( &stmt->blocks() ); } selcase_list default_statement END EOL { Falcon::StmtSelect *stmt = static_cast(COMPILER->getContext()); COMPILER->popContext(); COMPILER->popContextSet(); $$ = stmt; } ; select_decl: SELECT expression EOL { $$ = $2; } |SELECT error EOL { COMPILER->raiseError(Falcon::e_select_decl ); $$ = 0; } ; selcase_list: /* nothing */ | selcase_list selcase_statement | selcase_list error EOL { COMPILER->raiseError(Falcon::e_select_body ); } ; selcase_statement: EOL | CASE selcase_expression_list EOL { Falcon::StmtSelect *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtCaseBlock *lst = new Falcon::StmtCaseBlock( LINE ); COMPILER->pushContextSet( &lst->children() ); stmt->addBlock( lst ); } statement_list | CASE selcase_expression_list COLON { Falcon::StmtSelect *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtCaseBlock *lst = new Falcon::StmtCaseBlock( CURRENT_LINE ); COMPILER->pushContextSet( &lst->children() ); stmt->addBlock( lst ); } statement { COMPILER->addStatement( $5 ); } | CASE error EOL { COMPILER->raiseError(Falcon::e_case_decl ); Falcon::StmtSelect *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtCaseBlock *lst = new Falcon::StmtCaseBlock( LINE ); COMPILER->pushContextSet( &lst->children() ); stmt->addBlock( lst ); } statement_list | CASE error COLON { COMPILER->raiseError(Falcon::e_case_decl ); Falcon::StmtSelect *stmt = static_cast(COMPILER->getContext()); COMPILER->popContextSet(); Falcon::StmtCaseBlock *lst = new Falcon::StmtCaseBlock( CURRENT_LINE ); COMPILER->pushContextSet( &lst->children() ); stmt->addBlock( lst ); } statement { COMPILER->addStatement( $5 ); } ; ; selcase_expression_list: selcase_element | selcase_expression_list COMMA selcase_element ; selcase_element: | INTNUM { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); // todo: correct error Falcon::Value *val = new Falcon::Value( $1 ); if ( ! stmt->addIntCase( val ) ) { COMPILER->raiseError(Falcon::e_switch_clash, "", CURRENT_LINE ); delete val; } } | SYMBOL { Falcon::StmtSwitch *stmt = static_cast(COMPILER->getContext()); Falcon::Symbol *sym = COMPILER->searchLocalSymbol( *$1 ); if( sym == 0 ) sym = COMPILER->addGlobalSymbol( *$1 ); Falcon::Value *val = new Falcon::Value( sym ); if ( ! stmt->addSymbolCase( val ) ) { COMPILER->raiseError(Falcon::e_switch_clash, "", CURRENT_LINE ); delete val; } } ; /***************************************************** Try statement ******************************************************/ try_statement: TRY COLON statement { Falcon::StmtTry *t = new Falcon::StmtTry( CURRENT_LINE ); if ( $3 != 0 ) t->children().push_back( $3 ); $$ = t; } | try_decl { Falcon::StmtTry *t = new Falcon::StmtTry( LINE ); COMPILER->pushContext( t ); COMPILER->pushContextSet( &t->children() ); } statement_list catch_statements END EOL { $$ = COMPILER->getContext(); COMPILER->popContext(); COMPILER->popContextSet(); } ; try_decl: TRY EOL | TRY error EOL { COMPILER->raiseError(Falcon::e_syn_try ); } ; catch_statements: /* nothing */ | catch_list ; catch_list: catch_statement | catch_list catch_statement ; catch_statement: catch_decl statement_list ; catch_decl: CATCH EOL { COMPILER->popContextSet(); // popping previous catch Falcon::StmtTry *t = static_cast( COMPILER->getContext() ); // if we have already a default, raise an error if( t->defaultHandler() != 0 ) { COMPILER->raiseError(Falcon::e_catch_adef ); } // but continue by pushing this new context Falcon::StmtCatchBlock *lst = new Falcon::StmtCatchBlock( LINE, 0 ); t->defaultHandler( lst ); // will delete the previous one COMPILER->pushContextSet( &lst->children() ); } | CATCH OP_IN atomic_symbol EOL { COMPILER->popContextSet(); // popping previous catch Falcon::StmtTry *t = static_cast( COMPILER->getContext() ); // if we have already a default, raise an error if( t->defaultHandler() != 0 ) { COMPILER->raiseError(Falcon::e_catch_adef ); } // but continue by pushing this new context COMPILER->defineVal( $3 ); Falcon::StmtCatchBlock *lst = new Falcon::StmtCatchBlock( LINE, $3 ); t->defaultHandler( lst ); // will delete the previous one COMPILER->pushContextSet( &lst->children() ); } | CATCH catchcase_element_list EOL { COMPILER->popContextSet(); // popping previous catch Falcon::StmtTry *t = static_cast( COMPILER->getContext() ); Falcon::StmtCatchBlock *lst = new Falcon::StmtCatchBlock( LINE, 0 ); COMPILER->pushContextSet( &lst->children() ); t->addHandler( lst ); } | CATCH catchcase_element_list OP_IN atomic_symbol EOL { COMPILER->popContextSet(); // popping previous catch Falcon::StmtTry *t = static_cast( COMPILER->getContext() ); COMPILER->defineVal( $4 ); Falcon::StmtCatchBlock *lst = new Falcon::StmtCatchBlock( LINE, $4 ); COMPILER->pushContextSet( &lst->children() ); t->addHandler( lst ); } | CATCH error EOL { COMPILER->raiseError( Falcon::e_syn_catch ); } ; catchcase_element_list: catchcase_element | catchcase_element_list COMMA catchcase_element; ; catchcase_element: INTNUM { Falcon::StmtTry *stmt = static_cast(COMPILER->getContext()); Falcon::Value *val = new Falcon::Value( $1 ); if ( ! stmt->addIntCase( val ) ) { COMPILER->raiseError(Falcon::e_catch_clash, "", CURRENT_LINE ); delete val; } } | SYMBOL { Falcon::StmtTry *stmt = static_cast(COMPILER->getContext()); Falcon::Symbol *sym = COMPILER->searchLocalSymbol( *$1 ); if( sym == 0 ) { sym = COMPILER->addGlobalSymbol( *$1 ); } Falcon::Value *val = new Falcon::Value( sym ); if ( ! stmt->addSymbolCase( val ) ) { COMPILER->raiseError(Falcon::e_catch_clash, "", CURRENT_LINE ); delete val; } } ; /********************************************************** RAISE statement ***********************************************************/ raise_statement: RAISE expression EOL { $$ = new Falcon::StmtRaise( LINE, $2 ); } | RAISE error EOL { COMPILER->raiseError(Falcon::e_syn_raise ); $$ = 0; } ; /********************************************************** Function declaration ***********************************************************/ func_statement: func_decl static_block statement_list END EOL { $$ = COMPILER->getContext(); COMPILER->closeFunction(); } | func_decl_short statement { COMPILER->addStatement( $2 ); $$ = COMPILER->getContext(); COMPILER->closeFunction(); } ; func_decl: func_begin OPENPAR param_list CLOSEPAR EOL | func_begin OPENPAR param_list error { COMPILER->tempLine( CURRENT_LINE ); } CLOSEPAR EOL { COMPILER->raiseContextError(Falcon::e_syn_funcdecl, COMPILER->tempLine(), CTX_LINE ); } | func_begin error EOL { COMPILER->raiseError(Falcon::e_syn_funcdecl ); } ; func_decl_short: func_begin OPENPAR param_list CLOSEPAR COLON | func_begin OPENPAR error { COMPILER->tempLine( CURRENT_LINE ); } CLOSEPAR COLON { COMPILER->raiseContextError(Falcon::e_syn_funcdecl, COMPILER->tempLine(), CTX_LINE ); } ; func_begin: FUNCDECL SYMBOL { Falcon::FuncDef *def = new Falcon::FuncDef( 0, 0 ); // the SYMBOL which names the function goes in the old symbol table, while the parameters // will go in the new symbol table. // if we are in a class, I have to create the symbol classname.functionname Falcon::Statement *parent = COMPILER->getContext(); Falcon::String func_name; if ( parent != 0 ) { switch( parent->type() ) { case Falcon::Statement::t_class: { Falcon::StmtClass *stmt_cls = static_cast< Falcon::StmtClass *>( parent ); func_name = stmt_cls->symbol()->name() + "." + *$2; } break; case Falcon::Statement::t_state: { Falcon::StmtState *stmt_state = static_cast< Falcon::StmtState *>( parent ); func_name = stmt_state->owner()->symbol()->name() + "." + * stmt_state->name() + "#" + *$2; } break; case Falcon::Statement::t_function: { Falcon::StmtFunction *stmt_func = static_cast< Falcon::StmtFunction *>( parent ); func_name = stmt_func->name() + "##" + *$2; } break; default: func_name = *$2; } } else func_name = *$2; // find the global symbol for this. Falcon::Symbol *sym = COMPILER->searchGlobalSymbol( func_name ); // Not defined? if( sym == 0 ) { sym = COMPILER->addGlobalSymbol( func_name ); } else if ( sym->isFunction() || sym->isClass() ) { COMPILER->raiseError(Falcon::e_already_def, sym->name() ); } // anyhow, also in case of error, destroys the previous information to allow a correct parsing // of the rest. sym->setFunction( def ); // and eventually add it as a class property if ( parent != 0 ) { if( parent->type() == Falcon::Statement::t_class ) { Falcon::StmtClass *stmt_cls = static_cast< Falcon::StmtClass *>( parent ); Falcon::ClassDef *cd = stmt_cls->symbol()->getClassDef(); if ( cd->hasProperty( *$2 ) ) { COMPILER->raiseError(Falcon::e_prop_adef, *$2 ); } else { cd->addProperty( $2, new Falcon::VarDef( sym ) ); // is this a setter/getter? if( ( $2->find( "__set_" ) == 0 || $2->find( "__get_" ) == 0 ) && $2->length() > 6 ) { Falcon::String *pname = COMPILER->addString( $2->subString( 6 )); Falcon::VarDef *pd = cd->getProperty( *pname ); if( pd == 0 ) { pd = new Falcon::VarDef; cd->addProperty( pname, pd ); pd->setReflective( Falcon::e_reflectSetGet, 0xFFFFFFFF ); } else if( ! pd->isReflective() ) { COMPILER->raiseError(Falcon::e_prop_adef, *pname ); } } } } else if ( parent->type() == Falcon::Statement::t_state ) { Falcon::StmtState *stmt_state = static_cast< Falcon::StmtState *>( parent ); if( ! stmt_state->addFunction( $2, sym ) ) { COMPILER->raiseError(Falcon::e_sm_adef, *$2 ); } else { stmt_state->state()->addFunction( *$2, sym ); } // eventually add a property where to store this thing Falcon::ClassDef *cd = stmt_state->owner()->symbol()->getClassDef(); if ( ! cd->hasProperty( *$2 ) ) cd->addProperty( $2, new Falcon::VarDef ); } } Falcon::StmtFunction *func = new Falcon::StmtFunction( COMPILER->lexer()->line(), sym ); // prepare the statement allocation context COMPILER->pushContext( func ); COMPILER->pushFunctionContext( func ); COMPILER->pushContextSet( &func->statements() ); COMPILER->pushFunction( def ); } ; param_list: /* nothing */ | param_symbol | param_list COMMA param_symbol ; param_symbol: SYMBOL { Falcon::Symbol *sym = COMPILER->searchLocalSymbol( *$1 ); if ( sym != 0 ) { COMPILER->raiseError(Falcon::e_already_def, sym->name() ); } else { Falcon::FuncDef *func = COMPILER->getFunction(); Falcon::Symbol *sym = new Falcon::Symbol( COMPILER->module(), *$1 ); COMPILER->module()->addSymbol( sym ); func->addParameter( sym ); } } ; static_block: /* nothing */ | static_decl { Falcon::StmtFunction *func = static_cast(COMPILER->getContext()); COMPILER->pushContextSet( &func->staticBlock() ); COMPILER->staticPrefix( &func->symbol()->name() ); } statement_list END EOL { COMPILER->popContextSet(); COMPILER->staticPrefix(0); } | static_short_decl { Falcon::StmtFunction *func = static_cast(COMPILER->getContext()); COMPILER->pushContextSet( &func->staticBlock() ); COMPILER->staticPrefix( &func->symbol()->name() ); } statement { COMPILER->addStatement( $3 ); COMPILER->popContextSet(); COMPILER->staticPrefix(0); } ; static_decl: STATIC EOL | STATIC error EOL { COMPILER->raiseError(Falcon::e_syn_static ); } ; static_short_decl: STATIC COLON | STATIC error COLON { COMPILER->raiseError(Falcon::e_syn_static, "", CURRENT_LINE ); } ; /********************************************************** Launch Statement ***********************************************************/ launch_statement: LAUNCH func_call EOL { $$ = new Falcon::StmtLaunch( LINE, $2 ); } | LAUNCH error EOL { COMPILER->raiseError(Falcon::e_syn_launch ); $$ = 0; } ; /********************************************************** Const Statement ***********************************************************/ const_statement: CONST_KW SYMBOL OP_EQ const_atom EOL { // TODO: evalute const expressions on the fly. Falcon::Value *val = $4; //COMPILER->exprSimplify( $4 ); // will raise an error in case the expression is not atomic. COMPILER->addConstant( *$2, val, LINE ); // we don't need the expression anymore // no other action: $$ = 0; } | CONST_KW SYMBOL OP_EQ error EOL { COMPILER->raiseError(Falcon::e_inv_const_val ); $$ = 0; } | CONST_KW error EOL { COMPILER->raiseError(Falcon::e_syn_const ); $$ = 0; } ; /********************************************************** Export directive ***********************************************************/ export_statement: EXPORT EOL { if ( COMPILER->sourceTree()->isExportAll() ) COMPILER->raiseError(Falcon::e_export_all ); else COMPILER->sourceTree()->setExportAll(); // no effect $$=0; } | EXPORT export_symbol_list EOL { if ( COMPILER->sourceTree()->isExportAll() ) COMPILER->raiseError(Falcon::e_export_all ); // no effect $$ = 0; } | EXPORT error EOL { COMPILER->raiseError(Falcon::e_syn_export ); $$ = 0; } ; export_symbol_list: SYMBOL { Falcon::Symbol *sym = COMPILER->addGlobalSymbol( *$1 ); sym->exported(true); } | export_symbol_list COMMA SYMBOL { Falcon::Symbol *sym = COMPILER->addGlobalSymbol( *$3 ); sym->exported(true); } ; import_statement: IMPORT import_symbol_list EOL { COMPILER->importSymbols( $2 ); $$ = 0; } | IMPORT import_symbol_list FROM SYMBOL EOL { COMPILER->importSymbols( $2, *$4, "", false ); $$ = 0; } | IMPORT import_symbol_list FROM STRING EOL { COMPILER->importSymbols( $2, *$4, "", true ); $$ = 0; } | IMPORT import_symbol_list FROM SYMBOL OP_AS SYMBOL EOL { // destroy the list to avoid leak Falcon::ListElement *li = $2->begin(); int counter = 0; while( li != 0 ) { Falcon::String *symName = (Falcon::String *) li->data(); if ( counter == 0 ) COMPILER->importAlias( *symName, *$4, *$6, false ); delete symName; li = li->next(); counter++; } delete $2; if ( counter != 1 ) COMPILER->raiseError(Falcon::e_syn_import ); $$ = 0; } | IMPORT import_symbol_list FROM STRING OP_AS SYMBOL EOL { // destroy the list to avoid leak Falcon::ListElement *li = $2->begin(); int counter = 0; while( li != 0 ) { Falcon::String *symName = (Falcon::String *) li->data(); if ( counter == 0 ) COMPILER->importAlias( *symName, *$4, *$6, true ); delete symName; li = li->next(); counter++; } delete $2; if ( counter != 1 ) COMPILER->raiseError(Falcon::e_syn_import ); $$ = 0; } | IMPORT import_symbol_list FROM SYMBOL OP_IN SYMBOL EOL { COMPILER->importSymbols( $2, *$4, *$6, false ); $$ = 0; } | IMPORT import_symbol_list FROM STRING OP_IN SYMBOL EOL { COMPILER->importSymbols( $2, *$4, *$6, true ); $$ = 0; } | IMPORT SYMBOL error EOL { COMPILER->raiseError(Falcon::e_syn_import ); $$ = 0; } | IMPORT import_symbol_list error EOL { // destroy the list to avoid leak Falcon::ListElement *li = $2->begin(); while( li != 0 ) { Falcon::String *symName = (Falcon::String *) li->data(); delete symName; li = li->next(); } delete $2; COMPILER->raiseError(Falcon::e_syn_import ); $$ = 0; } | IMPORT FROM SYMBOL EOL { COMPILER->addNamespace( *$3, "", true, false ); $$ = 0; } | IMPORT FROM STRING EOL { COMPILER->addNamespace( *$3, "", true, true ); $$ = 0; } | IMPORT FROM SYMBOL OP_IN SYMBOL EOL { COMPILER->addNamespace( *$3, *$5, true, false ); $$ = 0; } | IMPORT FROM STRING OP_IN SYMBOL EOL { COMPILER->addNamespace( *$3, *$5, true, true ); $$ = 0; } | IMPORT error EOL { COMPILER->raiseError(Falcon::e_syn_import ); $$ = 0; } ; attribute_statement: SYMBOL COLON const_atom EOL { COMPILER->addAttribute( *$1, $3, LINE ); } | SYMBOL COLON error EOL { COMPILER->raiseError(Falcon::e_syn_attrdecl ); } ; import_symbol_list: SYMBOL { Falcon::List *lst = new Falcon::List; lst->pushBack( new Falcon::String( *$1 ) ); $$ = lst; } | import_symbol_list COMMA SYMBOL { $1->pushBack( new Falcon::String( *$3 ) ); $$ = $1; } ; /********************************************************** Directive directive (no, it's not an error) ***********************************************************/ directive_statement: DIRECTIVE directive_pair_list EOL { // no effect $$=0; } | DIRECTIVE error EOL { COMPILER->raiseError(Falcon::e_syn_directive ); $$=0; } ; directive_pair_list: directive_pair | directive_pair_list COMMA directive_pair ; directive_pair: SYMBOL OP_EQ SYMBOL { COMPILER->setDirective( *$1, *$3 ); } | SYMBOL OP_EQ STRING { COMPILER->setDirective( *$1, *$3 ); } | SYMBOL OP_EQ INTNUM_WITH_MINUS { COMPILER->setDirective( *$1, $3 ); } ; /********************************************************** Class Declaration ***********************************************************/ class_decl: CLASS SYMBOL { Falcon::ClassDef *def = new Falcon::ClassDef; // the SYMBOL which names the function goes in the old symbol table, while the parameters // will go in the new symbol table. // find the global symbol for this. Falcon::Symbol *sym = COMPILER->searchGlobalSymbol( *$2 ); // Not defined? if( sym == 0 ) { sym = COMPILER->addGlobalSymbol( *$2 ); } else if ( sym->isFunction() || sym->isClass() ) { COMPILER->raiseError(Falcon::e_already_def, sym->name() ); } // anyhow, also in case of error, destroys the previous information to allow a correct parsing // of the rest. sym->setClass( def ); Falcon::StmtClass *cls = new Falcon::StmtClass( COMPILER->lexer()->line(), sym ); // prepare the statement allocation context COMPILER->pushContext( cls ); // We don't have a context set here COMPILER->pushFunction( def ); } /* param_list convert the above classdef in a funcdef. */ class_def_inner class_statement_list END EOL { $$ = COMPILER->getContext(); // check for expressions in from clauses COMPILER->checkLocalUndefined(); Falcon::StmtClass *cls = static_cast($$); // if the class has no constructor, create one in case of inheritance. if( cls != 0 ) { if ( cls->ctorFunction() == 0 ) { Falcon::ClassDef *cd = cls->symbol()->getClassDef(); if ( ! cd->inheritance().empty() ) { COMPILER->buildCtorFor( cls ); // COMPILER->addStatement( func ); should be done in buildCtorFor // cls->ctorFunction( func ); idem } } COMPILER->popContext(); //We didn't pushed a context set COMPILER->popFunction(); } } ; class_def_inner: class_param_list from_clause EOL | error EOL { COMPILER->raiseError(Falcon::e_syn_class ); } ; class_param_list: /* nothing */ | OPENPAR param_list CLOSEPAR | OPENPAR param_list error { COMPILER->tempLine( CURRENT_LINE ); } CLOSEPAR { COMPILER->raiseContextError(Falcon::e_syn_class, COMPILER->tempLine(), CTX_LINE ); } ; from_clause: /* nothing */ | FROM inherit_list ; inherit_list: inherit_token | inherit_list COMMA inherit_token ; inherit_token: SYMBOL inherit_call { Falcon::StmtClass *cls = static_cast( COMPILER->getContext() ); // creates or find the symbol. Falcon::Symbol *sym = COMPILER->addGlobalSymbol(*$1); Falcon::ClassDef *clsdef = cls->symbol()->getClassDef(); Falcon::InheritDef *idef = new Falcon::InheritDef(sym); if ( clsdef->addInheritance( idef ) ) { cls->addInitExpression( new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_inherit, new Falcon::Value( sym ), $2 ) ) ); } else { COMPILER->raiseError(Falcon::e_prop_adef ); delete idef; delete $2; } } ; inherit_call: /* nothing */ { $$ = 0; } | OPENPAR CLOSEPAR { $$=0; } | OPENPAR expression_list CLOSEPAR { $$ = $2 == 0 ? 0 : new Falcon::Value( $2 ); } ; class_statement_list: /* nothing */ | class_statement_list class_statement ; class_statement: EOL | func_statement { COMPILER->addFunction( $1 ); } | property_decl { Falcon::StmtClass *cls = static_cast( COMPILER->getContext() ); if ( cls->initGiven() ) { COMPILER->raiseError(Falcon::e_prop_pinit ); } // have we got a complex property statement? if ( $1 != 0 ) { // as we didn't push the class context set, we have to do it by ourselves // see if the class has already a constructor. Falcon::StmtFunction *ctor_stmt = cls->ctorFunction(); if ( ctor_stmt == 0 ) { ctor_stmt = COMPILER->buildCtorFor( cls ); } ctor_stmt->statements().push_back( $1 ); // this goes directly in the auto constructor. } } | state_decl { Falcon::StmtState* ss = static_cast($1); Falcon::StmtClass *cls = static_cast( COMPILER->getContext() ); if( ! cls->addState( static_cast($1) ) ) { const Falcon::String* name = ss->name(); // we're not using the state we created, afater all. delete ss->state(); delete $1; COMPILER->raiseError( Falcon::e_state_adef, *name ); } else { // ownership passes on to the classdef cls->symbol()->getClassDef()->addState( ss->name(), ss->state() ); cls->symbol()->getClassDef()->addProperty( ss->name(), new Falcon::VarDef( ss->name() ) ); } } | init_decl | attribute_statement ; init_decl: INIT EOL { Falcon::StmtClass *cls = static_cast( COMPILER->getContext() ); if( cls->initGiven() ) { COMPILER->raiseError(Falcon::e_init_given ); } else { cls->initGiven( true ); Falcon::StmtFunction *func = cls->ctorFunction(); if ( func == 0 ) { func = COMPILER->buildCtorFor( cls ); } // prepare the statement allocation context COMPILER->pushContext( func ); COMPILER->pushFunctionContext( func ); COMPILER->pushContextSet( &func->statements() ); COMPILER->pushFunction( func->symbol()->getFuncDef() ); } } static_block statement_list END EOL { COMPILER->popContext(); COMPILER->popContextSet(); COMPILER->popFunction(); COMPILER->popFunctionContext(); } ; property_decl: STATIC SYMBOL OP_EQ const_atom EOL { COMPILER->checkLocalUndefined(); Falcon::StmtClass *cls = static_cast( COMPILER->getContext() ); Falcon::ClassDef *clsdef = cls->symbol()->getClassDef(); Falcon::VarDef *def = $4->genVarDef(); if ( def != 0 ) { Falcon::String prop_name = cls->symbol()->name() + "." + *$2; Falcon::Symbol *sym = COMPILER->addGlobalVar( prop_name, def ); if( clsdef->hasProperty( *$2 ) ) COMPILER->raiseError(Falcon::e_prop_adef, *$2 ); else clsdef->addProperty( $2, new Falcon::VarDef( Falcon::VarDef::t_reference, sym) ); } else { COMPILER->raiseError(Falcon::e_static_const ); } delete $4; // the expression is not needed anymore $$ = 0; // we don't add any statement } | SYMBOL OP_EQ expression EOL { COMPILER->checkLocalUndefined(); Falcon::StmtClass *cls = static_cast( COMPILER->getContext() ); Falcon::ClassDef *clsdef = cls->symbol()->getClassDef(); Falcon::VarDef *def = $3->genVarDef(); if ( def != 0 ) { if( clsdef->hasProperty( *$1 ) ) COMPILER->raiseError(Falcon::e_prop_adef, *$1 ); else clsdef->addProperty( $1, def ); delete $3; // the expression is not needed anymore $$ = 0; // we don't add any statement } else { // create anyhow a nil property if( clsdef->hasProperty( *$1 ) ) COMPILER->raiseError(Falcon::e_prop_adef, *$1 ); else clsdef->addProperty( $1, new Falcon::VarDef() ); // but also prepare a statement to be executed by the auto-constructor. $$ = new Falcon::StmtVarDef( LINE, $1, $3 ); } } ; state_decl: state_heading state_statement_list END EOL { $$ = COMPILER->getContext(); COMPILER->popContext(); } ; state_heading: OPENSQUARE SYMBOL CLOSESQUARE EOL { Falcon::StmtClass* cls = static_cast( COMPILER->getContext() ); COMPILER->pushContext( new Falcon::StmtState( $2, cls ) ); } | OPENSQUARE INIT CLOSESQUARE EOL { Falcon::StmtClass* cls = static_cast( COMPILER->getContext() ); Falcon::StmtState* state = new Falcon::StmtState( COMPILER->addString( "init" ), cls ); cls->initState( state ); COMPILER->pushContext( state ); } ; state_statement_list: /* nothing */ | state_statement_list state_statement ; state_statement: EOL | func_statement { COMPILER->addFunction( $1 ); } ; /***************************************************** ENUM declaration ******************************************************/ enum_statement: ENUM SYMBOL { Falcon::ClassDef *def = new Falcon::ClassDef; // the SYMBOL which names the function goes in the old symbol table, while the parameters // will go in the new symbol table. // find the global symbol for this. Falcon::Symbol *sym = COMPILER->searchGlobalSymbol( *$2 ); // Not defined? if( sym == 0 ) { sym = COMPILER->addGlobalSymbol( *$2 ); sym->setEnum( true ); } else if ( sym->isFunction() || sym->isClass() ) { COMPILER->raiseError(Falcon::e_already_def, sym->name() ); } // anyhow, also in case of error, destroys the previous information to allow a correct parsing // of the rest. sym->setClass( def ); Falcon::StmtClass *cls = new Falcon::StmtClass( COMPILER->lexer()->line(), sym ); // prepare the statement allocation context COMPILER->pushContext( cls ); // We don't have a context set here COMPILER->pushFunction( def ); COMPILER->resetEnum(); } EOL enum_statement_list END EOL { $$ = COMPILER->getContext(); COMPILER->popContext(); //We didn't pushed a context set COMPILER->popFunction(); } ; enum_statement_list: /* nothing */ | enum_statement_list enum_item_decl ; enum_item_decl: EOL | SYMBOL OP_EQ const_atom enum_item_terminator { COMPILER->addEnumerator( *$1, $3 ); } | attribute_statement | SYMBOL enum_item_terminator { COMPILER->addEnumerator( *$1 ); } ; enum_item_terminator: EOL | COMMA ; /***************************************************** Object declaration ******************************************************/ object_decl: OBJECT SYMBOL { Falcon::ClassDef *def = new Falcon::ClassDef; // the SYMBOL which names the function goes in the old symbol table, while the parameters // will go in the new symbol table. // we create a special symbol for the class. Falcon::String cl_name = "%"; cl_name += *$2; Falcon::Symbol *clsym = COMPILER->addGlobalSymbol( cl_name ); clsym->setClass( def ); // find the global symbol for this. Falcon::Symbol *sym = COMPILER->searchGlobalSymbol( *$2 ); // Not defined? if( sym == 0 ) { sym = COMPILER->addGlobalSymbol( *$2 ); } else if ( sym->isFunction() || sym->isClass() ) { COMPILER->raiseError(Falcon::e_already_def, sym->name() ); } // anyhow, also in case of error, destroys the previous information to allow a correct parsing // of the rest. sym->setInstance( clsym ); Falcon::StmtClass *cls = new Falcon::StmtClass( COMPILER->lexer()->line(), clsym ); cls->singleton( sym ); // prepare the statement allocation context COMPILER->pushContext( cls ); //Statements here goes in the auto constructor. //COMPILER->pushContextSet( &cls->autoCtor() ); COMPILER->pushFunction( def ); } object_decl_inner object_statement_list END EOL { $$ = COMPILER->getContext(); Falcon::StmtClass *cls = static_cast($$); // check for expressions in from clauses COMPILER->checkLocalUndefined(); // if the class has no constructor, create one in case of inheritance. if( cls->ctorFunction() == 0 ) { Falcon::ClassDef *cd = cls->symbol()->getClassDef(); if ( !cd->inheritance().empty() ) { COMPILER->buildCtorFor( cls ); // COMPILER->addStatement( func ); should be done in buildCtorFor // cls->ctorFunction( func ); idem } } COMPILER->popContext(); //COMPILER->popContextSet(); COMPILER->popFunction(); } ; object_decl_inner: from_clause EOL | error EOL { COMPILER->raiseError(Falcon::e_syn_object ); } ; object_statement_list: /* nothing */ | object_statement_list object_statement ; object_statement: EOL | func_statement { COMPILER->addFunction( $1 ); } | property_decl { Falcon::StmtClass *cls = static_cast( COMPILER->getContext() ); if ( cls->initGiven() ) { COMPILER->raiseError(Falcon::e_prop_pinit ); } COMPILER->checkLocalUndefined(); // have we got a complex property statement? if ( $1 != 0 ) { // as we didn't push the class context set, we have to do it by ourselves // see if the class has already a constructor. Falcon::StmtFunction *ctor_stmt = cls->ctorFunction(); if ( ctor_stmt == 0 ) { ctor_stmt = COMPILER->buildCtorFor( cls ); } ctor_stmt->statements().push_back( $1 ); // this goes directly in the auto constructor. } } | init_decl | attribute_statement ; /***************************************************** global statement ******************************************************/ global_statement: GLOBAL { Falcon::StmtGlobal *glob = new Falcon::StmtGlobal( CURRENT_LINE ); COMPILER->pushContext( glob ); } global_symbol_list EOL { // raise an error if we are not in a local context if ( ! COMPILER->isLocalContext() ) { COMPILER->raiseError(Falcon::e_global_notin_func, "", LINE ); } $$ = COMPILER->getContext(); COMPILER->popContext(); } ; global_symbol_list: globalized_symbol | error { COMPILER->raiseError( Falcon::e_syn_global ); } | globalized_symbol error { COMPILER->raiseError( Falcon::e_syn_global ); } | global_symbol_list COMMA globalized_symbol | global_symbol_list COMMA error { COMPILER->raiseError( Falcon::e_syn_global ); } ; globalized_symbol: SYMBOL { // we create (or retrieve) a globalized symbol Falcon::Symbol *sym = COMPILER->globalize( *$1 ); // then we add the symbol to the global statement (it's just for symbolic asm generation). Falcon::StmtGlobal *glob = static_cast( COMPILER->getContext() ); glob->addSymbol( sym ); } ; /***************************************************** return statement ******************************************************/ return_statement: RETURN EOL { $$ = new Falcon::StmtReturn(LINE, 0); } | RETURN expression EOL { $$ = new Falcon::StmtReturn( LINE, $2 ); } | RETURN error EOL { COMPILER->raiseError(Falcon::e_syn_return ); $$ = 0; } ; /***************************************************** Grammar tokens ******************************************************/ const_atom_non_minus: NIL { $$ = new Falcon::Value(); } | UNB { $$ = new Falcon::Value(); $$->setUnbound(); } | TRUE_TOKEN { $$ = new Falcon::Value( true ); } | FALSE_TOKEN { $$ = new Falcon::Value( false ); } | INTNUM { $$ = new Falcon::Value( $1 ); } | DBLNUM { $$ = new Falcon::Value( $1 ); } | STRING { $$ = new Falcon::Value( $1 ); } ; const_atom: NIL { $$ = new Falcon::Value(); } | UNB { $$ = new Falcon::Value(); $$->setUnbound(); } | TRUE_TOKEN { $$ = new Falcon::Value( true ); } | FALSE_TOKEN { $$ = new Falcon::Value( false ); } | INTNUM_WITH_MINUS { $$ = new Falcon::Value( $1 ); } | DBLNUM { $$ = new Falcon::Value( $1 ); } | STRING { $$ = new Falcon::Value( $1 ); } ; atomic_symbol: SYMBOL { Falcon::Value *val; Falcon::Symbol *sym = COMPILER->searchLocalSymbol( *$1 ); if( sym == 0 ) { val = new Falcon::Value(); val->setSymdef( $1 ); // warning: the symbol is still undefined. COMPILER->addSymdef( val ); } else { val = new Falcon::Value( sym ); } $$ = val; } ; var_atom: atomic_symbol | SELF { $$ = new Falcon::Value(); $$->setSelf(); } | FSELF { $$ = new Falcon::Value(); $$->setFself(); } ; /* Currently not needed atom: const_atom | var_atom ; */ OPT_EOL: /* nothing */ |EOL ; expression: const_atom_non_minus | var_atom | AMPER SYMBOL { $$ = new Falcon::Value(); $$->setLBind( $2 ); /* do not add the symbol to the compiler */ } | AMPER INTNUM { char space[32]; sprintf(space, "%d", (int)$2 ); $$ = new Falcon::Value(); $$->setLBind( COMPILER->addString(space) ); } | AMPER SELF { $$ = new Falcon::Value(); $$->setLBind( COMPILER->addString("self") ); /* do not add the symbol to the compiler */ } | AMPER DOT SYMBOL { $$ = new Falcon::Value(); $3->prepend( "." ); $$->setLBind( $3 ); /* do not add the symbol to the compiler */ } | AMPER DOT INTNUM { char space[32]; sprintf(space, ".%d", (int)$3 ); $$ = new Falcon::Value(); $$->setLBind( COMPILER->addString(space) ); } | AMPER DOT SELF { $$ = new Falcon::Value(); $$->setLBind( COMPILER->addString(".self") ); /* do not add the symbol to the compiler */ } | MINUS expression %prec NEG { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_neg, $2 ) ); } | SYMBOL VBAR expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_fbind, new Falcon::Value($1), $3) ); } | expression PLUS OPT_EOL expression { // is this an immediate string sum ? if ( $1->isString() ) { if ( $4->isString() ) { Falcon::String str( *$1->asString() ); str += *$4->asString(); $1->setString( COMPILER->addString( str ) ); delete $4; $$ = $1; } else if ( $4->isInteger() ) { Falcon::String str( *$1->asString() ); str.writeNumber( $4->asInteger() ); $1->setString( COMPILER->addString( str ) ); delete $4; $$ = $1; } else $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_plus, $1, $4 ) ); } else $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_plus, $1, $4 ) ); } | expression MINUS OPT_EOL expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_minus, $1, $4 ) ); } | expression STAR OPT_EOL expression { if ( $1->isString() ) { if ( $4->isInteger() ) { Falcon::String str( $1->asString()->length() * $4->asInteger() ); for( int i = 0; i < $4->asInteger(); ++i ) { str.append( *$1->asString() ); } $1->setString( COMPILER->addString( str ) ); delete $4; $$ = $1; } else $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_times, $1, $4 ) ); } else $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_times, $1, $4 ) ); } | expression SLASH OPT_EOL expression { if ( $1->isString() ) { if( $1->asString()->length() == 0 ) { COMPILER->raiseError( Falcon::e_invop ); } else { if ( $4->isInteger() ) { Falcon::String str( *$1->asString() ); str.setCharAt( str.length()-1, str.getCharAt(str.length()-1) + $4->asInteger() ); $1->setString( COMPILER->addString( str ) ); delete $4; $$ = $1; } else $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_divide, $1, $4 ) ); } } else $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_divide, $1, $4 ) ); } | expression PERCENT OPT_EOL expression { if ( $1->isString() ) { if ( $4->isInteger() ) { Falcon::String str( *$1->asString() ); str.append( (Falcon::uint32) $4->asInteger() ); $1->setString( COMPILER->addString( str ) ); delete $4; $$ = $1; } else $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_modulo, $1, $4 ) ); } else $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_modulo, $1, $4 ) ); } | expression POW OPT_EOL expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_power, $1, $4 ) ); } | expression AMPER_AMPER OPT_EOL expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_bin_and, $1, $4 ) ); } | expression VBAR_VBAR OPT_EOL expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_bin_or, $1, $4 ) ); } | expression CAP_CAP OPT_EOL expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_bin_xor, $1, $4 ) ); } | expression SHL OPT_EOL expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_shift_left, $1, $4 ) ); } | expression SHR OPT_EOL expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_shift_right, $1, $4 ) ); } | TILDE expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_bin_not, $2 ) ); } | expression NEQ expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_neq, $1, $3 ) ); } | expression INCREMENT { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_post_inc, $1 ) ); } | INCREMENT expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_pre_inc, $2 ) ); } | expression DECREMENT { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_post_dec, $1 ) ); } | DECREMENT expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_pre_dec, $2 ) ); } | expression EEQ expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_eq, $1, $3 ) ); } | expression OP_EXEQ expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_exeq, $1, $3 ) ); } | expression GT expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_gt, $1, $3 ) ); } | expression LT expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_lt, $1, $3 ) ); } | expression GE expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_ge, $1, $3 ) ); } | expression LE expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_le, $1, $3 ) ); } | expression AND expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_and, $1, $3 ) ); } | expression OR expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_or, $1, $3 ) ); } | NOT expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_not, $2 ) ); } | expression OP_IN expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_in, $1, $3 ) ); } | expression OP_NOTIN expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_notin, $1, $3 ) ); } | expression PROVIDES SYMBOL { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_provides, $1, new Falcon::Value( $3 ) ) ); } | DOLLAR atomic_symbol { $$ = new Falcon::Value( $2 ); } | DOLLAR INTNUM { $$ = new Falcon::Value( (Falcon::Value *) 0 ); } | ATSIGN expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_strexpand, $2 ) ); } | DIESIS expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_indirect, $2 ) ); } | CAP_EVAL expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_eval, $2 ) ); } | CAP_OOB expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_oob, $2 ) ); } | CAP_DEOOB expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_deoob, $2 ) ); } | CAP_ISOOB expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_isoob, $2 ) ); } | CAP_XOROOB expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_xoroob, $2 ) ); } | nameless_func | nameless_block | innerfunc | func_call | iif_expr | dotarray_decl | expression range_decl { Falcon::Expression *exp = new Falcon::Expression( Falcon::Expression::t_array_access, $1, $2 ); $$ = new Falcon::Value( exp ); } | array_decl { $$ = new Falcon::Value( $1 ); } | expression OPENSQUARE expression CLOSESQUARE { Falcon::Expression *exp = new Falcon::Expression( Falcon::Expression::t_array_access, $1, $3 ); $$ = new Falcon::Value( exp ); } | expression OPENSQUARE STAR expression CLOSESQUARE { Falcon::Expression *exp = new Falcon::Expression( Falcon::Expression::t_array_byte_access, $1, $4 ); $$ = new Falcon::Value( exp ); } | expression DOT SYMBOL { Falcon::Expression *exp = new Falcon::Expression( Falcon::Expression::t_obj_access, $1, new Falcon::Value( $3 ) ); if ( $3->getCharAt(0) == '_' && ! $1->isSelf() ) { COMPILER->raiseError(Falcon::e_priv_access, COMPILER->tempLine() ); } $$ = new Falcon::Value( exp ); } | dict_decl /*suqared expr*/ | range_decl /*suqared expr*/ | expression OP_EQ expression { COMPILER->defineVal( $1 ); $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_assign, $1, $3 ) ); } | expression OP_EQ expression COMMA expression_list { COMPILER->defineVal( $1 ); $5->pushFront( $3 ); Falcon::Value *second = new Falcon::Value( $5 ); $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_assign, $1, second ) ); } | expression ASSIGN_ADD expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_aadd, $1, $3 ) ); } | expression ASSIGN_SUB expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_asub, $1, $3 ) ); } | expression ASSIGN_MUL expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_amul, $1, $3 ) ); } | expression ASSIGN_DIV expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_adiv, $1, $3 ) ); } | expression ASSIGN_MOD expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_amod, $1, $3 ) ); } | expression ASSIGN_POW expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_apow, $1, $3 ) ); } | expression ASSIGN_BAND expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_aband, $1, $3 ) ); } | expression ASSIGN_BOR expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_abor, $1, $3 ) ); } | expression ASSIGN_BXOR expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_abxor, $1, $3 ) ); } | expression ASSIGN_SHL expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_ashl, $1, $3 ) ); } | expression ASSIGN_SHR expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_ashr, $1, $3 ) ); } | OPENPAR expression CLOSEPAR {$$=$2;} ; /*suqared expr NEED to start with an or with a nonambiguous symbol */ range_decl: OPENSQUARE COLON CLOSESQUARE { $$ = new Falcon::Value( new Falcon::RangeDecl( new Falcon::Value( (Falcon::int64) 0 ) ) ); } | OPENSQUARE expression COLON CLOSESQUARE { $$ = new Falcon::Value( new Falcon::RangeDecl( $2 ) ); } | OPENSQUARE COLON expression CLOSESQUARE { $$ = new Falcon::Value( new Falcon::RangeDecl( new Falcon::Value( (Falcon::int64) 0 ), $3 ) ); } | OPENSQUARE expression COLON expression CLOSESQUARE { $$ = new Falcon::Value( new Falcon::RangeDecl( $2, $4 ) ); } | OPENSQUARE expression COLON expression COLON expression CLOSESQUARE { $$ = new Falcon::Value( new Falcon::RangeDecl( $2, $4, $6 ) ); } ; func_call: expression OPENPAR expression_list CLOSEPAR { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_funcall, $1, new Falcon::Value( $3 ) ) ); } | expression OPENPAR CLOSEPAR { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_funcall, $1, 0 ) ); } | expression OPENPAR expression_list error { COMPILER->tempLine( CURRENT_LINE ); } CLOSEPAR { delete $3; COMPILER->raiseContextError(Falcon::e_syn_funcall, COMPILER->tempLine(), CTX_LINE ); $$ = new Falcon::Value; } ; nameless_func: FUNCDECL { Falcon::FuncDef *def = new Falcon::FuncDef( 0, 0 ); // set the def as a lambda. COMPILER->incLambdaCount(); COMPILER->incClosureContext(); int id = COMPILER->lambdaCount(); // find the global symbol for this. char buf[48]; sprintf( buf, "_lambda#_id_%d", id ); Falcon::String name( buf, -1 ); // Not defined? fassert( COMPILER->searchGlobalSymbol( name ) == 0 ); Falcon::Symbol *sym = COMPILER->addGlobalSymbol( name ); // anyhow, also in case of error, destroys the previous information to allow a correct parsing // of the rest. sym->setFunction( def ); Falcon::StmtFunction *func = new Falcon::StmtFunction( COMPILER->lexer()->line(), sym ); COMPILER->addFunction( func ); func->setLambda( id ); // prepare the statement allocation context COMPILER->pushContext( func ); COMPILER->pushFunctionContext( func ); COMPILER->pushContextSet( &func->statements() ); COMPILER->pushFunction( def ); COMPILER->lexer()->pushContext( Falcon::SrcLexer::ct_inner, COMPILER->lexer()->line() ); } nameless_func_decl_inner static_block statement_list END { COMPILER->lexer()->popContext(); $$ = COMPILER->closeClosure(); } ; nameless_block: OPEN_GRAPH { Falcon::FuncDef *def = new Falcon::FuncDef( 0, 0 ); // set the def as a lambda. COMPILER->incLambdaCount(); COMPILER->incClosureContext(); int id = COMPILER->lambdaCount(); // find the global symbol for this. char buf[48]; sprintf( buf, "_lambda#_id_%d", id ); Falcon::String name( buf, -1 ); Falcon::Symbol *sym = COMPILER->searchGlobalSymbol( name ); // Not defined? fassert( sym == 0 ); sym = COMPILER->addGlobalSymbol( name ); // anyhow, also in case of error, destroys the previous information to allow a correct parsing // of the rest. sym->setFunction( def ); Falcon::StmtFunction *func = new Falcon::StmtFunction( COMPILER->lexer()->line(), sym ); COMPILER->addFunction( func ); func->setLambda( id ); // prepare the statement allocation context COMPILER->pushContext( func ); COMPILER->pushFunctionContext( func ); COMPILER->pushContextSet( &func->statements() ); COMPILER->pushFunction( def ); } nameless_block_decl_inner static_block statement_list CLOSE_GRAPH { Falcon::StatementList *stmt = COMPILER->getContextSet(); if( stmt->size() == 1 && stmt->back()->type() == Falcon::Statement::t_autoexp ) { // wrap it around a return, so A is not nilled. Falcon::StmtAutoexpr *ae = static_cast( stmt->pop_back() ); stmt->push_back( new Falcon::StmtReturn( 1, ae->value()->clone() ) ); // we don't need the expression anymore. delete ae; } $$ = COMPILER->closeClosure(); } ; nameless_func_decl_inner: OPENPAR param_list CLOSEPAR EOL | OPENPAR param_list error { COMPILER->raiseContextError(Falcon::e_syn_funcdecl, LINE, CTX_LINE ); } | error EOL { COMPILER->raiseError(Falcon::e_syn_funcdecl ); } ; nameless_block_decl_inner: param_list ARROW | param_list error { COMPILER->raiseContextError(Falcon::e_syn_funcdecl, LINE, CTX_LINE ); } | error ARROW { COMPILER->raiseError(Falcon::e_syn_funcdecl ); } ; innerfunc: INNERFUNC { Falcon::FuncDef *def = new Falcon::FuncDef( 0, 0 ); // set the def as a lambda. COMPILER->incLambdaCount(); int id = COMPILER->lambdaCount(); // find the global symbol for this. char buf[48]; sprintf( buf, "_lambda#_id_%d", id ); Falcon::String name( buf, -1 ); // Not defined? fassert( COMPILER->searchGlobalSymbol( name ) == 0 ); Falcon::Symbol *sym = COMPILER->addGlobalSymbol( name ); // anyhow, also in case of error, destroys the previous information to allow a correct parsing // of the rest. sym->setFunction( def ); Falcon::StmtFunction *func = new Falcon::StmtFunction( COMPILER->lexer()->line(), sym ); COMPILER->addFunction( func ); func->setLambda( id ); // prepare the statement allocation context COMPILER->pushContext( func ); COMPILER->pushFunctionContext( func ); COMPILER->pushContextSet( &func->statements() ); COMPILER->pushFunction( def ); COMPILER->lexer()->pushContext( Falcon::SrcLexer::ct_inner, COMPILER->lexer()->line() ); } nameless_func_decl_inner static_block statement_list END { COMPILER->lexer()->popContext(); Falcon::StmtFunction *func = static_cast(COMPILER->getContext()); $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_lambda , new Falcon::Value( func->symbol() ) ) ); // analyze func in previous context. COMPILER->closeFunction(); } ; iif_expr: expression QUESTION expression COLON expression { $$ = new Falcon::Value( new Falcon::Expression( Falcon::Expression::t_iif, $1, $3, $5 ) ); } | expression QUESTION expression COLON error { delete $1; delete $3; COMPILER->raiseError(Falcon::e_syn_iif, CURRENT_LINE ); $$ = new Falcon::Value; } | expression QUESTION expression error { delete $1; delete $3; COMPILER->raiseError(Falcon::e_syn_iif, CURRENT_LINE ); $$ = new Falcon::Value; } | expression QUESTION error { delete $1; COMPILER->raiseError(Falcon::e_syn_iif, CURRENT_LINE ); $$ = new Falcon::Value; } ; array_decl: OPENSQUARE CLOSESQUARE { $$ = new Falcon::ArrayDecl(); } | OPENSQUARE expression_list CLOSESQUARE { $$ = $2; } | OPENSQUARE expression_list error { COMPILER->raiseContextError( Falcon::e_syn_arraydecl, CURRENT_LINE, CTX_LINE ); $$ = $2; } ; dotarray_decl: LISTPAR CLOSESQUARE { $$ = new Falcon::Value( new Falcon::ArrayDecl() ); } | LISTPAR listpar_expression_list CLOSESQUARE { $$ = new Falcon::Value( $2 ); } | LISTPAR listpar_expression_list error { COMPILER->raiseContextError( Falcon::e_syn_arraydecl, CURRENT_LINE, CTX_LINE ); $$ = new Falcon::Value( $2 ); } ; dict_decl: OPENSQUARE ARROW CLOSESQUARE { $$ = new Falcon::Value( new Falcon::DictDecl() ); } | OPENSQUARE expression_pair_list CLOSESQUARE { $$ = new Falcon::Value( $2 ); } | OPENSQUARE expression_pair_list error CLOSESQUARE { COMPILER->raiseContextError( Falcon::e_syn_dictdecl, LINE, CTX_LINE ); $$ = new Falcon::Value( $2 ); } ; expression_list: expression { $$ = new Falcon::ArrayDecl(); $$->pushBack( $1 ); } | expression_list COMMA expression { $1->pushBack( $3 ); $$ = $1; } ; listpar_expression_list: expression { $$ = new Falcon::ArrayDecl(); $$->pushBack( $1 ); } | listpar_expression_list listpar_comma expression { $1->pushBack( $3 ); $$ = $1; } ; listpar_comma: /*nothing */ | COMMA; symbol_list: atomic_symbol { COMPILER->defineVal( $1 ); Falcon::ArrayDecl *ad = new Falcon::ArrayDecl(); ad->pushBack( $1 ); $$ = ad; } | symbol_list COMMA atomic_symbol { COMPILER->defineVal( $3 ); $1->pushBack( $3 ); } ; expression_pair_list: expression ARROW expression { $$ = new Falcon::DictDecl(); $$->pushBack( $1, $3 ); } | expression_pair_list COMMA expression ARROW expression { $1->pushBack( $3, $5 ); $$ = $1; } ; %% /* c code */ void flc_src_error (const char *s) /* Called by yyparse on error */ { /* do nothing: manage it in the action */ } /* end of src_parser.yy */ engine/stackframe.cpp000066400000000000000000000045531176363201700151640ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: stackframe.cpp Implementation for stack frame functions ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom ott 15 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Implementation for stack frame functions */ #include #include namespace Falcon { StackFrame::StackFrame( const StackFrame& other ): m_break( other.m_break ), m_ret_pc( other.m_ret_pc ), m_call_pc( other.m_call_pc ), m_param_count( other.m_param_count ), m_try_base( other.m_try_base ), m_symbol( other.m_symbol ), m_module( other.m_module ), m_endFrameFunc( other.m_endFrameFunc ), m_prevTryFrame( other.m_prevTryFrame ), m_self( other.m_self ), m_binding( other.m_binding ), m_params( other.m_params ), m_prev(0), m_stack(other.m_stack) { } StackFrame* StackFrame::copyDeep( StackFrame** bottom ) { StackFrame* curTryFrame = 0; StackFrame* top = 0; StackFrame* current = 0; StackFrame* orig = this; // Copy this frame. while( orig != 0 ) { StackFrame* nframe = new StackFrame(*orig); // is this the top frame? if ( top == 0 ) { top = nframe; current = nframe; } else { current->prev( nframe ); current->prepareParams( nframe, current->m_param_count ); } // Did we reach a given try frame ? if( orig == curTryFrame ) { // then change all the matching fames with this one StackFrame* review = top; while( review != nframe ) { if( review->m_prevTryFrame == orig ) review->m_prevTryFrame = nframe; review = review->prev(); } } current = nframe; curTryFrame = orig->m_prevTryFrame; orig = orig->prev(); } if ( bottom != 0 ) *bottom = current; return top; } void StackFrame::gcMark( uint32 mark ) { uint32 sl = stackSize(); memPool->markItem( m_self ); memPool->markItem( m_binding ); for( uint32 pos = 0; pos < sl; pos++ ) { memPool->markItem( stackItems()[ pos ] ); } } } /* end of stackframe.cpp */ engine/stdstreams_unix.cpp000066400000000000000000000043571176363201700163020ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: stdstreams_unix.cpp Unix specific standard streams factories. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven ago 25 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Unix specific standard streams factories. */ #include #include #include #include namespace Falcon { Stream *stdInputStream() { Stream *stdin = new StreamBuffer( new StdInStream ); String enc; Falcon::Transcoder *coder; if ( Falcon::GetSystemEncoding( enc ) ) { coder = Falcon::TranscoderFactory( enc ); if ( coder == 0 ) { return stdin; } } else return stdin; coder->setUnderlying( stdin, true ); return coder; } Stream *stdOutputStream() { Stream *stdout = new StreamBuffer( new StdOutStream ); //Stream *stdout = new StdOutStream; String enc; Falcon::Transcoder *coder; if ( Falcon::GetSystemEncoding( enc ) ) { coder = Falcon::TranscoderFactory( enc ); if ( coder == 0 ) { return stdout; } } else return stdout; coder->setUnderlying( stdout, true ); return coder; } Stream *stdErrorStream() { Stream *stderr = new StreamBuffer( new StdErrStream ); String enc; Falcon::Transcoder *coder; if ( Falcon::GetSystemEncoding( enc ) ) { coder = Falcon::TranscoderFactory( enc ); if ( coder == 0 ) { return stderr; } } else return stderr; coder->setUnderlying( stderr, true ); return coder; } Stream *DefaultTextTranscoder( Stream *underlying, bool own ) { String encoding; if ( ! GetSystemEncoding( encoding ) ) return underlying; Transcoder *encap = TranscoderFactory( encoding, underlying, own ); // in unix there's no difference between text and binary, so there's no other transcoder to add. return encap; } Stream *AddSystemEOL( Stream *underlying, bool ) { return underlying; } } /* end of stdstreams_unix.cpp */ engine/stdstreams_win.cpp000066400000000000000000000056121176363201700161070ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: vm_stdstreams_win.cpp Windows specific standard streams factories. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven ago 25 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Windows specifi standard streams factories. Actually, the same as unix, but using also the CRLF transcoder. */ #include #include #include #include namespace Falcon { Stream *stdInputStream() { Stream *stdin = new StreamBuffer( new StdInStream ); String enc; Falcon::Transcoder *coder; if ( Falcon::GetSystemEncoding( enc ) ) { coder = Falcon::TranscoderFactory( enc ); if ( coder == 0 ) { coder = new TranscoderByte(0); } } else coder = new TranscoderByte(0); coder->setUnderlying( stdin, true ); /*Falcon::Transcoder *wincoder = new TranscoderEOL(0); wincoder->setUnderlying( coder, true );*/ return coder; } Stream *stdOutputStream() { Stream *stdout = new StreamBuffer( new StdOutStream ); String enc; Falcon::Transcoder *coder; if ( Falcon::GetSystemEncoding( enc ) ) { coder = Falcon::TranscoderFactory( enc ); if ( coder == 0 ) { coder = new TranscoderByte(0); } } else coder = new TranscoderByte(0); coder->setUnderlying( stdout, true ); Falcon::Transcoder *wincoder = new TranscoderEOL(0); wincoder->setUnderlying( coder, true ); return wincoder; } Stream *stdErrorStream() { Stream *stderr = new StreamBuffer( new StdErrStream ); String enc; Falcon::Transcoder *coder; if ( Falcon::GetSystemEncoding( enc ) ) { coder = Falcon::TranscoderFactory( enc ); if ( coder == 0 ) { coder = new TranscoderByte(0); } } else coder = new TranscoderByte(0); coder->setUnderlying( stderr, true ); Falcon::Transcoder *wincoder = new TranscoderEOL(0); wincoder->setUnderlying( coder, true ); return wincoder; } Stream *DefaultTextTranscoder( Stream *underlying, bool own ) { String encoding; if ( ! GetSystemEncoding( encoding ) ) { Falcon::Transcoder *wincoder = new TranscoderEOL(0); wincoder->setUnderlying( underlying, own ); return wincoder; } Transcoder *encap = TranscoderFactory( encoding ); encap->setUnderlying( underlying, own ); // the wincoder owns the underlying for sure. Falcon::Transcoder *wincoder = new TranscoderEOL(0); wincoder->setUnderlying( encap, true ); return wincoder; } Stream *AddSystemEOL( Stream *underlying, bool own ) { return new TranscoderEOL( underlying, own ); } } /* end of stdstreams_win.cpp */ engine/stream.cpp000066400000000000000000000106321176363201700143320ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: stream.cpp Implementation of common stream utility functions ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven ago 25 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Implementation of common stream utility functions */ #include #include #include namespace Falcon { Stream::Stream( const Stream &other ): m_rhBufferSize( other.m_rhBufferSize ), m_rhBufferPos( other.m_rhBufferPos ), m_streamType( other.m_streamType ), m_status( other.m_status ), m_lastMoved( other.m_lastMoved ) { if ( m_rhBufferSize != 0 ) { m_rhBuffer = (uint32 *) memAlloc( m_rhBufferSize * sizeof( uint32 ) ); memcpy( m_rhBuffer, other.m_rhBuffer, m_rhBufferSize * sizeof( uint32 ) ); } else m_rhBuffer = 0; } bool Stream::errorDescription( ::Falcon::String &description ) const { if ( m_status == t_unsupported ) { description = "Unsupported operation for this stream"; return true; } return false; } Stream::~Stream() { if ( m_rhBuffer != 0 ) memFree( m_rhBuffer ); } //================= // Private members void Stream::pushBuffer( uint32 chr ) { if ( m_rhBufferPos == m_rhBufferSize ) { m_rhBufferSize += FALCON_READAHEAD_BUFFER_BLOCK; uint32 *buf = (uint32 *) memRealloc( m_rhBuffer, m_rhBufferSize *sizeof(uint32) ); m_rhBuffer = buf; } m_rhBuffer[ m_rhBufferPos ] = chr; m_rhBufferPos ++; reset(); } bool Stream::popBuffer( uint32 &chr ) { if( m_rhBufferPos == 0 ) return false; m_rhBufferPos--; chr = m_rhBuffer[ m_rhBufferPos ]; return true; } //====================== // Public members void Stream::unget( const String &target ) { uint32 pos = target.length(); while( pos > 0 ) { pos--; unget( target.getCharAt( pos ) ); } } bool Stream::readAhead( uint32 &chr ) { if( popBuffer( chr ) ) return true; if ( ! get( chr ) ) return false; unget( chr ); return true; } bool Stream::readAhead( String &target, uint32 size ) { if ( readString( target, size ) ) return false; unget( target ); return true; } void Stream::discardReadAhead( uint32 count ) { if( count == 0 || count >= m_rhBufferPos ) m_rhBufferPos = 0; else m_rhBufferPos -= count; } bool Stream::flush() { // does nothing return true; } bool Stream::readString( String &target, uint32 size ) { if ( size == 0 ) return true; uint32 chr; // if we can't get EVEN a char, return false. if( ! get( chr ) || ! good() ) return false; target.append( chr ); size --; while( size > 0 ) { // if we can't get a char, return false on stream error. if ( ! get( chr ) ) return good(); target.append( chr ); size --; } return true; } bool Stream::writeString( const String &source, uint32 begin, uint32 end ) { uint32 pos = begin; if ( end > source.length() ) end = source.length(); while( pos < end ) { // some error in writing? if ( ! put( source.getCharAt( pos ) ) ) return false; ++pos; } return true; } //====================================== // Overridables // Stream *Stream::clone() const { return 0; } bool Stream::close() { status( t_unsupported ); return false; } int32 Stream::read( void *, int32 ) { status( t_unsupported ); return -1; } int32 Stream::write( const void *, int32 ) { status( t_unsupported ); return -1; } int64 Stream::tell() { status( t_unsupported ); return -1; } bool Stream::truncate( int64 ) { status( t_unsupported ); return false; } int32 Stream::readAvailable( int32, const Sys::SystemData *sysData ) { status( t_unsupported ); return -1; } int32 Stream::writeAvailable( int32, const Sys::SystemData *sysData ) { status( t_unsupported ); return -1; } bool Stream::put( uint32 chr ) { status( t_unsupported ); return false; } int64 Stream::seek( int64 , e_whence ) { status( t_unsupported ); return -1; } int64 Stream::lastError() const { return -1; } bool Stream::get( uint32 &chr ) { status( t_unsupported ); return false; } } /* end of stream.cpp */ engine/streambuffer.cpp000066400000000000000000000210641176363201700155250ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: streambuffer.cpp Buffer wrapper for stream operations. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 02 Feb 2009 16:45:57 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Buffer wrapper for stream operations. */ #include #include #include #include #include namespace Falcon { StreamBuffer::StreamBuffer( Stream *underlying, bool bOwn, uint32 bufSize ): Stream( underlying->type() ), m_bufSize( bufSize ), m_changed( false ), m_bufPos(0), m_bufLen(0), m_filePos(0), m_bReseek( false ), m_stream( underlying ), m_streamOwner( bOwn ) { m_buffer = (byte *) memAlloc( m_bufSize ); } StreamBuffer::StreamBuffer( const StreamBuffer &other ): Stream( other.m_streamType ), m_bufSize( other.m_bufSize ), m_changed( other.m_changed ), m_bufPos( other.m_bufPos ), m_bufLen( other.m_bufLen ), m_filePos( other.m_filePos ), m_bReseek( other.m_bReseek ), m_streamOwner( other.m_streamOwner ) { if( m_streamOwner ) m_stream = dyncast(other.m_stream->clone()); else m_stream = other.m_stream; m_buffer = (byte *) memAlloc( m_bufSize ); } StreamBuffer::~StreamBuffer() { flush(); if( m_streamOwner ) delete m_stream; memFree( m_buffer ); } StreamBuffer *StreamBuffer::clone() const { return new StreamBuffer( *this ); } bool StreamBuffer::refill() { if ( ! flush() ) return false; if( m_bReseek ) { m_stream->seekBegin( m_filePos ); m_bReseek = false; } else { m_filePos += m_bufLen; } m_bufPos = 0; int32 readIn = m_stream->read( m_buffer, m_bufSize ); if ( readIn < 0 ) { m_bufLen = 0; return false; } // changed was set to false from flush m_bufLen = readIn; return true; } int32 StreamBuffer::read( void *b, int32 size ) { // minimal sanity check if ( size <= 0 ) return 0; if( ! m_stream->good() || ! m_stream->open() ) return -1; byte *buf = (byte*) b; int32 avail = m_bufLen - m_bufPos; // have we something to store in the buffer? if ( avail > 0 ) { // enough to fill the buffer? if ( avail >= size ) { memcpy( buf, m_buffer + m_bufPos, size ); m_bufPos += size; return size; } else { // in the meanwhile, put the data in. memcpy( buf, m_buffer + m_bufPos, avail ); m_bufPos = m_bufLen; // declare we have consumed everything. // return a partial read in case of underlying networks if ( m_stream->type() == t_network ) return avail; } } // if we're here, we need to refill the buffer, or eventually to read everything from the stream // the amount of data we still have to put in the buffer is size - avail. int32 toBeRead = size - avail; // would be a new buffer enough to store the data? if ( toBeRead <= m_bufSize ) { if ( ! refill() ) { // if the refill operation failed, return what we have read. return m_stream->bad() ? -1 : avail; } // if the refill operation succed, it is still possible that it has read less than toBeRead. if ( m_bufLen < toBeRead ) toBeRead = m_bufLen; memcpy( buf + avail, m_buffer, toBeRead ); m_bufPos = toBeRead; // declare we have consumed the data. return toBeRead + avail; } else { // it's of no use to refill the buffer now. Just read the required size and update the file pointer. m_filePos += m_bufLen; // a buffer is gone. int32 readin = m_stream->read( buf + avail, size - avail ); if( readin > 0 ) { m_filePos += readin; return readin + avail; } else { // we have an error, but return the read data. return avail; } } } int32 StreamBuffer::write( const void *b, int32 size ) { // minimal sanity check if ( size <= 0 ) return 0; if( m_stream->status() != t_open ) return -1; const byte *buf = (byte*) b; // first; is there any space left in the buffer for write? int32 avail = m_bufSize - m_bufPos; if( avail > 0 ) { m_changed = true; // good; if we have enough space, advance and go away. if ( size <= avail ) { memcpy( m_buffer + m_bufPos, buf, size ); m_bufPos += size; if ( m_bufLen < m_bufPos ) { m_bufLen = m_bufPos; } return size; } else { // nay, we can write only part of the stuff. memcpy( m_buffer + m_bufPos, buf, avail ); m_bufPos = m_bufLen = m_bufSize; } } // we have still to write part or all the data. // now, if the rest of the data can be stored in the next buffer, // refill and write. Otherwise, just flush and try a single write out. int32 toBeWritten = size - avail; if( toBeWritten <= m_bufSize ) { flush(); m_changed = true; // ensure we declare this buffer changed anyhow memcpy( m_buffer, buf + avail, toBeWritten ); m_bufPos = m_bufLen = toBeWritten; return avail + toBeWritten; } else { flush(); // but don't reload now toBeWritten = m_stream->write( buf + avail, toBeWritten ); if( toBeWritten < 0 ) { return avail; } m_filePos += toBeWritten; return avail + toBeWritten; } } bool StreamBuffer::close() { flush(); return m_stream->close(); } int64 StreamBuffer::tell() { return m_filePos + m_bufPos; } bool StreamBuffer::truncate( int64 pos ) { if ( pos == -1 ) { // shorten the buffer to the current position m_bufLen = m_bufPos; // And truncate if( m_stream->truncate( m_filePos + m_bufPos ) ) { flush(); return true; } return false; } else { int64 curpos = m_filePos + m_bufPos; flush(); // and trunk at the desired position if ( pos < curpos ) { m_filePos = pos; curpos = pos; } m_stream->seekBegin( curpos ); return m_stream->truncate( pos ); } } int32 StreamBuffer::readAvailable( int32 msecs_timeout, const Sys::SystemData *sysData ) { if ( m_bufPos < m_bufLen ) return m_bufLen - m_bufPos; return m_stream->readAvailable( msecs_timeout, sysData ); } int32 StreamBuffer::writeAvailable( int32 msecs_timeout, const Sys::SystemData *sysData ) { if ( m_bufPos < m_bufSize ) return m_bufSize - m_bufPos; return m_stream->writeAvailable( msecs_timeout, sysData ); } int64 StreamBuffer::seek( int64 pos, e_whence whence ) { // TODO: optimize and avoid re-buffering if we're still in the buffer. if( whence == ew_cur ) { pos = m_filePos + m_bufPos + pos; whence = ew_begin; } flush(); m_bufLen = m_bufPos = 0; m_filePos = m_stream->seek( pos, whence ); m_bReseek = false; return m_filePos; } bool StreamBuffer::flush() { if ( ! m_changed || m_bufLen == 0 ) return true; int32 written = m_stream->write( m_buffer, m_bufLen ); int32 count = written; while( written > 0 && count < m_bufLen ) { written = m_stream->write( m_buffer + count, m_bufLen - count ); count += written; } if( written < 0 ) return false; m_filePos += m_bufPos; m_bReseek = m_bReseek || m_bufPos != m_bufLen; m_changed = false; m_bufPos = m_bufLen = 0; m_stream->flush(); return true; } bool StreamBuffer::get( uint32 &chr ) { if ( popBuffer(chr) ) return true; if ( m_bufPos == m_bufLen ) { if ( ! refill() ) return false; if( m_bufPos == m_bufLen ) // eof? return false; } chr = m_buffer[m_bufPos++ ]; return true; } bool StreamBuffer::put( uint32 chr ) { if ( m_bufPos == m_bufSize ) { if ( ! flush() ) return false; m_buffer[ 0 ] = chr; m_bufPos = m_bufLen = 1; m_changed = true; return true; } m_buffer[ m_bufPos++ ] = chr; m_changed = true; if ( m_bufLen < m_bufPos ) m_bufLen = m_bufPos; return true; } bool StreamBuffer::resizeBuffer( uint32 size ) { fassert( size > 0 ); if ( ! flush() ) return false; memFree( m_buffer ); m_buffer = (byte*) memAlloc( size ); m_bufSize = size; return true; } } /* end of streambuffer.cpp */ engine/string.cpp000066400000000000000000001551441176363201700143550ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: string.cpp Implementation of Core Strings. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 24 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Implementation of Core Strings. \todo Add support for international strings. */ #include #include #include #include #include #include #include #include #include namespace Falcon { namespace csh { Static handler_static; Buffer handler_buffer; Static16 handler_static16; Buffer16 handler_buffer16; Static32 handler_static32; Buffer32 handler_buffer32; template inline void copySized( byte* dest_, byte* src_, uint32 size ) { if ( size != 0 ) { t1* dest = (t1*) dest_; t2* src = (t2*) src_; do { size--; dest[size] = (t1) src[size]; } while( size > 0 ); } } template inline void copySame( byte* dest_, byte* src_, uint32 size ) { if ( size != 0 ) { uint32 len = size * sizeof(T); T* dest = (T*) dest_; T* src = (T*) src_; // overlapping? -- use memmove, else use memcpy; but do the check only if worth if( len < 10 || (src + len > dest) || (dest + len > src ) ) memmove( dest, src, len ); else memcpy( dest, src, len ); } } // service function; adapts a smaller buffer into a larger one // srclen is in "elements" (bytes * charLen). // returns also the dynamic buffer manipulator useful to handle the target buffer static Base* adaptBuffer( byte *srcBuffer, uint32 srcPos, uint32 srcCharLen, byte *destBuffer, uint32 destPos, uint32 destCharLen, uint32 srcLen ) { srcBuffer += srcPos * srcCharLen; destBuffer += destPos * destCharLen; switch( destCharLen ) { case 1: switch( srcCharLen ) { case 1: copySame( destBuffer, srcBuffer, srcLen ); break; case 2: copySized( destBuffer, srcBuffer, srcLen ); break; case 4: copySized( destBuffer, srcBuffer, srcLen ); break; } return &handler_buffer; case 2: switch( srcCharLen ) { case 1: copySized( destBuffer, srcBuffer, srcLen ); break; case 2: copySame( destBuffer, srcBuffer, srcLen ); break; case 4: copySized( destBuffer, srcBuffer, srcLen ); break; } return &handler_buffer16; case 4: switch( srcCharLen ) { case 1: copySized( destBuffer, srcBuffer, srcLen ); break; case 2: copySized( destBuffer, srcBuffer, srcLen ); break; case 4: copySame( destBuffer, srcBuffer, srcLen ); break; } return &handler_buffer32; } return 0; } uint32 Byte::length( const String *str ) const { return str->size() / charSize(); } uint32 Byte::getCharAt( const String *str, uint32 pos ) const { return (uint32) str->getRawStorage()[pos]; } uint32 Static16::length( const String *str ) const { return str->size() >> 1; } uint32 Static32::length( const String *str ) const { return str->size() >> 2; } uint32 Buffer16::length( const String *str ) const { return str->size() >> 1; } uint32 Buffer32::length( const String *str ) const { return str->size() >> 2; } uint32 Static16::getCharAt( const String *str, uint32 pos ) const { return (uint32) reinterpret_cast< uint16 *>(str->getRawStorage())[ pos ]; } uint32 Buffer16::getCharAt( const String *str, uint32 pos ) const { return (uint32) reinterpret_cast< uint16 *>(str->getRawStorage())[ pos ]; } uint32 Static32::getCharAt( const String *str, uint32 pos ) const { return reinterpret_cast< uint32 *>(str->getRawStorage())[ pos ]; } uint32 Buffer32::getCharAt( const String *str, uint32 pos ) const { return reinterpret_cast< uint32 *>(str->getRawStorage())[ pos ]; } void Byte::subString( const String *str, int32 start, int32 end, String *tgt ) const { uint32 nlen = str->length(); if( start < 0 ) start = int(nlen) + start; if( end < 0 ) end = int(nlen) + end + 1; if ( start < 0 || start >= (int)nlen || end < 0 || end == start) { tgt->size( 0 ); return; } byte *storage, *source; int16 cs = charSize(); source = str->getRawStorage(); if ( end < start ) { uint32 len = start - end + 1; if ( tgt->allocated() < len * cs ) { storage = (byte *) memAlloc( len * cs ); } else storage = tgt->getRawStorage(); switch( cs ) { case 1: { for( uint32 i = 0; i < len ; i ++ ) storage[i] = source[start-i]; tgt->size( len ); } break; case 2: { uint16 *storage16 = (uint16 *) storage; uint16 *source16 = (uint16 *) source; for( uint32 i = 0; i < len ; i ++ ) storage16[i] = source16[start-i]; tgt->size( len * 2 ); } break; case 4: { uint32 *storage32 = (uint32 *) storage; uint32 *source32 = (uint32 *) source; for( uint32 i = 0; i < len ; i ++ ) storage32[i] = source32[start-i]; tgt->size( len * 4 ); } break; } } else { if ( end > (int)nlen ) end = nlen; uint32 len = (end - start)*cs; if ( tgt->allocated() < len ) { storage = (byte *) memAlloc( len ); } else storage = tgt->getRawStorage(); memcpy( storage, str->getRawStorage() + (start * cs) , len ); tgt->size( len ); } // was the storage not enough? if ( storage != tgt->getRawStorage() ) { if ( tgt->allocated() != 0 ) tgt->manipulator()->destroy( tgt ); tgt->allocated( tgt->size() ); tgt->setRawStorage( storage ); } switch( cs ) { case 1: tgt->manipulator( &handler_buffer ); break; case 2: tgt->manipulator( &handler_buffer16 ); break; case 4: tgt->manipulator( &handler_buffer32 ); break; } } bool Byte::change( String *str, uint32 start, uint32 end, const String *source ) const { uint32 strLen = str->length(); if ( start >= strLen ) return false; if ( end > strLen ) end = strLen; if ( end < start ) { uint32 temp = end; end = start+1; start = temp; } int32 len = end - start; insert( str, start, len, source ); return true; } String *Byte::clone( const String *str ) const { return new String( *str ); } uint32 Byte::find( const String *str, const String *element, uint32 start, uint32 end ) const { if ( str->size() == 0 || element->size() == 0 ) return npos; if ( end > str->length() ) // npos is defined to be greater than any size end = str->length(); if ( end < start ) { uint32 temp = end; end = start; start = temp; } uint32 pos = start; uint32 keyStart = element->getCharAt( 0 ); uint32 elemLen = element->length(); while( pos + elemLen <= end ) { if ( str->getCharAt( pos ) == keyStart ) { uint32 len = 1; while( pos + len < end && len < elemLen && element->getCharAt(len) == str->getCharAt( pos + len ) ) len++; if ( len == elemLen ) return pos; } pos++; } // not found. return npos; } uint32 Byte::rfind( const String *str, const String *element, uint32 start, uint32 end ) const { if ( str->size() == 0 || element->size() == 0 ) return npos; if ( end > str->length() ) // npos is defined to be greater than any size end = str->length(); if ( end < start ) { uint32 temp = end; end = start; start = temp; } uint32 keyStart = element->getCharAt( 0 ); uint32 elemLen = element->length(); if ( elemLen > (end - start) ) { // can't possibly be found return npos; } uint32 pos = end - elemLen; while( pos >= start ) { if ( str->getCharAt( pos ) == keyStart ) { uint32 len = 1; while( pos + len < end && len < elemLen && element->getCharAt(len) == str->getCharAt( pos + len ) ) len++; if ( len == elemLen ) return pos; } if ( pos == 0 ) break; pos--; } // not found. return npos; } void Byte::remove( String *str, uint32 pos, uint32 len ) const { uint32 sl = str->length(); if ( len == 0 || pos > sl ) return; uint32 cs = charSize(); if ( pos + len > sl ) len = sl - pos; uint32 newLen = (sl - len) *cs; byte *mem = (byte *) memAlloc( newLen ); if ( pos > 0 ) memcpy( mem, str->getRawStorage(), pos * cs ); if ( pos + len < sl ) memcpy( mem + pos *cs, str->getRawStorage() +( pos + len) *cs , (sl - pos - len) * cs ); // for non-static strings... if ( str->allocated() != 0 ) { memFree( str->getRawStorage() ); } str->setRawStorage( mem, newLen ); // subclasses will set correct manipulator if needed. } void Byte::bufferize( String *str ) const { // already buffered? if ( ! str->isStatic() ) return; uint32 size = str->m_size; if ( size != 0 ) { uint32 oldSize = str->allocated(); byte *mem = (byte *) memAlloc( size ); memcpy( mem, str->getRawStorage(), size ); if( oldSize != 0 ) { memFree( str->getRawStorage() ); } str->setRawStorage( mem, size ); str->m_class = str->m_class->bufferedManipulator(); } } void Byte::bufferize( String *str, const String *strOrig ) const { // copy the other string contents. if ( str->m_allocated != 0 ) memFree( str->m_storage ); uint32 size = strOrig->m_size; if ( size == 0 ) { str->m_class = &handler_static; str->setRawStorage( 0, 0 ); } else { byte *mem = (byte *) memAlloc( size ); memcpy( mem, strOrig->getRawStorage(), size ); str->setRawStorage( mem, size ); str->m_class = strOrig->m_class->bufferedManipulator(); } } void Byte::reserve( String *str, uint32 size, bool relative, bool block ) const { if ( relative ) size += str->m_allocated; register int32 chs = charSize(); if ( block ) { if ( size % FALCON_STRING_ALLOCATION_BLOCK != 0 ) { size /= (FALCON_STRING_ALLOCATION_BLOCK * chs); size ++; size *= (FALCON_STRING_ALLOCATION_BLOCK * chs); } } uint32 nextAlloc = size * chs; // the required size may be already allocated if ( nextAlloc > str->allocated() ) { byte *mem = (byte *) memAlloc( nextAlloc ); uint32 size = str->m_size; if ( str->m_size > 0 ) memcpy( mem, str->m_storage, str->m_size ); // we can now destroy the old string. if ( str->allocated() != 0 ) memFree( str->m_storage ); str->m_storage = mem; str->m_size = size; str->m_allocated = nextAlloc; } // let the subclasses set the correct manipulator } //============================================================0 void Static::shrink( String *str ) const { // do nothing } void Static::reserve( String *str, uint32 size, bool relative, bool block ) const { Byte::reserve( str, size, relative, block ); str->m_class = &handler_buffer; } const Base *Static::bufferedManipulator() const { return &handler_buffer; } void Static16::reserve( String *str, uint32 size, bool relative, bool block ) const { Byte::reserve( str, size, relative, block ); str->m_class = &handler_buffer16; } const Base *Static16::bufferedManipulator() const { return &handler_buffer16; } void Static32::reserve( String *str, uint32 size, bool relative, bool block ) const { Byte::reserve( str, size, relative, block ); str->m_class = &handler_buffer32; } const Base *Static32::bufferedManipulator() const { return &handler_buffer32; } void Static::setCharAt( String *str, uint32 pos, uint32 chr ) const { byte *buffer; int32 size = str->size(); if( chr <= 0xFF ) { buffer = (byte *) memAlloc( size ); memcpy( buffer, str->getRawStorage(), size ); buffer[ pos ] = (byte) chr; str->manipulator( &handler_buffer ); } else if ( chr <= 0xFFFF ) { uint16 *buf16 = (uint16 *) memAlloc( size * 2 ); buffer = str->getRawStorage(); for ( int i = 0; i < size; i ++ ) buf16[ i ] = (uint16) buffer[ i ]; buf16[ pos ] = (uint16) chr; buffer = (byte *) buf16; size *= 2; str->manipulator( &handler_buffer16 ); } else { uint32 *buf32 = (uint32 *) memAlloc( size * 4 ); buffer = str->getRawStorage(); for ( int i = 0; i < size; i ++ ) buf32[ i ] = (uint32) buffer[ i ]; buf32[ pos ] = chr; buffer = (byte *) buf32; size *= 4; str->manipulator( &handler_buffer32 ); } uint32 oldSize = str->allocated(); if( oldSize != 0 ) memFree( str->getRawStorage() ); str->setRawStorage( buffer, size ); } void Static16::setCharAt( String *str, uint32 pos, uint32 chr ) const { byte *buffer; int32 size = str->size(); if ( chr <= 0xFFFF ) { uint16 *buf16 = (uint16 *) memAlloc( size ); memcpy( buf16, str->getRawStorage(), size ); buf16[ pos ] = (uint16) chr; buffer = (byte *) buf16; str->manipulator( &handler_buffer16 ); } else { uint32 *buf32 = (uint32 *) memAlloc( size * 2 ); uint16 *buf16 = (uint16 *) str->getRawStorage(); for ( int i = 0; i < size; i ++ ) buf32[ i ] = (uint32) buf16[ i ]; buf32[ pos ] = chr; buffer = (byte *) buf32; size *= 2; str->manipulator( &handler_buffer32 ); } uint32 oldSize = str->allocated(); str->setRawStorage( buffer, size ); if( oldSize != 0 ) memFree( str->getRawStorage() ); } void Static32::setCharAt( String *str, uint32 pos, uint32 chr ) const { byte *buffer; int32 size = str->size(); uint32 *buf32 = (uint32 *) memAlloc( size ); memcpy( buf32, str->getRawStorage(), size ); buf32[ pos ] = chr; buffer = (byte *) buf32; str->manipulator( &handler_buffer32 ); uint32 oldSize = str->allocated(); str->setRawStorage( buffer, size ); if( oldSize != 0 ) memFree( str->getRawStorage() ); } void Buffer::setCharAt( String *str, uint32 pos, uint32 chr ) const { byte *buffer; int32 size = str->size(); if( chr <= 0xFF ) { str->getRawStorage()[ pos ] = (byte) chr; } else if ( chr <= 0xFFFF ) { uint16 *buf16 = (uint16 *) memAlloc( size * 2 ); buffer = str->getRawStorage(); for ( int i = 0; i < size; i ++ ) buf16[ i ] = (uint16) buffer[ i ]; buf16[ pos ] = (uint16) chr; size *= 2; str->manipulator( &handler_buffer16 ); if( str->allocated() > 0 ) memFree( buffer ); str->setRawStorage( (byte *) buf16, size ); } else { uint32 *buf32 = (uint32 *) memAlloc( size * 4 ); buffer = str->getRawStorage(); for ( int i = 0; i < size; i ++ ) buf32[ i ] = (uint32) buffer[ i ]; buf32[ pos ] = chr; size *= 4; str->manipulator( &handler_buffer32 ); if( str->allocated() > 0 ) memFree( buffer ); str->setRawStorage( (byte *) buf32, size ); } } void Buffer16::setCharAt( String *str, uint32 pos, uint32 chr ) const { if ( chr <= 0xFFFF ) { uint16 *buf16 = (uint16 *) str->getRawStorage(); buf16[ pos ] = (uint16) chr; } else { int32 size = str->size(); uint32 *buf32 = (uint32 *) memAlloc( size * 2 ); uint16 *buf16 = (uint16 *) str->getRawStorage(); for ( int i = 0; i < size; i ++ ) buf32[ i ] = (uint32) buf16[ i ]; buf32[ pos ] = chr; size *= 2; str->manipulator( &handler_buffer32 ); if( str->allocated() > 0 ) memFree( buf16 ); str->setRawStorage( (byte *) buf32, size ); } } void Buffer32::setCharAt( String *str, uint32 pos, uint32 chr ) const { uint32 *buf32 = (uint32 *) str->getRawStorage(); buf32[ pos ] = chr; } void Static::insert( String *str, uint32 pos, uint32 len, const String *source ) const { uint32 sourceLen = source->length(); uint32 strLen = str->length(); if ( pos + len > str->size() ) len = str->size() - pos; uint32 strCharSize = str->manipulator()->charSize(); uint32 destCharSize = source->manipulator()->charSize() > str->manipulator()->charSize() ? source->manipulator()->charSize() : str->manipulator()->charSize() ; // can be 1 or larger uint32 finalSize = destCharSize * (strLen - len + sourceLen ); uint32 finalAlloc = ((finalSize / FALCON_STRING_ALLOCATION_BLOCK) + 1) * FALCON_STRING_ALLOCATION_BLOCK; // we know we have to relocate, so just do the relocation step byte *mem = (byte*) memAlloc( finalAlloc ); if ( pos > 0 ) adaptBuffer( str->getRawStorage(), 0, strCharSize, mem, 0, destCharSize, pos ); str->manipulator( adaptBuffer( source->getRawStorage(), 0, source->manipulator()->charSize(), mem, pos, destCharSize, sourceLen ) ); if ( pos + len < strLen ) adaptBuffer( str->getRawStorage(), pos + len, strCharSize, mem, pos + sourceLen, destCharSize, strLen - pos - len ); str->size( finalSize ); // Static strings CAN have non-static memory: expecially if they are de-serialized strings in modules. uint32 oldSize = str->allocated(); str->allocated( finalAlloc ); if ( oldSize > 0 ) { memFree( str->getRawStorage() ); } str->setRawStorage( mem ); } void Buffer::insert( String *str, uint32 pos, uint32 len, const String *source ) const { uint32 sourceLen = source->length(); uint32 strLen = str->length(); if ( pos + len > str->size() ) len = str->size() - pos; uint32 strCharSize = str->manipulator()->charSize(); uint32 posBytes = pos *strCharSize; uint32 lenBytes = len *strCharSize; uint32 destCharSize = source->manipulator()->charSize() > strCharSize ? source->manipulator()->charSize() : strCharSize; // can be 1 or larger uint32 finalSize = destCharSize * (strLen - len + sourceLen ); // should we re-allocate? if( finalSize > str->allocated() || destCharSize > strCharSize ) { uint32 finalAlloc = ((finalSize / FALCON_STRING_ALLOCATION_BLOCK) + 1) * FALCON_STRING_ALLOCATION_BLOCK; // we know we have to relocate, so just do the relocation step byte *mem = (byte*) memAlloc( finalAlloc ); if ( pos > 0 ) adaptBuffer( str->getRawStorage(), 0, strCharSize, mem, 0, destCharSize, pos ); str->manipulator( adaptBuffer( source->getRawStorage(), 0, source->manipulator()->charSize(), mem, pos, destCharSize, sourceLen ) ); if ( pos + len < strLen ) adaptBuffer( str->getRawStorage(), pos + len, strCharSize, mem, pos+sourceLen, destCharSize, strLen - pos - len ); if ( str->allocated() != 0 ) memFree( str->getRawStorage() ); str->allocated( finalAlloc ); str->setRawStorage( mem ); } else { // should we move the tail? if ( pos + len < strLen ) { // can we maintain our char size? if( destCharSize == strCharSize ) { uint32 sourceLenBytes = sourceLen * destCharSize; // then just move the postfix away memmove( str->getRawStorage() + posBytes + sourceLenBytes, str->getRawStorage() + posBytes + lenBytes, str->size() - posBytes - lenBytes ); } else { // adapt it to the new size. adaptBuffer( str->getRawStorage(), pos + len, strCharSize, str->getRawStorage(), pos + sourceLen, destCharSize, strLen - pos - len ); } } // adapt the incoming part str->manipulator( adaptBuffer( source->getRawStorage(), 0, source->manipulator()->charSize(), str->getRawStorage(), pos, destCharSize, sourceLen ) ); // eventually adapt the head -- adaptBuffer can work on itself if ( pos > 0 && destCharSize != strCharSize ) str->manipulator( adaptBuffer( str->getRawStorage(), 0, strCharSize, str->getRawStorage(), 0, destCharSize, pos ) ); } str->size( finalSize ); } void Static::remove( String *str, uint32 pos, uint32 len ) const { Byte::remove( str, pos, len ); // changing string type. str->manipulator( &handler_buffer ); } void Static16::remove( String *str, uint32 pos, uint32 len ) const { Byte::remove( str, pos, len ); // changing string type. str->manipulator( &handler_buffer16 ); } void Static32::remove( String *str, uint32 pos, uint32 len ) const { Byte::remove( str, pos, len ); // changing string type. str->manipulator( &handler_buffer32 ); } void Static::destroy( String *str ) const { if ( str->allocated() > 0 ) { memFree( str->getRawStorage() ); str->allocated( 0 ); str->size(0); } } void Buffer::shrink( String *str ) const { if( str->size() > str->allocated() ) { if ( str->size() == 0 ) { destroy( str ); } else { byte *mem = (byte *) memRealloc( str->getRawStorage(), str->size() ); if ( mem != str->getRawStorage() ) { memcpy( mem, str->getRawStorage(), str->size() ); str->setRawStorage( mem ); } str->allocated( str->size() ); } } } void Buffer::reserve( String *str, uint32 size, bool relative, bool block ) const { Byte::reserve( str, size, relative, block ); // manipulator is ok } void Buffer::destroy( String *str ) const { if ( str->allocated() > 0 ) { memFree( str->getRawStorage() ); str->allocated( 0 ); str->size(0); } } } // namespace csh //================================================================= // The string class //================================================================= String::String( uint32 size ): m_class( &csh::handler_buffer ), m_bExported( false ), m_bCore( false ) { m_storage = (byte *) memAlloc( size ); m_allocated = size; m_size = 0; } String::String( const char *data ): m_class( &csh::handler_static ), m_allocated( 0 ), m_storage( (byte*) const_cast< char *>(data) ), m_bExported( false ), m_bCore( false ) { m_size = strlen( data ); } String::String( const char *data, int32 len ): m_class( &csh::handler_buffer ), m_bExported( false ), m_bCore( false ) { m_size = len >= 0 ? len : strlen( data ); m_allocated = (( m_size / FALCON_STRING_ALLOCATION_BLOCK ) + 1 ) * FALCON_STRING_ALLOCATION_BLOCK; m_storage = (byte *) memAlloc( m_allocated ); memcpy( m_storage, data, m_size ); } String::String( const wchar_t *data ): m_allocated( 0 ), m_storage( (byte*) const_cast< wchar_t *>(data) ), m_bExported( false ), m_bCore( false ) { if ( sizeof( wchar_t ) == 2 ) m_class = &csh::handler_static16; else m_class = &csh::handler_static32; uint32 s = 0; while( data[s] != 0 ) ++s; m_size = s * sizeof( wchar_t ); } String::String( const wchar_t *data, int32 len ): m_allocated( 0 ), m_storage( (byte *) const_cast< wchar_t *>( data ) ), m_bExported( false ), m_bCore( false ) { if ( sizeof( wchar_t ) == 2 ) m_class = &csh::handler_buffer16; else m_class = &csh::handler_buffer32; if ( len >= 0 ) { m_size = len * sizeof( wchar_t ); } else { uint32 s = 0; while( data[s] != 0 ) ++s; m_size = s * sizeof( wchar_t ); } m_allocated = (( m_size / FALCON_STRING_ALLOCATION_BLOCK ) + 1 ) * FALCON_STRING_ALLOCATION_BLOCK; m_storage = (byte *) memAlloc( m_allocated ); memcpy( m_storage, data, m_size ); } String::String( const String &other, uint32 begin, uint32 end ): m_allocated( 0 ), m_size( 0 ), m_storage( 0 ), m_bExported( false ), m_bCore( false ) { // by default, copy manipulator m_class = other.m_class; if ( other.m_allocated == 0 ) { if ( other.m_size == 0 ) { m_size = 0; m_allocated = 0; setRawStorage( 0 ); } else { if( begin < end ) { uint32 cs = m_class->charSize(); setRawStorage( other.getRawStorage() + begin * cs ); m_size = (end - begin ) * cs; if ( m_size > (other.m_size - (begin *cs )) ) m_size = other.m_size - (begin *cs ); } else { // reverse substring, we have to bufferize. other.m_class->subString( &other, begin, end, this ); } } } else other.m_class->subString( &other, begin, end, this ); } void String::copy( const String &other ) { if ( m_allocated != 0 ) m_class->destroy( this ); m_class = other.m_class; m_size = other.m_size; m_allocated = other.m_allocated; if ( m_allocated > 0 ) { m_storage = (byte *) memAlloc( m_allocated ); if ( m_size > 0 ) memcpy( m_storage, other.m_storage, m_size ); } else m_storage = other.m_storage; } String &String::adopt( char *buffer, uint32 size, uint32 allocated ) { if ( m_allocated != 0 ) m_class->destroy( this ); m_class = &csh::handler_buffer; m_size = size; m_allocated = allocated; m_storage = (byte *) buffer; return *this; } String &String::adopt( wchar_t *buffer, uint32 size, uint32 allocated ) { if ( m_allocated != 0 ) m_class->destroy( this ); if ( sizeof( wchar_t ) == 2 ) m_class = &csh::handler_buffer16; else m_class = &csh::handler_buffer32; m_size = size * sizeof( wchar_t ); m_allocated = allocated; m_storage = (byte *) buffer; return *this; } int String::compare( const char *other ) const { uint32 pos = 0; uint32 len = length(); while( pos < len ) { uint32 c1 = getCharAt( pos ); if ( c1 < (uint32) *other ) return -1; // return greater also if other is ended; we are NOT ended... else if ( c1 > (uint32) *other || (uint32) *other == 0 ) return 1; other ++; pos++; } if ( *other != 0 ) // other is greater return -1; // the strings are the same return 0; } int String::compareIgnoreCase( const char *other ) const { uint32 pos = 0; uint32 len = length(); while( pos < len ) { uint32 c1 = getCharAt( pos ); uint32 cmpval = (uint32) *other; if ( c1 >=0x41 && c1 <= 0x5A ) c1 += 0x20; if ( cmpval >=0x41 && cmpval <= 0x5A ) cmpval += 0x20; if ( c1 < cmpval ) return -1; // return greater also if other is ended; we are NOT ended... else if ( c1 > cmpval || cmpval == 0 ) return 1; other ++; pos++; } if ( *other != 0 ) // other is greater return -1; // the strings are the same return 0; } int String::compare( const wchar_t *other ) const { uint32 pos = 0; uint32 len = length(); while( pos < len ) { uint32 c1 = getCharAt( pos ); if ( c1 < (uint32) *other ) return -1; // return greater also if other is ended; we are NOT ended... else if ( c1 > (uint32) *other || (uint32) *other == 0 ) return 1; other ++; pos++; } if ( *other != 0 ) // other is greater return -1; // the strings are the same return 0; } int String::compareIgnoreCase( const wchar_t *other ) const { uint32 pos = 0; uint32 len = length(); while( pos < len ) { uint32 c1 = getCharAt( pos ); uint32 cmpval = (uint32) *other; if ( c1 >=0x41 && c1 <= 0x5A ) c1 += 0x20; if ( cmpval >=0x41 && cmpval <= 0x5A ) cmpval += 0x20; if ( c1 < cmpval ) return -1; // return greater also if other is ended; we are NOT ended... else if ( c1 > cmpval || cmpval == 0 ) return 1; other ++; pos++; } if ( *other != 0 ) // other is greater return -1; // the strings are the same return 0; } int String::compare( const String &other ) const { uint32 len1 = length(); uint32 len2 = other.length(); uint32 len = len1 > len2 ? len2 : len1; uint32 pos = 0; while( pos < len ) { uint32 c1 = getCharAt( pos ); uint32 c2 = other.getCharAt( pos ); if ( c1 < c2 ) return -1; // return greater also if other is ended; we are NOT ended... else if ( c1 > c2 ) return 1; pos++; } // the strings are the same? if ( len1 < len2 ) return -1; // return greater also if other is ended; we are NOT ended... else if ( len1 > len2 ) return 1; // yes return 0; } int String::compareIgnoreCase( const String &other ) const { uint32 len1 = length(); uint32 len2 = other.length(); uint32 len = len1 > len2 ? len2 : len1; uint32 pos = 0; while( pos < len ) { uint32 c1 = getCharAt( pos ); uint32 c2 = other.getCharAt( pos ); if ( c1 >=0x41 && c1 <= 0x5A ) c1 += 0x20; if ( c2 >=0x41 && c2 <= 0x5A ) c2 += 0x20; if ( c1 < c2 ) return -1; // return greater also if other is ended; we are NOT ended... else if ( c1 > c2 ) return 1; pos++; } // the strings are the same? if ( len1 < len2 ) return -1; // return greater also if other is ended; we are NOT ended... else if ( len1 > len2 ) return 1; // yes return 0; } uint32 String::toCString( char *target, uint32 bufsize ) const { uint32 len = length(); // we already know that the buffer is too small? if ( bufsize <= len ) return npos; uint32 pos = 0; uint32 done = 0; while( done < len && pos < bufsize ) { uint32 chr = getCharAt( done ); if ( chr < 0x80 ) { target[ pos ++ ] = chr; } else if ( chr < 0x800 ) { if ( pos + 2 > bufsize ) return npos; target[ pos ++ ] = 0xC0 | ((chr >> 6 ) & 0x1f); target[ pos ++ ] = 0x80 | (0x3f & chr); } else if ( chr < 0x10000 ) { if ( pos + 3 > bufsize ) return npos; target[ pos ++ ] = 0xE0 | ((chr >> 12) & 0x0f ); target[ pos ++ ] = 0x80 | ((chr >> 6) & 0x3f ); target[ pos ++ ] = 0x80 | (0x3f & chr); } else { if ( pos + 4 > bufsize ) return npos; target[ pos ++ ] = 0xF0 | ((chr >> 18) & 0x7 ); target[ pos ++ ] = 0x80 | ((chr >> 12) & 0x3f ); target[ pos ++ ] = 0x80 | ((chr >> 6) & 0x3f ); target[ pos ++ ] = 0x80 | (0x3f & chr ); } ++done; } if ( pos >= bufsize ) return npos; target[pos] = '\0'; return pos; } uint32 String::toWideString( wchar_t *target, uint32 bufsize ) const { uint32 len = length(); if ( bufsize <= len * sizeof( wchar_t ) ) return npos; if ( sizeof( wchar_t ) == 2 ) { for( uint32 i = 0; i < len; i ++ ) { uint32 chr = getCharAt( i ); if ( chr > 0xFFFF ) target[ i ] = L'?'; else target[ i ] = chr; } } else { for( uint32 i = 0; i < len; i ++ ) { target[ i ] = getCharAt( i ); } } target[len] = 0; return (int32) len; } void String::append( const String &str ) { /* if ( str.size() < FALCON_STRING_ALLOCATION_BLOCK ) m_class->reserve( this, FALCON_STRING_ALLOCATION_BLOCK, true, true ); */ m_class->insert( this, length(), 0, &str ); } void String::append( uint32 chr ) { if ( size() >= allocated() ) { m_class->reserve( this, size() + FALCON_STRING_ALLOCATION_BLOCK, false, true ); // allocates a whole block next } // reserve forces to buffer, so we have only to extend size( size() + m_class->charSize() ); // and insert the last char: setCharAt( length() - 1, chr ); // if the chr can't fit our size, the whole string will be refitted, including the last // empty char that we added above. } void String::prepend( uint32 chr ) { if ( size() >= allocated() ) { m_class->reserve( this, size() + FALCON_STRING_ALLOCATION_BLOCK, false, true ); // allocates a whole block next } // reserve forces to buffer, so we have only to extend memmove( m_storage + m_class->charSize(), m_storage, size() ); size( size() + m_class->charSize() ); // and insert the first char setCharAt( 0, chr ); } void String::escape( String &strout ) const { internal_escape( strout, false ); } void String::escapeFull( String &strout ) const { internal_escape( strout, true ); } void String::internal_escape( String &strout, bool full ) const { int len = length(); int pos = 0; strout.m_class->reserve( &strout, len ); // prepare for at least len chars strout.size( 0 ); // clear target string while( pos < len ) { uint32 chat = getCharAt( pos ); switch( chat ) { case '"':strout += "\\\""; break; case '\'':strout += "\\\'"; break; case '\r': strout += "\\r"; break; case '\n': strout += "\\n"; break; case '\t': strout += "\\t"; break; case '\b': strout += "\\b"; break; case '\\': strout += "\\\\"; break; default: if ( chat < 32 || (chat >= 128 && full) ) { strout += 'x'; strout.writeNumberHex(chat, true ); } else{ strout += chat; } } pos++; } } void String::unescape() { uint32 len = length(); uint32 pos = 0; while( pos < len ) { uint32 chat = getCharAt( pos ); if ( chat == (uint32) '\\' ) { // an escape must take place uint32 endSub = pos + 1; if( endSub == len - 1 ) return; uint32 chnext = getCharAt( endSub ); uint32 chsub=0; switch( chnext ) { case '"': chsub = (uint32) '"'; break; case '\r': chsub = (uint32) '\r'; break; case '\n': chsub = (uint32) '\n'; break; case '\t': chsub = (uint32) '\t'; break; case '\b': chsub = (uint32) '\b'; break; case '\\': chsub = (uint32) '\\'; break; case '0': // parse octal number endSub ++; chsub = 0; // max lenght of octals = 11 chars, + 2 for stubs while( endSub < len && endSub - pos < 13 ) { chnext = getCharAt( endSub ); if ( chnext < 0x30 || chnext > 0x37 ) break; chsub <<= 3; //*8 chsub |= (0x7) & (chnext - 0x30); endSub ++; } break; case 'x': // parse exadecimal number endSub ++; chsub = 0; // max lenght of octals = 11 chars, + 2 for stubs while( endSub < len && endSub - pos < 13 ) { chnext = getCharAt( endSub ); if ( chnext >= 0x30 && chnext <= 0x39 ) // 0 - 9 { chsub <<= 4; //*16 chsub |= chnext - 0x30; } else if( chnext >= 0x41 && chnext <= 0x46 ) // A - F { chsub <<= 4; //*16 chsub |= chnext - 0x41 + 10; } else if( chnext >= 0x61 && chnext <= 0x66 ) // a - f { chsub <<= 4; //*16 chsub |= chnext - 0x61 + 10; } endSub ++; } break; } // substitute the char setCharAt( pos, chsub ); // remove the rest remove( pos + 1, endSub - pos ); } pos++; } } void String::serialize( Stream *out ) const { uint32 size = m_bExported ? m_size | 0x80000000 : m_size; size = endianInt32( size ); out->write( (byte *) &size, sizeof(size) ); if ( m_size != 0 && out->good() ) { byte chars = m_class->charSize(); out->write( &chars, 1 ); #ifdef FALCON_LITTLE_ENDIAN out->write( m_storage, m_size ); #else // in big endian environ, we have to reverse the code. if( chars == 1 ) { out->write( m_storage, m_size ); } else if ( chars == 2 ) { for( int i = 0; i < m_size/2; i ++ ) { uint16 chr = (uint32) endianInt16((uint16) getCharAt( i ) ); out->write( (byte *) &chr, 2 ); if (! out->good() ) return; } } else if ( chars == 4 ) { for( int i = 0; i < m_size/4; i ++ ) { uint32 chr = (uint32) endianInt32( getCharAt( i ) ); out->write( (byte *) &chr, 4 ); if (! out->good() ) return; } } #endif } } bool String::deserialize( Stream *in, bool bStatic ) { uint32 size; in->read( (byte *) &size, sizeof( size ) ); size = endianInt32(size); m_bExported = (size & 0x80000000) == 0x80000000; size = size & 0x7FFFFFFF; // if the size of the deserialized string is 0, we have an empty string. if ( size == 0 ) { m_size = 0; // if we had something allocated, we got to free it. if ( m_allocated > 0 ) { memFree( m_storage ); m_storage = 0; m_allocated = 0; } // anyhow, set the handler to static and return. manipulator(&csh::handler_static); return true; } if ( in->good() ) { byte chars; in->read( &chars, 1 ); // determine the needed manipulator if ( bStatic ) { if( m_size < size ) { return false; } m_size = size; switch( chars ) { case 1: manipulator( &csh::handler_static ); break; case 2: manipulator( &csh::handler_static16 ); break; case 4: manipulator( &csh::handler_static32 ); break; default: return false; } } else { switch( chars ) { case 1: manipulator( &csh::handler_buffer ); break; case 2: manipulator( &csh::handler_buffer16 ); break; case 4: manipulator( &csh::handler_buffer32 ); break; default: return false; } m_allocated = m_size = size; if ( m_storage != 0 ) memFree( m_storage ); m_storage = (byte *) memAlloc( m_allocated ); if( m_storage == 0 ) return false; } #ifdef FALCON_LITTLE_ENDIAN in->read( m_storage, m_size ); #else // in big endian environ, we have to reverse the code. in->read( m_storage, m_size ); if ( ! in->good() ) return; if ( chars == 2 ) { uint16* storage16 = (uint16*) m_storage; for( int i = 0; i < m_size/2; i ++ ) { storage16[i] = (uint16) endianInt16( storage16[i] ); } } else if ( chars == 4 ) { uint32* storage32 = (uint32*) m_storage; for( int i = 0; i < m_size/4; i ++ ) { storage32[i] = (uint32) endianInt32( storage32[i] ); } } #endif } return true; } void String::c_ize() { if ( allocated() <= size() || getCharAt( length() ) != 0 ) { append( 0 ); size( size() - m_class->charSize() ); } } bool String::setCharSize( uint32 nsize, uint32 subst ) { // same size? if ( nsize == m_class->charSize() ) return true; // change only the manipulator? if( size() == 0 ) { m_class->destroy( this ); // dispose anyhow allocated(0); switch( nsize ) { case 1: m_class = &csh::handler_buffer; break; case 2: m_class = &csh::handler_buffer16; break; case 4: m_class = &csh::handler_buffer32; break; default: return false; } return true; } if ( nsize != 1 && nsize != 2 && nsize != 4 ) return false; // full change. // use allocated to decide re-allocation under new char size. byte *mem = getRawStorage(); uint32 oldcs = m_class->charSize(); uint32 nalloc = (allocated()/oldcs) * nsize; uint32 oldsize = size(); byte *nmem = (byte*) memAlloc( nalloc ); csh::Base* manipulator = csh::adaptBuffer( mem, 0, oldcs, nmem, 0, nsize, length() ); m_class->destroy( this ); allocated( nalloc ); size( (oldsize/oldcs)*nsize ); m_class = manipulator; setRawStorage( nmem ); return true; } bool String::parseInt( int64 &target, uint32 pos ) const { uint32 len = length(); if ( pos >= len ) return false; target = 0; bool neg = false; uint32 chnext = getCharAt( pos ); if ( chnext == (uint32) '-' ) { neg = true; pos ++; if ( pos == len ) return false; chnext = getCharAt( pos ); } // detect overflow int64 tgtCopy = target; while( chnext >= 0x30 && chnext <= 0x39 ) { if( target < tgtCopy ) return false; tgtCopy = target; target *= 10; target += chnext - 0x30; pos ++; if ( pos == len ) break; chnext = getCharAt( pos ); } if ( chnext < 0x30 || chnext > 0x39 ) return false; if (neg) target = -target; return true; } bool String::parseDouble( double &target, uint32 pos ) const { char buffer[64]; uint32 maxlen = 63; uint32 len = length(); if ( pos >= len ) return false; // if we are single byte string, just copy if ( m_class->charSize() == 1 ) { if ( maxlen > len - pos ) maxlen = len - pos; memcpy( buffer, m_storage, maxlen ); buffer[ maxlen ] = '\0'; } else { // else convert to C string uint32 bufpos = 0; while ( bufpos < maxlen && pos < len ) { uint32 chr = getCharAt( pos ); if( chr != (uint32) '-' && chr != (uint32) 'e' && chr != (uint32) 'E' && chr < 0x30 && chr > 0x39 ) return false; buffer[ bufpos ] = (char) chr; bufpos ++; pos ++; } } // then apply sscanf if ( sscanf( buffer, "%lf", &target ) == 1 ) return true; return false; } bool String::parseBin( uint64 &target, uint32 pos ) const { uint32 len = length(); if ( pos >= len ) return false; // parse octal number target = 0; uint32 endSub = pos; // max length of binary = 64 chars, + 2 for stubs while( endSub < len && (endSub - pos < 64) ) { uint32 chnext = getCharAt( endSub ); if ( chnext < 0x30 || chnext > 0x31 ) break; target <<= 1; //*2 target |= (0x1) & (chnext - 0x30); endSub ++; } if( endSub != pos ) return true; return false; } bool String::parseOctal( uint64 &target, uint32 pos ) const { uint32 len = length(); if ( pos >= len ) return false; // parse octal number target = 0; uint32 endSub = pos; // max length of octals = 11 chars, + 2 for stubs while( endSub < len && (endSub - pos < 26) ) { uint32 chnext = getCharAt( endSub ); if ( chnext < 0x30 || chnext > 0x37 ) break; target <<= 3; //*8 target |= (0x7) & (chnext - 0x30); endSub ++; } if( endSub != pos ) return true; return false; } bool String::parseHex( uint64 &target, uint32 pos ) const { uint32 len = length(); if ( pos >= len ) return false; // parse octal number target = 0; uint32 endSub = pos; while( endSub < len && (endSub - pos < 16) ) { uint32 chnext = getCharAt( endSub ); if ( chnext >= 0x30 && chnext <= 0x39 ) // 0 - 9 { target <<= 4; //*16 target |= chnext - 0x30; } else if( chnext >= 0x41 && chnext <= 0x46 ) // A - F { target <<= 4; //*16 target |= chnext - 0x41 + 10; } else if( chnext >= 0x61 && chnext <= 0x66 ) // a - f { target <<= 4; //*16 target |= chnext - 0x61 + 10; } endSub ++; } if( endSub != pos ) return true; return false; } void String::writeNumber( int64 number ) { // prepare the buffer bool neg; char buffer[21]; uint32 pos = 19; buffer[20] = '\0'; if ( number == 0 ) { buffer[pos] = '0'; } else { if ( number < 0 ) { neg = true; number = - number; if ( number < 0 ) { // is NAN append( "NaN" ); return; } } else neg = false; while( number != 0 ) { buffer[pos--] = (char) ((number % 10) + 0x30); number /= 10; } if ( neg ) buffer[ pos ] = '-'; else pos++; } append( buffer + pos ); } void String::writeNumberHex( uint64 number, bool uppercase, int ciphers ) { // prepare the buffer char buffer[18]; uint32 pos = 16; buffer[17] = '\0'; byte base = uppercase ? 0x41 : 0x61; if( ciphers > 16 ) { ciphers = 16; } if ( number == 0 ) { buffer[pos] = '0'; } else { while( number != 0 ) { byte b = (byte)(number & 0xf); if ( b <= 9 ) buffer[pos--] = (char) (b + 0x30); else { buffer[pos--] = (char) ( b - 10 + base ); } number >>= 4; ciphers--; } } while( ciphers > 0 ) { buffer[pos--] = '0'; ciphers --; } append( buffer + pos + 1 ); } void String::writeNumberOctal( uint64 number ) { // prepare the buffer char buffer[32]; uint32 pos = 30; buffer[31] = '\0'; if ( number == 0 ) { buffer[pos] = '0'; } else { while( number != 0 ) { buffer[pos--] = (char) ((number & 0x7) + 0x30); number >>= 3; } pos++; } append( buffer + pos ); } void String::writeNumber( int64 number, const String &format ) { char buffer[64]; char bufFormat[32]; if ( format.toCString( bufFormat, 32 ) == npos ) return; sprintf( buffer, bufFormat, number ); append( buffer ); } void String::writeNumber( double number, const String &format ) { char buffer[64]; char bufFormat[32]; if ( format.toCString( bufFormat, 32 ) == npos ) return; sprintf( buffer, bufFormat, number ); append( buffer ); } String &String::bufferize( const String &other ) { m_class->bufferize( this, &other ); return *this; } String &String::bufferize() { m_class->bufferize( this ); return *this; } void String::trim( int mode ) { uint32 front = 0; uint32 len = length(); // modes: 0 = all, 1 = front, 2 = back // first, trim from behind. if ( mode == 0 || mode == 2 ) { while( len > 0 ) { uint32 chr = getCharAt( len - 1 ); if( chr != ' ' && chr != '\n' && chr != '\r' && chr != '\t' ) { break; } len --; } if ( len == 0 ) { // string is actually empty. m_size = 0; return; } } // front trim if ( mode == 0 || mode == 1 ) { while( front < len ) { uint32 chr = getCharAt( front ); if( chr != ' ' && chr != '\n' && chr != '\r' && chr != '\t' ) { break; } ++front; } } // front can't be == to len. if ( front > 0 ) { // source and dest should be different, but it will work for this configuration. m_class->subString( this, front, len, this ); } else { m_size = len * m_class->charSize(); } } void String::lower() { uint32 len = length(); for( uint32 i = 0; i < len; i++ ) { uint32 chr = getCharAt( i ); if ( chr >= 'A' && chr <= 'Z' ) { setCharAt( i, chr | 0x20 ); } } } void String::upper() { uint32 len = length(); for( uint32 i = 0; i < len; i++ ) { uint32 chr = getCharAt( i ); if ( chr >= 'a' && chr <= 'z' ) { setCharAt( i, chr & ~0x20 ); } } } bool String::fromUTF8( const char *utf8 ) { return fromUTF8( utf8, -1 ); } bool String::fromUTF8( const char *utf8, int len ) { // destroy old contents if ( m_allocated ) { m_class->destroy( this ); m_allocated = 0; } m_size = 0; // empty string? if ( len == 0 || *utf8 == 0 ) { m_class = &csh::handler_static; return true; } // start scanning while ( len != 0 && *utf8 != 0 ) { uint32 chr = 0; byte in = (byte) *utf8; // 4 bytes? -- pattern 1111 0xxx int count; if ( (in & 0xF8) == 0xF0 ) { chr = (in & 0x7 ) << 18; count = 18; } // pattern 1110 xxxx else if ( (in & 0xF0) == 0xE0 ) { chr = (in & 0xF) << 12; count = 12; } // pattern 110x xxxx else if ( (in & 0xE0) == 0xC0 ) { chr = (in & 0x1F) << 6; count = 6; } else if( in < 0x80 ) { chr = (uint32) in; count = 0; } // invalid pattern else { return false; } // read the other characters with pattern 0x10xx xxxx while( count > 0 ) { count -= 6; utf8++; byte in = (byte) *utf8; if ( in == 0 ) { // short utf8 sequence return false; } else if( (in & 0xC0) != 0x80 ) { // unrecognized pattern, protocol error return false; } chr |= (in & 0x3f) << count; } this->append( chr ); utf8++; --len; } return true; } bool String::startsWith( const String &str, bool icase ) const { uint32 len = str.length(); if ( len > length() ) return false; if ( icase ) { for ( uint32 i = 0; i < len; i ++ ) { uint32 chr1, chr2; if ( (chr1 = str.getCharAt(i)) != (chr2 = getCharAt(i)) ) { if ( chr1 >= 'A' && chr1 <= 'z' && (chr1 | 0x20) != (chr2|0x20) ) return false; } } } else { for ( uint32 i = 0; i < len; i ++ ) if ( str.getCharAt(i) != getCharAt(i) ) return false; } return true; } bool String::endsWith( const String &str, bool icase ) const { uint32 len = str.length(); uint32 mlen = length(); uint32 start = mlen-len; if ( len > mlen ) return false; if ( icase ) { for ( uint32 i = 0; i < len; ++i ) { uint32 chr1, chr2; if ( (chr1 = str.getCharAt(i)) != (chr2 = getCharAt(i+start)) ) { if ( ((chr1 >= 'A' && chr1 <= 'Z') || (chr1 >= 'a' && chr1 <= 'z') ) && (chr1 | 0x20) == (chr2|0x20) ) { continue; } return false; } } } else { for ( uint32 i = 0; i < len; ++i ) if ( str.getCharAt(i) != getCharAt(i+start) ) return false; } return true; } bool String::wildcardMatch( const String& wildcard, bool bIcase ) const { const String* wcard = &wildcard; const String* cfr = this; uint32 wpos = 0, wlen = wcard->length(); uint32 cpos = 0, clen = cfr->length(); uint32 wstarpos = 0xFFFFFFFF; while ( cpos < clen ) { if( wpos == wlen ) { // we have failed the match; but if we had a star, we // may roll back to the starpos and try to match the // rest of the string if ( wstarpos != 0xFFFFFFFF ) { wpos = wstarpos; } else { // no way, we're doomed. break; } } uint32 wchr = wcard->getCharAt( wpos ); uint32 cchr = cfr->getCharAt( cpos ); switch( wchr ) { case '?': // match any character wpos++; cpos++; break; case '*': { // mark for restart in case of bad match. wstarpos = wpos; // match till the next character wpos++; // eat all * in a row while( wpos < wlen ) { wchr = wcard->getCharAt( wpos ); if ( wchr != '*' ) break; wpos++; } if ( wpos == wlen ) { // we have consumed all the chars cpos = clen; break; } //eat up to next character while( cpos < clen ) { cchr = cfr->getCharAt( cpos ); if ( cchr == wchr ) break; cpos ++; } // we have eaten up the same char? -- then advance also wpos to prepare next loop if ( cchr == wchr ) { wpos++; cpos++; } // else, everything must stay as it is, so cpos == clen but wpos != wlen causing fail. } break; default: if ( cchr == wchr || ( bIcase && cchr < 128 && wchr < 128 && (cchr | 32) == (wchr | 32) ) ) { cpos++; wpos++; } else { // can we retry? if ( wstarpos != 0xFFFFFFFF ) wpos = wstarpos; else { // check failed -- we're doomed return false; } } } } // at the end of the loop, the match is ok only if both the cpos and wpos are at the end return wpos == wlen && cpos == clen; } void String::escapeQuotes() { uint32 len = length(); uint32 i = 0; while( i < len ) { register uint32 chr = getCharAt(i); switch( chr ) { case '\'': case '\"': insert( i, 0, "\\" ); i+=2; ++len; break; default: ++i; } } } void String::unescapeQuotes() { uint32 len = length(); uint32 i = 0; bool state = false; while( i < len ) { register uint32 chr = getCharAt(i); switch( chr ) { case '\'': case '\"': if( state ) { remove( i-1, 1 ); --len; } break; case '\\': state = true; ++i; break; default: state = false; ++i; } } } //============================================================ void string_deletor( void *data ) { delete (String *) data; } } /* end of cstring.cpp */ engine/stringitem.cpp000066400000000000000000000113451176363201700152260ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: string.cpp Item-oriented Core String implementation. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 03 Jan 2009 23:43:11 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include namespace Falcon { Item::Item( const String &str ) { CoreString* cstr = new CoreString( str ); cstr->bufferize(); setString( cstr ); } StringGarbage::~StringGarbage() { } bool StringGarbage::finalize() { delete m_str; // as we're in m_str, we are destroyed here. return true; // prevent destructor to be called. } void String::readProperty( const String &prop, Item &item ) { VMachine *vm = VMachine::getCurrent(); fassert( vm != 0 ); // try to find a generic method CoreClass* cc = vm->getMetaClass( FLC_ITEM_STRING ); if ( cc != 0 ) { uint32 id; if( cc->properties().findKey( prop, id ) ) { item = *cc->properties().getValue( id ); item.methodize( this ); return; } } String extra; item.typeName( extra ); extra.A( '.' ).A( prop ); throw new AccessError( ErrorParam( e_prop_acc, __LINE__ ).extra( extra ) ); } void String::readIndex( const Item &index, Item &target ) { switch( index.type() ) { case FLC_ITEM_INT: { int32 pos = (int32) index.asInteger(); if ( checkPosBound( pos ) ) { CoreString *gcs = new CoreString(); gcs->append( getCharAt( pos ) ); target = gcs; return; } } break; case FLC_ITEM_NUM: { int32 pos = (int32) index.asNumeric(); if ( checkPosBound( pos ) ) { CoreString *gcs = new CoreString(); gcs->append( getCharAt( pos ) ); target = gcs; return; } } break; case FLC_ITEM_RANGE: { int32 rstart = (int32) index.asRangeStart(); if ( index.asRangeIsOpen() ) { if ( checkPosBound( rstart ) ) { target = new CoreString( *this, rstart ); } else { target = new CoreString; } return; } else { int32 rend = (int32) index.asRangeEnd(); if ( checkRangeBound( rstart, rend ) ) { target = new CoreString( *this, rstart, rend ); return; } else { target = new CoreString; } return; } } break; case FLC_ITEM_REFERENCE: readIndex( index.asReference()->origin(), target ); return; } throw new AccessError( ErrorParam( e_arracc, __LINE__ ).extra( "string" ) ); } void String::writeIndex( const Item &index, const Item &target ) { register int32 pos; switch( index.type() ) { case FLC_ITEM_INT: pos = (int32) index.asInteger(); break; case FLC_ITEM_NUM: pos = (int32) index.asNumeric(); break; case FLC_ITEM_RANGE: { if ( target.isString() ) { register int pos = (int) index.asRangeStart(); register int end = (int) (index.asRangeIsOpen() ? this->length() : index.asRangeEnd()); if ( checkRangeBound( pos, end ) ) { if ( change( pos, end, *target.asString() ) ) { return; } } } } throw new AccessError( ErrorParam( e_arracc, __LINE__ ).extra( "string" ) ); case FLC_ITEM_REFERENCE: writeIndex( index.asReference()->origin(), target ); return; default: throw new AccessError( ErrorParam( e_arracc, __LINE__ ).extra( "string" ) ); } if ( target.type() == FLC_ITEM_STRING ) { String *cs_orig = target.asString(); if( cs_orig->length() > 0 ) { if ( checkPosBound( pos ) ) { setCharAt( pos, cs_orig->getCharAt(0) ); return; } } } else if( target.isOrdinal() ) { int64 chr = target.forceInteger(); if ( (chr >= 0) && (chr <= (int64) 0xFFFFFFFF) ) { if ( checkPosBound( pos ) ) { setCharAt( pos, (uint32) chr ); return; } } } throw new AccessError( ErrorParam( e_arracc, __LINE__ ).extra( "string" ) ); } } /* end of stringitem.cpp */ engine/stringstream.cpp000066400000000000000000000256231176363201700155670ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: file_StringStream.cpp Implementation of StringStream oriented streams. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab nov 13 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Implementation of StringStream oriented streams. */ #include #include #include #include #include #include namespace Falcon { class StringStream::Buffer { public: String* m_str; int32 m_refcount; Mutex m_mtx; Buffer(): m_str( new String() ), m_refcount(1) {} Buffer( const Buffer& other ): m_str( new String( *other.m_str ) ), m_refcount(1) {} void incref() { atomicInc(m_refcount); } void decref() { if (atomicDec(m_refcount) == 0) delete this; } private: ~Buffer() { delete m_str; } }; StringStream::StringStream( int32 size ): Stream( t_membuf ), m_b( new Buffer ), m_lastError(0), m_pos(0) { m_pos = 0; if ( size <= 0 ) size = 32; m_b->m_str->reserve(size); } StringStream::StringStream( const String &strbuf ): Stream( t_membuf ), m_b( new Buffer ), m_lastError(0), m_pos(0) { *m_b->m_str = strbuf; m_b->m_str->bufferize(); } StringStream::StringStream( const StringStream &strbuf ): Stream( strbuf ), m_lastError(0), m_pos( strbuf.m_pos ) { strbuf.m_b->incref(); m_b = strbuf.m_b; } StringStream::~StringStream() { m_b->decref(); } void StringStream::setBuffer( const String &source ) { m_pos = 0; m_b->m_mtx.lock(); if( m_b->m_str == 0 ) { m_b->m_str = new String( source ); } else { *m_b->m_str = source; } m_b->m_str->bufferize(); m_lastError = 0; m_b->m_mtx.unlock(); } void StringStream::setBuffer( const char* source, int size ) { m_b->m_mtx.lock(); m_pos = 0; if( m_b->m_str == 0 ) { m_b->m_str = new String; } m_b->m_str->reserve(size); memcpy( m_b->m_str->getRawStorage(), source, size ); m_b->m_str->size( size ); m_b->m_str->manipulator( &csh::handler_buffer ); m_lastError = 0; m_b->m_mtx.unlock(); } bool StringStream::detachBuffer() { m_b->m_mtx.lock(); if( m_b->m_str != 0 ) { m_b->m_str->setRawStorage(0); m_b->m_str->size(0); m_b->m_str->allocated(0); m_b->m_str->manipulator( &csh::handler_static ); m_b->m_mtx.unlock(); status( t_none ); return true; } m_b->m_mtx.unlock(); return false; } uint32 StringStream::length() const { return m_b->m_str->size(); } uint32 StringStream::allocated() const { return m_b->m_str->allocated(); } byte *StringStream::data() const { return m_b->m_str->getRawStorage(); } String *StringStream::closeToString() { m_b->m_mtx.lock(); String *s = m_b->m_str; m_b->m_str = 0; m_b->m_mtx.unlock(); return s; } CoreString *StringStream::closeToCoreString() { CoreString *temp = new CoreString; if (closeToString( *temp )) return temp; return 0; // CoreString is subject to GC } int64 StringStream::lastError() const { return (int64) m_lastError; } void StringStream::transfer( StringStream &strbuf ) { strbuf.m_b->incref(); m_b->decref(); m_b = strbuf.m_b; } bool StringStream::errorDescription( String &description ) const { switch( m_lastError ) { case 0: description = "None"; return true; case -1: description = "Out of Memory"; return true; } return false; } bool StringStream::close() { m_b->m_mtx.lock(); delete m_b->m_str; m_b->m_str = 0; m_b->m_mtx.unlock(); return false; } int32 StringStream::read( void *buffer, int32 size ) { m_b->m_mtx.lock(); if ( m_b->m_str == 0 ) { m_status = t_error; m_b->m_mtx.unlock(); return -1; } uint32 bsize = m_b->m_str->size(); if ( m_pos >= bsize ) { m_status = m_status | t_eof; m_b->m_mtx.unlock(); return 0; } int sret = size + m_pos < bsize ? size : bsize - m_pos; memcpy( buffer, m_b->m_str->getRawStorage() + m_pos, sret ); m_pos += sret; m_lastMoved = sret; m_b->m_mtx.unlock(); return sret; } bool StringStream::readString( String &target, uint32 size ) { uint32 chr; target.size(0); target.manipulator( &csh::handler_buffer ); while ( size > 0 && popBuffer(chr) ) { target += chr; --size; } if( size == 0 ) { return true; } m_b->m_mtx.lock(); if ( m_b->m_str == 0 ) { m_b->m_mtx.unlock(); m_status = t_error; return false; } String* src = m_b->m_str; int csize = src->manipulator()->charSize(); uint32 tglen = src->length(); uint32 start = (m_pos / csize); if ( start >= tglen ) { m_status = m_status | t_eof; m_b->m_mtx.unlock(); return false; } uint32 end = start + size; if ( end > tglen ) { end = tglen; } target = src->subString(start,end); m_pos = end * csize; m_b->m_mtx.unlock(); return true; } int32 StringStream::write( const void *buffer, int32 size ) { m_b->m_mtx.lock(); if ( m_b->m_str == 0 ) { m_b->m_mtx.unlock(); m_status = t_error; return -1; } // be sure there is enough space to write m_b->m_str->reserve(m_pos+size); // effectively write memcpy( m_b->m_str->getRawStorage() + m_pos, buffer, size ); m_pos += size; // are we writing at end? -- enlarge the string. if( m_pos > m_b->m_str->size() ) { m_b->m_str->size( m_pos ); } m_lastMoved = size; m_b->m_mtx.unlock(); return size; } bool StringStream::writeString( const String &source, uint32 begin, uint32 end ) { if( begin != 0 || ( end != -1 && end < source.length() ) ) { return StringStream::subWriteString( source.subString(begin, end) ); } else { return StringStream::subWriteString( source ); } } bool StringStream::subWriteString( const String &source ) { m_b->m_mtx.lock(); if ( m_b->m_str == 0 ) { m_b->m_mtx.unlock(); m_status = t_error; return false; } // writing at end? if ( m_pos >= m_b->m_str->size() ) { // easy *m_b->m_str += source; m_pos = m_b->m_str->size(); } else { // less easy int32 origCharSize = m_b->m_str->manipulator()->charSize(); int32 startPos = m_pos / origCharSize; if ( source.length() + startPos < m_b->m_str->length() ) { m_b->m_str->size(m_pos); *m_b->m_str += source; } else { // even less easy String part2 = m_b->m_str->subString( source.length() + startPos ); m_b->m_str->size(m_pos); *m_b->m_str += source + part2; } // the manipulator may have changed, so pos may have changed as well. m_pos = (startPos + source.length()) * m_b->m_str->manipulator()->charSize(); } m_b->m_mtx.unlock(); return true; } bool StringStream::put( uint32 chr ) { m_b->m_mtx.lock(); if ( m_b->m_str == 0 ) { m_b->m_mtx.unlock(); m_status = t_error; return false; } if( m_pos == m_b->m_str->size() ) { m_b->m_str->append(chr); // manipulator may have changed. m_pos = m_b->m_str->size(); } else { int32 pos = m_pos / m_b->m_str->manipulator()->charSize(); m_b->m_str->setCharAt( pos , chr ); // manipulator may have changed. m_pos = (pos+1) * m_b->m_str->manipulator()->charSize(); } m_b->m_mtx.unlock(); return true; } bool StringStream::get( uint32 &chr ) { if( popBuffer( chr ) ) return true; m_b->m_mtx.lock(); if ( m_b->m_str == 0 ) { m_b->m_mtx.unlock(); m_status = t_error; return false; } if( m_pos == m_b->m_str->size() ) { m_status = m_status | t_eof; m_b->m_mtx.unlock(); return false; } else { int32 cs = m_b->m_str->manipulator()->charSize(); chr = m_b->m_str->getCharAt( m_pos/cs ); // manipulator may have changed. m_pos += cs; } m_b->m_mtx.unlock(); return true; } int64 StringStream::seek( int64 pos, Stream::e_whence w ) { m_b->m_mtx.lock(); if ( m_b->m_str == 0 ) { m_b->m_mtx.unlock(); m_status = t_error; return -1; } switch( w ) { case Stream::ew_begin: m_pos = (int32) pos; break; case Stream::ew_cur: m_pos += (int32) pos; break; case Stream::ew_end: m_pos = (int32) (m_b->m_str->size() + (pos-1)); break; } if ( m_pos > m_b->m_str->size() ) { m_pos = m_b->m_str->size(); m_status = t_eof; } else if ( m_pos < 0 ) { m_pos = 0; m_status = t_none; } else m_status = t_none; pos = m_pos; m_b->m_mtx.unlock(); return pos; } int64 StringStream::tell() { m_b->m_mtx.lock(); if ( m_b->m_str == 0 ) { m_b->m_mtx.unlock(); m_status = t_error; return -1; } int32 pos = m_pos; m_b->m_mtx.unlock(); return pos; } bool StringStream::truncate( int64 pos ) { m_b->m_mtx.lock(); if ( m_b->m_str == 0 ) { m_b->m_mtx.unlock(); m_status = t_error; return false; } if ( pos <= 0 ) m_b->m_str->size( 0 ); else m_b->m_str->size( (int32) pos ); m_b->m_mtx.unlock(); return true; } int32 StringStream::readAvailable( int32, const Sys::SystemData * ) { return 1; } int32 StringStream::writeAvailable( int32, const Sys::SystemData * ) { return 1; } void StringStream::getString( String &target ) const { m_b->m_mtx.lock(); // as the string is always buffered, this operation is safe target = *m_b->m_str; m_b->m_mtx.unlock(); } bool StringStream::closeToString( String &target ) { m_b->m_mtx.lock(); if ( m_b->m_str == 0 ) { m_b->m_mtx.unlock(); return false; } // adopt original string buffer. target.adopt( (char*)m_b->m_str->getRawStorage(), m_b->m_str->size(), m_b->m_str->allocated() ); target.manipulator( (csh::Base*) m_b->m_str->manipulator() ); // apply correct manipulator // dispose the old string without disposing of its memory m_b->m_str->allocated( 0 ); m_b->m_str->setRawStorage( 0 ); delete m_b->m_str; m_b->m_str = 0; m_b->m_mtx.unlock(); return true; } byte * StringStream::closeToBuffer() { m_b->m_mtx.lock(); if ( m_b->m_str == 0 || m_b->m_str->size() == 0) { m_b->m_mtx.unlock(); return 0; } byte *retbuf = m_b->m_str->getRawStorage(); // dispose the old string without disposing of its memory m_b->m_str->allocated( 0 ); m_b->m_str->setRawStorage( 0 ); delete m_b->m_str; m_b->m_str = 0; m_b->m_mtx.unlock(); return retbuf; } StringStream *StringStream::clone() const { StringStream *sstr = new StringStream( *this ); return sstr; } } /* end of file_StringStream.cpp */ engine/strtable.cpp000066400000000000000000000141701176363201700146600ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: strtable.cpp String table implementation ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon Feb 14 2005 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file String table implementation */ #include #include #include #include #include #include #include namespace Falcon { StringTable::StringTable(): m_vector( &traits::t_stringptr_own() ), m_map( &traits::t_stringptr(), &traits::t_int() ), m_intMap( &traits::t_stringptr(), &traits::t_int() ), m_tableStorage(0), m_internatCount(0) {} StringTable::StringTable( const StringTable &other ): m_vector( &traits::t_stringptr_own() ), m_map( &traits::t_stringptr(), &traits::t_int() ), m_intMap( &traits::t_stringptr(), &traits::t_int() ), m_tableStorage(0), m_internatCount(0) { for( uint32 i = 0; i < other.m_vector.size(); i ++ ) { String* str = *(String**) other.m_vector.at(i); add( new String( *str ) ); } } StringTable::~StringTable() { if ( m_tableStorage != 0 ) memFree( m_tableStorage ); } int32 StringTable::add( String *str ) { fassert(str); if ( str->exported() ) { if ( int32 *pos = (int32 *) m_intMap.find( str ) ) { String *tableStr = (String *) m_vector.at( *pos ); if (str != tableStr ) { delete str; str = tableStr; } return *pos; } int32 id = m_vector.size(); m_vector.push( str ); m_intMap.insert( str, &id ); m_internatCount++; return id; } else { if ( int32 *pos = (int32 *) m_map.find( str ) ) { String *tableStr = (String *) m_vector.at( *pos ); if ( str != tableStr ) { delete str; str = tableStr; } return *pos; } int32 id = m_vector.size(); m_vector.push( str ); m_map.insert( str, &id ); return id; } } String *StringTable::find( const String &source ) const { MapIterator pos; if ( source.exported() ) { if ( m_intMap.find( &source, pos ) ) return *(String **) pos.currentKey(); } else { if ( m_map.find( &source, pos ) ) return *(String **) pos.currentKey(); } return 0; } int32 StringTable::findId( const String &source ) const { MapIterator pos; if ( source.exported() ) { if ( m_intMap.find( &source, pos ) ) return *(int32 *) pos.currentValue(); } else { if ( m_map.find( &source, pos ) ) return *(int32 *) pos.currentValue(); } return -1; } bool StringTable::save( Stream *out ) const { fassert(out); // write a minimal table if no table is needed. uint32 f; if ( size() == 0 ) { f = 0; out->write( &f, sizeof( f ) ); return true; } f = endianInt32( size() ); out->write( &f, sizeof( f ) ); // serialize all the strings for( uint32 i = 0; i < m_vector.size(); i++ ) { get(i)->serialize( out ); if( ! out->good() ) return false; } // align the stream to %4 == 0 f = 0; while( out->good() && out->tell() %4 != 0 ) { out->write( &f, 1 ); } return true; } bool StringTable::load( Stream *in ) { fassert(in); int32 size; in->read( &size, sizeof( size ) ); size = endianInt32( size ); for( int i = 0; i < size; i ++ ) { String *str = new String(); // deserialize and create self-destroying static strings. if ( ! str->deserialize( in, false ) ) { delete str; return false; } // consider the strings in the string table static. // the destructor will destroy the data anyhow, as we have an allocated data add( str ); } while( in->tell() %4 != 0 ) { in->read( &size, 1 ); } return true; } bool StringTable::skip( Stream *in ) const { fassert(in); int32 size; in->read( &size, sizeof( size ) ); size = endianInt32( size ); for( int i = 0; i < size; i ++ ) { String str; if ( ! str.deserialize( in ) ) { return false; } } while( in->tell() %4 != 0 ) { in->read( &size, 1 ); } return true; } bool StringTable::saveTemplate( Stream *out, const String &moduleName, const String &origLang ) const { fassert(out); // we shouldn't even have been called if we have no international strings. if( m_internatCount == 0 ) return true; // write the XML template // -- the caller should prepend the XML header if relevant String temp = "\n"; if ( ! out->writeString( temp ) ) return false; for ( int i = 0; i < size(); i++ ) { const String ¤t = *get(i); if( ! current.exported() ) continue; //====== an international string. String temp = "\n"; if ( ! out->writeString( temp ) ) return false; if ( ! out->writeString( current ) ) return false; if ( ! out->writeString( "\n\n\n" ) ) return false; } if ( ! out->writeString( "\n" ) ) return false; return true; } void StringTable::build( char **table, bool bInternational ) { fassert(table); char **ptr = table; while( *ptr != 0 ) { String *s = new String( *ptr ); s->exported( bInternational ); add( s ); ptr++; } } void StringTable::build( wchar_t **table, bool bInternational ) { fassert(table); wchar_t **ptr = table; while( *ptr != 0 ) { String *s = new String( *ptr ); s->exported( bInternational ); add( s ); ptr++; } } } /* end of strtable.cpp */ engine/symbol.cpp000066400000000000000000000473471176363201700143610ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: symbol.cpp Provide non-inlineizable symbol definitions ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2004-9-11 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #if FALCON_LITTLE_ENDIAN != 1 #include #include #endif namespace Falcon { void Symbol::clear() { switch( m_type ) { case tvar: delete m_value.v_prop; break; case tfunc: delete m_value.v_func; break; case textfunc: delete m_value.v_extfunc; break; case tclass: delete m_value.v_class; break; case tprop: delete m_value.v_prop; break; case tconst: delete m_value.v_prop; break; case timportalias: delete m_value.v_importalias; break; default: break; } m_type = tundef; } bool Symbol::fromClass( const String &find_name ) const { if( find_name == name() ) return true; if( ! isClass() ) return false; ClassDef *def = m_value.v_class; return def->inheritsFrom( find_name ); } bool Symbol::load( Stream *in ) { uint32 strid; setUndefined(); byte type; in->read( &type, sizeof( type ) ); byte exp; in->read( &exp, sizeof( exp ) ); m_flags = exp; uint16 pos; in->read( &pos, sizeof( pos ) ); m_itemPos = endianInt16( pos ); int32 line; in->read( &line, sizeof( line ) ); m_lineDecl = (int32) endianInt32( line ); // the id is not restored, as it is assigned by load order sequence. /*if ( ! m_name.deserialize( in, false ) ) { return false; }*/ switch( type_t( type ) ) { case tfunc: setFunction( new FuncDef( 0, 0 ) ); return getFuncDef()->load( m_module, in ); case tclass: setClass( new ClassDef ); return getClassDef()->load( m_module, in ); case tvar: setVar( new VarDef() ); return getVarDef()->load( m_module, in ); case tconst: setConst( new VarDef() ); return getVarDef()->load( m_module, in ); case tprop: setProp( new VarDef() ); return getVarDef()->load( m_module, in ); case tglobal: setGlobal(); break; case tinst: { in->read( &strid , sizeof( strid ) ); strid = endianInt32( strid ); Symbol *other = m_module->getSymbol( strid ); if ( other == 0 ) return false; setInstance( other ); } break; case timportalias: { String name, origMod; name.deserialize( in, false ); origMod.deserialize( in, false ); byte b; in->read( &b, 1 ); setImportAlias( name, origMod, b == 1 ); } break; case tparam: m_type = type_t( type ); break; case tundef: case tlocal: case tlocalundef: m_type = type_t( type ); break; default: // we don't expect anything else, included textfunc return false; } return true; } bool Symbol::save( Stream *out ) const { uint32 strid; byte type = (byte) m_type; byte flags = m_flags; int32 line = endianInt32( m_lineDecl ); uint16 pos = endianInt16( m_itemPos ); out->write( &type, sizeof( type ) ); out->write( &flags, sizeof( flags ) ); out->write( &pos, sizeof( pos ) ); out->write( &line, sizeof( line ) ); // the ID is not serialized, as it is determined by the module //m_name.serialize( out ); switch( m_type ) { case tfunc: getFuncDef()->save( m_module, out ); break; case tclass: getClassDef()->save( m_module, out ); break; case tvar: case tconst: case tprop: getVarDef()->save( m_module, out ); break; case tinst: strid = endianInt32( getInstance()->id() ); out->write( &strid, sizeof( strid ) ); break; case timportalias: getImportAlias()->name().serialize( out ); getImportAlias()->origModule().serialize( out ); { byte b = getImportAlias()->isOrigFileName() ? 1 : 0; out->write( &b, 1 ); } break; default: break; } return true; } Symbol* Symbol::addParam( const String ¶m ) { Symbol* tbc = this; if ( isClass() ) { tbc = getClassDef()->constructor(); if ( tbc == 0 ) return this; } switch( tbc->m_type ) { case tfunc: tbc->getFuncDef()->addParameter(m_module->addSymbol( param )); break; case textfunc: tbc->getExtFuncDef()->addParam(m_module->addSymbol( param )); break; default: return this; } return this; } //================================================================= // int32 ExtFuncDef::getParam( const String &name ) { if ( m_params == 0 ) return -1; Symbol *sym = m_params->findByName( name ); if ( sym == 0 ) return -1; return sym->itemId(); } /** Adds a function parameter with the specified ID. Consider using Symbol::addParam() instead (candy grammar). */ ExtFuncDef &ExtFuncDef::addParam( Symbol *param, int32 id ) { if ( m_params == 0 ) m_params = new SymbolTable; param->setParam(); param->itemId( id == -1 ? m_params->size() : id ); m_params->add( param ); return *this; } ExtFuncDef::~ExtFuncDef() { delete m_params; } //================================================================= // FuncDef::FuncDef( byte *code, uint32 codeSize ): m_code( code ), m_codeSize( codeSize ), m_params( 0 ), m_locals( 0 ), m_undefined( 0 ), m_onceItemId( NO_STATE ), m_basePC(0), m_attributes(0) { } FuncDef::~FuncDef() { memFree( m_code ); delete m_attributes; } Symbol *FuncDef::addParameter( Symbol *sym ) { sym->itemId( m_params++ ); sym->setParam(); m_symtab.add( sym ); return sym; } Symbol *FuncDef::addLocal( Symbol *sym ) { sym->itemId( m_locals++ ); sym->setLocal(); m_symtab.add( sym ); return sym; } Symbol *FuncDef::addUndefined( Symbol *sym ) { sym->itemId( m_undefined++ ); sym->setLocalUndef(); m_symtab.add( sym ); return sym; } bool FuncDef::save( const Module* mod, Stream *out ) const { uint16 locs = endianInt16( m_locals ); uint16 params = endianInt16( m_params ); out->write( &locs, sizeof( locs ) ); out->write( ¶ms, sizeof( params ) ); uint32 onceId = endianInt32(m_onceItemId); out->write( &onceId, sizeof( onceId ) ); uint32 basePC = endianInt32(m_basePC); out->write( &basePC, sizeof( basePC ) ); uint32 codeSize = endianInt32(m_codeSize); out->write( &codeSize, sizeof( codeSize ) ); if ( m_codeSize > 0 ) { // On little endian platforms, save an endianized copy of the code. #if FALCON_LITTLE_ENDIAN != 1 byte* ecode = (byte*) memAlloc( m_codeSize ); memcpy( ecode, m_code, m_codeSize ); PCODE::endianize( ecode, m_codeSize ); bool res = out->write( ecode, m_codeSize ); memFree( ecode ); #else bool res = out->write( m_code, m_codeSize ) == (int) m_codeSize; #endif if ( ! res ) return false; } if ( m_attributes != 0) { basePC = endianInt32(1); out->write( &basePC, sizeof( basePC ) ); m_attributes->save( mod, out ); } else { basePC = endianInt32(0); out->write( &basePC, sizeof( basePC ) ); } return m_symtab.save( out ); } void FuncDef::addAttrib( const String& name, VarDef* vd ) { if ( m_attributes == 0 ) m_attributes = new AttribMap; m_attributes->insertAttrib( name, vd ); } bool FuncDef::load( const Module *mod, Stream *in ) { uint16 loc; in->read( &loc, sizeof( loc ) ); m_locals = endianInt16( loc ); in->read( &loc, sizeof( loc ) ); m_params = endianInt16( loc ); uint32 onceItem = 0; in->read( &onceItem, sizeof( onceItem ) ); m_onceItemId = endianInt32( onceItem ); uint32 basePC = 0; in->read( &basePC, sizeof( basePC ) ); m_basePC = endianInt32( basePC ); int32 codeSize = 0; in->read( &codeSize, sizeof( codeSize ) ); m_codeSize = endianInt32( codeSize ); m_code = 0; // it's essential to check for errors now. if ( ! in->good() ) return false; if ( m_codeSize > 0 ) { m_code = (byte *) memAlloc( m_codeSize ); in->read( m_code, m_codeSize ); // it's essential to check for errors now. if ( ! in->good() ) return false; // de-endianize the code on little endian platforms. #if FALCON_LITTLE_ENDIAN != 1 PCODE::deendianize( m_code, m_codeSize ); #endif } in->read( &basePC, sizeof( basePC ) ); if( basePC != 0 ) { m_attributes = new AttribMap; if ( ! m_attributes->load( mod, in ) ) return false; } return m_symtab.load( mod, in ); } InheritDef::~InheritDef() { } bool InheritDef::save( Stream *out ) const { uint32 parentId = endianInt32( m_baseClass->id() ); out->write( &parentId, sizeof( parentId ) ); return true; } bool InheritDef::load( const Module *mod, Stream *in ) { uint32 parentId; in->read( &parentId , sizeof( parentId ) ); parentId = endianInt32( parentId ); m_baseClass = mod->getSymbol( parentId ); if ( m_baseClass == 0 ) return false; return true; } //================================================================= // ClassDef::ClassDef( ObjectFactory fact ): FuncDef( 0, 0 ), m_constructor( 0 ), m_properties( &traits::t_stringptr(), &traits::t_voidp() ), m_factory( fact ), m_metaclassFor( -1 ), m_bFinal( false ), m_states( &traits::t_stringptr(), &traits::t_voidp() ) {} ClassDef::ClassDef( Symbol *ext_ctor, ObjectFactory fact ): FuncDef( 0, 0 ), m_constructor( ext_ctor ), m_properties( &traits::t_stringptr(), &traits::t_voidp() ), m_factory( fact ), m_metaclassFor( -1 ), m_bFinal( false ), m_states( &traits::t_stringptr(), &traits::t_voidp() ) {} ClassDef::~ClassDef() { MapIterator iterp = m_properties.begin(); while( iterp.hasCurrent() ) { VarDef *vd = *(VarDef **) iterp.currentValue(); delete vd; iterp.next(); } ListElement *iteri = m_inheritance.begin(); while( iteri != 0 ) { InheritDef *def = (InheritDef *) iteri->data(); delete def; iteri = iteri->next(); } MapIterator iters = m_states.begin(); while( iters.hasCurrent() ) { StateDef *sd = *(StateDef **) iters.currentValue(); delete sd; iters.next(); } } bool ClassDef::checkCircularInheritance( const Symbol *child ) const { if ( child->isClass() && child->getClassDef() == this ) return true; ListElement *iteri = m_inheritance.begin(); while( iteri != 0 ) { InheritDef *def = (InheritDef *) iteri->data(); if( def->base() == child ) return true; if( def->base()->isClass() && def->base()->getClassDef()->checkCircularInheritance( child ) ) return true; iteri = iteri->next(); } return false; } void ClassDef::addProperty( const String *name, VarDef *definition ) { MapIterator iter; if ( m_properties.find( name, iter ) ) { VarDef **vd =(VarDef **) iter.currentValue(); delete *vd; *vd = definition; } else m_properties.insert( name, definition ); } VarDef *ClassDef::getProperty( const String *name ) const { VarDef **vd = (VarDef **) m_properties.find( name ); if ( vd != 0 ) return *vd; return 0; } bool ClassDef::addInheritance( InheritDef *parent_class ) { Symbol *parent = parent_class->base(); if ( getProperty( parent->name() ) != 0 ) return false; m_inheritance.pushBack( parent_class ); addProperty( &parent->name(), new VarDef( VarDef::t_base, parent ) ); return true; } bool ClassDef::inheritsFrom( const String &find_name ) const { ListElement *iter = m_inheritance.begin(); // perform a flat-first search ? -- not for now while( iter != 0 ) { const InheritDef *def = (const InheritDef *) iter->data(); const Symbol *i = def->base(); if( i->fromClass( find_name ) ) return true; iter = iter->next(); } return false; } bool ClassDef::addState( const String *state_name, StateDef* state ) { if( m_states.find( state_name ) != 0 ) return false; m_states.insert( state_name, state ); return true; } StateDef* ClassDef::addState( const String *state_name ) { if( m_states.find( state_name ) != 0 ) return 0; StateDef* state = new StateDef( state_name ); m_states.insert( state_name, state ); return state; } bool ClassDef::save( const Module* mod, Stream *out ) const { if ( ! FuncDef::save( mod, out ) ) return false; uint32 has; // Have we got a constructor? if( m_constructor == 0 ) { has = 0xffFFffFF; } else { has = endianInt32( (uint32)m_constructor->id() ); } out->write( &has , sizeof( has ) ); // now save the property table has = endianInt32(m_properties.size()); out->write( &has , sizeof( has ) ); MapIterator iter = m_properties.begin(); while( iter.hasCurrent() ) { const String *key = *(const String **) iter.currentKey(); const VarDef *value = *(const VarDef **) iter.currentValue(); key->serialize( out ); value->save( mod, out ); iter.next(); } // and finally the inheritance list has = endianInt32(m_inheritance.size()); out->write( &has , sizeof( has ) ); ListElement *iiter = m_inheritance.begin(); while( iiter != 0 ) { const InheritDef *def = (const InheritDef *) iiter->data(); if ( ! def->save( out ) ) return false; iiter = iiter->next(); } // and the state list has = endianInt32(m_states.size()); out->write( &has , sizeof( has ) ); MapIterator siter = m_states.begin(); while( siter.hasCurrent() ) { const String *key = *(const String **) siter.currentKey(); const StateDef *value = *(const StateDef **) siter.currentValue(); key->serialize( out ); value->save( out ); siter.next(); } return true; } bool ClassDef::load( const Module *mod, Stream *in ) { if ( ! FuncDef::load( mod, in ) ) return false; uint32 value; in->read( &value , sizeof( value ) ); value = endianInt32( value ); // Have we got a constructor? if( value == 0xffFFffFF ) { m_constructor = 0 ; } else { m_constructor = mod->getSymbol( value ); if ( m_constructor == 0 ) return false; } // now load the property table in->read( &value , sizeof( value ) ); value = endianInt32( value ); for( uint32 i = 0; i < value; i ++ ) { String key; if ( ! key.deserialize( in ) ) return false; VarDef *def = new VarDef(); // avoid memleak by early insertion m_properties.insert( new String( key ), def ); if ( ! def->load( mod, in ) ) return false; } // now load the inheritance table in->read( &value , sizeof( value ) ); value = endianInt32( value ); for( uint32 j = 0; j < value; j ++ ) { InheritDef *def = new InheritDef(); m_inheritance.pushBack( def ); if ( ! def->load( mod, in ) ) return false; } // and the state list in->read( &value , sizeof( value ) ); value = endianInt32( value ); for( uint32 i = 0; i < value; i ++ ) { uint32 id; in->read( &id , sizeof( id ) ); const String *name = mod->getString( endianInt32( id ) ); if ( name == 0 ) return false; StateDef *def = new StateDef( name ); // avoid memleak by early insertion m_states.insert( name, def ); if ( ! def->load( mod, in ) ) return false; } return true; } //=================================================================== // statedef StateDef::StateDef( const String* sname ): m_name( sname ), m_functions( &traits::t_stringptr_own(), &traits::t_voidp() ) { } bool StateDef::addFunction( const String& name, Symbol* func ) { if ( m_functions.find( &name ) != 0 ) return false; m_functions.insert( new String(name), func ); return true; } bool StateDef::save( Stream* out ) const { // List the functions uint32 size = endianInt32(m_functions.size()); out->write( &size , sizeof( size ) ); MapIterator siter = m_functions.begin(); while( siter.hasCurrent() ) { const String *key = *(const String **) siter.currentKey(); const Symbol *value = *(const Symbol **) siter.currentValue(); key->serialize( out ); size = endianInt32( value->id() ); out->write( &size, sizeof(size) ); siter.next(); } return out->good(); } bool StateDef::load( const Module *mod, Stream* in ) { // List the functions uint32 size; in->read( &size , sizeof( size ) ); size = endianInt32( size ); for( uint32 i = 0; i < size; ++i ) { int32 val; String name; if( ! name.deserialize( in, false ) ) return false; if( in->read( &val , sizeof( val ) ) != sizeof(val) ) return false; Symbol* func = mod->getSymbol(endianInt32( val )); if ( func == 0 ) return false; addFunction( name, func ); } return true; } //=================================================================== // vardef bool VarDef::save( const Module* mod, Stream *out ) const { int32 type = endianInt32((int32) m_val_type); out->write( &type , sizeof( type ) ); switch( m_val_type ) { case t_bool: { int32 val = m_value.val_bool ? 1: 0; out->write( &val , sizeof( val ) ); } break; case t_int: { int64 val = endianInt64( m_value.val_int ); out->write( &val , sizeof( val ) ); } break; case t_num: { numeric num = endianNum( m_value.val_num ); out->write( &num , sizeof( num ) ); } break; case t_string: { uint32 val = mod->stringTable().findId( *asString() ); out->write( &val , sizeof( val ) ); } break; case t_symbol: case t_reference: case t_base: { uint32 val = endianInt32( asSymbol()->id() ); out->write( &val , sizeof( val ) ); } default: return true; } return out->good(); } bool VarDef::load( const Module *mod, Stream *in ) { setNil(); int32 type; in->read( &type , sizeof( type ) ); m_val_type = (t_type) endianInt32(type); switch( m_val_type ) { case t_bool: { int32 val; in->read( &val , sizeof( val ) ); m_value.val_bool = val != 0; } break; case t_int: { int64 val; in->read( &val , sizeof( val ) ); m_value.val_int = endianInt64( val ); } break; case t_num: { numeric val; in->read( &val, sizeof( val ) ); m_value.val_num = endianNum( val ); } break; case t_string: { int32 val; in->read( &val , sizeof( val ) ); m_value.val_str = mod->getString(endianInt32( val )); if ( m_value.val_str == 0 ) return false; } break; case t_symbol: case t_reference: case t_base: { int32 val; in->read( &val , sizeof( val ) ); m_value.val_sym = mod->getSymbol(endianInt32( val )); if ( m_value.val_sym == 0 ) return false; } break; case t_reflective: // we can save only set/get reflectors setReflective( e_reflectSetGet, 0xFFFFFFFF ); break; default: break; } return true; } } /* end of symbol.cpp */ engine/symtab.cpp000066400000000000000000000064141176363201700143410ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: symtab.cpp Symbol table definition ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar ago 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include namespace Falcon { SymbolTable::SymbolTable(): m_map( &traits::t_stringptr(), &traits::t_voidp(), 19 ) { } void SymbolTable::exportUndefined() { MapIterator iter = m_map.begin(); while( iter.hasCurrent() ) { Symbol *sym = *(Symbol **) iter.currentValue(); if( ! sym->isUndefined() ) sym->exported( true ); iter.next(); } } bool SymbolTable::add( Symbol *sym ) { if( findByName( sym->name() ) != 0 ) return false; m_map.insert( &sym->name(), sym ); return true; } bool SymbolTable::remove( const String &name ) { return m_map.erase( &name ); } bool SymbolTable::save( Stream *out ) const { uint32 value; // save the symbol table size. value = endianInt32( size() ); out->write( &value, sizeof(value) ); MapIterator iter = m_map.begin(); while( iter.hasCurrent() ) { const Symbol *second = *(const Symbol **) iter.currentValue(); value = endianInt32( second->id() ); out->write( &value, sizeof(value) ); iter.next(); } return true; } bool SymbolTable::load( const Module *mod, Stream *in ) { // get the symtab type. int32 value; in->read( &value, sizeof(value) ); int32 final_size = endianInt32(value); // preallocate all the symbols; for ( int i = 0 ; i < final_size; i ++ ) { Symbol *sym; in->read( &value, sizeof(value) ); sym = mod->getSymbol( endianInt32(value) ); if ( sym == 0 ) { return false; } if ( sym->name().getRawStorage() == 0 ) { return false; } m_map.insert( &sym->name(), sym ); } return true; } SymbolVector::SymbolVector(): GenericVector( &traits::t_voidp() ) { } SymbolVector::~SymbolVector() { // free the symbols. for ( uint32 i = 0; i < size(); i ++ ) { delete symbolAt( i ); } } bool SymbolVector::save( Stream *out ) const { uint32 value = endianInt32(size()); out->write( &value, sizeof(value) ); for( uint32 iter = 0; iter < size(); iter++ ) { symbolAt( iter )->name().serialize( out ); } for( uint32 iter = 0; iter < size(); iter++ ) { if ( ! symbolAt( iter )->save( out ) ) return false; } return true; } bool SymbolVector::load( Module *owner, Stream *in ) { uint32 value; in->read( &value, sizeof(value) ); value = endianInt32( value ); resize( value ); for ( uint32 i = 0; i < value; i ++ ) { Symbol *sym = new Symbol(owner); sym->id( i ); set( sym, i ); if ( ! sym->name().deserialize( in ) ) return false; } for( uint32 iter = 0; iter < size(); iter++ ) { if ( ! symbolAt( iter )->load( in ) ) return false; } return true; } } /* end of symtab.cpp */ engine/syntree.cpp000066400000000000000000000567211176363201700145410ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: syntree.cpp Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include namespace Falcon { //================================================ // Array declaration // static void s_valueDeletor( void *data ) { Value *value = (Value *) data; delete value; } ArrayDecl::ArrayDecl(): List( s_valueDeletor ) {} ArrayDecl::ArrayDecl( const ArrayDecl &other ): List( s_valueDeletor ) { ListElement *element = other.begin(); while( element != 0 ) { Value *elem = (Value *) element->data(); pushBack( new Value( *elem ) ); element = element->next(); } } //================================================ // Dictionary declaration // static void s_valuePairDeletor( void *data ) { DictDecl::pair *p = (DictDecl::pair *) data; delete p->first; delete p->second; delete p; } DictDecl::DictDecl(): List( s_valuePairDeletor ) { } DictDecl::DictDecl( const DictDecl &other ): List( s_valuePairDeletor ) { ListElement *iter = other.begin(); while( iter != 0 ) { pair *elem = (pair *) iter->data(); Value *first = new Value( *elem->first ); Value *second = new Value( *elem->second ); pushBack( first, second ); iter = iter->next(); } } void DictDecl::pushBack( Value *first, Value *second ) { pair *p = new pair; p->first = first; p->second = second; List::pushBack( p ); } //================================================ // Range declaration // RangeDecl::RangeDecl( const RangeDecl &other ) { if ( other.m_rstart == 0 ) m_rstart = 0; else m_rstart = new Value( *other.m_rstart ); if ( other.m_rend == 0 ) m_rend = 0; else m_rend = new Value( *other.m_rend ); if ( other.m_step == 0 ) m_step = 0; else m_step = new Value( *other.m_step ); } RangeDecl::~RangeDecl() { delete m_rstart; delete m_rend; delete m_step; } //================================================ // Value // Value *Value::clone() const { return new Value( *this ); } bool Value::isEqualByValue( const Value &other ) const { if ( type() == other.type() ) { switch( type() ) { case Value::t_nil: return true; case Value::t_imm_integer: if( asInteger() == other.asInteger() ) return true; break; case Value::t_range_decl: if( other.asRange()->rangeStart() == asRange()->rangeStart() && ( (asRange()->isOpen() && other.asRange()->isOpen()) || (! asRange()->isOpen() && ! other.asRange()->isOpen() && other.asRange()->rangeEnd() == asRange()->rangeEnd()) ) ) return true; break; case Value::t_imm_num: if( asNumeric() == other.asNumeric() ) return true; break; case Value::t_imm_string: if( *asString() == *other.asString() ) return true; break; case Value::t_lbind: if( *asLBind() == *other.asLBind() ) return true; break; case Value::t_symbol: if( asSymbol()->id() == other.asSymbol()->id() ) return true; break; case Value::t_symdef: if( asSymdef() == other.asSymdef() ) return true; break; default: break; } } return false; } bool Value::less( const Value &other ) const { if ( type() == other.type() ) { switch( type() ) { case Value::t_nil: return true; case Value::t_imm_integer: if( asInteger() < other.asInteger() ) return true; break; case Value::t_range_decl: if( asRange()->rangeStart()->asInteger() < other.asRange()->rangeStart()->asInteger() ) return true; if ( ! asRange()->isOpen() && ! other.asRange()->isOpen() && asRange()->rangeEnd()->asInteger() < other.asRange()->rangeEnd()->asInteger() ) return true; break; case Value::t_imm_num: if( asNumeric() < other.asNumeric() ) return true; break; case Value::t_imm_string: if( *asString() < *other.asString() ) return true; break; case Value::t_lbind: if( *asLBind() < *other.asLBind() ) return true; break; case Value::t_symbol: if( asSymbol()->id() < other.asSymbol()->id() ) return true; break; case Value::t_symdef: if( *asSymdef() < *other.asSymdef() ) return true; break; default: break; } } else { return ((uint32)type()) < ((uint32)other.type()); } return false; } VarDef *Value::genVarDef() { VarDef *def; switch( type() ) { case Falcon::Value::t_nil: def = new Falcon::VarDef(); break; case Falcon::Value::t_imm_integer: def = new Falcon::VarDef( (int64) asInteger() ); break; case Falcon::Value::t_imm_num: def = new Falcon::VarDef( asNumeric() ); break; case Falcon::Value::t_imm_string: def = new Falcon::VarDef( asString() ); break; case Falcon::Value::t_imm_bool: def = new Falcon::VarDef( asBool() ); break; default: def = 0; // set a nil expression } return def; } Value::~Value() { switch( m_type ) { case t_byref: delete m_content.asRef; break; case t_array_decl: delete m_content.asArray; break; case t_dict_decl: delete m_content.asDict; break; case t_range_decl: delete m_content.asRange; break; case t_expression: delete m_content.asExpr; break; default: // In every other case, there is nothing to do as strings and symbols are held in the // module and are not to be disposed here. break; } } void Value::copy( const Value &other ) { m_type = other.m_type; switch( m_type ) { case t_byref: m_content.asRef = new Value( *other.m_content.asRef ); break; case t_array_decl: m_content.asArray = new ArrayDecl( *other.m_content.asArray ); break; case t_dict_decl: m_content.asDict = new DictDecl( *other.m_content.asDict ); break; case t_range_decl: m_content.asRange = new RangeDecl( *other.m_content.asRange ); break; case t_expression: m_content.asExpr = new Expression( *other.m_content.asExpr ); break; // In every other case, a flat copy is ok default: m_content = other.m_content; } } //================================================ // (owned) Value pointer traits for maps // void ValuePtrTraits::copy( void *targetZone, const void *sourceZone ) const { Value **target = (Value**) targetZone; *target = ((Value *) sourceZone); } int ValuePtrTraits::compare( const void *firstArea, const void *secondv ) const { Value *first = *(Value **) firstArea; Value *second = (Value *) secondv; if ( *first < *second ) return -1; else if ( *first == *second ) return 0; return 1; } void ValuePtrTraits::destroy( void *item ) const { Value *v = *(Value **) item; delete v; } bool ValuePtrTraits::owning() const { return true; } namespace traits { ValuePtrTraits &t_valueptr() { static ValuePtrTraits dt; return dt; } } //================================================ // Expression // Expression::Expression( const Expression &other ) { m_operator = other.m_operator; m_first = other.m_first == 0 ? 0 : new Value( *other.m_first ); m_second = other.m_second == 0 ? 0 : new Value( *other.m_second ); m_third = other.m_third == 0 ? 0 : new Value( *other.m_third ); } Expression::~Expression() { delete m_first; delete m_second; delete m_third; } bool Expression::isStandAlone() const { switch( m_operator ) { case t_eval: case t_funcall: case t_assign: case t_aadd: case t_asub: case t_amul: case t_adiv: case t_amod: case t_apow: case t_aband: case t_abor: case t_abxor: case t_ashl: case t_ashr: case t_pre_inc: case t_post_inc: case t_pre_dec: case t_post_dec: return true; // and are or are meaningful if their second element is an effective expression. // the first one needs not (i.e. may be a variable), and if the first one is // but the second one is not, then the second part does nothing, and we're // right in raising an error as there's something wrong. case t_and: case t_or: return m_second->isExpr() && m_second->asExpr()->isStandAlone(); default: return false; } } //================================================ // Statement list // StatementList::StatementList( const StatementList &other ) { Statement *elem = other.front(); while( elem != 0 ) { push_back( elem->clone() ); elem = (Statement *) elem->next(); } } StatementList::~StatementList() { Statement *elem = pop_back(); while( elem != 0 ) { delete elem; elem = pop_back(); } } SymbolList::SymbolList( const SymbolList &other ) { // we don't have the ownership of the symbol; // we just need to duplicate the things. ListElement *elem = other.begin(); while( elem != 0 ) { pushBack( elem->data() ); elem = elem->next(); } } //================================================ // Statement list // Statement::Statement( const Statement &other ): m_type( other.m_type ), m_line( other.m_line ) {} //================================================ // Statement NONE // StmtNone *StmtNone::clone() const { return new StmtNone( *this ); } // Global statement // StmtGlobal::StmtGlobal( const StmtGlobal &other ): Statement( other ), m_symbols( other.m_symbols ) {} StmtGlobal *StmtGlobal::clone() const { return new StmtGlobal( *this ); } // StmtUnref statement // StmtUnref::StmtUnref( const StmtUnref &other ): Statement( other ) { m_symbol = other.m_symbol == 0 ? 0 : other.m_symbol->clone(); } StmtUnref::~StmtUnref() { delete m_symbol; } StmtUnref *StmtUnref::clone() const { return new StmtUnref( *this ); } // StmtSelfPrint statement // StmtSelfPrint::StmtSelfPrint( const StmtSelfPrint &other ): Statement( other ), m_toPrint( new ArrayDecl( *other.m_toPrint ) ) {} StmtSelfPrint::~StmtSelfPrint() { delete m_toPrint; } StmtSelfPrint *StmtSelfPrint::clone() const { return new StmtSelfPrint( *this ); } // StmtExpression statement // StmtExpression::StmtExpression( const StmtExpression &other ): Statement( other ) { m_expr = other.m_expr == 0 ? 0 : other.m_expr->clone(); } StmtExpression::~StmtExpression() { delete m_expr; } StmtExpression *StmtExpression::clone() const { return new StmtExpression( *this ); } // StmtAutoexpr statement // StmtAutoexpr::StmtAutoexpr( const StmtAutoexpr &other ): StmtExpression( other ) {} StmtAutoexpr *StmtAutoexpr::clone() const { return new StmtAutoexpr( *this ); } // StmtFordot statement // StmtFordot::StmtFordot( const StmtFordot &other ): StmtExpression( other ) {} StmtFordot *StmtFordot::clone() const { return new StmtFordot( *this ); } // StmtReturn statement // StmtReturn *StmtReturn::clone() const { return new StmtReturn( *this ); } // StmtRaise statement // StmtRaise *StmtRaise::clone() const { return new StmtRaise( *this ); } // StmtLaunch statement // StmtLaunch *StmtLaunch::clone() const { return new StmtLaunch( *this ); } // StmtGive statement // StmtGive::StmtGive( const StmtGive &other ): Statement( other ) { m_objects = other.m_objects == 0 ? 0 : new ArrayDecl( *other.m_objects ); m_attribs = other.m_attribs == 0 ? 0 : new ArrayDecl( *other.m_attribs ); } StmtGive::~StmtGive() { delete m_objects; delete m_attribs; } StmtGive *StmtGive::clone() const { return new StmtGive( *this ); } // Loop ctrl // StmtLoopCtl::StmtLoopCtl( const StmtLoopCtl &other ): Statement( other ) { } StmtBreak *StmtBreak::clone() const { return new StmtBreak( *this ); } StmtContinue *StmtContinue::clone() const { return new StmtContinue( *this ); } // StmtBlock statement // StmtBlock::StmtBlock( const StmtBlock &other ): Statement( other ), m_list( other.m_list ) { } // StmtConditional statement // StmtConditional::StmtConditional( const StmtConditional &other ): StmtBlock( other ) { m_condition = other.m_condition == 0 ? 0 : new Value( *other.m_condition ); } StmtConditional::~StmtConditional() { delete m_condition; } // StmtLoop statement // StmtLoop *StmtLoop::clone() const { return new StmtLoop( *this ); } // StmtWhile statement // StmtWhile *StmtWhile::clone() const { return new StmtWhile( *this ); } // StmtElif statement // StmtElif *StmtElif::clone() const { return new StmtElif( *this ); } // Stmtif statement // StmtIf::StmtIf( const StmtIf &other ): StmtConditional( other ), m_else( other.m_else ), m_elseifs( other.m_elseifs ) { } StmtIf *StmtIf::clone() const { return new StmtIf( *this ); } // StmtForin statement // StmtForin::StmtForin( const StmtForin &other ): StmtBlock( other ), m_first( other.m_first ), m_last( other.m_last ), m_middle( other.m_middle ) { m_source = other.m_source == 0 ? 0 : new Value( * other.m_source ); m_dest = other.m_dest == 0 ? 0 : new ArrayDecl( * other.m_dest ); } StmtForin::~StmtForin() { delete m_source; delete m_dest; } StmtForin *StmtForin::clone() const { return new StmtForin( *this ); } // StmtCaseBlock statement // StmtCaseBlock *StmtCaseBlock::clone() const { return new StmtCaseBlock( *this ); } // StmtSwitch statement // StmtSwitch::StmtSwitch( uint32 line, Value *expr ): Statement( line, t_switch ), m_cases_int( &traits::t_valueptr(), &traits::t_int(), 19 ), m_cases_rng( &traits::t_valueptr(), &traits::t_int(), 19 ), m_cases_str( &traits::t_valueptr(), &traits::t_int(), 19 ), m_cases_obj( &traits::t_valueptr(), &traits::t_int(), 19 ), m_nilBlock( -1 ), m_cfr( expr ) {} StmtSwitch::StmtSwitch( const StmtSwitch &other ): Statement( other ), m_cases_int( other.m_cases_int ), m_cases_rng( other.m_cases_rng ), m_cases_str( other.m_cases_str ), m_cases_obj( other.m_cases_obj ), m_obj_list( other.m_obj_list ), m_blocks( other.m_blocks ), m_defaultBlock( other.m_defaultBlock ), m_nilBlock( other.m_nilBlock ) { m_cfr = other.m_cfr == 0 ? 0 : new Value( *other.m_cfr ); } StmtSwitch::~StmtSwitch() { delete m_cfr; } bool StmtSwitch::addIntCase( Value *itm ) { if ( m_cases_int.find( itm ) != 0 ) { return false; } // check against duplicated cases in ranges int32 val = (int32) itm->asInteger(); MapIterator iter = m_cases_rng.begin(); while( iter.hasCurrent() ) { Value *rng = *(Value **) iter.currentKey(); Value *begin = rng->asRange()->rangeStart(); if ( rng->asRange()->isOpen() ) { if ( val >= (int32) begin->asInteger() ) return false; } Value *end = rng->asRange()->rangeEnd(); if ( val >= (int32) begin->asInteger() && val <= (int32) end->asInteger()) return false; iter.next(); } uint32 temp = m_blocks.size(); m_cases_int.insert( itm, &temp ); return true; } bool StmtSwitch::addStringCase( Value *itm ) { if ( m_cases_str.find( itm ) != 0 ) { return false; } uint32 temp = m_blocks.size(); m_cases_str.insert( itm, &temp ); return true; } bool StmtSwitch::addRangeCase( Value *itm ) { if ( m_cases_rng.find( itm ) != 0 ) { return false; } // todo check int and ranges // check against duplicated cases in ranges bool isOpen = itm->asRange()->isOpen(); int32 start = (int32) itm->asRange()->rangeStart()->asInteger(); int32 end = isOpen ? 0 : (int32) itm->asRange()->rangeEnd()->asInteger(); // only positive range intervals are meaningful if ( ! isOpen && end < start ) { itm->asRange()->rangeStart()->setInteger( end ); itm->asRange()->rangeEnd()->setInteger( start ); int32 t = start; start = end; end = t; } // check integers MapIterator iter = m_cases_int.begin(); while( iter.hasCurrent() ) { Value *first = *(Value **) iter.currentKey(); int32 val = (int32) first->asInteger(); if ( (isOpen && val >= start) || (!isOpen && val >= start && val <= end) ) { return false; } iter.next(); } // then check against ranges iter = m_cases_rng.begin(); while( iter.hasCurrent() ) { Value *rng = *(Value **) iter.currentKey(); // as all the inserted ranges have been forced to positive interval // there's no need to do it here again. bool other_isOpen = rng->asRange()->isOpen(); int other_start = (int) (rng->asRange()->rangeStart()->asInteger()); int other_end = (int) (other_isOpen ? 0 : rng->asRange()->rangeEnd()->asInteger()); if ( other_isOpen ) { if ( isOpen ) return false; if ( end >= other_start ) return false; } else { if ( isOpen ) { return false; } else { if ( (start <= other_start && other_start <= end) || (start <= other_end && other_end <= end) || (other_start <= start && start <= other_end) || (other_start <= end && end <= other_end) ) return false; } } iter.next(); } // fine. uint32 temp = m_blocks.size(); m_cases_rng.insert( itm, &temp ); return true; } bool StmtSwitch::addSymbolCase( Value *itm ) { if ( m_cases_obj.find( itm ) != 0 ) { return false; } uint32 temp = m_blocks.size(); m_cases_obj.insert( itm, &temp ); m_obj_list.pushBack( itm ); return true; } void StmtSwitch::addBlock( StmtCaseBlock *sl ) { m_blocks.push_back( sl ); } StmtSwitch *StmtSwitch::clone() const { return new StmtSwitch( *this ); } // StmtSelect statement // StmtSelect::StmtSelect( uint32 line, Value *expr ): StmtSwitch( line, expr ) { m_type = t_select; } StmtSelect::StmtSelect( const StmtSelect &other ): StmtSwitch( other ) { m_type = t_select; } StmtSelect *StmtSelect::clone() const { return new StmtSelect( *this ); } // StmtCatchBlock statement // StmtCatchBlock::StmtCatchBlock( const StmtCatchBlock &other ): StmtBlock( other ) { m_into = other.m_into == 0 ? 0 : new Value( *other.m_into ); } StmtCatchBlock::~StmtCatchBlock() { delete m_into; } StmtCatchBlock *StmtCatchBlock::clone() const { return new StmtCatchBlock( *this ); } // StmtCatchBlock statement // StmtTry::StmtTry( uint32 line ): StmtBlock( line, t_try ), m_cases_int( &traits::t_valueptr(), &traits::t_int(), 13 ), m_cases_sym( &traits::t_valueptr(), &traits::t_int(), 13 ), m_into_values( s_valueDeletor ), m_default(0) {} StmtTry::StmtTry( const StmtTry &other ): StmtBlock( other ), m_cases_int( other.m_cases_int ), m_cases_sym( other.m_cases_sym ), m_sym_list( other.m_sym_list ), m_handlers( other.m_handlers ), m_into_values( s_valueDeletor ) { m_default = other.m_default == 0 ? 0 : other.m_default; ListElement *elem = other.m_into_values.begin(); while( elem != 0 ) { Value *into = (Value *) elem->data(); m_into_values.pushBack( new Value( *into ) ); elem = elem->next(); } } StmtTry::~StmtTry() { delete m_default; } void StmtTry::defaultHandler( StmtCatchBlock *block ) { delete m_default; m_default = block; } void StmtTry::addHandler( StmtCatchBlock *sl ) { m_handlers.push_back( sl ); } bool StmtTry::addIntCase( Value *itm ) { if ( m_cases_int.find( itm ) != 0 ) { return false; } uint32 temp = m_handlers.size(); m_cases_int.insert( itm, &temp ); return true; } bool StmtTry::addSymbolCase( Value *itm ) { if ( m_cases_sym.find( itm ) != 0 ) { return false; } uint32 temp = m_handlers.size(); m_cases_sym.insert( itm, &temp ); m_sym_list.pushBack( itm ); return true; } StmtTry *StmtTry::clone() const { return new StmtTry( *this ); } // StmtCallable statement // StmtCallable::~StmtCallable() {} // StmtClass statement // StmtClass::StmtClass( const StmtClass &other ): StmtCallable( other ), m_initGiven( other.m_initGiven ), m_states( other.m_states ) { if ( other.m_ctor == 0 ) { m_ctor = 0; m_bDeleteCtor = false; } else { new StmtFunction( *other.m_ctor ); m_bDeleteCtor = true; } m_ctor->setConstructorFor( this ); if( other.m_initState != 0 ) m_initState = other.m_initState->clone(); else m_initState = 0; } StmtClass::~StmtClass() { if( m_bDeleteCtor ) delete m_ctor; } bool StmtClass::addState( StmtState* state ) { Statement* f = m_states.front(); while( f != 0 ) { StmtState* fs = static_cast( f ); if ( * fs->name() == *state->name() ) return false; f = static_cast( f->next() ); } m_states.push_back( state ); return true; } StmtClass *StmtClass::clone() const { return new StmtClass( *this ); } //=================================================== // State statement // StmtState::StmtState( const String* name, StmtClass* owner ): Statement( t_state ), m_name( name ), m_owner( owner ), m_funcs( &traits::t_stringptr(), &traits::t_voidp(), 19 ) { m_stateDef = new StateDef( name ); } StmtState::~StmtState() { } StmtState::StmtState( const StmtState& other ): Statement( other ), m_name( other.m_name ), m_owner( other.m_owner ), m_funcs( other.m_funcs ) { m_stateDef = new StateDef( other.m_name ); } StmtState* StmtState::clone() const { return new StmtState( *this ); } bool StmtState::addFunction( const String* name, Symbol* func ) { if( m_funcs.find( name ) != 0 ) return false; m_funcs.insert( name, func ); return true; } //=================================================== // StmtFunction statement // StmtFunction::StmtFunction( const StmtFunction &other ): StmtCallable( other ), m_lambda_id( other.m_lambda_id ), m_staticBlock( other.m_staticBlock ), m_statements( other.m_statements ), m_ctor_for( 0 ) // ctor for always zero; it's eventually set by the class copy constructor { } StmtFunction *StmtFunction::clone() const { return new StmtFunction( *this ); } // StmtFunction statement // StmtVarDef::StmtVarDef( const StmtVarDef &other ): Statement( other ), m_name( other.m_name ) // the string is in the module { m_value = other.m_value == 0 ? 0 : new Value( *other.m_value ); } StmtVarDef::~StmtVarDef() { delete m_value; } StmtVarDef *StmtVarDef::clone() const { return new StmtVarDef( *this ); } // StmtFunction statement // SourceTree::SourceTree( const SourceTree &other ): m_statements( other.m_statements ), m_functions( other.m_functions ), m_classes( other.m_classes ), m_exportAll( other.m_exportAll ) { } SourceTree *SourceTree::clone() const { return new SourceTree( *this ); } } /* end of syntree.cpp */ engine/sys_solaris.cpp000066400000000000000000000106271176363201700154150ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: sys_solaris.cpp System specific (UNIX - solaris) support for VM. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar nov 9 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Short description */ extern "C" { extern char **environ; } #define _REENTRANT #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Falcon { namespace Sys { void _dummy_ctrl_c_handler() { } numeric _seconds() { struct timeval time; gettimeofday( &time, 0 ); return time.tv_sec + (time.tv_usec / 1000000.0 ); } numeric _localSeconds() { struct timeval current; struct tm date_local, date_gm; time_t t; gettimeofday( ¤t, 0 ); time( &t ); localtime_r( &t, &date_local ); gmtime_r( &t, &date_gm ); time_t leap = mktime( &date_local) - mktime( &date_gm ); return leap + current.tv_sec + (current.tv_usec / 1000000.0 ); } uint32 _milliseconds() { #if POSIX_TIMERS > 0 struct timespec time; clock_gettime( CLOCK_REALTIME, &time ); return time.tv_sec * 1000 + time.tv_nsec / 1000000; #else struct timeval time; gettimeofday( &time, 0 ); return time.tv_sec * 1000 + time.tv_usec / 1000; #endif } int64 _epoch() { return (int64) time(0); } void _tempName( String &res ) { static bool first = true; const char *temp_dir; char *fname; struct stat st; if( first ) { first = false; time_t t; srand( (unsigned int ) time( &t ) ); } temp_dir = getenv( "TMP" ); if ( temp_dir == 0 ) temp_dir = getenv( "TMPDIR" ); if ( temp_dir == 0 ) { temp_dir = DEFAULT_TEMP_DIR; } if ( stat( temp_dir, &st ) == -1 || ! S_ISDIR( st.st_mode ) ) { temp_dir = "."; } res = temp_dir; res.append( "/falcon_tmp_" ); res.writeNumber( (int64) getpid() ); res.append("_"); res.writeNumber( (int64) rand() ); res.bufferize(); // force buffering } bool _describeError( int64 eid, String &target ) { const char *error = strerror( eid ); if( error != 0 ) { target.bufferize( error ); return true; } return false; } int64 _lastError() { return (int64) errno; } bool _getEnv( const String &var, String &result ) { AutoCString convertBuf( var ); char *value = getenv( convertBuf.c_str() ); if ( value != 0 && value[0] != '\0' ) { TranscodeFromString( value, "utf-8", result ); return true; } return false; } bool _setEnv( const String &var, const String &value ) { // in unix system, we have at worst UTF-8 var names. String tmp = var + "=" + value; AutoCString env( tmp ); // Solaris putenv requires that memory is permanently stored in the system. char *buffer = (char*) malloc( env.length() + 1 ); memcpy( buffer, env.c_str(), env.length() ); buffer[ env.length() ] = '\0'; return (putenv( buffer ) == 0); } bool _unsetEnv( const String &var ) { // in unix system, we have at worst UTF-8 var names. String sVarValue = var + "="; AutoCString env( sVarValue ); // Solaris putenv requires that memory is permanently stored in the system. char *buffer = (char*) malloc( env.length() + 1 ); memcpy( buffer, env.c_str(), env.length() ); buffer[ env.length() ] = '\0'; return (putenv( buffer ) == 0); } void _enumerateEnvironment( EnvStringCallback cb, void* cbData ) { // do we know which encoding are we using? String enc; bool bTranscode = GetSystemEncoding( enc ) && enc != "C"; char** env = environ; while( *env != 0 ) { String temp; if( bTranscode ) { if( ! TranscodeFromString( *env, enc, temp ) ) { bTranscode = false; temp = *env; } } else temp = *env; uint32 pos; if ( (pos = temp.find( "=" )) ) { cb( temp.subString(0,pos), temp.subString(pos+1), cbData ); } ++env; } } int64 _getpid() { return (int64) getpid(); } } } /* end of sys_solaris.cpp */ engine/sys_unix.cpp000066400000000000000000000112611176363201700147170ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: sys_unix.cpp System specific (unix) support for VM. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar nov 9 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Short description */ #ifdef __APPLE__ #include #define environ (*_NSGetEnviron()) #else extern "C" { extern char **environ; } #endif #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Falcon { namespace Sys { void _dummy_ctrl_c_handler() { } numeric _seconds() { struct timeval time; gettimeofday( &time, 0 ); return time.tv_sec + (time.tv_usec / 1000000.0 ); } numeric _localSeconds() { struct timeval current; struct tm date_local, date_gm; time_t t; gettimeofday( ¤t, 0 ); time( &t ); localtime_r( &t, &date_local ); gmtime_r( &t, &date_gm ); time_t leap = mktime( &date_local) - mktime( &date_gm ); return leap + current.tv_sec + (current.tv_usec / 1000000.0 ); } uint32 _milliseconds() { #if POSIX_TIMERS > 0 struct timespec time; clock_gettime( CLOCK_REALTIME, &time ); return time.tv_sec * 1000 + time.tv_nsec / 1000000; #else struct timeval time; gettimeofday( &time, 0 ); return time.tv_sec * 1000 + time.tv_usec / 1000; #endif } int64 _epoch() { return (int64) time(0); } void _tempName( String &res ) { static bool first = true; const char *temp_dir; struct stat st; if( first ) { first = false; time_t t; srand( (unsigned int ) time( &t ) ); } temp_dir = getenv( "TMP" ); if ( temp_dir == 0 ) temp_dir = getenv( "TMPDIR" ); if ( temp_dir == 0 ) { temp_dir = DEFAULT_TEMP_DIR; } if ( stat( temp_dir, &st ) == -1 || ! S_ISDIR( st.st_mode ) ) { temp_dir = "."; } res = temp_dir; res.append( "/falcon_tmp_" ); res.writeNumber( (int64) getpid() ); res.append("_"); res.writeNumber( (int64) rand() ); res.bufferize(); // force buffering } bool _describeError( int64 eid, String &target ) { const char *error = strerror( eid ); if( error != 0 ) { target.bufferize( error ); return true; } return false; } int64 _lastError() { return (int64) errno; } bool _getEnv( const String &var, String &result ) { static char convertBuf[512]; // system var names larger than 512 are crazy. // in unix system, we have at worst UTF-8 var names. if ( var.toCString( convertBuf, 512 ) >= 0 ) { char *value = getenv( convertBuf ); if ( value != 0 ) { TranscodeFromString( value, "utf-8", result ); return true; } } return false; } bool _setEnv( const String &var, const String &value ) { // in unix system, we have at worst UTF-8 var names. uint32 varLen = var.length() * 4 + 2; uint32 valueLen = value.length() * 4 + 2; char *varBuf = (char *) memAlloc( varLen ); char *valueBuf = (char *) memAlloc( valueLen ); var.toCString( varBuf, varLen ); value.toCString( valueBuf, valueLen ); bool result = setenv( varBuf, valueBuf, 1 ) == 0; memFree( varBuf ); memFree( valueBuf ); return result; } bool _unsetEnv( const String &var ) { // in unix system, we have at worst UTF-8 var names. uint32 varLen = var.length() * 4 + 2; char *varBuf = (char *) memAlloc( varLen ); var.toCString( varBuf, varLen ); /* currently unsetenv does not return in darwin; we need sometime to find a solution bool result = unsetenv( varBuf ) == 0; */ bool result = true; unsetenv( varBuf ); memFree( varBuf ); return result; } void _enumerateEnvironment( EnvStringCallback cb, void* cbData ) { // do we know which encoding are we using? String enc; bool bTranscode = GetSystemEncoding( enc ) && enc != "C"; char** env = environ; while( *env != 0 ) { String temp; if( bTranscode ) { if( ! TranscodeFromString( *env, enc, temp ) ) { bTranscode = false; temp = *env; } } else temp = *env; uint32 pos; if ( (pos = temp.find( "=" )) != String::npos ) { cb( temp.subString(0,pos), temp.subString(pos+1), cbData ); } ++env; } } int64 _getpid() { return (int64) getpid(); } } } /* end of sys_unix.cpp */ engine/sys_win.cpp000066400000000000000000000174701176363201700145410ustar00rootroot00000000000000/* FALCON - Falcon advanced simple text evaluator. FILE: sys_win.cpp System specific (win) support for VM and other Falcon parts. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar nov 9 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifdef __MINGW32__ #define _time64 time #endif /** \file System level support for basic and common operatios. */ #define SECS_IN_HOUR 3600L #define SECS_IN_DAY (24*SECS_IN_HOUR) #define SECS_IN_YEAR (365 * SECS_IN_DAY) #include #include #include #include #include #include #include #ifndef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #endif namespace Falcon { namespace Sys { static BOOL CtrlHandler(DWORD dwCtrlType) { if( CTRL_C_EVENT == dwCtrlType) { // just terminate the process. exit(0); } return FALSE; } void _dummy_ctrl_c_handler() { SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE); } numeric SYSTEMTIME_TO_SECONDS( const SYSTEMTIME &st ) { int secsAt[12]; secsAt[0 ] = 0; secsAt[1 ] = 31 * SECS_IN_DAY; secsAt[2 ] = secsAt[1 ] + 28 * SECS_IN_DAY; if( st.wYear % 4 == 0 ) secsAt[2 ] += SECS_IN_DAY; secsAt[3 ] = secsAt[2 ] + 31 * SECS_IN_DAY; secsAt[4 ] = secsAt[3 ] + 30 * SECS_IN_DAY; secsAt[5 ] = secsAt[4 ] + 31 * SECS_IN_DAY; secsAt[6 ] = secsAt[5 ] + 30 * SECS_IN_DAY; secsAt[7 ] = secsAt[6 ] + 31 * SECS_IN_DAY; secsAt[8 ] = secsAt[7 ] + 31 * SECS_IN_DAY; secsAt[9 ] = secsAt[8 ] + 30 * SECS_IN_DAY; secsAt[10] = secsAt[9 ] + 31 * SECS_IN_DAY; secsAt[11] = secsAt[10] + 31 * SECS_IN_DAY; if( st.wYear < 1970 ) { int leapSeconds = (1969 - st.wYear)/4 * SECS_IN_DAY; return (1969 - st.wYear) * SECS_IN_YEAR + secsAt[st.wMonth-1] + st.wDay * SECS_IN_DAY + st.wHour * SECS_IN_HOUR + st.wMinute * 60 + st.wSecond + leapSeconds + (st.wMilliseconds / 1000.0); } else { // good also if wYear is 1970: /4 will neutralize it. int leapSeconds = ((st.wYear-1)-1970)/4 * SECS_IN_DAY; return (st.wYear-1970 ) * SECS_IN_YEAR + secsAt[st.wMonth-1] + st.wDay * SECS_IN_DAY + st.wHour * SECS_IN_HOUR + st.wMinute * 60 + st.wSecond + leapSeconds + (st.wMilliseconds / 1000.0); } } void _sleep( numeric time ) { Sleep( long( time * 1000 ) ); } numeric _seconds() { SYSTEMTIME st; GetSystemTime( &st ); return SYSTEMTIME_TO_SECONDS( st ); } numeric _localSeconds() { SYSTEMTIME st; GetLocalTime( &st ); return SYSTEMTIME_TO_SECONDS( st ); } uint32 _milliseconds() { return (uint32) GetTickCount(); } int64 _epoch() { return (int64) _time64(0); } void _tempName( String &res ) { String temp_dir; res.bufferize(); res.size(0); if ( ! Sys::_getEnv( "TEMP", temp_dir ) ) if ( ! Sys::_getEnv( "TMP", temp_dir ) ) temp_dir = "C:\\TEMP"; int tempLen = temp_dir.length() * sizeof( wchar_t ) + sizeof( wchar_t ); wchar_t *wct = (wchar_t *) memAlloc( tempLen ); temp_dir.toWideString( wct, tempLen ); DWORD attribs = GetFileAttributesW( wct ); if( attribs == INVALID_FILE_ATTRIBUTES && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ) { char *bufname_c = (char *) wct; if( temp_dir.toCString( bufname_c, tempLen ) > 0 ) attribs = GetFileAttributesA( bufname_c ); } if ( GetLastError() != 0 || ((attribs & FILE_ATTRIBUTE_DIRECTORY) == 0) ) { temp_dir = "."; } memFree( wct ); res += temp_dir; res += "\\"; res += "falcon_tmp_"; res.writeNumberHex( GetCurrentProcessId() ); /*res += "_"; res.writeNumberHex( (uint32) rand() );*/ } int64 _lastError() { return (int64) GetLastError(); } bool _describeError( int64 lastError, String &dest ) { LPVOID lpMsgBuf; DWORD error = (DWORD) lastError; DWORD res = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, error, LANG_USER_DEFAULT, (LPTSTR) &lpMsgBuf, 0, NULL ); if ( res == 0 ) { dest = "Impossible to retreive error description"; dest.bufferize(); return false; } else { dest = (char *) lpMsgBuf; dest.bufferize(); } LocalFree(lpMsgBuf); return true; } bool _getEnv( const String &var, String &result ) { #if _MSC_VER < 1400 char convertBuf[1024]; if ( var.toCString( convertBuf, 1024 ) ) { char *value = getenv( convertBuf ); if( value != 0 ) { result.bufferize( value ); return true; } } #else wchar_t convertBuf[512]; // system var names larger than 512 are crazy. if ( var.toWideString( convertBuf, 512 ) ) { wchar_t *value = (wchar_t *) memAlloc( 512 * sizeof( wchar_t ) ); size_t retSize; errno_t error = _wgetenv_s( &retSize, value, 512, convertBuf ); if ( error == ERANGE ) { memFree( value ); value = (wchar_t *) memAlloc( retSize * sizeof( wchar_t ) ); error = _wgetenv_s( &retSize, value, retSize, convertBuf ); } if ( error != EINVAL && retSize != 0 ) { result.bufferize( value ); memFree( value ); return true; } memFree( value ); } #endif return false; } bool _setEnv( const String &var, const String &value ) { #if _MSC_VER < 1400 String temp = var + "=" + value; uint32 tempLen = temp.length() * 4 + 4; char *tempBuf = (char*) memAlloc( tempLen ); temp.toCString( tempBuf, tempLen ); putenv( tempBuf ); memFree( tempBuf ); return true; #else uint32 varLen = var.length() * sizeof(wchar_t) + sizeof(wchar_t); uint32 valueLen = value.length() * sizeof(wchar_t) + sizeof(wchar_t); wchar_t *varBuf = (wchar_t *) memAlloc( varLen ); wchar_t *valueBuf = (wchar_t *) memAlloc( valueLen ); var.toWideString( varBuf, varLen ); value.toWideString( valueBuf, valueLen ); bool result = _wputenv_s( varBuf, valueBuf ) == 0; memFree( varBuf ); memFree( valueBuf ); return result; #endif } bool _unsetEnv( const String &var ) { #if _MSC_VER < 1400 String temp = var + "="; uint32 tempLen = temp.length() * 4 + 4; char *tempBuf = (char*) memAlloc( tempLen ); temp.toCString( tempBuf, tempLen ); putenv( tempBuf ); memFree( tempBuf ); return true; #else uint32 varLen = var.length() * sizeof(wchar_t) + sizeof(wchar_t); wchar_t *varBuf = (wchar_t *) memAlloc( varLen ); var.toWideString( varBuf, varLen ); bool result = _wputenv_s( varBuf, L"" ) == 0; memFree( varBuf ); return result; #endif } void _enumerateEnvironment( EnvStringCallback cb, void* cbData ) { #if _MSC_VER < 1400 char* envstr = GetEnvironmentStringsA(); #else wchar_t* envstr = GetEnvironmentStringsW(); #endif uint32 pos = 0; uint32 posn = 0; while( envstr[posn] != 0 ) { // not an error, we check it twice. uint32 poseq = 0; while( envstr[posn] != 0 ) { if( poseq == 0 && envstr[posn] == '=' ) poseq = posn; ++posn; } // did we find a variable? if( poseq != 0 ) { String key, value; key.adopt( envstr + pos, poseq-pos, 0 ); value.adopt( envstr + poseq+1, posn-poseq-1, 0 ); key.bufferize(); value.bufferize(); cb( key, value, cbData ); } // advancing to the next string; if the first char is zero, we exit ++posn; pos = posn; } #if _MSC_VER < 1400 FreeEnvironmentStringsA( envstr ); #else FreeEnvironmentStringsW( envstr ); #endif } int64 _getpid() { return (int64) GetCurrentProcessId(); } } } /* end of sys_win.cpp */ engine/time_sys_unix.cpp000066400000000000000000000130731176363201700157400ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: time_sys_unix.cpp Unix system implementation for time related service ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun mar 6 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Unix system implementation for time related service */ #define _REENTRANT #include #include #include namespace Falcon { namespace Sys { namespace Time { static TimeZone s_cached_timezone = tz_local; // which is also 0 void currentTime( ::Falcon::TimeStamp &ts ) { struct timeval current; struct tm date; time_t t; time( &t ); localtime_r( &t, &date ); ts.m_year = date.tm_year + 1900; ts.m_month = date.tm_mon+1; ts.m_day = date.tm_mday; ts.m_hour = date.tm_hour; ts.m_minute = date.tm_min; ts.m_second = date.tm_sec; // todo: collect day of year and weekday ts.m_timezone = tz_local; gettimeofday( ¤t, 0 ); ts.m_msec = current.tv_usec / 1000; } TimeZone getLocalTimeZone() { // this function is not reentrant, but it's ok, as // the worst thing that may happen in MT or multiprocess // is double calculation of the cached value. // infer timezone by checking gmtime/localtime if( s_cached_timezone == tz_local ) { struct tm gmt; struct tm loc; time_t now; time( &now ); localtime_r( &now, &loc ); gmtime_r( &now, &gmt ); // great, let's determine the time shift int64 tgmt = mktime( &gmt ); int64 tloc = mktime( &loc ); long shift = (long) (tloc - tgmt); if ( loc.tm_isdst ) { shift += 3600; } long hours = shift / 3600; long minutes = (shift/60) % 60; // and now, let's see if we have one of our zones: switch( hours ) { case 1: s_cached_timezone = tz_UTC_E_1; break; case 2: s_cached_timezone = tz_UTC_E_2; break; case 3: s_cached_timezone = tz_UTC_E_3; break; case 4: s_cached_timezone = tz_UTC_E_4; break; case 5: s_cached_timezone = tz_UTC_E_5; break; case 6: s_cached_timezone = tz_UTC_E_6; break; case 7: s_cached_timezone = tz_UTC_E_7; break; case 8: s_cached_timezone = tz_UTC_E_8; break; case 9: if( minutes == 30 ) s_cached_timezone = tz_ACST; else s_cached_timezone = tz_UTC_E_9; break; case 10: if( minutes == 30 ) s_cached_timezone = tz_ACDT; else s_cached_timezone = tz_UTC_E_10; break; case 11: if( minutes == 30 ) s_cached_timezone = tz_NFT; else s_cached_timezone = tz_UTC_E_11; break; case 12: s_cached_timezone = tz_UTC_E_11; break; case -1: s_cached_timezone = tz_UTC_W_1; case -2: if( minutes == 30 ) s_cached_timezone = tz_HAT; else s_cached_timezone = tz_UTC_W_2; break; case -3: if( minutes == 30 ) s_cached_timezone = tz_NST; else s_cached_timezone = tz_UTC_W_3; break; case -4: s_cached_timezone = tz_UTC_W_4; break; case -5: s_cached_timezone = tz_UTC_W_5; break; case -6: s_cached_timezone = tz_UTC_W_6; break; case -7: s_cached_timezone = tz_UTC_W_7; break; case -8: s_cached_timezone = tz_UTC_W_8; break; case -9: s_cached_timezone = tz_UTC_W_9; break; case -10: s_cached_timezone = tz_UTC_W_10; break; case -11: s_cached_timezone = tz_UTC_W_11; break; case -12: s_cached_timezone = tz_UTC_W_12; break; default: s_cached_timezone = tz_NONE; } } return s_cached_timezone; } numeric seconds() { struct timeval time; gettimeofday( &time, 0 ); return time.tv_sec + (time.tv_usec / 1000000.0 ); } bool absoluteWait( const TimeStamp &ts ) { TimeStamp now; currentTime( now ); if ( ts <= now ) return false; TimeStamp diff = ts - now; struct timespec tw; tw.tv_nsec = diff.m_msec *10000000; tw.tv_sec = (time_t) diff.m_day * (24*3600) + diff.m_hour * 3600 + diff.m_minute * 60 + diff.m_second; if ( nanosleep( &tw, 0 ) == -1 ) return false; return true; } bool relativeWait( const TimeStamp &ts ) { struct timespec tw; tw.tv_nsec = ts.m_msec *10000000; tw.tv_sec = (time_t) ts.m_day * (24*3600) + ts.m_hour * 3600 + ts.m_minute * 60 + ts.m_second; if ( nanosleep( &tw, 0 ) == -1 ) return false; return true; } bool nanoWait( int32 seconds, int32 nanoseconds ) { struct timespec tw; tw.tv_nsec = nanoseconds; tw.tv_sec = (time_t) seconds; if ( nanosleep( &tw, 0 ) == -1 ) return false; return true; } void timestampFromSystemTime( const SystemTime &sys_time, TimeStamp &ts ) { struct tm date; const UnixSystemTime *unix_time = static_cast< const UnixSystemTime *>( &sys_time ); localtime_r( & unix_time->m_time_t, &date ); ts.m_year = date.tm_year + 1900; ts.m_month = date.tm_mon+1; ts.m_day = date.tm_mday; ts.m_hour = date.tm_hour; ts.m_minute = date.tm_min; ts.m_second = date.tm_sec; // todo: collect day of year and weekday ts.m_timezone = tz_local; } } // time } // sys } /* end of time_sys_unix.cpp */ engine/time_sys_win.cpp000066400000000000000000000122511176363201700155470ustar00rootroot00000000000000/* FALCON - Falcon advanced simple text evaluator. FILE: time_sys_win.cpp Win system implementation for time related service ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun mar 6 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Win system implementation for time related service */ #include #include #include #include namespace Falcon { namespace Sys { namespace Time { static TimeZone s_cached_timezone = tz_local; // which is also 0 void currentTime( ::Falcon::TimeStamp &ts ) { SYSTEMTIME st; GetLocalTime( &st ); ts.m_year = st.wYear; ts.m_month = st.wMonth; ts.m_day = st.wDay; ts.m_hour = st.wHour; ts.m_minute = st.wMinute; ts.m_second = st.wSecond; ts.m_msec = st.wMilliseconds; // todo: collect day of year and weekday ts.m_timezone = tz_local; } TimeZone getLocalTimeZone() { // this function is not reentrant, but it's ok, as // the worst thing that may happen in MT or multiprocess // is double calculation of the cached value. // infer timezone by checking gmtime/localtime if( s_cached_timezone == tz_local ) { TIME_ZONE_INFORMATION timezone; DWORD dwMode = GetTimeZoneInformation( &timezone ); long shift = - (long) ( timezone.Bias ); if ( dwMode == TIME_ZONE_ID_DAYLIGHT ) shift -= (long) timezone.DaylightBias; long hours = shift / 60; long minutes = shift % 60; // and now, let's see if we have one of our zones: switch( hours ) { case 1: s_cached_timezone = tz_UTC_E_1; break; case 2: s_cached_timezone = tz_UTC_E_2; break; case 3: s_cached_timezone = tz_UTC_E_3; break; case 4: s_cached_timezone = tz_UTC_E_4; break; case 5: s_cached_timezone = tz_UTC_E_5; break; case 6: s_cached_timezone = tz_UTC_E_6; break; case 7: s_cached_timezone = tz_UTC_E_7; break; case 8: s_cached_timezone = tz_UTC_E_8; break; case 9: if( minutes == 30 ) s_cached_timezone = tz_ACST; else s_cached_timezone = tz_UTC_E_9; break; case 10: if( minutes == 30 ) s_cached_timezone = tz_ACDT; else s_cached_timezone = tz_UTC_E_10; break; case 11: if( minutes == 30 ) s_cached_timezone = tz_NFT; else s_cached_timezone = tz_UTC_E_11; break; case 12: s_cached_timezone = tz_UTC_E_11; break; case -1: s_cached_timezone = tz_UTC_W_1; case -2: if( minutes == 30 ) s_cached_timezone = tz_HAT; else s_cached_timezone = tz_UTC_W_2; break; case -3: if( minutes == 30 ) s_cached_timezone = tz_NST; else s_cached_timezone = tz_UTC_W_3; break; case -4: s_cached_timezone = tz_UTC_W_4; break; case -5: s_cached_timezone = tz_UTC_W_5; break; case -6: s_cached_timezone = tz_UTC_W_6; break; case -7: s_cached_timezone = tz_UTC_W_7; break; case -8: s_cached_timezone = tz_UTC_W_8; break; case -9: s_cached_timezone = tz_UTC_W_9; break; case -10: s_cached_timezone = tz_UTC_W_10; break; case -11: s_cached_timezone = tz_UTC_W_11; break; case -12: s_cached_timezone = tz_UTC_W_12; break; default: s_cached_timezone = tz_NONE; } } return s_cached_timezone; return tz_local; } bool sleep( numeric seconds ) { ::Sleep( (DWORD) (seconds * 1000.0) ); return true; } numeric seconds() { return Falcon::Sys::_seconds(); } bool absoluteWait( const TimeStamp &ts ) { TimeStamp now; currentTime( now ); if ( ts <= now ) return false; TimeStamp diff = ts - now; DWORD msecs = diff.m_msec + diff.m_day * (24*3600) + diff.m_hour * 3600 + diff.m_minute * 60 + diff.m_second; ::Sleep( msecs ); return true; } bool relativeWait( const TimeStamp &ts ) { DWORD msecs = ts.m_msec + ts.m_day * (24*3600) + ts.m_hour * 3600 + ts.m_minute * 60 + ts.m_second; ::Sleep( msecs ); return true; } bool nanoWait( int32 seconds, int32 nanoseconds ) { DWORD msecs = seconds * 1000 + nanoseconds / 1000000; ::Sleep( msecs ); return true; } void timestampFromSystemTime( const SystemTime &sys_time, TimeStamp &ts ) { const WinSystemTime *win_time = static_cast< const WinSystemTime *>( &sys_time ); ts.m_year = win_time->m_time.wYear; ts.m_month = win_time->m_time.wMonth; ts.m_day = win_time->m_time.wDay; ts.m_hour = win_time->m_time.wHour; ts.m_minute = win_time->m_time.wMinute; ts.m_second = win_time->m_time.wSecond; ts.m_msec = win_time->m_time.wMilliseconds; // todo: collect day of year and weekday ts.m_timezone = tz_local; } } // time } // sys } /* end of time_sys_win.cpp */ engine/timestamp.cpp000066400000000000000000000563641176363201700150560ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: TimeStampapi.cpp Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun mar 6 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Short description */ #include #include #include #include #include #include #include #include static const char *RFC_2822_days[] = { "Mon","Tue", "Wed","Thu","Fri","Sat","Sun" }; static const char *RFC_2822_months[] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug", "Sep","Oct","Nov","Dec" }; namespace Falcon { inline bool i_isLeapYear( int year ) { if ( year % 100 == 0 ) { // centuries that can be divided by 400 are leap // others are not. return ( year % 400 == 0 ); } // all other divisible by 4 years are leap return ( year % 4 == 0 ); } void TimeStamp::copy( const TimeStamp &ts ) { m_year = ts.m_year; m_month = ts.m_month; m_day = ts.m_day; m_timezone = ts.m_timezone; m_hour = ts.m_hour; m_minute = ts.m_minute; m_second = ts.m_second; m_msec = ts.m_msec; } const char *TimeStamp::getRFC2822_ZoneName( TimeZone tz, bool bSemantic, bool bSaving ) { if ( tz == tz_local ) tz = Sys::Time::getLocalTimeZone(); switch( tz ) { case tz_local: return "+????"; case tz_NONE: case tz_UTC: if ( bSemantic ) return "GMT"; return "+0000"; case tz_UTC_E_1: return "+0100"; case tz_UTC_E_2: return "+0200"; case tz_UTC_E_3: return "+0300"; case tz_UTC_E_4: return "+0400"; case tz_UTC_E_5: return "+0500"; case tz_UTC_E_6: return "+0600"; case tz_UTC_E_7: return "+0700"; case tz_UTC_E_8: return "+0800"; case tz_UTC_E_9: return "+0900"; case tz_UTC_E_10: return "+1000"; case tz_UTC_E_11: return "+1100"; case tz_UTC_E_12: return "+1200"; case tz_UTC_W_1: return "-0100"; case tz_UTC_W_2: return "-0200"; case tz_UTC_W_3: return "-0300"; case tz_UTC_W_4: if ( bSemantic ) return "EDT"; return "-0400"; case tz_UTC_W_5: if ( bSemantic ) return bSaving ? "EST":"CDT"; return "-0500"; case tz_UTC_W_6: if ( bSemantic ) return bSaving ? "CST":"MDT"; return "-0600"; case tz_UTC_W_7: if ( bSemantic ) return bSaving ? "MST":"PDT"; return "-0700"; case tz_UTC_W_8: if ( bSemantic ) return "PST"; return "-0800"; case tz_UTC_W_9: return "-0900"; case tz_UTC_W_10: return "-1000"; case tz_UTC_W_11: return "-1100"; case tz_UTC_W_12: return "-1200"; case tz_NFT: return "+1130"; case tz_ACDT: return "+1030"; case tz_ACST: return "+0930"; case tz_HAT: return "-0230"; case tz_NST: return "-0330"; } return "+????"; } TimeZone TimeStamp::getRFC2822_Zone( const char *csZoneName ) { if( strncmp( "UT", csZoneName, 2 ) == 0 || strncmp( "GMT", csZoneName, 3 ) == 0 || strncmp( "+0000", csZoneName, 5 ) == 0 ) { return tz_UTC; } if ( csZoneName[0] == '+' ) { int zone = atoi( csZoneName + 1 ); switch( zone ) { case 100: return tz_UTC_E_1; case 200: return tz_UTC_E_2; case 300: return tz_UTC_E_3; case 400: return tz_UTC_E_4; case 500: return tz_UTC_E_5; case 600: return tz_UTC_E_6; case 700: return tz_UTC_E_7; case 800: return tz_UTC_E_8; case 900: return tz_UTC_E_9; case 1000: return tz_UTC_E_10; case 1100: return tz_UTC_E_11; case 1200: return tz_UTC_E_12; case 1130: return tz_NFT; case 1030: return tz_ACDT; case 930: return tz_ACST; } return tz_NONE; } else if ( csZoneName[0] == '-' ) { int zone = atoi( csZoneName + 1 ); switch( zone ) { case 100: return tz_UTC_W_1; case 200: return tz_UTC_W_2; case 300: return tz_UTC_W_3; case 400: return tz_UTC_W_4; case 500: return tz_UTC_W_5; case 600: return tz_UTC_W_6; case 700: return tz_UTC_W_7; case 800: return tz_UTC_W_8; case 900: return tz_UTC_W_9; case 1000: return tz_UTC_W_10; case 1100: return tz_UTC_W_11; case 1200: return tz_UTC_W_12; case 230: return tz_HAT; case 330: return tz_NST; } return tz_NONE; } if( strncmp( "EDT", csZoneName, 3 ) == 0 ) { return tz_UTC_W_4; } if( strncmp( "EST", csZoneName, 3 ) == 0 || strncmp( "CDT", csZoneName, 3 ) == 0 ) { return tz_UTC_W_5; } if( strncmp( "CST", csZoneName, 3 ) == 0 || strncmp( "MDT", csZoneName, 3 ) == 0 ) { return tz_UTC_W_6; } if( strncmp( "MST", csZoneName, 3 ) == 0 || strncmp( "PDT", csZoneName, 3 ) == 0 ) { return tz_UTC_W_7; } if( strncmp( "PST", csZoneName, 3 ) == 0 ) { return tz_UTC_W_8; } // failure return tz_NONE; } const char *TimeStamp::getRFC2822_WeekDayName( int16 wd ) { if ( wd >=0 && wd < 7 ) { return RFC_2822_days[wd]; } return "???"; } const char *TimeStamp::getRFC2822_MonthName( int16 month ) { if ( month >= 1 && month <= 12 ) { return RFC_2822_months[ month - 1 ]; } return "???"; } int16 TimeStamp::getRFC2822_WeekDay( const char *name ) { for ( uint32 i = 0; i < sizeof( RFC_2822_days ) / sizeof( char *); i ++ ) { if ( strncmp( RFC_2822_days[i], name, 3) == 0 ) return i; } return -1; } int16 TimeStamp::getRFC2822_Month( const char *name ) { for ( uint32 i = 0; i < sizeof( RFC_2822_months ) / sizeof( char *); i ++ ) { if ( strncmp( RFC_2822_months[i], name, 3) == 0 ) return i+1; } return -1; } TimeStamp &TimeStamp::operator = ( const TimeStamp &ts ) { copy(ts); return *this; } bool TimeStamp::toRFC2822( String &target, bool bSemantic, bool bDst ) const { if ( ! isValid() ) { target = "?"; return false; } target.append( getRFC2822_WeekDayName( dayOfWeek() ) ); target.append( ',' ); target.append( ' ' ); target.writeNumber( (int64) m_day, "%02d" ); target.append( ' ' ); target.append( getRFC2822_MonthName( m_month ) ); target.append( ' ' ); if ( m_year < 0 ) target.append( "0000" ); else { target.writeNumber( (int64) m_year, "%04d" ); } target.append( ' ' ); target.writeNumber( (int64) m_hour, "%02d" ); target.append( ':' ); target.writeNumber( (int64) m_minute, "%02d" ); target.append( ':' ); target.writeNumber( (int64) m_second, "%02d" ); target.append( ' ' ); TimeZone tz = m_timezone; if ( tz == tz_local ) { tz = Sys::Time::getLocalTimeZone(); } target.append( getRFC2822_ZoneName( tz, bSemantic, bDst ) ); return true; } bool TimeStamp::fromRFC2822( TimeStamp &target, const String &source ) { AutoCString cstr( source ); return fromRFC2822( target, cstr.c_str() ); } bool TimeStamp::fromRFC2822( TimeStamp &target, const char *source ) { const char *pos = source; // Find the comma while ( *pos != 0 && *pos != ',' ) pos++; if ( *pos == 0 || (pos-source)!= 3 ) return false; // is this a valid day? if( getRFC2822_WeekDay( source ) < 0 ) return false; pos++; if ( *pos == 0 ) return false; pos++; const char *mon = pos; while( *mon != 0 && *mon != ' ' ) mon++; if ( *mon == 0 || (mon - pos) != 2) return false; target.m_day = atoi( pos ); mon++; pos = mon; while( *mon != 0 && *mon != ' ' ) mon++; if ( *mon == 0 || (mon - pos) != 3) return false; target.m_month = getRFC2822_Month( pos ); mon++; pos = mon; while( *mon != 0 && *mon != ' ' ) mon++; if ( *mon == 0 || (mon - pos) != 4) return false; target.m_year = atoi( pos ); mon++; pos = mon; while( *mon != 0 && *mon != ':' ) mon++; if ( *mon == 0 || (mon - pos) != 2 ) return false; target.m_hour = atoi( pos ); mon++; pos = mon; while( *mon != 0 && *mon != ':' ) mon++; if ( *mon == 0 || (mon - pos) != 2 ) return false; target.m_minute = atoi( pos ); mon++; pos = mon; while( *mon != 0 && *mon != ' ' ) mon++; if ( *mon == 0 || (mon - pos) != 2 ) return false; target.m_second = atoi( pos ); mon++; target.m_timezone = getRFC2822_Zone( mon ); if( target.m_timezone == tz_NONE ) return false; target.m_msec = 0; return target.isValid(); } bool TimeStamp::isValid() const { if ( m_msec < 0 || m_msec >= 1000 ) return false; if ( m_second < 0 || m_second >= 60 ) return false; if ( m_minute < 0 || m_minute >= 60 ) return false; if ( m_hour < 0 || m_hour >= 24 ) return false; if ( m_month < 1 || m_month > 12 ) return false; if ( m_day < 1 || m_day > 31 ) return false; // exclude months with 31 days if ( m_month == 1 || m_month == 3 || m_month == 5 || m_month == 7 || m_month == 8 || m_month == 10|| m_month == 12 ) return true; // now exclude the months with 30 days if ( m_month != 2 ) { if ( m_day != 31 ) return true; return false; // 31 days in a 30 days long month } // february. if ( m_day == 30 ) return false; if ( m_day < 29 ) return true; // we have to calculate the leap year if ( isLeapYear() ) return true; return false; } bool TimeStamp::isLeapYear() const { if ( m_year % 100 == 0 ) { // centuries that can be divided by 400 are leap // others are not. return ( m_year % 400 == 0 ); } // all other divisible by 4 years are leap return ( m_year % 4 == 0 ); } int16 TimeStamp::dayOfYear() const { if ( ! isValid() ) return 0; int days = 0; // add all the previous days switch( m_month ) { case 12: days += 30; case 11: days += 31; case 10: days += 30; case 9: days += 31; case 8: days += 31; case 7: days += 30; case 6: days += 31; case 5: days += 30; case 4: days += 31; case 3: days += 28; if ( isLeapYear() ) days ++; case 2: days += 31; } days += m_day; return days; } int16 TimeStamp::getDaysOfMonth( int16 month ) const { if ( month == -1 ) month = m_month; switch( month ) { case 1: return 31; case 2: return isLeapYear()? 29 : 28; case 3: return 31; case 4: return 30; case 5: return 31; case 6: return 30; case 7: return 31; case 8: return 31; case 9: return 30; case 10: return 31; case 11: return 30; case 12: return 31; } return 0; } /** week starting on monday, 0 based. */ int16 TimeStamp::dayOfWeek() const { // consider 1700 the epoch if ( m_year < 1700 ) return -1; // compute days since epoch. int32 nyears = m_year - 1700; int32 nday = nyears * 365; // add leap years (up to the previous year. This year is computed in dayOfYear() if( m_year > 1700 ) nday += ((nyears-1) / 4) - ((nyears-1) / 100) + ((nyears-1) / 400); // add day of the year. nday += dayOfYear(); // add epoch (1/1/1700 was a Friday) nday += 4; nday %= 7; return nday; } int64 TimeStamp::toLongFormat() const { if ( ! isValid() ) return -1; int64 res = 0; res |= m_year; // 4 bits for months res <<= 4; res |= m_month; // 5 bits for days res <<= 5; res |= m_day; // 5 bits for hours res <<= 5; res |= m_hour; // 6 bits for minutes res <<= 6; res |= m_minute; // 6 bits for seconds res <<= 6; res |= m_second; //10 bits for msecs res <<= 10; res |= m_msec; // 5 bits for tz. res <<= 5; res |= (int) m_timezone; return res; } void TimeStamp::fromLongFormat( int64 lf ) { m_timezone = (TimeZone) (0x1f & lf); lf >>= 5; m_msec = (int16) ( 0x3ff & lf ); lf >>= 10; m_second = (int16) ( 0x3f & lf ); lf >>= 6; m_minute = (int16) ( 0x3f & lf ); lf >>= 6; m_hour = (int16) ( 0x1f & lf ); lf >>= 5; m_day = (int16) ( 0x1f & lf ); lf >>= 5; m_month = (int16) ( 0xf & lf ); lf >>= 4; m_year = (int16) lf; } void TimeStamp::add( const TimeStamp &ts ) { m_day += ts.m_day; m_hour += ts.m_hour; m_minute += ts.m_minute; m_second += ts.m_second; m_msec += ts.m_msec; if ( m_timezone != ts.m_timezone && m_timezone != tz_NONE && ts.m_timezone != tz_NONE ) { int16 hours=0, mins=0, ts_hours=0, ts_mins=0; ts.getTZDisplacement( ts_hours, ts_mins ); getTZDisplacement( hours, mins ); m_hour += hours - ts_hours; m_minute += hours - ts_mins; } rollOver(); if ( m_timezone == tz_NONE ) m_timezone = ts.m_timezone; } void TimeStamp::add( int32 days, int32 hours, int32 mins, int32 secs, int32 msecs ) { m_day += days; m_hour += hours; m_minute += mins; m_second += secs; m_msec += msecs; rollOver(); } inline void cplxSub( int &sub1, int sub2, int unit, int &change ) { sub1 -= sub2; while( sub1 < 0 ) { change--; sub1 += unit; } } void TimeStamp::distance( const TimeStamp &ts ) { int days = 0; // first decide which date is bigger. const TimeStamp *startDate, *endDate; int comparation = this->compare( ts ); if (comparation == 0 ) { // the same date, means no distance. m_msec = m_second = m_minute = m_hour = m_day = m_month = m_year = 0; return; } if ( comparation > 0 ) { startDate = &ts; endDate = this; } else { startDate = this; endDate = &ts; } // If year is different: if( startDate->m_year != endDate->m_year ) { // calculate the number of days in the in-between years for ( int baseYear = startDate->m_year + 1; baseYear < endDate->m_year; baseYear++ ) days += i_isLeapYear( baseYear ) ? 366 : 365; // calculate the number of days from start day to the end of the year. int doy = ( startDate->isLeapYear() ? 366 : 365 ) - startDate->dayOfYear(); days += doy; // and add the days in the year of the target date days += endDate->dayOfYear(); } else { days += endDate->dayOfYear() - startDate->dayOfYear(); } m_year = 0; m_month = 0; //int tday = days; int thour = endDate->m_hour; int tminute = endDate->m_minute; int tsecond = endDate->m_second; int tmsec = endDate->m_msec; if ( m_timezone != ts.m_timezone && m_timezone != tz_NONE && ts.m_timezone != tz_NONE ) { int16 hours=0, mins=0, ts_hours=0, ts_mins=0; ts.getTZDisplacement( ts_hours, ts_mins ); getTZDisplacement( hours, mins ); // if ts bigger (positive distance) we must add the difference between TS timezone and us if ( comparation < 0 ) { thour += ts_hours - hours; tminute += ts_mins - mins; } else { // else we got to subtract it thour += ts_hours - hours; tminute += ts_mins - mins; } } cplxSub( tmsec, startDate->m_msec, 1000, tsecond ); cplxSub( tsecond, startDate->m_second, 60, tminute ); cplxSub( tminute, startDate->m_minute, 60, thour ); cplxSub( thour, startDate->m_hour, 24, days ); m_day = days; m_hour = thour; m_minute = tminute; m_second = tsecond; m_msec = tmsec; if( comparation > 0 ) { // the negative sign goes on the first non-zero unit if ( m_day != 0 ) m_day = -m_day; else if ( m_hour != 0 ) m_hour = -m_hour; else if ( m_minute != 0 ) m_minute = -m_minute; else if ( m_second != 0 ) m_second = -m_second; else m_msec = -m_msec; } m_timezone = tz_NONE; } void TimeStamp::rollOver( bool onlyDays ) { // do rollovers int32 adjust = 0; if( m_msec < 0 ) { adjust = - ( (-m_msec) / 1000 + 1 ); m_msec = 1000 - ((-m_msec)%1000); } if ( m_msec >= 1000 ) { adjust += m_msec / 1000; m_msec = m_msec % 1000; } m_second += adjust; adjust = 0; if( m_second < 0 ) { adjust = - ( (-m_second) / 60 + 1 ); m_second = 60 - ((-m_second)%60); } if (m_second >= 60 ) { adjust += m_second / 60; m_second = m_second % 60; } m_minute += adjust; adjust = 0; if( m_minute < 0 ) { adjust = - ( (-m_minute) / 60 + 1 ); m_minute = 60 - ((-m_minute)%60); } if ( m_minute >= 60 ) { adjust += m_minute / 60; m_minute = m_minute % 60; } m_hour += adjust; adjust = 0; if( m_hour < 0 ) { adjust = - ( (-m_hour) / 24 + 1 ); m_hour = 24 - ((-m_hour)%24); } if ( m_hour >= 24 ) { adjust += m_hour / 24; m_hour = m_hour % 24; } m_day += adjust; if ( onlyDays ) { return; } if ( m_day > 0 ) { int16 mdays; while( m_day > (mdays = getDaysOfMonth( m_month )) ) { m_day -= mdays; if( m_month == 12 ) { m_month = 1; m_year++; } else m_month++; } } else { while( m_day < 1 ) { if ( m_month == 1 ) { m_month = 12; m_year--; } else m_month --; m_day += getDaysOfMonth( m_month ); } } } int32 TimeStamp::compare( const TimeStamp &ts ) const { if ( m_year < ts.m_year ) return -1; if ( m_year > ts.m_year ) return 1; if ( m_month < ts.m_month ) return -1; if ( m_month > ts.m_month ) return 1; if ( m_day < ts.m_day ) return -1; if ( m_day > ts.m_day ) return 1; if ( ts.m_timezone == m_timezone || ts.m_timezone == tz_NONE || m_timezone == tz_NONE ) { if ( m_hour < ts.m_hour ) return -1; if ( m_hour > ts.m_hour ) return 1; if ( m_day < ts.m_day ) return -1; if ( m_day > ts.m_day ) return 1; } else { int16 hdisp=0, mdisp=0; int16 ts_hdisp=0, ts_mdisp=0;\ getTZDisplacement( hdisp, mdisp ); ts.getTZDisplacement( ts_hdisp, ts_mdisp ); if ( m_hour + hdisp < ts.m_hour + ts_hdisp ) return -1; if ( m_hour + hdisp > ts.m_hour + ts_hdisp ) return 1; if ( m_day + mdisp < ts.m_day + ts_mdisp ) return -1; if ( m_day + mdisp > ts.m_day + ts_mdisp ) return 1; } if ( m_minute < ts.m_minute ) return -1; if ( m_minute > ts.m_minute ) return 1; if ( m_second < ts.m_second ) return -1; if ( m_second > ts.m_second ) return 1; if ( m_msec < ts.m_msec ) return -1; if ( m_msec > ts.m_msec ) return 1; return 0; } void TimeStamp::getTZDisplacement( int16 &hours, int16 &minutes ) const { TimeZone tz = m_timezone; getTZDisplacement( tz, hours, minutes ); } void TimeStamp::getTZDisplacement( TimeZone tz, int16 &hours, int16 &minutes ) { if ( tz == tz_local ) { tz = Sys::Time::getLocalTimeZone(); } switch( tz ) { case tz_local: case tz_NONE: case tz_UTC: hours = 0; minutes = 0; break; case tz_UTC_E_1: hours = 1; minutes = 0; break; case tz_UTC_E_2: hours = 2; minutes = 0; break; case tz_UTC_E_3: hours = 3; minutes = 0; break; case tz_UTC_E_4: hours = 4; minutes = 0; break; case tz_UTC_E_5: hours = 5; minutes = 0; break; case tz_UTC_E_6: hours = 6; minutes = 0; break; case tz_UTC_E_7: hours = 7; minutes = 0; break; case tz_UTC_E_8: hours = 8; minutes = 0; break; case tz_UTC_E_9: hours = 9; minutes = 0; break; case tz_UTC_E_10: hours = 10; minutes = 0; break; case tz_UTC_E_11: hours = 11; minutes = 0; break; case tz_UTC_E_12: hours = 12; minutes = 0; break; case tz_UTC_W_1: hours = -1; minutes = 0; break; case tz_UTC_W_2: hours = -2; minutes = 0; break; case tz_UTC_W_3: hours = -3; minutes = 0; break; case tz_UTC_W_4: hours = -4; minutes = 0; break; case tz_UTC_W_5: hours = -5; minutes = 0; break; case tz_UTC_W_6: hours = -6; minutes = 0; break; case tz_UTC_W_7: hours = -7; minutes = 0; break; case tz_UTC_W_8: hours = -8; minutes = 0; break; case tz_UTC_W_9: hours = -9; minutes = 0; break; case tz_UTC_W_10: hours = -10; minutes = 0; break; case tz_UTC_W_11: hours = -11; minutes = 0; break; case tz_UTC_W_12: hours = -12; minutes = 0; break; case tz_NFT: hours = 11; minutes = 30; break; case tz_ACDT: hours = 10; minutes = 30; break; case tz_ACST: hours = 9; minutes = 30; break; case tz_HAT: hours = -2; minutes = -30; break; case tz_NST: hours = -3; minutes = -30; break; } } void TimeStamp::toString( String &target ) const { // for now a fast thing uint32 allocated = 23 > FALCON_STRING_ALLOCATION_BLOCK ? 24 : FALCON_STRING_ALLOCATION_BLOCK; char *storage = (char *) memAlloc( allocated ); sprintf( (char *)storage, "%04d-%02d-%02d %02d:%02d:%02d.%03d", m_year, m_month, m_day, m_hour, m_minute, m_second, m_msec ); target.adopt( storage, 23, allocated ); } bool TimeStamp::toString( String &target, const String &fmt ) const { AutoCString cfmt( fmt ); struct tm theTime; theTime.tm_sec = m_second; theTime.tm_min = m_minute; theTime.tm_hour = m_hour; theTime.tm_mday = m_day; theTime.tm_mon = m_month-1; theTime.tm_year = m_year - 1900; char timeTgt[512]; if( strftime( timeTgt, 512, cfmt.c_str(), &theTime) != 0 ) { target.bufferize( timeTgt ); uint32 pos = target.find( "%i" ); if( pos != String::npos ) { String rfc; toRFC2822( rfc ); while( pos != String::npos ) { target.change( pos, pos + 2, rfc ); pos = target.find( "%i", pos + 2 ); } } pos = target.find( "%q" ); if( pos != String::npos ) { String msecs; msecs.writeNumber( (int64) m_msec ); while( pos != String::npos ) { target.change( pos, pos + 2, msecs ); pos = target.find( "%q", pos + 2 ); } } pos = target.find( "%Q" ); if( pos != String::npos ) { String msecs; if( m_msec < 10 ) msecs = "00"; else if ( m_msec < 100 ) msecs = "0"; msecs.writeNumber( (int64) m_msec ); while( pos != String::npos ) { target.change( pos, pos + 2, msecs ); pos = target.find( "%Q", pos + 2 ); } } return true; } return false; } void TimeStamp::currentTime() { Sys::Time::currentTime( *this ); if ( m_timezone == tz_local ) { m_timezone = Sys::Time::getLocalTimeZone(); } } void TimeStamp::changeTimezone( TimeZone tz ) { if ( m_timezone == tz_local ) { m_timezone = Sys::Time::getLocalTimeZone(); if (tz == tz_local ) // no shift... return; } if ( tz == tz_local ) tz = Sys::Time::getLocalTimeZone(); // no shift if ( tz == m_timezone ) return; // get the relative total shift. int16 currentHour=0, currentMin=0, newHour=0, newMin=0; getTZDisplacement( tz, newHour, newMin ); getTZDisplacement( m_timezone, currentHour, currentMin ); m_hour -= currentHour; m_hour += newHour; m_minute -= currentMin; m_minute += newMin; rollOver( false ); m_timezone = tz; } TimeStamp *TimeStamp::clone() const { return new TimeStamp( *this ); } } /* end of TimeStampapi.cpp */ engine/tokenizer.cpp000066400000000000000000000156631176363201700150620ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: tokenizer.h Utility to parse complex and lengty strings. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 04 Feb 2009 16:19:28 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include namespace Falcon { Tokenizer::Tokenizer( TokenizerParams ¶ms, const String &seps, Stream *ins, bool bOwn ): m_separators( seps ), m_params( params ), m_input(ins), m_bOwnStream( bOwn ), m_version(0), m_nextToken( 0xFFFFFFFF ) { if ( seps == "" ) m_separators = " "; if ( ins ) m_hasCurrent = next(); else m_hasCurrent = false; } Tokenizer::Tokenizer( TokenizerParams ¶ms, const String &seps, const String &source ): m_separators( seps ), m_params( params ), m_input( new ROStringStream( source ) ), m_bOwnStream( true ), m_version(0), m_nextToken( 0xFFFFFFFF ) { if ( seps == "" ) m_separators = " "; m_hasCurrent = next(); } Tokenizer::Tokenizer( const Tokenizer &other ): m_separators( other.m_separators ), m_params( other.m_params ), m_input( other.m_input != 0 ? dyncast(other.m_input->clone()) : 0 ), m_bOwnStream( true ), m_version(other.m_version), m_nextToken( 0xFFFFFFFF ) { } Tokenizer::~Tokenizer() { if ( m_bOwnStream ) delete m_input; } bool Tokenizer::next() { // must be called when ready fassert( m_input != 0 ); if( m_nextToken != 0xFFFFFFFF ) { m_temp.size(0); m_temp.append( m_nextToken ); m_nextToken = 0xFFFFFFFF; return true; } if ( m_input->eof() ) { m_hasCurrent = false; return false; } // reset the input buffer m_temp.size(0); m_version++; uint32 chr; while( m_input->get( chr ) ) { bool skip = false; if( m_params.isWsToken() && String::isWhiteSpace( chr ) ) { if( m_temp.size() == 0 ) continue; return true; } // is this character a separator? for( uint32 i = 0; i < m_separators.length(); i ++ ) { if( chr == m_separators.getCharAt(i) ) { // Yes. should we return now? if ( m_params.isGroupSep() && m_temp.size() == 0 ) { skip = true; break; // we have to decide what to do outside. } // should we pack the thing? if( m_params.isBindSep() ) m_temp+=chr; else if( m_params.isReturnSep() ) { if ( m_temp.size() == 0 ) { m_temp.append( chr ); return true; } m_nextToken = chr; } if ( m_params.isTrim() ) m_temp.trim(); return true; } } if ( skip ) continue; // no, add this character m_temp += chr; if( m_params.maxToken() > 0 && m_temp.length() >= (uint32) m_params.maxToken() ) { if ( m_params.isTrim() ) m_temp.trim(); return true; } } if ( m_params.isTrim() ) m_temp.trim(); // ok we can't find any more character; but is this a valid token? m_hasCurrent = m_temp.size() != 0 || ! (m_params.isGroupSep() || m_params.isReturnSep()); return m_hasCurrent; } bool Tokenizer::empty() const { return m_input == 0 || m_input->eof(); } void Tokenizer::rewind() { if( m_input != 0 ) { m_input->seekBegin(0); m_version = 0; m_hasCurrent = next(); } } Tokenizer* Tokenizer::clone() const { return new Tokenizer( *this ); } const Item &Tokenizer::front() const { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ) .extra( "Tokenizer::front" ) ); } const Item &Tokenizer::back() const { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ).origin( e_orig_runtime ).extra( "Tokenizer::back" ) ); } void Tokenizer::clear() { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ).origin( e_orig_runtime ).extra( "Tokenizer::clear" ) ); } void Tokenizer::append( const Item& itm ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ).origin( e_orig_runtime ).extra( "Tokenizer::append" ) ); } void Tokenizer::prepend( const Item& itm ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ).origin( e_orig_runtime ).extra( "Tokenizer::prepend" ) ); } void Tokenizer::parse( const String &data ) { if ( m_bOwnStream ) delete m_input; m_input = new ROStringStream( data ); m_bOwnStream = true; m_version++; m_hasCurrent = next(); } void Tokenizer::parse( Stream *in, bool bOwn ) { if ( m_bOwnStream ) delete m_input; m_input = in; m_bOwnStream = bOwn; m_version++; } //============================================================ // Iterator management. //============================================================ void Tokenizer::getIterator( Iterator& tgt, bool tail ) const { Sequence::getIterator( tgt, tail ); // nothing to do } void Tokenizer::copyIterator( Iterator& tgt, const Iterator& source ) const { Sequence::copyIterator( tgt, source ); // nothing to do } void Tokenizer::insert( Iterator &iter, const Item &data ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "Tokenizer::insert" ) ); } void Tokenizer::erase( Iterator &iter ) { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "Tokenizer::erase" ) ); } bool Tokenizer::hasNext( const Iterator &iter ) const { return isReady(); } bool Tokenizer::hasPrev( const Iterator &iter ) const { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "Tokenizer::hasPrev" ) ); } bool Tokenizer::hasCurrent( const Iterator &iter ) const { return m_hasCurrent; } bool Tokenizer::next( Iterator &iter ) const { return const_cast(this)->next(); } bool Tokenizer::prev( Iterator &iter ) const { throw new CodeError( ErrorParam( e_not_implemented, __LINE__ ) .origin( e_orig_runtime ).extra( "Tokenizer::prev" ) ); } Item& Tokenizer::getCurrent( const Iterator &iter ) { static Item i_temp; i_temp = new CoreString( m_temp ); return i_temp; } Item& Tokenizer::getCurrentKey( const Iterator &iter ) { throw new CodeError( ErrorParam( e_non_dict_seq, __LINE__ ) .origin( e_orig_runtime ).extra( "Tokenizer::getCurrentKey" ) ); } bool Tokenizer::equalIterator( const Iterator &first, const Iterator &second ) const { return false; } } engine/trace.cpp000066400000000000000000000010571176363201700141360ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: trace.cpp Debug trace utility. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 18 Jul 2009 14:07:01 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef NDEBUG #include extern "C" { FALCON_DYN_SYM FILE* _falcon_trace_fp = 0; } #endif /* end of trace.cpp */ engine/traits.cpp000066400000000000000000000121741176363201700143500ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: traits.cpp Traits - informations on types for the generic containers ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven oct 27 11:02:00 CEST 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include // for memset namespace Falcon { ElementTraits::~ElementTraits() {} uint32 VoidpTraits::memSize() const { return sizeof( void * ); } void VoidpTraits::init( void *targetZone ) const { void **target = (void**) targetZone; *target = 0; } void VoidpTraits::copy( void *targetZone, const void *sourceZone ) const { void **target = (void**) targetZone; *target = (void *) sourceZone; } int VoidpTraits::compare( const void *targetZone, const void *sourceZone ) const { void **target = (void**) targetZone; if ( ((uint64) *target) < ((uint64)sourceZone) ) return - 1; else if ( ((uint64) *target) > ((uint64)sourceZone) ) return 1; else return 0; } void VoidpTraits::destroy( void *item ) const { // do nothing } bool VoidpTraits::owning() const { return false; } uint32 IntTraits::memSize() const { return sizeof( int32 ); } void IntTraits::init( void *targetZone ) const { int32 *target = (int32*) targetZone; *target = 0; } void IntTraits::copy( void *targetZone, const void *sourceZone ) const { int32 *target = (int32*) targetZone; const int32 *source = (int32*) sourceZone; *target = *source; } int IntTraits::compare( const void *first, const void *second ) const { const int32 *ifirst = (int32 *) first; const int32 *isecond = (int32 *) second; if ( *ifirst < *isecond ) return - 1; else if ( *ifirst > *isecond ) return 1; else return 0; } void IntTraits::destroy( void *item ) const { // do nothing } bool IntTraits::owning() const { return false; } void StringPtrTraits::init( void *targetZone ) const { String **target = (String**) targetZone; *target = 0; } uint32 StringPtrTraits::memSize() const { return sizeof( String * ); } void StringPtrTraits::copy( void *targetZone, const void *sourceZone ) const { String **target = (String**) targetZone; String *source = (String*) sourceZone; *target = source; } int StringPtrTraits::compare( const void *first, const void *second ) const { String **ifirst = (String **) first; String *isecond = (String *) second; return (*ifirst)->compare( *isecond ); } void StringPtrTraits::destroy( void *item ) const { // do nothing } bool StringPtrTraits::owning() const { return false; } void StringPtrOwnTraits::destroy( void *item ) const { String **ifirst = (String **) item; delete (*ifirst); } bool StringPtrOwnTraits::owning() const { return true; } uint32 StringTraits::memSize() const { return sizeof( String ); } void StringTraits::init( void *targetZone ) const { String *target = (String *) targetZone; // do minimal initialization memset( target, 0, sizeof( String ) ); // all values to zero and false. target->manipulator( &csh::handler_static ); } void StringTraits::copy( void *targetZone, const void *sourceZone ) const { String *target = (String *) targetZone; const String *source = (String *) sourceZone; // init so that bufferize won't do fancy deletes memset( target, 0, sizeof( String ) ); // all values to zero and false. target->manipulator( &csh::handler_static ); // then deep copy the other target->bufferize(*source); } int StringTraits::compare( const void *first, const void *second ) const { const String *ifirst = (String *) first; const String *isecond = (String *) second; return ifirst->compare( *isecond ); } void StringTraits::destroy( void *item ) const { String *ifirst = (String *) item; ifirst->manipulator()->destroy( ifirst ); } bool StringTraits::owning() const { return true; } namespace traits { StringTraits* string_dt = 0; VoidpTraits* voidp_dp = 0; IntTraits* int_dp = 0; StringPtrTraits* stringptr_dp = 0; StringPtrOwnTraits* stringptr_own_dp = 0; FALCON_DYN_SYM StringTraits &t_string() { if( !string_dt ) string_dt = new StringTraits; return *string_dt; } FALCON_DYN_SYM VoidpTraits &t_voidp() { if( !voidp_dp ) voidp_dp = new VoidpTraits; return *voidp_dp; } FALCON_DYN_SYM IntTraits &t_int() { if( !int_dp ) int_dp = new IntTraits; return *int_dp; } FALCON_DYN_SYM StringPtrTraits &t_stringptr() { if( !stringptr_dp ) stringptr_dp = new StringPtrTraits; return *stringptr_dp; } FALCON_DYN_SYM StringPtrOwnTraits &t_stringptr_own() { if( !stringptr_own_dp ) stringptr_own_dp = new StringPtrOwnTraits; return *stringptr_own_dp; } void releaseTraits() { delete string_dt; string_dt = 0; delete voidp_dp; voidp_dp = 0; delete int_dp; int_dp = 0; delete stringptr_dp; stringptr_dp = 0; delete stringptr_own_dp; stringptr_own_dp = 0; } } } /* end of traits.cpp */ engine/trans_tables.def000066400000000000000000001174431176363201700155040ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: trans_tables.def $Id: trans_tables.def,v 1.1.1.1 2006/10/08 15:05:11 gian Exp $ Transcode tables ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer ago 23 2006 Last modified because: ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. In order to use this file in its compiled form, this source or part of it you have to read, understand and accept the conditions that are stated in the LICENSE file that comes boundled with this package. */ /** \file Transcode tables. */ //============================================ // CP 1252 // static uint16 s_table_cp1252[] = { 0x20AC, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x017D, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x017E, 0x0178, 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF }; //============================================ // CP 1252 - 128 - 255 // static CP_ISO_UINT_TABLE s_rtable_cp1252[] = { {0x00A0, 0xA0}, {0x00A1, 0xA1}, {0x00A2, 0xA2}, {0x00A3, 0xA3}, {0x00A4, 0xA4}, {0x00A5, 0xA5}, {0x00A6, 0xA6}, {0x00A7, 0xA7}, {0x00A8, 0xA8}, {0x00A9, 0xA9}, {0x00AA, 0xAA}, {0x00AB, 0xAB}, {0x00AC, 0xAC}, {0x00AD, 0xAD}, {0x00AE, 0xAE}, {0x00AF, 0xAF}, {0x00B0, 0xB0}, {0x00B1, 0xB1}, {0x00B2, 0xB2}, {0x00B3, 0xB3}, {0x00B4, 0xB4}, {0x00B5, 0xB5}, {0x00B6, 0xB6}, {0x00B7, 0xB7}, {0x00B8, 0xB8}, {0x00B9, 0xB9}, {0x00BA, 0xBA}, {0x00BB, 0xBB}, {0x00BC, 0xBC}, {0x00BD, 0xBD}, {0x00BE, 0xBE}, {0x00BF, 0xBF}, {0x00C0, 0xC0}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C3, 0xC3}, {0x00C4, 0xC4}, {0x00C5, 0xC5}, {0x00C6, 0xC6}, {0x00C7, 0xC7}, {0x00C8, 0xC8}, {0x00C9, 0xC9}, {0x00CA, 0xCA}, {0x00CB, 0xCB}, {0x00CC, 0xCC}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00CF, 0xCF}, {0x00D0, 0xD0}, {0x00D1, 0xD1}, {0x00D2, 0xD2}, {0x00D3, 0xD3}, {0x00D4, 0xD4}, {0x00D5, 0xD5}, {0x00D6, 0xD6}, {0x00D7, 0xD7}, {0x00D8, 0xD8}, {0x00D9, 0xD9}, {0x00DA, 0xDA}, {0x00DB, 0xDB}, {0x00DC, 0xDC}, {0x00DD, 0xDD}, {0x00DE, 0xDE}, {0x00DF, 0xDF}, {0x00E0, 0xE0}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E3, 0xE3}, {0x00E4, 0xE4}, {0x00E5, 0xE5}, {0x00E6, 0xE6}, {0x00E7, 0xE7}, {0x00E8, 0xE8}, {0x00E9, 0xE9}, {0x00EA, 0xEA}, {0x00EB, 0xEB}, {0x00EC, 0xEC}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00EF, 0xEF}, {0x00F0, 0xF0}, {0x00F1, 0xF1}, {0x00F2, 0xF2}, {0x00F3, 0xF3}, {0x00F4, 0xF4}, {0x00F5, 0xF5}, {0x00F6, 0xF6}, {0x00F7, 0xF7}, {0x00F8, 0xF8}, {0x00F9, 0xF9}, {0x00FA, 0xFA}, {0x00FB, 0xFB}, {0x00FC, 0xFC}, {0x00FD, 0xFD}, {0x00FE, 0xFE}, {0x00FF, 0xFF}, {0x0152, 0x8C}, {0x0153, 0x9C}, {0x0160, 0x8A}, {0x0161, 0x9A}, {0x0178, 0x9F}, {0x017D, 0x8E}, {0x017E, 0x9E}, {0x0192, 0x83}, {0x02C6, 0x88}, {0x02DC, 0x98}, {0x2013, 0x96}, {0x2014, 0x97}, {0x2018, 0x91}, {0x2019, 0x92}, {0x201A, 0x82}, {0x201C, 0x93}, {0x201D, 0x94}, {0x201E, 0x84}, {0x2020, 0x86}, {0x2021, 0x87}, {0x2022, 0x95}, {0x2026, 0x85}, {0x2030, 0x89}, {0x2039, 0x8B}, {0x203A, 0x9B}, {0x20AC, 0x80}, {0x2122, 0x99} }; //======================================================== // iso latin 1 // define iso static uint16 s_table_iso8859_1[] = { 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF }; static CP_ISO_UINT_TABLE s_rtable_iso8859_1[] = { {0x00A0, 0xA0}, {0x00A1, 0xA1}, {0x00A2, 0xA2}, {0x00A3, 0xA3}, {0x00A4, 0xA4}, {0x00A5, 0xA5}, {0x00A6, 0xA6}, {0x00A7, 0xA7}, {0x00A8, 0xA8}, {0x00A9, 0xA9}, {0x00AA, 0xAA}, {0x00AB, 0xAB}, {0x00AC, 0xAC}, {0x00AD, 0xAD}, {0x00AE, 0xAE}, {0x00AF, 0xAF}, {0x00B0, 0xB0}, {0x00B1, 0xB1}, {0x00B2, 0xB2}, {0x00B3, 0xB3}, {0x00B4, 0xB4}, {0x00B5, 0xB5}, {0x00B6, 0xB6}, {0x00B7, 0xB7}, {0x00B8, 0xB8}, {0x00B9, 0xB9}, {0x00BA, 0xBA}, {0x00BB, 0xBB}, {0x00BC, 0xBC}, {0x00BD, 0xBD}, {0x00BE, 0xBE}, {0x00BF, 0xBF}, {0x00C0, 0xC0}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C3, 0xC3}, {0x00C4, 0xC4}, {0x00C5, 0xC5}, {0x00C6, 0xC6}, {0x00C7, 0xC7}, {0x00C8, 0xC8}, {0x00C9, 0xC9}, {0x00CA, 0xCA}, {0x00CB, 0xCB}, {0x00CC, 0xCC}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00CF, 0xCF}, {0x00D0, 0xD0}, {0x00D1, 0xD1}, {0x00D2, 0xD2}, {0x00D3, 0xD3}, {0x00D4, 0xD4}, {0x00D5, 0xD5}, {0x00D6, 0xD6}, {0x00D7, 0xD7}, {0x00D8, 0xD8}, {0x00D9, 0xD9}, {0x00DA, 0xDA}, {0x00DB, 0xDB}, {0x00DC, 0xDC}, {0x00DD, 0xDD}, {0x00DE, 0xDE}, {0x00DF, 0xDF}, {0x00E0, 0xE0}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E3, 0xE3}, {0x00E4, 0xE4}, {0x00E5, 0xE5}, {0x00E6, 0xE6}, {0x00E7, 0xE7}, {0x00E8, 0xE8}, {0x00E9, 0xE9}, {0x00EA, 0xEA}, {0x00EB, 0xEB}, {0x00EC, 0xEC}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00EF, 0xEF}, {0x00F0, 0xF0}, {0x00F1, 0xF1}, {0x00F2, 0xF2}, {0x00F3, 0xF3}, {0x00F4, 0xF4}, {0x00F5, 0xF5}, {0x00F6, 0xF6}, {0x00F7, 0xF7}, {0x00F8, 0xF8}, {0x00F9, 0xF9}, {0x00FA, 0xFA}, {0x00FB, 0xFB}, {0x00FC, 0xFC}, {0x00FD, 0xFD}, {0x00FE, 0xFE}, {0x00FF, 0xFF} }; //======================================================== // iso latin 2 // define iso static uint16 s_table_iso8859_2[] = { 0x00A0, 0x0104, 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7, 0x00A8, 0x0160, 0x015E, 0x0164, 0x0179, 0x00AD, 0x017D, 0x017B, 0x00B0, 0x0105, 0x02DB, 0x0142, 0x00B4, 0x013E, 0x015B, 0x02C7, 0x00B8, 0x0161, 0x015F, 0x0165, 0x017A, 0x02DD, 0x017E, 0x017C, 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9 }; static CP_ISO_UINT_TABLE s_rtable_iso8859_2[] = { {0x00A0, 0xA0}, {0x00A4, 0xA4}, {0x00A7, 0xA7}, {0x00A8, 0xA8}, {0x00AD, 0xAD}, {0x00B0, 0xB0}, {0x00B4, 0xB4}, {0x00B8, 0xB8}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C4, 0xC4}, {0x00C7, 0xC7}, {0x00C9, 0xC9}, {0x00CB, 0xCB}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00D3, 0xD3}, {0x00D4, 0xD4}, {0x00D6, 0xD6}, {0x00D7, 0xD7}, {0x00DA, 0xDA}, {0x00DC, 0xDC}, {0x00DD, 0xDD}, {0x00DF, 0xDF}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E4, 0xE4}, {0x00E7, 0xE7}, {0x00E9, 0xE9}, {0x00EB, 0xEB}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00F3, 0xF3}, {0x00F4, 0xF4}, {0x00F6, 0xF6}, {0x00F7, 0xF7}, {0x00FA, 0xFA}, {0x00FC, 0xFC}, {0x00FD, 0xFD}, {0x0102, 0xC3}, {0x0103, 0xE3}, {0x0104, 0xA1}, {0x0105, 0xB1}, {0x0106, 0xC6}, {0x0107, 0xE6}, {0x010C, 0xC8}, {0x010D, 0xE8}, {0x010E, 0xCF}, {0x010F, 0xEF}, {0x0110, 0xD0}, {0x0111, 0xF0}, {0x0118, 0xCA}, {0x0119, 0xEA}, {0x011A, 0xCC}, {0x011B, 0xEC}, {0x0139, 0xC5}, {0x013A, 0xE5}, {0x013D, 0xA5}, {0x013E, 0xB5}, {0x0141, 0xA3}, {0x0142, 0xB3}, {0x0143, 0xD1}, {0x0144, 0xF1}, {0x0147, 0xD2}, {0x0148, 0xF2}, {0x0150, 0xD5}, {0x0151, 0xF5}, {0x0154, 0xC0}, {0x0155, 0xE0}, {0x0158, 0xD8}, {0x0159, 0xF8}, {0x015A, 0xA6}, {0x015B, 0xB6}, {0x015E, 0xAA}, {0x015F, 0xBA}, {0x0160, 0xA9}, {0x0161, 0xB9}, {0x0162, 0xDE}, {0x0163, 0xFE}, {0x0164, 0xAB}, {0x0165, 0xBB}, {0x016E, 0xD9}, {0x016F, 0xF9}, {0x0170, 0xDB}, {0x0171, 0xFB}, {0x0179, 0xAC}, {0x017A, 0xBC}, {0x017B, 0xAF}, {0x017C, 0xBF}, {0x017D, 0xAE}, {0x017E, 0xBE}, {0x02C7, 0xB7}, {0x02D8, 0xA2}, {0x02D9, 0xFF}, {0x02DB, 0xB2}, {0x02DD, 0xBD} }; //======================================================== // iso latin 3 // define iso static uint16 s_table_iso8859_3[] = { 0x00A0, 0x0126, 0x02D8, 0x00A3, 0x00A4, 0x0000, 0x0124, 0x00A7, 0x00A8, 0x0130, 0x015E, 0x011E, 0x0134, 0x00AD, 0x0000, 0x017B, 0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x0125, 0x00B7, 0x00B8, 0x0131, 0x015F, 0x011F, 0x0135, 0x00BD, 0x0000, 0x017C, 0x00C0, 0x00C1, 0x00C2, 0x0000, 0x00C4, 0x010A, 0x0108, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x0000, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x0120, 0x00D6, 0x00D7, 0x011C, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x016C, 0x015C, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x0000, 0x00E4, 0x010B, 0x0109, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x0000, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7, 0x011D, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x016D, 0x015D, 0x02D9 }; static CP_ISO_UINT_TABLE s_rtable_iso8859_3[] = { {0x00A0, 0xA0}, {0x00A3, 0xA3}, {0x00A4, 0xA4}, {0x00A7, 0xA7}, {0x00A8, 0xA8}, {0x00AD, 0xAD}, {0x00B0, 0xB0}, {0x00B2, 0xB2}, {0x00B3, 0xB3}, {0x00B4, 0xB4}, {0x00B5, 0xB5}, {0x00B7, 0xB7}, {0x00B8, 0xB8}, {0x00BD, 0xBD}, {0x00C0, 0xC0}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C4, 0xC4}, {0x00C7, 0xC7}, {0x00C8, 0xC8}, {0x00C9, 0xC9}, {0x00CA, 0xCA}, {0x00CB, 0xCB}, {0x00CC, 0xCC}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00CF, 0xCF}, {0x00D1, 0xD1}, {0x00D2, 0xD2}, {0x00D3, 0xD3}, {0x00D4, 0xD4}, {0x00D6, 0xD6}, {0x00D7, 0xD7}, {0x00D9, 0xD9}, {0x00DA, 0xDA}, {0x00DB, 0xDB}, {0x00DC, 0xDC}, {0x00DF, 0xDF}, {0x00E0, 0xE0}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E4, 0xE4}, {0x00E7, 0xE7}, {0x00E8, 0xE8}, {0x00E9, 0xE9}, {0x00EA, 0xEA}, {0x00EB, 0xEB}, {0x00EC, 0xEC}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00EF, 0xEF}, {0x00F1, 0xF1}, {0x00F2, 0xF2}, {0x00F3, 0xF3}, {0x00F4, 0xF4}, {0x00F6, 0xF6}, {0x00F7, 0xF7}, {0x00F9, 0xF9}, {0x00FA, 0xFA}, {0x00FB, 0xFB}, {0x00FC, 0xFC}, {0x0108, 0xC6}, {0x0109, 0xE6}, {0x010A, 0xC5}, {0x010B, 0xE5}, {0x011C, 0xD8}, {0x011D, 0xF8}, {0x011E, 0xAB}, {0x011F, 0xBB}, {0x0120, 0xD5}, {0x0121, 0xF5}, {0x0124, 0xA6}, {0x0125, 0xB6}, {0x0126, 0xA1}, {0x0127, 0xB1}, {0x0130, 0xA9}, {0x0131, 0xB9}, {0x0134, 0xAC}, {0x0135, 0xBC}, {0x015C, 0xDE}, {0x015D, 0xFE}, {0x015E, 0xAA}, {0x015F, 0xBA}, {0x016C, 0xDD}, {0x016D, 0xFD}, {0x017B, 0xAF}, {0x017C, 0xBF}, {0x02D8, 0xA2}, {0x02D9, 0xFF} }; //======================================================== // iso latin 4 // define iso static uint16 s_table_iso8859_4[] = { 0x00A0, 0x0104, 0x0138, 0x0156, 0x00A4, 0x0128, 0x013B, 0x00A7, 0x00A8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00AD, 0x017D, 0x00AF, 0x00B0, 0x0105, 0x02DB, 0x0157, 0x00B4, 0x0129, 0x013C, 0x02C7, 0x00B8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014A, 0x017E, 0x014B, 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x012A, 0x0110, 0x0145, 0x014C, 0x0136, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x0168, 0x016A, 0x00DF, 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x012B, 0x0111, 0x0146, 0x014D, 0x0137, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x0169, 0x016B, 0x02D9 }; static CP_ISO_UINT_TABLE s_rtable_iso8859_4[] = { {0x00A0, 0xA0}, {0x00A4, 0xA4}, {0x00A7, 0xA7}, {0x00A8, 0xA8}, {0x00AD, 0xAD}, {0x00AF, 0xAF}, {0x00B0, 0xB0}, {0x00B4, 0xB4}, {0x00B8, 0xB8}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C3, 0xC3}, {0x00C4, 0xC4}, {0x00C5, 0xC5}, {0x00C6, 0xC6}, {0x00C9, 0xC9}, {0x00CB, 0xCB}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00D4, 0xD4}, {0x00D5, 0xD5}, {0x00D6, 0xD6}, {0x00D7, 0xD7}, {0x00D8, 0xD8}, {0x00DA, 0xDA}, {0x00DB, 0xDB}, {0x00DC, 0xDC}, {0x00DF, 0xDF}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E3, 0xE3}, {0x00E4, 0xE4}, {0x00E5, 0xE5}, {0x00E6, 0xE6}, {0x00E9, 0xE9}, {0x00EB, 0xEB}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00F4, 0xF4}, {0x00F5, 0xF5}, {0x00F6, 0xF6}, {0x00F7, 0xF7}, {0x00F8, 0xF8}, {0x00FA, 0xFA}, {0x00FB, 0xFB}, {0x00FC, 0xFC}, {0x0100, 0xC0}, {0x0101, 0xE0}, {0x0104, 0xA1}, {0x0105, 0xB1}, {0x010C, 0xC8}, {0x010D, 0xE8}, {0x0110, 0xD0}, {0x0111, 0xF0}, {0x0112, 0xAA}, {0x0113, 0xBA}, {0x0116, 0xCC}, {0x0117, 0xEC}, {0x0118, 0xCA}, {0x0119, 0xEA}, {0x0122, 0xAB}, {0x0123, 0xBB}, {0x0128, 0xA5}, {0x0129, 0xB5}, {0x012A, 0xCF}, {0x012B, 0xEF}, {0x012E, 0xC7}, {0x012F, 0xE7}, {0x0136, 0xD3}, {0x0137, 0xF3}, {0x0138, 0xA2}, {0x013B, 0xA6}, {0x013C, 0xB6}, {0x0145, 0xD1}, {0x0146, 0xF1}, {0x014A, 0xBD}, {0x014B, 0xBF}, {0x014C, 0xD2}, {0x014D, 0xF2}, {0x0156, 0xA3}, {0x0157, 0xB3}, {0x0160, 0xA9}, {0x0161, 0xB9}, {0x0166, 0xAC}, {0x0167, 0xBC}, {0x0168, 0xDD}, {0x0169, 0xFD}, {0x016A, 0xDE}, {0x016B, 0xFE}, {0x0172, 0xD9}, {0x0173, 0xF9}, {0x017D, 0xAE}, {0x017E, 0xBE}, {0x02C7, 0xB7}, {0x02D9, 0xFF}, {0x02DB, 0xB2} }; //======================================================== // iso Cyrillic // define iso static uint16 s_table_iso8859_5[] = { 0x00A0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x00AD, 0x040E, 0x040F, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x00A7, 0x045E, 0x045F }; static CP_ISO_UINT_TABLE s_rtable_iso8859_5[] = { {0x00A0, 0xA0}, {0x00A7, 0xFD}, {0x00AD, 0xAD}, {0x0401, 0xA1}, {0x0402, 0xA2}, {0x0403, 0xA3}, {0x0404, 0xA4}, {0x0405, 0xA5}, {0x0406, 0xA6}, {0x0407, 0xA7}, {0x0408, 0xA8}, {0x0409, 0xA9}, {0x040A, 0xAA}, {0x040B, 0xAB}, {0x040C, 0xAC}, {0x040E, 0xAE}, {0x040F, 0xAF}, {0x0410, 0xB0}, {0x0411, 0xB1}, {0x0412, 0xB2}, {0x0413, 0xB3}, {0x0414, 0xB4}, {0x0415, 0xB5}, {0x0416, 0xB6}, {0x0417, 0xB7}, {0x0418, 0xB8}, {0x0419, 0xB9}, {0x041A, 0xBA}, {0x041B, 0xBB}, {0x041C, 0xBC}, {0x041D, 0xBD}, {0x041E, 0xBE}, {0x041F, 0xBF}, {0x0420, 0xC0}, {0x0421, 0xC1}, {0x0422, 0xC2}, {0x0423, 0xC3}, {0x0424, 0xC4}, {0x0425, 0xC5}, {0x0426, 0xC6}, {0x0427, 0xC7}, {0x0428, 0xC8}, {0x0429, 0xC9}, {0x042A, 0xCA}, {0x042B, 0xCB}, {0x042C, 0xCC}, {0x042D, 0xCD}, {0x042E, 0xCE}, {0x042F, 0xCF}, {0x0430, 0xD0}, {0x0431, 0xD1}, {0x0432, 0xD2}, {0x0433, 0xD3}, {0x0434, 0xD4}, {0x0435, 0xD5}, {0x0436, 0xD6}, {0x0437, 0xD7}, {0x0438, 0xD8}, {0x0439, 0xD9}, {0x043A, 0xDA}, {0x043B, 0xDB}, {0x043C, 0xDC}, {0x043D, 0xDD}, {0x043E, 0xDE}, {0x043F, 0xDF}, {0x0440, 0xE0}, {0x0441, 0xE1}, {0x0442, 0xE2}, {0x0443, 0xE3}, {0x0444, 0xE4}, {0x0445, 0xE5}, {0x0446, 0xE6}, {0x0447, 0xE7}, {0x0448, 0xE8}, {0x0449, 0xE9}, {0x044A, 0xEA}, {0x044B, 0xEB}, {0x044C, 0xEC}, {0x044D, 0xED}, {0x044E, 0xEE}, {0x044F, 0xEF}, {0x0451, 0xF1}, {0x0452, 0xF2}, {0x0453, 0xF3}, {0x0454, 0xF4}, {0x0455, 0xF5}, {0x0456, 0xF6}, {0x0457, 0xF7}, {0x0458, 0xF8}, {0x0459, 0xF9}, {0x045A, 0xFA}, {0x045B, 0xFB}, {0x045C, 0xFC}, {0x045E, 0xFE}, {0x045F, 0xFF}, {0x2116, 0xF0} }; //======================================================== // iso arabic // define iso static uint16 s_table_iso8859_6[] = { 0x00A0, 0x0000, 0x0000, 0x0000, 0x00A4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x060C, 0x00AD, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x061B, 0x0000, 0x0000, 0x0000, 0x061F, 0x0000, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x0651, 0x0652 }; static CP_ISO_UINT_TABLE s_rtable_iso8859_6[] = { {0x00A0, 0xA0}, {0x00A4, 0xA4}, {0x00AD, 0xAD}, {0x060C, 0xAC}, {0x061B, 0xBB}, {0x061F, 0xBF}, {0x0621, 0xC1}, {0x0622, 0xC2}, {0x0623, 0xC3}, {0x0624, 0xC4}, {0x0625, 0xC5}, {0x0626, 0xC6}, {0x0627, 0xC7}, {0x0628, 0xC8}, {0x0629, 0xC9}, {0x062A, 0xCA}, {0x062B, 0xCB}, {0x062C, 0xCC}, {0x062D, 0xCD}, {0x062E, 0xCE}, {0x062F, 0xCF}, {0x0630, 0xD0}, {0x0631, 0xD1}, {0x0632, 0xD2}, {0x0633, 0xD3}, {0x0634, 0xD4}, {0x0635, 0xD5}, {0x0636, 0xD6}, {0x0637, 0xD7}, {0x0638, 0xD8}, {0x0639, 0xD9}, {0x063A, 0xDA}, {0x0640, 0xE0}, {0x0641, 0xE1}, {0x0642, 0xE2}, {0x0643, 0xE3}, {0x0644, 0xE4}, {0x0645, 0xE5}, {0x0646, 0xE6}, {0x0647, 0xE7}, {0x0648, 0xE8}, {0x0649, 0xE9}, {0x064A, 0xEA}, {0x064B, 0xEB}, {0x064C, 0xEC}, {0x064D, 0xED}, {0x064E, 0xEE}, {0x064F, 0xEF}, {0x0650, 0xF0}, {0x0651, 0xF1}, {0x0652, 0xF2} }; //======================================================== // iso Greek // define iso static uint16 s_table_iso8859_7[] = { 0x00A0, 0x02BD, 0x02BC, 0x00A3, 0x0000, 0x0000, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x0000, 0x00AB, 0x00AC, 0x00AD, 0x0000, 0x2015, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x0385, 0x0386, 0x00B7, 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F, 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE }; static CP_ISO_UINT_TABLE s_rtable_iso8859_7[] = { {0x00A0, 0xA0}, {0x00A3, 0xA3}, {0x00A6, 0xA6}, {0x00A7, 0xA7}, {0x00A8, 0xA8}, {0x00A9, 0xA9}, {0x00AB, 0xAB}, {0x00AC, 0xAC}, {0x00AD, 0xAD}, {0x00B0, 0xB0}, {0x00B1, 0xB1}, {0x00B2, 0xB2}, {0x00B3, 0xB3}, {0x00B7, 0xB7}, {0x00BB, 0xBB}, {0x00BD, 0xBD}, {0x02BC, 0xA2}, {0x02BD, 0xA1}, {0x0384, 0xB4}, {0x0385, 0xB5}, {0x0386, 0xB6}, {0x0388, 0xB8}, {0x0389, 0xB9}, {0x038A, 0xBA}, {0x038C, 0xBC}, {0x038E, 0xBE}, {0x038F, 0xBF}, {0x0390, 0xC0}, {0x0391, 0xC1}, {0x0392, 0xC2}, {0x0393, 0xC3}, {0x0394, 0xC4}, {0x0395, 0xC5}, {0x0396, 0xC6}, {0x0397, 0xC7}, {0x0398, 0xC8}, {0x0399, 0xC9}, {0x039A, 0xCA}, {0x039B, 0xCB}, {0x039C, 0xCC}, {0x039D, 0xCD}, {0x039E, 0xCE}, {0x039F, 0xCF}, {0x03A0, 0xD0}, {0x03A1, 0xD1}, {0x03A3, 0xD3}, {0x03A4, 0xD4}, {0x03A5, 0xD5}, {0x03A6, 0xD6}, {0x03A7, 0xD7}, {0x03A8, 0xD8}, {0x03A9, 0xD9}, {0x03AA, 0xDA}, {0x03AB, 0xDB}, {0x03AC, 0xDC}, {0x03AD, 0xDD}, {0x03AE, 0xDE}, {0x03AF, 0xDF}, {0x03B0, 0xE0}, {0x03B1, 0xE1}, {0x03B2, 0xE2}, {0x03B3, 0xE3}, {0x03B4, 0xE4}, {0x03B5, 0xE5}, {0x03B6, 0xE6}, {0x03B7, 0xE7}, {0x03B8, 0xE8}, {0x03B9, 0xE9}, {0x03BA, 0xEA}, {0x03BB, 0xEB}, {0x03BC, 0xEC}, {0x03BD, 0xED}, {0x03BE, 0xEE}, {0x03BF, 0xEF}, {0x03C0, 0xF0}, {0x03C1, 0xF1}, {0x03C2, 0xF2}, {0x03C3, 0xF3}, {0x03C4, 0xF4}, {0x03C5, 0xF5}, {0x03C6, 0xF6}, {0x03C7, 0xF7}, {0x03C8, 0xF8}, {0x03C9, 0xF9}, {0x03CA, 0xFA}, {0x03CB, 0xFB}, {0x03CC, 0xFC}, {0x03CD, 0xFD}, {0x03CE, 0xFE}, {0x2015, 0xAF} }; //======================================================== // iso Hebrew // define iso static uint16 s_table_iso8859_8[] = { 0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x203E, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2017, 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA }; static CP_ISO_UINT_TABLE s_rtable_iso8859_8[] = { {0x00A0, 0xA0}, {0x00A2, 0xA2}, {0x00A3, 0xA3}, {0x00A4, 0xA4}, {0x00A5, 0xA5}, {0x00A6, 0xA6}, {0x00A7, 0xA7}, {0x00A8, 0xA8}, {0x00A9, 0xA9}, {0x00AB, 0xAB}, {0x00AC, 0xAC}, {0x00AD, 0xAD}, {0x00AE, 0xAE}, {0x00B0, 0xB0}, {0x00B1, 0xB1}, {0x00B2, 0xB2}, {0x00B3, 0xB3}, {0x00B4, 0xB4}, {0x00B5, 0xB5}, {0x00B6, 0xB6}, {0x00B7, 0xB7}, {0x00B8, 0xB8}, {0x00B9, 0xB9}, {0x00BB, 0xBB}, {0x00BC, 0xBC}, {0x00BD, 0xBD}, {0x00BE, 0xBE}, {0x00D7, 0xAA}, {0x00F7, 0xBA}, {0x05D0, 0xE0}, {0x05D1, 0xE1}, {0x05D2, 0xE2}, {0x05D3, 0xE3}, {0x05D4, 0xE4}, {0x05D5, 0xE5}, {0x05D6, 0xE6}, {0x05D7, 0xE7}, {0x05D8, 0xE8}, {0x05D9, 0xE9}, {0x05DA, 0xEA}, {0x05DB, 0xEB}, {0x05DC, 0xEC}, {0x05DD, 0xED}, {0x05DE, 0xEE}, {0x05DF, 0xEF}, {0x05E0, 0xF0}, {0x05E1, 0xF1}, {0x05E2, 0xF2}, {0x05E3, 0xF3}, {0x05E4, 0xF4}, {0x05E5, 0xF5}, {0x05E6, 0xF6}, {0x05E7, 0xF7}, {0x05E8, 0xF8}, {0x05E9, 0xF9}, {0x05EA, 0xFA}, {0x2017, 0xDF}, {0x203E, 0xAF} }; //======================================================== // iso latin 5 // define iso static uint16 s_table_iso8859_9[] = { 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF }; static CP_ISO_UINT_TABLE s_rtable_iso8859_9[] = { {0x00A0, 0xA0}, {0x00A1, 0xA1}, {0x00A2, 0xA2}, {0x00A3, 0xA3}, {0x00A4, 0xA4}, {0x00A5, 0xA5}, {0x00A6, 0xA6}, {0x00A7, 0xA7}, {0x00A8, 0xA8}, {0x00A9, 0xA9}, {0x00AA, 0xAA}, {0x00AB, 0xAB}, {0x00AC, 0xAC}, {0x00AD, 0xAD}, {0x00AE, 0xAE}, {0x00AF, 0xAF}, {0x00B0, 0xB0}, {0x00B1, 0xB1}, {0x00B2, 0xB2}, {0x00B3, 0xB3}, {0x00B4, 0xB4}, {0x00B5, 0xB5}, {0x00B6, 0xB6}, {0x00B7, 0xB7}, {0x00B8, 0xB8}, {0x00B9, 0xB9}, {0x00BA, 0xBA}, {0x00BB, 0xBB}, {0x00BC, 0xBC}, {0x00BD, 0xBD}, {0x00BE, 0xBE}, {0x00BF, 0xBF}, {0x00C0, 0xC0}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C3, 0xC3}, {0x00C4, 0xC4}, {0x00C5, 0xC5}, {0x00C6, 0xC6}, {0x00C7, 0xC7}, {0x00C8, 0xC8}, {0x00C9, 0xC9}, {0x00CA, 0xCA}, {0x00CB, 0xCB}, {0x00CC, 0xCC}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00CF, 0xCF}, {0x00D1, 0xD1}, {0x00D2, 0xD2}, {0x00D3, 0xD3}, {0x00D4, 0xD4}, {0x00D5, 0xD5}, {0x00D6, 0xD6}, {0x00D7, 0xD7}, {0x00D8, 0xD8}, {0x00D9, 0xD9}, {0x00DA, 0xDA}, {0x00DB, 0xDB}, {0x00DC, 0xDC}, {0x00DF, 0xDF}, {0x00E0, 0xE0}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E3, 0xE3}, {0x00E4, 0xE4}, {0x00E5, 0xE5}, {0x00E6, 0xE6}, {0x00E7, 0xE7}, {0x00E8, 0xE8}, {0x00E9, 0xE9}, {0x00EA, 0xEA}, {0x00EB, 0xEB}, {0x00EC, 0xEC}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00EF, 0xEF}, {0x00F1, 0xF1}, {0x00F2, 0xF2}, {0x00F3, 0xF3}, {0x00F4, 0xF4}, {0x00F5, 0xF5}, {0x00F6, 0xF6}, {0x00F7, 0xF7}, {0x00F8, 0xF8}, {0x00F9, 0xF9}, {0x00FA, 0xFA}, {0x00FB, 0xFB}, {0x00FC, 0xFC}, {0x00FF, 0xFF}, {0x011E, 0xD0}, {0x011F, 0xF0}, {0x0130, 0xDD}, {0x0131, 0xFD}, {0x015E, 0xDE}, {0x015F, 0xFE} }; //======================================================== // iso latin 6 // define iso static uint16 s_table_iso8859_10[] = { 0x00A0, 0x0104, 0x0112, 0x0122, 0x012A, 0x0128, 0x0136, 0x00A7, 0x013B, 0x0110, 0x0160, 0x0166, 0x017D, 0x00AD, 0x016A, 0x014A, 0x00B0, 0x0105, 0x0113, 0x0123, 0x012B, 0x0129, 0x0137, 0x00B7, 0x013C, 0x0111, 0x0161, 0x0167, 0x017E, 0x2015, 0x016B, 0x014B, 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x0145, 0x014C, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0168, 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x0146, 0x014D, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0169, 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x0138 }; static CP_ISO_UINT_TABLE s_rtable_iso8859_10[] = { {0x00A0, 0xA0}, {0x00A7, 0xA7}, {0x00AD, 0xAD}, {0x00B0, 0xB0}, {0x00B7, 0xB7}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C3, 0xC3}, {0x00C4, 0xC4}, {0x00C5, 0xC5}, {0x00C6, 0xC6}, {0x00C9, 0xC9}, {0x00CB, 0xCB}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00CF, 0xCF}, {0x00D0, 0xD0}, {0x00D3, 0xD3}, {0x00D4, 0xD4}, {0x00D5, 0xD5}, {0x00D6, 0xD6}, {0x00D8, 0xD8}, {0x00DA, 0xDA}, {0x00DB, 0xDB}, {0x00DC, 0xDC}, {0x00DD, 0xDD}, {0x00DE, 0xDE}, {0x00DF, 0xDF}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E3, 0xE3}, {0x00E4, 0xE4}, {0x00E5, 0xE5}, {0x00E6, 0xE6}, {0x00E9, 0xE9}, {0x00EB, 0xEB}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00EF, 0xEF}, {0x00F0, 0xF0}, {0x00F3, 0xF3}, {0x00F4, 0xF4}, {0x00F5, 0xF5}, {0x00F6, 0xF6}, {0x00F8, 0xF8}, {0x00FA, 0xFA}, {0x00FB, 0xFB}, {0x00FC, 0xFC}, {0x00FD, 0xFD}, {0x00FE, 0xFE}, {0x0100, 0xC0}, {0x0101, 0xE0}, {0x0104, 0xA1}, {0x0105, 0xB1}, {0x010C, 0xC8}, {0x010D, 0xE8}, {0x0110, 0xA9}, {0x0111, 0xB9}, {0x0112, 0xA2}, {0x0113, 0xB2}, {0x0116, 0xCC}, {0x0117, 0xEC}, {0x0118, 0xCA}, {0x0119, 0xEA}, {0x0122, 0xA3}, {0x0123, 0xB3}, {0x0128, 0xA5}, {0x0129, 0xB5}, {0x012A, 0xA4}, {0x012B, 0xB4}, {0x012E, 0xC7}, {0x012F, 0xE7}, {0x0136, 0xA6}, {0x0137, 0xB6}, {0x0138, 0xFF}, {0x013B, 0xA8}, {0x013C, 0xB8}, {0x0145, 0xD1}, {0x0146, 0xF1}, {0x014A, 0xAF}, {0x014B, 0xBF}, {0x014C, 0xD2}, {0x014D, 0xF2}, {0x0160, 0xAA}, {0x0161, 0xBA}, {0x0166, 0xAB}, {0x0167, 0xBB}, {0x0168, 0xD7}, {0x0169, 0xF7}, {0x016A, 0xAE}, {0x016B, 0xBE}, {0x0172, 0xD9}, {0x0173, 0xF9}, {0x017D, 0xAC}, {0x017E, 0xBC}, {0x2015, 0xBD} }; //======================================================== // iso Thai // define iso static uint16 s_table_iso8859_11[] = { 0x0000, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07, 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F, 0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17, 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F, 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27, 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F, 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, 0x0E38, 0x0E39, 0x0E3A, 0x0000, 0x0000, 0x0000, 0x0E3F, 0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F, 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59, 0x0E5A, 0x0E5B }; static CP_ISO_UINT_TABLE s_rtable_iso8859_11[] = { {0x0E01, 0xA1}, {0x0E02, 0xA2}, {0x0E03, 0xA3}, {0x0E04, 0xA4}, {0x0E05, 0xA5}, {0x0E06, 0xA6}, {0x0E07, 0xA7}, {0x0E08, 0xA8}, {0x0E09, 0xA9}, {0x0E0A, 0xAA}, {0x0E0B, 0xAB}, {0x0E0C, 0xAC}, {0x0E0D, 0xAD}, {0x0E0E, 0xAE}, {0x0E0F, 0xAF}, {0x0E10, 0xB0}, {0x0E11, 0xB1}, {0x0E12, 0xB2}, {0x0E13, 0xB3}, {0x0E14, 0xB4}, {0x0E15, 0xB5}, {0x0E16, 0xB6}, {0x0E17, 0xB7}, {0x0E18, 0xB8}, {0x0E19, 0xB9}, {0x0E1A, 0xBA}, {0x0E1B, 0xBB}, {0x0E1C, 0xBC}, {0x0E1D, 0xBD}, {0x0E1E, 0xBE}, {0x0E1F, 0xBF}, {0x0E20, 0xC0}, {0x0E21, 0xC1}, {0x0E22, 0xC2}, {0x0E23, 0xC3}, {0x0E24, 0xC4}, {0x0E25, 0xC5}, {0x0E26, 0xC6}, {0x0E27, 0xC7}, {0x0E28, 0xC8}, {0x0E29, 0xC9}, {0x0E2A, 0xCA}, {0x0E2B, 0xCB}, {0x0E2C, 0xCC}, {0x0E2D, 0xCD}, {0x0E2E, 0xCE}, {0x0E2F, 0xCF}, {0x0E30, 0xD0}, {0x0E31, 0xD1}, {0x0E32, 0xD2}, {0x0E33, 0xD3}, {0x0E34, 0xD4}, {0x0E35, 0xD5}, {0x0E36, 0xD6}, {0x0E37, 0xD7}, {0x0E38, 0xD8}, {0x0E39, 0xD9}, {0x0E3A, 0xDA}, {0x0E3F, 0xDF}, {0x0E40, 0xE0}, {0x0E41, 0xE1}, {0x0E42, 0xE2}, {0x0E43, 0xE3}, {0x0E44, 0xE4}, {0x0E45, 0xE5}, {0x0E46, 0xE6}, {0x0E47, 0xE7}, {0x0E48, 0xE8}, {0x0E49, 0xE9}, {0x0E4A, 0xEA}, {0x0E4B, 0xEB}, {0x0E4C, 0xEC}, {0x0E4D, 0xED}, {0x0E4E, 0xEE}, {0x0E4F, 0xEF}, {0x0E50, 0xF0}, {0x0E51, 0xF1}, {0x0E52, 0xF2}, {0x0E53, 0xF3}, {0x0E54, 0xF4}, {0x0E55, 0xF5}, {0x0E56, 0xF6}, {0x0E57, 0xF7}, {0x0E58, 0xF8}, {0x0E59, 0xF9}, {0x0E5A, 0xFA}, {0x0E5B, 0xFB} }; //======================================================== // iso latin 7 // define iso static uint16 s_table_iso8859_13[] = { 0x00A0, 0x201D, 0x00A2, 0x00A3, 0x00A4, 0x201E, 0x00A6, 0x00A7, 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x201C, 0x00B5, 0x00B6, 0x00B7, 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6, 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B, 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF, 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C, 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x2019 }; static CP_ISO_UINT_TABLE s_rtable_iso8859_13[] = { {0x00A0, 0xA0}, {0x00A2, 0xA2}, {0x00A3, 0xA3}, {0x00A4, 0xA4}, {0x00A6, 0xA6}, {0x00A7, 0xA7}, {0x00A9, 0xA9}, {0x00AB, 0xAB}, {0x00AC, 0xAC}, {0x00AD, 0xAD}, {0x00AE, 0xAE}, {0x00B0, 0xB0}, {0x00B1, 0xB1}, {0x00B2, 0xB2}, {0x00B3, 0xB3}, {0x00B5, 0xB5}, {0x00B6, 0xB6}, {0x00B7, 0xB7}, {0x00B9, 0xB9}, {0x00BB, 0xBB}, {0x00BC, 0xBC}, {0x00BD, 0xBD}, {0x00BE, 0xBE}, {0x00C4, 0xC4}, {0x00C5, 0xC5}, {0x00C6, 0xAF}, {0x00C9, 0xC9}, {0x00D3, 0xD3}, {0x00D5, 0xD5}, {0x00D6, 0xD6}, {0x00D7, 0xD7}, {0x00D8, 0xA8}, {0x00DC, 0xDC}, {0x00DF, 0xDF}, {0x00E4, 0xE4}, {0x00E5, 0xE5}, {0x00E6, 0xBF}, {0x00E9, 0xE9}, {0x00F3, 0xF3}, {0x00F5, 0xF5}, {0x00F6, 0xF6}, {0x00F7, 0xF7}, {0x00F8, 0xB8}, {0x00FC, 0xFC}, {0x0100, 0xC2}, {0x0101, 0xE2}, {0x0104, 0xC0}, {0x0105, 0xE0}, {0x0106, 0xC3}, {0x0107, 0xE3}, {0x010C, 0xC8}, {0x010D, 0xE8}, {0x0112, 0xC7}, {0x0113, 0xE7}, {0x0116, 0xCB}, {0x0117, 0xEB}, {0x0118, 0xC6}, {0x0119, 0xE6}, {0x0122, 0xCC}, {0x0123, 0xEC}, {0x012A, 0xCE}, {0x012B, 0xEE}, {0x012E, 0xC1}, {0x012F, 0xE1}, {0x0136, 0xCD}, {0x0137, 0xED}, {0x013B, 0xCF}, {0x013C, 0xEF}, {0x0141, 0xD9}, {0x0142, 0xF9}, {0x0143, 0xD1}, {0x0144, 0xF1}, {0x0145, 0xD2}, {0x0146, 0xF2}, {0x014C, 0xD4}, {0x014D, 0xF4}, {0x0156, 0xAA}, {0x0157, 0xBA}, {0x015A, 0xDA}, {0x015B, 0xFA}, {0x0160, 0xD0}, {0x0161, 0xF0}, {0x016A, 0xDB}, {0x016B, 0xFB}, {0x0172, 0xD8}, {0x0173, 0xF8}, {0x0179, 0xCA}, {0x017A, 0xEA}, {0x017B, 0xDD}, {0x017C, 0xFD}, {0x017D, 0xDE}, {0x017E, 0xFE}, {0x2019, 0xFF}, {0x201C, 0xB4}, {0x201D, 0xA1}, {0x201E, 0xA5} }; //======================================================== // iso latin 8 // define iso static uint16 s_table_iso8859_14[] = { 0x00A0, 0x1E02, 0x1E03, 0x00A3, 0x010A, 0x010B, 0x1E0A, 0x00A7, 0x1E80, 0x00A9, 0x1E82, 0x1E0B, 0x1EF2, 0x00AD, 0x00AE, 0x0178, 0x1E1E, 0x1E1F, 0x0120, 0x0121, 0x1E40, 0x1E41, 0x00B6, 0x1E56, 0x1E81, 0x1E57, 0x1E83, 0x1E60, 0x1EF3, 0x1E84, 0x1E85, 0x1E61, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x0174, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x1E6A, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x0176, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x0175, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x1E6B, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x0177, 0x00FF }; static CP_ISO_UINT_TABLE s_rtable_iso8859_14[] = { {0x00A0, 0xA0}, {0x00A3, 0xA3}, {0x00A7, 0xA7}, {0x00A9, 0xA9}, {0x00AD, 0xAD}, {0x00AE, 0xAE}, {0x00B6, 0xB6}, {0x00C0, 0xC0}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C3, 0xC3}, {0x00C4, 0xC4}, {0x00C5, 0xC5}, {0x00C6, 0xC6}, {0x00C7, 0xC7}, {0x00C8, 0xC8}, {0x00C9, 0xC9}, {0x00CA, 0xCA}, {0x00CB, 0xCB}, {0x00CC, 0xCC}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00CF, 0xCF}, {0x00D1, 0xD1}, {0x00D2, 0xD2}, {0x00D3, 0xD3}, {0x00D4, 0xD4}, {0x00D5, 0xD5}, {0x00D6, 0xD6}, {0x00D8, 0xD8}, {0x00D9, 0xD9}, {0x00DA, 0xDA}, {0x00DB, 0xDB}, {0x00DC, 0xDC}, {0x00DD, 0xDD}, {0x00DF, 0xDF}, {0x00E0, 0xE0}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E3, 0xE3}, {0x00E4, 0xE4}, {0x00E5, 0xE5}, {0x00E6, 0xE6}, {0x00E7, 0xE7}, {0x00E8, 0xE8}, {0x00E9, 0xE9}, {0x00EA, 0xEA}, {0x00EB, 0xEB}, {0x00EC, 0xEC}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00EF, 0xEF}, {0x00F1, 0xF1}, {0x00F2, 0xF2}, {0x00F3, 0xF3}, {0x00F4, 0xF4}, {0x00F5, 0xF5}, {0x00F6, 0xF6}, {0x00F8, 0xF8}, {0x00F9, 0xF9}, {0x00FA, 0xFA}, {0x00FB, 0xFB}, {0x00FC, 0xFC}, {0x00FD, 0xFD}, {0x00FF, 0xFF}, {0x010A, 0xA4}, {0x010B, 0xA5}, {0x0120, 0xB2}, {0x0121, 0xB3}, {0x0174, 0xD0}, {0x0175, 0xF0}, {0x0176, 0xDE}, {0x0177, 0xFE}, {0x0178, 0xAF}, {0x1E02, 0xA1}, {0x1E03, 0xA2}, {0x1E0A, 0xA6}, {0x1E0B, 0xAB}, {0x1E1E, 0xB0}, {0x1E1F, 0xB1}, {0x1E40, 0xB4}, {0x1E41, 0xB5}, {0x1E56, 0xB7}, {0x1E57, 0xB9}, {0x1E60, 0xBB}, {0x1E61, 0xBF}, {0x1E6A, 0xD7}, {0x1E6B, 0xF7}, {0x1E80, 0xA8}, {0x1E81, 0xB8}, {0x1E82, 0xAA}, {0x1E83, 0xBA}, {0x1E84, 0xBD}, {0x1E85, 0xBE}, {0x1EF2, 0xAC}, {0x1EF3, 0xBC} }; //======================================================== // iso latin 9 // define iso static uint16 s_table_iso8859_15[] = { 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0160, 0x00A7, 0x0161, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x017D, 0x00B5, 0x00B6, 0x00B7, 0x017E, 0x00B9, 0x00BA, 0x00BB, 0x0152, 0x0153, 0x0178, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF }; static CP_ISO_UINT_TABLE s_rtable_iso8859_15[] = { {0x00A0, 0xA0}, {0x00A1, 0xA1}, {0x00A2, 0xA2}, {0x00A3, 0xA3}, {0x00A5, 0xA5}, {0x00A7, 0xA7}, {0x00A9, 0xA9}, {0x00AA, 0xAA}, {0x00AB, 0xAB}, {0x00AC, 0xAC}, {0x00AD, 0xAD}, {0x00AE, 0xAE}, {0x00AF, 0xAF}, {0x00B0, 0xB0}, {0x00B1, 0xB1}, {0x00B2, 0xB2}, {0x00B3, 0xB3}, {0x00B5, 0xB5}, {0x00B6, 0xB6}, {0x00B7, 0xB7}, {0x00B9, 0xB9}, {0x00BA, 0xBA}, {0x00BB, 0xBB}, {0x00BF, 0xBF}, {0x00C0, 0xC0}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C3, 0xC3}, {0x00C4, 0xC4}, {0x00C5, 0xC5}, {0x00C6, 0xC6}, {0x00C7, 0xC7}, {0x00C8, 0xC8}, {0x00C9, 0xC9}, {0x00CA, 0xCA}, {0x00CB, 0xCB}, {0x00CC, 0xCC}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00CF, 0xCF}, {0x00D0, 0xD0}, {0x00D1, 0xD1}, {0x00D2, 0xD2}, {0x00D3, 0xD3}, {0x00D4, 0xD4}, {0x00D5, 0xD5}, {0x00D6, 0xD6}, {0x00D7, 0xD7}, {0x00D8, 0xD8}, {0x00D9, 0xD9}, {0x00DA, 0xDA}, {0x00DB, 0xDB}, {0x00DC, 0xDC}, {0x00DD, 0xDD}, {0x00DE, 0xDE}, {0x00DF, 0xDF}, {0x00E0, 0xE0}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E3, 0xE3}, {0x00E4, 0xE4}, {0x00E5, 0xE5}, {0x00E6, 0xE6}, {0x00E7, 0xE7}, {0x00E8, 0xE8}, {0x00E9, 0xE9}, {0x00EA, 0xEA}, {0x00EB, 0xEB}, {0x00EC, 0xEC}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00EF, 0xEF}, {0x00F0, 0xF0}, {0x00F1, 0xF1}, {0x00F2, 0xF2}, {0x00F3, 0xF3}, {0x00F4, 0xF4}, {0x00F5, 0xF5}, {0x00F6, 0xF6}, {0x00F7, 0xF7}, {0x00F8, 0xF8}, {0x00F9, 0xF9}, {0x00FA, 0xFA}, {0x00FB, 0xFB}, {0x00FC, 0xFC}, {0x00FD, 0xFD}, {0x00FE, 0xFE}, {0x00FF, 0xFF}, {0x0152, 0xBC}, {0x0153, 0xBD}, {0x0160, 0xA6}, {0x0161, 0xA8}, {0x0178, 0xBE}, {0x017D, 0xB4}, {0x017E, 0xB8}, {0x20AC, 0xA4} }; /* end of trans_tables.def */ engine/trans_tables.h000066400000000000000000015673051176363201700152040ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: trans_tables.def $Id: trans_tables.def,v 1.1.1.1 2006/10/08 15:05:11 gian Exp $ Transcode tables ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer ago 23 2006 Last modified because: ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. In order to use this file in its compiled form, this source or part of it you have to read, understand and accept the conditions that are stated in the LICENSE file that comes boundled with this package. */ /** \file Transcode tables. */ //============================================ // CP 1252 // struct Table { void *table; /*It's depend on which kind of table you'd like:*/ uint32 len; }; static uint16 s_table_cp1252[] = { 0x20AC, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x017D, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x017E, 0x0178, // 0x20 - 0x7f untranslated 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF }; //============================================ // CP 1252 - 128 - 255 // static CP_ISO_UINT_TABLE s_rtable_cp1252[] = { {0x00A0, 0xA0}, {0x00A1, 0xA1}, {0x00A2, 0xA2}, {0x00A3, 0xA3}, {0x00A4, 0xA4}, {0x00A5, 0xA5}, {0x00A6, 0xA6}, {0x00A7, 0xA7}, {0x00A8, 0xA8}, {0x00A9, 0xA9}, {0x00AA, 0xAA}, {0x00AB, 0xAB}, {0x00AC, 0xAC}, {0x00AD, 0xAD}, {0x00AE, 0xAE}, {0x00AF, 0xAF}, {0x00B0, 0xB0}, {0x00B1, 0xB1}, {0x00B2, 0xB2}, {0x00B3, 0xB3}, {0x00B4, 0xB4}, {0x00B5, 0xB5}, {0x00B6, 0xB6}, {0x00B7, 0xB7}, {0x00B8, 0xB8}, {0x00B9, 0xB9}, {0x00BA, 0xBA}, {0x00BB, 0xBB}, {0x00BC, 0xBC}, {0x00BD, 0xBD}, {0x00BE, 0xBE}, {0x00BF, 0xBF}, {0x00C0, 0xC0}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C3, 0xC3}, {0x00C4, 0xC4}, {0x00C5, 0xC5}, {0x00C6, 0xC6}, {0x00C7, 0xC7}, {0x00C8, 0xC8}, {0x00C9, 0xC9}, {0x00CA, 0xCA}, {0x00CB, 0xCB}, {0x00CC, 0xCC}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00CF, 0xCF}, {0x00D0, 0xD0}, {0x00D1, 0xD1}, {0x00D2, 0xD2}, {0x00D3, 0xD3}, {0x00D4, 0xD4}, {0x00D5, 0xD5}, {0x00D6, 0xD6}, {0x00D7, 0xD7}, {0x00D8, 0xD8}, {0x00D9, 0xD9}, {0x00DA, 0xDA}, {0x00DB, 0xDB}, {0x00DC, 0xDC}, {0x00DD, 0xDD}, {0x00DE, 0xDE}, {0x00DF, 0xDF}, {0x00E0, 0xE0}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E3, 0xE3}, {0x00E4, 0xE4}, {0x00E5, 0xE5}, {0x00E6, 0xE6}, {0x00E7, 0xE7}, {0x00E8, 0xE8}, {0x00E9, 0xE9}, {0x00EA, 0xEA}, {0x00EB, 0xEB}, {0x00EC, 0xEC}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00EF, 0xEF}, {0x00F0, 0xF0}, {0x00F1, 0xF1}, {0x00F2, 0xF2}, {0x00F3, 0xF3}, {0x00F4, 0xF4}, {0x00F5, 0xF5}, {0x00F6, 0xF6}, {0x00F7, 0xF7}, {0x00F8, 0xF8}, {0x00F9, 0xF9}, {0x00FA, 0xFA}, {0x00FB, 0xFB}, {0x00FC, 0xFC}, {0x00FD, 0xFD}, {0x00FE, 0xFE}, {0x00FF, 0xFF}, {0x0152, 0x8C}, {0x0153, 0x9C}, {0x0160, 0x8A}, {0x0161, 0x9A}, {0x0178, 0x9F}, {0x017D, 0x8E}, {0x017E, 0x9E}, {0x0192, 0x83}, {0x02C6, 0x88}, {0x02DC, 0x98}, {0x2013, 0x96}, {0x2014, 0x97}, {0x2018, 0x91}, {0x2019, 0x92}, {0x201A, 0x82}, {0x201C, 0x93}, {0x201D, 0x94}, {0x201E, 0x84}, {0x2020, 0x86}, {0x2021, 0x87}, {0x2022, 0x95}, {0x2026, 0x85}, {0x2030, 0x89}, {0x2039, 0x8B}, {0x203A, 0x9B}, {0x20AC, 0x80}, {0x2122, 0x99} }; //============================================ // OEM - IBM437 // static uint16 s_table_IBM437[] = { /* 0x2007, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C, //0x10 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8, 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC, */ // 0x20 - 0x7f untranslated //0x80 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, //0x90 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, //0xA0 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, //0xB0 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, //0xC0 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, //0xD0 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, //0xE0 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, //0xF0 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 }; //============================================ // OEM - IBM437 - reverse // static CP_ISO_UINT_TABLE s_rtable_IBM437[] = { { 0, 0 }, // to count unpair { 0xA0 , 255 }, { 0xA1 , 173 }, { 0xA2 , 155 }, { 0xA3 , 156 }, { 0xA5 , 157 }, { 0xA7 , 21 }, { 0xAA , 166 }, { 0xAB , 174 }, { 0xAC , 170 }, { 0xB0 , 248 }, { 0xB1 , 241 }, { 0xB2 , 253 }, { 0xB5 , 230 }, { 0xB6 , 20 }, { 0xB7 , 250 }, { 0xBA , 167 }, { 0xBB , 175 }, { 0xBC , 172 }, { 0xBD , 171 }, { 0xBF , 168 }, { 0xC4 , 142 }, { 0xC5 , 143 }, { 0xC6 , 146 }, { 0xC7 , 128 }, { 0xC9 , 144 }, { 0xD1 , 165 }, { 0xD6 , 153 }, { 0xDC , 154 }, { 0xDF , 225 }, { 0xE0 , 133 }, { 0xE1 , 160 }, { 0xE2 , 131 }, { 0xE4 , 132 }, { 0xE5 , 134 }, { 0xE6 , 145 }, { 0xE7 , 135 }, { 0xE8 , 138 }, { 0xE9 , 130 }, { 0xEA , 136 }, { 0xEB , 137 }, { 0xEC , 141 }, { 0xED , 161 }, { 0xEE , 140 }, { 0xEF , 139 }, { 0xF1 , 164 }, { 0xF2 , 149 }, { 0xF3 , 162 }, { 0xF4 , 147 }, { 0xF6 , 148 }, { 0xF7 , 246 }, { 0xF9 , 151 }, { 0xFA , 163 }, { 0xFB , 150 }, { 0xFC , 129 }, { 0xFF , 152 }, { 0x192 , 159 }, { 0x393 , 226 }, { 0x398 , 233 }, { 0x3A3 , 228 }, { 0x3A6 , 232 }, { 0x3A9 , 234 }, { 0x3B1 , 224 }, { 0x3B4 , 235 }, { 0x3B5 , 238 }, { 0x3C0 , 227 }, { 0x3C3 , 229 }, { 0x3C4 , 231 }, { 0x3C6 , 237 }, { 0x2007 , 0 }, { 0x2022 , 7 }, { 0x203C , 19 }, { 0x207F , 252 }, { 0x20A7 , 158 }, { 0x2190 , 27 }, { 0x2191 , 24 }, { 0x2192 , 26 }, { 0x2193 , 25 }, { 0x2194 , 29 }, { 0x2195 , 18 }, { 0x21A8 , 23 }, { 0x2219 , 249 }, { 0x221A , 251 }, { 0x221E , 236 }, { 0x221F , 28 }, { 0x2229 , 239 }, { 0x2248 , 247 }, { 0x2261 , 240 }, { 0x2264 , 243 }, { 0x2265 , 242 }, { 0x2310 , 169 }, { 0x2320 , 244 }, { 0x2321 , 245 }, { 0x2500 , 196 }, { 0x2502 , 179 }, { 0x250C , 218 }, { 0x2510 , 191 }, { 0x2514 , 192 }, { 0x2518 , 217 }, { 0x251C , 195 }, { 0x2524 , 180 }, { 0x252C , 194 }, { 0x2534 , 193 }, { 0x253C , 197 }, { 0x2550 , 205 }, { 0x2551 , 186 }, { 0x2552 , 213 }, { 0x2553 , 214 }, { 0x2554 , 201 }, { 0x2555 , 184 }, { 0x2556 , 183 }, { 0x2557 , 187 }, { 0x2558 , 212 }, { 0x2559 , 211 }, { 0x255A , 200 }, { 0x255B , 190 }, { 0x255C , 189 }, { 0x255D , 188 }, { 0x255E , 198 }, { 0x255F , 199 }, { 0x2560 , 204 }, { 0x2561 , 181 }, { 0x2562 , 182 }, { 0x2563 , 185 }, { 0x2564 , 209 }, { 0x2565 , 210 }, { 0x2566 , 203 }, { 0x2567 , 207 }, { 0x2568 , 208 }, { 0x2569 , 202 }, { 0x256A , 216 }, { 0x256B , 215 }, { 0x256C , 206 }, { 0x2580 , 223 }, { 0x2584 , 220 }, { 0x2588 , 219 }, { 0x258C , 221 }, { 0x2590 , 222 }, { 0x2591 , 176 }, { 0x2592 , 177 }, { 0x2593 , 178 }, { 0x25A0 , 254 }, { 0x25AC , 22 }, { 0x25B2 , 30 }, { 0x25BA , 16 }, { 0x25BC , 31 }, { 0x25C4 , 17 }, { 0x25CB , 9 }, { 0x25D8 , 8 }, { 0x25D9 , 10 }, { 0x263A , 1 }, { 0x263B , 2 }, { 0x263C , 15 }, { 0x2640 , 12 }, { 0x2642 , 11 }, { 0x2660 , 6 }, { 0x2663 , 5 }, { 0x2665 , 3 }, { 0x2666 , 4 }, { 0x266A , 13 }, { 0x266B , 14 } }; //============================================ // OEM - IBM850 - DOS + western latin // static uint16 s_table_IBM850[] = { /* 0x2007, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C, //0x10 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8, 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC, */ // 0x20 - 0x7f untranslated //0x80 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, //0x90 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00DB, 0x00D7, 0x0192, //0xA0 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, //0xB0 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, //0xC0 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, //0xD0 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, //0xE0 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, //0xF0 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 }; //============================================ // OEM - IBM850 - reverse // static CP_ISO_UINT_TABLE s_rtable_IBM850[] = { { 0, 0 }, // to count unpair { 0xA0 , 255 }, { 0xA1 , 173 }, { 0x00A2, 189 }, { 0xA3 , 156 }, { 0x00A4, 207 }, { 0x00A5, 190 }, { 0x00A6, 221 }, { 0x00A7, 245 }, { 0x00A8, 249 }, { 0x00A9, 184 }, { 0xAA , 166 }, { 0xAB , 174 }, { 0xAC , 170 }, { 0x00AD, 240 }, { 0x00AE, 168 }, { 0x00AF, 238 }, { 0xB0 , 248 }, { 0xB1 , 241 }, { 0xB2 , 253 }, { 0x00B3, 252 }, { 0x00B4, 239 }, { 0xB5 , 230 }, { 0x00B6, 244 }, { 0xB7 , 250 }, { 0x00B8, 247 }, { 0x00B9, 251 }, { 0xBA , 167 }, { 0xBB , 175 }, { 0xBC , 172 }, { 0xBD , 171 }, { 0x00BE, 243 }, { 0xBF , 168 }, { 0x00C0, 183 }, { 0x00C1, 181 }, { 0x00C2, 182 }, { 0x00C3, 199 }, { 0xC4 , 142 }, { 0xC5 , 143 }, { 0xC6 , 146 }, { 0xC7 , 128 }, { 0x00C8, 212 }, { 0xC9 , 144 }, { 0x00CA, 210 }, { 0x00CB, 211 }, { 0x00CC, 222 }, { 0x00CD, 214 }, { 0x00CE, 215 }, { 0x00CF, 216 }, { 0x00D0, 209 }, { 0xD1 , 165 }, { 0x00D2, 227 }, { 0x00D3, 224 }, { 0x00D4, 226 }, { 0x00D5, 229 }, { 0xD6 , 153 }, { 0x00D7, 158 }, { 0x00D9, 235 }, { 0x00DA, 233 }, { 0x00DB, 234 }, { 0xDC , 154 }, { 0x00DD, 237 }, { 0x00DE, 232 }, { 0xDF , 225 }, { 0xE0 , 133 }, { 0xE1 , 160 }, { 0xE2 , 131 }, { 0x00E3, 198 }, { 0xE4 , 132 }, { 0xE5 , 134 }, { 0xE6 , 145 }, { 0xE7 , 135 }, { 0xE8 , 138 }, { 0xE9 , 130 }, { 0xEA , 136 }, { 0xEB , 137 }, { 0xEC , 141 }, { 0xED , 161 }, { 0xEE , 140 }, { 0xEF , 139 }, { 0x00F0, 208 }, { 0xF1 , 164 }, { 0xF2 , 149 }, { 0xF3 , 162 }, { 0xF4 , 147 }, { 0x00F5, 228 }, { 0xF6 , 148 }, { 0xF7 , 246 }, { 0xF8 , 155 }, { 0xF9 , 151 }, { 0xFA , 163 }, { 0xFB , 150 }, { 0xFC , 129 }, { 0x00FD, 236 }, { 0x00FE, 231 }, { 0xFF , 152 }, { 0x0131, 213 }, { 0x192 , 159 }, { 0x2007 , 0 }, { 0x2017, 242 }, { 0x2022 , 7 }, { 0x203C , 19 }, { 0x2190 , 27 }, { 0x2191 , 24 }, { 0x2192 , 26 }, { 0x2193 , 25 }, { 0x2194 , 29 }, { 0x2195 , 18 }, { 0x21A8 , 23 }, { 0x221F , 28 }, { 0x2500 , 196 }, { 0x2502 , 179 }, { 0x250C , 218 }, { 0x2510 , 191 }, { 0x2514 , 192 }, { 0x2518 , 217 }, { 0x251C , 195 }, { 0x2524 , 180 }, { 0x252C , 194 }, { 0x2534 , 193 }, { 0x253C , 197 }, { 0x2550 , 205 }, { 0x2551 , 186 }, { 0x2554 , 201 }, { 0x2557 , 187 }, { 0x255A , 200 }, { 0x255D , 188 }, { 0x2560 , 204 }, { 0x2563 , 185 }, { 0x2566 , 203 }, { 0x2569 , 202 }, { 0x256C , 206 }, { 0x2580 , 223 }, { 0x2584 , 220 }, { 0x2588 , 219 }, { 0x2591 , 176 }, { 0x2592 , 177 }, { 0x2593 , 178 }, { 0x25A0 , 254 }, { 0x25AC , 22 }, { 0x25B2 , 30 }, { 0x25BA , 16 }, { 0x25BC , 31 }, { 0x25C4 , 17 }, { 0x25CB , 9 }, { 0x25D8 , 8 }, { 0x25D9 , 10 }, { 0x263A , 1 }, { 0x263B , 2 }, { 0x263C , 15 }, { 0x2640 , 12 }, { 0x2642 , 11 }, { 0x2660 , 6 }, { 0x2663 , 5 }, { 0x2665 , 3 }, { 0x2666 , 4 }, { 0x266A , 13 }, { 0x266B , 14 } }; //======================================================== // iso latin 1 // define iso static uint16 s_table_iso8859_1[] = { 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF }; static CP_ISO_UINT_TABLE s_rtable_iso8859_1[] = { {0x00A0, 0xA0}, {0x00A1, 0xA1}, {0x00A2, 0xA2}, {0x00A3, 0xA3}, {0x00A4, 0xA4}, {0x00A5, 0xA5}, {0x00A6, 0xA6}, {0x00A7, 0xA7}, {0x00A8, 0xA8}, {0x00A9, 0xA9}, {0x00AA, 0xAA}, {0x00AB, 0xAB}, {0x00AC, 0xAC}, {0x00AD, 0xAD}, {0x00AE, 0xAE}, {0x00AF, 0xAF}, {0x00B0, 0xB0}, {0x00B1, 0xB1}, {0x00B2, 0xB2}, {0x00B3, 0xB3}, {0x00B4, 0xB4}, {0x00B5, 0xB5}, {0x00B6, 0xB6}, {0x00B7, 0xB7}, {0x00B8, 0xB8}, {0x00B9, 0xB9}, {0x00BA, 0xBA}, {0x00BB, 0xBB}, {0x00BC, 0xBC}, {0x00BD, 0xBD}, {0x00BE, 0xBE}, {0x00BF, 0xBF}, {0x00C0, 0xC0}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C3, 0xC3}, {0x00C4, 0xC4}, {0x00C5, 0xC5}, {0x00C6, 0xC6}, {0x00C7, 0xC7}, {0x00C8, 0xC8}, {0x00C9, 0xC9}, {0x00CA, 0xCA}, {0x00CB, 0xCB}, {0x00CC, 0xCC}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00CF, 0xCF}, {0x00D0, 0xD0}, {0x00D1, 0xD1}, {0x00D2, 0xD2}, {0x00D3, 0xD3}, {0x00D4, 0xD4}, {0x00D5, 0xD5}, {0x00D6, 0xD6}, {0x00D7, 0xD7}, {0x00D8, 0xD8}, {0x00D9, 0xD9}, {0x00DA, 0xDA}, {0x00DB, 0xDB}, {0x00DC, 0xDC}, {0x00DD, 0xDD}, {0x00DE, 0xDE}, {0x00DF, 0xDF}, {0x00E0, 0xE0}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E3, 0xE3}, {0x00E4, 0xE4}, {0x00E5, 0xE5}, {0x00E6, 0xE6}, {0x00E7, 0xE7}, {0x00E8, 0xE8}, {0x00E9, 0xE9}, {0x00EA, 0xEA}, {0x00EB, 0xEB}, {0x00EC, 0xEC}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00EF, 0xEF}, {0x00F0, 0xF0}, {0x00F1, 0xF1}, {0x00F2, 0xF2}, {0x00F3, 0xF3}, {0x00F4, 0xF4}, {0x00F5, 0xF5}, {0x00F6, 0xF6}, {0x00F7, 0xF7}, {0x00F8, 0xF8}, {0x00F9, 0xF9}, {0x00FA, 0xFA}, {0x00FB, 0xFB}, {0x00FC, 0xFC}, {0x00FD, 0xFD}, {0x00FE, 0xFE}, {0x00FF, 0xFF} }; //======================================================== // iso latin 2 // define iso static uint16 s_table_iso8859_2[] = { 0x00A0, 0x0104, 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7, 0x00A8, 0x0160, 0x015E, 0x0164, 0x0179, 0x00AD, 0x017D, 0x017B, 0x00B0, 0x0105, 0x02DB, 0x0142, 0x00B4, 0x013E, 0x015B, 0x02C7, 0x00B8, 0x0161, 0x015F, 0x0165, 0x017A, 0x02DD, 0x017E, 0x017C, 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9 }; static CP_ISO_UINT_TABLE s_rtable_iso8859_2[] = { {0x00A0, 0xA0}, {0x00A4, 0xA4}, {0x00A7, 0xA7}, {0x00A8, 0xA8}, {0x00AD, 0xAD}, {0x00B0, 0xB0}, {0x00B4, 0xB4}, {0x00B8, 0xB8}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C4, 0xC4}, {0x00C7, 0xC7}, {0x00C9, 0xC9}, {0x00CB, 0xCB}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00D3, 0xD3}, {0x00D4, 0xD4}, {0x00D6, 0xD6}, {0x00D7, 0xD7}, {0x00DA, 0xDA}, {0x00DC, 0xDC}, {0x00DD, 0xDD}, {0x00DF, 0xDF}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E4, 0xE4}, {0x00E7, 0xE7}, {0x00E9, 0xE9}, {0x00EB, 0xEB}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00F3, 0xF3}, {0x00F4, 0xF4}, {0x00F6, 0xF6}, {0x00F7, 0xF7}, {0x00FA, 0xFA}, {0x00FC, 0xFC}, {0x00FD, 0xFD}, {0x0102, 0xC3}, {0x0103, 0xE3}, {0x0104, 0xA1}, {0x0105, 0xB1}, {0x0106, 0xC6}, {0x0107, 0xE6}, {0x010C, 0xC8}, {0x010D, 0xE8}, {0x010E, 0xCF}, {0x010F, 0xEF}, {0x0110, 0xD0}, {0x0111, 0xF0}, {0x0118, 0xCA}, {0x0119, 0xEA}, {0x011A, 0xCC}, {0x011B, 0xEC}, {0x0139, 0xC5}, {0x013A, 0xE5}, {0x013D, 0xA5}, {0x013E, 0xB5}, {0x0141, 0xA3}, {0x0142, 0xB3}, {0x0143, 0xD1}, {0x0144, 0xF1}, {0x0147, 0xD2}, {0x0148, 0xF2}, {0x0150, 0xD5}, {0x0151, 0xF5}, {0x0154, 0xC0}, {0x0155, 0xE0}, {0x0158, 0xD8}, {0x0159, 0xF8}, {0x015A, 0xA6}, {0x015B, 0xB6}, {0x015E, 0xAA}, {0x015F, 0xBA}, {0x0160, 0xA9}, {0x0161, 0xB9}, {0x0162, 0xDE}, {0x0163, 0xFE}, {0x0164, 0xAB}, {0x0165, 0xBB}, {0x016E, 0xD9}, {0x016F, 0xF9}, {0x0170, 0xDB}, {0x0171, 0xFB}, {0x0179, 0xAC}, {0x017A, 0xBC}, {0x017B, 0xAF}, {0x017C, 0xBF}, {0x017D, 0xAE}, {0x017E, 0xBE}, {0x02C7, 0xB7}, {0x02D8, 0xA2}, {0x02D9, 0xFF}, {0x02DB, 0xB2}, {0x02DD, 0xBD} }; //======================================================== // iso latin 3 // define iso static uint16 s_table_iso8859_3[] = { 0x00A0, 0x0126, 0x02D8, 0x00A3, 0x00A4, 0x0000, 0x0124, 0x00A7, 0x00A8, 0x0130, 0x015E, 0x011E, 0x0134, 0x00AD, 0x0000, 0x017B, 0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x0125, 0x00B7, 0x00B8, 0x0131, 0x015F, 0x011F, 0x0135, 0x00BD, 0x0000, 0x017C, 0x00C0, 0x00C1, 0x00C2, 0x0000, 0x00C4, 0x010A, 0x0108, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x0000, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x0120, 0x00D6, 0x00D7, 0x011C, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x016C, 0x015C, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x0000, 0x00E4, 0x010B, 0x0109, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x0000, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7, 0x011D, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x016D, 0x015D, 0x02D9 }; static CP_ISO_UINT_TABLE s_rtable_iso8859_3[] = { {0x00A0, 0xA0}, {0x00A3, 0xA3}, {0x00A4, 0xA4}, {0x00A7, 0xA7}, {0x00A8, 0xA8}, {0x00AD, 0xAD}, {0x00B0, 0xB0}, {0x00B2, 0xB2}, {0x00B3, 0xB3}, {0x00B4, 0xB4}, {0x00B5, 0xB5}, {0x00B7, 0xB7}, {0x00B8, 0xB8}, {0x00BD, 0xBD}, {0x00C0, 0xC0}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C4, 0xC4}, {0x00C7, 0xC7}, {0x00C8, 0xC8}, {0x00C9, 0xC9}, {0x00CA, 0xCA}, {0x00CB, 0xCB}, {0x00CC, 0xCC}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00CF, 0xCF}, {0x00D1, 0xD1}, {0x00D2, 0xD2}, {0x00D3, 0xD3}, {0x00D4, 0xD4}, {0x00D6, 0xD6}, {0x00D7, 0xD7}, {0x00D9, 0xD9}, {0x00DA, 0xDA}, {0x00DB, 0xDB}, {0x00DC, 0xDC}, {0x00DF, 0xDF}, {0x00E0, 0xE0}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E4, 0xE4}, {0x00E7, 0xE7}, {0x00E8, 0xE8}, {0x00E9, 0xE9}, {0x00EA, 0xEA}, {0x00EB, 0xEB}, {0x00EC, 0xEC}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00EF, 0xEF}, {0x00F1, 0xF1}, {0x00F2, 0xF2}, {0x00F3, 0xF3}, {0x00F4, 0xF4}, {0x00F6, 0xF6}, {0x00F7, 0xF7}, {0x00F9, 0xF9}, {0x00FA, 0xFA}, {0x00FB, 0xFB}, {0x00FC, 0xFC}, {0x0108, 0xC6}, {0x0109, 0xE6}, {0x010A, 0xC5}, {0x010B, 0xE5}, {0x011C, 0xD8}, {0x011D, 0xF8}, {0x011E, 0xAB}, {0x011F, 0xBB}, {0x0120, 0xD5}, {0x0121, 0xF5}, {0x0124, 0xA6}, {0x0125, 0xB6}, {0x0126, 0xA1}, {0x0127, 0xB1}, {0x0130, 0xA9}, {0x0131, 0xB9}, {0x0134, 0xAC}, {0x0135, 0xBC}, {0x015C, 0xDE}, {0x015D, 0xFE}, {0x015E, 0xAA}, {0x015F, 0xBA}, {0x016C, 0xDD}, {0x016D, 0xFD}, {0x017B, 0xAF}, {0x017C, 0xBF}, {0x02D8, 0xA2}, {0x02D9, 0xFF} }; //======================================================== // iso latin 4 // define iso static uint16 s_table_iso8859_4[] = { 0x00A0, 0x0104, 0x0138, 0x0156, 0x00A4, 0x0128, 0x013B, 0x00A7, 0x00A8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00AD, 0x017D, 0x00AF, 0x00B0, 0x0105, 0x02DB, 0x0157, 0x00B4, 0x0129, 0x013C, 0x02C7, 0x00B8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014A, 0x017E, 0x014B, 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x012A, 0x0110, 0x0145, 0x014C, 0x0136, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x0168, 0x016A, 0x00DF, 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x012B, 0x0111, 0x0146, 0x014D, 0x0137, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x0169, 0x016B, 0x02D9 }; static CP_ISO_UINT_TABLE s_rtable_iso8859_4[] = { {0x00A0, 0xA0}, {0x00A4, 0xA4}, {0x00A7, 0xA7}, {0x00A8, 0xA8}, {0x00AD, 0xAD}, {0x00AF, 0xAF}, {0x00B0, 0xB0}, {0x00B4, 0xB4}, {0x00B8, 0xB8}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C3, 0xC3}, {0x00C4, 0xC4}, {0x00C5, 0xC5}, {0x00C6, 0xC6}, {0x00C9, 0xC9}, {0x00CB, 0xCB}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00D4, 0xD4}, {0x00D5, 0xD5}, {0x00D6, 0xD6}, {0x00D7, 0xD7}, {0x00D8, 0xD8}, {0x00DA, 0xDA}, {0x00DB, 0xDB}, {0x00DC, 0xDC}, {0x00DF, 0xDF}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E3, 0xE3}, {0x00E4, 0xE4}, {0x00E5, 0xE5}, {0x00E6, 0xE6}, {0x00E9, 0xE9}, {0x00EB, 0xEB}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00F4, 0xF4}, {0x00F5, 0xF5}, {0x00F6, 0xF6}, {0x00F7, 0xF7}, {0x00F8, 0xF8}, {0x00FA, 0xFA}, {0x00FB, 0xFB}, {0x00FC, 0xFC}, {0x0100, 0xC0}, {0x0101, 0xE0}, {0x0104, 0xA1}, {0x0105, 0xB1}, {0x010C, 0xC8}, {0x010D, 0xE8}, {0x0110, 0xD0}, {0x0111, 0xF0}, {0x0112, 0xAA}, {0x0113, 0xBA}, {0x0116, 0xCC}, {0x0117, 0xEC}, {0x0118, 0xCA}, {0x0119, 0xEA}, {0x0122, 0xAB}, {0x0123, 0xBB}, {0x0128, 0xA5}, {0x0129, 0xB5}, {0x012A, 0xCF}, {0x012B, 0xEF}, {0x012E, 0xC7}, {0x012F, 0xE7}, {0x0136, 0xD3}, {0x0137, 0xF3}, {0x0138, 0xA2}, {0x013B, 0xA6}, {0x013C, 0xB6}, {0x0145, 0xD1}, {0x0146, 0xF1}, {0x014A, 0xBD}, {0x014B, 0xBF}, {0x014C, 0xD2}, {0x014D, 0xF2}, {0x0156, 0xA3}, {0x0157, 0xB3}, {0x0160, 0xA9}, {0x0161, 0xB9}, {0x0166, 0xAC}, {0x0167, 0xBC}, {0x0168, 0xDD}, {0x0169, 0xFD}, {0x016A, 0xDE}, {0x016B, 0xFE}, {0x0172, 0xD9}, {0x0173, 0xF9}, {0x017D, 0xAE}, {0x017E, 0xBE}, {0x02C7, 0xB7}, {0x02D9, 0xFF}, {0x02DB, 0xB2} }; //======================================================== // iso Cyrillic // define iso static uint16 s_table_iso8859_5[] = { 0x00A0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x00AD, 0x040E, 0x040F, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x00A7, 0x045E, 0x045F }; static CP_ISO_UINT_TABLE s_rtable_iso8859_5[] = { {0x00A0, 0xA0}, {0x00A7, 0xFD}, {0x00AD, 0xAD}, {0x0401, 0xA1}, {0x0402, 0xA2}, {0x0403, 0xA3}, {0x0404, 0xA4}, {0x0405, 0xA5}, {0x0406, 0xA6}, {0x0407, 0xA7}, {0x0408, 0xA8}, {0x0409, 0xA9}, {0x040A, 0xAA}, {0x040B, 0xAB}, {0x040C, 0xAC}, {0x040E, 0xAE}, {0x040F, 0xAF}, {0x0410, 0xB0}, {0x0411, 0xB1}, {0x0412, 0xB2}, {0x0413, 0xB3}, {0x0414, 0xB4}, {0x0415, 0xB5}, {0x0416, 0xB6}, {0x0417, 0xB7}, {0x0418, 0xB8}, {0x0419, 0xB9}, {0x041A, 0xBA}, {0x041B, 0xBB}, {0x041C, 0xBC}, {0x041D, 0xBD}, {0x041E, 0xBE}, {0x041F, 0xBF}, {0x0420, 0xC0}, {0x0421, 0xC1}, {0x0422, 0xC2}, {0x0423, 0xC3}, {0x0424, 0xC4}, {0x0425, 0xC5}, {0x0426, 0xC6}, {0x0427, 0xC7}, {0x0428, 0xC8}, {0x0429, 0xC9}, {0x042A, 0xCA}, {0x042B, 0xCB}, {0x042C, 0xCC}, {0x042D, 0xCD}, {0x042E, 0xCE}, {0x042F, 0xCF}, {0x0430, 0xD0}, {0x0431, 0xD1}, {0x0432, 0xD2}, {0x0433, 0xD3}, {0x0434, 0xD4}, {0x0435, 0xD5}, {0x0436, 0xD6}, {0x0437, 0xD7}, {0x0438, 0xD8}, {0x0439, 0xD9}, {0x043A, 0xDA}, {0x043B, 0xDB}, {0x043C, 0xDC}, {0x043D, 0xDD}, {0x043E, 0xDE}, {0x043F, 0xDF}, {0x0440, 0xE0}, {0x0441, 0xE1}, {0x0442, 0xE2}, {0x0443, 0xE3}, {0x0444, 0xE4}, {0x0445, 0xE5}, {0x0446, 0xE6}, {0x0447, 0xE7}, {0x0448, 0xE8}, {0x0449, 0xE9}, {0x044A, 0xEA}, {0x044B, 0xEB}, {0x044C, 0xEC}, {0x044D, 0xED}, {0x044E, 0xEE}, {0x044F, 0xEF}, {0x0451, 0xF1}, {0x0452, 0xF2}, {0x0453, 0xF3}, {0x0454, 0xF4}, {0x0455, 0xF5}, {0x0456, 0xF6}, {0x0457, 0xF7}, {0x0458, 0xF8}, {0x0459, 0xF9}, {0x045A, 0xFA}, {0x045B, 0xFB}, {0x045C, 0xFC}, {0x045E, 0xFE}, {0x045F, 0xFF}, {0x2116, 0xF0} }; //======================================================== // iso arabic // define iso static uint16 s_table_iso8859_6[] = { 0x00A0, 0x0000, 0x0000, 0x0000, 0x00A4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x060C, 0x00AD, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x061B, 0x0000, 0x0000, 0x0000, 0x061F, 0x0000, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x0651, 0x0652 }; static CP_ISO_UINT_TABLE s_rtable_iso8859_6[] = { {0x00A0, 0xA0}, {0x00A4, 0xA4}, {0x00AD, 0xAD}, {0x060C, 0xAC}, {0x061B, 0xBB}, {0x061F, 0xBF}, {0x0621, 0xC1}, {0x0622, 0xC2}, {0x0623, 0xC3}, {0x0624, 0xC4}, {0x0625, 0xC5}, {0x0626, 0xC6}, {0x0627, 0xC7}, {0x0628, 0xC8}, {0x0629, 0xC9}, {0x062A, 0xCA}, {0x062B, 0xCB}, {0x062C, 0xCC}, {0x062D, 0xCD}, {0x062E, 0xCE}, {0x062F, 0xCF}, {0x0630, 0xD0}, {0x0631, 0xD1}, {0x0632, 0xD2}, {0x0633, 0xD3}, {0x0634, 0xD4}, {0x0635, 0xD5}, {0x0636, 0xD6}, {0x0637, 0xD7}, {0x0638, 0xD8}, {0x0639, 0xD9}, {0x063A, 0xDA}, {0x0640, 0xE0}, {0x0641, 0xE1}, {0x0642, 0xE2}, {0x0643, 0xE3}, {0x0644, 0xE4}, {0x0645, 0xE5}, {0x0646, 0xE6}, {0x0647, 0xE7}, {0x0648, 0xE8}, {0x0649, 0xE9}, {0x064A, 0xEA}, {0x064B, 0xEB}, {0x064C, 0xEC}, {0x064D, 0xED}, {0x064E, 0xEE}, {0x064F, 0xEF}, {0x0650, 0xF0}, {0x0651, 0xF1}, {0x0652, 0xF2} }; //======================================================== // iso Greek // define iso static uint16 s_table_iso8859_7[] = { 0x00A0, 0x02BD, 0x02BC, 0x00A3, 0x0000, 0x0000, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x0000, 0x00AB, 0x00AC, 0x00AD, 0x0000, 0x2015, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x0385, 0x0386, 0x00B7, 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F, 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE }; static CP_ISO_UINT_TABLE s_rtable_iso8859_7[] = { {0x00A0, 0xA0}, {0x00A3, 0xA3}, {0x00A6, 0xA6}, {0x00A7, 0xA7}, {0x00A8, 0xA8}, {0x00A9, 0xA9}, {0x00AB, 0xAB}, {0x00AC, 0xAC}, {0x00AD, 0xAD}, {0x00B0, 0xB0}, {0x00B1, 0xB1}, {0x00B2, 0xB2}, {0x00B3, 0xB3}, {0x00B7, 0xB7}, {0x00BB, 0xBB}, {0x00BD, 0xBD}, {0x02BC, 0xA2}, {0x02BD, 0xA1}, {0x0384, 0xB4}, {0x0385, 0xB5}, {0x0386, 0xB6}, {0x0388, 0xB8}, {0x0389, 0xB9}, {0x038A, 0xBA}, {0x038C, 0xBC}, {0x038E, 0xBE}, {0x038F, 0xBF}, {0x0390, 0xC0}, {0x0391, 0xC1}, {0x0392, 0xC2}, {0x0393, 0xC3}, {0x0394, 0xC4}, {0x0395, 0xC5}, {0x0396, 0xC6}, {0x0397, 0xC7}, {0x0398, 0xC8}, {0x0399, 0xC9}, {0x039A, 0xCA}, {0x039B, 0xCB}, {0x039C, 0xCC}, {0x039D, 0xCD}, {0x039E, 0xCE}, {0x039F, 0xCF}, {0x03A0, 0xD0}, {0x03A1, 0xD1}, {0x03A3, 0xD3}, {0x03A4, 0xD4}, {0x03A5, 0xD5}, {0x03A6, 0xD6}, {0x03A7, 0xD7}, {0x03A8, 0xD8}, {0x03A9, 0xD9}, {0x03AA, 0xDA}, {0x03AB, 0xDB}, {0x03AC, 0xDC}, {0x03AD, 0xDD}, {0x03AE, 0xDE}, {0x03AF, 0xDF}, {0x03B0, 0xE0}, {0x03B1, 0xE1}, {0x03B2, 0xE2}, {0x03B3, 0xE3}, {0x03B4, 0xE4}, {0x03B5, 0xE5}, {0x03B6, 0xE6}, {0x03B7, 0xE7}, {0x03B8, 0xE8}, {0x03B9, 0xE9}, {0x03BA, 0xEA}, {0x03BB, 0xEB}, {0x03BC, 0xEC}, {0x03BD, 0xED}, {0x03BE, 0xEE}, {0x03BF, 0xEF}, {0x03C0, 0xF0}, {0x03C1, 0xF1}, {0x03C2, 0xF2}, {0x03C3, 0xF3}, {0x03C4, 0xF4}, {0x03C5, 0xF5}, {0x03C6, 0xF6}, {0x03C7, 0xF7}, {0x03C8, 0xF8}, {0x03C9, 0xF9}, {0x03CA, 0xFA}, {0x03CB, 0xFB}, {0x03CC, 0xFC}, {0x03CD, 0xFD}, {0x03CE, 0xFE}, {0x2015, 0xAF} }; //======================================================== // iso Hebrew // define iso static uint16 s_table_iso8859_8[] = { 0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x203E, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2017, 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA }; static CP_ISO_UINT_TABLE s_rtable_iso8859_8[] = { {0x00A0, 0xA0}, {0x00A2, 0xA2}, {0x00A3, 0xA3}, {0x00A4, 0xA4}, {0x00A5, 0xA5}, {0x00A6, 0xA6}, {0x00A7, 0xA7}, {0x00A8, 0xA8}, {0x00A9, 0xA9}, {0x00AB, 0xAB}, {0x00AC, 0xAC}, {0x00AD, 0xAD}, {0x00AE, 0xAE}, {0x00B0, 0xB0}, {0x00B1, 0xB1}, {0x00B2, 0xB2}, {0x00B3, 0xB3}, {0x00B4, 0xB4}, {0x00B5, 0xB5}, {0x00B6, 0xB6}, {0x00B7, 0xB7}, {0x00B8, 0xB8}, {0x00B9, 0xB9}, {0x00BB, 0xBB}, {0x00BC, 0xBC}, {0x00BD, 0xBD}, {0x00BE, 0xBE}, {0x00D7, 0xAA}, {0x00F7, 0xBA}, {0x05D0, 0xE0}, {0x05D1, 0xE1}, {0x05D2, 0xE2}, {0x05D3, 0xE3}, {0x05D4, 0xE4}, {0x05D5, 0xE5}, {0x05D6, 0xE6}, {0x05D7, 0xE7}, {0x05D8, 0xE8}, {0x05D9, 0xE9}, {0x05DA, 0xEA}, {0x05DB, 0xEB}, {0x05DC, 0xEC}, {0x05DD, 0xED}, {0x05DE, 0xEE}, {0x05DF, 0xEF}, {0x05E0, 0xF0}, {0x05E1, 0xF1}, {0x05E2, 0xF2}, {0x05E3, 0xF3}, {0x05E4, 0xF4}, {0x05E5, 0xF5}, {0x05E6, 0xF6}, {0x05E7, 0xF7}, {0x05E8, 0xF8}, {0x05E9, 0xF9}, {0x05EA, 0xFA}, {0x2017, 0xDF}, {0x203E, 0xAF} }; //======================================================== // iso latin 5 // define iso static uint16 s_table_iso8859_9[] = { 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF }; static CP_ISO_UINT_TABLE s_rtable_iso8859_9[] = { {0x00A0, 0xA0}, {0x00A1, 0xA1}, {0x00A2, 0xA2}, {0x00A3, 0xA3}, {0x00A4, 0xA4}, {0x00A5, 0xA5}, {0x00A6, 0xA6}, {0x00A7, 0xA7}, {0x00A8, 0xA8}, {0x00A9, 0xA9}, {0x00AA, 0xAA}, {0x00AB, 0xAB}, {0x00AC, 0xAC}, {0x00AD, 0xAD}, {0x00AE, 0xAE}, {0x00AF, 0xAF}, {0x00B0, 0xB0}, {0x00B1, 0xB1}, {0x00B2, 0xB2}, {0x00B3, 0xB3}, {0x00B4, 0xB4}, {0x00B5, 0xB5}, {0x00B6, 0xB6}, {0x00B7, 0xB7}, {0x00B8, 0xB8}, {0x00B9, 0xB9}, {0x00BA, 0xBA}, {0x00BB, 0xBB}, {0x00BC, 0xBC}, {0x00BD, 0xBD}, {0x00BE, 0xBE}, {0x00BF, 0xBF}, {0x00C0, 0xC0}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C3, 0xC3}, {0x00C4, 0xC4}, {0x00C5, 0xC5}, {0x00C6, 0xC6}, {0x00C7, 0xC7}, {0x00C8, 0xC8}, {0x00C9, 0xC9}, {0x00CA, 0xCA}, {0x00CB, 0xCB}, {0x00CC, 0xCC}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00CF, 0xCF}, {0x00D1, 0xD1}, {0x00D2, 0xD2}, {0x00D3, 0xD3}, {0x00D4, 0xD4}, {0x00D5, 0xD5}, {0x00D6, 0xD6}, {0x00D7, 0xD7}, {0x00D8, 0xD8}, {0x00D9, 0xD9}, {0x00DA, 0xDA}, {0x00DB, 0xDB}, {0x00DC, 0xDC}, {0x00DF, 0xDF}, {0x00E0, 0xE0}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E3, 0xE3}, {0x00E4, 0xE4}, {0x00E5, 0xE5}, {0x00E6, 0xE6}, {0x00E7, 0xE7}, {0x00E8, 0xE8}, {0x00E9, 0xE9}, {0x00EA, 0xEA}, {0x00EB, 0xEB}, {0x00EC, 0xEC}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00EF, 0xEF}, {0x00F1, 0xF1}, {0x00F2, 0xF2}, {0x00F3, 0xF3}, {0x00F4, 0xF4}, {0x00F5, 0xF5}, {0x00F6, 0xF6}, {0x00F7, 0xF7}, {0x00F8, 0xF8}, {0x00F9, 0xF9}, {0x00FA, 0xFA}, {0x00FB, 0xFB}, {0x00FC, 0xFC}, {0x00FF, 0xFF}, {0x011E, 0xD0}, {0x011F, 0xF0}, {0x0130, 0xDD}, {0x0131, 0xFD}, {0x015E, 0xDE}, {0x015F, 0xFE} }; //======================================================== // iso latin 6 // define iso static uint16 s_table_iso8859_10[] = { 0x00A0, 0x0104, 0x0112, 0x0122, 0x012A, 0x0128, 0x0136, 0x00A7, 0x013B, 0x0110, 0x0160, 0x0166, 0x017D, 0x00AD, 0x016A, 0x014A, 0x00B0, 0x0105, 0x0113, 0x0123, 0x012B, 0x0129, 0x0137, 0x00B7, 0x013C, 0x0111, 0x0161, 0x0167, 0x017E, 0x2015, 0x016B, 0x014B, 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x0145, 0x014C, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0168, 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x0146, 0x014D, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0169, 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x0138 }; static CP_ISO_UINT_TABLE s_rtable_iso8859_10[] = { {0x00A0, 0xA0}, {0x00A7, 0xA7}, {0x00AD, 0xAD}, {0x00B0, 0xB0}, {0x00B7, 0xB7}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C3, 0xC3}, {0x00C4, 0xC4}, {0x00C5, 0xC5}, {0x00C6, 0xC6}, {0x00C9, 0xC9}, {0x00CB, 0xCB}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00CF, 0xCF}, {0x00D0, 0xD0}, {0x00D3, 0xD3}, {0x00D4, 0xD4}, {0x00D5, 0xD5}, {0x00D6, 0xD6}, {0x00D8, 0xD8}, {0x00DA, 0xDA}, {0x00DB, 0xDB}, {0x00DC, 0xDC}, {0x00DD, 0xDD}, {0x00DE, 0xDE}, {0x00DF, 0xDF}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E3, 0xE3}, {0x00E4, 0xE4}, {0x00E5, 0xE5}, {0x00E6, 0xE6}, {0x00E9, 0xE9}, {0x00EB, 0xEB}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00EF, 0xEF}, {0x00F0, 0xF0}, {0x00F3, 0xF3}, {0x00F4, 0xF4}, {0x00F5, 0xF5}, {0x00F6, 0xF6}, {0x00F8, 0xF8}, {0x00FA, 0xFA}, {0x00FB, 0xFB}, {0x00FC, 0xFC}, {0x00FD, 0xFD}, {0x00FE, 0xFE}, {0x0100, 0xC0}, {0x0101, 0xE0}, {0x0104, 0xA1}, {0x0105, 0xB1}, {0x010C, 0xC8}, {0x010D, 0xE8}, {0x0110, 0xA9}, {0x0111, 0xB9}, {0x0112, 0xA2}, {0x0113, 0xB2}, {0x0116, 0xCC}, {0x0117, 0xEC}, {0x0118, 0xCA}, {0x0119, 0xEA}, {0x0122, 0xA3}, {0x0123, 0xB3}, {0x0128, 0xA5}, {0x0129, 0xB5}, {0x012A, 0xA4}, {0x012B, 0xB4}, {0x012E, 0xC7}, {0x012F, 0xE7}, {0x0136, 0xA6}, {0x0137, 0xB6}, {0x0138, 0xFF}, {0x013B, 0xA8}, {0x013C, 0xB8}, {0x0145, 0xD1}, {0x0146, 0xF1}, {0x014A, 0xAF}, {0x014B, 0xBF}, {0x014C, 0xD2}, {0x014D, 0xF2}, {0x0160, 0xAA}, {0x0161, 0xBA}, {0x0166, 0xAB}, {0x0167, 0xBB}, {0x0168, 0xD7}, {0x0169, 0xF7}, {0x016A, 0xAE}, {0x016B, 0xBE}, {0x0172, 0xD9}, {0x0173, 0xF9}, {0x017D, 0xAC}, {0x017E, 0xBC}, {0x2015, 0xBD} }; //======================================================== // iso Thai // define iso static uint16 s_table_iso8859_11[] = { 0x0000, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07, 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F, 0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17, 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F, 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27, 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F, 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, 0x0E38, 0x0E39, 0x0E3A, 0x0000, 0x0000, 0x0000, 0x0E3F, 0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F, 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59, 0x0E5A, 0x0E5B }; static CP_ISO_UINT_TABLE s_rtable_iso8859_11[] = { {0x0E01, 0xA1}, {0x0E02, 0xA2}, {0x0E03, 0xA3}, {0x0E04, 0xA4}, {0x0E05, 0xA5}, {0x0E06, 0xA6}, {0x0E07, 0xA7}, {0x0E08, 0xA8}, {0x0E09, 0xA9}, {0x0E0A, 0xAA}, {0x0E0B, 0xAB}, {0x0E0C, 0xAC}, {0x0E0D, 0xAD}, {0x0E0E, 0xAE}, {0x0E0F, 0xAF}, {0x0E10, 0xB0}, {0x0E11, 0xB1}, {0x0E12, 0xB2}, {0x0E13, 0xB3}, {0x0E14, 0xB4}, {0x0E15, 0xB5}, {0x0E16, 0xB6}, {0x0E17, 0xB7}, {0x0E18, 0xB8}, {0x0E19, 0xB9}, {0x0E1A, 0xBA}, {0x0E1B, 0xBB}, {0x0E1C, 0xBC}, {0x0E1D, 0xBD}, {0x0E1E, 0xBE}, {0x0E1F, 0xBF}, {0x0E20, 0xC0}, {0x0E21, 0xC1}, {0x0E22, 0xC2}, {0x0E23, 0xC3}, {0x0E24, 0xC4}, {0x0E25, 0xC5}, {0x0E26, 0xC6}, {0x0E27, 0xC7}, {0x0E28, 0xC8}, {0x0E29, 0xC9}, {0x0E2A, 0xCA}, {0x0E2B, 0xCB}, {0x0E2C, 0xCC}, {0x0E2D, 0xCD}, {0x0E2E, 0xCE}, {0x0E2F, 0xCF}, {0x0E30, 0xD0}, {0x0E31, 0xD1}, {0x0E32, 0xD2}, {0x0E33, 0xD3}, {0x0E34, 0xD4}, {0x0E35, 0xD5}, {0x0E36, 0xD6}, {0x0E37, 0xD7}, {0x0E38, 0xD8}, {0x0E39, 0xD9}, {0x0E3A, 0xDA}, {0x0E3F, 0xDF}, {0x0E40, 0xE0}, {0x0E41, 0xE1}, {0x0E42, 0xE2}, {0x0E43, 0xE3}, {0x0E44, 0xE4}, {0x0E45, 0xE5}, {0x0E46, 0xE6}, {0x0E47, 0xE7}, {0x0E48, 0xE8}, {0x0E49, 0xE9}, {0x0E4A, 0xEA}, {0x0E4B, 0xEB}, {0x0E4C, 0xEC}, {0x0E4D, 0xED}, {0x0E4E, 0xEE}, {0x0E4F, 0xEF}, {0x0E50, 0xF0}, {0x0E51, 0xF1}, {0x0E52, 0xF2}, {0x0E53, 0xF3}, {0x0E54, 0xF4}, {0x0E55, 0xF5}, {0x0E56, 0xF6}, {0x0E57, 0xF7}, {0x0E58, 0xF8}, {0x0E59, 0xF9}, {0x0E5A, 0xFA}, {0x0E5B, 0xFB} }; //======================================================== // iso latin 7 // define iso static uint16 s_table_iso8859_13[] = { 0x00A0, 0x201D, 0x00A2, 0x00A3, 0x00A4, 0x201E, 0x00A6, 0x00A7, 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x201C, 0x00B5, 0x00B6, 0x00B7, 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6, 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B, 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF, 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C, 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x2019 }; static CP_ISO_UINT_TABLE s_rtable_iso8859_13[] = { {0x00A0, 0xA0}, {0x00A2, 0xA2}, {0x00A3, 0xA3}, {0x00A4, 0xA4}, {0x00A6, 0xA6}, {0x00A7, 0xA7}, {0x00A9, 0xA9}, {0x00AB, 0xAB}, {0x00AC, 0xAC}, {0x00AD, 0xAD}, {0x00AE, 0xAE}, {0x00B0, 0xB0}, {0x00B1, 0xB1}, {0x00B2, 0xB2}, {0x00B3, 0xB3}, {0x00B5, 0xB5}, {0x00B6, 0xB6}, {0x00B7, 0xB7}, {0x00B9, 0xB9}, {0x00BB, 0xBB}, {0x00BC, 0xBC}, {0x00BD, 0xBD}, {0x00BE, 0xBE}, {0x00C4, 0xC4}, {0x00C5, 0xC5}, {0x00C6, 0xAF}, {0x00C9, 0xC9}, {0x00D3, 0xD3}, {0x00D5, 0xD5}, {0x00D6, 0xD6}, {0x00D7, 0xD7}, {0x00D8, 0xA8}, {0x00DC, 0xDC}, {0x00DF, 0xDF}, {0x00E4, 0xE4}, {0x00E5, 0xE5}, {0x00E6, 0xBF}, {0x00E9, 0xE9}, {0x00F3, 0xF3}, {0x00F5, 0xF5}, {0x00F6, 0xF6}, {0x00F7, 0xF7}, {0x00F8, 0xB8}, {0x00FC, 0xFC}, {0x0100, 0xC2}, {0x0101, 0xE2}, {0x0104, 0xC0}, {0x0105, 0xE0}, {0x0106, 0xC3}, {0x0107, 0xE3}, {0x010C, 0xC8}, {0x010D, 0xE8}, {0x0112, 0xC7}, {0x0113, 0xE7}, {0x0116, 0xCB}, {0x0117, 0xEB}, {0x0118, 0xC6}, {0x0119, 0xE6}, {0x0122, 0xCC}, {0x0123, 0xEC}, {0x012A, 0xCE}, {0x012B, 0xEE}, {0x012E, 0xC1}, {0x012F, 0xE1}, {0x0136, 0xCD}, {0x0137, 0xED}, {0x013B, 0xCF}, {0x013C, 0xEF}, {0x0141, 0xD9}, {0x0142, 0xF9}, {0x0143, 0xD1}, {0x0144, 0xF1}, {0x0145, 0xD2}, {0x0146, 0xF2}, {0x014C, 0xD4}, {0x014D, 0xF4}, {0x0156, 0xAA}, {0x0157, 0xBA}, {0x015A, 0xDA}, {0x015B, 0xFA}, {0x0160, 0xD0}, {0x0161, 0xF0}, {0x016A, 0xDB}, {0x016B, 0xFB}, {0x0172, 0xD8}, {0x0173, 0xF8}, {0x0179, 0xCA}, {0x017A, 0xEA}, {0x017B, 0xDD}, {0x017C, 0xFD}, {0x017D, 0xDE}, {0x017E, 0xFE}, {0x2019, 0xFF}, {0x201C, 0xB4}, {0x201D, 0xA1}, {0x201E, 0xA5} }; //======================================================== // iso latin 8 // define iso static uint16 s_table_iso8859_14[] = { 0x00A0, 0x1E02, 0x1E03, 0x00A3, 0x010A, 0x010B, 0x1E0A, 0x00A7, 0x1E80, 0x00A9, 0x1E82, 0x1E0B, 0x1EF2, 0x00AD, 0x00AE, 0x0178, 0x1E1E, 0x1E1F, 0x0120, 0x0121, 0x1E40, 0x1E41, 0x00B6, 0x1E56, 0x1E81, 0x1E57, 0x1E83, 0x1E60, 0x1EF3, 0x1E84, 0x1E85, 0x1E61, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x0174, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x1E6A, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x0176, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x0175, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x1E6B, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x0177, 0x00FF }; static CP_ISO_UINT_TABLE s_rtable_iso8859_14[] = { {0x00A0, 0xA0}, {0x00A3, 0xA3}, {0x00A7, 0xA7}, {0x00A9, 0xA9}, {0x00AD, 0xAD}, {0x00AE, 0xAE}, {0x00B6, 0xB6}, {0x00C0, 0xC0}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C3, 0xC3}, {0x00C4, 0xC4}, {0x00C5, 0xC5}, {0x00C6, 0xC6}, {0x00C7, 0xC7}, {0x00C8, 0xC8}, {0x00C9, 0xC9}, {0x00CA, 0xCA}, {0x00CB, 0xCB}, {0x00CC, 0xCC}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00CF, 0xCF}, {0x00D1, 0xD1}, {0x00D2, 0xD2}, {0x00D3, 0xD3}, {0x00D4, 0xD4}, {0x00D5, 0xD5}, {0x00D6, 0xD6}, {0x00D8, 0xD8}, {0x00D9, 0xD9}, {0x00DA, 0xDA}, {0x00DB, 0xDB}, {0x00DC, 0xDC}, {0x00DD, 0xDD}, {0x00DF, 0xDF}, {0x00E0, 0xE0}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E3, 0xE3}, {0x00E4, 0xE4}, {0x00E5, 0xE5}, {0x00E6, 0xE6}, {0x00E7, 0xE7}, {0x00E8, 0xE8}, {0x00E9, 0xE9}, {0x00EA, 0xEA}, {0x00EB, 0xEB}, {0x00EC, 0xEC}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00EF, 0xEF}, {0x00F1, 0xF1}, {0x00F2, 0xF2}, {0x00F3, 0xF3}, {0x00F4, 0xF4}, {0x00F5, 0xF5}, {0x00F6, 0xF6}, {0x00F8, 0xF8}, {0x00F9, 0xF9}, {0x00FA, 0xFA}, {0x00FB, 0xFB}, {0x00FC, 0xFC}, {0x00FD, 0xFD}, {0x00FF, 0xFF}, {0x010A, 0xA4}, {0x010B, 0xA5}, {0x0120, 0xB2}, {0x0121, 0xB3}, {0x0174, 0xD0}, {0x0175, 0xF0}, {0x0176, 0xDE}, {0x0177, 0xFE}, {0x0178, 0xAF}, {0x1E02, 0xA1}, {0x1E03, 0xA2}, {0x1E0A, 0xA6}, {0x1E0B, 0xAB}, {0x1E1E, 0xB0}, {0x1E1F, 0xB1}, {0x1E40, 0xB4}, {0x1E41, 0xB5}, {0x1E56, 0xB7}, {0x1E57, 0xB9}, {0x1E60, 0xBB}, {0x1E61, 0xBF}, {0x1E6A, 0xD7}, {0x1E6B, 0xF7}, {0x1E80, 0xA8}, {0x1E81, 0xB8}, {0x1E82, 0xAA}, {0x1E83, 0xBA}, {0x1E84, 0xBD}, {0x1E85, 0xBE}, {0x1EF2, 0xAC}, {0x1EF3, 0xBC} }; //======================================================== // iso latin 9 // define iso static uint16 s_table_iso8859_15[] = { 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0160, 0x00A7, 0x0161, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x017D, 0x00B5, 0x00B6, 0x00B7, 0x017E, 0x00B9, 0x00BA, 0x00BB, 0x0152, 0x0153, 0x0178, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF }; static CP_ISO_UINT_TABLE s_rtable_iso8859_15[] = { {0x00A0, 0xA0}, {0x00A1, 0xA1}, {0x00A2, 0xA2}, {0x00A3, 0xA3}, {0x00A5, 0xA5}, {0x00A7, 0xA7}, {0x00A9, 0xA9}, {0x00AA, 0xAA}, {0x00AB, 0xAB}, {0x00AC, 0xAC}, {0x00AD, 0xAD}, {0x00AE, 0xAE}, {0x00AF, 0xAF}, {0x00B0, 0xB0}, {0x00B1, 0xB1}, {0x00B2, 0xB2}, {0x00B3, 0xB3}, {0x00B5, 0xB5}, {0x00B6, 0xB6}, {0x00B7, 0xB7}, {0x00B9, 0xB9}, {0x00BA, 0xBA}, {0x00BB, 0xBB}, {0x00BF, 0xBF}, {0x00C0, 0xC0}, {0x00C1, 0xC1}, {0x00C2, 0xC2}, {0x00C3, 0xC3}, {0x00C4, 0xC4}, {0x00C5, 0xC5}, {0x00C6, 0xC6}, {0x00C7, 0xC7}, {0x00C8, 0xC8}, {0x00C9, 0xC9}, {0x00CA, 0xCA}, {0x00CB, 0xCB}, {0x00CC, 0xCC}, {0x00CD, 0xCD}, {0x00CE, 0xCE}, {0x00CF, 0xCF}, {0x00D0, 0xD0}, {0x00D1, 0xD1}, {0x00D2, 0xD2}, {0x00D3, 0xD3}, {0x00D4, 0xD4}, {0x00D5, 0xD5}, {0x00D6, 0xD6}, {0x00D7, 0xD7}, {0x00D8, 0xD8}, {0x00D9, 0xD9}, {0x00DA, 0xDA}, {0x00DB, 0xDB}, {0x00DC, 0xDC}, {0x00DD, 0xDD}, {0x00DE, 0xDE}, {0x00DF, 0xDF}, {0x00E0, 0xE0}, {0x00E1, 0xE1}, {0x00E2, 0xE2}, {0x00E3, 0xE3}, {0x00E4, 0xE4}, {0x00E5, 0xE5}, {0x00E6, 0xE6}, {0x00E7, 0xE7}, {0x00E8, 0xE8}, {0x00E9, 0xE9}, {0x00EA, 0xEA}, {0x00EB, 0xEB}, {0x00EC, 0xEC}, {0x00ED, 0xED}, {0x00EE, 0xEE}, {0x00EF, 0xEF}, {0x00F0, 0xF0}, {0x00F1, 0xF1}, {0x00F2, 0xF2}, {0x00F3, 0xF3}, {0x00F4, 0xF4}, {0x00F5, 0xF5}, {0x00F6, 0xF6}, {0x00F7, 0xF7}, {0x00F8, 0xF8}, {0x00F9, 0xF9}, {0x00FA, 0xFA}, {0x00FB, 0xFB}, {0x00FC, 0xFC}, {0x00FD, 0xFD}, {0x00FE, 0xFE}, {0x00FF, 0xFF}, {0x0152, 0xBC}, {0x0153, 0xBD}, {0x0160, 0xA6}, {0x0161, 0xA8}, {0x0178, 0xBE}, {0x017D, 0xB4}, {0x017E, 0xB8}, {0x20AC, 0xA4} }; const static uint16 gbkDecoderInnerIndex0[]= { 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD, 0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0xFFFD,0x4E02, 0x4E04,0x4E05,0x4E06,0x4E0F,0x4E12,0x4E17,0x4E1F,0x4E20, 0x4E21,0x4E23,0x4E26,0x4E29,0x4E2E,0x4E2F,0x4E31,0x4E33, 0x4E35,0x4E37,0x4E3C,0x4E40,0x4E41,0x4E42,0x4E44,0x4E46, 0x4E4A,0x4E51,0x4E55,0x4E57,0x4E5A,0x4E5B,0x4E62,0x4E63, 0x4E64,0x4E65,0x4E67,0x4E68,0x4E6A,0x4E6B,0x4E6C,0x4E6D, 0x4E6E,0x4E6F,0x4E72,0x4E74,0x4E75,0x4E76,0x4E77,0x4E78, 0x4E79,0x4E7A,0x4E7B,0x4E7C,0x4E7D,0x4E7F,0x4E80,0x4E81, 0x4E82,0x4E83,0x4E84,0x4E85,0x4E87,0x4E8A,0xFFFD,0x4E90, 0x4E96,0x4E97,0x4E99,0x4E9C,0x4E9D,0x4E9E,0x4EA3,0x4EAA, 0x4EAF,0x4EB0,0x4EB1,0x4EB4,0x4EB6,0x4EB7,0x4EB8,0x4EB9, 0x4EBC,0x4EBD,0x4EBE,0x4EC8,0x4ECC,0x4ECF,0x4ED0,0x4ED2, 0x4EDA,0x4EDB,0x4EDC,0x4EE0,0x4EE2,0x4EE6,0x4EE7,0x4EE9, 0x4EED,0x4EEE,0x4EEF,0x4EF1,0x4EF4,0x4EF8,0x4EF9,0x4EFA, 0x4EFC,0x4EFE,0x4F00,0x4F02,0x4F03,0x4F04,0x4F05,0x4F06, 0x4F07,0x4F08,0x4F0B,0x4F0C,0x4F12,0x4F13,0x4F14,0x4F15, 0x4F16,0x4F1C,0x4F1D,0x4F21,0x4F23,0x4F28,0x4F29,0x4F2C, 0x4F2D,0x4F2E,0x4F31,0x4F33,0x4F35,0x4F37,0x4F39,0x4F3B, 0x4F3E,0x4F3F,0x4F40,0x4F41,0x4F42,0x4F44,0x4F45,0x4F47, 0x4F48,0x4F49,0x4F4A,0x4F4B,0x4F4C,0x4F52,0x4F54,0x4F56, 0x4F61,0x4F62,0x4F66,0x4F68,0x4F6A,0x4F6B,0x4F6D,0x4F6E, 0x4F71,0x4F72,0x4F75,0x4F77,0x4F78,0x4F79,0x4F7A,0x4F7D, 0x4F80,0x4F81,0x4F82,0x4F85,0x4F86,0x4F87,0x4F8A,0x4F8C, 0x4F8E,0x4F90,0x4F92,0x4F93,0x4F95,0x4F96,0x4F98,0x4F99, 0x4F9A,0x4F9C,0x4F9E,0x4F9F,0x4FA1,0x4FA2,0x4FA4,0x4FAB, 0x4FAD,0x4FB0,0x4FB1,0x4FB2,0x4FB3,0x4FB4,0x4FB6,0x4FB7, 0x4FB8,0x4FB9,0x4FBA,0x4FBB,0x4FBC,0x4FBD,0x4FBE,0x4FC0, 0x4FC1,0x4FC2,0x4FC6,0x4FC7,0x4FC8,0x4FC9,0x4FCB,0x4FCC, 0x4FCD,0x4FD2,0x4FD3,0x4FD4,0x4FD5,0x4FD6,0x4FD9,0x4FDB, 0x4FE0,0x4FE2,0x4FE4,0x4FE5,0x4FE7,0x4FEB,0x4FEC,0x4FF0, 0x4FF2,0x4FF4,0x4FF5,0x4FF6,0x4FF7,0x4FF9,0x4FFB,0x4FFC, 0x4FFD,0x4FFF,0x5000,0x5001,0x5002,0x5003,0x5004,0x5005, 0x5006,0x5007,0x5008,0x5009,0x500A,0xFFFD,0x500B,0x500E, 0x5010,0x5011,0x5013,0x5015,0x5016,0x5017,0x501B,0x501D, 0x501E,0x5020,0x5022,0x5023,0x5024,0x5027,0x502B,0x502F, 0x5030,0x5031,0x5032,0x5033,0x5034,0x5035,0x5036,0x5037, 0x5038,0x5039,0x503B,0x503D,0x503F,0x5040,0x5041,0x5042, 0x5044,0x5045,0x5046,0x5049,0x504A,0x504B,0x504D,0x5050, 0x5051,0x5052,0x5053,0x5054,0x5056,0x5057,0x5058,0x5059, 0x505B,0x505D,0x505E,0x505F,0x5060,0x5061,0x5062,0x5063, 0x5064,0x5066,0x5067,0x5068,0x5069,0x506A,0x506B,0x506D, 0x506E,0x506F,0x5070,0x5071,0x5072,0x5073,0x5074,0x5075, 0x5078,0x5079,0x507A,0x507C,0x507D,0x5081,0x5082,0x5083, 0x5084,0x5086,0x5087,0x5089,0x508A,0x508B,0x508C,0x508E, 0x508F,0x5090,0x5091,0x5092,0x5093,0x5094,0x5095,0x5096, 0x5097,0x5098,0x5099,0x509A,0x509B,0x509C,0x509D,0x509E, 0x509F,0x50A0,0x50A1,0x50A2,0x50A4,0x50A6,0x50AA,0x50AB, 0x50AD,0x50AE,0x50AF,0x50B0,0x50B1,0x50B3,0x50B4,0x50B5, 0x50B6,0x50B7,0x50B8,0x50B9,0x50BC,0x50BD,0x50BE,0x50BF, 0x50C0,0x50C1,0x50C2,0x50C3,0x50C4,0x50C5,0x50C6,0x50C7, 0x50C8,0x50C9,0x50CA,0x50CB,0x50CC,0x50CD,0x50CE,0x50D0, 0x50D1,0x50D2,0x50D3,0x50D4,0x50D5,0x50D7,0x50D8,0x50D9, 0x50DB,0x50DC,0x50DD,0x50DE,0x50DF,0x50E0,0x50E1,0x50E2, 0x50E3,0x50E4,0x50E5,0x50E8,0x50E9,0x50EA,0x50EB,0x50EF, 0x50F0,0x50F1,0x50F2,0x50F4,0x50F6,0x50F7,0x50F8,0x50F9, 0x50FA,0x50FC,0x50FD,0x50FE,0x50FF,0x5100,0x5101,0x5102, 0x5103,0x5104,0x5105,0x5108,0xFFFD,0x5109,0x510A,0x510C, 0x510D,0x510E,0x510F,0x5110,0x5111,0x5113,0x5114,0x5115, 0x5116,0x5117,0x5118,0x5119,0x511A,0x511B,0x511C,0x511D, 0x511E,0x511F,0x5120,0x5122,0x5123,0x5124,0x5125,0x5126, 0x5127,0x5128,0x5129,0x512A,0x512B,0x512C,0x512D,0x512E, 0x512F,0x5130,0x5131,0x5132,0x5133,0x5134,0x5135,0x5136, 0x5137,0x5138,0x5139,0x513A,0x513B,0x513C,0x513D,0x513E, 0x5142,0x5147,0x514A,0x514C,0x514E,0x514F,0x5150,0x5152, 0x5153,0x5157,0x5158,0x5159,0x515B,0x515D,0x515E,0x515F, 0x5160,0x5161,0x5163,0x5164,0x5166,0x5167,0x5169,0x516A, 0x516F,0x5172,0x517A,0x517E,0x517F,0x5183,0x5184,0x5186, 0x5187,0x518A,0x518B,0x518E,0x518F,0x5190,0x5191,0x5193, 0x5194,0x5198,0x519A,0x519D,0x519E,0x519F,0x51A1,0x51A3, 0x51A6,0x51A7,0x51A8,0x51A9,0x51AA,0x51AD,0x51AE,0x51B4, 0x51B8,0x51B9,0x51BA,0x51BE,0x51BF,0x51C1,0x51C2,0x51C3, 0x51C5,0x51C8,0x51CA,0x51CD,0x51CE,0x51D0,0x51D2,0x51D3, 0x51D4,0x51D5,0x51D6,0x51D7,0x51D8,0x51D9,0x51DA,0x51DC, 0x51DE,0x51DF,0x51E2,0x51E3,0x51E5,0x51E6,0x51E7,0x51E8, 0x51E9,0x51EA,0x51EC,0x51EE,0x51F1,0x51F2,0x51F4,0x51F7, 0x51FE,0x5204,0x5205,0x5209,0x520B,0x520C,0x520F,0x5210, 0x5213,0x5214,0x5215,0x521C,0x521E,0x521F,0x5221,0x5222, 0x5223,0x5225,0x5226,0x5227,0x522A,0x522C,0x522F,0x5231, 0x5232,0x5234,0x5235,0x523C,0x523E,0x5244,0x5245,0x5246, 0x5247,0x5248,0x5249,0x524B,0x524E,0x524F,0x5252,0x5253, 0x5255,0x5257,0x5258,0xFFFD,0x5259,0x525A,0x525B,0x525D, 0x525F,0x5260,0x5262,0x5263,0x5264,0x5266,0x5268,0x526B, 0x526C,0x526D,0x526E,0x5270,0x5271,0x5273,0x5274,0x5275, 0x5276,0x5277,0x5278,0x5279,0x527A,0x527B,0x527C,0x527E, 0x5280,0x5283,0x5284,0x5285,0x5286,0x5287,0x5289,0x528A, 0x528B,0x528C,0x528D,0x528E,0x528F,0x5291,0x5292,0x5294, 0x5295,0x5296,0x5297,0x5298,0x5299,0x529A,0x529C,0x52A4, 0x52A5,0x52A6,0x52A7,0x52AE,0x52AF,0x52B0,0x52B4,0x52B5, 0x52B6,0x52B7,0x52B8,0x52B9,0x52BA,0x52BB,0x52BC,0x52BD, 0x52C0,0x52C1,0x52C2,0x52C4,0x52C5,0x52C6,0x52C8,0x52CA, 0x52CC,0x52CD,0x52CE,0x52CF,0x52D1,0x52D3,0x52D4,0x52D5, 0x52D7,0x52D9,0x52DA,0x52DB,0x52DC,0x52DD,0x52DE,0x52E0, 0x52E1,0x52E2,0x52E3,0x52E5,0x52E6,0x52E7,0x52E8,0x52E9, 0x52EA,0x52EB,0x52EC,0x52ED,0x52EE,0x52EF,0x52F1,0x52F2, 0x52F3,0x52F4,0x52F5,0x52F6,0x52F7,0x52F8,0x52FB,0x52FC, 0x52FD,0x5301,0x5302,0x5303,0x5304,0x5307,0x5309,0x530A, 0x530B,0x530C,0x530E,0x5311,0x5312,0x5313,0x5314,0x5318, 0x531B,0x531C,0x531E,0x531F,0x5322,0x5324,0x5325,0x5327, 0x5328,0x5329,0x532B,0x532C,0x532D,0x532F,0x5330,0x5331, 0x5332,0x5333,0x5334,0x5335,0x5336,0x5337,0x5338,0x533C, 0x533D,0x5340,0x5342,0x5344,0x5346,0x534B,0x534C,0x534D, 0x5350,0x5354,0x5358,0x5359,0x535B,0x535D,0x5365,0x5368, 0x536A,0x536C,0x536D,0x5372,0x5376,0x5379,0x537B,0x537C, 0x537D,0x537E,0x5380,0x5381,0x5383,0x5387,0x5388,0x538A, 0x538E,0x538F,0xFFFD,0x5390,0x5391,0x5392,0x5393,0x5394, 0x5396,0x5397,0x5399,0x539B,0x539C,0x539E,0x53A0,0x53A1, 0x53A4,0x53A7,0x53AA,0x53AB,0x53AC,0x53AD,0x53AF,0x53B0, 0x53B1,0x53B2,0x53B3,0x53B4,0x53B5,0x53B7,0x53B8,0x53B9, 0x53BA,0x53BC,0x53BD,0x53BE,0x53C0,0x53C3,0x53C4,0x53C5, 0x53C6,0x53C7,0x53CE,0x53CF,0x53D0,0x53D2,0x53D3,0x53D5, 0x53DA,0x53DC,0x53DD,0x53DE,0x53E1,0x53E2,0x53E7,0x53F4, 0x53FA,0x53FE,0x53FF,0x5400,0x5402,0x5405,0x5407,0x540B, 0x5414,0x5418,0x5419,0x541A,0x541C,0x5422,0x5424,0x5425, 0x542A,0x5430,0x5433,0x5436,0x5437,0x543A,0x543D,0x543F, 0x5441,0x5442,0x5444,0x5445,0x5447,0x5449,0x544C,0x544D, 0x544E,0x544F,0x5451,0x545A,0x545D,0x545E,0x545F,0x5460, 0x5461,0x5463,0x5465,0x5467,0x5469,0x546A,0x546B,0x546C, 0x546D,0x546E,0x546F,0x5470,0x5474,0x5479,0x547A,0x547E, 0x547F,0x5481,0x5483,0x5485,0x5487,0x5488,0x5489,0x548A, 0x548D,0x5491,0x5493,0x5497,0x5498,0x549C,0x549E,0x549F, 0x54A0,0x54A1,0x54A2,0x54A5,0x54AE,0x54B0,0x54B2,0x54B5, 0x54B6,0x54B7,0x54B9,0x54BA,0x54BC,0x54BE,0x54C3,0x54C5, 0x54CA,0x54CB,0x54D6,0x54D8,0x54DB,0x54E0,0x54E1,0x54E2, 0x54E3,0x54E4,0x54EB,0x54EC,0x54EF,0x54F0,0x54F1,0x54F4, 0x54F5,0x54F6,0x54F7,0x54F8,0x54F9,0x54FB,0x54FE,0x5500, 0x5502,0x5503,0x5504,0x5505,0x5508,0x550A,0x550B,0x550C, 0x550D,0x550E,0x5512,0x5513,0x5515,0x5516,0x5517,0x5518, 0x5519,0x551A,0x551C,0x551D,0x551E,0x551F,0x5521,0x5525, 0x5526,0xFFFD,0x5528,0x5529,0x552B,0x552D,0x5532,0x5534, 0x5535,0x5536,0x5538,0x5539,0x553A,0x553B,0x553D,0x5540, 0x5542,0x5545,0x5547,0x5548,0x554B,0x554C,0x554D,0x554E, 0x554F,0x5551,0x5552,0x5553,0x5554,0x5557,0x5558,0x5559, 0x555A,0x555B,0x555D,0x555E,0x555F,0x5560,0x5562,0x5563, 0x5568,0x5569,0x556B,0x556F,0x5570,0x5571,0x5572,0x5573, 0x5574,0x5579,0x557A,0x557D,0x557F,0x5585,0x5586,0x558C, 0x558D,0x558E,0x5590,0x5592,0x5593,0x5595,0x5596,0x5597, 0x559A,0x559B,0x559E,0x55A0,0x55A1,0x55A2,0x55A3,0x55A4, 0x55A5,0x55A6,0x55A8,0x55A9,0x55AA,0x55AB,0x55AC,0x55AD, 0x55AE,0x55AF,0x55B0,0x55B2,0x55B4,0x55B6,0x55B8,0x55BA, 0x55BC,0x55BF,0x55C0,0x55C1,0x55C2,0x55C3,0x55C6,0x55C7, 0x55C8,0x55CA,0x55CB,0x55CE,0x55CF,0x55D0,0x55D5,0x55D7, 0x55D8,0x55D9,0x55DA,0x55DB,0x55DE,0x55E0,0x55E2,0x55E7, 0x55E9,0x55ED,0x55EE,0x55F0,0x55F1,0x55F4,0x55F6,0x55F8, 0x55F9,0x55FA,0x55FB,0x55FC,0x55FF,0x5602,0x5603,0x5604, 0x5605,0x5606,0x5607,0x560A,0x560B,0x560D,0x5610,0x5611, 0x5612,0x5613,0x5614,0x5615,0x5616,0x5617,0x5619,0x561A, 0x561C,0x561D,0x5620,0x5621,0x5622,0x5625,0x5626,0x5628, 0x5629,0x562A,0x562B,0x562E,0x562F,0x5630,0x5633,0x5635, 0x5637,0x5638,0x563A,0x563C,0x563D,0x563E,0x5640,0x5641, 0x5642,0x5643,0x5644,0x5645,0x5646,0x5647,0x5648,0x5649, 0x564A,0x564B,0x564F,0x5650,0x5651,0x5652,0x5653,0x5655, 0x5656,0x565A,0x565B,0x565D,0x565E,0x565F,0x5660,0x5661, 0xFFFD,0x5663,0x5665,0x5666,0x5667,0x566D,0x566E,0x566F, 0x5670,0x5672,0x5673,0x5674,0x5675,0x5677,0x5678,0x5679, 0x567A,0x567D,0x567E,0x567F,0x5680,0x5681,0x5682,0x5683, 0x5684,0x5687,0x5688,0x5689,0x568A,0x568B,0x568C,0x568D, 0x5690,0x5691,0x5692,0x5694,0x5695,0x5696,0x5697,0x5698, 0x5699,0x569A,0x569B,0x569C,0x569D,0x569E,0x569F,0x56A0, 0x56A1,0x56A2,0x56A4,0x56A5,0x56A6,0x56A7,0x56A8,0x56A9, 0x56AA,0x56AB,0x56AC,0x56AD,0x56AE,0x56B0,0x56B1,0x56B2, 0x56B3,0x56B4,0x56B5,0x56B6,0x56B8,0x56B9,0x56BA,0x56BB, 0x56BD,0x56BE,0x56BF,0x56C0,0x56C1,0x56C2,0x56C3,0x56C4, 0x56C5,0x56C6,0x56C7,0x56C8,0x56C9,0x56CB,0x56CC,0x56CD, 0x56CE,0x56CF,0x56D0,0x56D1,0x56D2,0x56D3,0x56D5,0x56D6, 0x56D8,0x56D9,0x56DC,0x56E3,0x56E5,0x56E6,0x56E7,0x56E8, 0x56E9,0x56EA,0x56EC,0x56EE,0x56EF,0x56F2,0x56F3,0x56F6, 0x56F7,0x56F8,0x56FB,0x56FC,0x5700,0x5701,0x5702,0x5705, 0x5707,0x570B,0x570C,0x570D,0x570E,0x570F,0x5710,0x5711, 0x5712,0x5713,0x5714,0x5715,0x5716,0x5717,0x5718,0x5719, 0x571A,0x571B,0x571D,0x571E,0x5720,0x5721,0x5722,0x5724, 0x5725,0x5726,0x5727,0x572B,0x5731,0x5732,0x5734,0x5735, 0x5736,0x5737,0x5738,0x573C,0x573D,0x573F,0x5741,0x5743, 0x5744,0x5745,0x5746,0x5748,0x5749,0x574B,0x5752,0x5753, 0x5754,0x5755,0x5756,0x5758,0x5759,0x5762,0x5763,0x5765, 0x5767,0x576C,0x576E,0x5770,0x5771,0x5772,0x5774,0x5775, 0x5778,0x5779,0x577A,0x577D,0x577E,0x577F,0x5780,0xFFFD, 0x5781,0x5787,0x5788,0x5789,0x578A,0x578D,0x578E,0x578F, 0x5790,0x5791,0x5794,0x5795,0x5796,0x5797,0x5798,0x5799, 0x579A,0x579C,0x579D,0x579E,0x579F,0x57A5,0x57A8,0x57AA, 0x57AC,0x57AF,0x57B0,0x57B1,0x57B3,0x57B5,0x57B6,0x57B7, 0x57B9,0x57BA,0x57BB,0x57BC,0x57BD,0x57BE,0x57BF,0x57C0, 0x57C1,0x57C4,0x57C5,0x57C6,0x57C7,0x57C8,0x57C9,0x57CA, 0x57CC,0x57CD,0x57D0,0x57D1,0x57D3,0x57D6,0x57D7,0x57DB, 0x57DC,0x57DE,0x57E1,0x57E2,0x57E3,0x57E5,0x57E6,0x57E7, 0x57E8,0x57E9,0x57EA,0x57EB,0x57EC,0x57EE,0x57F0,0x57F1, 0x57F2,0x57F3,0x57F5,0x57F6,0x57F7,0x57FB,0x57FC,0x57FE, 0x57FF,0x5801,0x5803,0x5804,0x5805,0x5808,0x5809,0x580A, 0x580C,0x580E,0x580F,0x5810,0x5812,0x5813,0x5814,0x5816, 0x5817,0x5818,0x581A,0x581B,0x581C,0x581D,0x581F,0x5822, 0x5823,0x5825,0x5826,0x5827,0x5828,0x5829,0x582B,0x582C, 0x582D,0x582E,0x582F,0x5831,0x5832,0x5833,0x5834,0x5836, 0x5837,0x5838,0x5839,0x583A,0x583B,0x583C,0x583D,0x583E, 0x583F,0x5840,0x5841,0x5842,0x5843,0x5845,0x5846,0x5847, 0x5848,0x5849,0x584A,0x584B,0x584E,0x584F,0x5850,0x5852, 0x5853,0x5855,0x5856,0x5857,0x5859,0x585A,0x585B,0x585C, 0x585D,0x585F,0x5860,0x5861,0x5862,0x5863,0x5864,0x5866, 0x5867,0x5868,0x5869,0x586A,0x586D,0x586E,0x586F,0x5870, 0x5871,0x5872,0x5873,0x5874,0x5875,0x5876,0x5877,0x5878, 0x5879,0x587A,0x587B,0x587C,0x587D,0x587F,0x5882,0x5884, 0x5886,0x5887,0x5888,0x588A,0x588B,0x588C,0xFFFD,0x588D, 0x588E,0x588F,0x5890,0x5891,0x5894,0x5895,0x5896,0x5897, 0x5898,0x589B,0x589C,0x589D,0x58A0,0x58A1,0x58A2,0x58A3, 0x58A4,0x58A5,0x58A6,0x58A7,0x58AA,0x58AB,0x58AC,0x58AD, 0x58AE,0x58AF,0x58B0,0x58B1,0x58B2,0x58B3,0x58B4,0x58B5, 0x58B6,0x58B7,0x58B8,0x58B9,0x58BA,0x58BB,0x58BD,0x58BE, 0x58BF,0x58C0,0x58C2,0x58C3,0x58C4,0x58C6,0x58C7,0x58C8, 0x58C9,0x58CA,0x58CB,0x58CC,0x58CD,0x58CE,0x58CF,0x58D0, 0x58D2,0x58D3,0x58D4,0x58D6,0x58D7,0x58D8,0x58D9,0x58DA, 0x58DB,0x58DC,0x58DD,0x58DE,0x58DF,0x58E0,0x58E1,0x58E2, 0x58E3,0x58E5,0x58E6,0x58E7,0x58E8,0x58E9,0x58EA,0x58ED, 0x58EF,0x58F1,0x58F2,0x58F4,0x58F5,0x58F7,0x58F8,0x58FA, 0x58FB,0x58FC,0x58FD,0x58FE,0x58FF,0x5900,0x5901,0x5903, 0x5905,0x5906,0x5908,0x5909,0x590A,0x590B,0x590C,0x590E, 0x5910,0x5911,0x5912,0x5913,0x5917,0x5918,0x591B,0x591D, 0x591E,0x5920,0x5921,0x5922,0x5923,0x5926,0x5928,0x592C, 0x5930,0x5932,0x5933,0x5935,0x5936,0x593B,0x593D,0x593E, 0x593F,0x5940,0x5943,0x5945,0x5946,0x594A,0x594C,0x594D, 0x5950,0x5952,0x5953,0x5959,0x595B,0x595C,0x595D,0x595E, 0x595F,0x5961,0x5963,0x5964,0x5966,0x5967,0x5968,0x5969, 0x596A,0x596B,0x596C,0x596D,0x596E,0x596F,0x5970,0x5971, 0x5972,0x5975,0x5977,0x597A,0x597B,0x597C,0x597E,0x597F, 0x5980,0x5985,0x5989,0x598B,0x598C,0x598E,0x598F,0x5990, 0x5991,0x5994,0x5995,0x5998,0x599A,0x599B,0x599C,0x599D, 0x599F,0x59A0,0x59A1,0x59A2,0x59A6,0xFFFD,0x59A7,0x59AC, 0x59AD,0x59B0,0x59B1,0x59B3,0x59B4,0x59B5,0x59B6,0x59B7, 0x59B8,0x59BA,0x59BC,0x59BD,0x59BF,0x59C0,0x59C1,0x59C2, 0x59C3,0x59C4,0x59C5,0x59C7,0x59C8,0x59C9,0x59CC,0x59CD, 0x59CE,0x59CF,0x59D5,0x59D6,0x59D9,0x59DB,0x59DE,0x59DF, 0x59E0,0x59E1,0x59E2,0x59E4,0x59E6,0x59E7,0x59E9,0x59EA, 0x59EB,0x59ED,0x59EE,0x59EF,0x59F0,0x59F1,0x59F2,0x59F3, 0x59F4,0x59F5,0x59F6,0x59F7,0x59F8,0x59FA,0x59FC,0x59FD, 0x59FE,0x5A00,0x5A02,0x5A0A,0x5A0B,0x5A0D,0x5A0E,0x5A0F, 0x5A10,0x5A12,0x5A14,0x5A15,0x5A16,0x5A17,0x5A19,0x5A1A, 0x5A1B,0x5A1D,0x5A1E,0x5A21,0x5A22,0x5A24,0x5A26,0x5A27, 0x5A28,0x5A2A,0x5A2B,0x5A2C,0x5A2D,0x5A2E,0x5A2F,0x5A30, 0x5A33,0x5A35,0x5A37,0x5A38,0x5A39,0x5A3A,0x5A3B,0x5A3D, 0x5A3E,0x5A3F,0x5A41,0x5A42,0x5A43,0x5A44,0x5A45,0x5A47, 0x5A48,0x5A4B,0x5A4C,0x5A4D,0x5A4E,0x5A4F,0x5A50,0x5A51, 0x5A52,0x5A53,0x5A54,0x5A56,0x5A57,0x5A58,0x5A59,0x5A5B, 0x5A5C,0x5A5D,0x5A5E,0x5A5F,0x5A60,0x5A61,0x5A63,0x5A64, 0x5A65,0x5A66,0x5A68,0x5A69,0x5A6B,0x5A6C,0x5A6D,0x5A6E, 0x5A6F,0x5A70,0x5A71,0x5A72,0x5A73,0x5A78,0x5A79,0x5A7B, 0x5A7C,0x5A7D,0x5A7E,0x5A80,0x5A81,0x5A82,0x5A83,0x5A84, 0x5A85,0x5A86,0x5A87,0x5A88,0x5A89,0x5A8A,0x5A8B,0x5A8C, 0x5A8D,0x5A8E,0x5A8F,0x5A90,0x5A91,0x5A93,0x5A94,0x5A95, 0x5A96,0x5A97,0x5A98,0x5A99,0x5A9C,0x5A9D,0x5A9E,0x5A9F, 0x5AA0,0x5AA1,0x5AA2,0x5AA3,0x5AA4,0x5AA5,0x5AA6,0x5AA7, 0x5AA8,0x5AA9,0x5AAB,0x5AAC,0xFFFD,0x5AAD,0x5AAE,0x5AAF, 0x5AB0,0x5AB1,0x5AB4,0x5AB6,0x5AB7,0x5AB9,0x5ABA,0x5ABB, 0x5ABC,0x5ABD,0x5ABF,0x5AC0,0x5AC3,0x5AC4,0x5AC5,0x5AC6, 0x5AC7,0x5AC8,0x5ACA,0x5ACB,0x5ACD,0x5ACE,0x5ACF,0x5AD0, 0x5AD1,0x5AD3,0x5AD5,0x5AD7,0x5AD9,0x5ADA,0x5ADB,0x5ADD, 0x5ADE,0x5ADF,0x5AE2,0x5AE4,0x5AE5,0x5AE7,0x5AE8,0x5AEA, 0x5AEC,0x5AED,0x5AEE,0x5AEF,0x5AF0,0x5AF2,0x5AF3,0x5AF4, 0x5AF5,0x5AF6,0x5AF7,0x5AF8,0x5AF9,0x5AFA,0x5AFB,0x5AFC, 0x5AFD,0x5AFE,0x5AFF,0x5B00,0x5B01,0x5B02,0x5B03,0x5B04, 0x5B05,0x5B06,0x5B07,0x5B08,0x5B0A,0x5B0B,0x5B0C,0x5B0D, 0x5B0E,0x5B0F,0x5B10,0x5B11,0x5B12,0x5B13,0x5B14,0x5B15, 0x5B18,0x5B19,0x5B1A,0x5B1B,0x5B1C,0x5B1D,0x5B1E,0x5B1F, 0x5B20,0x5B21,0x5B22,0x5B23,0x5B24,0x5B25,0x5B26,0x5B27, 0x5B28,0x5B29,0x5B2A,0x5B2B,0x5B2C,0x5B2D,0x5B2E,0x5B2F, 0x5B30,0x5B31,0x5B33,0x5B35,0x5B36,0x5B38,0x5B39,0x5B3A, 0x5B3B,0x5B3C,0x5B3D,0x5B3E,0x5B3F,0x5B41,0x5B42,0x5B43, 0x5B44,0x5B45,0x5B46,0x5B47,0x5B48,0x5B49,0x5B4A,0x5B4B, 0x5B4C,0x5B4D,0x5B4E,0x5B4F,0x5B52,0x5B56,0x5B5E,0x5B60, 0x5B61,0x5B67,0x5B68,0x5B6B,0x5B6D,0x5B6E,0x5B6F,0x5B72, 0x5B74,0x5B76,0x5B77,0x5B78,0x5B79,0x5B7B,0x5B7C,0x5B7E, 0x5B7F,0x5B82,0x5B86,0x5B8A,0x5B8D,0x5B8E,0x5B90,0x5B91, 0x5B92,0x5B94,0x5B96,0x5B9F,0x5BA7,0x5BA8,0x5BA9,0x5BAC, 0x5BAD,0x5BAE,0x5BAF,0x5BB1,0x5BB2,0x5BB7,0x5BBA,0x5BBB, 0x5BBC,0x5BC0,0x5BC1,0x5BC3,0x5BC8,0x5BC9,0x5BCA,0x5BCB, 0x5BCD,0x5BCE,0x5BCF,0xFFFD,0x5BD1,0x5BD4,0x5BD5,0x5BD6, 0x5BD7,0x5BD8,0x5BD9,0x5BDA,0x5BDB,0x5BDC,0x5BE0,0x5BE2, 0x5BE3,0x5BE6,0x5BE7,0x5BE9,0x5BEA,0x5BEB,0x5BEC,0x5BED, 0x5BEF,0x5BF1,0x5BF2,0x5BF3,0x5BF4,0x5BF5,0x5BF6,0x5BF7, 0x5BFD,0x5BFE,0x5C00,0x5C02,0x5C03,0x5C05,0x5C07,0x5C08, 0x5C0B,0x5C0C,0x5C0D,0x5C0E,0x5C10,0x5C12,0x5C13,0x5C17, 0x5C19,0x5C1B,0x5C1E,0x5C1F,0x5C20,0x5C21,0x5C23,0x5C26, 0x5C28,0x5C29,0x5C2A,0x5C2B,0x5C2D,0x5C2E,0x5C2F,0x5C30, 0x5C32,0x5C33,0x5C35,0x5C36,0x5C37,0x5C43,0x5C44,0x5C46, 0x5C47,0x5C4C,0x5C4D,0x5C52,0x5C53,0x5C54,0x5C56,0x5C57, 0x5C58,0x5C5A,0x5C5B,0x5C5C,0x5C5D,0x5C5F,0x5C62,0x5C64, 0x5C67,0x5C68,0x5C69,0x5C6A,0x5C6B,0x5C6C,0x5C6D,0x5C70, 0x5C72,0x5C73,0x5C74,0x5C75,0x5C76,0x5C77,0x5C78,0x5C7B, 0x5C7C,0x5C7D,0x5C7E,0x5C80,0x5C83,0x5C84,0x5C85,0x5C86, 0x5C87,0x5C89,0x5C8A,0x5C8B,0x5C8E,0x5C8F,0x5C92,0x5C93, 0x5C95,0x5C9D,0x5C9E,0x5C9F,0x5CA0,0x5CA1,0x5CA4,0x5CA5, 0x5CA6,0x5CA7,0x5CA8,0x5CAA,0x5CAE,0x5CAF,0x5CB0,0x5CB2, 0x5CB4,0x5CB6,0x5CB9,0x5CBA,0x5CBB,0x5CBC,0x5CBE,0x5CC0, 0x5CC2,0x5CC3,0x5CC5,0x5CC6,0x5CC7,0x5CC8,0x5CC9,0x5CCA, 0x5CCC,0x5CCD,0x5CCE,0x5CCF,0x5CD0,0x5CD1,0x5CD3,0x5CD4, 0x5CD5,0x5CD6,0x5CD7,0x5CD8,0x5CDA,0x5CDB,0x5CDC,0x5CDD, 0x5CDE,0x5CDF,0x5CE0,0x5CE2,0x5CE3,0x5CE7,0x5CE9,0x5CEB, 0x5CEC,0x5CEE,0x5CEF,0x5CF1,0x5CF2,0x5CF3,0x5CF4,0x5CF5, 0x5CF6,0x5CF7,0x5CF8,0x5CF9,0x5CFA,0x5CFC,0x5CFD,0x5CFE, 0x5CFF,0x5D00,0xFFFD,0x5D01,0x5D04,0x5D05,0x5D08,0x5D09, 0x5D0A,0x5D0B,0x5D0C,0x5D0D,0x5D0F,0x5D10,0x5D11,0x5D12, 0x5D13,0x5D15,0x5D17,0x5D18,0x5D19,0x5D1A,0x5D1C,0x5D1D, 0x5D1F,0x5D20,0x5D21,0x5D22,0x5D23,0x5D25,0x5D28,0x5D2A, 0x5D2B,0x5D2C,0x5D2F,0x5D30,0x5D31,0x5D32,0x5D33,0x5D35, 0x5D36,0x5D37,0x5D38,0x5D39,0x5D3A,0x5D3B,0x5D3C,0x5D3F, 0x5D40,0x5D41,0x5D42,0x5D43,0x5D44,0x5D45,0x5D46,0x5D48, 0x5D49,0x5D4D,0x5D4E,0x5D4F,0x5D50,0x5D51,0x5D52,0x5D53, 0x5D54,0x5D55,0x5D56,0x5D57,0x5D59,0x5D5A,0x5D5C,0x5D5E, 0x5D5F,0x5D60,0x5D61,0x5D62,0x5D63,0x5D64,0x5D65,0x5D66, 0x5D67,0x5D68,0x5D6A,0x5D6D,0x5D6E,0x5D70,0x5D71,0x5D72, 0x5D73,0x5D75,0x5D76,0x5D77,0x5D78,0x5D79,0x5D7A,0x5D7B, 0x5D7C,0x5D7D,0x5D7E,0x5D7F,0x5D80,0x5D81,0x5D83,0x5D84, 0x5D85,0x5D86,0x5D87,0x5D88,0x5D89,0x5D8A,0x5D8B,0x5D8C, 0x5D8D,0x5D8E,0x5D8F,0x5D90,0x5D91,0x5D92,0x5D93,0x5D94, 0x5D95,0x5D96,0x5D97,0x5D98,0x5D9A,0x5D9B,0x5D9C,0x5D9E, 0x5D9F,0x5DA0,0x5DA1,0x5DA2,0x5DA3,0x5DA4,0x5DA5,0x5DA6, 0x5DA7,0x5DA8,0x5DA9,0x5DAA,0x5DAB,0x5DAC,0x5DAD,0x5DAE, 0x5DAF,0x5DB0,0x5DB1,0x5DB2,0x5DB3,0x5DB4,0x5DB5,0x5DB6, 0x5DB8,0x5DB9,0x5DBA,0x5DBB,0x5DBC,0x5DBD,0x5DBE,0x5DBF, 0x5DC0,0x5DC1,0x5DC2,0x5DC3,0x5DC4,0x5DC6,0x5DC7,0x5DC8, 0x5DC9,0x5DCA,0x5DCB,0x5DCC,0x5DCE,0x5DCF,0x5DD0,0x5DD1, 0x5DD2,0x5DD3,0x5DD4,0x5DD5,0x5DD6,0x5DD7,0x5DD8,0x5DD9, 0x5DDA,0x5DDC,0x5DDF,0x5DE0,0x5DE3,0x5DE4,0x5DEA,0x5DEC, 0x5DED,0xFFFD,0x5DF0,0x5DF5,0x5DF6,0x5DF8,0x5DF9,0x5DFA, 0x5DFB,0x5DFC,0x5DFF,0x5E00,0x5E04,0x5E07,0x5E09,0x5E0A, 0x5E0B,0x5E0D,0x5E0E,0x5E12,0x5E13,0x5E17,0x5E1E,0x5E1F, 0x5E20,0x5E21,0x5E22,0x5E23,0x5E24,0x5E25,0x5E28,0x5E29, 0x5E2A,0x5E2B,0x5E2C,0x5E2F,0x5E30,0x5E32,0x5E33,0x5E34, 0x5E35,0x5E36,0x5E39,0x5E3A,0x5E3E,0x5E3F,0x5E40,0x5E41, 0x5E43,0x5E46,0x5E47,0x5E48,0x5E49,0x5E4A,0x5E4B,0x5E4D, 0x5E4E,0x5E4F,0x5E50,0x5E51,0x5E52,0x5E53,0x5E56,0x5E57, 0x5E58,0x5E59,0x5E5A,0x5E5C,0x5E5D,0x5E5F,0x5E60,0x5E63, 0x5E64,0x5E65,0x5E66,0x5E67,0x5E68,0x5E69,0x5E6A,0x5E6B, 0x5E6C,0x5E6D,0x5E6E,0x5E6F,0x5E70,0x5E71,0x5E75,0x5E77, 0x5E79,0x5E7E,0x5E81,0x5E82,0x5E83,0x5E85,0x5E88,0x5E89, 0x5E8C,0x5E8D,0x5E8E,0x5E92,0x5E98,0x5E9B,0x5E9D,0x5EA1, 0x5EA2,0x5EA3,0x5EA4,0x5EA8,0x5EA9,0x5EAA,0x5EAB,0x5EAC, 0x5EAE,0x5EAF,0x5EB0,0x5EB1,0x5EB2,0x5EB4,0x5EBA,0x5EBB, 0x5EBC,0x5EBD,0x5EBF,0x5EC0,0x5EC1,0x5EC2,0x5EC3,0x5EC4, 0x5EC5,0x5EC6,0x5EC7,0x5EC8,0x5ECB,0x5ECC,0x5ECD,0x5ECE, 0x5ECF,0x5ED0,0x5ED4,0x5ED5,0x5ED7,0x5ED8,0x5ED9,0x5EDA, 0x5EDC,0x5EDD,0x5EDE,0x5EDF,0x5EE0,0x5EE1,0x5EE2,0x5EE3, 0x5EE4,0x5EE5,0x5EE6,0x5EE7,0x5EE9,0x5EEB,0x5EEC,0x5EED, 0x5EEE,0x5EEF,0x5EF0,0x5EF1,0x5EF2,0x5EF3,0x5EF5,0x5EF8, 0x5EF9,0x5EFB,0x5EFC,0x5EFD,0x5F05,0x5F06,0x5F07,0x5F09, 0x5F0C,0x5F0D,0x5F0E,0x5F10,0x5F12,0x5F14,0x5F16,0x5F19, 0x5F1A,0x5F1C,0x5F1D,0x5F1E,0x5F21,0x5F22,0x5F23,0x5F24, 0xFFFD,0x5F28,0x5F2B,0x5F2C,0x5F2E,0x5F30,0x5F32,0x5F33, 0x5F34,0x5F35,0x5F36,0x5F37,0x5F38,0x5F3B,0x5F3D,0x5F3E, 0x5F3F,0x5F41,0x5F42,0x5F43,0x5F44,0x5F45,0x5F46,0x5F47, 0x5F48,0x5F49,0x5F4A,0x5F4B,0x5F4C,0x5F4D,0x5F4E,0x5F4F, 0x5F51,0x5F54,0x5F59,0x5F5A,0x5F5B,0x5F5C,0x5F5E,0x5F5F, 0x5F60,0x5F63,0x5F65,0x5F67,0x5F68,0x5F6B,0x5F6E,0x5F6F, 0x5F72,0x5F74,0x5F75,0x5F76,0x5F78,0x5F7A,0x5F7D,0x5F7E, 0x5F7F,0x5F83,0x5F86,0x5F8D,0x5F8E,0x5F8F,0x5F91,0x5F93, 0x5F94,0x5F96,0x5F9A,0x5F9B,0x5F9D,0x5F9E,0x5F9F,0x5FA0, 0x5FA2,0x5FA3,0x5FA4,0x5FA5,0x5FA6,0x5FA7,0x5FA9,0x5FAB, 0x5FAC,0x5FAF,0x5FB0,0x5FB1,0x5FB2,0x5FB3,0x5FB4,0x5FB6, 0x5FB8,0x5FB9,0x5FBA,0x5FBB,0x5FBE,0x5FBF,0x5FC0,0x5FC1, 0x5FC2,0x5FC7,0x5FC8,0x5FCA,0x5FCB,0x5FCE,0x5FD3,0x5FD4, 0x5FD5,0x5FDA,0x5FDB,0x5FDC,0x5FDE,0x5FDF,0x5FE2,0x5FE3, 0x5FE5,0x5FE6,0x5FE8,0x5FE9,0x5FEC,0x5FEF,0x5FF0,0x5FF2, 0x5FF3,0x5FF4,0x5FF6,0x5FF7,0x5FF9,0x5FFA,0x5FFC,0x6007 }; const static uint16 gbkDecoderInnerIndex1[]= { 0x6008,0x6009,0x600B,0x600C,0x6010,0x6011,0x6013,0x6017, 0x6018,0x601A,0x601E,0x601F,0x6022,0x6023,0x6024,0x602C, 0x602D,0x602E,0x6030,0x6031,0x6032,0x6033,0x6034,0x6036, 0x6037,0x6038,0x6039,0x603A,0x603D,0x603E,0x6040,0x6044, 0x6045,0x6046,0x6047,0x6048,0x6049,0x604A,0x604C,0x604E, 0x604F,0x6051,0x6053,0x6054,0x6056,0x6057,0x6058,0x605B, 0x605C,0x605E,0x605F,0x6060,0x6061,0x6065,0x6066,0x606E, 0x6071,0x6072,0x6074,0x6075,0x6077,0x607E,0x6080,0xFFFD, 0x6081,0x6082,0x6085,0x6086,0x6087,0x6088,0x608A,0x608B, 0x608E,0x608F,0x6090,0x6091,0x6093,0x6095,0x6097,0x6098, 0x6099,0x609C,0x609E,0x60A1,0x60A2,0x60A4,0x60A5,0x60A7, 0x60A9,0x60AA,0x60AE,0x60B0,0x60B3,0x60B5,0x60B6,0x60B7, 0x60B9,0x60BA,0x60BD,0x60BE,0x60BF,0x60C0,0x60C1,0x60C2, 0x60C3,0x60C4,0x60C7,0x60C8,0x60C9,0x60CC,0x60CD,0x60CE, 0x60CF,0x60D0,0x60D2,0x60D3,0x60D4,0x60D6,0x60D7,0x60D9, 0x60DB,0x60DE,0x60E1,0x60E2,0x60E3,0x60E4,0x60E5,0x60EA, 0x60F1,0x60F2,0x60F5,0x60F7,0x60F8,0x60FB,0x60FC,0x60FD, 0x60FE,0x60FF,0x6102,0x6103,0x6104,0x6105,0x6107,0x610A, 0x610B,0x610C,0x6110,0x6111,0x6112,0x6113,0x6114,0x6116, 0x6117,0x6118,0x6119,0x611B,0x611C,0x611D,0x611E,0x6121, 0x6122,0x6125,0x6128,0x6129,0x612A,0x612C,0x612D,0x612E, 0x612F,0x6130,0x6131,0x6132,0x6133,0x6134,0x6135,0x6136, 0x6137,0x6138,0x6139,0x613A,0x613B,0x613C,0x613D,0x613E, 0x6140,0x6141,0x6142,0x6143,0x6144,0x6145,0x6146,0x6147, 0x6149,0x614B,0x614D,0x614F,0x6150,0x6152,0x6153,0x6154, 0x6156,0x6157,0x6158,0x6159,0x615A,0x615B,0x615C,0x615E, 0x615F,0x6160,0x6161,0x6163,0x6164,0x6165,0x6166,0x6169, 0x616A,0x616B,0x616C,0x616D,0x616E,0x616F,0x6171,0x6172, 0x6173,0x6174,0x6176,0x6178,0x6179,0x617A,0x617B,0x617C, 0x617D,0x617E,0x617F,0x6180,0x6181,0x6182,0x6183,0x6184, 0x6185,0x6186,0x6187,0x6188,0x6189,0x618A,0x618C,0x618D, 0x618F,0x6190,0x6191,0x6192,0x6193,0x6195,0xFFFD,0x6196, 0x6197,0x6198,0x6199,0x619A,0x619B,0x619C,0x619E,0x619F, 0x61A0,0x61A1,0x61A2,0x61A3,0x61A4,0x61A5,0x61A6,0x61AA, 0x61AB,0x61AD,0x61AE,0x61AF,0x61B0,0x61B1,0x61B2,0x61B3, 0x61B4,0x61B5,0x61B6,0x61B8,0x61B9,0x61BA,0x61BB,0x61BC, 0x61BD,0x61BF,0x61C0,0x61C1,0x61C3,0x61C4,0x61C5,0x61C6, 0x61C7,0x61C9,0x61CC,0x61CD,0x61CE,0x61CF,0x61D0,0x61D3, 0x61D5,0x61D6,0x61D7,0x61D8,0x61D9,0x61DA,0x61DB,0x61DC, 0x61DD,0x61DE,0x61DF,0x61E0,0x61E1,0x61E2,0x61E3,0x61E4, 0x61E5,0x61E7,0x61E8,0x61E9,0x61EA,0x61EB,0x61EC,0x61ED, 0x61EE,0x61EF,0x61F0,0x61F1,0x61F2,0x61F3,0x61F4,0x61F6, 0x61F7,0x61F8,0x61F9,0x61FA,0x61FB,0x61FC,0x61FD,0x61FE, 0x6200,0x6201,0x6202,0x6203,0x6204,0x6205,0x6207,0x6209, 0x6213,0x6214,0x6219,0x621C,0x621D,0x621E,0x6220,0x6223, 0x6226,0x6227,0x6228,0x6229,0x622B,0x622D,0x622F,0x6230, 0x6231,0x6232,0x6235,0x6236,0x6238,0x6239,0x623A,0x623B, 0x623C,0x6242,0x6244,0x6245,0x6246,0x624A,0x624F,0x6250, 0x6255,0x6256,0x6257,0x6259,0x625A,0x625C,0x625D,0x625E, 0x625F,0x6260,0x6261,0x6262,0x6264,0x6265,0x6268,0x6271, 0x6272,0x6274,0x6275,0x6277,0x6278,0x627A,0x627B,0x627D, 0x6281,0x6282,0x6283,0x6285,0x6286,0x6287,0x6288,0x628B, 0x628C,0x628D,0x628E,0x628F,0x6290,0x6294,0x6299,0x629C, 0x629D,0x629E,0x62A3,0x62A6,0x62A7,0x62A9,0x62AA,0x62AD, 0x62AE,0x62AF,0x62B0,0x62B2,0x62B3,0x62B4,0x62B6,0x62B7, 0x62B8,0x62BA,0x62BE,0x62C0,0x62C1,0xFFFD,0x62C3,0x62CB, 0x62CF,0x62D1,0x62D5,0x62DD,0x62DE,0x62E0,0x62E1,0x62E4, 0x62EA,0x62EB,0x62F0,0x62F2,0x62F5,0x62F8,0x62F9,0x62FA, 0x62FB,0x6300,0x6303,0x6304,0x6305,0x6306,0x630A,0x630B, 0x630C,0x630D,0x630F,0x6310,0x6312,0x6313,0x6314,0x6315, 0x6317,0x6318,0x6319,0x631C,0x6326,0x6327,0x6329,0x632C, 0x632D,0x632E,0x6330,0x6331,0x6333,0x6334,0x6335,0x6336, 0x6337,0x6338,0x633B,0x633C,0x633E,0x633F,0x6340,0x6341, 0x6344,0x6347,0x6348,0x634A,0x6351,0x6352,0x6353,0x6354, 0x6356,0x6357,0x6358,0x6359,0x635A,0x635B,0x635C,0x635D, 0x6360,0x6364,0x6365,0x6366,0x6368,0x636A,0x636B,0x636C, 0x636F,0x6370,0x6372,0x6373,0x6374,0x6375,0x6378,0x6379, 0x637C,0x637D,0x637E,0x637F,0x6381,0x6383,0x6384,0x6385, 0x6386,0x638B,0x638D,0x6391,0x6393,0x6394,0x6395,0x6397, 0x6399,0x639A,0x639B,0x639C,0x639D,0x639E,0x639F,0x63A1, 0x63A4,0x63A6,0x63AB,0x63AF,0x63B1,0x63B2,0x63B5,0x63B6, 0x63B9,0x63BB,0x63BD,0x63BF,0x63C0,0x63C1,0x63C2,0x63C3, 0x63C5,0x63C7,0x63C8,0x63CA,0x63CB,0x63CC,0x63D1,0x63D3, 0x63D4,0x63D5,0x63D7,0x63D8,0x63D9,0x63DA,0x63DB,0x63DC, 0x63DD,0x63DF,0x63E2,0x63E4,0x63E5,0x63E6,0x63E7,0x63E8, 0x63EB,0x63EC,0x63EE,0x63EF,0x63F0,0x63F1,0x63F3,0x63F5, 0x63F7,0x63F9,0x63FA,0x63FB,0x63FC,0x63FE,0x6403,0x6404, 0x6406,0x6407,0x6408,0x6409,0x640A,0x640D,0x640E,0x6411, 0x6412,0x6415,0x6416,0x6417,0x6418,0x6419,0x641A,0x641D, 0x641F,0x6422,0x6423,0x6424,0xFFFD,0x6425,0x6427,0x6428, 0x6429,0x642B,0x642E,0x642F,0x6430,0x6431,0x6432,0x6433, 0x6435,0x6436,0x6437,0x6438,0x6439,0x643B,0x643C,0x643E, 0x6440,0x6442,0x6443,0x6449,0x644B,0x644C,0x644D,0x644E, 0x644F,0x6450,0x6451,0x6453,0x6455,0x6456,0x6457,0x6459, 0x645A,0x645B,0x645C,0x645D,0x645F,0x6460,0x6461,0x6462, 0x6463,0x6464,0x6465,0x6466,0x6468,0x646A,0x646B,0x646C, 0x646E,0x646F,0x6470,0x6471,0x6472,0x6473,0x6474,0x6475, 0x6476,0x6477,0x647B,0x647C,0x647D,0x647E,0x647F,0x6480, 0x6481,0x6483,0x6486,0x6488,0x6489,0x648A,0x648B,0x648C, 0x648D,0x648E,0x648F,0x6490,0x6493,0x6494,0x6497,0x6498, 0x649A,0x649B,0x649C,0x649D,0x649F,0x64A0,0x64A1,0x64A2, 0x64A3,0x64A5,0x64A6,0x64A7,0x64A8,0x64AA,0x64AB,0x64AF, 0x64B1,0x64B2,0x64B3,0x64B4,0x64B6,0x64B9,0x64BB,0x64BD, 0x64BE,0x64BF,0x64C1,0x64C3,0x64C4,0x64C6,0x64C7,0x64C8, 0x64C9,0x64CA,0x64CB,0x64CC,0x64CF,0x64D1,0x64D3,0x64D4, 0x64D5,0x64D6,0x64D9,0x64DA,0x64DB,0x64DC,0x64DD,0x64DF, 0x64E0,0x64E1,0x64E3,0x64E5,0x64E7,0x64E8,0x64E9,0x64EA, 0x64EB,0x64EC,0x64ED,0x64EE,0x64EF,0x64F0,0x64F1,0x64F2, 0x64F3,0x64F4,0x64F5,0x64F6,0x64F7,0x64F8,0x64F9,0x64FA, 0x64FB,0x64FC,0x64FD,0x64FE,0x64FF,0x6501,0x6502,0x6503, 0x6504,0x6505,0x6506,0x6507,0x6508,0x650A,0x650B,0x650C, 0x650D,0x650E,0x650F,0x6510,0x6511,0x6513,0x6514,0x6515, 0x6516,0x6517,0x6519,0x651A,0x651B,0x651C,0x651D,0x651E, 0x651F,0x6520,0x6521,0xFFFD,0x6522,0x6523,0x6524,0x6526, 0x6527,0x6528,0x6529,0x652A,0x652C,0x652D,0x6530,0x6531, 0x6532,0x6533,0x6537,0x653A,0x653C,0x653D,0x6540,0x6541, 0x6542,0x6543,0x6544,0x6546,0x6547,0x654A,0x654B,0x654D, 0x654E,0x6550,0x6552,0x6553,0x6554,0x6557,0x6558,0x655A, 0x655C,0x655F,0x6560,0x6561,0x6564,0x6565,0x6567,0x6568, 0x6569,0x656A,0x656D,0x656E,0x656F,0x6571,0x6573,0x6575, 0x6576,0x6578,0x6579,0x657A,0x657B,0x657C,0x657D,0x657E, 0x657F,0x6580,0x6581,0x6582,0x6583,0x6584,0x6585,0x6586, 0x6588,0x6589,0x658A,0x658D,0x658E,0x658F,0x6592,0x6594, 0x6595,0x6596,0x6598,0x659A,0x659D,0x659E,0x65A0,0x65A2, 0x65A3,0x65A6,0x65A8,0x65AA,0x65AC,0x65AE,0x65B1,0x65B2, 0x65B3,0x65B4,0x65B5,0x65B6,0x65B7,0x65B8,0x65BA,0x65BB, 0x65BE,0x65BF,0x65C0,0x65C2,0x65C7,0x65C8,0x65C9,0x65CA, 0x65CD,0x65D0,0x65D1,0x65D3,0x65D4,0x65D5,0x65D8,0x65D9, 0x65DA,0x65DB,0x65DC,0x65DD,0x65DE,0x65DF,0x65E1,0x65E3, 0x65E4,0x65EA,0x65EB,0x65F2,0x65F3,0x65F4,0x65F5,0x65F8, 0x65F9,0x65FB,0x65FC,0x65FD,0x65FE,0x65FF,0x6601,0x6604, 0x6605,0x6607,0x6608,0x6609,0x660B,0x660D,0x6610,0x6611, 0x6612,0x6616,0x6617,0x6618,0x661A,0x661B,0x661C,0x661E, 0x6621,0x6622,0x6623,0x6624,0x6626,0x6629,0x662A,0x662B, 0x662C,0x662E,0x6630,0x6632,0x6633,0x6637,0x6638,0x6639, 0x663A,0x663B,0x663D,0x663F,0x6640,0x6642,0x6644,0x6645, 0x6646,0x6647,0x6648,0x6649,0x664A,0x664D,0x664E,0x6650, 0x6651,0x6658,0xFFFD,0x6659,0x665B,0x665C,0x665D,0x665E, 0x6660,0x6662,0x6663,0x6665,0x6667,0x6669,0x666A,0x666B, 0x666C,0x666D,0x6671,0x6672,0x6673,0x6675,0x6678,0x6679, 0x667B,0x667C,0x667D,0x667F,0x6680,0x6681,0x6683,0x6685, 0x6686,0x6688,0x6689,0x668A,0x668B,0x668D,0x668E,0x668F, 0x6690,0x6692,0x6693,0x6694,0x6695,0x6698,0x6699,0x669A, 0x669B,0x669C,0x669E,0x669F,0x66A0,0x66A1,0x66A2,0x66A3, 0x66A4,0x66A5,0x66A6,0x66A9,0x66AA,0x66AB,0x66AC,0x66AD, 0x66AF,0x66B0,0x66B1,0x66B2,0x66B3,0x66B5,0x66B6,0x66B7, 0x66B8,0x66BA,0x66BB,0x66BC,0x66BD,0x66BF,0x66C0,0x66C1, 0x66C2,0x66C3,0x66C4,0x66C5,0x66C6,0x66C7,0x66C8,0x66C9, 0x66CA,0x66CB,0x66CC,0x66CD,0x66CE,0x66CF,0x66D0,0x66D1, 0x66D2,0x66D3,0x66D4,0x66D5,0x66D6,0x66D7,0x66D8,0x66DA, 0x66DE,0x66DF,0x66E0,0x66E1,0x66E2,0x66E3,0x66E4,0x66E5, 0x66E7,0x66E8,0x66EA,0x66EB,0x66EC,0x66ED,0x66EE,0x66EF, 0x66F1,0x66F5,0x66F6,0x66F8,0x66FA,0x66FB,0x66FD,0x6701, 0x6702,0x6703,0x6704,0x6705,0x6706,0x6707,0x670C,0x670E, 0x670F,0x6711,0x6712,0x6713,0x6716,0x6718,0x6719,0x671A, 0x671C,0x671E,0x6720,0x6721,0x6722,0x6723,0x6724,0x6725, 0x6727,0x6729,0x672E,0x6730,0x6732,0x6733,0x6736,0x6737, 0x6738,0x6739,0x673B,0x673C,0x673E,0x673F,0x6741,0x6744, 0x6745,0x6747,0x674A,0x674B,0x674D,0x6752,0x6754,0x6755, 0x6757,0x6758,0x6759,0x675A,0x675B,0x675D,0x6762,0x6763, 0x6764,0x6766,0x6767,0x676B,0x676C,0x676E,0x6771,0x6774, 0x6776,0xFFFD,0x6778,0x6779,0x677A,0x677B,0x677D,0x6780, 0x6782,0x6783,0x6785,0x6786,0x6788,0x678A,0x678C,0x678D, 0x678E,0x678F,0x6791,0x6792,0x6793,0x6794,0x6796,0x6799, 0x679B,0x679F,0x67A0,0x67A1,0x67A4,0x67A6,0x67A9,0x67AC, 0x67AE,0x67B1,0x67B2,0x67B4,0x67B9,0x67BA,0x67BB,0x67BC, 0x67BD,0x67BE,0x67BF,0x67C0,0x67C2,0x67C5,0x67C6,0x67C7, 0x67C8,0x67C9,0x67CA,0x67CB,0x67CC,0x67CD,0x67CE,0x67D5, 0x67D6,0x67D7,0x67DB,0x67DF,0x67E1,0x67E3,0x67E4,0x67E6, 0x67E7,0x67E8,0x67EA,0x67EB,0x67ED,0x67EE,0x67F2,0x67F5, 0x67F6,0x67F7,0x67F8,0x67F9,0x67FA,0x67FB,0x67FC,0x67FE, 0x6801,0x6802,0x6803,0x6804,0x6806,0x680D,0x6810,0x6812, 0x6814,0x6815,0x6818,0x6819,0x681A,0x681B,0x681C,0x681E, 0x681F,0x6820,0x6822,0x6823,0x6824,0x6825,0x6826,0x6827, 0x6828,0x682B,0x682C,0x682D,0x682E,0x682F,0x6830,0x6831, 0x6834,0x6835,0x6836,0x683A,0x683B,0x683F,0x6847,0x684B, 0x684D,0x684F,0x6852,0x6856,0x6857,0x6858,0x6859,0x685A, 0x685B,0x685C,0x685D,0x685E,0x685F,0x686A,0x686C,0x686D, 0x686E,0x686F,0x6870,0x6871,0x6872,0x6873,0x6875,0x6878, 0x6879,0x687A,0x687B,0x687C,0x687D,0x687E,0x687F,0x6880, 0x6882,0x6884,0x6887,0x6888,0x6889,0x688A,0x688B,0x688C, 0x688D,0x688E,0x6890,0x6891,0x6892,0x6894,0x6895,0x6896, 0x6898,0x6899,0x689A,0x689B,0x689C,0x689D,0x689E,0x689F, 0x68A0,0x68A1,0x68A3,0x68A4,0x68A5,0x68A9,0x68AA,0x68AB, 0x68AC,0x68AE,0x68B1,0x68B2,0x68B4,0x68B6,0x68B7,0x68B8, 0xFFFD,0x68B9,0x68BA,0x68BB,0x68BC,0x68BD,0x68BE,0x68BF, 0x68C1,0x68C3,0x68C4,0x68C5,0x68C6,0x68C7,0x68C8,0x68CA, 0x68CC,0x68CE,0x68CF,0x68D0,0x68D1,0x68D3,0x68D4,0x68D6, 0x68D7,0x68D9,0x68DB,0x68DC,0x68DD,0x68DE,0x68DF,0x68E1, 0x68E2,0x68E4,0x68E5,0x68E6,0x68E7,0x68E8,0x68E9,0x68EA, 0x68EB,0x68EC,0x68ED,0x68EF,0x68F2,0x68F3,0x68F4,0x68F6, 0x68F7,0x68F8,0x68FB,0x68FD,0x68FE,0x68FF,0x6900,0x6902, 0x6903,0x6904,0x6906,0x6907,0x6908,0x6909,0x690A,0x690C, 0x690F,0x6911,0x6913,0x6914,0x6915,0x6916,0x6917,0x6918, 0x6919,0x691A,0x691B,0x691C,0x691D,0x691E,0x6921,0x6922, 0x6923,0x6925,0x6926,0x6927,0x6928,0x6929,0x692A,0x692B, 0x692C,0x692E,0x692F,0x6931,0x6932,0x6933,0x6935,0x6936, 0x6937,0x6938,0x693A,0x693B,0x693C,0x693E,0x6940,0x6941, 0x6943,0x6944,0x6945,0x6946,0x6947,0x6948,0x6949,0x694A, 0x694B,0x694C,0x694D,0x694E,0x694F,0x6950,0x6951,0x6952, 0x6953,0x6955,0x6956,0x6958,0x6959,0x695B,0x695C,0x695F, 0x6961,0x6962,0x6964,0x6965,0x6967,0x6968,0x6969,0x696A, 0x696C,0x696D,0x696F,0x6970,0x6972,0x6973,0x6974,0x6975, 0x6976,0x697A,0x697B,0x697D,0x697E,0x697F,0x6981,0x6983, 0x6985,0x698A,0x698B,0x698C,0x698E,0x698F,0x6990,0x6991, 0x6992,0x6993,0x6996,0x6997,0x6999,0x699A,0x699D,0x699E, 0x699F,0x69A0,0x69A1,0x69A2,0x69A3,0x69A4,0x69A5,0x69A6, 0x69A9,0x69AA,0x69AC,0x69AE,0x69AF,0x69B0,0x69B2,0x69B3, 0x69B5,0x69B6,0x69B8,0x69B9,0x69BA,0x69BC,0x69BD,0xFFFD, 0x69BE,0x69BF,0x69C0,0x69C2,0x69C3,0x69C4,0x69C5,0x69C6, 0x69C7,0x69C8,0x69C9,0x69CB,0x69CD,0x69CF,0x69D1,0x69D2, 0x69D3,0x69D5,0x69D6,0x69D7,0x69D8,0x69D9,0x69DA,0x69DC, 0x69DD,0x69DE,0x69E1,0x69E2,0x69E3,0x69E4,0x69E5,0x69E6, 0x69E7,0x69E8,0x69E9,0x69EA,0x69EB,0x69EC,0x69EE,0x69EF, 0x69F0,0x69F1,0x69F3,0x69F4,0x69F5,0x69F6,0x69F7,0x69F8, 0x69F9,0x69FA,0x69FB,0x69FC,0x69FE,0x6A00,0x6A01,0x6A02, 0x6A03,0x6A04,0x6A05,0x6A06,0x6A07,0x6A08,0x6A09,0x6A0B, 0x6A0C,0x6A0D,0x6A0E,0x6A0F,0x6A10,0x6A11,0x6A12,0x6A13, 0x6A14,0x6A15,0x6A16,0x6A19,0x6A1A,0x6A1B,0x6A1C,0x6A1D, 0x6A1E,0x6A20,0x6A22,0x6A23,0x6A24,0x6A25,0x6A26,0x6A27, 0x6A29,0x6A2B,0x6A2C,0x6A2D,0x6A2E,0x6A30,0x6A32,0x6A33, 0x6A34,0x6A36,0x6A37,0x6A38,0x6A39,0x6A3A,0x6A3B,0x6A3C, 0x6A3F,0x6A40,0x6A41,0x6A42,0x6A43,0x6A45,0x6A46,0x6A48, 0x6A49,0x6A4A,0x6A4B,0x6A4C,0x6A4D,0x6A4E,0x6A4F,0x6A51, 0x6A52,0x6A53,0x6A54,0x6A55,0x6A56,0x6A57,0x6A5A,0x6A5C, 0x6A5D,0x6A5E,0x6A5F,0x6A60,0x6A62,0x6A63,0x6A64,0x6A66, 0x6A67,0x6A68,0x6A69,0x6A6A,0x6A6B,0x6A6C,0x6A6D,0x6A6E, 0x6A6F,0x6A70,0x6A72,0x6A73,0x6A74,0x6A75,0x6A76,0x6A77, 0x6A78,0x6A7A,0x6A7B,0x6A7D,0x6A7E,0x6A7F,0x6A81,0x6A82, 0x6A83,0x6A85,0x6A86,0x6A87,0x6A88,0x6A89,0x6A8A,0x6A8B, 0x6A8C,0x6A8D,0x6A8F,0x6A92,0x6A93,0x6A94,0x6A95,0x6A96, 0x6A98,0x6A99,0x6A9A,0x6A9B,0x6A9C,0x6A9D,0x6A9E,0x6A9F, 0x6AA1,0x6AA2,0x6AA3,0x6AA4,0x6AA5,0x6AA6,0xFFFD,0x6AA7, 0x6AA8,0x6AAA,0x6AAD,0x6AAE,0x6AAF,0x6AB0,0x6AB1,0x6AB2, 0x6AB3,0x6AB4,0x6AB5,0x6AB6,0x6AB7,0x6AB8,0x6AB9,0x6ABA, 0x6ABB,0x6ABC,0x6ABD,0x6ABE,0x6ABF,0x6AC0,0x6AC1,0x6AC2, 0x6AC3,0x6AC4,0x6AC5,0x6AC6,0x6AC7,0x6AC8,0x6AC9,0x6ACA, 0x6ACB,0x6ACC,0x6ACD,0x6ACE,0x6ACF,0x6AD0,0x6AD1,0x6AD2, 0x6AD3,0x6AD4,0x6AD5,0x6AD6,0x6AD7,0x6AD8,0x6AD9,0x6ADA, 0x6ADB,0x6ADC,0x6ADD,0x6ADE,0x6ADF,0x6AE0,0x6AE1,0x6AE2, 0x6AE3,0x6AE4,0x6AE5,0x6AE6,0x6AE7,0x6AE8,0x6AE9,0x6AEA, 0x6AEB,0x6AEC,0x6AED,0x6AEE,0x6AEF,0x6AF0,0x6AF1,0x6AF2, 0x6AF3,0x6AF4,0x6AF5,0x6AF6,0x6AF7,0x6AF8,0x6AF9,0x6AFA, 0x6AFB,0x6AFC,0x6AFD,0x6AFE,0x6AFF,0x6B00,0x6B01,0x6B02, 0x6B03,0x6B04,0x6B05,0x6B06,0x6B07,0x6B08,0x6B09,0x6B0A, 0x6B0B,0x6B0C,0x6B0D,0x6B0E,0x6B0F,0x6B10,0x6B11,0x6B12, 0x6B13,0x6B14,0x6B15,0x6B16,0x6B17,0x6B18,0x6B19,0x6B1A, 0x6B1B,0x6B1C,0x6B1D,0x6B1E,0x6B1F,0x6B25,0x6B26,0x6B28, 0x6B29,0x6B2A,0x6B2B,0x6B2C,0x6B2D,0x6B2E,0x6B2F,0x6B30, 0x6B31,0x6B33,0x6B34,0x6B35,0x6B36,0x6B38,0x6B3B,0x6B3C, 0x6B3D,0x6B3F,0x6B40,0x6B41,0x6B42,0x6B44,0x6B45,0x6B48, 0x6B4A,0x6B4B,0x6B4D,0x6B4E,0x6B4F,0x6B50,0x6B51,0x6B52, 0x6B53,0x6B54,0x6B55,0x6B56,0x6B57,0x6B58,0x6B5A,0x6B5B, 0x6B5C,0x6B5D,0x6B5E,0x6B5F,0x6B60,0x6B61,0x6B68,0x6B69, 0x6B6B,0x6B6C,0x6B6D,0x6B6E,0x6B6F,0x6B70,0x6B71,0x6B72, 0x6B73,0x6B74,0x6B75,0x6B76,0x6B77,0x6B78,0x6B7A,0x6B7D, 0x6B7E,0x6B7F,0x6B80,0x6B85,0x6B88,0xFFFD,0x6B8C,0x6B8E, 0x6B8F,0x6B90,0x6B91,0x6B94,0x6B95,0x6B97,0x6B98,0x6B99, 0x6B9C,0x6B9D,0x6B9E,0x6B9F,0x6BA0,0x6BA2,0x6BA3,0x6BA4, 0x6BA5,0x6BA6,0x6BA7,0x6BA8,0x6BA9,0x6BAB,0x6BAC,0x6BAD, 0x6BAE,0x6BAF,0x6BB0,0x6BB1,0x6BB2,0x6BB6,0x6BB8,0x6BB9, 0x6BBA,0x6BBB,0x6BBC,0x6BBD,0x6BBE,0x6BC0,0x6BC3,0x6BC4, 0x6BC6,0x6BC7,0x6BC8,0x6BC9,0x6BCA,0x6BCC,0x6BCE,0x6BD0, 0x6BD1,0x6BD8,0x6BDA,0x6BDC,0x6BDD,0x6BDE,0x6BDF,0x6BE0, 0x6BE2,0x6BE3,0x6BE4,0x6BE5,0x6BE6,0x6BE7,0x6BE8,0x6BE9, 0x6BEC,0x6BED,0x6BEE,0x6BF0,0x6BF1,0x6BF2,0x6BF4,0x6BF6, 0x6BF7,0x6BF8,0x6BFA,0x6BFB,0x6BFC,0x6BFE,0x6BFF,0x6C00, 0x6C01,0x6C02,0x6C03,0x6C04,0x6C08,0x6C09,0x6C0A,0x6C0B, 0x6C0C,0x6C0E,0x6C12,0x6C17,0x6C1C,0x6C1D,0x6C1E,0x6C20, 0x6C23,0x6C25,0x6C2B,0x6C2C,0x6C2D,0x6C31,0x6C33,0x6C36, 0x6C37,0x6C39,0x6C3A,0x6C3B,0x6C3C,0x6C3E,0x6C3F,0x6C43, 0x6C44,0x6C45,0x6C48,0x6C4B,0x6C4C,0x6C4D,0x6C4E,0x6C4F, 0x6C51,0x6C52,0x6C53,0x6C56,0x6C58,0x6C59,0x6C5A,0x6C62, 0x6C63,0x6C65,0x6C66,0x6C67,0x6C6B,0x6C6C,0x6C6D,0x6C6E, 0x6C6F,0x6C71,0x6C73,0x6C75,0x6C77,0x6C78,0x6C7A,0x6C7B, 0x6C7C,0x6C7F,0x6C80,0x6C84,0x6C87,0x6C8A,0x6C8B,0x6C8D, 0x6C8E,0x6C91,0x6C92,0x6C95,0x6C96,0x6C97,0x6C98,0x6C9A, 0x6C9C,0x6C9D,0x6C9E,0x6CA0,0x6CA2,0x6CA8,0x6CAC,0x6CAF, 0x6CB0,0x6CB4,0x6CB5,0x6CB6,0x6CB7,0x6CBA,0x6CC0,0x6CC1, 0x6CC2,0x6CC3,0x6CC6,0x6CC7,0x6CC8,0x6CCB,0x6CCD,0x6CCE, 0x6CCF,0x6CD1,0x6CD2,0x6CD8,0xFFFD,0x6CD9,0x6CDA,0x6CDC, 0x6CDD,0x6CDF,0x6CE4,0x6CE6,0x6CE7,0x6CE9,0x6CEC,0x6CED, 0x6CF2,0x6CF4,0x6CF9,0x6CFF,0x6D00,0x6D02,0x6D03,0x6D05, 0x6D06,0x6D08,0x6D09,0x6D0A,0x6D0D,0x6D0F,0x6D10,0x6D11, 0x6D13,0x6D14,0x6D15,0x6D16,0x6D18,0x6D1C,0x6D1D,0x6D1F, 0x6D20,0x6D21,0x6D22,0x6D23,0x6D24,0x6D26,0x6D28,0x6D29, 0x6D2C,0x6D2D,0x6D2F,0x6D30,0x6D34,0x6D36,0x6D37,0x6D38, 0x6D3A,0x6D3F,0x6D40,0x6D42,0x6D44,0x6D49,0x6D4C,0x6D50, 0x6D55,0x6D56,0x6D57,0x6D58,0x6D5B,0x6D5D,0x6D5F,0x6D61, 0x6D62,0x6D64,0x6D65,0x6D67,0x6D68,0x6D6B,0x6D6C,0x6D6D, 0x6D70,0x6D71,0x6D72,0x6D73,0x6D75,0x6D76,0x6D79,0x6D7A, 0x6D7B,0x6D7D,0x6D7E,0x6D7F,0x6D80,0x6D81,0x6D83,0x6D84, 0x6D86,0x6D87,0x6D8A,0x6D8B,0x6D8D,0x6D8F,0x6D90,0x6D92, 0x6D96,0x6D97,0x6D98,0x6D99,0x6D9A,0x6D9C,0x6DA2,0x6DA5, 0x6DAC,0x6DAD,0x6DB0,0x6DB1,0x6DB3,0x6DB4,0x6DB6,0x6DB7, 0x6DB9,0x6DBA,0x6DBB,0x6DBC,0x6DBD,0x6DBE,0x6DC1,0x6DC2, 0x6DC3,0x6DC8,0x6DC9,0x6DCA,0x6DCD,0x6DCE,0x6DCF,0x6DD0, 0x6DD2,0x6DD3,0x6DD4,0x6DD5,0x6DD7,0x6DDA,0x6DDB,0x6DDC, 0x6DDF,0x6DE2,0x6DE3,0x6DE5,0x6DE7,0x6DE8,0x6DE9,0x6DEA, 0x6DED,0x6DEF,0x6DF0,0x6DF2,0x6DF4,0x6DF5,0x6DF6,0x6DF8, 0x6DFA,0x6DFD,0x6DFE,0x6DFF,0x6E00,0x6E01,0x6E02,0x6E03, 0x6E04,0x6E06,0x6E07,0x6E08,0x6E09,0x6E0B,0x6E0F,0x6E12, 0x6E13,0x6E15,0x6E18,0x6E19,0x6E1B,0x6E1C,0x6E1E,0x6E1F, 0x6E22,0x6E26,0x6E27,0x6E28,0x6E2A,0x6E2C,0x6E2E,0x6E30, 0x6E31,0x6E33,0x6E35,0xFFFD,0x6E36,0x6E37,0x6E39,0x6E3B, 0x6E3C,0x6E3D,0x6E3E,0x6E3F,0x6E40,0x6E41,0x6E42,0x6E45, 0x6E46,0x6E47,0x6E48,0x6E49,0x6E4A,0x6E4B,0x6E4C,0x6E4F, 0x6E50,0x6E51,0x6E52,0x6E55,0x6E57,0x6E59,0x6E5A,0x6E5C, 0x6E5D,0x6E5E,0x6E60,0x6E61,0x6E62,0x6E63,0x6E64,0x6E65, 0x6E66,0x6E67,0x6E68,0x6E69,0x6E6A,0x6E6C,0x6E6D,0x6E6F, 0x6E70,0x6E71,0x6E72,0x6E73,0x6E74,0x6E75,0x6E76,0x6E77, 0x6E78,0x6E79,0x6E7A,0x6E7B,0x6E7C,0x6E7D,0x6E80,0x6E81, 0x6E82,0x6E84,0x6E87,0x6E88,0x6E8A,0x6E8B,0x6E8C,0x6E8D, 0x6E8E,0x6E91,0x6E92,0x6E93,0x6E94,0x6E95,0x6E96,0x6E97, 0x6E99,0x6E9A,0x6E9B,0x6E9D,0x6E9E,0x6EA0,0x6EA1,0x6EA3, 0x6EA4,0x6EA6,0x6EA8,0x6EA9,0x6EAB,0x6EAC,0x6EAD,0x6EAE, 0x6EB0,0x6EB3,0x6EB5,0x6EB8,0x6EB9,0x6EBC,0x6EBE,0x6EBF, 0x6EC0,0x6EC3,0x6EC4,0x6EC5,0x6EC6,0x6EC8,0x6EC9,0x6ECA, 0x6ECC,0x6ECD,0x6ECE,0x6ED0,0x6ED2,0x6ED6,0x6ED8,0x6ED9, 0x6EDB,0x6EDC,0x6EDD,0x6EE3,0x6EE7,0x6EEA,0x6EEB,0x6EEC, 0x6EED,0x6EEE,0x6EEF,0x6EF0,0x6EF1,0x6EF2,0x6EF3,0x6EF5, 0x6EF6,0x6EF7,0x6EF8,0x6EFA,0x6EFB,0x6EFC,0x6EFD,0x6EFE, 0x6EFF,0x6F00,0x6F01,0x6F03,0x6F04,0x6F05,0x6F07,0x6F08, 0x6F0A,0x6F0B,0x6F0C,0x6F0D,0x6F0E,0x6F10,0x6F11,0x6F12, 0x6F16,0x6F17,0x6F18,0x6F19,0x6F1A,0x6F1B,0x6F1C,0x6F1D, 0x6F1E,0x6F1F,0x6F21,0x6F22,0x6F23,0x6F25,0x6F26,0x6F27, 0x6F28,0x6F2C,0x6F2E,0x6F30,0x6F32,0x6F34,0x6F35,0x6F37, 0x6F38,0x6F39,0x6F3A,0x6F3B,0x6F3C,0x6F3D,0x6F3F,0x6F40, 0x6F41,0x6F42,0xFFFD,0x6F43,0x6F44,0x6F45,0x6F48,0x6F49, 0x6F4A,0x6F4C,0x6F4E,0x6F4F,0x6F50,0x6F51,0x6F52,0x6F53, 0x6F54,0x6F55,0x6F56,0x6F57,0x6F59,0x6F5A,0x6F5B,0x6F5D, 0x6F5F,0x6F60,0x6F61,0x6F63,0x6F64,0x6F65,0x6F67,0x6F68, 0x6F69,0x6F6A,0x6F6B,0x6F6C,0x6F6F,0x6F70,0x6F71,0x6F73, 0x6F75,0x6F76,0x6F77,0x6F79,0x6F7B,0x6F7D,0x6F7E,0x6F7F, 0x6F80,0x6F81,0x6F82,0x6F83,0x6F85,0x6F86,0x6F87,0x6F8A, 0x6F8B,0x6F8F,0x6F90,0x6F91,0x6F92,0x6F93,0x6F94,0x6F95, 0x6F96,0x6F97,0x6F98,0x6F99,0x6F9A,0x6F9B,0x6F9D,0x6F9E, 0x6F9F,0x6FA0,0x6FA2,0x6FA3,0x6FA4,0x6FA5,0x6FA6,0x6FA8, 0x6FA9,0x6FAA,0x6FAB,0x6FAC,0x6FAD,0x6FAE,0x6FAF,0x6FB0, 0x6FB1,0x6FB2,0x6FB4,0x6FB5,0x6FB7,0x6FB8,0x6FBA,0x6FBB, 0x6FBC,0x6FBD,0x6FBE,0x6FBF,0x6FC1,0x6FC3,0x6FC4,0x6FC5, 0x6FC6,0x6FC7,0x6FC8,0x6FCA,0x6FCB,0x6FCC,0x6FCD,0x6FCE, 0x6FCF,0x6FD0,0x6FD3,0x6FD4,0x6FD5,0x6FD6,0x6FD7,0x6FD8, 0x6FD9,0x6FDA,0x6FDB,0x6FDC,0x6FDD,0x6FDF,0x6FE2,0x6FE3, 0x6FE4,0x6FE5,0x6FE6,0x6FE7,0x6FE8,0x6FE9,0x6FEA,0x6FEB, 0x6FEC,0x6FED,0x6FF0,0x6FF1,0x6FF2,0x6FF3,0x6FF4,0x6FF5, 0x6FF6,0x6FF7,0x6FF8,0x6FF9,0x6FFA,0x6FFB,0x6FFC,0x6FFD, 0x6FFE,0x6FFF,0x7000,0x7001,0x7002,0x7003,0x7004,0x7005, 0x7006,0x7007,0x7008,0x7009,0x700A,0x700B,0x700C,0x700D, 0x700E,0x700F,0x7010,0x7012,0x7013,0x7014,0x7015,0x7016, 0x7017,0x7018,0x7019,0x701C,0x701D,0x701E,0x701F,0x7020, 0x7021,0x7022,0x7024,0x7025,0x7026,0x7027,0x7028,0x7029, 0x702A,0xFFFD,0x702B,0x702C,0x702D,0x702E,0x702F,0x7030, 0x7031,0x7032,0x7033,0x7034,0x7036,0x7037,0x7038,0x703A, 0x703B,0x703C,0x703D,0x703E,0x703F,0x7040,0x7041,0x7042, 0x7043,0x7044,0x7045,0x7046,0x7047,0x7048,0x7049,0x704A, 0x704B,0x704D,0x704E,0x7050,0x7051,0x7052,0x7053,0x7054, 0x7055,0x7056,0x7057,0x7058,0x7059,0x705A,0x705B,0x705C, 0x705D,0x705F,0x7060,0x7061,0x7062,0x7063,0x7064,0x7065, 0x7066,0x7067,0x7068,0x7069,0x706A,0x706E,0x7071,0x7072, 0x7073,0x7074,0x7077,0x7079,0x707A,0x707B,0x707D,0x7081, 0x7082,0x7083,0x7084,0x7086,0x7087,0x7088,0x708B,0x708C, 0x708D,0x708F,0x7090,0x7091,0x7093,0x7097,0x7098,0x709A, 0x709B,0x709E,0x709F,0x70A0,0x70A1,0x70A2,0x70A3,0x70A4, 0x70A5,0x70A6,0x70A7,0x70A8,0x70A9,0x70AA,0x70B0,0x70B2, 0x70B4,0x70B5,0x70B6,0x70BA,0x70BE,0x70BF,0x70C4,0x70C5, 0x70C6,0x70C7,0x70C9,0x70CB,0x70CC,0x70CD,0x70CE,0x70CF, 0x70D0,0x70D1,0x70D2,0x70D3,0x70D4,0x70D5,0x70D6,0x70D7, 0x70DA,0x70DC,0x70DD,0x70DE,0x70E0,0x70E1,0x70E2,0x70E3, 0x70E5,0x70EA,0x70EE,0x70F0,0x70F1,0x70F2,0x70F3,0x70F4, 0x70F5,0x70F6,0x70F8,0x70FA,0x70FB,0x70FC,0x70FE,0x70FF, 0x7100,0x7101,0x7102,0x7103,0x7104,0x7105,0x7106,0x7107, 0x7108,0x710B,0x710C,0x710D,0x710E,0x710F,0x7111,0x7112, 0x7114,0x7117,0x711B,0x711C,0x711D,0x711E,0x711F,0x7120, 0x7121,0x7122,0x7123,0x7124,0x7125,0x7127,0x7128,0x7129, 0x712A,0x712B,0x712C,0x712D,0x712E,0x7132,0x7133,0x7134, 0xFFFD,0x7135,0x7137,0x7138,0x7139,0x713A,0x713B,0x713C, 0x713D,0x713E,0x713F,0x7140,0x7141,0x7142,0x7143,0x7144, 0x7146,0x7147,0x7148,0x7149,0x714B,0x714D,0x714F,0x7150, 0x7151,0x7152,0x7153,0x7154,0x7155,0x7156,0x7157,0x7158, 0x7159,0x715A,0x715B,0x715D,0x715F,0x7160,0x7161,0x7162, 0x7163,0x7165,0x7169,0x716A,0x716B,0x716C,0x716D,0x716F, 0x7170,0x7171,0x7174,0x7175,0x7176,0x7177,0x7179,0x717B, 0x717C,0x717E,0x717F,0x7180,0x7181,0x7182,0x7183,0x7185, 0x7186,0x7187,0x7188,0x7189,0x718B,0x718C,0x718D,0x718E, 0x7190,0x7191,0x7192,0x7193,0x7195,0x7196,0x7197,0x719A, 0x719B,0x719C,0x719D,0x719E,0x71A1,0x71A2,0x71A3,0x71A4, 0x71A5,0x71A6,0x71A7,0x71A9,0x71AA,0x71AB,0x71AD,0x71AE, 0x71AF,0x71B0,0x71B1,0x71B2,0x71B4,0x71B6,0x71B7,0x71B8, 0x71BA,0x71BB,0x71BC,0x71BD,0x71BE,0x71BF,0x71C0,0x71C1, 0x71C2,0x71C4,0x71C5,0x71C6,0x71C7,0x71C8,0x71C9,0x71CA, 0x71CB,0x71CC,0x71CD,0x71CF,0x71D0,0x71D1,0x71D2,0x71D3 }; const static uint16 gbkDecoderInnerIndex2[]= { 0x71D6,0x71D7,0x71D8,0x71D9,0x71DA,0x71DB,0x71DC,0x71DD, 0x71DE,0x71DF,0x71E1,0x71E2,0x71E3,0x71E4,0x71E6,0x71E8, 0x71E9,0x71EA,0x71EB,0x71EC,0x71ED,0x71EF,0x71F0,0x71F1, 0x71F2,0x71F3,0x71F4,0x71F5,0x71F6,0x71F7,0x71F8,0x71FA, 0x71FB,0x71FC,0x71FD,0x71FE,0x71FF,0x7200,0x7201,0x7202, 0x7203,0x7204,0x7205,0x7207,0x7208,0x7209,0x720A,0x720B, 0x720C,0x720D,0x720E,0x720F,0x7210,0x7211,0x7212,0x7213, 0x7214,0x7215,0x7216,0x7217,0x7218,0x7219,0x721A,0xFFFD, 0x721B,0x721C,0x721E,0x721F,0x7220,0x7221,0x7222,0x7223, 0x7224,0x7225,0x7226,0x7227,0x7229,0x722B,0x722D,0x722E, 0x722F,0x7232,0x7233,0x7234,0x723A,0x723C,0x723E,0x7240, 0x7241,0x7242,0x7243,0x7244,0x7245,0x7246,0x7249,0x724A, 0x724B,0x724E,0x724F,0x7250,0x7251,0x7253,0x7254,0x7255, 0x7257,0x7258,0x725A,0x725C,0x725E,0x7260,0x7263,0x7264, 0x7265,0x7268,0x726A,0x726B,0x726C,0x726D,0x7270,0x7271, 0x7273,0x7274,0x7276,0x7277,0x7278,0x727B,0x727C,0x727D, 0x7282,0x7283,0x7285,0x7286,0x7287,0x7288,0x7289,0x728C, 0x728E,0x7290,0x7291,0x7293,0x7294,0x7295,0x7296,0x7297, 0x7298,0x7299,0x729A,0x729B,0x729C,0x729D,0x729E,0x72A0, 0x72A1,0x72A2,0x72A3,0x72A4,0x72A5,0x72A6,0x72A7,0x72A8, 0x72A9,0x72AA,0x72AB,0x72AE,0x72B1,0x72B2,0x72B3,0x72B5, 0x72BA,0x72BB,0x72BC,0x72BD,0x72BE,0x72BF,0x72C0,0x72C5, 0x72C6,0x72C7,0x72C9,0x72CA,0x72CB,0x72CC,0x72CF,0x72D1, 0x72D3,0x72D4,0x72D5,0x72D6,0x72D8,0x72DA,0x72DB,0xE4C6, 0xE4C7,0xE4C8,0xE4C9,0xE4CA,0xE4CB,0xE4CC,0xE4CD,0xE4CE, 0xE4CF,0xE4D0,0xE4D1,0xE4D2,0xE4D3,0xE4D4,0xE4D5,0xE4D6, 0xE4D7,0xE4D8,0xE4D9,0xE4DA,0xE4DB,0xE4DC,0xE4DD,0xE4DE, 0xE4DF,0xE4E0,0xE4E1,0xE4E2,0xE4E3,0xE4E4,0xE4E5,0xE4E6, 0xE4E7,0xE4E8,0xE4E9,0xE4EA,0xE4EB,0xE4EC,0xE4ED,0xE4EE, 0xE4EF,0xE4F0,0xE4F1,0xE4F2,0xE4F3,0xE4F4,0xE4F5,0xE4F6, 0xE4F7,0xE4F8,0xE4F9,0xE4FA,0xE4FB,0xE4FC,0xE4FD,0xE4FE, 0xE4FF,0xE500,0xE501,0xE502,0xE503,0xE504,0xFFFD,0xE505, 0xE506,0xE507,0xE508,0xE509,0xE50A,0xE50B,0xE50C,0xE50D, 0xE50E,0xE50F,0xE510,0xE511,0xE512,0xE513,0xE514,0xE515, 0xE516,0xE517,0xE518,0xE519,0xE51A,0xE51B,0xE51C,0xE51D, 0xE51E,0xE51F,0xE520,0xE521,0xE522,0xE523,0xE524,0xE525, 0x3000,0x3001,0x3002,0x00B7,0x02C9,0x02C7,0x00A8,0x3003, 0x3005,0x2014,0xFF5E,0x2016,0x2026,0x2018,0x2019,0x201C, 0x201D,0x3014,0x3015,0x3008,0x3009,0x300A,0x300B,0x300C, 0x300D,0x300E,0x300F,0x3016,0x3017,0x3010,0x3011,0x00B1, 0x00D7,0x00F7,0x2236,0x2227,0x2228,0x2211,0x220F,0x222A, 0x2229,0x2208,0x2237,0x221A,0x22A5,0x2225,0x2220,0x2312, 0x2299,0x222B,0x222E,0x2261,0x224C,0x2248,0x223D,0x221D, 0x2260,0x226E,0x226F,0x2264,0x2265,0x221E,0x2235,0x2234, 0x2642,0x2640,0x00B0,0x2032,0x2033,0x2103,0xFF04,0x00A4, 0xFFE0,0xFFE1,0x2030,0x00A7,0x2116,0x2606,0x2605,0x25CB, 0x25CF,0x25CE,0x25C7,0x25C6,0x25A1,0x25A0,0x25B3,0x25B2, 0x203B,0x2192,0x2190,0x2191,0x2193,0x3013,0xE526,0xE527, 0xE528,0xE529,0xE52A,0xE52B,0xE52C,0xE52D,0xE52E,0xE52F, 0xE530,0xE531,0xE532,0xE533,0xE534,0xE535,0xE536,0xE537, 0xE538,0xE539,0xE53A,0xE53B,0xE53C,0xE53D,0xE53E,0xE53F, 0xE540,0xE541,0xE542,0xE543,0xE544,0xE545,0xE546,0xE547, 0xE548,0xE549,0xE54A,0xE54B,0xE54C,0xE54D,0xE54E,0xE54F, 0xE550,0xE551,0xE552,0xE553,0xE554,0xE555,0xE556,0xE557, 0xE558,0xE559,0xE55A,0xE55B,0xE55C,0xE55D,0xE55E,0xE55F, 0xE560,0xE561,0xE562,0xE563,0xE564,0xFFFD,0xE565,0xE566, 0xE567,0xE568,0xE569,0xE56A,0xE56B,0xE56C,0xE56D,0xE56E, 0xE56F,0xE570,0xE571,0xE572,0xE573,0xE574,0xE575,0xE576, 0xE577,0xE578,0xE579,0xE57A,0xE57B,0xE57C,0xE57D,0xE57E, 0xE57F,0xE580,0xE581,0xE582,0xE583,0xE584,0xE585,0x2170, 0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,0x2177,0x2178, 0x2179,0xE586,0xE587,0xE588,0xE589,0xE58A,0xE58B,0x2488, 0x2489,0x248A,0x248B,0x248C,0x248D,0x248E,0x248F,0x2490, 0x2491,0x2492,0x2493,0x2494,0x2495,0x2496,0x2497,0x2498, 0x2499,0x249A,0x249B,0x2474,0x2475,0x2476,0x2477,0x2478, 0x2479,0x247A,0x247B,0x247C,0x247D,0x247E,0x247F,0x2480, 0x2481,0x2482,0x2483,0x2484,0x2485,0x2486,0x2487,0x2460, 0x2461,0x2462,0x2463,0x2464,0x2465,0x2466,0x2467,0x2468, 0x2469,0xE58C,0xE58D,0x3220,0x3221,0x3222,0x3223,0x3224, 0x3225,0x3226,0x3227,0x3228,0x3229,0xE58E,0xE58F,0x2160, 0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168, 0x2169,0x216A,0x216B,0xE590,0xE591,0xE592,0xE593,0xE594, 0xE595,0xE596,0xE597,0xE598,0xE599,0xE59A,0xE59B,0xE59C, 0xE59D,0xE59E,0xE59F,0xE5A0,0xE5A1,0xE5A2,0xE5A3,0xE5A4, 0xE5A5,0xE5A6,0xE5A7,0xE5A8,0xE5A9,0xE5AA,0xE5AB,0xE5AC, 0xE5AD,0xE5AE,0xE5AF,0xE5B0,0xE5B1,0xE5B2,0xE5B3,0xE5B4, 0xE5B5,0xE5B6,0xE5B7,0xE5B8,0xE5B9,0xE5BA,0xE5BB,0xE5BC, 0xE5BD,0xE5BE,0xE5BF,0xE5C0,0xE5C1,0xE5C2,0xE5C3,0xE5C4, 0xE5C5,0xE5C6,0xE5C7,0xE5C8,0xE5C9,0xE5CA,0xE5CB,0xE5CC, 0xE5CD,0xE5CE,0xE5CF,0xE5D0,0xFFFD,0xE5D1,0xE5D2,0xE5D3, 0xE5D4,0xE5D5,0xE5D6,0xE5D7,0xE5D8,0xE5D9,0xE5DA,0xE5DB, 0xE5DC,0xE5DD,0xE5DE,0xE5DF,0xE5E0,0xE5E1,0xE5E2,0xE5E3, 0xE5E4,0xE5E5,0xE5E6,0xE5E7,0xE5E8,0xE5E9,0xE5EA,0xE5EB, 0xE5EC,0xE5ED,0xE5EE,0xE5EF,0xE5F0,0xE5F1,0xFF01,0xFF02, 0xFF03,0xFFE5,0xFF05,0xFF06,0xFF07,0xFF08,0xFF09,0xFF0A, 0xFF0B,0xFF0C,0xFF0D,0xFF0E,0xFF0F,0xFF10,0xFF11,0xFF12, 0xFF13,0xFF14,0xFF15,0xFF16,0xFF17,0xFF18,0xFF19,0xFF1A, 0xFF1B,0xFF1C,0xFF1D,0xFF1E,0xFF1F,0xFF20,0xFF21,0xFF22, 0xFF23,0xFF24,0xFF25,0xFF26,0xFF27,0xFF28,0xFF29,0xFF2A, 0xFF2B,0xFF2C,0xFF2D,0xFF2E,0xFF2F,0xFF30,0xFF31,0xFF32, 0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,0xFF38,0xFF39,0xFF3A, 0xFF3B,0xFF3C,0xFF3D,0xFF3E,0xFF3F,0xFF40,0xFF41,0xFF42, 0xFF43,0xFF44,0xFF45,0xFF46,0xFF47,0xFF48,0xFF49,0xFF4A, 0xFF4B,0xFF4C,0xFF4D,0xFF4E,0xFF4F,0xFF50,0xFF51,0xFF52, 0xFF53,0xFF54,0xFF55,0xFF56,0xFF57,0xFF58,0xFF59,0xFF5A, 0xFF5B,0xFF5C,0xFF5D,0xFFE3,0xE5F2,0xE5F3,0xE5F4,0xE5F5, 0xE5F6,0xE5F7,0xE5F8,0xE5F9,0xE5FA,0xE5FB,0xE5FC,0xE5FD, 0xE5FE,0xE5FF,0xE600,0xE601,0xE602,0xE603,0xE604,0xE605, 0xE606,0xE607,0xE608,0xE609,0xE60A,0xE60B,0xE60C,0xE60D, 0xE60E,0xE60F,0xE610,0xE611,0xE612,0xE613,0xE614,0xE615, 0xE616,0xE617,0xE618,0xE619,0xE61A,0xE61B,0xE61C,0xE61D, 0xE61E,0xE61F,0xE620,0xE621,0xE622,0xE623,0xE624,0xE625, 0xE626,0xE627,0xE628,0xE629,0xE62A,0xE62B,0xE62C,0xE62D, 0xE62E,0xE62F,0xE630,0xFFFD,0xE631,0xE632,0xE633,0xE634, 0xE635,0xE636,0xE637,0xE638,0xE639,0xE63A,0xE63B,0xE63C, 0xE63D,0xE63E,0xE63F,0xE640,0xE641,0xE642,0xE643,0xE644, 0xE645,0xE646,0xE647,0xE648,0xE649,0xE64A,0xE64B,0xE64C, 0xE64D,0xE64E,0xE64F,0xE650,0xE651,0x3041,0x3042,0x3043, 0x3044,0x3045,0x3046,0x3047,0x3048,0x3049,0x304A,0x304B, 0x304C,0x304D,0x304E,0x304F,0x3050,0x3051,0x3052,0x3053, 0x3054,0x3055,0x3056,0x3057,0x3058,0x3059,0x305A,0x305B, 0x305C,0x305D,0x305E,0x305F,0x3060,0x3061,0x3062,0x3063, 0x3064,0x3065,0x3066,0x3067,0x3068,0x3069,0x306A,0x306B, 0x306C,0x306D,0x306E,0x306F,0x3070,0x3071,0x3072,0x3073, 0x3074,0x3075,0x3076,0x3077,0x3078,0x3079,0x307A,0x307B, 0x307C,0x307D,0x307E,0x307F,0x3080,0x3081,0x3082,0x3083, 0x3084,0x3085,0x3086,0x3087,0x3088,0x3089,0x308A,0x308B, 0x308C,0x308D,0x308E,0x308F,0x3090,0x3091,0x3092,0x3093, 0xE652,0xE653,0xE654,0xE655,0xE656,0xE657,0xE658,0xE659, 0xE65A,0xE65B,0xE65C,0xE65D,0xE65E,0xE65F,0xE660,0xE661, 0xE662,0xE663,0xE664,0xE665,0xE666,0xE667,0xE668,0xE669, 0xE66A,0xE66B,0xE66C,0xE66D,0xE66E,0xE66F,0xE670,0xE671, 0xE672,0xE673,0xE674,0xE675,0xE676,0xE677,0xE678,0xE679, 0xE67A,0xE67B,0xE67C,0xE67D,0xE67E,0xE67F,0xE680,0xE681, 0xE682,0xE683,0xE684,0xE685,0xE686,0xE687,0xE688,0xE689, 0xE68A,0xE68B,0xE68C,0xE68D,0xE68E,0xE68F,0xE690,0xE691, 0xE692,0xE693,0xE694,0xE695,0xE696,0xE697,0xE698,0xE699, 0xE69A,0xE69B,0xFFFD,0xE69C,0xE69D,0xE69E,0xE69F,0xE6A0, 0xE6A1,0xE6A2,0xE6A3,0xE6A4,0xE6A5,0xE6A6,0xE6A7,0xE6A8, 0xE6A9,0xE6AA,0xE6AB,0xE6AC,0xE6AD,0xE6AE,0xE6AF,0xE6B0, 0xE6B1,0xE6B2,0xE6B3,0xE6B4,0xE6B5,0xE6B6,0xE6B7,0xE6B8, 0xE6B9,0xE6BA,0xE6BB,0xE6BC,0x30A1,0x30A2,0x30A3,0x30A4, 0x30A5,0x30A6,0x30A7,0x30A8,0x30A9,0x30AA,0x30AB,0x30AC, 0x30AD,0x30AE,0x30AF,0x30B0,0x30B1,0x30B2,0x30B3,0x30B4, 0x30B5,0x30B6,0x30B7,0x30B8,0x30B9,0x30BA,0x30BB,0x30BC, 0x30BD,0x30BE,0x30BF,0x30C0,0x30C1,0x30C2,0x30C3,0x30C4, 0x30C5,0x30C6,0x30C7,0x30C8,0x30C9,0x30CA,0x30CB,0x30CC, 0x30CD,0x30CE,0x30CF,0x30D0,0x30D1,0x30D2,0x30D3,0x30D4, 0x30D5,0x30D6,0x30D7,0x30D8,0x30D9,0x30DA,0x30DB,0x30DC, 0x30DD,0x30DE,0x30DF,0x30E0,0x30E1,0x30E2,0x30E3,0x30E4, 0x30E5,0x30E6,0x30E7,0x30E8,0x30E9,0x30EA,0x30EB,0x30EC, 0x30ED,0x30EE,0x30EF,0x30F0,0x30F1,0x30F2,0x30F3,0x30F4, 0x30F5,0x30F6,0xE6BD,0xE6BE,0xE6BF,0xE6C0,0xE6C1,0xE6C2, 0xE6C3,0xE6C4,0xE6C5,0xE6C6,0xE6C7,0xE6C8,0xE6C9,0xE6CA, 0xE6CB,0xE6CC,0xE6CD,0xE6CE,0xE6CF,0xE6D0,0xE6D1,0xE6D2, 0xE6D3,0xE6D4,0xE6D5,0xE6D6,0xE6D7,0xE6D8,0xE6D9,0xE6DA, 0xE6DB,0xE6DC,0xE6DD,0xE6DE,0xE6DF,0xE6E0,0xE6E1,0xE6E2, 0xE6E3,0xE6E4,0xE6E5,0xE6E6,0xE6E7,0xE6E8,0xE6E9,0xE6EA, 0xE6EB,0xE6EC,0xE6ED,0xE6EE,0xE6EF,0xE6F0,0xE6F1,0xE6F2, 0xE6F3,0xE6F4,0xE6F5,0xE6F6,0xE6F7,0xE6F8,0xE6F9,0xE6FA, 0xE6FB,0xE6FC,0xE6FD,0xE6FE,0xE6FF,0xE700,0xE701,0xE702, 0xE703,0xFFFD,0xE704,0xE705,0xE706,0xE707,0xE708,0xE709, 0xE70A,0xE70B,0xE70C,0xE70D,0xE70E,0xE70F,0xE710,0xE711, 0xE712,0xE713,0xE714,0xE715,0xE716,0xE717,0xE718,0xE719, 0xE71A,0xE71B,0xE71C,0xE71D,0xE71E,0xE71F,0xE720,0xE721, 0xE722,0xE723,0xE724,0x0391,0x0392,0x0393,0x0394,0x0395, 0x0396,0x0397,0x0398,0x0399,0x039A,0x039B,0x039C,0x039D, 0x039E,0x039F,0x03A0,0x03A1,0x03A3,0x03A4,0x03A5,0x03A6, 0x03A7,0x03A8,0x03A9,0xE725,0xE726,0xE727,0xE728,0xE729, 0xE72A,0xE72B,0xE72C,0x03B1,0x03B2,0x03B3,0x03B4,0x03B5, 0x03B6,0x03B7,0x03B8,0x03B9,0x03BA,0x03BB,0x03BC,0x03BD, 0x03BE,0x03BF,0x03C0,0x03C1,0x03C3,0x03C4,0x03C5,0x03C6, 0x03C7,0x03C8,0x03C9,0xE72D,0xE72E,0xE72F,0xE730,0xE731, 0xE732,0xE733,0xFE35,0xFE36,0xFE39,0xFE3A,0xFE3F,0xFE40, 0xFE3D,0xFE3E,0xFE41,0xFE42,0xFE43,0xFE44,0xE734,0xE735, 0xFE3B,0xFE3C,0xFE37,0xFE38,0xFE31,0xE736,0xFE33,0xFE34, 0xE737,0xE738,0xE739,0xE73A,0xE73B,0xE73C,0xE73D,0xE73E, 0xE73F,0xE740,0xE741,0xE742,0xE743,0xE744,0xE745,0xE746, 0xE747,0xE748,0xE749,0xE74A,0xE74B,0xE74C,0xE74D,0xE74E, 0xE74F,0xE750,0xE751,0xE752,0xE753,0xE754,0xE755,0xE756, 0xE757,0xE758,0xE759,0xE75A,0xE75B,0xE75C,0xE75D,0xE75E, 0xE75F,0xE760,0xE761,0xE762,0xE763,0xE764,0xE765,0xE766, 0xE767,0xE768,0xE769,0xE76A,0xE76B,0xE76C,0xE76D,0xE76E, 0xE76F,0xE770,0xE771,0xE772,0xE773,0xE774,0xE775,0xE776, 0xE777,0xE778,0xE779,0xE77A,0xE77B,0xE77C,0xE77D,0xE77E, 0xFFFD,0xE77F,0xE780,0xE781,0xE782,0xE783,0xE784,0xE785, 0xE786,0xE787,0xE788,0xE789,0xE78A,0xE78B,0xE78C,0xE78D, 0xE78E,0xE78F,0xE790,0xE791,0xE792,0xE793,0xE794,0xE795, 0xE796,0xE797,0xE798,0xE799,0xE79A,0xE79B,0xE79C,0xE79D, 0xE79E,0xE79F,0x0410,0x0411,0x0412,0x0413,0x0414,0x0415, 0x0401,0x0416,0x0417,0x0418,0x0419,0x041A,0x041B,0x041C, 0x041D,0x041E,0x041F,0x0420,0x0421,0x0422,0x0423,0x0424, 0x0425,0x0426,0x0427,0x0428,0x0429,0x042A,0x042B,0x042C, 0x042D,0x042E,0x042F,0xE7A0,0xE7A1,0xE7A2,0xE7A3,0xE7A4, 0xE7A5,0xE7A6,0xE7A7,0xE7A8,0xE7A9,0xE7AA,0xE7AB,0xE7AC, 0xE7AD,0xE7AE,0x0430,0x0431,0x0432,0x0433,0x0434,0x0435, 0x0451,0x0436,0x0437,0x0438,0x0439,0x043A,0x043B,0x043C, 0x043D,0x043E,0x043F,0x0440,0x0441,0x0442,0x0443,0x0444, 0x0445,0x0446,0x0447,0x0448,0x0449,0x044A,0x044B,0x044C, 0x044D,0x044E,0x044F,0xE7AF,0xE7B0,0xE7B1,0xE7B2,0xE7B3, 0xE7B4,0xE7B5,0xE7B6,0xE7B7,0xE7B8,0xE7B9,0xE7BA,0xE7BB, 0x02CA,0x02CB,0x02D9,0x2013,0x2015,0x2025,0x2035,0x2105, 0x2109,0x2196,0x2197,0x2198,0x2199,0x2215,0x221F,0x2223, 0x2252,0x2266,0x2267,0x22BF,0x2550,0x2551,0x2552,0x2553, 0x2554,0x2555,0x2556,0x2557,0x2558,0x2559,0x255A,0x255B, 0x255C,0x255D,0x255E,0x255F,0x2560,0x2561,0x2562,0x2563, 0x2564,0x2565,0x2566,0x2567,0x2568,0x2569,0x256A,0x256B, 0x256C,0x256D,0x256E,0x256F,0x2570,0x2571,0x2572,0x2573, 0x2581,0x2582,0x2583,0x2584,0x2585,0x2586,0x2587,0xFFFD, 0x2588,0x2589,0x258A,0x258B,0x258C,0x258D,0x258E,0x258F, 0x2593,0x2594,0x2595,0x25BC,0x25BD,0x25E2,0x25E3,0x25E4, 0x25E5,0x2609,0x2641,0x3012,0x301D,0x301E,0xE7BC,0xE7BD, 0xE7BE,0xE7BF,0xE7C0,0xE7C1,0xE7C2,0xE7C3,0xE7C4,0xE7C5, 0xE7C6,0x0101,0x00E1,0x01CE,0x00E0,0x0113,0x00E9,0x011B, 0x00E8,0x012B,0x00ED,0x01D0,0x00EC,0x014D,0x00F3,0x01D2, 0x00F2,0x016B,0x00FA,0x01D4,0x00F9,0x01D6,0x01D8,0x01DA, 0x01DC,0x00FC,0x00EA,0x0251,0xE7C7,0x0144,0x0148,0xE7C8, 0x0261,0xE7C9,0xE7CA,0xE7CB,0xE7CC,0x3105,0x3106,0x3107, 0x3108,0x3109,0x310A,0x310B,0x310C,0x310D,0x310E,0x310F, 0x3110,0x3111,0x3112,0x3113,0x3114,0x3115,0x3116,0x3117, 0x3118,0x3119,0x311A,0x311B,0x311C,0x311D,0x311E,0x311F, 0x3120,0x3121,0x3122,0x3123,0x3124,0x3125,0x3126,0x3127, 0x3128,0x3129,0xE7CD,0xE7CE,0xE7CF,0xE7D0,0xE7D1,0xE7D2, 0xE7D3,0xE7D4,0xE7D5,0xE7D6,0xE7D7,0xE7D8,0xE7D9,0xE7DA, 0xE7DB,0xE7DC,0xE7DD,0xE7DE,0xE7DF,0xE7E0,0xE7E1,0x3021, 0x3022,0x3023,0x3024,0x3025,0x3026,0x3027,0x3028,0x3029, 0x32A3,0x338E,0x338F,0x339C,0x339D,0x339E,0x33A1,0x33C4, 0x33CE,0x33D1,0x33D2,0x33D5,0xFE30,0xFFE2,0xFFE4,0xE7E2, 0x2121,0x3231,0xE7E3,0x2010,0xE7E4,0xE7E5,0xE7E6,0x30FC, 0x309B,0x309C,0x30FD,0x30FE,0x3006,0x309D,0x309E,0xFE49, 0xFE4A,0xFE4B,0xFE4C,0xFE4D,0xFE4E,0xFE4F,0xFE50,0xFE51, 0xFE52,0xFE54,0xFE55,0xFE56,0xFE57,0xFE59,0xFE5A,0xFE5B, 0xFE5C,0xFE5D,0xFE5E,0xFE5F,0xFE60,0xFE61,0xFFFD,0xFE62, 0xFE63,0xFE64,0xFE65,0xFE66,0xFE68,0xFE69,0xFE6A,0xFE6B, 0xE7E7,0xE7E8,0xE7E9,0xE7EA,0xE7EB,0xE7EC,0xE7ED,0xE7EE, 0xE7EF,0xE7F0,0xE7F1,0xE7F2,0xE7F3,0x3007,0xE7F4,0xE7F5, 0xE7F6,0xE7F7,0xE7F8,0xE7F9,0xE7FA,0xE7FB,0xE7FC,0xE7FD, 0xE7FE,0xE7FF,0xE800,0x2500,0x2501,0x2502,0x2503,0x2504, 0x2505,0x2506,0x2507,0x2508,0x2509,0x250A,0x250B,0x250C, 0x250D,0x250E,0x250F,0x2510,0x2511,0x2512,0x2513,0x2514, 0x2515,0x2516,0x2517,0x2518,0x2519,0x251A,0x251B,0x251C, 0x251D,0x251E,0x251F,0x2520,0x2521,0x2522,0x2523,0x2524, 0x2525,0x2526,0x2527,0x2528,0x2529,0x252A,0x252B,0x252C, 0x252D,0x252E,0x252F,0x2530,0x2531,0x2532,0x2533,0x2534, 0x2535,0x2536,0x2537,0x2538,0x2539,0x253A,0x253B,0x253C, 0x253D,0x253E,0x253F,0x2540,0x2541,0x2542,0x2543,0x2544, 0x2545,0x2546,0x2547,0x2548,0x2549,0x254A,0x254B,0xE801, 0xE802,0xE803,0xE804,0xE805,0xE806,0xE807,0xE808,0xE809, 0xE80A,0xE80B,0xE80C,0xE80D,0xE80E,0xE80F,0x72DC,0x72DD, 0x72DF,0x72E2,0x72E3,0x72E4,0x72E5,0x72E6,0x72E7,0x72EA, 0x72EB,0x72F5,0x72F6,0x72F9,0x72FD,0x72FE,0x72FF,0x7300, 0x7302,0x7304,0x7305,0x7306,0x7307,0x7308,0x7309,0x730B, 0x730C,0x730D,0x730F,0x7310,0x7311,0x7312,0x7314,0x7318, 0x7319,0x731A,0x731F,0x7320,0x7323,0x7324,0x7326,0x7327, 0x7328,0x732D,0x732F,0x7330,0x7332,0x7333,0x7335,0x7336, 0x733A,0x733B,0x733C,0x733D,0x7340,0x7341,0x7342,0x7343, 0x7344,0x7345,0x7346,0x7347,0x7348,0xFFFD,0x7349,0x734A, 0x734B,0x734C,0x734E,0x734F,0x7351,0x7353,0x7354,0x7355, 0x7356,0x7358,0x7359,0x735A,0x735B,0x735C,0x735D,0x735E, 0x735F,0x7361,0x7362,0x7363,0x7364,0x7365,0x7366,0x7367, 0x7368,0x7369,0x736A,0x736B,0x736E,0x7370,0x7371,0xE000, 0xE001,0xE002,0xE003,0xE004,0xE005,0xE006,0xE007,0xE008, 0xE009,0xE00A,0xE00B,0xE00C,0xE00D,0xE00E,0xE00F,0xE010, 0xE011,0xE012,0xE013,0xE014,0xE015,0xE016,0xE017,0xE018, 0xE019,0xE01A,0xE01B,0xE01C,0xE01D,0xE01E,0xE01F,0xE020, 0xE021,0xE022,0xE023,0xE024,0xE025,0xE026,0xE027,0xE028, 0xE029,0xE02A,0xE02B,0xE02C,0xE02D,0xE02E,0xE02F,0xE030, 0xE031,0xE032,0xE033,0xE034,0xE035,0xE036,0xE037,0xE038, 0xE039,0xE03A,0xE03B,0xE03C,0xE03D,0xE03E,0xE03F,0xE040, 0xE041,0xE042,0xE043,0xE044,0xE045,0xE046,0xE047,0xE048, 0xE049,0xE04A,0xE04B,0xE04C,0xE04D,0xE04E,0xE04F,0xE050, 0xE051,0xE052,0xE053,0xE054,0xE055,0xE056,0xE057,0xE058, 0xE059,0xE05A,0xE05B,0xE05C,0xE05D,0x7372,0x7373,0x7374, 0x7375,0x7376,0x7377,0x7378,0x7379,0x737A,0x737B,0x737C, 0x737D,0x737F,0x7380,0x7381,0x7382,0x7383,0x7385,0x7386, 0x7388,0x738A,0x738C,0x738D,0x738F,0x7390,0x7392,0x7393, 0x7394,0x7395,0x7397,0x7398,0x7399,0x739A,0x739C,0x739D, 0x739E,0x73A0,0x73A1,0x73A3,0x73A4,0x73A5,0x73A6,0x73A7, 0x73A8,0x73AA,0x73AC,0x73AD,0x73B1,0x73B4,0x73B5,0x73B6, 0x73B8,0x73B9,0x73BC,0x73BD,0x73BE,0x73BF,0x73C1,0x73C3, 0x73C4,0x73C5,0x73C6,0x73C7,0xFFFD,0x73CB,0x73CC,0x73CE, 0x73D2,0x73D3,0x73D4,0x73D5,0x73D6,0x73D7,0x73D8,0x73DA, 0x73DB,0x73DC,0x73DD,0x73DF,0x73E1,0x73E2,0x73E3,0x73E4, 0x73E6,0x73E8,0x73EA,0x73EB,0x73EC,0x73EE,0x73EF,0x73F0, 0x73F1,0x73F3,0x73F4,0x73F5,0x73F6,0x73F7,0xE05E,0xE05F, 0xE060,0xE061,0xE062,0xE063,0xE064,0xE065,0xE066,0xE067, 0xE068,0xE069,0xE06A,0xE06B,0xE06C,0xE06D,0xE06E,0xE06F, 0xE070,0xE071,0xE072,0xE073,0xE074,0xE075,0xE076,0xE077, 0xE078,0xE079,0xE07A,0xE07B,0xE07C,0xE07D,0xE07E,0xE07F, 0xE080,0xE081,0xE082,0xE083,0xE084,0xE085,0xE086,0xE087, 0xE088,0xE089,0xE08A,0xE08B,0xE08C,0xE08D,0xE08E,0xE08F, 0xE090,0xE091,0xE092,0xE093,0xE094,0xE095,0xE096,0xE097, 0xE098,0xE099,0xE09A,0xE09B,0xE09C,0xE09D,0xE09E,0xE09F, 0xE0A0,0xE0A1,0xE0A2,0xE0A3,0xE0A4,0xE0A5,0xE0A6,0xE0A7, 0xE0A8,0xE0A9,0xE0AA,0xE0AB,0xE0AC,0xE0AD,0xE0AE,0xE0AF, 0xE0B0,0xE0B1,0xE0B2,0xE0B3,0xE0B4,0xE0B5,0xE0B6,0xE0B7, 0xE0B8,0xE0B9,0xE0BA,0xE0BB,0x73F8,0x73F9,0x73FA,0x73FB, 0x73FC,0x73FD,0x73FE,0x73FF,0x7400,0x7401,0x7402,0x7404, 0x7407,0x7408,0x740B,0x740C,0x740D,0x740E,0x7411,0x7412, 0x7413,0x7414,0x7415,0x7416,0x7417,0x7418,0x7419,0x741C, 0x741D,0x741E,0x741F,0x7420,0x7421,0x7423,0x7424,0x7427, 0x7429,0x742B,0x742D,0x742F,0x7431,0x7432,0x7437,0x7438, 0x7439,0x743A,0x743B,0x743D,0x743E,0x743F,0x7440,0x7442, 0x7443,0x7444,0x7445,0x7446,0x7447,0x7448,0x7449,0x744A, 0x744B,0x744C,0x744D,0xFFFD,0x744E,0x744F,0x7450,0x7451, 0x7452,0x7453,0x7454,0x7456,0x7458,0x745D,0x7460,0x7461, 0x7462,0x7463,0x7464,0x7465,0x7466,0x7467,0x7468,0x7469, 0x746A,0x746B,0x746C,0x746E,0x746F,0x7471,0x7472,0x7473, 0x7474,0x7475,0x7478,0x7479,0x747A,0xE0BC,0xE0BD,0xE0BE, 0xE0BF,0xE0C0,0xE0C1,0xE0C2,0xE0C3,0xE0C4,0xE0C5,0xE0C6, 0xE0C7,0xE0C8,0xE0C9,0xE0CA,0xE0CB,0xE0CC,0xE0CD,0xE0CE, 0xE0CF,0xE0D0,0xE0D1,0xE0D2,0xE0D3,0xE0D4,0xE0D5,0xE0D6, 0xE0D7,0xE0D8,0xE0D9,0xE0DA,0xE0DB,0xE0DC,0xE0DD,0xE0DE, 0xE0DF,0xE0E0,0xE0E1,0xE0E2,0xE0E3,0xE0E4,0xE0E5,0xE0E6, 0xE0E7,0xE0E8,0xE0E9,0xE0EA,0xE0EB,0xE0EC,0xE0ED,0xE0EE, 0xE0EF,0xE0F0,0xE0F1,0xE0F2,0xE0F3,0xE0F4,0xE0F5,0xE0F6, 0xE0F7,0xE0F8,0xE0F9,0xE0FA,0xE0FB,0xE0FC,0xE0FD,0xE0FE, 0xE0FF,0xE100,0xE101,0xE102,0xE103,0xE104,0xE105,0xE106, 0xE107,0xE108,0xE109,0xE10A,0xE10B,0xE10C,0xE10D,0xE10E, 0xE10F,0xE110,0xE111,0xE112,0xE113,0xE114,0xE115,0xE116, 0xE117,0xE118,0xE119,0x747B,0x747C,0x747D,0x747F,0x7482, 0x7484,0x7485,0x7486,0x7488,0x7489,0x748A,0x748C,0x748D, 0x748F,0x7491,0x7492,0x7493,0x7494,0x7495,0x7496,0x7497, 0x7498,0x7499,0x749A,0x749B,0x749D,0x749F,0x74A0,0x74A1, 0x74A2,0x74A3,0x74A4,0x74A5,0x74A6,0x74AA,0x74AB,0x74AC, 0x74AD,0x74AE,0x74AF,0x74B0,0x74B1,0x74B2,0x74B3,0x74B4, 0x74B5,0x74B6,0x74B7,0x74B8,0x74B9,0x74BB,0x74BC,0x74BD, 0x74BE,0x74BF,0x74C0,0x74C1,0x74C2,0x74C3,0x74C4,0x74C5, 0x74C6,0x74C7,0xFFFD,0x74C8,0x74C9,0x74CA,0x74CB,0x74CC, 0x74CD,0x74CE,0x74CF,0x74D0,0x74D1,0x74D3,0x74D4,0x74D5, 0x74D6,0x74D7,0x74D8,0x74D9,0x74DA,0x74DB,0x74DD,0x74DF, 0x74E1,0x74E5,0x74E7,0x74E8,0x74E9,0x74EA,0x74EB,0x74EC, 0x74ED,0x74F0,0x74F1,0x74F2,0xE11A,0xE11B,0xE11C,0xE11D, 0xE11E,0xE11F,0xE120,0xE121,0xE122,0xE123,0xE124,0xE125, 0xE126,0xE127,0xE128,0xE129,0xE12A,0xE12B,0xE12C,0xE12D, 0xE12E,0xE12F,0xE130,0xE131,0xE132,0xE133,0xE134,0xE135, 0xE136,0xE137,0xE138,0xE139,0xE13A,0xE13B,0xE13C,0xE13D, 0xE13E,0xE13F,0xE140,0xE141,0xE142,0xE143,0xE144,0xE145, 0xE146,0xE147,0xE148,0xE149,0xE14A,0xE14B,0xE14C,0xE14D, 0xE14E,0xE14F,0xE150,0xE151,0xE152,0xE153,0xE154,0xE155, 0xE156,0xE157,0xE158,0xE159,0xE15A,0xE15B,0xE15C,0xE15D, 0xE15E,0xE15F,0xE160,0xE161,0xE162,0xE163,0xE164,0xE165, 0xE166,0xE167,0xE168,0xE169,0xE16A,0xE16B,0xE16C,0xE16D, 0xE16E,0xE16F,0xE170,0xE171,0xE172,0xE173,0xE174,0xE175, 0xE176,0xE177,0x74F3,0x74F5,0x74F8,0x74F9,0x74FA,0x74FB, 0x74FC,0x74FD,0x74FE,0x7500,0x7501,0x7502,0x7503,0x7505, 0x7506,0x7507,0x7508,0x7509,0x750A,0x750B,0x750C,0x750E, 0x7510,0x7512,0x7514,0x7515,0x7516,0x7517,0x751B,0x751D, 0x751E,0x7520,0x7521,0x7522,0x7523,0x7524,0x7526,0x7527, 0x752A,0x752E,0x7534,0x7536,0x7539,0x753C,0x753D,0x753F, 0x7541,0x7542,0x7543,0x7544,0x7546,0x7547,0x7549,0x754A, 0x754D,0x7550,0x7551,0x7552,0x7553,0x7555,0x7556,0x7557, 0x7558,0xFFFD,0x755D,0x755E,0x755F,0x7560,0x7561,0x7562, 0x7563,0x7564,0x7567,0x7568,0x7569,0x756B,0x756C,0x756D, 0x756E,0x756F,0x7570,0x7571,0x7573,0x7575,0x7576,0x7577, 0x757A,0x757B,0x757C,0x757D,0x757E,0x7580,0x7581,0x7582, 0x7584,0x7585,0x7587,0xE178,0xE179,0xE17A,0xE17B,0xE17C, 0xE17D,0xE17E,0xE17F,0xE180,0xE181,0xE182,0xE183,0xE184, 0xE185,0xE186,0xE187,0xE188,0xE189,0xE18A,0xE18B,0xE18C, 0xE18D,0xE18E,0xE18F,0xE190,0xE191,0xE192,0xE193,0xE194, 0xE195,0xE196,0xE197,0xE198,0xE199,0xE19A,0xE19B,0xE19C, 0xE19D,0xE19E,0xE19F,0xE1A0,0xE1A1,0xE1A2,0xE1A3,0xE1A4, 0xE1A5,0xE1A6,0xE1A7,0xE1A8,0xE1A9,0xE1AA,0xE1AB,0xE1AC, 0xE1AD,0xE1AE,0xE1AF,0xE1B0,0xE1B1,0xE1B2,0xE1B3,0xE1B4, 0xE1B5,0xE1B6,0xE1B7,0xE1B8,0xE1B9,0xE1BA,0xE1BB,0xE1BC, 0xE1BD,0xE1BE,0xE1BF,0xE1C0,0xE1C1,0xE1C2,0xE1C3,0xE1C4, 0xE1C5,0xE1C6,0xE1C7,0xE1C8,0xE1C9,0xE1CA,0xE1CB,0xE1CC, 0xE1CD,0xE1CE,0xE1CF,0xE1D0,0xE1D1,0xE1D2,0xE1D3,0xE1D4, 0xE1D5,0x7588,0x7589,0x758A,0x758C,0x758D,0x758E,0x7590, 0x7593,0x7595,0x7598,0x759B,0x759C,0x759E,0x75A2,0x75A6, 0x75A7,0x75A8,0x75A9,0x75AA,0x75AD,0x75B6,0x75B7,0x75BA, 0x75BB,0x75BF,0x75C0,0x75C1,0x75C6,0x75CB,0x75CC,0x75CE, 0x75CF,0x75D0,0x75D1,0x75D3,0x75D7,0x75D9,0x75DA,0x75DC, 0x75DD,0x75DF,0x75E0,0x75E1,0x75E5,0x75E9,0x75EC,0x75ED, 0x75EE,0x75EF,0x75F2,0x75F3,0x75F5,0x75F6,0x75F7,0x75F8, 0x75FA,0x75FB,0x75FD,0x75FE,0x7602,0x7604,0x7606,0x7607, 0xFFFD,0x7608,0x7609,0x760B,0x760D,0x760E,0x760F,0x7611, 0x7612,0x7613,0x7614,0x7616,0x761A,0x761C,0x761D,0x761E, 0x7621,0x7623,0x7627,0x7628,0x762C,0x762E,0x762F,0x7631, 0x7632,0x7636,0x7637,0x7639,0x763A,0x763B,0x763D,0x7641, 0x7642,0x7644,0xE1D6,0xE1D7,0xE1D8,0xE1D9,0xE1DA,0xE1DB, 0xE1DC,0xE1DD,0xE1DE,0xE1DF,0xE1E0,0xE1E1,0xE1E2,0xE1E3, 0xE1E4,0xE1E5,0xE1E6,0xE1E7,0xE1E8,0xE1E9,0xE1EA,0xE1EB, 0xE1EC,0xE1ED,0xE1EE,0xE1EF,0xE1F0,0xE1F1,0xE1F2,0xE1F3, 0xE1F4,0xE1F5,0xE1F6,0xE1F7,0xE1F8,0xE1F9,0xE1FA,0xE1FB, 0xE1FC,0xE1FD,0xE1FE,0xE1FF,0xE200,0xE201,0xE202,0xE203, 0xE204,0xE205,0xE206,0xE207,0xE208,0xE209,0xE20A,0xE20B, 0xE20C,0xE20D,0xE20E,0xE20F,0xE210,0xE211,0xE212,0xE213, 0xE214,0xE215,0xE216,0xE217,0xE218,0xE219,0xE21A,0xE21B, 0xE21C,0xE21D,0xE21E,0xE21F,0xE220,0xE221,0xE222,0xE223, 0xE224,0xE225,0xE226,0xE227,0xE228,0xE229,0xE22A,0xE22B, 0xE22C,0xE22D,0xE22E,0xE22F,0xE230,0xE231,0xE232,0xE233 }; const static uint16 gbkDecoderInnerIndex3[]= { 0x7645,0x7646,0x7647,0x7648,0x7649,0x764A,0x764B,0x764E, 0x764F,0x7650,0x7651,0x7652,0x7653,0x7655,0x7657,0x7658, 0x7659,0x765A,0x765B,0x765D,0x765F,0x7660,0x7661,0x7662, 0x7664,0x7665,0x7666,0x7667,0x7668,0x7669,0x766A,0x766C, 0x766D,0x766E,0x7670,0x7671,0x7672,0x7673,0x7674,0x7675, 0x7676,0x7677,0x7679,0x767A,0x767C,0x767F,0x7680,0x7681, 0x7683,0x7685,0x7689,0x768A,0x768C,0x768D,0x768F,0x7690, 0x7692,0x7694,0x7695,0x7697,0x7698,0x769A,0x769B,0xFFFD, 0x769C,0x769D,0x769E,0x769F,0x76A0,0x76A1,0x76A2,0x76A3, 0x76A5,0x76A6,0x76A7,0x76A8,0x76A9,0x76AA,0x76AB,0x76AC, 0x76AD,0x76AF,0x76B0,0x76B3,0x76B5,0x76B6,0x76B7,0x76B8, 0x76B9,0x76BA,0x76BB,0x76BC,0x76BD,0x76BE,0x76C0,0x76C1, 0x76C3,0x554A,0x963F,0x57C3,0x6328,0x54CE,0x5509,0x54C0, 0x7691,0x764C,0x853C,0x77EE,0x827E,0x788D,0x7231,0x9698, 0x978D,0x6C28,0x5B89,0x4FFA,0x6309,0x6697,0x5CB8,0x80FA, 0x6848,0x80AE,0x6602,0x76CE,0x51F9,0x6556,0x71AC,0x7FF1, 0x8884,0x50B2,0x5965,0x61CA,0x6FB3,0x82AD,0x634C,0x6252, 0x53ED,0x5427,0x7B06,0x516B,0x75A4,0x5DF4,0x62D4,0x8DCB, 0x9776,0x628A,0x8019,0x575D,0x9738,0x7F62,0x7238,0x767D, 0x67CF,0x767E,0x6446,0x4F70,0x8D25,0x62DC,0x7A17,0x6591, 0x73ED,0x642C,0x6273,0x822C,0x9881,0x677F,0x7248,0x626E, 0x62CC,0x4F34,0x74E3,0x534A,0x529E,0x7ECA,0x90A6,0x5E2E, 0x6886,0x699C,0x8180,0x7ED1,0x68D2,0x78C5,0x868C,0x9551, 0x508D,0x8C24,0x82DE,0x80DE,0x5305,0x8912,0x5265,0x76C4, 0x76C7,0x76C9,0x76CB,0x76CC,0x76D3,0x76D5,0x76D9,0x76DA, 0x76DC,0x76DD,0x76DE,0x76E0,0x76E1,0x76E2,0x76E3,0x76E4, 0x76E6,0x76E7,0x76E8,0x76E9,0x76EA,0x76EB,0x76EC,0x76ED, 0x76F0,0x76F3,0x76F5,0x76F6,0x76F7,0x76FA,0x76FB,0x76FD, 0x76FF,0x7700,0x7702,0x7703,0x7705,0x7706,0x770A,0x770C, 0x770E,0x770F,0x7710,0x7711,0x7712,0x7713,0x7714,0x7715, 0x7716,0x7717,0x7718,0x771B,0x771C,0x771D,0x771E,0x7721, 0x7723,0x7724,0x7725,0x7727,0x772A,0x772B,0xFFFD,0x772C, 0x772E,0x7730,0x7731,0x7732,0x7733,0x7734,0x7739,0x773B, 0x773D,0x773E,0x773F,0x7742,0x7744,0x7745,0x7746,0x7748, 0x7749,0x774A,0x774B,0x774C,0x774D,0x774E,0x774F,0x7752, 0x7753,0x7754,0x7755,0x7756,0x7757,0x7758,0x7759,0x775C, 0x8584,0x96F9,0x4FDD,0x5821,0x9971,0x5B9D,0x62B1,0x62A5, 0x66B4,0x8C79,0x9C8D,0x7206,0x676F,0x7891,0x60B2,0x5351, 0x5317,0x8F88,0x80CC,0x8D1D,0x94A1,0x500D,0x72C8,0x5907, 0x60EB,0x7119,0x88AB,0x5954,0x82EF,0x672C,0x7B28,0x5D29, 0x7EF7,0x752D,0x6CF5,0x8E66,0x8FF8,0x903C,0x9F3B,0x6BD4, 0x9119,0x7B14,0x5F7C,0x78A7,0x84D6,0x853D,0x6BD5,0x6BD9, 0x6BD6,0x5E01,0x5E87,0x75F9,0x95ED,0x655D,0x5F0A,0x5FC5, 0x8F9F,0x58C1,0x81C2,0x907F,0x965B,0x97AD,0x8FB9,0x7F16, 0x8D2C,0x6241,0x4FBF,0x53D8,0x535E,0x8FA8,0x8FA9,0x8FAB, 0x904D,0x6807,0x5F6A,0x8198,0x8868,0x9CD6,0x618B,0x522B, 0x762A,0x5F6C,0x658C,0x6FD2,0x6EE8,0x5BBE,0x6448,0x5175, 0x51B0,0x67C4,0x4E19,0x79C9,0x997C,0x70B3,0x775D,0x775E, 0x775F,0x7760,0x7764,0x7767,0x7769,0x776A,0x776D,0x776E, 0x776F,0x7770,0x7771,0x7772,0x7773,0x7774,0x7775,0x7776, 0x7777,0x7778,0x777A,0x777B,0x777C,0x7781,0x7782,0x7783, 0x7786,0x7787,0x7788,0x7789,0x778A,0x778B,0x778F,0x7790, 0x7793,0x7794,0x7795,0x7796,0x7797,0x7798,0x7799,0x779A, 0x779B,0x779C,0x779D,0x779E,0x77A1,0x77A3,0x77A4,0x77A6, 0x77A8,0x77AB,0x77AD,0x77AE,0x77AF,0x77B1,0x77B2,0x77B4, 0x77B6,0x77B7,0x77B8,0x77B9,0x77BA,0xFFFD,0x77BC,0x77BE, 0x77C0,0x77C1,0x77C2,0x77C3,0x77C4,0x77C5,0x77C6,0x77C7, 0x77C8,0x77C9,0x77CA,0x77CB,0x77CC,0x77CE,0x77CF,0x77D0, 0x77D1,0x77D2,0x77D3,0x77D4,0x77D5,0x77D6,0x77D8,0x77D9, 0x77DA,0x77DD,0x77DE,0x77DF,0x77E0,0x77E1,0x77E4,0x75C5, 0x5E76,0x73BB,0x83E0,0x64AD,0x62E8,0x94B5,0x6CE2,0x535A, 0x52C3,0x640F,0x94C2,0x7B94,0x4F2F,0x5E1B,0x8236,0x8116, 0x818A,0x6E24,0x6CCA,0x9A73,0x6355,0x535C,0x54FA,0x8865, 0x57E0,0x4E0D,0x5E03,0x6B65,0x7C3F,0x90E8,0x6016,0x64E6, 0x731C,0x88C1,0x6750,0x624D,0x8D22,0x776C,0x8E29,0x91C7, 0x5F69,0x83DC,0x8521,0x9910,0x53C2,0x8695,0x6B8B,0x60ED, 0x60E8,0x707F,0x82CD,0x8231,0x4ED3,0x6CA7,0x85CF,0x64CD, 0x7CD9,0x69FD,0x66F9,0x8349,0x5395,0x7B56,0x4FA7,0x518C, 0x6D4B,0x5C42,0x8E6D,0x63D2,0x53C9,0x832C,0x8336,0x67E5, 0x78B4,0x643D,0x5BDF,0x5C94,0x5DEE,0x8BE7,0x62C6,0x67F4, 0x8C7A,0x6400,0x63BA,0x8749,0x998B,0x8C17,0x7F20,0x94F2, 0x4EA7,0x9610,0x98A4,0x660C,0x7316,0x77E6,0x77E8,0x77EA, 0x77EF,0x77F0,0x77F1,0x77F2,0x77F4,0x77F5,0x77F7,0x77F9, 0x77FA,0x77FB,0x77FC,0x7803,0x7804,0x7805,0x7806,0x7807, 0x7808,0x780A,0x780B,0x780E,0x780F,0x7810,0x7813,0x7815, 0x7819,0x781B,0x781E,0x7820,0x7821,0x7822,0x7824,0x7828, 0x782A,0x782B,0x782E,0x782F,0x7831,0x7832,0x7833,0x7835, 0x7836,0x783D,0x783F,0x7841,0x7842,0x7843,0x7844,0x7846, 0x7848,0x7849,0x784A,0x784B,0x784D,0x784F,0x7851,0x7853, 0x7854,0x7858,0x7859,0x785A,0xFFFD,0x785B,0x785C,0x785E, 0x785F,0x7860,0x7861,0x7862,0x7863,0x7864,0x7865,0x7866, 0x7867,0x7868,0x7869,0x786F,0x7870,0x7871,0x7872,0x7873, 0x7874,0x7875,0x7876,0x7878,0x7879,0x787A,0x787B,0x787D, 0x787E,0x787F,0x7880,0x7881,0x7882,0x7883,0x573A,0x5C1D, 0x5E38,0x957F,0x507F,0x80A0,0x5382,0x655E,0x7545,0x5531, 0x5021,0x8D85,0x6284,0x949E,0x671D,0x5632,0x6F6E,0x5DE2, 0x5435,0x7092,0x8F66,0x626F,0x64A4,0x63A3,0x5F7B,0x6F88, 0x90F4,0x81E3,0x8FB0,0x5C18,0x6668,0x5FF1,0x6C89,0x9648, 0x8D81,0x886C,0x6491,0x79F0,0x57CE,0x6A59,0x6210,0x5448, 0x4E58,0x7A0B,0x60E9,0x6F84,0x8BDA,0x627F,0x901E,0x9A8B, 0x79E4,0x5403,0x75F4,0x6301,0x5319,0x6C60,0x8FDF,0x5F1B, 0x9A70,0x803B,0x9F7F,0x4F88,0x5C3A,0x8D64,0x7FC5,0x65A5, 0x70BD,0x5145,0x51B2,0x866B,0x5D07,0x5BA0,0x62BD,0x916C, 0x7574,0x8E0C,0x7A20,0x6101,0x7B79,0x4EC7,0x7EF8,0x7785, 0x4E11,0x81ED,0x521D,0x51FA,0x6A71,0x53A8,0x8E87,0x9504, 0x96CF,0x6EC1,0x9664,0x695A,0x7884,0x7885,0x7886,0x7888, 0x788A,0x788B,0x788F,0x7890,0x7892,0x7894,0x7895,0x7896, 0x7899,0x789D,0x789E,0x78A0,0x78A2,0x78A4,0x78A6,0x78A8, 0x78A9,0x78AA,0x78AB,0x78AC,0x78AD,0x78AE,0x78AF,0x78B5, 0x78B6,0x78B7,0x78B8,0x78BA,0x78BB,0x78BC,0x78BD,0x78BF, 0x78C0,0x78C2,0x78C3,0x78C4,0x78C6,0x78C7,0x78C8,0x78CC, 0x78CD,0x78CE,0x78CF,0x78D1,0x78D2,0x78D3,0x78D6,0x78D7, 0x78D8,0x78DA,0x78DB,0x78DC,0x78DD,0x78DE,0x78DF,0x78E0, 0x78E1,0x78E2,0x78E3,0xFFFD,0x78E4,0x78E5,0x78E6,0x78E7, 0x78E9,0x78EA,0x78EB,0x78ED,0x78EE,0x78EF,0x78F0,0x78F1, 0x78F3,0x78F5,0x78F6,0x78F8,0x78F9,0x78FB,0x78FC,0x78FD, 0x78FE,0x78FF,0x7900,0x7902,0x7903,0x7904,0x7906,0x7907, 0x7908,0x7909,0x790A,0x790B,0x790C,0x7840,0x50A8,0x77D7, 0x6410,0x89E6,0x5904,0x63E3,0x5DDD,0x7A7F,0x693D,0x4F20, 0x8239,0x5598,0x4E32,0x75AE,0x7A97,0x5E62,0x5E8A,0x95EF, 0x521B,0x5439,0x708A,0x6376,0x9524,0x5782,0x6625,0x693F, 0x9187,0x5507,0x6DF3,0x7EAF,0x8822,0x6233,0x7EF0,0x75B5, 0x8328,0x78C1,0x96CC,0x8F9E,0x6148,0x74F7,0x8BCD,0x6B64, 0x523A,0x8D50,0x6B21,0x806A,0x8471,0x56F1,0x5306,0x4ECE, 0x4E1B,0x51D1,0x7C97,0x918B,0x7C07,0x4FC3,0x8E7F,0x7BE1, 0x7A9C,0x6467,0x5D14,0x50AC,0x8106,0x7601,0x7CB9,0x6DEC, 0x7FE0,0x6751,0x5B58,0x5BF8,0x78CB,0x64AE,0x6413,0x63AA, 0x632B,0x9519,0x642D,0x8FBE,0x7B54,0x7629,0x6253,0x5927, 0x5446,0x6B79,0x50A3,0x6234,0x5E26,0x6B86,0x4EE3,0x8D37, 0x888B,0x5F85,0x902E,0x790D,0x790E,0x790F,0x7910,0x7911, 0x7912,0x7914,0x7915,0x7916,0x7917,0x7918,0x7919,0x791A, 0x791B,0x791C,0x791D,0x791F,0x7920,0x7921,0x7922,0x7923, 0x7925,0x7926,0x7927,0x7928,0x7929,0x792A,0x792B,0x792C, 0x792D,0x792E,0x792F,0x7930,0x7931,0x7932,0x7933,0x7935, 0x7936,0x7937,0x7938,0x7939,0x793D,0x793F,0x7942,0x7943, 0x7944,0x7945,0x7947,0x794A,0x794B,0x794C,0x794D,0x794E, 0x794F,0x7950,0x7951,0x7952,0x7954,0x7955,0x7958,0x7959, 0x7961,0x7963,0xFFFD,0x7964,0x7966,0x7969,0x796A,0x796B, 0x796C,0x796E,0x7970,0x7971,0x7972,0x7973,0x7974,0x7975, 0x7976,0x7979,0x797B,0x797C,0x797D,0x797E,0x797F,0x7982, 0x7983,0x7986,0x7987,0x7988,0x7989,0x798B,0x798C,0x798D, 0x798E,0x7990,0x7991,0x7992,0x6020,0x803D,0x62C5,0x4E39, 0x5355,0x90F8,0x63B8,0x80C6,0x65E6,0x6C2E,0x4F46,0x60EE, 0x6DE1,0x8BDE,0x5F39,0x86CB,0x5F53,0x6321,0x515A,0x8361, 0x6863,0x5200,0x6363,0x8E48,0x5012,0x5C9B,0x7977,0x5BFC, 0x5230,0x7A3B,0x60BC,0x9053,0x76D7,0x5FB7,0x5F97,0x7684, 0x8E6C,0x706F,0x767B,0x7B49,0x77AA,0x51F3,0x9093,0x5824, 0x4F4E,0x6EF4,0x8FEA,0x654C,0x7B1B,0x72C4,0x6DA4,0x7FDF, 0x5AE1,0x62B5,0x5E95,0x5730,0x8482,0x7B2C,0x5E1D,0x5F1F, 0x9012,0x7F14,0x98A0,0x6382,0x6EC7,0x7898,0x70B9,0x5178, 0x975B,0x57AB,0x7535,0x4F43,0x7538,0x5E97,0x60E6,0x5960, 0x6DC0,0x6BBF,0x7889,0x53FC,0x96D5,0x51CB,0x5201,0x6389, 0x540A,0x9493,0x8C03,0x8DCC,0x7239,0x789F,0x8776,0x8FED, 0x8C0D,0x53E0,0x7993,0x7994,0x7995,0x7996,0x7997,0x7998, 0x7999,0x799B,0x799C,0x799D,0x799E,0x799F,0x79A0,0x79A1, 0x79A2,0x79A3,0x79A4,0x79A5,0x79A6,0x79A8,0x79A9,0x79AA, 0x79AB,0x79AC,0x79AD,0x79AE,0x79AF,0x79B0,0x79B1,0x79B2, 0x79B4,0x79B5,0x79B6,0x79B7,0x79B8,0x79BC,0x79BF,0x79C2, 0x79C4,0x79C5,0x79C7,0x79C8,0x79CA,0x79CC,0x79CE,0x79CF, 0x79D0,0x79D3,0x79D4,0x79D6,0x79D7,0x79D9,0x79DA,0x79DB, 0x79DC,0x79DD,0x79DE,0x79E0,0x79E1,0x79E2,0x79E5,0x79E8, 0x79EA,0xFFFD,0x79EC,0x79EE,0x79F1,0x79F2,0x79F3,0x79F4, 0x79F5,0x79F6,0x79F7,0x79F9,0x79FA,0x79FC,0x79FE,0x79FF, 0x7A01,0x7A04,0x7A05,0x7A07,0x7A08,0x7A09,0x7A0A,0x7A0C, 0x7A0F,0x7A10,0x7A11,0x7A12,0x7A13,0x7A15,0x7A16,0x7A18, 0x7A19,0x7A1B,0x7A1C,0x4E01,0x76EF,0x53EE,0x9489,0x9876, 0x9F0E,0x952D,0x5B9A,0x8BA2,0x4E22,0x4E1C,0x51AC,0x8463, 0x61C2,0x52A8,0x680B,0x4F97,0x606B,0x51BB,0x6D1E,0x515C, 0x6296,0x6597,0x9661,0x8C46,0x9017,0x75D8,0x90FD,0x7763, 0x6BD2,0x728A,0x72EC,0x8BFB,0x5835,0x7779,0x8D4C,0x675C, 0x9540,0x809A,0x5EA6,0x6E21,0x5992,0x7AEF,0x77ED,0x953B, 0x6BB5,0x65AD,0x7F0E,0x5806,0x5151,0x961F,0x5BF9,0x58A9, 0x5428,0x8E72,0x6566,0x987F,0x56E4,0x949D,0x76FE,0x9041, 0x6387,0x54C6,0x591A,0x593A,0x579B,0x8EB2,0x6735,0x8DFA, 0x8235,0x5241,0x60F0,0x5815,0x86FE,0x5CE8,0x9E45,0x4FC4, 0x989D,0x8BB9,0x5A25,0x6076,0x5384,0x627C,0x904F,0x9102, 0x997F,0x6069,0x800C,0x513F,0x8033,0x5C14,0x9975,0x6D31, 0x4E8C,0x7A1D,0x7A1F,0x7A21,0x7A22,0x7A24,0x7A25,0x7A26, 0x7A27,0x7A28,0x7A29,0x7A2A,0x7A2B,0x7A2C,0x7A2D,0x7A2E, 0x7A2F,0x7A30,0x7A31,0x7A32,0x7A34,0x7A35,0x7A36,0x7A38, 0x7A3A,0x7A3E,0x7A40,0x7A41,0x7A42,0x7A43,0x7A44,0x7A45, 0x7A47,0x7A48,0x7A49,0x7A4A,0x7A4B,0x7A4C,0x7A4D,0x7A4E, 0x7A4F,0x7A50,0x7A52,0x7A53,0x7A54,0x7A55,0x7A56,0x7A58, 0x7A59,0x7A5A,0x7A5B,0x7A5C,0x7A5D,0x7A5E,0x7A5F,0x7A60, 0x7A61,0x7A62,0x7A63,0x7A64,0x7A65,0x7A66,0x7A67,0x7A68, 0xFFFD,0x7A69,0x7A6A,0x7A6B,0x7A6C,0x7A6D,0x7A6E,0x7A6F, 0x7A71,0x7A72,0x7A73,0x7A75,0x7A7B,0x7A7C,0x7A7D,0x7A7E, 0x7A82,0x7A85,0x7A87,0x7A89,0x7A8A,0x7A8B,0x7A8C,0x7A8E, 0x7A8F,0x7A90,0x7A93,0x7A94,0x7A99,0x7A9A,0x7A9B,0x7A9E, 0x7AA1,0x7AA2,0x8D30,0x53D1,0x7F5A,0x7B4F,0x4F10,0x4E4F, 0x9600,0x6CD5,0x73D0,0x85E9,0x5E06,0x756A,0x7FFB,0x6A0A, 0x77FE,0x9492,0x7E41,0x51E1,0x70E6,0x53CD,0x8FD4,0x8303, 0x8D29,0x72AF,0x996D,0x6CDB,0x574A,0x82B3,0x65B9,0x80AA, 0x623F,0x9632,0x59A8,0x4EFF,0x8BBF,0x7EBA,0x653E,0x83F2, 0x975E,0x5561,0x98DE,0x80A5,0x532A,0x8BFD,0x5420,0x80BA, 0x5E9F,0x6CB8,0x8D39,0x82AC,0x915A,0x5429,0x6C1B,0x5206, 0x7EB7,0x575F,0x711A,0x6C7E,0x7C89,0x594B,0x4EFD,0x5FFF, 0x6124,0x7CAA,0x4E30,0x5C01,0x67AB,0x8702,0x5CF0,0x950B, 0x98CE,0x75AF,0x70FD,0x9022,0x51AF,0x7F1D,0x8BBD,0x5949, 0x51E4,0x4F5B,0x5426,0x592B,0x6577,0x80A4,0x5B75,0x6276, 0x62C2,0x8F90,0x5E45,0x6C1F,0x7B26,0x4F0F,0x4FD8,0x670D, 0x7AA3,0x7AA4,0x7AA7,0x7AA9,0x7AAA,0x7AAB,0x7AAE,0x7AAF, 0x7AB0,0x7AB1,0x7AB2,0x7AB4,0x7AB5,0x7AB6,0x7AB7,0x7AB8, 0x7AB9,0x7ABA,0x7ABB,0x7ABC,0x7ABD,0x7ABE,0x7AC0,0x7AC1, 0x7AC2,0x7AC3,0x7AC4,0x7AC5,0x7AC6,0x7AC7,0x7AC8,0x7AC9, 0x7ACA,0x7ACC,0x7ACD,0x7ACE,0x7ACF,0x7AD0,0x7AD1,0x7AD2, 0x7AD3,0x7AD4,0x7AD5,0x7AD7,0x7AD8,0x7ADA,0x7ADB,0x7ADC, 0x7ADD,0x7AE1,0x7AE2,0x7AE4,0x7AE7,0x7AE8,0x7AE9,0x7AEA, 0x7AEB,0x7AEC,0x7AEE,0x7AF0,0x7AF1,0x7AF2,0x7AF3,0xFFFD, 0x7AF4,0x7AF5,0x7AF6,0x7AF7,0x7AF8,0x7AFB,0x7AFC,0x7AFE, 0x7B00,0x7B01,0x7B02,0x7B05,0x7B07,0x7B09,0x7B0C,0x7B0D, 0x7B0E,0x7B10,0x7B12,0x7B13,0x7B16,0x7B17,0x7B18,0x7B1A, 0x7B1C,0x7B1D,0x7B1F,0x7B21,0x7B22,0x7B23,0x7B27,0x7B29, 0x7B2D,0x6D6E,0x6DAA,0x798F,0x88B1,0x5F17,0x752B,0x629A, 0x8F85,0x4FEF,0x91DC,0x65A7,0x812F,0x8151,0x5E9C,0x8150, 0x8D74,0x526F,0x8986,0x8D4B,0x590D,0x5085,0x4ED8,0x961C, 0x7236,0x8179,0x8D1F,0x5BCC,0x8BA3,0x9644,0x5987,0x7F1A, 0x5490,0x5676,0x560E,0x8BE5,0x6539,0x6982,0x9499,0x76D6, 0x6E89,0x5E72,0x7518,0x6746,0x67D1,0x7AFF,0x809D,0x8D76, 0x611F,0x79C6,0x6562,0x8D63,0x5188,0x521A,0x94A2,0x7F38, 0x809B,0x7EB2,0x5C97,0x6E2F,0x6760,0x7BD9,0x768B,0x9AD8, 0x818F,0x7F94,0x7CD5,0x641E,0x9550,0x7A3F,0x544A,0x54E5, 0x6B4C,0x6401,0x6208,0x9E3D,0x80F3,0x7599,0x5272,0x9769, 0x845B,0x683C,0x86E4,0x9601,0x9694,0x94EC,0x4E2A,0x5404, 0x7ED9,0x6839,0x8DDF,0x8015,0x66F4,0x5E9A,0x7FB9,0x7B2F, 0x7B30,0x7B32,0x7B34,0x7B35,0x7B36,0x7B37,0x7B39,0x7B3B, 0x7B3D,0x7B3F,0x7B40,0x7B41,0x7B42,0x7B43,0x7B44,0x7B46, 0x7B48,0x7B4A,0x7B4D,0x7B4E,0x7B53,0x7B55,0x7B57,0x7B59, 0x7B5C,0x7B5E,0x7B5F,0x7B61,0x7B63,0x7B64,0x7B65,0x7B66, 0x7B67,0x7B68,0x7B69,0x7B6A,0x7B6B,0x7B6C,0x7B6D,0x7B6F, 0x7B70,0x7B73,0x7B74,0x7B76,0x7B78,0x7B7A,0x7B7C,0x7B7D, 0x7B7F,0x7B81,0x7B82,0x7B83,0x7B84,0x7B86,0x7B87,0x7B88, 0x7B89,0x7B8A,0x7B8B,0x7B8C,0x7B8E,0x7B8F,0xFFFD,0x7B91, 0x7B92,0x7B93,0x7B96,0x7B98,0x7B99,0x7B9A,0x7B9B,0x7B9E, 0x7B9F,0x7BA0,0x7BA3,0x7BA4,0x7BA5,0x7BAE,0x7BAF,0x7BB0, 0x7BB2,0x7BB3,0x7BB5,0x7BB6,0x7BB7,0x7BB9,0x7BBA,0x7BBB, 0x7BBC,0x7BBD,0x7BBE,0x7BBF,0x7BC0,0x7BC2,0x7BC3,0x7BC4, 0x57C2,0x803F,0x6897,0x5DE5,0x653B,0x529F,0x606D,0x9F9A, 0x4F9B,0x8EAC,0x516C,0x5BAB,0x5F13,0x5DE9,0x6C5E,0x62F1, 0x8D21,0x5171,0x94A9,0x52FE,0x6C9F,0x82DF,0x72D7,0x57A2, 0x6784,0x8D2D,0x591F,0x8F9C,0x83C7,0x5495,0x7B8D,0x4F30, 0x6CBD,0x5B64,0x59D1,0x9F13,0x53E4,0x86CA,0x9AA8,0x8C37, 0x80A1,0x6545,0x987E,0x56FA,0x96C7,0x522E,0x74DC,0x5250, 0x5BE1,0x6302,0x8902,0x4E56,0x62D0,0x602A,0x68FA,0x5173, 0x5B98,0x51A0,0x89C2,0x7BA1,0x9986,0x7F50,0x60EF,0x704C, 0x8D2F,0x5149,0x5E7F,0x901B,0x7470,0x89C4,0x572D,0x7845, 0x5F52,0x9F9F,0x95FA,0x8F68,0x9B3C,0x8BE1,0x7678,0x6842, 0x67DC,0x8DEA,0x8D35,0x523D,0x8F8A,0x6EDA,0x68CD,0x9505, 0x90ED,0x56FD,0x679C,0x88F9,0x8FC7,0x54C8,0x7BC5,0x7BC8, 0x7BC9,0x7BCA,0x7BCB,0x7BCD,0x7BCE,0x7BCF,0x7BD0,0x7BD2, 0x7BD4,0x7BD5,0x7BD6,0x7BD7,0x7BD8,0x7BDB,0x7BDC,0x7BDE, 0x7BDF,0x7BE0,0x7BE2,0x7BE3,0x7BE4,0x7BE7,0x7BE8,0x7BE9, 0x7BEB,0x7BEC,0x7BED,0x7BEF,0x7BF0,0x7BF2,0x7BF3,0x7BF4, 0x7BF5,0x7BF6,0x7BF8,0x7BF9,0x7BFA,0x7BFB,0x7BFD,0x7BFF, 0x7C00,0x7C01,0x7C02,0x7C03,0x7C04,0x7C05,0x7C06,0x7C08, 0x7C09,0x7C0A,0x7C0D,0x7C0E,0x7C10,0x7C11,0x7C12,0x7C13, 0x7C14,0x7C15,0x7C17,0x7C18,0x7C19,0xFFFD,0x7C1A,0x7C1B, 0x7C1C,0x7C1D,0x7C1E,0x7C20,0x7C21,0x7C22,0x7C23,0x7C24, 0x7C25,0x7C28,0x7C29,0x7C2B,0x7C2C,0x7C2D,0x7C2E,0x7C2F, 0x7C30,0x7C31,0x7C32,0x7C33,0x7C34,0x7C35,0x7C36,0x7C37, 0x7C39,0x7C3A,0x7C3B,0x7C3C,0x7C3D,0x7C3E,0x7C42,0x9AB8, 0x5B69,0x6D77,0x6C26,0x4EA5,0x5BB3,0x9A87,0x9163,0x61A8, 0x90AF,0x97E9,0x542B,0x6DB5,0x5BD2,0x51FD,0x558A,0x7F55, 0x7FF0,0x64BC,0x634D,0x65F1,0x61BE,0x608D,0x710A,0x6C57, 0x6C49,0x592F,0x676D,0x822A,0x58D5,0x568E,0x8C6A,0x6BEB, 0x90DD,0x597D,0x8017,0x53F7,0x6D69,0x5475,0x559D,0x8377, 0x83CF,0x6838,0x79BE,0x548C,0x4F55,0x5408,0x76D2,0x8C89, 0x9602,0x6CB3,0x6DB8,0x8D6B,0x8910,0x9E64,0x8D3A,0x563F, 0x9ED1,0x75D5,0x5F88,0x72E0,0x6068,0x54FC,0x4EA8,0x6A2A, 0x8861,0x6052,0x8F70,0x54C4,0x70D8,0x8679,0x9E3F,0x6D2A, 0x5B8F,0x5F18,0x7EA2,0x5589,0x4FAF,0x7334,0x543C,0x539A, 0x5019,0x540E,0x547C,0x4E4E,0x5FFD,0x745A,0x58F6,0x846B, 0x80E1,0x8774,0x72D0,0x7CCA,0x6E56,0x7C43,0x7C44,0x7C45, 0x7C46,0x7C47,0x7C48,0x7C49,0x7C4A,0x7C4B,0x7C4C,0x7C4E, 0x7C4F,0x7C50,0x7C51,0x7C52,0x7C53,0x7C54,0x7C55,0x7C56, 0x7C57,0x7C58,0x7C59,0x7C5A,0x7C5B,0x7C5C,0x7C5D,0x7C5E, 0x7C5F,0x7C60,0x7C61,0x7C62,0x7C63,0x7C64,0x7C65,0x7C66, 0x7C67,0x7C68,0x7C69,0x7C6A,0x7C6B,0x7C6C,0x7C6D,0x7C6E, 0x7C6F,0x7C70,0x7C71,0x7C72,0x7C75,0x7C76,0x7C77,0x7C78, 0x7C79,0x7C7A,0x7C7E,0x7C7F,0x7C80,0x7C81,0x7C82,0x7C83, 0x7C84,0x7C85,0x7C86,0x7C87,0xFFFD,0x7C88,0x7C8A,0x7C8B, 0x7C8C,0x7C8D,0x7C8E,0x7C8F,0x7C90,0x7C93,0x7C94,0x7C96, 0x7C99,0x7C9A,0x7C9B,0x7CA0,0x7CA1,0x7CA3,0x7CA6,0x7CA7, 0x7CA8,0x7CA9,0x7CAB,0x7CAC,0x7CAD,0x7CAF,0x7CB0,0x7CB4, 0x7CB5,0x7CB6,0x7CB7,0x7CB8,0x7CBA,0x7CBB,0x5F27,0x864E, 0x552C,0x62A4,0x4E92,0x6CAA,0x6237,0x82B1,0x54D7,0x534E, 0x733E,0x6ED1,0x753B,0x5212,0x5316,0x8BDD,0x69D0,0x5F8A, 0x6000,0x6DEE,0x574F,0x6B22,0x73AF,0x6853,0x8FD8,0x7F13, 0x6362,0x60A3,0x5524,0x75EA,0x8C62,0x7115,0x6DA3,0x5BA6, 0x5E7B,0x8352,0x614C,0x9EC4,0x78FA,0x8757,0x7C27,0x7687, 0x51F0,0x60F6,0x714C,0x6643,0x5E4C,0x604D,0x8C0E,0x7070, 0x6325,0x8F89,0x5FBD,0x6062,0x86D4,0x56DE,0x6BC1,0x6094, 0x6167,0x5349,0x60E0,0x6666,0x8D3F,0x79FD,0x4F1A,0x70E9, 0x6C47,0x8BB3,0x8BF2,0x7ED8,0x8364,0x660F,0x5A5A,0x9B42, 0x6D51,0x6DF7,0x8C41,0x6D3B,0x4F19,0x706B,0x83B7,0x6216, 0x60D1,0x970D,0x8D27,0x7978,0x51FB,0x573E,0x57FA,0x673A, 0x7578,0x7A3D,0x79EF,0x7B95,0x7CBF,0x7CC0,0x7CC2,0x7CC3, 0x7CC4,0x7CC6,0x7CC9,0x7CCB,0x7CCE,0x7CCF,0x7CD0,0x7CD1, 0x7CD2,0x7CD3,0x7CD4,0x7CD8,0x7CDA,0x7CDB,0x7CDD,0x7CDE, 0x7CE1,0x7CE2,0x7CE3,0x7CE4,0x7CE5,0x7CE6,0x7CE7,0x7CE9, 0x7CEA,0x7CEB,0x7CEC,0x7CED,0x7CEE,0x7CF0,0x7CF1,0x7CF2, 0x7CF3,0x7CF4,0x7CF5,0x7CF6,0x7CF7,0x7CF9,0x7CFA,0x7CFC, 0x7CFD,0x7CFE,0x7CFF,0x7D00,0x7D01,0x7D02,0x7D03,0x7D04, 0x7D05,0x7D06,0x7D07,0x7D08,0x7D09,0x7D0B,0x7D0C,0x7D0D, 0x7D0E,0x7D0F,0x7D10,0xFFFD,0x7D11,0x7D12,0x7D13,0x7D14, 0x7D15,0x7D16,0x7D17,0x7D18,0x7D19,0x7D1A,0x7D1B,0x7D1C, 0x7D1D,0x7D1E,0x7D1F,0x7D21,0x7D23,0x7D24,0x7D25,0x7D26, 0x7D28,0x7D29,0x7D2A,0x7D2C,0x7D2D,0x7D2E,0x7D30,0x7D31, 0x7D32,0x7D33,0x7D34,0x7D35,0x7D36,0x808C,0x9965,0x8FF9, 0x6FC0,0x8BA5,0x9E21,0x59EC,0x7EE9,0x7F09,0x5409,0x6781, 0x68D8,0x8F91,0x7C4D,0x96C6,0x53CA,0x6025,0x75BE,0x6C72, 0x5373,0x5AC9,0x7EA7,0x6324,0x51E0,0x810A,0x5DF1,0x84DF, 0x6280,0x5180,0x5B63,0x4F0E,0x796D,0x5242,0x60B8,0x6D4E, 0x5BC4,0x5BC2,0x8BA1,0x8BB0,0x65E2,0x5FCC,0x9645,0x5993, 0x7EE7,0x7EAA,0x5609,0x67B7,0x5939,0x4F73,0x5BB6,0x52A0, 0x835A,0x988A,0x8D3E,0x7532,0x94BE,0x5047,0x7A3C,0x4EF7, 0x67B6,0x9A7E,0x5AC1,0x6B7C,0x76D1,0x575A,0x5C16,0x7B3A, 0x95F4,0x714E,0x517C,0x80A9,0x8270,0x5978,0x7F04,0x8327, 0x68C0,0x67EC,0x78B1,0x7877,0x62E3,0x6361,0x7B80,0x4FED, 0x526A,0x51CF,0x8350,0x69DB,0x9274,0x8DF5,0x8D31,0x89C1, 0x952E,0x7BAD,0x4EF6,0x7D37,0x7D38,0x7D39,0x7D3A,0x7D3B, 0x7D3C,0x7D3D,0x7D3E,0x7D3F,0x7D40,0x7D41,0x7D42,0x7D43, 0x7D44,0x7D45,0x7D46,0x7D47,0x7D48,0x7D49,0x7D4A,0x7D4B, 0x7D4C,0x7D4D,0x7D4E,0x7D4F,0x7D50,0x7D51,0x7D52,0x7D53, 0x7D54,0x7D55,0x7D56,0x7D57,0x7D58,0x7D59,0x7D5A,0x7D5B, 0x7D5C,0x7D5D,0x7D5E,0x7D5F,0x7D60,0x7D61,0x7D62,0x7D63, 0x7D64,0x7D65,0x7D66,0x7D67,0x7D68,0x7D69,0x7D6A,0x7D6B, 0x7D6C,0x7D6D,0x7D6F,0x7D70,0x7D71,0x7D72,0x7D73,0x7D74, 0x7D75,0x7D76,0xFFFD,0x7D78,0x7D79,0x7D7A,0x7D7B,0x7D7C, 0x7D7D,0x7D7E,0x7D7F,0x7D80,0x7D81,0x7D82,0x7D83,0x7D84, 0x7D85,0x7D86,0x7D87,0x7D88,0x7D89,0x7D8A,0x7D8B,0x7D8C, 0x7D8D,0x7D8E,0x7D8F,0x7D90,0x7D91,0x7D92,0x7D93,0x7D94, 0x7D95,0x7D96,0x7D97,0x7D98,0x5065,0x8230,0x5251,0x996F, 0x6E10,0x6E85,0x6DA7,0x5EFA,0x50F5,0x59DC,0x5C06,0x6D46, 0x6C5F,0x7586,0x848B,0x6868,0x5956,0x8BB2,0x5320,0x9171, 0x964D,0x8549,0x6912,0x7901,0x7126,0x80F6,0x4EA4,0x90CA, 0x6D47,0x9A84,0x5A07,0x56BC,0x6405,0x94F0,0x77EB,0x4FA5, 0x811A,0x72E1,0x89D2,0x997A,0x7F34,0x7EDE,0x527F,0x6559, 0x9175,0x8F7F,0x8F83,0x53EB,0x7A96,0x63ED,0x63A5,0x7686, 0x79F8,0x8857,0x9636,0x622A,0x52AB,0x8282,0x6854,0x6770, 0x6377,0x776B,0x7AED,0x6D01,0x7ED3,0x89E3,0x59D0,0x6212, 0x85C9,0x82A5,0x754C,0x501F,0x4ECB,0x75A5,0x8BEB,0x5C4A, 0x5DFE,0x7B4B,0x65A4,0x91D1,0x4ECA,0x6D25,0x895F,0x7D27, 0x9526,0x4EC5,0x8C28,0x8FDB,0x9773,0x664B,0x7981,0x8FD1, 0x70EC,0x6D78,0x7D99,0x7D9A,0x7D9B,0x7D9C,0x7D9D,0x7D9E, 0x7D9F,0x7DA0,0x7DA1,0x7DA2,0x7DA3,0x7DA4,0x7DA5,0x7DA7, 0x7DA8,0x7DA9,0x7DAA,0x7DAB,0x7DAC,0x7DAD,0x7DAF,0x7DB0, 0x7DB1,0x7DB2,0x7DB3,0x7DB4,0x7DB5,0x7DB6,0x7DB7,0x7DB8, 0x7DB9,0x7DBA,0x7DBB,0x7DBC,0x7DBD,0x7DBE,0x7DBF,0x7DC0, 0x7DC1,0x7DC2,0x7DC3,0x7DC4,0x7DC5,0x7DC6,0x7DC7,0x7DC8, 0x7DC9,0x7DCA,0x7DCB,0x7DCC,0x7DCD,0x7DCE,0x7DCF,0x7DD0, 0x7DD1,0x7DD2,0x7DD3,0x7DD4,0x7DD5,0x7DD6,0x7DD7,0x7DD8, 0x7DD9,0xFFFD,0x7DDA,0x7DDB,0x7DDC,0x7DDD,0x7DDE,0x7DDF, 0x7DE0,0x7DE1,0x7DE2,0x7DE3,0x7DE4,0x7DE5,0x7DE6,0x7DE7, 0x7DE8,0x7DE9,0x7DEA,0x7DEB,0x7DEC,0x7DED,0x7DEE,0x7DEF, 0x7DF0,0x7DF1,0x7DF2,0x7DF3,0x7DF4,0x7DF5,0x7DF6,0x7DF7, 0x7DF8,0x7DF9,0x7DFA,0x5C3D,0x52B2,0x8346,0x5162,0x830E, 0x775B,0x6676,0x9CB8,0x4EAC,0x60CA,0x7CBE,0x7CB3,0x7ECF, 0x4E95,0x8B66,0x666F,0x9888,0x9759,0x5883,0x656C,0x955C, 0x5F84,0x75C9,0x9756,0x7ADF,0x7ADE,0x51C0,0x70AF,0x7A98, 0x63EA,0x7A76,0x7EA0,0x7396,0x97ED,0x4E45,0x7078,0x4E5D, 0x9152,0x53A9,0x6551,0x65E7,0x81FC,0x8205,0x548E,0x5C31, 0x759A,0x97A0,0x62D8,0x72D9,0x75BD,0x5C45,0x9A79,0x83CA, 0x5C40,0x5480,0x77E9,0x4E3E,0x6CAE,0x805A,0x62D2,0x636E, 0x5DE8,0x5177,0x8DDD,0x8E1E,0x952F,0x4FF1,0x53E5,0x60E7, 0x70AC,0x5267,0x6350,0x9E43,0x5A1F,0x5026,0x7737,0x5377, 0x7EE2,0x6485,0x652B,0x6289,0x6398,0x5014,0x7235,0x89C9, 0x51B3,0x8BC0,0x7EDD,0x5747,0x83CC,0x94A7,0x519B,0x541B, 0x5CFB,0x7DFB,0x7DFC,0x7DFD,0x7DFE,0x7DFF,0x7E00,0x7E01, 0x7E02,0x7E03,0x7E04,0x7E05,0x7E06,0x7E07,0x7E08,0x7E09, 0x7E0A,0x7E0B,0x7E0C,0x7E0D,0x7E0E,0x7E0F,0x7E10,0x7E11, 0x7E12,0x7E13,0x7E14,0x7E15,0x7E16,0x7E17,0x7E18,0x7E19, 0x7E1A,0x7E1B,0x7E1C,0x7E1D,0x7E1E,0x7E1F,0x7E20,0x7E21, 0x7E22,0x7E23,0x7E24,0x7E25,0x7E26,0x7E27,0x7E28,0x7E29, 0x7E2A,0x7E2B,0x7E2C,0x7E2D,0x7E2E,0x7E2F,0x7E30,0x7E31, 0x7E32,0x7E33,0x7E34,0x7E35,0x7E36,0x7E37,0x7E38,0x7E39, 0xFFFD,0x7E3A,0x7E3C,0x7E3D,0x7E3E,0x7E3F,0x7E40,0x7E42, 0x7E43,0x7E44,0x7E45,0x7E46,0x7E48,0x7E49,0x7E4A,0x7E4B, 0x7E4C,0x7E4D,0x7E4E,0x7E4F,0x7E50,0x7E51,0x7E52,0x7E53, 0x7E54,0x7E55,0x7E56,0x7E57,0x7E58,0x7E59,0x7E5A,0x7E5B, 0x7E5C,0x7E5D,0x4FCA,0x7AE3,0x6D5A,0x90E1,0x9A8F,0x5580, 0x5496,0x5361,0x54AF,0x5F00,0x63E9,0x6977,0x51EF,0x6168, 0x520A,0x582A,0x52D8,0x574E,0x780D,0x770B,0x5EB7,0x6177, 0x7CE0,0x625B,0x6297,0x4EA2,0x7095,0x8003,0x62F7,0x70E4, 0x9760,0x5777,0x82DB,0x67EF,0x68F5,0x78D5,0x9897,0x79D1, 0x58F3,0x54B3,0x53EF,0x6E34,0x514B,0x523B,0x5BA2,0x8BFE, 0x80AF,0x5543,0x57A6,0x6073,0x5751,0x542D,0x7A7A,0x6050, 0x5B54,0x63A7,0x62A0,0x53E3,0x6263,0x5BC7,0x67AF,0x54ED, 0x7A9F,0x82E6,0x9177,0x5E93,0x88E4,0x5938,0x57AE,0x630E, 0x8DE8,0x80EF,0x5757,0x7B77,0x4FA9,0x5FEB,0x5BBD,0x6B3E, 0x5321,0x7B50,0x72C2,0x6846,0x77FF,0x7736,0x65F7,0x51B5, 0x4E8F,0x76D4,0x5CBF,0x7AA5,0x8475,0x594E,0x9B41,0x5080 }; const static uint16 gbkDecoderInnerIndex4[]= { 0x7E5E,0x7E5F,0x7E60,0x7E61,0x7E62,0x7E63,0x7E64,0x7E65, 0x7E66,0x7E67,0x7E68,0x7E69,0x7E6A,0x7E6B,0x7E6C,0x7E6D, 0x7E6E,0x7E6F,0x7E70,0x7E71,0x7E72,0x7E73,0x7E74,0x7E75, 0x7E76,0x7E77,0x7E78,0x7E79,0x7E7A,0x7E7B,0x7E7C,0x7E7D, 0x7E7E,0x7E7F,0x7E80,0x7E81,0x7E83,0x7E84,0x7E85,0x7E86, 0x7E87,0x7E88,0x7E89,0x7E8A,0x7E8B,0x7E8C,0x7E8D,0x7E8E, 0x7E8F,0x7E90,0x7E91,0x7E92,0x7E93,0x7E94,0x7E95,0x7E96, 0x7E97,0x7E98,0x7E99,0x7E9A,0x7E9C,0x7E9D,0x7E9E,0xFFFD, 0x7EAE,0x7EB4,0x7EBB,0x7EBC,0x7ED6,0x7EE4,0x7EEC,0x7EF9, 0x7F0A,0x7F10,0x7F1E,0x7F37,0x7F39,0x7F3B,0x7F3C,0x7F3D, 0x7F3E,0x7F3F,0x7F40,0x7F41,0x7F43,0x7F46,0x7F47,0x7F48, 0x7F49,0x7F4A,0x7F4B,0x7F4C,0x7F4D,0x7F4E,0x7F4F,0x7F52, 0x7F53,0x9988,0x6127,0x6E83,0x5764,0x6606,0x6346,0x56F0, 0x62EC,0x6269,0x5ED3,0x9614,0x5783,0x62C9,0x5587,0x8721, 0x814A,0x8FA3,0x5566,0x83B1,0x6765,0x8D56,0x84DD,0x5A6A, 0x680F,0x62E6,0x7BEE,0x9611,0x5170,0x6F9C,0x8C30,0x63FD, 0x89C8,0x61D2,0x7F06,0x70C2,0x6EE5,0x7405,0x6994,0x72FC, 0x5ECA,0x90CE,0x6717,0x6D6A,0x635E,0x52B3,0x7262,0x8001, 0x4F6C,0x59E5,0x916A,0x70D9,0x6D9D,0x52D2,0x4E50,0x96F7, 0x956D,0x857E,0x78CA,0x7D2F,0x5121,0x5792,0x64C2,0x808B, 0x7C7B,0x6CEA,0x68F1,0x695E,0x51B7,0x5398,0x68A8,0x7281, 0x9ECE,0x7BF1,0x72F8,0x79BB,0x6F13,0x7406,0x674E,0x91CC, 0x9CA4,0x793C,0x8389,0x8354,0x540F,0x6817,0x4E3D,0x5389, 0x52B1,0x783E,0x5386,0x5229,0x5088,0x4F8B,0x4FD0,0x7F56, 0x7F59,0x7F5B,0x7F5C,0x7F5D,0x7F5E,0x7F60,0x7F63,0x7F64, 0x7F65,0x7F66,0x7F67,0x7F6B,0x7F6C,0x7F6D,0x7F6F,0x7F70, 0x7F73,0x7F75,0x7F76,0x7F77,0x7F78,0x7F7A,0x7F7B,0x7F7C, 0x7F7D,0x7F7F,0x7F80,0x7F82,0x7F83,0x7F84,0x7F85,0x7F86, 0x7F87,0x7F88,0x7F89,0x7F8B,0x7F8D,0x7F8F,0x7F90,0x7F91, 0x7F92,0x7F93,0x7F95,0x7F96,0x7F97,0x7F98,0x7F99,0x7F9B, 0x7F9C,0x7FA0,0x7FA2,0x7FA3,0x7FA5,0x7FA6,0x7FA8,0x7FA9, 0x7FAA,0x7FAB,0x7FAC,0x7FAD,0x7FAE,0x7FB1,0xFFFD,0x7FB3, 0x7FB4,0x7FB5,0x7FB6,0x7FB7,0x7FBA,0x7FBB,0x7FBE,0x7FC0, 0x7FC2,0x7FC3,0x7FC4,0x7FC6,0x7FC7,0x7FC8,0x7FC9,0x7FCB, 0x7FCD,0x7FCF,0x7FD0,0x7FD1,0x7FD2,0x7FD3,0x7FD6,0x7FD7, 0x7FD9,0x7FDA,0x7FDB,0x7FDC,0x7FDD,0x7FDE,0x7FE2,0x7FE3, 0x75E2,0x7ACB,0x7C92,0x6CA5,0x96B6,0x529B,0x7483,0x54E9, 0x4FE9,0x8054,0x83B2,0x8FDE,0x9570,0x5EC9,0x601C,0x6D9F, 0x5E18,0x655B,0x8138,0x94FE,0x604B,0x70BC,0x7EC3,0x7CAE, 0x51C9,0x6881,0x7CB1,0x826F,0x4E24,0x8F86,0x91CF,0x667E, 0x4EAE,0x8C05,0x64A9,0x804A,0x50DA,0x7597,0x71CE,0x5BE5, 0x8FBD,0x6F66,0x4E86,0x6482,0x9563,0x5ED6,0x6599,0x5217, 0x88C2,0x70C8,0x52A3,0x730E,0x7433,0x6797,0x78F7,0x9716, 0x4E34,0x90BB,0x9CDE,0x6DCB,0x51DB,0x8D41,0x541D,0x62CE, 0x73B2,0x83F1,0x96F6,0x9F84,0x94C3,0x4F36,0x7F9A,0x51CC, 0x7075,0x9675,0x5CAD,0x9886,0x53E6,0x4EE4,0x6E9C,0x7409, 0x69B4,0x786B,0x998F,0x7559,0x5218,0x7624,0x6D41,0x67F3, 0x516D,0x9F99,0x804B,0x5499,0x7B3C,0x7ABF,0x7FE4,0x7FE7, 0x7FE8,0x7FEA,0x7FEB,0x7FEC,0x7FED,0x7FEF,0x7FF2,0x7FF4, 0x7FF5,0x7FF6,0x7FF7,0x7FF8,0x7FF9,0x7FFA,0x7FFD,0x7FFE, 0x7FFF,0x8002,0x8007,0x8008,0x8009,0x800A,0x800E,0x800F, 0x8011,0x8013,0x801A,0x801B,0x801D,0x801E,0x801F,0x8021, 0x8023,0x8024,0x802B,0x802C,0x802D,0x802E,0x802F,0x8030, 0x8032,0x8034,0x8039,0x803A,0x803C,0x803E,0x8040,0x8041, 0x8044,0x8045,0x8047,0x8048,0x8049,0x804E,0x804F,0x8050, 0x8051,0x8053,0x8055,0x8056,0x8057,0xFFFD,0x8059,0x805B, 0x805C,0x805D,0x805E,0x805F,0x8060,0x8061,0x8062,0x8063, 0x8064,0x8065,0x8066,0x8067,0x8068,0x806B,0x806C,0x806D, 0x806E,0x806F,0x8070,0x8072,0x8073,0x8074,0x8075,0x8076, 0x8077,0x8078,0x8079,0x807A,0x807B,0x807C,0x807D,0x9686, 0x5784,0x62E2,0x9647,0x697C,0x5A04,0x6402,0x7BD3,0x6F0F, 0x964B,0x82A6,0x5362,0x9885,0x5E90,0x7089,0x63B3,0x5364, 0x864F,0x9C81,0x9E93,0x788C,0x9732,0x8DEF,0x8D42,0x9E7F, 0x6F5E,0x7984,0x5F55,0x9646,0x622E,0x9A74,0x5415,0x94DD, 0x4FA3,0x65C5,0x5C65,0x5C61,0x7F15,0x8651,0x6C2F,0x5F8B, 0x7387,0x6EE4,0x7EFF,0x5CE6,0x631B,0x5B6A,0x6EE6,0x5375, 0x4E71,0x63A0,0x7565,0x62A1,0x8F6E,0x4F26,0x4ED1,0x6CA6, 0x7EB6,0x8BBA,0x841D,0x87BA,0x7F57,0x903B,0x9523,0x7BA9, 0x9AA1,0x88F8,0x843D,0x6D1B,0x9A86,0x7EDC,0x5988,0x9EBB, 0x739B,0x7801,0x8682,0x9A6C,0x9A82,0x561B,0x5417,0x57CB, 0x4E70,0x9EA6,0x5356,0x8FC8,0x8109,0x7792,0x9992,0x86EE, 0x6EE1,0x8513,0x66FC,0x6162,0x6F2B,0x807E,0x8081,0x8082, 0x8085,0x8088,0x808A,0x808D,0x808E,0x808F,0x8090,0x8091, 0x8092,0x8094,0x8095,0x8097,0x8099,0x809E,0x80A3,0x80A6, 0x80A7,0x80A8,0x80AC,0x80B0,0x80B3,0x80B5,0x80B6,0x80B8, 0x80B9,0x80BB,0x80C5,0x80C7,0x80C8,0x80C9,0x80CA,0x80CB, 0x80CF,0x80D0,0x80D1,0x80D2,0x80D3,0x80D4,0x80D5,0x80D8, 0x80DF,0x80E0,0x80E2,0x80E3,0x80E6,0x80EE,0x80F5,0x80F7, 0x80F9,0x80FB,0x80FE,0x80FF,0x8100,0x8101,0x8103,0x8104, 0x8105,0x8107,0x8108,0x810B,0xFFFD,0x810C,0x8115,0x8117, 0x8119,0x811B,0x811C,0x811D,0x811F,0x8120,0x8121,0x8122, 0x8123,0x8124,0x8125,0x8126,0x8127,0x8128,0x8129,0x812A, 0x812B,0x812D,0x812E,0x8130,0x8133,0x8134,0x8135,0x8137, 0x8139,0x813A,0x813B,0x813C,0x813D,0x813F,0x8C29,0x8292, 0x832B,0x76F2,0x6C13,0x5FD9,0x83BD,0x732B,0x8305,0x951A, 0x6BDB,0x77DB,0x94C6,0x536F,0x8302,0x5192,0x5E3D,0x8C8C, 0x8D38,0x4E48,0x73AB,0x679A,0x6885,0x9176,0x9709,0x7164, 0x6CA1,0x7709,0x5A92,0x9541,0x6BCF,0x7F8E,0x6627,0x5BD0, 0x59B9,0x5A9A,0x95E8,0x95F7,0x4EEC,0x840C,0x8499,0x6AAC, 0x76DF,0x9530,0x731B,0x68A6,0x5B5F,0x772F,0x919A,0x9761, 0x7CDC,0x8FF7,0x8C1C,0x5F25,0x7C73,0x79D8,0x89C5,0x6CCC, 0x871C,0x5BC6,0x5E42,0x68C9,0x7720,0x7EF5,0x5195,0x514D, 0x52C9,0x5A29,0x7F05,0x9762,0x82D7,0x63CF,0x7784,0x85D0, 0x79D2,0x6E3A,0x5E99,0x5999,0x8511,0x706D,0x6C11,0x62BF, 0x76BF,0x654F,0x60AF,0x95FD,0x660E,0x879F,0x9E23,0x94ED, 0x540D,0x547D,0x8C2C,0x6478,0x8140,0x8141,0x8142,0x8143, 0x8144,0x8145,0x8147,0x8149,0x814D,0x814E,0x814F,0x8152, 0x8156,0x8157,0x8158,0x815B,0x815C,0x815D,0x815E,0x815F, 0x8161,0x8162,0x8163,0x8164,0x8166,0x8168,0x816A,0x816B, 0x816C,0x816F,0x8172,0x8173,0x8175,0x8176,0x8177,0x8178, 0x8181,0x8183,0x8184,0x8185,0x8186,0x8187,0x8189,0x818B, 0x818C,0x818D,0x818E,0x8190,0x8192,0x8193,0x8194,0x8195, 0x8196,0x8197,0x8199,0x819A,0x819E,0x819F,0x81A0,0x81A1, 0x81A2,0x81A4,0x81A5,0xFFFD,0x81A7,0x81A9,0x81AB,0x81AC, 0x81AD,0x81AE,0x81AF,0x81B0,0x81B1,0x81B2,0x81B4,0x81B5, 0x81B6,0x81B7,0x81B8,0x81B9,0x81BC,0x81BD,0x81BE,0x81BF, 0x81C4,0x81C5,0x81C7,0x81C8,0x81C9,0x81CB,0x81CD,0x81CE, 0x81CF,0x81D0,0x81D1,0x81D2,0x81D3,0x6479,0x8611,0x6A21, 0x819C,0x78E8,0x6469,0x9B54,0x62B9,0x672B,0x83AB,0x58A8, 0x9ED8,0x6CAB,0x6F20,0x5BDE,0x964C,0x8C0B,0x725F,0x67D0, 0x62C7,0x7261,0x4EA9,0x59C6,0x6BCD,0x5893,0x66AE,0x5E55, 0x52DF,0x6155,0x6728,0x76EE,0x7766,0x7267,0x7A46,0x62FF, 0x54EA,0x5450,0x94A0,0x90A3,0x5A1C,0x7EB3,0x6C16,0x4E43, 0x5976,0x8010,0x5948,0x5357,0x7537,0x96BE,0x56CA,0x6320, 0x8111,0x607C,0x95F9,0x6DD6,0x5462,0x9981,0x5185,0x5AE9, 0x80FD,0x59AE,0x9713,0x502A,0x6CE5,0x5C3C,0x62DF,0x4F60, 0x533F,0x817B,0x9006,0x6EBA,0x852B,0x62C8,0x5E74,0x78BE, 0x64B5,0x637B,0x5FF5,0x5A18,0x917F,0x9E1F,0x5C3F,0x634F, 0x8042,0x5B7D,0x556E,0x954A,0x954D,0x6D85,0x60A8,0x67E0, 0x72DE,0x51DD,0x5B81,0x81D4,0x81D5,0x81D6,0x81D7,0x81D8, 0x81D9,0x81DA,0x81DB,0x81DC,0x81DD,0x81DE,0x81DF,0x81E0, 0x81E1,0x81E2,0x81E4,0x81E5,0x81E6,0x81E8,0x81E9,0x81EB, 0x81EE,0x81EF,0x81F0,0x81F1,0x81F2,0x81F5,0x81F6,0x81F7, 0x81F8,0x81F9,0x81FA,0x81FD,0x81FF,0x8203,0x8207,0x8208, 0x8209,0x820A,0x820B,0x820E,0x820F,0x8211,0x8213,0x8215, 0x8216,0x8217,0x8218,0x8219,0x821A,0x821D,0x8220,0x8224, 0x8225,0x8226,0x8227,0x8229,0x822E,0x8232,0x823A,0x823C, 0x823D,0x823F,0xFFFD,0x8240,0x8241,0x8242,0x8243,0x8245, 0x8246,0x8248,0x824A,0x824C,0x824D,0x824E,0x8250,0x8251, 0x8252,0x8253,0x8254,0x8255,0x8256,0x8257,0x8259,0x825B, 0x825C,0x825D,0x825E,0x8260,0x8261,0x8262,0x8263,0x8264, 0x8265,0x8266,0x8267,0x8269,0x62E7,0x6CDE,0x725B,0x626D, 0x94AE,0x7EBD,0x8113,0x6D53,0x519C,0x5F04,0x5974,0x52AA, 0x6012,0x5973,0x6696,0x8650,0x759F,0x632A,0x61E6,0x7CEF, 0x8BFA,0x54E6,0x6B27,0x9E25,0x6BB4,0x85D5,0x5455,0x5076, 0x6CA4,0x556A,0x8DB4,0x722C,0x5E15,0x6015,0x7436,0x62CD, 0x6392,0x724C,0x5F98,0x6E43,0x6D3E,0x6500,0x6F58,0x76D8, 0x78D0,0x76FC,0x7554,0x5224,0x53DB,0x4E53,0x5E9E,0x65C1, 0x802A,0x80D6,0x629B,0x5486,0x5228,0x70AE,0x888D,0x8DD1, 0x6CE1,0x5478,0x80DA,0x57F9,0x88F4,0x8D54,0x966A,0x914D, 0x4F69,0x6C9B,0x55B7,0x76C6,0x7830,0x62A8,0x70F9,0x6F8E, 0x5F6D,0x84EC,0x68DA,0x787C,0x7BF7,0x81A8,0x670B,0x9E4F, 0x6367,0x78B0,0x576F,0x7812,0x9739,0x6279,0x62AB,0x5288, 0x7435,0x6BD7,0x826A,0x826B,0x826C,0x826D,0x8271,0x8275, 0x8276,0x8277,0x8278,0x827B,0x827C,0x8280,0x8281,0x8283, 0x8285,0x8286,0x8287,0x8289,0x828C,0x8290,0x8293,0x8294, 0x8295,0x8296,0x829A,0x829B,0x829E,0x82A0,0x82A2,0x82A3, 0x82A7,0x82B2,0x82B5,0x82B6,0x82BA,0x82BB,0x82BC,0x82BF, 0x82C0,0x82C2,0x82C3,0x82C5,0x82C6,0x82C9,0x82D0,0x82D6, 0x82D9,0x82DA,0x82DD,0x82E2,0x82E7,0x82E8,0x82E9,0x82EA, 0x82EC,0x82ED,0x82EE,0x82F0,0x82F2,0x82F3,0x82F5,0x82F6, 0x82F8,0xFFFD,0x82FA,0x82FC,0x82FD,0x82FE,0x82FF,0x8300, 0x830A,0x830B,0x830D,0x8310,0x8312,0x8313,0x8316,0x8318, 0x8319,0x831D,0x831E,0x831F,0x8320,0x8321,0x8322,0x8323, 0x8324,0x8325,0x8326,0x8329,0x832A,0x832E,0x8330,0x8332, 0x8337,0x833B,0x833D,0x5564,0x813E,0x75B2,0x76AE,0x5339, 0x75DE,0x50FB,0x5C41,0x8B6C,0x7BC7,0x504F,0x7247,0x9A97, 0x98D8,0x6F02,0x74E2,0x7968,0x6487,0x77A5,0x62FC,0x9891, 0x8D2B,0x54C1,0x8058,0x4E52,0x576A,0x82F9,0x840D,0x5E73, 0x51ED,0x74F6,0x8BC4,0x5C4F,0x5761,0x6CFC,0x9887,0x5A46, 0x7834,0x9B44,0x8FEB,0x7C95,0x5256,0x6251,0x94FA,0x4EC6, 0x8386,0x8461,0x83E9,0x84B2,0x57D4,0x6734,0x5703,0x666E, 0x6D66,0x8C31,0x66DD,0x7011,0x671F,0x6B3A,0x6816,0x621A, 0x59BB,0x4E03,0x51C4,0x6F06,0x67D2,0x6C8F,0x5176,0x68CB, 0x5947,0x6B67,0x7566,0x5D0E,0x8110,0x9F50,0x65D7,0x7948, 0x7941,0x9A91,0x8D77,0x5C82,0x4E5E,0x4F01,0x542F,0x5951, 0x780C,0x5668,0x6C14,0x8FC4,0x5F03,0x6C7D,0x6CE3,0x8BAB, 0x6390,0x833E,0x833F,0x8341,0x8342,0x8344,0x8345,0x8348, 0x834A,0x834B,0x834C,0x834D,0x834E,0x8353,0x8355,0x8356, 0x8357,0x8358,0x8359,0x835D,0x8362,0x8370,0x8371,0x8372, 0x8373,0x8374,0x8375,0x8376,0x8379,0x837A,0x837E,0x837F, 0x8380,0x8381,0x8382,0x8383,0x8384,0x8387,0x8388,0x838A, 0x838B,0x838C,0x838D,0x838F,0x8390,0x8391,0x8394,0x8395, 0x8396,0x8397,0x8399,0x839A,0x839D,0x839F,0x83A1,0x83A2, 0x83A3,0x83A4,0x83A5,0x83A6,0x83A7,0x83AC,0x83AD,0x83AE, 0xFFFD,0x83AF,0x83B5,0x83BB,0x83BE,0x83BF,0x83C2,0x83C3, 0x83C4,0x83C6,0x83C8,0x83C9,0x83CB,0x83CD,0x83CE,0x83D0, 0x83D1,0x83D2,0x83D3,0x83D5,0x83D7,0x83D9,0x83DA,0x83DB, 0x83DE,0x83E2,0x83E3,0x83E4,0x83E6,0x83E7,0x83E8,0x83EB, 0x83EC,0x83ED,0x6070,0x6D3D,0x7275,0x6266,0x948E,0x94C5, 0x5343,0x8FC1,0x7B7E,0x4EDF,0x8C26,0x4E7E,0x9ED4,0x94B1, 0x94B3,0x524D,0x6F5C,0x9063,0x6D45,0x8C34,0x5811,0x5D4C, 0x6B20,0x6B49,0x67AA,0x545B,0x8154,0x7F8C,0x5899,0x8537, 0x5F3A,0x62A2,0x6A47,0x9539,0x6572,0x6084,0x6865,0x77A7, 0x4E54,0x4FA8,0x5DE7,0x9798,0x64AC,0x7FD8,0x5CED,0x4FCF, 0x7A8D,0x5207,0x8304,0x4E14,0x602F,0x7A83,0x94A6,0x4FB5, 0x4EB2,0x79E6,0x7434,0x52E4,0x82B9,0x64D2,0x79BD,0x5BDD, 0x6C81,0x9752,0x8F7B,0x6C22,0x503E,0x537F,0x6E05,0x64CE, 0x6674,0x6C30,0x60C5,0x9877,0x8BF7,0x5E86,0x743C,0x7A77, 0x79CB,0x4E18,0x90B1,0x7403,0x6C42,0x56DA,0x914B,0x6CC5, 0x8D8B,0x533A,0x86C6,0x66F2,0x8EAF,0x5C48,0x9A71,0x6E20, 0x83EE,0x83EF,0x83F3,0x83F4,0x83F5,0x83F6,0x83F7,0x83FA, 0x83FB,0x83FC,0x83FE,0x83FF,0x8400,0x8402,0x8405,0x8407, 0x8408,0x8409,0x840A,0x8410,0x8412,0x8413,0x8414,0x8415, 0x8416,0x8417,0x8419,0x841A,0x841B,0x841E,0x841F,0x8420, 0x8421,0x8422,0x8423,0x8429,0x842A,0x842B,0x842C,0x842D, 0x842E,0x842F,0x8430,0x8432,0x8433,0x8434,0x8435,0x8436, 0x8437,0x8439,0x843A,0x843B,0x843E,0x843F,0x8440,0x8441, 0x8442,0x8443,0x8444,0x8445,0x8447,0x8448,0x8449,0xFFFD, 0x844A,0x844B,0x844C,0x844D,0x844E,0x844F,0x8450,0x8452, 0x8453,0x8454,0x8455,0x8456,0x8458,0x845D,0x845E,0x845F, 0x8460,0x8462,0x8464,0x8465,0x8466,0x8467,0x8468,0x846A, 0x846E,0x846F,0x8470,0x8472,0x8474,0x8477,0x8479,0x847B, 0x847C,0x53D6,0x5A36,0x9F8B,0x8DA3,0x53BB,0x5708,0x98A7, 0x6743,0x919B,0x6CC9,0x5168,0x75CA,0x62F3,0x72AC,0x5238, 0x529D,0x7F3A,0x7094,0x7638,0x5374,0x9E4A,0x69B7,0x786E, 0x96C0,0x88D9,0x7FA4,0x7136,0x71C3,0x5189,0x67D3,0x74E4, 0x58E4,0x6518,0x56B7,0x8BA9,0x9976,0x6270,0x7ED5,0x60F9, 0x70ED,0x58EC,0x4EC1,0x4EBA,0x5FCD,0x97E7,0x4EFB,0x8BA4, 0x5203,0x598A,0x7EAB,0x6254,0x4ECD,0x65E5,0x620E,0x8338, 0x84C9,0x8363,0x878D,0x7194,0x6EB6,0x5BB9,0x7ED2,0x5197, 0x63C9,0x67D4,0x8089,0x8339,0x8815,0x5112,0x5B7A,0x5982, 0x8FB1,0x4E73,0x6C5D,0x5165,0x8925,0x8F6F,0x962E,0x854A, 0x745E,0x9510,0x95F0,0x6DA6,0x82E5,0x5F31,0x6492,0x6D12, 0x8428,0x816E,0x9CC3,0x585E,0x8D5B,0x4E09,0x53C1,0x847D, 0x847E,0x847F,0x8480,0x8481,0x8483,0x8484,0x8485,0x8486, 0x848A,0x848D,0x848F,0x8490,0x8491,0x8492,0x8493,0x8494, 0x8495,0x8496,0x8498,0x849A,0x849B,0x849D,0x849E,0x849F, 0x84A0,0x84A2,0x84A3,0x84A4,0x84A5,0x84A6,0x84A7,0x84A8, 0x84A9,0x84AA,0x84AB,0x84AC,0x84AD,0x84AE,0x84B0,0x84B1, 0x84B3,0x84B5,0x84B6,0x84B7,0x84BB,0x84BC,0x84BE,0x84C0, 0x84C2,0x84C3,0x84C5,0x84C6,0x84C7,0x84C8,0x84CB,0x84CC, 0x84CE,0x84CF,0x84D2,0x84D4,0x84D5,0x84D7,0xFFFD,0x84D8, 0x84D9,0x84DA,0x84DB,0x84DC,0x84DE,0x84E1,0x84E2,0x84E4, 0x84E7,0x84E8,0x84E9,0x84EA,0x84EB,0x84ED,0x84EE,0x84EF, 0x84F1,0x84F2,0x84F3,0x84F4,0x84F5,0x84F6,0x84F7,0x84F8, 0x84F9,0x84FA,0x84FB,0x84FD,0x84FE,0x8500,0x8501,0x8502, 0x4F1E,0x6563,0x6851,0x55D3,0x4E27,0x6414,0x9A9A,0x626B, 0x5AC2,0x745F,0x8272,0x6DA9,0x68EE,0x50E7,0x838E,0x7802, 0x6740,0x5239,0x6C99,0x7EB1,0x50BB,0x5565,0x715E,0x7B5B, 0x6652,0x73CA,0x82EB,0x6749,0x5C71,0x5220,0x717D,0x886B, 0x95EA,0x9655,0x64C5,0x8D61,0x81B3,0x5584,0x6C55,0x6247, 0x7F2E,0x5892,0x4F24,0x5546,0x8D4F,0x664C,0x4E0A,0x5C1A, 0x88F3,0x68A2,0x634E,0x7A0D,0x70E7,0x828D,0x52FA,0x97F6, 0x5C11,0x54E8,0x90B5,0x7ECD,0x5962,0x8D4A,0x86C7,0x820C, 0x820D,0x8D66,0x6444,0x5C04,0x6151,0x6D89,0x793E,0x8BBE, 0x7837,0x7533,0x547B,0x4F38,0x8EAB,0x6DF1,0x5A20,0x7EC5, 0x795E,0x6C88,0x5BA1,0x5A76,0x751A,0x80BE,0x614E,0x6E17, 0x58F0,0x751F,0x7525,0x7272,0x5347,0x7EF3,0x8503,0x8504, 0x8505,0x8506,0x8507,0x8508,0x8509,0x850A,0x850B,0x850D, 0x850E,0x850F,0x8510,0x8512,0x8514,0x8515,0x8516,0x8518, 0x8519,0x851B,0x851C,0x851D,0x851E,0x8520,0x8522,0x8523, 0x8524,0x8525,0x8526,0x8527,0x8528,0x8529,0x852A,0x852D, 0x852E,0x852F,0x8530,0x8531,0x8532,0x8533,0x8534,0x8535, 0x8536,0x853E,0x853F,0x8540,0x8541,0x8542,0x8544,0x8545, 0x8546,0x8547,0x854B,0x854C,0x854D,0x854E,0x854F,0x8550, 0x8551,0x8552,0x8553,0x8554,0x8555,0xFFFD,0x8557,0x8558, 0x855A,0x855B,0x855C,0x855D,0x855F,0x8560,0x8561,0x8562, 0x8563,0x8565,0x8566,0x8567,0x8569,0x856A,0x856B,0x856C, 0x856D,0x856E,0x856F,0x8570,0x8571,0x8573,0x8575,0x8576, 0x8577,0x8578,0x857C,0x857D,0x857F,0x8580,0x8581,0x7701, 0x76DB,0x5269,0x80DC,0x5723,0x5E08,0x5931,0x72EE,0x65BD, 0x6E7F,0x8BD7,0x5C38,0x8671,0x5341,0x77F3,0x62FE,0x65F6, 0x4EC0,0x98DF,0x8680,0x5B9E,0x8BC6,0x53F2,0x77E2,0x4F7F, 0x5C4E,0x9A76,0x59CB,0x5F0F,0x793A,0x58EB,0x4E16,0x67FF, 0x4E8B,0x62ED,0x8A93,0x901D,0x52BF,0x662F,0x55DC,0x566C, 0x9002,0x4ED5,0x4F8D,0x91CA,0x9970,0x6C0F,0x5E02,0x6043, 0x5BA4,0x89C6,0x8BD5,0x6536,0x624B,0x9996,0x5B88,0x5BFF, 0x6388,0x552E,0x53D7,0x7626,0x517D,0x852C,0x67A2,0x68B3, 0x6B8A,0x6292,0x8F93,0x53D4,0x8212,0x6DD1,0x758F,0x4E66, 0x8D4E,0x5B70,0x719F,0x85AF,0x6691,0x66D9,0x7F72,0x8700, 0x9ECD,0x9F20,0x5C5E,0x672F,0x8FF0,0x6811,0x675F,0x620D, 0x7AD6,0x5885,0x5EB6,0x6570,0x6F31,0x8582,0x8583,0x8586, 0x8588,0x8589,0x858A,0x858B,0x858C,0x858D,0x858E,0x8590, 0x8591,0x8592,0x8593,0x8594,0x8595,0x8596,0x8597,0x8598, 0x8599,0x859A,0x859D,0x859E,0x859F,0x85A0,0x85A1,0x85A2, 0x85A3,0x85A5,0x85A6,0x85A7,0x85A9,0x85AB,0x85AC,0x85AD, 0x85B1,0x85B2,0x85B3,0x85B4,0x85B5,0x85B6,0x85B8,0x85BA, 0x85BB,0x85BC,0x85BD,0x85BE,0x85BF,0x85C0,0x85C2,0x85C3, 0x85C4,0x85C5,0x85C6,0x85C7,0x85C8,0x85CA,0x85CB,0x85CC, 0x85CD,0x85CE,0x85D1,0x85D2,0xFFFD,0x85D4,0x85D6,0x85D7, 0x85D8,0x85D9,0x85DA,0x85DB,0x85DD,0x85DE,0x85DF,0x85E0, 0x85E1,0x85E2,0x85E3,0x85E5,0x85E6,0x85E7,0x85E8,0x85EA, 0x85EB,0x85EC,0x85ED,0x85EE,0x85EF,0x85F0,0x85F1,0x85F2, 0x85F3,0x85F4,0x85F5,0x85F6,0x85F7,0x85F8,0x6055,0x5237, 0x800D,0x6454,0x8870,0x7529,0x5E05,0x6813,0x62F4,0x971C, 0x53CC,0x723D,0x8C01,0x6C34,0x7761,0x7A0E,0x542E,0x77AC, 0x987A,0x821C,0x8BF4,0x7855,0x6714,0x70C1,0x65AF,0x6495, 0x5636,0x601D,0x79C1,0x53F8,0x4E1D,0x6B7B,0x8086,0x5BFA, 0x55E3,0x56DB,0x4F3A,0x4F3C,0x9972,0x5DF3,0x677E,0x8038, 0x6002,0x9882,0x9001,0x5B8B,0x8BBC,0x8BF5,0x641C,0x8258, 0x64DE,0x55FD,0x82CF,0x9165,0x4FD7,0x7D20,0x901F,0x7C9F, 0x50F3,0x5851,0x6EAF,0x5BBF,0x8BC9,0x8083,0x9178,0x849C, 0x7B97,0x867D,0x968B,0x968F,0x7EE5,0x9AD3,0x788E,0x5C81, 0x7A57,0x9042,0x96A7,0x795F,0x5B59,0x635F,0x7B0B,0x84D1, 0x68AD,0x5506,0x7F29,0x7410,0x7D22,0x9501,0x6240,0x584C, 0x4ED6,0x5B83,0x5979,0x5854,0x85F9,0x85FA,0x85FC,0x85FD, 0x85FE,0x8600,0x8601,0x8602,0x8603,0x8604,0x8606,0x8607, 0x8608,0x8609,0x860A,0x860B,0x860C,0x860D,0x860E,0x860F, 0x8610,0x8612,0x8613,0x8614,0x8615,0x8617,0x8618,0x8619, 0x861A,0x861B,0x861C,0x861D,0x861E,0x861F,0x8620,0x8621, 0x8622,0x8623,0x8624,0x8625,0x8626,0x8628,0x862A,0x862B, 0x862C,0x862D,0x862E,0x862F,0x8630,0x8631,0x8632,0x8633, 0x8634,0x8635,0x8636,0x8637,0x8639,0x863A,0x863B,0x863D, 0x863E,0x863F,0x8640,0xFFFD,0x8641,0x8642,0x8643,0x8644, 0x8645,0x8646,0x8647,0x8648,0x8649,0x864A,0x864B,0x864C, 0x8652,0x8653,0x8655,0x8656,0x8657,0x8658,0x8659,0x865B, 0x865C,0x865D,0x865F,0x8660,0x8661,0x8663,0x8664,0x8665, 0x8666,0x8667,0x8668,0x8669,0x866A,0x736D,0x631E,0x8E4B, 0x8E0F,0x80CE,0x82D4,0x62AC,0x53F0,0x6CF0,0x915E,0x592A, 0x6001,0x6C70,0x574D,0x644A,0x8D2A,0x762B,0x6EE9,0x575B, 0x6A80,0x75F0,0x6F6D,0x8C2D,0x8C08,0x5766,0x6BEF,0x8892, 0x78B3,0x63A2,0x53F9,0x70AD,0x6C64,0x5858,0x642A,0x5802, 0x68E0,0x819B,0x5510,0x7CD6,0x5018,0x8EBA,0x6DCC,0x8D9F, 0x70EB,0x638F,0x6D9B,0x6ED4,0x7EE6,0x8404,0x6843,0x9003, 0x6DD8,0x9676,0x8BA8,0x5957,0x7279,0x85E4,0x817E,0x75BC, 0x8A8A,0x68AF,0x5254,0x8E22,0x9511,0x63D0,0x9898,0x8E44, 0x557C,0x4F53,0x66FF,0x568F,0x60D5,0x6D95,0x5243,0x5C49, 0x5929,0x6DFB,0x586B,0x7530,0x751C,0x606C,0x8214,0x8146, 0x6311,0x6761,0x8FE2,0x773A,0x8DF3,0x8D34,0x94C1,0x5E16, 0x5385,0x542C,0x70C3,0x866D,0x866F,0x8670,0x8672,0x8673, 0x8674,0x8675,0x8676,0x8677,0x8678,0x8683,0x8684,0x8685, 0x8686,0x8687,0x8688,0x8689,0x868E,0x868F,0x8690,0x8691, 0x8692,0x8694,0x8696,0x8697,0x8698,0x8699,0x869A,0x869B, 0x869E,0x869F,0x86A0,0x86A1,0x86A2,0x86A5,0x86A6,0x86AB, 0x86AD,0x86AE,0x86B2,0x86B3,0x86B7,0x86B8,0x86B9,0x86BB, 0x86BC,0x86BD,0x86BE,0x86BF,0x86C1,0x86C2,0x86C3,0x86C5, 0x86C8,0x86CC,0x86CD,0x86D2,0x86D3,0x86D5,0x86D6,0x86D7, 0x86DA,0x86DC,0xFFFD,0x86DD,0x86E0,0x86E1,0x86E2,0x86E3, 0x86E5,0x86E6,0x86E7,0x86E8,0x86EA,0x86EB,0x86EC,0x86EF, 0x86F5,0x86F6,0x86F7,0x86FA,0x86FB,0x86FC,0x86FD,0x86FF, 0x8701,0x8704,0x8705,0x8706,0x870B,0x870C,0x870E,0x870F, 0x8710,0x8711,0x8714,0x8716,0x6C40,0x5EF7,0x505C,0x4EAD, 0x5EAD,0x633A,0x8247,0x901A,0x6850,0x916E,0x77B3,0x540C, 0x94DC,0x5F64,0x7AE5,0x6876,0x6345,0x7B52,0x7EDF,0x75DB, 0x5077,0x6295,0x5934,0x900F,0x51F8,0x79C3,0x7A81,0x56FE, 0x5F92,0x9014,0x6D82,0x5C60,0x571F,0x5410,0x5154,0x6E4D, 0x56E2,0x63A8,0x9893,0x817F,0x8715,0x892A,0x9000,0x541E, 0x5C6F,0x81C0,0x62D6,0x6258,0x8131,0x9E35,0x9640,0x9A6E, 0x9A7C,0x692D,0x59A5,0x62D3,0x553E,0x6316,0x54C7,0x86D9, 0x6D3C,0x5A03,0x74E6,0x889C,0x6B6A,0x5916,0x8C4C,0x5F2F, 0x6E7E,0x73A9,0x987D,0x4E38,0x70F7,0x5B8C,0x7897,0x633D, 0x665A,0x7696,0x60CB,0x5B9B,0x5A49,0x4E07,0x8155,0x6C6A, 0x738B,0x4EA1,0x6789,0x7F51,0x5F80,0x65FA,0x671B,0x5FD8, 0x5984,0x5A01,0x8719,0x871B,0x871D,0x871F,0x8720,0x8724, 0x8726,0x8727,0x8728,0x872A,0x872B,0x872C,0x872D,0x872F, 0x8730,0x8732,0x8733,0x8735,0x8736,0x8738,0x8739,0x873A, 0x873C,0x873D,0x8740,0x8741,0x8742,0x8743,0x8744,0x8745, 0x8746,0x874A,0x874B,0x874D,0x874F,0x8750,0x8751,0x8752, 0x8754,0x8755,0x8756,0x8758,0x875A,0x875B,0x875C,0x875D, 0x875E,0x875F,0x8761,0x8762,0x8766,0x8767,0x8768,0x8769, 0x876A,0x876B,0x876C,0x876D,0x876F,0x8771,0x8772,0x8773, 0x8775,0xFFFD,0x8777,0x8778,0x8779,0x877A,0x877F,0x8780, 0x8781,0x8784,0x8786,0x8787,0x8789,0x878A,0x878C,0x878E, 0x878F,0x8790,0x8791,0x8792,0x8794,0x8795,0x8796,0x8798, 0x8799,0x879A,0x879B,0x879C,0x879D,0x879E,0x87A0,0x87A1, 0x87A2,0x87A3,0x87A4,0x5DCD,0x5FAE,0x5371,0x97E6,0x8FDD, 0x6845,0x56F4,0x552F,0x60DF,0x4E3A,0x6F4D,0x7EF4,0x82C7, 0x840E,0x59D4,0x4F1F,0x4F2A,0x5C3E,0x7EAC,0x672A,0x851A, 0x5473,0x754F,0x80C3,0x5582,0x9B4F,0x4F4D,0x6E2D,0x8C13, 0x5C09,0x6170,0x536B,0x761F,0x6E29,0x868A,0x6587,0x95FB, 0x7EB9,0x543B,0x7A33,0x7D0A,0x95EE,0x55E1,0x7FC1,0x74EE, 0x631D,0x8717,0x6DA1,0x7A9D,0x6211,0x65A1,0x5367,0x63E1, 0x6C83,0x5DEB,0x545C,0x94A8,0x4E4C,0x6C61,0x8BEC,0x5C4B, 0x65E0,0x829C,0x68A7,0x543E,0x5434,0x6BCB,0x6B66,0x4E94, 0x6342,0x5348,0x821E,0x4F0D,0x4FAE,0x575E,0x620A,0x96FE, 0x6664,0x7269,0x52FF,0x52A1,0x609F,0x8BEF,0x6614,0x7199, 0x6790,0x897F,0x7852,0x77FD,0x6670,0x563B,0x5438,0x9521, 0x727A,0x87A5,0x87A6,0x87A7,0x87A9,0x87AA,0x87AE,0x87B0, 0x87B1,0x87B2,0x87B4,0x87B6,0x87B7,0x87B8,0x87B9,0x87BB, 0x87BC,0x87BE,0x87BF,0x87C1,0x87C2,0x87C3,0x87C4,0x87C5, 0x87C7,0x87C8,0x87C9,0x87CC,0x87CD,0x87CE,0x87CF,0x87D0, 0x87D4,0x87D5,0x87D6,0x87D7,0x87D8,0x87D9,0x87DA,0x87DC, 0x87DD,0x87DE,0x87DF,0x87E1,0x87E2,0x87E3,0x87E4,0x87E6, 0x87E7,0x87E8,0x87E9,0x87EB,0x87EC,0x87ED,0x87EF,0x87F0, 0x87F1,0x87F2,0x87F3,0x87F4,0x87F5,0x87F6,0x87F7,0x87F8, 0xFFFD,0x87FA,0x87FB,0x87FC,0x87FD,0x87FF,0x8800,0x8801, 0x8802,0x8804,0x8805,0x8806,0x8807,0x8808,0x8809,0x880B, 0x880C,0x880D,0x880E,0x880F,0x8810,0x8811,0x8812,0x8814, 0x8817,0x8818,0x8819,0x881A,0x881C,0x881D,0x881E,0x881F, 0x8820,0x8823,0x7A00,0x606F,0x5E0C,0x6089,0x819D,0x5915, 0x60DC,0x7184,0x70EF,0x6EAA,0x6C50,0x7280,0x6A84,0x88AD, 0x5E2D,0x4E60,0x5AB3,0x559C,0x94E3,0x6D17,0x7CFB,0x9699, 0x620F,0x7EC6,0x778E,0x867E,0x5323,0x971E,0x8F96,0x6687, 0x5CE1,0x4FA0,0x72ED,0x4E0B,0x53A6,0x590F,0x5413,0x6380, 0x9528,0x5148,0x4ED9,0x9C9C,0x7EA4,0x54B8,0x8D24,0x8854, 0x8237,0x95F2,0x6D8E,0x5F26,0x5ACC,0x663E,0x9669,0x73B0, 0x732E,0x53BF,0x817A,0x9985,0x7FA1,0x5BAA,0x9677,0x9650, 0x7EBF,0x76F8,0x53A2,0x9576,0x9999,0x7BB1,0x8944,0x6E58, 0x4E61,0x7FD4,0x7965,0x8BE6,0x60F3,0x54CD,0x4EAB,0x9879, 0x5DF7,0x6A61,0x50CF,0x5411,0x8C61,0x8427,0x785D,0x9704, 0x524A,0x54EE,0x56A3,0x9500,0x6D88,0x5BB5,0x6DC6,0x6653 }; const static uint16 gbkDecoderInnerIndex5[]= { 0x8824,0x8825,0x8826,0x8827,0x8828,0x8829,0x882A,0x882B, 0x882C,0x882D,0x882E,0x882F,0x8830,0x8831,0x8833,0x8834, 0x8835,0x8836,0x8837,0x8838,0x883A,0x883B,0x883D,0x883E, 0x883F,0x8841,0x8842,0x8843,0x8846,0x8847,0x8848,0x8849, 0x884A,0x884B,0x884E,0x884F,0x8850,0x8851,0x8852,0x8853, 0x8855,0x8856,0x8858,0x885A,0x885B,0x885C,0x885D,0x885E, 0x885F,0x8860,0x8866,0x8867,0x886A,0x886D,0x886F,0x8871, 0x8873,0x8874,0x8875,0x8876,0x8878,0x8879,0x887A,0xFFFD, 0x887B,0x887C,0x8880,0x8883,0x8886,0x8887,0x8889,0x888A, 0x888C,0x888E,0x888F,0x8890,0x8891,0x8893,0x8894,0x8895, 0x8897,0x8898,0x8899,0x889A,0x889B,0x889D,0x889E,0x889F, 0x88A0,0x88A1,0x88A3,0x88A5,0x88A6,0x88A7,0x88A8,0x88A9, 0x88AA,0x5C0F,0x5B5D,0x6821,0x8096,0x5578,0x7B11,0x6548, 0x6954,0x4E9B,0x6B47,0x874E,0x978B,0x534F,0x631F,0x643A, 0x90AA,0x659C,0x80C1,0x8C10,0x5199,0x68B0,0x5378,0x87F9, 0x61C8,0x6CC4,0x6CFB,0x8C22,0x5C51,0x85AA,0x82AF,0x950C, 0x6B23,0x8F9B,0x65B0,0x5FFB,0x5FC3,0x4FE1,0x8845,0x661F, 0x8165,0x7329,0x60FA,0x5174,0x5211,0x578B,0x5F62,0x90A2, 0x884C,0x9192,0x5E78,0x674F,0x6027,0x59D3,0x5144,0x51F6, 0x80F8,0x5308,0x6C79,0x96C4,0x718A,0x4F11,0x4FEE,0x7F9E, 0x673D,0x55C5,0x9508,0x79C0,0x8896,0x7EE3,0x589F,0x620C, 0x9700,0x865A,0x5618,0x987B,0x5F90,0x8BB8,0x84C4,0x9157, 0x53D9,0x65ED,0x5E8F,0x755C,0x6064,0x7D6E,0x5A7F,0x7EEA, 0x7EED,0x8F69,0x55A7,0x5BA3,0x60AC,0x65CB,0x7384,0x88AC, 0x88AE,0x88AF,0x88B0,0x88B2,0x88B3,0x88B4,0x88B5,0x88B6, 0x88B8,0x88B9,0x88BA,0x88BB,0x88BD,0x88BE,0x88BF,0x88C0, 0x88C3,0x88C4,0x88C7,0x88C8,0x88CA,0x88CB,0x88CC,0x88CD, 0x88CF,0x88D0,0x88D1,0x88D3,0x88D6,0x88D7,0x88DA,0x88DB, 0x88DC,0x88DD,0x88DE,0x88E0,0x88E1,0x88E6,0x88E7,0x88E9, 0x88EA,0x88EB,0x88EC,0x88ED,0x88EE,0x88EF,0x88F2,0x88F5, 0x88F6,0x88F7,0x88FA,0x88FB,0x88FD,0x88FF,0x8900,0x8901, 0x8903,0x8904,0x8905,0x8906,0x8907,0x8908,0xFFFD,0x8909, 0x890B,0x890C,0x890D,0x890E,0x890F,0x8911,0x8914,0x8915, 0x8916,0x8917,0x8918,0x891C,0x891D,0x891E,0x891F,0x8920, 0x8922,0x8923,0x8924,0x8926,0x8927,0x8928,0x8929,0x892C, 0x892D,0x892E,0x892F,0x8931,0x8932,0x8933,0x8935,0x8937, 0x9009,0x7663,0x7729,0x7EDA,0x9774,0x859B,0x5B66,0x7A74, 0x96EA,0x8840,0x52CB,0x718F,0x5FAA,0x65EC,0x8BE2,0x5BFB, 0x9A6F,0x5DE1,0x6B89,0x6C5B,0x8BAD,0x8BAF,0x900A,0x8FC5, 0x538B,0x62BC,0x9E26,0x9E2D,0x5440,0x4E2B,0x82BD,0x7259, 0x869C,0x5D16,0x8859,0x6DAF,0x96C5,0x54D1,0x4E9A,0x8BB6, 0x7109,0x54BD,0x9609,0x70DF,0x6DF9,0x76D0,0x4E25,0x7814, 0x8712,0x5CA9,0x5EF6,0x8A00,0x989C,0x960E,0x708E,0x6CBF, 0x5944,0x63A9,0x773C,0x884D,0x6F14,0x8273,0x5830,0x71D5, 0x538C,0x781A,0x96C1,0x5501,0x5F66,0x7130,0x5BB4,0x8C1A, 0x9A8C,0x6B83,0x592E,0x9E2F,0x79E7,0x6768,0x626C,0x4F6F, 0x75A1,0x7F8A,0x6D0B,0x9633,0x6C27,0x4EF0,0x75D2,0x517B, 0x6837,0x6F3E,0x9080,0x8170,0x5996,0x7476,0x8938,0x8939, 0x893A,0x893B,0x893C,0x893D,0x893E,0x893F,0x8940,0x8942, 0x8943,0x8945,0x8946,0x8947,0x8948,0x8949,0x894A,0x894B, 0x894C,0x894D,0x894E,0x894F,0x8950,0x8951,0x8952,0x8953, 0x8954,0x8955,0x8956,0x8957,0x8958,0x8959,0x895A,0x895B, 0x895C,0x895D,0x8960,0x8961,0x8962,0x8963,0x8964,0x8965, 0x8967,0x8968,0x8969,0x896A,0x896B,0x896C,0x896D,0x896E, 0x896F,0x8970,0x8971,0x8972,0x8973,0x8974,0x8975,0x8976, 0x8977,0x8978,0x8979,0x897A,0x897C,0xFFFD,0x897D,0x897E, 0x8980,0x8982,0x8984,0x8985,0x8987,0x8988,0x8989,0x898A, 0x898B,0x898C,0x898D,0x898E,0x898F,0x8990,0x8991,0x8992, 0x8993,0x8994,0x8995,0x8996,0x8997,0x8998,0x8999,0x899A, 0x899B,0x899C,0x899D,0x899E,0x899F,0x89A0,0x89A1,0x6447, 0x5C27,0x9065,0x7A91,0x8C23,0x59DA,0x54AC,0x8200,0x836F, 0x8981,0x8000,0x6930,0x564E,0x8036,0x7237,0x91CE,0x51B6, 0x4E5F,0x9875,0x6396,0x4E1A,0x53F6,0x66F3,0x814B,0x591C, 0x6DB2,0x4E00,0x58F9,0x533B,0x63D6,0x94F1,0x4F9D,0x4F0A, 0x8863,0x9890,0x5937,0x9057,0x79FB,0x4EEA,0x80F0,0x7591, 0x6C82,0x5B9C,0x59E8,0x5F5D,0x6905,0x8681,0x501A,0x5DF2, 0x4E59,0x77E3,0x4EE5,0x827A,0x6291,0x6613,0x9091,0x5C79, 0x4EBF,0x5F79,0x81C6,0x9038,0x8084,0x75AB,0x4EA6,0x88D4, 0x610F,0x6BC5,0x5FC6,0x4E49,0x76CA,0x6EA2,0x8BE3,0x8BAE, 0x8C0A,0x8BD1,0x5F02,0x7FFC,0x7FCC,0x7ECE,0x8335,0x836B, 0x56E0,0x6BB7,0x97F3,0x9634,0x59FB,0x541F,0x94F6,0x6DEB, 0x5BC5,0x996E,0x5C39,0x5F15,0x9690,0x89A2,0x89A3,0x89A4, 0x89A5,0x89A6,0x89A7,0x89A8,0x89A9,0x89AA,0x89AB,0x89AC, 0x89AD,0x89AE,0x89AF,0x89B0,0x89B1,0x89B2,0x89B3,0x89B4, 0x89B5,0x89B6,0x89B7,0x89B8,0x89B9,0x89BA,0x89BB,0x89BC, 0x89BD,0x89BE,0x89BF,0x89C0,0x89C3,0x89CD,0x89D3,0x89D4, 0x89D5,0x89D7,0x89D8,0x89D9,0x89DB,0x89DD,0x89DF,0x89E0, 0x89E1,0x89E2,0x89E4,0x89E7,0x89E8,0x89E9,0x89EA,0x89EC, 0x89ED,0x89EE,0x89F0,0x89F1,0x89F2,0x89F4,0x89F5,0x89F6, 0x89F7,0x89F8,0x89F9,0x89FA,0xFFFD,0x89FB,0x89FC,0x89FD, 0x89FE,0x89FF,0x8A01,0x8A02,0x8A03,0x8A04,0x8A05,0x8A06, 0x8A08,0x8A09,0x8A0A,0x8A0B,0x8A0C,0x8A0D,0x8A0E,0x8A0F, 0x8A10,0x8A11,0x8A12,0x8A13,0x8A14,0x8A15,0x8A16,0x8A17, 0x8A18,0x8A19,0x8A1A,0x8A1B,0x8A1C,0x8A1D,0x5370,0x82F1, 0x6A31,0x5A74,0x9E70,0x5E94,0x7F28,0x83B9,0x8424,0x8425, 0x8367,0x8747,0x8FCE,0x8D62,0x76C8,0x5F71,0x9896,0x786C, 0x6620,0x54DF,0x62E5,0x4F63,0x81C3,0x75C8,0x5EB8,0x96CD, 0x8E0A,0x86F9,0x548F,0x6CF3,0x6D8C,0x6C38,0x607F,0x52C7, 0x7528,0x5E7D,0x4F18,0x60A0,0x5FE7,0x5C24,0x7531,0x90AE, 0x94C0,0x72B9,0x6CB9,0x6E38,0x9149,0x6709,0x53CB,0x53F3, 0x4F51,0x91C9,0x8BF1,0x53C8,0x5E7C,0x8FC2,0x6DE4,0x4E8E, 0x76C2,0x6986,0x865E,0x611A,0x8206,0x4F59,0x4FDE,0x903E, 0x9C7C,0x6109,0x6E1D,0x6E14,0x9685,0x4E88,0x5A31,0x96E8, 0x4E0E,0x5C7F,0x79B9,0x5B87,0x8BED,0x7FBD,0x7389,0x57DF, 0x828B,0x90C1,0x5401,0x9047,0x55BB,0x5CEA,0x5FA1,0x6108, 0x6B32,0x72F1,0x80B2,0x8A89,0x8A1E,0x8A1F,0x8A20,0x8A21, 0x8A22,0x8A23,0x8A24,0x8A25,0x8A26,0x8A27,0x8A28,0x8A29, 0x8A2A,0x8A2B,0x8A2C,0x8A2D,0x8A2E,0x8A2F,0x8A30,0x8A31, 0x8A32,0x8A33,0x8A34,0x8A35,0x8A36,0x8A37,0x8A38,0x8A39, 0x8A3A,0x8A3B,0x8A3C,0x8A3D,0x8A3F,0x8A40,0x8A41,0x8A42, 0x8A43,0x8A44,0x8A45,0x8A46,0x8A47,0x8A49,0x8A4A,0x8A4B, 0x8A4C,0x8A4D,0x8A4E,0x8A4F,0x8A50,0x8A51,0x8A52,0x8A53, 0x8A54,0x8A55,0x8A56,0x8A57,0x8A58,0x8A59,0x8A5A,0x8A5B, 0x8A5C,0x8A5D,0x8A5E,0xFFFD,0x8A5F,0x8A60,0x8A61,0x8A62, 0x8A63,0x8A64,0x8A65,0x8A66,0x8A67,0x8A68,0x8A69,0x8A6A, 0x8A6B,0x8A6C,0x8A6D,0x8A6E,0x8A6F,0x8A70,0x8A71,0x8A72, 0x8A73,0x8A74,0x8A75,0x8A76,0x8A77,0x8A78,0x8A7A,0x8A7B, 0x8A7C,0x8A7D,0x8A7E,0x8A7F,0x8A80,0x6D74,0x5BD3,0x88D5, 0x9884,0x8C6B,0x9A6D,0x9E33,0x6E0A,0x51A4,0x5143,0x57A3, 0x8881,0x539F,0x63F4,0x8F95,0x56ED,0x5458,0x5706,0x733F, 0x6E90,0x7F18,0x8FDC,0x82D1,0x613F,0x6028,0x9662,0x66F0, 0x7EA6,0x8D8A,0x8DC3,0x94A5,0x5CB3,0x7CA4,0x6708,0x60A6, 0x9605,0x8018,0x4E91,0x90E7,0x5300,0x9668,0x5141,0x8FD0, 0x8574,0x915D,0x6655,0x97F5,0x5B55,0x531D,0x7838,0x6742, 0x683D,0x54C9,0x707E,0x5BB0,0x8F7D,0x518D,0x5728,0x54B1, 0x6512,0x6682,0x8D5E,0x8D43,0x810F,0x846C,0x906D,0x7CDF, 0x51FF,0x85FB,0x67A3,0x65E9,0x6FA1,0x86A4,0x8E81,0x566A, 0x9020,0x7682,0x7076,0x71E5,0x8D23,0x62E9,0x5219,0x6CFD, 0x8D3C,0x600E,0x589E,0x618E,0x66FE,0x8D60,0x624E,0x55B3, 0x6E23,0x672D,0x8F67,0x8A81,0x8A82,0x8A83,0x8A84,0x8A85, 0x8A86,0x8A87,0x8A88,0x8A8B,0x8A8C,0x8A8D,0x8A8E,0x8A8F, 0x8A90,0x8A91,0x8A92,0x8A94,0x8A95,0x8A96,0x8A97,0x8A98, 0x8A99,0x8A9A,0x8A9B,0x8A9C,0x8A9D,0x8A9E,0x8A9F,0x8AA0, 0x8AA1,0x8AA2,0x8AA3,0x8AA4,0x8AA5,0x8AA6,0x8AA7,0x8AA8, 0x8AA9,0x8AAA,0x8AAB,0x8AAC,0x8AAD,0x8AAE,0x8AAF,0x8AB0, 0x8AB1,0x8AB2,0x8AB3,0x8AB4,0x8AB5,0x8AB6,0x8AB7,0x8AB8, 0x8AB9,0x8ABA,0x8ABB,0x8ABC,0x8ABD,0x8ABE,0x8ABF,0x8AC0, 0x8AC1,0x8AC2,0xFFFD,0x8AC3,0x8AC4,0x8AC5,0x8AC6,0x8AC7, 0x8AC8,0x8AC9,0x8ACA,0x8ACB,0x8ACC,0x8ACD,0x8ACE,0x8ACF, 0x8AD0,0x8AD1,0x8AD2,0x8AD3,0x8AD4,0x8AD5,0x8AD6,0x8AD7, 0x8AD8,0x8AD9,0x8ADA,0x8ADB,0x8ADC,0x8ADD,0x8ADE,0x8ADF, 0x8AE0,0x8AE1,0x8AE2,0x8AE3,0x94E1,0x95F8,0x7728,0x6805, 0x69A8,0x548B,0x4E4D,0x70B8,0x8BC8,0x6458,0x658B,0x5B85, 0x7A84,0x503A,0x5BE8,0x77BB,0x6BE1,0x8A79,0x7C98,0x6CBE, 0x76CF,0x65A9,0x8F97,0x5D2D,0x5C55,0x8638,0x6808,0x5360, 0x6218,0x7AD9,0x6E5B,0x7EFD,0x6A1F,0x7AE0,0x5F70,0x6F33, 0x5F20,0x638C,0x6DA8,0x6756,0x4E08,0x5E10,0x8D26,0x4ED7, 0x80C0,0x7634,0x969C,0x62DB,0x662D,0x627E,0x6CBC,0x8D75, 0x7167,0x7F69,0x5146,0x8087,0x53EC,0x906E,0x6298,0x54F2, 0x86F0,0x8F99,0x8005,0x9517,0x8517,0x8FD9,0x6D59,0x73CD, 0x659F,0x771F,0x7504,0x7827,0x81FB,0x8D1E,0x9488,0x4FA6, 0x6795,0x75B9,0x8BCA,0x9707,0x632F,0x9547,0x9635,0x84B8, 0x6323,0x7741,0x5F81,0x72F0,0x4E89,0x6014,0x6574,0x62EF, 0x6B63,0x653F,0x8AE4,0x8AE5,0x8AE6,0x8AE7,0x8AE8,0x8AE9, 0x8AEA,0x8AEB,0x8AEC,0x8AED,0x8AEE,0x8AEF,0x8AF0,0x8AF1, 0x8AF2,0x8AF3,0x8AF4,0x8AF5,0x8AF6,0x8AF7,0x8AF8,0x8AF9, 0x8AFA,0x8AFB,0x8AFC,0x8AFD,0x8AFE,0x8AFF,0x8B00,0x8B01, 0x8B02,0x8B03,0x8B04,0x8B05,0x8B06,0x8B08,0x8B09,0x8B0A, 0x8B0B,0x8B0C,0x8B0D,0x8B0E,0x8B0F,0x8B10,0x8B11,0x8B12, 0x8B13,0x8B14,0x8B15,0x8B16,0x8B17,0x8B18,0x8B19,0x8B1A, 0x8B1B,0x8B1C,0x8B1D,0x8B1E,0x8B1F,0x8B20,0x8B21,0x8B22, 0x8B23,0xFFFD,0x8B24,0x8B25,0x8B27,0x8B28,0x8B29,0x8B2A, 0x8B2B,0x8B2C,0x8B2D,0x8B2E,0x8B2F,0x8B30,0x8B31,0x8B32, 0x8B33,0x8B34,0x8B35,0x8B36,0x8B37,0x8B38,0x8B39,0x8B3A, 0x8B3B,0x8B3C,0x8B3D,0x8B3E,0x8B3F,0x8B40,0x8B41,0x8B42, 0x8B43,0x8B44,0x8B45,0x5E27,0x75C7,0x90D1,0x8BC1,0x829D, 0x679D,0x652F,0x5431,0x8718,0x77E5,0x80A2,0x8102,0x6C41, 0x4E4B,0x7EC7,0x804C,0x76F4,0x690D,0x6B96,0x6267,0x503C, 0x4F84,0x5740,0x6307,0x6B62,0x8DBE,0x53EA,0x65E8,0x7EB8, 0x5FD7,0x631A,0x63B7,0x81F3,0x81F4,0x7F6E,0x5E1C,0x5CD9, 0x5236,0x667A,0x79E9,0x7A1A,0x8D28,0x7099,0x75D4,0x6EDE, 0x6CBB,0x7A92,0x4E2D,0x76C5,0x5FE0,0x949F,0x8877,0x7EC8, 0x79CD,0x80BF,0x91CD,0x4EF2,0x4F17,0x821F,0x5468,0x5DDE, 0x6D32,0x8BCC,0x7CA5,0x8F74,0x8098,0x5E1A,0x5492,0x76B1, 0x5B99,0x663C,0x9AA4,0x73E0,0x682A,0x86DB,0x6731,0x732A, 0x8BF8,0x8BDB,0x9010,0x7AF9,0x70DB,0x716E,0x62C4,0x77A9, 0x5631,0x4E3B,0x8457,0x67F1,0x52A9,0x86C0,0x8D2E,0x94F8, 0x7B51,0x8B46,0x8B47,0x8B48,0x8B49,0x8B4A,0x8B4B,0x8B4C, 0x8B4D,0x8B4E,0x8B4F,0x8B50,0x8B51,0x8B52,0x8B53,0x8B54, 0x8B55,0x8B56,0x8B57,0x8B58,0x8B59,0x8B5A,0x8B5B,0x8B5C, 0x8B5D,0x8B5E,0x8B5F,0x8B60,0x8B61,0x8B62,0x8B63,0x8B64, 0x8B65,0x8B67,0x8B68,0x8B69,0x8B6A,0x8B6B,0x8B6D,0x8B6E, 0x8B6F,0x8B70,0x8B71,0x8B72,0x8B73,0x8B74,0x8B75,0x8B76, 0x8B77,0x8B78,0x8B79,0x8B7A,0x8B7B,0x8B7C,0x8B7D,0x8B7E, 0x8B7F,0x8B80,0x8B81,0x8B82,0x8B83,0x8B84,0x8B85,0x8B86, 0xFFFD,0x8B87,0x8B88,0x8B89,0x8B8A,0x8B8B,0x8B8C,0x8B8D, 0x8B8E,0x8B8F,0x8B90,0x8B91,0x8B92,0x8B93,0x8B94,0x8B95, 0x8B96,0x8B97,0x8B98,0x8B99,0x8B9A,0x8B9B,0x8B9C,0x8B9D, 0x8B9E,0x8B9F,0x8BAC,0x8BB1,0x8BBB,0x8BC7,0x8BD0,0x8BEA, 0x8C09,0x8C1E,0x4F4F,0x6CE8,0x795D,0x9A7B,0x6293,0x722A, 0x62FD,0x4E13,0x7816,0x8F6C,0x64B0,0x8D5A,0x7BC6,0x6869, 0x5E84,0x88C5,0x5986,0x649E,0x58EE,0x72B6,0x690E,0x9525, 0x8FFD,0x8D58,0x5760,0x7F00,0x8C06,0x51C6,0x6349,0x62D9, 0x5353,0x684C,0x7422,0x8301,0x914C,0x5544,0x7740,0x707C, 0x6D4A,0x5179,0x54A8,0x8D44,0x59FF,0x6ECB,0x6DC4,0x5B5C, 0x7D2B,0x4ED4,0x7C7D,0x6ED3,0x5B50,0x81EA,0x6E0D,0x5B57, 0x9B03,0x68D5,0x8E2A,0x5B97,0x7EFC,0x603B,0x7EB5,0x90B9, 0x8D70,0x594F,0x63CD,0x79DF,0x8DB3,0x5352,0x65CF,0x7956, 0x8BC5,0x963B,0x7EC4,0x94BB,0x7E82,0x5634,0x9189,0x6700, 0x7F6A,0x5C0A,0x9075,0x6628,0x5DE6,0x4F50,0x67DE,0x505A, 0x4F5C,0x5750,0x5EA7,0xE810,0xE811,0xE812,0xE813,0xE814, 0x8C38,0x8C39,0x8C3A,0x8C3B,0x8C3C,0x8C3D,0x8C3E,0x8C3F, 0x8C40,0x8C42,0x8C43,0x8C44,0x8C45,0x8C48,0x8C4A,0x8C4B, 0x8C4D,0x8C4E,0x8C4F,0x8C50,0x8C51,0x8C52,0x8C53,0x8C54, 0x8C56,0x8C57,0x8C58,0x8C59,0x8C5B,0x8C5C,0x8C5D,0x8C5E, 0x8C5F,0x8C60,0x8C63,0x8C64,0x8C65,0x8C66,0x8C67,0x8C68, 0x8C69,0x8C6C,0x8C6D,0x8C6E,0x8C6F,0x8C70,0x8C71,0x8C72, 0x8C74,0x8C75,0x8C76,0x8C77,0x8C7B,0x8C7C,0x8C7D,0x8C7E, 0x8C7F,0x8C80,0x8C81,0x8C83,0x8C84,0x8C86,0x8C87,0xFFFD, 0x8C88,0x8C8B,0x8C8D,0x8C8E,0x8C8F,0x8C90,0x8C91,0x8C92, 0x8C93,0x8C95,0x8C96,0x8C97,0x8C99,0x8C9A,0x8C9B,0x8C9C, 0x8C9D,0x8C9E,0x8C9F,0x8CA0,0x8CA1,0x8CA2,0x8CA3,0x8CA4, 0x8CA5,0x8CA6,0x8CA7,0x8CA8,0x8CA9,0x8CAA,0x8CAB,0x8CAC, 0x8CAD,0x4E8D,0x4E0C,0x5140,0x4E10,0x5EFF,0x5345,0x4E15, 0x4E98,0x4E1E,0x9B32,0x5B6C,0x5669,0x4E28,0x79BA,0x4E3F, 0x5315,0x4E47,0x592D,0x723B,0x536E,0x6C10,0x56DF,0x80E4, 0x9997,0x6BD3,0x777E,0x9F17,0x4E36,0x4E9F,0x9F10,0x4E5C, 0x4E69,0x4E93,0x8288,0x5B5B,0x556C,0x560F,0x4EC4,0x538D, 0x539D,0x53A3,0x53A5,0x53AE,0x9765,0x8D5D,0x531A,0x53F5, 0x5326,0x532E,0x533E,0x8D5C,0x5366,0x5363,0x5202,0x5208, 0x520E,0x522D,0x5233,0x523F,0x5240,0x524C,0x525E,0x5261, 0x525C,0x84AF,0x527D,0x5282,0x5281,0x5290,0x5293,0x5182, 0x7F54,0x4EBB,0x4EC3,0x4EC9,0x4EC2,0x4EE8,0x4EE1,0x4EEB, 0x4EDE,0x4F1B,0x4EF3,0x4F22,0x4F64,0x4EF5,0x4F25,0x4F27, 0x4F09,0x4F2B,0x4F5E,0x4F67,0x6538,0x4F5A,0x4F5D,0x8CAE, 0x8CAF,0x8CB0,0x8CB1,0x8CB2,0x8CB3,0x8CB4,0x8CB5,0x8CB6, 0x8CB7,0x8CB8,0x8CB9,0x8CBA,0x8CBB,0x8CBC,0x8CBD,0x8CBE, 0x8CBF,0x8CC0,0x8CC1,0x8CC2,0x8CC3,0x8CC4,0x8CC5,0x8CC6, 0x8CC7,0x8CC8,0x8CC9,0x8CCA,0x8CCB,0x8CCC,0x8CCD,0x8CCE, 0x8CCF,0x8CD0,0x8CD1,0x8CD2,0x8CD3,0x8CD4,0x8CD5,0x8CD6, 0x8CD7,0x8CD8,0x8CD9,0x8CDA,0x8CDB,0x8CDC,0x8CDD,0x8CDE, 0x8CDF,0x8CE0,0x8CE1,0x8CE2,0x8CE3,0x8CE4,0x8CE5,0x8CE6, 0x8CE7,0x8CE8,0x8CE9,0x8CEA,0x8CEB,0x8CEC,0xFFFD,0x8CED, 0x8CEE,0x8CEF,0x8CF0,0x8CF1,0x8CF2,0x8CF3,0x8CF4,0x8CF5, 0x8CF6,0x8CF7,0x8CF8,0x8CF9,0x8CFA,0x8CFB,0x8CFC,0x8CFD, 0x8CFE,0x8CFF,0x8D00,0x8D01,0x8D02,0x8D03,0x8D04,0x8D05, 0x8D06,0x8D07,0x8D08,0x8D09,0x8D0A,0x8D0B,0x8D0C,0x8D0D, 0x4F5F,0x4F57,0x4F32,0x4F3D,0x4F76,0x4F74,0x4F91,0x4F89, 0x4F83,0x4F8F,0x4F7E,0x4F7B,0x4FAA,0x4F7C,0x4FAC,0x4F94, 0x4FE6,0x4FE8,0x4FEA,0x4FC5,0x4FDA,0x4FE3,0x4FDC,0x4FD1, 0x4FDF,0x4FF8,0x5029,0x504C,0x4FF3,0x502C,0x500F,0x502E, 0x502D,0x4FFE,0x501C,0x500C,0x5025,0x5028,0x507E,0x5043, 0x5055,0x5048,0x504E,0x506C,0x507B,0x50A5,0x50A7,0x50A9, 0x50BA,0x50D6,0x5106,0x50ED,0x50EC,0x50E6,0x50EE,0x5107, 0x510B,0x4EDD,0x6C3D,0x4F58,0x4F65,0x4FCE,0x9FA0,0x6C46, 0x7C74,0x516E,0x5DFD,0x9EC9,0x9998,0x5181,0x5914,0x52F9, 0x530D,0x8A07,0x5310,0x51EB,0x5919,0x5155,0x4EA0,0x5156, 0x4EB3,0x886E,0x88A4,0x4EB5,0x8114,0x88D2,0x7980,0x5B34, 0x8803,0x7FB8,0x51AB,0x51B1,0x51BD,0x51BC,0x8D0E,0x8D0F, 0x8D10,0x8D11,0x8D12,0x8D13,0x8D14,0x8D15,0x8D16,0x8D17, 0x8D18,0x8D19,0x8D1A,0x8D1B,0x8D1C,0x8D20,0x8D51,0x8D52, 0x8D57,0x8D5F,0x8D65,0x8D68,0x8D69,0x8D6A,0x8D6C,0x8D6E, 0x8D6F,0x8D71,0x8D72,0x8D78,0x8D79,0x8D7A,0x8D7B,0x8D7C, 0x8D7D,0x8D7E,0x8D7F,0x8D80,0x8D82,0x8D83,0x8D86,0x8D87, 0x8D88,0x8D89,0x8D8C,0x8D8D,0x8D8E,0x8D8F,0x8D90,0x8D92, 0x8D93,0x8D95,0x8D96,0x8D97,0x8D98,0x8D99,0x8D9A,0x8D9B, 0x8D9C,0x8D9D,0x8D9E,0x8DA0,0x8DA1,0xFFFD,0x8DA2,0x8DA4, 0x8DA5,0x8DA6,0x8DA7,0x8DA8,0x8DA9,0x8DAA,0x8DAB,0x8DAC, 0x8DAD,0x8DAE,0x8DAF,0x8DB0,0x8DB2,0x8DB6,0x8DB7,0x8DB9, 0x8DBB,0x8DBD,0x8DC0,0x8DC1,0x8DC2,0x8DC5,0x8DC7,0x8DC8, 0x8DC9,0x8DCA,0x8DCD,0x8DD0,0x8DD2,0x8DD3,0x8DD4,0x51C7, 0x5196,0x51A2,0x51A5,0x8BA0,0x8BA6,0x8BA7,0x8BAA,0x8BB4, 0x8BB5,0x8BB7,0x8BC2,0x8BC3,0x8BCB,0x8BCF,0x8BCE,0x8BD2, 0x8BD3,0x8BD4,0x8BD6,0x8BD8,0x8BD9,0x8BDC,0x8BDF,0x8BE0, 0x8BE4,0x8BE8,0x8BE9,0x8BEE,0x8BF0,0x8BF3,0x8BF6,0x8BF9, 0x8BFC,0x8BFF,0x8C00,0x8C02,0x8C04,0x8C07,0x8C0C,0x8C0F, 0x8C11,0x8C12,0x8C14,0x8C15,0x8C16,0x8C19,0x8C1B,0x8C18, 0x8C1D,0x8C1F,0x8C20,0x8C21,0x8C25,0x8C27,0x8C2A,0x8C2B, 0x8C2E,0x8C2F,0x8C32,0x8C33,0x8C35,0x8C36,0x5369,0x537A, 0x961D,0x9622,0x9621,0x9631,0x962A,0x963D,0x963C,0x9642, 0x9649,0x9654,0x965F,0x9667,0x966C,0x9672,0x9674,0x9688, 0x968D,0x9697,0x96B0,0x9097,0x909B,0x909D,0x9099,0x90AC, 0x90A1,0x90B4,0x90B3,0x90B6,0x90BA,0x8DD5,0x8DD8,0x8DD9, 0x8DDC,0x8DE0,0x8DE1,0x8DE2,0x8DE5,0x8DE6,0x8DE7,0x8DE9, 0x8DED,0x8DEE,0x8DF0,0x8DF1,0x8DF2,0x8DF4,0x8DF6,0x8DFC, 0x8DFE,0x8DFF,0x8E00,0x8E01,0x8E02,0x8E03,0x8E04,0x8E06, 0x8E07,0x8E08,0x8E0B,0x8E0D,0x8E0E,0x8E10,0x8E11,0x8E12, 0x8E13,0x8E15,0x8E16,0x8E17,0x8E18,0x8E19,0x8E1A,0x8E1B, 0x8E1C,0x8E20,0x8E21,0x8E24,0x8E25,0x8E26,0x8E27,0x8E28, 0x8E2B,0x8E2D,0x8E30,0x8E32,0x8E33,0x8E34,0x8E36,0x8E37, 0x8E38,0x8E3B,0x8E3C,0x8E3E,0xFFFD,0x8E3F,0x8E43,0x8E45, 0x8E46,0x8E4C,0x8E4D,0x8E4E,0x8E4F,0x8E50,0x8E53,0x8E54, 0x8E55,0x8E56,0x8E57,0x8E58,0x8E5A,0x8E5B,0x8E5C,0x8E5D, 0x8E5E,0x8E5F,0x8E60,0x8E61,0x8E62,0x8E63,0x8E64,0x8E65, 0x8E67,0x8E68,0x8E6A,0x8E6B,0x8E6E,0x8E71,0x90B8,0x90B0, 0x90CF,0x90C5,0x90BE,0x90D0,0x90C4,0x90C7,0x90D3,0x90E6, 0x90E2,0x90DC,0x90D7,0x90DB,0x90EB,0x90EF,0x90FE,0x9104, 0x9122,0x911E,0x9123,0x9131,0x912F,0x9139,0x9143,0x9146, 0x520D,0x5942,0x52A2,0x52AC,0x52AD,0x52BE,0x54FF,0x52D0, 0x52D6,0x52F0,0x53DF,0x71EE,0x77CD,0x5EF4,0x51F5,0x51FC, 0x9B2F,0x53B6,0x5F01,0x755A,0x5DEF,0x574C,0x57A9,0x57A1, 0x587E,0x58BC,0x58C5,0x58D1,0x5729,0x572C,0x572A,0x5733, 0x5739,0x572E,0x572F,0x575C,0x573B,0x5742,0x5769,0x5785, 0x576B,0x5786,0x577C,0x577B,0x5768,0x576D,0x5776,0x5773, 0x57AD,0x57A4,0x578C,0x57B2,0x57CF,0x57A7,0x57B4,0x5793, 0x57A0,0x57D5,0x57D8,0x57DA,0x57D9,0x57D2,0x57B8,0x57F4, 0x57EF,0x57F8,0x57E4,0x57DD,0x8E73,0x8E75,0x8E77,0x8E78, 0x8E79,0x8E7A,0x8E7B,0x8E7D,0x8E7E,0x8E80,0x8E82,0x8E83, 0x8E84,0x8E86,0x8E88,0x8E89,0x8E8A,0x8E8B,0x8E8C,0x8E8D, 0x8E8E,0x8E91,0x8E92,0x8E93,0x8E95,0x8E96,0x8E97,0x8E98, 0x8E99,0x8E9A,0x8E9B,0x8E9D,0x8E9F,0x8EA0,0x8EA1,0x8EA2, 0x8EA3,0x8EA4,0x8EA5,0x8EA6,0x8EA7,0x8EA8,0x8EA9,0x8EAA, 0x8EAD,0x8EAE,0x8EB0,0x8EB1,0x8EB3,0x8EB4,0x8EB5,0x8EB6, 0x8EB7,0x8EB8,0x8EB9,0x8EBB,0x8EBC,0x8EBD,0x8EBE,0x8EBF, 0x8EC0,0x8EC1,0x8EC2,0xFFFD,0x8EC3,0x8EC4,0x8EC5,0x8EC6, 0x8EC7,0x8EC8,0x8EC9,0x8ECA,0x8ECB,0x8ECC,0x8ECD,0x8ECF, 0x8ED0,0x8ED1,0x8ED2,0x8ED3,0x8ED4,0x8ED5,0x8ED6,0x8ED7, 0x8ED8,0x8ED9,0x8EDA,0x8EDB,0x8EDC,0x8EDD,0x8EDE,0x8EDF, 0x8EE0,0x8EE1,0x8EE2,0x8EE3,0x8EE4,0x580B,0x580D,0x57FD, 0x57ED,0x5800,0x581E,0x5819,0x5844,0x5820,0x5865,0x586C, 0x5881,0x5889,0x589A,0x5880,0x99A8,0x9F19,0x61FF,0x8279, 0x827D,0x827F,0x828F,0x828A,0x82A8,0x8284,0x828E,0x8291, 0x8297,0x8299,0x82AB,0x82B8,0x82BE,0x82B0,0x82C8,0x82CA, 0x82E3,0x8298,0x82B7,0x82AE,0x82CB,0x82CC,0x82C1,0x82A9, 0x82B4,0x82A1,0x82AA,0x829F,0x82C4,0x82CE,0x82A4,0x82E1, 0x8309,0x82F7,0x82E4,0x830F,0x8307,0x82DC,0x82F4,0x82D2, 0x82D8,0x830C,0x82FB,0x82D3,0x8311,0x831A,0x8306,0x8314, 0x8315,0x82E0,0x82D5,0x831C,0x8351,0x835B,0x835C,0x8308, 0x8392,0x833C,0x8334,0x8331,0x839B,0x835E,0x832F,0x834F, 0x8347,0x8343,0x835F,0x8340,0x8317,0x8360,0x832D,0x833A, 0x8333,0x8366,0x8365,0x8EE5,0x8EE6,0x8EE7,0x8EE8,0x8EE9, 0x8EEA,0x8EEB,0x8EEC,0x8EED,0x8EEE,0x8EEF,0x8EF0,0x8EF1, 0x8EF2,0x8EF3,0x8EF4,0x8EF5,0x8EF6,0x8EF7,0x8EF8,0x8EF9, 0x8EFA,0x8EFB,0x8EFC,0x8EFD,0x8EFE,0x8EFF,0x8F00,0x8F01, 0x8F02,0x8F03,0x8F04,0x8F05,0x8F06,0x8F07,0x8F08,0x8F09, 0x8F0A,0x8F0B,0x8F0C,0x8F0D,0x8F0E,0x8F0F,0x8F10,0x8F11, 0x8F12,0x8F13,0x8F14,0x8F15,0x8F16,0x8F17,0x8F18,0x8F19, 0x8F1A,0x8F1B,0x8F1C,0x8F1D,0x8F1E,0x8F1F,0x8F20,0x8F21, 0x8F22,0x8F23,0xFFFD,0x8F24,0x8F25,0x8F26,0x8F27,0x8F28, 0x8F29,0x8F2A,0x8F2B,0x8F2C,0x8F2D,0x8F2E,0x8F2F,0x8F30, 0x8F31,0x8F32,0x8F33,0x8F34,0x8F35,0x8F36,0x8F37,0x8F38, 0x8F39,0x8F3A,0x8F3B,0x8F3C,0x8F3D,0x8F3E,0x8F3F,0x8F40, 0x8F41,0x8F42,0x8F43,0x8F44,0x8368,0x831B,0x8369,0x836C, 0x836A,0x836D,0x836E,0x83B0,0x8378,0x83B3,0x83B4,0x83A0, 0x83AA,0x8393,0x839C,0x8385,0x837C,0x83B6,0x83A9,0x837D, 0x83B8,0x837B,0x8398,0x839E,0x83A8,0x83BA,0x83BC,0x83C1, 0x8401,0x83E5,0x83D8,0x5807,0x8418,0x840B,0x83DD,0x83FD, 0x83D6,0x841C,0x8438,0x8411,0x8406,0x83D4,0x83DF,0x840F, 0x8403,0x83F8,0x83F9,0x83EA,0x83C5,0x83C0,0x8426,0x83F0, 0x83E1,0x845C,0x8451,0x845A,0x8459,0x8473,0x8487,0x8488, 0x847A,0x8489,0x8478,0x843C,0x8446,0x8469,0x8476,0x848C, 0x848E,0x8431,0x846D,0x84C1,0x84CD,0x84D0,0x84E6,0x84BD, 0x84D3,0x84CA,0x84BF,0x84BA,0x84E0,0x84A1,0x84B9,0x84B4, 0x8497,0x84E5,0x84E3,0x850C,0x750D,0x8538,0x84F0,0x8539, 0x851F,0x853A,0x8F45,0x8F46,0x8F47,0x8F48,0x8F49,0x8F4A, 0x8F4B,0x8F4C,0x8F4D,0x8F4E,0x8F4F,0x8F50,0x8F51,0x8F52, 0x8F53,0x8F54,0x8F55,0x8F56,0x8F57,0x8F58,0x8F59,0x8F5A, 0x8F5B,0x8F5C,0x8F5D,0x8F5E,0x8F5F,0x8F60,0x8F61,0x8F62, 0x8F63,0x8F64,0x8F65,0x8F6A,0x8F80,0x8F8C,0x8F92,0x8F9D, 0x8FA0,0x8FA1,0x8FA2,0x8FA4,0x8FA5,0x8FA6,0x8FA7,0x8FAA, 0x8FAC,0x8FAD,0x8FAE,0x8FAF,0x8FB2,0x8FB3,0x8FB4,0x8FB5, 0x8FB7,0x8FB8,0x8FBA,0x8FBB,0x8FBC,0x8FBF,0x8FC0,0x8FC3, 0x8FC6,0xFFFD,0x8FC9,0x8FCA,0x8FCB,0x8FCC,0x8FCD,0x8FCF, 0x8FD2,0x8FD6,0x8FD7,0x8FDA,0x8FE0,0x8FE1,0x8FE3,0x8FE7, 0x8FEC,0x8FEF,0x8FF1,0x8FF2,0x8FF4,0x8FF5,0x8FF6,0x8FFA, 0x8FFB,0x8FFC,0x8FFE,0x8FFF,0x9007,0x9008,0x900C,0x900E, 0x9013,0x9015,0x9018,0x8556,0x853B,0x84FF,0x84FC,0x8559, 0x8548,0x8568,0x8564,0x855E,0x857A,0x77A2,0x8543,0x8572, 0x857B,0x85A4,0x85A8,0x8587,0x858F,0x8579,0x85AE,0x859C, 0x8585,0x85B9,0x85B7,0x85B0,0x85D3,0x85C1,0x85DC,0x85FF, 0x8627,0x8605,0x8629,0x8616,0x863C,0x5EFE,0x5F08,0x593C, 0x5941,0x8037,0x5955,0x595A,0x5958,0x530F,0x5C22,0x5C25, 0x5C2C,0x5C34,0x624C,0x626A,0x629F,0x62BB,0x62CA,0x62DA, 0x62D7,0x62EE,0x6322,0x62F6,0x6339,0x634B,0x6343,0x63AD, 0x63F6,0x6371,0x637A,0x638E,0x63B4,0x636D,0x63AC,0x638A, 0x6369,0x63AE,0x63BC,0x63F2,0x63F8,0x63E0,0x63FF,0x63C4, 0x63DE,0x63CE,0x6452,0x63C6,0x63BE,0x6445,0x6441,0x640B, 0x641B,0x6420,0x640C,0x6426,0x6421,0x645E,0x6484,0x646D, 0x6496,0x9019,0x901C,0x9023,0x9024,0x9025,0x9027,0x9028, 0x9029,0x902A,0x902B,0x902C,0x9030,0x9031,0x9032,0x9033, 0x9034,0x9037,0x9039,0x903A,0x903D,0x903F,0x9040,0x9043, 0x9045,0x9046,0x9048,0x9049,0x904A,0x904B,0x904C,0x904E, 0x9054,0x9055,0x9056,0x9059,0x905A,0x905C,0x905D,0x905E, 0x905F,0x9060,0x9061,0x9064,0x9066,0x9067,0x9069,0x906A, 0x906B,0x906C,0x906F,0x9070,0x9071,0x9072,0x9073,0x9076, 0x9077,0x9078,0x9079,0x907A,0x907B,0x907C,0x907E,0x9081, 0xFFFD,0x9084,0x9085,0x9086,0x9087,0x9089,0x908A,0x908C, 0x908D,0x908E,0x908F,0x9090,0x9092,0x9094,0x9096,0x9098, 0x909A,0x909C,0x909E,0x909F,0x90A0,0x90A4,0x90A5,0x90A7, 0x90A8,0x90A9,0x90AB,0x90AD,0x90B2,0x90B7,0x90BC,0x90BD, 0x90BF,0x90C0,0x647A,0x64B7,0x64B8,0x6499,0x64BA,0x64C0, 0x64D0,0x64D7,0x64E4,0x64E2,0x6509,0x6525,0x652E,0x5F0B, 0x5FD2,0x7519,0x5F11,0x535F,0x53F1,0x53FD,0x53E9,0x53E8, 0x53FB,0x5412,0x5416,0x5406,0x544B,0x5452,0x5453,0x5454, 0x5456,0x5443,0x5421,0x5457,0x5459,0x5423,0x5432,0x5482, 0x5494,0x5477,0x5471,0x5464,0x549A,0x549B,0x5484,0x5476, 0x5466,0x549D,0x54D0,0x54AD,0x54C2,0x54B4,0x54D2,0x54A7, 0x54A6,0x54D3,0x54D4,0x5472,0x54A3,0x54D5,0x54BB,0x54BF, 0x54CC,0x54D9,0x54DA,0x54DC,0x54A9,0x54AA,0x54A4,0x54DD, 0x54CF,0x54DE,0x551B,0x54E7,0x5520,0x54FD,0x5514,0x54F3, 0x5522,0x5523,0x550F,0x5511,0x5527,0x552A,0x5567,0x558F, 0x55B5,0x5549,0x556D,0x5541,0x5555,0x553F,0x5550,0x553C }; const static uint16 gbkDecoderInnerIndex6[]= { 0x90C2,0x90C3,0x90C6,0x90C8,0x90C9,0x90CB,0x90CC,0x90CD, 0x90D2,0x90D4,0x90D5,0x90D6,0x90D8,0x90D9,0x90DA,0x90DE, 0x90DF,0x90E0,0x90E3,0x90E4,0x90E5,0x90E9,0x90EA,0x90EC, 0x90EE,0x90F0,0x90F1,0x90F2,0x90F3,0x90F5,0x90F6,0x90F7, 0x90F9,0x90FA,0x90FB,0x90FC,0x90FF,0x9100,0x9101,0x9103, 0x9105,0x9106,0x9107,0x9108,0x9109,0x910A,0x910B,0x910C, 0x910D,0x910E,0x910F,0x9110,0x9111,0x9112,0x9113,0x9114, 0x9115,0x9116,0x9117,0x9118,0x911A,0x911B,0x911C,0xFFFD, 0x911D,0x911F,0x9120,0x9121,0x9124,0x9125,0x9126,0x9127, 0x9128,0x9129,0x912A,0x912B,0x912C,0x912D,0x912E,0x9130, 0x9132,0x9133,0x9134,0x9135,0x9136,0x9137,0x9138,0x913A, 0x913B,0x913C,0x913D,0x913E,0x913F,0x9140,0x9141,0x9142, 0x9144,0x5537,0x5556,0x5575,0x5576,0x5577,0x5533,0x5530, 0x555C,0x558B,0x55D2,0x5583,0x55B1,0x55B9,0x5588,0x5581, 0x559F,0x557E,0x55D6,0x5591,0x557B,0x55DF,0x55BD,0x55BE, 0x5594,0x5599,0x55EA,0x55F7,0x55C9,0x561F,0x55D1,0x55EB, 0x55EC,0x55D4,0x55E6,0x55DD,0x55C4,0x55EF,0x55E5,0x55F2, 0x55F3,0x55CC,0x55CD,0x55E8,0x55F5,0x55E4,0x8F94,0x561E, 0x5608,0x560C,0x5601,0x5624,0x5623,0x55FE,0x5600,0x5627, 0x562D,0x5658,0x5639,0x5657,0x562C,0x564D,0x5662,0x5659, 0x565C,0x564C,0x5654,0x5686,0x5664,0x5671,0x566B,0x567B, 0x567C,0x5685,0x5693,0x56AF,0x56D4,0x56D7,0x56DD,0x56E1, 0x56F5,0x56EB,0x56F9,0x56FF,0x5704,0x570A,0x5709,0x571C, 0x5E0F,0x5E19,0x5E14,0x5E11,0x5E31,0x5E3B,0x5E3C,0x9145, 0x9147,0x9148,0x9151,0x9153,0x9154,0x9155,0x9156,0x9158, 0x9159,0x915B,0x915C,0x915F,0x9160,0x9166,0x9167,0x9168, 0x916B,0x916D,0x9173,0x917A,0x917B,0x917C,0x9180,0x9181, 0x9182,0x9183,0x9184,0x9186,0x9188,0x918A,0x918E,0x918F, 0x9193,0x9194,0x9195,0x9196,0x9197,0x9198,0x9199,0x919C, 0x919D,0x919E,0x919F,0x91A0,0x91A1,0x91A4,0x91A5,0x91A6, 0x91A7,0x91A8,0x91A9,0x91AB,0x91AC,0x91B0,0x91B1,0x91B2, 0x91B3,0x91B6,0x91B7,0x91B8,0x91B9,0x91BB,0xFFFD,0x91BC, 0x91BD,0x91BE,0x91BF,0x91C0,0x91C1,0x91C2,0x91C3,0x91C4, 0x91C5,0x91C6,0x91C8,0x91CB,0x91D0,0x91D2,0x91D3,0x91D4, 0x91D5,0x91D6,0x91D7,0x91D8,0x91D9,0x91DA,0x91DB,0x91DD, 0x91DE,0x91DF,0x91E0,0x91E1,0x91E2,0x91E3,0x91E4,0x91E5, 0x5E37,0x5E44,0x5E54,0x5E5B,0x5E5E,0x5E61,0x5C8C,0x5C7A, 0x5C8D,0x5C90,0x5C96,0x5C88,0x5C98,0x5C99,0x5C91,0x5C9A, 0x5C9C,0x5CB5,0x5CA2,0x5CBD,0x5CAC,0x5CAB,0x5CB1,0x5CA3, 0x5CC1,0x5CB7,0x5CC4,0x5CD2,0x5CE4,0x5CCB,0x5CE5,0x5D02, 0x5D03,0x5D27,0x5D26,0x5D2E,0x5D24,0x5D1E,0x5D06,0x5D1B, 0x5D58,0x5D3E,0x5D34,0x5D3D,0x5D6C,0x5D5B,0x5D6F,0x5D5D, 0x5D6B,0x5D4B,0x5D4A,0x5D69,0x5D74,0x5D82,0x5D99,0x5D9D, 0x8C73,0x5DB7,0x5DC5,0x5F73,0x5F77,0x5F82,0x5F87,0x5F89, 0x5F8C,0x5F95,0x5F99,0x5F9C,0x5FA8,0x5FAD,0x5FB5,0x5FBC, 0x8862,0x5F61,0x72AD,0x72B0,0x72B4,0x72B7,0x72B8,0x72C3, 0x72C1,0x72CE,0x72CD,0x72D2,0x72E8,0x72EF,0x72E9,0x72F2, 0x72F4,0x72F7,0x7301,0x72F3,0x7303,0x72FA,0x91E6,0x91E7, 0x91E8,0x91E9,0x91EA,0x91EB,0x91EC,0x91ED,0x91EE,0x91EF, 0x91F0,0x91F1,0x91F2,0x91F3,0x91F4,0x91F5,0x91F6,0x91F7, 0x91F8,0x91F9,0x91FA,0x91FB,0x91FC,0x91FD,0x91FE,0x91FF, 0x9200,0x9201,0x9202,0x9203,0x9204,0x9205,0x9206,0x9207, 0x9208,0x9209,0x920A,0x920B,0x920C,0x920D,0x920E,0x920F, 0x9210,0x9211,0x9212,0x9213,0x9214,0x9215,0x9216,0x9217, 0x9218,0x9219,0x921A,0x921B,0x921C,0x921D,0x921E,0x921F, 0x9220,0x9221,0x9222,0x9223,0x9224,0xFFFD,0x9225,0x9226, 0x9227,0x9228,0x9229,0x922A,0x922B,0x922C,0x922D,0x922E, 0x922F,0x9230,0x9231,0x9232,0x9233,0x9234,0x9235,0x9236, 0x9237,0x9238,0x9239,0x923A,0x923B,0x923C,0x923D,0x923E, 0x923F,0x9240,0x9241,0x9242,0x9243,0x9244,0x9245,0x72FB, 0x7317,0x7313,0x7321,0x730A,0x731E,0x731D,0x7315,0x7322, 0x7339,0x7325,0x732C,0x7338,0x7331,0x7350,0x734D,0x7357, 0x7360,0x736C,0x736F,0x737E,0x821B,0x5925,0x98E7,0x5924, 0x5902,0x9963,0x9967,0x9968,0x9969,0x996A,0x996B,0x996C, 0x9974,0x9977,0x997D,0x9980,0x9984,0x9987,0x998A,0x998D, 0x9990,0x9991,0x9993,0x9994,0x9995,0x5E80,0x5E91,0x5E8B, 0x5E96,0x5EA5,0x5EA0,0x5EB9,0x5EB5,0x5EBE,0x5EB3,0x8D53, 0x5ED2,0x5ED1,0x5EDB,0x5EE8,0x5EEA,0x81BA,0x5FC4,0x5FC9, 0x5FD6,0x5FCF,0x6003,0x5FEE,0x6004,0x5FE1,0x5FE4,0x5FFE, 0x6005,0x6006,0x5FEA,0x5FED,0x5FF8,0x6019,0x6035,0x6026, 0x601B,0x600F,0x600D,0x6029,0x602B,0x600A,0x603F,0x6021, 0x6078,0x6079,0x607B,0x607A,0x6042,0x9246,0x9247,0x9248, 0x9249,0x924A,0x924B,0x924C,0x924D,0x924E,0x924F,0x9250, 0x9251,0x9252,0x9253,0x9254,0x9255,0x9256,0x9257,0x9258, 0x9259,0x925A,0x925B,0x925C,0x925D,0x925E,0x925F,0x9260, 0x9261,0x9262,0x9263,0x9264,0x9265,0x9266,0x9267,0x9268, 0x9269,0x926A,0x926B,0x926C,0x926D,0x926E,0x926F,0x9270, 0x9271,0x9272,0x9273,0x9275,0x9276,0x9277,0x9278,0x9279, 0x927A,0x927B,0x927C,0x927D,0x927E,0x927F,0x9280,0x9281, 0x9282,0x9283,0x9284,0x9285,0xFFFD,0x9286,0x9287,0x9288, 0x9289,0x928A,0x928B,0x928C,0x928D,0x928F,0x9290,0x9291, 0x9292,0x9293,0x9294,0x9295,0x9296,0x9297,0x9298,0x9299, 0x929A,0x929B,0x929C,0x929D,0x929E,0x929F,0x92A0,0x92A1, 0x92A2,0x92A3,0x92A4,0x92A5,0x92A6,0x92A7,0x606A,0x607D, 0x6096,0x609A,0x60AD,0x609D,0x6083,0x6092,0x608C,0x609B, 0x60EC,0x60BB,0x60B1,0x60DD,0x60D8,0x60C6,0x60DA,0x60B4, 0x6120,0x6126,0x6115,0x6123,0x60F4,0x6100,0x610E,0x612B, 0x614A,0x6175,0x61AC,0x6194,0x61A7,0x61B7,0x61D4,0x61F5, 0x5FDD,0x96B3,0x95E9,0x95EB,0x95F1,0x95F3,0x95F5,0x95F6, 0x95FC,0x95FE,0x9603,0x9604,0x9606,0x9608,0x960A,0x960B, 0x960C,0x960D,0x960F,0x9612,0x9615,0x9616,0x9617,0x9619, 0x961A,0x4E2C,0x723F,0x6215,0x6C35,0x6C54,0x6C5C,0x6C4A, 0x6CA3,0x6C85,0x6C90,0x6C94,0x6C8C,0x6C68,0x6C69,0x6C74, 0x6C76,0x6C86,0x6CA9,0x6CD0,0x6CD4,0x6CAD,0x6CF7,0x6CF8, 0x6CF1,0x6CD7,0x6CB2,0x6CE0,0x6CD6,0x6CFA,0x6CEB,0x6CEE, 0x6CB1,0x6CD3,0x6CEF,0x6CFE,0x92A8,0x92A9,0x92AA,0x92AB, 0x92AC,0x92AD,0x92AF,0x92B0,0x92B1,0x92B2,0x92B3,0x92B4, 0x92B5,0x92B6,0x92B7,0x92B8,0x92B9,0x92BA,0x92BB,0x92BC, 0x92BD,0x92BE,0x92BF,0x92C0,0x92C1,0x92C2,0x92C3,0x92C4, 0x92C5,0x92C6,0x92C7,0x92C9,0x92CA,0x92CB,0x92CC,0x92CD, 0x92CE,0x92CF,0x92D0,0x92D1,0x92D2,0x92D3,0x92D4,0x92D5, 0x92D6,0x92D7,0x92D8,0x92D9,0x92DA,0x92DB,0x92DC,0x92DD, 0x92DE,0x92DF,0x92E0,0x92E1,0x92E2,0x92E3,0x92E4,0x92E5, 0x92E6,0x92E7,0x92E8,0xFFFD,0x92E9,0x92EA,0x92EB,0x92EC, 0x92ED,0x92EE,0x92EF,0x92F0,0x92F1,0x92F2,0x92F3,0x92F4, 0x92F5,0x92F6,0x92F7,0x92F8,0x92F9,0x92FA,0x92FB,0x92FC, 0x92FD,0x92FE,0x92FF,0x9300,0x9301,0x9302,0x9303,0x9304, 0x9305,0x9306,0x9307,0x9308,0x9309,0x6D39,0x6D27,0x6D0C, 0x6D43,0x6D48,0x6D07,0x6D04,0x6D19,0x6D0E,0x6D2B,0x6D4D, 0x6D2E,0x6D35,0x6D1A,0x6D4F,0x6D52,0x6D54,0x6D33,0x6D91, 0x6D6F,0x6D9E,0x6DA0,0x6D5E,0x6D93,0x6D94,0x6D5C,0x6D60, 0x6D7C,0x6D63,0x6E1A,0x6DC7,0x6DC5,0x6DDE,0x6E0E,0x6DBF, 0x6DE0,0x6E11,0x6DE6,0x6DDD,0x6DD9,0x6E16,0x6DAB,0x6E0C, 0x6DAE,0x6E2B,0x6E6E,0x6E4E,0x6E6B,0x6EB2,0x6E5F,0x6E86, 0x6E53,0x6E54,0x6E32,0x6E25,0x6E44,0x6EDF,0x6EB1,0x6E98, 0x6EE0,0x6F2D,0x6EE2,0x6EA5,0x6EA7,0x6EBD,0x6EBB,0x6EB7, 0x6ED7,0x6EB4,0x6ECF,0x6E8F,0x6EC2,0x6E9F,0x6F62,0x6F46, 0x6F47,0x6F24,0x6F15,0x6EF9,0x6F2F,0x6F36,0x6F4B,0x6F74, 0x6F2A,0x6F09,0x6F29,0x6F89,0x6F8D,0x6F8C,0x6F78,0x6F72, 0x6F7C,0x6F7A,0x6FD1,0x930A,0x930B,0x930C,0x930D,0x930E, 0x930F,0x9310,0x9311,0x9312,0x9313,0x9314,0x9315,0x9316, 0x9317,0x9318,0x9319,0x931A,0x931B,0x931C,0x931D,0x931E, 0x931F,0x9320,0x9321,0x9322,0x9323,0x9324,0x9325,0x9326, 0x9327,0x9328,0x9329,0x932A,0x932B,0x932C,0x932D,0x932E, 0x932F,0x9330,0x9331,0x9332,0x9333,0x9334,0x9335,0x9336, 0x9337,0x9338,0x9339,0x933A,0x933B,0x933C,0x933D,0x933F, 0x9340,0x9341,0x9342,0x9343,0x9344,0x9345,0x9346,0x9347, 0x9348,0x9349,0xFFFD,0x934A,0x934B,0x934C,0x934D,0x934E, 0x934F,0x9350,0x9351,0x9352,0x9353,0x9354,0x9355,0x9356, 0x9357,0x9358,0x9359,0x935A,0x935B,0x935C,0x935D,0x935E, 0x935F,0x9360,0x9361,0x9362,0x9363,0x9364,0x9365,0x9366, 0x9367,0x9368,0x9369,0x936B,0x6FC9,0x6FA7,0x6FB9,0x6FB6, 0x6FC2,0x6FE1,0x6FEE,0x6FDE,0x6FE0,0x6FEF,0x701A,0x7023, 0x701B,0x7039,0x7035,0x704F,0x705E,0x5B80,0x5B84,0x5B95, 0x5B93,0x5BA5,0x5BB8,0x752F,0x9A9E,0x6434,0x5BE4,0x5BEE, 0x8930,0x5BF0,0x8E47,0x8B07,0x8FB6,0x8FD3,0x8FD5,0x8FE5, 0x8FEE,0x8FE4,0x8FE9,0x8FE6,0x8FF3,0x8FE8,0x9005,0x9004, 0x900B,0x9026,0x9011,0x900D,0x9016,0x9021,0x9035,0x9036, 0x902D,0x902F,0x9044,0x9051,0x9052,0x9050,0x9068,0x9058, 0x9062,0x905B,0x66B9,0x9074,0x907D,0x9082,0x9088,0x9083, 0x908B,0x5F50,0x5F57,0x5F56,0x5F58,0x5C3B,0x54AB,0x5C50, 0x5C59,0x5B71,0x5C63,0x5C66,0x7FBC,0x5F2A,0x5F29,0x5F2D, 0x8274,0x5F3C,0x9B3B,0x5C6E,0x5981,0x5983,0x598D,0x59A9, 0x59AA,0x59A3,0x936C,0x936D,0x936E,0x936F,0x9370,0x9371, 0x9372,0x9373,0x9374,0x9375,0x9376,0x9377,0x9378,0x9379, 0x937A,0x937B,0x937C,0x937D,0x937E,0x937F,0x9380,0x9381, 0x9382,0x9383,0x9384,0x9385,0x9386,0x9387,0x9388,0x9389, 0x938A,0x938B,0x938C,0x938D,0x938E,0x9390,0x9391,0x9392, 0x9393,0x9394,0x9395,0x9396,0x9397,0x9398,0x9399,0x939A, 0x939B,0x939C,0x939D,0x939E,0x939F,0x93A0,0x93A1,0x93A2, 0x93A3,0x93A4,0x93A5,0x93A6,0x93A7,0x93A8,0x93A9,0x93AA, 0x93AB,0xFFFD,0x93AC,0x93AD,0x93AE,0x93AF,0x93B0,0x93B1, 0x93B2,0x93B3,0x93B4,0x93B5,0x93B6,0x93B7,0x93B8,0x93B9, 0x93BA,0x93BB,0x93BC,0x93BD,0x93BE,0x93BF,0x93C0,0x93C1, 0x93C2,0x93C3,0x93C4,0x93C5,0x93C6,0x93C7,0x93C8,0x93C9, 0x93CB,0x93CC,0x93CD,0x5997,0x59CA,0x59AB,0x599E,0x59A4, 0x59D2,0x59B2,0x59AF,0x59D7,0x59BE,0x5A05,0x5A06,0x59DD, 0x5A08,0x59E3,0x59D8,0x59F9,0x5A0C,0x5A09,0x5A32,0x5A34, 0x5A11,0x5A23,0x5A13,0x5A40,0x5A67,0x5A4A,0x5A55,0x5A3C, 0x5A62,0x5A75,0x80EC,0x5AAA,0x5A9B,0x5A77,0x5A7A,0x5ABE, 0x5AEB,0x5AB2,0x5AD2,0x5AD4,0x5AB8,0x5AE0,0x5AE3,0x5AF1, 0x5AD6,0x5AE6,0x5AD8,0x5ADC,0x5B09,0x5B17,0x5B16,0x5B32, 0x5B37,0x5B40,0x5C15,0x5C1C,0x5B5A,0x5B65,0x5B73,0x5B51, 0x5B53,0x5B62,0x9A75,0x9A77,0x9A78,0x9A7A,0x9A7F,0x9A7D, 0x9A80,0x9A81,0x9A85,0x9A88,0x9A8A,0x9A90,0x9A92,0x9A93, 0x9A96,0x9A98,0x9A9B,0x9A9C,0x9A9D,0x9A9F,0x9AA0,0x9AA2, 0x9AA3,0x9AA5,0x9AA7,0x7E9F,0x7EA1,0x7EA3,0x7EA5,0x7EA8, 0x7EA9,0x93CE,0x93CF,0x93D0,0x93D1,0x93D2,0x93D3,0x93D4, 0x93D5,0x93D7,0x93D8,0x93D9,0x93DA,0x93DB,0x93DC,0x93DD, 0x93DE,0x93DF,0x93E0,0x93E1,0x93E2,0x93E3,0x93E4,0x93E5, 0x93E6,0x93E7,0x93E8,0x93E9,0x93EA,0x93EB,0x93EC,0x93ED, 0x93EE,0x93EF,0x93F0,0x93F1,0x93F2,0x93F3,0x93F4,0x93F5, 0x93F6,0x93F7,0x93F8,0x93F9,0x93FA,0x93FB,0x93FC,0x93FD, 0x93FE,0x93FF,0x9400,0x9401,0x9402,0x9403,0x9404,0x9405, 0x9406,0x9407,0x9408,0x9409,0x940A,0x940B,0x940C,0x940D, 0xFFFD,0x940E,0x940F,0x9410,0x9411,0x9412,0x9413,0x9414, 0x9415,0x9416,0x9417,0x9418,0x9419,0x941A,0x941B,0x941C, 0x941D,0x941E,0x941F,0x9420,0x9421,0x9422,0x9423,0x9424, 0x9425,0x9426,0x9427,0x9428,0x9429,0x942A,0x942B,0x942C, 0x942D,0x942E,0x7EAD,0x7EB0,0x7EBE,0x7EC0,0x7EC1,0x7EC2, 0x7EC9,0x7ECB,0x7ECC,0x7ED0,0x7ED4,0x7ED7,0x7EDB,0x7EE0, 0x7EE1,0x7EE8,0x7EEB,0x7EEE,0x7EEF,0x7EF1,0x7EF2,0x7F0D, 0x7EF6,0x7EFA,0x7EFB,0x7EFE,0x7F01,0x7F02,0x7F03,0x7F07, 0x7F08,0x7F0B,0x7F0C,0x7F0F,0x7F11,0x7F12,0x7F17,0x7F19, 0x7F1C,0x7F1B,0x7F1F,0x7F21,0x7F22,0x7F23,0x7F24,0x7F25, 0x7F26,0x7F27,0x7F2A,0x7F2B,0x7F2C,0x7F2D,0x7F2F,0x7F30, 0x7F31,0x7F32,0x7F33,0x7F35,0x5E7A,0x757F,0x5DDB,0x753E, 0x9095,0x738E,0x7391,0x73AE,0x73A2,0x739F,0x73CF,0x73C2, 0x73D1,0x73B7,0x73B3,0x73C0,0x73C9,0x73C8,0x73E5,0x73D9, 0x987C,0x740A,0x73E9,0x73E7,0x73DE,0x73BA,0x73F2,0x740F, 0x742A,0x745B,0x7426,0x7425,0x7428,0x7430,0x742E,0x742C, 0x942F,0x9430,0x9431,0x9432,0x9433,0x9434,0x9435,0x9436, 0x9437,0x9438,0x9439,0x943A,0x943B,0x943C,0x943D,0x943F, 0x9440,0x9441,0x9442,0x9443,0x9444,0x9445,0x9446,0x9447, 0x9448,0x9449,0x944A,0x944B,0x944C,0x944D,0x944E,0x944F, 0x9450,0x9451,0x9452,0x9453,0x9454,0x9455,0x9456,0x9457, 0x9458,0x9459,0x945A,0x945B,0x945C,0x945D,0x945E,0x945F, 0x9460,0x9461,0x9462,0x9463,0x9464,0x9465,0x9466,0x9467, 0x9468,0x9469,0x946A,0x946C,0x946D,0x946E,0x946F,0xFFFD, 0x9470,0x9471,0x9472,0x9473,0x9474,0x9475,0x9476,0x9477, 0x9478,0x9479,0x947A,0x947B,0x947C,0x947D,0x947E,0x947F, 0x9480,0x9481,0x9482,0x9483,0x9484,0x9491,0x9496,0x9498, 0x94C7,0x94CF,0x94D3,0x94D4,0x94DA,0x94E6,0x94FB,0x951C, 0x9520,0x741B,0x741A,0x7441,0x745C,0x7457,0x7455,0x7459, 0x7477,0x746D,0x747E,0x749C,0x748E,0x7480,0x7481,0x7487, 0x748B,0x749E,0x74A8,0x74A9,0x7490,0x74A7,0x74D2,0x74BA, 0x97EA,0x97EB,0x97EC,0x674C,0x6753,0x675E,0x6748,0x6769, 0x67A5,0x6787,0x676A,0x6773,0x6798,0x67A7,0x6775,0x67A8, 0x679E,0x67AD,0x678B,0x6777,0x677C,0x67F0,0x6809,0x67D8, 0x680A,0x67E9,0x67B0,0x680C,0x67D9,0x67B5,0x67DA,0x67B3, 0x67DD,0x6800,0x67C3,0x67B8,0x67E2,0x680E,0x67C1,0x67FD, 0x6832,0x6833,0x6860,0x6861,0x684E,0x6862,0x6844,0x6864, 0x6883,0x681D,0x6855,0x6866,0x6841,0x6867,0x6840,0x683E, 0x684A,0x6849,0x6829,0x68B5,0x688F,0x6874,0x6877,0x6893, 0x686B,0x68C2,0x696E,0x68FC,0x691F,0x6920,0x68F9,0x9527, 0x9533,0x953D,0x9543,0x9548,0x954B,0x9555,0x955A,0x9560, 0x956E,0x9574,0x9575,0x9577,0x9578,0x9579,0x957A,0x957B, 0x957C,0x957D,0x957E,0x9580,0x9581,0x9582,0x9583,0x9584, 0x9585,0x9586,0x9587,0x9588,0x9589,0x958A,0x958B,0x958C, 0x958D,0x958E,0x958F,0x9590,0x9591,0x9592,0x9593,0x9594, 0x9595,0x9596,0x9597,0x9598,0x9599,0x959A,0x959B,0x959C, 0x959D,0x959E,0x959F,0x95A0,0x95A1,0x95A2,0x95A3,0x95A4, 0x95A5,0x95A6,0x95A7,0x95A8,0x95A9,0x95AA,0xFFFD,0x95AB, 0x95AC,0x95AD,0x95AE,0x95AF,0x95B0,0x95B1,0x95B2,0x95B3, 0x95B4,0x95B5,0x95B6,0x95B7,0x95B8,0x95B9,0x95BA,0x95BB, 0x95BC,0x95BD,0x95BE,0x95BF,0x95C0,0x95C1,0x95C2,0x95C3, 0x95C4,0x95C5,0x95C6,0x95C7,0x95C8,0x95C9,0x95CA,0x95CB, 0x6924,0x68F0,0x690B,0x6901,0x6957,0x68E3,0x6910,0x6971, 0x6939,0x6960,0x6942,0x695D,0x6984,0x696B,0x6980,0x6998, 0x6978,0x6934,0x69CC,0x6987,0x6988,0x69CE,0x6989,0x6966, 0x6963,0x6979,0x699B,0x69A7,0x69BB,0x69AB,0x69AD,0x69D4, 0x69B1,0x69C1,0x69CA,0x69DF,0x6995,0x69E0,0x698D,0x69FF, 0x6A2F,0x69ED,0x6A17,0x6A18,0x6A65,0x69F2,0x6A44,0x6A3E, 0x6AA0,0x6A50,0x6A5B,0x6A35,0x6A8E,0x6A79,0x6A3D,0x6A28, 0x6A58,0x6A7C,0x6A91,0x6A90,0x6AA9,0x6A97,0x6AAB,0x7337, 0x7352,0x6B81,0x6B82,0x6B87,0x6B84,0x6B92,0x6B93,0x6B8D, 0x6B9A,0x6B9B,0x6BA1,0x6BAA,0x8F6B,0x8F6D,0x8F71,0x8F72, 0x8F73,0x8F75,0x8F76,0x8F78,0x8F77,0x8F79,0x8F7A,0x8F7C, 0x8F7E,0x8F81,0x8F82,0x8F84,0x8F87,0x8F8B,0x95CC,0x95CD, 0x95CE,0x95CF,0x95D0,0x95D1,0x95D2,0x95D3,0x95D4,0x95D5, 0x95D6,0x95D7,0x95D8,0x95D9,0x95DA,0x95DB,0x95DC,0x95DD, 0x95DE,0x95DF,0x95E0,0x95E1,0x95E2,0x95E3,0x95E4,0x95E5, 0x95E6,0x95E7,0x95EC,0x95FF,0x9607,0x9613,0x9618,0x961B, 0x961E,0x9620,0x9623,0x9624,0x9625,0x9626,0x9627,0x9628, 0x9629,0x962B,0x962C,0x962D,0x962F,0x9630,0x9637,0x9638, 0x9639,0x963A,0x963E,0x9641,0x9643,0x964A,0x964E,0x964F, 0x9651,0x9652,0x9653,0x9656,0x9657,0xFFFD,0x9658,0x9659, 0x965A,0x965C,0x965D,0x965E,0x9660,0x9663,0x9665,0x9666, 0x966B,0x966D,0x966E,0x966F,0x9670,0x9671,0x9673,0x9678, 0x9679,0x967A,0x967B,0x967C,0x967D,0x967E,0x967F,0x9680, 0x9681,0x9682,0x9683,0x9684,0x9687,0x9689,0x968A,0x8F8D, 0x8F8E,0x8F8F,0x8F98,0x8F9A,0x8ECE,0x620B,0x6217,0x621B, 0x621F,0x6222,0x6221,0x6225,0x6224,0x622C,0x81E7,0x74EF, 0x74F4,0x74FF,0x750F,0x7511,0x7513,0x6534,0x65EE,0x65EF, 0x65F0,0x660A,0x6619,0x6772,0x6603,0x6615,0x6600,0x7085, 0x66F7,0x661D,0x6634,0x6631,0x6636,0x6635,0x8006,0x665F, 0x6654,0x6641,0x664F,0x6656,0x6661,0x6657,0x6677,0x6684, 0x668C,0x66A7,0x669D,0x66BE,0x66DB,0x66DC,0x66E6,0x66E9, 0x8D32,0x8D33,0x8D36,0x8D3B,0x8D3D,0x8D40,0x8D45,0x8D46, 0x8D48,0x8D49,0x8D47,0x8D4D,0x8D55,0x8D59,0x89C7,0x89CA, 0x89CB,0x89CC,0x89CE,0x89CF,0x89D0,0x89D1,0x726E,0x729F, 0x725D,0x7266,0x726F,0x727E,0x727F,0x7284,0x728B,0x728D, 0x728F,0x7292,0x6308,0x6332,0x63B0,0x968C,0x968E,0x9691, 0x9692,0x9693,0x9695,0x9696,0x969A,0x969B,0x969D,0x969E, 0x969F,0x96A0,0x96A1,0x96A2,0x96A3,0x96A4,0x96A5,0x96A6, 0x96A8,0x96A9,0x96AA,0x96AB,0x96AC,0x96AD,0x96AE,0x96AF, 0x96B1,0x96B2,0x96B4,0x96B5,0x96B7,0x96B8,0x96BA,0x96BB, 0x96BF,0x96C2,0x96C3,0x96C8,0x96CA,0x96CB,0x96D0,0x96D1, 0x96D3,0x96D4,0x96D6,0x96D7,0x96D8,0x96D9,0x96DA,0x96DB, 0x96DC,0x96DD,0x96DE,0x96DF,0x96E1,0x96E2,0x96E3,0x96E4, 0x96E5,0x96E6,0x96E7,0x96EB,0xFFFD,0x96EC,0x96ED,0x96EE, 0x96F0,0x96F1,0x96F2,0x96F4,0x96F5,0x96F8,0x96FA,0x96FB, 0x96FC,0x96FD,0x96FF,0x9702,0x9703,0x9705,0x970A,0x970B, 0x970C,0x9710,0x9711,0x9712,0x9714,0x9715,0x9717,0x9718, 0x9719,0x971A,0x971B,0x971D,0x971F,0x9720,0x643F,0x64D8, 0x8004,0x6BEA,0x6BF3,0x6BFD,0x6BF5,0x6BF9,0x6C05,0x6C07, 0x6C06,0x6C0D,0x6C15,0x6C18,0x6C19,0x6C1A,0x6C21,0x6C29, 0x6C24,0x6C2A,0x6C32,0x6535,0x6555,0x656B,0x724D,0x7252, 0x7256,0x7230,0x8662,0x5216,0x809F,0x809C,0x8093,0x80BC, 0x670A,0x80BD,0x80B1,0x80AB,0x80AD,0x80B4,0x80B7,0x80E7, 0x80E8,0x80E9,0x80EA,0x80DB,0x80C2,0x80C4,0x80D9,0x80CD, 0x80D7,0x6710,0x80DD,0x80EB,0x80F1,0x80F4,0x80ED,0x810D, 0x810E,0x80F2,0x80FC,0x6715,0x8112,0x8C5A,0x8136,0x811E, 0x812C,0x8118,0x8132,0x8148,0x814C,0x8153,0x8174,0x8159, 0x815A,0x8171,0x8160,0x8169,0x817C,0x817D,0x816D,0x8167, 0x584D,0x5AB5,0x8188,0x8182,0x8191,0x6ED5,0x81A3,0x81AA, 0x81CC,0x6726,0x81CA,0x81BB,0x9721,0x9722,0x9723,0x9724, 0x9725,0x9726,0x9727,0x9728,0x9729,0x972B,0x972C,0x972E, 0x972F,0x9731,0x9733,0x9734,0x9735,0x9736,0x9737,0x973A, 0x973B,0x973C,0x973D,0x973F,0x9740,0x9741,0x9742,0x9743, 0x9744,0x9745,0x9746,0x9747,0x9748,0x9749,0x974A,0x974B, 0x974C,0x974D,0x974E,0x974F,0x9750,0x9751,0x9754,0x9755, 0x9757,0x9758,0x975A,0x975C,0x975D,0x975F,0x9763,0x9764, 0x9766,0x9767,0x9768,0x976A,0x976B,0x976C,0x976D,0x976E, 0x976F,0x9770,0x9771,0xFFFD,0x9772,0x9775,0x9777,0x9778, 0x9779,0x977A,0x977B,0x977D,0x977E,0x977F,0x9780,0x9781, 0x9782,0x9783,0x9784,0x9786,0x9787,0x9788,0x9789,0x978A, 0x978C,0x978E,0x978F,0x9790,0x9793,0x9795,0x9796,0x9797, 0x9799,0x979A,0x979B,0x979C,0x979D,0x81C1,0x81A6,0x6B24, 0x6B37,0x6B39,0x6B43,0x6B46,0x6B59,0x98D1,0x98D2,0x98D3, 0x98D5,0x98D9,0x98DA,0x6BB3,0x5F40,0x6BC2,0x89F3,0x6590, 0x9F51,0x6593,0x65BC,0x65C6,0x65C4,0x65C3,0x65CC,0x65CE, 0x65D2,0x65D6,0x7080,0x709C,0x7096,0x709D,0x70BB,0x70C0, 0x70B7,0x70AB,0x70B1,0x70E8,0x70CA,0x7110,0x7113,0x7116, 0x712F,0x7131,0x7173,0x715C,0x7168,0x7145,0x7172,0x714A, 0x7178,0x717A,0x7198,0x71B3,0x71B5,0x71A8,0x71A0,0x71E0, 0x71D4,0x71E7,0x71F9,0x721D,0x7228,0x706C,0x7118,0x7166, 0x71B9,0x623E,0x623D,0x6243,0x6248,0x6249,0x793B,0x7940, 0x7946,0x7949,0x795B,0x795C,0x7953,0x795A,0x7962,0x7957, 0x7960,0x796F,0x7967,0x797A,0x7985,0x798A,0x799A,0x79A7, 0x79B3,0x5FD1,0x5FD0,0x979E,0x979F,0x97A1,0x97A2,0x97A4, 0x97A5,0x97A6,0x97A7,0x97A8,0x97A9,0x97AA,0x97AC,0x97AE, 0x97B0,0x97B1,0x97B3,0x97B5,0x97B6,0x97B7,0x97B8,0x97B9, 0x97BA,0x97BB,0x97BC,0x97BD,0x97BE,0x97BF,0x97C0,0x97C1, 0x97C2,0x97C3,0x97C4,0x97C5,0x97C6,0x97C7,0x97C8,0x97C9, 0x97CA,0x97CB,0x97CC,0x97CD,0x97CE,0x97CF,0x97D0,0x97D1, 0x97D2,0x97D3,0x97D4,0x97D5,0x97D6,0x97D7,0x97D8,0x97D9, 0x97DA,0x97DB,0x97DC,0x97DD,0x97DE,0x97DF,0x97E0,0x97E1, 0x97E2,0x97E3,0xFFFD,0x97E4,0x97E5,0x97E8,0x97EE,0x97EF, 0x97F0,0x97F1,0x97F2,0x97F4,0x97F7,0x97F8,0x97F9,0x97FA, 0x97FB,0x97FC,0x97FD,0x97FE,0x97FF,0x9800,0x9801,0x9802, 0x9803,0x9804,0x9805,0x9806,0x9807,0x9808,0x9809,0x980A, 0x980B,0x980C,0x980D,0x980E,0x603C,0x605D,0x605A,0x6067, 0x6041,0x6059,0x6063,0x60AB,0x6106,0x610D,0x615D,0x61A9, 0x619D,0x61CB,0x61D1,0x6206,0x8080,0x807F,0x6C93,0x6CF6, 0x6DFC,0x77F6,0x77F8,0x7800,0x7809,0x7817,0x7818,0x7811, 0x65AB,0x782D,0x781C,0x781D,0x7839,0x783A,0x783B,0x781F, 0x783C,0x7825,0x782C,0x7823,0x7829,0x784E,0x786D,0x7856, 0x7857,0x7826,0x7850,0x7847,0x784C,0x786A,0x789B,0x7893, 0x789A,0x7887,0x789C,0x78A1,0x78A3,0x78B2,0x78B9,0x78A5, 0x78D4,0x78D9,0x78C9,0x78EC,0x78F2,0x7905,0x78F4,0x7913, 0x7924,0x791E,0x7934,0x9F9B,0x9EF9,0x9EFB,0x9EFC,0x76F1, 0x7704,0x770D,0x76F9,0x7707,0x7708,0x771A,0x7722,0x7719, 0x772D,0x7726,0x7735,0x7738,0x7750,0x7751,0x7747,0x7743, 0x775A,0x7768,0x980F,0x9810,0x9811,0x9812,0x9813,0x9814, 0x9815,0x9816,0x9817,0x9818,0x9819,0x981A,0x981B,0x981C, 0x981D,0x981E,0x981F,0x9820,0x9821,0x9822,0x9823,0x9824, 0x9825,0x9826,0x9827,0x9828,0x9829,0x982A,0x982B,0x982C, 0x982D,0x982E,0x982F,0x9830,0x9831,0x9832,0x9833,0x9834, 0x9835,0x9836,0x9837,0x9838,0x9839,0x983A,0x983B,0x983C, 0x983D,0x983E,0x983F,0x9840,0x9841,0x9842,0x9843,0x9844, 0x9845,0x9846,0x9847,0x9848,0x9849,0x984A,0x984B,0x984C, 0x984D,0xFFFD,0x984E,0x984F,0x9850,0x9851,0x9852,0x9853, 0x9854,0x9855,0x9856,0x9857,0x9858,0x9859,0x985A,0x985B, 0x985C,0x985D,0x985E,0x985F,0x9860,0x9861,0x9862,0x9863, 0x9864,0x9865,0x9866,0x9867,0x9868,0x9869,0x986A,0x986B, 0x986C,0x986D,0x986E,0x7762,0x7765,0x777F,0x778D,0x777D, 0x7780,0x778C,0x7791,0x779F,0x77A0,0x77B0,0x77B5,0x77BD, 0x753A,0x7540,0x754E,0x754B,0x7548,0x755B,0x7572,0x7579, 0x7583,0x7F58,0x7F61,0x7F5F,0x8A48,0x7F68,0x7F74,0x7F71, 0x7F79,0x7F81,0x7F7E,0x76CD,0x76E5,0x8832,0x9485,0x9486, 0x9487,0x948B,0x948A,0x948C,0x948D,0x948F,0x9490,0x9494, 0x9497,0x9495,0x949A,0x949B,0x949C,0x94A3,0x94A4,0x94AB, 0x94AA,0x94AD,0x94AC,0x94AF,0x94B0,0x94B2,0x94B4,0x94B6, 0x94B7,0x94B8,0x94B9,0x94BA,0x94BC,0x94BD,0x94BF,0x94C4, 0x94C8,0x94C9,0x94CA,0x94CB,0x94CC,0x94CD,0x94CE,0x94D0, 0x94D1,0x94D2,0x94D5,0x94D6,0x94D7,0x94D9,0x94D8,0x94DB, 0x94DE,0x94DF,0x94E0,0x94E2,0x94E4,0x94E5,0x94E7,0x94E8, 0x94EA,0x986F,0x9870,0x9871,0x9872,0x9873,0x9874,0x988B, 0x988E,0x9892,0x9895,0x9899,0x98A3,0x98A8,0x98A9,0x98AA, 0x98AB,0x98AC,0x98AD,0x98AE,0x98AF,0x98B0,0x98B1,0x98B2, 0x98B3,0x98B4,0x98B5,0x98B6,0x98B7,0x98B8,0x98B9,0x98BA, 0x98BB,0x98BC,0x98BD,0x98BE,0x98BF,0x98C0,0x98C1,0x98C2, 0x98C3,0x98C4,0x98C5,0x98C6,0x98C7,0x98C8,0x98C9,0x98CA, 0x98CB,0x98CC,0x98CD,0x98CF,0x98D0,0x98D4,0x98D6,0x98D7, 0x98DB,0x98DC,0x98DD,0x98E0,0x98E1,0x98E2,0x98E3,0x98E4, 0xFFFD,0x98E5,0x98E6,0x98E9,0x98EA,0x98EB,0x98EC,0x98ED, 0x98EE,0x98EF,0x98F0,0x98F1,0x98F2,0x98F3,0x98F4,0x98F5, 0x98F6,0x98F7,0x98F8,0x98F9,0x98FA,0x98FB,0x98FC,0x98FD, 0x98FE,0x98FF,0x9900,0x9901,0x9902,0x9903,0x9904,0x9905, 0x9906,0x9907,0x94E9,0x94EB,0x94EE,0x94EF,0x94F3,0x94F4, 0x94F5,0x94F7,0x94F9,0x94FC,0x94FD,0x94FF,0x9503,0x9502, 0x9506,0x9507,0x9509,0x950A,0x950D,0x950E,0x950F,0x9512, 0x9513,0x9514,0x9515,0x9516,0x9518,0x951B,0x951D,0x951E, 0x951F,0x9522,0x952A,0x952B,0x9529,0x952C,0x9531,0x9532, 0x9534,0x9536,0x9537,0x9538,0x953C,0x953E,0x953F,0x9542, 0x9535,0x9544,0x9545,0x9546,0x9549,0x954C,0x954E,0x954F, 0x9552,0x9553,0x9554,0x9556,0x9557,0x9558,0x9559,0x955B, 0x955E,0x955F,0x955D,0x9561,0x9562,0x9564,0x9565,0x9566, 0x9567,0x9568,0x9569,0x956A,0x956B,0x956C,0x956F,0x9571, 0x9572,0x9573,0x953A,0x77E7,0x77EC,0x96C9,0x79D5,0x79ED, 0x79E3,0x79EB,0x7A06,0x5D47,0x7A03,0x7A02,0x7A1E,0x7A14 }; const static uint16 gbkDecoderInnerIndex7[]= { 0x9908,0x9909,0x990A,0x990B,0x990C,0x990E,0x990F,0x9911, 0x9912,0x9913,0x9914,0x9915,0x9916,0x9917,0x9918,0x9919, 0x991A,0x991B,0x991C,0x991D,0x991E,0x991F,0x9920,0x9921, 0x9922,0x9923,0x9924,0x9925,0x9926,0x9927,0x9928,0x9929, 0x992A,0x992B,0x992C,0x992D,0x992F,0x9930,0x9931,0x9932, 0x9933,0x9934,0x9935,0x9936,0x9937,0x9938,0x9939,0x993A, 0x993B,0x993C,0x993D,0x993E,0x993F,0x9940,0x9941,0x9942, 0x9943,0x9944,0x9945,0x9946,0x9947,0x9948,0x9949,0xFFFD, 0x994A,0x994B,0x994C,0x994D,0x994E,0x994F,0x9950,0x9951, 0x9952,0x9953,0x9956,0x9957,0x9958,0x9959,0x995A,0x995B, 0x995C,0x995D,0x995E,0x995F,0x9960,0x9961,0x9962,0x9964, 0x9966,0x9973,0x9978,0x9979,0x997B,0x997E,0x9982,0x9983, 0x9989,0x7A39,0x7A37,0x7A51,0x9ECF,0x99A5,0x7A70,0x7688, 0x768E,0x7693,0x7699,0x76A4,0x74DE,0x74E0,0x752C,0x9E20, 0x9E22,0x9E28,0x9E29,0x9E2A,0x9E2B,0x9E2C,0x9E32,0x9E31, 0x9E36,0x9E38,0x9E37,0x9E39,0x9E3A,0x9E3E,0x9E41,0x9E42, 0x9E44,0x9E46,0x9E47,0x9E48,0x9E49,0x9E4B,0x9E4C,0x9E4E, 0x9E51,0x9E55,0x9E57,0x9E5A,0x9E5B,0x9E5C,0x9E5E,0x9E63, 0x9E66,0x9E67,0x9E68,0x9E69,0x9E6A,0x9E6B,0x9E6C,0x9E71, 0x9E6D,0x9E73,0x7592,0x7594,0x7596,0x75A0,0x759D,0x75AC, 0x75A3,0x75B3,0x75B4,0x75B8,0x75C4,0x75B1,0x75B0,0x75C3, 0x75C2,0x75D6,0x75CD,0x75E3,0x75E8,0x75E6,0x75E4,0x75EB, 0x75E7,0x7603,0x75F1,0x75FC,0x75FF,0x7610,0x7600,0x7605, 0x760C,0x7617,0x760A,0x7625,0x7618,0x7615,0x7619,0x998C, 0x998E,0x999A,0x999B,0x999C,0x999D,0x999E,0x999F,0x99A0, 0x99A1,0x99A2,0x99A3,0x99A4,0x99A6,0x99A7,0x99A9,0x99AA, 0x99AB,0x99AC,0x99AD,0x99AE,0x99AF,0x99B0,0x99B1,0x99B2, 0x99B3,0x99B4,0x99B5,0x99B6,0x99B7,0x99B8,0x99B9,0x99BA, 0x99BB,0x99BC,0x99BD,0x99BE,0x99BF,0x99C0,0x99C1,0x99C2, 0x99C3,0x99C4,0x99C5,0x99C6,0x99C7,0x99C8,0x99C9,0x99CA, 0x99CB,0x99CC,0x99CD,0x99CE,0x99CF,0x99D0,0x99D1,0x99D2, 0x99D3,0x99D4,0x99D5,0x99D6,0x99D7,0x99D8,0xFFFD,0x99D9, 0x99DA,0x99DB,0x99DC,0x99DD,0x99DE,0x99DF,0x99E0,0x99E1, 0x99E2,0x99E3,0x99E4,0x99E5,0x99E6,0x99E7,0x99E8,0x99E9, 0x99EA,0x99EB,0x99EC,0x99ED,0x99EE,0x99EF,0x99F0,0x99F1, 0x99F2,0x99F3,0x99F4,0x99F5,0x99F6,0x99F7,0x99F8,0x99F9, 0x761B,0x763C,0x7622,0x7620,0x7640,0x762D,0x7630,0x763F, 0x7635,0x7643,0x763E,0x7633,0x764D,0x765E,0x7654,0x765C, 0x7656,0x766B,0x766F,0x7FCA,0x7AE6,0x7A78,0x7A79,0x7A80, 0x7A86,0x7A88,0x7A95,0x7AA6,0x7AA0,0x7AAC,0x7AA8,0x7AAD, 0x7AB3,0x8864,0x8869,0x8872,0x887D,0x887F,0x8882,0x88A2, 0x88C6,0x88B7,0x88BC,0x88C9,0x88E2,0x88CE,0x88E3,0x88E5, 0x88F1,0x891A,0x88FC,0x88E8,0x88FE,0x88F0,0x8921,0x8919, 0x8913,0x891B,0x890A,0x8934,0x892B,0x8936,0x8941,0x8966, 0x897B,0x758B,0x80E5,0x76B2,0x76B4,0x77DC,0x8012,0x8014, 0x8016,0x801C,0x8020,0x8022,0x8025,0x8026,0x8027,0x8029, 0x8028,0x8031,0x800B,0x8035,0x8043,0x8046,0x804D,0x8052, 0x8069,0x8071,0x8983,0x9878,0x9880,0x9883,0x99FA,0x99FB, 0x99FC,0x99FD,0x99FE,0x99FF,0x9A00,0x9A01,0x9A02,0x9A03, 0x9A04,0x9A05,0x9A06,0x9A07,0x9A08,0x9A09,0x9A0A,0x9A0B, 0x9A0C,0x9A0D,0x9A0E,0x9A0F,0x9A10,0x9A11,0x9A12,0x9A13, 0x9A14,0x9A15,0x9A16,0x9A17,0x9A18,0x9A19,0x9A1A,0x9A1B, 0x9A1C,0x9A1D,0x9A1E,0x9A1F,0x9A20,0x9A21,0x9A22,0x9A23, 0x9A24,0x9A25,0x9A26,0x9A27,0x9A28,0x9A29,0x9A2A,0x9A2B, 0x9A2C,0x9A2D,0x9A2E,0x9A2F,0x9A30,0x9A31,0x9A32,0x9A33, 0x9A34,0x9A35,0x9A36,0x9A37,0x9A38,0xFFFD,0x9A39,0x9A3A, 0x9A3B,0x9A3C,0x9A3D,0x9A3E,0x9A3F,0x9A40,0x9A41,0x9A42, 0x9A43,0x9A44,0x9A45,0x9A46,0x9A47,0x9A48,0x9A49,0x9A4A, 0x9A4B,0x9A4C,0x9A4D,0x9A4E,0x9A4F,0x9A50,0x9A51,0x9A52, 0x9A53,0x9A54,0x9A55,0x9A56,0x9A57,0x9A58,0x9A59,0x9889, 0x988C,0x988D,0x988F,0x9894,0x989A,0x989B,0x989E,0x989F, 0x98A1,0x98A2,0x98A5,0x98A6,0x864D,0x8654,0x866C,0x866E, 0x867F,0x867A,0x867C,0x867B,0x86A8,0x868D,0x868B,0x86AC, 0x869D,0x86A7,0x86A3,0x86AA,0x8693,0x86A9,0x86B6,0x86C4, 0x86B5,0x86CE,0x86B0,0x86BA,0x86B1,0x86AF,0x86C9,0x86CF, 0x86B4,0x86E9,0x86F1,0x86F2,0x86ED,0x86F3,0x86D0,0x8713, 0x86DE,0x86F4,0x86DF,0x86D8,0x86D1,0x8703,0x8707,0x86F8, 0x8708,0x870A,0x870D,0x8709,0x8723,0x873B,0x871E,0x8725, 0x872E,0x871A,0x873E,0x8748,0x8734,0x8731,0x8729,0x8737, 0x873F,0x8782,0x8722,0x877D,0x877E,0x877B,0x8760,0x8770, 0x874C,0x876E,0x878B,0x8753,0x8763,0x877C,0x8764,0x8759, 0x8765,0x8793,0x87AF,0x87A8,0x87D2,0x9A5A,0x9A5B,0x9A5C, 0x9A5D,0x9A5E,0x9A5F,0x9A60,0x9A61,0x9A62,0x9A63,0x9A64, 0x9A65,0x9A66,0x9A67,0x9A68,0x9A69,0x9A6A,0x9A6B,0x9A72, 0x9A83,0x9A89,0x9A8D,0x9A8E,0x9A94,0x9A95,0x9A99,0x9AA6, 0x9AA9,0x9AAA,0x9AAB,0x9AAC,0x9AAD,0x9AAE,0x9AAF,0x9AB2, 0x9AB3,0x9AB4,0x9AB5,0x9AB9,0x9ABB,0x9ABD,0x9ABE,0x9ABF, 0x9AC3,0x9AC4,0x9AC6,0x9AC7,0x9AC8,0x9AC9,0x9ACA,0x9ACD, 0x9ACE,0x9ACF,0x9AD0,0x9AD2,0x9AD4,0x9AD5,0x9AD6,0x9AD7, 0x9AD9,0x9ADA,0x9ADB,0x9ADC,0xFFFD,0x9ADD,0x9ADE,0x9AE0, 0x9AE2,0x9AE3,0x9AE4,0x9AE5,0x9AE7,0x9AE8,0x9AE9,0x9AEA, 0x9AEC,0x9AEE,0x9AF0,0x9AF1,0x9AF2,0x9AF3,0x9AF4,0x9AF5, 0x9AF6,0x9AF7,0x9AF8,0x9AFA,0x9AFC,0x9AFD,0x9AFE,0x9AFF, 0x9B00,0x9B01,0x9B02,0x9B04,0x9B05,0x9B06,0x87C6,0x8788, 0x8785,0x87AD,0x8797,0x8783,0x87AB,0x87E5,0x87AC,0x87B5, 0x87B3,0x87CB,0x87D3,0x87BD,0x87D1,0x87C0,0x87CA,0x87DB, 0x87EA,0x87E0,0x87EE,0x8816,0x8813,0x87FE,0x880A,0x881B, 0x8821,0x8839,0x883C,0x7F36,0x7F42,0x7F44,0x7F45,0x8210, 0x7AFA,0x7AFD,0x7B08,0x7B03,0x7B04,0x7B15,0x7B0A,0x7B2B, 0x7B0F,0x7B47,0x7B38,0x7B2A,0x7B19,0x7B2E,0x7B31,0x7B20, 0x7B25,0x7B24,0x7B33,0x7B3E,0x7B1E,0x7B58,0x7B5A,0x7B45, 0x7B75,0x7B4C,0x7B5D,0x7B60,0x7B6E,0x7B7B,0x7B62,0x7B72, 0x7B71,0x7B90,0x7BA6,0x7BA7,0x7BB8,0x7BAC,0x7B9D,0x7BA8, 0x7B85,0x7BAA,0x7B9C,0x7BA2,0x7BAB,0x7BB4,0x7BD1,0x7BC1, 0x7BCC,0x7BDD,0x7BDA,0x7BE5,0x7BE6,0x7BEA,0x7C0C,0x7BFE, 0x7BFC,0x7C0F,0x7C16,0x7C0B,0x9B07,0x9B09,0x9B0A,0x9B0B, 0x9B0C,0x9B0D,0x9B0E,0x9B10,0x9B11,0x9B12,0x9B14,0x9B15, 0x9B16,0x9B17,0x9B18,0x9B19,0x9B1A,0x9B1B,0x9B1C,0x9B1D, 0x9B1E,0x9B20,0x9B21,0x9B22,0x9B24,0x9B25,0x9B26,0x9B27, 0x9B28,0x9B29,0x9B2A,0x9B2B,0x9B2C,0x9B2D,0x9B2E,0x9B30, 0x9B31,0x9B33,0x9B34,0x9B35,0x9B36,0x9B37,0x9B38,0x9B39, 0x9B3A,0x9B3D,0x9B3E,0x9B3F,0x9B40,0x9B46,0x9B4A,0x9B4B, 0x9B4C,0x9B4E,0x9B50,0x9B52,0x9B53,0x9B55,0x9B56,0x9B57, 0x9B58,0x9B59,0x9B5A,0xFFFD,0x9B5B,0x9B5C,0x9B5D,0x9B5E, 0x9B5F,0x9B60,0x9B61,0x9B62,0x9B63,0x9B64,0x9B65,0x9B66, 0x9B67,0x9B68,0x9B69,0x9B6A,0x9B6B,0x9B6C,0x9B6D,0x9B6E, 0x9B6F,0x9B70,0x9B71,0x9B72,0x9B73,0x9B74,0x9B75,0x9B76, 0x9B77,0x9B78,0x9B79,0x9B7A,0x9B7B,0x7C1F,0x7C2A,0x7C26, 0x7C38,0x7C41,0x7C40,0x81FE,0x8201,0x8202,0x8204,0x81EC, 0x8844,0x8221,0x8222,0x8223,0x822D,0x822F,0x8228,0x822B, 0x8238,0x823B,0x8233,0x8234,0x823E,0x8244,0x8249,0x824B, 0x824F,0x825A,0x825F,0x8268,0x887E,0x8885,0x8888,0x88D8, 0x88DF,0x895E,0x7F9D,0x7F9F,0x7FA7,0x7FAF,0x7FB0,0x7FB2, 0x7C7C,0x6549,0x7C91,0x7C9D,0x7C9C,0x7C9E,0x7CA2,0x7CB2, 0x7CBC,0x7CBD,0x7CC1,0x7CC7,0x7CCC,0x7CCD,0x7CC8,0x7CC5, 0x7CD7,0x7CE8,0x826E,0x66A8,0x7FBF,0x7FCE,0x7FD5,0x7FE5, 0x7FE1,0x7FE6,0x7FE9,0x7FEE,0x7FF3,0x7CF8,0x7D77,0x7DA6, 0x7DAE,0x7E47,0x7E9B,0x9EB8,0x9EB4,0x8D73,0x8D84,0x8D94, 0x8D91,0x8DB1,0x8D67,0x8D6D,0x8C47,0x8C49,0x914A,0x9150, 0x914E,0x914F,0x9164,0x9B7C,0x9B7D,0x9B7E,0x9B7F,0x9B80, 0x9B81,0x9B82,0x9B83,0x9B84,0x9B85,0x9B86,0x9B87,0x9B88, 0x9B89,0x9B8A,0x9B8B,0x9B8C,0x9B8D,0x9B8E,0x9B8F,0x9B90, 0x9B91,0x9B92,0x9B93,0x9B94,0x9B95,0x9B96,0x9B97,0x9B98, 0x9B99,0x9B9A,0x9B9B,0x9B9C,0x9B9D,0x9B9E,0x9B9F,0x9BA0, 0x9BA1,0x9BA2,0x9BA3,0x9BA4,0x9BA5,0x9BA6,0x9BA7,0x9BA8, 0x9BA9,0x9BAA,0x9BAB,0x9BAC,0x9BAD,0x9BAE,0x9BAF,0x9BB0, 0x9BB1,0x9BB2,0x9BB3,0x9BB4,0x9BB5,0x9BB6,0x9BB7,0x9BB8, 0x9BB9,0x9BBA,0xFFFD,0x9BBB,0x9BBC,0x9BBD,0x9BBE,0x9BBF, 0x9BC0,0x9BC1,0x9BC2,0x9BC3,0x9BC4,0x9BC5,0x9BC6,0x9BC7, 0x9BC8,0x9BC9,0x9BCA,0x9BCB,0x9BCC,0x9BCD,0x9BCE,0x9BCF, 0x9BD0,0x9BD1,0x9BD2,0x9BD3,0x9BD4,0x9BD5,0x9BD6,0x9BD7, 0x9BD8,0x9BD9,0x9BDA,0x9BDB,0x9162,0x9161,0x9170,0x9169, 0x916F,0x917D,0x917E,0x9172,0x9174,0x9179,0x918C,0x9185, 0x9190,0x918D,0x9191,0x91A2,0x91A3,0x91AA,0x91AD,0x91AE, 0x91AF,0x91B5,0x91B4,0x91BA,0x8C55,0x9E7E,0x8DB8,0x8DEB, 0x8E05,0x8E59,0x8E69,0x8DB5,0x8DBF,0x8DBC,0x8DBA,0x8DC4, 0x8DD6,0x8DD7,0x8DDA,0x8DDE,0x8DCE,0x8DCF,0x8DDB,0x8DC6, 0x8DEC,0x8DF7,0x8DF8,0x8DE3,0x8DF9,0x8DFB,0x8DE4,0x8E09, 0x8DFD,0x8E14,0x8E1D,0x8E1F,0x8E2C,0x8E2E,0x8E23,0x8E2F, 0x8E3A,0x8E40,0x8E39,0x8E35,0x8E3D,0x8E31,0x8E49,0x8E41, 0x8E42,0x8E51,0x8E52,0x8E4A,0x8E70,0x8E76,0x8E7C,0x8E6F, 0x8E74,0x8E85,0x8E8F,0x8E94,0x8E90,0x8E9C,0x8E9E,0x8C78, 0x8C82,0x8C8A,0x8C85,0x8C98,0x8C94,0x659B,0x89D6,0x89DE, 0x89DA,0x89DC,0x9BDC,0x9BDD,0x9BDE,0x9BDF,0x9BE0,0x9BE1, 0x9BE2,0x9BE3,0x9BE4,0x9BE5,0x9BE6,0x9BE7,0x9BE8,0x9BE9, 0x9BEA,0x9BEB,0x9BEC,0x9BED,0x9BEE,0x9BEF,0x9BF0,0x9BF1, 0x9BF2,0x9BF3,0x9BF4,0x9BF5,0x9BF6,0x9BF7,0x9BF8,0x9BF9, 0x9BFA,0x9BFB,0x9BFC,0x9BFD,0x9BFE,0x9BFF,0x9C00,0x9C01, 0x9C02,0x9C03,0x9C04,0x9C05,0x9C06,0x9C07,0x9C08,0x9C09, 0x9C0A,0x9C0B,0x9C0C,0x9C0D,0x9C0E,0x9C0F,0x9C10,0x9C11, 0x9C12,0x9C13,0x9C14,0x9C15,0x9C16,0x9C17,0x9C18,0x9C19, 0x9C1A,0xFFFD,0x9C1B,0x9C1C,0x9C1D,0x9C1E,0x9C1F,0x9C20, 0x9C21,0x9C22,0x9C23,0x9C24,0x9C25,0x9C26,0x9C27,0x9C28, 0x9C29,0x9C2A,0x9C2B,0x9C2C,0x9C2D,0x9C2E,0x9C2F,0x9C30, 0x9C31,0x9C32,0x9C33,0x9C34,0x9C35,0x9C36,0x9C37,0x9C38, 0x9C39,0x9C3A,0x9C3B,0x89E5,0x89EB,0x89EF,0x8A3E,0x8B26, 0x9753,0x96E9,0x96F3,0x96EF,0x9706,0x9701,0x9708,0x970F, 0x970E,0x972A,0x972D,0x9730,0x973E,0x9F80,0x9F83,0x9F85, 0x9F86,0x9F87,0x9F88,0x9F89,0x9F8A,0x9F8C,0x9EFE,0x9F0B, 0x9F0D,0x96B9,0x96BC,0x96BD,0x96CE,0x96D2,0x77BF,0x96E0, 0x928E,0x92AE,0x92C8,0x933E,0x936A,0x93CA,0x938F,0x943E, 0x946B,0x9C7F,0x9C82,0x9C85,0x9C86,0x9C87,0x9C88,0x7A23, 0x9C8B,0x9C8E,0x9C90,0x9C91,0x9C92,0x9C94,0x9C95,0x9C9A, 0x9C9B,0x9C9E,0x9C9F,0x9CA0,0x9CA1,0x9CA2,0x9CA3,0x9CA5, 0x9CA6,0x9CA7,0x9CA8,0x9CA9,0x9CAB,0x9CAD,0x9CAE,0x9CB0, 0x9CB1,0x9CB2,0x9CB3,0x9CB4,0x9CB5,0x9CB6,0x9CB7,0x9CBA, 0x9CBB,0x9CBC,0x9CBD,0x9CC4,0x9CC5,0x9CC6,0x9CC7,0x9CCA, 0x9CCB,0x9C3C,0x9C3D,0x9C3E,0x9C3F,0x9C40,0x9C41,0x9C42, 0x9C43,0x9C44,0x9C45,0x9C46,0x9C47,0x9C48,0x9C49,0x9C4A, 0x9C4B,0x9C4C,0x9C4D,0x9C4E,0x9C4F,0x9C50,0x9C51,0x9C52, 0x9C53,0x9C54,0x9C55,0x9C56,0x9C57,0x9C58,0x9C59,0x9C5A, 0x9C5B,0x9C5C,0x9C5D,0x9C5E,0x9C5F,0x9C60,0x9C61,0x9C62, 0x9C63,0x9C64,0x9C65,0x9C66,0x9C67,0x9C68,0x9C69,0x9C6A, 0x9C6B,0x9C6C,0x9C6D,0x9C6E,0x9C6F,0x9C70,0x9C71,0x9C72, 0x9C73,0x9C74,0x9C75,0x9C76,0x9C77,0x9C78,0x9C79,0x9C7A, 0xFFFD,0x9C7B,0x9C7D,0x9C7E,0x9C80,0x9C83,0x9C84,0x9C89, 0x9C8A,0x9C8C,0x9C8F,0x9C93,0x9C96,0x9C97,0x9C98,0x9C99, 0x9C9D,0x9CAA,0x9CAC,0x9CAF,0x9CB9,0x9CBE,0x9CBF,0x9CC0, 0x9CC1,0x9CC2,0x9CC8,0x9CC9,0x9CD1,0x9CD2,0x9CDA,0x9CDB, 0x9CE0,0x9CE1,0x9CCC,0x9CCD,0x9CCE,0x9CCF,0x9CD0,0x9CD3, 0x9CD4,0x9CD5,0x9CD7,0x9CD8,0x9CD9,0x9CDC,0x9CDD,0x9CDF, 0x9CE2,0x977C,0x9785,0x9791,0x9792,0x9794,0x97AF,0x97AB, 0x97A3,0x97B2,0x97B4,0x9AB1,0x9AB0,0x9AB7,0x9E58,0x9AB6, 0x9ABA,0x9ABC,0x9AC1,0x9AC0,0x9AC5,0x9AC2,0x9ACB,0x9ACC, 0x9AD1,0x9B45,0x9B43,0x9B47,0x9B49,0x9B48,0x9B4D,0x9B51, 0x98E8,0x990D,0x992E,0x9955,0x9954,0x9ADF,0x9AE1,0x9AE6, 0x9AEF,0x9AEB,0x9AFB,0x9AED,0x9AF9,0x9B08,0x9B0F,0x9B13, 0x9B1F,0x9B23,0x9EBD,0x9EBE,0x7E3B,0x9E82,0x9E87,0x9E88, 0x9E8B,0x9E92,0x93D6,0x9E9D,0x9E9F,0x9EDB,0x9EDC,0x9EDD, 0x9EE0,0x9EDF,0x9EE2,0x9EE9,0x9EE7,0x9EE5,0x9EEA,0x9EEF, 0x9F22,0x9F2C,0x9F2F,0x9F39,0x9F37,0x9F3D,0x9F3E,0x9F44, 0x9CE3,0x9CE4,0x9CE5,0x9CE6,0x9CE7,0x9CE8,0x9CE9,0x9CEA, 0x9CEB,0x9CEC,0x9CED,0x9CEE,0x9CEF,0x9CF0,0x9CF1,0x9CF2, 0x9CF3,0x9CF4,0x9CF5,0x9CF6,0x9CF7,0x9CF8,0x9CF9,0x9CFA, 0x9CFB,0x9CFC,0x9CFD,0x9CFE,0x9CFF,0x9D00,0x9D01,0x9D02, 0x9D03,0x9D04,0x9D05,0x9D06,0x9D07,0x9D08,0x9D09,0x9D0A, 0x9D0B,0x9D0C,0x9D0D,0x9D0E,0x9D0F,0x9D10,0x9D11,0x9D12, 0x9D13,0x9D14,0x9D15,0x9D16,0x9D17,0x9D18,0x9D19,0x9D1A, 0x9D1B,0x9D1C,0x9D1D,0x9D1E,0x9D1F,0x9D20,0x9D21,0xFFFD, 0x9D22,0x9D23,0x9D24,0x9D25,0x9D26,0x9D27,0x9D28,0x9D29, 0x9D2A,0x9D2B,0x9D2C,0x9D2D,0x9D2E,0x9D2F,0x9D30,0x9D31, 0x9D32,0x9D33,0x9D34,0x9D35,0x9D36,0x9D37,0x9D38,0x9D39, 0x9D3A,0x9D3B,0x9D3C,0x9D3D,0x9D3E,0x9D3F,0x9D40,0x9D41, 0x9D42,0xE234,0xE235,0xE236,0xE237,0xE238,0xE239,0xE23A, 0xE23B,0xE23C,0xE23D,0xE23E,0xE23F,0xE240,0xE241,0xE242, 0xE243,0xE244,0xE245,0xE246,0xE247,0xE248,0xE249,0xE24A, 0xE24B,0xE24C,0xE24D,0xE24E,0xE24F,0xE250,0xE251,0xE252, 0xE253,0xE254,0xE255,0xE256,0xE257,0xE258,0xE259,0xE25A, 0xE25B,0xE25C,0xE25D,0xE25E,0xE25F,0xE260,0xE261,0xE262, 0xE263,0xE264,0xE265,0xE266,0xE267,0xE268,0xE269,0xE26A, 0xE26B,0xE26C,0xE26D,0xE26E,0xE26F,0xE270,0xE271,0xE272, 0xE273,0xE274,0xE275,0xE276,0xE277,0xE278,0xE279,0xE27A, 0xE27B,0xE27C,0xE27D,0xE27E,0xE27F,0xE280,0xE281,0xE282, 0xE283,0xE284,0xE285,0xE286,0xE287,0xE288,0xE289,0xE28A, 0xE28B,0xE28C,0xE28D,0xE28E,0xE28F,0xE290,0xE291,0x9D43, 0x9D44,0x9D45,0x9D46,0x9D47,0x9D48,0x9D49,0x9D4A,0x9D4B, 0x9D4C,0x9D4D,0x9D4E,0x9D4F,0x9D50,0x9D51,0x9D52,0x9D53, 0x9D54,0x9D55,0x9D56,0x9D57,0x9D58,0x9D59,0x9D5A,0x9D5B, 0x9D5C,0x9D5D,0x9D5E,0x9D5F,0x9D60,0x9D61,0x9D62,0x9D63, 0x9D64,0x9D65,0x9D66,0x9D67,0x9D68,0x9D69,0x9D6A,0x9D6B, 0x9D6C,0x9D6D,0x9D6E,0x9D6F,0x9D70,0x9D71,0x9D72,0x9D73, 0x9D74,0x9D75,0x9D76,0x9D77,0x9D78,0x9D79,0x9D7A,0x9D7B, 0x9D7C,0x9D7D,0x9D7E,0x9D7F,0x9D80,0x9D81,0xFFFD,0x9D82, 0x9D83,0x9D84,0x9D85,0x9D86,0x9D87,0x9D88,0x9D89,0x9D8A, 0x9D8B,0x9D8C,0x9D8D,0x9D8E,0x9D8F,0x9D90,0x9D91,0x9D92, 0x9D93,0x9D94,0x9D95,0x9D96,0x9D97,0x9D98,0x9D99,0x9D9A, 0x9D9B,0x9D9C,0x9D9D,0x9D9E,0x9D9F,0x9DA0,0x9DA1,0x9DA2, 0xE292,0xE293,0xE294,0xE295,0xE296,0xE297,0xE298,0xE299, 0xE29A,0xE29B,0xE29C,0xE29D,0xE29E,0xE29F,0xE2A0,0xE2A1, 0xE2A2,0xE2A3,0xE2A4,0xE2A5,0xE2A6,0xE2A7,0xE2A8,0xE2A9, 0xE2AA,0xE2AB,0xE2AC,0xE2AD,0xE2AE,0xE2AF,0xE2B0,0xE2B1, 0xE2B2,0xE2B3,0xE2B4,0xE2B5,0xE2B6,0xE2B7,0xE2B8,0xE2B9, 0xE2BA,0xE2BB,0xE2BC,0xE2BD,0xE2BE,0xE2BF,0xE2C0,0xE2C1, 0xE2C2,0xE2C3,0xE2C4,0xE2C5,0xE2C6,0xE2C7,0xE2C8,0xE2C9, 0xE2CA,0xE2CB,0xE2CC,0xE2CD,0xE2CE,0xE2CF,0xE2D0,0xE2D1, 0xE2D2,0xE2D3,0xE2D4,0xE2D5,0xE2D6,0xE2D7,0xE2D8,0xE2D9, 0xE2DA,0xE2DB,0xE2DC,0xE2DD,0xE2DE,0xE2DF,0xE2E0,0xE2E1, 0xE2E2,0xE2E3,0xE2E4,0xE2E5,0xE2E6,0xE2E7,0xE2E8,0xE2E9, 0xE2EA,0xE2EB,0xE2EC,0xE2ED,0xE2EE,0xE2EF,0x9DA3,0x9DA4, 0x9DA5,0x9DA6,0x9DA7,0x9DA8,0x9DA9,0x9DAA,0x9DAB,0x9DAC, 0x9DAD,0x9DAE,0x9DAF,0x9DB0,0x9DB1,0x9DB2,0x9DB3,0x9DB4, 0x9DB5,0x9DB6,0x9DB7,0x9DB8,0x9DB9,0x9DBA,0x9DBB,0x9DBC, 0x9DBD,0x9DBE,0x9DBF,0x9DC0,0x9DC1,0x9DC2,0x9DC3,0x9DC4, 0x9DC5,0x9DC6,0x9DC7,0x9DC8,0x9DC9,0x9DCA,0x9DCB,0x9DCC, 0x9DCD,0x9DCE,0x9DCF,0x9DD0,0x9DD1,0x9DD2,0x9DD3,0x9DD4, 0x9DD5,0x9DD6,0x9DD7,0x9DD8,0x9DD9,0x9DDA,0x9DDB,0x9DDC, 0x9DDD,0x9DDE,0x9DDF,0x9DE0,0x9DE1,0xFFFD,0x9DE2,0x9DE3, 0x9DE4,0x9DE5,0x9DE6,0x9DE7,0x9DE8,0x9DE9,0x9DEA,0x9DEB, 0x9DEC,0x9DED,0x9DEE,0x9DEF,0x9DF0,0x9DF1,0x9DF2,0x9DF3, 0x9DF4,0x9DF5,0x9DF6,0x9DF7,0x9DF8,0x9DF9,0x9DFA,0x9DFB, 0x9DFC,0x9DFD,0x9DFE,0x9DFF,0x9E00,0x9E01,0x9E02,0xE2F0, 0xE2F1,0xE2F2,0xE2F3,0xE2F4,0xE2F5,0xE2F6,0xE2F7,0xE2F8, 0xE2F9,0xE2FA,0xE2FB,0xE2FC,0xE2FD,0xE2FE,0xE2FF,0xE300, 0xE301,0xE302,0xE303,0xE304,0xE305,0xE306,0xE307,0xE308, 0xE309,0xE30A,0xE30B,0xE30C,0xE30D,0xE30E,0xE30F,0xE310, 0xE311,0xE312,0xE313,0xE314,0xE315,0xE316,0xE317,0xE318, 0xE319,0xE31A,0xE31B,0xE31C,0xE31D,0xE31E,0xE31F,0xE320, 0xE321,0xE322,0xE323,0xE324,0xE325,0xE326,0xE327,0xE328, 0xE329,0xE32A,0xE32B,0xE32C,0xE32D,0xE32E,0xE32F,0xE330, 0xE331,0xE332,0xE333,0xE334,0xE335,0xE336,0xE337,0xE338, 0xE339,0xE33A,0xE33B,0xE33C,0xE33D,0xE33E,0xE33F,0xE340, 0xE341,0xE342,0xE343,0xE344,0xE345,0xE346,0xE347,0xE348, 0xE349,0xE34A,0xE34B,0xE34C,0xE34D,0x9E03,0x9E04,0x9E05, 0x9E06,0x9E07,0x9E08,0x9E09,0x9E0A,0x9E0B,0x9E0C,0x9E0D, 0x9E0E,0x9E0F,0x9E10,0x9E11,0x9E12,0x9E13,0x9E14,0x9E15, 0x9E16,0x9E17,0x9E18,0x9E19,0x9E1A,0x9E1B,0x9E1C,0x9E1D, 0x9E1E,0x9E24,0x9E27,0x9E2E,0x9E30,0x9E34,0x9E3B,0x9E3C, 0x9E40,0x9E4D,0x9E50,0x9E52,0x9E53,0x9E54,0x9E56,0x9E59, 0x9E5D,0x9E5F,0x9E60,0x9E61,0x9E62,0x9E65,0x9E6E,0x9E6F, 0x9E72,0x9E74,0x9E75,0x9E76,0x9E77,0x9E78,0x9E79,0x9E7A, 0x9E7B,0x9E7C,0x9E7D,0x9E80,0xFFFD,0x9E81,0x9E83,0x9E84, 0x9E85,0x9E86,0x9E89,0x9E8A,0x9E8C,0x9E8D,0x9E8E,0x9E8F, 0x9E90,0x9E91,0x9E94,0x9E95,0x9E96,0x9E97,0x9E98,0x9E99, 0x9E9A,0x9E9B,0x9E9C,0x9E9E,0x9EA0,0x9EA1,0x9EA2,0x9EA3, 0x9EA4,0x9EA5,0x9EA7,0x9EA8,0x9EA9,0x9EAA,0xE34E,0xE34F, 0xE350,0xE351,0xE352,0xE353,0xE354,0xE355,0xE356,0xE357, 0xE358,0xE359,0xE35A,0xE35B,0xE35C,0xE35D,0xE35E,0xE35F, 0xE360,0xE361,0xE362,0xE363,0xE364,0xE365,0xE366,0xE367, 0xE368,0xE369,0xE36A,0xE36B,0xE36C,0xE36D,0xE36E,0xE36F, 0xE370,0xE371,0xE372,0xE373,0xE374,0xE375,0xE376,0xE377, 0xE378,0xE379,0xE37A,0xE37B,0xE37C,0xE37D,0xE37E,0xE37F, 0xE380,0xE381,0xE382,0xE383,0xE384,0xE385,0xE386,0xE387, 0xE388,0xE389,0xE38A,0xE38B,0xE38C,0xE38D,0xE38E,0xE38F, 0xE390,0xE391,0xE392,0xE393,0xE394,0xE395,0xE396,0xE397, 0xE398,0xE399,0xE39A,0xE39B,0xE39C,0xE39D,0xE39E,0xE39F, 0xE3A0,0xE3A1,0xE3A2,0xE3A3,0xE3A4,0xE3A5,0xE3A6,0xE3A7, 0xE3A8,0xE3A9,0xE3AA,0xE3AB,0x9EAB,0x9EAC,0x9EAD,0x9EAE, 0x9EAF,0x9EB0,0x9EB1,0x9EB2,0x9EB3,0x9EB5,0x9EB6,0x9EB7, 0x9EB9,0x9EBA,0x9EBC,0x9EBF,0x9EC0,0x9EC1,0x9EC2,0x9EC3, 0x9EC5,0x9EC6,0x9EC7,0x9EC8,0x9ECA,0x9ECB,0x9ECC,0x9ED0, 0x9ED2,0x9ED3,0x9ED5,0x9ED6,0x9ED7,0x9ED9,0x9EDA,0x9EDE, 0x9EE1,0x9EE3,0x9EE4,0x9EE6,0x9EE8,0x9EEB,0x9EEC,0x9EED, 0x9EEE,0x9EF0,0x9EF1,0x9EF2,0x9EF3,0x9EF4,0x9EF5,0x9EF6, 0x9EF7,0x9EF8,0x9EFA,0x9EFD,0x9EFF,0x9F00,0x9F01,0x9F02, 0x9F03,0x9F04,0x9F05,0xFFFD,0x9F06,0x9F07,0x9F08,0x9F09, 0x9F0A,0x9F0C,0x9F0F,0x9F11,0x9F12,0x9F14,0x9F15,0x9F16, 0x9F18,0x9F1A,0x9F1B,0x9F1C,0x9F1D,0x9F1E,0x9F1F,0x9F21, 0x9F23,0x9F24,0x9F25,0x9F26,0x9F27,0x9F28,0x9F29,0x9F2A, 0x9F2B,0x9F2D,0x9F2E,0x9F30,0x9F31,0xE3AC,0xE3AD,0xE3AE, 0xE3AF,0xE3B0,0xE3B1,0xE3B2,0xE3B3,0xE3B4,0xE3B5,0xE3B6, 0xE3B7,0xE3B8,0xE3B9,0xE3BA,0xE3BB,0xE3BC,0xE3BD,0xE3BE, 0xE3BF,0xE3C0,0xE3C1,0xE3C2,0xE3C3,0xE3C4,0xE3C5,0xE3C6, 0xE3C7,0xE3C8,0xE3C9,0xE3CA,0xE3CB,0xE3CC,0xE3CD,0xE3CE, 0xE3CF,0xE3D0,0xE3D1,0xE3D2,0xE3D3,0xE3D4,0xE3D5,0xE3D6, 0xE3D7,0xE3D8,0xE3D9,0xE3DA,0xE3DB,0xE3DC,0xE3DD,0xE3DE, 0xE3DF,0xE3E0,0xE3E1,0xE3E2,0xE3E3,0xE3E4,0xE3E5,0xE3E6, 0xE3E7,0xE3E8,0xE3E9,0xE3EA,0xE3EB,0xE3EC,0xE3ED,0xE3EE, 0xE3EF,0xE3F0,0xE3F1,0xE3F2,0xE3F3,0xE3F4,0xE3F5,0xE3F6, 0xE3F7,0xE3F8,0xE3F9,0xE3FA,0xE3FB,0xE3FC,0xE3FD,0xE3FE, 0xE3FF,0xE400,0xE401,0xE402,0xE403,0xE404,0xE405,0xE406, 0xE407,0xE408,0xE409,0x9F32,0x9F33,0x9F34,0x9F35,0x9F36, 0x9F38,0x9F3A,0x9F3C,0x9F3F,0x9F40,0x9F41,0x9F42,0x9F43, 0x9F45,0x9F46,0x9F47,0x9F48,0x9F49,0x9F4A,0x9F4B,0x9F4C, 0x9F4D,0x9F4E,0x9F4F,0x9F52,0x9F53,0x9F54,0x9F55,0x9F56, 0x9F57,0x9F58,0x9F59,0x9F5A,0x9F5B,0x9F5C,0x9F5D,0x9F5E, 0x9F5F,0x9F60,0x9F61,0x9F62,0x9F63,0x9F64,0x9F65,0x9F66, 0x9F67,0x9F68,0x9F69,0x9F6A,0x9F6B,0x9F6C,0x9F6D,0x9F6E, 0x9F6F,0x9F70,0x9F71,0x9F72,0x9F73,0x9F74,0x9F75,0x9F76, 0x9F77,0x9F78,0xFFFD,0x9F79,0x9F7A,0x9F7B,0x9F7C,0x9F7D, 0x9F7E,0x9F81,0x9F82,0x9F8D,0x9F8E,0x9F8F,0x9F90,0x9F91, 0x9F92,0x9F93,0x9F94,0x9F95,0x9F96,0x9F97,0x9F98,0x9F9C, 0x9F9D,0x9F9E,0x9FA1,0x9FA2,0x9FA3,0x9FA4,0x9FA5,0xF92C, 0xF979,0xF995,0xF9E7,0xF9F1,0xE40A,0xE40B,0xE40C,0xE40D, 0xE40E,0xE40F,0xE410,0xE411,0xE412,0xE413,0xE414,0xE415, 0xE416,0xE417,0xE418,0xE419,0xE41A,0xE41B,0xE41C,0xE41D, 0xE41E,0xE41F,0xE420,0xE421,0xE422,0xE423,0xE424,0xE425, 0xE426,0xE427,0xE428,0xE429,0xE42A,0xE42B,0xE42C,0xE42D, 0xE42E,0xE42F,0xE430,0xE431,0xE432,0xE433,0xE434,0xE435, 0xE436,0xE437,0xE438,0xE439,0xE43A,0xE43B,0xE43C,0xE43D, 0xE43E,0xE43F,0xE440,0xE441,0xE442,0xE443,0xE444,0xE445, 0xE446,0xE447,0xE448,0xE449,0xE44A,0xE44B,0xE44C,0xE44D, 0xE44E,0xE44F,0xE450,0xE451,0xE452,0xE453,0xE454,0xE455, 0xE456,0xE457,0xE458,0xE459,0xE45A,0xE45B,0xE45C,0xE45D, 0xE45E,0xE45F,0xE460,0xE461,0xE462,0xE463,0xE464,0xE465, 0xE466,0xE467,0xFA0C,0xFA0D,0xFA0E,0xFA0F,0xFA11,0xFA13, 0xFA14,0xFA18,0xFA1F,0xFA20,0xFA21,0xFA23,0xFA24,0xFA27, 0xFA28,0xFA29,0xE815,0xE816,0xE817,0xE818,0xE819,0xE81A, 0xE81B,0xE81C,0xE81D,0xE81E,0xE81F,0xE820,0xE821,0xE822, 0xE823,0xE824,0xE825,0xE826,0xE827,0xE828,0xE829,0xE82A, 0xE82B,0xE82C,0xE82D,0xE82E,0xE82F,0xE830,0xE831,0xE832, 0xE833,0xE834,0xE835,0xE836,0xE837,0xE838,0xE839,0xE83A, 0xE83B,0xE83C,0xE83D,0xE83E,0xE83F,0xE840,0xE841,0xE842, 0xE843,0xFFFD,0xE844,0xE845,0xE846,0xE847,0xE848,0xE849, 0xE84A,0xE84B,0xE84C,0xE84D,0xE84E,0xE84F,0xE850,0xE851, 0xE852,0xE853,0xE854,0xE855,0xE856,0xE857,0xE858,0xE859, 0xE85A,0xE85B,0xE85C,0xE85D,0xE85E,0xE85F,0xE860,0xE861, 0xE862,0xE863,0xE864,0xE468,0xE469,0xE46A,0xE46B,0xE46C, 0xE46D,0xE46E,0xE46F,0xE470,0xE471,0xE472,0xE473,0xE474, 0xE475,0xE476,0xE477,0xE478,0xE479,0xE47A,0xE47B,0xE47C, 0xE47D,0xE47E,0xE47F,0xE480,0xE481,0xE482,0xE483,0xE484, 0xE485,0xE486,0xE487,0xE488,0xE489,0xE48A,0xE48B,0xE48C, 0xE48D,0xE48E,0xE48F,0xE490,0xE491,0xE492,0xE493,0xE494, 0xE495,0xE496,0xE497,0xE498,0xE499,0xE49A,0xE49B,0xE49C, 0xE49D,0xE49E,0xE49F,0xE4A0,0xE4A1,0xE4A2,0xE4A3,0xE4A4, 0xE4A5,0xE4A6,0xE4A7,0xE4A8,0xE4A9,0xE4AA,0xE4AB,0xE4AC, 0xE4AD,0xE4AE,0xE4AF,0xE4B0,0xE4B1,0xE4B2,0xE4B3,0xE4B4, 0xE4B5,0xE4B6,0xE4B7,0xE4B8,0xE4B9,0xE4BA,0xE4BB,0xE4BC, 0xE4BD,0xE4BE,0xE4BF,0xE4C0,0xE4C1,0xE4C2,0xE4C3,0xE4C4, 0xE4C5 }; const static uint16 gbkEncoderInnerIndex0[]= { 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x00FF,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0xA1E8,0x0000,0x0000,0xA1EC, 0xA1A7,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA1E3,0xA1C0,0x0000,0x0000,0x0000,0x0000,0x0000,0xA1A4, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xA1C1, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA8A4,0xA8A2,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA8A8,0xA8A6,0xA8BA,0x0000,0xA8AC,0xA8AA,0x0000,0x0000, 0x0000,0x0000,0xA8B0,0xA8AE,0x0000,0x0000,0x0000,0xA1C2, 0x0000,0xA8B4,0xA8B2,0x0000,0xA8B9,0x0000,0x0000,0x0000, 0x0000,0xA8A1,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0xA8A5,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0xA8A7,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0xA8A9,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0xA8BD,0x0000,0x0000,0x0000, 0xA8BE,0x0000,0x0000,0x0000,0x0000,0xA8AD,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0xA8B1,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xA8A3,0x0000, 0xA8AB,0x0000,0xA8AF,0x0000,0xA8B3,0x0000,0xA8B5,0x0000, 0xA8B6,0x0000,0xA8B7,0x0000,0xA8B8,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0xA8BB,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0xA8C0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xA1A6, 0x0000,0xA1A5,0xA840,0xA841,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0xA842,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0xA6A1,0xA6A2,0xA6A3,0xA6A4,0xA6A5,0xA6A6,0xA6A7, 0xA6A8,0xA6A9,0xA6AA,0xA6AB,0xA6AC,0xA6AD,0xA6AE,0xA6AF, 0xA6B0,0xA6B1,0x0000,0xA6B2,0xA6B3,0xA6B4,0xA6B5,0xA6B6, 0xA6B7,0xA6B8,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0xA6C1,0xA6C2,0xA6C3,0xA6C4,0xA6C5,0xA6C6,0xA6C7, 0xA6C8,0xA6C9,0xA6CA,0xA6CB,0xA6CC,0xA6CD,0xA6CE,0xA6CF, 0xA6D0,0xA6D1,0x0000,0xA6D2,0xA6D3,0xA6D4,0xA6D5,0xA6D6, 0xA6D7,0xA6D8,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0xA7A7,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA7A1,0xA7A2,0xA7A3,0xA7A4,0xA7A5,0xA7A6,0xA7A8,0xA7A9, 0xA7AA,0xA7AB,0xA7AC,0xA7AD,0xA7AE,0xA7AF,0xA7B0,0xA7B1, 0xA7B2,0xA7B3,0xA7B4,0xA7B5,0xA7B6,0xA7B7,0xA7B8,0xA7B9, 0xA7BA,0xA7BB,0xA7BC,0xA7BD,0xA7BE,0xA7BF,0xA7C0,0xA7C1, 0xA7D1,0xA7D2,0xA7D3,0xA7D4,0xA7D5,0xA7D6,0xA7D8,0xA7D9, 0xA7DA,0xA7DB,0xA7DC,0xA7DD,0xA7DE,0xA7DF,0xA7E0,0xA7E1, 0xA7E2,0xA7E3,0xA7E4,0xA7E5,0xA7E6,0xA7E7,0xA7E8,0xA7E9, 0xA7EA,0xA7EB,0xA7EC,0xA7ED,0xA7EE,0xA7EF,0xA7F0,0xA7F1, 0x0000,0xA7D7,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA95C,0x0000,0x0000,0xA843,0xA1AA,0xA844,0xA1AC,0x0000, 0xA1AE,0xA1AF,0x0000,0x0000,0xA1B0,0xA1B1,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0xA845,0xA1AD,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA1EB,0x0000,0xA1E4,0xA1E5,0x0000,0xA846,0x0000,0x0000, 0x0000,0x0000,0x0000,0xA1F9,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0080,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0xA1E6,0x0000,0xA847,0x0000,0x0000, 0x0000,0xA848,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xA1ED,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0xA959,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA2F1,0xA2F2,0xA2F3,0xA2F4,0xA2F5,0xA2F6,0xA2F7,0xA2F8, 0xA2F9,0xA2FA,0xA2FB,0xA2FC,0x0000,0x0000,0x0000,0x0000, 0xA2A1,0xA2A2,0xA2A3,0xA2A4,0xA2A5,0xA2A6,0xA2A7,0xA2A8, 0xA2A9,0xA2AA,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA1FB,0xA1FC,0xA1FA,0xA1FD,0x0000,0x0000,0xA849,0xA84A, 0xA84B,0xA84C,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA1CA,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xA1C7, 0x0000,0xA1C6,0x0000,0x0000,0x0000,0xA84D,0x0000,0x0000, 0x0000,0x0000,0xA1CC,0x0000,0x0000,0xA1D8,0xA1DE,0xA84E, 0xA1CF,0x0000,0x0000,0xA84F,0x0000,0xA1CE,0x0000,0xA1C4, 0xA1C5,0xA1C9,0xA1C8,0xA1D2,0x0000,0x0000,0xA1D3,0x0000, 0x0000,0x0000,0x0000,0x0000,0xA1E0,0xA1DF,0xA1C3,0xA1CB, 0x0000,0x0000,0x0000,0x0000,0x0000,0xA1D7,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA1D6,0x0000,0x0000,0x0000,0xA1D5,0x0000,0x0000,0x0000, 0x0000,0x0000,0xA850,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA1D9,0xA1D4,0x0000,0x0000,0xA1DC,0xA1DD,0xA851,0xA852, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xA1DA,0xA1DB, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0xA1D1,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0xA1CD,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xA853, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0xA1D0,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA2D9,0xA2DA,0xA2DB,0xA2DC,0xA2DD,0xA2DE,0xA2DF,0xA2E0, 0xA2E1,0xA2E2,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0xA2C5,0xA2C6,0xA2C7,0xA2C8, 0xA2C9,0xA2CA,0xA2CB,0xA2CC,0xA2CD,0xA2CE,0xA2CF,0xA2D0, 0xA2D1,0xA2D2,0xA2D3,0xA2D4,0xA2D5,0xA2D6,0xA2D7,0xA2D8, 0xA2B1,0xA2B2,0xA2B3,0xA2B4,0xA2B5,0xA2B6,0xA2B7,0xA2B8, 0xA2B9,0xA2BA,0xA2BB,0xA2BC,0xA2BD,0xA2BE,0xA2BF,0xA2C0, 0xA2C1,0xA2C2,0xA2C3,0xA2C4,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA9A4,0xA9A5,0xA9A6,0xA9A7,0xA9A8,0xA9A9,0xA9AA,0xA9AB, 0xA9AC,0xA9AD,0xA9AE,0xA9AF,0xA9B0,0xA9B1,0xA9B2,0xA9B3, 0xA9B4,0xA9B5,0xA9B6,0xA9B7,0xA9B8,0xA9B9,0xA9BA,0xA9BB, 0xA9BC,0xA9BD,0xA9BE,0xA9BF,0xA9C0,0xA9C1,0xA9C2,0xA9C3, 0xA9C4,0xA9C5,0xA9C6,0xA9C7,0xA9C8,0xA9C9,0xA9CA,0xA9CB, 0xA9CC,0xA9CD,0xA9CE,0xA9CF,0xA9D0,0xA9D1,0xA9D2,0xA9D3, 0xA9D4,0xA9D5,0xA9D6,0xA9D7,0xA9D8,0xA9D9,0xA9DA,0xA9DB, 0xA9DC,0xA9DD,0xA9DE,0xA9DF,0xA9E0,0xA9E1,0xA9E2,0xA9E3, 0xA9E4,0xA9E5,0xA9E6,0xA9E7,0xA9E8,0xA9E9,0xA9EA,0xA9EB, 0xA9EC,0xA9ED,0xA9EE,0xA9EF,0x0000,0x0000,0x0000,0x0000, 0xA854,0xA855,0xA856,0xA857,0xA858,0xA859,0xA85A,0xA85B, 0xA85C,0xA85D,0xA85E,0xA85F,0xA860,0xA861,0xA862,0xA863, 0xA864,0xA865,0xA866,0xA867,0xA868,0xA869,0xA86A,0xA86B, 0xA86C,0xA86D,0xA86E,0xA86F,0xA870,0xA871,0xA872,0xA873, 0xA874,0xA875,0xA876,0xA877,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0xA878,0xA879,0xA87A,0xA87B,0xA87C,0xA87D,0xA87E, 0xA880,0xA881,0xA882,0xA883,0xA884,0xA885,0xA886,0xA887, 0x0000,0x0000,0x0000,0xA888,0xA889,0xA88A,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA1F6,0xA1F5,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0xA1F8,0xA1F7,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0xA88B,0xA88C,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xA1F4,0xA1F3, 0x0000,0x0000,0x0000,0xA1F0,0x0000,0x0000,0xA1F2,0xA1F1, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0xA88D,0xA88E,0xA88F,0xA890,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0xA1EF,0xA1EE,0x0000, 0x0000,0xA891,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA1E2,0xA892,0xA1E1,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA1A1,0xA1A2,0xA1A3,0xA1A8,0x0000,0xA1A9,0xA965,0xA996, 0xA1B4,0xA1B5,0xA1B6,0xA1B7,0xA1B8,0xA1B9,0xA1BA,0xA1BB, 0xA1BE,0xA1BF,0xA893,0xA1FE,0xA1B2,0xA1B3,0xA1BC,0xA1BD, 0x0000,0x0000,0x0000,0x0000,0x0000,0xA894,0xA895,0x0000, 0x0000,0xA940,0xA941,0xA942,0xA943,0xA944,0xA945,0xA946, 0xA947,0xA948,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0xA4A1,0xA4A2,0xA4A3,0xA4A4,0xA4A5,0xA4A6,0xA4A7, 0xA4A8,0xA4A9,0xA4AA,0xA4AB,0xA4AC,0xA4AD,0xA4AE,0xA4AF, 0xA4B0,0xA4B1,0xA4B2,0xA4B3,0xA4B4,0xA4B5,0xA4B6,0xA4B7, 0xA4B8,0xA4B9,0xA4BA,0xA4BB,0xA4BC,0xA4BD,0xA4BE,0xA4BF, 0xA4C0,0xA4C1,0xA4C2,0xA4C3,0xA4C4,0xA4C5,0xA4C6,0xA4C7, 0xA4C8,0xA4C9,0xA4CA,0xA4CB,0xA4CC,0xA4CD,0xA4CE,0xA4CF, 0xA4D0,0xA4D1,0xA4D2,0xA4D3,0xA4D4,0xA4D5,0xA4D6,0xA4D7, 0xA4D8,0xA4D9,0xA4DA,0xA4DB,0xA4DC,0xA4DD,0xA4DE,0xA4DF, 0xA4E0,0xA4E1,0xA4E2,0xA4E3,0xA4E4,0xA4E5,0xA4E6,0xA4E7, 0xA4E8,0xA4E9,0xA4EA,0xA4EB,0xA4EC,0xA4ED,0xA4EE,0xA4EF, 0xA4F0,0xA4F1,0xA4F2,0xA4F3,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0xA961,0xA962,0xA966,0xA967,0x0000, 0x0000,0xA5A1,0xA5A2,0xA5A3,0xA5A4,0xA5A5,0xA5A6,0xA5A7, 0xA5A8,0xA5A9,0xA5AA,0xA5AB,0xA5AC,0xA5AD,0xA5AE,0xA5AF, 0xA5B0,0xA5B1,0xA5B2,0xA5B3,0xA5B4,0xA5B5,0xA5B6,0xA5B7, 0xA5B8,0xA5B9,0xA5BA,0xA5BB,0xA5BC,0xA5BD,0xA5BE,0xA5BF, 0xA5C0,0xA5C1,0xA5C2,0xA5C3,0xA5C4,0xA5C5,0xA5C6,0xA5C7, 0xA5C8,0xA5C9,0xA5CA,0xA5CB,0xA5CC,0xA5CD,0xA5CE,0xA5CF, 0xA5D0,0xA5D1,0xA5D2,0xA5D3,0xA5D4,0xA5D5,0xA5D6,0xA5D7, 0xA5D8,0xA5D9,0xA5DA,0xA5DB,0xA5DC,0xA5DD,0xA5DE,0xA5DF, 0xA5E0,0xA5E1,0xA5E2,0xA5E3,0xA5E4,0xA5E5,0xA5E6,0xA5E7, 0xA5E8,0xA5E9,0xA5EA,0xA5EB,0xA5EC,0xA5ED,0xA5EE,0xA5EF, 0xA5F0,0xA5F1,0xA5F2,0xA5F3,0xA5F4,0xA5F5,0xA5F6,0x0000, 0x0000,0x0000,0x0000,0x0000,0xA960,0xA963,0xA964,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0xA8C5,0xA8C6,0xA8C7, 0xA8C8,0xA8C9,0xA8CA,0xA8CB,0xA8CC,0xA8CD,0xA8CE,0xA8CF, 0xA8D0,0xA8D1,0xA8D2,0xA8D3,0xA8D4,0xA8D5,0xA8D6,0xA8D7, 0xA8D8,0xA8D9,0xA8DA,0xA8DB,0xA8DC,0xA8DD,0xA8DE,0xA8DF, 0xA8E0,0xA8E1,0xA8E2,0xA8E3,0xA8E4,0xA8E5,0xA8E6,0xA8E7, 0xA8E8,0xA8E9,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA2E5,0xA2E6,0xA2E7,0xA2E8,0xA2E9,0xA2EA,0xA2EB,0xA2EC, 0xA2ED,0xA2EE,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0xA95A,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0xA949,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000 }; const static uint16 gbkEncoderInnerIndex1[]= { 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xA94A,0xA94B, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0xA94C,0xA94D,0xA94E,0x0000, 0x0000,0xA94F,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0xA950,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xA951,0x0000, 0x0000,0xA952,0xA953,0x0000,0x0000,0xA954,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xD2BB,0xB6A1,0x8140,0xC6DF,0x8141,0x8142,0x8143,0xCDF2, 0xD5C9,0xC8FD,0xC9CF,0xCFC2,0xD8A2,0xB2BB,0xD3EB,0x8144, 0xD8A4,0xB3F3,0x8145,0xD7A8,0xC7D2,0xD8A7,0xCAC0,0x8146, 0xC7F0,0xB1FB,0xD2B5,0xB4D4,0xB6AB,0xCBBF,0xD8A9,0x8147, 0x8148,0x8149,0xB6AA,0x814A,0xC1BD,0xD1CF,0x814B,0xC9A5, 0xD8AD,0x814C,0xB8F6,0xD1BE,0xE3DC,0xD6D0,0x814D,0x814E, 0xB7E1,0x814F,0xB4AE,0x8150,0xC1D9,0x8151,0xD8BC,0x8152, 0xCDE8,0xB5A4,0xCEAA,0xD6F7,0x8153,0xC0F6,0xBED9,0xD8AF, 0x8154,0x8155,0x8156,0xC4CB,0x8157,0xBEC3,0x8158,0xD8B1, 0xC3B4,0xD2E5,0x8159,0xD6AE,0xCEDA,0xD5A7,0xBAF5,0xB7A6, 0xC0D6,0x815A,0xC6B9,0xC5D2,0xC7C7,0x815B,0xB9D4,0x815C, 0xB3CB,0xD2D2,0x815D,0x815E,0xD8BF,0xBEC5,0xC6F2,0xD2B2, 0xCFB0,0xCFE7,0x815F,0x8160,0x8161,0x8162,0xCAE9,0x8163, 0x8164,0xD8C0,0x8165,0x8166,0x8167,0x8168,0x8169,0x816A, 0xC2F2,0xC2D2,0x816B,0xC8E9,0x816C,0x816D,0x816E,0x816F, 0x8170,0x8171,0x8172,0x8173,0x8174,0x8175,0xC7AC,0x8176, 0x8177,0x8178,0x8179,0x817A,0x817B,0x817C,0xC1CB,0x817D, 0xD3E8,0xD5F9,0x817E,0xCAC2,0xB6FE,0xD8A1,0xD3DA,0xBFF7, 0x8180,0xD4C6,0xBBA5,0xD8C1,0xCEE5,0xBEAE,0x8181,0x8182, 0xD8A8,0x8183,0xD1C7,0xD0A9,0x8184,0x8185,0x8186,0xD8BD, 0xD9EF,0xCDF6,0xBFBA,0x8187,0xBDBB,0xBAA5,0xD2E0,0xB2FA, 0xBAE0,0xC4B6,0x8188,0xCFED,0xBEA9,0xCDA4,0xC1C1,0x8189, 0x818A,0x818B,0xC7D7,0xD9F1,0x818C,0xD9F4,0x818D,0x818E, 0x818F,0x8190,0xC8CB,0xD8E9,0x8191,0x8192,0x8193,0xD2DA, 0xCAB2,0xC8CA,0xD8EC,0xD8EA,0xD8C6,0xBDF6,0xC6CD,0xB3F0, 0x8194,0xD8EB,0xBDF1,0xBDE9,0x8195,0xC8D4,0xB4D3,0x8196, 0x8197,0xC2D8,0x8198,0xB2D6,0xD7D0,0xCACB,0xCBFB,0xD5CC, 0xB8B6,0xCFC9,0x8199,0x819A,0x819B,0xD9DA,0xD8F0,0xC7AA, 0x819C,0xD8EE,0x819D,0xB4FA,0xC1EE,0xD2D4,0x819E,0x819F, 0xD8ED,0x81A0,0xD2C7,0xD8EF,0xC3C7,0x81A1,0x81A2,0x81A3, 0xD1F6,0x81A4,0xD6D9,0xD8F2,0x81A5,0xD8F5,0xBCFE,0xBCDB, 0x81A6,0x81A7,0x81A8,0xC8CE,0x81A9,0xB7DD,0x81AA,0xB7C2, 0x81AB,0xC6F3,0x81AC,0x81AD,0x81AE,0x81AF,0x81B0,0x81B1, 0x81B2,0xD8F8,0xD2C1,0x81B3,0x81B4,0xCEE9,0xBCBF,0xB7FC, 0xB7A5,0xD0DD,0x81B5,0x81B6,0x81B7,0x81B8,0x81B9,0xD6DA, 0xD3C5,0xBBEF,0xBBE1,0xD8F1,0x81BA,0x81BB,0xC9A1,0xCEB0, 0xB4AB,0x81BC,0xD8F3,0x81BD,0xC9CB,0xD8F6,0xC2D7,0xD8F7, 0x81BE,0x81BF,0xCEB1,0xD8F9,0x81C0,0x81C1,0x81C2,0xB2AE, 0xB9C0,0x81C3,0xD9A3,0x81C4,0xB0E9,0x81C5,0xC1E6,0x81C6, 0xC9EC,0x81C7,0xCBC5,0x81C8,0xCBC6,0xD9A4,0x81C9,0x81CA, 0x81CB,0x81CC,0x81CD,0xB5E8,0x81CE,0x81CF,0xB5AB,0x81D0, 0x81D1,0x81D2,0x81D3,0x81D4,0x81D5,0xCEBB,0xB5CD,0xD7A1, 0xD7F4,0xD3D3,0x81D6,0xCCE5,0x81D7,0xBACE,0x81D8,0xD9A2, 0xD9DC,0xD3E0,0xD8FD,0xB7F0,0xD7F7,0xD8FE,0xD8FA,0xD9A1, 0xC4E3,0x81D9,0x81DA,0xD3B6,0xD8F4,0xD9DD,0x81DB,0xD8FB, 0x81DC,0xC5E5,0x81DD,0x81DE,0xC0D0,0x81DF,0x81E0,0xD1F0, 0xB0DB,0x81E1,0x81E2,0xBCD1,0xD9A6,0x81E3,0xD9A5,0x81E4, 0x81E5,0x81E6,0x81E7,0xD9AC,0xD9AE,0x81E8,0xD9AB,0xCAB9, 0x81E9,0x81EA,0x81EB,0xD9A9,0xD6B6,0x81EC,0x81ED,0x81EE, 0xB3DE,0xD9A8,0x81EF,0xC0FD,0x81F0,0xCACC,0x81F1,0xD9AA, 0x81F2,0xD9A7,0x81F3,0x81F4,0xD9B0,0x81F5,0x81F6,0xB6B1, 0x81F7,0x81F8,0x81F9,0xB9A9,0x81FA,0xD2C0,0x81FB,0x81FC, 0xCFC0,0x81FD,0x81FE,0xC2C2,0x8240,0xBDC4,0xD5EC,0xB2E0, 0xC7C8,0xBFEB,0xD9AD,0x8241,0xD9AF,0x8242,0xCEEA,0xBAEE, 0x8243,0x8244,0x8245,0x8246,0x8247,0xC7D6,0x8248,0x8249, 0x824A,0x824B,0x824C,0x824D,0x824E,0x824F,0x8250,0xB1E3, 0x8251,0x8252,0x8253,0xB4D9,0xB6ED,0xD9B4,0x8254,0x8255, 0x8256,0x8257,0xBFA1,0x8258,0x8259,0x825A,0xD9DE,0xC7CE, 0xC0FE,0xD9B8,0x825B,0x825C,0x825D,0x825E,0x825F,0xCBD7, 0xB7FD,0x8260,0xD9B5,0x8261,0xD9B7,0xB1A3,0xD3E1,0xD9B9, 0x8262,0xD0C5,0x8263,0xD9B6,0x8264,0x8265,0xD9B1,0x8266, 0xD9B2,0xC1A9,0xD9B3,0x8267,0x8268,0xBCF3,0xD0DE,0xB8A9, 0x8269,0xBEE3,0x826A,0xD9BD,0x826B,0x826C,0x826D,0x826E, 0xD9BA,0x826F,0xB0B3,0x8270,0x8271,0x8272,0xD9C2,0x8273, 0x8274,0x8275,0x8276,0x8277,0x8278,0x8279,0x827A,0x827B, 0x827C,0x827D,0x827E,0x8280,0xD9C4,0xB1B6,0x8281,0xD9BF, 0x8282,0x8283,0xB5B9,0x8284,0xBEF3,0x8285,0x8286,0x8287, 0xCCC8,0xBAF2,0xD2D0,0x8288,0xD9C3,0x8289,0x828A,0xBDE8, 0x828B,0xB3AB,0x828C,0x828D,0x828E,0xD9C5,0xBEEB,0x828F, 0xD9C6,0xD9BB,0xC4DF,0x8290,0xD9BE,0xD9C1,0xD9C0,0x8291, 0x8292,0x8293,0x8294,0x8295,0x8296,0x8297,0x8298,0x8299, 0x829A,0x829B,0xD5AE,0x829C,0xD6B5,0x829D,0xC7E3,0x829E, 0x829F,0x82A0,0x82A1,0xD9C8,0x82A2,0x82A3,0x82A4,0xBCD9, 0xD9CA,0x82A5,0x82A6,0x82A7,0xD9BC,0x82A8,0xD9CB,0xC6AB, 0x82A9,0x82AA,0x82AB,0x82AC,0x82AD,0xD9C9,0x82AE,0x82AF, 0x82B0,0x82B1,0xD7F6,0x82B2,0xCDA3,0x82B3,0x82B4,0x82B5, 0x82B6,0x82B7,0x82B8,0x82B9,0x82BA,0xBDA1,0x82BB,0x82BC, 0x82BD,0x82BE,0x82BF,0x82C0,0xD9CC,0x82C1,0x82C2,0x82C3, 0x82C4,0x82C5,0x82C6,0x82C7,0x82C8,0x82C9,0xC5BC,0xCDB5, 0x82CA,0x82CB,0x82CC,0xD9CD,0x82CD,0x82CE,0xD9C7,0xB3A5, 0xBFFE,0x82CF,0x82D0,0x82D1,0x82D2,0xB8B5,0x82D3,0x82D4, 0xC0FC,0x82D5,0x82D6,0x82D7,0x82D8,0xB0F8,0x82D9,0x82DA, 0x82DB,0x82DC,0x82DD,0x82DE,0x82DF,0x82E0,0x82E1,0x82E2, 0x82E3,0x82E4,0x82E5,0x82E6,0x82E7,0x82E8,0x82E9,0x82EA, 0x82EB,0x82EC,0x82ED,0xB4F6,0x82EE,0xD9CE,0x82EF,0xD9CF, 0xB4A2,0xD9D0,0x82F0,0x82F1,0xB4DF,0x82F2,0x82F3,0x82F4, 0x82F5,0x82F6,0xB0C1,0x82F7,0x82F8,0x82F9,0x82FA,0x82FB, 0x82FC,0x82FD,0xD9D1,0xC9B5,0x82FE,0x8340,0x8341,0x8342, 0x8343,0x8344,0x8345,0x8346,0x8347,0x8348,0x8349,0x834A, 0x834B,0x834C,0x834D,0x834E,0x834F,0x8350,0x8351,0xCFF1, 0x8352,0x8353,0x8354,0x8355,0x8356,0x8357,0xD9D2,0x8358, 0x8359,0x835A,0xC1C5,0x835B,0x835C,0x835D,0x835E,0x835F, 0x8360,0x8361,0x8362,0x8363,0x8364,0x8365,0xD9D6,0xC9AE, 0x8366,0x8367,0x8368,0x8369,0xD9D5,0xD9D4,0xD9D7,0x836A, 0x836B,0x836C,0x836D,0xCBDB,0x836E,0xBDA9,0x836F,0x8370, 0x8371,0x8372,0x8373,0xC6A7,0x8374,0x8375,0x8376,0x8377, 0x8378,0x8379,0x837A,0x837B,0x837C,0x837D,0xD9D3,0xD9D8, 0x837E,0x8380,0x8381,0xD9D9,0x8382,0x8383,0x8384,0x8385, 0x8386,0x8387,0xC8E5,0x8388,0x8389,0x838A,0x838B,0x838C, 0x838D,0x838E,0x838F,0x8390,0x8391,0x8392,0x8393,0x8394, 0x8395,0xC0DC,0x8396,0x8397,0x8398,0x8399,0x839A,0x839B, 0x839C,0x839D,0x839E,0x839F,0x83A0,0x83A1,0x83A2,0x83A3, 0x83A4,0x83A5,0x83A6,0x83A7,0x83A8,0x83A9,0x83AA,0x83AB, 0x83AC,0x83AD,0x83AE,0x83AF,0x83B0,0x83B1,0x83B2,0xB6F9, 0xD8A3,0xD4CA,0x83B3,0xD4AA,0xD0D6,0xB3E4,0xD5D7,0x83B4, 0xCFC8,0xB9E2,0x83B5,0xBFCB,0x83B6,0xC3E2,0x83B7,0x83B8, 0x83B9,0xB6D2,0x83BA,0x83BB,0xCDC3,0xD9EE,0xD9F0,0x83BC, 0x83BD,0x83BE,0xB5B3,0x83BF,0xB6B5,0x83C0,0x83C1,0x83C2, 0x83C3,0x83C4,0xBEA4,0x83C5,0x83C6,0xC8EB,0x83C7,0x83C8, 0xC8AB,0x83C9,0x83CA,0xB0CB,0xB9AB,0xC1F9,0xD9E2,0x83CB, 0xC0BC,0xB9B2,0x83CC,0xB9D8,0xD0CB,0xB1F8,0xC6E4,0xBEDF, 0xB5E4,0xD7C8,0x83CD,0xD1F8,0xBCE6,0xCADE,0x83CE,0x83CF, 0xBCBD,0xD9E6,0xD8E7,0x83D0,0x83D1,0xC4DA,0x83D2,0x83D3, 0xB8D4,0xC8BD,0x83D4,0x83D5,0xB2E1,0xD4D9,0x83D6,0x83D7, 0x83D8,0x83D9,0xC3B0,0x83DA,0x83DB,0xC3E1,0xDAA2,0xC8DF, 0x83DC,0xD0B4,0x83DD,0xBEFC,0xC5A9,0x83DE,0x83DF,0x83E0, 0xB9DA,0x83E1,0xDAA3,0x83E2,0xD4A9,0xDAA4,0x83E3,0x83E4, 0x83E5,0x83E6,0x83E7,0xD9FB,0xB6AC,0x83E8,0x83E9,0xB7EB, 0xB1F9,0xD9FC,0xB3E5,0xBEF6,0x83EA,0xBFF6,0xD2B1,0xC0E4, 0x83EB,0x83EC,0x83ED,0xB6B3,0xD9FE,0xD9FD,0x83EE,0x83EF, 0xBEBB,0x83F0,0x83F1,0x83F2,0xC6E0,0x83F3,0xD7BC,0xDAA1, 0x83F4,0xC1B9,0x83F5,0xB5F2,0xC1E8,0x83F6,0x83F7,0xBCF5, 0x83F8,0xB4D5,0x83F9,0x83FA,0x83FB,0x83FC,0x83FD,0x83FE, 0x8440,0x8441,0x8442,0xC1DD,0x8443,0xC4FD,0x8444,0x8445, 0xBCB8,0xB7B2,0x8446,0x8447,0xB7EF,0x8448,0x8449,0x844A, 0x844B,0x844C,0x844D,0xD9EC,0x844E,0xC6BE,0x844F,0xBFAD, 0xBBCB,0x8450,0x8451,0xB5CA,0x8452,0xDBC9,0xD0D7,0x8453, 0xCDB9,0xB0BC,0xB3F6,0xBBF7,0xDBCA,0xBAAF,0x8454,0xD4E4, 0xB5B6,0xB5F3,0xD8D6,0xC8D0,0x8455,0x8456,0xB7D6,0xC7D0, 0xD8D7,0x8457,0xBFAF,0x8458,0x8459,0xDBBB,0xD8D8,0x845A, 0x845B,0xD0CC,0xBBAE,0x845C,0x845D,0x845E,0xEBBE,0xC1D0, 0xC1F5,0xD4F2,0xB8D5,0xB4B4,0x845F,0xB3F5,0x8460,0x8461, 0xC9BE,0x8462,0x8463,0x8464,0xC5D0,0x8465,0x8466,0x8467, 0xC5D9,0xC0FB,0x8468,0xB1F0,0x8469,0xD8D9,0xB9CE,0x846A, 0xB5BD,0x846B,0x846C,0xD8DA,0x846D,0x846E,0xD6C6,0xCBA2, 0xC8AF,0xC9B2,0xB4CC,0xBFCC,0x846F,0xB9F4,0x8470,0xD8DB, 0xD8DC,0xB6E7,0xBCC1,0xCCEA,0x8471,0x8472,0x8473,0x8474, 0x8475,0x8476,0xCFF7,0x8477,0xD8DD,0xC7B0,0x8478,0x8479, 0xB9D0,0xBDA3,0x847A,0x847B,0xCCDE,0x847C,0xC6CA,0x847D, 0x847E,0x8480,0x8481,0x8482,0xD8E0,0x8483,0xD8DE,0x8484, 0x8485,0xD8DF,0x8486,0x8487,0x8488,0xB0FE,0x8489,0xBEE7, 0x848A,0xCAA3,0xBCF4,0x848B,0x848C,0x848D,0x848E,0xB8B1, 0x848F,0x8490,0xB8EE,0x8491,0x8492,0x8493,0x8494,0x8495, 0x8496,0x8497,0x8498,0x8499,0x849A,0xD8E2,0x849B,0xBDCB, 0x849C,0xD8E4,0xD8E3,0x849D,0x849E,0x849F,0x84A0,0x84A1, 0xC5FC,0x84A2,0x84A3,0x84A4,0x84A5,0x84A6,0x84A7,0x84A8, 0xD8E5,0x84A9,0x84AA,0xD8E6,0x84AB,0x84AC,0x84AD,0x84AE, 0x84AF,0x84B0,0x84B1,0xC1A6,0x84B2,0xC8B0,0xB0EC,0xB9A6, 0xBCD3,0xCEF1,0xDBBD,0xC1D3,0x84B3,0x84B4,0x84B5,0x84B6, 0xB6AF,0xD6FA,0xC5AC,0xBDD9,0xDBBE,0xDBBF,0x84B7,0x84B8, 0x84B9,0xC0F8,0xBEA2,0xC0CD,0x84BA,0x84BB,0x84BC,0x84BD, 0x84BE,0x84BF,0x84C0,0x84C1,0x84C2,0x84C3,0xDBC0,0xCAC6, 0x84C4,0x84C5,0x84C6,0xB2AA,0x84C7,0x84C8,0x84C9,0xD3C2, 0x84CA,0xC3E3,0x84CB,0xD1AB,0x84CC,0x84CD,0x84CE,0x84CF, 0xDBC2,0x84D0,0xC0D5,0x84D1,0x84D2,0x84D3,0xDBC3,0x84D4, 0xBFB1,0x84D5,0x84D6,0x84D7,0x84D8,0x84D9,0x84DA,0xC4BC, 0x84DB,0x84DC,0x84DD,0x84DE,0xC7DA,0x84DF,0x84E0,0x84E1, 0x84E2,0x84E3,0x84E4,0x84E5,0x84E6,0x84E7,0x84E8,0x84E9, 0xDBC4,0x84EA,0x84EB,0x84EC,0x84ED,0x84EE,0x84EF,0x84F0, 0x84F1,0xD9E8,0xC9D7,0x84F2,0x84F3,0x84F4,0xB9B4,0xCEF0, 0xD4C8,0x84F5,0x84F6,0x84F7,0x84F8,0xB0FC,0xB4D2,0x84F9, 0xD0D9,0x84FA,0x84FB,0x84FC,0x84FD,0xD9E9,0x84FE,0xDECB, 0xD9EB,0x8540,0x8541,0x8542,0x8543,0xD8B0,0xBBAF,0xB1B1, 0x8544,0xB3D7,0xD8CE,0x8545,0x8546,0xD4D1,0x8547,0x8548, 0xBDB3,0xBFEF,0x8549,0xCFBB,0x854A,0x854B,0xD8D0,0x854C, 0x854D,0x854E,0xB7CB,0x854F,0x8550,0x8551,0xD8D1,0x8552, 0x8553,0x8554,0x8555,0x8556,0x8557,0x8558,0x8559,0x855A, 0x855B,0xC6A5,0xC7F8,0xD2BD,0x855C,0x855D,0xD8D2,0xC4E4, 0x855E,0xCAAE,0x855F,0xC7A7,0x8560,0xD8A6,0x8561,0xC9FD, 0xCEE7,0xBBDC,0xB0EB,0x8562,0x8563,0x8564,0xBBAA,0xD0AD, 0x8565,0xB1B0,0xD7E4,0xD7BF,0x8566,0xB5A5,0xC2F4,0xC4CF, 0x8567,0x8568,0xB2A9,0x8569,0xB2B7,0x856A,0xB1E5,0xDFB2, 0xD5BC,0xBFA8,0xC2AC,0xD8D5,0xC2B1,0x856B,0xD8D4,0xCED4, 0x856C,0xDAE0,0x856D,0xCEC0,0x856E,0x856F,0xD8B4,0xC3AE, 0xD3A1,0xCEA3,0x8570,0xBCB4,0xC8B4,0xC2D1,0x8571,0xBEED, 0xD0B6,0x8572,0xDAE1,0x8573,0x8574,0x8575,0x8576,0xC7E4, 0x8577,0x8578,0xB3A7,0x8579,0xB6F2,0xCCFC,0xC0FA,0x857A, 0x857B,0xC0F7,0x857C,0xD1B9,0xD1E1,0xD8C7,0x857D,0x857E, 0x8580,0x8581,0x8582,0x8583,0x8584,0xB2DE,0x8585,0x8586, 0xC0E5,0x8587,0xBAF1,0x8588,0x8589,0xD8C8,0x858A,0xD4AD, 0x858B,0x858C,0xCFE1,0xD8C9,0x858D,0xD8CA,0xCFC3,0x858E, 0xB3F8,0xBEC7,0x858F,0x8590,0x8591,0x8592,0xD8CB,0x8593, 0x8594,0x8595,0x8596,0x8597,0x8598,0x8599,0xDBCC,0x859A, 0x859B,0x859C,0x859D,0xC8A5,0x859E,0x859F,0x85A0,0xCFD8, 0x85A1,0xC8FE,0xB2CE,0x85A2,0x85A3,0x85A4,0x85A5,0x85A6, 0xD3D6,0xB2E6,0xBCB0,0xD3D1,0xCBAB,0xB7B4,0x85A7,0x85A8, 0x85A9,0xB7A2,0x85AA,0x85AB,0xCAE5,0x85AC,0xC8A1,0xCADC, 0xB1E4,0xD0F0,0x85AD,0xC5D1,0x85AE,0x85AF,0x85B0,0xDBC5, 0xB5FE,0x85B1,0x85B2,0xBFDA,0xB9C5,0xBEE4,0xC1ED,0x85B3, 0xDFB6,0xDFB5,0xD6BB,0xBDD0,0xD5D9,0xB0C8,0xB6A3,0xBFC9, 0xCCA8,0xDFB3,0xCAB7,0xD3D2,0x85B4,0xD8CF,0xD2B6,0xBAC5, 0xCBBE,0xCCBE,0x85B5,0xDFB7,0xB5F0,0xDFB4,0x85B6,0x85B7, 0x85B8,0xD3F5,0x85B9,0xB3D4,0xB8F7,0x85BA,0xDFBA,0x85BB, 0xBACF,0xBCAA,0xB5F5,0x85BC,0xCDAC,0xC3FB,0xBAF3,0xC0F4, 0xCDC2,0xCFF2,0xDFB8,0xCFC5,0x85BD,0xC2C0,0xDFB9,0xC2F0, 0x85BE,0x85BF,0x85C0,0xBEFD,0x85C1,0xC1DF,0xCDCC,0xD2F7, 0xB7CD,0xDFC1,0x85C2,0xDFC4,0x85C3,0x85C4,0xB7F1,0xB0C9, 0xB6D6,0xB7D4,0x85C5,0xBAAC,0xCCFD,0xBFD4,0xCBB1,0xC6F4, 0x85C6,0xD6A8,0xDFC5,0x85C7,0xCEE2,0xB3B3,0x85C8,0x85C9, 0xCEFC,0xB4B5,0x85CA,0xCEC7,0xBAF0,0x85CB,0xCEE1,0x85CC, 0xD1BD,0x85CD,0x85CE,0xDFC0,0x85CF,0x85D0,0xB4F4,0x85D1, 0xB3CA,0x85D2,0xB8E6,0xDFBB,0x85D3,0x85D4,0x85D5,0x85D6, 0xC4C5,0x85D7,0xDFBC,0xDFBD,0xDFBE,0xC5BB,0xDFBF,0xDFC2, 0xD4B1,0xDFC3,0x85D8,0xC7BA,0xCED8,0x85D9,0x85DA,0x85DB, 0x85DC,0x85DD,0xC4D8,0x85DE,0xDFCA,0x85DF,0xDFCF,0x85E0, 0xD6DC,0x85E1,0x85E2,0x85E3,0x85E4,0x85E5,0x85E6,0x85E7, 0x85E8,0xDFC9,0xDFDA,0xCEB6,0x85E9,0xBAC7,0xDFCE,0xDFC8, 0xC5DE,0x85EA,0x85EB,0xC9EB,0xBAF4,0xC3FC,0x85EC,0x85ED, 0xBED7,0x85EE,0xDFC6,0x85EF,0xDFCD,0x85F0,0xC5D8,0x85F1, 0x85F2,0x85F3,0x85F4,0xD5A6,0xBACD,0x85F5,0xBECC,0xD3BD, 0xB8C0,0x85F6,0xD6E4,0x85F7,0xDFC7,0xB9BE,0xBFA7,0x85F8, 0x85F9,0xC1FC,0xDFCB,0xDFCC,0x85FA,0xDFD0,0x85FB,0x85FC, 0x85FD,0x85FE,0x8640,0xDFDB,0xDFE5,0x8641,0xDFD7,0xDFD6, 0xD7C9,0xDFE3,0xDFE4,0xE5EB,0xD2A7,0xDFD2,0x8642,0xBFA9, 0x8643,0xD4DB,0x8644,0xBFC8,0xDFD4,0x8645,0x8646,0x8647, 0xCFCC,0x8648,0x8649,0xDFDD,0x864A,0xD1CA,0x864B,0xDFDE, 0xB0A7,0xC6B7,0xDFD3,0x864C,0xBAE5,0x864D,0xB6DF,0xCDDB, 0xB9FE,0xD4D5,0x864E,0x864F,0xDFDF,0xCFEC,0xB0A5,0xDFE7, 0xDFD1,0xD1C6,0xDFD5,0xDFD8,0xDFD9,0xDFDC,0x8650,0xBBA9, 0x8651,0xDFE0,0xDFE1,0x8652,0xDFE2,0xDFE6,0xDFE8,0xD3B4, 0x8653,0x8654,0x8655,0x8656,0x8657,0xB8E7,0xC5B6,0xDFEA, 0xC9DA,0xC1A8,0xC4C4,0x8658,0x8659,0xBFDE,0xCFF8,0x865A, 0x865B,0x865C,0xD5DC,0xDFEE,0x865D,0x865E,0x865F,0x8660, 0x8661,0x8662,0xB2B8,0x8663,0xBADF,0xDFEC,0x8664,0xDBC1, 0x8665,0xD1E4,0x8666,0x8667,0x8668,0x8669,0xCBF4,0xB4BD, 0x866A,0xB0A6,0x866B,0x866C,0x866D,0x866E,0x866F,0xDFF1, 0xCCC6,0xDFF2,0x8670,0x8671,0xDFED,0x8672,0x8673,0x8674, 0x8675,0x8676,0x8677,0xDFE9,0x8678,0x8679,0x867A,0x867B, 0xDFEB,0x867C,0xDFEF,0xDFF0,0xBBBD,0x867D,0x867E,0xDFF3, 0x8680,0x8681,0xDFF4,0x8682,0xBBA3,0x8683,0xCADB,0xCEA8, 0xE0A7,0xB3AA,0x8684,0xE0A6,0x8685,0x8686,0x8687,0xE0A1, 0x8688,0x8689,0x868A,0x868B,0xDFFE,0x868C,0xCDD9,0xDFFC, 0x868D,0xDFFA,0x868E,0xBFD0,0xD7C4,0x868F,0xC9CC,0x8690, 0x8691,0xDFF8,0xB0A1,0x8692,0x8693,0x8694,0x8695,0x8696, 0xDFFD,0x8697,0x8698,0x8699,0x869A,0xDFFB,0xE0A2,0x869B, 0x869C,0x869D,0x869E,0x869F,0xE0A8,0x86A0,0x86A1,0x86A2, 0x86A3,0xB7C8,0x86A4,0x86A5,0xC6A1,0xC9B6,0xC0B2,0xDFF5, 0x86A6,0x86A7,0xC5BE,0x86A8,0xD8C4,0xDFF9,0xC4F6,0x86A9, 0x86AA,0x86AB,0x86AC,0x86AD,0x86AE,0xE0A3,0xE0A4,0xE0A5, 0xD0A5,0x86AF,0x86B0,0xE0B4,0xCCE4,0x86B1,0xE0B1,0x86B2, 0xBFA6,0xE0AF,0xCEB9,0xE0AB,0xC9C6,0x86B3,0x86B4,0xC0AE, 0xE0AE,0xBAED,0xBAB0,0xE0A9,0x86B5,0x86B6,0x86B7,0xDFF6, 0x86B8,0xE0B3,0x86B9,0x86BA,0xE0B8,0x86BB,0x86BC,0x86BD, 0xB4AD,0xE0B9,0x86BE,0x86BF,0xCFB2,0xBAC8,0x86C0,0xE0B0, 0x86C1,0x86C2,0x86C3,0x86C4,0x86C5,0x86C6,0x86C7,0xD0FA, 0x86C8,0x86C9,0x86CA,0x86CB,0x86CC,0x86CD,0x86CE,0x86CF, 0x86D0,0xE0AC,0x86D1,0xD4FB,0x86D2,0xDFF7,0x86D3,0xC5E7, 0x86D4,0xE0AD,0x86D5,0xD3F7,0x86D6,0xE0B6,0xE0B7,0x86D7, 0x86D8,0x86D9,0x86DA,0x86DB,0xE0C4,0xD0E1,0x86DC,0x86DD, 0x86DE,0xE0BC,0x86DF,0x86E0,0xE0C9,0xE0CA,0x86E1,0x86E2, 0x86E3,0xE0BE,0xE0AA,0xC9A4,0xE0C1,0x86E4,0xE0B2,0x86E5, 0x86E6,0x86E7,0x86E8,0x86E9,0xCAC8,0xE0C3,0x86EA,0xE0B5, 0x86EB,0xCECB,0x86EC,0xCBC3,0xE0CD,0xE0C6,0xE0C2,0x86ED, 0xE0CB,0x86EE,0xE0BA,0xE0BF,0xE0C0,0x86EF,0x86F0,0xE0C5, 0x86F1,0x86F2,0xE0C7,0xE0C8,0x86F3,0xE0CC,0x86F4,0xE0BB, 0x86F5,0x86F6,0x86F7,0x86F8,0x86F9,0xCBD4,0xE0D5,0x86FA, 0xE0D6,0xE0D2,0x86FB,0x86FC,0x86FD,0x86FE,0x8740,0x8741, 0xE0D0,0xBCCE,0x8742,0x8743,0xE0D1,0x8744,0xB8C2,0xD8C5, 0x8745,0x8746,0x8747,0x8748,0x8749,0x874A,0x874B,0x874C, 0xD0EA,0x874D,0x874E,0xC2EF,0x874F,0x8750,0xE0CF,0xE0BD, 0x8751,0x8752,0x8753,0xE0D4,0xE0D3,0x8754,0x8755,0xE0D7, 0x8756,0x8757,0x8758,0x8759,0xE0DC,0xE0D8,0x875A,0x875B, 0x875C,0xD6F6,0xB3B0,0x875D,0xD7EC,0x875E,0xCBBB,0x875F, 0x8760,0xE0DA,0x8761,0xCEFB,0x8762,0x8763,0x8764,0xBAD9, 0x8765,0x8766,0x8767,0x8768,0x8769,0x876A,0x876B,0x876C, 0x876D,0x876E,0x876F,0x8770,0xE0E1,0xE0DD,0xD2AD,0x8771, 0x8772,0x8773,0x8774,0x8775,0xE0E2,0x8776,0x8777,0xE0DB, 0xE0D9,0xE0DF,0x8778,0x8779,0xE0E0,0x877A,0x877B,0x877C, 0x877D,0x877E,0xE0DE,0x8780,0xE0E4,0x8781,0x8782,0x8783, 0xC6F7,0xD8AC,0xD4EB,0xE0E6,0xCAC9,0x8784,0x8785,0x8786, 0x8787,0xE0E5,0x8788,0x8789,0x878A,0x878B,0xB8C1,0x878C, 0x878D,0x878E,0x878F,0xE0E7,0xE0E8,0x8790,0x8791,0x8792, 0x8793,0x8794,0x8795,0x8796,0x8797,0xE0E9,0xE0E3,0x8798, 0x8799,0x879A,0x879B,0x879C,0x879D,0x879E,0xBABF,0xCCE7, 0x879F,0x87A0,0x87A1,0xE0EA,0x87A2,0x87A3,0x87A4,0x87A5, 0x87A6,0x87A7,0x87A8,0x87A9,0x87AA,0x87AB,0x87AC,0x87AD, 0x87AE,0x87AF,0x87B0,0xCFF9,0x87B1,0x87B2,0x87B3,0x87B4, 0x87B5,0x87B6,0x87B7,0x87B8,0x87B9,0x87BA,0x87BB,0xE0EB, 0x87BC,0x87BD,0x87BE,0x87BF,0x87C0,0x87C1,0x87C2,0xC8C2, 0x87C3,0x87C4,0x87C5,0x87C6,0xBDC0,0x87C7,0x87C8,0x87C9, 0x87CA,0x87CB,0x87CC,0x87CD,0x87CE,0x87CF,0x87D0,0x87D1, 0x87D2,0x87D3,0xC4D2,0x87D4,0x87D5,0x87D6,0x87D7,0x87D8, 0x87D9,0x87DA,0x87DB,0x87DC,0xE0EC,0x87DD,0x87DE,0xE0ED, 0x87DF,0x87E0,0xC7F4,0xCBC4,0x87E1,0xE0EE,0xBBD8,0xD8B6, 0xD2F2,0xE0EF,0xCDC5,0x87E2,0xB6DA,0x87E3,0x87E4,0x87E5, 0x87E6,0x87E7,0x87E8,0xE0F1,0x87E9,0xD4B0,0x87EA,0x87EB, 0xC0A7,0xB4D1,0x87EC,0x87ED,0xCEA7,0xE0F0,0x87EE,0x87EF, 0x87F0,0xE0F2,0xB9CC,0x87F1,0x87F2,0xB9FA,0xCDBC,0xE0F3, 0x87F3,0x87F4,0x87F5,0xC6D4,0xE0F4,0x87F6,0xD4B2,0x87F7, 0xC8A6,0xE0F6,0xE0F5,0x87F8,0x87F9,0x87FA,0x87FB,0x87FC, 0x87FD,0x87FE,0x8840,0x8841,0x8842,0x8843,0x8844,0x8845, 0x8846,0x8847,0x8848,0x8849,0xE0F7,0x884A,0x884B,0xCDC1, 0x884C,0x884D,0x884E,0xCAA5,0x884F,0x8850,0x8851,0x8852, 0xD4DA,0xDBD7,0xDBD9,0x8853,0xDBD8,0xB9E7,0xDBDC,0xDBDD, 0xB5D8,0x8854,0x8855,0xDBDA,0x8856,0x8857,0x8858,0x8859, 0x885A,0xDBDB,0xB3A1,0xDBDF,0x885B,0x885C,0xBBF8,0x885D, 0xD6B7,0x885E,0xDBE0,0x885F,0x8860,0x8861,0x8862,0xBEF9, 0x8863,0x8864,0xB7BB,0x8865,0xDBD0,0xCCAE,0xBFB2,0xBBB5, 0xD7F8,0xBFD3,0x8866,0x8867,0x8868,0x8869,0x886A,0xBFE9, 0x886B,0x886C,0xBCE1,0xCCB3,0xDBDE,0xB0D3,0xCEEB,0xB7D8, 0xD7B9,0xC6C2,0x886D,0x886E,0xC0A4,0x886F,0xCCB9,0x8870, 0xDBE7,0xDBE1,0xC6BA,0xDBE3,0x8871,0xDBE8,0x8872,0xC5F7, 0x8873,0x8874,0x8875,0xDBEA,0x8876,0x8877,0xDBE9,0xBFC0, 0x8878,0x8879,0x887A,0xDBE6,0xDBE5,0x887B,0x887C,0x887D, 0x887E,0x8880,0xB4B9,0xC0AC,0xC2A2,0xDBE2,0xDBE4,0x8881, 0x8882,0x8883,0x8884,0xD0CD,0xDBED,0x8885,0x8886,0x8887, 0x8888,0x8889,0xC0DD,0xDBF2,0x888A,0x888B,0x888C,0x888D, 0x888E,0x888F,0x8890,0xB6E2,0x8891,0x8892,0x8893,0x8894, 0xDBF3,0xDBD2,0xB9B8,0xD4AB,0xDBEC,0x8895,0xBFD1,0xDBF0, 0x8896,0xDBD1,0x8897,0xB5E6,0x8898,0xDBEB,0xBFE5,0x8899, 0x889A,0x889B,0xDBEE,0x889C,0xDBF1,0x889D,0x889E,0x889F, 0xDBF9,0x88A0,0x88A1,0x88A2,0x88A3,0x88A4,0x88A5,0x88A6, 0x88A7,0x88A8,0xB9A1,0xB0A3,0x88A9,0x88AA,0x88AB,0x88AC, 0x88AD,0x88AE,0x88AF,0xC2F1,0x88B0,0x88B1,0xB3C7,0xDBEF, 0x88B2,0x88B3,0xDBF8,0x88B4,0xC6D2,0xDBF4,0x88B5,0x88B6, 0xDBF5,0xDBF7,0xDBF6,0x88B7,0x88B8,0xDBFE,0x88B9,0xD3F2, 0xB2BA,0x88BA,0x88BB,0x88BC,0xDBFD,0x88BD,0x88BE,0x88BF, 0x88C0,0x88C1,0x88C2,0x88C3,0x88C4,0xDCA4,0x88C5,0xDBFB, 0x88C6,0x88C7,0x88C8,0x88C9,0xDBFA,0x88CA,0x88CB,0x88CC, 0xDBFC,0xC5E0,0xBBF9,0x88CD,0x88CE,0xDCA3,0x88CF,0x88D0, 0xDCA5,0x88D1,0xCCC3,0x88D2,0x88D3,0x88D4,0xB6D1,0xDDC0, 0x88D5,0x88D6,0x88D7,0xDCA1,0x88D8,0xDCA2,0x88D9,0x88DA, 0x88DB,0xC7B5,0x88DC,0x88DD,0x88DE,0xB6E9,0x88DF,0x88E0, 0x88E1,0xDCA7,0x88E2,0x88E3,0x88E4,0x88E5,0xDCA6,0x88E6, 0xDCA9,0xB1A4,0x88E7,0x88E8,0xB5CC,0x88E9,0x88EA,0x88EB, 0x88EC,0x88ED,0xBFB0,0x88EE,0x88EF,0x88F0,0x88F1,0x88F2, 0xD1DF,0x88F3,0x88F4,0x88F5,0x88F6,0xB6C2,0x88F7,0x88F8, 0x88F9,0x88FA,0x88FB,0x88FC,0x88FD,0x88FE,0x8940,0x8941, 0x8942,0x8943,0x8944,0x8945,0xDCA8,0x8946,0x8947,0x8948, 0x8949,0x894A,0x894B,0x894C,0xCBFA,0xEBF3,0x894D,0x894E, 0x894F,0xCBDC,0x8950,0x8951,0xCBFE,0x8952,0x8953,0x8954, 0xCCC1,0x8955,0x8956,0x8957,0x8958,0x8959,0xC8FB,0x895A, 0x895B,0x895C,0x895D,0x895E,0x895F,0xDCAA,0x8960,0x8961, 0x8962,0x8963,0x8964,0xCCEE,0xDCAB,0x8965,0x8966,0x8967, 0x8968,0x8969,0x896A,0x896B,0x896C,0x896D,0x896E,0x896F, 0x8970,0x8971,0x8972,0x8973,0x8974,0x8975,0xDBD3,0x8976, 0xDCAF,0xDCAC,0x8977,0xBEB3,0x8978,0xCAFB,0x8979,0x897A, 0x897B,0xDCAD,0x897C,0x897D,0x897E,0x8980,0x8981,0x8982, 0x8983,0x8984,0xC9CA,0xC4B9,0x8985,0x8986,0x8987,0x8988, 0x8989,0xC7BD,0xDCAE,0x898A,0x898B,0x898C,0xD4F6,0xD0E6, 0x898D,0x898E,0x898F,0x8990,0x8991,0x8992,0x8993,0x8994, 0xC4AB,0xB6D5,0x8995,0x8996,0x8997,0x8998,0x8999,0x899A, 0x899B,0x899C,0x899D,0x899E,0x899F,0x89A0,0x89A1,0x89A2, 0x89A3,0x89A4,0x89A5,0x89A6,0xDBD4,0x89A7,0x89A8,0x89A9, 0x89AA,0xB1DA,0x89AB,0x89AC,0x89AD,0xDBD5,0x89AE,0x89AF, 0x89B0,0x89B1,0x89B2,0x89B3,0x89B4,0x89B5,0x89B6,0x89B7, 0x89B8,0xDBD6,0x89B9,0x89BA,0x89BB,0xBABE,0x89BC,0x89BD, 0x89BE,0x89BF,0x89C0,0x89C1,0x89C2,0x89C3,0x89C4,0x89C5, 0x89C6,0x89C7,0x89C8,0x89C9,0xC8C0,0x89CA,0x89CB,0x89CC, 0x89CD,0x89CE,0x89CF,0xCABF,0xC8C9,0x89D0,0xD7B3,0x89D1, 0xC9F9,0x89D2,0x89D3,0xBFC7,0x89D4,0x89D5,0xBAF8,0x89D6, 0x89D7,0xD2BC,0x89D8,0x89D9,0x89DA,0x89DB,0x89DC,0x89DD, 0x89DE,0x89DF,0xE2BA,0x89E0,0xB4A6,0x89E1,0x89E2,0xB1B8, 0x89E3,0x89E4,0x89E5,0x89E6,0x89E7,0xB8B4,0x89E8,0xCFC4, 0x89E9,0x89EA,0x89EB,0x89EC,0xD9E7,0xCFA6,0xCDE2,0x89ED, 0x89EE,0xD9ED,0xB6E0,0x89EF,0xD2B9,0x89F0,0x89F1,0xB9BB, 0x89F2,0x89F3,0x89F4,0x89F5,0xE2B9,0xE2B7,0x89F6,0xB4F3, 0x89F7,0xCCEC,0xCCAB,0xB7F2,0x89F8,0xD8B2,0xD1EB,0xBABB, 0x89F9,0xCAA7,0x89FA,0x89FB,0xCDB7,0x89FC,0x89FD,0xD2C4, 0xBFE4,0xBCD0,0xB6E1,0x89FE,0xDEC5,0x8A40,0x8A41,0x8A42, 0x8A43,0xDEC6,0xDBBC,0x8A44,0xD1D9,0x8A45,0x8A46,0xC6E6, 0xC4CE,0xB7EE,0x8A47,0xB7DC,0x8A48,0x8A49,0xBFFC,0xD7E0, 0x8A4A,0xC6F5,0x8A4B,0x8A4C,0xB1BC,0xDEC8,0xBDB1,0xCCD7, 0xDECA,0x8A4D,0xDEC9,0x8A4E,0x8A4F,0x8A50,0x8A51,0x8A52, 0xB5EC,0x8A53,0xC9DD,0x8A54,0x8A55,0xB0C2,0x8A56,0x8A57, 0x8A58,0x8A59,0x8A5A,0x8A5B,0x8A5C,0x8A5D,0x8A5E,0x8A5F, 0x8A60,0x8A61,0x8A62,0xC5AE,0xC5AB,0x8A63,0xC4CC,0x8A64, 0xBCE9,0xCBFD,0x8A65,0x8A66,0x8A67,0xBAC3,0x8A68,0x8A69, 0x8A6A,0xE5F9,0xC8E7,0xE5FA,0xCDFD,0x8A6B,0xD7B1,0xB8BE, 0xC2E8,0x8A6C,0xC8D1,0x8A6D,0x8A6E,0xE5FB,0x8A6F,0x8A70, 0x8A71,0x8A72,0xB6CA,0xBCCB,0x8A73,0x8A74,0xD1FD,0xE6A1, 0x8A75,0xC3EE,0x8A76,0x8A77,0x8A78,0x8A79,0xE6A4,0x8A7A, 0x8A7B,0x8A7C,0x8A7D,0xE5FE,0xE6A5,0xCDD7,0x8A7E,0x8A80, 0xB7C1,0xE5FC,0xE5FD,0xE6A3,0x8A81,0x8A82,0xC4DD,0xE6A8, 0x8A83,0x8A84,0xE6A7,0x8A85,0x8A86,0x8A87,0x8A88,0x8A89, 0x8A8A,0xC3C3,0x8A8B,0xC6DE,0x8A8C,0x8A8D,0xE6AA,0x8A8E, 0x8A8F,0x8A90,0x8A91,0x8A92,0x8A93,0x8A94,0xC4B7,0x8A95, 0x8A96,0x8A97,0xE6A2,0xCABC,0x8A98,0x8A99,0x8A9A,0x8A9B, 0xBDE3,0xB9C3,0xE6A6,0xD0D5,0xCEAF,0x8A9C,0x8A9D,0xE6A9, 0xE6B0,0x8A9E,0xD2A6,0x8A9F,0xBDAA,0xE6AD,0x8AA0,0x8AA1, 0x8AA2,0x8AA3,0x8AA4,0xE6AF,0x8AA5,0xC0D1,0x8AA6,0x8AA7, 0xD2CC,0x8AA8,0x8AA9,0x8AAA,0xBCA7,0x8AAB,0x8AAC,0x8AAD, 0x8AAE,0x8AAF,0x8AB0,0x8AB1,0x8AB2,0x8AB3,0x8AB4,0x8AB5, 0x8AB6,0xE6B1,0x8AB7,0xD2F6,0x8AB8,0x8AB9,0x8ABA,0xD7CB, 0x8ABB,0xCDFE,0x8ABC,0xCDDE,0xC2A6,0xE6AB,0xE6AC,0xBDBF, 0xE6AE,0xE6B3,0x8ABD,0x8ABE,0xE6B2,0x8ABF,0x8AC0,0x8AC1, 0x8AC2,0xE6B6,0x8AC3,0xE6B8,0x8AC4,0x8AC5,0x8AC6,0x8AC7, 0xC4EF,0x8AC8,0x8AC9,0x8ACA,0xC4C8,0x8ACB,0x8ACC,0xBEEA, 0xC9EF,0x8ACD,0x8ACE,0xE6B7,0x8ACF,0xB6F0,0x8AD0,0x8AD1, 0x8AD2,0xC3E4,0x8AD3,0x8AD4,0x8AD5,0x8AD6,0x8AD7,0x8AD8, 0x8AD9,0xD3E9,0xE6B4,0x8ADA,0xE6B5,0x8ADB,0xC8A2,0x8ADC, 0x8ADD,0x8ADE,0x8ADF,0x8AE0,0xE6BD,0x8AE1,0x8AE2,0x8AE3, 0xE6B9,0x8AE4,0x8AE5,0x8AE6,0x8AE7,0x8AE8,0xC6C5,0x8AE9, 0x8AEA,0xCDF1,0xE6BB,0x8AEB,0x8AEC,0x8AED,0x8AEE,0x8AEF, 0x8AF0,0x8AF1,0x8AF2,0x8AF3,0x8AF4,0xE6BC,0x8AF5,0x8AF6, 0x8AF7,0x8AF8,0xBBE9,0x8AF9,0x8AFA,0x8AFB,0x8AFC,0x8AFD, 0x8AFE,0x8B40,0xE6BE,0x8B41,0x8B42,0x8B43,0x8B44,0xE6BA, 0x8B45,0x8B46,0xC0B7,0x8B47,0x8B48,0x8B49,0x8B4A,0x8B4B, 0x8B4C,0x8B4D,0x8B4E,0x8B4F,0xD3A4,0xE6BF,0xC9F4,0xE6C3, 0x8B50,0x8B51,0xE6C4,0x8B52,0x8B53,0x8B54,0x8B55,0xD0F6, 0x8B56,0x8B57,0x8B58,0x8B59,0x8B5A,0x8B5B,0x8B5C,0x8B5D, 0x8B5E,0x8B5F,0x8B60,0x8B61,0x8B62,0x8B63,0x8B64,0x8B65, 0x8B66,0x8B67,0xC3BD,0x8B68,0x8B69,0x8B6A,0x8B6B,0x8B6C, 0x8B6D,0x8B6E,0xC3C4,0xE6C2,0x8B6F,0x8B70,0x8B71,0x8B72, 0x8B73,0x8B74,0x8B75,0x8B76,0x8B77,0x8B78,0x8B79,0x8B7A, 0x8B7B,0x8B7C,0xE6C1,0x8B7D,0x8B7E,0x8B80,0x8B81,0x8B82, 0x8B83,0x8B84,0xE6C7,0xCFB1,0x8B85,0xEBF4,0x8B86,0x8B87, 0xE6CA,0x8B88,0x8B89,0x8B8A,0x8B8B,0x8B8C,0xE6C5,0x8B8D, 0x8B8E,0xBCDE,0xC9A9,0x8B8F,0x8B90,0x8B91,0x8B92,0x8B93, 0x8B94,0xBCB5,0x8B95,0x8B96,0xCFD3,0x8B97,0x8B98,0x8B99, 0x8B9A,0x8B9B,0xE6C8,0x8B9C,0xE6C9,0x8B9D,0xE6CE,0x8B9E, 0xE6D0,0x8B9F,0x8BA0,0x8BA1,0xE6D1,0x8BA2,0x8BA3,0x8BA4, 0xE6CB,0xB5D5,0x8BA5,0xE6CC,0x8BA6,0x8BA7,0xE6CF,0x8BA8, 0x8BA9,0xC4DB,0x8BAA,0xE6C6,0x8BAB,0x8BAC,0x8BAD,0x8BAE, 0x8BAF,0xE6CD,0x8BB0,0x8BB1,0x8BB2,0x8BB3,0x8BB4,0x8BB5, 0x8BB6,0x8BB7,0x8BB8,0x8BB9,0x8BBA,0x8BBB,0x8BBC,0x8BBD, 0x8BBE,0x8BBF,0x8BC0,0x8BC1,0x8BC2,0x8BC3,0x8BC4,0x8BC5, 0x8BC6,0xE6D2,0x8BC7,0x8BC8,0x8BC9,0x8BCA,0x8BCB,0x8BCC, 0x8BCD,0x8BCE,0x8BCF,0x8BD0,0x8BD1,0x8BD2,0xE6D4,0xE6D3, 0x8BD3,0x8BD4,0x8BD5,0x8BD6,0x8BD7,0x8BD8,0x8BD9,0x8BDA, 0x8BDB,0x8BDC,0x8BDD,0x8BDE,0x8BDF,0x8BE0,0x8BE1,0x8BE2, 0x8BE3,0x8BE4,0x8BE5,0x8BE6,0x8BE7,0x8BE8,0x8BE9,0x8BEA, 0x8BEB,0x8BEC,0xE6D5,0x8BED,0xD9F8,0x8BEE,0x8BEF,0xE6D6, 0x8BF0,0x8BF1,0x8BF2,0x8BF3,0x8BF4,0x8BF5,0x8BF6,0x8BF7, 0xE6D7,0x8BF8,0x8BF9,0x8BFA,0x8BFB,0x8BFC,0x8BFD,0x8BFE, 0x8C40,0x8C41,0x8C42,0x8C43,0x8C44,0x8C45,0x8C46,0x8C47, 0xD7D3,0xE6DD,0x8C48,0xE6DE,0xBFD7,0xD4D0,0x8C49,0xD7D6, 0xB4E6,0xCBEF,0xE6DA,0xD8C3,0xD7CE,0xD0A2,0x8C4A,0xC3CF, 0x8C4B,0x8C4C,0xE6DF,0xBCBE,0xB9C2,0xE6DB,0xD1A7,0x8C4D, 0x8C4E,0xBAA2,0xC2CF,0x8C4F,0xD8AB,0x8C50,0x8C51,0x8C52, 0xCAEB,0xE5EE,0x8C53,0xE6DC,0x8C54,0xB7F5,0x8C55,0x8C56, 0x8C57,0x8C58,0xC8E6,0x8C59,0x8C5A,0xC4F5,0x8C5B,0x8C5C, 0xE5B2,0xC4FE,0x8C5D,0xCBFC,0xE5B3,0xD5AC,0x8C5E,0xD3EE, 0xCAD8,0xB0B2,0x8C5F,0xCBCE,0xCDEA,0x8C60,0x8C61,0xBAEA, 0x8C62,0x8C63,0x8C64,0xE5B5,0x8C65,0xE5B4,0x8C66,0xD7DA, 0xB9D9,0xD6E6,0xB6A8,0xCDF0,0xD2CB,0xB1A6,0xCAB5,0x8C67, 0xB3E8,0xC9F3,0xBFCD,0xD0FB,0xCAD2,0xE5B6,0xBBC2,0x8C68, 0x8C69,0x8C6A,0xCFDC,0xB9AC,0x8C6B,0x8C6C,0x8C6D,0x8C6E, 0xD4D7,0x8C6F,0x8C70,0xBAA6,0xD1E7,0xCFFC,0xBCD2,0x8C71, 0xE5B7,0xC8DD,0x8C72,0x8C73,0x8C74,0xBFED,0xB1F6,0xCBDE, 0x8C75,0x8C76,0xBCC5,0x8C77,0xBCC4,0xD2FA,0xC3DC,0xBFDC, 0x8C78,0x8C79,0x8C7A,0x8C7B,0xB8BB,0x8C7C,0x8C7D,0x8C7E, 0xC3C2,0x8C80,0xBAAE,0xD4A2,0x8C81,0x8C82,0x8C83,0x8C84, 0x8C85,0x8C86,0x8C87,0x8C88,0x8C89,0xC7DE,0xC4AF,0xB2EC, 0x8C8A,0xB9D1,0x8C8B,0x8C8C,0xE5BB,0xC1C8,0x8C8D,0x8C8E, 0xD5AF,0x8C8F,0x8C90,0x8C91,0x8C92,0x8C93,0xE5BC,0x8C94, 0xE5BE,0x8C95,0x8C96,0x8C97,0x8C98,0x8C99,0x8C9A,0x8C9B, 0xB4E7,0xB6D4,0xCBC2,0xD1B0,0xB5BC,0x8C9C,0x8C9D,0xCAD9, 0x8C9E,0xB7E2,0x8C9F,0x8CA0,0xC9E4,0x8CA1,0xBDAB,0x8CA2, 0x8CA3,0xCEBE,0xD7F0,0x8CA4,0x8CA5,0x8CA6,0x8CA7,0xD0A1, 0x8CA8,0xC9D9,0x8CA9,0x8CAA,0xB6FB,0xE6D8,0xBCE2,0x8CAB, 0xB3BE,0x8CAC,0xC9D0,0x8CAD,0xE6D9,0xB3A2,0x8CAE,0x8CAF, 0x8CB0,0x8CB1,0xDECC,0x8CB2,0xD3C8,0xDECD,0x8CB3,0xD2A2, 0x8CB4,0x8CB5,0x8CB6,0x8CB7,0xDECE,0x8CB8,0x8CB9,0x8CBA, 0x8CBB,0xBECD,0x8CBC,0x8CBD,0xDECF,0x8CBE,0x8CBF,0x8CC0, 0xCAAC,0xD2FC,0xB3DF,0xE5EA,0xC4E1,0xBEA1,0xCEB2,0xC4F2, 0xBED6,0xC6A8,0xB2E3,0x8CC1,0x8CC2,0xBED3,0x8CC3,0x8CC4, 0xC7FC,0xCCEB,0xBDEC,0xCEDD,0x8CC5,0x8CC6,0xCABA,0xC6C1, 0xE5EC,0xD0BC,0x8CC7,0x8CC8,0x8CC9,0xD5B9,0x8CCA,0x8CCB, 0x8CCC,0xE5ED,0x8CCD,0x8CCE,0x8CCF,0x8CD0,0xCAF4,0x8CD1, 0xCDC0,0xC2C5,0x8CD2,0xE5EF,0x8CD3,0xC2C4,0xE5F0,0x8CD4, 0x8CD5,0x8CD6,0x8CD7,0x8CD8,0x8CD9,0x8CDA,0xE5F8,0xCDCD, 0x8CDB,0xC9BD,0x8CDC,0x8CDD,0x8CDE,0x8CDF,0x8CE0,0x8CE1, 0x8CE2,0xD2D9,0xE1A8,0x8CE3,0x8CE4,0x8CE5,0x8CE6,0xD3EC, 0x8CE7,0xCBEA,0xC6F1,0x8CE8,0x8CE9,0x8CEA,0x8CEB,0x8CEC, 0xE1AC,0x8CED,0x8CEE,0x8CEF,0xE1A7,0xE1A9,0x8CF0,0x8CF1, 0xE1AA,0xE1AF,0x8CF2,0x8CF3,0xB2ED,0x8CF4,0xE1AB,0xB8DA, 0xE1AD,0xE1AE,0xE1B0,0xB5BA,0xE1B1,0x8CF5,0x8CF6,0x8CF7, 0x8CF8,0x8CF9,0xE1B3,0xE1B8,0x8CFA,0x8CFB,0x8CFC,0x8CFD, 0x8CFE,0xD1D2,0x8D40,0xE1B6,0xE1B5,0xC1EB,0x8D41,0x8D42, 0x8D43,0xE1B7,0x8D44,0xD4C0,0x8D45,0xE1B2,0x8D46,0xE1BA, 0xB0B6,0x8D47,0x8D48,0x8D49,0x8D4A,0xE1B4,0x8D4B,0xBFF9, 0x8D4C,0xE1B9,0x8D4D,0x8D4E,0xE1BB,0x8D4F,0x8D50,0x8D51, 0x8D52,0x8D53,0x8D54,0xE1BE,0x8D55,0x8D56,0x8D57,0x8D58, 0x8D59,0x8D5A,0xE1BC,0x8D5B,0x8D5C,0x8D5D,0x8D5E,0x8D5F, 0x8D60,0xD6C5,0x8D61,0x8D62,0x8D63,0x8D64,0x8D65,0x8D66, 0x8D67,0xCFBF,0x8D68,0x8D69,0xE1BD,0xE1BF,0xC2CD,0x8D6A, 0xB6EB,0x8D6B,0xD3F8,0x8D6C,0x8D6D,0xC7CD,0x8D6E,0x8D6F, 0xB7E5,0x8D70,0x8D71,0x8D72,0x8D73,0x8D74,0x8D75,0x8D76, 0x8D77,0x8D78,0x8D79,0xBEFE,0x8D7A,0x8D7B,0x8D7C,0x8D7D }; const static uint16 gbkEncoderInnerIndex2[]= { 0x8D7E,0x8D80,0xE1C0,0xE1C1,0x8D81,0x8D82,0xE1C7,0xB3E7, 0x8D83,0x8D84,0x8D85,0x8D86,0x8D87,0x8D88,0xC6E9,0x8D89, 0x8D8A,0x8D8B,0x8D8C,0x8D8D,0xB4DE,0x8D8E,0xD1C2,0x8D8F, 0x8D90,0x8D91,0x8D92,0xE1C8,0x8D93,0x8D94,0xE1C6,0x8D95, 0x8D96,0x8D97,0x8D98,0x8D99,0xE1C5,0x8D9A,0xE1C3,0xE1C2, 0x8D9B,0xB1C0,0x8D9C,0x8D9D,0x8D9E,0xD5B8,0xE1C4,0x8D9F, 0x8DA0,0x8DA1,0x8DA2,0x8DA3,0xE1CB,0x8DA4,0x8DA5,0x8DA6, 0x8DA7,0x8DA8,0x8DA9,0x8DAA,0x8DAB,0xE1CC,0xE1CA,0x8DAC, 0x8DAD,0x8DAE,0x8DAF,0x8DB0,0x8DB1,0x8DB2,0x8DB3,0xEFFA, 0x8DB4,0x8DB5,0xE1D3,0xE1D2,0xC7B6,0x8DB6,0x8DB7,0x8DB8, 0x8DB9,0x8DBA,0x8DBB,0x8DBC,0x8DBD,0x8DBE,0x8DBF,0x8DC0, 0xE1C9,0x8DC1,0x8DC2,0xE1CE,0x8DC3,0xE1D0,0x8DC4,0x8DC5, 0x8DC6,0x8DC7,0x8DC8,0x8DC9,0x8DCA,0x8DCB,0x8DCC,0x8DCD, 0x8DCE,0xE1D4,0x8DCF,0xE1D1,0xE1CD,0x8DD0,0x8DD1,0xE1CF, 0x8DD2,0x8DD3,0x8DD4,0x8DD5,0xE1D5,0x8DD6,0x8DD7,0x8DD8, 0x8DD9,0x8DDA,0x8DDB,0x8DDC,0x8DDD,0x8DDE,0x8DDF,0x8DE0, 0x8DE1,0x8DE2,0xE1D6,0x8DE3,0x8DE4,0x8DE5,0x8DE6,0x8DE7, 0x8DE8,0x8DE9,0x8DEA,0x8DEB,0x8DEC,0x8DED,0x8DEE,0x8DEF, 0x8DF0,0x8DF1,0x8DF2,0x8DF3,0x8DF4,0x8DF5,0x8DF6,0x8DF7, 0x8DF8,0xE1D7,0x8DF9,0x8DFA,0x8DFB,0xE1D8,0x8DFC,0x8DFD, 0x8DFE,0x8E40,0x8E41,0x8E42,0x8E43,0x8E44,0x8E45,0x8E46, 0x8E47,0x8E48,0x8E49,0x8E4A,0x8E4B,0x8E4C,0x8E4D,0x8E4E, 0x8E4F,0x8E50,0x8E51,0x8E52,0x8E53,0x8E54,0x8E55,0xE1DA, 0x8E56,0x8E57,0x8E58,0x8E59,0x8E5A,0x8E5B,0x8E5C,0x8E5D, 0x8E5E,0x8E5F,0x8E60,0x8E61,0x8E62,0xE1DB,0x8E63,0x8E64, 0x8E65,0x8E66,0x8E67,0x8E68,0x8E69,0xCEA1,0x8E6A,0x8E6B, 0x8E6C,0x8E6D,0x8E6E,0x8E6F,0x8E70,0x8E71,0x8E72,0x8E73, 0x8E74,0x8E75,0x8E76,0xE7DD,0x8E77,0xB4A8,0xD6DD,0x8E78, 0x8E79,0xD1B2,0xB3B2,0x8E7A,0x8E7B,0xB9A4,0xD7F3,0xC7C9, 0xBEDE,0xB9AE,0x8E7C,0xCED7,0x8E7D,0x8E7E,0xB2EE,0xDBCF, 0x8E80,0xBCBA,0xD2D1,0xCBC8,0xB0CD,0x8E81,0x8E82,0xCFEF, 0x8E83,0x8E84,0x8E85,0x8E86,0x8E87,0xD9E3,0xBDED,0x8E88, 0x8E89,0xB1D2,0xCAD0,0xB2BC,0x8E8A,0xCBA7,0xB7AB,0x8E8B, 0xCAA6,0x8E8C,0x8E8D,0x8E8E,0xCFA3,0x8E8F,0x8E90,0xE0F8, 0xD5CA,0xE0FB,0x8E91,0x8E92,0xE0FA,0xC5C1,0xCCFB,0x8E93, 0xC1B1,0xE0F9,0xD6E3,0xB2AF,0xD6C4,0xB5DB,0x8E94,0x8E95, 0x8E96,0x8E97,0x8E98,0x8E99,0x8E9A,0x8E9B,0xB4F8,0xD6A1, 0x8E9C,0x8E9D,0x8E9E,0x8E9F,0x8EA0,0xCFAF,0xB0EF,0x8EA1, 0x8EA2,0xE0FC,0x8EA3,0x8EA4,0x8EA5,0x8EA6,0x8EA7,0xE1A1, 0xB3A3,0x8EA8,0x8EA9,0xE0FD,0xE0FE,0xC3B1,0x8EAA,0x8EAB, 0x8EAC,0x8EAD,0xC3DD,0x8EAE,0xE1A2,0xB7F9,0x8EAF,0x8EB0, 0x8EB1,0x8EB2,0x8EB3,0x8EB4,0xBBCF,0x8EB5,0x8EB6,0x8EB7, 0x8EB8,0x8EB9,0x8EBA,0x8EBB,0xE1A3,0xC4BB,0x8EBC,0x8EBD, 0x8EBE,0x8EBF,0x8EC0,0xE1A4,0x8EC1,0x8EC2,0xE1A5,0x8EC3, 0x8EC4,0xE1A6,0xB4B1,0x8EC5,0x8EC6,0x8EC7,0x8EC8,0x8EC9, 0x8ECA,0x8ECB,0x8ECC,0x8ECD,0x8ECE,0x8ECF,0x8ED0,0x8ED1, 0x8ED2,0x8ED3,0xB8C9,0xC6BD,0xC4EA,0x8ED4,0xB2A2,0x8ED5, 0xD0D2,0x8ED6,0xE7DB,0xBBC3,0xD3D7,0xD3C4,0x8ED7,0xB9E3, 0xE2CF,0x8ED8,0x8ED9,0x8EDA,0xD7AF,0x8EDB,0xC7EC,0xB1D3, 0x8EDC,0x8EDD,0xB4B2,0xE2D1,0x8EDE,0x8EDF,0x8EE0,0xD0F2, 0xC2AE,0xE2D0,0x8EE1,0xBFE2,0xD3A6,0xB5D7,0xE2D2,0xB5EA, 0x8EE2,0xC3ED,0xB8FD,0x8EE3,0xB8AE,0x8EE4,0xC5D3,0xB7CF, 0xE2D4,0x8EE5,0x8EE6,0x8EE7,0x8EE8,0xE2D3,0xB6C8,0xD7F9, 0x8EE9,0x8EEA,0x8EEB,0x8EEC,0x8EED,0xCDA5,0x8EEE,0x8EEF, 0x8EF0,0x8EF1,0x8EF2,0xE2D8,0x8EF3,0xE2D6,0xCAFC,0xBFB5, 0xD3B9,0xE2D5,0x8EF4,0x8EF5,0x8EF6,0x8EF7,0xE2D7,0x8EF8, 0x8EF9,0x8EFA,0x8EFB,0x8EFC,0x8EFD,0x8EFE,0x8F40,0x8F41, 0x8F42,0xC1AE,0xC0C8,0x8F43,0x8F44,0x8F45,0x8F46,0x8F47, 0x8F48,0xE2DB,0xE2DA,0xC0AA,0x8F49,0x8F4A,0xC1CE,0x8F4B, 0x8F4C,0x8F4D,0x8F4E,0xE2DC,0x8F4F,0x8F50,0x8F51,0x8F52, 0x8F53,0x8F54,0x8F55,0x8F56,0x8F57,0x8F58,0x8F59,0x8F5A, 0xE2DD,0x8F5B,0xE2DE,0x8F5C,0x8F5D,0x8F5E,0x8F5F,0x8F60, 0x8F61,0x8F62,0x8F63,0x8F64,0xDBC8,0x8F65,0xD1D3,0xCDA2, 0x8F66,0x8F67,0xBDA8,0x8F68,0x8F69,0x8F6A,0xDEC3,0xD8A5, 0xBFAA,0xDBCD,0xD2EC,0xC6FA,0xC5AA,0x8F6B,0x8F6C,0x8F6D, 0xDEC4,0x8F6E,0xB1D7,0xDFAE,0x8F6F,0x8F70,0x8F71,0xCABD, 0x8F72,0xDFB1,0x8F73,0xB9AD,0x8F74,0xD2FD,0x8F75,0xB8A5, 0xBAEB,0x8F76,0x8F77,0xB3DA,0x8F78,0x8F79,0x8F7A,0xB5DC, 0xD5C5,0x8F7B,0x8F7C,0x8F7D,0x8F7E,0xC3D6,0xCFD2,0xBBA1, 0x8F80,0xE5F3,0xE5F2,0x8F81,0x8F82,0xE5F4,0x8F83,0xCDE4, 0x8F84,0xC8F5,0x8F85,0x8F86,0x8F87,0x8F88,0x8F89,0x8F8A, 0x8F8B,0xB5AF,0xC7BF,0x8F8C,0xE5F6,0x8F8D,0x8F8E,0x8F8F, 0xECB0,0x8F90,0x8F91,0x8F92,0x8F93,0x8F94,0x8F95,0x8F96, 0x8F97,0x8F98,0x8F99,0x8F9A,0x8F9B,0x8F9C,0x8F9D,0x8F9E, 0xE5E6,0x8F9F,0xB9E9,0xB5B1,0x8FA0,0xC2BC,0xE5E8,0xE5E7, 0xE5E9,0x8FA1,0x8FA2,0x8FA3,0x8FA4,0xD2CD,0x8FA5,0x8FA6, 0x8FA7,0xE1EA,0xD0CE,0x8FA8,0xCDAE,0x8FA9,0xD1E5,0x8FAA, 0x8FAB,0xB2CA,0xB1EB,0x8FAC,0xB1F2,0xC5ED,0x8FAD,0x8FAE, 0xD5C3,0xD3B0,0x8FAF,0xE1DC,0x8FB0,0x8FB1,0x8FB2,0xE1DD, 0x8FB3,0xD2DB,0x8FB4,0xB3B9,0xB1CB,0x8FB5,0x8FB6,0x8FB7, 0xCDF9,0xD5F7,0xE1DE,0x8FB8,0xBEB6,0xB4FD,0x8FB9,0xE1DF, 0xBADC,0xE1E0,0xBBB2,0xC2C9,0xE1E1,0x8FBA,0x8FBB,0x8FBC, 0xD0EC,0x8FBD,0xCDBD,0x8FBE,0x8FBF,0xE1E2,0x8FC0,0xB5C3, 0xC5C7,0xE1E3,0x8FC1,0x8FC2,0xE1E4,0x8FC3,0x8FC4,0x8FC5, 0x8FC6,0xD3F9,0x8FC7,0x8FC8,0x8FC9,0x8FCA,0x8FCB,0x8FCC, 0xE1E5,0x8FCD,0xD1AD,0x8FCE,0x8FCF,0xE1E6,0xCEA2,0x8FD0, 0x8FD1,0x8FD2,0x8FD3,0x8FD4,0x8FD5,0xE1E7,0x8FD6,0xB5C2, 0x8FD7,0x8FD8,0x8FD9,0x8FDA,0xE1E8,0xBBD5,0x8FDB,0x8FDC, 0x8FDD,0x8FDE,0x8FDF,0xD0C4,0xE2E0,0xB1D8,0xD2E4,0x8FE0, 0x8FE1,0xE2E1,0x8FE2,0x8FE3,0xBCC9,0xC8CC,0x8FE4,0xE2E3, 0xECFE,0xECFD,0xDFAF,0x8FE5,0x8FE6,0x8FE7,0xE2E2,0xD6BE, 0xCDFC,0xC3A6,0x8FE8,0x8FE9,0x8FEA,0xE3C3,0x8FEB,0x8FEC, 0xD6D2,0xE2E7,0x8FED,0x8FEE,0xE2E8,0x8FEF,0x8FF0,0xD3C7, 0x8FF1,0x8FF2,0xE2EC,0xBFEC,0x8FF3,0xE2ED,0xE2E5,0x8FF4, 0x8FF5,0xB3C0,0x8FF6,0x8FF7,0x8FF8,0xC4EE,0x8FF9,0x8FFA, 0xE2EE,0x8FFB,0x8FFC,0xD0C3,0x8FFD,0xBAF6,0xE2E9,0xB7DE, 0xBBB3,0xCCAC,0xCBCB,0xE2E4,0xE2E6,0xE2EA,0xE2EB,0x8FFE, 0x9040,0x9041,0xE2F7,0x9042,0x9043,0xE2F4,0xD4F5,0xE2F3, 0x9044,0x9045,0xC5AD,0x9046,0xD5FA,0xC5C2,0xB2C0,0x9047, 0x9048,0xE2EF,0x9049,0xE2F2,0xC1AF,0xCBBC,0x904A,0x904B, 0xB5A1,0xE2F9,0x904C,0x904D,0x904E,0xBCB1,0xE2F1,0xD0D4, 0xD4B9,0xE2F5,0xB9D6,0xE2F6,0x904F,0x9050,0x9051,0xC7D3, 0x9052,0x9053,0x9054,0x9055,0x9056,0xE2F0,0x9057,0x9058, 0x9059,0x905A,0x905B,0xD7DC,0xEDA1,0x905C,0x905D,0xE2F8, 0x905E,0xEDA5,0xE2FE,0xCAD1,0x905F,0x9060,0x9061,0x9062, 0x9063,0x9064,0x9065,0xC1B5,0x9066,0xBBD0,0x9067,0x9068, 0xBFD6,0x9069,0xBAE3,0x906A,0x906B,0xCBA1,0x906C,0x906D, 0x906E,0xEDA6,0xEDA3,0x906F,0x9070,0xEDA2,0x9071,0x9072, 0x9073,0x9074,0xBBD6,0xEDA7,0xD0F4,0x9075,0x9076,0xEDA4, 0xBADE,0xB6F7,0xE3A1,0xB6B2,0xCCF1,0xB9A7,0x9077,0xCFA2, 0xC7A1,0x9078,0x9079,0xBFD2,0x907A,0x907B,0xB6F1,0x907C, 0xE2FA,0xE2FB,0xE2FD,0xE2FC,0xC4D5,0xE3A2,0x907D,0xD3C1, 0x907E,0x9080,0x9081,0xE3A7,0xC7C4,0x9082,0x9083,0x9084, 0x9085,0xCFA4,0x9086,0x9087,0xE3A9,0xBAB7,0x9088,0x9089, 0x908A,0x908B,0xE3A8,0x908C,0xBBDA,0x908D,0xE3A3,0x908E, 0x908F,0x9090,0xE3A4,0xE3AA,0x9091,0xE3A6,0x9092,0xCEF2, 0xD3C6,0x9093,0x9094,0xBBBC,0x9095,0x9096,0xD4C3,0x9097, 0xC4FA,0x9098,0x9099,0xEDA8,0xD0FC,0xE3A5,0x909A,0xC3F5, 0x909B,0xE3AD,0xB1AF,0x909C,0xE3B2,0x909D,0x909E,0x909F, 0xBCC2,0x90A0,0x90A1,0xE3AC,0xB5BF,0x90A2,0x90A3,0x90A4, 0x90A5,0x90A6,0x90A7,0x90A8,0x90A9,0xC7E9,0xE3B0,0x90AA, 0x90AB,0x90AC,0xBEAA,0xCDEF,0x90AD,0x90AE,0x90AF,0x90B0, 0x90B1,0xBBF3,0x90B2,0x90B3,0x90B4,0xCCE8,0x90B5,0x90B6, 0xE3AF,0x90B7,0xE3B1,0x90B8,0xCFA7,0xE3AE,0x90B9,0xCEA9, 0xBBDD,0x90BA,0x90BB,0x90BC,0x90BD,0x90BE,0xB5EB,0xBEE5, 0xB2D2,0xB3CD,0x90BF,0xB1B9,0xE3AB,0xB2D1,0xB5AC,0xB9DF, 0xB6E8,0x90C0,0x90C1,0xCFEB,0xE3B7,0x90C2,0xBBCC,0x90C3, 0x90C4,0xC8C7,0xD0CA,0x90C5,0x90C6,0x90C7,0x90C8,0x90C9, 0xE3B8,0xB3EE,0x90CA,0x90CB,0x90CC,0x90CD,0xEDA9,0x90CE, 0xD3FA,0xD3E4,0x90CF,0x90D0,0x90D1,0xEDAA,0xE3B9,0xD2E2, 0x90D2,0x90D3,0x90D4,0x90D5,0x90D6,0xE3B5,0x90D7,0x90D8, 0x90D9,0x90DA,0xD3DE,0x90DB,0x90DC,0x90DD,0x90DE,0xB8D0, 0xE3B3,0x90DF,0x90E0,0xE3B6,0xB7DF,0x90E1,0xE3B4,0xC0A2, 0x90E2,0x90E3,0x90E4,0xE3BA,0x90E5,0x90E6,0x90E7,0x90E8, 0x90E9,0x90EA,0x90EB,0x90EC,0x90ED,0x90EE,0x90EF,0x90F0, 0x90F1,0x90F2,0x90F3,0x90F4,0x90F5,0x90F6,0x90F7,0xD4B8, 0x90F8,0x90F9,0x90FA,0x90FB,0x90FC,0x90FD,0x90FE,0x9140, 0xB4C8,0x9141,0xE3BB,0x9142,0xBBC5,0x9143,0xC9F7,0x9144, 0x9145,0xC9E5,0x9146,0x9147,0x9148,0xC4BD,0x9149,0x914A, 0x914B,0x914C,0x914D,0x914E,0x914F,0xEDAB,0x9150,0x9151, 0x9152,0x9153,0xC2FD,0x9154,0x9155,0x9156,0x9157,0xBBDB, 0xBFAE,0x9158,0x9159,0x915A,0x915B,0x915C,0x915D,0x915E, 0xCEBF,0x915F,0x9160,0x9161,0x9162,0xE3BC,0x9163,0xBFB6, 0x9164,0x9165,0x9166,0x9167,0x9168,0x9169,0x916A,0x916B, 0x916C,0x916D,0x916E,0x916F,0x9170,0x9171,0x9172,0x9173, 0x9174,0x9175,0x9176,0xB1EF,0x9177,0x9178,0xD4F7,0x9179, 0x917A,0x917B,0x917C,0x917D,0xE3BE,0x917E,0x9180,0x9181, 0x9182,0x9183,0x9184,0x9185,0x9186,0xEDAD,0x9187,0x9188, 0x9189,0x918A,0x918B,0x918C,0x918D,0x918E,0x918F,0xE3BF, 0xBAA9,0xEDAC,0x9190,0x9191,0xE3BD,0x9192,0x9193,0x9194, 0x9195,0x9196,0x9197,0x9198,0x9199,0x919A,0x919B,0xE3C0, 0x919C,0x919D,0x919E,0x919F,0x91A0,0x91A1,0xBAB6,0x91A2, 0x91A3,0x91A4,0xB6AE,0x91A5,0x91A6,0x91A7,0x91A8,0x91A9, 0xD0B8,0x91AA,0xB0C3,0xEDAE,0x91AB,0x91AC,0x91AD,0x91AE, 0x91AF,0xEDAF,0xC0C1,0x91B0,0xE3C1,0x91B1,0x91B2,0x91B3, 0x91B4,0x91B5,0x91B6,0x91B7,0x91B8,0x91B9,0x91BA,0x91BB, 0x91BC,0x91BD,0x91BE,0x91BF,0x91C0,0x91C1,0xC5B3,0x91C2, 0x91C3,0x91C4,0x91C5,0x91C6,0x91C7,0x91C8,0x91C9,0x91CA, 0x91CB,0x91CC,0x91CD,0x91CE,0x91CF,0xE3C2,0x91D0,0x91D1, 0x91D2,0x91D3,0x91D4,0x91D5,0x91D6,0x91D7,0x91D8,0xDCB2, 0x91D9,0x91DA,0x91DB,0x91DC,0x91DD,0x91DE,0xEDB0,0x91DF, 0xB8EA,0x91E0,0xCEEC,0xEAA7,0xD0E7,0xCAF9,0xC8D6,0xCFB7, 0xB3C9,0xCED2,0xBDE4,0x91E1,0x91E2,0xE3DE,0xBBF2,0xEAA8, 0xD5BD,0x91E3,0xC6DD,0xEAA9,0x91E4,0x91E5,0x91E6,0xEAAA, 0x91E7,0xEAAC,0xEAAB,0x91E8,0xEAAE,0xEAAD,0x91E9,0x91EA, 0x91EB,0x91EC,0xBDD8,0x91ED,0xEAAF,0x91EE,0xC2BE,0x91EF, 0x91F0,0x91F1,0x91F2,0xB4C1,0xB4F7,0x91F3,0x91F4,0xBBA7, 0x91F5,0x91F6,0x91F7,0x91F8,0x91F9,0xECE6,0xECE5,0xB7BF, 0xCBF9,0xB1E2,0x91FA,0xECE7,0x91FB,0x91FC,0x91FD,0xC9C8, 0xECE8,0xECE9,0x91FE,0xCAD6,0xDED0,0xB2C5,0xD4FA,0x9240, 0x9241,0xC6CB,0xB0C7,0xB4F2,0xC8D3,0x9242,0x9243,0x9244, 0xCDD0,0x9245,0x9246,0xBFB8,0x9247,0x9248,0x9249,0x924A, 0x924B,0x924C,0x924D,0xBFDB,0x924E,0x924F,0xC7A4,0xD6B4, 0x9250,0xC0A9,0xDED1,0xC9A8,0xD1EF,0xC5A4,0xB0E7,0xB3B6, 0xC8C5,0x9251,0x9252,0xB0E2,0x9253,0x9254,0xB7F6,0x9255, 0x9256,0xC5FA,0x9257,0x9258,0xB6F3,0x9259,0xD5D2,0xB3D0, 0xBCBC,0x925A,0x925B,0x925C,0xB3AD,0x925D,0x925E,0x925F, 0x9260,0xBEF1,0xB0D1,0x9261,0x9262,0x9263,0x9264,0x9265, 0x9266,0xD2D6,0xCAE3,0xD7A5,0x9267,0xCDB6,0xB6B6,0xBFB9, 0xD5DB,0x9268,0xB8A7,0xC5D7,0x9269,0x926A,0x926B,0xDED2, 0xBFD9,0xC2D5,0xC7C0,0x926C,0xBBA4,0xB1A8,0x926D,0x926E, 0xC5EA,0x926F,0x9270,0xC5FB,0xCCA7,0x9271,0x9272,0x9273, 0x9274,0xB1A7,0x9275,0x9276,0x9277,0xB5D6,0x9278,0x9279, 0x927A,0xC4A8,0x927B,0xDED3,0xD1BA,0xB3E9,0x927C,0xC3F2, 0x927D,0x927E,0xB7F7,0x9280,0xD6F4,0xB5A3,0xB2F0,0xC4B4, 0xC4E9,0xC0AD,0xDED4,0x9281,0xB0E8,0xC5C4,0xC1E0,0x9282, 0xB9D5,0x9283,0xBEDC,0xCDD8,0xB0CE,0x9284,0xCDCF,0xDED6, 0xBED0,0xD7BE,0xDED5,0xD5D0,0xB0DD,0x9285,0x9286,0xC4E2, 0x9287,0x9288,0xC2A3,0xBCF0,0x9289,0xD3B5,0xC0B9,0xC5A1, 0xB2A6,0xD4F1,0x928A,0x928B,0xC0A8,0xCAC3,0xDED7,0xD5FC, 0x928C,0xB9B0,0x928D,0xC8AD,0xCBA9,0x928E,0xDED9,0xBFBD, 0x928F,0x9290,0x9291,0x9292,0xC6B4,0xD7A7,0xCAB0,0xC4C3, 0x9293,0xB3D6,0xB9D2,0x9294,0x9295,0x9296,0x9297,0xD6B8, 0xEAFC,0xB0B4,0x9298,0x9299,0x929A,0x929B,0xBFE6,0x929C, 0x929D,0xCCF4,0x929E,0x929F,0x92A0,0x92A1,0xCDDA,0x92A2, 0x92A3,0x92A4,0xD6BF,0xC2CE,0x92A5,0xCECE,0xCCA2,0xD0AE, 0xC4D3,0xB5B2,0xDED8,0xD5F5,0xBCB7,0xBBD3,0x92A6,0x92A7, 0xB0A4,0x92A8,0xC5B2,0xB4EC,0x92A9,0x92AA,0x92AB,0xD5F1, 0x92AC,0x92AD,0xEAFD,0x92AE,0x92AF,0x92B0,0x92B1,0x92B2, 0x92B3,0xDEDA,0xCDA6,0x92B4,0x92B5,0xCDEC,0x92B6,0x92B7, 0x92B8,0x92B9,0xCEE6,0xDEDC,0x92BA,0xCDB1,0xC0A6,0x92BB, 0x92BC,0xD7BD,0x92BD,0xDEDB,0xB0C6,0xBAB4,0xC9D3,0xC4F3, 0xBEE8,0x92BE,0x92BF,0x92C0,0x92C1,0xB2B6,0x92C2,0x92C3, 0x92C4,0x92C5,0x92C6,0x92C7,0x92C8,0x92C9,0xC0CC,0xCBF0, 0x92CA,0xBCF1,0xBBBB,0xB5B7,0x92CB,0x92CC,0x92CD,0xC5F5, 0x92CE,0xDEE6,0x92CF,0x92D0,0x92D1,0xDEE3,0xBEDD,0x92D2, 0x92D3,0xDEDF,0x92D4,0x92D5,0x92D6,0x92D7,0xB4B7,0xBDDD, 0x92D8,0x92D9,0xDEE0,0xC4ED,0x92DA,0x92DB,0x92DC,0x92DD, 0xCFC6,0x92DE,0xB5E0,0x92DF,0x92E0,0x92E1,0x92E2,0xB6DE, 0xCADA,0xB5F4,0xDEE5,0x92E3,0xD5C6,0x92E4,0xDEE1,0xCCCD, 0xC6FE,0x92E5,0xC5C5,0x92E6,0x92E7,0x92E8,0xD2B4,0x92E9, 0xBEF2,0x92EA,0x92EB,0x92EC,0x92ED,0x92EE,0x92EF,0x92F0, 0xC2D3,0x92F1,0xCCBD,0xB3B8,0x92F2,0xBDD3,0x92F3,0xBFD8, 0xCDC6,0xD1DA,0xB4EB,0x92F4,0xDEE4,0xDEDD,0xDEE7,0x92F5, 0xEAFE,0x92F6,0x92F7,0xC2B0,0xDEE2,0x92F8,0x92F9,0xD6C0, 0xB5A7,0x92FA,0xB2F4,0x92FB,0xDEE8,0x92FC,0xDEF2,0x92FD, 0x92FE,0x9340,0x9341,0x9342,0xDEED,0x9343,0xDEF1,0x9344, 0x9345,0xC8E0,0x9346,0x9347,0x9348,0xD7E1,0xDEEF,0xC3E8, 0xCCE1,0x9349,0xB2E5,0x934A,0x934B,0x934C,0xD2BE,0x934D, 0x934E,0x934F,0x9350,0x9351,0x9352,0x9353,0xDEEE,0x9354, 0xDEEB,0xCED5,0x9355,0xB4A7,0x9356,0x9357,0x9358,0x9359, 0x935A,0xBFAB,0xBEBE,0x935B,0x935C,0xBDD2,0x935D,0x935E, 0x935F,0x9360,0xDEE9,0x9361,0xD4AE,0x9362,0xDEDE,0x9363, 0xDEEA,0x9364,0x9365,0x9366,0x9367,0xC0BF,0x9368,0xDEEC, 0xB2F3,0xB8E9,0xC2A7,0x9369,0x936A,0xBDC1,0x936B,0x936C, 0x936D,0x936E,0x936F,0xDEF5,0xDEF8,0x9370,0x9371,0xB2AB, 0xB4A4,0x9372,0x9373,0xB4EA,0xC9A6,0x9374,0x9375,0x9376, 0x9377,0x9378,0x9379,0xDEF6,0xCBD1,0x937A,0xB8E3,0x937B, 0xDEF7,0xDEFA,0x937C,0x937D,0x937E,0x9380,0xDEF9,0x9381, 0x9382,0x9383,0xCCC2,0x9384,0xB0E1,0xB4EE,0x9385,0x9386, 0x9387,0x9388,0x9389,0x938A,0xE5BA,0x938B,0x938C,0x938D, 0x938E,0x938F,0xD0AF,0x9390,0x9391,0xB2EB,0x9392,0xEBA1, 0x9393,0xDEF4,0x9394,0x9395,0xC9E3,0xDEF3,0xB0DA,0xD2A1, 0xB1F7,0x9396,0xCCAF,0x9397,0x9398,0x9399,0x939A,0x939B, 0x939C,0x939D,0xDEF0,0x939E,0xCBA4,0x939F,0x93A0,0x93A1, 0xD5AA,0x93A2,0x93A3,0x93A4,0x93A5,0x93A6,0xDEFB,0x93A7, 0x93A8,0x93A9,0x93AA,0x93AB,0x93AC,0x93AD,0x93AE,0xB4DD, 0x93AF,0xC4A6,0x93B0,0x93B1,0x93B2,0xDEFD,0x93B3,0x93B4, 0x93B5,0x93B6,0x93B7,0x93B8,0x93B9,0x93BA,0x93BB,0x93BC, 0xC3FE,0xC4A1,0xDFA1,0x93BD,0x93BE,0x93BF,0x93C0,0x93C1, 0x93C2,0x93C3,0xC1CC,0x93C4,0xDEFC,0xBEEF,0x93C5,0xC6B2, 0x93C6,0x93C7,0x93C8,0x93C9,0x93CA,0x93CB,0x93CC,0x93CD, 0x93CE,0xB3C5,0xC8F6,0x93CF,0x93D0,0xCBBA,0xDEFE,0x93D1, 0x93D2,0xDFA4,0x93D3,0x93D4,0x93D5,0x93D6,0xD7B2,0x93D7, 0x93D8,0x93D9,0x93DA,0x93DB,0xB3B7,0x93DC,0x93DD,0x93DE, 0x93DF,0xC1C3,0x93E0,0x93E1,0xC7CB,0xB2A5,0xB4E9,0x93E2, 0xD7AB,0x93E3,0x93E4,0x93E5,0x93E6,0xC4EC,0x93E7,0xDFA2, 0xDFA3,0x93E8,0xDFA5,0x93E9,0xBAB3,0x93EA,0x93EB,0x93EC, 0xDFA6,0x93ED,0xC0DE,0x93EE,0x93EF,0xC9C3,0x93F0,0x93F1, 0x93F2,0x93F3,0x93F4,0x93F5,0x93F6,0xB2D9,0xC7E6,0x93F7, 0xDFA7,0x93F8,0xC7DC,0x93F9,0x93FA,0x93FB,0x93FC,0xDFA8, 0xEBA2,0x93FD,0x93FE,0x9440,0x9441,0x9442,0xCBD3,0x9443, 0x9444,0x9445,0xDFAA,0x9446,0xDFA9,0x9447,0xB2C1,0x9448, 0x9449,0x944A,0x944B,0x944C,0x944D,0x944E,0x944F,0x9450, 0x9451,0x9452,0x9453,0x9454,0x9455,0x9456,0x9457,0x9458, 0x9459,0x945A,0x945B,0x945C,0x945D,0x945E,0x945F,0x9460, 0xC5CA,0x9461,0x9462,0x9463,0x9464,0x9465,0x9466,0x9467, 0x9468,0xDFAB,0x9469,0x946A,0x946B,0x946C,0x946D,0x946E, 0x946F,0x9470,0xD4DC,0x9471,0x9472,0x9473,0x9474,0x9475, 0xC8C1,0x9476,0x9477,0x9478,0x9479,0x947A,0x947B,0x947C, 0x947D,0x947E,0x9480,0x9481,0x9482,0xDFAC,0x9483,0x9484, 0x9485,0x9486,0x9487,0xBEF0,0x9488,0x9489,0xDFAD,0xD6A7, 0x948A,0x948B,0x948C,0x948D,0xEAB7,0xEBB6,0xCAD5,0x948E, 0xD8FC,0xB8C4,0x948F,0xB9A5,0x9490,0x9491,0xB7C5,0xD5FE, 0x9492,0x9493,0x9494,0x9495,0x9496,0xB9CA,0x9497,0x9498, 0xD0A7,0xF4CD,0x9499,0x949A,0xB5D0,0x949B,0x949C,0xC3F4, 0x949D,0xBEC8,0x949E,0x949F,0x94A0,0xEBB7,0xB0BD,0x94A1, 0x94A2,0xBDCC,0x94A3,0xC1B2,0x94A4,0xB1D6,0xB3A8,0x94A5, 0x94A6,0x94A7,0xB8D2,0xC9A2,0x94A8,0x94A9,0xB6D8,0x94AA, 0x94AB,0x94AC,0x94AD,0xEBB8,0xBEB4,0x94AE,0x94AF,0x94B0, 0xCAFD,0x94B1,0xC7C3,0x94B2,0xD5FB,0x94B3,0x94B4,0xB7F3, 0x94B5,0x94B6,0x94B7,0x94B8,0x94B9,0x94BA,0x94BB,0x94BC, 0x94BD,0x94BE,0x94BF,0x94C0,0x94C1,0x94C2,0x94C3,0xCEC4, 0x94C4,0x94C5,0x94C6,0xD5AB,0xB1F3,0x94C7,0x94C8,0x94C9, 0xECB3,0xB0DF,0x94CA,0xECB5,0x94CB,0x94CC,0x94CD,0xB6B7, 0x94CE,0xC1CF,0x94CF,0xF5FA,0xD0B1,0x94D0,0x94D1,0xD5E5, 0x94D2,0xCED3,0x94D3,0x94D4,0xBDEF,0xB3E2,0x94D5,0xB8AB, 0x94D6,0xD5B6,0x94D7,0xEDBD,0x94D8,0xB6CF,0x94D9,0xCBB9, 0xD0C2,0x94DA,0x94DB,0x94DC,0x94DD,0x94DE,0x94DF,0x94E0, 0x94E1,0xB7BD,0x94E2,0x94E3,0xECB6,0xCAA9,0x94E4,0x94E5, 0x94E6,0xC5D4,0x94E7,0xECB9,0xECB8,0xC2C3,0xECB7,0x94E8, 0x94E9,0x94EA,0x94EB,0xD0FD,0xECBA,0x94EC,0xECBB,0xD7E5, 0x94ED,0x94EE,0xECBC,0x94EF,0x94F0,0x94F1,0xECBD,0xC6EC, 0x94F2,0x94F3,0x94F4,0x94F5,0x94F6,0x94F7,0x94F8,0x94F9, 0xCEDE,0x94FA,0xBCC8,0x94FB,0x94FC,0xC8D5,0xB5A9,0xBEC9, 0xD6BC,0xD4E7,0x94FD,0x94FE,0xD1AE,0xD0F1,0xEAB8,0xEAB9, 0xEABA,0xBAB5,0x9540,0x9541,0x9542,0x9543,0xCAB1,0xBFF5, 0x9544,0x9545,0xCDFA,0x9546,0x9547,0x9548,0x9549,0x954A, 0xEAC0,0x954B,0xB0BA,0xEABE,0x954C,0x954D,0xC0A5,0x954E, 0x954F,0x9550,0xEABB,0x9551,0xB2FD,0x9552,0xC3F7,0xBBE8, 0x9553,0x9554,0x9555,0xD2D7,0xCEF4,0xEABF,0x9556,0x9557, 0x9558,0xEABC,0x9559,0x955A,0x955B,0xEAC3,0x955C,0xD0C7, 0xD3B3,0x955D,0x955E,0x955F,0x9560,0xB4BA,0x9561,0xC3C1, 0xD7F2,0x9562,0x9563,0x9564,0x9565,0xD5D1,0x9566,0xCAC7, 0x9567,0xEAC5,0x9568,0x9569,0xEAC4,0xEAC7,0xEAC6,0x956A, 0x956B,0x956C,0x956D,0x956E,0xD6E7,0x956F,0xCFD4,0x9570, 0x9571,0xEACB,0x9572,0xBBCE,0x9573,0x9574,0x9575,0x9576, 0x9577,0x9578,0x9579,0xBDFA,0xC9CE,0x957A,0x957B,0xEACC, 0x957C,0x957D,0xC9B9,0xCFFE,0xEACA,0xD4CE,0xEACD,0xEACF, 0x957E,0x9580,0xCDED,0x9581,0x9582,0x9583,0x9584,0xEAC9, 0x9585,0xEACE,0x9586,0x9587,0xCEEE,0x9588,0xBBDE,0x9589, 0xB3BF,0x958A,0x958B,0x958C,0x958D,0x958E,0xC6D5,0xBEB0, 0xCEFA,0x958F,0x9590,0x9591,0xC7E7,0x9592,0xBEA7,0xEAD0, 0x9593,0x9594,0xD6C7,0x9595,0x9596,0x9597,0xC1C0,0x9598, 0x9599,0x959A,0xD4DD,0x959B,0xEAD1,0x959C,0x959D,0xCFBE, 0x959E,0x959F,0x95A0,0x95A1,0xEAD2,0x95A2,0x95A3,0x95A4, 0x95A5,0xCAEE,0x95A6,0x95A7,0x95A8,0x95A9,0xC5AF,0xB0B5, 0x95AA,0x95AB,0x95AC,0x95AD,0x95AE,0xEAD4,0x95AF,0x95B0, 0x95B1,0x95B2,0x95B3,0x95B4,0x95B5,0x95B6,0x95B7,0xEAD3, 0xF4DF,0x95B8,0x95B9,0x95BA,0x95BB,0x95BC,0xC4BA,0x95BD, 0x95BE,0x95BF,0x95C0,0x95C1,0xB1A9,0x95C2,0x95C3,0x95C4, 0x95C5,0xE5DF,0x95C6,0x95C7,0x95C8,0x95C9,0xEAD5,0x95CA, 0x95CB,0x95CC,0x95CD,0x95CE,0x95CF,0x95D0,0x95D1,0x95D2, 0x95D3,0x95D4,0x95D5,0x95D6,0x95D7,0x95D8,0x95D9,0x95DA, 0x95DB,0x95DC,0x95DD,0x95DE,0x95DF,0x95E0,0x95E1,0x95E2, 0x95E3,0xCAEF,0x95E4,0xEAD6,0xEAD7,0xC6D8,0x95E5,0x95E6, 0x95E7,0x95E8,0x95E9,0x95EA,0x95EB,0x95EC,0xEAD8,0x95ED, 0x95EE,0xEAD9,0x95EF,0x95F0,0x95F1,0x95F2,0x95F3,0x95F4, 0xD4BB,0x95F5,0xC7FA,0xD2B7,0xB8FC,0x95F6,0x95F7,0xEAC2, 0x95F8,0xB2DC,0x95F9,0x95FA,0xC2FC,0x95FB,0xD4F8,0xCCE6, 0xD7EE,0x95FC,0x95FD,0x95FE,0x9640,0x9641,0x9642,0x9643, 0xD4C2,0xD3D0,0xEBC3,0xC5F3,0x9644,0xB7FE,0x9645,0x9646, 0xEBD4,0x9647,0x9648,0x9649,0xCBB7,0xEBDE,0x964A,0xC0CA, 0x964B,0x964C,0x964D,0xCDFB,0x964E,0xB3AF,0x964F,0xC6DA, 0x9650,0x9651,0x9652,0x9653,0x9654,0x9655,0xEBFC,0x9656, 0xC4BE,0x9657,0xCEB4,0xC4A9,0xB1BE,0xD4FD,0x9658,0xCAF5, 0x9659,0xD6EC,0x965A,0x965B,0xC6D3,0xB6E4,0x965C,0x965D, 0x965E,0x965F,0xBBFA,0x9660,0x9661,0xD0E0,0x9662,0x9663, 0xC9B1,0x9664,0xD4D3,0xC8A8,0x9665,0x9666,0xB8CB,0x9667, 0xE8BE,0xC9BC,0x9668,0x9669,0xE8BB,0x966A,0xC0EE,0xD0D3, 0xB2C4,0xB4E5,0x966B,0xE8BC,0x966C,0x966D,0xD5C8,0x966E, 0x966F,0x9670,0x9671,0x9672,0xB6C5,0x9673,0xE8BD,0xCAF8, 0xB8DC,0xCCF5,0x9674,0x9675,0x9676,0xC0B4,0x9677,0x9678, 0xD1EE,0xE8BF,0xE8C2,0x9679,0x967A,0xBABC,0x967B,0xB1AD, 0xBDDC,0x967C,0xEABD,0xE8C3,0x967D,0xE8C6,0x967E,0xE8CB, 0x9680,0x9681,0x9682,0x9683,0xE8CC,0x9684,0xCBC9,0xB0E5, 0x9685,0xBCAB,0x9686,0x9687,0xB9B9,0x9688,0x9689,0xE8C1, 0x968A,0xCDF7,0x968B,0xE8CA,0x968C,0x968D,0x968E,0x968F, 0xCEF6,0x9690,0x9691,0x9692,0x9693,0xD5ED,0x9694,0xC1D6, 0xE8C4,0x9695,0xC3B6,0x9696,0xB9FB,0xD6A6,0xE8C8,0x9697, 0x9698,0x9699,0xCAE0,0xD4E6,0x969A,0xE8C0,0x969B,0xE8C5, 0xE8C7,0x969C,0xC7B9,0xB7E3,0x969D,0xE8C9,0x969E,0xBFDD, 0xE8D2,0x969F,0x96A0,0xE8D7,0x96A1,0xE8D5,0xBCDC,0xBCCF, 0xE8DB,0x96A2,0x96A3,0x96A4,0x96A5,0x96A6,0x96A7,0x96A8, 0x96A9,0xE8DE,0x96AA,0xE8DA,0xB1FA,0x96AB,0x96AC,0x96AD, 0x96AE,0x96AF,0x96B0,0x96B1,0x96B2,0x96B3,0x96B4,0xB0D8, 0xC4B3,0xB8CC,0xC6E2,0xC8BE,0xC8E1,0x96B5,0x96B6,0x96B7, 0xE8CF,0xE8D4,0xE8D6,0x96B8,0xB9F1,0xE8D8,0xD7F5,0x96B9, 0xC4FB,0x96BA,0xE8DC,0x96BB,0x96BC,0xB2E9,0x96BD,0x96BE, 0x96BF,0xE8D1,0x96C0,0x96C1,0xBCED,0x96C2,0x96C3,0xBFC2, 0xE8CD,0xD6F9,0x96C4,0xC1F8,0xB2F1,0x96C5,0x96C6,0x96C7, 0x96C8,0x96C9,0x96CA,0x96CB,0x96CC,0xE8DF,0x96CD,0xCAC1, 0xE8D9,0x96CE,0x96CF,0x96D0,0x96D1,0xD5A4,0x96D2,0xB1EA, 0xD5BB,0xE8CE,0xE8D0,0xB6B0,0xE8D3,0x96D3,0xE8DD,0xC0B8, 0x96D4,0xCAF7,0x96D5,0xCBA8,0x96D6,0x96D7,0xC6DC,0xC0F5, 0x96D8,0x96D9,0x96DA,0x96DB,0x96DC,0xE8E9,0x96DD,0x96DE, 0x96DF,0xD0A3,0x96E0,0x96E1,0x96E2,0x96E3,0x96E4,0x96E5, 0x96E6,0xE8F2,0xD6EA,0x96E7,0x96E8,0x96E9,0x96EA,0x96EB, 0x96EC,0x96ED,0xE8E0,0xE8E1,0x96EE,0x96EF,0x96F0,0xD1F9, 0xBACB,0xB8F9,0x96F1,0x96F2,0xB8F1,0xD4D4,0xE8EF,0x96F3, 0xE8EE,0xE8EC,0xB9F0,0xCCD2,0xE8E6,0xCEA6,0xBFF2,0x96F4, 0xB0B8,0xE8F1,0xE8F0,0x96F5,0xD7C0,0x96F6,0xE8E4,0x96F7, 0xCDA9,0xC9A3,0x96F8,0xBBB8,0xBDDB,0xE8EA,0x96F9,0x96FA, 0x96FB,0x96FC,0x96FD,0x96FE,0x9740,0x9741,0x9742,0x9743, 0xE8E2,0xE8E3,0xE8E5,0xB5B5,0xE8E7,0xC7C5,0xE8EB,0xE8ED, 0xBDB0,0xD7AE,0x9744,0xE8F8,0x9745,0x9746,0x9747,0x9748, 0x9749,0x974A,0x974B,0x974C,0xE8F5,0x974D,0xCDB0,0xE8F6, 0x974E,0x974F,0x9750,0x9751,0x9752,0x9753,0x9754,0x9755, 0x9756,0xC1BA,0x9757,0xE8E8,0x9758,0xC3B7,0xB0F0,0x9759, 0x975A,0x975B,0x975C,0x975D,0x975E,0x975F,0x9760,0xE8F4, 0x9761,0x9762,0x9763,0xE8F7,0x9764,0x9765,0x9766,0xB9A3, 0x9767,0x9768,0x9769,0x976A,0x976B,0x976C,0x976D,0x976E, 0x976F,0x9770,0xC9D2,0x9771,0x9772,0x9773,0xC3CE,0xCEE0, 0xC0E6,0x9774,0x9775,0x9776,0x9777,0xCBF3,0x9778,0xCCDD, 0xD0B5,0x9779,0x977A,0xCAE1,0x977B,0xE8F3,0x977C,0x977D, 0x977E,0x9780,0x9781,0x9782,0x9783,0x9784,0x9785,0x9786, 0xBCEC,0x9787,0xE8F9,0x9788,0x9789,0x978A,0x978B,0x978C, 0x978D,0xC3DE,0x978E,0xC6E5,0x978F,0xB9F7,0x9790,0x9791, 0x9792,0x9793,0xB0F4,0x9794,0x9795,0xD7D8,0x9796,0x9797, 0xBCAC,0x9798,0xC5EF,0x9799,0x979A,0x979B,0x979C,0x979D, 0xCCC4,0x979E,0x979F,0xE9A6,0x97A0,0x97A1,0x97A2,0x97A3, 0x97A4,0x97A5,0x97A6,0x97A7,0x97A8,0x97A9,0xC9AD,0x97AA, 0xE9A2,0xC0E2,0x97AB,0x97AC,0x97AD,0xBFC3,0x97AE,0x97AF, 0x97B0,0xE8FE,0xB9D7,0x97B1,0xE8FB,0x97B2,0x97B3,0x97B4, 0x97B5,0xE9A4,0x97B6,0x97B7,0x97B8,0xD2CE,0x97B9,0x97BA, 0x97BB,0x97BC,0x97BD,0xE9A3,0x97BE,0xD6B2,0xD7B5,0x97BF, 0xE9A7,0x97C0,0xBDB7,0x97C1,0x97C2,0x97C3,0x97C4,0x97C5, 0x97C6,0x97C7,0x97C8,0x97C9,0x97CA,0x97CB,0x97CC,0xE8FC, 0xE8FD,0x97CD,0x97CE,0x97CF,0xE9A1,0x97D0,0x97D1,0x97D2, 0x97D3,0x97D4,0x97D5,0x97D6,0x97D7,0xCDD6,0x97D8,0x97D9, 0xD2AC,0x97DA,0x97DB,0x97DC,0xE9B2,0x97DD,0x97DE,0x97DF, 0x97E0,0xE9A9,0x97E1,0x97E2,0x97E3,0xB4AA,0x97E4,0xB4BB, 0x97E5,0x97E6,0xE9AB,0x97E7,0x97E8,0x97E9,0x97EA,0x97EB, 0x97EC,0x97ED,0x97EE,0x97EF,0x97F0,0x97F1,0x97F2,0x97F3, 0x97F4,0x97F5,0x97F6,0x97F7,0xD0A8,0x97F8,0x97F9,0xE9A5, 0x97FA,0x97FB,0xB3FE,0x97FC,0x97FD,0xE9AC,0xC0E3,0x97FE, 0xE9AA,0x9840,0x9841,0xE9B9,0x9842,0x9843,0xE9B8,0x9844, 0x9845,0x9846,0x9847,0xE9AE,0x9848,0x9849,0xE8FA,0x984A, 0x984B,0xE9A8,0x984C,0x984D,0x984E,0x984F,0x9850,0xBFAC, 0xE9B1,0xE9BA,0x9851,0x9852,0xC2A5,0x9853,0x9854,0x9855, 0xE9AF,0x9856,0xB8C5,0x9857,0xE9AD,0x9858,0xD3DC,0xE9B4, 0xE9B5,0xE9B7,0x9859,0x985A,0x985B,0xE9C7,0x985C,0x985D, 0x985E,0x985F,0x9860,0x9861,0xC0C6,0xE9C5,0x9862,0x9863, 0xE9B0,0x9864,0x9865,0xE9BB,0xB0F1,0x9866,0x9867,0x9868, 0x9869,0x986A,0x986B,0x986C,0x986D,0x986E,0x986F,0xE9BC, 0xD5A5,0x9870,0x9871,0xE9BE,0x9872,0xE9BF,0x9873,0x9874, 0x9875,0xE9C1,0x9876,0x9877,0xC1F1,0x9878,0x9879,0xC8B6, 0x987A,0x987B,0x987C,0xE9BD,0x987D,0x987E,0x9880,0x9881, 0x9882,0xE9C2,0x9883,0x9884,0x9885,0x9886,0x9887,0x9888, 0x9889,0x988A,0xE9C3,0x988B,0xE9B3,0x988C,0xE9B6,0x988D, 0xBBB1,0x988E,0x988F,0x9890,0xE9C0,0x9891,0x9892,0x9893, 0x9894,0x9895,0x9896,0xBCF7,0x9897,0x9898,0x9899,0xE9C4, 0xE9C6,0x989A,0x989B,0x989C,0x989D,0x989E,0x989F,0x98A0, 0x98A1,0x98A2,0x98A3,0x98A4,0x98A5,0xE9CA,0x98A6,0x98A7, 0x98A8,0x98A9,0xE9CE,0x98AA,0x98AB,0x98AC,0x98AD,0x98AE, 0x98AF,0x98B0,0x98B1,0x98B2,0x98B3,0xB2DB,0x98B4,0xE9C8, 0x98B5,0x98B6,0x98B7,0x98B8,0x98B9,0x98BA,0x98BB,0x98BC, 0x98BD,0x98BE,0xB7AE,0x98BF,0x98C0,0x98C1,0x98C2,0x98C3, 0x98C4,0x98C5,0x98C6,0x98C7,0x98C8,0x98C9,0x98CA,0xE9CB, 0xE9CC,0x98CB,0x98CC,0x98CD,0x98CE,0x98CF,0x98D0,0xD5C1, 0x98D1,0xC4A3,0x98D2,0x98D3,0x98D4,0x98D5,0x98D6,0x98D7, 0xE9D8,0x98D8,0xBAE1,0x98D9,0x98DA,0x98DB,0x98DC,0xE9C9, 0x98DD,0xD3A3,0x98DE,0x98DF,0x98E0,0xE9D4,0x98E1,0x98E2, 0x98E3,0x98E4,0x98E5,0x98E6,0x98E7,0xE9D7,0xE9D0,0x98E8, 0x98E9,0x98EA,0x98EB,0x98EC,0xE9CF,0x98ED,0x98EE,0xC7C1, 0x98EF,0x98F0,0x98F1,0x98F2,0x98F3,0x98F4,0x98F5,0x98F6, 0xE9D2,0x98F7,0x98F8,0x98F9,0x98FA,0x98FB,0x98FC,0x98FD, 0xE9D9,0xB3C8,0x98FE,0xE9D3,0x9940,0x9941,0x9942,0x9943, 0x9944,0xCFF0,0x9945,0x9946,0x9947,0xE9CD,0x9948,0x9949, 0x994A,0x994B,0x994C,0x994D,0x994E,0x994F,0x9950,0x9951, 0x9952,0xB3F7,0x9953,0x9954,0x9955,0x9956,0x9957,0x9958, 0x9959,0xE9D6,0x995A,0x995B,0xE9DA,0x995C,0x995D,0x995E, 0xCCB4,0x995F,0x9960,0x9961,0xCFAD,0x9962,0x9963,0x9964, 0x9965,0x9966,0x9967,0x9968,0x9969,0x996A,0xE9D5,0x996B, 0xE9DC,0xE9DB,0x996C,0x996D,0x996E,0x996F,0x9970,0xE9DE, 0x9971,0x9972,0x9973,0x9974,0x9975,0x9976,0x9977,0x9978, 0xE9D1,0x9979,0x997A,0x997B,0x997C,0x997D,0x997E,0x9980, 0x9981,0xE9DD,0x9982,0xE9DF,0xC3CA,0x9983,0x9984,0x9985, 0x9986,0x9987,0x9988,0x9989,0x998A,0x998B,0x998C,0x998D, 0x998E,0x998F,0x9990,0x9991,0x9992,0x9993,0x9994,0x9995, 0x9996,0x9997,0x9998,0x9999,0x999A,0x999B,0x999C,0x999D, 0x999E,0x999F,0x99A0,0x99A1,0x99A2,0x99A3,0x99A4,0x99A5, 0x99A6,0x99A7,0x99A8,0x99A9,0x99AA,0x99AB,0x99AC,0x99AD, 0x99AE,0x99AF,0x99B0,0x99B1,0x99B2,0x99B3,0x99B4,0x99B5, 0x99B6,0x99B7,0x99B8,0x99B9,0x99BA,0x99BB,0x99BC,0x99BD, 0x99BE,0x99BF,0x99C0,0x99C1,0x99C2,0x99C3,0x99C4,0x99C5, 0x99C6,0x99C7,0x99C8,0x99C9,0x99CA,0x99CB,0x99CC,0x99CD, 0x99CE,0x99CF,0x99D0,0x99D1,0x99D2,0x99D3,0x99D4,0x99D5, 0x99D6,0x99D7,0x99D8,0x99D9,0x99DA,0x99DB,0x99DC,0x99DD, 0x99DE,0x99DF,0x99E0,0x99E1,0x99E2,0x99E3,0x99E4,0x99E5, 0x99E6,0x99E7,0x99E8,0x99E9,0x99EA,0x99EB,0x99EC,0x99ED, 0x99EE,0x99EF,0x99F0,0x99F1,0x99F2,0x99F3,0x99F4,0x99F5, 0xC7B7,0xB4CE,0xBBB6,0xD0C0,0xECA3,0x99F6,0x99F7,0xC5B7, 0x99F8,0x99F9,0x99FA,0x99FB,0x99FC,0x99FD,0x99FE,0x9A40, 0x9A41,0x9A42,0xD3FB,0x9A43,0x9A44,0x9A45,0x9A46,0xECA4, 0x9A47,0xECA5,0xC6DB,0x9A48,0x9A49,0x9A4A,0xBFEE,0x9A4B, 0x9A4C,0x9A4D,0x9A4E,0xECA6,0x9A4F,0x9A50,0xECA7,0xD0AA, 0x9A51,0xC7B8,0x9A52,0x9A53,0xB8E8,0x9A54,0x9A55,0x9A56, 0x9A57,0x9A58,0x9A59,0x9A5A,0x9A5B,0x9A5C,0x9A5D,0x9A5E, 0x9A5F,0xECA8,0x9A60,0x9A61,0x9A62,0x9A63,0x9A64,0x9A65, 0x9A66,0x9A67,0xD6B9,0xD5FD,0xB4CB,0xB2BD,0xCEE4,0xC6E7, 0x9A68,0x9A69,0xCDE1,0x9A6A,0x9A6B,0x9A6C,0x9A6D,0x9A6E, 0x9A6F,0x9A70,0x9A71,0x9A72,0x9A73,0x9A74,0x9A75,0x9A76, 0x9A77,0xB4F5,0x9A78,0xCBC0,0xBCDF,0x9A79,0x9A7A,0x9A7B, 0x9A7C,0xE9E2,0xE9E3,0xD1EA,0xE9E5,0x9A7D,0xB4F9,0xE9E4, 0x9A7E,0xD1B3,0xCAE2,0xB2D0,0x9A80,0xE9E8,0x9A81,0x9A82, 0x9A83,0x9A84,0xE9E6,0xE9E7,0x9A85,0x9A86,0xD6B3,0x9A87, 0x9A88,0x9A89,0xE9E9,0xE9EA,0x9A8A,0x9A8B,0x9A8C,0x9A8D, 0x9A8E,0xE9EB,0x9A8F,0x9A90,0x9A91,0x9A92,0x9A93,0x9A94, 0x9A95,0x9A96,0xE9EC,0x9A97,0x9A98,0x9A99,0x9A9A,0x9A9B, 0x9A9C,0x9A9D,0x9A9E,0xECAF,0xC5B9,0xB6CE,0x9A9F,0xD2F3, 0x9AA0,0x9AA1,0x9AA2,0x9AA3,0x9AA4,0x9AA5,0x9AA6,0xB5EE, 0x9AA7,0xBBD9,0xECB1,0x9AA8,0x9AA9,0xD2E3,0x9AAA,0x9AAB, 0x9AAC,0x9AAD,0x9AAE,0xCEE3,0x9AAF,0xC4B8,0x9AB0,0xC3BF, 0x9AB1,0x9AB2,0xB6BE,0xD8B9,0xB1C8,0xB1CF,0xB1D1,0xC5FE, 0x9AB3,0xB1D0,0x9AB4,0xC3AB,0x9AB5,0x9AB6,0x9AB7,0x9AB8, 0x9AB9,0xD5B1,0x9ABA,0x9ABB,0x9ABC,0x9ABD,0x9ABE,0x9ABF, 0x9AC0,0x9AC1,0xEBA4,0xBAC1,0x9AC2,0x9AC3,0x9AC4,0xCCBA, 0x9AC5,0x9AC6,0x9AC7,0xEBA5,0x9AC8,0xEBA7,0x9AC9,0x9ACA, 0x9ACB,0xEBA8,0x9ACC,0x9ACD,0x9ACE,0xEBA6,0x9ACF,0x9AD0, 0x9AD1,0x9AD2,0x9AD3,0x9AD4,0x9AD5,0xEBA9,0xEBAB,0xEBAA, 0x9AD6,0x9AD7,0x9AD8,0x9AD9,0x9ADA,0xEBAC,0x9ADB,0xCACF, 0xD8B5,0xC3F1,0x9ADC,0xC3A5,0xC6F8,0xEBAD,0xC4CA,0x9ADD, 0xEBAE,0xEBAF,0xEBB0,0xB7D5,0x9ADE,0x9ADF,0x9AE0,0xB7FA, 0x9AE1,0xEBB1,0xC7E2,0x9AE2,0xEBB3,0x9AE3,0xBAA4,0xD1F5, 0xB0B1,0xEBB2,0xEBB4,0x9AE4,0x9AE5,0x9AE6,0xB5AA,0xC2C8, 0xC7E8,0x9AE7,0xEBB5,0x9AE8,0xCBAE,0xE3DF,0x9AE9,0x9AEA, 0xD3C0,0x9AEB,0x9AEC,0x9AED,0x9AEE,0xD9DB,0x9AEF,0x9AF0, 0xCDA1,0xD6AD,0xC7F3,0x9AF1,0x9AF2,0x9AF3,0xD9E0,0xBBE3, 0x9AF4,0xBABA,0xE3E2,0x9AF5,0x9AF6,0x9AF7,0x9AF8,0x9AF9, 0xCFAB,0x9AFA,0x9AFB,0x9AFC,0xE3E0,0xC9C7,0x9AFD,0xBAB9, 0x9AFE,0x9B40,0x9B41,0xD1B4,0xE3E1,0xC8EA,0xB9AF,0xBDAD, 0xB3D8,0xCEDB,0x9B42,0x9B43,0xCCC0,0x9B44,0x9B45,0x9B46, 0xE3E8,0xE3E9,0xCDF4,0x9B47,0x9B48,0x9B49,0x9B4A,0x9B4B, 0xCCAD,0x9B4C,0xBCB3,0x9B4D,0xE3EA,0x9B4E,0xE3EB,0x9B4F, 0x9B50,0xD0DA,0x9B51,0x9B52,0x9B53,0xC6FB,0xB7DA,0x9B54, 0x9B55,0xC7DF,0xD2CA,0xCED6,0x9B56,0xE3E4,0xE3EC,0x9B57, 0xC9F2,0xB3C1,0x9B58,0x9B59,0xE3E7,0x9B5A,0x9B5B,0xC6E3, 0xE3E5,0x9B5C,0x9B5D,0xEDB3,0xE3E6,0x9B5E,0x9B5F,0x9B60, 0x9B61,0xC9B3,0x9B62,0xC5E6,0x9B63,0x9B64,0x9B65,0xB9B5, 0x9B66,0xC3BB,0x9B67,0xE3E3,0xC5BD,0xC1A4,0xC2D9,0xB2D7, 0x9B68,0xE3ED,0xBBA6,0xC4AD,0x9B69,0xE3F0,0xBEDA,0x9B6A, 0x9B6B,0xE3FB,0xE3F5,0xBAD3,0x9B6C,0x9B6D,0x9B6E,0x9B6F, 0xB7D0,0xD3CD,0x9B70,0xD6CE,0xD5D3,0xB9C1,0xD5B4,0xD1D8, 0x9B71,0x9B72,0x9B73,0x9B74,0xD0B9,0xC7F6,0x9B75,0x9B76, 0x9B77,0xC8AA,0xB2B4,0x9B78,0xC3DA,0x9B79,0x9B7A,0x9B7B, 0xE3EE,0x9B7C,0x9B7D,0xE3FC,0xE3EF,0xB7A8,0xE3F7,0xE3F4, 0x9B7E,0x9B80,0x9B81,0xB7BA,0x9B82,0x9B83,0xC5A2,0x9B84, 0xE3F6,0xC5DD,0xB2A8,0xC6FC,0x9B85,0xC4E0,0x9B86,0x9B87, 0xD7A2,0x9B88,0xC0E1,0xE3F9,0x9B89,0x9B8A,0xE3FA,0xE3FD, 0xCCA9,0xE3F3,0x9B8B,0xD3BE,0x9B8C,0xB1C3,0xEDB4,0xE3F1, 0xE3F2,0x9B8D,0xE3F8,0xD0BA,0xC6C3,0xD4F3,0xE3FE,0x9B8E }; const static uint16 gbkEncoderInnerIndex3[]= { 0x9B8F,0xBDE0,0x9B90,0x9B91,0xE4A7,0x9B92,0x9B93,0xE4A6, 0x9B94,0x9B95,0x9B96,0xD1F3,0xE4A3,0x9B97,0xE4A9,0x9B98, 0x9B99,0x9B9A,0xC8F7,0x9B9B,0x9B9C,0x9B9D,0x9B9E,0xCFB4, 0x9B9F,0xE4A8,0xE4AE,0xC2E5,0x9BA0,0x9BA1,0xB6B4,0x9BA2, 0x9BA3,0x9BA4,0x9BA5,0x9BA6,0x9BA7,0xBDF2,0x9BA8,0xE4A2, 0x9BA9,0x9BAA,0xBAE9,0xE4AA,0x9BAB,0x9BAC,0xE4AC,0x9BAD, 0x9BAE,0xB6FD,0xD6DE,0xE4B2,0x9BAF,0xE4AD,0x9BB0,0x9BB1, 0x9BB2,0xE4A1,0x9BB3,0xBBEE,0xCDDD,0xC7A2,0xC5C9,0x9BB4, 0x9BB5,0xC1F7,0x9BB6,0xE4A4,0x9BB7,0xC7B3,0xBDAC,0xBDBD, 0xE4A5,0x9BB8,0xD7C7,0xB2E2,0x9BB9,0xE4AB,0xBCC3,0xE4AF, 0x9BBA,0xBBEB,0xE4B0,0xC5A8,0xE4B1,0x9BBB,0x9BBC,0x9BBD, 0x9BBE,0xD5E3,0xBFA3,0x9BBF,0xE4BA,0x9BC0,0xE4B7,0x9BC1, 0xE4BB,0x9BC2,0x9BC3,0xE4BD,0x9BC4,0x9BC5,0xC6D6,0x9BC6, 0x9BC7,0xBAC6,0xC0CB,0x9BC8,0x9BC9,0x9BCA,0xB8A1,0xE4B4, 0x9BCB,0x9BCC,0x9BCD,0x9BCE,0xD4A1,0x9BCF,0x9BD0,0xBAA3, 0xBDFE,0x9BD1,0x9BD2,0x9BD3,0xE4BC,0x9BD4,0x9BD5,0x9BD6, 0x9BD7,0x9BD8,0xCDBF,0x9BD9,0x9BDA,0xC4F9,0x9BDB,0x9BDC, 0xCFFB,0xC9E6,0x9BDD,0x9BDE,0xD3BF,0x9BDF,0xCFD1,0x9BE0, 0x9BE1,0xE4B3,0x9BE2,0xE4B8,0xE4B9,0xCCE9,0x9BE3,0x9BE4, 0x9BE5,0x9BE6,0x9BE7,0xCCCE,0x9BE8,0xC0D4,0xE4B5,0xC1B0, 0xE4B6,0xCED0,0x9BE9,0xBBC1,0xB5D3,0x9BEA,0xC8F3,0xBDA7, 0xD5C7,0xC9AC,0xB8A2,0xE4CA,0x9BEB,0x9BEC,0xE4CC,0xD1C4, 0x9BED,0x9BEE,0xD2BA,0x9BEF,0x9BF0,0xBAAD,0x9BF1,0x9BF2, 0xBAD4,0x9BF3,0x9BF4,0x9BF5,0x9BF6,0x9BF7,0x9BF8,0xE4C3, 0xB5ED,0x9BF9,0x9BFA,0x9BFB,0xD7CD,0xE4C0,0xCFFD,0xE4BF, 0x9BFC,0x9BFD,0x9BFE,0xC1DC,0xCCCA,0x9C40,0x9C41,0x9C42, 0x9C43,0xCAE7,0x9C44,0x9C45,0x9C46,0x9C47,0xC4D7,0x9C48, 0xCCD4,0xE4C8,0x9C49,0x9C4A,0x9C4B,0xE4C7,0xE4C1,0x9C4C, 0xE4C4,0xB5AD,0x9C4D,0x9C4E,0xD3D9,0x9C4F,0xE4C6,0x9C50, 0x9C51,0x9C52,0x9C53,0xD2F9,0xB4E3,0x9C54,0xBBB4,0x9C55, 0x9C56,0xC9EE,0x9C57,0xB4BE,0x9C58,0x9C59,0x9C5A,0xBBEC, 0x9C5B,0xD1CD,0x9C5C,0xCCED,0xEDB5,0x9C5D,0x9C5E,0x9C5F, 0x9C60,0x9C61,0x9C62,0x9C63,0x9C64,0xC7E5,0x9C65,0x9C66, 0x9C67,0x9C68,0xD4A8,0x9C69,0xE4CB,0xD7D5,0xE4C2,0x9C6A, 0xBDA5,0xE4C5,0x9C6B,0x9C6C,0xD3E6,0x9C6D,0xE4C9,0xC9F8, 0x9C6E,0x9C6F,0xE4BE,0x9C70,0x9C71,0xD3E5,0x9C72,0x9C73, 0xC7FE,0xB6C9,0x9C74,0xD4FC,0xB2B3,0xE4D7,0x9C75,0x9C76, 0x9C77,0xCEC2,0x9C78,0xE4CD,0x9C79,0xCEBC,0x9C7A,0xB8DB, 0x9C7B,0x9C7C,0xE4D6,0x9C7D,0xBFCA,0x9C7E,0x9C80,0x9C81, 0xD3CE,0x9C82,0xC3EC,0x9C83,0x9C84,0x9C85,0x9C86,0x9C87, 0x9C88,0x9C89,0x9C8A,0xC5C8,0xE4D8,0x9C8B,0x9C8C,0x9C8D, 0x9C8E,0x9C8F,0x9C90,0x9C91,0x9C92,0xCDC4,0xE4CF,0x9C93, 0x9C94,0x9C95,0x9C96,0xE4D4,0xE4D5,0x9C97,0xBAFE,0x9C98, 0xCFE6,0x9C99,0x9C9A,0xD5BF,0x9C9B,0x9C9C,0x9C9D,0xE4D2, 0x9C9E,0x9C9F,0x9CA0,0x9CA1,0x9CA2,0x9CA3,0x9CA4,0x9CA5, 0x9CA6,0x9CA7,0x9CA8,0xE4D0,0x9CA9,0x9CAA,0xE4CE,0x9CAB, 0x9CAC,0x9CAD,0x9CAE,0x9CAF,0x9CB0,0x9CB1,0x9CB2,0x9CB3, 0x9CB4,0x9CB5,0x9CB6,0x9CB7,0x9CB8,0x9CB9,0xCDE5,0xCAAA, 0x9CBA,0x9CBB,0x9CBC,0xC0A3,0x9CBD,0xBDA6,0xE4D3,0x9CBE, 0x9CBF,0xB8C8,0x9CC0,0x9CC1,0x9CC2,0x9CC3,0x9CC4,0xE4E7, 0xD4B4,0x9CC5,0x9CC6,0x9CC7,0x9CC8,0x9CC9,0x9CCA,0x9CCB, 0xE4DB,0x9CCC,0x9CCD,0x9CCE,0xC1EF,0x9CCF,0x9CD0,0xE4E9, 0x9CD1,0x9CD2,0xD2E7,0x9CD3,0x9CD4,0xE4DF,0x9CD5,0xE4E0, 0x9CD6,0x9CD7,0xCFAA,0x9CD8,0x9CD9,0x9CDA,0x9CDB,0xCBDD, 0x9CDC,0xE4DA,0xE4D1,0x9CDD,0xE4E5,0x9CDE,0xC8DC,0xE4E3, 0x9CDF,0x9CE0,0xC4E7,0xE4E2,0x9CE1,0xE4E1,0x9CE2,0x9CE3, 0x9CE4,0xB3FC,0xE4E8,0x9CE5,0x9CE6,0x9CE7,0x9CE8,0xB5E1, 0x9CE9,0x9CEA,0x9CEB,0xD7CC,0x9CEC,0x9CED,0x9CEE,0xE4E6, 0x9CEF,0xBBAC,0x9CF0,0xD7D2,0xCCCF,0xEBF8,0x9CF1,0xE4E4, 0x9CF2,0x9CF3,0xB9F6,0x9CF4,0x9CF5,0x9CF6,0xD6CD,0xE4D9, 0xE4DC,0xC2FA,0xE4DE,0x9CF7,0xC2CB,0xC0C4,0xC2D0,0x9CF8, 0xB1F5,0xCCB2,0x9CF9,0x9CFA,0x9CFB,0x9CFC,0x9CFD,0x9CFE, 0x9D40,0x9D41,0x9D42,0x9D43,0xB5CE,0x9D44,0x9D45,0x9D46, 0x9D47,0xE4EF,0x9D48,0x9D49,0x9D4A,0x9D4B,0x9D4C,0x9D4D, 0x9D4E,0x9D4F,0xC6AF,0x9D50,0x9D51,0x9D52,0xC6E1,0x9D53, 0x9D54,0xE4F5,0x9D55,0x9D56,0x9D57,0x9D58,0x9D59,0xC2A9, 0x9D5A,0x9D5B,0x9D5C,0xC0EC,0xD1DD,0xE4EE,0x9D5D,0x9D5E, 0x9D5F,0x9D60,0x9D61,0x9D62,0x9D63,0x9D64,0x9D65,0x9D66, 0xC4AE,0x9D67,0x9D68,0x9D69,0xE4ED,0x9D6A,0x9D6B,0x9D6C, 0x9D6D,0xE4F6,0xE4F4,0xC2FE,0x9D6E,0xE4DD,0x9D6F,0xE4F0, 0x9D70,0xCAFE,0x9D71,0xD5C4,0x9D72,0x9D73,0xE4F1,0x9D74, 0x9D75,0x9D76,0x9D77,0x9D78,0x9D79,0x9D7A,0xD1FA,0x9D7B, 0x9D7C,0x9D7D,0x9D7E,0x9D80,0x9D81,0x9D82,0xE4EB,0xE4EC, 0x9D83,0x9D84,0x9D85,0xE4F2,0x9D86,0xCEAB,0x9D87,0x9D88, 0x9D89,0x9D8A,0x9D8B,0x9D8C,0x9D8D,0x9D8E,0x9D8F,0x9D90, 0xC5CB,0x9D91,0x9D92,0x9D93,0xC7B1,0x9D94,0xC2BA,0x9D95, 0x9D96,0x9D97,0xE4EA,0x9D98,0x9D99,0x9D9A,0xC1CA,0x9D9B, 0x9D9C,0x9D9D,0x9D9E,0x9D9F,0x9DA0,0xCCB6,0xB3B1,0x9DA1, 0x9DA2,0x9DA3,0xE4FB,0x9DA4,0xE4F3,0x9DA5,0x9DA6,0x9DA7, 0xE4FA,0x9DA8,0xE4FD,0x9DA9,0xE4FC,0x9DAA,0x9DAB,0x9DAC, 0x9DAD,0x9DAE,0x9DAF,0x9DB0,0xB3CE,0x9DB1,0x9DB2,0x9DB3, 0xB3BA,0xE4F7,0x9DB4,0x9DB5,0xE4F9,0xE4F8,0xC5EC,0x9DB6, 0x9DB7,0x9DB8,0x9DB9,0x9DBA,0x9DBB,0x9DBC,0x9DBD,0x9DBE, 0x9DBF,0x9DC0,0x9DC1,0x9DC2,0xC0BD,0x9DC3,0x9DC4,0x9DC5, 0x9DC6,0xD4E8,0x9DC7,0x9DC8,0x9DC9,0x9DCA,0x9DCB,0xE5A2, 0x9DCC,0x9DCD,0x9DCE,0x9DCF,0x9DD0,0x9DD1,0x9DD2,0x9DD3, 0x9DD4,0x9DD5,0x9DD6,0xB0C4,0x9DD7,0x9DD8,0xE5A4,0x9DD9, 0x9DDA,0xE5A3,0x9DDB,0x9DDC,0x9DDD,0x9DDE,0x9DDF,0x9DE0, 0xBCA4,0x9DE1,0xE5A5,0x9DE2,0x9DE3,0x9DE4,0x9DE5,0x9DE6, 0x9DE7,0xE5A1,0x9DE8,0x9DE9,0x9DEA,0x9DEB,0x9DEC,0x9DED, 0x9DEE,0xE4FE,0xB1F4,0x9DEF,0x9DF0,0x9DF1,0x9DF2,0x9DF3, 0x9DF4,0x9DF5,0x9DF6,0x9DF7,0x9DF8,0x9DF9,0xE5A8,0x9DFA, 0xE5A9,0xE5A6,0x9DFB,0x9DFC,0x9DFD,0x9DFE,0x9E40,0x9E41, 0x9E42,0x9E43,0x9E44,0x9E45,0x9E46,0x9E47,0xE5A7,0xE5AA, 0x9E48,0x9E49,0x9E4A,0x9E4B,0x9E4C,0x9E4D,0x9E4E,0x9E4F, 0x9E50,0x9E51,0x9E52,0x9E53,0x9E54,0x9E55,0x9E56,0x9E57, 0x9E58,0x9E59,0x9E5A,0x9E5B,0x9E5C,0x9E5D,0x9E5E,0x9E5F, 0x9E60,0x9E61,0x9E62,0x9E63,0x9E64,0x9E65,0x9E66,0x9E67, 0x9E68,0xC6D9,0x9E69,0x9E6A,0x9E6B,0x9E6C,0x9E6D,0x9E6E, 0x9E6F,0x9E70,0xE5AB,0xE5AD,0x9E71,0x9E72,0x9E73,0x9E74, 0x9E75,0x9E76,0x9E77,0xE5AC,0x9E78,0x9E79,0x9E7A,0x9E7B, 0x9E7C,0x9E7D,0x9E7E,0x9E80,0x9E81,0x9E82,0x9E83,0x9E84, 0x9E85,0x9E86,0x9E87,0x9E88,0x9E89,0xE5AF,0x9E8A,0x9E8B, 0x9E8C,0xE5AE,0x9E8D,0x9E8E,0x9E8F,0x9E90,0x9E91,0x9E92, 0x9E93,0x9E94,0x9E95,0x9E96,0x9E97,0x9E98,0x9E99,0x9E9A, 0x9E9B,0x9E9C,0x9E9D,0x9E9E,0xB9E0,0x9E9F,0x9EA0,0xE5B0, 0x9EA1,0x9EA2,0x9EA3,0x9EA4,0x9EA5,0x9EA6,0x9EA7,0x9EA8, 0x9EA9,0x9EAA,0x9EAB,0x9EAC,0x9EAD,0x9EAE,0xE5B1,0x9EAF, 0x9EB0,0x9EB1,0x9EB2,0x9EB3,0x9EB4,0x9EB5,0x9EB6,0x9EB7, 0x9EB8,0x9EB9,0x9EBA,0xBBF0,0xECE1,0xC3F0,0x9EBB,0xB5C6, 0xBBD2,0x9EBC,0x9EBD,0x9EBE,0x9EBF,0xC1E9,0xD4EE,0x9EC0, 0xBEC4,0x9EC1,0x9EC2,0x9EC3,0xD7C6,0x9EC4,0xD4D6,0xB2D3, 0xECBE,0x9EC5,0x9EC6,0x9EC7,0x9EC8,0xEAC1,0x9EC9,0x9ECA, 0x9ECB,0xC2AF,0xB4B6,0x9ECC,0x9ECD,0x9ECE,0xD1D7,0x9ECF, 0x9ED0,0x9ED1,0xB3B4,0x9ED2,0xC8B2,0xBFBB,0xECC0,0x9ED3, 0x9ED4,0xD6CB,0x9ED5,0x9ED6,0xECBF,0xECC1,0x9ED7,0x9ED8, 0x9ED9,0x9EDA,0x9EDB,0x9EDC,0x9EDD,0x9EDE,0x9EDF,0x9EE0, 0x9EE1,0x9EE2,0x9EE3,0xECC5,0xBEE6,0xCCBF,0xC5DA,0xBEBC, 0x9EE4,0xECC6,0x9EE5,0xB1FE,0x9EE6,0x9EE7,0x9EE8,0xECC4, 0xD5A8,0xB5E3,0x9EE9,0xECC2,0xC1B6,0xB3E3,0x9EEA,0x9EEB, 0xECC3,0xCBB8,0xC0C3,0xCCFE,0x9EEC,0x9EED,0x9EEE,0x9EEF, 0xC1D2,0x9EF0,0xECC8,0x9EF1,0x9EF2,0x9EF3,0x9EF4,0x9EF5, 0x9EF6,0x9EF7,0x9EF8,0x9EF9,0x9EFA,0x9EFB,0x9EFC,0x9EFD, 0xBAE6,0xC0D3,0x9EFE,0xD6F2,0x9F40,0x9F41,0x9F42,0xD1CC, 0x9F43,0x9F44,0x9F45,0x9F46,0xBFBE,0x9F47,0xB7B3,0xC9D5, 0xECC7,0xBBE2,0x9F48,0xCCCC,0xBDFD,0xC8C8,0x9F49,0xCFA9, 0x9F4A,0x9F4B,0x9F4C,0x9F4D,0x9F4E,0x9F4F,0x9F50,0xCDE9, 0x9F51,0xC5EB,0x9F52,0x9F53,0x9F54,0xB7E9,0x9F55,0x9F56, 0x9F57,0x9F58,0x9F59,0x9F5A,0x9F5B,0x9F5C,0x9F5D,0x9F5E, 0x9F5F,0xD1C9,0xBAB8,0x9F60,0x9F61,0x9F62,0x9F63,0x9F64, 0xECC9,0x9F65,0x9F66,0xECCA,0x9F67,0xBBC0,0xECCB,0x9F68, 0xECE2,0xB1BA,0xB7D9,0x9F69,0x9F6A,0x9F6B,0x9F6C,0x9F6D, 0x9F6E,0x9F6F,0x9F70,0x9F71,0x9F72,0x9F73,0xBDB9,0x9F74, 0x9F75,0x9F76,0x9F77,0x9F78,0x9F79,0x9F7A,0x9F7B,0xECCC, 0xD1E6,0xECCD,0x9F7C,0x9F7D,0x9F7E,0x9F80,0xC8BB,0x9F81, 0x9F82,0x9F83,0x9F84,0x9F85,0x9F86,0x9F87,0x9F88,0x9F89, 0x9F8A,0x9F8B,0x9F8C,0x9F8D,0x9F8E,0xECD1,0x9F8F,0x9F90, 0x9F91,0x9F92,0xECD3,0x9F93,0xBBCD,0x9F94,0xBCE5,0x9F95, 0x9F96,0x9F97,0x9F98,0x9F99,0x9F9A,0x9F9B,0x9F9C,0x9F9D, 0x9F9E,0x9F9F,0x9FA0,0x9FA1,0xECCF,0x9FA2,0xC9B7,0x9FA3, 0x9FA4,0x9FA5,0x9FA6,0x9FA7,0xC3BA,0x9FA8,0xECE3,0xD5D5, 0xECD0,0x9FA9,0x9FAA,0x9FAB,0x9FAC,0x9FAD,0xD6F3,0x9FAE, 0x9FAF,0x9FB0,0xECD2,0xECCE,0x9FB1,0x9FB2,0x9FB3,0x9FB4, 0xECD4,0x9FB5,0xECD5,0x9FB6,0x9FB7,0xC9BF,0x9FB8,0x9FB9, 0x9FBA,0x9FBB,0x9FBC,0x9FBD,0xCFA8,0x9FBE,0x9FBF,0x9FC0, 0x9FC1,0x9FC2,0xD0DC,0x9FC3,0x9FC4,0x9FC5,0x9FC6,0xD1AC, 0x9FC7,0x9FC8,0x9FC9,0x9FCA,0xC8DB,0x9FCB,0x9FCC,0x9FCD, 0xECD6,0xCEF5,0x9FCE,0x9FCF,0x9FD0,0x9FD1,0x9FD2,0xCAEC, 0xECDA,0x9FD3,0x9FD4,0x9FD5,0x9FD6,0x9FD7,0x9FD8,0x9FD9, 0xECD9,0x9FDA,0x9FDB,0x9FDC,0xB0BE,0x9FDD,0x9FDE,0x9FDF, 0x9FE0,0x9FE1,0x9FE2,0xECD7,0x9FE3,0xECD8,0x9FE4,0x9FE5, 0x9FE6,0xECE4,0x9FE7,0x9FE8,0x9FE9,0x9FEA,0x9FEB,0x9FEC, 0x9FED,0x9FEE,0x9FEF,0xC8BC,0x9FF0,0x9FF1,0x9FF2,0x9FF3, 0x9FF4,0x9FF5,0x9FF6,0x9FF7,0x9FF8,0x9FF9,0xC1C7,0x9FFA, 0x9FFB,0x9FFC,0x9FFD,0x9FFE,0xECDC,0xD1E0,0xA040,0xA041, 0xA042,0xA043,0xA044,0xA045,0xA046,0xA047,0xA048,0xA049, 0xECDB,0xA04A,0xA04B,0xA04C,0xA04D,0xD4EF,0xA04E,0xECDD, 0xA04F,0xA050,0xA051,0xA052,0xA053,0xA054,0xDBC6,0xA055, 0xA056,0xA057,0xA058,0xA059,0xA05A,0xA05B,0xA05C,0xA05D, 0xA05E,0xECDE,0xA05F,0xA060,0xA061,0xA062,0xA063,0xA064, 0xA065,0xA066,0xA067,0xA068,0xA069,0xA06A,0xB1AC,0xA06B, 0xA06C,0xA06D,0xA06E,0xA06F,0xA070,0xA071,0xA072,0xA073, 0xA074,0xA075,0xA076,0xA077,0xA078,0xA079,0xA07A,0xA07B, 0xA07C,0xA07D,0xA07E,0xA080,0xA081,0xECDF,0xA082,0xA083, 0xA084,0xA085,0xA086,0xA087,0xA088,0xA089,0xA08A,0xA08B, 0xECE0,0xA08C,0xD7A6,0xA08D,0xC5C0,0xA08E,0xA08F,0xA090, 0xEBBC,0xB0AE,0xA091,0xA092,0xA093,0xBEF4,0xB8B8,0xD2AF, 0xB0D6,0xB5F9,0xA094,0xD8B3,0xA095,0xCBAC,0xA096,0xE3DD, 0xA097,0xA098,0xA099,0xA09A,0xA09B,0xA09C,0xA09D,0xC6AC, 0xB0E6,0xA09E,0xA09F,0xA0A0,0xC5C6,0xEBB9,0xA0A1,0xA0A2, 0xA0A3,0xA0A4,0xEBBA,0xA0A5,0xA0A6,0xA0A7,0xEBBB,0xA0A8, 0xA0A9,0xD1C0,0xA0AA,0xC5A3,0xA0AB,0xEAF2,0xA0AC,0xC4B2, 0xA0AD,0xC4B5,0xC0CE,0xA0AE,0xA0AF,0xA0B0,0xEAF3,0xC4C1, 0xA0B1,0xCEEF,0xA0B2,0xA0B3,0xA0B4,0xA0B5,0xEAF0,0xEAF4, 0xA0B6,0xA0B7,0xC9FC,0xA0B8,0xA0B9,0xC7A3,0xA0BA,0xA0BB, 0xA0BC,0xCCD8,0xCEFE,0xA0BD,0xA0BE,0xA0BF,0xEAF5,0xEAF6, 0xCFAC,0xC0E7,0xA0C0,0xA0C1,0xEAF7,0xA0C2,0xA0C3,0xA0C4, 0xA0C5,0xA0C6,0xB6BF,0xEAF8,0xA0C7,0xEAF9,0xA0C8,0xEAFA, 0xA0C9,0xA0CA,0xEAFB,0xA0CB,0xA0CC,0xA0CD,0xA0CE,0xA0CF, 0xA0D0,0xA0D1,0xA0D2,0xA0D3,0xA0D4,0xA0D5,0xA0D6,0xEAF1, 0xA0D7,0xA0D8,0xA0D9,0xA0DA,0xA0DB,0xA0DC,0xA0DD,0xA0DE, 0xA0DF,0xA0E0,0xA0E1,0xA0E2,0xC8AE,0xE1EB,0xA0E3,0xB7B8, 0xE1EC,0xA0E4,0xA0E5,0xA0E6,0xE1ED,0xA0E7,0xD7B4,0xE1EE, 0xE1EF,0xD3CC,0xA0E8,0xA0E9,0xA0EA,0xA0EB,0xA0EC,0xA0ED, 0xA0EE,0xE1F1,0xBFF1,0xE1F0,0xB5D2,0xA0EF,0xA0F0,0xA0F1, 0xB1B7,0xA0F2,0xA0F3,0xA0F4,0xA0F5,0xE1F3,0xE1F2,0xA0F6, 0xBAFC,0xA0F7,0xE1F4,0xA0F8,0xA0F9,0xA0FA,0xA0FB,0xB9B7, 0xA0FC,0xBED1,0xA0FD,0xA0FE,0xAA40,0xAA41,0xC4FC,0xAA42, 0xBADD,0xBDC6,0xAA43,0xAA44,0xAA45,0xAA46,0xAA47,0xAA48, 0xE1F5,0xE1F7,0xAA49,0xAA4A,0xB6C0,0xCFC1,0xCAA8,0xE1F6, 0xD5F8,0xD3FC,0xE1F8,0xE1FC,0xE1F9,0xAA4B,0xAA4C,0xE1FA, 0xC0EA,0xAA4D,0xE1FE,0xE2A1,0xC0C7,0xAA4E,0xAA4F,0xAA50, 0xAA51,0xE1FB,0xAA52,0xE1FD,0xAA53,0xAA54,0xAA55,0xAA56, 0xAA57,0xAA58,0xE2A5,0xAA59,0xAA5A,0xAA5B,0xC1D4,0xAA5C, 0xAA5D,0xAA5E,0xAA5F,0xE2A3,0xAA60,0xE2A8,0xB2FE,0xE2A2, 0xAA61,0xAA62,0xAA63,0xC3CD,0xB2C2,0xE2A7,0xE2A6,0xAA64, 0xAA65,0xE2A4,0xE2A9,0xAA66,0xAA67,0xE2AB,0xAA68,0xAA69, 0xAA6A,0xD0C9,0xD6ED,0xC3A8,0xE2AC,0xAA6B,0xCFD7,0xAA6C, 0xAA6D,0xE2AE,0xAA6E,0xAA6F,0xBAEF,0xAA70,0xAA71,0xE9E0, 0xE2AD,0xE2AA,0xAA72,0xAA73,0xAA74,0xAA75,0xBBAB,0xD4B3, 0xAA76,0xAA77,0xAA78,0xAA79,0xAA7A,0xAA7B,0xAA7C,0xAA7D, 0xAA7E,0xAA80,0xAA81,0xAA82,0xAA83,0xE2B0,0xAA84,0xAA85, 0xE2AF,0xAA86,0xE9E1,0xAA87,0xAA88,0xAA89,0xAA8A,0xE2B1, 0xAA8B,0xAA8C,0xAA8D,0xAA8E,0xAA8F,0xAA90,0xAA91,0xAA92, 0xE2B2,0xAA93,0xAA94,0xAA95,0xAA96,0xAA97,0xAA98,0xAA99, 0xAA9A,0xAA9B,0xAA9C,0xAA9D,0xE2B3,0xCCA1,0xAA9E,0xE2B4, 0xAA9F,0xAAA0,0xAB40,0xAB41,0xAB42,0xAB43,0xAB44,0xAB45, 0xAB46,0xAB47,0xAB48,0xAB49,0xAB4A,0xAB4B,0xE2B5,0xAB4C, 0xAB4D,0xAB4E,0xAB4F,0xAB50,0xD0FE,0xAB51,0xAB52,0xC2CA, 0xAB53,0xD3F1,0xAB54,0xCDF5,0xAB55,0xAB56,0xE7E0,0xAB57, 0xAB58,0xE7E1,0xAB59,0xAB5A,0xAB5B,0xAB5C,0xBEC1,0xAB5D, 0xAB5E,0xAB5F,0xAB60,0xC2EA,0xAB61,0xAB62,0xAB63,0xE7E4, 0xAB64,0xAB65,0xE7E3,0xAB66,0xAB67,0xAB68,0xAB69,0xAB6A, 0xAB6B,0xCDE6,0xAB6C,0xC3B5,0xAB6D,0xAB6E,0xE7E2,0xBBB7, 0xCFD6,0xAB6F,0xC1E1,0xE7E9,0xAB70,0xAB71,0xAB72,0xE7E8, 0xAB73,0xAB74,0xE7F4,0xB2A3,0xAB75,0xAB76,0xAB77,0xAB78, 0xE7EA,0xAB79,0xE7E6,0xAB7A,0xAB7B,0xAB7C,0xAB7D,0xAB7E, 0xE7EC,0xE7EB,0xC9BA,0xAB80,0xAB81,0xD5E4,0xAB82,0xE7E5, 0xB7A9,0xE7E7,0xAB83,0xAB84,0xAB85,0xAB86,0xAB87,0xAB88, 0xAB89,0xE7EE,0xAB8A,0xAB8B,0xAB8C,0xAB8D,0xE7F3,0xAB8E, 0xD6E9,0xAB8F,0xAB90,0xAB91,0xAB92,0xE7ED,0xAB93,0xE7F2, 0xAB94,0xE7F1,0xAB95,0xAB96,0xAB97,0xB0E0,0xAB98,0xAB99, 0xAB9A,0xAB9B,0xE7F5,0xAB9C,0xAB9D,0xAB9E,0xAB9F,0xABA0, 0xAC40,0xAC41,0xAC42,0xAC43,0xAC44,0xAC45,0xAC46,0xAC47, 0xAC48,0xAC49,0xAC4A,0xC7F2,0xAC4B,0xC0C5,0xC0ED,0xAC4C, 0xAC4D,0xC1F0,0xE7F0,0xAC4E,0xAC4F,0xAC50,0xAC51,0xE7F6, 0xCBF6,0xAC52,0xAC53,0xAC54,0xAC55,0xAC56,0xAC57,0xAC58, 0xAC59,0xAC5A,0xE8A2,0xE8A1,0xAC5B,0xAC5C,0xAC5D,0xAC5E, 0xAC5F,0xAC60,0xD7C1,0xAC61,0xAC62,0xE7FA,0xE7F9,0xAC63, 0xE7FB,0xAC64,0xE7F7,0xAC65,0xE7FE,0xAC66,0xE7FD,0xAC67, 0xE7FC,0xAC68,0xAC69,0xC1D5,0xC7D9,0xC5FD,0xC5C3,0xAC6A, 0xAC6B,0xAC6C,0xAC6D,0xAC6E,0xC7ED,0xAC6F,0xAC70,0xAC71, 0xAC72,0xE8A3,0xAC73,0xAC74,0xAC75,0xAC76,0xAC77,0xAC78, 0xAC79,0xAC7A,0xAC7B,0xAC7C,0xAC7D,0xAC7E,0xAC80,0xAC81, 0xAC82,0xAC83,0xAC84,0xAC85,0xAC86,0xE8A6,0xAC87,0xE8A5, 0xAC88,0xE8A7,0xBAF7,0xE7F8,0xE8A4,0xAC89,0xC8F0,0xC9AA, 0xAC8A,0xAC8B,0xAC8C,0xAC8D,0xAC8E,0xAC8F,0xAC90,0xAC91, 0xAC92,0xAC93,0xAC94,0xAC95,0xAC96,0xE8A9,0xAC97,0xAC98, 0xB9E5,0xAC99,0xAC9A,0xAC9B,0xAC9C,0xAC9D,0xD1FE,0xE8A8, 0xAC9E,0xAC9F,0xACA0,0xAD40,0xAD41,0xAD42,0xE8AA,0xAD43, 0xE8AD,0xE8AE,0xAD44,0xC1A7,0xAD45,0xAD46,0xAD47,0xE8AF, 0xAD48,0xAD49,0xAD4A,0xE8B0,0xAD4B,0xAD4C,0xE8AC,0xAD4D, 0xE8B4,0xAD4E,0xAD4F,0xAD50,0xAD51,0xAD52,0xAD53,0xAD54, 0xAD55,0xAD56,0xAD57,0xAD58,0xE8AB,0xAD59,0xE8B1,0xAD5A, 0xAD5B,0xAD5C,0xAD5D,0xAD5E,0xAD5F,0xAD60,0xAD61,0xE8B5, 0xE8B2,0xE8B3,0xAD62,0xAD63,0xAD64,0xAD65,0xAD66,0xAD67, 0xAD68,0xAD69,0xAD6A,0xAD6B,0xAD6C,0xAD6D,0xAD6E,0xAD6F, 0xAD70,0xAD71,0xE8B7,0xAD72,0xAD73,0xAD74,0xAD75,0xAD76, 0xAD77,0xAD78,0xAD79,0xAD7A,0xAD7B,0xAD7C,0xAD7D,0xAD7E, 0xAD80,0xAD81,0xAD82,0xAD83,0xAD84,0xAD85,0xAD86,0xAD87, 0xAD88,0xAD89,0xE8B6,0xAD8A,0xAD8B,0xAD8C,0xAD8D,0xAD8E, 0xAD8F,0xAD90,0xAD91,0xAD92,0xB9CF,0xAD93,0xF0AC,0xAD94, 0xF0AD,0xAD95,0xC6B0,0xB0EA,0xC8BF,0xAD96,0xCDDF,0xAD97, 0xAD98,0xAD99,0xAD9A,0xAD9B,0xAD9C,0xAD9D,0xCECD,0xEAB1, 0xAD9E,0xAD9F,0xADA0,0xAE40,0xEAB2,0xAE41,0xC6BF,0xB4C9, 0xAE42,0xAE43,0xAE44,0xAE45,0xAE46,0xAE47,0xAE48,0xEAB3, 0xAE49,0xAE4A,0xAE4B,0xAE4C,0xD5E7,0xAE4D,0xAE4E,0xAE4F, 0xAE50,0xAE51,0xAE52,0xAE53,0xAE54,0xDDF9,0xAE55,0xEAB4, 0xAE56,0xEAB5,0xAE57,0xEAB6,0xAE58,0xAE59,0xAE5A,0xAE5B, 0xB8CA,0xDFB0,0xC9F5,0xAE5C,0xCCF0,0xAE5D,0xAE5E,0xC9FA, 0xAE5F,0xAE60,0xAE61,0xAE62,0xAE63,0xC9FB,0xAE64,0xAE65, 0xD3C3,0xCBA6,0xAE66,0xB8A6,0xF0AE,0xB1C2,0xAE67,0xE5B8, 0xCCEF,0xD3C9,0xBCD7,0xC9EA,0xAE68,0xB5E7,0xAE69,0xC4D0, 0xB5E9,0xAE6A,0xEEAE,0xBBAD,0xAE6B,0xAE6C,0xE7DE,0xAE6D, 0xEEAF,0xAE6E,0xAE6F,0xAE70,0xAE71,0xB3A9,0xAE72,0xAE73, 0xEEB2,0xAE74,0xAE75,0xEEB1,0xBDE7,0xAE76,0xEEB0,0xCEB7, 0xAE77,0xAE78,0xAE79,0xAE7A,0xC5CF,0xAE7B,0xAE7C,0xAE7D, 0xAE7E,0xC1F4,0xDBCE,0xEEB3,0xD0F3,0xAE80,0xAE81,0xAE82, 0xAE83,0xAE84,0xAE85,0xAE86,0xAE87,0xC2D4,0xC6E8,0xAE88, 0xAE89,0xAE8A,0xB7AC,0xAE8B,0xAE8C,0xAE8D,0xAE8E,0xAE8F, 0xAE90,0xAE91,0xEEB4,0xAE92,0xB3EB,0xAE93,0xAE94,0xAE95, 0xBBFB,0xEEB5,0xAE96,0xAE97,0xAE98,0xAE99,0xAE9A,0xE7DC, 0xAE9B,0xAE9C,0xAE9D,0xEEB6,0xAE9E,0xAE9F,0xBDAE,0xAEA0, 0xAF40,0xAF41,0xAF42,0xF1E2,0xAF43,0xAF44,0xAF45,0xCAE8, 0xAF46,0xD2C9,0xF0DA,0xAF47,0xF0DB,0xAF48,0xF0DC,0xC1C6, 0xAF49,0xB8ED,0xBECE,0xAF4A,0xAF4B,0xF0DE,0xAF4C,0xC5B1, 0xF0DD,0xD1F1,0xAF4D,0xF0E0,0xB0CC,0xBDEA,0xAF4E,0xAF4F, 0xAF50,0xAF51,0xAF52,0xD2DF,0xF0DF,0xAF53,0xB4AF,0xB7E8, 0xF0E6,0xF0E5,0xC6A3,0xF0E1,0xF0E2,0xB4C3,0xAF54,0xAF55, 0xF0E3,0xD5EE,0xAF56,0xAF57,0xCCDB,0xBED2,0xBCB2,0xAF58, 0xAF59,0xAF5A,0xF0E8,0xF0E7,0xF0E4,0xB2A1,0xAF5B,0xD6A2, 0xD3B8,0xBEB7,0xC8AC,0xAF5C,0xAF5D,0xF0EA,0xAF5E,0xAF5F, 0xAF60,0xAF61,0xD1F7,0xAF62,0xD6CC,0xBADB,0xF0E9,0xAF63, 0xB6BB,0xAF64,0xAF65,0xCDB4,0xAF66,0xAF67,0xC6A6,0xAF68, 0xAF69,0xAF6A,0xC1A1,0xF0EB,0xF0EE,0xAF6B,0xF0ED,0xF0F0, 0xF0EC,0xAF6C,0xBBBE,0xF0EF,0xAF6D,0xAF6E,0xAF6F,0xAF70, 0xCCB5,0xF0F2,0xAF71,0xAF72,0xB3D5,0xAF73,0xAF74,0xAF75, 0xAF76,0xB1D4,0xAF77,0xAF78,0xF0F3,0xAF79,0xAF7A,0xF0F4, 0xF0F6,0xB4E1,0xAF7B,0xF0F1,0xAF7C,0xF0F7,0xAF7D,0xAF7E, 0xAF80,0xAF81,0xF0FA,0xAF82,0xF0F8,0xAF83,0xAF84,0xAF85, 0xF0F5,0xAF86,0xAF87,0xAF88,0xAF89,0xF0FD,0xAF8A,0xF0F9, 0xF0FC,0xF0FE,0xAF8B,0xF1A1,0xAF8C,0xAF8D,0xAF8E,0xCEC1, 0xF1A4,0xAF8F,0xF1A3,0xAF90,0xC1F6,0xF0FB,0xCADD,0xAF91, 0xAF92,0xB4F1,0xB1F1,0xCCB1,0xAF93,0xF1A6,0xAF94,0xAF95, 0xF1A7,0xAF96,0xAF97,0xF1AC,0xD5CE,0xF1A9,0xAF98,0xAF99, 0xC8B3,0xAF9A,0xAF9B,0xAF9C,0xF1A2,0xAF9D,0xF1AB,0xF1A8, 0xF1A5,0xAF9E,0xAF9F,0xF1AA,0xAFA0,0xB040,0xB041,0xB042, 0xB043,0xB044,0xB045,0xB046,0xB0A9,0xF1AD,0xB047,0xB048, 0xB049,0xB04A,0xB04B,0xB04C,0xF1AF,0xB04D,0xF1B1,0xB04E, 0xB04F,0xB050,0xB051,0xB052,0xF1B0,0xB053,0xF1AE,0xB054, 0xB055,0xB056,0xB057,0xD1A2,0xB058,0xB059,0xB05A,0xB05B, 0xB05C,0xB05D,0xB05E,0xF1B2,0xB05F,0xB060,0xB061,0xF1B3, 0xB062,0xB063,0xB064,0xB065,0xB066,0xB067,0xB068,0xB069, 0xB9EF,0xB06A,0xB06B,0xB5C7,0xB06C,0xB0D7,0xB0D9,0xB06D, 0xB06E,0xB06F,0xD4ED,0xB070,0xB5C4,0xB071,0xBDD4,0xBBCA, 0xF0A7,0xB072,0xB073,0xB8DE,0xB074,0xB075,0xF0A8,0xB076, 0xB077,0xB0A8,0xB078,0xF0A9,0xB079,0xB07A,0xCDEE,0xB07B, 0xB07C,0xF0AA,0xB07D,0xB07E,0xB080,0xB081,0xB082,0xB083, 0xB084,0xB085,0xB086,0xB087,0xF0AB,0xB088,0xB089,0xB08A, 0xB08B,0xB08C,0xB08D,0xB08E,0xB08F,0xB090,0xC6A4,0xB091, 0xB092,0xD6E5,0xF1E4,0xB093,0xF1E5,0xB094,0xB095,0xB096, 0xB097,0xB098,0xB099,0xB09A,0xB09B,0xB09C,0xB09D,0xC3F3, 0xB09E,0xB09F,0xD3DB,0xB0A0,0xB140,0xD6D1,0xC5E8,0xB141, 0xD3AF,0xB142,0xD2E6,0xB143,0xB144,0xEEC1,0xB0BB,0xD5B5, 0xD1CE,0xBCE0,0xBAD0,0xB145,0xBFF8,0xB146,0xB8C7,0xB5C1, 0xC5CC,0xB147,0xB148,0xCAA2,0xB149,0xB14A,0xB14B,0xC3CB, 0xB14C,0xB14D,0xB14E,0xB14F,0xB150,0xEEC2,0xB151,0xB152, 0xB153,0xB154,0xB155,0xB156,0xB157,0xB158,0xC4BF,0xB6A2, 0xB159,0xEDEC,0xC3A4,0xB15A,0xD6B1,0xB15B,0xB15C,0xB15D, 0xCFE0,0xEDEF,0xB15E,0xB15F,0xC5CE,0xB160,0xB6DC,0xB161, 0xB162,0xCAA1,0xB163,0xB164,0xEDED,0xB165,0xB166,0xEDF0, 0xEDF1,0xC3BC,0xB167,0xBFB4,0xB168,0xEDEE,0xB169,0xB16A, 0xB16B,0xB16C,0xB16D,0xB16E,0xB16F,0xB170,0xB171,0xB172, 0xB173,0xEDF4,0xEDF2,0xB174,0xB175,0xB176,0xB177,0xD5E6, 0xC3DF,0xB178,0xEDF3,0xB179,0xB17A,0xB17B,0xEDF6,0xB17C, 0xD5A3,0xD1A3,0xB17D,0xB17E,0xB180,0xEDF5,0xB181,0xC3D0, 0xB182,0xB183,0xB184,0xB185,0xB186,0xEDF7,0xBFF4,0xBEEC, 0xEDF8,0xB187,0xCCF7,0xB188,0xD1DB,0xB189,0xB18A,0xB18B, 0xD7C5,0xD5F6,0xB18C,0xEDFC,0xB18D,0xB18E,0xB18F,0xEDFB, 0xB190,0xB191,0xB192,0xB193,0xB194,0xB195,0xB196,0xB197, 0xEDF9,0xEDFA,0xB198,0xB199,0xB19A,0xB19B,0xB19C,0xB19D, 0xB19E,0xB19F,0xEDFD,0xBEA6,0xB1A0,0xB240,0xB241,0xB242, 0xB243,0xCBAF,0xEEA1,0xB6BD,0xB244,0xEEA2,0xC4C0,0xB245, 0xEDFE,0xB246,0xB247,0xBDDE,0xB2C7,0xB248,0xB249,0xB24A, 0xB24B,0xB24C,0xB24D,0xB24E,0xB24F,0xB250,0xB251,0xB252, 0xB253,0xB6C3,0xB254,0xB255,0xB256,0xEEA5,0xD8BA,0xEEA3, 0xEEA6,0xB257,0xB258,0xB259,0xC3E9,0xB3F2,0xB25A,0xB25B, 0xB25C,0xB25D,0xB25E,0xB25F,0xEEA7,0xEEA4,0xCFB9,0xB260, 0xB261,0xEEA8,0xC2F7,0xB262,0xB263,0xB264,0xB265,0xB266, 0xB267,0xB268,0xB269,0xB26A,0xB26B,0xB26C,0xB26D,0xEEA9, 0xEEAA,0xB26E,0xDEAB,0xB26F,0xB270,0xC6B3,0xB271,0xC7C6, 0xB272,0xD6F5,0xB5C9,0xB273,0xCBB2,0xB274,0xB275,0xB276, 0xEEAB,0xB277,0xB278,0xCDAB,0xB279,0xEEAC,0xB27A,0xB27B, 0xB27C,0xB27D,0xB27E,0xD5B0,0xB280,0xEEAD,0xB281,0xF6C4, 0xB282,0xB283,0xB284,0xB285,0xB286,0xB287,0xB288,0xB289, 0xB28A,0xB28B,0xB28C,0xB28D,0xB28E,0xDBC7,0xB28F,0xB290, 0xB291,0xB292,0xB293,0xB294,0xB295,0xB296,0xB297,0xB4A3, 0xB298,0xB299,0xB29A,0xC3AC,0xF1E6,0xB29B,0xB29C,0xB29D, 0xB29E,0xB29F,0xCAB8,0xD2D3,0xB2A0,0xD6AA,0xB340,0xEFF2, 0xB341,0xBED8,0xB342,0xBDC3,0xEFF3,0xB6CC,0xB0AB,0xB343, 0xB344,0xB345,0xB346,0xCAAF,0xB347,0xB348,0xEDB6,0xB349, 0xEDB7,0xB34A,0xB34B,0xB34C,0xB34D,0xCEF9,0xB7AF,0xBFF3, 0xEDB8,0xC2EB,0xC9B0,0xB34E,0xB34F,0xB350,0xB351,0xB352, 0xB353,0xEDB9,0xB354,0xB355,0xC6F6,0xBFB3,0xB356,0xB357, 0xB358,0xEDBC,0xC5F8,0xB359,0xD1D0,0xB35A,0xD7A9,0xEDBA, 0xEDBB,0xB35B,0xD1E2,0xB35C,0xEDBF,0xEDC0,0xB35D,0xEDC4, 0xB35E,0xB35F,0xB360,0xEDC8,0xB361,0xEDC6,0xEDCE,0xD5E8, 0xB362,0xEDC9,0xB363,0xB364,0xEDC7,0xEDBE,0xB365,0xB366, 0xC5E9,0xB367,0xB368,0xB369,0xC6C6,0xB36A,0xB36B,0xC9E9, 0xD4D2,0xEDC1,0xEDC2,0xEDC3,0xEDC5,0xB36C,0xC0F9,0xB36D, 0xB4A1,0xB36E,0xB36F,0xB370,0xB371,0xB9E8,0xB372,0xEDD0, 0xB373,0xB374,0xB375,0xB376,0xEDD1,0xB377,0xEDCA,0xB378, 0xEDCF,0xB379,0xCEF8,0xB37A,0xB37B,0xCBB6,0xEDCC,0xEDCD, 0xB37C,0xB37D,0xB37E,0xB380,0xB381,0xCFF5,0xB382,0xB383, 0xB384,0xB385,0xB386,0xB387,0xB388,0xB389,0xB38A,0xB38B, 0xB38C,0xB38D,0xEDD2,0xC1F2,0xD3B2,0xEDCB,0xC8B7,0xB38E, 0xB38F,0xB390,0xB391,0xB392,0xB393,0xB394,0xB395,0xBCEF, 0xB396,0xB397,0xB398,0xB399,0xC5F0,0xB39A,0xB39B,0xB39C, 0xB39D,0xB39E,0xB39F,0xB3A0,0xB440,0xB441,0xB442,0xEDD6, 0xB443,0xB5EF,0xB444,0xB445,0xC2B5,0xB0AD,0xCBE9,0xB446, 0xB447,0xB1AE,0xB448,0xEDD4,0xB449,0xB44A,0xB44B,0xCDEB, 0xB5E2,0xB44C,0xEDD5,0xEDD3,0xEDD7,0xB44D,0xB44E,0xB5FA, 0xB44F,0xEDD8,0xB450,0xEDD9,0xB451,0xEDDC,0xB452,0xB1CC, 0xB453,0xB454,0xB455,0xB456,0xB457,0xB458,0xB459,0xB45A, 0xC5F6,0xBCEE,0xEDDA,0xCCBC,0xB2EA,0xB45B,0xB45C,0xB45D, 0xB45E,0xEDDB,0xB45F,0xB460,0xB461,0xB462,0xC4EB,0xB463, 0xB464,0xB4C5,0xB465,0xB466,0xB467,0xB0F5,0xB468,0xB469, 0xB46A,0xEDDF,0xC0DA,0xB4E8,0xB46B,0xB46C,0xB46D,0xB46E, 0xC5CD,0xB46F,0xB470,0xB471,0xEDDD,0xBFC4,0xB472,0xB473, 0xB474,0xEDDE,0xB475,0xB476,0xB477,0xB478,0xB479,0xB47A, 0xB47B,0xB47C,0xB47D,0xB47E,0xB480,0xB481,0xB482,0xB483, 0xC4A5,0xB484,0xB485,0xB486,0xEDE0,0xB487,0xB488,0xB489, 0xB48A,0xB48B,0xEDE1,0xB48C,0xEDE3,0xB48D,0xB48E,0xC1D7, 0xB48F,0xB490,0xBBC7,0xB491,0xB492,0xB493,0xB494,0xB495, 0xB496,0xBDB8,0xB497,0xB498,0xB499,0xEDE2,0xB49A,0xB49B, 0xB49C,0xB49D,0xB49E,0xB49F,0xB4A0,0xB540,0xB541,0xB542, 0xB543,0xB544,0xB545,0xEDE4,0xB546,0xB547,0xB548,0xB549, 0xB54A,0xB54B,0xB54C,0xB54D,0xB54E,0xB54F,0xEDE6,0xB550, 0xB551,0xB552,0xB553,0xB554,0xEDE5,0xB555,0xB556,0xB557, 0xB558,0xB559,0xB55A,0xB55B,0xB55C,0xB55D,0xB55E,0xB55F, 0xB560,0xB561,0xB562,0xB563,0xEDE7,0xB564,0xB565,0xB566, 0xB567,0xB568,0xCABE,0xECEA,0xC0F1,0xB569,0xC9E7,0xB56A, 0xECEB,0xC6EE,0xB56B,0xB56C,0xB56D,0xB56E,0xECEC,0xB56F, 0xC6ED,0xECED,0xB570,0xB571,0xB572,0xB573,0xB574,0xB575, 0xB576,0xB577,0xB578,0xECF0,0xB579,0xB57A,0xD7E6,0xECF3, 0xB57B,0xB57C,0xECF1,0xECEE,0xECEF,0xD7A3,0xC9F1,0xCBEE, 0xECF4,0xB57D,0xECF2,0xB57E,0xB580,0xCFE9,0xB581,0xECF6, 0xC6B1,0xB582,0xB583,0xB584,0xB585,0xBCC0,0xB586,0xECF5, 0xB587,0xB588,0xB589,0xB58A,0xB58B,0xB58C,0xB58D,0xB5BB, 0xBBF6,0xB58E,0xECF7,0xB58F,0xB590,0xB591,0xB592,0xB593, 0xD9F7,0xBDFB,0xB594,0xB595,0xC2BB,0xECF8,0xB596,0xB597, 0xB598,0xB599,0xECF9,0xB59A,0xB59B,0xB59C,0xB59D,0xB8A3, 0xB59E,0xB59F,0xB5A0,0xB640,0xB641,0xB642,0xB643,0xB644, 0xB645,0xB646,0xECFA,0xB647,0xB648,0xB649,0xB64A,0xB64B, 0xB64C,0xB64D,0xB64E,0xB64F,0xB650,0xB651,0xB652,0xECFB, 0xB653,0xB654,0xB655,0xB656,0xB657,0xB658,0xB659,0xB65A, 0xB65B,0xB65C,0xB65D,0xECFC,0xB65E,0xB65F,0xB660,0xB661, 0xB662,0xD3ED,0xD8AE,0xC0EB,0xB663,0xC7DD,0xBACC,0xB664, 0xD0E3,0xCBBD,0xB665,0xCDBA,0xB666,0xB667,0xB8D1,0xB668, 0xB669,0xB1FC,0xB66A,0xC7EF,0xB66B,0xD6D6,0xB66C,0xB66D, 0xB66E,0xBFC6,0xC3EB,0xB66F,0xB670,0xEFF5,0xB671,0xB672, 0xC3D8,0xB673,0xB674,0xB675,0xB676,0xB677,0xB678,0xD7E2, 0xB679,0xB67A,0xB67B,0xEFF7,0xB3D3,0xB67C,0xC7D8,0xD1ED, 0xB67D,0xD6C8,0xB67E,0xEFF8,0xB680,0xEFF6,0xB681,0xBBFD, 0xB3C6,0xB682,0xB683,0xB684,0xB685,0xB686,0xB687,0xB688, 0xBDD5,0xB689,0xB68A,0xD2C6,0xB68B,0xBBE0,0xB68C,0xB68D, 0xCFA1,0xB68E,0xEFFC,0xEFFB,0xB68F,0xB690,0xEFF9,0xB691, 0xB692,0xB693,0xB694,0xB3CC,0xB695,0xC9D4,0xCBB0,0xB696, 0xB697,0xB698,0xB699,0xB69A,0xEFFE,0xB69B,0xB69C,0xB0DE, 0xB69D,0xB69E,0xD6C9,0xB69F,0xB6A0,0xB740,0xEFFD,0xB741, 0xB3ED,0xB742,0xB743,0xF6D5,0xB744,0xB745,0xB746,0xB747, 0xB748,0xB749,0xB74A,0xB74B,0xB74C,0xB74D,0xB74E,0xB74F, 0xB750,0xB751,0xB752,0xCEC8,0xB753,0xB754,0xB755,0xF0A2, 0xB756,0xF0A1,0xB757,0xB5BE,0xBCDA,0xBBFC,0xB758,0xB8E5, 0xB759,0xB75A,0xB75B,0xB75C,0xB75D,0xB75E,0xC4C2,0xB75F, 0xB760,0xB761,0xB762,0xB763,0xB764,0xB765,0xB766,0xB767, 0xB768,0xF0A3,0xB769,0xB76A,0xB76B,0xB76C,0xB76D,0xCBEB, 0xB76E,0xB76F,0xB770,0xB771,0xB772,0xB773,0xB774,0xB775, 0xB776,0xB777,0xB778,0xB779,0xB77A,0xB77B,0xB77C,0xB77D, 0xB77E,0xB780,0xB781,0xB782,0xB783,0xB784,0xB785,0xB786, 0xF0A6,0xB787,0xB788,0xB789,0xD1A8,0xB78A,0xBEBF,0xC7EE, 0xF1B6,0xF1B7,0xBFD5,0xB78B,0xB78C,0xB78D,0xB78E,0xB4A9, 0xF1B8,0xCDBB,0xB78F,0xC7D4,0xD5AD,0xB790,0xF1B9,0xB791, 0xF1BA,0xB792,0xB793,0xB794,0xB795,0xC7CF,0xB796,0xB797, 0xB798,0xD2A4,0xD6CF,0xB799,0xB79A,0xF1BB,0xBDD1,0xB4B0, 0xBEBD,0xB79B,0xB79C,0xB79D,0xB4DC,0xCED1,0xB79E,0xBFDF, 0xF1BD,0xB79F,0xB7A0,0xB840,0xB841,0xBFFA,0xF1BC,0xB842, 0xF1BF,0xB843,0xB844,0xB845,0xF1BE,0xF1C0,0xB846,0xB847, 0xB848,0xB849,0xB84A,0xF1C1,0xB84B,0xB84C,0xB84D,0xB84E, 0xB84F,0xB850,0xB851,0xB852,0xB853,0xB854,0xB855,0xC1FE, 0xB856,0xB857,0xB858,0xB859,0xB85A,0xB85B,0xB85C,0xB85D, 0xB85E,0xB85F,0xB860,0xC1A2,0xB861,0xB862,0xB863,0xB864, 0xB865,0xB866,0xB867,0xB868,0xB869,0xB86A,0xCAFA,0xB86B, 0xB86C,0xD5BE,0xB86D,0xB86E,0xB86F,0xB870,0xBEBA,0xBEB9, 0xD5C2,0xB871,0xB872,0xBFA2,0xB873,0xCDAF,0xF1B5,0xB874, 0xB875,0xB876,0xB877,0xB878,0xB879,0xBDDF,0xB87A,0xB6CB, 0xB87B,0xB87C,0xB87D,0xB87E,0xB880,0xB881,0xB882,0xB883, 0xB884,0xD6F1,0xF3C3,0xB885,0xB886,0xF3C4,0xB887,0xB8CD, 0xB888,0xB889,0xB88A,0xF3C6,0xF3C7,0xB88B,0xB0CA,0xB88C, 0xF3C5,0xB88D,0xF3C9,0xCBF1,0xB88E,0xB88F,0xB890,0xF3CB, 0xB891,0xD0A6,0xB892,0xB893,0xB1CA,0xF3C8,0xB894,0xB895, 0xB896,0xF3CF,0xB897,0xB5D1,0xB898,0xB899,0xF3D7,0xB89A, 0xF3D2,0xB89B,0xB89C,0xB89D,0xF3D4,0xF3D3,0xB7FB,0xB89E, 0xB1BF,0xB89F,0xF3CE,0xF3CA,0xB5DA,0xB8A0,0xF3D0,0xB940, 0xB941,0xF3D1,0xB942,0xF3D5,0xB943,0xB944,0xB945,0xB946, 0xF3CD,0xB947,0xBCE3,0xB948,0xC1FD,0xB949,0xF3D6,0xB94A, 0xB94B,0xB94C,0xB94D,0xB94E,0xB94F,0xF3DA,0xB950,0xF3CC, 0xB951,0xB5C8,0xB952,0xBDEE,0xF3DC,0xB953,0xB954,0xB7A4, 0xBFF0,0xD6FE,0xCDB2,0xB955,0xB4F0,0xB956,0xB2DF,0xB957, 0xF3D8,0xB958,0xF3D9,0xC9B8,0xB959,0xF3DD,0xB95A,0xB95B, 0xF3DE,0xB95C,0xF3E1,0xB95D,0xB95E,0xB95F,0xB960,0xB961, 0xB962,0xB963,0xB964,0xB965,0xB966,0xB967,0xF3DF,0xB968, 0xB969,0xF3E3,0xF3E2,0xB96A,0xB96B,0xF3DB,0xB96C,0xBFEA, 0xB96D,0xB3EF,0xB96E,0xF3E0,0xB96F,0xB970,0xC7A9,0xB971, 0xBCF2,0xB972,0xB973,0xB974,0xB975,0xF3EB,0xB976,0xB977, 0xB978,0xB979,0xB97A,0xB97B,0xB97C,0xB9BF,0xB97D,0xB97E, 0xF3E4,0xB980,0xB981,0xB982,0xB2AD,0xBBFE,0xB983,0xCBE3, 0xB984,0xB985,0xB986,0xB987,0xF3ED,0xF3E9,0xB988,0xB989, 0xB98A,0xB9DC,0xF3EE,0xB98B,0xB98C,0xB98D,0xF3E5,0xF3E6, 0xF3EA,0xC2E1,0xF3EC,0xF3EF,0xF3E8,0xBCFD,0xB98E,0xB98F, 0xB990,0xCFE4,0xB991,0xB992,0xF3F0,0xB993,0xB994,0xB995, 0xF3E7,0xB996,0xB997,0xB998,0xB999,0xB99A,0xB99B,0xB99C, 0xB99D,0xF3F2,0xB99E,0xB99F,0xB9A0,0xBA40,0xD7AD,0xC6AA, 0xBA41,0xBA42,0xBA43,0xBA44,0xF3F3,0xBA45,0xBA46,0xBA47, 0xBA48,0xF3F1,0xBA49,0xC2A8,0xBA4A,0xBA4B,0xBA4C,0xBA4D, 0xBA4E,0xB8DD,0xF3F5,0xBA4F,0xBA50,0xF3F4,0xBA51,0xBA52, 0xBA53,0xB4DB,0xBA54,0xBA55,0xBA56,0xF3F6,0xF3F7,0xBA57, 0xBA58,0xBA59,0xF3F8,0xBA5A,0xBA5B,0xBA5C,0xC0BA,0xBA5D, 0xBA5E,0xC0E9,0xBA5F,0xBA60,0xBA61,0xBA62,0xBA63,0xC5F1, 0xBA64,0xBA65,0xBA66,0xBA67,0xF3FB,0xBA68,0xF3FA,0xBA69, 0xBA6A,0xBA6B,0xBA6C,0xBA6D,0xBA6E,0xBA6F,0xBA70,0xB4D8, 0xBA71,0xBA72,0xBA73,0xF3FE,0xF3F9,0xBA74,0xBA75,0xF3FC, 0xBA76,0xBA77,0xBA78,0xBA79,0xBA7A,0xBA7B,0xF3FD,0xBA7C, 0xBA7D,0xBA7E,0xBA80,0xBA81,0xBA82,0xBA83,0xBA84,0xF4A1, 0xBA85,0xBA86,0xBA87,0xBA88,0xBA89,0xBA8A,0xF4A3,0xBBC9, 0xBA8B,0xBA8C,0xF4A2,0xBA8D,0xBA8E,0xBA8F,0xBA90,0xBA91, 0xBA92,0xBA93,0xBA94,0xBA95,0xBA96,0xBA97,0xBA98,0xBA99, 0xF4A4,0xBA9A,0xBA9B,0xBA9C,0xBA9D,0xBA9E,0xBA9F,0xB2BE, 0xF4A6,0xF4A5,0xBAA0,0xBB40,0xBB41,0xBB42,0xBB43,0xBB44, 0xBB45,0xBB46,0xBB47,0xBB48,0xBB49,0xBCAE,0xBB4A,0xBB4B, 0xBB4C,0xBB4D,0xBB4E,0xBB4F,0xBB50,0xBB51,0xBB52,0xBB53, 0xBB54,0xBB55,0xBB56,0xBB57,0xBB58,0xBB59,0xBB5A,0xBB5B, 0xBB5C,0xBB5D,0xBB5E,0xBB5F,0xBB60,0xBB61,0xBB62,0xBB63, 0xBB64,0xBB65,0xBB66,0xBB67,0xBB68,0xBB69,0xBB6A,0xBB6B, 0xBB6C,0xBB6D,0xBB6E,0xC3D7,0xD9E1,0xBB6F,0xBB70,0xBB71, 0xBB72,0xBB73,0xBB74,0xC0E0,0xF4CC,0xD7D1,0xBB75,0xBB76, 0xBB77,0xBB78,0xBB79,0xBB7A,0xBB7B,0xBB7C,0xBB7D,0xBB7E, 0xBB80,0xB7DB,0xBB81,0xBB82,0xBB83,0xBB84,0xBB85,0xBB86, 0xBB87,0xF4CE,0xC1A3,0xBB88,0xBB89,0xC6C9,0xBB8A,0xB4D6, 0xD5B3,0xBB8B,0xBB8C,0xBB8D,0xF4D0,0xF4CF,0xF4D1,0xCBDA, 0xBB8E,0xBB8F,0xF4D2,0xBB90,0xD4C1,0xD6E0,0xBB91,0xBB92, 0xBB93,0xBB94,0xB7E0,0xBB95,0xBB96,0xBB97,0xC1B8,0xBB98, 0xBB99,0xC1BB,0xF4D3,0xBEAC,0xBB9A,0xBB9B,0xBB9C,0xBB9D, 0xBB9E,0xB4E2,0xBB9F,0xBBA0,0xF4D4,0xF4D5,0xBEAB,0xBC40, 0xBC41,0xF4D6,0xBC42,0xBC43,0xBC44,0xF4DB,0xBC45,0xF4D7, 0xF4DA,0xBC46,0xBAFD,0xBC47,0xF4D8,0xF4D9,0xBC48,0xBC49, 0xBC4A,0xBC4B,0xBC4C,0xBC4D,0xBC4E,0xB8E2,0xCCC7,0xF4DC, 0xBC4F,0xB2DA,0xBC50,0xBC51,0xC3D3,0xBC52,0xBC53,0xD4E3, 0xBFB7,0xBC54,0xBC55,0xBC56,0xBC57,0xBC58,0xBC59,0xBC5A, 0xF4DD,0xBC5B,0xBC5C,0xBC5D,0xBC5E,0xBC5F,0xBC60,0xC5B4, 0xBC61,0xBC62,0xBC63,0xBC64,0xBC65,0xBC66,0xBC67,0xBC68, 0xF4E9,0xBC69,0xBC6A,0xCFB5,0xBC6B,0xBC6C,0xBC6D,0xBC6E }; const static uint16 gbkEncoderInnerIndex4[]= { 0xBC6F,0xBC70,0xBC71,0xBC72,0xBC73,0xBC74,0xBC75,0xBC76, 0xBC77,0xBC78,0xCEC9,0xBC79,0xBC7A,0xBC7B,0xBC7C,0xBC7D, 0xBC7E,0xBC80,0xBC81,0xBC82,0xBC83,0xBC84,0xBC85,0xBC86, 0xBC87,0xBC88,0xBC89,0xBC8A,0xBC8B,0xBC8C,0xBC8D,0xBC8E, 0xCBD8,0xBC8F,0xCBF7,0xBC90,0xBC91,0xBC92,0xBC93,0xBDF4, 0xBC94,0xBC95,0xBC96,0xD7CF,0xBC97,0xBC98,0xBC99,0xC0DB, 0xBC9A,0xBC9B,0xBC9C,0xBC9D,0xBC9E,0xBC9F,0xBCA0,0xBD40, 0xBD41,0xBD42,0xBD43,0xBD44,0xBD45,0xBD46,0xBD47,0xBD48, 0xBD49,0xBD4A,0xBD4B,0xBD4C,0xBD4D,0xBD4E,0xBD4F,0xBD50, 0xBD51,0xBD52,0xBD53,0xBD54,0xBD55,0xBD56,0xBD57,0xBD58, 0xBD59,0xBD5A,0xBD5B,0xBD5C,0xBD5D,0xBD5E,0xBD5F,0xBD60, 0xBD61,0xBD62,0xBD63,0xBD64,0xBD65,0xBD66,0xBD67,0xBD68, 0xBD69,0xBD6A,0xBD6B,0xBD6C,0xBD6D,0xBD6E,0xBD6F,0xBD70, 0xBD71,0xBD72,0xBD73,0xBD74,0xBD75,0xBD76,0xD0F5,0xBD77, 0xBD78,0xBD79,0xBD7A,0xBD7B,0xBD7C,0xBD7D,0xBD7E,0xF4EA, 0xBD80,0xBD81,0xBD82,0xBD83,0xBD84,0xBD85,0xBD86,0xBD87, 0xBD88,0xBD89,0xBD8A,0xBD8B,0xBD8C,0xBD8D,0xBD8E,0xBD8F, 0xBD90,0xBD91,0xBD92,0xBD93,0xBD94,0xBD95,0xBD96,0xBD97, 0xBD98,0xBD99,0xBD9A,0xBD9B,0xBD9C,0xBD9D,0xBD9E,0xBD9F, 0xBDA0,0xBE40,0xBE41,0xBE42,0xBE43,0xBE44,0xBE45,0xBE46, 0xBE47,0xBE48,0xBE49,0xBE4A,0xBE4B,0xBE4C,0xF4EB,0xBE4D, 0xBE4E,0xBE4F,0xBE50,0xBE51,0xBE52,0xBE53,0xF4EC,0xBE54, 0xBE55,0xBE56,0xBE57,0xBE58,0xBE59,0xBE5A,0xBE5B,0xBE5C, 0xBE5D,0xBE5E,0xBE5F,0xBE60,0xBE61,0xBE62,0xBE63,0xBE64, 0xBE65,0xBE66,0xBE67,0xBE68,0xBE69,0xBE6A,0xBE6B,0xBE6C, 0xBE6D,0xBE6E,0xBE6F,0xBE70,0xBE71,0xBE72,0xBE73,0xBE74, 0xBE75,0xBE76,0xBE77,0xBE78,0xBE79,0xBE7A,0xBE7B,0xBE7C, 0xBE7D,0xBE7E,0xBE80,0xBE81,0xBE82,0xBE83,0xBE84,0xBE85, 0xBE86,0xBE87,0xBE88,0xBE89,0xBE8A,0xBE8B,0xBE8C,0xBE8D, 0xBE8E,0xBE8F,0xBE90,0xBE91,0xBE92,0xBE93,0xBE94,0xBE95, 0xBE96,0xBE97,0xBE98,0xBE99,0xBE9A,0xBE9B,0xBE9C,0xBE9D, 0xBE9E,0xBE9F,0xBEA0,0xBF40,0xBF41,0xBF42,0xBF43,0xBF44, 0xBF45,0xBF46,0xBF47,0xBF48,0xBF49,0xBF4A,0xBF4B,0xBF4C, 0xBF4D,0xBF4E,0xBF4F,0xBF50,0xBF51,0xBF52,0xBF53,0xBF54, 0xBF55,0xBF56,0xBF57,0xBF58,0xBF59,0xBF5A,0xBF5B,0xBF5C, 0xBF5D,0xBF5E,0xBF5F,0xBF60,0xBF61,0xBF62,0xBF63,0xBF64, 0xBF65,0xBF66,0xBF67,0xBF68,0xBF69,0xBF6A,0xBF6B,0xBF6C, 0xBF6D,0xBF6E,0xBF6F,0xBF70,0xBF71,0xBF72,0xBF73,0xBF74, 0xBF75,0xBF76,0xBF77,0xBF78,0xBF79,0xBF7A,0xBF7B,0xBF7C, 0xBF7D,0xBF7E,0xBF80,0xF7E3,0xBF81,0xBF82,0xBF83,0xBF84, 0xBF85,0xB7B1,0xBF86,0xBF87,0xBF88,0xBF89,0xBF8A,0xF4ED, 0xBF8B,0xBF8C,0xBF8D,0xBF8E,0xBF8F,0xBF90,0xBF91,0xBF92, 0xBF93,0xBF94,0xBF95,0xBF96,0xBF97,0xBF98,0xBF99,0xBF9A, 0xBF9B,0xBF9C,0xBF9D,0xBF9E,0xBF9F,0xBFA0,0xC040,0xC041, 0xC042,0xC043,0xC044,0xC045,0xC046,0xC047,0xC048,0xC049, 0xC04A,0xC04B,0xC04C,0xC04D,0xC04E,0xC04F,0xC050,0xC051, 0xC052,0xC053,0xC054,0xC055,0xC056,0xC057,0xC058,0xC059, 0xC05A,0xC05B,0xC05C,0xC05D,0xC05E,0xC05F,0xC060,0xC061, 0xC062,0xC063,0xD7EB,0xC064,0xC065,0xC066,0xC067,0xC068, 0xC069,0xC06A,0xC06B,0xC06C,0xC06D,0xC06E,0xC06F,0xC070, 0xC071,0xC072,0xC073,0xC074,0xC075,0xC076,0xC077,0xC078, 0xC079,0xC07A,0xC07B,0xF4EE,0xC07C,0xC07D,0xC07E,0xE6F9, 0xBEC0,0xE6FA,0xBAEC,0xE6FB,0xCFCB,0xE6FC,0xD4BC,0xBCB6, 0xE6FD,0xE6FE,0xBCCD,0xC8D2,0xCEB3,0xE7A1,0xC080,0xB4BF, 0xE7A2,0xC9B4,0xB8D9,0xC4C9,0xC081,0xD7DD,0xC2DA,0xB7D7, 0xD6BD,0xCEC6,0xB7C4,0xC082,0xC083,0xC5A6,0xE7A3,0xCFDF, 0xE7A4,0xE7A5,0xE7A6,0xC1B7,0xD7E9,0xC9F0,0xCFB8,0xD6AF, 0xD6D5,0xE7A7,0xB0ED,0xE7A8,0xE7A9,0xC9DC,0xD2EF,0xBEAD, 0xE7AA,0xB0F3,0xC8DE,0xBDE1,0xE7AB,0xC8C6,0xC084,0xE7AC, 0xBBE6,0xB8F8,0xD1A4,0xE7AD,0xC2E7,0xBEF8,0xBDCA,0xCDB3, 0xE7AE,0xE7AF,0xBEEE,0xD0E5,0xC085,0xCBE7,0xCCD0,0xBCCC, 0xE7B0,0xBCA8,0xD0F7,0xE7B1,0xC086,0xD0F8,0xE7B2,0xE7B3, 0xB4C2,0xE7B4,0xE7B5,0xC9FE,0xCEAC,0xC3E0,0xE7B7,0xB1C1, 0xB3F1,0xC087,0xE7B8,0xE7B9,0xD7DB,0xD5C0,0xE7BA,0xC2CC, 0xD7BA,0xE7BB,0xE7BC,0xE7BD,0xBCEA,0xC3E5,0xC0C2,0xE7BE, 0xE7BF,0xBCA9,0xC088,0xE7C0,0xE7C1,0xE7B6,0xB6D0,0xE7C2, 0xC089,0xE7C3,0xE7C4,0xBBBA,0xB5DE,0xC2C6,0xB1E0,0xE7C5, 0xD4B5,0xE7C6,0xB8BF,0xE7C8,0xE7C7,0xB7EC,0xC08A,0xE7C9, 0xB2F8,0xE7CA,0xE7CB,0xE7CC,0xE7CD,0xE7CE,0xE7CF,0xE7D0, 0xD3A7,0xCBF5,0xE7D1,0xE7D2,0xE7D3,0xE7D4,0xC9C9,0xE7D5, 0xE7D6,0xE7D7,0xE7D8,0xE7D9,0xBDC9,0xE7DA,0xF3BE,0xC08B, 0xB8D7,0xC08C,0xC8B1,0xC08D,0xC08E,0xC08F,0xC090,0xC091, 0xC092,0xC093,0xF3BF,0xC094,0xF3C0,0xF3C1,0xC095,0xC096, 0xC097,0xC098,0xC099,0xC09A,0xC09B,0xC09C,0xC09D,0xC09E, 0xB9DE,0xCDF8,0xC09F,0xC0A0,0xD8E8,0xBAB1,0xC140,0xC2DE, 0xEEB7,0xC141,0xB7A3,0xC142,0xC143,0xC144,0xC145,0xEEB9, 0xC146,0xEEB8,0xB0D5,0xC147,0xC148,0xC149,0xC14A,0xC14B, 0xEEBB,0xD5D6,0xD7EF,0xC14C,0xC14D,0xC14E,0xD6C3,0xC14F, 0xC150,0xEEBD,0xCAF0,0xC151,0xEEBC,0xC152,0xC153,0xC154, 0xC155,0xEEBE,0xC156,0xC157,0xC158,0xC159,0xEEC0,0xC15A, 0xC15B,0xEEBF,0xC15C,0xC15D,0xC15E,0xC15F,0xC160,0xC161, 0xC162,0xC163,0xD1F2,0xC164,0xC7BC,0xC165,0xC3C0,0xC166, 0xC167,0xC168,0xC169,0xC16A,0xB8E1,0xC16B,0xC16C,0xC16D, 0xC16E,0xC16F,0xC1E7,0xC170,0xC171,0xF4C6,0xD0DF,0xF4C7, 0xC172,0xCFDB,0xC173,0xC174,0xC8BA,0xC175,0xC176,0xF4C8, 0xC177,0xC178,0xC179,0xC17A,0xC17B,0xC17C,0xC17D,0xF4C9, 0xF4CA,0xC17E,0xF4CB,0xC180,0xC181,0xC182,0xC183,0xC184, 0xD9FA,0xB8FE,0xC185,0xC186,0xE5F1,0xD3F0,0xC187,0xF4E0, 0xC188,0xCECC,0xC189,0xC18A,0xC18B,0xB3E1,0xC18C,0xC18D, 0xC18E,0xC18F,0xF1B4,0xC190,0xD2EE,0xC191,0xF4E1,0xC192, 0xC193,0xC194,0xC195,0xC196,0xCFE8,0xF4E2,0xC197,0xC198, 0xC7CC,0xC199,0xC19A,0xC19B,0xC19C,0xC19D,0xC19E,0xB5D4, 0xB4E4,0xF4E4,0xC19F,0xC1A0,0xC240,0xF4E3,0xF4E5,0xC241, 0xC242,0xF4E6,0xC243,0xC244,0xC245,0xC246,0xF4E7,0xC247, 0xBAB2,0xB0BF,0xC248,0xF4E8,0xC249,0xC24A,0xC24B,0xC24C, 0xC24D,0xC24E,0xC24F,0xB7AD,0xD2ED,0xC250,0xC251,0xC252, 0xD2AB,0xC0CF,0xC253,0xBFBC,0xEBA3,0xD5DF,0xEAC8,0xC254, 0xC255,0xC256,0xC257,0xF1F3,0xB6F8,0xCBA3,0xC258,0xC259, 0xC4CD,0xC25A,0xF1E7,0xC25B,0xF1E8,0xB8FB,0xF1E9,0xBAC4, 0xD4C5,0xB0D2,0xC25C,0xC25D,0xF1EA,0xC25E,0xC25F,0xC260, 0xF1EB,0xC261,0xF1EC,0xC262,0xC263,0xF1ED,0xF1EE,0xF1EF, 0xF1F1,0xF1F0,0xC5D5,0xC264,0xC265,0xC266,0xC267,0xC268, 0xC269,0xF1F2,0xC26A,0xB6FA,0xC26B,0xF1F4,0xD2AE,0xDEC7, 0xCBCA,0xC26C,0xC26D,0xB3DC,0xC26E,0xB5A2,0xC26F,0xB9A2, 0xC270,0xC271,0xC4F4,0xF1F5,0xC272,0xC273,0xF1F6,0xC274, 0xC275,0xC276,0xC1C4,0xC1FB,0xD6B0,0xF1F7,0xC277,0xC278, 0xC279,0xC27A,0xF1F8,0xC27B,0xC1AA,0xC27C,0xC27D,0xC27E, 0xC6B8,0xC280,0xBEDB,0xC281,0xC282,0xC283,0xC284,0xC285, 0xC286,0xC287,0xC288,0xC289,0xC28A,0xC28B,0xC28C,0xC28D, 0xC28E,0xF1F9,0xB4CF,0xC28F,0xC290,0xC291,0xC292,0xC293, 0xC294,0xF1FA,0xC295,0xC296,0xC297,0xC298,0xC299,0xC29A, 0xC29B,0xC29C,0xC29D,0xC29E,0xC29F,0xC2A0,0xC340,0xEDB2, 0xEDB1,0xC341,0xC342,0xCBE0,0xD2DE,0xC343,0xCBC1,0xD5D8, 0xC344,0xC8E2,0xC345,0xC0DF,0xBCA1,0xC346,0xC347,0xC348, 0xC349,0xC34A,0xC34B,0xEBC1,0xC34C,0xC34D,0xD0A4,0xC34E, 0xD6E2,0xC34F,0xB6C7,0xB8D8,0xEBC0,0xB8CE,0xC350,0xEBBF, 0xB3A6,0xB9C9,0xD6AB,0xC351,0xB7F4,0xB7CA,0xC352,0xC353, 0xC354,0xBCE7,0xB7BE,0xEBC6,0xC355,0xEBC7,0xB0B9,0xBFCF, 0xC356,0xEBC5,0xD3FD,0xC357,0xEBC8,0xC358,0xC359,0xEBC9, 0xC35A,0xC35B,0xB7CE,0xC35C,0xEBC2,0xEBC4,0xC9F6,0xD6D7, 0xD5CD,0xD0B2,0xEBCF,0xCEB8,0xEBD0,0xC35D,0xB5A8,0xC35E, 0xC35F,0xC360,0xC361,0xC362,0xB1B3,0xEBD2,0xCCA5,0xC363, 0xC364,0xC365,0xC366,0xC367,0xC368,0xC369,0xC5D6,0xEBD3, 0xC36A,0xEBD1,0xC5DF,0xEBCE,0xCAA4,0xEBD5,0xB0FB,0xC36B, 0xC36C,0xBAFA,0xC36D,0xC36E,0xD8B7,0xF1E3,0xC36F,0xEBCA, 0xEBCB,0xEBCC,0xEBCD,0xEBD6,0xE6C0,0xEBD9,0xC370,0xBFE8, 0xD2C8,0xEBD7,0xEBDC,0xB8EC,0xEBD8,0xC371,0xBDBA,0xC372, 0xD0D8,0xC373,0xB0B7,0xC374,0xEBDD,0xC4DC,0xC375,0xC376, 0xC377,0xC378,0xD6AC,0xC379,0xC37A,0xC37B,0xB4E0,0xC37C, 0xC37D,0xC2F6,0xBCB9,0xC37E,0xC380,0xEBDA,0xEBDB,0xD4E0, 0xC6EA,0xC4D4,0xEBDF,0xC5A7,0xD9F5,0xC381,0xB2B1,0xC382, 0xEBE4,0xC383,0xBDC5,0xC384,0xC385,0xC386,0xEBE2,0xC387, 0xC388,0xC389,0xC38A,0xC38B,0xC38C,0xC38D,0xC38E,0xC38F, 0xC390,0xC391,0xC392,0xC393,0xEBE3,0xC394,0xC395,0xB8AC, 0xC396,0xCDD1,0xEBE5,0xC397,0xC398,0xC399,0xEBE1,0xC39A, 0xC1B3,0xC39B,0xC39C,0xC39D,0xC39E,0xC39F,0xC6A2,0xC3A0, 0xC440,0xC441,0xC442,0xC443,0xC444,0xC445,0xCCF3,0xC446, 0xEBE6,0xC447,0xC0B0,0xD2B8,0xEBE7,0xC448,0xC449,0xC44A, 0xB8AF,0xB8AD,0xC44B,0xEBE8,0xC7BB,0xCDF3,0xC44C,0xC44D, 0xC44E,0xEBEA,0xEBEB,0xC44F,0xC450,0xC451,0xC452,0xC453, 0xEBED,0xC454,0xC455,0xC456,0xC457,0xD0C8,0xC458,0xEBF2, 0xC459,0xEBEE,0xC45A,0xC45B,0xC45C,0xEBF1,0xC8F9,0xC45D, 0xD1FC,0xEBEC,0xC45E,0xC45F,0xEBE9,0xC460,0xC461,0xC462, 0xC463,0xB8B9,0xCFD9,0xC4E5,0xEBEF,0xEBF0,0xCCDA,0xCDC8, 0xB0F2,0xC464,0xEBF6,0xC465,0xC466,0xC467,0xC468,0xC469, 0xEBF5,0xC46A,0xB2B2,0xC46B,0xC46C,0xC46D,0xC46E,0xB8E0, 0xC46F,0xEBF7,0xC470,0xC471,0xC472,0xC473,0xC474,0xC475, 0xB1EC,0xC476,0xC477,0xCCC5,0xC4A4,0xCFA5,0xC478,0xC479, 0xC47A,0xC47B,0xC47C,0xEBF9,0xC47D,0xC47E,0xECA2,0xC480, 0xC5F2,0xC481,0xEBFA,0xC482,0xC483,0xC484,0xC485,0xC486, 0xC487,0xC488,0xC489,0xC9C5,0xC48A,0xC48B,0xC48C,0xC48D, 0xC48E,0xC48F,0xE2DF,0xEBFE,0xC490,0xC491,0xC492,0xC493, 0xCDCE,0xECA1,0xB1DB,0xD3B7,0xC494,0xC495,0xD2DC,0xC496, 0xC497,0xC498,0xEBFD,0xC499,0xEBFB,0xC49A,0xC49B,0xC49C, 0xC49D,0xC49E,0xC49F,0xC4A0,0xC540,0xC541,0xC542,0xC543, 0xC544,0xC545,0xC546,0xC547,0xC548,0xC549,0xC54A,0xC54B, 0xC54C,0xC54D,0xC54E,0xB3BC,0xC54F,0xC550,0xC551,0xEAB0, 0xC552,0xC553,0xD7D4,0xC554,0xF4AB,0xB3F4,0xC555,0xC556, 0xC557,0xC558,0xC559,0xD6C1,0xD6C2,0xC55A,0xC55B,0xC55C, 0xC55D,0xC55E,0xC55F,0xD5E9,0xBECA,0xC560,0xF4A7,0xC561, 0xD2A8,0xF4A8,0xF4A9,0xC562,0xF4AA,0xBECB,0xD3DF,0xC563, 0xC564,0xC565,0xC566,0xC567,0xC9E0,0xC9E1,0xC568,0xC569, 0xF3C2,0xC56A,0xCAE6,0xC56B,0xCCF2,0xC56C,0xC56D,0xC56E, 0xC56F,0xC570,0xC571,0xE2B6,0xCBB4,0xC572,0xCEE8,0xD6DB, 0xC573,0xF4AD,0xF4AE,0xF4AF,0xC574,0xC575,0xC576,0xC577, 0xF4B2,0xC578,0xBABD,0xF4B3,0xB0E3,0xF4B0,0xC579,0xF4B1, 0xBDA2,0xB2D5,0xC57A,0xF4B6,0xF4B7,0xB6E6,0xB2B0,0xCFCF, 0xF4B4,0xB4AC,0xC57B,0xF4B5,0xC57C,0xC57D,0xF4B8,0xC57E, 0xC580,0xC581,0xC582,0xC583,0xF4B9,0xC584,0xC585,0xCDA7, 0xC586,0xF4BA,0xC587,0xF4BB,0xC588,0xC589,0xC58A,0xF4BC, 0xC58B,0xC58C,0xC58D,0xC58E,0xC58F,0xC590,0xC591,0xC592, 0xCBD2,0xC593,0xF4BD,0xC594,0xC595,0xC596,0xC597,0xF4BE, 0xC598,0xC599,0xC59A,0xC59B,0xC59C,0xC59D,0xC59E,0xC59F, 0xF4BF,0xC5A0,0xC640,0xC641,0xC642,0xC643,0xF4DE,0xC1BC, 0xBCE8,0xC644,0xC9AB,0xD1DE,0xE5F5,0xC645,0xC646,0xC647, 0xC648,0xDCB3,0xD2D5,0xC649,0xC64A,0xDCB4,0xB0AC,0xDCB5, 0xC64B,0xC64C,0xBDDA,0xC64D,0xDCB9,0xC64E,0xC64F,0xC650, 0xD8C2,0xC651,0xDCB7,0xD3F3,0xC652,0xC9D6,0xDCBA,0xDCB6, 0xC653,0xDCBB,0xC3A2,0xC654,0xC655,0xC656,0xC657,0xDCBC, 0xDCC5,0xDCBD,0xC658,0xC659,0xCEDF,0xD6A5,0xC65A,0xDCCF, 0xC65B,0xDCCD,0xC65C,0xC65D,0xDCD2,0xBDE6,0xC2AB,0xC65E, 0xDCB8,0xDCCB,0xDCCE,0xDCBE,0xB7D2,0xB0C5,0xDCC7,0xD0BE, 0xDCC1,0xBBA8,0xC65F,0xB7BC,0xDCCC,0xC660,0xC661,0xDCC6, 0xDCBF,0xC7DB,0xC662,0xC663,0xC664,0xD1BF,0xDCC0,0xC665, 0xC666,0xDCCA,0xC667,0xC668,0xDCD0,0xC669,0xC66A,0xCEAD, 0xDCC2,0xC66B,0xDCC3,0xDCC8,0xDCC9,0xB2D4,0xDCD1,0xCBD5, 0xC66C,0xD4B7,0xDCDB,0xDCDF,0xCCA6,0xDCE6,0xC66D,0xC3E7, 0xDCDC,0xC66E,0xC66F,0xBFC1,0xDCD9,0xC670,0xB0FA,0xB9B6, 0xDCE5,0xDCD3,0xC671,0xDCC4,0xDCD6,0xC8F4,0xBFE0,0xC672, 0xC673,0xC674,0xC675,0xC9BB,0xC676,0xC677,0xC678,0xB1BD, 0xC679,0xD3A2,0xC67A,0xC67B,0xDCDA,0xC67C,0xC67D,0xDCD5, 0xC67E,0xC6BB,0xC680,0xDCDE,0xC681,0xC682,0xC683,0xC684, 0xC685,0xD7C2,0xC3AF,0xB7B6,0xC7D1,0xC3A9,0xDCE2,0xDCD8, 0xDCEB,0xDCD4,0xC686,0xC687,0xDCDD,0xC688,0xBEA5,0xDCD7, 0xC689,0xDCE0,0xC68A,0xC68B,0xDCE3,0xDCE4,0xC68C,0xDCF8, 0xC68D,0xC68E,0xDCE1,0xDDA2,0xDCE7,0xC68F,0xC690,0xC691, 0xC692,0xC693,0xC694,0xC695,0xC696,0xC697,0xC698,0xBCEB, 0xB4C4,0xC699,0xC69A,0xC3A3,0xB2E7,0xDCFA,0xC69B,0xDCF2, 0xC69C,0xDCEF,0xC69D,0xDCFC,0xDCEE,0xD2F0,0xB2E8,0xC69E, 0xC8D7,0xC8E3,0xDCFB,0xC69F,0xDCED,0xC6A0,0xC740,0xC741, 0xDCF7,0xC742,0xC743,0xDCF5,0xC744,0xC745,0xBEA3,0xDCF4, 0xC746,0xB2DD,0xC747,0xC748,0xC749,0xC74A,0xC74B,0xDCF3, 0xBCF6,0xDCE8,0xBBC4,0xC74C,0xC0F3,0xC74D,0xC74E,0xC74F, 0xC750,0xC751,0xBCD4,0xDCE9,0xDCEA,0xC752,0xDCF1,0xDCF6, 0xDCF9,0xB5B4,0xC753,0xC8D9,0xBBE7,0xDCFE,0xDCFD,0xD3AB, 0xDDA1,0xDDA3,0xDDA5,0xD2F1,0xDDA4,0xDDA6,0xDDA7,0xD2A9, 0xC754,0xC755,0xC756,0xC757,0xC758,0xC759,0xC75A,0xBAC9, 0xDDA9,0xC75B,0xC75C,0xDDB6,0xDDB1,0xDDB4,0xC75D,0xC75E, 0xC75F,0xC760,0xC761,0xC762,0xC763,0xDDB0,0xC6CE,0xC764, 0xC765,0xC0F2,0xC766,0xC767,0xC768,0xC769,0xC9AF,0xC76A, 0xC76B,0xC76C,0xDCEC,0xDDAE,0xC76D,0xC76E,0xC76F,0xC770, 0xDDB7,0xC771,0xC772,0xDCF0,0xDDAF,0xC773,0xDDB8,0xC774, 0xDDAC,0xC775,0xC776,0xC777,0xC778,0xC779,0xC77A,0xC77B, 0xDDB9,0xDDB3,0xDDAD,0xC4AA,0xC77C,0xC77D,0xC77E,0xC780, 0xDDA8,0xC0B3,0xC1AB,0xDDAA,0xDDAB,0xC781,0xDDB2,0xBBF1, 0xDDB5,0xD3A8,0xDDBA,0xC782,0xDDBB,0xC3A7,0xC783,0xC784, 0xDDD2,0xDDBC,0xC785,0xC786,0xC787,0xDDD1,0xC788,0xB9BD, 0xC789,0xC78A,0xBED5,0xC78B,0xBEFA,0xC78C,0xC78D,0xBACA, 0xC78E,0xC78F,0xC790,0xC791,0xDDCA,0xC792,0xDDC5,0xC793, 0xDDBF,0xC794,0xC795,0xC796,0xB2CB,0xDDC3,0xC797,0xDDCB, 0xB2A4,0xDDD5,0xC798,0xC799,0xC79A,0xDDBE,0xC79B,0xC79C, 0xC79D,0xC6D0,0xDDD0,0xC79E,0xC79F,0xC7A0,0xC840,0xC841, 0xDDD4,0xC1E2,0xB7C6,0xC842,0xC843,0xC844,0xC845,0xC846, 0xDDCE,0xDDCF,0xC847,0xC848,0xC849,0xDDC4,0xC84A,0xC84B, 0xC84C,0xDDBD,0xC84D,0xDDCD,0xCCD1,0xC84E,0xDDC9,0xC84F, 0xC850,0xC851,0xC852,0xDDC2,0xC3C8,0xC6BC,0xCEAE,0xDDCC, 0xC853,0xDDC8,0xC854,0xC855,0xC856,0xC857,0xC858,0xC859, 0xDDC1,0xC85A,0xC85B,0xC85C,0xDDC6,0xC2DC,0xC85D,0xC85E, 0xC85F,0xC860,0xC861,0xC862,0xD3A9,0xD3AA,0xDDD3,0xCFF4, 0xC8F8,0xC863,0xC864,0xC865,0xC866,0xC867,0xC868,0xC869, 0xC86A,0xDDE6,0xC86B,0xC86C,0xC86D,0xC86E,0xC86F,0xC870, 0xDDC7,0xC871,0xC872,0xC873,0xDDE0,0xC2E4,0xC874,0xC875, 0xC876,0xC877,0xC878,0xC879,0xC87A,0xC87B,0xDDE1,0xC87C, 0xC87D,0xC87E,0xC880,0xC881,0xC882,0xC883,0xC884,0xC885, 0xC886,0xDDD7,0xC887,0xC888,0xC889,0xC88A,0xC88B,0xD6F8, 0xC88C,0xDDD9,0xDDD8,0xB8F0,0xDDD6,0xC88D,0xC88E,0xC88F, 0xC890,0xC6CF,0xC891,0xB6AD,0xC892,0xC893,0xC894,0xC895, 0xC896,0xDDE2,0xC897,0xBAF9,0xD4E1,0xDDE7,0xC898,0xC899, 0xC89A,0xB4D0,0xC89B,0xDDDA,0xC89C,0xBFFB,0xDDE3,0xC89D, 0xDDDF,0xC89E,0xDDDD,0xC89F,0xC8A0,0xC940,0xC941,0xC942, 0xC943,0xC944,0xB5D9,0xC945,0xC946,0xC947,0xC948,0xDDDB, 0xDDDC,0xDDDE,0xC949,0xBDAF,0xDDE4,0xC94A,0xDDE5,0xC94B, 0xC94C,0xC94D,0xC94E,0xC94F,0xC950,0xC951,0xC952,0xDDF5, 0xC953,0xC3C9,0xC954,0xC955,0xCBE2,0xC956,0xC957,0xC958, 0xC959,0xDDF2,0xC95A,0xC95B,0xC95C,0xC95D,0xC95E,0xC95F, 0xC960,0xC961,0xC962,0xC963,0xC964,0xC965,0xC966,0xD8E1, 0xC967,0xC968,0xC6D1,0xC969,0xDDF4,0xC96A,0xC96B,0xC96C, 0xD5F4,0xDDF3,0xDDF0,0xC96D,0xC96E,0xDDEC,0xC96F,0xDDEF, 0xC970,0xDDE8,0xC971,0xC972,0xD0EE,0xC973,0xC974,0xC975, 0xC976,0xC8D8,0xDDEE,0xC977,0xC978,0xDDE9,0xC979,0xC97A, 0xDDEA,0xCBF2,0xC97B,0xDDED,0xC97C,0xC97D,0xB1CD,0xC97E, 0xC980,0xC981,0xC982,0xC983,0xC984,0xC0B6,0xC985,0xBCBB, 0xDDF1,0xC986,0xC987,0xDDF7,0xC988,0xDDF6,0xDDEB,0xC989, 0xC98A,0xC98B,0xC98C,0xC98D,0xC5EE,0xC98E,0xC98F,0xC990, 0xDDFB,0xC991,0xC992,0xC993,0xC994,0xC995,0xC996,0xC997, 0xC998,0xC999,0xC99A,0xC99B,0xDEA4,0xC99C,0xC99D,0xDEA3, 0xC99E,0xC99F,0xC9A0,0xCA40,0xCA41,0xCA42,0xCA43,0xCA44, 0xCA45,0xCA46,0xCA47,0xCA48,0xDDF8,0xCA49,0xCA4A,0xCA4B, 0xCA4C,0xC3EF,0xCA4D,0xC2FB,0xCA4E,0xCA4F,0xCA50,0xD5E1, 0xCA51,0xCA52,0xCEB5,0xCA53,0xCA54,0xCA55,0xCA56,0xDDFD, 0xCA57,0xB2CC,0xCA58,0xCA59,0xCA5A,0xCA5B,0xCA5C,0xCA5D, 0xCA5E,0xCA5F,0xCA60,0xC4E8,0xCADF,0xCA61,0xCA62,0xCA63, 0xCA64,0xCA65,0xCA66,0xCA67,0xCA68,0xCA69,0xCA6A,0xC7BE, 0xDDFA,0xDDFC,0xDDFE,0xDEA2,0xB0AA,0xB1CE,0xCA6B,0xCA6C, 0xCA6D,0xCA6E,0xCA6F,0xDEAC,0xCA70,0xCA71,0xCA72,0xCA73, 0xDEA6,0xBDB6,0xC8EF,0xCA74,0xCA75,0xCA76,0xCA77,0xCA78, 0xCA79,0xCA7A,0xCA7B,0xCA7C,0xCA7D,0xCA7E,0xDEA1,0xCA80, 0xCA81,0xDEA5,0xCA82,0xCA83,0xCA84,0xCA85,0xDEA9,0xCA86, 0xCA87,0xCA88,0xCA89,0xCA8A,0xDEA8,0xCA8B,0xCA8C,0xCA8D, 0xDEA7,0xCA8E,0xCA8F,0xCA90,0xCA91,0xCA92,0xCA93,0xCA94, 0xCA95,0xCA96,0xDEAD,0xCA97,0xD4CC,0xCA98,0xCA99,0xCA9A, 0xCA9B,0xDEB3,0xDEAA,0xDEAE,0xCA9C,0xCA9D,0xC0D9,0xCA9E, 0xCA9F,0xCAA0,0xCB40,0xCB41,0xB1A1,0xDEB6,0xCB42,0xDEB1, 0xCB43,0xCB44,0xCB45,0xCB46,0xCB47,0xCB48,0xCB49,0xDEB2, 0xCB4A,0xCB4B,0xCB4C,0xCB4D,0xCB4E,0xCB4F,0xCB50,0xCB51, 0xCB52,0xCB53,0xCB54,0xD1A6,0xDEB5,0xCB55,0xCB56,0xCB57, 0xCB58,0xCB59,0xCB5A,0xCB5B,0xDEAF,0xCB5C,0xCB5D,0xCB5E, 0xDEB0,0xCB5F,0xD0BD,0xCB60,0xCB61,0xCB62,0xDEB4,0xCAED, 0xDEB9,0xCB63,0xCB64,0xCB65,0xCB66,0xCB67,0xCB68,0xDEB8, 0xCB69,0xDEB7,0xCB6A,0xCB6B,0xCB6C,0xCB6D,0xCB6E,0xCB6F, 0xCB70,0xDEBB,0xCB71,0xCB72,0xCB73,0xCB74,0xCB75,0xCB76, 0xCB77,0xBDE5,0xCB78,0xCB79,0xCB7A,0xCB7B,0xCB7C,0xB2D8, 0xC3EA,0xCB7D,0xCB7E,0xDEBA,0xCB80,0xC5BA,0xCB81,0xCB82, 0xCB83,0xCB84,0xCB85,0xCB86,0xDEBC,0xCB87,0xCB88,0xCB89, 0xCB8A,0xCB8B,0xCB8C,0xCB8D,0xCCD9,0xCB8E,0xCB8F,0xCB90, 0xCB91,0xB7AA,0xCB92,0xCB93,0xCB94,0xCB95,0xCB96,0xCB97, 0xCB98,0xCB99,0xCB9A,0xCB9B,0xCB9C,0xCB9D,0xCB9E,0xCB9F, 0xCBA0,0xCC40,0xCC41,0xD4E5,0xCC42,0xCC43,0xCC44,0xDEBD, 0xCC45,0xCC46,0xCC47,0xCC48,0xCC49,0xDEBF,0xCC4A,0xCC4B, 0xCC4C,0xCC4D,0xCC4E,0xCC4F,0xCC50,0xCC51,0xCC52,0xCC53, 0xCC54,0xC4A2,0xCC55,0xCC56,0xCC57,0xCC58,0xDEC1,0xCC59, 0xCC5A,0xCC5B,0xCC5C,0xCC5D,0xCC5E,0xCC5F,0xCC60,0xCC61, 0xCC62,0xCC63,0xCC64,0xCC65,0xCC66,0xCC67,0xCC68,0xDEBE, 0xCC69,0xDEC0,0xCC6A,0xCC6B,0xCC6C,0xCC6D,0xCC6E,0xCC6F, 0xCC70,0xCC71,0xCC72,0xCC73,0xCC74,0xCC75,0xCC76,0xCC77, 0xD5BA,0xCC78,0xCC79,0xCC7A,0xDEC2,0xCC7B,0xCC7C,0xCC7D, 0xCC7E,0xCC80,0xCC81,0xCC82,0xCC83,0xCC84,0xCC85,0xCC86, 0xCC87,0xCC88,0xCC89,0xCC8A,0xCC8B,0xF2AE,0xBBA2,0xC2B2, 0xC5B0,0xC2C7,0xCC8C,0xCC8D,0xF2AF,0xCC8E,0xCC8F,0xCC90, 0xCC91,0xCC92,0xD0E9,0xCC93,0xCC94,0xCC95,0xD3DD,0xCC96, 0xCC97,0xCC98,0xEBBD,0xCC99,0xCC9A,0xCC9B,0xCC9C,0xCC9D, 0xCC9E,0xCC9F,0xCCA0,0xB3E6,0xF2B0,0xCD40,0xF2B1,0xCD41, 0xCD42,0xCAAD,0xCD43,0xCD44,0xCD45,0xCD46,0xCD47,0xCD48, 0xCD49,0xBAE7,0xF2B3,0xF2B5,0xF2B4,0xCBE4,0xCFBA,0xF2B2, 0xCAB4,0xD2CF,0xC2EC,0xCD4A,0xCD4B,0xCD4C,0xCD4D,0xCD4E, 0xCD4F,0xCD50,0xCEC3,0xF2B8,0xB0F6,0xF2B7,0xCD51,0xCD52, 0xCD53,0xCD54,0xCD55,0xF2BE,0xCD56,0xB2CF,0xCD57,0xCD58, 0xCD59,0xCD5A,0xCD5B,0xCD5C,0xD1C1,0xF2BA,0xCD5D,0xCD5E, 0xCD5F,0xCD60,0xCD61,0xF2BC,0xD4E9,0xCD62,0xCD63,0xF2BB, 0xF2B6,0xF2BF,0xF2BD,0xCD64,0xF2B9,0xCD65,0xCD66,0xF2C7, 0xF2C4,0xF2C6,0xCD67,0xCD68,0xF2CA,0xF2C2,0xF2C0,0xCD69, 0xCD6A,0xCD6B,0xF2C5,0xCD6C,0xCD6D,0xCD6E,0xCD6F,0xCD70, 0xD6FB,0xCD71,0xCD72,0xCD73,0xF2C1,0xCD74,0xC7F9,0xC9DF, 0xCD75,0xF2C8,0xB9C6,0xB5B0,0xCD76,0xCD77,0xF2C3,0xF2C9, 0xF2D0,0xF2D6,0xCD78,0xCD79,0xBBD7,0xCD7A,0xCD7B,0xCD7C, 0xF2D5,0xCDDC,0xCD7D,0xD6EB,0xCD7E,0xCD80,0xF2D2,0xF2D4, 0xCD81,0xCD82,0xCD83,0xCD84,0xB8F2,0xCD85,0xCD86,0xCD87, 0xCD88,0xF2CB,0xCD89,0xCD8A,0xCD8B,0xF2CE,0xC2F9,0xCD8C, 0xD5DD,0xF2CC,0xF2CD,0xF2CF,0xF2D3,0xCD8D,0xCD8E,0xCD8F, 0xF2D9,0xD3BC,0xCD90,0xCD91,0xCD92,0xCD93,0xB6EA,0xCD94, 0xCAF1,0xCD95,0xB7E4,0xF2D7,0xCD96,0xCD97,0xCD98,0xF2D8, 0xF2DA,0xF2DD,0xF2DB,0xCD99,0xCD9A,0xF2DC,0xCD9B,0xCD9C, 0xCD9D,0xCD9E,0xD1D1,0xF2D1,0xCD9F,0xCDC9,0xCDA0,0xCECF, 0xD6A9,0xCE40,0xF2E3,0xCE41,0xC3DB,0xCE42,0xF2E0,0xCE43, 0xCE44,0xC0AF,0xF2EC,0xF2DE,0xCE45,0xF2E1,0xCE46,0xCE47, 0xCE48,0xF2E8,0xCE49,0xCE4A,0xCE4B,0xCE4C,0xF2E2,0xCE4D, 0xCE4E,0xF2E7,0xCE4F,0xCE50,0xF2E6,0xCE51,0xCE52,0xF2E9, 0xCE53,0xCE54,0xCE55,0xF2DF,0xCE56,0xCE57,0xF2E4,0xF2EA, 0xCE58,0xCE59,0xCE5A,0xCE5B,0xCE5C,0xCE5D,0xCE5E,0xD3AC, 0xF2E5,0xB2F5,0xCE5F,0xCE60,0xF2F2,0xCE61,0xD0AB,0xCE62, 0xCE63,0xCE64,0xCE65,0xF2F5,0xCE66,0xCE67,0xCE68,0xBBC8, 0xCE69,0xF2F9,0xCE6A,0xCE6B,0xCE6C,0xCE6D,0xCE6E,0xCE6F, 0xF2F0,0xCE70,0xCE71,0xF2F6,0xF2F8,0xF2FA,0xCE72,0xCE73, 0xCE74,0xCE75,0xCE76,0xCE77,0xCE78,0xCE79,0xF2F3,0xCE7A, 0xF2F1,0xCE7B,0xCE7C,0xCE7D,0xBAFB,0xCE7E,0xB5FB,0xCE80, 0xCE81,0xCE82,0xCE83,0xF2EF,0xF2F7,0xF2ED,0xF2EE,0xCE84, 0xCE85,0xCE86,0xF2EB,0xF3A6,0xCE87,0xF3A3,0xCE88,0xCE89, 0xF3A2,0xCE8A,0xCE8B,0xF2F4,0xCE8C,0xC8DA,0xCE8D,0xCE8E, 0xCE8F,0xCE90,0xCE91,0xF2FB,0xCE92,0xCE93,0xCE94,0xF3A5, 0xCE95,0xCE96,0xCE97,0xCE98,0xCE99,0xCE9A,0xCE9B,0xC3F8, 0xCE9C,0xCE9D,0xCE9E,0xCE9F,0xCEA0,0xCF40,0xCF41,0xCF42, 0xF2FD,0xCF43,0xCF44,0xF3A7,0xF3A9,0xF3A4,0xCF45,0xF2FC, 0xCF46,0xCF47,0xCF48,0xF3AB,0xCF49,0xF3AA,0xCF4A,0xCF4B, 0xCF4C,0xCF4D,0xC2DD,0xCF4E,0xCF4F,0xF3AE,0xCF50,0xCF51, 0xF3B0,0xCF52,0xCF53,0xCF54,0xCF55,0xCF56,0xF3A1,0xCF57, 0xCF58,0xCF59,0xF3B1,0xF3AC,0xCF5A,0xCF5B,0xCF5C,0xCF5D, 0xCF5E,0xF3AF,0xF2FE,0xF3AD,0xCF5F,0xCF60,0xCF61,0xCF62, 0xCF63,0xCF64,0xCF65,0xF3B2,0xCF66,0xCF67,0xCF68,0xCF69, 0xF3B4,0xCF6A,0xCF6B,0xCF6C,0xCF6D,0xF3A8,0xCF6E,0xCF6F, 0xCF70,0xCF71,0xF3B3,0xCF72,0xCF73,0xCF74,0xF3B5,0xCF75, 0xCF76,0xCF77,0xCF78,0xCF79,0xCF7A,0xCF7B,0xCF7C,0xCF7D, 0xCF7E,0xD0B7,0xCF80,0xCF81,0xCF82,0xCF83,0xF3B8,0xCF84, 0xCF85,0xCF86,0xCF87,0xD9F9,0xCF88,0xCF89,0xCF8A,0xCF8B, 0xCF8C,0xCF8D,0xF3B9,0xCF8E,0xCF8F,0xCF90,0xCF91,0xCF92, 0xCF93,0xCF94,0xCF95,0xF3B7,0xCF96,0xC8E4,0xF3B6,0xCF97, 0xCF98,0xCF99,0xCF9A,0xF3BA,0xCF9B,0xCF9C,0xCF9D,0xCF9E, 0xCF9F,0xF3BB,0xB4C0,0xCFA0,0xD040,0xD041,0xD042,0xD043, 0xD044,0xD045,0xD046,0xD047,0xD048,0xD049,0xD04A,0xD04B, 0xD04C,0xD04D,0xEEC3,0xD04E,0xD04F,0xD050,0xD051,0xD052, 0xD053,0xF3BC,0xD054,0xD055,0xF3BD,0xD056,0xD057,0xD058, 0xD1AA,0xD059,0xD05A,0xD05B,0xF4AC,0xD0C6,0xD05C,0xD05D, 0xD05E,0xD05F,0xD060,0xD061,0xD0D0,0xD1DC,0xD062,0xD063, 0xD064,0xD065,0xD066,0xD067,0xCFCE,0xD068,0xD069,0xBDD6, 0xD06A,0xD1C3,0xD06B,0xD06C,0xD06D,0xD06E,0xD06F,0xD070, 0xD071,0xBAE2,0xE1E9,0xD2C2,0xF1C2,0xB2B9,0xD072,0xD073, 0xB1ED,0xF1C3,0xD074,0xC9C0,0xB3C4,0xD075,0xD9F2,0xD076, 0xCBA5,0xD077,0xF1C4,0xD078,0xD079,0xD07A,0xD07B,0xD6D4, 0xD07C,0xD07D,0xD07E,0xD080,0xD081,0xF1C5,0xF4C0,0xF1C6, 0xD082,0xD4AC,0xF1C7,0xD083,0xB0C0,0xF4C1,0xD084,0xD085, 0xF4C2,0xD086,0xD087,0xB4FC,0xD088,0xC5DB,0xD089,0xD08A, 0xD08B,0xD08C,0xCCBB,0xD08D,0xD08E,0xD08F,0xD0E4,0xD090, 0xD091,0xD092,0xD093,0xD094,0xCDE0,0xD095,0xD096,0xD097, 0xD098,0xD099,0xF1C8,0xD09A,0xD9F3,0xD09B,0xD09C,0xD09D, 0xD09E,0xD09F,0xD0A0,0xB1BB,0xD140,0xCFAE,0xD141,0xD142, 0xD143,0xB8A4,0xD144,0xD145,0xD146,0xD147,0xD148,0xF1CA, 0xD149,0xD14A,0xD14B,0xD14C,0xF1CB,0xD14D,0xD14E,0xD14F, 0xD150,0xB2C3,0xC1D1,0xD151,0xD152,0xD7B0,0xF1C9,0xD153, 0xD154,0xF1CC,0xD155,0xD156,0xD157,0xD158,0xF1CE,0xD159, 0xD15A,0xD15B,0xD9F6,0xD15C,0xD2E1,0xD4A3,0xD15D,0xD15E, 0xF4C3,0xC8B9,0xD15F,0xD160,0xD161,0xD162,0xD163,0xF4C4, 0xD164,0xD165,0xF1CD,0xF1CF,0xBFE3,0xF1D0,0xD166,0xD167, 0xF1D4,0xD168,0xD169,0xD16A,0xD16B,0xD16C,0xD16D,0xD16E, 0xF1D6,0xF1D1,0xD16F,0xC9D1,0xC5E1,0xD170,0xD171,0xD172, 0xC2E3,0xB9FC,0xD173,0xD174,0xF1D3,0xD175,0xF1D5,0xD176, 0xD177,0xD178,0xB9D3,0xD179,0xD17A,0xD17B,0xD17C,0xD17D, 0xD17E,0xD180,0xF1DB,0xD181,0xD182,0xD183,0xD184,0xD185, 0xBAD6,0xD186,0xB0FD,0xF1D9,0xD187,0xD188,0xD189,0xD18A, 0xD18B,0xF1D8,0xF1D2,0xF1DA,0xD18C,0xD18D,0xD18E,0xD18F, 0xD190,0xF1D7,0xD191,0xD192,0xD193,0xC8EC,0xD194,0xD195, 0xD196,0xD197,0xCDCA,0xF1DD,0xD198,0xD199,0xD19A,0xD19B, 0xE5BD,0xD19C,0xD19D,0xD19E,0xF1DC,0xD19F,0xF1DE,0xD1A0, 0xD240,0xD241,0xD242,0xD243,0xD244,0xD245,0xD246,0xD247, 0xD248,0xF1DF,0xD249,0xD24A,0xCFE5,0xD24B,0xD24C,0xD24D, 0xD24E,0xD24F,0xD250,0xD251,0xD252,0xD253,0xD254,0xD255, 0xD256,0xD257,0xD258,0xD259,0xD25A,0xD25B,0xD25C,0xD25D, 0xD25E,0xD25F,0xD260,0xD261,0xD262,0xD263,0xF4C5,0xBDF3, 0xD264,0xD265,0xD266,0xD267,0xD268,0xD269,0xF1E0,0xD26A, 0xD26B,0xD26C,0xD26D,0xD26E,0xD26F,0xD270,0xD271,0xD272, 0xD273,0xD274,0xD275,0xD276,0xD277,0xD278,0xD279,0xD27A, 0xD27B,0xD27C,0xD27D,0xF1E1,0xD27E,0xD280,0xD281,0xCEF7, 0xD282,0xD2AA,0xD283,0xF1FB,0xD284,0xD285,0xB8B2,0xD286, 0xD287,0xD288,0xD289,0xD28A,0xD28B,0xD28C,0xD28D,0xD28E, 0xD28F,0xD290,0xD291,0xD292,0xD293,0xD294,0xD295,0xD296, 0xD297,0xD298,0xD299,0xD29A,0xD29B,0xD29C,0xD29D,0xD29E, 0xD29F,0xD2A0,0xD340,0xD341,0xD342,0xD343,0xD344,0xD345, 0xD346,0xD347,0xD348,0xD349,0xD34A,0xD34B,0xD34C,0xD34D, 0xD34E,0xD34F,0xD350,0xD351,0xD352,0xD353,0xD354,0xD355, 0xD356,0xD357,0xD358,0xD359,0xD35A,0xD35B,0xD35C,0xD35D, 0xD35E,0xBCFB,0xB9DB,0xD35F,0xB9E6,0xC3D9,0xCAD3,0xEAE8, 0xC0C0,0xBEF5,0xEAE9,0xEAEA,0xEAEB,0xD360,0xEAEC,0xEAED, 0xEAEE,0xEAEF,0xBDC7,0xD361,0xD362,0xD363,0xF5FB,0xD364, 0xD365,0xD366,0xF5FD,0xD367,0xF5FE,0xD368,0xF5FC,0xD369, 0xD36A,0xD36B,0xD36C,0xBDE2,0xD36D,0xF6A1,0xB4A5,0xD36E, 0xD36F,0xD370,0xD371,0xF6A2,0xD372,0xD373,0xD374,0xF6A3, 0xD375,0xD376,0xD377,0xECB2,0xD378,0xD379,0xD37A,0xD37B, 0xD37C,0xD37D,0xD37E,0xD380,0xD381,0xD382,0xD383,0xD384, 0xD1D4,0xD385,0xD386,0xD387,0xD388,0xD389,0xD38A,0xD9EA, 0xD38B,0xD38C,0xD38D,0xD38E,0xD38F,0xD390,0xD391,0xD392, 0xD393,0xD394,0xD395,0xD396,0xD397,0xD398,0xD399,0xD39A, 0xD39B,0xD39C,0xD39D,0xD39E,0xD39F,0xD3A0,0xD440,0xD441, 0xD442,0xD443,0xD444,0xD445,0xD446,0xD447,0xD448,0xD449, 0xD44A,0xD44B,0xD44C,0xD44D,0xD44E,0xD44F,0xD450,0xD451, 0xD452,0xD453,0xD454,0xD455,0xD456,0xD457,0xD458,0xD459, 0xD45A,0xD45B,0xD45C,0xD45D,0xD45E,0xD45F,0xF6A4,0xD460, 0xD461,0xD462,0xD463,0xD464,0xD465,0xD466,0xD467,0xD468, 0xEEBA,0xD469,0xD46A,0xD46B,0xD46C,0xD46D,0xD46E,0xD46F, 0xD470,0xD471,0xD472,0xD473,0xD474,0xD475,0xD476,0xD477, 0xD478,0xD479,0xD47A,0xD47B,0xD47C,0xD47D,0xD47E,0xD480, 0xD481,0xD482,0xD483,0xD484,0xD485,0xD486,0xD487,0xD488, 0xD489,0xD48A,0xD48B,0xD48C,0xD48D,0xD48E,0xD48F,0xD490, 0xD491,0xD492,0xD493,0xD494,0xD495,0xD496,0xD497,0xD498, 0xD499,0xD5B2,0xD49A,0xD49B,0xD49C,0xD49D,0xD49E,0xD49F, 0xD4A0,0xD540,0xD541,0xD542,0xD543,0xD544,0xD545,0xD546, 0xD547,0xD3FE,0xCCDC,0xD548,0xD549,0xD54A,0xD54B,0xD54C, 0xD54D,0xD54E,0xD54F,0xCAC4,0xD550,0xD551,0xD552,0xD553, 0xD554,0xD555,0xD556,0xD557,0xD558,0xD559,0xD55A,0xD55B, 0xD55C,0xD55D,0xD55E,0xD55F,0xD560,0xD561,0xD562,0xD563, 0xD564,0xD565,0xD566,0xD567,0xD568,0xD569,0xD56A,0xD56B, 0xD56C,0xD56D,0xD56E,0xD56F,0xD570,0xD571,0xD572,0xD573, 0xD574,0xD575,0xD576,0xD577,0xD578,0xD579,0xD57A,0xD57B, 0xD57C,0xD57D,0xD57E,0xD580,0xD581,0xD582,0xD583,0xD584, 0xD585,0xD586,0xD587,0xD588,0xD589,0xD58A,0xD58B,0xD58C, 0xD58D,0xD58E,0xD58F,0xD590,0xD591,0xD592,0xD593,0xD594, 0xD595,0xD596,0xD597,0xD598,0xD599,0xD59A,0xD59B,0xD59C, 0xD59D,0xD59E,0xD59F,0xD5A0,0xD640,0xD641,0xD642,0xD643, 0xD644,0xD645,0xD646,0xD647,0xD648,0xD649,0xD64A,0xD64B, 0xD64C,0xD64D,0xD64E,0xD64F,0xD650,0xD651,0xD652,0xD653, 0xD654,0xD655,0xD656,0xD657,0xD658,0xD659,0xD65A,0xD65B, 0xD65C,0xD65D,0xD65E,0xD65F,0xD660,0xD661,0xD662,0xE5C0, 0xD663,0xD664,0xD665,0xD666,0xD667,0xD668,0xD669,0xD66A, 0xD66B,0xD66C,0xD66D,0xD66E,0xD66F,0xD670,0xD671,0xD672, 0xD673,0xD674,0xD675,0xD676,0xD677,0xD678,0xD679,0xD67A, 0xD67B,0xD67C,0xD67D,0xD67E,0xD680,0xD681,0xF6A5,0xD682, 0xD683,0xD684,0xD685,0xD686,0xD687,0xD688,0xD689,0xD68A, 0xD68B,0xD68C,0xD68D,0xD68E,0xD68F,0xD690,0xD691,0xD692, 0xD693,0xD694,0xD695,0xD696,0xD697,0xD698,0xD699,0xD69A, 0xD69B,0xD69C,0xD69D,0xD69E,0xD69F,0xD6A0,0xD740,0xD741, 0xD742,0xD743,0xD744,0xD745,0xD746,0xD747,0xD748,0xD749, 0xD74A,0xD74B,0xD74C,0xD74D,0xD74E,0xD74F,0xD750,0xD751, 0xD752,0xD753,0xD754,0xD755,0xD756,0xD757,0xD758,0xD759, 0xD75A,0xD75B,0xD75C,0xD75D,0xD75E,0xD75F,0xBEAF,0xD760, 0xD761,0xD762,0xD763,0xD764,0xC6A9,0xD765,0xD766,0xD767, 0xD768,0xD769,0xD76A,0xD76B,0xD76C,0xD76D,0xD76E,0xD76F, 0xD770,0xD771,0xD772,0xD773,0xD774,0xD775,0xD776,0xD777, 0xD778,0xD779,0xD77A,0xD77B,0xD77C,0xD77D,0xD77E,0xD780, 0xD781,0xD782,0xD783,0xD784,0xD785,0xD786,0xD787,0xD788, 0xD789,0xD78A,0xD78B,0xD78C,0xD78D,0xD78E,0xD78F,0xD790, 0xD791,0xD792,0xD793,0xD794,0xD795,0xD796,0xD797,0xD798, 0xDAA5,0xBCC6,0xB6A9,0xB8BC,0xC8CF,0xBCA5,0xDAA6,0xDAA7, 0xCCD6,0xC8C3,0xDAA8,0xC6FD,0xD799,0xD1B5,0xD2E9,0xD1B6, 0xBCC7,0xD79A,0xBDB2,0xBBE4,0xDAA9,0xDAAA,0xD1C8,0xDAAB, 0xD0ED,0xB6EF,0xC2DB,0xD79B,0xCBCF,0xB7ED,0xC9E8,0xB7C3, 0xBEF7,0xD6A4,0xDAAC,0xDAAD,0xC6C0,0xD7E7,0xCAB6,0xD79C, 0xD5A9,0xCBDF,0xD5EF,0xDAAE,0xD6DF,0xB4CA,0xDAB0,0xDAAF, 0xD79D,0xD2EB,0xDAB1,0xDAB2,0xDAB3,0xCAD4,0xDAB4,0xCAAB, 0xDAB5,0xDAB6,0xB3CF,0xD6EF,0xDAB7,0xBBB0,0xB5AE,0xDAB8, 0xDAB9,0xB9EE,0xD1AF,0xD2E8,0xDABA,0xB8C3,0xCFEA,0xB2EF, 0xDABB,0xDABC,0xD79E,0xBDEB,0xCEDC,0xD3EF,0xDABD,0xCEF3, 0xDABE,0xD3D5,0xBBE5,0xDABF,0xCBB5,0xCBD0,0xDAC0,0xC7EB, 0xD6EE,0xDAC1,0xC5B5,0xB6C1,0xDAC2,0xB7CC,0xBFCE,0xDAC3, 0xDAC4,0xCBAD,0xDAC5,0xB5F7,0xDAC6,0xC1C2,0xD7BB,0xDAC7, 0xCCB8,0xD79F,0xD2EA,0xC4B1,0xDAC8,0xB5FD,0xBBD1,0xDAC9, 0xD0B3,0xDACA,0xDACB,0xCEBD,0xDACC,0xDACD,0xDACE,0xB2F7, 0xDAD1,0xDACF,0xD1E8,0xDAD0,0xC3D5,0xDAD2,0xD7A0,0xDAD3, 0xDAD4,0xDAD5,0xD0BB,0xD2A5,0xB0F9,0xDAD6,0xC7AB,0xDAD7, 0xBDF7,0xC3A1,0xDAD8,0xDAD9,0xC3FD,0xCCB7,0xDADA,0xDADB, 0xC0BE,0xC6D7,0xDADC,0xDADD,0xC7B4,0xDADE,0xDADF,0xB9C8, 0xD840,0xD841,0xD842,0xD843,0xD844,0xD845,0xD846,0xD847, 0xD848,0xBBED,0xD849,0xD84A,0xD84B,0xD84C,0xB6B9,0xF4F8, 0xD84D,0xF4F9,0xD84E,0xD84F,0xCDE3,0xD850,0xD851,0xD852, 0xD853,0xD854,0xD855,0xD856,0xD857,0xF5B9,0xD858,0xD859, 0xD85A,0xD85B,0xEBE0,0xD85C,0xD85D,0xD85E,0xD85F,0xD860, 0xD861,0xCFF3,0xBBBF,0xD862,0xD863,0xD864,0xD865,0xD866, 0xD867,0xD868,0xBAC0,0xD4A5,0xD869,0xD86A,0xD86B,0xD86C, 0xD86D,0xD86E,0xD86F,0xE1D9,0xD870,0xD871,0xD872,0xD873, 0xF5F4,0xB1AA,0xB2F2,0xD874,0xD875,0xD876,0xD877,0xD878, 0xD879,0xD87A,0xF5F5,0xD87B,0xD87C,0xF5F7,0xD87D,0xD87E, 0xD880,0xBAD1,0xF5F6,0xD881,0xC3B2,0xD882,0xD883,0xD884, 0xD885,0xD886,0xD887,0xD888,0xF5F9,0xD889,0xD88A,0xD88B, 0xF5F8,0xD88C,0xD88D,0xD88E,0xD88F,0xD890,0xD891,0xD892, 0xD893,0xD894,0xD895,0xD896,0xD897,0xD898,0xD899,0xD89A, 0xD89B,0xD89C,0xD89D,0xD89E,0xD89F,0xD8A0,0xD940,0xD941, 0xD942,0xD943,0xD944,0xD945,0xD946,0xD947,0xD948,0xD949, 0xD94A,0xD94B,0xD94C,0xD94D,0xD94E,0xD94F,0xD950,0xD951, 0xD952,0xD953,0xD954,0xD955,0xD956,0xD957,0xD958,0xD959, 0xD95A,0xD95B,0xD95C,0xD95D,0xD95E,0xD95F,0xD960,0xD961, 0xD962,0xD963,0xD964,0xD965,0xD966,0xD967,0xD968,0xD969, 0xD96A,0xD96B,0xD96C,0xD96D,0xD96E,0xD96F,0xD970,0xD971, 0xD972,0xD973,0xD974,0xD975,0xD976,0xD977,0xD978,0xD979, 0xD97A,0xD97B,0xD97C,0xD97D,0xD97E,0xD980,0xD981,0xD982, 0xD983,0xD984,0xD985,0xD986,0xD987,0xD988,0xD989,0xD98A, 0xD98B,0xD98C,0xD98D,0xD98E,0xD98F,0xD990,0xD991,0xD992 }; const static uint16 gbkEncoderInnerIndex5[]= { 0xD993,0xD994,0xD995,0xD996,0xD997,0xD998,0xD999,0xD99A, 0xD99B,0xD99C,0xD99D,0xD99E,0xD99F,0xD9A0,0xDA40,0xDA41, 0xDA42,0xDA43,0xDA44,0xDA45,0xDA46,0xDA47,0xDA48,0xDA49, 0xDA4A,0xDA4B,0xDA4C,0xDA4D,0xDA4E,0xB1B4,0xD5EA,0xB8BA, 0xDA4F,0xB9B1,0xB2C6,0xD4F0,0xCFCD,0xB0DC,0xD5CB,0xBBF5, 0xD6CA,0xB7B7,0xCCB0,0xC6B6,0xB1E1,0xB9BA,0xD6FC,0xB9E1, 0xB7A1,0xBCFA,0xEADA,0xEADB,0xCCF9,0xB9F3,0xEADC,0xB4FB, 0xC3B3,0xB7D1,0xBAD8,0xEADD,0xD4F4,0xEADE,0xBCD6,0xBBDF, 0xEADF,0xC1DE,0xC2B8,0xD4DF,0xD7CA,0xEAE0,0xEAE1,0xEAE4, 0xEAE2,0xEAE3,0xC9DE,0xB8B3,0xB6C4,0xEAE5,0xCAEA,0xC9CD, 0xB4CD,0xDA50,0xDA51,0xE2D9,0xC5E2,0xEAE6,0xC0B5,0xDA52, 0xD7B8,0xEAE7,0xD7AC,0xC8FC,0xD8D3,0xD8CD,0xD4DE,0xDA53, 0xD4F9,0xC9C4,0xD3AE,0xB8D3,0xB3E0,0xDA54,0xC9E2,0xF4F6, 0xDA55,0xDA56,0xDA57,0xBAD5,0xDA58,0xF4F7,0xDA59,0xDA5A, 0xD7DF,0xDA5B,0xDA5C,0xF4F1,0xB8B0,0xD5D4,0xB8CF,0xC6F0, 0xDA5D,0xDA5E,0xDA5F,0xDA60,0xDA61,0xDA62,0xDA63,0xDA64, 0xDA65,0xB3C3,0xDA66,0xDA67,0xF4F2,0xB3AC,0xDA68,0xDA69, 0xDA6A,0xDA6B,0xD4BD,0xC7F7,0xDA6C,0xDA6D,0xDA6E,0xDA6F, 0xDA70,0xF4F4,0xDA71,0xDA72,0xF4F3,0xDA73,0xDA74,0xDA75, 0xDA76,0xDA77,0xDA78,0xDA79,0xDA7A,0xDA7B,0xDA7C,0xCCCB, 0xDA7D,0xDA7E,0xDA80,0xC8A4,0xDA81,0xDA82,0xDA83,0xDA84, 0xDA85,0xDA86,0xDA87,0xDA88,0xDA89,0xDA8A,0xDA8B,0xDA8C, 0xDA8D,0xF4F5,0xDA8E,0xD7E3,0xC5BF,0xF5C0,0xDA8F,0xDA90, 0xF5BB,0xDA91,0xF5C3,0xDA92,0xF5C2,0xDA93,0xD6BA,0xF5C1, 0xDA94,0xDA95,0xDA96,0xD4BE,0xF5C4,0xDA97,0xF5CC,0xDA98, 0xDA99,0xDA9A,0xDA9B,0xB0CF,0xB5F8,0xDA9C,0xF5C9,0xF5CA, 0xDA9D,0xC5DC,0xDA9E,0xDA9F,0xDAA0,0xDB40,0xF5C5,0xF5C6, 0xDB41,0xDB42,0xF5C7,0xF5CB,0xDB43,0xBEE0,0xF5C8,0xB8FA, 0xDB44,0xDB45,0xDB46,0xF5D0,0xF5D3,0xDB47,0xDB48,0xDB49, 0xBFE7,0xDB4A,0xB9F2,0xF5BC,0xF5CD,0xDB4B,0xDB4C,0xC2B7, 0xDB4D,0xDB4E,0xDB4F,0xCCF8,0xDB50,0xBCF9,0xDB51,0xF5CE, 0xF5CF,0xF5D1,0xB6E5,0xF5D2,0xDB52,0xF5D5,0xDB53,0xDB54, 0xDB55,0xDB56,0xDB57,0xDB58,0xDB59,0xF5BD,0xDB5A,0xDB5B, 0xDB5C,0xF5D4,0xD3BB,0xDB5D,0xB3EC,0xDB5E,0xDB5F,0xCCA4, 0xDB60,0xDB61,0xDB62,0xDB63,0xF5D6,0xDB64,0xDB65,0xDB66, 0xDB67,0xDB68,0xDB69,0xDB6A,0xDB6B,0xF5D7,0xBEE1,0xF5D8, 0xDB6C,0xDB6D,0xCCDF,0xF5DB,0xDB6E,0xDB6F,0xDB70,0xDB71, 0xDB72,0xB2C8,0xD7D9,0xDB73,0xF5D9,0xDB74,0xF5DA,0xF5DC, 0xDB75,0xF5E2,0xDB76,0xDB77,0xDB78,0xF5E0,0xDB79,0xDB7A, 0xDB7B,0xF5DF,0xF5DD,0xDB7C,0xDB7D,0xF5E1,0xDB7E,0xDB80, 0xF5DE,0xF5E4,0xF5E5,0xDB81,0xCCE3,0xDB82,0xDB83,0xE5BF, 0xB5B8,0xF5E3,0xF5E8,0xCCA3,0xDB84,0xDB85,0xDB86,0xDB87, 0xDB88,0xF5E6,0xF5E7,0xDB89,0xDB8A,0xDB8B,0xDB8C,0xDB8D, 0xDB8E,0xF5BE,0xDB8F,0xDB90,0xDB91,0xDB92,0xDB93,0xDB94, 0xDB95,0xDB96,0xDB97,0xDB98,0xDB99,0xDB9A,0xB1C4,0xDB9B, 0xDB9C,0xF5BF,0xDB9D,0xDB9E,0xB5C5,0xB2E4,0xDB9F,0xF5EC, 0xF5E9,0xDBA0,0xB6D7,0xDC40,0xF5ED,0xDC41,0xF5EA,0xDC42, 0xDC43,0xDC44,0xDC45,0xDC46,0xF5EB,0xDC47,0xDC48,0xB4DA, 0xDC49,0xD4EA,0xDC4A,0xDC4B,0xDC4C,0xF5EE,0xDC4D,0xB3F9, 0xDC4E,0xDC4F,0xDC50,0xDC51,0xDC52,0xDC53,0xDC54,0xF5EF, 0xF5F1,0xDC55,0xDC56,0xDC57,0xF5F0,0xDC58,0xDC59,0xDC5A, 0xDC5B,0xDC5C,0xDC5D,0xDC5E,0xF5F2,0xDC5F,0xF5F3,0xDC60, 0xDC61,0xDC62,0xDC63,0xDC64,0xDC65,0xDC66,0xDC67,0xDC68, 0xDC69,0xDC6A,0xDC6B,0xC9ED,0xB9AA,0xDC6C,0xDC6D,0xC7FB, 0xDC6E,0xDC6F,0xB6E3,0xDC70,0xDC71,0xDC72,0xDC73,0xDC74, 0xDC75,0xDC76,0xCCC9,0xDC77,0xDC78,0xDC79,0xDC7A,0xDC7B, 0xDC7C,0xDC7D,0xDC7E,0xDC80,0xDC81,0xDC82,0xDC83,0xDC84, 0xDC85,0xDC86,0xDC87,0xDC88,0xDC89,0xDC8A,0xEAA6,0xDC8B, 0xDC8C,0xDC8D,0xDC8E,0xDC8F,0xDC90,0xDC91,0xDC92,0xDC93, 0xDC94,0xDC95,0xDC96,0xDC97,0xDC98,0xDC99,0xDC9A,0xDC9B, 0xDC9C,0xDC9D,0xDC9E,0xDC9F,0xDCA0,0xDD40,0xDD41,0xDD42, 0xDD43,0xDD44,0xDD45,0xDD46,0xDD47,0xDD48,0xDD49,0xDD4A, 0xDD4B,0xDD4C,0xDD4D,0xDD4E,0xDD4F,0xDD50,0xDD51,0xDD52, 0xDD53,0xDD54,0xDD55,0xDD56,0xDD57,0xDD58,0xDD59,0xDD5A, 0xDD5B,0xDD5C,0xDD5D,0xDD5E,0xDD5F,0xDD60,0xDD61,0xDD62, 0xDD63,0xDD64,0xDD65,0xDD66,0xDD67,0xDD68,0xDD69,0xDD6A, 0xDD6B,0xDD6C,0xDD6D,0xDD6E,0xDD6F,0xDD70,0xDD71,0xDD72, 0xDD73,0xDD74,0xDD75,0xDD76,0xDD77,0xDD78,0xDD79,0xDD7A, 0xDD7B,0xDD7C,0xDD7D,0xDD7E,0xDD80,0xDD81,0xDD82,0xDD83, 0xDD84,0xDD85,0xDD86,0xDD87,0xDD88,0xDD89,0xDD8A,0xDD8B, 0xDD8C,0xDD8D,0xDD8E,0xDD8F,0xDD90,0xDD91,0xDD92,0xDD93, 0xDD94,0xDD95,0xDD96,0xDD97,0xDD98,0xDD99,0xDD9A,0xDD9B, 0xDD9C,0xDD9D,0xDD9E,0xDD9F,0xDDA0,0xDE40,0xDE41,0xDE42, 0xDE43,0xDE44,0xDE45,0xDE46,0xDE47,0xDE48,0xDE49,0xDE4A, 0xDE4B,0xDE4C,0xDE4D,0xDE4E,0xDE4F,0xDE50,0xDE51,0xDE52, 0xDE53,0xDE54,0xDE55,0xDE56,0xDE57,0xDE58,0xDE59,0xDE5A, 0xDE5B,0xDE5C,0xDE5D,0xDE5E,0xDE5F,0xDE60,0xB3B5,0xD4FE, 0xB9EC,0xD0F9,0xDE61,0xE9ED,0xD7AA,0xE9EE,0xC2D6,0xC8ED, 0xBAE4,0xE9EF,0xE9F0,0xE9F1,0xD6E1,0xE9F2,0xE9F3,0xE9F5, 0xE9F4,0xE9F6,0xE9F7,0xC7E1,0xE9F8,0xD4D8,0xE9F9,0xBDCE, 0xDE62,0xE9FA,0xE9FB,0xBDCF,0xE9FC,0xB8A8,0xC1BE,0xE9FD, 0xB1B2,0xBBD4,0xB9F5,0xE9FE,0xDE63,0xEAA1,0xEAA2,0xEAA3, 0xB7F8,0xBCAD,0xDE64,0xCAE4,0xE0CE,0xD4AF,0xCFBD,0xD5B7, 0xEAA4,0xD5DE,0xEAA5,0xD0C1,0xB9BC,0xDE65,0xB4C7,0xB1D9, 0xDE66,0xDE67,0xDE68,0xC0B1,0xDE69,0xDE6A,0xDE6B,0xDE6C, 0xB1E6,0xB1E7,0xDE6D,0xB1E8,0xDE6E,0xDE6F,0xDE70,0xDE71, 0xB3BD,0xC8E8,0xDE72,0xDE73,0xDE74,0xDE75,0xE5C1,0xDE76, 0xDE77,0xB1DF,0xDE78,0xDE79,0xDE7A,0xC1C9,0xB4EF,0xDE7B, 0xDE7C,0xC7A8,0xD3D8,0xDE7D,0xC6F9,0xD1B8,0xDE7E,0xB9FD, 0xC2F5,0xDE80,0xDE81,0xDE82,0xDE83,0xDE84,0xD3AD,0xDE85, 0xD4CB,0xBDFC,0xDE86,0xE5C2,0xB7B5,0xE5C3,0xDE87,0xDE88, 0xBBB9,0xD5E2,0xDE89,0xBDF8,0xD4B6,0xCEA5,0xC1AC,0xB3D9, 0xDE8A,0xDE8B,0xCCF6,0xDE8C,0xE5C6,0xE5C4,0xE5C8,0xDE8D, 0xE5CA,0xE5C7,0xB5CF,0xC6C8,0xDE8E,0xB5FC,0xE5C5,0xDE8F, 0xCAF6,0xDE90,0xDE91,0xE5C9,0xDE92,0xDE93,0xDE94,0xC3D4, 0xB1C5,0xBCA3,0xDE95,0xDE96,0xDE97,0xD7B7,0xDE98,0xDE99, 0xCDCB,0xCBCD,0xCACA,0xCCD3,0xE5CC,0xE5CB,0xC4E6,0xDE9A, 0xDE9B,0xD1A1,0xD1B7,0xE5CD,0xDE9C,0xE5D0,0xDE9D,0xCDB8, 0xD6F0,0xE5CF,0xB5DD,0xDE9E,0xCDBE,0xDE9F,0xE5D1,0xB6BA, 0xDEA0,0xDF40,0xCDA8,0xB9E4,0xDF41,0xCAC5,0xB3D1,0xCBD9, 0xD4EC,0xE5D2,0xB7EA,0xDF42,0xDF43,0xDF44,0xE5CE,0xDF45, 0xDF46,0xDF47,0xDF48,0xDF49,0xDF4A,0xE5D5,0xB4FE,0xE5D6, 0xDF4B,0xDF4C,0xDF4D,0xDF4E,0xDF4F,0xE5D3,0xE5D4,0xDF50, 0xD2DD,0xDF51,0xDF52,0xC2DF,0xB1C6,0xDF53,0xD3E2,0xDF54, 0xDF55,0xB6DD,0xCBEC,0xDF56,0xE5D7,0xDF57,0xDF58,0xD3F6, 0xDF59,0xDF5A,0xDF5B,0xDF5C,0xDF5D,0xB1E9,0xDF5E,0xB6F4, 0xE5DA,0xE5D8,0xE5D9,0xB5C0,0xDF5F,0xDF60,0xDF61,0xD2C5, 0xE5DC,0xDF62,0xDF63,0xE5DE,0xDF64,0xDF65,0xDF66,0xDF67, 0xDF68,0xDF69,0xE5DD,0xC7B2,0xDF6A,0xD2A3,0xDF6B,0xDF6C, 0xE5DB,0xDF6D,0xDF6E,0xDF6F,0xDF70,0xD4E2,0xD5DA,0xDF71, 0xDF72,0xDF73,0xDF74,0xDF75,0xE5E0,0xD7F1,0xDF76,0xDF77, 0xDF78,0xDF79,0xDF7A,0xDF7B,0xDF7C,0xE5E1,0xDF7D,0xB1DC, 0xD1FB,0xDF7E,0xE5E2,0xE5E4,0xDF80,0xDF81,0xDF82,0xDF83, 0xE5E3,0xDF84,0xDF85,0xE5E5,0xDF86,0xDF87,0xDF88,0xDF89, 0xDF8A,0xD2D8,0xDF8B,0xB5CB,0xDF8C,0xE7DF,0xDF8D,0xDAF5, 0xDF8E,0xDAF8,0xDF8F,0xDAF6,0xDF90,0xDAF7,0xDF91,0xDF92, 0xDF93,0xDAFA,0xD0CF,0xC4C7,0xDF94,0xDF95,0xB0EE,0xDF96, 0xDF97,0xDF98,0xD0B0,0xDF99,0xDAF9,0xDF9A,0xD3CA,0xBAAA, 0xDBA2,0xC7F1,0xDF9B,0xDAFC,0xDAFB,0xC9DB,0xDAFD,0xDF9C, 0xDBA1,0xD7DE,0xDAFE,0xC1DA,0xDF9D,0xDF9E,0xDBA5,0xDF9F, 0xDFA0,0xD3F4,0xE040,0xE041,0xDBA7,0xDBA4,0xE042,0xDBA8, 0xE043,0xE044,0xBDBC,0xE045,0xE046,0xE047,0xC0C9,0xDBA3, 0xDBA6,0xD6A3,0xE048,0xDBA9,0xE049,0xE04A,0xE04B,0xDBAD, 0xE04C,0xE04D,0xE04E,0xDBAE,0xDBAC,0xBAC2,0xE04F,0xE050, 0xE051,0xBFA4,0xDBAB,0xE052,0xE053,0xE054,0xDBAA,0xD4C7, 0xB2BF,0xE055,0xE056,0xDBAF,0xE057,0xB9F9,0xE058,0xDBB0, 0xE059,0xE05A,0xE05B,0xE05C,0xB3BB,0xE05D,0xE05E,0xE05F, 0xB5A6,0xE060,0xE061,0xE062,0xE063,0xB6BC,0xDBB1,0xE064, 0xE065,0xE066,0xB6F5,0xE067,0xDBB2,0xE068,0xE069,0xE06A, 0xE06B,0xE06C,0xE06D,0xE06E,0xE06F,0xE070,0xE071,0xE072, 0xE073,0xE074,0xE075,0xE076,0xE077,0xE078,0xE079,0xE07A, 0xE07B,0xB1C9,0xE07C,0xE07D,0xE07E,0xE080,0xDBB4,0xE081, 0xE082,0xE083,0xDBB3,0xDBB5,0xE084,0xE085,0xE086,0xE087, 0xE088,0xE089,0xE08A,0xE08B,0xE08C,0xE08D,0xE08E,0xDBB7, 0xE08F,0xDBB6,0xE090,0xE091,0xE092,0xE093,0xE094,0xE095, 0xE096,0xDBB8,0xE097,0xE098,0xE099,0xE09A,0xE09B,0xE09C, 0xE09D,0xE09E,0xE09F,0xDBB9,0xE0A0,0xE140,0xDBBA,0xE141, 0xE142,0xD3CF,0xF4FA,0xC7F5,0xD7C3,0xC5E4,0xF4FC,0xF4FD, 0xF4FB,0xE143,0xBEC6,0xE144,0xE145,0xE146,0xE147,0xD0EF, 0xE148,0xE149,0xB7D3,0xE14A,0xE14B,0xD4CD,0xCCAA,0xE14C, 0xE14D,0xF5A2,0xF5A1,0xBAA8,0xF4FE,0xCBD6,0xE14E,0xE14F, 0xE150,0xF5A4,0xC0D2,0xE151,0xB3EA,0xE152,0xCDAA,0xF5A5, 0xF5A3,0xBDB4,0xF5A8,0xE153,0xF5A9,0xBDCD,0xC3B8,0xBFE1, 0xCBE1,0xF5AA,0xE154,0xE155,0xE156,0xF5A6,0xF5A7,0xC4F0, 0xE157,0xE158,0xE159,0xE15A,0xE15B,0xF5AC,0xE15C,0xB4BC, 0xE15D,0xD7ED,0xE15E,0xB4D7,0xF5AB,0xF5AE,0xE15F,0xE160, 0xF5AD,0xF5AF,0xD0D1,0xE161,0xE162,0xE163,0xE164,0xE165, 0xE166,0xE167,0xC3D1,0xC8A9,0xE168,0xE169,0xE16A,0xE16B, 0xE16C,0xE16D,0xF5B0,0xF5B1,0xE16E,0xE16F,0xE170,0xE171, 0xE172,0xE173,0xF5B2,0xE174,0xE175,0xF5B3,0xF5B4,0xF5B5, 0xE176,0xE177,0xE178,0xE179,0xF5B7,0xF5B6,0xE17A,0xE17B, 0xE17C,0xE17D,0xF5B8,0xE17E,0xE180,0xE181,0xE182,0xE183, 0xE184,0xE185,0xE186,0xE187,0xE188,0xE189,0xE18A,0xB2C9, 0xE18B,0xD3D4,0xCACD,0xE18C,0xC0EF,0xD6D8,0xD2B0,0xC1BF, 0xE18D,0xBDF0,0xE18E,0xE18F,0xE190,0xE191,0xE192,0xE193, 0xE194,0xE195,0xE196,0xE197,0xB8AA,0xE198,0xE199,0xE19A, 0xE19B,0xE19C,0xE19D,0xE19E,0xE19F,0xE1A0,0xE240,0xE241, 0xE242,0xE243,0xE244,0xE245,0xE246,0xE247,0xE248,0xE249, 0xE24A,0xE24B,0xE24C,0xE24D,0xE24E,0xE24F,0xE250,0xE251, 0xE252,0xE253,0xE254,0xE255,0xE256,0xE257,0xE258,0xE259, 0xE25A,0xE25B,0xE25C,0xE25D,0xE25E,0xE25F,0xE260,0xE261, 0xE262,0xE263,0xE264,0xE265,0xE266,0xE267,0xE268,0xE269, 0xE26A,0xE26B,0xE26C,0xE26D,0xE26E,0xE26F,0xE270,0xE271, 0xE272,0xE273,0xE274,0xE275,0xE276,0xE277,0xE278,0xE279, 0xE27A,0xE27B,0xE27C,0xE27D,0xE27E,0xE280,0xE281,0xE282, 0xE283,0xE284,0xE285,0xE286,0xE287,0xE288,0xE289,0xE28A, 0xE28B,0xE28C,0xE28D,0xE28E,0xE28F,0xE290,0xE291,0xE292, 0xE293,0xE294,0xE295,0xE296,0xE297,0xE298,0xE299,0xE29A, 0xE29B,0xE29C,0xE29D,0xE29E,0xE29F,0xE2A0,0xE340,0xE341, 0xE342,0xE343,0xE344,0xE345,0xE346,0xE347,0xE348,0xE349, 0xE34A,0xE34B,0xE34C,0xE34D,0xE34E,0xE34F,0xE350,0xE351, 0xE352,0xE353,0xE354,0xE355,0xE356,0xE357,0xE358,0xE359, 0xE35A,0xE35B,0xE35C,0xE35D,0xE35E,0xE35F,0xE360,0xE361, 0xE362,0xE363,0xE364,0xE365,0xE366,0xE367,0xE368,0xE369, 0xE36A,0xE36B,0xE36C,0xE36D,0xBCF8,0xE36E,0xE36F,0xE370, 0xE371,0xE372,0xE373,0xE374,0xE375,0xE376,0xE377,0xE378, 0xE379,0xE37A,0xE37B,0xE37C,0xE37D,0xE37E,0xE380,0xE381, 0xE382,0xE383,0xE384,0xE385,0xE386,0xE387,0xF6C6,0xE388, 0xE389,0xE38A,0xE38B,0xE38C,0xE38D,0xE38E,0xE38F,0xE390, 0xE391,0xE392,0xE393,0xE394,0xE395,0xE396,0xE397,0xE398, 0xE399,0xE39A,0xE39B,0xE39C,0xE39D,0xE39E,0xE39F,0xE3A0, 0xE440,0xE441,0xE442,0xE443,0xE444,0xE445,0xF6C7,0xE446, 0xE447,0xE448,0xE449,0xE44A,0xE44B,0xE44C,0xE44D,0xE44E, 0xE44F,0xE450,0xE451,0xE452,0xE453,0xE454,0xE455,0xE456, 0xE457,0xE458,0xE459,0xE45A,0xE45B,0xE45C,0xE45D,0xE45E, 0xF6C8,0xE45F,0xE460,0xE461,0xE462,0xE463,0xE464,0xE465, 0xE466,0xE467,0xE468,0xE469,0xE46A,0xE46B,0xE46C,0xE46D, 0xE46E,0xE46F,0xE470,0xE471,0xE472,0xE473,0xE474,0xE475, 0xE476,0xE477,0xE478,0xE479,0xE47A,0xE47B,0xE47C,0xE47D, 0xE47E,0xE480,0xE481,0xE482,0xE483,0xE484,0xE485,0xE486, 0xE487,0xE488,0xE489,0xE48A,0xE48B,0xE48C,0xE48D,0xE48E, 0xE48F,0xE490,0xE491,0xE492,0xE493,0xE494,0xE495,0xE496, 0xE497,0xE498,0xE499,0xE49A,0xE49B,0xE49C,0xE49D,0xE49E, 0xE49F,0xE4A0,0xE540,0xE541,0xE542,0xE543,0xE544,0xE545, 0xE546,0xE547,0xE548,0xE549,0xE54A,0xE54B,0xE54C,0xE54D, 0xE54E,0xE54F,0xE550,0xE551,0xE552,0xE553,0xE554,0xE555, 0xE556,0xE557,0xE558,0xE559,0xE55A,0xE55B,0xE55C,0xE55D, 0xE55E,0xE55F,0xE560,0xE561,0xE562,0xE563,0xE564,0xE565, 0xE566,0xE567,0xE568,0xE569,0xE56A,0xE56B,0xE56C,0xE56D, 0xE56E,0xE56F,0xE570,0xE571,0xE572,0xE573,0xF6C9,0xE574, 0xE575,0xE576,0xE577,0xE578,0xE579,0xE57A,0xE57B,0xE57C, 0xE57D,0xE57E,0xE580,0xE581,0xE582,0xE583,0xE584,0xE585, 0xE586,0xE587,0xE588,0xE589,0xE58A,0xE58B,0xE58C,0xE58D, 0xE58E,0xE58F,0xE590,0xE591,0xE592,0xE593,0xE594,0xE595, 0xE596,0xE597,0xE598,0xE599,0xE59A,0xE59B,0xE59C,0xE59D, 0xE59E,0xE59F,0xF6CA,0xE5A0,0xE640,0xE641,0xE642,0xE643, 0xE644,0xE645,0xE646,0xE647,0xE648,0xE649,0xE64A,0xE64B, 0xE64C,0xE64D,0xE64E,0xE64F,0xE650,0xE651,0xE652,0xE653, 0xE654,0xE655,0xE656,0xE657,0xE658,0xE659,0xE65A,0xE65B, 0xE65C,0xE65D,0xE65E,0xE65F,0xE660,0xE661,0xE662,0xF6CC, 0xE663,0xE664,0xE665,0xE666,0xE667,0xE668,0xE669,0xE66A, 0xE66B,0xE66C,0xE66D,0xE66E,0xE66F,0xE670,0xE671,0xE672, 0xE673,0xE674,0xE675,0xE676,0xE677,0xE678,0xE679,0xE67A, 0xE67B,0xE67C,0xE67D,0xE67E,0xE680,0xE681,0xE682,0xE683, 0xE684,0xE685,0xE686,0xE687,0xE688,0xE689,0xE68A,0xE68B, 0xE68C,0xE68D,0xE68E,0xE68F,0xE690,0xE691,0xE692,0xE693, 0xE694,0xE695,0xE696,0xE697,0xE698,0xE699,0xE69A,0xE69B, 0xE69C,0xE69D,0xF6CB,0xE69E,0xE69F,0xE6A0,0xE740,0xE741, 0xE742,0xE743,0xE744,0xE745,0xE746,0xE747,0xF7E9,0xE748, 0xE749,0xE74A,0xE74B,0xE74C,0xE74D,0xE74E,0xE74F,0xE750, 0xE751,0xE752,0xE753,0xE754,0xE755,0xE756,0xE757,0xE758, 0xE759,0xE75A,0xE75B,0xE75C,0xE75D,0xE75E,0xE75F,0xE760, 0xE761,0xE762,0xE763,0xE764,0xE765,0xE766,0xE767,0xE768, 0xE769,0xE76A,0xE76B,0xE76C,0xE76D,0xE76E,0xE76F,0xE770, 0xE771,0xE772,0xE773,0xE774,0xE775,0xE776,0xE777,0xE778, 0xE779,0xE77A,0xE77B,0xE77C,0xE77D,0xE77E,0xE780,0xE781, 0xE782,0xE783,0xE784,0xE785,0xE786,0xE787,0xE788,0xE789, 0xE78A,0xE78B,0xE78C,0xE78D,0xE78E,0xE78F,0xE790,0xE791, 0xE792,0xE793,0xE794,0xE795,0xE796,0xE797,0xE798,0xE799, 0xE79A,0xE79B,0xE79C,0xE79D,0xE79E,0xE79F,0xE7A0,0xE840, 0xE841,0xE842,0xE843,0xE844,0xE845,0xE846,0xE847,0xE848, 0xE849,0xE84A,0xE84B,0xE84C,0xE84D,0xE84E,0xF6CD,0xE84F, 0xE850,0xE851,0xE852,0xE853,0xE854,0xE855,0xE856,0xE857, 0xE858,0xE859,0xE85A,0xE85B,0xE85C,0xE85D,0xE85E,0xE85F, 0xE860,0xE861,0xE862,0xE863,0xE864,0xE865,0xE866,0xE867, 0xE868,0xE869,0xE86A,0xE86B,0xE86C,0xE86D,0xE86E,0xE86F, 0xE870,0xE871,0xE872,0xE873,0xE874,0xE875,0xE876,0xE877, 0xE878,0xE879,0xE87A,0xF6CE,0xE87B,0xE87C,0xE87D,0xE87E, 0xE880,0xE881,0xE882,0xE883,0xE884,0xE885,0xE886,0xE887, 0xE888,0xE889,0xE88A,0xE88B,0xE88C,0xE88D,0xE88E,0xE88F, 0xE890,0xE891,0xE892,0xE893,0xE894,0xEEC4,0xEEC5,0xEEC6, 0xD5EB,0xB6A4,0xEEC8,0xEEC7,0xEEC9,0xEECA,0xC7A5,0xEECB, 0xEECC,0xE895,0xB7B0,0xB5F6,0xEECD,0xEECF,0xE896,0xEECE, 0xE897,0xB8C6,0xEED0,0xEED1,0xEED2,0xB6DB,0xB3AE,0xD6D3, 0xC4C6,0xB1B5,0xB8D6,0xEED3,0xEED4,0xD4BF,0xC7D5,0xBEFB, 0xCED9,0xB9B3,0xEED6,0xEED5,0xEED8,0xEED7,0xC5A5,0xEED9, 0xEEDA,0xC7AE,0xEEDB,0xC7AF,0xEEDC,0xB2A7,0xEEDD,0xEEDE, 0xEEDF,0xEEE0,0xEEE1,0xD7EA,0xEEE2,0xEEE3,0xBCD8,0xEEE4, 0xD3CB,0xCCFA,0xB2AC,0xC1E5,0xEEE5,0xC7A6,0xC3AD,0xE898, 0xEEE6,0xEEE7,0xEEE8,0xEEE9,0xEEEA,0xEEEB,0xEEEC,0xE899, 0xEEED,0xEEEE,0xEEEF,0xE89A,0xE89B,0xEEF0,0xEEF1,0xEEF2, 0xEEF4,0xEEF3,0xE89C,0xEEF5,0xCDAD,0xC2C1,0xEEF6,0xEEF7, 0xEEF8,0xD5A1,0xEEF9,0xCFB3,0xEEFA,0xEEFB,0xE89D,0xEEFC, 0xEEFD,0xEFA1,0xEEFE,0xEFA2,0xB8F5,0xC3FA,0xEFA3,0xEFA4, 0xBDC2,0xD2BF,0xB2F9,0xEFA5,0xEFA6,0xEFA7,0xD2F8,0xEFA8, 0xD6FD,0xEFA9,0xC6CC,0xE89E,0xEFAA,0xEFAB,0xC1B4,0xEFAC, 0xCFFA,0xCBF8,0xEFAE,0xEFAD,0xB3FA,0xB9F8,0xEFAF,0xEFB0, 0xD0E2,0xEFB1,0xEFB2,0xB7E6,0xD0BF,0xEFB3,0xEFB4,0xEFB5, 0xC8F1,0xCCE0,0xEFB6,0xEFB7,0xEFB8,0xEFB9,0xEFBA,0xD5E0, 0xEFBB,0xB4ED,0xC3AA,0xEFBC,0xE89F,0xEFBD,0xEFBE,0xEFBF, 0xE8A0,0xCEFD,0xEFC0,0xC2E0,0xB4B8,0xD7B6,0xBDF5,0xE940, 0xCFC7,0xEFC3,0xEFC1,0xEFC2,0xEFC4,0xB6A7,0xBCFC,0xBEE2, 0xC3CC,0xEFC5,0xEFC6,0xE941,0xEFC7,0xEFCF,0xEFC8,0xEFC9, 0xEFCA,0xC7C2,0xEFF1,0xB6CD,0xEFCB,0xE942,0xEFCC,0xEFCD, 0xB6C6,0xC3BE,0xEFCE,0xE943,0xEFD0,0xEFD1,0xEFD2,0xD5F2, 0xE944,0xEFD3,0xC4F7,0xE945,0xEFD4,0xC4F8,0xEFD5,0xEFD6, 0xB8E4,0xB0F7,0xEFD7,0xEFD8,0xEFD9,0xE946,0xEFDA,0xEFDB, 0xEFDC,0xEFDD,0xE947,0xEFDE,0xBEB5,0xEFE1,0xEFDF,0xEFE0, 0xE948,0xEFE2,0xEFE3,0xC1CD,0xEFE4,0xEFE5,0xEFE6,0xEFE7, 0xEFE8,0xEFE9,0xEFEA,0xEFEB,0xEFEC,0xC0D8,0xE949,0xEFED, 0xC1AD,0xEFEE,0xEFEF,0xEFF0,0xE94A,0xE94B,0xCFE2,0xE94C, 0xE94D,0xE94E,0xE94F,0xE950,0xE951,0xE952,0xE953,0xB3A4, 0xE954,0xE955,0xE956,0xE957,0xE958,0xE959,0xE95A,0xE95B, 0xE95C,0xE95D,0xE95E,0xE95F,0xE960,0xE961,0xE962,0xE963, 0xE964,0xE965,0xE966,0xE967,0xE968,0xE969,0xE96A,0xE96B, 0xE96C,0xE96D,0xE96E,0xE96F,0xE970,0xE971,0xE972,0xE973, 0xE974,0xE975,0xE976,0xE977,0xE978,0xE979,0xE97A,0xE97B, 0xE97C,0xE97D,0xE97E,0xE980,0xE981,0xE982,0xE983,0xE984, 0xE985,0xE986,0xE987,0xE988,0xE989,0xE98A,0xE98B,0xE98C, 0xE98D,0xE98E,0xE98F,0xE990,0xE991,0xE992,0xE993,0xE994, 0xE995,0xE996,0xE997,0xE998,0xE999,0xE99A,0xE99B,0xE99C, 0xE99D,0xE99E,0xE99F,0xE9A0,0xEA40,0xEA41,0xEA42,0xEA43, 0xEA44,0xEA45,0xEA46,0xEA47,0xEA48,0xEA49,0xEA4A,0xEA4B, 0xEA4C,0xEA4D,0xEA4E,0xEA4F,0xEA50,0xEA51,0xEA52,0xEA53, 0xEA54,0xEA55,0xEA56,0xEA57,0xEA58,0xEA59,0xEA5A,0xEA5B, 0xC3C5,0xE3C5,0xC9C1,0xE3C6,0xEA5C,0xB1D5,0xCECA,0xB4B3, 0xC8F2,0xE3C7,0xCFD0,0xE3C8,0xBCE4,0xE3C9,0xE3CA,0xC3C6, 0xD5A2,0xC4D6,0xB9EB,0xCEC5,0xE3CB,0xC3F6,0xE3CC,0xEA5D, 0xB7A7,0xB8F3,0xBAD2,0xE3CD,0xE3CE,0xD4C4,0xE3CF,0xEA5E, 0xE3D0,0xD1CB,0xE3D1,0xE3D2,0xE3D3,0xE3D4,0xD1D6,0xE3D5, 0xB2FB,0xC0BB,0xE3D6,0xEA5F,0xC0AB,0xE3D7,0xE3D8,0xE3D9, 0xEA60,0xE3DA,0xE3DB,0xEA61,0xB8B7,0xDAE2,0xEA62,0xB6D3, 0xEA63,0xDAE4,0xDAE3,0xEA64,0xEA65,0xEA66,0xEA67,0xEA68, 0xEA69,0xEA6A,0xDAE6,0xEA6B,0xEA6C,0xEA6D,0xC8EE,0xEA6E, 0xEA6F,0xDAE5,0xB7C0,0xD1F4,0xD2F5,0xD5F3,0xBDD7,0xEA70, 0xEA71,0xEA72,0xEA73,0xD7E8,0xDAE8,0xDAE7,0xEA74,0xB0A2, 0xCDD3,0xEA75,0xDAE9,0xEA76,0xB8BD,0xBCCA,0xC2BD,0xC2A4, 0xB3C2,0xDAEA,0xEA77,0xC2AA,0xC4B0,0xBDB5,0xEA78,0xEA79, 0xCFDE,0xEA7A,0xEA7B,0xEA7C,0xDAEB,0xC9C2,0xEA7D,0xEA7E, 0xEA80,0xEA81,0xEA82,0xB1DD,0xEA83,0xEA84,0xEA85,0xDAEC, 0xEA86,0xB6B8,0xD4BA,0xEA87,0xB3FD,0xEA88,0xEA89,0xDAED, 0xD4C9,0xCFD5,0xC5E3,0xEA8A,0xDAEE,0xEA8B,0xEA8C,0xEA8D, 0xEA8E,0xEA8F,0xDAEF,0xEA90,0xDAF0,0xC1EA,0xCCD5,0xCFDD, 0xEA91,0xEA92,0xEA93,0xEA94,0xEA95,0xEA96,0xEA97,0xEA98, 0xEA99,0xEA9A,0xEA9B,0xEA9C,0xEA9D,0xD3E7,0xC2A1,0xEA9E, 0xDAF1,0xEA9F,0xEAA0,0xCBE5,0xEB40,0xDAF2,0xEB41,0xCBE6, 0xD2FE,0xEB42,0xEB43,0xEB44,0xB8F4,0xEB45,0xEB46,0xDAF3, 0xB0AF,0xCFB6,0xEB47,0xEB48,0xD5CF,0xEB49,0xEB4A,0xEB4B, 0xEB4C,0xEB4D,0xEB4E,0xEB4F,0xEB50,0xEB51,0xEB52,0xCBED, 0xEB53,0xEB54,0xEB55,0xEB56,0xEB57,0xEB58,0xEB59,0xEB5A, 0xDAF4,0xEB5B,0xEB5C,0xE3C4,0xEB5D,0xEB5E,0xC1A5,0xEB5F, 0xEB60,0xF6BF,0xEB61,0xEB62,0xF6C0,0xF6C1,0xC4D1,0xEB63, 0xC8B8,0xD1E3,0xEB64,0xEB65,0xD0DB,0xD1C5,0xBCAF,0xB9CD, 0xEB66,0xEFF4,0xEB67,0xEB68,0xB4C6,0xD3BA,0xF6C2,0xB3FB, 0xEB69,0xEB6A,0xF6C3,0xEB6B,0xEB6C,0xB5F1,0xEB6D,0xEB6E, 0xEB6F,0xEB70,0xEB71,0xEB72,0xEB73,0xEB74,0xEB75,0xEB76, 0xF6C5,0xEB77,0xEB78,0xEB79,0xEB7A,0xEB7B,0xEB7C,0xEB7D, 0xD3EA,0xF6A7,0xD1A9,0xEB7E,0xEB80,0xEB81,0xEB82,0xF6A9, 0xEB83,0xEB84,0xEB85,0xF6A8,0xEB86,0xEB87,0xC1E3,0xC0D7, 0xEB88,0xB1A2,0xEB89,0xEB8A,0xEB8B,0xEB8C,0xCEED,0xEB8D, 0xD0E8,0xF6AB,0xEB8E,0xEB8F,0xCFF6,0xEB90,0xF6AA,0xD5F0, 0xF6AC,0xC3B9,0xEB91,0xEB92,0xEB93,0xBBF4,0xF6AE,0xF6AD, 0xEB94,0xEB95,0xEB96,0xC4DE,0xEB97,0xEB98,0xC1D8,0xEB99, 0xEB9A,0xEB9B,0xEB9C,0xEB9D,0xCBAA,0xEB9E,0xCFBC,0xEB9F, 0xEBA0,0xEC40,0xEC41,0xEC42,0xEC43,0xEC44,0xEC45,0xEC46, 0xEC47,0xEC48,0xF6AF,0xEC49,0xEC4A,0xF6B0,0xEC4B,0xEC4C, 0xF6B1,0xEC4D,0xC2B6,0xEC4E,0xEC4F,0xEC50,0xEC51,0xEC52, 0xB0D4,0xC5F9,0xEC53,0xEC54,0xEC55,0xEC56,0xF6B2,0xEC57, 0xEC58,0xEC59,0xEC5A,0xEC5B,0xEC5C,0xEC5D,0xEC5E,0xEC5F, 0xEC60,0xEC61,0xEC62,0xEC63,0xEC64,0xEC65,0xEC66,0xEC67, 0xEC68,0xEC69,0xC7E0,0xF6A6,0xEC6A,0xEC6B,0xBEB8,0xEC6C, 0xEC6D,0xBEB2,0xEC6E,0xB5E5,0xEC6F,0xEC70,0xB7C7,0xEC71, 0xBFBF,0xC3D2,0xC3E6,0xEC72,0xEC73,0xD8CC,0xEC74,0xEC75, 0xEC76,0xB8EF,0xEC77,0xEC78,0xEC79,0xEC7A,0xEC7B,0xEC7C, 0xEC7D,0xEC7E,0xEC80,0xBDF9,0xD1A5,0xEC81,0xB0D0,0xEC82, 0xEC83,0xEC84,0xEC85,0xEC86,0xF7B0,0xEC87,0xEC88,0xEC89, 0xEC8A,0xEC8B,0xEC8C,0xEC8D,0xEC8E,0xF7B1,0xEC8F,0xEC90, 0xEC91,0xEC92,0xEC93,0xD0AC,0xEC94,0xB0B0,0xEC95,0xEC96, 0xEC97,0xF7B2,0xF7B3,0xEC98,0xF7B4,0xEC99,0xEC9A,0xEC9B, 0xC7CA,0xEC9C,0xEC9D,0xEC9E,0xEC9F,0xECA0,0xED40,0xED41, 0xBECF,0xED42,0xED43,0xF7B7,0xED44,0xED45,0xED46,0xED47, 0xED48,0xED49,0xED4A,0xF7B6,0xED4B,0xB1DE,0xED4C,0xF7B5, 0xED4D,0xED4E,0xF7B8,0xED4F,0xF7B9,0xED50,0xED51,0xED52, 0xED53,0xED54,0xED55,0xED56,0xED57,0xED58,0xED59,0xED5A, 0xED5B,0xED5C,0xED5D,0xED5E,0xED5F,0xED60,0xED61,0xED62, 0xED63,0xED64,0xED65,0xED66,0xED67,0xED68,0xED69,0xED6A, 0xED6B,0xED6C,0xED6D,0xED6E,0xED6F,0xED70,0xED71,0xED72, 0xED73,0xED74,0xED75,0xED76,0xED77,0xED78,0xED79,0xED7A, 0xED7B,0xED7C,0xED7D,0xED7E,0xED80,0xED81,0xCEA4,0xC8CD, 0xED82,0xBAAB,0xE8B8,0xE8B9,0xE8BA,0xBEC2,0xED83,0xED84, 0xED85,0xED86,0xED87,0xD2F4,0xED88,0xD4CF,0xC9D8,0xED89, 0xED8A,0xED8B,0xED8C,0xED8D,0xED8E,0xED8F,0xED90,0xED91, 0xED92,0xED93,0xED94,0xED95,0xED96,0xED97,0xED98,0xED99, 0xED9A,0xED9B,0xED9C,0xED9D,0xED9E,0xED9F,0xEDA0,0xEE40, 0xEE41,0xEE42,0xEE43,0xEE44,0xEE45,0xEE46,0xEE47,0xEE48, 0xEE49,0xEE4A,0xEE4B,0xEE4C,0xEE4D,0xEE4E,0xEE4F,0xEE50, 0xEE51,0xEE52,0xEE53,0xEE54,0xEE55,0xEE56,0xEE57,0xEE58, 0xEE59,0xEE5A,0xEE5B,0xEE5C,0xEE5D,0xEE5E,0xEE5F,0xEE60, 0xEE61,0xEE62,0xEE63,0xEE64,0xEE65,0xEE66,0xEE67,0xEE68, 0xEE69,0xEE6A,0xEE6B,0xEE6C,0xEE6D,0xEE6E,0xEE6F,0xEE70, 0xEE71,0xEE72,0xEE73,0xEE74,0xEE75,0xEE76,0xEE77,0xEE78, 0xEE79,0xEE7A,0xEE7B,0xEE7C,0xEE7D,0xEE7E,0xEE80,0xEE81, 0xEE82,0xEE83,0xEE84,0xEE85,0xEE86,0xEE87,0xEE88,0xEE89, 0xEE8A,0xEE8B,0xEE8C,0xEE8D,0xEE8E,0xEE8F,0xEE90,0xEE91, 0xEE92,0xEE93,0xEE94,0xEE95,0xEE96,0xEE97,0xEE98,0xEE99, 0xEE9A,0xEE9B,0xEE9C,0xEE9D,0xEE9E,0xEE9F,0xEEA0,0xEF40, 0xEF41,0xEF42,0xEF43,0xEF44,0xEF45,0xD2B3,0xB6A5,0xC7EA, 0xF1FC,0xCFEE,0xCBB3,0xD0EB,0xE7EF,0xCDE7,0xB9CB,0xB6D9, 0xF1FD,0xB0E4,0xCBCC,0xF1FE,0xD4A4,0xC2AD,0xC1EC,0xC6C4, 0xBEB1,0xF2A1,0xBCD5,0xEF46,0xF2A2,0xF2A3,0xEF47,0xF2A4, 0xD2C3,0xC6B5,0xEF48,0xCDC7,0xF2A5,0xEF49,0xD3B1,0xBFC5, 0xCCE2,0xEF4A,0xF2A6,0xF2A7,0xD1D5,0xB6EE,0xF2A8,0xF2A9, 0xB5DF,0xF2AA,0xF2AB,0xEF4B,0xB2FC,0xF2AC,0xF2AD,0xC8A7, 0xEF4C,0xEF4D,0xEF4E,0xEF4F,0xEF50,0xEF51,0xEF52,0xEF53, 0xEF54,0xEF55,0xEF56,0xEF57,0xEF58,0xEF59,0xEF5A,0xEF5B, 0xEF5C,0xEF5D,0xEF5E,0xEF5F,0xEF60,0xEF61,0xEF62,0xEF63, 0xEF64,0xEF65,0xEF66,0xEF67,0xEF68,0xEF69,0xEF6A,0xEF6B, 0xEF6C,0xEF6D,0xEF6E,0xEF6F,0xEF70,0xEF71,0xB7E7,0xEF72, 0xEF73,0xECA9,0xECAA,0xECAB,0xEF74,0xECAC,0xEF75,0xEF76, 0xC6AE,0xECAD,0xECAE,0xEF77,0xEF78,0xEF79,0xB7C9,0xCAB3, 0xEF7A,0xEF7B,0xEF7C,0xEF7D,0xEF7E,0xEF80,0xEF81,0xE2B8, 0xF7CF,0xEF82,0xEF83,0xEF84,0xEF85,0xEF86,0xEF87,0xEF88, 0xEF89,0xEF8A,0xEF8B,0xEF8C,0xEF8D,0xEF8E,0xEF8F,0xEF90, 0xEF91,0xEF92,0xEF93,0xEF94,0xEF95,0xEF96,0xEF97,0xEF98, 0xEF99,0xEF9A,0xEF9B,0xEF9C,0xEF9D,0xEF9E,0xEF9F,0xEFA0, 0xF040,0xF041,0xF042,0xF043,0xF044,0xF7D0,0xF045,0xF046, 0xB2CD,0xF047,0xF048,0xF049,0xF04A,0xF04B,0xF04C,0xF04D, 0xF04E,0xF04F,0xF050,0xF051,0xF052,0xF053,0xF054,0xF055, 0xF056,0xF057,0xF058,0xF059,0xF05A,0xF05B,0xF05C,0xF05D, 0xF05E,0xF05F,0xF060,0xF061,0xF062,0xF063,0xF7D1,0xF064, 0xF065,0xF066,0xF067,0xF068,0xF069,0xF06A,0xF06B,0xF06C, 0xF06D,0xF06E,0xF06F,0xF070,0xF071,0xF072,0xF073,0xF074, 0xF075,0xF076,0xF077,0xF078,0xF079,0xF07A,0xF07B,0xF07C, 0xF07D,0xF07E,0xF080,0xF081,0xF082,0xF083,0xF084,0xF085, 0xF086,0xF087,0xF088,0xF089,0xF7D3,0xF7D2,0xF08A,0xF08B, 0xF08C,0xF08D,0xF08E,0xF08F,0xF090,0xF091,0xF092,0xF093, 0xF094,0xF095,0xF096,0xE2BB,0xF097,0xBCA2,0xF098,0xE2BC, 0xE2BD,0xE2BE,0xE2BF,0xE2C0,0xE2C1,0xB7B9,0xD2FB,0xBDA4, 0xCACE,0xB1A5,0xCBC7,0xF099,0xE2C2,0xB6FC,0xC8C4,0xE2C3, 0xF09A,0xF09B,0xBDC8,0xF09C,0xB1FD,0xE2C4,0xF09D,0xB6F6, 0xE2C5,0xC4D9,0xF09E,0xF09F,0xE2C6,0xCFDA,0xB9DD,0xE2C7, 0xC0A1,0xF0A0,0xE2C8,0xB2F6,0xF140,0xE2C9,0xF141,0xC1F3, 0xE2CA,0xE2CB,0xC2F8,0xE2CC,0xE2CD,0xE2CE,0xCAD7,0xD8B8, 0xD9E5,0xCFE3,0xF142,0xF143,0xF144,0xF145,0xF146,0xF147, 0xF148,0xF149,0xF14A,0xF14B,0xF14C,0xF0A5,0xF14D,0xF14E, 0xDCB0,0xF14F,0xF150,0xF151,0xF152,0xF153,0xF154,0xF155, 0xF156,0xF157,0xF158,0xF159,0xF15A,0xF15B,0xF15C,0xF15D, 0xF15E,0xF15F,0xF160,0xF161,0xF162,0xF163,0xF164,0xF165, 0xF166,0xF167,0xF168,0xF169,0xF16A,0xF16B,0xF16C,0xF16D, 0xF16E,0xF16F,0xF170,0xF171,0xF172,0xF173,0xF174,0xF175, 0xF176,0xF177,0xF178,0xF179,0xF17A,0xF17B,0xF17C,0xF17D, 0xF17E,0xF180,0xF181,0xF182,0xF183,0xF184,0xF185,0xF186, 0xF187,0xF188,0xF189,0xF18A,0xF18B,0xF18C,0xF18D,0xF18E, 0xF18F,0xF190,0xF191,0xF192,0xF193,0xF194,0xF195,0xF196, 0xF197,0xF198,0xF199,0xF19A,0xF19B,0xF19C,0xF19D,0xF19E, 0xF19F,0xF1A0,0xF240,0xF241,0xF242,0xF243,0xF244,0xF245, 0xF246,0xF247,0xF248,0xF249,0xF24A,0xF24B,0xF24C,0xF24D, 0xF24E,0xF24F,0xF250,0xF251,0xF252,0xF253,0xF254,0xF255, 0xF256,0xF257,0xF258,0xF259,0xF25A,0xF25B,0xF25C,0xF25D, 0xF25E,0xF25F,0xF260,0xF261,0xF262,0xF263,0xF264,0xF265, 0xF266,0xF267,0xF268,0xF269,0xF26A,0xF26B,0xF26C,0xF26D, 0xF26E,0xF26F,0xF270,0xF271,0xF272,0xF273,0xF274,0xF275, 0xF276,0xF277,0xF278,0xF279,0xF27A,0xF27B,0xF27C,0xF27D, 0xF27E,0xF280,0xF281,0xF282,0xF283,0xF284,0xF285,0xF286, 0xF287,0xF288,0xF289,0xF28A,0xF28B,0xF28C,0xF28D,0xF28E, 0xF28F,0xF290,0xF291,0xF292,0xF293,0xF294,0xF295,0xF296, 0xF297,0xF298,0xF299,0xF29A,0xF29B,0xF29C,0xF29D,0xF29E, 0xF29F,0xF2A0,0xF340,0xF341,0xF342,0xF343,0xF344,0xF345, 0xF346,0xF347,0xF348,0xF349,0xF34A,0xF34B,0xF34C,0xF34D, 0xF34E,0xF34F,0xF350,0xF351,0xC2ED,0xD4A6,0xCDD4,0xD1B1, 0xB3DB,0xC7FD,0xF352,0xB2B5,0xC2BF,0xE6E0,0xCABB,0xE6E1, 0xE6E2,0xBED4,0xE6E3,0xD7A4,0xCDD5,0xE6E5,0xBCDD,0xE6E4, 0xE6E6,0xE6E7,0xC2EE,0xF353,0xBDBE,0xE6E8,0xC2E6,0xBAA7, 0xE6E9,0xF354,0xE6EA,0xB3D2,0xD1E9,0xF355,0xF356,0xBFA5, 0xE6EB,0xC6EF,0xE6EC,0xE6ED,0xF357,0xF358,0xE6EE,0xC6AD, 0xE6EF,0xF359,0xC9A7,0xE6F0,0xE6F1,0xE6F2,0xE5B9,0xE6F3, 0xE6F4,0xC2E2,0xE6F5,0xE6F6,0xD6E8,0xE6F7,0xF35A,0xE6F8, 0xB9C7,0xF35B,0xF35C,0xF35D,0xF35E,0xF35F,0xF360,0xF361, 0xF7BB,0xF7BA,0xF362,0xF363,0xF364,0xF365,0xF7BE,0xF7BC, 0xBAA1,0xF366,0xF7BF,0xF367,0xF7C0,0xF368,0xF369,0xF36A, 0xF7C2,0xF7C1,0xF7C4,0xF36B,0xF36C,0xF7C3,0xF36D,0xF36E, 0xF36F,0xF370,0xF371,0xF7C5,0xF7C6,0xF372,0xF373,0xF374, 0xF375,0xF7C7,0xF376,0xCBE8,0xF377,0xF378,0xF379,0xF37A, 0xB8DF,0xF37B,0xF37C,0xF37D,0xF37E,0xF380,0xF381,0xF7D4, 0xF382,0xF7D5,0xF383,0xF384,0xF385,0xF386,0xF7D6,0xF387, 0xF388,0xF389,0xF38A,0xF7D8,0xF38B,0xF7DA,0xF38C,0xF7D7, 0xF38D,0xF38E,0xF38F,0xF390,0xF391,0xF392,0xF393,0xF394, 0xF395,0xF7DB,0xF396,0xF7D9,0xF397,0xF398,0xF399,0xF39A, 0xF39B,0xF39C,0xF39D,0xD7D7,0xF39E,0xF39F,0xF3A0,0xF440, 0xF7DC,0xF441,0xF442,0xF443,0xF444,0xF445,0xF446,0xF7DD, 0xF447,0xF448,0xF449,0xF7DE,0xF44A,0xF44B,0xF44C,0xF44D, 0xF44E,0xF44F,0xF450,0xF451,0xF452,0xF453,0xF454,0xF7DF, 0xF455,0xF456,0xF457,0xF7E0,0xF458,0xF459,0xF45A,0xF45B, 0xF45C,0xF45D,0xF45E,0xF45F,0xF460,0xF461,0xF462,0xDBCB, 0xF463,0xF464,0xD8AA,0xF465,0xF466,0xF467,0xF468,0xF469, 0xF46A,0xF46B,0xF46C,0xE5F7,0xB9ED,0xF46D,0xF46E,0xF46F, 0xF470,0xBFFD,0xBBEA,0xF7C9,0xC6C7,0xF7C8,0xF471,0xF7CA, 0xF7CC,0xF7CB,0xF472,0xF473,0xF474,0xF7CD,0xF475,0xCEBA, 0xF476,0xF7CE,0xF477,0xF478,0xC4A7,0xF479,0xF47A,0xF47B, 0xF47C,0xF47D,0xF47E,0xF480,0xF481,0xF482,0xF483,0xF484, 0xF485,0xF486,0xF487,0xF488,0xF489,0xF48A,0xF48B,0xF48C, 0xF48D,0xF48E,0xF48F,0xF490,0xF491,0xF492,0xF493,0xF494, 0xF495,0xF496,0xF497,0xF498,0xF499,0xF49A,0xF49B,0xF49C, 0xF49D,0xF49E,0xF49F,0xF4A0,0xF540,0xF541,0xF542,0xF543, 0xF544,0xF545,0xF546,0xF547,0xF548,0xF549,0xF54A,0xF54B, 0xF54C,0xF54D,0xF54E,0xF54F,0xF550,0xF551,0xF552,0xF553, 0xF554,0xF555,0xF556,0xF557,0xF558,0xF559,0xF55A,0xF55B, 0xF55C,0xF55D,0xF55E,0xF55F,0xF560,0xF561,0xF562,0xF563, 0xF564,0xF565,0xF566,0xF567,0xF568,0xF569,0xF56A,0xF56B, 0xF56C,0xF56D,0xF56E,0xF56F,0xF570,0xF571,0xF572,0xF573, 0xF574,0xF575,0xF576,0xF577,0xF578,0xF579,0xF57A,0xF57B, 0xF57C,0xF57D,0xF57E,0xF580,0xF581,0xF582,0xF583,0xF584, 0xF585,0xF586,0xF587,0xF588,0xF589,0xF58A,0xF58B,0xF58C, 0xF58D,0xF58E,0xF58F,0xF590,0xF591,0xF592,0xF593,0xF594, 0xF595,0xF596,0xF597,0xF598,0xF599,0xF59A,0xF59B,0xF59C, 0xF59D,0xF59E,0xF59F,0xF5A0,0xF640,0xF641,0xF642,0xF643, 0xF644,0xF645,0xF646,0xF647,0xF648,0xF649,0xF64A,0xF64B, 0xF64C,0xF64D,0xF64E,0xF64F,0xF650,0xF651,0xF652,0xF653, 0xF654,0xF655,0xF656,0xF657,0xF658,0xF659,0xF65A,0xF65B, 0xF65C,0xF65D,0xF65E,0xF65F,0xF660,0xF661,0xF662,0xF663, 0xF664,0xF665,0xF666,0xF667,0xF668,0xF669,0xF66A,0xF66B, 0xF66C,0xF66D,0xF66E,0xF66F,0xF670,0xF671,0xF672,0xF673, 0xF674,0xF675,0xF676,0xF677,0xF678,0xF679,0xF67A,0xF67B, 0xF67C,0xF67D,0xF67E,0xF680,0xF681,0xF682,0xF683,0xF684, 0xF685,0xF686,0xF687,0xF688,0xF689,0xF68A,0xF68B,0xF68C, 0xF68D,0xF68E,0xF68F,0xF690,0xF691,0xF692,0xF693,0xF694, 0xF695,0xF696,0xF697,0xF698,0xF699,0xF69A,0xF69B,0xF69C, 0xF69D,0xF69E,0xF69F,0xF6A0,0xF740,0xF741,0xF742,0xF743, 0xF744,0xF745,0xF746,0xF747,0xF748,0xF749,0xF74A,0xF74B, 0xF74C,0xF74D,0xF74E,0xF74F,0xF750,0xF751,0xF752,0xF753, 0xF754,0xF755,0xF756,0xF757,0xF758,0xF759,0xF75A,0xF75B, 0xF75C,0xF75D,0xF75E,0xF75F,0xF760,0xF761,0xF762,0xF763, 0xF764,0xF765,0xF766,0xF767,0xF768,0xF769,0xF76A,0xF76B, 0xF76C,0xF76D,0xF76E,0xF76F,0xF770,0xF771,0xF772,0xF773, 0xF774,0xF775,0xF776,0xF777,0xF778,0xF779,0xF77A,0xF77B, 0xF77C,0xF77D,0xF77E,0xF780,0xD3E3,0xF781,0xF782,0xF6CF, 0xF783,0xC2B3,0xF6D0,0xF784,0xF785,0xF6D1,0xF6D2,0xF6D3, 0xF6D4,0xF786,0xF787,0xF6D6,0xF788,0xB1AB,0xF6D7,0xF789, 0xF6D8,0xF6D9,0xF6DA,0xF78A,0xF6DB,0xF6DC,0xF78B,0xF78C, 0xF78D,0xF78E,0xF6DD,0xF6DE,0xCFCA,0xF78F,0xF6DF,0xF6E0, 0xF6E1,0xF6E2,0xF6E3,0xF6E4,0xC0F0,0xF6E5,0xF6E6,0xF6E7, 0xF6E8,0xF6E9,0xF790,0xF6EA,0xF791,0xF6EB,0xF6EC,0xF792, 0xF6ED,0xF6EE,0xF6EF,0xF6F0,0xF6F1,0xF6F2,0xF6F3,0xF6F4, 0xBEA8,0xF793,0xF6F5,0xF6F6,0xF6F7,0xF6F8,0xF794,0xF795, 0xF796,0xF797,0xF798,0xC8FA,0xF6F9,0xF6FA,0xF6FB,0xF6FC, 0xF799,0xF79A,0xF6FD,0xF6FE,0xF7A1,0xF7A2,0xF7A3,0xF7A4, 0xF7A5,0xF79B,0xF79C,0xF7A6,0xF7A7,0xF7A8,0xB1EE,0xF7A9, 0xF7AA,0xF7AB,0xF79D,0xF79E,0xF7AC,0xF7AD,0xC1DB,0xF7AE, 0xF79F,0xF7A0,0xF7AF,0xF840,0xF841,0xF842,0xF843,0xF844, 0xF845,0xF846,0xF847,0xF848,0xF849,0xF84A,0xF84B,0xF84C, 0xF84D,0xF84E,0xF84F,0xF850,0xF851,0xF852,0xF853,0xF854, 0xF855,0xF856,0xF857,0xF858,0xF859,0xF85A,0xF85B,0xF85C }; const static uint16 gbkEncoderInnerIndex6[]= { 0xF85D,0xF85E,0xF85F,0xF860,0xF861,0xF862,0xF863,0xF864, 0xF865,0xF866,0xF867,0xF868,0xF869,0xF86A,0xF86B,0xF86C, 0xF86D,0xF86E,0xF86F,0xF870,0xF871,0xF872,0xF873,0xF874, 0xF875,0xF876,0xF877,0xF878,0xF879,0xF87A,0xF87B,0xF87C, 0xF87D,0xF87E,0xF880,0xF881,0xF882,0xF883,0xF884,0xF885, 0xF886,0xF887,0xF888,0xF889,0xF88A,0xF88B,0xF88C,0xF88D, 0xF88E,0xF88F,0xF890,0xF891,0xF892,0xF893,0xF894,0xF895, 0xF896,0xF897,0xF898,0xF899,0xF89A,0xF89B,0xF89C,0xF89D, 0xF89E,0xF89F,0xF8A0,0xF940,0xF941,0xF942,0xF943,0xF944, 0xF945,0xF946,0xF947,0xF948,0xF949,0xF94A,0xF94B,0xF94C, 0xF94D,0xF94E,0xF94F,0xF950,0xF951,0xF952,0xF953,0xF954, 0xF955,0xF956,0xF957,0xF958,0xF959,0xF95A,0xF95B,0xF95C, 0xF95D,0xF95E,0xF95F,0xF960,0xF961,0xF962,0xF963,0xF964, 0xF965,0xF966,0xF967,0xF968,0xF969,0xF96A,0xF96B,0xF96C, 0xF96D,0xF96E,0xF96F,0xF970,0xF971,0xF972,0xF973,0xF974, 0xF975,0xF976,0xF977,0xF978,0xF979,0xF97A,0xF97B,0xF97C, 0xF97D,0xF97E,0xF980,0xF981,0xF982,0xF983,0xF984,0xF985, 0xF986,0xF987,0xF988,0xF989,0xF98A,0xF98B,0xF98C,0xF98D, 0xF98E,0xF98F,0xF990,0xF991,0xF992,0xF993,0xF994,0xF995, 0xF996,0xF997,0xF998,0xF999,0xF99A,0xF99B,0xF99C,0xF99D, 0xF99E,0xF99F,0xF9A0,0xFA40,0xFA41,0xFA42,0xFA43,0xFA44, 0xFA45,0xFA46,0xFA47,0xFA48,0xFA49,0xFA4A,0xFA4B,0xFA4C, 0xFA4D,0xFA4E,0xFA4F,0xFA50,0xFA51,0xFA52,0xFA53,0xFA54, 0xFA55,0xFA56,0xFA57,0xFA58,0xFA59,0xFA5A,0xFA5B,0xFA5C, 0xFA5D,0xFA5E,0xFA5F,0xFA60,0xFA61,0xFA62,0xFA63,0xFA64, 0xFA65,0xFA66,0xFA67,0xFA68,0xFA69,0xFA6A,0xFA6B,0xFA6C, 0xFA6D,0xFA6E,0xFA6F,0xFA70,0xFA71,0xFA72,0xFA73,0xFA74, 0xFA75,0xFA76,0xFA77,0xFA78,0xFA79,0xFA7A,0xFA7B,0xFA7C, 0xFA7D,0xFA7E,0xFA80,0xFA81,0xFA82,0xFA83,0xFA84,0xFA85, 0xFA86,0xFA87,0xFA88,0xFA89,0xFA8A,0xFA8B,0xFA8C,0xFA8D, 0xFA8E,0xFA8F,0xFA90,0xFA91,0xFA92,0xFA93,0xFA94,0xFA95, 0xFA96,0xFA97,0xFA98,0xFA99,0xFA9A,0xFA9B,0xFA9C,0xFA9D, 0xFA9E,0xFA9F,0xFAA0,0xFB40,0xFB41,0xFB42,0xFB43,0xFB44, 0xFB45,0xFB46,0xFB47,0xFB48,0xFB49,0xFB4A,0xFB4B,0xFB4C, 0xFB4D,0xFB4E,0xFB4F,0xFB50,0xFB51,0xFB52,0xFB53,0xFB54, 0xFB55,0xFB56,0xFB57,0xFB58,0xFB59,0xFB5A,0xFB5B,0xC4F1, 0xF0AF,0xBCA6,0xF0B0,0xC3F9,0xFB5C,0xC5B8,0xD1BB,0xFB5D, 0xF0B1,0xF0B2,0xF0B3,0xF0B4,0xF0B5,0xD1BC,0xFB5E,0xD1EC, 0xFB5F,0xF0B7,0xF0B6,0xD4A7,0xFB60,0xCDD2,0xF0B8,0xF0BA, 0xF0B9,0xF0BB,0xF0BC,0xFB61,0xFB62,0xB8EB,0xF0BD,0xBAE8, 0xFB63,0xF0BE,0xF0BF,0xBEE9,0xF0C0,0xB6EC,0xF0C1,0xF0C2, 0xF0C3,0xF0C4,0xC8B5,0xF0C5,0xF0C6,0xFB64,0xF0C7,0xC5F4, 0xFB65,0xF0C8,0xFB66,0xFB67,0xFB68,0xF0C9,0xFB69,0xF0CA, 0xF7BD,0xFB6A,0xF0CB,0xF0CC,0xF0CD,0xFB6B,0xF0CE,0xFB6C, 0xFB6D,0xFB6E,0xFB6F,0xF0CF,0xBAD7,0xFB70,0xF0D0,0xF0D1, 0xF0D2,0xF0D3,0xF0D4,0xF0D5,0xF0D6,0xF0D8,0xFB71,0xFB72, 0xD3A5,0xF0D7,0xFB73,0xF0D9,0xFB74,0xFB75,0xFB76,0xFB77, 0xFB78,0xFB79,0xFB7A,0xFB7B,0xFB7C,0xFB7D,0xF5BA,0xC2B9, 0xFB7E,0xFB80,0xF7E4,0xFB81,0xFB82,0xFB83,0xFB84,0xF7E5, 0xF7E6,0xFB85,0xFB86,0xF7E7,0xFB87,0xFB88,0xFB89,0xFB8A, 0xFB8B,0xFB8C,0xF7E8,0xC2B4,0xFB8D,0xFB8E,0xFB8F,0xFB90, 0xFB91,0xFB92,0xFB93,0xFB94,0xFB95,0xF7EA,0xFB96,0xF7EB, 0xFB97,0xFB98,0xFB99,0xFB9A,0xFB9B,0xFB9C,0xC2F3,0xFB9D, 0xFB9E,0xFB9F,0xFBA0,0xFC40,0xFC41,0xFC42,0xFC43,0xFC44, 0xFC45,0xFC46,0xFC47,0xFC48,0xF4F0,0xFC49,0xFC4A,0xFC4B, 0xF4EF,0xFC4C,0xFC4D,0xC2E9,0xFC4E,0xF7E1,0xF7E2,0xFC4F, 0xFC50,0xFC51,0xFC52,0xFC53,0xBBC6,0xFC54,0xFC55,0xFC56, 0xFC57,0xD9E4,0xFC58,0xFC59,0xFC5A,0xCAF2,0xC0E8,0xF0A4, 0xFC5B,0xBADA,0xFC5C,0xFC5D,0xC7AD,0xFC5E,0xFC5F,0xFC60, 0xC4AC,0xFC61,0xFC62,0xF7EC,0xF7ED,0xF7EE,0xFC63,0xF7F0, 0xF7EF,0xFC64,0xF7F1,0xFC65,0xFC66,0xF7F4,0xFC67,0xF7F3, 0xFC68,0xF7F2,0xF7F5,0xFC69,0xFC6A,0xFC6B,0xFC6C,0xF7F6, 0xFC6D,0xFC6E,0xFC6F,0xFC70,0xFC71,0xFC72,0xFC73,0xFC74, 0xFC75,0xEDE9,0xFC76,0xEDEA,0xEDEB,0xFC77,0xF6BC,0xFC78, 0xFC79,0xFC7A,0xFC7B,0xFC7C,0xFC7D,0xFC7E,0xFC80,0xFC81, 0xFC82,0xFC83,0xFC84,0xF6BD,0xFC85,0xF6BE,0xB6A6,0xFC86, 0xD8BE,0xFC87,0xFC88,0xB9C4,0xFC89,0xFC8A,0xFC8B,0xD8BB, 0xFC8C,0xDCB1,0xFC8D,0xFC8E,0xFC8F,0xFC90,0xFC91,0xFC92, 0xCAF3,0xFC93,0xF7F7,0xFC94,0xFC95,0xFC96,0xFC97,0xFC98, 0xFC99,0xFC9A,0xFC9B,0xFC9C,0xF7F8,0xFC9D,0xFC9E,0xF7F9, 0xFC9F,0xFCA0,0xFD40,0xFD41,0xFD42,0xFD43,0xFD44,0xF7FB, 0xFD45,0xF7FA,0xFD46,0xB1C7,0xFD47,0xF7FC,0xF7FD,0xFD48, 0xFD49,0xFD4A,0xFD4B,0xFD4C,0xF7FE,0xFD4D,0xFD4E,0xFD4F, 0xFD50,0xFD51,0xFD52,0xFD53,0xFD54,0xFD55,0xFD56,0xFD57, 0xC6EB,0xECB4,0xFD58,0xFD59,0xFD5A,0xFD5B,0xFD5C,0xFD5D, 0xFD5E,0xFD5F,0xFD60,0xFD61,0xFD62,0xFD63,0xFD64,0xFD65, 0xFD66,0xFD67,0xFD68,0xFD69,0xFD6A,0xFD6B,0xFD6C,0xFD6D, 0xFD6E,0xFD6F,0xFD70,0xFD71,0xFD72,0xFD73,0xFD74,0xFD75, 0xFD76,0xFD77,0xFD78,0xFD79,0xFD7A,0xFD7B,0xFD7C,0xFD7D, 0xFD7E,0xFD80,0xFD81,0xFD82,0xFD83,0xFD84,0xFD85,0xB3DD, 0xF6B3,0xFD86,0xFD87,0xF6B4,0xC1E4,0xF6B5,0xF6B6,0xF6B7, 0xF6B8,0xF6B9,0xF6BA,0xC8A3,0xF6BB,0xFD88,0xFD89,0xFD8A, 0xFD8B,0xFD8C,0xFD8D,0xFD8E,0xFD8F,0xFD90,0xFD91,0xFD92, 0xFD93,0xC1FA,0xB9A8,0xEDE8,0xFD94,0xFD95,0xFD96,0xB9EA, 0xD9DF,0xFD97,0xFD98,0xFD99,0xFD9A,0xFD9B,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xAAA1,0xAAA2,0xAAA3,0xAAA4,0xAAA5,0xAAA6,0xAAA7,0xAAA8, 0xAAA9,0xAAAA,0xAAAB,0xAAAC,0xAAAD,0xAAAE,0xAAAF,0xAAB0, 0xAAB1,0xAAB2,0xAAB3,0xAAB4,0xAAB5,0xAAB6,0xAAB7,0xAAB8, 0xAAB9,0xAABA,0xAABB,0xAABC,0xAABD,0xAABE,0xAABF,0xAAC0, 0xAAC1,0xAAC2,0xAAC3,0xAAC4,0xAAC5,0xAAC6,0xAAC7,0xAAC8, 0xAAC9,0xAACA,0xAACB,0xAACC,0xAACD,0xAACE,0xAACF,0xAAD0, 0xAAD1,0xAAD2,0xAAD3,0xAAD4,0xAAD5,0xAAD6,0xAAD7,0xAAD8, 0xAAD9,0xAADA,0xAADB,0xAADC,0xAADD,0xAADE,0xAADF,0xAAE0, 0xAAE1,0xAAE2,0xAAE3,0xAAE4,0xAAE5,0xAAE6,0xAAE7,0xAAE8, 0xAAE9,0xAAEA,0xAAEB,0xAAEC,0xAAED,0xAAEE,0xAAEF,0xAAF0, 0xAAF1,0xAAF2,0xAAF3,0xAAF4,0xAAF5,0xAAF6,0xAAF7,0xAAF8, 0xAAF9,0xAAFA,0xAAFB,0xAAFC,0xAAFD,0xAAFE,0xABA1,0xABA2, 0xABA3,0xABA4,0xABA5,0xABA6,0xABA7,0xABA8,0xABA9,0xABAA, 0xABAB,0xABAC,0xABAD,0xABAE,0xABAF,0xABB0,0xABB1,0xABB2, 0xABB3,0xABB4,0xABB5,0xABB6,0xABB7,0xABB8,0xABB9,0xABBA, 0xABBB,0xABBC,0xABBD,0xABBE,0xABBF,0xABC0,0xABC1,0xABC2, 0xABC3,0xABC4,0xABC5,0xABC6,0xABC7,0xABC8,0xABC9,0xABCA, 0xABCB,0xABCC,0xABCD,0xABCE,0xABCF,0xABD0,0xABD1,0xABD2, 0xABD3,0xABD4,0xABD5,0xABD6,0xABD7,0xABD8,0xABD9,0xABDA, 0xABDB,0xABDC,0xABDD,0xABDE,0xABDF,0xABE0,0xABE1,0xABE2, 0xABE3,0xABE4,0xABE5,0xABE6,0xABE7,0xABE8,0xABE9,0xABEA, 0xABEB,0xABEC,0xABED,0xABEE,0xABEF,0xABF0,0xABF1,0xABF2, 0xABF3,0xABF4,0xABF5,0xABF6,0xABF7,0xABF8,0xABF9,0xABFA, 0xABFB,0xABFC,0xABFD,0xABFE,0xACA1,0xACA2,0xACA3,0xACA4, 0xACA5,0xACA6,0xACA7,0xACA8,0xACA9,0xACAA,0xACAB,0xACAC, 0xACAD,0xACAE,0xACAF,0xACB0,0xACB1,0xACB2,0xACB3,0xACB4, 0xACB5,0xACB6,0xACB7,0xACB8,0xACB9,0xACBA,0xACBB,0xACBC, 0xACBD,0xACBE,0xACBF,0xACC0,0xACC1,0xACC2,0xACC3,0xACC4, 0xACC5,0xACC6,0xACC7,0xACC8,0xACC9,0xACCA,0xACCB,0xACCC, 0xACCD,0xACCE,0xACCF,0xACD0,0xACD1,0xACD2,0xACD3,0xACD4, 0xACD5,0xACD6,0xACD7,0xACD8,0xACD9,0xACDA,0xACDB,0xACDC, 0xACDD,0xACDE,0xACDF,0xACE0,0xACE1,0xACE2,0xACE3,0xACE4, 0xACE5,0xACE6,0xACE7,0xACE8,0xACE9,0xACEA,0xACEB,0xACEC, 0xACED,0xACEE,0xACEF,0xACF0,0xACF1,0xACF2,0xACF3,0xACF4, 0xACF5,0xACF6,0xACF7,0xACF8,0xACF9,0xACFA,0xACFB,0xACFC, 0xACFD,0xACFE,0xADA1,0xADA2,0xADA3,0xADA4,0xADA5,0xADA6, 0xADA7,0xADA8,0xADA9,0xADAA,0xADAB,0xADAC,0xADAD,0xADAE, 0xADAF,0xADB0,0xADB1,0xADB2,0xADB3,0xADB4,0xADB5,0xADB6, 0xADB7,0xADB8,0xADB9,0xADBA,0xADBB,0xADBC,0xADBD,0xADBE, 0xADBF,0xADC0,0xADC1,0xADC2,0xADC3,0xADC4,0xADC5,0xADC6, 0xADC7,0xADC8,0xADC9,0xADCA,0xADCB,0xADCC,0xADCD,0xADCE, 0xADCF,0xADD0,0xADD1,0xADD2,0xADD3,0xADD4,0xADD5,0xADD6, 0xADD7,0xADD8,0xADD9,0xADDA,0xADDB,0xADDC,0xADDD,0xADDE, 0xADDF,0xADE0,0xADE1,0xADE2,0xADE3,0xADE4,0xADE5,0xADE6, 0xADE7,0xADE8,0xADE9,0xADEA,0xADEB,0xADEC,0xADED,0xADEE, 0xADEF,0xADF0,0xADF1,0xADF2,0xADF3,0xADF4,0xADF5,0xADF6, 0xADF7,0xADF8,0xADF9,0xADFA,0xADFB,0xADFC,0xADFD,0xADFE, 0xAEA1,0xAEA2,0xAEA3,0xAEA4,0xAEA5,0xAEA6,0xAEA7,0xAEA8, 0xAEA9,0xAEAA,0xAEAB,0xAEAC,0xAEAD,0xAEAE,0xAEAF,0xAEB0, 0xAEB1,0xAEB2,0xAEB3,0xAEB4,0xAEB5,0xAEB6,0xAEB7,0xAEB8, 0xAEB9,0xAEBA,0xAEBB,0xAEBC,0xAEBD,0xAEBE,0xAEBF,0xAEC0, 0xAEC1,0xAEC2,0xAEC3,0xAEC4,0xAEC5,0xAEC6,0xAEC7,0xAEC8, 0xAEC9,0xAECA,0xAECB,0xAECC,0xAECD,0xAECE,0xAECF,0xAED0, 0xAED1,0xAED2,0xAED3,0xAED4,0xAED5,0xAED6,0xAED7,0xAED8, 0xAED9,0xAEDA,0xAEDB,0xAEDC,0xAEDD,0xAEDE,0xAEDF,0xAEE0, 0xAEE1,0xAEE2,0xAEE3,0xAEE4,0xAEE5,0xAEE6,0xAEE7,0xAEE8, 0xAEE9,0xAEEA,0xAEEB,0xAEEC,0xAEED,0xAEEE,0xAEEF,0xAEF0, 0xAEF1,0xAEF2,0xAEF3,0xAEF4,0xAEF5,0xAEF6,0xAEF7,0xAEF8, 0xAEF9,0xAEFA,0xAEFB,0xAEFC,0xAEFD,0xAEFE,0xAFA1,0xAFA2, 0xAFA3,0xAFA4,0xAFA5,0xAFA6,0xAFA7,0xAFA8,0xAFA9,0xAFAA, 0xAFAB,0xAFAC,0xAFAD,0xAFAE,0xAFAF,0xAFB0,0xAFB1,0xAFB2, 0xAFB3,0xAFB4,0xAFB5,0xAFB6,0xAFB7,0xAFB8,0xAFB9,0xAFBA, 0xAFBB,0xAFBC,0xAFBD,0xAFBE,0xAFBF,0xAFC0,0xAFC1,0xAFC2, 0xAFC3,0xAFC4,0xAFC5,0xAFC6,0xAFC7,0xAFC8,0xAFC9,0xAFCA, 0xAFCB,0xAFCC,0xAFCD,0xAFCE,0xAFCF,0xAFD0,0xAFD1,0xAFD2, 0xAFD3,0xAFD4,0xAFD5,0xAFD6,0xAFD7,0xAFD8,0xAFD9,0xAFDA, 0xAFDB,0xAFDC,0xAFDD,0xAFDE,0xAFDF,0xAFE0,0xAFE1,0xAFE2, 0xAFE3,0xAFE4,0xAFE5,0xAFE6,0xAFE7,0xAFE8,0xAFE9,0xAFEA, 0xAFEB,0xAFEC,0xAFED,0xAFEE,0xAFEF,0xAFF0,0xAFF1,0xAFF2, 0xAFF3,0xAFF4,0xAFF5,0xAFF6,0xAFF7,0xAFF8,0xAFF9,0xAFFA, 0xAFFB,0xAFFC,0xAFFD,0xAFFE,0xF8A1,0xF8A2,0xF8A3,0xF8A4, 0xF8A5,0xF8A6,0xF8A7,0xF8A8,0xF8A9,0xF8AA,0xF8AB,0xF8AC, 0xF8AD,0xF8AE,0xF8AF,0xF8B0,0xF8B1,0xF8B2,0xF8B3,0xF8B4, 0xF8B5,0xF8B6,0xF8B7,0xF8B8,0xF8B9,0xF8BA,0xF8BB,0xF8BC, 0xF8BD,0xF8BE,0xF8BF,0xF8C0,0xF8C1,0xF8C2,0xF8C3,0xF8C4, 0xF8C5,0xF8C6,0xF8C7,0xF8C8,0xF8C9,0xF8CA,0xF8CB,0xF8CC, 0xF8CD,0xF8CE,0xF8CF,0xF8D0,0xF8D1,0xF8D2,0xF8D3,0xF8D4, 0xF8D5,0xF8D6,0xF8D7,0xF8D8,0xF8D9,0xF8DA,0xF8DB,0xF8DC, 0xF8DD,0xF8DE,0xF8DF,0xF8E0,0xF8E1,0xF8E2,0xF8E3,0xF8E4, 0xF8E5,0xF8E6,0xF8E7,0xF8E8,0xF8E9,0xF8EA,0xF8EB,0xF8EC, 0xF8ED,0xF8EE,0xF8EF,0xF8F0,0xF8F1,0xF8F2,0xF8F3,0xF8F4, 0xF8F5,0xF8F6,0xF8F7,0xF8F8,0xF8F9,0xF8FA,0xF8FB,0xF8FC, 0xF8FD,0xF8FE,0xF9A1,0xF9A2,0xF9A3,0xF9A4,0xF9A5,0xF9A6, 0xF9A7,0xF9A8,0xF9A9,0xF9AA,0xF9AB,0xF9AC,0xF9AD,0xF9AE, 0xF9AF,0xF9B0,0xF9B1,0xF9B2,0xF9B3,0xF9B4,0xF9B5,0xF9B6, 0xF9B7,0xF9B8,0xF9B9,0xF9BA,0xF9BB,0xF9BC,0xF9BD,0xF9BE, 0xF9BF,0xF9C0,0xF9C1,0xF9C2,0xF9C3,0xF9C4,0xF9C5,0xF9C6, 0xF9C7,0xF9C8,0xF9C9,0xF9CA,0xF9CB,0xF9CC,0xF9CD,0xF9CE, 0xF9CF,0xF9D0,0xF9D1,0xF9D2,0xF9D3,0xF9D4,0xF9D5,0xF9D6, 0xF9D7,0xF9D8,0xF9D9,0xF9DA,0xF9DB,0xF9DC,0xF9DD,0xF9DE, 0xF9DF,0xF9E0,0xF9E1,0xF9E2,0xF9E3,0xF9E4,0xF9E5,0xF9E6, 0xF9E7,0xF9E8,0xF9E9,0xF9EA,0xF9EB,0xF9EC,0xF9ED,0xF9EE, 0xF9EF,0xF9F0,0xF9F1,0xF9F2,0xF9F3,0xF9F4,0xF9F5,0xF9F6, 0xF9F7,0xF9F8,0xF9F9,0xF9FA,0xF9FB,0xF9FC,0xF9FD,0xF9FE, 0xFAA1,0xFAA2,0xFAA3,0xFAA4,0xFAA5,0xFAA6,0xFAA7,0xFAA8, 0xFAA9,0xFAAA,0xFAAB,0xFAAC,0xFAAD,0xFAAE,0xFAAF,0xFAB0, 0xFAB1,0xFAB2,0xFAB3,0xFAB4,0xFAB5,0xFAB6,0xFAB7,0xFAB8, 0xFAB9,0xFABA,0xFABB,0xFABC,0xFABD,0xFABE,0xFABF,0xFAC0, 0xFAC1,0xFAC2,0xFAC3,0xFAC4,0xFAC5,0xFAC6,0xFAC7,0xFAC8, 0xFAC9,0xFACA,0xFACB,0xFACC,0xFACD,0xFACE,0xFACF,0xFAD0, 0xFAD1,0xFAD2,0xFAD3,0xFAD4,0xFAD5,0xFAD6,0xFAD7,0xFAD8, 0xFAD9,0xFADA,0xFADB,0xFADC,0xFADD,0xFADE,0xFADF,0xFAE0, 0xFAE1,0xFAE2,0xFAE3,0xFAE4,0xFAE5,0xFAE6,0xFAE7,0xFAE8, 0xFAE9,0xFAEA,0xFAEB,0xFAEC,0xFAED,0xFAEE,0xFAEF,0xFAF0, 0xFAF1,0xFAF2,0xFAF3,0xFAF4,0xFAF5,0xFAF6,0xFAF7,0xFAF8, 0xFAF9,0xFAFA,0xFAFB,0xFAFC,0xFAFD,0xFAFE,0xFBA1,0xFBA2, 0xFBA3,0xFBA4,0xFBA5,0xFBA6,0xFBA7,0xFBA8,0xFBA9,0xFBAA, 0xFBAB,0xFBAC,0xFBAD,0xFBAE,0xFBAF,0xFBB0,0xFBB1,0xFBB2, 0xFBB3,0xFBB4,0xFBB5,0xFBB6,0xFBB7,0xFBB8,0xFBB9,0xFBBA, 0xFBBB,0xFBBC,0xFBBD,0xFBBE,0xFBBF,0xFBC0,0xFBC1,0xFBC2, 0xFBC3,0xFBC4,0xFBC5,0xFBC6,0xFBC7,0xFBC8,0xFBC9,0xFBCA, 0xFBCB,0xFBCC,0xFBCD,0xFBCE,0xFBCF,0xFBD0,0xFBD1,0xFBD2, 0xFBD3,0xFBD4,0xFBD5,0xFBD6,0xFBD7,0xFBD8,0xFBD9,0xFBDA, 0xFBDB,0xFBDC,0xFBDD,0xFBDE,0xFBDF,0xFBE0,0xFBE1,0xFBE2, 0xFBE3,0xFBE4,0xFBE5,0xFBE6,0xFBE7,0xFBE8,0xFBE9,0xFBEA, 0xFBEB,0xFBEC,0xFBED,0xFBEE,0xFBEF,0xFBF0,0xFBF1,0xFBF2, 0xFBF3,0xFBF4,0xFBF5,0xFBF6,0xFBF7,0xFBF8,0xFBF9,0xFBFA, 0xFBFB,0xFBFC,0xFBFD,0xFBFE,0xFCA1,0xFCA2,0xFCA3,0xFCA4, 0xFCA5,0xFCA6,0xFCA7,0xFCA8,0xFCA9,0xFCAA,0xFCAB,0xFCAC, 0xFCAD,0xFCAE,0xFCAF,0xFCB0,0xFCB1,0xFCB2,0xFCB3,0xFCB4, 0xFCB5,0xFCB6,0xFCB7,0xFCB8,0xFCB9,0xFCBA,0xFCBB,0xFCBC, 0xFCBD,0xFCBE,0xFCBF,0xFCC0,0xFCC1,0xFCC2,0xFCC3,0xFCC4, 0xFCC5,0xFCC6,0xFCC7,0xFCC8,0xFCC9,0xFCCA,0xFCCB,0xFCCC, 0xFCCD,0xFCCE,0xFCCF,0xFCD0,0xFCD1,0xFCD2,0xFCD3,0xFCD4, 0xFCD5,0xFCD6,0xFCD7,0xFCD8,0xFCD9,0xFCDA,0xFCDB,0xFCDC, 0xFCDD,0xFCDE,0xFCDF,0xFCE0,0xFCE1,0xFCE2,0xFCE3,0xFCE4, 0xFCE5,0xFCE6,0xFCE7,0xFCE8,0xFCE9,0xFCEA,0xFCEB,0xFCEC, 0xFCED,0xFCEE,0xFCEF,0xFCF0,0xFCF1,0xFCF2,0xFCF3,0xFCF4, 0xFCF5,0xFCF6,0xFCF7,0xFCF8,0xFCF9,0xFCFA,0xFCFB,0xFCFC, 0xFCFD,0xFCFE,0xFDA1,0xFDA2,0xFDA3,0xFDA4,0xFDA5,0xFDA6, 0xFDA7,0xFDA8,0xFDA9,0xFDAA,0xFDAB,0xFDAC,0xFDAD,0xFDAE, 0xFDAF,0xFDB0,0xFDB1,0xFDB2,0xFDB3,0xFDB4,0xFDB5,0xFDB6, 0xFDB7,0xFDB8,0xFDB9,0xFDBA,0xFDBB,0xFDBC,0xFDBD,0xFDBE, 0xFDBF,0xFDC0,0xFDC1,0xFDC2,0xFDC3,0xFDC4,0xFDC5,0xFDC6, 0xFDC7,0xFDC8,0xFDC9,0xFDCA,0xFDCB,0xFDCC,0xFDCD,0xFDCE, 0xFDCF,0xFDD0,0xFDD1,0xFDD2,0xFDD3,0xFDD4,0xFDD5,0xFDD6, 0xFDD7,0xFDD8,0xFDD9,0xFDDA,0xFDDB,0xFDDC,0xFDDD,0xFDDE, 0xFDDF,0xFDE0,0xFDE1,0xFDE2,0xFDE3,0xFDE4,0xFDE5,0xFDE6, 0xFDE7,0xFDE8,0xFDE9,0xFDEA,0xFDEB,0xFDEC,0xFDED,0xFDEE, 0xFDEF,0xFDF0,0xFDF1,0xFDF2,0xFDF3,0xFDF4,0xFDF5,0xFDF6, 0xFDF7,0xFDF8,0xFDF9,0xFDFA,0xFDFB,0xFDFC,0xFDFD,0xFDFE, 0xFEA1,0xFEA2,0xFEA3,0xFEA4,0xFEA5,0xFEA6,0xFEA7,0xFEA8, 0xFEA9,0xFEAA,0xFEAB,0xFEAC,0xFEAD,0xFEAE,0xFEAF,0xFEB0, 0xFEB1,0xFEB2,0xFEB3,0xFEB4,0xFEB5,0xFEB6,0xFEB7,0xFEB8, 0xFEB9,0xFEBA,0xFEBB,0xFEBC,0xFEBD,0xFEBE,0xFEBF,0xFEC0, 0xFEC1,0xFEC2,0xFEC3,0xFEC4,0xFEC5,0xFEC6,0xFEC7,0xFEC8, 0xFEC9,0xFECA,0xFECB,0xFECC,0xFECD,0xFECE,0xFECF,0xFED0, 0xFED1,0xFED2,0xFED3,0xFED4,0xFED5,0xFED6,0xFED7,0xFED8, 0xFED9,0xFEDA,0xFEDB,0xFEDC,0xFEDD,0xFEDE,0xFEDF,0xFEE0, 0xFEE1,0xFEE2,0xFEE3,0xFEE4,0xFEE5,0xFEE6,0xFEE7,0xFEE8, 0xFEE9,0xFEEA,0xFEEB,0xFEEC,0xFEED,0xFEEE,0xFEEF,0xFEF0, 0xFEF1,0xFEF2,0xFEF3,0xFEF4,0xFEF5,0xFEF6,0xFEF7,0xFEF8, 0xFEF9,0xFEFA,0xFEFB,0xFEFC,0xFEFD,0xFEFE,0xA140,0xA141, 0xA142,0xA143,0xA144,0xA145,0xA146,0xA147,0xA148,0xA149, 0xA14A,0xA14B,0xA14C,0xA14D,0xA14E,0xA14F,0xA150,0xA151, 0xA152,0xA153,0xA154,0xA155,0xA156,0xA157,0xA158,0xA159, 0xA15A,0xA15B,0xA15C,0xA15D,0xA15E,0xA15F,0xA160,0xA161, 0xA162,0xA163,0xA164,0xA165,0xA166,0xA167,0xA168,0xA169, 0xA16A,0xA16B,0xA16C,0xA16D,0xA16E,0xA16F,0xA170,0xA171, 0xA172,0xA173,0xA174,0xA175,0xA176,0xA177,0xA178,0xA179, 0xA17A,0xA17B,0xA17C,0xA17D,0xA17E,0xA180,0xA181,0xA182, 0xA183,0xA184,0xA185,0xA186,0xA187,0xA188,0xA189,0xA18A, 0xA18B,0xA18C,0xA18D,0xA18E,0xA18F,0xA190,0xA191,0xA192, 0xA193,0xA194,0xA195,0xA196,0xA197,0xA198,0xA199,0xA19A, 0xA19B,0xA19C,0xA19D,0xA19E,0xA19F,0xA1A0,0xA240,0xA241, 0xA242,0xA243,0xA244,0xA245,0xA246,0xA247,0xA248,0xA249, 0xA24A,0xA24B,0xA24C,0xA24D,0xA24E,0xA24F,0xA250,0xA251, 0xA252,0xA253,0xA254,0xA255,0xA256,0xA257,0xA258,0xA259, 0xA25A,0xA25B,0xA25C,0xA25D,0xA25E,0xA25F,0xA260,0xA261, 0xA262,0xA263,0xA264,0xA265,0xA266,0xA267,0xA268,0xA269, 0xA26A,0xA26B,0xA26C,0xA26D,0xA26E,0xA26F,0xA270,0xA271, 0xA272,0xA273,0xA274,0xA275,0xA276,0xA277,0xA278,0xA279, 0xA27A,0xA27B,0xA27C,0xA27D,0xA27E,0xA280,0xA281,0xA282, 0xA283,0xA284,0xA285,0xA286,0xA287,0xA288,0xA289,0xA28A, 0xA28B,0xA28C,0xA28D,0xA28E,0xA28F,0xA290,0xA291,0xA292, 0xA293,0xA294,0xA295,0xA296,0xA297,0xA298,0xA299,0xA29A, 0xA29B,0xA29C,0xA29D,0xA29E,0xA29F,0xA2A0,0xA2AB,0xA2AC, 0xA2AD,0xA2AE,0xA2AF,0xA2B0,0xA2E3,0xA2E4,0xA2EF,0xA2F0, 0xA2FD,0xA2FE,0xA340,0xA341,0xA342,0xA343,0xA344,0xA345, 0xA346,0xA347,0xA348,0xA349,0xA34A,0xA34B,0xA34C,0xA34D, 0xA34E,0xA34F,0xA350,0xA351,0xA352,0xA353,0xA354,0xA355, 0xA356,0xA357,0xA358,0xA359,0xA35A,0xA35B,0xA35C,0xA35D, 0xA35E,0xA35F,0xA360,0xA361,0xA362,0xA363,0xA364,0xA365, 0xA366,0xA367,0xA368,0xA369,0xA36A,0xA36B,0xA36C,0xA36D, 0xA36E,0xA36F,0xA370,0xA371,0xA372,0xA373,0xA374,0xA375, 0xA376,0xA377,0xA378,0xA379,0xA37A,0xA37B,0xA37C,0xA37D, 0xA37E,0xA380,0xA381,0xA382,0xA383,0xA384,0xA385,0xA386, 0xA387,0xA388,0xA389,0xA38A,0xA38B,0xA38C,0xA38D,0xA38E, 0xA38F,0xA390,0xA391,0xA392,0xA393,0xA394,0xA395,0xA396, 0xA397,0xA398,0xA399,0xA39A,0xA39B,0xA39C,0xA39D,0xA39E, 0xA39F,0xA3A0,0xA440,0xA441,0xA442,0xA443,0xA444,0xA445, 0xA446,0xA447,0xA448,0xA449,0xA44A,0xA44B,0xA44C,0xA44D, 0xA44E,0xA44F,0xA450,0xA451,0xA452,0xA453,0xA454,0xA455, 0xA456,0xA457,0xA458,0xA459,0xA45A,0xA45B,0xA45C,0xA45D, 0xA45E,0xA45F,0xA460,0xA461,0xA462,0xA463,0xA464,0xA465, 0xA466,0xA467,0xA468,0xA469,0xA46A,0xA46B,0xA46C,0xA46D, 0xA46E,0xA46F,0xA470,0xA471,0xA472,0xA473,0xA474,0xA475, 0xA476,0xA477,0xA478,0xA479,0xA47A,0xA47B,0xA47C,0xA47D, 0xA47E,0xA480,0xA481,0xA482,0xA483,0xA484,0xA485,0xA486, 0xA487,0xA488,0xA489,0xA48A,0xA48B,0xA48C,0xA48D,0xA48E, 0xA48F,0xA490,0xA491,0xA492,0xA493,0xA494,0xA495,0xA496, 0xA497,0xA498,0xA499,0xA49A,0xA49B,0xA49C,0xA49D,0xA49E, 0xA49F,0xA4A0,0xA4F4,0xA4F5,0xA4F6,0xA4F7,0xA4F8,0xA4F9, 0xA4FA,0xA4FB,0xA4FC,0xA4FD,0xA4FE,0xA540,0xA541,0xA542, 0xA543,0xA544,0xA545,0xA546,0xA547,0xA548,0xA549,0xA54A, 0xA54B,0xA54C,0xA54D,0xA54E,0xA54F,0xA550,0xA551,0xA552, 0xA553,0xA554,0xA555,0xA556,0xA557,0xA558,0xA559,0xA55A, 0xA55B,0xA55C,0xA55D,0xA55E,0xA55F,0xA560,0xA561,0xA562, 0xA563,0xA564,0xA565,0xA566,0xA567,0xA568,0xA569,0xA56A, 0xA56B,0xA56C,0xA56D,0xA56E,0xA56F,0xA570,0xA571,0xA572, 0xA573,0xA574,0xA575,0xA576,0xA577,0xA578,0xA579,0xA57A, 0xA57B,0xA57C,0xA57D,0xA57E,0xA580,0xA581,0xA582,0xA583, 0xA584,0xA585,0xA586,0xA587,0xA588,0xA589,0xA58A,0xA58B, 0xA58C,0xA58D,0xA58E,0xA58F,0xA590,0xA591,0xA592,0xA593, 0xA594,0xA595,0xA596,0xA597,0xA598,0xA599,0xA59A,0xA59B, 0xA59C,0xA59D,0xA59E,0xA59F,0xA5A0,0xA5F7,0xA5F8,0xA5F9, 0xA5FA,0xA5FB,0xA5FC,0xA5FD,0xA5FE,0xA640,0xA641,0xA642, 0xA643,0xA644,0xA645,0xA646,0xA647,0xA648,0xA649,0xA64A, 0xA64B,0xA64C,0xA64D,0xA64E,0xA64F,0xA650,0xA651,0xA652, 0xA653,0xA654,0xA655,0xA656,0xA657,0xA658,0xA659,0xA65A, 0xA65B,0xA65C,0xA65D,0xA65E,0xA65F,0xA660,0xA661,0xA662, 0xA663,0xA664,0xA665,0xA666,0xA667,0xA668,0xA669,0xA66A, 0xA66B,0xA66C,0xA66D,0xA66E,0xA66F,0xA670,0xA671,0xA672, 0xA673,0xA674,0xA675,0xA676,0xA677,0xA678,0xA679,0xA67A, 0xA67B,0xA67C,0xA67D,0xA67E,0xA680,0xA681,0xA682,0xA683, 0xA684,0xA685,0xA686,0xA687,0xA688,0xA689,0xA68A,0xA68B, 0xA68C,0xA68D,0xA68E,0xA68F,0xA690,0xA691,0xA692,0xA693, 0xA694,0xA695,0xA696,0xA697,0xA698,0xA699,0xA69A,0xA69B, 0xA69C,0xA69D,0xA69E,0xA69F,0xA6A0,0xA6B9,0xA6BA,0xA6BB, 0xA6BC,0xA6BD,0xA6BE,0xA6BF,0xA6C0,0xA6D9,0xA6DA,0xA6DB, 0xA6DC,0xA6DD,0xA6DE,0xA6DF,0xA6EC,0xA6ED,0xA6F3,0xA6F6, 0xA6F7,0xA6F8,0xA6F9,0xA6FA,0xA6FB,0xA6FC,0xA6FD,0xA6FE, 0xA740,0xA741,0xA742,0xA743,0xA744,0xA745,0xA746,0xA747, 0xA748,0xA749,0xA74A,0xA74B,0xA74C,0xA74D,0xA74E,0xA74F, 0xA750,0xA751,0xA752,0xA753,0xA754,0xA755,0xA756,0xA757, 0xA758,0xA759,0xA75A,0xA75B,0xA75C,0xA75D,0xA75E,0xA75F, 0xA760,0xA761,0xA762,0xA763,0xA764,0xA765,0xA766,0xA767, 0xA768,0xA769,0xA76A,0xA76B,0xA76C,0xA76D,0xA76E,0xA76F, 0xA770,0xA771,0xA772,0xA773,0xA774,0xA775,0xA776,0xA777, 0xA778,0xA779,0xA77A,0xA77B,0xA77C,0xA77D,0xA77E,0xA780, 0xA781,0xA782,0xA783,0xA784,0xA785,0xA786,0xA787,0xA788, 0xA789,0xA78A,0xA78B,0xA78C,0xA78D,0xA78E,0xA78F,0xA790, 0xA791,0xA792,0xA793,0xA794,0xA795,0xA796,0xA797,0xA798, 0xA799,0xA79A,0xA79B,0xA79C,0xA79D,0xA79E,0xA79F,0xA7A0, 0xA7C2,0xA7C3,0xA7C4,0xA7C5,0xA7C6,0xA7C7,0xA7C8,0xA7C9, 0xA7CA,0xA7CB,0xA7CC,0xA7CD,0xA7CE,0xA7CF,0xA7D0,0xA7F2, 0xA7F3,0xA7F4,0xA7F5,0xA7F6,0xA7F7,0xA7F8,0xA7F9,0xA7FA, 0xA7FB,0xA7FC,0xA7FD,0xA7FE,0xA896,0xA897,0xA898,0xA899, 0xA89A,0xA89B,0xA89C,0xA89D,0xA89E,0xA89F,0xA8A0,0xA8BC, 0xA8BF,0xA8C1,0xA8C2,0xA8C3,0xA8C4,0xA8EA,0xA8EB,0xA8EC, 0xA8ED,0xA8EE,0xA8EF,0xA8F0,0xA8F1,0xA8F2,0xA8F3,0xA8F4, 0xA8F5,0xA8F6,0xA8F7,0xA8F8,0xA8F9,0xA8FA,0xA8FB,0xA8FC, 0xA8FD,0xA8FE,0xA958,0xA95B,0xA95D,0xA95E,0xA95F,0xA989, 0xA98A,0xA98B,0xA98C,0xA98D,0xA98E,0xA98F,0xA990,0xA991, 0xA992,0xA993,0xA994,0xA995,0xA997,0xA998,0xA999,0xA99A, 0xA99B,0xA99C,0xA99D,0xA99E,0xA99F,0xA9A0,0xA9A1,0xA9A2, 0xA9A3,0xA9F0,0xA9F1,0xA9F2,0xA9F3,0xA9F4,0xA9F5,0xA9F6, 0xA9F7,0xA9F8,0xA9F9,0xA9FA,0xA9FB,0xA9FC,0xA9FD,0xA9FE, 0xD7FA,0xD7FB,0xD7FC,0xD7FD,0xD7FE,0xFE50,0xFE51,0xFE52, 0xFE53,0xFE54,0xFE55,0xFE56,0xFE57,0xFE58,0xFE59,0xFE5A, 0xFE5B,0xFE5C,0xFE5D,0xFE5E,0xFE5F,0xFE60,0xFE61,0xFE62, 0xFE63,0xFE64,0xFE65,0xFE66,0xFE67,0xFE68,0xFE69,0xFE6A, 0xFE6B,0xFE6C,0xFE6D,0xFE6E,0xFE6F,0xFE70,0xFE71,0xFE72, 0xFE73,0xFE74,0xFE75,0xFE76,0xFE77,0xFE78,0xFE79,0xFE7A, 0xFE7B,0xFE7C,0xFE7D,0xFE7E,0xFE80,0xFE81,0xFE82,0xFE83, 0xFE84,0xFE85,0xFE86,0xFE87,0xFE88,0xFE89,0xFE8A,0xFE8B, 0xFE8C,0xFE8D,0xFE8E,0xFE8F,0xFE90,0xFE91,0xFE92,0xFE93, 0xFE94,0xFE95,0xFE96,0xFE97,0xFE98,0xFE99,0xFE9A,0xFE9B, 0xFE9C,0xFE9D,0xFE9E,0xFE9F,0xFEA0,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0xFD9C,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0xFD9D,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0xFD9E,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xFD9F, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0xFDA0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0xFE40,0xFE41,0xFE42,0xFE43, 0x0000,0xFE44,0x0000,0xFE45,0xFE46,0x0000,0x0000,0x0000, 0xFE47,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xFE48, 0xFE49,0xFE4A,0x0000,0xFE4B,0xFE4C,0x0000,0x0000,0xFE4D, 0xFE4E,0xFE4F,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA955,0xA6F2,0x0000,0xA6F4,0xA6F5,0xA6E0,0xA6E1,0xA6F0, 0xA6F1,0xA6E2,0xA6E3,0xA6EE,0xA6EF,0xA6E6,0xA6E7,0xA6E4, 0xA6E5,0xA6E8,0xA6E9,0xA6EA,0xA6EB,0x0000,0x0000,0x0000, 0x0000,0xA968,0xA969,0xA96A,0xA96B,0xA96C,0xA96D,0xA96E, 0xA96F,0xA970,0xA971,0x0000,0xA972,0xA973,0xA974,0xA975, 0x0000,0xA976,0xA977,0xA978,0xA979,0xA97A,0xA97B,0xA97C, 0xA97D,0xA97E,0xA980,0xA981,0xA982,0xA983,0xA984,0x0000, 0xA985,0xA986,0xA987,0xA988,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0xA3A1,0xA3A2,0xA3A3,0xA1E7,0xA3A5,0xA3A6,0xA3A7, 0xA3A8,0xA3A9,0xA3AA,0xA3AB,0xA3AC,0xA3AD,0xA3AE,0xA3AF, 0xA3B0,0xA3B1,0xA3B2,0xA3B3,0xA3B4,0xA3B5,0xA3B6,0xA3B7, 0xA3B8,0xA3B9,0xA3BA,0xA3BB,0xA3BC,0xA3BD,0xA3BE,0xA3BF, 0xA3C0,0xA3C1,0xA3C2,0xA3C3,0xA3C4,0xA3C5,0xA3C6,0xA3C7, 0xA3C8,0xA3C9,0xA3CA,0xA3CB,0xA3CC,0xA3CD,0xA3CE,0xA3CF, 0xA3D0,0xA3D1,0xA3D2,0xA3D3,0xA3D4,0xA3D5,0xA3D6,0xA3D7, 0xA3D8,0xA3D9,0xA3DA,0xA3DB,0xA3DC,0xA3DD,0xA3DE,0xA3DF, 0xA3E0,0xA3E1,0xA3E2,0xA3E3,0xA3E4,0xA3E5,0xA3E6,0xA3E7, 0xA3E8,0xA3E9,0xA3EA,0xA3EB,0xA3EC,0xA3ED,0xA3EE,0xA3EF, 0xA3F0,0xA3F1,0xA3F2,0xA3F3,0xA3F4,0xA3F5,0xA3F6,0xA3F7, 0xA3F8,0xA3F9,0xA3FA,0xA3FB,0xA3FC,0xA3FD,0xA1AB,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0xA1E9,0xA1EA,0xA956,0xA3FE,0xA957,0xA3A4,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000 }; #define LENGTH_OF(x) sizeof(x)/sizeof((x)[0]) #define TABLE_CONSTANT(x) {(void*)(x), LENGTH_OF(x)} const static uint16 gbkDecoderIndex1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0 }; const uint32 REPLACE_CHAR = 0xFFFD; static Table gbkDecoderIndex2[] = { TABLE_CONSTANT(gbkDecoderInnerIndex0), TABLE_CONSTANT(gbkDecoderInnerIndex1), TABLE_CONSTANT(gbkDecoderInnerIndex2), TABLE_CONSTANT(gbkDecoderInnerIndex3), TABLE_CONSTANT(gbkDecoderInnerIndex4), TABLE_CONSTANT(gbkDecoderInnerIndex5), TABLE_CONSTANT(gbkDecoderInnerIndex6), TABLE_CONSTANT(gbkDecoderInnerIndex7) }; static Table gbkDecoderTable1 = TABLE_CONSTANT(gbkDecoderIndex1); static Table gbkDecoderTable2 = TABLE_CONSTANT(gbkDecoderIndex2); const static uint16 gbkEncoderIndex1[] = { 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 100, 101, 102, 103, 104, 105, 106, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, 109, 0, 0, 0, 110, 111 }; static Table gbkEncoderIndex2[] = { TABLE_CONSTANT(gbkEncoderInnerIndex0), TABLE_CONSTANT(gbkEncoderInnerIndex1), TABLE_CONSTANT(gbkEncoderInnerIndex2), TABLE_CONSTANT(gbkEncoderInnerIndex3), TABLE_CONSTANT(gbkEncoderInnerIndex4), TABLE_CONSTANT(gbkEncoderInnerIndex5), TABLE_CONSTANT(gbkEncoderInnerIndex6) }; static Table gbkEncoderTable1 = TABLE_CONSTANT(gbkEncoderIndex1); static Table gbkEncoderTable2 = TABLE_CONSTANT(gbkEncoderIndex2); /* end of trans_tables.def */ engine/transcoding.cpp000066400000000000000000001501021176363201700153470ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: transcoding.cpp Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer ago 23 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Short description */ #include #include #include #include #include #include #include #include #include #include namespace Falcon { #include "trans_tables.h" /** UTF-16LE encoding transcoder. */ class TranscoderUTF16LE: public TranscoderUTF16 { public: TranscoderUTF16LE( Stream *s, bool bOwn=false ): TranscoderUTF16( s, bOwn, e_le ) { m_bom = false; } virtual const String encoding() const { return "utf-16LE"; } }; /** UTF-16BE encoding transcoder. */ class TranscoderUTF16BE: public TranscoderUTF16 { public: TranscoderUTF16BE( Stream *s, bool bOwn=false ): TranscoderUTF16( s, bOwn, e_be ) { m_bom = false; } virtual const String encoding() const { return "utf-16BE"; } }; /** GBK encoding transcoder. */ class TranscoderGBK: public Transcoder { private: Table *decoderTable1; Table *decoderTable2; Table *encoderTable1; Table *encoderTable2; const static uint32 start = 0x40; const static uint32 end = 0xFE; public: TranscoderGBK( const TranscoderGBK &other ): Transcoder( other ), decoderTable1( other.decoderTable1 ), decoderTable2( other.decoderTable2 ), encoderTable1( other.encoderTable1 ), encoderTable2( other.encoderTable2 ) {} public: TranscoderGBK( Stream *s, bool bOwn=false ): Transcoder(s, bOwn) { decoderTable1 = &gbkDecoderTable1; decoderTable2 = &gbkDecoderTable2; encoderTable1 = &gbkEncoderTable1; encoderTable2 = &gbkEncoderTable2; } private: inline uint16 getUint16(Table *t, uint32 pos) { return ( (uint16*)t->table )[pos]; } inline Table *getTable(Table *t, uint32 pos) { return ( (Table*)t->table ) + pos; } uint32 decodeDouble(uint32 byte1, uint32 byte2) { if ( (byte2 < start) || (byte2 > end) ) return REPLACE_CHAR; int n0 = getUint16(decoderTable1, byte1) & 0xf; int n = n0 * (end - start + 1) + (byte2 - start); int ti = getUint16(decoderTable1, byte1)>>4; // Table 2 Index Table *charTable = getTable(decoderTable2, ti); return getUint16(charTable, n); } uint32 encodeDouble(uint32 ch) { int offset = getUint16(encoderTable1, ((ch & 0xff00) >> 8 )) << 8; Table *charTable = getTable(encoderTable2, offset >> 12); return getUint16(charTable, (offset & 0xfff) + (ch & 0xff)); } public: virtual const String encoding() const { return "gbk"; } virtual TranscoderGBK *clone() const { return new TranscoderGBK( *this ); } public: virtual bool get( uint32 &chr ) { m_parseStatus = true; if( popBuffer( chr ) ) return true; // converting the byte array into an unicode. byte b1; byte b2=0; if ( m_stream->read( &b1, 1 ) != 1 ) return false; if (b1 < 0x80) { chr = b1; } else if ( b1&0x7F ) { if ( b1^0xff ) { if (m_stream->read( &b2, 1 ) != 1) return false; chr = decodeDouble(b1, b2); } else { chr = 0xf8f5; } } else { chr = 0x20ac; } if (chr == REPLACE_CHAR) { m_parseStatus = false; } return true; } virtual bool put( uint32 chr ) { m_parseStatus = true; byte bPtr[2]; if ( chr < 0x80 ) { bPtr[0] = (byte)chr; return ( m_stream->write( bPtr, 1 ) == 1 ); } uint32 ncode = encodeDouble( chr ); if ( ncode == 0 || chr == 0 ) { m_parseStatus = false; return true; } bPtr[0] = (byte)( (ncode & 0xff00) >> 8 ); bPtr[1] = (byte)( ncode ); if (bPtr[0]>0) return ( m_stream->write(bPtr, 2) == 2 ); else return ( m_stream->write(bPtr + 1, 1) == 1 ); } }; /** Base class for codepage like transcoders. */ class TranscoderISO_CP: public Transcoder { protected: TranscoderISO_CP( Stream *s, bool bOwn=false ): Transcoder( s, bOwn ) { m_nMinUntl = 0x0; m_nMaxUntl = 0x9F; } TranscoderISO_CP( const TranscoderISO_CP &other ); uint16 *m_directTable; uint32 m_dirTabSize; CP_ISO_UINT_TABLE *m_reverseTable; uint32 m_revTabSize; uint32 m_nMinUntl; uint32 m_nMaxUntl; public: virtual bool get( uint32 &chr ); virtual bool put( uint32 chr ); }; class TranscoderOEM: public TranscoderISO_CP { protected: TranscoderOEM( Stream *s, bool bOwn=false ): TranscoderISO_CP( s, bOwn ) { m_nMinUntl = 0x00; m_nMaxUntl = 0x7F; } TranscoderOEM( const TranscoderOEM &other ); public: virtual bool get( uint32 &chr ); }; class TranscoderIBM437:public TranscoderOEM { public: TranscoderIBM437( const TranscoderIBM437 &other ): TranscoderOEM( other ) {} TranscoderIBM437( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "IBM437"; } virtual TranscoderIBM437 *clone() const; }; class TranscoderCP1252:public TranscoderISO_CP { public: TranscoderCP1252( const TranscoderCP1252 &other ): TranscoderISO_CP( other ) {} TranscoderCP1252( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "cp1252"; } virtual TranscoderCP1252 *clone() const; }; class TranscoderIBM850:public TranscoderOEM { public: TranscoderIBM850( const TranscoderIBM850 &other ): TranscoderOEM( other ) {} TranscoderIBM850( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "IBM850"; } virtual TranscoderIBM850 *clone() const; }; /** Latin-1 (ISO8859_1) transcoder. */ class TranscoderISO8859_1: public TranscoderISO_CP { public: TranscoderISO8859_1( const TranscoderISO8859_1 &other ): TranscoderISO_CP( other ) {} TranscoderISO8859_1( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "iso8859-1"; } virtual TranscoderISO8859_1 *clone() const; }; class TranscoderISO8859_2: public TranscoderISO_CP { public: TranscoderISO8859_2( const TranscoderISO8859_2 &other ): TranscoderISO_CP( other ) {} TranscoderISO8859_2( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "iso8859-2"; } virtual TranscoderISO8859_2 *clone() const; }; class TranscoderISO8859_3: public TranscoderISO_CP { public: TranscoderISO8859_3( const TranscoderISO8859_3 &other ): TranscoderISO_CP( other ) {} TranscoderISO8859_3( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "iso8859-3"; } virtual TranscoderISO8859_3 *clone() const; }; class TranscoderISO8859_4: public TranscoderISO_CP { public: TranscoderISO8859_4( const TranscoderISO8859_4 &other ): TranscoderISO_CP( other ) {} TranscoderISO8859_4( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "iso8859-4"; } virtual TranscoderISO8859_4 *clone() const; }; class TranscoderISO8859_5: public TranscoderISO_CP { public: TranscoderISO8859_5( const TranscoderISO8859_5 &other ): TranscoderISO_CP( other ) {} TranscoderISO8859_5( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "iso8859-5"; } virtual TranscoderISO8859_5 *clone() const; }; class TranscoderISO8859_6: public TranscoderISO_CP { public: TranscoderISO8859_6( const TranscoderISO8859_6 &other ): TranscoderISO_CP( other ) {} TranscoderISO8859_6( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "iso8859-6"; } virtual TranscoderISO8859_6 *clone() const; }; class TranscoderISO8859_7: public TranscoderISO_CP { public: TranscoderISO8859_7( const TranscoderISO8859_7 &other ): TranscoderISO_CP( other ) {} TranscoderISO8859_7( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "iso8859-7"; } virtual TranscoderISO8859_7 *clone() const; }; class TranscoderISO8859_8: public TranscoderISO_CP { public: TranscoderISO8859_8( const TranscoderISO8859_8 &other ): TranscoderISO_CP( other ) {} TranscoderISO8859_8( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "iso8859-8"; } virtual TranscoderISO8859_8 *clone() const; }; class TranscoderISO8859_9: public TranscoderISO_CP { public: TranscoderISO8859_9( const TranscoderISO8859_9 &other ): TranscoderISO_CP( other ) {} TranscoderISO8859_9( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "iso8859-9"; } virtual TranscoderISO8859_9 *clone() const; }; class TranscoderISO8859_10: public TranscoderISO_CP { public: TranscoderISO8859_10( const TranscoderISO8859_10 &other ): TranscoderISO_CP( other ) {} TranscoderISO8859_10( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "iso8859-10"; } virtual TranscoderISO8859_10 *clone() const; }; class TranscoderISO8859_11: public TranscoderISO_CP { public: TranscoderISO8859_11( const TranscoderISO8859_11 &other ): TranscoderISO_CP( other ) {} TranscoderISO8859_11( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "iso8859-11"; } virtual TranscoderISO8859_11 *clone() const; }; class TranscoderISO8859_13: public TranscoderISO_CP { public: TranscoderISO8859_13( const TranscoderISO8859_13 &other ): TranscoderISO_CP( other ) {} TranscoderISO8859_13( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "iso8859-13"; } virtual TranscoderISO8859_13 *clone() const; }; class TranscoderISO8859_14: public TranscoderISO_CP { public: TranscoderISO8859_14( const TranscoderISO8859_14 &other ): TranscoderISO_CP( other ) {} TranscoderISO8859_14( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "iso8859-14"; } virtual TranscoderISO8859_14 *clone() const; }; class TranscoderISO8859_15: public TranscoderISO_CP { public: TranscoderISO8859_15( const TranscoderISO8859_15 &other ): TranscoderISO_CP( other ) {} TranscoderISO8859_15( Stream *s, bool bOwn=false ); virtual const String encoding() const { return "iso8859-15"; } virtual TranscoderISO8859_15 *clone() const; }; //============================================================================= // Transcoder class // Transcoder::Transcoder( Stream *s, bool bOwn ): Stream( t_proxy ), m_stream( s ), m_parseStatus( true ), m_streamOwner( bOwn ) { } Transcoder::Transcoder( const Transcoder &other ): Stream( other ), m_parseStatus( other.m_parseStatus ), m_streamOwner( other.m_streamOwner ) { if( m_streamOwner ) m_stream = static_cast(other.m_stream->clone()); else m_stream = other.m_stream; } Transcoder::~Transcoder() { if ( m_streamOwner ) delete m_stream; } void Transcoder::setUnderlying( Stream *s, bool owner ) { if ( m_streamOwner ) delete m_stream; m_stream = s; m_streamOwner = owner; } int64 Transcoder::seek( int64 pos, e_whence w ) { int64 res = m_stream->seek( pos, w ); m_status = m_stream->status(); return res; } int64 Transcoder::tell() { int64 res = m_stream->tell(); m_status = m_stream->status(); return res; } bool Transcoder::flush() { bool res = m_stream->flush(); m_status = m_stream->status(); return res; } bool Transcoder::truncate( int64 pos ) { bool ret = m_stream->truncate( pos ); m_status = m_stream->status(); return ret; } bool Transcoder::close() { bool ret = m_stream->close(); m_status = m_stream->status(); return ret; } bool Transcoder::writeString( const String &source, uint32 begin, uint32 end ) { uint32 pos = begin; bool exitStatus = true; if ( end > source.length() ) end = source.length(); while( pos < end ) { // some error in writing? if ( ! put( source.getCharAt( pos ) ) ) return false; // Status broken? -- record this and report later if ( exitStatus && ! m_parseStatus ) exitStatus = false; ++pos; } // write is complete, but status may be broken m_parseStatus = exitStatus; return true; } bool Transcoder::readString( String &source, uint32 size ) { bool exitStatus = true; source.manipulator( &csh::handler_buffer ); source.size(0); uint32 chr; while( size > 0 && get(chr) ) { // Status broken? -- record this and report later if ( exitStatus && ! m_parseStatus ) exitStatus = false; source.append( chr ); size--; } // write is complete, but status may be broken m_parseStatus = exitStatus; return exitStatus; } //============================================================================= // Transparent byte oriented transcoder. // TranscoderByte::TranscoderByte( const TranscoderByte &other ): Transcoder( other ), m_substitute( other.m_substitute ) {} bool TranscoderByte::get( uint32 &chr ) { m_parseStatus = true; if( popBuffer( chr ) ) return true; // we may have hit eof before if ( m_stream->eof() ) return false; byte b; if ( m_stream->read( &b, 1 ) == 1 ) { m_parseStatus = m_stream->good(); chr = (uint32) b; return true; } return false; } bool TranscoderByte::put( uint32 chr ) { m_parseStatus = true; byte b; if ( chr <= 0xFF ) { b = (byte) chr; } else { b = m_substitute; m_parseStatus = false; } return ( m_stream->write( &b, 1 ) == 1 ); } TranscoderByte *TranscoderByte::clone() const { return new TranscoderByte( *this ); } //============================================================================= // EOL transcoder // TranscoderEOL::TranscoderEOL( const TranscoderEOL &other ): Transcoder( other ) {} bool TranscoderEOL::get( uint32 &chr ) { if( popBuffer( chr ) ) return true; if ( m_stream->get( chr ) ) { if ( chr == (uint32) '\r' ) { if ( m_stream->get( chr ) ) { if ( chr != (uint32) '\n' ) { unget( (uint32) chr ); chr = (uint32) '\r'; } } else { chr = (uint32) '\r'; // reset chr } } return true; } return false; } bool TranscoderEOL::put( uint32 chr ) { if ( chr == (uint32)'\n' ) { if ( m_stream->put( (uint32)'\r' ) ) return m_stream->put( (uint32)'\n' ); else return false; } return m_stream->put( chr ); } TranscoderEOL *TranscoderEOL::clone() const { return new TranscoderEOL( *this ); } //============================================================================= // UTF-8 Class // TranscoderUTF8::TranscoderUTF8( const TranscoderUTF8 &other ): Transcoder( other ) {} bool TranscoderUTF8::get( uint32 &chr ) { m_parseStatus = true; if( popBuffer( chr ) ) return true; byte in; if ( m_stream->read( &in, 1 ) != 1 ) return false; // 4 bytes? -- pattern 1111 0xxx int count; if ( (in & 0xF8) == 0xF0 ) { chr = (in & 0x7 ) << 18; count = 18; } // pattern 1110 xxxx else if ( (in & 0xF0) == 0xE0 ) { chr = (in & 0xF) << 12; count = 12; } // pattern 110x xxxx else if ( (in & 0xE0) == 0xC0 ) { chr = (in & 0x1F) << 6; count = 6; } else if( in < 0x80 ) { chr = (uint32) in; return true; } // invalid pattern else { m_parseStatus = false; return false; } // read the other characters with pattern 0x10xx xxxx while( count > 0 ) { count -= 6; int res = m_stream->read( &in, 1 ); if ( res < 0 ) { // stream error? return false; } else if( res == 0 ) { // eof before complete? -- protocol error m_parseStatus = false; return false; } else if( (in & 0xC0) != 0x80 ) { // unrecognized pattern, protocol error m_parseStatus = false; return false; } chr |= (in & 0x3f) << count; } return true; } bool TranscoderUTF8::put( uint32 chr ) { m_parseStatus = true; byte res[4]; uint32 resCount; if ( chr < 0x80 ) { res[0] = (byte) chr; resCount = 1; } else if ( chr < 0x800 ) { res[0] = 0xC0 | ((chr >> 6 ) & 0x1f); res[1] = 0x80 | (0x3f & chr); resCount = 2; } else if ( chr < 0x10000 ) { res[0] = 0xE0 | ((chr >> 12) & 0x0f ); res[1] = 0x80 | ((chr >> 6) & 0x3f ); res[2] = 0x80 | (0x3f & chr); resCount = 3; } else { res[0] = 0xF0 | ((chr >> 18) & 0x7 ); res[1] = 0x80 | ((chr >> 12) & 0x3f ); res[2] = 0x80 | ((chr >> 6) & 0x3f ); res[3] = 0x80 | (0x3f & chr); resCount = 4; } return ( m_stream->write( res, resCount ) == (int32) resCount); } TranscoderUTF8 *TranscoderUTF8::clone() const { return new TranscoderUTF8( *this ); } //============================================================================= // UTF-16 Class // TranscoderUTF16::TranscoderUTF16( const TranscoderUTF16 &other ): Transcoder( other ), m_defEndian( other.m_defEndian ), m_bFirstIn( other.m_bFirstIn ), m_bFirstOut( other.m_bFirstOut ), m_bom( other.m_bom ) {} TranscoderUTF16::TranscoderUTF16( Stream *s, bool bOwn, t_endianity endianity ): Transcoder( s, bOwn ), m_defEndian( endianity ), m_bFirstIn( true ), m_bFirstOut( true ), m_bom( true ) { // detect host endianity uint16 BOM = 0xFEFF; byte *bit = (byte *) &BOM; m_hostEndian = bit[0] == 0xFE ? e_be : e_le; } bool TranscoderUTF16::get( uint32 &chr ) { m_parseStatus = true; if( popBuffer( chr ) ) return true; uint16 in; // first time around? read the marker. if( m_bFirstIn ) { m_bFirstIn = false; // is this version of UTF working with a byte order mark? if ( m_bom ) { if ( m_stream->read( &in, 2 ) != 2 ) { return false; } if( in == 0xFEFF ) { // local encoding m_streamEndian = m_hostEndian; if ( m_stream->read( &in, 2 ) != 2 ) return false; } else if ( in == 0xFFFE ) { // opposite encoding if( m_hostEndian == e_le ) m_streamEndian = e_be; else m_streamEndian = e_le; if ( m_stream->read( &in, 2 ) != 2 ) return false; } else { // consider stream endianity = default endianity if ( m_defEndian == e_detect ) m_streamEndian = m_hostEndian; else m_streamEndian = m_defEndian; // and keep char. } } else{ // consider stream endianity = default endianity if ( m_defEndian == e_detect ) m_streamEndian = m_hostEndian; else m_streamEndian = m_defEndian; // and read char if ( m_stream->read( &in, 2 ) != 2 ) return false; } } else { if ( m_stream->read( &in, 2 ) != 2 ) return false; } // check endianity. if ( m_streamEndian != m_hostEndian ) in = (in >> 8) | (in << 8); // high order char surrgoate? if ( in >= 0xD800 && in <= 0xDFFF ) { if( in > 0xDBFF ) { // protocol error m_parseStatus = false; return false; } chr = (in & 0x3FF) << 10; int res = m_stream->read( &in, 2 ); if ( res == -1 ) return false; else if ( res != 2 ) { // eof before m_parseStatus = false; return false; } // endianity if ( m_streamEndian != m_hostEndian ) in = (in >> 8) | (in << 8); // protocol check if( in < 0xDC00 || in > 0xDFFF ) { m_parseStatus = false; return false; } // fine, we can complete. chr |= (in & 0x3FF); } else chr = (uint32) in; return true; } bool TranscoderUTF16::put( uint32 chr ) { uint16 out; if ( m_bFirstOut ) { m_bFirstOut = false; if ( m_bom ) { // write byte order marker out = 0xFEFF; // consider stream endianity = default endianity if ( m_defEndian == e_detect ) { m_streamEndian = m_hostEndian; } else { m_streamEndian = m_defEndian; if( m_streamEndian != m_hostEndian ) out = (out >> 8 ) | ( out << 8 ); } if ( m_stream->write( &out, 2 ) != 2 ) return false; } else { // just set endianity if ( m_defEndian == e_detect ) { m_streamEndian = m_hostEndian; } else { m_streamEndian = m_defEndian; } } } if ( chr < 0x10000 ) { out = (uint16) chr; if( m_streamEndian != m_hostEndian ) out = (out >> 8 ) | ( out << 8 ); if ( m_stream->write( &out, 2 ) != 2 ) return false; } else { out = 0xD800 | ((chr >> 10) & 0x3FF); if( m_streamEndian != m_hostEndian ) out = (out >> 8 ) | ( out << 8 ); if ( m_stream->write( &out, 2 ) != 2 ) return false; out = 0xDC00 | ((chr >> 10) & 0x3FF); if( m_streamEndian != m_hostEndian ) out = (out >> 8 ) | ( out << 8 ); if ( m_stream->write( &out, 2 ) != 2 ) return false; } return true; } TranscoderUTF16 *TranscoderUTF16::clone() const { return new TranscoderUTF16( *this ); } //========================================= // TABLES // TranscoderISO_CP::TranscoderISO_CP( const TranscoderISO_CP &other ): Transcoder( other ), m_directTable( other.m_directTable ), m_dirTabSize( other.m_dirTabSize ), m_reverseTable( other.m_reverseTable ), m_revTabSize( other.m_revTabSize ), m_nMinUntl( other.m_nMinUntl ), m_nMaxUntl( other.m_nMaxUntl ) {} bool TranscoderISO_CP::get( uint32 &chr ) { m_parseStatus = true; if( popBuffer( chr ) ) return true; // converting the character into an unicode. byte in; if ( m_stream->read( &in, 1 ) != 1 ) return false; if ( in < 0xA0 ) { chr = (uint32) in; return true; } if ( in >= 0xA0 + m_dirTabSize ) { chr = (uint32) '?'; m_parseStatus = false; return true; } chr = (uint32) m_directTable[ in - 0xA0 ]; if ( chr == 0 ) { chr = (uint32) '?'; m_parseStatus = false; } return true; } bool TranscoderISO_CP::put( uint32 chr ) { m_parseStatus = true; byte out; if ( chr >= m_nMinUntl && chr <= m_nMaxUntl ) { out = (byte) chr; } else { // scan the table. uint32 lower = 0; uint32 higher = m_revTabSize-1; uint32 point = higher / 2; uint32 val; while ( true ) { // get the table row val = (uint32) m_reverseTable[ point ].unicode; if ( val == chr ) { out = (byte) m_reverseTable[ point ].local; break; } else { if ( lower == higher ) // not found { out = (byte) '?'; m_parseStatus = false; break; } // last try. In pair sized dictionaries, it can be also in the other node else if ( lower == higher -1 ) { point = lower = higher; continue; } } if ( chr > val ) { lower = point; } else { higher = point; } point = ( lower + higher ) / 2; } } // write the result return (m_stream->write( &out, 1 ) == 1); } TranscoderCP1252::TranscoderCP1252( Stream *s, bool bOwn ): TranscoderISO_CP( s, bOwn ) { m_directTable = s_table_cp1252; m_dirTabSize = sizeof( s_table_cp1252 ) / sizeof( uint16 ); m_reverseTable = s_rtable_cp1252; m_revTabSize = sizeof( s_rtable_cp1252 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderCP1252 *TranscoderCP1252::clone() const { return new TranscoderCP1252( *this ); } TranscoderISO8859_1::TranscoderISO8859_1( Stream *s, bool bOwn ): TranscoderISO_CP( s, bOwn ) { m_directTable = s_table_iso8859_1; m_dirTabSize = sizeof( s_table_iso8859_1 ) / sizeof( uint16 ); m_reverseTable = s_rtable_iso8859_1; m_revTabSize = sizeof( s_rtable_iso8859_1 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderISO8859_1 *TranscoderISO8859_1::clone() const { return new TranscoderISO8859_1( *this ); } TranscoderISO8859_2::TranscoderISO8859_2( Stream *s, bool bOwn ): TranscoderISO_CP( s, bOwn ) { m_directTable = s_table_iso8859_2; m_dirTabSize = sizeof( s_table_iso8859_2 ) / sizeof( uint16 ); m_reverseTable = s_rtable_iso8859_2; m_revTabSize = sizeof( s_rtable_iso8859_2 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderISO8859_2 *TranscoderISO8859_2::clone() const { return new TranscoderISO8859_2( *this ); } TranscoderISO8859_3::TranscoderISO8859_3( Stream *s, bool bOwn ): TranscoderISO_CP( s, bOwn ) { m_directTable = s_table_iso8859_3; m_dirTabSize = sizeof( s_table_iso8859_3 ) / sizeof( uint16 ); m_reverseTable = s_rtable_iso8859_3; m_revTabSize = sizeof( s_rtable_iso8859_3 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderISO8859_3 *TranscoderISO8859_3::clone() const { return new TranscoderISO8859_3( *this ); } TranscoderISO8859_4::TranscoderISO8859_4( Stream *s, bool bOwn ): TranscoderISO_CP( s, bOwn ) { m_directTable = s_table_iso8859_4; m_dirTabSize = sizeof( s_table_iso8859_4 ) / sizeof( uint16 ); m_reverseTable = s_rtable_iso8859_4; m_revTabSize = sizeof( s_rtable_iso8859_4 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderISO8859_4 *TranscoderISO8859_4::clone() const { return new TranscoderISO8859_4( *this ); } TranscoderISO8859_5::TranscoderISO8859_5( Stream *s, bool bOwn ): TranscoderISO_CP( s, bOwn ) { m_directTable = s_table_iso8859_5; m_dirTabSize = sizeof( s_table_iso8859_5 ) / sizeof( uint16 ); m_reverseTable = s_rtable_iso8859_5; m_revTabSize = sizeof( s_rtable_iso8859_5 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderISO8859_5 *TranscoderISO8859_5::clone() const { return new TranscoderISO8859_5( *this ); } TranscoderISO8859_6::TranscoderISO8859_6( Stream *s, bool bOwn ): TranscoderISO_CP( s, bOwn ) { m_directTable = s_table_iso8859_6; m_dirTabSize = sizeof( s_table_iso8859_6 ) / sizeof( uint16 ); m_reverseTable = s_rtable_iso8859_6; m_revTabSize = sizeof( s_rtable_iso8859_6 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderISO8859_6 *TranscoderISO8859_6::clone() const { return new TranscoderISO8859_6( *this ); } TranscoderISO8859_7::TranscoderISO8859_7( Stream *s, bool bOwn ): TranscoderISO_CP( s, bOwn ) { m_directTable = s_table_iso8859_7; m_dirTabSize = sizeof( s_table_iso8859_7 ) / sizeof( uint16 ); m_reverseTable = s_rtable_iso8859_7; m_revTabSize = sizeof( s_rtable_iso8859_7 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderISO8859_7 *TranscoderISO8859_7::clone() const { return new TranscoderISO8859_7( *this ); } TranscoderISO8859_8::TranscoderISO8859_8( Stream *s, bool bOwn ): TranscoderISO_CP( s, bOwn ) { m_directTable = s_table_iso8859_8; m_dirTabSize = sizeof( s_table_iso8859_8 ) / sizeof( uint16 ); m_reverseTable = s_rtable_iso8859_8; m_revTabSize = sizeof( s_rtable_iso8859_8 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderISO8859_8 *TranscoderISO8859_8::clone() const { return new TranscoderISO8859_8( *this ); } TranscoderISO8859_9::TranscoderISO8859_9( Stream *s, bool bOwn ): TranscoderISO_CP( s, bOwn ) { m_directTable = s_table_iso8859_9; m_dirTabSize = sizeof( s_table_iso8859_9 ) / sizeof( uint16 ); m_reverseTable = s_rtable_iso8859_9; m_revTabSize = sizeof( s_rtable_iso8859_9 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderISO8859_9 *TranscoderISO8859_9::clone() const { return new TranscoderISO8859_9( *this ); } TranscoderISO8859_10::TranscoderISO8859_10( Stream *s, bool bOwn ): TranscoderISO_CP( s, bOwn ) { m_directTable = s_table_iso8859_10; m_dirTabSize = sizeof( s_table_iso8859_10 ) / sizeof( uint16 ); m_reverseTable = s_rtable_iso8859_10; m_revTabSize = sizeof( s_rtable_iso8859_10 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderISO8859_10 *TranscoderISO8859_10::clone() const { return new TranscoderISO8859_10( *this ); } TranscoderISO8859_11::TranscoderISO8859_11( Stream *s, bool bOwn ): TranscoderISO_CP( s, bOwn ) { m_directTable = s_table_iso8859_11; m_dirTabSize = sizeof( s_table_iso8859_11 ) / sizeof( uint16 ); m_reverseTable = s_rtable_iso8859_11; m_revTabSize = sizeof( s_rtable_iso8859_11 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderISO8859_11 *TranscoderISO8859_11::clone() const { return new TranscoderISO8859_11( *this ); } TranscoderISO8859_13::TranscoderISO8859_13( Stream *s, bool bOwn ): TranscoderISO_CP( s, bOwn ) { m_directTable = s_table_iso8859_13; m_dirTabSize = sizeof( s_table_iso8859_13 ) / sizeof( uint16 ); m_reverseTable = s_rtable_iso8859_13; m_revTabSize = sizeof( s_rtable_iso8859_13 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderISO8859_13 *TranscoderISO8859_13::clone() const { return new TranscoderISO8859_13( *this ); } TranscoderISO8859_14::TranscoderISO8859_14( Stream *s, bool bOwn ): TranscoderISO_CP( s, bOwn ) { m_directTable = s_table_iso8859_14; m_dirTabSize = sizeof( s_table_iso8859_14 ) / sizeof( uint16 ); m_reverseTable = s_rtable_iso8859_14; m_revTabSize = sizeof( s_rtable_iso8859_14 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderISO8859_14 *TranscoderISO8859_14::clone() const { return new TranscoderISO8859_14( *this ); } TranscoderISO8859_15::TranscoderISO8859_15( Stream *s, bool bOwn ): TranscoderISO_CP( s, bOwn ) { m_directTable = s_table_iso8859_15; m_dirTabSize = sizeof( s_table_iso8859_15 ) / sizeof( uint16 ); m_reverseTable = s_rtable_iso8859_15; m_revTabSize = sizeof( s_rtable_iso8859_15 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderISO8859_15 *TranscoderISO8859_15::clone() const { return new TranscoderISO8859_15( *this ); } //========================================= // OEM tables (covering 0x0 to 0x20 and 0x80 to 0xff ranges) // TranscoderOEM::TranscoderOEM( const TranscoderOEM &other ): TranscoderISO_CP( other ) {} bool TranscoderOEM::get( uint32 &chr ) { m_parseStatus = true; if( popBuffer( chr ) ) return true; // converting the character into an unicode. byte in; if ( m_stream->read( &in, 1 ) != 1 ) return false; if ( in >= m_nMinUntl && in <= m_nMaxUntl ) { chr = (uint32) in; return true; } if ( in >= 0xFF ) { chr = (uint32) '?'; m_parseStatus = false; return true; } chr = chr < m_nMinUntl ? (uint32) m_directTable[ in ] : (uint32) m_directTable[ in + m_nMinUntl - m_nMaxUntl-1 ]; return true; } TranscoderIBM437::TranscoderIBM437( Stream *s, bool bOwn ): TranscoderOEM( s, bOwn ) { m_directTable = s_table_IBM437; m_dirTabSize = sizeof( s_table_IBM437 ) / sizeof( uint16 ); m_reverseTable = s_rtable_IBM437; m_revTabSize = sizeof( s_rtable_IBM437 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderIBM437 *TranscoderIBM437::clone() const { return new TranscoderIBM437( *this ); } TranscoderIBM850::TranscoderIBM850( Stream *s, bool bOwn ): TranscoderOEM( s, bOwn ) { m_directTable = s_table_IBM850; m_dirTabSize = sizeof( s_table_IBM850 ) / sizeof( uint16 ); m_reverseTable = s_rtable_IBM850; m_revTabSize = sizeof( s_rtable_IBM850 ) / sizeof( CP_ISO_UINT_TABLE ); } TranscoderIBM850 *TranscoderIBM850::clone() const { return new TranscoderIBM850( *this ); } //================================================================== // Utilities Transcoder *TranscoderFactory( const String &encoding, Stream *stream, bool own ) { if ( encoding == "C" ) return new TranscoderByte( stream, own ); if ( encoding == "utf-8" ) return new TranscoderUTF8( stream, own ); if ( encoding == "utf-16" ) return new TranscoderUTF16( stream, own ); if ( encoding == "utf-16LE" ) return new TranscoderUTF16LE( stream, own ); if ( encoding == "utf-16BE" ) return new TranscoderUTF16BE( stream, own ); if ( encoding == "cp1252" ) return new TranscoderCP1252( stream, own ); if ( encoding == "IBM850" ) return new TranscoderIBM850( stream, own ); if ( encoding == "IBM437" ) return new TranscoderIBM437( stream, own ); if ( encoding == "iso8859-1" ) return new TranscoderISO8859_1( stream, own ); if ( encoding == "iso8859-2" ) return new TranscoderISO8859_2( stream, own ); if ( encoding == "iso8859-3" ) return new TranscoderISO8859_3( stream, own ); if ( encoding == "iso8859-4" ) return new TranscoderISO8859_4( stream, own ); if ( encoding == "iso8859-5" ) return new TranscoderISO8859_5( stream, own ); if ( encoding == "iso8859-6" ) return new TranscoderISO8859_6( stream, own ); if ( encoding == "iso8859-7" ) return new TranscoderISO8859_7( stream, own ); if ( encoding == "iso8859-8" ) return new TranscoderISO8859_8( stream, own ); if ( encoding == "iso8859-9" ) return new TranscoderISO8859_9( stream, own ); if ( encoding == "iso8859-10" ) return new TranscoderISO8859_10( stream, own ); if ( encoding == "iso8859-11" ) return new TranscoderISO8859_11( stream, own ); // in case you are wondering, iso8859-12 has never been defined. if ( encoding == "iso8859-13" ) return new TranscoderISO8859_13( stream, own ); if ( encoding == "iso8859-14" ) return new TranscoderISO8859_14( stream, own ); if ( encoding == "iso8859-15" ) return new TranscoderISO8859_15( stream, own ); if ( encoding == "gbk" ) return new TranscoderGBK( stream, own ); return 0; } bool TranscodeString( const String &source, const String &encoding, String &target ) { Transcoder *tr = TranscoderFactory( encoding ); if ( tr == 0 ) return false; StringStream output; tr->setUnderlying( &output ); tr->writeString( source ); output.closeToString( target ); delete tr; return true; } bool TranscodeFromString( const String &source, const String &encoding, String &target ) { Transcoder *tr = TranscoderFactory( encoding ); if ( tr == 0 ) return false; StringStream input; input.writeString( source ); input.seekBegin( 0 ); tr->setUnderlying( &input ); tr->readString( target, 4096 ); while( tr->good() && ! tr->eof() ) { String temp; tr->readString( temp, 4096 ); target += temp; } delete tr; return true; } #ifdef WIN32 bool GetSystemEncodingWin(String &encoding) { UINT ConsoleInputCP = GetConsoleCP(); switch (ConsoleInputCP) { case 37: encoding = "IBM037"; return false; // IBM EBCDIC US-Canada case 437: encoding = "IBM437"; return true; // OEM United States case 500: encoding = "IBM500"; return false; // IBM EBCDIC International case 708: encoding = "ASMO-708"; return false; // Arabic (ASMO 708) case 709: encoding = ""; return false; // Arabic (ASMO-449+, BCON V4) case 710: encoding = ""; return false; // Arabic - Transparent Arabic case 720: encoding = "DOS-720"; return false; // Arabic (Transparent ASMO); Arabic (DOS) case 737: encoding = "IBM737"; return false; // OEM Greek (formerly 437G); Greek (DOS) case 775: encoding = "IBM775"; return false; // OEM Baltic; Baltic (DOS) case 850: encoding = "IBM850"; return true; // OEM Multilingual Latin 1; Western European (DOS) case 852: encoding = "IBM852"; return false; // OEM Latin 2; Central European (DOS) case 855: encoding = "IBM855"; return false; // OEM Cyrillic (primarily Russian) case 857: encoding = "IBM857"; return false; // OEM Turkish; Turkish (DOS) case 858: encoding = "IBM00858"; return false; // OEM Multilingual Latin 1 + Euro symbol case 860: encoding = "IBM860"; return false; // OEM Portuguese; Portuguese (DOS) case 861: encoding = "IBM861"; return false; // OEM Icelandic; Icelandic (DOS) case 862: encoding = "DOS-862"; return false; // OEM Hebrew; Hebrew (DOS) case 863: encoding = "IBM863"; return false; // OEM French Canadian; French Canadian (DOS) case 864: encoding = "IBM864"; return false; // OEM Arabic; Arabic (864) case 865: encoding = "IBM865"; return false; // OEM Nordic; Nordic (DOS) case 866: encoding = "cp866"; return false; // OEM Russian; Cyrillic (DOS) case 869: encoding = "IBM869"; return false; // OEM Modern Greek; Greek, Modern (DOS) case 870: encoding = "IBM870"; return false; // IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 case 874: encoding = "windows-874"; return false; // ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows) case 875: encoding = "cp875"; return false; // IBM EBCDIC Greek Modern case 932: encoding = "shift_jis"; return false; // ANSI/OEM Japanese; Japanese (Shift-JIS) //case 936: encoding = "gb2312"; return false; // ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) //TODO: must select a properly name for GBK; such as MS936, cp936 is too general, case 936: encoding = "gbk"; return true; // ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) case 949: encoding = "ks_c_5601-1987"; return false; // ANSI/OEM Korean (Unified Hangul Code) case 950: encoding = "big5 ANSI/OEM"; return false; // Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) case 1026: encoding = "IBM1026"; return false; // IBM EBCDIC Turkish (Latin 5) case 1047: encoding = "IBM01047"; return false; // IBM EBCDIC Latin 1/Open System case 1140: encoding = "IBM01140"; return false; // IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) case 1141: encoding = "IBM01141"; return false; // IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro) case 1142: encoding = "IBM01142"; return false; // IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro) case 1143: encoding = "IBM01143"; return false; // IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro) case 1144: encoding = "IBM01144"; return false; // IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro) case 1145: encoding = "IBM01145"; return false; // IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro) case 1146: encoding = "IBM01146"; return false; // IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro) case 1147: encoding = "IBM01147"; return false; // IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro) case 1148: encoding = "IBM01148"; return false; // IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro) case 1149: encoding = "IBM01149"; return false; // IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro) case 1200: encoding = "utf-16LE"; return true; // Unicode UTF-16, little endian byte order (BMP of ISO 10646); available only to managed applications case 1201: encoding = "utf-16BE"; return false; // Unicode UTF-16, big endian byte order; available only to managed applications case 1250: encoding = "windows-1250"; return false; // ANSI Central European; Central European (Windows) case 1251: encoding = "windows-1251"; return false; // ANSI Cyrillic; Cyrillic (Windows) case 1252: encoding = "cp1252"; return true; // ANSI Latin 1; Western European (Windows) case 1253: encoding = "windows-1253"; return false; // ANSI Greek; Greek (Windows) case 1254: encoding = "windows-1254"; return false; // ANSI Turkish; Turkish (Windows) case 1255: encoding = "windows-1255"; return false; // ANSI Hebrew; Hebrew (Windows) case 1256: encoding = "windows-1256"; return false; // ANSI Arabic; Arabic (Windows) case 1257: encoding = "windows-1257"; return false; // ANSI Baltic; Baltic (Windows) case 1258: encoding = "windows-1258"; return false; // ANSI/OEM Vietnamese; Vietnamese (Windows) case 1361: encoding = ""; return false; //Johab Korean (Johab) case 10000: encoding = "macintosh"; return false; // MAC Roman; Western European (Mac) case 10001: encoding = "x-mac-japanese Japanese"; return false; // (Mac) case 10002: encoding = "x-mac-chinesetrad"; return false; // MAC Traditional Chinese (Big5); Chinese Traditional (Mac) case 10003: encoding = "x-mac-korean"; return false; // Korean (Mac) case 10004: encoding = "x-mac-arabic"; return false; // Arabic (Mac) case 10005: encoding = "x-mac-hebrew"; return false; // Hebrew (Mac) case 10006: encoding = "x-mac-greek"; return false; // Greek (Mac) case 10007: encoding = "x-mac-cyrillic"; return false; // Cyrillic (Mac) case 10008: encoding = "x-mac-chinesesimp"; return false; // MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac) case 10010: encoding = "x-mac-romanian"; return false; // Romanian (Mac) case 10017: encoding = "x-mac-ukrainian"; return false; // Ukrainian (Mac) case 10021: encoding = "x-mac-thai"; return false; // Thai (Mac) case 10029: encoding = "x-mac-ce"; return false; // MAC Latin 2; Central European (Mac) case 10079: encoding = "x-mac-icelandic"; return false; // Icelandic (Mac) case 10081: encoding = "x-mac-turkish"; return false; // Turkish (Mac) case 10082: encoding = "x-mac-croatian"; return false; // Croatian (Mac) case 12000: encoding = "utf-32"; return false; // Unicode UTF-32, little endian byte order; available only to managed applications case 12001: encoding = "utf-32BE"; return false; // Unicode UTF-32, big endian byte order; available only to managed applications case 20000: encoding = "x-Chinese_CNS"; return false; // CNS Taiwan; Chinese Traditional (CNS) case 20001: encoding = "x-cp20001"; return false; // TCA Taiwan case 20002: encoding = "x_Chinese-Eten"; return false; // Eten Taiwan; Chinese Traditional (Eten) case 20003: encoding = "x-cp20003"; return false; // IBM5550 Taiwan case 20004: encoding = "x-cp20004"; return false; // TeleText Taiwan case 20005: encoding = "x-cp20005"; return false; // Wang Taiwan case 20105: encoding = "x-IA5 IA5"; return false; // (IRV International Alphabet No. 5, 7-bit); Western European (IA5) case 20106: encoding = "x-IA5-German"; return false; // IA5 German (7-bit) case 20107: encoding = "x-IA5-Swedish"; return false; // IA5 Swedish (7-bit) case 20108: encoding = "x-IA5-Norwegian"; return false; // IA5 Norwegian (7-bit) case 20127: encoding = "us-ascii"; return false; // US-ASCII (7-bit) case 20261: encoding = "x-cp20261"; return false; // T.61 case 20269: encoding = "x-cp20269"; return false; // ISO 6937 Non-Spacing Accent case 20273: encoding = "IBM273"; return false; // IBM EBCDIC Germany case 20277: encoding = "IBM277"; return false; // IBM EBCDIC Denmark-Norway case 20278: encoding = "IBM278"; return false; // IBM EBCDIC Finland-Sweden case 20280: encoding = "IBM280"; return false; // IBM EBCDIC Italy case 20284: encoding = "IBM284"; return false; // IBM EBCDIC Latin America-Spain case 20285: encoding = "IBM285"; return false; // IBM EBCDIC United Kingdom case 20290: encoding = "IBM290"; return false; // IBM EBCDIC Japanese Katakana Extended case 20297: encoding = "IBM297"; return false; // IBM EBCDIC France case 20420: encoding = "IBM420"; return false; // IBM EBCDIC Arabic case 20423: encoding = "IBM423"; return false; // IBM EBCDIC Greek case 20424: encoding = "IBM424"; return false; // IBM EBCDIC Hebrew case 20833: encoding = "x-EBCDIC-KoreanExtended"; return false; // IBM EBCDIC Korean Extended case 20838: encoding = "IBM-Thai"; return false; // IBM EBCDIC Thai case 20866: encoding = "koi8-r"; return false; // Russian (KOI8-R); Cyrillic (KOI8-R) case 20871: encoding = "IBM871"; return false; // IBM EBCDIC Icelandic case 20880: encoding = "IBM880"; return false; // IBM EBCDIC Cyrillic Russian case 20905: encoding = "IBM905"; return false; // IBM EBCDIC Turkish case 20924: encoding = "IBM00924"; return false; // IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) case 20932: encoding = "EUC-JP"; return false; // Japanese (JIS 0208-1990 and 0121-1990) case 20936: encoding = "x-cp20936"; return false; // Simplified Chinese (GB2312); Chinese Simplified (GB2312-80) case 20949: encoding = "x-cp20949"; return false; // Korean Wansung case 21025: encoding = "cp1025"; return false; // IBM EBCDIC Cyrillic Serbian-Bulgarian case 21027: encoding = ""; return false; // (deprecated) case 21866: encoding = "koi8-u"; return false; // Ukrainian (KOI8-U); Cyrillic (KOI8-U) case 28591: encoding = "iso8859-1"; return true; // ISO 8859-1 Latin 1; Western European (ISO) case 28592: encoding = "iso8859-2"; return true; // ISO 8859-2 Central European; Central European (ISO) case 28593: encoding = "iso8859-3"; return true; // ISO 8859-3 Latin 3 case 28594: encoding = "iso8859-4"; return true; // ISO 8859-4 Baltic case 28595: encoding = "iso8859-5"; return true; // ISO 8859-5 Cyrillic case 28596: encoding = "iso8859-6"; return true; // ISO 8859-6 Arabic case 28597: encoding = "iso8859-7"; return true; // ISO 8859-7 Greek case 28598: encoding = "iso8859-8"; return true; // ISO 8859-8 Hebrew; Hebrew (ISO-Visual) case 28599: encoding = "iso8859-9"; return true; // ISO 8859-9 Turkish case 28603: encoding = "iso8859-13"; return true; // ISO 8859-13 Estonian case 28605: encoding = "iso8859-15"; return true; // ISO 8859-15 Latin 9 case 29001: encoding = "x-Europa"; return false; // Europa 3 case 38598: encoding = "iso-8859-8-i"; return false; // ISO 8859-8 Hebrew; Hebrew (ISO-Logical) case 50220: encoding = "iso-2022-jp"; return false; // ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) case 50221: encoding = "csISO2022JP"; return false; // ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana) case 50222: encoding = "iso-2022-jp"; return false; // ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI) case 50225: encoding = "iso-2022-kr"; return false; // ISO 2022 Korean case 50227: encoding = "x-cp50227"; return false; // ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022) case 50229: encoding = ""; return false; // ISO 2022 Traditional Chinese case 50930: encoding = ""; return false; // EBCDIC Japanese (Katakana) Extended case 50931: encoding = ""; return false; // EBCDIC US-Canada and Japanese case 50933: encoding = ""; return false; // EBCDIC Korean Extended and Korean case 50935: encoding = ""; return false; // EBCDIC Simplified Chinese Extended and Simplified Chinese case 50936: encoding = ""; return false; // EBCDIC Simplified Chinese case 50937: encoding = ""; return false; // EBCDIC US-Canada and Traditional Chinese case 50939: encoding = ""; return false; // EBCDIC Japanese (Latin) Extended and Japanese case 51932: encoding = "euc-jp"; return false; // EUC Japanese case 51936: encoding = "EUC-CN"; return false; // EUC Simplified Chinese; Chinese Simplified (EUC) case 51949: encoding = "euc-kr"; return false; // EUC Korean case 51950: encoding = ""; return false; // EUC Traditional Chinese case 52936: encoding = "hz-gb-2312"; return false; // HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ) case 54936: encoding = "GB18030"; return false; // Windows XP and later: encoding = "GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030) case 57002: encoding = "x-iscii-de"; return false; // ISCII Devanagari case 57003: encoding = "x-iscii-be"; return false; // ISCII Bengali case 57004: encoding = "x-iscii-ta"; return false; // ISCII Tamil case 57005: encoding = "x-iscii-te"; return false; // ISCII Telugu case 57006: encoding = "x-iscii-as"; return false; // ISCII Assamese case 57007: encoding = "x-iscii-or"; return false; // ISCII Oriya case 57008: encoding = "x-iscii-ka"; return false; // ISCII Kannada case 57009: encoding = "x-iscii-ma"; return false; // ISCII Malayalam case 57010: encoding = "x-iscii-gu"; return false; // ISCII Gujarati case 57011: encoding = "x-iscii-pa"; return false; // ISCII Punjabi case 65000: encoding = "utf-7"; return false; // Unicode (UTF-7) case 65001: encoding = "utf-8"; return true; // Unicode (UTF-8) default: encoding = ""; return false; } } #endif bool GetSystemEncoding( String &encoding ) { #ifdef WIN32 return GetSystemEncodingWin(encoding); #else const char *lc_ctype = setlocale( LC_CTYPE, 0 ); if ( lc_ctype == 0 ) return false; encoding = lc_ctype; if ( encoding == "C" || encoding == "POSIX" ) { // this is a request to use only non-international strings. // but first see if the program has not set a locale. String language; if ( ! Sys::_getEnv( "LANG", language ) ) { Sys::_getEnv( "LC_ALL", language ); } if( language != "" ) { encoding = language; if ( encoding == "C" || encoding == "POSIX" ) return false; // else continue processing. } else return false; } //do we have an encoding specification? uint32 pos = encoding.find("."); if( pos != csh::npos && pos < encoding.length() - 1 ) { // we have a dot. encoding = encoding.subString( pos + 1 ); // eventually remove "@" pos = encoding.find( "@" ); if ( pos != csh::npos ) { encoding = encoding.subString( 0, pos ); } pos = encoding.find( "8859-" ); if ( pos != csh::npos && pos < encoding.length() - 6) { // a subkind of iso encoding. encoding = encoding.subString( pos + 6 ); if ( encoding == "1" || encoding == "2" || encoding == "3" || encoding == "4" || encoding == "5" || encoding == "6" || encoding == "7" || encoding == "7" || encoding == "8" || encoding == "9" || encoding == "10" || encoding == "11" || encoding == "13" || encoding == "14" || encoding == "15" ) { encoding = "iso8859-" + encoding; return true; } } else { // utf variant? pos = encoding.find( "UTF" ); if ( pos == csh::npos ) pos = encoding.find( "utf" ); uint32 utf_prefix_len=3; if(pos != csh::npos && encoding.getCharAt(utf_prefix_len) == '-' ) utf_prefix_len++;; if ( pos != csh::npos && pos < encoding.length() - utf_prefix_len ) { encoding = encoding.subString( pos + utf_prefix_len ); if ( encoding == "8" || encoding == "16" || encoding == "16LE" || encoding == "16BE" ) { encoding = "utf-" + encoding; return true; } } else { // known windows cp? if( encoding == "1252" ) { encoding = "cp" + encoding; return true; } } } // if we are not returned up to date, surrender. return false; } // no dot. Guess one from the language. if ( encoding.length() >= 2 ) { encoding = encoding.subString( 0, 2 ); if ( encoding == "it" || encoding == "fr" || encoding == "de" || encoding == "es" || encoding == "pt" ) { encoding = "iso8859-15"; return true; } if ( encoding == "en" ) { encoding = "iso8859-1"; return true; } if ( encoding == "gr" ) { encoding = "iso8859-7"; return true; } } // no way return false; #endif } } /* end of transcoding.cpp */ engine/uri.cpp000066400000000000000000000415511176363201700136420ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: uri.cpp RFC 3986 - Uniform Resource Identifier - implementation ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 17 Feb 2008 12:23:28 +0100 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include namespace Falcon { URI::URI(): m_bValid( true ), m_path( this ), m_queryMap( 0 ) { } URI::URI( const String &suri ): m_bValid( true ), m_path( this ), m_queryMap(0) { parse( suri ); } URI::URI( const URI &other ): m_bValid( true ), m_path( this ), m_queryMap(0) { parse( other.get( true ) ); } URI::~URI() { delete m_queryMap; } void URI::clear() { m_scheme = ""; m_userInfo = ""; m_host = ""; m_port = ""; m_path.set( "" ); if ( m_queryMap != 0 ) { delete m_queryMap; m_queryMap = 0; } m_query = ""; m_fragment = ""; m_encoded = ""; m_bValid = true; // by default. } bool URI::parse( const String &newUri, bool parseQuery, bool decode ) { m_bValid = internal_parse( newUri, parseQuery, decode ); return m_bValid; } bool URI::internal_parse( const String &newUri, bool parseQuery, bool decode ) { // had we a previous parsing? if ( m_original.size() != 0 ) { clear(); } m_original = newUri; if ( Engine::getWindowsNamesConversion() ) { if ( newUri.find( "\\" ) != String::npos || (newUri.length() > 2 && newUri.getCharAt(1) == ':' ) ) { Path::winToUri( m_original ); } } // We must parse before decoding each element. uint32 pStart = 0; uint32 pEnd = 0; uint32 len = m_original.length(); typedef enum { e_begin, e_colon, e_postScheme, e_host, e_port, e_path, e_done } t_status; t_status state = e_begin; bool bUserGiven = false; String tempPath; // we're setting the path after. while( pEnd < len ) { uint32 chr = m_original.getCharAt( pEnd ); switch ( chr ) { case ':': // if we don't have a scheme yet, this is our scheme. if( pEnd == 0 ) return false; if ( state == e_begin ) state = e_colon; else if ( state == e_host ) { m_host = m_original.subString( pStart, pEnd ); state = e_port; pStart = pEnd + 1; } // otherwise, it's just part of what's going on. break; case '/': // Nothing before? if ( state == e_begin ) { // we're parsing a (relative or absolute) path and we didn't know! state = e_path; } // if we had a colon, we have :/ else if ( state == e_colon ) { if ( pStart == pEnd ) // scheme cannot be empty return false; state = e_postScheme; // like begin, we may have a host or a path m_scheme = m_original.subString( pStart, pEnd-1 ); // removing extra ':' pStart = pEnd+1; } else if ( state == e_postScheme ) { state = e_host; // we have a host starting one next. pStart = pEnd + 1; } else if( state == e_host ) { // we have the full host // may be empty (as in file:///path) if ( pStart != pEnd ) m_host = m_original.subString( pStart, pEnd ); // anyhow, start the path from here pStart = pEnd; state = e_path; } else if ( state == e_port ) { // we have the port. if ( pStart == pEnd ) // cannot be empty. return false; m_port = m_original.subString( pStart, pEnd ); // anyhow, start the path from here pStart = pEnd; state = e_path; } break; case '@': // can be found only in host or path state. In path, it is just ignored. if ( state == e_port ) { // ops, the host wasn't the host, and the port wasn't the port. state = e_host; m_userInfo = m_host + ":" + m_original.subString( pStart, pEnd ); m_host = ""; pStart = pEnd + 1; } else if ( state == e_host ) { // if we have already user info, we failed. if ( bUserGiven ) return false; m_userInfo = m_original.subString( pStart, pEnd ); pStart = pEnd + 1; // state stays host, but signal we have already seen a @ here bUserGiven = true; } else if ( state != e_path ) { return false; } break; case '?': case '#': // can be found in host, port, path and begin state if ( state == e_begin || state == e_path ) { tempPath = m_original.subString( pStart, pEnd ); } else if ( state == e_host ) { m_host = m_original.subString( pStart, pEnd ); } else if ( state == e_port ) { m_port = m_original.subString( pStart, pEnd ); } // cannot be found in e_colon, that would be an error else if ( state == e_colon ) return false; // can be found in postScheme state, in which case we have nothing to do // in every case, parse the query (+fragment) and exit loop if ( chr == '?' ) { if ( ! internal_parseQuery( m_original, pEnd + 1, parseQuery, decode ) ) return false; } else { if ( ! internal_parseFragment( pEnd + 1 ) ) return false; } // complete loop state = e_done; pEnd = len; break; default: // if we're in post scheme, a non '/' char starts a path. if ( state == e_postScheme ) state = e_path; else if ( state == e_colon ) { // if we are in colon state, then previous thing (begin) is to be considered host m_host = m_original.subString( pStart, pEnd - 1 ); pStart = pEnd; state = e_port; } } pEnd++; } // what do we have to do now? switch ( state ) { case e_begin: case e_path: case e_colon: // colon too, as it may be i.e. C: tempPath = m_original.subString( pStart, pEnd ); break; case e_host: m_host = m_original.subString( pStart, pEnd ); break; case e_port: m_port = m_original.subString( pStart, pEnd ); break; // in all other cases, just let it through default: break; } if ( decode ) { // decode each element if ( m_scheme.size() ) m_scheme = URLDecode( m_scheme ); if ( m_host.size() ) m_host = URLDecode( m_host ); if ( m_port.size() ) m_port = URLDecode( m_port ); if ( tempPath.size() ) tempPath = URLDecode( tempPath ); if ( m_fragment.size() ) m_fragment = URLDecode( m_fragment ); } // finally, store the path if any if ( tempPath.size() ) { m_path.set( tempPath ); if( ! m_path.isValid() ) return false; } return true; } bool URI::internal_parseQuery( const String &src, uint32 pEnd, bool parseQuery, bool bDecode ) { if ( ! parseQuery ) { uint32 pSharp = src.find( "#", pEnd ); if( pSharp != String::npos ) { query( src.subString( pEnd, pSharp ) ); return internal_parseFragment( pSharp+1 ); } else { if ( pEnd == 0 ) query( src ); else query( src.subString( pEnd ) ); } return true; } delete m_queryMap; m_queryMap = new Map( &traits::t_string(), &traits::t_string() ); // break & and = fields. uint32 len = src.length(); uint32 pStart = pEnd; String tempKey; bool bIsValue = false; while ( pEnd < len ) { uint32 chr = src.getCharAt( pEnd ); if ( chr == '=' && ! bIsValue ) { // we had the key; we want the value. if ( pEnd == pStart ) { // 0 lenght key not allowed return false; } if ( bDecode ) URLDecode( src.subString( pStart, pEnd ), tempKey ); else tempKey = src.subString( pStart, pEnd ); bIsValue = true; pStart = pEnd + 1; } else if ( chr == '&' ) { // have we got a value? String val; if ( bIsValue && pStart != pEnd ) { if ( bDecode ) URLDecode( src.subString( pStart, pEnd ), val ); else val = src.subString( pStart, pEnd ); } // save this key m_queryMap->insert( &tempKey, &val ); bIsValue = false; pStart = pEnd + 1; } else if ( chr == '#' ) { return internal_parseFragment( pEnd + 1 ); } pEnd ++; } return true; } bool URI::parseQuery( bool mode ) { m_bValid = internal_parseQuery( m_query, 0, mode, true ); return m_bValid; } bool URI::internal_parseFragment( uint32 pos ) { // there is actually nothing to do, but getting everything left as substring if ( pos < m_original.length() ) m_fragment = m_original.subString( pos ); return true; } void URI::query( const String &q, bool encode ) { m_encoded = ""; // ok also if m_queryMap is 0. delete m_queryMap; m_queryMap = 0; if ( encode ) URLEncode( q, m_query ); else m_query = q; } void URI::scheme( const String &s ) { m_encoded = ""; m_scheme = s; } void URI::userInfo( const String &s ) { m_encoded = ""; m_userInfo = s; } void URI::host( const String &h ) { m_encoded = ""; m_host = h; } void URI::port( const String &h ) { m_encoded = ""; m_port = h; } void URI::path( const String &p ) { m_encoded = ""; m_path.set( p ); } void URI::path( const Path &p ) { m_encoded = ""; m_path = p; } void URI::fragment( const String &s ) { m_encoded = ""; m_fragment = s; } bool URI::hasField( const String &f ) const { if( m_queryMap == 0 ) return false; String *res = (String *) m_queryMap->find( &f ); return res != 0; } bool URI::getField( const String &key, String &value ) const { if( m_queryMap == 0 ) return false; String *res = (String *) m_queryMap->find( &key ); if ( res != 0 ) { value = *res; return true; } return false; } void URI::setField( const String &key, const String &value ) { if( m_queryMap == 0 ) { m_queryMap = new Map( &traits::t_string(), &traits::t_string() ); } m_queryMap->insert( &key, &value ); } bool URI::removeField( const String &key ) { if( m_queryMap == 0 ) return false; m_queryMap->erase( &key ); return true; } bool URI::firstField( String &key, String &value ) { if ( m_queryMap != 0 && m_queryMap->size() > 0 ) { m_queryIter = m_queryMap->begin(); key = *(String *) m_queryIter.currentKey(); value = *(String *) m_queryIter.currentValue(); return true; } return false; } bool URI::nextField( String &key, String &value ) { if ( m_queryMap != 0 && m_queryMap->size() > 0 && m_queryIter.hasNext() ) { m_queryIter.next(); key = *(String *) m_queryIter.currentKey(); value = *(String *) m_queryIter.currentValue(); return true; } return false; } uint32 URI::fieldCount() { if ( m_queryMap != 0 ) return m_queryMap->size(); return 0; } const String &URI::get( bool synthQuery ) const { if ( m_encoded.size() != 0 ) return m_encoded; if ( synthQuery && m_queryMap != 0 ) makeQuery(); if ( m_scheme.size() != 0 ) { m_encoded = m_scheme + ":/"; } if( (m_userInfo.size() != 0) || (m_host.size() != 0) || (m_port.size() != 0) ) { if ( m_encoded.size() != 0 ) m_encoded += "/"; if (m_userInfo.size() != 0) { //TODO Break into user and password uint32 pos = m_userInfo.find(":"); if( pos != String::npos ) { m_encoded += URLEncode( m_userInfo.subString( 0 , pos ) ) + ":" + URLEncode( m_userInfo.subString( pos+1 ) ) + "@"; } else m_encoded += URLEncode( m_userInfo ) + "@"; } if (m_host.size() != 0) m_encoded += URLEncode( m_host ); if (m_port.size() != 0) m_encoded += ":" + URLEncode( m_port ); if ( m_path.get().size() != 0 ) { if ( ! m_path.isAbsolute() ) m_encoded += "/"; } } else if ( m_scheme.size() != 0 && m_path.isAbsolute() ) m_encoded += "/"; if ( m_path.get().size() != 0 ) m_encoded += URLEncodePath( m_path.get() ); if ( m_query.size() != 0 ) m_encoded += "?" + m_query; if ( m_fragment.size() != 0 ) m_encoded += "#" + URLEncode( m_fragment ); return m_encoded; } const String &URI::makeQuery() const { m_query = ""; if ( m_queryMap != 0 && m_queryMap->size() > 0 ) { MapIterator iter = m_queryMap->begin(); m_query += URLEncode( *(String *) iter.currentKey() ) + "=" + URLEncode( *(String *) iter.currentValue() ); while( iter.hasNext() ) { iter.next(); m_query += "&"; m_query += URLEncode( *(String *) iter.currentKey() ) + "=" + URLEncode( *(String *) iter.currentValue() ); } } return m_query; } void URI::URLEncode( const String &source, String &target ) { target = ""; // resets manipulator target.reserve( source.size() ); // encode as UTF-8 AutoCString sutf( source ); const char *cutf = sutf.c_str(); target.reserve( sutf.length() ); while ( *cutf != 0 ) { unsigned char chr = (unsigned char) *cutf; if ( ! isUnreserved( chr ) ) { target.append( '%' ); target.append( URI::CharToHex( chr >> 4 ) ); target.append( URI::CharToHex( chr & 0xF ) ); } else { target.append( chr ); } ++cutf; } } //TODO: Make one with above void URI::URLEncodePath( const String &source, String &target ) { target = ""; // resets manipulator target.reserve( source.size() ); // encode as UTF-8 AutoCString sutf( source ); const char *cutf = sutf.c_str(); target.reserve( sutf.length() ); while ( *cutf != 0 ) { unsigned char chr = (unsigned char) *cutf; if ( chr == 0x20 ) { target.append( '+' ); } // in the paths, we can't encode path chars as '/' and '\\' else if ( chr < 0x20 || chr > 0x7F || isSubDelim( chr ) || chr == '%' || chr == '"' || chr == '\'' || chr == '`' || chr == '{' || chr == '}' || chr == '<' || chr == '>' ) { target.append( '%' ); target.append( URI::CharToHex( chr >> 4 ) ); target.append( URI::CharToHex( chr & 0xF ) ); } else { target.append( chr ); } ++cutf; } } bool URI::URLDecode( const String &source, String &target ) { // the target buffer can be - at worst - long as the source. char *tgbuf = (char *) memAlloc( source.length() + 1 ); char *pos = tgbuf; bool bOk = true; uint32 len = source.length(); for( uint32 i = 0; i < len; i ++ ) { uint32 chr = source.getCharAt( i ); // an URL encoded string cannot have raw characters outside defined ranges. if ( chr > 0x7F ) { bOk = false; break; } if ( chr == '+' ) *pos = ' '; else if ( chr == '%' ) { // not enough space? if ( i+3 > len ) { bOk = false; break; } // get the characters -- check also for non-hex digits. unsigned char c1, c2; if ( ( c1 = HexToChar( source.getCharAt( ++i ) ) ) == 0xFF || ( c2 = HexToChar( source.getCharAt( ++i ) ) ) == 0xFF ) { bOk = false; break; } *pos = c1 << 4 | c2; } else *pos = chr; ++pos; } *pos = 0; if ( bOk ) { // reconvert from UTF8 to Falcon target.fromUTF8( tgbuf ); } memFree( tgbuf ); return bOk; } } engine/vfs_file_unix.cpp000066400000000000000000000072411176363201700157010ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vfs_file.cpp VSF provider for physical file system on the host system. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 12 Sep 2008 21:47:10 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Falcon { // we don't use filesystem data. VFSFile::VFSFile(): VFSProvider( "file" ), m_fsdata(0) {} VFSFile::~VFSFile() { } Stream *VFSFile::open( const URI& uri, const OParams &p ) { int omode = paramsToMode( p ); // todo: do something about share mode AutoCString cfilename( uri.path() ); errno = 0; int handle = ::open( cfilename.c_str(), omode ); if ( handle >= 0 ) { UnixFileSysData *ufd = new UnixFileSysData( handle, 0 ); return new StreamBuffer( new FileStream( ufd ) ); } return 0; } Stream *VFSFile::create( const URI& uri, const CParams &p, bool &bSuccess ) { int omode = paramsToMode( p ); if ( p.isNoOvr() ) omode |= O_EXCL; //TODO: something about sharing AutoCString cfilename( uri.path() ); errno=0; umask( 0000 ); int handle = ::open( cfilename.c_str(), O_CREAT | omode, p.createMode() ); if ( handle >= 0 ) { bSuccess = true; if ( ! p.isNoStream() ) { UnixFileSysData *ufd = new UnixFileSysData( handle, 0 ); return new StreamBuffer( new FileStream( ufd ) ); } else return 0; } bSuccess = false; return 0; } DirEntry* VFSFile::openDir( const URI& uri ) { AutoCString filename( uri.path() ); DIR *dir = ::opendir( filename.c_str() ); if ( dir == 0 ) { return 0; } return new DirEntry_unix( uri.path(), dir ); } bool VFSFile::readStats( const URI& uri, FileStat &s ) { return Sys::fal_stats( uri.path(), s ); } bool VFSFile::writeStats( const URI& uri, const FileStat &s ) { // TODO: write contents return false; } bool VFSFile::chown( const URI &uri, int uid, int gid ) { AutoCString filename( uri.path() ); return ::chown( filename.c_str(), uid, gid ) == 0; } bool VFSFile::chmod( const URI &uri, int mode ) { AutoCString filename( uri.path() ); return ::chmod( filename.c_str(), mode ) == 0; } bool VFSFile::link( const URI &uri1, const URI &uri2, bool bSymbolic ) { // TODO return false; } bool VFSFile::unlink( const URI &uri ) { AutoCString filename( uri.path() ); return ::unlink( filename.c_str() ) == 0; } bool VFSFile::move( const URI &suri, const URI &duri ) { AutoCString filename( suri.path() ); AutoCString dest( duri.path() ); return ::rename( filename.c_str(), dest.c_str() ) == 0; } bool VFSFile::mkdir( const URI &uri, uint32 mode ) { AutoCString filename( uri.path() ); return ::mkdir( filename.c_str(), mode ) == 0; } bool VFSFile::rmdir( const URI &uri ) { AutoCString filename( uri.path() ); return ::rmdir( filename.c_str() ) == 0; } int64 VFSFile::getLastFsError() { return (int64) errno; } Error *VFSFile::getLastError() { if( errno != 0 ) { IoError *e = new IoError( e_io_error ); e->systemError( errno ); return e; } return 0; } } /* end of vsf_file.cpp */ engine/vfs_file_win.cpp000066400000000000000000000124641176363201700155160ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vfs_file.cpp VSF provider for physical file system on the host system. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 12 Sep 2008 21:47:10 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include #include namespace Falcon { static DWORD win_paramsToMode( VFSFile::OParams p ) { DWORD omode; if ( p.isRdwr() ) omode = GENERIC_READ | GENERIC_WRITE; else if ( p.isRdOnly() ) omode = GENERIC_READ; else omode = GENERIC_WRITE; return omode; } static DWORD win_paramsToShare( VFSFile::OParams p ) { DWORD shmode; if ( p.isShNone() ) shmode = 0; else if( p.isShNoRead() ) shmode = FILE_SHARE_WRITE; else if ( p.isShNoWrite() ) shmode = FILE_SHARE_READ; else shmode = FILE_SHARE_READ | FILE_SHARE_WRITE; return shmode; } // we don't use filesystem data. VFSFile::VFSFile(): VFSProvider( "file" ), m_fsdata(0) {} VFSFile::~VFSFile() { } Stream *VFSFile::open( const URI& uri, const OParams &p ) { DWORD omode = win_paramsToMode( p ); DWORD oshare = win_paramsToShare( p ); String path = uri.path(); Path::uriToWin( path ); AutoWString wstr( path ); HANDLE handle = CreateFileW( wstr.w_str(), omode, oshare, NULL, OPEN_EXISTING, 0, NULL ); DWORD dwError = GetLastError(); if ( handle == 0 || handle == INVALID_HANDLE_VALUE ) { if ( dwError == ERROR_CALL_NOT_IMPLEMENTED ) { AutoCString cstr( path ); handle = CreateFile( cstr.c_str(), omode, oshare, NULL, OPEN_EXISTING, 0, NULL ); } } if ( handle == 0 || handle == INVALID_HANDLE_VALUE ) { return 0; } FileStream *fs = new FileStream( new WinFileSysData( handle, 0 ) ); return new StreamBuffer( fs ); } Stream *VFSFile::create( const URI& uri, const CParams &p, bool &bSuccess ) { DWORD omode = win_paramsToMode( p ); DWORD oshare = win_paramsToShare( p ); DWORD ocreate = p.isNoOvr() ? 0 : CREATE_ALWAYS; // turn the xxx bytes DWORD oattribs = FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_ARCHIVE; if ( p.createMode() ) { // use the owner bits int obits = p.createMode() & 0700; // set read only if write bit is not set if ( (obits & 0200) == 0 ) { oattribs |= FILE_ATTRIBUTE_READONLY; } // set hidden if read bit is not set if ( (obits & 0400) == 0 ) { oattribs |= FILE_ATTRIBUTE_HIDDEN; } } String path = uri.path(); Path::uriToWin( path ); AutoWString wstr( path ); HANDLE handle = CreateFileW( wstr.w_str(), omode, oshare, NULL, ocreate, oattribs, NULL ); DWORD dwError = GetLastError(); if ( handle == 0 || handle == INVALID_HANDLE_VALUE ) { if ( dwError == ERROR_CALL_NOT_IMPLEMENTED ) { AutoCString cstr( path ); handle = CreateFile( cstr.c_str(), omode, oshare, NULL, ocreate, oattribs, NULL ); } } if ( handle == 0 || handle == INVALID_HANDLE_VALUE ) { bSuccess = false; return 0; } bSuccess = true; // the caller may not really want to open the stream. if( p.isNoStream() ) { CloseHandle( handle ); return 0; } FileStream *fs = new FileStream( new WinFileSysData( handle, 0 ) ); return new StreamBuffer( fs ); } DirEntry* VFSFile::openDir( const URI& uri ) { int32 error = 0; return Sys::fal_openDir( uri.path(), error ); } bool VFSFile::readStats( const URI& uri, FileStat &s ) { return Sys::fal_stats( uri.path(), s ); } bool VFSFile::writeStats( const URI& uri, const FileStat &s ) { // TODO: write contents return false; } bool VFSFile::chown( const URI &uri, int uid, int gid ) { return false; } bool VFSFile::chmod( const URI &uri, int mode ) { return false; } bool VFSFile::link( const URI &uri1, const URI &uri2, bool bSymbolic ) { // TODO return false; } bool VFSFile::unlink( const URI &uri ) { int32 err = 0; return Sys::fal_unlink( uri.path(), err ); } bool VFSFile::move( const URI &suri, const URI &duri ) { int32 err = 0; return Sys::fal_move( suri.path(), duri.path(), err ); } bool VFSFile::mkdir( const URI &uri, uint32 mode ) { int32 err = 0; return Sys::fal_mkdir( uri.path(), err ); } bool VFSFile::rmdir( const URI &uri ) { int32 err = 0; return Sys::fal_rmdir( uri.path(), err ); } int64 VFSFile::getLastFsError() { return (int64) GetLastError(); } Error *VFSFile::getLastError() { DWORD ew = GetLastError(); if( ew != 0 ) { IoError *e = new IoError( e_io_error ); e->systemError( ew ); return e; } return 0; } } /* end of vsf_file_unix.cpp */ engine/vfsprovider.cpp000066400000000000000000000010761176363201700154120ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vfsprovider.cpp Generic provider of file system abstraction. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 11 Sep 2008 08:58:33 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include namespace Falcon { VFSProvider::~VFSProvider() { } } /* end of vsfprovider.cpp */ engine/vm.cpp000066400000000000000000003203741176363201700134700ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vm.cpp Implementation of virtual machine - non main loop ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2004-09-08 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Falcon { static ThreadSpecific s_currentVM; VMachine *VMachine::getCurrent() { return (VMachine *) s_currentVM.get(); } VMachine::VMachine(): m_services( &traits::t_string(), &traits::t_voidp() ), m_systemData( this ), m_slots( &traits::t_string(), &traits::t_coreslotptr() ), m_nextVM(0), m_prevVM(0), m_idleNext( 0 ), m_idlePrev( 0 ), m_baton( this ), m_msg_head(0), m_msg_tail(0), m_refcount(1), m_break(false) { internal_construct(); init(); } VMachine::VMachine( bool initItems ): m_services( &traits::t_string(), &traits::t_voidp() ), m_systemData( this ), m_slots( &traits::t_string(), &traits::t_coreslotptr() ), m_nextVM(0), m_prevVM(0), m_idleNext( 0 ), m_idlePrev( 0 ), m_baton( this ), m_msg_head(0), m_msg_tail(0), m_refcount(1), m_break(false) { internal_construct(); if ( initItems ) init(); } void VMachine::incref() { atomicInc( m_refcount ); } void VMachine::decref() { if( atomicDec( m_refcount ) == 0 ) { delete this; } } void VMachine::setCurrent() const { s_currentVM.set( (void*) this ); } void VMachine::internal_construct() { // use a ring for lock items. m_onFinalize = 0; m_userData = 0; m_bhasStandardStreams = false; m_loopsGC = FALCON_VM_DFAULT_CHECK_LOOPS; m_loopsContext = FALCON_VM_DFAULT_CHECK_LOOPS; m_loopsCallback = 0; m_opLimit = 0; m_generation = 0; m_bSingleStep = false; m_stdIn = 0; m_stdOut = 0; m_stdErr = 0; m_launchAtLink = true; m_bGcEnabled = true; m_bWaitForCollect = false; m_bPirorityGC = false; resetCounters(); // this initialization must be performed by all vms. m_mainModule = 0; m_allowYield = true; m_opCount = 0; // This vectror has also context ownership -- when we remove a context here, it's dead m_contexts.deletor( ContextList_deletor ); // finally we create the context (and the stack) m_currentContext = new VMContext; // saving also the first context for accounting reasons. m_contexts.pushBack( m_currentContext ); m_opHandlers = (tOpcodeHandler *) memAlloc( FLC_PCODE_COUNT * sizeof( tOpcodeHandler ) ); m_metaClasses = (CoreClass**) memAlloc( FLC_ITEM_COUNT * sizeof(CoreClass*) ); memset( m_metaClasses, 0, FLC_ITEM_COUNT * sizeof(CoreClass*) ); // Search path appSearchPath( Engine::getSearchPath() ); // This code is actually here for debug reasons. Opcode management should // be performed via a swtich in the end, but until the beta version, this // method allows to have a stack trace telling immediately which opcode // were served in case a problem arises. m_opHandlers[ P_END ] = opcodeHandler_END ; m_opHandlers[ P_NOP ] = opcodeHandler_NOP ; m_opHandlers[ P_PSHN] = opcodeHandler_PSHN; m_opHandlers[ P_RET ] = opcodeHandler_RET ; m_opHandlers[ P_RETA] = opcodeHandler_RETA; // Range 2: one parameter ops m_opHandlers[ P_PTRY] = opcodeHandler_PTRY; m_opHandlers[ P_LNIL] = opcodeHandler_LNIL; m_opHandlers[ P_RETV] = opcodeHandler_RETV; m_opHandlers[ P_FORK] = opcodeHandler_FORK; m_opHandlers[ P_BOOL] = opcodeHandler_BOOL; m_opHandlers[ P_GENA] = opcodeHandler_GENA; m_opHandlers[ P_GEND] = opcodeHandler_GEND; m_opHandlers[ P_PUSH] = opcodeHandler_PUSH; m_opHandlers[ P_PSHR] = opcodeHandler_PSHR; m_opHandlers[ P_POP ] = opcodeHandler_POP ; m_opHandlers[ P_JMP ] = opcodeHandler_JMP ; m_opHandlers[ P_INC ] = opcodeHandler_INC ; m_opHandlers[ P_DEC ] = opcodeHandler_DEC ; m_opHandlers[ P_NEG ] = opcodeHandler_NEG ; m_opHandlers[ P_NOT ] = opcodeHandler_NOT ; m_opHandlers[ P_TRAL] = opcodeHandler_TRAL; m_opHandlers[ P_IPOP] = opcodeHandler_IPOP; m_opHandlers[ P_XPOP] = opcodeHandler_XPOP; m_opHandlers[ P_GEOR] = opcodeHandler_GEOR; m_opHandlers[ P_TRY ] = opcodeHandler_TRY ; m_opHandlers[ P_JTRY] = opcodeHandler_JTRY; m_opHandlers[ P_RIS ] = opcodeHandler_RIS ; m_opHandlers[ P_BNOT] = opcodeHandler_BNOT; m_opHandlers[ P_NOTS] = opcodeHandler_NOTS; m_opHandlers[ P_PEEK] = opcodeHandler_PEEK; // Range3: Double parameter ops m_opHandlers[ P_LD ] = opcodeHandler_LD ; m_opHandlers[ P_LDRF] = opcodeHandler_LDRF; m_opHandlers[ P_ADD ] = opcodeHandler_ADD ; m_opHandlers[ P_SUB ] = opcodeHandler_SUB ; m_opHandlers[ P_MUL ] = opcodeHandler_MUL ; m_opHandlers[ P_DIV ] = opcodeHandler_DIV ; m_opHandlers[ P_MOD ] = opcodeHandler_MOD ; m_opHandlers[ P_POW ] = opcodeHandler_POW ; m_opHandlers[ P_ADDS] = opcodeHandler_ADDS; m_opHandlers[ P_SUBS] = opcodeHandler_SUBS; m_opHandlers[ P_MULS] = opcodeHandler_MULS; m_opHandlers[ P_DIVS] = opcodeHandler_DIVS; m_opHandlers[ P_MODS] = opcodeHandler_MODS; m_opHandlers[ P_POWS] = opcodeHandler_POWS; m_opHandlers[ P_BAND] = opcodeHandler_BAND; m_opHandlers[ P_BOR ] = opcodeHandler_BOR ; m_opHandlers[ P_BXOR] = opcodeHandler_BXOR; m_opHandlers[ P_ANDS] = opcodeHandler_ANDS; m_opHandlers[ P_ORS ] = opcodeHandler_ORS ; m_opHandlers[ P_XORS] = opcodeHandler_XORS; m_opHandlers[ P_GENR] = opcodeHandler_GENR; m_opHandlers[ P_EQ ] = opcodeHandler_EQ ; m_opHandlers[ P_NEQ ] = opcodeHandler_NEQ ; m_opHandlers[ P_GT ] = opcodeHandler_GT ; m_opHandlers[ P_GE ] = opcodeHandler_GE ; m_opHandlers[ P_LT ] = opcodeHandler_LT ; m_opHandlers[ P_LE ] = opcodeHandler_LE ; m_opHandlers[ P_IFT ] = opcodeHandler_IFT ; m_opHandlers[ P_IFF ] = opcodeHandler_IFF ; m_opHandlers[ P_CALL] = opcodeHandler_CALL; m_opHandlers[ P_INST] = opcodeHandler_INST; m_opHandlers[ P_ONCE] = opcodeHandler_ONCE; m_opHandlers[ P_LDV ] = opcodeHandler_LDV ; m_opHandlers[ P_LDP ] = opcodeHandler_LDP ; m_opHandlers[ P_TRAN] = opcodeHandler_TRAN; m_opHandlers[ P_LDAS] = opcodeHandler_LDAS; m_opHandlers[ P_SWCH] = opcodeHandler_SWCH; m_opHandlers[ P_IN ] = opcodeHandler_IN ; m_opHandlers[ P_NOIN] = opcodeHandler_NOIN; m_opHandlers[ P_PROV] = opcodeHandler_PROV; m_opHandlers[ P_STPS] = opcodeHandler_STPS; m_opHandlers[ P_STVS] = opcodeHandler_STVS; m_opHandlers[ P_AND ] = opcodeHandler_AND; m_opHandlers[ P_OR ] = opcodeHandler_OR; // Range 4: ternary opcodes m_opHandlers[ P_STP ] = opcodeHandler_STP ; m_opHandlers[ P_STV ] = opcodeHandler_STV ; m_opHandlers[ P_LDVT] = opcodeHandler_LDVT; m_opHandlers[ P_LDPT] = opcodeHandler_LDPT; m_opHandlers[ P_STPR] = opcodeHandler_STPR; m_opHandlers[ P_STVR] = opcodeHandler_STVR; m_opHandlers[ P_TRAV] = opcodeHandler_TRAV; m_opHandlers[ P_INCP] = opcodeHandler_INCP; m_opHandlers[ P_DECP] = opcodeHandler_DECP; m_opHandlers[ P_SHL ] = opcodeHandler_SHL; m_opHandlers[ P_SHR ] = opcodeHandler_SHR; m_opHandlers[ P_SHLS] = opcodeHandler_SHLS; m_opHandlers[ P_SHRS] = opcodeHandler_SHRS; m_opHandlers[ P_LSB ] = opcodeHandler_LSB; m_opHandlers[ P_SELE ] = opcodeHandler_SELE; m_opHandlers[ P_INDI ] = opcodeHandler_INDI; m_opHandlers[ P_STEX ] = opcodeHandler_STEX; m_opHandlers[ P_TRAC ] = opcodeHandler_TRAC; m_opHandlers[ P_WRT ] = opcodeHandler_WRT; m_opHandlers[ P_STO ] = opcodeHandler_STO; m_opHandlers[ P_FORB ] = opcodeHandler_FORB; m_opHandlers[ P_EVAL ] = opcodeHandler_EVAL; m_opHandlers[ P_CLOS ] = opcodeHandler_CLOS; m_opHandlers[ P_PSHL ] = opcodeHandler_PSHL; m_opHandlers[ P_OOB ] = opcodeHandler_OOB; m_opHandlers[ P_TRDN ] = opcodeHandler_TRDN; m_opHandlers[ P_EXEQ ] = opcodeHandler_EXEQ; // Finally, register to the GC system memPool->registerVM( this ); } void VMachine::init() { //================================ // Preparing minimal input/output if ( m_stdIn == 0 ) m_stdIn = stdInputStream(); if ( m_stdOut == 0 ) m_stdOut = stdOutputStream(); if ( m_stdErr == 0 ) m_stdErr = stdErrorStream(); } void VMachine::finalize() { // we should have at least 2 refcounts here: one is from the caller and one in the GC. fassert( m_refcount >= 2 ); /* * We are destroying the VM, so disable any things * that may access it when it's being freed. */ m_systemData.earlyCleanup(); // disengage from mempool if ( memPool != 0 ) { memPool->unregisterVM( this ); } if ( m_onFinalize != 0 ) m_onFinalize( this ); decref(); } VMachine::~VMachine() { // Free generic tables (quite safe) memFree( m_opHandlers ); memFree( m_metaClasses ); // and finally, the streams. delete m_stdErr; delete m_stdIn; delete m_stdOut; // clear now the global maps // this also decrefs the modules and destroys the globals. // Notice that this would be done automatically also at destructor exit. m_liveModules.clear(); } LiveModule* VMachine::link( Runtime *rt ) { fassert(rt); // link all the modules in the runtime from first to last. // FIFO order is important. uint32 listSize = rt->moduleVector()->size(); LiveModule** lmodList = new LiveModule*[listSize]; LiveModule* lmod = 0; //Make sure we catch falcon errors to delete lmodList afterwards. try { uint32 iter; for( iter = 0; iter < listSize; ++iter ) { ModuleDep *md = rt->moduleVector()->moduleDepAt( iter ); if ( (lmod = prelink( md->module(), rt->hasMainModule() && (iter + 1 == listSize), md->isPrivate() ) ) == 0 ) { delete [] lmodList; return 0; } // use the temporary storage. lmodList[ iter ] = lmod; } // now again, do the complete phase. for( iter = 0; iter < listSize; ++iter ) { if ( ! completeModLink( lmodList[ iter ] ) ) { delete [] lmodList; return 0; } } // returns the topmost livemodule delete [] lmodList; } catch( Error *err ) { delete [] lmodList; throw err; } return lmod; } LiveModule *VMachine::link( Module *mod, bool isMainModule, bool bPrivate ) { // Ok, the module is now in. // We can now increment reference count and add it to ourselves LiveModule *livemod = prelink( mod, isMainModule, bPrivate ); if ( livemod && completeModLink( livemod ) ) { return livemod; } return 0; } LiveModule *VMachine::prelink( Module *mod, bool isMainModule, bool bPrivate ) { // See if we have a module with the same name LiveModule *oldMod = findModule( mod->name() ); if ( oldMod != 0 ) { // if the publish policy is changed, allow this if( oldMod->isPrivate() && ! bPrivate ) { // try to export all if ( ! exportAllSymbols( oldMod ) ) { return 0; } // success; change official policy and return the livemod oldMod->setPrivate( false ); } return oldMod; } // Ok, the module is now in. // We can now increment reference count and add it to ourselves LiveModule *livemod = new LiveModule( mod, bPrivate ); // set this as the main module if required. if ( isMainModule ) m_mainModule = livemod; // then we always need the symbol table. const SymbolTable *symtab = &livemod->module()->symbolTable(); // A shortcut ItemArray& globs = livemod->globals(); // resize() creates a series of NIL items. globs.resize( symtab->size()+1 ); bool success = true; // now, the symbol table must be traversed. MapIterator iter = symtab->map().begin(); while( iter.hasCurrent() ) { Symbol *sym = *(Symbol **) iter.currentValue(); if ( ! sym->isUndefined() ) { if ( ! linkDefinedSymbol( sym, livemod ) ) { // but continue to expose other errors as well. success = false; } } // next symbol iter.next(); } // return zero and dispose of the module if not succesful. if ( ! success ) { // no need to free on failure: livemod are garbaged livemod->mark( 0 ); // LiveModule is garbageable, cannot be destroyed. return 0; } // We can now add the module to our list of available modules. m_liveModules.insert( &livemod->name(), livemod ); livemod->initialized( LiveModule::init_complete ); return livemod; } bool VMachine::completeModLink( LiveModule *livemod ) { // we need to record the classes in the module as they have to be evaluated last. SymbolList modClasses; SymbolList modObjects; // then we always need the symbol table. const SymbolTable *symtab = &livemod->module()->symbolTable(); // we won't be preemptible during link bool atomic = m_currentContext->atomicMode(); m_currentContext->atomicMode(true); bool success = true; // now, the symbol table must be traversed. MapIterator iter = symtab->map().begin(); while( iter.hasCurrent() ) { Symbol *sym = *(Symbol **) iter.currentValue(); if ( sym->isUndefined() ) { if (! linkUndefinedSymbol( sym, livemod ) ) success = false; } else { // save classes and objects for later linking. if( sym->type() == Symbol::tclass ) modClasses.pushBack( sym ); else if ( sym->type() == Symbol::tinst ) modObjects.pushBack( sym ); } // next symbol iter.next(); } // now that the symbols in the module have been linked, link the classes. ListElement *cls_iter = modClasses.begin(); while( cls_iter != 0 ) { Symbol *sym = (Symbol *) cls_iter->data(); fassert( sym->isClass() ); // on error, report failure but proceed. if ( ! linkClassSymbol( sym, livemod ) ) success = false; cls_iter = cls_iter->next(); } // then, prepare the instances of standalone objects ListElement *obj_iter = modObjects.begin(); while( obj_iter != 0 ) { Symbol *obj = (Symbol *) obj_iter->data(); fassert( obj->isInstance() ); // on error, report failure but proceed. if ( ! linkInstanceSymbol( obj, livemod ) ) success = false; obj_iter = obj_iter->next(); } if ( success ) { // eventually, call the constructors declared by the instances obj_iter = modObjects.begin(); // In case we have some objects to link - and while we have no errors, // -- we can't afford calling constructors if everything is not ok. while( success && obj_iter != 0 ) { Symbol *obj = (Symbol *) obj_iter->data(); initializeInstance( obj, livemod ); obj_iter = obj_iter->next(); } } // Initializations of module objects is complete; return to non-atomic mode m_currentContext->atomicMode( atomic ); // return zero and dispose of the module if not succesful. if ( ! success ) { // LiveModule is garbageable, cannot be destroyed. return false; } // and for last, export all the services. MapIterator svmap_iter = livemod->module()->getServiceMap().begin(); while( svmap_iter.hasCurrent() ) { // throws on error. publishService( *(Service ** ) svmap_iter.currentValue() ); svmap_iter.next(); } // execute the main code, if we have one // -- but only if this is NOT the main module if ( m_launchAtLink && m_mainModule != livemod ) { Item *mainItem = livemod->findModuleItem( "__main__" ); if( mainItem != 0 ) { callItem( *mainItem, 0 ); } } return true; } // Link a single symbol bool VMachine::linkSymbol( const Symbol *sym, LiveModule *livemod ) { if ( sym->isUndefined() ) { return linkUndefinedSymbol( sym, livemod ); } return linkDefinedSymbol( sym, livemod ); } bool VMachine::linkDefinedSymbol( const Symbol *sym, LiveModule *livemod ) { // A shortcut ItemArray& globs = livemod->globals(); // Ok, the symbol is defined here. Link (record) it. // create an appropriate item here. // NOTE: Classes and instances are handled separately. switch( sym->type() ) { case Symbol::tfunc: case Symbol::textfunc: globs[ sym->itemId() ].setFunction( new CoreFunc( sym, livemod ) ); break; case Symbol::tvar: case Symbol::tconst: { Item& itm = globs[ sym->itemId() ]; VarDef *vd = sym->getVarDef(); switch( vd->type() ) { case VarDef::t_bool: itm.setBoolean( vd->asBool() ); break; case VarDef::t_int: itm.setInteger( vd->asInteger() ); break; case VarDef::t_num: itm.setNumeric( vd->asNumeric() ); break; case VarDef::t_string: { itm.setString( new CoreString( *vd->asString() ) ); } break; default: break; } } break; // nil when we don't know what it is. default: globs[ sym->itemId() ].setNil(); } // see if the symbol needs exportation and eventually do that. if ( ! exportSymbol( sym, livemod ) ) return false; return true; } bool VMachine::linkUndefinedSymbol( const Symbol *sym, LiveModule *livemod ) { // A shortcut ItemArray& globs = livemod->globals(); const Module *mod = livemod->module(); // is the symbol name-spaced? uint32 dotPos; String localSymName; ModuleDepData *depData; LiveModule *lmod = 0; if ( ( dotPos = sym->name().rfind( "." ) ) != String::npos && sym->imported() ) { String nameSpace = sym->name().subString( 0, dotPos ); // get the module name for the given module depData = mod->dependencies().findModule( nameSpace ); // if we linked it, it must exist // -- but in some cases, the compiler may generate a dotted symbol loaded from external sources // -- this is usually an error, so let the undefined error to be declared. if ( depData != 0 ) { // ... then find the module in the item String absName; Module::absoluteName( depData->isFile() ? nameSpace: depData->moduleName(), mod->name(), absName ); lmod = findModule( absName ); // we must convert the name if it contains self or if it starts with "." if ( lmod != 0 ) localSymName = sym->name().subString( dotPos + 1 ); } } else if ( sym->isImportAlias() ) { depData = mod->dependencies().findModule( sym->getImportAlias()->origModule() ); // if we linked it, it must exist fassert( depData != 0 ); // ... then find the module in the item String absName; Module::absoluteName( depData->moduleName(), mod->name(), absName ); lmod = findModule( absName ); if( lmod != 0 ) localSymName = sym->getImportAlias()->name(); } // If we found it... if ( lmod != 0 ) { Symbol *localSym = lmod->module()->findGlobalSymbol( localSymName ); if ( localSym != 0 ) { referenceItem( globs[ sym->itemId() ], lmod->globals()[ localSym->itemId() ] ); return true; } // last chance: if the module is flexy, we may ask it do dynload it. if( lmod->module()->isFlexy() ) { // Destroy also constness; flexy modules love to be abused. FlexyModule *fmod = (FlexyModule *)( lmod->module() ); Symbol *newsym = fmod->onSymbolRequest( localSymName ); // Found -- great, link it and if all it's fine, link again this symbol. if ( newsym != 0 ) { // be sure to allocate enough space in the module global table. if ( newsym->itemId() >= lmod->globals().length() ) { lmod->globals().resize( newsym->itemId()+1 ); } // now we have space to link it. if ( linkCompleteSymbol( newsym, lmod ) ) { referenceItem( globs[ sym->itemId() ], lmod->globals()[newsym->itemId()] ); return true; } else { // we found the symbol, but it was flacky. We must have raised an error, // and so we should return now. // Notice that there is no need to free the symbol. return false; } } } // ... otherwise, the symbol is undefined. } else { // try to find the imported symbol. SymModule *sm = (SymModule *) m_globalSyms.find( &sym->name() ); if( sm != 0 ) { // link successful, we must set the current item as a reference of the original referenceItem( globs[ sym->itemId() ], *sm->item() ); return true; } } // try to dynamically load the symbol from flexy modules. SymModule symmod; if ( linkSymbolDynamic( sym->name(), symmod ) ) { referenceItem( globs[ sym->itemId() ], *symmod.item() ); return true; } // We failed every try; raise undefined symbol. throw new CodeError( ErrorParam( e_undef_sym, sym->declaredAt() ).origin( e_orig_vm ). module( mod->name() ). extra( sym->name() ) ); } bool VMachine::exportAllSymbols( LiveModule *livemod ) { bool success = true; // now, the symbol table must be traversed. const SymbolTable *symtab = &livemod->module()->symbolTable(); MapIterator iter = symtab->map().begin(); while( iter.hasCurrent() ) { Symbol *sym = *(Symbol **) iter.currentValue(); if ( ! exportSymbol( sym, livemod ) ) { // but continue to expose other errors as well. success = false; } // next symbol iter.next(); } return success; } bool VMachine::exportSymbol( const Symbol *sym, LiveModule *livemod ) { // A shortcut ItemArray& globs = livemod->globals(); const Module *mod = livemod->module(); // Is this symbol exported? if ( ! livemod->isPrivate() && sym->exported() && sym->name().getCharAt(0) != '_' ) { // as long as the module is referenced, the symbols are alive, and as we // hold a reference to the module, we are sure that symbols are alive here. // also, in case an entry already exists, the previous item is just overwritten. if ( m_globalSyms.find( &sym->name() ) != 0 ) { throw new CodeError( ErrorParam( e_already_def, sym->declaredAt() ).origin( e_orig_vm ). module( mod->name() ). symbol( sym->name() ) ); } SymModule tmp( &globs[ sym->itemId() ], livemod, sym ); m_globalSyms.insert( &sym->name(), &tmp ); // export also the instance, if it is not already exported. if ( sym->isInstance() ) { Symbol* tsym = sym->getInstance(); if ( ! tsym->exported() ) { SymModule tmp( &globs[ tsym->itemId() ], livemod, tsym ); m_globalSyms.insert( &tsym->name(), &tmp ); } } } // Is this symbol a well known item? if ( sym->isWKS() ) { if ( m_wellKnownSyms.find( &sym->name() ) != 0 ) { throw new CodeError( ErrorParam( e_already_def, sym->declaredAt() ).origin( e_orig_vm ). module( mod->name() ). symbol( sym->name() ). extra( "Well Known Item" ) ); } SymModule tmp( livemod->wkitems().length(), livemod, sym ); m_wellKnownSyms.insert( &sym->name(), &tmp ); // and don't forget to add a copy of the item livemod->wkitems().append( globs[ sym->itemId() ] ); } return true; } bool VMachine::linkSymbolDynamic( const String &name, SymModule &symdata ) { // For now, the thing is very unoptimized; we'll traverse all the live modules, // and see which of them is flexy. MapIterator iter = m_liveModules.begin(); while( iter.hasCurrent() ) { LiveModule *lmod = *(LiveModule **)(iter.currentValue()); if( lmod->module()->isFlexy() ) { // Destroy also constness; flexy modules love to be abused. FlexyModule *fmod = (FlexyModule *)( lmod->module() ); Symbol *newsym = fmod->onSymbolRequest( name ); // Found -- great, link it and if all it's fine, link again this symbol. if ( newsym != 0 ) { // be sure to allocate enough space in the module global table. if ( newsym->itemId() >= lmod->globals().length() ) { lmod->globals().resize( newsym->itemId()+1 ); } // now we have space to link it. if ( linkCompleteSymbol( newsym, lmod ) ) { symdata = SymModule( &lmod->globals()[ newsym->itemId() ], lmod, newsym ); return true; } else { // we found the symbol, but it was flacky. We must have raised an error, // and so we should return now. // Notice that there is no need to free the symbol. return false; } } // otherwise, go on } iter.next(); } // sorry, not found. return false; } bool VMachine::linkClassSymbol( const Symbol *sym, LiveModule *livemod ) { // shortcut ItemArray& globs = livemod->globals(); CoreClass *cc = linkClass( livemod, sym ); if ( cc == 0 ) return false; // we need to add it anyhow to the GC to provoke its destruction at VM end. // and hey, you could always destroy symbols if your mood is so from falcon ;-) // dereference as other classes may have referenced this item1 globs[ cc->symbol()->itemId() ].dereference()->setClass( cc ); // if this class was a WKI, we must also set the relevant exported symbol if ( sym->isWKS() ) { SymModule *tmp = (SymModule *) m_wellKnownSyms.find( &sym->name() ); fassert( tmp != 0 ); // we just added it tmp->liveModule()->wkitems()[ tmp->wkiid() ] = cc; } if ( sym->getClassDef()->isMetaclassFor() >= 0 ) { m_metaClasses[ sym->getClassDef()->isMetaclassFor() ] = cc; } return true; } bool VMachine::linkInstanceSymbol( const Symbol *obj, LiveModule *livemod ) { // shortcut ItemArray& globs = livemod->globals(); Symbol *cls = obj->getInstance(); Item *clsItem = globs[ cls->itemId() ].dereference(); if ( clsItem == 0 || ! clsItem->isClass() ) { new CodeError( ErrorParam( e_no_cls_inst, obj->declaredAt() ).origin( e_orig_vm ). symbol( obj->name() ). module( obj->module()->name() ) ); return false; } else { CoreObject *co = clsItem->asClass()->createInstance(); globs[ obj->itemId() ].dereference()->setObject( co ); // if this class was a WKI, we must also set the relevant exported symbol if ( obj->isWKS() ) { SymModule *tmp = (SymModule *) m_wellKnownSyms.find( &obj->name() ); fassert ( tmp != 0 ); tmp->liveModule()->wkitems()[ tmp->wkiid() ] = co; } } return true; } void VMachine::initializeInstance( const Symbol *obj, LiveModule *livemod ) { ItemArray& globs = livemod->globals(); Symbol *cls = obj->getInstance(); if ( cls->getClassDef()->constructor() != 0 ) { Item ctor = *globs[cls->getClassDef()->constructor()->itemId() ].dereference(); ctor.methodize( *globs[ obj->itemId() ].dereference() ); // If we can't call, we have a wrong init. callItemAtomic( ctor, 0 ); } CoreObject* cobj = globs[ obj->itemId() ].dereference()->asObject(); if( cobj->generator()->initState() != 0 ) { cobj->setState( "init", cobj->generator()->initState() ); // If we can't call, we have a wrong init. Item enterItem; if( cobj->getMethod("__enter", enterItem ) ) { pushParam(Item()); callItemAtomic( enterItem, 1 ); } } } bool VMachine::linkCompleteSymbol( const Symbol *sym, LiveModule *livemod ) { // try a pre-link bool bSuccess = linkSymbol( sym, livemod ); // Proceed anyhow, even on failure, for classes and instance symbols if( sym->type() == Symbol::tclass ) { if ( ! linkClassSymbol( sym, livemod ) ) bSuccess = false; } else if ( sym->type() == Symbol::tinst ) { fassert( sym->getInstance() != 0 ); // we can't try to call the initialization method // if the creation of the symbol fails. if ( linkClassSymbol( sym->getInstance(), livemod ) && linkInstanceSymbol( sym, livemod ) ) { initializeInstance( sym, livemod ); } else bSuccess = false; } return bSuccess; } bool VMachine::linkCompleteSymbol( Symbol *sym, const String &moduleName ) { LiveModule *lm = findModule( moduleName ); if ( lm != 0 ) return linkCompleteSymbol( sym, lm ); return false; } PropertyTable *VMachine::createClassTemplate( LiveModule *lmod, const Map &pt ) { MapIterator iter = pt.begin(); PropertyTable *table = new PropertyTable( pt.size() ); bool bHasSetGet = false; while( iter.hasCurrent() ) { VarDefMod *vdmod = *(VarDefMod **) iter.currentValue(); VarDef *vd = vdmod->vd; String *key = *(String **) iter.currentKey(); PropEntry &e = table->appendSafe( key ); e.m_bReadOnly = vd->isReadOnly(); // configure the element if ( vd->isReflective() ) { e.m_eReflectMode = vd->asReflecMode(); // we must keep this information for later. if ( e.m_eReflectMode == e_reflectSetGet ) bHasSetGet = true; else e.m_reflection.offset = vd->asReflecOffset(); } else if ( vd->isReflectFunc() ) { e.m_eReflectMode = e_reflectFunc; e.m_reflection.rfunc.to = vd->asReflectFuncTo(); e.m_reflection.rfunc.from = vd->asReflectFuncFrom(); e.reflect_data = vd->asReflectFuncData(); // just to be paranoid if( e.m_reflection.rfunc.to == 0 ) e.m_bReadOnly = true; } // create the instance switch( vd->type() ) { case VarDef::t_nil: e.m_value.setNil(); break; case VarDef::t_bool: e.m_value.setBoolean( vd->asBool() ); break; case VarDef::t_int: e.m_value.setInteger( vd->asInteger() ); break; case VarDef::t_num: e.m_value.setNumeric( vd->asNumeric() ); break; case VarDef::t_string: { e.m_value.setString( new CoreString( *vd->asString() ) ); } break; case VarDef::t_base: e.m_bReadOnly = true; case VarDef::t_reference: { const Symbol *sym = vd->asSymbol(); referenceItem( e.m_value, vdmod->lmod->globals()[ sym->itemId() ] ); } break; case VarDef::t_symbol: { Symbol *sym = const_cast< Symbol *>( vd->asSymbol() ); // may be a function or an extfunc fassert( sym->isExtFunc() || sym->isFunction() ); if ( sym->isExtFunc() || sym->isFunction() ) { e.m_value.setFunction( new CoreFunc( sym, vdmod->lmod ) ); } } break; default: break; // compiler warning no-op } iter.next(); } // complete setter/getter if( bHasSetGet ) { for( uint32 pp = 0; pp < table->added(); ++pp ) { PropEntry& pe = table->getEntry(pp); if( pe.m_eReflectMode == e_reflectSetGet ) { uint32 pos; if( table->findKey(String("__set_") + *pe.m_name, pos ) ) { pe.m_reflection.gs.m_setterId = pos; } else { pe.m_reflection.gs.m_setterId = PropEntry::NO_OFFSET; pe.m_bReadOnly = true; } if( table->findKey(String("__get_") + *pe.m_name, pos ) ) { pe.m_reflection.gs.m_getterId = pos; } else { pe.m_reflection.gs.m_getterId = PropEntry::NO_OFFSET; } } } } table->checkProperties(); return table; } CoreClass *VMachine::linkClass( LiveModule *lmod, const Symbol *clssym ) { Map props( &traits::t_stringptr(), &traits::t_voidp() ); Map states( &traits::t_stringptr(), &traits::t_voidp() ); ObjectFactory factory = 0; if( ! linkSubClass( lmod, clssym, props, states, &factory ) ) return 0; CoreClass *cc = new CoreClass( clssym, lmod, createClassTemplate( lmod, props ) ); Symbol *ctor = clssym->getClassDef()->constructor(); if ( ctor != 0 ) { cc->constructor().setFunction( new CoreFunc( ctor, lmod ) ); } // destroy the temporary vardef we have created MapIterator iter = props.begin(); while( iter.hasCurrent() ) { VarDefMod *value = *(VarDefMod **) iter.currentValue(); delete value; iter.next(); } // apply the state map if( ! states.empty() ) { MapIterator siter = states.begin(); ItemDict* dict = new LinearDict; ItemDict* initState = 0; while ( siter.hasCurrent() ) { const String* sname = *(String**) siter.currentKey(); const Map* sd = *(Map**) siter.currentValue(); ItemDict *sdict = new LinearDict(sd->size()); MapIterator fiter = sd->begin(); while( fiter.hasCurrent() ) { const String* fname = *(String**) fiter.currentKey(); CoreFunc* sfunc = *(CoreFunc**) fiter.currentValue(); sdict->put( new CoreString( *fname ), sfunc ); fiter.next(); } delete sd; // TODO: See if we can use the const String* form sd->name() here dict->put( new CoreString( *sname ), new CoreDict(sdict) ); if( *sname == "init" ) initState = sdict; siter.next(); } cc->states( dict, initState ); } // ok, now determine the default object factory, if not provided. if( factory != 0 ) { cc->factory( factory ); } else { // use one of our standard factories. if ( ! cc->properties().isReflective() ) { // a standard falcon object cc->factory( FalconObjectFactory ); } else { if ( cc->properties().isStatic() ) { // A fully reflective class. cc->factory( ReflectFalconFactory ); } else { // a partially reflective class. cc->factory( CRFalconFactory ); } } } return cc; } bool VMachine::linkSubClass( LiveModule *lmod, const Symbol *clssym, Map &props, Map &states, ObjectFactory *factory ) { // first sub-instantiates all the inheritances. ClassDef *cd = clssym->getClassDef(); ListElement *from_iter = cd->inheritance().begin(); const Module *class_module = clssym->module(); // If the class is final, we're doomed, as this is called on subclasses if( cd->isFinal() ) { throw new CodeError( ErrorParam( e_final_inherit, clssym->declaredAt() ).origin( e_orig_vm ). symbol( clssym->name() ). module( class_module->name() ) ); } if( *factory != 0 && cd->factory() != 0 ) { // raise an error for duplicated object manager. throw new CodeError( ErrorParam( e_inv_inherit2, clssym->declaredAt() ).origin( e_orig_vm ). symbol( clssym->name() ). module( class_module->name() ) ); } ObjectFactory subFactory = 0; while( from_iter != 0 ) { const InheritDef *def = (const InheritDef *) from_iter->data(); const Symbol *parent = def->base(); // iterates in the parent. Where is it? // 1) in the same module or 2) in the global modules. if( parent->isClass() ) { // do we have some circular inheritance if ( parent->getClassDef()->checkCircularInheritance( clssym ) ) throw new CodeError( ErrorParam( e_circular_inh, __LINE__ ) .origin( e_orig_vm ) .extra( clssym->name() ) ); // we create the item anew instead of relying on the already linked item. LiveModule *parmod = findModule( parent->module()->name() ); if ( ! linkSubClass( parmod, parent, props, states, &subFactory ) ) return false; } else if ( parent->isUndefined() ) { // we have already linked the symbol for sure. Item *icls = lmod->globals()[ parent->itemId() ].dereference(); if ( ! icls->isClass() ) { throw new CodeError( ErrorParam( e_inv_inherit, clssym->declaredAt() ).origin( e_orig_vm ). symbol( clssym->name() ). module( class_module->name() ) ); } parent = icls->asClass()->symbol(); if ( parent->getClassDef()->checkCircularInheritance( clssym ) ) throw new CodeError( ErrorParam( e_circular_inh, __LINE__ ) .origin( e_orig_vm ) .extra( clssym->name() ) ); LiveModule *parmod = findModule( parent->module()->name() ); if ( ! linkSubClass( parmod, parent, props, states, &subFactory ) ) return false; } else { throw new CodeError( ErrorParam( e_inv_inherit, clssym->declaredAt() ).origin( e_orig_vm ). symbol( clssym->name() ). module( class_module->name() ) ); } from_iter = from_iter->next(); } // assign our manager if ( cd->factory() != 0 ) *factory = cd->factory(); else *factory = subFactory; // then copies the vardefs declared in this class. MapIterator iter = cd->properties().begin(); while( iter.hasCurrent() ) { String *key = *(String **) iter.currentKey(); VarDefMod *value = new VarDefMod; value->vd = *(VarDef **) iter.currentValue(); value->lmod = lmod; // TODO: define vardefvalue traits VarDefMod **oldValue = (VarDefMod **) props.find( key ); if ( oldValue != 0 ) delete *oldValue; //========================== props.insert( key, value ); iter.next(); } // and the same for the states. MapIterator siter = cd->states().begin(); while( siter.hasCurrent() ) { String *stateName = *(String **) siter.currentKey(); StateDef* sd = *(StateDef **) siter.currentValue(); Map* sfuncs = new Map( &traits::t_stringptr(), &traits::t_voidp() ); MapIterator fiter = sd->functions().begin(); while( fiter.hasCurrent() ) { const String* fname = *(String**) fiter.currentKey(); const Symbol* fsym = *(Symbol**) fiter.currentValue(); CoreFunc* sfunc = new CoreFunc( fsym, lmod ); CoreFunc **oldFunc = (CoreFunc **) sfuncs->find( fname ); if ( oldFunc != 0 ) { delete *oldFunc; *oldFunc = sfunc; } else { sfuncs->insert( fname, sfunc ); } fiter.next(); } //========================== Map** oldFuncs =(Map**) states.find( stateName ); if( oldFuncs != 0 ) { delete *oldFuncs; *oldFuncs = sfuncs; } else { states.insert( stateName, sfuncs ); } siter.next(); } return true; } void VMachine::reset() { // first, the trivial resets. // reset counters resetCounters(); // clear the accounting of sleeping contexts. m_sleepingContexts.clear(); if ( m_contexts.size() > 1 ) { // clear the contexts m_contexts.clear(); // as our frame, stack and tryframe were in one of the contexts, // they have been destroyed. m_currentContext = new VMContext; // saving also the first context for accounting reasons. m_contexts.pushBack( m_currentContext ); } else { m_currentContext->resetFrames(); } } const SymModule *VMachine::findGlobalSymbol( const String &name ) const { return (SymModule *) m_globalSyms.find( &name ); } bool VMachine::getCaller( const Symbol *&sym, const Module *&module) { StackFrame* frame = currentFrame(); if( frame == 0 || frame->m_module == 0 ) return false; sym = frame->m_symbol; module = frame->m_module->module(); return sym != 0 && module != 0; } bool VMachine::getCallerItem( Item &caller, uint32 level ) { StackFrame* frame = currentFrame(); while( (frame != 0 && frame->m_symbol != 0 ) && level > 0 ) { frame = frame->prev(); level--; } if ( frame == 0 || frame->m_symbol == 0 ) return false; const Symbol* sym = frame->m_symbol; const LiveModule* module = frame->m_module; caller = module->globals()[ sym->itemId() ]; if ( ! caller.isFunction() ) return false; // was it a method ? if ( ! frame->m_self.isNil() ) { caller.methodize( frame->m_self ); } return true; } void VMachine::fillErrorContext( Error *err, bool filltb ) { if( currentSymbol() != 0 ) { if ( err->module().size() == 0 ) err->module( currentModule()->name() ); if ( err->symbol().size() == 0 ) err->symbol( currentSymbol()->name() ); if( currentSymbol()->isFunction() ) err->line( currentModule()->getLineAt( currentSymbol()->getFuncDef()->basePC() + programCounter() ) ); err->pcounter( programCounter() ); } if ( filltb ) fillErrorTraceback( *err ); } void VMachine::callFrameNow( ext_func_frame_t callbackFunc ) { currentFrame()->m_endFrameFunc = callbackFunc; switch( m_currentContext->pc() ) { case i_pc_call_external_ctor: m_currentContext->pc_next() = i_pc_call_external_ctor_return; break; case i_pc_call_external: m_currentContext->pc_next() = i_pc_call_external_return; break; default: m_currentContext->pc_next() = m_currentContext->pc(); } } void VMachine::callItemAtomic(const Item &callable, int32 paramCount ) { bool oldAtomic = m_currentContext->atomicMode(); m_currentContext->atomicMode( true ); callFrame( callable, paramCount ); execFrame(); m_currentContext->atomicMode( oldAtomic ); } void VMachine::yield( numeric secs ) { if ( m_currentContext->atomicMode() ) { throw new InterruptedError( ErrorParam( e_wait_in_atomic, __LINE__ ) .origin( e_orig_vm ) .symbol( "yield" ) .module( "core.vm" ) .hard() ); } // be sure to allow yelding. m_allowYield = true; // a pure sleep time can never be < 0. if( secs < 0.0 ) secs = 0.0; m_currentContext->scheduleAfter( secs ); rotateContext(); } void VMachine::putAtSleep( VMContext *ctx ) { // consider the special case of a context not willing to be awaken if( ctx->isWaitingForever() ) { m_sleepingContexts.pushBack( ctx ); return; } ListElement *iter = m_sleepingContexts.begin(); while( iter != 0 ) { VMContext *curctx = (VMContext *) iter->data(); if ( ctx->schedule() < curctx->schedule() || curctx->schedule() < 0.0 ) { m_sleepingContexts.insertBefore( iter, ctx ); return; } iter = iter->next(); } // can't find it anywhere? m_sleepingContexts.pushBack( ctx ); } void VMachine::reschedule( VMContext *ctx ) { ListElement *iter = m_sleepingContexts.begin(); bool bPlaced = false; bool bRemoved = false; while( iter != 0 ) { VMContext *curctx = (VMContext *) iter->data(); // if the rescheduled context is in sleeping context, // signal we've found it. if ( curctx == ctx ) { ListElement *old = iter; iter = iter->next(); m_sleepingContexts.erase( old ); // -- did we find the position where to place it, we did it all. // -- go away. if ( bPlaced ) return; // but if item was not placed because it has an endless sleep, we have // no gain in scanning more than this. So just place at the end // (by breaking) if( ctx->schedule() < 0.0 ) break; // otherwise, continue working bRemoved = true; continue; } // avoid to place twice if ( ! bPlaced && ( ctx->schedule() < curctx->schedule() || curctx->schedule() < 0.0 ) ) { m_sleepingContexts.insertBefore( iter, ctx ); // if we have also already removed the previous position, we did all if( bRemoved ) return; bPlaced = true; } iter = iter->next(); } // can't find any place to store it? if ( ! bPlaced ) m_sleepingContexts.pushBack( ctx ); } void VMachine::rotateContext() { putAtSleep( m_currentContext ); electContext(); } void VMachine::electContext() { // if there is some sleeping context... if ( ! m_sleepingContexts.empty() ) { bool bInterrupted = false; while( true ) { VMContext *elect = (VMContext *) m_sleepingContexts.front(); m_sleepingContexts.popFront(); // change the context to the first ready to run. m_currentContext = elect; // we must move to the next instruction after the context was swapped. m_currentContext->pc() = m_currentContext->pc_next(); numeric tgtTime = elect->schedule(); // Is the most ready context willing to sleep? if( tgtTime < 0.0 || (tgtTime -= Sys::_seconds()) > 0.0 ) { // If we're here after being interrupted, it means we didn't find // a suitable runnable context after an interruption. /*if( bInterrupted ) { // Then this is a real interruption, and we got to die. throw new InterruptedError( ErrorParam( e_interrupted ).origin( e_orig_vm ). symbol( currentSymbol()->name() ). module( currentModule()->name() ) ); }*/ // This may wait forever (with a tgtime < 0); // in this case the function may throw a deadlock error bInterrupted = replaceMe_onIdleTime( tgtTime ); // very probably, the context we selected is not ready to run. // Try again selecting a new context that another thread may have // inserted in the meanwhile. if( bInterrupted ) { putAtSleep( m_currentContext ); continue; } } // tell the context that it is not waiting anymore, if it was. m_currentContext->wakeup( false ); m_opCount = 0; break; } } } void VMachine::terminateCurrentContext() { // don't destroy this context if it's the last one. // inspectors outside this VM may want to check it. if ( ! m_contexts.empty() && m_contexts.begin()->next() != 0 ) { // scan the contexts and remove the current one. ListElement *iter = m_contexts.begin(); while( iter != 0 ) { if( iter->data() == m_currentContext ) { m_contexts.erase( iter ); m_currentContext = 0; break; } iter = iter->next(); } // there must be something sleeping fassert( ! m_sleepingContexts.empty() ); electContext(); } else { // we're done throw VMEventQuit(); } } void VMachine::itemToString( String &target, const Item *itm, const String &format ) { if( itm->isObject() ) { Item propString; if( itm->asObjectSafe()->getMethod( "toString", propString ) ) { if ( propString.type() == FLC_ITEM_STRING ) target = *propString.asString(); else { Item old = self(); // eventually push parameters if format is required int params = 0; if( format.size() != 0 ) { pushParam( new CoreString(format) ); params = 1; } // atomically call the item callItemAtomic( propString, params ); self() = old; // if regA is already a string, it's a quite light operation. regA().toString( target ); } } else itm->toString( target ); } else itm->toString( target ); } bool VMachine::seekInteger( int64 value, byte *base, uint16 size, uint32 &landing ) const { #undef SEEK_STEP #define SEEK_STEP (sizeof(int64) + sizeof(int32)) fassert( size > 0 ); // should be granted before call int32 higher = size-1; byte *pos; int32 lower = 0; int32 point = higher / 2; while ( lower < higher - 1 ) { pos = base + point * SEEK_STEP; if ( loadInt64( pos ) == value ) { landing = *reinterpret_cast< uint32 *>( pos + sizeof(int64) ); return true; } if ( value > loadInt64( pos ) ) lower = point; else higher = point; point = ( lower + higher ) / 2; } // see if it was in the last loop if ( loadInt64( base + lower * SEEK_STEP ) == value ) { landing = *reinterpret_cast< uint32 *>( base + lower * SEEK_STEP + sizeof( int64 ) ); return true; } if ( lower != higher && loadInt64( base + higher * SEEK_STEP ) == value ) { // YATTA, we found it at last landing = *reinterpret_cast< uint32 *>( base + higher * SEEK_STEP + sizeof( int64 ) ); return true; } return false; } bool VMachine::seekInRange( int64 numLong, byte *base, uint16 size, uint32 &landing ) const { #undef SEEK_STEP #define SEEK_STEP (sizeof(int32) + sizeof(int32) + sizeof(int32)) fassert( size > 0 ); // should be granted before call int32 higher = size-1; byte *pos; int32 value = (int32) numLong; int32 lower = 0; int32 point = higher / 2; while ( lower < higher - 1 ) { pos = base + point * SEEK_STEP; if ( *reinterpret_cast< int32 *>( pos ) <= value && *reinterpret_cast< int32 *>( pos + sizeof( int32 ) ) >= value) { landing = *reinterpret_cast< int32 *>( pos + sizeof( int32 ) + sizeof( int32 ) ); return true; } if ( value > *reinterpret_cast< int32 *>( pos ) ) lower = point; else higher = point; point = ( lower + higher ) / 2; } // see if it was in the last loop pos = base + lower * SEEK_STEP; if ( *reinterpret_cast< int32 *>( pos ) <= value && *reinterpret_cast< int32 *>( pos + sizeof( int32 ) ) >= value ) { landing = *reinterpret_cast< uint32 *>( pos + sizeof( int32 ) + sizeof( int32 ) ); return true; } if( lower != higher ) { pos = base + higher * SEEK_STEP; if ( *reinterpret_cast< int32 *>( pos ) <= value && *reinterpret_cast< int32 *>( pos + sizeof( int32 ) ) >= value ) { // YATTA, we found it at last landing = *reinterpret_cast< uint32 *>( pos + sizeof( int32 ) + sizeof( int32 ) ); return true; } } return false; } bool VMachine::seekString( const String *value, byte *base, uint16 size, uint32 &landing ) const { #undef SEEK_STEP #define SEEK_STEP (sizeof(int32) + sizeof(int32)) fassert( size > 0 ); // should be granted before call int32 higher = size-1; byte *pos; int32 lower = 0; int32 point = higher / 2; const String *paragon; while ( lower < higher - 1 ) { pos = base + point * SEEK_STEP; paragon = currentModule()->getString( *reinterpret_cast< int32 *>( pos ) ); fassert( paragon != 0 ); if ( paragon == 0 ) return false; if ( *paragon == *value ) { landing = *reinterpret_cast< int32 *>( pos + sizeof(int32) ); return true; } if ( *value > *paragon ) lower = point; else higher = point; point = ( lower + higher ) / 2; } // see if it was in the last loop paragon = currentModule()->getString( *reinterpret_cast< uint32 *>( base + lower * SEEK_STEP ) ); if ( paragon != 0 && *paragon == *value ) { landing = *reinterpret_cast< uint32 *>( base + lower * SEEK_STEP + sizeof( int32 ) ); return true; } if ( lower != higher ) { paragon = currentModule()->getString( *reinterpret_cast< uint32 *>( base + higher * SEEK_STEP ) ); if ( paragon != 0 && *paragon == *value ) { // YATTA, we found it at last landing = *reinterpret_cast< uint32 *>( base + higher * SEEK_STEP + sizeof( int32 ) ); return true; } } return false; } bool VMachine::seekItem( const Item *item, byte *base, uint16 size, uint32 &landing ) { #undef SEEK_STEP #define SEEK_STEP (sizeof(int32) + sizeof(int32)) byte *target = base + size *SEEK_STEP; while ( base < target ) { Symbol *sym = currentModule()->getSymbol( *reinterpret_cast< int32 *>( base ) ); fassert( sym ); if ( sym == 0 ) return false; switch( sym->type() ) { case Symbol::tlocal: if( *local( sym->itemId() )->dereference() == *item ) goto success; break; case Symbol::tparam: if( *param( sym->itemId() )->dereference() == *item ) goto success; break; default: if( *moduleItem( sym->itemId() ).dereference() == *item ) goto success; } base += SEEK_STEP; } return false; success: landing = *reinterpret_cast< uint32 *>( base + sizeof(int32) ); return true; } bool VMachine::seekItemClass( const Item *itm, byte *base, uint16 size, uint32 &landing ) const { #undef SEEK_STEP #define SEEK_STEP (sizeof(int32) + sizeof(int32)) byte *target = base + size *SEEK_STEP; while ( base < target ) { Symbol *sym = currentModule()->getSymbol( *reinterpret_cast< uint32 *>( base ) ); fassert( sym ); if ( sym == 0 ) return false; const Item *cfr; if ( sym->isLocal() ) { cfr = local( sym->itemId() )->dereference(); } else if ( sym->isParam() ) { cfr = param( sym->itemId() ); } else { cfr = moduleItem( sym->itemId() ).dereference(); } switch( cfr->type() ) { case FLC_ITEM_CLASS: if ( itm->isObject() ) { const CoreObject *obj = itm->asObjectSafe(); if ( obj->derivedFrom( cfr->asClass()->symbol()->name() ) ) goto success; } else if (itm->isClass() && itm->asClass()->derivedFrom( cfr->asClass()->symbol() ) ) { goto success; } break; case FLC_ITEM_OBJECT: if ( itm->isObject() ) { if( itm->asObject() == cfr->asObjectSafe() ) goto success; } break; case FLC_ITEM_INT: if ( cfr->asInteger() == itm->type() ) { goto success; } break; case FLC_ITEM_STRING: if ( itm->isObject() && itm->asObjectSafe()->derivedFrom( *cfr->asString() ) ) goto success; break; } base += SEEK_STEP; } return false; success: landing = *reinterpret_cast< uint32 *>( base + sizeof(int32) ); return true; } void VMachine::publishService( Service *svr ) { Service **srv = (Service **) m_services.find( &svr->getServiceName() ); if ( srv == 0 ) { m_services.insert( &svr->getServiceName(), svr ); } else { throw new CodeError( ErrorParam( e_service_adef ).origin( e_orig_vm ). extra( svr->getServiceName() ). symbol( "publishService" ). module( "core.vm" ) ); } } Service *VMachine::getService( const String &name ) { Service **srv = (Service **) m_services.find( &name ); if ( srv == 0 ) return 0; return *srv; } void VMachine::stdIn( Stream *nstream ) { delete m_stdIn; m_stdIn = nstream; } void VMachine::stdOut( Stream *nstream ) { delete m_stdOut; m_stdOut = nstream; } void VMachine::stdErr( Stream *nstream ) { delete m_stdErr; m_stdErr = nstream; } void ContextList_deletor( void *data ) { VMContext *vmc = (VMContext *) data; delete vmc; } const String &VMachine::moduleString( uint32 stringId ) const { static String empty; if ( currentModule() == 0 ) return empty; const String *str = currentModule()->getString( stringId ); if( str != 0 ) return *str; return empty; } void VMachine::resetCounters() { m_opCount = 0; m_opNextGC = m_loopsGC; m_opNextContext = m_loopsContext; m_opNextCallback = m_loopsCallback; m_opNextCheck = m_loopsGC < m_loopsContext ? m_loopsGC : m_loopsContext; if ( m_opNextCallback != 0 && m_opNextCallback < m_opNextCheck ) { m_opNextCheck = m_opNextCallback; } } // basic implementation does nothing. void VMachine::periodicCallback() {} // TODO move elsewhere inline bool vmIsWhiteSpace( uint32 chr ) { return chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r'; } inline bool vmIsTokenChr( uint32 chr ) { return chr >= 'A' || (chr >= '0' && chr <= '9') || chr == '_'; } Item *VMachine::findLocalSymbolItem( const String &symName ) const { // parse self and sender if( symName == "self" ) { return const_cast(&self()); } // find the symbol const Symbol *sym = currentSymbol(); if ( sym != 0 ) { // get the relevant symbol table. const SymbolTable *symtab; switch( sym->type() ) { case Symbol::tclass: symtab = &sym->getClassDef()->symtab(); break; case Symbol::tfunc: symtab = &sym->getFuncDef()->symtab(); break; case Symbol::textfunc: symtab = sym->getExtFuncDef()->parameters(); break; default: symtab = 0; } if ( symtab != 0 ) sym = symtab->findByName( symName ); else sym = 0; // try again } // -- not a local symbol? -- try the global module table. if( sym == 0 ) { sym = currentModule()->findGlobalSymbol( symName ); // still zero? Let's try the global symbol table. if( sym == 0 ) { Item *itm = findGlobalItem( symName ); if ( itm != 0 ) return itm->dereference(); } } Item *itm = 0; if ( sym != 0 ) { if ( sym->isLocal() ) { itm = const_cast(this)->local( sym->getItemId() )->dereference(); } else if ( sym->isParam() ) { itm = const_cast(this)->param( sym->getItemId() )->dereference(); } else { itm = const_cast(this)->moduleItem( sym->getItemId() ).dereference(); } } // if the item is zero, we didn't found it return itm; } bool VMachine::findLocalVariable( const String &name, Item &itm ) const { // item to be returned. String sItemName; uint32 squareLevel = 0; uint32 len = name.length(); typedef enum { initial, firstToken, interToken, dotAccessor, dotArrayAccessor, dotDictAccessor, squareAccessor, postSquareAccessor, singleQuote, doubleQuote, strEscapeSingle, strEscapeDouble } t_state; t_state state = initial; uint32 pos = 0; while( pos <= len ) { // little trick: force a ' ' at len uint32 chr; if( pos == len ) chr = ' '; else chr = name.getCharAt( pos ); switch( state ) { case initial: if( vmIsWhiteSpace( chr ) ) { pos++; continue; } if( chr < 'A' ) return false; state = firstToken; sItemName.append( chr ); break; //=================================================== // Parse first token. It must be a valid local symbol case firstToken: if ( vmIsWhiteSpace( chr ) || chr == '.' || chr == '[' ) { Item *lsi = findLocalSymbolItem( sItemName ); // item not found? if( lsi == 0 ) return false; itm = *lsi; // set state accordingly to chr. goto resetState; } else if ( vmIsTokenChr( chr ) ) { sItemName.append( chr ); } else { // invalid format return false; } break; //=================================================== // Parse a dot accessor. // case dotAccessor: // wating for a complete token. if ( vmIsWhiteSpace( chr ) || chr == '.' || chr == '[' ) { // ignore leading ws. if( sItemName.size() == 0 && vmIsWhiteSpace( chr ) ) break; // access the item. We know it's an object or class or we wouldn't be in this state. // also, notice that we change the item itself. Item prop; if( itm.isClass() ) { const Falcon::Item* requested = itm.asClass()->properties().getValue( sItemName ); if( requested == 0 ) return false; prop = *requested; } else if ( !itm.asObjectSafe()->getProperty( sItemName, prop ) ) return false; prop.methodize( itm ); itm = prop; // set state accordingly to chr. goto resetState; } else if ( vmIsTokenChr( chr ) ) { sItemName.append( chr ); } else return false; break; case dotArrayAccessor: // wating for a complete token. if ( vmIsWhiteSpace( chr ) || chr == '.' || chr == '[' ) { // ignore leading ws. if( sItemName.size() == 0 && vmIsWhiteSpace( chr ) ) break; // access the item. We know it's an object or we wouldn't be in this state. // also, notice that we change the item itself. Item *tmp; if ( ( tmp = itm.asArray()->getProperty( sItemName ) ) == 0 ) return false; if ( tmp->isFunction() ) tmp->setMethod( itm, tmp->asFunction() ); itm = *tmp; // set state accordingly to chr. goto resetState; } else if ( vmIsTokenChr( chr ) ) { sItemName.append( chr ); } else return false; break; case dotDictAccessor: // wating for a complete token. if ( vmIsWhiteSpace( chr ) || chr == '.' || chr == '[' ) { // ignore leading ws. if( sItemName.size() == 0 && vmIsWhiteSpace( chr ) ) break; // access the item. We know it's an object or we wouldn't be in this state. // also, notice that we change the item itself. Item *tmp; if ( ( tmp = itm.asDict()->find( sItemName ) ) == 0 ) return false; if ( tmp->isFunction() ) tmp->setMethod( itm, tmp->asFunction() ); itm = *tmp; // set state accordingly to chr. goto resetState; } else if ( vmIsTokenChr( chr ) ) { sItemName.append( chr ); } else return false; break; //=================================================== // Parse the square accessor; from [ to matching ] case squareAccessor: // wating for complete square token. switch( chr ) { case '[': squareLevel++; sItemName.append( chr ); break; case ']': if( --squareLevel == 0 ) { Item *lsi = parseSquareAccessor( itm, sItemName ); if( lsi == 0 ) return false; itm = *lsi; goto resetState; } else sItemName.append( chr ); break; case '\'': sItemName.append( chr ); state = singleQuote; break; case '"': sItemName.append( chr ); state = doubleQuote; break; default: sItemName.append( chr ); } break; case postSquareAccessor: // wating for complete square token. if( chr == ']' ) { if( --squareLevel == 0 ) { Item *lsi = parseSquareAccessor( itm, sItemName ); if( lsi == 0 ) return false; itm = *lsi; goto resetState; } else sItemName.append( chr ); } else if( ! vmIsWhiteSpace( chr ) ) { return false; } break; //=================================================== // Parse the double quote inside suqare accessor case doubleQuote: switch( chr ) { case '\\': state = strEscapeDouble; break; case '"': state = postSquareAccessor; // do not break default: sItemName.append( chr ); } break; //=================================================== // Parse the single quote inside suqare accessor case singleQuote: switch( chr ) { case '\\': state = strEscapeSingle; break; case '\'': state = squareAccessor; // do not break default: sItemName.append( chr ); } break; //=================================================== // Parse the double quote inside suqare accessor case strEscapeDouble: sItemName.append( chr ); state = doubleQuote; break; //=================================================== // Parse the single quote inside suqare accessor case strEscapeSingle: sItemName.append( chr ); state = singleQuote; break; //=================================================== // Parse the space between tokens. case interToken: switch( chr ) { case '.': if( itm.isObject() ) state = dotAccessor; else if( itm.isArray() ) state = dotArrayAccessor; else if( itm.isDict() && itm.asDict()->isBlessed() ) state = dotDictAccessor; else return false; break; case '[': if( ! itm.isDict() && ! itm.isArray() ) return false; state = squareAccessor; squareLevel = 1; break; default: if( ! vmIsWhiteSpace( chr ) ) return false; } break; } // end the loop here pos++; continue; // state reset area. resetState: sItemName.size(0); // clear sItemName. switch( chr ) { case '.': if( itm.isObject() || itm.isClass() ) state = dotAccessor; else if ( itm.isArray() ) state = dotArrayAccessor; else if ( itm.isDict() && itm.asDict()->isBlessed() ) state = dotDictAccessor; else return false; break; case '[': if( ! itm.isDict() && ! itm.isArray() ) return false; state = squareAccessor; squareLevel = 1; break; default: state = interToken; } // end of loop, increment pos. pos++; } // if the state is not "interToken" we have an incomplete parse if( state != interToken ) return false; // Success return true; } Item *VMachine::parseSquareAccessor( const Item &accessed, String &accessor ) const { accessor.trim(); // empty accessor? -- can't access! if( accessor.length() == 0) return 0; // what's the first character of the accessor? uint32 firstChar = accessor.getCharAt( 0 ); // parse the accessor. Item acc; String da; if( firstChar >= '0' && firstChar <= '9' ) { // try to parse a number. int64 num; if( accessor.parseInt( num ) ) acc.setInteger( num ); else return 0; } else if( firstChar == '\'' || firstChar == '"' ) { // arrays cannot be accessed by strings. if( accessed.isArray() ) return 0; da = accessor.subString( 1, accessor.length() - 1 ); acc.setString( &da ); } else { // reparse the accessor as a token if( ! findLocalVariable( accessor, acc ) ) return 0; } // what's the accessed item? if ( accessed.isDict() ) { // find the accessor return accessed.asDict()->find( acc ); } else if( accessed.isArray() ) { // for arrays, only nubmbers and reaccessed items are if( !acc.isOrdinal() ) return 0; uint32 pos = (uint32) acc.forceInteger(); if( pos >= accessed.asArray()->length() ) return 0; return &accessed.asArray()->at( pos ); } return 0; } VMachine::returnCode VMachine::expandString( const String &src, String &target ) { uint32 pos0 = 0; uint32 pos1 = src.find( "$" ); uint32 len = src.length(); while( pos1 != String::npos ) { target.append( src.subString( pos0, pos1 ) ); pos1++; if( pos1 == len ) { return return_error_string; } typedef enum { none, token, open, singleQuote, doubleQuote, escapeSingle, escapeDouble, complete, complete1, noAction, fail } t_state; t_state state = none; pos0 = pos1; uint32 chr = 0; while( pos1 < len && state != fail && state != complete && state != complete1 && state != noAction ) { chr = src.getCharAt( pos1 ); switch( state ) { case none: if( chr == '$' ) { target.append( '$' ); state = noAction; break; } else if ( chr == '(' ) { // scan for balanced ')' pos0 = pos1+1; state = open; } else if ( chr < '@' ) { state = fail; } else { state = token; } break; case token: // allow also ':' and '|' if( (( chr < '0' && chr != '.' && chr != '%' ) || ( chr > ':' && chr <= '@' )) && chr != '|' && chr != '[' && chr != ']' ) { state = complete; pos1--; // else we do this below. } break; case open: if( chr == ')' ) state = complete1; else if ( chr == '\'' ) state = singleQuote; else if ( chr == '\"' ) state = doubleQuote; // else just continue break; case singleQuote: if( chr == '\'' ) state = open; else if ( chr == '\\' ) state = escapeSingle; // else just continue break; case doubleQuote: if( chr == '\"' ) state = open; else if ( chr == '\\' ) state = escapeDouble; // else just continue break; case escapeSingle: state = singleQuote; break; case escapeDouble: state = escapeDouble; break; default: // compiler warning no-op break; } ++pos1; } // parse the result in to the target. switch( state ) { case token: case complete: case complete1: { uint32 pos2 = pos1; if( state == complete1 ) { pos2--; } // todo: record this while scanning uint32 posColon = src.find( ":", pos0, pos2 ); uint32 posPipe = src.find( "|", pos0, pos2 ); uint32 posEnd; Item itm; if( posColon != String::npos ) { posEnd = posColon; } else if( posPipe != String::npos ) { posEnd = posPipe; } else { posEnd = pos2; } if ( ! findLocalVariable( src.subString( pos0, posEnd ), itm ) ) { return return_error_parse; } String temp; // do we have a format? if( posColon != String::npos ) { Format fmt( src.subString( posColon+1, pos2 ) ); if( ! fmt.isValid() ) { return return_error_parse_fmt; } if( ! fmt.format( this, *itm.dereference(), temp ) ) { return return_error_parse_fmt; } } // do we have a toString parameter? else if( posPipe != String::npos ) { itemToString( temp, &itm, src.subString( posPipe+1, pos2 ) ); } else { // otherwise, add the toString version (todo format) // append to target. itemToString( temp, &itm ); } target.append( temp ); } break; case noAction: break; default: return return_error_string; } pos0 = pos1; pos1 = src.find( "$", pos1 ); } // add the last segment if( pos0 != pos1 ) { target.append( src.subString( pos0, pos1 ) ); } return return_ok; } void VMachine::referenceItem( Item &target, Item &source ) { if( source.isReference() ) { target.setReference( source.asReference() ); } else { GarbageItem *itm = new GarbageItem( source ); source.setReference( itm ); target.setReference( itm ); } } static bool vm_func_eval( VMachine *vm ) { CoreArray *arr = vm->local( 0 )->asArray(); uint32 count = (uint32) vm->local( 1 )->asInteger(); // interrupt functional sequence request? if ( vm->regA().isOob() && vm->regA().isInteger() ) { int64 val = vm->regA().asInteger(); if ( val == 1 || val == 0 ) return false; } // let's push other function's return value if ( vm->regA().isLBind() ) { if ( vm->regA().isFutureBind() ) { vm->regBind().flagsOn( 0xF0 ); } else { String *binding = vm->regA().asLBind(); Item *bind = vm->getBinding( *binding ); if ( bind == 0 ) { vm->regA().setReference( new GarbageItem( Item() ) ); vm->setBinding( *binding, vm->regA() ); } else { //fassert( bind->isReference() ); vm->regA() = *bind; } } } vm->pushParam( vm->regA() ); // fake a call return while ( count < arr->length() ) { *vm->local( 1 ) = (int64) count+1; if ( vm->functionalEval( arr->at(count) ) ) { return true; } vm->pushParam( vm->regA() ); ++count; } // done? -- have we to perform a last reduction call? if( count > 0 && vm->local( 2 )->isCallable() ) { vm->returnHandler(0); vm->callFrame( *vm->local( 2 ), count - 1 ); return true; } // if the first element is not callable, generate an array CoreArray *array = new CoreArray( count ); Item *data = array->items().elements(); int32 base = vm->stack().length() - count; memcpy( data, &vm->stack()[base],array->items().esize( count ) ); array->length( count ); vm->regA() = array; vm->stack().resize( base ); return false; } bool VMachine::functionalEval( const Item &itm, uint32 paramCount, bool retArray ) { // An array switch( itm.type() ) { case FLC_ITEM_ARRAY: { CoreArray *arr = itm.asArray(); // prepare for parametric evaluation for( uint32 pi = 1; pi <= paramCount; ++pi ) { String s; s.writeNumber( (int64) pi ); arr->setProperty(s, stack()[ stack().length() - pi] ); } createFrame(0); if ( regBind().isNil() ) regBind() = arr->makeBindings(); // great. Then recursively evaluate the parameters. uint32 count = arr->length(); if ( count > 0 ) { // if the first element is an ETA function, just call it as frame and return. if ( (*arr)[0].isFunction() && (*arr)[0].asFunction()->symbol()->isEta() ) { callFrame( arr, 0 ); return true; } // create two locals; we may need it addLocals( 2 ); // time to install our handleres returnHandler( vm_func_eval ); *local(0) = itm; *local(1) = (int64)0; for ( uint32 l = 0; l < count; l ++ ) { const Item &citem = (*arr)[l]; *local(1) = (int64)l+1; if ( functionalEval( citem ) ) { return true; } if ( regA().isFutureBind() ) { // with this marker, the next call operation will search its parameters. // Let's consider this a temporary (but legitimate) hack. regBind().flags( 0xF0 ); } pushParam( regA() ); } // we got nowere to go returnHandler( 0 ); // is there anything to call? -- is the first element an atom? // local 2 is the first element we have pushed if( local(2)->isCallable() ) { callFrame( *local(2), count-1 ); return true; } } // if the first element is not callable, generate an array if( retArray ) { CoreArray *array = new CoreArray( count ); Item *data = array->items().elements(); int32 base = stack().length() - count; memcpy( data, &stack()[base], array->items().esize( count ) ); array->length( count ); regA() = array; } else { regA().setNil(); } callReturn(); } break; case FLC_ITEM_LBIND: if ( ! itm.isFutureBind() ) { if ( regBind().isDict() ) { Item *bind = getBinding( *itm.asLBind() ); if ( bind == 0 ) { regA().setReference( new GarbageItem( Item() ) ); setBinding( *itm.asLBind(), regA() ); } else { //fassert( bind->isReference() ); regA() = *bind; } } else regA().setNil(); break; } // fallback default: regA() = itm; } return false; } Item *VMachine::findGlobalItem( const String &name ) const { const SymModule *sm = findGlobalSymbol( name ); if ( sm == 0 ) return 0; return sm->item()->dereference(); } LiveModule *VMachine::findModule( const String &name ) { LiveModule **lm =(LiveModule **) m_liveModules.find( &name ); if ( lm != 0 ) return *lm; return 0; } Item *VMachine::findWKI( const String &name ) const { const SymModule *sm = (SymModule *) m_wellKnownSyms.find( &name ); if ( sm == 0 ) return 0; return &sm->liveModule()->wkitems()[ sm->wkiid() ]; } bool VMachine::unlink( const Runtime *rt ) { for( uint32 iter = 0; iter < rt->moduleVector()->size(); ++iter ) { if (! unlink( rt->moduleVector()->moduleAt( iter ) ) ) return false; } return true; } bool VMachine::unlink( const Module *module ) { MapIterator iter; if ( !m_liveModules.find( &module->name(), iter ) ) return false; // get the thing LiveModule *lm = *(LiveModule **) iter.currentValue(); // ensure this module is not the active one if ( currentLiveModule() == lm ) { return false; } // delete all the exported and well known symbols MapIterator stiter = lm->module()->symbolTable().map().begin(); while( stiter.hasCurrent() ) { Symbol *sym = *(Symbol **) stiter.currentValue(); if ( sym->isWKS() ) m_wellKnownSyms.erase( &sym->name() ); else if ( sym->exported() ) m_globalSyms.erase( &sym->name() ); stiter.next(); } // delete the iterator from the map m_liveModules.erase( iter ); //detach the object, so it becomes an invalid callable reference lm->detachModule(); // delete the key, which will detach the module, if found. return true; } bool VMachine::interrupted( bool raise, bool reset, bool dontCheck ) { if( dontCheck || m_systemData.interrupted() ) { if( reset ) m_systemData.resetInterrupt(); if ( raise ) { uint32 line = currentSymbol()->isFunction() ? currentModule()->getLineAt( currentSymbol()->getFuncDef()->basePC() + programCounter() ) : 0; throw new InterruptedError( ErrorParam( e_interrupted ).origin( e_orig_vm ). symbol( currentSymbol()->name() ). module( currentModule()->name() ). line( line ) ); } return true; } return false; } Item *VMachine::getBinding( const String &bind ) const { if ( ! regBind().isDict() ) return 0; return regBind().asDict()->find( bind ); } Item *VMachine::getSafeBinding( const String &bind ) { if ( ! regBind().isDict() ) return 0; Item *found = regBind().asDict()->find( bind ); if ( found == 0 ) { regBind().asDict()->put( new CoreString( bind ), Item() ); found = regBind().asDict()->find( bind ); found->setReference( new GarbageItem( Item() ) ); } return found; } bool VMachine::setBinding( const String &bind, const Item &value ) { if ( ! regBind().isDict() ) return false; regBind().asDict()->put( new CoreString(bind), value ); return true; } CoreSlot* VMachine::getSlot( const String& slotName, bool create ) { m_slot_mtx.lock(); MapIterator iter; if ( ! m_slots.find( &slotName, iter ) ) { if ( create ) { CoreSlot* cs = new CoreSlot( slotName ); m_slots.insert( &slotName, cs ); m_slot_mtx.unlock(); return cs; } m_slot_mtx.unlock(); return 0; } // get the thing CoreSlot *cs = *(CoreSlot **) iter.currentValue(); m_slot_mtx.unlock(); return cs; } void VMachine::removeSlot( const String& slotName ) { MapIterator iter; m_slot_mtx.lock(); if ( m_slots.find( &slotName, iter ) ) { m_slots.erase( iter ); // erase will decrefc, because of item traits in m_slots } m_slot_mtx.unlock(); } void VMachine::markSlots( uint32 mark ) { MapIterator iter = m_slots.begin(); m_slot_mtx.lock(); while( iter.hasCurrent() ) { (*(CoreSlot**) iter.currentValue() )->gcMark( mark ); iter.next(); } m_slot_mtx.unlock(); } bool VMachine::consumeSignal() { StackFrame* frame = currentFrame(); while( frame != 0 ) { if( frame->m_endFrameFunc == coreslot_broadcast_internal ) { frame->m_endFrameFunc = 0; const Item& msgItem = frame->localItem(4); // local(4) if( msgItem.isInteger() ) { VMMessage* msg = (VMMessage*) msgItem.asInteger(); msg->onMsgComplete( true ); delete msg; } return true; } frame = frame->prev(); } return false; } void VMachine::gcEnable( bool mode ) { m_bGcEnabled = mode; } bool VMachine::isGcEnabled() const { return m_bGcEnabled; } VMContext* VMachine::coPrepare( int32 pSize ) { // create a new context VMContext *ctx = new VMContext( *m_currentContext ); // if there are some parameters... if ( pSize > 0 ) { // avoid reallocation afterwards. ctx->stack().reserve( pSize ); // copy flat for( int32 i = 0; i < pSize; i++ ) { ctx->stack().append( stack()[ stack().length() - pSize + i ] ); } stack().resize( stack().length() - pSize ); } // rotate the context m_contexts.pushBack( ctx ); return ctx; } bool VMachine::callCoroFrame( const Item &callable, int32 pSize ) { if ( ! callable.isCallable() ) return false; // rotate the context putAtSleep( m_currentContext ); m_currentContext = coPrepare( pSize ); // fake the frame as a pure return value; this will force this coroutine to terminate // without peeking any code in the module. m_currentContext->pc_next() = i_pc_call_external_return; callFrame( callable, pSize ); return true; } //===================================================================================== // messages // void VMachine::postMessage( VMMessage *msg ) { // ok, we can't do it now; post the message m_mtx_mesasges.lock(); if ( m_msg_head == 0 ) { m_msg_head = msg; } else { m_msg_tail->append( msg ); } // reach the end of the msg list and set the new tail while( msg->next() != 0 ) msg = msg->next(); m_msg_tail = msg; // also, ask for early checks. // We're really not concerned about spurious reads here or in the other thread, // everything would be ok even without this operation. It's just ok if some of // the two threads sync on this asap. m_opNextCheck = m_opCount; m_mtx_mesasges.unlock(); // can we process the message now? if( m_baton.tryAcquire() ) { // this doesn't actually process data, // it just prepares coroutines to process them. -- so it can't throw. processPendingMessages(); m_baton.release(); // wake up the vm, if it was sleeping. m_systemData.interrupt(); } } void VMachine::processMessage( VMMessage *msg ) { // find the slot CoreSlot* slot = getSlot( msg->name(), false ); if ( slot == 0 || slot->empty() ) { msg->onMsgComplete( false ); delete msg; return; } // create the coroutine, whose first operation will be // to call our external return frame. VMContext* sleepCtx = coPrepare(0); // force the sleeping context to call the return frame immediately sleepCtx->pc_next() = i_pc_call_external_return; sleepCtx->pc() = i_pc_call_external_return; // prepare the broadcast in the frame. slot->prepareBroadcast( sleepCtx, 0, 0, msg ); // force immediate context rotation /* putAtSleep( m_currentContext ); m_currentContext = sleepCtx;*/ putAtSleep( sleepCtx ); } void VMachine::performGC( bool bWaitForCollect ) { m_bWaitForCollect = bWaitForCollect; memPool->idleVM( this, true ); m_eGCPerformed.wait(); } void VMachine::prepareFrame( CoreFunc* target, uint32 paramCount ) { // eventually check for named parameters if ( this->regBind().flags() == 0xF0 ) { const SymbolTable *symtab; if( target->symbol()->isFunction() ) symtab = &target->symbol()->getFuncDef()->symtab(); else symtab = target->symbol()->getExtFuncDef()->parameters(); this->regBind().flags(0); // We know we have (probably) a named parameter. uint32 size = this->stack().length(); uint32 paramBase = size - paramCount; ItemArray iv(8); uint32 pid = 0; // first step; identify future binds and pack parameters. while( paramBase+pid < size ) { Item &item = this->stack()[ paramBase+pid ]; if ( item.isFutureBind() ) { // we must move the parameter into the right position iv.append( item ); for( uint32 pos = paramBase + pid + 1; pos < size; pos ++ ) { this->stack()[ pos - 1 ] = this->stack()[ pos ]; } this->stack()[ size-1 ].setNil(); size--; paramCount--; } else pid++; } this->stack().resize( size ); // second step: apply future binds. for( uint32 i = 0; i < iv.length(); i ++ ) { Item &item = iv[i]; // try to find the parameter const String *pname = item.asLBind(); Symbol *param = symtab == 0 ? 0 : symtab->findByName( *pname ); if ( param == 0 || ! param->isParam( ) ) { throw new CodeError( ErrorParam( e_undef_param, __LINE__ ).extra(*pname) ); } // place it in the stack; if the stack is not big enough, resize it. if ( this->stack().length() <= param->itemId() + paramBase ) { paramCount = param->itemId()+1; this->stack().resize( paramCount + paramBase ); } this->stack()[ param->itemId() + paramBase ] = item.asFBind()->origin(); } } // ensure against optional parameters. if( target->symbol()->isFunction() ) { FuncDef *tg_def = target->symbol()->getFuncDef(); if( paramCount < tg_def->params() ) { this->stack().resize( this->stack().length() + tg_def->params() - paramCount ); paramCount = tg_def->params(); } this->createFrame( paramCount ); // space for locals if ( tg_def->locals() > 0 ) { this->currentFrame()->resizeStack( tg_def->locals() ); // are part of this locals closed? if( target->closure() != 0 ) { fassert( target->closure()->length() <= tg_def->locals() ); this->stack().copyOnto( 0, *target->closure() ); } } this->m_currentContext->lmodule( target->liveModule() ); this->m_currentContext->symbol( target->symbol() ); //jump this->m_currentContext->pc_next() = 0; } else { this->createFrame( paramCount ); // so we can have adequate tracebacks. this->m_currentContext->lmodule( target->liveModule() ); this->m_currentContext->symbol( target->symbol() ); this->m_currentContext->pc_next() = VMachine::i_pc_call_external; } } void VMachine::prepareFrame( CoreArray* arr, uint32 paramCount ) { fassert( arr->length() > 0 && arr->at(0).isCallable() ); Item& carr = (*arr)[0]; uint32 arraySize = arr->length(); uint32 sizeNow = this->stack().length(); CoreDict* bindings = arr->bindings(); bool hasFuture = false; // move parameters beyond array parameters arraySize -- ; // first element is the callable item. if ( arraySize > 0 ) { // first array element is the called item. this->stack().resize( sizeNow + arraySize ); sizeNow -= paramCount; for ( uint32 j = sizeNow + paramCount; j > sizeNow; j -- ) { this->stack()[ j-1 + arraySize ] = this->stack()[ j-1 ]; } // push array paramers for ( uint32 i = 0; i < arraySize; i ++ ) { Item &itm = (*arr)[i + 1]; if( itm.isLBind() ) { if ( itm.asFBind() == 0 ) { if ( this->regBind().isNil() && bindings == 0 ) { // we must create bindings for this array. bindings = arr->makeBindings(); } if ( bindings != 0 ) { // have we got this binding? Item *bound = bindings->find( *itm.asLBind() ); if ( ! bound ) { arr->setProperty( *itm.asLBind(), Item() ); bound = bindings->find( *itm.asLBind() ); } this->stack()[ i + sizeNow ] = *bound; } else { // fall back to currently provided bindings this->stack()[ i + sizeNow ] = *this->getSafeBinding( *itm.asLBind() ); } } else { // treat as a future binding hasFuture = true; this->stack()[ i + sizeNow ] = itm; } } else { // just transfer the parameters this->stack()[ i + sizeNow ] = itm; } } } // inform the called about future state if( hasFuture ) this->regBind().flagsOn( 0xF0 ); carr.readyFrame( this, arraySize + paramCount ); // change the bindings now, before the VM runs this frame. if ( this->regBind().isNil() && arr->bindings() != 0 ) { this->regBind() = arr->bindings(); } } uint32 VMachine::generation() const { return m_generation; } void VMachine::setupScript( int argc, char** argv ) { if ( m_mainModule != 0 ) { Item *scriptName = findGlobalItem( "scriptName" ); if ( scriptName != 0 ) *scriptName = new CoreString( m_mainModule->module()->name() ); Item *scriptPath = findGlobalItem( "scriptPath" ); if ( scriptPath != 0 ) *scriptPath = new Falcon::CoreString( m_mainModule->module()->name() ); } Falcon::Item *args = findGlobalItem( "args" ); if ( args != 0 ) { // create the arguments. // It is correct to pass an empty array if we haven't any argument to pass. Falcon::CoreArray *argsArray = new Falcon::CoreArray; for( int i = 0; i < argc; i ++ ) { argsArray->append( new Falcon::CoreString( argv[i] ) ); } *args = argsArray; } } void VMachine::onIdleTime( numeric seconds ) { } bool VMachine::replaceMe_onIdleTime( numeric seconds ) { if ( seconds < 0.0 ) { throw new CodeError( ErrorParam( e_deadlock ).origin( e_orig_vm ). symbol( currentSymbol()->name() ). module( currentModule()->name() ) ); } idle(); bool complete = m_systemData.sleep( seconds ); unidle(); if ( ! complete ) { m_systemData.resetInterrupt(); return true; } return false; } void VMachine::handleRaisedItem( Item& value ) { Error* err = 0; if ( value.isObject() && value.isOfClass( "Error" ) ) { err = static_cast(value.asObjectSafe())->getError(); if( ! err->hasTraceback() ) fillErrorContext(err, true); } // can someone get it? if( currentContext()->tryFrame() == 0 ) // uncaught error raised from scripts... { // create the error that the external application will see. if ( err != 0 ) { // in case of an error of class Error, we have already a good error inside of it. err->incref(); } else { // else incapsulate the item in an error. err = new GenericError( ErrorParam( e_uncaught ).origin( e_orig_vm ) ); err->raised( value ); } throw err; } // Enter the stack frame that should handle the error (or raise to the top if uncaught) while( currentFrame()->m_try_base == i_noTryFrame ) { // neutralize post-processors // currentFrame()->m_endFrameFunc = 0; -- done by currentContext()->callReturn(); m_break = currentContext()->callReturn(); // let the VM deal with returns if ( m_break ) { m_break = false; throw value; } } regB() = value; // We are in the frame that should handle the error, in one way or another // should we catch it? popTry( true ); } void VMachine::handleRaisedError( Error* err ) { if( ! err->hasTraceback() ) fillErrorContext( err, true ); // catch it if possible if( err->catchable() && currentContext()->tryFrame() != 0 ) { // Enter the stack frame that should handle the error (or raise to the top if uncaught) while( currentFrame() != currentContext()->tryFrame() ) { // neutralize post-processors // currentFrame()->m_endFrameFunc = 0; -- done by currentFrame()->callReturn(); m_break = currentContext()->callReturn(); // let the VM deal with returns if ( m_break ) { m_break = false; throw err; } } CoreObject *obj = err->scriptize( this ); if ( obj != 0 ) { err->decref(); regB() = obj; // We are in the frame that should handle the error, in one way or another // should we catch it? popTry( true ); } else { // Panic. Should not happen -- scriptize has raised a symbol not found error // describing the missing error class; we must tell the user so that the module // not declaring the correct error class, or failing to export it, can be // fixed. fassert( false ); throw err; } } // we couldn't catch the error (this also means we're at stackBase() zero) // we should handle it then exit else { // we should manage the error; if we're here, stackBase() is zero, // so we are the last in charge throw err; } } void VMachine::periodicChecks() { // pulse VM idle if( m_bGcEnabled ) m_baton.checkBlock(); if ( m_opLimit > 0 ) { // Bail out??? if ( m_opCount > m_opLimit ) return; else if ( m_opNextCheck > m_opLimit ) m_opNextCheck = m_opLimit; } if( ! m_currentContext->atomicMode() ) { if( m_allowYield && ! m_sleepingContexts.empty() && m_opCount > m_opNextContext ) { rotateContext(); m_opNextContext = m_opCount + m_loopsContext; if( m_opNextContext < m_opNextCheck ) m_opNextCheck = m_opNextContext; } // Periodic Callback if( m_loopsCallback > 0 && m_opCount > m_opNextCallback ) { periodicCallback(); m_opNextCallback = m_opCount + m_loopsCallback; if( m_opNextCallback < m_opNextCheck ) m_opNextCheck = m_opNextCallback; } // in case of single step: if( m_bSingleStep ) { // stop also next op m_opNextCheck = m_opCount + 1; return; // maintain the event we have, but exit now. } // perform messages processPendingMessages(); } } void VMachine::processPendingMessages() { m_mtx_mesasges.lock(); while( m_msg_head != 0 ) { VMMessage* msg = m_msg_head; m_msg_head = msg->next(); if( m_msg_head == 0 ) m_msg_tail = 0; // it is ok if m_msg_tail is left dangling. m_mtx_mesasges.unlock(); processMessage( msg ); // do not delete msg m_mtx_mesasges.lock(); } m_mtx_mesasges.unlock(); } void VMachine::raiseHardError( int code, const String &expl, int32 line ) { Error *err = new CodeError( ErrorParam( code, line ).origin( e_orig_vm ) .hard() .extra( expl ) ); // of course, if we're not executing, there nothing to raise if( currentSymbol() != 0 ) { fillErrorContext( err ); } throw err; } void VMachine::launch( const String &startSym, uint32 paramCount ) { Item* lItem = 0; if( m_mainModule != 0 ) { lItem = m_mainModule->findModuleItem( startSym ); } if ( lItem == 0 ) { lItem = findGlobalItem( startSym ); if( lItem == 0 ) { throw new CodeError( ErrorParam( e_undef_sym, __LINE__ ).origin( e_orig_vm ).extra( startSym ). symbol( "launch" ). module( "core.vm" ) ); } } /** \todo allow to call classes at startup. Something like "all-classes" a-la-java */ if ( ! lItem->isCallable() ) { throw new CodeError( ErrorParam( e_non_callable, __LINE__ ).origin( e_orig_vm ). extra( startSym ). symbol( "launch" ). module( "core.vm" ) ); } // be sure to pass a clean env. try { reset(); callItem( *lItem, paramCount ); } catch( VMEventQuit& ) { } } void VMachine::bindItem( const String& name, const Item &tgt ) { if ( ! regBind().isDict() ) { regBind() = new CoreDict(new LinearDict() ); } CoreDict* cd = regBind().asDict(); cd->put( Item( new CoreString( name ) ), tgt ); } void VMachine::unbindItem( const String& name, Item &tgt ) const { fassert( name.size() > 0 ); if ( name[0] == '.' ) { tgt.setLBind( new CoreString( name, 1 ) ); } else { if ( regBind().isDict() ) { if( regBind().asDict()->find( Item( const_cast(&name) ), tgt ) ) return; } // create the lbind. tgt.setLBind( new CoreString( name ) ); } } void VMachine::expandTRAV( uint32 count, Iterator& iter ) { // we work a bit differently for dictionaries and normal sequences. if ( iter.sequence()->isDictionary() ) { if( count == 1 ) { // if we have one variable, we must create a pair holding key and value CoreArray* ca = new CoreArray(2); ca->items()[0] = iter.getCurrentKey(); ca->items()[1] = iter.getCurrent(); ca->length(2); *getNextTravVar() = ca; } else if ( count != 2 ) { throw new AccessError( ErrorParam( e_unpack_size ).origin( e_orig_vm ).extra( "TR*" ) ); } else { // we have two vars to decode Item* k = getNextTravVar(); Item* v = getNextTravVar(); *k = iter.getCurrentKey(); *v = iter.getCurrent(); } } else { // for all the other cases, when we have 1 variable we must just set it inside... if( count == 1 ) { *getNextTravVar() = iter.getCurrent(); } else { // otherwise, we must match the number of variables with the count of sub-variables. const Item& current = iter.getCurrent(); if( ! current.isArray() || current.asArray()->length() != count ) { throw new AccessError( ErrorParam( e_unpack_size ).origin( e_orig_vm ).extra( "TR*" ) ); } CoreArray* source = current.asArray(); for( uint32 p = 0; p < count; p++ ) { *getNextTravVar() = source->items()[p]; } } } } Item* VMachine::getNextTravVar() { fassert( m_currentContext->pc_next() % 4 == 0 ); byte* code = m_currentContext->code(); fassert( code[ m_currentContext->pc_next() ] == P_NOP ); int32 type = int( code[ m_currentContext->pc_next()+1 ] ); m_currentContext->pc_next()+=sizeof( int32 ); int32 id = *reinterpret_cast< int32 * >( code + m_currentContext->pc_next() ); m_currentContext->pc_next()+=sizeof( int32 ); switch( type ) { case P_PARAM_GLOBID: return &moduleItem( id ); case P_PARAM_LOCID: return local( id ); case P_PARAM_PARID: return param( id ); } // shall never be here. fassert( false ); return 0; } //===================================================================================== // baton // void VMBaton::release() { Baton::release(); // See if the memPool has anything interesting for us. memPool->idleVM( m_owner ); } void VMBaton::releaseNotIdle() { Baton::release(); } void VMBaton::onBlockedAcquire() { // See if the memPool has anything interesting for us. memPool->idleVM( m_owner ); } } /* end of vm.cpp */ engine/vm_run.cpp000066400000000000000000001470311176363201700143510ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vm_run.cpp Implementation of virtual machine - main loop ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2004-09-08 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Virtual machine main loop. The main loop of the virtual machine deserves a space on its own. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Falcon { Item *VMachine::getOpcodeParam( register uint32 bc_pos ) { Item *ret; // Load the operator ? switch( m_currentContext->code()[ m_currentContext->pc() + bc_pos ] ) { case P_PARAM_INT32: m_imm[bc_pos].setInteger( *reinterpret_cast( m_currentContext->code() + m_currentContext->pc_next() ) ); m_currentContext->pc_next() += sizeof( int32 ); return m_imm + bc_pos; case P_PARAM_INT64: m_imm[bc_pos].setInteger( loadInt64( m_currentContext->code() + m_currentContext->pc_next() ) ); m_currentContext->pc_next() += sizeof( int64 ); return m_imm + bc_pos; case P_PARAM_STRID: { String *temp = currentLiveModule()->getString( *reinterpret_cast( m_currentContext->code() + m_currentContext->pc_next() ) ); //m_imm[bc_pos].setString( temp, const_cast(currentLiveModule()) ); m_imm[bc_pos].setString( temp ); m_currentContext->pc_next() += sizeof( int32 ); } return m_imm + bc_pos; case P_PARAM_LBIND: m_imm[bc_pos].setLBind( currentLiveModule()->getString( *reinterpret_cast( m_currentContext->code() + m_currentContext->pc_next() ) ) ); m_currentContext->pc_next() += sizeof( int32 ); return m_imm + bc_pos; case P_PARAM_NUM: m_imm[bc_pos].setNumeric( loadNum( m_currentContext->code() + m_currentContext->pc_next() ) ); m_currentContext->pc_next() += sizeof( numeric ); return m_imm + bc_pos; case P_PARAM_NIL: m_imm[bc_pos].setNil(); return m_imm + bc_pos; case P_PARAM_UNB: m_imm[bc_pos].setUnbound(); return m_imm + bc_pos; case P_PARAM_GLOBID: { register int32 id = *reinterpret_cast< int32 * >( m_currentContext->code() + m_currentContext->pc_next() ); m_currentContext->pc_next()+=sizeof( int32 ); return &moduleItem( id ); } break; case P_PARAM_LOCID: ret = local( *reinterpret_cast< int32 * >( m_currentContext->code() + m_currentContext->pc_next() ) ); m_currentContext->pc_next()+=sizeof(int32); return ret; case P_PARAM_PARID: ret = param( *reinterpret_cast< int32 * >( m_currentContext->code() + m_currentContext->pc_next() ) ); m_currentContext->pc_next()+=sizeof(int32); return ret; case P_PARAM_TRUE: m_imm[bc_pos].setBoolean( true ); return m_imm + bc_pos; case P_PARAM_FALSE: m_imm[bc_pos].setBoolean( false ); return m_imm + bc_pos; case P_PARAM_NTD32: m_currentContext->pc_next() += sizeof(int32); return 0; case P_PARAM_NTD64: m_currentContext->pc_next() += sizeof(int64); return 0; case P_PARAM_REGA: return ®A(); case P_PARAM_REGB: return ®B(); case P_PARAM_REGS1: return &self(); case P_PARAM_REGL1: return &latch(); case P_PARAM_REGL2: return &latcher(); case P_PARAM_FSELF: return &m_currentContext->fself(); } // we should not be here. fassert( false ); return 0; } void VMachine::run() { tOpcodeHandler *ops = m_opHandlers; // declare this as the running machine setCurrent(); while( ! m_break ) { try { // move m_currentContext->pc_next() to the end of instruction and beginning of parameters. // in case of opcode in the call_request range, this will move next pc to return. m_currentContext->pc_next() = m_currentContext->pc() + sizeof( uint32 ); // external call required? if ( m_currentContext->pc() >= i_pc_call_request ) { switch( m_currentContext->pc() ) { case i_pc_call_external_ctor_return: regA() = self(); //fallthrough case i_pc_call_external_return: callReturn(); break; // the request was just to ignore opcode case i_pc_redo_request: if ( m_opCount ) m_opCount--; // prevent hitting oplimit break; default: regA().setNil(); currentSymbol()->getExtFuncDef()->call( this ); } } else { ops[ m_currentContext->code()[ m_currentContext->pc() ] ]( this ); } m_opCount ++; //========================= // Executes periodic checks // if ( m_opCount >= m_opNextCheck ) { // By default, if nothing else happens, we should do a check no sooner than this. m_opNextCheck += FALCON_VM_DFAULT_CHECK_LOOPS; // manage periodic callbacks periodicChecks(); } // Jump to next isntruction. m_currentContext->pc() = m_currentContext->pc_next(); } // catches explicitly raised items. catch( Item& raised ) { handleRaisedItem( raised ); } // errors thrown by C extensions; // they may get encapsulated in Error() instances and handled by the script // via handleRaised catch( Error *err ) { handleRaisedError( err ); } } // end while -- VM LOOP m_break = false; } /**************************************************** Op code handlers *****************************************************/ // 0 void opcodeHandler_END( register VMachine *vm ) { vm->regA().setNil(); vm->terminateCurrentContext(); } // 1 void opcodeHandler_NOP( register VMachine *vm ) { } // 2 void opcodeHandler_PSHN( register VMachine *vm ) { vm->stack().append( Item() ); } // 3 void opcodeHandler_RET( register VMachine *vm ) { vm->retnil(); vm->callReturn(); } // 4 void opcodeHandler_RETA( register VMachine *vm ) { vm->callReturn(); //? Check this -- I don't think is anymore necessary if( vm->regA().type() == FLC_ITEM_REFERENCE ) vm->regA().copy( vm->regA().asReference()->origin() ); } // 5 void opcodeHandler_PTRY( register VMachine *vm ) { register int32 target = vm->getNextNTD32(); while( target > 0 ) { vm->popTry( false ); --target; } } // 6 void opcodeHandler_LNIL( register VMachine *vm ) { register Item *op1 = vm->getOpcodeParam( 1 )->dereference(); op1->setNil(); } // 7 void opcodeHandler_RETV(register VMachine *vm) { vm->regA() = *vm->getOpcodeParam( 1 )->dereference(); vm->callReturn(); } // 8 void opcodeHandler_BOOL( register VMachine *vm ) { vm->regA().setBoolean( vm->getOpcodeParam( 1 )->dereference()->isTrue() ); } // 9 void opcodeHandler_JMP( register VMachine *vm ) { vm->m_currentContext->pc_next() = vm->getNextNTD32(); } // 0A void opcodeHandler_GENA( register VMachine *vm ) { register uint32 size = (uint32) vm->getNextNTD32(); CoreArray *array = new CoreArray( size ); vm->regA().setArray( array ); // copy the m-topmost items in the stack into the array if( size > 0 ) { array->items().copyOnto( vm->stack(), vm->stack().length() - size, size ); vm->currentFrame()->pop( size ); } } // 0B void opcodeHandler_GEND( register VMachine *vm ) { register uint32 length = (uint32) vm->getNextNTD32(); LinearDict *dict = new LinearDict( length ); // copy the m-topmost items in the stack into the array uint32 len = vm->stack().length(); uint32 base = len - ( length * 2 ); for ( uint32 i = base ; i < len; i += 2 ) { // insert may modify the stack (if using special "compare" functions) Item i1 = vm->stackItem(i); Item i2 = vm->stackItem(i+1); dict->put( i1, i2 ); fassert( vm->stack().length() == len ); } vm->stack().resize( base ); vm->regA().setDict( new CoreDict(dict) ); } // 0C void opcodeHandler_PUSH( register VMachine *vm ) { /** \TODO Raise a stack overflow error on VM stack boundary limit. */ Item *data = vm->getOpcodeParam( 1 )->dereference(); if ( data->isFutureBind() ) { // with this marker, the next call operation will search its parameters. // Let's consider this a temporary (but legitimate) hack. vm->regBind().flags( 0xF0 ); } vm->stack().append( *data ); } // 0D void opcodeHandler_PSHR( register VMachine *vm ) { Item *referenced = vm->getOpcodeParam( 1 ); if ( ! referenced->isReference() ) { GarbageItem *ref = new GarbageItem( *referenced ); referenced->setReference( ref ); } vm->stack().append( *referenced ); } // 0E void opcodeHandler_POP( register VMachine *vm ) { if ( vm->stack().length() == 0 ) { vm->raiseHardError( e_stackuf, "POP", __LINE__ ); return; } // --- WARNING: do not dereference! vm->getOpcodeParam( 1 )->copy( vm->stack().back() ); vm->stack().resize( vm->stack().length() - 1); } // 0F void opcodeHandler_INC( register VMachine *vm ) { Item *operand = vm->getOpcodeParam( 1 ); operand->inc(vm->regA()); } // 10 void opcodeHandler_DEC( register VMachine *vm ) { Item *operand = vm->getOpcodeParam( 1 ); operand->dec(vm->regA()); } // 11 void opcodeHandler_NEG( register VMachine *vm ) { Item *operand = vm->getOpcodeParam( 1 ); operand->neg( vm->regA() ); } // 12 void opcodeHandler_NOT( register VMachine *vm ) { vm->regA().setInteger( vm->getOpcodeParam( 1 )->dereference()->isTrue() ? 0 : 1 ); } //13 void opcodeHandler_TRAL( register VMachine *vm ) { if ( vm->stack().length() < 1 ) { vm->raiseHardError( e_stackuf, "TRAL", __LINE__ ); } uint32 loop_last = vm->getNextNTD32(); const Item &top = vm->stack().back(); fassert( top.isGCPointer() ); Iterator* iter = dyncast(top.asGCPointer()); // is this the last element? if ( ! iter->hasNext() ) { if( iter->hasCurrent() ) iter->next(); // position past last element vm->jump( loop_last ); } } //14 void opcodeHandler_IPOP( register VMachine *vm ) { register uint32 amount = (uint32) vm->getNextNTD32(); if ( vm->stack().length() < amount ) { vm->raiseHardError( e_stackuf, "IPOP", __LINE__ ); return; } vm->stack().resize( vm->stack().length() - amount ); } //15 void opcodeHandler_XPOP( register VMachine *vm ) { Item *operand = vm->getOpcodeParam( 1 )->dereference(); // use copy constructor. Item itm( *operand ); operand->copy( vm->stack()[vm->stack().length() - 1] ); vm->stack()[vm->stack().length() - 1].copy( itm ); } //16 void opcodeHandler_GEOR( register VMachine *vm ) { vm->regA().setRange( new CoreRange( (int32) vm->getOpcodeParam( 1 )->dereference()->forceIntegerEx() ) ); } //17 void opcodeHandler_TRY( register VMachine *vm ) { vm->pushTry( vm->getNextNTD32() ); } //18 void opcodeHandler_JTRY( register VMachine *vm ) { register int32 target = vm->getNextNTD32(); vm->popTry( false ); // underflows are checked here vm->m_currentContext->pc_next() = target; } //19 void opcodeHandler_RIS( register VMachine *vm ) { // todo - when this will be in the main loop, // we may use directly handleRaisedEvent() throw *vm->getOpcodeParam( 1 )->dereference(); } //1A void opcodeHandler_BNOT( register VMachine *vm ) { register Item *operand = vm->getOpcodeParam( 1 )->dereference(); if ( operand->type() == FLC_ITEM_INT ) { vm->regA().setInteger( ~operand->asInteger() ); } else throw new TypeError( ErrorParam( e_bitwise_op ).extra("BNOT").origin( e_orig_vm ) ); } //1B void opcodeHandler_NOTS( register VMachine *vm ) { register Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); if ( operand1->isOrdinal() ) operand1->setInteger( ~operand1->forceInteger() ); else throw new TypeError( ErrorParam( e_bitwise_op ).extra("NOTS").origin( e_orig_vm ) ); } //1c void opcodeHandler_PEEK( register VMachine *vm ) { register Item *operand = vm->getOpcodeParam( 1 )->dereference(); if ( vm->stack().length() == 0 ) { vm->raiseHardError( e_stackuf, "PEEK", __LINE__ ); return; } *operand = vm->stack().back(); } // 1D void opcodeHandler_FORK( register VMachine *vm ) { uint32 pSize = (uint32) vm->getNextNTD32(); uint32 pJump = (uint32) vm->getNextNTD32(); // create the coroutine vm->putAtSleep( vm->coPrepare( pSize ) ); // fork vm->m_currentContext->pc_next() = pJump; } // 1D - Missing // 1E void opcodeHandler_LD( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); /* if( operand1->isLBind() && operand1->asLBind()->getCharAt(0) != '.' ) { vm->bindItem( *operand1->asLBind(), *operand2 ); vm->regA() = *operand2; } else { switch( operand2->type() ) { case FLC_ITEM_STRING: operand1->setString( new CoreString( *operand2->asString() ) ); operand1->flags( operand2->flags() ); break; case FLC_ITEM_LBIND: vm->unbindItem( *operand2->asLBind(), *operand1 ); break; default: operand1->copy( *operand2 ); } vm->regA() = *operand1; } */ if ( operand2->isString() ) { operand1->setString( new CoreString( *operand2->asString() ) ); operand1->flags( operand2->flags() ); } else operand1->copy( *operand2 ); vm->regA() = *operand1; } // 1F void opcodeHandler_LDRF( register VMachine *vm ) { // don't dereference Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); if( operand2 == &vm->m_imm[2] ) operand1->setNil(); // breaks the reference else { // we don't use memPool::referenceItem for performace reasons GarbageItem *gitem; if ( operand2->isReference() ) { gitem = operand2->asReference(); } else { gitem = new GarbageItem( *operand2 ); operand2->setReference( gitem ); } operand1->setReference( gitem ); } } // 20 void opcodeHandler_ADD( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); Item target; // neutralize auto-ops operand1->add( *operand2, target ); vm->regA() = target; } // 21 void opcodeHandler_SUB( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); Item target; // neutralize auto-ops operand1->sub( *operand2, target ); vm->regA() = target; } // 22 void opcodeHandler_MUL( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); operand1->mul( *operand2, vm->regA() ); } // 23 void opcodeHandler_DIV( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); operand1->div( *operand2, vm->regA() ); } //24 void opcodeHandler_MOD( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); operand1->mod( *operand2, vm->regA() ); } // 25 void opcodeHandler_POW( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); operand1->pow( *operand2, vm->regA() ); } // 26 void opcodeHandler_ADDS( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 ); // TODO: S-operators operand1->add( *operand2, *operand1 ); vm->regA() = *operand1; } //27 void opcodeHandler_SUBS( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 ); // TODO: S-operators operand1->sub( *operand2, *operand1 ); vm->regA() = *operand1; } //28 void opcodeHandler_MULS( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 ); // TODO: S-operators operand1->mul( *operand2, *operand1 ); vm->regA() = *operand1; } //29 void opcodeHandler_DIVS( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 ); // TODO: S-operators operand1->div( *operand2, *operand1 ); vm->regA() = *operand1; } //2A void opcodeHandler_MODS( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 ); // TODO: S-operators operand1->mod( *operand2, *operand1 ); vm->regA() = *operand1; } //2B void opcodeHandler_BAND( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); if ( operand1->isOrdinal() && operand2->isOrdinal() ) { vm->regA().setInteger( operand1->forceInteger() & operand2->forceInteger() ); } else throw new TypeError( ErrorParam( e_invop ).extra("BAND").origin( e_orig_vm ) ); } //2C void opcodeHandler_BOR( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); if ( operand1->isOrdinal() && operand2->isOrdinal() ) { vm->regA().setInteger( operand1->forceInteger() | operand2->forceInteger() ); } else throw new TypeError( ErrorParam( e_invop ).extra("BOR").origin( e_orig_vm ) ); } //2D void opcodeHandler_BXOR( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); if ( operand1->isOrdinal() && operand2->isOrdinal() ) { vm->regA().setInteger( operand1->forceInteger() ^ operand2->forceInteger() ); } else throw new TypeError( ErrorParam( e_invop ).extra("BXOR").origin( e_orig_vm ) ); } //2E void opcodeHandler_ANDS( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); if ( operand1->isOrdinal() && operand2->isOrdinal() ) { operand1->setInteger( operand1->forceInteger() & operand2->forceInteger() ); } else throw new TypeError( ErrorParam( e_invop ).extra("ANDS").origin( e_orig_vm ) ); } //2F void opcodeHandler_ORS( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); if ( operand1->type() == FLC_ITEM_INT && operand2->type() == FLC_ITEM_INT ) { operand1->setInteger( operand1->asInteger() | operand2->asInteger() ); } else throw new TypeError( ErrorParam( e_invop ).extra("ORS").origin( e_orig_vm ) ); } //30 void opcodeHandler_XORS( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); if ( operand1->isOrdinal() && operand2->isOrdinal() ) { operand1->setInteger( operand1->forceInteger() ^ operand2->forceInteger() ); } else throw new TypeError( ErrorParam( e_invop ).extra("XORS").origin( e_orig_vm ) ); } //31 void opcodeHandler_GENR( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); Item *operand3 = vm->getOpcodeParam( 3 )->dereference(); int64 secondOp = operand2->forceIntegerEx(); int64 step; if ( operand3->isNil() ) { step = vm->stack()[ vm->stack().length() - 1].forceIntegerEx(); vm->stack().resize( vm->stack().length() - 1); } else { step = operand3->forceIntegerEx(); } int64 firstOp = operand1->forceIntegerEx(); if( operand2->isOob() && firstOp <= secondOp ) secondOp++; vm->regA().setRange( new CoreRange( firstOp, secondOp, step ) ); } //32 void opcodeHandler_EQ( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); vm->regA().setBoolean( *operand1 == *operand2 ); } //33 void opcodeHandler_NEQ( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); vm->regA().setBoolean( *operand1 != *operand2 ); } //34 void opcodeHandler_GT( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); vm->regA().setBoolean( *operand1 > *operand2 ); } //35 void opcodeHandler_GE( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); vm->regA().setBoolean( *operand1 >= *operand2 ); } //36 void opcodeHandler_LT( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); vm->regA().setBoolean( *operand1 < *operand2 ); } //37 void opcodeHandler_LE( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); vm->regA().setBoolean( *operand1 <= *operand2 ); } //38 void opcodeHandler_IFT( register VMachine *vm ) { uint32 pNext = (uint32) vm->getNextNTD32(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); if( operand2->isTrue() ) vm->m_currentContext->pc_next() = pNext; } //39 void opcodeHandler_IFF( register VMachine *vm ) { uint32 pNext = (uint32) vm->getNextNTD32(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); if( ! operand2->isTrue() ) vm->m_currentContext->pc_next() = pNext; } //3A void opcodeHandler_CALL( register VMachine *vm ) { uint32 pNext = (uint32) vm->getNextNTD32(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); operand2->readyFrame( vm, pNext ); } //3B void opcodeHandler_INST( register VMachine *vm ) { uint32 pNext = (uint32) vm->getNextNTD32(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); if( operand2->type() != FLC_ITEM_CLASS ) { throw new TypeError( ErrorParam( e_invop ).extra("INST").origin( e_orig_vm ) ); } Item method = operand2->asClass()->constructor(); if ( ! method.isNil() ) { // set self in the function method. method.methodize( vm->self() ); method.readyFrame( vm, pNext ); } } //3C void opcodeHandler_ONCE( register VMachine *vm ) { uint32 pNext = (uint32) vm->getNextNTD32(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); const Symbol *call = 0; if ( operand2->isFunction() ) { call = operand2->asFunction()->symbol(); } else if ( operand2->isMethod() && operand2->asMethodFunc()->isFunc() ) { call = static_cast(operand2->asMethodFunc())->symbol(); } if ( call != 0 && call->isFunction() ) { // we suppose we're in the same module as the function things we are... register uint32 itemId = call->getFuncDef()->onceItemId(); if ( vm->moduleItem( itemId ).isNil() ) vm->moduleItem( itemId ).setInteger( 1 ); else vm->m_currentContext->pc_next() = pNext; return; } vm->raiseHardError( e_invop, "ONCE", __LINE__ ); } //3D void opcodeHandler_LDV( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); vm->latch() = *operand1; vm->latcher() = *operand2; operand1->getIndex( *operand2, vm->regA() ); } //3E void opcodeHandler_LDP( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); vm->latch() = *operand1; vm->latcher() = *operand2; if( operand2->isString() ) { String *property = operand2->asString(); operand1->getProperty( *property, vm->regA() ); } else throw new AccessError( ErrorParam( e_prop_acc ).origin( e_orig_vm ) . extra( operand2->isString() ? *operand2->asString() : "?" ) ); } // 3F void opcodeHandler_TRAN( register VMachine *vm ) { if ( vm->stack().length() < 1 ) { vm->raiseHardError( e_stackuf, "TRAN", __LINE__ ); } uint32 loop_begin = vm->getNextNTD32(); uint32 varCount = vm->getNextNTD32(); const Item &top = vm->stack().back(); fassert( top.isGCPointer() ); Iterator* iter = dyncast(top.asGCPointer()); if( iter->isValid() && iter->next() ) { // Ok, we proceeded to the next item; prepare the vars vm->expandTRAV( varCount, *iter ); vm->currentContext()->pc_next() = loop_begin; } else { vm->currentContext()->pc_next() += sizeof(uint32) * 2 * varCount; } } //40 void opcodeHandler_LDAS( register VMachine *vm ) { uint32 size = (uint32) vm->getNextNTD32(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); if( operand2->isArray() ) { CoreArray* arr = operand2->asArray(); if( arr->length() != size ) { throw new AccessError( ErrorParam( e_unpack_size ) .extra( "LDAS" ) .origin( e_orig_vm ) ); } else { uint32 oldpos = vm->stack().length(); vm->stack().resize( oldpos + size ); void* mem = &vm->stack()[ oldpos ];; memcpy( mem, arr->items().elements(), size * sizeof(Item) ); } } else { throw new AccessError( ErrorParam( e_invop ) .extra( "LDAS" ) .origin( e_orig_vm ) ); } } //41 void opcodeHandler_SWCH( register VMachine *vm ) { uint32 pNext = (uint32) vm->getNextNTD32(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); uint64 sw_count = (uint64) vm->getNextNTD64(); byte *tableBase = vm->m_currentContext->code() + vm->m_currentContext->pc_next(); uint16 sw_int = (int16) (sw_count >> 48); uint16 sw_rng = (int16) (sw_count >> 32); uint16 sw_str = (int16) (sw_count >> 16); uint16 sw_obj = (int16) sw_count; //determine the value type to be checked switch( operand2->type() ) { case FLC_ITEM_NIL: if ( *reinterpret_cast( tableBase ) != 0xFFFFFFFF ) { vm->m_currentContext->pc_next() = *reinterpret_cast( tableBase ); return; } break; case FLC_ITEM_INT: if ( sw_int > 0 && vm->seekInteger( operand2->asInteger(), tableBase + sizeof(uint32), sw_int, vm->m_currentContext->pc_next() ) ) return; if ( sw_rng > 0 && vm->seekInRange( operand2->asInteger(), tableBase + sizeof(uint32) + sw_int * (sizeof(uint64) + sizeof(uint32) ), sw_rng, vm->m_currentContext->pc_next() ) ) return; break; case FLC_ITEM_NUM: if ( sw_int > 0 && vm->seekInteger( operand2->forceInteger(), tableBase + sizeof(uint32), sw_int, vm->m_currentContext->pc_next() ) ) return; if ( sw_rng > 0 && vm->seekInRange( operand2->forceInteger(), tableBase + sizeof(uint32) + sw_int * (sizeof(uint64) + sizeof(uint32) ), sw_rng, vm->m_currentContext->pc_next() ) ) return; break; case FLC_ITEM_STRING: if ( sw_str > 0 && vm->seekString( operand2->asString(), tableBase + sizeof(uint32) + sw_int * (sizeof(uint64) + sizeof(uint32) )+ sw_rng * (sizeof(uint64) + sizeof(uint32) ), sw_str, vm->m_currentContext->pc_next() ) ) return; break; } // always tries with the objects, if given and had not success up to here. if ( sw_obj > 0 && vm->seekItem( operand2, tableBase + sizeof(uint32) + sw_int * (sizeof(uint64) + sizeof(uint32) ) + sw_rng * (sizeof(uint64) + sizeof(uint32) ) + sw_str * (sizeof(uint32) + sizeof(uint32) ), sw_obj, vm->m_currentContext->pc_next() ) ) return; // in case of failure... vm->m_currentContext->pc_next() = pNext; } //46 void opcodeHandler_IN( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); bool result = false; switch( operand2->type() ) { case FLC_ITEM_STRING: if( operand1->type() == FLC_ITEM_STRING ) result = operand2->asString()->find( *operand1->asString() ) != csh::npos; break; case FLC_ITEM_ARRAY: { Item *elements = operand2->asArray()->items().elements(); for( uint32 pos = 0; pos asArray()->length(); pos++ ) { if( elements[pos] == *operand1 ) { result = true; break; } } } break; case FLC_ITEM_DICT: { result = operand2->asDict()->find( *operand1 ) != 0; } break; case FLC_ITEM_OBJECT: { // is the object embedding a dictionary interface? Sequence* seq = operand2->asObjectSafe()->getSequence(); if( seq != 0 && seq->isDictionary() ) { result = static_cast(seq)->find(*operand1) != 0 ; } else { if( operand1->type() == FLC_ITEM_STRING ) result = operand2->asObjectSafe()->hasProperty( *operand1->asString() ); } } break; case FLC_ITEM_CLASS: { if( operand1->type() == FLC_ITEM_STRING ) { uint32 pos; result = operand2->asClass()->properties().findKey( *operand1->asString(), pos ) != 0; } } break; } vm->regA().setBoolean( result ); } //47 void opcodeHandler_NOIN( register VMachine *vm ) { // do not decode operands; IN will do it opcodeHandler_IN( vm ); vm->regA().setBoolean( ! vm->regA().asBoolean() ); } //48 void opcodeHandler_PROV( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); if ( operand2->type() != FLC_ITEM_STRING ) { vm->raiseHardError( e_invop, "PROV", __LINE__ ); // hard error, as the string should be created by the compiler return; } bool result; uint32 pos; switch ( operand1->type() ) { case FLC_ITEM_CLASS: result = operand1->asClass()->properties().findKey( *operand2->asString(), pos ) != 0; break; case FLC_ITEM_OBJECT: result = operand1->asObjectSafe()->hasProperty( *operand2->asString() ); break; case FLC_ITEM_ARRAY: result = operand1->asArray()->getProperty( *operand2->asString() )!=0; break; case FLC_ITEM_DICT: result = operand1->asDict()->isBlessed() && operand1->asDict()->find( *operand2->asString() )!=0; break; default: result = false; } vm->regA().setBoolean( result ); } //49 void opcodeHandler_STVS( register VMachine *vm ) { if( vm->stack().empty() ) { vm->raiseHardError( e_stackuf, "STVS", __LINE__ ); return; } Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); Item *origin = &vm->stack()[vm->stack().length() - 1]; operand1->setIndex( *operand2, *origin ); vm->regA() = *origin; vm->stack().resize( vm->stack().length() - 1); } //4A void opcodeHandler_STPS( register VMachine *vm ) { if( vm->stack().empty() ) { vm->raiseHardError( e_stackuf, "STPS", __LINE__ ); return; } Item *target = vm->getOpcodeParam( 1 ); Item *method = vm->getOpcodeParam( 2 )->dereference(); if ( method->isString() ) { target->setProperty( *method->asString(), vm->stack()[vm->stack().length() - 1] ); vm->regA() = vm->stack()[vm->stack().length() - 1]; vm->stack().resize( vm->stack().length() - 1 ); } else { vm->stack().resize( vm->stack().length() - 1 ); throw new TypeError( ErrorParam( e_prop_acc ).extra("STPS").origin( e_orig_vm ) ); } } //4B void opcodeHandler_AND( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); vm->regA().setBoolean( operand1->isTrue() && operand2->isTrue() ); } //4C void opcodeHandler_OR( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); vm->regA().setBoolean( operand1->isTrue() || operand2->isTrue() ); } //50 void opcodeHandler_STV( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); Item *origItm = vm->getOpcodeParam( 3 ); Item *origin = origItm->dereference(); operand1->setIndex( *operand2, *origin ); if( origItm != &vm->regB() ) vm->regA() = *origin; } //51 void opcodeHandler_STP( register VMachine *vm ) { Item *target = vm->getOpcodeParam( 1 ); Item *method = vm->getOpcodeParam( 2 )->dereference(); Item *sourcend = vm->getOpcodeParam( 3 ); Item *source = sourcend->dereference(); if ( method->isString() ) { target->setProperty( *method->asString(), *source ); // when B is the source, the right value is already in A. if( sourcend != &vm->regB() ) vm->regA() = *source; return; } throw new TypeError( ErrorParam( e_invop ).extra("STP").origin( e_orig_vm ) ); /** \todo check this code This code originally allowed to load a method of an object into a method of another object. This is considered now too danjerous, as external methods can rely on how the object is built. So, it's fine to set a function into a method, but it's not fine to extract a method or to exchange methods between classes. An alternative could be that to force methods to check for user data signature, but that was exactly what I wanted to avoid when putting user data in objects. Giancarlo Niccolai \code if( source->type() == FLC_ITEM_METHOD ) { // will instantiate by value if( target->asObject()->setProperty( vm->m_op2->asString(), source->asMethodFunction() ) ) return; } else if ( source->type() == FLC_ITEM_EXTMETHOD ) { Item itm; itm.setExtFunc(source->asMethodExtFunc()); if( target->asObject()->setProperty( vm->m_op2->asString(), itm ) ) return; } else { if( target->asObject()->setProperty( vm->m_op2->asString(), *source ) ) return; } } \endcode */ } //52 void opcodeHandler_LDVT( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); vm->latch() = *operand1; vm->latcher() = *operand2; operand1->getIndex( *operand2, *vm->getOpcodeParam( 3 ) ); } //53 void opcodeHandler_LDPT( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); vm->latch() = *operand1; vm->latcher() = *operand2; if( operand2->isString() ) { String *property = operand2->asString(); operand1->getProperty( *property, *vm->getOpcodeParam( 3 ) ); } else throw new AccessError( ErrorParam( e_prop_acc ).origin( e_orig_vm ) . extra( operand2->isString() ? *operand2->asString() : "?" ) ); } //54 void opcodeHandler_STVR( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); // do not deref op3 Item *origin = vm->getOpcodeParam( 3 ); //STV counts as an assignment, we must copy to expression value. if( origin != &vm->regA()) vm->regA() = *origin; GarbageItem *gitem; if( ! origin->isReference() ) { gitem = new GarbageItem( *origin ); origin->setReference( gitem ); } operand1->setIndex( *operand2, *origin ); } //55 void opcodeHandler_STPR( register VMachine *vm ) { Item *target = vm->getOpcodeParam( 1 ); Item *method = vm->getOpcodeParam( 2 )->dereference(); Item *source = vm->getOpcodeParam( 3 ); vm->regA() = *source; if ( method->isString() ) { target->setProperty( *method->asString(), *source ); return; } throw new TypeError( ErrorParam( e_prop_acc ).extra("STPR").origin( e_orig_vm ) ); } //56 void opcodeHandler_TRAV( register VMachine *vm ) { // get the jump label. uint32 wayout = (uint32) vm->getNextNTD32(); // get the number of variables in the trav loop uint32 varcount = (uint32) vm->getNextNTD32(); Item *source = vm->getOpcodeParam( 3 )->dereference(); // our sequence: Sequence *seq = 0; switch( source->type() ) { case FLC_ITEM_ARRAY: seq = &source->asArray()->items(); break; case FLC_ITEM_DICT: seq = &source->asDict()->items(); break; case FLC_ITEM_OBJECT: seq = source->asObjectSafe()->getSequence(); // is the object a direct sequence? if( seq == 0 ) { // is it a callable entity? if ( source->asObjectSafe()->hasProperty( OVERRIDE_OP_CALL )) { seq = new GeneratorSeq( vm, *source ); GarbagePointer* ptr = new GarbagePointer( seq ); seq->owner( ptr ); } else // we have mess on the stack, but it doesn't matter here. throw new TypeError( ErrorParam( e_invop ).extra("TRAV").origin( e_orig_vm ) ); } break; case FLC_ITEM_RANGE: // optimize: avoid creating a bit of stuff if not needed if( source->asRangeIsOpen() || (source->asRangeEnd() == source->asRangeStart() && source->asRangeStep() == 0) || ( source->asRangeStep() > 0 && source->asRangeStart() > source->asRangeEnd() ) || ( source->asRangeStep() < 0 && source->asRangeStart() < source->asRangeEnd() ) ) { vm->jump( wayout ); return; } // ok, the range can generate a sequence. Hence... try { seq = new RangeSeq( *source->asRange() ); // also, save the sequence in a garbage sensible item. // we don't record anywhere this item, but it will be marked // as long as we have the iterator active in the stack. GarbagePointer* ptr = new GarbagePointer( seq ); seq->owner( ptr ); seq->gcMark( vm->generation() ); // will mark our pointer } catch( ... ) { fassert( false ); // must not throw } break; case FLC_ITEM_NIL: // jump out vm->jump( wayout ); return; default: if( source->isCallable() ) { seq = new GeneratorSeq( vm, *source ); GarbagePointer* ptr = new GarbagePointer( seq ); seq->owner( ptr ); } else { throw new TypeError( ErrorParam( e_invop ).extra("TRAV").origin( e_orig_vm ) ); } } // empty sequence? if ( seq == 0 || seq->empty() ) { vm->jump(wayout); return; } // create the iterator and push it. Item iterItem; Iterator* current = new Iterator( seq ); iterItem.setGCPointer( current ); vm->pushParam( iterItem ); // the source is safe through back-marking of the iterator on its sequence, // and of the sequence on its owner. // now distribute the current value of the element on the variables. vm->expandTRAV( varcount, *current ); } //57 void opcodeHandler_INCP( register VMachine *vm ) { Item *operand = vm->getOpcodeParam( 1 )->dereference(); Item temp; operand->incpost( temp ); vm->regB() = *operand; vm->regA() = temp; } //58 void opcodeHandler_DECP( register VMachine *vm ) { Item *operand = vm->getOpcodeParam( 1 )->dereference(); Item temp; operand->decpost( temp ); vm->regB() = *operand; vm->regA() = temp; } //59 void opcodeHandler_SHL( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); if( operand1->isInteger() && operand2->isInteger() ) vm->regA().setInteger( operand1->asInteger() << operand2->asInteger() ); else throw new TypeError( ErrorParam( e_invop ).origin( e_orig_vm ).extra("SHL") ); } //5A void opcodeHandler_SHR( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); if( operand1->isInteger() && operand2->isInteger() ) vm->regA().setInteger( operand1->asInteger() >> operand2->asInteger() ); else throw new TypeError( ErrorParam( e_invop ).origin( e_orig_vm ).extra("SHR") ); } //5B void opcodeHandler_SHLS( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); if( operand1->isInteger() && operand2->isInteger() ) operand1->setInteger( operand1->asInteger() << operand2->asInteger() ); else throw new TypeError( ErrorParam( e_invop ).origin( e_orig_vm ).extra("SHLS") ); } //5C void opcodeHandler_SHRS( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); if( operand1->isInteger() && operand2->isInteger() ) operand1->setInteger( operand1->asInteger() >> operand2->asInteger() ); else throw new TypeError( ErrorParam( e_invop ).origin( e_orig_vm ).extra("SHRS") ); } //5D void opcodeHandler_CLOS( register VMachine *vm ) { uint32 size = (uint32) vm->getNextNTD32(); Item *tgt = vm->getOpcodeParam( 2 )->dereference(); Item *src = vm->getOpcodeParam( 3 )->dereference(); fassert( src->isFunction() ); *tgt = new CoreFunc( *src->asFunction() ); if( size > 0 ) { if ( size > vm->stack().length() ) { throw new CodeError( ErrorParam( e_stackuf ).origin( e_orig_vm ).extra("CLOS") ); } ItemArray* closure = new ItemArray( size ); Item *data = closure->elements(); int32 base = vm->stack().length() - size; memcpy( data, &vm->stack()[ base ], sizeof(Item)*size ); closure->length( size ); vm->stack().resize( base ); tgt->asFunction()->closure( closure ); } } // 5D void opcodeHandler_PSHL( register VMachine *vm ) { vm->stack().append( *vm->getOpcodeParam( 1 ) ); } // 5E void opcodeHandler_POWS( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 ); // TODO: S-operators operand1->pow( *operand2, *operand1 ); vm->regA() = *operand1; } //5F void opcodeHandler_LSB( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); switch( operand1->type() << 8 | operand2->type() ) { case FLC_ITEM_STRING << 8 | FLC_ITEM_INT: case FLC_ITEM_STRING << 8 | FLC_ITEM_NUM: { int32 pos = (int32) operand2->forceInteger(); String *cs = operand1->asString(); if ( cs->checkPosBound( pos ) ) vm->retval( (int64) cs->getCharAt( pos ) ); else throw new AccessError( ErrorParam( e_arracc ).origin( e_orig_vm ).extra( "LSB" ) ); } return; } throw new TypeError( ErrorParam( e_invop ).origin( e_orig_vm ).extra( "LSB" ) ); } // 60 void opcodeHandler_EVAL( register VMachine *vm ) { // We know the first operand must be a string Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); if ( operand1->isArray() ) { CoreArray *arr = operand1->asArray(); if ( arr->length() > 0 ) { // fake as if we were called by a function // This will cause functionalEval to produce a correct return frame // in case it needs sub functional evals. vm->createFrame(0); uint32 savePC = vm->m_currentContext->pc_next(); vm->m_currentContext->pc_next() = VMachine::i_pc_call_external_return; if( ! vm->functionalEval( *operand1 ) ) { // it wasn't necessary; reset pc to the correct value vm->callReturn(); vm->m_currentContext->pc_next() = savePC; } // ok here we're ready either to jump where required by functionalEval or // to go on as usual return; } } else if ( operand1->isCallable() ) { vm->callFrame( *operand1, 0 ); return; } // by default, just copy the operand vm->regA() = *operand1; } //61 void opcodeHandler_SELE( register VMachine *vm ) { uint32 pNext = (uint32) vm->getNextNTD32(); Item *real_op2 = vm->getOpcodeParam( 2 ); Item *op2 = real_op2->dereference(); uint64 sw_count = (uint64) vm->getNextNTD64(); byte *tableBase = vm->m_currentContext->code() + vm->m_currentContext->pc_next(); // SELE B has a special semantic used as a TRY/CATCH helper bool hasDefault = *reinterpret_cast( tableBase ) == 0; bool reRaise = real_op2 == &vm->regB(); // In select we use only integers and object fields. uint16 sw_int = (int16) (sw_count >> 48); uint16 sw_obj = (int16) sw_count; //determine the value type to be checked int type = op2->type(); if ( sw_int > 0 ) { if ( type == FLC_ITEM_INT ) type = FLC_ITEM_NUM; if ( vm->seekInteger( type, tableBase + sizeof(uint32), sw_int, vm->m_currentContext->pc_next() ) ) return; } if (sw_obj > 0 ) { // we have to search the symbol of the class of our object // always tries with the objects, if given and had not success up to here. if ( vm->seekItemClass( op2, tableBase + sizeof(uint32) + sw_int * (sizeof(uint64) + sizeof(uint32) ), sw_obj, vm->m_currentContext->pc_next() ) ) return; } if ( reRaise && hasDefault ) // in case of re-raise { throw vm->regB(); } // in case of failure go to default or past sele... vm->m_currentContext->pc_next() = pNext; } //62 void opcodeHandler_INDI( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); if ( ! operand1->isString() ) { throw new TypeError( ErrorParam( e_invop ).extra("INDI").origin( e_orig_vm ) ); } if ( ! vm->findLocalVariable( *operand1->asString(), vm->regA() ) ) { throw new ParamError( ErrorParam( e_param_indir_code ).origin( e_orig_vm ).extra( "INDI" ) ); } } //63 void opcodeHandler_STEX( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); if ( ! operand1->isString() ) { throw new TypeError( ErrorParam( e_invop ).extra("STEX").origin( e_orig_vm ) ); } CoreString *target = new CoreString; String *src = operand1->asString(); VMachine::returnCode retval = vm->expandString( *src, *target ); switch( retval ) { case VMachine::return_ok: vm->regA() = target; break; case VMachine::return_error_parse_fmt: throw new TypeError( ErrorParam( e_fmt_convert ).origin( e_orig_vm ).extra( "STEX-format" ) ); break; case VMachine::return_error_string: throw new ParamError( ErrorParam( e_param_strexp_code ).origin( e_orig_vm ).extra( "STEX-string" ) ); break; case VMachine::return_error_parse: throw new ParamError( ErrorParam( e_param_indir_code ).origin( e_orig_vm ).extra( "STEX-parse" ) ); break; default: // warning no-op return; } } //64 void opcodeHandler_TRAC( register VMachine *vm ) { if ( vm->stack().length() < 1 ) { vm->raiseHardError( e_stackuf, "TRAC", __LINE__ ); } Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); const Item &top = vm->stack().back(); fassert( top.isGCPointer() ); Iterator* iter = dyncast(top.asGCPointer()); if( operand1->isString() ) iter->getCurrent() = new CoreString( *operand1->asString() ); else iter->getCurrent() = *operand1; } //65 void opcodeHandler_WRT( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 )->dereference(); Stream *stream = vm->stdOut(); if ( stream == 0 ) { vm->raiseHardError( e_invop, "WRT", __LINE__ ); return; } if( operand1->isString() ) { String *s = operand1->asString(); stream->writeString( *s ); } else { String temp; vm->itemToString( temp, operand1 ); stream->writeString( temp ); } stream->flush(); } void opcodeHandler_STO( register VMachine *vm ) { // STO is like LD, but it doesn't dereference param 1. Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); /*if ( operand2->isString() ) operand1->setString( new CoreString( *operand2->asString() ) ); else*/ operand1->copy( *operand2 ); vm->regA() = *operand1; } // 0x67 void opcodeHandler_FORB( register VMachine *vm ) { // We know the first operand must be a string Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 )->dereference(); if ( ! operand1->isString() ) { vm->raiseHardError( e_invop, "FORB", __LINE__ ); return; } vm->regA().setLBind( new CoreString( *operand1->asString() ), new GarbageItem( *operand2 ) ); } // 0x68 void opcodeHandler_OOB( register VMachine *vm ) { uint32 pmode = (uint32) vm->getNextNTD32(); Item *operand = vm->getOpcodeParam( 2 )->dereference(); switch( pmode ) { case 0: vm->regA() = *operand; vm->regA().setOob( false ); break; case 1: vm->regA() = *operand; vm->regA().setOob( true ); break; case 2: vm->regA() = *operand; vm->regA().setOob( ! operand->isOob() ); break; default: vm->regA().setBoolean( operand->isOob() ); } } // 0x69 void opcodeHandler_TRDN( register VMachine *vm ) { if ( vm->stack().length() < 1 ) { vm->raiseHardError( e_stackuf, "TRAC", __LINE__ ); } uint32 loop_begin = vm->getNextNTD32(); uint32 loop_out = vm->getNextNTD32(); uint32 vars = vm->getNextNTD32(); const Item &top = vm->stack().back(); fassert( top.isGCPointer() ); Iterator* iter = dyncast(top.asGCPointer()); if( vars == 0 ) { fassert( iter->hasPrev() ); iter->prev(); iter->erase(); vm->jump( loop_begin ); // actually, the "out" pointer } else { // a normal TRDN iter->erase(); if( iter->hasCurrent() ) { vm->expandTRAV( vars, *iter ); vm->jump( loop_begin ); } else vm->jump( loop_out ); } } // 0x70 void opcodeHandler_EXEQ( register VMachine *vm ) { Item *operand1 = vm->getOpcodeParam( 1 ); Item *operand2 = vm->getOpcodeParam( 2 ); vm->regA().setBoolean( operand1->exactlyEqual(*operand2) ); } } /* end of vm_run.cpp */ engine/vm_sys_posix.cpp000066400000000000000000000061341176363201700156030ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vm_sys_posix.cpp System specifics for the falcon VM - POSIX compliant systems. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 25 Apr 2008 17:30:00 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include // if poll.h does not define POLLIN, then it's in stropts. #ifndef POLLIN #include #endif #include namespace Falcon { namespace Sys { SystemData::SystemData(VMachine *vm) { m_sysData = (struct VM_SYS_DATA*) memAlloc( sizeof( struct VM_SYS_DATA ) ); m_vm = vm; m_sysData->isSignalTarget = false; // create the dup'd suspend pipe. if( pipe( m_sysData->interruptPipe ) != 0 ) { printf( "Falcon: fatal allocation error in creating pipe at %s:%d\n", __FILE__, __LINE__ ); exit(1); } } SystemData::~SystemData() { // close the pipe close( m_sysData->interruptPipe[0] ); close( m_sysData->interruptPipe[1] ); // delete the structure memFree( m_sysData ); } bool SystemData::interrupted() const { int res; struct pollfd fds[1]; fds[0].events = POLLIN; fds[0].fd = m_sysData->interruptPipe[0]; while( (res = poll( fds, 1, 0 ) ) == EAGAIN ); // If we have one event, the we have to read... return res == 1; } void SystemData::interrupt() { if( write( m_sysData->interruptPipe[1], "0", 1 ) != 1 ) { printf( "Falcon: fatal error in writing to the interrupt pipe at %s:%d\n", __FILE__, __LINE__ ); exit(1); } } void SystemData::resetInterrupt() { int res; struct pollfd fds[1]; fds[0].events = POLLIN; fds[0].fd = m_sysData->interruptPipe[0]; do { res = poll( fds, 1, 0 ); if( res == 1 ) { char dt; if( read( m_sysData->interruptPipe[0], &dt, 1 ) < 0 ) { printf( "Falcon: fatal error in reading from the interrupt pipe at %s:%d\n", __FILE__, __LINE__ ); exit(1); } continue; } } while( res == EAGAIN ); } bool SystemData::sleep( numeric seconds ) const { int ms = (int) (seconds * 1000.0); struct pollfd fds[1]; fds[0].events = POLLIN; fds[0].fd = m_sysData->interruptPipe[0]; int res; while( ( res = poll( fds, 1, ms )) == EAGAIN ); // if res is 0, then we completed the wait. return res == 0; } const char *SystemData::getSystemType() { return "POSIX"; } bool SystemData::becomeSignalTarget() { if ( 0 != signalReceiver ) return false; m_sysData->isSignalTarget = true; signalReceiver = new SignalReceiver(m_vm); signalReceiver->start(); return true; } void SystemData::earlyCleanup() { if ( m_sysData->isSignalTarget ) { delete signalReceiver; signalReceiver = 0; } } } } /* end of vm_sys_posix.cpp */ engine/vm_sys_win.cpp000066400000000000000000000031461176363201700152360ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vm_sys_win.cpp System specifics for the falcon VM - POSIX compliant systems. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 25 Apr 2008 17:30:00 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include namespace Falcon { namespace Sys { SystemData::SystemData(VMachine *vm) { m_vm = vm; m_sysData = (struct VM_SYS_DATA*) memAlloc( sizeof( struct VM_SYS_DATA ) ); // create our interrupting event (a manual reset event) m_sysData->evtInterrupt = CreateEvent( NULL, TRUE, FALSE, NULL ); } SystemData::~SystemData() { CloseHandle( m_sysData->evtInterrupt ); // delete the structure memFree( m_sysData ); } bool SystemData::interrupted() const { return WaitForSingleObject( m_sysData->evtInterrupt, 0 ) == WAIT_OBJECT_0; } void SystemData::interrupt() { SetEvent( m_sysData->evtInterrupt ); } void SystemData::resetInterrupt() { ResetEvent( m_sysData->evtInterrupt ); } bool SystemData::sleep( numeric seconds ) const { return WaitForSingleObject( m_sysData->evtInterrupt, (DWORD) (seconds * 1000.0) ) != WAIT_OBJECT_0; } const char *SystemData::getSystemType() { return "WIN"; } bool SystemData::becomeSignalTarget() { /* no-op for now */ return true; } void SystemData::earlyCleanup() { } } } /* end of vm_sys_win.cpp */ engine/vmcontext.cpp000066400000000000000000000210321176363201700150620ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vmcontext.cpp Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar nov 9 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Short description */ #include #include "vmsema.h" #include #include #include #include #define VM_STACK_MEMORY_THRESHOLD 128 namespace Falcon { //================================== // Deletor for the frame list. VMContext::VMContext(): m_frames(0), m_spareFrames(0) { m_sleepingOn = 0; m_schedule = 0.0; m_priority = 0; m_atomicMode = false; m_tryFrame = 0; m_pc = 0; m_pc_next = 0; m_symbol = 0; m_lmodule = 0; m_frames = allocFrame(); // reset stuff for the first frame, // as allocFrame doesn't clear everything for performance reasons. m_frames->m_param_count = 0; m_frames->m_break = false; } VMContext::VMContext( const VMContext& other ): m_frames(0), m_spareFrames(0) { m_sleepingOn = 0; m_schedule = 0.0; m_priority = 0; m_atomicMode = false; m_tryFrame = 0; m_pc = other.m_pc; m_pc_next = other.m_pc_next; m_symbol = other.m_symbol; m_lmodule = other.m_lmodule; m_frames = allocFrame(); // reset stuff for the first frame, // as allocFrame doesn't clear everything for performance reasons. m_frames->m_param_count = 0; m_frames->m_break = false; } VMContext::~VMContext() { StackFrame* frame = m_spareFrames; while ( frame != 0 ) { StackFrame* gone = frame; frame = frame->prev(); delete gone; } frame = m_frames; while ( frame != 0 ) { StackFrame* gone = frame; frame = frame->prev(); delete gone; } m_frames = m_spareFrames = 0; } void VMContext::scheduleAfter( numeric secs ) { m_schedule = Sys::_seconds() + secs; } void VMContext::waitOn( VMSemaphore* sem, numeric secs ) { if( secs < 0.0 ) m_schedule = -1.0; else m_schedule = Sys::_seconds() + secs; m_sleepingOn = sem; } void VMContext::wakeup( bool signaled ) { if ( m_sleepingOn != 0 ) // overkill, but... { m_sleepingOn->unsubscribe( this ); m_sleepingOn = 0; m_schedule = 0.0; // immediately runnable // don't change the A status if not sleeping on a semaphore. regA().setBoolean(signaled); // we have not been awaken, and must return false } } void VMContext::signaled() { if ( m_sleepingOn != 0 ) // overkill, but... { // Don't unsubscribe; the semaphore is unsubscribing us. m_sleepingOn = 0; m_schedule = 0.0; // immediately runnable } regA().setBoolean(true); // we have not been awaken, and must return false} } StackFrame* VMContext::createFrame( uint32 paramCount, ext_func_frame_t frameEndFunc ) { StackFrame* frame = allocFrame(); frame->prepareParams( m_frames, paramCount ); frame->m_symbol = symbol(); frame->m_module = lmodule(); frame->m_ret_pc = pc_next(); frame->m_call_pc = pc(); frame->m_break = false; frame->m_prevTryFrame = m_tryFrame; frame->m_try_base = VMachine::i_noTryFrame; // parameter count. frame->m_param_count = paramCount; // iterative processing support frame->m_endFrameFunc = frameEndFunc; frame->m_self.setNil(); frame->m_binding = regBind(); return frame; } void VMContext::fillErrorTraceback( Error &error ) { fassert( ! error.hasTraceback() ); const Symbol *csym = m_symbol; if ( csym != 0 ) { uint32 curLine; if( csym->isFunction() ) { curLine = csym->module()->getLineAt( csym->getFuncDef()->basePC() + pc() ); } else { // should have been filled by raise curLine = error.line(); } error.addTrace( csym->module()->name(), csym->module()->path(), csym->name(), curLine, pc()); } StackFrame* frame = currentFrame(); while( frame != 0 ) { const Symbol *sym = frame->m_symbol; if ( sym != 0 ) { // possible when VM has not been initiated from main uint32 line; if( sym->isFunction() ) line = sym->module()->getLineAt( sym->getFuncDef()->basePC() + frame->m_call_pc ); else line = 0; error.addTrace( sym->module()->name(), sym->module()->path(), sym->name(), line, frame->m_call_pc ); } frame = frame->prev(); } } bool VMContext::getTraceStep( uint32 level, const Symbol* &sym, uint32& line, uint32 &pc ) { StackFrame* frame = currentFrame(); while( frame != 0 && frame->m_symbol != 0 && level > 0 ) { frame = frame->prev(); level--; } if( frame == 0 || frame->m_symbol == 0 ) return false; sym = frame->m_symbol; pc = frame->m_call_pc; if( sym->isFunction() ) line = sym->module()->getLineAt( sym->getFuncDef()->basePC() + pc ); else line = 0; return true; } void VMContext::addFrame( StackFrame* frame ) { frame->prev( m_frames ); m_frames = frame; } StackFrame* VMContext::popFrame() { if ( m_frames == 0 ) return 0; StackFrame *ret = m_frames; m_frames = m_frames->prev(); ret->prev(0); return ret; } StackFrame* VMContext::allocFrame() { if( m_spareFrames != 0 ) { StackFrame* ret = m_spareFrames; m_spareFrames = m_spareFrames->prev(); ret->resizeStack(0); ret->prev(0); ret->m_try_base = VMachine::i_noTryFrame; ret->m_prevTryFrame = 0; ret->m_module = 0; ret->m_symbol = 0; return ret; } return new StackFrame; } void VMContext::disposeFrame( StackFrame* frame ) { frame->prev( m_spareFrames ); m_spareFrames = frame; } void VMContext::disposeFrames( StackFrame* first, StackFrame* last ) { last->prev( m_spareFrames ); m_spareFrames = first; } void VMContext::resetFrames() { if( m_frames == 0 ) { m_frames = allocFrame(); } else { StackFrame* top = m_frames->prev(); m_frames->prev( 0 ); m_frames->resizeStack(0); if ( top != 0 ) { StackFrame* last = top; while ( last->prev() != 0 ) { last = last->prev(); } disposeFrames( top, last ); } } /* if ( m_frames != 0 ) { StackFrame* last = m_frames; while ( last->prev() != 0 ) { last = last->prev(); } disposeFrames( m_frames, last ); m_frames = 0; } */ m_frames->m_symbol = 0; m_frames->m_module = 0; m_tryFrame = 0; } void VMContext::pushTry( uint32 landingPC ) { Item frame1( (((int64) landingPC) << 32) | (int64) currentFrame()->m_try_base ); m_tryFrame = currentFrame(); currentFrame()->pushItem( frame1 ); currentFrame()->m_try_base = currentFrame()->stackSize(); } void VMContext::popTry( bool moveTo ) { // If the try frame is wrong or not in current stack frame... if( m_tryFrame == 0 ) { throw new CodeError( ErrorParam( e_stackuf, __LINE__ ). origin( e_orig_vm ) ); } // search the frame to pop while ( currentFrame() != m_tryFrame ) { disposeFrame( popFrame() ); } // get the frame and resize the stack currentFrame()->resizeStack( currentFrame()->m_try_base ); fassert( currentFrame()->topItem().isInteger() ); int64 tf_land = currentFrame()->topItem().asInteger(); currentFrame()->pop(1); // Change the try frame, and eventually move the PC to the proper position currentFrame()->m_try_base = (uint32) tf_land; if( ((uint32) tf_land) == VMachine::i_noTryFrame ) { m_tryFrame = currentFrame()->m_prevTryFrame; } if( moveTo ) { pc_next() = (uint32)(tf_land>>32); pc() = pc_next(); } } bool VMContext::callReturn() { // Get the stack frame. StackFrame &frame = *currentFrame(); // change symbol symbol( frame.m_symbol ); pc_next() = frame.m_ret_pc; // eventually change active module. lmodule( frame.m_module ); // reset try frame m_tryFrame = frame.m_prevTryFrame; regBind() = frame.m_binding; // reset stack base and resize the stack disposeFrame( popFrame() ); // currentFrame may be zero, but in that case m_param_count would be zero too if ( frame.m_param_count != 0 ) { fassert( currentFrame() != 0 ); currentFrame()->pop( frame.m_param_count ); } return frame.m_break; } } /* end of vmcontext.cpp */ engine/vmmaps.cpp000066400000000000000000000035461176363201700143500ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: vmmaps.cpp Map items used in VM and related stuff. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab nov 4 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Map items used in VM and related stuff. */ #include #include #include namespace Falcon { uint32 SymModuleTraits::memSize() const { return sizeof( SymModule ); } void SymModuleTraits::init( void *itemZone ) const { // nothing } void SymModuleTraits::copy( void *targetZone, const void *sourceZone ) const { SymModule *target = (SymModule *) targetZone; SymModule *source = (SymModule *) sourceZone; memcpy( target, source, sizeof( SymModule ) ); } int SymModuleTraits::compare( const void *firstz, const void *secondz ) const { // Never used as key return 0; } void SymModuleTraits::destroy( void *item ) const { // do nothing } bool SymModuleTraits::owning() const { return false; } namespace traits { SymModuleTraits &t_symmodule() { static SymModuleTraits dt; return dt; } } SymModuleMap::SymModuleMap(): Map( &traits::t_stringptr(), &traits::t_symmodule() ) {} SymModule::SymModule( LiveModule *mod, const Symbol *sym ): m_item( &mod->globals()[ sym->itemId() ] ), m_symbol( sym ), m_lmod( mod ), m_wkiid( -1 ) {} //================================================================== // Live module map // namespace traits { LiveModulePtrTraits &t_livemoduleptr() { static LiveModulePtrTraits dt; return dt; } } LiveModuleMap::LiveModuleMap(): Map( &traits::t_string(), &traits::t_livemoduleptr() ) {} } /* end of vmmaps.cpp */ engine/vmmsg.cpp000066400000000000000000000031741176363201700141730ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vmmsg.cpp Asynchronous message for the Virtual Machine. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 08 Feb 2009 16:08:50 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Asynchronous message for the Virtual Machine - Implementation. */ #include #include #include #include #define PARAMS_GROWTH 8 namespace Falcon { VMMessage::VMMessage( const String &msgName ): m_msg( msgName ), m_params(0), m_allocated(0), m_pcount(0), m_next(0), m_error(0) { } VMMessage::~VMMessage() { if( m_params != 0 ) { for(uint32 i = 0; i < m_pcount; ++i ) { memPool->markItem( m_params[i] ); } memFree( m_params ); } if ( m_error != 0 ) m_error->decref(); } void VMMessage::addParam( const SafeItem &itm ) { if ( m_params == 0 ) { m_params = (SafeItem *) memAlloc( sizeof( SafeItem ) * PARAMS_GROWTH ); m_allocated = PARAMS_GROWTH; } else if( m_pcount == m_allocated ) { m_allocated += PARAMS_GROWTH; m_params = (SafeItem *) memRealloc( m_params, sizeof( SafeItem ) * m_allocated ); } m_params[ m_pcount++ ].copy( itm ); } SafeItem *VMMessage::param( uint32 p ) const { return p < m_pcount ? &m_params[p] : 0; } void VMMessage::onMsgComplete( bool ) { } } /* end of vmmsg.cpp */ engine/vmsema.cpp000066400000000000000000000031301176363201700143220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vmsema.cpp Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: gio nov 11 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Short description */ #include "vmsema.h" #include namespace Falcon { void VMSemaphore::post( VMachine *vm, int32 count ) { m_count += count; while( m_count > 0 && ! m_waiting.empty() ) { VMContext *ctx = (VMContext *) m_waiting.front(); ctx->signaled(); vm->reschedule( ctx ); m_waiting.popFront(); m_count --; } } void VMSemaphore::wait( VMachine *vm, numeric to ) { if ( m_count == 0 ) { m_waiting.pushBack( vm->m_currentContext ); vm->m_currentContext->waitOn( this, to ); // by default will be zero; 1 if correctly awaken vm->regA().setBoolean( false ); // now we can change the context vm->rotateContext(); } else { vm->regA().setBoolean( true ); m_count --; } } void VMSemaphore::unsubscribe( VMContext *ctx ) { ListElement *elem = m_waiting.begin(); while( elem != 0 ) { VMContext *cty = (VMContext *) elem->data(); if ( ctx == cty ) { m_waiting.erase( elem ); return; } elem = elem->next(); } } VMSemaphore *VMSemaphore::clone() const { // ! not supported. return 0; } } /* end of vmsema.cpp */ engine/vmsema.h000066400000000000000000000017611176363201700137770ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vmsema.h Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: gio nov 11 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Short description */ #ifndef flc_vmsema_H #define flc_vmsema_H #include #include namespace Falcon { class VMContext; class VMSemaphore: public FalconData { int32 m_count; ContextList m_waiting; public: VMSemaphore( int32 count = 0 ): m_count( count ) {} ~VMSemaphore() {} void post( VMachine *vm, int32 value=1 ); void wait( VMachine *vm, double time = -1.0 ); void unsubscribe( VMContext *ctx ); virtual VMSemaphore *clone() const; virtual void gcMark( uint32 mark ) {} }; } #endif /* end of vmsema.h */ frameworks/000077500000000000000000000000001176363201700132445ustar00rootroot00000000000000frameworks/CMakeLists.txt000066400000000000000000000001661176363201700160070ustar00rootroot00000000000000############################################################ # CMake list for frameworks. # add_subdirectory( nest ) frameworks/nest/000077500000000000000000000000001176363201700142155ustar00rootroot00000000000000frameworks/nest/CMakeLists.txt000066400000000000000000000003511176363201700167540ustar00rootroot00000000000000############################################################ # CMake list for nest framework. # set( module_dirs nest ) falcon_install_moddirs( ${module_dirs} ) install( DIRECTORY nest/res DESTINATION "${FALCON_MOD_DIR}/nest" ) frameworks/nest/docs/000077500000000000000000000000001176363201700151455ustar00rootroot00000000000000frameworks/nest/docs/config.fd000066400000000000000000000034131176363201700167260ustar00rootroot00000000000000/*# @page config Nest basic setup and configuration @after quickstart Nest is usually installed as a part of the Falcon distribution, and doesn't require any particular setup other than a simple WOPI installation. Because of its nature of web-oriented application engine, CGI drivers are not highly suitable to support its full features. However, it is possible to run the application entry point as a CGI under any of the WOPI CGI based drivers providing proper CGI environment setup. For example, using the falcgi driver it would be necessary to add @code load cgi @endcode at top of the entry point script. Nest is configured through a set of properties to be set directly in the application entry point. If this is not desirable, it is possible to set some configuration keys in a separate module and then load and assign those configuration items in the entry point. For example, to change the default **nest/** application tree top and move it outside the web server host scope, you may: @code load nest ... Nest.site_nest_dir = "../this_site_nest" ... Nest.route() @endcode @section config_opt Configuration options @subsection config_loc Install & setup options - @b site_nest_dir: Path relative to the application entry point where the site-specific Nest services, configurations, data managers, hooks and so on are placed. - @b self_uri: URI of the main script. If not set, in case of need the URI of the entry point script will be determined through the Request fields. @subsection config_loc Debug options - @b pcheck: if true, do extra checks on parameter consistency, to trap common module configuration errors. - @b log_level: 0 for none - 3 for debug - @b debug: set to true to send log informatons at the end of the page being generated. */ frameworks/nest/docs/faldoc.fd000066400000000000000000000031421176363201700167100ustar00rootroot00000000000000# # FALDOC - Falcon Documentation system # # Faldoc file for core module. # LogLevel = 4 AutoBrief = true GetUndoc = false GetPrivate = false ################################################ # Documentation generic data ################################################ Title = Nest Web application framework Author = The Falcon Committee Version = 1.0 ################################################ # Faldoc Input settings ################################################ Input.directory=../ #By default, input directories are also roots for module names, but... Input.moduleRoot = ../ Input.wildcard=*.fal Input.recursive=true # Other files may be specified here Input.extra=main.fd Input.extra=config.fd Input.extra=overview.fd #Input.extra=quickstart.fd Input.extra=widget.fd ################################################ # Faldoc Output settings ################################################ Output.module=html Output.module=docbook ################################################ # Faldoc HTML Output settings # Each module can be self-configured through # Output.. configs ################################################ Output.html.directory=Nest_docs-2.0 Output.html.url=. Output.html.doctitle=Nest Web Framework developer manual ################################################ # Faldoc docbook Output settings ################################################ Output.docbook.file = Nest_doc.xml Output.docbook.encoding = utf-8 Output.docbook.copyright.year = 2012 Output.docbook.copyright.holder = The Falcon Committee Output.docbook.frame = book Output.docbook.detailed = false frameworks/nest/docs/main.fd000066400000000000000000000042361176363201700164110ustar00rootroot00000000000000/*# @main Nest - The Falcon Web Application Framework. Nest is a Web Application Framework standing on top of the web oriented programming interface (WOPI). It's role is that of standardizing and simplifying the development and the deployment of applications residing on a web server and serving multiple client. In other words, it's a standardized base platform for the so called S-D-S (server-database-script) applications. @section status Nest development status The current release of Nest has full support for its application development model. Vital elements of web based applications as session handling, login management, page routing are all supported and flexible but efficient site-specific expansion hooks are provided. Thie release 2.0 of Nest had steered the development approach from a concept of "service" (server-side application element) to Widget (client-side application element), with a great attention to AJAX. ORM elements have been removed from the Nest tree and moved into the main Falcon development system. In other words, Nest, as a web application framework, doesn't include itself an Object-Relation-Model, but relies on the Falcon language framework to provide one. @section phylosophy Philosophy Nest is conceptually based on the orthogonal concepts of __minimal lines per result__ and __minimal override__. Minimal lines per result means that the target of Nest is that of minimizing the amount of live code that must be written to obtain a complete web application. Minimal override means that Nest doesn't "cover" the standards of the system hosting it (Falcon and WOPI), but instead adds new features to it. This means that, although Nest offers powerful features in top of WOPI, all the features of the standard web interface are at disposal of the final implementor. Other than the two cardinal concepts, Nest is based on __modular design__. Nest exposes some components that can be separately configured, integrated or eventually completely reimplemented and overridden at implementor's will, without the need to intervene on the official installation, directly on the target site. The strictly modular design allows for easier code sharing and reuse. */ frameworks/nest/docs/overview.fd000066400000000000000000000253711176363201700173360ustar00rootroot00000000000000/*# @page overview Nest Overview @after main In this section, we'll introduce the basic components that makes up a Nest application, as well as the concepts that must be known by an application designer to write a Nest application. Although not strictly necessary, it's advisable to read this chapter prior moving to the quick start, so that the code written there can be understood in a broader context. @section arch Overall architecture Nest is mainly organized into the following of elements: - Library - AJAX support - Widgets - Hooks - Modules The library is a set of common functions and objects that are exposed to the nest components and to third party users, as the final application. __Modules__ are generic plugins known and loaded by "name". They offer a minimal support for the web application and third party module writers to integrate with Nest. The idea is that copying a module file and its resources in place should be enough to have the module functionalities ready for use in the final web application on a remote web site. __Hooks__ are callback points invoked by the other elements that can be overridden by the host application, so that it can provide new functionalities either dynamically or statically - but on a per-site basis. __AJAX__ support is a feature that integrates AJAX server-side functionalities into the Nest framework. While Nest doesn't prevent using direct AJAX calls (either via raw calls to server-side scripts respecting the AJAX protocol or via the WebAPI Falcon wrapper), Nest programmers may find useful to use the nest AJAX facilities so that the server-side function scripts can see everythign a standard Nest page can see: session variables, services, data managers and so on. __Widgets__ are visual elements that are rendered client-side (on the target browser), and that have support for both dynamic HTML/css/javascript programming and AJAX automation. Widgets can be deemed as AJAX hosts, so that they can dynamically exchange contents with their server-side AJAX host counterpart almost transparently to the web site programmer. Also, Widgets are extensible via sub-classing, and this feature can be used to wrap client-side widgets (i.e. those provided by third party client-side web programming libraries as JQuery and Mootools) into AJAX hosts. Nest also provides a small javascript library that implements some common functionalities of the common AJAX features of Nest widgets and AJAX hosting; this library can be also used stand-alone. For instance, instead of writing a Nest-side widget wrapper around a widget in an external JS library, a web programmer may just use the Nest JS library to send raw AJAX request to a Nest-enabled server. @section appenv Nest application anatomy An application is composed of __pages__, each one presenting exactly one view to the remote guest. An entry point (generally index.fal) loads the Nest system and performs what is called __routing__. This consists in determining which page should be delivered to the user depending on the input variables. The routed page is then loaded as a Falcon plugin (via the reflexive compiler interface) and executed in the current virtual machine context. This makes all the Nest system available to the loaded script. Nest modules are themselves plugins which the routed page can load through the facilities provided by Nest. Services work as abstract processors that can process input from several soruces and then can be shown int the final page through a method that is named __render__. Nest Widgets are simply stand-alone classes that have facilities to generate standardized HTML code, having also some utilities to generate javascripts hooks and callbacks. Also, they can be especially exposed to Nest so that they can receive and respond to AJAX messages generated by their visible counterpart on browser side; yet, if only dynamic javascript is needed, while connection with the AJAX server is not necessary, the Nest framework doesn't need the widgets to be especially published. Otherwise, widgets with AJAX capabilities must be published in the appropriate place so that Nest can load them directly as modules. @section sitestruct Nest site structure Before descending in the details of the Nest routing process, it is useful to describe how Nest site and modules are structured and layed down. Nest system has its own installation location and structure, which is not necessarily related to the way sites using Nest should be organized; at site developer level, the Nest module and utilities are opaque, and all that's required is that Nest and its components are correctly installed as a Falcon module somewhere in the **FALCON_LOAD_PATH**, or even directly in the default Falcon module location (e.g. /usr/lib/falcon). Also, here we'll describe the default site structure; the vast majority of this settings can be changed, even dynamically, through configuration options that are explained later. Consider the entry point of an application a script in a given location; usually, but not necessarily, the script is also the default script for the given directory (e.g. index.fal). Then, the site will be organized as follows: @code /homedir index.fal /nest /ajax /pages /mod /res /hooks /widgets @endcode The **nest** directory is called __root in the nest tree__ or referred as __nest tree__. - The **nest/ajax** subdirectory contains the AJAX functions, one per script. - The **nest/pages** subdirectory contains the pages. - The **nest/mod** subdirectory contains modules and/or their configuration. - The **nest/res** subdirectory contains data files that can be served through a request to nest. Having an out-of-site resource directory can help creating modules that install their needed resources away from the direct reach of the web server. - The **nest/hooks** directory contains __static hooks__, or entry points and callback functions that the site prefer to have statically stored as a separate module. As hooks are little more than callback functions, the site may just present them to the Nest system from the entry point or from the pages where they are relevant, but this is a simple way to setup a callback function that is valid throughout all the site. - The **nest/widgets** contains those widgets that must be published as AJAX hosts. Widgets that don't need AJAX interaction can be declared elsewhere, for instance even in the page they are used. A module in widgets directory can store only one AJAX host widgets, whose class must be named exactly as the module (extension excluded), but it can contain any number of other non-host widget classes. @note It is advisable to place the __nest tree__ outside the scope of the directories served by the web server, if possible, to minimize security risks. The default is to search it right under the entry point script **just** because some low-cost web hostings don't allow to write data outside the scope of the directories served by the provided web server. @section ov_routing Page routing Typical applications will want to load more pages from a site, depending on the connection status and input parameters. This process of selection of the page that is served by Nest to the remote user is called __routing__ and can be widely configured. However, the default mechanism is usually enough to serve the needs of even complex web applications; it consists of serving the page named after an input variable called **pid**. For example, an url like @code http://www.mysite.com/index.fal?pid=user_settings @endcode would search for a falcon module named {{user_settings.fal}} or {{user_setting.ftd}} (or their equivalent **fam** module), in the **nest/pages** directory, and run it as a nest page. If the "pid" variable is not given, the default routing scheme will fallback to the page ID **home**. This is configurable as well. @note AJAX functions are routed through the variable 'aid' and widget AJAX hosts are routed through the 'wid' variable. Theese routing rules cannot be overridden, while a special class called Router can be provided to change the rules used to determine the page served by Nest. @section ov_framing Framing Sites, and especially web applications, are usually providing a common code that is consistent throughout all the areas, which sourrounds the web site. This is called __framing__. Nest doesn't force the users to use a particular framing scheme. For example, they may use the standard falcon {{include()}} function to include dynamically header and footer elements, or they may consistently {{load}} a set of site-specific function which include framing facilities. We'll illustrate some of this mechanisms later on in this document, However, Nest provides a ready-to-use framing mechanism that fits most of the common needs of web applications, and this is covered by this quickstart. When the @a Nest.frame property is set to a function, then Nest will load the page that the routing system required and render it separately. The output of the rendered page can be retrieved through the @a Nest.content method. A frame function can also be used to load services that are common through all the pages sharing that frame. For example, the following code can be placed on the entry point script to provide the same headers and footers for all the pages of a site: @code // this is index.fal load nest Nest.frame = function() > 'My site' > '

    My site starts here...


    ' > Nest.content() > '

    And this was My Site! ...

    ' end Nest.route() @endcode The function set in @a Nest.frame may be also a direct __include__ that loads the frame page. The location of those pages is always relative to the entry point. For example, to externalize the frame in another module, it is possible to write: @code // this is index.fal load nest Nest.frame = .[include "frame.ftd"] Nest.route() @endcode Then, {{frame.ftd}} would be beside index.fal, and would look like the following: @code My site

    My site starts here...



    And this was My Site! ...

    @endcode Framing can be disabled on specific pages adding the Falcon attibute @b nest_frame as false; for example: @code

    Unframed page

    This page won't be framed, even if nest has a site-wide frame

    @endcode @note Unframing .fal and .ftd files is useful to build simple AJAX based sites. @section ov_services Services Nest modular web application composition is based on the concept of __services__, that are modules performing specific tasks and then, optionally, being rendered on the page shown by the browser. */ frameworks/nest/docs/quickstart.fd000066400000000000000000000310151176363201700176520ustar00rootroot00000000000000/*# @page quickstart Quickstart -- turning the engine on. @after overview Supposing you have WOPI in place, and that you have configured your WOPI to find the entry point of your application (or your manually access it), we start from the __index.fal__ file in the top directory of the configured site. As a framework, Nest becomes part of your application (or, we may say, your application becomes part of Nest); as such, you have to declare @code load nest @endcode at the very start of your entry point script. A minimal but complete entry point may look like: @code // index.fal load nest // Tell nest to start. Nest.route() @endcode @note A minimal but complete (for our purposes) way to serve the test sites in this guide is firing up falhttpd with the following command: @code $ falhttpd -h -p 8080 -t 600 @endcode Route will tell nest to get the page that the overall status of the connection requires; by default, a new incoming user is served by the page called __home__. So we need a {{home.ftd}} or {{home.fal}} script under {{nest/pages}}. For example, using a ftd: @code My first Nest site

    Welcome!

    Welcome to my first Nest Site. I hope you enjoy it,

    Running with Falcon

    @endcode Or if you prefer a more structured approach, we can use a falcon script: @code // this is head.fal > ' My first Nest site

    Welcome!

    Welcome to my first Nest Site. I hope you enjoy it,

    ' version = ".".merge(vmVersionInfo()) > @'

    Running with Falcon $version

    ' > "\n\n" // this was head.fal @endcode Or even, you may chose to use a __very__ structured approach through the htmaker module. @code // this is head.fal import from htmaker in ht doc = ht.TransitionalFrame() doc.html.head.add( ht.Title( "My first Nest site" ) ) doc.html.body.add( ht.H1( "Welcome!" ), ht.P(" Welcome to my first Nest Site. I hope you enjoy it" ), ht.P("Running Falcon version " + ".".merge(vmVersionInfo()) ) ) > doc.render() @endcode @section qs_service Introducing services. As a first example of a service, we'll introduce the Menu service, which has the role of generating a configurable menu that can be shown on different pages. First, let's create a site structure for our test; make the following directories: @code menus/ nest/ pages/ hooks/ srv/ Menu/ @endcode We'll need to place an entry point for our application under **menus**; "index.fal" is the standard name. For this sample we'll add some debugging facilities that we'll later remove. @code load nest // debugging directives Nest.log_level = 3 // (1) Nest.debug = true // (2) Nest.pcheck = true // (3) // Nest site configuration Nest.stylesheets += "nest.css" Nest.frame = .[ include "frame.ftd" ] // (4) Nest.addHook( "TableCheckUser" ) // start the engine Nest.route() @endcode The debug directives tell Nest to perform low level logging (1) and also send the log output to the user after the page is generated (2) The directive at (3) instructs Nest modules to perform extra verbose checks on their configuration variables to catch for common mistakes in early site development stages. All this directives should be removed from a production server; it may be interesting to instruct Nest to log at level 1 (warn) or 2 (info) in case the site needs to be monitored for suspect activity. On some security sensible site, it is advisable to set the log level to 1, where minimal and critical information on Nest activity is sent to the web server for recording. The other instructions are setup directives; the most important one, at the moment, is (4), @a Nest.frame. It points to a function that orders to include the frame that will be valid for all the pages of the site (or, at least, all the pages accessed through this entry point). The frame is a ftd file as follows. @code Nest test suite - Menu service


    To login, enter "user" / "user".

    Nest test suite.

    @endcode Notice the @a Nest.headings call on the headings section. This allows Nest to fill the target page with headings suggested in the entry point (notice the @a Nest.stylesheets assignment in index.fal) or required by modules that may want to publish some meta information, stylesheet, css code or client-side script on the target page. The two @a Nest.service calls load the @a Login and @a Menu service. We'll diffusely talk about the Login service throughout the guide; let's concentrate on the Menu. Downwards, you can see the @a Menu.render call where the main menu should appare. Then, the frame includes the specific page through the @a Nest.content call. Finally, notice the usage of the Nest.auth_level variable do show conditional parts of the frame to the visitor. The variable is set through the Login service, and is available to the whole application as @a Nest.auth_level. The Login service can be configured so that your user storage system doesn't need to understand Nest authorization levels, and you can publish some extra authorization policies to the application through more sophisticated mechanisms (provided by Nest). This variable is a facility that can be used to rapidly prototypes simple web sites. @subsection qs_login_hooks Hook for the Login srvice To clear the ground from the Login service, and also introduce a concept we'll be using later on, we now show how to configure it to serve a simple web site. The Login service invokes a @a Hook called "check_user", passing the userID and the password to it in two parameters, and expects a pair of values in return in case of authorization granted: the @a Nest.AL authorization level (a number in 0-100), and an optional user-specific data that can be used by the web application for different reasons (i.e. displaying its real name, enforcing finer authorization policies and so on). You may write an Hook to serve the Login service matching your need and data structures, but Nest provides two ready-made hooks called TableCheckUser and DBCheckUser that can do the job with just a little configuration. This time, we use the TableCheckUser, writing the following file in the **nest/dm/TableCheckUser/config.fal** file in the site-specific nest directory. @code // Configuration for a TableCheckUser import from nest.hooks.TableCheckUser in tab users = [ "admin" => tab.User( "admin", Nest.AL.ADMIN, "Administrator" ), "staff" => tab.User( "staff", Nest.AL.STAFF, "Staff member" ), "user" => tab.User( "user", Nest.AL.USER, "Generic user" ) ] @endcode The user-id is the key of the @b users dictionary, while the @a password is the first parameter of the @b TableCheckUser.User class instance. There are more configuration options, as the ability to use @b hash function to check the incoming password against pre-hashed passwords stored in the table, but they are beyond the scope of this quikstart. @subsection qs_menuconf The configuration for the menu service. The menu is configured through an array of MenuItems, a class found in the menu service module. In our case, we'll setup some simple page: @code // Configuration for the Menu service import MenuItem from nest.srv.Menu as MenuItem items = .[ MenuItem( "Home", "nest:home" ) MenuItem( "Account settings", "nest:account", Nest.AL.USER, .[ MenuItem( "Change password", "nest:acc_password" ) MenuItem( "Preferences", "nest:acc_preferences" ) ] ) MenuItem( ^+ "Plugins", nil, Nest.AL.USER ) MenuItem( "Contacts", "nest:contacts" ) MenuItem( "Go to falcon", ">http://www.falconpl.org" ) ] @endcode The first parameter is the text that should appare, the second is the page ID or link target of the menuy voice, and the third is an optional minimal authorization level that must be granted to display the voice. An optional fourth parameter is a sub-menu. There isn't any built-in nesting limit, but web sites rarely use more than 2 levels for readability; in case the need arises, other menus (as navigation menu or sidebars) are added, and we'll se an example of that later on. The special "nest:" scheme in the target parameter indicates that the target is a page in the site; it's equivalent to using @a Nest.pageLink on the following page ID. The ">" marker in the last element indicates that the link generated by this entity should open in a new window (or navigation tab), and may be applied also to "nest:" schemes. Also, notice the "Plugin" menu; we're adding that to show how on-the-fly installed modules may add a menu item through a simple procedure. Notice that it's name is marked as __out of band__ (**^+** falcon operator); this instructs the Nest system not to display that menu unless it has some sub-items. @section qs_pages The pages It's time to setup the pages. They are included as samples; we show here the most interesting one: this page adds a menu item calling the appropriate service function. @code https://www.microsoft.com/protect/fraud/passwords/checker.aspx"), "Plugins") ?>

    Change password

    Sorry, this is only a test, and we're not using a database.

    So, in short, this is just a fake page

    @endcode The @b add_menu emit ask the menu service to add an item that is not in the configuration. An extra optional parameter indicates the parent of the new node, and may reference also submenu items separating them with dot, as i.e. "Menu 1.Menu 2...." @section qs_menu_instance Service instances Another useful feature of Falcon is that of providing the ability to create multiple instances of the same service. So, we may have more menus per page. For example, the "preferences" page has the following structure: @code @endcode @subsection serivce_skin_alt Alternative Skins It is legal to change the a skin value explicitly during the lifetime of a service. So, a service may load different skins to pick the most adequate one to render itself. For example, services requiring confirm after a request, may alter their skin with the following code: @code class TheService( inst ) from Service( inst ) ... skin_confirm = ServiceVar( b.SkinBinder ) ... function run() ... if req == "delete" // substitute the normal skin with a confirm request. self.skin.value = self.skin_confirm.value end end ... end @endcode @subsection serivce_skin_dyn Dynamic skin loading. Instead of creating a skin service variable, it is possible to use the dynamic skin loader to load an adequate skin at runtime. The @a SkinBinder.getSubSkin method can be directly called to search dynamically for a skin module with an arbitrary name. For exmple, the code in the previous section may be rewritten as: @code class TheService( inst ) from Service( inst ) ... function run() ... if req == "delete" // substitute the normal skin with a confirm request. self.skin.value = b.SkinBinder( "skin_confirm", self ) end end ... end @endcode @section service_devel_life Life cycle of a service Services are initialized as normal classes. Their @b init method gets called right after filling the properties and before service startup, so it may be a good place where prepare the service structure variables. Right after, bindings are resolved and service variables gets bound to their value. After that, the service gets a chanche to check its status on the @a Service.startup callback. If something vital is wrong, the service may raise a @a NestError to stop the genration of the page. During the page generation, the service may be marked for rendering calling the @a Service.render method. This will tell Nest that the service will place some code to be seen by the user at a later moment. After all the page is generated and all the services are read, the @a Service.run() callback of every load service is called (in strict load order). When all the services are run, the @a Service.perform_render method is called. If the service doesn't want or need to be rendered, it may just override this method with a method doing nothing, or it may have its own rendering routine (directly printing the content that should be placed on the point where the @a Service.render() placeholder was left. The default behavior is that of - checking for the service authorization, and returning if @a Service.allowed returns false. - checking for the css_id and css_class variables, setting css_id to the name of the service if not previously set. - calling the skin if it's set to a callable value. Finally the finalize callback is called after the page has been generated, but before session variables are saved. This gives the service a last chance to save persistent data or clean held resources after the rendering step. Resuming, the service life cycle consists of: # initialization # variable binding # call of @a Service.startup # page generation and @a Service.render invocations. # call of @a Service.run callback in service load order # Rendering process via @a Service.perform_render # call of @a Service.finalize callback */ frameworks/nest/docs/widgets.fd000066400000000000000000000271411176363201700171330ustar00rootroot00000000000000/*# @page widgets Nest AJAX enabled client-side widgets. Nest provides an integrated AJAX visual framework that goes under the name of @i Nest @i Widgets. Nest Widgets, can be organized much like GUI widgets being structured into hierarcies and rendered on the final page. Other than abstracting the task of creating visual interactive elements on the target page, Nest Widgets offer support for two specific needs: - AJAX hosting (automatic server-side reflection of the widgets). - Local inter-widget messaging. While most nest wigets come ready to use, a full exploting of them requires to write some Javascript routines (or anyhow server side code) that are needed to integrate them in the browser. While offering limited support for visual effects, Nest is mostly client-side agnostic, and provides support for integrating any external client-side Javascript framework. In fact, all the widgets can be configure to be under the domain of a foreign Javascript library (this usually happens setting their 'class' HTML property), or to invoke foreign Javascript code from the AJAX host or local callback points. The base class of the Widget system is the @a nest.widget.Widget class. @note To load a widget in a document it is possible to use the standard Falcon @b import directive; if the widget also comply to the required interface and naming conventions, it is possible to use the @a Nest.widget method. Notice that widgets acting as AJAX hosts (see below) are required to comply with this conventions, while widgets not exposing an AJAX host interface can simply be placed in any reachable location as loaded as normal Falcon modules. @section ajax_hosting AJAX Widget Hosting Nest widgets support AJAX hosting, that is, a direct callback to themselves via AJAX. In other words, they can receive requests, and send orders to their counterpart on the user browser page. Each HTTP transaction is a standalone operation, so the status of the widget cannot be simply changed to be the same when the remote client asks for the server-side widget to be operate. However, accessing a widget via Nest ensures that the session variables and the special Nest session support system are in place when the widget is re-created to serve the ajax request. @note AJAX hosts \b must be exposed in a widget module (a file under nest/widget named after the exposed class). Also, they \b must provide a @b tag, that is, they must be real possibly visible entities (or at least, an invisible empty "div"), and not just "widget groups". A widget willing to act as an AJAX host must declare it by setting the isAJAXHost property to true. By doing this, it will become responsible to process AJAX requests generated also by child widgets (the ID of the sender child widget will be provided as a parameter of the AJAX handler). It is also possible to subclass and override the subclass AJAX hosting behavior mechanism by setting its isAJAXHost property to valse from within the init method of the new child, and setting the child's own isAJAXHost to true. In this way, the new derived class might pre-process any message originally directed to the base class, and then invoke the parent's AJAX handler method. To help re-create the widgets so that they can be rebuilt upon remote AJAX request, it is necessary to provide an initInfos array, which contains all the parameters used to create this widget, in proper order, except the widget ID, which is mandatory and \b must always be provided and appare as the first parameter of the widget class declaration. For example, a simple AJAX host would look like this: @code class ChildHost( id, name, surname, somedata ) from Widget( id ) isAJAXHost = true initInfos = [ name, surname, somedata ] init ... some init code ... end ... function AJAX( params ) ... return [ message1, message2 ... ] end end @endcode @note The initInfos are stored on the remote side side as JSON data; be sure that the data used to initialize the widget can be expressed as a JSON entity (strings, numbers, true/false and nil values, as well as arrays and dictionaries of those values, are safe). @section ajax_requests Automatic AJAX requests. The class AJAXRequest embeds an automatic invocation to the innermost AJAX host which is parent of the entity implementing the requset. They have also the ability to extract current information about the status of the document being presented to the user and pack it into JSON for the AJAX host to receive it. To generate automatically AJAX requests as method of this widget, it is simply necessary to populate the \b ajaxReqs property with a dictionary of methodName => AJAXRequest instance. Particularly interesting is the case of the method name being the same as a Javascript event name (i.e. onClick). For instance: @code class SubWid(id) from Widget(id) ajaxReqs = [ "onClick"=> AJAXRequest( ["../tb/value"] ), "invoke_me"=> AJAXRequest( nil ) ] ... end @endcode If the widget in the example is a AJAX host or added as a child of a host, click action will cause the JS side of the script to invoke immediately the Nest AJAX counterpart. The "invoke_me" Javascript method is available for any other Javascript on the page to be invoked. The dictionary received by the AJAX host method will contain the following members: - "id" => the full ID of the widget that generated the message (should be a child of the host, or the host itself). - "msg" => the message that was invoked (in this case, either "onClick" or "invoke_me"). - "widget" => the AJAX host widget that should receive this message (it's used by Nest to re-create the widget). - "infos" => dictionary of automatic informations (see below) @subsection ajax_infos AJAX automatic information retrieval As specified, AJAX request can automatically create the code to read and pack current values in the remote document for the AJAX host to receive them. The example in the section reads the "value" JS proprty of the sibling "tb" child widget, (notice the ../ in front of the name). The informations so obtained are stored in the \b "infos" element of the dictionary that is provided to the AJAX method of the host. The "infos" element is a dictionary itself, its keys being the (relative) ID of the widgets where the information was taken, and the values being a string representing the current value of that property in the document. In the above example, it shall be "tb.value". Notice that the informations are extracted using a DOM path locator, while they are provided to the target host widget in the usual dot-accessor notation. @subsection ajax_messages AJAX actions and message handlers The return value of the AJAX callback method is a list of "actions" to perform. This class provides support for some basic actions that can be performed on remote widgets, but it is possible to create custom mesages that can be received by any widget (or Javascript code) in the target page. A return message is a dictionary that can contain different elements depending on the message type. The following widget methods helps creating messages for the receiving page. - \b msgSetProperty creates a message which sends a request to the remote page to change a property of the given widget with the value that is passed. - \b msgInvoke creates a message requiring the remote page to invoke a method, optionally receiving some arbitrary as a single parameter. - \b msgGeneric creates a generic message that can be received by one or more "listeners" in the target page; the receivers can be both nest Widgets on the Javascript side, or arbitrary Javascript code. Listeners widgets can register to AJAX generic messages by declaring a dictionary in the ajaxMessages property; the key of the dictionary is the name of the message they respond to, and the value is a full javascript function receiving a single parameter (which is the data passed as extra parameter to msgGeneric on the Falcon server side). Javascript code can register to generic AJAX reply messages invoking the Nest.listenAJAX( message, object, method ) function in the Nest.js support script. The object can be null, so that a simple function receives the message. An AJAX generic message can be received at most by one receiver (but it is possible to propagate the information locally in the target page via the Nest.listen extension in Javascript). See the following example: @code class SomeContainer( id ) from wid.Widget( id ) ajaxMessages = [ 'changeMe' => "function(obj) { this.value = obj.value; }" ] myarea = MyArea() mybutton = mybutton() init self.addChild( self.myarea ) self.addChild( self.mybutton ) end function AJAX( params ) return [ self.myarea.msgSetProperty( "innerHTML", "The new text" ), self.mybutton.msgInvoke( "aMethodOfButton", ["..."=>"..."] ), Widget.msgGeneric( "changeMe", ["value" => "My value is changed." ] ) ] end @endcode As the generic message is not targetted to a specific widget (but in the example, we're receiving it from the same AJAX host), it is possible to use the msgGeneric method statically from the Widget class. The msgSetProperty and msgInvoke methods are specific of their target widget, so they require the proper target to be specified. @note To receive callbacks from a msgInvoke AJAX invocation, do not use ajaxMessages, but simply provide a method in jsMethods. @section local_page_messages Local page widget messaging Widgets rendered on the remote browser page can communicate by listening messages generated the Javascript side method "Nest.message". The method takes three parameters: the object generating the message, a logical name or id for the message and an arbitrary parameter that can be any Javascript object. To listen for all the messages that a Nest widget can emit, the function Nest.listen must be invoked; this method is structured so that it can be invoked in any part of the javascript source, making it easier to create listeners for widgets still not completely created. The method takes three parameters: the emitter object, the id of the listener object and a function receiving three parameters, which is called back when Nest.message is invoked. A listener callback can be created automatically via the jsListeners Falcon-side widget member: @code x = Button( ... ) y = Button( ... ) class z from Widget(...) jsListeners = [ x => "function( emitter, message_id, data ) {....}", // 1 y => "function( emitter, message_id, data ) {....}" // 2 ] end @endcode The remote "z" widget will receive a method call each time x or y will emit a message by calling Nest.message. The function 1 would be called after x, and 2 after y. @note It is extremely difficult to know the final ID and entity of a widget after all the renderings are completed (also because the widget being created might be parented after its full creation into something else); as such, invoking methods from other widgets directly from a server side Nest widget is impractical. This indirect mechanism allows to create proper callback methods after all the structure of the widgets in the page is setup. */ frameworks/nest/nest.fal000066400000000000000000001300401176363201700156500ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: nest.fal Main file driving the engine. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 25 Jun 2010 10:52:46 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ const _version = "2.0" directive lang = "en_US" import NestError from self.error as NestError import Hook from self.hook in hook_module import from self.utils import from compiler import from json const pages_dir = "pages" const widget_dir = "widgets" const hooks_dir = "hooks" const ajax_dir = "ajax" const mod_dir = "mod" const res_dir = "res" /*# Basic Nest router class. This class has the role of - determining what page ID is implied in the web request currently being served. - building an URI that will include the current page ID. */ class BasicRouter //# Returns the page ID implied in the current web request function route() return Request.getField("pid", "home") end //# Returns a string that may be used to reach the given page function routingURI( baseUri, pid ) if not pid: pid = "home" return baseUri + "?pid=" + URI.encode( pid ) end end /*# Basic Nest filter for input variables. This class is meant to provide Nest with input coming from the web requests. An instance of this class must be set as the @a Nest.filter property; Nest will call the @b filter method of the instance at proper time, and store its return value into @a Nest.inputVars. This base class just provides nest with the sum of Request.gets and Request.post dictionaries, effectively feeding all the standard input fields into @a Nest.inputVars. */ class BasicFilter //# Returns a dictionary of name/value variable pairs. function filter() return Request.gets + Request.posts end end /*# Nest basic session manager. Session managers have the role of providing Nest with persistent storage for variables related to a specific remote user connection, thus enabling the sateless-http requests to be seen as "sessions". The basic session manager uses the WOPI session system as a backend for persistent storage. More sophisticated application-specific session managers may be written to store sessions on serialized files, databases, memory mapped files and so on. */ class BasicSessionManager _session = nil /*# Restores a previously existing session. @return a pair of session data + session status. The function returns an array of two values; the first one is the dictionary of session variables and their own values. The second is true if the session expired. A new empty session is represented by the return values [ [=>], false], while an expired session will be returned as [ [=>], true]. */ function restore() if Request.hasSession() try self._session = Request.getSession() if "_Nest" in self._session // we have a living session sd = self._session["_Nest"] se = false else // a new session was generated by getSession() sd = [=>] se = true end catch WopiError sd = [=>] se = true end return [sd, se] end // new and not expired return [ [=>], false] end /*# Saves the session data. @param sessionData Dictionary of session variables with their values to be saved. */ function save( sessionData ) if not Request.hasSession() self._session = Request.getSession() end //inspect( Reply ) self._session["_Nest"] = sessionData end end /*# Basic Nest Logger. @param channel a Stream where to send the log information. Logger class is used to render nest log information to. The basic logger writes its output to a given stream. */ class BasicLogger( channel ) channel = channel _ts = TimeStamp() function log( mode, caller, data ) self._ts.currentTime() if caller.startsWith( "Method" ) caller = caller[caller.find(".")+1:] end self.channel.writeText( self._ts.toString() + " " + mode + " " + caller + "\t" + data +"\n" ) end end /*# Standard authorization levels. A lower authorization code means more priviledges. */ enum AuthLevel ADMIN = 0 STAFF = 10 USER = 20 VISITOR = 30 NONE = 100 end /*# Nest application framework. This object represents the core of the Nest system. It is mean to be part of the web application loading it, so it should be loaded with the directive @code load nest @endcode */ object Nest //=================================================================== // Public properties //=================================================================== /*# Nest application directory for current site. It's relative to the entrypoint script, and defaults to "./nest". */ site_nest_dir = "./nest" /*# Home of the nest installed hierarcy. It defaults to vmModulePath(), which is the path from where Falcon loaded this Nest module. */ nest_dir = nil /*# URI of the entry point script. If set to nil, in case of need the URI of the entry point script will be determined through the Request fields. */ self_uri = nil /*# Page ID of the currently served page */ pid = nil /*# Repeat the AuthLevel enumeration */ AL = AuthLevel /*# For now, use the max authlevel by default. */ auth_level = AuthLevel.ADMIN /*# User that has logged in */ auth_user = nil /*# Arbitrary data for logged in entity */ auth_data = nil /*# Name of the module used for error reporting. Nest uses the Module.render() method of a dynamic Nest module to report errors that have happened during the routing of a page. The error module delegated to show the error to the user is "ErrorReport" by default, but it can be changed, even dynamically, so to provide context-sensible errors. */ ereport_module = "ErrorReport" /*# Router. This object maps a raw web request into a page id. The property can be assigned by the user application to an arbitrary router, which is an object exposing a route() function. The function must return a page id (a string) if the router could determine where the request should be sent, or nil if the page ID cannot be determined. In this case, Nest raises an error and the execution terminates. The default router seeks a "pid" field first in the posts, then the gets fields of the Request. If the "pid" is not found, the router returns a "home" page id. */ router = BasicRouter /*# Filter for input variables (POST or GET key/value pairs). Filters the content of the get and post values and stores them in the @a Nest.inputVars field as a dictionary pairs. Cookies and session data are used by Nest and provided to the application via the @a Nest.sessionData property. However, the application can access them separately (nest uses a "Nest" namespace to save its own variables in cookies or session data). The filter object must expose a "filter" function, that returns the dictionar function should return a dictionary of variables then saved in the inputVars field. The basic (default) filter just stores all the GET and POST variables in the inputVars field (post variables having higer priority). */ filter = BasicFilter /*# Nest-specific session data. Contains data which must be retained across sessions. Services use it automatically, but it can be used also by the user application at will. */ sessionData = [=>] /*# True when we detect the session data to be exipred. */ sessionExpired = false /*# Manager for persistent variables. By default, it's pointing to a BasicSessionManager object, which implements the stanard WOPI session management. */ sessionManager = BasicSessionManager() /*# Input variables as parsed and filtered. At the disposal of services and user applications. */ inputVars = [=>] /*# Javascripts to be loaded in html headers */ extscripts = [] /*# Stylesheets to be loaded in html headers */ stylesheets = [] /*# Inline scripts to be put before body. */ scripts = [] /*# Inline CSS texts to be put before body. */ styles = [] /*# Javascript functions to be executed after all the widgets inits */ onLoad = [] /*# Title for the page. This will be renedered as \\<?=Nest.title?>\ when Nest.headings are called. */ title = nil /*# Basic logger */ logger = nil /*# Debug mode? */ debug = false //# Instructs the modules to perform checks on service parameters. False by default. pcheck = false /*# Framing. When the @b frame property is set to a function, then Nest will load the page that the routing system required and render it separately. The output of the rendered page will be retrieved through the @a Nest.content method; then, the function stored in @b frame will be called, and it will just need to print @a Nest.content variable at the desired position. Interesting functions can be: @code Nest.frame = .[ include "some_file" ] Nest.frame = .[ include Nest.pageLink("pid-of-frame") ] @endcode The function should produce an output that will be captured by Nest and then rendered. */ frame = nil /*# Log level. Can be: - 0: none - 1: warning - 2: info - 3: debug */ log_level = 0 //# If true, widget declared @b css properties are rendered in-line genWidgetStyles = true //=================================================================== // Private properties //=================================================================== _comp = compiler.Compiler() // Used to send logs to output during debug. _debugStream = nil // Function registered to be execued at startup. _startupFunctions = nil // dictionary of ambient variables _ambient = [=>] // Content set later on during the page work _delayedSlices = [=>] // Rendered content of the page, each being a strtring or a function to be called back. _contentSlices = nil _contentProvider = nil // Current stream where the page is generating its output _pageStream = nil _jsComponents = [ "base" => "nest.js", "fx" => "nest_fx.js" ] // widgets subscribed for rendering. _widgets = nil // Styles to be applied by class _classStyles = [=>] // Hook functions invoked by direct emit("...") requests _hooks = [=>] // Modules _modules = [=>] _menus = [=>] _menu_registar = [=>] //=================================================================== // Public interface //=================================================================== init self.nest_dir = Path(vmModulePath()).fulloc + "/nest" self._comp.launchAtLink = true self._comp.sourceEncoding = 'utf-8' end /*# Nest entry point. @optparam pid If given, force the entry point skipping the router. This method routes a web request down into the nest sytem. */ function route( pid ) self.logi( "Routing started" ) // 1. Determine PID if not pid if (rid = Request.getField( "r", nil ) ) self.logi( "Selecting resource " + rid ) self.selector_resource( rid ) return end aid = Request.getField( "a", nil ) wid = Request.getField( "w", nil ) if not aid and not wid pid = self.router.route( self ) end end if pid == nil and aid == nil and wid == nil // Todo: get the proper error. self.logw( "No route found" ) self._manageError( NestError( NestError.no_route ), "" ) if self.debug: self._displayDebug() return end self.pid = pid // 2. Load variables self._loadSession() self.logi( "Filtering input variables" ) self.inputVars = self.filter.filter( self ) if self.log_level >= 3: self.logd( "Input vars: " + self.inputVars.describe() ) // 3. Enter config phase try self._startup() catch Error in e self._manageError(e) if self.debug: self._displayDebug() return end // 4. Read the pages if pid self.selector( pid ) elif aid self.selector_ajax( aid ) else self.selector_ajax_widget( wid ) end end /*# Nest redirect function. @param url An URL or Page ID where to redirect the page. @optparam values A dictionary of values to be added to the page id. This method kills the current processing of the Nest page, and invokes WOPI Reply.redirect request. The redirection request will be sent as an HTTP header, and all the parts of the document generated up to date will be discarded. @note If URL begins with a "!", then is inteded as a nest PID. */ function redirect( url, values ) if url.startsWith('!') url = self.pageLink(url[1:], values) end Reply.redirect(url) Reply.commit() exit(0) end /*# Adds a startup function. @param func a function that must be invoked at startup. This is the ideal place to put code that must be always executed. Normally, database managers and login services are configured here. */ function addStartup( func ) if self._startupFunctions == nil self._startupFunctions = [func] else self._startupFunctions += func end end /*# AJAX function selector. @param aid The ID of the AJAX function or widget. @optparam method A method for an AJAX widget. */ function selector_ajax( aid ) ajax_file = utils.findFileInDirs( [self.site_nest_dir + "/" + ajax_dir, self.nest_dir + "/" + ajax_dir ], aid, ["fal", "fam"] ) if( ajax_file ) self._selector_ajax( aid, ajax_file ) else raise NestError( NestError.page_notfound, "", aid ) end end /** Selects a resource */ function selector_resource( rid ) if self.log_level >= 3: self.logd( "Checking resourece for RID: " + rid ) dirs = [self.site_nest_dir + "/" + res_dir, self.nest_dir + "/" + res_dir ] res_file = utils.findFileInDirs( dirs, rid ) if( res_file ) // shall we really send it? if utils.checkModifiedSince( res_file ): return if res_file[-3:] == ".js" Reply.ctype( "application","javascript" ) text = true elif ( ext = res_file[-4:] ) == ".png" Reply.ctype( "image","png" ) elif ext == ".jpg" Reply.ctype( "image","jpg" ) elif ext == ".gif" Reply.ctype( "image","gif" ) else Reply.ctype( "octect","stream" ) end Reply.setHeader( "Content-disposition", @"inline; filename=\"$(rid)\"" ) Reply.setHeader( "Cache-Control", "public,max-age=0" ) Reply.setHeader( "Pragma" ) stdOut().write( readURI( res_file, text ? "utf-8" : "C" ) ) else utils.exitReply( 404, "File not found", "The request " + Request.uri + " is pointing to a resource that couldn't be found") end end /*# Page function or method selector. Call this function to get the main ID of the page to be displayed. This is the entry point of a Nest web application. */ function selector( pid ) if not pid: pid = "home" p = Path() p.fulloc = self.site_nest_dir + "/" + pages_dir p.file = pid extfunc = .[ .[ 'fal' self._selector_script ] .[ 'ftd' self._selector_script ] .[ 'fam' self._selector_script ] .[ 'html' self._selector_html ] .[ 'htm' self._selector_html ] ] // see if there is a file matching our page in the directory. for ext, func in extfunc p.extension = ext pagefile = p.path if fileType( pagefile ) == FileStat.NORMAL func( pagefile ) return end end raise NestError( NestError.page_notfound, "", pid ) end /*# Declare a user. @param uid An identifier for the logged in user. @param level An authentication level. @param data Extra data that is to be associated to the user. @optparam save If true, automatically save the parameters to the session. This method advertises the fact that the remote browsing entity has been recognized as a user. If the @b param save is true, the auth_user, auth_level and auth_data are automatically stored in the session data (and then, automatically retrieved when the session is re-accessed). Notice that doing so might cause secuirity breaches or be plainly impossible if the auth_data variable is containing data that cannot be stored to the session. In general, it's better to re-authorize the remote user on the base of partially negotiated session information. */ function loggedIn( uid, level, data, save ) self.auth_user = uid self.auth_level = level self.auth_data = data if save self.sessionData["auth_user"] = uid self.sessionData["auth_level"] = level self.sessionData["auth_data"] = data end end /*# Invoke an ambient variable. Ambient variables are service variables which are immediately repeated to all the system. */ function ambient( aname, val ) if aname in self._ambient s = self._ambient[aname] elif val != nil s = val self._ambient[aname] = s end return s end /*# Invoke a widget class @param name The logical name of the widget class to be invoked. @return A @a Widget class. Theoretically, the widget may be located anywhere it can be reached via import/load and then just intantiated; but this might make hard to impossible to create project-specific widget overrides or may require to dirty the code-out-of-site model of Nest. So, this method is just a facility to load the desired class as a Nest aware plug-in. The widget file must contain a class named exactly as the file (extension excluded), which is the class that will be returned by this method. */ function widget( name ) wid = self._internal_module( name, widget_dir ); if not wid: raise NestError( NestError.mod_notfound, "", name ) return wid end /*# Invoke a module. @param name The logical name of the module class to be invoked. @return A @a Module instance. A @a nest.Module class instance is a dynamic plugin that can be loaded on demand when necessary. This method is actually a simple helper that searches for the module in standard "mod" directories. If already loaded, the previously created instance is returned. When loaded, a [modname]_cfg.fal file is searched in the modules directory as well. If found, it is loaded and each global variable in the _cfg module is set in a pseoudo-object that is then passed to the onConfig() methdod of the module instance. */ function module( name ) if name in self._modules return self._modules[name] end modc = self._internal_module( name, mod_dir ); if not modc: raise NestError( NestError.mod_notfound, "", name ) mod = modc() self.loadConfig( mod ) self._modules[name] = mod return mod end /*# Loads a module class without creating an instance. @param name The logical name of the module class to be invoked. @return A @a Module class. */ function moduleClass( name ) modc = self._internal_module( name, mod_dir ); if not modc: raise NestError( NestError.mod_notfound, "", name ) return modc end function loadConfig( entity, name ) modName = entity.className() if name: modName += "_" + name modName += "_cfg" w_file = utils.findFileInDirs( .[ self.site_nest_dir + "/" + mod_dir self.site_nest_dir + "/" + widget_dir self.nest_dir + "/" + mod_dir self.nest_dir + "/" + widget_dir ], modName, ["fal", "fam"] ) if( w_file ) // by default, launchAtLink is true. try privAlias = modName + "-" + random(1,1000000) self._comp.launchAtLink = true confMod = self._comp.loadFile( w_file, privAlias ) vars = [=>] for var in confMod.globals() value = confMod.get( var ) if var.startsWith("_") continue end vars[var] = value end entity.onConfig( bless(vars) ) catch in e nerr = NestError( NestError.econfig, "" ) nerr.boxed = e raise nerr end else self.logi( "Configuration module not found " + modName ) if name raise NestError( NestError.config_notfound, "", modName ) end // else, we simply didn't want any configuration end end function subscribeWidget( widget ) if not self._widgets: self._widgets = [] self._widgets += widget end function selector_ajax_widget( wid ) widgetClass = self.widget( wid ) try // init the widget. params = Request.getField( "params", nil ) if params: params = json.JSONdecode( params ) if params and "init" in params initfunc = [widgetClass] + params["init"] widget = initfunc() else widget = widgetClass() end // Execute the method. if not widget provides AJAX ctx = [ "error"=>"-4", "errorDesc"=>"Widget is not exposing the AJAX entry point: " + wid] else ctx = widget.AJAX( params ) if typeId( ctx ) != DictionaryType and typeId( ctx ) != ArrayType ctx = [ "error"=>"-2", "errorDesc"=>"AJAX function \"" + wid + ".AJAX\" didn't return a dicitonary" ] end end self._saveSession() catch in e ctx = [ "error"=>"-3", "errorDesc"=>"Uncaught error: " + e.toString() ] end // Ok send the output >> json.JSONencode(ctx) if self.debug self._displayDebug() end end /*# Produces nest automatic headings. */ function headings() Nest.logi( "Placing render mark for headers" ) // save the rendering up to date self._contentSlices += self._pageStream.closeToString() // prepare for later rendering at this point self._contentSlices += self._render_headings // generate a new stream for the rest of the page. self._pageStream = StringStream() stdOut( self._pageStream ) end function _render_headings() str = "" if self.title str += @"$(self.title)\n" end for item in self.extscripts str += @"\n" end for item in self.stylesheets str += @"\n" end for item in self.styles str += "\n" end for item in self.scripts str += "\n" end // Eventually write the page-specific styles if self._widgets and self.genWidgetStyles self.logi( "Rendering widgets styles" ) // first to all the setup... styles = "" classCSS = [=>] empty = [=>] for item in self._widgets // first, the CSS per item self.logi( "Rendering styles for widget " + item.id ) if item.idStyles item_id = "#" + strReplace( item.getFullID(), ".", "\\." ) styles += self._makeCSSforID( item_id, item.idStyles, item ) end //then the CSS for the classes classStyles = item.pickClassStyles( self._classStyles ) if classStyles id = item.CSSClassID() if id notin classCSS styles += self._makeCSSforID( "." + id, classStyles, item ) classCSS[id] = 1 end end end if styles str += "\n" end end return str end /*# Create a link to this or another page. @optparam pid The pageID of the desired page; leave nil to use the current pid. @optparam vars A dictionary of variables to be passed to the given page. */ function pageLink( pid, vars ) return self.linkVars( self.router.routingURI( self.getURI(), pid ? pid : self.pid ), vars ) end /*# Create a link to an ajax function. @param ajax The name of the Ajax function. @optparam vars A dictionary of variables to be passed to the given function. */ function ajaxLink( ajax, vars ) return self.linkVars( self.getURI() + "?a=" + ajax, vars ) end /*# Create a link to an internal resource file. @param rid Name of the resource to be served. */ function resLink( rid ) return self.getURI() + "?r=" + rid end /*# Create a link to this page */ function linkVars( link, vars ) base = v = nil // Base: the address, v: existing params d = [ => ] l = link.split("?") base = l[0] if l.len() > 1: v = l[1].trim() if v parms = strSplit( v, "&") for param in parms name, value = param.split( "=" ) d[name] = value end end if vars for key, value in vars: \ d[ URI.encode(key) ] = ^? value ? value:URI.encode(value.toString()) end v = "" for key, value in d if not value: continue v += key + "=" + value forfirst v = "?" + v end formiddle v += "&" end end return base + v end function actionLink( pid, cmd, cmdField ) v = self.router.routingURI( self.getURI(), pid ? pid : self.pid ) if cmd if "?" notin v: v += "?" if not cmdField: cmdField = "cmd" v += URI.encode(cmdField) + "=" + URI.encode(cmd) end return v end function eidLink( pid, cmd, cmdField ) v = self.actionLink( pid, cmd, cmdField ) if "?" notin v v += "?" else v += "&" end return v + "eid=$(eid)" end /*# Returns the URI confgiured for this site. If the site has been configured with an explicit callback uri, it is returned, otherwise the URI of the entry point script is determined looking at the Request WOPI fields. */ function getURI( scheme ) if self.self_uri: return self.self_uri if not scheme: scheme = "http" try return scheme +"://" + Request.headers["Host"] + Request.location catch AccessError return "/" end end /*# Calls a hook. @param The name of a previously registered hook @optparam ... Calls a previously registered @a Hook instance. The hook function should return an oob value to signal that the message has been handled, and stop from further propagation. */ function emit( name ) self.logi( "Calling hook " + name ) if name in self._hooks hook = self._hooks[name] else // try to load the hook mod = self._internal_module( name, hooks_dir ) if mod hook = mod() self._hooks[name] = hook end end if hook ret = passvp( hook.invoke ) self.logd( "Hook " + name + " returned " + ret.describe() ) else self.logw( "Can't find hook " + name ) end return ret end /*# Invoke the content of a frame page. @raise NestError if not invoked from a frame function, or if called more than once. If called inside a frame function, this will cause the routed page to be copied verbatim at the required position. @note The function returns nothing, so nothing should be printed out of it. The content page is simply copied verbatim in place of this declaration. */ function content() if not self._contentProvider NestError( NestError.not_in_frame, "" ) end // save the rendering up to date self._contentSlices += self._pageStream.closeToString() // generate a new stream for the rest of the page. self._pageStream = StringStream() stdOut( self._pageStream ) // includes the content page and proceeds with the rest of the framing. self._contentProvider() self._contentSlices += self._pageStream.closeToString() self._pageStream = StringStream() stdOut( self._pageStream ) // also, make sure this can't be done anymore self._contentProvider = nil end /*# Defers a text or a function. @param id A text ID for a later definition, or a function to be called later on. This methods places in the current position of the currently processed page a text that will be determined later on. The text to be displayed in this position can be set, either before or after this call, through the @a Nest.dtext method. The @b id parameter can be a function; in that case, it will be called during the page render step, and its return value will be placed in the current page position. @note If @a Next.dtext is never called to define the value of the required text @b id, then this call is ignored, and nothing is displayed in its place. */ function defer( id ) // save the rendering up to date self._contentSlices += self._pageStream.closeToString() if id.isCallable() self._contentSlices += id else dl = self._delayedSlices self._contentSlices += {=> if id in dl: return dl[id]; return "" } end // generate a new stream for the rest of the page. self._pageStream = StringStream() stdOut( self._pageStream ) end /*# Sets a deferred text value. @param id The id of the text. @param text The text that should be rendered on the place where the @a Nest.defer method is invoked. This method specifies what text should be rendered on the place where a placeholder has been left through the @a Nest.defer call. */ function dtext( id, text ) self._delayedSlices[id] = text end /*# Checks for the current page to be accessible, and eventually loads an alternate page. @param level The authorization level required to access this resource. @param pid_on_failure The Page ID of a resource to be loaded instead of this page. @return True if the current page is authorized, false if it's not allowed. This method should be called as the very first method of the routed page (or eventually of the routed frame, if the frame is security-sensible. The caller may simply use the returned value to switch into a security-insensible code branch, or it may provide a PID of a resource to be loaded in place of the currently processed page. In this latter case, the caller should return immediately, as nest will have routed the required resource. For example, home.ftd may look like @code

    Welcome user

    sensible things here...

    ,,, @endcode and unauthorized.ftd may simply be a warning message: @code

    Unauthorized

    Sorry, you shouldn't be here...

    @endcode */ function allowed( level, pid_on_failure ) if self.auth_level <= level: return true if pid_on_failure self.selector( pid_on_failure ) end return false end /** Remotely loads a standard javascript in the Nest environment */ function requireJS( component ) self.logi( "Requiring JS component " + component ) jsc = self._jsComponents if component notin jsc raise NestError( NestError.jscomponent_notfound, "", component ) end fname = jsc[ component ] if fname fname = self.resLink( fname ) self.logd( "Loading JS component " + fname ) self.extscripts += fname jsc[ component ] = nil end // else it was already loaded. end /*# Gets version/subversion of the Nest system. @return A version string in format major.minor */ function getVersion() return _version end /*# Register a menu item for dynamic menu instantation. @param menuName The socket (public) name of the menu where the item is to be registered. @param menuItem The item to be registered. This method allows to dynamically create new menu items when/if a target menu is created. */ function registerMenuItem( menuName, menuItem ) if menuName in self._menus self._menus[menuName].addChild( menuItem ) else if menuName notin self.menu_registar self._menu_registar = [menuItem] else self._menu_registar += menuItem end end end /*# Registers a public menu. This method is automatically called by the menu class during its initialization process. */ function registerMenu( menu ) if menu.socket in self._menu_registar for item in self._menu_registar[menu.socket] menu.addChild( item ) end end self._menus[ menu.socket ] = menu end //============================================================= // Style functions //============================================================= function addClassStyle( wcls, style ) if not self.genWidgetStyles self.logd( "Skipping style \"" + style.name + "\" as widget style generation is not active" ) return end self.logd( "Adding style \"" + style.name + "\" on class " + wcls ) dict = self._classStyles if wcls in dict dict[wcls] += style else dict[wcls] = [style] end end function getClassStyles( cls ) if cls in self._classStyles: return self._classStyles[cls] end function hasOneOfClassStyles( classes ) for cls in classes if cls in self._classStyles: return true end return false end //============================================================= // Logging functions //============================================================= function logw( data ) if self.log_level >= 1 if not self.logger: self._initLogger() self.logger.log( "WARN", fself.caller().toString(), data ) end end function logi( data ) if self.log_level >= 2 if not self.logger: self._initLogger() self.logger.log( "INFO", fself.caller().toString(), data ) end end function logd( data ) if self.log_level >= 3 if not self.logger: self._initLogger() self.logger.log( "DBG ", fself.caller().toString(), data ) end end //============================================================= // private part //============================================================= function _startup() self.logd("Calling onStartup functions" ) for func in self._startupFunctions func() end end function _initLogger() if self.debug self._debugStream = StringStream() self.logger = BasicLogger( self._debugStream ) else self.logger = BasicLogger( stdErr() ) end end function _internal_module( name, dir ) w_file = utils.findFileInDirs( [self.site_nest_dir + "/" + dir, self.nest_dir + "/" + dir ], name, ["fal", "fam"] ) if( w_file ) // by default, launchAtLink is true. page = self._comp.loadFile( w_file ) widget = page.get(name) if typeId( widget ) != ClassType raise NestError( NestError.no_mod_class, "", name ) end return widget else return nil end end function _selector_script( file ) // prepare the output stream self._pageStream = StringStream() old = stdOut( self._pageStream ) // prepare the content slices self._contentSlices = [] try self._comp.launchAtLink = false page = self._comp.loadFile( file ) self._comp.launchAtLink = true main = page.get("__main__") // should the page be framed? if self.frame.isCallable() attrs = page.attributes() if "nest_frame" in attrs and not attrs["nest_frame"] main() else self._contentProvider = main self.frame() end else main() end // render the slices requiring rendering. self._renderSlices() // render the closeScripts self._renderClosingScript() self._renderOnLoad() // generate the (last) content slice self._contentSlices += self._pageStream.closeToString() // save an eventual error for later self._saveSession() catch in e end // Return to standard output stdOut( old ) // Save what we have achieved up to date. ctx = self._packSlices() // do we have an error to manage? if e self._manageError( e, ctx ) else // Ok send the output >> ctx end if self.debug self._displayDebug() end end function _renderSlices() self.logi( "Rendering data slices" ) i = 0 cs = self._contentSlices slen = cs.len() while i < slen mth = cs[i] if mth.isCallable() if self.log_level >= 3: self.logd( "Calling slice " + mth ) cs[i] = mth() end ++i end end function _renderClosingScript() if self._widgets self.logi( "Rendering widgets setup scripts" ) onCreate = [] str = "\n" self._contentSlices += str end end function _renderOnLoad() if self.onLoad self.logi( "Rendering onLoad script requests" ) str = "\n" self._contentSlices += str end end function _packSlices() self.logi( "Packing data slices" ) str = "" for s in self._contentSlices str += s end return str end function _selector_html( file ) f = InputStream( file ) out = stdOut() r = "" while not f.eof() f.read( r, 4096 ) out.write( r ) end f.close() end function _selector_ajax( aid, file ) try // by default, launchAtLink is true. page = self._comp.loadFile( file ) ajax = page.get(aid) if not ajax.isCallable() ctx = [ "error"=>"-1", "errorDesc"=>"Module \"" + file + "\" is not defining '"+aid+"' as function." ] else // parameters are in Request.getField params = Request.getField( "params", nil ) if params and params != "{}": params = json.JSONdecode( params ) ctx = ajax( params ) if typeId( ctx ) != DictionaryType ctx = [ "error"=>"-2", "errorDesc"=>"AJAX function \"" + file + "\" didn't return a dicitonary" ] end end self._saveSession() catch in e ctx = [ "error"=>"-3", "errorDesc"=>"Uncaught error: " + e.toString()+ "\nDATA: " + params ] end // Ok send the output >> json.JSONencode(ctx) if self.debug self._displayDebug() end end function _onSessionVarChanged( sv, value ) self.logd( @"Session var changed: $(sv)=$value" ) self.sessionData[sv] = value end function _manageError( e, ctx ) try // force excluding bindings self.title = "Nest Framework Error" ereport = self.moduleClass( self.ereport_module )( e, ctx ) self.loadConfig( ereport ) > ereport.render() catch in f self._defErrorReport( e, ctx, f ) end end function _defErrorReport( e, ctx, f ) > ctx > "=" * 66 > "FATAL ERROR -- failed to load error report service, using default error reporting:" > e.toString() > "=" * 66 > "Error in loading the error report service was:" > f.toString() end // Restore session data function _loadSession() self.logi( "Loading session data" ) self.sessionData, self.sessionExpired = self.sessionManager.restore() if self.log_level >= 3: self.logd( "Session data: " + self.sessionData.describe() ) end // save session data function _saveSession() if self.sessionData if self.log_level >= 3: self.logd( "Saving session data: "+ self.sessionData.describe() ) self.sessionManager.save( self.sessionData ) end end // Display debug data function _displayDebug() if self._debugStream > "

    Debug log sent to client

    "
             > htmlEscape(self._debugStream.closeToString())
             > "
    " > "
    " end end function _makeCSSforID( cssid, styles, item ) results = [=>] id = strReplace(item.getFullID(), ".", "\\.") for style in styles full_id = style.mode ? (cssid + ":" + style.mode) : cssid if style.spec if style.spec.typeId() == ArrayType newid = ",".merge(map({v=> full_id+" " + v}, style.spec)) full_id = newid else full_id += " " + style.spec end end if full_id in results res_by_id = results[full_id] if style notin res_by_id: res_by_id += style else results[full_id] = [style] end end result = "" for k,v in results singleCSS = "" for style in v singleCSS += style.css if not singleCSS.endsWith(";"): singleCSS += ";" end result += @k + " {\n" + singleCSS + "}\n" end return result end end // Re-export relevant Nest elements export Nest, NestError frameworks/nest/nest/000077500000000000000000000000001176363201700151665ustar00rootroot00000000000000frameworks/nest/nest/ajax.fal000066400000000000000000000020701176363201700165740ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: ajax.fal Ajax utilities. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 14 Aug 2010 19:41:05 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @brief Creates a javascript request for the Nest module in the page. @param id The ID of the Nest ajax request. @param params The parameters to send to request. @param onSuccess Javascript to run on success. @optparam extra elements to be put in the button field (class, id, etc) in a dictionary. @note double quotes in onClick are escaped. */ function request( id, params, onSuccess ) extratxt = "" for k,v in params extratxt += @"$k:\"$v\"" formiddle: extratxt += "," end Nest.requireJS( "base" ) >> @"Nest.ajax('$id',{$(extratxt)},$(onSuccess) );" end frameworks/nest/nest/dbidp.fal000066400000000000000000000052371176363201700167430ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: dbidp.fal Data provider for widgets interfacing simple DBI tables. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 09 Aug 2010 15:33:56 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import NestError from nest.error as NestError class DBIDataProvider( dbh, table, keyfield ) dbh = dbh table = table keyfield = keyfield _curvalues = nil _curop = nil _curkey = nil function openNew() self._curop = 'new' return true end function openGet( keyvalue ) self._curkey = keyvalue self._curop = 'get' self._curvalues = [=>] return true end function openSet( keyvalue ) self._curvalues = [=>] self._curop = 'set' self._curkey = keyvalue return true end function openDelete( keyvalue ) self._curop = 'del' self._curkey = keyvalue return true end function apply() if self._curop == 'del' sql = "delete from " + self.table + " where " + self.keyfield + " = ?" self.dbh.query( sql, self._curkey ) elif self._curvalues fnames = self._curvalues.keys() values = self._curvalues.values() if self._curop == 'new' sql = "insert into " + self.table + "(" + ",".merge(fnames) + ") " + "values( ? " + (",?" * (fnames.len() - 1) ) + ")" elif self._curop == 'get' sql = "select " + ",".merge(fnames) + " from " + self.table + " where " + self.keyfield + "=?" res = self.dbh.query( sql, self._curkey ) self._curvalues = res.fetch([=>]) res.close() elif self._curop == 'set' sql = "update " + self.table + " set " + "=?,".merge(fnames) + "=? "+ "where " + self.keyfield + " = ?" values += self._curkey end if sql self.dbh.aquery( sql, values ) end end self._curop = nil end function discard() // do nothing self._curop = nil end function set( fname, value ) if self._curvalues == nil: self._curvalues = [=>] self._curvalues[fname] = value end function get( fname ) if fname notin self._curvalues raise NestError( NestError.get_field_not_found, nil, fname ) end return self._curvalues[fname] end end frameworks/nest/nest/error.fal000066400000000000000000000035241176363201700170070ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: error.fal Common Nest Errors ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 25 Jun 2010 10:52:46 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ class NestError( code, desc, extra ) from Error( code, desc, extra ) inv_ttype = 10001 page_notfound = 10002 undef_var = 10003 invalid_var = 10004 mod_notfound = 10005 no_mod_class = 10006 pcheck = 10008 res_notfound = 10009 jscomponent_notfound = 10010 element_not_in_skin = 10011 set_field_not_found = 10012 get_field_not_found = 10013 config_not_found = 10014 other = 10100 _desc = [ 10001 => i"Invalid content generator descriptor", 10002 => i"Required page not found", 10003 => i"Accessing undeclared service variable", 10004 => i"Given value has failed validation test", 10005 => i"Required module not found", 10006 => i"The module must expose a class with its name", 10007 => i"This method can be invoked only within a frame, and just once", 10008 => i"Optional parameter consistency check falied", 10009 => i"Resource not found", 10010 => i"Requested JS component not found", 10011 => i"Element not found in skin", 10012 => i"Field required in a DataProvider-set operation not found", 10013 => i"Field required in a DataProvider-get operation not found", 10014 => i"Required configuration file not found", 0 => i"Dummy" ] init if code in self._desc self.description = self._desc[ code ] end end end frameworks/nest/nest/hook.fal000066400000000000000000000012161176363201700166120ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: hook.fal Modules intercepting function requests. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 02 Jul 2010 18:39:59 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# Base class for function hooks. This class represents the minimal structure of function hooks. */ class Hook() function invoke( /*...*/ ) end end exportframeworks/nest/nest/js.fal000066400000000000000000000034661176363201700162770ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: js.fal Javascript utilities. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 14 Aug 2010 19:41:05 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @brief Creates a javascript enabled button @param value The text of the button @param onClick Javascript to run on click @optparam extra elements to be put in the button field (class, id, etc) in a dictionary. @note double quotes in onClick are escaped. */ function button( value, onClick, extra ) extratxt = "" for k,v in extra extratxt += @"$k=\"$v\" " end onClick = onClick.replace( '"', '\"' ) >> @"" end /*# @brief Creates a javascript enabled link @param href The target of the link @param text The text of the link @param onClick Javascript to run on click @optparam extra elements to be put in the anchor (class, id, target, etc) in a dictionary. @note double quotes in onClick are escaped. */ function link( href, text, onClick, extra ) extratxt = "" for k,v in extra extratxt += @"$k=\"$v\" " end onClick = onClick.replace( '"', '\"' ) >> @"$(text)" end /*# @brief Puts a javascript in the page. @param text Content of the javascript. Useful as correctly formated scripts are stripped of extra-spaces automatically. */ function script( text ) >> @"" end frameworks/nest/nest/mod/000077500000000000000000000000001176363201700157455ustar00rootroot00000000000000frameworks/nest/nest/mod/ErrorReport.fal000066400000000000000000000021431176363201700207160ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: ErrorReport Error report module. This module is always loaded and rendered as-is in case of errors. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 09 Apr 2012 15:32:14 +0200 ------------------------------------------------------------------- (C) Copyright 2012: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ class ErrorReport( error, context ) //# The Falcon error that happened error = error //# Text of the page up to the moment where the error was raised context = context skin = "" init Nest.loadConfig( self ) end function render() error = self.error.toString().replace("<","<").replace(">", ">") if self.context context = self.context.replace("<","<").replace(">", ">") else context = "" end return @ self.skin end function onConfig( cfg ) self.skin = cfg.skin end end frameworks/nest/nest/mod/ErrorReport_cfg.fal000066400000000000000000000005501176363201700215350ustar00rootroot00000000000000 skin = ' Nest framework error

    Nest Framework error

    The following error occoured while the Nest Framework was composing the page.

       $(error)
    

    The error happened while the following context was being formed:

    'frameworks/nest/nest/res/000077500000000000000000000000001176363201700157575ustar00rootroot00000000000000frameworks/nest/nest/res/ajax-loader.gif000066400000000000000000000223231176363201700206370ustar00rootroot00000000000000GIF89a 興~~~pppRRR|||666:::@@@jjjzzz<<>> ! NETSCAPE2.0!Created with ajaxload.info! ,  964!5:; "";=5 ? 7@)26<>(B(.*0(#"3B/(8$+1  $"pA&X("["hDBhH‡ A D ED^:*ŋiH[ @N"^`:G@=IBTD "nD  (-$`ܹxwR ! , *X[ VY\ 1-LQ IJ3ZQ $FJLP TVZ] 7@GKMQLSUW<^B(. &KPR(@KJB/(8B12˄4 :j/Q "E% 'RPA!BZH۶$0 Ep"q耈BGxѭz@2=P'.8daۋG4/H"-)dA^N8ƶ pJBHΆ ˷߿! , $N?h6b-(.$ b\H!!US((1@/(8.'S u .FbŔ @@^"C.A x(:*zr ~/)*@3_=sς W>  Ƞ  . T#ŀ$AG cXn] 7AdXhJ +_[\6_l FF n8_km-oHZ=pS.?b-im=eG4 D'GXDϛ!i "R22%E8օP@  9, EBJT$ǍA EuM_AP8Y @6}XD%2")TRDaL `1TNBT=T`ѹxWo ! , BCHQ"E kQq b`DNj*c .  # )JO6 (3PQCUrY:?N9+RO#:.9,6p;a(/ Qn)r" HXt6EEB66ġpHA$6,hCT>*B$^IcEaQ& _`yRȊ7) 0 yDh:(,KHQ<[lʝK@! , .+l %T3 &M#sb /Ol( tPk _nQR^?Hi It _egrN+ENu`l?vVu So6[/FfrpFjo%GF$ AƂQ@P(Јhт@EPhDAC0nHHFn܈@.2bQ2 Q@/\"ȦYFh|ё荋&5 }h4 ` L+pc젵5Lx0$˷߿}! , l#m2 mc w_ 2C$(\t$)nG j1Gm\ 0hvc!382#F)Bu+\|jb>FBhB7d, ~ؘD"/*:ңdܸPTY.@S (^,DI$B$GȞX|UBBG5,U A72Ks*@bܻx݋7! ,  i! SH 2C?,2H96.GM%/BuHw NTPL7((//?/C]ML3@($x .^Lx.ц[u4@LH=h EȆ+VPq"YBj\dFBEĈX9·}3D H; Bp8=p_ǎ 8 ` "砱.'8#PT ʝKݹ! , $ C37,C/j %4Dmc$/BiD%F\] ((/@u]K8(4$Nc]T9 /4d.̆ '[ABƉ 0aT$ϐc"+2Lb!fBc@ 7 *z! &N3TG: b1Dg \"K!@6)P@zL[7t іHDЂ8t &$:PH˷_! ,  BO,$i2,@ 7i2o&-B/7B[S+&k((/B-jb[8( 4_kFH /ۅ[q6.Ȇ ,' @@B)0J&bP$0ZC9RB)D*cG\!0JPCb^d!C؄T 1h2#N1 =(fZ6$#5Ȉ2*tY^.M(v+n$˷߿! , 7 (( / D477BR2G9(AB(.R6 8( u&^ /Åjt. O!皋$88.@cd@T'0PEQ L'EQ p ȀSY(x,[@/+XN<A X(<)K^"thxd']pS F~˷߿! ,  8(( .7B(A/(8/B-/  (-j@(/B-S(.ֆB܈B+EN6(41 [˗—c(Rw"!D$ܹ9B 2vsːl:v B"!440و1OJ)E"=ȕ 4G VBŎ1@xahZA>R⊎"؄DHʝK.@! , $$8((  B(A///BB  (/(8.І4 NjS++R(ۉ,UEHH?S 6\(w7Hlaǭ(eF  "L`M" LjįrZ!F*y(6l[9X p BC*+ p:)BB$& @ a "OmDFOٰ۷pF ! , $$((  B(A/4/B7(8 (/B.҆ /j ,̉(݈7uC'RQp㜢fq[!⚹e 5 ͅ!/cTֈįsK.q(xT ,0$Ns<B ؁aX0?@P ;> HU1#QPb.ࡒn$˗P ! , $.(($ B(A (/B7( B/(8.І4@Ջ(܉ÿٟ7A/FZ@R"X~ 04/9pfXg"$``<-&KRc@"@ &)D|);R0=Z㛀0h" P΁< Gؘ8 0Zi#" q x6RPnl@IFڤ $!^ -dvݻx ! , B.(($B-M)E(A$@Y:VB7( pZ;Î8TpgB/(8%[̅44B4isdK@ 5QQx(""t(T8C`@I1Ѧ! ".\@3uFQȅB/(84؋/GsN@(H$ Ϛ6ma6 |8Xe &P'*cE:PJBQDO."QNSI8tBw<tPMgP@FA nH7Dvf Z0ܻx݋)! ,  E3 B!t;:G"x;=&4\)*D$@ X26Pn8 2(_K(4$ڵpʝKWn ;frameworks/nest/nest/res/grad_metal_1.png000066400000000000000000000004211176363201700210010ustar00rootroot00000000000000PNG  IHDRdN7sRGB pHYs  tIME4%,ztEXtCommentCreated with GIMPW~IDATe1 @?ZTFLa)tD2$.MVC,ML0 Kc#ΑxT=7JN<28کe*Xo}_Hq֡2ٝg_qz:ڀIENDB`frameworks/nest/nest/res/grad_metal_2.png000066400000000000000000000004171176363201700210070ustar00rootroot00000000000000PNG  IHDRdN7sRGB pHYs  tIME50XstEXtCommentCreated with GIMPW|IDATuA1j R$ C& NZueO9f\Þ7b.ݻrfpBHh%XBZX=GOm;na5w&-Fuk?xyIENDB`frameworks/nest/nest/res/info.png000066400000000000000000000047321176363201700174260ustar00rootroot00000000000000PNG  IHDR szzsRGBbKGD pHYs  tIME :| ZIDATXmWklΙ}ƀy$&?h!iITmFjJ+gJ?*TVR"pFB cc㵽ڻڐ+vs9y}\Mka xb!֝E@jW7TuPDOsMyܕzeܬ4Gcū/<'뇔43cmlnJc~kRH7%145ֆMxZDckyK:jDi@^ Vs^0p-mѻm[@=Mũo[vߣƭ Hڲyٺy 7d?uI*)Y+ulݴ?{%k;*g]{L_v]ۖHoP(TUEcBK "*N^,X(5V9H i-{]̭XZ m]Uk+` %\!ak40"R'7r(4z9*kyl=1DD R(HZdQ15±M-@I@(D Vu,_ڊ+[_g.N^KTQk<: GVјgϘtd=0D燴rY>:; ^tʅ% 0^PEh;:ll^[T#>)F$D ՘( ŲT 4(xiUuu"a!49By8 2Iei8v @B`(dYQU8$ ~ih'b#U,PPLB#MOY# Ƕ(đ)±,%b(:(y~#m:"1Zy< F 4tKKU01W0Qc!@ cLƈVQVǕj (Eq7Fk^EZy9ʦL1 P,[U/=L@@U *`0A[KTMP5(_ 0"Z( ) `T˜VHL5uI1 j|V.UQnica:ŀ)yqIU `b$*¸UD 98CChihN[CzH\@%_8cW5)" BJGe `fY씘qIUFͺ7_CȂʾK VE90Z2ĞQsLLNcULuvřNJQV*sPgöbu0ZS?3H;X:ZSL8QC:Οӻ8;] 7DT# l;n]ñ6Tƺ,nM9R\cװ@kY̞oC>8+Kp."Bjި"[ƌ'*CC7fBMh`:jñvY]'N}swO~~5W~17I+Զ,". `|ʇeQ4s\&&l֑)2ݽMR\OO߿c&^{_:8}ޖmj:)̈>ḱE8ãt/#{25KAhm[deF*e1B |qcLQf B9Y#G󼪞[z ?z%tO`j9^0K6"І y مfK3Lz=/.}l߾oǕ5;1| lٰ=?eAC%44OD2"Q6|󥴲q~͗IENDB`frameworks/nest/nest/res/infobox_close.png000066400000000000000000000010031176363201700213100ustar00rootroot00000000000000PNG  IHDR exPLTEYi~\kAQ+8"-"-#VU#x2A-:ަ+7P^;L訯v0?4B5Enx7G.<+82A4D8H6FUd0?MVe[i4>/>GTAQ=Mt\j(4.=쪱.=0>$0-<-;$/9I5EÉ/=.<2A=L+8ES-7.>ݕmAn^-!^vK ׄIENDB`frameworks/nest/nest/res/json2.js000066400000000000000000000146521176363201700173600ustar00rootroot00000000000000/* http://www.JSON.org/json2.js -- See http://www.JSON.org/js.html */ var JSON; if (!JSON) { JSON = {}; } (function () { "use strict"; function f(n) { // Format integers to have at least two digits. return n < 10 ? '0' + n : n; } if (typeof Date.prototype.toJSON !== 'function') { Date.prototype.toJSON = function (key) { return isFinite(this.valueOf()) ? this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z' : null; }; String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function (key) { return this.valueOf(); }; } var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, gap, indent, meta = { // table of character substitutions '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' }, rep; function quote(string) { escapable.lastIndex = 0; return escapable.test(string) ? '"' + string.replace(escapable, function (a) { var c = meta[a]; return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }) + '"' : '"' + string + '"'; } function str(key, holder) { var i, // The loop counter. k, // The member key. v, // The member value. length, mind = gap, partial, value = holder[key]; if (value && typeof value === 'object' && typeof value.toJSON === 'function') { value = value.toJSON(key); } if (typeof rep === 'function') { value = rep.call(holder, key, value); } switch (typeof value) { case 'string': return quote(value); case 'number': return isFinite(value) ? String(value) : 'null'; case 'boolean': case 'null': return String(value); case 'object': if (!value) { return 'null'; } gap += indent; partial = []; if (Object.prototype.toString.apply(value) === '[object Array]') { length = value.length; for (i = 0; i < length; i += 1) { partial[i] = str(i, value) || 'null'; } v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']'; gap = mind; return v; } if (rep && typeof rep === 'object') { length = rep.length; for (i = 0; i < length; i += 1) { if (typeof rep[i] === 'string') { k = rep[i]; v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } } else { for (k in value) { if (Object.prototype.hasOwnProperty.call(value, k)) { v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } } v = partial.length === 0 ? '{}' : gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}'; gap = mind; return v; } } if (typeof JSON.stringify !== 'function') { JSON.stringify = function (value, replacer, space) { var i; gap = ''; indent = ''; if (typeof space === 'number') { for (i = 0; i < space; i += 1) { indent += ' '; } } else if (typeof space === 'string') { indent = space; } rep = replacer; if (replacer && typeof replacer !== 'function' && (typeof replacer !== 'object' || typeof replacer.length !== 'number')) { throw new Error('JSON.stringify'); } return str('', {'': value}); }; } if (typeof JSON.parse !== 'function') { JSON.parse = function (text, reviver) { var j; function walk(holder, key) { var k, v, value = holder[key]; if (value && typeof value === 'object') { for (k in value) { if (Object.prototype.hasOwnProperty.call(value, k)) { v = walk(value, k); if (v !== undefined) { value[k] = v; } else { delete value[k]; } } } } return reviver.call(holder, key, value); } text = String(text); cx.lastIndex = 0; if (cx.test(text)) { text = text.replace(cx, function (a) { return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }); } if (/^[\],:{}\s]*$/ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { j = eval('(' + text + ')'); return typeof reviver === 'function' ? walk({'': j}, '') : j; } throw new SyntaxError('JSON.parse'); }; } }()); frameworks/nest/nest/res/name_avail.png000066400000000000000000000045061176363201700205660ustar00rootroot00000000000000PNG  IHDR**[sRGBbKGD pHYs  tIME  /Oj5IDATX͙kpU} DH-)I(5VGOb$38m:V[m:Bv:mZ AQcV,:IeTЀy:hK. I'⹡ .O:ϧ@󋽮_rc*QPټCBx#l dѺXٰpg3RpkUd?q(ߏH%8~$i=w ;m]-:udzWS+6ϺZ7wġ|%dt;Pt\rL8 AKU=<{EJTcɥk3;Ypw?+f?> %X P>r=j3'L$w׼;k]2ȑ 5`yK3`~h1$(2IZF9wvׁ¡~C8ozG4ְ=7]{}0pkO`<+BRT 0rѹ~8;\/5o!hhǢ/=+^,|™Aޜu&gI1JDZpc,c0 cpK2Ьd7/nk"b_M6¬{HE@X5bYbP! Q>QzH8ɉDUIŕq8G~䬛i7vP%&# BU$izꉿwLdja2)[4##,zP*AK(-4 8 s _z~K, 4FTEC\Z:glͲP@T X|ǘm ڢW=4PBL)ҪQ2:V-jS#>~IR_<^S( 'zMcJ"bQD1iLR*/ evk--*ZTsU"(C `( ?1N( #2BKKG%ZvI3Ԛud"aR1BP5VNB^@S]": Q52P, Qݍ=} }S"ԃ"<"j:T(kaP8u1ҋ[-v@P!Q HB V{8X]DKT}Q|AhArbJTRIDtvc%- w^R".Gd(UlY$D@W=]#iއ]rn8By@$I"+]YB>s{oˌtgidK Yb_bɣpAT 9{𓭧殣_ڒN9=MkOt: " AĢ7"TS˭~ow>];PWWعs׷|4)Tgl@ו jma5w~a>m݊f;}3҅۷o~Z/ yL̷z= iP[twxW=k8X5cxm۶L&[ By0`.su]{ 8e#Ӱn?F;d>yޜb !0Tue˂}W> 9u]D6A ڎqHA0 AknU&Y͟8\~}AAUohnnGkk+? 0 ljll̮[Za-^vxUESSӱ AD"L&D$kuMkllڵktt͓ u]Ex'MMM_X4tJrD"8N%yUZ[[[l)b"p'_"RI Z___Nd2ٛNK͋$ִNW;O>Ƙd2)qL^D\)>bf?wN䒩ԏ>3Ie2'u]x~9쳽S,ujbuQM'gѺ:tttꫯ("cLAq a"K5!>@G҇jDp雧 ϧ%֞IENDB`frameworks/nest/nest/res/name_loading.gif000066400000000000000000000076301176363201700210710ustar00rootroot00000000000000GIF89a**4@Q6F=<ʼnlUXCmD `PmE^ t  Pm ECE l EPFxE O~EE tNlFD[  Y jM pP *t@C B>$x@6$0bSD8!_4x\!($ఒ%Q k`fB Ua`N 5IIa` T&qȂO! @{ZLAjLg  (.2z:A tiMCFXF^m iAT8q- x-rSD JaZ `A@ DDC6heoX^oB &)`=K! ,**@pH, Cal:E@50ЬVp>4@Q6fm 8( {NxUhBmD]^ EmEv m Pm ECm`OmE lGOF FPFFSeYlpDfM  YjCjPE  MB^၂ *AB>) H@YmiAĉ^l ,)Qp'#Uap]Ђh ᡁ:qptAH h3;WM8ZL0*pa4U * iVx`U# 7/(0IbD$Ex*Uf !a4@Q6fm 8( {NxUhBmD]^ EmEv m Pm ECm`OmE lGOF FPFFSeYlpDfM  YjCjP  N} B^# O$ \@ c"ӂ@Bfm@l , t}2'-ĩ!5 )l 84]8๠ϞNHx)csxeO8TEOpF(P`-NHF<ZOL*2Bb -Ve i/?(`" RD0@|Ȁ=+իMY`-[Z"=3nȠ9M՛yt#ޤTAb! ,**@pH, Cal:E@50ЬVp>4@Q6fm 8( {NxUhBmD]^ EmEv m Pm ECm`OmE lGOF FPFFSeYlpDfM  YjCjP  N} B^# O$ \@ c"ӂ@Bfm@l , t}2'-ĩ!5 )l 84]8]> xnY`(&H.'HT(e@S'(uBh'yZF_Zў`DU!ʰsRB.Z R9U~R(^CA ;+P]q1".,zC)h_6qW"f*~McC{xLB07/<uB93ۅЉ0VK! ,**@pH, Cal:E@50ЬVp>4@Q6fm 8( {NxUhBmD]^ EmEv m Pm ECm`OmE lGOF FPFFSeYlpDfM  YjCjP&N} B(7g VHo2)!0$D`fIOT<Y>>q0dd@`@^A8*΂Mi 64h`΁F&u'(XS#9( ++.O A?"I*CF…i`y=&UD|DP*d;*,6ݐ 8:3@B9 }M;I P! ,**@pH, Cal:E@50ЬVp>4@Q6vadD⡭  ~N DmD]^ FE~ m PD{n m`OE lCP!F FMFFSeY!E^NYj(YOF +HC"(@!i E[ .^\@@>-$(le`HIL@˖*d P68Щ{ x gF!G"XpUW""x# AOw &  URn HZ{둑$tAq`H3V2gbJANyXaT6pj/,VLdA;7KVZ!$D hLI׶,mwf;nR&9~9)xvZksjĶm̙L'(g?' ou:L緷B),H޲\.~d+M7dzO?Ez(>OsFm6lK/"sPd@sɮhQ*}af@^&+d=yT05JpV]*ʃ)Cn+lmX=d]C8ź+Q€Y@g{SE>hmYD4AfJvb} <Ytw̾am-y w?Z lr? oyrڍ#5&`T-= tDԏ\"}V'܇)DW-WF/ iYJ4*`QI:"5EFX aa|!H|i?ƈ8ɬ|ӼJTSDo8 L+ 8_Ho-<͢1]2gl5j{)DqêgiER}gY<5ZCϤi_;=3{FZba% kT /En%پ}(m85N37rY񰯕Ć!H\bҰgjz9_ә螇%~ՔWJHhz_C'iw,73_% k-,_mQh3n % }\dV_54w{_&fZηYfte*5 XM,2rь[XD[O!5(jM tt3Efak qN-@cz^ bUغLE?SD葱Q Sޒdg @-*wS,b6Y(dpbdnM{D,,ͳr;OyAh㕅x@]eMH̑0onNd}~' 'M0r:q`#h= Zx aN{E ۨ{qG`DT+5ݒ3VR^f`NQ8V-'5ceq;y K{O:"2Bz 0lǜ:0^KKͰx$ݵCMY3 4 x9J2C5G3 ~1͓G{+rq*'j$ҷ/AXp?,VRYݽ3U ?^F$uF6>Y'5Ԙ]=@й vwU@!,jk8T?W^N˜&2ـ.[듨C3~6kiw"Œ/RMud3KҔ½Da)ѩs#}K52fX7;ӂD[Uf81t`pձSvlicP_X #2óieX^[WAw>+⎏^Xq< NJA3Dh\K5 3Iٌ22یf |SMDEțqHʑ]qJ/ x^%%ǻN'B&ʓpAt@$aqbY$C"ðih `߹ o=s#{ @j"ɾ(Xv">$2XB3BS%HEՃrn./~@dӞ_Qܕix+Zh˿xb6A[ 9$d6P!.  B(($c3k އ^KfSӖkW'ɦ'#OHfx^Y`=#cƧUO--//P$\-'K]룱2KpUr?B#}g|̗]rcmo`_ǮNLcnVS7$B׌O:$6ѡu͒|Yaܚ(pAj!s Ł{rWƇNE{ /\ĩ F_ Rn+y"t4^LU;oWz@3%6N ^i(qgE P][zYGѾ#5/$5kDr-RrqMI$kT߻AΪz7>{i 7-=_Xk 0;]OW&9 7 S牮qD-,r7-+d麽rXJq?u]e7܋?E9>ڵ#gPV{}+;8; f=8.YKCN% ˮ ߱D3BVnk-'nIfA=Aw׺>[ sj4Aage)Q0K"WG^3"ps9wCwB Xmf80 9mNC U {Hlex炓;ݟNWHZÁn+z(ϺG@Wҹgȯ:8!Nv+)Z+@x}2a`SK}[Y,IZЪd.&'EҡVRρ π)HhPkb<㞭?׷i4daxX?*&j)^]ls.Cth6{UʔIENDB`frameworks/nest/nest/res/nest.js000066400000000000000000000520371176363201700172750ustar00rootroot00000000000000/* Falcon - Nest framework - AJAX support */ var Nest; if(!Nest) { Nest = {}; } (function () { "use strict"; if (!Array.prototype.indexOf) { Array.prototype.indexOf = function (obj, fromIndex) { if (fromIndex == null) { fromIndex = 0; } else if (fromIndex < 0) { fromIndex = Math.max(0, this.length + fromIndex); } for (var i = fromIndex, j = this.length; i < j; i++) { if (this[i] === obj) return i; } return -1; }; } //============================================================================ Private part var pendingAjaxReqs = new Array(); function ajax( url, data, callback, errCallback ) { var http = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); http.onreadystatechange = function() { //Call a function when the state changes. if(http.readyState == 4 ) { if ( http.status == 200 ) { var obj; try { // try to understand as json. obj = JSON.parse(http.responseText); } catch( err ) { // if not json, raise proper error. Nest.onJSONError( http.responseText, err ) } if( obj ) { // application error? if ( obj.error ) { Nest.onAPIError( obj ); } else if (callback) { callback( obj ); } } } else { if( errCallback ) errCallback( http.status, http.responseText ); else Nest.onAJAXError( http.status, http.responseText ); } } } var params = ""; if(data) { /* for( var key in data ) { if ( params != "" ) { params = params + "&"; } params = params + encodeURIComponent(key) + "=" + encodeURIComponent(data[key]); } */ params = "params=" + encodeURIComponent( JSON.stringify( data ) ); http.open("POST", url, true); //Send the proper header information along with the request http.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); // http.setRequestHeader("Content-length", params.length); // http.setRequestHeader("Connection", "close"); } else { http.open("GET", url, true); } if( data ) { http.send(params); } else { http.send(null); } } function onAjaxDone( obj ) { var req = pendingAjaxReqs[0]; /* Do not shift now to prevent the callback to re-fire */ if( req.callback ) req.callback( obj ); pendingAjaxReqs.shift(); if( pendingAjaxReqs.length > 0 ) { req = pendingAjaxReqs[0]; ajax( req.url, req.data, onAjaxDone, onAjaxFailed ); } } function onAjaxFailed( status, response ) { /* Try again? */ var req = pendingAjaxReqs[0]; if (req.errCount < 2 && status == 0) { req.errCount = req.errCount + 1; ajax( req.url, req.data, onAjaxDone, onAjaxFailed ); } else { /* Don't bother processing other pending requests */ pendingAjaxReqs = new Array(); if( req.errCallback ) req.errCallback( status, response ); else Nest.onAJAXError( status, response ); } } function addAjaxRequest( url_, data_, callback_, errCallback_ ) { var req = {url: url_, data: data_, callback: callback_, errCallback: errCallback_, errCount: 0 }; pendingAjaxReqs.push( req ); if( pendingAjaxReqs.length == 1 ) { ajax( url_, data_, onAjaxDone, onAjaxFailed ); } } // Handler for set message function handler_set( obj ) { var element = document.getElementById( obj.id ); if( element ) { if( obj.property == 'value' && element.setValue ) { element.setValue( obj.value ) } else { element[obj.property] = obj.value; } } } // Handler for set message function handler_set_style( obj ) { var element = document.getElementById( obj.id ); if( element ) { element.style[obj.property] = obj.value; } } // Handler for invoke message function handler_invoke( obj ) { var element = document.getElementById( obj.id ); if( element ) { element[obj.method].call( element, obj.param ); } } function prepareInit( widID, obj ) { // get the root widget. var list = widID.split("."); while( list.length > 0 ) { widID = list.join("."); var rootElement = document.getElementById( widID ); // If we have a root widget, see if we assigned an init info to it. if ( rootElement && rootElement.Nest_initInfo ) { obj.init = rootElement.Nest_initInfo; break; } list.pop(); } } //=================================================================================== Public interace // Method 'i' -- shortcut for document.getElementByID if (typeof Nest.i !== 'function') { Nest.i = function ( id ) { return document.getElementById( id ); } } // Stop event propagation Nest.eatEvent = function(evt){ if(typeof(event) != "undefined") event.cancelBubble = true; else evt.stopPropagation(); } // All the widgets declared by nest if (!Nest.w) { Nest.w = new Array();} // Method 'rw' -- relative widget. if (typeof Nest.rw !== 'function') { Nest.rw = function ( wid, path ) { var pathArr = path.split("/") var widIdArr = wid.id.split( "." ); while( pathArr.length > 0 && pathArr[0] == '..' ) { widIdArr.pop(); pathArr.shift(); } widIdArr = widIdArr.concat( pathArr ); var widID = widIdArr.join( "." ); wid = document.getElementById( widID ); return wid; } } // Method rp - relative property // Get a property in a relative widget. // info: the live property to be taken. // widID: optional entity ID to which we are relative // objName: optional; if given will be filled with: // - name: the value of the "name" property in the target item. // - id: document ID of the target property // - path: relative path in info, transformed into local ID // Returns the value of the property. // Nest.rp = function( info, widID, objName ) { var element; var useName = false; var idArr = info.split("/"); var valname = idArr.pop(); if( idArr.length == 0 && widID != null ) { element = document.getElementById( widID ); } else { if( idArr[0] == "" ) { // the path is absolute idArr.shift(); element = document.getElementById( idArr.join(".") ); } else { var widIdArr = widID == null? [] : widID.split( "." ); while( idArr.length > 0 && idArr[0] == '..' ) { widIdArr.pop(); idArr.shift(); } widIdArr = widIdArr.concat( idArr ); var wid = widIdArr.join( "." ); element = document.getElementById( wid ); } } if (element != null) { // get the proper value var value; if ( valname == 'value' && element.getValue != null ) value = element.getValue(); else value = element[valname]; // recreate the full entity name, re-localized after .. purging. // if the object was given, we want name, property name and path name if( objName != null ) { var name = element.name; if( name ) objName['name'] = name.replace("[]", "") objName['id'] = element.id; // empty if we didn't pop ../ if( widIdArr == null ) { widIdArr = idArr; } widIdArr.push( valname ); objName['path'] = widIdArr.join("."); } return value; } } // Method 'ajax' if (typeof Nest.ajax !== 'function') { Nest.ajax = function ( req_id, params, callback ) { var url = "./?a=" + req_id; addAjaxRequest( url, params, callback ); } } if (typeof Nest.setWidVal !== 'function') { Nest.setWidVal = function ( wid, value ) { var element = Nest.i(wid); if( element != null ) { if( element.setValue != null ) { element.setValue( value ); } else { element.value = value; } } } } // Method 'widgetMsg' -- Sending AJAX requests to remote widget server. if (typeof Nest.widgetAJAX !== 'function') { Nest.widgetAJAX = function ( widClass, widID, msg, params ) { // the object to be sent. var objToSend = { 'widget': widClass, 'id': widID, 'msg': msg }; // let's get rid of the details now -- this is extra data to send as-is prepareInit( widID, objToSend ); // Params if( params != null) { objToSend["params"] = params; } var url = "./?w=" + widClass; //alert( JSON.stringify( objToSend ) ); addAjaxRequest( url, objToSend, Nest.widgetUpdate ); } } // Method 'message' -- sends a local message to listeners in the page. if (typeof Nest.message !== 'function') { Nest.message = function ( wid, msg, value ) { var listener = Nest.listeners[wid.id]; if( listener ) { for (var i = 0; i < listener.length; i++) { var lrec = listener[i]; var func = lrec.func; var tgtid = lrec.tgt; func.call( tgtid, wid, msg, value ); } } } } // Method 'listen' -- Waits for updates on a certain widget ID // callbacks are in this prototype: func( target, source_wid, msg, value ); if (typeof Nest.listen !== 'function') { Nest.listen = function ( target, wid, cbfunc ) { var listener = Nest.listeners[wid]; var listenRecord = { "tgt": target, "func": cbfunc }; if( listener ) { listener.push( listenRecord ); } else { Nest.listeners[wid] = Array( listenRecord ); } } } // Method 'widgetUpdate' -- handling requests from widget server. if (typeof Nest.widgetUpdate !== 'function') { Nest.widgetUpdate = function ( obj ) { // handle multiple messages. if( typeof obj == 'object' && obj.length ) { var i = 0; while( i < obj.length ) { Nest.processMessage( obj[i] ); i = i + 1; } } else { Nest.processMessage( obj ); } } } // Method 'processMessage' -- handling a single request from widget server. if (typeof Nest.processMessage !== 'function') { Nest.processMessage = function ( obj ) { if( obj.message ) { var handler = Nest.messageHandlers[ obj.message ]; if( ! handler ) { Nest.onMessageNotFound( obj ); } else { handler.method.call( handler.object, obj ); } } else { Nest.onWidgetUpdateError( obj ); } } } // Method 'processMessage' -- handling a single request from widget server. if (typeof Nest.listenAJAX !== 'function') { Nest.listenAJAX = function ( msg, obj, func ) { Nest.messageHandlers[ msg ] = { object: obj, method: func }; } } //=========================================================================== Error management. if (typeof Nest.onAJAXError !== 'function') { Nest.onAJAXError = function( code, text ){ alert( "Nest framework AJAX error.\n" + "Response from server: " + code + "\n" + text ); } } if (typeof Nest.onJSONError !== 'function') { Nest.onJSONError = function( text, synerr ){ alert( "Nest framework AJAX error.\n" + "JSON parse error: " + synerr.name+"\n"+ synerr.message +"\n" + "Response was: " + text ); } } if (typeof Nest.onAPIError !== 'function') { Nest.onAPIError = function( obj ){ alert( "Nest framework AJAX API error.\n" + "Remote API error : " + obj.error +"\n"+ obj.errorDesc ); } } if (typeof Nest.onMessageNotFound !== 'function') { Nest.onMessageNotFound = function( obj ){ alert( "No handler registered for Nest widget message '" + obj.message + "'.\n" + "Received: " + obj +"\n" ); } } if (typeof Nest.onWidgetUpdateError !== 'function') { Nest.onWidgetUpdateError = function( obj ) { alert( "Not a widget update message in Nest widget update.\n" + "Received: " + obj +"\n" ); } } //=========================================================================== DynParams API if (typeof Nest.startPar !== 'function') { Nest.startPar = function(wid) { return { params: {}, add: function( key, value ) { this.params[key] = value; return this; }, addPath: function( key, value ) { this.params[key] = Nest.rp(value,wid); return this; }, addTPath: function( value ) { var names = {}; var v = Nest.rp(value,wid,names); this.params[names.path] = v; return this; }, addName: function( value ) { var names = {}; var v = Nest.rp(value,wid,names); this.params[names.name] = v; return this; }, addId: function( value ) { var names = {}; var v = Nest.rp(value,wid,names); this.params[names.id] = v; return this; }, gen: function() { return this.params; } } } } //=========================================================================== Object initialization. // set the default widget server message handlers if (! Nest.messageHandlers ) { Nest.messageHandlers = { 'set': { object: null, method: handler_set}, 'set_style': { object: null, method: handler_set_style}, 'invoke': { object: null, method: handler_invoke} } } // Set the local message handlers if (! Nest.listeners ) { Nest.listeners = {}; } }()); /* http://www.JSON.org/json2.js -- See http://www.JSON.org/js.html */ var JSON; if (!JSON) { JSON = {}; } (function () { "use strict"; function f(n) { // Format integers to have at least two digits. return n < 10 ? '0' + n : n; } if (typeof Date.prototype.toJSON !== 'function') { Date.prototype.toJSON = function (key) { return isFinite(this.valueOf()) ? this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z' : null; }; String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function (key) { return this.valueOf(); }; } var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, gap, indent, meta = { // table of character substitutions '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' }, rep; function quote(string) { escapable.lastIndex = 0; return escapable.test(string) ? '"' + string.replace(escapable, function (a) { var c = meta[a]; return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }) + '"' : '"' + string + '"'; } function str(key, holder) { var i, // The loop counter. k, // The member key. v, // The member value. length, mind = gap, partial, value = holder[key]; if (value && typeof value === 'object' && typeof value.toJSON === 'function') { value = value.toJSON(key); } if (typeof rep === 'function') { value = rep.call(holder, key, value); } switch (typeof value) { case 'string': return quote(value); case 'number': return isFinite(value) ? String(value) : 'null'; case 'boolean': case 'null': return String(value); case 'object': if (!value) { return 'null'; } gap += indent; partial = []; if (Object.prototype.toString.apply(value) === '[object Array]') { length = value.length; for (i = 0; i < length; i += 1) { partial[i] = str(i, value) || 'null'; } v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']'; gap = mind; return v; } if (rep && typeof rep === 'object') { length = rep.length; for (i = 0; i < length; i += 1) { if (typeof rep[i] === 'string') { k = rep[i]; v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } } else { for (k in value) { if (Object.prototype.hasOwnProperty.call(value, k)) { v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } } v = partial.length === 0 ? '{}' : gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}'; gap = mind; return v; } } if (typeof JSON.stringify !== 'function') { JSON.stringify = function (value, replacer, space) { var i; gap = ''; indent = ''; if (typeof space === 'number') { for (i = 0; i < space; i += 1) { indent += ' '; } } else if (typeof space === 'string') { indent = space; } rep = replacer; if (replacer && typeof replacer !== 'function' && (typeof replacer !== 'object' || typeof replacer.length !== 'number')) { throw new Error('JSON.stringify'); } return str('', {'': value}); }; } if (typeof JSON.parse !== 'function') { JSON.parse = function (text, reviver) { var j; function walk(holder, key) { var k, v, value = holder[key]; if (value && typeof value === 'object') { for (k in value) { if (Object.prototype.hasOwnProperty.call(value, k)) { v = walk(value, k); if (v !== undefined) { value[k] = v; } else { delete value[k]; } } } } return reviver.call(holder, key, value); } text = String(text); cx.lastIndex = 0; if (cx.test(text)) { text = text.replace(cx, function (a) { return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); }); } if (/^[\],:{}\s]*$/ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { j = eval('(' + text + ')'); return typeof reviver === 'function' ? walk({'': j}, '') : j; } throw new SyntaxError('JSON.parse'); }; } }()); frameworks/nest/nest/res/nest_fx.js000066400000000000000000000160121176363201700177630ustar00rootroot00000000000000/* Falcon - Nest framework - Special effexcts */ var Nest; if(!Nest) { Nest = {}; } (function () { "use strict"; //========================================================== Private var transArray = new Array(); var tempArray = new Array(); var nextArray = new Array(); var fps = 20; function getStyle(oElm, strCssRule){ var strValue = ""; if(document.defaultView && document.defaultView.getComputedStyle){ strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule); } else if(oElm.currentStyle){ strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){ return p1.toUpperCase(); }); strValue = oElm.currentStyle[strCssRule]; } return strValue; } function getNumValue( string ) { if( typeof string == 'string' ) { var value = string.match(/\|([0-9.-]+)\|/); if( value ) return parseFloat(value[1]); return null; } return string; } function getNumFromValue( string, source ) { if( typeof string == 'string' ) { var pos = string.search(/\|([0-9.-]+)\|/); if( pos >= 0 ) return parseFloat(source.substr(pos)); } return parseFloat(source); } function setNumValue( string, value ) { if( typeof string == 'string' ) { return string.replace(/\|([0-9.-]+)\|/, value); } return value; } function getOriginalValue( string, value ) { if( typeof string == 'string' ) { return string.replace(/\|/gi, ""); } return value; } function progress() { // clear the temporary array if( tempArray.length > 0 ) tempArray.splice( 0, tempArray.length ); if( nextArray.length > 0 ) nextArray.splice( 0, nextArray.length ); var time = 1000.0/fps; var trans; while( (trans = transArray.pop() ) ) { trans.elapsed += time; //document.getElementById('txt').value=trans.elapsed; if( trans.elapsed >= trans.time ) { var tostyle = trans.tostyle; for( var key in tostyle) { var toValue = getNumValue( tostyle[key] ); trans.object.style[key] = setNumValue( tostyle[key], toValue ); } if( trans.next ) { nextArray.push( trans.next ); } if( trans.ondone ) { trans.ondone(); } } else { // save the data tempArray.push( trans ); // perform the transitions var tostyle = trans.tostyle; var fromstyle = trans.fromstyle; var ratio = trans.elapsed/trans.time; for( var key in tostyle) { var fromValue = fromstyle[key]; var toValue = getNumValue( tostyle[key] ); var value = ((toValue - fromValue)*ratio)+fromValue; trans.object.style[key] = setNumValue( tostyle[key], value ); } } } // save a copy of the temp array transArray.splice( 0, transArray.length ); if( tempArray.length > 0 ) { transArray = transArray.concat(tempArray); setTimeout( progress, time ); } // eventually start new transitions. if( nextArray.length > 0 ) { for( var count in nextArray ) { Nest.addTransition( nextArray[count] ); } nextArray.splice( 0, nextArray.length ); } }; //========================================================== Public interface. // Appends a transition // // object: An entity of the document that must be transited, // time: Duration of the transition // tostyle: change into the given style; "text |number| text" indicates styles with textual elements. // fromstyle: (optional) Initial status of the transition; use only numbers. // next: (optional) Transition to perform when this is complete. // ondone: (optional) callback to execute when the transition is complete. if (typeof Nest.addTransition !== 'function') { Nest.addTransition = function( obj ) { // first, update the object as we desire. var fromstyle = obj.fromstyle; var tostyle = obj.tostyle; if( fromstyle ) { for( var key in fromstyle) { obj.object.style[key] = setNumValue(tostyle[key], fromstyle[key]); } } // reads the current defaults in the object fromstyle = {}; for( var key in tostyle ) { fromstyle[key] = getNumFromValue(tostyle[key], getStyle(obj.object, key)); } obj.fromstyle = fromstyle; // start the transation obj.elapsed = 0; if ( transArray.length == 0 ){ transArray.push( obj ); setTimeout( progress, 1000.0/fps ); } else { transArray.push( obj ); } }; } if (typeof Nest.transite !== 'function') { Nest.transite = function( object_, time_, tostyle_, fromstyle_, next_, ondone_ ) { Nest.addTransition( { object: object_, time: time_, fromstyle: fromstyle_, tostyle: tostyle_, next: next_, ondone:ondone_ } ); } } if (typeof Nest.getStyle !== 'function') { Nest.getStyle = getStyle; } if (typeof Nest.findPos !== 'function') { Nest.findPos = function(obj) { var curleft = 0, curtop = 0; if (obj.offsetParent) { do { curleft += obj.offsetLeft; curtop += obj.offsetTop; } while (obj = obj.offsetParent && (obj.style.display=='static')); return { x: curleft, y: curtop }; } } } if (typeof Nest.findPagePos !== 'function') { Nest.findPagePos = function(obj) { var curleft = 0, curtop = 0; if (obj.offsetParent) { do { curleft += obj.offsetLeft; curtop += obj.offsetTop; } while (obj = obj.offsetParent); return { x: curleft, y: curtop }; } } } if (typeof Nest.reposition !== 'function') { Nest.reposition = function(objSrc, objTgt, dispx, dispy, height, width ) { var curleft = 0, curtop = 0; var pos = Nest.findPos(objSrc); var top = (pos.y + dispy); var left = (pos.x + dispx); if ( height == null ) height = objTgt.offsetHeight; if ( width == null ) width = objTgt.offsetWidth; /* Find absolute positioning in page and see if we're out */ pos = Nest.findPagePos(objSrc); if( document.body.clientHeight <= pos.y + dispy + height ) { top = (top - ((pos.y + dispy + height) - document.body.clientHeight) - 10 ); } if( document.body.clientWidth <= pos.x + dispx + width ) { left = (left - ((pos.x + dispx + width) - document.body.clientWidth) - 10 ); } objTgt.style.left = left+"px"; objTgt.style.top = top+"px"; } } }()); frameworks/nest/nest/res/showcase_current.png000066400000000000000000000022641176363201700220470ustar00rootroot00000000000000PNG  IHDRw=sBIT|d pHYs@tEXtSoftwarewww.inkscape.org<1IDATHOh]UjTŢZE) Zwe]H]wUȲݸFƂHnЦ(k% iH{θ羾f;gyԳxxv?k=]=3ak aK@2:fF\lhh"XYYqQ"!FG# k,7nQ7oϦv(t yQX<;f1׬Q׿X[[۔c?I|cccx6"d,cZMROcܝfyԩSoIUyD[X Q#urfFe>Py0<< $d̓bx2~1 ye"$ Ȁw"4bc%{V>%twL,4h4^1'͓WWND^JWX4 Б#Gvٶ9q){w G7A@hqq]}>)}3+U;3!bh[w<͉'.K +uJI5`x511ѣG?i6[jZ7{E^~U'Kfs{ٱ{vܹju^;sճg^Mz \nuI< < [;? l7,׀<02)`xN{$ `X^d>tW%}VO)nUu][Yh7IENDB`frameworks/nest/nest/res/showcase_hover.png000066400000000000000000000025741176363201700215140ustar00rootroot00000000000000PNG  IHDRw=sBIT|d pHYs@tEXtSoftwarewww.inkscape.org<IDATHVϫ]W>?ϧC}'6V!8K 3p_Qa&A@L(%m!c%XM014{{-ܗ۴)=c}k}omܽ{w C>of33ڗ8RJf(cdPL\o_ Ν;ߑUըUIN ^, ș)W<lgH<2:MtmԠ^G{A6ѸmmmH4϶wެۄ.@HOf!FeRdٳg@9.EW^}juuu~ʭ&Y&kYUa8!#1τED'wo,f@R]o#.n5jӪut"Gf^h ;LҞ◧O~ 3SOfmLEUk[ӦU[նSM/4EQMNٴMcqeeGn4 셶K"&ET}MZIguJ6iөu,F)"N)ǟ=]`j!۷o_<)aoN<%Dɨpn;)2'Onll]vY)8a(yXd"eܤ2* 'E&(ޑyfYgΜ@70ͱcRt0}nR"eH;fHp  \…nmmmgVI:^#C/̂H\\LFeȑ#x6AO8p{0y7.JQܨ7.ōJ'Rd\zybXGԩS1JffbGX]}f,IENDB`frameworks/nest/nest/res/showcase_normal.png000066400000000000000000000022351176363201700216530ustar00rootroot00000000000000PNG  IHDRw=sBIT|d pHYs@tEXtSoftwarewww.inkscape.org<IDATH_L[e9H QF2AE !!1$K%1^v71IP/h+,&] eysH`Mޛ;y9{WH)9-677m6[#P+B+ܑRK)g߳~(0H̭/OMM-;t;srr>RN'ɯb'I)F}Eh;;[fY0@yѡכ5455m$G~^YY[z@)pP2R8Uf۰Z^)!~5.z\gcƳ---?iɝ;wB(B{;PsX,p8U,@vvvҚ<$wee%ZPP>mō7,K/i!uaa!fZ!-PH)UUs_DJ%I8AZx}IϷ844>/xgggu[[G]]]?F0m`MI7!30??޽{_*w*wbbsp`` ,]J)3i`nEQ`dii)EEEZmmmIuuuɅ ʦmmm?G" lX&}9J|~~T\2i(M +vAb@9&i! =҆3dR'$XF < lp$&ѹ̴ytqTA 6y˛[IENDB`frameworks/nest/nest/utils.fal000066400000000000000000000045111176363201700170130ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: utils.fal File with general utils ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 29 Jun 2010 16:52:46 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ function findFile( dir, rname, exts ) // search under dir for the file or the file + some of the exts. fpath = Path() fpath.fulloc = dir fpath.file = rname for ext in exts fpath.extension = ext if fileType( fpath.path ) == FileStat.NORMAL return fpath.path end end // no luck with the extension. Have we the file as-is? fname = dir + "/" + rname if fileType( fname ) == FileStat.NORMAL return fname end // no luck at all return nil end function findFileInDirs( locations, name, exts ) for loc in locations if ( f = findFile( loc, name, exts ) ) return f end end end function reldir( parent, child ) if child.startsWith( "/" ) return child end if not parent.endsWith("/"): parent += "/" return parent + child end function checkModifiedSince( res_file ) if "If-Modified-Since" in Request.headers try // don't bother too much if we don't succeed at first. since = TimeStamp() date = Request.headers["If-Modified-Since"] since.fromRFC2822(date) fs = FileStat(res_file) if fs.mtime <= since Reply.status = 304 Reply.reason = "Not modified" Reply.setHeader( "Content-Type" ) Reply.setHeader( "Cache-Control" ) Reply.commit() exit(0) return true end catch in e Nest.logw( "Error during If-Modified-Since check request: " + e ) end else // else, save the timestamp fs = FileStat(res_file) Reply.setHeader("Last-Modified", fs.mtime.toRFC2822() ) end end function exitReply( status, reason, text ) Reply.status = status Reply.reason = reason Reply.ctype("text","html","utf-8") stdOut().writeText( @"

    $(status) $(reason)

    \n

    $(text)

    \n") end frameworks/nest/nest/widgets/000077500000000000000000000000001176363201700166345ustar00rootroot00000000000000frameworks/nest/nest/widgets/ActiveInput.fal000066400000000000000000000061171176363201700215600ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: ActiveInput.fal AJAX widgeting subsystem -- Text input Widget exciting msg and AJAX ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import InputText from nest.widgets.InputText as InputText /*# Class dispatching local messages and/or AJAX messages on text change. Might be a text (default) or a textarea (if its tag is changed into textarea) field that can send a 'change' message to local listeners and to ajax listeners. */ class ActiveInput( id, prompt, iPrompt ) from InputText( id, prompt, iPrompt ) //# Timeout before starting to check updates typeTimeout = 0 //# if true (default), sends a local message on change. checkLocal = true //# if true (default is false), sends an ajax message on change. checkAjax = false //# code used to prevent generating local messages. localFilter = nil //# code used to prevent generating ajax messages. ajaxFilter = nil init if "onchange" in self.props self.props["onchange"] += "this.onActiveChange();" else self.props["onchange"] = "this.onActiveChange();" end self.props["onkeyup"] = "this.onActiveChange();" if self.onCreate self.onCreate += "this.activeCheck();" else self.onCreate = "this.activeCheck();" end // TODO: Remove this when we have automatic parentship self.addClassInParentship( ActiveInput ) end function onSetup() // create the onChange callback with preliminary check (if not changed, do nothing). checkFunc = "function() { if(this.oldValue == this.value) {return;}\n" if self.checkLocal if self.localFilter checkFunc += "if( " + self.localFilter + ") { return; }\n" end // This is the message for the listener widgets. checkFunc += self.jsEmit( 'change', 'this.value' ) + "\n" end if self.checkAjax if self.ajaxFilter checkFunc += "if( " + self.ajaxFilter + ") { return; }\n" end // This is the message for the listener widgets. checkFunc += self.ajaxMsg( 'change', ['value'] ) + "\n" end checkFunc += "this.oldValue = this.value; }\n" self.jsMethods["activeCheck"] = checkFunc if self.typeTimeout id = self.getFullID() self.jsMethods[ "onActiveChange" ] = " function() { if( this.timeoutVar ) { clearTimeout( this.timeoutVar ); } this.timeoutVar = setTimeout('Nest.i(\"" + id + "\").activeCheck()', " + self.typeTimeout + ");}" else self.jsMethods[ "onActiveChange" ] = " function() { this.activeCheck(); }" end end end /* end of ActiveInput.fal */ frameworks/nest/nest/widgets/ActiveTextArea.fal000066400000000000000000000030621176363201700221720ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: ActiveTextArea.fal AJAX widgeting subsystem -- Textarea Widget exciting msg and AJAX ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import ActiveInput from nest.widgets.ActiveInput as ActiveInput /*# Class dispatching local messages and/or AJAX messages on text change. @param id The id of the widget. @param prompt Prompt for this area to be rendered in forms. @param rows Number of rows @param cols Number of columns Might be a text (default) or a textarea (if its tag is changed into textarea) field that can send a 'change' message to local listeners and to ajax listeners. */ class ActiveTextArea( id, prompt, rows, cols ) from ActiveInput( id, prompt ) tag = "textarea" isSelfClosing = false content = "" init if rows and cols self.props["rows"] = rows self.props["cols"] = cols end // TODO: Remove this when we have automatic parentship self.addClassInParentship( ActiveTextArea ) end function setValue( val ) self.content = val end function renderContent() return self.content end function getValue() return self.content end end /* end of ActiveTextArea.fal */ frameworks/nest/nest/widgets/AjaxPane.fal000066400000000000000000000060501176363201700210100ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: AjaxPane.fal AJAX widgeting subsystem -- Pane loading AJAX content. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 05 May 2012 20:40:37 +0200 ------------------------------------------------------------------- (C) Copyright 2012: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget /*# Pane ready to load AJAX content. @param id The ID of this widget. @optparam loadingMsg An HTML text to be displayed as the pane is being loaded. The AjaxPane is simply a DIV that has a simple pre-disposition to load dynamic content from its subclasses. The subclasses are supposed to become AJAX host by setting the isAJAXHost property to true, and to define the @b paneData dictionrary. Each entry in the @b paneData property is a pane name, and it can be associated with a string or a function returning a string. The class offers a simple javascript-side method called loadPane, that receives a string that must match with one paneData entry. The AJAX method of this class resoponds to the "load-pane" message and searches for an entry to be read or called in the subclass's pageData dictionary. If not found, the overridable onUnknownPane method will be called, and its return value will be used instead. Once the content is determined, the widget sets the innerHTML property of the remote side script accordingly. */ class AjaxPane( id, loadingMsg ) from widget.Widget( id ) //# This is not an ajax host. Subclasses should become. isAJAXHost = false //# The subclass should define the panes contents (text or functions). paneData = [=>] loadingMsg = loadingMsg init // TODO: Remove this when we have automatic parentship self.addClassInParentship( AjaxPane ) end function onRender() msg = self.ajaxMsg( "load-pane", "extra" ) if( self.loadingMsg ) self.jsMethods["loadingMsg"] = '"' + self.loadingMsg.replace('"', '\"') + '"' end self.jsMethods["loadPane"] = @" function( pane ) { if( this.loadingMsg ) this.innerHTML = this.loadingMsg; var extra = {'pane':pane}; $(msg); }" end function AJAX( data ) if data['msg'] == "load-pane" pane = data['params']['pane']; if pane in self.paneData data = self.paneData[pane] if data.isCallable(): data = data() else data = self.onUnknownPane( pane ) end return [self.msgSetProperty('innerHTML', data)] end return [] end /*# Function called when a required pane is not found in the paneData dictionrary. @param pane The pane ID that was not found. @return A string to be sent remotely. */ function onUnknownPane( pane ) return @"AjaxPane: pane \"$(pane)\" not found." end end frameworks/nest/nest/widgets/AjaxPart.fal000066400000000000000000000111711176363201700210330ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: AjaxPart.fal AJAX widgeting subsystem -- Generic dynamic openable component. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 05 May 2012 20:40:37 +0200 ------------------------------------------------------------------- (C) Copyright 2012: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Widget from nest.widgets.widget as Widget /*# Openable dynamic AJAX content. @param id The ID of this widget. @param closedWid The widget to be displayed when closed. @optparam openWid The widget to be displayed when open. @optparam loadingMsg An HTML text to be displayed as the pane is being loaded. @optparam autoOpen Click on this closed part will open it. @initOpen initOpen if true, the widget will be initially open. The AjaxPart is a dynamically openable pane, whose content when "open" is retrieved as a pane of an @a AjaxPane widget. If @b autoOpen is set, then a click on the widget in the closed status will be considered a request to open it, otherwise the javascript open() method exposed by this widget shall be called. The default name for the pane when open is @b "open". */ class AjaxPart( id, closedWid, openWid, loadingMsg, autoOpen, initOpen ) from Widget(id) closedWid = closedWid openWid = openWid ? openWid : Widget( 'open' ) //# This is not an ajax host. Subclasses should become. isAJAXHost = false //# The subclass should define the panes contents (text or functions). paneData = [=>] loadingMsg = loadingMsg init self.addChildren( self.closedWid, self.openWid ) if autoOpen: self.closedWid.set(["onclick"=> "Nest.rw(this,'..').open();"]) if initOpen self.closedWid.addStyle("display:none") self.onCreate = "this.open();" else self.openWid.addStyle("display:none") end // TODO: Remove this when we have automatic parentship self.addClassInParentship( AjaxPart ) end function onRender() msg = self.ajaxMsg( "open", "extra" ) clID = self.closedWid.getFullID() if( self.loadingMsg ) self.jsMethods["loadingMsg"] = '"' + self.loadingMsg.replace('"', '\"') + '"' end self.jsMethods["open"] = " function( pane ) { if( this.isOpen ) return; this.isOpen = true; if( this.loadingMsg ) { var op = Nest.rw(this,'open'); var cp = Nest.i('" + clID + "');" +" op.style.width = cp.style.width; op.style.height = cp.style.height; op.innerHTML = this.loadingMsg; op.style.display = ''; cp.style.display='none';"+" } if( pane == null ) pane = 'open'; var extra = {'pane':pane};" + msg + ";}" self.jsMethods["close"] = " function() { if( ! this.isOpen ) return; this.isOpen = false; Nest.i(this.id + '.open').style.display='none'; Nest.i('" + clID + "').style.display='';"+" }" self.jsMethods["setOpenView"] = " function( data ) { var op = Nest.rw(this,'open'); var cp = Nest.i('" + clID + "');"+" op.innerHTML = data.content; if ( data.width && data.height ) { op.style.width = data.width + 'px'; op.style.height = data.height + 'px'; op.style.display = 'inline-block'; } else { op.style.display = 'block'; } cp.style.display = 'none'; }" end function AJAX( data ) if data['msg'] == "open" pane = data['params']['pane']; if pane in self.paneData data = self.paneData[pane] if data.isCallable(): data = data() else data = self.onUnknownPane( pane ) end if data.typeId() == StringType ret = ["content"=>data] else ret = ["content"=>data[2], "width"=>data[0], "height"=>data[1]] end return [self.msgInvoke('setOpenView', ret)] end return [] end /*# Function called when a required pane is not found in the paneData dictionrary. @param pane The pane ID that was not found. @return A string to be sent remotely. */ function onUnknownPane( pane ) return @"AjaxPart: pane \"$(pane)\" not found." end end frameworks/nest/nest/widgets/Button.fal000066400000000000000000000043711176363201700206000ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Button.fal AJAX widgeting subsystem -- Basic button (not for form submits) ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 02 Oct 2011 13:53:51 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget /*# Basic button (not for form submits). @param id The widget ID @param content The HTML content displayed in the button. @optparam type Optional type (default: 'button') @optparam onclick A JS routine to call on click (if not specified, will emit a Nest onclick message) This generates an HTML button. Normally, you should rely to Submit or Reset fields in forms, as they generate the same controlled values in form submits in different browsers, while button tag based submits can differ (some use the value attribute, some use the contents of the tag). To attach actions to this button, use the @a nest.widget.Widget.jsListeners method. */ class Button( id, content, type, onclick ) from widget.Widget( id ) tag = "button" content = content isSelfClosing = false init if self.props == nil: self.props = [=>] self.props['name'] = nil // will be filled at render self.props['type'] = type ? type : "button" self.props['onclick'] = onclick ? onclick : self.jsEmit( 'onclick', "null") // TODO: Remove this when we have automatic parentship self.addClassInParentship( Button ) end function renderContent() return self.content end /*# Turns this button in an AJAX form submitting button. @param w the form widget that is to be sent. @optparam confirmFunc A javascript function used to check if the form should be really sent. */ function makeSendFormAJAX( w, confirmFunc ) id = w.getFullID() xcall = @"Nest.i('$(id)').sendFormAJAX()" if confirmFunc xcall = "if(" + confirmFunc + "){" + xcall + ";}" end self.props['onclick'] = xcall return self end end frameworks/nest/nest/widgets/Calendar.fal000066400000000000000000000522621176363201700210400ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Calendar.fal AJAX widgeting subsystem -- Calendar for date picking ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 12 Apr 2012 22:53:31 +0200 ------------------------------------------------------------------- (C) Copyright 2012: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in wid import from nest.js /*# Calendar form widget. @param id The widget ID @optparam currentDate the Date on which to "center" the Calendar default is today @optparam subject Another Nest widget that is used as "popup" base for this widget. @optparam popup set to true to force a popup behavior even without subject widget. This widget shows a calendar centered on a date. @code +=============================+ << span id == [widgetid] + [X] + << div id == [widgetid].bar class="windowbar" + |<< <| month / year |> >>| + << div id == [widgetid].head -> ... + + + Sun Mon ... Sat + << div id == [widgetid].intest (each span class="intest") + [ ] [ ] 1 2 .. + << div id == [widgetid].body + ... + << each row div id == [widgetid].row + ... + << each cell: span class='day' (or 'noday' if empty), 'today' or 'busyday' + [ 15 ] + << span class='today' + ... [25!] + << span class='xday' +=============================+ @endcode The buttons generate a message from current widget with message id: - nexty - nextm - prevy - prevm The div [widgetid].head is divided into 3 parts: - class = btnprev - class = curdate - class = btnnext Buttons id in order: - [widgetid].back1y (under span id=btnprev) - [widgetid].back1m (under span id=btnprev) - [widgetid].fwd1m (under span id=btnnext) - [widgetid].fwd1y (under span id=btnnext) @section base_inter Basic interaction The widget can be controlled through the following methoss: - 'goto( month, year)': Moves the current month/year as specified, or invokes the AJAX 'goto' message if this is an AJAX enabled widget. - 'selectDay( year, month, day )': Selects the given day. If the widget is AJAX enabled, it sends a 'selectDay' AJAX message to the host widget. If the widget is in popup mode, it is also closed. - 'close()': Closes the widget. If the widget is AJAX enabled, it sends a 'close' AJAX message to the host widget. If the widget is in popup mode, it is also closed. The following callbacks can be overriden to change the default behavior: - onGoto( month, year ): when the a goto action is performed; if the method returns false, no action is taken. - onDaySelected( year, month, day ): when a day is selected and is going to be communicated to a subject widget (if any). If the method returns false, no action is performed, otherwise, if the widget is in popup mode, it will be hidden. - onClose(): called back when the user is explicitly closing the widget in popup mode. If the method returns false, no action is taken. AJAX processing takes precedence with respect to calling this callbacks; if the widget is AJAX enabled, the AJAX messages are invoked, and then, if the AJAX processor confirms the action, the above callbacks are called. When the action is actually confirmed and performed, the following Nest messages are invoked. - 'onCreate': after the widget is initialized - 'onGoto': with parameter {year, month} whenever the date is changed. - 'onDaySelected': {year, month, day} when a certain day is selected. - 'onClose': when the calendar is explicitly closed; it has also a {year, month} parameter, which indicates from which month/year it was closed. @section ajax AJAX interaction. If the isAJAXHost property is true, then this calendar is considered an AJAX enabled calendar. Calls to goto(), selectDay() and close() JS methods are deviated to the ajax host as described in the previous section. To confirm the actions the following methods can be invoked via an AJAX @a Widget.msgInvoke request: - AJAXgoto( {year, month, busyDays}): busyDays is an array of numbers (1-31) that indicates which days in the calendar are to be marked as "busy". Can be not given or null if there aren't busy days. - AJAXselectDay( {year, month day}): confirms the selection of the day. - AJAXclose(): confirms the action of closing this widget. @section subject_widget Actions on a subject widget. If a subject widget is provided, it is altered at javascript side adding two methods: - onfocus: event callback, to be called when the widget is onfocus; the effect is that of popping up the calendar at an adequate position and using the date that was previously set. It's default action is that of calling this widget's 'popup(month,year)'. - onDaySelected( year, month, day ): called back when a day is selected in the engine; it's default effect is that of setting the year, month and day properties in the widget, and setting it's value as a YYYY-MM-DD string. If the widget has jsMethods for "onfocus" and/or "onDaySelected", then the relative method is not created. */ class Calendar(id, currentDate, subject, popup) from wid.Widget( id ) tag = "span" currentDate = nil isSelfClosing = false isAJAXHost = false cellWidth = 35 cellHeight = 30 subject = subject monthNames = .[ i"January" i"February" i"March" i"April" i"May" i"June" i"July" i"August" i"September" i"October" i"November" i"December"] dayNames = .[ i"Sun" i"Mon" i"Tru" i"Wed" i"Thu" i"Fri" i"Sat"] firstDay = 0 jsMethods = [ "busyDays" => "Array()", "subject" => "null", "useAjax" => "false", "isPopup" => subject or popup ? "true" : "false", "daysPerMonth" => " function( month, year ) { var dpm = Array(31,28,31,30,31,30,31,31,30,31,30,31); var days = dpm[month-1]; if ( days == 28 && this.isLeapYear( year ) ) days = 29; return days; }", "isLeapYear" => " function isleap( year ) { var yr = parseInt(year); if (yr%4 == 0) { if (yr%100 == 0) { return yr%100 != 0 || yr%400 == 0; } return true; } return false; }", "create" => " function() { var self = this; this.style.paddingTop=0; var bar = document.createElement('div'); bar.id = this.id + '.bar'; bar.style.height='16px'; bar.style.paddingRight='9px'; bar.style.paddingLeft='9px'; bar.className = 'windowbar'; if( this.isPopup ) { var closebtn = document.createElement('button'); closebtn.id = this.id + '.closebtn'; closebtn.className = 'closebtn'; closebtn.innerHTML='X'; closebtn.style.width = '30px'; closebtn.style.height = '13px'; closebtn.style.fontSize = '8pt'; closebtn.style.padding = '0'; closebtn.style.margin = '0pt'; closebtn.style.fontWeight = 'bold'; closebtn.style.fontFamily = 'arial, sans'; closebtn.style.position = 'absolute'; closebtn.style.top = '0'; closebtn.style.right = '20px'; closebtn.onclick = function() { self.close(); }; bar.appendChild( closebtn ); this.style.display = 'none'; } var head = document.createElement('div'); head.style.clear = 'both'; head.id = this.id + '.head'; this.createHead( head ); this.head = head; var intest = document.createElement('div'); intest.id = this.id + '.intest'; this.createIntest( intest ); this.appendChild( bar ); this.appendChild( head ); this.appendChild( intest ); Nest.message( this, 'onCreate', {year:this.year, month:this.month} ); if( ! this.isPopup ) this.goto( this.month, this.year); }", "createHead" => " function( headDiv ) { var self = this; var btnBack1Year = document.createElement('button'); btnBack1Year.id = this.id + '.back1y'; btnBack1Year.innerHTML ='<<'; btnBack1Year.onclick = function(){ self.backOneYear(); }; var btnBack1Month = document.createElement('button'); btnBack1Month.id = this.id + '.back1m'; btnBack1Month.innerHTML ='<'; btnBack1Month.onclick = function(){ self.backOneMonth(); }; var btnprev = document.createElement('span'); btnprev.className = 'btnprev'; this.curMonthSpan = document.createElement('span'); this.curMonthSpan.id = this.id + '.current'; this.curMonthSpan.className = 'curdate'; var btnFwd1Month = document.createElement('button'); btnFwd1Month.id = this.id + '.fwd1m'; btnFwd1Month.innerHTML ='>'; btnFwd1Month.onclick = function(){ self.fwdOneMonth(); }; var btnFwd1Year = document.createElement('button'); btnFwd1Year.id = this.id + '.fwd1y'; btnFwd1Year.innerHTML ='>>'; btnFwd1Year.onclick = function(){ self.fwdOneYear(); }; var btnnext = document.createElement('span'); btnnext.className = 'btnnext'; btnnext.style.float = 'right'; btnprev.appendChild( btnBack1Year ); btnprev.appendChild( btnBack1Month ); btnnext.appendChild( btnFwd1Month ); btnnext.appendChild( btnFwd1Year ); headDiv.appendChild( btnprev ); headDiv.appendChild( this.curMonthSpan ); headDiv.appendChild( btnnext ); this.setCurMonth(); } ", "setCurMonth" => " function() { if (navigator.appName == 'Microsoft Internet Explorer') { setTimeout( 'Nest.i(\"' + this.id + '\").resetCurMonth()', 1 ); } else { var mname = this.monthNames[ this.month - 1 ]; this.curMonthSpan.innerHTML = mname + \" \" + this.year; } }", "resetCurMonth" => " function() { var mname = this.monthNames[ this.month - 1 ]; var nMonthSpan = document.createElement('span'); nMonthSpan.id = this.id + '.current'; nMonthSpan.className = 'curdate'; nMonthSpan.innerHTML = mname + \" \" + this.year; this.head.replaceChild( nMonthSpan, this.curMonthSpan ); this.curMonthSpan = nMonthSpan; } ", "refresh" => " function() { var self = this; if( this.body ) { this.removeChild( this.body ); } this.body = document.createElement('div'); this.body.id = this.id + '.body'; this.appendChild( this.body ); var days = this.daysPerMonth( this.month, this.year ); var curday = 1; /* put in the first row */ var row = document.createElement('div'); row.className = 'row'; /* calculate the first day of this month */ var today_date = new Date(); var today = 0; if( this.month == today_date.getMonth()+1 && this.year == today_date.getFullYear() ) { today = today_date.getDate(); } var d = new Date( this.year, this.month-1, 1 ); var dow = d.getDay() - this.firstDay; if( dow < 0 ) dow = 6; var rowday = 0; /* fill with nodays */ var nodays = 0; while( nodays < dow ) { var noday = document.createElement('span'); noday.className = 'noday'; noday.style.width= this.cellWidth + 'px'; noday.style.height= this.cellHeight+ 'px'; noday.style.display='inline-block'; noday.innerHTML = ' '; row.appendChild( noday ); ++nodays; ++rowday; } while( curday <= days ) { var day = document.createElement('span'); day.className = 'day'; if ( curday == today ) { day.id = this.id + '.today'; day.className = 'today'; } if( this.busyDays.indexOf(curday) != -1 ) { day.className = 'busyday'; } day.innerHTML = curday; day.style.width= this.cellWidth + 'px'; day.style.height= this.cellHeight+ 'px'; day.style.display='inline-block'; day.day = curday; day.onclick = function() { self.selectDay( self.year, self.month, this.day ); }; row.appendChild( day ); ++curday; ++rowday; if( rowday == 7 ) { this.body.appendChild( row ); var row = document.createElement('div'); row.className = 'row'; rowday = 0; } } this.body.appendChild( row ); this.setCurMonth(); } ", "createIntest" => " function( headDiv ) { for( var i = this.firstDay; i < 7+this.firstDay; i = i + 1 ) { var dname = i; if ( dname > 6 ) dname = dname - 7; var inner = document.createElement('span'); inner.style.width= this.cellWidth + 'px'; inner.style.display='inline-block'; inner.className = 'intest'; inner.innerHTML = this.dayNames[dname]; inner.style.width= this.cellWidth + 'px'; headDiv.appendChild( inner ); } } ", "goto" => " function( month, year ) { if( this.useAjax ) { Nest.widgetAJAX( '" + self.className() + "'"+" , this.id, 'goto', {month: month, year: year } ); } else if( this.onGoto( month, year ) ) { Nest.message( this, 'onGoto', {year:year, month:month } ); this.month = month; this.year = year; this.refresh(); } }", "selectDay" => " function( year, month, day ) { if( this.useAjax ) { Nest.widgetAJAX( '" + self.className() + "'"+" , this.id, 'selectDay', {month: month, year: year, day:day } ); } else if( this.onDaySelected( year, month, day ) ) { Nest.message( this, 'onDaySelected', {month: month, year: year, day:day} ); if (this.subject && subject.onDaySelected ) { subject.onDaySelected( year, month, day ); } if( this.isPopup ) this.style.display = 'none'; } }", "close" => " function() { if( this.useAjax ) { Nest.widgetAJAX( '" + self.className() + "'"+" , this.id, 'close', {month: this.month, year: this.year } ); } else if( this.onClose() ) { Nest.message( this, 'onClose', {year:this.year, month:this.month} ); if( this.isPopup ) this.style.display = 'none'; } }", "AJAXgoto" => " function( params ) { if( this.onGoto( params.month, params.year ) ) { Nest.message( this, 'onGoto', {year:params.year, month:params.month } ); if( params.busyDays != null ){ this.busyDays = params.busyDays; } this.month = params.month; this.year = params.year; this.refresh(); } }", "AJAXselectDay" => " function( params ) { if( this.onDaySelected( params.year, params.month, params.day ) ) { Nest.message( this, 'onDaySelected', {month: params.month, year: params.year, day:params.day} ); if (this.subject && subject.onDaySelected ) { subject.onDaySelected( params.year, params.month, params.day ); } if( this.isPopup ) this.style.display = 'none'; } }", "AJAXclose" => " function() { if( this.onClose() ) { Nest.message( this, 'onClose', {year:this.year, month:this.month} ); if( this.isPopup ) this.style.display = 'none'; } }", "onGoto" => " function( year, month ) { return true; } ", "onDaySelected" => " function( year, month, day ) { return true; }", "onClose" => " function() { return true; } ", "popup" => " function( year, month, day ) { this.style.position = 'absolute'; Nest.reposition( Nest.i(this.subject), this, 20, 20, this.cellHeight*7 ); if( year != null && month != null ) { this.goto( month, year ); } else { this.goto( this.month, this.year); } this.style.display = 'inline-block'; }", "backOneYear" => "function(){ this.goto( this.month, this.year -1 ); }", "backOneMonth" => " function(){ var month = this.month - 1; var year = this.year; if (month == 0 ) { year = year - 1; month = 12; } this.goto( month, year); }", "fwdOneMonth" => " function(){ var month = this.month + 1; var year = this.year; if (month == 13 ) { year = year + 1; month = 1; } this.goto( month, year ); }", "fwdOneYear" => "function() { this.goto( this.month, this.year +1 );}", "configSubject" => " function() { if( this.subject != null ) { var subject = Nest.i( this.subject ); subject.year = null; subject.momth = null; subject.day = null; var self = this; if( subject.setAttribute == undefined ) { subject.onfocus = 'var v=Nest.i(\"' +self.id +'\"); if(v) v.popup()'; subject.onclick = 'Nest.i(\"' +self.id +'\").popup()'; } else { subject.setAttribute('onfocus', 'var v=Nest.i(\"' +self.id +'\"); if(v) v.popup()'); subject.setAttribute('onclick', 'Nest.i(\"' +self.id +'\").popup()'); } if( subject.onkeyup == null ) { subject.onkeyup = function( event ) { if ( event.which == 27 || event.keyCode == 27 ) { if( self.style.display == 'inline-block' ) self.close(); } } } if( subject.onDaySelected == null ) { subject.onDaySelected = function( year, month, day ) { this.year = year; this.month = month; this.day = day; this.value = year +'-'+month+'-'+day; } } } }" ] onCreate = "this.configSubject(); this.create();" init // activate effects Nest.requireJS("fx") self.set( ["class" => "calendar", "style" => "display: inline-block; zoom:1;"] ) if currentDate self.currentDate = currentDate else self.currentDate = TimeStamp() self.currentDate.currentTime() end // TODO: Remove this when we have automatic parentship self.addClassInParentship( Calendar ) end function onRender() self.jsMethods["month"] = self.currentDate.month self.jsMethods["year"] = self.currentDate.year self.jsMethods["firstDay"] = self.firstDay self.jsMethods["cellWidth"] = self.cellWidth self.jsMethods["cellHeight"] = self.cellHeight self.jsMethods["dayNames"] = "Array('" + "','".merge( self.dayNames ) + "')" self.jsMethods["monthNames"] = "Array('" + "','".merge( self.monthNames ) + "')" if self.subject self.jsMethods["subject"] = '"' + self.subject.getFullID() +'"' self.jsMethods["isPopup"] = "true" end if self.isAJAXHost self.jsMethods["useAjax"] = true end end function renderContent() return "" end function AJAX( params ) raise "Please override the AJAX method" end end frameworks/nest/nest/widgets/CheckBox.fal000066400000000000000000000035421176363201700210120ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: CheckBox.fal AJAX widgeting subsystem -- Checkbox widget. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 02 Oct 2011 13:53:51 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget /*# Checkbox form widget. @param id The widget ID @param value value of the radio button in the @param text Text displayed at radio button (label) Radio buttons are the only widgets that share the same name across different widgets with different ids. This fact is enforced during the onRender phase by filing the correct name in the widget properties. Radio button selections are seen as arrays by the receiving scripts, with values in the array being the checked widgets. To achieve this, the name of the checkbox in the input tag gets added square parenthesis, marking them as array web fields. */ class CheckBox( id, value, text ) from widget.Widget( id ) label = text tag = "input" checkName = "checked" init if self.props == nil: self.props = [=>] self.props['value'] = value self.props['name'] = nil // will be filled at render. self.props['type'] = 'checkbox' // TODO: Remove this when we have automatic parentship self.addClassInParentship( CheckBox ) end function onRender() if self.parent if self.parent provides isCheckBoxSet pname = self.parent.getFullID() + "[]" else pname = self.getFullID() end else pname = self.name end self.props['name'] = pname end end frameworks/nest/nest/widgets/CheckBoxSet.fal000066400000000000000000000051141176363201700214630ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: CheckBoxSet.fal AJAX widgeting subsystem -- Automated radio-button choice generator ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 02 Oct 2011 13:53:51 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Container from nest.widgets.Container as Container import CheckBox from nest.widgets.CheckBox as CheckBox /*# Automated CheckBox choice generator @param id The widget ID @optparam choices An array of choices (strings). @optparam tagtype Tag used for this container (defaults to fieldset). @optparam tag_beg HTML code to be put before the first child (defaults to ""). @optparam tag_beg HTML code to be put in between children (defaults to " "). @optparam tag_beg HTML code to be put after the last child (defaults to ""). This is a widget that automatically creates a set of radio buttons and adds them to the container. Each element in choice is a string with the following format: "value:text". If ':' is not in the choice string, then the whole text will be used both as a submit value and as a text. You can also add more children after the widget is created for better control on the apparence of each choice. */ class CheckBoxSet( id, choices, tagtype, tag_beg, tag_sep, tag_end ) from \ Container( id, tagtype, tag_beg, tag_sep, tag_end ) isCheckBoxSet = true isValueHost = true init self.autoAddChildren( choices ) // TODO: Remove this when we have automatic parentship self.addClassInParentship( CheckBoxSet ) end function onRender() checks = "var result = Array(); var item;" sets = "var item;" for child in self.children checks += " item=Nest.i('" + child.getFullID() + "'); if(item.checked) result.push(item.value);" sets += " item=Nest.i('" + child.getFullID() + "'); item.checked = value.indexOf(item.value) >= 0;" end self.jsMethods[ "getValue" ] = "function(){" + checks + "; return result;}" self.jsMethods[ "setValue" ] = "function(value){" + sets + "}" self.jsMethods[ "name" ] = "'" + self.getFullID() + "'" end function makeAutoChild( value, text, checked ) child = CheckBox( value, value, text ) if checked: child.props["checked"] = "checked" return child end end frameworks/nest/nest/widgets/Container.fal000066400000000000000000000054401176363201700212450ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Container.fal AJAX widgeting subsystem -- Generic container (seen as a field). ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 02 Oct 2011 13:53:51 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget /*# Generic container (seen as a field). @param id The widget ID @optparam tagtype Tag used for this container (defaults to fieldset). @optparam tag_beg HTML code to be put before the first child (defaults to ""). @optparam tag_sep HTML code to be put in between children (defaults to " "). @optparam tag_end HTML code to be put after the last child (defaults to ""). This is the base class for widget containers; it can be used to display multiple fields as a single field in forms. The default tag of this container is 'fieldset'. You should overload the class or reset it to another */ class Container( id, tagtype, tag_beg, tag_sep, tag_end ) from widget.Widget( id ) tag = tagtype ? tagtype : "span" isSelfClosing = false tag_beg = tag_beg ? tag_beg : "" tag_sep = tag_sep ? tag_sep : " " tag_end = tag_end ? tag_end : "" //# value of the checkbox set (array of checked box values) value = nil checkName = "checked" init // TODO: Remove this when we have automatic parentship self.addClassInParentship( Container ) end function renderContent() res = "" for child in self.children forfirst: res += self.tag_beg res += child.renderInParent( self ) formiddle: res += self.tag_sep forlast: res += self.tag_end end return res end //# Override this for personalized label/content/info display function renderChild( child ) return child.render() end function setValue( val ) if val.typeId() == ArrayType for item in val if item in self.childrenById child = self.childrenById[item] self.setChildChecked( child ) end end elif val in self.childrenById self.setChildChecked( self.childrenById[val] ) end end function setChildChecked( child ) if self.value == nil: self.value = [] if 'value' in child.props self.value += child.props['value'] elif 'name' in child.props self.value += child.props['name'] else self.value += child.id end child.props[self.checkName] = self.checkName end function getValue() return self.value end end frameworks/nest/nest/widgets/DateSelector.fal000066400000000000000000000067151176363201700217070ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: DateSelector.fal AJAX widgeting subsystem -- Container for dates. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 02 Oct 2011 13:53:51 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Container from nest.widgets.Container as Container import Select from nest.widgets.Select as Select import Option from nest.widgets.Option as Option import InputText from nest.widgets.InputText as InputText //import Regex from Regex as Regex /*# Selector for date. @param id The widget ID @optparam yearRange Range of years to be used to generate a year selector. @optparam tagtype Tag used for this container (defaults to fieldset). @optparam tag_beg HTML code to be put before the first child (defaults to ""). @optparam tag_sep HTML code to be put in between children (defaults to " "). @optparam tag_end HTML code to be put after the last child (defaults to ""). */ class DateSelector( id, yearRange, tagtype, tag_beg, tag_sep, tag_end ) from \ Container( id, tagtype, tag_beg, tag_sep, tag_end ) // declare we're not holding a value in AJAX isValueHost = false months_names = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ] short_months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ] num_months = [ "?", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" ] //re = Regex( "([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2})") days = nil months = nil years = nil isYearRange = false init self.makeMonths() self.days = Select( "day" ) self.days.addChild( Option("", "") ) for i = 1 to 31: self.days.addChild( Option( ""+i, i) ) self.months = Select( "month" ) self.months.addChild( Option("", "") ) for i = 1 to 12: self.months.addChild( Option( ""+i, self.months_names[i-1]) ) self.addChild( self.days ) self.addChild( self.months ) if yearRange self.years = Select( "year" ) for i in yearRange self.years.addChild( Option( ""+i, i) ) end self.isYearRange = true else self.years = InputText( "year" ) self.years.props['size'] = 4 self.years.props['maxlength'] = 4 end self.addChild( self.years ) // TODO: Remove this when we have automatic parentship self.addClassInParentship( DateSelector ) end /*# Override to change the name of the months. */ function makeMonths() end function setValue( val ) if val if val.typeId() == StringType vect = val.split('-') elif val.derivedFrom( TimeStamp ) vect = [ val.year.toString(), val.month.toString(), val.day.toString() ] end if(vect.len() == 3) self.years.setValue( vect[0].trim() ) self.months.setValue( vect[1].trim() ) self.days.setValue( vect[2].trim() ) end end end function getValue() return self.years.getValue() + "-" + self.months.getValue() + "-" + self.days.getValue() end end frameworks/nest/nest/widgets/Form.fal000066400000000000000000000176611176363201700202360ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Form.fal AJAX widgeting subsystem -- Form-like collection of widgets. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 28 Sep 2011 11:31:25 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Widget from nest.widgets.widget as Widget import JSP from nest.widgets.widget as JSP import Hidden from nest.widgets.Hidden as Hidden /*# Class presenting multiple widget for posting as a form. @param id The ID of the form. @optparam action The action of the form at submit. @optparam method The method (GET or POST), defaults to "POST". This class has support to route the input variables filling them into its children. The children will receive setValue callbacks with the value the Form expects them to receive, and may use this callback to check for correct values and eventually prepare their @b fieldInfo property with an error message. If the @b action is nil, the form is intended as generating an AJAX request that is sent to this widget. The default action of this widget is calling the hook "handle_form", which receives this form and the incoming data in a dictionary. The handler might then decide to apply the values via @a nest.Widget.routeValues or check the values directly from the dictionary. The routeValues method comes particularly handy when used in combination with a @a DataManager, as the data collected via routing can then be applied directly to the data manager. @note If the form is AJAX, it is given a method named "sendFormAJAX" on the Javascript side, and the onsubmit method is forcefully set to this method. it might be useful to use the onclick property of a button to invoke this method directly via 'Nest.i("form-id").sendFormAJAX()'. */ class Form( id, action, method ) from Widget( id ) tag = "form" // we need a proxy for isAJAXHost, as a parent might remove it // while we still need it for rendering purposes _isAJAXForm = false init if self.props == nil: self.props = [=>] self.props["accept-charset"] = "UTF-8" if action self.props["method"] = method ? method : "POST" self.props["action"] = action else self._isAJAXForm = true self.isAJAXHost = true end // TODO: Remove this when we have automatic parentship self.addClassInParentship( Form ) end /*# Sets the "accept-charset" property. @parm cs The charset. */ function setCharset( cs ) self.props["accept-charset"] = cs end /*# Sets the "enctype" property to "multipart/form-data". You'll want to call this when you have some file widget in the form. "FileUpload" widgets call it automatically when they are added to a form. */ function setSumbitData() self.props["enctype"] = "multipart/form-data" end /*# Helper to add a confirmation callback on a form. @param content The text that should be displayed in the confirm box. This will setup a simple confirm javascript on form submit. To do more sophisticated things, just set the 'onsubmit' property in Form.props to your preferred callback. */ function setConfirmSubmit( content ) self.props['onsubmit'] = 'return confirm("' + htmlEscape(content) + '");' end /*# Helper to add a reset callback on a form. @param content The text that should be displayed in the confirm box. This will setup a simple confirm javascript on form reset. To do more sophisticated things, just set the 'onreset' property in Form.props to your preferred callback. */ function setConfirmReset( content ) self.props['onreset'] = 'return confirm("' + htmlEscape(content) + '");' end /*# Adds an hidden value that is to be sent as variable. @param name The name of the field @param value The value to be assigned to the field. This is a helper automatically adding hidden fields to this form. The value will automatically be transformed in a set of dictionary or array values in case it's a dictionary or an array. */ function addHidden( name, value ) select value case ArrayType for val in value valstr = value.toString() field = Hidden( name + "." + valstr, valstr ) field.addName = "[]" self.addChild( field ) end case DictionaryType for key, val in value valstr = value.toString() field = Hidden( name + "." + key, valstr ) field.addName = @"[$key]" self.addChild( field ) end default field = Hidden( name, value ) self.addChild( field ) end end function onRender() vns = [] for child in self.children self._getValueNodes( vns, child ) end if self._isAJAXForm reqs = map( {child => JSP("*", "/" + child.getFullID() + "/value") }, vns ) self.jsMethods += [ "sendFormAJAX"=> "function(){" + self.ajaxMsg("sendFormAJAX", reqs ) + ";}" ] self.props['onsubmit'] = "this.sendFormAJAX(); return false;" end clearFields = map( { child => "Nest.setWidVal('" + child.getFullID() +"', '');" }, vns ) self.jsMethods += [ "clearFields"=> "function(){" + "".merge(clearFields) + "}", "setFields"=> " function( vals ) { var item; for( item in vals ) { Nest.setWidVal(this.id + '.' + item, vals[item] ); } }" ] end function getAllValues() vns = [] self._getValueNodes( vns, self ) data = [=>] for child in vns data[child.id] = child.getValue() end return data end function msgSetAllFields() data = self.getAllValues() return self.msgInvoke( "setFields", data ) end function _getValueNodes( vns, child ) // use */ to send by name and not by ID if child.isValueHost: vns += child for subchild in child.children self._getValueNodes( vns, subchild ) end end /*# Overrides the base widget render content. This version calls: - renderFormBegin before rendering each widget. - renderChild for every child. - renderFormEnd after rendering all the children. Each method must return a string that is the rendered representation of the element. */ function renderContent() rend = self.renderFormBegin() for child in self.children rend += child.renderInParent( self ) +"\n" end return rend + self.renderFormEnd() end /*# Called back in rendering each element of the form. @param child A child widget of this form. The base version just renders the prompt via child.renderPrompt() and then renders the body via child.render(). */ function renderChild( child ) return child.renderPrompt() + child.render() end /*# Renders the start entry of the form elements. @return A string that is used before rendering any element. The base class returns an empty string. */ function renderFormBegin() return "" end /*# Renders the end entry of the form elements. @return A string that is used before after rendering all the elements. */ function renderFormEnd() return "" end /*# Base implementation of the AJAX form callback. */ function AJAX( params ) data = Nest.emit( "handle_form", self, params ) if data: return data return [=>] end end /* end of Form.fal */ frameworks/nest/nest/widgets/Hidden.fal000066400000000000000000000020531176363201700205130ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Hidden.fal AJAX widgeting subsystem -- input field. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget /*# Hidden fields. @param id The id (and name) of this widget. @param value The value. */ class Hidden( id, value ) from widget.Widget( id ) tag = "input" isSelfClosing = true isValueHost = true init if self.props == nil: self.props = [=>] self.props['name'] = nil // will be filled at render self.props['type'] = "hidden" self.props['value'] = value ? value : "" // TODO: Remove this when we have automatic parentship self.addClassInParentship( Hidden ) end end frameworks/nest/nest/widgets/Image.fal000066400000000000000000000020051176363201700203370ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Image.fal AJAX widgeting subsystem -- dynamic images. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget class Image( id, src, status ) from widget.Widget( id ) tag = "img" status = status initInfos = [id, src, status] isSelfClosing = true init if self.props == nil: self.props = [=>] self.props['src'] = src if status != nil and not status self.props['style'] = "display:none" else self.props['style'] = "" end // TODO: Remove this when we have automatic parentship self.addClassInParentship( Image ) end end frameworks/nest/nest/widgets/InfoBox.fal000066400000000000000000000115101176363201700206620ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: InfoBox.fal AJAX widgeting subsystem -- Inofrmative popup widget. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 02 Oct 2011 13:53:51 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget class _InfoBoxText(id, title, content) from widget.Widget( id ) content = content imgclose= Nest.resLink( "infobox_close.png" ) title = title titleHeight = 23 width = 350 height = 200 fadeInTime = 600 fadeOutTime = 250 jsMethods = [ "show" => @' function(itemID) { Nest.reposition( Nest.i(itemID), this, 10, 10, $(self.height), $(self.width) ); var scroller = Nest.i(this.id + ".scroller" ); this.style.display = ""; this.style.overflow= "hidden"; scroller.style.overflow= "hidden"; var self = this; var trans = { object: this, time: $(self.fadeInTime), fromstyle: { "width": 30, "height": 10, "opacity": 0.2 }, tostyle: { "width": "|$(self.width)|px", "height": "|$(self.height)|px", "opacity": 0.9 }, ondone: function(){ scroller.style.overflow = "auto"; } }; Nest.addTransition( trans ); }', "hide" => @' function() { var self = this; var hidef = function() { self.style.display = "none"; } Nest.transite( this, $(self.fadeOutTime), {"opacity":0}, {"opacity":0.9}, null, hidef ); }' ] init if self.props == nil: self.props = [=>] self.props["style"] = " display:none; padding:0; margin:0; position:absolute; " self.props["class"] = "nest_InfoBoxText" // TODO: Remove this when we have automatic parentship self.addClassInParentship( _InfoBoxText ) end function renderContent() fullID = self.getFullID() hc = self.height - self.titleHeight return @"
    $(self.title)
    $(self.content)
    " end end /*# Inofrmative popup widget. @param id The widget ID @param content The HTML content of the text box @optparam icon Icon location used for the informative popup. This method generates a popup that gets displayed when the user clicks the given icon (by default, "res/info.png"). Style components: @code (I) << img id=[widget id] widget class InfoBox +++++++++++++++++++++++++++++++++++++++++ << widget class _InfoBoxText + Title bar [X] + << div id=[widget id].titlebar class=titlebar +++++++++++++++++++++++++++++++++++++++++ + + << div id=[widget id].contentbox class=contentbox + + + + + + + + + + + + +++++++++++++++++++++++++++++++++++++++++ @endcode */ class InfoBox( id, title, content, icon ) from widget.Widget( id ) tag = "img" content = content icon = icon? icon : Nest.resLink("info.png") isSelfClosing = true infobox = _InfoBoxText( "infobox", title, content ) init // activate effects Nest.requireJS("fx") // add the infobox self.addChild( self.infobox ) if self.props == nil: self.props = [=>] self.props["border"] = "0" // TODO: Remove this when we have automatic parentship self.addClassInParentship( InfoBox ) end function onSetup() self.jsMethods = [ "onclick" => "function(event) {" + self.infobox.jsCall( 'show', self.getFullID() ) +" ;Nest.eatEvent(event); }" ] end function onRender() self.props["src"] = self.icon end end frameworks/nest/nest/widgets/InputText.fal000066400000000000000000000073151176363201700212720ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: InputText.fal AJAX widgeting subsystem -- input field. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget /*# Input text */ class InputText( id, prompt, iPrompt ) from widget.Widget( id ) tag = "input" isSelfClosing = true iPrompt = iPrompt label = prompt ? prompt : "" //# Style color to be applied on the "empty field" entity blurcolor = "#999" //# in forms, we do have values isValueHost = true init if self.props == nil: self.props = [=>] self.props['name'] = nil // will be filled at render self.props['type'] = "text" // TODO: Remove this when we have automatic parentship self.addClassInParentship( InputText ) end function onRender() if self.iPrompt self.props['onblur'] = "this.checkIPrompt();" self.props['onfocus'] = "this.disableIPrompt();" if self.onCreate self.onCreate += "this.checkIPrompt();" else self.onCreate = "this.checkIPrompt();" end self.jsMethods["setfocus"] = " function(){ var other = Nest.i(this.id + '.iprompt'); if ( other != null && other.style.display != 'none' ){other.focus();} else{ this.focus();} }" self.jsMethods["checkIPrompt"] = @" function() { var other = Nest.i(this.id + '.iprompt'); if(this.value.length == 0 ) { this.style.display = 'none'; other.style.display = ''; } else { other.style.display = 'none'; this.style.display = ''; } }" self.jsMethods["disableIPrompt"] = " function() { Nest.i(this.id + '.iprompt').style.display = 'none'; this.style.display = ''; this.focus(); }" else self.jsMethods["setfocus"] = "function(){this.focus();}" end end function renderCore() res = self.widget.Widget.renderCore() if self.iPrompt id = self.getFullID() res += @"" end return res end function onEnter( jsFunc ) onkeydown = "if( event.which == 13 || event.keyCode == 13 ) this.onEnter();" if 'onkeydown' in self.props self.props['onkeydown'] += onkeydown else self.props['onkeydown'] = onkeydown end self.jsMethods["onEnter"] = jsFunc end function onEsc( jsFunc ) onkeydown = "if( event.which == 27 || event.keyCode == 27) this.onEsc();" if 'onkeydown' in self.props self.props['onkeydown'] += onkeydown else self.props['onkeydown'] = onkeydown end self.jsMethods["onEsc"] = jsFunc end end frameworks/nest/nest/widgets/Link.fal000066400000000000000000000032031176363201700202130ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Link.fal AJAX widgeting subsystem -- Simple widget implementation of A tag ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 02 Oct 2011 13:53:51 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Widget from nest.widgets.widget as Widget /*# Widget representing an hyperlink ('a' HTML tag). @param id The id of the link @param href The location where the link is pointing to. @param content A string or a widget that will be rendered as link target. @optparam target A "target" property to be set in the 'a' HTML element. If the @b href is prefixed with "!", then the target is intended to be a link to a Nest page. */ class Link( id, href, content, target ) from Widget( id ) tag = "a" target = target href = href content = content init if content provides addChild self.addChild( content ) end if href.startsWith( "!" ) href = Nest.pageLink( href[1:] ) end settings = ["href" => href] if target: settings["target" ] = target self.set( settings ) // TODO: Remove this when we have automatic parentship self.addClassInParentship( Link ) end function renderContent() if not self.content provides render return self.content else return self.content.render() end end end frameworks/nest/nest/widgets/ListForm.fal000066400000000000000000000037771176363201700210750ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: ListForm.fal AJAX widgeting subsystem -- Form rendered as a list of items ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 28 Sep 2011 11:31:25 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.Form in wid /*# List-specialized form. This specialization of the form class is built to render its own children as a list of items. It is very common to use a stylesheet to transform UL/LI/label element sequences into well formatted fields. */ class ListForm( id, action, method ) from wid.Form( id, action, method ) init // TODO: Remove this when we have automatic parentship self.addClassInParentship( ListForm ) end /*# Called back in rendering each element of the form. @param child A child widget of this form. The base version just renders the prompt via child.renderPrompt() and then renders the body via child.render(). */ function renderChild( field ) return "
  • " + self.labelFraming( field ) + " " + field.renderCore() + " " + field.renderInfo() + "
  • \n" end /*# Renders the start entry of the form elements. @return A string that is used before rendering any element. The base class returns an empty string. */ function renderFormBegin() return "
      \n" end /*# Renders the end entry of the form elements. @return A string that is used before after rendering all the elements. */ function renderFormEnd() return "
    \n" end function labelFraming( child ) id = child.getFullID() return "" end end /* end of Form.fal */ frameworks/nest/nest/widgets/LoginMask.fal000066400000000000000000000160611176363201700212100ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: LoginMask.fal AJAX widgeting subsystem -- A standard login mask. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 08 Apr 2012 15:30:25 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget import InputText from nest.widgets.InputText as InputText import Password from nest.widgets.Password as Password import CheckBox from nest.widgets.CheckBox as CheckBox import Link from nest.widgets.Link as Link import Button from nest.widgets.Button as Button import Submit from nest.widgets.Submit as Submit import Text from nest.widgets.Text as Text import Image from nest.widgets.Image as Image class LoginBtn from Button( "login" ) jsMethods = [ "onRequest"=> self.makeEmit( 'change', 'this.value' ), "sendLogin"=> .[self.makeAjaxMsg "sendLogin" .[ widget.JSP("/uid", "../user-id/value") widget.JSP("/pwd","../pwd/value") widget.JSP("/remember","../rememberMe/checked")]] ] props = ["onclick" =>"this.onRequest();this.sendLogin();" ] init // TODO: Remove this when we have automatic parentship self.addClassInParentship( LoginBtn ) end end /*# Manages a full login process. @param id The id of logins. @param formAction If not nil, use a form for logins, and send the login data to that action. */ class LoginMask( id, formAction ) from widget.Widget( id ) formAction = formAction jsMethods = [ "onLoginFailed" => "function( params ) { Nest.message( this, 'login', null ); }", "onLoginSuccess" => "function( params ) { Nest.message( this, 'login', params ); }" ] userID = InputText( "user-id") pwd = Password( "pwd" ) userid_prompt = "User ID" pwd_prompt = "Password" remember_prompt = "Remember me" login_prompt = "Login" userid_iprompt = nil pwd_iprompt = nil tag = "span" rememberMe = CheckBox( "rememberMe" ) loginBtn = formAction ? Submit("login", "") : LoginBtn() accessStatus = Text( "status", "Login failed!" ) loginBusy = Image( "busy-ind", Nest.resLink("ajax-loader.gif"), false ) recover = nil // Link( "recover", recoverLink, "Forgot my password" ) //# A default skin (to be configured) recover_skin = "
    " //# A default skin (to be configured) skin = " $(form_begin)

    Preferences

    Sorry, this is only a test, and we're not using a database.

    ... ... @endcode The @b side name for the Menu service instructs Nest to create a separate instance of the service; its configuration, skin and resources are to be found in **nest/srv/Menu/side**. This is the configuration in config.fal: @code // Configuration for the Menu service -- "side" import MenuItem from nest.srv.Menu as MenuItem items = .[ MenuItem( "First seciton", "#first" ) MenuItem( "Second seciton", "#second" ) MenuItem( "Third seciton", "#third" ) ] @endcode @section qs_srv_skin Service skins The service skin can be set changing the @a Service.skin configuration variable, or simply writing a skin file (default skin.fal) in the service home directory: the site-specific skin will take priority. We'll use the main service rendered through a non-standard skin to illustrate this feature. Adding the following line after towards the end of our frame, we can create the typical footer navigation-menu that is commonly see in most modern sites: @code @endcode The service will search for configuration and skin in under **nest/srv/Menu/footer**, but when not finding the configuration there, it will fall back to the global Menu configuration file; the same for our main menu. This simplified skin.fal will do the trick: @code

    $(item.text)' end end > "  -  ".merge( entries ) ?>

    @end @note A skin named "skin_footer.fal" is available as a part of the standard Menu service. A similar effect can be achieved setting the "skin" configuration variable in the service invocation call: @code "skin_footer.fal" ] ).render() ?> @endcode */ frameworks/nest/docs/service.fd000066400000000000000000000422311176363201700171220ustar00rootroot00000000000000/* @page services Anatomy of a Service Services are functional modules of Nest that perform background operations on behalf of the calling page, coordinate with each other to achieve a common result and then, optionally, are rendered as a part of the final page. A service can be loaded by a Nest page using the @a Nest.service method; invoking the same service more than once causes the already loaded service to be accessed from different place. For example: @code // Loads the login service x = Nest.service( "Login" ) ... ... y = Nest.service( "Login" ) > x == y // true. @endcode A page may also desire more copies of the same service to be ative at once; for example, one may desider to see its own calendar and it's organizational unit calendar in two separate elements of the same page. The second parameter of the @a Nest.service method is called the __instance name__, and can be used for that purpose. @code my_cal = Nest.service( "Calendar", "mine" ) office_cal = Nest.service( "Calendar", "office" ) > my_cal == office_cal // false @endcode @section service_cfg Service Configuration Services can read their own configuration both from falcon files placed in their Nest service tree and from a dictionary they receive as a third optional parameter in the @a Nest.service call. Nest loads any file named {{config.fal}} under {{srv/}} in the Nest active directory, and extracts the variables found at global level putting them at disposal of the services. For example, the configuration for a "Calendar" service may be placed under {{nest/srv/Calendar/config.fal}}, and look like this: @code // A nice color for the display? color = "#FFEE00" // Month or week? display = "month" welcome_cb = function( name ) return i@"Welcome $name!" end @endcode As in the example, configuration files can declare simple data or include even complex logic, to be performed at load time (in the configuration file main code) or to be used as callbacks by the service on need. Services with an instance name can have their own configuration placed under the {{config}} subdirectory under the service location, named after their instance name. So, a Calendar service being loaded as "mine" instance will search for its configuration under {{nest/srv/Calendar/mine/mine.fal}}. If that file is not found, the file {{config.fal}} will be loaded instead. @subsection srv_live_cfg Live configurations Other than load a static configuration from a pre-existing source file, services can be given a configuration through a dictionary passed as the third parameter of @a Nest.service. The following code snippet achieves a result similar to the just seen example. @code my_cal = Nest.service( "Calendar", "mine", [ "color" => "#FFEE00", "display" => "month" "welcome_cb" => {name => i@"Welcome $name!" } ]) @endcode The values passed to the service in this way override the ones eventually found in the configuration file, and allow for dynamic reconfiguration of services at load time. @subsection service_cfg_prio Configuration priority. The service will load only the first configuration file it finds. If there is an instance-specific configuration file, that one will be loaded; if not found, the main service file will be loaded, and if none if found, a default file may be loaded from the **nest** main directory. @note In case it is useful for a service configuration module to load a master common configuration, it may use import and then republish the variables locally, as in the following example: @code import from "../common.fal" in common // declare (repeat) the used common variables var1 = common.var1 var2 = common.var2 local = "A local variable" @endcode @subsection service_cfg_callback A simple way for a service to read its configuration is just that of overriding the @a Service.configure method. The method receives the dictionary of configuration key/value pairs as the parameter. The default behavior is that of registering the dictionary in the @a Service.configuration property, from where configuration settings can be read later on by the @a Service.setup or @a Service.run methods. Other than that, the service variables may be automatically bound with some configuration keys; for that, see later the paragraph @a binder_config. @section srvice_vars Service variables Service variables are special variables declared in the service class body through the following statement. @code ... srv_var = ServiceVar( [binding] ) ... @endcode Service variables are actually instances of a class named @a ServiceVar, which has some special features. - Their value can be automatically injected at startup by Nest through the binding mechanism. - They can be "listened" for change "asynchronously". - Their value is extracted and provided as a blessed dictionary to skins during the render step. Bindings are algorithm through which the variables can be initialized or saved. All the bindings are found in the @b nest.bindings module, and are usually imported as: @code import from nest.bindings in b ... class MyService( instance ) from Service( instance ) ... srv_var = ServiceVar( b.InputBinder ) ... @endcode @note Be careful when using short prefix names; when you create a namespace like "b", then "b" becomes invalid as a variable, property or symbol name. We'll briefly discuss the various bindings algorithms in this section. Service variables are actually instances of the @a ServiceVar class. As such, their value must be accessed throught the @a ServiceVar.value accessor. The @b ServiceVar.isSet accessor will indicate if a value (even nil) has been saved in the the value property, that is if the variable has been directly set. @note Both the @a ServiceVar.value and @a ServiceVar.isSet properties are actually accessors, and so, setting and reading them is a relatively heavy operation. Cache those properties in a local variable when possible. Service variables needs not to be bound. It may be useful to create a plain service variable without a direct binding so that its value is set during the @a Service.run process, and passed as data to be rendered to the service skins. @subsection var_listen Listening to service variables. Whenever the value of a service variable changes, it is possible to notify automatically listeners. To subscribe to service variable changes, the @a ServiceVar.listen method can be used. The registered callback function will be called with the new value as the parameter. @note If the handler needs to know the name or context of the service variable, use a callable array storing this informations as the callback item, like in the following example: @code function onThingsChanged( srv, varname, value ) > @"Variable $(varname) changed into $(value) in service "+ srv.fullName() end service = Nest.service( "TheService" ) service.some_var.listen( .[ onThingsChanged service "some_var" ] ) @endcode @subsection binder_config Configuration variable binding Configuration variables can be bound to servive variables through the @a ConfigBinder object. Creating a @a ServiceVar instance with @a ConfigBinder as the parameter will cause the values of configuration variables with the same name of the property to be automatically stored before the @a Service.startup method is invoked. @subsection binder_input Input variable bindings Input variables can be bound to servive variables through the following binders: - @a GetBinder: binds fields sent via HTTP queries. - @a PostBinder: binds fields sent via HTTP POST requests (usually, HTML form fields). - @a CookieBinder: binds fields sent via HTTP Cookie request headers. - @a WebVarBinder: binds fields sent via HTTP Cookie request headers, POST fields or GET queries, whichever comes first. - @a InputVarBinder: binds the varable with one an input value (generally POST or GET fields) after that it has been processed by the Nest input variable filter (@a Nest.filter). Creating a @a ServiceVar instance with any of the above classes as the parameter will cause the values of some of the input sources in web applications to be set as the variable value before the @a Service.startup method is invoked. If the variable is not present in the input source, the @a ServiceVar.value property will be nil and @a ServiceVar.isSet will be false. Keep in mind that all the input values are strings, or array/dictionaries of strings. If the variable should semantically be a nunber, it is necessary to transform it via the @a int or @a float functions. @subsection binder_session Session variable bindings The session binder (@a SessionBinder object) can be used to make a server variable persistent across sessions that is, across calls coming from the same remote user (registered or not). The value of service variables may be anything that can be safely serialized and restored across a stream through the falcon standard @a serialize function. To know if the value of session variables has been properly restored, it is enough to check the @a ServiceVar.isSet member. It will be true in case of full restore. In case the variables are not restored, the property @a Nest.sessionExpired can be checked to determine if the incoming entity has been assinged a new session (@a Nest.sessionExpired == false), or if it was owner of a session that has been destroyed in or closed in the meanwhile. Error in restoring the session would be traced before the services are possibly loaded. @subsection ambient_vars Ambient Service variables Nest provides a simple way to share the exact same service variable across different modules. The @a Nest.ambient method allows to create a Service Variable, eventually bound, that can be then invoked by name by different modules as well as from the calling application. For example, @code // Srv1.fal class Srv1(inst) from Service(inst) my_var = Nest.ambient( "ambient_var" ) ... end // Srv2.fal class Srv2(inst) from Service(inst) some_var = Nest.ambient( "ambient_var" ) ... end @endcode In the above example, the "ambient_var" variable is shared across the two services, where it comes to be locally known as @b my_var or @b some_var (althought it is reccomendable to use the same name for every user). The service variable created in this way is exactly the same instance for every user referencing it. However, the module defining it may decide to use a binder to initialize its value, passing it as a second optional paramter of @a Nest.ambient. The variable can be referenced without a binder by all the other modules or by the application; it will be properly bound to the owning module during the service resolution phase. @section serivce_skin Skins Skins are functions called to render the results of the work of a service to the final web page being formed. They usually (but not necessarily) are built to receive two paramters: - A dictionary of service variable names and the actual value they possesses. - The object instance of the calling service. Skins are usually stored in separate files that are to be found as fal, ftd or fam modules in the directories where service configuration data is found. They can be automatically loaded through a special binder called @a SkinBinder. Assigning a skin-bound service variable to a property marks it as a skin property. Upon resolution, the skin property will contain the function to be called to render the service. The @a Service base class has a property named @a Service.skin which is read by the default rendering process. Other skins may be defined by the services to simplify and granularize a complex rendering process. For example, a service that is meant to be rendered as a table of items may rely on its default @b skin to provide the overall framing and table setup, while it may use a new @b skin_row service variable to render each row. @note it is advisable to name service variable names meant to be skins with the @b skin_ prefix. When assigned directly by the service code, the Skin value should be directly the function that will perform the rendering. If not explicitly assinged in this way, the skin binder will try to find a function or callable item named after the service variable in the service configuration. The item in the configuration may also be a string representing a module name, in which case a moule with that name will be searched. Finally, the variable may be unreferenced in the configuration; in that case a module having the same name of the variable will be searched. The skin file search order is the following: - /srv// - /srv/ - /srv// - /srv/ The skin module must expose a function named "skin" at its topmost level. If a variable named after the skin-bound property is explicitly set in the configuration, then an error will be raised if the required skin is not found. Otherwise, the skin is considered optional (or to be filled by the service later on), and even if the binder can't find any module corresponding to the skin bound property name, it won't raise any error. Once resolved, skins are just functions. Sub-skins other than the @a Service.skin properties should be explicitly invoked by the master skin. For example, the following service is rendered through a table, and uses a separate skin to render each row: @code // srv/TheService.fal class TheService( inst ) from Service( inst ) ... skin_row = ServiceBinder( b.SkinBinder ) ... rows = ServiceVar() // final contents ... end // srv/TheService/skin.ftd
    ...
    // srv/TheService/skin_row.ftd
    $(recover_skin)
    $(form_end)" jsListeners = [ self.loginBtn => "function( item, msg, params ){ Nest.i(this.id+'.busy-ind').style.display=''; }" ] init self.addChildren( self.userID, self.pwd, self.rememberMe, self.loginBtn, self.accessStatus, self.loginBusy ) self.props = ["style"=>"display:inline-block"] self.loginBusy.props["width"] = 16 self.loginBusy.props["height"] = 16 self.accessStatus.set( ["style" => "display:none"] ) self.accessStatus.jsListeners = [ self => " function( obj, msg, value ) { if( msg == 'login' ) { if( value == null ) { this.style.display = ''; Nest.rw(this,'../user-id').value=''; Nest.rw(this,'../pwd').value=''; Nest.rw(this,'../user-id').setfocus(); } else { this.style.display = 'none'; } } }" ] self.userID.onEnter( "function(){ if( this.value.length >0 ) Nest.rw(this,'../pwd').setfocus();}" ) self.pwd.onEnter( formAction ? "function(){ if( this.value.length >0 ) Nest.rw(this,'../').submit();}" : "function(){ if( this.value.length >0 ) Nest.rw(this,'../login').sendLogin();}") // if we're not using a form convention, we're ajax if not formAction self.isAJAXHost = true jsListeners = [ self.loginBtn => "function( item, msg, params ){ Nest.i(this.id+'.busy-ind').style.display=''; }" ] end // TODO: Remove this when we have automatic parentship self.addClassInParentship( LoginMask ) end function renderContent() if( self.formAction ) self.loginBtn.props["value"] = self.login_prompt else self.loginBtn.content = self.login_prompt end if self.recover self.addChild( self.recover ) recover = self.recover.render() recover_skin = @ self.recover_skin else recover_skin = "" end if self.formAction form_begin = @"
    " form_end = "
    " else form_begin = "" form_end = "" end userid_prompt = self.userid_prompt pwd_prompt = self.pwd_prompt remember_prompt = self.remember_prompt userID = self.userID.render() pwd = self.pwd.render() rememberMe = self.rememberMe.render() loginBtn = self.loginBtn.render() accessStatus = self.accessStatus.render() loginBusy = self.loginBusy.render() return @self.skin end function AJAX( params ) if params["id"].endsWith( ".login" ) result = self.checkLogin( params["params"]["uid"], params["params"]["pwd"], params["params"]["remember"] ) if result res = [ "uid" => result[0], "level" => result[1], "data" => result[2], "name" => params["params"]["uid"] ] end return [ self.loginBusy.msgSetStyle( "display", "none" ), self.msgInvoke( result ? "onLoginSuccess" : "onLoginFailed", res ) ] end return [] end /*# Override to decide if we're in busienss or not. The default implementation generates a hook call for "check_user". */ function checkLogin( username, password, rememberMe ) // Check login should set the following variables auth = Nest.emit( "check_user", username, password, rememberMe ) if auth.len() == 3 return auth end return false end end frameworks/nest/nest/widgets/Menu.fal000066400000000000000000000130301176363201700202210ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Menu.fal AJAX widgeting subsystem -- navigation menu ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Widget from nest.widgets.widget as Widget import MenuStyles from nest.widgets.menustyles as MenuStyles /*# Menu bar widget. @param id The id of this widget. @param mode nil/false for horizontal, true for vertical menus. @param separator nil for nothing, any string (including an empty string) to create a separator item between the shown item. @param socket Public name of the menu that is used to hook menu requests from plugins. This class creates a context-sensible menu that is also minimally styled to support CSS3 menu facilities. Submenus are automatically rendered as the mouse hovers over the menu voice. Except for this minimal functionality, the menu is completely unstyled, and requires custom styling to be minimally useable. The menu is created by adding @a nest.widget.MenuItem entities to this widget. The MenuItem widget has support to accept dynamically created entries (plugin), and to be automatically shown on a per-authorization-level basis; see the @a nest.widget.MenuItem for further details. @section style Styling Toplevel Menu Widgets is rendered as a @b ul HTML element, and it receives forcefully a CSS class called "nest_horizontal_menu" or "nest_vertical_menu" depending on their @b mode property. Items in the menu are rendered as @b li HTML elements, and receive a CSS class as "first_item", "item" or "last_item" depending on their position. @note Items that are hidden because of the automatic authorization level filters might be the first one or the last one; in case this class information is used to specially render the first and last elements of a menu, be sure not to set a maskable menu item as first or last choice. Notice also that this CSS class information is not used by Nest, it's just provided for the final implementor to finetune the rendering of the elements in case of need. The menu item CSS classes can be overridden by setting explicitly the "class" entry in the Widget.props dictionary. If a separator is set in this menu, it is rendered by surrounding it by a @b li element of CSS class "separator". The menus are rendered as inline-block display. This means that they occupy the space indicated by their components only; if this is not desired, the display: CSS property of the main menu can be overridden, or preferabily, the menu can be embedded in a outer widget that is then styled as desired. It is particularly important to set the height CSS property of horizontal menus, and the CSS width property of submenus as appropriate. When styling the menus through the @a nest.widget.Style inteface, it is preferabily to apply the style to the outer widget or class, and then use the sub-specifier to target a precise element or submenu entity. */ class Menu( id, mode, separator, socket ) from Widget( id ) tag = "ul" //# false = horizontal, true = vertical mode = mode //# if not nil causes a li of class separator to be generated. separator = separator socket = socket init if self.props == nil: self.props = [=>] if self.mode self.props["class"] = 'nest_vertical_menu' else self.props["class"] = 'nest_horizontal_menu' end if self.socket Nest.registerMenu(self) end // TODO: Remove this when we have automatic parentship self.addClassInParentship( Menu ) end function addChild( child ) if "class" notin child.props if not self.children child.props["class"] = "first_item" else child.props["class"] = "last_item" end end if self.children and self.children[-1].props["class"] == "last_item" self.children[-1].props["class"] = "item" end self.Widget.addChild( child ) end function onRender() if "class" in self.props msty = Nest.ambient( "MenuStyles" ) if not msty msty = Nest.ambient( "MenuStyles", MenuStyles() ) end for style in msty.menu_styles_common: style.setToClass( self ) if self.props["class"] == 'nest_vertical_menu' for style in msty.menu_styles_vert: style.setToClass( self ) else for style in msty.menu_styles_horiz: style.setToClass( self ) end end // hide ourselves? if not self.children self.tag = "" end end function renderContent() rprev = "" rend = "" for child in self.children rchild = child.renderInParent( self ) if rprev rend += rprev + "\n" if self.separator != nil: rend += "
  • " + self.separator + "
  • \n" end rprev = rchild end rend += rprev return rend end function renderChild( child ) auth_level = Nest.auth_level if child provides minAuthLevel and child.minAuthLevel and child.minAuthLevel < auth_level return "" end return child.render() end end frameworks/nest/nest/widgets/MenuItem.fal000066400000000000000000000063301176363201700210450ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Menu.fal AJAX widgeting subsystem -- navigation menu ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Widget from nest.widgets.widget as Widget import Image from nest.widgets.Image as Image import Text from nest.widgets.Text as Text /*# @param id @param text Text for the menu entry @optparam icon @optparam submenu @optparam submenu_ind @optparam minAuthLevel The @b text param may be: - "Text" A text to be viewed - "link:Text" A link to be opened on click. - Any widget. A widget to be shown in the text area. Link can start with: - '!' A link to a Nest page - '[target]' Open on a different (new) target. - '[target]!...' Open a nest page in a different target. If the @b text parameter is a widget, "target" and "href" properties are queried and they are used in this widget. */ class MenuItem( id, text, icon, submenu, submenu_ind, minAuthLevel ) from Widget( id ) tag = "li" text = nil icon = nil submenu = submenu submenu_ind = nil minAuthLevel = minAuthLevel href = nil target = nil init if not self.props: self.props = [=>] select text case NilType: self.text = Text( "text", "" ) case StringType if ":" in text link, text = text.split(":") if link.startsWith('[') self.target = link[1:link.find(']')] link = link[self.target.len()+2:] end if link.startsWith('!') self.href = Nest.pageLink(link[1:]) else self.href = text end end self.text = Text( "text", text ) self.text.tag = nil default self.text = text if "href" in text self.href = text.href end if "target" in text self.target = text.target end end select icon case NilType: self.icon = Image( "icon", "", false ) case StringType: self.icon = Image( "icon", icon ) default: self.icon = icon end select submenu_ind case StringType: self.submenu_ind = Text( "sbind", submenu_ind ) default: self.submenu_ind = submenu_ind end self.addChild( self.icon ) self.addChild( self.text ) if submenu_ind: self.addChild( self.submenu_ind ) if submenu submenu.props -= "class" self.addChild( submenu ) end // TODO: Remove this when we have automatic parentship self.addClassInParentship( MenuItem ) end function onRender() if self.href if self.target self.set(["onclick"=>@"window.open('$(self.href)','$(self.target)')"]) else self.set(["onclick"=>@"window.location.href=\"$(self.href)\""]) end end end end frameworks/nest/nest/widgets/NameWithAvail.fal000066400000000000000000000165101176363201700220140ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: NameWithAvail.fal AJAX widgeting subsystem -- Widget with password and checkers ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget import InputText from nest.widgets.InputText as InputText import ActiveInput from nest.widgets.ActiveInput as ActiveInput import Image from nest.widgets.Image as Image class ImgAvail( id, name_wid ) from Image( id, "", true ) avail_img = Nest.resLink("name_avail.png") na_img = Nest.resLink("name_na.png") short_img = Nest.resLink("name_short.png") loading_img = Nest.resLink("name_loading.gif") name_wid = name_wid init // TODO: Remove this when we have automatic parentship self.addClassInParentship( ImgAvail ) end function onRender() self.addStyle( "vertical-align:middle" ) // initially, the image is that of too short. if self.short_img self.props["src"] = self.short_img else self.props["src"] = "" self.addStyle( "display:none" ) end // Create a property that stores the minimum size self.jsMethods += [ "minsize" => "" + self.parent.parent.minSize + ";", "setAvailable" => @" function( obj ) { if( obj.avail ) { this.src=\"$(self.avail_img)\"; } else { this.src=\"$(self.na_img)\"; } }" ] if self.short_img self.jsListeners = [ self.name_wid => @" function( wid, msg, value ) { if( value.length < this.minsize ) { this.src = \"$(self.short_img)\"; } else { this.src = \"$(self.loading_img)\"; } }" ] else self.jsListeners = [ self.name_wid => @" function( wid, msg, value ) { if( value.length > this.minsize ) { this.src = \"$(self.loading_img)\"; this.style.display=\"\"; } else { this.style.display=\"none\"; } }" ] end end end class TextAvail( id, name_wid ) from widget.Widget( id ) tag="span" avail_prompt = "Name available" na_prompt = "Name NOT available" short_prompt = "Name too short" working_prompt = "Checking..." first_prompt = "" name_wid = name_wid init // TODO: Remove this when we have automatic parentship self.addClassInParentship( TextAvail ) end function onRender() short_prompt = htmlEscape(self.short_prompt) working_prompt = htmlEscape(self.working_prompt) avail_prompt = htmlEscape(self.avail_prompt) na_prompt = htmlEscape(self.na_prompt) if not self.first_prompt and self.short_prompt self.first_prompt = self.short_prompt end // Create a property that stores the minimum size self.jsMethods += [ "minsize" => "" + self.parent.parent.minSize + ";", "setAvailable" => @" function(obj){ if( obj.avail ) { this.innerHTML=\"$(avail_prompt)\"; } else { this.innerHTML=\"$(na_prompt)\"; } }" ] if self.short_prompt self.jsListeners = [ self.name_wid => @" function( wid, msg, value ) { if( value.length < this.minsize ) { this.innerHTML = \"$(short_prompt)\"; } else { this.innerHTML = \"$(working_prompt)\"; } }" ] else self.jsListeners = [ self.name_wid => @" function( wid, msg, value ) { if( value.length > this.minsize ) { this.innerHTML = \"$(working_prompt)\"; } else { this.innerHTML = \"\"; } }" ] end end function renderContent() return self.first_prompt end end class AvailWidget( id, name_wid ) from widget.Widget( id ) tag = "span" name_wid = name_wid img_wid = ImgAvail( "status_img", name_wid ) status_wid = TextAvail( "status_text", name_wid ) init self.addChild( self.img_wid ) self.addChild( self.status_wid ) // TODO: Remove this when we have automatic parentship self.addClassInParentship( AvailWidget ) end function renderContent( framing ) return self.img_wid.render() + " " + self.status_wid.render() end end // /*# Class checking for a name to be available. */ class NameWithAvail( id, prompt, iPrompt ) from widget.Widget( id ) tag = nil isAJAXHost = true initInfos = [prompt] prompt = prompt minSize = 6 name_wid = ActiveInput( "name", nil, iPrompt ) avail_wid = AvailWidget( "avail", self.name_wid ) name_checked = widget.Widget( "name_checked" ) init self.name_checked.tag = "input" self.name_checked.props = ["type" => "hidden"] self.name_wid.typeTimeout = 250 self.name_wid.checkAjax = true self.name_wid.jsMethods += ["min_size" => self.minSize] self.addChild( self.name_wid ) self.addChild( self.avail_wid ) self.addChild( self.name_checked ) self.name_wid.ajaxFilter = "this.min_size > this.value.length" // TODO: Remove this when we have automatic parentship self.addClassInParentship( NameWithAvail ) end //#default rendering, not very complex. function renderLabel() if self.prompt provides render return self.prompt.render() else return self.prompt end end function renderInfo() return self.avail_wid.renderCore() end function renderContent() return self.name_checked.render() + self.name_wid.render() end function setValue( val ) self.name_wid.props['value'] = val end function onSetup() self.name_wid.props["name"] = self.getFullID() end function AJAX( params ) name = params["params"]["value"] swid = self.avail_wid.status_wid iwid = self.avail_wid.img_wid if self.checkAvail( name ) return [ swid.msgInvoke( "setAvailable", ["avail"=>true] ), iwid.msgInvoke( "setAvailable", ["avail"=>true] ), self.name_checked.msgSetProperty( "value", "checked" ) ] else return [ swid.msgInvoke( "setAvailable", ["avail"=>false] ), iwid.msgInvoke( "setAvailable", ["avail"=>false] ), self.name_checked.msgSetProperty( "value", "" ) ] end end /*# Overridable Method checking for availability of a name. @param name The name to be checked @return true if the name is available, false otherwise. */ function checkAvail( name ) return true end function getValue() return self.name_wid.getValue() end function setAlertText( text ) self.avail_wid.status_wid.first_prompt = text end end frameworks/nest/nest/widgets/Option.fal000066400000000000000000000023441176363201700205730ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Select.fal AJAX widgeting subsystem -- select input field. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget /*# Option widget (part of Select). @param value Value of the option when posted in a form. @param text The text. @param selected If true, this option is selected. Actually this widget can't stand alone wihtout beinga child of a Select widget. @note The @b id of this widget is set as the @b value parameter. */ class Option( value, text, selected ) from widget.Widget( value ) tag = "option" text = text init self.props = ["value" => value ] if selected: self.props["selected"] = "selected" // TODO: Remove this when we have automatic parentship self.addClassInParentship( Option ) end function renderContent() return self.text end endframeworks/nest/nest/widgets/PairCheck.fal000066400000000000000000000036041176363201700211540ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: PairCheck.fal AJAX widgeting subsystem -- A field checking that two fields match. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget class PairCheck( id, first_widget, second_widget ) from widget.Widget( id ) tag = "span" content = "" first_widget = first_widget second_widget = second_widget text_match = "Matches" text_no_match = "Is not matching!" jsMethods = [ "checkFirst" => 'function( value ){ this.valueOfFirst = value; this.checkPair();}', "checkSecond" => 'function( value ){ this.valueOfSecond = value; this.checkPair();}' ] jsListeners = [ first_widget => "function( wid, msg, value ) { this.checkFirst(value); }", second_widget => "function( wid, msg, value ) { this.checkSecond(value); }" ] init // TODO: Remove this when we have automatic parentship self.addClassInParentship( PairCheck ) end function onRender() // we render the method here to be able to change the text after init. self.jsMethods["checkPair"] = @" function(){ var match = ''; if( this.valueOfFirst == this.valueOfSecond ) { this.innerHTML='$(self.text_match)'; match = 'ok'; } else { this.innerHTML='$(self.text_no_match)'; match = ''; } Nest.message(this, 'pair', match); }" end function renderContent() return self.content end end frameworks/nest/nest/widgets/Password.fal000066400000000000000000000024071176363201700211250ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Password.fal AJAX widgeting subsystem -- input field. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import InputText from nest.widgets.InputText as InputText class Password( id, prompt, iPrompt, size ) from InputText( id, prompt, iPrompt ) size = size ? size : 20 init self.props['type'] = "password" self.props['size'] = self.size self.props['maxlength'] = self.size if self.onCreate self.onCreate += ";if (this.value){ this.onchange(); }" else self.onCreate = ";if (this.value){ this.onchange(); }" end // TODO: Remove this when we have automatic parentship self.addClassInParentship( Password ) end function onRender() self.InputText.onRender() self.props['onkeyup'] = self.jsEmit( 'value', 'this.value' ) self.props['onchange'] = self.jsEmit( 'value', 'this.value' ) end end frameworks/nest/nest/widgets/PasswordStrength.fal000066400000000000000000000045511176363201700226460ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: PasswordStrength.fal AJAX widgeting subsystem -- A field checking strengt of a password ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget class PasswordStrength( id, pwd_widget ) from widget.Widget( id ) tag = "span" pwd_widget = pwd_widget content = "" desc_level0 = "Insecure"; desc_level1 = "Very weak"; desc_level2 = "Weak"; desc_level3 = "Strong"; desc_level4 = "Very strong"; pswdMeterFunc = ' function( value ) { var m = 0; // has 6 chars minimum if ( value.length >= 6 ) { m += 20; // has both lower and uppercase if ( value.match( /^.*(?=.*[a-z])(?=.*[A-Z]).*$/ ) ) { m += 40; } // has a figure if ( value.match( /^.*(?=.*\d).*$/ ) ) { m += 20; } // has a special char if ( value.match( /^.*(?=.*[_@#$%%^&+=:;-]).*$/ ) ) { m += 20; } } this.innerHTML = this.getDesc( m ); this.strenght = m; Nest.message(this, "strength", m); } ' jsMethods = [ "checkStrength" => self.pswdMeterFunc, "strenght" => 0 ] jsListeners = [ pwd_widget => "function( srcwid, msg, value ) { this.checkStrength(value); }" ] init hidvalue = widget.Widget( "strength" ) hidvalue.tag = "input" hidvalue.props = ["type"=>"hidden", "value"=>0] self.addChild( hidvalue ) // TODO: Remove this when we have automatic parentship self.addClassInParentship( PasswordStrength ) end function onRender() self.jsMethods["getDesc"] = @' function( m ) { if( m < 20 ){ return "$(self.desc_level0)"; } if( m < 40 ){ return "$(self.desc_level1)"; } if( m < 60 ){ return "$(self.desc_level2)"; } if( m < 80 ){ return "$(self.desc_level3)"; } return "$(self.desc_level4)"; } ' end function renderContent() return self.content end end frameworks/nest/nest/widgets/PickItem.fal000066400000000000000000000117141176363201700210310ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: PickItem.fal AJAX widgeting subsystem -- A single item for large visual menus ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 15 Apr 2012 18:03:41 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Widget from nest.widgets.widget as Widget /*# A single item for large visual menus @param id The widget ID @param title A string or widget representing the title of the item. @param detail A string or widget representing a detail description of the item. @optparam icon A resource name or Image widget to be rendered besides the title. @optparam link A link to open on item click. A pick shelf is a large, page-wide menu that displays a collection of PickItem entities. Each pick item automatically generates a nest message when clicked in any part, or can excite a link if required. An optional separator widget can be passed to be automatically rendered between each pickshelf entity. Actually, the content of this widget is not restricted to PickShelf items, it could be any widget; however, PickShelf has a nice support to represent a pickable item and send a Nest message when an item is chosen. The pick item automatically emits a Nest.message at javascript side; to react to the clicking of an item, just set a Widget.jsListener on the listening widget. @section link PickItem automatic link. The preferred way to use a PickItem is that to react to the Nest message it generates; however, it is possible to set the @b link property to generate a window.open JS call on a click over PickItem. The property can be: - A string: will open the new page in the _blank target, usually causing the browser to open the given link in a new tab. - A string prefixed with '*'; will open the given link in place of this document - A string prefixed with '!'; will consider the rest of the string as a Nest page ID, and open it in this document. @section styling PickItem styling The pickitem has the following CSS structure: @code ++++++++++++++++++++++++++++++++ << div (class defaults to nest_pickitem) + *--------------------------* + << div class = nest_pickitem_title + * |ICON| TITLE * + << img & span + * ------------------------ * + << div class = nest_pickitem_detail + * Description of this item * + + *--------------------------* + ++++++++++++++++++++++++++++++++ @endcode The class to the title and detail boxes is set only if the respective entity is created through a simple string. Similarly, a "span" is automatically created for the title text only if the text was given as a plain string. In case the title or detail elements are given as widget, this element just renders them. Notice that the icon is not rendered if the title is set as a widget. @note You may float the ICON image right to make it to appare to the right of the title, and you might set the relative position of the div class=nest_pickitem_detail to align it with the title. */ class PickItem( id, title, detail, icon, link ) from Widget(id) title = title detail = detail icon = icon link = link init if title provides addChild: self.addChild( title ) if detail provides addChild: self.addChild( detail ) if icon provides addChild: self.addChild( icon ) if self.props == nil: self.props = [=>] self.props["class"] = "nest_pickitem" self.props["onclick"] = "Nest.message( this, 'clicked', null)" if link if link.startsWith( "!" ) link = Nest.pageLink( link[1:] ) self.props["onclick"] += @";window.location.href=\"$(link)\"" elif link.startsWith( "*" ) link = link[1:] self.props["onclick"] += @";window.location.href=\"$(link)\"" else self.props["onclick"] += @";window.open(\"$(link)\", \"_blank\" )" end end self.addClassInParentship( PickItem ) end function renderContent() rend = "" // is title a widget? if self.title provides render rend += self.title.render() else rend += "
    \n" if self.icon provides render rend += self.icon.render() elif self.icon rend += "" end rend += "" + self.title + "\n" rend += "
    \n" end // is detail a widget? if self.detail provides render rend += self.detail.render() else rend += "
    \n" rend += self.detail rend += "
    \n" end return rend end end frameworks/nest/nest/widgets/PickShelf.fal000066400000000000000000000053111176363201700211700ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: PickShelf.fal AJAX widgeting subsystem -- Container for large menu items. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 15 Apr 2012 18:03:41 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Widget from nest.widgets.widget as Widget /*# Container for PickItem @param id The widget ID @optparam separator A separator widget to be inserted between each element. A pick shelf is a large, page-wide menu that displays a collection of PickItem entities. Each pick item automatically generates a nest message when clicked in any part, or can excite a link if required. An optional separator widget can be passed to be automatically rendered between each pickshelf entity. Actually, the content of this widget is not restricted to PickShelf items, it could be any widget; however, PickShelf has a nice support to represent a pickable item and send a Nest message when an item is chosen. @section styling PickShelf styling The pickshelf has the following CSS structure: @code +++++++++++++++++++++++++++++++ << div (id and class user-defined) + *-------------------------* + << div class=nest_pickitem + * A pick item... * + + *-------------------------* + + //-------- separator -----//+ << usually div, class=nest_pickshelf_separator + *-------------------------* + .... + * A pick item... * + + *-------------------------* + + //-------- separator -----//+ .. .... .. .. .... .. +++++++++++++++++++++++++++++++ @endcode Notice that this widget doesn't offer any default styling; usually, you'll want to add the inline-block display style. */ class PickShelf( id, separator ) from Widget(id) separator = separator init self.addClassInParentship( PickShelf ) end function renderContent() rend = "" if self.separator if not self.separator.props self.separator.props = ["class" => "nest_pickshelf_separator"] elif "class" notin self.separator.props self.separator.props["class"] = "separator" end seprend = self.separator.render() for child in self.children if rend: rend += seprend rend += child.render() end else for child in self.children rend += child.render() end end return rend end end frameworks/nest/nest/widgets/Popup.fal000066400000000000000000000124141176363201700204250ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Popup.fal AJAX widgeting subsystem -- Base class for popup widgets ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 12 Apr 2012 22:53:31 +0200 ------------------------------------------------------------------- (C) Copyright 2012: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget /*# Generic popup widget. @param id The widget ID This is the base class for widgets that have the capability to be popped up. As they are meant to be absolutely placed on the page, they are represented as 'div' entities. @section jscript Interface to Javascript The widget offers the following JS methods and properties: - show(): makes the widget visible. - hide(): hides the widget. - onShow(): callback invoked when show() is called; if it returns false, the widget is not shown. - onHide(): callback invoked when hide() is called; if it returns false, the widget is not hidden. - fadeInTime: if > 0, the widget fades in when show() is called. - fadeOutTime: if > 0, the widget fades out when hide() is called. - shown: false if currently hidden, true if currently visible. They offer an onShow and onHide callback pair that can be used to prevent the action to be actually performed if they return false. @section messages Messages When the window changes its visibility, a 'visibility' Nest message is generated, with an object {visible: } as parameter. The message is generated after transitions (fade-in, fade-out) are completed */ class Popup( id, left, top, width, height, fixed, center ) from widget.Widget( id ) top = top left = left width = width height = height center = center fixed = fixed fadeInTime = 0 fadeOutTime = 0 jsMethods = [ "fadeInTime" => "0", "fadeOutTime" => "0", "shown" => "false", "show" => " function() { if( ! this.shown && this.onShow() ) { if ( this.relativeTo != null ) { this.style.position='absolute'; Nest.reposition( Nest.i(this.relativeTo), this, this.left, this.top, this.heigth ); } this.shown = true; this.showTransition(); } }", "hide" => " function() { if( this.shown && this.onHide() ) { this.shown = false; this.showTransition(); } }", "switchShow" => " function() { if( this.shown ) this.hide(); else this.show(); } ", "onShow" => "function(){ return true; }", "onHide" => "function(){ return true; }", "showTransition" => " function() { var self = this; if( this.shown ) { this.style.display = ''; if( this.fadeInTime > 0 ) { Nest.transite( this, this.fadeInTime, {'opacity':1}, {'opacity':0}, null, function(){self.endTransition();} ); } else { this.endTransition(); } } else { if( this.fadeOutTime > 0 ) { Nest.transite(this, this.fadeOutTime, {'opacity':0}, {'opacity':1}, null, function(){self.endTransition();} ); } else { this.endTransition(); } } }", "endTransition" => " function() { if( ! this.shown ) { this.style.display = 'none'; } Nest.message( this, 'visibility', {visible: this.shown} ); }" ] init if self.props == nil: self.props = [=>] self.props["style"] = @"display: none; overflow:hidden;" // activate effects Nest.requireJS("fx") // TODO: Remove this when we have automatic parentship self.addClassInParentship( Popup ) end function setRelativeTo( widgetID ) self.jsMethods["left"] = self.left self.jsMethods["top"] = self.top self.jsMethods["width"] = self.width self.jsMethods["height"] = self.height self.jsMethods["relativeTo"] = @'"$widgetID"' end function onRender() if self.fadeInTime self.jsMethods["fadeInTime"] = self.fadeInTime end if self.fadeOutTime self.jsMethods["fadeOutTime"] = self.fadeOutTime end if self.center marginleft = - (self.width/2) margintop = - (self.height/2) self.addStyle( @" width: $(self.width)px; height: $(self.height)px; top: 50%; left: 50%; margin-left: $(marginleft)px; margin-top: $(margintop)px; " ) else self.addStyle( @"top:$(self.top)px; left:$(self.left)px; width:$(self.width)px; height:$(self.height)px;" ) end if self.fixed self.addStyle( "position: fixed" ) else self.addStyle( "position: absolute" ) end end end frameworks/nest/nest/widgets/PopupWindow.fal000066400000000000000000000060471176363201700216220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: PopupWindow.fal AJAX widgeting subsystem -- Base class for popup widgets ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 12 Apr 2012 22:53:31 +0200 ------------------------------------------------------------------- (C) Copyright 2012: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Popup from nest.widgets.Popup as Popup import Widget from nest.widgets.widget as Widget import Image from nest.widgets.Image as Image import Container from nest.widgets.Container as Container class TitleBar( id, title, height, icon, closeIcon ) from Widget( id ) title = title icon = icon height = height closeIcon = closeIcon ? closeIcon : Image( 'close', Nest.resLink( "infobox_close.png" ) ) init self.addStyle( @"height: $(height)px; padding:0; margin:0;" ) if icon: self.addChild( icon ) self.addChild( self.closeIcon ) self.closeIcon.addStyle( "position:absolute; right:2px; top:2px;" ) self.closeIcon.jsMethods["onclick"] = self.closeIcon.makeEmit( 'onclick', "{}" ) self.jsListeners[self.closeIcon] = self.makeEmit( 'close', "{}" ) self.addClassInParentship( TitleBar ) end function renderContent() str = "" if self.icon: str += self.icon.render() id = self.getFullID() str += @"$(self.title)" str += self.closeIcon.render() return str end end class _ContainerWidget( id,popup ) from Widget( id ) popup = popup init // TODO: Remove this when we have automatic parentship self.addClassInParentship( _ContainerWidget ) end function renderContent() return self.popup.renderContainerWidget() end end /*# Window popup widget. @param id The widget ID */ class PopupWindow( id, title, left, top, width, height, fixed, center ) from Popup( id, left, top, width, height, fixed, center ) title = title titleWidget = nil scroller = Widget( "scroller" ) container = _ContainerWidget( "container", self ) init if self.props == nil: self.props = [=>] if title.typeId() == StringType self.titleWidget = TitleBar( "titlebar", title, 23 ) else self.titleWidget = title end height = self.height if self.titleWidget.height: height -= self.titleWidget.height self.scroller.addChild( self.container ) self.scroller.addStyle( @"overflow: auto; height:$(height)px; padding:0; margin:0;" ) self.container.set(["class"=>"container"]) self.addChild( self.titleWidget ) self.addChild( self.scroller ) self.jsListeners[self.titleWidget] = "function(){ this.hide(); }" // TODO: Remove this when we have automatic parentship self.addClassInParentship( PopupWindow ) end function renderContainerWidget() return self.container.Widget.renderContent() end end frameworks/nest/nest/widgets/RadioButton.fal000066400000000000000000000027531176363201700215610ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: RadioButton.fal AJAX widgeting subsystem -- Radio button widget. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 02 Oct 2011 13:53:51 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget /*# Radio button form widget. @param id The widget ID @param name The name of the radio-button set. @param value value of the radio button in the @param text Text displayed at radio button (label) Radio buttons are the only widgets that share the same name across different widgets with different ids. This fact is enforced during the onRender phase by filing the correct name in the widget properties. */ class RadioButton( id, name, value, text ) from widget.Widget( id ) label = text name = name tag = "input" init if self.props == nil: self.props = [=>] self.props['value'] = value self.props['type'] = 'radio' // TODO: Remove this when we have automatic parentship self.addClassInParentship( RadioButton ) end function onRender() if self.parent pname = self.parent.getFullID() else pname = self.name end self.props['name'] = pname end end frameworks/nest/nest/widgets/RadioButtonSet.fal000066400000000000000000000055321176363201700222330ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: RadioButtonSet.fal AJAX widgeting subsystem -- Automated radio-button choice generator ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 02 Oct 2011 13:53:51 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Container from nest.widgets.Container as Container import RadioButton from nest.widgets.RadioButton as RadioButton /*# Automated radio-button choice generator @param id The widget ID @optparam choices An array of choices (strings). @optparam tagtype Tag used for this container (defaults to fieldset). @optparam tag_beg HTML code to be put before the first child (defaults to ""). @optparam tag_beg HTML code to be put in between children (defaults to " "). @optparam tag_beg HTML code to be put after the last child (defaults to ""). This is a widget that automatically creates a set of radio buttons and adds them to the container. Each element in choice is a string with the following format: "value:text". If ':' is not in the choice string, then the whole text will be used both as a submit value and as a text. You can also add more children after the widget is created for better control on the apparence of each choice. */ class RadioButtonSet( id, choices, tagtype, tag_beg, tag_sep, tag_end ) from \ Container( id, tagtype, tag_beg, tag_sep, tag_end ) //# Value of the radio button set (that is, the active radio button) value = nil isValueHost = true init self.autoAddChildren( choices ) // TODO: Remove this when we have automatic parentship self.addClassInParentship( RadioButtonSet ) end function makeAutoChild( value, text, checked ) child = RadioButton( value, self.id, value, text ) if checked: child.props["checked"] = "checked" return child end function setValue( val ) if val in self.childrenById self.value = val self.childrenById[val].props["checked"] = "checked" end end function onRender() checks = "var item;" sets = "var item;" for child in self.children checks += " item=Nest.i('" + child.getFullID() + "'); if(item.checked) return item.value;" sets += " item=Nest.i('" + child.getFullID() + "'); item.checked = (value == item.value);" end self.jsMethods[ "getValue" ] = "function(){" + checks + " }" self.jsMethods[ "setValue" ] = "function(value){" + sets + " }" self.jsMethods[ "name" ] = "'" + self.getFullID() + "'" end function getValue() return self.value end end frameworks/nest/nest/widgets/Reset.fal000066400000000000000000000020061176363201700204000ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Reset.fal AJAX widgeting subsystem -- Reset input tag. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 02 Oct 2011 13:53:51 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.Button in widget /*# Reset input tag. @param id The widget ID @param value The value attribute of the input tag. */ class Reset( id, value ) from widget.Button( id ) tag = "input" isSelfClosing = true init if self.props == nil: self.props = [=>] self.props['name'] = nil // will be filled at render self.props['type'] = "reset" self.props['value'] = value // TODO: Remove this when we have automatic parentship self.addClassInParentship( Reset ) end end frameworks/nest/nest/widgets/RichPassword.fal000066400000000000000000000052031176363201700217300ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: RichPassword.fal AJAX widgeting subsystem -- Widget with password and checkers ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget import Password from nest.widgets.Password as Password import PasswordStrength from nest.widgets.PasswordStrength as PasswordStrength import PairCheck from nest.widgets.PairCheck as PairCheck class RichPassword( id, pw_prompt, rep_prompt ) from widget.Widget( id ) tag = nil pw_wid = Password( "pwd", pw_prompt ); pws_wid = PasswordStrength( "pwstr", self.pw_wid ); pw_ck_wid = Password( "pwcheck", rep_prompt ); pw_repres_wid = PairCheck( "pwrepres", self.pw_wid, self.pw_ck_wid ); strength = 0 match = false value = nil init self.addChild( self.pw_wid ) self.addChild( self.pws_wid ) self.addChild( self.pw_ck_wid ) self.addChild( self.pw_repres_wid ) self.pw_wid.fieldInfo = self.pws_wid self.pw_ck_wid.fieldInfo = self.pw_repres_wid // TODO: Remove this when we have automatic parentship self.addClassInParentship( RichPassword ) end //#default rendering, not very complex. function renderInParent( parent ) return parent.renderChild( self.pw_wid ) + parent.renderChild( self.pw_ck_wid ) end function renderContent() return self.pw_wid.render() + "
    " + self.pw_ck_wid.render() end function set( props ) self.pw_wid.set( props ) self.pw_ck_wid.set( props ) return self end function routeValues( vals, root ) // sets some safe default self.strength = 0 self.match = false myRoot = root + "." + self.id strengthVal = myRoot + "." + self.pws_wid.id + ".strength" pwdVal = myRoot+"." + self.pw_wid.id try if strengthVal in vals: self.strength = int(vals[strengthVal]) end self.pw_wid.routeValues( vals, myRoot ) self.pws_wid.routeValues( vals, myRoot ) self.pw_ck_wid.routeValues( vals, myRoot ) self.pw_repres_wid.routeValues( vals, myRoot ) self.value = self.pw_wid.getValue() self.match = self.value == self.pw_ck_wid.getValue() end function setValue( v ) self.value = v end function getValue() return self.value end end frameworks/nest/nest/widgets/Select.fal000066400000000000000000000062441176363201700205450ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Select.fal AJAX widgeting subsystem -- select input field. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget import Option from nest.widgets.Option as Option /*# HTML Select widget. @param id The ID of this widget. @optparam options List of strings used as options. @optparam prompt Label for this widget. @optparam multisize If > 0, will set this as a multiple choice select and set the number of options that are shown in a row. With -1, it will set the size of shown options to the amount of options in @b options parameter. The option parameter is an array of strings in the format specified in @a Widget.autoAddChildren. @note To set the size independently from multiple choice, or after other options has been set, set the 'size' property in the Widget.props dictionary. */ class Select( id, options, prompt, multisize ) from widget.Widget( id ) tag = "select" isSelfClosing = false label = prompt ? prompt : "" value = nil //# in forms, we do have values isValueHost = true init if self.props == nil: self.props = [=>] self.props['name'] = nil // will be filled at render if multisize self.props["multiple"] = "multiple" self.props["size"] = multisize self.addName = "[]" end self.autoAddChildren( options ) // TODO: Remove this when we have automatic parentship self.addClassInParentship( Select ) end function makeAutoChild( value, text, checked ) return Option( value, text, checked ) end function onRender() if "multiple" in self.props checks = "var result = Array(); var item;" sets = "var item;" for child in self.children checks += " item=Nest.i('" + child.getFullID() + "'); if(item.selected) result.push(item.value);" sets += " item=Nest.i('" + child.getFullID() + "'); item.selected = value.indexOf(item.value) >= 0;" end self.jsMethods[ "getValue" ] = "function(){" + checks + "; return result;}" self.jsMethods[ "setValue" ] = "function(value){" + sets + ";}" end end function setValue( val ) if "multiple" in self.props and val.typeId() == ArrayType for item in val if item in self.childrenById child = self.childrenById[item] if self.value == nil: self.value = [] self.value += child.id child.props["selected"] = "selected" end end elif val in self.childrenById child = self.childrenById[val] self.value = child.id child.props["selected"] = "selected" end end function getValue() return self.value end end frameworks/nest/nest/widgets/Showcase.fal000066400000000000000000000207421176363201700211010ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Menu.fal AJAX widgeting subsystem -- navigation menu ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Widget from nest.widgets.widget as Widget import Style from nest.widgets.style as Style class ShowcaseSlots(id, mode) from Widget(id) mode = mode jsMethods = [ "activate" => " function( id ) { for( var i = 0; i < this.count; i++ ) { var item = Nest.i(this.id + '.' + i ); if (i!=id) { item.className = 'nest_showcase_slot'; } else { item.className = 'nest_showcase_current'; } } } " ] init self.addStyle( "z-index:10; position:absolute;" ) if self.mode == 1 res = Nest.resLink("showcase_normal.png") style1 = Style( "Style for showcase slots items", nil, @" background-image: url('$(res)'); ", "span.nest_showcase_slot" ).setToClass( self ) res = Nest.resLink("showcase_current.png") style2 = Style( "Style for showcase currently shown slots items", nil, @" background-image: url('$(res)'); ", "span.nest_showcase_current" ).setToClass( self ) res = Nest.resLink("showcase_hover.png") style3 = Style( "Style for showcase slots items - hover", nil, @" background-image: url('$(res)'); ", "span.nest_showcase_slot:hover" ).setToClass( self ) end end function onRender() self.jsMethods["count"] = self.parent.children.len()-1 end function renderContent() res = "" p = self.parent pid = p.getFullID() count = p.children.len()-1 curID = self.getFullID() for i = 0 to count-1 id = curID + "." + i switch self.mode case 0 res += @" $(i)" case 1 res += @"  " end end return res end end /*# Showcase with timed show slots for rotating promotional messages. @param id Widget ID @param width width of the display area of the widget. @param height height of the display area of the widget. @optparam autoWait Automatic wait before rotating page (in ms) @optparam autoSpeed Length of the transition (in ms) @optparam transType Kind of transition (0-2) @optparam slotsMode text of the items in the ShowcaseSlots rack -- set nil to use stack images. This widget - @section styling Styling of the widget - nest_showcase_slot - nest_showcase_current @section Links from panels Links from the panels going to a certain location can be implemented directly by inserting links or click-sensible objects (images, buttons, etc.) as a child of the page widgets. However, if the the panel structure is self-explanatory enough for the user to understand that a click may generate a link to another page, it might be interesting to create a widget that is click-sensible. The simplest way to achieve this is just to set the \b onclick property on the widget that will be used as the showcase page, like in the following example. @code text0 = Text('text0', " Some text" ) image0 = Image('image0', "some_image.png") page0 = Widget('page0').set(["onclick"=>"window.location.href='"+Nest.pageLink('test')+"'"]) page0.addChildren( text0, image0 ) ... showcase.addChildren( page0, ... ) @endcode */ class Showcase(id, width, height, autoWait, autoSpeed, transType, slotsMode) from Widget(id) jsMethods = [ "slideNext" => "function(){}", "current" => "0", "transtype" => transType == 0 or transType == 1 or transType== 2 ? transType : 0, "intrans" => "false", "autoWait" => autoWait ? autoWait:0, "autoSpeed" => autoSpeed ? autoSpeed : 1000, "hideAll" => " function() { for( var item in this.pages ) { var div = this.pages[item]; div.style.display = 'inline-block'; } }", "setup" => " function() { for( var item in this.pages ) { var div = this.pages[item]; div.style.display = 'inline-block'; div.style.position = 'absolute'; div.style.top = '0'; div.style.left = '100%'; div.style.zIndex = 1; } var ent1 = this.pages[0]; if( this.autoWait > 0 ) { this.waitingFor = setTimeout('Nest.i(\"' + this.id +'\").slideNext()', this.autoWait ); } ent1.style.left = '0'; ent1.style.zIndex = 2; Nest.i( this.id + '.slots.0' ).className = 'nest_showcase_current'; }", "slideTo" => " function(pos) { if( this.current == pos || this.intrans ) return; if( this.waitingFor != null ) { clearTimeout( this.waitingFor ); this.waitingFor = null; } var ent1 = this.pages[this.current]; var entity = this.pages[pos]; this.current = pos; Nest.i(this.id + '.slots').activate( pos ); entity.style.zIndex=2; ent1.style.zIndex=1; Nest.i( this.id + '.slots' ).style.zIndex = 3; if( this.transtype == 0 || this.transtype == 1) { this.showSlide( entity, ent1 ); } else { entity.style.left = '0'; this.showXFade( entity, ent1 ); } if( this.autoWait > 0 ) { this.waitingFor = setTimeout('Nest.i(\"' + this.id +'\").slideNext()', this.autoWait+this.autoSpeed ); } }", "slideNext" => " function() { var pos1 = this.current + 1; if( pos1 >= this.pageCount ) pos1 = 0; this.waitingFor = null; this.slideTo(pos1); } ", "showXFade" => " function(entity, ent1) { this.intrans = true; var self = this; Nest.transite( ent1, this.autoSpeed, {'opacity':'|0.0|'}, {'opacity':'1.0'}, null, null ); Nest.transite( entity, this.autoSpeed, {'opacity':'|1.0|'}, {'opacity':'0.0'}, null, function(){self.intrans = false;} ); }", "showSlide" => " function(entity, ent1) { this.intrans = true; var self = this; var endfunc = function() { self.intrans = false; ent1.style.left = '100%';}; Nest.transite( entity, this.autoSpeed, {'left':'|0|%'}, {'left':'100%'}, null, endfunc ); if( this.transtype == 0 ) { Nest.transite( ent1, this.autoSpeed, {'left':'|-100|%'}, {'left':'0%'}, null, function(){ent1.style.left = '100%';} ); } }" ] onCreate = "this.setup();" sslot = ShowcaseSlots("slots", slotsMode) width = width height = height init Nest.requireJS("fx") self.addChild( self.sslot ) self.addClassInParentship( Showcase ) if width.typeId() != StringType: width = width.toString() + "px" if height.typeId() != StringType: height = height.toString() + "px" self.addStyle( @"display: inline-block; width:$(width); height:$(height); overflow:hidden; position:relative;" ); end function onRender() jsMethods = self.jsMethods jsMethods["pageCount"] = self.children.len()-1 jsMethods["pages"] = "Array()" count = 0; for child in self.children if child.baseClass() != ShowcaseSlots child.addStyle( "display: inline-block; position:absolute;z-index:0" ) jsMethods[@"pages[$(count)]"] = "Nest.i('" + child.getFullID() + "')" ++count end end end end frameworks/nest/nest/widgets/Submit.fal000066400000000000000000000020361176363201700205640ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Submit.fal AJAX widgeting subsystem -- Submit input tag. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 02 Oct 2011 13:53:51 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.Button in widget /*# Submit input tag. @param id The widget ID @param value The value attribute of the input tag. */ class Submit( id, value ) from widget.Button( id ) tag = "input" isSelfClosing = true init if self.props == nil: self.props = [=>] self.props['name'] = nil // will be filled at render self.props['type'] = "submit" self.props['value'] = value // TODO: Remove this when we have automatic parentship self.addClassInParentship( Submit ) end end frameworks/nest/nest/widgets/Table.fal000066400000000000000000000555401176363201700203600ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Table.fal AJAX widgeting subsystem -- Basic data-oriented table view. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget import from nest.widgets.action in action import from json in json /*# Class representing a table column @optparam name @optparam heading @optparam fmt @optparam nullrep */ class TableColumn( name, heading, fmt, nullrep ) fieldName = name heading = heading format = fmt nullRep = nullrep ordering = true // by default, class == name cls = name /*# Override or re-assign to provide column-specific data rendering */ renderData = { value => if value == nil return self.nullRep ? self.nullRep : "" end if self.cls cls=" class=\"" + self.cls + "\"" else cls = "" end if self.format return @"" + (@("$("+htmlEscape(toString(value))+":" + self.format + ")")) + "" else return "" + value + "" end } /*# Uses a column definition string to fill the information about this column. @param initData The column definition Format of column definition is: @code data|haeading|format|nil @endcode - @b data: The source data where the data from the column is taken. - @b heading: The name of the heading. - @b format: A render format for the data in the cell - @b nil: The text printed for NIL data. All fields are optional except the first. */ function fromHeading( initData ) recs = strSplit( initData, "|" ) rlen = recs.len() self.fieldName = recs[0] self.heading = rlen > 1 ? recs[1] : recs[0] if rlen > 2: self.format = rlen[2] if rlen > 3: self.nullRep = rlen[3] end end class ActionTableColumn( heading, keyFields, commentFields, actions ) heading = heading keyFields = keyFields commentFields = commentFields actions = actions actionSeparator = " " number = 0 combineComment = { data, fields => values = map( {v=> data[v]}, fields ) return " ".merge(values) } combineKey = { data, fields => values = map( {v=> data[v]}, fields ) return "|".merge(values) } renderData = { data => str = "" dict = [=>] if self.commentFields comment= self.combineComment( data, self.commentFields ) dict['comment'] = comment end key = self.combineKey( data, self.keyFields ) dict['key'] = key value = json.JSONencode( dict ) for item in self.actions str += item.render( value, self.number ) formiddle: str += self.actionSeparator end str +="" ++self.number return str } end class CheckTableColumn( colName, heading, keyFields, commentFields ) colName = colName heading = heading keyFields = keyFields commentFields = commentFields // This is automatically filled on render by our owner. table = nil number = 0 combineComment = { data, fields => values = map( {v=> data[v]}, fields ) return " ".merge(values) } combineKey = { data, fields => values = map( {v=> data[v]}, fields ) return "|".merge(values) } renderCheckboxKey = { data => return self.combineKey( data, self.keyFields ) } renderCheckboxComment = { data => if self.commentFields return self.combineComment( data, self.commentFields ) end return "" } renderData = { data => str = "" str +="" str +="" ++self.number return str } function makeHeading() str = self.heading + "
    " return str end end class TableBody( id ) from widget.Widget( id + ".tb" ) /*# Side on which to print the order text. - -1 = left - 0 = None - 1 = right */ orderSide = 1 textBeforeAsc = " " /*# Text to print to put the ascending order. */ textOrderAsc = "(^)" /*# Text to print to put the descending order. */ textOrderDesc = "(V)" /*# Text for active order ascending */ textActiveAsc = "[^]" /*# Text for active descending order. */ textActiveDesc = "[V]" textAfterDesc = " " init // TODO: Remove this when we have automatic parentship self.addClassInParentship( TableBody ) end function renderContent() str = "\n" str += "" headers = self.parent.headers data = self.parent.data // prepare the header row col = 1 for tc in headers ordering = nil if self.orderSide != 0 and tc provides ordering and tc.ordering ordering = self.textBeforeAsc if col == self.parent.orderColumn // positive ordering += self.textActiveAsc else ordering += "" + self.textOrderAsc + "" end if col == -self.parent.orderColumn // negative ordering += self.textActiveDesc else ordering += "" + self.textOrderDesc + "" end ordering += self.textAfterDesc end str += "" col++ end str += "\n" // now read the data. for row in data str += "" for tc in headers if tc provides fieldName if tc.fieldName in row str += tc.renderData( row[tc.fieldName] ) else str += tc.renderData( nil ) end else str += tc.renderData( row ) end if tc provides renderCheckboxKey and tc provides colName and tc provides renderCheckboxComment self.parent.addCheckboxData( tc.colName, tc.renderCheckboxKey(row), tc.renderCheckboxComment(row) ) end end str += "\n" end str +="
    " if ordering and self.orderSide < 0: str += ordering str += tc provides makeHeading ? tc.makeHeading() : tc.heading if ordering and self.orderSide > 0: str += ordering str += "
    \n" return str end end class PageCursor( id ) from widget.Widget( id + ".pc" ) /*# Text used to render the 'first' marker */ firstText = "<<" /*# Text used to render the 'pervious' marker */ prevText = "<" /*# Text used to render the 'next' marker */ nextText = ">" /*# Text used to render the 'last' marker */ lastText = ">>" /*# Cell style or class (or both) */ cellStyle = "" /*# Style for current page */ currCellStyle = "" /*# Maximum number of previous pages with respect to current page to be seen */ prevPages = 3 /*# Maximum number of next pages with respect to current page to be seen */ nextPages = 3 /*# Number of first pages always shown */ firstPages = 2 /*# Number of last pages always shown */ lastPages = 2 /*# Ellips to represent pages out of range */ outOfRangePages = "..." makeCurrentCell = { value => "" + value + "" } function renderContent() // fetch some (sanitized) value from parent ipp = self.parent.itemsPerPage < 0 ? self.parent.data.len() : self.parent.itemsPerPage ic = self.parent.itemCount < 0 ? self.parent.data.len() : self.parent.itemCount items = self.parent.itemCount firstItem = self.parent.firstItem // do we really need to be printed? if ic <= ipp: return "" // ok, we're paged -- calculate the current page. cp = int( firstItem / ipp ) + 1 // bootstrap the string str = "\n" // previous markers if cp > 1 str += "\n" if cp > 2 str += "\n" end // first pages fp = self.firstPages if fp >= cp: fp = cp - 1 for i = 1 to fp, 1 str += "\n" end // Minimum page minPage = cp - self.prevPages if minPage <= fp minPage = fp+1 else str += "\n" end for i = minPage to cp - 1, 1 str += "\n" end end // the current page. str += "\n" // last part of the page list if firstItem + ipp < items maxPage = cp + self.nextPages totPages = int( items / ipp ) if items % ipp != 0: totPages += 1 maxCount = maxPage > totPages ? totPages : maxPage for i = cp + 1 to maxCount, 1 str += "\n" end if maxCount != totPages // last pages lp = totPages - self.lastPages + 1 if lp <= maxCount lp = maxCount + 1 else str += "\n" end for i = lp to totPages, 1 str += "\n" end end if cp + 1 < totPages str += "\n" end if cp < totPages // always display last if not last page str += "\n" end end // close the table str += "
    " + self.firstText + "" + self.prevText + "" + i + "" + self.outOfRangePages + "" + i + "" + self.makeCurrentCell(cp) + "" + i + "" + self.outOfRangePages + "" + i + "" + self.nextText + "" + self.lastText + "
    \n" return str end end /*# Base abstract Class representing a table. @param id The widget id @param itemsPerPage The number of items shown for each page @param itemCount Number of total items in the data set, or -1 for all in self.data @param firstItem first item shown (0 based) @param orderColumn column having ordering priority in display (negative for descending) (1 based) This is a base abstract class that must be reimplemented to provide a fully working, ajax-sensible multi-page table widget. @note It is possible to have a single-page table widget setting itemsPerPage = itemCount. The subclasses must reimplement the following methods: - makeColumns() -- called back to re-create the heading and columns information of the table. - setDataView() -- called back to update the viewable dataset in the current page. On AJAX request, the Nest AJAX system takes care of re-creating the widget with its ID and creation parameters (itemsPerPage, itemCount, currentPage, orderColumn); it's responsibility of the concrete subclass to recreate the headers and set the right dataset portion in @b data as it has to be viewed. @note It is not strictly necessary to set a valid value for itemCount at object creation if setDataView() is able to recalculate it properly. @note Pages and colums are numbered starting from 1. @section nest_wid_table_style Styling the table. The table is fully represented under a div that will be the topmost widget. The div ID is the ID declared in the table widget. It may be given a class by adding a property 'class' to its props dictionary. The widget contains two sub-widgets called Table.tablebody and Table.pagecursor. They both are rapresented in a div which can be given a specific class through the props dictionary, and they both appare as a table inside their div element. The ID of the div element of Table.tablebody is set to the parent ID plus ".tb", while the ID of the div element of Table.pagecursor is set to the parent ID plus ".tc". This widget doesn't come with any style whatsoever. */ class Table( id, itemsPerPage, itemCount, firstItem, orderColumn ) from widget.Widget( id ) tablebody = TableBody( id ) pagecursor = PageCursor( id ) isAJAXHost = true /*# The headers of the table body. Each entry is an instance of class TableColumn Format of each field in headers is @code data|haeading|format|nil @endcode - @b data: The source data where the data from the column is taken. - @b heading: The name of the heading. - @b format: A render format for the data in the cell - @b nil: The text printed for NIL data. */ headers = [] /*# Where to store the array of dictionaries containing the required data */ data = nil /*# First item in view */ firstItem = firstItem ? firstItem : 0 /*# Count of items in view -- defaults to 20 */ itemsPerPage = itemsPerPage ? itemsPerPage : 20 /*# Count of total items in the dataset */ itemCount = itemCount ? itemCount : 0 /*# Column to be ordered. */ orderColumn = orderColumn ? orderColumn : 0 /*# Methods for the javascript part */ jsMethods = [=>] /* A filter in format field:string */ filter = "" checboxData = [=>] init self.initInfos = [self.itemsPerPage, self.itemCount, self.firstItem, self.orderColumn] // initialize our javascript counterpart self.onCreate = @" if ( ! this.itemsPerPage ) { this.itemsPerPage = $(self.itemsPerPage); this.firstItem = $(self.firstItem); this.column = $(self.orderColumn); this.filter = \"$(self.filter)\"; } " self.addChild( self.tablebody ) self.addChild( self.pagecursor ) self.makeColumns() self.setDataView() // TODO: Remove this when we have automatic parentship self.addClassInParentship( Table ) end function AJAX( params ) msg = params["msg"] pars = params["params"] self.firstItem = int(pars["fi"]) self.itemsPerPage = int(pars["ipp"]) self.orderColumn = int(pars["column"]) self.filter = pars["filter"] if msg == "goto" or msg == "order" or msg == 'filter' or msg == 'refresh' self.setDataView() end retval = [ self.msgSetProperty( 'innerHTML', self.render() ) ] if self.checboxData retval += self.msgInvoke( 'setCheckboxData', self.checboxData ) end return retval end function onRender() // hook for check columns self.checboxData = [=>] if self.jsMethods: return self.jsMethods['ckcols'] = "{};" // As messages may be called on create, force to call onCreate() self.jsMethods['goToPage'] = \ "function( page ) { this.firstItem = (page-1)*this.itemsPerPage; " + self.ajaxMsg( "goto", self._makeAjaxDict() ) + "; }" self.jsMethods['refresh'] = \ "function() {" + self.ajaxMsg( "refresh", self._makeAjaxDict() ) + "; }" self.jsMethods['setOrder'] = \ "function( col ) { this.column=col; " + self.ajaxMsg( "order", self._makeAjaxDict() ) + "; }" self.jsMethods['setFilter'] = \ "function( filter ) { this.onCreate(); this.filter=filter; " + self.ajaxMsg( "filter", self._makeAjaxDict() ) + "; }" self.jsMethods['onCheckCol'] = " function( cb, num, name ) { this.ckcols[name][num].checked = cb.checked; if( ! cb.checked ) { document.getElementById(this.id + '.'+name).checked = false; } }" self.jsMethods['setCheckboxData'] = " function( value ) {this.ckcols = value;}" self.jsMethods['onCheckAllCol'] = " function( cb, name ) { for( var num = 0; num < this.ckcols[name].length; ++num ) { this.ckcols[name][num].checked = cb.checked; document.getElementById( this.id + '.' + name + '.' + num ).checked = cb.checked; } }" self.jsMethods['clearChecks'] = " function() { for( var name in this.ckcols ) { document.getElementById( this.id + '.' + name ).checked = false; for( var num = 0; num < this.ckcols[name].length; ++num ) { this.ckcols[name][num].checked = false; document.getElementById( this.id + '.' + name + '.' + num ).checked = false; } } }" self.jsMethods['withSelected'] = " function( name, callback ) { var res = new Array(); for( var num = 0; num < this.ckcols[name].length; ++num ) { if( this.ckcols[name][num].checked == true ) { res.push( this.ckcols[name][num] ); } } callback( res ); }" end function _makeAjaxDict() return "{\"fi\": this.firstItem, \"ipp\": this.itemsPerPage, \"column\":this.column, \"filter\": this.filter}" end /*# Helper method to create the column data through string defintions. @param headers An array of header definition Format of each field in headers is @code data|haeading|format|nil @endcode - @b data: The source data where the data from the column is taken. - @b heading: The name of the heading. - @b format: A render format for the data in the cell - @b nil: The text printed for NIL data. @note This just iteratively adds a new TableColumn instance calling its @a TableColumn.fromHeading method. @note Pages and colums are numbered starting from 1. */ function columsFromHeadings( headers ) for head in headers tc = TableColumn() tc.fromHeading( head ) self.headers.add( tc ) end end /*# Adds a widget that is intended as a filter. @param wid A widget for which this table waits for a change message. @param filterPrefix If given, messages from this widget will be prefixed by this text and ":". When the widget in @b wid issues a change message, the value of the message is sent to the setFilter javascript method of this table, which, by default, sends a "filter" AJAX message to its AJAX widget host. The filterPrefix may be used to tell different filters. */ function addFilterWidget( wid, filterPrefix ) if not self.jsListeners: self.jsListeners = [=>] self.jsListeners[wid] = \ "function( source, msg, value ) { if (msg == 'change') { this.setFilter( " + (filterPrefix ? "\"" + filterPrefix + ":\"+": "" ) + "value );} }" end /*# Fixes the first item and return a last item according to itemCount and ItemPerPage. \return a suitable value for the last item to be shown, to create a partition in a data array This method perform some sensible checks on the values of the firstItem property and calculates a lastItem that should be displayed (excluded, as in range format), considering the itemsPerPage and itemCount properties. */ function sanitizeItemRange() if self.itemCount <= self.itemsPerPage self.firstItem = 0 elif self.firstItem >= self.itemCount self.firstItem = self.itemCount-1 end if self.firstItem < 0 self.firstItem = 0 end lastItem = self.firstItem + self.itemsPerPage if lastItem > self.itemCount: lastItem = self.itemCount return lastItem end function addActionColumn( heading, keyFields, commentFields, actions ) self.headers.add( ActionTableColumn( heading, keyFields, commentFields, actions ) ) end function addCheckboxColumn( colName, heading, keyFields, commentFields ) col = CheckTableColumn( colName, heading, keyFields, commentFields ) col.table = self self.headers.add( col ) end function addCheckboxData( colName, key, comment ) if colName notin self.checboxData arr = [] self.checboxData[colName] = arr else arr = self.checboxData[colName] end arr += [ "key" => key, "comment" => comment ] end //========================================================================== // Overrides // /*# Called back on AJAX requests to set the data view This method should read the current class values of firstItem and itemPerPage, and set the values into self.data accordingly @note Pages and colums are numbered starting from 1. So, the item with id 0 is located at top of page 1. */ function setDataView() raise "Please override setDataView" end /*# Called back to create the table heading. This method should put a set of TableColumn instances in the @a Table.headers array. @note It might be useful to use the helper method @a Table.columsFromHeadings. */ function makeColumns() raise "Please override makeColumns" end end frameworks/nest/nest/widgets/TableForm.fal000066400000000000000000000036051176363201700211770ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: ListForm.fal AJAX widgeting subsystem -- Form rendered as a list of items ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 28 Sep 2011 11:31:25 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.Form in wid /*# Table-specialized form. This specialization of the form class is built to render its own children as a list of items. It is very common to use a stylesheet to transform UL/LI/label element sequences into well formatted fields. */ class TableForm( id, action, method ) from wid.Form( id, action, method ) init // TODO: Remove this when we have automatic parentship self.addClassInParentship( TableForm ) end /*# Called back in rendering each element of the form. @param child A child widget of this form. The base version just renders the prompt via child.renderPrompt() and then renders the body via child.render(). */ function renderChild( field ) return "" + field.renderLabel() + "" + field.renderCore() + " " + field.renderInfo() + "\n" end /*# Renders the start entry of the form elements. @return A string that is used before rendering any element. The base class returns an empty string. */ function renderFormBegin() return "\n" end /*# Renders the end entry of the form elements. @return A string that is used before after rendering all the elements. */ function renderFormEnd() return "
    \n" end end /* end of TableForm.fal */ frameworks/nest/nest/widgets/Text.fal000066400000000000000000000016011176363201700202420ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: Text.fal AJAX widgeting subsystem -- Simple text/label widget (span). ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from nest.widgets.widget in widget class Text( id, deftext ) from widget.Widget( id ) tag = "span" content = deftext ? deftext : "" //# in forms, we do have values isValueHost = true init // TODO: Remove this when we have automatic parentship self.addClassInParentship( Text ) end function renderContent() return self.content end end frameworks/nest/nest/widgets/action.fal000066400000000000000000000042721176363201700206020ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: action.fal AJAX widgeting subsystem -- Action for tables. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 02 Oct 2011 13:53:51 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ class Action( owner, message, props ) owner = owner message = message props = props function makeMsg( keyvalue ) id = self.owner.getFullID() msg = self.message keyvalue = htmlEscape( keyvalue ) return @"Nest.message(Nest.i('$(id)'), '$(msg)', $(keyvalue) )" end function renderProps() str = "" if self.props.typeId() == DictionaryType for key, value in self.props // add a " " at the end anyhow to space the rendering str += key + "=\"" + htmlEscape( value ) + "\" " end end return str end end class ActionButton( content, owner, message, props ) from Action( owner, message, props ) content = content function render( keyvalue, number ) id = self.owner.getFullID() msg = self.makeMsg( keyvalue ) props = self.renderProps() return @"" end end class ActionLink( content, owner, message, props ) from Action( owner, message, props ) content = content function render( keyvalue, number ) id = self.owner.getFullID() msg = self.makeMsg( keyvalue ) props = self.renderProps() return @"$(self.content)" end end class ActionCheckBox( name, owner, message, props ) from Action( owner, message, props ) name = name function render( keyvalue, number ) id = self.owner.getFullId() props = self.renderProps() return @" " end end frameworks/nest/nest/widgets/menustyles.fal000066400000000000000000000036171176363201700215370ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: menustyles.fal AJAX widgeting subsystem -- Minimal CSS3 styles for CSS menus ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 14 Apr 2012 20:00:06 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Style from nest.widgets.style as Style class MenuStyles lms0 = Style( "Menu", nil, "list-style:none; display:inline-block;" ) lms1 = Style( "Submenu", nil, "list-style:none;", "ul" ) lms2 = Style( "Menu item hover", nil, "position: relative;", "li:hover" ) lms3 = Style( "Menu item hover to submenu", nil, "display:block;", "li:hover> ul" ) lms5 = Style( "Submenu for submenu", nil, "position: absolute; left: 100%; top: 0;", "ul ul" ) lms6 = Style( "Submenu items", nil, "display: block; float: none;", "ul li" ) lmsh0 = Style( "Horizontal submenu", nil, " display: none; position: absolute; left: 0; top: 100%; ", "ul" ) lmsh1 = Style( "Horizontal Menu item", nil, " display:inline-block; float:left; margin:0; pading:0; ", "li" ) lmsv0 = Style( "Vertical submenu", nil, " display: none; position: absolute; left: 100%; top: 0; ", "ul" ) lmsv1 = Style( "Horizontal Menu item", nil, " display:block; margin:0; pading:0; ", "li" ) menu_styles_common = nil menu_styles_horiz = nil menu_styles_vert = nil init self.menu_styles_common = .[self.lms0 self.lms1 self.lms2 self.lms3 self.lms5 self.lms6 ] self.menu_styles_horiz = .[self.lmsh0 self.lmsh1] self.menu_styles_vert = .[self.lmsv0 self.lmsv1] end end frameworks/nest/nest/widgets/style.fal000066400000000000000000000044311176363201700204620ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: style.fal AJAX widgeting subsystem -- stylesheet generator ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# Entity representing a CSS style to be applied to one or more widgets. @param name A logical name for the style @optparam mode The CSS pseudo-class for the style ("hover", "focus" etc.) @optparam css The css declarations @optparam spec The Extra specification for the style (sub-class, sub-ids) */ class Style( name, mode, css, spec ) name = name css = css mode = mode spec = spec /**# Apply this CSS to the given widget by ID @param widget The widget to which this CSS elements must be applied. @return self to allow chains of calls */ function setToID( widget ) widget.addStyleAsID( self ) return self end /**# Apply this CSS to the given widget by class. @param widget The widget to which this CSS elements must be applied. @return self to allow chains of calls When this widget is rendered, the style is made valid for all the widgets of this same class. */ function setToClass( widget ) widget.addStyleAsClass( self ) return self end /**# Apply to all the widgets that have a certain Widget (Falcon) class or inherit from that. @param wcls The class (by name; it's the class name as a string). @return self to allow chains of calls When a widget with this class is created, Nest will automatically add this style to the CSS class styles of the newly added widget. The @b wcls parameter is the class of the widget as known by Falcon, not the CSS class that the widget declares. If two widgets of the same Falcon class declare a different CSS class, Nest generates a copy of the declaration to make sure both the CSS classes are covered. */ function applyToClass( wcls ) Nest.addClassStyle( wcls, self ) return self end end frameworks/nest/nest/widgets/widget.fal000066400000000000000000000665461176363201700206240ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Nest - Falcon web applcation engine FILE: widget.fal Basic definition for the AJAX widgeting subsystem. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 26 Sep 2011 11:55:58 +0200 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from json in json /*# @brief Parameter indicator to be used in AJAX and Javascript composite calls. @param name The name of the parameter @param value The value to be associated to the parameter. This object is rendered so that the JavaSript side creates an object containing the required name and value. Special prefixes of the @name property allows to access to runtime values in the remote page. The object is then passed as a parameter to AJAX or JavaScript functions. Many Nest widgets consider their parameter to be an array of JSP instances, that are then rendered to an object for the JavaScript and AJAX functions to use. This is a typical usage in a Falcon side script: @code <? infos = .[ JSP("string", "hello world") JSP("!number", 20 ) JSP("!variable", "js_expr() + varname" ) JSP(".jsonObject", [ "json_dict" => [1,true,nil] ] ) JSP("/live_property", "../widget1/property1") JSP("*property_as_name", "../form/input/value") JSP("#property_as_path", "../some/widget/value") ] print( myWidget.ajaxMsg( "message", infos ) ) ?> @endcode If @b name doesn't start with any marker, then @b value is passed in the resulting dictionary as a string (string characters get escaped). If @name starts with "!", then @b value is considered a literal expression to be placed in the javascript. For instance If @name starts with a dot, ".", then the value will be considered an expression to be placed as a json object. If @b name starts with "/" then the @b value is considered a path to a property relative to the widget where the parameters are used. For instance, - "value" reprsents the property "value" of this object. - "../value" represents the property "value" of the parent object. - "../sibling/value" represents the text value of an object called "sibling" being rendered at the same level of this widget. - "/a/b/value" is the text value of a widget "b" under widget "a" in the topmost widget parent of this one. Normally, the found value is stored as a part of a dictionary given to the target AJAX or JS function, with the @b name parameter as the JS dictionary key. If @name is "*", the @b value is still a path to a live property in the page, but the key is derived from the NAME property in the target item. If @name is "#", the @b value is still a path to a live property in the page, but the key is the document ID of the target widget. */ class JSP( name, value ) name = name value = value /*# @brief Creates a JS script to generate this JSP in JavaScript @optparam widId if given, paths are relative to that widget ID. @return A string containing a Nest invocation to create this parameter. */ function render( widId ) if widId: return "(Nest.startPar('"+widId+"')." + self.renderInner() + ".gen())" return "(Nest.startPar()." + self.renderInner() + ".gen())" end /*# Render only the inner part of the parameter generator */ function renderInner() name = self.name value = self.value switch name[0:1] // JS expression case "!": return "add('" + name[1:] + "',"+ value + ")" // Objects case ".": return "add('" + name[1:] + "',"+ json.JSONencode( value )+ ")" // Paths case "/": return "addPath('" + name[1:] + "', '"+ value + "')" // Paths -- as name case "*": return "addName('" + value + "')" // Paths -- as ID case "#": return "addId('"+ value + "')" default return "add('"+ name + "','"+ value.toString().replace("'", "\\'") + "')" end end /*# @brief Creates a JS script to generate the JSP list (static) @param list An array or sequence of JSP entities or strings. @optparam widId if given, paths are relative to that widget ID. If the entry is providing a renderInner (as JSP), that method is called, otherwise the string is rendered as a JS call to "Nest.addTPath()". This causes the string to be interpreted as a relative path, and the name of the variable as passed in the parameters will be the "inner id": the relative path stripped of initial parent references "../" and with "/" changed into ".". @return A string containing a Nest invocation to create a parameter list */ function renderList( list, widId ) for jsp in list forfirst res = widId ? @"(Nest.startPar('$(widId)')." : "(Nest.startPar()." end res += jsp provides renderInner ? jsp.renderInner() : "addTPath('"+jsp+"')" formiddle res += "." end forlast res += ".gen())" end end if not res: res = "null" return res end end /*# Base class for the widgeting system. @param id The id of the widget as know in the HTML/AJAX rendering. @note All the widget subclasses willing to become AJAX hosts MUST have an @b id parameter as their @b first parameter in the class constructor. This @b id parameter will be propagated through the widget hierarcy up to this base class, always as first parameter. */ class Widget( id ) //# 'local' ID of this widget in the DOM model. id = id //# HTML Tag associated with this widget. tag = "div" //# HTML properties associated with this widget at DOM level. props = nil //# Custom message handlers. ajaxMessages = nil //# list of children. children = [] //# dictionary of children ordered by their ID. childrenById = nil //# parent of this widget. parent = nil /*# Dictionary of javascript methods to be added to this widget. Each entry is a method that can be invoked on the javascript side. The key is the method name, and the value must be a full javascript function declaration. */ jsMethods = [=>] /*# Dictionary of javascript Listeners Each entry is a method that can be invoked on the javascript side. The key is the method name, and the value must be a full javascript function declaration. */ jsListeners = [=>] //# Some widget cannot be closed with the XML self-closing tag. isSelfClosing = false //# Method invoked at javascript level after rendering. onCreate = nil /*# If true, this is a ajax host, and can receive ajax messages directly. */ isAJAXHost = false /*# If true, this widget is also a value host (i.e. has value in forms). */ isValueHost = false /*# Init informations. This are the parameters that must be repeated to create a copy of this widget. The @b id field alone needs not to be repeated as it is automatically added in front of every initialization information as the widget is created to fulfil AJAX host requests. */ initInfos = nil //# Name of the field in a data provider associated with this widget datafield = nil //# Text or widget used as label/prompt form or form-like widgets label = nil //# Text or widget associated with this field in form or form-like widgets fieldInfo = nil //# Thing to be added automatically to names when generating the name field. addName = nil //# Set true to put label AFTER the main widget in standard representation labelsOnRight = false //# Stylesheet for this item for automated stilesheet generation in page idStyles = nil //# Stylesheet for this class of items, for automated stilesheet generation in page classStyles = nil //# TODO: remove when we have automatic parentship _parentship = [] init Nest.requireJS("base") Nest.subscribeWidget( self ) // TODO: use parentship in new engine to have Nest do it automagically self.addClassInParentship( Widget ) end //# Invoked by widget creation in Nest framework function setup() self.onSetup() rend = self.renderJSMethods() // Listeners go after methods, as listener usually refer to methods. rend += self.renderAJAXMessages() rend += self.renderJSListeners() if self.isAJAXHost rend += self.renderInitInfos() end if self.onCreate if self.onCreate.isCallable() value = self.onCreate() else value = self.onCreate end rend += "element.onCreate = function(){"+ value + ";}\n" end if rend: return "element=Nest.i('" + self.getFullID() + "');\n" + rend end /*# Called back at widget creation. This is invoked when all the structure of the widget is complete. Here the widget should initialize: - jsMethods - jsListeners - ajaxMessages */ function onSetup() end //# Adds a child to this widget. function addChild( wid ) wid.parent = self self.children += wid if self.childrenById self.childrenById[wid.id] = wid else self.childrenById = [ wid.id =>wid ] end return self end //# Shortcut to add multiple children function addChildren() for child in passvp() self.addChild( child ) end return self end /*# Adds children to this widget using a list of string. @optparam choices An array of choices (strings). This method adds automatically a set of common children which depends on the parent subwidget type. Container classes that accept this automatic child addition protocol must provide an overload for @a Widget.makeAutoChild, which returns the created widget, or nil if the widget cannot be created. Each element in choice is a string with the following format: "value:text". If ':' is not in the choice string, then the whole text will be used both as a submit value and as a text. If the value part is preceeded by an asterisk (*), then the entry will be checked, selected, specially marked or put in evidence, depending on the type of container. If the text part ends with a dollar mark ('$'), then the label is placed at right in rendering. */ function autoAddChildren( choices ) for value in choices if (pos = value.find(':')) >= 0 text = value[pos+1:] value = value[0:pos] else // don't copy, so the changes we do below apply to both text = $value end if value.startsWith( '*' ) checked = true value = value[1:] else checked = false end if text.endsWith('$') text = text[0:-1] onRight = true else onRight = false end child = self.makeAutoChild( value, text, checked ) if child child.labelsOnRight = onRight self.addChild( child ) end end end /*# Callback used by autoAddChildren to create a proper child for this container. @param value The value of this choice when submitted. @param text The text or label associated with this choice. @optparam checked If true, the element check or highlight should be actived. @return A valid widget for the subclass container or nil if the widget cannot be created. */ function makeAutoChild( value, text, checked ) return nil end /*# Method used to set properties of the widget. @param props the properties to be set in the widget. @return the widget itself. */ function set( props ) if self.props == nil self.props = props else self.props += props end return self end //# Renders an XHTML representation of this widget. function render() if self.labelsOnRight return self.renderCore() + self.renderLabel() + self.renderInfo() else return self.renderLabel() + self.renderCore() + self.renderInfo() end end function renderCore() id = self.getFullID() // calculate a default name, if required ... if 'name' in self.props // then, if the name was set to nil... if not self.props['name'] name = id if self.addName: name += self.addName self.props['name'] = name end end // and the class for the CSS // TODO: use the proper parentship on the new engine. if not Nest.genWidgetStyles or self.classStyles or Nest.hasOneOfClassStyles( self._parentship ) self.CSSClassID() end // ... then allow the widget to self-render. self.onRender() // render attributes -- if we have a tag we're an element in the document. if self.tag rend = "<" + self.tag + " id=\"" + id + "\" " rend += self.renderProps() if self.isSelfClosing and not self.children rend += "/>" else rend += ">" rend += self.renderContent() rend += "\n" end else rend = "" if self.isAJAXHost // in case of ajax hosting, we need a "mute div" to have a document entity with our name. rend +="
    " end // otherwise we're just a set of children entities. rend += self.renderContent() end return rend end function renderJSMethods() rend = "" for mthName, mthDef in self.jsMethods // Add a ; just in case if mthDef.isCallable() mthDef = mthDef() end rend += "element." + mthName + "=" + mthDef + ";\n" end return rend end function renderJSListeners() rend = "" for widget, mthDef in self.jsListeners if mthDef.isCallable() mthDef = mthDef() end rend += "Nest.listen( element, '"+ widget.getFullID() + "'," + mthDef + ");\n" end return rend end /*# Renders the contents of this widget. @return A string with the full HTML contents of this widget rendered. @note A sub-widget may define renderPrompt method to differentiate the prompt from the main body. */ function renderContent() rend = "" for child in self.children forfirst: rend += "\n" rend += child.renderInParent( self ) +"\n" end return rend end /*# Allows children to be rendered back by a parent. @param parent The parent where the child is being rendered. Override this if you want the children to have specific behavior when rendered in some parent. The default behavior is that to call directly parent.renderChild(). */ function renderInParent( parent ) return parent.renderChild( self ) end /*# Override this for personalized label/content/info display @param child The child to be rendered. The default is to put a br tag after each child. */ function renderChild( child ) return child.render() end function renderLabel() if self.label if self.label provides render: return self.label.render() return self.label end return "" end function renderInfo() if self.fieldInfo if self.fieldInfo provides render: return self.fieldInfo.render() return self.fieldInfo end return "" end //# private function renderInitInfos() id = self.getFullID() if self.initInfos obj = json.JSONencode( [id] + self.initInfos ) else obj = "[\"" + id +"\"]" end rend = @"element.Nest_initInfo = $(obj);\n" return rend end //# private function renderProps() rend = "" for key, value in self.props rend += key + "=\"" + htmlEscape(toString(value)) + "\"" formiddle: rend += " " end return rend end //# private function renderAJAXMessages() rend = "" for key, value in self.ajaxMessages rend += "Nest.messageHandlers['" + key +"']=" + "{ object: element, method: " + value + "};\n" end return rend end /*# Returns the full ID of this widget (including the parent ones). @return A string representing the full ID of this widget at DOM model level. The full-id of a widget is its own ID preceded by a dot-separated list of the parent ids up to the root. */ function getFullID() if self.parent pid = self.parent.getFullID() if pid.typeId() != StringType inspect( pid ) end return pid + "." + self.id end return self.id end /*# Get the topmost widget class name. */ function parentName() if self.parent return self.parent.parentName() end return self.className() end /*# Get the topmost widget class name with ajax host capabilities. */ function ajaxHostName() if self.isAJAXHost or not self.parent return self.className() else return self.parent.ajaxHostName() end end /*# Utility get ourselevs at Javascript level during render. Returns a string defined as document.getElementById('"+self.getFullID()+ "') @note getFullID() is completely defined only at render time. */ function getJSSelf() return "document.getElementById('"+self.getFullID()+ "')" end /*# Utility to emit a callback message. @param msg The name of the message generated by the widget in the browser page. @param value A value associated with the message (may be any valid javascript expression). To be used in form @code wid.jsMethods = [ "onclick" => wid.makeEmit( "onclick", "this.value" ) ] @endcode */ function makeEmit( msg, value ) return "function(){ Nest.message(this, '" + msg + "', " + value +"); }" end function makeInvoke( method, param ) param = json.JSONencode( param ) id = self.getFullID() return @"function() { Nest.processMessage({ message:'invoke', id:\"$(id)\", method:\"$(method)\", param:$(param)});}" end function makeSetProperty( prop, value ) id = self.getFullID() return @"function(){Nest.processMessage({ message:'set', id:\"$(id)\", property:\"$(prop)\", value:\"$(value)\"});}" end function makeSetStyle( prop, value ) id = self.getFullID() return @"function(){Nest.processMessage({ message:'set_style', id:\"$(id)\", property:\"$(prop)\", value:\"$(value)\"});}" end /*# Utility to create the code to invoke a nest javascript local message. @param msg The name of the message generated by the widget in the browser page. @param value A value associated with the message (may be any valid javascript expression). @note The value member is directly placed in the javascript invocation; if it's meant to be a string, proper quotes must be added inside the string. */ function jsEmit( msg, value ) return "Nest.message(this, '" + msg + "', " + value +");" end function jsInvoke( method, param ) param = json.JSONencode( param ) id = self.getFullID() return @"Nest.processMessage({ message:'invoke', id:\"$(id)\", method:\"$(method)\", param:$(param)});" end function jsSetProperty( prop, value ) id = self.getFullID() return @"Nest.processMessage({ message:'set', id:\"$(id)\", property:\"$(prop)\", value:\"$(value)\"});" end function jsSetStyle( prop, value ) id = self.getFullID() return @"Nest.processMessage({ message:'set_style', id:\"$(id)\", property:\"$(prop)\", value:\"$(value)\"});" end /*# Utility to generate a method calling another method. @param funcname The name of the message generated by the widget in the browser page. @optparam ... Other values to be passed in the call. Can be put into any nest widget. @code wid.jsMethods = [ "onclick" => wid.makeCall( "onclick", "this.value" ) ] @endcode */ function makeCall( funcname ) v = passvp() if v vals = ",".merge(map({x=>"\""+toString(x).escape()+"\""}, v)) else vals = "" end return "function(){Nest.i(\""+self.getFullID()+"\")."+funcname+"("+ vals +");}" end /*# Utility to generate a direct JS call to this nest widget. @param funcname The name of the message generated by the widget in the browser page. @optparam ... Other values to be passed in the call. Can be put into any nest widget. @code wid.jsMethods = [ "..." => "function(){ ...." + wid.jCall( "onclick", "this.value" ) +".something; ...}" ] @endcode */ function jsCall( funcname ) v = passvp() if v vals = ",".merge(map({x=>"\""+toString(x).escape()+"\""},v)) else vals = "" end return "Nest.i(\""+self.getFullID()+"\")."+funcname+"("+ vals +")" end /*# Called back right before rendering. Widget are often created and then added to a parent. This means that the information about their hierarcy is not ready at creation, but it's ready at render. This callback allows to do last setups before the widget is rendered. */ function onRender() end /*# Sets the value of the widget before its rendering. */ function setValue( val ) if self.props == nil: self.props = [=>] self.props['value'] = val end /*# Gets a pre-render value set in the widget. @return A value that is known to be associated with this widget. Possibly overridable; the default is to peek the value in the 'value' property. Complex widgets as containers may have different definitions of their value. */ function getValue() if self.props and 'value' in self.props: return self.props['value'] return nil end /*# Route a set of incoming post values in a widget hierarcy. @param vals A dictionary of routable values. @param top ID of the parent widget. */ function routeValues( vals, top ) //> "Routing at "+top+"
    " current = top ? top+"."+self.id : self.id if current in vals //> "Rendering "+vals[current]+"
    " self.setValue( vals[current] ) end //> "Subroutong...
    " for item in self.children item.routeValues( vals, current ) end end /*# Adds this style to the 'style' property of this widget. */ function addStyle( style ) if not self.props self.props = ["style" => style] else if "style" in self.props self.props["style"] += ";" + style else self.props["style"] = style end end return self end /*# Creates a method that sends a request remotely. @param msg The message to be sent to this widget @optparam params An array of @a JSP instances or a pre-formatted string */ function ajaxMsg( msg, params ) id = self.getFullID() infos_s = params.typeId() == StringType ? params : JSP.renderList(params,id) return "Nest.widgetAJAX('" + self.ajaxHostName() + "', '" + id + "', '" + msg + "', " + infos_s + ");" end function makeAjaxMsg( msg, params ) return "function(){" + self.ajaxMsg(msg, params) + ";}" end //=================================================== // Data management // function setData( provider ) if self.datafield provider.set( self.datafield, self.getValue() ) end for child in self.children child.setData( provider ) end end function getData( provider ) if self.datafield // the data provider is bound to raise if the field is not valid. self.setValue( provider.get( self.datafield ) ) end for child in self.children child.getData( provider ) end end //=================================================== // CSS batch generator interface // function CSSClassID() if self.props == nil CSS_id = "nest_widget_" + self.className() self.props = ["class"=> CSS_id] elif "class" notin self.props CSS_id = "nest_widget_" + self.className() self.props["class"] = CSS_id else CSS_id = self.props["class"] end return CSS_id end // TODO: use parentship in new engine to have Nest do it automagically function addClassInParentship( cls ) name = cls.className() self._parentship += name end function addStyleAsID( style ) if self.idStyles self.idStyles += style else self.idStyles = [style] end end function addStyleAsClass( style ) if self.classStyles self.classStyles += style else self.classStyles = [style] end end function pickClassStyles( classStyleDict ) result = nil for item in self._parentship if item in classStyleDict if not result result = [] + classStyleDict[item] else result += classStyleDict[item] end end end if result if self.classStyles: result += self.classStyles elif self.classStyles result = self.classStyles end return result end //=================================================== /*# Route ajax requests. @params The parameters generated by the remote ajax request. @return A dictionary that is rendered as JSON and passed back. The default base class implementation does nothing. */ function AJAX( params ) return [=>] end //# Generates a set-property message function msgSetProperty( prop, value ) return [ "message" => "set", "id" => self.getFullID(), "property" => prop, "value"=>value ] end //# Generates a set-property message function msgSetStyle( prop, value ) return [ "message" => "set_style", "id" => self.getFullID(), "property" => prop, "value"=>value ] end //# Generates a invoke method message function msgInvoke( method, data ) return [ "message" => "invoke", "id" => self.getFullID(), "method" => method, "param"=> data ] end //# Generates a generic message function msgGeneric( message, value ) if value == nil: value = [=>] value[ "message" ] = message return value end //# Generates a message to reset the values of all the children function msgSetAllValues( arr ) if not arr: arr = [] if self.isValueHost arr.add( [ "message" => "set", "id" => self.getFullID(), "property" => "value", "value" => self.getValue() ] ); end for child in self.children child.msgSetAllValues( arr ) end return arr end end include/000077500000000000000000000000001176363201700125075ustar00rootroot00000000000000include/CMakeLists.txt000066400000000000000000000014121176363201700152450ustar00rootroot00000000000000#################################################################### # The Falcon Programming language # # Install header files for module development ################################################################### configure_file( falcon/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/falcon/config.h @ONLY ) file( GLOB falcon_files falcon/*.h) file( GLOB falcon_srv falcon/srv/*.h) set(FALCON_HEADERS ${falcon_files} ${CMAKE_CURRENT_BINARY_DIR}/falcon/config.h) set(FALCON_SRV_HEADERS ${falcon_srv} ) install(FILES ${FALCON_HEADERS} DESTINATION ${FALCON_INC_DIR}/falcon) install(FILES ${FALCON_SRV_HEADERS} DESTINATION ${FALCON_INC_DIR}/falcon/srv) set(FALCON_HEADERS ${FALCON_HEADERS} PARENT_SCOPE) set(FALCON_SRV_HEADERS ${FALCON_SRV_HEADERS} PARENT_SCOPE)include/falcon/000077500000000000000000000000001176363201700137515ustar00rootroot00000000000000include/falcon/.gitignore000066400000000000000000000000111176363201700157310ustar00rootroot00000000000000config.h include/falcon/allocator.h000066400000000000000000000044111176363201700161020ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_allocator.h Standard falcon allocator. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar ago 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef flc_ALLOCATOR_H #define flc_ALLOCATOR_H #include namespace Falcon { /** Standard falcon allocator. Bridges STL with falcon memory allocation. */ template class Allocator { public: typedef size_t size_type; typedef ptrdiff_t difference_type; typedef _Tp* pointer; typedef const _Tp* const_pointer; typedef _Tp& reference; typedef const _Tp& const_reference; typedef _Tp value_type; template struct rebind { typedef Allocator<_Tp1> other; }; Allocator() throw() {} //Allocator(const Allocator&) throw() {} template Allocator(const Allocator<_Tp1>&) throw() {} ~Allocator() throw() {} pointer address(reference __x) const { return &__x; } const_pointer address(const_reference __x) const { return &__x; } size_type max_size() const throw() { return size_t(-1) / sizeof(_Tp); } void construct(pointer __p, const _Tp& __val) { new(__p) _Tp(__val); } void destroy(pointer __p) { __p->~_Tp(); } _Tp* allocate( size_type n, const void* = 0 ) { return reinterpret_cast<_Tp*>( memAlloc( n * sizeof( _Tp ) ) ); } void deallocate( _Tp* p, size_type n ) { memFree( p ); } #ifdef _MSC_VER void deallocate( void* p, size_type n ) { memFree( p ); } _Tp* _Charalloc( size_type n ) { return allocate( n ); } #endif }; template bool operator== (const Allocator& one, const Allocator& two) throw() { if ( &one == &two ) return true; return false; } template bool operator!= (const Allocator& one, const Allocator& two) throw() { if ( &one == &two ) return false; } } #endif /* end of flc_allocator.h */ include/falcon/attribmap.h000066400000000000000000000021661176363201700161120ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: attribmap.h Attribute Map - specialized string - vardef map. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 11 Jul 2009 20:42:43 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_ATTRIBMAP_H #define FLC_ATTRIBMAP_H #include #include #include namespace Falcon { class VarDef; class String; class Stream; class Module; /** Specialized attribute map. It's actually just a String -> VarDef specialized map. */ class FALCON_DYN_CLASS AttribMap: public Map { public: AttribMap(); AttribMap( const AttribMap& other ); virtual ~AttribMap(); void insertAttrib( const String& name, VarDef* vd ); VarDef* findAttrib( const String& name ); bool save( const Module* mod, Stream *out ) const; bool load( const Module* mod, Stream *out ); }; } #endif /* end of attribmap.h */ include/falcon/autocstring.h000066400000000000000000000125541176363201700164730ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: autocstring.h SUtility to convert falcon items and strings into C Strings. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab ago 4 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Utility to convert falcon items and strings into C Strings. Header file. */ #ifndef flc_autocstring_H #define flc_autocstring_H #include #include #include namespace Falcon { class Item; class VMachine; /** Automatically converts and allocate temporary memory for C strings. Falcon has a complete API model in which every representation, naming, and in general string operation is performed through Falcon::String. Embedding applications may use that class inside their own code to access the advanced features that the Falcon::String class provides. However, when it is necessary to embed Falcon in previously existing applications, or when it is necessary to interface Falcon with plain C code, it is necessary to turn items and strings into something C functions can understand. Falcon::String has a toCString() member that can fill a C string with an UTF-8 representation of the internal string data (as UTF-8 is manageable by any C function and is thus the standard for internationalized C libraries maintaining char * as their primary string type). Using that interface is a bit tricky, as it requires to provide enough buffer space; C applications may find themselves in checking Falcon::String size and creating temporary buffers all the time. This class provides a very simple mean for applications interfacing to C to store small strings in a temporary stack based buffer, while longer strings are placed in a heap allocated space. The casts and the member c_str() allow to access the buffer or the heap space transparently, and on destruction if a buffer was allocated, it is automatically deleted. The stack space provided is 128 bytes, which is enough for the vast majority of strings an ordinary program has to manage. If the converted string is longer a wide enough char buffer is allocated with memAlloc() and automatically deleted at scope termination. The class provides also a constructor that allows to automatically convert any falcon item to string (with eventually a falcon format to be applied) by providing a VM that will be eventually called to execute "toString()" methods, in case the provided item is an object. Usage is: \code Module *module = .... // a mean to create the module. AutoCString modName( module->name() ); // printf cannot cast to (char*), we'll have to do... printf( "The module name is %s\n", modName.c_str() ); // but strlen and strcpy does, so there's no need for that. char *retval = (char *) malloc( strlen( modName ) ); strcpy( retval, modName ); \endcode If you want to convert item to plain old C strings on the fly: \code Item number = 3.24; // also pass the optional format (right aling, 10 size fixed 4 decimals) AutoCString numrep( vm, number, "r10.4" ); if ( ! numrep.isValid() ) { ... conversion didn't work, i.e. because of errors in the toString() call. } // printf cannot cast to (char*), we'll have to do... printf( "The item is %s\n", numrep.c_str() ); \endcode It is possible also to convert an item without using a VM; in that case convertion of items is performed using the default to string representation. \code Item number = 3.24; AutoCString numrep( number ); // printf cannot cast to (char*), we'll have to do... printf( "The item is %s\n", numrep.c_str() ); \endcode */ class FALCON_DYN_CLASS AutoCString { typedef enum { AutoCString_BUF_SPACE = 128 } e_consts; char *m_pData; uint32 m_len; char m_buffer[ AutoCString_BUF_SPACE ]; void init_vm_and_format( VMachine *vm, const Item &itm, const String &fmt ); public: AutoCString(); AutoCString( const Falcon::String &str ); AutoCString( const Falcon::Item &itm ); AutoCString( Falcon::VMachine *vm, const Falcon::Item &itm ): m_pData( 0 ) { init_vm_and_format( vm, itm, "" ); } AutoCString( Falcon::VMachine *vm, const Falcon::Item &itm, const Falcon::String &fmt ): m_pData( 0 ) { init_vm_and_format( vm, itm, fmt ); } ~AutoCString(); void set( const Falcon::String &str ); void set( const Falcon::Item &itm ); void set( Falcon::VMachine *vm, const Falcon::Item &itm ); void set( Falcon::VMachine *vm, const Falcon::Item &itm, const Falcon::String &fmt ); const char *c_str() const { return m_pData+3; } const char *bom_str(); operator const char *() const { return m_pData+3; } bool isValid() const { return m_pData[3] != (char) 255; } /** Size of the returned buffer. This returns the number of bytes in the returned buffer, not the number of charcaters actually contained in the string. It's the distance between c_str() begin and the terminating 0. */ uint32 length() const { return m_len; } }; } #endif /* end of autocstring.h */ include/falcon/autoucsstring.h000066400000000000000000000046041176363201700170400ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: autoucsstring.h Utility to convert falcon items and strings into UCS-2 Strings. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 12 Sep 2010 12:53:18 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Utility to convert falcon items and strings into UCS Strings. Header file. */ #ifndef FALCON_AUTOUCSSTRING_H #define FALCON_AUTOUCSSTRING_H #include #include #include namespace Falcon { class Item; class VMachine; /** Automatically converts and allocate temporary memory for UCS-2 strings. Falcon has a complete API model in which every representation, naming, and in general string operation is performed through Falcon::String. However, it is often necessary to pass Falcon String to outer world. This class converts automatically falcon strings to UCS-2 strings, that is, strings having character long exactly 2 bytes, with machine-dependent byte ordering (pure number) without any encoding in place. A 2-bytes long position corresponds 1:1 to a UNICODE character in the range 0-0xFFFF. UNICODE Characters above that range are translated into a square (U25A1), but this default can be changed in the constructor. @note This behavior is similar to AutoWString, but AutoWString uses the platform-dependent wchar_t type. Some libraries that may be bound into Falcon modules may use UCS-2 (16-bit) character encoding as an hard-coded choice, hence the need to avoid relying on wchar_t to deal with those. libraries. @see AutoCString @see AutoWString */ class FALCON_DYN_CLASS AutoUCSString { typedef enum { AutoUCSString_BUF_SPACE = 128, DefaultUnknownChar = 0x25A1 } e_consts; uint16 *m_pData; uint32 m_len; uint16 m_buffer[ AutoUCSString_BUF_SPACE ]; public: AutoUCSString(); AutoUCSString( const Falcon::String &str, uint16 defChar = DefaultUnknownChar ); ~AutoUCSString(); void set( const Falcon::String &str, uint16 defChar = DefaultUnknownChar ); const uint16 *ucs_str() const { return m_pData; } uint32 length() const { return m_len; } }; } #endif /* end of autowstring.h */ include/falcon/autowstring.h000066400000000000000000000044031176363201700165110ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: autowstring.h SUtility to convert falcon items and strings into C Strings. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab ago 4 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Utility to convert falcon items and strings into C Strings. Header file. */ #ifndef flc_autowstring_H #define flc_autowstring_H #include #include #include namespace Falcon { class Item; class VMachine; /** Automatically converts and allocate temporary memory for C wide character strings. Works exactly as Falcon::AutoCString, but this is meant to translate Falcon::String into the wchar_t representation of the string. \see Falcon::AutoCString */ class FALCON_DYN_CLASS AutoWString { typedef enum { AutoWString_BUF_SPACE = 128 } e_consts; wchar_t *m_pData; wchar_t m_buffer[AutoWString_BUF_SPACE]; uint32 m_len; void init_vm_and_format( VMachine *vm, const Item &itm, const String &fmt ); public: AutoWString(); AutoWString( const Falcon::String &str ); AutoWString( const Falcon::Item &itm ); AutoWString( Falcon::VMachine *vm, const Falcon::Item &itm ): m_pData( 0 ) { init_vm_and_format( vm, itm, "" ); } AutoWString( Falcon::VMachine *vm, const Falcon::Item &itm, const Falcon::String &fmt ): m_pData( 0 ) { init_vm_and_format( vm, itm, fmt ); } ~AutoWString(); void set( const Falcon::String &str ); void set( const Falcon::Item &itm ); void set( Falcon::VMachine *vm, const Falcon::Item &itm ); void set( Falcon::VMachine *vm, const Falcon::Item &itm, const Falcon::String &fmt ); const wchar_t *w_str() const { return m_pData; } operator const wchar_t *() const { return m_pData; } bool isValid() const { return m_pData[0] != (wchar_t) 0xFFFF; } /** Size of the returned buffer. This returns the number of wide characters that have been transcoded. */ uint32 length() const { return m_len; } }; } #endif /* end of autowstring.h */ include/falcon/base64.h000066400000000000000000000023131176363201700152050ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: base64.h Base64 encoding as per rfc3548 ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 20 Jun 2010 15:47:05 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef _FALCON_BASE64_H_ #define _FALCON_BASE64_H_ #include #include #include namespace Falcon { class FALCON_DYN_CLASS Base64 { public: /** Basic implementation */ static bool encode( byte* data, uint32 dsize, byte* target, uint32& tgsize ); /** Basic implementation \return True if the data could be properly parsed (is a valid Base64 string). */ static bool decode( const String& data, byte* target, uint32& tgsize ); static void encode( byte* data, uint32 dsize, String& target ); static void encode( const String& data, String& target ); static bool decode( const String& data, String& target ); private: static byte getBits( uint32 bits ); }; } #endif /* BASE64_H_ */ /* end of base64 */ include/falcon/basealloc.h000066400000000000000000000014611176363201700160510ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: basealloc.h Base allocation declaration for engine classes ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar dic 5 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Base allocation declaration for engine classes */ #ifndef flc_basealloc_H #define flc_basealloc_H #include #include // for size_t declaration namespace Falcon { class FALCON_DYN_CLASS BaseAlloc { public: void *operator new( size_t size ); void operator delete( void *mem, size_t size ); }; } #endif /* end of basealloc.h */ include/falcon/baton.h000066400000000000000000000064701176363201700152340ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: baton.h Baton synchronization structure. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 14 Mar 2009 00:03:28 +0100 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_BATON_H #define FLC_BATON_H #include #include namespace Falcon { /** Baton concurrency controller class. Like in a 4x4 relay match, a baton is an object which gives the grant to continue operating on a set of objects. It is tightly related with the concept of "monitor", but other than that, it is possible to force blockade of the runner by issuing a block request. It is used by VM users and by the GC, which has inspection rights that overseed VM execution rights (normally). */ class FALCON_DYN_CLASS Baton: public BaseAlloc { void *m_data; public: Baton( bool bBusy = false ); virtual ~Baton(); /** Acquires the baton. The caller blocks until it is able to acquire the baton. If the baton is blocked, it can be acquired only by the blocking thread. \note Succesful acquisition of the blocking thread causes the baton to be automatically unblocked. */ virtual void acquire(); /** Tries to acquire the baton. If the baton is currently available (unacquired) it is acquired, unless blocked. If it is blocked, it can be acquired only by the blocker thread. \note Succesful acquisition of the blocking thread causes the baton to be automatically unblocked. \return true if the baton is acquired. */ bool tryAcquire(); /** Releases the baton. This makes the baton available for another acquirer. */ virtual void release(); /** Checks if the baton is blocked, honouring pending block requests. If the baton is blocked, it is atomically released and the calling thread puts itself in wait for re-acquisition. */ void checkBlock(); /** Blocks the baton. If the call is succesful, this prevents any acquire request coming from other threads to be accepted. Only one thread can block the baton; the call will fail if there is an already pending block request issued by another thread. It will succed (with no effect) if the caller thread is the one already blocking the baton. \return true on success. */ bool block(); /** Unblocks the baton. If a succesful blocking thread decides it doesn't want to acquire the baton anymore, it can cast an unblock() to allow other acquiring threads to progress. \note A succesful acquisition releases atomically the block request. \return true if the thread was owning the block onthe baton. */ bool unblock(); /** Debug routine. True if currently busy. */ bool busy(); /** Function called when the baton is released while there is a pending blocking request. The base class version does nothing. */ virtual void onBlockedAcquire(); }; } #endif /* end of baton.h */ include/falcon/cacheobject.h000066400000000000000000000051131176363201700163540ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: cacheobject.h Falcon Object - Standard instance of classes in script ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 24 Jan 2009 13:48:11 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Falcon Object - Standard instance of classes in script */ #ifndef FLC_CACHE_OBJECT_H #define FLC_CACHE_OBJECT_H #include #include #include namespace Falcon { class VMachine; class FALCON_DYN_CLASS CacheObject: public CoreObject { protected: mutable Item *m_cache; CacheObject( const CoreClass* generator, bool bSeralizing = false ); CacheObject( const CacheObject &other ); public: virtual ~CacheObject(); virtual bool serialize( Stream *stream, bool bLive ) const; virtual bool deserialize( Stream *stream, bool bLive ); virtual bool setProperty( const String &prop, const Item &value ); virtual bool getProperty( const String &key, Item &ret ) const; /** Mark the items in the cache and the inner data, if it's garbageable. If this object contains an external FalconData instance, it gets marked; anyhow, this function marks the cache items. */ virtual void gcMark( uint32 mark ); Item *cachedProperty( const String &name ) const { register uint32 pos; if ( ! m_generatedBy->properties().findKey( name, pos ) ) return 0; return m_cache + pos; } Item *cachedPropertyAt( uint32 pos ) const { if ( pos > m_generatedBy->properties().added() ) return 0; return m_cache + pos; } /** Reflect an external data into this object. This method uses the reflection informations in the generator class property table to load the data stored in the user_data parameter into the local item vector cache. As this is an optional operation, the base class implementation does nothing. */ virtual void reflectFrom( void *user_data ); /** Reflect this object into external data. This method uses the reflection informations in the generator class property table to store a C/C++ copy of the data stored in the local cache of this object into an external data. As this is an optional operation, the base class implementation does nothing. */ virtual void reflectTo( void *user_data ) const; }; } #endif /* end of cacheobject.h */ include/falcon/callpoint.h000066400000000000000000000021221176363201700161040ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: corefunc.h Abstract class for immediately callable items at language levels. They are functions and arrays. Classes, methods and class methods are secondary callable items, which relay on this primary callable items (arrays and functions). ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 28 Jul 2009 00:32:58 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_CALLPOINT_H_ #define FALCON_CALLPOINT_H_ #include #include #include namespace Falcon { class FALCON_DYN_CLASS CallPoint: public Garbageable { public: virtual ~CallPoint() {} virtual const String& name() const = 0; virtual void readyFrame( VMachine* vm, uint32 paramCount ) = 0; virtual bool isFunc() const = 0; }; } #endif /* CALLPOINT_H_ */ /* end of callpoint.h */ include/falcon/carray.h000066400000000000000000000151631176363201700154110ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: carray.h Core array ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab dic 4 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Core array. */ #ifndef flc_flc_carray_H #define flc_flc_carray_H #include #include #include #include #include #include #include #define flc_ARRAY_GROWTH 128 namespace Falcon { class Item; class Bindings; class LinearDict; /** Core array (or array of items). */ class FALCON_DYN_CLASS CoreArray: public DeepItem, public CallPoint { ItemArray m_itemarray; CoreDict *m_bindings; CoreObject *m_table; /** Position in a table. * * This indicator is also used to determine if an array can be a method. * * Table arrays can never be methods (as they refer to the table they are * stored in), so when m_table != 0, arrays are never methodic. * * When m_table == 0, m_tablePos != 0 indicates a non-methodic array. */ uint32 m_tablePos; CoreArray( Item *buffer, uint32 size, uint32 alloc ); public: /** Creates the core array. */ CoreArray(); CoreArray( const CoreArray& other ); CoreArray( uint32 prealloc ); ~CoreArray(); virtual void gcMark( uint32 gen ); const ItemArray& items() const { return m_itemarray; } ItemArray& items() { return m_itemarray; } void append( const Item &ndata ) { if ( m_table != 0 ) return; m_itemarray.append( ndata ); } void prepend( const Item &ndata ) { if ( m_table != 0 ) return; m_itemarray.prepend( ndata ); } void merge( const CoreArray &other ) { if ( m_table != 0 ) return; m_itemarray.merge( other.m_itemarray ); } void merge_front( const CoreArray &other ) { if ( m_table != 0 ) return; m_itemarray.merge( other.m_itemarray ); } bool insert( const Item &ndata, int32 pos ) { if ( m_table != 0 ) return false; return m_itemarray.insert( ndata, pos ); } bool insert( const CoreArray &other, int32 pos ) { if ( m_table != 0 ) return false; return m_itemarray.insert( other.m_itemarray, pos ); } bool remove( int32 pos ) { if ( m_table != 0 ) return false; return m_itemarray.remove( pos ); } bool remove( int32 first, int32 last ) { if ( m_table != 0 ) return false; return m_itemarray.remove( first, last ); } bool change( const CoreArray &other, int32 begin, int32 end ) { if ( m_table != 0 ) return false; return m_itemarray.change( other.m_itemarray, begin, end ); } int32 find( const Item &itm ) const { return m_itemarray.find( itm ); } bool insertSpace( uint32 pos, uint32 size ) { if ( m_table != 0 ) return false; return m_itemarray.insertSpace( pos, size ); } void resize( uint32 size ) { if ( m_table != 0 ) return; m_itemarray.resize( size ); } void reserve( uint32 size ) { m_itemarray.reserve( size ); } CoreArray *partition( int32 start, int32 end ) const; CoreArray *clone() const; uint32 length() const { return m_itemarray.length(); } void length( uint32 size ) { return m_itemarray.length( size ); } /** Create the bindings for this array, or get those already created. */ CoreDict *makeBindings(); CoreDict *bindings() const { return m_bindings; } void setBindings( CoreDict *binds ) { m_bindings = binds; } /** Gets a proprty of this vector. Properties are either bindings or table properties. Bindings override table-wide properties. If the given property is not found, 0 is returned. \param name The property to be found. \return the property item if found or zero. */ Item* getProperty( const String &name ); /** Set a property in this array. If there is a biniding with the given property name, that item is updated. If not, If there is a table with a column name, the coresponding item in the array is updated. If not, new bindings are created, and the property is stored as a new binding. \param name The property to be updated. \param data The update data. */ void setProperty( const String &name, const Item &data ); /** Checks the position to be in the array, and eventually changes it if it's negative. \param pos the position to be checked and eventually turned into a positive value. \return false if pos is outside the array size */ bool checkPosBound( int32 &pos ) { register int s = length(); if ( pos < 0 ) pos = s + pos; if ( pos < 0 || pos >= s ) return false; return true; } const Item &at( int32 pos ) const { return m_itemarray.at( pos ); } Item &at( int32 pos ) { return m_itemarray.at( pos ); } Item &operator[]( int32 pos ) throw() { return m_itemarray[pos]; } const Item &operator[]( int32 pos ) const throw() { return m_itemarray[pos]; } CoreObject *table() const { return m_table; } void table( CoreObject *t ) { m_table = t; } uint32 tablePos() const { return m_tablePos; } void tablePos( uint32 tp ) { m_tablePos = tp; } virtual bool isFunc() const { return false; } virtual void readyFrame( VMachine* vm, uint32 paramCount ); virtual const String& name() const; virtual void readProperty( const String &prop, Item &item ); virtual void writeProperty( const String &prop, const Item &item ); virtual void readIndex( const Item &pos, Item &target ); virtual void writeIndex( const Item &pos, const Item &target ); /** Determines if this array can be seen as a method in a class. * * If the array is part of a table, it can never be a method, and * this setting is ignored. * */ void canBeMethod( bool b ) { if ( m_table == 0 ) m_tablePos = b ? 0 : (uint32) -1; } /** Returns true if this array should be considered a method when callable and stored in a property. * */ bool canBeMethod() const { return m_table == 0 && m_tablePos == 0; } /** Compare two arrays for deep equality. Internally calls item array's compare. */ int compare( const CoreArray& other ) { return m_itemarray.compare( other.m_itemarray ); } }; } #endif /* end of flc_carray.h */ include/falcon/cclass.h000066400000000000000000000127541176363201700154030ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: cclass.h Core Class definition ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu Jan 20 2005 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Core Class definition */ #ifndef FLC_CCLASS_H #define FLC_CCLASS_H #include #include #include #include #include namespace Falcon { class VMachine; class ItemDict; /** Representation of classes held in VM while executing code. The Virtual Machine has his own image of the classes. Classes items are meant to i.e. access classes wide variables or classes methods. Class Items are also used to create objects in a faster way. They maintain a copy of an empty object, that is just duplicated as-is via memcopy, making the creation of a new object a quite fast operation. They also store a list of attributes that must be given at object after their creation. As the classes doesn't really has attributes, they do not participate in attribute loops and list; they just have to remember which attributes must be given to objects being constructed out of them. */ class FALCON_DYN_CLASS CoreClass: public Garbageable { private: LiveModule *m_lmod; const Symbol *m_sym; Item m_constructor; PropertyTable *m_properties; /** Locally cached factory. */ ObjectFactory m_factory; /** Dictionary of dictionaries of states. */ ItemDict* m_states; /** Shortcut for the init state to speed up instance creation. */ ItemDict* m_initState; bool m_bHasInitEnter; public: /** Creates an item representation of a live class. The representation of the class needs a bit of extra informations that are provided by the virtual machine, other than the symbol that generated this item. The module id is useful as this object often refers to its module in the VM. Having the ID recorded here prevents the need to search for the live ID in the VM at critical times. */ CoreClass( const Symbol *sym, LiveModule *lmod, PropertyTable *pt ); ~CoreClass(); LiveModule *liveModule() const { return m_lmod; } const Symbol *symbol() const { return m_sym; } PropertyTable &properties() { return *m_properties; } const PropertyTable &properties() const { return *m_properties; } ObjectFactory factory() const { return m_factory; } void factory( ObjectFactory f ) { m_factory = f; } /** Creates an instance of this class. The returned object and all its properties are stored in the same memory pool from which this instance is created. On a multithreading application, this method can only be called from inside the thread that is running the VM. In some cases (e.g. de-serialization) the caller may wish not to have the object initialized and filled with attributes. The optional appedAtribs parameter may be passed false to have the caller to fill the instance with startup data. \note This function never calls the constructor of the object. \param user_data pre-allocated user data. \param bDeserialize set to true if you are deserializing an instance (that is, if its constructor should not configure it). \return The new instance of the Core Object. */ CoreObject *createInstance( void *user_data=0, bool bDeserialize = false ) const; const Item &constructor() const { return m_constructor; } Item &constructor() { return m_constructor; } /** Returns true if the class is derived from a class with the given name. This function scans the property table of the class (template properties) for an item with the given name, and if that item exists and it's a class item, then this method returns true. \param className the name of a possibly parent class \return true if the class is derived from a class having the given name */ bool derivedFrom( const String &className ) const; /** Returns true if the class is derived from a given symbol. This method checks if this class is compatible with the given symbol, or in other words, if the given symbol is present somewhere in the class hierarcy. True is returned also if this class is exactly created from the given symbol. \param sym The symbol that has to be checked for parentship. \return true if the class is derived from a class having the given name */ bool derivedFrom( const Symbol* sym ) const; /** Marks the class and its inner data. This marks the class, the livemodule it is bound to, the property table data and the ancestors. */ void gcMark( uint32 mark ); /** Sets a state dictionary for this class. States are usually string -> CoreFunc dictionaries; so, an ItemDict like this will contain a set of String -> CoreDict( string -> CoreFunc ); This is mainly used by VMachine::link. \note If a previous state dictionary was set, it will be destroyed. \param sd State dictionary \param is Dictionary for the init state, if existing. */ void states( ItemDict* sd, ItemDict* is = 0 ); ItemDict* states() const { return m_states; } ItemDict* initState() const { return m_initState; } bool hasInitEnter() const { return m_bHasInitEnter; } }; } #endif /* end of flc_cclass.h */ include/falcon/citerator.h000066400000000000000000000036641176363201700161270ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: citerator.h Base abstract class for generic collection iterators. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom giu 24 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Base abstract class for generic collection iterators. */ #ifndef flc_citerator_H #define flc_citerator_H #include #include #include namespace Falcon { class Sequence; class Garbageable; class Item; /** Base abstract class for generic collection iterators. This is also used as internal object for iterators. */ class FALCON_DYN_CLASS CoreIterator: public FalconData { protected: CoreIterator(); CoreIterator( const CoreIterator& other ); Garbageable* m_creator; Sequence* m_creatorSeq; public: virtual ~CoreIterator(); virtual bool next() = 0; virtual bool prev() = 0; virtual bool hasNext() const = 0; virtual bool hasPrev() const = 0; /** Must be called after an isValid() check */ virtual Item &getCurrent() const = 0; virtual bool isValid() const = 0; virtual bool isOwner( void *collection ) const = 0; virtual bool equal( const CoreIterator &other ) const = 0; virtual bool erase() = 0; virtual bool insert( const Item &item ) = 0; virtual void invalidate() = 0; /** On all the non-temporary iterators use this!!! Creates a local copy of the VM item to which this iterator refers to. The owner is marked on GC mark, so it stays alive as long as at least one iterator points to it. */ virtual void setOwner( Garbageable *owner ); virtual void setOwner( Sequence *owner ); virtual void gcMark( uint32 mark ); }; } #endif /* end of citerator.h */ include/falcon/common.h000066400000000000000000000113111176363201700154070ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_common.h Definition for falcon common library. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom giu 20 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef flc_COMMON_H #define flc_COMMON_H #include namespace Falcon { class String; #define flc_CURRENT_VER 1 #define flc_CURRENT_SUB 0 #define flcC_SYM_VAR 0 #define flcC_SYM_FUNC 1 #define flcC_SYM_EXT 2 #define flcC_SYM_CLASS 3 #define flcC_SYM_VARPROP 4 #define flcC_EXPORT_BIT 0x80 #define flcC_VAL_NIL 0 #define flcC_VAL_INT 1 #define flcC_VAL_NUM 2 #define flcC_VAL_STRID 3 #define flcC_VAL_SIMID 4 /** Seed for the hash key checksum generator */ #define flc_HASH_SEED 0xC2AF3DE4 /** Utility class to char pointers. This class is just an operator that compares the strings pointed by its parameters. It is used in some maps that have a string pointer as they key, as it points a string being the name of a symbol or other kind of string whose instance must be kept somewhere else. */ class CharPtrCmp { public: bool operator() ( const char *s1, const char *s2 ) const { while( *s1 && *s2 && *s1 == *s2 ) { s1 ++; s2 ++; } return (*s1 < *s2); } }; #define flc_ASM_GLOBAL 0 #define flc_ASM_LOCAL 1 #define flc_ASM_PARAM 2 #if FALCON_LITTLE_ENDIAN == 1 inline uint64 grabInt64( void* data ) { return *(uint64*)data; } inline int64 loadInt64( void* data ) { return *(int64*)data; } inline numeric grabNum( void* data ) { return *(numeric*)data; } inline numeric loadNum( void* data ) { return *(numeric*)data; } inline uint64 endianInt64( const uint64 param ) { return param; } inline uint32 endianInt32( const uint32 param ) { return param; } inline uint16 endianInt16( const uint16 param ) { return param; } inline numeric endianNum( const numeric param ) { return param; } #else inline uint64 endianInt64( const uint64 param ) { byte *chars = (byte *) ¶m; return ((uint64)chars[7]) << 56 | ((uint64)chars[6]) << 48 | ((uint64)chars[5]) << 40 | ((uint64)chars[4]) << 32 | ((uint64)chars[3]) << 24 | ((uint64)chars[2]) << 16 | ((uint64)chars[1]) << 8 | ((uint64)chars[0]); } inline uint64 grabInt64( void* data ) { byte *chars = (byte *) data; return ((uint64)chars[7]) << 56 | ((uint64)chars[6]) << 48 | ((uint64)chars[5]) << 40 | ((uint64)chars[4]) << 32 | ((uint64)chars[3]) << 24 | ((uint64)chars[2]) << 16 | ((uint64)chars[1]) << 8 | ((uint64)chars[0]); } inline numeric grabNum( void* numMemory ) { const byte* data = (const byte*) numMemory; union t_unumeric { byte buffer[ sizeof(numeric) ]; numeric number; } unumeric; uint32 i; for ( i = 0; i < sizeof( numeric ); i++ ) { unumeric.buffer[i] = data[(sizeof( numeric )-1) - i]; } return unumeric.number; } inline numeric endianNum( const numeric ¶m ) { return grabNum( (void*) ¶m ); } inline numeric loadNum( void* data ) { byte* bdata = (byte*) data; union t_unumeric { struct t_integer { uint32 high; uint32 low; } integer; numeric number; } unumeric; unumeric.integer.high = *reinterpret_cast(bdata); unumeric.integer.low = *reinterpret_cast(bdata+sizeof(uint32)); return unumeric.number; } inline int64 loadInt64( void* data ) { byte* bdata = (byte*) data; uint64 res = *reinterpret_cast(bdata); res <<= 32; res |= *reinterpret_cast(bdata+sizeof(uint32)); return (int64) res; } inline uint32 endianInt32( const uint32 param ) { byte *chars = (byte *) ¶m; return ((uint32)chars[3]) << 24 | ((uint32)chars[2]) << 16 | ((uint32)chars[1]) << 8 | ((uint32)chars[0]); } inline uint16 endianInt16( const uint16 param ) { byte *chars = (byte *) ¶m; return ((uint32)chars[1]) << 8 | ((uint32)chars[0]); } #endif /* FALCON_LITTLE_ENDIAN */ inline int charToHex( const char elem ) { if( elem >= '0' && elem <= '9' ) return elem - '0'; else if( elem >= 'A' && elem <= 'F' ) return elem - 'A'; else if( elem >= 'a' && elem <= 'f' ) return elem - 'a'; return -1; } FALCON_DYN_SYM uint32 calcMemHash( const char *memory, uint32 size ); FALCON_DYN_SYM uint32 calcCstrHash( const char *cstring ); FALCON_DYN_SYM uint32 calcStringHash( const String &string ); inline uint32 calcIntHash( const int32 number ) { return flc_HASH_SEED * number; } } #endif /* end of flc_common.h */ include/falcon/compiler.h000066400000000000000000000575711176363201700157530ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: compiler.h Main Falcon source compiler. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom giu 6 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_COMPILER_H #define FALCON_COMPILER_H #include #include #include #include #include #include #include extern "C" int flc_src_parse( void *param ); namespace Falcon { class SrcLexer; class Stream; class InteractiveCompiler; class VMachine; class ModuleLoader; /** ( const String *, Symbol * ) */ class FALCON_DYN_CLASS AliasMap: public Map { public: AliasMap(); }; class FALCON_DYN_CLASS DeclarationContext: public BaseAlloc { byte m_value; public: DeclarationContext(){ m_value = 0; } DeclarationContext( const DeclarationContext &dc ) { m_value = dc.m_value; } DeclarationContext &operator =( const DeclarationContext &other ) { m_value = other.m_value; return *this; } DeclarationContext &setGlobalQuery() { m_value = 0; return *this; } DeclarationContext &setQuery() { m_value &= ~0x1; return *this; } DeclarationContext &setAssign() { m_value |= 0x1; return *this; } DeclarationContext &setGlobalBody() { m_value &= ~(0x2|0x4|0x8); return *this; } DeclarationContext &setFunctionBody() { m_value |= 0x2; m_value &= ~(0x4); return *this; } DeclarationContext &setClassBody() { m_value |= 0x4; m_value &= ~(0x2); return *this; } DeclarationContext &setStatic() { m_value |= 0x8; return *this; } DeclarationContext &setNonStatic() { m_value &= ~0x8; return *this; } }; /** FALCON source compiler. This class is responsible for creating a syntactic tree given a linear input stream. The stream may be from file, standard input or from a memory buffer ( the compiler does not need the ability of random file access, so even a network stream may be used ). */ class FALCON_DYN_CLASS Compiler: public BaseAlloc { protected: /** Declaration context. Depending on where a variable is initialized, or how a symbol is declared, the final symbol added to the module may be different. This enumeration is used to keep track of the current declaration context. */ typedef enum { e_dc_global, e_dc_param, e_dc_static_func, e_dc_func_body, e_dc_static_class, e_dc_class_body } t_decl_context; /** Map of constants. (String &, Value *) */ Map m_constants; /** Map of namespaces. (String &, void) */ Map m_namespaces; SourceTree *m_root; int m_errors; int m_optLevel; SrcLexer *m_lexer; Stream *m_stream; /** This is the module that is being formed in the meanwhile. */ Module *m_module; int64 m_enumId; /** Leading instruction that owns currently parsed statements */ List m_context; /** Context limited to leading function instructions. */ List m_func_ctx; /** Area to save currently parsed statements. */ List m_contextSet; /** Leading instruction, specialized for loops. */ List m_loops; /** Last statement's symbols. The type of symbols cannot be determined by the context while they are built; they get defined as the compiler understands the surrounding context. However, it is an error to reference undefined symbols in local context (i.e. lambdas, functions etc.). This list has a reference to each symbol that has been built during the last statement parsing. At the end of the statement, the list is scanned for undefined symbol, and an error is risen in case any is found. list of Value * */ List m_statementVals; /** Stack of currently active functions. Can be nested in case of i.e. lambas. (FuncDef *) */ List m_functions; /** Aliased symbols are stored here. List of alias maps. */ List m_alias; /** The static prefix is the name of the symbol currently declaring the static namespace. Do not delete: we're not owners. */ const String *m_staticPrefix; int m_lambdaCount; int m_closureContexts; int m_tempLine; /** Directive strict. */ bool m_strict; /** Directive language. */ String m_language; /** Directive version. */ int64 m_modVersion; bool m_defContext; bool m_bParsingFtd; bool m_bInteractive; Error *m_rootError; InteractiveCompiler *m_metacomp; VMachine *m_serviceVM; ModuleLoader *m_serviceLoader; /** Search path inherited from upper facilities. */ String m_searchPath; /** Removes all the structures and temporary data used to compile a file. This function is called automatically by the various compile() and destructors. */ void clear(); /** Initializes structures and variables used for compilation. This function is called automatically by the various compile() and destructors. */ void init(); /** Add predefined symbols and constants. This method prepares the compiler so that it has basic constant symbols set and in place; embeding apps may wish to provide different base symbols. */ void addPredefs(); public: /** Creates an empty compiler. This constructor doesn't set a stream and a module for the compiler. It is intended for repeated usage through compile( Module *, Stream *). */ Compiler(); /** Creates the compiler setting a default module and input stream. This configures this instance as a single-file-compilation only compiler. After the compile() call, the instance may (should) be disposed. However, after calling this constructor it is possible to use the compiler( Module *, Stram *) as well. */ Compiler( Module *mod, Stream *input ); /** Destroys the compiler. Internally calls clear() */ virtual ~Compiler(); /** Reset compiler settings to defaults and prepares for a new compilation. Precisely, this function: # destroys tree and function information from previous run, if they exist. # clears the constants and fills them with the Falcon language default constants # clears the ftd compilation flag. This function should be called before a repeated compilation; then the caller is free to add specific application constants and setting, and finally call the compile( Module *, Stream *) method. Directives are automatically cleared at the end of a compilation, and they keep the value they had before. This allows to set directives from outside and have scripts locally modify their directives. */ void reset(); /** Compiles the module given in the constructor. This method is to be used for one-time only compilation (build the compiler, compile, destroy the compiler), when the Compiler( Module *, Stream *) constructor version has been used. Otherwise, it will raise an error and exit. */ bool compile(); /** Compile a module from a stream. This version of the function is suitable to be used multiple times for the same compiler. The caller should call resetDefaults(), give the compiler the wished setings and then call compiler. \param mod a newly allocated and empty module that will be filled by the compilation \param input the stream from which to read the source \return false on compilation failed. */ bool compile( Module *mod, Stream *input ); /** Front-end to raiseError( *e ). The compiler doesn't throw the error list until the compilation is over. error raisal is delayed until the end of the compilation step. */ void raiseError( int errorNum, int errorLine=0); /** Raises an error. The compiler doesn't throw the error list until the compilation is over. error raisal is delayed until the end of the compilation step. */ void raiseError( Error *e ); /** Raises an error related to a context problem. The error reports the line where the problem has been detected, and the line that begun current faulty context. \param code the error code. \param line the line where the error is detected \param startLine initial line of the context. */ void raiseContextError( int code, int line, int startLine ); void raiseError( int errorNum, const String &errorp, int errorLine=0); void addError() { m_errors++; } /** Searches a symbol in the local context. * * If the current context is the global context, then it just calls * searchGlobalSymbol. If there is a local context, the symbol is serched * in the current context; in case it's not found, this method returns * if bRecurse is false. * * If bRecurse is true, the symbol is searched down in the parent contexts * until found or until there is no more local context. In that case, the * method returns 0; so if there is the need to bind a local symbol or a global * one if local symbols are not found, this must be done by the caller * calling searchGlobalSymbol when this method returns 0. * * \param symname The symbol name as a pointer to a module string. * \param bRecurse true to bind symbols from any parent, false to search in the local context. * \return The symbol if found or 0 otherwise. */ Symbol *searchLocalSymbol( const String &symname, bool bRecurse = false ); Symbol *searchGlobalSymbol( const String &symname ); Symbol *addLocalSymbol( const String &symname, bool parameter ); Symbol *addGlobalSymbol( const String &symname ); Symbol *searchOuterSymbol( const String &symname ); /** Creates a symbol that will be an initially defined global variable. The global variables may be created with an initial values (i.e. for static declarations). This function adds the global symbol for the variable and sets it to the default value. */ Symbol *addGlobalVar( const String &symname, VarDef *value ); bool isLocalContext() { return ! m_functions.empty(); } /** Seek a constant in the predefined constant list. If the constant is found, the function returns the value associated with the given constant. Constant values are owned by the compiler (yet the constant strings are still held in the module), and are destroyed at compiler destruction. \note just a placeholder for now \param name the constant to be searched \return the value of the constant or 0 if the constant doesn't exists. */ const Value *getConstant( const String &name ) { Value **findp = (Value **) m_constants.find( &name ); if ( findp != 0 ) return *findp; return 0; } /** Adds a direct load request to the module being compiled. \param name The name of the module to be loaded \param isFilename True if the name to be loaded is actually a filename path. */ virtual void addLoad( const String &name, bool isFilename ) { m_module->addDepend( name, false, isFilename ); } // Inlines void addStatement( Statement *stm ) { if ( stm != 0 ) getContextSet()->push_back( stm ); } void addFunction( Statement *stm ) { if ( stm != 0 ) m_root->functions().push_back( stm ); } void addClass( Statement *stm ) { if ( stm != 0 ) m_root->classes().push_back( stm ); } void pushLoop( Statement *stm ) { m_loops.pushBack( stm ); } void pushFunction( FuncDef *f ); void pushContext( Statement *stm ) { m_context.pushBack( stm ); } void pushContextSet( StatementList *list ) { m_contextSet.pushBack( list ); } Statement *getContext() const { if ( m_context.empty() ) return 0; return (Statement *) m_context.back(); } Statement *getLoop() const { if ( m_loops.empty() ) return 0; return (Statement *) m_loops.back(); } StatementList *getContextSet() const { if ( m_contextSet.empty() ) return 0; return (StatementList *)m_contextSet.back(); } FuncDef * getFunction() const { if ( m_functions.empty() ) return 0; return (FuncDef *) m_functions.back(); } void popLoop() { m_loops.popBack(); } void popContext() { m_context.popBack(); } void popContextSet() { m_contextSet.popBack(); } void popFunction(); void pushFunctionContext( StmtFunction *func ) { m_func_ctx.pushBack( func ); } void popFunctionContext() { if ( !m_func_ctx.empty() ) m_func_ctx.popBack(); } StmtFunction *getFunctionContext() const { if ( m_func_ctx.empty() ) return 0; return (StmtFunction*) m_func_ctx.back(); } String *addString( const String &str ) { return m_module->addString( str ); } SrcLexer *lexer() const { return m_lexer; } Stream *stream() const { return m_stream; } int lambdaCount() const { return m_lambdaCount; } void incLambdaCount() { m_lambdaCount++; } void addNilConstant( const String &name, uint32 line=0 ); void addIntConstant( const String &name, int64 value, uint32 line=0 ); void addNumConstant( const String &name, numeric value, uint32 line=0 ); void addStringConstant( const String &name, const String &value, uint32 line=0 ); void addConstant( const String &name, Value *val, uint32 line=0 ); /** Adds an attribute to the currently active context-sensible symbol */ void addAttribute( const String &name, Value *val, uint32 line=0 ); SourceTree *sourceTree() const { return m_root; } Module *module() const { return m_module; } int errors() const { return m_errors; } /** Process an include instruction. In Falcon, \b include is a compile time instruction more than a directive. Falcon does not support pre-processing or directives by design. The \b include statement takes as argument a single immediate string or an expression that can be statically evaluated into a string (i.e. consts or string sums), and includes it by changing the internal file stream. This causes an as-is inclusion at lexer level. */ //void include( const char *filename ); //void includePath( const Hstring &incp ); /** Instruct the compiler that this value is used as a definition. This is used to turn local undefined into local variables, or global undefinded into globals. Variables may rightfully become something else later on (i.e. functions) however. @param val The value to be inspected in search for defined symbols. */ void defineVal( Value *val ); /** Define all the values int the given array definition. As the array definition is to the left of an assignment, all the atomic symbols that are found in the array definition are to be defined. Of course, non atomic symbols (as functions, other array defintions and so on) are NOT to be defined. @param val The array of left-assigment values, possibly symbols */ void defineVal( ArrayDecl *val ); Symbol *globalize( const String &str ); /** Checks if the current statemsnt has referenced a locally undefined symbol. */ bool checkLocalUndefined(); void addSymdef( Value *val ) { List *l = (List *) m_statementVals.back(); l->pushBack( val ); } /** Return the current static prefix, if any. Zero shall be returned if the current symbol is not currently using the static prefix. */ const String *staticPrefix() const { return m_staticPrefix; } void staticPrefix( const String *v ) { m_staticPrefix = v; } /** Builds the constructor function for a given class. This is an utility that creates a ._init suffixed function statement and symbol; the symbol is added as the constructor for the class stored in the parameter, while the StmtFunction object is inserted in the functions syntax tree and returned. \param sym a symbol containing a ClassDef for which a constructor function must be built. \return A newly created StmtFunction object to hold the source tree for the constructor. */ StmtFunction *buildCtorFor( StmtClass *sym ); /** Cleanup for function closing. */ void closeFunction(); /** Store current line for later error signaling. */ void tempLine( int line ) { m_tempLine = line; } int tempLine() const { return m_tempLine; } /** Activate "strict" feature. When turned on, the compiler will raise an undefined symbol when assigning this values outside a "def" statement. */ void strictMode( bool breq ) { m_strict = breq; } bool strictMode() const { return m_strict; } /** Are we parsing a normal file or an escaped template file? */ bool parsingFtd() const; void parsingFtd( bool b ); /** Set directive as string value. In case the directive doesn't exist or doesnt accept the given value as valid, an error may be raised. Applications setting directives externally may give bRaise false to prevent error raising and manage internally directive set failure. \param directive the name of the directive to be set. \param value the value that the given directive should be given. \param bRaise true in case of invalid directive or value, also raise an error \return true on success, false on failure */ bool setDirective( const String &directive, const String &value, bool bRaise = true ); /** Set directive as string value. In case the directive doesn't exist or doesnt accept the given value as valid, an error may be raised. Applications setting directives externally may give bRaise false to prevent error raising and manage internally directive set failure. \param directive the name of the directive to be set. \param value the value that the given directive should be given. \param bRaise true in case of invalid directive or value, also raise an error \return true on success, false on failure */ bool setDirective( const String &directive, int64 value, bool bRaise = true ); void defContext( bool ctx ) { m_defContext = ctx; } bool defContext() const { return m_defContext; } /** Closes the currently worked on closure */ Value *closeClosure(); void incClosureContext() { m_closureContexts++; } void decClosureContext() { m_closureContexts--; } /** Add an enumeration item to current enumeration. */ void addEnumerator( const String &name, Value *value ); /** Add an enumeration item to current enumeration. This version assigns the enumerated value a progressive integer. */ void addEnumerator( const String &name ); void resetEnum() { m_enumId = 0; } /** Return true if the current symbol is actually a namespace. Checks if sym was previosly declared as a namespace with addNamspace(). \note Namespaces can contain dots. \param symName the name that may be possibly a namespace. */ bool isNamespace( const String &symName ); /** Adds a known namespace. \param nspace The namespace to be added (as module name). \param alias If not empty, will be the alias under which the module will be locally known. \param full If true, import all symbols. \param filename If true, the load request was for a direct filename. */ virtual void addNamespace( const String &nspace, const String &alias, bool full=false, bool filename=false ); /** Import the symbols named in a List. The \b lst parameter contains a list of String* which will be imported. If a prefix is given, then the prefix is added to the list of known namespaces, and it is added in front of the symbol to be imported. This will force the VM to search the symbol only in the specified namespace (that is, in the module named after the prefix). The function is meant to be used in the conjunction with the parser, and will destroy both the string in \b lst and \b lst itself. \param lst The list of symbols (disposeable strings) to be loaded \param prefix The module name or path \param alias The namespace. If not given, will be the name of the module. \param filename if true the module load request is for a direct file name */ virtual void importSymbols( List *lst, const String &prefix=String(), const String &alias=String(), bool filename=false ); /** Import a single aliased symbol from a module. This tries to resolve the given symbol in the given symName in the target from module during link time, and assings the local alias to it \param symName The symbol name to be aliased. \param fromMod The module name or path. \param alias The namespace. If not given, will be the name of the module. \param filename if true the module load request is for a direct file name. \param return The newly added symbol. */ virtual Symbol* importAlias( const String &symName, const String &fromMod, const String &alias, bool filename=false ); /** Performs a meta compilation. If any data is written on the metacompiler output stream, the stream is immediately sent to the lexer for further compilation. */ void metaCompile( const String &data, int startline ); /** Gets the service VM for this compiler. Used to create the associate meta-compiler. The VM is owned by this compiler (or of its meta-compiler). */ VMachine *serviceVM() const { return m_serviceVM; } /** Gets the service VM for this compiler. Used to create the associate meta-compiler. The loader is owned by this compiler (or of its meta-compiler). */ ModuleLoader *serviceLoader() const { return m_serviceLoader; } /** Sets the service VM for this compiler. Used to create the associate meta-compiler. The VM is owned by this compiler (or of its meta-compiler). If a metacompilation is required, the ownership of this VM is transferred to the meta compiler. If a service VM is not set and a meta-compilation is requried, the compiler creates a VM on the fly. */ void serviceVM( VMachine *vm ) { m_serviceVM = vm; } /** Sets the service VM for this compiler. Used to create the associate meta-compiler. The loader is owned by this compiler (or of its meta-compiler). If a metacompilation is required, the ownership of this loader is transferred to the meta compiler. If a service loader is not set and a meta-compilation is requried, the compiler creates a loader on the fly. */ void serviceLoader(ModuleLoader *l) { m_serviceLoader = l; } /** Passes the ownership of the error structure to the caller. This allows the caller to get the errors received during the processing of the last compilation and handle them separately. The ownership is passed to the caller, or in other world, the list of errors in this compiler is zeroed and the reference count of the returned error list is not changed. \return 0 if the compiler was not in error state, or a list of one or more errors if it raised some errors during the processing of the files. */ Error *detachErrors() { Error *e = m_rootError; m_rootError = 0; m_errors = 0; return e; } /** Sets this compiler as interactive. A compiler meant to run code from the command line has different rules; for example, it can accept autoexpressions without raising a "statement does nothing" error, as the meaning of expressions on the command line is just that to be evaluated and their result being interactively displayed. */ void setInteractive( bool bint ) { m_bInteractive = bint; } /** Checks if this compiler is as an interactive I/O */ bool isInteractive() const { return m_bInteractive; } /** Return the search path inherited from upper facilities. This search path is used to drive module loading in macro compiler. */ const String& searchPath() const { return m_searchPath; } /** Sets the compiler-specific search path. This search path is used to drive module loading in macro compiler. */ void searchPath( const String& path ) { m_searchPath = path; } }; } // end of namespace #endif /* end of compiler.h */ include/falcon/complex.h000066400000000000000000000077521176363201700156040ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: complex.h Complex class for Falcon ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 09 Sep 2009 23:09:25 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Complex class for Falcon Internal logic functions - declarations. */ #ifndef FALCON_complex_H #define FALCON_complex_H #include #include #include namespace Falcon { class Complex: public BaseAlloc { numeric m_real; numeric m_imag; void throw_div_by_zero(); public: Complex( numeric r=0, numeric i=0 ): m_real(r), m_imag(i) {} Complex( const Complex& other ): m_real( other.m_real ), m_imag( other.m_imag ) {} ~Complex ( ) {} inline numeric real() const { return m_real; } inline numeric imag() const { return m_imag; } inline void real( numeric r ) { m_real = r; } inline void imag( numeric i ) { m_imag = i; } //============================================= // Math operators // inline Complex operator +( const Complex &other ) { return Complex( m_real + other.m_real, m_imag + other.m_imag ); } inline Complex operator -( const Complex &other ) { return Complex( m_real - other.m_real, m_imag - other.m_imag ); } // (ac−bd,bc+ad) inline Complex operator *( const Complex &other ) { return Complex( m_real * other.m_real - m_imag * other.m_imag, m_imag * other.m_real + m_real * other.m_imag ); } //(ac+bd+i(bc-ad))/(c2+d2) inline Complex operator /( const Complex &other ) { numeric divisor = other.m_real*other.m_real + other.m_imag * other.m_imag; if ( divisor == 0 ) throw_div_by_zero(); // don't want this inline. return Complex( (m_real * other.m_real + m_imag * other.m_imag) / divisor, (m_imag * other.m_real - m_real * other.m_imag) / divisor ); } numeric abs() const; Complex conj() const; //============================================= // Assignment operators // inline Complex &operator =( const Complex &other ) { m_real = other.m_real; m_imag = other.m_imag; return *this; } inline Complex& operator +=( const Complex &other ) { m_real += other.m_real; m_imag += other.m_imag; return *this; } inline Complex& operator -=( const Complex &other ) { m_real -= other.m_real; m_imag -= other.m_imag; return *this; } inline Complex& operator *=( const Complex &other ) { m_real = m_real * other.m_real - m_imag * other.m_imag; m_imag = m_imag * other.m_real + m_real * other.m_imag; return *this; } inline Complex& operator /=( const Complex &other ) { *this = *this / other; return *this; } //============================================= // Relational operators // inline bool operator ==( const Complex &other ) { return m_real == other.m_real && m_imag == other.m_imag; } inline bool operator !=( const Complex &other ) { return m_real != other.m_real || m_imag != other.m_imag; } inline bool operator >( const Complex &other ) { return m_real > other.m_real || (m_real == other.m_real && m_imag > other.m_imag); } inline bool operator >=( const Complex &other ) { return m_real >= other.m_real || (m_real == other.m_real && m_imag >= other.m_imag); } inline bool operator <( const Complex &other ) { return m_real < other.m_real || (m_real == other.m_real && m_imag < other.m_imag); } inline bool operator <=( const Complex &other ) { return m_real <= other.m_real || (m_real == other.m_real && m_imag <= other.m_imag); } }; } #endif /* end of complex.h */ include/falcon/config.h.in000066400000000000000000000050531176363201700157770ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: config.h This file is partially generated before compile time by the configuration process, or manually finetuned. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: gio giu 7 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Falcon compile time configuration file. This file is partially generated before compile time by the configuration process, or manually finetuned. */ #ifndef FLC_CONFIG_H #define FLC_CONFIG_H //============================================ // Version informations // #define FALCON_VERSION_MAJOR @FALCON_VERSION_MAJOR@ #define FALCON_VERSION_MINOR @FALCON_VERSION_MINOR@ #define FALCON_VERSION_REVISION @FALCON_VERSION_REVISION@ #define FALCON_VERSION_PATCH @FALCON_VERSION_PATCH@ #define FALCON_VERSION_NUM ( @FALCON_VERSION_MAJOR@ <<24 | @FALCON_VERSION_MINOR@ <<16 | @FALCON_VERSION_REVISION@ << 8 | @FALCON_VERSION_PATCH@ ) #define FALCON_VERSION "@FALCON_VERSION_ID@" #define FALCON_VERSION_NAME "@FALCON_VERSION_NAME@" //============================================ // Version RCINFO // #define FALCON_VERSION_RCINFO "@FALCON_VERSION_RC@" #define FALCON_VERSION_RCINFO_N @FALCON_VERSION_RC@ //============================================ // System informations // // Basic system. May be either // - FALCON_SYSTEM_UNIX // - FALCON_SYSTEM_WIN // - FALCON_SYSTEM_MAC #cmakedefine FALCON_SYSTEM_UNIX #cmakedefine FALCON_SYSTEM_WIN #cmakedefine FALCON_SYSTEM_MAC #define FALCON_HOST_SYSTEM "@FALCON_HOST_SYSTEM@" #define FALCON_LITTLE_ENDIAN @FALCON_LITTLE_ENDIAN@ //============================================ // Config informations // #ifdef FALCON_SYSTEM_WIN #define FALCON_DEFAULT_BIN "@CMAKE_INSTALL_PREFIX@\\@FALCON_BIN_DIR@" #define FALCON_DEFAULT_LIB "@CMAKE_INSTALL_PREFIX@\\@FALCON_LIB_DIR@" #define FALCON_DEFAULT_CONFIG "@CMAKE_INSTALL_PREFIX@\\etc" #define FALCON_DEFAULT_LOAD_PATH ".;@CMAKE_INSTALL_PREFIX@\\@FALCON_MOD_DIR@" #else #define FALCON_DEFAULT_BIN "@CMAKE_INSTALL_PREFIX@/@FALCON_BIN_DIR@" #define FALCON_DEFAULT_LIB "@CMAKE_INSTALL_PREFIX@/@FALCON_LIB_DIR@" #define FALCON_DEFAULT_CONFIG "/etc" #define FALCON_DEFAULT_LOAD_PATH ".;@CMAKE_INSTALL_PREFIX@/@FALCON_MOD_DIR@" #endif #endif /* end of config.h */ include/falcon/continuation.h000066400000000000000000000073551176363201700166460ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: continuation.h Continuation object. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 05 Dec 2009 17:04:27 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_CONTINUATION_H_ #define FALCON_CONTINUATION_H_ #include #include namespace Falcon { class Continuation: public BaseAlloc { public: /** Form the continuation. The place where the continuation is formed will be used as the return point when the invoke method is called. */ Continuation( VMachine* vm ); Continuation( const Continuation& e ); virtual ~Continuation(); /** Applies the continuation on the virtual machine. If the continuation has already been suspended, then it resumes the continuation and returns true. Otherwise, it does nothing and returns false. The parameter is set into the return value in case of positive jump. */ bool jump( const Item& jump_param ); /** Invoke the continuation. The effect of the invocation is that of: 1) saving the current context. 2) returning the given value to the original caller. 3) applying the original context and unrolling the stack up to the call level. */ void suspend( const Item& retval ); void reset() { m_bComplete = false; m_tgtSymbol = 0; } /** Returns true if the topmost code in this continuation has been fully executed. In other words, it returns false if it has been not yet executed or executed but suspended. * */ bool complete() const { return m_tgtSymbol != 0 && m_tgtPC >= m_tgtSymbol->getFuncDef()->codeSize(); } bool ready() const { return m_tgtSymbol == 0; } ItemArray ¶ms() { return m_params; } StackFrame* frames() const { return m_top; } // the first parameter of the bottom call... bool updateSuspendItem( const Item& itm ); private: VMachine* m_vm; VMContext* m_context; /** Level at which the stack is created */ uint32 m_stackLevel; /** Symbol/module where the continuation is invoked. */ const Symbol* m_tgtSymbol; /** Symbol/module where the continuation is invoked. */ LiveModule *m_tgtLModule; uint32 m_tgtPC; /** Frame where the call is started. */ StackFrame* m_callingFrame; /** Current frame when the continuation is called. */ StackFrame* m_top; /** First frame on top of the calling frame. */ StackFrame* m_bottom; /** Parameters for the bottom frame (safely stored here) */ ItemArray m_params; bool m_bComplete; int32 m_refCount; }; class ContinuationCarrier: public CoreObject { public: ContinuationCarrier( const CoreClass* cc ); ContinuationCarrier( const ContinuationCarrier& other ); virtual ~ContinuationCarrier(); virtual ContinuationCarrier *clone() const; virtual bool setProperty( const String &prop, const Item &value ); virtual bool getProperty( const String &prop, Item &value ) const; Continuation* cont() const { return m_cont; } void cont( Continuation* cc ) { m_cont = cc; } virtual void gcMark( uint32 mark ); const Item& ccItem() const { return m_citem; } void ccItem( const Item& itm ) { m_citem = itm; } const Item& suspendItem() const { return m_suspendItem; } Item& suspendItem() { return m_suspendItem; } static CoreObject* factory( const CoreClass* cls, void* data, bool deser ); private: Continuation* m_cont; Item m_citem; Item m_suspendItem; uint32 m_mark; }; } #endif /* CONTINUATION_H_ */ include/falcon/core_ext.h000066400000000000000000000042251176363201700157350ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: core_func.h Falcon module manager ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2004-08-01 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef flc_COREFUNC_H #define flc_COREFUNC_H #include #include #include namespace Falcon { class VMachine; /** Core module namespace This namespace contains the extension functions that the falcon scripts are expecting to be able to access in every circumstance. They are mainly harmless functions that allow item manipulations, and they can usually be considere "language standards". Anyhow, it is possible that the embedding application may wish to provide a limited set of this, or a personalized version. In this case, it will provide a different core moudle by using something similar to the core_module_init() function. */ namespace core { FALCON_FUNC len ( ::Falcon::VMachine *vm ); FALCON_FUNC DgetKeyAt ( ::Falcon::VMachine *vm ); FALCON_FUNC DgetValueAt ( ::Falcon::VMachine *vm ); FALCON_FUNC DgetPairAt( ::Falcon::VMachine *vm ); /** Useful symbol to be exported by the engine DLL */ FALCON_FUNC_DYN_SYM CreateTraceback( ::Falcon::VMachine *vm ); /** Useful symbol to be exported by the engine DLL */ FALCON_FUNC_DYN_SYM TraceStep( ::Falcon::VMachine *vm ); /** Useful symbol to be exported by the engine DLL */ FALCON_FUNC_DYN_SYM TraceStep_toString( ::Falcon::VMachine *vm ); /** Useful symbol to be exported by the engine DLL */ FALCON_FUNC_DYN_SYM Error( ::Falcon::VMachine *vm ); /** Useful symbol to be exported by the engine DLL */ FALCON_FUNC_DYN_SYM Error_toString( ::Falcon::VMachine *vm ); /** Useful symbol to be exported by the engine DLL This is exported also by error.h */ FALCON_FUNC_DYN_SYM Error_init( ::Falcon::VMachine *vm ); } // end of core namespace FALCON_DYN_SYM Module * core_module_init(); extern Module *core_module; } #endif /* end of core_func.h */ include/falcon/corecarrier.h000066400000000000000000000043011176363201700164200ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: corecarrier.h Template class to simplify full reflection of application data. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 06 Sep 2009 20:29:54 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_CORECARRIER_H #define FLC_CORECARRIER_H #include #include #include namespace Falcon { template class CoreCarrier: public CoreObject { _T* m_carried; public: CoreCarrier( const CoreClass* base, _T* la ): CoreObject( base ), m_carried( la ) { if ( la != 0 ) la->incref(); setUserData(la); // just not to have it 0 } CoreCarrier( const CoreCarrier& cc ): CoreObject( cc ), m_carried( cc.m_carried ) { if( m_carried != 0 ) m_carried->incref(); setUserData( m_carried ); // just not to have it 0 } virtual ~CoreCarrier() { if ( m_carried != 0 ) carried()->decref(); } bool hasProperty( const String &key ) const { uint32 pos = 0; return generator()->properties().findKey(key, pos); } bool setProperty( const String &prop, const Item &value ) { if ( hasProperty(prop) ) { throw new AccessError( ErrorParam( e_prop_ro, __LINE__ ) .origin( e_orig_runtime ) .extra( prop ) ); } return false; } bool getProperty( const String &key, Item &ret ) const { return defaultProperty( key, ret ); } virtual CoreCarrier *clone() const { return new CoreCarrier<_T>( *this ); } _T* carried() const { return m_carried; } void carried( _T* c ) { if ( m_carried ) m_carried->decref(); m_carried = c; c->incref(); } }; template CoreObject* CoreCarrier_Factory( const CoreClass *cls, void *data, bool ) { return new CoreCarrier<_T>( cls, reinterpret_cast<_T*>(data)); } } #endif /* end of corecarrier.h */ include/falcon/coredict.h000066400000000000000000000071631176363201700157250ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: coredict.h Core dictionary -- base abstract class for dictionary interfaces. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab dic 4 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Core dictionary -- base abstract class for dictionary interfaces. */ #ifndef FALCON_CORE_DICT_H #define FALCON_CORE_DICT_H #include #include #include #include namespace Falcon { class Item; class FALCON_DYN_CLASS CoreDict: public DeepItem, public Garbageable { bool m_blessed; ItemDict* m_dict; public: CoreDict( ItemDict* dict ): m_blessed( false ), m_dict( dict ) { m_dict->owner( this ); } CoreDict( const CoreDict& other ): m_blessed( other.m_blessed ), m_dict( (ItemDict*) other.m_dict->clone() ) { m_dict->owner( this ); } virtual ~CoreDict() { delete m_dict; } const ItemDict& items() const { return *m_dict; } ItemDict& items() { return *m_dict; } uint32 length() const { return m_dict->length(); } Item *find( const Item &key ) const { return m_dict->find( key ); } bool findIterator( const Item &key, Iterator &iter ) { return m_dict->findIterator( key, iter ); } bool remove( const Item &key ) { return m_dict->remove( key ); } void put( const Item &key, const Item &value ) { return m_dict->put( key, value ); } void smartInsert( const Iterator &iter, const Item &key, const Item &value ) { return m_dict->smartInsert( iter, key, value ); } CoreDict *clone() const { return new CoreDict( *this ); } void merge( const CoreDict &dict ) { m_dict->merge( *dict.m_dict ); } void clear() { m_dict->clear(); } int compare( const CoreDict* other ) const { return items().compare( other->items() ); } /** Performs a find using a static string as a key. This wraps the string in a temporary item and calls the normal find(const Item &) */ Item *find( const String &key ) const; //======================================= // Utilities bool find( const Item &key, Item &value ); bool empty() const { return length() == 0; } /** Returns true if this dictionary is blessed. */ bool isBlessed() const { return m_blessed; } /** Returns a method out of this dictionary. This method returns true if there is a function item stored under the given key, provided that this dictionary is blessed. \param name The name of the method to be searched. \param mth An item that will receive a full readied method on success. \return true if the method was found. */ bool getMethod( const String& name, Item &mth ); /** Bless this dictionary. A blessed dictionary becomes a flessible instance. Its elements are treated as properties; the dot accessor will act as searching for the given property, and a read dot accessor will create a mehtod; in the method, the dictionary can be accessed through "self". */ void bless( bool b ) { m_blessed = b; } virtual void readProperty( const String &, Item &item ); virtual void writeProperty( const String &, const Item &item ); virtual void readIndex( const Item &pos, Item &target ); virtual void writeIndex( const Item &pos, const Item &target ); virtual void gcMark( uint32 gen ); }; } #endif /* end of coredict.h */ include/falcon/corefunc.h000066400000000000000000000033171176363201700157320ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: corefunc.h Language level live function object. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 07 Jan 2009 14:54:36 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Abstract live function object. */ #ifndef FLC_CORE_FUNC_H #define FLC_CORE_FUNC_H #include #include #include namespace Falcon { class LiveModule; class VMachine; class ItemArray; /** Class implementing a live function in the VM. */ class FALCON_DYN_CLASS CoreFunc: public CallPoint { LiveModule *m_lm; const Symbol* m_symbol; ItemArray* m_closure; public: /** Creates a Falcon function. The symbol determines if this will be an external or internal function. */ CoreFunc( const Symbol *sym, LiveModule *lm ): m_lm( lm ), m_symbol( sym ), m_closure(0) {} CoreFunc( const CoreFunc& other ): m_closure(0) { m_lm = other.m_lm; m_symbol = other.m_symbol; } virtual ~CoreFunc(); LiveModule *liveModule() const { return m_lm; } const Symbol *symbol() const { return m_symbol; } ItemArray* closure() const { return m_closure; } void closure( ItemArray* cl ) { m_closure = cl; } virtual void readyFrame( VMachine* vm, uint32 paramCount ); virtual bool isFunc() const { return true; } virtual const String& name() const { return m_symbol->name(); } virtual void gcMark( uint32 gen ); }; } #endif /* end of corefunc.h */ include/falcon/coreobject.h000066400000000000000000000404631176363201700162500ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: coreobject.h Base class for all the strict OOP available in falcon. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom dic 5 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef flc_cobject_H #define flc_cobject_H #include #include #include #include #include #include #include namespace Falcon { class VMachine; class String; class CoreClass; class MemPool; class Sequence; class FalconData; class ItemDict; /** Base core object class. To create your own objects, derive from this class and reimplement: \code virtual bool hasProperty( const String &key ) const; virtual bool setProperty( const String &prop, const Item &value ); virtual bool getProperty( const String &key, Item &ret ) const; virtual CoreObject *clone() const; \endcode Eventually, reimplement also: \code virtual bool serialize( Stream *stream, bool bLive ) const; virtual bool deserialize( Stream *stream, bool bLive ); \endcode Those default methods in this class will save the object on the stream on live serialization (i.e. inter-vm serializations), but will just fail on normal serializations. Then, create a factory function returning an object of your class when creating an instance, like the following: \code // typedef ObjectFactory ... CoreObject* MyClassFactory( const CoreClass *cls, void *data, bool bDeserializing ); \endcode Remember that inner program-specific data may be provided later via setUserData() method, so the factory function must take into accunt the fact that \b data may not be provided (may be 0). The \b bDeserializing parameter will be true when creating an instance after a serialized data. As deserialize() is going to be called next, the constructor may take this parameter to avoid performing full construction and let deserialize() to setup the object. Then, in the module owning this object, the class that shall return an instance of this must be configured via ClassDef::factory( ObjectFactory ) method. In example: \code Symbol *my_class = self->addClass( "MyClass", my_class_init ); my_class->getClassDef()->factory( &MyClassFactory ); \endcode Three standard subclasses with their respective factories are provided. - FalconObject is the standard CoreObject containing falcon Item instances in each property and possibly a FalconData entity. - ReflectObject is totally opaque, and all its get/set properties are sent to the class reflection table. The class reflection table indicates which action must be taken when setting or getting a property, and can both store C native data in the underlying user_data, or call functions to perform this task. - CRObject: Is a reflective object that has a back-up Item for each property. If a prioperty is not declared reflective in the class reflection table, it is treated as if in a FalconObject, otherwise its value is generated through the reflection mechanism and cached in the property item. Those three class factories are automatically applied by the VM in case it has not been set. If the class has all the properties fully reflected (or reflected on read and read only) ReflectObject factory will be used; if one or more properties are not reflected CRObject factory will be used; if none is reflected, FalconObject factory is used. \note Actually, there are three factories for each one of the basic classes, depending on which kind of user data is expected to be associated with the created instance, if any. The VM uses the factory functions that suppose that the data stored in the instance will be an instance of FalconData class, as this is the most common case and the only data stored in objects by the engine. */ class FALCON_DYN_CLASS CoreObject: public DeepItem, public Garbageable { protected: void *m_user_data; bool m_bIsFalconData; bool m_bIsSequence; const CoreClass *m_generatedBy; /** State name. */ String* m_state; CoreObject( const CoreClass *parent ); CoreObject( const CoreObject &other ); /** Creates the default value for this property. Simple reflection can be done by overloading CoreObject and handling reflected properties in a string if/else cascade or in a switch based on property searches in the class property table. This utility function can be used as a default way to return basic values (and eventually methods) from the property table if they doesn't need special management. Example: \code bool MyClass::getProperty( const String &key, Item &ret ) const { if ( key == "prop1" ) { //... } else if ( key == "prop2" ) { //... } else { return defaultProperty( key, ret ); } return true; // we found it or we would have called defalutProperty. } \endcode */ bool defaultProperty( const String &key, Item &prop ) const; /** Generates a property access error. If the property is in the class property table, a "read only" property error is raised, otherwise a "property not found" error is raised instead. Useful as a default terminator of simple subclass setProperty() overloads. */ void readOnlyError( const String &key ) const; protected: /** Return a default method among those recorded in the generator class. Takes the property table of the generator class and, if it's a function, methodizes it. @note This method is protected, and meant to extend objects by replacing directly the getProperty() method; external users should rely on getMethod(). @param name The name of the method to be searched. @param mth The item that will be filled with a full ready method instance on success. @return true if the property exists and can be methodized. */ bool getMethodDefault( const String &name, Item &mth ) const; public: /** The base destructor does nothing. Use finalize() hook to dispose of internal, reflected data. */ virtual ~CoreObject(); /** Returns a valid sequence instance if this object's user data is a "Falcon Sequence". Sequences can be used in sequential operations as the for-in loops, or in functional sequence operations (as map, filter and so on). Objects containing a Falcon Sequence as user data can declare this changing this function and returning the sequence data. */ Sequence* getSequence() const { return m_bIsSequence ? static_cast( m_user_data ) : 0; } /** Returns a valid sequence instance if this object's user data is a "Falcon Data". Sequences can be used in sequential operations as the for-in loops, or in functional sequence operations (as map, filter and so on). Objects containing a Falcon Sequence as user data can declare this changing this function and returning the sequence data. */ FalconData *getFalconData() const { return m_bIsFalconData ? static_cast( m_user_data ) : 0; } /** Return the inner user data that is attached to this item. */ void *getUserData() const { return m_user_data; } /** Set a generic user data for this object. This user data is completely un-handled by this class; it's handling is completely demanded to user-defined sub-classes and/or to property-level reflection system. */ void setUserData( void *data ) { m_user_data = data; } /** Set a FalconData as the user data for this object. FalconData class present a minimal interface that cooperates with this class: - It has a virtual destructor, that is called when the wrapping CoreObject instance is destroyed. - It provides a clone() method that is called when the wrapping CoreObject is cloned. - It provides a gcMark() method, called when this Object is marked. - Serialization support is available but defaulted to fail. */ void setUserData( FalconData* fdata ); /** Set a Sequence as the user data for this object. Sequence class is derived from FalconData, and it adds an interface for serial access to items. */ void setUserData( Sequence* sdata ); /** Returns true if this object has the given class among its ancestors. */ bool derivedFrom( const String &className ) const; /** Serializes this instance on a stream. \throw IOError in case of stream error. */ virtual bool serialize( Stream *stream, bool bLive ) const; /** Deserializes the object from a stream. The object should be created shortly before this call, giving instruction to the constructor not to perform a full initialization, as the content of the object will be soon overwritten. Will throw in case of error. \throw IOError in case of stream error. \param stream The stream from which to read the object. \param bLive If true, \return External call indicator. In case it returns true, the caller should */ virtual bool deserialize( Stream *stream, bool bLive ); /** Performs GC marking of the inner object data. This marks properties and eventually inner data, if it's a FalconData* subclass instance. */ virtual void gcMark( uint32 mark ); /** Returns true if the class provides a certain property. Should not account Object metaclass properties, unless explicitly overloaded. \param prop The property to be searched. \return true if the generator class provides this property. */ virtual bool hasProperty( const String &prop ) const; /** Creates a shallow copy of this item. Will return zero if this item has a non-cloneable user-defined data, that is, it's not fully manageable by the language. Clone operation requests the class ObjectManager to clone the user_data stored in this object, if any. In turn, the ObjectManager may ask the user_data, properly cast, to clone itself. If one of this operation fails or is not possible, then the method returns 0. The VM will eventually raise a CloneError to signal that the operation tried to clone a non manageable user-data object. If this object has not a user_data, then the cloneing will automatically succeed. \return a shallow copy of this item. */ virtual CoreObject *clone() const = 0; /** Sets a property in the object. If the property is found, the value in the item is copied, otherwise the object is untouched and false is returned. In case of reflected objects, it may be impossible to set the property. In that case, the owning vm gets an error, and false is returned. \param prop The property to be set. \param value The item to be set in the property. \return ture if the property can be set, false otherwise. */ virtual bool setProperty( const String &prop, const Item &value ) = 0; /** Stores an arbitrary string in a property. The string is copied in a garbageable string created by the object itself. */ bool setProperty( const String &prop, const String &value ); /** Returns the a shallow item copy of required property. The copy is shallow; strings, arrays and complex data inside the original property are shared. \param prop the property to be found \param value an item containing the object proerty copy. \return true if the property can be found, false otherwise */ virtual bool getProperty( const String &prop, Item &value ) const = 0; /** Returns a method from an object. This function searches for the required property; if it's found, and if it's a callable function, the object fills the returned item with a "method" that may be directly called by the VM. \note Actually this function calls methodize() on the retreived item. The value of the \b method parameter may change even if this call is unsuccesfull. \param key the name of the potential method \param method an item where the method will be stored \return true if the property exists and is a callable item. */ bool getMethod( const String &propName, Item &method ) const { if ( getProperty( propName, method ) ) return method.methodize( SafeItem(const_cast(this)) ); return false; } /** Get the class that generated this object. This is the phisical core object instance that generated this object. The symbol in the returned class is the value returend by instanceOf(). \return the CoreClass that were used to generate this object. */ const CoreClass *generator() const { return m_generatedBy; } /** Apply the given values to this object. This function applies the values in dict inside this object, applying repeatedly them via setProperty. This function skips writeProperty and calls directly to the virtual setProperty method. This means that VM overrides are uncalled. If bRaiseOnError is set to true, a failed setProperty will cause the function to raise an AccessError. Otherwise, the function will ignore the error and proceed, but it will return false when completed. \param dict The property => value dictionary to be applied. \param bRaiseOnError if true, raise when a property in dict is not found here. \return true on success, false if some property was not found and bRaiseOnError is false. */ bool apply( const ItemDict& dict, bool bRaiseOnError = false ); /** Retrieves values of properties in this object. This method calls iteratively getProperty to fill the properties that corespond to the keys in dict. If bFillDict is true, the object is scanned and its properties are set in the dictionary instead. If bIgnoreMethods is true, method proeprties are skipped when bFillDict is true. If bRaiseOnError is true the function throws an AccessError when getProperty fails; this setting is ignored when bFillDict is true. \param dict The dictionary to be filled. \param bRaiseOnError If true, throw an AccessError when dict contains a key that doesn't match any property. \param bFillDict If true, ignore the contents of dict, and fill it with the actual properties instead. \param bIgnoreMethods When true, copy only data properties. \return True on success, false if bRaiseOnError is false and some keys in dict can't be found as properties of this object. */ bool retrieve( ItemDict& dict, bool bRaiseOnError = false, bool bFillDict = false, bool bIgnoreMethods = true ) const; /** Override deep item accessor. By default, it searches a method named "getIndex__" and passes the target item to it; it raises if the method is not found. */ virtual void readIndex( const Item &pos, Item &target ); /** Override deep item accessor. By default, it searches a method named "setIndex__" and passes the target item to it; it raises if the method is not found. */ virtual void writeIndex( const Item &pos, const Item &target ); /** Override deep item accessor. Resolves into getProperty(). */ virtual void readProperty( const String &pos, Item &target ); /** Override deep item accessor. Resolves into setProperty(). */ virtual void writeProperty( const String &pos, const Item &target ); /** Enter a given state. If the state doesn't exist, will throw a CodeError (state not found) error. If it is found, it will change the state of the object via apply and will set the the current state to the given string. The function will also try to set the __leave -> setState -> __enter sequence. */ void setState( const String& state, VMachine* vm ); void setState( const String& state, ItemDict* stateDict ); bool hasState() const { return m_state != 0; } const String& state() const { return *m_state; } }; } #endif /* end of cobject.h */ include/falcon/corerange.h000066400000000000000000000036171176363201700160760ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: corerange.h Range object implementation ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 07 Jan 2009 11:03:32 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_CORE_RANGE_H #define FLC_CORE_RANGE_H #include #include #include #include #ifndef LLONG_MIN #define LLONG_MIN (-9223372036854775807LL-1) #endif namespace Falcon { class FALCON_DYN_CLASS CoreRange: public Garbageable { int64 m_start; int64 m_end; int64 m_step; public: CoreRange(): Garbageable(), m_start(0), m_end( 0 ), m_step( 0 ) {} CoreRange( int64 start ): Garbageable(), m_start( start ), m_end( 0 ), m_step( LLONG_MIN ) {} CoreRange( int64 start, int64 end ): Garbageable(), m_start( start ), m_end( end ), m_step( 0 ) {} CoreRange( int64 start, int64 end, int64 step ): Garbageable(), m_start( start ), m_end( end ), m_step( step ) {} CoreRange( const CoreRange &other ): Garbageable(), m_start( other.m_start ), m_end( other.m_end ), m_step( other.m_step ) {} virtual ~CoreRange() {} bool isOpen() const { return m_step == (int64) LLONG_MIN; } int64 start() const { return m_start; } int64 end() const { return m_end; } int64 step() const { return m_step; } void setOpen() { m_step = LLONG_MIN; } void start( int64 s ) { m_start = s; } void end( int64 s ) { m_end = s; } void step( int64 s ) { m_step = s; } CoreRange* clone() const { return new CoreRange( *this ); } }; } #endif /* end of corerange.h */ include/falcon/coreslot.h000066400000000000000000000113431176363201700157560ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: coreslot.h Core Slot - Messaging slot system. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 11 Jan 2009 18:28:35 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_CORESLOT_H #define FALCON_CORESLOT_H #include #include #include #include #include #include #include namespace Falcon { class VMachine; class VMContext; class VMMessage; /** Slot for messages sent around by the VM. This class provide abstract support for low level messaging system. The slot represents an end of the communication process where the incoming message is definitely. */ class FALCON_DYN_CLASS CoreSlot: public ItemList { String m_name; mutable Mutex m_mtx; mutable volatile int32 m_refcount; Item m_assertion; bool m_bHasAssert; Map* m_children; public: CoreSlot( const String &name ): m_name( name ), m_refcount(1), m_bHasAssert( false ), m_children( 0 ) {} virtual ~CoreSlot(); const String& name() const { return m_name; } /** Prepares a broadcast from the current frame. Meant to be called from inside extension functions going to perform broadcasts, this function prepares the multi-call frame used for repeated broadcast-based calls. @param vmc The VM context on which the broadcast is going to be performed. @param pfirst The first parameter in the current call frame that must be repeated. @param pcount Parameter count to be passed in the broadcast. \param msg The message that caused the slot to be broadcast (can be none if internally broadcast). \param msgName Name of the message that must be broadcast if different from the name of this slot -- used by send. */ void prepareBroadcast( VMContext *vmc, uint32 pfirst, uint32 pcount, VMMessage* msg = 0, String* nsgName = 0 ); /** Remove a ceratin item from this slot. This will remove an item considered equal to the subscriber from this list. */ bool remove( const Item &subsriber ); void incref() const; void decref(); /** Returns true if this slot is associated with an assertion. */ bool hasAssert() const { return m_bHasAssert; } /** Return the asserted data. This data is meaningless if hasAssert() isn't true. */ const Item &assertion() const { return m_assertion; } /** Sets an assertion for this slot. No action is taken. */ void setAssertion( const Item &a ) { m_assertion = a; m_bHasAssert = true; } /** Performs an assertion for this slot. Also, prepares the VM to run a broadcast loop with the asserted item. */ void setAssertion( VMachine* vm, const Item &a ); /** Retracts the assert data. This function does nothing if the slot didn't have an assertion. */ void retract() { m_bHasAssert = false; m_assertion.setNil(); // so the GC can rip it. } virtual CoreSlot *clone() const; virtual void gcMark( uint32 mark ); virtual void getIterator( Iterator& tgt, bool tail = false ) const; virtual void copyIterator( Iterator& tgt, const Iterator& source ) const; virtual void disposeIterator( Iterator& tgt ) const; /** Gets or eventually creates a child slot. */ CoreSlot* getChild( const String& name, bool create = false ); }; /** Traits for the core slots. */ class CoreSlotPtrTraits: public ElementTraits { public: virtual ~CoreSlotPtrTraits() {} virtual uint32 memSize() const; virtual void init( void *itemZone ) const; virtual void copy( void *targetZone, const void *sourceZone ) const; virtual int compare( const void *first, const void *second ) const; virtual void destroy( void *item ) const; virtual bool owning() const; }; namespace traits { extern CoreSlotPtrTraits &t_coreslotptr(); } bool coreslot_broadcast_internal( VMachine *vm ); /** Class taking care of finalizing the core slots when they are published to scripts. */ class FALCON_DYN_CLASS CoreSlotCarrier: public FalconObject { public: CoreSlotCarrier( const CoreClass* generator, CoreSlot* cs, bool bSeralizing = false ); CoreSlotCarrier( const CoreSlotCarrier &other ); virtual ~CoreSlotCarrier(); virtual CoreSlotCarrier *clone() const; /** Change slot after the creation of this carrier (for VMSlot_init) */ void setSlot( CoreSlot* cs ); }; CoreObject* CoreSlotFactory( const CoreClass *cls, void *user_data, bool bDeserial ); } #endif /* end of coreslot.h */ include/falcon/coretable.h000066400000000000000000000173101176363201700160640ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: coretable.h Table support Iterface for Falcon. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Sep 2008 15:15:56 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_CORE_TABLE_H #define FLC_CORE_TABLE_H #include #include #include #include #include #include #include namespace Falcon { class CoreTable; //========================================================= // class CoreTable: public Sequence { CoreArray *m_currentPage; GenericVector m_pages; GenericVector m_headerData; Map m_heading; uint32 m_currentPageId; uint32 m_order; /** Used for votation and bidding operations */ numeric *m_biddingVals; uint32 m_biddingSize; public: enum { noitem = 0xFFFFFFFF }; CoreTable(); CoreTable( const CoreTable& other ); virtual ~CoreTable(); CoreArray *page( uint32 num ) const { return ( num >= m_pages.size() ) ? 0 : *reinterpret_cast( m_pages.at(num) ); } uint32 pageCount() const { return m_pages.size(); } /** Returns the order (number of colums) in the table */ uint32 order() const { return m_order; } /** Returns nth column-wide data in the table (const). Using an incorrect (greater than order) position parameter will crash. */ const Item& columnData( uint32 pos ) const { fassert( pos < order() ); return *(Item*) m_headerData.at(pos); } /** Returns nth column-wide data in the table. Using an incorrect (greater than order) position parameter will crash. */ Item& columnData( uint32 pos ) { fassert( pos < order() ); return *(Item*) m_headerData.at(pos); } /** Sets nth column-wide data in the table. Using an incorrect (greater than order) position parameter will crash. */ void columnData( uint32 pos, const Item& data ) { fassert( pos < order() ); m_headerData.set( const_cast( &data ), pos); } /** Returns the nth heading (column title). Using an incorrect (greater than order) position parameter will crash. */ const String &heading( uint32 pos ) const; /** Rename a colunm. \param pos Column to be renamed. \param name The new name for the given column. */ void renameColumn( uint32 pos, const String &name ); /** Inserts a colum in the table. This alters the heading and all the pages, inserting a row of default items (can be nil) as the new column. If pos >= order, the column will be appended at end. \param pos The position where to add the column. \param name The name of the column to be inserted. \param data The column data, can be nil. \param dflt The default value for inserted items; can be nil. */ void insertColumn( uint32 pos, const String &name, const Item &data, const Item &dflt ); /** Removes a colum from the table. This alters the heading and all the pages, removing the item at given position in all the arrays in every page. \param pos The index of the column to be removed. \return true on success, false if pos >= order. */ bool removeColumn( uint32 pos ); bool setCurrentPage( uint32 num ) { if ( num < m_pages.size() ) { m_currentPageId = num; m_currentPage = *reinterpret_cast(m_pages.at( num )); return true; } return false; } CoreArray *currentPage() const { return m_currentPage; } uint32 currentPageId() const { return m_currentPageId; } //TODO: Transform in ItemArray& bool setHeader( CoreArray *header ); bool setHeader( const ItemArray& header ); uint32 getHeaderPos( const String &name ) const; Item *getHeaderData( uint32 pos ) const; Item *getHeaderData( const String &name ) const { uint32 pos = getHeaderPos( name ); if ( pos == noitem ) return 0; return getHeaderData( pos ); } /** Inserts a new row in a page. If the core array has not the same length of the order of this table, the function will fail. \param pos The position at which to insert the row; will add it if position >= length. \param ca The array to be added. \param page The page into which the row must be added, set \return true on success */ bool insertRow( CoreArray *ca, uint32 pos=noitem, uint32 page = noitem ); /** Removes a row. \param pos The position at which to remove the row. \param page The page into which the row must be remove, set \return true on success */ bool removeRow( uint32 pos, uint32 page = noitem ); /** Inserts a page. The function checks if the array is actually a matrix with an order compatible with the table. \param self The core object representing this table in the VM. \param pos Where to insert the page. If greater than the number of pages, or set to noitem, will append the page at the end. \param array An array to fill the page. Can be empty; otherwise, every element must be an array of the same order of this table. \return true if the page can be added, false if the given array wasn't of correct order. */ bool insertPage( CoreObject *self, CoreArray *data, uint32 pos = noitem ); /** Removes a page. \param pos Which page to remove. \return true if the page can be remove, false the position is invalid. */ bool removePage( uint32 pos ); //======================================== //===== Sequence interface =============== // /** Deletes the list. Items are shallowly destroyed. */ virtual CoreTable *clone() const; virtual const Item &front() const; virtual const Item &back() const; /** Removes all the raws in the current page. */ virtual void clear(); /** Append an item at the end of the sequence. */ virtual void append( const Item &data ); /** Prepend an item at the beginning of the sequence. */ virtual void prepend( const Item &data ); /** Tells if the list is empty. \return true if the list is empty. */ virtual bool empty() const { return m_currentPage == 0 || m_currentPage->length() == 0; } /** Perform marking of items stored in the table. */ virtual void gcMark( uint32 mark ); /** Returns bidding results. Must be initialized with makebiddings(). */ numeric *biddings() const { return m_biddingVals; } /** Returns max size of the biddings array. */ uint32 biddingsSize() const { return m_biddingSize; } void reserveBiddings( uint32 size ); //============================================ // Iterator implementation //============================================ protected: virtual void getIterator( Iterator& tgt, bool tail = false ) const ; virtual void copyIterator( Iterator& tgt, const Iterator& source ) const ; virtual void insert( Iterator &iter, const Item &data ) ; virtual void erase( Iterator &iter ) ; virtual bool hasNext( const Iterator &iter ) const ; virtual bool hasPrev( const Iterator &iter ) const ; virtual bool hasCurrent( const Iterator &iter ) const ; virtual bool next( Iterator &iter ) const ; virtual bool prev( Iterator &iter ) const ; virtual Item& getCurrent( const Iterator &iter ) ; virtual Item& getCurrentKey( const Iterator &iter ) ; virtual bool equalIterator( const Iterator &first, const Iterator &second ) const ; }; } #endif /* end of coretable.h */ include/falcon/crobject.h000066400000000000000000000024231176363201700157160ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: crobject.h Falcon Cached/Reflective Object - Automatic reflected objects with a cache. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 24 Jan 2009 13:48:11 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Falcon Object - Standard instance of classes in script */ #ifndef FLC_CR_OBJECT_H #define FLC_CR_OBJECT_H #include #include namespace Falcon { class VMachine; class Stream; /** Reflective Object with cache. Classes that need to provide an item cache AND require reflection of some or all the properties must derive from this class. */ class FALCON_DYN_CLASS CRObject: public CacheObject { public: CRObject( const CoreClass* generator, bool bSeralizing = false ); CRObject( const CRObject &other ); virtual ~CRObject() {} virtual CRObject *clone() const; virtual bool setProperty( const String &prop, const Item &value ); virtual bool getProperty( const String &key, Item &ret ) const; }; } #endif /* end of crobject.h */ include/falcon/debug.h000066400000000000000000000024721176363201700152150ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: debug.h Falcon debugging system. ------------------------------------------------------------------- Author: Paul Davey Begin: Thur, 26 Nov 2009 06:08:00 +1200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include namespace Falcon { class FALCON_DYN_CLASS DebugVMachine : public VMachine { public: DebugVMachine(); void setBreakPoint(Symbol* func, int32 lineNo); void breakPoint(); void resume(); void stop(); void step(); void runTo(); void restart(); void stepInto(); void stepOut(); void setWatch(String name); void removeWatch(String name); const Map& watches() const; const ItemArray& stackTrace() const; private: //map to hold breakpoints Map m_breakPoints; Map m_watches; bool m_step; bool m_stepInto; bool m_stepOut; virtual void periodicCallback(); }; } //end namespace Falcon include/falcon/deepitem.h000066400000000000000000000044361176363201700157250ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: deepitem.cpp Common interface for deep items. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 02 Jan 2009 21:07:58 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_DEEP_ITEM_H #define FLC_DEEP_ITEM_H #include #include namespace Falcon { class String; class Item; /** Pure interface for deep item core data. Deep items are: - Strings - Object (instances) - Dictionaries - Arrays - Classes Code using this generic interface must be ready to receive Falcon::Error in throws and handle it correctly. This is always true for the VM and ancillary modules. Code directly implementing this access at lower level, as for example, getting a character from a string, doesn't need to be consistent with this behavior (i.e. it may just return false on set error). The rationale of this is that this is high-level code still working with abstract items, with no idea of the lower level implementation, even when this code is directly implemented into the target classes. */ class FALCON_DYN_CLASS DeepItem { public: virtual ~DeepItem() {} /** Implements the deep item interface. May throw AccessError * if the property cannot be read. */ virtual void readProperty( const String &, Item &item ) = 0; /** Implements the deep item interface. May throw AccessError * if the property cannot be set. */ virtual void writeProperty( const String &, const Item &item ) = 0; /** Implements the deep item interface. May throw AccessError * if the pos doesn't represents a valid item index for the current type. */ virtual void readIndex( const Item &pos, Item &target ) = 0; /** Implements the deep item interface. May throw AccessError * if the pos doesn't represents a valid item index for the current type or if the target item can't be set into this item at the given index. */ virtual void writeIndex( const Item &pos, const Item &target ) = 0; }; } #endif /* end of deepitem.h */ include/falcon/deptab.h000066400000000000000000000050711176363201700153640ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_deptab.h Dependency table declaration ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom ago 8 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef flc_DEPTAB_H #define flc_DEPTAB_H #include #include /** \file Dependency table support for modules - header - . */ namespace Falcon { class Module; class Stream; /** Class storing dependency data. */ class ModuleDepData: public BaseAlloc { String m_modName; bool m_bPrivate; bool m_bFile; public: ModuleDepData( const String modName, bool bPrivate = false, bool bFile = false ): m_modName( modName ), m_bPrivate( bPrivate ), m_bFile( bFile ) {} const String &moduleName() const { return m_modName; } bool isPrivate() const { return m_bPrivate; } bool isFile() const { return m_bFile; } void setPrivate( bool mode ) { m_bPrivate = mode; } }; /** Module dependency table. Actually it's just a string map supporting module-aware serialization. The strings are actually held in the module string table. */ class FALCON_DYN_CLASS DependTable: public Map { public: DependTable(); ~DependTable(); bool save( Stream *out ) const ; bool load( Module *mod, Stream *in ); /** Adds a dependency to the table. This method creates a new dependency entry with a given dependency local alias, a physical or logical module name and a privacy setting. \note Strings must be pointers to Strings held in the module string table. \param alias The local module alias. \param name The logical or physical module name. \param bPrivate true if the module is private, false to honor its exports. */ void addDependency( const String &alias, const String &name, bool bPrivate, bool bFile=false ); /** Adds a dependency to the table. This version of the function adds a dependency with the same physical or logical name as the local alias. */ void addDependency( const String &name, bool bPrivate = false, bool bFile = false ) { addDependency( name, name, bPrivate, bFile ); } ModuleDepData *findModule( const String &name ) const { ModuleDepData **data = (ModuleDepData **) find( &name ); if( data == 0 ) return 0; return *data; } }; } #endif /* end of flc_deptab.h */ include/falcon/destroyable.h000066400000000000000000000022711176363201700164410ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: destroyable.h Base class for user-defined pointers in objects. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab feb 25 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Base class for user-defined pointers in objects. */ #ifndef flc_destroyable_H #define flc_destroyable_H #include #include #include namespace Falcon { /** Vitual destructor stub class for user defined pointers. The Destroyable class is a simple stub class that provides a virtual destroyer and a signature field. The user willing to set private data into objecs (CoreObject class) will have to instantiate one item from this class and set it into the object. When the object is destroyed, the destroyable virtual destructor is called with him. */ class FALCON_DYN_CLASS Destroyable: public BaseAlloc { public: virtual ~Destroyable(); }; } #endif /* end of destroyable.h */ include/falcon/detmempool.h000066400000000000000000000030051176363201700162650ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: detmempool.h Deterministic memory pool ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom gen 28 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Deterministic memory pool declaration. */ #ifndef flc_detmempool_H #define flc_detmempool_H #include #include namespace Falcon { /** Deterministic Memory Pool. This class works exactly as a basic memory pool, with the exception that if a garbage collection loop does not terminate by a certain time, the loop is interrupted. This may result in none of the garbage to be reclaimed; so the applications usign this memory pool must at times disable the collection timeout, or set it to a sufficently wide interval, so that a full collection may take place. However, the application stays in control of the garbage collection process. Sophisticated applications may derive this class to provide automatic timing strategies that would turn temporarily off the timeout when allocated memory becomes too huge. */ class DetMemPool: public MemPool { uint32 m_msTarget; bool gcDetMark(); void gcDetSweep(); public: DetMemPool() {} virtual bool performGC( bool bForceReclaim = false ); }; } #endif /* end of detmempool.h */ include/falcon/dir_sys.h000066400000000000000000000051211176363201700155750ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dir_internal.h Internal functions prototypes for DirApi. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun feb 13 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Internal functions prototypes for DirApi. This files holds the internal api for directories that is not to be published. */ #ifndef flc_dir_sys_H #define flc_dir_sys_H #include #include #include namespace Falcon { /** Directory entry. This class encapsulate one directory entry, that is, one name found in directory searches. It has methods to read the next entry and to close the search. */ class DirEntry: public FalconData { protected: uint32 m_lastError; String m_path; public: DirEntry( const String &path ): m_lastError(0), m_path( path ) {} virtual bool read( String &fname ) = 0; virtual void close() = 0; uint32 lastError() const { return m_lastError; } // unsupported (for now) virtual DirEntry *clone() const { return 0; } virtual void gcMark( uint32 mark ) {} const String &path() const { return m_path; } }; namespace Sys { bool FALCON_DYN_SYM fal_fileType( const String &filename, FileStat::e_fileType &st ); bool FALCON_DYN_SYM fal_stats( const String &filename, FileStat &st ); bool FALCON_DYN_SYM fal_mkdir( const String &filename, int32 &fsStatus ); bool FALCON_DYN_SYM fal_mkdir( const String &filename, int32 &fsStatus, bool descend ); bool FALCON_DYN_SYM fal_unlink( const String &filename, int32 &fsStatus ); bool FALCON_DYN_SYM fal_rmdir( const String &filename, int32 &fsStatus ); bool FALCON_DYN_SYM fal_chdir( const String &filename, int32 &fsStatus ); bool FALCON_DYN_SYM fal_move( const String &filename, const String &dest, int32 &fsStatus ); bool FALCON_DYN_SYM fal_getcwd( String &fname, int32 &fsError ); bool FALCON_DYN_SYM fal_chmod( const String &fname, uint32 mode ); bool FALCON_DYN_SYM fal_chown( const String &fname, int32 owner ); bool FALCON_DYN_SYM fal_chgrp( const String &fname, int32 grp ); bool FALCON_DYN_SYM fal_readlink( const String &fname, String &link ); bool FALCON_DYN_SYM fal_writelink( const String &fname, const String &link ); DirEntry FALCON_DYN_SYM *fal_openDir( const String &path, int32 &fsError ); void FALCON_DYN_SYM fal_closeDir( DirEntry *entry ); } } #endif /* end of dir_internal.h */ include/falcon/dir_sys_unix.h000066400000000000000000000021211176363201700166350ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dir_sys_unix.h Support for directory oriented operations in unix systems ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom nov 7 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Support for directory oriented operations in unix systems. */ #ifndef flc_dir_sys_unix_H #define flc_dir_sys_unix_H #include #include #include #include namespace Falcon { class String; /** Support for low level directory system for unix. */ class DirEntry_unix: public DirEntry { protected: DIR *m_raw_dir; public: DirEntry_unix( const String &p, DIR *d ): DirEntry(p), m_raw_dir( d ) {} virtual ~DirEntry_unix() { close(); } virtual bool read( String &dir ); virtual void close(); }; } #endif /* end of dir_sys_unix.h */ include/falcon/dir_sys_win.h000066400000000000000000000023221176363201700164520ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dir_win.h Support for directory oriented operations in unix systems ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom nov 7 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Support for directory oriented operations in unix systems */ #ifndef flc_dir_win_H #define flc_dir_win_H #include #ifndef INVALID_FILE_ATTRIBUTES #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) #endif #include namespace Falcon { class String; class DirEntry_win: public DirEntry { protected: HANDLE m_handle; WIN32_FIND_DATAW m_raw_dir; bool m_first; uint32 m_lastError; public: DirEntry_win( const String &p, HANDLE handle, WIN32_FIND_DATAW dir_data ): DirEntry(p), m_first( true ), m_lastError( 0 ), m_handle( handle ), m_raw_dir( dir_data ) {} ~DirEntry_win() { close(); } virtual bool read( String &name ); virtual void close(); }; } #endif /* end of dir_win.h */ include/falcon/dll.h000066400000000000000000000012201176363201700146700ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_dll.h Base class for Dynamic load system ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar ago 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef flc_DLL_H #define flc_DLL_H #if defined(FALCON_SYSTEM_WIN) #include #elif defined(FALCON_SYSTEM_MAC) #include #else #include #endif #endif /* end of flc_dll.h */ include/falcon/dll_base.h000066400000000000000000000022021176363201700156630ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_dll.h Base class for Dynamic load system ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar ago 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef flc_DLL_BASE_H #define flc_DLL_BASE_H #include namespace Falcon { class DllFunc { void *m_data; public: DllFunc( void *data ): m_data(data) {} void *data() const { return m_data; } void data( void *dt ) { m_data = dt; } }; class DllLoader_base { public: DllLoader_base() {} virtual ~DllLoader_base() {} virtual bool open( const String &dll_name ) = 0; virtual bool close() = 0; virtual DllFunc getSymbol( const String &sym_name ) const = 0; virtual void getErrorDescription( String &descr ) const = 0; static bool isDllMark( char ch1, char ch2 ) { return false; } static const char *dllExt() { return ""; } }; } #endif /* end of flc_dll.h */ include/falcon/dll_dl.h000066400000000000000000000022261176363201700153560ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_dll_dl.h libdl Dynamic Link support ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom set 12 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file This file contains the multiplatform DLL incarnation for those systems implementing lib DL (dlopen/dlsym/dlclose). */ #ifndef flc_DLL_DL_H #define flc_DLL_DL_H #include #include namespace Falcon { class DllLoader_dl { void *m_module; public: DllLoader_dl(): m_module( 0 ) {} virtual ~DllLoader_dl(); bool open( const String &dll_name ); bool close(); virtual void getErrorDescription( String &descr ) const; void assign( DllLoader_dl &other ); DllFunc getSymbol( const String &sym_name ) const ; static bool isDllMark( char ch1, char ch2 ); static const char *dllExt() { return "so"; }; }; typedef DllLoader_dl DllLoader; } #endif /* end of flc_dll_dl.h */ include/falcon/dll_mac.h000066400000000000000000000022721176363201700155200ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dll_mac.h libdl Dynamic Link support ------------------------------------------------------------------- Author: Guerra Francesco Begin: sab mag 06 2006 ------------------------------------------------------------------- (C) Copyright 2006: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file This file contains the multiplatform DLL incarnation for those systems implementing lib DL (dlopen/dlsym/dlclose). */ /** CONST */ #ifndef flc_DLL_DL_H #define flc_DLL_DL_H #include #include namespace Falcon { class DllLoader_Mac { void *m_module; public: DllLoader_Mac(): m_module( 0 ) {} virtual ~DllLoader_Mac(); bool open( const String &dll_name ); bool close(); virtual void getErrorDescription( String &descr ) const; void assign( DllLoader_Mac &other ); DllFunc getSymbol( const String &sym_name ) const ; static bool isDllMark( unsigned char ch1, unsigned char ch2 ); static const char *dllExt() { return "dylib"; }; }; typedef DllLoader_Mac DllLoader; } #endif /* end of flc_dll_dl.h */ include/falcon/dll_win.h000066400000000000000000000021601176363201700155510ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_dll_win.h Windows specific class for Dynamic load system ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar ago 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef flc_DLL_WIN_H #define flc_DLL_WIN_H #include #include namespace Falcon { class FALCON_DYN_CLASS DllLoader_win { HMODULE m_module; DWORD m_error; public: DllLoader_win(): //DllLoader_base(), m_module( NULL ) {} virtual ~DllLoader_win(); bool open( const String &dll_name ); bool close(); virtual void getErrorDescription( String &descr ) const; void assign( DllLoader_win &other ); DllFunc getSymbol( const char *sym_name ) const ; static bool isDllMark( char ch1, char ch2 ); static const char *dllExt() { return "dll"; }; }; typedef DllLoader_win DllLoader; } #endif /* end of flc_dll_win.h */ include/falcon/eng_messages.h000066400000000000000000000415461176363201700165740ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: eng_messages.h String table used for the engine and the core module messages. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 13 Jan 2009 21:35:17 +0100 ------------------------------------------------------------------- (C) Copyright 2004-2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file String table used for the engine and the core module messages. */ #undef FAL_ENGMSG #undef FAL_ERRORDECL #ifdef FLC_DECLARE_ERROR_TABLE // when creating the error enumeration #define FAL_ENGMSG( x, y ) #define FAL_ERRORDECL( errid, code, x ) const int errid = code; #else #ifdef FLC_DECLARE_ENGINE_MSG // when globally declaring the stings #define FAL_ENGMSG( msg, x ) int msg; #define FAL_ERRORDECL( errid, code, x ) int msg_##errid; #else #ifdef FLC_REALIZE_ENGINE_MSG // when creating the string ids. #undef FAL_ENGMSG #undef FAL_ERRORDECL #define FAL_ENGMSG( msg, str ) {String *s = new String( str ); s->exported(true); msg = engineStrings->add( s ); } #define FAL_ERRORDECL( errid, code, str ) {String *s = new String( str ); s->exported(true); msg_##errid = engineStrings->add( s ); } #else #ifdef FLC_MAKE_ERROR_MESSAGE_SELECTOR #define FAL_ENGMSG( msg, str ) #define FAL_ERRORDECL( errid, code, str ) case errid: return Falcon::Engine::getMessage( msg_##errid ); #else // The normal case #define FAL_ENGMSG( msg, x ) extern int msg; #define FAL_ERRORDECL( errid, code, x ) extern int msg_##errid; #endif #endif #endif #endif //================================================================ // Generic engine messages // FAL_ENGMSG( msg_banner, "The Falcon Programming Language" ); FAL_ENGMSG( msg_unknown_error, "Unrecognized error code" ); FAL_ENGMSG( msg_io_curdir, "I/O Error in reading current directory" ); //================================================================ // RTL/ Core module MESSAGES // FAL_ENGMSG( rtl_start_outrange, "start position out of range" ); FAL_ENGMSG( rtl_array_missing, "required an array, a start and an end position" ); FAL_ENGMSG( rtl_inv_startend, "invalid start/end positions" ); FAL_ENGMSG( rtl_cmdp_0, "parameter array contains non string elements" ); FAL_ENGMSG( rtl_emptyarr, "parameter array is empty" ); FAL_ENGMSG( rtl_iterator_not_found, "\"Iterator\" class not found in VM" ); FAL_ENGMSG( rtl_invalid_iter, "Given item is not a valid iterator for the collection" ); FAL_ENGMSG( rtl_marshall_not_cb, "Marshalled event name must be a string as first element in the given array" ); FAL_ENGMSG( rtl_invalid_path, "Invalid path" ); FAL_ENGMSG( rtl_invalid_uri, "Invalid URI" ); FAL_ENGMSG( rtl_no_tabhead, "First row of the table must be a header" ); FAL_ENGMSG( rtl_invalid_tabhead, "Table header must be composed of strings or future bindings" ); FAL_ENGMSG( rtl_invalid_order, "Table order must be greater than zero" ); FAL_ENGMSG( rtl_invalid_tabrow, "Row inserted in table has different order" ); FAL_ENGMSG( rtl_broken_table, "The table changed during a const operation" ); FAL_ENGMSG( rtl_uncallable_col, "Given column contains some uncallable items" ); FAL_ENGMSG( rtl_no_page, "Page ID not present in table" ); FAL_ENGMSG( rtl_tabhead_given, "Table heading already given" ); FAL_ENGMSG( rtl_string_empty, "Refill string cannot be empty" ); FAL_ENGMSG( rtl_row_out_of_bounds, "Row larger than current page size" ); FAL_ENGMSG( rtl_buffer_full, "Given memory buffer is full" ); FAL_ENGMSG( rtl_zero_size, "Given size less or equal to zero" ); //================================================================ // Error messages. // FAL_ERRORDECL( e_none, 0, "No error" ); FAL_ERRORDECL( e_syntax, 1, "Generic syntax error" ); FAL_ERRORDECL( e_unpack_size, 2, "Incompatible unpack size for list assignment" ); FAL_ERRORDECL( e_break_out, 3, "Break outside loops" ); FAL_ERRORDECL( e_continue_out, 4, "Continue outside loops" ); FAL_ERRORDECL( e_div_by_zero, 5, "Division by zero" ); FAL_ERRORDECL( e_mod_by_zero, 6, "Module by zero" ); FAL_ERRORDECL( e_invalid_op, 7, "Invalid operator" ); FAL_ERRORDECL( e_assign_const, 8, "Assignment to a constant" ); FAL_ERRORDECL( e_assign_sym, 9, "Assignment to a non assignable symbol" ); FAL_ERRORDECL( e_static_call, 10, "Non-static method called statically" ); FAL_ERRORDECL( e_global_notin_func, 11, "Global statement not inside a function" ); FAL_ERRORDECL( e_already_def, 12, "Symbol already defined" ); FAL_ERRORDECL( e_inv_token, 13, "Unrecognized token" ); FAL_ERRORDECL( e_undef_sym, 14, "Undefined symbol" ); FAL_ERRORDECL( e_export_all, 15, "Already exported all" ); FAL_ERRORDECL( e_static_notin_func, 16, "Static related instruction outside a function" ); FAL_ERRORDECL( e_invop, 17, "Invalid operands given opcode" ); FAL_ERRORDECL( e_prop_pinit, 18, "Property definition after init definition" ); FAL_ERRORDECL( e_prop_adef, 19, "Property already defined" ); FAL_ERRORDECL( e_stackuf, 20, "Stack underflow" ); FAL_ERRORDECL( e_stackof, 21, "Stack overflow" ); FAL_ERRORDECL( e_arracc, 22, "Access array out of bounds" ); FAL_ERRORDECL( e_nostartsym, 23, "No startup symbol found" ); FAL_ERRORDECL( e_uncaught, 24, "Explicitly raised item is uncaught" ); FAL_ERRORDECL( e_binload, 25, "System error in loading a binary module" ); FAL_ERRORDECL( e_binstartup, 26, "Binary module has not the 'falcon_module_init' startup procedure" ); FAL_ERRORDECL( e_bininit, 27, "Module cannot be initialized" ); FAL_ERRORDECL( e_modver, 28, "Unrecognized module version" ); FAL_ERRORDECL( e_modformat, 29, "Generic falcon module format error" ); FAL_ERRORDECL( e_modio, 30, "I/O error while loading a module" ); FAL_ERRORDECL( e_unclosed_cs, 31, "Unclosed control structure" ); FAL_ERRORDECL( e_runaway_eof, 32, "Parse error at end of file" ); FAL_ERRORDECL( e_prop_acc, 33, "Requested property not found in object" ); FAL_ERRORDECL( e_deadlock, 34, "Deadlock detected" ); FAL_ERRORDECL( e_prov_name, 35, "Operator ''provides'' must be followed by a symbol name" ); FAL_ERRORDECL( e_init_given, 36, "Constructor already declared" ); FAL_ERRORDECL( e_static_const, 37, "Static member initializers must be a constant expression" ); FAL_ERRORDECL( e_inv_inherit, 38, "Class inherits from a symbol that is not a class" ); FAL_ERRORDECL( e_nonsym_ref, 39, "Trying to get a reference from something that's not a symbol" ); FAL_ERRORDECL( e_no_cls_inst, 40, "No internal class found for standalone object" ); FAL_ERRORDECL( e_switch_clash, 41, "Duplicate or clashing switch case" ); FAL_ERRORDECL( e_switch_default, 42, "Default block already defined in switch" ); FAL_ERRORDECL( e_service_adef, 43, "Service already published" ); FAL_ERRORDECL( e_service_undef, 44, "Required service has not been published" ); FAL_ERRORDECL( e_file_output, 46, "Can't create output file" ); FAL_ERRORDECL( e_domain, 47, "Mathematical domain error" ); FAL_ERRORDECL( e_charRange, 48, "Invalid character while parsing source" ); FAL_ERRORDECL( e_par_close_unbal, 49, "Closing a parenthesis, but never opened" ); FAL_ERRORDECL( e_square_close_unbal, 50, "Closing square bracket, but never opened" ); FAL_ERRORDECL( e_graph_close_unbal, 51, "Closing a bracket, but never opened" ); FAL_ERRORDECL( e_inv_num_format, 52, "Invalid numeric format" ); FAL_ERRORDECL( e_inv_esc_sequence, 53, "Invalid string escape sequence" ); FAL_ERRORDECL( e_numparse_long, 54, "String too long for numeric conversion" ); FAL_ERRORDECL( e_bitwise_op, 55, "Bitwise operation on non-numeric parameters" ); FAL_ERRORDECL( e_switch_body, 56, "Invalid statement in switch body" ); FAL_ERRORDECL( e_select_body, 57, "Invalid statement in select body" ); FAL_ERRORDECL( e_lone_end, 58, "'end' statement without open contexts" ); FAL_ERRORDECL( e_inv_inherit2, 59, "Inheritance from more than one subtree of reflected classes" ); FAL_ERRORDECL( e_byte_access, 60, "Byte accessor [*x] is read-only" ); FAL_ERRORDECL( e_global_again, 61, "Variable was already global" ); FAL_ERRORDECL( e_malformed_uri, 62, "Malformed or invalid URI" ); FAL_ERRORDECL( e_unknown_vfs, 63, "Unknown virtual file system for scheme part in URI" ); FAL_ERRORDECL( e_modname_inv, 64, "Invalid module logical name" ); FAL_ERRORDECL( e_final_inherit, 65, "Inheriting from a final class" ); FAL_ERRORDECL( e_numparse, 66, "Invalid source data while converting to number" ); FAL_ERRORDECL( e_default_decl, 100, "Syntax error in 'default' statement" ); FAL_ERRORDECL( e_case_decl, 101, "Syntax error in case statement" ); FAL_ERRORDECL( e_switch_decl, 103, "Syntax error in 'switch' statement" ); FAL_ERRORDECL( e_select_decl, 104, "Syntax error in 'select' statement" ); FAL_ERRORDECL( e_case_outside, 105, "Statement 'case' is valid only within switch or select statements" ); FAL_ERRORDECL( e_syn_load, 106, "Syntax error in 'load' directive" ); FAL_ERRORDECL( e_toplevel_func, 107, "Non-anonymous functions must be declared at top level" ); FAL_ERRORDECL( e_toplevel_obj, 108, "Objects must be declared at top level" ); FAL_ERRORDECL( e_toplevel_class, 109, "Classes must be declared at top level" ); FAL_ERRORDECL( e_toplevel_load, 110, "Load directive must be called at top level" ); FAL_ERRORDECL( e_syn_while , 111, "Syntax error in 'while' statement" ); FAL_ERRORDECL( e_syn_if, 112, "Syntax error in 'if' statement" ); FAL_ERRORDECL( e_syn_else, 113, "Syntax error in 'else' statement" ); FAL_ERRORDECL( e_syn_elif, 114, "Syntax error in 'elif' statement" ); FAL_ERRORDECL( e_syn_break, 115, "Syntax error in 'break' statement" ); FAL_ERRORDECL( e_syn_continue, 116, "Syntax error in 'continue' statement" ); FAL_ERRORDECL( e_syn_for, 117, "Syntax error in 'for' statement" ); FAL_ERRORDECL( e_syn_forfirst, 118, "Syntax error in 'forfirst' statement" ); FAL_ERRORDECL( e_syn_forlast, 119, "Syntax error in 'forlast' statement" ); FAL_ERRORDECL( e_syn_formiddle, 120, "Syntax error in 'formiddle' statement" ); FAL_ERRORDECL( e_syn_try, 121, "Syntax error in 'try' statement" ); FAL_ERRORDECL( e_syn_catch, 122, "Syntax error in 'catch' statement" ); FAL_ERRORDECL( e_syn_raise, 123, "Syntax error in 'raise' statement" ); FAL_ERRORDECL( e_syn_funcdecl, 124, "Syntax error in function declaration" ); FAL_ERRORDECL( e_syn_static, 125, "Syntax error in 'static' statement" ); FAL_ERRORDECL( e_syn_launch, 126, "Syntax error in 'launch' statement" ); FAL_ERRORDECL( e_inv_const_val, 127, "Invalid value for constant declaration" ); FAL_ERRORDECL( e_syn_const, 128, "Syntax error in 'const' statement" ); FAL_ERRORDECL( e_syn_export, 129, "Syntax error in 'export' statement" ); FAL_ERRORDECL( e_syn_forin, 130, "Syntax error in 'for..in' statement" ); FAL_ERRORDECL( e_syn_attrdecl, 131, "Syntax error in attribute declaration" ); FAL_ERRORDECL( e_syn_class, 132, "Syntax error in 'class' statement" ); FAL_ERRORDECL( e_syn_object, 133, "Syntax error in 'object' statement" ); FAL_ERRORDECL( e_syn_global, 134, "Syntax error in 'global' statement" ); FAL_ERRORDECL( e_syn_return, 135, "Syntax error in 'return' statement" ); FAL_ERRORDECL( e_syn_arraccess, 136, "Syntax error in array access" ); FAL_ERRORDECL( e_syn_funcall, 137, "Syntax error in function call" ); FAL_ERRORDECL( e_syn_lambda, 138, "Syntax error in 'lambda' statement" ); FAL_ERRORDECL( e_syn_iif, 139, "Syntax error in '?:' expression" ); FAL_ERRORDECL( e_syn_dictdecl, 140, "Syntax error in dictionary declaration" ); FAL_ERRORDECL( e_syn_arraydecl, 141, "Syntax error in array declaration" ); FAL_ERRORDECL( e_syn_def, 142, "Syntax error in 'def' statement" ); FAL_ERRORDECL( e_syn_fordot, 143, "Syntax error in 'for.' statement" ); FAL_ERRORDECL( e_syn_self_print, 144, "Syntax error in fast print statement" ); FAL_ERRORDECL( e_syn_directive, 145, "Syntax error in directive" ); FAL_ERRORDECL( e_syn_import, 146, "Syntax error in import statement" ); FAL_ERRORDECL( e_syn_macro, 147, "Syntax error in macro definition" ); FAL_ERRORDECL( e_syn_macro_call, 148, "Syntax error in macro call" ); FAL_ERRORDECL( e_syn_loop, 149, "Syntax error in loop statement" ); FAL_ERRORDECL( e_catch_clash, 150, "Duplicate type identifier in catch selector" ); FAL_ERRORDECL( e_catch_adef, 151, "Default catch block already defined" ); FAL_ERRORDECL( e_already_forfirst, 152, "Block 'forfirst' already declared" ); FAL_ERRORDECL( e_already_forlast, 153, "Block 'forlast' already declared" ); FAL_ERRORDECL( e_already_formiddle, 154, "Block 'formiddle' already declared" ); FAL_ERRORDECL( e_fordot_outside, 155, "Statement '.=' must be inside a for/in loop" ); FAL_ERRORDECL( e_par_unbal, 156, "Unbalanced parenthesis at end of file" ); FAL_ERRORDECL( e_square_unbal, 157, "Unbalanced square parenthesis at end of file" ); FAL_ERRORDECL( e_unclosed_string, 158, "Unclosed string at end of file" ); FAL_ERRORDECL( e_graph_unbal, 159, "Unbalanced bracket parenthesis at end of file" ); FAL_ERRORDECL( e_cmp_unprep, 161, "Compiler not prepared (still needs to be fed with a module)" ); FAL_ERRORDECL( e_not_implemented, 162, "Feature not implemented/not available on this instance" ); FAL_ERRORDECL( e_nl_in_lit, 163, "New line in literal string" ); FAL_ERRORDECL( e_fself_outside, 164, "'fself' outside functions or blocks" ); FAL_ERRORDECL( e_undef_param, 165, "Required parameter not found" ); FAL_ERRORDECL( e_noeffect, 166, "Statement has no effect (at least in part)" ); FAL_ERRORDECL( e_ns_clash, 167, "Clash in namespaces aliasing" ); FAL_ERRORDECL( e_directive_unk, 168, "Unknown directive" ); FAL_ERRORDECL( e_directive_value, 169, "Invalid value for directive" ); FAL_ERRORDECL( e_sm_adef, 170, "State member already defined" ); FAL_ERRORDECL( e_state_adef, 171, "State already defined" ); FAL_ERRORDECL( e_undef_state, 172, "Undefined state" ); FAL_ERRORDECL( e_circular_inh, 173, "Circular inheritance detected" ); FAL_ERRORDECL( e_invop_unb, 174, "Unbound value used in arbitrary operation" ); FAL_ERRORDECL( e_open_file, 200, "Can't open file" ); FAL_ERRORDECL( e_loaderror, 201, "Error in loading a module" ); FAL_ERRORDECL( e_nofile, 202, "File not found" ); FAL_ERRORDECL( e_invformat, 203, "Invalid or damaged Falcon VM file" ); FAL_ERRORDECL( e_loader_unsupported, 204, "Operation not supported by the module loader" ); FAL_ERRORDECL( e_io_error, 205, "Generic I/O Error" ); FAL_ERRORDECL( e_unknown_encoding, 206, "Unknown encoding name" ); FAL_ERRORDECL( e_unrec_file_type, 207, "Unrecognized file type" ); FAL_ERRORDECL( e_io_unsup, 208, "Unrecognized file type" ); FAL_ERRORDECL( e_io_invalid, 209, "Unrecognized file type" ); FAL_ERRORDECL( e_deser_eof, 210, "Hit EOF while deserializing" ); FAL_ERRORDECL( e_search_eof, 211, "Search operation failed or item not found" ); FAL_ERRORDECL( e_fmt_convert, 500, "Format not applicable to object" ); FAL_ERRORDECL( e_interrupted, 501, "Asynchronous wait interruption" ); FAL_ERRORDECL( e_priv_access, 502, "Access to private member not through 'self'" ); FAL_ERRORDECL( e_noninst_cls, 503, "Target class cannot be instantiated" ); FAL_ERRORDECL( e_unserializable, 504, "Object cannot be serialized (because of inner native data)" ); FAL_ERRORDECL( e_uncloneable, 506, "Uncloneable object, as part of it is not available to VM" ); FAL_ERRORDECL( e_prop_ro, 507, "Tried to write a read-only property" ); FAL_ERRORDECL( e_wait_in_atomic, 508, "VM received a suspension request in an atomic operation" ); FAL_ERRORDECL( e_table_aconf, 509, "Table already configured" ); FAL_ERRORDECL( e_non_callable, 510, "Non callable symbol called" ); FAL_ERRORDECL( e_prop_invalid, 511, "Invalid or inconsistent property value" ); FAL_ERRORDECL( e_invalid_iter, 512, "Invalid iterator applied to sequence method" ); FAL_ERRORDECL( e_iter_outrange, 513, "Iterator out of range" ); FAL_ERRORDECL( e_non_dict_seq, 514, "Given sequence is not a dictionary sequence"); FAL_ERRORDECL( e_miss_iface, 515, "Missing interface: needed method not found"); FAL_ERRORDECL( e_acc_forbidden, 516, "Access forbidden"); FAL_ERRORDECL( e_prop_wo, 517, "Tried to read a write-only property" ); FAL_ERRORDECL( e_prop_loop, 518, "Property accessed inside its accessor" ); FAL_ERRORDECL( e_table_empty, 519, "Operation on an empty table" ); FAL_ERRORDECL( e_tabcol_acc, 520, "Table column not found" ); FAL_ERRORDECL( e_cont_atomic, 521, "Continuation while in atomic mode" ); FAL_ERRORDECL( e_cont_out, 522, "Continuation invoked when already complete" ); FAL_ERRORDECL( e_parse_format, 523, "Input data is not in expected format" ); FAL_ERRORDECL( e_inv_params, 900, "Invalid parameters" ); FAL_ERRORDECL( e_missing_params, 901, "Mandatory parameter missing" ); FAL_ERRORDECL( e_param_type, 902, "Invalid parameters type" ); FAL_ERRORDECL( e_param_range, 903, "Parameters content invalid/out of range" ); FAL_ERRORDECL( e_param_indir_code, 904, "Parse error in indirect code" ); FAL_ERRORDECL( e_param_strexp_code, 905, "Parse error in expanded string" ); FAL_ERRORDECL( e_param_fmt_code, 906, "Parse error in format specifier" ); /* end of eng_messages.h */ include/falcon/engine.h000066400000000000000000000045331176363201700153740ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: engine.h Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom mag 6 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Main embedding inclusion file. The embedding application should include this file to include all the necessary files. */ #ifndef flc_engine_H #define flc_engine_H // basic functionalities #include #include #include // OS signal handling #include // Global engine functions and variables #include #include // Falcon item system #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Falcon String helpers #include #include #include // Falcon stream helpers #include #include #include #include // compiler and builder #include #include #include // main VM and helpers #include #include #include #include #include #include // Environmental support #include // #include #include #include #include #include // Special types #include #include #include #include // Falcon specific object user_data #include #include #include #endif /* end of engine.h */ include/falcon/engstrings.h000066400000000000000000000021351176363201700163060ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: engstrings.h Declarations of engine string table ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar feb 13 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Declarations of engine string table */ #ifndef flc_engstrings_H #define flc_engstrings_H #include #include #include #include namespace Falcon { class FALCON_DYN_SYM StringTable; const String &getMessage( uint32 id ); bool setTable( StringTable *tab ); /** Sets the engine language. Searches the given language definition in ISO format (i.e. en_US or it_IT) in the string tables that the engine uses as message source. The string table may be built in or searched on the disk *TODO*. */ bool FALCON_DYN_SYM setEngineLanguage( const String &language ); } #endif /* end of engstrings.h */ include/falcon/error.h000066400000000000000000000412111176363201700152520ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: error.h Error class. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom feb 18 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Error class definition file. (this file contains also the TraceStep class). */ #ifndef FALCON_ERROR_H #define FALCON_ERROR_H #include #include #include #include #include #include #include namespace Falcon { class Error; namespace core { FALCON_FUNC_DYN_SYM Error_init ( ::Falcon::VMachine *vm ); FALCON_FUNC_DYN_SYM SyntaxError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC_DYN_SYM CodeError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC_DYN_SYM IoError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC_DYN_SYM AccessError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC_DYN_SYM MathError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC_DYN_SYM ParamError_init ( ::Falcon::VMachine *vm ); FALCON_FUNC_DYN_SYM ParseError_init ( ::Falcon::VMachine *vm ); /** Reflective function to support error property: code */ extern reflectionFuncDecl Error_code_rfrom; extern reflectionFuncDecl Error_description_rfrom; extern reflectionFuncDecl Error_message_rfrom; extern reflectionFuncDecl Error_systemError_rfrom; extern reflectionFuncDecl Error_origin_rfrom; extern reflectionFuncDecl Error_module_rfrom; extern reflectionFuncDecl Error_symbol_rfrom; extern reflectionFuncDecl Error_line_rfrom; extern reflectionFuncDecl Error_pc_rfrom; extern reflectionFuncDecl Error_subErrors_rfrom; extern reflectionFuncDecl Error_boxed_rfrom; extern reflectionFuncDecl Error_code_rto; extern reflectionFuncDecl Error_description_rto; extern reflectionFuncDecl Error_message_rto; extern reflectionFuncDecl Error_systemError_rto; extern reflectionFuncDecl Error_origin_rto; extern reflectionFuncDecl Error_module_rto; extern reflectionFuncDecl Error_symbol_rto; extern reflectionFuncDecl Error_line_rto; extern reflectionFuncDecl Error_pc_rto; extern reflectionFuncDecl Error_boxed_rto; /** Reflective class for error */ class ErrorObject: public CRObject { public: ErrorObject( const CoreClass* cls, Error *err ); Error* getError() const { return (::Falcon::Error*) getUserData(); } virtual ~ErrorObject(); virtual void gcMark( uint32 mark ); virtual ErrorObject *clone() const; }; CoreObject* ErrorObjectFactory( const CoreClass *cls, void *user_data, bool bDeserial ); } // Declare the messaages... #include // and set the error IDS. #define FLC_DECLARE_ERROR_TABLE #include #undef FLC_DECLARE_ERROR_TABLE typedef enum { e_orig_unknown = 0, e_orig_compiler = 1, e_orig_assembler = 2, e_orig_loader = 3, e_orig_vm = 4, e_orig_script = 5, e_orig_runtime = 9, e_orig_mod = 10 } t_origin; class FALCON_DYN_CLASS TraceStep: public BaseAlloc { String m_module; String m_symbol; uint32 m_line; uint32 m_pc; String m_modpath; public: //TODO: Remove this version in the next version. TraceStep( const String &module, const String symbol, uint32 line, uint32 pc ): m_module( module ), m_symbol( symbol ), m_line( line ), m_pc( pc ) {} TraceStep( const String &module, const String &mod_path, const String symbol, uint32 line, uint32 pc ): m_module( module ), m_symbol( symbol ), m_line( line ), m_pc( pc ), m_modpath( mod_path ) {} const String &module() const { return m_module; } const String &modulePath() const { return m_modpath; } const String &symbol() const { return m_symbol; } uint32 line() const { return m_line; } uint32 pcounter() const { return m_pc; } String toString() const { String temp; return toString( temp ); } String &toString( String &target ) const; }; /** Error Parameter class. This class provides the main Error class and its subclasses with named parameter idiom. Errors have many parameters and their configuration is bourdensome and also a big "bloaty" exactly in spots when one would want code to be small. This class, completely inlined, provides the compiler and the programmer with a fast and easy way to configure the needed parameters, preventing the other, unneded details from getting into the way of the coders. The Error class (and its subclasses) has a constructor accepting an ErrorParameter by reference. \code Error *e = new SomeKindOfError( ErrorParam( ... ).p1().p2()....pn() ) \endcode is an acceptable grammar to create an Error. */ class ErrorParam: public BaseAlloc { public: /** Standard constructor. In the constructor a source line may be provided. This makes possible to use the __LINE__ ansi C macro to indicate the point in the source C++ file where an error is raised. \param code error code. \param line optional line where error occurs. */ ErrorParam( int code, uint32 line = 0 ): m_errorCode( code ), m_line( line ), m_character( 0 ), m_pc( 0 ), m_sysError( 0 ), m_origin( e_orig_mod ), m_catchable( true ) {} ErrorParam &code( int code ) { m_errorCode = code; return *this; } ErrorParam &desc( const String &d ) { m_description = d; return *this; } ErrorParam &extra( const String &e ) { m_extra.bufferize(e); return *this; } ErrorParam &symbol( const String &sym ) { m_symbol = sym; return *this; } ErrorParam &module( const String &mod ) { m_module = mod; return *this; } ErrorParam &line( uint32 line ) { m_line = line; return *this; } ErrorParam &pc( uint32 pc ) { m_pc = pc; return *this; } ErrorParam &sysError( uint32 e ) { m_sysError = e; return *this; } ErrorParam &chr( uint32 c ) { m_character = c; return *this; } ErrorParam &origin( t_origin orig ) { m_origin = orig; return *this; } ErrorParam &hard() { m_catchable = false; return *this; } private: friend class Error; int m_errorCode; String m_description; String m_extra; String m_symbol; String m_module; uint32 m_line; uint32 m_character; uint32 m_pc; uint32 m_sysError; t_origin m_origin; bool m_catchable; }; /** The Error class. This class implements an error instance. Errors represent problems occoured both during falcon engine operations (i.e. compilation syntax errors, link errors, file I/O errors, dynamic library load errors ands o on) AND during runtime (i.e. VM opcode processing errors, falcon program exceptions, module function errors). When an error is raised by an engine element whith this capability (i.e. the compiler, the assembler, the runtime etc.), it is directly passed to the error handler, which has the duty to do something with it and eventually destroy it. When an error is raised by a module function with the VMachine::raiseError() method, the error is stored in the VM; if the error is "catchable" AND it occours inside a try/catch statement, it is turned into a Falcon Error object and passed to the script. When a script raises an error both explicitly via the "raise" function or by performing a programming error (i.e. array out of bounds), if there is a try/catch block at work the error is turned into a Falcon error and passed to the script. If there isn't a try/catch block or if the error is raised again by the script, the error instance is passed to the VM error handler. Scripts may raise any item, which may not necessary be Error instances. The item is then copied in the m_item member and passed to the error handler. */ class FALCON_DYN_CLASS Error: public BaseAlloc { protected: int32 m_refCount; int m_errorCode; String m_description; String m_extra; String m_symbol; String m_module; String m_className; uint32 m_line; uint32 m_character; uint32 m_pc; uint32 m_sysError; t_origin m_origin; bool m_catchable; Item m_raised; List m_steps; ListElement *m_stepIter; Error *m_nextError; Error *m_LastNextError; /** Error boxed form other error raising (possibly recursive) */ Error* m_boxed; /** Empty constructor. The error must be filled with proper values. */ Error( const String &className ): m_refCount( 1 ), m_errorCode ( e_none ), m_className( className ), m_line( 0 ), m_character( 0 ), m_pc( 0 ), m_sysError( 0 ), m_origin( e_orig_unknown ), m_catchable( true ), m_nextError( 0 ), m_LastNextError( 0 ), m_boxed(0) { m_raised.setNil(); } /** Copy constructor. */ Error( const Error &e ); /** Minimal constructor. If the description is not filled, the toString() method will use the default description for the given error code. */ Error( const String &className, const ErrorParam ¶ms ): m_refCount( 1 ), m_errorCode ( params.m_errorCode ), m_description( params.m_description ), m_extra( params.m_extra ), m_symbol( params.m_symbol ), m_module( params.m_module ), m_className( className ), m_line( params.m_line ), m_character( params.m_character ), m_pc( params.m_pc ), m_sysError( params.m_sysError ), m_origin( params.m_origin ), m_catchable( params.m_catchable ), m_nextError( 0 ), m_LastNextError( 0 ), m_boxed(0) { m_raised.setNil(); } /** Private destructor. Can be destroyed only via decref. */ virtual ~Error(); public: Error(): m_refCount( 1 ), m_errorCode ( e_none ), m_className( "Error" ), m_line( 0 ), m_character( 0 ), m_pc( 0 ), m_sysError( 0 ), m_origin( e_orig_unknown ), m_catchable( true ), m_nextError( 0 ), m_LastNextError( 0 ), m_boxed(0) { m_raised.setNil(); } Error( const ErrorParam ¶ms ): m_refCount( 1 ), m_errorCode ( params.m_errorCode ), m_description( params.m_description ), m_extra( params.m_extra ), m_symbol( params.m_symbol ), m_module( params.m_module ), m_className( "Error" ), m_line( params.m_line ), m_character( params.m_character ), m_pc( params.m_pc ), m_sysError( params.m_sysError ), m_origin( params.m_origin ), m_catchable( params.m_catchable ), m_nextError( 0 ), m_LastNextError( 0 ), m_boxed(0) { m_raised.setNil(); } void errorCode( int ecode ) { m_errorCode = ecode; } void systemError( uint32 ecode ) { m_sysError = ecode; } void errorDescription( const String &errorDesc ) { m_description = errorDesc; } void extraDescription( const String &extra ) { m_extra = extra; } void module( const String &moduleName ) { m_module = moduleName; } void symbol( const String &symbolName ) { m_symbol = symbolName; } void line( uint32 line ) { m_line = line; } void character( uint32 chr ) { m_character = chr; } void pcounter( uint32 pc ) { m_pc = pc; } void origin( t_origin o ) { m_origin = o; } void catchable( bool c ) { m_catchable = c; } void raised( const Item &itm ) { m_raised = itm; } int errorCode() const { return m_errorCode; } uint32 systemError() const { return m_sysError; } const String &errorDescription() const { return m_description; } const String &extraDescription() const { return m_extra; } const String &module() const { return m_module; } const String &symbol() const { return m_symbol; } uint32 line() const { return m_line; } uint32 character() const { return m_character; } uint32 pcounter() const { return m_pc; } t_origin origin() const { return m_origin; } bool catchable() const { return m_catchable; } const Item &raised() const { return m_raised; } String toString() const { String temp; return toString( temp ); } virtual String &toString( String &target ) const; /** Writes only the heading of the error to the target string. The error heading is everything of the error without the traceback. This method never recurse on error lists; only the first heading is returned. \note the input target string is not cleared; error contents are added at at the end. \note The returned string doesn't terminate with a "\n". */ virtual String &heading( String &target ) const; void appendSubError( Error *sub ); /** Returns an object that can be set in a Falcon item and handled by a script. This method converts the error object in a Falcon Object, derived from the proper class. The method must be fed with a virtual machine. The target virtual machine should have linked a module providing a "specular class". This method will search the given VM for a class having the same name as the one that is returned by the className() method (set in the constructor by the subclasses of Error), and it will create an instance of that class. The method will then fill the resulting object with the needed values, and finally it will set itself as the User Data of the given object. The target class Falcon should be a class derived from the Core class "Error", so that the inherited methods as "toString" and "traceback" are inherited too, and so that a check on "Error" inheritance will be positive. */ virtual CoreObject *scriptize( VMachine *vm ); void addTrace( const String &module, const String &symbol, uint32 line, uint32 pc ); void addTrace( const String &module, const String &mod_path, const String &symbol, uint32 line, uint32 pc ); bool nextStep( String &module, String &symbol, uint32 &line, uint32 &pc ); void rewindStep(); const String &className() const { return m_className; } void incref(); void decref(); Error* subError() const { return m_nextError; } virtual Error *clone() const; void boxError( Error *error ); Error* getBoxedError() const { return m_boxed; } bool hasTraceback() const { return ! m_steps.empty(); } }; class GenericError: public Error { public: GenericError(): Error( "GenericError" ) {} GenericError( const ErrorParam ¶ms ): Error( "GenericError", params ) {} }; class CodeError: public Error { public: CodeError(): Error( "CodeError" ) {} CodeError( const ErrorParam ¶ms ): Error( "CodeError", params ) {} }; class SyntaxError: public Error { public: SyntaxError(): Error( "SyntaxError" ) {} SyntaxError( const ErrorParam ¶ms ): Error( "SyntaxError", params ) {} }; class AccessError: public Error { public: AccessError(): Error( "AccessError" ) {} AccessError( const ErrorParam ¶ms ): Error( "AccessError", params ) {} }; class MathError: public Error { public: MathError(): Error( "MathError" ) {} MathError( const ErrorParam ¶ms ): Error( "MathError", params ) {} }; class TypeError: public Error { public: TypeError(): Error( "TypeError" ) {} TypeError( const ErrorParam ¶ms ): Error( "TypeError", params ) {} }; class IoError: public Error { public: IoError(): Error( "IoError" ) {} IoError( const ErrorParam ¶ms ): Error( "IoError", params ) {} }; class ParamError: public Error { public: ParamError(): Error( "ParamError" ) {} ParamError( const ErrorParam ¶ms ): Error( "ParamError", params ) {} }; class ParseError: public Error { public: ParseError(): Error( "ParseError" ) {} ParseError( const ErrorParam ¶ms ): Error( "ParseError", params ) {} }; class CloneError: public Error { public: CloneError(): Error( "CloneError" ) {} CloneError( const ErrorParam ¶ms ): Error( "CloneError", params ) {} }; class InterruptedError: public Error { public: InterruptedError(): Error( "InterruptedError" ) {} InterruptedError( const ErrorParam ¶ms ): Error( "InterruptedError", params ) {} }; class MessageError: public Error { public: MessageError(): Error( "MessageError" ) {} MessageError( const ErrorParam ¶ms ): Error( "MessageError", params ) {} }; class TableError: public Error { public: TableError(): Error( "TableError" ) {} TableError( const ErrorParam ¶ms ): Error( "TableError", params ) {} }; /** Returns the description of a falcon error. In case the error ID is not found, a sensible message will be returned. */ const String &errorDesc( int errorCode ); } #endif /* end of error.h */ include/falcon/error_base.h000066400000000000000000000037261176363201700162550ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: error_base.h Base error codes for well known modules. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 24 May 2008 13:55:23 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Base error codes for well known modules. This file contains the defines used as "base error codes" by the well known modules (Falcon feathers and other featured modules) authorized to use this resource. Each macro defines an base value for error codes that is then used by the module to declare unique vaules. Codes below 1000 are reserved for the engine, and below 2000 are reserved for feathers. Codes above 10000 (FALCON_USER_ERROR_BASE) are granted to be available for user applications. */ #ifndef flc_error_base_H #define flc_error_base_H #define FALCON_RTL_ERROR_BASE 1000 #define FALCON_COMPILER_ERROR_BASE 1100 #define FALCON_CONFPARSER_ERROR_BASE 1110 #define FALCON_MXML_ERROR_BASE 1120 #define FALCON_PROCESS_ERROR_BASE 1140 #define FALCON_REGEX_ERROR_BASE 1160 #define FALCON_SOCKET_ERROR_BASE 1170 #define FALCON_ZLIB_ERROR_BASE 1190 #define FALCON_LOGGING_ERROR_BASE 1200 #define FALCON_JSON_ERROR_BASE 1210 #define FALCON_DBI_ERROR_BASE 2000 #define FALCON_THREADING_ERROR_BASE 2050 #define FALCON_SDL_ERROR_BASE 2100 #define FALCON_PDF_ERROR_BASE 2200 #define FALCON_ERROR_DYNLIB_BASE 2250 #define FALCON_ERROR_DBUS_BASE 2300 #define FALCON_ERROR_GD_BASE 2330 #define FALCON_ERROR_CURL_BASE 2350 #define FALCON_ERROR_WOPI_BASE 2400 #define FALCON_USER_ERROR_BASE 10000 #endif /* end of error_base.h */ include/falcon/falcondata.h000066400000000000000000000033631176363201700162230ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falcondata.h Falcon common object reflection architecture. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 22 Jun 2008 11:09:16 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Falcon common object reflection architecture. */ #ifndef FALCON_DATA_H #define FALCON_DATA_H #include #include #include namespace Falcon { class MemPool; class Stream; /** Common falcon inner object data infrastructure */ class FALCON_DYN_CLASS FalconData: public BaseAlloc { public: virtual ~FalconData() {} virtual bool isSequence() const { return false; } virtual void gcMark( uint32 mark ) = 0; virtual FalconData *clone() const = 0; /** Serializes this instance on a stream. \throw IOError in case of stream error. */ virtual bool serialize( Stream *stream, bool bLive ) const; /** Deserializes the object from a stream. The object should be created shortly before this call, giving instruction to the constructor not to perform a full initialization, as the content of the object will be soon overwritten. Will throw in case of error. \throw IOError in case of stream error. \param stream The stream from which to read the object. \param bLive If true, \return External call indicator. In case it returns true, the caller should */ virtual bool deserialize( Stream *stream, bool bLive ); }; } #endif /* end of falcondata.h */ include/falcon/falconobject.h000066400000000000000000000017601176363201700165570ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falconobject.h Falcon Object - Standard instance of classes in script ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 24 Jan 2009 13:48:11 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Falcon Object - Standard instance of classes in script */ #ifndef FLC_FALCON_OBJECT_H #define FLC_FALCON_OBJECT_H #include #include namespace Falcon { class VMachine; class FalconData; class FALCON_DYN_CLASS FalconObject: public CacheObject { public: FalconObject( const CoreClass* generator, bool bSeralizing = false ); FalconObject( const FalconObject &other ); virtual ~FalconObject(); virtual FalconObject *clone() const; }; } #endif /* end of falconobject.h */ include/falcon/fassert.h000066400000000000000000000044361176363201700156000ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: fassert.h Falcon specific assertion raising ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab nov 4 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Falcon specific assertion raising */ #ifndef flc_fassert_H #define flc_fassert_H #include #ifndef NDEBUG #ifdef _MSC_VER #ifdef __func__ # define fassert(expr) \ {if (!(expr)) _perform_FALCON_assert_func( #expr, __FILE__, __LINE__, __func__ );} #else # define fassert(expr) \ {if (!(expr)) _perform_FALCON_assert( #expr, __FILE__, __LINE__ );} #endif #else // older versions of g++/mingw hadn't __STRING #ifndef __STRING #define __STRING(x) #x #endif #ifdef __func__ # define fassert(expr) \ {if (!(expr)) _perform_FALCON_assert_func( __STRING(expr), __FILE__, __LINE__, __func__ );} #else # define fassert(expr) \ {if (!(expr)) _perform_FALCON_assert( __STRING(expr), __FILE__, __LINE__ );} #endif #endif #else # define fassert(expr) #endif extern "C" void FALCON_DYN_SYM _perform_FALCON_assert_func( const char *expr, const char *filename, int line, const char *assertFunc ); extern "C" void FALCON_DYN_SYM _perform_FALCON_assert( const char *expr, const char *filename, int line ); namespace Falcon { // for pointers template inline rtype_ptr dyncast(stype* pSource) { #ifndef NDEBUG // Fassert should resolve in nothing in release, but it may change in future. fassert ( pSource == 0 || ( static_cast(pSource) == dynamic_cast(pSource) ) ); #endif return static_cast(pSource); } // for references /* Breaks MINGW template inline rtype_ref dyncast(stype& rSource) { #ifndef NDEBUG try { fassert ( &static_cast(rSource) == &dynamic_cast(rSource) ); } catch(...) { // Block exceptions from dynamic_cast and assert instead. fassert(false); } #endif return static_cast(rSource); } */ } #endif /* end of fassert.h */ include/falcon/filestat.h000066400000000000000000000027211176363201700157370ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: filestat.h Directory and file specific statistic accounting ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: gio giu 21 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Directory and file specific statistic accounting */ #ifndef flc_filestat_H #define flc_filestat_H #include #include #include namespace Falcon { class String; class TimeStamp; class MemPool; /** Multiplatform statistics on files. */ class FALCON_DYN_CLASS FileStat: public FalconData { public: typedef enum { t_notFound = -1, t_unknown = 0, t_normal = 1, t_dir = 2, t_pipe = 3, t_link = 4, t_device = 5, t_socket = 6 } e_fileType; e_fileType m_type; int64 m_size; int32 m_owner; int32 m_group; int32 m_access; /** Dos attribs */ int32 m_attribs; /** Creation or status change */ TimeStamp *m_ctime; /** Last write time */ TimeStamp *m_mtime; /** Last access time */ TimeStamp *m_atime; FileStat(); FileStat( const FileStat &other ); virtual ~FileStat(); virtual FileStat * clone() const; virtual void gcMark( uint32 mark ) {} }; } #endif /* end of filestat.h */ include/falcon/flexymodule.h000066400000000000000000000056351176363201700164700ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flextmodule.h Falcon flexible module prototype. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 24 Jun 2008 18:24:13 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_FLEXY_MODULE_H #define FLC_FLEXY_MODULE_H #include #include #include namespace Falcon { /** Flexible module class. Normally, modules are read-only from a VM standpoint. However, modules willing to be dynamically changed by the VM can declare their disponibility by inheriting from this class. Such modules should be given only to the VM they are linked first. Modules created by a VM for internal needs (i.e. by the reflexive system) are of this type. The most interesting feature of this kind of modules is that of being able to provide new symbols at runtime. See the onSymbolRequest() method. */ class FALCON_DYN_CLASS FlexyModule: public Module { public: /** Mark this module as a Flexy module. */ virtual bool isFlexy() const { return true; } /** Describe this module constness. Flexy modules should not normally be cached by applications willing to cache modules on a global map and serving to the VMs through a modified module loader. However, if the module guarantees that it doesn't modify the interface it presents when loaded by other vms, i.e. by not creating new symbols or changing the valence and internals of existing ones, they can change this mehtod to return true. Application are advised that flexy modules returning true in isConst are safe to be shared among different VMs and can be cached at application level. */ virtual bool isConst() const { return false; } /** Provides symbol on VM dynamic request. The onSymbolRequest() method is a callback that the VM lanunches on all the regiestered modules when a module wants to link a symbol that is not found in the global export table. The flexy module then able to provide the symbol to the VM, and to get it linked just-in-time, on script request. This makes room for full by-request import, as supported by many scripting languages. The flexy module may respect this call and still be const; the module may create the needed symbol locally and refuse to export them. Then, on VM request, it can provide the local symbol returing them through this callback. Doing so, it is granted that the module won't be changed in runtime, and this makes possible to share a flexy module across several VMs too. */ virtual Symbol *onSymbolRequest( const String &name ) = 0; }; } #endif /* end of flexymodule.h */ include/falcon/format.h000066400000000000000000000414411176363201700154160ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: format.h Format base class. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab giu 16 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Format base class */ #ifndef flc_format_H #define flc_format_H #include #include #include #include /* To be added to docs in the next version. convType Type of conversion; determines what type of variable is expected when formatting, depending on the format string. for example, when a decimal number is indicated in the format string, it is implied that the input variable to be formatted is meant to be a numeric value. decimalChr Unicode character used to separate decimal parts of numbers. Defaults to '.'. decimals Number of decimals that should be represented. Zero means to represent only integer numbers. fixedSize If true, the 'size' field is mandatory, and representation of this variable is truncated to a maximum of 'size' characters. grouping Count of grouping characters in numeric representation. For example, a number like "1,000,000" has a grouping of 3, while Japanese stanrd representation is 4-grouping (like "10,0000"). Zero means no grouping. groupingChr Character using for grouping. Defaults to comma (','). misAct Action to perform when a variable of unexpected type is formatted. Can be: */ namespace Falcon { class VMachine; class Item; /** Item to string format class. */ class Format: public FalconData { public: /** negative representation style */ typedef enum { /** Optional minus in front of the number if negative. */ e_minusFront, /** Plus and minus in front of the number. */ e_plusMinusFront, /** Minus after the number if negative. */ e_minusBack, /** Plus or minus after the number always. */ e_plusMinusBack, /** Minus at field alignment end. */ e_minusEnd, /** Plus or minus at field alignment end. */ e_plusMinusEnd, /** In parenthesis if negative */ e_parenthesis, /** In parenthesis if negative, add padding space if positive */ e_parpad } t_negFormat; /** Integer representation style */ typedef enum { /** No transformation */ e_decimal, /** To binary */ e_binary, /** To binary, add b after the number */ e_binaryB, /** To octal */ e_octal, /** To octal, add 0 in front */ e_octalZero, /** To hex, lower case */ e_hexLower, /** To hex, upper case */ e_hexUpper, /** To hex, lowercase, add 0x in front */ e_cHexLower, /** To hex, uppercase, add 0x in front */ e_cHexUpper, /** To scientific notation. */ e_scientific } t_numFormat; /** Conversion type */ typedef enum { /** Basically a numeric conversion */ e_tNum, /** Basically a string conversion */ e_tStr, /** Wrong format / parse error */ e_tError } t_convType; /** Action to perform on type mismatch / impossible conversion */ typedef enum { /** Just return false. In the VM, this will cause the formatter to return nil, or to raise an error if embedded in strings. */ e_actNoAction, /** Act as incompatible types were nil */ e_actNil, /** Act as incompatible types were zero or empty */ e_actZero, /** Raise a type error */ e_actRaise, /** Try to convert, if conversion fails act as if nil */ e_actConvertNil, /** Try to convert, if conversion fails act as if zero of empty */ e_actConvertZero, /** Try to convert, if conversion fails raise a type error */ e_actConvertRaise } t_convMismatch; /** How to represent a nil */ typedef enum { /** Nil is not represented */ e_nilEmpty, /** Nil is written as 'Nil' */ e_nilNil, /** Nil is written as 'N' */ e_nilN, /** Nil is written as 'nil' */ e_nilnil, /** Nil is written as N/A */ e_nilNA, /** Nil is written as None */ e_nilNone, /** Nil is written as NULL */ e_nilNULL, /** Nil is written as Null */ e_nilNull, /** Nil is written as padding */ e_nilPad } t_nilFormat; private: t_convType m_convType; t_convMismatch m_misAct; uint16 m_size; uint32 m_paddingChr; uint32 m_thousandSep; uint32 m_decimalSep; uint8 m_grouping; bool m_fixedSize; t_nilFormat m_nilFormat; bool m_rightAlign; uint8 m_decimals; t_negFormat m_negFormat; t_numFormat m_numFormat; String m_originalFormat; uint32 m_posOfObjectFmt; void formatInt( int64 number, String &target, bool bUseGroup ); void applyNeg( String &target, int64 number ); void applyPad( String &target, uint32 extraSize=0 ); /** Calculate needs of padding size. */ int negPadSize( int64 number ); /** Return true if negative format should be added before adding padding. */ bool negBeforePad(); /** Process a format mismatch. */ bool processMismatch( VMachine *vm, const Item &source, String &target ); /** Try a basic conversion into the desired item of this format. */ bool tryConvertAndFormat( VMachine *vm, const Item &source, String &target ); /** Procude a scientific string */ void formatScientific( numeric num, String &sBuffer ); public: Format() { reset(); } Format( const String &fmt ) { reset(); parse( fmt ); } /** Parses a format string. Transforms a format string into a setup for this format object. The format is a sequence of commands that are parsed independently from their position. Commands are usually described by one, two or more character. Formats are meant to deal with different item types. A format thought for a certain kind of object, for example, a number, may be applied to something different, for example, a string, or the other way around. For this reason, Falcon formats include also what to do if the given item is not thought for the given format. Format elements: - Size: The minimum field lengt; it can be just expressed by a number. if the formatted output is wide as or wider than the allocated size, the output will NOT be truncated, and the resulting string may be just too wide to be displayed where it was intented to be. The size can be mandatory by adding '*' after it. In this case, the function will return false (and eventually raise an error) if the conversion caused the output to be wider than allowed. - Padding: the padding character is appended after the formatted size, or it is prepended before it alignment is right. To define padding character, use 'p' followed by the character. For example, p0 to fill the field with zeroes. Of course, the character may be any Unicode character (the format string accepts standard falcon character escapes). In the special case of p0, front sign indicators are placed at the beginning of the field; for example "4p0+" will produce "+001" "-002" and so on, while "4px+" will produce "xx+1", "xx-2" etc. - Numeric base: the way an integer should be rendered. It may be: - Decimal; as it's the default translation, no command is needed; a 'N' character may be added to the format to specify that we are actually expecting a number. - Hexadecimal: Command may be 'x' (lowercase hex), 'X' (uppercase Hex), 'c' (0x prefixed lowercase hex) or 'C' (0x prefixed uppercase hex). - Binary: 'b' to convert to binary, and 'B' to convert to binary and add a "b" after the number. - Octal: 'o' to display an octal number, or '0' to display an octal with "0" prefix. - Scientific: 'e' to display a number in scientific notation W.D+/-eM. Format of numbers in scientific notation is fixed, so thousand separator and decimal digit separator cannot be set, but decimals cipher setting will still work. - Decimals: '.' followed by a number indicates the number of decimal to be displayed. If no decimal is specified, floating point numbers will be displayed with all significant digits digits, while if is's set to zero, decimal numbers will be rounded. - Decimal separator: a 'd' followed by any non-cipher character will be interpreted as decimal separator setting. For example, to use central european standard for decimal nubmers and limit the output to 3 decimals, write ".3d,", or "d,.3". The default value is '.'. - (Thousand) Grouping: actually it's the integer part group separator, as it will be displayed also for hexadecimal, octal and binary conversions. It is set using 'g' followed by the separator character, it defaults to ','. Normally, it is not displayed; to activate it set also the integer grouping digit count; normally is 3, but it's 4 in Jpanaese and Chinese localses, while it may be useful to set it to 2 or 4 for hexadecimal, 3 for octal and 4 or 8 for binary. For example 'g4-' would group digits 4 by 4, grouping them with a "-". Zero would disable grouping. - Grouping Character: If willing to change only the grouping character and not the default grouping count, use 'G'. - Alignment: by default the field is aligned to the left; to align the field to the right use 'r'. - Negative display format: By default, a '-' sign is appended in front of the number if it's negative. If the '+' character is added to the format, then in case the number is positive, '+' will be appended in front. '--' will postpend a '-' if the number is negative, while '++' will postpend either '+' or '-' depending on the sign of the number. To display a parenthesis around negative numbers, use '[', or use ']' to display a parenthesis for negative numbers and use the padding character in front and after positive numbers. Using parenthesis will prevent using '+', '++' or '--' formats. Format '-^' will add a - in front of padding space if the number is negative, while '+^' will add plus or minus depending on number sign. For example, "5+" would render -12 as " -12", while "5+^" will render as "- 12". If alignment is to the right, the sign will be added at the other side of the padding: "5+^r" would render -12 as "12 -". If size is not mandatory, parenthesis will be wrapped around the formatted field, while if size is mandatory they will be wrapped around the whole field, included padding. For example "5[r" on -4 would render as " (4)", while "5*[r" would render as "( 4)". - Object specific format: Objects may accept an object specific formatting as parameter of the standard "toString" method. A pipe separator '|' will cause all the following format to be passed unparsed to the toString method of objects eventually being formatted. If the object does not provides a toString method, or if it's not an object at all, an error will be raised. The object is the sole responsible for parsing and applying its specific format. - Nil format: How to represent a nil. It may be one of the following: - 'nn': nil is not represented (mute). - 'nN': nil is represented by "N" - 'nl': nil is rendered with "nil" - 'nL': nil is rendered with "Nil". This is also the default. - 'nu': nil is rendered with "Null" - 'nU': nil is rendered with "NULL" - 'no': nil is rendered with "None" - 'nA': nil is rendered with "NA" - Action on error: Normally, if trying to format something different from what is expected, format() the method will simply return false. For example, to format a string in a number, a string using the date formatter, a number in a simple pad-and-size formatter etc. To change this behavior, use '/' followed by one of the following: - 'n': act as the wrong item was nil (and uses the defined nil formatter). - '0': act as if the given item was 0, the empty string or an invalid date, or anyhow the neuter member of the expected type. - 'r': raise a type error. A 'c' letter may be added after the '/' and before the specifier to try a basic conversion into the expected type before triggering the requested effect. This will, for example, cause the toString() method of objects to be called if the formatting is detected to be a string-type format. If the pattern is invalid, a paramter error will be raised. Examples: - "8*Xs-g2": Mandatory 8 characters, Hexadecimal uppercase, grouped 2 by 2 with '-' characters. A result may be "0A-F1-DA". - "12.3'0r+/r" - 12 ciphers, of which 3 are fixed decimals, 0 padded, right aligned. + is always added in front of positive numbers. In case the formatted item is not a number, a type error is raised. \note ranges will be represented as [n1:n2] or [n1:] if they are open. Size, alignment and padding will work on the whole range, while numeric formatting will be applied to each end of the range. \return true if parse is succesful, false on parse format error. */ bool parse( const String &fmt ); /** Checks if this format is valid. */ bool isValid() const { return m_convType != e_tError; } /** Formats given item into the target string. This version doesn't require a VM, but will fail if source item is an object. Also, in case of failure there will be no report about the error context, as that would be normally reported by raising an exception in the VM. \see bool format( VMachine *vm, const Item &source, String &target ) \param source the item to be formatted \param target the string where to output the format. \return true on success, false on error. */ bool format( const Item &source, String &target ) { return format( 0, source, target ); } /** Formats given item into the target string. The target string is not cleared before format occours; in other words, the format output is just appended to the end of the string. Use a pre-allocated string for better performance. In case the source item is an object, its toString() method is called on the given VM. In case of raise from the routine, the method will return false; if raise on failure is set, then the exception is left untouched and it will be passed to the calling routine, else the exception will be reset and the format routine will simply fail. \see parse for format details. \return true on success, false on error. */ bool format( VMachine *vm, const Item &source, String &target ); /** sets default values */ void reset(); //============================================================== // Accessors // const String &originalFormat() const { return m_originalFormat; } t_convType convType() const { return m_convType; } t_convMismatch mismatchAction() const { return m_misAct; } void mismatchAction( t_convMismatch t ) { m_misAct = t; } uint16 fieldSize() const { return m_size; } void fieldSize( uint16 size ) { m_size = size; } uint32 paddingChr() const { return m_paddingChr; } void paddingChr( uint32 chr ) { m_paddingChr = chr; } bool rightAlign() const { return m_rightAlign; } void rightAlign( bool b ) { m_rightAlign = b; } uint8 decimals() const { return m_decimals; } void decimals( uint8 d ) { m_decimals = d; } uint8 grouping() const { return m_grouping; } void grouping( uint8 g ) { m_grouping = g; } t_negFormat negFormat() const { return m_negFormat; } void negFormat( t_negFormat f ) { m_negFormat = f; } t_nilFormat nilFormat() const { return m_nilFormat; } void nilFormat( t_nilFormat f ) { m_nilFormat = f; } t_numFormat numFormat() const { return m_numFormat; } void numFormat( t_numFormat t ) { m_numFormat = t; } bool fixedSize() const { return m_fixedSize; } void fixedSize( bool f ) { m_fixedSize = f; } //================================================= virtual Format *clone() const; virtual void gcMark( uint32 mark ) {} }; } #endif /* end of format.h */ include/falcon/fstream.h000066400000000000000000000265161176363201700155750ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: fstream.h Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven ago 18 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Definition for file based streams. This file contains both the FileStream definitions and the Standard Stream embeddings. The implementation is fully system dependant, so the common library will contain different implementation modules for different systems; in example: - fstream_sys_win.cpp - windows implementation - fstream_sys_unix.cpp - unixlike implementation ecc... */ #ifndef flc_fstream_H #define flc_fstream_H #include namespace Falcon { /** Class storing system specific file data. This class is used by generic file stream to store system dependent data. The implementors may use this class to open a file or a file-like handle with their own native preferred methods, and then store the handle inside the system-specific subclass of this class. The instance will then be passed to the appropriate Stream constructor. */ class FALCON_DYN_CLASS FileSysData: public BaseAlloc { protected: FileSysData() {} public: virtual ~FileSysData() {} virtual FileSysData *dup() = 0; }; /** File stream base class. The BaseFileStream class is the base for the system-specific stream handlers. It provides an interface to the system functions that work with system streams, as files or standard streams. */ class FALCON_DYN_CLASS BaseFileStream: public Stream { public: /** Open mode. */ typedef enum { e_omReadWrite, e_omReadOnly, e_omWriteOnly } t_openMode; /** Share mode. */ typedef enum { e_smExclusive, e_smShareRead, e_smShareFull } t_shareMode; /** System attributes. */ typedef enum { e_aOtherRead = 04, e_aOtherWrite = 02, e_aOtherExecute = 01, e_aGroupRead = 040, e_aGroupWrite = 020, e_aGroupExecute = 010, e_aUserRead = 0400, e_aUserWrite = 0200, e_aUserExecute = 0100, e_aAll = 0777, e_aReadOnly = 0444, e_aHidden = 0600 } t_attributes; protected: FileSysData *m_fsData; virtual int64 seek( int64 pos, Stream::e_whence whence ); public: BaseFileStream( t_streamType streamType, FileSysData *fsdata ): Stream( streamType ), m_fsData( fsdata ) {} BaseFileStream( const BaseFileStream &other ); virtual ~BaseFileStream(); virtual bool close(); virtual int32 read( void *buffer, int32 size ); virtual int32 write( const void *buffer, int32 size ); virtual int64 tell(); virtual bool truncate( int64 pos = - 1); virtual bool errorDescription( ::Falcon::String &description ) const; const FileSysData *getFileSysData() const { return m_fsData; } virtual int64 lastError() const; virtual bool put( uint32 chr ); virtual bool get( uint32 &chr ); /** Return 1 if read available, 0 if not available, -1 on error; */ virtual int32 readAvailable( int32 msecs_timeout, const Sys::SystemData *sysData = 0 ); virtual int32 writeAvailable( int32 msecs_timeout, const Sys::SystemData *sysData = 0 ); virtual bool writeString( const String &source, uint32 begin = 0, uint32 end = csh::npos ); virtual bool readString( String &content, uint32 size ); /** Set the error. Subclasses may use this to set error across platforms. The status is not changed. */ void setError( int64 errorCode ); virtual BaseFileStream *clone() const; }; class FALCON_DYN_CLASS FileStream: public BaseFileStream { public: /** Constructs a stream based on system resources. The implementor must create a FileSysData suitable for the host system containing the resoruces the target system need to access files. */ FileStream( FileSysData *fsdata ): BaseFileStream( t_file, fsdata ) { status( t_open ); } /** Default constructor. System specific implementations will create a consistent FileSysData representing an unopened system resource. Use this constructor if this stream must be used to manage a file that must be created or opened. If the stream is created or opened independently, use the FileStream( FileSysData *) constructor. */ FileStream(); virtual void setSystemData( const FileSysData &data ); /** Open the file. On success, the internal file system specific data are filled with the newly created data. On failure, lastError() will return the error code. */ virtual bool open( const String &filename, t_openMode mode=e_omReadOnly, t_shareMode share=e_smExclusive ); /** Create the file. If the file already existed, it is destroyed and overwritten. On success, the internal file system specific data are filled with the newly created data. On failure, lastError() will return the error code. */ virtual bool create( const String &filename, t_attributes mode, t_shareMode share=e_smExclusive); }; class FALCON_DYN_CLASS StdStream: public BaseFileStream { virtual int64 seek( int64 pos, Stream::e_whence whence ) { m_status = t_unsupported; return -1; } public: StdStream( FileSysData *fsdata ): BaseFileStream( t_stream, fsdata ) { status( t_open ); } /** The StdStream destructor. Overrides basic stream to avoid closing of the underlying stream. The stream must be explicitly closed. */ virtual ~StdStream() {} /* virtual int64 tell() { m_status = t_unsupported; return -1; } */ virtual bool truncate( int64 pos=-1 ) { m_status = t_unsupported; return false; } }; class FALCON_DYN_CLASS InputStream: public StdStream { public: InputStream( FileSysData *fsdata ): StdStream( fsdata ) {} virtual int32 write( const void *buffer, int32 size ) { m_status = t_unsupported; return -1; } }; class FALCON_DYN_CLASS OutputStream: public StdStream { public: OutputStream( FileSysData *fsdata ): StdStream( fsdata ) {} virtual int32 read( void *buffer, int32 size ) { m_status = t_unsupported; return -1; } }; /** Standard Input Stream proxy. This proxy opens a dupped stream that interacts with the standard stream of the process. The application (and the VM, and the scripts too) may open and close an arbitrary number of this instances, without interfering each other. If a script, the VM or an embedding application (that wishes to do it through Falcon portable xplatform API) needs to close the standard stream, then it must create and delete (or simply close) an instance of RawStdxxxStream. */ class FALCON_DYN_CLASS StdInStream: public InputStream { public: StdInStream(); }; /** Standard Output Stream proxy. This proxy opens a dupped stream that interacts with the standard stream of the process. The application (and the VM, and the scripts too) may open and close an arbitrary number of this instances, without interfering each other. If a script, the VM or an embedding application (that wishes to do it through Falcon portable xplatform API) needs to close the standard stream, then it must create and delete (or simply close) an instance of RawStdxxxStream. */ class FALCON_DYN_CLASS StdOutStream: public OutputStream { public: StdOutStream(); }; /** Standard Error Stream proxy. This proxy opens a dupped stream that interacts with the standard stream of the process. The application (and the VM, and the scripts too) may open and close an arbitrary number of this instances, without interfering each other. If a script, the VM or an embedding application (that wishes to do it through Falcon portable xplatform API) needs to close the standard stream, then it must create and delete (or simply close) an instance of RawStdxxxStream. */ class FALCON_DYN_CLASS StdErrStream: public OutputStream { public: StdErrStream(); }; /** Standard Input Stream encapsulation. This Falcon Stream class encapsulates in a multiplatform and script wise class the real physical unerlying process standard stream. Whatever happens to an instance of this class, it will happen also to the embedding process stream. In example, a script willing to close the output stream to signal that there's no more data to be sent before its termiantion, may get an instance of the raw output class through the RTL function stdOutRaw() and then close it with the close() method. If the embedding application wishes to stop VM and scripts from accessing the real process standard stream, it may simply disable the stdInRaw() stdOutRaw() and stdErrRaw() functions by removing them from the RTL module before linking it in the VM. */ class FALCON_DYN_CLASS RawStdInStream: public InputStream { public: RawStdInStream(); }; /** Standard Output Stream encapsulation. This Falcon Stream class encapsulates in a multiplatform and script wise class the real physical unerlying process standard stream. Whatever happens to an instance of this class, it will happen also to the embedding process stream. In example, a script willing to close the output stream to signal that there's no more data to be sent before its termiantion, may get an instance of the raw output class through the RTL function stdOutRaw() and then close it with the close() method. If the embedding application wishes to stop VM and scripts from accessing the real process standard stream, it may simply disable the stdInRaw() stdOutRaw() and stdErrRaw() functions by removing them from the RTL module before linking it in the VM. */ class FALCON_DYN_CLASS RawStdOutStream: public OutputStream { public: RawStdOutStream(); }; /** Standard Error Stream encapsulation. This Falcon Stream class encapsulates in a multiplatform and script wise class the real physical unerlying process standard stream. Whatever happens to an instance of this class, it will happen also to the embedding process stream. In example, a script willing to close the output stream to signal that there's no more data to be sent before its termiantion, may get an instance of the raw output class through the RTL function stdOutRaw() and then close it with the close() method. If the embedding application wishes to stop VM and scripts from accessing the real process standard stream, it may simply disable the stdInRaw() stdOutRaw() and stdErrRaw() functions by removing them from the RTL module before linking it in the VM. */ class FALCON_DYN_CLASS RawStdErrStream: public OutputStream { public: RawStdErrStream(); }; inline BaseFileStream::t_attributes operator|( BaseFileStream::t_attributes one, BaseFileStream::t_attributes two) { return (BaseFileStream::t_attributes) ( ((uint32)one) | ((uint32)two) ); } inline BaseFileStream::t_attributes operator&( BaseFileStream::t_attributes one, BaseFileStream::t_attributes two) { return (BaseFileStream::t_attributes) ( ((uint32)one) & ((uint32)two) ); } inline BaseFileStream::t_attributes operator^( BaseFileStream::t_attributes one, BaseFileStream::t_attributes two) { return (BaseFileStream::t_attributes) ( ((uint32)one) & ((uint32)two) ); } } #endif /* end of fstream.h */ include/falcon/fstream_sys_unix.h000066400000000000000000000017761176363201700175370ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: file_srv_unix.h UNIX system specific data used by FILE service. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom mar 12 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file UNIX system specific data used by file streams service. */ #ifndef flc_fstream_sys_unix_H #define flc_fstream_sys_unix_H #include namespace Falcon { /** Unix specific stream service support. This class provides UNIX system specific data to FILE service. */ class UnixFileSysData: public FileSysData { public: int m_handle; int m_lastError; UnixFileSysData( int handle, int m_lastError ): m_handle( handle ), m_lastError( m_lastError ) {} virtual FileSysData *dup(); }; } #endif /* end of file_srv_unix.h */ include/falcon/fstream_sys_win.h000066400000000000000000000025421176363201700173410ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: fstream_sys_win.h MS-Windows system specific data used by FILE service. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom mar 12 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file MS-Windows system specific data used by FILE service. */ #ifndef flc_fstream_sys_win_H #define flc_fstream_sys_win_H #include #include #include namespace Falcon { /** Win specific stream service support. This class provides MS-Windows system specific data to FILE service. */ class FALCON_DYN_CLASS WinFileSysData: public FileSysData { public: typedef enum { e_dirIn, e_dirOut } t_direction; HANDLE m_handle; DWORD m_lastError; bool m_isConsole; bool m_isPipe; t_direction m_direction; WinFileSysData( HANDLE handle, DWORD m_lastError, bool console=false, t_direction dir=e_dirIn, bool pipe = false ): m_handle( handle ), m_lastError( m_lastError ), m_isConsole( console ), m_direction( dir ), m_isPipe( pipe ) {} virtual FileSysData *dup(); }; } #endif /* end of fstream_sys_win.h */ include/falcon/garbageable.h000066400000000000000000000054321176363201700163420ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: garbageable.h Garbageable interface definition ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven dic 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Garbageable interface. This file contains the interface for objects and classes that can be subject of garbaging (i.e. because they can be inserted into items) and other utility definition for garbage collecting process. */ #ifndef flc_garbageable_H #define flc_garbageable_H #include #include #include namespace Falcon { class FALCON_DYN_CLASS GarbageableBase: public GCAlloc { protected: GarbageableBase *m_garbage_next; GarbageableBase *m_garbage_prev; mutable uint32 m_gcStatus; public: GarbageableBase() {} /** Copy constructor. */ GarbageableBase( const GarbageableBase &other ); virtual ~GarbageableBase(); /** Performs pre-delete finalization of the object. If this function returns false, then the destructor is called. If it returns true, it means that the finalizer has somewhat reclaimed the memory in a clean way (i.e. deleting itself), so the delete on this garbageable won't be called. \return true to prevent destructor to be applied on this garbageable. */ virtual bool finalize(); /** Returns an estimation of the size occupied by this object in memory. The final GC size is determined by an heuristic algorithm allocating part of the allocated space to the items returning 0 from this call (the default), taking away all the memory declared by items not returning 0. */ virtual uint32 occupation(); void mark( uint32 gen ) const { m_gcStatus = gen; } /** Return the current GC mark status. */ uint32 mark() const { return m_gcStatus; } GarbageableBase *nextGarbage() const { return m_garbage_next; } GarbageableBase *prevGarbage() const { return m_garbage_prev; } void nextGarbage( GarbageableBase *next ) { m_garbage_next = next; } void prevGarbage( GarbageableBase *prev ) { m_garbage_prev = prev; } }; class FALCON_DYN_CLASS Garbageable: public GarbageableBase { public: Garbageable(); /** Copy constructor. */ Garbageable( const Garbageable &other ); virtual ~Garbageable(); /** Applies mark to subclasses. * By default, this method just changes the mark() value. * * Subclasses having deep data may overload this to take care * of marking it. */ virtual void gcMark( uint32 mk ); }; } #endif /* end of garbageable.h */ include/falcon/garbagelock.h000066400000000000000000000046621176363201700163730ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: garbagelock.h Garbage lock - safeguards for items in VMs. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 19 Mar 2009 19:59:27 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_GARBAGELOCK_H #define FALCON_GARBAGELOCK_H #include #include #include #include namespace Falcon { class MemPool; /* Notice that this class is implemented in mempool.cpp */ /** Protects an item from garbage collecting. This class safely store an item, and all its contents in case it is a sort of container, from being collected. The garbage locked items are marked as soon as they reach the lowest possible value in the live generation count. Extensions are granted to have their ::gcMark() method called when this happens, so any kind of item can be safely stored as the item() element. Once destroyed, the garbage lock releases the item, that can then be immediately collected. */ class FALCON_DYN_CLASS GarbageLock: public BaseAlloc { GarbageLock *m_garbage_next; GarbageLock *m_garbage_prev; Item m_item; friend class MemPool; GarbageLock *next() const { return m_garbage_next; } GarbageLock *prev() const { return m_garbage_prev; } void next( GarbageLock *next ) { m_garbage_next = next; } void prev( GarbageLock *prev ) { m_garbage_prev = prev; } // Constructor used to initialize the gclock ring GarbageLock( bool ); public: /** Creates an empty garbage lock. The item inside this garbage lock is nil. */ GarbageLock(); /** Creates a garbage lock protecting an item. */ GarbageLock( const Item &itm ); /** Releases the item that can now be collected. */ ~GarbageLock(); /** Return the guarded item (const version). */ const Item &item() const { return m_item; } /** Return the guarded item. The returned value can be modified. For example, setting it to nil or to another flat value will cause the previously guarded value to be released, and collectible for garbage. */ Item &item() { return m_item; } }; /* Notice that this class is implemented in mempool.cpp */ } #endif /* end of garbagelock.h */ include/falcon/garbagepointer.h000066400000000000000000000037651176363201700171260ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: garbagepointer.h Poiter that can be used to automatically dispose inner FalconData items. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven ott 15 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Poiter that can be used to automatically dispose inner FalconData items. */ #ifndef FALCON_GARBAGE_POINTER_H #define FALCON_GARBAGE_POINTER_H #include #include namespace Falcon { class VMachine; /** Implements a generic garbage shell for inner data. This pointer can be used to wrap FalconData derived classes into garbage sensible behavior. In this way, it's possible to bless simple pointers to FalconData managed internally by VM or inner routines, and let them to live in the wild. They will be marked when reachable and disposed cleanly when not reachable anymore. GarbagePointer can be set directly into items (they are the "user pointer" items). */ class FALCON_DYN_CLASS GarbagePointer: public Garbageable { FalconData *m_ptr; public: /** Creates the garbage pointer. Must be filled with the data guarded falcon data */ GarbagePointer( FalconData *p ): Garbageable(), m_ptr(p) { if ( p->isSequence() ) static_cast(p)->owner( this ); } /** Destructor. The guard will destroy its content with it. */ virtual ~GarbagePointer() {} virtual bool finalize() { delete m_ptr; return false; } /** Returns the inner data stored in this garbage pointer. */ FalconData *ptr() const { return m_ptr; } virtual void gcMark( uint32 gen ) { if( mark() != gen ) { mark( gen ); m_ptr->gcMark( gen ); } } }; } #endif /* end of garbagepointer.h */ include/falcon/gcalloc.h000066400000000000000000000014701176363201700155300ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: gcalloc.h Base allocation declaration for engine classes ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 01 Mar 2009 14:17:48 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Base allocation declaration for engine classes */ #ifndef flc_gcalloc_H #define flc_gcalloc_H #include #include // for size_t declaration namespace Falcon { class FALCON_DYN_CLASS GCAlloc { public: void *operator new( size_t size ); void operator delete( void *mem, size_t size ); }; } #endif /* end of gcalloc.h */ include/falcon/gencode.h000066400000000000000000000272021176363201700155310ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: gencode.h Generates a compiler-debug oriented representation of the input symtree. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab giu 5 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_GENCODE_H #define FALCON_GENCODE_H #include #include #include #include #include namespace Falcon { class LineMap; class FALCON_DYN_CLASS GenCode: public Generator { typedef enum { e_parND, e_parNIL, e_parUND, e_parVAL, e_parVAR, e_parINT32, e_parSYM, e_parA, e_parB, e_parS1, e_parL1, e_parL2, e_parLBIND, e_parTRUE, e_parFALSE, e_parNTD32, e_parNTD64, e_parSTRID } t_paramType; class c_jmptag { /* Varpar and jmptag are never newed, so they don't need operator new */ uint32 m_defs[4]; List m_queries[4]; GenericVector m_elifDefs; GenericVector m_elifQueries; uint32 m_tries; uint32 m_offset; bool m_bIsForLast; const StmtForin* m_ForinLoop; Stream *m_stream; uint32 addQuery( uint32 id, uint32 pos ); void define( uint32 id ); public: c_jmptag( Stream *stream, uint32 offset = 0 ); uint32 addQueryBegin( uint32 blocks=1 ) { return addQuery( 0, blocks ); } uint32 addQueryEnd( uint32 blocks=1 ) { return addQuery( 1, blocks ); } uint32 addQueryNext( uint32 blocks=1 ) { return addQuery( 2, blocks ); } uint32 addQueryPostEnd( uint32 blocks=1 ) { return addQuery( 3, blocks ); } void defineBegin() { define( 0 ); } void defineEnd() { define( 1 ); } void defineNext() { define( 2 ); } void definePostEnd() { define( 3 ); } uint32 addQueryIfElse( uint32 blocks=1) { return addQuery( 0, blocks ); } uint32 addQueryIfEnd( uint32 blocks=1 ) { return addQuery( 1, blocks ); } uint32 addQueryElif( uint32 elifID, uint32 blocks=1 ); uint32 addQuerySwitchBlock( uint32 blockID, uint32 blocks=1 ) { return addQueryElif( blockID, blocks ); } void defineIfElse() { define( 0 ); } void defineIfEnd() { define( 1 ); } void defineElif( uint32 id ); void defineSwitchCase( uint32 id ) { defineElif( id ); } void addTry( uint32 count = 1 ) { m_tries += count; } void removeTry( uint32 count = 1 ) { m_tries -= count; } uint32 tryCount() const { return m_tries; } bool isForIn() const { return m_ForinLoop != 0; } const StmtForin* ForinLoop() const { return m_ForinLoop; } void setForIn( const StmtForin* forin ) { m_ForinLoop = forin; } void setForLast( bool l ) { m_bIsForLast = l; } bool isForLast() const { return m_bIsForLast; } }; class c_varpar { public: /* Varpar and jmptag are never newed, so they don't need operator new */ t_paramType m_type; union { const Value *value; const VarDef *vd; const Symbol *sym; int32 immediate; int64 immediate64; } m_content; c_varpar(): m_type( e_parND ) {} c_varpar( t_paramType t ): m_type( t ) {} explicit c_varpar( bool val ): m_type( val ? e_parTRUE : e_parFALSE ) {} c_varpar( const Value *val ): m_type( e_parVAL ) { if ( val->isNil() ) m_type = e_parNIL; else if( val->isUnbound() ) m_type = e_parUND; else m_content.value = val; } c_varpar( const VarDef *vd ): m_type( e_parVAR ) { if ( vd->isNil() ) m_type = e_parNIL; else m_content.vd = vd; } c_varpar( const Symbol *sym ): m_type( e_parSYM ) { m_content.sym = sym; } c_varpar( const int32 immediate ): m_type( e_parINT32 ) { m_content.immediate = immediate; } c_varpar( const c_varpar &other ): m_type( other.m_type ), m_content( other.m_content ) {} void generate( GenCode *owner ) const; }; friend class c_varpar; c_varpar c_param_fixed( uint32 num ) { c_varpar ret( e_parNTD32 ); ret.m_content.immediate = num; return ret; } c_varpar c_param_str( uint32 num ) { c_varpar ret( e_parSTRID ); ret.m_content.immediate = num; return ret; } void gen_pcode( byte pcode ) { gen_pcode( pcode, e_parND, e_parND, e_parND ); } void gen_pcode( byte pcode, const c_varpar &first ) { gen_pcode( pcode, first, e_parND, e_parND ); } void gen_pcode( byte pcode, const c_varpar &first, const c_varpar &second ) { gen_pcode( pcode, first, second, e_parND ); } void gen_pcode( byte pcode, const c_varpar &first, const c_varpar &second, const c_varpar &third ); byte gen_pdef( const c_varpar &elem ); void gen_var( const VarDef &def ); /* Pushes a label with a certain displacement. This function marks the position of a label in the next N int32 blocks, where N is the parameter. Usually, the jump or similar instruction has the jump target in the first int32 parameter, so the default for the param is 1. In case the position of the label target cannot be determined in advance, output operations must be perfomed manually, but at the moment all the functions with jump targets have only one or more int32 parmeters. \param displacement the distance in int32 block from current write position where label value must be written. */ /** Writes a previously recorede label. This pops the label definition from the stack, writes the current file position and moves the file pointer back to where it was. This is the only operation causing write pointer to move (except for write). */ void pop_label(); void gen_function( const StmtFunction *func ); void gen_block( const StatementList *slist ); void gen_statement( const Statement *stmt ); void gen_condition( const Value *stmt, int mode=-1 ); void gen_value( const Value *stmt ); typedef enum { l_value, p_value, v_value } t_valType; /** Generates a complex value. Complex values are divided in two classes; expressions and structure constructions. Structure construction are namely: - range generations - array generation - dictionary generation - symbol reference generation Expresions are themselves divided into two kind; the ones that returns a "volatile" value, called l-value, and the ones that refer to an assignable, persistent location, called x-value. Namely, x-values are divided into p-values (expressions terminating with a property accesor) or v-value ( expression terminating with a sequence accessor). Some l-values: \code (a+1) functionCall() (a + b * c) var[ accessor ]() obj.property() obj.property[5].otherProp[2] + 1 \endcode Some p-values: \code obj.property ( #"indirect"() ).property obj.property++ // we still refer to obj.property \endcode Some v-values: \code var[ accessorGenerator() ] obj.property[ x ] --var[5] // we still refer to var[5] \endcode This distintion must be known, as assignments to l-values should be taken with care; for now, we do that and we assign to the A register. Conversely, assignment and auto-expressions on x-values is common. Instead of analyzing the structure of the expression, we let the underlying calls to the expression generators to determine if they are unrolling an l-value or an x-value. The parameter x_value is initially false; it gets changed to true if the last expanded expression element is an accessor. Operators ++ and -- don't change the character of the value, and all the others reset the character to l-value. \param value The value to be generated. \param x_value At exit, returns characteristic of the value. */ void gen_complex_value( const Value *value, t_valType &x_value ); /** Non l-value sensible version of complex value generator. Some operations do not require to know if the value generated is an l-value expression or not. They may use this version instead. \param value The value to be generated. */ void gen_complex_value( const Value *value ) { t_valType dummy; gen_complex_value( value, dummy ); } /** Generate an expression. Expressions are the most common complex value. See gen_complex_value for a description. \param expr The expression to be generated. \param x_value On exit, characteristic value type of the expression. \see gen_complex_value */ void gen_expression( const Expression *expr, t_valType &x_value ); /** Generate an expression without taking its l-value characteristics. \param expr The expression to be generated. */ void gen_expression( const Expression *expr ) { t_valType dummy; gen_expression( expr, dummy ); } void gen_dict_decl( const DictDecl *stmt ); void gen_array_decl( const ArrayDecl *stmt ); void gen_range_decl( const RangeDecl *stmt ); /** Geneare a push instruction based on a source value. Push is a kinda tricky instruction. Theoretically, if the value holds a reference taking expression, one can LDRF on the target and then push A, but PSHR is provided to do this in just one step. Hence the need of a specialized push generator that is a little smarter than the usual gen_value. */ void gen_push( const Value *val ); /** Generate a load instruction. LD is a kinda tricky instruction. Theoretically, if the source value holds a reference taking expression, one can LDRF on the target and then load A into the source but LDRF is provided to do this in just one step. Hence the need of a specialized load generator that is a little smarter. */ void gen_load( const Value *target, const Value *source ); void gen_store_to_deep( byte type, const Value *source, const Value *first, const Value *second ); void gen_inc_prefix( const Value *target ); void gen_dec_prefix( const Value *target ); void gen_inc_postfix( const Value *target ); void gen_dec_postfix( const Value *target ); void gen_autoassign( byte opcode, const Value *target, const Value *source ); void gen_store_to_deep_A( byte type, const Value *first, const Value *second ); void gen_store_to_deep_reg( byte type, const Value *first, const Value *second, t_paramType reg ); void gen_load_from_deep( byte type, const Value *first, const Value *second ); void gen_load_from_A( const Value *target ); void gen_load_from_reg( const Value *target, t_paramType reg ); int gen_refArray( const ArrayDecl *tarr, bool bGenArray ); void gen_operand( const Value *stmt ); /** Generates a function call from an expression. */ void gen_funcall( const Expression *call, bool fork=false ); List m_labels; List m_labels_loop; /** Current context of functions, needed i.e. for states. */ List m_functions; uint32 m_pc; StringStream *m_outTemp; Module *m_module; public: GenCode( Module *mod ); virtual ~GenCode(); virtual void generate( const SourceTree *st ); }; } #endif /* end of gencode.h */ include/falcon/generator.h000066400000000000000000000021441176363201700161110ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: generator.h Code genartor base class. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab giu 5 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_GENERATOR_H #define FALCON_GENERATOR_H #include #include namespace Falcon { class Stream; class Module; class SourceTree; class FALCON_DYN_CLASS Generator: public BaseAlloc { protected: Stream *m_out; public: /** Creates the generator. Although a stream is provided as a parameter, ownership is not taken. The stream is still open and available after generator destruction. \param out the stream where output of this generator will be sent. */ Generator( Stream *out ): m_out( out ) {} virtual ~Generator() {} virtual void generate( const SourceTree *st ) = 0; }; } #endif /* end of generator.h */ include/falcon/generatorseq.h000066400000000000000000000043561176363201700166310ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: generatorseq.h Virtual sequence that can be used to iterate over generators. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 09 Aug 2009 19:04:17 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_GENERATORSEQ_H #define FALCON_GENERATORSEQ_H #include #include #include namespace Falcon { class FALCON_DYN_CLASS GeneratorSeq: public Sequence { VMachine* m_vm; Item m_callable; mutable Item m_cache_cur; mutable Item m_cache_next; mutable bool m_bHasCachedCur; mutable bool m_bHasCachedNext; mutable bool m_bComplete; bool fillCurrentValue() const; bool fillNextValue() const; public: GeneratorSeq( VMachine* runEvn, const Item& callable ); GeneratorSeq( const GeneratorSeq& other ); virtual ~GeneratorSeq(); virtual const Item &front() const; virtual const Item &back() const; virtual void clear(); virtual bool empty() const; virtual void append( const Item &data ); virtual void prepend( const Item &data ); virtual GeneratorSeq* clone() const; virtual void gcMark( uint32 gen ); //============================================================== // Iterator management // protected: virtual void getIterator( Iterator& tgt, bool tail = false ) const; virtual void copyIterator( Iterator& tgt, const Iterator& source ) const; virtual void insert( Iterator &iter, const Item &data ); virtual void erase( Iterator &iter ); virtual bool hasNext( const Iterator &iter ) const; virtual bool hasPrev( const Iterator &iter ) const; virtual bool hasCurrent( const Iterator &iter ) const; virtual bool next( Iterator &iter ) const; virtual bool prev( Iterator &iter ) const; virtual Item& getCurrent( const Iterator &iter ); virtual Item& getCurrentKey( const Iterator &iter ); virtual bool equalIterator( const Iterator &first, const Iterator &second ) const; }; } #endif /* FALCON_GENERATORSEQ_H */ /* end of generatorseq.h */ include/falcon/genericlist.h000066400000000000000000000060511176363201700164340ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: genlist.h Generic list - a list holding generic values. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom ott 15 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file a list holding generic values. */ #ifndef flc_genlist_H #define flc_genlist_H #include #include #include #include namespace Falcon { /** Generic list element. */ class FALCON_DYN_CLASS ListElement: public BaseAlloc { ListElement *m_next; ListElement *m_previous; union { const void *m_data; uint32 m_iData; } dt; ListElement( const void *data ) { dt.m_data = data; } ListElement( uint32 d ) { dt.m_iData = d; } void data( const void *data ) { dt.m_data = data; } void prev( ListElement *elem ) { m_previous = elem; } void next( ListElement *elem ) { m_next = elem; } friend class List; public: const void *data() const { return dt.m_data; } uint32 iData() const { return dt.m_iData; } void iData( uint32 d ) { dt.m_iData = d; } ListElement *next() const { return m_next; } ListElement *prev() const { return m_previous; } }; /** Generic list. */ class FALCON_DYN_CLASS List: public BaseAlloc { ListElement *m_head; ListElement *m_tail; uint32 m_size; void (*m_deletor)( void *); friend class ListTraits; public: List(): m_head(0), m_tail(0), m_size(0), m_deletor(0) { } List( void (*deletor)(void *) ): m_head(0), m_tail(0), m_size(0), m_deletor( deletor ) { } ~List() { clear(); } ListElement *begin() const { return m_head; } ListElement *end() const { return m_tail; } const void *front() const { return m_head->data(); } const void *back() const { return m_tail->data(); } bool empty() const { return m_head == 0; } void pushFront( const void *data ); void pushBack( const void *data ); void pushFront( uint32 data ); void pushBack( uint32 data ); void popFront(); void popBack(); void insertAfter( ListElement *position, const void *data ); void insertBefore( ListElement *position, const void *data ); ListElement *erase( ListElement *position ); uint32 size() const {return m_size;} void clear(); void deletor( void (*del)( void * ) ) { m_deletor = del; } }; class ListTraits: public ElementTraits { public: virtual ~ListTraits() {} virtual uint32 memSize() const; virtual void init( void *itemZone ) const; virtual void copy( void *targetZone, const void *sourceZone ) const; virtual int compare( const void *first, const void *second ) const; virtual void destroy( void *item ) const; virtual bool owning() const; }; namespace traits { extern FALCON_DYN_SYM ListTraits &t_List(); } } #endif /* end of genlist.h */ include/falcon/genericmap.h000066400000000000000000000120021176363201700162270ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: genericmap.h Generic map - a map holding generic values. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun ago 23 21:55:38 CEST 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef flc_genericmap_h #define flc_genericmap_h #include #include #include #include namespace Falcon { typedef struct tag_MapPage { uint16 m_count; uint16 m_allocated; // not used by now uint16 m_parentElement; // position of the parent element in the parent page uint16 m_dummy; // filler tag_MapPage *m_parent; tag_MapPage *m_higher; } MAP_PAGE; class MapIterator; /** Generic Map. This implements a generic map, whose keys and values are of an undetermined type and size. The map is implemented as a B-tree. \note The structure forces 4-bytes alignment, so keys and values may be classes instances provided the target architecture supports 4-bytes aligned items. */ class FALCON_DYN_CLASS Map: public BaseAlloc { ElementTraits *m_keyTraits; ElementTraits *m_valueTraits; uint16 m_treeOrder; uint16 m_keySize; uint16 m_valueSize; uint32 m_size; MAP_PAGE *m_treeTop; friend class MapIterator; MAP_PAGE *allocPage() const; MAP_PAGE **ptrsOfPage( const MAP_PAGE *ptr ) const; void *keysOfPage( const MAP_PAGE *ptr ) const; void *valuesOfPage( const MAP_PAGE *ptr ) const; MAP_PAGE *ptrInPage( const MAP_PAGE *ptr, uint16 count ) const; void *keyInPage( const MAP_PAGE *ptr, uint16 count ) const; void *valueInPage( const MAP_PAGE *ptr, uint16 count ) const ; bool subFind( const void *key, MapIterator &iter, MAP_PAGE *page ) const; bool scanPage( const void *key, MAP_PAGE *currentPage, uint16 count, uint16 &pos ) const; void insertSpaceInPage( MAP_PAGE *page, uint16 pos ); void removeSpaceFromPage( MAP_PAGE *page, uint16 pos ); void splitPage( MAP_PAGE *page ); void rebalanceNode( MAP_PAGE *page, MapIterator *scanner = 0 ); void reshapeChildPointers( MAP_PAGE *page, uint16 startFrom = 0 ); MAP_PAGE *getRightSibling( const MAP_PAGE *page ) const; MAP_PAGE *getLeftSibling( const MAP_PAGE *page ) const; public: Map( ElementTraits *keyt, ElementTraits *valuet, uint16 order = 33 ); ~Map(); void destroyPage( MAP_PAGE *page ); bool insert( const void *key, const void *value); bool erase( const void *key ); MapIterator erase( const MapIterator &iter ); void *find(const void *key ) const; /** Finds a value or the nearest value possible. If the value is found, the function returns true; If it's not found, the function returns false and the iterator points to the smallest item greater than the given key (so that an insert would place the key in the correct position). */ bool find( const void *key, MapIterator &iter )const ; MapIterator begin() const; MapIterator end() const; bool empty() const { return m_size == 0; } void clear(); uint32 size() const { return m_size; } uint16 order() const { return m_treeOrder; } }; class FALCON_DYN_CLASS MapIterator: public BaseAlloc { const Map *m_map; MAP_PAGE *m_page; uint16 m_pagePosition; friend class Map; public: MapIterator() {} MapIterator( const Map *m, MAP_PAGE *p, uint16 ppos): m_map(m), m_page( p ), m_pagePosition( ppos ) {} MapIterator( const MapIterator &other ) { m_map = other.m_map; m_page = other.m_page; m_pagePosition = other.m_pagePosition; } bool next(); bool prev(); bool hasCurrent() const { return m_page != 0 && m_page->m_count > m_pagePosition; } bool hasNext() const; bool hasPrev() const; void *currentKey() const { return m_map->keyInPage( m_page, m_pagePosition ); } void *currentValue() const { return m_map->valueInPage( m_page, m_pagePosition ); } void currentValue( void* source ) { m_map->m_valueTraits->copy( m_map->valueInPage( m_page, m_pagePosition ), source ); } bool equal( const MapIterator &other ) const; }; class MapPtrTraits: public ElementTraits { public: virtual ~MapPtrTraits() {} virtual uint32 memSize() const; virtual void init( void *itemZone ) const; virtual void copy( void *targetZone, const void *sourceZone ) const; virtual int compare( const void *first, const void *second ) const; virtual void destroy( void *item ) const; virtual bool owning() const; }; class MapPtrOwnTraits: public MapPtrTraits { public: virtual ~MapPtrOwnTraits() {} virtual void destroy( void *item ) const; virtual bool owning() const; }; namespace traits { extern FALCON_DYN_SYM MapPtrTraits &t_MapPtr(); extern FALCON_DYN_SYM MapPtrOwnTraits &t_MapPtrOwn(); } } #endif /* end of genericmap.h */ include/falcon/genericvector.h000066400000000000000000000036501176363201700167650ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: genericvector.h Generic vector - a generic vector of elements ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven oct 27 11:02:00 CEST 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef flc_genericvector_h #define flc_genericvector_h #include #include #include namespace Falcon { /** Generic typeless vector. The allocated size of a generic vector is always the needed size + 1. This is because if you need to push a pointer from the same vector, you can push it and THEN reallocate it. */ class FALCON_DYN_CLASS GenericVector: public BaseAlloc { byte *m_data; uint32 m_size; uint32 m_allocated; uint32 m_threshold_size; typedef enum { alloc_block = 32 } consts; protected: uint32 m_itemSize; const ElementTraits *m_traits; GenericVector(): m_data(0), m_size(0), m_allocated(0), m_threshold_size(0) {} void init( const ElementTraits *traits, uint32 prealloc ); public: GenericVector( const ElementTraits *traits, uint32 prealloc=0 ); ~GenericVector(); void insert( void *data, uint32 pos ); bool remove( uint32 pos ); void *at( uint32 pos ) const { return m_data + ( pos * m_itemSize ); } void set( void *data, uint32 pos ); void push( void *data ); void pop() { m_size --; } void *top() const { return m_data + ( (m_itemSize) * (m_size-1) ); } void reserve( uint32 s ); void resize( uint32 s ); void threshHold( uint32 size ) { m_threshold_size = size; } uint32 threshHold() const { return m_threshold_size; } uint32 size() const { return m_size; } bool empty() const { return m_size == 0; } }; } #endif /* end of genericvector.h */ include/falcon/genhasm.h000066400000000000000000000155001176363201700155450ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: genhasm.h Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab giu 5 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_GENHSAM_H #define FALCON_GENHSAM_H #include #include #include namespace Falcon { class FALCON_DYN_CLASS GenHAsm: public Generator { void gen_depTable( const Module *mod ); void gen_stringTable( const Module *mod ); void gen_symbolTable( const Module *mod ); void gen_function( const StmtFunction *func ); void gen_class( const StmtClass *cls ); void gen_propdef( const VarDef &def ); void gen_block( const StatementList *slist ); void gen_statement( const Statement *stmt ); void gen_condition( const Value *stmt, int mode=0 ); void gen_value( const Value *stmt, const char *prefix = 0, const char *cpl_post = 0 ); void gen_operand( const Value *stmt ); typedef enum { l_value, p_value, v_value } t_valType; /** Generates a complex value. Complex values are divided in two classes; expressions and structure constructions. Structure construction are namely: - range generations - array generation - dictionary generation - symbol reference generation Expresions are themselves divided into two kind; the ones that returns a "volatile" value, called l-value, and the ones that refer to an assignable, persistent location, called x-value. Namely, x-values are divided into p-values (expressions terminating with a property accesor) or v-value ( expression terminating with a sequence accessor). Some l-values: \code (a+1) functionCall() (a + b * c) var[ accessor ]() obj.property() obj.property[5].otherProp[2] + 1 \endcode Some p-values: \code obj.property ( #"indirect"() ).property obj.property++ // we still refer to obj.property \endcode Some v-values: \code var[ accessorGenerator() ] obj.property[ x ] --var[5] // we still refer to var[5] \endcode This distintion must be known, as assignments to l-values should be taken with care; for now, we do that and we assign to the A register. Conversely, assignment and auto-expressions on x-values is common. Instead of analyzing the structure of the expression, we let the underlying calls to the expression generators to determine if they are unrolling an l-value or an x-value. The parameter x_value is initially false; it gets changed to true if the last expanded expression element is an accessor. Operators ++ and -- don't change the character of the value, and all the others reset the character to l-value. \param value The value to be generated. \param x_value At exit, returns characteristic of the value. */ void gen_complex_value( const Value *value, t_valType &x_value ); /** Non l-value sensible version of complex value generator. Some operations do not require to know if the value generated is an l-value expression or not. They may use this version instead. \param value The value to be generated. */ void gen_complex_value( const Value *value ) { t_valType dummy; gen_complex_value( value, dummy ); } /** Generate an expression. Expressions are the most common complex value. See gen_complex_value for a description. \param expr The expression to be generated. \param x_value On exit, characteristic value type of the expression. \see gen_complex_value */ void gen_expression( const Expression *expr, t_valType &x_value ); /** Generate an expression without taking its l-value characteristics. \param expr The expression to be generated. */ void gen_expression( const Expression *expr ) { t_valType dummy; gen_expression( expr, dummy ); } void gen_dict_decl( const DictDecl *stmt ); void gen_array_decl( const ArrayDecl *stmt ); void gen_range_decl( const RangeDecl *stmt ); /** Geneare a push instruction based on a source value. Push is a kinda tricky instruction. Theoretically, if the value holds a reference taking expression, one can LDRF on the target and then push A, but PSHR is provided to do this in just one step. Hence the need of a specialized push generator that is a little smarter than the usual gen_value. */ void gen_push( const Value *val ); /** Generate a load instruction. LD is a kinda tricky instruction. Theoretically, if the source value holds a reference taking expression, one can LDRF on the target and then load A into the source but LDRF is provided to do this in just one step. Hence the need of a specialized load generator that is a little smarter. */ void gen_load( const Value *target, const Value *source ); //void gen_load( const Value *target, const char *source ); void gen_store_to_deep( const char *type, const Value *source, const Value *first, const Value *second ); void gen_inc_prefix( const Value *target ); void gen_dec_prefix( const Value *target ); void gen_inc_postfix( const Value *target ); void gen_dec_postfix( const Value *target ); void gen_autoassign( const char *op, const Value *target, const Value *source ); void gen_store_to_deep_A( const char *type, const Value *first, const Value *second ); void gen_store_to_deep_reg( const char *type, const Value *first, const Value *second, const char *reg ); void gen_load_from_deep( const char *type, const Value *first, const Value *second ); void gen_load_from_A( const Value *target ); void gen_load_from_reg( const Value *target, const char *reg ); int gen_refArray( const ArrayDecl *tarr, bool bGenArray ); /** Writes a set of cases. */ void dump_cases( int branch, const MapIterator &begin ); /** Generates a function call from an expression. */ void gen_funcall( const Expression *call, bool fork=false ); int m_branch_id; int m_loop_id; int m_try_id; List m_branches; List m_loops; List m_trys; class LoopInfo { public: LoopInfo( int id, const Statement* l ): m_loop( l ), m_id( id ), m_isForLast( false ) {} const Statement* m_loop; int m_id; bool m_isForLast; }; /** Current context of functions, needed i.e. for states. */ List m_functions; public: GenHAsm( Stream *out ); virtual void generate( const SourceTree *st ); void generatePrologue( const Module *module ); }; } #endif /* end of genhasm.h */ include/falcon/gentree.h000066400000000000000000000023351176363201700155560ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: gentree.h Generates a compiler-debug oriented representation of the input symtree. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab giu 5 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_GENTREE_H #define FALCON_GENTREE_H #include #include namespace Falcon { class Statement; class Value; class StatementList; class Expression; class ArrayDecl; class DictDecl; class FALCON_DYN_CLASS GenTree: public Generator { void generate( const Statement *cmp, const char *spec=0, bool sameline = false, int depth=0 ); void gen_value( const Value *val ); void gen_block( const StatementList &blk, int depth, const char *prefix=0 ); void gen_expression( const Expression *exp ); void gen_array( const ArrayDecl *exp ); void gen_dict( const DictDecl *ad ); public: GenTree( Stream *out ): Generator( out ) {} virtual void generate( const SourceTree *st ); }; } #endif /* end of gentree.h */ include/falcon/globals.h000066400000000000000000000074511176363201700155540ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: globals.h Engine-wide exported variables and global functions. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 02 Mar 2009 20:19:00 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_GLOBALS_H #define FALCON_GLOBALS_H #include #include #include // for size_t namespace Falcon { class MemPool; class StringTable; class VFSProvider; class String; class ModuleCache; FALCON_DYN_SYM extern void * (*memAlloc) ( size_t ); FALCON_DYN_SYM extern void (*memFree) ( void * ); FALCON_DYN_SYM extern void * (*memRealloc) ( void *, size_t ); FALCON_DYN_SYM extern void * (*gcAlloc) ( size_t ); FALCON_DYN_SYM extern void (*gcFree) ( void * ); FALCON_DYN_SYM extern void * (*gcRealloc) ( void *, size_t ); FALCON_DYN_SYM extern MemPool *memPool; FALCON_DYN_SYM extern StringTable *engineStrings; class MemPool; namespace Engine { FALCON_DYN_SYM void Init(); FALCON_DYN_SYM void PerformGC(); FALCON_DYN_SYM void Shutdown(); /** Utility function recording the preferential encodings for sources and VM I/O. When the engine has to create its own VMs and streams, i.e. to fulfil interactive compiler requests, it uses this encodings that can be defined and changed by the application at any time. The values can be used also by the calling application as a convenient inter-moule communication area for this critical aspect of preferential encoding. \param sSrcEnc The encoding preferentially used by source files. \param sIOEnc The encoding preferentially used in I/O streams different from sources. */ FALCON_DYN_SYM void setEncodings( const String &sSrcEnc, const String &sIOEnc ); /** Utility function recording the preferential encodings for sources and VM I/O. \see recordEncodings \param sSrcEnc The encoding preferentially used by source files. \param sIOEnc The encoding preferentially used in I/O streams different from sources. */ FALCON_DYN_SYM void getEncodings( String &sSrcEnc, String &sIOEnc ); /** Utility function that adds a VFSProvider to the engine. The engine takes ownership of the instance so Don't add the same instance more than once! \param protocol The protocol which this VFSProvider responds to \param prv The provider that should be added */ FALCON_DYN_SYM bool addVFS( const String &protocol, VFSProvider *prv ); FALCON_DYN_SYM bool addVFS( VFSProvider *prv ); FALCON_DYN_SYM VFSProvider* getVFS( const String &name ); FALCON_DYN_SYM const String &getMessage( uint32 id ); FALCON_DYN_SYM bool setTable( StringTable *tab ); FALCON_DYN_SYM bool setLanguage( const String &language ); /** Set application wide search path. This is used by default in new VMs, module loaders and metacompilers. */ FALCON_DYN_SYM void setSearchPath( const String &path ); /** Returns the application-wide default search path by copy. */ FALCON_DYN_SYM String getSearchPath(); /** Return global setting for automatic conversion from windows paths */ FALCON_DYN_SYM bool getWindowsNamesConversion(); /** Changes global setting for automatic conversion from windows paths */ FALCON_DYN_SYM void setWindowsNamesConversion( bool s ); /** Turn on automatic module caching. */ FALCON_DYN_SYM void cacheModules( bool tmode ); /** Public module cache. */ FALCON_DYN_SYM ModuleCache* getModuleCache(); class AutoInit { public: AutoInit() { Init(); } ~AutoInit() { PerformGC(); Shutdown(); } }; } } #endif /* end of globals.h */ include/falcon/gpage.h000066400000000000000000000021341176363201700152050ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_itempage.h Definition of the page that holds garbageable data pointers. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun ott 4 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Definition of the page that holds garbageable data pointers. */ #ifndef flc_flc_gpage_H #define flc_flc_gpage_H #include #include namespace Falcon { /** Garbageable pointer page. \note This is old code to be removed. A segregated allocation page holding references to items that may get garbaged if not marked. */ class GarbagePage: public SegregatedPage< Garbageable * > { public: GarbagePage( GarbagePage *prev = 0, GarbagePage *next = 0 ): SegregatedPage< Garbageable * >( prev, next ) {} }; class GPageList: public LinkedList< GarbagePage > {}; } #endif /* end of flc_gpage.h */ include/falcon/heap.h000066400000000000000000000024631176363201700150440ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: heap.h ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 13 Dec 2008 13:45:59 +0100 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_HEAP_H #define FALCON_HEAP_H namespace Falcon { namespace Sys { int sys_pageSize(); void* sys_allocPage(); void sys_freePage( void *page ); } /** HeapMem class. This class allows to manage direct memory pages from the system. it's the base of the global SBA (Small Block Allocator). The engine creates a singleton instance of this class called Heap. All its functions are inlined to system specific sys_* function; this means that release build won't actually access the this-> pointer, and calling the methods of the singleton Heap will be exactly as calling system specific functions. */ class HeapMem { public: HeapMem(); ~HeapMem(); int pageSize() { return Sys::sys_pageSize(); } void *allocPage() { return Sys::sys_allocPage(); } void freePage( void *page ) { Sys::sys_freePage( page ); } }; extern HeapMem Heap; } #endif /* end of heap.h */ include/falcon/heap_linux.h000066400000000000000000000031161176363201700162570ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_heap_linux.h Base class for heap management. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer set 29 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Heap memory for linux. */ #ifndef flc_HEAP_LINUX_H #define flc_HEAP_LINUX_H #include #include /** Page size. \todo add a configure system to put the page size in the config.h */ #define PAGE_SIZE 4096 namespace Falcon { class HeapMem_Linux { static long m_pageSize; public: /* static void init() { if ( m_pageSize == 0 ) { m_pageSize = sysconf( _SC_PAGESIZE ); } } static void uninit() {} */ static void *getPage() { return getPages(1); } static void *getPages( int pages ) { //fassert( m_pageSize != 0 ); void *ret = mmap(((void *)0), pages * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); fassert( ret != MAP_FAILED ); return ret; } static void freePage( void *memory ) { free( memory, 1 ); } // free one page static void free( void *memory, int pages ) { //fassert( m_pageSize != 0 ); munmap( memory, pages * PAGE_SIZE ); } //static long pageSize() { return m_pageSize; } static long pageSize() { return PAGE_SIZE; } }; typedef HeapMem_Linux HeapMem; } #endif /* end of flc_heap_linux.h */ include/falcon/heap_win.h000066400000000000000000000030061176363201700157130ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_heap_win.h Windows specific class for Dynamic load system ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2004-11-1 02:34+0200UTC ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef flc_HEAP_WIN_H #define flc_HEAP_WIN_H #include /** Page size. \todo add a configure system to put the page size in the config.h */ #define PAGE_SIZE 4096 #include namespace Falcon { class HeapMem_Win32 { static long m_pageSize; static HANDLE m_heapHandle; public: /* static void init() { } static void uninit() {} */ static void *getPage() { return getPages(1); } static void *getPages( int pages ) { if ( m_heapHandle == 0 ) m_heapHandle = GetProcessHeap(); void *ret = HeapAlloc( m_heapHandle, HEAP_NO_SERIALIZE, pages * PAGE_SIZE ); fassert( ret != 0 ); return ret; } static void freePage( void *memory ) { free( memory, 1 ); } // free one page static void free( void *memory, int pages ) { fassert( m_heapHandle != 0 ); HeapFree( m_heapHandle, HEAP_NO_SERIALIZE, memory ); } //static long pageSize() { return m_pageSize; } static long pageSize() { return PAGE_SIZE; } }; typedef HeapMem_Win32 HeapMem; } #endif /* end of flc_heap_win.h */ include/falcon/intcomp.h000066400000000000000000000101751176363201700155770ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: intcomp.h Complete encapsulation of an incremental interactive compiler. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 17 Aug 2008 11:10:24 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_INTCOMP_H #define FALCON_INTCOMP_H #include #include #include #include #include namespace Falcon { class ModuleLoader; /** Interactive compiler. This compiler performs incremental compiling, loading dependencies and can also execute on the fly single statements. For this reason, the compiler is provided with a VM and a flexy module; the compiler independently creates the module (which may be referenced and taken also externally) and exposes a function that allows incremental compilation. Using compileNext(), this class independently loads dependencies as they are found, executes statements and fills the module symbol table. The compiler may be provided with a VM generated from the outside, or it will create a standard VM on its own (which can be configured at a later time. */ class FALCON_DYN_CLASS InteractiveCompiler: public Compiler { VMachine *m_vm; LiveModule *m_lmodule; ModuleLoader *m_loader; bool m_interactive; void loadNow( const String &name, bool isFilename, bool bPrivate ); public: /** Create the interactive compiler. If a VM is not provided, it will be automatically created. Notice that the compiler will apply its error handler to the loader at compile time. */ InteractiveCompiler( ModuleLoader *loader, VMachine *vm ); ~InteractiveCompiler(); typedef enum { /** Do-nothing statements (comments, whitespaces ...)*/ e_nothing, /** We need more to finish the current statement */ e_more, /** Incomplete context */ e_incomplete, /** Statement was a complete declaration (function, object, class...) */ e_decl, /** Normal statement (assignment, if block, while block...) */ e_statement, /** Stand alone expression (sum, sub, single value) */ e_expression, /** Special expression consisting of a single call to a sub expression. Usually, the user expects a return value. */ e_call, e_terminated } t_ret_type; /** Compile another code slice coming from the stream. The calling application will receive the control back when the compilation, and eventually the execution of the needed steps are completed. The return value may be one of the t_ret_type enumeration, and the calling application can take proper actions. A return indicating error won't block the compiler nor invalidate, which is still available to compile incrementally other code slices. */ t_ret_type compileNext( Stream *input ); /** Compile another codeslice from a string. \see compileNext( Stream *); */ t_ret_type compileNext( const String &input ); t_ret_type compileAll( const String &input ); VMachine *vm() const { return m_vm; } virtual void addLoad( const String &name, bool isFilename ); virtual void addNamespace( const String &nspace, const String &alias, bool full=false, bool filename=false ); ModuleLoader *loader() const { return m_loader; } void loader( ModuleLoader *l ) { m_loader = l; } bool interactive() const { return m_interactive; } /** Sets or resets interactive mode. In interactive mode, the compiler accepts one statement at a time and transforms expressions into "return [expression]" statements to allow their value to be visible after the execution. Also, the lexer is configured so that it can accept partial elements from streams and tell back about a partial status to the compiler. */ void interactive( bool iactive ) { m_interactive = iactive; } }; } #endif /* end of intcomp.h */ include/falcon/item.h000066400000000000000000000757031176363201700150740ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_item.h Basic Item Api. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun ott 4 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Basic Item Api */ #ifndef flc_flc_item_H #define flc_flc_item_H #include #include #include #include #include #include #include #define OVERRIDE_OP_ADD "__add" #define OVERRIDE_OP_SUB "__sub" #define OVERRIDE_OP_MUL "__mul" #define OVERRIDE_OP_DIV "__div" #define OVERRIDE_OP_MOD "__mod" #define OVERRIDE_OP_POW "__pow" #define OVERRIDE_OP_NEG "__neg" #define OVERRIDE_OP_INC "__inc" #define OVERRIDE_OP_DEC "__dec" #define OVERRIDE_OP_INCPOST "__incpost" #define OVERRIDE_OP_DECPOST "__decpost" #define OVERRIDE_OP_CALL "__call" #define OVERRIDE_OP_GETINDEX "__getIndex" #define OVERRIDE_OP_SETINDEX "__setIndex" namespace Falcon { class Symbol; class CoreString; class CoreObject; class CoreDict; class CoreArray; class CoreClass; class CallPoint; class CoreFunc; class GarbageItem; class VMachine; class Stream; class LiveModule; class MemBuf; class GarbagePointer; class FalconData; class CodeError; class DeepItem; typedef void** CommOpsTable; extern FALCON_DYN_SYM CommOpsTable CommOpsDict[]; /** Basic item abstraction.*/ class FALCON_DYN_CLASS Item: public BaseAlloc { public: /** Common operations that can be performed on items. Each item type has a function pointer table taking care of this operations. Deep items operations is that of searching for overloadings via the deep item common DeepItem::getProperty() method, and then eventually calling the operator implementation. The operator implementation is called on the VM instance of the deep item. */ typedef enum { co_add, co_sub, co_mul, co_div, co_mod, co_pow, co_neg, co_inc, co_dec, co_incpost, co_decpost, co_compare, co_getIndex, co_setIndex, co_getProperty, co_setProperty, co_call } e_commops; /** Serialization / deserialization error code. This value is returned by serialization and deserialization functions to communicate what went wrong. */ typedef enum { /** All was fine. */ sc_ok, /** File error in serialization/deserialization */ sc_ferror, /** Invalid format in deserialization */ sc_invformat, /** Missing class in deserialization (object cannot be instantiated).*/ sc_missclass, /** Missing symbol in deserialization (requested function not present in VM).*/ sc_misssym, /** VM Error in serialization or de-serialization. This is called if VM raised an error during the call of serialization() object methods in case serialization is called, or if the VM raises an error during init() or deserialization() of deserialized objects. */ sc_vmerror, /** Needed VM but missing. This serialization or deserialization operation required a VM to be provided, but it wasn't. */ sc_missvm, /** Hit EOF while de-serializing */ sc_eof } e_sercode; protected: union { struct { union { int32 val32; int64 val64; numeric number; Garbageable *content; struct { void *voidp; void *extra; } ptr; } data; CallPoint *method; union { struct { byte type; byte flags; byte oldType; byte reserved; } bits; uint16 half; uint32 whole; } base; } ctx; struct { uint64 low; uint64 high; } parts; } all; bool serialize_object( Stream *file, CoreObject *obj, bool bLive ) const; bool serialize_symbol( Stream *file, const Symbol *sym ) const; bool serialize_function( Stream *file, const CoreFunc *func, bool bLive ) const; bool serialize_class( Stream *file, const CoreClass *cls ) const; e_sercode deserialize_symbol( Stream *file, VMachine *vm, Symbol **tg_sym, LiveModule **modId ); e_sercode deserialize_function( Stream *file, VMachine *vm ); e_sercode deserialize_class( Stream *file, VMachine *vm ); #ifdef _MSC_VER #if _MSC_VER < 1299 #define flagIsMethodic 0x02 #define flagIsOob 0x04 #define flagLiteral 0x08 #else static const byte flagIsMethodic = 0x02; static const byte flagIsOob = 0x04; static const byte flagLiteral = 0x08; #endif #else static const byte flagIsMethodic = 0x02; static const byte flagIsOob = 0x04; static const byte flagLiteral = 0x08; #endif public: Item( byte t=FLC_ITEM_NIL ) { type( t ); } Item( byte t, Garbageable *dt ); Item( CoreFunc* cf ) { setFunction( cf ); } void setNil() { type( FLC_ITEM_NIL ); } void setUnbound() { type( FLC_ITEM_UNB ); } Item( const Item &other ) { copy( other ); } /** Creates a boolean item. */ /** Sets this item as boolean */ void setBoolean( bool tof ) { type( FLC_ITEM_BOOL ); all.ctx.data.val32 = tof? 1: 0; } /** Creates an garbage pointer item */ Item( GarbagePointer* val ) { setGCPointer( val ); } /** Creates an integer item */ Item( int16 val ) { setInteger( (int64) val ); } /** Creates an integer item */ Item( uint16 val ) { setInteger( (int64) val ); } /** Creates an integer item */ Item( int32 val ) { setInteger( (int64) val ); } /** Creates an integer item */ Item( uint32 val ) { setInteger( (int64) val ); } /** Creates an integer item */ Item( int64 val ) { setInteger( val ); } /** Creates an integer item */ Item( uint64 val ) { setInteger( (int64)val ); } void setInteger( int64 val ) { type(FLC_ITEM_INT); all.ctx.data.val64 = val; } /** Creates a numeric item */ Item( numeric val ) { setNumeric( val ); } void setNumeric( numeric val ) { type( FLC_ITEM_NUM ); all.ctx.data.number = val; } /** Creates a range item */ Item( CoreRange *r ) { setRange( r ); } void setRange( CoreRange *r ); /** Creates a corestring. The given string is copied and stored in the Garbage system. It is also bufferized, as the most common usage of this constructor is in patterns like Item( "a static string" ); */ Item( const String &str ); /** Creates a CoreString item */ Item( String *str ) { setString( str ); } /* Creates a String item dependent from a module */ /*Item( String *str, LiveModule* lm ) { setString( str, lm ); }*/ void setString( String *str ); //void setString( String *str, LiveModule* lm ); /** Creates an array item */ Item( CoreArray *array ) { setArray( array ); } void setArray( CoreArray *array ); /** Creates an object item */ Item( CoreObject *obj ) { setObject( obj ); } void setObject( CoreObject *obj ); /** Creates a dictionary item */ Item( CoreDict *obj ) { setDict( obj ); } void setDict( CoreDict *dict ); /** Creates a memory buffer. */ Item( MemBuf *buf ) { setMemBuf( buf ); } void setMemBuf( MemBuf *b ); Item( GarbageItem *ref ) { setReference( ref ); } /** Creates a reference to another item. */ void setReference( GarbageItem *ref ); GarbageItem *asReference() const { return (GarbageItem *) all.ctx.data.ptr.voidp; } /** Creates a function item */ void setFunction( CoreFunc* cf ); /** Creates a late binding item. The late binding is just a CoreString in a live module which is resolved into a value by referencing a item in the current context (symbol tables) having the given name at runtime. Thus, the CoreString representing the late binding symbol name lives in the live module that generated this LBind. If the module is unloaded, the LBind is invalidated. \param lbind The name of the late binding symbol. \param val If provided, a future value (future binding). */ void setLBind( String *lbind, GarbageItem *val=0 ); /** Returns true if this item is a valid LBind. */ bool isLBind() const { return type() == FLC_ITEM_LBIND; } bool isFutureBind() const { return isLBind() && all.ctx.data.ptr.extra != 0; } /** Return the binding name associate with this LBind item. */ String *asLBind() const { return (String *) all.ctx.data.ptr.voidp; } GarbageItem *asFBind() const { return (GarbageItem *) all.ctx.data.ptr.extra; } bool isLitLBind() const { return isLBind() && asLBind()->getCharAt(0) == '.'; } const Item &asFutureBind() const; Item &asFutureBind(); /** Creates a method. The method is able to remember if it was called with a Function pointer or using an external function. */ Item( const Item &data, CallPoint* func ) { setMethod( data, func ); } Item( CoreObject *obj, CoreClass *cls ) { setClassMethod( obj, cls ); } /** Creates a method. The method is able to remember if it was called with a Function pointer or using an external function. */ void setMethod( const Item &data, CallPoint *func ); void setClassMethod( CoreObject *obj, CoreClass *cls ); /** Creates a class item */ Item( CoreClass *cls ) { setClass( cls ); } void setClass( CoreClass *cls ); /** Defines this item as a out of band data. Out of band data allow out-of-order sequencing in functional programming. If an item is out of band, it's type it's still the original one, and its value is preserved across function calls and returns; however, the out of band data may innescate a meta-level processing of the data travelling through the functions. In example, returning an out of band NIL value from an xmap mapping function will cause xmap to discard the data. */ void setOob() { all.ctx.base.bits.flags |= flagIsOob; } /** Clear out of band status of this item. \see setOob() */ void resetOob() { all.ctx.base.bits.flags &= ~flagIsOob; } /** Sets or clears the out of band status status of this item. \param oob true to make this item out of band. \see setOob() */ void setOob( bool oob ) { if ( oob ) all.ctx.base.bits.flags |= flagIsOob; else all.ctx.base.bits.flags &= ~flagIsOob; } /** Set this item as a user-defined Garbage pointers. VM provides GC-control over them. */ void setGCPointer( FalconData *ptr ); void setGCPointer( GarbagePointer *shell ); FalconData *asGCPointer() const; GarbagePointer *asGCPointerShell() const; bool isGCPointer() const { return type() == FLC_ITEM_GCPTR; } /** Tells wether this item is out of band. \return true if out of band. \see oob() */ bool isOob() const { return (all.ctx.base.bits.flags & flagIsOob )== flagIsOob; } /** Returns true if this item is an instance of some sort. \return true if this is an object, blessed dictionary or bound array. */ bool isComposed() const { return isObject() || isArray() || isDict(); } /** Returns the item type*/ byte type() const { return all.ctx.base.bits.type; } /** Returns the item type as string*/ void typeName( String &target ) const; /** Changes the item type. Flags are also reset. For example, if this item was OOB before, the OOB flag is cleared. */ void type( byte nt ) { all.ctx.base.bits.flags = 0; all.ctx.base.bits.type = nt; } /** Returns the content of the item */ Garbageable *content() const { return all.ctx.data.content; } void content( Garbageable *dt ) { all.ctx.data.content = dt; } void copy( const Item &other ) { #ifdef _SPARC32_ITEM_HACK register int32 *pthis, *pother; pthis = (int32*) this; pother = (int32*) &other; pthis[0]= pother[0]; pthis[1]= pother[1]; pthis[2]= pother[2]; pthis[3]= pother[3]; #else all = other.all; #endif } /** Tells if this item is callable. This function will turn this object into a nil if the item referenced a dead module. As this is a pathological situation, a const cast is forced. */ bool isCallable() const; /** Return true if this is a callable item that is turned into a method when found as property.*/ bool canBeMethod() const; bool isOrdinal() const { return type() == FLC_ITEM_INT || type() == FLC_ITEM_NUM; } bool asBoolean() const { return all.ctx.data.val32 != 0; } int64 asInteger() const { return all.ctx.data.val64; } numeric asNumeric() const { return all.ctx.data.number; } int64 asRangeStart() const { return static_cast(all.ctx.data.content)->start(); } int64 asRangeEnd() const { return static_cast(all.ctx.data.content)->end(); } int64 asRangeStep() const { return static_cast(all.ctx.data.content)->step(); } bool asRangeIsOpen() const { return static_cast(all.ctx.data.content)->isOpen(); } CoreRange* asRange() const { return static_cast(all.ctx.data.content); } String *asString() const { return (String *) all.ctx.data.ptr.voidp; } LiveModule *asStringModule() const { return (LiveModule *) all.ctx.data.ptr.extra; } CoreString *asCoreString() const { return (CoreString *) all.ctx.data.ptr.voidp; } DeepItem *asDeepItem() const { return (DeepItem *) all.ctx.data.ptr.voidp; } /** Provides a basic CoreString representation of the item. Use Falcon::Format for a finer control of item representation. \param target a CoreString where the item CoreString representation will be placed. */ void toString( String &target ) const; CoreArray *asArray() const { return (CoreArray *) all.ctx.data.ptr.voidp; } CoreObject *asObject() const; CoreObject *asObjectSafe() const { return (CoreObject *) all.ctx.data.ptr.voidp; } CoreDict *asDict() const { return ( CoreDict *) all.ctx.data.ptr.voidp; } MemBuf *asMemBuf() const { return ( MemBuf *) all.ctx.data.ptr.voidp; } CoreClass* asClass() const { return (CoreClass *) all.ctx.data.ptr.extra; } CoreFunc* asFunction() const { return (CoreFunc*) all.ctx.data.ptr.extra; } CallPoint* asMethodFunc() const { return (CallPoint*) all.ctx.method; } /** Gets the "self" in an item (return the item version). */ Item asMethodItem() const { Item temp = *this; temp.type( all.ctx.base.bits.oldType ); temp = *temp.dereference(); temp.flagsOn( flagIsMethodic ); return temp; } /** Gets the "self" in an item (pass byref version). */ void getMethodItem( Item &itm ) const { itm = *this; itm.type( all.ctx.base.bits.oldType ); itm = *itm.dereference(); itm.flagsOn( flagIsMethodic ); } /** Turns a method item into its original "self". */ void deMethod() { type( all.ctx.base.bits.oldType ); } CoreClass *asMethodClass() const { return (CoreClass*) all.ctx.data.ptr.extra; } CoreObject *asMethodClassOwner() const { return (CoreObject*) all.ctx.data.ptr.voidp; } //LiveModule *asModule() const { return all.ctx.data.ptr.m_liveMod; } /** Convert current object into an integer. This operations is usually done on integers, numeric and CoreStrings. It will do nothing meaningfull on other types. */ int64 forceInteger() const ; /** Convert current object into an integer. This operations is usually done on integers, numeric and CoreStrings. It will do nothing meaningfull on other types. \note this version will throw a code error if the item is not an ordinal. */ int64 forceIntegerEx() const ; /** Convert current object into a numeric. This operations is usually done on integers, numeric and CoreStrings. It will do nothing meaningfull on other types. */ numeric forceNumeric() const ; /** Calculates the hash function for this item. */ uint32 hash() const; bool isNil() const { return type() == FLC_ITEM_NIL; } bool isBoolean() const { return type() == FLC_ITEM_BOOL; } bool isInteger() const { return type() == FLC_ITEM_INT; } bool isNumeric() const { return type() == FLC_ITEM_NUM; } bool isScalar() const { return type() == FLC_ITEM_INT || type() == FLC_ITEM_NUM; } bool isRange() const { return type() == FLC_ITEM_RANGE; } bool isString() const { return type() == FLC_ITEM_STRING; } bool isArray() const { return type() == FLC_ITEM_ARRAY; } bool isDict() const { return type() == FLC_ITEM_DICT; } bool isMemBuf() const { return type() == FLC_ITEM_MEMBUF; } bool isObject() const { return type() == FLC_ITEM_OBJECT; } bool isReference() const { return type() == FLC_ITEM_REFERENCE; } bool isFunction() const { return type() == FLC_ITEM_FUNC; } bool isMethod() const { return type() == FLC_ITEM_METHOD; } bool isClassMethod() const { return type() == FLC_ITEM_CLSMETHOD; } bool isClass() const { return type() == FLC_ITEM_CLASS; } bool isUnbound() const { return type() == FLC_ITEM_UNB; } bool isOfClass( const String &className ) const; bool isMethodic() const { return (flags() & flagIsMethodic) != 0; } bool isTrue() const; Item &operator=( const Item &other ) { copy( other ); return *this; } bool operator==( const Item &other ) const { return compare(other) == 0; } bool operator!=( const Item &other ) const { return compare(other) != 0; } bool operator<(const Item &other) const { return compare( other ) < 0; } bool operator<=(const Item &other) const { return compare( other ) <= 0; } bool operator>(const Item &other) const { return compare( other ) > 0; } bool operator>=(const Item &other) const { return compare( other ) >= 0; } bool exactlyEqual( const Item &other ) const; inline Item *dereference(); inline const Item *dereference() const; /** Turns this item in a method of the given object. This is meant to be used by external functions when accessing object properties. VM always creates a method when accessor is used; in example, myObject.someFunc() will create a method myObject.someFunc if there is some callable inside the given property, and then will call it. In this way, a function may be assigned to that property, and the VM will take care to create an item that will turn the function in a method. But when a non-vm program accesses an object "method", the calling program may have assigned something different to it in the meanwhile, and what its returned in CoreObject::getProperty() won't be a callable method, but just the assigned object. Methodize will turn such an item in a callable method of the object given as parameter, if this is possible, else it will return false. \note External methods (that is, methods calling external functions) won't be methodized, as they often rely in external values carried inside their CoreObject owners. However, the function will return true, as the object can be called, and very probably it will do what expected by the user (as the external method would have never used anything other than the VM generated self anyhow). \note CoreObject::getMethod() is a shortcut to this function. \param self the object that will be set as "self" for the method \return true if the item can be called properly, false if it's not a callable. */ bool methodize( const Item& self ); bool methodize( const CoreObject *co ) { return methodize( Item(const_cast(co)) ); } /** Serialize this item. This method stores an item on a stream for later retrival. The function can return true if the serialization succeded. It will return false if the serialization failed; in that case, if there is an error on the stream, it can be retreived form the stream variable. If the stream reports no error, then the serialization failed because a VM was not provided while a serialized method should have been called on the target object, or in any referenced object, or because the VM received an error during the method call. \param out the output stream \param bLive true \return an error code in case of error (\see e_sercode). */ e_sercode serialize( Stream *out, bool bLive = false ) const; /** Loads a serialized item from a stream. This method restores an item previously stored on a stream for later retrival. Providing a virtual machine that is optional; if not provided, items requiring the VM for deserialization won't be correctly restored. Objects deserialization requires a VM readied with the class the object derives from. \see serialize() \param in the input stream \param vm the virtual machine that can be used for object deserialization \return an error code in case of error (\see e_sercode). */ e_sercode deserialize( Stream *in, VMachine *vm = 0 ); /** Flags, used for internal vm needs. */ byte flags() const { return all.ctx.base.bits.flags; } void flags( byte b ) { all.ctx.base.bits.flags = b; } void flagsOn( byte b ) { all.ctx.base.bits.flags |= b; } void flagsOff( byte b ) { all.ctx.base.bits.flags &= ~b; } /** Clone the item (with the help of a VM). If the item is not cloneable, the method returns false. Is up to the caller to raise an appropriate error if that's the case. The VM parameter may be zero; in that case, returned items will not be stored in any garbage collector. Reference items are de-referenced; if cloning a reference, the caller will obtain a clone of the target item, not a clone of the reference. Also, in that case, the returned item will be free of reference. \param vm the virtual machine used for cloning. \param target the item where to stored the cloned instance of this item. \return true if the clone operation is possible */ bool clone( Item &target ) const; /** Return true if the item deep. Deep items are the ones that are subject to garbage collecting. \return true if the item is deep. */ bool isDeep() const { return type() >= FLC_ITEM_FIRST_DEEP; } //====================================================================// void add( const Item &operand, Item &result ) const { void (*addfunc)( const Item &first, const Item& second, Item &third) = (void (*)( const Item &first, const Item& second, Item &third)) CommOpsDict[type()][co_add]; addfunc( *this, operand, result ); } void sub( const Item &operand, Item &result ) const { void (*func)( const Item &first, const Item& second, Item &third) = (void (*)( const Item &first, const Item& second, Item &third)) CommOpsDict[type()][co_sub]; func( *this, operand, result ); } void mul( const Item &operand, Item &result ) const { void (*func)( const Item &first, const Item& second, Item &third) = (void (*)( const Item &first, const Item& second, Item &third)) CommOpsDict[type()][co_mul]; func( *this, operand, result ); } void div( const Item &operand, Item &result ) const { void (*func)( const Item &first, const Item& second, Item &third) = (void (*)( const Item &first, const Item& second, Item &third)) CommOpsDict[type()][co_div]; func( *this, operand, result ); } void mod( const Item &operand, Item &result ) const { void (*func)( const Item &first, const Item& second, Item &third) = (void (*)( const Item &first, const Item& second, Item &third)) CommOpsDict[type()][co_mod]; func( *this, operand, result ); } void pow( const Item &operand, Item &result ) const { void (*func)( const Item &first, const Item& second, Item &third) = (void (*)( const Item &first, const Item& second, Item &third)) CommOpsDict[type()][co_pow]; func( *this, operand, result ); } void neg( Item& target ) const { void (*func)( const Item &first, Item &tg ) = (void (*)( const Item &first, Item &tg )) CommOpsDict[type()][co_neg]; func( *this, target ); } void inc( Item& target ) { void (*func)( Item &first, Item &second ) = (void (*)( Item &first, Item &second )) CommOpsDict[type()][co_inc]; func( *this, target ); } void dec( Item& target ) { void (*func)( Item &first, Item &second ) = (void (*)( Item &first, Item &second )) CommOpsDict[type()][co_dec]; func( *this, target ); } void incpost( Item& target ) { void (*func)( Item &first, Item &tg ) = (void (*)( Item &first, Item &tg )) CommOpsDict[type()][co_incpost]; func( *this, target ); } void decpost( Item& target ) { void (*func)( Item &first, Item &tg ) = (void (*)( Item &first, Item &tg )) CommOpsDict[type()][co_decpost]; func( *this, target ); } int compare( const Item &operand ) const { int (*func)( const Item &first, const Item& second ) = (int (*)( const Item &first, const Item& second )) CommOpsDict[type()][co_compare]; return func( *this, operand ); } void getIndex( const Item &idx, Item &result ) const { void (*func)( const Item &first, const Item &idx, Item &third) = (void (*)( const Item &first, const Item &idx, Item &third)) CommOpsDict[type()][co_getIndex]; func( *this, idx, result ); } void setIndex( const Item &idx, const Item &result ) { void (*func)( Item &first, const Item &name, const Item &third) = (void (*)( Item &first, const Item &name, const Item &third)) CommOpsDict[type()][co_setIndex]; func( *this, idx, result ); } void getProperty( const String &property, Item &result ) const { void (*func)( const Item &first, const String &property, Item &third) = (void (*)( const Item &first, const String &property, Item &third)) CommOpsDict[type()][co_getProperty]; func( *this, property, result ); } void setProperty( const String &prop, const Item &result ) { void (*func)( Item &first, const String &prop, const Item &third) = (void (*)( Item &first, const String &prop, const Item &third)) CommOpsDict[type()][co_setProperty]; func( *this, prop, result ); } /** Prepares a call frame that will be called at next VM loop. \note You can use vm->execFrame() to execute the prepared frame immediately instead of waiting for the loop to complete. */ void readyFrame( VMachine *vm, uint32 paramCount ) const { void (*func)( const Item &first, VMachine *vm, int paramCount ) = (void (*)( const Item &first, VMachine *vm, int paramCount )) CommOpsDict[type()][co_call]; func( *this, vm, paramCount ); } }; /** Creates a garbageable version of an item. This class repeats the structure of an item holding an instance of it, but it derives from Garbageable. This makes it a vessel for item references. It must be created by a MemPool with the MemPool::referenceItem() method. */ class FALCON_DYN_CLASS GarbageItem: public Garbageable { Item m_item; public: GarbageItem( const Item &origin ): Garbageable(), m_item( origin ) {} virtual ~GarbageItem() {}; /** Returns the item part stored in this garbage item. \return the held item. */ const Item &origin() const { return m_item; } /** Returns the item part stored in this garbage item. \return the held item. */ Item &origin() { return m_item; } }; inline Item *Item::dereference() { if ( type() != FLC_ITEM_REFERENCE ) return this; return &asReference()->origin(); }; inline const Item *Item::dereference() const { if ( type() != FLC_ITEM_REFERENCE ) return this; return &asReference()->origin(); }; class FALCON_DYN_CLASS SafeItem: public Item { public: SafeItem( const SafeItem &other ) { copy( other ); } SafeItem( const Item &other ) { copy( other ); } /** Creates a boolean SafeItem. */ /** Sets this SafeItem as boolean */ void setBoolean( bool tof ) { type( FLC_ITEM_BOOL ); all.ctx.data.val32 = tof? 1: 0; } /** Creates an integer SafeItem */ SafeItem( int16 val ) { setInteger( (int64) val ); } /** Creates an integer SafeItem */ SafeItem( uint16 val ) { setInteger( (int64) val ); } /** Creates an integer SafeItem */ SafeItem( int32 val ) { setInteger( (int64) val ); } /** Creates an integer SafeItem */ SafeItem( uint32 val ) { setInteger( (int64) val ); } /** Creates an integer SafeItem */ SafeItem( int64 val ) { setInteger( val ); } /** Creates an integer SafeItem */ SafeItem( uint64 val ) { setInteger( (int64)val ); } void setInteger( int64 val ) { type(FLC_ITEM_INT); all.ctx.data.val64 = val; } /** Creates a numeric SafeItem */ SafeItem( numeric val ) { setNumeric( val ); } void setNumeric( numeric val ) { type( FLC_ITEM_NUM ); all.ctx.data.number = val; } SafeItem( byte t, Garbageable *dt ); SafeItem( CoreRange *r ) { setRange( r ); } SafeItem( String *str ) { setString( str ); } SafeItem( CoreArray *array ) { setArray( array ); } SafeItem( CoreObject *obj ) { setObject( obj ); } SafeItem( CoreDict *dict ) { setDict( dict ); } SafeItem( MemBuf *b ) { setMemBuf( b ); } SafeItem( GarbageItem *r ) { setReference( r ); } SafeItem( CoreFunc* cf ) { setFunction( cf ); } SafeItem( String *lbind, GarbageItem *val ) { setLBind( lbind, val ); } SafeItem( const Item &data, CallPoint *func ) { setMethod( data, func ); } SafeItem( CoreObject *obj, CoreClass *cls ) { setClassMethod( obj, cls ); } SafeItem( CoreClass *cls ) { setClass( cls ); } SafeItem( FalconData *ptr ) { setGCPointer( ptr ); } SafeItem( GarbagePointer *shell ) { setGCPointer( shell ); } void setRange( CoreRange *r ); void setString( String *str ); void setArray( CoreArray *array ); void setObject( CoreObject *obj ); void setDict( CoreDict *dict ); void setMemBuf( MemBuf *b ); void setReference( GarbageItem *r ); void setFunction( CoreFunc* cf ); void setLBind( String *lbind, GarbageItem *val ); void setMethod( const Item &data, CallPoint *func ); void setClassMethod( CoreObject *obj, CoreClass *cls ); void setClass( CoreClass *cls ); void setGCPointer( FalconData *ptr ); void setGCPointer( GarbagePointer *shell ); }; } #endif /* end of flc_item.h */ include/falcon/itemarray.h000066400000000000000000000150511176363201700161210ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: itemarray.h Basic array of items. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 27 Jul 2009 20:45:11 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_ITEMARRAY_H_ #define FALCON_ITEMARRAY_H_ #include #include #include #include namespace Falcon { class CoreArray; class CoreTable; class FALCON_DYN_CLASS ItemArray: public Sequence { uint32 m_alloc; uint32 m_size; Item *m_data; uint32 m_growth; Garbageable* m_owner; // point starting from which the iterators to this sequence are invalidated (included). // needs no initialization uint32 m_invalidPoint; friend class CoreArray; friend class CoreTable; ItemArray( Item *buffer, uint32 size, uint32 alloc ); /** Classed used internally to track loops in traversals. */ class Parentship { public: const ItemArray* m_array; Parentship* m_parent; Parentship( const ItemArray* d, Parentship* parent=0 ): m_array(d), m_parent( parent ) {} }; int compare( const ItemArray& other, Parentship* parent ) const; public: ItemArray(); ItemArray( const ItemArray& other ); ItemArray( uint32 prealloc ); virtual ~ItemArray(); virtual const Item &front() const { return m_data[0]; } virtual const Item &back() const { return m_data[m_size-1]; } Item *elements() const { return m_data; } void elements( Item *d ) { m_data = d; } uint32 allocated() const { return m_alloc; } uint32 length() const { return m_size; } void length( uint32 size ) { m_size = size; } void allocated( uint32 size ) { m_alloc = size; } virtual void clear() { m_size = 0; } virtual bool empty() const { return m_size == 0; } virtual void gcMark( uint32 mark ); virtual ItemArray *clone() const ; virtual void append( const Item &ndata ); virtual void prepend( const Item &ndata ); void merge( const ItemArray &other ); void merge_front( const ItemArray &other ); bool insert( const Item &ndata, int32 pos ); bool insert( const ItemArray &other, int32 pos ); bool remove( int32 pos ); bool remove( int32 first, int32 last ); bool change( const ItemArray &other, int32 begin, int32 end ); int32 find( const Item &itm ) const; bool insertSpace( uint32 pos, uint32 size ); void resize( uint32 size ); /* Returns a loop-free deep compare of the array. The return value is the compare value of the first non-equal items, or -1 in case this array is the same but shorter than the other. */ int compare( const ItemArray& other ) const { return compare( other, 0 ); } /** * Reduce the memory used by this array to exactly its size. */ void compact(); void reserve( uint32 size ); ItemArray *partition( int32 start, int32 end ) const; /** Copy part or all of another vector on this vector. This method performs a flat copy of another array into this one. It is possible to copy part of the target array, specifying the first and amount parameters. Also, it's possible to select a starting position different from the beginning through the from parameter. If the from parameter is larger than the size of this array, or if the first parameter is larger than the size of the source array, the function returns false. If the required amount of items to be copied is greater than the number of items in the source array (starting from the first), then it's rounded down to copy all the items starting from first. If this array is not large enough to store all the items starting from the from parameter, it is enlarged. \param from The first item from which to start copying. \param src The source array \param first The first element in the array to be copied. \param amount Number of elements to be copied. */ bool copyOnto( uint32 from, const ItemArray& src, uint32 first=0, uint32 amount=0xFFFFFFFF ); /** Copy part or all of another vector on this vector. Shortcut for copyOnto starting from element 0 of this vector. \param src The source array \param first The first element in the array to be copied. \param amount Number of elements to be copied. */ bool copyOnto( const ItemArray& src, uint32 first=0, uint32 amount=0xFFFFFFFF ) { return copyOnto( 0, src, first, amount ); } inline virtual const Item &at( int32 pos ) const { if ( pos < 0 ) pos = m_size + pos; if ( pos < 0 || pos > (int32) m_size ) throw "Invalid range while accessing Falcon::CoreArray"; return m_data[pos]; } inline virtual Item &at( int32 pos ) { if ( pos < 0 ) pos = m_size + pos; if ( pos < 0 || pos > (int32)m_size ) throw "Invalid range while accessing Falcon::CoreArray"; return m_data[pos]; } inline Item &operator[]( int32 pos ) throw() { return m_data[pos]; } inline const Item &operator[]( int32 pos ) const throw() { return m_data[pos]; } /** An inline utility to compute element size. * * @param count numbrer of elements * @return the amout of bytes needed to store the elements */ int32 esize( int32 count=1 ) const { return sizeof( Item ) * count; } //======================================================== // Iterator implementation. //======================================================== protected: virtual void getIterator( Iterator& tgt, bool tail = false ) const; virtual void copyIterator( Iterator& tgt, const Iterator& source ) const; virtual void insert( Iterator &iter, const Item &data ); virtual void erase( Iterator &iter ); virtual bool hasNext( const Iterator &iter ) const; virtual bool hasPrev( const Iterator &iter ) const; virtual bool hasCurrent( const Iterator &iter ) const; virtual bool next( Iterator &iter ) const; virtual bool prev( Iterator &iter ) const; virtual Item& getCurrent( const Iterator &iter ); virtual Item& getCurrentKey( const Iterator &iter ); virtual bool equalIterator( const Iterator &first, const Iterator &second ) const; virtual bool onCriterion( Iterator* elem ) const; }; } #endif /* FALCON_ITEMARRAY_H_ */ /* end of itemarray.h */ include/falcon/itemdict.h000066400000000000000000000041301176363201700157220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: coredict.h Core dictionary -- base abstract class for dictionary interfaces. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 02 Aug 2009 21:18:28 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Core dictionary -- base abstract class for dictionary interfaces. */ #ifndef FALCON_ITEM_DICT_H #define FALCON_ITEM_DICT_H #include #include namespace Falcon { class Item; /** Base class for item dictionaries. * This is the base class for item dictionaries. Dictionaries * must support the sequence interface. They cannot be immediately * stored into falcon Items; a CoreDict wrapper is necessary. */ class FALCON_DYN_CLASS ItemDict: public Sequence { public: /** Override sequence to inform all that we're a dictionary. */ virtual bool isDictionary() const { return true; } virtual uint32 length() const = 0; virtual Item *find( const Item &key ) const = 0; virtual bool findIterator( const Item &key, Iterator &iter ) = 0; virtual bool remove( const Item &key ) = 0; virtual void put( const Item &key, const Item &value ) = 0; virtual void smartInsert( const Iterator &iter, const Item &key, const Item &value ) = 0; virtual void merge( const ItemDict &dict ) = 0; virtual void clear() = 0; virtual int compare( const ItemDict& other ) const { return compare( other, 0); } private: /** Classed used internally to track loops in traversals. */ class Parentship { public: const ItemDict* m_dict; Parentship* m_parent; Parentship( const ItemDict* d, Parentship* parent=0 ): m_dict(d), m_parent( parent ) {} }; int compare( const ItemDict& other, Parentship* p ) const; int checkValue( const Item& first, const Item& second, Parentship& current ) const; }; } #endif /* FALCON_ITEM_DICT_H */ include/falcon/itemid.h000066400000000000000000000025651176363201700154050ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_itemid.h List of item ids ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven ott 15 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file List if item ids. */ #ifndef flc_flc_itemid_H #define flc_flc_itemid_H #define FLC_ITEM_NIL 0 #define FLC_ITEM_BOOL 1 #define FLC_ITEM_INT 2 #define FLC_ITEM_NUM 3 /** From this point on, we have possibly deep items */ #define FLC_ITEM_FIRST_DEEP 4 #define FLC_ITEM_RANGE 4 #define FLC_ITEM_LBIND 5 #define FLC_ITEM_FUNC 6 /* Some shallow callable items. */ /** Used to store pointers in temporary local items by two-step VM functions. */ #define FLC_ITEM_GCPTR 7 #define FLC_ITEM_STRING 8 #define FLC_ITEM_ARRAY 9 #define FLC_ITEM_DICT 10 #define FLC_ITEM_OBJECT 11 #define FLC_ITEM_MEMBUF 12 #define FLC_ITEM_REFERENCE 13 #define FLC_ITEM_CLSMETHOD 14 #define FLC_ITEM_METHOD 15 #define FLC_ITEM_CLASS 16 #define FLC_ITEM_UNB 17 #define FLC_ITEM_COUNT 18 #define FLC_ITEM_INVALID 99 #endif /* end of flc_itemid.h */ include/falcon/itemlist.h000066400000000000000000000147641176363201700157700ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: itemlist.h List of Falcon Items ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2007-12-01 ------------------------------------------------------------------- (C) Copyright 2007: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file List of Falcon Items definition */ #ifndef flc_itemlist_H #define flc_itemlist_H #include #include #include #include #include #include #include namespace Falcon { class ItemListElement; class ItemList; class Iterator; /** Element of a standard list of Falcon items. */ class FALCON_DYN_CLASS ItemListElement: public BaseAlloc { Item m_item; ItemListElement *m_next; ItemListElement *m_prev; public: /** Create the element by copying an item. The item is shallow copied. */ ItemListElement( const Item &itm, ItemListElement *p = 0, ItemListElement *n = 0 ): m_item( itm ), m_next( n ), m_prev( p ) {} /** Deletes the element. Called when all the iterators pointing to this element are gone. */ ~ItemListElement() { } const Item &item() const { return m_item; } Item &item() { return m_item; } void next( ItemListElement *n ) { m_next = n; } ItemListElement *next() const { return m_next; } void prev( ItemListElement *p ) { m_prev = p; } ItemListElement *prev() const { return m_prev; } }; /** List of Falcon items. This class is designed to work together with Falcon object as a UserData, but it can be also used for other reasons, when an Array is not the best way to represent data. */ class FALCON_DYN_CLASS ItemList: public Sequence { private: uint32 m_size; ItemListElement *m_head; ItemListElement *m_tail; // temporary variable using during iter-erase Iterator* m_erasingIter; ItemListElement* m_disposingElem; public: /** Builds an empty list. */ ItemList(): m_size(0), m_head(0), m_tail(0), m_erasingIter(0), m_disposingElem(0) {} /** Clones a list. */ ItemList( const ItemList &l ); virtual ~ItemList() { clear(); } /** Deletes the list. Items are shallowly destroyed. */ virtual ItemList *clone() const; /** Gets the first item in the list. If the list is empty, you will crash, so use this only when the list is NOT empty. \return a reference to the first item in the list or a spectacular crash. */ virtual const Item &front() const; /** Gets the last item in the list. If the list is empty, you will crash, so use this only when the list is NOT empty. \return a reference to the last item in the list or a spectacular crash. */ virtual const Item &back() const; /** Gets the pointer to the first element for list traversal. The list element is just an item with previous and next pointers. If the list is empty, this method will return 0. \return the pointer to the first element pointer, or 0. */ ItemListElement *first() const; /** Gets the pointer to the last element for list traversal. The list element is just an item with previous and next pointers. If the list is empty, this method will return 0. \return the pointer to the last element pointer, or 0. */ ItemListElement *last() const; virtual void append( const Item& itm ) { push_back( itm ); } virtual void prepend( const Item& itm ) { push_front( itm ); } /** Pushes a shallow copy of the item to the end of the list. \param itm the item to be pushed. */ void push_back( const Item &itm ); /** Removes the last element from the list. The item is shallowly removed. Deep content will be reclaimed through GC. Calling pop_back() on an empty list will have no effect. */ void pop_back(); /** Pushes a shallow copy of the item in front of the list. \param itm the item to be pushed. */ void push_front( const Item &itm ); /** Removes the first element from the list. The item is shallowly removed. Deep content will be reclaimed by GC. Calling pop_front() on an empty list will have no effect. */ void pop_front(); /** Removes all the elements in the list. */ virtual void clear(); /** Remove given element. If this is the last element of the list, the method returns 0, else it return the element that was following the delete element in the list, and that now has its place. \param elem an element from this list (or you'll witness psychedelic crashes) */ ItemListElement *erase( ItemListElement *elem ); /** Insert an item after given before given element. To insert an item past the last element, use 0 as element pointer (last->next); this will work also to insert an item in an empty list. \param elem the element before which to insert the item, or 0 to apped at tail. \param item the item to be inserted. */ void insert( ItemListElement *elem, const Item &item ); /** Tells if the list is empty. \return true if the list is empty. */ virtual bool empty() const { return m_size == 0; } /** Return the number of the items in the list. \return count of items in the list */ uint32 size() const { return m_size; } /** Perform marking of items stored in the list. */ virtual void gcMark( uint32 mark ); // Deletion criterion. virtual bool onCriterion( Iterator* elem ) const; //======================================================== // Iterator implementation. //======================================================== protected: virtual void getIterator( Iterator& tgt, bool tail = false ) const; virtual void copyIterator( Iterator& tgt, const Iterator& source ) const; virtual void insert( Iterator &iter, const Item &data ); virtual void erase( Iterator &iter ); virtual bool hasNext( const Iterator &iter ) const; virtual bool hasPrev( const Iterator &iter ) const; virtual bool hasCurrent( const Iterator &iter ) const; virtual bool next( Iterator &iter ) const; virtual bool prev( Iterator &iter ) const; virtual Item& getCurrent( const Iterator &iter ); virtual Item& getCurrentKey( const Iterator &iter ); virtual bool equalIterator( const Iterator &first, const Iterator &second ) const; }; } #endif /* end of itemlist.h */ include/falcon/itempage.h000066400000000000000000000020761176363201700157220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_itempage.h Definition of the page that holds items. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun ott 4 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Definition of the page that holds items. */ #ifndef flc_flc_itempage_H #define flc_flc_itempage_H #include #include #include #define PAGE_SIZE 4096 #define FREE_PAGE_SPACE (PAGE_SIZE - sizeof( ItemPage *) - sizeof( ItemPage *) ) #define PAGE_ITEM_COUNT (FREE_PAGE_SPACE/sizeof( Item ) ) namespace Falcon { class MemPool; /** Item page. A segregated allocation page holding Falcon VM items. */ class ItemPage: public BaseAlloc { public: ItemPage *m_prev; ItemPage *m_next; Item items[ PAGE_ITEM_COUNT ]; // padding here }; } #endif /* end of flc_itempage.h */ include/falcon/itemset.h000066400000000000000000000153341176363201700156020ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: itemset.h (Ordered) set of falcon items. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 07 Aug 2009 18:36:22 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Setof Falcon Items definition */ #ifndef FALCON_ITEMSET_H #define FALCON_ITEMSET_H #include #include #include #include #include #include #include namespace Falcon { class ItemSetElement; class ItemSet; /** Element of a standard set of Falcon items. */ class FALCON_DYN_CLASS ItemSetElement: public BaseAlloc { Item m_item; ItemSetElement *m_left; ItemSetElement *m_right; ItemSetElement *m_parent; public: /** Create the element by copying an item. The item is shallow copied. */ ItemSetElement( const Item &itm, ItemSetElement* p=0, ItemSetElement *l = 0, ItemSetElement *r = 0 ): m_item( itm ), m_left( l ), m_right( r ), m_parent( p ) {} /** Deletes the element. */ ~ItemSetElement() { } const Item &item() const { return m_item; } Item &item() { return m_item; } void left( ItemSetElement *n ) { m_left = n; } ItemSetElement *left() const { return m_left; } void right( ItemSetElement *p ) { m_right = p; } ItemSetElement *right() const { return m_right; } void parent( ItemSetElement *p ) { m_parent = p; } ItemSetElement *parent() const { return m_parent; } }; /** Set of Falcon items. This class is designed to work together with Falcon object as a UserData, but it can be also used alone to store unique entities of items. The set is internally represented as a binary tree (eventually balanced). */ class FALCON_DYN_CLASS ItemSet: public Sequence { private: uint32 m_size; ItemSetElement *m_root; uint32 m_mark; // temporary variable using during iter-erase Iterator* m_erasingIter; ItemSetElement* m_disposingElem; static ItemSetElement* duplicateSubTree( ItemSetElement* parent, const ItemSetElement* source ); static void clearSubTree( ItemSetElement* source ); static ItemSetElement* smallestInTree( ItemSetElement* e ); static ItemSetElement* largestInTree( ItemSetElement* e ); static bool insertInSubtree( ItemSetElement* elem, const Item& item ); static void markSubTree( ItemSetElement* e ); static ItemSetElement* nextElem( ItemSetElement* e ); static ItemSetElement* prevElem( ItemSetElement* e ); static ItemSetElement* findInTree( ItemSetElement* elem, const Item &item ); public: /** Builds an empty list. */ ItemSet(): m_size(0), m_root(0), m_mark( 0xFFFFFFFF ), m_erasingIter(0), m_disposingElem(0) {} /** Clones a list. */ ItemSet( const ItemSet &l ); virtual ~ItemSet() { clear(); } /** Deletes the list. Items are shallowly destroyed. */ virtual ItemSet *clone() const; /** Gets the first item in the list. If the list is empty, you will crash, so use this only when the list is NOT empty. \return a reference to the first item in the list or a spectacular crash. */ virtual const Item &front() const; /** Gets the last item in the list. If the list is empty, you will crash, so use this only when the list is NOT empty. \return a reference to the last item in the list or a spectacular crash. */ virtual const Item &back() const; /** Gets the pointer to the first element for list traversal. The list element is just an item with previous and next pointers. If the list is empty, this method will return 0. \return the pointer to the first element pointer, or 0. */ ItemSetElement *first() const; /** Gets the pointer to the last element for list traversal. The list element is just an item with previous and next pointers. If the list is empty, this method will return 0. \return the pointer to the last element pointer, or 0. */ ItemSetElement *last() const; virtual void append( const Item& itm ) { insert( itm ); } virtual void prepend( const Item& itm ) { insert( itm ); } /** Removes all the elements in the list. */ virtual void clear(); /** Remove given element. If this is the last element of the list, the method returns 0, else it return the element that was following the delete element in the list, and that now has its place. \param elem an element from this list (or you'll witness psychedelic crashes) */ void erase( ItemSetElement *elem ); /** Finds an item and eventually returns the relative element. * This function is useful for direct deletion of an item, * or creation of an iterator at a given position. */ ItemSetElement* find( const Item &item ); /** Creates an iterator. * */ void getIteratorAt( Iterator &tgt, ItemSetElement* elem ); /** Insert an item after given before given element. To insert an item past the last element, use 0 as element pointer (last->next); this will work also to insert an item in an empty list. \param item the item to be inserted. */ void insert( const Item &item ); /** Tells if the list is empty. \return true if the list is empty. */ virtual bool empty() const { return m_root == 0; } /** Return the number of the items in the list. \return count of items in the list */ uint32 size() const { return m_size; } /** Perform marking of items stored in the list. */ virtual void gcMark( uint32 mark ); // Deletion criterion. virtual bool onCriterion( Iterator* elem ) const; //======================================================== // Iterator implementation. //======================================================== protected: virtual void getIterator( Iterator& tgt, bool tail = false ) const; virtual void copyIterator( Iterator& tgt, const Iterator& source ) const; virtual void insert( Iterator &iter, const Item &data ); virtual void erase( Iterator &iter ); virtual bool hasNext( const Iterator &iter ) const; virtual bool hasPrev( const Iterator &iter ) const; virtual bool hasCurrent( const Iterator &iter ) const; virtual bool next( Iterator &iter ) const; virtual bool prev( Iterator &iter ) const; virtual Item& getCurrent( const Iterator &iter ); virtual Item& getCurrentKey( const Iterator &iter ); virtual bool equalIterator( const Iterator &first, const Iterator &second ) const; }; } #endif /* end of itemset.h */ include/falcon/itemtraits.h000066400000000000000000000021611176363201700163070ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: itemtraits.h Traits for putting items in generic containers ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom ott 29 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Traits for putting items in generic containers */ #ifndef flc_itemtraits_H #define flc_itemtraits_H #include #include #include namespace Falcon { class VMachine; class FALCON_DYN_CLASS ItemTraits: public ElementTraits { public: virtual uint32 memSize() const; virtual void init( void *itemZone ) const; virtual void copy( void *targetZone, const void *sourceZone ) const; virtual int compare( const void *first, const void *second ) const; virtual void destroy( void *item ) const; virtual bool owning() const; }; namespace traits { extern ItemTraits &t_item(); } } #endif /* end of itemtraits.h */ include/falcon/iterator.h000066400000000000000000000211551176363201700157570ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: iterator.h Base abstract class for generic collection iterators. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 01 Aug 2009 23:24:09 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Base abstract class for generic collection iterators. */ #ifndef flc_iterator_H #define flc_iterator_H #include #include #include #include #include namespace Falcon { class Sequence; class CoreTable; class Garbageable; class Item; /** Base abstract class for generic Item sequence/collection iterators. Iterators and sequences cooperate so that: - The iterator provides a common non-abstract non-virtual interface to access sequentially sequences of different nature. - The underlying sequence provides the virtual functionalities needed to access itself and receive orders from its own iterators. A common usage pattern may be: \code // ItemArray() supports the sequence interface. Sequence* seq = &some_item.asArray()->items(); ... Iterator iter( seq ); // starts at begin. while( iter.hasCurrent() ) // while we have some element { Item& current = iter.getCurrent(); // ... do something iter.next() } \endcode Iterators support the FalconData interface, so they can be set directly as inner data for FalconObject (i.e. Falcon script level iterator instances), or be guarded by the garbage collector via the GCPointer garbage pointer shells. In that case, instead of having iterators in the stack, it's possible to have them in the heap (allocated with "new"). Iterators can be deleted via a simple delete. Iterators forward their gcMark() call to the sequence they are linked to, so if the sequence is part of a garbage sensible data (CoreArray, CoreObject, GCPointer etc.), the item stays alive untill all the iterators are gone. If a sequence is autonomously destroyed (i.e. via an explicit delete on a non-garbage sensible data), all the iterators on that sequence are "invalidated". Iterators are invalidated also when the sequence is changed so that the values of some or all iterators become no more valid (i.e. iterators to array or to paged maps). Invalidated iterators do not point to any sequence anymore, and calling any of their member except for disposal or isValid() causes a crash. Iterators are usually able to refer to the target sequence without the need for local structures. When this is not possible, their data member is filled with some structure specific iterator data, and their deletor() function is set to a function that can collect this deep reference data when the iterator is destroyed. As this function is totally independent from the source sequence, even deep iterators can be destroyed after their sequence has been destroyed, and they have been invalidated. If the iterator is destroyed while still valid (i.e. because of stack unroll), the Sequence::disposeIterator() callback method of the owner sequence is called; this gives the sequence the chance to de-account the iterator (and it's usually done in the Sequence base class). The disposeIterator() callback must NEVER destroy deep data in the iterator, which is done by the deletor() callback as a separate step. \note The Sequence-iterator pattern works well when there are a limited number of active iterator per sequence (1 to 3), which is a common practical usage in many cases. \b Avoid using an iterator-per-item strategy (i.e. back-referenced sequence items, able to delete themselves via an erase on their iterator). */ class FALCON_DYN_CLASS Iterator: public FalconData { public: typedef void(*t_deletor_func)(Iterator*); private: Sequence* m_owner; typedef union t_position_holder { int64 pos; void* ptr; } u_idata; u_idata m_idata; t_deletor_func m_deletor; Iterator* m_next; public: Iterator( Sequence* owner, bool tail = false ): m_owner( owner ), m_deletor(0), m_next(0) { m_idata.ptr = 0; owner->getIterator( *this, tail ); } Iterator( const Iterator& other ): m_owner( other.m_owner ), m_deletor( other.m_deletor ), m_next(0) { m_idata.ptr = 0; m_owner->copyIterator( *this, other ); } public: virtual ~Iterator(); virtual void gcMark( uint32 mark ); virtual Iterator *clone() const; /** Sets a deep deletor for this iterator. Should be called by the Sequence::getIterator and Sequence::copyIterator re-implementations in case this iterator is given a deep data that must be deleted at termination. If non-zero, this callback is called at iterator destruction. */ void deletor( t_deletor_func f ) { m_deletor = f; } t_deletor_func deletor() const { return m_deletor; } /** Advance to the next item. * \return false if the iterator couldn't be advanced (because invalid or at end). */ inline bool next() { fassert( m_owner != 0 ); return m_owner->next( *this ); } /** Retreat to the previous item. * \return false if the iterator couldn't be retreated (because invalid or at begin). */ inline bool prev() { fassert( m_owner != 0 ); return m_owner->prev( *this ); } inline bool hasNext() const { return m_owner != 0 && m_owner->hasNext( *this ); } inline bool hasPrev() const { return m_owner != 0 && m_owner->hasPrev( *this ); } /** True if this iterator has a current element. * * Iterators past the last element (at end) are still valid, but they can't be used * to access a current element. */ inline bool hasCurrent() const { return m_owner != 0 && m_owner->hasCurrent( *this ); } /** Must be called after an isValid() check */ inline Item &getCurrent() const { fassert( m_owner != 0 ); return m_owner->getCurrent( *this ); } /** Must be called after an isValid() check. * Calling this on non-dictionary sequences will cause an AccessError to be thrown. * */ inline Item &getCurrentKey() const { fassert( m_owner != 0 ); return m_owner->getCurrentKey( *this ); } inline bool isValid() const { return m_owner != 0; } inline bool isOwner( void *collection ) const { return collection == static_cast(m_owner); } inline bool equal( const Iterator &other ) const { fassert( m_owner != 0 ); return m_owner == other.m_owner && m_owner->equalIterator( *this, other ); } /** Erase an element pointed by this iterator. This erases the element in the sequence pointed by this iterator. the iterator is moved to the next element, or if this was the last element in the collection, it points to past-end. The operation may or may not invalidate the other iterators on the sequence, depending on the nature of the sequecne; however, the iterator itself it's granted to stay valid. */ inline void erase() { fassert( m_owner != 0 ); m_owner->erase( *this ); } /** Insert an item at current position in a non-dictionary sequence. Calling this on a dictionary sequence will cause an AccessError to be thrown. The item is insert before the position to which the iterator points. If the iterator isValid() but not hasCurrent(), insertion is at past end (append). After the insert, the iterator is not moved (still points to the same element). **/ inline void insert( const Item &item ) { fassert( m_owner != 0 ); m_owner->insert( *this, item ); } /** Turns this in an invalid iterator. * Notice that invalid iterators doesn't back-mark their owner. */ void invalidate() { m_owner = 0; } Sequence* sequence() const { return m_owner; } void sequence( Sequence* s ) { m_owner = s; } void *data() const { return m_idata.ptr; } void data( void* dt ) { m_idata.ptr = dt; } int64 position() const { return m_idata.pos; } void position( int64 pos ) { m_idata.pos = pos; } Iterator* nextIter() const { return m_next; } void nextIter( Iterator* n ) { fassert( n != this ); m_next = n; } inline void goTop() { m_owner->getIterator( *this ); } inline void goBottom() { m_owner->getIterator( *this, true ); } }; } #endif /* ITERATOR_H_ */ include/falcon/itm_deref.h000066400000000000000000000020511176363201700160560ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: itm_deref.h Inline implementation of Item::dereference( byte code ) ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu Feb 3 2005 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Inline implementation of Item::dereference( byte code ) As the pcodes may vary greatly and this inline function is needed only in a small set of internal files (i.e. vm_run.cpp), the declaration of this inline function has been moved to a separate file. */ #ifndef flc_itm_deref_H #define flc_itm_deref_H #include #include namespace Falcon { inline Item *Item::dereference( byte opcode ) const { if ( type() != FLC_ITEM_REFERENCE ) //|| opcode >= P_TRAV ) return this; return &asReference()->origin(); } } #endif /* end of itm_deref.h */ include/falcon/lineardict.h000066400000000000000000000103151176363201700162400ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_cdict.h Core dictionary and related utilities. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab dic 4 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Core dictionary and related utilities. */ #ifndef flc_lineardict_H #define flc_lineardict_H #include #include #include #include #define flc_DICT_GROWTH 16 namespace Falcon { class LinearDict; class VMachine; class Iterator; /** Class representing one element in a dictionary. */ class FALCON_DYN_CLASS LinearDictEntry { Item m_key; Item m_value; public: LinearDictEntry( const Item &k, const Item &v ): m_key( k ), m_value( v ) {} void key( const Item &v ) { m_key = v; } const Item &key() const { return m_key; } const Item &value() const { return m_value; } Item &key() { return m_key; } Item &value() { return m_value; } void value( const Item &v ) { m_value = v; } friend class LinearDict; }; class FALCON_DYN_CLASS LinearDict: public ItemDict { uint32 m_size; uint32 m_alloc; uint32 m_invalidPos; LinearDictEntry *m_data; uint32 m_mark; bool addInternal( uint32 pos, const Item &key, const Item &value ); /** Make a search in a standard dictionary. This function returns true if the required item is found, and false if the item is not found. In the first case ret_pos becomes the position in the dictionary array where the item is stored, while in the second case it is set to the best position where a new item with the not present key may be inserted. */ bool findInternal( const Item &key, uint32 &ret_pos ) const; bool removeAt( uint32 pos ); public: LinearDict(); LinearDict( uint32 prealloc ); ~LinearDict(); virtual LinearDict *clone() const; virtual void gcMark( uint32 gen ); virtual uint32 length() const; virtual Item *find( const Item &key ) const; virtual bool findIterator( const Item &key, Iterator &iter ); virtual const Item &front() const; virtual const Item &back() const; virtual void append( const Item& item ); virtual void prepend( const Item& item ); virtual bool remove( const Item &key ); virtual void put( const Item &key, const Item &value ); virtual void smartInsert( const Iterator &iter, const Item &key, const Item &value ); virtual void merge( const ItemDict &dict ); virtual void clear(); virtual bool empty() const; uint32 esize( uint32 num ) const { return sizeof( LinearDictEntry ) * num; } LinearDictEntry *entries() const { return m_data; } uint32 allocated() const { return m_alloc; } void length( uint32 size ) { m_size = size; } void allocated( uint32 size ) { m_alloc = size; } void entries( LinearDictEntry *d ) { m_data = d; } LinearDictEntry *elementAt( uint32 pos ) const { if ( pos >= length() ) return 0; return entries() + pos; } bool find( const Item &key, uint32 &ret_pos ) const { return findInternal( key, ret_pos ); } //======================================================== // Iterator implementation. //======================================================== protected: virtual void getIterator( Iterator& tgt, bool tail = false ) const; virtual void copyIterator( Iterator& tgt, const Iterator& source ) const; virtual void insert( Iterator &iter, const Item &data ); virtual void erase( Iterator &iter ); virtual bool hasNext( const Iterator &iter ) const; virtual bool hasPrev( const Iterator &iter ) const; virtual bool hasCurrent( const Iterator &iter ) const; virtual bool next( Iterator &iter ) const; virtual bool prev( Iterator &iter ) const; virtual Item& getCurrent( const Iterator &iter ); virtual Item& getCurrentKey( const Iterator &iter ); virtual bool equalIterator( const Iterator &first, const Iterator &second ) const; virtual bool onCriterion( Iterator* elem ) const; }; } #endif /* end of flc_cdict.h */ include/falcon/linemap.h000066400000000000000000000021161176363201700155470ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: linemap.h Line map with module serialization support. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom ago 21 2005 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Line map with module serialization support -header file-. */ #ifndef flc_linemap_H #define flc_linemap_H #include #include namespace Falcon { class Stream; /** Line information map. The aim of this class is to map a certain code position with a line number in the source file, so to allow debugging of the modules. (int32,int32) */ class FALCON_DYN_CLASS LineMap: public Map { public: LineMap(); ~LineMap() {} void addEntry( uint32 pcounter, uint32 line ) { insert( &pcounter, &line ); } bool save( Stream *out ) const; bool load( Stream *in ); }; } #endif /* end of linemap.h */ include/falcon/livemodule.h000066400000000000000000000142411176363201700162710ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: livemodule.h The Representation of module live data once linked in a VM ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 03 Apr 2009 23:09:55 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file The Representation of module live (dynamic) data once linked in a VM. */ #ifndef FALCON_LIVEMODULE_H #define FALCON_LIVEMODULE_H #include #include #include #include #include #include #include #include #include namespace Falcon { /** Instance of a live module entity. The VM sees modules as a closed, read-only entity. Mutable data in a module is actually held in a per-module map in the VM. This class helds a reference to a module known by the VM and to the variable data known in the module. A module can be entered only once in the VM, and it is uniquely identified by a name. This class acts also as a weak reference between live callable items and modules. When a live module is unlinked, the contents of this class are zeroed and every callable item referencing this module becomes a nil as isCallable() gets called. The live module keeps also track of strings taken from the underlying module string table and injected in the live VM. In this way, the strings become independent from the underlying module, that can be unloaded while still sharing string data with the host VM. This object is garbageable; it gets referenced when it's in the module map and by items holding a callable in this module. When a module is unlinked, the LiveModule may be destroyed when it is not referenced in garbage anymore, or at VM termination. \note Although this is a Garbageable item, and as such, it resides in the memory pool, there isn't any need for precise accounting of related memory, as globals are allocated and destroyed when the module is linked or unlinked, and not (necessarily) when this item is collected. As the memory associated with this object is separately reclaimed by detachModule(), and it cannot possibly be reclaimed during a collection loop, there is no meaning in accounting it. */ class FALCON_DYN_CLASS LiveModule: public Garbageable { Module *m_module; mutable CoreString** m_strings; mutable uint32 m_strCount; mutable uint32 m_aacc; mutable int32 m_iacc; ItemArray m_globals; ItemArray m_wkitems; bool m_bPrivate; bool m_bAlive; bool m_needsCompleteLink; ItemArray m_userItems; public: typedef enum { init_none, init_trav, init_complete } t_initState; private: t_initState m_initState; public: LiveModule( Module *mod, bool bPrivate=false ); ~LiveModule(); virtual bool finalize(); const Module *module() const { return m_module; } const ItemArray &globals() const { return m_globals; } ItemArray &globals() { return m_globals; } const ItemArray &wkitems() const { return m_wkitems; } ItemArray &wkitems() { return m_wkitems; } /** Just a shortcut to the name of the module held by this LiveModule. */ const String &name() const { return m_module->name(); } /** Disengage a module after a module unlink. */ void detachModule(); /** Is this module still alive? Short for this->module() != 0 \return true if the module is still alive. */ bool isAlive() const { return m_bAlive; } /** Return a module item given a global symbol name. This is an utility funtion retreiving an global item declared by the module that is referenced in this live data. \param symName the name of the global symbol for which this item must be found \return 0 if not found or a pointer to the item which is indicated by the symbol */ Item *findModuleItem( const String &symName ) const; /** Returns the privacy status of this module. If the module is not private, the VM will export all the exported symbol to the global namespace at link time. \return true if this module is private, and so, if didn't export any symbol. */ bool isPrivate() const { return m_bPrivate; } /** Changes the privacy status of this module. If changing private to public, the VM funcrion exporting public symbols must be separately called. */ void setPrivate( bool mode ) { m_bPrivate = mode; } t_initState initialized() const { return m_initState; } void initialized( t_initState tis ) { m_initState = tis; } /** Return the string in the module with the given ID. */ String* getString( uint32 stringId ) const; /** True if this module requires a second link step. */ bool needsCompleteLink() const { return m_needsCompleteLink; } /** True if this module requires a second link step. */ void needsCompleteLink( bool l ) { m_needsCompleteLink = l; } void gcMark( uint32 mark ); /** Returns the user items of this live modules. * * Modules often need a module-global storage. However, the virtual * machine doesn't offer this space, and using global variables requires * a bit of gym to get the variable ID and the item in the live * module global array. * * The module functions, at extension level (i.e. the C++ implementations) * can take advantage of this item array which is dedicated to them. * * The array is also autonomously GC marked. */ ItemArray& userItems() { return m_userItems; } const ItemArray& userItems() const { return m_userItems; } }; class LiveModulePtrTraits: public ElementTraits { public: virtual ~LiveModulePtrTraits() {} virtual uint32 memSize() const; virtual void init( void *itemZone ) const; virtual void copy( void *targetZone, const void *sourceZone ) const; virtual int compare( const void *first, const void *second ) const; virtual void destroy( void *item ) const; virtual bool owning() const; }; } #endif /* end of livemodule.h */ include/falcon/llist.h000066400000000000000000000064331176363201700152570ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: llist.h Inlined linked list templates. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven dic 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Inlined linked list templates. */ #ifndef FLC_LLIST_H #define FLC_LLIST_H #include namespace Falcon { /** Element for LinkedList class */ class LinkableElement { LinkableElement *m_next; LinkableElement *m_prev; public: LinkableElement( LinkableElement *prev=0, LinkableElement *next=0 ): m_next( next ), m_prev( prev ) {} LinkableElement *next() const { return m_next; } void next( LinkableElement *n ) { m_next = n; } LinkableElement *prev() const { return m_prev; } void prev( LinkableElement *p ) { m_prev = p; } }; /** Linked list class. This class is quite similar to a STL linked list; the main difference is that the elements that are in the list have the linked list pointers inside them; this is useful when is not advisable to have external pointers holding the items, and the control of allocated memory must be inside the linked item themselves. Class _T must be a subclass of Falcon::LinkableElement or the compilation of this module will fail. */ template< class _T > class LinkedList { LinkableElement *m_head; LinkableElement *m_tail; uint32 m_size; public: LinkedList(): m_head(0), m_tail(0), m_size(0) {} void push_front( _T *e ) { LinkableElement *elem = static_cast< LinkableElement *>( e ); elem->next( m_head ); elem->prev(0); m_head = elem; if( m_tail == 0 ) m_tail = elem; m_size++; } void push_back( _T *e ) { LinkableElement *elem = static_cast< LinkableElement *>( e ); elem->next( 0 ); elem->prev( m_tail ); m_tail = elem; if( m_head == 0 ) m_head = elem; m_size++; } _T *front() { return static_cast<_T *>( m_head ); } _T *back() { return static_cast<_T *>( m_tail ); } _T *pop_front() { _T *h = static_cast<_T *>( m_head ); if (m_head != 0 ) { m_head = m_head->next(); if ( m_head != 0 ) m_head->prev(0); else m_tail = 0; m_size--; } return h; } _T *pop_back() { _T *t = static_cast<_T *>( m_tail ); if (m_tail != 0 ) { m_tail = m_tail->prev(); if ( m_tail != 0 ) m_tail->next(0); else m_head = 0; m_size--; } return t; } void remove( _T * e ) { LinkableElement *elem = static_cast< LinkableElement *>( e ); if ( m_size == 0 ) return; if ( elem == m_head ) { m_head = elem->next(); } else { elem->prev()->next( elem->next() ); } if ( elem == m_tail ) { m_tail = elem->prev(); } else { elem->next()->prev( elem->prev() ); } m_size--; } uint32 size() const { return m_size; } bool empty() const { return m_size == 0; } }; } #endif /* end of llist.h */ include/falcon/ltree.h000066400000000000000000000042001176363201700152310ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: ltree.h Inlined tree linked list templates. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat Feb 26 08:45:59 CET 2005 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Inlined linked list templates. */ #ifndef FLC_LTREE_H #define FLC_LTREE_H #include #include #include namespace Falcon { class StrongList; /** Element for StrongList class */ class FALCON_DYN_CLASS SLElement: public BaseAlloc { SLElement *m_next; SLElement *m_prev; StrongList *m_parent; public: SLElement( SLElement *prev=0, SLElement *next=0 ): m_next( next ), m_prev( prev ), m_parent(0) {} SLElement( StrongList *parent, SLElement *prev=0, SLElement *next=0 ): m_next( next ), m_prev( prev ), m_parent(parent) {} SLElement *next() const { return m_next; } void next( SLElement *n ) { m_next = n; } SLElement *prev() const { return m_prev; } void prev( SLElement *p ) { m_prev = p; } StrongList *owner() const { return m_parent; } void owner( StrongList *lt ) { m_parent = lt; } void remove(); }; /** Strong linked list class. This is similar to a linked list in which each element has a link to its owner. */ class FALCON_DYN_CLASS StrongList: public BaseAlloc { SLElement *m_head; SLElement *m_tail; uint32 m_size; public: StrongList(): m_head(0), m_tail(0), m_size(0) {} void push_front( SLElement *e ); void push_back( SLElement *e ); SLElement *front() const { return m_head; } SLElement *back() const { return m_tail; } SLElement *pop_front(); SLElement *pop_back(); void remove( SLElement *e ); uint32 size() const { return m_size; } bool empty() const { return m_size == 0; } }; inline void SLElement::remove() { if (m_parent!=0) m_parent->remove( this ); } } #endif /* end of llist.h */ include/falcon/memblock.h000066400000000000000000000014571176363201700157220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_memblock.h Managed block abstraction. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: gio ott 7 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Short description */ #ifndef flc_flc_memblock_H #define flc_flc_memblock_H namespace Falcon { class MemBlock { MemBlock *m_prev; MemBlock *m_next; public: MemBlock( MemBlock *prev = 0, MemBlock *next=0 ): m_prev(prev), m_next(next) {} void *memory() { return ( void *) ((char *)this)+sizeof(this); } }; } #endif /* end of flc_memblock.h */ include/falcon/membuf.h000066400000000000000000000236051176363201700154030ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: membuf.h Core memory buffer. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 17 Mar 2008 23:07:21 +0100 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Memory buffer - Pure memory for Falcon. */ #ifndef flc_membuf_H #define flc_membuf_H #include #include #include #include #include namespace Falcon { class VMachine; class Stream; class CoreObject; class FALCON_DYN_CLASS MemBuf: public DeepItem, public Garbageable { public: typedef void (*tf_deletor)( void* memory ); enum { INVALID_MARK = 0xFFFFFFFF, MAX_LEN = 0xFFFFFFFF } t_enum_mark; protected: byte *m_memory; uint32 m_length; uint32 m_mark; uint32 m_limit; uint32 m_position; uint16 m_wordSize; uint16 m_byteOrder; CoreObject *m_dependant; tf_deletor m_deletor; public: /** Creates a pure memory based memory buffer. * * This constructor creates a memory buffer containing a memory data that * will be freed at buffer destruction. */ MemBuf( uint32 ws, uint32 length ); /** Creates an unowned memory buffer. * * This simply stores the data in this memory buffer; * the ownership of the data stays on the creator, which must dispose it * separately. * * \param ws Size of each element in bytes (1-4) * \param data The memory buffer * \param length Maximum length of the buffer; pass MAX_LEN if unknown, or * 0 if unknown and unavailable to the calling program. */ MemBuf( uint32 ws, byte *data, uint32 length ); /** Creates an owned memory buffer. * * This stores the memory in the memory buffer and associates it with * a deletor function which will be called when this MemBuf is disposed of. * the ownership of the data stays on the creator, which must dispose it * separately. * * \param ws Size of each element in bytes (1-4) * \param data The memory buffer * \param length Maximum length of the buffer; pass MAX_LEN if unknown, or * 0 if unknown and unavailable to the calling program. * \param deletor The deletor for the given data. Pass memFree for data * created with memAlloc, or 0 to make data unowned. */ MemBuf( uint32 ws, byte *data, uint32 length, tf_deletor deletor ); /** * Destructor. * * The destructor will also destroy the associated data if a deletor * function has been specified in the constructor. */ virtual ~MemBuf(); uint16 wordSize() const { return m_wordSize; } uint32 length() const { return m_length; } virtual uint32 get( uint32 pos ) const = 0; virtual void set( uint32 pos, uint32 value ) = 0; uint32 position() const { return m_position; } uint32 getMark() const { return m_mark; } uint32 limit() const { return m_limit; } /** Puts a value at current position and advance. Will throw AccessError on error. This is meant to be called from scripts. */ void put( uint32 data ) { if ( m_position >= length() ) throw new AccessError( ErrorParam( e_arracc, __LINE__ ).module( __FILE__ ).symbol( "put" ).extra( "MemBuf" ) ); set( m_position++, data ); } /** Gets a value at current position and advance. Will throw AccessError on error. This is meant to be called from scripts. */ uint32 get() { if ( m_position >= m_limit ) throw new AccessError( ErrorParam( e_arracc, __LINE__ ).module( __FILE__ ).symbol( "get" ).extra( "MemBuf" ) ); return get( m_position++ ); } /** Returns the size of the buffer in bytes. It's the length of the buffer * the word size. */ uint32 size() const { return m_length * m_wordSize; } /** Resizes the Memory Buffer Invariants are maintained. \note This method uses memRealloc, so it can be used only if the memory buffer has been created through memAlloc and should be released with memFree. The method doesn't perform any check, so be sure that you're knowing what you're doing when you use it. \param newSize number of items stored. */ void resize( uint32 newSize ); /** Returns the raw buffer data. */ byte *data() const { return m_memory; } /** Sets the limit of read operations. */ void limit( uint32 l ) { if ( l > length() ) throw new AccessError( ErrorParam( e_arracc, __LINE__ ).module( __FILE__ ).symbol( "limit" ).extra( "MemBuf" ) ); m_limit = l; if ( l < m_position ) m_position = l; } /** Set current read-write position. Will throw if the limit is less than the current length(). */ void position( uint32 p ) { if ( p > m_limit ) throw new AccessError( ErrorParam( e_arracc, __LINE__ ).module( __FILE__ ).symbol( "position" ).extra( "MemBuf" ) ); m_position = p; if ( m_mark < m_position ) m_mark = INVALID_MARK; } /** Mark a position for a subquesent reset. A subquesent reset() will bring the position() pointer here. */ void placeMark( uint32 m ) { if ( m > m_position ) throw new AccessError( ErrorParam( e_arracc, __LINE__ ).module( __FILE__ ).symbol( "mark" ).extra( "MemBuf" ) ); m_mark = m; } /** Mark current position. Records the position() as it is now. A subquesent reset() will bring the position() pointer here. */ void placeMark() { m_mark = m_position; } /** Returns the pointer at previous mark. Raises an access error if the mark is not set. */ void reset() { if ( m_mark == INVALID_MARK ) throw new AccessError( ErrorParam( e_arracc, __LINE__ ).module( __FILE__ ).symbol( "reset" ).extra( "MemBuf" ) ); m_position = m_mark; } /** Rewinds the membuf The position is set to 0 and the mark is invalidated. */ void rewind() { m_position = 0; m_mark = INVALID_MARK; } /** Rewinds the membuf The position is set to 0 and the mark is invalidated. Limit is set to size. */ void clear() { m_position = 0; m_limit = m_length; m_mark = INVALID_MARK; } /** After a write, prepares for a read. Useful to parse an incoming buffer or to drop the incoming buffer in another place. */ void flip() { m_limit = m_position; m_position = 0; m_mark = INVALID_MARK; } /** Compacts the buffer and prepares it for a new read. Remaining data, if any, is moved to the beginning of the buffer. Position is set to the previous limit, and limit is set to length. The mark is discarded. */ void compact(); /** Return the amount of items that can be taken before causing an exception. */ uint32 remaining() const { return m_limit - m_position; } /** Sets the amount of items in this buffer. The actual size in bytes is obtained multiplying this length by the word size. */ void length( uint32 s ) { m_length = s; } void setData( byte *data, uint32 length, tf_deletor deletor = 0 ); /** Return the CoreObject that stores vital data for this mempool. \see void dependant( CoreObject *g ) */ CoreObject *dependant() const { return m_dependant; } /** Links this data to a CoreObject. Some object may provide MemBuf properties to access data or data portion stored in some part of the user_data they reflect. To make this possible and easy, the MemBuf can be given a back reference to the object that created it. In this way, the object will be granted to stay alive as long as the MemBuf is alive. */ void dependant( CoreObject *g ) { m_dependant = g; } virtual bool serialize( Stream *stream, bool bLive = false ) const; static MemBuf *deserialize( VMachine *vm, Stream *stream ); /** Creates a membuf with defined wordsize. The length parameter is the final element count; it gets multiplied by nWordSize. */ static MemBuf *create( int nWordSize, uint32 length ); virtual MemBuf* clone() const; virtual void gcMark( uint32 gen ); virtual void readProperty( const String &, Item &item ); virtual void writeProperty( const String &, const Item &item ); virtual void readIndex( const Item &pos, Item &target ); virtual void writeIndex( const Item &pos, const Item &target ); }; class FALCON_DYN_CLASS MemBuf_1: public virtual MemBuf { public: MemBuf_1( uint32 length ): MemBuf( 1, length ) {} MemBuf_1( byte *data, uint32 length, tf_deletor deletor = 0 ): MemBuf( 1, data, length, deletor ) {} virtual uint32 get( uint32 pos ) const; virtual void set( uint32 pos, uint32 value ); }; class FALCON_DYN_CLASS MemBuf_2: public virtual MemBuf { public: MemBuf_2( uint32 length ): MemBuf( 2, length ) {} MemBuf_2( byte *data, uint32 length, tf_deletor deletor = 0 ): MemBuf( 2, data, length, deletor ) {} virtual uint32 get( uint32 pos ) const; virtual void set( uint32 pos, uint32 value ); }; class FALCON_DYN_CLASS MemBuf_3: public virtual MemBuf { public: MemBuf_3( uint32 length ): MemBuf( 3, length ) {} MemBuf_3( byte *data, uint32 length, tf_deletor deletor = 0 ): MemBuf( 3, data, length, deletor ) {} virtual uint32 get( uint32 pos ) const; virtual void set( uint32 pos, uint32 deletor ); }; class FALCON_DYN_CLASS MemBuf_4: public virtual MemBuf { public: MemBuf_4( uint32 length ): MemBuf( 4, length ) {} MemBuf_4( byte *data, uint32 length, tf_deletor deletor = 0 ): MemBuf( 4, data, length, deletor ) {} virtual uint32 get( uint32 pos ) const; virtual void set( uint32 pos, uint32 value ); }; } #endif /* end of membuf.h */ include/falcon/memory.h000066400000000000000000000053421176363201700154360ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_memory.h User overridable basic memory allocation ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar ago 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef flc_MEMORY_H #define flc_MEMORY_H #include #include #include /** \file This files contains the user overridable memory allocation systems. Memory is alwas allocated via memAlloc() and released via memFree() function pointers. These are initially set at DflMemAlloc() and DflMemFree() functions, but they may be overridden with functions of the same kind at every point. Falcon memory management is twofold: Memory used for internal storage (i.e. module loading, symbol tables, file names, configurations etc) is not accounted nor controlled, and never garbaged; the only control is during allocation and releasing via the basic functions, which print an error message and quit in case of allocation errors. Items used by the VM (generated by scripts or returned to scripts by extensions) are created via the MemPool object; the memory allocated by this object is stored in the memory pool and may be garbaged, recycled or corrected. In either case, Falcon never makes any assumption on the given memory. The embedding application may safely add any kind of memory management & accounting it likes. */ namespace Falcon { /** Account allocated memory. Allocators creating Falcon object should call this function to inform the GC system of the memory they are consuming, and so, the memory that may be freed if they are reclaimed. Global functions Falcon::gcAlloc(), Falcon::gcRealloc() and Falcon::gcFree() call automatically this account function. Garbageable objects are derived by a base class that automatically calls this functions. */ FALCON_DYN_SYM void gcMemAccount( size_t memSize ); FALCON_DYN_SYM void gcMemUnaccount( size_t memSize ); /** Call once Falcon is shut down. */ FALCON_DYN_SYM void gcMemShutdown(); /** Return the total memory allocated by the GC system. */ FALCON_DYN_SYM size_t gcMemAllocated(); FALCON_DYN_SYM void * DflMemAlloc( size_t amount ); FALCON_DYN_SYM void DflMemFree( void *mem ); FALCON_DYN_SYM void * DflMemRealloc( void *mem, size_t amount ); FALCON_DYN_SYM void * DflAccountMemAlloc( size_t amount ); FALCON_DYN_SYM void DflAccountMemFree( void *mem ); FALCON_DYN_SYM void * DflAccountMemRealloc( void *mem, size_t amount ); } #endif /* end of flc_memory.h */ include/falcon/mempool.h000066400000000000000000000222301176363201700155710ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: mempool.h garbage basket class ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 08 Feb 2009 16:08:50 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_MEMPOOL_H #define FALCON_MEMPOOL_H /** \file Garbage basket holder. */ #include #include #include #include #include namespace Falcon { class Garbageable; class GarbageableBase; class GarbageLock; /** Storage pit for garbageable data. Garbage items can be removed acting directly on them. */ #if 0 class FALCON_DYN_CLASS PoolRing: public BaseAlloc { Garbageable* m_head; public: PoolRing(); ~PoolRing(); /** Adds a garbageable item to this pool ring. */ void add( Garbageable * ); void transfer( PoolRing *target ); Garbageable *linearize(); }; #endif /** Falcon Memory pool The garbage basket is the Falcon standard memory allocator. It provides newly created objects and memory chunks and saves them for later recycle. The garbage collector moves unused items and memory chunks to the basket holder. Is then responsibility of the holder to decide what to do about them. The memory pool is responsible for: - Allocating new memory or use recycled memory. - Destroy memory or save block in the recycle bins for later use. - Keep a track of all "live" object so that the garbage collector can detect unused memory, and memory leaks can be detected. */ class FALCON_DYN_CLASS MemPool: public Runnable, public BaseAlloc { protected: size_t m_thresholdNormal; size_t m_thresholdActive; /** Minimal generation. Items marked with generations lower than this are killed. */ uint32 m_mingen; /** Alive and possibly collectable items are stored in this ring. */ GarbageableBase *m_garbageRoot; /** Newly created and unreclaimable items are stored in this ring. */ GarbageableBase *m_newRoot; /** Used to block prevent the pool from grabbing new garbage. */ bool m_bNewReady; /** The machine with the oldest generation loop checked out. */ VMachine *m_olderVM; /** Ring of VMs */ VMachine *m_vmRing; int32 m_vmCount; /** List of VM in idle state and waiting to be inspected */ VMachine *m_vmIdle_head; VMachine *m_vmIdle_tail; // for gc uint32 m_generation; int32 m_allocatedItems; uint32 m_allocatedMem; SysThread *m_th; bool m_bLive; Event m_eRequest; Mutex m_mtxa; /** Mutex for newly created items ring. - GarbageableBase::nextGarbage() - GarbageableBase::prevGarbage() - m_generation - m_newRoot - rollover() \note This mutex is acquired once while inside m_mtx_vms.lock() */ mutable Mutex m_mtx_newitem; /** Mutex for the VM ring structure. - VMachine::m_nextVM - VMachine::m_prevVM - m_vmRing - electOlderVM() -- call guard - advanceGeneration() */ Mutex m_mtx_vms; /** Mutex for the idle VM list structure. Guards the linked list of VMs being in idle state. - VMachine::m_idleNext - VMachine::m_idlePrev - m_vmIdle_head - m_vmIdle_tail */ Mutex m_mtx_idlevm; /** Guard for ramp modes. */ mutable Mutex m_mtx_ramp; mutable Mutex m_mtx_gen; RampMode* m_ramp[RAMP_MODE_COUNT]; RampMode* m_curRampMode; int m_curRampID; Mutex m_mtxRequest; Event m_eGCPerformed; bool m_bRequestSweep; /** Mutex for locked items ring. */ Mutex m_mtx_lockitem; /** Locked and non reclaimable items are stored in this ring. */ GarbageLock *m_lockRoot; /** Generation at which locked items have been marked. * * */ uint32 m_lockGen; //================================================== // Private functions //================================================== bool markVM( VMachine *vm ); void gcSweep(); /* To reimplement this, we need to have anti-recursion checks on item, which are currently being under consideration. However, I would prefer not to need to have this functions back, as they were meant to be used when the memory model wasn't complete. In other words, I want items to be in garbage as soon as they are created, and to exit when they are destroyed. void removeFromGarbage( String *ptr ); void removeFromGarbage( Garbageable *ptr ); void storeForGarbageDeep( const Item &item ); void removeFromGarbageDeep( const Item &item ); */ void clearRing( GarbageableBase *ringRoot ); void rollover(); void remark(uint32 mark); void electOlderVM(); // to be called with m_mtx_vms locked void promote( uint32 oldgen, uint32 curgen ); void advanceGeneration( VMachine* vm, uint32 oldGeneration ); void markLocked(); friend class GarbageLock; void addGarbageLock( GarbageLock* lock ); void removeGarbageLock( GarbageLock* lock ); public: enum constants { MAX_GENERATION = 0xFFFFFFFE, SWEEP_GENERATION = 0xFFFFFFFF }; /** Builds a memory pool. Initializes all element at 0 and set buffer sizes to the FALCON default. */ MemPool(); /** Destroys all the items. Needless to say, this must be called outside any VM. */ virtual ~MemPool(); /** Called upon creation of a new VM. This sets the current generation of the VM so that it is unique among the currently living VMs. */ void registerVM( VMachine *vm ); /** Called before destruction of a VM. Takes also care to disengage the VM from idle VM list. */ void unregisterVM( VMachine *vm ); /** Marks an item during a GC Loop. This method should be called only from inside GC mark callbacks of class having some GC hook. */ void markItem( const Item &itm ); /** Returns the number of elements managed by this mempool. */ int32 allocatedItems() const; /** Returns the current generation. */ uint32 generation() const { return m_generation; } /* void generation( uint32 i ); uint32 incgen(); */ /** Stores a garbageable instance in the pool. Called by the Garbageable constructor to ensure accounting of this item. */ void storeForGarbage( Garbageable *ptr ); virtual void* run(); /** Starts the parallel garbage collector. */ void start(); /** Stops the collector. The function synchronously wait for the thread to exit and sets it to 0. */ void stop(); /** Turns the GC safe allocation mode on. In case an "core" class object (like CoreObject, CoreDict, CoreArray, CoreString and so on) needs to be declared in a place where it cannot be granted that there is a working virtual machine, it is necessary to ask the Garbage Collector not to try to collect newly allocated data. When core data is allocated inside a running VM, the GC ensures that the data cannot be ripped away before it reaches a safe area in a virtual machine; but modules or embedding applications may will to allocate garbage sensible data without any chance to control the idle status of the running virtual machines. To inform the GC about this fact, the safeArea(); / unsafeArea() functions are provided. Data should be assigned to a virtual machine or alternatively garbage locked before unsafeArea() is called to allow the Garbage Collector to proceed normally. */ void safeArea(); /** Allows VM to proceed in checking newly allocated data. \see safeArea() */ void unsafeArea(); /** Declares the given VM idle. The VM may be sent to the the main memory pool garbage collector mark loop if it is found outdated and in need of a new marking. Set prio = true if the VM requests a priority GC. In that case, the VM must present itself non-idle, and the idle-ship is taken implicitly by the GC. The VM is notified with m_eGCPerformed being set after the complete loop is performed. */ void idleVM( VMachine *vm, bool bPrio = false ); /** Sets the normal threshold level. */ void thresholdNormal( size_t mem ) { m_thresholdNormal = mem; } /** Sets the active threshold level. */ void thresholdActive( size_t mem ) { m_thresholdActive = mem; } size_t thresholdNormal() const { return m_thresholdNormal; } size_t thresholdActive() const { return m_thresholdActive; } /** Sets the algorithm used to dynamically configure the collection levels. Can be one of: - RAMP_MODE_STRICT_ID - RAMP_MODE_LOOSE_ID - RAMP_MODE_SMOOTH_SLOW_ID - RAMP_MODE_SMOOTH_FAST_ID Or RAMP_MODE_OFF to disable dynamic auto-adjust of collection levels. \param mode the mode to be set. \return true if the mode can be set, false if it is an invalid value. */ bool rampMode( int mode ); int rampMode() const; /** Alter the count of live items. For internal use. */ void accountItems( int itemCount ); void performGC(); }; } #endif /* end of mempool.h */ include/falcon/mersennetwister.h000066400000000000000000000355641176363201700173750ustar00rootroot00000000000000// MersenneTwister.h // Mersenne Twister random number generator -- a C++ class MTRand // Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus // Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com // The Mersenne Twister is an algorithm for generating random numbers. It // was designed with consideration of the flaws in various other generators. // The period, 2^19937-1, and the order of equidistribution, 623 dimensions, // are far greater. The generator is also fast; it avoids multiplication and // division, and it benefits from caches and pipelines. For more information // see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html // Reference // M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally // Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on // Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. // Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, // Copyright (C) 2000 - 2003, Richard J. Wagner // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. The names of its contributors may not be used to endorse or promote // products derived from this software without specific prior written // permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // The original code included the following notice: // // When you use this, send an email to: matumoto@math.keio.ac.jp // with an appropriate reference to your work. // // It would be nice to CC: rjwagner@writeme.com and Cokus@math.washington.edu // when you write. #ifndef MERSENNETWISTER_H #define MERSENNETWISTER_H // Not thread safe (unless auto-initialization is avoided and each thread has // its own MTRand object) #include #include #include #include class FALCON_DYN_CLASS MTRand { // Data public: typedef Falcon::uint32 uint32; typedef Falcon::int32 int32; typedef Falcon::uint64 uint64; enum { N = 624 }; // length of state vector enum { SAVE = N + 1 }; // length of array for save() protected: enum { M = 397 }; // period parameter uint32 state[N]; // internal state uint32 *pNext; // next value to get from state int left; // number of values left before reload needed //Methods public: MTRand( const uint32& oneSeed ); // initialize with a simple uint32 MTRand( uint32 *const bigSeed, uint32 const seedLength = N ); // or an array MTRand(); // auto-initialize with /dev/urandom or time() and clock() MTRand(const MTRand&); // prevent copy constructor MTRand& operator=(const MTRand&); // no-op operator= // Do NOT use for CRYPTOGRAPHY without securely hashing several returned // values together, otherwise the generator state can be learned after // reading 624 consecutive values. // Access to 32-bit random numbers double rand(); // real number in [0,1] double rand( const double& n ); // real number in [0,n] double randExc(); // real number in [0,1) double randExc( const double& n ); // real number in [0,n) double randDblExc(); // real number in (0,1) double randDblExc( const double& n ); // real number in (0,n) uint32 randInt(); // integer in [0,2^32-1] uint32 randInt( const uint32& n ); // integer in [0,n] for n < 2^32 double operator()() { return rand(); } // same as rand() // uint64 randomness especially for Falcon uint64 randInt64(); uint64 randInt64( const uint64& n ); // integer in [0,n] for n < 2^64 // Access to 53-bit random numbers (capacity of IEEE double precision) double rand53(); // real number in [0,1) // Access to nonuniform random number distributions double randNorm( const double& mean = 0.0, const double& variance = 0.0 ); // Re-seeding functions with same behavior as initializers void seed( const uint32 oneSeed ); void seed( uint32 *const bigSeed, const uint32 seedLength = N ); void seed(); // Saving and loading generator state void save( uint32* saveArray ) const; // to array of size SAVE void load( uint32 *const loadArray ); // from such array /* Avoid dependency from STL for Falcon friend std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ); friend std::istream& operator>>( std::istream& is, MTRand& mtrand ); */ protected: void initialize( const uint32 oneSeed ); void reload(); uint32 hiBit( const uint32& u ) const { return u & 0x80000000UL; } uint32 loBit( const uint32& u ) const { return u & 0x00000001UL; } uint32 loBits( const uint32& u ) const { return u & 0x7fffffffUL; } uint32 mixBits( const uint32& u, const uint32& v ) const { return hiBit(u) | loBits(v); } uint32 twist( const uint32& m, const uint32& s0, const uint32& s1 ) const { return m ^ (mixBits(s0,s1)>>1) ^ uint32(-(int32)(loBit(s1) & 0x9908b0dfUL)); } static uint32 hash( time_t t, clock_t c ); }; inline MTRand::MTRand(const MTRand&) { seed(); } inline MTRand& MTRand::operator=(const MTRand&) { return *this; } inline MTRand::MTRand( const uint32& oneSeed ) { seed(oneSeed); } inline MTRand::MTRand( uint32 *const bigSeed, const uint32 seedLength ) { seed(bigSeed,seedLength); } inline MTRand::MTRand() { seed(); } inline double MTRand::rand() { return double(randInt()) * (1.0/4294967295.0); } inline double MTRand::rand( const double& n ) { return rand() * n; } inline double MTRand::randExc() { return double(randInt()) * (1.0/4294967296.0); } inline double MTRand::randExc( const double& n ) { return randExc() * n; } inline double MTRand::randDblExc() { return ( double(randInt()) + 0.5 ) * (1.0/4294967296.0); } inline double MTRand::randDblExc( const double& n ) { return randDblExc() * n; } inline double MTRand::rand53() { uint32 a = randInt() >> 5, b = randInt() >> 6; return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); // by Isaku Wada } inline MTRand::uint64 MTRand::randInt64() { return (uint64(randInt()) << 32) | randInt(); } inline double MTRand::randNorm( const double& mean, const double& variance ) { // Return a real number from a normal (Gaussian) distribution with given // mean and variance by Box-Muller method double r = sqrt( -2.0 * log( 1.0-randDblExc()) ) * variance; double phi = 2.0 * 3.14159265358979323846264338328 * randExc(); return mean + r * cos(phi); } inline MTRand::uint32 MTRand::randInt() { // Pull a 32-bit integer from the generator state // Every other access function simply transforms the numbers extracted here if( left == 0 ) reload(); --left; register uint32 s1; s1 = *pNext++; s1 ^= (s1 >> 11); s1 ^= (s1 << 7) & 0x9d2c5680UL; s1 ^= (s1 << 15) & 0xefc60000UL; return ( s1 ^ (s1 >> 18) ); } inline MTRand::uint32 MTRand::randInt( const uint32& n ) { // Find which bits are used in n // Optimized by Magnus Jonsson (magnus@smartelectronix.com) uint32 used = n; used |= used >> 1; used |= used >> 2; used |= used >> 4; used |= used >> 8; used |= used >> 16; // Draw numbers until one is found in [0,n] uint32 i; do i = randInt() & used; // toss unused bits to shorten search while( i > n ); return i; } inline MTRand::uint64 MTRand::randInt64( const uint64& n ) { // Find which bits are used in n // Optimized by Magnus Jonsson (magnus@smartelectronix.com) uint64 used = n; used |= used >> 1; used |= used >> 2; used |= used >> 4; used |= used >> 8; used |= used >> 16; used |= used >> 32; // Draw numbers until one is found in [0,n] uint64 i; do i = randInt64() & used; // toss unused bits to shorten search while( i > n ); return i; } inline void MTRand::seed( const uint32 oneSeed ) { // Seed the generator with a simple uint32 initialize(oneSeed); reload(); } inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength ) { // Seed the generator with an array of uint32's // There are 2^19937-1 possible initial states. This function allows // all of those to be accessed by providing at least 19937 bits (with a // default seed length of N = 624 uint32's). Any bits above the lower 32 // in each element are discarded. // Just call seed() if you want to get array from /dev/urandom initialize(19650218UL); register int i = 1; register uint32 j = 0; register int k = ( N > seedLength ? N : seedLength ); for( ; k; --k ) { state[i] = state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1664525UL ); state[i] += ( bigSeed[j] & 0xffffffffUL ) + j; state[i] &= 0xffffffffUL; ++i; ++j; if( i >= N ) { state[0] = state[N-1]; i = 1; } if( j >= seedLength ) j = 0; } for( k = N - 1; k; --k ) { state[i] = state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL ); state[i] -= i; state[i] &= 0xffffffffUL; ++i; if( i >= N ) { state[0] = state[N-1]; i = 1; } } state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array reload(); } inline void MTRand::seed() { // Seed the generator with hash of time() and clock() values seed( hash( time(NULL), clock() ) ); } inline void MTRand::initialize( const uint32 seed ) { // Initialize generator state with seed // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. // In previous versions, most significant bits (MSBs) of the seed affect // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. register uint32 *s = state; register uint32 *r = state; register int i = 1; *s++ = seed & 0xffffffffUL; for( ; i < N; ++i ) { *s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL; r++; } } inline void MTRand::reload() { // Generate N new values in state // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) register uint32 *p = state; register int i; for( i = N - M; i--; ++p ) *p = twist( p[M], p[0], p[1] ); for( i = M; --i; ++p ) *p = twist( p[M-N], p[0], p[1] ); *p = twist( p[M-N], p[0], state[0] ); left = N, pNext = state; } inline MTRand::uint32 MTRand::hash( time_t t, clock_t c ) { // Get a uint32 from t and c // Better than uint32(x) in case x is floating point in [0,1] // Based on code by Lawrence Kirby (fred@genesis.demon.co.uk) static uint32 differ = 0; // guarantee time-based seeds will change uint32 h1 = 0; unsigned char *p = (unsigned char *) &t; for( size_t i = 0; i < sizeof(t); ++i ) { h1 *= UCHAR_MAX + 2U; h1 += p[i]; } uint32 h2 = 0; p = (unsigned char *) &c; for( size_t j = 0; j < sizeof(c); ++j ) { h2 *= UCHAR_MAX + 2U; h2 += p[j]; } return ( h1 + differ++ ) ^ h2; } inline void MTRand::save( uint32* saveArray ) const { register uint32 *sa = saveArray; register const uint32 *s = state; register int i = N; for( ; i--; *sa++ = *s++ ) {} *sa = left; } inline void MTRand::load( uint32 *const loadArray ) { register uint32 *s = state; register uint32 *la = loadArray; register int i = N; for( ; i--; *s++ = *la++ ) {} left = *la; pNext = &state[N-left]; } /* Avoid dependency from STL for Falcon inline std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ) { register const MTRand::uint32 *s = mtrand.state; register int i = mtrand.N; for( ; i--; os << *s++ << "\t" ) {} return os << mtrand.left; } inline std::istream& operator>>( std::istream& is, MTRand& mtrand ) { register MTRand::uint32 *s = mtrand.state; register int i = mtrand.N; for( ; i--; is >> *s++ ) {} is >> mtrand.left; mtrand.pNext = &mtrand.state[mtrand.N-mtrand.left]; return is; } */ #endif // MERSENNETWISTER_H // Change log: // // v0.1 - First release on 15 May 2000 // - Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus // - Translated from C to C++ // - Made completely ANSI compliant // - Designed convenient interface for initialization, seeding, and // obtaining numbers in default or user-defined ranges // - Added automatic seeding from /dev/urandom or time() and clock() // - Provided functions for saving and loading generator state // // v0.2 - Fixed bug which reloaded generator one step too late // // v0.3 - Switched to clearer, faster reload() code from Matthew Bellew // // v0.4 - Removed trailing newline in saved generator format to be consistent // with output format of built-in types // // v0.5 - Improved portability by replacing static const int's with enum's and // clarifying return values in seed(); suggested by Eric Heimburg // - Removed MAXINT constant; use 0xffffffffUL instead // // v0.6 - Eliminated seed overflow when uint32 is larger than 32 bits // - Changed integer [0,n] generator to give better uniformity // // v0.7 - Fixed operator precedence ambiguity in reload() // - Added access for real numbers in (0,1) and (0,n) // // v0.8 - Included time.h header to properly support time_t and clock_t // // v1.0 - Revised seeding to match 26 Jan 2002 update of Nishimura and Matsumoto // - Allowed for seeding with arrays of any length // - Added access for real numbers in [0,1) with 53-bit resolution // - Added access for real numbers from normal (Gaussian) distributions // - Increased overall speed by optimizing twist() // - Doubled speed of integer [0,n] generation // - Fixed out-of-range number generation on 64-bit machines // - Improved portability by substituting literal constants for long enum's // - Changed license from GNU LGPL to BSD include/falcon/message_defs.h000066400000000000000000000100111176363201700165400ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: message_defs.h Definitions for messages. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** @page howto_modstrings How to use module string tables. The module string table is useful to declare error descriptions, error specific explanations and generically messages that the module may display to its users. Module message tables can be internationalized through the Falcon Module Internationalization support. Some macros are provided in module.h to help build module string tables, and are meant to be used under a certain pattern. Applying the module table to the module is a matter of four simple steps. First, each module willing to create an internationalizable module table should create two related files: \_st.h and \_st.c(pp) Second, create the table declaring it in the header file using the FAL_MODSTR macro, like in the following example: \code // My module string table mymod_st.h // Message IDS (identifiers) must be unique. #include \ FAL_MODSTR( MSG0, "Message 0" ); FAL_MODSTR( MSG1, "Message 1" ); FAL_MODSTR( MSG2, "Message 2" ); \endcode If the program is being compiled in c++ mode, it is possible to declare a namespace around the FAL_MODSTR marcors for better encapsulation. The semicomma ";" at the end of each macro are optional. Second, write the C/C++ table implementation. This is only required to declare the macro FALCON_REALIZE_STRTAB before including the string table definition: \code // Module string table realize file mymod_st.cpp #define FALCON_REALIZE_STRTAB #include "mymod_st.h" \endcode Third, the main module file (usually called something as \.cpp) must first include the string table at top level, and then realize it by declaring FALCON_DECLARE_MODULE, setting it to the local variable pointer used to instance the module, and then include the string table: \code #include \ #include "mymod_st.h" FALCON_MODULE_DECL( const Falcon::EngineData &data ) { // setup DLL engine common data data.set(); // Module declaration Falcon::Module *self = new Falcon::Module(); // Declare "self" as the variable holding the module #define FALCON_DECLARE_MODULE self #include "mymod_st.h" ... } \endcode Fourth, retreive the strings from the VM using the Falcon::VMachine::moduleString method. That method actually peeks the current module for the desired string id. In example: \code #include "mymod_st.h" FALCON_FUNC aFuncIn_MyModule( Falcon::VMachine *vm ) { const String *tlstring = vm->moduleString( MSG0 ); // do something with tlstring } \endcode The same can be achieved with Module::getString provided it is possible to access the module: \code #include "mymod_st.h" // MyNewModule extends Falcon::Module void MyNewModule::some_method(...) { const String *tlstring = this->getString( MSG0 ); // do something with tlstring } \endcode The macro FAL_STR( id ), defined as "vm->moduleString( id )" can be used as a handy shortcut for a standard compliant extension function. */ #ifndef FAL_STR #define FAL_STR( id ) vm->moduleString( id ) #endif // allow redefinition of this macros at each include. #undef FAL_MODSTR #ifdef FALCON_DECLARE_MODULE #define FAL_MODSTR( str_id, text ) \ str_id = FALCON_DECLARE_MODULE->addStringID( text, true ); #else #ifdef FALCON_REALIZE_STRTAB #define FAL_MODSTR( id, text ) unsigned int id; #else #define FAL_MODSTR( id, text ) extern unsigned int id; #endif #endif /* end of message_defs.h */ include/falcon/modloader.h000066400000000000000000000541501176363201700160750ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_modloader.h Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar ago 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_MODLOADER_H #define FLC_MODLOADER_H #include #include #include #include #include namespace Falcon { class Module; class Stream; class URI; class FileStat; class VFSProvider; /** Module Loader support. This class enables embedding applications and falcon VM (and thus, Falcon scripts) to load modules. */ class FALCON_DYN_CLASS ModuleLoader: public BaseAlloc { public: /** File type enumeration. The module loader tries to determine which type of file the it is trying to load (or is told this information by the loader user). */ typedef enum { /** Undetermined / detect. */ t_none, /** The module is a source. */ t_source, /** The module is a Falcon Templage Document. */ t_ftd, /** The module is a falcon FAM module. */ t_vmmod, /** The module is a native .so/.dll. */ t_binmod, /** Special file type. Try to determine the type of the file, but in case it cannot be determined, defaults to source. */ t_defaultSource } t_filetype; protected: Module *loadModule_ver_1_0( Stream *in ); bool m_alwaysRecomp; bool m_compMemory; bool m_saveModule; bool m_saveMandatory; bool m_detectTemplate; bool m_forceTemplate; bool m_delayRaise; bool m_ignoreSources; bool m_saveRemote; uint32 m_compileErrors; Compiler m_compiler; String m_srcEncoding; bool m_bSaveIntTemplate; Module *compile( const String &path ); t_filetype searchForModule( String &final_name ); t_filetype checkForModuleAlreadyThere( String &final_name ); /** Required language during load. */ String m_language; Module *loadModule_select_ver( Stream *in ); /** Discovers the module name given a complete file path. \param path the path to a possible falcon module \param modNmae the possible falcon module name */ static void getModuleName( const String &path, String &modName ); /** Path where to search the modules. Each entry of the list is one single system path encoded in falcon file name format. */ List m_path; /** Basic function used to open modules. This virtual function opens a disk file as given in the path (absolute or relative to Current Working Directory). By overloading this method, subclasses may create method loaders for non-disk resources (i.e. interface virtual file systems on compressed files or on networks). The method may invoke the error handler to signal errors, and will return 0 in case the file can't be opened for reading. An optional file type is provided to give the subclasses an hint on how to configure the stream for the final user. \param path the path to file \param type the file type that should be opened (t_none meaning unknown / generic). \return a valid input stream or 0 on error. */ virtual Stream *openResource( const String &path, t_filetype type = t_none ); /** Scan for files that may be loaded. Utility funciton searching for one possible file in a directory. Tries all the possible system extensions (applied directly on origUri) and searches for a matching file. If one is found, true is returned and type and fs are filled with the item module-type and its data. Priority is scan file types are ftd, fal, fam and .so/.dll/.dylib. The function doesn't check for validity of the given file, but only for its existence. \return on success, returns true. */ bool scanForFile( URI &origUri, VFSProvider*, t_filetype &type, FileStat &fs ); /** Determine file types. This method tries to determine the type of a file given a path.+ This function is meant to allow subclassess to determine file types on their own will; this method tell sources, binary module and Falcon native modules. This basic version of the method will try to open the file for reading to determine their type. It will also close them before returning. \param path the file to be tested (absolute / relative to cwd) \return the type of file or t_none if it can't be determined. */ virtual t_filetype fileType( const String &path ); /** Try to load the language table for the given module. */ bool applyLangTable( Module *mod, const String &file_path ); public: /** Creates a module loader. As default, the current directory is included in the path. If this is not desired, use ModuleLoader( "" ) as a constructor. */ ModuleLoader(); ModuleLoader( const ModuleLoader &other ); /** Creates a module loader with a given path. The path is in the FALCON format; path are separated by semicolons (;). If the system uses disk specification, falcon is able to understand a single letter, a colon and a slash as a disk specification. I.e. \code ".;C:/projects;d:/modules"; \endcode will be processed as ".", "C:\projects" and "D:\modules" under systems using disk specifications. The current directory is \b not included in the default path. If this is desired, add it as a single "." entry. */ ModuleLoader( const String &path ); virtual ~ModuleLoader(); virtual ModuleLoader *clone() const; /** Changes the search path used to load modules by this module loader This method changes the search specification path used by this module loader with the one provided the parameter to the search path of those already known. Directory must be expressed in Falcon standard directory notation ( forward slashes to separate subdirectories). If the path contains more than one directory they must be separated with a semicomma; for example: \code modloader.addSearchPath( "../;/my/modules;d:/other/modules" ); \endcode \see addSearchPath getSearchPath \see ModuleLoader \param path the new module search path */ void setSearchPath( const String &path ); /** Add standard system Falcon module paths to the search path of this module loader. By default, Falcon system module paths are not added to newly created Module Loader. This is because an embedding application may wish to load its own version of the modules from somwhere else. This method appends the path where Falcon is installed on this system, to the search path of this module loader. The added path will be searched after the ones that have already been added to this loader. To give system Falcon libraries and modules higher priority, call this method before adding your application paths (including "."). If Falcon is not installed on this system, this method will have no effect. */ void addFalconPath(); /** Adds one or more directories to the module loader search path. This method appends the search specification path passed as a parameter to the search path of those already known. Directory must be expressed in Falcon standard directory notation ( forward slashes to separate subdirectories). If the path contains more than one directory they must be separated with a semicomma; for example: \code modloader.addSearchPath( "../;/my/modules;/other/modules" ); \endcode The directories will be added with a priority lower than the currently searched ones; that is, they will be searched after the ones that have been previously added are searched. \see setSearchPath getSearchPath \param path the search specification to be added to the path */ void addSearchPath( const String &path ); /** Adds a single directory to the module loader path, with higher priority. This method adds a directory with a priority higher than the ones already defined. \note Don't use ";" separated paths here; just call this method once for each path. \param directory the directory to be added to the path */ void addDirectoryFront( const String &directory ) { if ( directory != "" ) m_path.pushFront( new String( directory ) ); } /** Adds a single path specification to the module loader path, with lower priority. This method adds a directory with a priority lower than the ones already defined. \note Don't use ";" separated paths here; just call this method once for each path. \param directory the directory to be added to the path */ void addDirectoryBack( const String &directory ) { if ( directory != "" ) m_path.pushBack( new String( directory ) ); } /** Loads a module by its name. This function scan the directories in the path for a matching module name. The logical module name, which can be logically related with a parent module (as in the case of "load self.submodule") is used to determine possible phisical file names within the given search path and filesystems. Once a suitable file is found, loadFile() is called with the appriopriate path and type parameters. The loadFile() method has its own precompiled-versus-source resolution logic. As this function is just a front-end to resolve logical name into potential physical names, this function follows the same compile-or-load logic as loadFile(). \note On success, the returned module will have its physical name and path set accordingly to the module_name parameter and the path where the module has been found. If a suitable file is found, but fails to load (beynd the recovery logic as stored in loadFile()), the search is interrupted and an error is raised. \param module_name the name of the module to search for \param parent_name the name of the module that is asking for this module to be loaded \return newly allocated module on success. \throw Error or appropriate subclass on error. */ virtual Module *loadName( const String &module_name, const String &parent_module = "" ); /** Loads a module by its path. This method loads directly a module. If the \b scan parameter is given \b and the path is relative, then the module is searched in the modloader path. Current directory is not explicitly searched unless it is present in the loader search path. If the path is relative and \b scan is false, the path will be considered relative to current working directory. If it's true, a relative path would be searched across all the paths in turn, that doesn't necesarily include the current working directory. If the type of file is not given (set to t_none) the method tries to dentermine the type of the file. If it's given, it will ignore the file type detection and will pass an opened stream directly to the loadSource(), loadModule() or loadBinModule() method. In case the module is determined to be a source (or an FTD), this method scans for a file in the same directory with a .fam extension, and checks if the module can is newer and can be loaded (unless alwaysRecomp() is true, In this case, if the .fam module fails to load, then the the error is discarded and the program continues trying to compile the original source. If trying to use a .fam is not desired, either set alwaysRecomp() or call directly loadSource(). Conversely, if the file is determined to be a .fam module, a source with the same name but with newer timestamp is searched, and eventually compiled if found. If this is not wanted, either call directly loadModule() or set ignoreSources() to true. This implementation will raise an error if t_source is explicitly provided as a type or if the target file is detected as a source. On load, the logical module name will be set to the file part of the path. However, it may be changed after loading. \note on failure, the module loader will post an error to its error handler, if it has been provided. \param module_path the relative or absolute path of the file to load. The file is not URI encoded; to load URI encoded filenames, pass directly the URI file. \param type the type of the file to be loaded, or t_none to autodetect \param scan if module_path is relative, set to true to allow scanning of the modloader path \return a valid module on success. \throw Error or appropriate subclass on error. */ virtual Module *loadFile( const String &module_path, t_filetype type=t_none, bool scan=false ); /** Loads a module by its URI. \see Module *loadFile( const String &module_path, t_filetype type=t_none, bool scan=false ); \param module_URI the relative or absolute path of the file to load \param type the type of the file to be loaded, or t_none to autodetect \param scan if module_path is relative, set to true to allow scanning of the modloader path \return a valid module on success. \throw Error or appropriate subclass on error. */ virtual Module *loadFile( const URI &module_URI, t_filetype type=t_none, bool scan=false ); /** Loads a Falcon precompiled native module from the input stream. This function tries to load a Falcon native module. It will detect falcon module mark ('F' and 'M'), and if successful it will recognize the module format, and finally pass the stream to the correct module loader for the version/subversion that has been detected. On success, a new memory representation of the module, ready for linking, will be returned. \note after loading, the caller must properly set returned module name and path. \param input An input stream from which a module can be deserialized. \return a newly allocated module on success. \throw Error or appropriate subclass on error. */ virtual Module *loadModule( Stream *input ); /** Loads a Falcon precompiled native module. Front end for loadModule(Stream). This function sets the module name and path accordingly to the \b file parameter. The caller may know better and reset the module logical name once a valid module is returned. \note This method doesn't set the module language table. \param path A path from which to load the module. \return a newly allocated module on success. \throw Error or appropriate subclass on error. TODO: make virtual */ Module *loadModule( const String &file ); /** Load a source. Tries to load a file that is directly considered a source file. This is just a front-end to loadSource( Stream*,const String &, const String & ). This function sets the module name and path accordingly to the \b file parameter. The caller may know better and reset the module logical name once a valid module is returned. \note This method doesn't set the module language table. \param file a complete (relative or absolute) path to a source to be compiled. \return a valid module on success, 0 on failure (with error risen). \throw Error or appropriate subclass on error. */ virtual Module *loadSource( const String &file ); /** Compile the source from an input stream. This function sets the module name and path accordingly to the \b file parameter. The caller may know better and reset the module logical name once a valid module is returned. \note Notice that this function doesn't load the translation table. Also, this function tries to save the generated module in a .fam file, if saveModules is set to true; if saveMandatory is also true, the function will raise an error if the compiled module can't be properly saved. \throw Error or appropriate subclass on error. \param in the file from which to load the file. \param uri the complete URI of the source file from which the stream is open, \param modname logical name of the module. */ virtual Module *loadSource( Stream *in, const String &uri, const String &modname ); /** Loads a binary module by realizing a system dynamic file. The method calls the dll/dynamic object loader associated with this module loader. The dll loader usually loads the dynamic objects and calls the startup routine of the loaded object, which should return a valid Falcon module instance. This method then fills the module path and logical name accordingly with the given parameter. Special strategies in opening binary modules may be implemented by subclassing the binary module loader. This function sets the module name and path accordingly to the \b file parameter. The caller may know better and reset the module logical name once a valid module is returned. \note This method doesn't set the module language table. \param module_path the relative or absolute path. \return newly allocated module on success. \throw Error or appropriate subclass on error. */ virtual Module *loadBinaryModule( const String &module_path ); void raiseError( int code, const String &expl, int fsError=0 ); /** Get the search path used by this module loader. \param target a string where the path will be saved. */ void getSearchPath( String &target ) const; /** Get the search path used by this module loader. \return the search path that is searched by this module loader. */ String getSearchPath() const { String temp; getSearchPath( temp ); return temp; } /** Save international templates for loaded modules. If this option is set to true, and if the loaded modules have international strings, then a template for the internationalization file will be saved. */ void saveIntTemplates( bool mode /*, bool force=false */ ) { m_bSaveIntTemplate = mode; } /** Sets the language required to modules during load. This informs the module loader that the owner wishes the string table of the loaded module configured for the given language. If the loaded module doesn't declare itself to be written in the desired language, the module loader will try to load \b modulename.ftr binary file, get the table for the desired language and change the strings according to the translation table before returning it to the caller. In case of failure, the original string table will be left untouched. Language names are the ISO language names in 5 characters: xx_YY. Setting the language to "" disables this feature. \param langname the name of the language that should be loaded. */ void setLanguage( const String &langname ) { m_language = langname; } /** Returns the translation language that is searched by this module loader. */ const String &getLanguage() const { return m_language; } /** Load a determined language table directly into the module. On success, the language table of the module and it's declared language are changed. \return true on success. */ bool loadLanguageTable( Module *module, const String &language ); /** Ignore Source accessor. \return true if the Module Loader must load only pre-compiled modules, false otherwise. */ bool ignoreSources() const { return m_ignoreSources; } /** Always recompile accessor. \return true if source modules must always be recompiled before loading. */ bool alwaysRecomp() const { return m_alwaysRecomp;} void ignoreSources( bool mode ) { m_ignoreSources = mode; } void alwaysRecomp( bool mode ) { m_alwaysRecomp = mode; } void compileInMemory( bool ci ) { m_compMemory = ci; } bool compileInMemory() const { return m_compMemory; } void saveModules( bool t ) { m_saveModule = t; } bool saveModules() const { return m_saveModule; } void sourceEncoding( const String &name ) { m_srcEncoding = name; } const String &sourceEncoding() const { return m_srcEncoding; } void delayRaise( bool setting ) { m_delayRaise = setting; } bool delayRaise() const { return m_delayRaise; } void saveMandatory( bool setting ) { m_saveMandatory = setting; } bool saveMandatory() const { return m_saveMandatory; } void detectTemplate( bool bDetect ) { m_detectTemplate = bDetect; } bool detectTemplate() const { return m_detectTemplate; } void compileTemplate( bool bCompTemplate ) { m_forceTemplate = bCompTemplate; } bool compileTemplate() const { return m_forceTemplate; } /** Tells if this modloader should save .fam on remote filesystems. By default, if a source file is loaded from a remote filesystem, the module loader doesn't try to save a .fam serialized version of the module besides the source. You can set this to true to force a try to store modules also on remote filesystems. \param brem true to try to save .fam files on remote filesystems. */ void saveRemote( bool brem ) { m_saveRemote = brem; } /** Tells wether this loader tries to save .fam on remote filesystems. \see saveRemote( bool ) */ bool saveRemote() const { return m_saveRemote; } /** return last compile errors. */ uint32 compileErrors() const { return m_compileErrors; } /** Return the compiler used by this module loader. This object can be inspected, or compiler options can be set by the caller. \return a reference of the compiler used by the loader. */ const Compiler &compiler() const { return m_compiler; } /** Return the compiler used by this module loader (non const). This object can be inspected, or compiler options can be set by the caller. \return a reference of the compiler used by the loader. */ Compiler &compiler() { return m_compiler; } }; } #endif /* end of modloader.h */ include/falcon/module.h000066400000000000000000000667271176363201700154310ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: module.h Falcon module extensions. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar ago 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_MODULE_H #define FLC_MODULE_H #include #include #include #include #include #include #include #include #include #include #include #include namespace Falcon { class String; class Stream; class AttribMap; /** Module class abstraction. A module is uniquely indicated by its name; loading a module which has the same name of a previous one will make the other existing module to be discarded. When a module is added to the VM, it requests the module symbol table. The exported symbols are then loaded in the global VM symbol table. Only a module may be executed; when it is executed, it may load additional modules (the vm acts in its behalf) and then the VM creates the global context array. The array is filled with the items provided in the symbols of the symbol table, or with NIL were missing; if symbols are external, they are searched in the global VM symbol table (and again filled with the provided item or with NIL). A module may even exist without code. This is the case of pure C modules that register themselves using only C functions as symbol's initial items. */ class FALCON_DYN_CLASS Module: public BaseAlloc { protected: mutable volatile int32 m_refcount; /****************************** * Anagraphic section *******************************/ char m_version; char m_subversion; String m_name; String m_path; String m_language; uint32 m_modVersion; uint32 m_engineVersion; /****************************** * Tables section *******************************/ StringTable* m_strTab; bool m_bOwnStringTable; SymbolVector m_symbols; SymbolTable m_symtab; DependTable m_depend; /** The line map is a pointer as it can be absent or given after the generation step. */ LineMap *m_lineInfo; /** Dynamic link loader attached with this module. If this module is created using a DLL, then it is necessary to close the DLL handle as soon as the module is disposed (or, at least, not before). For this reason, the loader that generated the module should be contained in the module itself. The loader provides internally a system to provide resource reference counting, so that if it insists on the same handler as other loaders (contained by other module instances), the correct library handler count is kept (Actually, this service is usually available at kernel level on many OSs). Also, DllLoader instance contains usually a minimal representation for the DLL handler and points to a minimal set of non-virtual methods to manage it, so it has the same weight as a reference count or even a pointer to the an handler. This justifies the presence of this field also in modules that are not generated by DLLs, but are loaded from binary Falcon objects. */ DllLoader *m_loader; /** Detach the loader for decref. If the module has been loaded through a DLL, decreffing it to death should unload the module. If this was the last instance of the module in memory, the return address of the DllLoader desctructor may not be available anymore when the destructor returns. For this reason, the decref() member calls this method and destroys autonomously the handler after having destroyed the module. */ DllLoader *detachLoader(); Map m_serviceMap; AttribMap* m_attributes; virtual ~Module(); public: static const uint32 c_noEntry; /** Variable parameter initializer semantic. Use the following functions to initialize the module. If a SymbolTable is not provided here, an empty one is immediately created. */ Module(); Module &name( const String &n ) { m_name.bufferize( n ); return *this; } Module &path( const String &p ) { m_path.bufferize( p ); return *this; } Module &language( const String &lang ) { m_language = lang; return *this; } Module &version( uint32 version ) { m_modVersion = version; return *this; } Module &version( int major, int minor, int revision ) { m_modVersion = major << 16 | minor << 8 | revision; return *this; } Module &engineVersion( uint32 version ) { m_engineVersion = version; return *this; } Module &engineVersion( int major, int minor, int revision ) { m_engineVersion = major << 16 | minor << 8 | revision; return *this; } virtual char pcodeVersion() const; virtual char pcodeSubVersion() const; void incref() const; void decref() const; /** Determine this to be a flexy module. Flexy modules can be queried for unknown symbols at runtime; they declare themselves as sticking only with this VM (or otherwise providing a VM-sensible local environment), and are available for late-lazy binding of symbols. */ virtual bool isFlexy() const { return false; } Symbol *findGlobalSymbol( const String &name ) const { return m_symtab.findByName( name ); } const String &name() const { return m_name; } const String &path() const { return m_path; } const String &language() const { return m_language;} String *addCString( const char *pos, uint32 size ); /** Add a string to the module string table. If the string already exists, the previous entry is returned. \note If the string isn't found in the string table and is created on the spod, its exported status (international string) is copied from \b st. \param st the string to be added. \return A pointer to the string in the module string table. */ String *addString( const String &st ); /** Add a string to the module string table and eventually exports it. If the string already exists, the previous entry is returned. This method also set the "exported" bit of the returned string, so that the string can be directly used in exported intenrationalization tables. \param st the string to be added. \param exported set to true to make this string subject to internationalization table exports. \return A pointer to the string in the module string table. */ String *addString( const String &st, bool exported ); /** Add a string to the module string table, but return its ID. This always adds a new string, even if another identical string already existed. \param st the string to be added. \param exported set to true to make this string subject to internationalization table exports. \return The ID in the string table of the newly added string. */ uint32 addStringID( const String &st, bool exported ); /** Return a string given an ID in the string table. This function doesn't check for out-of-bounds access, so be careful. \param id the ID of the string in the string table (insertion order). \return A pointer to the string. */ const String *getString( uint32 id ) const { return m_strTab->get(id); } Symbol *getSymbol( uint32 id ) const { if ( id > m_symbols.size() ) return 0; return *(Symbol **) m_symbols.at(id); } /** Returns the module symbol table (const version). This holds the global variables that are available at module level. \return the global symbol table (const version). */ const SymbolTable &symbolTable() const { return m_symtab; } /** Returns the module symbol table. This holds the global variables that are available at module level. \return the global symbol table. */ SymbolTable &symbolTable() { return m_symtab; } /** Returns the symbol list (const version). The symbol list is a vector which contains the list of all the symbols in the module (including local, static, private, parameters and the like). Each Symbol contains an id that backreferences the position in the module symbol list so that symbols()[i]->id() == symbols[i]; \return a reference to the symbol vector. */ const SymbolVector &symbols() const { return m_symbols; } SymbolVector &symbols() { return m_symbols; } const DependTable &dependencies() const { return m_depend; } /** Add a dependency to an external module. \param name The name of the module from which this module depends. \param bPrivate if true import the module only locally. \param bIsFilename if true the load name is a filename. */ void addDepend( const String &name, bool bPrivate = false, bool bIsFilename = false ); /** Add a dependency to an external module. \param alias The local alias under which the module is known. \param module The name of the module from which this module depends. \param bPrivate if true import the module only locally. \param bIsFilename if true the load name is a filename. */ void addDepend( const String &alias, const String &module, bool bPrivate=false, bool bIsFilename = false ); /** Adds a generic symbol to the module */ void addSymbol( Symbol *sym ); /** Shortcut to create a symbol. Creates a symbol and adds it to the module symbol vector. It is not added to the global symbol table, though. For that, use addGlobalSymbol(). \note this function allows the creation of symbols having the same name of already existing ones (useful i.e. for local/params). \param name the name of the symbol that must be created. \return the newly created symbol */ Symbol *addSymbol( const String &name ); Symbol *addExternalRef( const String &name ) { return addGlobalSymbol( addSymbol( name ) ); } /** Shortcut to create a symbol as an external function. Binary modules may use this as a shortcut to create external function definitions. The function will also store the symbol int the global symbol table, and eventually add the string to the module string table if needed. In case the symbol is already used, it will return 0, else it will return the newly created symbol. If the symbol has to be set as a class method, or otherwise used internally by the module, then set the \b exp parameter to false, so that the symbol is not exported in the VM, sparing link time and memory. \param name the name of the symbol that must be created. \param func a function pointer to the external function. \param exp true if the symbol myst be exported (true by default) \return the newly created symbol */ Symbol *addExtFunc( const String &name, ext_func_t func, bool exp = true ) { return addExtFunc( name, func, 0, exp ); } /** Shortcut to create a symbol as an external function. This also sets extra data for the callable symbol. \see ExtFuncDef \param name the name of the symbol that must be created. \param func a function pointer to the external function. \param extra extra data for this function definition. \param exp true if the symbol myst be exported (true by default) \return the newly created symbol */ Symbol *addExtFunc( const String &name, ext_func_t func, void *extra, bool exp = true ); /** Shortcut to create a symbol and store it in the module symbol list. \return a newly allocated symbol which is already in the symbol list. */ Symbol *addSymbol(); /** Adds a symbol to the global symbol table. The function checks if the symbol is already in the module table; if not it adds it to the module table and sets the proper id. \return A pointer to the symbol. */ Symbol *addGlobalSymbol( Symbol *sym ); /** Adds a global variable symbol to the module @param name the name of the symbol @param exp true if the symbol is exported, false otherwise @return the newly created symbol */ Symbol *addGlobal( const String &name, bool exp=true ); /** Shortcut to add class properties that are to be considered variables. This method takes care to create the module strings needed to represent the class properties. \note The Symbol passed to this method must be one of the symbols held by this module. \param cls the symbol holding the ClassDef that must be operated on. \param prop the property name. \return the newly added VarDef; it can be used to set default values or vardef properties. */ VarDef& addClassProperty( Symbol *cls, const String &prop ); /** Adds methods to ClassDefs in symbols held by this module. This method takes care to create the module strings needed to represent the class properties. \note The Symbols passed to this method must be held by this module. \param cls the symbol holding the ClassDef that must be operated on \param prop the property name \param method the symbol that will be used as a method \return the newly added VarDef; it can be used to set default values or vardef properties. */ VarDef& addClassMethod( Symbol *cls, const String &prop, Symbol *method ); /** Shortcut to add external function methods to Classes in this module. This is a shortcut that creates a non-exported symbol represented by the class name, a dot, and the property name. This symbol is created in the module as non-exported, and then is fed into addClassMethod( Symbol *, const String, Symbol * ). \note The Symbols passed to this method must be held by this module. \param cls the symbol holding the ClassDef that must be operated on \param prop the property name \param func the function that will be used as a method \return the newly added VarDef; it can be used to set default values or vardef properties. */ VarDef& addClassMethod( Symbol *cls, const String &prop, ext_func_t func ); /** Adds an internal function to the module. As the functions and other callable are usually created during module load and added directly to the symbol table, this method serves only as a shortcut to define pseudo-functions. The most notable of them is the "__main__" function that starts at module entry point and never accepts parameters or locals The name parameter will be copied in a new string ar stored in the module string table. If the string already exists, that instance will be used as the symbol name. @param name the name of the symbol @param code the code of the function to be executed. The data will be destroyed with memFree at symbol destruction. @param size size of the function code in bytes. @param exp true if the symbol is to be exported @return a pointer to the created symbol */ Symbol *addFunction( const String &name, byte *code, uint32 size, bool exp=true ); /** Adds a class definition to this module. This method creates a class which is pertinent to this modules and return the corresponding class symbol. The symbol may then be used to add properties to the class; every property must be a valid symbol that is already inserted in the same module of the class (at the moment, in future it may also be a symbol from another module). * @param name The name of the class * @param ctor_sym An optional symbol that is registered in the same module acting as a constructor (pass zero if no constructor is needed). * @param exp true if the symbol is to be exported * @return The created class */ Symbol *addClass( const String &name, Symbol *ctor_sym, bool exp=true ); /** Adds a class definition with an external constructor to this module. This is a shortcut that creates a method in the module by getting the class name and adding a "._init". The created symbol is not exported, and is set as class constructor by calling addClass( name, Symbo *, bool ). * @param name The name of the class * @param ctor the external function that will be used as constructor. * @param exp true if the symbol is to be exported * @return The created class */ Symbol *addClass( const String &name, ext_func_t ctor, bool exp=true ); /** Adds a class definition without a constructor. * @param name The name of the class * @param exp true if the symbol is to be exported * @return The created class */ Symbol *addClass( const String &name, bool exp=true ) { return addClass( name, static_cast( 0 ), exp ); } /** Adds a singleton object to this module. This method creates a singleton object, that is a private class which is instantiated at link time in an unique, eventually exported object. The given name is the name by which the object will be known; the private class is given the special name "%[object name]", where "[object name]" is the \b name parameter. If both the name and the "%" prefixed name are currently unassigned, a new symbol containing the singletone instance declaration is returned. On failure, 0 is returned. The class giving form to this singleton can be retrieved using the Symbol::instanceOf() method. That symbol can then be used to add methods and properties to the singleton, or to derive the private class from other classes. \param name the name of the singleton to be created \param ctor_sym the constructor of the base class, or 0 for none \param exp true to export the singleton. The class is always private; to export it get it with Symbol::instanceOf() and export it with Symbol::exported(). \return the symbol of the singleton object on success, zero on failure. */ Symbol *addSingleton( const String &name, Symbol *ctor_sym=0, bool exp = true ); /** Adds a singleton object to this module. This is like addSingleton( const String &, Symbol *, bool ), but this version of the function takes an external function as a constructor and assigns it a name that coresponds to the class name followed by "._init". If the constructor name, the class name or the instance name are not free, the method returns false. \param name the name of the singleton to be created \param ctor the constructor of the base class; cannot be zero (use the other version instead) \param exp true to export the singleton. The class is always private; to export it get it with Symbol::instanceOf() and export it with Symbol::exported(). \return the symbol of the singleton object on success, zero on failure. */ Symbol *addSingleton( const String &name, ext_func_t ctor, bool exp = true ); /** Returns the line number that generated a certain code portion. Given a Program Counter index, this funtion returns the code line that originally generated the part of the code to which the Program Counter is pointing to, if this information is available. The function will return zero if the information cannot be retreived (i.e. be cause debug informations have been stripped). \param pc a code position for which the original code line must be discovered. \return the code line that generated the coed at PC position, or 0 if line info are missing. */ uint32 getLineAt( uint32 pc ) const; /** Adds a line information to the module. The line informations are used in debug and error reporting to provide the users with the line number of the source falcon script that generated a certain code portion in the bytecode. The the assembler calls this function each time a new line generates new code. \param pc the current code position. \param line the line that generated the code starting at PC. */ void addLineInfo( uint32 pc, uint32 line ); /** Adds pre-compiled line informations to the module. The line informations are used in debug and error reporting to provide the users with the line number of the source falcon script that generated a certain code portion in the bytecode. Generators knowing the line informations (i.e. the code generator) can create a ready-to-use line information map, that can then be inserted into the same module where the target code will find place. The given line map is then owned by the module, and it will destroyed at module destruction; so the creator must give off its ownership. If the module already had some line infrmations, they are destroyed. \param infos a map containing line informations relative to the code used in this module. */ void setLineInfo( LineMap *infos ); /** Access the DllLoader attached with this module. The internal dll loader is created the first time this mehtod gets called. This is to avoid application-only modules to have a Dll loader attached. \see m_loader \return the dynamic code loader that is used by this module. */ DllLoader &dllLoader(); /** Write the module to a stream. The module is written to a stream (that does not requires seeking capabilities, like i.e. stdout). If skipCode is true, the code member of the module (m_code) is not written and when the operation reaches the point where the code should be written, it skips forward of m_codeSize bytes. If skipCode is false (default), the m_code member is regularily written. The code is usually skipped by the compiler and assembler if they have the byte code handy in another place (i.e. stored on a seekable temporary stream). In that case, importing the bytecode in the module just to serialize it would be a waste. The very last things to be written are 4 bytes representing the de-endianized code size and then the bytecode itself. This allow a caller that sets skipCode=true to write those data right after save() has returned successfully. \param os an output stream with seek capabilities \param skipCode true if the caller doesn't want to save the bytecode. \throw std::ostream::failure in case the stream reports a write error */ virtual bool save( Stream *os, bool skipCode=false ) const; /** Reads the module from a stream. This method may only load a Falcon binary module; it has no capability to compile a source module or to load a native module in DLL or shared object format. This method is used as a convenience by the module loader or by other agents when they discover that the module is a native Falcon module. For this reason, the 4 bytes containing the Falcon module intestation should be skipped (read) by the caller \b before calling this method. In case they are not, i.e. because the caller knows the module being right without reading the header, then it should call this function with skipHeader set to false. The skipHeader parameter only refers to the first 4 bytes of the module file, which contain a signature, the version and the subversion of the module; the whole header may be much longer and depends on the module version. \param is an input stream \param skipHeader wheter to skip or read the first 4 module bytes. */ virtual bool load( Stream *is, bool skipHeader=true ); const StringTable &stringTable() const { return *m_strTab; } StringTable &stringTable() { return *m_strTab; } void adoptStringTable( StringTable *st, bool bOwn = false ); /** Creates an istance for a certain service. Returns 0 if the module does not provide the required service. The caller will have to delete the service. */ Service *getService( const String &name ) const; /** Publishes a service on the module. \param sp the ServiceProvider (function pointer) that will create the service on request. \return true if possible, false if the service was already registered. */ bool publishService( Service *sp ); /** Return a map of the provided services. Should be used only in the vm. */ const Map &getServiceMap() const { return m_serviceMap; } /** Adds a constant to the module. This object will become a constant item in the VM. \param exp true if the symbol is to be exported. \param value the value that the constant will assume in the vm. \param name the name of the symbol in the vm. */ Symbol *addConstant( const String &name, int64 value, bool exp = true ); /** Adds a constant to the module. This object will become a constant item in the VM. \param exp true if the symbol is to be exported. \param value the value that the constant will assume in the vm. \param name the name of the symbol in the vm. */ Symbol *addConstant( const String &name, numeric value, bool exp = true ); /** Adds a constant to the module. This object will become a constant item in the VM. \param exp true if the symbol is to be exported. \param value the value that the constant will assume in the vm. \param name the name of the symbol in the vm. */ Symbol *addConstant( const String &name, const String &value, bool exp = true ); /** Get the version declared by this module */ void getModuleVersion( int &major, int &minor, int &revision ) const; /** Get the version of the engine libraries under which this module has been compiled. */ void getEngineVersion( int &major, int &minor, int &revision ) const; /** Save internationalization infos on required file. Creates an XML file staring with ?xml tag. Shouldn't be called if it's table has no international strintgs. */ bool saveTableTemplate( Stream *stream, const String &encoding ) const; /** Extract the full logical module name from current module name and parent loader. */ static void absoluteName( const String &module_name, const String &parent_name, String& res ); /** Adds a module-wide attribute. */ void addAttribute( const String &name, VarDef* vd ); /** Returns the module-wide attribute list. May be zero if this module has no attributes. */ AttribMap* attributes() const { return m_attributes; } /** Unrolls the symbols back to a previous state. * This function removes the symbols added past a size. * This allows to return to a previous state in the module * declaration, that can be simply recored by getting * the size of the symbol table. * * For example. * @code * int modSize = module->symbols().size(); * try { * // ... do something altering the module * } * catch( Error* e ) * { * // roll back * module->rollBackSymbols( modSize ); * throw; * } * @endcode */ void rollBackSymbols( uint32 size ); }; } //==================================================== // Module declaration and service macros // // module init and declaration #define DEFALUT_FALCON_MODULE_INIT falcon_module_init #define FALCON_MODULE_DECL \ FALCON_MODULE_TYPE DEFALUT_FALCON_MODULE_INIT() // Module string table service functions #ifndef FAL_STR #define FAL_STR( id ) vm->moduleString( id ) #endif #endif /* end of module.h */ include/falcon/modulecache.h000066400000000000000000000031171176363201700163750ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: modulecache.h Cache-sensible module loader. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 30 May 2010 15:12:30 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_MODULECACHE_H_ #define FALCON_MODULECACHE_H_ #include #include #include #include #include #include namespace Falcon { /** The cache where modules are stored. Updates are threadsafe. */ class ModuleCache: public BaseAlloc { public: ModuleCache(); /** When the cache is destroyed, all the modules in it are decreffed. */ ~ModuleCache(); /** Adds a module to the cache. The module is increffed when added to the cache. If another module with the same name is already in the map, the old module is returned (increffed), and the incoming module is decreffed. */ Module* add( const String& muri, Module* module ); /** Removes a module from the cache. If the module is in the cache, it is decreffed. */ bool remove( const String& muri ); /** Returns a module if it is in cache, or 0 if not found. The returned instance is increffed. */ Module* find( const String& muri ); private: Mutex m_mtx; int m_refCount; Map m_modMap; }; } #endif /* end of modulecache.h */ include/falcon/mt.h000066400000000000000000000135741176363201700145540ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: mt.h Multithreaded extensions. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 13 Dec 2008 13:08:38 +0100 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_MT_H #define FALCON_MT_H #include #include #include #ifdef FALCON_SYSTEM_WIN #include #else #include #endif namespace Falcon { /** Runnable base class. When the abstraction of a thread is quite good to represent the final object to be run, this pure interface can be passed to a thread that will run it. */ class FALCON_DYN_CLASS Runnable { public: virtual ~Runnable() {}; virtual void* run() = 0; }; /** Thread creation parameters. */ class FALCON_DYN_CLASS ThreadParams: public BaseAlloc { uint32 m_stackSize; bool m_bDetached; public: ThreadParams(): m_stackSize(0), m_bDetached( false ) {} ThreadParams &stackSize( uint32 size ) { m_stackSize = size; return *this; } ThreadParams &detached( bool setting ) { m_bDetached = setting; return *this; } uint32 stackSize() const { return m_stackSize; } bool detached() const { return m_bDetached; } }; struct SYSTH_DATA; /** System Thread class. Can be used both to run a Runnable class instance, or to run its own virtual run method. This class is called SysThread to distinguish it from the higher level Thread. This class just encapsulates the system threading, wheres Thread gives more multithreading programming high level support, as interrupt management and multiple object wait. Notice that we don't have a stop() or cancel() or interrupt() method in this class. This is because all stop requests are handled at higher level, by the threads interested in VM operations (so that they can be handled i.e. by the scripts). MS-Windows doesn't provide a cancelation protocol, while POSIX cancelation system doesn't allow for handling of the cancelation event (the semantic is such that cancelation of I/O blocking operations must be honored ASAP, with at least a small control on cleanup). Both ways to do the thing (i.e. none or too much) aren't good for the code we're writing above this class, so we just drop this feature and re-implement cancelation requests at higher level, preventing - controlling blocking I/O. */ class FALCON_DYN_CLASS SysThread: public BaseAlloc { struct SYSTH_DATA* m_sysdata; protected: Runnable* m_runnable; virtual ~SysThread(); public: SysThread( Runnable* r = 0 ); /** Makes this object to represent currently running system thread. This must be called after the constructor and before start(). */ void attachToCurrent(); /** Launches a new instance of this thread. Only one start can be performed for a thread. Trying to execute start more than once will fail. */ bool start( const ThreadParams ¶ms ); /** Launches a new instance of this thread with default parameters. Only one start can be performed for a thread. Trying to execute start more than once will fail. */ bool start() { return start( ThreadParams() ); } /** Detach this thread. After this call, the thread will take care of destroying itself at termination. Joining threads will receive an unjoinable exception. Notice that if the run() return value is dynamically allocate, it will be leaked, so this method should be called only for threads not returning any value. After this call, this instance must be considered invalid. \note A SysThread must terminate either because a detach or with a join. The destroyer cannot be called directly (this means you can't create a SysThread instance in the stack). */ void detach(); /** Join this thread. Wait for this thread to terminate, and return the value returned by run(). After join has returned, this thread cannot be used anymore (it is virtually destroyed). \note A SysThread must terminate either because a detach or with a join. The destroyer cannot be called directly (this means you can't create a SysThread instance in the stack). \param result The output of the run() method of this thread. \return false if the thread is not joinable, true if it has been joined. */ bool join( void* &result ); /** Returns the current thread ID. On POSIX systems, it returns the value of an unique value associated with each thread; on MS-Windows systems returns the system Thread ID. */ uint64 getID(); /** Returns the thread ID of the running thread. On POSIX systems, it returns the value of an unique value associated with each thread; on MS-Windows systems returns the system Thread ID. */ static uint64 getCurrentID(); /** Returns true if the thread is currently running. */ bool isCurrentThread(); /** Returns true if two threads represents the same low level threads */ bool equal( const SysThread *th1 ) const; static bool equal( const SysThread *th1, const SysThread *th2 ) { return th1->equal( th2 ); } /** Run method. The base class method just runs the runnable's run(). It asserts( or crashes) if the runnable has not been set in the first place. Subclasses are free to set everything they want as the run method. */ virtual void* run(); SYSTH_DATA *sysdata() const { return m_sysdata; } static void *RunAThread(void *); /** Dispose of this object without modifying the underlying system data. */ void disengage(); }; } #endif /* end of mt.h */ include/falcon/mt_posix.h000066400000000000000000000162661176363201700157770ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: mt_posix.h Multithreaded extensions - POSIX specific header. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 13 Dec 2008 13:08:38 +0100 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_MT_POSIX_H #define FALCON_MT_POSIX_H #include #include #include #include #include namespace Falcon { inline void mutex_lock( pthread_mutex_t& mtx ) { #ifdef NDEBUG pthread_mutex_lock(&mtx); #else int res = pthread_mutex_lock(&mtx); fassert( res != EINVAL ); fassert( res != EDEADLK ); fassert( res == 0 ); #endif } inline void mutex_unlock( pthread_mutex_t& mtx ) { #ifdef NDEBUG pthread_mutex_unlock(&mtx); #else int res = pthread_mutex_unlock(&mtx); fassert( res == 0 ); #endif } inline void cv_wait( pthread_cond_t& cv, pthread_mutex_t& mtx ) { #ifdef NDEBUG pthread_cond_wait(&cv, &mtx); #else int res = pthread_cond_wait(&cv, &mtx); fassert( res == 0 ); #endif } inline void cv_broadcast( pthread_cond_t& cv ) { #ifdef NDEBUG pthread_cond_broadcast(&cv); #else int res = pthread_cond_broadcast(&cv); fassert( res == 0 ); #endif } /** Generic mutex class. Directly mapping to the underlying type via inline functions. The mutex must be considered as non-reentrant. */ class Mutex { pthread_mutex_t m_mtx; public: /** Creates the mutex. Will assert on failure. */ inline Mutex() { #ifdef NDEBUG pthread_mutex_init( &m_mtx, 0 ); #else int result = pthread_mutex_init( &m_mtx, 0 ); fassert( result == 0 ); #endif } /** Destroys the mutex. Will assert on failure. */ inline ~Mutex() { #ifdef NDEBUG pthread_mutex_destroy( &m_mtx ); #else int result = pthread_mutex_destroy( &m_mtx ); fassert( result == 0 ); #endif } /** Locks the mutex. Will assert on failure -- but only in debug */ inline void lock() { #ifdef NDEBUG pthread_mutex_lock( &m_mtx ); #else int result = pthread_mutex_lock( &m_mtx ); fassert( result != EINVAL ); fassert( result != EDEADLK ); #endif } /** Unlocks the mutex. Will assert on failure -- but only in debug */ inline void unlock() { #ifdef NDEBUG pthread_mutex_unlock( &m_mtx ); #else int result = pthread_mutex_unlock( &m_mtx ); fassert( result == 0 ); #endif } /** Tries to lock the mutex. Will assert on failure. */ inline bool trylock() { int result = pthread_mutex_trylock( &m_mtx ); if ( result == EBUSY ) return false; #ifndef NDEBUG fassert( result == 0 ); #endif return true; } }; /** Generic event class. Directly mapping to the underlying type via inline functions. Well, events are definitely not the best way to handle MT things, the mutex / POSIX cv / predicate is definitely better (faster, more flexible, safer etc), but we're using a set of definite MT patterns in which using MS-WIN style events doesn't make a great difference. For low level business (i.e. implementing the script-level Waitable system) we're still using the system specific features (multiple wait on MS-WIN, condvars on POSIX). This is class is used as a middle-level equalizer in simple MT tasks as i.e. signaling non-empty queues or generic work-to-be-done flags. */ class Event { pthread_mutex_t m_mtx; pthread_cond_t m_cv; bool m_bIsSet; bool m_bAutoReset; public: /** Creates the mutex. Will assert on failure. */ inline Event( bool bAutoReset = true, bool initState = false ): m_bIsSet( initState ), m_bAutoReset( bAutoReset ) { #ifdef NDEBUG pthread_mutex_init( &m_mtx, 0 ); pthread_cond_init( &m_cv, 0 ); #else int result = pthread_mutex_init( &m_mtx, 0 ); fassert( result == 0 ); result = pthread_cond_init( &m_cv, 0 ); fassert( result == 0 ); #endif } /** Destroys the event. Will assert on failure. */ inline ~Event() { #ifdef NDEBUG pthread_mutex_destroy( &m_mtx ); pthread_cond_destroy( &m_cv ); #else int result = pthread_mutex_destroy( &m_mtx ); fassert( result == 0 ); result = pthread_cond_destroy( &m_cv ); fassert( result == 0 ); #endif } /** Signals the event. Will assert on failure -- but only in debug */ void set(); /** Resets the event. Useful only if the event is not auto-reset. */ inline void reset() { #ifdef NDEBUG pthread_mutex_lock( &m_mtx ); m_bIsSet = false; pthread_mutex_unlock( &m_mtx ); #else int result = pthread_mutex_lock( &m_mtx ); fassert( result == 0 ); m_bIsSet = false; result = pthread_mutex_unlock( &m_mtx ); fassert( result == 0 ); #endif } /** Waits on the given event. The wait is not interruptible. If a thread is blocked on this wait, the event must be signaled somewhere else to allow it to proceed and check for closure request. Falcon script level have better semantics, but this object is meant for fairly basic and low-level system related activites. If the event is auto-reset, only one waiting thread is woken up, and after the wakeup the event is automatically reset. \param to The timeout; set to < 0 for infinite timeout, 0 to check without blocking and > 0 for a number of MSecs wait. \return True if the event was signaled, false otherwise. */ bool wait( int32 to = -1 ); }; /** Thread Specific data. Directly mapping to the underlying type via inline functions. */ class ThreadSpecific { private: pthread_key_t m_key; public: ThreadSpecific() { pthread_key_create( &m_key, NULL ); } ThreadSpecific( void (*destructor)(void*) ); virtual ~ThreadSpecific() { #ifndef NDEBUG int value = pthread_key_delete( m_key ); fassert( value == 0 ); #else pthread_key_delete( m_key ); #endif } void set( void *value ) { #ifndef NDEBUG int res = pthread_setspecific( m_key, value ); fassert( res == 0 ); #else pthread_setspecific( m_key, value ); #endif } void* get() const { return pthread_getspecific( m_key ); } }; struct SYSTH_DATA { pthread_t pth; /** Mutex controlling detachment and termination. */ pthread_mutex_t m_mtxT; /** True when the thread is done and this data is disposeable. */ bool m_bDone; /** Controls joinability and destruction on run exit */ bool m_bDetached; int m_lastError; }; /** Performs an atomic thread safe increment. */ int32 atomicInc( volatile int32 &data ); /** Performs an atomic thread safe decrement. */ int32 atomicDec( volatile int32 &data ); } #endif /* end of mt_posix.h */ include/falcon/mt_win.h000066400000000000000000000124471176363201700154270ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: mt_win.h Multithreaded extensions - MS-Windows specific header. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 13 Dec 2008 13:08:38 +0100 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_MT_WIN_H #define FALCON_MT_WIN_H #include #include #include #include namespace Falcon { /** Generic mutex class. Directly mapping to the underlying type via inline functions. The mutex must be considered as non-reentrant. */ class FALCON_DYN_CLASS Mutex { CRITICAL_SECTION m_mtx; public: /** Creates the mutex. Will assert on failure. */ inline Mutex() { //TODO: Remove from inline; this as inline is a mess on windows. InitializeCriticalSectionAndSpinCount( &m_mtx, 512 ); } /** Destroys the mutex. Will assert on failure. */ inline ~Mutex() { DeleteCriticalSection( &m_mtx ); } /** Locks the mutex. Will assert on failure -- but only in debug */ inline void lock() { EnterCriticalSection( &m_mtx ); } /** Unlocks the mutex. Will assert on failure -- but only in debug */ inline void unlock() { LeaveCriticalSection( &m_mtx ); } /** Tries to lock the mutex. Will assert on failure. */ inline bool trylock() { return TryEnterCriticalSection( &m_mtx ) == TRUE; } }; /** Thread Specific data. Directly mapping to the underlying type via inline functions. */ class FALCON_DYN_CLASS ThreadSpecific { private: DWORD m_key; void (*m_destructor)(void*); ThreadSpecific* m_nextDestructor; public: ThreadSpecific() { m_key = TlsAlloc(); m_destructor = 0; } ThreadSpecific( void (*destructor)(void*) ); virtual ~ThreadSpecific() { #ifndef NDEBUG BOOL res = TlsFree( m_key ); fassert( res ); #else TlsFree( m_key ); #endif } ThreadSpecific* clearAndNext(); void set( void *value ); void* get() const { return TlsGetValue( m_key ); } }; /** Performs an atomic thread safe increment. */ inline int32 atomicInc( volatile int32 &data ) { volatile LONG* dp = (volatile LONG*) &data; return InterlockedIncrement( dp ); } /** Performs an atomic thread safe decrement. */ inline int32 atomicDec( volatile int32 &data ) { volatile LONG* dp = (volatile LONG*) &data; return InterlockedDecrement( dp ); } /** Generic event class. Directly mapping to the underlying type via inline functions. Well, events are definitely not the best way to handle MT things, the mutex / POSIX cv / predicate is definitely better (faster, more flexible, safer etc), but we're using a set of definite MT patterns in which using MS-WIN style events doesn't make a great difference. For low level business (i.e. implementing the script-level Waitable system) we're still using the system specific features (multiple wait on MS-WIN, condvars on POSIX). This is class is used as a middle-level equalizer in simple MT tasks as i.e. signaling non-empty queues or generic work-to-be-done flags. */ class FALCON_DYN_CLASS Event { HANDLE m_hEvent; public: /** Creates the mutex. Will assert on failure. */ inline Event( bool bAutoReset = true, bool initState = false ) { // Second parameter is MANUAL RESET m_hEvent = CreateEvent( NULL, bAutoReset ? FALSE : TRUE, initState ? TRUE : FALSE, NULL ); } /** Destroys the event. Will assert on failure. */ inline ~Event() { CloseHandle( m_hEvent ); } /** Signals the event. Will assert on failure -- but only in debug */ inline void set() { SetEvent( m_hEvent ); } /** Resets the event. Useful only if the event is not auto-reset. */ inline void reset() { ResetEvent( m_hEvent ); } /** Waits on the given event. The wait is not interruptible. If a thread is blocked on this wait, the event must be signaled somewhere else to allow it to proceed and check for closure request. Falcon script level have better semantics, but this object is meant for fairly basic and low-level system related activites. If the event is auto-reset, only one waiting thread is woken up, and after the wakeup the event is automatically reset. \param to The timeout; set to < 0 for infinite timeout, 0 to check without blocking and > 0 for a number of MSecs wait. \return True if the event was signaled, false otherwise. */ bool wait( int32 to = -1 ) { return WaitForSingleObject( m_hEvent, to >= 0 ? to : INFINITE ) == WAIT_OBJECT_0; } }; struct SYSTH_DATA { HANDLE hThread; unsigned nThreadID; HANDLE hEvtDetach; void *retval; /** Mutex controlling detachment and termination. */ CRITICAL_SECTION m_csT; /** True when the thread is done and this data is disposeable. */ bool m_bDone; /** Controls joinability and destruction on run exit */ bool m_bDetached; bool m_bJoining; }; } #endif /* end of mt_win.h */ include/falcon/objectfactory.h000066400000000000000000000122121176363201700167560ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: objectfactory.h Definition for the function used to create object instances. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 25 Jan 2009 13:09:48 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_OBJECT_FACTORY_H #define FLC_OBJECT_FACTORY_H #include namespace Falcon { class CoreObject; class CoreClass; /** Defines the core object factory function type. This factory function is used by core classes to create an object of the appropriate type. \param cls The class that is generating the givenn object. \param user_data A data that is to be stored in the object instance. \param deserializing true to prevent object structure initialization (it will be read from a somewhere else afterwards). */ typedef CoreObject* (*ObjectFactory)( const CoreClass *cls, void *user_data, bool bDeserializing ); /** Factory function creating a standard non-shelled object. If \b data is provided, it will be set as an opaque object in a standard non reflective FalconObject instance. */ CoreObject* OpaqueObjectFactory( const CoreClass *cls, void *data, bool bDeserializing ); /** Factory function creating a standard complete FalconObject. If \b data is provided, it will be cast to a FalconData (standard minimal carrier instance known by the system) and set in the returned FalconObject. \note This is the default factory type set for all the classes which doesn't provide property level reflectivity. */ CoreObject* FalconObjectFactory( const CoreClass *cls, void *data, bool bDeserializing ); /** Factory function creating a standard complete FalconObject and holding a sequence. If \b data is provided, it will be cast to a Sequence (FalconData with sequential access facilities) and set in the returned FalconObject. */ CoreObject* FalconSequenceFactory( const CoreClass *cls, void *data, bool bDeserializing ); /** Factory function creating a full reflective non-shelled object. If \b data is provided, it will be set as an opaque object in a fully reflective class. All the properties in the generator class must provide reflectivity. The user_data will be considered opaque and will not take part in object-wide actions (GC marking, destruction, cloning and so on). */ CoreObject* ReflectOpaqueFactory( const CoreClass *cls, void *user_data, bool ); /** Factory function creating a full reflective FalconData object. If \b data is provided, it will be set as a FalconData object in a fully reflective class. Being a FalconData, the user data will participate in the life of the reflector object, i.e. being cloned, GC marked or destroyed with its owner. \note This is the default factory functions for classes presenting all their properties as reflective. */ CoreObject* ReflectFalconFactory( const CoreClass *cls, void *user_data, bool ); /** Factory function creating a full reflective Sequence object. If \b data is provided, it will be set as a Sequence object in a fully reflective class. Being a derived from a FalconData, the user data will participate in the life of the reflector object, i.e. being cloned, GC marked or destroyed with its owner. */ CoreObject* ReflectSequenceFactory( const CoreClass *cls, void *user_data, bool ); /** Factory function creating a partially reflective non-shelled object. If \b data is provided, it will be set as an opaque object in a fully reflective class. The user_data will be considered opaque and will not take part in object-wide actions (GC marking, destruction, cloning and so on). */ CoreObject* CROpaqueFactory( const CoreClass *cls, void *user_data, bool bDeserial ); /** Factory function creating a partially reflective FalconData object. If \b data is provided, it will be set as a FalconData object in a fully reflective class. Being a FalconData, the user data will participate in the life of the reflector object, i.e. being cloned, GC marked or destroyed with its owner. \note This is the default factory functions for classes presenting some of their properties as reflective. */ CoreObject* CRFalconFactory( const CoreClass *cls, void *user_data, bool bDeserial ); /** Factory function creating a partially reflective Sequence object. If \b data is provided, it will be set as a Sequence object in a partially reflective class. Being a derived from a FalconData, the user data will participate in the life of the reflector object, i.e. being cloned, GC marked or destroyed with its owner. */ CoreObject* CRSequenceFactory( const CoreClass *cls, void *user_data, bool bDeserial ); } #endif /* end of objectfactory.h */ include/falcon/pagedict.h000066400000000000000000000051621176363201700157060ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: pagedict.h Item dictionary - paged dictionary version and related utilities. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab dic 4 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Core dictionary - paged dictionary version and related utilities. */ #ifndef flc_pagedict_H #define flc_pagedict_H #include #include #include #include #include #include #define flc_DICT_GROWTH 16 namespace Falcon { class PageDict; class VMachine; class Iterator; class FALCON_DYN_CLASS PageDict: public ItemDict { ItemTraits m_itemTraits; Map m_map; uint32 m_mark; static void PageDictIterDeletor( Iterator* iter ); public: PageDict(); PageDict( uint32 pageSize ); ~PageDict(); virtual uint32 length() const; virtual Item *find( const Item &key ) const; virtual bool findIterator( const Item &key, Iterator &iter ); virtual void smartInsert( const Iterator &iter, const Item &key, const Item &value ); virtual const Item &front() const; virtual const Item &back() const; virtual void append( const Item& item ); virtual void prepend( const Item& item ); virtual bool remove( const Item &key ); virtual void put( const Item &key, const Item &value ); virtual PageDict *clone() const; virtual void merge( const ItemDict &dict ); virtual void clear(); virtual bool empty() const; void gcMark( uint32 gen ); //======================================================== // Iterator implementation. //======================================================== protected: virtual void getIterator( Iterator& tgt, bool tail = false ) const; virtual void copyIterator( Iterator& tgt, const Iterator& source ) const; virtual void insert( Iterator &iter, const Item &data ); virtual void erase( Iterator &iter ); virtual bool hasNext( const Iterator &iter ) const; virtual bool hasPrev( const Iterator &iter ) const; virtual bool hasCurrent( const Iterator &iter ) const; virtual bool next( Iterator &iter ) const; virtual bool prev( Iterator &iter ) const; virtual Item& getCurrent( const Iterator &iter ); virtual Item& getCurrentKey( const Iterator &iter ); virtual bool equalIterator( const Iterator &first, const Iterator &second ) const; }; } #endif /* end of pagedict.h */ include/falcon/path.h000066400000000000000000000267301176363201700150660ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: path.h RFC 3986 compliant file path definition. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 27 Feb 2008 22:03:00 +0100 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_PATH_H #define FALCON_PATH_H #include #include #include namespace Falcon { class URI; /** Falcon path representation. This class is actually a string wrapper which parses the path and builds it as necessary. With respect to a string, 0 overhead is required. However, notice that this class cannot be used as a garbage string, and must be wrapped into a UserData to be part of a falcon object. Path must be provided in Falcon format (RFC 3986): path elements must be separated by forward slashes and resource identifiers must be preceded by a single "/"; in example: \code /C:/falcon/file.fal \endcode With a resource identifier, the first "/" is optional when setting the path, but the internal representation will be normalized so that it is present. Methods to transfrorm this representation to and from MS-Windows path are provided. The path is not internally checked, by this class, so any string may be set, but it may get checked i.e. when insernted in a URI. */ class FALCON_DYN_CLASS Path: public GCAlloc { String m_path; String m_device; String m_location; String m_file; String m_extension; bool m_bValid; // resStart is always 1 URI* m_owner; void compose(); public: /** Empty constructor. */ Path(); /** Path-in-uri constructor */ Path( URI *owner ); /** Path constructor from strings. */ Path( const String &path ): m_bValid( true ), m_owner(0) { set( path ); } /** Copy constructor. Copies the other path as-is. */ Path( const Path &other ): m_bValid( true ), m_owner(0) { copy( other ); } /** Copy another path. Copies the other path as-is. */ void copy( const Path &other ); /** Set a path from RFC 3986 format. */ bool set( const String &p ); /** Retrurn the path in RFC 3986 format. */ const String &get() const { return m_path; } /** Returns a path in MS-Windows format. */ String getWinFormat() const { String fmt; getWinFormat( fmt ); return fmt; } /** Stores this path in windows format in a given string. */ void getWinFormat( String &str ) const; /** Get the resource part (usually the disk specificator). */ String getResource() const { String fmt; getResource( fmt ); return fmt; } /** Stores the resource part in a given string. If the path has not a resource part, the string is also cleaned. \param str the string where to store the resource part. \return true if the path has a resource part. */ bool getResource( String &str ) const; /** Get the location part (path to file) in RFC3986 format. */ String getLocation() const { String fmt; getLocation( fmt ); return fmt; } /** Gets the location part, eventually including the resource specificator if present. */ bool getFullLocation( String &str ) const; /** Gets the location part, eventually including the resource specificator if present. */ String getFullLocation() const { String fmt; getFullLocation( fmt ); return fmt; } /** Stores the resource part in a given string. If the path has not a location part, the string is also cleaned. \param str the string where to store the location part. \return true if the path has a location part. */ bool getLocation( String &str ) const; /** Get the location part (path to file) in MS-Windows format. */ String getWinLocation() const { String fmt; getWinLocation( fmt ); return fmt; } /** Stores the location part in a given string in MS-Windows format. If the path has not a location part, the string is also cleaned. \param str the string where to store the location part. \return true if the path has a location part. */ bool getWinLocation( String &str ) const; /** Get Windows disk specificator + location (windows absolute path). \param str the string where to store the location part. \return true if the path has a disk or location part. */ bool getFullWinLocation( String &str ) const; /** returns the disk specificator + location (windows absolute path) */ String getFullWinLocation() const { String fmt; getFullWinLocation( fmt ); return fmt; } /** Get the filename part. This returns the file and extension parts separated by a '.' */ String getFilename() const { String fmt; getFilename( fmt ); return fmt; } /** Stores the filename part in a given string. If the path has not a filename part, the string is also cleaned. \param str the string where to store the filename part. \return true if the path has a filename part. */ bool getFilename( String &str ) const; /** Get the file part alone (without extension). */ String getFile() const { String fmt; getFile( fmt ); return fmt; } /** Get the file part alone (without extension). If the path has not a filename part, the string is also cleaned. \param str the string where to store the filename part. \return true if the path has a filename part. */ bool getFile( String &str ) const; /** Get the extension part. */ String getExtension() const { String fmt; getExtension( fmt ); return fmt; } /** Stores the extension part in a given string. If the path has not a extension part, the string is also cleaned. \param str the string where to store the extension part. \return true if the path has a extension part. */ bool getExtension( String &str ) const; /** Sets the resource part. */ void setResource( const String &res ); /** Sets the location part in RFC3986 format. */ void setLocation( const String &loc ); /** Sets both the resource and the location in one step. If the parameter is empty, both location and resource are cleared. If the parameter is just a resource specificator (i.e. "C:" or "/C:"), then the location is cleared. If it's just a location, then the resource is cleared. May return false if the parsing of the new content fails. \param floc Full location. \return true if correctly parsed. */ bool setFullLocation( const String &floc ); /** Sets the file part. */ void setFile( const String &file ); /** Sets the filename part (both file and extension). */ void setFilename( const String &fname ); /** Sets the extension part. */ void setExtension( const String &extension ); /** Returns true if this path is an absolute path. */ bool isAbsolute() const; /** Returns true if this path defines a location without a file */ bool isLocation() const; /** Returns true if the path is valid. Notice that an empty path is still valid. */ bool isValid() const { return m_bValid; } /** Splits the path into its constituents. This version would eventually put the resource part in the first parameter. \param loc a string where the location will be placed. \param name a string where the filename in this path will be placed. \param ext a string where the file extension will be placed. */ void split( String &loc, String &name, String &ext ); /** Splits the path into its constituents. \param res a string where the resource locator will be placed. \param loc a string where the location will be placed. \param name a string where the filename in this path will be placed. \param ext a string where the file extension will be placed. */ void split( String &res, String &loc, String &name, String &ext ); /** Splits the path into its constituents. This version will convert the output loc parameter in MS-Windows path format (backslashes). \param res a string where the resource locator will be placed. \param loc a string where the location will be placed. \param name a string where the filename in this path will be placed. \param ext a string where the file extension will be placed. */ void splitWinFormat( String &res, String &loc, String &name, String &ext ); /** Joins a path divided into its constituents into this path. Using this version it is not possible to set a resource locator (i.e. a disk unit). \param loc the path location of the file. \param name the filename. \param ext the file extension. */ void join( const String &loc, const String &name, const String &ext ); /** Joins a path divided into its constituents into this path. \param res the resource locator (i.e. disk unit) \param loc the path location of the file. \param name the filename. \param ext the file extension. \param bWin true if the location may be in MS-Windows format (backslashes). */ void join( const String &res, const String &loc, const String &name, const String &ext ); /** Add a path element at the end of a path. This extens the path adding some path element after the currently existing location portion. Leading "/" in npath, or trailing "/" in this path are ignored, and a traling "/" is forcefully added if there is a file element. In example, adding p1/p2 or /p1/p2 through this method: /C:file.txt => /C:/p1/p2/file.txt /path/ => /path/p1/p2 /path/file.txt => /path/p1/p2/file.txt */ void extendLocation( const String &npath ); Path & operator =( const Path &other ) { copy( other ); return *this; } bool operator ==( const Path &other ) const { return other.m_path == m_path; } bool operator !=( const Path &other ) const { return other.m_path != m_path; } bool operator <( const Path &other ) const { return m_path < other.m_path; } /** Converts an arbitrary MS-Windows Path to URI path. An URI valid path starts either with a filename or with a "/" if absolute. It can't start with a MS-Windows disk or unit specifier as c:\\. Also, it can't contain backslashes. This static function transforms the path parameter in place so that it is changed into a valid URI path. For example: \code path\\to\\file.txt => path/to/file.txt \\path\\to\\file.txt => /path/to/file.txt c:\\path\\to\\file.txt => /c:/path/to/file.txt \endcode @param path the path to be converted on place */ static void winToUri( String &path ); /** Converts an arbitrary URI path into a MS-Windows path. An URI valid path starts either with a filename or with a "/" if absolute. It can't start with a MS-Windows disk or unit specifier as c:\\. Also, it can't contain backslashes. This static function transforms the path parameter in place so that it is changed into a valid MSWindows path. For example: \code path/to/file.txt => path\\to\\file.txt /path/to/file.txt => \\path\\to\\file.txt /c:/path/to/file.txt = c:\\path\\to\\file.txt \endcode @note This function won't complain nor emit any warning if path is not a valid URI path in the first place. It's pretty dumb, but efficient. @param path the path to be converted on place @see getWinFormat */ static void uriToWin( String &path ); }; } #endif include/falcon/pcode.h000066400000000000000000000026671176363201700152270ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: pcode.h Utilities to manage the Falcon Virtual Machine pseudo code. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 24 Jul 2009 19:42:40 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_PCODE_H_ #define FALCON_PCODE_H_ #include #include #include namespace Falcon { class Module; class FALCON_DYN_SYM PCODE { public: /** Rotates endianity in IDs and numbers inside the PCode. * * \param code the raw pcode sequence * \param codeSize the size in bytes of the code sequence. * \param into true to actually endianize the code. */ static void deendianize( byte* code, uint32 codeSize, bool into = false ); /** Rotates endianity in IDs and numbers inside the PCode. * * \param code the raw pcode sequence * \param codeSize the size in bytes of the code sequence. */ static void endianize( byte* code, uint32 codeSize ) { deendianize( code, codeSize, true ); } static void convertEndianity( uint32 paramType, byte* targetArea, bool into=false ); static uint32 advanceParam( uint32 paramType ); }; } #endif /* FALCON_PCODE_H_ */ /* end of pcode.h */ include/falcon/pcodes.h000066400000000000000000000426371176363201700154130ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_pcodes.h The list of byte cods ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun giu 21 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef flc_PCODES_H #define flc_PCODES_H #define FALCON_PCODE_VERSION 3 #define FALCON_PCODE_MINOR 1 /** \page opcode_format Virtual machine opcode format Opcodes are formed by exactly fuor bytes. The first byte is the Op code ID, and uniquely identifies the instruction that must be performed. The remaining three bytes are the operator type an can be: - \b 00H - not used - \b 01H - an immediate integer (32 bit) - \b 02H - a string ID (32 bit) - \b 03H - an immediate double value (64 bit) - \b 04H - an immediate NIL value - \b 05H - an immediate 64 bit integer - \b 06H - Variable ID in the global table - \b 07H - Variable ID in the local table - \b 08H - Variable ID in the parameter table - \b 09H - Reserved for external symbol load - \b 0AH - a string ID representing a late binding - \b 0BH - True - \b 0CH - False - \b 0EH - 32 bit not to be decoded - \b 0FH - 64 bit not to be decoded - \b 10H - register A - Accumulator - \b 11H - register B - Extra - \b 12H - register S1 - self - \b 14H - register L1 - first latch (accessed item) - \b 15H - register L2 - second latch (accessor item) The paramters immediately follow the opcode and are stored in little endian order, if being integer, or directly in float format if being double. If the relative parameter indicator is 0E or 0F the PC is moved accordingly, but the paramters are left undecoded; this is useful for instructions that take a fixed parameter type to avoid variable encoding/decoding. Otherwise, the parameter are decoded and stored in the VM volatile registers OP1, OP2 and OP3 depending on their position. In example, a CALL 3, $func, where $func is stored in the global variable table at ID 15H, look like this in hex:
       3A 0E 05 00   03 00 00 00   15 00 00 00
    
    The first 3A is the CALL opcode, then a 0E ordering the VM not to read the first parameter (which will be handled directly by the CALL handler), but just to skip 32 bits, then the 05 informing the VM that OP2 is a variable in the global table, and the 00 indicating that there's no third parameter. This is followed by "3" and "15H", both stored in big endian order. The VM won't fill 0P1 register, while it will fill OP2 with an integer item containing 15H. */ #define P_PARAM_NOTUSED 0x00 #define P_PARAM_INT32 0x01 #define P_PARAM_STRID 0x02 #define P_PARAM_NUM 0x03 #define P_PARAM_NIL 0x04 #define P_PARAM_INT64 0x05 #define P_PARAM_GLOBID 0x06 #define P_PARAM_LOCID 0x07 #define P_PARAM_PARID 0x08 #define P_PARAM_LBIND 0x0A #define P_PARAM_TRUE 0x0B #define P_PARAM_FALSE 0x0C #define P_PARAM_NTD32 0x0E #define P_PARAM_NTD64 0x0F #define P_PARAM_REGA 0x10 #define P_PARAM_REGB 0x11 #define P_PARAM_REGS1 0x12 #define P_PARAM_FSELF 0x13 #define P_PARAM_REGL1 0x14 #define P_PARAM_REGL2 0x15 #define P_PARAM_UNB 0x16 // Range 1: parameterless ops /** END: terminate. */ #define P_END 0x00 /** NOP: No operation. */ #define P_NOP 0x01 /** PSHN: Push a NIL on top of stack. */ #define P_PSHN 0x02 /** RET: Return from a function, nil -> A. */ #define P_RET 0x03 /** RET: Return from a function, assuming A is the return value. */ #define P_RETA 0x04 /** LNIL: Load nil into the parameter, nil -> OP1. */ #define P_LNIL 0x05 // Range 2: one parameter ops /** RET: Pop last N TRY position from the TRY stack. */ #define P_PTRY 0x06 /** RETV: Return from a function and sets A to the operand, OP1 -> A. */ #define P_RETV 0x07 /** BOOL: Check is the parameter is true or false, and sets A to 0 or 1. */ #define P_BOOL 0x08 /** JMP \: Jumps at given position, OP1 -> PC. */ #define P_JMP 0x09 /** GENA: Generates an array given the OP1 previous elements in the stack. */ #define P_GENA 0x0A /** GEND: Generates a dictionray given the OP1*2 previous elements in the stack. */ #define P_GEND 0x0B /** PUSH: pushes OP1 in the stack. */ #define P_PUSH 0x0C /** PSHR: pushes a reference to OP1 in the stack. */ #define P_PSHR 0x0D /** POP OP1: pops OP1 from the stack. */ #define P_POP 0x0E /** INC: Inc prefix INC OP1: OP1 + 1 -> OP1, OP1-> A */ #define P_INC 0x0F /** DEC: Dec prefix DEC OP1: OP1 - 1 -> OP1, OP1-> A */ #define P_DEC 0x10 /** NEG: Negates OP1, - OP1 -> OP1. */ #define P_NEG 0x11 /** NOT: sets OP1 to 0 if it's true, and to 1 if it's false. */ #define P_NOT 0x12 /** TRAL \: Traverse last. * @see P_TRAV * */ #define P_TRAL 0x13 /** IPOP: Pops last OP1 elements from the stack. */ #define P_IPOP 0x14 /** XPOP: exchange OP1 and the topmost stack element. */ #define P_XPOP 0x15 /** GEOR: Generates an open range object [OP1,*[ -> A */ #define P_GEOR 0x16 /** TRY: push OP1 in the try stack. */ #define P_TRY 0x17 /** JTRY: jump out of a try, going at OP1 and popping the try stack. */ #define P_JTRY 0x18 /** RIS: Raise an exception whose value is OP1. */ #define P_RIS 0x19 /** BNOT: Binary NOT ( ~OP1 ) -> A */ #define P_BNOT 0x1A /** NOTS: Binary self NOT ( ~OP1 ) -> OP1 */ #define P_NOTS 0x1B /** PEEK: peeks the stack top and copies it in the operand. STACK[top] -> OP1 */ #define P_PEEK 0x1C // Range3: Double parameter ops /** FORK: Creates a coroutine jumping at OP2 position. OP1 is the amount of stack elements to be passed to the new thread. Both ops are fixed */ #define P_FORK 0x1D /** LD: Loads OP1 in OP2 OP1 -> OP2 */ #define P_LD 0x1E /** LDRF: Loads a reference to OP1 in OP2, &OP1 -> OP2. IF OP2 is immediate, then break the reference pointed by OP1 \todo: add opcode to break reference. */ #define P_LDRF 0x1F /** ADD: OP1 + OP2 -> A */ #define P_ADD 0x20 /** SUB: OP1 - OP2 -> A */ #define P_SUB 0x21 /** MUL: OP1 * OP2 -> A */ #define P_MUL 0x22 /** DIV: OP1 / OP2 -> A */ #define P_DIV 0x23 /** MOD: OP1 % OP2 -> A */ #define P_MOD 0x24 /** POW: OP1^OP2 -> A */ #define P_POW 0x25 /** ADDS: add to self, OP1 + OP2 -> OP1 */ #define P_ADDS 0x26 /** SUBS: subtract from self, OP1 - OP2 -> OP1 */ #define P_SUBS 0x27 /** MULS: multiply to self, OP1 * OP2 -> OP1 */ #define P_MULS 0x28 /** DIVS: divide from self, OP1 / OP2 -> OP1 */ #define P_DIVS 0x29 /** MODS: self module, OP1 % OP2 -> OP1 */ #define P_MODS 0x2A /** BAND: Binary and ( OP1 & OP2 ) -> A */ #define P_BAND 0x2B /** BOR: Binary OR ( OP1 | OP2 ) -> A */ #define P_BOR 0x2C /** BXOR: Binary XOR ( OP1 ^ OP2 ) -> A */ #define P_BXOR 0x2D /** ANDS: self binary and ( OP1 & OP2 ) -> OP1 */ #define P_ANDS 0x2E /** ORS: self binary OR ( OP1 | OP2 ) -> OP1 */ #define P_ORS 0x2F /** XORS: self binary XOR ( OP1 ^ OP2 ) -> OP1 */ #define P_XORS 0x30 /** GENR: Generates an range object [OP1, OP2] -> A */ #define P_GENR 0x31 /** EQ: Compares OP1 and OP2, and if they are equal 1 -> A, else 0 -> A */ #define P_EQ 0x32 /** NEQ: Compares OP1 and OP2, and if they are equal 0 -> A, else 1 -> A */ #define P_NEQ 0x33 /** GT: Compares OP1 and OP2, and if OP1 \> OP2 1 -> A, else 0 -> A */ #define P_GT 0x34 /** GE: Compares OP1 and OP2, and if OP1 \>= OP2 1 -> A, else 0 -> A */ #define P_GE 0x35 /** LT: Compares OP1 and OP2, and if OP1 \< OP2 1 -> A, else 0 -> A */ #define P_LT 0x36 /** LE: Compares OP1 and OP2, and if OP1 \<= OP2 1 -> A, else 0 -> A */ #define P_LE 0x37 /** IFT \, $sym If OP2 is true then jumps to OP1 (OP1 -> PC). */ #define P_IFT 0x38 /** IFF \, $sym If OP2 is false then jumps to OP1 (OP1 -> PC). */ #define P_IFF 0x39 /** CALL \, $sym. calls subroutine at OP2; at subroutine returns, removes OP1 params from the stack. */ #define P_CALL 0x3A /** INST $sym, \. As call, but does not changes self register. */ #define P_INST 0x3B /** ONCE: Execute a code portion only once. If the function OP2 has not been executed, then proceeds, else jumps at OP1. */ #define P_ONCE 0x3C /** LDV: Load from vector OP1 the item at OP2 in A, OP1[ OP2 ] -> A */ #define P_LDV 0x3D /** LDV: Load from object OP1 the property OP2 in A, OP1.OP2 -> A */ #define P_LDP 0x3E /** TRAN \, \ Traverse next. @see P_TRAV */ #define P_TRAN 0x3F /** LDAS \, V: Load all in stack. OP1: size to be loaded in the stack. OP2: vector to be unpacked. Each element in V is verbatim copied in the stack, the highest stack element becomes the last element in the array. */ #define P_LDAS 0x40 /** SWCH: switch. \todo explain */ #define P_SWCH 0x41 /** IN: sets A to 1 if OP1 is in set OP2, else sets A to 0 */ #define P_IN 0x46 /** NOIN: sets A to 0 if OP1 is in set OP2, else sets A to 1 */ #define P_NOIN 0x47 /** PROV: sets A to 1 if OP1 has property OP2, else sets A to 0 */ #define P_PROV 0x48 /** STVS: Store the topmost stack element into property OP2 of OP1: STACK -> OP1.OP2 */ #define P_STVS 0x49 /** STPS: Store the topmost stack element into position OP2 of OP1: STACK -> OP1[OP2] */ #define P_STPS 0x4A /** AND: Logical AND if (op1 is true and op2 is true ) true->A */ #define P_AND 0x4B /** AND: Logical OR if (op1 is true or op2 is true ) true->A */ #define P_OR 0x4C // Range 4: ternary opcodes /** STV: Store OP3 into vector OP1 at position OP2, OP3 -> OP1[OP2] */ #define P_STV 0x4F /** STP: Store OP3 into Property OP2 of OP1: OP3 -> OP1.OP2 */ #define P_STP 0x50 /** LDVT: Load from vector OP1 the item at OP2 in OP3, OP1[ OP2 ] -> OP3 */ #define P_LDVT 0x51 /** LDPT: Load from object OP1 the property OP2 in OP3, OP1.OP2 -> OP3 */ #define P_LDPT 0x52 /** STVR: store reference to OP3 into vector OP1 at pos OP2: &OP3 -> OP1[OP2] */ #define P_STVR 0x53 /** STPR: store reference to OP3 into property OP2 of OP1: &OP3 -> OP1.OP2 */ #define P_STPR 0x54 /** TRAV, Traverse an iterable Sequence. TRAV is a complex instruction that involves the stack, a set of target variables, and 3 other opcodes (TRAN traverse next, TRDN, Traverse dropping next and TRAL, traverse last). This instructions are meant to map the for/in loop at Falcon language level. TRAV initializes the loop adding an iterator to the SOURCE variable on top of the stack, or eventually skips the loop altogether if the source is empty (or nil). TRAV declares how many variables will receive elements from source. Those variables are then stored immediately below as NOP opcodes with a single parameter (a target variable). If source is a dictionary sequence, TRAV must be provided with exactly two variables that will receive the key and the value of each element; otherwise, if provided with one variable, TRAV will store the current item in that variable, and if provided with more variables, it will try to consider each element as an array and unpack it in the variables. TRAN and TRDN work similarly to TRAN, but they use the variable at stack top as the current iterator. A for/in loop is divided in 4 areas, all of which optional. The main area (MAIN) is executed for each item in the collection. The FIRST block is executed only BEFORE the first time MAIN is executed, the MIDDLE block is executed after each MAIN block execution except for the last one (when the iterator has not a "next" element), and the LAST block which is executed after the MAIN for the last element. If a MIDDLE block is provided, then then loop is configured as if there was a LAST block, possibly left empty if there isn't any code for that. TRAN skips to the next element and loads the variables in its var block. If there is a loop to be performed, it loops to the MAIN block, otherwise it proceeds. TRDN is similar to TRAN, but instead advancing to the next element, it deletes the current element, and loads the loop variables with the new value of the iterator. It is generated 1:1 on "continue dropping" instruction. BOTH TRAN and TRDN are meant to execute the last block after the last element main block for the last element has been executed. If there isn't any last block, it is simply left empty and the jump label lands at the same position of the loop end (same target as a "break" instruction). TRAL simply jumps to its label if the current item is the last item in the sequence (i.e. if the iterator on stack top ! hasNext()). This switches the middle and last blocks. \code TRAV NV, p_label_loop_end, $S ==> push iterator($S) {VARBLOCK: NV rep of NOP $v } [FIRST BLOCK ... TRDN NV, label_loop_begin, label_loop_last {VARBLOCK: NV rep of NOP $v } ... ] label_loop_begin: [MAIN BLOCK ... TRDN NV, label_loop_begin, label_loop_last {VARBLOCK: NV rep of NOP $v } ... ] [MIDDLE BLOCK ; this skips this block if the current element is the last one. TRAL label_loop_last ... TRDN NV, label_loop_begin, label_loop_last {VARBLOCK: NV rep of NOP $v } ... ] label_loop_next: TRAN NV, label_loop_begin {VARBLOCK: NV rep of NOP $v } label_loop_last: [LAST BLOCK ... TRDN 0, label_loop_end, label_loop_end ... ] label_loop_end: POP p_label_loop_end: \endcode \note TRAV, TRAN and TRDN VARBLOCK elements can contain only global, local or parameter symbols. \note TRDN has a special behavior when OP1 == 0: it pops the last element in the sequence and goes to loop end, instead of erasing the current element and preparing the loop variables. \note TRAL also advances the iterator past the last element, to have the position of the iterator at last block after the last element, as TRDN and TRAV would leave it, and so that "TRDN 0, .., .." can go back one position and delete the element. */ #define P_TRAV 0x55 /** INC postfix INCP V : V->A, V := V + 1 -> B */ #define P_INCP 0x56 /** DEC Postfix DECP V : V->A, V := V - 1 -> B */ #define P_DECP 0x57 /** Shift left op1 of op2 positions and place the result in A*/ #define P_SHL 0x58 /** Shift right op1 of op2 positions and place the result in A*/ #define P_SHR 0x59 /** Shift left op1 of op2 positions and place the result in OP1*/ #define P_SHLS 0x5A /** Shift right op1 of op2 positions and place the result in OP1*/ #define P_SHRS 0x5B /** Create a closure. CLOS N, , N elements from the stack are extracted and an array is made as for GENA; The function in src is cloned into , and the closed array is added in tgt. */ #define P_CLOS 0x5C /** Shift right op1 of op2 positions and place the result in OP1*/ #define P_SHRS 0x5B /** PUSH LITERAL PSHL OP1 -> OP1 => stack This opcode pushes the operand on the stack without peforming any check (and without dereferencing it). It allows to push raw references in the stack. */ #define P_PSHL 0x5D /** free */ // free 0x5D /** POWS: self power, OP1 ** OP2 -> OP1 */ #define P_POWS 0x5E /** Load from byte or character from sting */ #define P_LSB 0x5F /** EVAL Perform functional evaluation, direct call or return the value as-is FORB OP1 -> A := if OP1 is callable if OP1 is array eval(op1) else OP1() else OP1 */ #define P_EVAL 0x60 /** SELECT: Select a branch depending on a variable type. Similar to switch. */ #define P_SELE 0x61 /** INDI: Indirect symbol reference; get the value of the string OP1 */ #define P_INDI 0x62 /** STEX: String expansion; expand string in OP1. */ #define P_STEX 0x63 /** TRAC: Traverse change. * TRAC OP1 => OP1-> current value * Change current (value) item in traversal loops to shallow copy of OP1 * @see TRAV * */ #define P_TRAC 0x64 /** WRT: Write on standard output (TODO: also other vm streams using parameters) */ #define P_WRT 0x65 /** STO: STO OP1, OP2 -> OP1 := OP2. Works as LD, but it overwrites target value even if it's a reference. */ #define P_STO 0x66 /** FORB Forward binding FORB OP1, OP2 -> A := lbind( OP1, OP2 ). */ #define P_FORB 0x67 /** OOB Marks, unmarks or checks the code to be an oob. OOB NTD32, OP if NTD32 == 0 -> A := deOob( OP ) if NTD32 == 1 -> A := oob( OP ) if NTD32 == 2 -> A := isOob( OP ) ? deOob( OP ) : oob( OP ) if NTD32 == 3 (or else ) A := isOob( OP ) */ #define P_OOB 0x68 /** TRDN: Traverse dropping next. * TRDN \, \, \ * @see TRAV * */ #define P_TRDN 0x69 /** EXEQ: Exactly equal. Checks for item identity. */ #define P_EXEQ 0x70 #define FLC_PCODE_COUNT 0x71 #endif /* end of pcodes.h */ include/falcon/pointerpage.h000066400000000000000000000015141176363201700164400ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_itempage.h Definition of the page that holds garbageable data pointers. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun ott 4 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Definition of the page that holds garbageable data pointers. */ #ifndef flc_flc_itempage_H #define flc_flc_itempage_H #include #include namespace Falcon { /** Item page. A segregated allocation page holding Falcon VM items. */ class GarbagePage: public SegregatedPage< Garbageable * > {}; } #endif /* end of flc_itempage.h */ include/falcon/poopseq.h000066400000000000000000000040741176363201700156150ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: poopseq.h Virtual sequence that can be used to iterate over poop providers. *AT THE MOMENT* providing just "append" method to re-use sequence comprehension in OOP and POOP contexts. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 10 Aug 2009 10:53:29 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_POOPSEQ_H #define FALCON_POOPSEQ_H #include #include #include namespace Falcon { class FALCON_DYN_CLASS PoopSeq: public Sequence { Item m_appendMth; uint32 m_mark; VMachine* m_vm; public: PoopSeq( VMachine* vm, const Item &iobj ); PoopSeq( const PoopSeq& other ); virtual ~PoopSeq(); virtual const Item &front() const; virtual const Item &back() const; virtual void clear(); virtual bool empty() const; virtual void append( const Item &data ); virtual void prepend( const Item &data ); virtual PoopSeq* clone() const; virtual void gcMark( uint32 gen ); //============================================================== // Iterator management // protected: virtual void getIterator( Iterator& tgt, bool tail = false ) const; virtual void copyIterator( Iterator& tgt, const Iterator& source ) const; virtual void insert( Iterator &iter, const Item &data ); virtual void erase( Iterator &iter ); virtual bool hasNext( const Iterator &iter ) const; virtual bool hasPrev( const Iterator &iter ) const; virtual bool hasCurrent( const Iterator &iter ) const; virtual bool next( Iterator &iter ) const; virtual bool prev( Iterator &iter ) const; virtual Item& getCurrent( const Iterator &iter ); virtual Item& getCurrentKey( const Iterator &iter ); virtual bool equalIterator( const Iterator &first, const Iterator &second ) const; }; } #endif /* end of poop.h */ include/falcon/proptable.h000066400000000000000000000164441176363201700161230ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: proptable.h Very simple double entry table for pure properties. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven ott 14 2005 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Very simple double entry table for pure properties. */ #ifndef flc_proptable_H #define flc_proptable_H #include #include #include #include #include #include namespace Falcon { class CoreObject; /** Descriptor of single property. * This structure descrbes the contents of a single property; * It also stores all the data needed for reflection. */ struct PropEntry { enum { NO_OFFSET = 0xFFFFFFFF }; /** Name of this property */ const String *m_name; /** True if this property is read-only */ bool m_bReadOnly; /** True if this property is write-only. If this property is accessed via accessors, but has only the set accessor, it cannot be read back (not even at C++ level). Doing so would cause an access error (for write-only read access). \return true if this property is write-only. */ bool isWriteOnly() const { return m_eReflectMode == e_reflectSetGet && m_reflection.gs.m_getterId == NO_OFFSET; } /** Reflection mode. */ t_reflection m_eReflectMode; /** Module-specific property reflection data */ void *reflect_data; /** Default value of the property */ SafeItem m_value; union { uint32 offset; struct { reflectionFunc to; reflectionFunc from; } rfunc; struct { uint32 m_getterId; uint32 m_setterId; } gs; } m_reflection; /** Reflects a single property Item -> user_data */ void reflectTo( CoreObject *instance, void *user_data, const Item &prop ) const; /** Reflects a single property. user_data -> Item */ void reflectFrom( CoreObject *instance, void *user_data, Item &prop ) const; }; /** Very simple double entry table for pure properties. Property tables are a convenient way to store efficiently and search at the fastest possible speed a set of pre-defined pure Falcon strings stored in a safe memory area that does not need procetion, collecting or reference counting. Unluckily, this is a quite rare situation. Luckily, this is exactly the situation of Falcon objects (and classes), whose property names are allocated as Falcon Strings in the same Module that declares them. The property table stores also informations about the class that declared the property. In fact, in case of inheritance, the property may come from an inner class, that needs to be managed through a different handler. The needed information is the ID of the class in the inheritance list, which is used to pick the correct user_data in the object instance, and the user_data handler (ObjectHandler). Actually, the ObjectHandler information would not be needed, but it is kept here for caching. There is only one property table per class in each program, so it's an affordable cost. Once created, the m_value field of each entry is read-only. It stores enumeration and constant initialization values for properties and method/class entries for methods/class accessors. A property table has two characteristics that are accounted at its creation: - It's reflective if it has at least a reflective property. - It's static if all of its properties are either reflective or read-only. */ class FALCON_DYN_CLASS PropertyTable: public BaseAlloc { public: uint32 m_size; uint32 m_added; bool m_bReflective; bool m_bStatic; PropEntry *m_entries; public: PropertyTable( uint32 size ); PropertyTable( const PropertyTable & ); ~PropertyTable(); uint32 size() const { return m_size; } uint32 added() const { return m_added; } bool isReflective() const { return m_bReflective; } bool isStatic() const { return m_bStatic; } /** Analyzes the table and sets its properties. */ void checkProperties(); bool findKey( const String &key, uint32 &pos ) const; PropEntry &getEntry( uint32 pos ) { return m_entries[pos]; } const PropEntry &getEntry( uint32 pos ) const { return m_entries[pos]; } const Item *getValue( const String &key ) const { uint32 pos; if( findKey( key, pos ) ) return getValue( pos ); return 0; } Item *getValue( uint32 pos ) { return &m_entries[ pos ].m_value; } const Item *getValue( uint32 pos ) const { return &m_entries[ pos ].m_value; } const String *getKey( uint32 pos ) const { return m_entries[pos].m_name; } PropEntry &append( const String *name ); bool append( const String *key, const Item &itm, bool bReadOnly = false ) { if ( m_added <= m_size ) { PropEntry e = append( key ); e.m_value = itm; e.m_bReadOnly = bReadOnly; e.m_eReflectMode = e_reflectNone; return true; } return false; } bool append( const String *key, const Item &itm, t_reflection mode, uint32 offset, bool bReadOnly = false ) { if ( m_added <= m_size ) { PropEntry e = append( key ); e.m_value = itm; e.m_bReadOnly = bReadOnly; e.m_eReflectMode = mode; e.m_reflection.offset = offset; return true; } return false; } bool append( const String *key, const Item &itm, reflectionFunc func_from, reflectionFunc func_to = 0 ) { if ( m_added <= m_size ) { PropEntry e = append( key ); e.m_value = itm; e.m_bReadOnly = func_to == 0; e.m_eReflectMode = e_reflectFunc; e.m_reflection.rfunc.from = func_from; e.m_reflection.rfunc.to = func_to; return true; } return false; } PropEntry &appendSafe( const String *key ) { m_entries[m_added].m_name = key; PropEntry *ret = m_entries + m_added; m_added++; return *ret; } void appendSafe( const String *key, const Item &itm, bool bReadOnly = false ) { m_entries[m_added].m_name = key; m_entries[m_added].m_value = itm; m_entries[m_added].m_bReadOnly = bReadOnly; m_entries[m_added].m_eReflectMode = e_reflectNone; m_added++; } void appendSafe( const String *key, const Item &itm, t_reflection mode, uint32 offset, bool bReadOnly = false ) { m_entries[m_added].m_name = key; m_entries[m_added].m_value = itm; m_entries[m_added].m_bReadOnly = bReadOnly; m_entries[m_added].m_eReflectMode = mode; m_entries[m_added].m_reflection.offset = offset; m_added++; } void appendSafe( const String *key, const Item &itm, reflectionFunc func_from, reflectionFunc func_to = 0 ) { m_entries[m_added].m_name = key; m_entries[m_added].m_value = itm; m_entries[m_added].m_bReadOnly = func_to == 0; m_entries[m_added].m_eReflectMode = e_reflectFunc; m_entries[m_added].m_reflection.rfunc.from = func_from; m_entries[m_added].m_reflection.rfunc.to = func_to; m_added++; } }; } #endif /* end of proptable.h */ include/falcon/rampmode.h000066400000000000000000000060351176363201700157320ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: rampmode.h Ramp mode - progressive GC limits adjustment algoritmhs ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 18 Mar 2009 19:55:25 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_RAMP_MODE_H #define FALCON_RAMP_MODE_H #include #include #include #include namespace Falcon { /** Ramp-up GC parameters base class. The subclasses of this virtual class contain a configurable algorithm used by the Memory Pool to update its status after a succesful garbage collection. */ class FALCON_DYN_CLASS RampMode: public BaseAlloc { protected: size_t m_normal; size_t m_active; public: RampMode() {} virtual ~RampMode(); /** Called before starting a scan loop. No need for parameters as mempool, memory sizes and statistics are globally available. */ virtual void onScanInit()=0; /** Called when the scan is complete and there is the need for a new calculation. No need for parameters as mempool, memory sizes and statistics are globally available. */ virtual void onScanComplete()=0; /** Called when first set. */ virtual void reset(); /** Returns the lastly calculated memory level for the normal status. */ size_t normalLevel() const { return m_normal; } /** Returns the lastly calculated memory level for the active status. */ size_t activeLevel() const { return m_active; } }; #define RAMP_MODE_OFF 0 /** Disables ramping. Never changes the warning levels. */ class FALCON_DYN_CLASS RampNone: public RampMode { public: RampNone(): RampMode() {} virtual ~RampNone(); virtual void onScanInit(); virtual void onScanComplete(); }; /** Enforces a strict inspection policy. The warning active level is set to the quantity of memory used after the last collection loop. */ class FALCON_DYN_CLASS RampStrict: public RampMode { public: RampStrict(): RampMode() {} virtual ~RampStrict(); virtual void onScanInit(); virtual void onScanComplete(); }; #define RAMP_MODE_STRICT_ID 1 class FALCON_DYN_CLASS RampLoose: public RampMode { public: RampLoose(): RampMode() {} virtual ~RampLoose(); virtual void onScanInit(); virtual void onScanComplete(); }; #define RAMP_MODE_LOOSE_ID 2 class FALCON_DYN_CLASS RampSmooth: public RampMode { size_t m_pNormal; size_t m_pActive; numeric m_factor; public: RampSmooth( numeric factor ); virtual ~RampSmooth(); virtual void reset(); virtual void onScanInit(); virtual void onScanComplete(); }; #define RAMP_MODE_SMOOTH_SLOW_ID 3 #define RAMP_MODE_SMOOTH_FAST_ID 4 #define RAMP_MODE_COUNT 5 #define DEFAULT_RAMP_MODE RAMP_MODE_SMOOTH_SLOW_ID } #endif /* end of rampmode.h */ include/falcon/rangeseq.h000066400000000000000000000041351176363201700157320ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: rangeseq.h Virtual sequence that can be used to iterate over ranges. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 06 Aug 2009 22:10:00 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_RANGESEQ_H #define FALCON_RANGESEQ_H #include #include #include namespace Falcon { class FALCON_DYN_CLASS RangeSeq: public Sequence { int64 m_start; int64 m_end; int64 m_step; mutable Item m_number; public: RangeSeq( const CoreRange &rng ); RangeSeq( int64 s, int64 e, int64 step ); RangeSeq( const RangeSeq& other ): m_start( other.m_start ), m_end( other.m_end ), m_step( other.m_step ) {} virtual ~RangeSeq(); virtual const Item &front() const; virtual const Item &back() const; virtual void clear(); virtual bool empty() const; virtual void append( const Item &data ); virtual void prepend( const Item &data ); virtual RangeSeq* clone() const; //============================================================== // Iterator management // protected: virtual void getIterator( Iterator& tgt, bool tail = false ) const; virtual void copyIterator( Iterator& tgt, const Iterator& source ) const; virtual void insert( Iterator &iter, const Item &data ); virtual void erase( Iterator &iter ); virtual bool hasNext( const Iterator &iter ) const; virtual bool hasPrev( const Iterator &iter ) const; virtual bool hasCurrent( const Iterator &iter ) const; virtual bool next( Iterator &iter ) const; virtual bool prev( Iterator &iter ) const; virtual Item& getCurrent( const Iterator &iter ); virtual Item& getCurrentKey( const Iterator &iter ); virtual bool equalIterator( const Iterator &first, const Iterator &second ) const; }; } #endif /* FALCON_RANGESEQ_H */ /* end of rangeseq.h */ include/falcon/rc_version.h000066400000000000000000000020211176363201700162660ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: verion.h WINDOWS RC Versioning helper ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2007-07-25 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file RC versioning helper. This small header is used in RC files, or by C programs, create version strings from version numbers stored in #define directive as numeric values. */ #include #define FALCON_MAKE_VERSION_NUMBER(v1,v2,v3,v4) v1, v2, v3, v4 #define FALCON_MAKE_VERSION_STRING(v1,v2,v3) STR(v1) "." STR(v2) "." STR(v3) #define FALCON_MAKE_VERSION_STRING_4(v1,v2,v3,v4) STR(v1) "." STR(v2) "." STR(v3) "." STR(v4) #define FALCON_MAKE_VERSION_STRING_RC(v1,v2,v3,v4) STR(v1) ", " STR(v2) ", " STR(v3) ", " STR(v4) #ifndef VS_VERSION_INFO #define VS_VERSION_INFO 1 #endif include/falcon/refcount.h000066400000000000000000000027041176363201700157520ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_refcount.h Reference count system. This is not intendended for VM or API usage, but only for internal FALCON compiler(s) usage. VM has special handling of refcounting when needed. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer giu 9 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Reference count system. This is not intendended for VM or API usage, but only for internal FALCON compiler(s) usage. VM has special handling of refcounting when needed. */ #ifndef FALCON_REFCOUNTER_H #define FALCON_REFCOUNTER_H namespace Falcon { template class Refcounter { T m_item; int m_count; public: Refcounter(): m_count(0) {} virtual ~Refcounter() {} /** Creator. Sets the count to zero. The creator must incref, it it wishes. */ Refcounter( const T &val ): m_item( val ), m_count(0) {} Refcounter( const Refcounter &source ): m_item( source.m_item ), m_count( 0 ) {} void incref() { m_count++; } void decref() { m_count--; if ( m_count <= 0 ) delete this ; } T &access() { return m_item; } T &operator *() { return m_item; } }; } #endif /* end of flc_refcount.h */ include/falcon/reflectfunc.h000066400000000000000000000121411176363201700164210ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: reflectionFunc.h Generic property reflection function definition. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 18 Jun 2008 22:33:55 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Generic property reflection function definition. */ #ifndef FALCON_REFLECTION_FUNC_H #define FALCON_REFLECTION_FUNC_H #include namespace Falcon { class CoreObject; class Item; struct PropEntry; /** Reflection type enumeration. Determines how reflective properties are accounted for. */ typedef enum { e_reflectNone = 0, e_reflectBool, e_reflectByte, e_reflectChar, e_reflectShort, e_reflectUShort, e_reflectInt, e_reflectUInt, e_reflectLong, e_reflectULong, e_reflectLL, e_reflectULL, e_reflectFloat, e_reflectDouble, e_reflectFunc, e_reflectSetGet } t_reflection; /** Callback function for reflective properties needing complete reflection. It is possible to give complete reflection to properties by assigning the VarDef (definition of the property type) a reflection function. When a configureFrom/configureTo is explicitly asked, or if the user data is reflective, this function may be called to configure the property value. \param instance The object were the user_data to be reflected is stored. The virtual machine that is involved in the operation can be retreived from there; errors on variable type or conditional read only settings can be enforced by raising on this VM. \param user_data The data on which reflection is going to be performed. It is not necessarily the user_data stored inside the instance, as the model allows to get or set reflective data also from/to external sources. \param property The property to be set or to get the setting from. When retreiving, the property is the original item that will be copied in the final property on success. When setting, it's the phisical property in the object to be set by this method. \param entry The entry descriptor indicating the property that has been called. \return When setting the value, should return false if the value is incompatible with the final object data structure (that is, if a parameter error should have been raised on the virtual machine). */ typedef void (*reflectionFunc)(CoreObject *instance, void *user_data, Item &property, const PropEntry& entry ); typedef void reflectionFuncDecl(CoreObject *instance, void *user_data, Item &property, const PropEntry& entry ); } /** Little macro to automatize reflection of strings. This is not a very clean programming technique, but is effective. Used inside a reflectionFunc with parameters named as the standard one (i.e. as those provided in the function declaration), it is able to correctly reflect a string property into an object which provides an accessor to a Falcon::String. The macro raises a standard parameter error if the property is not a string during setting, and checks for the property being unchanged before resetting it to a new string during reading. As it raises a ParamError, proper .h files (falcon/error.h) must be included. Used mainly by the engine, users may find it useful. \param obj the object (pointer) containing the string to be reflected. \param accessor the name of the accessor used to read the variable. */ #define FALCON_REFLECT_STRING_FROM( obj, accessor ) \ property = new CoreString( obj->accessor() ); /** Little macro to automatize reflection of strings. This stores the data coming from the engine into the object via an accessor. Used mainly by the engine, users may find it useful. \see FALCON_REFLECT_STRING_FROM \param obj the object (pointer) containing the string to be reflected. \param accessor the name of the accessor used to read the variable. */ #define FALCON_REFLECT_STRING_TO( obj, accessor ) \ if ( ! property.isString() ) {\ throw new ParamError( ErrorParam( e_inv_params ).extra( "S" ) );\ }\ obj->accessor( *property.asString() );\ /** Little macro to automatize reflection of integers. \see FALCON_REFLECT_STRING_FROM \param obj the object (pointer) containing the string to be reflected. \param accessor the name of the accessor used to read the variable. */ #define FALCON_REFLECT_INTEGER_FROM( obj, accessor ) \ property = (int64) obj->accessor();\ /** Little macro to automatize reflection of integers. \see FALCON_REFLECT_STRING_TO \param obj the object (pointer) containing the string to be reflected. \param accessor the name of the accessor used to read the variable. */ #define FALCON_REFLECT_INTEGER_TO( obj, accessor ) \ if ( ! property.isOrdinal() ) {\ throw new ParamError( ErrorParam( e_inv_params ).extra( "N" ) );\ }\ obj->accessor( (uint32) property.forceInteger() );\ #endif /* FALCON_REFLECTION_FUNC_H */ /* end of reflectionFunc.h */ include/falcon/reflectobject.h000066400000000000000000000022271176363201700167400ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: reflectobject.h Base abstract class for object with reflective data (providing a user data). ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 25 Jan 2009 15:42:48 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_REFLECT_OBJECT_H #define FLC_REFLECT_OBJECT_H #include namespace Falcon { class FALCON_DYN_CLASS ReflectObject: public CoreObject { public: ReflectObject( const CoreClass* generator, void* userData ); ReflectObject( const CoreClass* generator, FalconData* fdata ); ReflectObject( const CoreClass* generator, Sequence* seq ); ReflectObject( const ReflectObject &other ); virtual ~ReflectObject(); virtual bool setProperty( const String &prop, const Item &value ); virtual bool getProperty( const String &key, Item &ret ) const; virtual ReflectObject *clone() const; }; } #endif /* end of reflectobject.h */ include/falcon/refstring.h000066400000000000000000000012521176363201700161250ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_refstring.h Referenced string ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer giu 9 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file This file defines the StringRef type. */ #ifndef FALCON_STRINGREF_H #define FALCON_STRINGREF_H #include #include namespace Falcon { typedef Refcounter< Hstring > StringRef; } #endif /* end of flc_refstring.h */ include/falcon/rosstream.h000066400000000000000000000035431176363201700161460ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: rosstream.h Definition of read only string stream. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab ago 19 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Definition of read only string stream. */ #ifndef flc_rosstream_H #define flc_rosstream_H #include namespace Falcon { /** Read only string stream for fast operations. The base class StringStream copies strings used as source data. When the string stream is just bound to read from the string as if it were a file, i.e. with encoders, this is not desirable. This implementation takes the buffer of the incoming string as stream bytes source, and never alter or destroys the content of the given buffer. The original string must stay available and ummodified for the whole life of the read only string stream, or bad things will happen. This stringstream can be constructed also with a static char * source, so that it is possible to use statically written code as source of streams. */ class FALCON_DYN_CLASS ROStringStream: public StringStream { public: ROStringStream( const String &source ); ROStringStream( const char *source, int size = -1 ); ROStringStream( const ROStringStream &other ); virtual ~ROStringStream() { close(); } virtual bool close(); virtual int32 write( const void *buffer, int32 size ); virtual int32 write( const String &source ); virtual int32 writeAvailable( int32 msecs, const Falcon::Sys::SystemData* ); virtual bool truncate( int64 pos=-1 ); virtual ROStringStream *clone() const; }; } #endif /* end of rosstream.h */ include/falcon/runtime.h000066400000000000000000000234111176363201700156060ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_runtime.h Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer ago 18 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef flc_RUNTIME_H #define flc_RUNTIME_H #include #include #include #include #include #include namespace Falcon { class VMachine; /** Structure holding live dependency informations. * * Modules loaded through "load" directive will export their exported symbols * to the VM. Modules referenced through "import" will be linked but won't export * symbols, and they will be referenced as "private". */ class ModuleDep: public BaseAlloc { Module *m_module; bool m_bIsPrivate; public: ModuleDep( Module *mod, bool priv = false ): m_module( mod ), m_bIsPrivate( priv ) { mod->incref(); } ~ModuleDep() { m_module->decref(); } Module *module() const { return m_module; } bool isPrivate() const { return m_bIsPrivate; } void setPrivate( bool mode ) { m_bIsPrivate = mode; } }; /** Map of module names-> modules. ( const String *, ModuleDep * ) */ class FALCON_DYN_CLASS ModuleMap: public Map { public: ModuleMap(); }; /** Vector of ModuleDep objects. * * Ownership is in not in the vector, the owner class must destroy ModuleDep items at * when required. */ class FALCON_DYN_CLASS ModuleVector: public GenericVector { public: ModuleVector(); Module *moduleAt( uint32 pos ) const { return (*(ModuleDep**) at(pos))->module(); } ModuleDep *moduleDepAt( uint32 pos ) const { return *(ModuleDep**) at(pos); } }; /** Runtime code representation. A runtime object represents a set of module that constitutes the existing runtime library. Two basic operations can be done on a runtime set: module addition and linking. A runtime module is given a runtime error manager to inform the calling application about errors during module addition and linking (mainly, load failure ). If provided with a ModuleLoader instance, the Runtime instance will ask the module loader to load any module that the given modules require to import, unless already present in the module set; else, the calling application must provide the modules to the Runtime one by one. This allows to put the embedding application in complete control of what modules may be loaded and how, and allows it to provide its own internal modules instead of the required ones. At the moment, the modules must be pre-compiled in hsc format, but in future the module loader may detect if a module is given in source and compile it if the hsc is not present; this is a behavior bound in the module loader. \todo In future, this class may support serialization to store the linked modules on disk, so that it may become a somewhat complete linker. */ class FALCON_DYN_CLASS Runtime: public BaseAlloc { ModuleMap m_modules; ModuleVector m_modvect; ModuleLoader *m_loader; VMachine *m_provider; Map m_modPending; bool m_hasMainModule; public: /** Creates the runtime and allows module load requests. Using this version of the constructor, the runtime will try to resolve module load requests that are embedded in each module before trying to link given modules. An error manager can be specified later on with errorHandler(). The error manager is used to feedback the calling application or the user with rilevant error codes (i.e. the module loading faulire, symbol duplication or missing externals). Neither the error manager nor the module loader are deleted at Runtime object deletion. \param loader a module loader that will be used to load modules when they are required by other modules at link() time. \param provider a virtual machine where the runtime must be linked later on. */ Runtime( ModuleLoader *loader, VMachine *provider = 0 ); /** Creates the runtime. Using this version of the constructor, the runtime will not load any module that may be defined in the dependency list during the link phase; the calling application must load or provide the modules on its behalf and feed them in the link() method before the subsequesing modules require some symbols as external. An error manager can be specified later on with errorHandler(). The error manager is used to feedback the calling application or the user with rilevant error codes (i.e. the module loading faulire, symbol duplication or missing externals). The error manager is not deleted at Runtime object deletion. */ Runtime(); /** Destroy this Runtime. When a runtime is destroyed, modules linked by this runtime are de-referenced and eventually destroyed. To save modules after their last runtime released them, add an Module::incref() call soon after their creation. */ ~Runtime(); /** Tell wether this runtime tries to interpret the module load requests or just denies them. \return true if the runtime fulfills module requests with a provided ModuleLoader, false otherwise. */ bool willLoad() { return m_loader != 0 ; } /** Adds a module to the Runtime library. This function adds a module to the runtime library and then it links it in two steps. When a module is added to the runtime, its reference count is incremented, and a record containing its ID (relative position) is added. The ID will soft-link the module with its own global variable representation in the executing VM (i.e. Module N will have global variable vector number N, module K will refer to variables in the K vector and so on). \param mod The module to be added. \param bIsPrivate false (default) when linking requires exported symbols to be published globally in the VM. \throw Error on compilation/load error. */ void addModule( Module *mod, bool bIsPrivate = false ); /** Return the symbol tably containing the global symbols. As a symbol table is just a symbol map that self-deletes owned symbol, and as this map just holds a reference to some symbols inside the modules contained in the runtime, the map must not destroy the symbols at runtime destruction. Symbols are owned by symbols table, which are owned by their modules. However, as modules cannot be deleted while they are still owned by some Runtime, this references are guaranteed to stay valid at least untill this Runtime is destroyed. */ /** Return the modules linked in this runtime. */ const ModuleMap *moduleMap() const { return &m_modules; } /** Return the modules linked in this runtime. */ const ModuleVector *moduleVector() const { return &m_modvect; } /** Returns a module with the given name. Or zero if the module with the required name is not found. */ Module *findModule( const String &name ) { ModuleDep **modp = (ModuleDep **) m_modules.find( &name ); if ( modp != 0 ) return (*modp)->module(); return 0; } /** Return the nth module. */ Module *findModuleByID( uint32 id ) { return m_modvect.moduleAt( id ); } /** Return the amount of modules in this runtime. */ uint32 size() { return m_modvect.size(); } /** Loads the given module and adds it to the runtime. This is actually a just shortcut to load a module with a given logical name. The name is resolved by the module loader, and if load is succesfull, then the module is added to the runtime. \param name the logical name of the module to be loaded \param parent the logical name of the parent module, if any \param bIsPrivate false (default) to allow the module to export its symbols to the VM, true otherwise. \throw Error on compilation/load error. */ void loadName( const String &name, const String &parent = "", bool bIsPrivate=false ); /** Loads the given module and adds it to the runtime. This is actually a just shortcut to load a module from a given file. The name is resolved by the module loader, and if load is succesfull, then the module is added to the runtime. \param file the file name of the module to be loaded \param bIsPrivate false (default) to allow the module to export its symbols to the VM, true otherwise. \throw Error on compilation/load error. */ void loadFile( const String &file, bool bIsPrivate=false ); /** Returns true if there are still some pending modules. This means that the module list may not be completed in case the runtime is not using a loader to resolve dependencies. */ bool hasPendingModules() const { return ! m_modPending.empty(); } /** Returns true if this loader top module is the main module for a final application. By default, this is true unless changed with hasMainModule(bool). */ bool hasMainModule() const { return m_hasMainModule; } /** Changes the relevance of this runtime. If the runtime "has the main module", then the topmost module will be added as main when the runtime is linked to the target vm. This is the default behavior. As some runtime may be used to link sub-components which are not meant to build a complete application, this method is provided to inhibit this behavior. \param b true if this runtime will provide the main module to the VM at link time, false otherwise. */ void hasMainModule( bool b ) { m_hasMainModule = b; } }; } #endif /* end of flc_runtime.h */ include/falcon/sequence.h000066400000000000000000000263241176363201700157410ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: sequence.h Definition of abstract sequence class. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2007-12-01 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Definition of abstract sequence class. */ #ifndef flc_sequence_H #define flc_sequence_H #include #include namespace Falcon { class VMachine; class Iterator; class Item; class Garbageable; /** Abstract sequence class. A sequence is a special user data which is used as internal mean by sequence oriented falcon classes. It may be also used by extension code to create special lists or generator objects. The sequence must be able to create an Iterator object for itself. The Iterator will be used internally by the VM or eventually wrapped in a Falcon iterator object and then given to the script. */ class FALCON_DYN_CLASS Sequence: public FalconData { Garbageable* m_owner; protected: mutable Iterator* m_iterList; public: Sequence(): m_owner(0), m_iterList(0) {} virtual ~Sequence() { invalidateAllIters(); } /** Invalidate all the iterators. This disengage all the iterators from this sequence. Used when all the sequence becomes invalid. Iterators are not destroyed (they belong to their owners), but they become invalid and stop pointing to this sequence. */ void invalidateAllIters(); /** Invalidate all the iterators but one. This method invalidate all the iterators except the one provided. This is useful in sequence where destructive operations on one iterator (i.e. erase) cause all the other unsupported iterators to become invalid. */ void invalidateAnyOtherIter( Iterator* iter ); /** Disposes all the iterators matching a given criterion. Useful to dispose all the iterators in a sequence that match a certain criterion, as i.e. pointing to an element being deleted. The criterion is provided by subclasses via the onCriterion() callback. */ virtual void invalidateIteratorOnCriterion() const; /** Criterion called back by disposeIteratorOnCriterion(). Return true to remove this iterator, false to let it alive. Disposed iterators are removed from the iterator list of this sequence and invalidated. */ virtual bool onCriterion( Iterator* elem ) const {return false;} /** Mark this class as a sequence. */ virtual bool isSequence() const { return true; } /** Mark this class as a dictionary based sequence. */ virtual bool isDictionary() const { return false; } /** Returns the first element of the sequence. If the sequence has not an underlying storage, it may generate a temporary item, as the item is immediately copied into some destination by the caller. Guarantees are taken so that this method is never called when v_empty() returns false. \return a valid reference to the first item of the sequence */ virtual const Item &front() const = 0; /** Returns the first element of the sequence. If the sequence has not an underlying storage, it may generate a temporary item, as the item is immediately copied into some destination by the caller. This method is never used by the engine to modify the underlying item. Guarantees are taken so that this method is never called when v_empty() returns false. \return a valid reference to the first item of the sequence */ virtual const Item &back() const = 0; /** Removes all the items in the sequence. */ virtual void clear() = 0; /** Tells if the series is empty. \return false if there is at least one valid item in the series, false otherwise. */ virtual bool empty() const =0; /** Append an item at the end of the sequence. */ virtual void append( const Item &data ) = 0; /** Prepend an item at the beginning of the sequence. */ virtual void prepend( const Item &data ) = 0; /** Appends to a sequence comprehension. Just create the target sequence, and then use this function to Fulfill compounds. \note DON'T PASS directly parameter pointers, as the stack may be destroyed in the meanwhile. Instead, pass copies of the items. \param vm The virtual machine where to perform atomic calls. \param compounder a range, a sequence or a generator function providing a sequence of data. \param filter an optional filter function returning true to accept elemnts, false to discard them pass nil if none. \TODO: Remove this in the next major release. */ virtual void comprehension( VMachine* vm, const Item& compounder, const Item& filter ); /** Start a comprehension loop. Creates a stack frame that can be used to iterate ove a multiple comprehension. After the return, push this in the stack (as local variables): - (or nil) - - ... - And then immediately return. Local variables needed for accounting will be already pushed by the comprehension startup function, so the stack will probably be partially popualted before this method returns. The return frame function will take care to generate the comprehension. If more than one comprehension source is provided, then each element to be stored in this sequence will be a sequence of this kind, with their components taken one at a time from each source. For example: - Source 1: [a, b, c] - Source 2: List( e, f, g ) - This: Set() - Generated elements: Set(a, e), Set(b, f), Set(c, g) The comprehension terminates when the first source is empty. If a filter function is provided, then it's called after each element is created and before it is added to this sequence. \TODO: Make this virtual in the next major release. \return */ /* virtual */ bool comprehension_start( VMachine* vm, const Item& self, const Item& filter ); /** The sequence may be bound to an object. * If the sequence is bound with a falcon script level object, * when it receives a gcMark() request, for example, from an iterator * referencing it, the related garbageable must be flagged. * */ void owner( Garbageable* owner ) { m_owner = owner; } Garbageable* owner() { return m_owner; } virtual void gcMark( uint32 gen ); //============================================================== // Iterator management // protected: friend class Iterator; /** Gets an Iterator valid for this sequence. If you need an iterator as a pointer or in the target stack, use Iterator( Sequence*, bool) instead. The iterator constructor calls back this method to be configured. It is possible to call this method thereafter to reset the iterator, even if it's gone invalid. However, it is not legal to call this method with an iterator coming from another sequence; this will cause the program to throw a CodeError. \note The base version of this function just adds the iterator to the iterator list; it \b MUST be called by all the implementations. \param An Iterator to be set. \param tail if false, get an iterator to the first element, else get an iterator to the last element. */ virtual void getIterator( Iterator& tgt, bool tail = false ) const; /** Copy an iterator so that the two points to the same item. The source iterator may point to the past-end element, but must not be invalid. \note The base version of this function just adds the iterator to the iterator list; it \b MUST be called by all the implementations. */ virtual void copyIterator( Iterator& tgt, const Iterator& source ) const; /** Called back to destroy deep data that may be associated with an iterator. This method is called back at iterator destructor to clear deep data that the sequence may have stored in the iterator. After this call, the iterator is invalidated (if correctly found in the list). \note The base class version disengage the iterator from the iterator list and invalidates it. It normally shouldn't be overloaded by subclasses, as the final memory cleaning from a deep iterator, if needed, must be separately provided via the Iterator::deletor() interface. */ virtual void disposeIterator( Iterator& tgt ) const; /** Inserts an element in a position indicated by the iterator. The implementation must check that the iterator is a valid iterator created by this object and pointing to a valid position. Insertion happens at given position, shifting all the remaining elements forward; after a successful insert, the iterator must point to the newly inserted element, and the previously current element is found safely in the next() position of the iterator. Valid iterators (generated by this owner) pointing to invalid positions must be treated as pointing to last-past-one element; insertion causes append on tail, and at return they must be valid and point to the last valid element (the one just inserted). If the iterator cannot be used, for example because their owner is not this item, this method will raise a CodeError. \param iterator an iterator. \param data the item to be inserted \return true if the iterator was valid for this object. */ virtual void insert( Iterator &iter, const Item &data ) = 0; /** Deletes the element at position indicated by the iterator. The implementation must check that the iterator is a valid iterator created by this object and pointing to a valid position. Deletion happens at given position, shifting all the remaining elements backward; after a successful erase, the iterator must point to the element that was previously next in the series, or must be invalidated if the removed element was the last. If the sequence is empty or the iterator is invalid, an AccessError must be thrown. If the iterator is referencing another sequence, a CodeError must be thrown. \param iter an iterator (possibly invalid or not generated by this class). \return true if the iterator was valid for this object. */ virtual void erase( Iterator &iter ) = 0; virtual bool hasNext( const Iterator &iter ) const = 0; virtual bool hasPrev( const Iterator &iter ) const = 0; virtual bool hasCurrent( const Iterator &iter ) const = 0; virtual bool next( Iterator &iter ) const = 0; virtual bool prev( Iterator &iter ) const = 0; virtual Item& getCurrent( const Iterator &iter ) = 0; virtual Item& getCurrentKey( const Iterator &iter ) = 0; virtual bool equalIterator( const Iterator &first, const Iterator &second ) const = 0; }; } #endif /* end of sequence.h */ include/falcon/service.h000066400000000000000000000066201176363201700155660ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: service.h Service declaration ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun feb 13 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Service and service provider classes. */ #ifndef flc_service_H #define flc_service_H #include namespace Falcon { /** Service class. Falcon modules are meant to interact with OS, databases, web servers, and many other resources in behalf of Falcon scripts. However, some of the resources handled by Falcon binary modules may be extremely useful also for embedding application. Instead of re-linking or re-abstracting the required low level services, this interface allows the modules to publish a set of functionalities that can be used by both the Falcon scripts and the embedding application. This also grants a somehow more direct interface between applications and scripts, that can share objects representing low level resources as files, database connections, shared memory, internet sockets and so on. Service are required to a module by the Falcon::Module::getService(). a module will publish its services via Falcon::Module::publishService(). Applications willing to create many service instance in a row may find more efficient to use Falcon::Module::getServiceProvider(), which returns a factory for the given service. As Falcon modules are objects that should usually be loaded via DLL interface, a service will declare all of its methods as virtual (except for const inline methdos), so that access to methods by the user application won't require any linkage with the falcon modules. Of course, this causes a minor inefficience in function calls. The module where services are defined should declare \code #define FALCON_EXPORT_SERVICE \endcode before any Falcon file is inlcuded (specifically, before falcon/setup.h). This will tell MS-Windows compilers that the module is willing to offer the service to the users of the target DLL. Subclasses derived from services should maintain the FALCON_SERVICE signature to provide the same DLL export policy (necessary in MSVC C++ projects). \note Willing to spare every bit of CPU on module side for non-required overhead, the FALCON_FUNC that interacts with the script may call directly the needed function as C calls, passing the pointer to the service to them just as a data holder. The Service instance will hold the required data plus the set of pointers to the functions that are to be published. Methods may then be just inline calls to those function pointers; this will still require no linkage for modules into applications, and will have the same speed as virtual method calls, but you'll spare extra virtual calls inside the module. */ class FALCON_SERVICE Service { String m_name; public: /** Creates the service assigning it a certain name. The service requries a name by which it can be published by the module. */ Service( const String & name ); /** Destructor needs to be virtual. */ virtual ~Service(); const String &getServiceName() const { return m_name; } }; } #endif /* end of service.h */ include/falcon/setup.h000066400000000000000000000112121176363201700152570ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_setup.h Setup for compilation environment and OS specific includes ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom giu 6 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Setup for compilation environment and OS specific includes. This file is responsible for inclusion of all the system or compiler specific resources. It should be directly or indirectly included by all the falcon inclusion files. It should be included also (and mainly by) extension libraries, that will find here the macros needed to create dynamic link libraries under different systems. */ #ifndef FLC_SETUP_H #define FLC_SETUP_H #include //================================= // Windows specific defines // #ifdef FALCON_SYSTEM_WIN // Minimal specific. #if ! defined(_WIN32_WINNT) #define _WIN32_WINNT 0x0403 #endif //=============================== // Compiler specific defines // /* Specifigs for MSVC */ #ifdef _MSC_VER #undef CDECL #define CDECL __cdecl #define FALCON_FUNC \ void CDECL #ifndef FALCON_ENGINE_STATIC #if defined(FALCON_ENGINE_EXPORTS) #define FALCON_DYN_CLASS __declspec(dllexport) #define FALCON_DYN_SYM __declspec(dllexport) #define EXTERN_TEMPLATE // Falcon export service is optional, but mandatory with engine exports. #ifndef FALCON_EXPORT_SERVICE #define FALCON_EXPORT_SERVICE #endif #else #define FALCON_DYN_CLASS __declspec(dllimport) #define FALCON_DYN_SYM __declspec(dllimport) #define EXTERN_TEMPLATE export #endif #else #define FALCON_DYN_CLASS #define FALCON_DYN_SYM #endif #ifdef FALCON_EXPORT_SERVICE #define FALCON_SERVICE __declspec(dllexport) #else #define FALCON_SERVICE __declspec(dllimport) #endif #define FALCON_FUNC_DYN_SYM \ FALCON_DYN_SYM void CDECL #define FALCON_MODULE_TYPE \ extern "C" __declspec(dllexport) ::Falcon::Module * CDECL #pragma warning (disable: 4786 ) #pragma warning (disable: 4291 ) #pragma warning (disable: 579 ) #pragma warning (disable: 4290 ) #pragma warning (disable: 4231 ) #pragma warning (disable: 4355) #pragma warning (disable: 4996) #if _MSC_VER <= 1400 #ifndef _WCHAR_T_DEFINED typedef unsigned short wchar_t; #endif #endif #define atoll _atoi64 #define snprintf _snprintf #define LLFMT "I64" #define I64LIT(x) (x ## i64) #define UI64LIT(x) (x ## ui64) #endif /* Specifics for Gcc/Mingw */ #ifdef __GNUC__ #ifndef CDECL #define CDECL #endif #define FALCON_FUNC \ void #ifdef FALCON_ENGINE_EXPORTS #define FALCON_DYN_CLASS __declspec(dllexport) #define FALCON_DYN_SYM __declspec(dllexport) #define EXTERN_TEMPLATE #else #define FALCON_DYN_CLASS __declspec(dllimport) #define FALCON_DYN_SYM __declspec(dllimport) #define EXTERN_TEMPLATE export #endif #ifdef FALCON_EXPORT_SERVICE #define FALCON_SERVICE __declspec(dllexport) #else #define FALCON_SERVICE __declspec(dllimport) #endif #define FALCON_FUNC_DYN_SYM \ FALCON_DYN_SYM void CDECL #define FALCON_MODULE_TYPE \ extern "C" __declspec(dllexport) ::Falcon::Module * #define LLFMT "ll" #define I64LIT(x) (x ## LL) #define UI64LIT(x) (x ## ULL) #endif /* Other Windonws specific system defines */ #define DIR_SEP_STR "\\" #define DIR_SEP_CHR '\\' // paths are always indicated in falcon convention. #define DEFAULT_TEMP_DIR "C:/TEMP" #define FALCON_SYS_EOL "\r\n" //================================= // Unix specific defines // #else #define CDECL #define FALCON_FUNC \ void #define FALCON_DYN_CLASS #define FALCON_DYN_SYM #define EXTERN_TEMPLATE #define FALCON_SERVICE #define FALCON_MODULE_TYPE \ extern "C" ::Falcon::Module * #define FALCON_FUNC_DYN_SYM FALCON_FUNC #define DIR_SEP_STR "/" #define DIR_SEP_CHR '/' #define DEFAULT_TEMP_DIR "/tmp" #define FALCON_SYS_EOL "\n" #define LLFMT "ll" #define I64LIT(x) (x ## LL) #define UI64LIT(x) (x ## ULL) #endif //=================================== // Helper STR / _STR / #x converter // #ifndef _STR #define _STR(x) #x #endif #ifndef STR #define STR(x) _STR(x) #endif #endif /* end of setup.h */ include/falcon/signals.h000066400000000000000000000014121176363201700155600ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: signals.h OS signal handling ------------------------------------------------------------------- Author: Jan Dvorak Begin: 2010-02-19 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_SIGNALS_H #define FALCON_SIGNALS_H #include #ifndef FALCON_SYSTEM_WIN # include #endif namespace Falcon { /** Block OS signals in this thread. */ void FALCON_DYN_SYM BlockSignals(); /** Unblock OS signals in this thread. */ void FALCON_DYN_SYM UnblockSignals(); } #endif // vim: et ts=3 sw=3 : /* end of signals.h */ include/falcon/signals_posix.h000066400000000000000000000040711176363201700170060ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: signals_posix.h POSIX-specific signal handling ------------------------------------------------------------------- Author: Jan Dvorak Begin: Fri, 12 Feb 2010 11:23:13 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_SIGNALS_POSIX_H #define FALCON_SIGNALS_POSIX_H /* \file POSIX signals handling. */ #include #include #include #include namespace Falcon { /* * POSIX signal to VM Message translator. */ class FALCON_DYN_CLASS SignalReceiver: public Runnable, public BaseAlloc { protected: /* * VM to send messages based on signals to. */ VMachine *m_targetVM; /* * Thread waiting for signals to be delivered as long * as the shallRun is true. */ SysThread *m_thread; bool m_shallRun; /* * Wait for signals in ->sigset while ->shallRun is true. */ void *run(); /* * Function called from our signal handler to deliver * signal information to the helper thread. */ void deliver(int signum, siginfo_t *siginfo); /* * Wake up the helper thread to check ->shallRun. */ void wakeup(void); /* * For access to ->deliver(). */ friend void signal_handler(int signum, siginfo_t *siginfo, void *ctx); public: /* * Initializes signal receiver with a target VM. */ SignalReceiver(VMachine *targetVM); /* * Waits for the helper thread to stop. */ ~SignalReceiver(); /* * Starts the helper thread. */ void start(); /* * Asks thread to exit and waits for that. */ void stop(); /* * Trap given signal. */ bool trap(int signum); /* * Stop traping given signal. */ bool reset(int signum); }; /* * Global signal receiver instance. */ extern SignalReceiver *signalReceiver; } #endif // vim: et ts=3 sw=3 : /* end of signals_posix.h */ include/falcon/smba.h000066400000000000000000000046631176363201700150550ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: smba.h Small Memory Block Allocator. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 13 Dec 2008 14:42:24 +0100 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Small Memory Block Allocator. */ #ifndef FALCON_SMBA_H #define FALCON_SMBA_H #include namespace Falcon { class Mutex; /** Small Memory Block Allocator. This is an optimized allocator which works with fixed size small memory blocks: 8, 16, 32 and 64 bytes respectively, without memory overhead for accounting. The allocator manages directly heap pages and is able to determine the size of the allocated block by getting the address of the page from which it came from. This allocator is useful to account very small data as the trie memory blocks, but can be cool also to store small strings, item references and all those things for which you may want a lightning fast memory allocator and zero memory overhead. The engine provides a single SMBA for all the falcon. */ class SmallMemBlockAlloc { protected: typedef struct tag_Page { struct tag_Page *next; struct tag_Page *prev; void* firstFree; // for blank allocations short int allocated; short int pageArea; } PAGE_HEADER; enum{ page_list_size = 4 }; /** Pages, organized for block size */ PAGE_HEADER* page_lists[page_list_size]; /** List of free pointers, organized for block size */ void* page_free_lists[page_list_size]; // Using a pointer here because I don't want to include mt.h here. Mutex *m_mtx; PAGE_HEADER* newPage( int blockSize ); public: SmallMemBlockAlloc(); ~SmallMemBlockAlloc(); /** Allocates a small block wide at least bytes. If the requested memory size is greater than the maximum size managed by this memory manager, function returns 0. \param bytes Quantity of memory required. \return The allocated block. */ void* alloc( unsigned int bytes ); /** Frees the given memory block. \param The memory block to be freed. */ void free( void* bytes ); }; extern SmallMemBlockAlloc SMBA; } #endif /* end of smba.h */ include/falcon/spage.h000066400000000000000000000053051176363201700152240ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_itempage.h Definition of the page that holds items. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun ott 4 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Definition of a segregated allocation page. A SAP is a memory page where all the allocated objects has the same size (and possibly are of the same type). The SAP is a template class that is instantiated by providint the size of the items at compile time. */ #ifndef flc_flc_spage_H #define flc_flc_spage_H #include #include #include namespace Falcon { /** \note This is old code to be removed. */ class MemPage:public LinkableElement { protected: uint16 m_lastFree; public: MemPage( MemPage *prev=0, MemPage *next=0 ): LinkableElement( prev, next ) {} void *operator new( size_t ) throw() { return HeapMem::getPage(); } void *operator new( size_t, void *pos ) throw() { return pos; } void operator delete( void *page ) { HeapMem::freePage( page ); } }; class MemPageList: public LinkedList< MemPage > {}; /** Definition of a segregated allocation page. A SAP is a memory page where all the allocated objects has the same size (and possibly are of the same type). The SAP is a template class that is instantiated by providint the size of the items at compile time. \note This is old code to be removed. */ template class SegregatedPage: public MemPage { public: SegregatedPage( SegregatedPage<_T> *prev=0, SegregatedPage<_T> *next=0 ): MemPage( prev, next ) { m_lastFree = sizeof( SegregatedPage<_T> ); } void *nextItem() { if ( m_lastFree + sizeof(_T) > PAGE_SIZE ) return 0; void *data= (void *)( ((char *) this) + m_lastFree ); m_lastFree += sizeof(_T); return data; } void backItem() { if ( m_lastFree > sizeof(SegregatedPage<_T>) ) m_lastFree -= sizeof( _T ); } void resetPage() { m_lastFree = sizeof( SegregatedPage<_T> ); } _T *currentItem() { if ( m_lastFree == sizeof( SegregatedPage<_T> ) ) return 0; return ((_T *) (((char *) this)+m_lastFree)) -1; } _T *firstItem() { return (_T *) ((char*)this + sizeof( SegregatedPage<_T> ) ); } _T *lastItem() { return ((_T *) ((char*)this + sizeof( SegregatedPage<_T> ))) + ( (PAGE_SIZE - sizeof( SegregatedPage<_T> )) / sizeof( _T) ); } }; } #endif /* end of flc_spage.h */ include/falcon/src_lexer.h000066400000000000000000000140651176363201700161160ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falcon_lexer.h Lexer. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom giu 6 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_LEXER_H #define FALCON_LEXER_H #include #include #include #include #include namespace Falcon { class SyntreeElement; class Compiler; class Stream; class FALCON_DYN_CLASS SrcLexer: public BaseAlloc { public: /** Type of context */ typedef enum { /** Topmost context */ ct_top, /** Inside round parenthesis */ ct_round, /** Inside square parenthesis */ ct_square, /** Inside a string (of any kind; the kind is determined by the status) */ ct_string, /** Inside function declaration */ ct_graph, /** Inside an inner function */ ct_inner } t_contextType; private: class Context: public BaseAlloc { public: t_contextType m_ct; int m_oline; Context* m_prev; Context( t_contextType ct, int openLine, Context* prev=0): m_ct( ct ), m_oline( openLine ), m_prev( prev ) { } }; void *m_value; int m_line; int m_previousLine; int m_character; int m_prevStat; bool m_firstEq; bool m_done; bool m_addEol; bool m_lineFilled; bool m_bIsDirectiveLine; bool m_incremental; bool m_lineContContext; bool m_graphAgain; uint32 m_chrEndString; bool m_mlString; Stream *m_in; List m_streams; List m_streamLines; Compiler *m_compiler; String m_string; typedef enum { e_line, e_string, e_stringOctal, e_stringBin, e_stringHex, e_stringRunning, e_loadDirective, e_eolComment, e_blockComment, e_zeroNumber, e_intNumber, e_binNumber, e_octNumber, e_hexNumber, e_floatNumber, e_floatNumber_e, e_floatNumber_e1, e_operator, e_symbol, e_litString } t_state; t_state m_state; typedef enum { t_mNormal, t_mOutscape, t_mEval } t_mode; t_mode m_mode; bool m_bParsingFtd; bool m_bWasntEmpty; String m_whiteLead; Context* m_topCtx; //================== /** Scans m_string for recognized tokens, and eventually returns them. */ int checkUnlimitedTokens( uint32 nextChar ); int checkLimitedTokens(); void checkContexts(); bool isWhiteOrEOL( uint32 chr ) { return chr == ' ' || chr == '\t' || chr == '\r' || chr == '\n' || chr == '\b' || chr == 0x12 || chr == 0x3000 || chr == 0x00A0; // unicode ideographic & nbs } bool isWhiteSpace( uint32 chr ) { return chr == ' ' || chr == '\t' || chr == '\r' || chr == '\b' || chr == 0x12 || chr == 0x3000 || chr == 0x00A0; // unicode ideographic & nbs } /** Special (non symbolic) high characters. */ bool isSpecialChar( uint32 chr ) { // special spaces... return chr == 0x3000 || chr == 0x00A0 || // Special quotes chr == 0x201C || chr == 0x201D || chr == 0x300C || chr == 0x300D || chr == 0xFF09 || chr == 0xFF08; } bool isSymbolChar( uint32 chr ) { return (chr >= '0' && chr <= '9') || (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || chr == '_' || ( chr > 0x80 && ! isSpecialChar(chr) ); } bool isTokenLimit( uint32 chr ) { return ! isSymbolChar( chr ); } // Process the state line int state_line( uint32 chr ); int lex_normal(); int lex_outscape(); int lex_eval(); public: SrcLexer( Compiler *comp ); ~SrcLexer(); /** Return current line in file (starting from 1). */ int line() const { return m_line; } /** Return current character position in current line (starting from 1). */ int character() const { return m_character; } int previousLine() const { return m_previousLine; } void resetContexts(); void line( int val ) { m_line = val; } /** Hook for bison. Bison will call this with his "parameter", which is the value of the various expression. The type for lexed values is the lex_value_t union, declared in the bison files. */ int doLex( void *param ) { m_value = param; return lex(); } void *value() const { return m_value; } int lex(); void input( Stream *i ); Stream *input() const { return m_in; } /** Resets the lexer, preparing it for another compilation. */ void reset(); bool parsingFtd() const { return m_bParsingFtd; } void parsingFtd( bool b ); bool hasOpenContexts() { return m_topCtx != 0 || m_lineContContext; } bool incremental() const { return m_incremental; } void incremental( bool b ) { m_incremental = b; } void appendStream( Stream *s ); void parseMacro(); void parseMacroCall(); /** Add a compilation lexer context. */ void pushContext( t_contextType ct, int startLine ); /** Pops a compilation lexer context. \return true if the contest was popped, false if we had no context. */ bool popContext(); /** Reads the current context type. \return ct_top if outside any context. */ t_contextType currentContext(); /** Gets the line at which the current context started. \return 0 if no context is open, a valid line otherwise. */ int contextStart(); /** Determines if the current context is a parentetic context. \return true if in parenthesis. */ bool inParCtx(); /** Specialized version of ReadAhead, killing unneeded \\r \return true if the character could be read ahead \param chr Where to place the read character */ bool readAhead( uint32 &chr ); }; } #endif /* end of falcon_lexer.h */ include/falcon/stackframe.h000066400000000000000000000055251176363201700162510ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: stackframe.h Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab mar 18 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Short description */ #ifndef flc_stackframe_H #define flc_stackframe_H #include #include #include #include #include namespace Falcon { class Module; class StackFrame: public BaseAlloc { public: enum { pa_default = 8 } constants; bool m_break; uint32 m_ret_pc; uint32 m_call_pc; uint32 m_param_count; uint32 m_try_base; const Symbol *m_symbol; LiveModule *m_module; ext_func_frame_t m_endFrameFunc; StackFrame* m_prevTryFrame; Item m_self; Item m_fself; Item m_binding; // points to the parameter part in the previous area. Item* m_params; StackFrame( int preAlloc = pa_default ): m_symbol(0), m_module(0), m_prevTryFrame(0), m_prev(0), m_stack( preAlloc ) {} StackFrame( const StackFrame& other ); /** Retrieves the previous frame in the call stack. */ StackFrame* prev() const { return m_prev; } void prev( StackFrame* p ) { m_prev = p; } /** Returns the items in the stack. */ const ItemArray& stack() const { return m_stack; } ItemArray& stack() { return m_stack; } /** Remvoves N elements from thes stack */ void pop( uint32 size ) { m_stack.resize( m_stack.length() - size ); } /** Allocates new space in the stack. */ void push( uint32 size ) { m_stack.resize( m_stack.length() + size ); } Item* stackItems() const { return m_stack.elements(); } uint32 stackSize() const { return m_stack.length(); } void pushItem( const Item& v ) { m_stack.append(v); } const Item& topItem() const { return m_stack.back(); } Item& topItem() { return m_stack[m_stack.length()-1]; } void popItem( Item &tgt ) { tgt = m_stack.back(); m_stack.resize( m_stack.length() - 1 ); } void resizeStack( uint32 size ) { m_stack.resize( size ); } void prepareParams( StackFrame* previous, uint32 paramCount ) { if( paramCount != 0 ) { m_params = previous->m_stack.elements() + previous->m_stack.length() - paramCount; } else m_params = 0; } const Item& localItem( uint32 id ) const { return m_stack[id]; } Item& localItem( uint32 id ) { return m_stack[id]; } /** Copy a whole hierarcy of frames. */ StackFrame* copyDeep( StackFrame** bottom ); void gcMark( uint32 mark ); private: StackFrame* m_prev; ItemArray m_stack; } ; } #endif /* end of stackframe.h */ include/falcon/stdstreams.h000066400000000000000000000027751176363201700163260ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vm_stdstreams.h System dependant default I/O streams. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven ago 25 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file System dependant default I/O streams. This file contains the declaration of the standard streams that will fill VM and other systems basic I/O systems and that may be system dependant. */ #ifndef flc_stdstreams_H #define flc_stdstreams_H namespace Falcon { class Stream; /** System specific input stream factory function. This function will return a text oriented input stream. */ FALCON_DYN_SYM Stream *stdInputStream(); /** System specific output stream factory function. */ FALCON_DYN_SYM Stream *stdOutputStream(); /** System specific error stream factory function. */ FALCON_DYN_SYM Stream *stdErrorStream(); /** Default text converter. Depending on the system, this method will wrap the underlying stream in a EOL transcoder or it will just return it. */ FALCON_DYN_SYM Stream *DefaultTextTranscoder( Stream *underlying, bool own = true ); /** Adds just the sytem default EOL transcoding policy. */ FALCON_DYN_SYM Stream *AddSystemEOL( Stream *underlying, bool own = true ); } #endif /* end of stdstreams.h */ include/falcon/stream.h000066400000000000000000000275601176363201700154270ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: file_base.h Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar nov 2 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file The Falcon Stream API. Falcon Streams are the basic I/O of every Falcon subsystem, including VM, compiler, assembler, generators and modules. */ #ifndef flc_stream_H #define flc_stream_H #include #include #include #include #include #define FALCON_READAHEAD_BUFFER_BLOCK 32 namespace Falcon { /** Base class for file and filelike services. This class is used by all the I/O in Falcon libraries and modules. Subclassess to store or read data from standard streams, files and memory buffers are already provided; the implementors may extend this class or the derived classes to support more systems and/or special I/O devices. This is a purely abstract class that serves as a base class for system-specific or system independent implementations. */ class FALCON_DYN_CLASS Stream: public FalconData { protected: uint32 *m_rhBuffer; uint32 m_rhBufferSize; uint32 m_rhBufferPos; /** Push a character in the read ahead buffer. If the buffer is not still constructed, it is constructed here. \param chr the char to be pushed. */ void pushBuffer( uint32 chr ); /** Pops next character from the buffer. If the buffer is not still constructed, or if its empty, the functionr returns false. \param chr the character that will be retreived. \return false if buffer empty. */ bool popBuffer( uint32 &chr ); /** Returns true if the buffer is empty. */ bool bufferEmpty() const { return m_rhBufferPos == 0; } /** Protected constructor. Transcoder constructor of this class and of all subclassess is guaranteed not to use the stream in any way but to store it in a protected member; so it is actually possible to switch streams after creation, or to pass 0 as stream initially. Just, be sure YOU KNOW WHAT YOU ARE DOING, as switching streams on stateful encoders may be a bad idea, as it may be a bad idea to create a transcoder passing a 0 stream to it. Also, there's nowhere any control about the stream not being null before calling parsing functions, so be carefull. */ public: typedef enum { t_undefined = 0, t_file = 1, t_stream = 2, t_membuf = 3, t_network = 4, t_proxy = 5 } t_streamType; typedef enum { t_none = 0, t_open = 0x1, t_eof = 0x2, t_error = 0x4, t_unsupported = 0x8, t_invalid = 0x10, t_interrupted = 0x20 } t_status ; protected: t_streamType m_streamType; t_status m_status; int32 m_lastMoved; /** Initializes the base file class. */ Stream( t_streamType streamType ): m_rhBuffer( 0 ), m_rhBufferSize( 0 ), m_rhBufferPos( 0 ), m_streamType( streamType ), m_status( t_none ), m_lastMoved( 0 ) {} typedef enum { ew_begin, ew_cur, ew_end } e_whence; friend class Transcoder; public: Stream( const Stream &other ); t_streamType type() const { return m_streamType; } virtual t_status status() const { return m_status; } virtual void status( t_status s ) { m_status = s; } uint32 lastMoved() const { return m_lastMoved; } void reset(); bool good() const; bool bad() const; bool open() const; bool eof() const; bool unsupported() const; bool invalid() const; bool error() const; bool interrupted() const; virtual ~Stream(); virtual void gcMark( uint32 mark ) {} virtual bool isStreamBuffer() const { return false; } virtual bool isTranscoder() const { return false; } /** Reads from target stream. \param buffer the buffer where read data will be stored. \param size the amount of bytes to read */ virtual int32 read( void *buffer, int32 size ); /** Write to the target stream. */ virtual int32 write( const void *buffer, int32 size ); /** Close target stream. */ virtual bool close(); virtual int64 tell(); virtual bool truncate( int64 pos=-1 ); virtual bool errorDescription( ::Falcon::String &description ) const; /** Determines if the stream can be read, possibly with a given timeout. If sysData is not zero, it will be used to honor concurrent interrupt requests. */ virtual int32 readAvailable( int32 msecs_timeout, const Sys::SystemData *sysData = 0 ); /** Determines if the stream can be written, possibly with a given timeout. If sysData is not zero, it will be used to honor concurrent interrupt requests. */ virtual int32 writeAvailable( int32 msecs_timeout, const Sys::SystemData *sysData = 0 ); int64 seekBegin( int64 pos ) { return seek( pos, ew_begin ); } int64 seekCurrent( int64 pos ) { return seek( pos, ew_cur ); } int64 seekEnd( int64 pos ) { return seek( pos, ew_end ); } virtual int64 seek( int64 pos, e_whence w ); virtual int64 lastError() const; /** Gets next character from the stream. Subclasses must manage both stateful transcoding and properly popping readahead characters from the buffer. \return true if the character is available, false on stream end or error. */ virtual bool get( uint32 &chr ) = 0; /** Gets a whole string from the stream. This is implemented by iteratively calling get( uint32 ). The caller should provide a string with enough space already reserved, if possible, to make operations more efficient. The target string may be shorter than required if the stream ends before all the characters are read, or in case of error. \return true if some characters are available, false on stream end or error. */ virtual bool readString( String &target, uint32 size ); /** Writes a character on the stream. \param chr the character to write. \return true success, false on stream error. */ virtual bool put( uint32 chr ); /** Writes a string on the stream. Encoding range is in [begin, end), that is, the last character encoded is end - 1. \param source the string that must be encoded \param begin first character from which to encode \param end one past last character to encode (can be safely greater than string lenght() ) \return true success, false on stream error. */ virtual bool writeString( const String &source, uint32 begin=0, uint32 end = csh::npos ); /** Write a character in the readahead buffer. Next get() operation will return characters pushed in the buffer in reverse order. So, if the next character on the stream is 100 and the caller class unget(1) and unget(2), three consecutive get will return in turn 2, 1 and 100. Unget is interleaved with readAhead(), so that the sequence \code Stream *s = ... Transcoder x( s ); x.unget( 10 ); x.readAhead( chr ); // chr <- 20 x.unget( 30 ); String res; x.get( res, 3 ); \endcode Will fill res with 30, 20 and 10. \see readAhead \param chr the character to be pushed. */ void unget( uint32 chr ) { pushBuffer( chr ); } /** Ungets a whole string. The string is pushed on the read back buffer so that the next target.length() get() operations return the content of the string. \note use wisely. */ void unget( const String &target ); /** Read a character but don't remove from get(). This function is equivalent to: \code Stream *s = ... Transcoder xss ); uint32 chr; x.get( chr ); x.unget( chr ); \endcode \param chr the read character \return false on stream end or error. */ bool readAhead( uint32 &chr ); /** Read a string but don't remove from get(). Every character in the returned string will still be read by other get() operations; this allows to "peek" forward a bit in the target stream to i.e. take lexer decisions that won 't affect a parser. The target string may be shorter than required if the stream ends before all the characters are read, or in case of error. \param target the read string \param size the amount of character to be read. \return false on stream end or error. */ bool readAhead( String &target, uint32 size ); /** Discards ungetted and read ahead characters. If the lexer finds that it would be useless to retreive again the read ahead characters, it can use this function to discard the content of the buffer instead of re-reading and ignoring them. However, this can be done only if the final application has a state memory of what is happening, as there may be some ungetted or read ahaead strings that the code portion calling this function may not be aware of. In that case, the caller should know the amount of character it has read ahead and pass as parameter for this function. \param count number of character to discard from read ahead buffer (0 for all). */ void discardReadAhead( uint32 count = 0 ); /** Flushes stream buffers. Hook for buffered streams. */ virtual bool flush(); /** Clones the stream. This version returns 0 and sets error to unsupported; subclasses must properly clone the stream. */ virtual Stream *clone() const; }; /** Or operator on status bitfiled. This is to allow integer oinline processing on enum fields in Stream class. */ inline Stream::t_status operator|( const Stream::t_status &elem1, const Stream::t_status &elem2) { return static_cast( static_cast(elem1) | static_cast(elem2) ); } /** And operator on status bitfiled. This is to allow integer oinline processing on enum fields in Stream class. */ inline Stream::t_status operator&( const Stream::t_status &elem1, const Stream::t_status &elem2) { return static_cast( static_cast(elem1) & static_cast(elem2) ); } /** Xor operator on status bitfiled. This is to allow integer oinline processing on enum fields in Stream class. */ inline Stream::t_status operator^( const Stream::t_status &elem1, const Stream::t_status &elem2) { return static_cast( static_cast(elem1) ^ static_cast(elem2) ); } /** Not operator on status bitfiled. This is to allow integer oinline processing on enum fields in Stream class. */ inline Stream::t_status operator~( const Stream::t_status &elem1 ) { return static_cast( ~ static_cast(elem1) ); } inline void Stream::reset() { status( status() & static_cast(~static_cast(t_error|t_unsupported|t_invalid)) ); m_lastMoved = 0; } inline bool Stream::good() const { return (status() &( t_error | t_unsupported | t_invalid )) == 0; } inline bool Stream::bad() const { return (status() &( t_error | t_unsupported | t_invalid )) != 0; } inline bool Stream::open() const { return (status() & t_open ) != 0; } inline bool Stream::eof() const { return (status() & t_eof ) != 0; } inline bool Stream::unsupported() const { return (status() & t_unsupported ) != 0; } inline bool Stream::invalid() const { return (status() & t_invalid ) != 0; } inline bool Stream::error() const { return ( status() & t_error ) != 0; } inline bool Stream::interrupted() const { return ( status() & t_interrupted ) != 0; } } //end of Falcon namespace #endif /* end of file_base.h */ include/falcon/streambuffer.h000066400000000000000000000050701176363201700166110ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: streambuffer.h Buffer for stream operations. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 02 Feb 2009 16:26:17 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Buffered stream. */ #ifndef FLC_STREAM_BUFFER_H #define FLC_STREAM_BUFFER_H #include namespace Falcon { /** Buffered version of basic stream. \TODO Everything except reading straight is to be tested. */ class FALCON_DYN_CLASS StreamBuffer: public Stream { public: enum { default_buffer_size = 4096 } enum_default_buffer_size; private: int32 m_bufSize; bool m_changed; byte *m_buffer; int32 m_bufPos; int32 m_bufLen; uint64 m_filePos; bool m_bReseek; Stream *m_stream; bool m_streamOwner; bool refill(); protected: virtual int64 seek( int64 pos, e_whence whence ); public: StreamBuffer( Stream *underlying, bool bOwn = true, uint32 bufSize = default_buffer_size ); StreamBuffer( const StreamBuffer &other ); virtual ~StreamBuffer(); virtual StreamBuffer *clone() const; virtual bool isStreamBuffer() const { return true; } /** Returns the underlying stream used by this transcoder. \return the underlying stream. */ Stream *underlying() const { return m_stream; } virtual bool close(); virtual int64 tell(); virtual bool truncate( int64 pos=-1 ); virtual int32 readAvailable( int32 msecs_timeout, const Sys::SystemData *sysData = 0 ); virtual int32 writeAvailable( int32 msecs_timeout, const Sys::SystemData *sysData ); virtual bool flush(); virtual bool get( uint32 &chr ); virtual bool put( uint32 chr ); virtual int32 read( void *buffer, int32 size ); virtual int32 write( const void *buffer, int32 size ); virtual bool errorDescription( ::Falcon::String &description ) const { return m_stream->errorDescription( description ); } virtual int64 lastError() const { return m_stream->lastError(); } virtual t_status status() const { return m_stream->status(); } virtual void status(t_status s) { return m_stream->status(s); } /** Disengages this transcoder from the underlying stream. */ void detach() { m_stream = 0; m_streamOwner = false; } bool resizeBuffer( uint32 size ); uint32 bufferSize() const { return m_bufSize; } }; } #endif /* end of streambuffer.h */ include/falcon/string.h000066400000000000000000001552751176363201700154470ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: string.h Core falcon string representation. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven nov 19 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Core falcon string representation */ #ifndef flc_string_H #define flc_string_H #include #include #include #include #include #define FALCON_STRING_ALLOCATION_BLOCK 32 namespace Falcon { class Stream; class VMachine; class LiveModule; /** Core falcon string representation. This is a string as seen by the VM and its fellows (module loader, module and so on). As times goes by, it will substitute ever c_string and Hstring ( stl++ string + falcon allocator), so it must be small and efficient, and support different allocation schemes seamlessly. Strings are polymorphic; that is, they always occupy the same amount of space, independently of the kind (being the string deep data pointed by a pointer in the class), so that the type of a string may be changed without the need to reallocate it. So, if/when more than one item are pointing to the core string object, and the core string changes, the new status is immediately reflected to all the pointing items. This wastes a little bit of allocation space on every string, but speeds up the engine notably. */ class String; /** Core string utility namespace. Core strings cannot be virtual classes. This is because core strings may be polimorphic; some operations may change the very nature of core strings. In example, read-only static strings will be turned to memory buffers in case of write operations. Chunked strings may be turned into sequential buffers, and the other way around. Re-creating them with a new() operator would not provide a viable solution as all the other data must stay the same. For this reason, a manager class hyerarcy is provided. Every core string has a manager, (which is actually a pointer to a pure-virtual class with no data), and every operation on a string is an inline call to the manager class. So, the string virtuality is externalized in the manager. With a "standard" compiler optimization this is 100% equivalent to have a pointer to a table of function, but actually faster as the offset of the virtual table is calculated at compile time in every situation. */ namespace csh { /** Type of core string. As the type of the core string is extracted by the manager, this is actually stored in the manager. */ typedef enum { cs_static, cs_buffer, cs_static16, cs_buffer16, cs_static32, cs_buffer32 } t_type; /** Invalid position for core strings. */ const uint32 npos = 0xFFFFFFFF; /** Base corestring manager class. This is actually an interface that must be implemented by all the core string managers. */ class FALCON_DYN_CLASS Base { public: virtual ~Base() {} virtual t_type type() const =0; virtual uint32 charSize() const = 0; virtual uint32 length( const String *str ) const =0; virtual uint32 getCharAt( const String *str, uint32 pos ) const =0; virtual void setCharAt( String *str, uint32 pos, uint32 chr ) const =0; virtual void subString( const String *str, int32 start, int32 end, String *target ) const =0; /** Finds a substring in a string, and eventually returns npos if not found. */ virtual uint32 find( const String *str, const String *element, uint32 start =0, uint32 end = npos) const = 0; virtual uint32 rfind( const String *str, const String *element, uint32 start =0, uint32 end = npos) const = 0; virtual void insert( String *str, uint32 pos, uint32 len, const String *source ) const =0; virtual bool change( String *str, uint32 start, uint32 end, const String *source ) const =0; virtual void remove( String *str, uint32 pos, uint32 len ) const =0; virtual String *clone( const String *str ) const =0; virtual void destroy( String *str ) const =0; virtual void bufferize( String *str ) const =0; virtual void bufferize( String *str, const String *strOrig ) const =0; virtual void reserve( String *str, uint32 size, bool relative = false, bool block = false ) const = 0; virtual void shrink( String *str ) const = 0; virtual const Base *bufferedManipulator() const =0; }; /** Byte orientet base class. This is still an abstract class, but it provides minimal behavior for byte oriented strings (ascii or system specific). */ class FALCON_DYN_CLASS Byte: public Base { public: virtual ~ Byte() {} virtual uint32 length( const String *str ) const; virtual uint32 getCharAt( const String *str, uint32 pos ) const; virtual void subString( const String *str, int32 start, int32 end, String *target ) const; virtual bool change( String *str, uint32 pos, uint32 end, const String *source ) const; virtual String *clone( const String *str ) const; virtual uint32 find( const String *str, const String *element, uint32 start =0, uint32 end = 0) const; virtual uint32 rfind( const String *str, const String *element, uint32 start =0, uint32 end = 0) const; virtual void remove( String *str, uint32 pos, uint32 len ) const; virtual void bufferize( String *str ) const; virtual void bufferize( String *str, const String *strOrig ) const; virtual void reserve( String *str, uint32 size, bool relative = false, bool block = false ) const; virtual const Base *bufferedManipulator() const { return this; } }; /** Static byte oriented string manager. Useful to instantiante and manage strings whose content is byte oriented and whose size is known in advance; for example, symbol names in the Falcon module are easily managed with this class. Every write operation on strings managed by this class will cause its manager to be changed into the Buffer class. */ class FALCON_DYN_CLASS Static: public Byte { public: virtual ~Static() {} virtual t_type type() const { return cs_static; } virtual uint32 charSize() const { return 1; } virtual void setCharAt( String *str, uint32 pos, uint32 chr ) const; virtual void insert( String *str, uint32 pos, uint32 len, const String *source ) const; virtual void remove( String *str, uint32 pos, uint32 len ) const; virtual void destroy( String *str ) const; virtual void reserve( String *str, uint32 size, bool relative = false, bool block = false ) const; virtual void shrink( String *str ) const; virtual const Base *bufferedManipulator() const; }; /** Variable size byte oriented string. This class manages a variable size strings that are stored in one region of memory. Strings may or may not be zero terminated (in case of need, the zero after the length of the string is checked, and if not present, added). This is actually a useful class to store C strings created on the fly from memory buffer; the requirement is that the memory stored in the managed class is created with the Falcon::memAlloc() function (as it will be freed with Falcon::memFree() and reallocated with Falcon::memRealloc() ). */ class FALCON_DYN_CLASS Buffer: public Byte { public: virtual ~Buffer() {} virtual t_type type() const { return cs_buffer; } virtual uint32 charSize() const { return 1; } virtual void setCharAt( String *str, uint32 pos, uint32 chr ) const; virtual void insert( String *str, uint32 pos, uint32 len, const String *source ) const; virtual void destroy( String *str ) const; virtual void reserve( String *str, uint32 size, bool relative = false, bool block = false ) const; virtual void shrink( String *str ) const; }; class FALCON_DYN_CLASS Static16: public Static { public: virtual ~Static16() {} virtual uint32 charSize() const { return 2; } virtual uint32 length( const String *str ) const; virtual uint32 getCharAt( const String *str, uint32 pos ) const; virtual void setCharAt( String *str, uint32 pos, uint32 chr ) const; virtual void remove( String *str, uint32 pos, uint32 len ) const; virtual void reserve( String *str, uint32 size, bool relative = false, bool block = false ) const; virtual const Base *bufferedManipulator() const; }; class FALCON_DYN_CLASS Static32: public Static16 { public: virtual ~Static32() {} virtual uint32 charSize() const { return 4; } virtual uint32 length( const String *str ) const; virtual uint32 getCharAt( const String *str, uint32 pos ) const; virtual void setCharAt( String *str, uint32 pos, uint32 chr ) const; virtual void remove( String *str, uint32 pos, uint32 len ) const; virtual void reserve( String *str, uint32 size, bool relative = false, bool block = false ) const; virtual const Base *bufferedManipulator() const; }; class FALCON_DYN_CLASS Buffer16: public Buffer { public: virtual uint32 charSize() const { return 2; } virtual uint32 length( const String *str ) const; virtual uint32 getCharAt( const String *str, uint32 pos ) const; virtual void setCharAt( String *str, uint32 pos, uint32 chr ) const; }; class FALCON_DYN_CLASS Buffer32: public Buffer16 { public: virtual ~Buffer32() {} virtual uint32 charSize() const { return 4; } virtual uint32 length( const String *str ) const; virtual uint32 getCharAt( const String *str, uint32 pos ) const; virtual void setCharAt( String *str, uint32 pos, uint32 chr ) const; }; extern FALCON_DYN_SYM Static handler_static; extern FALCON_DYN_SYM Buffer handler_buffer; extern FALCON_DYN_SYM Static16 handler_static16; extern FALCON_DYN_SYM Buffer16 handler_buffer16; extern FALCON_DYN_SYM Static32 handler_static32; extern FALCON_DYN_SYM Buffer32 handler_buffer32; } // namespace csh /** Core string This class is called "Core String" because it represents the strings as the internal VM and engine sees them. This class is highly configurable and may manage any string that Falcon will ever need to mangle with. A set of fields is used to store the informations about the memory buffer where the string is actually held. The "kind" of string is determined by its manager. The manager is a special friend class that is in charge to effect all the needed operations on a particular kind of string. In example, there's a manager for static C strings, one for memAlloc() allocated strings, and in future also for chunked (multi buffer) stings and a parallel set of managers for international strings. The kind of the string can be changed by just changing its manager; this is often done automatically by an appropriate constructor or when some operation occour (i.e. a static string may be turned into a chunked one at write operations, and a chunked may get transformed into a buffered one if a linear access on the whole string is needed. String have a set of specialized subclasses which actually does nothing if not construct the base String with the appropriate string manager. Every corestring class is BOUND having not any private data member, because the derived String may be turned in something else at every moment without changing its memory position or layout. There's no RTTI information about this changes; all the polimorphism needed is applied by changing the string manager. However, String sublcass may define some new function members to handle initialization steps before "unmasking" the String structure and handle it back to the rest of the system. Also, as the String subclass may be determined by looking at the manager, a subclass with special operations (new member function) may be casted later on safely. The only requisite is that there's a 1:1 mapping between corestring subclasses and the manager they use. */ class FALCON_DYN_CLASS String: public GCAlloc { friend class csh::Base; friend class csh::Byte; friend class csh::Static; friend class csh::Buffer; friend class csh::Static16; friend class csh::Buffer16; friend class csh::Static32; friend class csh::Buffer32; protected: const csh::Base *m_class; uint32 m_allocated; uint32 m_size; byte *m_storage; /** * True if this string is exportable - importable in GC context. */ bool m_bExported; // Reserved for future usage. byte m_bFlags; bool m_bCore; /**sym * Creates the core string. * * This method is protected. It can be accessed only by subclasses. */ explicit String( csh::Base *cl ) : m_class( cl ) {} void internal_escape( String &strout, bool full ) const; public: enum constants { npos = csh::npos }; /** * Creates an empty string. * * The string is created non-zero terminated with length 0. It has also * no valid internal storage at creation time. */ String(): m_class( &csh::handler_static ), m_allocated( 0 ), m_size( 0 ), m_storage( 0 ), m_bExported( false ), m_bCore( false ) { } /** Adopt a static buffer as the internal buffer. This version of the string adopts the given buffer and becomes a "static string". A static string is just meant to carry around a pre-existing unchangeable (read-only) static buffer. The passed buffer must stay vaild for the whole duration of this string (i.e. it may be allocated as static string in some module). The string is automatically "bufferized" when some write operations are performed, so the original static data stays untouched even if this string is modified. This constructor allows for automatic fast char-to-string conversion in temporary operations. \note No assumption is made of the encoding of the source string. The data is just accepted as a mere sequence of bytes. \note The method bufferize() may be used later to force copy of the contents of this string. In that case, the underlying data must just stay valid until bufferize() is called. \see adopt \param data the source data to be copied */ String( const char *data ); /** Adopt a static buffer as the internal buffer. This is the wide char version. This version of the string adopts the given buffer and becomes a "static string". A static string is just meant to carry around a pre-existing unchangeable (read-only) static buffer. The passed buffer must stay vaild for the whole duration of this string (i.e. it may be allocated as static string in some module). The string is automatically "bufferized" when some write operations are performed, so the original static data stays untouched even if this string is modified. This constructor allows for automatic fast char-to-string conversion in temporary operations. \note No assumption is made of the encoding of the source string. The data is just accepted as a mere sequence of wide characters. \note The method bufferize() may be used later to force copy of the contents of this string. In that case, the underlying data must just stay valid until bufferize() is called. \see adopt \param data the source data to be copied */ String( const wchar_t *data ); /** Allows on-the-fly core string creation from static data. The resulting string is a bufferized copy of the static data; the source may be destroyed or become invalid, while this string will be still useable. \note To adopt an undestroyable buffer, use String( const char* ) version. \note No assumption is made of the encoding of the source string. The data is just accepted as a mere sequence of bytes. \param data the source data to be copied \param len the length of the string in buffer (in bytes). Pass -1 to make the constructor to determine the buffer length by scanning it in search for '\\0' */ String( const char *data, int32 len ); /** Allows on-the-fly core string creation from static data. This is the wide string version. The resulting string is a bufferized copy of the static data; the source may be destroyed or become invalid, while this string will be still useable. \note To adopt an undestroyable buffer, use String( const wchar_t* ) version. \note No assumption is made of the encoding of the source string. The data is just accepted as a mere sequence of wide characters. \param data the source data to be copied \param len the length of the buffer (in wide characters). Pass -1 to make the constructor to determine the buffer length by scanning it in search for '\\0' */ String( const wchar_t *data, int32 len ); /** Creates a bufferized string with preallocated space. */ explicit String( uint32 prealloc ); /** Copies a string. If the copied string is a bufferized string, a new bufferzied string is created, else a static string pointing to the same location of the original one is created. \note Static strings are constructed by simpling pointing the other string start position. Remember: static strings are meant to "carry" underlying memory and interpret it as a string, so the underlying memory must stay valid. Use bufferize() on this string to ensure that it is deep-copied. */ String( const String &other ): m_allocated( 0 ), m_bExported( false ), m_bCore( false ) { copy( other ); } /** Substring constructor. This constructor is used to extract a substring from the original one, and is used in the subString() metod to return a string & as an inline call. Being an inline call, the optimized version optimizes the involved copy away. However, a string copy will still be present in debug. \note Static strings are constructed by simpling pointing the other string start position. Remember: static strings are meant to "carry" underlying memory and interpret it as a string, so the underlying memory must stay valid. Use bufferize() on this string to ensure that it is copied. */ String( const String &other, uint32 begin, uint32 end = csh::npos ); /** Destroys the String. As the method is not virtual (neither the class is), different kind of strings are destroyed by calling the destroy() method of their manipulators. */ ~String() { m_class->destroy( this ); } /** Copies the original string as-is. If the original string is of a static type, the buffer is just referenced, else a deep copy is performed. \param other the string to be copied. \return itself */ void copy( const String &other ); /** Creates a String forcing bufferization of the original one. This function copies the foreign string in a buffer responding to the toCstring requirements (zero terminate 8-bit strings, i.e. char* or UTF8). As clone and copy (and copy constructor) try to preserve remote string static allocation, this function is required when a bufferized copy is explicitly needed. \param other the string to be copied. \return itself */ String &bufferize( const String &other ); /** Forces this string to get buffer space to store even static strings. \return itself */ String &bufferize(); /** Adopt a pre-allocated dynamic buffer. This function takes the content of the given buffer and sets it as the internal storage of the string. The buffer is considered dynamically allocated with memAlloc(), and will be destroyed with memFree(). This string is internally transformed in a raw buffer string; any previous content is destroyed. String is considered a single byte char width string. \param buffer the buffer to be adopted \param size the size of the string contained in the buffer (in bytes) \param allocated the size of the buffer as it was allocated (in bytes) \return itself */ String &adopt( char *buffer, uint32 size, uint32 allocated ); /** Adopt a pre-allocated dynamic buffer (wide char version). This function takes the content of the given buffer and sets it as the internal storage of the string. The buffer is considered dynamically allocated with memAlloc(), and will be destroyed with memFree(). This string is internally transformed in a raw buffer string; any previous content is destroyed. String is considered a wide char width string. \param buffer the buffer to be adopted \param size the size of the string contained in the buffer (in character count) \param allocated the size of the buffer as it was allocated (in bytes) \return itself */ String &adopt( wchar_t *buffer, uint32 size, uint32 allocated ); /** Return the manipulator of the class. The manipulator is the function vector (under the form of a pure-virtual without-data class) that is used to handle the string. The manipulator identifies also the type of string, and so the subclass of the String that this strings currently belongs to. */ const csh::Base *manipulator() const { return m_class; } /** Set the manipulator. Changing the manipulator also changes the meaning of the class deep data, and finally the correct subclass of String to which this item can be safely casted. Actually this method should be called only by internal functions, or only if you are really knowing what you are doing. */ void manipulator( csh::Base *m ) { m_class = m; } /** Return the type of the string. The type is determined by the manipulator. Warning: this method calls a function virtually, so is quite slow. Better use it rarely. An acid test could be performed by matching the manipulator pointer with the standard manipulators. */ csh::t_type type() const { return m_class->type(); } /** Returns the amount of allocated memory in the deep buffer. Used in buffers strings or in general in contiguous memory strings. Other kind of strings may ignore this (and so let it undefined) or use it for special purposes (i.e. amount of free memory on the last chunk in chunked strings.) */ uint32 allocated() const { return m_allocated; } /** Changes the amount of allocated memory. Used in buffers strings or in general in contiguous memory strings. Other kind of strings may ignore this (and so let it undefined) or use it for special purposes (i.e. amount of free memory on the last chunk in chunked strings.) */ void allocated( uint32 s ) { m_allocated = s; } /** Returns the amount of bytes the string occupies. This is the byte-size of the string, and may or may not be the same as the string length. */ uint32 size() const { return m_size; } /** Changes the amount of bytes the string is considered to occupy. This is the byte-size of the string, and may or may not be the same as the string length. */ void size( uint32 s ) { m_size = s; } /** Return the raw storage for this string. The raw storage is where the strings byte are stored. For more naive string (i.e. chunked), it may return the pointer to a structure helding more informations about the string data. */ byte *getRawStorage() const { return m_storage; } /** Changes the raw storage in this string. This makes the string to point to a new memory position for its character data. */ void setRawStorage( byte *b ) { m_storage = b; } /** Changes the raw storage in this string. This makes the string to point to a new memory position for its character data. */ void setRawStorage( byte *b, int size ) { m_storage = b; m_size = size; m_allocated = size; } /** Return the length of the string in characters. The string length may vary depending on the string manipulator. This function calls a method in the manipuplator that selects the right way to calculate the string character count. Being not a pure accessor, is actually better to cache this value somewhere if repeteadly needed. */ uint32 length() const { return m_class->length( this ); } /** Tranforms the string into a zero-terminated string. This function fills a buffer that can be fed in libc and STL function requiring a zero terminated string. The string manager will ensure that the data returned has one zero at the end of the string. 8-bit strings are left unchanged. International strings are turned into UTF-8 strings (so that they can be fed into internationalized STL and libc functions). The operation is relatively slow. Use when no other option is avalaible, and cache the result. Provide a reasonable space. Safe space is size() * 4 + 1. \param target the buffer where to place the C string. \param bufsize the size of the target buffer in bytes. \return npos if the buffer is not long enough, else returns the used size. */ uint32 toCString( char *target, uint32 bufsize ) const; /** Tranforms the string into a zero-terminated wide string. This function returns fills a buffer that can be fed in functions accpeting wchar_t strings. Returned strings are encoded in fixed lenght UTF-16, with endianity equivalent to native platform endianity. Character from extended planes are rendered as a single 0x003f. The operation is relatively slow. Use when no other option is avalaible, and cache the result. Required space is constant, and exactly (lenght() + 1) * sizeof(wchar_t) bytes (last "+1" is for final wchar_t "0" marker). \param target the buffer where to place the wchar_t string. \param bufsize the size of the target buffer in bytes. \return npos if the buffer size is not large enough, else returns the string length in wchar_t count */ uint32 toWideString( wchar_t *target, uint32 bufsize ) const; /** Reduces the size of allocated memory to fit the string size. Use this method to shrink the allocated buffer storing the string to the minimal size needed to hold the string. This is useful when i.e. a buffered string was allocated to provide extra space for more efficient iterative appending, and the iterative appending is over. This has no effect on static string. \note use wisely as the shrink operation may require a string copy. */ void shrink() { m_class->shrink( this ); } uint32 getCharAt( uint32 pos ) const { return m_class->getCharAt( this, pos ); } void setCharAt( uint32 pos, uint32 chr ) { m_class->setCharAt( this, pos, chr ); } String subString( int32 start, int32 end ) const { return String( *this, start, end ); } String subString( int32 start ) const { return String( *this, start, length() ); } bool change( int32 start, const String &other ) { return m_class->change( this, start, csh::npos, &other ); } bool change( int32 start, int32 end, const String &other ) { return m_class->change( this, start, end, &other ); } void insert( uint32 pos, uint32 len, const String &source ) { m_class->insert( this, pos, len, &source ); } void remove( uint32 pos, uint32 len ) { m_class->remove( this, pos, len ); } void append( const String &source ); void append( uint32 chr ); void prepend( uint32 chr ); void prepend( const String &source ) { m_class->insert( this, 0, 0, &source ); } uint32 find( const String &element, uint32 start=0, uint32 end=csh::npos) const { return m_class->find( this, &element, start, end ); } uint32 rfind( const String &element, uint32 start=0, uint32 end=csh::npos) const { return m_class->rfind( this, &element, start, end ); } /** Compares a string to another. Optimized to match against C strings. \see compare( const String &other ) \param other the other string to be compared \return -1 if this is less than the other, 0 if it's the same, 1 if it's greater. */ int compare( const char *other ) const; /** Compares a string to another. Optimized to match against wide characters C strings. \see compare( const String &other ) \param other the other string to be compared \return -1 if this is less than the other, 0 if it's the same, 1 if it's greater. */ int compare( const wchar_t *other ) const ; /** Compares a string to another ignoring the case. This metod returns -1 if this string is less than the other, 0 if it's the same and 1 if it's greater. \param other the other string to be compared \return -1 if this is less than the other, 0 if it's the same, 1 if it's greater. */ int compare( const String &other ) const; /** Compares a string to another ignoring the case. This metod returns -1 if this string is less than the other, 0 if it's the same and 1 if it's greater. Before checking them, uppercase characters are converted in the equivalent lowercase version; in this way "aBc" and "AbC" are considered the same. TODO - more caseization of accentuated letters \param other the other string to be compared \return -1 if this is less than the other, 0 if it's the same, 1 if it's greater. */ int compareIgnoreCase( const String &other ) const; /** Compares a string to another ignoring the case. Optimized to match against C strings. \see compareIgnoreCase( const String &other ) \param other the other string to be compared \return -1 if this is less than the other, 0 if it's the same, 1 if it's greater. */ int compareIgnoreCase( const char *other ) const; /** Compares a string to another ignoring the case. Optimized to match against C strings. \see compareIgnoreCase( const String &other ) \param other the other string to be compared \return -1 if this is less than the other, 0 if it's the same, 1 if it's greater. */ int compareIgnoreCase( const wchar_t *other ) const; /** Returns true if this string is empty. */ bool operator !() { return m_size == 0; } String & operator+=( const String &other ) { append( other ); return *this; } String & operator+=( uint32 other ) { append( other ); return *this; } String & operator+=( char other ) { append( (uint32) other ); return *this; } String & operator+=( const char *other ) { append( String( other ) ); return *this; } String & operator+=( wchar_t other ) { append( (uint32) other ); return *this; } String & operator+=( const wchar_t *other ) { append( String( other ) ); return *this; } String & operator=( const String &other ) { copy( other ); return *this; } String & operator=( uint32 chr ) { m_size = 0; append( chr ); return *this; } /** Assign from a const char string. If this string is not empty, its content are destroyed; then this object is changed into a static zero terminated C string and the phisical location of the const char assigned to this string is taken as undestroyable reference. This operation is meant for C string phisically stored somewhere in the program and that stay valid for the whole program duration, or at least for the whole lifespan of this Falcon::String object. */ String & operator=( const char *other ) { if ( m_storage != 0 ) m_class->destroy( this ); copy( String( other ) ); return *this; } /** Order predicate. This predicate is used to sort Falcon::String objects and is provided mainly as an interface for the stl container classes. \param other the other string to check for \return true if this string is considered less (smaller in collation order) than the other one. */ bool less( const String &other ) const { return compare( other ) < 0; } /** Save the string to a stream. The function never fails, but on failure something weird will happen on the stream. This may raise an exception, if the exceptions on the stream are turned on. \param out the stream on which to save the string. */ void serialize( Stream *out ) const; /** Load the string from a stream. The string is deserialized from the stream and allocated in memory. This means that if the original was a static string, the deserialized one will be a string buffer of compatible type. If the string cannot be de-serialized the function returns false and the value is left as it were before calling the function. If the de serialization is succesful, the bufferized string is initializated and the function returns true. A failure usually means a stream corruption or an incompatible format. \param in the input stream where the string must be read from \param bStatic true to create a self-destroryable static string \return true on success, false on failure. */ bool deserialize( Stream *in, bool bStatic=false ); /** Escapes a string for external representation. Convert special control characters to "\" prefixed characters, so that the resulting string can be used in a source code to regenerate the same string in parsing. Characters below 0x0008 (backspace) are turned into hexadecimal representation, while international characters (from 0x0080 up) are left unchanged. This means that the resulting string must still be sent through an encoder to be safely saved on a stream. \param target the target string */ void escape( String &target ) const; /** Escapes a string for external representation - full version. Convert special control characters to "\" prefixed characters, so tha the resulting string can be used in a source code to regenerate the same string in parsing. Characters below 0x0008 (backspace) and international characters (from 0x0080 up) are turned into hexadecimal representation. This means that the resulting string can be safely written on an output file without concerns for final encoding. \param target the target string */ void escapeFull( String &target ) const; /** Unescape this string. Unescaping string is always an operation that leaves the string unchanged or shortened, so it can be done in place. Static strings are converted into buffered strings only if some actual unescape takes place. String unescaping understands special codes \\", \\\\, \\\\r, \\\\n, \\\\t and \\\\b, octal numbers \\0nnnn and hexadecimal numbers as \\xnnnnn, up to 32 bit precision. */ void unescape(); /** Unescape this string placing the result in another one. \see unescape() */ void unescape( String &other ) const { other = *this; other.unescape(); } /** Minimal numerical conversion. If this string represents a valid integer, the integer is returned. The string is considered a valid integer also if it is followed by non-numerical data. \param target place where to store the number \param pos initial position in the string from which to start the conversion \return true if succesful, false if parse failed */ bool parseInt( int64 &target, uint32 pos = 0 ) const; /** Minimal numerical conversion. If this string represents a valid integer in octal format, the integer is returned. Pos must start after the octal marker \\0 or \\c. \param target place where to store the number \param pos initial position in the string from which to start the conversion \return true if succesful, false if parse failed */ bool parseOctal( uint64 &target, uint32 pos = 0 ) const; /** Minimal numerical conversion. If this string represents a valid integer in octal format, the integer is returned. Pos must start after the octal marker \\b. \param target place where to store the number \param pos initial position in the string from which to start the conversion \return true if succesful, false if parse failed */ bool parseBin( uint64 &target, uint32 pos = 0 ) const; /** Minimal numerical conversion. If this string represents a valid integer in hexadecimal format, the integer is returned. Pos must start after the octal marker \\x. \param target place where to store the number \param pos initial position in the string from which to start the conversion \return true if succesful, false if parse failed */ bool parseHex( uint64 &target, uint32 pos = 0 ) const; /** Minimal numerical conversion. If this string represents a valid floating point number, the number is returned. The string is considered a valid number also if it is followed by non-numerical data. Floating point number may be in scientific notation. \param target place where to store the number \param pos initial position in the string from which to start the conversion \return true if succesful, false if parse failed */ bool parseDouble( double &target, uint32 pos = 0 ) const; /** Converts a number to a string and appends it to this string. \param number the number to be converted. */ void writeNumber( int64 number ); /** Converts a number to a string and appends it to this string. This version writes the number in hex format. \param number the number to be converted. \param uppercase true to use ABCDEF letters instead of abdef */ void writeNumberHex( uint64 number, bool uppercase = true, int count = 0 ); /** Converts a number to a string and appends it to this string. This version writes the number in octal format. \param number the number to be converted. */ void writeNumberOctal( uint64 number ); /** Converts a number to a string and appends it to this string. Number is formatted with prinf format "%e"; to specify a different format, use the other version of this method. \param number the number to be converted. */ void writeNumber( double number ) { writeNumber( number, "%e" ); } /** Converts a number to a string and appends it to this string. This version allows to specify a format to be passed to sprintf in the conversion of the number. Regardless of the fact that sprintf writes a string int 8-bit char space, both the format string and this string where the result is to be appended may be in any char width. The function does not check for validity of the format. \param number the number to be converted. \param format the format to be passed to printf for conversion. */ void writeNumber( double number, const String &format ); void writeNumber( int64 number, const String &format ); /** Cumulative version of writeNumber. * * This method can be used to concatenate strings and number such as for * String s = String( "You got ").N( msg_count ).A( " messages " ); */ inline String& N( int64 number ) { writeNumber( number ); return *this; } /** Cumulative version of writeNumber. * * This method can be used to concatenate strings and number such as for * String s = String( "You got ").N( msg_count ).A( " messages " ); */ inline String& N( int32 number ) { writeNumber( (int64) number ); return *this; } /** Cumulative version of writeNumber. * * This method can be used to concatenate strings and number such as for * String s = String( "You got ").N( msg_count ).A( " messages " ); */ inline String& N( int64 number, const String& format ) { writeNumber( (int64) number, format ); return *this; } /** Cumulative version of writeNumber. * * This method can be used to concatenate strings and number such as for * String s = String( "You got ").N( msg_count ).A( " messages " ); */ inline String& N( double number ) { writeNumber( number ); return *this; } /** Cumulative version of writeNumber. * * This method can be used to concatenate strings and number such as for * String s = String( "You got ").N( msg_count ).A( " messages " ); */ inline String& N( double number, const String& format ) { writeNumber( number, format ); return *this; } /** Cumulative version of writeHex */ inline String& H( uint64 number, bool ucase, int ciphers = 0 ) { writeNumberHex( number, ucase, ciphers ); return *this; } /** Cumulative version of append. * * This method can be used to concatenate strings and number such as for * String s = String( "You got ").N( msg_count ).A( " messages " ); */ inline String& A( const String& str ) { append(str); return *this; } /** Cumulative version of append. * * This method can be used to concatenate strings and number such as for * String s = String( "You got ").N( msg_count ).A( " messages " ); */ inline String& A( int chr ) { append((uint32)chr); return *this; } /** Cumulative version of append. * * This method can be used to concatenate strings and number such as for * String s = String( "You got ").N( msg_count ).A( " messages " ); */ inline String& A( uint32 chr ) { append(chr); return *this; } /** Checks the position to be in the string, and eventually changes it if it's negative. This is just a nice inline shortuct so that the string constructor for substrings can be called safely. \param pos the position to be checked and eventually turned into a positive value. \return false if pos is outside thte string */ bool checkPosBound( int32 &pos ) { register int s = length(); if ( pos < 0 ) pos = s + pos; if ( pos < 0 || pos >= s ) return false; return true; } /** Checks the range to be in the string, and eventually changes it if it's negative. This is just a nice inline shortuct so that the string constructor for substrings can be called safely. \param begin the start to be checked and eventually turned into a positive value. \param end the end to be checked and eventually turned into a positive value. \return false if the range cannot be mapped in string. */ bool checkRangeBound( int32 &begin, int32 &end ) { register int s = length(); if ( begin < 0 ) begin = s + begin; if ( begin < 0 || begin >= s ) return false; if ( end < 0 ) end = s + end; // end can be the same as lenght if ( end < 0 || end > s ) return false; return true; } /** Reserve buffer space in the target string. This ensures that the space allocated in the string is bufferzed and at least wide enough to store the requested bytes. \note the width is in bytes. \param size minimal space that must be allocated as writeable heap buffer (in bytes). */ void reserve( uint32 size ) { m_class->reserve( this, size ); } /** Remove efficiently whitespaces at beginning and end of the string. If whitespaces are only at the end of the string, the lenght of the string is simply reduced; this means that static strings may stay static after this process. In case of whitespaces at beginning, the string will be resized, and eventually allocated, moving the characters back to the beginning position. \param mode 0 = all, 1 = front, 2 = back */ void trim( int mode ); void trim() { trim( 0 ); } /** * Remove efficiently 'what' at the beginning of the string. * * If what is empty, whitespaces are removed. */ void frontTrim() { trim( 1 ); } void backTrim() { trim( 2 ); } /** * Convert the string to all lower case. */ void lower(); /** * Convert the string to all upper case. */ void upper(); bool isStatic() const { return manipulator()->type() == csh::cs_static || manipulator()->type() == csh::cs_static16 || manipulator()->type() == csh::cs_static32; } /** Bufferize an UTF-8 string. This is an efficient shortcut for the very common case of UTF8 strings being turned into falcon string. There isn't a drect constructor that understands that the input char * is an UTF8 string, but the proxy generators UTF8String and UTF8String serve to this purpose. After the call, the previous content of this string is destroyed. In case of an invalid UTF8 sequence, up to what is possible to decode is read, and the function return false. \param utf8 the utf8 string to be loaded \param len Expected length (-1 to scan for '\0' in the input string). \return true on success, false if the sequence is invalid. */ bool fromUTF8( const char *utf8, int32 len ); bool fromUTF8( const char *utf8 ); /** Access to a single character. Please, notice that Falcon strings are polymorphic in assignment, so they cannot support the following syntax: \code s[n] = c; // can't work with Falcon strings. \endcode This operator is provided as a candy grammar for getCharAt(). */ const uint32 operator []( uint32 pos ) const { return getCharAt( pos ); } /** Return wether this exact string instance should be internationalized. \note exported() attribute is not copied across string copies. */ bool exported() const { return m_bExported; } /** Sets wether this string should be exported in international context or not. */ void exported( bool e ) { m_bExported = e; } /** Adds an extra '\0' terminator past the end of the string. This makes the string data (available through getRawStorage()) suitable to be sent to C functions compatible with the character size of this string. Eventually, it should be preceded by a call to setCharSize(). */ void c_ize(); /** Compares a string with the beginning of this string. If \b str is empty, returns true, if it's larger than this string returns false. \param str The string to be compared against the beginning of this string. \param icase true to perform a case neutral compare \return true on success. */ bool startsWith( const String &str, bool icase=false ) const; /** Compares a string with the end of this string. If \b str is empty, returns true, if it's larger than this string returns false. \param str The string to be compared against the end of this string. \param icase true to perform a case neutral compare \return true on success. */ bool endsWith( const String &str, bool icase=false ) const; /** Matches this string against a dos-like wildcard. \param wildcard A dos-like wildcard. \param bICase true if this function should ignore the character case of the two strings. \return true if the wildcard matches this string. */ bool wildcardMatch( const String& wildcard, bool bICase = false ) const; /** Makes all the quotes and double quotes in this string to be preceeded by a '\' slash */ void escapeQuotes(); /** Removes all the slashes before quotes. */ void unescapeQuotes(); /** Alters the character size of this string. Changes the number of bytes used to represent a single character in this string. The number of byte used can be 1, 2 or 4. If the new character size of this string is smaller than the original one, characters that cannot be represented are substituted with a \b subst value (by default, the maximum value allowed for the given character size). If the original character size was different, the string is bufferized into a new memory area, otherwise the string is unchanged. \note subst param is not currently implemented. @param nsize The new character size for the string. @param subst The substitute character to be used when reducing size. @return True if the nsize value is valid, false otherwise. */ bool setCharSize( uint32 nsize, uint32 subst=0xFFFFFFFF ); void writeIndex( const Item &index, const Item &target ); void readIndex( const Item &index, Item &target ); void readProperty( const String &prop, Item &item ); bool isCore() const { return m_bCore; } static bool isWhiteSpace( uint32 chr ) { return chr == ' ' || chr == '\t' || chr == '\r' || chr == '\n'; } }; /** Equality operator */ inline bool operator == ( const String &str1, const String &str2 ) { return str1.compare( str2 ) == 0; } inline bool operator == ( const String &str1, const char *str2 ) { return str1.compare( str2 ) == 0; } inline bool operator == ( const String &str1, const wchar_t *str2 ) { return str1.compare( str2 ) == 0; } inline bool operator != ( const String &str1, const String &str2 ) { return str1.compare( str2 ) != 0; } inline bool operator != ( const String &str1, const char *str2 ) { return str1.compare( str2 ) != 0; } inline bool operator != ( const String &str1, const wchar_t *str2 ) { return str1.compare( str2 ) != 0; } inline bool operator > ( const String &str1, const String &str2 ) { return str1.compare( str2 ) > 0; } inline bool operator > ( const String &str1, const wchar_t *str2 ) { return str1.compare( str2 ) > 0; } inline bool operator > ( const String &str1, const char *str2 ) { return str1.compare( str2 ) > 0; } inline bool operator < ( const String &str1, const String &str2 ) { return str1.compare( str2 ) < 0; } inline bool operator < ( const String &str1, const char *str2 ) { return str1.compare( str2 ) < 0; } inline bool operator < ( const String &str1, const wchar_t *str2 ) { return str1.compare( str2 ) < 0; } inline bool operator >= ( const String &str1, const String &str2 ) { return str1.compare( str2 ) >= 0; } inline bool operator >= ( const String &str1, const char *str2 ) { return str1.compare( str2 ) >= 0; } inline bool operator >= ( const String &str1, const wchar_t *str2 ) { return str1.compare( str2 ) >= 0; } inline bool operator <= ( const String &str1, const String &str2 ) { return str1.compare( str2 ) <= 0; } inline bool operator <= ( const String &str1, const char *str2 ) { return str1.compare( str2 ) <= 0; } inline bool operator <= ( const String &str1, const wchar_t *str2 ) { return str1.compare( str2 ) <= 0; } inline String operator +( const String &str1, const String &str2 ) { String str3; str3.append( str1 ); str3.append( str2); return str3; } inline String operator +( const char *str1, const String &str2 ) { String str3; str3.append( str1 ); str3.append( str2); return str3; } inline String operator +( const wchar_t *str1, const String &str2 ) { String str3; str3.append( str1 ); str3.append( str2); return str3; } inline String operator +( const String &str1, const char *str2 ) { String str3; str3.append( str1 ); str3.append( str2); return str3; } inline String operator +( const String &str1, const wchar_t *str2 ) { String str3; str3.append( str1 ); str3.append( str2); return str3; } /** Core string comparer class */ class StringPtrCmp { public: bool operator() ( const String *s1, const String *s2 ) const { return s1->compare( *s2 ) < 0; } }; //================================= // Helpful list of string deletor // void string_deletor( void *data ); class CoreString; class FALCON_DYN_CLASS StringGarbage: public Garbageable { CoreString *m_str; public: StringGarbage( CoreString *owner ): Garbageable(), m_str( owner ) {} virtual ~StringGarbage(); virtual bool finalize(); }; /** Garbage storage string. This is the String used in VM operations (i.e. in all the VM items). It is allocated inside the garbage collector, and cannot be directly deleted. */ class FALCON_DYN_CLASS CoreString: public String { StringGarbage m_gcptr; public: CoreString(): String(), m_gcptr( this ) { m_bCore = true; } CoreString( const String &str ): String( str ), m_gcptr( this ) { m_bCore = true; } CoreString( const CoreString &str ): String( str ), m_gcptr( this ) { m_bCore = true; } CoreString( const char *data ): String( data ), m_gcptr( this ) { m_bCore = true; } CoreString( const wchar_t *data ): String( data ), m_gcptr( this ) { m_bCore = true; } CoreString( const char *data, int32 len ): String( data, len ), m_gcptr( this ) { m_bCore = true; } CoreString( const wchar_t *data, int32 len ): String( data, len ), m_gcptr( this ) { m_bCore = true; } /** Creates a bufferized string with preallocated space. */ explicit CoreString( uint32 prealloc ): String( prealloc ), m_gcptr( this ) { m_bCore = true; } CoreString( const String &other, uint32 begin, uint32 end = csh::npos ): String( other, begin, end ), m_gcptr( this ) { m_bCore = true; } const StringGarbage &garbage() const { return m_gcptr; } StringGarbage &garbage() { return m_gcptr; } void mark( uint32 m ) { m_gcptr.mark( m ); } CoreString & operator+=( const CoreString &other ) { append( other ); return *this; } CoreString & operator+=( const String &other ) { append( other ); return *this; } CoreString & operator+=( uint32 other ) { append( other ); return *this; } CoreString & operator+=( char other ) { append( (uint32) other ); return *this; } CoreString & operator+=( const char *other ) { append( String( other ) ); return *this; } CoreString & operator+=( wchar_t other ) { append( (uint32) other ); return *this; } CoreString & operator+=( const wchar_t *other ) { append( String( other ) ); return *this; } CoreString & operator=( const CoreString &other ) { copy( other ); return *this; } CoreString & operator=( const String &other ) { copy( other ); return *this; } CoreString & operator=( uint32 chr ) { m_size = 0; append( chr ); return *this; } CoreString & operator=( const char *other ) { if ( m_storage != 0 ) m_class->destroy( this ); copy( String( other ) ); return *this; } }; //================================= // inline proxy constructors // /** Creates a String from an utf8 sequence on the fly. This is a proxy constructor for String. The string data is bufferized, so the sequence needs not to be valid after this call. \note this function returns a valid string also if the \b utf8 paramter is not a valid utf8 sequence. If there is the need for error detection, use String::fromUTF8 instead. \see String::fromUTF8 \param utf8 the sequence \return a string containing the decoded sequence */ inline CoreString *UTF8String( const char *utf8 ) { CoreString *str = new CoreString; str->fromUTF8( utf8 ); return str; } } #endif /* end of string.h */ include/falcon/stringstream.h000066400000000000000000000120441176363201700166450ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: file_string.h Management of membuffer strings; directly included by file_base.h ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: sab nov 13 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Management of membuffer strings; directly included by file_base.h. */ #ifndef flc_file_string_H #define flc_file_string_H #include #include namespace Falcon { class FALCON_DYN_CLASS StringStream: public Stream { private: class Buffer; Buffer* m_b; protected: uint64 m_lastError; uint32 m_pos; virtual int64 seek( int64 pos, e_whence whence ); void setBuffer( const String &source ); void setBuffer( const char* source, int size=-1 ); bool detachBuffer(); bool subWriteString( const String &source ); public: StringStream( int32 size=0 ); StringStream( const String &strbuf ); StringStream( const StringStream &strbuf ); virtual ~StringStream(); virtual bool close(); virtual int32 read( void *buffer, int32 size ); virtual bool readString( String &dest, uint32 size ); virtual int32 write( const void *buffer, int32 size ); virtual bool writeString( const String &source, uint32 begin = 0, uint32 end = csh::npos ); virtual bool put( uint32 chr ); virtual bool get( uint32 &chr ); virtual int32 readAvailable( int32 msecs, const Sys::SystemData *sysData = 0 ); virtual int32 writeAvailable( int32 msecs, const Sys::SystemData *sysData = 0 ); virtual int64 tell(); virtual bool truncate( int64 pos=-1 ); uint32 length() const; uint32 allocated() const; byte *data() const; virtual bool errorDescription( ::Falcon::String &description ) const; /** Transfers a string stream buffer into this one. The original buffer is emptied, and this buffer aqcuires the same status, allocation and contents of the other one. */ void transfer( StringStream &strbuf ); /** Gets a string copying the content of the stream. The memory that is currently held in this object is copied in a string. Read-write operations can then continue, and the status of the object is not changed. \param target The string where the buffer is copied. */ void getString( String &target ) const; /** Gets a string copying the content of the stream, newly allocating the target string. The memory that is currently held in this object is copied in a string. Read-write operations can then continue, and the status of the object is not changed. \return a string containing all the data in the stream (may be empty, but not 0). */ String *getString() const { String *temp = new String; getString( *temp ); return temp; } /** Gets a string copying the content of the stream, allocating in the garbage the target string. The memory that is currently held in this object is copied in a string. Read-write operations can then continue, and the status of the object is not changed. \return a string containing all the data in the stream (may be empty, but not 0). */ CoreString *getCoreString() const { CoreString *temp = new CoreString; getString( *temp ); return temp; } /** Gets the phisical memory created by this object and turns it into a string. The memory that has been created by the stream-like operations is directly passed into a string object, in a very efficient way; as a result, the buffer in this object is transferred as-is into the returned string, and this object becomes unuseable (closed). If the stream has already been closed, the function will return 0. \return a string containing all the data in the stream. */ String *closeToString(); /** Gets the phisical memory created by this object and turns it into a newly created garbage collected string. \see closeToString() \return a string containing all the data in the stream. */ CoreString *closeToCoreString(); /** Gets the phisical memory created by this object and turns it into a string. This version of the method stores the phisical memory in the given string, and configures it as a single byte memory buffer string. \return false if the stream has already been closed. */ bool closeToString( String &target ); /** Gets the phisical memory created by this object. This version of the method retreives the internally allocated buffer and empties this StringStream. The returned buffer must be de-allocated using Falcon::memFree() \return a byte * that will receive the internally created data. */ byte *closeToBuffer(); virtual int64 lastError() const; virtual StringStream *clone() const; virtual void gcMark( uint32 mark ) {} }; } #endif /* end of file_string.h */ include/falcon/stringstream_srv.h000066400000000000000000000020461176363201700175400ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: sstreamapi.h String Stream service publisher. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom mar 5 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file String Stream service publisher. Import this service ("StringStream") from RTL module if your embedding application needs to create Falcon::StingStreams. */ #ifndef flc_stringstream_srv_H #define flc_stringstream_srv_H #include #include #include namespace Falcon { class StringStreamService: public Service { public: StringStreamService(): Service("StringStream") {} virtual StringStream *create(); virtual StringStream *create( int32 size ); virtual StringStream *create( String *origin ); }; } #endif /* end of sstreamapi.h */ include/falcon/stringwithid.h000066400000000000000000000030301176363201700166350ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: stringwithid.h An extenson of the Falcon::String that saves also the module string ID. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: gio lug 21 2005 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file An extenson of the Falcon::String that saves also the module string ID. */ #ifndef flc_stringwithid_H #define flc_stringwithid_H #include namespace Falcon { /** Pair of strings and their ID. Useful during compilation to create objects that need to know the string ID in the module rather than the physical string. */ class StringWithID: public String { int32 m_id; public: /** allows on-the-fly core string creation from static data */ StringWithID( const char *data ): String( data ) {} StringWithID( const char *data, uint32 len ): String( data, len ) {} StringWithID(): String() {} StringWithID( byte *buffer, uint32 size, uint32 allocated ): String( buffer, size, allocated ) {} explicit StringWithID( uint32 prealloc ): String( prealloc ) {} StringWithId( const StringWithID &other ): String( other ) { m_id = other.m_id; } int32 id() const { return m_id; } void id( int32 val ) { m_id = val; } }; } #endif /* end of stringwithid.h */ include/falcon/strtable.h000066400000000000000000000140241176363201700157430ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: strtable.h String table used in modules ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon Feb 14 2005 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file String table used in modules */ #ifndef flc_strtable_H #define flc_strtable_H #include #include #include #include #include namespace Falcon { class Stream; class ModuleLoader; class FALCON_DYN_CLASS StringTable: public BaseAlloc { GenericVector m_vector; Map m_map; Map m_intMap; char *m_tableStorage; uint32 m_internatCount; friend class ModuleLoader; // Non-const version of get is private String *getNonConst( uint32 id ) { if ( id < m_vector.size() ) return *(String **) m_vector.at( id ); return 0; } public: StringTable(); StringTable( const StringTable &other ); ~StringTable(); void reserve( int32 size ) { m_vector.reserve( size ); } int32 add( String *str ); int32 findId( const String &str ) const; String *find( const String &source ) const; /** Skip the string table from a stream. This is useful when i.e. you have a string table embedded in a stream but you don't want to load it, for example because you want to use an external string table instead. */ bool skip( Stream *in ) const; const String *get( uint32 id ) const { if ( id < m_vector.size() ) return *(String **) m_vector.at( id ); return 0; } const String *operator[]( int32 id ) { return *(String **) m_vector.at( id );; } int32 size() const { return m_vector.size(); } /** Save the string table in a stream. The string table is saved as a block, without using the serialization function of the Falcon::String objects. The block has a string table specific format, so that the load() function can load the whole block back in memory and then create each string in the table as a static non-zero terminated or static zero terminated string (in the proper encoding). None of the serialized string is re-created as bufferized, as the memory in which the string raw data resides is held internally in this object. Also, notice that all the strings and the relative raw data memory is destroyed with this object. The rationale for this behavior is that the StringTable object is meant to hold a specific set of strings that are related to some specific task. Usually, serialization and de-serialization of the string table occurs in module compilation and loading. As the vast majority of the strings in the table will be accessed read only, to provide flexible storage at load time for all of the would be unefficient. The data block is aligned to a multiple of 4. I.e. if the function were to write 1438 bytes, it will actually deliver on the stream 1440 bytes, two of which being padding. The function never fails, but if the output stream has a failure the function doesn't detect it. The output stream status must be checked on exit. \param out the stream where to save the table. \return true on success, false on failure */ bool save( Stream *out ) const; /** Restores a string table that was saved on a stream. For more details see save(). \see save() \param in the input stream where the table must be loaded from \return true on success, false on failure (format error). */ bool load( Stream *in ); /** Saves a template file out of this string table. Template files are needed for internationalization. The template file will be written in an XML format. This function doesn't write the ?xml header of the xml file, as that requires the caller to know the encoding of the output stream. The caller should do it instead. \TODO Add encoding ID to common Stream interface. A template file contains all the strings of the table so that the compiler of a translation set can associate them with translation. \param out A Falcon::Stream for output. \param modName The name of the moule to be written in the template file. \param origLangCode the language code of this symbol table. */ bool saveTemplate( Stream *out, const String &modName, const String &origLangCode ) const; /** Builds the table from a source file initialization. Useful to build static string tables in modules and in the engine. Provide the function with an array of char pointers, the last of which being 0; this will create a suitable module string table where the first string in the array has id 0, the second has id 1 and so on. \param table a vector of char * terminated by zero. \param bInternational the string table contains items to be internationalized. */ void build( char **table, bool bInternational ); /** Builds the table from a source file initialization. Useful to build static string tables in modules and in the engine. Provide the function with an array of char pointers, the last of which being 0; this will create a suitable module string table where the first string in the array has id 0, the second has id 1 and so on. \param table a vector of wchar_t * terminated by zero. \param bInternational the string table contains items to be internationalized. */ void build( wchar_t **table, bool bInternational ); /** Returns the count of international strings added to this symbol table. \note if this is zero, then the module writers shouldn't even create the template for the given module. */ uint32 internatCount() const { return m_internatCount; } }; } #endif /* end of strtable.h */ include/falcon/symbol.h000066400000000000000000000645461176363201700154460ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: symbol.h Symbol -- load time static definitions for program-wide entities. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar ago 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_SYMBOL_H #define FLC_SYMBOL_H #include #include #include #include #include #include #include #include #include #include namespace Falcon { class Symbol; class Stream; class AttribMap; /** Implements an external function definition. */ class FALCON_DYN_CLASS ExtFuncDef: public BaseAlloc { /** Function. */ ext_func_t m_func; /** Extra data. \see extra() */ void *m_extra; SymbolTable *m_params; public: ExtFuncDef( ext_func_t func ): m_func( func ), m_extra( 0 ), m_params( 0 ) {} /** Crates this definition setting extra data. \see extra() */ ExtFuncDef( ext_func_t func, void *extra ): m_func( func ), m_extra( extra ), m_params(0) {} ~ExtFuncDef(); /** Call this function. Will crash if function is not an external function. */ void call( VMachine *vm ) const { m_func( vm ); } /** Call this function. */ void operator()( VMachine *vm ) const { call( vm ); } /** Gets extra data. \see void extra( void *) */ void *extra() const { return m_extra; } /** Sets extra data for this function call. This extra data is useful to create flexible reflected calls. The ext_func_t gets called with the VM data; it can then decode the VM data and prepare it accordingly to m_extra, and finally call some different function as retreived data instructs to do. This allows to reuse a single ext_func_t in m_func to call different binding function. This item doesn't own m_extra; the data must be held in the same module in which this function exists (or in the application where this module is run), and must stay valid for all the time this function stays valid. Extra data will be available to m_func through VMachine::symbol(), which will return the symbol containing this funcdef. */ void extra( void *e ) { m_extra = e; } /** Returns the ID of the given function parameter, or -1 if the parameter doesn't exist. */ int32 getParam( const String &name ); /** Adds a function parameter with the specified ID. Consider using Symbol::addParam() instead (candy grammar). */ ExtFuncDef &addParam( Symbol *param, int32 id =-1 ); /** External Function symbol table. Not all the external functions need to be provided with a symbol table (actually storing only formal parameters). For this reason, the symbol table of external functions gets allocated only when actually adding parameters. */ SymbolTable *parameters() const { return m_params; } uint32 paramCount() const { return m_params == 0 ? 0 : m_params->size(); } ext_func_t func() const { return m_func; } }; /** Implements an imported symbol. */ class FALCON_DYN_CLASS ImportAlias: public BaseAlloc { const String m_name; const String m_origModule; bool m_isFileName; public: ImportAlias( const String& name, const String& origModule, bool bIsFileName = false ): m_name( name ), m_origModule( origModule ), m_isFileName( bIsFileName ) {} const String &name() const { return m_name; } const String &origModule() const { return m_origModule; } bool isOrigFileName() const { return m_isFileName; } }; /** Implements a callable symbol. A callable symbol has many more fields to keep track of. It has a back-pointer to the owning module, because the module contains the bytecode where data is held. It has also a symbol table pointer holding variable only symbols in it. */ class FALCON_DYN_CLASS FuncDef: public BaseAlloc { /** Private sub-symbol table. Used in classes & callables */ SymbolTable m_symtab; /** Function code. Owned by the symbol and destroyed on exit. */ byte *m_code; /** Function size of the code. Owned by the symbol and destroyed on exit. */ uint32 m_codeSize; /** Minimal default parameters */ uint16 m_params; /** Count of local variables */ uint16 m_locals; /** Count of local variables - still undefined */ uint16 m_undefined; /** Cache item for functions with static elements. If the function is not static, it will be NO_STATE. */ uint32 m_onceItemId; uint32 m_basePC; /** Attributes table. It's a map of attributes. They're symbol-specific dictionaries of strings-values that become read-only in realtime. (String& , VarDef *) */ AttribMap* m_attributes; public: enum { NO_STATE = 0xFFFFFFFF } enum_NO_STATE; /** Constructor for external generators. Requires that the funcdef is provided with a previously allocated code. The code is owned by the FuncDef and destroyed with this instance. */ FuncDef( byte *code, uint32 codeSize ); ~FuncDef(); const SymbolTable &symtab() const { return m_symtab; } SymbolTable &symtab() { return m_symtab; } Symbol *addParameter( Symbol *sym ); Symbol *addLocal( Symbol *sym ); Symbol *addUndefined( Symbol *sym ); uint32 codeSize() const { return m_codeSize; } void codeSize( uint32 p ) { m_codeSize = p; } byte *code() const { return m_code; } void code( byte *b ) { m_code = b; } uint16 params() const { return m_params; } uint16 locals() const { return m_locals; } uint16 undefined() const { return m_undefined; } void params( uint16 p ) { m_params = p; } void locals( uint16 l ) { m_locals = l; } void undefined( uint16 u ) { m_undefined = u; } void basePC( uint32 pc ) { m_basePC = pc; } /** BasePC at which this symbol was declared. This is useful only to find lines in the code of this function in the global line table. The line that generated a certain function is found through basepc + VM->pc. */ uint32 basePC() const { return m_basePC; } /** Counts the parameters and the local variables that this funcdef has in its symtab. Useful when the function object is created by the compiler, that won't use addParameter() and addLocal(). Those two metods are for API implementors and extensions, while the compiler has to add symbols to the function symbol table; for this reason, at the end of the function the compiler calls this method to cache the count of locals and parameters that the linker must create. */ void recount(); bool save( const Module *mod, Stream *out ) const; bool load( const Module *mod, Stream *in ); void onceItemId( uint32 id ) { m_onceItemId = id; } uint32 onceItemId() const { return m_onceItemId; } /** Adds an attribute. The first attribute added will cause the map to be created. */ void addAttrib( const String& name, VarDef* value ); /** Returns a map of String& -> VarDef* containing metadata about this symbol. May return if the item has no attributes. */ AttribMap* attributes() const { return m_attributes; } }; /** Inheritance definition. Every class may be derived from one or more subclasses. Every inheritance will force the class to include all the properties of the subclasses, and to eventually call the base class constructors, if they have. Constructors may be called with parameters, which may be constants or symbols taken from the class symbol table, that is, from the parameters that have been passed to the class. This structure is needed to record the order and kind of parameters given to subclasses instantation without writing VM code. In this way, each class constructor will handle only its local instantation, and the correct call of each constructor will be handled outside the main VM loop, in a smart and efficient way. */ class FALCON_DYN_CLASS InheritDef: public BaseAlloc { private: /** The base class. */ Symbol *m_baseClass; public: /** Constructs the Inheritance. The inheritance is initially built without parameters. \param bc the base class this inheritance refers to. */ InheritDef( Symbol *bc ): m_baseClass( bc ) {} /** Empty constructor. Mainly used during deserialization. */ InheritDef(): m_baseClass( 0 ) {} ~InheritDef(); bool save( Stream *out ) const; bool load( const Module *mod, Stream *in ); Symbol *base() const { return m_baseClass; } }; class StateDef: public BaseAlloc { const String* m_name; /** Functions in this state. * (String*, Symbol*) * Strings and symbol are owned by the module. */ Map m_functions; public: StateDef( const String* sname ); const String& name() const { return *m_name; } const Map& functions() const { return m_functions; } Map& functions() { return m_functions; } bool addFunction( const String& name, Symbol* func ); bool load( const Module *mod, Stream* in ); bool save( Stream* out ) const; }; /** Class symbol abstraction. A class symbol has multiple lives: it looks like a function, and if used as a function it has the effect to create an object instance. The class constructor is in fact the "function" held by the symbol. Other than that, the class symbol stores also symbol tables for variables (properties) and functions (methods), other than the local + params symbol table as any other callable. Finally, the ClassSymbol holds a vector of direct parent pointers, each of which being a ClassSymbol. Class symbols can be "reflective". They can be linked with a "user data manager". As reflection status cannot be properly serialized (atm), only user-provided and binary modules can declare reflective classes. */ class FALCON_DYN_CLASS ClassDef: public FuncDef { public: typedef List InheritList; private: /** Class onstructor. This symbol may contain a Falcon function in any module; the function must just be aware that it will "self" valorized to the newborn object, and it will be called with the same parameters by which the class is called. */ Symbol *m_constructor; /** Properties table. The properties symbol table has not the ownership of its symbol. All symbols are owned by the module. If the class is somehow destroyed, its member may be still available to the module (i.e. they may be have already been moved to another class, or as standalone functions). This is valid also for the data members, which are named after the "class.property" scheme. However, the strings used as the keys in this table are NOT the symbol name ("class.property"), but the property name that the class uses to identify a member (i.e. just "property"). The inheritance is part of the properties, as parent classes are accesible via the operator "class.base_class". (const String *, VarDef *) */ Map m_properties; /** Inheritance list. */ InheritList m_inheritance; /** Object factory used by this class. Function used to create instances of objects from this class. */ ObjectFactory m_factory; int m_metaclassFor; bool m_bFinal; /** State table. (const String *, StateDef *) */ Map m_states; public: /** Creates a class definition without a constructor. This constructor creates a class definition for those classes which doesn't require an "init" method declared at Falcon level, nor an external C method used to provide an "init" feature. \param manager The object factory used to manipulate externally provided user_data of this class. If not provided, the factory will depend on added properties. */ ClassDef( ObjectFactory factory=0 ); /** Creates the definition of this class an external constructor. This constructor creates a class that has an init method provided by an external symbol (a C function). \param offset the start offset of the constructor, if this class as a Falcon code constructor. \param manager The object factory used to manipulate externally provided user_data of this class. If not provided, the factory will depend on added properties. */ ClassDef( Symbol *ext_ctor, ObjectFactory factory=0 ); ~ClassDef(); /** Declares an object manager for this class. The instance of the object manager should be allocated in the user application or in the binary modules. The classes never dispose them. */ void factory( ObjectFactory om ) { m_factory = om; } ObjectFactory factory() const { return m_factory; } const Map &properties() const { return m_properties; } /** Return a updateable version of the property map. */ Map &properties() { return m_properties; } const InheritList &inheritance() const { return m_inheritance; } /** Return a updateable version of the inheritance list. */ InheritList &inheritance() { return m_inheritance; } void constructor( Symbol *ctor ) { m_constructor = ctor; } Symbol *constructor() const { return m_constructor; } bool save( const Module* mod, Stream *out ) const; bool load( const Module* mod, Stream *in ); /** Adds a property to a class. If the a property with the same name already existed, the content is destroyed and then overwritten. The name is a string pointer that should be contained int the module string table. \param name the name of the property \param definition the definition of the property as a VarDef */ void addProperty( const String *name, VarDef *definition ); bool hasProperty( const String &name ) { return m_properties.find( &name ) != 0; } /** Shortcut to add a variable property. \param name the name of the property that is considered a NIL variable. */ void addProperty( const String *name ) { addProperty( name, new VarDef() ); } /** Shortcut to add a method to a class. \param name the name of the method. \param method the Symbol holding the method call. */ void addProperty( const String *name, Symbol *method ) { addProperty( name, new VarDef( method ) ); } VarDef *getProperty( const String *name ) const; VarDef *getProperty( const String &name ) const { return getProperty( &name ); } /** Adds a symbol as an inheritance instance. It also takes care to store it in the property table. \return true if the add is successful, false if the inheritance has been already added. */ bool addInheritance( InheritDef *parent_class ); /** Checks if a given children of this class compares in the ancestor list. */ bool checkCircularInheritance( const Symbol *child ) const; /** Returns true if one of the base classes of this one has the given name. */ bool inheritsFrom( const String &find_name ) const; int isMetaclassFor() const { return m_metaclassFor; } void setMetaclassFor( int ItemID ) { fassert( ItemID >= 0 && ItemID < FLC_ITEM_COUNT ); m_metaclassFor = ItemID; } /** Set this as a "final" class. Final classes can be inherited, but it is not possible to inherit from more than one final class in the whole hierarchy. Final classes store binary data that must be uniquely identified by functions in the hierarchy. */ void setFinal( bool mod ) { m_bFinal = mod; } /** Returns true if this class is final. \see setFinal() */ bool isFinal() const { return m_bFinal; } /** Creates a new state for this class. States are set of functions that are applied all at the same time to an object. Also, special __enter and __leave functions are called back with the name of the state from which this state is entered or where this state is going to. An object enters the "init" state, if provided, after complete instantation. */ StateDef* addState( const String* stateName ); bool addState( const String* stateName, StateDef* state ); const Map& states() const { return m_states; } Map& states() { return m_states; } }; /** Representation of a VM symbol For now, it is only an accessible strucutre. As symbol names are allocated in the symbol table part of the module, they are currently not deleted at item destruction. Symbol names \b must be allocated in the module symbol table. */ class FALCON_DYN_CLASS Symbol: public BaseAlloc { public: typedef enum { tundef, tglobal, tlocal, tparam, tlocalundef, tfunc, textfunc, tclass, tprop, tvar, tinst, tconst, timportalias } type_t; private: typedef enum { FLAG_EXPORTED=0x1, FLAG_ETAFUNC=0x2, FLAG_WELLKNOWN=0x4, FLAG_IMPORTED=0x8, FLAG_ENUM=0x16 } e_flags; type_t m_type; /** Flags as exported or ETA func */ uint8 m_flags; /** Position of the item in the variable table. */ uint16 m_itemPos; /** ID of a symbol in its module table. */ uint32 m_id; /** Line at which a symbol is declared */ int32 m_lineDecl; /** Symbol name; actually existing in its module definition, so not allocated nor deallocated. */ String m_name; /** Module where the symbol resides. */ Module *m_module; union { FuncDef *v_func; ExtFuncDef *v_extfunc; ImportAlias *v_importalias; ClassDef *v_class; VarDef *v_prop; Symbol *v_symbol; } m_value; void clear(); public: /** Convenience constructor. This creates the symbol with minimal setup \param mod the owner module \param id the id of the symbol in the module \param name the name of the symbol \param exp true if this symbol is exported. */ Symbol( Module *mod, uint32 id, const String &name, bool exp ): m_type( tundef ), m_flags(exp ? 1: 0), m_id( id ), m_lineDecl(0), m_name( name ), m_module(mod) {} /** Builds a symbol without ID and export class. The symbol is by default not exported, and it's id is left to a non-meaningful value. This constructor is meant to be used only on symbols that are going to be added somehow to a module soon after. The type of the symbol is Symbol::tundef. \param mod the owner module \param name a String that has been created and stored somewhere safe (i.e. module string table). */ Symbol( Module *mod, const String &name ): m_type( tundef ), m_flags( 0 ), m_id(0), m_lineDecl(0), m_name( name ), m_module( mod ) {} /** Basic empty constructor. This is mainly used for de-serialization to prepare the symbols before it can be de-serialized. \param owner the module that is owning this symbol \note by default, the symbol is not exported. */ Symbol( Module *owner ): m_type( tundef ), m_flags( 0 ), m_id( 0 ), m_lineDecl(0), m_module( owner ) {} ~Symbol() { clear(); } /** Changes the symbol name. Symbol names are never destroyed, so the old name is not de-allocated. If necessary, the application must keep track of that. \param name The new name. */ void name( const String &name ) { m_name = name; } /** Changes the symbol id. \param i the new id. */ void id( uint32 i ) { m_id = i; } /** Sets the symbol export class. \param exp true if the symbol must be exported, false otherwise. \return itself */ Symbol* exported( bool exp ) { if ( exp ) m_flags |= FLAG_EXPORTED; else m_flags &=~FLAG_EXPORTED; return this; } /** Sets the symbol import class. Import class has higher priority than export class; that is, if a symbol is imported exported() will always return false. Also, imported symbols report their type as unknown, no matter what local setting is provided. \param exp true if the symbol must be imported, false otherwise. \return itself */ Symbol* imported( bool exp ) { if ( exp ) m_flags |= FLAG_IMPORTED; else m_flags &=~FLAG_IMPORTED; return this; } /** Declares the symbol as an "eta function". Eta functions are self-managed functions in Sigma-evaluation (functional evaluation). \param exp true if the symbol is an ETA function, false otherwise. \return itself */ Symbol* setEta( bool exp ) { if ( exp ) m_flags |= FLAG_ETAFUNC; else m_flags &=~FLAG_ETAFUNC; return this; } /** Declares the symbol as an "well known symbol". Normal symbols are generated in a module, and eventually exported to the global namespace of the VM. Well known symbols live in a special space in the VM; they are always referenced and the module declaring them receives a copy of the original item, but not the original one. Modules can i.e. change objects and can alter functions, but a copy of the original well known items is kept by the VM and is available to C++ extensions. Well known items are meant to provide language-oriented special features, or to provide special hooks for modules. Error, TimeStamp and other language relevant classes are WKS (well known symbol). Modules can declare new well known symbol to i.e. declare new classes and provide C++ factory functions. \param exp true if the symbol is a Well Known Symbol. \return itself */ Symbol* setWKS( bool exp ) { if ( exp ) m_flags |= FLAG_WELLKNOWN; else m_flags &=~FLAG_WELLKNOWN; return this; } /** Declares the symbol as an "enum class". Enum classes are classes that only store constant values, or serve otherwise as namespaces. They cannot be instantiated. \param exp true if the symbol is an enumeration. \return itself */ Symbol* setEnum( bool exp ) { if ( exp ) m_flags |= FLAG_ENUM; else m_flags &=~FLAG_ENUM; return this; } void setUndefined() { clear(); m_type = tundef; } void setLocalUndef() { clear(); m_type = tlocalundef; } void setGlobal() { clear(); m_type = tglobal; } void setLocal() { clear(); m_type = tlocal; } void setParam() { clear(); m_type = tparam; } void setFunction( FuncDef *func ) { clear(); m_type = tfunc; m_value.v_func = func; } void setExtFunc( ExtFuncDef *f ) { clear(); m_type = textfunc; m_value.v_extfunc = f; } void setClass( ClassDef *f ) { clear(); m_type = tclass; m_value.v_class = f; } void setProp( VarDef *p ) { clear(); m_type = tprop; m_value.v_prop = p; } void setVar( VarDef *p ) { clear(); m_type = tvar; m_value.v_prop = p; } void setConst( VarDef *p ) { clear(); m_type = tconst; m_value.v_prop = p; } void setInstance( Symbol *base_class ) { clear(); m_type = tinst; m_value.v_symbol = base_class; } void setImportAlias( ImportAlias* alias ) { clear(); m_type = timportalias; m_value.v_importalias = alias; imported(true); } void setImportAlias( const String &name, const String& origModule, bool bIsFileName = false ) { setImportAlias( new ImportAlias( name, origModule, bIsFileName ) ); } const String &name() const { return m_name; } // TODO: Remove this String &name() { return m_name; } uint32 id() const { return m_id; } type_t type() const { return m_type; } bool exported() const { return (! imported()) && ((m_flags & FLAG_EXPORTED) == FLAG_EXPORTED); } bool imported() const { return (m_flags & FLAG_IMPORTED) == FLAG_IMPORTED; } uint16 itemId() const { return m_itemPos; } void itemId( uint16 ip ) { m_itemPos = ip; } bool isEta() const { return (m_flags & FLAG_ETAFUNC) == FLAG_ETAFUNC; } bool isWKS() const { return (m_flags & FLAG_WELLKNOWN) == FLAG_WELLKNOWN; } bool isEnum() const { return (m_flags & FLAG_ENUM) == FLAG_ENUM; } bool isUndefined() const { return imported() || m_type == tundef; } bool isLocalUndef() const { return m_type == tlocalundef; } bool isGlobal() const { return m_type == tglobal; } bool isLocal() const { return m_type == tlocal; } bool isParam() const { return m_type == tparam; } bool isFunction() const { return m_type == tfunc; } bool isExtFunc() const { return m_type == textfunc; } bool isClass() const { return m_type == tclass; } bool isProp() const { return m_type == tprop; } bool isVar() const { return m_type == tvar; } bool isCallable() const { return isFunction() || isExtFunc() || isClass(); } bool isInstance() const { return m_type == tinst; } bool isConst() const { return m_type == tconst; } bool isImportAlias() const { return m_type == timportalias; } /** Candy grammar to add a parameter to a function (internal or external) */ Symbol* addParam( const String ¶m ); FuncDef *getFuncDef() const { return m_value.v_func; } ExtFuncDef *getExtFuncDef() const { return m_value.v_extfunc; } ClassDef *getClassDef() const { return m_value.v_class; } VarDef *getVarDef() const { return m_value.v_prop; } Symbol *getInstance() const { return m_value.v_symbol; } ImportAlias* getImportAlias() const { return m_value.v_importalias; } uint16 getItemId() const { return m_itemPos; } /** If the symbol is a class, check if this class is the named one or derived from the named one. If the name passed as parameters is the name of this class or one of its ancestor names, the function returns true. \param find_name The class name. */ bool fromClass( const String &find_name ) const; bool save( Stream *out ) const; bool load( Stream *in ); /** Returns the module associated with a symbol. \return the owner module or zero if the information is not available. */ const Module *module() const { return m_module; } void module( Module *mod ) { m_module = mod; } void declaredAt( int32 line ) { m_lineDecl = line; } int32 declaredAt() const { return m_lineDecl; } }; /** maybe not elegant, but a safe way to store the kind of pointer. */ #define FLC_CLSYM_VAR ((Falcon::Symbol *) 0) #define FLC_CLSYM_METHOD ((Falcon::Symbol *) 1) #define FLC_CLSYM_BASE ((Falcon::Symbol *) 2) } #endif /* end of flc_symbol.h */ include/falcon/symlist.h000066400000000000000000000013351176363201700156300ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: symlist.h Declaration of a list of symbols. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun ott 3 2005 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Declaration of a list of symbols. */ #ifndef flc_symlist_H #define flc_symlist_H #include namespace Falcon { class FALCON_DYN_CLASS SymbolList: public List { public: SymbolList() {} SymbolList( const SymbolList &other ); }; } #endif /* end of symlist.h */ include/falcon/symtab.h000066400000000000000000000071671176363201700154340ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_symtab.h Symbol table definition ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar ago 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_SYMBOLTABLE_H #define FLC_SYMBOLTABLE_H #include #include #include #include #include #include namespace Falcon { class Symbol; class Stream; class Module; class FALCON_DYN_CLASS SymbolTable: public BaseAlloc { /** Internal symbol map. (const String *, Symbol * ) */ Map m_map; public: /** Constructs the symbol table. The symbol table is built with owndership of the symbols. */ SymbolTable(); /** Adds a symbol. The name of the symbol is used as the key in the symbol table. \param sym the symbol to be added. \return true if the symbol can be added, false if it were already present. */ bool add( Symbol *sym ); /** Seek a symbol given its name. If the symbol with the given name can't be found in the symbol table, the function returns null. \note it is possible also to call this function with static C strings from code. \param name the name of the symbol to be found. \return a pointer to the symbol if it's found, or 0 otherwise. */ Symbol *findByName( const String &name ) const { Symbol **ptrsym = (Symbol **) m_map.find( &name ); if ( ptrsym == 0 ) return 0; return *ptrsym; } /** Exports all the undefined symbols. Used by the compiler if the module being compiled asked for complete export. */ void exportUndefined(); /** Remove a symbol given it's name. If the SymbolTable has the ownership of the symbols, then the symbol is destroyed. The symbol name is never destroyed though. \param name the name of the symbol to be destroyed. \return true if the name can be found, false otherwise. */ bool remove( const String &name ); /** Return the number of symbols stored in this table. \return the number of symbols stored in this table. */ int size() const { return m_map.size(); } /** Save the symbol table on a stream. The serialization of the table involves only saving the ID of the strings and of the symbols that are in the table. \param out the stream where the table must be saved. \return true if the operation has success, false otherwise. */ bool save( Stream *out ) const; /** Load the symbol table from a stream. If the symbol table owns the symbols, then the symbols are serialized on the stream too; otherwise, only the ID are saved. The deserialization may return false if the function detects some problem, i.e. an invald format. \param owner the module for which the symbols are created. \param in the input stream where the table must be loaded from. \return true if the operation has success, false otherwise. */ bool load( const Module *owner, Stream *in ); const Map &map() const { return m_map; } }; class FALCON_DYN_CLASS SymbolVector: public GenericVector { public: SymbolVector(); ~SymbolVector(); Symbol *symbolAt( uint32 pos ) const { return *(Symbol **) at( pos ); } bool save( Stream *out ) const; bool load( Module *owner, Stream *in ); }; } #endif /* end of flc_symtab.h */ include/falcon/syntree.h000066400000000000000000000760441176363201700156260ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: syntree.h Syntactic tree item definitions. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven giu 4 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_SYNTREE_H #define FALCON_SYNTREE_H #include #include #include #include #include #include #include #include namespace Falcon { class Value; class Expression; class Compiler; class Map; /** Class storing array declarations in source code. This class records the content of [ v1, v2 .. vn ] declarations in source code; it's basically a Value * Hlist with ownership. **/ class FALCON_DYN_CLASS ArrayDecl: public List { public: ArrayDecl(); ArrayDecl( const ArrayDecl &other ); ArrayDecl *clone() const { return new ArrayDecl( *this ); } }; class FALCON_DYN_CLASS DictDecl: public List { public: typedef struct t_pair { Value *first; Value *second; } pair; DictDecl(); DictDecl( const DictDecl &other ); void pushBack( Value *first, Value *second ); DictDecl *clone() const { return new DictDecl( *this ); } }; class FALCON_DYN_CLASS RangeDecl: public BaseAlloc { Value *m_rstart; Value *m_rend; Value *m_step; public: RangeDecl( Value *start, Value *end = 0, Value *step = 0 ): m_rstart( start ), m_rend( end ), m_step( step ) {} RangeDecl( const RangeDecl &other ); ~RangeDecl(); bool isOpen() const { return m_rend == 0; } Value *rangeStart() const { return m_rstart; } Value *rangeEnd() const { return m_rend; } Value *rangeStep() const { return m_step; } RangeDecl *clone() const { return new RangeDecl( *this ); } }; class FALCON_DYN_CLASS Value: public BaseAlloc { public: typedef enum { t_nil, t_imm_bool, t_imm_integer, t_imm_string, t_imm_num, t_symbol, t_symdef, t_self, t_fself, t_lbind, t_byref, t_array_decl, t_dict_decl, t_range_decl, t_unbound, t_expression } type_t; private: type_t m_type; union { bool asBool; int64 asInteger; numeric asNumeric; String *asString; Symbol *asSymbol; ArrayDecl *asArray; DictDecl *asDict; RangeDecl *asRange; Expression *asExpr; Value *asRef; } m_content; public: Value(): m_type( t_nil ) {} Value( const Value &other ) { copy( other ); } explicit Value( bool val ): m_type( t_imm_bool ) { m_content.asBool = val; } explicit Value( int64 val ): m_type( t_imm_integer ) { m_content.asInteger = val; } explicit Value( numeric val ): m_type( t_imm_num ) { m_content.asNumeric = val; } Value( String *val ): m_type( t_imm_string ) { m_content.asString = val; } Value( Symbol *val ): m_type( t_symbol ) { m_content.asSymbol = val; } Value( Expression *val ): m_type( t_expression ) { m_content.asExpr = val; } Value( ArrayDecl *val ): m_type( t_array_decl ) { m_content.asArray = val; } Value( DictDecl *val ): m_type( t_dict_decl ) { m_content.asDict = val; } Value( RangeDecl *val ): m_type( t_range_decl ) { m_content.asRange = val; } Value( Value *val ): m_type( t_byref ) { m_content.asRef = val; } ~Value(); /** Copies the value. */ void copy( const Value &other ); /** Clone constructor. */ Value *clone() const; /** Transfers the value of the original to this instance. The original type is set to nil, so that this instance remains the sole owner of the deep value data. */ void transfer( Value &other ) { m_type = other.m_type; m_content = other.m_content; other.m_type = t_nil; } type_t type() const { return m_type; } /** Creates a new Falcon::VarDef using the contents of this object. The Value() object is specific for the compiler; the VM and the module system does not know it. At times, it is needed to save in the module some data that are stored in the Falcon::Value; this is an utility function that does the job. If the value is not simple, i.e. it's an expression or a statement, it cannot be converted and the function won't create a VarDef. Also, this version won't create a VarDef for symbols; use genVarDefSym() if this is required. \return a newly allocated prodef that has the same contents a the value, or 0 for complex values. */ VarDef *genVarDef(); bool isImmediate() const { return m_type == t_nil || m_type == t_unbound || m_type == t_imm_bool || m_type == t_imm_integer || m_type == t_imm_string || m_type == t_imm_num; } bool isSimple() const { return isImmediate() || m_type == t_symbol || m_type == t_symdef || m_type == t_lbind || m_type == t_self || m_type == t_fself; } bool isTrue() const { switch( m_type ) { case t_imm_integer: return asInteger() != 0; case t_imm_num: return asNumeric() != 0.0; case t_imm_string: return asString()->size() != 0; default: return false; } } bool asBool() const { return m_content.asBool; } int64 asInteger() const { return m_content.asInteger; } numeric asNumeric() const { return m_content.asNumeric; } String *asString() const { return m_content.asString; } String *asSymdef() const { return m_content.asString; } String *asLBind() const { return m_content.asString; } Symbol *asSymbol() const { return m_content.asSymbol; } Value *asReference() const { return m_content.asRef; } ArrayDecl *asArray() const { return m_content.asArray; } DictDecl *asDict() const { return m_content.asDict; } RangeDecl *asRange() const { return m_content.asRange; } Expression *asExpr() const { return m_content.asExpr; } void setNil() { m_type = t_nil; } void setUnbound() { m_type = t_unbound; } void setBool( bool val ) { m_type = t_imm_bool; m_content.asBool = val; } void setInteger( int64 val ) { m_type = t_imm_integer; m_content.asInteger = val; } void setNumeric( numeric val ) { m_type = t_imm_num; m_content.asNumeric = val; } void setString( String *val ) { m_type = t_imm_string; m_content.asString = val; } void setSymdef( String *val ) { m_type = t_symdef; m_content.asString = val; } void setSymbol( Symbol *val ) { m_type = t_symbol; m_content.asSymbol = val; } void setReference( Value *val ) { m_type = t_byref; m_content.asRef = val; } void setExpr( Expression *val ) { m_type = t_expression; m_content.asExpr = val; } void setArray( ArrayDecl *val ) { m_type = t_array_decl; m_content.asArray = val; } void setDict( DictDecl *val ) { m_type = t_dict_decl; m_content.asDict = val; } void setRange( RangeDecl *val ) { m_type = t_range_decl; m_content.asRange = val; } void setSelf() { m_type = t_self; } void setFself() { m_type = t_fself; } void setLBind( String *val ) { m_type = t_lbind; m_content.asString = val; } bool isNil() const { return m_type == t_nil; } bool isUnbound() const { return m_type == t_unbound; } bool isBool() const { return m_type == t_imm_bool; } bool isInteger() const { return m_type == t_imm_integer; } bool isNumeric() const { return m_type == t_imm_num; } bool isString() const { return m_type == t_imm_string; } bool isSymdef() const { return m_type == t_symdef; } bool isSymbol() const { return m_type == t_symbol; } bool isReference() const { return m_type == t_byref; } bool isExpr() const { return m_type == t_expression; } bool isArray() const { return m_type == t_array_decl; } bool isDict() const { return m_type == t_dict_decl; } bool isSelf() const { return m_type == t_self; } bool isRange() const { return m_type == t_range_decl; } bool isLBind() const { return m_type == t_lbind; } Value &operator =( const Value &other ) { copy( other ); return *this; } /** Verifies if another Falcon::Value is internally greater or equal than this one. Works for nil, immediates, ranges and symbols. The ordering between type is nil less than interger less than range less than string less than symbol. \param val the other value to be compared to this one. \return true if the type of the element is the same, and this item is less than val. */ bool less( const Value &val ) const; bool operator <( const Value &val ) const { return less( val ); } /** Verifies if another Falcon::Value is internally equal to this one. Works for nil, immediates and symbols. If the contents of the parameter are the same as those of this obeject, returns true, else false. \param other the other value to be compared to this one. \return true if the type and content of the other element are the same as this. */ bool isEqualByValue( const Value &other ) const; bool operator==( const Value &other ) const { return isEqualByValue( other ); } bool operator!=( const Value &other ) const { return !isEqualByValue( other ); } bool operator >=( const Value &other ) const { return ! less( other ); } bool operator <=( const Value &other ) const { return less( other ) || isEqualByValue( other ); } bool operator >( const Value &other) const { return ! ( less( other ) || isEqualByValue( other ) ); } }; class FALCON_DYN_CLASS ValuePtrTraits: public VoidpTraits { public: virtual void copy( void *targetZone, const void *sourceZone ) const; virtual int compare( const void *first, const void *second ) const; virtual void destroy( void *item ) const; virtual bool owning() const; }; namespace traits { extern ValuePtrTraits &t_valueptr(); } class FALCON_DYN_CLASS Expression: public BaseAlloc { public: typedef enum { t_none, t_neg, t_bin_not, t_not, t_bin_and, t_bin_or, t_bin_xor, t_shift_left, t_shift_right, t_and, t_or, t_plus, t_minus, t_times, t_divide, t_modulo, t_power, t_pre_inc, t_post_inc, t_pre_dec, t_post_dec, t_gt, t_ge, t_lt, t_le, t_eq, t_exeq, t_neq, t_has, t_hasnt, t_in, t_notin, t_provides, t_iif, t_lambda, t_obj_access, t_funcall, t_inherit, t_array_access, t_array_byte_access, t_strexpand, t_indirect, t_assign, t_fbind, t_aadd, t_asub, t_amul, t_adiv, t_amod, t_apow, t_aband, t_abor, t_abxor, t_ashl, t_ashr, t_eval, t_deoob, t_oob, t_xoroob, t_isoob, /** An optimized expression is like an unary operator */ t_optimized } operator_t; private: operator_t m_operator; Value *m_first; Value *m_second; Value *m_third; public: Expression( operator_t t, Value *first, Value *second = 0, Value *third = 0 ): m_operator( t ), m_first( first ), m_second( second ), m_third( third ) {} Expression( const Expression &other ); ~Expression(); operator_t type() const { return m_operator; } Value *first() const { return m_first; } void first( Value *f ) { delete m_first; m_first= f; } Value *second() const { return m_second; } void second( Value *s ) { delete m_second; m_second = s; } Value *third() const { return m_third; } void third( Value *t ) { delete m_third; m_third = t; } bool isStandAlone() const; bool isBinaryOperator() const { switch( m_operator ) { case t_bin_and: case t_bin_or: case t_bin_xor: case t_shift_left: case t_shift_right: case t_and: case t_or: case t_plus: case t_minus: case t_times: case t_divide: case t_modulo: case t_power: case t_gt: case t_ge: case t_lt: case t_le: case t_eq: case t_neq: case t_exeq: case t_has: case t_hasnt: case t_in: case t_notin: case t_provides: return true; default: return false; } // returning from switch } }; //================================================================= // Statements below this line //================================================================= class FALCON_DYN_CLASS Statement: public SLElement { public: typedef enum { t_none, t_break, t_continue, t_launch, t_autoexp, t_return, t_attributes, t_raise, t_give, t_unref, t_if, t_elif, t_while, t_loop, t_forin, t_try, t_catch, t_switch, t_select, t_case, t_module, t_global, t_class, t_state, t_function, t_propdef, t_fordot, t_self_print } type_t; protected: type_t m_type; uint32 m_line; Statement( type_t t ): m_type(t), m_line(0) {} Statement( int32 l, type_t t ): m_type(t), m_line(l) {} public: Statement( const Statement &other ); virtual ~Statement() {}; type_t type() const { return m_type; } uint32 line() const { return m_line; } void line( uint32 l ) { m_line = l; } virtual Statement *clone() const =0; }; /** Typed strong list holding statements. */ class FALCON_DYN_CLASS StatementList: public StrongList { public: StatementList() {} StatementList( const StatementList &other ); ~StatementList(); Statement *front() const { return static_cast< Statement *>( StrongList::front() ); } Statement *back() const { return static_cast< Statement *>( StrongList::back() ); } Statement *pop_front() { return static_cast< Statement *>( StrongList::pop_front() ); } Statement *pop_back() { return static_cast< Statement *>( StrongList::pop_back() ); } }; class FALCON_DYN_CLASS StmtNone: public Statement { public: StmtNone( int32 l ): Statement( l, t_none ) {} StmtNone( const StmtNone &other ): Statement( other ) {} StmtNone *clone() const; }; class FALCON_DYN_CLASS StmtGlobal: public Statement { SymbolList m_symbols; public: StmtGlobal(int line): Statement( line, t_global) {} StmtGlobal( const StmtGlobal &other ); void addSymbol( Symbol *sym ) { m_symbols.pushBack( sym ); } SymbolList &getSymbols() { return m_symbols; } const SymbolList &getSymbols() const { return m_symbols; } virtual StmtGlobal *clone() const; }; class FALCON_DYN_CLASS StmtUnref: public Statement { Value *m_symbol; public: StmtUnref( int line, Value *sym ): Statement( line, t_unref ), m_symbol( sym ) {} StmtUnref( const StmtUnref &other ); ~StmtUnref(); Value *symbol() const { return m_symbol; } virtual StmtUnref *clone() const; }; class FALCON_DYN_CLASS StmtSelfPrint: public Statement { ArrayDecl *m_toPrint; public: StmtSelfPrint( uint32 line, ArrayDecl *toPrint ): Statement( line, t_self_print ), m_toPrint( toPrint ) {} StmtSelfPrint( const StmtSelfPrint &other ); ~StmtSelfPrint(); virtual StmtSelfPrint *clone() const; ArrayDecl *toPrint() const { return m_toPrint; } }; class FALCON_DYN_CLASS StmtExpression: public Statement { Value *m_expr; public: StmtExpression( uint32 line, type_t t, Value *exp ): Statement( line, t ), m_expr( exp ) {} StmtExpression( const StmtExpression &other ); virtual ~StmtExpression(); Value *value() const { return m_expr; } virtual StmtExpression *clone() const; }; class FALCON_DYN_CLASS StmtFordot: public StmtExpression { public: StmtFordot( uint32 line, Value *exp ): StmtExpression( line, t_fordot, exp ) {} StmtFordot( const StmtFordot &other ); virtual StmtFordot *clone() const; }; class FALCON_DYN_CLASS StmtAutoexpr: public StmtExpression { public: StmtAutoexpr( uint32 line, Value *exp ): StmtExpression( line, t_autoexp, exp ) {} StmtAutoexpr( const StmtAutoexpr &other ); virtual StmtAutoexpr *clone() const; }; class FALCON_DYN_CLASS StmtReturn: public StmtExpression { public: StmtReturn( uint32 line, Value *exp ): StmtExpression( line, t_return, exp ) {} StmtReturn( const StmtReturn &other ): StmtExpression( other ) {} virtual StmtReturn *clone() const; }; class FALCON_DYN_CLASS StmtLaunch: public StmtExpression { public: StmtLaunch( uint32 line, Value *exp ): StmtExpression( line, t_launch, exp ) {} StmtLaunch( const StmtLaunch &other ): StmtExpression( other ) {} virtual StmtLaunch *clone() const; }; class FALCON_DYN_CLASS StmtRaise: public StmtExpression { public: StmtRaise( uint32 line, Value *exp ): StmtExpression( line, t_raise, exp ) {} StmtRaise( const StmtRaise &other ): StmtExpression( other ) {} virtual StmtRaise *clone() const; }; class FALCON_DYN_CLASS StmtGive: public Statement { ArrayDecl *m_objects; ArrayDecl *m_attribs; public: StmtGive( uint32 line, ArrayDecl *objects, ArrayDecl *attribs ): Statement( line, t_give ), m_objects( objects ), m_attribs( attribs ) {} StmtGive( const StmtGive &other ); virtual ~StmtGive(); ArrayDecl *objects() const { return m_objects; } ArrayDecl *attributes() const { return m_attribs; } virtual StmtGive *clone() const; }; /** Loop control statements (break and continue) */ class FALCON_DYN_CLASS StmtLoopCtl: public Statement { public: StmtLoopCtl( uint32 line, type_t t ): Statement( line, t ) {} StmtLoopCtl( const StmtLoopCtl &other ); // pure virtual, no clone. }; class FALCON_DYN_CLASS StmtBreak: public StmtLoopCtl { public: StmtBreak( uint32 line ): StmtLoopCtl( line, t_break ) {} StmtBreak( const StmtBreak &other ): StmtLoopCtl( other ) {} virtual StmtBreak *clone() const; }; class FALCON_DYN_CLASS StmtContinue: public StmtLoopCtl { bool m_dropping; public: StmtContinue( uint32 line, bool dropping = false ): StmtLoopCtl( line, t_continue ), m_dropping( dropping ) {} StmtContinue( const StmtContinue &other ): StmtLoopCtl( other ) {} bool dropping() const { return m_dropping; } virtual StmtContinue *clone() const; }; class FALCON_DYN_CLASS StmtBlock: public Statement { StatementList m_list; public: StmtBlock( uint32 line, type_t t ): Statement( line, t ) {} StmtBlock( const StmtBlock &other ); const StatementList &children() const { return m_list; } StatementList &children() { return m_list; } // pure virtual, no clone }; class FALCON_DYN_CLASS StmtConditional: public StmtBlock { protected: Value *m_condition; public: StmtConditional( uint32 line, type_t t, Value *cond ): StmtBlock( line, t ), m_condition( cond ) {} StmtConditional( const StmtConditional &other ); virtual ~StmtConditional(); Value *condition() const { return m_condition; } // pure virtual }; class FALCON_DYN_CLASS StmtLoop: public StmtConditional { public: StmtLoop( uint32 line, Value *cond = 0): StmtConditional( line, t_loop, cond ) {} StmtLoop( const StmtLoop &other ): StmtConditional( other ) {} virtual StmtLoop *clone() const; void setCondition( Value *cond ) { m_condition = cond; } }; class FALCON_DYN_CLASS StmtWhile: public StmtConditional { public: StmtWhile( uint32 line, Value *cond ): StmtConditional( line, t_while, cond ) {} StmtWhile( const StmtWhile &other ): StmtConditional( other ) {} virtual StmtWhile *clone() const; }; class FALCON_DYN_CLASS StmtElif: public StmtConditional { public: StmtElif( uint32 line, Value *cond ): StmtConditional( line, t_elif, cond ) {} StmtElif( const StmtElif &other ): StmtConditional( other ) {} virtual StmtElif *clone() const; }; class FALCON_DYN_CLASS StmtIf: public StmtConditional { StatementList m_else; StatementList m_elseifs; public: StmtIf( uint32 line, Value *cond ): StmtConditional( line, t_if, cond ) {} StmtIf( const StmtIf &other ); const StatementList &elseChildren() const { return m_else; } StatementList &elseChildren() { return m_else; } const StatementList &elifChildren() const { return m_elseifs; } StatementList &elifChildren() { return m_elseifs; } virtual StmtIf *clone() const; }; class FALCON_DYN_CLASS StmtForin: public StmtBlock { StatementList m_first; StatementList m_last; StatementList m_middle; Value *m_source; ArrayDecl *m_dest; public: StmtForin( uint32 line, ArrayDecl *dest, Value *source ): StmtBlock( line, t_forin ), m_source( source ), m_dest( dest ) {} StmtForin( const StmtForin &other ); virtual ~StmtForin(); const StatementList &firstBlock() const { return m_first; } StatementList &firstBlock() { return m_first; } const StatementList &lastBlock() const { return m_last; } StatementList &lastBlock() { return m_last; } const StatementList &middleBlock() const { return m_middle; } StatementList &middleBlock() { return m_middle; } Value *source() const { return m_source; } ArrayDecl *dest() const { return m_dest; } virtual StmtForin *clone() const; }; class FALCON_DYN_CLASS StmtCaseBlock: public StmtBlock { public: StmtCaseBlock( uint32 line ): StmtBlock( line, t_case ) {} StmtCaseBlock( const StmtCaseBlock &other ): StmtBlock( other ) {} virtual StmtCaseBlock *clone() const; }; /** Statement switch. */ class FALCON_DYN_CLASS StmtSwitch: public Statement { /** Maps of Value *, uint32 */ Map m_cases_int; Map m_cases_rng; Map m_cases_str; Map m_cases_obj; /** We store the objects also in a list to keep track of their declaration order */ List m_obj_list; StatementList m_blocks; StatementList m_defaultBlock; int32 m_nilBlock; Value *m_cfr; public: StmtSwitch( uint32 line, Value *expr ); StmtSwitch( const StmtSwitch &other ); virtual ~StmtSwitch(); const Map &intCases() const { return m_cases_int; } const Map &rngCases() const { return m_cases_rng; } const Map &strCases() const { return m_cases_str; } const Map &objCases() const { return m_cases_obj; } const List &objList() const { return m_obj_list; } const StatementList &blocks() const { return m_blocks; } void addBlock( StmtCaseBlock *sl ); Map &intCases() { return m_cases_int; } Map &rngCases() { return m_cases_rng; } Map &strCases() { return m_cases_str; } Map &objCases() { return m_cases_obj; } List &objList() { return m_obj_list; } StatementList &blocks() { return m_blocks; } int32 nilBlock() const { return m_nilBlock; } void nilBlock( int32 v ) { m_nilBlock = v; } const StatementList &defaultBlock() const { return m_defaultBlock; } StatementList &defaultBlock() { return m_defaultBlock; } Value *switchItem() const { return m_cfr; } bool addIntCase( Value *itm ); bool addStringCase( Value *itm ); bool addRangeCase( Value *itm ); bool addSymbolCase( Value *itm ); int currentBlock() const { return m_blocks.size(); } virtual StmtSwitch *clone() const; }; /** Statement select. */ class FALCON_DYN_CLASS StmtSelect: public StmtSwitch { public: StmtSelect( uint32 line, Value *expr ); StmtSelect( const StmtSelect &other ); virtual StmtSelect *clone() const; }; class FALCON_DYN_CLASS StmtCatchBlock: public StmtBlock { Value *m_into; public: StmtCatchBlock( uint32 line, Value *into = 0 ): StmtBlock( line, t_catch ), m_into( into ) {} StmtCatchBlock( const StmtCatchBlock &other ); ~StmtCatchBlock(); Value *intoValue() const { return m_into; } virtual StmtCatchBlock *clone() const; }; class FALCON_DYN_CLASS StmtTry: public StmtBlock { Map m_cases_int; Map m_cases_sym; /** Objects must be stored in the order they are presented. */ List m_sym_list; /** Handlers for non-default branches */ StatementList m_handlers; /** Also catcher-into must be listed. */ List m_into_values; /** Default block. */ StmtCatchBlock *m_default; public: StmtTry( uint32 line ); StmtTry( const StmtTry &other ); ~StmtTry(); const StmtCatchBlock *defaultHandler() const { return m_default; } StmtCatchBlock *defaultHandler() { return m_default; } void defaultHandler( StmtCatchBlock *block ); bool defaultGiven() const { return m_default != 0; } const StatementList &handlers() const { return m_handlers; } StatementList &handlers() { return m_handlers; } const Map &intCases() const { return m_cases_int; } const Map &objCases() const { return m_cases_sym; } const List &objList() const { return m_sym_list; } void addHandler( StmtCatchBlock *block ); bool addIntCase( Value *itm ); bool addSymbolCase( Value *itm ); int currentBlock() const { return m_handlers.size(); } virtual StmtTry *clone() const; }; class FALCON_DYN_CLASS StmtCallable: public Statement { Symbol *m_name; public: StmtCallable( uint32 line, type_t t, Symbol *name ): Statement( line, t ), m_name( name ) {} StmtCallable( const StmtCallable &other ): Statement( other ), m_name( other.m_name ) {} virtual ~StmtCallable(); Symbol *symbol() { return m_name; } const Symbol *symbol() const { return m_name; } const String &name() const { return m_name->name(); } // pure virtual, no clone }; class StmtFunction; class StmtState; class FALCON_DYN_CLASS StmtClass: public StmtCallable { StmtFunction *m_ctor; bool m_initGiven; /** if this class is a clone it must delete it's constructor statement. */ bool m_bDeleteCtor; Symbol *m_singleton; /** set of expressions (values, usually inherit calls) to be prepended to the constructor */ ArrayDecl m_initExpressions; /** Init state */ StmtState *m_initState; StatementList m_states; public: StmtClass( uint32 line, Symbol *name ): StmtCallable( line, t_class, name ), m_ctor(0), m_initGiven( false ), m_bDeleteCtor( false ), m_singleton(0), m_initState(0) {} StmtClass( const StmtClass &other ); virtual ~StmtClass(); /** Function data that is used as a constructor. As properties may be initialized "randomly", we need a simple way to access the statements that will be generated in the constructor for this class. The function returned is a normal function that is found in the module function tree. */ StmtFunction *ctorFunction() const { return m_ctor; } void ctorFunction( StmtFunction *func ) { m_ctor = func; } bool initGiven() const { return m_initGiven; } void initGiven( bool val ) { m_initGiven = val; } void addInitExpression( Value *expr ) { m_initExpressions.pushBack( expr ); } const ArrayDecl& initExpressions() const { return m_initExpressions; } ArrayDecl& initExpressions() { return m_initExpressions; } /** Singleton associated to this class, if any. */ Symbol *singleton() const { return m_singleton; } void singleton( Symbol *s ) { m_singleton = s; } virtual StmtClass *clone() const; /** Return the Statement declaring the init state of this class */ StmtState* initState() const { return m_initState; } /** Sets the init state of this class. */ void initState( StmtState* m ) { m_initState = m; } /** Return the Statement declaring the init state of this class */ bool addState( StmtState* m_state ); }; class FALCON_DYN_CLASS StmtFunction: public StmtCallable { private: int m_lambda_id; StatementList m_staticBlock; StatementList m_statements; const StmtClass *m_ctor_for; public: StmtFunction( uint32 line, Symbol *name ): StmtCallable( line, t_function, name ), m_lambda_id(0), m_ctor_for(0) {} StmtFunction( const StmtFunction &other ); StatementList &statements() { return m_statements; } const StatementList &statements() const { return m_statements; } StatementList &staticBlock() { return m_staticBlock; } const StatementList &staticBlock() const { return m_staticBlock; } bool hasStatic() const { return !m_staticBlock.empty(); } void setLambda( int id ) { m_lambda_id = id; } int lambdaId() const { return m_lambda_id; } bool isLambda() const { return m_lambda_id != 0; } void setConstructorFor( const StmtClass *cd ) { m_ctor_for = cd; } const StmtClass *constructorFor() const { return m_ctor_for; } virtual StmtFunction *clone() const; }; class StmtState: public Statement { const String* m_name; StmtClass* m_owner; Map m_funcs; StateDef* m_stateDef; public: StmtState( const String* name, StmtClass* owner ); StmtState( const StmtState& other ); virtual ~StmtState(); virtual StmtState* clone() const; /** Functions subscribed to this state, ordered by alias. */ const Map& functions() const { return m_funcs; } /** Functions subscribed to this state, ordered by alias. */ Map& functions() { return m_funcs; } /** Just a shortcut to insertion in the map. * Returns false if the map exists. */ bool addFunction( const String* name, Symbol* func ); const String* name() const { return m_name; } StmtClass* owner() const { return m_owner; } StateDef* state() const { return m_stateDef; } }; class FALCON_DYN_CLASS StmtVarDef: public Statement { String *m_name; Value *m_value; public: StmtVarDef( uint32 line, String *name, Value *value ): Statement( line, t_propdef ), m_name( name ), m_value( value ) {} StmtVarDef( const StmtVarDef &other ); virtual ~StmtVarDef(); String *name() const { return m_name; } Value *value() const { return m_value; } virtual StmtVarDef *clone() const; }; /** Source File syntactic tree. This tree represent a compiled program. Together with the module being created by the compiler during the compilation step, which contains the string table and the symbol table, this defines the internal representation of a script */ class FALCON_DYN_CLASS SourceTree: public BaseAlloc { StatementList m_statements; StatementList m_functions; StatementList m_classes; bool m_exportAll; public: SourceTree(): m_exportAll( false ) {} SourceTree( const SourceTree &other ); const StatementList &statements() const { return m_statements; } StatementList &statements() { return m_statements; } const StatementList &functions() const { return m_functions; } StatementList &functions() { return m_functions; } const StatementList &classes() const { return m_classes; } StatementList &classes() { return m_classes; } void setExportAll( bool mode = true ) { m_exportAll = mode; } bool isExportAll() const { return m_exportAll; } SourceTree *clone() const; }; } #endif /* end of syntree.h */ include/falcon/sys.h000066400000000000000000000065711176363201700147510ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_sys.h System related services. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar nov 9 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file System related services. */ #ifndef flc_flc_sys_H #define flc_flc_sys_H #include #include #include namespace Falcon { class String; namespace Sys { /** Gives current second count from Epoch. The number of seconds is generally returned in GMT, if this feature is available in the system. \return a float nubmer, where decimals are up to milliseconds. */ FALCON_DYN_SYM numeric _seconds(); /** Gives current second count from Epoch in localtime. The number of seconds is generally returned in localtime, if this feature is available in the system. \return a float nubmer, where decimals are up to milliseconds. */ FALCON_DYN_SYM numeric _localSeconds(); /** Returns a counter representing a time entity. This is a direct interface to the fastest possible function the hosts systems has to provide a millisecond counter. The "0" moment is undefined (it may be the start of the program, the last reboot time or epoch), and the resolution of the calls are limited to what the system provides, but it is granted that calling _milliseconds() at T1 > T0 will result in the return value of the second call being greater or equal than the first. The difference between the two numbers express the number of milliseconds roughly elapsed between the two calls. \return millisecond counter value. */ FALCON_DYN_SYM uint32 _milliseconds(); /** Returns a valid and possibly unique temporary file name. Just a haky test for now, final version must OPEN the stream and return it. \param res on return will contain a to C stringed filename. */ FALCON_DYN_SYM void _tempName( ::Falcon::String &res ); FALCON_DYN_SYM bool _describeError( int64 eid, String &target ); FALCON_DYN_SYM int64 _lastError(); FALCON_DYN_SYM bool _getEnv( const String &var, String &result ); FALCON_DYN_SYM bool _setEnv( const String &var, const String &value ); FALCON_DYN_SYM bool _unsetEnv( const String &var ); /** Returns seconds since epoch. Used in many systems. */ FALCON_DYN_SYM int64 _epoch(); /** Callback for environment variable enumeration. \see _enumerateEnvironment */ typedef void(*EnvStringCallback)( const String& key, const String& value, void* cbData ); /** Gets all the environemnt string of the calling process. Each pair of key/value pair is sent to the cb function for processing. If possible, the strings are already rendered from local encoding. The cbData parameter is an arbitrary data that is passed to the CB function for processing. */ FALCON_DYN_SYM void _enumerateEnvironment( EnvStringCallback cb, void* cbData ); /** Returns process ID of the current process. */ FALCON_DYN_SYM int64 _getpid(); FALCON_DYN_SYM void _dummy_ctrl_c_handler(); #ifdef FALCON_SYSTEM_WIN } } #include #include namespace Falcon { namespace Sys { FALCON_DYN_SYM numeric SYSTEMTIME_TO_SECONDS( const SYSTEMTIME &st ); #endif } } #endif /* end of flc_sys.h */ include/falcon/testsuite.h000066400000000000000000000053611176363201700161600ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: testsuite.h Helper functions allowing the application to inteact with the testsuite module. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun feb 13 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Helper functions allowing the application to inteact with the testsuite module. */ #ifndef flc_testsuite_H #define flc_testsuite_H #include namespace Falcon { namespace TestSuite { /** Check wether the last module execution exited with a succes or a failure. If the last module that was passed into the VM called success() to terminate it's execution, this function returns true. If it called failure(), this function return false. In case the script termiantes without calling success or failure, the return is undetermined. For this reason, it's advisable to reset the success status with setSuccesss() function to a consistent value. \return true if the last module execution was succesful */ bool getSuccess(); void setSuccess( bool mode ); /** Retreives the last failure reason. If a module caused a failure, this function returns a string that the module passed to the failure() falcon function. If the module didn't set a reason for it's failure, the returned string is empty. */ const ::Falcon::String &getFailureReason(); /** Sets the name of the test. Used for internal reporting and alive functions. \param name the name of the test that is being run. */ void setTestName( const ::Falcon::String &name ); /** Get internal test timings. */ void getTimings( ::Falcon::numeric &totTime, ::Falcon::numeric &numOps ); /** Get internal test time factor. */ ::Falcon::int64 getTimeFactor(); /** set internal test time factor. */ void setTimeFactor( ::Falcon::int64 factor ); } } #ifdef FALCON_EMBED_MODULES /** Embedding function. This function can be directly called by embedder application to create the testsuite module. This function is defined only if the symbol FALCON_EMBED_MODULES is defined; otherwise, the standard dynamic lynk library initialization function is declared, and the embedder would have to use the module loader to bring the modules in the application. With FALCON_EMBED_MODULES, the embedder application can link the desidred modules directly from soruces and then call the function that prepares the module data. */ Falcon::Module *init_testsuite_module(); #endif #endif /* end of testsuite.h */ include/falcon/time_sys.h000066400000000000000000000046341176363201700157650ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: sys_time.h Time related system service interface. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: lun mar 6 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Time related system service interface. */ #ifndef flc_sys_time_H #define flc_sys_time_H namespace Falcon { /** Empty SystemTime class. This will be overloaded by actual OS system time, and it will be used as an opaque object in the whole TimeStamp api; it will be decoded by the system-level functions into a system independent "TimeStamp" object. */ class SystemTime { }; typedef enum { tz_local = 0, tz_UTC = 1, tz_UTC_E_1 = 2, tz_UTC_E_2 = 3, tz_UTC_E_3 = 4, tz_UTC_E_4 = 5, tz_UTC_E_5 = 6, tz_UTC_E_6 = 7, tz_UTC_E_7 = 8, tz_UTC_E_8 = 9, tz_UTC_E_9 = 10, tz_UTC_E_10 = 11, tz_UTC_E_11 = 12, tz_UTC_E_12 = 13, tz_UTC_W_1 = 14, tz_UTC_W_2 = 15, tz_UTC_W_3 = 16, tz_UTC_W_4 = 17, tz_UTC_W_5 = 18, tz_UTC_W_6 = 19, tz_UTC_W_7 = 20, tz_UTC_W_8 = 21, tz_UTC_W_9 = 22, tz_UTC_W_10 = 23, tz_UTC_W_11 = 24, tz_UTC_W_12 = 25, /** Norfolk (Island) Time UTC + 11:30 hours */ tz_NFT = 26, /** Australian Central Daylight Time UTC + 10:30 hours */ tz_ACDT = 27, /** Australian Central Standard Time UTC + 9:30 hours */ tz_ACST = 28, /** Advanced time of Terre-Neuve UTC - 2:30 hours */ tz_HAT = 29, /** Newfoundland Standard Time UTC - 3:30 hours */ tz_NST = 30, /** No zone. Used for date differences */ tz_NONE = 31 } TimeZone; // forward decl class TimeStamp; namespace Sys { namespace Time { void FALCON_DYN_SYM currentTime( ::Falcon::TimeStamp &ts ); TimeZone FALCON_DYN_SYM getLocalTimeZone(); numeric FALCON_DYN_SYM seconds(); bool FALCON_DYN_SYM absoluteWait( const TimeStamp &ts ); bool FALCON_DYN_SYM relativeWait( const TimeStamp &ts ); bool FALCON_DYN_SYM nanoWait( int32 seconds, int32 nanoseconds ); void FALCON_DYN_SYM timestampFromSystemTime( const SystemTime &st, ::Falcon::TimeStamp &ts ); } // time } // sys } // falcon #endif /* end of sys_time.h */ include/falcon/time_sys_unix.h000066400000000000000000000014061176363201700170220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: time_sys_unix.h Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom mar 12 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Unix specific system time definitions */ #ifndef flc_time_sys_unix_H #define flc_time_sys_unix_H #include #include namespace Falcon { class UnixSystemTime: public SystemTime { public: time_t m_time_t; UnixSystemTime( time_t val ): m_time_t( val ) { } }; } #endif /* end of time_sys_unix.h */ include/falcon/time_sys_win.h000066400000000000000000000014061176363201700166340ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: time_sys_win.h Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom mar 12 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Win specific system time definitions */ #ifndef flc_time_sys_win_H #define flc_time_sys_win_H #include #include namespace Falcon { class WinSystemTime: public SystemTime { public: SYSTEMTIME m_time; WinSystemTime( SYSTEMTIME val ): m_time( val ) { } }; } #endif /* end of time_sys_win.h */ include/falcon/timestamp.h000066400000000000000000000145061176363201700161330ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: timestamp.h Multiplatform date and time description. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: gio giu 21 2007 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Multiplatform date and time description. */ #ifndef flc_timestamp_H #define flc_timestamp_H #include #include #include #include #include namespace Falcon { class String; /** TimeStamp class. This class is both used as a system independent time accounting object and as a internal object for the TimeStamp falcon core object. */ class FALCON_DYN_CLASS TimeStamp: public FalconData { private: void rollOver( bool onlyDays = false); int16 getDaysOfMonth( int16 month = -1 ) const; public: int16 m_year; int16 m_month; int16 m_day; int16 m_hour; int16 m_minute; int16 m_second; int16 m_msec; TimeZone m_timezone; public: TimeStamp( int16 y=0, int16 M=0, int16 d=0, int16 h=0, int16 m=0, int16 s=0, int16 ms = 0, TimeZone tz=tz_NONE ): m_year( y ), m_month( M ), m_day( d ), m_hour( h ), m_minute( m ), m_second( s ), m_msec( ms ), m_timezone( tz ) {} TimeStamp( const TimeStamp &ts ) { copy( ts ); } TimeStamp( int64 lfmt ) { fromLongFormat( lfmt ); } ~TimeStamp() {} virtual void gcMark( uint32 mark ) {} TimeStamp &operator = ( const TimeStamp &ts ); TimeStamp &operator += ( const TimeStamp &ts ) { add( ts ); return *this; } TimeStamp &operator -= ( const TimeStamp &ts ) { distance(ts); return *this; } /** Return a RCF2822 timezone name. \param tz The timezone. \param bSemantic return semantic zone names instead of + displacements when available. \param bDst Get the DST version of the semantic zone. \return the zone name. */ static const char *getRFC2822_ZoneName( TimeZone tz, bool bSemantic=false, bool bDst=false ); /** Return a timezone given a RCF2822 timezone name. \param csZoneName The timezone name. \return the zone, or tz_NONE if the zone didn't parse succesfully. */ static TimeZone getRFC2822_Zone( const char *csZoneName ); /** Get a timezone displacement \param tz the timezone. \param hours hours the displacement in hours \param minutes minutes the displacement in minutes */ static void getTZDisplacement( TimeZone tz, int16 &hours, int16 &minutes ); /** Gets a RFC 2822 timestamp compliant weekday name. \return weekday name. */ static const char *getRFC2822_WeekDayName( int16 wd ); /** Gets a RFC 2822 timestamp compliant month name. \return Month name. */ static const char *getRFC2822_MonthName( int16 wd ); /** Return numeric weekday from a RFC2822 format weekday name. \return -1 if the name is not valid, 0-6 otherwise (Monday being 0). */ static int16 getRFC2822_WeekDay( const char *name ); /** Return numeric month from a RFC2822 format month name. \return -1 if the name is not valid, 1-12 otherwise (january being 1). */ static int16 getRFC2822_Month( const char *name ); /** Convert this timestamp to RFC2822 format. \param target The string that will receive the converted date, or "?" in case it doesn't work. \param bSemantic return semantic zone names instead of + displacements when available. \param bDst Get the DST version of the semantic zone. \return false if the date is invalid. */ bool toRFC2822( String &target, bool bSemantic=false, bool bDst=false ) const; /** Convert this timestamp to RFC2822 format. \param bSemantic return semantic zone names instead of + displacements when available. \param bDst Get the DST version of the semantic zone. \return The string converted. */ String toRFC2822( bool bSemantic=false, bool bDst=false ) const { String temp(32); toRFC2822( temp, bSemantic, bDst ); return temp; } /** Parse a RFC2822 date format and configure the given timestamp. */ static bool fromRFC2822( TimeStamp &target, const String &source ); /** Parse a RFC2822 date format and configure the given timestamp. */ static bool fromRFC2822( TimeStamp &target, const char *source ); /** Shifts this timestamp moving the old timezone into the new one. */ void changeTimezone( TimeZone tz ); void copy( const TimeStamp &ts ); void currentTime(); bool isValid() const; bool isLeapYear() const; int16 dayOfYear() const; /** Gets the day of week. Week starting on monday, 0 based. */ int16 dayOfWeek() const; int64 toLongFormat() const; void fromLongFormat( int64 lf ); void fromSystemTime( const SystemTime &st ) { Sys::Time::timestampFromSystemTime( st, *this ); } void add( const TimeStamp &ts ); void add( int32 days, int32 hours=0, int32 mins=0, int32 secs=0, int32 msecs=0 ); void distance( const TimeStamp &ts ); int32 compare( const TimeStamp &ts ) const; void toString( String &target ) const; bool toString( String &target, const String &fmt ) const; void getTZDisplacement( int16 &hours, int16 &minutes ) const; bool operator ==( const TimeStamp &ts ) const { return this->compare( ts ) == 0; } bool operator !=( const TimeStamp &ts ) const { return this->compare( ts ) != 0; } bool operator <( const TimeStamp &ts ) const { return this->compare( ts ) < 0; } bool operator >( const TimeStamp &ts ) const { return this->compare( ts ) > 0; } bool operator <=( const TimeStamp &ts ) const { return this->compare( ts ) <= 0; } bool operator >=( const TimeStamp &ts ) const { return this->compare( ts ) >= 0; } virtual TimeStamp *clone() const; //TODO: Add serialization }; inline TimeStamp operator + ( const TimeStamp &ts1, const TimeStamp &ts2 ) { TimeStamp ts3(ts1); ts3.add( ts2 ); return ts3; } inline TimeStamp operator - ( const TimeStamp &ts1, const TimeStamp &ts2 ) { TimeStamp ts3(ts1); ts3.distance( ts2 ); return ts3; } } #endif /* end of timestamp.h */ include/falcon/tokenizer.h000066400000000000000000000205451176363201700161420ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: tokenizer.h Utility to parse complex and lengty strings. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 04 Feb 2009 12:02:01 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_TOKENIZER_H #define FLC_TOKENIZER_H #include #include #include #include #include namespace Falcon { /** Parameters for the tokenizer. This is used for variable parameter idiom initialization of the Tokenizer class. Pass a direct instance of this class to configure the target Tokenizer. The setting methods in this class return a reference to this class itself, so that is possible to set several behavior and settings in cascade. */ class FALCON_DYN_CLASS TokenizerParams: public BaseAlloc { bool m_bGroupSep; bool m_bBindSep; bool m_bTrim; int32 m_nMaxToken; bool m_bWsIsToken; bool m_bReturnSep; public: TokenizerParams(): m_bGroupSep( false ), m_bBindSep( false ), m_bTrim( false ), m_nMaxToken( -1 ), m_bWsIsToken( false ), m_bReturnSep( false ) {} /** Activate this option to have the Tokenizer return only once for a sequence of separators all alike. In example, if the token list includes a space, then only one token will be returned no matter how many spaces are encountered. If not given, an empty string would be returned as a token if two tokens are found one after another. */ TokenizerParams &groupSep( bool mode = true ) { m_bGroupSep = mode; return *this; } /** Treat a sequence of whitespaces of any lenght as a single token. This separates words between spaces and other tokens. For example, a text analyzer may use this mode to get words and puntactions with a single "next" call. */ TokenizerParams &wsIsToken( bool mode = true ) { m_bWsIsToken = mode; return *this; } /** Add the tokens to the non-token previous element. This adds the separators to the token preceding them when returning the token. If grouping is activated, then more than a single separator may be returned. */ TokenizerParams &bindSep( bool mode = true ) { m_bBindSep = mode; return *this; } /** Returns found tokens separately. This forces the tokenizer to return each token in a separate call. For example, if "," is a token: \code "a, b, c" \endcode would be returned as "a" - "," - " b" - "," - " c". */ TokenizerParams &returnSep( bool mode = true ) { m_bReturnSep = mode; return *this; } /** Whitespaces are trimmed from the retuned tokens. Whitespaces are tab, space, carrige return and line feed characters. If this option is actived, the returned tokens won't include spaces found at the beginning or at the end of the token. In example, if the spearator is ':', and trim is enabled, the following sequence: \code : a: b : :c \endcode Will be parsed as a sequence of "a", "b", "", "c" tokens; otherwise, it would be parsed as " a", " b ", " ", "c". */ TokenizerParams &trim( bool mode = true ) { m_bTrim = mode; return *this; } /** Sets the maximum size of the returned tokens. If the size of the input data exceeds this size while searching for a token, an item is returned as if a separator was found. */ TokenizerParams &maxToken( int32 size ) { m_nMaxToken = size; return *this; } bool isGroupSep() const { return m_bGroupSep; } bool isBindSep() const { return m_bBindSep; } bool isTrim() const { return m_bTrim; } bool isWsToken() const { return m_bWsIsToken; } int32 maxToken() const { return m_nMaxToken; } bool isReturnSep() const { return m_bReturnSep; } }; /** Base tokenizer base class. Although this class is declared as a sequence, it only supports empty() and getIterator() operations, needed for the TRAV loop in the vm. In future, some subclasses may support some specific operations when they are locally buffered. The tokenizer is designed to operate with subclasses tokenizing a string and or a stream. The StringStream class is not used for string for performance reasons; as a very limited subset of operations are needed (namely, get), and as the visibility of the underlying buffer is useful (i.e. to avoid storing a local copy of the forming buffer), the StringTokenizer has a different, optimized implementation. The iterator generated by a Tokenizer is one-way only. Every next() operation on an iterator invalidates the others; this means that only one iterator at a time can be used on a tokenizer. \note As this class is used only internally, there is no need to mark the owner on GC as usual. All GC marking must be external (i.e. placing the item as an hidden property of the tokenizer object). */ class FALCON_DYN_CLASS Tokenizer: public Sequence { String m_separators; TokenizerParams m_params; Stream *m_input; bool m_bOwnStream; String m_temp; uint32 m_version; uint32 m_nextToken; bool m_hasCurrent; public: /** Creates a ROStringStream and uses that to read the source. WARNING: the source must be granted to stay alive for the whole duration of the tokenization, as nothing is going to create a local safe copy of the given string. */ Tokenizer( TokenizerParams ¶ms, const String &seps, const String &source ); Tokenizer( TokenizerParams ¶ms, const String &seps, Stream *inp=0, bool bOwn = false ); Tokenizer( const Tokenizer &other ); virtual ~Tokenizer(); /** Calling this causes a CodeError (unimplemented) to be raised. Subclasses may provide a consistent behavior if they wish. */ virtual const Item &front() const; /** Calling this causes a CodeError (unimplemented) to be raised. Subclasses may provide a consistent behavior if they wish. */ virtual const Item &back() const; /** Calling this causes a CodeError (unimplemented) to be raised. Subclasses may provide a consistent behavior if they wish. */ virtual void clear(); /** Returns true if the tokenizer knows it can't return any other element. */ virtual bool empty() const; /** Advance to a further token. \return false if no more token can be found */ virtual bool next(); /** Returns the currently active range after a succesful next. */ const String &getToken() const { return m_temp; } /** This may fail if the underlying stream doesn't support seek. */ virtual void rewind(); virtual Tokenizer* clone() const; virtual void gcMark( uint32 mark ) { Sequence::gcMark( mark ); } /** Resets the tokenizer providing new data to be tokenized. The string \b data must stay alive as long as this parser is used, as it is not internally copied anywhere. */ void parse( const String &data ); /** Resets the tokenizer providing new data to be tokenized. If the parameter \b bOwn is true, ti */ void parse( Stream *in, bool bOwn = false ); /** Returns true if the tokenizer has been readied with a stream. */ bool isReady() const { return m_input != 0; } bool hasCurrent() const { return m_hasCurrent; } virtual void append( const Item& itm ); virtual void prepend( const Item& itm ); //======================================================== // Iterator implementation. //======================================================== protected: virtual void getIterator( Iterator& tgt, bool tail = false ) const; virtual void copyIterator( Iterator& tgt, const Iterator& source ) const; virtual void insert( Iterator &iter, const Item &data ); virtual void erase( Iterator &iter ); virtual bool hasNext( const Iterator &iter ) const; virtual bool hasPrev( const Iterator &iter ) const; virtual bool hasCurrent( const Iterator &iter ) const; virtual bool next( Iterator &iter ) const; virtual bool prev( Iterator &iter ) const; virtual Item& getCurrent( const Iterator &iter ); virtual Item& getCurrentKey( const Iterator &iter ); virtual bool equalIterator( const Iterator &first, const Iterator &second ) const; }; } #endif /* end of tokenizer.h */ include/falcon/trace.h000066400000000000000000000023521176363201700152220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: trace.h Debug trace utility. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 18 Jul 2009 14:07:01 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef _FALCON_TRACE_H_ #define _FALCON_TRACE_H_ #ifdef NDEBUG #define TRACE_ON(...) #define TRACE_OFF #define TRACE(...) #define MESSAGE(...) #else #include #include #define TRACE_ON( name ) _falcon_trace_fp = fopen( name, "w" ); #define TRACE_OFF fclose(_falcon_trace_fp); _falcon_trace_fp = 0; #define TRACE( fmt, ... ) if( _falcon_trace_fp != 0 ) fprintf( _falcon_trace_fp, "%s:%d: " fmt "\n", __FILE__, __LINE__, __VA_ARGS__ ); #define MESSAGE( fmt ) if( _falcon_trace_fp != 0 ) fprintf( _falcon_trace_fp, "%s:%d: " fmt "\n", __FILE__, __LINE__ ); #define TRACEVAR( type, var ) if( _falcon_trace_fp != 0 ) fprintf( _falcon_trace_fp, "%s:%d: %s=%" type "\n", __FILE__, __LINE__, #var, var ); extern "C" { extern FALCON_DYN_SYM FILE* _falcon_trace_fp; } #endif #endif /* end of trace.h */ include/falcon/traits.h000066400000000000000000000057441176363201700154420ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: traits.h Traits - informations on types for the generic containers ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ven oct 27 11:02:00 CEST 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef fal_traits_h #define fal_traits_h #include #include namespace Falcon { class FALCON_DYN_CLASS ElementTraits { public: virtual ~ElementTraits(); virtual uint32 memSize() const = 0; virtual void init( void *itemZone ) const = 0; virtual void copy( void *targetZone, const void *sourceZone ) const = 0; virtual int compare( const void *first, const void *second ) const = 0; virtual void destroy( void *item ) const = 0; virtual bool owning() const = 0; }; class FALCON_DYN_CLASS VoidpTraits: public ElementTraits { public: virtual uint32 memSize() const; virtual void init( void *itemZone ) const; virtual void copy( void *targetZone, const void *sourceZone ) const; virtual int compare( const void *first, const void *second ) const; virtual void destroy( void *item ) const; virtual bool owning() const; }; class FALCON_DYN_CLASS IntTraits: public ElementTraits { public: virtual uint32 memSize() const; virtual void init( void *itemZone ) const; virtual void copy( void *targetZone, const void *sourceZone ) const; virtual int compare( const void *first, const void *second ) const; virtual void destroy( void *item ) const; virtual bool owning() const; }; class FALCON_DYN_CLASS StringPtrTraits: public ElementTraits { public: virtual uint32 memSize() const; virtual void init( void *itemZone ) const; virtual void copy( void *targetZone, const void *sourceZone ) const; virtual int compare( const void *first, const void *second ) const; virtual void destroy( void *item ) const; virtual bool owning() const; }; class FALCON_DYN_CLASS StringPtrOwnTraits: public StringPtrTraits { public: virtual ~StringPtrOwnTraits() {} virtual bool owning() const; virtual void destroy( void *item ) const; }; class FALCON_DYN_CLASS StringTraits: public ElementTraits { public: virtual uint32 memSize() const; virtual void init( void *itemZone ) const; virtual void copy( void *targetZone, const void *sourceZone ) const; virtual int compare( const void *first, const void *second ) const; virtual void destroy( void *item ) const; virtual bool owning() const; }; namespace traits { extern FALCON_DYN_SYM StringTraits &t_string(); extern FALCON_DYN_SYM VoidpTraits &t_voidp(); extern FALCON_DYN_SYM IntTraits &t_int(); extern FALCON_DYN_SYM StringPtrTraits &t_stringptr(); extern FALCON_DYN_SYM StringPtrOwnTraits &t_stringptr_own(); void FALCON_DYN_SYM releaseTraits(); } } #endif /* end of traits.h */ include/falcon/transcoding.h000066400000000000000000000323241176363201700164410ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: transcoding.h Declarations of encoders and decoders. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: dom ago 20 2006 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Declarations of encoders and decoders. */ #ifndef flc_transcoding_H #define flc_transcoding_H #include #include #include #include #include namespace Falcon { typedef struct tag_cp_iso_uint_table { uint16 unicode; uint16 local; } CP_ISO_UINT_TABLE; /** Basic transcoder class. Falcon strings are internally organized in a format that is meant to: - store every possible UNICODE character - allow scripts to access nth character randomly at cost o(1) - allow easy transcoding in international formats. Transcoder are the objects that allow importing of text from streams (including "StringStreams") and encoding of falcon strings to output devices. By picking the right transcoder, the embedder is able to read a text file which is recorded in any encoding and to import it in a Falcon String, or to write a falcon string to a file that can be then opened in other applications. The stream the transcoder receives is not owned by the transcoder: the caller must close and dispose of the given stream separatedly. As all transcoding operations are character oriented, unless the stream is considerabily small, using a BufferedStream is highly recommended (unless, of course, using a memory based stream like object). A convenience function TranscoderFactory is provided to build a transcoder for a given character encoding. This class has also a read ahead and write-back buffer facilities. This allows to build very complex lexers and parsers by using this class as an interface to a stream that may not have seek capabilities. The buffer is constructed only if needed, so there is no extra cost for this facility. \note default behavior when transcoding a character that cannot be represented in the target encoding is to write a "?" (unicode question mark) character. \see TranscoderFactory */ class FALCON_DYN_CLASS Transcoder: public Stream { protected: Stream *m_stream; bool m_parseStatus; bool m_streamOwner; Transcoder( Stream *s, bool bOwn ); Transcoder( const Transcoder &other ); public: virtual ~Transcoder(); virtual t_status status() const { return m_stream->status(); } virtual void status( t_status s ) { m_stream->status( s ); } virtual bool isTranscoder() const { return true; } /** Returns the character encoding which is managed by this transcoder. Subclasses must reimplement this to return the name of the supported encoding. */ virtual const String encoding() const = 0; /** Returns the underlying stream used by this transcoder. \return the underlying stream. */ Stream *underlying() const { return m_stream; } /** Return encoder status. A false status indicates that the last encoding or decoding operation wasn't successful. In example, it may indicate an incorrect UTF-8 sequence in utf-8 reading, or an unencodable character in ISO8859 writing. Stream I/O error do not set to false this state, and operations may return true even if this state get false. In example, ISO8859 encoding writes a "?" in case of unencodable character and correctly completes the operation, but to inform the user about this fact it sets the status to false. Iterative get operations (i.e. get string) will be interrupted if status is false, and will return false, as the quality of the stream cannot be granted. Iterative write operations will return true and complete iterations, but will set state to false on exit. \return false if last get or write operation caused an encoding breaking. */ bool encoderStatus() const { return m_parseStatus; } /** Sets the underlying stream. To be able to set a stream after that the transcoder has been created. This function can set property on the stream. If the owner parameter is set to true, the stream pointer will be destroyed when the transcoder will be destroyed. \note If the previously used stream was owned by this instance, it is destroyed here. \param s stream to be used as underlying stream \param owner true if the stream must be destroyed at transcoder termination. */ void setUnderlying( Stream *s, bool owner=false ); virtual bool writeString( const String &source, uint32 begin=0, uint32 end = csh::npos ); virtual bool readString( String &source, uint32 size ); protected: virtual int64 seek( int64 pos, e_whence w ); public: virtual bool close(); virtual int64 tell(); virtual bool truncate( int64 pos=-1 ); virtual int32 read( void *buffer, int32 size ) { return m_stream->read( buffer, size ); } virtual int32 write( const void *buffer, int32 size ) { return m_stream->write( buffer, size ); } virtual bool errorDescription( ::Falcon::String &description ) const { return m_stream->errorDescription( description ); } virtual int32 readAvailable( int32 msecs_timeout, const Sys::SystemData *sysData = 0 ) { return m_stream->readAvailable( msecs_timeout, sysData ); } virtual int32 writeAvailable( int32 msecs_timeout, const Sys::SystemData *sysData ) { return m_stream->writeAvailable( msecs_timeout, sysData ); } virtual int64 lastError() const { return m_stream->lastError(); } virtual bool flush(); /** Disengages this transcoder from the underlying stream. */ void detach() { m_stream = 0; m_streamOwner = false; } }; /** EOL Transcoder. Under some OSs, and in some stream environment (i.e. TELNET streams, HTTP headers etc.), the line terminator is a sequence of characters CR+LF. Falcon strings internal encoding sees '\n' (LF) as a line terminator marker. As the transcoders are objects meant to translate the Falcon internal encoding into a text resource under the rules of external encoding, they also provide the facility to translate LF character into a CRLF sequence, and to recognize the CRLF sequence in read to translate it back into a '\n' line terminator. This trancoder can be cascaded with other transcoder; the stream used by this transcoder may be another transcoder storing output to files. */ class FALCON_DYN_CLASS TranscoderEOL: public Transcoder { public: TranscoderEOL( Stream *s, bool bOwn=false ): Transcoder( s, bOwn ) {} TranscoderEOL( const TranscoderEOL &other ); virtual bool get( uint32 &chr ); virtual bool put( uint32 chr ); virtual const String encoding() const { return "EOL"; } virtual TranscoderEOL *clone() const; }; /** Transparent byte oriented encoder. This encoder writes anything below 256 directly; characters above 256 are translated into a '?', but the default can be overridden with the substituteChar method. This is a good default transcoder to be used in case it is not possibile to determine a transcoder. Code name for this transcoder is "byte", incase you want to summon it throgh TranscoderFactory. */ class FALCON_DYN_CLASS TranscoderByte: public Transcoder { byte m_substitute; public: TranscoderByte( Stream *s, bool bOwn=false ): Transcoder( s, bOwn ), m_substitute( (byte) '?' ) {} TranscoderByte( const TranscoderByte &other ); /** Set the substitute character. This is the character that this transcoder writes insead of chars above 255. By default it is '?'. */ void substituteChar( char chr ) { m_substitute = (byte) chr; } char substituteChar() const { return m_substitute; } virtual bool get( uint32 &chr ); virtual bool put( uint32 chr ); virtual const String encoding() const { return "byte"; } virtual TranscoderByte *clone() const; }; /** UTF-8 encoding transcoder. */ class FALCON_DYN_CLASS TranscoderUTF8: public Transcoder { public: TranscoderUTF8( Stream *s, bool bOwn=false ): Transcoder( s, bOwn ) {} TranscoderUTF8( const TranscoderUTF8 &other ); virtual bool get( uint32 &chr ); virtual bool put( uint32 chr ); virtual const String encoding() const { return "utf-8"; } virtual TranscoderUTF8 *clone() const; }; /** UTF-16 encoding transcoder. */ class FALCON_DYN_CLASS TranscoderUTF16: public Transcoder { public: /** Endianity specification, see the constructor. */ typedef enum { e_detect, e_le, e_be } t_endianity; private: t_endianity m_defEndian; t_endianity m_streamEndian; t_endianity m_hostEndian; bool m_bFirstIn; bool m_bFirstOut; protected: bool m_bom; public: /** Constructor UTF-16 requires specification of endianity with a prefix character. On output transcoding, this character is written right before the first write() is performed, while on input it is read right before the first get() is performed. This constructor allows to select an endianity used in I/O. The default is e_none; in this case, the transcoder will detect and use the system endianity on output. In input, the marker read at first get() determines the endianity. If the marker can't be found (i.e. if the stream was not at beginning), the supplied parameter will be used. If the endianity is e_detect, the host system endianity is used. The method endianity() can be used to determine the endianity of the stream in input that was decided either by reading the marker or by the constructor. \param s the underlying stream \param bOwn own the underlying stream \param endianity the endianity for operations. */ TranscoderUTF16( Stream *s, bool bOwn=false, t_endianity endianity = e_detect ); TranscoderUTF16( const TranscoderUTF16 &other ); virtual bool get( uint32 &chr ); virtual bool put( uint32 chr ); virtual const String encoding() const { return "utf-16"; } t_endianity endianity() const { return m_streamEndian; } virtual TranscoderUTF16 *clone() const; }; /** Creates a transcoder for the given encoding. If the encoding is not recognized or not supported, null is returned. The function can be called without having the stream ready to be sure that a certain encoding is supported before opening the stream. After that, the returned transcoder must be given a stream with the setStream method as soon as possible. \param encoding the ISO encoding name for which to build a transcoder \param stream optional stream that shall be used by the created transcoder \param own set to true if the given stream should be owned (and destroyed) by the transcoder. */ FALCON_DYN_SYM Transcoder *TranscoderFactory( const String &encoding, Stream *stream=0, bool own = false ); /** Transcode a string into another. This is a convenience function that transcodes a source string into a target string. The target string is given a byte manipulator; this means that both the embedders and the scripts will be able to access every byte in the resulting string. The target string can be written as-is on a stream and the resulting output will respect the format specified by the selected encoding. \param source the Falcon string to be encoded \param encoding the name of the target encoding \param target the target where the string will be encoded \return false if the target encoding is not supported. */ FALCON_DYN_SYM bool TranscodeString( const String &source, const String &encoding, String &target ); /** Transcode an external text string into a Falcon string. This is a convenience function that transcodes a source string into a target string. The target string is a Falcon string that will accept data a foreign, text encoded string transcoding. The target string must be re-encoded into something else before being written into a text oriented file. \param source a text encoded source string \param encoding the encoding that is used in the source string \param target the target string. \return false if the target encoding is not supported. */ FALCON_DYN_SYM bool TranscodeFromString( const String &source, const String &encoding, String &target ); /** Determines the default encoding used on the system. On success, the parameter is filled with the name of an encoding that can be instantiated through TranscodeFactory(). If the function is not able to determine the default system encoding, false is returned. The transparent "byte" encoding is never returned. It is supposed that, on failure the caller should decide whether to use the "byte" encoding or take a sensible action. However, "byte" encoding is returned if system encoding is detected to be "C" or "POSIX". \param encoding on success will be filled with a FALCON encoding name \return true on success, false if the encoding used on the system cannot be determined. */ FALCON_DYN_SYM bool GetSystemEncoding( String &encoding ); } #endif /* end of transcoding.h */ include/falcon/types.h000066400000000000000000000023301176363201700152640ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: ht_types.h Declaration of types used by Falcon language. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2004-05-15 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef HT_TYPES_H #define HT_TYPES_H #include namespace Falcon { class EngineData; typedef char * cstring; typedef unsigned char byte; typedef unsigned char * bytearray; typedef byte uint8; typedef unsigned short int uint16; typedef unsigned int uint32; #ifdef _MSC_VER typedef unsigned __int64 uint64; #else typedef unsigned long long int uint64; #endif typedef char int8; typedef short int int16; typedef int int32; #ifdef _MSC_VER typedef __int64 int64; #else typedef long long int int64; #endif typedef double numeric; typedef void * voidp; class VMachine; class Module; typedef void ( CDECL *ext_func_t) ( VMachine *); typedef bool ( CDECL *ext_func_frame_t) ( VMachine * ); extern "C" { typedef Module* (CDECL *ext_mod_init)(); } } #endif /* end of ht_types.h */ include/falcon/uintpair.h000066400000000000000000000015631176363201700157620ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: $FILE$.h Short description ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: gio ago 26 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef flc_UINTPAIR_H #define flc_UINTPAIR_H #include namespace Falcon { /** Prefect hash base structure. Perfect hashes are hashes that has not duplicate hash values by definition. They are used in switch() statements and class properties. */ template< class _T > class uint_pair { public: uint32 first; _T second; uint_pair( uint32 f, _T s ): first(f), second(s) {} }; } #endif /* end of flc_uintpair.h */ include/falcon/uri.h000066400000000000000000000347521176363201700147340ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: uri.h RFC 3986 - Uniform Resource Identifier ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 17 Feb 2008 12:23:28 +0100 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_URI_H #define FALCON_URI_H #include #include #include #include namespace Falcon { /** RFC 3986 - Uniform Resource Identifier. This class offer falcon engine and its users an interface to URI. */ class FALCON_DYN_CLASS URI: public BaseAlloc { /** A copy of the original string, for diagniostics.*/ String m_original; /** The final normalized and encoded URI. */ mutable String m_encoded; /** False if this URI is not valid. */ bool m_bValid; /** URI scheme (e.g. http) */ String m_scheme; // Authority ---------------------- /** User or user:password. */ String m_userInfo; /** Host part of the URI. */ String m_host; /** Port part of the URI. */ String m_port; /** Path. Virtually divided into 4 elements: \code /[resource:]path/file.ext \endcode but calculus on path is done realtime. \note relative paths (not starting with /) cannot have a resource part. */ Path m_path; /** Query string, recorded as-is. */ mutable String m_query; /** Query part. Map of string->string. Will be used only if query parsing is explicitly requested. Many things using URI may not want this to be done, i.e. becasuse they want to parse the query on their own. Also, query form is free and may be a per-application format, even if the key=value& list is a quite eshtablished standard. */ Map *m_queryMap; /** Iterator used for opaque traversal of query objects */ MapIterator m_queryIter; /** Fragment. */ String m_fragment; /** Encodes a prebuilt string which is then placed in the m_encoded field. */ void encode( const String &u ); /** Parses the query element. */ bool internal_parseQuery( const String &str, uint32 pos, bool parseQuery , bool bDecode ); /** Parses the fragment element. */ bool internal_parseFragment( uint32 pos ); bool internal_parse( const String &newUri, bool parseQuery, bool decode = true ); friend class Path; public: /** Empty constructor. Creates an empty URI. To be filled at a later time. */ URI(); /** Complete URI constructor. Decodes the uri and parses it in its fields. In case URI is not valid, isValid() will return false after construction. */ URI( const String &suri ); /** Copy constructor. Copies everything in the other URI, including validity. */ URI( const URI &other ); virtual ~URI(); /** Parses the given string into this URI. The URI will be normalized and eventually decoded, so that the internal format of the URI. Normally, the method decodes any % code into it's value, and considers the uri as encoded into UTF-8 sequences. If the \b decode param is false, the input string is read as-is. By default, the function will just store the query field for later retrival with the query() accessor. The query field will be returned in its original form, undecoded. If the makeQueryMap boolean field is set to true, the parseQuery() method will be called upon succesful completion of URI parsing, before the function returns. \param newUri the new URI to be parsed. \param decode set to false to use the given URI as is. \param bMakeQueryMap if true, will create a string map with pre-parsed from query field, if present. \return true on success, false if the given string is not a valid URI. */ bool parse( const String &newUri, bool parseQuery = false, bool decode = true ); /** Parses the query field. Taken the query field of this class, it perform a RFC3986 scan for "&" separated keys and values pairs, each of which separated with a "=" sign. The result is set in the internal map of fields, that can then be inspected or changed keywise. This method overwrites existing keys with new ones, so it is not possible to use it to implement PHP-like URI arrays as in i.e. \code k[]=1&k[]=2 \endcode \param decode true to automatically URL decode keys and values that will be stored in the map. \return true on success, false if the query field cannot be decoded. */ bool parseQuery( bool decode = true ); /** Changes query and the parses it field. This method calls in sequecnce the query() accessor and then the parseQuery() method. \param q a string that will be set as-is in the query field of this URI \param decode true to automatically URL decode keys and values that will be stored in the map. \return true on success, false if the query field cannot be decoded. */ bool parseQuery( const String &q, bool decode = true ) { query( q ); return parseQuery( decode ); } /** Sets the query field of this URI. The query field is set as-is. This destroys previously created maps of keys values that should be used as query field generators. The parameter should be URL encoded before being set into this method, or the \b encode parameter may be used to have this method to perform URL encoding. \param q the query to be set. \param encode if true, q is considered a plain string still to be encoded. */ void query( const String &q, bool encode = false ); /** Returns previously set query. This method returns a previously set query field as-is. The content of the key-value map of this URI object, if any, is ignored. \note To make a query field out of a query-map, use the makeQuery() method. */ const String &query() const { return m_query; } /** Synthetizes a query field out of the key-values stored in this URI object. This call clears the content of the query field and changes it with an RFC3986 encoded key-value pair list in the format \code k1=v1&k2=v2&...&kn=vn \endcode \return The synthetized string. */ const String &makeQuery() const; /** Returns the current URI. This method eventually builds a new URI from the internally parsed data and returns it. If a set of key-value pairs has been set in this URI, it is used to synthetize a query field using makeQuery() method. If this is not desired, i.e. because already done, or because the query field has been set separately, the \b synthQuery parameter may be set to false, and the content of the query field will be used instead. */ const String &get( bool synthQuery = true ) const; // A bit of parsing support. /** Character is a reserved delimiter under RFC3986 */ inline static bool isResDelim( uint32 chr ); /** Character is main section delimiter under RFC3986 */ inline static bool isMainDelim( uint32 chr ); /** Character is a general delimiter under RFC3986 */ inline static bool isGenDelim( uint32 chr ); /** Character is a subdelimiter under RFC4986 */ inline static bool isSubDelim( uint32 chr ); /** Unreserved characters under RFC3986 */ inline static bool isUnreserved( uint32 chr ); /** Normalzies the URI sequence. Transforms ranges of ALPHA (%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) into their coresponding values. Other than that, it transforms %20 in spaces. The normalization is performed on a result string. Every string set in input by any method in this class is normalized prior to storage. However, notice that parts in the class may be invalid URI elements if extracted as is, as they are stored as Falcon international strings. Encoding to URI conformance happens only when required. \param part the element of the URI (or the complete URI) to be normalized. \param result the string where the normalization is performed. */ //static void normalize( const String &part, String &result ); /** Returns current scheme. */ const String &scheme() const { return m_scheme; } /** Sets a different scheme for this URI. This will invalidate current URI until next uri() is called. */ void scheme( const String &s ); /** Returns current userInfo. */ const String &userInfo() const { return m_userInfo; } /** Sets a different userInfo for this URI. This will invalidate current URI until next uri() is called. */ void userInfo( const String &s ); /** Returns current host. */ const String &host() const { return m_host; } /** Sets a different host for this URI. This will invalidate current URI until next uri() is called. */ void host( const String &h ); /** Returns current port. */ const String &port() const { return m_port; } /** Sets a different port for this URI. This will invalidate current URI until next uri() is called. */ void port( const String &h ); /** Returns current path. */ const String &path() const { return m_path.get(); } /** Returns current path as a path class. */ const Path &pathElement() const { return m_path; } /** Returns current path as a path class. */ Path &pathElement() { return m_path; } /** Sets a different path for this URI. This will invalidate current URI until next uri() is called. */ void path( const String &p ); /** Sets a different path for this URI. This will invalidate current URI until next uri() is called. */ void path( const Path &p ); /** Returns true if the query part has this field. */ bool hasField( const String &f ) const ; /** Returns the required value, if it exists. */ bool getField( const String &key, String &value ) const; /** Sets a given query field. As a convention, if the string contains only a single 0 character (NUL), the final result won't include = in the query part, while an empty string will result in a query string containing only a keu and a "=" followed by nothing. Strings longer than 1 element are not interpreted this way, so a nul followed by some data would be rendere as "%00" followed by the rest of the value. */ void setField( const String &key, const String &value ); /** Removes a query field. */ bool removeField( const String &key ); /** Enumerates the query fields - gets the first field. Returns true if there is a first field in the query. \note The query element must have been previously parsed, or fields must have been explicitly inserted. \param key a string where the key of the first field will be placed \param value a string where the value of the first field will be placed (can be an empty string). \return true if there is a first field. */ bool firstField( String &key, String &value ); /** Enumerates the query fields - gets the next field. Returns true if there is a next field. \note The query element must have been previously parsed, or fields must have been explicitly inserted. \param key a string where the key of the first field will be placed \param value a string where the value of the first field will be placed (can be an empty string). */ bool nextField( String &key, String &value ); /** Enumerates the query fields - counts the fields. If the query has fields, or if fields have been explicitly set throug setField() method, returns the count of fields stored in this URI. \note The query element must have been previously parsed, or fields must have been explicitly inserted. \return count of fields in this query, 0 for none. */ uint32 fieldCount(); /** Returns the fragment part. */ const String &fragment() const { return m_fragment; } /** Sets the fragment part. */ void fragment( const String &s ); /** Clears the content of this URI */ void clear(); /** Returns true if the URI is valid. */ bool isValid() const { return m_bValid; } static void URLEncode( const String &source, String &target ); static String URLEncode( const String &source ) { String t; URLEncode( source, t ); return t; } static void URLEncodePath( const String &source, String &target ); static String URLEncodePath( const String &source ) { String t; URLEncodePath( source, t ); return t; } /** Decode an URI-URL encoded string. \param source the string to be decoded. \param target the target where to store the decoded string. \return true if the decoding was succesful, false otherwise. */ static bool URLDecode( const String &source, String &target ); static String URLDecode( const String &source ) { String t; URLDecode( source, t ); return t; } static unsigned char CharToHex( unsigned char ch ) { return ch <= 9 ? '0' + ch : 'A' + (ch - 10); } static unsigned char HexToChar( unsigned char ch ) { if ( ch >= '0' && ch <= '9' ) return ch - '0'; else if ( ch >= 'A' && ch <= 'F' ) return ch - 'A'+ 10; else if ( ch >= 'a' && ch <= 'f' ) return ch - 'a'+ 10; else return 0xFF; } }; //================================================== // Inline implementation // inline bool URI::isResDelim( uint32 chr ) { return isGenDelim( chr ) || isSubDelim( chr ); } inline bool URI::isMainDelim( uint32 chr ) { return (chr == ':') || (chr == '?') || (chr == '#') || (chr == '@'); } inline bool URI::isGenDelim( uint32 chr ) { return (chr == ':') || (chr == '/') || (chr == '?') || (chr == '#') || (chr == '[') || (chr == ']') || (chr == '@'); } inline bool URI::isSubDelim( uint32 chr ) { return (chr == '!') || (chr == '$') || (chr == '&') || (chr == '\'') || (chr == '(') || (chr == ')') || (chr == '*') || (chr == '+') | (chr == ',') || (chr == ';') || (chr == '='); } inline bool URI::isUnreserved( uint32 chr ) { return (chr == '-') || (chr == '_') || (chr == '.') || (chr == '~') || (chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9'); } } // Falcon namespace #endif include/falcon/vardef.h000066400000000000000000000176701176363201700154040ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vardef.h Special compile-time definition for variables ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 11 Jul 2009 20:42:43 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_VARDEF_H #define FLC_VARDEF_H #include #include #include #include namespace Falcon { class Symbol; class Stream; /** Variable initial value definition. This class holds the immediate values of properties of classes and objects, and eventually the values static symbols, when an initial value has been declared if it's declared. If the properties are declared as expressions, or anyhow not as immediate values, the relative property definition is set to nil and the code generator will create an internal constructor (called before the user-defined constructor) that will initialize the complex property values. Property definition can then be: - nil (actually declared nil or to be filled by the internal construtor) - integer - numeric - string (only the string ID in the module string table is stored) - symbol (only the symbol ID in the module symbol table is stored) */ class FALCON_DYN_CLASS VarDef: public BaseAlloc { public: typedef enum { t_nil, t_int, t_bool, t_num, t_string, t_symbol, t_base, t_reference, t_reflective, t_reflectFunc } t_type; private: t_type m_val_type; bool m_bReadOnly; union { bool val_bool; uint64 val_int; numeric val_num; struct { t_reflection mode; uint32 offset; } val_reflect; struct { reflectionFunc from; reflectionFunc to; void *data; } val_rfunc; const String *val_str; Symbol *val_sym; } m_value; public: VarDef(): m_val_type(t_nil), m_bReadOnly( false ) {} explicit VarDef( bool val ): m_val_type(t_bool), m_bReadOnly( false ) { m_value.val_bool = val; } VarDef( int64 val ): m_val_type(t_int), m_bReadOnly( false ) { m_value.val_int = val; } VarDef( numeric val ): m_val_type(t_num), m_bReadOnly( false ) { m_value.val_num = val; } VarDef( const String *str ): m_val_type(t_string), m_bReadOnly( false ) { m_value.val_str = str; } VarDef( Symbol *sym ): m_val_type( t_symbol ), m_bReadOnly( false ) { m_value.val_sym = sym; } VarDef( t_type t, Symbol *sym ): m_val_type( t ), m_bReadOnly( false ) { m_value.val_sym = sym; } VarDef( t_type t, int64 iv ): m_val_type(t), m_bReadOnly( false ) { m_value.val_int = iv; } VarDef( reflectionFunc rfrom, reflectionFunc rto=0 ): m_val_type( t_reflectFunc ), m_bReadOnly( rto==0 ) { m_value.val_rfunc.from = rfrom; m_value.val_rfunc.to = rto; } VarDef( t_reflection mode, uint32 offset ): m_val_type( t_reflective ), m_bReadOnly( false ) { m_value.val_reflect.mode = mode; m_value.val_reflect.offset = offset; } t_type type() const { return m_val_type; } /** Describes this property as nil. \return a reference to this instance, for variable parameter initialization idiom. */ VarDef& setNil() { m_val_type = t_nil; return *this; } VarDef& setBool( bool val ) { m_val_type = t_bool; m_value.val_bool = val; return *this;} VarDef& setInteger( int64 val ) { m_val_type = t_int; m_value.val_int = val; return *this;} VarDef& setString( const String *str ) { m_val_type = t_string; m_value.val_str = str; return *this; } VarDef& setSymbol( Symbol *sym ) { m_val_type = t_symbol; m_value.val_sym = sym; return *this;} VarDef& setNumeric( numeric val ) { m_val_type = t_num; m_value.val_num = val; return *this;} VarDef& setBaseClass( Symbol *sym ) { m_val_type = t_base; m_value.val_sym = sym; return *this;} VarDef& setReference( Symbol *sym ) { m_val_type = t_reference; m_value.val_sym = sym; return *this;} /** Describes this property as reflective. This ValDef defines a property that will have user functions called when the VM wants to set or get a property. It is also possible to define an extra reflective data, that should be alive during the lifespan of the module defining it, that will be passed back to the property set/get callback functions as the \a PropEntry::reflect_data property of the "entry" parameter. \param rfrom Function that gets called when the property is \b set \b from an external source. \param rto Function that gets called when the property is \b read and then stored \b to the external source; set to 0 to have a read-only reflective property. \param reflect_data a pointer that will be passed as a part of the entry structure in the callback method. \return a reference to this instance, for variable parameter initialization idiom. */ VarDef &setReflectFunc( reflectionFunc rfrom, reflectionFunc rto=0, void *reflect_data = 0 ) { m_val_type = t_reflectFunc; m_bReadOnly = rto == 0; m_value.val_rfunc.from = rfrom; m_value.val_rfunc.to = rto; m_value.val_rfunc.data = reflect_data; return *this; } /** Describes this property as reflective. \return a reference to this instance, for variable parameter initialization idiom. */ VarDef &setReflective( t_reflection mode, uint32 offset ) { m_val_type = t_reflective; m_value.val_reflect.mode = mode; m_value.val_reflect.offset = offset; return *this; } /** Describes this property as reflective. Shortcut calculating the offset given a sample structure and a field in that. \return a reference to this instance, for variable parameter initialization idiom. */ VarDef& setReflective( t_reflection mode, void *base, void *position ) { return setReflective( mode, static_cast( static_cast(position) - static_cast(base)) ); } /** Describes this property as reflective. \return a reference to this instance, for variable parameter initialization idiom. */ VarDef& setReadOnly( bool ro ) { m_bReadOnly = ro; return *this; } bool asBool() const { return m_value.val_bool; } int64 asInteger() const { return m_value.val_int; } const String *asString() const { return m_value.val_str; } Symbol *asSymbol() const { return m_value.val_sym; } numeric asNumeric() const { return m_value.val_num; } reflectionFunc asReflectFuncFrom() const { return m_value.val_rfunc.from; } reflectionFunc asReflectFuncTo() const { return m_value.val_rfunc.to; } void* asReflectFuncData() const { return m_value.val_rfunc.data; } t_reflection asReflecMode() const { return m_value.val_reflect.mode; } uint32 asReflecOffset() const { return m_value.val_reflect.offset; } bool isNil() const { return m_val_type == t_nil; } bool isBool() const { return m_val_type == t_bool; } bool isInteger() const { return m_val_type == t_int; } bool isString() const { return m_val_type == t_string; } bool isNumeric() const { return m_val_type == t_num; } bool isSymbol() const { return m_val_type == t_symbol || m_val_type == t_base; } bool isBaseClass() const { return m_val_type == t_base; } bool isReference() const { return m_val_type == t_reference; } bool isReflective() const { return m_val_type == t_reflective; } bool isReflectFunc() const { return m_val_type == t_reflectFunc; } bool isReadOnly() const { return m_bReadOnly; } bool save( const Module *mod, Stream *out ) const; bool load( const Module *mod, Stream *in ); }; } #endif /* end of vardef.h */ include/falcon/vfs_file.h000066400000000000000000000041221176363201700157160ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vfs_file.h VSF provider for physical file system on the host system. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 12 Sep 2008 21:47:10 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file VSF provider for physical file system on the host system. */ #ifndef flc_vfs_file_H #define flc_vfs_file_H #include #include namespace Falcon { /** VSF provider for physical file system on the host system. This class wraps the "file" URI provider and is implemented with different cpp files on different host systems. */ class FALCON_DYN_CLASS VFSFile: public VFSProvider { protected: void *m_fsdata; inline int paramsToMode( const OParams &p ) { int omode = 0; if ( p.isRdwr() ) omode = O_RDWR; else if ( p.isRdOnly() ) omode = O_RDONLY; else omode = O_WRONLY; if( p.isTruncate() ) omode |= O_TRUNC; if( p.isAppend() ) omode |= O_APPEND; return omode; } public: VFSFile(); virtual ~VFSFile(); virtual Stream* open( const URI &uri, const OParams &p ); virtual Stream* create( const URI &uri, const CParams &p, bool &bSuccess ); virtual DirEntry* openDir( const URI &uri ); virtual bool readStats( const URI &uri, FileStat &s ); virtual bool writeStats( const URI &uri, const FileStat &s ); virtual bool chown( const URI &uri, int uid, int gid ); virtual bool chmod( const URI &uri, int mode ); virtual bool link( const URI &uri1, const URI &uri2, bool bSymbolic ); virtual bool unlink( const URI &uri ); virtual bool mkdir( const URI &uri, uint32 mode ); virtual bool rmdir( const URI &uri ); virtual bool move( const URI &suri, const URI &duri ); virtual int64 getLastFsError(); virtual Error *getLastError(); }; } #endif /* end of vsf_file.h */ include/falcon/vfsprovider.h000066400000000000000000000157011176363201700164770ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vfsprovider.h Generic provider of file system abstraction. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 11 Sep 2008 08:58:33 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Generic provider of file system abstraction. */ #ifndef flc_vfs_provider_H #define flc_vfs_provider_H #include #include #include #include #include #include namespace Falcon { class Error; /** Base class for Falcon Virtual File System Providers. VFS providers are singletons containing virtual pure functions (function vectors) meant to give information about a particular filesystem, or factory functions generating VFS related objects as streams and directory handlers. VFS handler respond to a single protocol specification. When the VM receives a request to open an URI (be it a file or a specific directory) it parses the given uri and finds an appropriate VFS provider for that kind of resource. */ class FALCON_DYN_CLASS VFSProvider: public BaseAlloc { String m_servedProto; protected: VFSProvider( const String &name ): m_servedProto( name ) {} public: virtual ~VFSProvider(); /** Open Parameters. Parameters for opening a stream on the provider. Subclasses may overload this class to provide VFS specific open-flags. */ class OParams { uint32 m_oflags; uint32 m_shflags; friend class VSFProvider; public: OParams(): m_oflags(0), m_shflags(0) {} OParams& rdOnly() { m_oflags |= 0x1; return *this; } bool isRdOnly() const { return (m_oflags & 0x1) == 0x1; } OParams& wrOnly() { m_oflags |= 0x2; return *this; } bool isWrOnly() const { return (m_oflags & 0x2) == 0x2; } OParams& rdwr() { m_oflags |= 0x3; return *this; } bool isRdwr() const { return (m_oflags & 0x3) == 0x3; } /** Open the file for append. File pointer is moved to the end of file at open. (Some FS guarantee also moving the file pointer at end of file after each write). */ OParams& append() { m_oflags |= 0x4; return *this; } bool isAppend() const { return (m_oflags & 0x4) == 0x4; } /** If the file exists, it is truncated. Can be specified also when creating a file. In that case, truncating a file causes it's modification time to be changed, but all its other stats (as owner, security access, creation date, etc.) are left untouched. */ OParams& truncate() { m_oflags |= 0x8; return *this; } bool isTruncate() const { return (m_oflags & 0x8) == 0x8; } OParams& shNoRead() { m_shflags |= 0x1; return *this; } bool isShNoRead() const { return (m_shflags & 0x1) == 0x1; } OParams& shNoWrite() { m_shflags |= 0x2; return *this; } bool isShNoWrite() const { return (m_shflags & 0x2) == 0x2; } OParams& shNone() { m_shflags |= 0x3; return *this; } bool isShNone() const { return (m_shflags & 0x3) == 0x3; } }; /** Create Paramenter. Parameters for creating a file on the provider. Subclasses may overload this class to provide VFS specific create-flags. Note that the "create" operation is also entitled to create a directory on the VFS, if the appropriate flag is set. The class is used for variable parameters idiom in the VFSProvider::create() method. Read/write and share modes are inherited from open settings. */ class CParams: public OParams { uint32 m_cflags; uint32 m_cmode; friend class VFSProvider; public: CParams(): m_cflags(0), m_cmode( 0644 ) {} /** Fail if the file exists. If the file exists and none of append() or truncate() options are specified, the operation fails. The subsystem is bound to return a nonzero value from getLastFsError() if returning faulty from a this operation. */ CParams& noOvr() { m_cflags |= 0x1; return *this; } bool isNoOvr() const { return (m_cflags & 0x1) == 0x1; } /** Avoid returning an open stream to the caller. Usually, if create() is successful an open stream is returned. Conversely, if this flag is set, the create function will return 0, eventually closing immediately the handle to the file in those systems with "open creating" semantics. */ CParams& noStream() { m_cflags |= 0x2; return *this; } bool isNoStream() const { return (m_cflags & 0x2) == 0x2; } CParams& createMode( uint32 cm ) { m_cmode = cm; return *this; } uint32 createMode() const { return m_cmode; } }; inline const String& protocol() const { return m_servedProto; } /** Just an inline for opening file with default parameters. Default parameters are "read only, full sharing". */ inline Stream *open( const URI &uri ) { return open( uri, OParams() ); } /** Open a file. */ virtual Stream* open( const URI &uri, const OParams &p )=0; inline Stream* create( const URI &uri ) { bool dummy; return create( uri, CParams(), dummy ); } inline Stream* create( const URI& uri, bool &bSuccess ) { return create( uri, CParams(), bSuccess ); } inline Stream* create( const URI& uri, const CParams &p ) { bool dummy; return create( uri, p, dummy ); } virtual bool link( const URI &uri1, const URI &uri2, bool bSymbolic )=0; virtual bool unlink( const URI &uri )=0; virtual Stream *create( const URI &uri, const CParams &p, bool &bSuccess )=0; virtual DirEntry* openDir( const URI &uri )=0; virtual bool mkdir( const URI &uri, uint32 mode )=0; virtual bool rmdir( const URI &uri )=0; virtual bool move( const URI &suri, const URI &duri )=0; virtual bool readStats( const URI &uri, FileStat &s )=0; virtual bool writeStats( const URI &uri, const FileStat &s )=0; virtual bool chown( const URI &uri, int uid, int gid )=0; virtual bool chmod( const URI &uri, int mode )=0; /** Get an integer representing the last file system specific error. The semantic of this number may be different on different VFS, but in all the VFS a return value of 0 is granted to indicate that the last operation performed was succesful. Also, the returned error code must be made thread specific or otherwise reentrant/interlocked. */ virtual int64 getLastFsError()=0; /** Wraps the last system error into a suitable Falcon Error. If getLastFsError() returns 0, then this method will return 0 too. */ virtual Error *getLastError()=0; }; } #endif /* end of vsfprovider.h */ include/falcon/vm.h000066400000000000000000002732651176363201700145630ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vm.h Falcon virtual machine. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Dom, 08 Aug 2004 01:22:55 +0100 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_VM_H #define FLC_VM_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FALCON_VM_DFAULT_CHECK_LOOPS 5000 namespace Falcon { class Runtime; class VMachine; class PropertyTable; class AttribHandler; class MemPool; class VMMessage; class GarbageLock; typedef void (*tOpcodeHandler)( register VMachine *); void ContextList_deletor( void * ); class FALCON_DYN_CLASS ContextList: public List { friend void ContextList_deletor( void * ); public: ContextList() {} }; void opcodeHandler_END ( register VMachine *vm ); void opcodeHandler_NOP ( register VMachine *vm ); void opcodeHandler_PSHN( register VMachine *vm ); void opcodeHandler_RET ( register VMachine *vm ); void opcodeHandler_RETA( register VMachine *vm ); void opcodeHandler_PTRY( register VMachine *vm ); // Range 2: one parameter ops; void opcodeHandler_LNIL( register VMachine *vm ); void opcodeHandler_RETV( register VMachine *vm ); void opcodeHandler_FORK( register VMachine *vm ); void opcodeHandler_BOOL( register VMachine *vm ); void opcodeHandler_GENA( register VMachine *vm ); void opcodeHandler_GEND( register VMachine *vm ); void opcodeHandler_PUSH( register VMachine *vm ); void opcodeHandler_PSHR( register VMachine *vm ); void opcodeHandler_POP ( register VMachine *vm ); void opcodeHandler_JMP ( register VMachine *vm ); void opcodeHandler_INC ( register VMachine *vm ); void opcodeHandler_DEC ( register VMachine *vm ); void opcodeHandler_NEG ( register VMachine *vm ); void opcodeHandler_NOT ( register VMachine *vm ); void opcodeHandler_TRAL( register VMachine *vm ); void opcodeHandler_IPOP( register VMachine *vm ); void opcodeHandler_XPOP( register VMachine *vm ); void opcodeHandler_GEOR( register VMachine *vm ); void opcodeHandler_TRY ( register VMachine *vm ); void opcodeHandler_JTRY( register VMachine *vm ); void opcodeHandler_RIS ( register VMachine *vm ); void opcodeHandler_BNOT( register VMachine *vm ); void opcodeHandler_NOTS( register VMachine *vm ); void opcodeHandler_PEEK( register VMachine *vm ); // Range3: Double parameter ops; void opcodeHandler_LD ( register VMachine *vm ); void opcodeHandler_LDRF( register VMachine *vm ); void opcodeHandler_ADD ( register VMachine *vm ); void opcodeHandler_SUB ( register VMachine *vm ); void opcodeHandler_MUL ( register VMachine *vm ); void opcodeHandler_DIV ( register VMachine *vm ); void opcodeHandler_MOD ( register VMachine *vm ); void opcodeHandler_POW ( register VMachine *vm ); void opcodeHandler_ADDS( register VMachine *vm ); void opcodeHandler_SUBS( register VMachine *vm ); void opcodeHandler_MULS( register VMachine *vm ); void opcodeHandler_DIVS( register VMachine *vm ); void opcodeHandler_MODS( register VMachine *vm ); void opcodeHandler_BAND( register VMachine *vm ); void opcodeHandler_BOR ( register VMachine *vm ); void opcodeHandler_BXOR( register VMachine *vm ); void opcodeHandler_ANDS( register VMachine *vm ); void opcodeHandler_ORS ( register VMachine *vm ); void opcodeHandler_XORS( register VMachine *vm ); void opcodeHandler_GENR( register VMachine *vm ); void opcodeHandler_EQ ( register VMachine *vm ); void opcodeHandler_NEQ ( register VMachine *vm ); void opcodeHandler_GT ( register VMachine *vm ); void opcodeHandler_GE ( register VMachine *vm ); void opcodeHandler_LT ( register VMachine *vm ); void opcodeHandler_LE ( register VMachine *vm ); void opcodeHandler_IFT ( register VMachine *vm ); void opcodeHandler_IFF ( register VMachine *vm ); void opcodeHandler_CALL( register VMachine *vm ); void opcodeHandler_INST( register VMachine *vm ); void opcodeHandler_ONCE( register VMachine *vm ); void opcodeHandler_LDV ( register VMachine *vm ); void opcodeHandler_LDP ( register VMachine *vm ); void opcodeHandler_TRAN( register VMachine *vm ); void opcodeHandler_LDAS( register VMachine *vm ); void opcodeHandler_SWCH( register VMachine *vm ); void opcodeHandler_IN ( register VMachine *vm ); void opcodeHandler_NOIN( register VMachine *vm ); void opcodeHandler_PROV( register VMachine *vm ); void opcodeHandler_SSTA( register VMachine *vm ); void opcodeHandler_SVAL( register VMachine *vm ); void opcodeHandler_STPS( register VMachine *vm ); void opcodeHandler_STVS( register VMachine *vm ); void opcodeHandler_LSTA( register VMachine *vm ); void opcodeHandler_LVAL( register VMachine *vm ); void opcodeHandler_AND ( register VMachine *vm ); void opcodeHandler_OR ( register VMachine *vm ); void opcodeHandler_PASS( register VMachine *vm ); void opcodeHandler_PSIN( register VMachine *vm ); // Range 4: ternary opcodes; void opcodeHandler_STP ( register VMachine *vm ); void opcodeHandler_STV ( register VMachine *vm ); void opcodeHandler_LDVT( register VMachine *vm ); void opcodeHandler_LDPT( register VMachine *vm ); void opcodeHandler_STPR( register VMachine *vm ); void opcodeHandler_STVR( register VMachine *vm ); void opcodeHandler_TRAV( register VMachine *vm ); void opcodeHandler_SJMP( register VMachine *vm ); void opcodeHandler_INCP( register VMachine *vm ); void opcodeHandler_DECP( register VMachine *vm ); void opcodeHandler_SHL( register VMachine *vm ); void opcodeHandler_SHR( register VMachine *vm ); void opcodeHandler_SHLS( register VMachine *vm ); void opcodeHandler_SHRS( register VMachine *vm ); void opcodeHandler_CLOS( register VMachine *vm ); void opcodeHandler_PSHL( register VMachine *vm ); void opcodeHandler_POWS( register VMachine *vm ); void opcodeHandler_LSB( register VMachine *vm ); void opcodeHandler_SELE( register VMachine *vm ); void opcodeHandler_INDI( register VMachine *vm ); void opcodeHandler_STEX( register VMachine *vm ); void opcodeHandler_TRAC( register VMachine *vm ); void opcodeHandler_WRT( register VMachine *vm ); void opcodeHandler_STO( register VMachine *vm ); void opcodeHandler_FORB( register VMachine *vm ); void opcodeHandler_EVAL( register VMachine *vm ); void opcodeHandler_OOB( register VMachine *vm ); void opcodeHandler_TRDN( register VMachine *vm ); void opcodeHandler_EXEQ( register VMachine *vm ); class VMachine; class FALCON_DYN_CLASS VMBaton: public Baton { VMachine *m_owner; public: VMBaton( VMachine* owner ): Baton( true ), m_owner( owner ) {} virtual ~VMBaton() {} virtual void release(); virtual void onBlockedAcquire(); void releaseNotIdle(); }; /** The Falcon virtual machine. Virtual machine is in charge to execute Falcon bytecode. The virtual machine execution model is quite flexible, and allow infinite execution, step-by-step execution, limited runs and interrupts (event rising and debug requests) from external code while running. To run, the Virtual Machine needs to be properly setup. However, this "setup steps" are thought as customization steps so that the VM user is able to configure it on its needs, and they are both simple and effective. Minimal configuration needed requires to add a Runtime object, which holds the lists of all non-main modules. It is then possible to execute a routine exported by any of the modules in the runtime, or to provide a "running module", or "main module" with the setup() method. Once the startup symbol, or running module (or both) are set, the it is possible to call run(). \note It is possible that this interface will be simplified in the future so that only one method is used to set up the runtime and the start symbol or main module. \note This class implements the error handler interface because it may act as an error handler for the embedding components: compiler, assembler, module loader and runtime may all be embedded in a VM that may act as a referee for the script to be i.e. evaluating correctly a piece of falcon source code. */ class FALCON_DYN_CLASS VMachine: public BaseAlloc { friend class MemPool; public: /** This valuess indicates that the VM is in external execution mode. With this value in m_pc, the VM will execute the symbol found in m_symbol. */ enum { i_pc_call_request = 0xFFFFFFFF-sizeof(int32)*4, i_pc_redo_request =0xFFFFFFFF-sizeof(int32)*4, i_pc_call_external_ctor = 0xFFFFFFFF-sizeof(int32)*3, i_pc_call_external_ctor_return = 0xFFFFFFFF-sizeof(int32)*2, i_pc_call_external = 0xFFFFFFFF-sizeof(int32), i_pc_call_external_return = 0xFFFFFFFF }; protected: /** Structure used to save the vardefs during class link */ class VarDefMod: public BaseAlloc { public: VarDef *vd; LiveModule *lmod; }; mutable Mutex m_mtx; /** Mutex guarding the slot structure. */ mutable Mutex m_slot_mtx; /** Main module. This is the last linked module, that should also be the module where to search for start symbols; by default, launch() searches symbol here, and if not found, they search start symbols in globally exported symbol tables. */ LiveModule *m_mainModule; /** Space for immediate operands. */ Item m_imm[4]; Stream *m_stdIn; Stream *m_stdOut; Stream *m_stdErr; bool m_bhasStandardStreams; /** Number of opcodes that the current coroutine has performed. */ uint32 m_opCount; /** Maximum operations that can be performed */ uint32 m_opLimit; /** Timeout for GC collection */ uint32 m_loopsGC; /** Timeout for Context checks */ uint32 m_loopsContext; /** Timeout for Callbacks */ uint32 m_loopsCallback; /** True for single stepping */ bool m_bSingleStep; uint32 m_opNextGC; uint32 m_opNextContext; uint32 m_opNextCallback; /** Smaller check in op loops. */ uint32 m_opNextCheck; /** Generation at which the garbage data in this VM is marked. */ uint32 m_generation; /** Map of live modules. Todo: better docs. */ LiveModuleMap m_liveModules; /** This value indicate that there isn't any active try handler in the stack */ enum { i_noTryFrame = 0xFFFFFFFF }; /** Context currently being executed. */ VMContext *m_currentContext; /** Ready to run contexts. */ ContextList m_contexts; /** Contexts willing to sleep for a while */ ContextList m_sleepingContexts; /** Wether or not to allow a VM hostile takeover of the current context. */ bool m_allowYield; /** Execute at link time? */ bool m_launchAtLink; /** Opcode handler function calls. */ tOpcodeHandler *m_opHandlers; /** Map of global symbols (and the item they are connected to). Each item of the map contains a Symbol * and an ID that allows to */ SymModuleMap m_globalSyms; /** Map of well knwon symbols (and the item they are connected to). Each item of the map contains a Symbol * and an ID that allows to */ SymModuleMap m_wellKnownSyms; /** Puts the given context at sleep. This function inserts the given context in the sleep queue, inserting it in schedule order. Contexts waiting forever are put at bottom; contexts not wishing to sleep are put at top. */ void putAtSleep( VMContext *ctx ); /** Resort this context changing its position in the sleep list. Actually, this function works as putAtSleep(), but it continues to scan the sleeping context sleep till it finds the previous position of the context to remove it. */ void reschedule( VMContext *ctx ); /** Service recursive function called by LinkClass to create a class. */ bool linkSubClass( LiveModule *mod , const Symbol *clssym, Map &props, Map &states, ObjectFactory *factory ); friend class VMContext; friend class VMSemaphore; /** Subscribed services map. Services are allocated in the respective module. Currently, there's no way to remove a module from a VM once it's linked, so there's no need for a special de-subscription mechanism. Of course, if/when an unlink method is added, the module should also de-subscribe its services. */ Map m_services; /** User available pointer. \see userData( void * ); */ void *m_userData; /** System specific data. \see systemData(); */ Sys::SystemData m_systemData; CoreClass **m_metaClasses; Map m_slots; VMachine *m_nextVM; VMachine *m_prevVM; VMachine *m_idleNext; VMachine *m_idlePrev; /** If true, the VM allows to be blocked periodically by the GC in case of need. If false, the VM is left alone, and in case it becomes the VM having taken a GC scan for the last, its GC data gets promoted only if the GC enters the active state. */ bool m_bGcEnabled; /** Main synchronization baton. */ VMBaton m_baton; Mutex m_mtx_mesasges; VMMessage* m_msg_head; VMMessage* m_msg_tail; /** Event set by the VM to ask for priority GC. This is used by the performGC() function to inform the GC loop about the priority of this VM. */ bool m_bPirorityGC; /** Event set by the GC to confirm the forced GC loop is over. */ Event m_eGCPerformed; /** True when we want to wait for collection before being notified in priority scans. */ bool m_bWaitForCollect; /** Mutex for locked items ring. * -- unused; kept for binary compatibilty * */ Mutex m_mtx_lockitem; /** Locked and unreclaimable items are stored in this ring. * -- unused; kept for binary compatibilty * */ GarbageLock *m_lockRoot; /** Reference count. Usually, shouldn't be very high: the caller program, the GC and possibly some extra method in embedding applications. */ volatile int m_refcount; /** True if current frame should break */ bool m_break; /** Finalization hook for MT system. */ void (*m_onFinalize)(VMachine *vm); /** filtered load path */ String m_appSearchPath; /** random number generator */ MTRand _mtrand; //============================================================= // Private functions // /** Utility for switch opcode. Just pretend it's not here. */ bool seekInteger( int64 num, byte *base, uint16 size, uint32 &landing ) const; /** Utility for switch opcode. Just pretend it's not here. */ bool seekInRange( int64 num, byte *base, uint16 size, uint32 &landing ) const; /** Utility for switch opcode. Just pretend it's not here. */ bool seekString( const String *str, byte *base, uint16 size, uint32 &landing ) const; /** Utility for switch opcode. Just pretend it's not here. */ bool seekItem( const Item *item, byte *base, uint16 size, uint32 &landing ); /** Utility for select opcode. Just pretend it's not here. */ bool seekItemClass( const Item *obj, byte *base, uint16 size, uint32 &landing ) const; void internal_construct(); Item *parseSquareAccessor( const Item &accessed, String &accessor ) const; /** Returns the next NTD32 parameter, advancing the pointer to the next instruction */ int32 getNextNTD32() { register int32 ret = *reinterpret_cast( m_currentContext->code() + m_currentContext->pc_next() ); m_currentContext->pc_next() += sizeof( int32 ); return ret; } /** Returns the next NTD64 parameter, advancing the pointer to the next instruction */ int64 getNextNTD64() { register int64 ret = loadInt64( m_currentContext->code() + m_currentContext->pc_next() ); m_currentContext->pc_next() += sizeof( int64 ); return ret; } /** Gets the nth parameter of an opcode. */ Item *getOpcodeParam( register uint32 bc_pos ); /** Perform an item raise. This implements the "raise" keyword or "RIS" opcode, that is, takes an item and sends it to the relevant catch in the call hierarcy. If the item can't be caught by the current context, then it is eventually encapsulated in an error and thrown to the applicaiton with a C++ throw new Error*. If the item contains an instance of a Falcon Error class, the inner core Error* is taken and that is raised instead. After this call, if the item or error could be caught by the script, the context is prepared to run the error handler at the very next VM loop. @param value The item to be raised, possibly but not necessarily derived from a Falcon level Error class. */ void handleRaisedItem( Item& value ); /** Decides what to do with an error incoming in the main loop. Usually, this get either rethrown to the application or handled as an item down to the script. Stack is eventually unrolled till the item handler is found. */ void handleRaisedError( Error* err ); /** Performs periodic checks on the virtual machine. */ void periodicChecks(); /** Creates a new stack frame in the current context \param paramCount number of parameters in the stack \param frameEndFunc Callback function to be executed at frame end */ void createFrame( uint32 paramCount, ext_func_frame_t frameEndFunc = 0 ) { m_currentContext->prepareFrame( paramCount, frameEndFunc ); } /** Sets the currently running VM. The Currently running VM is the VM currently in control of garbage collecting and item creation. There can be only one current VM per thread; the value is stored in a thread specific variable. Accessing it can be relatively heavy, so it is highly advised not tu use it except when absolutely necessary to know the current vm and the value is currently unknown. \note Embedding applications are advised not to "corss VM", that is, not to nest call into different VMs in the same thread */ void setCurrent() const; /** Process all the pending messages. * Pending mesasges are sent to the processMessage() method, which * creates a coroutine context ready to be fired as soon as the VM * gets back in action. * * The processing order is not granted. */ void processPendingMessages(); /** Processes an incoming message. This searches for the slot requierd by the message; if it is found, the message is broadcast to the slot in a newly created coroutine, otherwise the onMessageComplete is immedately called. void terminateCurrentContext(); The message will be immediately destroyed if it can't be broadcast. \param msg The message to be processed. */ void processMessage( VMMessage* msg ); virtual bool linkDefinedSymbol( const Symbol *sym, LiveModule *lmod ); virtual bool linkUndefinedSymbol( const Symbol *sym, LiveModule *lmod ); virtual bool completeModLink( LiveModule *lmod ); virtual LiveModule *prelink( Module *mod, bool bIsMain, bool bPrivate ); void raiseHardError( int code, const String &expl, int32 line ); /** Gets the next item in a trav loop */ Item* getNextTravVar(); /** Prepare a coroutine context. The context is immediately swapped, so any operation performed by the caller is done on the new context. This allow to call an item right after coroutine creation. \param paramCount Number of parameters to be passed in the coroutine stack. */ VMContext* coPrepare( int32 paramCount ); /** Destroys the virtual machine. Protected as it can't be called directly. */ virtual ~VMachine(); public: /** Returns the currently running VM. The Currently running VM is the VM currently in control of garbage collecting and item creation. There can be only one current VM per thread; the value is stored in a thread specific variable. Accessing it can be relatively heavy, so it is highly advised not tu use it except when absolutely necessary to know the current vm and the value is currently unknown. \note Embedding applications are advised not to "corss VM", that is, not to nest call into different VMs in the same thread */ static VMachine *getCurrent(); /** Initialize VM from subclasses. Subclasses willing to provide their own initialization routine, or code wishing to configure the machine, may use this constructor to prevent the call of the init() routine of this class, which fills some configurable component of the machine. \param initItems false to prevent creation of base items \see init() */ VMachine( bool initItems ); /** Creates the virtual machine. The default constructor calss the virtual method init(). Subclasses may overload it to setup personalized VMs. \see init(). */ VMachine(); /** Initialize the virutal machine. Setups some configurable items that are used by the machine. They are namely: - m_stdIn: standard input. The default is to use an instance returned by stdInputStream(), which interfaces a localized text version of process stdin. - m_stdOut: standard output. The default is to use an instance returned by stdOutputStream(), which interfaces a localized text version of process stdout. - m_stdErr: standard error. The default is to use an instance returned by stdErrorStream(), which interfaces a localized text version of process stderr. void terminateCurrentContext(); The subclass should set up its own items, if the default ones are not suitable, and then call the base class init(). The base init will not overwrite those items if they are valorized, but it may configure them to set up them properly, so it should always be called even if all the items are set by the subclass. \see VMachine( bool ) */ void init(); /** Links a set of modules stored in a runtime. The modules that have been loaded and pre-linked by the runtime are correctly inserted in the VM. The topmost module in the Runtime is set as main module. After the link step, the runtime is not needed anymore and can be destroyed; the modules are safely referenced in the VM. In case of link error, the error handler of the VM is signaled and the function returns false, otherwise it returns true. If the VM hasn't an error handler set, nothing is signaled, but the error is still available after return through exitError() method. \note The main module is the module that is preferentially searched for start symbol(s) by prepare() function. \param rt the runtime to be linked \return The topmost module in the runtime turned into a livemoude, or zero on failure. */ virtual LiveModule* link( Runtime *rt ); /** Links a single module. The last-linked module is usually set as the main module, but it is possible to link a non-main module. After linking, the caller may release the reference if the module is needed only in this VM; the VM keeps a reference to the module. The VM holds a reference to the module also in case of errors: the module may be still needed for error reports or debug. To destroy definitely the module, the VM must be destroyed or the module must be unlinked. In case of link error, the error handler of the VM is signaled and the function returns false, otherwise it returns true. If the VM hasn't an error handler set, nothing is signaled, but the error is still available after return through exitError() method. \note The main module is the module that is preferentially searched for start symbol(s) by prepare() function. \param rt the runtime to be linked \param isMainModule false to prevent this module to be chosen as startup module. \param bPrivate false to allow this module to export the exported symbols, true to make the module private (and prevent exports). \return 0 time error, the internally built LiveModule instance on success. */ virtual LiveModule *link( Module *module, bool isMainModule=true, bool bPrivate = false ); /** Links a single symbol on an already existing live module. This won't actually link instances and classes which must be post-processed. \param sym The symbol to be linked. \param lmod The live module where the symbol must go. \return false if the link fails, true on success. */ virtual bool linkSymbol( const Symbol *sym, LiveModule *lmod ); /** Try to link a dynamic symbol. This method asks the modules that can provide dynamic symbols if they are interested to serve us with a symbol for the given name. If so, the symbol is linked, and a complete reference to its position (SymModule) is returned. The symbol needs not to be global to be exported this way. The providers may return private symbols that will be used just in this occasion and won't enter the global symbol map. The method may raise any error that linkSymbolComplete may raise. The same cares used for LinkSymbolComplete void terminateCurrentContext(); should be used. The method is virtual, so subclasses are able to create symbols dynamically by providing them directly. However, subclasses creating directly symbols must still create them inside a FlexyModule and use linkCompleteSymbol to bless the symbol in the VM. It is advisable to call the base class version of the method on subclass default. \param name The symbol to be searched for. \param symdata Coordinates of the linked symbol, on success. \return true on success, false if the symbol is not found or if it was found but couldn't be linked. */ virtual bool linkSymbolDynamic( const String &name, SymModule &symdata ); /** Links a class symbol. Class symbols must be specially post-processed after all the other symbols (including themselves) are pre-linked, so that cross references can be resolved. This method preforms the creation of a CoreClass that will reside in the live module passed as parameter. In debug, an assert checks that the incoming symbol is a class. If dynamically called, the caller should check that \b sym is a real class symbol. \param sym The class symbol to be linked. \param lmod The live module where the symbol must go. \return false if the link fails, true on success. */ virtual bool linkClassSymbol( const Symbol *sym, LiveModule *livemod ); /** Links a class instance. Class instances are what is commonly called "Singleton objects", declared through the "object" Falcon keyword. Class instances must be specially post-processed after all the other symbols and class declarations are pre-linked, so that cross references can be resolved. This method preforms the creation of a CoreClass that will reside in the live module passed as parameter. In debug, an assert checks that the incoming symbol is a class. If dynamically called, the caller should check that \b sym is a real class symbol. \param sym The instance symbol to be linked. \param lmod The live module where the symbol must go. \return false if the link fails, true on success. */ virtual bool linkInstanceSymbol( const Symbol *sym, LiveModule *livemod ); /** Constructs an instance calling its _init method if necessary. Instance construction must be performed after all the items are pre-linked, all the classes are formed and all the instances are set-up; in this way, it is possible for object _init method to refer to other objects, even if not yet initialized, as their structure is complete and coherent in the VM. The VM may call either its own VM bytecode or external functions; calls are performed in non-atomic mode. This means that this method should not be called from functions executed by the VM, normally. If this is necessary, then the call of this method must be surrounded by setting and resetting the atomic mode. \note Running in atomic mode doesn't mean "locking the VM"; the atomic mode is just a mode in which the VM runs without switching coroutines, accepting sleep requests or fulfilling suspension requests. Trying to suspend or prehempt the VM in atomic mode raises an unblockable exception, terminating the current script (and leaving it in a non-resumable status). \note This method won't apply the init state, if existing. \param sym The class instance to be initialized. \param lmod The live module where the symbol must go. \throw Error if the instance cannot be created. */ virtual void initializeInstance( const Symbol *sym, LiveModule *livemod ); /** Links a symbol eventually performing class and instances initializations. This method should be called when incrementally adding symbol once is knonw that the classes and objects they refer to are (or should) be already linked in the VM, in already existing live modules. This is the case of dynamic symbol creation in flexy modules, where the running scripts can create new symbols or invoke module actions that will create symbols; this is perfectly legal, but in that case the created instance will need to have everything prepared; cross references won't be resolved. This also means that the caller willing to link completely a new instance symbol must first link it's referenced class. This method should run in atomic mode (see initializeInstance() ). */ virtual bool linkCompleteSymbol( const Symbol *sym, LiveModule *livemod ); /** Links a symbol eventually performing class and instances initializations. This method resoves the module name into its liveMod before performing complete linking. This allows external elements (i.e. FALCON_FUNC methods) to create new symbols on the fly, declaring just into which module they should be created. If the target module is not found the method returns false, otherwise it calls directly linkCompleteSymbol( Symbol *, LiveModule * ) with the appropriate instance of the LiveModule. This method should run in atomic mode (see initializeInstance() ). \param sym Symbol created dynamically. \param moduleName name of the module that has created it and wants it to be inserted in the VM. */ virtual bool linkCompleteSymbol( Symbol *sym, const String &moduleName ); /** Returns the main module, if it exists. Returns an instance of the LiveModule class, that is the local representation of a module linked in the VM, that holds informations about the "main" module in this virtual machine. */ LiveModule *mainModule() const { return m_mainModule; } /** Unlinks all the modules in the runtime. The unlinked module(s) become unavailable, and all the callable items referencing symbols in the module become uncallable. Exported global variables survive unlinking, and their value can still be inspected, modified and discarded after they have been unlinked. It is not possible to unlink a module which is currently being run (that is, which is the module holding the currently executed symbol). It is possible to unlink the main module, but a new main module won't be automatically elected; unless the start symbol is exported by other modules, prepare() will fail if a new main module is not linked in the VM. The function may return false either if one of the module in the runtime is not present in the VM or if one of them is the "current module". However, some of the modules may have got unlinked int he meanwhile, and unlinking also dereferences them. \param rt the runtime with all the modules to be unlinked \return true on success, false on error. */ virtual bool unlink( const Runtime *rt ); /** Unlinks a module. The unlinked module become unavailable, and all the callable items referencing symbols in the module become uncallable. Exported global variables survive unlinking, and their value can still be inspected, modified and discarded after they have been unlinked. It is not possible to unlink a module which is currently being run (that is, which is the module holding the currently executed symbol). It is possible to unlink the main module, but a new main module won't be automatically elected; unless the start symbol is exported by other modules, prepare() will fail if a new main module is not linked in the VM. The function may return false either if the module is not present in the VM or if it is the "current module". \param module the module to be unlinked. \return true on success, false on error. */ virtual bool unlink( const Module *module ); /** Creates a new class live item. This function recursively resolves inheritences and constructor of classes to generate a "CoreClass", which is a template ready to create new instances. \param lmod the live module (module + live data) where this class is generated \param clsym the symbol where the class is defined. */ virtual CoreClass *linkClass( LiveModule *lmod, const Symbol *clsym ); /** Launches the "__main__" symbol. This is a proxy call to launch( "__main__" ); */ void launch() { launch( "__main__" ); } /** Launches a routine. This methods prepares the execution environment of the script by allocating the items that represents the global variables in the various modules. This step is called "realization" of the Runtime, as it provides a set of "real" (in the sense of effective) values for the items that are drawn in the library. This may be a relatively long step. If the VM had been already used to launch a routine, the status and memory are cleaned and everything is returned to the startup state. Then the routine whose name is given is searched in the top module (the one that has been added last to the runtime). If not found there, it is searched in the exported symbols of the whole runtime. If not given, the __main__ symbol of the top module is executed. If the required symbol cannot be found an error is raised and false is returned. The routine returns true if execution is successful; the VM may return because the routine terminates, because of an explicit END opcode and because execution limit (single step or limited step count) is matched. Also, uncaught exceptions or explicit requests for termination may cause the VM to return. It is possible to know if the execution is complete by looking at the last VM event with the lastEvent() method: if none or quit, then the execution is just terminated, else an event is set (i.e. tSuspend). \param startSym the name of a routine to be executed. \param paramCount Number of parameters that have been pushed in the stack as parameters. \throw CodeError* if the symbol to be launched couldn't be found. */ void launch( const String &startSym, uint32 paramCount = 0 ); /** Virtual machine main loop. This is the method that is responsible for the main virtual machine loop. The VM can be configured to run a limited number of steps (or even one); there are also several other settings that may affect the run behavior. Usually, the user program won't call this; the launch() method sets up the execution environment and then calls run(). Calling this method is useful to continue a pending execution (including single step execution). If a symbol name is provided, then the symbol is executed retaining all the current status. The symbol is searched in the locals of the top module, and then in the global of the runtime eaxtly as for launch(). This is useful, for example, to provide callback entry points in scripts that must maintain their execution status between calls. In this way, each run maintain the previous status but grants execution of different entry points; also this is quite faster than having to enter the realization phase. An error will be risen if launch has not been previously called and the routine name is not provided. */ virtual void run(); /** Become target of OS signals. */ bool becomeSignalTarget() { return m_systemData.becomeSignalTarget(); } /** Fills an error traceback with the current VM traceback. */ void fillErrorTraceback( Error &error ) { currentContext()->fillErrorTraceback( error ); } /** Returns a single step traceback in the current context. */ bool getTraceStep( uint32 level, const Symbol* &sym, uint32& line, uint32 &pc ) { return m_currentContext->getTraceStep( level, sym, line, pc ); } /** Get the caller of the current symbol. If the caller cannot be found (i.e. because the current symbol is called directly by the embedding program) the method returns false. \param sym on success, will hold a pointer to the symbol that called the current symbol. \param module on success, will hold a pointer to the module where the caller symbol resides. \return true on success, false on failure. */ bool getCaller( const Symbol *&sym, const Module *&module); /** Get the item that called the current symbol. If the caller cannot be found (i.e. because the current symbol is called directly by the embedding program) the method returns false. \param item on success, will hold the item (eventually the method) that originated the call. \param level previous callers desired (0 is the first caller). \return true on success, false on failure. */ bool getCallerItem( Item &caller, uint32 level=0 ); /** Fills an error with current VM execution context and traceback. */ void fillErrorContext( Error *err, bool filltb = true ); /** Returns the current stack as a reference. */ ItemArray &stack() { return m_currentContext->stack(); } /** Returns the current stack as a reference (const version). */ const ItemArray &stack() const { return m_currentContext->stack(); } /** Returns a reference to the nth item in the current stack. */ Item &stackItem( uint32 pos ) { return stack()[ pos ]; } /** Returns a reference to the nth item in the current stack (const version). */ const Item &stackItem( uint32 pos ) const { return stack()[pos]; } /** Returns the current module global variables vector. */ ItemArray ¤tGlobals() { return m_currentContext->globals(); } /** Returns the current module global variables vector (const version). */ const ItemArray ¤tGlobals() const { return m_currentContext->globals(); } /** Returns a reference to the nth item in the current module global variables vector. */ Item &moduleItem( uint32 pos ) { return currentGlobals()[ pos ]; } /** Returns a reference to the nth item in the current module global variables vector (const version). */ const Item &moduleItem( uint32 pos ) const { return currentGlobals()[ pos ]; } /** Returns the module in which the execution is currently taking place. */ const Module *currentModule() const { return m_currentContext->lmodule()->module(); } /** Returns the module in which the execution is currently taking place. */ LiveModule *currentLiveModule() const { return m_currentContext->lmodule(); } /** Find a linked module with a given name. Returns a pointer to the linked live module if the name exists, or 0 if the named module doesn't exist. */ LiveModule *findModule( const String &name ); /** Return the map of live modules ordered by name. \return the list of live (linked) modules. */ LiveModuleMap &liveModules() { return m_liveModules; } /** Return the map of live modules ordered by name (const version). \return the list of live (linked) modules. */ const LiveModuleMap &liveModules() const { return m_liveModules; } /** Returns the parameter count for the current function. \note Calling this when the VM is not engaged in executing a function will crash. \return parameter count for the current function. */ int32 paramCount() const { return currentContext()->paramCount(); } /** Returns the nth paramter passed to the VM. Const version of param(uint32). */ const Item *param( uint32 itemId ) const { return currentContext()->param( itemId ); } /** Returns the nth paramter passed to the VM. This is just the noncost version. The count is 0 based (0 is the first parameter). If the parameter exists, a pointer to the Item holding the parameter will be returned. If the item is a reference, the referenced item is returned instead (i.e. the parameter is dereferenced before the return). The pointer may be modified by the caller, but this will usually have no effect in the calling program unless the parameter has been passed by reference. \note Fetched item pointers are valid while the stack doesn't change. Pushes, addLocal(), item calls and VM operations may alter the stack. Using this method again after such operations allows to get a valid pointer to the desired item. Items extracted with this method can be also saved locally in an Item instance, at the cost of a a flat item copy (a few bytes). \param itemId the number of the parameter accessed, 0 based. \return a valid pointer to the (dereferenced) parameter or 0 if itemId is invalid. \see isParamByRef */ Item *param( uint32 itemId ) { return currentContext()->param( itemId ); } /** Returns the nth pre-paramter passed to the VM. Pre-parameters can be used to pass items to external functions. They are numbered 0...n in reverse push order, and start at the first push before the first real parameter. For example; \code Item *p0, *p1, *callable; ... vm->pushParameter( (int64) 0 ); // pre-parameter 1 vm->pushParameter( vm->self() ); // pre-parameter 0 vm->pushParameter( *p0 ); vm->pushParameter( *p1 ); vm->callFrame( *callable, 2 ); // 2 parameters \endcode */ Item *preParam( uint32 itemId ) { return currentContext()->preParam( itemId ); } /** Const version of preParam */ const Item *preParam( uint32 itemId ) const { return currentContext()->preParam( itemId ); } /** Returns true if the nth element of the current function has been passed by reference. \param itemId the number of the parameter accessed, 0 based. \return true if the parameter exists and has been passed by reference, false otherwise */ bool isParamByRef( uint32 itemId ) const { return currentContext()->isParamByRef( itemId ); } /** Returns the nth local item. The first variable in the local context is numbered 0. \note Fetched item pointers are valid while the stack doesn't change. Pushes, addLocal(), item calls and VM operations may alter the stack. Using this method again after such operations allows to get a valid pointer to the desired item again. Items extracted with this method can be also saved locally in an Item instance, at the cost of a a flat item copy (a few bytes). \param itemId the number of the local item accessed. \return a valid pointer to the (dereferenced) local variable or 0 if itemId is invalid. */ const Item *local( uint32 itemId ) const { return m_currentContext->local( itemId ); } /** Returns the nth local item. This is just the non-const version. The first variable in the local context is numbered 0. \param itemId the number of the local item accessed. \return a valid pointer to the (dereferenced) local variable or 0 if itemId is invalid. */ Item *local( uint32 itemId ) { return m_currentContext->local( itemId ); } const Item ®A() const { return m_currentContext->regA(); } Item ®A() { return m_currentContext->regA(); } const Item ®B() const { return m_currentContext->regB(); } Item ®B() { return m_currentContext->regB(); } const Item ®Bind() const { return m_currentContext->regBind(); } Item ®Bind() { return m_currentContext->regBind(); } const Item ®BindP() const { return m_currentContext->regBindP(); } Item ®BindP() { return m_currentContext->regBindP(); } const Item &self() const { return m_currentContext->self(); } Item &self() { return m_currentContext->self(); } const Item &felf() const { return m_currentContext->fself(); } Item &fself() { return m_currentContext->fself(); } /** Latch item. Generated on load property/vector instructions, it stores the accessed object. */ const Item &latch() const { return m_currentContext->latch(); } /** Latch item. Generated on load property/vector instructions, it stores the accessed object. */ Item &latch() { return m_currentContext->latch(); } /** Latcher item. Generated on load property/vector instructions, it stores the accessor item. */ const Item &latcher() const { return m_currentContext->latcher(); } /** Latcher item. Generated on load property/vector instructions, it stores the accessor item. */ Item &latcher() { return m_currentContext->latcher(); } void retval( bool val ) { m_currentContext->regA().setBoolean( val ); } void retval( int32 val ) { m_currentContext->regA().setInteger( (int64) val ); } void retval( int64 val ) { m_currentContext->regA().setInteger( val ); } void retval( numeric val ) { m_currentContext->regA().setNumeric( val ); } void retval( const Item &val ) { m_currentContext->regA() = val; } /** Returns a non garbageable string. */ void retval( String *cs ) { m_currentContext->regA().setString(cs); } /** Returns a garbageable string. \note to put a nongarbage string in the VM use regA() accessor, but you must know what you're doing. */ void retval( CoreString *cs ) { m_currentContext->regA().setString(cs); } void retval( CoreArray *ca ) { m_currentContext->regA().setArray(ca); } void retval( MemBuf* mb ) { m_currentContext->regA().setMemBuf(mb); } void retval( CoreDict *cd ) { m_currentContext->regA().setDict( cd ); } void retval( CoreObject *co ) { m_currentContext->regA().setObject(co); } void retval( CoreClass *cc ) { m_currentContext->regA().setClass(cc); } void retval( const String &cs ) { CoreString *cs1 = new CoreString( cs ); cs1->bufferize(); m_currentContext->regA().setString( cs1 ); } void retnil() { m_currentContext->regA().setNil();} const Symbol *currentSymbol() const { return m_currentContext->symbol(); } uint32 programCounter() const { return m_currentContext->pc(); } const SymModule *findGlobalSymbol( const String &str ) const; /** Make this context to sleep and elect a new one. If no other context can be elected, the VM may issue an onIdleTime() and eventually sleep a bit. */ void yield( numeric seconds ); void rotateContext(); void terminateCurrentContext(); /** Returns a well known item. A well known item is an item that does not phiscally resides in any module, and is at complete disposal of VM. Usually, System relevant classes as Error, TimeStamp, Stream and so on are considered WKI. Modules can declare their own WKIs so that they can safely retreive their own original live data that cannot be possibly transformed by scripts. Modules just need to declare a symbol adding the Symbol::setWKI clause, and the VM will create a proper entry on link step. WKI items are created for the module, but a safe copy is also saved in another location. WKI items have not to be exported, although they can. \note the data is not deep-copied. WKI are prevented to be chaged in their nature, but their content can still be changed. Please, notice that both flat items and classes are read-only from a script standpoint, while strings, arrays, objects and dictionaries can have their contents changed. \note The returned Well Known item is never to be de/referenced. \param name the WKI to be found \return 0 if a WKI with that name can't be found, a valid item pointer on success. */ Item *findWKI( const String &name ) const; /** Returns a live global given the name of its symbol. Items exported by moduels becomes associated with an item that can be accessed (and then changed) by both scripts and modules extensions. This method returns a pointer to the item that stores the current value of the given global symbol, and it is exactly the item that the scripts using that symbol are seeing. The returned item is already dereferenced. Changing it will change all items referred by this symbol in all the modules. \note To create failsafe new instances of classes or to access critical functions exported by modules, use findWKI(). \param name the symbol to be searched. \return the global item associated with the symbol if found, or 0 if the item does not exist. */ Item *findGlobalItem( const String &name ) const; /** Returns the value of a local variable. This function searces for a variable in the local context, and eventually in the global context if not locally found. The function also parses accessors as the "." dot operator or the [] array access operator, but it doesn't support range accessors or function calls. As the value of object accessor may be synthezized on the fly, the method cannot return a physical pointer; the value of the variable is flat-copied in the data parameter. \param name the variable to be found. \param data where to store the value if found. \return true if the value can be found, false otherwise. */ bool findLocalVariable( const String &name, Item &data ) const; /** Returns the value of a local variable. Similar to findLocalVariable(), but will return only the item coresponding to the named symbol. The symName parameter must be already trimmed and corespond exactly to the name of a variable in the local context. If the symbol is not found in the local symbol table, accessible global tables are searched in order of visibility. If the variable cannot be found, 0 is returned. \param symName the symbol name to be found \return the physical pointer to the variable storage, or 0 on failure. */ Item *findLocalSymbolItem( const String &symName ) const; /** Calls an item. The item may contain any valid callable object: - An external (C/C++) function. - A falcon function. - A method - A class External functions are immediately called, the flow of the VM being interrupted. Falcon function and method calls prepare this vm to execute the first instruction of the item, pushing a stack frame that allows RET and similar pcodes to return to the VM instruction after the one that was currently being executed. Control may or may not return immediately to the caller; if e_callNormal, e_callInst or e_callPassIn modes are used, the function returns only after the called item returns, else the function returns immediately and the vm is readied to continue execution from the new position. Class calls actually searches for the class constructor, and call that one. If the called class has no constructor, the function returns true but actually does nothing. Before calling this function, enough parameters must be pushed in the stack using pushParam() method. The paramCount parameter must be smaller or equal to the size of the stack, or an unblockable error will be raised. \param callable the item to be called. \param paramCount the number of elements in the stack to be considered parameters. \param mode the item call mode. */ void callItem( const Item &callable, int32 paramCount ) { callFrame( callable, paramCount ); execFrame(); } /** Shortcut for to call an item from a VM frame. Extension functions and VM/core functions meant to be called from the run() loop should use this function instead the callItem. The function prepares the VM to execute the desired item at the next run loop, as soon as the calling function returns. The caller should return immediately, or after a short cleanup, in case the call is succesful (and the function returns true). If the function needs to continue or do some post-processing after calling the callable item, it must install a return frame handler using returnHandler() */ void callFrame( const Item &callable, int32 paramCount ) { callable.readyFrame( this, paramCount ); } /** Prepares a VM call frame with a return handler. When the code flow will return to the calling run(), or when it will enter run() for the first time, the code in callable will be executed. At termination, the vm will immediately call callbackFunc that may create new frames and return true, to ask the VM to continue the evaluation, or return false and terminate this frame, effectively "returning" from the callable. */ void callFrame( const Item &callable, int32 paramCount, ext_func_frame_t callbackFunc ) { callable.readyFrame( this, paramCount ); m_currentContext->returnHandler( callbackFunc ); } /** Prepare a frame for a function call */ void prepareFrame( CoreFunc* cf, uint32 paramCount ); /** Prepare a frame for an array call. The function oesn't check for the array to be callable because it's supposed that the check is on the caller. However, a debug assert is performed. */ void prepareFrame( CoreArray* ca, uint32 paramCount ); /** Executes an executable item in a coroutine. \param callable The callable routine. \param paramCount Number of parameters to be passed in the coroutine stack. */ bool callCoroFrame( const Item &callable, int32 paramCount ); /** Executes currently prepared frame. */ void execFrame() { currentFrame()->m_break = true; // force to exit when meeting this limit m_currentContext->pc() = m_currentContext->pc_next(); run(); } StackFrame* currentFrame() const { return m_currentContext->currentFrame(); } VMContext* currentContext() const { return m_currentContext; } /** Resets the return handler and prepares to call given external handler. This function prepares the VM to execute a return handler immediately after the calling function returns. The net effect is that, when called from inside an extension function, the given callback will be called by the VM as the very next operation, after checks on events, timings and context switches. The difference with callFrame is that the stack is unaffected, and the called function will have the same call frame as the caller. The difference with returnHandler is that the VM is instructed to execute the retun procedure (that will call the given call back function) immediately, while returnHandler just sets an handler for a future time when the readied call frame will be unrolled. This function can be safely called from inside the same callback function, so to create loops in which each call goes through VM checks for operation counts and events. The callbackFunc should behave as specified for returnHandler(), returning true if creating another frame with callFrame() or calling another return handler (or even itself) using callFrameNow(). \note recallFrame() is a (more) efficient shortcut for using callFrameNow on the same calling function. \see returnHandler() \param callbackFunc the function to be called ASAP. */ void callFrameNow( ext_func_frame_t callbackFunc ); /** Prepare the VM to recall current return frame. Calling this method and returning true, an handler set with returnFrame() can instruct the VM to call itself again after having performed a loop check. \note This method must be called only inside extension functions, better if they are also return frame handlers. \see callFrameNow() */ void recallFrame() { m_currentContext->pc_next() = m_currentContext->pc(); } // reset pc advancement /** Call an item in atomic mode. This method is meant to call the vm run loop from inside another vm run loop. When this is necessary, the inner call must end as soon as possible. The VM becomes unprehemptible; contexts are never switched, operation count limits (except for hard limits) are not accounted and any explicit try to ask for the VM to suspend, wait, yield or sleep raises an unblockable error. Things to be called in atomic mode are i.e. small VM operations overload methods, as the toString or the compare. All the rest should be performed using the callFrame mechanism. \throw CodeError if the item is not callable. */ void callItemAtomic(const Item &callable, int32 paramCount ); /** Installs a post-processing return frame handler. The function passed as a parmeter will receive a pointer to this VM. The function MUST return true if it performs another frame item call. This will tell the VM that the stack cannot be freed now, as a new call stack has been prepared for immediate execution. When done, the function will be called again. A frame handler willing to call another frame and not willing to be called anymore must first unininstall itself by calling this method with parameters set at 0, and then it MUST return true. A frame handler not installing a new call frame MUST return false. This will terminate the current stack frame and cause the VM to complete the return stack. \param callbackFunct the return frame handler, or 0 to disinstall a previously set handler. */ void returnHandler( ext_func_frame_t callbackFunc ) { m_currentContext->returnHandler( callbackFunc ); } ext_func_frame_t returnHandler() const { return m_currentContext->returnHandler(); } /** Returns currently installed return handler, or zero if none. \return currently installed return handler, or zero if none. */ ext_func_frame_t returnHandler(); /** Alias for void pushParameter() */ void pushParam( const Item &item ) { m_currentContext->pushParam(item); } void pushParameter( const Item &item ) { m_currentContext->pushParam(item); } /** Adds some local space in the current context. \param amount how many local variables must be created */ void addLocals( uint32 space ) { m_currentContext->addLocals( space ); } byte operandType( byte opNum ) const { return m_currentContext->code()[m_currentContext->pc() + 1 + opNum]; } /** True if the VM is allowed to execute a context switch. */ bool allowYield() { return m_allowYield; } /** Change turnover mode. */ void allowYield( bool mode ) { m_allowYield = mode; } const ContextList *getCtxList() const { return &m_contexts; } const ContextList *getSleepingList() const { return &m_sleepingContexts; } /** Return from the last called subroutine. Usually used internally by the opcodes of the VM. */ void callReturn() { // if the stack frame requires an end handler... // ... but only if not unrolling a stack because of error... if ( currentFrame()->m_endFrameFunc != 0 ) { currentContext()->pc_next() = currentContext()->pc(); // if the frame requires to stay here, return immediately if ( currentFrame()->m_endFrameFunc( this ) ) { return; } } m_break = currentContext()->callReturn(); // if we have nowhere to return... if( currentContext()->currentFrame() == 0 && ! m_break ) { terminateCurrentContext(); } } /** Converts an item into a string. The string is NOT added to the garbage collecting system, so it may be disposed freely by the caller. This translates into a direct call to Item::toString(), unless the item is an object. In that case, if the item provides a toString() method, then that method is called passing the "format" parameter (only if it's not empty). \param itm a pointer to the item that must be represented as a string, \param format a format that will be eventually sent to the "toString" method of the object. \param target the output representation of the string */ void itemToString( String &target, const Item *itm, const String &format ); void itemToString( String &target, const Item *itm ) { itemToString( target, itm, "" ); } /** Creates the template for a core class. \param lmod the module id for which this class is being created. \param pt a ClassDef wise property table containing properties definition. \return a newly created property table ready to be added to a core class. */ PropertyTable *createClassTemplate( LiveModule *lmod, const Map &pt ); /** Publish a service. Will raise an error and return false if the service is already published. \param svr the service to be published on this VM. \throws CodeError on dupilcated names. */ void publishService( Service *svr ); /** Queries the VM for a published service. If a service with the given name has been published, the VM will return it; otherwise returns 0. \param name the service to be published on this VM. \return the required service or 0. */ Service *getService( const String &name ); /** Gets the standard input stream associated with this VM. VM may offer this stream to RTL and module functions wishing to get some input (as, i.e., input() ). By default, it is pointing to a correctly internationalized verisons of text-oriented Stream Transcoders. The transcoder is selected depending on the system environmental conditions, but there is no particular reason to use a transcoder instead of a direct stream, if this vm and functions using the standard streams abstractions held in the VM are willingt to operate on binary streams or on streams with different encodings. \return current incarnation of standard input stream. */ Stream *stdIn() const { return m_stdIn; } /** Gets the standard input stream associated with this VM. VM may offer this stream to RTL and module functions wishing to write generic output, as print, printl and inspect. By default, it is pointing to a correctly internationalized verisons of text-oriented Stream Transcoders. The transcoder is selected depending on the system environmental conditions, but there is no particular reason to use a transcoder instead of a direct stream, if this vm and functions using the standard streams abstractions held in the VM are willingt to operate on binary streams or on streams with different encodings. \return current incarnation of standard output stream. */ Stream *stdOut() const { return m_stdOut; } /** Gets the standard input stream associated with this VM. VM may offer this stream to RTL and module functions wishing to print error messages. By default, it is pointing to a correctly internationalized verisons of text-oriented Stream Transcoders. The transcoder is selected depending on the system environmental conditions, but there is no particular reason to use a transcoder instead of a direct stream, if this vm and functions using the standard streams abstractions held in the VM are willingt to operate on binary streams or on streams with different encodings. \note the VM uses the default std error stream to feed the standard Default Error Handler at its creation. That handler has its own copy of a StdErrorStream implementation, and changing this object won't affect the default error handler. If the error handler output has to be redirected as well, the new stream must be directly set. Also, be sure not to give ownership of the stream to the default handler, as giving ownership would cause the handler AND the VM to try to destroy the same pointer. \return current incarnation of standard error stream. */ Stream *stdErr() const { return m_stdErr; } /** Set stdandard input stream. Old standard input stream abstraction is destroyed (vm owns it). \see stdIn() \param nstream the new stream */ void stdIn( Stream *nstream ); /** Set stdandard output stream. Old standard output stream abstraction is destroyed (vm owns it). \see stdOut() \param nstream the new stream */ void stdOut( Stream *nstream ); /** Set stdandard error stream. Old standard error stream abstraction is destroyed (vm owns it). \note This doesn't changes the stream used by the standard error handler, which, by default, is set to a owned copy of this stream. \see stdOut() \param nstream the new stream */ void stdErr( Stream *nstream ) ; bool hasProcessStreams() const { return m_bhasStandardStreams; } /** Indicastes if this VM has been given standard streams control or not. VM in embedding applications hold generally streams that are not pointing to the process streams. Only direct itepreters (e.g. falcon command line) generally provides the VM with the process standard streams; normally, the process doesn't want the VM to use its standard stream, and even when it does, it doesn't want the VM (and the scripts) to be able to close the streams on its behalf. This is however desirable when scripts are running in a standalone environment, so that they can interact with other piped processes. In that case, both embedding applications, but more generally script interpreters as falcon command line, will give full stream ownership to the VM, and let them to possibly close the streams directly. \param b set to true to allow the scripts on this VM to close standard streams. */ void hasProcessStreams( bool b ) { m_bhasStandardStreams = b; } /** Return current module string at given ID. Retreives the module string which has been given the passed ID, and returns it to the caller. If the string is not found, a statically allocated empty string is returned instead. \param stringId the id for the string in the current module */ const String &moduleString( uint32 stringId ) const; /** Reset machine for a clean execution. This resets the VM to execute cleanly a ascript, removing all the dirty variables, execution context and the rest. */ virtual void reset(); void limitLoops( uint32 l ) { m_opLimit = l; } uint32 limitLoops() const { return m_opLimit; } bool limitLoopsHit() const { return m_opLimit >= m_opCount; } void resetCounters(); /** Performs a single VM step and return. */ void step() { m_currentContext->pc() = m_currentContext->pc_next(); m_bSingleStep = true; // stop next loop m_opNextCheck = m_opCount + 1; run(); } void singleStep( bool ss ) { m_bSingleStep = ss; } bool singleStep() const { return m_bSingleStep; } /** Periodic callback. This is the periodic callback routine. Subclasses may use this function to get called every now and then to i.e. stop the VM asynchronously, or to perform debugging. */ virtual void periodicCallback(); void callbackLoops( uint32 cl ) { m_loopsCallback = cl; } uint32 callbackLoops() const { return m_loopsCallback; } void gcCheckLoops( uint32 cl ) { m_loopsGC = cl; } uint32 gcCheckLoops() const { return m_loopsGC; } void contextCheckLoops( uint32 cl ) { m_loopsContext = cl; } uint32 contextCheckLoops() const { return m_loopsContext; } /** Return the loops that this VM has performed. There is no guarantee that the loops performed by the virtual machine hadn't overflown the uint32 size, nuless there is a physical loop limit set with loopLimit(). However, this counter can be useful to check different algorithm performances. */ uint32 elapsedLoops() const { return m_opCount; } /** Push current try position */ void pushTry( uint32 landingPC ) { currentContext()->pushTry( landingPC ); } /** Pop a try position, eventually changing the frame to the handler. */ void popTry( bool moveTo ) { currentContext()->popTry( moveTo ); } /** Elects a new context ready for execution. This method should be called by embedding applications that have performed a sleep operations right after the elapsed sleep time. The VM will elect the most suitable context for execution. On output, the VM will be ready to run (call the run() function); if no context is willing to run yet, there this method will set the eventSleep (hadSleepRequest() will return true), and the sleep request time will be propery set. */ void electContext(); /** Types of return values. When a function may return more than simply syccess or failure, this code specifies what went wrong in a VM function. */ typedef enum { return_ok, return_error_string, return_error_parse, return_error_internal, return_error_parse_fmt } returnCode; /** Performs a string expansion. \return a returnCode enumeration explaining possible errors in string expansion */ returnCode expandString( const String &src, String &target ); /** Creates a reference to an item. The source item is turned into a reference which is passed in the target item. If the source is already a reference, the reference is just passed in the target. \param target the item that will accept reference to source \param source the source item to be referenced */ void referenceItem( Item &target, Item &source ); /** Set user data. VM is passed to every extension function, and is also quite used by the embedding application. To provide application specific per-vm data to the scripts, the best solution for embedding applications is usually to extend the VM into a subclass. Contrarily, middleware extensions, as, for example, script plugins for applications, may prefer to use the standard Falcon VM and use this pointer to store application specific data. The VM makes no assumption on the kind of user data. The data is not destroyed at VM destruction. If there is the need to destroy the data at VM destruction, then VM derivation seems a more sensible choice. \param ud the application specific user data. */ void userData( void *ud ) { m_userData = ud; } /** Get the user data associated with this VM. \see userData( void * ) \return the previously set user data or 0 if user data is not set. */ void *userData() const { return m_userData; } /** Evaluate the item in a functional context. The function return either true or false, evaluating the item as a funcional token. # If the item is an array, it is recursively scanned. # If the item is a callable item (including callable arrayas), it is called and then the return value is evaluated in a non-functional context (plain evaluation). # In all the other cases, the item is evaluated in a non-functional context. More simply, if the item is callable is called, and is result is checked for Falcon truth value. If not, it's simply checked. The return value will be in regA(), and it's the "reduced" list of items. The function returns true if it has to stop for frame evaluation, false if the caller can loop. When the function returns true, the caller must immediately return to the calling frame; in case it needs to continue processing, it must install a frame return handler to be called back when the frame created by functionalEval is done. \param itm the item to be functionally evaluated. \param pcount the number of parameters in the VM to be treated as evaluation parameters. \param retArray true to force return of an evaluated array in case the input is not a Sigma sequence. \return false if the caller can proceed, true if it must return. */ bool functionalEval( const Item &itm, uint32 pcount=0, bool retArray = true ); /** Interrupts pending I/O on this machine from a separate thread. Interrupts compliant streams I/O and wait operations. The next I/O or the next wait, or the next compliant system blocking operation will cause an Interrupted exception to be raised. This method does not generate an "interrupt request" on the VM, that will keep on running until a blocking operation is encountered. This method can be safely called from other threads in the same application where the is currently executed, as well as from inside the same thread. */ void interrupt() { m_systemData.interrupt(); } /** Returns true if the VM has been interrupted. If an asynchronous interrupt has been generated, this method will return true. This is meant to be called from those compliant operations that require polling, or just before starting waiting on interruptable code to avoid useless calculations, or right after to know if the VM has been interrupted during the computation. \param raise if true, prepare an Interrupted exception in the VM context if the VM has been interrupted. \param reset if true, reset interrupted status now. \param dontCheck suppse interruption test is already done, and just raise the error and reset. \return true if the machine has been interrupted through interrupt() request. */ bool interrupted( bool raise = false, bool reset = false, bool dontCheck = false ); /** Get System Specific data. \returns system specific data bound with this machine. */ virtual const Sys::SystemData &systemData() const { return m_systemData; } /** Get System Specific data (non const). \returns system specific data bound with this machine. */ Sys::SystemData &systemData() { return m_systemData; } /** Exports a single symbol. This publishes a symbol on the global symbol map, and/or eventually to the WKS map. The function checks for the symbol to be exported and/or Well Known before actually performing the final export. */ virtual bool exportSymbol( const Symbol *sym, LiveModule *mod ); /** Exports all symbols in a module. To be called when changing the module publicity policy. */ virtual bool exportAllSymbols( LiveModule *mod ); /** Changes the status of launch-at-link mode */ void launchAtLink( bool mode ) { m_launchAtLink = mode; } /** Returns the launch-at-link mode. This method returns true if the VM will launch the __main__ symbol of modules as soon as they are linked in the vm, or false otherwise. */ bool launchAtLink() const { return m_launchAtLink; } /** Set current binding context. The current binding context is a dictionary containing a set of bound symbols and their value (referenced). Binding context is NOT GC masked, so it must exist elsewhere (i.e. in a live dictionary). The binding context is automatically removed at frame return. */ void setBindingContext( CoreDict *ctx ) { m_currentContext->regBind() = ctx; } /** Return the value associated with a binding symbol. This function searches the given binding symbol name in the current binding context and in all the visible contexts (?). If the function returns 0, the symbol is unbound. \param bind The binding symbol name. \return A valid non-dereferenced binding value or 0 if the symbol is unbound. */ Item *getBinding( const String &bind ) const; /** Return the value associated with a binding symbol, or creates one if not found. This function searches the given binding symbol name in the current binding context and in all the visible contexts (?). If the symbol is not found, it is created in the innermost visible context. If the function returns 0, then there is no visible context. \param bind The binding symbol name. \return A valid non-dereferenced binding value or 0 if there is no visible context. */ Item *getSafeBinding( const String &bind ); /** Set a binding value. This function sets a binding value in the current context. If a binding context has not been set, the function returns false. \param bind The binding symbol name. \param value The value to associate to this binding. */ bool setBinding( const String &bind, const Item &value ); typedef enum { lm_complete, lm_prelink, lm_postlink } t_linkMode; /** Request a constructor call after a call frame. If the preceding callFrame() was directed to an external function, requests the VM to treat the return value as an init() return, placing self() in regA() when all is done. */ void requestConstruct() { if( m_currentContext->pc_next() == i_pc_call_external ) m_currentContext->pc_next() = i_pc_call_external_ctor; } void setMetaClass( int itemID, CoreClass *metaClass ) { fassert( itemID >= 0 && itemID < FLC_ITEM_COUNT ); m_metaClasses[ itemID ] = metaClass; } CoreClass *getMetaClass( int itemID ) { fassert( itemID >= 0 && itemID < FLC_ITEM_COUNT ); return m_metaClasses[ itemID ]; } /** Gets a slot for a given message. If the slot doesn't exist, 0 is returned, unless create is set to true. In that case, the slot is created anew and returned. */ virtual CoreSlot* getSlot( const String& slotName, bool create = true ); /** Removes and dereference a message slot in the VM. */ virtual void removeSlot( const String& slotName ); /** Used by the garbage collector to accunt for items stored as slot callbacks. */ virtual void markSlots( uint32 mark ); /** Comsume the currently broadcast signal. This blocks the processing of signals to further listener of the currently broadcasting slot. \return true if the signal is consumed, false if there was no signal to consume. */ virtual bool consumeSignal(); /** Declares an IDLE section. In code sections where the VM is idle, it is granted not to change its internal structure. This allow inspection from outer code, as i.e. the garbage collector. \note Calls VM baton release (just candy grammar). \see baton() */ virtual void idle() { m_baton.release(); } /** Declares the end of an idle code section. \note Calls VM baton acquire (just candy grammar). \see baton() */ virtual void unidle() { m_baton.acquire(); } /** Enable the Garbage Collector requests on this VM. If \b mode is false, the VM never checks for garbage collector requests to block operations for detailed inspections. If \b mode is true, the VM periodically tells the GC that it is ready for inspection, and it is forced to honor block requests when memory gets critically high. However, if the GC is disabled, the VM may be inspected if it volountarily enters the idle state (sleep or I/O calls). \param mode True to allow forced periodic inspections in case of need. */ void gcEnable( bool mode ); bool isGcEnabled() const; /** Setup the main script standard parameters and variables. This is an utility function filling the follwing global variables, provided they have been linked and are globally exported from some module in the VM: - script_name: the logical module name of the main module. - script_path: physical path of the main module. - args: filled with argc and argv. */ virtual void setupScript( int argc, char** argv ); /** Class automating idle-unidle fragments. This purely inlined class automathises the task of calling unidle() as a function putting the VM in idle (i.e. I/O function) returns. */ class Pauser { VMachine *m_vm; public: inline Pauser( VMachine *vm ): m_vm( vm ) { m_vm->idle(); } inline ~Pauser() { m_vm->unidle(); } }; /** Accessor to the VM baton. Used to serialize concurrent access to this VM. */ const VMBaton& baton() const { return m_baton; } /** Accessor to the VM baton. Used to serialize concurrent access to this VM. */ VMBaton& baton() { return m_baton; } /** Send a message to the VMachine. If the virtual machine is currently idle, the message is immediately processed. Otherwise, it is posted to the main VM loop, and it is executed as soon as possible. The ownership of the message passes to the virtual machine, which will destroy it as the message is complete. The message is processed by broadcasting on the coresponding VM slot. */ void postMessage( VMMessage *vm ); /** Return current generation. */ uint32 generation() const; /** Force a GC collection loop on the virtual machine. Waits for the GC loop to be completed. The virtual machine must be in non-idle mode when calling this function, as the idle ownership is directly transferred to the GC and then back to the calling VM without interruption. Normallym the GC will notify the VM back as soon as the mark loop is over; If the VM wants to wait for the free memory to be collected, set the parameter to true. */ void performGC( bool bWaitForCollection = false ); /** Increments the reference count for this VMachine. */ void incref(); /** Decrements the reference count for this virtual machine. If the count hits zero, the virtual machine is immediately destroyed. */ void decref(); /** Terminates this VM activity. The thread creating the VM or the VM owner shall call this to inform the system that the VM will not be actvive anymore. The VM will be destroyed immediately or as soon as all the other references are released. This fucntion also dereference the VM once (in the behalf of the VM original owner). */ void finalize(); /** Finalization callback function (used by MT) */ void setFinalizeCallback( void (*finfunc)( VMachine* vm ) ) { m_onFinalize = finfunc; } /** Executes the return frame as soon as the control reaches the VM main loop. Can be used in conjunction with a fake call frame where the real work is done by the return frame function. \code static bool real_handler( VMachine *vm ) { //do real work return false; // remove frame? } void extension_func( VMachine *vm ) { vm->invokeReturnFrame( real_handler ); // prevent executing unexisting code in this frame. vm->addLocals( 5 ); // configure local variables... // return, and let the VM call the real_handler } \endcode. */ void invokeReturnFrame( ext_func_frame_t func ) { createFrame( 0, func ); m_currentContext->pc_next() = i_pc_call_external_return; } /** Get the default application load path. */ const String& appSearchPath() const { return m_appSearchPath; } /** Sets the default application load path (as seen by this vm). */ void appSearchPath( const String &p ) { m_appSearchPath = p; } /** Call back on sleep requests. This method is called back when the virtual machine detects the need to perform a pause. The default VMachine version calls the system "sleep" routine, but the application may find something more interesting to do. @note The application should eventually call idle() and unidle() respectively at enter and exit of this callback if it doesn't use the VM while in this routine. @param seconds Number of seconds (and fractions) that the VM is idle. @throws InterruptedError if the wait was interrupted. @throws CodeError if the wait is < 0 (infinite) and there are no active contexts able to wake up this one. */ virtual void onIdleTime( numeric seconds ); //TODO: change signature next version bool replaceMe_onIdleTime( numeric seconds ); /** Binds a late binding in the current context. As late bindings are known by name, only the name is necessary for the binding to complete. \param name the symbolic name of the binding. \param value the value to be associated with the late binding. */ void bindItem( const String& name, const Item& value ); /** Unbinds a late binding on the given target. If the lbind item is not a late binding, tgt is copied from this item. If lbind is a literal late binding, returns a non-literal late binding. If lbind is a non-literal late binding tries to resolve the late binding; in success placese the bound item (by value) in tgt. */ void unbindItem( const String& name, Item &tgt ) const; /** Change the position of the next executed instruction. */ void jump( uint32 pos ) { m_currentContext->pc_next() = pos; } /** Expand a number of variables from the current value of an iterator. Uses the current VM pc_next position to decode \b count variables stored in NOP opcodes, storing the current value of the iterator into them. * */ void expandTRAV( uint32 count, Iterator& iter ); void breakRequest( bool mode ) { m_break = mode; } bool breakRequest() const { return m_break; } /** Returns the Random Number Generator */ MTRand& getRNG(void) { return _mtrand; } //========================================================================== //========================================================================== //========================================================================== // Range 1: Parameterless ops /** End opcode handler. END opcode terminates current coroutine or current virtual machine execution. */ friend void opcodeHandler_END ( register VMachine *vm ); friend void opcodeHandler_NOP ( register VMachine *vm ); friend void opcodeHandler_PSHN( register VMachine *vm ); friend void opcodeHandler_RET ( register VMachine *vm ); friend void opcodeHandler_RETA( register VMachine *vm ); friend void opcodeHandler_PTRY( register VMachine *vm ); // Range 2: one parameter ops; friend void opcodeHandler_LNIL( register VMachine *vm ); friend void opcodeHandler_RETV( register VMachine *vm ); friend void opcodeHandler_FORK( register VMachine *vm ); friend void opcodeHandler_BOOL( register VMachine *vm ); friend void opcodeHandler_GENA( register VMachine *vm ); friend void opcodeHandler_GEND( register VMachine *vm ); friend void opcodeHandler_PUSH( register VMachine *vm ); friend void opcodeHandler_PSHR( register VMachine *vm ); friend void opcodeHandler_POP ( register VMachine *vm ); friend void opcodeHandler_JMP ( register VMachine *vm ); friend void opcodeHandler_INC ( register VMachine *vm ); friend void opcodeHandler_DEC ( register VMachine *vm ); friend void opcodeHandler_NEG ( register VMachine *vm ); friend void opcodeHandler_NOT ( register VMachine *vm ); friend void opcodeHandler_TRAL( register VMachine *vm ); friend void opcodeHandler_IPOP( register VMachine *vm ); friend void opcodeHandler_XPOP( register VMachine *vm ); friend void opcodeHandler_GEOR( register VMachine *vm ); friend void opcodeHandler_TRY ( register VMachine *vm ); friend void opcodeHandler_JTRY( register VMachine *vm ); friend void opcodeHandler_RIS ( register VMachine *vm ); friend void opcodeHandler_BNOT( register VMachine *vm ); friend void opcodeHandler_NOTS( register VMachine *vm ); friend void opcodeHandler_PEEK( register VMachine *vm ); // Range3: Double parameter ops; friend void opcodeHandler_LD ( register VMachine *vm ); friend void opcodeHandler_LDRF( register VMachine *vm ); friend void opcodeHandler_ADD ( register VMachine *vm ); friend void opcodeHandler_SUB ( register VMachine *vm ); friend void opcodeHandler_MUL ( register VMachine *vm ); friend void opcodeHandler_DIV ( register VMachine *vm ); friend void opcodeHandler_MOD ( register VMachine *vm ); friend void opcodeHandler_POW ( register VMachine *vm ); friend void opcodeHandler_ADDS( register VMachine *vm ); friend void opcodeHandler_SUBS( register VMachine *vm ); friend void opcodeHandler_MULS( register VMachine *vm ); friend void opcodeHandler_DIVS( register VMachine *vm ); friend void opcodeHandler_MODS( register VMachine *vm ); friend void opcodeHandler_BAND( register VMachine *vm ); friend void opcodeHandler_BOR ( register VMachine *vm ); friend void opcodeHandler_BXOR( register VMachine *vm ); friend void opcodeHandler_ANDS( register VMachine *vm ); friend void opcodeHandler_ORS ( register VMachine *vm ); friend void opcodeHandler_XORS( register VMachine *vm ); friend void opcodeHandler_GENR( register VMachine *vm ); friend void opcodeHandler_EQ ( register VMachine *vm ); friend void opcodeHandler_NEQ ( register VMachine *vm ); friend void opcodeHandler_GT ( register VMachine *vm ); friend void opcodeHandler_GE ( register VMachine *vm ); friend void opcodeHandler_LT ( register VMachine *vm ); friend void opcodeHandler_LE ( register VMachine *vm ); friend void opcodeHandler_IFT ( register VMachine *vm ); friend void opcodeHandler_IFF ( register VMachine *vm ); friend void opcodeHandler_CALL( register VMachine *vm ); friend void opcodeHandler_INST( register VMachine *vm ); friend void opcodeHandler_ONCE( register VMachine *vm ); friend void opcodeHandler_LDV ( register VMachine *vm ); friend void opcodeHandler_LDP ( register VMachine *vm ); friend void opcodeHandler_TRAN( register VMachine *vm ); friend void opcodeHandler_LDAS( register VMachine *vm ); friend void opcodeHandler_SWCH( register VMachine *vm ); friend void opcodeHandler_IN ( register VMachine *vm ); friend void opcodeHandler_NOIN( register VMachine *vm ); friend void opcodeHandler_PROV( register VMachine *vm ); friend void opcodeHandler_SSTA( register VMachine *vm ); friend void opcodeHandler_SVAL( register VMachine *vm ); friend void opcodeHandler_STPS( register VMachine *vm ); friend void opcodeHandler_STVS( register VMachine *vm ); friend void opcodeHandler_LSTA( register VMachine *vm ); friend void opcodeHandler_LVAL( register VMachine *vm ); friend void opcodeHandler_AND ( register VMachine *vm ); friend void opcodeHandler_OR ( register VMachine *vm ); friend void opcodeHandler_PASS( register VMachine *vm ); friend void opcodeHandler_PSIN( register VMachine *vm ); // Range 4: ternary opcodes; friend void opcodeHandler_STP ( register VMachine *vm ); friend void opcodeHandler_STV ( register VMachine *vm ); friend void opcodeHandler_LDVT( register VMachine *vm ); friend void opcodeHandler_LDPT( register VMachine *vm ); friend void opcodeHandler_STPR( register VMachine *vm ); friend void opcodeHandler_STVR( register VMachine *vm ); friend void opcodeHandler_TRAV( register VMachine *vm ); friend void opcodeHandler_INCP( register VMachine *vm ); friend void opcodeHandler_DECP( register VMachine *vm ); friend void opcodeHandler_SHL( register VMachine *vm ); friend void opcodeHandler_SHR( register VMachine *vm ); friend void opcodeHandler_SHLS( register VMachine *vm ); friend void opcodeHandler_SHRS( register VMachine *vm ); friend void opcodeHandler_CLOS( register VMachine *vm ); friend void opcodeHandler_PSHL( register VMachine *vm ); friend void opcodeHandler_POWS( register VMachine *vm ); friend void opcodeHandler_LSB( register VMachine *vm ); friend void opcodeHandler_SELE( register VMachine *vm ); friend void opcodeHandler_INDI( register VMachine *vm ); friend void opcodeHandler_STEX( register VMachine *vm ); friend void opcodeHandler_TRAC( register VMachine *vm ); friend void opcodeHandler_WRT( register VMachine *vm ); friend void opcodeHandler_STO( register VMachine *vm ); friend void opcodeHandler_FORB( register VMachine *vm ); friend void opcodeHandler_EVAL( register VMachine *vm ); friend void opcodeHandler_OOB( register VMachine *vm ); friend void opcodeHandler_TRDN( register VMachine *vm ); friend void opcodeHandler_EXEQ( register VMachine *vm ); }; class FALCON_DYN_SYM VMachineWrapper { private: VMachine *m_vm; public: VMachineWrapper(): m_vm( new VMachine ) { } VMachineWrapper( VMachine *host ): m_vm(host) {} ~VMachineWrapper() { m_vm->finalize(); } VMachine *operator->() const { return m_vm; } VMachine *vm() const { return m_vm; } }; } #endif /* end of vm.h */ include/falcon/vm_sys.h000066400000000000000000000046731176363201700154540ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vm_sys.h Implementation of virtual - non main loop ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 25 Apr 2008 17:30:00 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_VM_SYS_H #define FLC_VM_SYS_H #include #include namespace Falcon { namespace Sys { struct VM_SYS_DATA; /** System specific Falcon VM data. Events, mutexes, pipes, sockets and all what's needed to run the VM in specific systems. System specific structures are in vm_sys_*.h, while implementation of this functions are in vm_sys_*.cpp. The structures are opaque to the virtual machine, but they can be inspected by embedding applications and modules (i.e. for threading support). */ class FALCON_DYN_CLASS SystemData { protected: /** Parent VM */ VMachine *m_vm; public: struct VM_SYS_DATA *m_sysData; /** Creates the system data. This operation is granted to succeed. */ SystemData(VMachine *vm); /** Destroys VM specific system data. Will be performed only at ownwer vm's destruction. */ ~SystemData(); /** Called from VM::finalize() to stop any activity that may access VM when it's being freed. */ void earlyCleanup(); /** Checks wether the VM has been interrupted in a blocking wait or I/O. \return true if interrupted. */ bool interrupted() const; /** Safe interthread VM interruption request on blocking I/O. Will work only on compliant I/O and waits. */ void interrupt(); /** Clear interruption status. */ void resetInterrupt(); /** Wait for a given count of seconds. \return false if interrupted. */ bool sleep( numeric seconds ) const; /** Returns overall underlying system architecture type. It may be something as WIN or POSIX. More detailed informations about underlying systems can be retreived through modules. \return a static 7bit ASCII 0 terminated C string containing a very basic description of the underlying system overall architecture where this VM runs. */ static const char *getSystemType(); /** Send OS signals to parent VM. */ bool becomeSignalTarget(); }; } } #endif /* end of vm_sys.h */ include/falcon/vm_sys_posix.h000066400000000000000000000012471176363201700166700ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vm_sys_posix.h System specifics for the falcon VM - POSIX compliant systems. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 25 Apr 2008 17:30:00 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_VM_SYS_POSIX_H #define FLC_VM_SYS_POSIX_H namespace Falcon { namespace Sys { struct VM_SYS_DATA { int interruptPipe[2]; bool isSignalTarget; }; } } #endif /* end of vm_sys_posix.h */ include/falcon/vm_sys_win.h000066400000000000000000000012341176363201700163170ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vm_sys_win.h System specifics for the falcon VM - POSIX compliant systems. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 25 Apr 2008 17:30:00 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_VM_SYS_WIN_H #define FLC_VM_SYS_WIN_H #include namespace Falcon { namespace Sys { struct VM_SYS_DATA { HANDLE evtInterrupt; }; } } #endif /* end of vm_sys_win.h */ include/falcon/vmcontext.h000066400000000000000000000344711176363201700161620ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: flc_vmcontext.h Virtual Machine coroutine execution context. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mar nov 9 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Virtual Machine coroutine execution context. */ #ifndef flc_vmcontext_H #define flc_vmcontext_H #include #include #include #include #include #include #include namespace Falcon { class Symbol; class Item; class VMSemaphore; /** Class representing a coroutine execution context. */ class FALCON_DYN_CLASS VMContext: public BaseAlloc { Item m_regA; Item m_regB; //Item m_regS1; Item m_regL1; Item m_regL2; Item m_regBind; Item m_regBindP; VMSemaphore *m_sleepingOn; /** Currently executed symbol. May be 0 if the startmodule has not a "__main__" symbol; this should be impossible when things are set up properly. */ const Symbol* m_symbol; /** Module that contains the currently being executed symbol. */ LiveModule *m_lmodule; /** Point in time when this context is due to run again. (this is an absolute measure). */ numeric m_schedule; int32 m_priority; /** Program counter register. Current execution point in current code. */ uint32 m_pc; /** Next program counter register. This is the next instruction the VM has to execute. Call returns and jumps can easily modify the VM execution flow by changing this value, that is normally set just to m_pc + the length of the current instruction. */ uint32 m_pc_next; /** Topmost try frame handler. Inside a frame, the m_prevTryFrame points to the previous try frame, and m_tryPos points to the position in the stack of the current frame where the try try is to be restored. */ StackFrame* m_tryFrame; /** In atomic mode, the VM refuses to be kindly interrupted or to rotate contexts. */ bool m_atomicMode; friend class VMSemaphore; /** Stack of stack frames. * The topmost stack frame is that indicated here. */ StackFrame *m_frames; StackFrame *m_spareFrames; public: VMContext(); VMContext( const VMContext& other ); ~VMContext(); /** Wakes up the context after a wait. */ void wakeup( bool signaled = false ); void priority( int32 value ) { m_priority = value; } int32 priority() const { return m_priority; } void schedule( numeric value ) { m_schedule = value; } numeric schedule() const { return m_schedule; } /** Schedule this context after some time from now. This function add the current system time to secs and prepares this context to be scheduled after that absolute time. @param secs Number of seconds and fraction after current time. */ void scheduleAfter( numeric secs ); /** Return true if this is waiting forever on a semaphore signal */ bool isWaitingForever() const { return m_sleepingOn != 0 && m_schedule < 0; } VMSemaphore* waitingOn() const { return m_sleepingOn; } void waitOn( VMSemaphore* sem, numeric value=-1 ); void signaled(); //=========================== uint32& pc() { return m_pc; } const uint32& pc() const { return m_pc; } uint32& pc_next() { return m_pc_next; } const uint32& pc_next() const { return m_pc_next; } StackFrame* tryFrame() const { return m_tryFrame; } Item ®A() { return m_regA; } const Item ®A() const { return m_regA; } Item ®B() { return m_regB; } const Item ®B() const { return m_regB; } Item ®Bind() { return m_regBind; } const Item ®Bind() const { return m_regBind; } /* Item ®Bind() { return currentFrame()->m_binding; } const Item ®Bind() const { return currentFrame()->m_binding; } */ Item ®BindP() { return m_regBindP; } const Item ®BindP() const { return m_regBindP; } Item &self() { return currentFrame()->m_self; } const Item &self() const { return currentFrame()->m_self; } Item &fself() { return currentFrame()->m_fself; } const Item &fself() const { return currentFrame()->m_fself; } /** Latch item. Generated on load property/vector instructions, it stores the accessed object. */ const Item &latch() const { return m_regL1; } /** Latch item. Generated on load property/vector instructions, it stores the accessed object. */ Item &latch() { return m_regL1; } /** Latcher item. Generated on load property/vector instructions, it stores the accessor item. */ const Item &latcher() const { return m_regL2; } /** Latcher item. Generated on load property/vector instructions, it stores the accessor item. */ Item &latcher() { return m_regL2; } //=========================================== const ItemArray& stack() const { return m_frames->stack(); } ItemArray& stack() { return m_frames->stack(); } VMSemaphore *sleepingOn() const { return m_sleepingOn; } void sleepOn( VMSemaphore *sl ) { m_sleepingOn = sl; } /** Returns the current module global variables vector. */ ItemArray &globals() { return m_lmodule->globals(); } /** Returns the current module global variables vector (const version). */ const ItemArray &globals() const { return m_lmodule->globals(); } /** Returns the currently active live module. */ LiveModule *lmodule() const { return m_lmodule; } /** Changes the currently active live module. */ void lmodule(LiveModule *lm) { m_lmodule = lm; } /** Returns the currently active symbol. */ const Symbol *symbol() const { return m_symbol; } /** Changes the currently active symbol. */ void symbol( const Symbol* s ) { m_symbol = s; } /** Returns the current code. */ byte* code() const { fassert( symbol()->isFunction() ); return symbol()->getFuncDef()->code(); } /** The currently active frame in this context */ StackFrame* currentFrame() const { return m_frames; } void setFrames( StackFrame* newTop ) { m_frames = newTop; } /** Creates a stack frame taking a certain number of parameters. The stack is created so that it is ready to run the new context; use addFrame to add it at a later moment, or prepareFrame to create it and add it immediately. \param paramCount number of parameters in the stack \param frameEndFunc Callback function to be executed at frame end \return The newly created or recycled stack frame. */ StackFrame* createFrame( uint32 pcount, ext_func_frame_t frameEndFunc = 0 ); /** Creates a stack frame and adds it immediately to the stack list. This call is equivalent to addFrame( callFrame( pcount, frameEndFunc) ). \param paramCount number of parameters in the stack \param frameEndFunc Callback function to be executed at frame end \return The newly created or recycled stack frame. */ inline StackFrame* prepareFrame( uint32 pcount, ext_func_frame_t frameEndFunc = 0 ) { StackFrame* frame = createFrame( pcount, frameEndFunc ); addFrame( frame ); return frame; } /** Adds a prepared stack frame on top of the frame list. * */ void addFrame( StackFrame* frame ); /** Removes the topmost frame. * The frame can be deleted or disposed via disposeFrame for further recycling. * \return The just removed topmost frame or 0 if the frame stack is empty. */ StackFrame* popFrame(); void pushTry( uint32 locationPC ); void popTry( bool bMoveTo ); /** Returns from the current stack frame. Modifies context PC and PCNext, and reutrns true if this is a break context. */ bool callReturn(); bool atomicMode() const { return m_atomicMode; } void atomicMode( bool b ) { m_atomicMode = b; } /** Adds some space in the local stack for local variables. */ void addLocals( uint32 space ) { if ( stack().length() < space ) stack().resize( space ); } /** Returns the nth local item. The first variable in the local context is numbered 0. \note Fetched item pointers are valid while the stack doesn't change. Pushes, addLocal(), item calls and VM operations may alter the stack. Using this method again after such operations allows to get a valid pointer to the desired item again. Items extracted with this method can be also saved locally in an Item instance, at the cost of a a flat item copy (a few bytes). \param itemId the number of the local item accessed. \return a valid pointer to the (dereferenced) local variable or 0 if itemId is invalid. */ const Item *local( uint32 itemId ) const { return stack()[ itemId ].dereference(); } /** Returns the nth local item. This is just the non-const version. The first variable in the local context is numbered 0. \param itemId the number of the local item accessed. \return a valid pointer to the (dereferenced) local variable or 0 if itemId is invalid. */ Item *local( uint32 itemId ) { return stack()[ itemId ].dereference(); } /** Returns the parameter count for the current function. \note If the method as not a current frame, you'll have a crash. \return parameter count for the current function. */ int32 paramCount() const { fassert( m_frames != 0 ); return m_frames->m_param_count; } /** Returns the nth paramter passed to the VM. Const version of param(uint32). */ const Item *param( uint32 itemId ) const { if ( itemId >= m_frames->m_param_count ) return 0; return m_frames->m_params[ itemId ].dereference(); } /** Returns the nth paramter passed to the VM. This is just the noncost version. The count is 0 based (0 is the first parameter). If the parameter exists, a pointer to the Item holding the parameter will be returned. If the item is a reference, the referenced item is returned instead (i.e. the parameter is dereferenced before the return). The pointer may be modified by the caller, but this will usually have no effect in the calling program unless the parameter has been passed by reference. \note Fetched item pointers are valid while the stack doesn't change. Pushes, addLocal(), item calls and VM operations may alter the stack. Using this method again after such operations allows to get a valid pointer to the desired item. Items extracted with this method can be also saved locally in an Item instance, at the cost of a a flat item copy (a few bytes). \param itemId the number of the parameter accessed, 0 based. \return a valid pointer to the (dereferenced) parameter or 0 if itemId is invalid. \see isParamByRef */ Item *param( uint32 itemId ) { if ( itemId >= m_frames->m_param_count ) return 0; //return &m_frames->prev()->stack()[ m_frames->prev()->stack().length() - m_frames->m_param_count + itemId]; return m_frames->m_params[ itemId ].dereference(); } /** Returns the nth pre-paramter passed to the VM. Pre-parameters can be used to pass items to external functions. They are numbered 0...n in reverse push order, and start at the first push before the first real parameter. For example; \code Item *p0, *p1, *callable; ... vm->pushParameter( (int64) 0 ); // pre-parameter 1 vm->pushParameter( vm->self() ); // pre-parameter 0 vm->pushParameter( *p0 ); vm->pushParameter( *p1 ); vm->callFrame( *callable, 2 ); // 2 parameters \endcode */ Item *preParam( uint32 itemId ) { Item* params = m_frames->m_params; params -= 1 + itemId; return params->dereference(); } /** Const version of preParam */ const Item *preParam( uint32 itemId ) const { Item* params = m_frames->m_params; params -= 1 + itemId; return params->dereference(); } /** Returns true if the nth element of the current function has been passed by reference. \param itemId the number of the parameter accessed, 0 based. \return true if the parameter exists and has been passed by reference, false otherwise */ bool isParamByRef( uint32 itemId ) const { if ( itemId >= m_frames->m_param_count ) return 0; return m_frames->m_params[ itemId ].type() == FLC_ITEM_REFERENCE; } /** Installs a post-processing return frame handler. The function passed as a parmeter will receive a pointer to this VM. The function MUST return true if it performs another frame item call. This will tell the VM that the stack cannot be freed now, as a new call stack has been prepared for immediate execution. When done, the function will be called again. A frame handler willing to call another frame and not willing to be called anymore must first unininstall itself by calling this method with parameters set at 0, and then it MUST return true. A frame handler not installing a new call frame MUST return false. This will terminate the current stack frame and cause the VM to complete the return stack. \param callbackFunct the return frame handler, or 0 to disinstall a previously set handler. */ void returnHandler( ext_func_frame_t callbackFunc ) { currentFrame()->m_endFrameFunc = callbackFunc; } ext_func_frame_t returnHandler() const { return currentFrame()->m_endFrameFunc; } /** Pushes a parameter for the vm callItem and callFrame functions. \see callItem \see callFrame \param item the item to be passes as a parameter to the next call. */ void pushParam( const Item &item ) { stack().append(item); } StackFrame* allocFrame(); void disposeFrame( StackFrame* frame ); void disposeFrames( StackFrame* first, StackFrame* last ); void resetFrames(); void fillErrorTraceback( Error& err ); //! Gets a step in a traceback. bool getTraceStep( uint32 level, const Symbol* &sym, uint32& line, uint32 &pc ); }; } #endif /* end of flc_vmcontext.h */ include/falcon/vmevent.h000066400000000000000000000032271176363201700156120ustar00rootroot00000000000000/* FALCON - Falcon advanced simple text evaluator. FILE: VMEvent.h Special exception to communicate relevant events. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 08 Jul 2009 14:18:55 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_VMEVENT_H #define FALCON_VMEVENT_H #include #include namespace Falcon { /** Virtual machine events This class is thrown inside the VM to communicate special requests to the listening application. Type of requests are: - evQuit: Clean exit. The VM terminated. - evReturn: Some inner function asked immediate suspension of the VM; the VM is in a coherent state and can be reentered in any moment. - evOpLimit: The virtual machine exited due to excessive iterations. - evMemLimit: The virtual machine exited due to excessive memory consumption. - evDepthLimit: The virtual machine exited due to excessive depth of the stack calls. */ class VMEvent: public BaseAlloc { public: typedef enum { evQuit, evOpLimit, evMemLimit, evDepthLimit } tEvent; VMEvent( tEvent t ): m_type( t ) {} VMEvent( const VMEvent& other ): m_type( other.m_type ) {} ~VMEvent() {} tEvent type() const { return m_type; } private: tEvent m_type; }; class VMEventQuit: public VMEvent { public: VMEventQuit(): VMEvent( evQuit ) {} }; } #endif /* end of vmevent.h */ include/falcon/vmmaps.h000066400000000000000000000075241176363201700154350ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vmmaps.h Map items used in VM and related stuff ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer ott 20 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Map items used in VM and related stuff. */ #ifndef flc_vmmaps_H #define flc_vmmaps_H #include #include #include #include #include #include #include #include #include namespace Falcon { class Symbol; class Item; class LiveModule; /** Pair of the symbol and the module it is declared in. This is just a commodity class used to store the association between a certain symbol and the module it came from given the VM viewpoint (that is, the ID of the source module in the VM module list. The class may store either a global item (an item generated by an export request in the source module) or a well known item (an item generated via a setWKI request by the module), and it can never be both at the same time. */ class FALCON_DYN_CLASS SymModule: public BaseAlloc { Item *m_item; const Symbol *m_symbol; LiveModule *m_lmod; int32 m_wkiid; public: SymModule(): m_item(0), m_symbol(0), m_lmod(0), m_wkiid( -1 ) {} /** Creates an exported Global Item. */ SymModule( Item *itm, LiveModule *mod, const Symbol *sym ): m_item( itm ), m_symbol( sym ), m_lmod( mod ), m_wkiid( -1 ) {} /** Creates an exported Global Item. This shortcut initializes the item pointer atonomously. */ SymModule( LiveModule *mod, const Symbol *sym ); /** Creates an exported Well Known Item. */ SymModule( int32 wiid, LiveModule *mod, const Symbol *sym ): m_item( 0 ), m_symbol( sym ), m_lmod( mod ), m_wkiid( wiid ) {} /** Global item pointer. This pointers always points to a valid global variable in a vector inside LiveModule structure. As the global variable area never chages, the item pointer stays valid as long as the LiveModule in which it's stored is alive. \note If this SymModule refers to a WKI, the pointer is 0. \return a pointer to the referenced item. */ Item *item() const { return m_item; } const Symbol *symbol() const { return m_symbol; } uint32 symbolId() const { return m_symbol->itemId(); } LiveModule *liveModule() const { return m_lmod; } /** Well known item id. To find an item in the well known item array, it is necessary to use a local ID, as the array grows as the VM finds wki in the module. \note if the SymModule refers to a globally exported item, this id is -1 \return the id of the item in the WKI array. */ int32 wkiid() const { return m_wkiid; } }; class SymModuleTraits: public ElementTraits { public: virtual ~SymModuleTraits() {} virtual uint32 memSize() const; virtual void init( void *itemZone ) const; virtual void copy( void *targetZone, const void *sourceZone ) const; virtual int compare( const void *first, const void *second ) const; virtual void destroy( void *item ) const; virtual bool owning() const; }; namespace traits { extern SymModuleTraits &t_SymModule(); } /** Map of symbol names and module where they are located. (const String *, SymModule ) */ class FALCON_DYN_CLASS SymModuleMap: public Map { public: SymModuleMap(); }; /** Map of active modules in this VM. (const String *, LiveModule * ) */ class FALCON_DYN_CLASS LiveModuleMap: public Map { public: LiveModuleMap(); }; } #endif /* end of vmmaps.h */ include/falcon/vmmsg.h000066400000000000000000000064461176363201700152650ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: vmmsg.h Asynchronous message for the Virtual Machine. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 08 Feb 2009 16:08:50 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FLC_VMMSG_H #define FLC_VMMSG_H /** \file Asynchronous message for the Virtual Machine. */ #include #include #include #include namespace Falcon { class GarbageLock; class VMachine; class Error; /** Asynchronous message for the Virtual Machine. When the virtual machine receives this, it executes a broadcast loop on a coroutine as soon as the message reaches the main VM loop. Once done, the message owner is notified back via the onMessageComplete callback directly in the running VM thread. All the items used as parameters are garbage locked when given to the message, and garbage-unlocked on message destruction (which happens after the completion notify callback is called). */ class FALCON_DYN_CLASS VMMessage: public BaseAlloc { String m_msg; SafeItem *m_params; uint32 m_allocated; uint32 m_pcount; VMMessage *m_next; Error* m_error; public: /** Creates a VMMessage without parameters. \param msgName the name of the message. */ VMMessage( const String &msgName ); virtual ~VMMessage(); /** Returns the name of the message. */ const String& name() const { return m_msg; } /** Adds a paramter to the message. \param itm The item to be added (will be copied and garbage locked). */ void addParam( const SafeItem &itm ); /** Gets the number of parameters allocated in this message. */ uint32 paramCount() const {return m_pcount;} /** Gets the nth parameter of this message. */ SafeItem *param( uint32 p ) const; /** Called by the target VM when the message has been processed. The caller should create a subclass of VMMessage in case it needs to be notified about message completion and analyze asynchronous processing results. The base class implementation does nothing. The bProcessed parameter is set to true if at least one subscriber received the message, while it is set to false if the given VM hasn't the required slot, or if the slot is currently not subscribe by any listener. \note This call happens in the target VM thread. \param bProcessed true if called after a complete processing, false if the target VM didn't have active slots for this message. */ virtual void onMsgComplete( bool bProcessed ); /** Adds a message to be processed after this one. This method is called by the target VM to store an incoming message at the end of the message queue, but it may be also used by the message sender to send more than one message in one spot to the target VM. */ void append( VMMessage *msg ) { m_next = msg; } /** Gets the next message to be processed after this one. Should be called only by the target VM. */ VMMessage *next() const { return m_next; } }; } #endif /* end of vmmsg.h */ include/falcon/xtree_fix.h000066400000000000000000000751111176363201700161240ustar00rootroot00000000000000// tree internal header #if _MSC_VER > 1000 #pragma once #endif #ifndef _XTREE_ #define _XTREE_ #pragma message ("Using modified VC6 XTREE for Falcon...") #include #include #include #include #define _IsNil( x ) (_Left(x) == 0) #define _IsNotNil( x ) (_Left(x) != 0) #ifdef _MSC_VER #pragma pack(push,8) #endif /* _MSC_VER */ _STD_BEGIN // TEMPLATE CLASS _Tree template class _Tree { protected: enum _Redbl {_Red, _Black}; struct _Node; friend struct _Node; typedef _POINTER_X(_Node, _A) _Nodeptr; struct _Node { _Nodeptr _Left, _Parent, _Right; _Ty _Value; _Redbl _Color; }; typedef _REFERENCE_X(_Nodeptr, _A) _Nodepref; typedef _REFERENCE_X(const _K, _A) _Keyref; typedef _REFERENCE_X(_Redbl, _A) _Rbref; typedef _REFERENCE_X(_Ty, _A) _Vref; static _Rbref _Color(_Nodeptr _P) {return ((_Rbref)(*_P)._Color); } static _Keyref _Key(_Nodeptr _P) {return (_Kfn()(_Value(_P))); } static _Nodepref _Left(_Nodeptr _P) {return ((_Nodepref)(*_P)._Left); } static _Nodepref _Parent(_Nodeptr _P) {return ((_Nodepref)(*_P)._Parent); } static _Nodepref _Right(_Nodeptr _P) {return ((_Nodepref)(*_P)._Right); } static _Vref _Value(_Nodeptr _P) {return ((_Vref)(*_P)._Value); } public: typedef _Tree<_K, _Ty, _Kfn, _Pr, _A> _Myt; typedef _K key_type; typedef _Ty value_type; typedef _A::size_type size_type; typedef _A::difference_type difference_type; typedef _POINTER_X(_Ty, _A) _Tptr; typedef _POINTER_X(const _Ty, _A) _Ctptr; typedef _REFERENCE_X(_Ty, _A) reference; typedef _REFERENCE_X(const _Ty, _A) const_reference; // CLASS const_iterator class iterator; class const_iterator; friend class const_iterator; class const_iterator : public _Bidit<_Ty, difference_type> { public: const_iterator() {} const_iterator(_Nodeptr _P) : _Ptr(_P) {} const_iterator(const iterator& _X) : _Ptr(_X._Ptr) {} const_reference operator*() const {return (_Value(_Ptr)); } _Ctptr operator->() const {return (&**this); } const_iterator& operator++() {_Inc(); return (*this); } const_iterator operator++(int) {const_iterator _Tmp = *this; ++*this; return (_Tmp); } const_iterator& operator--() {_Dec(); return (*this); } const_iterator operator--(int) {const_iterator _Tmp = *this; --*this; return (_Tmp); } bool operator==(const const_iterator& _X) const {return (_Ptr == _X._Ptr); } bool operator!=(const const_iterator& _X) const {return (!(*this == _X)); } void _Dec() {if (_Color(_Ptr) == _Red && _Parent(_Parent(_Ptr)) == _Ptr) _Ptr = _Right(_Ptr); /* JC else if (_Left(_Ptr) != _Nil) */ else if ( _IsNotNil(_Left(_Ptr)) ) _Ptr = _Max(_Left(_Ptr)); else {_Nodeptr _P; while (_Ptr == _Left(_P = _Parent(_Ptr))) _Ptr = _P; _Ptr = _P; }} void _Inc() {/* JC if (_Right(_Ptr) != _Nil)*/ if ( _IsNotNil(_Right(_Ptr)) ) _Ptr = _Min(_Right(_Ptr)); else {_Nodeptr _P; while (_Ptr == _Right(_P = _Parent(_Ptr))) _Ptr = _P; if (_Right(_Ptr) != _P) _Ptr = _P; }} _Nodeptr _Mynode() const {return (_Ptr); } protected: _Nodeptr _Ptr; }; // CLASS iterator friend class iterator; class iterator : public const_iterator { public: iterator() {} iterator(_Nodeptr _P) : const_iterator(_P) {} reference operator*() const {return (_Value(_Ptr)); } _Tptr operator->() const {return (&**this); } iterator& operator++() {_Inc(); return (*this); } iterator operator++(int) {iterator _Tmp = *this; ++*this; return (_Tmp); } iterator& operator--() {_Dec(); return (*this); } iterator operator--(int) {iterator _Tmp = *this; --*this; return (_Tmp); } bool operator==(const iterator& _X) const {return (_Ptr == _X._Ptr); } bool operator!=(const iterator& _X) const {return (!(*this == _X)); } }; typedef reverse_bidirectional_iterator reverse_iterator; typedef reverse_bidirectional_iterator const_reverse_iterator; typedef pair _Pairib; typedef pair _Pairii; typedef pair _Paircc; explicit _Tree(const _Pr& _Parg, bool _Marg = true, const _A& _Al = _A()) : allocator(_Al), key_compare(_Parg), _Multi(_Marg) {_Init(); } _Tree(const _Ty *_F, const _Ty *_L, const _Pr& _Parg, bool _Marg = true, const _A& _Al = _A()) : allocator(_Al), key_compare(_Parg), _Multi(_Marg) {_Init(); insert(_F, _L); } _Tree(const _Myt& _X) : allocator(_X.allocator), key_compare(_X.key_compare), _Multi(_X._Multi) {_Init(); _Copy(_X); } ~_Tree() {erase(begin(), end()); _Freenode(_Head); _Head = 0, _Size = 0; _Nodeptr _Tmp = 0; {_Lockit _Lk; if (--_Nilrefs == 0) {_Tmp = _Nil; _Nil = 0; }} if (_Tmp != 0) _Freenode(_Tmp); } _Myt& operator=(const _Myt& _X) {if (this != &_X) {erase(begin(), end()); key_compare = _X.key_compare; _Copy(_X); } return (*this); } iterator begin() {return (iterator(_Lmost())); } const_iterator begin() const {return (const_iterator(_Lmost())); } iterator end() {return (iterator(_Head)); } const_iterator end() const {return (const_iterator(_Head)); } reverse_iterator rbegin() {return (reverse_iterator(end())); } const_reverse_iterator rbegin() const {return (const_reverse_iterator(end())); } reverse_iterator rend() {return (reverse_iterator(begin())); } const_reverse_iterator rend() const {return (const_reverse_iterator(begin())); } size_type size() const {return (_Size); } size_type max_size() const {return (allocator.max_size()); } bool empty() const {return (size() == 0); } _A get_allocator() const {return (allocator); } _Pr key_comp() const {return (key_compare); } _Pairib insert(const value_type& _V) {_Nodeptr _X = _Root(); _Nodeptr _Y = _Head; bool _Ans = true; /* JC while (_X != _Nil) */ while( _IsNotNil(_X) ) {_Y = _X; _Ans = key_compare(_Kfn()(_V), _Key(_X)); _X = _Ans ? _Left(_X) : _Right(_X); } if (_Multi) return (_Pairib(_Insert(_X, _Y, _V), true)); iterator _P = iterator(_Y); if (!_Ans) ; else if (_P == begin()) return (_Pairib(_Insert(_X, _Y, _V), true)); else --_P; if (key_compare(_Key(_P._Mynode()), _Kfn()(_V))) return (_Pairib(_Insert(_X, _Y, _V), true)); return (_Pairib(_P, false)); } iterator insert(iterator _P, const value_type& _V) {if (size() == 0) ; else if (_P == begin()) {if (key_compare(_Kfn()(_V), _Key(_P._Mynode()))) return (_Insert(_Head, _P._Mynode(), _V)); } else if (_P == end()) {if (key_compare(_Key(_Rmost()), _Kfn()(_V))) return (_Insert(_Nil, _Rmost(), _V)); } else {iterator _Pb = _P; if (key_compare(_Key((--_Pb)._Mynode()), _Kfn()(_V)) && key_compare(_Kfn()(_V), _Key(_P._Mynode()))) { /* JC if (_Right(_Pb._Mynode()) == _Nil) */ if ( _IsNil(_Right(_Pb._Mynode())) ) return (_Insert(_Nil, _Pb._Mynode(), _V)); else return (_Insert(_Head, _P._Mynode(), _V)); }} return (insert(_V).first); } void insert(iterator _F, iterator _L) {for (; _F != _L; ++_F) insert(*_F); } void insert(const value_type *_F, const value_type *_L) {for (; _F != _L; ++_F) insert(*_F); } iterator erase(iterator _P) {_Nodeptr _X; _Nodeptr _Y = (_P++)._Mynode(); _Nodeptr _Z = _Y; /* JC if (_Left(_Y) == _Nil) */ if ( _IsNil(_Left(_Y)) ) _X = _Right(_Y); /* JC else if (_Right(_Y) == _Nil) */ else if (_IsNil(_Right(_Y))) _X = _Left(_Y); else _Y = _Min(_Right(_Y)), _X = _Right(_Y); { _Lockit _Lk; if (_Y != _Z) {_Parent(_Left(_Z)) = _Y; _Left(_Y) = _Left(_Z); if (_Y == _Right(_Z)) _Parent(_X) = _Y; else {_Parent(_X) = _Parent(_Y); _Left(_Parent(_Y)) = _X; _Right(_Y) = _Right(_Z); _Parent(_Right(_Z)) = _Y; } if (_Root() == _Z) _Root() = _Y; else if (_Left(_Parent(_Z)) == _Z) _Left(_Parent(_Z)) = _Y; else _Right(_Parent(_Z)) = _Y; _Parent(_Y) = _Parent(_Z); std::swap(_Color(_Y), _Color(_Z)); _Y = _Z; } else {_Parent(_X) = _Parent(_Y); if (_Root() == _Z) _Root() = _X; else if (_Left(_Parent(_Z)) == _Z) _Left(_Parent(_Z)) = _X; else _Right(_Parent(_Z)) = _X; if (_Lmost() != _Z) ; /* JC else if (_Right(_Z) == _Nil) */ else if ( _IsNil(_Right(_Z)) ) _Lmost() = _Parent(_Z); else _Lmost() = _Min(_X); if (_Rmost() != _Z) ; /* JC else if (_Left(_Z) == _Nil) */ else if ( _IsNil(_Left(_Z)) ) _Rmost() = _Parent(_Z); else _Rmost() = _Max(_X); } if (_Color(_Y) == _Black) {while (_X != _Root() && _Color(_X) == _Black) if (_X == _Left(_Parent(_X))) {_Nodeptr _W = _Right(_Parent(_X)); if (_Color(_W) == _Red) {_Color(_W) = _Black; _Color(_Parent(_X)) = _Red; _Lrotate(_Parent(_X)); _W = _Right(_Parent(_X)); } if (_Color(_Left(_W)) == _Black && _Color(_Right(_W)) == _Black) {_Color(_W) = _Red; _X = _Parent(_X); } else {if (_Color(_Right(_W)) == _Black) {_Color(_Left(_W)) = _Black; _Color(_W) = _Red; _Rrotate(_W); _W = _Right(_Parent(_X)); } _Color(_W) = _Color(_Parent(_X)); _Color(_Parent(_X)) = _Black; _Color(_Right(_W)) = _Black; _Lrotate(_Parent(_X)); break; }} else {_Nodeptr _W = _Left(_Parent(_X)); if (_Color(_W) == _Red) {_Color(_W) = _Black; _Color(_Parent(_X)) = _Red; _Rrotate(_Parent(_X)); _W = _Left(_Parent(_X)); } if (_Color(_Right(_W)) == _Black && _Color(_Left(_W)) == _Black) {_Color(_W) = _Red; _X = _Parent(_X); } else {if (_Color(_Left(_W)) == _Black) {_Color(_Right(_W)) = _Black; _Color(_W) = _Red; _Lrotate(_W); _W = _Left(_Parent(_X)); } _Color(_W) = _Color(_Parent(_X)); _Color(_Parent(_X)) = _Black; _Color(_Left(_W)) = _Black; _Rrotate(_Parent(_X)); break; }} _Color(_X) = _Black; } } _Destval(&_Value(_Y)); _Freenode(_Y); --_Size; return (_P); } iterator erase(iterator _F, iterator _L) {if (size() == 0 || _F != begin() || _L != end()) {while (_F != _L) erase(_F++); return (_F); } else {_Erase(_Root()); _Root() = _Nil, _Size = 0; _Lmost() = _Head, _Rmost() = _Head; return (begin()); }} size_type erase(const _K& _X) {_Pairii _P = equal_range(_X); size_type _N = 0; _Distance(_P.first, _P.second, _N); erase(_P.first, _P.second); return (_N); } void erase(const _K *_F, const _K *_L) {for (; _F != _L; ++_F) erase(*_F); } void clear() {erase(begin(), end()); } iterator find(const _K& _Kv) {iterator _P = lower_bound(_Kv); return (_P == end() || key_compare(_Kv, _Key(_P._Mynode())) ? end() : _P); } const_iterator find(const _K& _Kv) const {const_iterator _P = lower_bound(_Kv); return (_P == end() || key_compare(_Kv, _Key(_P._Mynode())) ? end() : _P); } size_type count(const _K& _Kv) const {_Paircc _Ans = equal_range(_Kv); size_type _N = 0; _Distance(_Ans.first, _Ans.second, _N); return (_N); } iterator lower_bound(const _K& _Kv) {return (iterator(_Lbound(_Kv))); } const_iterator lower_bound(const _K& _Kv) const {return (const_iterator(_Lbound(_Kv))); } iterator upper_bound(const _K& _Kv) {return (iterator(_Ubound(_Kv))); } const_iterator upper_bound(const _K& _Kv) const {return (iterator(_Ubound(_Kv))); } _Pairii equal_range(const _K& _Kv) {return (_Pairii(lower_bound(_Kv), upper_bound(_Kv))); } _Paircc equal_range(const _K& _Kv) const {return (_Paircc(lower_bound(_Kv), upper_bound(_Kv))); } void swap(_Myt& _X) {std::swap(key_compare, _X.key_compare); if (allocator == _X.allocator) {std::swap(_Head, _X._Head); std::swap(_Multi, _X._Multi); std::swap(_Size, _X._Size); } else {_Myt _Ts = *this; *this = _X, _X = _Ts; }} friend void swap(_Myt& _X, _Myt& _Y) {_X.swap(_Y); } protected: static _Nodeptr _Nil; static size_t _Nilrefs; void _Copy(const _Myt& _X) {_Root() = _Copy(_X._Root(), _Head); _Size = _X.size(); /* JC if (_Root() != _Nil ) */ if ( _IsNotNil(_Root()) ) {_Lmost() = _Min(_Root()); _Rmost() = _Max(_Root()); } else _Lmost() = _Head, _Rmost() = _Head; } _Nodeptr _Copy(_Nodeptr _X, _Nodeptr _P) {_Nodeptr _R = _X; /* JC1 for (; _X != _Nil; _X = _Left(_X)) */ for (; _IsNotNil(_X); _X = _Left(_X)) {_Nodeptr _Y = _Buynode(_P, _Color(_X)); if (_R == _X) _R = _Y; _Right(_Y) = _Copy(_Right(_X), _Y); _Consval(&_Value(_Y), _Value(_X)); _Left(_P) = _Y; _P = _Y; } _Left(_P) = _Nil; return (_R); } void _Erase(_Nodeptr _X) {/* JC for (_Nodeptr _Y = _X; _Y != _Nil; _X = _Y) */ for (_Nodeptr _Y = _X; _IsNotNil(_Y); _X = _Y) {_Erase(_Right(_Y)); _Y = _Left(_Y); _Destval(&_Value(_X)); _Freenode(_X); }} void _Init() {_Nodeptr _Tmp = _Buynode(0, _Black); {_Lockit _Lk; if (_Nil == 0) {_Nil = _Tmp; _Tmp = 0; _Left(_Nil) = 0, _Right(_Nil) = 0; } ++_Nilrefs; } if (_Tmp != 0) _Freenode(_Tmp); _Head = _Buynode(_Nil, _Red), _Size = 0; _Lmost() = _Head, _Rmost() = _Head; } iterator _Insert(_Nodeptr _X, _Nodeptr _Y, const _Ty& _V) {_Nodeptr _Z = _Buynode(_Y, _Red); _Left(_Z) = _Nil, _Right(_Z) = _Nil; _Consval(&_Value(_Z), _V); ++_Size; /* JC if (_Y == _Head || _X != _Nil */ if (_Y == _Head || _IsNotNil(_X) || key_compare(_Kfn()(_V), _Key(_Y))) {_Left(_Y) = _Z; if (_Y == _Head) {_Root() = _Z; _Rmost() = _Z; } else if (_Y == _Lmost()) _Lmost() = _Z; } else {_Right(_Y) = _Z; if (_Y == _Rmost()) _Rmost() = _Z; } for (_X = _Z; _X != _Root() && _Color(_Parent(_X)) == _Red; ) if (_Parent(_X) == _Left(_Parent(_Parent(_X)))) {_Y = _Right(_Parent(_Parent(_X))); if (_Color(_Y) == _Red) {_Color(_Parent(_X)) = _Black; _Color(_Y) = _Black; _Color(_Parent(_Parent(_X))) = _Red; _X = _Parent(_Parent(_X)); } else {if (_X == _Right(_Parent(_X))) {_X = _Parent(_X); _Lrotate(_X); } _Color(_Parent(_X)) = _Black; _Color(_Parent(_Parent(_X))) = _Red; _Rrotate(_Parent(_Parent(_X))); }} else {_Y = _Left(_Parent(_Parent(_X))); if (_Color(_Y) == _Red) {_Color(_Parent(_X)) = _Black; _Color(_Y) = _Black; _Color(_Parent(_Parent(_X))) = _Red; _X = _Parent(_Parent(_X)); } else {if (_X == _Left(_Parent(_X))) {_X = _Parent(_X); _Rrotate(_X); } _Color(_Parent(_X)) = _Black; _Color(_Parent(_Parent(_X))) = _Red; _Lrotate(_Parent(_Parent(_X))); }} _Color(_Root()) = _Black; return (iterator(_Z)); } _Nodeptr _Lbound(const _K& _Kv) const {_Nodeptr _X = _Root(); _Nodeptr _Y = _Head; /* JC while (_X != _Nil) */ while ( _IsNotNil(_X) ) if (key_compare(_Key(_X), _Kv)) _X = _Right(_X); else _Y = _X, _X = _Left(_X); return (_Y); } _Nodeptr& _Lmost() {return (_Left(_Head)); } _Nodeptr& _Lmost() const {return (_Left(_Head)); } void _Lrotate(_Nodeptr _X) {_Nodeptr _Y = _Right(_X); _Right(_X) = _Left(_Y); /* JC if (_Left(_Y) != _Nil) */ if ( _IsNotNil(_Left(_Y)) ) _Parent(_Left(_Y)) = _X; _Parent(_Y) = _Parent(_X); if (_X == _Root()) _Root() = _Y; else if (_X == _Left(_Parent(_X))) _Left(_Parent(_X)) = _Y; else _Right(_Parent(_X)) = _Y; _Left(_Y) = _X; _Parent(_X) = _Y; } static _Nodeptr _Max(_Nodeptr _P) {/* JC while (_Right(_P) != _Nil) */ while ( _IsNotNil(_Right(_P)) ) _P = _Right(_P); return (_P); } static _Nodeptr _Min(_Nodeptr _P) {/* JC while (_Left(_P) != _Nil) */ while ( _IsNotNil(_Left(_P)) ) _P = _Left(_P); return (_P); } _Nodeptr& _Rmost() {return (_Right(_Head)); } _Nodeptr& _Rmost() const {return (_Right(_Head)); } _Nodeptr& _Root() {return (_Parent(_Head)); } _Nodeptr& _Root() const {return (_Parent(_Head)); } void _Rrotate(_Nodeptr _X) {_Nodeptr _Y = _Left(_X); _Left(_X) = _Right(_Y); /* JC1 if (_Right(_Y) != _Nil) */ if ( _IsNotNil( _Right(_Y) )) _Parent(_Right(_Y)) = _X; _Parent(_Y) = _Parent(_X); if (_X == _Root()) _Root() = _Y; else if (_X == _Right(_Parent(_X))) _Right(_Parent(_X)) = _Y; else _Left(_Parent(_X)) = _Y; _Right(_Y) = _X; _Parent(_X) = _Y; } _Nodeptr _Ubound(const _K& _Kv) const {_Nodeptr _X = _Root(); _Nodeptr _Y = _Head; /* JC while (_X != _Nil) */ while ( _IsNotNil(_X) ) if (key_compare(_Kv, _Key(_X))) _Y = _X, _X = _Left(_X); else _X = _Right(_X); return (_Y); } _Nodeptr _Buynode(_Nodeptr _Parg, _Redbl _Carg) {_Nodeptr _S = (_Nodeptr)allocator._Charalloc( 1 * sizeof (_Node)); _Parent(_S) = _Parg; _Color(_S) = _Carg; return (_S); } void _Consval(_Tptr _P, const _Ty& _V) {_Construct(&*_P, _V); } void _Destval(_Tptr _P) {_Destroy(&*_P); } void _Freenode(_Nodeptr _S) {allocator.deallocate(_S, 1); } _A allocator; _Pr key_compare; _Nodeptr _Head; bool _Multi; size_type _Size; }; template _Tree<_K, _Ty, _Kfn, _Pr, _A>::_Nodeptr _Tree<_K, _Ty, _Kfn, _Pr, _A>::_Nil = 0; template size_t _Tree<_K, _Ty, _Kfn, _Pr, _A>::_Nilrefs = 0; // tree TEMPLATE OPERATORS template inline bool operator==(const _Tree<_K, _Ty, _Kfn, _Pr, _A>& _X, const _Tree<_K, _Ty, _Kfn, _Pr, _A>& _Y) {return (_X.size() == _Y.size() && equal(_X.begin(), _X.end(), _Y.begin())); } template inline bool operator!=(const _Tree<_K, _Ty, _Kfn, _Pr, _A>& _X, const _Tree<_K, _Ty, _Kfn, _Pr, _A>& _Y) {return (!(_X == _Y)); } template inline bool operator<(const _Tree<_K, _Ty, _Kfn, _Pr, _A>& _X, const _Tree<_K, _Ty, _Kfn, _Pr, _A>& _Y) {return (lexicographical_compare(_X.begin(), _X.end(), _Y.begin(), _Y.end())); } template inline bool operator>(const _Tree<_K, _Ty, _Kfn, _Pr, _A>& _X, const _Tree<_K, _Ty, _Kfn, _Pr, _A>& _Y) {return (_Y < _X); } template inline bool operator<=(const _Tree<_K, _Ty, _Kfn, _Pr, _A>& _X, const _Tree<_K, _Ty, _Kfn, _Pr, _A>& _Y) {return (!(_Y < _X)); } template inline bool operator>=(const _Tree<_K, _Ty, _Kfn, _Pr, _A>& _X, const _Tree<_K, _Ty, _Kfn, _Pr, _A>& _Y) {return (!(_X < _Y)); } _STD_END #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ #endif /* _XTREE_ */ /* * Copyright (c) 1995 by P.J. Plauger. ALL RIGHTS RESERVED. * Consult your license regarding permissions and restrictions. */ /* * This file is derived from software bearing the following * restrictions: * * Copyright (c) 1994 * Hewlett-Packard Company * * Permission to use, copy, modify, distribute and sell this * software and its documentation for any purpose is hereby * granted without fee, provided that the above copyright notice * appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation. * Hewlett-Packard Company makes no representations about the * suitability of this software for any purpose. It is provided * "as is" without express or implied warranty. */ /* * File modified to make it work across DLL calls by Giancarlo Niccolai * Parts changed are marked by JC comment. * * Copyright (C) 2004 Giancarlo Niccolai * * The changes are distributed under the same statement as above, * including, but not limited to, the exclusion of any express or implied * warranty */ modules/000077500000000000000000000000001176363201700125345ustar00rootroot00000000000000modules/falcon/000077500000000000000000000000001176363201700137765ustar00rootroot00000000000000modules/falcon/CMakeLists.txt000066400000000000000000000003001176363201700165270ustar00rootroot00000000000000############################################################ # CMake list for modules. # set( module_dirs parser net web qrcode img ) falcon_install_moddirs( ${module_dirs} ) modules/falcon/img/000077500000000000000000000000001176363201700145525ustar00rootroot00000000000000modules/falcon/img/qrcode.fal000066400000000000000000000217221176363201700165170ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: qrcode.fal QR-Code library front end. ----------------------------------------------------------------------------- Author: Giancarlo Niccolai & Giuseppe Greco Begin:Wed, 27 Oct 2010 21:51:41 +0200 ----------------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from self.lib in qr /*# @main QR-Code generator module. This module exposes two classes (@a Maker and @a Image) that are meant to create QR-Code images. QR-Code is a sort of barcode that can store binary and text information on paper or screen devices, and can be read back with optical devices. QR-Code is a patent held by @link http://www.denso-wave.com/qrcode/index-e.html "Denso wave", but is released free of charge for everyone to use. A minimal sample program may be: @code import from img.qrcode in qr // Generates a QR Code image with a utf-8 encded text img = qr.Maker().text("Hello world", qr.ECL.L, "utf-8") // Save it as a PNG with 3 pixels per bit and 2 pixels of white border. img.savePng("hello.png", 3, 2) @endcodce The library has support for dumping the image directly to an arbitrary stream (and so, for example, generating a live document on a web server). The code of this library is heavily based on @link "http://fukuchi.org/works/qrencode/index.en.html" "Lib QR Encode". */ // workaround to circumvent error in module loader QrMask = qr.QrMask /*# Error correction level enumeration. Sets the trade off between generation complexity and scan readability. Can be one of: - **L**: Low correction - **M**: Medium correction - **Q**: Medium-high correction - **H**: High correction. */ ECL = qr.QrEcl /*# QR-Code generator. This class provides functionality for creating QR-Code images. The class instance is used to generate one or more @a Image instances, which are the actual content that can be streamed to a target file or to an arbitrary stream. */ class Maker /*# Cache directory -- set to nil to disable caching. */ cache_dir = nil /*# * Encoding of final output. */ encoding = "utf-8" /*# Specifies whether or not to estimate best mask. * * Set this attribute to false if performance is an issue. */ find_best_mask = true /*# The number of masks to be checked -- mask ids are generated randomly. */ find_from_random = 0 /*# Default mask. * * Only used when best mask estimation is disabled. */ default_mask = 2 /*# Max width (in pixels) allowed for png images. */ png_max_size = 1024 /*# Encodes raw binary data. @param data The text to be encoded. @optparam ecl The error correction level (defaults to ECL.L). @see Maker.ECL */ function encodeBin(data, ecl) input = self._getInput(ecl) input.append(qr.QrMode.Bin, len(data), data) return self._encodeInput(input) end /*# Encodes a text string. @param text The text to be encoded. @optparam ecl The error correction level (defaults to ECL.L). @optparam encoding Optional text encoding for the final output. @optparam caseSensitive Set to false to create alphanumeric sequences. @see Maker.ECL */ function encodeText(text, ecl, encoding, caseSensitive) if encoding == nil: encoding = self.encoding if caseSensitive == nil: caseSensitive = true if encoding text = transcodeTo(text, encoding) end input = self._getInput(ecl) qr.QrSplit.splitStringToInput(text, input, qr.QrMode.Bin, caseSensitive) return self._encodeInput(input) end function encodeNum(data, ecl) raise qr.QrError(qr.QrError.functionality_not_supported, "encodeNum") end function encodeAn(data, ecl) raise qr.QrError(qr.QrError.functionality_not_supported, "encodeAn") end function encodeKanji(data, ecl) raise qr.QrError(qr.QrError.functionality_not_supported, "encodeKanji") end function encodeStruct(data, ecl) raise qr.QrError(qr.QrError.functionality_not_supported, "encodeStruct") end /*# @private */ function _getInput(ecl) if ecl == nil: ecl = qr.QrEcl.L if ecl < qr.QrEcl.L or ecl > qr.QrEcl.H raise qr.QrError(qr.QrError.invalid_ecl, @i"$(ecl) is not supported") end return qr.QrInput(0, ecl) end /*# @private */ function _encodeInput(input, maskNo) if maskNo == nil: maskNo = -1 spec = qr.QrSpec() raw = qr.QrRawCode(input) version = raw.version width = spec.getWidth(version) frame = spec.createFrame(version, self.cache_dir) if (filler = qr.QrFrameFiller(width, frame)) == nil raise qr.QrError(qr.QrError.encoding_error, i"error creating frame filler") end // cache function references to improve performance funcGetCode = raw.getCode funcNext = filler.next funcSetFrameAt = filler.setFrameAt for i in [0:raw.dataLength + raw.eccLength] code = funcGetCode() bit = 0x80 for j in [0:8] addr = funcNext() funcSetFrameAt(addr, 0x02 || ((bit && code) != 0 ? 1 : 0)) bit >>= 1 end end; raw = nil for i in [0:spec.getRemainder(version)] addr = funcNext() funcSetFrameAt(addr, 0x02) end frame = filler.frame maskObj = qr.QrMask(self) if maskNo < 0 if self.find_best_mask masked = maskObj.mask(width, frame, input.ecl) else masked = maskObj.createMask(width, frame, self.default_mask % 8, input.ecl) end else masked = maskObj.createMask(width, frame, maskNo, input.ecl) end if masked == nil raise qr.QrError(qr.QrError.encoding_error, i"error masking frame") end return Image(version, width, masked, self) end end /*# Class representing a QR-Code image. @param version The version of this QR-Code image. @param width The width of this QR-Code image. @param data Raw binary data. @param maker The maker that built this Image (used for configuration). @note This class should not be instanced directly; it should be created trhough the @a Maker methods. This class contains the data that can be rendered to an output image. @prop version (read only) The version of this QR-Code image (indicating its complexity and data size). @prop width Size in read points of this image. */ class Image(version, width, data, maker) _version = version _width = width _code = data _maker = maker _binarized = nil /*# Saves the image on the given file as a JPG image. @param filename The file where the image will be saved. @optparam size Size of dots, in pixels. @optparam margin Size of the white margin around the image, in pixels. @raise IoError i/o on error. */ function saveJpg(filename, size, margin) self._saveImage(qr.QrImage.jpg, filename, size, margin) end /*# Saves the image on the given file as a PNG image. @param filename The file where the image will be saved. @optparam size Size of dots, in pixels. @optparam margin Size of the white margin around the image, in pixels. @raise IoError i/o on error. */ function savePng(filename, size, margin) self._saveImage(qr.QrImage.png, filename, size, margin) end /*# Writes the image to the given stream as a JPG image. @param outStream The stream where the image will be written. @optparam size Size of dots, in pixels. @optparam margin Size of the white margin around the image, in pixels. @raise IoError on i/o error. */ function writeJpg(outStream, size, margin) self._writeImage(qr.QrImage.jpg, outStream, size, margin) end /*# Writes the image to the given stream as a PNG image. @param outStream The stream where the image will be written. @optparam size Size of dots, in pixels. @optparam margin Size of the white margin around the image, in pixels. @raise IoError on i/o error. */ function writePng(outStream, size, margin) self._writeImage(qr.QrImage.png, outStream, size, margin) end function __get_version() return self._version end function __get_width() return self._width end /*# @private */ function _writeImage(func, outStream, size, margin) if not self._binarized: self._binarized = qr.QrTools.binarize(self._code) maxSize = int(self._maker.png_max_size / (len(self._code) + 2 * margin)) func(self._binarized, outStream, min(max(1, size), maxSize), margin) end /*# @private */ function _saveImage(func, filename, size, margin) stream = OutputStream(filename) try self._writeImage(func, stream, size, margin) stream.close() catch in error stream.close() raise error end end end modules/falcon/img/qrcode/000077500000000000000000000000001176363201700160275ustar00rootroot00000000000000modules/falcon/img/qrcode/lib.fal000066400000000000000000001530471176363201700172730ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: lib.fal QR Code library back end. ----------------------------------------------------------------------------- Author: Giuseppe Greco Begin: Wed, 27 Oct 2010 13:11:50 +0200 ----------------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# @beginignore */ import from gd2 load bufext const MaxInt = 2147483647 const MaxVersion = 40 const MaxWidth = 177 const StructHeaderSize = 20 const FrameBaseName = "frame" const MaskBaseName = "mask" /*# Capacity attributes. */ enum QrCapacity Width = 0 Words = 1 Reminder = 2 Ec = 3 end /*# Encoding modes. */ enum QrMode Null = -1 Num = 0 An = 1 Bin = 2 Kanji = 3 Struct = 4 end /*# Error correction levels. */ enum QrEcl L = 0 M = 1 Q = 2 H = 3 end /*# Demerit coefficients. */ enum QrDemeritCoeff N1 = 3 N2 = 3 N3 = 40 N4 = 10 end /*# Length table. */ QrLengthTable = [ [10, 12, 14], [ 9, 11, 13], [ 8, 16, 16], [ 8, 10, 12] ] /*# Alphanumeric table. */ QrAnTable = [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ] /*# Capacity table. */ QrCapacityTable = [ [ 0, 0, 0, [ 0, 0, 0, 0]], [ 21, 26, 0, [ 7, 10, 13, 17]], // 1 [ 25, 44, 7, [ 10, 16, 22, 28]], [ 29, 70, 7, [ 15, 26, 36, 44]], [ 33, 100, 7, [ 20, 36, 52, 64]], [ 37, 134, 7, [ 26, 48, 72, 88]], // 5 [ 41, 172, 7, [ 36, 64, 96, 112]], [ 45, 196, 0, [ 40, 72, 108, 130]], [ 49, 242, 0, [ 48, 88, 132, 156]], [ 53, 292, 0, [ 60, 110, 160, 192]], [ 57, 346, 0, [ 72, 130, 192, 224]], //10 [ 61, 404, 0, [ 80, 150, 224, 264]], [ 65, 466, 0, [ 96, 176, 260, 308]], [ 69, 532, 0, [ 104, 198, 288, 352]], [ 73, 581, 3, [ 120, 216, 320, 384]], [ 77, 655, 3, [ 132, 240, 360, 432]], //15 [ 81, 733, 3, [ 144, 280, 408, 480]], [ 85, 815, 3, [ 168, 308, 448, 532]], [ 89, 901, 3, [ 180, 338, 504, 588]], [ 93, 991, 3, [ 196, 364, 546, 650]], [ 97, 1085, 3, [ 224, 416, 600, 700]], //20 [101, 1156, 4, [ 224, 442, 644, 750]], [105, 1258, 4, [ 252, 476, 690, 816]], [109, 1364, 4, [ 270, 504, 750, 900]], [113, 1474, 4, [ 300, 560, 810, 960]], [117, 1588, 4, [ 312, 588, 870, 1050]], //25 [121, 1706, 4, [ 336, 644, 952, 1110]], [125, 1828, 4, [ 360, 700, 1020, 1200]], [129, 1921, 3, [ 390, 728, 1050, 1260]], [133, 2051, 3, [ 420, 784, 1140, 1350]], [137, 2185, 3, [ 450, 812, 1200, 1440]], //30 [141, 2323, 3, [ 480, 868, 1290, 1530]], [145, 2465, 3, [ 510, 924, 1350, 1620]], [149, 2611, 3, [ 540, 980, 1440, 1710]], [153, 2761, 3, [ 570, 1036, 1530, 1800]], [157, 2876, 0, [ 570, 1064, 1590, 1890]], //35 [161, 3034, 0, [ 600, 1120, 1680, 1980]], [165, 3196, 0, [ 630, 1204, 1770, 2100]], [169, 3362, 0, [ 660, 1260, 1860, 2220]], [173, 3532, 0, [ 720, 1316, 1950, 2310]], [177, 3706, 0, [ 750, 1372, 2040, 2430]] //40 ] /*# Error correction code table. */ QrEccTable = [ [[ 0, 0], [ 0, 0], [ 0, 0], [ 0, 0]], [[ 1, 0], [ 1, 0], [ 1, 0], [ 1, 0]], // 1 [[ 1, 0], [ 1, 0], [ 1, 0], [ 1, 0]], [[ 1, 0], [ 1, 0], [ 2, 0], [ 2, 0]], [[ 1, 0], [ 2, 0], [ 2, 0], [ 4, 0]], [[ 1, 0], [ 2, 0], [ 2, 2], [ 2, 2]], // 5 [[ 2, 0], [ 4, 0], [ 4, 0], [ 4, 0]], [[ 2, 0], [ 4, 0], [ 2, 4], [ 4, 1]], [[ 2, 0], [ 2, 2], [ 4, 2], [ 4, 2]], [[ 2, 0], [ 3, 2], [ 4, 4], [ 4, 4]], [[ 2, 2], [ 4, 1], [ 6, 2], [ 6, 2]], //10 [[ 4, 0], [ 1, 4], [ 4, 4], [ 3, 8]], [[ 2, 2], [ 6, 2], [ 4, 6], [ 7, 4]], [[ 4, 0], [ 8, 1], [ 8, 4], [12, 4]], [[ 3, 1], [ 4, 5], [11, 5], [11, 5]], [[ 5, 1], [ 5, 5], [ 5, 7], [11, 7]], //15 [[ 5, 1], [ 7, 3], [15, 2], [ 3, 13]], [[ 1, 5], [10, 1], [ 1, 15], [ 2, 17]], [[ 5, 1], [ 9, 4], [17, 1], [ 2, 19]], [[ 3, 4], [ 3, 11], [17, 4], [ 9, 16]], [[ 3, 5], [ 3, 13], [15, 5], [15, 10]], //20 [[ 4, 4], [17, 0], [17, 6], [19, 6]], [[ 2, 7], [17, 0], [ 7, 16], [34, 0]], [[ 4, 5], [ 4, 14], [11, 14], [16, 14]], [[ 6, 4], [ 6, 14], [11, 16], [30, 2]], [[ 8, 4], [ 8, 13], [ 7, 22], [22, 13]], //25 [[10, 2], [19, 4], [28, 6], [33, 4]], [[ 8, 4], [22, 3], [ 8, 26], [12, 28]], [[ 3, 10], [ 3, 23], [ 4, 31], [11, 31]], [[ 7, 7], [21, 7], [ 1, 37], [19, 26]], [[ 5, 10], [19, 10], [15, 25], [23, 25]], //30 [[13, 3], [ 2, 29], [42, 1], [23, 28]], [[17, 0], [10, 23], [10, 35], [19, 35]], [[17, 1], [14, 21], [29, 19], [11, 46]], [[13, 6], [14, 23], [44, 7], [59, 1]], [[12, 7], [12, 26], [39, 14], [22, 41]], //35 [[ 6, 14], [ 6, 34], [46, 10], [ 2, 64]], [[17, 4], [29, 14], [49, 10], [24, 46]], [[ 4, 18], [13, 32], [48, 14], [42, 32]], [[20, 4], [40, 7], [43, 22], [10, 67]], [[19, 6], [18, 31], [34, 34], [20, 61]] //40 ] /*# Alignment pattern table. */ QrAlignmentPatternTable = [ [ 0, 0], [ 0, 0], [18, 0], [22, 0], [26, 0], [30, 0], // 1- 5 [34, 0], [22, 38], [24, 42], [26, 46], [28, 50], // 6-10 [30, 54], [32, 58], [34, 62], [26, 46], [26, 48], //11-15 [26, 50], [30, 54], [30, 56], [30, 58], [34, 62], //16-20 [28, 50], [26, 50], [30, 54], [28, 54], [32, 58], //21-25 [30, 58], [34, 62], [26, 50], [30, 54], [26, 52], //26-30 [30, 56], [34, 60], [30, 58], [34, 62], [30, 54], //31-35 [24, 50], [28, 54], [32, 58], [26, 54], [30, 58] //35-40 ] /*# Version pattern table. */ QrVersionPatternTable = [ 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, 0x27541, 0x28c69 ] /*# Format information table. */ QrFormatInfoTable = [ [0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976], [0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0], [0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed], [0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b] ] /*# Class that represents a QR-Code error. @param code The error code. @param desc The error description. @param extra Extra error information. */ class QrError(code, desc, extra) from Error(code, desc, extra) functionality_not_supported = 10000 invalid_param = 10001 invalid_version = 10002 version_not_found = 10003 invalid_ecl = 10004 invalid_mode = 10005 invalid_mask = 10006 encoding_error = 10007 _desc = [ self.functionality_not_supported => i"Functionality not supported", self.invalid_param => i"Invalid parameter", self.invalid_version => i"Invalid version", self.version_not_found => i"Version not found", self.invalid_ecl => i"Invalid error correction level", self.invalid_mode => i"Invalid mode", self.invalid_mask => i"Invalid mask", self.encoding_error => i"Encoding error" ] init if code in self._desc self.description = self._desc[code] end end end /*# Class that represents an input item. @param mode One of the @a QrMode values. @param size The size of the item data. @param data The item data. @param bitBuf The bit buffer where the intput data will be encoded. */ class QrInputItem(mode, size, data, bitBuf) _mode = QrMode.Null _size = 0 _data = nil _bitBuf = nil init if data == nil raise QrError(QrError.invalid_param, i"data is null") end dataLength = len(data) if size and size > dataLength data[dataLength:] = "\x0" * (size - dataLength) end if not QrInput.verifyMode(mode, size, data) raise QrError(QrError.encoding_error, @i"Mode $(mode) is not suitable for encoding the specified data") end self._mode = mode self._size = size self._data = data self._bitBuf = bitBuf end function __get_mode() return self._mode end function __get_bitBuf() return self._bitBuf end function estimateBitBufferSize(version) size = 0 if version == 0: version = 1 if self._mode == QrMode.Num size = QrInput.estimateNumSize(self._size) elif self._mode == QrMode.An size = QrInput.estimateAnSize(self._size) elif self._mode == QrMode.Bin size = QrInput.estimateBinSize(self._size) elif self._mode == QrMode.Kanji size = QrInput.estimateKanjiSize(self._size) elif self._mode == QrMode.Struct return StructHeaderSize else return 0 end length = QrSpec.getLengthIndicator(self._mode, version) w = 1 << length num = int((self._size + w - 1) / w) size += num * (length + 4) return size end function encodeBitBuffer(version) words = QrSpec.getMaxWords(self._mode, version) if self._size > words inputItem1 = QrInputItem(self._mode, words, self._data) inputItem2 = QrInputItem(self._mode, self.size - words, self._data[words:len(self._data) - words]) inputItem1.encodeBitBuffer(version) inputItem2.encodeBitBuffer(version) self._bitBuf = BitBuf() inputItem1.bitBuf.readToBuf(self._bitBuf) inputItem2.bitBuf.readToBuf(self._bitBuf) else if self._mode == QrMode.Num self._encodeNum(version) elif self._mode == QrMode.An self._encodeAn(version) elif self._mode == QrMode.Bin self._encodeBin(version) elif self._mode == QrMode.Kanji self._encodeKanji(version) elif self._mode == QrMode.Struct self._encodeStruct() else raise QrError(QrError.invalid_mode, @i"$(self._mode) is not supported") end end return self._bitBuf.sizeBits() end /*# @private */ function _encodeNum(version) val = 0x1 words = int(self._size / 3) zero = "0"[*0] bitBuf = BitBuf() bitBuf.bitCount(4).writeBits(val) bitBuf.bitCount(QrSpec.getLengthIndicator(QrMode.Num, version)).writeBits(self._size) // cache function reference to improve performance funcWriteBits = bitBuf.bitCount(10).writeBits for i in [0:words] val = (self._data[*(i * 3)] - zero) * 100 val += (self._data[*(i * 3 + 1)] - zero) * 10 val += self._data[*(i * 3 + 2)] - zero funcWriteBits(val) end if self._size - words * 3 == 1 val = self._data[*(words * 3)] - zero bitBuf.bitCount(4).writeBits(val) elif self._size - words * 3 == 2 val = (self._data[*(words * 3)] - zero) * 10 val += self._data[*(words * 3 + 1)] - zero bitBuf.bitCount(7).writeBits(val) end self._bitBuf = bitBuf end /*# @private */ function _encodeAn(version) words = self._size >> 1 bitBuf = BitBuf() bitBuf.bitCount(4).writeBits(0x02) bitBuf.bitCount(QrSpec.getLengthIndicator(QrMode.An, version)).writeBits(self._size) // cache function references to improve performance funcWriteBits = bitBuf.bitCount(11).writeBits funcLookAnTable = QrInput.lookAnTable for i in [0:words] val = funcLookAnTable(self._data[*(i * 2)]) * 45 val += funcLookAnTable(self._data[*(i * 2 + 1)]) funcWriteBits(val) end if self._size && 1 val = funcLookAnTable(self._data[*(words * 2)]) bitBuf.bitCount(6).writeBits(val) end self._bitBuf = bitBuf end /*# @private */ function _encodeBin(version) bitBuf = BitBuf() bitBuf.bitCount(4).writeBits(0x4) bitBuf.bitCount(QrSpec.getLengthIndicator(QrMode.Bin, version)).writeBits(self._size) // cache function reference to improve performance funcWriteBits = bitBuf.bitCount(8).writeBits for i in [0:self._size] funcWriteBits(self._data[*i]) end self._bitBuf = bitBuf end /*# @private */ function _encodeKanji(version) bitBuf = BitBuf() /* bitBuf.bitCount(4).writeBits(0x8) bitBuf.bitCount(QrSpec.getLengthIndicator(QrMode.Kanji, version)).writeBits(int(self._size / 2)) // cache function reference to improve performance funcWriteBits = bitBuf.bitCount(13).writeBits for i in [0:self._size] val = self._data[i][*0] if val <= 0x9ffc val -= 0x8140 else val -= 0xc140 end h = (val >> 8) * 0xc0 val = (val && 0xff) + h funcWriteBits(val) end self._bitBuf = bitBuf */ // TODO: Kanji mode requires shift-jis do be used. // ATM we don't have it. Just adding the Unicode. bitBuf.bitCount(4).writeBits(0x8) bitBuf.bitCount(QrSpec.getLengthIndicator(QrMode.Kanji, version)).writeBits(int(self._size)) // cache function reference to improve performance funcWriteBits = bitBuf.bitCount(13).writeBits for i in [0:self._size] funcWriteBits(self._data[*i]) end self._bitBuf = bitBuf end /*# @private */ function _encodeStruct() bitBuf = BitBuf() bitBuf.bitCount(4).writeBits(0x03) bitBuf.bitCount(4).writeBits(self._data[*1] - 1) bitBuf.bitCount(4).writeBits(self._data[*0] - 1) bitBuf.bitCount(8).writeBits(self._data[*2]) self._bitBuf = bitBuf end end /*# Class that represents input data. @param version The QR-Code version. @param ecl One of the @a QrEcl values. */ class QrInput(version, ecl) _version = 0 _items = [] _ecl = 0 init if version == nil: version = 0 if ecl == nil: ecl = QrEcl.L self._version = version self.ecl = ecl end function __get_version() return self._version end function __set_version(version) if version < 0 or version > MaxVersion raise QrError(QrError.invalid_version, @i"$(version) is not supported") end self._version = version end function __get_ecl() return self._ecl end function __set_ecl(ecl) if ecl > QrEcl.H raise QrError(QrError.invalid_ecl, @i"$(ecl) is not supported") end self._ecl = ecl end function appendEntry(entry) arrayAdd(self._items, entry) end function append(mode, size, data) entry = QrInputItem(mode, size, data) arrayAdd(self._items, entry) end function lookAnTable(char) return char > 127 ? -1 : QrAnTable[char] end function estimateNumSize(run) w = int(run / 3) size = w * 10 switch run - w * 3 case 1 size += 4 case 2 size += 7 end return size; end function estimateAnSize(run) size = (run >> 1) * 11 if run && 1: size += 6 return size end function estimateBinSize(run) return run << 3 end function estimateKanjiSize(run) return (run >> 1) * 13 end function verifyMode(mode, size, data) if size <= 0 raise QrError(QrError.invalid_param, i"size is less than 1") end if mode == QrMode.Num return self._verifyNum(size, data) elif mode == QrMode.An return self._verifyAn(size, data) elif mode == QrMode.Kanji return self._verifyKanji(size, data) elif mode == QrMode.Bin return true elif mode == QrMode.Struct return true else raise QrError(QrError.invalid_mode, @i"$(mode) is not supported") end end function getByteStream() bitBuf = self._mergeBitBuffer() self._appendPaddingBits(bitBuf) // return bitBuf.toMemBuf() // The following piece of code is a temporary workaround; it is going to // be removed as soon as BitBuf.toMemBuf() works correctly. size = bitBuf.size() if size == 0: return [] data = arrayBuffer(size , 0) // cache function reference to improve performance funcRead8 = bitBuf.r8 bitBuf.rposBits(0) i = 0; while bitBuf.readableBits() > 0 data[i++] = funcRead8() end return data end /*# @private */ function _appendPaddingBits(bitBuf) size = bitBuf.sizeBits() maxWords = QrSpec.getDataLength(self.version, self.ecl) maxSize = maxWords << 3 if maxSize == size: return if maxSize - size < 5 bitBuf.bitCount(maxSize - size).writeBits(0) return end size += 4 words = (size + 7) >> 3 padding = BitBuf() padding.bitCount(words * 8 - size + 4).writeBits(0) paddingLength = maxWords - words if paddingLength > 0 paddingBuffer = arrayBuffer(paddingLength) // cache function reference to improve performance funcWrite8 = padding.w8 for i in [0:paddingLength] paddingBuffer[i] = (i && 1) ? 0x11 : 0xec funcWrite8(paddingBuffer[i]); end end padding.readToBuf(bitBuf) end /*# @private */ function _convertData() version = self._estimateVersion() if version > self.version self.version = version end loop size = self._createBitBuffer() version = QrSpec.getMinVersion((size + 7) >> 3, self.ecl) if version > self.version self.version = version else; break; end end false end /*# @private */ function _createBitBuffer() size = 0 for item in self._items size += item.encodeBitBuffer(self.version) end return size end /*# @private */ function _mergeBitBuffer() self._convertData() bitBuf = BitBuf() for item in self._items item.bitBuf.readToBuf(bitBuf) end return bitBuf end /*# @private */ function _estimateVersion() version = previous = 0 loop previous = version size = self._estimateBitBufferSize(previous) version = QrSpec.getMinVersion((size + 7) >> 3, self.ecl) end not version > previous return version end /*# @private */ function _estimateBitBufferSize(version) size = 0 for item in self._items size += item.estimateBitBufferSize(version) end return size end /*# @private */ function _verifyNum(size, data) zero = "0"[*0] nine = "9"[*0] for i in [0:size] if (data[*i] < zero) or (data[*i] > nine): return false end return true end /*# @private */ function _verifyAn(size, data) // cache function reference to improve performance funcLookAnTable = self.lookAnTable for i in [0:size] if funcLookAnTable(data[*i]) == -1: return false end return true end /*# @private */ function _verifyKanji(size, data) for i in [0:size] val = data[*i] /* if val < 0x8140 or (val > 0x9ffc and val < 0xe040) or val > 0xebbf return false end */ if val < 0x3000: return false end return true end end /*# Class that provides functionality for masking encoded data. @param maker The mask driver. */ class QrMask(maker) _runLength = nil _maker = maker _mask = [ [innerfunc (x, y); return (x + y) && 1; end], [innerfunc (x, y); return y && 1; end], [innerfunc (x, y); return x % 3; end], [innerfunc (x, y); return (x + y) % 3; end], [innerfunc (x, y); return (int(y / 2) + int(x / 3)) && 1; end], [innerfunc (x, y); return ((x * y) && 1) + (x * y) % 3; end], [innerfunc (x, y); return (((x * y) && 1) + (x * y) % 3) && 1; end], [innerfunc (x, y); return (((x * y) % 3) + ((x + y) && 1)) && 1; end] ] init self._runLength = arrayBuffer(MaxWidth + 1, 0) end function createMaskNo(maskNo, width, frame, mask, generateBitMaskOnly) if generateBitMaskOnly == nil: generateBitMaskOnly = false bitMask = [] if self._maker.cache_dir filename = self._maker.cache_dir \ + "/" + MaskBaseName + "_" + maskNo \ + "/" + MaskBaseName + "_" + width \ + "_" + maskNo + ".dat" if fileType(filename) != FileStat.NOTFOUND inputStream = InputStream(filename) bitMask = deserialize(inputStream) inputStream.close() else bitMask = self._generateBitMask(maskNo, width, frame) if fileType(self._maker.cache_dir + "/" + MaskBaseName + "_" + maskNo) == FileStat.NOTFOUND dirMake(self._maker.cache_dir + "/" + MaskBaseName + "_" + maskNo) end outputStream = OutputStream(filename) serialize(bitMask, outputStream) outputStream.close() end else bitMask = self._generateBitMask(maskNo, width, frame) end if generateBitMaskOnly: return mask = clone(frame) maskNo = 0 for y in [0:width] bitMaskRow = bitMask[y] frameRow = frame[y] for x in [0:width] if bitMaskRow[x] == "\x1" mask[y][x] = chr(frameRow[*x] ^^ bitMaskRow[*x]) end maskNo += mask[y][*x] && 1 end end return maskNo end function createMask(width, frame, maskNo, ecl) masked = arrayBuffer(width) for i in [0:width]; masked[i] = "\x0" * width; end self.createMaskNo(maskNo, width, frame, $masked) self._writeFormatInformation(width, masked, maskNo, ecl) return masked end function mask(width, frame, ecl) minDemerit = MaxInt bestMask = [] checkedMasks = [0, 1, 2, 3, 4, 5, 6, 7] if self._maker.find_from_random != 0 count = 8 - (self._maker.find_from_random % 9) for i in [0:count] pos = random(0, len(checkedMasks) - 1) arrayRemove(checkedMasks, pos) end end bestMask = frame for i in checkedMasks mask = arrayBuffer(width) for j in [0:width]; mask[j] = "\x0" * width; end demerit = blacks = 0 blacks = self.createMaskNo(i, width, frame, $mask) blacks += self._writeFormatInformation(width, mask, i, ecl) blacks = int(100 * blacks / (width * width)) demerit = int(int(abs(blacks - 50) / 5) * QrDemeritCoeff.N4) demerit += self._evaluateSymbol(width, mask) if demerit < minDemerit minDemerit = demerit bestMask = mask end end return bestMask end /*# @private */ function _calculateN1N3(length) demerit = 0 for i in [0:length] if self._runLength[i] >= 5 demerit += (QrDemeritCoeff.N1 + (self._runLength[i] - 5)) end if i && 1 if (i >= 3) and (i < (length - 2)) and (self._runLength[i] % 3 == 0) fact = int(self._runLength[i] / 3) if (self._runLength[i - 2] == fact) and \ (self._runLength[i - 1] == fact) and \ (self._runLength[i + 1] == fact) and \ (self._runLength[i + 2] == fact) if (self._runLength[i - 3] < 0) or (self._runLength[i - 3] >= (fact << 2)) demerit += QrDemeritCoeff.N3 elif ((i + 3) >= length) or (self._runLength[i + 3] >= (fact << 2)) demerit += QrDemeritCoeff.N3 end end end end end return demerit end /*# @private */ function _evaluateSymbol(width, frame) head = demerit = 0 // cache function reference to improve performance funcCalculateN1N3 = self._calculateN1N3 for y in [0:width] head = 0 self._runLength[0] = 1 currRow = frame[y] prevRow = frame[y -1] for x in [0:width] if x > 0 and y > 0 b22 = currRow[*x] && currRow[*(x - 1)] && prevRow[*x] && prevRow[*(x - 1)] w22 = currRow[*x] || currRow[*(x - 1)] || prevRow[*x] || prevRow[*(x - 1)] if (b22 || (w22 ^^ 1)) && 1: demerit += QrDemeritCoeff.N2 end if x == 0 and (currRow[*x] && 1) self._runLength[0] = -1 head = 1 self._runLength[head] = 1 elif x > 0 if (currRow[*x] ^^ currRow[*(x - 1)]) && 1 head++ self._runLength[head] = 1 else self._runLength[head]++ end end end demerit += funcCalculateN1N3(head + 1) end for x in [0:width] head = 0 self._runLength[0] = 1 for y in [0:width] if y == 0 and (frame[y][*x] && 1) self._runLength[0] = -1 head = 1 self._runLength[head] = 1 elif y > 0 if (frame[y][*x] ^^ frame[y - 1][*x]) && 1 head++ self._runLength[head] = 1 else self._runLength[head]++ end end end demerit += funcCalculateN1N3(head + 1) end return demerit end /*# @private */ function _generateBitMask(maskNo, width, frame) bitMask = arrayBuffer(width) for y in [0:width] bitMaskRow = "\x0" * width frameRow = frame[y] for x in [0:width] if not frameRow[*x] && 0x80 and self._mask[maskNo](x, y) == 0 bitMaskRow[x] = "\x1" end end bitMask[y] = bitMaskRow end return bitMask end /*# @private */ function _writeFormatInformation(width, frame, maskNo, ecl) blacks = 0 format = QrSpec.getFormatInfo(maskNo, ecl) for i in [0:8] if format && 1 blacks += 2 val = chr(0x85) else val = chr(0x84) end frame[8][width - 1 - i] = val if i < 6 frame[i][8] = val else frame[i + 1][8] = val end format >>= 1 end for i in [0:7] if format && 1 blacks += 2 val = chr(0x85) else val = chr(0x84) end frame[width - 7 + i][8] = val if i == 0 frame[8][7] = val else frame[8][6 - i] = val end format >>= 1 end return blacks end end /*# Class that represents a Reed-Solomon codec. @param symSize The symbol size, in bits. @param gfPoly The extended Galois field generator polynomial coefficients, with the 0th coefficient in the low order bit. The polynomial must be primitive. @param fcr The first consecutive root of the code generator polynomial, in index from. @param prim The primitive element to generate polynomial roots. @param nRoots The code generator polynomial degree (number of roots). @param pad The number of padding bytes in shortened block. */ class QrRsCodec(symSize, gfPoly, fcr, prim, nRoots, pad) _nn = 0 _alphaTo = nil _indexOf = nil _genPoly = nil _iPrim = 0 mm = 0 nRoots = 0 fcr = 0 prim = 0 pad = 0 gfPoly = 0 init // Common code for intializing a Reed-Solomon control block (char or int symbols) // Copyright 2004 Phil Karn, KA9Q // May be used under the terms of the GNU Lesser General Public License (LGPL) if (symSize < 0 or symSize > 8) \ or (fcr < 0 or fcr >= (1 << symSize)) \ or (prim <= 0 or prim >= (1 << symSize)) \ or (nRoots < 0 or nRoots >= (1 << symSize)) \ or (pad < 0 or pad >= ((1 << symSize) -1 - nRoots)) raise QrError(QrError.encoding_error, i"error initializing Reed-Solomon control block") end self.mm = symSize self._nn = (1 << symSize) - 1 self.pad = pad self._alphaTo = arrayBuffer(self._nn + 1, 0) self._indexOf = arrayBuffer(self._nn + 1, 0) self._indexOf[0] = self._nn self._alphaTo[self._nn] = 0 sr = 1 for i in [0:self._nn] self._indexOf[sr] = i self._alphaTo[i] = sr sr <<= 1 if sr && (1 << symSize): sr ^= gfPoly sr &= self._nn end if sr != 1 raise QrError(QrError.encoding_error, i"polynominal is not primitive") end self._genPoly = arrayBuffer(nRoots + 1, 0) self.fcr = fcr self.prim = prim self.nRoots = nRoots self.gfPoly = gfPoly iprim = 1; while (iprim % prim) != 0; iprim += self._nn; end self._iPrim = int(iprim / prim) self._genPoly[0] = 1 root = fcr * prim for i in [0:nRoots] self._genPoly[i + 1] = 1 for j in [i:1:-1] if self._genPoly[j] != 0 self._genPoly[j] = self._genPoly[j - 1] ^^ \ self._alphaTo[self._modnn(self._indexOf[self._genPoly[j]] + root)] else self._genPoly[j] = self._genPoly[j - 1] end end self._genPoly[0] = self._alphaTo[self._modnn(self._indexOf[self._genPoly[0]] + root)] root += prim end for i in [0:nRoots + 1] self._genPoly[i] = self._indexOf[self._genPoly[i]] end end function encodeChar(data) parity = arrayBuffer(self.nRoots, 0) for i in [0:self._nn - self.nRoots - self.pad] feedback = self._indexOf[data[i] ^^ parity[0]] if feedback != self._nn feedback = self._modnn(self._nn - self._genPoly[self.nRoots] + feedback) for j in [1:self.nRoots] parity[j] ^= self._alphaTo[self._modnn(feedback + self._genPoly[self.nRoots - j])] end end if len(parity) > 0: arrayRemove(parity, 0) if feedback != self._nn arrayAdd(parity, self._alphaTo[self._modnn(feedback + self._genPoly[0])]) else arrayAdd(parity, 0) end end return parity end /*# @private */ function _modnn(val) while val >= self._nn val -= self._nn val = (val >> self.mm) + (val && self._nn) end return val end end /*# Utility class that provides functionality for creating and caching Reed-Solomon codecs. */ class QrRs _codecs = [] function getCodec(symSize, gfPoly, fcr, prim, nRoots, pad) for codec in self._codecs if codec.pad != pad: continue if codec.nRoots != nRoots: continue if codec.mm != symSize: continue if codec.gfPoly != gfPoly: continue if codec.item.fcr != fcr: continue if codec.prim != prim: continue return codec; end codec = QrRsCodec(symSize, gfPoly, fcr, prim, nRoots, pad) arrayIns(self._codecs, 0, codec) return codec end end /*# Utility class that provides functionality for splitting data. @param data The data to split. @param input The split data. @param mode One of the @a QrMode values. */ class QrSplit(data, input, mode) _data = nil _input = nil _mode = nil init self._data = data self._input = input self._mode = mode end function splitStringToInput(string, input, mode, caseSensitive) if caseSensitive == nil: caseSensitive = true if string == nil or string == "\x0" or string == "" raise QrError(QrError.invalid_param, i"string is empty") end split = QrSplit(string, input, mode) if not caseSensitive: split.toUpper() split.splitString() end function splitString() while len(self._data) > 0 if self._data == "": break mode = self._identifyMode(0) if mode == QrMode.Num length = self._eatNum() elif mode == QrMode.An length = self._eatAn() elif mode == QrMode.Kanji if self._mode == QrMode.Kanji length = self._eatKanji() else length = self._eatBin() end else length = self._eatBin() end if length > 0: self._data = strBack(self._data, len(self._data) - length) end end function toUpper() stringLength = len(self._data) a = "a"[*0] z = "z"[*0] i = 0; while i < stringLength mode = self._identifyMode(strBack(self._data, stringLength - i), self._mode) if mode == QrMode.Kanji i += 2 else if self._data[*i] >= a and self._data[*i] <= z self._data[i] = chr(self._data[*i] - 32) end i++ end end return self._data end /*# @private */ function _eatNum() length = QrSpec.getLengthIndicator(QrMode.Num, self._input.version) run = 0; while self._isDigitAt(self._data, run); run++; end mode = self._identifyMode(run) if mode == QrMode.Bin if QrInput.estimateNumSize(run) + 4 + length \ + QrInput.estimateBinSize(1) \ - QrInput.estimateBinSize(run + 1) > 0 return self._eatBin() end end if mode == QrMode.An if QrInput.estimateNumSize(run) + 4 + length \ + QrInput.estimateAnSize(1) \ - QrInput.estimateAnSize(run + 1) > 0 return self._eatAn() end end self._input.append(QrMode.Num, run, self._data) return run; end /*# @private */ function _eatAn() lengthAn= QrSpec.getLengthIndicator(QrMode.An, self._input.version) lengthNum = QrSpec.getLengthIndicator(QrMode.Num, self._input.version) run = 0; while self._isAlNumAt(self._data, run) if self._isDigitAt(self._data, run) q = run while self._isDigitAt(self._data, q); q++; end if QrInput.estimateAnSize(run) \ + QrInput.estimateNumSize(q - run) + 4 + lengthNum \ - QrInput.estimateAnSize(q) < 0 break else run = q end else run++ end end if not self._isAlNumAt(self._data, run) if QrInput.estimateAnSize(run) + 4 + lengthAn \ + QrInput.estimateBinSize(1) \ - QrInput.estimateBinSize(run + 1) > 0 return self._eatBin() end end self._input.append(QrMode.An, run, self._data) return run end /*# @private */ function _eatKanji() run = 0; while self._identifyMode(run) == QrMode.Kanji; run += 2; end self._input.append(QrMode.Kanji, run, self._data) return run end function _eatBin() lengthAn = QrSpec.getLengthIndicator(QrMode.An, self._input.version) lengthNum = QrSpec.getLengthIndicator(QrMode.Num, self._input.version) // cache function references to improve performance funcIdentifyMode = self._identifyMode funcIsAlNumAt = self._isAlNumAt funcIsDigitAt = self._isDigitAt funcEstimateAnSize = QrInput.estimateAnSize funcEstimateNumSize = QrInput.estimateNumSize funcEstimateBinSize = QrInput.estimateBinSize dataLength = len(self._data) run = 1; while run < dataLength mode = funcIdentifyMode(run) if mode == QrMode.Kanji: break if mode == QrMode.Num q = run while funcIsDigitAt(self._data, q); q++; end if funcEstimateBinSize(run) \ + funcEstimateNumSize(q - run) + 4 + lengthNum \ - funcEstimateBinSize(q) < 0 break else run = q end elif mode == QrMode.An q = run while funcIsAlNumAt(self._data, q); q++; end if funcEstimateBinSize(run) \ + funcEstimateAnSize(q - run) + 4 + lengthAn \ - funcEstimateBinSize(q) < 0 break else run = q end else run++ end end self._input.append(QrMode.Bin, run, self._data) return run end /*# @private */ function _identifyMode(pos) if pos >= len(self._data): return QrMode.Null if self._isDigitAt(self._data, pos) return QrMode.Num elif self._isAlNumAt(self._data, pos) return QrMode.An elif self._mode == QrMode.Kanji if pos + 1 < len(self._data) word = self._data[*pos] << 8 || self._data[*(pos + 1)] // if (word >= 0x8140 and word <= 0x9ffc) or (word >= 0xe040 and word <= 0xebbf) // Our kanjis are in the UNICODE range, not in shift-jis if word >= 0x3040 and word <= 0x4db0 return QrMode.Kanji end end end return QrMode.Bin end /*# @private */ function _isDigitAt(str, pos) if pos >= len(str): return false return str[*pos] >= "0"[*0] and str[*pos] <= "9"[*0] end /*# @private */ function _isAlNumAt(str, pos) if pos >= len(str): return false return QrInput.lookAnTable(str[*pos]) >= 0 end end /*# Object that provides functionality for generating QR-Code images. */ object QrImage function png(frame, outFile, pixelPerPoint, outerFrame) img = self._create(frame, pixelPerPoint, outerFrame) img.Png(outFile) end function jpg(frame, outFile, pixelPerPoint, outerFrame, quality) if quality == nil: quality = 85 img = self._create(frame, pixelPerPoint, outerFrame) img.Jpeg(outFile, quality) end /*# @private */ function _create(frame, pixelPerPoint, outerFrame) if frame == nil raise QrError(QrError.invalid_param, i"frame is null") end if pixelPerPoint == nil: pixelPerPoint = 4 if outerFrame == nil: outerFrame = 4 height = len(frame) width = len(frame[0]) imgHeight = height + 2 * outerFrame imgWidth = width + 2 * outerFrame baseImg = gd2.GdImage(imgWidth, imgHeight) col = .[ baseImg.ColorAllocate(255, 255, 255) baseImg.ColorAllocate(0, 0, 0) ] baseImg.Fill(0, 0, col[0]) // cache function reference to improve performance funcSetPixel = baseImg.SetPixel for y in [0:height] row = frame[y] for x in [0:width] if row[x] == "1" funcSetPixel(x + outerFrame, y + outerFrame, col[1]) end end end targetImg = gd2.GdImage(imgWidth * pixelPerPoint, imgHeight * pixelPerPoint) targetImg.CopyResized(baseImg, 0, 0, 0, 0, \ imgWidth * pixelPerPoint, \ imgHeight * pixelPerPoint, \ imgWidth, imgHeight) return targetImg end end /*# Class that represents a Reed-Salomon control block. @param dataLength The length of the data to encode, in bytes. @param data The data to encode. @param ecc The error correction code. @param codec The codec to be used to encode the data. */ class QrRsBlock(dataLength, data, ecc, codec) dataLength = 0 data = nil ecc = nil init self.dataLength = dataLength self.data = data self.ecc = ecc = codec.encodeChar(data) end end /*# Class that represents raw code. @param input The input data to encode. */ class QrRawCode(input) _version = 0 _data = nil _dataLength = 0 _ecc = nil _eccLength = 0 _rsBlocks = [] _blockNum = 0 _blockNum1 = 0 _count = 0 _rs = QrRs() init if input == nil raise QrError(QrError.invalid_param, i"input is null") end self._data = input.getByteStream() self._version = input.version spec = QrSpec.getEccSpec(input.version, input.ecl) self._blockNum1 = QrSpec.rsBlockNum1(spec) self._dataLength = QrSpec.rsDataLength(spec) self._eccLength = QrSpec.rsEccLength(spec) self._ecc = arrayBuffer(self._eccLength, 0) self._blockNum = QrSpec.rsBlockNum(spec) self._initialize(spec) end function __get_version() return self._version end function __get_dataLength() return self._dataLength end function __get_eccLength() return self._eccLength end function getCode() code = 0 if self._count < self._dataLength row = self._count % self._blockNum col = self._count / self._blockNum if col >= self._rsBlocks[0].dataLength row += self._blockNum1 end code = self._rsBlocks[row].data[col] elif self._count < self._dataLength + self._eccLength row = (self._count - self._dataLength) % self._blockNum col = (self._count - self._dataLength) / self._blockNum code = self._rsBlocks[row].ecc[col] else return code end self._count++ return code end /*# @private */ function _initialize(spec) dataPos = eccPos = 0 ecc = nil self._rsBlocks = [] dataLength = QrSpec.rsDataCodes1(spec) eccLength = QrSpec.rsEccCodes1(spec) codec = self._rs.getCodec(8, 0x11d, 0, 1, eccLength, 255 - dataLength - eccLength) for i in [0:QrSpec.rsBlockNum1(spec)] arrayAdd(self._rsBlocks, QrRsBlock(dataLength, self._data[dataPos:], $ecc, codec)) self._ecc = self._ecc[0:eccPos] + ecc[0:] dataPos += dataLength eccPos += eccLength end if QrSpec.rsBlockNum2(spec) != 0 dataLength = QrSpec.rsDataCodes2(spec) eccLength = QrSpec.rsEccCodes2(spec) codec = self._rs.getCodec(8, 0x11d, 0, 1, eccLength, 255 - dataLength - eccLength) for i in [0:QrSpec.rsBlockNum2(spec)] arrayAdd(self._rsBlocks, QrRsBlock(dataLength, self._data[dataPos:], $ecc, codec)) self._ecc = self._ecc[0:eccPos] + ecc[0:] dataPos += dataLength eccPos += eccLength end end end end /*# Class that represents a frame filler. @param width The frame width, in bytes. @param frame The frame to fill. */ class QrFrameFiller(width, frame) _width = 0 _frame = nil _x = 0 _y = 0 _dir = 0 _bit = 0 init self._width = width self._frame = frame self._x = width - 1 self._y = width - 1 self._dir = -1 self._bit = -1 end function __get_width() return self._width end function __get_frame() return self._frame end function setFrameAt(at, val) self._frame[at["y"]][at["x"]] = chr(val) end function getFrameAt(at) return self._frame[at["y"]][*at["x"]] end function next() x = self._x y = self._y loop if self._bit == -1 self._bit = 0 return ["x" => self._x, "y" => self._y] end if self._bit == 0 x-- self._bit++ else x++ y += self._dir self._bit-- end if self._dir < 0 if y < 0 y = 0 x -= 2 self._dir = 1 if x == 6 x-- y = 9 end end else if y == self.width y = self.width - 1 x -= 2 self._dir = -1 if x == 6 x-- y -= 8 end end end if x < 0 or y < 0 raise QrError(QrError.encoding_error, i"error masking frame") end self._x = x self._y = y end (self._frame[y][*x] && 0x80) == 0 return ["x" => x, "y" => y] end end /*# Utility class that provides general-purpose functionality. */ class QrTools function binarize(frame) frameLength = len(frame) for row in frame for i in [0:frameLength] row[i] = (row[*i] && 1) ? "1" : "0" end end return frame end function buildCache(dir) if dir == nil or dir == "" raise QrError(QrError.invalid_param, i"dir is null or empty") end mask = QrMask() spec = QrSpec() for version in [1:MaxVersion + 1] frame = spec.createFrame(version, dir) filename = dir + "/" + FrameBaseName + "_" + version + ".png" QrImage.png(self.binarize(frame), filename, 1, 0) width = len(frame) bitMask = arrayBuffer(width) for i in [0:width]; bitMask[i] = "\x0" * width; end for maskNo in [0:8] mask.createMaskNo(maskNo, width, frame, $bitMask, true) end end end function dumpBitBuf(bitBuf) size = bitBuf.sizeBits() data = arrayBuffer(size , 0) rpos = bitBuf.rposBits() bitBuf.rposBits(0) while (count = bitBuf.readableBits()) > 0 if count > 8: count = 8 bits = bitBuf.bitCount(count).readBits() mask = 0x80 mask >>= (8 - count) for i in [0:count] if bits && mask; >> 1; else; >> 0; end mask >>= 1 end end > bitBuf.rposBits(rpos) end end /*# Class that represents the QR-Code specification. */ class QrSpec frames = nil function getDataLength(version, ecl) return QrCapacityTable[version][QrCapacity.Words] - QrCapacityTable[version][QrCapacity.Ec][ecl] end function getEccLength(version, ecl) return QrCapacityTable[version][QrCapacity.Ec][ecl] end function getWidth(version) return QrCapacityTable[version][QrCapacity.Width] end function getRemainder(version) return QrCapacityTable[version][QrCapacity.Reminder] end function getMinVersion(size, ecl) for i in [1:MaxVersion + 1] if QrCapacityTable[i][QrCapacity.Words] - \ QrCapacityTable[i][QrCapacity.Ec][ecl] >= size return i end end raise QrError(QrError.version_not_found) end function getLengthIndicator(mode, version) if mode == QrMode.Struct: return 0 length = 0 if version <= 9 length = 0 elif version <= 26 length = 1 else length = 2 end return QrLengthTable[mode][length] end function getMaxWords(mode, version) if mode == QrMode.Struct: return 3 length = 0 if version <= 9 length = 0 elif version <= 26 length = 1 else length = 2 end words = (1 << (QrLengthTable[mode][length])) - 1 if mode == QrMode.Kanji: words <<= 1 return words end function getEccSpec(version, ecl) spec = [0, 0, 0, 0, 0] b1 = QrEccTable[version][ecl][0] b2 = QrEccTable[version][ecl][1] dataLength = self.getDataLength(version, ecl) eccLength = self.getEccLength(version, ecl) if b2 == 0 spec[0] = b1 spec[1] = int(dataLength / b1) spec[2] = int(eccLength / b1) spec[3] = 0 spec[4] = 0 else spec[0] = b1 spec[1] = int(dataLength / (b1 + b2)) spec[2] = int(eccLength / (b1 + b2)) spec[3] = b2 spec[4] = spec[1] + 1 end return spec end function putAlignmentMarker(frame, x, y) finder = [ "\xa1\xa1\xa1\xa1\xa1", "\xa1\xa0\xa0\xa0\xa1", "\xa1\xa0\xa1\xa0\xa1", "\xa1\xa0\xa0\xa0\xa1", "\xa1\xa1\xa1\xa1\xa1" ] x -= 2 y -= 2 for i in [0:5] self._applyPattern(frame, x, y + i, finder[i]) end end function getVersionPattern(version) if version < 7 or version > MaxVersion raise QrError(QrError.invalid_version, @i"$(version) is not supported") end return QrVersionPatternTable[version -7] end function getFormatInfo(mask, ecl) if mask < 0 or mask > 7; raise QrError(QrError.invalid_mask, @i"$(mask) is not supported") end if ecl < QrEcl.L or ecl > QrEcl.H raise QrError(QrError.invalid_ecl, @i"$(ecl) not supported") end return QrFormatInfoTable[ecl][mask] end function putFinderPattern(frame, x, y) finder = [ "\xc1\xc1\xc1\xc1\xc1\xc1\xc1", "\xc1\xc0\xc0\xc0\xc0\xc0\xc1", "\xc1\xc0\xc1\xc1\xc1\xc0\xc1", "\xc1\xc0\xc1\xc1\xc1\xc0\xc1", "\xc1\xc0\xc1\xc1\xc1\xc0\xc1", "\xc1\xc0\xc0\xc0\xc0\xc0\xc1", "\xc1\xc1\xc1\xc1\xc1\xc1\xc1" ] for i in [0:7] self._applyPattern(frame, x, y + i, finder[i]) end end function createFrame(version, cacheDir) if cacheDir == "": cacheDir = nil if version < 1 or version > MaxVersion raise QrError(QrError.invalid_version, @i"$(version) is not supported") end arrayLength = version + 1 if self.frames == nil self.frames = arrayBuffer(arrayLength, nil) elif len(self.frames) < arrayLength arrayResize(self.frames, arrayLength) end if self.frames[version] == nil if cacheDir filename = cacheDir + "/" + FrameBaseName + "_" + version + ".dat" if fileType(cacheDir) == FileStat.NOTFOUND dirMake(cacheDir, true) fileChmod(cacheDir, 0755) end if fileType(filename) != FileStat.NOTFOUND inputStream = InputStream(filename) deserialized = deserialize(inputStream) inputStream.close() self.frames[version] = deserialized else frame = self._createFrame(version) outputStream = OutputStream(filename) serialize(frame, outputStream) outputStream.close() self.frames[version] = frame end else self.frames[version] = self._createFrame(version) end end if self.frames[version] == nil raise QrError(QrError.encoding_error, i"error creating frame") end return self.frames[version] end function rsBlockNum(spec); return spec[0] + spec[3]; end function rsBlockNum1(spec); return spec[0]; end function rsDataCodes1(spec); return spec[1]; end function rsEccCodes1(spec); return spec[2]; end function rsBlockNum2(spec); return spec[3]; end function rsDataCodes2(spec); return spec[4]; end function rsEccCodes2(spec); return spec[2]; end function rsDataLength(spec); return (spec[0] * spec[1]) + (spec[3] * spec[4]); end function rsEccLength(spec); return (spec[0] + spec[3]) * spec[2]; end /*# @private */ function _applyPattern(frame, x, y, pattern, length) substr = strFront(frame[y], x) srcLength = len(frame[y]) parLength = len(substr) if length == nil parLength += len(pattern) substr += pattern elif length > 0 parLength += length substr += strFront(pattern, length) end delta = srcLength - parLength if delta > 0: substr += strBack(frame[y], delta) return (frame[y] = substr) end /*# @private */ function _createFrame(version) width = QrCapacityTable[version][QrCapacity.Width] frame = arrayBuffer(width) for i in [0:width]; frame[i] = "\x0" * width; end self.putFinderPattern(frame, 0, 0) self.putFinderPattern(frame, width - 7, 0) self.putFinderPattern(frame, 0, width - 7) offset = width - 7 for y in [0:7] frame[y][7] = "\xc0" frame[y][width - 8] = "\xc0" frame[offset][7] = "\xc0" offset++ end pattern = "\xc0" * 8 self._applyPattern(frame, 0, 7, pattern) self._applyPattern(frame, width - 8, 7, pattern) self._applyPattern(frame, 0, width - 8, pattern) pattern = "\x84" * 9 self._applyPattern(frame, 0, 8, pattern) self._applyPattern(frame, width - 8, 8, pattern, 8) offset = width - 8 for y in [0:8] frame[y][8] = "\x84" frame[offset][8] = "\x84" offset++ end for i in [1:width - 15] frame[6][7 + i] = frame[7 + i][6] = chr(0x90 || (i && 1)) end self._putAlignmentPattern(version, frame, width) if version >= 7 versionPattern = self.getVersionPattern(version) v = versionPattern for x in [0:6] for y in [0:3] frame[(width - 11) + y][x] = chr(0x88 || (v && 1)) v >>= 1 end end v = versionPattern for y in [0:6] for x in [0:3] frame[y][x + (width - 11)] = chr(0x88 || (v && 1)) v >>= 1 end end end frame[width - 8][8] = "\x81" return frame end /*# @private */ function _putAlignmentPattern(version, frame, width) if version < 2: return d = QrAlignmentPatternTable[version][1] - QrAlignmentPatternTable[version][0] if d < 0 w = 2 else w = int((width - QrAlignmentPatternTable[version][0]) / d + 2) end if w * w - 3 == 1 x = QrAlignmentPatternTable[version][0] y = QrAlignmentPatternTable[version][0] self.putAlignmentMarker(frame, x, y) return end x = QrAlignmentPatternTable[version][0] for i in [1:w - 1] self.putAlignmentMarker(frame, 6, x) self.putAlignmentMarker(frame, x, 6) x += d end y = QrAlignmentPatternTable[version][0] for i in [0:w - 1] x = QrAlignmentPatternTable[version][0] for j in [0:w - 1] self.putAlignmentMarker(frame, x, y) x += d end y += d end end end modules/falcon/net/000077500000000000000000000000001176363201700145645ustar00rootroot00000000000000modules/falcon/net/imap.fal000066400000000000000000000434051176363201700162040ustar00rootroot00000000000000/* FALCON - IMAP client module FILE: imap.fal Interface for IMAP remote services ------------------------------------------------------------------- Author: Stanislas Marquis Begin: Sun, 21 Nov 2010 19:38:39 +0100 See http://tools.ietf.org/html/rfc3501 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from socket import from regex export class IMAPError( code, desc, extra ) from Error( code, desc, extra ) end /*# IMAP states. */ enum IMAPState logout = -2, nostate = -1, // initial state connected = 0, authenticated = 1, selected = 2 end /*# @brief IMAP class. @param server Server address. @optparam port Port number (default 143). @optparam timeout Socket timeout in milliseconds (default 1000). @optparam useSsl Wether to use SSL encrypted socket (default false). @optparam sslVer SSL version to use (default TLSv1). */ class IMAP( server, port, timeout, useSsl, sslVer ) /*# Server address. */ server = server /*# Port number. */ port = port ? toString( port ) : "143" /*# Timeout (in milliseconds) waiting for server contact. */ timeout = timeout ? timeout : 1000 /*# Wether to use ssl. Change it before connection. */ useSsl = useSsl ? useSsl : false /*# Ssl version to use, default TLSv1. */ sslVersion = sslVer ? sslVer : socket.TLSv1 /*# Function used to show communication between client and server. */ trace = {=>} /*# Current state (see @a IMAPState). */ state = IMAPState.nostate /*# Current selected mailbox. */ selected = nil _sock = nil _n = 0 // save multiple lines, if we receive them. oldReply = "" re_num = regex.Regex( "\\* ([0-9]+) (.*)$" ) re_ok = regex.Regex( "\\* OK ?(\\[(.*)\\])? ?(.*)$" ) re_flags = regex.Regex( "\\* FLAGS (.*)$" ) /*# @brief Connect to IMAP server. @raise IMAPError Unable to connect. */ function connect() sock = socket.TCPSocket() sock.setTimeout( self.timeout ) sock.connect( self.server, self.port ) if not sock.isConnected() raise IMAPError( 10001, "Can't connect to required server", self.server.toString() + ":" + self.port ) end sock.setTimeout( 0 ) self._sock = sock // get the OK or PREAUTH message reply = self._recvLine() if reply[0:9] == "* PREAUTH" self.state = IMAPState.authenticated elif reply[0:4] != "* OK" raise IMAPError( 10002, "Remote server not ready", reply.trim() ) else self.state = IMAPState.connected end // ssl ? if self.useSsl self.startTLS() self._sock.sslConfig( false, self.sslVersion ) self._sock.sslConnect() end end /*# @brief Login procedure. @param user username. @param pasw password. @raise IMAPError Login failure. Connection attempt will be performed if not already done. */ function login( user, pasw ) // connect if not done if self._sock == nil self.connect() end if self.state >= IMAPState.authenticated: return tag = self._tag() out = @ "$(tag) LOGIN $(user) $(pasw)" self._send( out ) self._getTagResponse( "LOGIN", tag ) // error is thrown by getTagResponse in case of failure. self.state = IMAPState.authenticated end /*# @brief Logout procedure. @raise IMAPError Logout failure. */ function logout() if self.state < IMAPState.connected: return tag = self._tag() out = @ "$(tag) LOGOUT" self._send( out ) reply = self._recvLine() if not reply.startsWith( "* BYE" ) raise IMAPError( 10010, "Logout failed", reply.trim() ) end self.state = IMAPState.logout self.selected = nil self._n = 0 // reset internal counter end /*# @brief Start TLS procedure. */ function startTLS() tag = self._tag() out = @ "$(tag) STARTTLS" self._send( out ) self._getTagResponse( "STARTTLS", tag ) end /*# @brief Select a mailbox. @optparam mailbox The mailbox to select (default INBOX). @optparam readonly (boolean) Default false. @raise IMAPError Select failure. */ function sel( mailbox, readonly ) if mailbox == nil: mailbox = "INBOX" cmd = readonly ? "EXAMINE" : "SELECT" tag = self._tag() out = @ "$(tag) $(cmd) $(mailbox)" self._send( out ) reply = self._getTagResponse( cmd, tag ) // error is thrown by getTagResponse in case of failure. reply = self.parseReply( reply ) self.selected = mailbox self.state = IMAPState.selected return reply end /*# @brief Close selected mailbox. @raise IMAPError Close failure. */ function close() if not self.selected: return tag = self._tag() out = @ "$(tag) CLOSE" self._send( out ) self._getTagResponse( "CLOSE", tag ) // error is thrown by getTagResponse in case of failure. self.selected = nil self.state = IMAPState.authenticated end /*# @brief Expunge a mailbox. @raise IMAPError Expunge failure. */ function expunge() if not self.selected: return tag = self._tag() out = @ "$(tag) EXPUNGE" self._send( out ) self._getTagResponse( "EXPUNGE", tag ) // error is thrown by getTagResponse in case of failure. end /*# @brief Create a mailbox. @param mailbox The mailbox to create. @raise IMAPError Create failure. */ function create( mailbox ) tag = self._tag() out = @ "$(tag) CREATE $(mailbox)" self._send( out ) self._getTagResponse( "CREATE", tag ) end /*# @brief Rename a mailbox. @param oldbox The actual mailbox to rename. @param newbox The desired mailbox name. @raise IMAPError Rename failure. */ function rename( oldbox, newbox ) tag = self._tag() out = @ "$(tag) RENAME $(oldbox) $(newbox)" self._send( out ) self._getTagResponse( "RENAME", tag ) // error is thrown by getTagResponse in case of failure. end /*# @brief Delete a mailbox. @param mailbox The mailbox to delete. @raise IMAPError Delete failure. */ function delete( mailbox ) tag = self._tag() out = @ "$(tag) DELETE $(mailbox)" self._send( out ) self._getTagResponse( "DELETE", tag ) // error is thrown by getTagResponse in case of failure. end /*# @brief Subscribe to a mailbox. @param mailbox The mailbox to subscribe to. @raise IMAPError Subscribe failure. */ function subscribe( mailbox ) tag = self._tag() out = @ "$(tag) SUBSCRIBE $(mailbox)" self._send( out ) self._getTagResponse( "SUBSCRIBE", tag ) // error is thrown by getTagResponse in case of failure. end /*# @brief Unsubscribe to a mailbox. @param mailbox The mailbox to unsubscribe to. @raise IMAPError Unsubscribe failure. */ function unsubscribe( mailbox ) tag = self._tag() out = @ "$(tag) UNSUBSCRIBE $(mailbox)" self._send( out ) self._getTagResponse( "UNSUBSCRIBE", tag ) // error is thrown by getTagResponse in case of failure. end /*# @brief List mailboxes. @optparam dir Reference directory (default ""). @optparam pattern Search pattern (default ""). @return A list of mailboxes information. @raise IMAPError List failure. */ function lst( dir, pattern ) if not dir: dir = '""' if not pattern: pattern = '""' tag = self._tag() out = @ "$(tag) LIST $(dir) $(pattern)" self._send( out ) reply = self._getTagResponse( "LIST", tag ) // error is thrown by getTagResponse in case of failure. lines = reply.split( "\r\n" ) if lines.len() == 0: return [] ret = arrayBuffer( lines.len() ) i = 0 for line in lines ret[i++] = [].comp( line.split(" ")[2:], { v => v[1:-1] } ) end return ret end /*# @brief List subscribed mailboxes. @optparam dir Reference directory (default ""). @optparam pattern Search pattern (default ""). @return A list of subscribed mailboxes names. @raise IMAPError Lsub failure. */ function lsub( dir, pattern ) if not dir: dir = '""' if not pattern: pattern = '""' tag = self._tag() out = @ "$(tag) LSUB $(dir) $(pattern)" self._send( out ) reply = self._getTagResponse( "LSUB", tag ) lines = reply.split( "\r\n" ) return [].comp( lines[0].split(" ")[2:] ) end /*# @brief Search for messages. @optparam criterion Search criteria (default ALL). @optparam charset Optional charset specification. @return A list of message ids matching the criteria. @raise IMAPError Search failure. */ function search( criterion, charset ) tag = self._tag() if not criterion: criterion = "ALL" charset = charset ? " CHARSET " + charset : "" out = @ "$(tag) SEARCH$(charset) $(criterion)" self._send( out ) data = self._getTagResponse( "SEARCH", tag ) ids = data.split(" ") // skip * SELECT in return if ids.len() <= 2: return [] return ids[2:] end /*# @brief Fetch data associated with a message. @param msg_set Message id or a sequence of message ids. @optparam msg_parts message data item names or macro (default FAST). @return the raw data. @raise IMAPError Fetch failure. @see http://tools.ietf.org/html/rfc3501#section-6.4.5 That is a raw, 'low-level' function returning the untreated answer got from the server. More appropriate functions are the IMAP.get* functions. */ function fetch( msg_set, msg_parts ) tag = self._tag() msg_set = toString( msg_set ) if not msg_parts: msg_parts = "FAST" out = @ "$(tag) FETCH $(msg_set) $(msg_parts)" self._send( out ) // in fetch, the first line may contain a total size of the incoming data, // if the incoming data is not internally generated by the fetch command // In this case, the fetch reply starts with * and has {size} bytes that // must be read. reply = [] while fetchStatus = self._recvLine() reply += fetchStatus // A long reply? posStart = fetchStatus.find( "{" ) posEnd = fetchStatus.find( "}", posStart ) if posStart >= 0 and posEnd >= posStart // in this case, read all what's next size = int( fetchStatus[posStart+1:posEnd] ) reply += self._recvAll( size ) end // are we done? if fetchStatus.startsWith( tag ) // fetchStatus starts with tag, but it's ok or fail? if not fetchStatus.startsWith( tag + " OK" ) raise IMAPError( 10010, "FETCH failed", fetchStatus ) end break end end return "".merge(reply) end /*# @brief Get a whole message. @param msg_id The message id. @return The whole message. @raise IMAPError Fetch failed. */ function getBody( msg_id ) raw = self.fetch( msg_id, "BODY[]" ) apos = raw.find( "\r\n" ) + 2 zpos = raw.rfind( ")" ) return raw[apos:zpos] end /*# @brief Get a section of a message. @param msg_id The message id. @param section The desired section. @raise IMAPError Fetch failed. Examples: @code imap.getBodySection( 1, 'HEADER' ) # get header imap.getBodySection( 3, '1.HEADER' ) # get header of first part imap.getBodySection( 5, 3 ) # get third part... @endcode */ function getBodySection( msg_id, section ) raw = self.fetch( msg_id, "BODY[" + toString( section ) + "]" ) apos = raw.find("\r\n") + 2 zpos = raw.rfind(")") return raw[apos:zpos] end /*# @brief Get the flags associated with a message. @param msg_id The message id. @return A list of flags. @raise IMAPError Fetch failed. */ function getFlags( msg_id ) raw = self.fetch( msg_id, "FLAGS" ) lines = raw.split( "\r\n" ) apos = lines[0].find("(FLAGS ") + 7 zpos = lines[0].rfind(")") return lines[0][apos:zpos].split(" ") end /*# @brief Get the RFC822 size of a message. @param msg_id The message id. @return The message size. @raise IMAPError Fetch failed. */ function getSize( msg_id ) raw = self.fetch( msg_id, "RFC822.SIZE" ) lines = raw.split( "\r\n" ) apos = lines[0].rfind(" ") + 1 zpos = lines[0].rfind(")") return int( lines[0][apos:zpos] ) end /*# @brief Get the hierarchy delimiter. @optparam ref The reference (default ""). @return The character used as hierarchy delimiter. */ function getHierarchyDelimiter( ref ) res = self.lst( ref, "" ) return res[0][1] end function _tag() n = self._n++ return @ "A$(n:5p0r)" end function _lastTag() n = self._n - 1 return @ "A$(n:5p0r)" end function _send( cmd ) self.trace( "--> " + cmd ) if self._sock.writeAvailable( self.timeout/1000.0 ) self._sock.send( cmd + "\r\n" ) else raise IMAPError( 10001, "Socket write timeout (" + self.timeout.toString() + ")" ) end end function _recvAll( totalsz ) // if we have already more than enough buffer... if self.oldReply.len() >= totalsz if self.oldReply.len() == totalsz reply = self.oldReply self.oldReply = "" else reply = self.oldReply[:totalsz] self.oldReply = self.oldReply[totalsz:] end self.trace( "<--("+reply.len()+") " + reply.trim() ) return reply end // read each block separately reply = strBuffer( totalsz + 1024 ) buf = strBuffer( 1501 ) recCount = self.oldReply.len() oldsz = recCount // for trace restsz = totalsz - oldsz // for trace if recCount reply = self.oldReply + reply self.oldReply = "" end excess = "" while 1 // timeout? -- the remote server hung. if not self._sock.readAvailable( self.timeout/1000.0 ) // Indicate a network problem, and how much data we got in the meanwhile raise socket.NetError( 10020, "Timeout in waiting for full size reply.", "(" + reply.len() + "/" + totalsz + ")" ) end // receive more data. recCount += self._sock.recv( buf, 1500 ) reply += buf // see if we have the tag response entirely with OK. if recCount > totalsz excess = reply[totalsz:] if excess.endsWith( "\r\n" ) and excess.startsWith( ")\r\n"+self._lastTag()+" OK " ) break end end end // put excess in internal buffer. self.oldReply = excess reply = reply[:totalsz] self.trace( "<--["+oldsz+"+"+restsz+"] " + reply.trim() ) return reply end function parseReply( reply ) res = [=>] lines = reply.split( "\r\n" ) for line in lines self.parseLine( line, res ) end return bless(res) end function parseLine( line, res ) // it's a numeric field? if (fields = self.re_num.grab( line )) res[strLower( fields[2] )] = int( fields[1] ) elif (fields = self.re_ok.grab( line )) // do we have a value to be parsed in an OK response? if fields[2] field = strSplit( fields[2], " " ) res[field[0]] = field.len() > 1 ? self.parseValue( field[1] ) : "" end elif (fields = self.re_flags.grab( line )) res["flags"] = self.parseValue( fields[1] ) end // for now, ignore other kinds of responses end function parseValue( value ) // check for special values. if value.len() > 0 firstChar = value[0] // lists? if firstChar == '(' and value[-1] == ')' // empty array? if value == "()": return nil // otherwise, just split return value[1:-1].split(' ') // numbers? elif firstChar >= "0" and value <= "9" return int( value ) end end // nope, just values. return value end function _recvLine( maxsz ) static buf = strBuffer( 1024 ) end if maxsz == nil: maxsz = 1024 reply = self.oldReply + strBuffer( maxsz ) while (pos = reply.find( "\r\n" )) < 0 and reply.len() < maxsz // timeout? -- the remote server hung. if not self._sock.readAvailable( self.timeout/1000.0 ) raise socket.NetError( 10021, "Timeout in reading next line from reply." ) end // receive more data. self._sock.recv( buf ) reply += buf end // send the first line and save the rest for later. self.oldReply = reply[pos+2:] reply = reply[0:pos+2] // include \r\n in this reply self.trace( "<-- " + reply.trim() ) return reply end function _getTagResponse( command, tag ) data = "" while line = self._recvLine() // last line? if line.startsWith( tag ) if not line.startsWith( tag + " OK" ) raise IMAPError( 10010, @"$(command) failed after receiving " + data.len() + " bytes", line.trim() ) end // we're done. break end data += line end // remove trailing space. return data.trim() end end // class IMAP /* vim: set et ai sw=3 ts=3 sts=3: */ modules/falcon/net/pop.fal000066400000000000000000000147711176363201700160600ustar00rootroot00000000000000/* FALCON - POP3 client module FILE: pop.fal Interface for POP3 remote services ------------------------------------------------------------------- Author: Stanislas Marquis Begin: Wed, 30 Nov 2011 17:28:20 +0100 See http://tools.ietf.org/html/rfc3501 ------------------------------------------------------------------- (C) Copyright 2011: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from socket export class POPError( code, desc, extra ) from Error( code, desc, extra ) end /*# @brief POP class @param server Server address. @optparam port Port number (default 110, or 995 is useSSl is true). @optparam timeout Socket timeout in milliseconds (default 1000). @optparam useSsl Use SSL encrypted socket (default false). @optparam sslVersion SSL version to use (default SSLv3). */ class POP( server, port, timeout, useSsl, sslVersion ) /*# Server address. */ server = server /*# Port number. */ port = port ? toString( port ) : useSsl ? "995" : "110" /*# Timeout (in milliseconds) waiting for server contact. */ timeout = timeout ? timeout : 1000 /*# Wether to use ssl. Change it before connection. */ useSsl = useSsl ? useSsl : false /*# Ssl version to use, default TLSv1. */ sslVersion = sslVersion ? sslVersion : socket.SSLv3 /*# Function used to show communication between client and server. */ trace = {=>} _sock = nil _buf = "" /*# @brief Connect to POP3 server. @raise POPError Unable to connect. */ function connect() sock = socket.TCPSocket() sock.setTimeout( self.timeout ) sock.connect( self.server, self.port ) if not sock.isConnected() raise POPError( 10001, "Can't connect to required server", self.server.toString() + ":" + self.port ) end sock.setTimeout( 0 ) self._sock = sock // encryption if self.useSsl self._sock.sslConfig( false, self.sslVersion ) self._sock.sslConnect() end // get greeting self._expectOK( "Greeting" ) end /*# @brief Issue a QUIT command. */ function quit() self._simpleCmd( "QUIT" ) end /*# @brief Authentication. @param user String identifying a mailbox. @param pswd Password. */ function authenticate( user, pswd ) self._simpleCmd( "USER " + user ) self._simpleCmd( "PASS " + pswd ) end /*# @brief Issue a STAT command. @return An array of integers ( total messages, total size ). */ function stat() rep = self._simpleCmd( "STAT" ) return [].comp( rep.split( " " )[1:], { x => int(x) } ) end /*# @brief Issue a LIST command. @optparam msg A message number (may not be marked as deleted). @return A dict ( message number => message size ). */ function list( msg ) res = [=>] if msg == nil rep = self._simpleCmd( "LIST" ) tot_msg = int( rep.split( " " )[1] ) i = 0 while i < tot_msg rep = self._recvLine().trim() msg_id, msg_sz = rep.split( " " ) res[ int(msg_id) ] = int( msg_sz ) i += 1 end self._recvLine() // ignore last line (dot) else rep = self._simpleCmd( "LIST " + msg ) msg_id, msg_sz = rep.split( " " )[1:] res[ int(msg_id) ] = int( msg_sz ) end return res end /*# @brief Retrieve a message. @param msg The message number. @optparam sz The message size. ??? @return The message. */ function retr( msg, sz ) self._simpleCmd( "RETR " + msg ) if sz == nil return self._recvFinalDot() else /*rep = self._recvAll( sz ) if not rep.endsWith( "\r\n.\r\n" ) rep += self._recvFinalDot().trim() end return rep*/ end end /*# @brief Mark a message as deleted. @param msg The message number. */ function dele( msg ) self._simpleCmd( "DELE " + msg ) end /*# @brief Issue a NOOP command. */ function noop() self._simpleCmd( "NOOP" ) end /*# @brief Reset deletion mark. */ function rset() self._simpleCmd( "RSET" ) end function _send( cmd ) self.trace( "--> " + cmd ) if self._sock.writeAvailable( self.timeout/1000.0 ) self._sock.send( cmd + "\r\n" ) else raise POPError( 10001, "Socket write timeout (" + self.timeout.toString() + ")" ) end end // read the next line sent by server // return the complete line with trailing \r\n function _recvLine() buf = strBuffer( 1024 ) rep = self._buf + strBuffer( 1024 ) while ( (pos = rep.find( "\r\n" )) < 0 ) //if not self._sock.readAvailable( self.timeout/1000.0 ) // raise socket.NetError( 10021, "Timeout in reading next line from reply." ) //end self._sock.recv( buf ) rep += buf end self._buf = rep[pos+2:] rep = rep[0:pos+2] // include \r\n in reply self.trace( "<-- " + rep.trim() ) return rep end function _recvAll( totalsz ) // check old buffer if self._buf.len() >= totalsz if self._buf.len() == totalsz reply = self._buf self._buf = "" else reply = self._buf[:totalsz] self._buf = self._buf[totalsz:] end self.trace( "<-- " + reply.trim() ) return reply end reply = self._buf self._buf = "" cnt = totalsz - reply.len() buf = strBuffer( cnt ) self._sock.recv( buf, cnt ) reply += buf self.trace( "<-- " + reply.trim() ) return reply end // get response until termination byte is found // return everything but final line function _recvFinalDot() rep = "" while not rep.endsWith( "\r\n.\r\n" ) rep += self._recvLine() end return rep[:-3] end // expect a one-liner beginning with OK // return the line function _expectOK( cmd ) rep = self._recvLine() if not rep.startsWith( "+OK" ) raise POPError( 10001, @"$(cmd) failed.", rep.trim() ) end return rep.trim() end // issue a command expecting a one-liner beginning with +OK // return the line function _simpleCmd( cmd ) self._send( cmd ) return self._expectOK( cmd ) end end // class POP /* vim: set ai et sw=3 ts=3 sts=3: */ modules/falcon/net/smtp.fal000066400000000000000000000151741176363201700162430ustar00rootroot00000000000000/* FALCON - SMTP client Module FILE: smtp.fal Send a mail! ------------------------------------------------------------------- Author: Giancarlo Nicclai Begin: Sun, 21 Nov 2010 19:38:39 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from socket /*# Simple Mail Transfer Protocol interface. @param server A remote server IP or DNS. @param port A port name or number, defaults to 'smtp'. */ class SMTP( server, port ) //# Helo string sent at connection heloMsg = "Falcon_SMTP" timeout = 5000 server = server port = port ? port : "smtp" _smtp = nil _auth = nil trace = {=>} authSchemes = [ "PLAIN" => self._plainChech ] /*# Set authentication parameters. @param scheme The authentication scheme to be used. @param ... Authentication data (usually, a pair of userid, password parameters). @return self Available schemes are: - "PLAIN" */ function auth( scheme ) if not scheme in self.authSchemes raise ParamError(501, nil, "Unknown authorization scheme" ) end sfunc = self.authSchemes[scheme] passvp(sfunc) return self end /*# Send a MIME part. @param part A @a mime.Part instance. @raise CodeError if the contents of From or To fields are invalid Sends a mail to the described recipients. The part must include the From and To headers. If the Date header is not included, it will be added and set to the current date and date. @see mime.Part */ function send( part ) if not part provides headers raise ParamError( 501, nil, "mime.Part") end headers = part.headers if not "From" in headers or not "To" in headers raise CodeError( 10000, "Missing 'From' or 'To' fields in mail" ) end from_field = headers["From"] to_field = headers["To"] from_mail = self.extractMail( from_field ) to_list = map( self.extractMail, to_field.split( "," ) ) if "Date" notin headers time = CurrentTime() headers[ "Date" ] = time.toRFC2822() end self.sendTo( from_mail, to_list, part.toString() ) end /*# Send a mail data to a remote SMTP server. @param sender The sender of the mail. @param rcpt Single or multiple mail target recipients. @param data A string containing the mail content, properly encoded. */ function sendTo( sender, rcpt, data ) self.connect() self.helo() if self._auth: self._auth() self.mailFrom( sender ) if rcpt.typeId() != StringType for target in rcpt self.rcpt( target ) end end self.data( data ) self.quit() end function connect() //connect with server smtp = socket.TCPSocket() smtp.setTimeout(self.timeout) if not smtp.connect( self.server, self.port ) raise socket.NetError( 10001, "Can't connect to required server", self.server.toString() + ":" + self.port ) end self.trace( "Connected." ) // get the hello message reply = "" smtp.recv(reply, 256) self.trace( reply ) if reply[0:3] != "220" raise socket.NetError( 10002, "Remote server not ready", reply.trim() ) end self._smtp = smtp end function helo() reply = "" smtp = self._smtp out = "HELO " + self.heloMsg self.trace( "-->" + out ) smtp.send( out +"\r\n" ) smtp.recv(reply,256) self.trace( "<--" + reply ) if reply[0] != "2" raise socket.NetError( 10003, "HELO refused", reply.trim() ) end end function mailFrom( sender ) reply = "" smtp = self._smtp out = "MAIL FROM:"+ sender self.trace( "-->" + out ) smtp.send( out + "\r\n" ) smtp.recv(reply,256) self.trace( "<--" + reply ) if reply[0:3] != "250" raise socket.NetError( 10004, "FROM refused", reply.trim() ) end end function rcpt( target ) reply = "" smtp = self._smtp out = "RCPT TO:"+ target self.trace( "-->" + out ) smtp.send( out + "\r\n" ) smtp.recv(reply,256) self.trace( "<--" + reply ) if reply[0:3] != "250" raise socket.NetError( 10005, "RCPT refused", reply.trim() ) end end function data( content ) reply = "" smtp = self._smtp self.trace( "-->DATA" ) smtp.send( "DATA\r\n" ) smtp.recv(reply,256) self.trace( "<--" + reply ) if reply[0:2] != "35" raise socket.NetError( 10006, "DATA refused", reply.trim() ) end sent = 0 blen = content.len() while sent < blen sent += smtp.send( content, blen - sent, sent ) self.trace( "--> sent " + sent + " bytes" ) end out = "\r\n.\r\n" self.trace( "-->." ) smtp.send( out ) smtp.recv(reply,256) self.trace( "<--" + reply ) if reply[0:2] != "25" raise socket.NetError( 10007, "DATA not correctly closed", reply ) end end function quit() reply = "" smtp = self._smtp self.trace( "-->QUIT" ) smtp.send( "QUIT\r\n" ) smtp.recv(reply,256) self.trace( "<--" + reply ) if reply[0:2] != "22" raise socket.NetError( 10008, "QUIT failed", reply ) end end function extractMail( field ) p1 = field.rfind( "<" ) p2 = field.rfind( ">" ) if p1 < p2 return field[p1+1:p2] end return field end // Manages the plain auth scheme function _plainChech(user, password) if user.typeId() != StringType or password.typeId() != StringType raise ParamError( 501, nil, "'PLAIN',S,S") end self._auth = .[self._plainDo user password] end function _plainDo( user, password ) reply = "" smtp = self._smtp self.trace( "-->AUTH PLAIN" ) smtp.send( "AUTH PLAIN\r\n" ) smtp.recv(reply,256) self.trace( "<--" + reply ) if not reply.startsWith("334") raise socket.NetError( 10009, "AUTH PLAIN failed", reply ) end // send the auth data data = Base64.encode( strToMemBuf( @"\x00$(user)\x00$(password)" ) ) self.trace( "-->" + data ) smtp.send( data + "\r\n") smtp.recv(reply,256) self.trace( "<--" + reply ) if not reply.startsWith("2") raise socket.NetError( 10010, "Authorization for AUTH PLAIN failed", reply ) end end end modules/falcon/parser/000077500000000000000000000000001176363201700152725ustar00rootroot00000000000000modules/falcon/parser/creole.fal000066400000000000000000000305621176363201700172350ustar00rootroot00000000000000/* FALCON - Generic Parser FILE: creole.fal Read and parses creole WIKI data. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 30 Aug 2008 09:42:22 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Node from parser.genparser.node as Node import LevelNode from parser.genparser.node as LevelNode import InfoNode from parser.genparser.node as InfoNode import PState from parser.genparser as PState import Context from parser.genparser as Context import ReRule from parser.genparser.rules as ReRule import ReRuleLA from parser.genparser.rules as ReRuleLA import TagRule from parser.genparser.rules as TagRule import EmptyLineRule from parser.genparser.rules as EmptyLineRule import DummyRule from parser.genparser.rules as DummyRule import CharRule from parser.genparser.rules as CharRule import Parser from parser.genparser Headers = .[ ReRule( '^\s*======\s*(.+?)\s*=*$', "#stay", {ctx, ma, title=> ctx.add( LevelNode("header", 6, title))}) ReRule( '^\s*=====\s*(.+?)\s*=*$', "#stay", {ctx, ma, title=> ctx.add( LevelNode("header", 5, title))}) ReRule( '^\s*====\s*(.+?)\s*=*$', "#stay", {ctx, ma, title=> ctx.add( LevelNode("header", 4, title))}) ReRule( '^\s*===\s*(.+?)\s*=*$', "#stay", {ctx, ma, title=> ctx.add( LevelNode("header", 3, title))}) ReRule( '^\s*==\s*(.+?)\s*=*$', "#stay", {ctx, ma, title=> ctx.add( LevelNode("header", 2, title))} ) ReRule( '^\s*=\s*(.+?)\s*=*$', "#stay", {ctx, ma, title=> ctx.add( LevelNode("header", 1, title))} ) ReRule( '^\s*----*$', "#stay", {ctx, ma, title=> ctx.add( Node("hr", title))} ) ] Headers2 = .[ ReRule( '^\s*======\s*(.+?)\s*=*$', "#pop", {ctx, ma, title=> ctx.popNode(); ctx.add( LevelNode("header", 6, title))}) ReRule( '^\s*=====\s*(.+?)\s*=*$', "#pop", {ctx, ma, title=> ctx.popNode();ctx.add( LevelNode("header", 5, title))}) ReRule( '^\s*====\s*(.+?)\s*=*$', "#pop", {ctx, ma, title=> ctx.popNode();ctx.add( LevelNode("header", 4, title))}) ReRule( '^\s*===\s*(.+?)\s*=*$', "#pop", {ctx, ma, title=> ctx.popNode();ctx.add( LevelNode("header", 3, title))}) ReRule( '^\s*==\s*(.+?)\s*=*$', "#pop", {ctx, ma, title=> ctx.popNode(); ctx.add( LevelNode("header", 2, title))} ) ReRule( '^\s*=\s*(.+?)\s*=*$', "#pop", {ctx, ma, title=> ctx.popNode(); ctx.add( LevelNode("header", 1, title))} ) ReRule( '^\s*----*$', "#pop", {ctx=> ctx.popNode(); ctx.add( Node("hr"))} ) ] TableStart = .[ ReRule( '^\s*\|=\s*', "table", {ctx => ctx.pushNode( Node("table") ); ctx.pushNode(Node("tr")); ctx.pushNode(Node("th"))} ) ReRule( '^\s*\|\s*', "table", {ctx => ctx.pushNode( Node("table") ); ctx.pushNode(Node("tr")); ctx.pushNode(Node("td"))} ) ] Indents = .[ ReRule( '^\s*::::\s*', "#stay", {ctx=> ctx.pushNode( LevelNode("indent", 4) ) }) ReRule( '^\s*:::\s*', "#stay", {ctx=> ctx.pushNode( LevelNode("indent", 3) ) }) ReRule( '^\s*::\s*', "#stay", {ctx=> ctx.pushNode( LevelNode("indent", 2) ) }) ReRule( '^\s*:\s*', "#stay", {ctx=> ctx.pushNode( LevelNode("indent", 1) ) }) ] SpecialEntities = .[ TagRule( '<', "#stay", {ctx => ctx.add("<")} ) TagRule( '>', "#stay", {ctx => ctx.add(">")} ) TagRule( '&', "#stay", {ctx => ctx.add("&")} ) TagRule( '"', "#stay", {ctx => ctx.add(""")} ) ] ListStart = .[ ReRule( '^\s*#\s*', "list", {ctx=> ctx.setListDepth( 1, "ol") }) ReRule( '^\s*\*\s*', "list", {ctx=> ctx.setListDepth( 1, "ul") }) ] FormatElements = .[ TagRule( "**", "bold", {ctx => ctx.pushNode( Node("b"))} ) TagRule( "//", "italic", {ctx => ctx.pushNode( Node("i"))} ) TagRule( "__", "underline", {ctx => ctx.pushNode( Node("u"))} ) TagRule( "^^", "superscript", {ctx => ctx.pushNode( Node("sup"))} ) TagRule( ",,", "subscript", {ctx => ctx.pushNode( Node("sub"))} ) TagRule( '\\', "#stay", {ctx=> ctx.add( Node("br"))} ) TagRule( '{{{', "verbatim", {ctx => ctx.pushNode( Node("tt"))} ) TagRule( "~~", "#stay", {ctx => ctx.add( "~" )} ) // escape escaped TagRule( "~", "escape" ) // escape char ] LinkElements = .[ ReRule( '\[\[:(.*?)(\|(.*?))?(\|(.*?))?\]\]', "#stay", {ctx, ma, pg, d1, txt, d2, mx => ctx.add( InfoNode("file", _options(mx, ["name"=>pg, "text"=>txt, "ext"=>true])) ) }) ReRule( '\[\[(https?:.*?|ftp:.*?)(\|(.*?))?(\|(.*?))?\]\]', "#stay", {ctx, ma, pg, d1, txt, d2, mx => ctx.add( InfoNode("link", _options(mx, ["name"=>pg, "text"=>txt, "ext"=>true])) ) }) ReRule( '\[\[([^:|\]]+):(.*?)(\|(.*?))?(\|(.*?))?\]\]', "#stay", {ctx, ma, ilink, pg, d1, txt, d2, mx => ctx.add( InfoNode("link", _options(mx, ["name"=>pg, "ext"=>false, "text"=>txt, "ilink"=>ilink])) ) }) ReRule( '\[\[(.*?)(\|(.*?))?(\|(.*?))?\]\]', "#stay", {ctx, ma, pg, d1, txt, d2, mx => ctx.add( InfoNode("link", _options(mx, ["name"=>pg, "ext"=>false, "text"=>txt])) ) }) ReRule( 'https?://[^ ;,\?!:]+', "#stay", {ctx, ma=> ctx.add( InfoNode("link", ["name"=>ma, "text"=>nil, "ext"=>true]))} ) ReRule( 'ftp://[^ ;,\?!:]+', "#stay", {ctx, ma=> ctx.add( InfoNode("link", ["name"=>ma, "text"=>nil, "ext"=>true]))} ) ReRule( '\{\{(.*?)(\|(.*?))?(\|(.*?))\}\}', "#stay", {ctx, ma, pg, d0, txt, d1, mx => ctx.add( InfoNode("img", _options(mx, ["name"=>pg, "alt"=> (txt? txt:pg)])))} ) ReRule( '<<(.*?)(\s+(.*?))?>>', "#stay", {ctx, ma, pname, d0, popts => ctx.add( InfoNode("plugin", ["name"=>pname, "params"=>_params(popts)], true) ) } ) ] InlineElements = FormatElements + LinkElements + SpecialEntities /* Determine the options of a optioned link */ function _options( mx, res ) if not mx: return res elems = mx.split("|") ielem = 0 le = elems.len() while ielem < le elem = elems[ielem++] if "=" in elem k,v = elem.split("=",2) res[k] = v else res[elem] = true end end return res end /* Determine options of plugins */ function _params( opts ) return opts end function _inlineState( limit ) return PState(.[ EmptyLineRule( "#pop;#pop", {ctx => ctx.popNode(); ctx.popNode()} ) // return to base // Putting it before allows to override TagRule( limit, "#pop", {ctx => ctx.popNode()} ) ] + InlineElements, {ctx, data=>ctx.add( data )}, "para_continue" // the paragraph can contiune at the end of an inline ) end class Parser from parser.genparser.Parser states = [ "start"=> PState(.[ EmptyLineRule( "#stay" ) // at start, empty lines are ignored ] + Headers + .[ DummyRule( "para", { ctx => ctx.pushNode( Node("para") )} ) ] ), "para" =>PState(.[ EmptyLineRule( "#pop", {ctx=> ctx.popNode()}) ReRule( '^\{\{\{([^\s]+)$', "verbatim_code", {ctx, ma, lang => ctx.pushNode( InfoNode("code", ["lang"=>lang]))} ) ReRule( '^\{\{\{$', "verbatim_line", {ctx => ctx.pushNode( Node("pre"))} ) ReRule( '^\+\+\+*\s*(.*)?$', "quote_base", {ctx, ma, link => ctx.popNode(); ctx.pushNode( InfoNode("quote", ["link"=>link]))} ) ] + TableStart + ListStart + InlineElements + Indents + Headers2, {ctx, data=>ctx.add( data )}, "para_continue" ), "quote_base" =>PState(.[ ReRule( '^\+\+\+*$', "#pop;#pop", {ctx => ctx.popNode();} ) DummyRule( "quote", { ctx => ctx.pushNode( Node("para") )} ) ] ), "quote" =>PState(.[ EmptyLineRule( "#pop", {ctx=> ctx.popNode()}) ReRuleLA( '^\+\+\+*$', "#pop", {ctx => ctx.popNode()} ) ] + ListStart + InlineElements, {ctx, data=>ctx.add( data )}, "para_continue" ), "para_continue"=> PState( .[ DummyRule( "#pop", { ctx => ctx.add(" ")} ) ] ), "eolverbatim" =>PState(.[ DummyRule( "#pop", { ctx => ctx.add( "\n") } ) ] ), "table" => PState(.[ // at the end of the line we close TD and TR EmptyLineRule( "#pop") ] + InlineElements +.[ ReRule( '\s*\|$', "tableBeginLine", {ctx=> ctx.popNode(); ctx.popNode()} ) ReRule( '\s*\|=\s*', "#stay", {ctx=> ctx.popNode(); ctx.pushNode(Node("th"))} ) ReRule( '\s*\|\s*', "#stay", {ctx=> ctx.popNode(); ctx.pushNode(Node("td"))} ) ReRule( '\s*$', "#pop", {ctx=> ctx.popNode(); ctx.popNode(); ctx.popNode()} ) ], {ctx, data=>ctx.add( data )} ), "tableBeginLine" => PState(.[ ReRule( '^\s*\|=\s*', "#pop", {ctx=> ctx.pushNode(Node("tr")); ctx.pushNode(Node("th"))} ) ReRule( '^\s*\|\s*', "#pop", {ctx=> ctx.pushNode(Node("tr")); ctx.pushNode(Node("td"))} ) DummyRule( "#pop", {ctx=> ctx.popNode(); } ) ] ), // Some inline states "bold" => _inlineState( "**" ), "italic" => _inlineState( "//" ), "superscript" => _inlineState( "^^" ), "subscript" => _inlineState( ",," ), "underline" => _inlineState( "__" ), "list" => PState(.[ EmptyLineRule( "#pop;#pop", { ctx=> ctx.setListDepth( 0 ); ctx.popNode(); }) ReRule( '^\s*######\s*', "#stay", {ctx=> ctx.setListDepth(6, "ol" ) }) ReRule( '^\s*#####\s*', "#stay", {ctx=> ctx.setListDepth(5, "ol" ) }) ReRule( '^\s*####\s*', "#stay", {ctx=> ctx.setListDepth(4, "ol" ) }) ReRule( '^\s*###\s*', "#stay", {ctx=> ctx.setListDepth(3, "ol" ) }) ReRule( '^\s*##\s*', "#stay", {ctx=> ctx.setListDepth(2, "ol" )}) ReRule( '^\s*#\s*', "#stay", {ctx=> ctx.setListDepth(1, "ol" )}) ReRule( '^\s*\*\*\*\*\*\*\s*', "#stay", {ctx=> ctx.setListDepth(6, "ul" ) }) ReRule( '^\s*\*\*\*\*\*\s*', "#stay", {ctx=> ctx.setListDepth(5, "ul" ) }) ReRule( '^\s*\*\*\*\*\s*', "#stay", {ctx=> ctx.setListDepth(4, "ul" ) }) ReRule( '^\s*\*\*\*\s*', "#stay", {ctx=> ctx.setListDepth(3, "ul" ) }) ReRule( '^\s*\*\*\s*', "#stay", {ctx=> ctx.setListDepth(2, "ul" )}) ReRule( '^\s*\*\s*', "#stay", {ctx=> ctx.setListDepth(1, "ul" )}) ] + InlineElements, {ctx, data=>ctx.add( data )}, "para_continue" ), "escape" =>PState(.[ TagRule( "https://", "#pop", {ctx => ctx.add( "https://" )} ) // escaped links TagRule( "http://", "#pop", {ctx => ctx.add( "http://" )} ) // escaped links TagRule( "ftp://", "#pop", {ctx => ctx.add( "ftp://" )} ) // escaped links CharRule( "#pop", {ctx, c => if c: ctx.add(c) } ) // force to go away if can't grab even a char ], nil, // No unrecognised possibile "*#pop" // pop if not matched at the end of the line, like ~\n ), "verbatim_line" =>PState(.[ ReRule( '^}}}$', "#pop", {ctx => ctx.popNode()} ) ] + SpecialEntities, {ctx, data=>ctx.add( data )}, "eolverbatim" ), "verbatim_code" =>PState(.[ ReRule( '^}}}$', "#pop", {ctx => ctx.popNode()} ) ], {ctx, data=>ctx.add( data )}, "eolverbatim" ), "verbatim" =>PState(.[ EmptyLineRule( "#pop;#pop", {ctx => ctx.popNode(); ctx.popNode()} ) // pop if newpara TagRule( '}}}', "#pop", {ctx => ctx.popNode()} ) ] + SpecialEntities, {ctx, data=>ctx.add( data )} ), // Just to be able to add always "," at the end "*dummy" => nil ] function reset() self.initParser( "start" ) end end modules/falcon/parser/docs/000077500000000000000000000000001176363201700162225ustar00rootroot00000000000000modules/falcon/parser/docs/main.fd000066400000000000000000000012321176363201700174570ustar00rootroot00000000000000/*# @module parser Generic line-oriented parser and related renderers @brief Generic line-oriented parser and related renderers The modules in this tree are providing a common framework to parse line-oriented texts and generate a semantic tree. The @a genparser module is the core of this subsystem; other ancillary modules are provided to parse well known formats, as the @link "http://www.creole.org" creole WIKI text format, or the faldoc text blocks. The obtained semantic tree can be used to generate documents in various formats. Some useful generators (i.e. html or docbook) are already provided in this module hierarcy. */modules/falcon/parser/faldoc/000077500000000000000000000000001176363201700165225ustar00rootroot00000000000000modules/falcon/parser/faldoc/falsrc.fal000066400000000000000000000277631176363201700204770ustar00rootroot00000000000000/* FALCON - Generic Parser FILE: falsrc.fal Read faldoc blocks inside source files. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 25 Sep 2010 14:53:06 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Node from parser.genparser.node as Node import InfoNode from parser.genparser.node as InfoNode import TagRule from parser.genparser.rules as TagRule import TokenRule from parser.genparser.rules as TokenRule import TokenRuleLA from parser.genparser.rules as TokenRuleLA import ReRule from parser.genparser.rules as ReRule import ReRuleLA from parser.genparser.rules as ReRuleLA import ReRuleEOL from parser.genparser.rules as ReRuleEOL import EmptyLineRule from parser.genparser.rules as EmptyLineRule import DummyRule from parser.genparser.rules as DummyRule import CharRule from parser.genparser.rules as CharRule import EOLRule from parser.genparser.rules as EOLRule import EOLRuleLA from parser.genparser.rules as EOLRuleLA import PState from parser.genparser as PState import Parser from parser.genparser CommentsAndGrabs = .[ ReRule( '/\*#[\*|#|\s]*+\*/', "#stay" ) ReRule( '/\*#[*#\s]?', "grab", {ctx=> ctx.pushNode( InfoNode( "faldoc_txt", ["line"=>ctx.row] )) }) ReRule( '//#\s*(?:@)', "grab_eol", {ctx=> ctx.pushNode( InfoNode( "faldoc_txt", ["line"=>ctx.row] )) }) TagRule( "/*", "comment" ) ReRule( '\s*//.*$', "#stay" ) ] FuncAndVars = .[ ReRule( '\s*function\s+(\w+)', "funcdecl", {ctx, d0, cname=> ctx.pushNode( InfoNode( "function", ["line"=>ctx.row,"name"=> cname] )) }) ReRule( '^\s*(\w+)\s*=', "#stay", {ctx, d0, cname=> ctx.add( InfoNode( "variable", ["line"=>ctx.row,"name"=> cname] )) }) ] escaping = .[ TagRule( '"', "string1" ) TagRule( '''', "string2" ) TagRule( '(', "par_round" ) TagRule( '[', "par_square" ) TagRule( '{', "par_graph" ) ] // We don't care about contents, but we need to properly count "end" BlockStatements = .[ TokenRule( "function", "block_begin" ) TokenRule( "try", "block_begin" ) TokenRule( "while", "block_begin" ) TokenRule( "if", "block_begin" ) TokenRule( "loop", "block_begin" ) TokenRule( "for", "block_begin" ) TokenRule( "if", "block_begin" ) TokenRule( "switch", "block_begin" ) TokenRule( "select", "block_begin" ) TokenRule( "forfirst", "block_begin" ) TokenRule( "formiddle", "block_begin" ) TokenRule( "forlast", "block_begin" ) ] class Parser from parser.genparser.Parser states = [ "start" => PState( escaping + CommentsAndGrabs + FuncAndVars + .[ ReRule( 'class\s+(\w+)', "classdecl", {ctx, d0, cname=> ctx.pushNode( InfoNode( "class", ["line"=>ctx.row, "name"=> cname] )) }) ReRule( 'object\s+(\w+)', "classdecl", {ctx, d0, cname=> ctx.pushNode( InfoNode( "object", ["line"=>ctx.row,"name"=> cname] )) }) ReRule( 'enum\s+(\w+)', "enum", {ctx, d0, cname=> ctx.pushNode( InfoNode( "enum", ["line"=>ctx.row,"name"=> cname] )) }) ], "*" // ignore everything else ), //=========================================================== // Generic part //=========================================================== "grab" =>PState(.[ TagRule( "\\*", "#stay", {ctx=> ctx.add("*")} ) ReRule( '\s*\*+/', "#pop", {ctx=> ctx.popNode()}) // Consumes useless * at begin of comment ReRule( '^\s*\*+', "#stay" ) ], {ctx, data=>ctx.add( data )}, "grab_continue" ), "grab_continue"=> PState( .[ DummyRule( "#pop", { ctx => ctx.add("\n")} ) ] ), "grab_eol" =>PState( .[], {ctx, data=>ctx.add( data )}, "grab_eol_checker" ), "grab_eol_checker" =>PState( .[ // if there is a //# on the next line, go on returning on the prev state TagRule( "//#", "#pop", {ctx=>ctx.add("\n")} ) // Else, pop the node and return to top DummyRule( "#pop;#pop", {ctx=>ctx.popNode()} ) ] ), "comment" =>PState(.[ ReRule( '.*?\*/', "#pop" ) ], "*" // get all ), //=========================================================== // class and object //=========================================================== "classdecl" => PState( CommentsAndGrabs +.[ ReRule( '\s*', "#stay" ) ReRule( '\(', "params", {ctx => ctx.pushNode( InfoNode("params", ["line"=>ctx.row])) }) ReRule( 'from', "inherit", {ctx => ctx.pushNode( InfoNode("inherit", ["line"=>ctx.row])) }) // consume multiline tokens, so that the eol rule doesn't fire. ReRuleEOL( '\\$', "#stay" ) // and return immmediately without waiting an end ReRule( ':.*$', "#pop", {ctx=> ctx.popNode()} ) // consume "end" after returning from the class. TokenRule( 'end', "#pop", {ctx=> ctx.popNode()} ) // enter the function state at eol (and dump errors ) TagRule( ';', "class" ) EOLRule( "class" ) ] ), "inherit" => PState( CommentsAndGrabs +.[ ReRule( '\s*', "#stay" ) ReRule( '((\w|\.)+)', "inherit_cls", {ctx, d0, cls, d1 =>ctx.pushNode( InfoNode("from", ["line"=>ctx.row, "class"=>cls ]))} ) TokenRuleLA( 'end', "#pop", {ctx=> ctx.popNode()} ) // consume multiline tokens, so that the eol rule doesn't fire. ReRuleEOL( '\\$', "#stay" ) // Consume errors and go to the class state EOLRuleLA( "#pop", {ctx=>ctx.popNode()} ) ] ), "inherit_cls" => PState( CommentsAndGrabs + .[ ReRule( '\s*', "#stay" ) ReRule( '\(', "inherit_expr", {ctx=>ctx.add("(")} ) ReRule( ',', "#pop", {ctx=>ctx.popNode()} ) // consume multiline tokens, so that the eol rule doesn't fire. ReRuleEOL( '\\$', "#stay" ) // return to previous state where ; is parsed ReRuleLA( ';', "#pop", {ctx=>ctx.popNode()} ) // Consume errors and go to the class state -- removing the inherit node. EOLRuleLA( "#pop", {ctx=>ctx.popNode()} ) ] ), "inherit_expr" => PState( CommentsAndGrabs + .[ // add a stack layer for expressions with ( ReRule( '\(\s*', "inherit_expr", {ctx => ctx.add("(") } ) ReRule( '\s*\)', "#pop", {ctx => ctx.add(")")} ) ], {ctx, data=>ctx.add( data ) }, "inerhit_expr_eol" ), "inerhit_expr_eol" => PState( .[ DummyRule( "#pop", {ctx=>ctx.add(" ") } ) ] ), "class" => PState( escaping + CommentsAndGrabs + FuncAndVars +.[ // eat init, or the parser will think that everything in init is a property ReRule('^\s*init\s*:\s*$', "#stay" ) ReRule('^\s*init\s*$', "init" ) TokenRuleLA( 'end', "#pop" ) ], "*" ), "init" => PState( escaping + BlockStatements + .[ TagRule( "/*", "comment" ) ReRule( '\s*//.*$', "#stay" ) TokenRule( 'end', "#pop" ) ], "*" ), //=========================================================== // Function //=========================================================== "funcdecl" => PState( CommentsAndGrabs + .[ ReRule( '\s*', "#stay" ) ReRule( '\(', "params", {ctx => ctx.pushNode( InfoNode("params", ["line"=>ctx.row])) }) // consume multiline tokens, so that the eol rule doesn't fire. ReRuleEOL( '\\$', "#stay" ) // and return immmediately without waiting an end ReRule( '\s*:.*$', "#pop", {ctx=> ctx.popNode()} ) ReRule( ';', "function" ) EOLRule( "function" ) ] ), "function" => PState( escaping + BlockStatements + .[ TagRule( "/*", "comment" ) ReRule( '\s*//.*$', "#stay" ) TokenRule( "end", "#pop;#pop", {ctx => ctx.popNode()} ) ], "*" ), "params" => PState( CommentsAndGrabs +.[ ReRule( '(\w+)', "params_continue", {ctx, d0, param=>ctx.add( InfoNode("param", ["line"=>ctx.row, "name"=>param]))} ) TagRule( ")", "#pop", {ctx=> ctx.popNode()} ) ReRule( '\s+', "#stay" ) // consume multiline tokens, so that the eol rule doesn't fire. ReRuleEOL( '\\$', "#stay" ) ] ), "params_continue" => PState( CommentsAndGrabs +.[ TagRule( ',', "#pop" ) ReRuleLA( '\)', "#pop" ) // consume multiline tokens, so that the eol rule doesn't fire. ReRule( '\s+', "#stay" ) ReRuleEOL( '\\$', "#stay" ) ] ), //=========================================================== // Enums //=========================================================== "enum" => PState( CommentsAndGrabs +.[ TagRule( "end", "#pop", {ctx => ctx.popNode()} ) ReRule( '\s*((\w|_)+)\s*=(.*)$', "#stay", {ctx, d0, cname=> ctx.add( InfoNode( "variable", ["line"=>ctx.row,"name"=> cname] )) }) ReRule( '\s*((\w|_)+)\s*$', "#stay", {ctx, d0, cname=> ctx.add( InfoNode( "variable", ["line"=>ctx.row,"name"=> cname] )) }) ReRule( '\s*((\w|_)+)\s*;', "#stay", {ctx, d0, cname=> ctx.add( InfoNode( "variable", ["line"=>ctx.row,"name"=> cname] )) }) ], "*" ), //=========================================================== // Normal code //=========================================================== "string1" => PState(.[ TagRule( '\"', "#stay" ) TagRule( '"', "#pop" ) ], "*" ), "string2" => PState(.[ TagRule( '''''', "#stay" ) TagRule( '''', "#pop" ) ], "*" ), "par_round" => PState( escaping +.[ TagRule( ')', "#pop" ) ], "*" ), "par_square" => PState( escaping +.[ TagRule( ']', "#pop" ) ], "*" ), "par_graph" => PState( escaping +.[ TagRule( '}', "#pop" ) ], "*" ), "block_begin" => PState( escaping +.[ TagRule( "/*", "comment" ) ReRule( '\s*//.*$', "#stay" ) // if we find a \\$, the definition continues... ReRuleEOL( '\\$', "#stay" ) // Avoid confusing ?: with ":" TagRule( "?", "question_mark" ) ReRule( ':.*$', "#pop" ) // Abandon this state either on ";" or on EOL TagRule( ";", "block_continue" ) EOLRule( "block_continue" ) ], "*" ), "block_continue" => PState( // we can have nested blocks escaping +BlockStatements + .[ TagRule( "/*", "comment" ) ReRule( '\s*//.*$', "#stay" ) // if we find a \\$, the definition continues... TokenRule( 'end', "#pop;#pop" ) ], "*" ), "question_mark" => PState( escaping + .[ // Return to block declaration when we find ':' TagRule( ':', "#pop" ) ], "*" ), // Just to be able to add always "," at the end "*dummy" => nil ] function reset() self.initParser( "start" ) end end modules/falcon/parser/faldoc/generic.fal000066400000000000000000000044141176363201700206250ustar00rootroot00000000000000/* FALCON - Generic Parser FILE: generic.fal Read faldoc blocks inside generic files. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 25 Sep 2010 14:53:06 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import InfoNode from parser.genparser.node as InfoNode import TagRule from parser.genparser.rules as TagRule import ReRule from parser.genparser.rules as ReRule import DummyRule from parser.genparser.rules as DummyRule import Parser from parser.genparser import PState from parser.genparser as PState class Parser from parser.genparser.Parser states = [ "start" => PState( .[ ReRule( '/\*#[*#\s]?', "grab", {ctx=> ctx.pushNode( InfoNode( "faldoc_txt", ["line"=>ctx.row] )) }) ReRule( '//#\s*(?:@)', "grab_eol", {ctx=> ctx.pushNode( InfoNode( "faldoc_txt", ["line"=>ctx.row] )) }) TagRule( "/*", "comment" ) ReRule( "//.*$", "#stay" ) ], "*" // get all ), "grab" =>PState(.[ TagRule( "\\*", "#stay", {ctx=> ctx.add("*")} ) ReRule( '\s*\*+/', "#pop", {ctx=> ctx.popNode()}) // Consumes useless * at begin of comment ReRule( '^\s*\*+', "#stay" ) ], {ctx, data=>ctx.add( data )}, "grab_continue" ), "grab_continue"=> PState( .[ DummyRule( "#pop", { ctx => ctx.add("\n")} ) ] ), "grab_eol" =>PState( .[], {ctx, data=>ctx.add( data )}, "grab_eol_checker" ), "grab_eol_checker" =>PState( .[ // if there is a //# on the next line, go on returning on the prev state TagRule( "//#", "#pop", {ctx=>ctx.add("\n")} ) // Else, pop the node and return to top DummyRule( "#pop;#pop", {ctx=>ctx.popNode()} ) ] ), "comment" =>PState(.[ ReRule( '.*?\*/', "#pop" ) ReRule( '.*', "#stay" ) ] ), // Just to be able to add always "," at the end "*dummy" => nil ] function reset() self.initParser( "start" ) end end modules/falcon/parser/faldoc/txt.fal000066400000000000000000000355221176363201700200340ustar00rootroot00000000000000/* FALCON - Generic Parser FILE: txt.fal Read faldoc documentation text files. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 30 Aug 2008 09:42:22 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Node from parser.genparser.node as Node import LevelNode from parser.genparser.node as LevelNode import InfoNode from parser.genparser.node as InfoNode import PState from parser.genparser as PState import Context from parser.genparser as Context import ReRule from parser.genparser.rules as ReRule import ReRuleLA from parser.genparser.rules as ReRuleLA import TagRule from parser.genparser.rules as TagRule import TokenRuleLA from parser.genparser.rules as TokenRuleLA import EmptyLineRule from parser.genparser.rules as EmptyLineRule import DummyRule from parser.genparser.rules as DummyRule import CharRule from parser.genparser.rules as CharRule import ProxyRule from parser.genparser.rules as ProxyRule import Parser from parser.genparser ParaCommands = .[ ReRule( '^\s*@after\s+(.*)$', "#stay", {ctx,p0,p1 => ctx.add(InfoNode("@after", ['line' => ctx.row, "dest"=>p1 ] )) }) ReRule( '^\s*@brief\s+', "para", {ctx => ctx.pushNode(InfoNode("@brief", ['line' => ctx.row ] )) }) ReRule( '^\s*@note\s+', "para", {ctx => ctx.pushNode(InfoNode("@note", ['line' => ctx.row ] )) }) ReRule( '^\s*@return\s+', "para", {ctx => ctx.pushNode(InfoNode("@return", ['line' => ctx.row ] )) }) ReRule( '^\s*@main\s+(.*)$', "#stay", {ctx, d0, p0 => ctx.add(InfoNode("@main", ['line' => ctx.row, 'title' => p0 ] )) }) ReRule( '^\s*@raise\s+(\w+)', "para", {ctx, d0, p0 => ctx.pushNode(InfoNode("@raise", ['line' => ctx.row, 'what'=> p0 ] )) }) ReRule( '^\s*@param\s+(\w+|...)', "para", {ctx, d0, p0 => ctx.pushNode(InfoNode("@param", ['line' => ctx.row, 'name'=> p0 ] )) }) ReRule( '^\s*@optparam\s+(\w+|...)', "para", {ctx, d0, p0 => ctx.pushNode(InfoNode("@optparam", ['line' => ctx.row, 'name'=> p0 ] )) }) ReRule( '^\s*@from\s+(\w+)', "para", {ctx, d0, p0 => ctx.pushNode(InfoNode("@from", ['line' => ctx.row, 'what'=> p0 ] )) }) ReRule( '^\s*@group\s+(\w+)\s+(.*)$', "para", {ctx, d0, p0, p1 => ctx.pushNode(InfoNode("@group", ['line' => ctx.row, 'name'=> p0, 'title' => p1 ] )) }) ReRule( '^\s*@funset\s+(\w+)\s+(.*)$', "para", {ctx, d0, p0, p1 => ctx.pushNode(InfoNode("@funset", ['line' => ctx.row, 'name'=> p0, 'title' => p1 ] )) }) ReRule( '^\s*@page\s+(\w+)\s+(.*)$', "para", {ctx, d0, p0, p1 => ctx.add(InfoNode("@page", ['line' => ctx.row, 'name'=> p0, 'title'=>p1 ] )) ctx.pushNode(Node("para")) }) ReRule( '^\s*@module\s+([\w.]+)\s+(.*)', "para", {ctx, d0, p0, p1 => ctx.pushNode(InfoNode("@module", ['line' => ctx.row, 'name'=> p0,'title'=> p1 ] )) }) ReRule( '^\s*@prop\s+(\w+)', "para", {ctx, d0, p0 => ctx.pushNode(InfoNode("@prop", ['line' => ctx.row, 'name'=> p0] )) }) ReRule( '^\s*@ignore\s*$', "#stay", {ctx, d0, p0 => ctx.add(InfoNode("@ignore", ['line' => ctx.row] )) } ) ] NamingCommands = .[ ReRule( '^\s*@see\s+(\w+).*$', "#stay", {ctx, d0, p0 => ctx.add(InfoNode("@see", ['line' => ctx.row, 'what'=> p0 ] )) }) ReRule( '^\s*@class\s+(\w+).*$', "#stay", {ctx, d0, p0 => ctx.add(InfoNode("@class", ['line' => ctx.row, 'name'=> p0] )) }) ReRule( '^\s*@object\s+(\w+).*$', "#stay", {ctx, d0, p0 => ctx.add(InfoNode("@object", ['line' => ctx.row, 'name'=> p0] )) }) ReRule( '^\s*@function\s+(\w+).*$', "#stay", {ctx, d0, p0 => ctx.add(InfoNode("@function", ['line' => ctx.row, 'name'=> p0] )) }) ReRule( '^\s*@message\s+(\w+).*$', "#stay", {ctx, d0, p0 => ctx.add(InfoNode("@message", ['line' => ctx.row, 'name'=> p0] )) }) ReRule( '^\s*@enum\s+(\w+).*$', "#stay", {ctx, d0, p0 => ctx.add(InfoNode("@enum", ['line' => ctx.row, 'name'=> p0] )) }) ReRule( '^\s*@global\s+(\w+).*$', "#stay", {ctx, d0, p0 => ctx.add(InfoNode("@global", ['line' => ctx.row, 'name'=> p0] )) }) ReRule( '^\s*@method\s+(\w+)\s+(\w+)$', "#stay", {ctx, d0, p0, p1 => ctx.add(InfoNode("@method", ['line' => ctx.row, 'name'=> p0, 'class'=>p1 ] )) }) ReRule( '^\s*@property\s+(\w+)\s+(\w+)$', "#stay", {ctx, d0, p0, p1 => ctx.add(InfoNode("@property", ['line' => ctx.row, 'name'=> p0, 'class'=>p1 ] )) }) ] ScopeCommands = .[ ReRule( '^\s*@beginmodule\s+([\w.]+).*$', "#stay", {ctx, d0, p0 => ctx.add(InfoNode("@beginmodule", ['line' => ctx.row, 'name'=> p0 ] )) }) ReRule( '^\s*@beginset\s+(\w+).*$', "#stay", {ctx, d0, p0 => ctx.add(InfoNode("@beginset", ['line' => ctx.row, 'name'=> p0 ] )) }) ReRule( '^\s*@begingroup\s+(\w+).*$', "#stay", {ctx, d0, p0 => ctx.add(InfoNode("@begingroup", ['line' => ctx.row, 'name'=> p0 ] )) }) ReRule( '^\s*@beginignore\s*$', "#stay", {ctx => ctx.add(InfoNode("@beginignore", ['line' => ctx.row, 'name'=>""])) } ) ReRule( '^\s*@inmodule\s+([\w.]+).*$', "#stay", {ctx, d0, p0 => ctx.add(InfoNode("@inmodule", ['line' => ctx.row, 'name'=> p0 ] )) }) ReRule( '^\s*@inset\s+(\w+).*$', "#stay", {ctx, d0, p0 => ctx.add(InfoNode("@inset", ['line' => ctx.row, 'name'=> p0 ] )) }) ReRule( '^\s*@ingroup\s+(\w+).*$', "#stay", {ctx, d0, p0 => ctx.add(InfoNode("@ingroup", ['line' => ctx.row, 'name'=> p0 ] )) }) ReRule( '^\s*@endmodule(\s+.*$|$)', "#stay", {ctx => ctx.add(InfoNode("@endmodule", ['line' => ctx.row] )) }) ReRule( '^\s*@endset(\s+.*$|$)', "#stay", {ctx => ctx.add(InfoNode("@endset", ['line' => ctx.row] )) }) ReRule( '^\s*@endgroup(\s+.*$|$)', "#stay", {ctx => ctx.add(InfoNode("@endgroup", ['line' => ctx.row] )) }) ReRule( '^\s*@endignore\s*$', "#stay", {ctx => ctx.add(InfoNode("@endignore", ['line' => ctx.row])) } ) ] InlineCommands = .[ ReRule( '@a\s+("([^"]*)"|((\w|\.(?:\w)|_)+))', "#stay", // add with standout, so that we can transform it without parsing the whole tree {ctx, d0, d1, p0, p0b => ctx.add(InfoNode("@a", ['line' => ctx.row, 'dest'=> p0 ? p0:p0b] )) }) ReRule( '@b\s+("([^"]*)"|([^\s:;]+))', "#stay", {ctx, d0, d1, p0, p0b => ctx.add( Node("b", p0 ? p0:p0b ) ) }) ReRule( '@i\s+("([^"]*)"|([^\s:;]+))', "#stay", {ctx, d0, d1, p0, p0b => ctx.add( Node("i", p0 ? p0:p0b ) ) }) ReRule( '@l(ink)?\s+("([^"]*)"|([\w/:\?#&%.-]+))\s+("([^"]*)"|(\w+))', "#stay", {ctx, d0, d1, d2, p0, p0b, d3, p1, p1b => ctx.add(InfoNode("link", ['line' => ctx.row, 'name'=> p0 ? p0:p0b, 'text' => p1?p1:p1b, "ext"=>true ] )) }) ] BlockCommands = .[ ReRule( '^\s*@pre(\s.*$|$)', "pre_text", {ctx => ctx.pushNode(InfoNode("pre", ['line' => ctx.row ] )) }) ReRule( '^\s*@source\s+(\w+).*$', "code_text", {ctx, d0, p0 => ctx.pushNode(InfoNode("code", ['line' => ctx.row, "lang"=>p0 ] )) }) ReRule( '^\s*@code\s*$', "code_text", {ctx => ctx.pushNode(InfoNode("code", ['line' => ctx.row, "lang"=>"" ] )) }) ] TableStart = .[ ReRule( '^\s*\|=\s*', "table", {ctx => ctx.pushNode( Node("table") ); ctx.pushNode(Node("tr")); ctx.pushNode(Node("th"))} ) ReRule( '^\s*\|\s*', "table", {ctx => ctx.pushNode( Node("table") ); ctx.pushNode(Node("tr")); ctx.pushNode(Node("td"))} ) ] ListStart = .[ ReRule( '^\s*#\s*', "list", {ctx=> ctx.setListDepth( 1, "ol") }) ReRule( '^\s*-\s*', "list", {ctx=> ctx.setListDepth( 1, "ul") }) ] FormatElements = .[ TagRule( "**", "bold", {ctx => ctx.pushNode( Node("b"))} ) TagRule( "{{", "teletype", {ctx => ctx.pushNode( Node("tt"))} ) TagRule( "//", "italic", {ctx => ctx.pushNode( Node("i"))} ) TagRule( "__", "underline", {ctx => ctx.pushNode( Node("u"))} ) TagRule( "^^", "superscript", {ctx => ctx.pushNode( Node("sup"))} ) TagRule( ",,", "subscript", {ctx => ctx.pushNode( Node("sub"))} ) TagRule( "~~", "#stay", {ctx => ctx.add( "~" )} ) // escape escaped TagRule( "\\\\", "#stay", {ctx => ctx.add( "\\" )} ) // escape escaped TagRule( "~", "escape" ) // escape char TagRule( "\\", "escape" ) // escape char ] function _inlineState( limit ) return PState(.[ EmptyLineRule( "#pop;#pop", {ctx => ctx.popNode(); ctx.popNode()} ) // return to base // Putting it before allows to override TagRule( limit, "#pop", {ctx => ctx.popNode()} ) ] + InlineCommands + FormatElements, {ctx, data=>ctx.add( data )}, "para_continue" // the paragraph can contiune at the end of an inline ) end ParaStartCommands = \ ParaCommands \ + NamingCommands \ + ScopeCommands \ + BlockCommands StartRules = \ .[ EmptyLineRule( "#stay" ) ] \ + ParaStartCommands \ + .[ // serve also inline commands, but generate a para node for them. ProxyRule( InlineCommands, "para;*", {ctx=> ctx.pushNode( Node("para") )} ) // consuming everything else ReRule( '\s*', "#stay" ) // Otherwise, enter the para mode ReRuleLA( '[^\s]', "para", { ctx => ctx.pushNode( Node("para") )} ) ] class Parser from parser.genparser.Parser states = [ "start"=> PState(.[ ReRule( '^\s*@section\s+(\w+)\s+(.*)$', "section", {ctx, d0, p0, p1 => ctx.pushNode(InfoNode("@section", ['line' => ctx.row, 'name'=> p0, 'title'=>p1] )) }) // ignore subsection here, making it an error ] + StartRules ), "section" =>PState(.[ ReRuleLA( '^\s*@section', "#pop", {ctx => ctx.popNode() } ) ReRule( '^\s*@subsection\s+(\w+)\s+(.*)$', "section", {ctx, d0, p0, p1 => ctx.pushNode(InfoNode("@section", ['line' => ctx.row, 'name'=> p0, 'title'=>p1] )) }) ] + StartRules ), "subsection" =>PState(.[ ReRuleLA( '^\s*@section', "#pop", {ctx => ctx.popNode() } ) ReRuleLA( '^\s*@subsection', "#pop", {ctx => ctx.popNode() } ) ] + StartRules ), "para" =>PState(.[ EmptyLineRule( "#pop", {ctx=> ctx.popNode()}) ReRule( '^\s*$', "#pop", {ctx=> ctx.popNode()}) // a "@" not recognized here means we need to pop the paragraph, and handle the recognition to upper level ProxyRule( ParaStartCommands, "#pop;*", {ctx, rule=>ctx.popNode(); } ) ] + TableStart + ListStart + FormatElements + .[ // eat useless spaces at line start. ReRule( '^\s+', "#stay" ) ] + InlineCommands + .[ // while a @ in the middle of the para means an unrecognized command for sure. ReRule( '@(\w+)', "#stay", {ctx, d0, p0=>ctx.add( InfoNode( "cmd_unrecognized", ['line' => ctx.row, "cmd"=>d0] ) )}) ], {ctx, data=>ctx.add( data )}, "para_continue" ), "para_continue"=> PState( .[ DummyRule( "#pop", { ctx => ctx.add(" ")} ) ] ), "table" => PState(.[ // at the end of the line we close TD and TR EmptyLineRule( "#pop") ] + InlineCommands + FormatElements +.[ ReRule( '\s*\|$', "tableBeginLine", {ctx=> ctx.popNode(); ctx.popNode()} ) ReRule( '\s*\|=\s*', "#stay", {ctx=> ctx.popNode(); ctx.pushNode(Node("th"))} ) ReRule( '\s*\|\s*', "#stay", {ctx=> ctx.popNode(); ctx.pushNode(Node("td"))} ) ReRule( '\s*$', "#pop", {ctx=> ctx.popNode(); ctx.popNode(); ctx.popNode()} ) ], {ctx, data=>ctx.add( data )} ), "tableBeginLine" => PState(.[ ReRule( '^\s*\|=\s*', "#pop", {ctx=> ctx.pushNode(Node("tr")); ctx.pushNode(Node("th"))} ) ReRule( '^\s*\|\s*', "#pop", {ctx=> ctx.pushNode(Node("tr")); ctx.pushNode(Node("td"))} ) DummyRule( "#pop", {ctx=> ctx.popNode(); } ) ] ), // Some inline states "bold" => _inlineState( "**" ), "teletype" => _inlineState( "}}" ), "italic" => _inlineState( "//" ), "superscript" => _inlineState( "^^" ), "subscript" => _inlineState( ",," ), "underline" => _inlineState( "__" ), "list" => PState(.[ EmptyLineRule( "#pop;#pop", { ctx=> ctx.setListDepth( 0 ); ctx.popNode(); }) ReRule( '^\s*######\s*', "#stay", {ctx=> ctx.setListDepth(6, "ol" ) }) ReRule( '^\s*#####\s*', "#stay", {ctx=> ctx.setListDepth(5, "ol" ) }) ReRule( '^\s*####\s*', "#stay", {ctx=> ctx.setListDepth(4, "ol" ) }) ReRule( '^\s*###\s*', "#stay", {ctx=> ctx.setListDepth(3, "ol" ) }) ReRule( '^\s*##\s*', "#stay", {ctx=> ctx.setListDepth(2, "ol" )}) ReRule( '^\s*#\s*', "#stay", {ctx=> ctx.setListDepth(1, "ol" )}) ReRule( '^\s*------\s*', "#stay", {ctx=> ctx.setListDepth(6, "ul" ) }) ReRule( '^\s*-----\s*', "#stay", {ctx=> ctx.setListDepth(5, "ul" ) }) ReRule( '^\s*----\s*', "#stay", {ctx=> ctx.setListDepth(4, "ul" ) }) ReRule( '^\s*---\s*', "#stay", {ctx=> ctx.setListDepth(3, "ul" ) }) ReRule( '^\s*--\s*', "#stay", {ctx=> ctx.setListDepth(2, "ul" )}) ReRule( '^\s*-\s*', "#stay", {ctx=> ctx.setListDepth(1, "ul" )}) ] + ProxyRule( ParaStartCommands, "#pop;#pop;*", {ctx, rule=>ctx.setListDepth( 0 );ctx.popNode(); } ) + InlineCommands + FormatElements + .[ // while a @ in the middle of the para means an unrecognized command for sure. ReRule( '@(\w+)', "#stay", {ctx, d0, p0=>ctx.add( InfoNode( "cmd_unrecognized", ['line' => ctx.row, "cmd"=>d0] ) )}) ], {ctx, data=>ctx.add( data )}, "para_continue" ), "escape" =>PState(.[ CharRule( "#pop", {ctx, c => if c: ctx.add(c) } ) // force to go away if can't grab even a char ], nil, // No unrecognised possibile "*#pop" // pop if not matched at the end of the line, like ~\n ), "pre_text" =>PState(.[ ReRule( '^\s*@endpre\s*$', "#pop", {ctx => ctx.popNode()} ) ], {ctx, data=>ctx.add( data )}, "eolverbatim" ), "code_text" =>PState(.[ ReRule( '^\s*@endcode\s*$', "#pop", {ctx => ctx.popNode()} ) ], {ctx, data=>ctx.add( data )}, "eolverbatim" ), "eolverbatim" =>PState(.[ DummyRule( "#pop", { ctx => ctx.add( "\n") } ) ] ), // Just to be able to add always "," at the end "*dummy" => nil ] function reset() self.initParser( "start" ) end end modules/falcon/parser/genparser.fal000066400000000000000000000265701176363201700177560ustar00rootroot00000000000000/* FALCON - Generic Parser FILE: parser.fal Generic customizable grammar parser. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 30 Aug 2010 09:42:22 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ directive version = 0x010001 import Context from self.context as Context import Node from self.node as Node import DummyRule from self.rules //# Parser states (behaviors) class PState( rules, onUnmatched, eolRule ) rules = rules onUnmatched = onUnmatched eolRule = eolRule end /*# @brief Main parsing class. A generic parser is a parsing system based on token recognition and callback invocation. This class provides general token recognition and calls adequate callbacks. In strick terms, this class acts more like a @b lexer which drives a callback-driven parser provided by the user. The @b Parser class works by using a set of named states (in the class @a PState), which corespond to different ruleset which are used in different parsing steps. Rules, that is, user-provided callbacks, can return a string representing a state name; the coresponding satate is then pushed on a stack, and can be popped later by returning the special state "#pop". Each @a PState provides a set of four elements: - separators: a string containing characters considered "blanks" which hasn't any grammar meaning except that of separating tokens and keywords. In example, " \t" would recognize spaces and tab characters as separators. - tokens: A list of @a Rule instances which match recognized tokens. Tokens are recognized when it can be demonstrated that no other token can be matched. In example, if the parser has "+" and "+=" tokens, the proper action (either the one associated with + or +=) will be called when the ambiguity can be resolved. - keywords: A list of @a Rule instances which are recognized only if surrounded by separators or tokens. In example, a keyword "fun" would be recognized only if preceded and followed by any of the characters in the @a seperators string, or by any recognized token. - onElement: it's a callback which gets called when identifying a sequence between separators and/or tokens which is not listed in the keywords. In example, the following parser regognizes and saves strings by pushing a different state when they are found: @code load parser // base state base = PState( separators|" \t\n\r", tokens| [ Rule( '"', onQuote ) ], onElement|ignoring ) // state when parsing a string pstring = PState( separators|"", // spaces are relevant tokens|[ Rule( "\\\"", onEscapeQuote ), Rule( "\"", onStringClosed ) ], onElement| addToString ) content = "" function onQuote( qt ) global content // starting string mode content = "" return "string" end function ignoring( item ) > "Ignoring ", item end function addToString( data ) global content content += data end function onEscapeQuote( qt ) global content content += "\"" end function onStringClosed( qt ) global content > "Found string: >>", content, "<<" return "#pop" end parser = Parser() parser.addState( "base", base ) parser.addState( "string", pstring ) stream = StringStream( 'unrecognized text, "an \"interesting\" string", other text') parser.parse( stream, "base" ) @endcode The parser ensures that all the tokens, keywords and unrecognized elements are notified via callbacks in the same order in which they are found in the parsed file. Other than returning a state name, the rule callbacks may also return a new instance of @a PState; this would push the state on the stack and make it the currently active state. This makes possible to create new parser states on the fly, in case some of the parsing conditions are not known in advance. It is also possible to modify existing states by i.e. adding new keywords or tokens as they become defined while parsing the source code. */ class Parser() //# known states by name states = nil //# stack of ParserStates _stack = [] //# Used for tracing _stateNames = [] //# false when asked to quit _proceed = false //# current parsing row row = 1 //# true to activate rule tracing trace = false /*# Pushes a given state making it the active state. * @param name The name of the state that should be pushed. * @raise ParseError if the state name is not known. */ function pushState( name ) if not name in self.states raise ParseError( 10000, i"Setting invalid state", name ) end self._stack += self.states[name] self._stateNames += name if self.trace self.traceStates() end end /*# @brief Pops current state. @raise ParseError if the state backlist is empty. */ function popState() len = self._stack.len() if len <= 1 raise ParseError( 10001, i"Cannot pop topmost state" ) end arrayResize( self._stack, len - 1 ) arrayResize( self._stateNames, len - 1 ) if self.trace self.traceStates() end end /*# @brief Performs complete parsing. @param stream An input stream where the input data is read. @param initState The name of the initial state. @optparam initRow If given, set the initial row to this number @raise ParseError if the parser has not been correctly initialized. */ function parse( string, initState, initRow ) if not initState: initState = "start" self.initParser(initState) if initRow: self.row = initRow ctx = Context() pos = 0 strlen = string.len() loop // find the next line poseol = string.find( "\n", pos ) eolsize = 1 // consider \r\n or \n\r if poseol > 0 if string[* (poseol-1)] == 0xD --poseol ++eolsize elif poseol+1 < strlen and string[* (poseol+1)] == 0xD ++eolsize end end line = string[pos: (poseol >= 0 ? poseol: strlen)] self.parseLine( line, ctx ) if poseol < 0: break pos = poseol + eolsize end return ctx end //# Parses a file function parseStream( stream, initState ) self.initParser( initState ) ctx = Context() line = "" while stream.readLine( line, 4096 ) self.parseLine( line, ctx ) end return ctx end function initParser(initState) if initState self._stack = [] if self.trace: self._stateNames = [] self.pushState( initState ) elif self._stack.len() != 1 raise ParseError( 10002, i"Parser not initialized before parsing" ) end self._proceed = true self.row = 1 end function parseLine( line, ctx ) linelen = line.len() col = 0 hadWinner = false if self.trace: self.traceText( line ) atEnd = false loop matchAt = linelen winner = nil state = self._stack[-1] sr = state.rules ruleId = 0 srlen = sr.len() while ruleId < srlen rule = sr[ruleId++] rulePos = rule.match( line, col ) if rulePos == 0 // we found the rule winner = rule hadWinner = true matchAt = 0 break elif rulePos > 0 if rulePos < matchAt winner = rule matchAt = rulePos hadWinner = true elif rulePos == matchAt and not winner // match at end winner = rule hadWinner = true end end end //we found it -- do we have a prefix? if col < matchAt // else, the rule doesn't match prefix = line[col:matchAt] if (not state.onUnmatched) statedesc = ", ".merge(self._stateNames) raise ParseError( 10003, i"Unrecognized text", @"[$(self.row):$(col)]{$(statedesc)} \"$prefix\"" ) end if state.onUnmatched != "*": state.onUnmatched( ctx, prefix ) end if winner count = winner.apply( ctx ) if self.trace: self.traceRule( winner, ctx ) self._fulfil( winner.nextState ) // ensure that end of lines with look-ahead are respected if winner.lookAhead: atEnd = false else // empty line and no matching rule, so it seems. break end col = matchAt + count // are we done? if col > linelen break elif col == linelen // offer an extra loop for rules matching at end of line if atEnd: break atEnd = true end end if state.eolRule and (not winner or winner.nextState == "#stay") // is this a conditional rule? if state.eolRule[0] == "*" if not hadWinner if self.trace: self.traceMsg("Following optional eolRule") self._fulfil( state.eolRule[1:] ) end else if self.trace: self.traceMsg("Following mandatory eolRule") self._fulfil( state.eolRule ) end end ++self.row ctx.row = self.row end function _fulfil( slist ) states = slist.split( ";") si = 0 l = states.len() while si < l what = states[si++] if what == "#pop" self.popState() elif what == "#stay" if self.trace: self.traceMsg( "Staying in current state" ) return elif what == "#quit" self.terminate() elif what.typeId() == StringType self.pushState( what ) elif what.derivedFrom( PState ) self._stack += what self._stateNames += "anon(" + what.toString()+ ")" if self.trace self.traceStates() end else // otherwise we have something wrong raise ParseError( 10004, i"Invalid state change", what ) end end end /*# @brief Request parsing termination. A rule callback may call this method via @b sender.terminate() to request immediate exit from the parser sequence. A rule may also quit the parser by returning the special "#quit" state. */ function terminate() self._proceed = false end function traceText( line ) > "[PTRC:text] <<", line, ">>" end function traceStates() > "[PTRC:stat] ", ", ".merge(self._stateNames) end function traceMsg( msg ) > "[PTRC:msg ] ", msg end function traceRule( r, context ) >> "[PTRC:rule] (ctx depth:", context.nodeStack.len(), ") Applying ", r.className(), "(" if r provides token: >> '"', r.token.escape(), "\", " >> '"', r.nextState, '")' > " matcing ", r.matchLen, " chars." end end export modules/falcon/parser/genparser/000077500000000000000000000000001176363201700172605ustar00rootroot00000000000000modules/falcon/parser/genparser/context.fal000066400000000000000000000053251176363201700214350ustar00rootroot00000000000000/* FALCON - Generic Parser FILE: context.fal Generic parser, Syntactic Tree context. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 19 Aug 2010 01:34:20 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from .node /*# Syntree context. Has support to create the syntree as the parsing proceeds. */ class Context() row = 1 topNode = node.Node( node.node_top_type ) nodeStack = [ self.topNode ] standouts = [] currentNode = self.topNode listDepth = 0 /*# Adds a node in the context stack. @param lnode Node to be added. */ function pushNode( lnode ) self.currentNode.add( lnode ) self.nodeStack.add( lnode ) self.currentNode = lnode if lnode provides standout and lnode.standout self.standouts.add( lnode ) end end /*# Remove last node in the stack. @return The topmost stack node. */ function popNode() ns = self.nodeStack lnode = ns[-1] if ns.len() == 1 raise ParseError( 10010, "Context empty in pop" ) end ns.resize(ns.len()-1) self.currentNode = ns[-1] return lnode end /*# Adds data to the current node. @param Data the data to be added. The data may be either a string or a full node-headed structure. */ function add( data ) self.currentNode.add( data ) if data provides standout and data.standout self.standouts.add( data ) end end /*# Creates a list and sets the list depth. @param lvl Current level of the list. @param ltype Type of list. Can make a list deeper or flatter. Setting level to 0 is the same as closing the list. The type of the list may be "ol" or "ul". */ function setListDepth( lvl, ltype ) if lvl == self.listDepth self.popNode() self.pushNode( node.Node("li") ) elif lvl < self.listDepth // we must go back of 2 elements per level i = lvl while i++ < self.listDepth self.popNode() self.popNode() end self.listDepth = lvl // but also, change the item that was li-ing if the list continues if lvl > 0 self.popNode() self.pushNode( node.Node("li") ) end else // we must go forward of 2 elements per level i = self.listDepth while i++ < lvl self.pushNode(node.Node(ltype)) self.pushNode(node.Node("li")) end end self.listDepth = lvl end end modules/falcon/parser/genparser/node.fal000066400000000000000000000052551176363201700207000ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language Generic Parser - Line oriented general rule based parser. FILE: node.fal The syntactic tree. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin:Tue, 17 Aug 2010 22:15:59 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from struct.tree node_text_type = "text" node_top_type = "top" function _isBlank( c ) return c == 0x20 or c == 0xD or c == 0xA or c == 0x8 end class Node( type, text ) from struct.tree.Node( text ) type = type /*# If true, will be moved notified to the renderer while processing. */ standout = false add = self._nilAdder function _nilAdder( data ) select data case StringType self.content = data self.add = self._stringAdder case Node self.appendChild( data ) self.add = self._listAdder default raise ParseError( 10001, "Invalid data added to a node: " + data.describe() ) end end function _stringAdder( data ) select data case StringType self.content += data case Node n = Node( node_text_type, self.content ) self.appendChild( n ) self.appendChild( data ) self.content = nil self.add = self._listAdder default raise ParseError( 10001, "Invalid data added to a node: " + data.describe() ) end end function _listAdder( data ) select data case StringType if self.lastChild.type == node_text_type self.lastChild.content += data else self.appendChild( Node( node_text_type, data ) ) end case Node self.appendChild( data ) default raise ParseError( 10001, "Invalid data added to a node: " + data.describe() ) end end end class InfoNode( type, infos, standout ) from Node( type ) infos = infos standout = standout ? true :false end class LevelNode( type, level, data ) from Node( type, data ) level = level end /*# A node that should standout. This node is somewhere in the syntree, but it must be brougt to a general list of standout nodes. Used for tocs, categories, footnotes and so on. */ class StandoutNode( type, data ) from Node( type, data ) standout = true end class TagNode( tagType, tagData ) from InfoNode( "tag", tagData ) tag = tagType end modules/falcon/parser/genparser/rules.fal000066400000000000000000000211701176363201700210770ustar00rootroot00000000000000/* FALCON - Generic Parser FILE: Rules.fal Generic customizable grammar parser. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 17 Aug 2010 15:49:48 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import Regex from regex /*# Base rule. An abstract class. */ class Rule( nextState, action ) nextState = nextState action = action matchLen = 0 lookAhead = false function match( text, pos ) return -1 end function apply( context ) if self.action: self.action(context) return self.matchLen end end /*# @brief Class representing a token rule. Token rules are simply fixed length rules matching the input text. @param token the string matching a fixed token @param action the action (function) that should be done when hitting the token. The function will be called with the token as last parameter. */ class TokenRule( token, nextState, action ) from Rule( nextState, action ) token = token separators = " \r\n\t,.!?;:*+-/|^'\"\\#<>()[]" function match( text, pos ) t = self.token if (pfind = text.find( t, pos ) ) >= 0 if (pfind == 0 or self.isSep( text[(pfind-1)] )) and \ (pfind + t.len() == text.len() or self.isSep( text[(pfind + t.len())] )) return pfind end end return -1 end function apply( ctx ) if self.action: self.action( ctx, self.token ) self.matchLen = self.lookAhead ? 0 : self.token.len() return self.matchLen end function isSep( item ) return item in self.separators end end /*# Class representing a language token, with look ahead. */ class TokenRuleLA( token, nextState, action ) from TokenRule( token, nextState, action ) lookAhead = true end /*# @brief Class representing a tag rule. Token rules are simply fixed length rules matching the input text. @param token the string matching a fixed token @param action the action (function) that should be done when hitting the token. The function will be called with the token as last parameter. */ class TagRule( tag, nextState, action ) from Rule( nextState, action ) token = tag function match( text, pos ) t = self.token if (pfind = text.find( t, pos ) ) >= 0 return pfind end return -1 end function apply( ctx ) if self.action: self.action( ctx, self.token ) self.matchLen = self.token.len() return self.matchLen end end /*# @brief Eats blanks */ class BlanksRule() from Rule( "#stay" ) matchLen = 0 function match( text, pos ) ipos = pos l = text.len() while pos < l c = text[*pos] if (c != 0x20 and c != 0x8 and c != 0x0D and c != 0x0A) self.matchLen = pos - ipos if self.matchLen > 0: return ipos end ++pos end return -1 end end /*# @brief Regular expression enabled ruler matcher. @param token the string matching a fixed token @param action the action (function) that should be done when hitting the token. The function will be called with the token as last parameter. */ class ReRule( re, nextState, action ) from Rule( nextState, action ) anchored = re.startsWith("^") token = re re = regex.Regex(re) grabs = nil eol = false function match( text, pos ) // can't match "^" past the begin of the line if self.anchored and pos > 0: return -1 re = self.re // We need our grabs if (rng = re.find( text, pos )) g = [] n = 0 recapcount = re.capturedCount() while n < recapcount g += text[re.captured(n)] ++n end self.grabs = g return rng[0] end return -1 end function apply( context ) g = self.grabs if self.action: (.[ self.action context ] + g)() self.matchLen = g[0].len() // allows eating EOL if self.eol: return self.matchLen + 1 return self.matchLen end end /*# @brief Regular expression enabled ruler matcher -- consuming end of line. @param token the string matching a fixed token @param action the action (function) that should be done when hitting the token. The function will be called with the token as last parameter. */ class ReRuleEOL( re, nextState, action ) from ReRule( re, nextState, action ) eol = true end /*# @brief Regular expression matcher with look ahead @param token the string matching a fixed token @param action the action (function) that should be done when hitting the token. The function will be called with the token as last parameter. This rule won't indicate how many characters have been matched, causing an application of the rule and a state change but not discarding the matched characters. */ class ReRuleLA( re, nextState, action ) from ReRule( re, nextState, action ) lookAhead = true function apply( context ) g = self.grabs if self.action: (.[ self.action context ] + g)() return 0 end end /*# @brief Empty line rule matcher. */ class EmptyLineRule( nextState, action ) from Rule( nextState, action ) matchLen = 0 function match( text, pos ) if text.len() == 0: return 0 return -1 end end /*# @brief Matches a single character. */ class CharRule( nextState, action ) from Rule( nextState, action ) grabbedChar = " " matchLen = 1 function match( text, pos ) if text.len() > pos self.grabbedChar = text[pos] return pos end return -1 end function apply( ctx ) if self.action: self.action( ctx, self.grabbedChar ) return 1 end end /*# Empty rule meant to immediately do an action and change state without consuming input. */ class DummyRule( nextState, action ) from Rule( nextState, action ) function match( text, pos ) return pos end end /*# Rule matching the end of line */ class EOLRule( nextState, action ) from Rule( nextState, action ) function match( text, pos ) return text.len() end end /*# Rule matching the end of line, with look ahead. This prevents exiting from the loop checking this line when EOLRuleLA is matched */ class EOLRuleLA( nextState, action ) from EOLRule( nextState, action ) lookAhead = true end /*# Rule combining one or more sub-rules. @param rules An array of rules that may be matched. @param nextState The state where to go if a sub-rule is matched. @param actionPre An action to run as action(context, winningRule) @b before winning rule action is performed. @param actionPost An action to run as action(context, winningRule) @b after winning rule action is performed. If @b nextState is nil, then the winning rule state transition is applied, otherwise @b nextstate overrides winning rule state setting. If @b nextState contains a "*" character, it is replaced with the state from the rule. So, you can have "#pop;*" to first pop the current state and then apply the state transition from the winner rule. */ class ProxyRule( rules, nextState, actionPre, actionPost) from Rule( nextState, actionPre ) rules = rules actionPost = actionPost winningRule = nil hasState = nextState ? true : false incState = "*" in nextState origState = nextState function match( text, pos ) self.winningRule = nil n = 0 l = self.rules.len() res = -1 matchPos = text.len() while n < l res = self.rules[n].match( text, pos ) if res == 0 // we have the winner self.winningRule = self.rules[n] return res elif res > -1 and res < matchPos matchPos = res self.winningRule = self.rules end ++n end return res end function apply( ctx ) if self.winningRule if self.action: self.action( ctx, self.winningRule ) retval = self.winningRule.apply(ctx) if self.actionPost: self.actionPost( ctx, self.winningRule ) if self.hasState if self.incState self.nextState = self.origState.replace( "*", self.winningRule.nextState ) end // else, let the state it has. else self.nextState = self.winningRule.nextState end self.matchLen = retval return retval end self.matchLen = 0 return 0 end end modules/falcon/parser/render/000077500000000000000000000000001176363201700165515ustar00rootroot00000000000000modules/falcon/parser/render/docbook.fal000066400000000000000000000322351176363201700206620ustar00rootroot00000000000000/* FALCON - Generic Parser FILE: doocbook.fal Generates a doocbook output of an input genparser.Context tree ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 30 Aug 2008 09:42:22 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# Doocbook Renderer. @optparam frame An object used to surround produced data with proper docbook framing. The docbook renderer transforms the input text in a valid docbook output, which may be inserted under a
    entry of a DocBook 5.1 document. Heading elements are treated as sections, and properly nested based on their level. If given, the @b frame object will be used to complete the document by appending and prepending proper XML and docbook starters. The @b frame object will provide an open() method that shall return the docbook code to declare the document (including the xml document declaration) and a close() method so that the whole document can be built through @code frame.open() + rendered_content + frame.close() @endcode */ class Renderer( frame ) blevel = 0 frame = frame renderers = [ "text" => {node => Renderer.sgmlEscape(node.content) }, "b" => .[ self._render_markup "emphasis" "role=\"bold\""], "i" => .[ self._render_markup "emphasis" nil], "u" => .[ self._render_markup "emphasis" nil], "sup" => .[self._render_markup "superscript" nil], "sub" => .[self._render_markup "subscript" nil], "pre" => .[self._render_markup "literal" nil], "tt" => .[self._render_markup "literal" nil], "ol" => .[self._render_markup "orderedlist" nil ], "ul" => .[self._render_markup "itemizedlist" nil ], "li" => .[self._render_li], "hr" => self._render_hr, "br" => self._renderUNKNOWN, "para" => self._render_para, "header" => self._render_header, "table" => self._render_table, "tr" => self._render_tr, "th" => self._render_header_cell, "td" => self._render_cell, "indent" => self._render_indent, "quote" => self._render_quote, "link" => self._render_link, "file" => self._render_file, "img" => self._render_img, "code" => self._render_code, "plugin" => self._render_plugin, "tag" => self._renderUNKNOWN ] init if frame if not frame provides open or not frame provides close raise GenericError( 10000, i"The 'frame' parameter must provide a callable open/close method pairs" ) end end end function render( context ) //preprocess standouts //self.preprocess( context.standouts ) // and return the content content = self.rcont( context.topNode ) // close opened sections while self.blevel > 0 if self.frame content += self.frame.closeSection( self.blevel ) else content += "
    \n" end self.blevel-- end f = self.frame if f return f.open() + content + f.close() else return content end end /* function preprocess( sout ) end */ function renderNode( node ) if node.type in self.renderers return self.renderers[ node.type ]( node ) else return self._renderUNKNOWN( node ) end end function rcont( node ) content = node.content if content: return Renderer.sgmlEscape( content ) text = "" node = node.firstChild while node text += self.renderNode( node ) node = node.next end return text end //========================================================== // rendering functions // function _renderUNKNOWN( node ) return "UNKNOWN NODE TYPE: " + node.type + " - " +self.rcont( node ) + "\n" end function _render_para( node ) content = self.rcont(node) return content ? "" + content +"\n" : "" end function _render_markup( tag, attribs, node ) return "<" + tag + (attribs ? " " +attribs : "") +">" + self.rcont(node) + "" end function _render_li( node ) return "" + self.rcont(node) + "\n" end function _render_hr( node ) return "-----------------------------------------------\n" end function _render_header( node ) content = self.rcont( node ) return self.open_section( node.level, content, URI.encode(content) ) end function open_section( level, id, title ) prefix = self.go_to_level( level ) if self.frame header = self.frame.openSection( level, id ) else header = "
    \n" end self.blevel = level + 1 return prefix + header + "" + title + "\n" end function close_section( level ) prefix = self.go_to_level( level ) if self.frame header = self.frame.closeSection( level ) else header = "
    \n" end self.blevel = level return prefix + header end function go_to_level( l ) prefix = "" while self.blevel+1 < l self.blevel++ prefix += self.frame ? self.frame.openSection( self.blevel ) : "
    \n" end while self.blevel-1 > l prefix += self.frame ? self.frame.closeSection( self.blevel ) : "
    \n" self.blevel-- end return prefix end function _render_table( node ) return "\n\n" + self.rcont(node) + "\n" end function _render_tr( node ) return ""+ self.rcont(node) + "\n" end function _render_header_cell( node ) return ""+ self.rcont(node) + "\n" end function _render_cell( node ) return ""+ self.rcont(node) + "\n" end function _render_indent( node ) return "" + (" " * node.level ) + "" + self.rcont(node) + "\n" end function _render_link( node ) i = bless(node.infos) link = "" + Renderer.sgmlEscape(i.text ? i.text : i.name) + "" return link end function _render_file( node ) //todo end function _render_quote( node ) attrib = node.infos["link"] if attrib return "
    " + Renderer.sgmlEscape(attrib) + "\n" + self.rcont(node) +"
    \n" else return "
    " + self.rcont(node) +"
    \n" end end function _render_img( node ) // todo end function _render_code( node ) return ""+ self.rcont(node) + "\n" end function _render_plugin( node ) // not supported end function sgmlEscape( content ) /* static amp = "&"[*0] lt = "<"[*0] gt = ">"[*0] qt = "\""[*0] end i = 0 l = content.len() res = strBuffer( l ) olds = 0 while i < l c = content[*i] if c < 32 or c > 127 res += content[olds:i] res += "&#"+c+";" olds = i+1 elif c == amp res += content[olds:i] res += "&" olds = i+1 elif c == gt res += content[olds:i] res += ">" olds = i+1 elif c == lt res += content[olds:i] res += "<" olds = i+1 elif c == qt res += content[olds:i] res += """ olds = i+1 end ++i end return res + content[olds:] */ return content.replace( "&", "&").replace( "<", "<" ).replace( ">", ">" ).replace("\"", """ ) end end /*# Frame creating a book frame @param encoding The target encoding that will be placed in the xml declaration @param name The name of the book @param title Title of the book as a whole @optparam author The author @optparam copyyear Year of copyright @optparam copyholder Holder of copyright */ class BookFrame( encoding, name, title, author, copyyear, copyholder ) encoding = encoding name = name title = title author = author copyyear = copyyear copyholder = copyholder function open() str = "\n" str += " \n" str += "\n" str += "\n" str += "" + Renderer.sgmlEscape( self.title ) + "\n" if self.author str += "" + Renderer.sgmlEscape( self.author ) + "\n" end if self.copyyear and self.copyholder str+= "\n" str += "" + self.copyyear + "\n" str += "" + Renderer.sgmlEscape( self.copyholder ) + "\n" str+= "\n" end str += "\n" return str end function close() return "\n" end function openSection( level, id ) ids = id ? " id=\"" + strEscape(id) +"\"" : "" if level == 0 return @"\n" else return @"\n" end end function closeSection( level ) if level == 0 return "\n" else return @"\n" end end end /*# Frame creating a book frame -- in docbook 5 @param encoding The target encoding that will be placed in the xml declaration @param name The name of the book @param title Title of the book as a whole @optparam author The author @optparam copyyear Year of copyright @optparam copyholder Holder of copyright */ class Book5Frame( encoding, name, title, author, copyyear, copyholder ) encoding = encoding name = name title = title author = author copyyear = copyyear copyholder = copyholder function open() str = "\n" str += "\n" str += "\n" str += "" + Renderer.sgml( self.title ) + "\n" if self.author str += "" + Renderer.sgmlEscape( self.author ) + "\n" end if self.copyyear and self.copyholder str+= "\n" str += "" + self.copyyear + "\n" str += "" + Renderer.sgmlEscape( self.copyholder ) + "\n" str+= "\n" end str += "\n" return str end function close() return "\n" end function openSection( level, id ) ids = id ? " id=\"" + strEscape(id) +"\"" : "" if level == 0 return @"\n" else return @"\n" end end function closeSection( level ) if level == 0 return "\n" else return "\n" end end end /*# Frame creating an article frame @param encoding The target encoding that will be placed in the xml declaration @optparam optparam lang The language of the article. */ class ArticleFrame( encoding, lang ) encoding = encoding lang = lang ? lang : "" function open() str = "\n" str += " \n" str += "\n" end function openSection( level, id ) return "\n" end function closeSection( level ) return "\n" end end /*# Frame creating an article frame in DocBook 5 @param encoding The target encoding that will be placed in the xml declaration @optparam optparam lang The language of the article. */ class Article5Frame( encoding, lang ) encoding = encoding lang = lang ? lang : "" function open() str = "\n" str += "
    \n" end function openSection( level, id ) return "\n" end function closeSection( level ) return "\n" end endmodules/falcon/parser/render/html.fal000066400000000000000000000160321176363201700202030ustar00rootroot00000000000000/* FALCON - Generic Parser FILE: html.fal Generates an html file out of an input genparser.Context tree ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 30 Aug 2008 09:42:22 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import SiteHelper from .sitehelper as SiteHelper /*# Default renderer with basic functionalities. */ class BaseSiteHelper from SiteHelper function code(v, lang) return "
    \n"+v.replace("&","&").replace( "<","<").replace(">", ">" )+"
    " end function pageLink( v ) return "/"+URI.encode(v) end function iLink( wiki, v ) return @"http://www.$(wiki).com/wiki/" + URI.encode(v) end function img( v ) return v end function file( v ) return v end function plugin( v, params, node ) return @"

    Placeholder for plugin \"$v\"($params)

    " end end /*# Html Renderer. @param siteHelper A site helper in the class of @param cls Class used to render block elements (for easier CSS application). */ class Renderer( siteHelper, cls ) cls = cls ? cls : "" site = siteHelper ? siteHelper : BaseSiteHelper() _btype = nil _blevel = 0 renderers = [ "text" => {node => node.content}, "b" => self._render_markup, "i" => self._render_markup, "u" => self._render_markup, "sup" => self._render_markup, "sub" => self._render_markup, "pre" => self._render_markup, "tt" => self._render_markup, "ol" => self._render_markup, "ul" => self._render_markup, "li" => self._render_markup, "hr" => self._render_break, "br" => self._render_break, "para" => self._render_para, "header" => self._render_header, "table" => self._render_table, "tr" => self._render_tr, "th" => self._render_cell, "td" => self._render_cell, "indent" => self._render_indent, "quote" => self._render_quote, "link" => self._render_link, "file" => self._render_file, "img" => self._render_img, "code" => self._render_code, "plugin" => self._render_plugin, "tag" => self._render_tag ] function render( context, page_name ) self.site.page_name = page_name self.site.topnode = context.topNode //preprocess standouts self.preprocess( context.standouts ) // and return the content return self.rcont( context.topNode ) end function preprocess( sout ) i = 0 soutlen = sout.len() while i < soutlen node_sout = sout[i++] if node_sout.type == "plugin" self.site.plugin( node_sout.infos["name"], node_sout.infos["params"], node_sout, true ) end end end function renderNode( node ) if node.type in self.renderers return self.renderers[ node.type ]( node ) else return self._renderUNKNOWN( node ) end end function rcont( node ) content = node.content if content: return content text = "" node = node.firstChild while node text += self.renderNode( node ) node = node.next end return text end //========================================================== // rendering functions // function _renderUNKNOWN( node ) return self._render_markup(node) end function _ct( tag ) if self.cls return "<" + tag + " class=\""+self.cls+"\">" else return "<" + tag + ">" end end function _render_para( node ) return self._ct("p") + self.rcont(node)+"

    \n" end function _render_markup( node ) return "<" + node.type + ">" + self.rcont(node) + "" end function _render_break( node ) return "<" + node.type + "/>\n" end function _render_header( node ) content = self.rcont( node ) return ""+ self._ct("h" + node.level) + content + "\n\n" end function _render_table( node ) return self._ct("table") + "\n\n" + self.rcont(node) + "\n" end function _render_tr( node ) return ""+ self.rcont(node) + "\n" end function _render_cell( node ) return self._render_markup( node ) end function _render_indent( node ) return self._ct("div style=\"margin-left:" + 2 * node.level + "em\"") + self._ct("p") + self.rcont(node) + "

    \n" end function _render_link( node ) i = bless(node.infos) link = "" return link end function _render_file( node ) i = bless(node.infos) link = "" + (i.text ? i.text : i.name) + "" return link end function _render_quote( node ) i = bless(node.infos) if i.link str = self._ct("blockquote cite=\"" + strEscape(i.link) + "\" ") else str = self._ct("blockquote") end str += self.rcont(node) +"\n" return str end function _render_img( node ) i = bless(node.infos) link = "" style = "" if "thumb" in i link +="" end link += "" + self.rcont( node ) + "\n" end end modules/falcon/parser/render/sitehelper.fal000066400000000000000000000036361176363201700214110ustar00rootroot00000000000000/* FALCON - Generic Parser FILE: sitehelper.fal Helper for generic parser renderers. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 30 Aug 2008 09:42:22 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# Base abstract class for site helpers. Site helpers are classes used to render items whose rendering is specific to a certain site. For example, inline images and link to file requre a knowledge of the target site that can't be directly embedded in the renderer. The user of the renderer classes should use class to help rendering those elements whose final format depends on where they should be rendered. Those are namely: - code elements, which may require special formatting. - link to internal wiki pages, that must be generated by the owning engine. - Wiki interlinks. - img elements. - Link to local file resources. - plugins. As some of these elements (most notabily the plugins) may require to have some context information, the site helper can be initialized using a entity id or page name, and the syntax top node (or tree root). */ class SiteHelper( page_name, topnode ) page_name = page_name topnode = topnode function code(v, lang) raise "SiteHelper.code To be implemented" end function pageLink( v ) raise "SiteHelper.pageLink To be implemented" end function iLink( wiki, v ) raise "SiteHelper.iLink To be implemented" end function img( v ) raise "SiteHelper.img To be implemented" end function file( v ) raise "SiteHelper.file To be implemented" end function plugin( plugin_name, params, node, isDuringStandout ) return "SiteHelper.plugin To be implemented" end end modules/falcon/struct/000077500000000000000000000000001176363201700153225ustar00rootroot00000000000000modules/falcon/struct/tree.fal000066400000000000000000000101221176363201700167410ustar00rootroot00000000000000/* FALCON - Generic modules FILE: tree.fal Generic structures: tree ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 28 Sep 2010 15:30:16 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# Generic Node of a tree structure. @optparam content Some generic payload for the tree structure. */ class Node( content ) content = content parent = nil next = nil prev = nil //# first child of the children of this node firstChild = nil //# last child of the children of this node lastChild = nil /*# Exchange this node with another one. @pram node The new node. */ function change( node ) node.parent = self.parent node.next = self.next node.prev = self.prev if self.next self.next.prev = node else if self.parent: self.parent.lastChild = node end if self.prev self.prev.next = node else if self.parent: self.parent.firstChild = node end end /*# Inserts the parameter before this node @pram node The node to be inserted. */ function insertBefore( node ) if self.prev self.prev.next = node else // we're the last node of our parent. if self.parent self.parent.firstChild = node end end node.prev = self.prev node.next = self node.parent = self.parent self.prev = node end /*# Inserts the parameter after this node @pram node The node to be inserted. */ function insertAfter( node ) if self.next self.next.prev = node else // we're the first node of our parent. if self.parent self.parent.lastChild = node end end node.next = self.next node.prev = self node.parent = self.parent self.next = node end /*# Detach this node from the tree @return The next node, if it exists. */ function remove() if self.next == nil if self.prev == nil // we are the only child of our parent? if self.parent != nil self.parent.firstChild = nil self.parent.lastChild = nil end else self.prev.next = nil if self.parent != nil // however, without next we're the last child self.parent.lastChild = self.prev end end else self.next.prev = self.prev if self.prev == nil // we are the FIRST child of our parent if self.parent != nil self.parent.firstChild = self.next end else self.prev.next = self.next end end return self.next end /*# Adds a child after the already present children. @param The child to be added. */ function appendChild( child ) child.prev = self.lastChild if self.lastChild self.lastChild.next = child else self.firstChild = child end self.lastChild = child child.parent = self end /*# Adds a child in front of the children @param The child to be added. */ function prependChild( child ) child.next = self.firstChild if self.firstChild self.firstChild.prev = child else // this is the first child self.lastChild = child end self.firstChild = child child.parent = self end /*# Recursively traverse the node tree @param f A callable that will be called with each node. The @b f function is called frist on this node, and then the traverse method of each children is called. This makes the structure descend-first-then-call structure. */ function traverse( f ) f( self ) node = self.firstChild while node != nil node.traverse( f ) node = node.next end end end modules/falcon/web/000077500000000000000000000000001176363201700145535ustar00rootroot00000000000000modules/falcon/web/ajax.fal000066400000000000000000000171601176363201700161670ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: ajax.fal Module supporting AJAX hybrid client-server development. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 14 Jul 2010 14:26:45 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import JSONencode from json as JSONencode import ZLib from zlib as ZLib const zip_mode = "deflate" /*# @main AJAX hybrid server-client support. @note As this is a web component, this module is to be used under WOPI. This module simplifies the development of AJAX components of dynamic web-sites. It consists of a master class @a Function that helps implementing server-site web functions participating in AJAX data exchanges. */ /*# AJAX function parameter. @param name Name of the parameter @optparam mand If true, the parameter is mandatory; if not received from the web, an error will be returned. @optparam check Fucntion performing consistency checks on the received value. */ class Param( name, mand, check ) name = name mand = mand check = check end /*# Ajax server-side function wrapper. @param dry Just create a descriptive instance. */ class Function(dry) //# Name of the fucntion -- defaults to the class. name = nil //# @ignore dry = dry //# Compress the reply zipReply = false //# Set the max age for a reply (?) maxAge = "1728000" //# Array of allowed origins (nil to allow all) originFilter = nil //# Specify an array of application-wise headers that you want to pass through, or nil for none headerFilter = nil //# Errors that can be generated by this function errorMap = nil //# Array of Parameters object describing the parameters of this entity parameters = nil /*# Generats a dictionary that can be sent as an error report. @param id The error ID @optparam detail Detail to be added to the error description. Returns a dictionary containing the following entries: - error: The @b id passed as parameter. - edesc: Error descrption, taken from the @a errorMap property, if the @b id error code is associated to a description. If not, this element is not inserted in the returned dictionary. - detail: If the @b detail parameter is specified, this key is added to the dictionary and the @b detail value is added here. The following errors are added to all the Ajax subclasses, and are always available: - -1 = Uncaught exception - -2 = Missing mandatory parameters - -3 = run() method not reimplmented */ function error( id, detail ) edict = ["error" => id] if detail edict += ["detail" => detail ] end if id in self.errorMap edict += ["edesc" => self.errorMap[id] ] end return edict end //# @ignore function setup() missing = [] // grab the parameters func = [self.run] for param in self.parameters value = Request.getField( param.name, nil ) if value if param.check value = param.check( value, self ) end elif param.mand missing += param.name end // anyhow, form the call func += value end if missing: raise self.error( -2, ",".merge(missing) ) return func end /*# Method implementing the functionalities of this AJAX function. @return A dictionary of key-value objects to be sent as ajax values. */ function run() raise self.error( -3 ) end /*# Returns a list of names the parameters of this AJAX function. @return A list of parameter names. */ function paramNames() return map( { v => v.name}, self.parameters ) end //================================================================ // Ajax specifics // function _handleOptions() h = Request.headers if "Access-Control-Request-Method" in h mth = strUpper( h["Access-Control-Request-Method"] ) if mth != "POST" and mth != "GET" and mth != "ORIGIN" Reply.status = 501 Reply.reason = "Method " + mth + " not allowed" Reply.commit() return end end Reply.setHeader( "Access-Control-Allow-Methods", "POST, GET, OPTIONS" ) Reply.setHeader( "Access-Control-Max-Age", self.maxAge ) self._filterOrigin() if "Access-Control-Request-Headers" in h if self.headerFilter chead = ", ".merge(self.headerFilter) Reply.setHeader( "Access-Control-Allow-Headers", chead ) end // otherwise, we don't pass through any application header. end Reply.setHeader( "Content-Length", "0" ) Reply.setHeader( "Content-Type", "application/json" ) self._checkZip() Reply.commit() end function _handleAJAXReq() self._filterOrigin() self._checkZip() h = Request.headers for passHeader in self.headerFilter if passHeader in h Reply.setHeader( passHeader, h[passHeader] ) end end end function _filterOrigin() h = Request.headers if self.originFilter if "Origin" in h origin = h["Origin"] if origin in self.originFilter Reply.setHeader( "Access-Control-Allow-Origin", origin ) end end else Reply.setHeader( "Access-Control-Allow-Origin","*" ) end end function _checkZip() h = Request.headers if self.zipReply // is the remote side agreeing? if "Accept-Encoding" in h if zip_mode in map( {v=>v.trim()}, h["Accept-Encoding"].split(",") ) Reply.setHeader( "Content-Encoding", zip_mode ) return end end // nope, remote won't accept self.zipReply = false end end function _process() // If the remote asked our options... if Request.method == "OPTIONS" // just tell who we are and exit. self._handleOptions() return else // else, prepare a vaild ajax reply. self._handleAJAXReq() end try // Read the paramters and check the value func = self.setup() // Run the subclass run method. result = func() catch DictionaryType in errorDesc result = errorDesc catch in error result = self.error( -1, error.toString() ) end retval = JSONencode( result ) // should we encode the result? if self.zipReply retval = ZLib.compress( retval ) end // dispatch the data. Reply.commit() stdOut().write( retval ) end //======================================================== // Post creation initialization // [init] function __enter() // --- sets the name of the function if not already set self.name = self.className().replace( "%", "") // --- adds the standard error map standardErrors = [ -1 => i"Uncaught exception", -2 => i"Missing mandatory parameters", -3 => i"run() method not reimplmented" ] if self.errorMap self.errorMap += standardErrors else self.errorMap = standardErrors end if not self.dry self._process() end end end end modules/falcon/web/ajax/000077500000000000000000000000001176363201700154765ustar00rootroot00000000000000modules/falcon/web/ajax/library.fal000066400000000000000000000154431176363201700176350ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language FILE: library.fal Module supporting AJAX hybrid client-server development. Client-side helpers to talk with Falcon AJAX servers. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 14 Jul 2010 14:26:45 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from compiler /*# JSon decoder for javascript. Original work in public domain by JSON.org at @a http://www.JSON.org/json2.js. See the original copyleft: @code http://www.JSON.org/json2.js 2010-08-25 Public Domain. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. See http://www.JSON.org/js.html This code should be minified before deployment. See http://javascript.crockford.com/jsmin.html @endcode */ _json_decode = ' if (!this.Falcon){this.Falcon={}} (function() { if (!Falcon.req) { Falcon.req = new XMLHttpRequest(); } if (!Falcon.JSON) { Falcon.JSON ={}; } var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, gap,indent,meta = {''\b'': ''\\b'',''\t'': ''\\t'',''\n'': ''\\n'',''\f'': ''\\f'',''\r'': ''\\r'',''"'' : ''\\"'',''\\'': ''\\\\''},rep; if (typeof Falcon.JSON.parse !== ''function'') { Falcon.JSON.parse = function (text, reviver) { var j; function walk(holder, key) { var k, v, value = holder[key]; if (value && typeof value === ''object'') { for (k in value) { if (Object.hasOwnProperty.call(value, k)) { v = walk(value, k); if (v !== undefined) { value[k] = v; } else { delete value[k]; }}}} return reviver.call(holder, key, value); } text = String(text); cx.lastIndex = 0; if (cx.test(text)) { text = text.replace(cx, function (a) { return ''\\u'' + (''0000'' + a.charCodeAt(0).toString(16)).slice(-4); });} if (/^[\],:{}\s]*$/ .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, ''@'') .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, '']'') .replace(/(?:^|:|,)(?:\s*\[)+/g, ''''))) { j = eval(''('' + text + '')''); return typeof reviver === ''function'' ? walk({'''': j}, '''') : j;} throw new SyntaxError(''JSON.parse'');};} if (typeof Falcon.call !== ''function'') { Falcon.call = function (uri, onSuccess, onError, onFailure ) { var http=Falcon.req; http.open("GET", uri, true); http.onreadystatechange = function () { if (http.readyState == 4) { // data complete if ( http.status >= 200 && http.status < 300 ) { try{ var reply = Falcon.JSON.parse( http.responseText ); if( reply.error != null ) { if( onError != null ) { onError( reply.error, reply.edesc, reply.detail ); } } else { onSuccess( reply ); } } catch( e ) { if( e.name.toString()=="SyntaxError" ) { onError( 0, "JSON Decode Error", http.responseText ) } else { alert(e.name + "\n" + e.message) } } } else { onFailure( http.status ); } } }; http.send(null); }} ' _close = '}());' /*# AJAX Library generator. @param baseUri The URI where the AJAX functions can be found. @param basePath Path where the function modules should be searched. @param functions List of function names to be loaded. This class generates a javascript library to be used in pair with ajax functions. It is means to be instantiated in a fal script that is then loaded as a javascript source from a remote HTTP client It reads the specified functions from their own .fal file and creates prototype calls for javascript. An example library script that we may call library_js.fal may look like: @code import Generator from web.ajax.library as Generator Generator( "http://api.server.com/path", "./", .[ "MakeThis" "MakeThat" "MakeThatToo"] ).generate() @endcode The generator will generate javascript functions like: @code Falcon.( onSuccess, onError, onFailure ).call( param1, param2 ... paramN ); @endcode where: - @b onSuccess: is a callback that is called with the JSON object returned by the Falcon JSON api if everything was successful. - @b onError: is a callback that is invoked if the API generated an application error; it will receive the error ID, the error description and an optional error detail explaining exactly what happened. - @b onFailure: is a callback generated when the contact with the remote server failed, or the AJAX remote function failed to generate a valid success or error reply. The parameters of the call() method of the returned object are in the same order as specified for the API. They will be added as = to the query string if not null. */ class Generator( baseUri, basePath, functions ) baseUri = baseUri basePath = basePath functions = functions _comp = compiler.Compiler() init self._comp.launchAtLink = false if not baseUri.endsWith( "/" ) self.baseUri += "/" end end function generate() Reply.ctype( "application/x-javascript" ) stdOut().write( _json_decode ) for func in self.functions self.makeFunc( func ) end stdOut().write( _close ) end function makeFunc( funcName ) path = Path( self.basePath ) path.file = funcName path.extension = "fal" mod = self._comp.loadFile( path.path ) inst = mod.get( funcName )(true) params = inst.paramNames() uri = self.baseUri + funcName + ".fal" prototype = ",".merge(params) uriCheck = "var uriVars= \"\";\n" for varName in params uriCheck += @' if($(varName) != null) { if (uriVars == ""){uriVars ="?";}else{uriVars +="&";} uriVars += "$(varName)=" + escape($(varName)); } ' end func = @' if (typeof Falcon.$(funcName) !== ''function'') { Falcon.$(funcName) = function ( onSuccess, onError, onFailure ) { return { call: function($(prototype)){ $(uriCheck) Falcon.call( "$(uri)"+uriVars, onSuccess, onError, onFailure ); } } } } ' stdOut().write( func ) end end modules/falcon/web/htmaker.fal000066400000000000000000001632651176363201700167070ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. Hyper Text Maker - XHTML generator FILE: htmaker.fal Main module file ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 11 Jun 2010 21:44:23 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import HTMError from self.htmerror as HTMError /****************************************************************** Options ******************************************************************/ /*# General module options. */ object HTMaker /*# Determine if tree inseretions must be checked for DOM consistency. If this switch is active, every inserted node must respect HTML 4.01 hierarcy constraints. The check is relatively heavy, and once moving in a production phase, it's hardly likely that generated trees will change. For those application that are known to create well-formed XHTML trees, this option can be turned to false to improve performance. */ checkTree = true end function _htmlEscape( text ) return text.replace( "&", "&" ).replace( "<", "<" ).replace( ">", ">" ).replace( "\"", """ ) end /****************************************************************** Base common stuff ******************************************************************/ class AttribsBase() /*# render the attributes */ function toString() string = "" for key, value in self.retrieve() key = key.replace("_", "-" ).replace("__", ":" ).lower() // boolean true values are rendered via attrib-name="attrib-name" // false values are stripped out by removing the key too if value == true string += " " + key + "=\""+key+"\"" else if value.typeId() != StringType: continue string += " " + key + "=\"" + value.replace( '"', '"' ).replace( '&', '&' ) +"\"" end end return string end end /*# Class managing event related attributes */ class EventAttribs from AttribsBase /*# Script to be run on a mouse click */ onclick = nil /*# Script to be run on a mouse double-click */ ondblclick = nil /*# Script to be run when a document load */ onload = nil /*# Script to be run when mouse button is pressed */ onmousedown = nil /*# Script to be run when mouse pointer moves */ onmousemove = nil /*# Script to be run when mouse pointer moves out of an element */ onmouseout = nil /*# Script to be run when mouse pointer moves over an element */ onmouseover = nil /*# Script to be run when mouse button is released */ onmouseup = nil /*# Script to be run when a key is pressed */ onkeydown = nil /*# Script to be run when a key is pressed and released */ onkeypress = nil /*# Script to be run when a key is released */ onkeyup = nil /*# Script to be run when a document unload */ onunload = nil end class I18nAttribs from AttribsBase /*# Specifies a language code for the content in an element */ lang = nil /*# Specifies a language code for the content in an element, in XHTML documents */ xml__lang = nil end /*# Attributes for all the DomElements */ class CoreAttribs from AttribsBase /*# Specifies a classname for an element. Will be rendered as "class" */ CLASS = nil /*# Specifies the text direction for the content in an element */ dir = nil /*# Specifies a unique id for an element */ id = nil /*# Specifies a language code for the content in an element */ style = nil /*# Specifies extra information about an element */ title = nil end /*# Utility class exposing all the attributes */ class AllAttribs from CoreAttribs, I18nAttribs, EventAttribs end class DomList( parent ) parent = parent items = [] function empty(): return self.children.len() == 0 function add( item ) if HTMaker.checkTree and item.hname and item.hname != "verbatim" if self.parent.childTypeList and item.hname notin self.parent.childTypeList pcls = self.parent.hname cls = item.hname raise HTMError( HTMError.parentship, @i"Parenting element of class \"$(cls)\" in a parent of class \"$(pcls)\"" ) end end self.items += item end end object ContentModel fontstyle = .[ 'tt' 'i' 'b' 'big' 'small' ] phrase = .[ 'em' 'strong' 'dfn' 'code' 'samp' 'kbd' 'var' 'cite' 'abbr' 'acronym' ] special = .[ 'a' 'img' 'object' 'br' 'script' 'map' 'q' 'sub' 'sup' 'span' 'bdo' ] formctrl = .[ 'input' 'select' 'textarea' 'label' 'button' ] inline = .['text'] inline_pre = nil inline_a = nil inline_label = nil heading = .[ 'h1' 'h2' 'h3' 'h4' 'h5' 'h6' ] list = .[ 'ol' 'ul' ] preformatted = .[ 'pre' ] block = .['p' 'dl' 'div' 'noscript' 'blockquote' 'form' 'hr' 'table' 'fieldset' 'address' ] block_form = nil flow = nil flow_body = nil flow_object = nil flow_fieldset = nil flow_button = nil listitem = .[ 'li' ] defitems = .[ 'dd' 'dt' ] tableitems = .[ 'caption' 'col' 'colgroup' 'thead' 'tfoot' 'tbody' ] tablerow = .[ 'tr' ] tablecol = .[ 'col' ] tablecells = .[ 'th' 'td' ] head_misc = .[ 'script' 'style' 'meta' 'link' 'object' 'base' ] options = .[ 'option' 'optgroup' ] init self.inline = self.inline + self.fontstyle + self.phrase + self.special + self.formctrl self.inline_pre = self.inline - .[ 'img' 'object' 'big' 'small' 'sub' 'sup' ] self.inline_a = self.inline - .[ 'a' ] self.inline_label = self.inline - .[ 'label' ] self.block = self.block + self.heading + self.list + self.preformatted self.block_form = (self.block + .[ 'script' ]) - .[ 'form' ] self.flow = self.inline + self.block self.flow_body = self.flow + .['ins' 'del'] self.flow_object = self.flow + .['param'] self.flow_fieldset = self.flow +.[ 'legend' ] self.flow_button = self.flow - ( .['a' 'form' 'fieldset'] ) - self.formctrl end end /*# Topmost node for XHTML documents. In rendering, it creates an XHTML compilant document. */ class XHTMLFrame( encoding, version ) encoding = encoding ? encoding : "utf-8" version = version ? version: "1.0" html = HTML(self) function render(stream) if stream == nil: stream = stdOut() stream.writeText( @' ') self.html.render( stream ) end end /*# Topmost node for Transitional documents. In rendering, it creates an HTML 4.01 (Transitional) compilant document. */ class TransitionalFrame html = HTML(self) function render(stream) if stream == nil: stream = stdOut() stream.writeText( ' ') self.html.render(stream) end end /*# Topmost node for HTML Strict documents. In rendering, it creates an HTML 4.01 (Strict) compilant document. */ class StrictFrame html = HTML(self) function render(stream) if stream == nil: stream = stdOut() stream.writeText( @' ') self.html.render(stream) end end /*# Topmost node for HTML Frameset documents. In rendering, it creates an HTML 4.01 (Frameset) compilant document. */ class FramesetFrame html = HTML(self) function render(stream) if stream == nil: stream = stdOut() stream.writeText( @' ') self.html.render(stream) end end /****************************************************************** * Dom Entities ******************************************************************/ /*# Base dom elements. The basic dom element has a real parent and 0 or more restrictions on the parents it could have. */ class BaseDom( hname, childTypeList, attribClass ) childTypeList = childTypeList parent = nil children = childTypeList ? DomList( self ) : nil eattr = attribClass != nil ? attribClass() : nil text = nil hname = hname _sepAfter = "" _sepDecl = "" function st_makesep( level ) return " " * (level * 3) end function render( level, stream ) if not level: level = 1 if not stream: stream = stdOut() sep = self.st_makesep( level ) stream.writeText( "<" + self.hname ) if self.eattr sattr = self.eattr.toString() if sattr: stream.writeText( sattr ) end // ending ? if not self.text and not self.children stream.writeText( "/>" ) if self._sepAfter: stream.writeText(self._sepAfter + sep) return end stream.writeText( ">" ) if self._sepDecl: stream.writeText(self._sepDecl + sep) if self.text.typeId() == StringType // normally does not escapes, but oob text can be passed to escape automatically if ^? self.text stream.writeText( _htmlEscape( self.text ) ) else stream.writeText( self.text ) end end if self.children for item in self.children.items item.render( level + 1, stream ) end end stream.writeText( "" ) if self._sepAfter: stream.writeText( self._sepAfter + sep ) end function add() if self.children for i in [0:paramCount()] item = parameter(i) if item.typeId() == StringType item = ^? item ? Verbatim(item) : Text( item ) elif not item.derivedFrom( BaseDom ) raise HTMError( HTMError.addnodom, self.hname, item.describe(1) ) end self.children.add( item ) item.parent = self end else raise HTMError( HTMError.nochildren, self.hname ) end return self end /*# Method to set arbitrary attributes. @param v Either a dictionary used to set the properties or a callable item. @raise HTMError if the properties in @b v are not found. @return self If @b v is a dictioanry, then properties in this item matching the dictionary kyes are searched and the corresponding values are set. If @b v is a callable, then it is called with the properties instance as the first parameter, and self as the second. Returns the self item so it can be used in chains with other methods. */ function set( v ) if v.isCallable() v( self.eattr, self ) elif v.typeId() == DictionaryType for key, val in v if key notin self.eattr raise HTMError( HTMError.noattrib, key ) end self.eattr.setProperty( key, val ) end end return self end /*# Method to set the item ID. @param value The value of the ID. @raise HTMError if this entity doesn't expose an ID property. @return self Even if not all the XHTML entities expose an ID, most do. This method is a simple way to set it. Returns the self item so it can be used in chains with other methods. */ function id( value ) if not self.eattr or not self.eattr provides id raise HTMError( HTMError.noid, self.hname ) end self.eattr.id = value return self end /*# Method to set the entity class. @param value The value of the class. @raise HTMError if this entity doesn't expose a class property. @return self Even if not all the XHTML entities expose an class, most do. This method is a simple way to set it. Returns the self item so it can be used in chains with other methods. */ function CLASS( value ) if not self.eattr or not self.eattr provides CLASS raise HTMError( HTMError.noclass, self.hname ) end self.eattr.CLASS = value return self end /*# Method to set the entity style. @param value The value of the style. @raise HTMError if this entity doesn't expose a style property. @return self Even if not all the XHTML entities expose a style, most do. This method is a simple way to set it. Returns the self item so it can be used in chains with other methods. */ function style( value ) if not self.eattr or not self.eattr provides style raise HTMError( HTMError.nostyle, self.hname ) end self.eattr.style = value return self end /*# Return the stream associated with the frame. */ function getFrame() p = self.parent while p provides parent p = p.parent end return p end end class HTML(parent) from BaseDom( "html", nil, I18nAttribs ) /*# Language of this document */ lang = "en" parent = parent head = nil body = nil function render( stream ) if not stream: stream = stdOut() if not self.head self.head = Head().add( Title("") ) end if not self.body self.body = Body() end if self.parent and self.parent.derivedFrom( XHTMLFrame ) stream.writeText( @" \n" ) else stream.writeText( "\n" ) end self.head.render(1, stream) self.body.render(1, stream) stream.writeText( "\n" ) end function add() for i in [0:paramCount()] item = parameter(i) select item case Head if self.head != nil raise HTMError( HTMError.headdef ) end self.head = item case Body, Frameset if self.body != nil raise HTMError( HTMError.bodydef ) end self.body = item default raise HTMError( HTMError.nottoplevel ) end item.parent = self end end end /****************************************************************** * Head entities ******************************************************************/ /*# Attributes used for the @a Head class. */ class HeadAttribs from I18nAttribs /*# Meta-profiles. This attribute specifies the location of one or more meta data profiles, separated by white space. For future extensions, user agents should consider the value to be a list even though this specification only considers the first URI to be significant. */ profile = nil end /*# Class encapsulating the xhtml header. If the title text (a string) is given, a Title node is automatically created. */ class Head( title ) from BaseDom( "head", ContentModel.head_misc + .['title'], HeadAttribs ) init if title self.add( Title( title ) ) end end end /*# Class encapsulating the xhtml header. If the title text (a string) is given, a Title node is automatically created. */ class Title( text ) from BaseDom( "title", nil, I18nAttribs ) hname = "title" text = text end /*# Attributes used for the @a Meta class */ class MetaAttribs from I18nAttribs /*# HTTP response header name */ http_equiv = nil /*# Metainformation name */ name = nil /*# associated information */ content = nil /*# select form of content */ scheme = nil end class Meta( name, content, http_equiv, scheme ) \ from BaseDom( "meta", nil , MetaAttribs ) init self.eattr.name = name self.eattr.content = content self.eattr.http_equiv = http_equiv self.eattr.scheme = scheme end end /****************************************************************** * Body entities ******************************************************************/ /*# Attributes used for the @a Body class */ class BodyAttribs from AllAttribs /*# The onload event occurs when the user agent finishes loading a window or all frames within a FRAMESET. This attribute may be used with BODY and FRAMESET elements. */ onload = nil /*# The onunload event occurs when the user agent removes a document from a window or frame. This attribute may be used with BODY and FRAMESET elements. */ onunload = nil end /*# Body class */ class Body from BaseDom( "body", ContentModel.flow_body , BodyAttribs ) end class FramesetAttribs from BodyAttribs rows = nil cols = nil end /*# Frameset class */ class Frameset from BaseDom( "frameset", .['frameset' 'frame' 'noframes'], FramesetAttribs ) end /****************************************************************** * Boxing tags ******************************************************************/ class FrameAttribs from CoreAttribs /*# Link to long description (complements title) */ longdesc = nil /*# name of frame for targetting -- deprecated in XHTML*/ name = nil /*# source of frame content */ src = nil /*# request frame borders? Can be 0 or 1 */ frameborder = nil /*# margin widths in pixels */ marginwidth = nil /*# margin heights in pixels */ marginheight = nil /*# allow users to resize frames? -- boolean value */ noresize = false /*# Should we have the scrollbar? -- "yes", "no" or "auto" */ scrolling = nil end class Frame from \ BaseDom( 'frame', ContentModel.flow, FrameAttribs ) // also div and frameset end class Noframes from \ BaseDom( "noframes", ContentModel.flow, AllAttribs ) // also div and frameset end /****************************************************************** * Text data ******************************************************************/ /*# Class representing a DIV html element. As "div" blocks are often used to express styles, classes or ids, they are directly exposed as parameters for user convenience. */ class Div( id, CLASS, style ) from BaseDom( "div", ContentModel.flow, AllAttribs ) // also div and frameset init self.eattr.id = id self.eattr.CLASS = CLASS self.eattr.style = style end end class IFrameAttribs from FrameAttribs /*# (top|middle|bottom|left|right)" -- center? */ align = nil end class IFrame from \ BaseDom( "iframe", ContentModel.flow, IFrameAttribs ) // also div and frameset end class ObjectAttribs from AllAttribs /*# declare but don't instantiate flag. Set to "declare" to enable */ declare = nil /*# identifies an implementation */ classid = nil /*# base URI for classid, data, archive */ codebase = nil /*# reference to object's data */ data = nil /*# content type for data */ type = nil /*# content type for code */ codetype = nil /*# space-separated list of URIs */ archive = nil /*# message to show while loading */ standby = nil /*# override height */ height = nil /*# override width */ width = nil /*# use client-side image map */ usemap = nil /*# submit as part of form */ name = nil /*# position in tabbing order */ tabindex = nil end /*# HTML embedding object class. */ class Object from \ BaseDom( "object", ContentModel.flow_object, ObjectAttribs ) end class ParamAttribs from AttribsBase /*# document-wide unique id */ id = nil /*# property name */ name = nil /*# property value */ value = nil /*# How to interpret value (can be "data", "ref" or "object")*/ valuetype = nil /*# content type for value */ type = nil end /*# Class representing a single parameter to be sent to an object. @param name The name of the parameter. @param value The value of the parameter. @optparam vtype Valye type (data, ref or object). @optparam ctype Content-type of the parameter value. @optparam id The unique entity ID in the document. */ class Param( name, value, vtype, ctype, id ) from \ BaseDom( "param", nil, ParamAttribs ) init self.eattr.name = name self.eattr.value = value self.eattr.valuetype = vtype self.eattr.type = ctype self.eattr.id = id end end class ImgAttribs from AllAttribs /*# URI of image to embed */ src = nil /*# short description */ alt = nil /*# link to long description */ longdesc = nil /*# override height */ height = nil /*# override width */ width = nil /*# use client-side image map */ usemap = nil /*# use server-side image map */ ismap = nil end /*# Hypertext embedded image. */ class Img( src, width, height, alt, usemap, ismap ) from \ BaseDom( "img", nil, ImgAttribs ) init self.eattr.src = src ? src : "" // mandatory self.eattr.width = width self.eattr.height = height self.eattr.alt = alt ? alt : "" // mandatory self.eattr.usemap = usemap self.eattr.ismap = ismap end end class MapAttribs from AllAttribs /*# for reference by usemap */ name = nil end /*# Image active link map. */ class Map(name) from \ BaseDom( "map", ContentModel.block + .['area'], MapAttribs ) init self.eattr.name = name end end class AreaAttribs from AllAttribs /*# controls interpretation of coords */ shape = nil /*# comma-separated list of lengths */ coords = nil /*# URI for linked resource */ href = nil /*# this region has no action */ nohref = nil /*# short description */ alt = nil /*# position in tabbing order */ tabindex = nil /*# accessibility key character */ accesskey = nil /*# the element got the focus */ onfocus = nil /*# the element lost the focus */ onblur = nil end class Area( href, shape, coords,nohref, alt ) from \ BaseDom( "area", nil, AreaAttribs ) init self.eattr.href = href self.eattr.shape = shape self.eattr.coords = coords self.eattr.nohref = nohref self.eattr.alt = alt end end /****************************************************************** * Stylesheets and scripts ******************************************************************/ class EscapingData( name, attr ) from \ BaseDom( name, nil, attr ) function render( level, stream ) sep = self.st_makesep( level ) stream.writeText( sep + "<" + self.hname ) sattr = self.eattr.toString() if sattr: stream.writeText( sattr ) if not self.text // empty element -- closing mark is required by many browsers stream.writeText( ">\n" ) return end item = self while item and item provides parent if item.parent.derivedFrom( XHTMLFrame ) use_cdata = true break end item = item.parent end // we have a content // CDATA markers must be at line begin. if( use_cdata ) stream.writeText( ">\n\n" + sep + "\n") else stream.writeText( ">\n" + self.text + sep + "\n") end end end class StyleAttribs from I18nAttribs /*# content type of style language */ type = nil /*# designed for use with these media. (single or comma-separated list of media descriptors) */ media = nil /*# advisory title */ title = nil end /*# Creates an embedded style sheet. The style will be rendered wrapped in an XML CDATA element. However, XHTML standard suggest not to use this tag and load the stylesheet via link to other documents, to avoid compatibility problems with not totally compliant browsers. If @b type is not given, it will be defaulted to "text/css" */ class Style( data, type ) from \ EscapingData( "style", StyleAttribs ) text = data init self.eattr.type = type? type : "text/css" end end class ScriptAttribs from AttribsBase /*# char encoding of linked resource */ charset = nil /*# content type of script language */ type = nil /*# URI for an external script */ src = nil /*# URI for an external script */ defer = nil end /*# Creates an embedded script. The script will be rendered wrapped in an XML CDATA element. The user should escape any "]]>" on its own before setting the data. However, XHTML standard suggest not to use this tag and load the stylesheet via link to other documents, to avoid compatibility problems with not totally compliant browsers. If @b type is not given, it will be defaulted to "text/javascript" */ class Script( src, type, data ) from \ EscapingData( "script", ScriptAttribs ) text = data init self.eattr.src = src self.eattr.type = type? type : "text/javascript" end end /*# Node representing the things to do if the browser doesn't support scripts. */ class NoScript from \ BaseDom( "noscript", ContentModel.block, AllAttribs ) end /****************************************************************** * Text data ******************************************************************/ /*# Class bounding the standard paragraph. If @b text is specified, an automatic single text node is added below the item. */ class P(text) from \ BaseDom( "p", ContentModel.inline, AllAttribs ) _sepAfter = "\n" text = text end /*# Class bounding pre-formatted text. If @b text is specified, an automatic single text node is added below the item. */ class Pre(text) from \ BaseDom( "pre", ContentModel.inline_pre, CoreAttribs ) _sepAfter = "\n" text = text end /*# Terminal text node. This class is meant to store a terminal text (a pure string) which is directly rendered on the output. The string is escaped so that html special characters are correctly rendered. */ class Text(text) from \ BaseDom( "text", nil, nil ) text = text function render( level, stream ) if self.text stream.writeText( _htmlEscape(self.text) ) end end end /*# Terminal unparsed node. This class is meant to store a terminal text (a pure string) which is directly rendered on the output. The parsed string is included verbatim at required position and never escaped. */ class Verbatim(text) from \ BaseDom( "verbatim", nil, nil ) text = text function render( level, stream ) if self.text: stream.writeText( self.text ) end end class BDOAttribs from CoreAttribs /*# Language code */ lang = nil /*# directionality -- can be "ltr" or "rtl" */ dir = nil end /*# Class overriding directionality in documents If @b text is specified, an automatic single text node is added below the item. */ class BDO(text) from \ BaseDom( "bdo", ContentModel.inline, BDOAttribs ) text = text end /****************************************************************** * Lists ******************************************************************/ /*# Unordered list. */ class UL from \ BaseDom( "ul", ContentModel.listitem, AllAttribs ) end /*# Ordered list. */ class OL from \ BaseDom( "ol", ContentModel.listitem, AllAttribs ) end /*# List item If @b text is specified, an automatic single text node is added below the item. */ class LI(text) from \ BaseDom( "li", ContentModel.flow, AllAttribs ) _sepAfter = "\n" text = text end /*# Definition list. */ class DL from \ BaseDom( "dl", ContentModel.defitems, AllAttribs ) _sepAfter = "\n" _sepDecl = "\n" end /*# Definition term. If @b text is specified, an automatic single text node is added below the item. */ class DT(text) from \ BaseDom( "dt", ContentModel.inline, AllAttribs ) _sepAfter = "\n" text = text end /*# Definition data. If @b text is specified, an automatic single text node is added below the item. */ class DD(text) from \ BaseDom( "dd", ContentModel.flow, AllAttribs ) _sepAfter = "\n" text = text end /****************************************************************** * Tables ******************************************************************/ /*# Definition data. If @b text is specified, an automatic single text node is added below the item. */ class TableAttribs from AllAttribs /*# purpose/structure for speech output */ summary = nil /*# table width */ width = nil /*# controls frame width around table */ border = nil /*# which parts of frame to render */ frame = nil /*# rulings between rows and cols */ rules = nil /*# spacing between cells */ cellspacing = nil /*# spacing within cells */ cellpadding = nil end /*# Hyper text Table class. (As opposed to the Falcon language Table class). */ class TABLE from \ BaseDom( "table", ContentModel.tableitems, TableAttribs ) _sepAfter = "\n" _sepDecl = "\n" end /*# Caption for the table. If @b text is specified, an automatic single text node is added below the item. */ class Caption(text) from \ BaseDom( "caption", ContentModel.inline, AllAttribs ) text = text end class TElemAttribs from AllAttribs /*# Cell horizontal aligment (left|center|right|justify|char) */ align = nil /*# alignment char, e.g. char=':' */ char = nil /*# offset for alignment char */ charoff = nil /*# Vertical cell alignment (top|middle|bottom|baseline) */ valign = nil end /*# Table heading. */ class THead from \ BaseDom( "thead", ContentModel.tablerow, TElemAttribs ) _sepAfter = "\n" end /*# Table footer. */ class TFoot from \ BaseDom( "tfoot", ContentModel.tablerow, TElemAttribs ) _sepAfter = "\n" end /*# Table body. */ class TBody from \ BaseDom( "tbody", ContentModel.tablerow, TElemAttribs ) _sepAfter = "\n" end class ColAttribs from TElemAttribs /*# default number of columns in group or size of a column */ span = nil /*# default width for enclosed COLs or size of the column */ width = nil end /*# Group of columns. Can contain only a list of Col items. */ class ColGroup from \ BaseDom( "colgroup", ContentModel.tablecol, ColAttribs ) end /*# Defines attributes for a single column Can contain only a list of Col items. */ class Col from \ BaseDom( "col", nil, ColAttribs ) end /*# Group of columns. Can contain only a list of Col items. */ class TR from \ BaseDom( "tr", ContentModel.tablecells, TElemAttribs ) _sepAfter = "\n" end class TCellAttribs from TElemAttribs /*# abbreviation for header cell */ abbr = nil /*# comma-separated list of related headers */ axis = nil /*# list of id's for header cells */ headers = nil /*# scope covered by header cells */ scope = nil /*# number of rows spanned by cell */ rowspan = nil /*# number of cols spanned by cell */ colspan = nil end /*# Table cell at header position. If @b text is specified, an automatic single text node is added below the item. */ class TH( text ) from \ BaseDom( "th", ContentModel.flow, TCellAttribs ) text = text end /*# Normal table cell. If @b text is specified, an automatic single text node is added below the item. */ class TD( text ) from \ BaseDom( "td", ContentModel.flow, TCellAttribs ) text = text end /****************************************************************** * Anchor ******************************************************************/ class CommonLinkAttribs from AllAttribs /*# char encoding of linked resource */ charset = nil /*# advisory content type */ type = nil /*# named link end */ name = nil /*# URI for linked resource */ href = nil /*# language code */ hreflang = nil /*# forward link types */ rel = nil /*# reverse link types */ rev = nil end class AnchorAttribs from CommonLinkAttribs /*# accessibility key character */ accesskey = nil /*# for use with client-side image maps */ shape = nil /*# for use with client-side image maps */ coords = nil /*# position in tabbing order */ tabindex = nil /*# the element got the focus */ onfocus = nil /*# the element lost the focus */ onblur = nil end class LinkAttribs from CommonLinkAttribs /*# for rendering on these media */ media = nil end /*# Anchor. If @b text is specified, an automatic single text node is added below the item. */ class A( href, text ) from \ BaseDom( "a", ContentModel.inline_a, AnchorAttribs ) text = text init self.eattr.href = href end end /*# Document relationship (link). */ class Link( href, rel, type ) from \ BaseDom( "link", nil, LinkAttribs ) init self.eattr.href = href self.eattr.rel = rel self.eattr.type = type end end /****************************************************************** * Font/prhase control ******************************************************************/ /*# Base class for all the font and phrase control tags. @param type The type of node (a valid HTML font/text control inline tag). @optparam text An optional text to add immediately to this node. */ class FontControl( type, text ) from \ BaseDom( type, ContentModel.inline, AllAttribs ) text = text end /*# Class generating Typewriter/Tele Type text. @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class TT(text) from \ FontControl( "tt", text ) end /*# Class generating italic text. @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class I(text) from \ FontControl( "i", text ) end /*# Class generating bold text. @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class B(text) from \ FontControl( "b", text ) end /*# Class generating big (uppercase/caps) text. @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class Big(text) from \ FontControl( "big", text ) end /*# Class generating small text. @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class Small(text) from \ FontControl( "small", text ) end /*# Class generating emphathised phrases. @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class Em(text) from \ FontControl( "em", text ) end /*# Class generating stronger phrases. @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class Strong(text) from FontControl( "strong", text ) end /*# Class generating definition phrases. @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class Dfn(text) from \ FontControl( "dfn", text ) end /*# Class generating a 'code' section. @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class Code(text) from \ FontControl( "code", text ) end /*# Class generating a 'sample' section. @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class Samp(text) from \ FontControl( "samp", text ) end /*# Class generating a 'text digited on keyboard' section. @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class Kbd(text) from \ FontControl( "kbd", text ) end /*# Class generating a 'variable' phrase. @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class Var(text) from \ FontControl( "var", text ) end /*# Class generating a citation phrase. @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class Cite(text) from \ FontControl( "cite", text ) end /*# Class generating an abbreviation phrase. @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class Abbr(text) from \ FontControl( "abbr", text ) end /*# Class indicating an acronym phrase. @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class Acronym(text) from \ FontControl( "acronym", text ) end /*# Subscript phrase generator. @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class Sub(text) from \ FontControl( "sub", text ) end /*# Superscript phrase generator @optparam text An optional text to add immediately to this node. If @b text is specified, an automatic single text node is added below the item. */ class Sup(text) from \ FontControl( "sup", text ) end /*# Span text. @optparam text An optional text to add immediately to this node. Special flexible flex marker. If @b text is specified, an automatic single text node is added below the item. */ class Span(text) from \ FontControl( "span", text ) end /****************************************************************** * Quotations ******************************************************************/ class QuotAttribs from AllAttribs /*# URI for source document or msg */ cite = nil end /*# Class used to specify a block quotation. */ class Blockquote from \ BaseDom( "blockquote", ContentModel.block + .['script'], QuotAttribs ) end /*# Class used to specify a quotation. If @b text is specified, an automatic single text node is added below the item. */ class Q(text) from \ BaseDom( "q", ContentModel.inline, QuotAttribs ) _sepAfter = "\n" text = text end class InsDelAttribs from AllAttribs /*# info on reason for change */ cite = nil /*# date and time of change */ datetime = nil end /*# Node for text inserted in a document. If @b text is specified, an automatic single text node is added below the item. */ class Ins(text) from \ BaseDom( "ins", ContentModel.inline, InsDelAttribs ) text = text end /*# Node for text removed from a document. If @b text is specified, an automatic single text node is added below the item. */ class Del(text) from \ BaseDom( "del", ContentModel.inline, InsDelAttribs ) text = text end /****************************************************************** * Headings ******************************************************************/ class Heading( type, text ) from \ BaseDom( type, ContentModel.inline, AllAttribs ) text = text end /*# Heading level 1 node. If @b text is specified, an automatic single text node is added below the item. */ class H1(text) from \ Heading( "h1", text ) end /*# Heading level 2 node. If @b text is specified, an automatic single text node is added below the item. */ class H2(text) from \ Heading( "h2", text ) end /*# Heading level 3 node. If @b text is specified, an automatic single text node is added below the item. */ class H3(text) from \ Heading( "h3", text ) end /*# Heading level 4 node. If @b text is specified, an automatic single text node is added below the item. */ class H4(text) from \ Heading( "h4", text ) end /*# Heading level 5 node. If @b text is specified, an automatic single text node is added below the item. */ class H5(text) from \ Heading( "h5", text ) end /*# Heading level 6 node. If @b text is specified, an automatic single text node is added below the item. */ class H6(text) from \ Heading( "h6", text ) end /****************************************************************** * Forms ******************************************************************/ class FormAttribs from AllAttribs /*# server-side form handler */ action = nil /*# HTTP method used to submit the form ("get" or "post") */ method = nil /*# Content type of the document */ enctype = nil /*# list of MIME types for file upload */ accept = nil /*# name of form for scripting -- deprecated in xhtml */ name = nil /*# the form was submitted */ onsubmit = nil /*# the form was reset */ onreset = nil /*# list of supported charsets */ accept_charset = nil end /*# Creates a form node for data submission and file uploads. @param action the action where to send the form. @param method How to send the form ("get" or "post"). */ class Form( action, method ) from \ BaseDom( "form", ContentModel.block_form, FormAttribs ) method_get = "get" method_post = "post" init self.eattr.action = action self.eattr.method = method end /*# Helper method configuring this object for upload. @param href The URI that has to be set in the action This method sets the following attributes of the form tag so that the form can send files to Falcon remote server-side scripts receiving the data. - method = "post" - enctype = "multipart/form-data" - accept-charset="utf-8" This settings are sensible also for generic file uploads. */ function forUpload( href ) self.eattr.action = href self.eattr.method = "post" self.eattr.enctype = "multipart/form-data" self.eattr.accept_charset="utf-8" return self end end class InputAttribs from AllAttribs /*# what kind of widget is needed. Can be one of - text - password - checkbox - radio - submit - reset - file - hidden - image - button types are declared as properties in the InputType enumeration */ type = nil /*# submit as part of form */ name = nil /*# Specify for radio buttons and checkboxes */ value = nil /*# for radio buttons and check boxes (boolean)*/ checked = false /*# unavailable in this context (boolean) */ disabled = false /*# for text and passwd (boolean) */ readonly = false /*# specific to each type of field */ size = nil /*# max chars for text fields */ maxlength = nil /*# for fields with images */ src = nil /*# short description */ alt = nil /*# use client-side image map */ usemap = nil /*# use server-side image map */ ismap = nil /*# position in tabbing order */ tabindex = nil /*# accessibility key character */ accesskey = nil /*# the element got the focus */ onfocus = nil /*# the element lost the focus */ onblur = nil /*# some text was selected */ onselect = nil /*# the element value was changed */ onchange = nil /*# list of MIME types for file upload */ accept = nil end enum InputType text = "text" password = "password" checkbox = "checkbox" radio = "radio" submit = "submit" reset = "reset" file = "file" hidden = "hidden" image = "image" button = "button" end /*# Base class for Input items with some common helpers. */ class BaseInput( type, at ) from \ BaseDom( type, nil, at ) /*# Helper method to disable this input. Return the self object so it's possible to do @code s = Form("manage.ftd").add( Input( "text", "the_field" ).disabled() ) @endcode */ function disabled() self.eattr.disabled = true return self end /*# Helper method to set this field read-only. Return the self object so it's possible to do @code s = Form("manage.ftd").add( Input( "text", "the_field" ).readonly() ) @endcode */ function readonly() self.eattr.readonly = true return self end end /*# Creates an input, with an optional type. @param type The type of the input. @param name The name of the input (mandatory). @param value The value of the item. Input @b type should be in the @a InputType enumeration. If not specified, the type will be set to 'hidden'. */ class Input( type, name, value ) from \ BaseInput( "input", InputAttribs ) init if not type: type = InputType.hidden self.eattr.name = name self.eattr.type = type self.eattr.value = value end end /*# Creates a text input. @param name The name that will be sent back in the form post. @param value Initial value for the item. @param size Width in characters on the screen. @param maxlength Maximum allowed caracters that can be posted in this field. */ class TextInput( name, value, size, maxlength ) from Input( InputType.text, name, value ) init // force to be a boolean self.eattr.size = size self.eattr.maxlength = maxlength end end /*# Creates a masked text input. @param name The name that will be sent back in the form post. @param value Initial value for the item. @param size Width in characters on the screen. @param maxlength Maximum allowed caracters that can be posted in this field. */ class PasswordInput( name, value, size, maxlength ) from Input( InputType.password, name, value ) init // force to be a boolean self.eattr.size = size self.eattr.maxsize = maxlength end end /*# Creates a checkbox input. @param name The name that will be sent back in the form post. @param value Initial value for the item. @param checked True to make this checkbox checked. */ class CheckboxInput( name, value, checked ) from Input( InputType.checkbox, name, value ) init // force to be a boolean self.eattr.checked = checked ? true : false end end /*# Creates a radio input. @param name The name that will be sent back in the form post. @param value Initial value for the item. @param checked True to make this radio button selected. All the radio buttons having the same name will be selected mutually exclusively. */ class RadioInput( name, value, checked ) from Input( InputType.radio, name, value ) init // force to be a boolean self.eattr.checked = checked ? true : false end end /*# Creates a submit button. @param name The name that will be sent back in the form post. @param value Initial value for the item. @optparam onclick The action to perform on click. */ class SubmitInput( name, value, onclick ) from Input( InputType.submit, name, value ) init self.eattr.onclick = onclick end end /*# Creates a reset button. @param name The name that will be sent back in the form post. @param value Initial value for the item. @optparam onclick The action to perform on click. */ class ResetInput( name, value, onclick ) from Input( InputType.reset, name, value ) init self.eattr.onclick = onclick end end /*# Creates a file input for file upload. @param name The name that will be sent back in the form post. @param value Initial value for the item. */ class FileInput( name, value ) from Input( InputType.file, name, value ) end /*# Creates a graphic button. @param name The name that will be sent back in the form post. @param value Initial value for the item. @optparam onclick The action to perform on click. */ class ImageInput( name, value, onclick ) from Input( InputType.image, name, value ) init self.eattr.onclick = onclick end end /*# Creates an hidden input field (for parameter propagation in forms). @param name The name that will be sent back in the form post. @param value Initial value for the item. */ class HiddenInput( name, value ) from Input( InputType.hidden, name, value ) end /*# Creates a generic button. @param name The name that will be sent back in the form post. @param value Initial value for the item. @optparam onclick The action to perform on click. */ class ButtonInput( name, value, onclick ) from Input( InputType.button, name, value ) init self.eattr.onclick = onclick end end class SelectAttribs from AllAttribs /*# Field name */ name = nil /*# Rows visible */ size = nil /*# Default is single selection */ multiple = false /*# Unavailable in this context */ disabled = false /*# Position in tabbing order */ tabindex = nil /*# The element got the focus */ onfocus = nil /*# The element lost the focus */ onblur = nil /*# The element value was changed */ onchange = nil end class Select( name, size, multiple ) from \ BaseDom( "select", ContentModel.options, SelectAttribs ) init self.eattr.name = name self.eattr.size = size self.eattr.multiple = multiple ? true : false end end class OptGroupAttribs from AllAttribs /*# Unavailable in this context. */ disabled = false /*# For use in hierarchical menus. */ label = nil end /*# Group of otpions represented separated in the @a Select menu. @param label The text to be displayed at the beginning of the group. @optparam disabled Set to true to shade the group. */ class OptGroup( label, disabled ) from \ BaseDom( "optgroup", ContentModel.options, OptGroupAttribs ) init self.eattr.label = label self.eattr.disabled = disabled ? true : false end end class OptionAttribs from AllAttribs /*# Set to true to pre-select this option */ selected = false /*# Unavailable in this context */ disabled = false /*# For use in hierarchical menus */ label = nil /*# Value that will be returned in the form if selected. If nil, it defaults to element content. */ value = nil end /*# Single choice of a @a Select menu. @param text Text of the component. @optparam value The value to be sent in the form if selected -- defaults to @b text @optparam label The text to be displayed in the menu -- defaults to @b text @optparam selected true to mark this option as selected. @optparam disabled Set to true to shade the option. Even if both @b value and @b label are defined, the text element can still be used, for example to display hints by browsers. */ class Option( text, value, label, selected, disabled ) from \ BaseDom( "option", nil, OptionAttribs ) text = text _sepAfter = "\n" init self.eattr.label = label self.eattr.value = value if selected: self.eattr.selected = true if disabled: self.eattr.disabled = true end /*# Helper method to set this option on. Return the self object so it's possible to do @code s = Select("the select").add( Option( "the option" ).selected() ) @endcode */ function selected() self.eattr.selected = true return self end /*# Helper method to disable this option. Return the self object so it's possible to do @code s = Select("the select").add( Option( "the option" ).disabled() ) @endcode */ function disabled() self.eattr.disabled = true return self end end class TextAreaAttribs from AllAttribs /*# Name of the field in the form. */ name = nil /*# Displayed text rows */ rows = nil /*# Displayed column numbers */ cols = nil /*# Not available in this context */ disabled = false /*# Set true to prevent changes */ readonly = false /*# Position in tabbing order */ tabindex = nil /*# Accessibility key character */ accesskey = nil /*# The element got the focus */ onfocus = nil /*# The element lost the focus */ onblur = nil /*# Some text was selected */ onselect = nil /*# The element value was changed */ onchange = nil end /*# Class representing a Text area in a form. @param name The name of the field to be sent in the form. @optparam text The text to be displayed in the area. @optparam rows The number of rows for the visualization. @optparam cols The number of columns for the visualization. @optparam readonly set to true to make this text area read-only. @optparam disabled set to true to make this text area grayed out. */ class TextArea( name, text, rows, cols, readonly, disabled ) from \ BaseInput( "textarea", TextAreaAttribs ) text = text ? text : "" init self.eattr.name = name self.eattr.rows = rows self.eattr.cols = cols if readonly: self.eattr.readonly = true if disabled: self.eattr.disabled = true end end class LabelAttribs from AllAttribs /*# matches field ID value (not NAME. It matches ID) */ FOR = nil /*# the element lost the focus */ accesskey = nil /*# the element lost the focus */ onfocus = nil /*# the element lost the focus */ onblur = nil end /*# Class representing a Text label in input forms. @optparam Text some text to be used as the label. @optparam forid The ID of the target input element. If @b forid is not given, the label should contain the target input element. The Label item can contain any other text */ class Label( text, forid ) from \ BaseDom( "label", ContentModel.inline_label, LabelAttribs ) _sepAfter = "\n" text = text init self.eattr.FOR = forid end end /*# Class used to group fields in forms */ class FieldSet from \ BaseDom( "fieldset", ContentModel.flow_fieldset, AllAttribs ) end class LegendAttribs from AllAttribs /*# accessibility key character */ accesskey = nil end /*# Used to specify a legend for a set of fields. */ class Legend(text) from \ BaseDom( "legend", ContentModel.inline, LegendAttribs ) text = text end class ButtonAttribs from AllAttribs /*# Field name for submission. */ name = nil /*# Sent to server when submitted */ value = nil /*# For use as form button (button|submit|reset) */ type = nil /*# Unavailable in this context */ disabled = nil /*# Position in tabbing order */ tabindex = nil /*# Accessibility key character */ accesskey = nil /*# The element got the focus */ onfocus = nil /*# The element lost the focus */ onblur = nil end /*# Used to create a button type container in a form. */ class Button( name, value, onclick, type, disabled ) from \ BaseDom( "button", ContentModel.flow_button, ButtonAttribs ) _sepAfter = "\n" type_button = "button" type_submit = "submit" type_reset = "reset" init self.eattr.name = name self.eattr.value = value self.eattr.type = type ? type : self.type_button self.eattr.onclick = onclick if disabled: self.eattr.disabled = true end end /****************************************************************** * Breaks ******************************************************************/ /*# Line Break */ class BR from \ BaseDom( "br", nil, CoreAttribs ) _sepAfter = "\n" end /*# Horizontal ruler */ class HR from \ BaseDom( "hr", nil, AllAttribs ) _sepAfter = "\n" end /****************************************************************** * Special elements ******************************************************************/ /*# Element rendering as the "Valid XHTML 1.0" official mark. */ class ValidXhtmlMark from BaseDom( "p" ) // fake to be a p function render(level, stream) stream.writeText( '

    Valid XHTML 1.0 Strict

    ' ) end end /*# Element rendering as the "Valid HTML 4.01" official mark. */ class ValidHtmlMark from BaseDom( "p" ) // fake to be a p function render(level, stream) stream.writeText('

    Valid HTML 4.01 Transitional

    ' ) end end /*# Creates a Falcon script node. This can be used to create XHTML compliant FTD documents that need a second-pass parsing server-side. The data is wrapped in a '' set of tags. */ class Falcon( data ) from BaseDom( "" ) // fake to be a i text = data function render( level, stream ) stream.write( "" ) end end /*# Creates a Falcon "print variable" node. This can be used to create XHTML compliant FTD documents that need a second-pass parsing server-side. The data is wrapped in a '>.... ?>' set of tags. */ class FalconVar( variable ) from BaseDom( "" ) // fake to be a i text = variable function render( level, stream ) stream.writeText( "> (" + self.text + ") ?>" ) end end /*# Includes a document verbatim. @param fname an URI that can be read by InputStream. @optparam encoding The text-encoding for the included file. Defaults to C (none -- read/write verbatim). */ class Include( fname, encoding ) from BaseDom( "" ) init file = InputStream( fname ) if encoding: file.setEncoding( encoding ) data = "" self.text = "" while not file.eof() file.readText( data, 4096 ) self.text += data end file.close() end function render(level, stream) stream.writeText( self.text ) end end /*# Includes a falcon document. @param fname an URI that can be passed in Falcon. @optparam encoding The text-encoding for the included file. Defaults to C (none -- read/write verbatim). */ class IncludeFalcon( fname, encoding ) from BaseDom( "" ) init ss = StringStream() out = stdOut( ss ) try include( fname, encoding ) self.text = ss.closeToString() catch in error end stdOut(out) if error: raise error end function render(level, stream) stream.writeText( self.text ) end end /* end of htmaker.fal */ modules/falcon/web/htmaker/000077500000000000000000000000001176363201700162065ustar00rootroot00000000000000modules/falcon/web/htmaker/htmerror.fal000066400000000000000000000030141176363201700205320ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. Hyper Text Maker - XHTML generator FILE: htmerror.fal Error class with specific descriptions ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 11 Jun 2010 21:44:23 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /*# Class raised in case of logic errors in composing XHTML documents. */ class HTMError(code, extra) from Error(code, "HTML generic error", extra) parentship = 10001 nochildren = 10002 noattrib = 10003 headdef = 10004 bodydef = 10005 nottoplevel = 10006 noid = 10007 noclass = 10008 nostyle = 10009 addnodom = 10010 _codes = [ 10001 => i"Parentship restraint wasn't respected", 10002 => i"Adding an item to a flat item", 10003 => i"Invalid attribute name", 10004 => i"Head part already defined", 10005 => i"Body already defined", 10006 => i"Addign a non-toplevel item to ", 10007 => i"The XHTML element doesn't expose an 'id' property", 10008 => i"The XHTML element doesn't expose a 'class' property", 10009 => i"The XHTML element doesn't expose a 'style' property", 10010 => i"Adding a non-DOM element item." ] init if code in self._codes self.description = self._codes[code] end end end /* end of htmerror.fal */ modules/falcon/web/mime.fal000066400000000000000000000567621176363201700162060ustar00rootroot00000000000000/* FALCON - MIME Parser FILE: mime.fal Multipurpose Internet Mail Extensions parser. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 21 Nov 2010 15:22:29 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ const _bstring = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" const _default_boundary_length = 32 const _header_line_size = 4096 const _block_buffer = 16384 /*# Type of multipart entity */ enum Multipart //# Mixed multipart mixed = "mixed" //# Alternative multipart alternative = "alternative" //# Digest multipart digest = "digest" //# Parallel multipart parallel = "parallel" end /*# Creates a MIME data part. @param data A string or memory buffer with the data to be attached. @optparam type Content/type of the data (defaults to "application/octet-stream") @optparam fname The file name of the part element. @optparam partName Logical name of the part in the Mime document, @return The new part created by this method. This method creates a part that is immediately ready to be attached as a data part in a multipart mime. The part created by this method can be configured afterwards. By default, it creates the following default headers: @code Content-Disposition: attachment Content-Transfer-Encoding: base64 @endcode */ function makeDataPart( data, type, fname, partName ) if not type: type = "application/octet-stream" if fname if not partName: partName = fname part = Part( [ "Content-Type" => [ "" => type, "name" => partName ], "Content-Disposition" => ["" => "attachment", "filename" => fname], "Content-Transfer-Encoding" => "base64"] ) else part = Part( [ "Content-Type" => type, "Content-Disposition" => "attachment", "Content-Transfer-Encoding" => "base64"] ) end // encoding the content if data.typeId() == MemBufType data = Base64.encode( data ) else data = Base64.encode( strToMemBuf(data) ) end // Breaking the part into lines length = data.len() dbuffer = StringStream( length + (length/76)*2+2) for i in [76:length:76] dbuffer.writeText( data, i-76, i ) dbuffer.write("\r\n") end if length % 76 dbuffer.writeText( data, int(length/76)*76 ) dbuffer.write( "\r\n" ) end part.body = dbuffer.closeToString() return part end /*# Part of a MIME document. @optparam headers The headers of this part. @optparam body The body of this part. About the formats of the @b headers and @b body parameters, see respectively the @a Part.headers and @a Part.body properties. @prop parts The sequence of sub-parts stored in this part. It's a read-only field, to manipulate it you must use the proper methods. */ class Part( headers, body ) /*# Dictionary of headers. This dictionary holds headers in the form - "header" => "value"; or - "header" => [ "parameter" => "value", ...] In the second form, the main parameter is always indicated by the "" (empty string) key, while the sub-parameters are expressed as list of strings. The parameters will be escaped during output. */ headers = headers ? headers : [=>] /*# Body of the part. The body represents the part payoff. The body should be simply the untranslated content you want to send to the remote part; the carrier will eventually fiddle with the encoding to make it compatible with the final stream. */ body = body _isMulti = false _boundary = nil _parts = nil function __get_boundary(): return self._boundary function __get_parts(): return self._parts /*# Declares that this will be a part holding more parts. @optparam partMode Mode of this multipart (defaults to "mixed") @optparam boundary If there is an already defined boundary for this part. Setting a MIME element as multi-part has important semantic consequences. A mime-part with sub-parts will receive an automatic content-type and boundary, The body, if present, becomes a "preamble", and is to be generally ignored. The content type becomes "multipart", with a subtype that declares the semantic mode. The @b partMode parameter is used to declare the subtype, and may be one of the @a Multipart enumeration, or a string (in which case it's used directly). */ function setMultipart( partMode, boundary ) if self._isMulti raise CodeError( 10001, "Already declared as multipart or message content-type" ) end self._isMulti = true if not boundary: boundary = self.makeBoundary() self._boundary = boundary self.headers["Content-Type"] = [""=> @"multipart/$partMode", "boundary" => boundary] return self end /*# Declare this part as a full MIME 1.0 message. This method sets up this part to be a full top-level message in compliancy with RFC2045 (MIME). Mandatory top-level headers are set. After this call, this part should not be added as subpart to another parent; however, no check is currently performed. */ function setTop() self.headers["MIME-Version"] = "1.0" end /*# Sets this part to be a Message part @optparam id If given, this is a message/partial part, where the whole message has the given ID. @optparam partNo If the message is "partial", then this indicates the number of the part. @optparam partCount If the message is "partial", then this indicates the count of all the parts. @return self If @b id is not given, the part will be considered a "message/RFC822" multipart message. When @b id is given, @b partNo is mandatory, while @b partCount is optional (even if it's strongly suggested to add it if possible). */ function setMessage( id, partNo, partCount ) if self._isMulti raise CodeError( 10001, "Already declared as multipart or message content-type" ) end self._isMulti = true if id header = [""=> "message/partial", "id" => id, "number"=> partNo ] if partCount: header["total"] = partCount self.headers["Content-Type"] = header else self.headers["Content-Type"] = [""=> "message/RFC822"] end return self end /*# Adds a part to this MIME part. @param part the Part to be added. @return self The part must have been previously set as multipart via @a Part.setMessage or @a Part.setMultipart. */ function addPart( part ) if not part.derivedFrom( Part ) raise ParamError( 501, nil, "'part' is not a mime.Part" ) end if not self._isMulti raise CodeError( 10002, "Must first declare this part as multipart via setMultipart" ) end if self._parts self._parts += part else self._parts = [part] end return self end /*# Removes a previously added part @param part The part to be removed. @return true If the part was found (and removed), false otherwise */ function removePart( part ) n = 0 parts = self._parts length = parts.len() while n < length if parts[n] == part arrayRemove( parts, n ) return true end ++n end return false end /*# Attach a data content to the mail part. @param data A string or memory buffer with the data to be attached. @param type Content/type of the data (defaults to "application/octet-stream") @param fname The file name of the part element. @optparam partName Logical name of the part in the Mime document, @return The new part created by this method. This method creates an attachment for this MIME Part suitable to hold binary data. If this part had already a content but wasn't a multi-part element, then the content is moved in a new multipart element that is prepended to the part to be added. The part created by this method can be configured afterwards. @see makeDataPart */ function attachData( data, type, fname, partName ) part = makeDataPart( data, type, fname, partName ) self.attachPart( part ) return part end /*# Attach a given file. @param fullPath the path to the file. @param type the MIME type for the attached file. @optparam partName Logical name of the part in the final document. */ function attachFile( fullPath, type, partName ) whole = "" try stream = InputStream( fullPath ) data = strBuffer(4096) while stream.read( data, 4096 ) whole += data end stream.close() catch in e if stream: stream.close() raise e end // TODO if type is empty, determine the mime type from the extension uri = URI( fullPath ) self.attachData( whole, type, Path(uri.path).filename, partName ) end /*# Attach a ready-made part content to the mail part. @param part The part to be added. @return self This is similar to AddPart, but in case this parent part is not a multipart element, it is turned into a "multipart/mixed" element and its former contents are moved in a separate part, which is prepended to the parameter. */ function attachPart( part ) if not self._isMulti // move away the body if self.body firstPart = Part() if "Content-Type" in self.headers firstPart.headers["Content-Type"] = self.headers["Content-Type"] end if "Content-Transfer-Encoding" in self.headers firstPart.headers["Content-Transfer-Encoding"] = self.headers["Content-Transfer-Encoding"] self.headers -= "Content-Transfer-Encoding" end firstPart.body = self.body self._parts = [firstPart] end self.setMultipart( Multipart.mixed ) self.body = "This is a MIME Multipart document." end if self._parts self._parts += part else self._parts = [part] end return self end //# @ignore function makeBoundary( count ) static _bound_array = strSplit( _bstring ) end if not count: count = _default_boundary_length str = "=_" + (" " * count) for n in [2:count+2] str[n] = randomPick( _bound_array ) end return str end /*# Transforms this MIME in a string. @return This MIME element rendered as a String. */ function toString() s = StringStream() self.write(s) return s.closeToString() end /*# Writes this mime-part on the given stream. @param stream The stream where to write the part. */ function write( stream ) self._writeHeaders( stream ) stream.write( "\r\n" ) if self.body stream.write( self.body ) end if self._isMulti and self._boundary for part in self._parts forfirst stream.write( "\r\n--" ) stream.write( self._boundary ) stream.write( "\r\n") end part.write( stream ) formiddle stream.write( "\r\n--" ) stream.write( self._boundary ) stream.write( "\r\n") end forlast stream.write( "\r\n--" ) stream.write( self._boundary ) stream.write( "--\r\n" ) end end // a message/xxx mesasge has just 1 part that is stored in the body. end end function _writeHeaders( stream ) for header, value in self.headers stream.write( header + ": " ) if typeOf( value ) == StringType stream.write( value ) else // as the main header is "", we can always be sure that it will be the first for key, val in value if key: stream.write( key + "=" ) if '"' in val or ';' in val or " " in val val = '"' + val.replace( '"', '\"' ) + '"' end stream.write( val ) formiddle: stream.write("; ") end end stream.write( "\r\n" ) end end /*# Sets the text for this MIME part. @param text The text to be encoded. @optparam ctype Content type of the part; if not given, defaults to "text/plain" @optparam cset Type of encoding of the rendered text. If not given, defaults to "utf-8" Converts the text string into an string of the given encoding, and prepares the headers of the part so that the transport is notified about the fact that this part is meant to be an encoded text. The @b cset parameter, if specificed, must be a character encoding know by Falcon. */ function setText( text, ctype, cset ) if not ctype: ctype = "text/plain" if not cset: cset = "utf-8" ttext = transcodeTo( text, cset ) self.body = quote( ttext ) self.headers[ "Content-Type" ] = [""=>ctype, "charset" => cset] self.headers[ "Content-Transfer-Encoding" ] = "quoted-printable" end function setText8Bit( text, ctype, cset ) if not ctype: ctype = "text/plain" if not cset: cset = "utf-8" self.body = transcodeTo( text, cset ) self.headers[ "Content-Type" ] = [""=>ctype, "charset" => cset] self.headers[ "Content-Transfer-Encoding" ] = "8-bit" end end /*# Parses a stream containing a MIME message. @param stream The stream holding the incoming data. @return a new @a Part generated reading the stream. @raise ParseError if the stream is not in MIME format. @note To parse a string, use @a parseString. */ function parse( stream ) part = Part() _parseHeaders( stream, part ) _parseBody( stream, part ) return part end /*# Parses a string containing a MIME message. @param string A string containing a whole MIME message. @return a new @a Part generated by parsing the string. @raise ParseError if the stream is not in MIME format. */ function parseString( string ) ss = StringStream( string ) part = Part() _parseHeaders( ss, part ) _parseBody( ss, part ) return part end function _parseHeaders( stream, part ) // read headers line = strBuffer( 1024 ) buf = strBuffer( 4096 ) hdr = strBuffer( 4096 ) while not stream.eof() and stream.readLine( line, _header_line_size ) and line if line.startsWith("\t") or line.startsWith(" ") // we must be in a multiple-lines header if buf == "": raise ParseError( 10003, "Malformed header", line ) buf += line line = "" continue else // new header coming if buf != "" // parse the previous header hdr = buf buf = line line = "" else buf = line line = "" continue end end bd = _parseSingleHeader( hdr, part ) if bd: bound = bd end // parse the last line if buf bd = _parseSingleHeader( buf, part ) if bd: bound = bd end return bound end function _parseSingleHeader( hdr, part ) key,value = _parseHeaderLine( hdr ) hdr = "" // should we consider some content type ? if key.lower() == "content-type" bound = _checkMultipart( value, part ) end part.headers[key] = value return bound end function _checkMultipart( value, part ) ct = value.typeId() == StringType ? value : value[""] if ct.startsWith( "multipart/", true ) if value.typeId() != DictionaryType or not "boundary" in value raise ParseError( 10002, "Invaid MIME format", "boundary not defined in multipart content-type" ) end bound = value["boundary"] subType = ct[10:] part.setMultipart( subType, bound ) return bound elif ct.startsWith( "message/", true ) subType = ct[8:] if subType == "partial" // we must have id, if value.typeId() != DictionaryType or not "id" in value or not "number" in value raise ParseError( 10002, "Invaid MIME format", "id or value not defined in message/partial content-type" ) end id = value["id"] number = value["number"] if "total" in value: total = value["total"] part.setMessage( id, number, total ) else part.setMessage() end end end function _parseHeaderLine( line ) pos = line.find( ":" ) if pos <= 0 raise ParseError( 10003, "Malformed header", line ) end key, value = line[0:pos].trim(), line[pos+1:] // parse the value state = 0 posParam = 0 xvalues = nil for n in [0:value.len()] char = value[*n] switch state case 0 // normal if char == 34 // quote state = 1 elif char == 59 // ";" // maybe found a parameter if not xvalues: xvalues = [=>] _parseHeaderParam( value[posParam:n], xvalues ) posParam = n + 1 end case 1 if char == 92 // backslash state = 2 elif char == 34 state = 0 end case 2 state = 1 // back to string end end // unclosed quote? if state != 0 raise ParseError( 10006, "Malformed header parameter -- unclosed quote", line ) end if xvalues // parse the last parameter if posParam != value.len() _parseHeaderParam( value[posParam:], xvalues ) end // if the only key is "", we return the only value if xvalues.len() == 1 and xvalues.get("") != nil return [key, xvalues[""].trim()] else return [key, xvalues] end else return [key, value.trim()] end end function _parseHeaderParam( param, xvalues ) eqpos = param.find( "=" ) if eqpos < 0 // it is acceptable not to have a parameter name if this is the first value. //if xvalues // raise ParseError( 10005, "Malformed header parameter", param ) //end key = "" value = param.trim() else key = param[0:eqpos].trim() value = param[eqpos+1:].trim() // it is not acceptable to have an empty key here if not key raise ParseError( 10005, "Malformed header parameter", param ) end end // eventually unquote the value if value and value[0] == '"' // we know value[-1] is a quote or the upstream would have raised. value = value[1:-1].replace( '\"', '"' ) end if key == "" and xvalues.get("") != nil xvalues[key] += value else xvalues[key] = value end end function _parseBody( stream, part ) bound = part.boundary // read all the stream. if not bound _parseWholeBody( stream, part ) else _parseBoundBody( stream, part, bound ) end end function _parseWholeBody( stream, part ) blocks = [] totalSize = 0 while not stream.eof() block = stream.grab( _block_buffer ) blocks += block totalSize += block.len() end part.body = totalSize != 0 ? strBuffer( totalSize ) : '' for block in blocks part.body += block end end function _parseBoundBody( stream, part, bound ) bound = "\r\n--" + bound boundLen = bound.len() // read up to the next boundary block = strBuffer( _block_buffer ) bblock = strBuffer( _block_buffer * 2 ) data = "" boundPos = -1 while not stream.eof() stream.read( block, _block_buffer ) bblock += block boundPos = bblock.find( bound ) while boundPos >= 0 // save the data data += bblock[0:boundPos] // enough data for the last block? // last block? posEnd = boundPos+boundLen+4 if posEnd > bblock.len() // just get more data and redo // Notice: a stream terminating with a non terminal boundary is malformed, // so we're authorized to only check for "--\r\n" in this constraint. bblock[0:boundPos] = "" break end if bblock[boundPos+boundLen:posEnd] == "--\r\n" // if this is the last block, we're authrized do trop the rest of the stream as insignificant. bIsLast = true else // try with a normal boundary posEnd = boundPos+boundLen+2 if bblock[ boundPos + boundLen : posEnd ] != "\r\n" // malformed entity raise ParseError( 10010, "Malformed body", "boundary constraint not respected" ) end end // the first part, before the first boundary, is the prologue if part.body == nil part.body = data else // parse the subpart as a whole mime message part.addPart( parse( StringStream( data ) ) ) end // if this is marked as last block, we have nothing else to do if bIsLast return end // empty the data bblock[0:posEnd] = "" if data: data[0:] = "" boundPos = bblock.find( bound ) end end if boundPos >= 0 raise ParseError( 10010, "Malformed body", "boundary broken at end of stream or no boundary" ) end end /** Quotes a text in MIME quoted-printable format. @param text The text to be quoted. @return The quoted text @note The input should be 8-bit. Transcoded to utf-8 if unsure. */ function quote( text ) fmt = Format("rp02X") size = text.len() ttgt = strBuffer( size*1.5 ) p = 0 line = "" line_len = 0 while p < size c = text[*p] if c >= 32 and c <= 127 and c != 61 and \ (line_len < 72 or c != 32 ) line += "\x0"/c if ++line_len == 75 ttgt += line + "=\r\n" line = "" line_len = 0 end else if line_len + 3 >= 76 ttgt += line +"=\r\n" line = "="+fmt.format(c) line_len = 0 else line += "="+fmt.format(c) line_len += 3 end end ++p end ttgt += line return ttgt end /** Unquotes a text in MIME quoted-printable format. @param text The text to be unquoted. @return The clear text */ function unquote( text ) size = text.len() ttgt = strBuffer( size ) p0 = 0 p1 = text.find( "=" ) while p0 < size and p1 != -1 try ttgt += text[p0:p1] chr0 = text[* ++p1] chr1 = text[* ++p1] catch AccessError raise ParseError( 10011, "Malformed quoted-printable text", "= at end of text" ) end p0 = p1+1 p1 = text.find( "=", p0 ) // Is this an =\r\n sequence (inserted EOL) if chr0 == 0xD and chr1 == 0xA continue end // is this an hex escape? num = 0 if chr0 >= 0x30 and chr0 <= 0x39 num = (chr0-0x30) << 4 elif chr0 >= 0x41 and chr0 <= 0x45 num = (chr0-0x41+10) << 4 else raise ParseError( 10011, "Malformed quoted-printable text", "Invaid =XX sequence" ) end if chr1 >= 0x30 and chr1 <= 0x39 num |= chr1-0x30 elif chr1 >= 0x41 and chr1 <= 0x45 num |= chr1-0x41 + 10 else raise ParseError( 10011, "Malformed quoted-printable text", "Invaid =XX sequence" ) end ttgt += "\x0"/num end return ttgt+text[p0:] end /* vi: set ai et sts=3 sw=3: */ modules/falcon/web/oauth.fal000066400000000000000000000301761176363201700163660ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. OAuth authentication scheme support - main file FILE: oauth.fal Main module file ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 21 Jun 2010 13:38:47 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from hash import from curl /*# @main Support for OAuth 1.0 protocol. This modules code to establish OAuth sessions. OAuth is a cross-site authentication exchange protocol used in Web2.0 development. @note The module depends on Feathers @a hash module and on the optional @a curl module. */ const OAUTH_REQ_PREFIX = "OAUTH-REQ-" //# Enumeration used as OAUTH connection method. enum Via //# Use Post method POST = "POST" //# Use Get method GET = "GET" end //# Enumeration used to determine if the Authorization header should be used or not. enum UseHeader //# Do not use the Authorization header in request. NONE //# Send the oauth_* parameters in the header and the rest via the standard method. ALTERN //# Send the oauth_* parameters in the header, but send them via the standard request. FULL end /*# Error raised from OAuth in case of protocol breach. @param code Code of the error @param desc Description of the error @param extra Error condition or specific description. */ class ProtoError( code, desc, extra ) from Error( code, desc, extra ) end /*# Class representing an OAuth token. @param token OAuth authorization token. @param secret Secret data used to sign the request. Oauth steps */ class Token( token, secret ) //# Token ID (empty for initial request-token queries). token = token //# Signature key associated with this token secret = secret //# Validator field returned by the remote server verifier = nil //# full data returned by the remote server. raw_data = nil //# true if the token has been authorized authorized = false //# true if this token has been exchanged. exchanged = false /*# Check if this token is a request token. @return true if this is a valid request token. */ function isRequest(): return self.token and not self.exchanged /*# Check if this token is an access token. @return true if this is a valid access token. */ function isAccess(): return self.exchanged // Gets extra data returned in the raw data. TODO function getExtraData() ret = [=>] for k,v in self.raw_data if not k.startsWith( "oauth_" ) ret[k] = v end end return ret end end /*# Interface to remote OAuth authentication process server. @param cust_id The consumer key identifying the requester on the remote OAuth server. @param cust_secret The consumer secret used to sign OAuth requests. @optparam mode One of the @a Via methods (Defaults to POST). This class acts as an authentication client connecting with a remote server. */ class Client( cust_id, cust_secret, mode ) //# Customer id associated with this client cust_id = cust_id //# Secret data used to countersign requests for this client secret = cust_secret //# connection mode (one of the @a Via methods) mode = mode ? mode : Via.POST /*# Should we use the header field? Should be one of the @a UseHeader enumeration values. If @b UseHeader.NONE, the Authorization header field is never sent. If @b UseHeader.ALTERN, the OAuth fields are MOVED in the header, and the selected mode ("GET" or "POST") is used @b only to send the non-oauth parameters. If @b UseHeader.FULL, the OAuth fields are copied in the Authorization header, but they are sent also via the POST or GET query string. */ use_header = UseHeader.NONE //# @ignore signature_method = "HMAC-SHA1" //# OAuth protocol version. Defaults to 1.0 version = "1.0" /*# Perform a token request. @param address The address of the remote token provider. @optparam callback Address to be called back by authenticator if the caller is of a web application. @optparam token An instance of the Token class to be exchanged token exchange. @return A new @a Token created through this call. @raise ProtoError if the remote side doesn't complain with the OAuth protocol. This method requests a "Request token" or an "Access token" the remote OAuth service. Initially, the caller must create a request token by calling this method; on success, a valid (but not yet authorized) request token is returned. Once this token is authorized through other means (i.e. redirecting the user to the remote service site), it can be exchanged with an access token calling this method and passing the previously returned token. The request token is discarded and the parameter becomes an access token, that can then be used to access reserved resources (via the @a callAPI method). For example, a theoretic workflow may be @code import from web.oauth in oauth client = oauth.Client( "MyClientID", "MyClientSecret" ) req_token = client.getToken( "https://TheRemoteService/get_req_token" ) //... // authorize the token //... access_token = client.getToken( "https://TheRemoteService/login", nil, req_token ) userData = client.callAPI( access_token, "https://TheRemoteService/get_user", ["user_id"=> my_user_id] ) @endcode @note This method blocks until the remote side replies. */ function getToken( address, callback, token ) params = self._makeBaseParams() if token params[ "oauth_token" ] = token.token tsecret = token.secret if token.verifier params["oauth_verifier"] = token.verifier end end if callback: params[ "oauth_callback" ] = callback cr = self.makeOAuthHandler( address, tsecret, params, nil ) cr.setOutString() cr.exec() data = cr.getData() try dt = self.parseQS( data ) catch ParamError raise ProtoError( 10001, i"Invalid answer from remote.", data ) end if not "oauth_token" in dt raise ProtoError( 10002, i"Response didn't contain an oauth_token", data ) end if not "oauth_token_secret" in dt raise ProtoError( 10003, i"Response didn't contain an oauth_token_secret", data ) end token = Token( dt["oauth_token"], dt["oauth_token_secret"] ) token.raw_data = dt if "oauth_verifier" in dt token.validator = dt["oauth_verifier"] end return token end /*# Signature key-string generator. @param cust_secret The customer signature part. @optparam token_secret The part of the secret associated with a token. The OAuth protocol doesn't dictate exactly the way in which authorization strings must be signed, but in cases where counter-signature is required, it mandates that both the customer secret and the token secret must be used. The most common way to counter-sign the authorization string is to concatenate them through a "&" character, which is what this method does. In case different OAuth applications requires different conuter-signature strategies, this method can be overridden by subclasses. */ function makeSecret( cust_secret, token_secret ) if token_secret return cust_secret + "&" + token_secret else return cust_secret + "&" end end //========================================================== // API utilities //========================================================== /*# Call an API protected by OAuth. @param token An instance of @a Token. @param uri The URI of the remote OAuth protected Web API to be called. @optparam params Optional parameters for the call. @return The raw data returned by the remote OAuth procedure. Calls a remote web API and blocks until a result is available. */ function callAPI( token, uri, params ) oauth_params = self._makeBaseParams() oauth_params["oauth_token"] = token.token cr = self.makeOAuthHandler( uri, token.secret, oauth_params, params ) cr.setOutString() cr.exec() return cr.getData() end //# @ignore function makeOAuthHandler( address, tsecret, oauth_params, params ) // Create the base string. if params all_params = oauth_params + params else all_params = oauth_params end base_fields = self._makeGet( all_params ) bstr = self._makeBaseString( self.mode, address, base_fields ) secret = self.makeSecret( self.secret, tsecret ) oauth_signature = Base64.encode( hash.hmac( true, hash.SHA1Hash, secret, bstr ) ) // Add the signature to the fields. oauth_params["oauth_signature"] = oauth_signature all_params["oauth_signature"] = oauth_signature // Prepare the Authorization header. if self.use_header == UseHeader.ALTERN // In use header mode, send OAuth parameters via header. query_string = self._makeGet( params ) headers = ["Authorization: OAuth realm=\"" +address+"\","+ self._makeAuthHeader( oauth_params ) ] elif self.use_header == UseHeader.FULL // In use header mode, send OAuth parameters via header AND via query query_string = self._makeGet( all_params ) headers = ["Authorization: OAuth realm=\"" +address+"\","+ self._makeAuthHeader( oauth_params ) ] else // Send oauth fields only via query query_string = self._makeGet( all_params ) headers = [] end if self.mode == Via.POST cr = curl.Handle( address ) cr.postData( query_string ) headers += ["Content-type: application/x-www-form-urlencoded"] else cr = curl.Handle( address + (query_string ? ("?" + query_string) : "" )) end if headers: cr.setOption( curl.OPT.HTTPHEADER, headers ) cr.setOption( curl.OPT.SSL_VERIFYPEER, false ) //cr.setOption( curl.OPT.HEADER, true ) return cr end //========================================================== // Generic utilities //========================================================== /*# Static utility to parse a query string into a dictionary of values. @param data A query string @return a dictionary of values. Typically, the query string is a pair of "key=value" strings separated by "&" valeus, and encoded as URI encoded values. */ function parseQS( data ) res = [=>] for item in data.split("&") v = item.split("=",2) if v.len() == 1 res[v[0]] ="" else res[v[0]] = URI.decode(v[1]) end end return res end function _makeBaseParams() params = [ "oauth_consumer_key" => self.cust_id, "oauth_signature_method" => self.signature_method, "oauth_timestamp" => toString(epoch()), "oauth_nonce" => self._randomString(25), "oauth_version" => self.version ] return params end function _makeBaseString( method, base_uri, fields ) return method + "&" + URI.encode( base_uri ) + "&" + URI.encode( fields ) end function _makeAuthHeader( params ) str = "" for key, val in params str += URI.encode( key ) + "=\"" + URI.encode( val ) + "\"" formiddle: str += "," end return str end function _makeGet( params ) str = "" for key, val in params str += URI.encode( key ) + "=" + URI.encode(val) + "" formiddle: str += "&" end return str end function _randomString( size ) s = " " * size r = random(0,51) rstep = random(1,351234) for i in [0:size] r = (r + rstep) % 51 rstep = (rstep*2) % 349 + 1 if r >= 26 s[i] = "a"/ (r-26) else s[i] = "A"/ r end end return s end end modules/falcon/web/oauth2.fal000066400000000000000000000141101176363201700164360ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. OAuth2 authentication scheme support - main file FILE: oauth2.fal Main module file ------------------------------------------------------------------- Author: Greta Carenzo Begin: Mon, 21 Jun 2010 13:38:47 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from hash import from curl import from json import from web.oauth // repeat common enums import UseHeader from web.oauth as UseHeader import Via from web.oauth as Via export UseHeader, Via /*# @main Support for OAuth 2.0 protocol. This modules code to establish OAuth sessions under the 2.0 protocol. OAuth is a cross-site authentication exchange protocol used in Web2.0 development. @note The module depends on Feathers @a hash module and on the optional @a curl module. This module also depends from @a web.oauth */ /*# Interface to remote OAuth2 authentication process server. @param consumer_key The consumer key identifying the requester on the remote OAuth2 server. @optparam mode One of the @a Via methods (Defaults to POST). This class acts as an authentication client connecting with a remote server. */ class Client( cust_id, cust_secret, mode ) from web.oauth.Client( cust_id, cust_secret, mode ) //# OAuth protocol is 2.0 here. version = "2.0" /*# Perform a token request. @param address The address of the remote token provider. @param callback Address to be called back by authenticator if the caller is of a web application. @param code A code received calling the authorize uri. @return A new @a Token created through this call. @raise ProtoError if the remote side doesn't complain with the OAuth protocol. This method requests an "Access token" the remote OAuth service. For example, a theoretic workflow may be @code import from web.oauth in oauth code = Reply.redirect("https://TheRemoteService/oauth/authorize",0) //the GET parameters that must be passed are: client_id and redirect_uri. //An important thing is that the redirect_uri and the callback parameter passed in the getToken function must be the same! client = oauth.Client( "MyClientID", "MyClientSecret" ) access_token = client.getToken( "https://TheRemoteService/oauth/access_token", callback,code ) userData = client.callAPI( access_token, "https://TheRemoteService/get_user", ["user_id"=> my_user_id] ) @endcode @note This method blocks until the remote side replies. */ function getToken( address, callback, code) paramsB = self._makeBaseParams() paramsB["grant_type"] = "authorization_code" paramsB["code"] = code if callback: paramsB[ "redirect_uri" ] = callback cr = self.makeOAuthHandler( address, paramsB, nil ) cr.setOutString() cr.exec() data = cr.getData() try dt = self.parseQS( data ) catch ParamError raise web.oauth.ProtoError( 10001, i"Invalid answer from remote.", data ) end if not "access_token" in dt raise web.oauth.ProtoError( 10002, i"Response didn't contain an oauth_token", data ) end token = web.oauth.Token( dt["access_token"] ) return token end //========================================================== // API utilities //========================================================== /*# Call an API protected by OAuth. @param token An instance of @a Token. @param uri The URI of the remote OAuth protected Web API to be called. @optparam params Optional parameters for the call. @return The raw data returned by the remote OAuth procedure. Calls a remote web API and blocks until a result is available. */ function callAPI( token, uri, params ) oauth_params = self._makeBaseParams() oauth_params["access_token"] = token cr = self.makeOAuthHandler( uri, oauth_params, params ) cr.setOutString() cr.exec() data = json.JSONdecode(cr.getData()) if "error" in data raise web.oauth.ProtoError(10004,data["error"]["message"],nil) end return data end //# @ignore function makeOAuthHandler( address, oauth_params, params ) // Create the base string. if params all_params = oauth_params + params else all_params = oauth_params end base_fields = self._makeGet( all_params ) bstr = self._makeBaseString( self.mode, address, base_fields ) // Prepare the Authorization header. if self.use_header == UseHeader.ALTERN // In use header mode, send OAuth parameters via header. query_string = self._makeGet( params ) headers = ["Authorization: OAuth realm=\"" +address+"\","+ self._makeAuthHeader( oauth_params ) ] elif self.use_header == UseHeader.FULL // In use header mode, send OAuth parameters via header AND via query query_string = self._makeGet( all_params ) headers = ["Authorization: OAuth realm=\"" +address+"\","+ self._makeAuthHeader( oauth_params ) ] else // Send oauth fields only via query query_string = self._makeGet( all_params ) headers = [] end if self.mode == Via.POST cr = curl.Handle( address ) cr.postData( query_string ) headers += ["Content-type: application/x-www-form-urlencoded"] else cr = curl.Handle( address + (query_string ? ("?" + query_string) : "" )) end if headers: cr.setOption( curl.OPT.HTTPHEADER, headers ) cr.setOption( curl.OPT.SSL_VERIFYPEER, false ) return cr end //========================================================== // Generic utilities //========================================================== function _makeBaseParams() params = [ "client_id" => self.cust_id , "client_secret" => self.secret ] return params end end modules/native/000077500000000000000000000000001176363201700140225ustar00rootroot00000000000000modules/native/CMakeLists.txt000066400000000000000000000061261176363201700165670ustar00rootroot00000000000000############################################################################### # Falcon Programming language # Build and installation suite # # Native modules # list( APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ) ######################################################### # Utilities used in this section # macro( FalconMakeBinding module lib desc ) if( FALCON_NATMODS_AUTO ) find_package( ${lib} ) if ( ${lib}_FOUND ) option( FALCON_BUILD_${module} "${desc}" ON ) else() option( FALCON_BUILD_${module} "${desc}" OFF ) endif() else() option( FALCON_BUILD_${module} "${desc}" OFF ) endif() endmacro() ######################################################### OPTION( FALCON_NATMODS_AUTO "Select automatically all the modules that have support" ON ) # Native modules, usually compiled OPTION( FALCON_BUILD_DBI "Install DBI module" ON ) OPTION( FALCON_BUILD_DYNLIB "Install DYNLIB module" OFF) OPTION( FALCON_BUILD_MONGODB "Install MongoDB module" ON ) # Bindings -- native modules requiring external libraries. FalconMakeBinding( CURL CURL "Install CURL binding" ) FalconMakeBinding( SDL SDL "Install SDL binding" ) # Other modules option( FALCON_BUILD_WOPI "Install Web Oriented Programming Interface" OFF ) # TODO: foreach ftw if( FALCON_NATMODS_AUTO ) #### Check DBUS find_path( DBUS_INCLUDE_DIR dbus.h HINTS /usr/include/dbus-1.0/dbus ) if ( DBUS_INCLUDE_DIR ) set( _dbus ON ) endif() #### Check DataMatrix include( FalconFindDataMatrix ) #### Check HPDF (LibHARU) find_path( LibHaru_INCLUE_DIR hpdf.h ) if ( LibHaru_INCLUE_DIR ) set( _pdf ON ) endif() #### Check GD2 find_path( GD_INCLUDE_DIR gd.h ) if ( GD_INCLUDE_DIR ) set( _gd2 ON ) endif() #### Check GTK2 include( FalconFindGTK2 ) else() set( _dbus OFF ) set( _dmtx OFF ) set( _gd2 OFF ) set( _gtk OFF ) set( _pdf OFF ) endif() # Bindings -- Requiring more sophisticated checks option( FALCON_BUILD_DBUS "Install DBUS binding" ${_dbus}) option( FALCON_BUILD_DMTX "Install DataMatrix binding" ${_dmtx} ) option( FALCON_BUILD_GD2 "Install GD library binding" ${_gd2}) option( FALCON_BUILD_GTK "Install GTK2 module" ${_gtk} ) option( FALCON_BUILD_PDF "Install libharu (hpdf) binding" ${_pdf}) ######################################################### # Inclusion statements # if(FALCON_BUILD_CURL) add_subdirectory( curl ) endif() if ( FALCON_BUILD_DBI ) add_subdirectory( dbi ) endif() if ( FALCON_BUILD_DBUS ) add_subdirectory( dbus ) endif() if ( FALCON_BUILD_DMTX ) add_subdirectory( dmtx ) endif() if ( FALCON_BUILD_DYNLIB ) add_subdirectory( dynlib ) endif() if ( FALCON_BUILD_GD2 ) add_subdirectory( gd2 ) endif() if ( FALCON_BUILD_GTK ) add_subdirectory( gtk ) endif() if ( FALCON_BUILD_MONGODB ) add_subdirectory( mongodb ) endif() if ( FALCON_BUILD_PDF ) add_subdirectory( hpdf ) endif() if ( FALCON_BUILD_SDL ) add_subdirectory( sdl ) endif() if ( FALCON_BUILD_WOPI ) add_subdirectory( wopi ) endif() if ( FALCON_BUILD_CONIO ) add_subdirectory( conio ) endif() modules/native/MP/000077500000000000000000000000001176363201700143365ustar00rootroot00000000000000modules/native/MP/CMakeLists.txt000066400000000000000000000012011176363201700170700ustar00rootroot00000000000000#################################################################### # FALCON - The Falcon Programming Language. # # CMake configuration file for MP #################################################################### PROJECT(MP) cmake_minimum_required(VERSION 2.6) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) # Set here project-wide options # OPTION( WITH_OPT "An poption" OFF) #message("ARPREC_ROOT=${ARPREC_ROOT}") find_package( Falcon REQUIRED ) #find_package(ARPREC REQUIRED) #include_directories(${ARPREC_INCLUDE_DIR}) #process source directory ADD_SUBDIRECTORY(src) #add other subdirectories here. modules/native/MP/README000066400000000000000000000061541176363201700152240ustar00rootroot00000000000000 The Falcon Programming Language Skeleton Module This directory contains a skeleton Falcon Module. The module defines a minimal module meant as a minimal development base to help speed up module writing startup phase. The skeleton module is divided into several files to provide better customizability: fmodskel.cpp - The main module file; here exported the module object is created and exported functions are defined. fmodskel_ext.cpp/.h - this files declare and define the extension (script interface) functions. Add new functions here. fmodskel_mod.cpp/.h - this files declare and define the module internal logic. If the module is extremely symple, or if it has not an internal logic that may be exported or treated separately with resepect to the extension functions, you may remove this files. fmodskel_srv.cpp/.h - this files declares the service exported by the module. A service is a re-publishing of the internal logic (mod) towards C++ application via a fully virtual class. The service is published to the module and to all the VM where the module is linked. Applications may then load the module and access the service by knowing only its name and including the header file. In this way it is possible to reuse the internal logic that scripts may access also through C++ code. fmodskel_st.cpp/.h - this files are the string table for module internationalization. See the directions in the files to get more details on how to use this table to provide translations for binary falcon modules. Other than these, there are project files/make files working on the supported platform, and a set of "template" files to be used when creating new sources. To configure this files and start a new project, copy the directory, enter it and use the falcon script "falconeer.fal". Falconeer is a Falcon utility that is shipped with development packages in of every supported platform. On systems where scripts can be given execution rights (i.e. Linux, Bsd, Macosx ecc. ) you may call directly $ falconeer.fal from the command prompt. Systems as MS-Windows will require the interpreter to load the script from the right path. A "configure.bat" script is provided as a sample; it can be changed so to call falconeer with the proper parametrs. On systems provided with "man" utility a falconeer.fal man page is shipped with every Falcon installation; in other systems, you may load the script without arguments to have a short inline help. The script changes the names of the modules, and the supported development platforms project settings so to match the project name that you have set. Once configured with Falconeer, the module in this directory may be immediately compiled and tested. The module provides a single RTL function called "skeleton()" that returns 0, and publish that function through a Service interface which just calls it and rerturns its value. Once configured the skeleton module, you may remove unneded files and add new ones. To install a module, just copy it in one of the directories listed in the FALCON_LOAD_PATH environment variables. modules/native/MP/src/000077500000000000000000000000001176363201700151255ustar00rootroot00000000000000modules/native/MP/src/CMakeLists.txt000066400000000000000000000015161176363201700176700ustar00rootroot00000000000000#################################################################### # FALCON - The Falcon Programming Language. # # CMake configuration file for MP #################################################################### # Inclusion settings INCLUDE_DIRECTORIES(.) # Enable this include if the project has a private include #INCLUDE_DIRECTORIES( "${PROJECT_SOURCE_DIR}/include" ) # # Falcon generic directories # INCLUDE_DIRECTORIES( ${falcon_INCLUDE_DIRS}) LINK_DIRECTORIES("${FALCON_LIB_PATH}") #sources FILE( GLOB SRC_FILES "*.cpp" ) # Target ADD_LIBRARY( MP_fm MODULE ${SRC_FILES} ) #Link TARGET_LINK_LIBRARIES( MP_fm ${GMP_LIBRARIES}) falcon_finalize_module( MP_fm ) #If your module needs some lib more, use the followings: # # TARGET_LINK_LIBRARIES( ${CURRENT_MODULE} falcon_engine) # FALCON_INSTALL_MODULE( ${CURRENT_MODULE} ) # modules/native/MP/src/MP.cpp000066400000000000000000000055051176363201700161520ustar00rootroot00000000000000 /* FALCON - The Falcon Programming Language. FILE: MP_ext.cpp Multi-Precision Math support Main module file, providing the module object to the Falcon engine. ------------------------------------------------------------------- Author: Paul Davey Begin: Fri, 12 Mar 2010 15:58:42 +0000 ------------------------------------------------------------------- (C) Copyright 2010: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Main module file, providing the module object to the Falcon engine. */ #include #include "MP_ext.h" #include "MP_srv.h" #include "MP_st.h" #include "version.h" /*--# << change this to activate. @module MP MP @brief MP module */ FALCON_MODULE_DECL { #define FALCON_DECLARE_MODULE self // initialize the module Falcon::Module *self = new Falcon::Module(); self->name( "MP" ); self->language( "en_US" ); self->engineVersion( FALCON_VERSION_NUM ); self->version( VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION ); //============================================================ // Here declare the international string table implementation // #include "MP_st.h" //============================================================ // Here declare skeleton api // //self->addExtFunc( "skeleton", Falcon::Ext::skeleton ); //self->addExtFunc( "skeletonString", Falcon::Ext::skeletonString ); Falcon::Symbol *MPZ_cls = self->addClass( "MPZ", Falcon::Ext::MPZ_init )->addParam( "value" )->addParam("base")->setWKS(true); self->addClassMethod(MPZ_cls, "__add", Falcon::Ext::MPZ_add).asSymbol()->addParam( "other" ); self->addClassMethod(MPZ_cls, "add", Falcon::Ext::MPZ_add).asSymbol()->addParam( "other" )->addParam( "inPlace" ); self->addClassMethod(MPZ_cls, "__sub", Falcon::Ext::MPZ_sub).asSymbol()->addParam( "other" ); self->addClassMethod(MPZ_cls, "sub", Falcon::Ext::MPZ_sub).asSymbol()->addParam( "other" )->addParam( "inPlace" ); self->addClassMethod(MPZ_cls, "toString", Falcon::Ext::MPZ_toString).asSymbol()->addParam( "base" ); //============================================================ // Publish Skeleton service // //self->publishService( new Falcon::Srv::Skeleton() ); return self; } /* end of MP.cpp */ modules/native/MP/src/MP_ext.cpp000066400000000000000000000137561176363201700170410ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: MP_ext.cpp Multi-Precision Math support Interface extension functions ------------------------------------------------------------------- Author: Paul Davey Begin: Fri, 12 Mar 2010 15:58:42 +0000 ------------------------------------------------------------------- (C) Copyright 2010: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Multi-Precision Math support Interface extension functions */ #include #include "MP_mod.h" #include "MP_ext.h" #include "MP_st.h" namespace Falcon { namespace Ext { // The following is a faldoc block for the function /*--# << change this to activate. @function skeleton @brief A basic script function. @return Zero. This function just illustrates how to bind the ineer MOD logic with the script. Also, Mod::skeleton(), used by this function, is exported through the "service", so it is possible to call the MOD logic directly from an embedding application, once loaded the module and accessed the service. */ FALCON_FUNC MPZ_init( ::Falcon::VMachine *vm ) { Item *i_value = vm->param(0); Item *i_self = &vm->self(); if ( !i_self->isOfClass( "MPZ" ) ) { throw new Falcon::TypeError( Falcon::ErrorParam( Falcon::e_not_implemented, __LINE__ ) .extra( "This should never ever ever happen!" ) ); } if ( i_value == 0 ) { i_self->asObject()->setUserData(new Mod::MPZ_carrier()); } else if ( i_value->isInteger() ) { i_self->asObject()->setUserData(new Mod::MPZ_carrier(i_value->asInteger())); } else if ( i_value->isNumeric() ) { i_self->asObject()->setUserData(new Mod::MPZ_carrier(i_value->asNumeric())); } else if ( i_value->isString() ) { Item *i_base = vm->param(1); int64 base; if ( i_base != 0 && ( i_base->isNumeric() || i_base->isInteger() ) ) { base = i_base->forceInteger(); } else { base = 0; } i_self->asObject()->setUserData(new Mod::MPZ_carrier(i_value->asString(), base)); } else { throw new Falcon::ParamError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ) .extra( "N or S,[N]" ) ); } } /*--# << change this to activate. @function __add @brief Addition of MP Integers. @return The sum of the MPZ and the other parameter. Addition of Multi-Precision Integers. Supports MPInts, standard integers as well as floating point numbers MPRationals and MPFloats. */ FALCON_FUNC MPZ_add( ::Falcon::VMachine *vm ) { Item *i_other = vm->param(0); if ( i_other == 0 ) { throw new Falcon::ParamError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ) .extra( "N or MPZ or MPQ or MPF" ) ); } Item *i_inplace = vm->param(1); Item *i_self = &vm->self(); CoreClass *mpz_class = vm->findWKI("MPZ")->asClass(); CoreObject *result; if ( i_inplace == 0 || !i_inplace->isTrue() ) { result = mpz_class->createInstance(); Mod::MPZ_carrier *result_data = new Mod::MPZ_carrier(); result->setUserData(result_data); } else { result = i_self->asObject(); } if ( i_other->isInteger() ) { Mod::MPZ_carrier otherMPZ(i_other->asInteger()); } else if ( i_other->isNumeric() ) { Mod::MPZ_carrier otherMPZ(i_other->asNumeric()); } else if ( i_other->isOfClass("MPZ") ) { Mod::MPZ_carrier *otherMPZ = static_cast(i_other->asObject()->getFalconData()); } else { throw new Falcon::ParamError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ) .extra( "N or MPZ or MPQ or MPF" ) ); } vm->retval(result); } /*--# << change this to activate. @function __sub @brief Subtraction of MP Integers. @return The difference between the MPZ and the other parameter. Subtraction of Multi-Precision Integers. Supports MPInts, standard integers as well as floating point numbers MPRationals and MPFloats. */ FALCON_FUNC MPZ_sub( ::Falcon::VMachine *vm ) { Item *i_other = vm->param(0); if ( i_other == 0 ) { throw new Falcon::ParamError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ) .extra( "N or MPZ or MPQ or MPF" ) ); } Item *i_inplace = vm->param(1); Item *i_self = &vm->self(); CoreClass *mpz_class = vm->findWKI("MPZ")->asClass(); CoreObject *result; if ( i_inplace == 0 || !i_inplace->isTrue() ) { result = mpz_class->createInstance(); Mod::MPZ_carrier *result_data = new Mod::MPZ_carrier(); result->setUserData(result_data); } else { result = i_self->asObject(); } if ( i_other->isInteger() ) { Mod::MPZ_carrier otherMPZ(i_other->asInteger()); } else if ( i_other->isNumeric() ) { Mod::MPZ_carrier otherMPZ(i_other->asNumeric()); } else if ( i_other->isOfClass("MPZ") ) { Mod::MPZ_carrier *otherMPZ = static_cast(i_other->asObject()->getFalconData()); } else { throw new Falcon::ParamError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ) .extra( "N or MPZ or MPQ or MPF" ) ); } vm->retval(result); } FALCON_FUNC MPZ_toString( ::Falcon::VMachine *vm ) { Item *i_base = vm->param(0); CoreObject *self = vm->self().asObject(); int64 base; if ( i_base != 0 && ( i_base->isNumeric() || i_base->isInteger() ) ) { base = i_base->forceInteger(); } else { base = 10; } vm->retval(static_cast(self->getFalconData())->toString(base)); } } } /* end of MP_mod.cpp */ modules/native/MP/src/MP_ext.h000066400000000000000000000026241176363201700164760ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: MP_ext.cpp Multi-Precision Math support Interface extension functions ------------------------------------------------------------------- Author: Paul Davey Begin: Fri, 12 Mar 2010 15:58:42 +0000 ------------------------------------------------------------------- (C) Copyright 2010: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Multi-Precision Math support Interface extension functions - header file */ #ifndef MP_ext_H #define MP_ext_H #include namespace Falcon { namespace Ext { FALCON_FUNC MPZ_init( ::Falcon::VMachine *vm ); FALCON_FUNC MPZ_add( ::Falcon::VMachine *vm ); FALCON_FUNC MPZ_sub( ::Falcon::VMachine *vm ); FALCON_FUNC MPZ_toString( ::Falcon::VMachine *vm ); } } #endif /* end of MP_ext.h */ modules/native/MP/src/MP_mod.cpp000066400000000000000000000040631176363201700170070ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: MP_ext.cpp Multi-Precision Math support Interface extension functions ------------------------------------------------------------------- Author: Paul Davey Begin: Fri, 12 Mar 2010 15:58:42 +0000 ------------------------------------------------------------------- (C) Copyright 2010: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Multi-Precision Math support Internal logic functions - implementation. */ #include "MP_mod.h" #include #include namespace Falcon { namespace Mod { MPZ_carrier::MPZ_carrier() { } MPZ_carrier::MPZ_carrier(int64 num) { } MPZ_carrier::MPZ_carrier(double num) { } MPZ_carrier::MPZ_carrier(String *num, int64 base) { AutoCString numStr(*num); } MPZ_carrier::MPZ_carrier(const MPZ_carrier &otherMPZ) { } MPZ_carrier::~MPZ_carrier() { } MPZ_carrier *MPZ_carrier::clone() const { return new MPZ_carrier(*this); } bool MPZ_carrier::serialize( Stream *stream, bool bLive ) const { return false; } bool MPZ_carrier::deserialize( Stream *stream, bool bLive ) { return false; } String *MPZ_carrier::toString(int64 base) { //size_t length = //String *string = new String(); //string->setCharSize(1); //char *buff = (char *)memAlloc(length); //string->adopt(buff, std::strlen(buff),length); //return string; } } } /* end of MP_mod.cpp */ modules/native/MP/src/MP_mod.h000066400000000000000000000033241176363201700164530ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: MP_ext.cpp Multi-Precision Math support Interface extension functions ------------------------------------------------------------------- Author: Paul Davey Begin: Fri, 12 Mar 2010 15:58:42 +0000 ------------------------------------------------------------------- (C) Copyright 2010: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Multi-Precision Math support Internal logic functions - declarations. */ #ifndef MP_mod_H #define MP_mod_H #include #include namespace Falcon { namespace Mod { class MPZ_carrier : public FalconData { public: MPZ_carrier(); MPZ_carrier( int64 num ); MPZ_carrier( double num ); MPZ_carrier( String *num, int64 base ); MPZ_carrier( const MPZ_carrier &otherMPZ ); ~MPZ_carrier(); virtual void gcMark( uint32 mark ){} virtual MPZ_carrier *clone() const; virtual bool serialize( Stream *stream, bool bLive ) const; virtual bool deserialize( Stream *stream, bool bLive ); String *toString( int64 base ); }; } } #endif /* end of MP_mod.h */ modules/native/MP/src/MP_srv.cpp000066400000000000000000000022621176363201700170410ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: MP_ext.cpp Multi-Precision Math support Interface extension functions ------------------------------------------------------------------- Author: Paul Davey Begin: Fri, 12 Mar 2010 15:58:42 +0000 ------------------------------------------------------------------- (C) Copyright 2010: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Service publishing - reuse Falcon module logic (mod) in your applications! */ #include "MP_srv.h" #include "MP_mod.h" namespace Falcon { namespace Srv { } } /* end of MP_srv.cpp */ modules/native/MP/src/MP_srv.h000066400000000000000000000027621176363201700165130ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: MP_ext.cpp Multi-Precision Math support Interface extension functions ------------------------------------------------------------------- Author: Paul Davey Begin: Fri, 12 Mar 2010 15:58:42 +0000 ------------------------------------------------------------------- (C) Copyright 2010: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Service publishing - reuse Falcon module logic (mod) in your applications! */ #ifndef MP_SRV_H #define MP_SRV_H #include namespace Falcon { namespace Srv { // provide a class that will serve as a service provider. class Skeleton: public Service { public: // declare the name of the service as it will be published. Skeleton(): Service( "Skeleton" ) {} // Provide here methods that needs to be exported. int skeleton(); }; } } #endif /* end of MP_srv.h */ modules/native/MP/src/MP_st.cpp000066400000000000000000000022461176363201700166570ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: MP_st.cpp Multi-Precision Math support Interface extension functions ------------------------------------------------------------------- Author: Paul Davey Begin: Fri, 12 Mar 2010 15:58:42 +0000 ------------------------------------------------------------------- (C) Copyright 2010: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file String table - export internationalizable stings to both C++ and Falcon modules. */ #define FALCON_REALIZE_STRTAB #include "MP_st.h" /* end of MP_st.cpp */ modules/native/MP/src/MP_st.h000066400000000000000000000034221176363201700163210ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: MP_st.h Multi-Precision Math support Interface extension functions ------------------------------------------------------------------- Author: Paul Davey Begin: Fri, 12 Mar 2010 15:58:42 +0000 ------------------------------------------------------------------- (C) Copyright 2010: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file String table - export internationalizable stings to both C++ and Falcon modules. */ //WARNING: the missing of usual #ifndef/#define pair // is intentional! // See the contents of this header file for a deeper overall // explanation of the MODSTR system. #include // The first parameter is an unique identifier in your project that // will be bound to the correct entry in the module string table. // Falcon::VMachine::moduleString( MP_msg_1 ) will // return the associated string or the internationalized version. // FAL_STR( MP_msg_1 ) macro can be used in standard // functions as a shortcut. FAL_MODSTR( MP_msg_1, "An internationalizable message" ); //... add here your messages, and remove or configure the above one /* end of MP_st.h */ modules/native/MP/src/version.h000066400000000000000000000007451176363201700167710ustar00rootroot00000000000000/* @MAIN_PRJ@ FILE: version.h @DESCRIPTION@ Interface extension functions ------------------------------------------------------------------- Author: @AUTHOR@ Begin: @DATE@ ------------------------------------------------------------------- (C) Copyright @YEAR@: @COPYRIGHT@ @LICENSE@ */ #ifndef VERSION_H #define VERSION_H #define VERSION_MAJOR 0 #define VERSION_MINOR 0 #define VERSION_REVISION 0 #endif /* end of version.h */ modules/native/README000066400000000000000000000014671176363201700147120ustar00rootroot00000000000000NATIVE modules This directory contains important falcon modules that are not considered standard language parts (as Feathers) but are nevertheless important and to be distributed with Falcon where possible and applicable. The install destination on target system is the default falcon modules destination, although some of the native modules may be installed in non-root parts of the falcon module hierarcy (i.e. net.*, web.* and so on). Some of the native modules may require special requirements on the target system to be succesfully installed, or build and install also helper applications, framework elements, documentation and other items that are not "falcon modules", strictly speaking. Please, refer to the documentation and to the README files of each single module to have more information on each element. modules/native/cmake/000077500000000000000000000000001176363201700151025ustar00rootroot00000000000000modules/native/cmake/FalconFindDataMatrix.cmake000066400000000000000000000015701176363201700220710ustar00rootroot00000000000000# FalconFindDataMatrix.cmake # # Set vars _dmtx to ON or OFF, # DMTX_INCLUDE_PATH and DMTX_LIBRARY. if ( WIN32 ) find_path( DMTX_INCLUDE_PATH dmtx.h HINTS c:/dmtx c:/libdmtx-0.7.2 c:/data/dmtx c:/data/libdmtx-0.7.2 ) find_library( DMTX_LIBRARY libdmtx HINTS c:/dmtx c:/libdmtx-0.7.2 c:/data/dmtx c:/data/libdmtx-0.7.2 ) else() find_path( DMTX_INCLUDE_PATH dmtx.h HINTS /usr/include /usr/local/include ) find_library( DMTX_LIBRARY dmtx HINTS /usr/lib /usr/local/lib ) endif() mark_as_advanced( DMTX_INCLUDE_PATH ) mark_as_advanced( DMTX_LIBRARY ) if ( ( DMTX_INCLUDE_PATH STREQUAL DMTX_INCLUDE_PATH-NOTFOUND ) OR ( DMTX_LIBRARY STREQUAL DMTX_LIBRARY-NOTFOUND ) ) set( _dmtx OFF ) else() set( _dmtx ON ) endif() modules/native/cmake/FalconFindGTK2.cmake000066400000000000000000000007461176363201700205460ustar00rootroot00000000000000# FalconFindGTK2.cmake # # Set var _gtk to ON or OFF. # On Windows, set a special var GTK_BUNDLE_DIR. if ( WIN32 ) find_file( GTK_BUNDLE_DIR NAMES gtk gtk2 GTK GTK2 HINTS c:/ c:/data ENV GTK_BUNDLE_DIR ) if ( GTK_BUNDLE_DIR STREQUAL GTK_BUNDLE_DIR-NOTFOUND ) set( _gtk OFF ) else() set( _gtk ON ) endif() else() find_package( GTK2 ) if ( GTK2_FOUND ) set( _gtk ON ) else() set( _gtk OFF ) endif() endif() modules/native/conio/000077500000000000000000000000001176363201700151315ustar00rootroot00000000000000modules/native/conio/CMakeLists.txt000066400000000000000000000011651176363201700176740ustar00rootroot00000000000000#################################################################### # @MAIN_PRJ@ # # CMake configuration file for @PROJECT_NAME@ #################################################################### cmake_minimum_required(VERSION 2.6) project(conio) # find the falcon installation. Set CMAKE_INSTALL_PREFIX to the falcon root # directory to make it work. find_package(Falcon REQUIRED) ## process source directories # add other subdirectories that contain libraries on which the final module depends here.. #add_subdirectory(awesome_library) # ..before the module's source dir: add_subdirectory(src) add_subdirectory(conbox) modules/native/conio/conbox/000077500000000000000000000000001176363201700164215ustar00rootroot00000000000000modules/native/conio/conbox/CMakeLists.txt000066400000000000000000000022631176363201700211640ustar00rootroot00000000000000#################################################################### # @MAIN_PRJ@ # # CMake configuration file for @PROJECT_NAME@ #################################################################### # Creates the proper module file name from the project name. falcon_define_module( CURRENT_MODULE conbox ) # Inclusion settings include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${Falcon_INCLUDE_DIRS} ) # Enable this include if the project has a private include include_directories( ${PROJECT_SOURCE_DIR}/include ) # Sources files the module is built from. set(SRC_FILES conbox.cpp conbox_ext.cpp conbox_st.cpp # add more source files starting here: ) # These are actually not needed by cmake to build. But if omitted they won't be # listed in the virtual file tree of Visual Studio. set(HDR_FILES conbox_ext.h conbox_st.h version.h # add more header files starting here: ) # Builds and links from the source files add_library( ${CURRENT_MODULE} MODULE ${SRC_FILES} ${HDR_FILES} # optional, see comment above. ) # If your module needs some lib more, use the followings: target_link_libraries( ${CURRENT_MODULE} falcon_engine) falcon_install_module( ${CURRENT_MODULE} ) modules/native/conio/conbox/conbox.cpp000066400000000000000000000050531176363201700204200ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: conbox_ext.cpp Basic Console I/O support Main module file, providing the module object to the Falcon engine. ------------------------------------------------------------------- Author: Unknown author Begin: Thu, 05 Sep 2008 20:12:14 +0200 ------------------------------------------------------------------- (C) Copyright 2008: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Main module file, providing the module object to the Falcon engine. */ #include #include "conbox_ext.h" #include "conbox_st.h" #include "version.h" /*# @module conio_conbox conbox @brief Extended GUI-like interface for text-based terminals. @inmodule conio @note Currently this module is under development and not released. This module accompains the conio module and provides extended text-oriented interface functionalities to create boxes, virtual windows, mouse buttons and so on in text interfaces. */ FALCON_MODULE_DECL( const Falcon::EngineData &data ) { #define FALCON_DECLARE_MODULE self // set the static engine data data.set(); // initialize the module Falcon::Module *self = new Falcon::Module(); self->name( "conbox" ); self->language( "en_US" ); self->engineVersion( FALCON_VERSION_NUM ); self->version( VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION ); //============================================================ // Here declare the international string table implementation // #include "conbox_st.h" //============================================================ // Here declare skeleton api // self->addExtFunc( "cb_skeleton", Falcon::Ext::cb_skeleton ); self->addExtFunc( "cb_skeletonString", Falcon::Ext::cb_skeletonString ); //============================================================ // Declare dependency (private --- we just need the service) // self->addDepend( "conio", true ); return self; } /* end of conbox.cpp */ modules/native/conio/conbox/conbox_ext.cpp000066400000000000000000000040321176363201700212740ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: conbox_ext.cpp Basic Console I/O support Interface extension functions ------------------------------------------------------------------- Author: Unknown author Begin: Thu, 05 Sep 2008 20:12:14 +0200 ------------------------------------------------------------------- (C) Copyright 2008: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Basic Console I/O support Interface extension functions */ #include #include "conbox_ext.h" #include "conbox_st.h" /*--# @beginmodule conbox */ namespace Falcon { namespace Ext { // The following is a faldoc block for the function /*--# @function skeleton @brief A basic script function. @return Zero. */ FALCON_FUNC cb_skeleton( ::Falcon::VMachine *vm ) { vm->retval( (int64) 0 ); } /*--# @function skeletonString @brief A function returning a string in the string table. @return A message that can be internationalized. This function returns a string from the string table (see conbox_st.h). The returned string may be internationalized through the standard falcon internationalization system (the same available for scripts). A real module will want to use this system to produce locale-configurable messages (expecially the "extra" field of error descriptions). */ FALCON_FUNC cb_skeletonString( ::Falcon::VMachine *vm ) { vm->retval( FAL_STR( conbox_msg_1 ) ); } } } /* end of conbox_ext.cpp */ modules/native/conio/conbox/conbox_ext.h000066400000000000000000000024511176363201700207440ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: conbox_ext.cpp Basic Console I/O support Interface extension functions ------------------------------------------------------------------- Author: Unknown author Begin: Thu, 05 Sep 2008 20:12:14 +0200 ------------------------------------------------------------------- (C) Copyright 2008: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Basic Console I/O support Interface extension functions - header file */ #ifndef conbox_ext_H #define conbox_ext_H #include namespace Falcon { namespace Ext { FALCON_FUNC cb_skeleton( ::Falcon::VMachine *vm ); FALCON_FUNC cb_skeletonString( ::Falcon::VMachine *vm ); } } #endif /* end of conbox_ext.h */ modules/native/conio/conbox/conbox_st.cpp000066400000000000000000000022221176363201700211210ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: conbox_st.cpp Basic Console I/O support Interface extension functions ------------------------------------------------------------------- Author: Unknown author Begin: Thu, 05 Sep 2008 20:12:14 +0200 ------------------------------------------------------------------- (C) Copyright 2008: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file String table - export internationalizable stings to both C++ and Falcon modules. */ #define FALCON_REALIZE_STRTAB #include "conbox_st.h" /* end of conbox_st.cpp */ modules/native/conio/conbox/conbox_st.h000066400000000000000000000034061176363201700205730ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: conbox_st.h Basic Console I/O support Interface extension functions ------------------------------------------------------------------- Author: Unknown author Begin: Thu, 05 Sep 2008 20:12:14 +0200 ------------------------------------------------------------------- (C) Copyright 2008: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file String table - export internationalizable stings to both C++ and Falcon modules. */ //WARNING: the missing of usual #ifndef/#define pair // is intentional! // See the contents of this header file for a deeper overall // explanation of the MODSTR system. #include // The first parameter is an unique identifier in your project that // will be bound to the correct entry in the module string table. // Falcon::VMachine::moduleString( conbox_msg_1 ) will // return the associated string or the internationalized version. // FAL_STR( conbox_msg_1 ) macro can be used in standard // functions as a shortcut. FAL_MODSTR( conbox_msg_1, "An internationalizable message" ); //... add here your messages, and remove or configure the above one /* end of conbox_st.h */ modules/native/conio/conbox/version.h000066400000000000000000000006011176363201700202540ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: verion.h Conbox module version informations ------------------------------------------------------------------- Author: Begin: */ #ifndef VERSION_H #define VERSION_H #define VERSION_MAJOR 0 #define VERSION_MINOR 0 #define VERSION_REVISION 0 #endif /* end of version.h */ modules/native/conio/include/000077500000000000000000000000001176363201700165545ustar00rootroot00000000000000modules/native/conio/include/conio_srv.h000066400000000000000000000035741176363201700207370ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: conio_srv.h Basic Console I/O support Interface extension functions ------------------------------------------------------------------- Author: Unknown author Begin: Thu, 05 Sep 2008 20:12:14 +0200 ------------------------------------------------------------------- (C) Copyright 2008: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Service publishing - reuse Falcon module logic (mod) in your applications! */ #ifndef conio_SRV_H #define conio_SRV_H #include namespace Falcon { namespace Srv { //forward for system specific data class ConioSrvSys; // provide a class that will serve as a service provider. class ConsoleSrv: public Service { class ConioSrvSys *m_sys; public: typedef enum { e_none, e_init, e_not_init, e_dbl_init, e_read, e_write } error_type; // declare the name of the service as it will be published. ConsoleSrv(); virtual ~ConsoleSrv(); /** Initialize the console. On error during the initialization, returns e_init */ virtual error_type init(); /** Clears the screen. */ virtual error_type cls(); /** Turns off console services. */ virtual void shutdown(); }; } } #endif /* end of conio_srv.h */ modules/native/conio/src/000077500000000000000000000000001176363201700157205ustar00rootroot00000000000000modules/native/conio/src/CMakeLists.txt000066400000000000000000000024741176363201700204670ustar00rootroot00000000000000#################################################################### # FALCON - The Falcon Programming Language. # # CMake configuration file for conio #################################################################### # Creates the proper module file name from the project name. falcon_define_module( CURRENT_MODULE conio ) # Inclusion settings include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${Falcon_INCLUDE_DIRS} ) # Enable this include if the project has a private include include_directories( ${PROJECT_SOURCE_DIR}/include ) # Sources files the module is built from. set(SRC_FILES conio.cpp conio_ext.cpp conio_mod.cpp conio_st.cpp # add more source files starting here: ) #system specific sources if(WIN32) list(APPEND SRC_FILES sys/conio_srv_win.cpp) endif() # These are actually not needed by cmake to build. But if omitted they won't be # listed in the virtual file tree of Visual Studio. set(HDR_FILES conio_ext.h conio_mod.h conio_st.h version.h # add more header files starting here: ) # Builds and links from the source files add_library( ${CURRENT_MODULE} MODULE ${SRC_FILES} ${HDR_FILES} # optional, see comment above. ) # If your module needs some lib more, use the followings: target_link_libraries( ${CURRENT_MODULE} falcon_engine) falcon_install_module( ${CURRENT_MODULE} ) modules/native/conio/src/conio.cpp000066400000000000000000000063571176363201700175460ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: conio_ext.cpp Basic Console I/O support Main module file, providing the module object to the Falcon engine. ------------------------------------------------------------------- Author: Unknown author Begin: Thu, 05 Sep 2008 20:12:14 +0200 ------------------------------------------------------------------- (C) Copyright 2008: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Main module file, providing the module object to the Falcon engine. */ #include #include "conio_ext.h" #include #include "conio_st.h" #include "version.h" Falcon::Srv::ConsoleSrv *console_service; /*# @module conio Console (terminal) I/O support. @brief Console (terminal) I/O support. @note Currently this module is under development and not released. This module provides an interface to a generic terminal/character oriented device. Even in an age where GUI is omnipresent, a character-based terminal interface can be useful on some high-end server environments (i.e. text-based ssh/tty network connections), or to setup a minimal user interface for low-level system maintainance utilities. */ FALCON_MODULE_DECL( const Falcon::EngineData &data ) { #define FALCON_DECLARE_MODULE self // set the static engine data data.set(); // initialize the module Falcon::Module *self = new Falcon::Module(); self->name( "conio" ); self->language( "en_US" ); self->engineVersion( FALCON_VERSION_NUM ); self->version( VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION ); //============================================================ // Here declare the international string table implementation // #include "conio_st.h" //============================================================ // Here declare skeleton api // self->addExtFunc( "initscr", Falcon::Ext::initscr )-> addParam( "flags" ); self->addExtFunc( "ttString", Falcon::Ext::ttString )-> addParam( "str" ); self->addExtFunc( "closescr", Falcon::Ext::closescr ); //============================================================ // ConioError class Falcon::Symbol *error_class = self->addExternalRef( "Error" ); // it's external Falcon::Symbol *conioerr_cls = self->addClass( "ConioError", Falcon::Ext::ConioError_init ); conioerr_cls->setWKS( true ); conioerr_cls->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); //============================================================ // Publish CONSOLE service // console_service = new Falcon::Srv::ConsoleSrv(); self->publishService( console_service ); return self; } /* end of conio.cpp */ modules/native/conio/src/conio_ext.cpp000066400000000000000000000067571176363201700204320ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: conio_ext.cpp Basic Console I/O support Interface extension functions ------------------------------------------------------------------- Author: Unknown author Begin: Thu, 05 Sep 2008 20:12:14 +0200 ------------------------------------------------------------------- (C) Copyright 2008: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Basic Console I/O support Interface extension functions */ #include #include "conio_mod.h" #include "conio_ext.h" #include "conio_st.h" #include extern Falcon::Srv::ConsoleSrv *console_service; /*# @beginmodule conio */ namespace Falcon { namespace Ext { // The following is a faldoc block for the function /*# @function initscr @brief Initializes the screen. @optparam flags Initialization params. @raise ConioError on initialization failed. Initializes the screen. */ FALCON_FUNC initscr( ::Falcon::VMachine *vm ) { Item *i_flags = vm->param( 0 ); if( i_flags != 0 && ! i_flags->isOrdinal() ) { vm->raiseModError( new ParamError( ErrorParam( e_inv_params, __LINE__ ). extra("[N]") ) ); return; } // get the flags... int flags = i_flags == 0 ? 0 : (int) i_flags->forceInteger(); //... init things... if ( console_service->init() != Srv::ConsoleSrv::e_none ) { vm->raiseModError( new ConioError( ErrorParam( 5000, __LINE__ ). desc( FAL_STR( conio_msg_2 ) ) ) ); } } /*# @function ttString @brief Write teletype a given string. @param str The string to be written. Writes a string on the screen and advaces the position of the logical cursor. */ FALCON_FUNC ttString( ::Falcon::VMachine *vm ) { Item *i_str = vm->param( 0 ); if( i_str == 0 || ! i_str->isString() ) { vm->raiseModError( new ParamError( ErrorParam( e_inv_params, __LINE__ ). origin( e_orig_runtime ). extra("S") ) ); return; } AutoCString cstr( * i_str->asString() ); //... use the string... // ... let's return a fake value... vm->retval( (int64)cstr.length() ); } /*# @function closescr @brief Deinitializes the screen. @raise ConioError on deinitialization failed. Closes the screen. */ FALCON_FUNC closescr( ::Falcon::VMachine *vm ) { // chiudi console_service->shutdown(); } /*# @class ConioError @brief Error generated because of problems on the console I/O. @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. @from Error code, description, extra See the Error class in the core module. */ FALCON_FUNC ConioError_init ( ::Falcon::VMachine *vm ) { CoreObject *einst = vm->self().asObject(); if( einst->getUserData() == 0 ) einst->setUserData( new ConioError ); ::Falcon::core::Error_init( vm ); } } } /* end of conio_mod.cpp */ modules/native/conio/src/conio_ext.h000066400000000000000000000031421176363201700200600ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: conio_ext.cpp Basic Console I/O support Interface extension functions ------------------------------------------------------------------- Author: Unknown author Begin: Thu, 05 Sep 2008 20:12:14 +0200 ------------------------------------------------------------------- (C) Copyright 2008: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Basic Console I/O support Interface extension functions - header file */ #ifndef conio_ext_H #define conio_ext_H #include #include namespace Falcon { namespace Ext { FALCON_FUNC initscr( ::Falcon::VMachine *vm ); FALCON_FUNC ttString( ::Falcon::VMachine *vm ); FALCON_FUNC closescr( ::Falcon::VMachine *vm ); class ConioError: public ::Falcon::Error { public: ConioError(): Error( "ConioError" ) {} ConioError( const ErrorParam ¶ms ): Error( "ConioError", params ) {} }; FALCON_FUNC ConioError_init ( ::Falcon::VMachine *vm ); } } #endif /* end of conio_ext.h */ modules/native/conio/src/conio_mod.cpp000066400000000000000000000022141176363201700203710ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: conio_ext.cpp Basic Console I/O support Interface extension functions ------------------------------------------------------------------- Author: Unknown author Begin: Thu, 05 Sep 2008 20:12:14 +0200 ------------------------------------------------------------------- (C) Copyright 2008: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Basic Console I/O support Internal logic functions - implementation. */ namespace Falcon { namespace Mod { int skeleton() { return 0; } } } /* end of conio_mod.cpp */ modules/native/conio/src/conio_mod.h000066400000000000000000000022501176363201700200360ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: conio_ext.cpp Basic Console I/O support Interface extension functions ------------------------------------------------------------------- Author: Unknown author Begin: Thu, 05 Sep 2008 20:12:14 +0200 ------------------------------------------------------------------- (C) Copyright 2008: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Basic Console I/O support Internal logic functions - declarations. */ #ifndef conio_mod_H #define conio_mod_H namespace Falcon { namespace Mod { int skeleton(); } } #endif /* end of conio_mod.h */ modules/native/conio/src/conio_st.cpp000066400000000000000000000022171176363201700202430ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: conio_st.cpp Basic Console I/O support Interface extension functions ------------------------------------------------------------------- Author: Unknown author Begin: Thu, 05 Sep 2008 20:12:14 +0200 ------------------------------------------------------------------- (C) Copyright 2008: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file String table - export internationalizable stings to both C++ and Falcon modules. */ #define FALCON_REALIZE_STRTAB #include "conio_st.h" /* end of conio_st.cpp */ modules/native/conio/src/conio_st.h000066400000000000000000000034601176363201700177110ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: conio_st.h Basic Console I/O support Interface extension functions ------------------------------------------------------------------- Author: Unknown author Begin: Thu, 05 Sep 2008 20:12:14 +0200 ------------------------------------------------------------------- (C) Copyright 2008: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file String table - export internationalizable stings to both C++ and Falcon modules. */ //WARNING: the missing of usual #ifndef/#define pair // is intentional! // See the contents of this header file for a deeper overall // explanation of the MODSTR system. #include // The first parameter is an unique identifier in your project that // will be bound to the correct entry in the module string table. // Falcon::VMachine::moduleString( conio_msg_1 ) will // return the associated string or the internationalized version. // FAL_STR( conio_msg_1 ) macro can be used in standard // functions as a shortcut. FAL_MODSTR( conio_msg_1, "Not yet implemented" ); FAL_MODSTR( conio_msg_2, "Error during initialization" ); //... add here your messages, and remove or configure the above one /* end of conio_st.h */ modules/native/conio/src/sys/000077500000000000000000000000001176363201700165365ustar00rootroot00000000000000modules/native/conio/src/sys/conio_srv_win.cpp000066400000000000000000000060421176363201700221220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: conio_srv_win.cpp Basic Console I/O support Interface extension functions ------------------------------------------------------------------- Author: Unknown author Begin: Thu, 05 Sep 2008 20:12:14 +0200 ------------------------------------------------------------------- (C) Copyright 2008: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include "conio_srv_win.h" namespace Falcon { namespace Srv { ConsoleSrv::ConsoleSrv(): Service( "CONSOLE" ), m_sys(0) { } // be sure to shutdown: ConsoleSrv::~ConsoleSrv() { shutdown(); } ConsoleSrv::error_type ConsoleSrv::init() { // already initialized if ( m_sys != 0 ) return e_dbl_init; // we must create our system-specific data // try to alloc a console; the result doesn't really matters AllocConsole(); // now, if opening the console fails THIS matters. HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); m_sys = new ConioSrvSys( hIn, hOut ); // we know we have in and out streams after an AllocConsole // but what about the buffer info? if( !GetConsoleScreenBufferInfo( m_sys->hConsoleOut, &m_sys->csbi ) ) return e_init; //todo; setup input/output masks, CTRL- handlers etc. // Great, we're on business; emulate a CLS to match other behaviors cls(); return e_none; } ConsoleSrv::error_type ConsoleSrv::cls() { if ( m_sys == 0 ) return e_not_init; COORD coordScreen = { 0, 0 }; // home for the cursor DWORD cCharsWritten; DWORD dwConSize; // Get the number of character cells in the current buffer. dwConSize = m_sys->csbi.dwSize.X * m_sys->csbi.dwSize.Y; // Fill the entire screen with blanks. if( !FillConsoleOutputCharacter( m_sys->hConsoleOut, (TCHAR) ' ', dwConSize, coordScreen, &cCharsWritten )) return e_write; // Get the current text attribute. if( !GetConsoleScreenBufferInfo( m_sys->hConsoleOut, &m_sys->csbi )) return e_write; // Set the buffer's attributes accordingly. if( !FillConsoleOutputAttribute( m_sys->hConsoleOut, m_sys->csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )) return e_write; // Put the cursor at its home coordinates. SetConsoleCursorPosition( m_sys->hConsoleOut, coordScreen ); return e_none; } void ConsoleSrv::shutdown() { cls(); delete m_sys; m_sys = 0; } } } /* end of conio_srv_win.cpp */ modules/native/conio/src/sys/conio_srv_win.h000066400000000000000000000025251176363201700215710ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: conio_srv_win.h Basic Console I/O support Interface extension functions ------------------------------------------------------------------- Author: Unknown author Begin: Thu, 05 Sep 2008 20:12:14 +0200 ------------------------------------------------------------------- (C) Copyright 2008: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef conio_SRV_WIN_H #define conio_SRV_WIN_H #include #include namespace Falcon { namespace Srv { class ConioSrvSys { public: HANDLE hConsoleIn; HANDLE hConsoleOut; CONSOLE_SCREEN_BUFFER_INFO csbi; ConioSrvSys( HANDLE hIn, HANDLE hOut ): hConsoleIn( hIn ), hConsoleOut( hOut ) {} }; } } #endif /* end of conio_srv.h */ modules/native/conio/src/version.h000066400000000000000000000005521176363201700175600ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: verion.h Conio module version informations ------------------------------------------------------------------- Author: Begin: */ #ifndef VERSION_H #define VERSION_H #define VERSION_MAJOR 0 #define VERSION_MINOR 0 #define VERSION_REVISION 0 #endif /* end of version.h */ modules/native/curl/000077500000000000000000000000001176363201700147675ustar00rootroot00000000000000modules/native/curl/CMakeLists.txt000066400000000000000000000010531176363201700175260ustar00rootroot00000000000000#################################################################### # FALCON - The Falcon Programming Language. # # CMake configuration file for curl #################################################################### cmake_minimum_required(VERSION 2.6) project(Falcon_curl) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) find_package(Falcon REQUIRED) # find_package(CURL 7.18.2 REQUIRED) ## process source directories # add other subdirectories that contain libraries on which the final module depends here.. add_subdirectory(src) modules/native/curl/ChangeLog000066400000000000000000000002101176363201700165320ustar00rootroot00000000000000Falcon-curl (1.1) ! Handle.setOptions was not in the list of the exported functions. Falcon-curl (1.0) * first official release. modules/native/curl/LICENSE000066400000000000000000000436071176363201700160060ustar00rootroot00000000000000 Falcon Programming Language License Version 1.1, March 2008 http://www.falconpl.org/?page_id=license_1_1 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. * "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 10 of this document. * "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. * "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. * "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. * "Source" form shall mean the preferred form for making modifications, including but not limited to software source code and example code. * "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. * "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). * "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. * "Embedding Works" shall mean any work, whether in Source or Object form, that links (or binds by name) to the interface of the Work and Derivative Works. * "Applications of the Work" shall mean any work, whether in Source or Object form, that is expressed through the grammar rules which are known by the Work and that require the Work to perform its execution. * "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." * "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, prepare Embedding Works, prepare Applications of the Work, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution of Work and Derivative Works. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and 2. You must cause any modified files to carry prominent notices stating that You changed the files; and 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 4. You must state in the Source Form and in the documentation of any Derivative Work the fact that such work is a derivation of the Work, and include a copy of the Work in its Source form or provide directions on how to obtain a copy of the Work in its Source form; and 5. The Derivative Works are distributed under the terms of this License, or under terms that do not cause infringement of this License. 5. Distribution of Embedding Works and Applications of the Work. You may produce and distribute any Embedding Work or Applications of the Work thereof in any medium, in Source or Object form, provided You meet the following conditions: 1. The Embedding Works and Applications of the Work are distributed under the term of this License, or the application of another License is explicitly stated in the documentation of the Embedding Works and Applications of the Work or included in the Source form of the Embedding Works and Applications of the Work; and 2. If the Source form of Embedding Works is not distributed nor made available to the Users in any form, the Embedding Works carry a prominent notice in their documentation, or when not applicable, in any place that the Users are exposed to, about the fact that the Work is embedded, along with a general statement about the task that the Work is performing in the Embedding Works; and 3. If the Source form of Applications of the Work is not distributed nor made available to the Users in any form, the Applications of the Work carry a prominent notice in their documentation, or when not applicable, in any place that the Users are exposed to, about the fact that the Work is used by the Application of the Work. 6. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement You may have executed with Licensor regarding such Contributions. 7. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 8. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 9. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 10. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of Your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS ============================================================================== APPENDIX: How to apply the Falcon Programming Language License to your work To apply the Falcon Programming Language License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ============================================================================== APPENDIX: License commentary Dear reader, First of all, thanks for considering using The Falcon Language for Your job, or to embed it into Your application, or to use it for any other reason. The Falcon Programming Language License explains what You are allowed to do with Falcon and what You are supposed (and allowed) not to do. Since the legal jargon may be cryptic, You are provided here with a little resume of the License. It is important that You read and accept the License, as this resume hasn't any legal valence, and is provided only for the reason to express clarifications and examples that cannot find their place in the formal document. The License grants You the rights to use the Source code of Falcon and its various components in any way; You can study it, You can copy it, You can modify it, and You can even sell it, but You can't change the license under which it is distributed: even if You sell it, You have to provide the customers with the source code of Falcon and to grant them the same rights You have on it. The License also grants Your copyrights and intellectual rights for any modification or addition You may want to apply to Falcon. In case or addition and modifications, the License binds You to provide the user with the information that the original program, that is Falcon, was modified by You and what are the changes or the additions that You applied. Also, even if You can freely distribute, or even charge a fee, for Your derived work, You MUST apply the Falcon Programming Language License to Your modifications, and distribute them under the same terms. In other words, Your modifications, if made public, must be provided or available in source code. The license also grants You the right to embed Falcon in any application. Here You are granted the right to pick the terms and licenses You prefer, and to distribute Your embedding application without providing its source code. Even if significant portions of Falcon are in line functions, and even if You decide to statically link Falcon in Your application, this doesn't make it a "Derivative Work" of it: Your Embedding application is free to embed Falcon "as is" as it wish, without any requirements in terms of source distribution. You can also modify Falcon for Your specific needs and THEN embed Your modified version; the modified version of Falcon is under Falcon License, and must be made available in source code (or be proposed as a Contribution to the Falcon Committee), but Your Embedding application can still be distributed under Your preferred terms. The License has only a requirement that is demanded on Your Embedding application: in case you don't distribute your embedding application isn't made available in source code, You HAVE to state somewhere (in a place that CAN possibly be seen by the average user) that You are embedding Falcon and the reason for that. In example, You may state "ProgramX uses the Falcon Programming Language to automatize the Gamma procedure". About the scripts, which are more or less what a scripting language is for, You are granted the right to apply the license You prefer. As Falcon is also a script "compiler", You may even retain from distributing the script sources, and apply closed-source license to Your script-based application or to the scripts that are embedded in Your embedding application. However, if You don't distribute the script sources, and you use Falcon or another application covered by the same license to run them, You are again required to state somewhere in Your documentation or where the user can read it that You are using "Falcon" (or the derivative work you used), and more or less why You are doing it. For a pure Falcon application, You can just state "This application is written in Falcon". In example, if You use Falcon to drive a web site, and You don't want Your site visitors to ever see Your scripts, You have to put somewhere a reading like "Powered with Falcon". What You cannot do is to claim that any thing You learnt from Falcon is Yours: especially, You are forbidden to patent any element that is found in Falcon. Another thing that You can't do is to sell a Falcon based product as if it were "completely" Yours, forgetting to cite, even in a very small reading, the fact that Falcon is in. Finally, a thing that the License prevents You from doing is to put the blame for failures on Falcon: the product is provided as-is, without any warranty. It's up to You to test if it is suitable to solve Your problems, and if a Falcon based application can be sold and granted as "working" to Your customers. If that application breaks, whether there's a problem with Falcon or not, You can't issue any claim on the Falcon contributors. Finally, notice that this version of falcon is released under dual license. You may chose either to use this FPLLv1.1 license or the standard GNU GPLv2. GNU GPLv2 is usually shipped with Your distribution and commentaries are widely available on the Net, so we won't discuss it here. Be kind on the Open Source Community: they have already made a lot for You even if You don't know them (and even if they don't know You). Best regards, Giancarlo Niccolai modules/native/curl/LICENSE_GPLv2000066400000000000000000000431411176363201700167510ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. modules/native/curl/README000066400000000000000000000026401176363201700156510ustar00rootroot00000000000000 Falcon libcurl binding Module Version 1.0 This module provides binding of the famous libCURL http/ftp interface for the Falcon programming language. LibCURL is available at http://curl.haxx.se/libcurl/ Falcon can be downloaded at http://www.falconpl.org The homesite for the development of this module is at http://www.falconpl.org/index.ftd?page_id=prjs&prj_id=curl BUILD INSTRUCTIONS ================== To build falcon-curl you need: - The CMake buid system (http://www.cmake.org/) - Development files of the Falcon programming language. - Development fiels of the libCURL project. - A compiler/IDE compatible with CMake. Once unpacked the files, create a "build" directory where you prefer. We'll suppose you create it below the topmost path of the source package. Then, enter the build directory and launch $ cmake -DCMAKE_BUILD_TYPE=Release Where is the relative or absolute path to the directory where this file you're reading resides. On POSIX systems, you may find useful to add the option -DCMAKE_BUILD_PREFIX to specify where the module should be finally installed. Please, refer to Falcon module generic build instructions for more sophisticated options. Refer to CMake online guide to specify the directories where libcurl may be installed. modules/native/curl/cmake/000077500000000000000000000000001176363201700160475ustar00rootroot00000000000000modules/native/curl/cmake/FindCURL.cmake000066400000000000000000000036451176363201700204270ustar00rootroot00000000000000 if(NOT DEFINED CURL_ROOT) if(NOT "$ENV{CURL_ROOT}" STREQUAL "") set(CURL_ROOT "$ENV{CURL_ROOT}") else() set(CURL_ROOT /usr) endif() endif() find_path(CURL_INCLUDE_DIR NAMES curl/curl.h PATHS ${CURL_ROOT}/include ) # Look for the library. find_library(CURL_LIBRARY NAMES curl # Windows MSVC prebuilts: curllib libcurl_imp curllib_static PATHS ${CURL_ROOT}/lib ${CURL_ROOT}/lib64 ${CURL_ROOT}/bin # dll ) if(CURL_INCLUDE_DIR AND EXISTS "${CURL_INCLUDE_DIR}/curl/curlver.h") file(READ "${CURL_INCLUDE_DIR}/curl/curlver.h" _curlver_h) string(REGEX REPLACE ".*#define LIBCURL_VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" CURL_VERSION_MAJOR "${_curlver_h}") string(REGEX REPLACE ".*#define LIBCURL_VERSION_MINOR[ \t]+([0-9]+).*" "\\1" CURL_VERSION_MINOR "${_curlver_h}") string(REGEX REPLACE ".*#define LIBCURL_VERSION_PATCH[ \t]+([0-9]+).*" "\\1" CURL_VERSION_PATCH "${_curlver_h}") set(CURL_VERSION "${CURL_VERSION_MAJOR}.${CURL_VERSION_MINOR}.${CURL_VERSION_PATCH}") set(CURL_VERSION_STRING "${CURL_VERSION}") endif() if(CURL_FIND_VERSION) set(CURL_VERSION_FOUND false) if(CURL_FIND_VERSION_EXACT) if(CURL_FIND_VERSION VERSION_EQUAL "${CURL_VERSION}") set(CURL_VERSION_FOUND true) else() message(STATUS "Didn't find the exact version ${CURL_FIND_VERSION}, but ${CURL_VERSION}") endif() else() if(CURL_FIND_VERSION VERSION_LESS "${CURL_VERSION}" OR CURL_FIND_VERSION VERSION_EQUAL "${CURL_VERSION}") set(CURL_VERSION_FOUND true) else() message(STATUS "Didn't find a sufficient version to meet ${CURL_FIND_VERSION}, but ${CURL_VERSION}") endif() endif() else() set(CURL_VERSION_FOUND true) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(CURL DEFAULT_MSG CURL_LIBRARY CURL_INCLUDE_DIR CURL_VERSION_FOUND) set(CURL_LIBRARIES ${CURL_LIBRARY}) set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR}) modules/native/curl/copyright000066400000000000000000000023531176363201700167250ustar00rootroot00000000000000 Falcon-curl module Falcon-dbus module is currently distributed under a dual licensing scheme GPL/FPLL. The GPL licensing scheme is provided to ensure compatibilty with Open Source and Free Software products. It's a relative strict license which is not particularly adequate for virtual machine based scripting languge interpreters and engines, but it can be chosen to develop pure GPL applications or to favor integration with already existing GPL embedding applications. The Falcon Programming Language License is specifically designed around the concept of an open source scripting language engine. It grants usability of the covered source in commercial applications, as it supports applying different and incompatible licenses to embedding and extension elements, while ensuring the openness of the core engine and of any work derived directly from it. Falcon 0.8.10 and followings are released under one of GPLv2 or FPLLv1.1, at your choice. The GPLv2 license can be found at: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The FPLLv1.1 license can be found at: http://www.falconpl.org?page_id=license_1_1 Copyright owners: (C) 2003-2008 Giancarlo Niccolai modules/native/curl/docs/000077500000000000000000000000001176363201700157175ustar00rootroot00000000000000modules/native/curl/docs/CMakeLists.txt000066400000000000000000000002351176363201700204570ustar00rootroot00000000000000file(GLOB_RECURSE scriptExtensions_SRCS "../src/*.cpp") faldoc_module_docs(curl faldoc.fd.in # input ${scriptExtensions_SRCS} # additional depenencies ) modules/native/curl/docs/faldoc.fd.in000066400000000000000000000020421176363201700200650ustar00rootroot00000000000000# # FALDOC - Falcon Documentation system # # Faldoc file for curl # ################################################ # Documentation generic data ################################################ Title = curl Author = Giancarlo Niccolai Version = 1.0 ################################################ # Faldoc Input settings ################################################ Input.basedir=@PROJECT_SOURCE_DIR@ Input.directory=src #you may place other input directory here Input.wildcard=*.cpp #we use only... Input.recursive=false # Other files may be specified here #Input.extra=main.fd ################################################ # Faldoc Output settings ################################################ Output.module=html ################################################ # Faldoc HTML Output settings # Each module can be self-configured through # Output.. configs ################################################ Output.html.directory=@output_dir@ Output.html.url=. Output.html.doctitle=curl module Function Reference modules/native/curl/src/000077500000000000000000000000001176363201700155565ustar00rootroot00000000000000modules/native/curl/src/CMakeLists.txt000066400000000000000000000024361176363201700203230ustar00rootroot00000000000000#################################################################### # FALCON - The Falcon Programming Language. # # CMake configuration file for curl #################################################################### # Creates the proper module file name from the project name. falcon_define_module( CURRENT_MODULE curl ) # Inclusion settings include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${Falcon_INCLUDE_DIRS} ${CURL_INCLUDE_DIR} ) # Enable this include if the project has a private include #include_directories( ${PROJECT_SOURCE_DIR}/include ) # Sources files the module is built from. set(SRC_FILES curl.cpp curl_ext.cpp curl_mod.cpp curl_st.cpp # add more source files starting here: ) # These are actually not needed by cmake to build. But if omitted they won't be # listed in the virtual file tree of Visual Studio. set(HDR_FILES curl_ext.h curl_mod.h curl_st.h version.h # add more header files starting here: ) # Builds and links from the source files add_library( ${CURRENT_MODULE} MODULE ${SRC_FILES} ${HDR_FILES} # optional but useful, see comment above. ) # If your module needs some lib more, use the followings: target_link_libraries( ${CURRENT_MODULE} falcon_engine ${CURL_LIBRARY} ) falcon_install_module( ${CURRENT_MODULE} ) modules/native/curl/src/curl.cpp000066400000000000000000000762541176363201700172450ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: curl_ext.cpp cURL library binding for Falcon Main module file, providing the module object to the Falcon engine. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 27 Nov 2009 16:31:15 +0100 ------------------------------------------------------------------- (C) Copyright 2009: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file Main module file, providing the module object to the Falcon engine. */ #ifdef _MSC_VER #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0403 #elif _WIN32_WINNT < 0x0403 #undef _WIN32_WINNT #define _WIN32_WINNT 0x0403 #endif #endif #include #include #include "curl_ext.h" #include "curl_mod.h" #include "curl_st.h" #include "version.h" //================================================== // Extension of Falcon module //================================================== class CurlModule: public Falcon::Module { static int init_count; public: CurlModule(); virtual ~CurlModule(); }; int CurlModule::init_count = 0; CurlModule::CurlModule(): Module() { if( init_count == 0 ) { curl_global_init( CURL_GLOBAL_ALL ); } ++init_count; } CurlModule::~CurlModule() { if( --init_count == 0 ) curl_global_cleanup(); } /*# @module curl cURL Http/Ftp library binding. This module provides a tight and complete integration with the @link "http://curl.haxx.se/libcurl/" libcurl library. Libcurl provides a complete set of RFC Internet protocol clients and allows a Falcon program to download remote files through simple commands. The curl Falcon module is structured in a way that allows to handle multiple downloads in a single thread, and even in a simple coroutine, simplifying by orders of magnitude the complexity of sophisticated client programs. @section code_status Status of this binding. Currently the @b curl module presents a minimal interface to the underlying libCURL. The library is actually served through Falcon-wise objects and structures. Some of the most advanced features in the library are still not bound, but you'll find everything you need to upload or download files, send POST http requests, get transfer information and basically manage multiplexed transfers. More advance binding is scheduled for the next version of this library, that will take advantage of a new binding engine in Falcon 0.9.8. @section load_request Importing the curl module. Since the names of the classes that are declared in this module are short and simple, it is advisable to use the @b import directive to store the module in its own namespace. For example: @code import from curl h = curl.Handle() @endcode @section enums Libcurl enumerations. The library wrapped by this module, libcurl, uses various sets of @b define directives to specify parameters and configure connection values. To reduce the complexity of this module, each set of enumerations is stored in a different Falcon enumerative class. For example, all the options starting with "CURLOPT_" are stored in the OPT enumeration. The option that sets the overall operation timeout for a given curl handle can be set through the OPT.TIMEOUT option (which corresponds to the CURLOPT_TIMEOUT define in the original C API of libcurl). */ FALCON_MODULE_DECL { #define FALCON_DECLARE_MODULE self // initialize the module Falcon::Module *self = new CurlModule(); self->name( "curl" ); self->language( "en_US" ); self->engineVersion( FALCON_VERSION_NUM ); self->version( VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION ); //============================================================ // Here declare the international string table implementation // #include "curl_st.h" //============================================================ // Here declare CURL - easy api // self->addExtFunc( "curl_version", Falcon::Ext::curl_version ); self->addExtFunc( "dload", Falcon::Ext::curl_dload )-> addParam("uri")->addParam("stream"); Falcon::Symbol *easy_class = self->addClass( "Handle", Falcon::Ext::Handle_init ) ->addParam( "uri" ); easy_class->setWKS(true); easy_class->getClassDef()->factory( &Falcon::Mod::CurlHandle::Factory ); self->addClassMethod( easy_class, "exec", Falcon::Ext::Handle_exec ); self->addClassMethod( easy_class, "setOutConsole", &Falcon::Ext::Handle_setOutConsole ); self->addClassMethod( easy_class, "setOutString", &Falcon::Ext::Handle_setOutString ); self->addClassMethod( easy_class, "setOutStream", &Falcon::Ext::Handle_setOutStream ).asSymbol() ->addParam( "stream" ); self->addClassMethod( easy_class, "setOutCallback", &Falcon::Ext::Handle_setOutCallback ).asSymbol() ->addParam( "cb" ); self->addClassMethod( easy_class, "setInStream", &Falcon::Ext::Handle_setInStream ).asSymbol() ->addParam( "stream" ); self->addClassMethod( easy_class, "setInCallback", &Falcon::Ext::Handle_setInCallback ).asSymbol() ->addParam( "cb" ); //self->addClassMethod( easy_class, "setOutMessage", Falcon::Ext::Handle_setOutMessage ).asSymbol() // ->addParam( "slot" ); self->addClassMethod( easy_class, "getData", &Falcon::Ext::Handle_getData ); self->addClassMethod( easy_class, "setOption", &Falcon::Ext::Handle_setOption ).asSymbol() ->addParam( "option" )->addParam( "data" ); self->addClassMethod( easy_class, "setOptions", &Falcon::Ext::Handle_setOptions ).asSymbol() ->addParam( "options" ); self->addClassMethod( easy_class, "postData", &Falcon::Ext::Handle_postData ).asSymbol() ->addParam( "data" ); self->addClassMethod( easy_class, "getInfo", &Falcon::Ext::Handle_getInfo ).asSymbol() ->addParam( "option" ); self->addClassMethod( easy_class, "cleanup", &Falcon::Ext::Handle_cleanup ); self->addClassProperty( easy_class, "data" ); //============================================================ // Here declare CURL - multi api // Falcon::Symbol *multy_class = self->addClass( "Multi", &Falcon::Ext::Multi_init ); multy_class->getClassDef()->factory( &Falcon::Mod::CurlMultiHandle::Factory ); self->addClassMethod( multy_class, "add", &Falcon::Ext::Multi_add ).asSymbol() ->addParam( "h" ); self->addClassMethod( multy_class, "remove", &Falcon::Ext::Multi_remove ).asSymbol() ->addParam( "h" ); self->addClassMethod( multy_class, "perform", &Falcon::Ext::Multi_perform ); //============================================================ // Enumeration class CURL // Falcon::Symbol *curle_class = self->addClass( "CURL" ); self->addClassProperty( curle_class, "READFUNC_ABORT" ).setInteger(CURL_READFUNC_ABORT).setReadOnly(true); self->addClassProperty( curle_class, "READFUNC_PAUSE" ).setInteger(CURL_READFUNC_PAUSE).setReadOnly(true); self->addClassProperty( curle_class, "WRITEFUNC_PAUSE" ).setInteger(CURL_WRITEFUNC_PAUSE).setReadOnly(true); //============================================================ // Enumeration class COPT // Falcon::Symbol *copt_class = self->addClass( "OPT" ); self->addClassProperty( copt_class, "VERBOSE" ).setInteger(CURLOPT_VERBOSE).setReadOnly(true); self->addClassProperty( copt_class, "HEADER" ).setInteger(CURLOPT_HEADER).setReadOnly(true); self->addClassProperty( copt_class, "NOPROGRESS" ).setInteger(CURLOPT_NOPROGRESS).setReadOnly(true); self->addClassProperty( copt_class, "HTTPPROXYTUNNEL" ).setInteger(CURLOPT_HTTPPROXYTUNNEL).setReadOnly(true); #if LIBCURL_VERSION_NUM >= 0x071904 self->addClassProperty( copt_class, "SOCKS5_GSSAPI_NEC" ).setInteger(CURLOPT_SOCKS5_GSSAPI_NEC).setReadOnly(true); #endif self->addClassProperty( copt_class, "TCP_NODELAY" ).setInteger(CURLOPT_TCP_NODELAY).setReadOnly(true); self->addClassProperty( copt_class, "AUTOREFERER" ).setInteger(CURLOPT_AUTOREFERER).setReadOnly(true); self->addClassProperty( copt_class, "FOLLOWLOCATION" ).setInteger(CURLOPT_FOLLOWLOCATION).setReadOnly(true); self->addClassProperty( copt_class, "UNRESTRICTED_AUTH" ).setInteger(CURLOPT_UNRESTRICTED_AUTH).setReadOnly(true); self->addClassProperty( copt_class, "PUT" ).setInteger(CURLOPT_PUT).setReadOnly(true); self->addClassProperty( copt_class, "POST" ).setInteger(CURLOPT_POST).setReadOnly(true); self->addClassProperty( copt_class, "COOKIESESSION" ).setInteger(CURLOPT_COOKIESESSION).setReadOnly(true); self->addClassProperty( copt_class, "HTTPGET" ).setInteger(CURLOPT_HTTPGET).setReadOnly(true); self->addClassProperty( copt_class, "IGNORE_CONTENT_LENGTH" ).setInteger(CURLOPT_IGNORE_CONTENT_LENGTH).setReadOnly(true); self->addClassProperty( copt_class, "DIRLISTONLY" ).setInteger(CURLOPT_DIRLISTONLY).setReadOnly(true); self->addClassProperty( copt_class, "APPEND" ).setInteger(CURLOPT_APPEND).setReadOnly(true); self->addClassProperty( copt_class, "FTP_USE_EPRT" ).setInteger(CURLOPT_FTP_USE_EPRT).setReadOnly(true); self->addClassProperty( copt_class, "FTP_USE_EPSV" ).setInteger(CURLOPT_FTP_USE_EPSV).setReadOnly(true); self->addClassProperty( copt_class, "FTP_CREATE_MISSING_DIRS" ).setInteger(CURLOPT_FTP_CREATE_MISSING_DIRS).setReadOnly(true); self->addClassProperty( copt_class, "FTP_SKIP_PASV_IP" ).setInteger(CURLOPT_FTP_SKIP_PASV_IP).setReadOnly(true); self->addClassProperty( copt_class, "TRANSFERTEXT" ).setInteger(CURLOPT_TRANSFERTEXT).setReadOnly(true); self->addClassProperty( copt_class, "PROXY_TRANSFER_MODE" ).setInteger(CURLOPT_PROXY_TRANSFER_MODE).setReadOnly(true); self->addClassProperty( copt_class, "CRLF" ).setInteger(CURLOPT_CRLF).setReadOnly(true); self->addClassProperty( copt_class, "FILETIME" ).setInteger(CURLOPT_FILETIME).setReadOnly(true); self->addClassProperty( copt_class, "NOBODY" ).setInteger(CURLOPT_NOBODY).setReadOnly(true); self->addClassProperty( copt_class, "UPLOAD" ).setInteger(CURLOPT_UPLOAD).setReadOnly(true); self->addClassProperty( copt_class, "FRESH_CONNECT" ).setInteger(CURLOPT_FRESH_CONNECT).setReadOnly(true); self->addClassProperty( copt_class, "FORBID_REUSE" ).setInteger(CURLOPT_FORBID_REUSE).setReadOnly(true); self->addClassProperty( copt_class, "CONNECT_ONLY" ).setInteger(CURLOPT_CONNECT_ONLY).setReadOnly(true); self->addClassProperty( copt_class, "SSLENGINE_DEFAULT" ).setInteger(CURLOPT_SSLENGINE_DEFAULT).setReadOnly(true); self->addClassProperty( copt_class, "SSL_VERIFYPEER" ).setInteger(CURLOPT_SSL_VERIFYPEER).setReadOnly(true); #if LIBCURL_VERSION_NUM >= 0x071901 self->addClassProperty( copt_class, "CERTINFO" ).setInteger(CURLOPT_CERTINFO).setReadOnly(true); #endif self->addClassProperty( copt_class, "SSL_VERIFYHOST" ).setInteger(CURLOPT_SSL_VERIFYHOST).setReadOnly(true); self->addClassProperty( copt_class, "SSL_SESSIONID_CACHE" ).setInteger(CURLOPT_SSL_SESSIONID_CACHE).setReadOnly(true); #if CURLOPT_PROTOCOLS self->addClassProperty( copt_class, "PROTOCOLS" ).setInteger(CURLOPT_PROTOCOLS).setReadOnly(true); self->addClassProperty( copt_class, "REDIR_PROTOCOLS" ).setInteger(CURLOPT_REDIR_PROTOCOLS).setReadOnly(true); #endif self->addClassProperty( copt_class, "PROXYPORT" ).setInteger(CURLOPT_PROXYPORT).setReadOnly(true); self->addClassProperty( copt_class, "PROXYTYPE" ).setInteger(CURLOPT_PROXYTYPE).setReadOnly(true); Falcon::Symbol *cproxy_class = self->addClass( "PROXY" ); self->addClassProperty( cproxy_class, "HTTP" ).setInteger(CURLPROXY_HTTP).setReadOnly(true); #if LIBCURL_VERSION_NUM >= 0x071904 self->addClassProperty( cproxy_class, "HTTP_1_0" ).setInteger(CURLPROXY_HTTP_1_0).setReadOnly(true); #endif self->addClassProperty( cproxy_class, "SOCKS4" ).setInteger(CURLPROXY_SOCKS4).setReadOnly(true); self->addClassProperty( cproxy_class, "SOCKS5" ).setInteger(CURLPROXY_SOCKS5).setReadOnly(true); self->addClassProperty( cproxy_class, "SOCKS4A" ).setInteger(CURLPROXY_SOCKS4A).setReadOnly(true); self->addClassProperty( copt_class, "LOCALPORT" ).setInteger(CURLOPT_LOCALPORT).setReadOnly(true); self->addClassProperty( copt_class, "LOCALPORTRANGE" ).setInteger(CURLOPT_LOCALPORTRANGE).setReadOnly(true); self->addClassProperty( copt_class, "DNS_CACHE_TIMEOUT" ).setInteger(CURLOPT_DNS_CACHE_TIMEOUT).setReadOnly(true); self->addClassProperty( copt_class, "DNS_USE_GLOBAL_CACHE" ).setInteger(CURLOPT_DNS_USE_GLOBAL_CACHE).setReadOnly(true); self->addClassProperty( copt_class, "BUFFERSIZE" ).setInteger(CURLOPT_BUFFERSIZE).setReadOnly(true); self->addClassProperty( copt_class, "PORT" ).setInteger(CURLOPT_PORT).setReadOnly(true); #if LIBCURL_VERSION_NUM >= 0x071900 self->addClassProperty( copt_class, "ADDRESS_SCOPE" ).setInteger(CURLOPT_ADDRESS_SCOPE).setReadOnly(true); #endif self->addClassProperty( copt_class, "NETRC" ).setInteger(CURLOPT_NETRC).setReadOnly(true); Falcon::Symbol *cnetrc_class = self->addClass( "NETRC" ); self->addClassProperty( cnetrc_class, "OPTIONAL" ).setInteger(CURL_NETRC_OPTIONAL).setReadOnly(true); self->addClassProperty( cnetrc_class, "IGNORED" ).setInteger(CURL_NETRC_IGNORED).setReadOnly(true); self->addClassProperty( copt_class, "HTTPAUTH" ).setInteger(CURLOPT_HTTPAUTH).setReadOnly(true); Falcon::Symbol *cauth_class = self->addClass( "AUTH" ); self->addClassProperty( cauth_class, "BASIC" ).setInteger(CURLAUTH_BASIC).setReadOnly(true); self->addClassProperty( cauth_class, "DIGEST" ).setInteger(CURLAUTH_DIGEST).setReadOnly(true); #if LIBCURL_VERSION_NUM >= 0x071903 self->addClassProperty( cauth_class, "DIGEST_IE" ).setInteger(CURLAUTH_DIGEST_IE).setReadOnly(true); #endif self->addClassProperty( cauth_class, "GSSNEGOTIATE" ).setInteger(CURLAUTH_GSSNEGOTIATE).setReadOnly(true); self->addClassProperty( cauth_class, "NTLM" ).setInteger(CURLAUTH_NTLM).setReadOnly(true); self->addClassProperty( cauth_class, "ANY" ).setInteger(CURLAUTH_ANY).setReadOnly(true); self->addClassProperty( cauth_class, "ANYSAFE" ).setInteger(CURLAUTH_ANYSAFE).setReadOnly(true); self->addClassProperty( copt_class, "PROXYAUTH" ).setInteger(CURLOPT_PROXYAUTH).setReadOnly(true); self->addClassProperty( copt_class, "MAXREDIRS" ).setInteger(CURLOPT_MAXREDIRS).setReadOnly(true); #if LIBCURL_VERSION_NUM >= 0x071901 self->addClassProperty( copt_class, "POSTREDIR" ).setInteger(CURLOPT_POSTREDIR).setReadOnly(true); #endif self->addClassProperty( copt_class, "HTTP_VERSION" ).setInteger(CURLOPT_HTTP_VERSION).setReadOnly(true); Falcon::Symbol *chttp_class = self->addClass( "HTTP" ); self->addClassProperty( chttp_class, "VERSION_NONE" ).setInteger(CURL_HTTP_VERSION_NONE).setReadOnly(true); self->addClassProperty( chttp_class, "VERSION_1_0" ).setInteger(CURL_HTTP_VERSION_1_0).setReadOnly(true); self->addClassProperty( chttp_class, "VERSION_1_1" ).setInteger(CURL_HTTP_VERSION_1_1).setReadOnly(true); self->addClassProperty( copt_class, "HTTP_CONTENT_DECODING" ).setInteger(CURLOPT_HTTP_CONTENT_DECODING).setReadOnly(true); self->addClassProperty( copt_class, "HTTP_TRANSFER_DECODING" ).setInteger(CURLOPT_HTTP_TRANSFER_DECODING).setReadOnly(true); #if LIBCURL_VERSION_NUM >= 0x071904 self->addClassProperty( copt_class, "TFTP_BLKSIZE" ).setInteger(CURLOPT_TFTP_BLKSIZE).setReadOnly(true); #endif self->addClassProperty( copt_class, "FTP_RESPONSE_TIMEOUT" ).setInteger(CURLOPT_FTP_RESPONSE_TIMEOUT).setReadOnly(true); self->addClassProperty( copt_class, "USE_SSL" ).setInteger(CURLOPT_USE_SSL).setReadOnly(true); Falcon::Symbol *cusessl_class = self->addClass( "USESSL" ); self->addClassProperty( cusessl_class, "NONE" ).setInteger(CURLUSESSL_NONE).setReadOnly(true); self->addClassProperty( cusessl_class, "TRY" ).setInteger(CURLUSESSL_TRY).setReadOnly(true); self->addClassProperty( cusessl_class, "CONTROL" ).setInteger(CURLUSESSL_CONTROL).setReadOnly(true); self->addClassProperty( cusessl_class, "ALL" ).setInteger(CURLUSESSL_ALL).setReadOnly(true); self->addClassProperty( copt_class, "FTPSSLAUTH" ).setInteger(CURLOPT_FTPSSLAUTH).setReadOnly(true); Falcon::Symbol *cftpauth_class = self->addClass( "FTPAUTH" ); self->addClassProperty( cftpauth_class, "DEFAULT" ).setInteger(CURLFTPAUTH_DEFAULT).setReadOnly(true); self->addClassProperty( cftpauth_class, "SSL" ).setInteger(CURLFTPAUTH_SSL).setReadOnly(true); self->addClassProperty( cftpauth_class, "TLS" ).setInteger(CURLFTPAUTH_TLS).setReadOnly(true); self->addClassProperty( copt_class, "FTP_SSL_CCC" ).setInteger(CURLOPT_FTP_SSL_CCC).setReadOnly(true); Falcon::Symbol *cftpssl_ccc_class = self->addClass( "FTPSSL_CCC" ); self->addClassProperty( cftpssl_ccc_class, "NONE" ).setInteger(CURLFTPSSL_CCC_NONE).setReadOnly(true); self->addClassProperty( cftpssl_ccc_class, "PASSIVE" ).setInteger(CURLFTPSSL_CCC_PASSIVE).setReadOnly(true); self->addClassProperty( cftpssl_ccc_class, "ACTIVE" ).setInteger(CURLFTPSSL_CCC_ACTIVE).setReadOnly(true); self->addClassProperty( copt_class, "FTP_FILEMETHOD" ).setInteger(CURLOPT_FTP_FILEMETHOD).setReadOnly(true); Falcon::Symbol *cftpmethod_class = self->addClass( "FTPMETHOD" ); self->addClassProperty( cftpmethod_class, "MULTICWD" ).setInteger(CURLFTPMETHOD_MULTICWD).setReadOnly(true); self->addClassProperty( cftpmethod_class, "NOCWD" ).setInteger(CURLFTPSSL_CCC_PASSIVE).setReadOnly(true); self->addClassProperty( cftpmethod_class, "SINGLECWD" ).setInteger(CURLFTPMETHOD_SINGLECWD).setReadOnly(true); self->addClassProperty( copt_class, "RESUME_FROM" ).setInteger(CURLOPT_RESUME_FROM).setReadOnly(true); self->addClassProperty( copt_class, "INFILESIZE" ).setInteger(CURLOPT_INFILESIZE).setReadOnly(true); self->addClassProperty( copt_class, "MAXFILESIZE" ).setInteger(CURLOPT_MAXFILESIZE).setReadOnly(true); self->addClassProperty( copt_class, "TIMEVALUE" ).setInteger(CURLOPT_TIMEVALUE).setReadOnly(true); self->addClassProperty( copt_class, "TIMEOUT" ).setInteger(CURLOPT_TIMEOUT).setReadOnly(true); self->addClassProperty( copt_class, "TIMEOUT_MS" ).setInteger(CURLOPT_TIMEOUT_MS).setReadOnly(true); self->addClassProperty( copt_class, "LOW_SPEED_LIMIT" ).setInteger(CURLOPT_LOW_SPEED_LIMIT).setReadOnly(true); self->addClassProperty( copt_class, "LOW_SPEED_TIME" ).setInteger(CURLOPT_LOW_SPEED_TIME).setReadOnly(true); self->addClassProperty( copt_class, "MAXCONNECTS" ).setInteger(CURLOPT_MAXCONNECTS).setReadOnly(true); self->addClassProperty( copt_class, "CONNECTTIMEOUT" ).setInteger(CURLOPT_CONNECTTIMEOUT).setReadOnly(true); self->addClassProperty( copt_class, "CONNECTTIMEOUT_MS" ).setInteger(CURLOPT_CONNECTTIMEOUT_MS).setReadOnly(true); self->addClassProperty( copt_class, "IPRESOLVE" ).setInteger(CURLOPT_IPRESOLVE).setReadOnly(true); Falcon::Symbol *cipresolve_class = self->addClass( "IPRESOLVE" ); self->addClassProperty( cipresolve_class, "WHATEVER" ).setInteger(CURL_IPRESOLVE_WHATEVER).setReadOnly(true); self->addClassProperty( cipresolve_class, "V4" ).setInteger(CURL_IPRESOLVE_V4).setReadOnly(true); self->addClassProperty( cipresolve_class, "V6" ).setInteger(CURL_IPRESOLVE_V6).setReadOnly(true); self->addClassProperty( copt_class, "SSLVERSION" ).setInteger(CURLOPT_SSLVERSION).setReadOnly(true); Falcon::Symbol *csslversion_class = self->addClass( "SSLVERSION" ); self->addClassProperty( csslversion_class, "DEFAULT" ).setInteger(CURL_SSLVERSION_DEFAULT).setReadOnly(true); self->addClassProperty( csslversion_class, "TLSv1" ).setInteger(CURL_SSLVERSION_TLSv1).setReadOnly(true); self->addClassProperty( csslversion_class, "SSLv2" ).setInteger(CURL_SSLVERSION_SSLv2).setReadOnly(true); self->addClassProperty( csslversion_class, "SSLv3" ).setInteger(CURL_SSLVERSION_SSLv3).setReadOnly(true); self->addClassProperty( copt_class, "SSH_AUTH_TYPES" ).setInteger(CURLOPT_SSH_AUTH_TYPES).setReadOnly(true); Falcon::Symbol *cssh_auth_class = self->addClass( "SSH_AUTH" ); self->addClassProperty( cssh_auth_class, "PUBLICKEY" ).setInteger(CURLSSH_AUTH_PUBLICKEY).setReadOnly(true); self->addClassProperty( cssh_auth_class, "PASSWORD" ).setInteger(CURLSSH_AUTH_PASSWORD).setReadOnly(true); self->addClassProperty( cssh_auth_class, "HOST" ).setInteger(CURLSSH_AUTH_HOST).setReadOnly(true); self->addClassProperty( cssh_auth_class, "KEYBOARD" ).setInteger(CURLSSH_AUTH_KEYBOARD).setReadOnly(true); self->addClassProperty( cssh_auth_class, "ANY" ).setInteger(CURLSSH_AUTH_ANY).setReadOnly(true); self->addClassProperty( copt_class, "NEW_FILE_PERMS" ).setInteger(CURLOPT_NEW_FILE_PERMS).setReadOnly(true); self->addClassProperty( copt_class, "NEW_DIRECTORY_PERMS" ).setInteger(CURLOPT_NEW_DIRECTORY_PERMS).setReadOnly(true); self->addClassProperty( copt_class, "RESUME_FROM_LARGE" ).setInteger(CURLOPT_RESUME_FROM_LARGE).setReadOnly(true); self->addClassProperty( copt_class, "INFILESIZE_LARGE" ).setInteger(CURLOPT_INFILESIZE_LARGE).setReadOnly(true); self->addClassProperty( copt_class, "MAXFILESIZE_LARGE" ).setInteger(CURLOPT_MAXFILESIZE_LARGE).setReadOnly(true); self->addClassProperty( copt_class, "MAX_SEND_SPEED_LARGE" ).setInteger(CURLOPT_MAX_SEND_SPEED_LARGE).setReadOnly(true); self->addClassProperty( copt_class, "MAX_RECV_SPEED_LARGE" ).setInteger(CURLOPT_MAX_RECV_SPEED_LARGE).setReadOnly(true); self->addClassProperty( copt_class, "URL" ).setInteger(CURLOPT_URL).setReadOnly(true); self->addClassProperty( copt_class, "PROXY" ).setInteger(CURLOPT_PROXY).setReadOnly(true); #if LIBCURL_VERSION_NUM >= 0x071904 self->addClassProperty( copt_class, "NOPROXY" ).setInteger(CURLOPT_NOPROXY).setReadOnly(true); self->addClassProperty( copt_class, "SOCKS5_GSSAPI_SERVICE" ).setInteger(CURLOPT_SOCKS5_GSSAPI_SERVICE).setReadOnly(true); #endif self->addClassProperty( copt_class, "INTERFACE" ).setInteger(CURLOPT_INTERFACE).setReadOnly(true); self->addClassProperty( copt_class, "NETRC_FILE" ).setInteger(CURLOPT_NETRC_FILE).setReadOnly(true); self->addClassProperty( copt_class, "USERPWD" ).setInteger(CURLOPT_USERPWD).setReadOnly(true); self->addClassProperty( copt_class, "PROXYUSERPWD" ).setInteger(CURLOPT_PROXYUSERPWD).setReadOnly(true); #if LIBCURL_VERSION_NUM >= 0x071901 self->addClassProperty( copt_class, "USERNAME" ).setInteger(CURLOPT_USERNAME).setReadOnly(true); self->addClassProperty( copt_class, "PASSWORD" ).setInteger(CURLOPT_PASSWORD).setReadOnly(true); self->addClassProperty( copt_class, "PROXYUSERNAME" ).setInteger(CURLOPT_PROXYUSERNAME).setReadOnly(true); self->addClassProperty( copt_class, "PROXYPASSWORD" ).setInteger(CURLOPT_PROXYPASSWORD).setReadOnly(true); #endif self->addClassProperty( copt_class, "ENCODING" ).setInteger(CURLOPT_ENCODING).setReadOnly(true); self->addClassProperty( copt_class, "REFERER" ).setInteger(CURLOPT_REFERER).setReadOnly(true); self->addClassProperty( copt_class, "USERAGENT" ).setInteger(CURLOPT_USERAGENT).setReadOnly(true); self->addClassProperty( copt_class, "COOKIE" ).setInteger(CURLOPT_COOKIE).setReadOnly(true); self->addClassProperty( copt_class, "COOKIEFILE" ).setInteger(CURLOPT_COOKIEFILE).setReadOnly(true); self->addClassProperty( copt_class, "COOKIEJAR" ).setInteger(CURLOPT_COOKIEJAR).setReadOnly(true); self->addClassProperty( copt_class, "COOKIELIST" ).setInteger(CURLOPT_COOKIELIST).setReadOnly(true); self->addClassProperty( copt_class, "FTPPORT" ).setInteger(CURLOPT_FTPPORT).setReadOnly(true); self->addClassProperty( copt_class, "FTP_ALTERNATIVE_TO_USER" ).setInteger(CURLOPT_FTP_ALTERNATIVE_TO_USER).setReadOnly(true); self->addClassProperty( copt_class, "FTP_ACCOUNT" ).setInteger(CURLOPT_FTP_ACCOUNT).setReadOnly(true); self->addClassProperty( copt_class, "RANGE" ).setInteger(CURLOPT_RANGE).setReadOnly(true); self->addClassProperty( copt_class, "CUSTOMREQUEST" ).setInteger(CURLOPT_CUSTOMREQUEST).setReadOnly(true); self->addClassProperty( copt_class, "SSLCERT" ).setInteger(CURLOPT_SSLCERT).setReadOnly(true); self->addClassProperty( copt_class, "SSLCERTTYPE" ).setInteger(CURLOPT_SSLCERTTYPE).setReadOnly(true); self->addClassProperty( copt_class, "SSLKEY" ).setInteger(CURLOPT_SSLKEY).setReadOnly(true); self->addClassProperty( copt_class, "SSLKEYTYPE" ).setInteger(CURLOPT_SSLKEYTYPE).setReadOnly(true); self->addClassProperty( copt_class, "KEYPASSWD" ).setInteger(CURLOPT_KEYPASSWD).setReadOnly(true); self->addClassProperty( copt_class, "SSLENGINE" ).setInteger(CURLOPT_SSLENGINE).setReadOnly(true); self->addClassProperty( copt_class, "CAINFO" ).setInteger(CURLOPT_CAINFO).setReadOnly(true); #if LIBCURL_VERSION_NUM >= 0x071900 self->addClassProperty( copt_class, "ISSUERCERT" ).setInteger(CURLOPT_ISSUERCERT).setReadOnly(true); self->addClassProperty( copt_class, "CRLFILE" ).setInteger(CURLOPT_CRLFILE).setReadOnly(true); #endif self->addClassProperty( copt_class, "CAPATH" ).setInteger(CURLOPT_CAPATH).setReadOnly(true); self->addClassProperty( copt_class, "RANDOM_FILE" ).setInteger(CURLOPT_RANDOM_FILE).setReadOnly(true); self->addClassProperty( copt_class, "EGDSOCKET" ).setInteger(CURLOPT_EGDSOCKET).setReadOnly(true); self->addClassProperty( copt_class, "SSL_CIPHER_LIST" ).setInteger(CURLOPT_SSL_CIPHER_LIST).setReadOnly(true); self->addClassProperty( copt_class, "KRBLEVEL" ).setInteger(CURLOPT_KRBLEVEL).setReadOnly(true); self->addClassProperty( copt_class, "SSH_HOST_PUBLIC_KEY_MD5" ).setInteger(CURLOPT_SSH_HOST_PUBLIC_KEY_MD5).setReadOnly(true); self->addClassProperty( copt_class, "SSH_PUBLIC_KEYFILE" ).setInteger(CURLOPT_SSH_PUBLIC_KEYFILE).setReadOnly(true); self->addClassProperty( copt_class, "SSH_PRIVATE_KEYFILE" ).setInteger(CURLOPT_SSH_PRIVATE_KEYFILE).setReadOnly(true); #ifdef CURLOPT_SSH_KNOWNHOSTS self->addClassProperty( copt_class, "SSH_KNOWNHOSTS" ).setInteger(CURLOPT_SSH_KNOWNHOSTS).setReadOnly(true); #endif // List options self->addClassProperty( copt_class, "HTTPHEADER" ).setInteger(CURLOPT_HTTPHEADER).setReadOnly(true); self->addClassProperty( copt_class, "HTTP200ALIASES" ).setInteger(CURLOPT_HTTP200ALIASES).setReadOnly(true); self->addClassProperty( copt_class, "QUOTE" ).setInteger(CURLOPT_QUOTE).setReadOnly(true); self->addClassProperty( copt_class, "POSTQUOTE" ).setInteger(CURLOPT_POSTQUOTE).setReadOnly(true); self->addClassProperty( copt_class, "PREQUOTE" ).setInteger(CURLOPT_PREQUOTE).setReadOnly(true); // To be implemented separately /* CURLOPT_HTTPPOST CURLOPT_SSH_KEYFUNCTION CURLOPT_SSH_KEYDATA CURLOPT_SHARE (?) CURLOPT_TELNETOPTIONS CURLOPT_TIMECONDITION */ //============================================================ // Enumeration class CURLINFO // Falcon::Symbol *curlinfo_class = self->addClass( "INFO" ); self->addClassProperty( curlinfo_class, "EFFECTIVE_URL" ).setInteger(CURLINFO_EFFECTIVE_URL).setReadOnly(true); self->addClassProperty( curlinfo_class, "RESPONSE_CODE" ).setInteger(CURLINFO_RESPONSE_CODE).setReadOnly(true); self->addClassProperty( curlinfo_class, "HTTP_CONNECTCODE" ).setInteger(CURLINFO_HTTP_CONNECTCODE).setReadOnly(true); self->addClassProperty( curlinfo_class, "FILETIME" ).setInteger(CURLINFO_FILETIME).setReadOnly(true); self->addClassProperty( curlinfo_class, "TOTAL_TIME" ).setInteger(CURLINFO_TOTAL_TIME).setReadOnly(true); self->addClassProperty( curlinfo_class, "NAMELOOKUP_TIME" ).setInteger(CURLINFO_NAMELOOKUP_TIME).setReadOnly(true); self->addClassProperty( curlinfo_class, "CONNECT_TIME" ).setInteger(CURLINFO_CONNECT_TIME).setReadOnly(true); #if LIBCURL_VERSION_NUM >= 0x071900 self->addClassProperty( curlinfo_class, "APPCONNECT_TIME" ).setInteger(CURLINFO_APPCONNECT_TIME).setReadOnly(true); #endif self->addClassProperty( curlinfo_class, "PRETRANSFER_TIME" ).setInteger(CURLINFO_PRETRANSFER_TIME).setReadOnly(true); self->addClassProperty( curlinfo_class, "STARTTRANSFER_TIME" ).setInteger(CURLINFO_STARTTRANSFER_TIME).setReadOnly(true); self->addClassProperty( curlinfo_class, "REDIRECT_TIME" ).setInteger(CURLINFO_REDIRECT_TIME).setReadOnly(true); self->addClassProperty( curlinfo_class, "REDIRECT_COUNT" ).setInteger(CURLINFO_REDIRECT_COUNT).setReadOnly(true); self->addClassProperty( curlinfo_class, "REDIRECT_URL" ).setInteger(CURLINFO_REDIRECT_URL).setReadOnly(true); self->addClassProperty( curlinfo_class, "SIZE_UPLOAD" ).setInteger(CURLINFO_SIZE_UPLOAD).setReadOnly(true); self->addClassProperty( curlinfo_class, "SIZE_DOWNLOAD" ).setInteger(CURLINFO_SIZE_DOWNLOAD).setReadOnly(true); self->addClassProperty( curlinfo_class, "SPEED_DOWNLOAD" ).setInteger(CURLINFO_SPEED_DOWNLOAD).setReadOnly(true); self->addClassProperty( curlinfo_class, "SPEED_UPLOAD" ).setInteger(CURLINFO_SPEED_UPLOAD).setReadOnly(true); self->addClassProperty( curlinfo_class, "HEADER_SIZE" ).setInteger(CURLINFO_HEADER_SIZE).setReadOnly(true); self->addClassProperty( curlinfo_class, "REQUEST_SIZE" ).setInteger(CURLINFO_REQUEST_SIZE).setReadOnly(true); self->addClassProperty( curlinfo_class, "SSL_VERIFYRESULT" ).setInteger(CURLINFO_SSL_VERIFYRESULT).setReadOnly(true); self->addClassProperty( curlinfo_class, "SSL_ENGINES" ).setInteger(CURLINFO_SSL_ENGINES).setReadOnly(true); self->addClassProperty( curlinfo_class, "CONTENT_LENGTH_DOWNLOAD" ).setInteger(CURLINFO_CONTENT_LENGTH_DOWNLOAD).setReadOnly(true); self->addClassProperty( curlinfo_class, "CONTENT_LENGTH_UPLOAD" ).setInteger(CURLINFO_CONTENT_LENGTH_UPLOAD).setReadOnly(true); self->addClassProperty( curlinfo_class, "CONTENT_TYPE" ).setInteger(CURLINFO_CONTENT_TYPE).setReadOnly(true); self->addClassProperty( curlinfo_class, "HTTPAUTH_AVAIL" ).setInteger(CURLINFO_HTTPAUTH_AVAIL).setReadOnly(true); self->addClassProperty( curlinfo_class, "PROXYAUTH_AVAIL" ).setInteger(CURLINFO_PROXYAUTH_AVAIL).setReadOnly(true); self->addClassProperty( curlinfo_class, "NUM_CONNECTS" ).setInteger(CURLINFO_NUM_CONNECTS).setReadOnly(true); #if LIBCURL_VERSION_NUM >= 0x071900 self->addClassProperty( curlinfo_class, "PRIMARY_IP" ).setInteger(CURLINFO_PRIMARY_IP).setReadOnly(true); #endif self->addClassProperty( curlinfo_class, "COOKIELIST" ).setInteger(CURLINFO_COOKIELIST).setReadOnly(true); self->addClassProperty( curlinfo_class, "FTP_ENTRY_PATH" ).setInteger(CURLINFO_FTP_ENTRY_PATH).setReadOnly(true); self->addClassProperty( curlinfo_class, "SSL_ENGINES" ).setInteger(CURLINFO_SSL_ENGINES).setReadOnly(true); #if LIBCURL_VERSION_NUM >= 0x071904 self->addClassProperty( curlinfo_class, "CONDITION_UNMET" ).setInteger(CURLINFO_CONDITION_UNMET).setReadOnly(true); #endif /** * Separately handled * CURLINFO_PRIVATE -> CURLOPT_PRIVATE * CURLINFO_LASTSOCKET -> socket? * CURLINFO_CERTINFO */ //============================================================ // CurlError class Falcon::Symbol *error_class = self->addExternalRef( "Error" ); // it's external Falcon::Symbol *ce_cls = self->addClass( "CurlError", Falcon::Ext::CurlError_init ); ce_cls->setWKS( true ); ce_cls->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); return self; } /* end of curl.cpp */ modules/native/curl/src/curl_ext.cpp000066400000000000000000001176521176363201700201230ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: curl_ext.cpp cURL library binding for Falcon Interface extension functions ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 27 Nov 2009 16:31:15 +0100 ------------------------------------------------------------------- (C) Copyright 2009: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file cURL library binding for Falcon Interface extension functions */ #ifndef _MSC_VER #include #else #include #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0403 #elif _WIN32_WINNT < 0x0403 #undef _WIN32_WINNT #define _WIN32_WINNT 0x0403 #endif #endif #include #include #include "curl_mod.h" #include "curl_ext.h" #include "curl_st.h" /*# @beginmodule curl */ namespace Falcon { namespace Ext { static void throw_error( int code, int line, const String& cd, const CURLcode retval ) { String error = String( curl_easy_strerror( retval ) ); throw new Mod::CurlError( ErrorParam( code, line ) .desc( cd ) .extra( error.A(" (").N(retval).A(")") ) ); } static void throw_merror( int code, int line, const String& cd, const CURLMcode retval ) { String error = String( curl_multi_strerror( retval ) ); throw new Mod::CurlError( ErrorParam( code, line ) .desc( cd ) .extra( error.A(" (").N(retval).A(")") ) ); } static void internal_curl_init( VMachine* vm, Mod::CurlHandle* h, Item* i_uri ) { CURL* curl = h->handle(); // we had a general init error from curl if ( curl == 0 ) { throw new Mod::CurlError( ErrorParam( FALCON_ERROR_CURL_INIT, __LINE__ ) .desc( FAL_STR( curl_err_init ) ) .extra( FAL_STR( curl_err_resources ) )); } curl_easy_setopt( curl, CURLOPT_NOPROGRESS, 1 ); curl_easy_setopt( curl, CURLOPT_NOSIGNAL, 1 ); // we need ourselves inside the object, so we can handle us back in multiple. curl_easy_setopt( curl, CURLOPT_PRIVATE, h ); // no parameter? -- nothing to do if( i_uri == 0 ) return; CURLcode retval; if( i_uri->isString() ) { //String enc = URI::URLEncode( *i_uri->asString() ); AutoCString curi( *i_uri->asString() ); retval = curl_easy_setopt( curl, CURLOPT_URL, curi.c_str() ); } else if( i_uri->isOfClass( "URI" ) ) { URI* uri = (URI*) i_uri->asObjectSafe()->getUserData(); AutoCString curi( uri->get(true) ); retval = curl_easy_setopt( curl, CURLOPT_URL, curi.c_str() ); } else { throw new ParamError( ErrorParam( e_inv_params, __LINE__) .extra( "[S|URI]" ) ); } if( retval != CURLE_OK ) { throw_error( FALCON_ERROR_CURL_INIT, __LINE__, FAL_STR( curl_err_init ), retval ); } } /*# @function curl_version @brief Returns the version of libcurl @return A string containing the description of the used version-. */ FALCON_FUNC curl_version( ::Falcon::VMachine *vm ) { vm->retval( new CoreString( ::curl_version() ) ); } /*# @class Handle @brief Stores an handle for a CURL (easy) connection. @optparam uri A string or an URI to be used to initialize the connection. The following is a simple complete program that retrieves the main page of the Falcon programming language site: @code import from curl try h = curl.Handle( "http://www.falconpl.org" ) h.setOutString() h.exec() > "Complete data transfer:", h.getData() catch curl.CurlError in e > "ERROR: ", e end @endcode @prop data User available data. Store any data that the client application wants to be related to this handle in this property. It is also possible to derive a child from this class and store more complete behavior there. */ FALCON_FUNC Handle_init( ::Falcon::VMachine *vm ) { // setup our options Mod::CurlHandle* h = dyncast< Mod::CurlHandle* >( vm->self().asObject() ); Item* i_uri = vm->param(0); internal_curl_init( vm, h, i_uri ); } /*# @method exec Handle @brief Transfers data from the remote. @return self (to put this call in a chain) @raise CurlError on error This function performs the whole transfer towards the target that has been selected via @a Handle.setOutString, @a Handle.setOutStream, @a Handle.setOutConsole or @a Handle.setOutCallback routines. The call is blocking and normally it cannot be interrupted; however, a timeout can be set through @note Internally, this method performs a curl_easy_perform call on the inner */ FALCON_FUNC Handle_exec( ::Falcon::VMachine *vm ) { // setup our options Mod::CurlHandle* h = dyncast< Mod::CurlHandle* >( vm->self().asObject() ); CURL* curl = h->handle(); if ( curl == 0 ) throw new Mod::CurlError( ErrorParam( FALCON_ERROR_CURL_PM, __LINE__ ) .desc( FAL_STR( curl_err_pm ) ) ); CURLcode retval = curl_easy_perform(curl); if( retval != CURLE_OK ) { throw_error( FALCON_ERROR_CURL_EXEC, __LINE__, FAL_STR( curl_err_exec ), retval ); } vm->retval( vm->self() ); } /*# @method setOutConsole Handle @brief Asks for subsequent transfer(s) to be sent to process console (raw stdout). @return self (to put this call in a chain) This is the default at object creation. */ FALCON_FUNC Handle_setOutConsole( ::Falcon::VMachine *vm ) { // setup our options Mod::CurlHandle* h = dyncast< Mod::CurlHandle* >( vm->self().asObject() ); if ( h->handle() == 0 ) throw new Mod::CurlError( ErrorParam( FALCON_ERROR_CURL_PM, __LINE__ ) .desc( FAL_STR( curl_err_pm ) ) ); h->setOnDataStdOut(); } /*# @method setOutString Handle @brief Asks for subsequent transfer(s) to be stored in a temporary string. @return self (to put this call in a chain) After @a Handle.exec has been called, the data will be available in a string that can be retrieved via the @a Handle.getData method. */ FALCON_FUNC Handle_setOutString( ::Falcon::VMachine *vm ) { // setup our options Mod::CurlHandle* h = dyncast< Mod::CurlHandle* >( vm->self().asObject() ); if ( h->handle() == 0 ) throw new Mod::CurlError( ErrorParam( FALCON_ERROR_CURL_PM, __LINE__ ) .desc( FAL_STR( curl_err_pm ) ) ); h->setOnDataGetString(); vm->retval( vm->self() ); } /*# @method setOutStream Handle @brief Asks for subsequent transfer(s) to be stored in a given stream. @param stream The stream to be used. @return self (to put this call in a chain) When called, @a Handle.exec will store incoming data in this stream object via binary Stream.write operations. */ FALCON_FUNC Handle_setOutStream( ::Falcon::VMachine *vm ) { // setup our options Mod::CurlHandle* h = dyncast< Mod::CurlHandle* >( vm->self().asObject() ); if ( h->handle() == 0 ) throw new Mod::CurlError( ErrorParam( FALCON_ERROR_CURL_PM, __LINE__ ) .desc( FAL_STR( curl_err_pm ) ) ); Item* i_stream = vm->param(0); if ( i_stream == 0 || ! i_stream->isOfClass("Stream") ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__) .extra( "Stream" ) ); } h->setOnDataStream( (Stream*) i_stream->asObjectSafe()->getUserData() ); vm->retval( vm->self() ); } /*# @method setOutCallback Handle @brief Asks for subsequent transfer(s) to be handled to a given callback. @param cb A callback item that will receive incoming data as a binary string. @return self (to put this call in a chain) This method instructs this handle to call a given callback when data is received. When called, @a Handle.exec will repeatedly call the @b cb item providing a single string as a parameter. The string is not encoded in any format, and could be considered filled with binary data. */ FALCON_FUNC Handle_setOutCallback( ::Falcon::VMachine *vm ) { // setup our options Mod::CurlHandle* h = dyncast< Mod::CurlHandle* >( vm->self().asObject() ); if ( h->handle() == 0 ) throw new Mod::CurlError( ErrorParam( FALCON_ERROR_CURL_PM, __LINE__ ) .desc( FAL_STR( curl_err_pm ) ) ); Item* i_cb = vm->param(0); if ( i_cb == 0 || ! i_cb->isCallable() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__) .extra( "C" ) ); } h->setOnDataCallback( *i_cb ); vm->retval( vm->self() ); } // not yet active /* @method setOutMessage Handle @brief Asks for subsequent transfer(s) to be handled as a message broadcast. @param msg A string representing a message or a VMSlot. @return self (to put this call in a chain) This method instructs this handle to perform message broadcast when data is received. When called, @a Handle.exec will repeatedly broadcast @msg sending two parameters: itself (this Handle object) and the received data, as a binary string. The string is not encoded in any format, and could be considered filled with binary data. vm->retval( vm->self() ); */ /* FALCON_FUNC Handle_setOutMessage( ::Falcon::VMachine *vm ) { // setup our options Mod::CurlHandle* h = dyncast< Mod::CurlHandle* >( vm->self().asObject() ); if ( h->handle() == 0 ) throw new Mod::CurlError( ErrorParam( FALCON_ERROR_CURL_PM, __LINE__ ) .desc( FAL_STR( curl_err_pm ) ) ); Item* i_msg = vm->param(0); if ( i_msg == 0 || (! i_msg->isString() && ! i_msg->isOfClass("VMSlot") ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__) .extra( "S|VMSlot" ) ); } if( i_msg->isString() ) { h->setOnDataMessage( *i_msg->asString() ); } else { CoreSlot* cs = (CoreSlot*) i_msg->asObjectSafe()->getUserData(); h->setOnDataMessage( cs->name() ); } } */ /*# @method cleanup Handle @brief Close a connection and destroys all associated data. After this call, the handle is not usable anymore. This is executed also automatically at garbage collection, but the user may be interested in clearing the data as soon as possible. */ FALCON_FUNC Handle_cleanup( ::Falcon::VMachine *vm ) { // setup our options Mod::CurlHandle* h = dyncast< Mod::CurlHandle* >( vm->self().asObject() ); if ( h->handle() == 0 ) throw new Mod::CurlError( ErrorParam( FALCON_ERROR_CURL_PM, __LINE__ ) .desc( FAL_STR( curl_err_pm ) ) ); h->cleanup(); } /*# @method setInCallback Handle @brief Asks for subsequent uploads to be handled to a given callback. @param cb A callback item that will write data in an incoming MemBuf @return self (to put this call in a chain) This method instructs this handle to call a given callback when new data can be uploaded to the remote side. The function receives a MemBuf that must be filled with data. It should return the amount of data really written to the membuf. It can also return CURL.WRITE_PAUSE to ask for @a Handle.exec to return with a pause status. The callback must return 0 when it has no more data to transfer. */ FALCON_FUNC Handle_setInCallback( ::Falcon::VMachine *vm ) { // setup our options Mod::CurlHandle* h = dyncast< Mod::CurlHandle* >( vm->self().asObject() ); if ( h->handle() == 0 ) throw new Mod::CurlError( ErrorParam( FALCON_ERROR_CURL_PM, __LINE__ ) .desc( FAL_STR( curl_err_pm ) ) ); Item* i_cb = vm->param(0); if ( i_cb == 0 || ! i_cb->isCallable() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__) .extra( "C" ) ); } h->setReadCallback( *i_cb ); vm->retval( vm->self() ); } /*# @method setInStream Handle @brief Asks for subsequent upload(s) to read data from the given stream. @param stream The stream to be used. @return self (to put this call in a chain) When called, @a Handle.exec will read data to be uploaded from this stream. */ FALCON_FUNC Handle_setInStream( ::Falcon::VMachine *vm ) { // setup our options Mod::CurlHandle* h = dyncast< Mod::CurlHandle* >( vm->self().asObject() ); if ( h->handle() == 0 ) throw new Mod::CurlError( ErrorParam( FALCON_ERROR_CURL_PM, __LINE__ ) .desc( FAL_STR( curl_err_pm ) ) ); Item* i_stream = vm->param(0); if ( i_stream == 0 || ! i_stream->isOfClass("Stream") ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__) .extra( "Stream" ) ); } h->setReadStream( (Stream*) i_stream->asObjectSafe()->getUserData() ); vm->retval( vm->self() ); } /*# @method getData Handle @brief Gets data temporarily stored in a string during a transfer. @return A string containing data that has been transfered. This function returns the data received in the meanwhile. This data is captured when the @a Handle.setOutString option has been set. */ FALCON_FUNC Handle_getData( ::Falcon::VMachine *vm ) { // setup our options Mod::CurlHandle* h = dyncast< Mod::CurlHandle* >( vm->self().asObject() ); CoreString* s = h->getData(); if( s != 0 ) { vm->retval( s ); } } static void internal_setOpt( VMachine* vm, Mod::CurlHandle* h, CURLoption iOpt, Item* i_data ) { CURLcode ret; CURL* curl = h->handle(); switch( iOpt ) { case CURLOPT_VERBOSE: case CURLOPT_HEADER: case CURLOPT_NOPROGRESS: case CURLOPT_HTTPPROXYTUNNEL: #if LIBCURL_VERSION_NUM >= 0x071904 case CURLOPT_SOCKS5_GSSAPI_NEC: #endif case CURLOPT_TCP_NODELAY: case CURLOPT_AUTOREFERER: case CURLOPT_FOLLOWLOCATION: case CURLOPT_UNRESTRICTED_AUTH: case CURLOPT_PUT: case CURLOPT_POST: case CURLOPT_COOKIESESSION: case CURLOPT_HTTPGET: case CURLOPT_IGNORE_CONTENT_LENGTH: case CURLOPT_DIRLISTONLY: case CURLOPT_APPEND: case CURLOPT_FTP_USE_EPRT: case CURLOPT_FTP_USE_EPSV: case CURLOPT_FTP_CREATE_MISSING_DIRS: case CURLOPT_FTP_SKIP_PASV_IP: case CURLOPT_TRANSFERTEXT: case CURLOPT_PROXY_TRANSFER_MODE: case CURLOPT_CRLF: case CURLOPT_FILETIME: case CURLOPT_NOBODY: case CURLOPT_UPLOAD: case CURLOPT_FRESH_CONNECT: case CURLOPT_FORBID_REUSE: case CURLOPT_CONNECT_ONLY: case CURLOPT_SSLENGINE_DEFAULT: case CURLOPT_SSL_VERIFYPEER: #if LIBCURL_VERSION_NUM >= 0x071901 case CURLOPT_CERTINFO: #endif case CURLOPT_SSL_VERIFYHOST: case CURLOPT_SSL_SESSIONID_CACHE: { long bVal = i_data->isTrue() ? 1 : 0; ret = curl_easy_setopt( curl, iOpt, bVal ); } break; #ifdef CURLOPT_PROTOCOLS case CURLOPT_PROTOCOLS: case CURLOPT_REDIR_PROTOCOLS: #endif case CURLOPT_PROXYPORT: case CURLOPT_PROXYTYPE: case CURLOPT_LOCALPORT: case CURLOPT_LOCALPORTRANGE: case CURLOPT_DNS_CACHE_TIMEOUT: case CURLOPT_DNS_USE_GLOBAL_CACHE: case CURLOPT_BUFFERSIZE: case CURLOPT_PORT: #if LIBCURL_VERSION_NUM >= 0x071900 case CURLOPT_ADDRESS_SCOPE: #endif case CURLOPT_NETRC: case CURLOPT_HTTPAUTH: case CURLOPT_PROXYAUTH: case CURLOPT_MAXREDIRS: #if LIBCURL_VERSION_NUM >= 0x071901 case CURLOPT_POSTREDIR: #endif case CURLOPT_HTTP_VERSION: case CURLOPT_HTTP_CONTENT_DECODING: case CURLOPT_HTTP_TRANSFER_DECODING: #if LIBCURL_VERSION_NUM >= 0x071904 case CURLOPT_TFTP_BLKSIZE: #endif case CURLOPT_FTP_RESPONSE_TIMEOUT: case CURLOPT_USE_SSL: case CURLOPT_FTPSSLAUTH: case CURLOPT_FTP_SSL_CCC: case CURLOPT_FTP_FILEMETHOD: case CURLOPT_RESUME_FROM: case CURLOPT_INFILESIZE: case CURLOPT_MAXFILESIZE: case CURLOPT_TIMECONDITION: case CURLOPT_TIMEVALUE: case CURLOPT_TIMEOUT: case CURLOPT_TIMEOUT_MS: case CURLOPT_LOW_SPEED_LIMIT: case CURLOPT_LOW_SPEED_TIME: case CURLOPT_MAXCONNECTS: case CURLOPT_CONNECTTIMEOUT: case CURLOPT_CONNECTTIMEOUT_MS: case CURLOPT_IPRESOLVE: case CURLOPT_SSLVERSION: case CURLOPT_SSH_AUTH_TYPES: case CURLOPT_NEW_FILE_PERMS: case CURLOPT_NEW_DIRECTORY_PERMS: { long lVal = (long) i_data->asInteger(); ret = curl_easy_setopt( curl, iOpt, lVal ); } break; case CURLOPT_RESUME_FROM_LARGE: case CURLOPT_INFILESIZE_LARGE: case CURLOPT_MAXFILESIZE_LARGE: case CURLOPT_MAX_SEND_SPEED_LARGE: case CURLOPT_MAX_RECV_SPEED_LARGE: { if( ! i_data->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( FAL_STR( curl_err_setopt ) )); } curl_off_t offset = (curl_off_t) i_data->forceInteger(); ret = curl_easy_setopt( curl, iOpt, offset ); } break; case CURLOPT_URL: case CURLOPT_PROXY: #if LIBCURL_VERSION_NUM >= 0x071904 case CURLOPT_NOPROXY: case CURLOPT_SOCKS5_GSSAPI_SERVICE: #endif case CURLOPT_INTERFACE: case CURLOPT_NETRC_FILE: case CURLOPT_USERPWD: case CURLOPT_PROXYUSERPWD: #if LIBCURL_VERSION_NUM >= 0x071901 case CURLOPT_USERNAME: case CURLOPT_PASSWORD: case CURLOPT_PROXYUSERNAME: case CURLOPT_PROXYPASSWORD: #endif case CURLOPT_ENCODING: case CURLOPT_REFERER: case CURLOPT_USERAGENT: case CURLOPT_COOKIE: case CURLOPT_COOKIEFILE: case CURLOPT_COOKIEJAR: case CURLOPT_COOKIELIST: case CURLOPT_FTPPORT: case CURLOPT_FTP_ALTERNATIVE_TO_USER: case CURLOPT_FTP_ACCOUNT: case CURLOPT_RANGE: case CURLOPT_CUSTOMREQUEST: case CURLOPT_SSLCERT: case CURLOPT_SSLCERTTYPE: case CURLOPT_SSLKEY: case CURLOPT_SSLKEYTYPE: case CURLOPT_KEYPASSWD: case CURLOPT_SSLENGINE: case CURLOPT_CAINFO: #if LIBCURL_VERSION_NUM >= 0x071900 case CURLOPT_ISSUERCERT: case CURLOPT_CRLFILE: #endif case CURLOPT_CAPATH: case CURLOPT_RANDOM_FILE: case CURLOPT_EGDSOCKET: case CURLOPT_SSL_CIPHER_LIST: case CURLOPT_KRBLEVEL: case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: case CURLOPT_SSH_PUBLIC_KEYFILE: case CURLOPT_SSH_PRIVATE_KEYFILE: #ifdef CURLOPT_SSH_KNOWNHOSTS case CURLOPT_SSH_KNOWNHOSTS: #endif { if( ! i_data->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( FAL_STR( curl_err_setopt ) )); } AutoCString cstr( *i_data ); ret = curl_easy_setopt( curl, iOpt, cstr.c_str() ); } break; case CURLOPT_HTTPHEADER: case CURLOPT_HTTP200ALIASES: case CURLOPT_QUOTE: case CURLOPT_POSTQUOTE: case CURLOPT_PREQUOTE: { if( ! i_data->isArray() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( FAL_STR( curl_err_setopt ) )); } CoreArray* items = i_data->asArray(); struct curl_slist *slist = h->slistFromArray( items ); if ( slist == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( FAL_STR( curl_err_setopt ) )); } ret = curl_easy_setopt( curl, iOpt, slist ); } break; default: throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( FAL_STR( curl_err_unkopt ) )); } if( ret != CURLE_OK ) { throw_error( FALCON_ERROR_CURL_SETOPT, __LINE__, FAL_STR( curl_err_setopt ), ret ); } } /*# @method setOption Handle @brief Sets a cURL option for this specific handle. @param option The option to be set (an enumeration). @param data The value to be set. @return self (to put this call in a chain) Depending on the option, @b data must be a boolean, a number or a string. Some options, as CURLOPT.HTTPHEADER, require the data to be an array of strings. @note CURLOPT.POSTFIELDS family options are not supported directly by this function; use @a Handle.postData function instead. @note Callback related options are not supported by this function. Specific functions are provided to setup automated or manual callback facilities (see the various set* methods in this class). */ FALCON_FUNC Handle_setOption( ::Falcon::VMachine *vm ) { Item* i_option = vm->param(0); Item* i_data = vm->param(1); if ( i_option == 0 || ! i_option->isInteger() || i_data == 0 ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "I,X" ) ); } // setup our options Mod::CurlHandle* h = dyncast< Mod::CurlHandle* >( vm->self().asObject() ); if ( h->handle() == 0 ) throw new Mod::CurlError( ErrorParam( FALCON_ERROR_CURL_PM, __LINE__ ) .desc( FAL_STR( curl_err_pm ) ) ); CURLoption iOpt = (CURLoption) i_option->asInteger(); internal_setOpt( vm, h, iOpt, i_data ); vm->retval( vm->self() ); } /*# @method setOptions Handle @brief Sets a list of cURL option for this specific handle. @param opts A dictionary of options, where each key is an option number, and its value is the option value. @return self (to put this call in a chain) */ FALCON_FUNC Handle_setOptions( ::Falcon::VMachine *vm ) { Item* i_opts = vm->param(0); if ( i_opts == 0 || ! i_opts->isDict() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "D" ) ); } // setup our options Mod::CurlHandle* h = dyncast< Mod::CurlHandle* >( vm->self().asObject() ); if ( h->handle() == 0 ) throw new Mod::CurlError( ErrorParam( FALCON_ERROR_CURL_PM, __LINE__ ) .desc( FAL_STR( curl_err_pm ) ) ); Iterator iter( &i_opts->asDict()->items() ); while( iter.hasCurrent() ) { Item& opt = iter.getCurrentKey(); if( ! opt.isInteger() ) { throw new ParamError( ErrorParam( e_param_type, __LINE__ ) .extra( "D[I=>X]" ) ); } internal_setOpt( vm, h, (CURLoption) opt.asInteger(), &iter.getCurrent() ); iter.next(); } vm->retval( vm->self() ); } /*# @method postData Handle @brief Sets data to be sent in one unique POST request. @param data A string to be sent as post data. This function substitutes the CURLOPT_POSTFIELDS family of options of the C level libcurl. It allows to set a string that will be sent in HTTP post request. All the other setOut* methods can be used for the same purpose to take data from streams, callback or even strings, but all the other methods will transfer data in chunks, and require to set the HTTP header transfer-encoding as "chunked" via the CURL.HTTP_HEADERS option, and to use HTTP/1.1 protocol. Using this method, the postData will be sent as an unique chunk, so it doesn't require extra header setting and works with any HTTP protocol. @note The data will be sent not encoded in any particular format (it will be binary-transmitted as it is in the string memory). If the remote server expects a particular encoding (usually, UTF-8), appropriate transocoding functions must be used in advance. */ FALCON_FUNC Handle_postData( ::Falcon::VMachine *vm ) { Item* i_data = vm->param(0); if ( i_data == 0 || ! i_data->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S" ) ); } // setup our options Mod::CurlHandle* h = dyncast< Mod::CurlHandle* >( vm->self().asObject() ); if ( h->handle() == 0 ) throw new Mod::CurlError( ErrorParam( FALCON_ERROR_CURL_PM, __LINE__ ) .desc( FAL_STR( curl_err_pm ) ) ); h->postData( *i_data->asString() ); } /*# @method getInfo Handle @brief Returns informations about the status of this handle. @param option The specific information to be read. @return The value associated with the required information, or nil if the option is not available. This method returns one of the informations that can be retrieved from this handle. The option value are stored in the INFO enumeration, and they correspond to the values in the CURLINFO_* set of defines of the libcurl SDK, associated with the curl_easy_getinfo function. The type of the returned value depends of the type of information required; in general it may be a number or a string. Possible values for @b option are - INFO.EFFECTIVE_URL - the last used effective URL. - INFO.RESPONSE_CODE - the last received HTTP or FTP code. This will be zero if no server response code has been received. Note that a proxy's CONNECT response should be read with INFO.HTTP_CONNECTCODE and not this. - INFO.HTTP_CONNECTCODE - the last received proxy response code to a CONNECT request. - INFO.FILETIME - time of the retrieved document (as a Falcon TimeStamp, in GMT). If you get @b nil, it can be because of many reasons (unknown, the server hides it or the server doesn't support the command that tells document time etc) and the time of the document is unknown. Note that you must tell the server to collect this information before the transfer is made, by using the OPT.FILETIME option to @a Handle.setOption or you will unconditionally get a @b nil back. - INFO.TOTAL_TIME - the total time in seconds and fractions for the previous transfer, including name resolving, TCP connect etc. - INFO.NAMELOOKUP_TIME - time, in seconds and fractions, it took from the start until the name resolving was completed. - INFO.CONNECT_TIME - time, in seconds and fraction, it took from the start until the connect to the remote host (or proxy) was completed. - INFO.APPCONNECT_TIME - time, in seconds and fractions, it took from the start until the SSL/SSH connect/handshake to the remote host was completed. This time is most often very near to the PRETRANSFER time, except for cases such as HTTP pippelining where the pretransfer time can be delayed due to waits in line for the pipeline and more. (Added in 7.19.0) - INFO.PRETRANSFER_TIME - time, in seconds and fractions, it took from the start until the file transfer is just about to begin. This includes all pre-transfer commands and negotiations that are specific to the particular protocol(s) involved. - INFO.STARTTRANSFER_TIME - time, in seconds and fractions, it took from the start until the first byte is just about to be transferred. This includes CURLINFO_PRETRANSFER_TIME and also the time the server needs to calculate the result. - INFO.REDIRECT_TIME - total time, in seconds and fractions, it took for all redirection steps include name lookup, connect, pretransfer and transfer before final transaction was started. CURLINFO_REDIRECT_TIME contains the complete execution time for multiple redirections. (Added in 7.9.7) - INFO.REDIRECT_COUNT - total number of redirections that were actually followed. - INFO.REDIRECT_URL - the URL a redirect would take you to if you would enable OPT.FOLLOWLOCATION. This can come very handy if you think using the built-in libcurl redirect logic isn't good enough for you but you would still prefer to avoid implementing all the magic of figuring out the new URL. - INFO.SIZE_UPLOAD - total amount of bytes that were uploaded. - INFO.SIZE_DOWNLOAD - total amount of bytes that were downloaded. The amount is only for the latest transfer and will be reset again for each new transfer. - INFO.SPEED_DOWNLOAD - average download speed that curl measured for the complete download. Measured in bytes/second. - INFO.SPEED_UPLOAD - average upload speed that curl measured for the complete upload. Measured in bytes/second. - INFO.HEADER_SIZE - total size of all the headers received. Measured in number of bytes. - INFO.REQUEST_SIZE - total size of the issued requests. This is so far only for HTTP requests. Note that this may be more than one request if OPT.FOLLOWLOCATION is true. - INFO.SSL_VERIFYRESULT - the certification verification that was requested (using the OPT.SSL_VERIFYPEER option to @a Handle.setOption). - INFO.SSL_ENGINES - Array of OpenSSL crypto-engines supported. Note that engines are normally implemented in separate dynamic libraries. Hence not all the returned engines may be available at run-time. - INFO.CONTENT_LENGTH_DOWNLOAD - content-length of the download. This is the value read from the Content-Length: field. Since 7.19.4, this returns -1 if the size isn't known. - INFO.CONTENT_LENGTH_UPLOAD - specified size of the upload. Since 7.19.4, this returns -1 if the size isn't known. - INFO.CONTENT_TYPE - Pass a pointer to a char pointer to receive the content-type of the downloaded object. This is the value read from the Content-Type: field. If you get @b nil, it means that the server didn't send a valid Content-Type header or that the protocol used doesn't support this. - INFO.HTTPAUTH_AVAIL - bitmask indicating the authentication method(s) available. The meaning of the bits is explained in the OPT.HTTPAUTH option for @a Handle.setOption. - INFO.PROXYAUTH_AVAIL - bitmask indicating the authentication method(s) available for your proxy authentication. - INFO.OS_ERRNO - @b errno variable from a connect failure. Note that the value is only set on failure, it is not reset upon a successful operation. - INFO.NUM_CONNECTS - count of connections libcurl had to create to achieve the previous transfer (only the successful connects are counted). Combined with INFO.REDIRECT_COUNT you are able to know how many times libcurl successfully reused existing connection(s) or not. - INFO.PRIMARY_IP - IP address of the most recent connection done with this curl handle. This string may be IPv6 if that's enabled. - INFO_COOKIELIST - Returns an array of all the known cookies - INFO_FTP_ENTRY_PATH - FTP entry path. That is the initial path libcurl ended up in when logging on to the remote FTP server. This stores a @b nil if something is wrong. - INFO.CONDITION_UNMET - 1 if the condition provided in the previous request didn't match (see OPT.TIMECONDITION). Alas, if this returns a 1 you know that the reason you didn't get data in return is because it didn't fulfill the condition. The long this argument points to will get a zero stored if the condition instead was met. */ FALCON_FUNC Handle_getInfo( ::Falcon::VMachine *vm ) { Item* i_option = vm->param(0); if( i_option == 0 || ! i_option->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "N" ) ); } // setup our options Mod::CurlHandle* h = dyncast< Mod::CurlHandle* >( vm->self().asObject() ); if ( h->handle() == 0 ) throw new Mod::CurlError( ErrorParam( FALCON_ERROR_CURL_PM, __LINE__ ) .desc( FAL_STR( curl_err_pm ) ) ); CURLINFO info = (CURLINFO) i_option->forceInteger(); CURLcode cerr = CURLE_OK; switch( info ) { // char* case CURLINFO_EFFECTIVE_URL: case CURLINFO_CONTENT_TYPE: #if LIBCURL_VERSION_NUM >= 0x071900 case CURLINFO_PRIMARY_IP: #endif case CURLINFO_FTP_ENTRY_PATH: { char* rv; cerr = curl_easy_getinfo( h->handle(), info, &rv ); if( cerr == CURLE_OK && rv != 0 ) { CoreString* cs = new CoreString(); cs->bufferize( rv ); vm->retval( cs ); } } break; // long case CURLINFO_RESPONSE_CODE: case CURLINFO_HTTP_CONNECTCODE: case CURLINFO_HEADER_SIZE: case CURLINFO_REQUEST_SIZE: case CURLINFO_SSL_VERIFYRESULT: case CURLINFO_HTTPAUTH_AVAIL: case CURLINFO_PROXYAUTH_AVAIL: case CURLINFO_OS_ERRNO: case CURLINFO_NUM_CONNECTS: #if LIBCURL_VERSION_NUM >= 0x071904 case CURLINFO_CONDITION_UNMET: #endif { long rv; cerr = curl_easy_getinfo( h->handle(), info, &rv ); if( cerr == CURLE_OK ) { vm->retval( (int64) rv ); } } break; // timestamp case CURLINFO_FILETIME: { long rv; cerr = curl_easy_getinfo( h->handle(), info, &rv ); if( cerr == CURLE_OK && rv != -1 ) { time_t trv = (time_t)rv; TimeStamp* timestamp = new TimeStamp; timestamp->m_timezone = tz_UTC; #ifndef FALCON_SYSTEM_WIN struct tm rtm; struct tm *ftime = gmtime_r( &trv, &rtm ); #else struct tm *ftime = gmtime( &trv ); #endif timestamp->m_year = ftime->tm_year + 1900; timestamp->m_month = ftime->tm_mon + 1; timestamp->m_day = ftime->tm_mday; timestamp->m_hour = ftime->tm_hour; timestamp->m_minute = ftime->tm_min; timestamp->m_second = ftime->tm_sec; timestamp->m_msec = 0; Item* i_ts = vm->findGlobalItem("TimeStamp"); fassert( i_ts->isClass() ); vm->retval( i_ts->asClass()->createInstance(timestamp) ); } } break; // double case CURLINFO_TOTAL_TIME: case CURLINFO_NAMELOOKUP_TIME: case CURLINFO_CONNECT_TIME: #if LIBCURL_VERSION_NUM >= 0x071900 case CURLINFO_APPCONNECT_TIME: #endif case CURLINFO_PRETRANSFER_TIME: case CURLINFO_STARTTRANSFER_TIME: case CURLINFO_REDIRECT_TIME: case CURLINFO_REDIRECT_COUNT: case CURLINFO_REDIRECT_URL: case CURLINFO_SIZE_UPLOAD: case CURLINFO_SIZE_DOWNLOAD: case CURLINFO_SPEED_DOWNLOAD: case CURLINFO_SPEED_UPLOAD: case CURLINFO_CONTENT_LENGTH_DOWNLOAD: case CURLINFO_CONTENT_LENGTH_UPLOAD: { double rv; cerr = curl_easy_getinfo( h->handle(), info, &rv ); if( cerr == CURLE_OK ) { vm->retval( (numeric) rv ); } } break; // slist case CURLINFO_SSL_ENGINES: case CURLINFO_COOKIELIST: { curl_slist* rv; cerr = curl_easy_getinfo( h->handle(), info, &rv ); if( cerr == CURLE_OK ) { CoreArray* ca = new CoreArray; curl_slist* p = rv; while( p != 0 ) { ca->append( new CoreString( p->data, -1 ) ); p = p->next; } curl_slist_free_all( rv ); vm->retval( ca ); } } break; default: throw new ParamError( ErrorParam( e_param_range, __LINE__ ) ); } if( cerr != CURLE_OK ) { throw_error( FALCON_ERROR_CURL_GETINFO, __LINE__, FAL_STR( curl_err_getinfo ), cerr ); } } /*# @function dlaod @brief Downloads file. @param uri The uri to be downloaded. @optparam stream a Stream where to download the data. Downloads a file from a remote source and stores it on a string, or on a @b stream, as in the following sequence: @code import from curl data = curl.Handle( "http://www.falconpl.org" ).setOutString().exec().getData() // equivalent: data = curl.dload( "http://www.falconpl.org" ) @endcode */ FALCON_FUNC curl_dload( ::Falcon::VMachine *vm ) { Item* i_uri = vm->param(0); Item* i_stream = vm->param(1); if ( i_uri == 0 || ! (i_uri->isString() || i_uri->isOfClass( "URI" )) || (i_stream != 0 && ! (i_stream->isNil() || i_stream->isOfClass("Stream")) ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S|URI,[Stream]" ) ); } Mod::CurlHandle* ca = new Mod::CurlHandle( vm->findWKI("Handle")->asClass() ); internal_curl_init( vm, ca, i_uri ); if( i_stream == 0 || i_stream->isNil() ) ca->setOnDataGetString(); else ca->setOnDataStream( dyncast(i_stream->asObject()->getFalconData()) ); CURLcode retval = curl_easy_perform(ca->handle()); if( retval != CURLE_OK ) { ca->cleanup(); ca->gcMark(1); // let the gc kill it throw_error( FALCON_ERROR_CURL_EXEC, __LINE__, FAL_STR( curl_err_exec ), retval ); } ca->cleanup(); if( i_stream == 0 || i_stream->isNil() ) vm->retval( ca->getData() ); ca->gcMark(1); // let the gc kill it } static void internal_handle_add( VMachine*vm, Item* i_handle ) { if( i_handle == 0 || ! i_handle->isOfClass( "Handle" ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "Handle" ) ); } Mod::CurlMultiHandle* mh = dyncast< Mod::CurlMultiHandle* >( vm->self().asObject() ); Mod::CurlHandle* sh = dyncast< Mod::CurlHandle* >(i_handle->asObjectSafe()); if( ! mh->addHandle(sh) ) { throw new Mod::CurlError( ErrorParam( FALCON_ERROR_CURL_HISIN, __LINE__) .desc( FAL_STR( curl_err_easy_already_in ) ) ); } } /*# @class Multi @brief Interface to CURL multi_* operations. @optparam ... @a Handle instances to be immediately added. The Multi interface is meant to perform multiple CURL connections handled by a single application. A @b Multi instance lifetime is usually like the following: - Add one or more pre-configured @a Handle instances. - Loop on @a Multi.perform() up to when it returns 0 indicating that all transfers are complete. For example, a minimal operation may be like the following: @code import from curl h1 = curl.Handle( "http://www.falconpl.org" ).setOutString() h2 = curl.Handle( "http://www.google.com" ).setOutString() hm = curl.Multi( h1, h2 ) loop v = hm.perform() > "Currently ", v, " transfers ongoing." sleep(0.1) end v == 0 > h1.getData() > h2.getData() @endcode */ FALCON_FUNC Multi_init ( ::Falcon::VMachine *vm ) { for ( int i = 0; i < vm->paramCount(); ++i ) { internal_handle_add( vm, vm->param(i) ); } } /*# @method add Multi @brief Adds an @a Handle instance to the multi interface. @param h The @a Handle instance to be added. Adds a handle to an existing curl multihandle. */ FALCON_FUNC Multi_add ( ::Falcon::VMachine *vm ) { Item* i_handle = vm->param(0); internal_handle_add( vm, i_handle ); } /*# @method remove Multi @brief Adds an @a Handle instance to the multi interface. @param h The @a Handle instance to be added. Adds a handle to an existing curl multihandle. */ FALCON_FUNC Multi_remove ( ::Falcon::VMachine *vm ) { Item* i_handle = vm->param(0); if( i_handle == 0 || ! i_handle->isOfClass( "Handle" ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "Handle" ) ); } Mod::CurlMultiHandle* mh = dyncast< Mod::CurlMultiHandle* >( vm->self().asObject() ); Mod::CurlHandle* sh = dyncast< Mod::CurlHandle* >(i_handle->asObjectSafe()); if( ! mh->removeHandle(sh) ) { throw new Mod::CurlError( ErrorParam( FALCON_ERROR_CURL_HNOIN, __LINE__) .desc( FAL_STR( curl_err_easy_not_in ) ) ); } } /*# @method perform Multi @brief Starts or proceeds with the transfers. @return The count of remaining operations to be handled. The calling application should call repeatedly this method until it returns 0, indicating that all the transfers are compelete. */ FALCON_FUNC Multi_perform ( ::Falcon::VMachine *vm ) { Mod::CurlMultiHandle* mh = dyncast< Mod::CurlMultiHandle* >( vm->self().asObject() ); int rh = 0; CURLMcode ret; do{ ret = curl_multi_perform( mh->handle(), &rh ); } while ( ret == CURLM_CALL_MULTI_PERFORM ); if ( ret != CURLM_OK ) { throw_merror( FALCON_ERROR_CURL_MULTI, __LINE__, FAL_STR( curl_err_multi_error), ret ); } vm->retval( rh ); } /*# @class CurlError @brief Error generated by cURL while operating. @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. @from Error code, description, extra See the Error class in the core module. */ FALCON_FUNC CurlError_init ( ::Falcon::VMachine *vm ) { CoreObject *einst = vm->self().asObject(); if( einst->getUserData() == 0 ) einst->setUserData( new Mod::CurlError ); ::Falcon::core::Error_init( vm ); } } } /* end of curl_mod.cpp */ modules/native/curl/src/curl_ext.h000066400000000000000000000060711176363201700175600ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: curl_ext.cpp cURL library binding for Falcon Interface extension functions ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 27 Nov 2009 16:31:15 +0100 ------------------------------------------------------------------- (C) Copyright 2009: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file cURL library binding for Falcon Interface extension functions - header file */ #ifndef curl_ext_H #define curl_ext_H #include #ifndef FALCON_ERROR_CURL_BASE #define FALCON_ERROR_CURL_BASE 2350 #endif #define FALCON_ERROR_CURL_INIT (FALCON_ERROR_CURL_BASE+0) #define FALCON_ERROR_CURL_EXEC (FALCON_ERROR_CURL_BASE+1) #define FALCON_ERROR_CURL_PM (FALCON_ERROR_CURL_BASE+2) #define FALCON_ERROR_CURL_SETOPT (FALCON_ERROR_CURL_BASE+3) #define FALCON_ERROR_CURL_GETINFO (FALCON_ERROR_CURL_BASE+4) #define FALCON_ERROR_CURL_HISIN (FALCON_ERROR_CURL_BASE+5) #define FALCON_ERROR_CURL_HNOIN (FALCON_ERROR_CURL_BASE+6) #define FALCON_ERROR_CURL_MULTI (FALCON_ERROR_CURL_BASE+7) namespace Falcon { namespace Ext { FALCON_FUNC curl_dload( ::Falcon::VMachine *vm ); FALCON_FUNC curl_version( ::Falcon::VMachine *vm ); FALCON_FUNC Handle_init( ::Falcon::VMachine *vm ); FALCON_FUNC Handle_exec( ::Falcon::VMachine *vm ); FALCON_FUNC Handle_setOutConsole( ::Falcon::VMachine *vm ); FALCON_FUNC Handle_setOutString( ::Falcon::VMachine *vm ); FALCON_FUNC Handle_setOutStream( ::Falcon::VMachine *vm ); FALCON_FUNC Handle_setOutCallback( ::Falcon::VMachine *vm ); FALCON_FUNC Handle_getInfo( ::Falcon::VMachine *vm ); FALCON_FUNC Handle_setInCallback( ::Falcon::VMachine *vm ); FALCON_FUNC Handle_setInStream( ::Falcon::VMachine *vm ); FALCON_FUNC Handle_postData( ::Falcon::VMachine *vm ); FALCON_FUNC Handle_setOption( ::Falcon::VMachine *vm ); FALCON_FUNC Handle_setOptions( ::Falcon::VMachine *vm ); //FALCON_FUNC Handle_setOutMessage( ::Falcon::VMachine *vm ); FALCON_FUNC Handle_cleanup( ::Falcon::VMachine *vm ); FALCON_FUNC Handle_getData( ::Falcon::VMachine *vm ); FALCON_FUNC Multi_init( ::Falcon::VMachine *vm ); FALCON_FUNC Multi_add( ::Falcon::VMachine *vm ); FALCON_FUNC Multi_remove( ::Falcon::VMachine *vm ); FALCON_FUNC Multi_perform( ::Falcon::VMachine *vm ); FALCON_FUNC CurlError_init ( ::Falcon::VMachine *vm ); } } #endif /* end of curl_ext.h */ modules/native/curl/src/curl_mod.cpp000066400000000000000000000304561176363201700200760ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: curl_ext.cpp cURL library binding for Falcon Interface extension functions ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 27 Nov 2009 16:31:15 +0100 ------------------------------------------------------------------- (C) Copyright 2009: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file cURL library binding for Falcon Internal logic functions - implementation. */ #include "curl_mod.h" #include #include #include #include #include #include #include #include namespace Falcon { namespace Mod { CurlHandle::CurlHandle( const CoreClass* cls, bool bDeser ): CacheObject( cls, bDeser ), m_sReceived(0), m_dataStream(0), m_cbMode( e_cbmode_stdout ), m_readStream(0), m_pPostBuffer(0) { if ( bDeser ) m_handle = 0; else { m_handle = curl_easy_init(); if (m_handle) curl_easy_setopt( m_handle, CURLOPT_WRITEFUNCTION, write_stdout ); } } CurlHandle::CurlHandle( const CurlHandle &other ): CacheObject( other ), m_iDataCallback( other.m_iDataCallback ), m_sReceived(0), m_dataStream( other.m_dataStream ), m_sSlot( other.m_sSlot ), m_cbMode( e_cbmode_stdout ) { if ( other.m_handle != 0 ) m_handle = curl_easy_duphandle( other.m_handle ); else m_handle = 0; } CurlHandle::~CurlHandle() { cleanup(); } CurlHandle* CurlHandle::clone() const { return new CurlHandle( *this ); } void CurlHandle::cleanup() { if( m_handle != 0 ) { curl_easy_cleanup( m_handle ); m_handle = 0; ListElement* head = m_slists.begin(); while( head != 0 ) { struct curl_slist* slist = (struct curl_slist*) head->data(); curl_slist_free_all( slist ); head = head->next(); } } if (m_pPostBuffer != 0 ) { memFree( m_pPostBuffer ); m_pPostBuffer = 0; } } bool CurlHandle::serialize( Stream *stream, bool bLive ) const { if ( ! bLive ) { return false; } uint64 ptr = endianInt64( (uint64) m_handle ); stream->write( &ptr, sizeof(ptr) ); return CacheObject::serialize( stream, bLive ); } bool CurlHandle::deserialize( Stream *stream, bool bLive ) { if ( ! bLive ) return false; fassert( m_handle == 0 ); uint64 ptr; if( stream->read( &ptr, sizeof(ptr) ) != sizeof(ptr) ) { return false; } ptr = endianInt64( ptr ); m_handle = (CURL*) ptr; return true; } void CurlHandle::gcMark( uint32 mark ) { memPool->markItem( m_iDataCallback ); memPool->markItem( m_iReadCallback ); if( m_sReceived != 0 ) m_sReceived->mark( mark ); if( m_dataStream != 0 ) m_dataStream->gcMark( mark ); if( m_readStream != 0 ) m_readStream->gcMark( mark ); CacheObject::gcMark( mark ); } size_t CurlHandle::write_stdout( void *ptr, size_t size, size_t nmemb, void *) { return fwrite( ptr, size, nmemb, stdout ); } size_t CurlHandle::write_stream( void *ptr, size_t size, size_t nmemb, void *data) { Stream* s = (Stream*) data; return s->write( ptr, size * nmemb ); } size_t CurlHandle::write_msg( void *ptr, size_t size, size_t nmemb, void *data) { VMachine* vm = VMachine::getCurrent(); if( vm != 0 ) { CurlHandle* cs = (CurlHandle*) data; VMMessage* vmmsg = new VMMessage( cs->m_sSlot ); vmmsg->addParam( cs ); CoreString* str = new CoreString; str->adopt( (char*) ptr, (int32) size * nmemb, 0 ); str->bufferize(); vmmsg->addParam( str ); vm->postMessage( vmmsg ); } return size * nmemb; } size_t CurlHandle::write_string( void *ptr, size_t size, size_t nmemb, void *data) { CurlHandle* h = (CurlHandle*) data; if ( h->m_sReceived == 0 ) h->m_sReceived = new CoreString( size * nmemb ); String str; str.adopt( (char*) ptr, (int32) size * nmemb, 0 ); h->m_sReceived->append( str ); return size * nmemb; } size_t CurlHandle::write_callback( void *ptr, size_t size, size_t nmemb, void *data) { VMachine* vm = VMachine::getCurrent(); if( vm != 0 ) { CurlHandle* self = (CurlHandle*) data; CoreString* str = new CoreString; str->adopt( ( char*) ptr, (int32) size * nmemb, 0 ); vm->pushParameter( str ); vm->callItemAtomic( self->m_iDataCallback, 1 ); if( vm->regA().isNil() || (vm->regA().isBoolean() && vm->regA().asBoolean() ) ) return size * nmemb; else if( vm->regA().isOrdinal() ) return (size_t) vm->regA().forceInteger(); } return 0; } void CurlHandle::setOnDataCallback( const Item& itm ) { m_sReceived = 0; m_dataStream = 0; m_iDataCallback = itm; m_cbMode = e_cbmode_callback; if( m_handle != 0 ) { curl_easy_setopt( m_handle, CURLOPT_WRITEFUNCTION, write_callback ); curl_easy_setopt( m_handle, CURLOPT_WRITEDATA, this ); } } void CurlHandle::setOnDataStream( Stream* s ) { m_iDataCallback.setNil(); m_sReceived = 0; m_dataStream = s; m_cbMode = e_cbmode_stream; if( m_handle != 0 ) { curl_easy_setopt( m_handle, CURLOPT_WRITEFUNCTION, write_stream ); curl_easy_setopt( m_handle, CURLOPT_WRITEDATA, s ); } } void CurlHandle::setOnDataMessage( const String& msg ) { m_sReceived = 0; m_iDataCallback.setNil(); m_dataStream = 0; m_sSlot = msg; m_cbMode = e_cbmode_slot; if( m_handle != 0 ) { curl_easy_setopt( m_handle, CURLOPT_WRITEFUNCTION, write_msg ); curl_easy_setopt( m_handle, CURLOPT_WRITEDATA, this ); } } void CurlHandle::setOnDataGetString() { // the string is initialized by the callback m_sReceived = 0; m_iDataCallback.setNil(); m_dataStream = 0; m_cbMode = e_cbmode_string; if( m_handle != 0 ) { curl_easy_setopt( m_handle, CURLOPT_WRITEFUNCTION, write_string ); curl_easy_setopt( m_handle, CURLOPT_WRITEDATA, this ); } } void CurlHandle::setOnDataStdOut() { // the string is initialized by the callback m_sReceived = 0; m_iDataCallback.setNil(); m_dataStream = 0; m_cbMode = e_cbmode_stdout; if( m_handle != 0 ) { curl_easy_setopt( m_handle, CURLOPT_WRITEFUNCTION, write_stdout ); } } void CurlHandle::setReadCallback( const Item& callable ) { // the string is initialized by the callback m_iReadCallback = callable; m_readStream = 0; if( m_handle != 0 ) { curl_easy_setopt( m_handle, CURLOPT_READFUNCTION, read_callback ); curl_easy_setopt( m_handle, CURLOPT_READDATA, this ); } } void CurlHandle::setReadStream( Stream* stream ) { // the string is initialized by the callback m_iReadCallback.setNil(); m_readStream = stream; if( m_handle != 0 ) { curl_easy_setopt( m_handle, CURLOPT_READFUNCTION, read_stream ); curl_easy_setopt( m_handle, CURLOPT_READDATA, this ); } } CoreString* CurlHandle::getData() { CoreString* ret = m_sReceived; m_sReceived = 0; return ret; } size_t CurlHandle::read_callback( void *ptr, size_t size, size_t nmemb, void *data) { VMachine* vm = VMachine::getCurrent(); if ( vm != 0 ) { CurlHandle* h = (CurlHandle *) data; MemBuf_1 m( (byte*) ptr, size* nmemb, 0 ); vm->pushParameter( (MemBuf*) &m ); vm->callItemAtomic( h->m_iReadCallback, 1 ); if( vm->regA().isOrdinal() ) return (size_t) vm->regA().forceInteger(); } return 0; } size_t CurlHandle::read_stream( void *ptr, size_t size, size_t nmemb, void *data) { CurlHandle* h = (CurlHandle *) data; if( h->m_readStream != 0 ) { return h->m_readStream->read( ptr, size * nmemb ); } return CURL_READFUNC_ABORT; } struct curl_slist* CurlHandle::slistFromArray( CoreArray* ca ) { struct curl_slist* sl = NULL; for( uint32 pos = 0; pos < ca->length(); ++pos ) { Item& current = ca->at(pos); if( ! current.isString() ) { if( sl != 0 ) m_slists.pushBack( sl ); return 0; } AutoCString str( current ); sl = curl_slist_append( sl, str.c_str() ); } if( sl != 0 ) m_slists.pushBack( sl ); return sl; } void CurlHandle::postData( const String& str ) { if (m_pPostBuffer != 0 ) memFree( m_pPostBuffer ); m_pPostBuffer = memAlloc( str.size() ); memcpy(m_pPostBuffer, str.getRawStorage(), str.size() ); curl_easy_setopt( handle(), CURLOPT_POSTFIELDS, m_pPostBuffer ); curl_easy_setopt( handle(), CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t) str.size() ); } CoreObject* CurlHandle::Factory( const CoreClass *cls, void *data, bool deser ) { return new CurlHandle( cls, deser ); } //============================================================== // CurlMultiHandle::CurlMultiHandle( const CoreClass* cls, bool bDeser ): CacheObject( cls, bDeser ) { if ( bDeser ) m_handle = 0; else { m_handle = curl_multi_init(); m_mtx = new Mutex; m_refCount = new int(1); } } CurlMultiHandle::CurlMultiHandle( const CurlMultiHandle &other ): CacheObject( other ) { if( other.m_handle != 0 ) { m_mtx = other.m_mtx; m_refCount = other.m_refCount; m_handle = other.m_handle; m_mtx->lock(); (*m_refCount)++; m_mtx->unlock(); } else { m_mtx = new Mutex; m_refCount = 0; } } CurlMultiHandle::~CurlMultiHandle() { if ( m_handle != 0 ) { m_mtx->lock(); bool bDelete = --(*m_refCount) == 0; m_mtx->unlock(); if( bDelete ) { delete m_refCount; delete m_mtx; curl_multi_cleanup( m_handle ); } } } CurlMultiHandle* CurlMultiHandle::clone() const { return new CurlMultiHandle( *this ); } bool CurlMultiHandle::serialize( Stream *stream, bool bLive ) const { if ( ! bLive ) return false; // incref immediately m_mtx->lock(); (*m_refCount)++; m_mtx->unlock(); uint64 ptrh = endianInt64( (uint64) m_handle ); uint64 ptrm = endianInt64( (uint64) m_mtx ); uint64 ptrrc = endianInt64( (uint64) m_refCount ); stream->write( &ptrh, sizeof(ptrh) ); stream->write( &ptrm, sizeof(ptrm) ); stream->write( &ptrrc, sizeof(ptrrc) ); bool bOk = CacheObject::serialize( stream, bLive ); if( ! bOk ) { m_mtx->lock(); (*m_refCount)--; m_mtx->unlock(); } return true; } bool CurlMultiHandle::deserialize( Stream *stream, bool bLive ) { if ( ! bLive ) return false; fassert( m_handle == 0 ); uint64 ptrh; uint64 ptrm; uint64 ptrrc; if( stream->read( &ptrh, sizeof(ptrh) ) != sizeof( ptrh ) || stream->read( &ptrm, sizeof(ptrm) ) != sizeof( ptrm ) || stream->read( &ptrrc, sizeof(ptrrc) ) != sizeof( ptrrc ) ) { return false; } m_handle = (CURLM*) endianInt64( ptrh ); m_mtx = (Mutex*) endianInt64( ptrm ); m_refCount = (int*) endianInt64( ptrrc ); return true; } CoreObject* CurlMultiHandle::Factory( const CoreClass *cls, void *data, bool bDeser ) { return new CurlMultiHandle( cls, bDeser ); } void CurlMultiHandle::gcMark( uint32 mark ) { m_handles.gcMark( mark ); CacheObject::gcMark( mark ); } bool CurlMultiHandle::addHandle( CurlHandle* h ) { for ( uint32 i = 0; i < m_handles.length(); ++i ) { if ( m_handles[i].asObjectSafe() == h ) return false; } m_handles.append( h ); curl_multi_add_handle( handle(), h->handle() ); return true; } bool CurlMultiHandle::removeHandle( CurlHandle* h ) { for ( uint32 i = 0; i < m_handles.length(); ++i ) { if ( m_handles[i].asObjectSafe() == h ) { curl_multi_remove_handle( handle(), h->handle() ); m_handles.remove( i ); return true; } } return false; } } } /* end of curl_mod.cpp */ modules/native/curl/src/curl_mod.h000066400000000000000000000113271176363201700175370ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: curl_ext.cpp cURL library binding for Falcon Interface extension functions ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 27 Nov 2009 16:31:15 +0100 ------------------------------------------------------------------- (C) Copyright 2009: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file cURL library binding for Falcon Internal logic functions - declarations. */ #ifndef curl_mod_H #define curl_mod_H #include #include #include #include #include namespace Falcon { namespace Mod { class CurlHandle: public CacheObject { public: CurlHandle( const CoreClass* cls, bool bDeser = false ); CurlHandle( const CurlHandle &other ); virtual ~CurlHandle(); virtual CurlHandle* clone() const; CURL* handle() const { return m_handle; } virtual void gcMark( uint32 mark ); virtual bool serialize( Stream *stream, bool bLive ) const; virtual bool deserialize( Stream *stream, bool bLive ); static CoreObject* Factory( const CoreClass *cls, void *data, bool ); void setOnDataCallback( const Item& callable ); void setOnDataStream( Stream* s ); void setOnDataMessage( const String& msgName ); void setOnDataGetString(); void setOnDataStdOut(); void setReadCallback( const Item& callable ); void setReadStream( Stream* read ); CoreString* getData(); void cleanup(); /** Creates a curl_slist from a Falcon array of strings. * The list is stored here and destroyed by cleanup(). * Returns zero if some elements of ca are not strings. * */ struct curl_slist* slistFromArray( CoreArray* ca ); /** Stores data for post operations. * Saves a copy of the string in a local buffer, that is destroyed * at cleanup(), and sets the POSTFIELDS and CURLOPT_POSTFIELDSIZE_LARGE * options correctly. * * Multiple operations will cause the previous buffer to be discarded. */ void postData( const String& str ); protected: /** Callback modes. * */ typedef enum { e_cbmode_stdout, e_cbmode_string, e_cbmode_stream, e_cbmode_slot, e_cbmode_callback } t_cbmode; private: static size_t write_stdout( void *ptr, size_t size, size_t nmemb, void *data); static size_t write_stream( void *ptr, size_t size, size_t nmemb, void *data); static size_t write_msg( void *ptr, size_t size, size_t nmemb, void *data); static size_t write_string( void *ptr, size_t size, size_t nmemb, void *data); static size_t write_callback( void *ptr, size_t size, size_t nmemb, void *data); static size_t read_callback( void *ptr, size_t size, size_t nmemb, void *data); static size_t read_stream( void *ptr, size_t size, size_t nmemb, void *data); CURL* m_handle; Item m_iDataCallback; CoreString* m_sReceived; Stream* m_dataStream; String m_sSlot; /** Callback mode, determining which of the method to notify the app is used. */ t_cbmode m_cbMode; Item m_iReadCallback; Stream* m_readStream; // lists of lists to be destroyed at exit. List m_slists; void* m_pPostBuffer; }; class CurlMultiHandle: public CacheObject { public: CurlMultiHandle( const CoreClass* cls, bool bDeser = false ); CurlMultiHandle( const CurlMultiHandle &other ); virtual ~CurlMultiHandle(); virtual CurlMultiHandle* clone() const; CURLM* handle() const { return m_handle; } virtual bool serialize( Stream *stream, bool bLive ) const; virtual bool deserialize( Stream *stream, bool bLive ); static CoreObject* Factory( const CoreClass *cls, void *data, bool ); virtual void gcMark( uint32 mark ); bool addHandle( CurlHandle* h ); bool removeHandle( CurlHandle* ); private: CURLM* m_handle; Mutex* m_mtx; int* m_refCount; ItemArray m_handles; }; class CurlError: public ::Falcon::Error { public: CurlError(): Error( "CurlError" ) {} CurlError( const ErrorParam ¶ms ): Error( "CurlError", params ) {} }; } } #endif /* end of curl_mod.h */ modules/native/curl/src/curl_st.cpp000066400000000000000000000022671176363201700177440ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: curl_st.cpp cURL library binding for Falcon Interface extension functions ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 27 Nov 2009 16:31:15 +0100 ------------------------------------------------------------------- (C) Copyright 2009: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file String table - export internationalizable stings to both C++ and Falcon modules. */ #define FALCON_REALIZE_STRTAB #include "curl_st.h" /* end of curl_st.cpp */ modules/native/curl/src/curl_st.h000066400000000000000000000047041176363201700174070ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: curl_st.h cURL library binding for Falcon Interface extension functions ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 27 Nov 2009 16:31:15 +0100 ------------------------------------------------------------------- (C) Copyright 2009: The above AUTHOR Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** \file String table - export internationalizable stings to both C++ and Falcon modules. */ //WARNING: the missing of usual #ifndef/#define pair // is intentional! // See the contents of this header file for a deeper overall // explanation of the MODSTR system. #include // The first parameter is an unique identifier in your project that // will be bound to the correct entry in the module string table. // Falcon::VMachine::moduleString( curl_msg_1 ) will // return the associated string or the internationalized version. // FAL_STR( curl_msg_1 ) macro can be used in standard // functions as a shortcut. FAL_MODSTR( curl_err_desc, "CURL error code:" ); FAL_MODSTR( curl_err_init, "Error during intialization" ); FAL_MODSTR( curl_err_exec, "Error during transfer" ); FAL_MODSTR( curl_err_resources, "Not enough resources to complete the operation" ); FAL_MODSTR( curl_err_pm, "Curl handle already closed" ); FAL_MODSTR( curl_err_setopt, "Type of parameter incompatible for this option" ); FAL_MODSTR( curl_err_unkopt, "Unknown option for setOption" ); FAL_MODSTR( curl_err_getinfo, "Error while reading required information." ); FAL_MODSTR( curl_err_easy_already_in, "Handle already added" ); FAL_MODSTR( curl_err_easy_not_in, "Handle currently not present" ); FAL_MODSTR( curl_err_multi_error, "Error in CURL multiple operation" ); //... add here your messages, and remove or configure the above one /* end of curl_st.h */ modules/native/curl/src/version.h000066400000000000000000000007741176363201700174240ustar00rootroot00000000000000/* FALCON - curl falcon binding module FILE: version.h ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 19 Jun 2010 11:41:40 +0200 ------------------------------------------------------------------- (C) Copyright 2010 - Giancarlo Niccola */ #ifndef VERSION_H #define VERSION_H #define VERSION_MAJOR 1 #define VERSION_MINOR 0 #define VERSION_REVISION 0 #endif /* end of version.h */ modules/native/dbi/000077500000000000000000000000001176363201700145605ustar00rootroot00000000000000modules/native/dbi/AUTHORS000066400000000000000000000002331176363201700156260ustar00rootroot00000000000000Jeremy Cowgar Giancarlo Niccolai Tiziano de Rubeis Steven Oliver modules/native/dbi/CMakeLists.txt000066400000000000000000000054001176363201700173170ustar00rootroot00000000000000#################################################################### # The Falcon Programming language # # CMake configuration file for DBI #################################################################### cmake_minimum_required(VERSION 2.6.2) project(Falcon_DBI) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) # The required package find_package(Falcon REQUIRED) ### Options ### # SQLite3 OPTION( FALCON_DBI_BUILD_SQLITE "Include SQLite3 DBI Module" ON ) # MySQL FIND_PATH( MYSQL_INCLUDE_DIR mysql.h HINTS /usr/include/mysql /usr/local/include/mysql ) IF ( MYSQL_INCLUDE_DIR ) set( _mysql ON ) ELSE() set( _mysql OFF ) ENDIF() OPTION( FALCON_DBI_BUILD_MYSQL "Include MySQL DBI Module" ${_mysql} ) # PostgreSQL FIND_PATH( PGSQL_INCLUDE_DIR libpq-fe.h HINTS /usr/include/postgresql /usr/include/pgsql ) IF ( PGSQL_INCLUDE_DIR ) set( _pgsql ON ) ELSE() set( _pgsql OFF ) ENDIF() OPTION( FALCON_DBI_BUILD_PGSQL "Include PostgreSQL DBI Module" ${_pgsql} ) # Oracle FIND_PATH( ORACLE_INCLUDE_DIR occi.h HINTS /usr/include/oracle /usr/local/include/oracle $ENV{ORACLE_HOME}/sdk/include ) IF ( ORACLE_INCLUDE_DIR ) set ( _oracle ON ) ELSE() set ( _oracle OFF ) ENDIF() OPTION( FALCON_DBI_BUILD_ORACLE "Include Oracle DBI Module" ${_oracle} ) # Firebird FIND_PATH( FIREBIRD_INCLUDE_DIR ibase.h HINTS /usr/include /usr/local/include) IF ( FIREBIRD_INCLUDE_DIR ) set( _fbsql ON ) ELSE() set(_fbsql OFF) ENDIF() OPTION(FALCON_DBI_BUILD_FIREBIRD "Include Firebird DBI Module" ${_fbsql} ) # For now, activate ODBC on windows IF(MSVC) OPTION(FALCON_DBI_BUILD_ODBC "Include ODBC DBI Module" ON) ELSE(MSVC) OPTION(FALCON_DBI_BUILD_ODBC "Include ODBC DBI Module" OFF) ENDIF(MSVC) INCLUDE_DIRECTORIES(include) INCLUDE_DIRECTORIES(${Falcon_INCLUDE_DIRS}) # Common files set( dbi_common_files ../dbi_common/dbi_common.cpp ../dbi_common/dbi_error.cpp ../dbi_common/dbi_handle.cpp ../dbi_common/dbi_inbind.cpp ../dbi_common/dbi_outbind.cpp ../dbi_common/dbi_params.cpp ../dbi_common/dbi_recordset.cpp ../dbi_common/dbi_stmt.cpp # Header files are useful for IDEs ../include/falcon/dbi_error.h ../include/falcon/dbi_handle.h ../include/falcon/dbi_inbind.h ../include/falcon/dbi_outbind.h ../include/falcon/dbi_params.h ../include/falcon/dbi_recordset.h ../include/falcon/dbi_stmt.h ) # Project directories ADD_SUBDIRECTORY(dbi) # DBI Drivers IF(FALCON_DBI_BUILD_MYSQL) ADD_SUBDIRECTORY(mysql) ENDIF() IF(FALCON_DBI_BUILD_PGSQL) ADD_SUBDIRECTORY(pgsql) ENDIF() IF(FALCON_DBI_BUILD_SQLITE) ADD_SUBDIRECTORY(sqlite3) ENDIF() IF(FALCON_DBI_BUILD_ODBC) ADD_SUBDIRECTORY(odbc) ENDIF() IF(FALCON_DBI_BUILD_ORACLE) ADD_SUBDIRECTORY(oracle) ENDIF() IF(FALCON_DBI_BUILD_FIREBIRD) ADD_SUBDIRECTORY(fbsql) ENDIF() modules/native/dbi/CTestConfig.cmake000066400000000000000000000003451176363201700177340ustar00rootroot00000000000000set(CTEST_PROJECT_NAME "falcon-module-dbi") set(CTEST_DROP_METHOD "http") set(CTEST_DROP_SITE "maikbeckmann.dyndns.org") set(CTEST_DROP_LOCATION "/CDash/submit.php?project=${CTEST_PROJECT_NAME}") set(CTEST_DROP_SITE_CDASH true) modules/native/dbi/ChangeLog000066400000000000000000000000451176363201700163310ustar00rootroot00000000000000DBI( 1.0 ) * First official releasemodules/native/dbi/Doxyfile000066400000000000000000000245431176363201700162760ustar00rootroot00000000000000# Doxyfile 1.5.3 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = Falcon_Modules_DBI PROJECT_NUMBER = 0.1.0 OUTPUT_DIRECTORY = CREATE_SUBDIRS = YES OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class " \ "The $name widget " \ "The $name file " \ is \ provides \ specifies \ contains \ represents \ a \ an \ the ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = /home/jeremy/Projects/falcon/devel/release/build/core/ STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO DETAILS_AT_TOP = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 8 ALIASES = OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = YES SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_DIRECTORIES = NO FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text " WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.d \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.idl \ *.odl \ *.cs \ *.php \ *.php3 \ *.inc \ *.m \ *.mm \ *.dox \ *.py \ *.C \ *.CC \ *.C++ \ *.II \ *.I++ \ *.H \ *.HH \ *.H++ \ *.CS \ *.PHP \ *.PHP3 \ *.M \ *.MM \ *.PY \ *.C \ *.H \ *.tlh \ *.diff \ *.patch \ *.moc \ *.xpm \ *.dox RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = NO INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES REFERENCES_LINK_SOURCE = YES USE_HTAGS = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO HTML_DYNAMIC_SECTIONS = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO BINARY_TOC = NO TOC_EXPAND = NO DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = NO TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = NO USE_PDFLATEX = NO LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = Falcon_Core.tag ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES CLASS_GRAPH = YES COLLABORATION_GRAPH = NO GROUP_GRAPHS = YES UML_LOOK = NO TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO CALLER_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = NO DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 1000 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = YES modules/native/dbi/LICENSE000066400000000000000000000436071176363201700155770ustar00rootroot00000000000000 Falcon Programming Language License Version 1.1, March 2008 http://www.falconpl.org/?page_id=license_1_1 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. * "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 10 of this document. * "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. * "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. * "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. * "Source" form shall mean the preferred form for making modifications, including but not limited to software source code and example code. * "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. * "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). * "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. * "Embedding Works" shall mean any work, whether in Source or Object form, that links (or binds by name) to the interface of the Work and Derivative Works. * "Applications of the Work" shall mean any work, whether in Source or Object form, that is expressed through the grammar rules which are known by the Work and that require the Work to perform its execution. * "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." * "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, prepare Embedding Works, prepare Applications of the Work, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution of Work and Derivative Works. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and 2. You must cause any modified files to carry prominent notices stating that You changed the files; and 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 4. You must state in the Source Form and in the documentation of any Derivative Work the fact that such work is a derivation of the Work, and include a copy of the Work in its Source form or provide directions on how to obtain a copy of the Work in its Source form; and 5. The Derivative Works are distributed under the terms of this License, or under terms that do not cause infringement of this License. 5. Distribution of Embedding Works and Applications of the Work. You may produce and distribute any Embedding Work or Applications of the Work thereof in any medium, in Source or Object form, provided You meet the following conditions: 1. The Embedding Works and Applications of the Work are distributed under the term of this License, or the application of another License is explicitly stated in the documentation of the Embedding Works and Applications of the Work or included in the Source form of the Embedding Works and Applications of the Work; and 2. If the Source form of Embedding Works is not distributed nor made available to the Users in any form, the Embedding Works carry a prominent notice in their documentation, or when not applicable, in any place that the Users are exposed to, about the fact that the Work is embedded, along with a general statement about the task that the Work is performing in the Embedding Works; and 3. If the Source form of Applications of the Work is not distributed nor made available to the Users in any form, the Applications of the Work carry a prominent notice in their documentation, or when not applicable, in any place that the Users are exposed to, about the fact that the Work is used by the Application of the Work. 6. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement You may have executed with Licensor regarding such Contributions. 7. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 8. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 9. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 10. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of Your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS ============================================================================== APPENDIX: How to apply the Falcon Programming Language License to your work To apply the Falcon Programming Language License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Falcon Programming Language License, Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.falconpl.org/?page_id=license_1_1 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ============================================================================== APPENDIX: License commentary Dear reader, First of all, thanks for considering using The Falcon Language for Your job, or to embed it into Your application, or to use it for any other reason. The Falcon Programming Language License explains what You are allowed to do with Falcon and what You are supposed (and allowed) not to do. Since the legal jargon may be cryptic, You are provided here with a little resume of the License. It is important that You read and accept the License, as this resume hasn't any legal valence, and is provided only for the reason to express clarifications and examples that cannot find their place in the formal document. The License grants You the rights to use the Source code of Falcon and its various components in any way; You can study it, You can copy it, You can modify it, and You can even sell it, but You can't change the license under which it is distributed: even if You sell it, You have to provide the customers with the source code of Falcon and to grant them the same rights You have on it. The License also grants Your copyrights and intellectual rights for any modification or addition You may want to apply to Falcon. In case or addition and modifications, the License binds You to provide the user with the information that the original program, that is Falcon, was modified by You and what are the changes or the additions that You applied. Also, even if You can freely distribute, or even charge a fee, for Your derived work, You MUST apply the Falcon Programming Language License to Your modifications, and distribute them under the same terms. In other words, Your modifications, if made public, must be provided or available in source code. The license also grants You the right to embed Falcon in any application. Here You are granted the right to pick the terms and licenses You prefer, and to distribute Your embedding application without providing its source code. Even if significant portions of Falcon are in line functions, and even if You decide to statically link Falcon in Your application, this doesn't make it a "Derivative Work" of it: Your Embedding application is free to embed Falcon "as is" as it wish, without any requirements in terms of source distribution. You can also modify Falcon for Your specific needs and THEN embed Your modified version; the modified version of Falcon is under Falcon License, and must be made available in source code (or be proposed as a Contribution to the Falcon Committee), but Your Embedding application can still be distributed under Your preferred terms. The License has only a requirement that is demanded on Your Embedding application: in case you don't distribute your embedding application isn't made available in source code, You HAVE to state somewhere (in a place that CAN possibly be seen by the average user) that You are embedding Falcon and the reason for that. In example, You may state "ProgramX uses the Falcon Programming Language to automatize the Gamma procedure". About the scripts, which are more or less what a scripting language is for, You are granted the right to apply the license You prefer. As Falcon is also a script "compiler", You may even retain from distributing the script sources, and apply closed-source license to Your script-based application or to the scripts that are embedded in Your embedding application. However, if You don't distribute the script sources, and you use Falcon or another application covered by the same license to run them, You are again required to state somewhere in Your documentation or where the user can read it that You are using "Falcon" (or the derivative work you used), and more or less why You are doing it. For a pure Falcon application, You can just state "This application is written in Falcon". In example, if You use Falcon to drive a web site, and You don't want Your site visitors to ever see Your scripts, You have to put somewhere a reading like "Powered with Falcon". What You cannot do is to claim that any thing You learnt from Falcon is Yours: especially, You are forbidden to patent any element that is found in Falcon. Another thing that You can't do is to sell a Falcon based product as if it were "completely" Yours, forgetting to cite, even in a very small reading, the fact that Falcon is in. Finally, a thing that the License prevents You from doing is to put the blame for failures on Falcon: the product is provided as-is, without any warranty. It's up to You to test if it is suitable to solve Your problems, and if a Falcon based application can be sold and granted as "working" to Your customers. If that application breaks, whether there's a problem with Falcon or not, You can't issue any claim on the Falcon contributors. Finally, notice that this version of falcon is released under dual license. You may chose either to use this FPLLv1.1 license or the standard GNU GPLv2. GNU GPLv2 is usually shipped with Your distribution and commentaries are widely available on the Net, so we won't discuss it here. Be kind on the Open Source Community: they have already made a lot for You even if You don't know them (and even if they don't know You). Best regards, Giancarlo Niccolai modules/native/dbi/README000066400000000000000000000014631176363201700154440ustar00rootroot00000000000000 The Falcon Programming Language Falcon DBI 0.9.10 Falcon DBI is the database abstraction layer that is part of the official Falcon distributions. They are granted to be present in a correct falcon installation. Yet, they are handled as a separate project, both for administrative reasons and to isolate them from the development of the core system, which may be erratic and cause sudden changes in the interfaces. Modules admitted in the DBI are currently: * mysql * pgsql * sqlite3 * ODBC BUILDING ======== To build the Falcon DBI module, you just need to have Falcon development files (or a complete Falcon development environment) setup on your machine. Then, cmake . make make install should do. modules/native/dbi/TODO000066400000000000000000000014451176363201700152540ustar00rootroot00000000000000MISCELLANEOUS: * Unit tests including "dbi standard" test that will test required functionality from all drivers * Organize the dbi_status listings so that they are in some type of logical order. Right now I just keep appending to the end as I see the need for a new one thus they are in a very erratic order. OBJECT PERSISTENCE: * Object persistence needs to be rethought. Many potential problems have appeared along the way. I am going to leave it as is but perform a study on how this should actually look/function. * Could not determine how to store a static class variable, so I made a dangerous global DBIHandle variable in C++ that needs to be changed right away. DRIVERS: * firebird/interbase driver * mssql driver * Oracle driver modules/native/dbi/cmake/000077500000000000000000000000001176363201700156405ustar00rootroot00000000000000modules/native/dbi/cmake/FindSqlite.cmake000066400000000000000000000004451176363201700207070ustar00rootroot00000000000000find_path(Sqlite_INCLUDE_DIR sqlite3.h) find_library(Sqlite_LIBRARY sqlite3) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Sqlite DEFAULT_MSG Sqlite_LIBRARY Sqlite_INCLUDE_DIR) set(Sqlite_INCLUDE_DIRS ${Sqlite_INCLUDE_DIR}) set(Sqlite_LIBRARIES ${Sqlite_LIBRARY}) modules/native/dbi/copyright000066400000000000000000000024451176363201700165200ustar00rootroot00000000000000 The Falcon Programming Language Featured modules copyright statement http://www.falconpl.org Falcon and featured modules are currently distributed under a dual licensing scheme GPL/FPLL. The GPL licensing scheme is provided to ensure compatibility with Open Source and Free Software products. It's a relative strict license which is not particularly adequate for virtual machine based scripting language interpreters and engines, but it can be chosen to develop pure GPL applications or to favor integration with already existing GPL embedding applications. The Falcon Programming Language License is specifically designed around the concept of an open source scripting language engine. It grants usability of the covered source in commercial applications, as it supports applying different and incompatible licenses to embedding and extension elements, while ensuring the openness of the core engine and of any work derived directly from it. Falcon 0.8.10 and following is released under one of GPLv2 or FPLLv1.1, at your choice. The GPLv2 license can be found at: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html The FPLLv1.1 license can be found at: http://www.falconpl.org?page_id=license_1_1 Copyright owners: (C) 2003-2008 Giancarlo Niccolai modules/native/dbi/dbi/000077500000000000000000000000001176363201700153165ustar00rootroot00000000000000modules/native/dbi/dbi/CMakeLists.txt000066400000000000000000000013051176363201700200550ustar00rootroot00000000000000#################################################################### # The Falcon Programming language # # CMake configuration file for module DBI - database interface #################################################################### if(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) endif(COMMAND cmake_policy) # Inclusion settings INCLUDE_DIRECTORIES(.) falcon_define_module( FALCON_MODULE dbi ) Message( "Adding ${FALCON_MODULE}" ) # Target ADD_LIBRARY( ${FALCON_MODULE} MODULE ${dbi_common_files} dbi.cpp dbi_ext.cpp dbi_st.cpp dbi_service.cpp dbiloaderimpl.cpp ) #Link TARGET_LINK_LIBRARIES( ${FALCON_MODULE} falcon_engine ) falcon_install_module( ${FALCON_MODULE} ) modules/native/dbi/dbi/dbi.cpp000066400000000000000000000556111176363201700165700ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: dbi.cpp * * Database interface - Main Module * ------------------------------------------------------------------- * Author: Giancarlo Niccolai * Begin: Sun Dec 2007 23 21:54:34 +0100 * * ------------------------------------------------------------------- * (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include "dbi.h" #include "version.h" #include "dbi_ext.h" #include "dbi_st.h" #include /*# @module dbi Falcon Database Interface. @after feathers @brief Main module for the Falcon DBI module suite. Falcon Database Interface (DBI) is a common infrastructure that interfaces different SQL database engines with the Falcon Programming Language. The DBI module suite presents a base interface module through which all the engines can be accessed by the script as a seamless @a Handle instance, where the nature of the underlying engine is abstracted and hidden away from the final program. Other than presenting a common interface, each engine driver is wrapped in a separate module that can be loaded or imported directly by the final user, and that offers engine-specific functionalities as well as the common DBI interface. More precisely, every database driver inherits from the base @a Handle class and may offer functionalities that cover more adequately the specific engine that they interface. @note At version 1.x, all the modules just cover the basic DBI requirements. Coverage of engine-specific functionalities is due for version 2.0. @section dbi_load DBI foreign modules architecture The DBI module automatically finds and instantiates the proper subclass of the @a Handle object through the @a connect Function. A "database resource locator" (DRL), which is a string describing connection parameters, must be provided to the @a connect function; the first element of the DRL is a database name, separated from the rest of the DRL by a colon, which must mirror the name of a DBI enabled module (usually a binary module in a .dll/.so/.dylib file), where the DBI @a Handle class is derived. Other parameters of the connection string are provided in "=" format, and separated by semicolons. For example, to connect a local @a http://sqlite3.org/ SQLite3 database, the DRL is the following: @code import from dbi hdb = dbi.connect( "sqlite3:db=/home/user/mydb.db" ) @endcode DRL strings can have driver-specific parameters, but the following parameters are known by all the drivers (even if not necessarily used): - **db**: The database name or path (depending on the underlying driver model) - **uid**: User ID (user name, account or similar). - **pwd**: Password. To perform a password-less connection, don't provide this parameter. To send an empty password, pass the value as "pwd=;" - **host**: Host where to connect, in case the database engine is network based. When not given, it defaults to "nothing", or in other words, the target engine default is picked. - **port**: TCP Port where to connect, in case the database engine is network based. It is passed untraslated to the engine, so it must respect the values that the engine may accept. If not given, the default setting for the required engine is used. @section dbi_settings Common DBI settings As a database abstaction layer, DBI tries to enforce a set of policies that stay common across different database engines, causing different engines with different defaults to behave coherently. If the database is not able to support a certain default behavior that DBI tries to apply to it, then the default is ignored. In case the DBI user tries to explicitly enforce a non-supported policy by setting the @a dbi_opts "connection options", then a @a DBIError is raised. DBI options can be set at connection through a second parameter or during the rest of the work through the @a Handle.options method. Options can be set through a string whose content is in the same format of the DRI (=;...) In general, connections are opened with any kind of autocommit mechanism turned off. An explicit @a Handle.commit must issued if the operations perfomred on the database are meant to be actually saved; closing the database or exiting from the virtual machine in an unclean way causes an implicit rollback of any pending change. Autocommit can be turned on through the "autocommit=on" option. Queries are fully fetched client-side before the control is returned to the script. Not all the engines can change this behavior, but the "prefetch" option can be used to keep the resultset in the server (prefetch=none) or to transfer the resultset only if it is smaller than a given amount (prefetch=N). In this second case, the DBI module will try to transfer N records at a time, if possible. @section dbi_methods DBI common methods and usage patterns As a database layer abstraction, DBI tries to perform operations differently handled under different engines in a way that can be considered valid across the widest range of databases possible. DBI distinguishes between SQL queries (operations meant to return recordsets, as, for example, "select"), SQL statements not meant to return any recordset and stored procedure calls, which may or may not return a recordset. In the DBI framework, prepared statements cannot be seen as queries; when the engine supports queries as prepared statements, this is internally managed by the DBI engine. To perform queries, the user should call the @a Handle.query method, that will return a @a Recordset instance (even if empty), nil if the query didn't return any recordset. If the stored procedure can produce resultset even when not being invoked from an explicit "select" statement (that may be invoked by a falcon), @a Handle.call may return a @a Recordset instance. It will return @b nil if this is not the case. Finally, the @a Handle.prepare method returns an instance of the @a Statement class, on which multiple @a Statement.execute can be invoked. As different database engine behave very differently on this regard, the base @a Statement.execute method never returns any recordset. @subsection dbi_pos_params Positional parameters All the four SQL command methods (query and prepare) accept positional parameters as question mark placeholders; the SQL parameters can be then specificed past the first parameter (except in the case of the @a Handle.prepare, where the parameters are to be specified in through the returned @a Statement class). If the underlying engine supports positional parameters, then DBI uses the database driver specific functions to pass the parameters directly to the engine, otherwise the parameters are transformed into SQL aware string values and inserted in the statement prior passing it to the underlying functions. In case of engines using different conventions for positional parameters, while the engine-specific convention can be used, the engine ensures that the question mark can be used as a positional parameter indicator, and eventually turns it into the engine-specific placeholder prior passing it to the underlying functions. When the parameters are stored in a Falcon array, the array call semantic can be used as in the following example: @code import from dbi dbh = dbi.connect( "..." ) limits = [ 123, "2010-3-1", "2010-4-1"] // select all the data in the limits dbr = (.[ dbh.query " select * from mytab where f1 < ? and date_start >= ? and date_end <= ?" ] + limits)() @endcode The above code is semantically equivalent to calling the @ dbh.query method applying the parameters specified in the @b limits array. @subsection dbi_fetching Fetching data from queries Fetch is the basic operation that allows to retreive data from queries. Every succesful query or stored procedure call returning data generates a @a Record instance that can be iteratively fetched for all the data to be returned. The @a Recordset.fetch method accepts a value where the incoming query data will be stored. Recycling the same input parameter allows to minimize the memory requirements that a script needs to fetch a recordset. The following example is the most common pattern to retreive all the data stored in a table: @code import from dbi dbh = dbi.connect( "..." ) dbr = dbh.query( "select * from mytable" ) row = [] while dbr.fetch( row ) > row.describe() end @endcode A @a Recordset instance provides method to retreive informations about the size of the recordset, the list of the retreived colums, their type and so on. In case the structure of the table is unknwon, it may be useful to invoke @a Recordset.fetch passing an empty dictionary; this will fill the dictionary with column-name => column-value pairs: @code import from dbi dbh = dbi.connect( "..." ) dbr = dbh.query( "select * from mytable" ) > "Data in the first row: " inspect( dbr.fetch( [=>] ) @endcode The @a Recordset.fetch method can also accept a Table instance; if not yet configured, the table will get shaped on the colums retrieved from the query, and will be filled with all the data from the recordset in one step: @code import from dbi dbh = dbi.connect( "..." ) dbr = dbh.query( "select * from mytable" ) // dbr.fetch returns the same object passed as the parameter // the [3] accessor gets the fourth row, which is an array > "Data in the fourth row: " + dbr.fetch( Table() )[3].describe() @endcode @section dbi_value_types Database and Falcon values Databases have different, often non-standard data-types that must be mapped into Falcon item types when they must be translated into SQL parameters or when they are returned as SQL query results. DBI transform database data types into falcon items, and eventually into Falcon standard language class instances, using the nearest data type. The following list indicates how data coming from and going to the database is transformed in/to Falcon item types. - @b nil: This values indicates a NULL database value. - @b integer: Falcon 64-bit integers are used for all the integer-based operations. When they are used as parameters to be stored in fields with smaller precision, the values may be truncated. - @b numeric: Falcon 64-bit floating point values are used to store REAL, FLOAT and DOUBLE data types, as well as fixed-point data types as DECIMAL or FIXED. If the precision of the underlying database type is smaller than the IEEE 64 bit floating point variable type (a C double data type), then the value may be truncated or may lose precision. If it has a greater precision than the Falcon data type, then it's the Falcon output variable the one that may get truncated. In that case, consider using the "string" parameter for the database connection (see below). - @b string: Text based fields as fixed or variable length CHAR fields or text-oriented blobs can be updated using string values and are saved in output to Falcon strings. If the database support text encoding, then the transforamation between Falcon strings and underlying encoding is transparent (in both the directions). If the encoding is not supported, or the field is a binary-oriented CHAR or encoding neutral, then the data retrieved in the string must be transcoded by the Falcon application with the usual means (using a transcoded string stream or using the transcodeTo/transcodeFrom string methods). - @b MemBuf: Falcon memory buffers can be used to store binary data into binary blobs, and are created when reading binary blob fields from the database. Usually, they can also be sent to text fields, in which case they will be stored in the database as binary sequences without any database conversion. - @b TimeStamp: Falcon TimeStamp standard class instances can be used to store date and time related datatype, and they are created when retrieving this kind of fields. Timezones are ignored, and they are not restored when reading informations from the database. When storing values into database fields that reprsent time-related values but provide just part of the information reported by TimeStamp, extra data is discarded. When reading fields containing only partial time information, unexpressed data is zeroed. So, when reading a DATE sql field, the hour, minute, second and millisecond fields of the resulting TimeStamp instance are zeroed. - @b Object: When presenting any other object as an input field in a SQL statement, the Object.toString method is applied and the result is sent to the database driver instead. The "string=on" option can be specified in the connection parameter or database handle option (see @a Handle.options) to have all the results of the queries returned as string values, except for NULL and binary blobs, that are still returned as @b nil and @b MemBuf items. If the underlying engine supports this method natively and the extracted data should just be represented on output, or if the database engine provides some information that cannot be easily determined after the automatic Value-to-Item translation, this modality may be extremely useful. @beginmodule dbi */ // Instantiate the loader service Falcon::DBILoaderImpl theDBIService; // the main module FALCON_MODULE_DECL { #define FALCON_DECLARE_MODULE self // Module declaration Falcon::Module *self = new Falcon::Module(); self->name( "dbi" ); self->engineVersion( FALCON_VERSION_NUM ); self->version( VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION ); //==================================== // Message setting #include "dbi_st.h" // main factory function self->addExtFunc( "connect", &Falcon::Ext::DBIConnect )-> addParam("params")->addParam("queryops"); /*# @class Statement @brief Prepared statements abstraction class This class represents a statement prepared through the @a Handle.prepare method which is ready to be excecuted multiple times. When done, use the @a Statement.close method to release the resources and refresh the database connection status. @note Closing the database handle while the statement is still open and in use may lead to various kind of errors. It's a thing that should be generally avoided. */ Falcon::Symbol *stmt_class = self->addClass( "%Statement", false ); // private class stmt_class->setWKS( true ); self->addClassMethod( stmt_class, "execute", &Falcon::Ext::Statement_execute ); self->addClassMethod( stmt_class, "aexec", &Falcon::Ext::Statement_aexec ).asSymbol()-> addParam( "params" ); self->addClassMethod( stmt_class, "reset", &Falcon::Ext::Statement_reset ); self->addClassMethod( stmt_class, "close", &Falcon::Ext::Statement_close ); self->addClassProperty( stmt_class, "affected" ).setReflectFunc( &Falcon::Ext::Statement_affected ); /*# @class Handle @brief DBI connection handle returned by @a connect. This is the main database interface connection abstraction, which allows to issue SQL statements and inspect the result of SQL queries. */ // create the base class DBIHandler for falcon Falcon::Symbol *handler_class = self->addClass( "%Handle", true ); handler_class->setWKS( true ); self->addClassMethod( handler_class, "options", &Falcon::Ext::Handle_options ).asSymbol() ->addParam("options"); self->addClassMethod( handler_class, "query", &Falcon::Ext::Handle_query ).asSymbol()-> addParam("sql"); self->addClassMethod( handler_class, "aquery", &Falcon::Ext::Handle_aquery ).asSymbol()-> addParam("sql")->addParam("params"); self->addClassMethod( handler_class, "prepare", &Falcon::Ext::Handle_prepare ).asSymbol()-> addParam("sql"); self->addClassMethod( handler_class, "close", &Falcon::Ext::Handle_close ); self->addClassMethod( handler_class, "getLastID", &Falcon::Ext::Handle_getLastID ).asSymbol() ->addParam("name"); self->addClassMethod( handler_class, "begin", &Falcon::Ext::Handle_begin ); self->addClassMethod( handler_class, "commit", &Falcon::Ext::Handle_commit ); self->addClassMethod( handler_class, "rollback", &Falcon::Ext::Handle_rollback ); self->addClassMethod( handler_class, "expand", &Falcon::Ext::Handle_expand ).asSymbol() ->addParam("sql"); self->addClassMethod( handler_class, "lselect", &Falcon::Ext::Handle_lselect ).asSymbol() ->addParam("sql")->addParam("begin")->addParam("count"); self->addClassProperty( handler_class, "affected" ).setReflectFunc( &Falcon::Ext::Handle_affected ); /*# @class Recordset @brief Data retuned by SQL queries. The recordset class abstracts a set of data returned by SQL queries. Data can be fetched row by row into Falcon arrays or dictionaries by the @a Recordset.fetch method. In the first case, the value extracted from each column is returned in the corresponding position of the returned array (the first column value at array position [0], the second column value in the array [1] and so on). When fetching a dictionary, it will be filled with column names and values respectively as the key corresponding value entries. The @a Recordset.fetch method can also be used to retrieve all the recordset contents (or part of it) into a Falcon Table. Returned values can be of various falcon item types or classes; see the @a dbi_value_types section for further details. Other than fetching data, the @a Recordset class can be used to retrieve general informations about the recordset (as the returned column size and names). @note Closing the database handle while the recordset is still open and in use may lead to various kind of errors. It's a thing that should be generally avoided. */ // create the base class DBIRecordset for falcon Falcon::Symbol *rs_class = self->addClass( "%Recordset", false ); // private class rs_class->setWKS( true ); self->addClassMethod( rs_class, "discard", &Falcon::Ext::Recordset_discard ).asSymbol()-> addParam( "count" ); self->addClassMethod( rs_class, "fetch",&Falcon::Ext::Recordset_fetch ).asSymbol()-> addParam( "item" )->addParam( "count" ); self->addClassMethod( rs_class, "do", &Falcon::Ext::Recordset_do ).asSymbol()-> addParam( "cb" )->addParam( "item" ); self->addClassMethod( rs_class, "next", &Falcon::Ext::Recordset_next ); self->addClassMethod( rs_class, "getCurrentRow", &Falcon::Ext::Recordset_getCurrentRow ); self->addClassMethod( rs_class, "getRowCount", &Falcon::Ext::Recordset_getRowCount ); self->addClassMethod( rs_class, "getColumnCount", &Falcon::Ext::Recordset_getColumnCount ); self->addClassMethod( rs_class, "getColumnNames", &Falcon::Ext::Recordset_getColumnNames ); self->addClassMethod( rs_class, "close", &Falcon::Ext::Recordset_close ); /*# @class DBIError @brief DBI specific error. Inherited class from Error to distinguish from a standard Falcon error. In many cases, DBIError.extra will contain the SQL query that caused the problem. Error code is one of the following: - DBIError.COLUMN_RANGE - DBIError.INVALID_DRIVER - DBIError.NOMEM - DBIError.CONNPARAMS - DBIError.CONNECT - DBIError.QUERY - DBIError.QUERY_EMPTY - DBIError.OPTPARAMS - DBIError.NO_SUBTRANS - DBIError.NO_MULTITRANS - DBIError.UNPREP_EXEC - DBIError.BIND_SIZE - DBIError.BIND_MIX - DBIError.EXEC - DBIError.FETCH - DBIError.UNHANDLED_TYPE - DBIError.RESET - DBIError.BIND_INTERNAL - DBIError.TRANSACTION - DBIError.CLOSED_STMT - DBIError.CLOSED_RSET - DBIError.CLOSED_DB - DBIError.DB_NOTFOUND - DBIError.CONNECT_CREATE */ // create the base class DBIError for falcon Falcon::Symbol *error_class = self->addExternalRef( "Error" ); // it's external Falcon::Symbol *dbierr_cls = self->addClass( "DBIError", &Falcon::Ext::DBIError_init ); dbierr_cls->setWKS( true ); dbierr_cls->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); // exporting error codes self->addClassProperty( dbierr_cls, "COLUMN_RANGE").setInteger( FALCON_DBI_ERROR_COLUMN_RANGE); self->addClassProperty( dbierr_cls, "INVALID_DRIVER" ).setInteger(FALCON_DBI_ERROR_INVALID_DRIVER); self->addClassProperty( dbierr_cls, "NOMEM" ).setInteger(FALCON_DBI_ERROR_NOMEM); self->addClassProperty( dbierr_cls, "CONNPARAMS" ).setInteger(FALCON_DBI_ERROR_CONNPARAMS); self->addClassProperty( dbierr_cls, "CONNECT" ).setInteger(FALCON_DBI_ERROR_CONNECT); self->addClassProperty( dbierr_cls, "QUERY" ).setInteger(FALCON_DBI_ERROR_QUERY); self->addClassProperty( dbierr_cls, "QUERY_EMPTY" ).setInteger(FALCON_DBI_ERROR_QUERY_EMPTY); self->addClassProperty( dbierr_cls, "OPTPARAMS" ).setInteger(FALCON_DBI_ERROR_OPTPARAMS); self->addClassProperty( dbierr_cls, "NO_SUBTRANS" ).setInteger(FALCON_DBI_ERROR_NO_SUBTRANS); self->addClassProperty( dbierr_cls, "NO_MULTITRANS" ).setInteger(FALCON_DBI_ERROR_NO_MULTITRANS); self->addClassProperty( dbierr_cls, "UNPREP_EXEC" ).setInteger(FALCON_DBI_ERROR_UNPREP_EXEC ); self->addClassProperty( dbierr_cls, "BIND_SIZE" ).setInteger(FALCON_DBI_ERROR_BIND_SIZE ); self->addClassProperty( dbierr_cls, "BIND_MIX" ).setInteger(FALCON_DBI_ERROR_BIND_MIX ); self->addClassProperty( dbierr_cls, "EXEC" ).setInteger(FALCON_DBI_ERROR_EXEC ); self->addClassProperty( dbierr_cls, "FETCH" ).setInteger(FALCON_DBI_ERROR_FETCH ); self->addClassProperty( dbierr_cls, "UNHANDLED_TYPE" ).setInteger(FALCON_DBI_ERROR_UNHANDLED_TYPE ); self->addClassProperty( dbierr_cls, "RESET" ).setInteger(FALCON_DBI_ERROR_RESET); self->addClassProperty( dbierr_cls, "BIND_INTERNAL" ).setInteger(FALCON_DBI_ERROR_BIND_INTERNAL ); self->addClassProperty( dbierr_cls, "TRANSACTION" ).setInteger(FALCON_DBI_ERROR_TRANSACTION ); self->addClassProperty( dbierr_cls, "CLOSED_STMT" ).setInteger(FALCON_DBI_ERROR_CLOSED_STMT ); self->addClassProperty( dbierr_cls, "CLOSED_RSET" ).setInteger(FALCON_DBI_ERROR_CLOSED_RSET ); self->addClassProperty( dbierr_cls, "CLOSED_DB" ).setInteger(FALCON_DBI_ERROR_CLOSED_DB ); self->addClassProperty( dbierr_cls, "DB_NOTFOUND" ).setInteger(FALCON_DBI_ERROR_DB_NOTFOUND ); self->addClassProperty( dbierr_cls, "CONNECT_CREATE").setInteger(FALCON_DBI_ERROR_CONNECT_CREATE ); // service publication self->publishService( &theDBIService ); // we're done return self; } /* end of dbi.cpp */ modules/native/dbi/dbi/dbi.h000066400000000000000000000015421176363201700162270ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: dbi.h * * Short description * ------------------------------------------------------------------- * Author: Giancarlo Niccolai and Jeremy Cowgar * Begin: Sun, 23 Dec 2007 20:33:57 +0100 * * ------------------------------------------------------------------- * (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #ifndef DBI_H #define DBI_H #include #include namespace Falcon { /** * Load the DBI driver. */ class DBILoaderImpl: public DBILoader { public: DBILoaderImpl(); ~DBILoaderImpl(); virtual DBIService *loadDbProvider( VMachine *vm, const String &provName ); }; } // Singleton instance. extern Falcon::DBILoaderImpl theDBIService; #endif /* end of dbi.h */ modules/native/dbi/dbi/dbi_ext.cpp000066400000000000000000001006701176363201700174440ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: dbi_ext.cpp * * DBI Falcon extension interface * ------------------------------------------------------------------- * Author: Giancarlo * Begin: Sun, 23 May 2010 20:17:58 +0200 * * ------------------------------------------------------------------- * (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include #include #include #include #include #include "dbi.h" #include "dbi_ext.h" #include "dbi_st.h" #include /*# @beginmodule dbi */ namespace Falcon { namespace Ext { /****************************************************************************** * Main DBIConnect *****************************************************************************/ /*# @function connect @brief Connect to a database server through a DBI driver. @param conn SQL connection string. @optparam queryops Default transaction options to be applied to operations performed on the returned handler returned handle. @return an instance of @a Handle. @raise DBIError if the connection fails. This function acts as a front-end to dynamically determine the DBI driver that should be used to connect to a determined database. The @b conn connection string is in the format described in @a dbi_load. An optional parameter @b queryops can be given to change some default value in the connection. @see Handle.options */ void DBIConnect( VMachine *vm ) { Item *paramsI = vm->param(0); Item *i_tropts = vm->param(1); if ( paramsI == 0 || ! paramsI->isString() || ( i_tropts != 0 && ! i_tropts->isString() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S,[S]" ) ); } String *params = paramsI->asString(); String provName = *params; String connString = ""; uint32 colonPos = params->find( ":" ); if ( colonPos != csh::npos ) { provName = params->subString( 0, colonPos ); connString = params->subString( colonPos + 1 ); } DBIHandle *hand = 0; try { DBIService *provider = theDBIService.loadDbProvider( vm, provName ); // if it's 0, the service has already raised an error in the vm and we have nothing to do. fassert( provider != 0 ); hand = provider->connect( connString ); if( i_tropts != 0 ) { hand->options( *i_tropts->asString() ); } // great, we have the database handler open. Now we must create a falcon object to store it. CoreObject *instance = provider->makeInstance( vm, hand ); vm->retval( instance ); } catch( DBIError* error ) { delete hand; throw error; } } /********************************************************** Statement class **********************************************************/ /*# @method execute Statement @brief Executes a repeated statement. @optparam ... The data to be passed to the repeated statement. @return An instance of @a Recorset if the query generated a recorset. @raise DBIError if the database engine reports an error. This method executes a statement that has been prepared through the @a Handle.prepare method. If the prepared statement could return a recordset, it is returned. The number of affected rows will be stored also in the @a Statement.affected property. @see Handle.prepare */ void Statement_execute( VMachine *vm ) { CoreObject *self = vm->self().asObject(); DBIStatement *dbt = static_cast( self->getUserData() ); DBIRecordset* res; if( vm->paramCount() != 0 ) { ItemArray params( vm->paramCount() ); for( int32 i = 0; i < vm->paramCount(); i++) { params.append( *vm->param(i) ); } res = dbt->execute( ¶ms ); } else { res = dbt->execute(); } if( res != 0 ) { Item* rset_item = vm->findWKI( "%Recordset" ); fassert( rset_item != 0 ); fassert( rset_item->isClass() ); CoreObject* rset = rset_item->asClass()->createInstance(); rset->setUserData( res ); vm->retval( rset ); } else { vm->retnil(); } } /*# @method aexec Statement @brief Executes a repeated statement. @optparam params The data to be passed to the repeated statement. @return An instance of @a Recorset if the query generated a recorset. @raise DBIError if the database engine reports an error. This method executes a statement that has been prepared through the @a Handle.prepare method. If the prepared statement could return a recordset, it is returned. Instead of passing the extra parameters to the underlying query, this method sends the value of a single array paramter. The number of affected rows will be stored also in the @a Statement.affected property. @see Handle.prepare @see Handle.execute */ void Statement_aexec( VMachine *vm ) { Item* i_params = vm->param(0); if( i_params == 0 || ! i_params->isArray() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "A" ) ); } CoreObject *self = vm->self().asObject(); DBIStatement *dbt = static_cast( self->getUserData() ); DBIRecordset* res; res = dbt->execute( &i_params->asArray()->items() ); if( res != 0 ) { Item* rset_item = vm->findWKI( "%Recordset" ); fassert( rset_item != 0 ); fassert( rset_item->isClass() ); CoreObject* rset = rset_item->asClass()->createInstance(); rset->setUserData( res ); vm->retval( rset ); } else { vm->retnil(); } } /*# @property affected Statement Indicates the amount of rows affected by the last query performed on this statement (through the @a Statement.execute method). Will be 0 if none, -1 if unknown, or a positive value if the number of rows can be determined. */ void Statement_affected(CoreObject *instance, void *user_data, Item &property, const PropEntry& entry ) { DBIStatement *dbt = static_cast( user_data ); property = dbt->affectedRows(); } /*# @method reset Statement @brief Resets this statement @raise DBIError if the statement cannot be reset. Some Database engines allow to reset a statement and retry to issue (execute) it without re-creating it anew. If the database engine doesn't support this feature, a DBIError will be thrown. */ void Statement_reset( VMachine *vm ) { CoreObject *self = vm->self().asObject(); DBIStatement *dbt = static_cast( self->getUserData() ); dbt->reset(); } /*# @method close Statement @brief Close this statement. Statements are automatically closed when the statement object is garbage collected, but calling explicitly this helps to reclaim data as soon as it is not necessary anymore. */ void Statement_close( VMachine *vm ) { CoreObject *self = vm->self().asObject(); DBIStatement *dbt = static_cast( self->getUserData() ); dbt->close(); } /********************************************************** Handler class **********************************************************/ /*# @method options Handle @brief Sets the default options for SQL operations performed on this handle. @param options The string containing the transaction options. @raise DBIError if the options are invalid. This method sets the default options that are used to create new transactions or performing statements. The options are set using a string where the settings are specified as = pairs. Common options to all drivers include the followings: - prefetch: number of records to be pre-fetched at each query. The value may be "all" to wholly fetch queries locally, "none" to prefetch none or an arbitrary number of rows to be read from the server. By default, it's "all". - autocommit: Performs a transaction commit after each sql command. Can be "on" or "off"; it's "off" by default. - cursor: Number of records returned by a query that should trigger the creation of a server side cursor. Can be "none" to prevent creation of server side cursor (the default) "all" to always create a cursor or an arbitrary number to create a server side cursor only if the query returns at least the indicated number of rows. - strings: All the data in the resultset is returned as a string, without transformation into Falcon items. In case the data is queried to just perform a direct output without particular needs for string formatting, or if the needed data formatting is performed by the engine, this option may improve performance considerably. Also, some data types may be not easily represented by Falcon types, and having the native engine representation may be crucial. If the database engine doesn't offer this option natively, the driver may ignore it or emulate it. Different database drivers may specify more transaction options; refer to their documentation for further parameters. */ void Handle_options( VMachine *vm ) { Item* i_options = vm->param(0); if( i_options == 0 || ! i_options->isString() ) { throw new ParamError(ErrorParam( e_inv_params, __LINE__ ) .extra( "S") ); } CoreObject *self = vm->self().asObject(); DBIHandle *dbh = static_cast( self->getUserData() ); dbh->options( *i_options->asString() ); } /*# @method begin Handle @brief Issues a "begin work", "start transaction" or other appropriate command. @raise DBIError in case of error in starting the transaction. This method helps creating code portable across different database engines. It just issues the correct command for the database engine to start a transaction. It is not mandatory to manage transactions through this method, and this method can be intermixed with direct calls to @a Handle.perform calling the database engine commands directly. If the database engine doesn't support transaction, the command is ignored. */ void Handle_begin( VMachine *vm ) { CoreObject *self = vm->self().asObject(); DBIHandle *dbh = static_cast( self->getUserData() ); dbh->begin(); } /*# @method commit Handle @brief Issues a "commit work" command. @raise DBIError in case of error in starting the transaction. This method helps creating code portable across different database engines. It just issues the correct command for the database engine to commit the current transaction. It is not mandatory to manage transactions through this method, and this method can be intermixed with direct calls to @a Handle.perform calling the database engine commands directly. If the database engine doesn't support transaction, the command is ignored. */ void Handle_commit( VMachine *vm ) { CoreObject *self = vm->self().asObject(); DBIHandle *dbh = static_cast( self->getUserData() ); dbh->commit(); } /*# @method rollback Handle @brief Issues a "rollback work" command. @raise DBIError in case of error in starting the transaction. This method helps creating code portable across different database engines. It just issues the correct command for the database engine to roll back the current transaction. It is not mandatory to manage transactions through this method, and this method can be intermixed with direct calls to @a Handle.perform calling the database engine commands directly. If the database engine doesn't support transaction, the command is ignored. */ void Handle_rollback( VMachine *vm ) { CoreObject *self = vm->self().asObject(); DBIHandle *dbh = static_cast( self->getUserData() ); dbh->rollback(); } /*# @method lselect Handle @brief Returns a "select" query configured to access a sub-recordset. @param sql The query (excluded the "select" command). @optparam begin The first row to be returned (0 based). @optparam count The number of rows to be returned. @return A fully configured sql command that can be fed into @a Handle.query This method should create a "select" query adding the commands and/or the parameters needed by the engine to limit the resultset to a specified part part of the dataset. The query parameter must be a complete query EXCEPT for the "select" command, which is added by the engine. It must NOT terminate with a ";", which, in case of need is added by the engine. For example, to limit the following query to rows 5-14 (row 5 is the 6th row): @code SELECT field1, field2 FROM mytable WHERE key = ?; @endcode write this Falcon code: @code // dbh is a DBI handle rset = dbh.query( dbh.lselect( "field1, field2 FROM mytable WHERE key = ?", 5, 10 ), "Key value" ) @endcode The @b count parameter can be 0 or @b nil to indicate "from @b begin to the end". It's not possible to return the n-last elements; to do that, reverse the query ordering logic. @note If the engine doesn't support limited recordsets, the limit parameters are ignored. */ void Handle_lselect( VMachine *vm ) { Item* i_sql = vm->param(0); Item* i_nBegin = vm->param(1); Item* i_nCount = vm->param(2); if( i_sql == 0 || ! i_sql->isString() || ( i_nBegin != 0 && ! (i_nBegin->isOrdinal() || i_nBegin->isNil() ) ) || ( i_nCount != 0 && ! i_nCount->isOrdinal() ) ) { throw new ParamError(ErrorParam( e_inv_params, __LINE__ ) .extra( "S,[N],[N]") ); } CoreObject *self = vm->self().asObject(); DBIHandle *dbh = static_cast( self->getUserData() ); CoreString* result = new CoreString; dbh->selectLimited( *i_sql->asString(), i_nBegin == 0 ? 0 : i_nBegin->forceInteger(), i_nCount == 0 ? 0 : i_nCount->forceInteger(), *result ); vm->retval( result ); } /*# @method close DBIHandle @brief Close the database handle. Every further operation on this object or on any related object will cause an exception to be raised. */ void Handle_close( VMachine *vm ) { CoreObject *self = vm->self().asObject(); DBIHandle *dbh = static_cast( self->getUserData() ); dbh->close(); } /*# @property affected Handle Indicates the amount of rows affected by the last query performed on this connection. Will be 0 if none, -1 if unknown, or a positive value if the number of rows can be determined. */ void Handle_affected(CoreObject *instance, void *user_data, Item &property, const PropEntry& entry ) { DBIHandle *dbt = static_cast( user_data ); property = dbt->affectedRows(); } /*# @method getLastID Handle @brief Get the ID of the last record inserted. @optparam name A sequence name that is known by the engine. @return The value of the last single-field numeric key inserted in this transaction. This is database dependent but so widely used, it is included in the DBI module. Some databases such as MySQL only support getting the last inserted ID globally in the database server while others like PostgreSQL allow you to get the last inserted ID of any table. Thus, it is suggested that you always supply the sequence id as which to query. DBI drivers such as MySQL are programmed to ignore the extra information and return simply the last ID inserted into the database. */ void Handle_getLastID( VMachine *vm ) { CoreObject *self = vm->self().asObject(); DBIHandle *dbh = static_cast( self->getUserData() ); if ( vm->paramCount() == 0 ) vm->retval( dbh->getLastInsertedId() ); else { Item *sequenceNameI = vm->param( 0 ); if ( sequenceNameI == 0 || ! sequenceNameI->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S" ) ); return; } String sequenceName = *sequenceNameI->asString(); vm->retval( dbh->getLastInsertedId( sequenceName ) ); } } static void internal_stmt_open( VMachine* vm, DBIStatement* trans ) { Item *trclass = vm->findWKI( "%Statement" ); fassert( trclass != 0 && trclass->isClass() ); CoreObject *oth = trclass->asClass()->createInstance(); oth->setUserData( trans ); vm->retval( oth ); } /*# @method query Handle @brief Execute a SQL query bound to return a recordset. @param sql The SQL query @optparam ... Parameters for the query @return an instance of @a Recordset, or nil. @raise DBIError if the database engine reports an error. On a succesful query, the property @a Handle.affected is assumes the count of affected rows, or -1 if the driver can't provide this information. */ void Handle_query( VMachine *vm ) { Item* i_sql = vm->param(0); if ( i_sql == 0 || ! i_sql->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S, ..." ) ); } CoreObject *self = vm->self().asObject(); DBIHandle *dbt = static_cast( self->getUserData() ); DBIRecordset* res = 0; int32 pCount = vm->paramCount(); if( pCount > 1 ) { ItemArray params( pCount - 1 ); for( int32 i = 1; i < vm->paramCount(); i++) { params.append( *vm->param(i) ); } // Query may throw. res = dbt->query( *i_sql->asString(), ¶ms ); } else { res = dbt->query( *i_sql->asString() ); } if( res != 0 ) { Item* rset_item = vm->findWKI( "%Recordset" ); fassert( rset_item != 0 ); fassert( rset_item->isClass() ); CoreObject* rset = rset_item->asClass()->createInstance(); rset->setUserData( res ); vm->retval( rset ); } } /*# @method aquery Handle @brief Execute a SQL query bound to return a recordset. @param sql The SQL query @param params Values to be passed to the query in an array. @return an instance of @a Recordset, or nil. @raise DBIError if the database engine reports an error. On a succesful query, the property @a Handle.affected is assumes the count of affected rows, or -1 if the driver can't provide this information. */ void Handle_aquery( VMachine *vm ) { Item* i_sql = vm->param(0); Item* i_params = vm->param(1); if ( i_sql == 0 || ! i_sql->isString() || i_params == 0 || ! i_params->isArray() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S,A" ) ); } CoreObject *self = vm->self().asObject(); DBIHandle *dbt = static_cast( self->getUserData() ); DBIRecordset* res = 0; res = dbt->query( *i_sql->asString(), &i_params->asArray()->items() ); if( res != 0 ) { Item* rset_item = vm->findWKI( "%Recordset" ); fassert( rset_item != 0 ); fassert( rset_item->isClass() ); CoreObject* rset = rset_item->asClass()->createInstance(); rset->setUserData( res ); vm->retval( rset ); } } /*# @method expand Handle @brief Expands a sql query with provided parameters. @param sql The SQL query @optparam ... Parameters for the query @return A string containing a complete SQL statement. @raise DBIError in case the expansion fails. Some underlying database engine may not be consistently working with the Falcon types used as parameters. As such, this utility can be used to create complete query strings that doesn't require driver-side parameter binding and expansion. */ void Handle_expand( VMachine *vm ) { Item* i_sql = vm->param(0); if ( i_sql == 0 || ! i_sql->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S, ..." ) ); } CoreObject *self = vm->self().asObject(); DBIHandle *dbt = static_cast( self->getUserData() ); DBIRecordset* res = 0; int32 pCount = vm->paramCount(); CoreString* target = new CoreString; ItemArray params( pCount - 1 ); for( int32 i = 1; i < vm->paramCount(); i++) { params.append( *vm->param(i) ); } // May throw dbt->sqlExpand( *i_sql->asString(), *target, params ); vm->retval( target ); } /*# @method prepare Handle @brief Prepares a repeated statement. @param sql The SQL query @raise DBIError if the database engine reports an error. This method creates a "prepared statement" that can be iteratively called with different parameters to perform multiple time the same operations. Typically, the SQL statement will be a non-query data statement meant */ void Handle_prepare( VMachine *vm ) { Item* i_sql = vm->param(0); if ( i_sql == 0 || ! i_sql->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S, ..." ) ); } CoreObject *self = vm->self().asObject(); DBIHandle *dbt = static_cast( self->getUserData() ); DBIStatement* stmt = dbt->prepare( *i_sql->asString() ); internal_stmt_open(vm, stmt); } /****************************************************************************** * Recordset class *****************************************************************************/ static void internal_record_fetch( VMachine* vm, DBIRecordset* dbr, Item& target ) { int count = dbr->getColumnCount(); if( target.isArray() ) { CoreArray* aret = target.asArray(); aret->resize( count ); for ( int i = 0; i < count; i++ ) { dbr->getColumnValue( i, aret->items()[i] ); } vm->retval( aret ); } else if( target.isDict() ) { CoreDict* dret = target.asDict(); for ( int i = 0; i < count; i++ ) { String fieldName; dbr->getColumnName( i, fieldName ); Item* value = dret->find( Item(&fieldName) ); if( value == 0 ) { Item v; dbr->getColumnValue( i, v ); CoreString* key = new CoreString(fieldName); key->bufferize(); dret->put( key, v ); } else { dbr->getColumnValue( i, *value ); } } vm->retval( dret ); } /* else { CoreTable* tbl = dyncast(target.asObject()->getFalconData()); ItemArray iaCols( count ); if( tbl->order() == CoreTable::noitem ) { String* fieldName = new String[count]; for( int i = 0; i < count; ++ i ) { dbr->getColumnName( i, fieldName[i] ); iaCols.append( fieldName ); } if( ! tbl->setHeader( iaCols ) ) { delete[] fieldName; throw new DBIError( ErrorParam( FALCON_DBI_ERROR_FETCH, __LINE__ ) .extra("Incompatible table columns" ) ); } delete[] fieldName; } else { if( tbl->order() != (unsigned) count ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_FETCH, __LINE__ ) .extra("Incompatible table columns" ) ); } } // put in the values do { CoreArray* row = new CoreArray(); row->resize( count ); for( int i = 0; i < count; ++ i ) { dbr->getColumnValue( i, row->at( i ) ); } tbl->insertRow( row ); } while( dbr->fetchRow() ); vm->retval( target ); } */ } /*# @method fetch Recordset @brief Fetches a record and advances to the next. @optparam item Where to store the fetched record. @optparam count Number of rows fetched when @b item is a Table. @raise DBIError if the database engine reports an error. @return The @b item passed as a paramter filled with fetched data or nil when the recordset is terminated. If @b item is not given, a newly created array is filled with the fetched data and then returned, otherwise, the given item is filled with the data from the fetch. The @b item may be: - An Array. - A Dictionary. */ void Recordset_fetch( VMachine *vm ) { Item *i_data = vm->param( 0 ); Item *i_count = vm->param( 1 ); // prepare the array in case of need. if( i_data == 0 ) { vm->addLocals(1); i_data = vm->local(0); *i_data = new CoreArray(); // if i_data is zero, then i_count is also zero, so we don't have to worry // about the VM stack being moved. } /* if ( ! ( i_data->isArray() || i_data->isDict() || i_data->isOfClass("Table") ) || (i_count != 0 && ! i_count->isOrdinal()) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "[A|D|Table],[N]" ) ); } */ if ( ! ( i_data->isArray() || i_data->isDict() ) || (i_count != 0 && ! i_count->isOrdinal()) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "[A|D],[N]" ) ); } CoreObject *self = vm->self().asObject(); DBIRecordset *dbr = static_cast( self->getUserData() ); if( ! dbr->fetchRow() ) { vm->retnil(); return; } internal_record_fetch( vm, dbr, *i_data ); } /*# @method discard Recordset @brief Discards one or more records in the recordset. @param count The number of records to be skipped. @return true if successful, false if the recordset is over. This skips the next @b count records. */ void Recordset_discard( VMachine *vm ) { Item *i_count = vm->param( 0 ); if ( i_count == 0 || ! i_count->isOrdinal() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "N" ) ); } CoreObject *self = vm->self().asObject(); DBIRecordset *dbr = static_cast( self->getUserData() ); vm->regA().setBoolean( dbr->discard( i_count->forceInteger() ) ); } /*# @method getColumnNames DBIRecordset @brief Gets the names of the recordset columns. @return an array of one or more strings, containing the names of the rows in the record sect. */ void Recordset_getColumnNames( VMachine *vm ) { CoreObject *self = vm->self().asObject(); DBIRecordset *dbr = static_cast( self->getUserData() ); int count = dbr->getColumnCount(); CoreArray* ret = new CoreArray( count ); for( int i = 0; i < count; ++i ) { CoreString* str = new CoreString; dbr->getColumnName( i, *str ); str->bufferize(); ret->append( str ); } vm->retval( ret ); } /*# @method getCurrentRow DBIRecordset @brief Returns the number of the current row. @return An integer number >= 0 if the number of the current row is known, -1 if the driver can't access this information. This method returns how many rows have been fetched before the current one. It will be -1 if the method @a Recordset.fetch has still not been called, 0 if the current row is the first one, 1 for the second and so on. */ void Recordset_getCurrentRow( VMachine *vm ) { CoreObject *self = vm->self().asObject(); DBIRecordset *dbr = static_cast( self->getUserData() ); vm->retval( dbr->getRowCount() ); } /*# @method getRowCount DBIRecordset @brief Return the number of rows in the recordset. @return An integer number >= 0 if the number of the row count row is known, -1 if the driver can't access this information. */ void Recordset_getRowCount( VMachine *vm ) { CoreObject *self = vm->self().asObject(); DBIRecordset *dbr = static_cast( self->getUserData() ); vm->retval( dbr->getRowCount() ); } /*# @method getColumnCount DBIRecordset @brief Return the number of columns in the recordset. @return An integer number > 0. */ void Recordset_getColumnCount( VMachine *vm ) { CoreObject *self = vm->self().asObject(); DBIRecordset *dbr = static_cast( self->getUserData() ); vm->retval( dbr->getColumnCount() ); } /*# @method close DBIRecordset @brief Close a recordset */ void Recordset_close( VMachine *vm ) { CoreObject *self = vm->self().asObject(); DBIRecordset *dbr = static_cast( self->getUserData() ); dbr->close(); } static bool Recordset_do_next( VMachine* vm ) { if( vm->regA().isOob() && vm->regA().isInteger() && vm->regA().asInteger() == 0 ) { return false; } CoreObject *self = vm->self().asObject(); DBIRecordset *dbr = static_cast( self->getUserData() ); if( ! dbr->fetchRow() ) { return false; } // copy, as we may disrupt the stack Item i_callable = *vm->param(0); if( vm->paramCount() == 1 ) { int count = dbr->getColumnCount(); for ( int i = 0; i < count; i++ ) { Item value; dbr->getColumnValue( i, value ); vm->pushParam( value ); } vm->callFrame( i_callable, count ); } else { internal_record_fetch( vm, dbr, *vm->param(1) ); vm->pushParam( vm->regA() ); vm->regA().setNil(); vm->callFrame( i_callable, 1 ); } return true; } /*# @method do Recordset @brief Calls back a function for each row in the recordset. @param cb The callback function that must be called for each row. @optparam item A fetchable item that will be filled and then passed to @b cb. @raise DBIError if the database engine reports an error. This method calls back a given @b cb callable item fetching one row at a time from the recordset, and then passing the data to @b cb either as parameters or as a single item. If @b item is not given, all the field values in the recordset are passed directly as parameters of the given @b cb function. If it is given, then that @b item is filled along the rules of @b Recordset.fetch and then it is passed to the @b cb item. The @b item may be: - An Array. - A Dictionary. The @b cb method may return an oob(0) value to interrupt the processing of the recordset. @b The recordset is not rewinded before starting to call @b cb. Any previously fetched data won't be passed to @b cb. */ void Recordset_do( VMachine *vm ) { Item* i_callable = vm->param(0); Item* i_extra = vm->param(1); if( i_callable == 0 || ! i_callable->isCallable() || ( i_extra != 0 && ! ( i_extra->isArray() || i_extra->isDict() || i_extra->isOfClass("Table") ) ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "C,[A|D|Table]" ) ); } vm->regA().setNil(); vm->returnHandler( Recordset_do_next ); } /*# @method next Recordset @brief Gets the next recordset in queries returning multiple recordsets. @return Another recordset on success, nil if this query didn't generate any more recordset. @raise DBIError if the database engine reports an error. Some engines may cause multiple recordsets to be generated after a single query. In that case, this method may be used to retrieve the secondary recordest after the first one has been completed. When there aren't any more recordset to be fetched, this method returns nil. The rules to access sub-recorsets may vary depending on the final engine, but usually this can be considered safe: @code rs = dbi.query( "..." ) // process the query result as usual data = [=>] while rs.fetch(data) // do things end // process the sub-queries. while (sub_rs = rs.next() ) data = [=>] while sub_rs.fetch(data) // do more things things end end rs.close() @endcode */ void Recordset_next( VMachine *vm ) { CoreObject *self = vm->self().asObject(); DBIRecordset *dbr = static_cast( self->getUserData() ); DBIRecordset *res = dbr->getNext(); if( res != 0 ) { Item* rset_item = vm->findWKI( "%Recordset" ); fassert( rset_item != 0 ); fassert( rset_item->isClass() ); CoreObject* rset = rset_item->asClass()->createInstance(); rset->setUserData( res ); vm->retval( rset ); } // else return nothing } //====================================================== // DBI error //====================================================== void DBIError_init( VMachine *vm ) { CoreObject *einst = vm->self().asObject(); if( einst->getUserData() == 0 ) einst->setUserData( new DBIError ); ::Falcon::core::Error_init( vm ); } } } /* end of dbi_ext.cpp */ modules/native/dbi/dbi/dbi_ext.h000066400000000000000000000042031176363201700171040ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: dbi_ext.h * * DBI Falcon extension interface * ------------------------------------------------------------------- * Author: Giancarlo Niccolai and Jeremy Cowgar * Begin: Sun, 23 Dec 2007 22:02:37 +0100 * * ------------------------------------------------------------------- * (C) Copyright 2007: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include #include #ifndef DBI_EXT_H #define DBI_EXT_H namespace Falcon { class VMachine; namespace Ext { //===================== // DBI Generic //===================== void DBIConnect( VMachine *vm ); //===================== // DBIBaseTrans //===================== void Statement_execute( VMachine *vm ); void Statement_aexec( VMachine *vm ); void Statement_reset( VMachine *vm ); void Statement_close( VMachine *vm ); void Statement_affected(CoreObject *instance, void *user_data, Item &property, const PropEntry& entry ); //===================== // DBI Handle //===================== void Handle_query( VMachine *vm ); void Handle_aquery( VMachine *vm ); void Handle_options( VMachine *vm ); void Handle_prepare( VMachine *vm ); void Handle_getLastID( VMachine *vm ); void Handle_close( VMachine *vm ); void Handle_begin( VMachine *vm ); void Handle_commit( VMachine *vm ); void Handle_rollback( VMachine *vm ); void Handle_lselect( VMachine *vm ); void Handle_expand( VMachine *vm ); void Handle_affected(CoreObject *instance, void *user_data, Item &property, const PropEntry& entry ); //===================== // DBI Recordset //===================== void Recordset_discard( VMachine *vm ); void Recordset_fetch( VMachine *vm ); void Recordset_do( VMachine *vm ); void Recordset_next( VMachine *vm ); void Recordset_getCurrentRow( VMachine *vm ); void Recordset_getRowCount( VMachine *vm ); void Recordset_getColumnCount( VMachine *vm ); void Recordset_getColumnNames( VMachine *vm ); void Recordset_close( VMachine *vm ); //===================== // DBI Error class //===================== void DBIError_init( VMachine *vm ); } } #endif /* end of dbi_ext.h */ modules/native/dbi/dbi/dbi_mod.h000066400000000000000000000017461176363201700170740ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: dbi_mod.h * * Helper/inner functions for DBI base. * ------------------------------------------------------------------- * Author: Giancarlo Niccolai and Jeremy Cowgar * Begin: Mon, 13 Apr 2009 18:56:48 +0200 * * ------------------------------------------------------------------- * (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #ifndef FALCON_DBI_MOD_H #define FALCON_DBI_MOD_H #include #include namespace Falcon { int dbh_itemToSqlValue( DBIHandle *dbh, const Item *i, String &value ); int dbh_realSqlExpand( VMachine *vm, DBIHandle *dbh, String &sql, int startAt=0 ); void dbh_escapeString( const String& input, String& value ); void dbh_throwError( const char* file, int line, int code, const String& desc ); void dbh_return_recordset( VMachine *vm, DBIRecordset *rec ); } #endif /* end of dbi_mod.h */ modules/native/dbi/dbi/dbi_service.cpp000066400000000000000000000010611176363201700202760ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: dbiservice.cpp * * DBI service that DBI drivers inherit from and implement * ------------------------------------------------------------------- * Author: Giancarlo Niccolai * Begin: Sat, 15 May 2010 15:40:39 +0200 * * ------------------------------------------------------------------- * (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include namespace Falcon { } /* end of dbi_service.cpp */ modules/native/dbi/dbi/dbi_st.cpp000066400000000000000000000010711176363201700172650ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_st.cpp Database interface - String table. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 09 May 2010 12:26:20 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Regular expression module - String table. */ #define FALCON_REALIZE_STRTAB #include "dbi_st.h" /* dbi_st.cpp */ modules/native/dbi/dbi/dbi_st.h000066400000000000000000000026651176363201700167440ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_st.h Database interface - String table. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 09 May 2010 12:26:20 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ /** \file Regular expression module - String table. */ #include FAL_MODSTR( dbi_msg_invalid_col, "Column out of range" ); FAL_MODSTR( dbi_msg_driver_not_found, "DBI driver service not found" ); FAL_MODSTR( dbi_msg_nomem, "Not enough memory to perform the operation" ); FAL_MODSTR( dbi_msg_connparams, "Malformed or invalid connection parameter string" ); FAL_MODSTR( dbi_msg_connect, "Connection to database failed" ); FAL_MODSTR( dbi_msg_query, "Database query error" ); FAL_MODSTR( dbi_msg_query_empty, "Query didn't return any result" ); FAL_MODSTR( dbi_msg_option_error, "Unrecognized or invalid options" ); FAL_MODSTR( dbi_msg_no_subtrans, "DBEngine doesn't support sub-transactions" ); FAL_MODSTR( dbi_msg_no_multitrans, "DBEngine doesn't support multiple transactions" ); FAL_MODSTR( dbi_msg_unprep_exec, "Called 'execute' without having previously called 'prepare'" ); FAL_MODSTR( dbi_msg_bind_mix, "Input variables in 'execute' differs from previously bound ones" ); /* dbi_st.h */ modules/native/dbi/dbi/dbiloaderimpl.cpp000066400000000000000000000041251176363201700206330ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: dbiloaderimpl.cpp * * Implementation of the DBI loader service, used mainly by this module. * ------------------------------------------------------------------- * Author: Giancarlo Niccolai * Begin: Sun, 23 Dec 2007 20:33:57 +0100 * * ------------------------------------------------------------------- * (C) Copyright 2007: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include "dbi.h" #include "dbi_st.h" #include #include #include namespace Falcon { DBILoaderImpl::DBILoaderImpl(): DBILoader( "DBILOADER" ) { } DBILoaderImpl::~DBILoaderImpl() {} DBIService *DBILoaderImpl::loadDbProvider( VMachine *vm, const String &provName ) { ModuleLoader* loader = new ModuleLoader(""); DBIService *serv = static_cast( vm->getService( "DBI_" + provName ) ); loader->addFalconPath(); Module *mod = 0; if ( serv == 0 ) { // ok, let's try to load the service try { mod = loader->loadName( "dbi."+ provName ); vm->link( mod, false ); } catch( Error * ) { delete loader; if ( mod != 0 ) mod->decref(); throw; } // the VM has linked the module, we get rid of it. mod->decref(); delete loader; //NOTE: we must actually have a local map, as we may be fed with different VMs. //We should load only one module for each type, and give to each VM a pre-loaded // module, if available. // everything went fine; the service is in the vm, but we can access it also // from the module serv = static_cast( mod->getService( "DBI_" + provName ) ); if ( serv == 0 ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_INVALID_DRIVER, __LINE__ ) .desc( FAL_STR( dbi_msg_driver_not_found ) ) .extra( "DBI_" + provName ) ); } } serv->init(); return serv; } } /* end of dbiloaderimpl.cpp */ modules/native/dbi/dbi/version.h000066400000000000000000000011621176363201700171540ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: verion.h * * DBI module version informations * ------------------------------------------------------------------- * Author: Giancarlo Niccolai and Jeremy Cowgar * Begin: dom dic 23 21:58:21 CET 2007 * * * ------------------------------------------------------------------- * (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #ifndef VERSION_H #define VERSION_H #define VERSION_MAJOR 0 #define VERSION_MINOR 9 #define VERSION_REVISION 10 #endif /* end of version.h */ modules/native/dbi/dbi_common/000077500000000000000000000000001176363201700166665ustar00rootroot00000000000000modules/native/dbi/dbi_common/dbi_common.cpp000066400000000000000000000064171176363201700215100ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_common.cpp Database Interface - common useful functions ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 16 May 2010 00:25:45 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include /****************************************************************************** * Local Helper Functions - DBH database handle *****************************************************************************/ namespace Falcon { bool dbi_itemToSqlValue( const Item &item, String &value ) { switch( item.type() ) { case FLC_ITEM_BOOL: value = item.asBoolean() ? "TRUE" : "FALSE"; return true; case FLC_ITEM_INT: value.writeNumber( item.asInteger() ); return true; case FLC_ITEM_NUM: value.writeNumber( item.asNumeric(), "%f" ); return true; case FLC_ITEM_STRING: dbi_escapeString( *item.asString(), value ); value.prepend( "'" ); value.append( "'" ); return true; case FLC_ITEM_OBJECT: { CoreObject *o = item.asObject(); //vm->itemToString( value, ??? ) if ( o->derivedFrom( "TimeStamp" ) ) { TimeStamp *ts = (TimeStamp *) o->getUserData(); value.prepend( "'" ); value.append( "'" ); return true; } return false; } case FLC_ITEM_NIL: value = "NULL"; return true; default: return false; } } void dbi_escapeString( const String& input, String& value ) { uint32 len = input.length(); uint32 pos = 0; value.reserve( len + 8 ); while( pos < len ) { uint32 chr = input.getCharAt(pos); switch( chr ) { case '\\': value.append(chr); value.append(chr); break; case '\'': value.append( '\\' ); value.append( '\'' ); break; case '"': value.append( '\\' ); value.append( '"' ); break; default: value.append( chr ); } ++pos; } } bool dbi_sqlExpand( const String& input, String& output, const ItemArray& arr ) { output.reserve( input.size() ); output.size(0); String temp; uint32 iCount = 0; uint32 pos = 0; uint32 pos1 = input.find( "?" ); while( pos1 != String::npos ) { // too many ? if ( iCount >= arr.length() ) return false; // can convert? if ( ! dbi_itemToSqlValue( arr[iCount++], temp ) ) return false; // go! output += input.subString( pos, pos1 ); output += temp; temp.size(0); // search next pos = pos1 + 1; pos1 = input.find( "?", pos ); } // did we miss some elements in the array? if ( iCount != arr.length() ) return false; output += input.subString( pos ); return true; } } /* end of dbi_mod.cpp */ modules/native/dbi/dbi_common/dbi_error.cpp000066400000000000000000000073011176363201700213420ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_error.cpp Database Interface - Error management ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 15 May 2010 23:47:36 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include namespace Falcon { DBIError::DBIError( const ErrorParam ¶ms ): Error( "DBIError", params ) { describeError(); } void DBIError::describeError() { switch( errorCode() ) { case FALCON_DBI_ERROR_COLUMN_RANGE: this->errorDescription( "Column out of range" ); break; case FALCON_DBI_ERROR_INVALID_DRIVER: this->errorDescription( "DBI driver service not found" ); break; case FALCON_DBI_ERROR_NOMEM: this->errorDescription( "Not enough memory to perform the operation" ); break; case FALCON_DBI_ERROR_CONNPARAMS: this->errorDescription( "Malformed or invalid connection parameter string" ); break; case FALCON_DBI_ERROR_CONNECT: this->errorDescription( "Connection to database failed" ); break; case FALCON_DBI_ERROR_QUERY: this->errorDescription( "Database query error" ); break; case FALCON_DBI_ERROR_QUERY_EMPTY: this->errorDescription( "Query didn't return any result" ); break; case FALCON_DBI_ERROR_OPTPARAMS: this->errorDescription( "Unrecognized or invalid options" ); break; case FALCON_DBI_ERROR_NO_SUBTRANS: this->errorDescription( "DBEngine doesn't support sub-transactions" ); break; case FALCON_DBI_ERROR_NO_MULTITRANS: this->errorDescription( "DBEngine doesn't support multiple transactions" ); break; case FALCON_DBI_ERROR_UNPREP_EXEC: this->errorDescription( "Called 'execute' without having previously called 'prepare'" ); break; case FALCON_DBI_ERROR_BIND_SIZE: this->errorDescription( "Input variables in 'execute' and statement parameters have different size" ); break; case FALCON_DBI_ERROR_BIND_MIX: this->errorDescription( "Input variables passed in 'execute' cannot be bound to the statement" ); break; case FALCON_DBI_ERROR_EXEC: this->errorDescription( "Error during an 'execute' on a prepared statement" ); break; case FALCON_DBI_ERROR_FETCH: this->errorDescription( "Failed to fetch part of the recordset" ); break; case FALCON_DBI_ERROR_UNHANDLED_TYPE: this->errorDescription( "Unhandled field type in return dataset" ); break; case FALCON_DBI_ERROR_RESET: this->errorDescription( "Error while resetting a statement" ); break; case FALCON_DBI_ERROR_BIND_INTERNAL: this->errorDescription( "Internal SQL expansion failed" ); break; case FALCON_DBI_ERROR_TRANSACTION: this->errorDescription( "Error in issuing standard transactional command" ); break; case FALCON_DBI_ERROR_CLOSED_STMT: this->errorDescription( "Statement already closed" ); break; case FALCON_DBI_ERROR_CLOSED_RSET: this->errorDescription( "Recordset already closed" ); break; case FALCON_DBI_ERROR_CLOSED_DB: this->errorDescription( "DB already closed" ); break; case FALCON_DBI_ERROR_DB_NOTFOUND: this->errorDescription( "Requested database not found" ); break; case FALCON_DBI_ERROR_CONNECT_CREATE: this->errorDescription( "Unable to create the database as required" ); break; // by default, do nothing -- let the base system to put an appropriate description } } } /* end of dbi_error.cpp */ modules/native/dbi/dbi_common/dbi_handle.cpp000066400000000000000000000023121176363201700214410ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_handle.cpp Database Interface - Main handle driver ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 16 May 2010 00:09:13 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include namespace Falcon { DBIHandle::DBIHandle(): m_nLastAffected(-1) { } DBIHandle::~DBIHandle() { } void DBIHandle::sqlExpand( const String& sql, String& tgt, const ItemArray& params ) { if( !dbi_sqlExpand( sql, tgt, params ) ) { String temp = ""; temp.A("Array of ").N((int32)params.length()).A(" -> "); temp += sql; throw new DBIError( ErrorParam( FALCON_DBI_ERROR_BIND_INTERNAL, __LINE__ ) .extra( temp )); } } void DBIHandle::gcMark( uint32 ) { } FalconData* DBIHandle::clone() const { return 0; } int64 DBIHandle::affectedRows() { return m_nLastAffected; } } /* end of dbi_handle.h */ modules/native/dbi/dbi_common/dbi_inbind.cpp000066400000000000000000000152561176363201700214640ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_bind.cpp Database Interface Helper for general Falcon-to-C variable binding ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 15 May 2010 23:47:36 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include #include namespace Falcon { //========================================================= // Default time converter //========================================================= void DBITimeConverter_ISO::convertTime( TimeStamp* ts, void* buffer, int& bufsize ) const { fassert( bufsize > 19 ); sprintf( (char*) buffer, "%4.0d-%2.0d-%2.0d %2.0d:%2.0d:%2.0d", ts->m_year, ts->m_month, ts->m_day, ts->m_hour, ts->m_minute, ts->m_second ); bufsize = 19; } DBITimeConverter_ISO DBITimeConverter_ISO_impl; //========================================================= // Default string converter //========================================================= char* DBIStringConverter_UTF8::convertString( const String& str, char* target, int &bufsize ) const { char *ret; int maxlen = str.length() * 4 + 1; if( maxlen <= bufsize ) { // Ok, we can use the buffer ret = target; } else { ret = (char *) memAlloc( maxlen ); } while( (bufsize = str.toCString( ret, maxlen )) < 0 ) { maxlen *= 2; if ( ret != target ) memFree(ret); ret = (char *) memAlloc( maxlen ); } return ret; } DBIStringConverter_UTF8 DBIStringConverter_UTF8_impl; char* DBIStringConverter_WCHAR::convertString( const String& str, char* target, int &bufsize ) const { wchar_t *ret; int maxlen = str.length() * 2; if( maxlen <= bufsize ) { // Ok, we can use the buffer ret = (wchar_t*) target; } else { ret = (wchar_t *) memAlloc( maxlen ); } while( (bufsize = str.toWideString( ret, maxlen )) < 0 ) { maxlen *= 2; if ( ret != (wchar_t*) target ) memFree(ret); ret = (wchar_t *) memAlloc( maxlen ); } return (char*) ret; } DBIStringConverter_WCHAR DBIStringConverter_WCHAR_impl; //========================================================= // Single item binding converter //========================================================= DBIBindItem::DBIBindItem(): m_type( t_nil ), m_buflen(0) { } DBIBindItem::~DBIBindItem() { clear(); } void DBIBindItem::set(const Item& value, const DBITimeConverter& tc, const DBIStringConverter& sc ) { clear(); switch( value.type() ) { case FLC_ITEM_NIL: break; case FLC_ITEM_BOOL: m_type = t_bool; m_cdata.v_bool = value.asBoolean(); break; case FLC_ITEM_INT: m_type = t_int; m_cdata.v_int64 = value.asInteger(); break; case FLC_ITEM_NUM: m_type = t_double; m_cdata.v_double = (double) value.asNumeric(); break; case FLC_ITEM_STRING: m_type = t_string; m_buflen = bufsize; m_cdata.v_string = sc.convertString( *value.asString(), m_buffer, m_buflen ); break; case FLC_ITEM_MEMBUF: m_type = t_buffer; m_buflen = value.asMemBuf()->length(); m_cdata.v_buffer = value.asMemBuf()->data(); break; case FLC_ITEM_OBJECT: { CoreObject* obj = value.asObjectSafe(); if( obj->derivedFrom( "TimeStamp" ) ) { m_type = t_time; TimeStamp* ts = static_cast( obj->getFalconData() ); m_buflen = bufsize; tc.convertTime( ts, m_buffer, m_buflen ); m_cdata.v_buffer = m_buffer; break; } } // else, fall through default: { VMachine* vm = VMachine::getCurrent(); String str; if( vm != 0 ) { vm->itemToString( str, &value ); } else { str = ""; } m_type = t_string; m_buflen = bufsize; m_cdata.v_string = sc.convertString( str, m_buffer, m_buflen ); } } } void DBIBindItem::clear() { if ( m_type == t_string ) { if ( m_cdata.v_string != m_buffer ) { memFree( m_cdata.v_string ); } m_buflen = 0; } m_type = t_nil; } //========================================================= // Bindings for a whole ItemArray //========================================================= DBIInBind::DBIInBind( bool bAc ): m_ibind(0), m_bAlwaysChange( bAc ), m_size(0) { } DBIInBind::~DBIInBind() { delete[] m_ibind; } void DBIInBind::unbind() { if ( m_size == 0 ) { m_size = -1; return; } if( m_size != -1 ) { // time to explode. throw new DBIError( ErrorParam( FALCON_DBI_ERROR_BIND_SIZE, __LINE__ ) .extra( String("").N( (int64) m_size ).A(" != ").N( (int64) 0 ) ) ); } } void DBIInBind::bind( const ItemArray& arr, const DBITimeConverter& tc, const DBIStringConverter& sc ) { bool bFirst; // as a marker of having completed the first loop, we'll change m_size only at the end int nSize; // first time around? if ( m_ibind == 0 ) { bFirst = true; nSize = arr.length(); m_ibind = new DBIBindItem[nSize]; onFirstBinding( nSize ); } else { bFirst = false; nSize = m_size; if( ((uint32)nSize) != arr.length() ) { // time to explode. throw new DBIError( ErrorParam( FALCON_DBI_ERROR_BIND_SIZE, __LINE__ ) .extra( String("").N( (int64) nSize ).A(" != ").N( (int64) arr.length() ) ) ); } } // force recalculation of the items. if(m_bAlwaysChange ) { bFirst = true; } for( int i = 0; i < nSize; ++i ) { DBIBindItem& bi = m_ibind[i]; DBIBindItem::datatype type = bi.type(); void* buffer = bi.databuffer(); int len = bi.length(); bi.set( arr[i], tc, sc ); // first time around, or changed buffer? if( bFirst || bi.type() != type || bi.databuffer() != buffer || bi.length() != len ) { // let the engine determine if the type is compatible with the type of column onItemChanged( i ); } } // Mark the first loop as complete. m_size = nSize; } } /* end of dbi_bind.cpp */ modules/native/dbi/dbi_common/dbi_outbind.cpp000066400000000000000000000064531176363201700216640ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_outbind.cpp Database Interface Helper for general C-to-Falcon variable binding ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 17 May 2010 22:32:39 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include namespace Falcon { //============================================================= // Output bind single item. //============================================================= DBIOutBind::DBIOutBind(): m_allocated( bufsize ), m_allBlockSizes( 0 ), m_memory( m_stdBuffer ), m_headBlock(0) { } DBIOutBind::~DBIOutBind() { if( m_memory != 0 && m_memory != m_stdBuffer ) { memFree( m_memory ); m_memory = 0; } void* block = m_headBlock; while( block != 0 ) { fassert( sizeof(long) == sizeof(void*) ); long* l = (long*) block; l = l-2; void *nblock = (void*) l[0]; memFree( l ); block = nblock; } m_headBlock = m_tailBlock = 0; } void* DBIOutBind::allocBlock( unsigned size ) { fassert( sizeof(long) == sizeof(void*) ); long* lblock = (long*) memAlloc( size + (sizeof(long)*2)); lblock[0] = 0; lblock[1] = (long) size; lblock += 2; if ( m_tailBlock == 0 ) { fassert( m_headBlock == 0 ); m_tailBlock = m_headBlock = lblock; } else { long* tail = (long*) m_tailBlock; tail = tail - 2; tail[0] = (long) lblock; m_tailBlock = lblock; } return lblock; } void DBIOutBind::setBlockSize( void* block, unsigned size ) { long* lblock = (long*) block; lblock -= 2; m_allBlockSizes += size - lblock[1]; lblock[1] = size; } void* DBIOutBind::consolidate() { if( m_memory != 0 && m_memory != m_stdBuffer ) { memFree( m_memory ); } if( m_allocated == 0 ) { m_memory = 0; return 0; } m_memory = memAlloc( m_allocated ); char* memory = (char*) m_memory; m_allocated = 0; long* head = (long*) m_headBlock; while( head != 0 ) { head -= 2; memcpy( memory + m_allocated, head + 2, head[1] ); m_allocated += head[1]; long* old = head; head = (long*)head[0]; memFree( old ); } return m_memory; } void* DBIOutBind::alloc( unsigned size ) { if( m_memory == 0 || m_memory == m_stdBuffer ) { m_memory = memAlloc( size ); } else { m_memory = memRealloc( m_memory, size ); } m_allocated = size; return m_memory; } void* DBIOutBind::reserve( unsigned size ) { if( m_headBlock != 0 ) consolidate(); if( m_allocated >= size ) return m_memory; if( m_memory == 0 || m_memory == m_stdBuffer ) { m_memory = memAlloc( size ); } else { m_memory = memRealloc( m_memory, size ); } m_allocated = size; return m_memory; } void* DBIOutBind::getMemory() { if( m_memory == 0 || m_memory == m_stdBuffer ) { return 0; } void* mem = m_memory; m_allocated = 0; m_memory = 0; return mem; } } /* end of dbi_outbind.cpp */ modules/native/dbi/dbi_common/dbi_params.cpp000066400000000000000000000127041176363201700214770ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_params.cpp DBI service that DBI drivers inherit from and implement ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 15 May 2010 15:40:39 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include namespace Falcon { //============================================================ // Generic parameter parser //============================================================ DBIParams::DBIParams(): m_pFirst(0) { } DBIParams::~DBIParams() { Param* p = m_pFirst; while( p != 0 ) { Param* old = p; p = p->m_pNext; delete old; } m_pFirst = 0; } bool DBIParams::checkBoolean( const String& pvalue, bool &boolVar ) { if( pvalue.compareIgnoreCase("on") == 0 ) { boolVar = true; } else if( pvalue.compareIgnoreCase("off") == 0 ) { boolVar = false; } else if ( pvalue != "" && pvalue != "\"\"" ) { return false; } return true; } void DBIParams::addParameter( const String& name, String& value ) { Param* p = new Param( name, value ); p->m_pNext = m_pFirst; m_pFirst = p; } void DBIParams::addParameter( const String& name, String& value, const char **szValue ) { Param* p = new Param( name, value, szValue ); p->m_pNext = m_pFirst; m_pFirst = p; } bool DBIParams::parse( const String& connStr ) { uint32 pos = 0; // find next ";" uint32 pos1 = connStr.find(";"); do { pos1 = connStr.find( ";", pos ); String part = connStr.subString( pos, pos1 ); pos = pos1 + 1; if ( ! parsePart( part ) ) { return false; } } while( pos1 != String::npos ); return true; } bool DBIParams::parsePart( const String& strPart ) { // Break the record = uint32 pos = strPart.find( "=" ); if ( pos == String::npos ) return false; String key = strPart.subString(0,pos); key.trim(); Param* p = m_pFirst; while( p != 0 ) { if ( p->m_name.compareIgnoreCase( key ) == 0 ) { // Found! p->m_output = strPart.subString( pos + 1 ); // use this convention to declare an empty, but given value. if( p->m_output == "" ) { p->m_output = "''"; if( p->m_szOutput != 0 ) *p->m_szOutput = ""; // given but empty } else { if( p->m_szOutput != 0 ) { p->m_cstrOut = new AutoCString( p->m_output ); *p->m_szOutput = p->m_cstrOut->c_str(); } } return true; } p = p->m_pNext; } // not found... it's an invalid key return false; } DBIParams::Param::~Param() { delete m_cstrOut; } //============================================================ // Settings parameter parser //============================================================ DBISettingParams::DBISettingParams(): m_bAutocommit( defaultAutocommit ), m_nCursorThreshold( defaultCursor ), m_nPrefetch( defaultPrefetch ), m_bFetchStrings( defaultFetchStrings ) { addParameter( "autocommit", m_sAutocommit ); addParameter( "cursor", m_sCursor ); addParameter( "prefetch", m_sPrefetch ); addParameter( "strings", m_sFetchStrings ); } DBISettingParams::DBISettingParams( const DBISettingParams & other): m_bAutocommit( other.m_bAutocommit ), m_nCursorThreshold( other.m_nCursorThreshold ), m_nPrefetch( other.m_nPrefetch ), m_bFetchStrings( other.m_bFetchStrings ) { // we don't care about the parameter parsing during the copy. } DBISettingParams::~DBISettingParams() { } bool DBISettingParams::parse( const String& connStr ) { if( ! DBIParams::parse(connStr) ) { return false; } if ( ! checkBoolean( m_sAutocommit, m_bAutocommit ) ) return false; if ( ! checkBoolean( m_sFetchStrings, m_bFetchStrings ) ) return false; if( m_sPrefetch.compareIgnoreCase("all") == 0 ) { m_nPrefetch = -1; } else if( m_sPrefetch.compareIgnoreCase("none") == 0 ) { m_nPrefetch = 0; } else if ( m_sPrefetch != "" && m_sPrefetch != "\"\"" ) { if ( ! m_sPrefetch.parseInt( m_nPrefetch ) ) return false; } if( m_sCursor.compareIgnoreCase("none") == 0 ) { m_nCursorThreshold = -1; } else if( m_sCursor.compareIgnoreCase("all") == 0 ) { m_nCursorThreshold = 0; } else if ( m_sCursor != "" && m_sCursor != "\"\"" ) { if ( ! m_sCursor.parseInt( m_nCursorThreshold ) ) return false; } return true; } //============================================================ // Connection parameter parser //============================================================ DBIConnParams::DBIConnParams( bool bNoDef ): m_szUser(0), m_szPassword(0), m_szHost(0), m_szPort(0), m_szDb(0) { if( ! bNoDef ) { // add the default parameters addParameter( "uid", m_sUser, &m_szUser ); addParameter( "pwd", m_sPassword, &m_szPassword ); addParameter( "db", m_sDb, &m_szDb ); addParameter( "port", m_sPort, &m_szPort ); addParameter( "host", m_sHost, &m_szHost ); addParameter( "create", m_sCreate, &m_szCreate ); } } DBIConnParams::~DBIConnParams() { } } /* end of dbi_params.cpp */ modules/native/dbi/dbi_common/dbi_recordset.cpp000066400000000000000000000014601176363201700222030ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_recordset.cpp Database Interface - SQL Recordset class ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 16 May 2010 00:09:13 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include namespace Falcon { DBIRecordset::DBIRecordset( DBIHandle* generator ): m_dbh( generator ) {} DBIRecordset::~DBIRecordset() {} FalconData *DBIRecordset::clone() const { return 0; } DBIRecordset *DBIRecordset::getNext() { return 0; } void DBIRecordset::gcMark( uint32 v ) { } } /* end of dbi_recorset.cpp */ modules/native/dbi/dbi_common/dbi_stmt.cpp000066400000000000000000000014021176363201700211740ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_trans.cpp Database Interface - SQL Transaction class ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 16 May 2010 00:09:13 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include namespace Falcon { DBIStatement::DBIStatement( DBIHandle *dbh ): m_dbh( dbh ), m_nLastAffected(-1) { } DBIStatement::~DBIStatement() { } FalconData *DBIStatement::clone() const { return 0; } void DBIStatement::gcMark( uint32 v ) { } } /* end of dbi_trans.cpp */ modules/native/dbi/docs/000077500000000000000000000000001176363201700155105ustar00rootroot00000000000000modules/native/dbi/docs/main.fd000066400000000000000000000017401176363201700167510ustar00rootroot00000000000000/*# @main Falcon DBI module suite. This is the Falcon DBI module (and related modules) reference. The DBI module provides an abstract interface to many database servers. Falcon handles making the methods and data appear to Falcon scripts to be identical, thus making it possible to change only your connect string when a new database server type is to be used. @section dbi_setup Setup A minimal setup for this module may be something like the following: @code load dbi try db = DBIConnect( "sqlite3:sample.db" ) r = db.query("SELECT name, dob FROM names") while r.next() == 0 name = r.asString( 0 ) dob = r.asDate( 1 ) > name, " was born on ", dob end r.close() db.close() catch DBIError in e > "DBI failed: " > e end @endcode More advanced fetch methods exist such as @a DBIRecordset.fetchArray, @a DBIRecordset.fetchDict and @a DBIRecordset.fetchObject. */ modules/native/dbi/fbsql/000077500000000000000000000000001176363201700156675ustar00rootroot00000000000000modules/native/dbi/fbsql/CMakeLists.txt000066400000000000000000000031561176363201700204340ustar00rootroot00000000000000#################################################################### # The Falcon Programming language # # CMake configuration file for module mysql #################################################################### if(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) endif(COMMAND cmake_policy) falcon_define_module( FALCON_MODULE fbsql ) set( firebird_include_search_path ${FIREBIRD_INCLUDE_DIR} /usr/local/include /usr/include ) # Find FB includes FIND_PATH( FIREBIRD_INC_DIR ibase.h ${firebird_include_search_path} ) if (NOT FIREBIRD_INC_DIR) MESSAGE(FATAL_ERROR "Can't find ibase.h header file in ${firebird_include_search_path}" ) endif() # Find MySQL Client Library SET( firebird_lib_search_path "${FIREBIRD_LIB_DIR}" /usr/local/lib /usr/lib ) FIND_LIBRARY( FIREBIRD_LIBRARY NAMES libfbclient.so PATHS ${firebird_lib_search_path} ) if (NOT FIREBIRD_LIBRARY) MESSAGE(FATAL_ERROR "Can't find library libfbclient.so in ${firebird_lib_search_path}" ) endif() MESSAGE(STATUS "Found ibase.h in ${FIREBIRD_INC_DIR}") MESSAGE(STATUS "Found firebird client library ${FIREBIRD_LIBRARY}") # Inclusion settings INCLUDE_DIRECTORIES(.) INCLUDE_DIRECTORIES(../include) INCLUDE_DIRECTORIES(../dbi) INCLUDE_DIRECTORIES(${FIREBIRD_INC_DIR}) # Target ADD_LIBRARY( ${FALCON_MODULE} MODULE ${dbi_common_files} fbsql.cpp fbsql_ext.cpp fbsql_mod.cpp fbsql_srv.cpp ) # Link IF(MSVC) SET(FIREBIRD_OPT_LIBS wsock32) ENDIF(MSVC) TARGET_LINK_LIBRARIES( ${FALCON_MODULE} falcon_engine ${FIREBIRD_OPT_LIBS} ${FIREBIRD_LIBRARY}) falcon_install_module2( ${FALCON_MODULE} dbi ) modules/native/dbi/fbsql/fbsql.cpp000066400000000000000000000032751176363201700175110ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: fbsql.cpp * * Firebird driver main module * * This is BOTH a driver for the DBI interface AND a standalone * Firebird module. * ------------------------------------------------------------------- * Author: Giancarlo Niccolai * Begin: Mon, 20 Sep 2010 21:02:16 +0200 * * ------------------------------------------------------------------- * (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include "fbsql_mod.h" #include "fbsql_ext.h" #include "version.h" // Instantiate the driver service Falcon::DBIServiceFB theFirebirdService; /*# @module dbi.fbsql Firebird Database driver module @brief DBI extension supporting Firebird Directly importable as @b dbi.fbsql, it is usually loaded through the @a dbi module. */ // the main module FALCON_MODULE_DECL { // Module declaration Falcon::Module *self = new Falcon::Module(); self->name( "fbsql" ); self->engineVersion( FALCON_VERSION_NUM ); self->version( VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION ); // first of all, we need to declare our dependency from the DBI module. self->addDepend( "dbi", "dbi", true, false ); Falcon::Symbol *dbh_class = self->addExternalRef( "dbi.%Handle" ); // it's external dbh_class->imported( true ); Falcon::Symbol *firebird_class = self->addClass( "FirebirdSQL", Falcon::Ext::Firebird_init ); firebird_class->getClassDef()->addInheritance( new Falcon::InheritDef( dbh_class ) ); firebird_class->setWKS( true ); // service publication self->publishService( &theFirebirdService ); // we're done return self; } /* end of fbsql.cpp */ modules/native/dbi/fbsql/fbsql_ext.cpp000066400000000000000000000066511176363201700203720ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: fbsql_ext.cpp * * Firebird Falcon extension interface * ------------------------------------------------------------------- * Author: Giancarlo Niccolai * Begin: Mon, 20 Sep 2010 21:02:16 +0200 * * ------------------------------------------------------------------- * (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include #include "fbsql_mod.h" #include "fbsql_ext.h" /*# @beginmodule dbi.fbsql */ namespace Falcon { namespace Ext { /*# @class Firebird @brief Direct interface to Firebird database. @param connect String containing connection parameters. The connect string uses the standard connection values: - uid: user id - pwd: password - db: database where to connect - host: host where to connect (defaults to localhost) - port: prot where to connect (defaults to mysql standard port) Following connection options are specific of the Firebird database engine, and require a value to be passed in the form of @code "fbsql:...;key=value;key=value" @endcode - epwd: Encrypted password - role: Role - sa: System Administrator user name - license: Authorization key for a software license - ekey: Database encryption key - nbuf: Number of cache buffers - kscope: dbkey context scope - lcmsg: Language-specific message file - lctype: Character set to be utilized - tout: Connection timeout Following connection options are specific of the Firebird database engine, and require a boolean value that can be either "yes" or "no". - reserve: Specify whether or not to reserve a small amount of space on each database page for holding backup versions of records when modifications are made - dmg: Specify whether or not the database should be marked as damaged - verify: Perform consistency checking of internal structures - shadow: Activate the database shadow, an optional, duplicate, in-sync copy of the database - delshadow: Delete the database shadow - beginlog: Activate a replay logging system to keep track of all database calls - quitlog: Deactivate the replay logging system @section fb_opt Transaction options. Other than the default options provided by @a Handle.options, the Firebird driver provides the following transaction options: - getaffected: Reads affected rows after every query/execute operation. Can be 'on' or 'off' (defaults on). */ FALCON_FUNC Firebird_init( VMachine *vm ) { Item *paramsI = vm->param(0); Item *i_tropts = vm->param(1); if ( paramsI == 0 || ! paramsI->isString() || ( i_tropts != 0 && ! i_tropts->isString() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S,[S]" ) ); } String *params = paramsI->asString(); DBIHandle *hand = 0; try { hand = theFirebirdService.connect( *params ); if( i_tropts != 0 ) { hand->options( *i_tropts->asString() ); } // great, we have the database handler open. Now we must create a falcon object to store it. CoreObject *instance = theFirebirdService.makeInstance( vm, hand ); vm->retval( instance ); } catch( DBIError* error ) { delete hand; throw error; } } } /* namespace Ext */ } /* namespace Falcon */ /* end of fbsql_ext.cpp */ modules/native/dbi/fbsql/fbsql_ext.h000066400000000000000000000014131176363201700200260ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: fbsql_ext.h * * Firebird database server Falcon extension interface * ------------------------------------------------------------------- * Author: Giancarlo Niccolai * Begin: Mon, 20 Sep 2010 21:08:53 +0200 * * ------------------------------------------------------------------- * (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ /* #include #include */ #include #ifndef FALCON_FIREBIRD_EXT_H #define FALCON_FIREBIRD_EXT_H namespace Falcon { class VMachine; namespace Ext { FALCON_FUNC Firebird_init( VMachine *vm ); } } #endif /* FALCON_FIREBIRD_EXT_H */ /* end of fbsql_ext.h */ modules/native/dbi/fbsql/fbsql_mod.cpp000066400000000000000000000663331176363201700203540ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: fbsql_mod.cpp Firebird Falcon service/driver ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 20 Sep 2010 21:15:06 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include "fbsql_mod.h" namespace Falcon { DBIServiceFB theFirebirdService; /****************************************************************************** * Private class used to convert timestamp to Firebird format. *****************************************************************************/ class DBITimeConverter_Firebird_TIME: public DBITimeConverter { public: virtual void convertTime( TimeStamp* ts, void* buffer, int& bufsize ) const; } DBITimeConverter_Firebird_TIME_impl; void DBITimeConverter_Firebird_TIME::convertTime( TimeStamp* ts, void* buffer, int& bufsize ) const { fassert( ((unsigned)bufsize) >= sizeof( ISC_TIMESTAMP ) ); ISC_TIMESTAMP* mtime = (ISC_TIMESTAMP*) buffer; struct tm entry_time; entry_time.tm_year = ts->m_year < 1900 ? 0 : ts->m_year - 1900; entry_time.tm_mon = ts->m_month-1; entry_time.tm_mday = (unsigned) ts->m_day; entry_time.tm_hour = (unsigned) ts->m_hour; entry_time.tm_min = (unsigned) ts->m_minute; entry_time.tm_sec = (unsigned) ts->m_second; isc_encode_timestamp( &entry_time, mtime ); mtime->timestamp_time += ts->m_msec*10; bufsize = sizeof( ISC_TIMESTAMP ); } /****************************************************************************** * (Input) bindings class *****************************************************************************/ FBInBind::FBInBind( isc_db_handle dbh, isc_tr_handle tr, isc_stmt_handle stmt ): m_dbh( dbh ), m_tr(tr), m_stmt(stmt), m_sqlInd(0), m_GIDS(0) { } FBInBind::~FBInBind() { if( m_sqlInd != 0 ) memFree(m_sqlInd); if( m_GIDS != 0 ) memFree( m_GIDS ); } void FBInBind::onFirstBinding( int size ) { m_data.describeIn( m_stmt ); if( size != m_data.varCount() ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_BIND_SIZE, __LINE__ ) .extra( String("").N(size).A("!=").N(m_data.varCount())) ); } m_sqlInd = (ISC_SHORT*) memAlloc( size * sizeof(ISC_SHORT) ); } void FBInBind::onItemChanged( int num ) { DBIBindItem& item = m_ibind[num]; XSQLVAR* var = m_data.var( num ); // Normally not nil var->sqlind = m_sqlInd+num; *var->sqlind = 0; printf( "Binding item %d - %d/%d\n", num, item.type(), var->sqltype ); switch( item.type() ) { // set to null case DBIBindItem::t_nil: var->sqltype = SQL_LONG+1; var->sqldata = item.userbuffer(); var->sqllen = sizeof( ISC_LONG ); *var->sqlind = -1; break; case DBIBindItem::t_bool: var->sqltype = SQL_SHORT; var->sqldata = (ISC_SCHAR*) item.userbuffer(); (*(ISC_SHORT*)var->sqldata) = (ISC_SHORT) (item.asInteger() > 0 ? 1 : 0); var->sqllen = sizeof( ISC_SHORT ); break; case DBIBindItem::t_int: var->sqltype = SQL_INT64; var->sqldata = (ISC_SCHAR*) item.asIntegerPtr(); var->sqllen = sizeof( int64 ); break; case DBIBindItem::t_double: var->sqltype = SQL_DOUBLE; var->sqldata = (ISC_SCHAR*) item.asDoublePtr(); var->sqllen = sizeof( double ); break; case DBIBindItem::t_string: var->sqltype = SQL_TEXT; var->sqldata = (ISC_SCHAR*) item.asString(); var->sqllen = item.asStringLen(); break; case DBIBindItem::t_buffer: { // Create the blob data if ( m_GIDS == 0 ) { m_GIDS = (ISC_QUAD*) memAlloc( sizeof( ISC_QUAD ) * m_size ); } m_GIDS[num] = createBlob( (byte*)item.asBuffer(), item.asStringLen() ); var->sqltype = SQL_BLOB; var->sqldata = (ISC_SCHAR*)(m_GIDS + num); var->sqllen = sizeof( ISC_QUAD ); } break; case DBIBindItem::t_time: var->sqltype = SQL_TIMESTAMP; var->sqldata = (ISC_SCHAR*) item.asBuffer(); var->sqllen = item.asStringLen(); break; } } ISC_QUAD FBInBind::createBlob( byte* data, int64 size ) { isc_blob_handle handle; handle = 0; ISC_QUAD blob_id; ISC_STATUS status[20]; ISC_STATUS res; res = isc_create_blob2( status, &m_dbh, &m_tr, &handle, /* set by this function to refer to the new Blob */ &blob_id, /* Blob ID set by this function */ 0, /* Blob Parameter Buffer length = 0; no filter will be used */ NULL /* NULL Blob Parameter Buffer, since no filter will be used */ ); if( res != 0 ) { DBIHandleFB::throwError( __LINE__, FALCON_DBI_ERROR_EXEC, status ); } int64 done = 0; int len = 4096; while ( done < size ) { len = (size-done) < 4096 ? (int)(size-done) : 4096; res = isc_put_segment( status, &handle, len, (const ISC_SCHAR*)(data+done) ); if( res != 0 ) { ISC_STATUS dummy[20]; isc_cancel_blob( status, &handle ); DBIHandleFB::throwError( __LINE__, FALCON_DBI_ERROR_EXEC, status ); } done += len; } if( isc_close_blob( status, &handle ) != 0 ) { DBIHandleFB::throwError( __LINE__, FALCON_DBI_ERROR_EXEC, status ); } return blob_id; } /****************************************************************************** * Recordset class *****************************************************************************/ DBIRecordsetFB::DBIRecordsetFB( DBIHandleFB *dbt, FBTransRef* tref, isc_stmt_handle stmt, FBSqlData* data ): DBIRecordset( dbt ), m_nRow(0), m_nRowCount(-2), m_dbref( dbt->connRef() ), m_tref(tref), m_sref( new FBStmtRef(stmt) ), m_data( data ) { m_dbref->incref(); tref->incref(); } DBIRecordsetFB::DBIRecordsetFB( DBIHandleFB *dbt, FBTransRef* tref, FBStmtRef* sref, FBSqlData* data ): DBIRecordset( dbt ), m_nRow(0), m_nRowCount(-2), m_dbref( dbt->connRef() ), m_tref(tref), m_sref( sref ), m_data( data ) { m_dbref->incref(); tref->incref(); sref->incref(); } DBIRecordsetFB::~DBIRecordsetFB() { close(); } int DBIRecordsetFB::getColumnCount() { return m_data->varCount(); } int64 DBIRecordsetFB::getRowIndex() { return m_nRow; } int64 DBIRecordsetFB::getRowCount() { if( m_nRowCount == -2 ) { m_nRowCount = DBIHandleFB::getAffected( m_sref->handle(), FALCON_DBI_ERROR_EXEC ); } return m_nRowCount; } bool DBIRecordsetFB::getColumnName( int nCol, String& name ) { if( nCol < 0 || nCol >= m_data->varCount() ) { return false; } XSQLVAR* var = m_data->var(nCol); if(var->aliasname != 0 && var->aliasname_length !=0 ) { name = String((char*)var->aliasname, var->aliasname_length ); } else if( var->ownname != 0 && var->ownname_length != 0 ) { name = String((char*)var->ownname, var->ownname_length ); } else if( var->relname != 0 && var->relname_length != 0 ) { name = String((char*)var->relname, var->relname_length ); } else { return false; } name.bufferize(); return true; } bool DBIRecordsetFB::fetchRow() { // first time? if( m_nRow == 0 ) { // generate enough space. m_data->allocOutput(); } ISC_STATUS status[20]; ISC_STATUS res; // more data incoming res = isc_dsql_fetch( status, &m_sref->handle(), 1, m_data->table() ); // Done? if ( res == 100L ) { return false; } else if( res != 0 ) { DBIHandleFB::throwError( __LINE__, FALCON_DBI_ERROR_FETCH, status ); } // else, we can proceed m_nRow++; return true; } bool DBIRecordsetFB::discard( int64 ncount ) { while ( ncount > 0 ) { if( ! fetchRow() ) { return false; } --ncount; } return true; } void DBIRecordsetFB::close() { if( m_tref != 0 ) { m_sref->decref(); m_sref = 0; m_tref->decref(); m_tref = 0; delete m_data; m_data = 0; m_dbref->decref(); m_dbref = 0; } } bool DBIRecordsetFB::getColumnValue( int nCol, Item& value ) { if( nCol < 0 || nCol >= m_data->varCount() ) { return false; } XSQLVAR* var = m_data->var(nCol); if( (var->sqltype & 1) && *var->sqlind ) { // null value.setNil(); return true; } // not null char* buf = (char*) var->sqldata; int varType = var->sqltype & ~1; switch( varType ) { case SQL_VARYING: // pascal strings - a short with a length information followed by the data value.setString( new CoreString ); value.asString()->fromUTF8(var->sqldata + sizeof(short)); break; case SQL_INT64: if ( var->sqlscale < 0 ) value = (*(int64*)buf) * pow(10.0, var->sqlscale ); else value = *(int64*)buf; break; case SQL_LONG: if (var->sqllen == sizeof(int32) ) { if ( var->sqlscale < 0 ) value = (*(int32*)buf) * pow(10.0, var->sqlscale ); else value = *(int32*)buf; break; } else { value = *(int64*)buf; } break; case SQL_SHORT: if ( var->sqlscale < 0 ) value = (*(int32*)buf) * pow(10.0, var->sqlscale ); else value = *(int32*)buf; break; case SQL_FLOAT: value = (numeric) (*(float*)buf); break; case SQL_DOUBLE: value = (numeric) (*(double*)buf); break; case SQL_TIMESTAMP: case SQL_TYPE_TIME: case SQL_TYPE_DATE: { TimeStamp* ts = new TimeStamp; struct tm tstamp; if( varType == SQL_TIMESTAMP ) { isc_decode_timestamp( (ISC_TIMESTAMP*) buf, &tstamp ); ts->m_year = tstamp.tm_year+1900; ts->m_month = tstamp.tm_mon+1; ts->m_day = tstamp.tm_mday; ts->m_hour = tstamp.tm_hour; ts->m_minute = tstamp.tm_min; ts->m_second = tstamp.tm_sec; ts->m_msec = (((ISC_TIMESTAMP*) buf)->timestamp_time/10) %1000; } else if ( varType == SQL_TYPE_TIME ) { isc_decode_sql_time( (ISC_TIME*) buf, &tstamp ); ts->m_hour = tstamp.tm_hour; ts->m_minute = tstamp.tm_min; ts->m_second = tstamp.tm_sec; ts->m_msec = ((*(ISC_TIME*) buf)/10) %1000; } else // date { isc_decode_sql_date( (ISC_DATE*) buf, &tstamp ); ts->m_year = tstamp.tm_year+1900; ts->m_month = tstamp.tm_mon+1; ts->m_day = tstamp.tm_mday; } CoreObject *ots = VMachine::getCurrent()->findWKI("TimeStamp")->asClass()->createInstance(); ots->setUserData( ts ); value = ots; } break; case SQL_TEXT: value.setString( new CoreString ); value.asString()->fromUTF8(var->sqldata, var->sqllen ); break; case SQL_BLOB: value = fetchBlob((ISC_QUAD*)buf); break; case SQL_ARRAY: //row[idx] = d->fetchArray(i, (ISC_QUAD*)buf); break; default: return false; } return true; } MemBuf* DBIRecordsetFB::fetchBlob( ISC_QUAD *bId ) { ISC_STATUS status[20]; ISC_STATUS res; isc_blob_handle handle = 0; res = isc_open_blob2(status, &m_dbref->handle(), &m_tref->handle(), &handle, bId, 0, 0); if ( res != 0 ) { DBIHandleFB::throwError( __LINE__, FALCON_DBI_ERROR_FETCH, status ); } unsigned short len = 0; int64 fullSize = 0; struct chunk { int size; struct chunk* next; char data[4096]; }; struct chunk* current = (struct chunk*) memAlloc( sizeof(struct chunk)); struct chunk* first = current; while( (res = isc_get_segment(status, &handle, &len, 4096, current->data )) == 0 || status[1] == isc_segment ) { fullSize += len; current->size = len; struct chunk* next = (struct chunk*) memAlloc( sizeof(struct chunk)); current->next = next; current = next; current->next = 0; current->size = 0; } if( res != 0 && res != isc_segstr_eof ) { while( first != 0 ) { current = first->next; memFree( first ); first = current; } ISC_STATUS dummy[20]; isc_close_blob(dummy, &handle); DBIHandleFB::throwError( __LINE__, FALCON_DBI_ERROR_FETCH, status ); } // try to close if( isc_close_blob(status, &handle) != 0 ) { while( first != 0 ) { current = first->next; memFree( first ); first = current; } DBIHandleFB::throwError( __LINE__, FALCON_DBI_ERROR_FETCH, status ); } // save the membuffer MemBuf* mb = new MemBuf_1( fullSize ); fullSize = 0; while( first != 0 ) { memcpy( mb->data() + fullSize, first->data, first->size ); fullSize += first->size; current = first->next; memFree( first ); first = current; } return mb; } /****************************************************************************** * DB Statement class *****************************************************************************/ DBIStatementFB::DBIStatementFB( DBIHandleFB *dbh, FBTransRef* tref, const isc_stmt_handle& stmt, FBSqlData* dt ): DBIStatement( dbh ), m_statement(stmt), m_pTref( tref ), m_pStmt( new FBStmtRef(stmt) ), m_outData( dt ), m_inBind( 0 ) { m_pConn = dbh->connRef(); m_pConn->incref(); tref->incref(); m_bAutoCommit = dbh->options()->m_bAutocommit; m_bGetAffected = dbh->options()->m_bGetAffected; } DBIStatementFB::~DBIStatementFB() { close(); } DBIRecordset* DBIStatementFB::execute( ItemArray* params ) { ISC_STATUS status[20]; // initialize bindings the first time. if ( m_inBind == 0 ) { m_inBind = new FBInBind( m_pConn->handle(), m_pTref->handle(), m_statement ); } if( params != 0 ) { printf( "PArams size %d\n", params->length()); m_inBind->bind( *params, DBITimeConverter_Firebird_TIME_impl ); } else { m_inBind->unbind(); } if( isc_dsql_execute( status, &m_pTref->handle(), &m_statement, 1, m_inBind->table() ) != 0 ) { DBIHandleFB::throwError(__LINE__, FALCON_DBI_ERROR_EXEC, status ); } if( m_bGetAffected ) { m_nLastAffected = DBIHandleFB::getAffected( m_statement, FALCON_DBI_ERROR_EXEC ); } if ( m_bAutoCommit ) { m_pTref->commitRetaining(); } // do we have a recordset? if( m_outData != 0 ) { // return it return new DBIRecordsetFB( static_cast(m_dbh), m_pTref, m_pStmt, m_outData ); } // nope. Go on return 0; } void DBIStatementFB::reset() { } void DBIStatementFB::close() { if( m_pStmt != 0 ) { delete m_inBind; m_pStmt->decref(); m_pStmt = 0; m_pTref->decref(); m_pConn->decref(); } } /****************************************************************************** * Reaensaction handler *****************************************************************************/ FBTransRef::~FBTransRef() { // force closing if still not closed. if ( ! m_bClosed ) { static ISC_STATUS status[20]; isc_commit_transaction( status, &handle() ); } } void FBTransRef::commit() { static ISC_STATUS status[20]; if( isc_commit_transaction( status, &handle() ) != 0 ) { DBIHandleFB::throwError(__LINE__, FALCON_DBI_ERROR_TRANSACTION, status ); } m_bClosed = true; decref(); } void FBTransRef::commitRetaining() { static ISC_STATUS status[20]; if( isc_commit_retaining( status, &handle() ) != 0 ) { DBIHandleFB::throwError(__LINE__, FALCON_DBI_ERROR_TRANSACTION, status ); } } void FBTransRef::rollback() { static ISC_STATUS status[20]; if( isc_rollback_transaction( status, &handle() ) != 0 ) { DBIHandleFB::throwError(__LINE__, FALCON_DBI_ERROR_TRANSACTION, status ); } m_bClosed = true; decref(); } /****************************************************************************** * DB Handler class *****************************************************************************/ FBSqlData::FBSqlData(): m_sqlda(0), m_indicators(0), m_bOwnBuffers( false ) { m_sqlda = (XSQLDA*) memAlloc( XSQLDA_LENGTH(5) ); m_sqlda->version = SQLDA_VERSION1; m_sqlda->sqln = 5; m_sqlda->sqld = 0; } FBSqlData::~FBSqlData() { release(); } void FBSqlData::release() { if( m_sqlda != 0 ) { if( m_bOwnBuffers ) { for( int i = 0; i < varCount(); ++i ) { memFree(var(i)->sqldata); } memFree( m_indicators ); } memFree(m_sqlda); m_sqlda = 0; m_bOwnBuffers = false; } } void FBSqlData::describeIn( isc_stmt_handle stmt ) { ISC_STATUS status[20]; if( isc_dsql_describe_bind( status, &stmt, 1, m_sqlda ) != 0 ) { DBIHandleFB::throwError( __LINE__, FALCON_DBI_ERROR_BIND_INTERNAL, status ); } if ( m_sqlda->sqld > m_sqlda->sqln ) { int count = m_sqlda->sqld; memFree( m_sqlda ); m_sqlda = (XSQLDA*) memAlloc( XSQLDA_LENGTH(count) ); m_sqlda->version = SQLDA_VERSION1; m_sqlda->sqln = count; m_sqlda->sqld = 0; isc_dsql_describe_bind( status, &stmt, 1, m_sqlda ); } } void FBSqlData::describeOut( isc_stmt_handle stmt ) { ISC_STATUS status[20]; if( isc_dsql_describe( status, &stmt, 1, m_sqlda ) != 0 ) { DBIHandleFB::throwError( __LINE__, FALCON_DBI_ERROR_BIND_INTERNAL, status ); } if ( m_sqlda->sqld > m_sqlda->sqln ) { int count = m_sqlda->sqld; memFree( m_sqlda ); m_sqlda = (XSQLDA*) memAlloc( XSQLDA_LENGTH(count) ); m_sqlda->version = SQLDA_VERSION1; m_sqlda->sqln = count; m_sqlda->sqld = 0; isc_dsql_describe( status, &stmt, 1, m_sqlda ); } } void FBSqlData::allocOutput() { m_bOwnBuffers = true; m_indicators = (ISC_SHORT*) memAlloc( sizeof(ISC_SHORT) * varCount() ); for( int i = 0; i < varCount(); ++i ) { XSQLVAR* v = var(i); v->sqldata = (ISC_SCHAR*) memAlloc( v->sqllen ); v->sqlind = m_indicators + i; *v->sqlind = 0; } } /****************************************************************************** * DB Settings *****************************************************************************/ DBISettingParamsFB::DBISettingParamsFB(): m_bGetAffected( true ) { addParameter( "getaffected", m_sGetAffected ); } DBISettingParamsFB::DBISettingParamsFB( const DBISettingParamsFB& other ): DBISettingParams( other ), m_bGetAffected( other.m_bGetAffected ) { } DBISettingParamsFB::~DBISettingParamsFB() { // noop } bool DBISettingParamsFB::parse( const String& connStr ) { if ( ! DBISettingParams::parse( connStr) ) return false; if( ! checkBoolean( m_sGetAffected, m_bGetAffected ) ) return false; return true; } /****************************************************************************** * DB Handler class *****************************************************************************/ DBIHandleFB::DBIHandleFB(): m_bCommitted( false ) { m_pConn = 0; m_pTrans = 0; m_nLastAffected = -1; } DBIHandleFB::DBIHandleFB( const isc_db_handle &conn ): m_bCommitted( false ) { m_pConn = new FBConnRef( conn ); m_pTrans = 0; m_nLastAffected = -1; } DBIHandleFB::~DBIHandleFB() { // don't call close -- close will throw in case of commit error. if( m_pConn != 0 ) { if ( m_pTrans != 0 ) { //... and don't call commit; decref will commit without error report on destroy m_pTrans->decref(); m_pTrans = 0; } m_pConn->decref(); m_pConn = 0; } } isc_db_handle DBIHandleFB::getConnData() { if( m_pConn == 0 ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); } return m_pConn->handle(); } void DBIHandleFB::options( const String& params ) { if( ! m_settings.parse( params ) ) { // autocommit status is read on query throw new DBIError( ErrorParam( FALCON_DBI_ERROR_OPTPARAMS, __LINE__ ) .extra( params ) ); } } const DBISettingParamsFB* DBIHandleFB::options() const { return &m_settings; } DBIRecordset *DBIHandleFB::query( const String &sql, ItemArray* params ) { m_nLastAffected = -1; // may throw isc_stmt_handle stmt = internal_prepare( sql ); // internal_prepare sets up the transaction isc_tr_handle tr1 = m_pTrans->handle(); ISC_STATUS status[20]; ISC_STATUS res; FBSqlData* out_tab = 0; // call the query try { if( params == 0 ) { res = isc_dsql_execute( status, &tr1, &stmt, 1, 0 ); } else { FBInBind bindings( m_pConn->handle(), tr1, stmt ); bindings.bind( *params, DBITimeConverter_Firebird_TIME_impl ); res = isc_dsql_execute( status, &tr1, &stmt, 1, bindings.table() ); } if( res != 0 ) { throwError( __LINE__, FALCON_DBI_ERROR_QUERY, status ); } if( options()->m_bAutocommit ) { m_pTrans->commitRetaining(); } // get affected rows if ( m_settings.m_bGetAffected ) { m_nLastAffected = getAffected( stmt ); } // TODO // use isc_info_sql_num_variables before allocating FBSqlData out_tab = new FBSqlData; out_tab->describeOut( stmt ); if( out_tab->varCount() != 0 ) { return new DBIRecordsetFB( this, m_pTrans, stmt, out_tab ); } else { if( isc_dsql_free_statement( status, &stmt, DSQL_drop ) != 0 ) { throwError( __LINE__, FALCON_DBI_ERROR_QUERY, status ); } delete out_tab; return 0; } } catch( ... ) { delete out_tab; isc_dsql_free_statement( status, &stmt, DSQL_drop ); throw; } } isc_stmt_handle DBIHandleFB::internal_prepare( const String& query ) { ISC_STATUS status[20]; isc_db_handle db1 = getConnData(); // Open a transaction if we don't have any. if ( m_pTrans == 0 ) { begin(); } isc_tr_handle tr1 = m_pTrans->handle(); // Get a new statement handle isc_stmt_handle stmt; stmt = NULL; if ( isc_dsql_allocate_statement(status, &db1, &stmt) != 0 ) { throwError( __LINE__, FALCON_DBI_ERROR_NOMEM, status ); } AutoCString asQuery( query ); // TODO: can we remove out_table here? if( isc_dsql_prepare( status, &tr1, &stmt, asQuery.length(), asQuery.c_str(), 3, 0 ) != 0 ) { ISC_STATUS dummy_status[20]; isc_dsql_free_statement( dummy_status, &stmt, DSQL_drop ); throwError( __LINE__, FALCON_DBI_ERROR_QUERY, status ); } return stmt; } DBIStatement* DBIHandleFB::prepare( const String &query ) { isc_stmt_handle stmt = internal_prepare( query ); FBSqlData* out_tab = new FBSqlData; out_tab->describeOut( stmt ); if( out_tab->varCount() != 0 ) { new DBIStatementFB( this, m_pTrans, stmt, out_tab ); } else { delete out_tab; return new DBIStatementFB( this, m_pTrans, stmt, 0 ); } } int64 DBIHandleFB::getLastInsertedId( const String& sequenceName ) { isc_db_handle handle = getConnData(); return 0; // TODO } int64 DBIHandleFB::getAffected( isc_stmt_handle stmt, int etype ) { ISC_STATUS status[20]; // First, determine the statement type char acBuffer[9]; char qType = isc_info_sql_stmt_type; if( isc_dsql_sql_info(status, &stmt, 1, &qType, sizeof(acBuffer), acBuffer) != 0 ) { throwError( __LINE__, etype, status ); } int iLength = isc_vax_integer(&acBuffer[1], 2); int queryType = (int) isc_vax_integer(&acBuffer[3], iLength); // then, get the right info. char info = isc_info_sql_records; char buffer[64]; if( isc_dsql_sql_info( status, &stmt, 1, &info, sizeof(buffer), buffer ) != 0 ) { throwError( __LINE__, etype, status ); } int cCountType = 0; switch (queryType) { case isc_info_sql_stmt_select: cCountType = isc_info_req_select_count; break; case isc_info_sql_stmt_update: cCountType = isc_info_req_update_count; break; case isc_info_sql_stmt_delete: cCountType = isc_info_req_delete_count; break; case isc_info_sql_stmt_insert: cCountType = isc_info_req_insert_count; break; default: // not affecting any row return 0; } for (char *pcBuf = buffer + 3; *pcBuf != isc_info_end; /*nothing*/) { char cType = *pcBuf++; short sLength = isc_vax_integer (pcBuf, 2); pcBuf += 2; int iValue = isc_vax_integer (pcBuf, sLength); pcBuf += sLength; if (cType == cCountType) { return iValue; } } // unknown return -1; } void DBIHandleFB::begin() { ISC_STATUS status[20]; isc_db_handle handle = getConnData(); // have we an open transaction? if ( m_pTrans != 0 ) { // this closes and decrefs m_pTrans->commit(); } // Try to open new transaction isc_tr_handle htr = 0L; // TODO Use options to create different transaction types char isc_tbp[] = {isc_tpb_version3, isc_tpb_write, isc_tpb_concurrency, isc_tpb_wait}; /* Code for attaching to database here is omitted. */ ISC_STATUS res = isc_start_transaction(status, &htr, 1, &handle, (unsigned short) sizeof(isc_tbp), isc_tbp); if( res != 0 ) { throwError( __LINE__, FALCON_DBI_ERROR_TRANSACTION, status ); } m_pTrans = new FBTransRef( htr ); } void DBIHandleFB::commit() { getConnData(); // check for db open. m_pTrans->commitRetaining(); // don't really close the transaction. } void DBIHandleFB::rollback() { getConnData(); // check for db open. m_pTrans->rollback(); // this closes and decref the trans. m_pTrans = 0; // say we need a new transaction } void DBIHandleFB::selectLimited( const String& query, int64 nBegin, int64 nCount, String& result ) { String sSkip, sCount; if ( nBegin > 0 ) { sSkip = " SKIP "; sSkip.N( nBegin ); } if( nCount > 0 ) { sCount = " FIRST "; sCount.N( nCount ); } result = "SELECT" + sCount + sSkip +" "+ query; } void DBIHandleFB::close() { if ( m_pTrans != 0 ) { m_pTrans->commit(); // commit decrefs, and eventually throws m_pTrans = 0; } if( m_pConn != 0 ) { m_pConn->decref(); m_pConn = 0; } } void DBIHandleFB::throwError( int line, int code, ISC_STATUS* status ) { String desc; ISC_SCHAR msgBuffer[512]; // Get the main error ISC_STATUS* ep; ep = status; isc_interprete( msgBuffer, &ep ); desc += msgBuffer; // Write the secondary errors as a list of [...; ...; ...] messages bool bDone = false; while( isc_interprete( msgBuffer, &ep ) ) { if ( ! bDone ) { desc += " ["; bDone = true; } else { desc += "; "; } desc += msgBuffer; } if( bDone ) { desc += "]"; } throw new DBIError( ErrorParam( code, line ).extra(desc) ); } } /* namespace Falcon */ /* end of fbsql_mod.cpp */ modules/native/dbi/fbsql/fbsql_mod.h000066400000000000000000000132711176363201700200120ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: fbsql_mod.h * * FB driver main module interface * ------------------------------------------------------------------- * Author: Giancarlo Niccolai * Begin: Sun, 23 May 2010 16:58:53 +0200 * * ------------------------------------------------------------------- * (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #ifndef FALCON_FBSQL_H #define FALCON_FBSQL_H #include #include #include #include #include namespace Falcon { /** Class keeping SQLDA structures. */ class FBSqlData { public: FBSqlData(); ~FBSqlData(); void describeIn( isc_stmt_handle stmt ); void describeOut( isc_stmt_handle stmt ); /** Free allocated resources. */ void release(); void allocOutput(); XSQLDA* table() const { return m_sqlda; } XSQLVAR* var(int n) { return m_sqlda->sqlvar + n; } int varCount() { return m_sqlda->sqld; } private: XSQLDA* m_sqlda; ISC_SHORT* m_indicators; bool m_bOwnBuffers; }; class FBConnRef: public DBIRefCounter { public: FBConnRef( const isc_db_handle& hDb ): DBIRefCounter( hDb ) {} virtual ~FBConnRef() { ISC_STATUS status[20]; isc_detach_database( status, &handle() ); } }; class FBTransRef: public DBIRefCounter { public: FBTransRef( const isc_tr_handle& hStmt ): DBIRefCounter( hStmt ), m_bClosed( false ) {} virtual ~FBTransRef(); void commit(); void rollback(); void commitRetaining(); bool isClosed() const { return m_bClosed; } private: bool m_bClosed; }; class FBStmtRef: public DBIRefCounter { public: FBStmtRef( isc_stmt_handle hStmt ): DBIRefCounter( hStmt ) {} virtual ~FBStmtRef() { ISC_STATUS status[20]; isc_dsql_free_statement( status, &handle(), DSQL_drop ); } }; class FBInBind: public DBIInBind { public: FBInBind( isc_db_handle dbh, isc_tr_handle tr, isc_stmt_handle stmt ); virtual ~FBInBind(); virtual void onFirstBinding( int size ); virtual void onItemChanged( int num ); XSQLDA* table() const { return m_data.table(); } ISC_QUAD createBlob( byte* data, int64 size ); private: FBSqlData m_data; isc_db_handle m_dbh; isc_tr_handle m_tr; isc_stmt_handle m_stmt; ISC_SHORT* m_sqlInd; ISC_QUAD* m_GIDS; }; class DBIHandleFB; class DBIRecordsetFB: public DBIRecordset { public: DBIRecordsetFB( DBIHandleFB *dbt, FBTransRef* tref, isc_stmt_handle stmt, FBSqlData* data ); DBIRecordsetFB( DBIHandleFB *dbt, FBTransRef* tref, FBStmtRef* sref, FBSqlData* data ); virtual ~DBIRecordsetFB(); virtual int64 getRowIndex(); virtual int64 getRowCount(); virtual int getColumnCount(); virtual bool getColumnName( int nCol, String& name ); virtual bool getColumnValue( int nCol, Item& value ); virtual bool fetchRow(); virtual bool discard( int64 ncount ); virtual void close(); protected: int m_nRow; int m_nRowCount; FBConnRef* m_dbref; FBTransRef* m_tref; FBStmtRef* m_sref; FBSqlData* m_data; MemBuf* fetchBlob( ISC_QUAD *bId ); }; class DBIStatementFB : public DBIStatement { protected: isc_stmt_handle m_statement; FBStmtRef* m_pStmt; FBTransRef* m_pTref; FBConnRef* m_pConn; FBSqlData* m_outData; FBInBind* m_inBind; bool m_bAutoCommit; bool m_bGetAffected; public: DBIStatementFB( DBIHandleFB *dbh, FBTransRef* pTref, const isc_stmt_handle& stmt, FBSqlData* outData ); virtual ~DBIStatementFB(); virtual DBIRecordset* execute( ItemArray* params ); virtual void reset(); virtual void close(); }; class DBISettingParamsFB: public DBISettingParams { public: DBISettingParamsFB(); DBISettingParamsFB( const DBISettingParamsFB& other ); virtual ~DBISettingParamsFB(); virtual bool parse( const String& connStr ); /** Read affected rows after each query operation ( defaults to true ) */ bool m_bGetAffected; private: String m_sGetAffected; }; class DBIHandleFB : public DBIHandle { public: DBIHandleFB(); DBIHandleFB( const isc_db_handle &conn ); virtual ~DBIHandleFB(); virtual void options( const String& params ); virtual const DBISettingParamsFB* options() const; virtual void close(); virtual DBIRecordset *query( const String &sql, ItemArray* params = 0 ); virtual DBIStatement* prepare( const String &query ); virtual int64 getLastInsertedId( const String& name = "" ); virtual void begin(); virtual void commit(); virtual void rollback(); virtual void selectLimited( const String& query, int64 nBegin, int64 nCount, String& result ); // Checks for the db to be open and alive before proceed isc_db_handle getConnData(); FBConnRef* connRef() const { return m_pConn; } FBTransRef* transRef() const { return m_pTrans; } // Throws a DBI error, using the last error code and description. static void throwError( int line, int code, ISC_STATUS* status ); static int64 getAffected( isc_stmt_handle stmt, int etype = FALCON_DBI_ERROR_QUERY ); private: FBConnRef* m_pConn; FBTransRef* m_pTrans; DBISettingParamsFB m_settings; bool m_bCommitted; isc_stmt_handle internal_prepare( const String& query ); }; class DBIServiceFB : public DBIService { public: DBIServiceFB() : DBIService( "DBI_fbsql" ) {} virtual void init(); virtual DBIHandle *connect( const String ¶meters ); virtual CoreObject *makeInstance( VMachine *vm, DBIHandle *dbh ); }; extern DBIServiceFB theFirebirdService; } #endif /* FALCON_FB_H */ /* end of fbsql_mod.h */ modules/native/dbi/fbsql/fbsql_srv.cpp000066400000000000000000000157421176363201700204050ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: fbsql_srv.cpp * * Firebird Falcon service/driver * ------------------------------------------------------------------- * Author: Giancarlo Niccolai * Begin: Mon, 06 Dec 2010 12:10:39 +0100 * * ------------------------------------------------------------------- * (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include #include #include #include #include #include "fbsql_mod.h" namespace Falcon { /****************************************************************************** * Main service class *****************************************************************************/ void DBIServiceFB::init() { } static void checkParamNumber(char *&dpb, const String& value, byte dpb_type, const String &option ) { if ( value.length() ) { int64 res; if( ! value.parseInt(res) ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CONNPARAMS, __LINE__) .extra( option + "=" + value ) ); *dpb++ = dpb_type; *dpb++ = 1; *dpb++ = (byte) res; } } static void checkParamYesOrNo(char *&dpb, const String& value, byte dpb_type, const String &option ) { if ( value.size() ) { *dpb++ = dpb_type; *dpb++ = 1; if( value.compareIgnoreCase( "yes" ) == 0 ) *dpb++ = (byte) 1; else if( value.compareIgnoreCase( "no" ) == 0 ) *dpb++ = (byte) 0; else throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CONNPARAMS, __LINE__) .extra( option + "=" + value ) ); } } static void checkParamString(char *&dpb, const String& value, const char* szValue, byte dpb_type ) { if ( value.size() ) { *dpb = dpb_type; ++dpb; *dpb = (char) value.size(); ++dpb; strcpy( dpb, szValue ); dpb += value.size(); } } DBIHandle *DBIServiceFB::connect( const String ¶meters ) { isc_db_handle handle = 0L; char dpb_buffer[256*10], *dpb, *p; // User name (uid) // Password (pwd) dpb = dpb_buffer; int dpb_length; // Parse the connection string. DBIConnParams connParams; // add Firebird specific parameters // Encrypted password (epwd) String sPwdEnc; const char* szPwdEncode; connParams.addParameter( "epwd", sPwdEnc, &szPwdEncode ); // Role name (role) String sRole; const char* szRole; connParams.addParameter( "role", sRole, &szRole ); // System database administrator’s user name (sa) String sSAName; const char* szSAName; connParams.addParameter( "sa", sSAName, &szSAName ); // Authorization key for a software license (license) String sLicense; const char* szLicense; connParams.addParameter( "license", sLicense, &szLicense ); // Database encryption key (ekey) String sKey; const char* szKey; connParams.addParameter( "ekey", sKey, &szKey ); // Number of cache buffers (nbuf) String sNBuf; connParams.addParameter( "nbuf", sNBuf ); // dbkey context scope (kscope) String sDBKeyScope; connParams.addParameter( "kscope", sDBKeyScope ); // Specify whether or not to reserve a small amount of space on each database // --- page for holding backup versions of records when modifications are made (noreserve) String sNoRserve; connParams.addParameter( "reserve", sNoRserve ); // Specify whether or not the database should be marked as damaged (dmg) String sDmg; connParams.addParameter( "dmg", sDmg ); // Perform consistency checking of internal structures (verify) String sVerify; connParams.addParameter( "verify", sVerify ); // Activate the database shadow, an optional, duplicate, in-sync copy of the database (shadow) String sShadow; connParams.addParameter( "shadow", sShadow ); // Delete the database shadow (delshadow) String sDelShadow; connParams.addParameter( "delshadow", sDelShadow ); // Activate a replay logging system to keep track of all database calls (beginlog) String sBeginLog; connParams.addParameter( "beginlog", sBeginLog ); // Deactivate the replay logging system (quitlog) String sQuitLog; connParams.addParameter( "quitlog", sQuitLog ); // Language-specific message file (lcmsg) String sLcMsg; const char* szLcMsg; connParams.addParameter( "lcmsg", sLcMsg, &szLcMsg ); // Character set to be utilized (lctype) String sLcType; const char* szLcType; connParams.addParameter( "lctype", sLcType, &szLcType ); // Connection timeout (tout) String sTimeout; connParams.addParameter( "tout", sTimeout ); if( ! connParams.parse( parameters ) ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CONNPARAMS, __LINE__) .extra( parameters ) ); } // create the dpb; first the numerical values. *dpb++ = isc_dpb_version1; checkParamNumber( dpb, sNBuf, isc_dpb_num_buffers, "nbuf" ); checkParamNumber( dpb, sTimeout, isc_dpb_connect_timeout, "tout" ); checkParamYesOrNo( dpb, sDBKeyScope, isc_dpb_no_reserve, "kscope" ); checkParamYesOrNo( dpb, sNoRserve, isc_dpb_no_reserve, "reserve" ); checkParamYesOrNo( dpb, sDmg, isc_dpb_damaged, "dmg" ); checkParamYesOrNo( dpb, sVerify, isc_dpb_verify, "verify" ); checkParamYesOrNo( dpb, sShadow, isc_dpb_activate_shadow, "shadow" ); checkParamYesOrNo( dpb, sDelShadow, isc_dpb_delete_shadow, "delshadow" ); checkParamYesOrNo( dpb, sBeginLog, isc_dpb_begin_log, "beginlog" ); checkParamYesOrNo( dpb, sQuitLog, isc_dpb_quit_log, "sQuitLog" ); checkParamString( dpb, connParams.m_sUser, connParams.m_szUser, isc_dpb_user_name ); checkParamString( dpb, connParams.m_sPassword, connParams.m_szPassword, isc_dpb_password ); checkParamString( dpb, sPwdEnc, szPwdEncode, isc_dpb_password_enc ); checkParamString( dpb, sRole, szRole, isc_dpb_sql_role_name ); checkParamString( dpb, sLicense, szLicense, isc_dpb_license ); checkParamString( dpb, sKey, szKey, isc_dpb_encrypt_key ); //checkParamString( dpb, sLcMsg, szLcMsg, isc_dpb_lc_messages ); // We'll ALWAYS use AutoCString to talk with Firebird, as such we'll ALWAYS use UTF8 checkParamString( dpb, "UTF8", "UTF8", isc_dpb_lc_messages ); /* Attach to the database. */ ISC_STATUS status_vector[20]; isc_attach_database(status_vector, strlen(connParams.m_szDb), connParams.m_szDb, &handle, dpb-dpb_buffer, dpb_buffer); if ( status_vector[0] == 1 && status_vector[1] ) { DBIHandleFB::throwError( __LINE__, FALCON_DBI_ERROR_CONNECT, status_vector ); } return new DBIHandleFB( handle ); } CoreObject *DBIServiceFB::makeInstance( VMachine *vm, DBIHandle *dbh ) { Item *cl = vm->findWKI( "FirebirdSQL" ); if ( cl == 0 || ! cl->isClass() ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_INVALID_DRIVER, __LINE__ ) ); } CoreObject *obj = cl->asClass()->createInstance(); obj->setUserData( dbh ); return obj; } } /* namespace Falcon */ /* end of fbsql_srv.cpp */ modules/native/dbi/fbsql/version.h000066400000000000000000000011171176363201700175250ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: version.h * * MySQL module version information * ------------------------------------------------------------------- * Author: Jeremy Cowgar * Begin: Wed Jan 02 21:35:18 2008 * * ------------------------------------------------------------------- * (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #ifndef VERSION_H #define VERSION_H #define VERSION_MAJOR 1 #define VERSION_MINOR 0 #define VERSION_REVISION 0 #endif /* end of version.h */ modules/native/dbi/include/000077500000000000000000000000001176363201700162035ustar00rootroot00000000000000modules/native/dbi/include/falcon/000077500000000000000000000000001176363201700174455ustar00rootroot00000000000000modules/native/dbi/include/falcon/dbi_common.h000066400000000000000000000021251176363201700217240ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_common.h Database Interface - Helper/inner functions for DBI base. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 13 Apr 2009 18:56:48 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_DBI_COMMON_H_ #define FALCON_DBI_COMMON_H_ #include #include #include #include #include #include #include #include #include namespace Falcon { class String; class VMachine; class Item; bool dbi_itemToSqlValue( const Item &item, String &value ); void dbi_escapeString( const String& input, String& value ); bool dbi_sqlExpand( const String& input, String& output, const ItemArray& arr ); } #endif /* end of dbi_common.h */ modules/native/dbi/include/falcon/dbi_error.h000066400000000000000000000051421176363201700215670ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_error.h Database Interface - Error management ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 15 May 2010 23:47:36 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_DBI_ERROR_H_ #define FALCON_DBI_ERROR_H_ #include #include #ifndef FALCON_DBI_ERROR_BASE #define FALCON_DBI_ERROR_BASE 2000 #endif #define FALCON_DBI_ERROR_COLUMN_RANGE (FALCON_DBI_ERROR_BASE+1) #define FALCON_DBI_ERROR_INVALID_DRIVER (FALCON_DBI_ERROR_BASE+2) #define FALCON_DBI_ERROR_NOMEM (FALCON_DBI_ERROR_BASE+3) #define FALCON_DBI_ERROR_CONNPARAMS (FALCON_DBI_ERROR_BASE+4) #define FALCON_DBI_ERROR_CONNECT (FALCON_DBI_ERROR_BASE+5) #define FALCON_DBI_ERROR_QUERY (FALCON_DBI_ERROR_BASE+6) #define FALCON_DBI_ERROR_QUERY_EMPTY (FALCON_DBI_ERROR_BASE+7) #define FALCON_DBI_ERROR_OPTPARAMS (FALCON_DBI_ERROR_BASE+8) #define FALCON_DBI_ERROR_NO_SUBTRANS (FALCON_DBI_ERROR_BASE+9) #define FALCON_DBI_ERROR_NO_MULTITRANS (FALCON_DBI_ERROR_BASE+10) #define FALCON_DBI_ERROR_UNPREP_EXEC (FALCON_DBI_ERROR_BASE+11) #define FALCON_DBI_ERROR_BIND_SIZE (FALCON_DBI_ERROR_BASE+12) #define FALCON_DBI_ERROR_BIND_MIX (FALCON_DBI_ERROR_BASE+13) #define FALCON_DBI_ERROR_EXEC (FALCON_DBI_ERROR_BASE+14) #define FALCON_DBI_ERROR_FETCH (FALCON_DBI_ERROR_BASE+15) #define FALCON_DBI_ERROR_UNHANDLED_TYPE (FALCON_DBI_ERROR_BASE+16) #define FALCON_DBI_ERROR_RESET (FALCON_DBI_ERROR_BASE+17) #define FALCON_DBI_ERROR_BIND_INTERNAL (FALCON_DBI_ERROR_BASE+18) #define FALCON_DBI_ERROR_TRANSACTION (FALCON_DBI_ERROR_BASE+19) #define FALCON_DBI_ERROR_CLOSED_STMT (FALCON_DBI_ERROR_BASE+20) #define FALCON_DBI_ERROR_CLOSED_RSET (FALCON_DBI_ERROR_BASE+21) #define FALCON_DBI_ERROR_CLOSED_DB (FALCON_DBI_ERROR_BASE+22) #define FALCON_DBI_ERROR_DB_NOTFOUND (FALCON_DBI_ERROR_BASE+23) #define FALCON_DBI_ERROR_CONNECT_CREATE (FALCON_DBI_ERROR_BASE+24) namespace Falcon { /** Base error class for all DBI errors. DBI Error descriptions are available in English ONLY, until the new per-module string table support is ready. */ class DBIError: public ::Falcon::Error { public: DBIError(): Error( "DBIError" ) {} DBIError( const ErrorParam ¶ms ); private: void describeError(); }; } #endif modules/native/dbi/include/falcon/dbi_handle.h000066400000000000000000000117321176363201700216730ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_handle.h Database Interface - Main handle driver ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 16 May 2010 00:09:13 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_DBI_HANDLE_H_ #define FALCON_DBI_HANDLE_H_ #include #include namespace Falcon { class DBIStatement; class DBIRecordset; class DBISettingParams; class ItemArray; /** * Base class for handlers. * This class holds handle to database connections. * * Database drivers must derive this and provide specific handlers * towards given connections. * * Shared connection management (i.e. persistent connections, resource * sharing and so on) must be handled at driver level; at user * level, each instance of database object must receive a different * handler. * * The handle is derived from UserData as it is likely to be assigned * to a CoreObject. */ class DBIHandle: public FalconData { public: DBIHandle(); virtual ~DBIHandle(); /** Sets the common transaction options. * * Used to change the default values for transaction creation. * * @param params Parameters for the transaction (see DBISettingParams). * @return true on success, false on parse error */ virtual void options( const String& params ) = 0; /** Return the transaction settings used as the default options by this connection. */ virtual const DBISettingParams* options() const = 0; virtual void begin() = 0; virtual void commit() = 0; virtual void rollback() = 0; /** Writes a select query with limited bounds that is valid for the engine. This method should create a "select" query adding the commands and/or the parameters needed by the engine to limit the resultset to a specified part part of the dataset. The query parameter must be a complete query EXCEPT for the "select" command, which is added by the engine. It must NOT terminate with a ";", which, in case of need is added by the engine. For example, the following query @code SELECT field1, field2 FROM mytable WHERE key = 2; @endcode should be passed as @code field1, field2 FROM mytable WHERE key = 2 @endcode An engine must at least add the "select" command and return the modified query i8n the result output parameter. If supported, it must modify the query so that it contains informations to skip the records selected up to nBegin (0 based), and to return nCount rows. The nCount parameter will be 0 to indicate "from nBegin to the end". It's not possible to return the n-last elements; to do that, reverse the query ordering logic. @param query The SQL statement stripped of the initial "select" and of the final ";" @param nBegin First row to be returned (0-based). @param nCount Number of rows to be returned in the recordset. @param result The SQL query statement correctly modified for the engine to parse it. */ virtual void selectLimited( const String& query, int64 nBegin, int64 nCount, String& result ) = 0; /** Launches a query (an SQL operation bound to return a recordset). * * \param sql SQL query to execute * \param params An array of items that will be used to expand query variables. * \return DBIRecordset if there is an output recordset. * NULL if the query has an error. */ virtual DBIRecordset *query( const String &sql, ItemArray* params=0 )=0; /** Prepare/execute step1 */ virtual DBIStatement* prepare( const String &query )=0; /** Returns the last inserted ID. * * Many engines provide this feature so that the last inserted ID auto-generated * number in the last inserted translation can be retrieved. * * Return -1 if the engine doesn't provide this feature; 0 is the common return * value when no auto-increment ID has been inserted. */ virtual int64 getLastInsertedId( const String& name = "" )=0; /** * Close the connection with the Database. * This tells the DB API that this database will not be used anymore. */ virtual void close()=0; /** * Utility performing direct sql expansion. * * This utility transforms question marks into values (properly formatted and escaped) * to be used in SQL statements. * * Will throw an adequate DBI error in case of expansion error. */ virtual void sqlExpand( const String& sql, String& tgt, const ItemArray& values ); virtual void gcMark( uint32 ); virtual FalconData* clone() const; /** returns the count of rows affected by the last query() operation */ int64 affectedRows(); protected: int64 m_nLastAffected; }; } #endif /* end of dbi_handle.h */ modules/native/dbi/include/falcon/dbi_inbind.h000066400000000000000000000173171176363201700217100ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_bind.h Database Interface Helper for general Falcon-to-C variable binding ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 15 May 2010 23:47:36 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_DBI_BIND_H_ #define FALCON_DBI_BIND_H_ #include #include #include namespace Falcon { class Item; class GarbageLock; class TimeStamp; class String; class ItemArray; /** Time Convert functor. This functor is reimplemented by the drivers to allow transforming a Falcon TimeStamp class into a local timestamp representation. */ class DBITimeConverter: public BaseAlloc { public: inline void operator() ( TimeStamp* ts, void* buffer, int& bufsize ) const { convertTime( ts, buffer, bufsize ); } /** Sublcasses must re-define this to construct a timestamp that can be used in bindings. */ virtual void convertTime( TimeStamp* ts, void* buffer, int& bufsize ) const = 0; }; /** Default time converter. This time converter tranforms falcon timestamp in ISO timestamps, as 1-byte strings encoded like AAAA-MM-GG HH:MM:SS (ignoring milliseconds). */ class DBITimeConverter_ISO: public DBITimeConverter { public: virtual void convertTime( TimeStamp* ts, void* buffer, int& bufsize ) const; }; extern DBITimeConverter_ISO DBITimeConverter_ISO_impl; /** String Convert functor. This functor is reimplemented by the drivers to allow transforming a Falcon string class into a local string representation. */ class DBIStringConverter: public BaseAlloc { public: inline char* operator() ( const String& str, char* target, int &bufsize ) const { return convertString( str, target, bufsize ); } /** Sublcasses must re-define this to construct a string that can be used in bindings. */ virtual char* convertString( const String& str, char* target, int &bufsize ) const = 0; }; /** Default string Convert functor for InBind class. Returns a string converted into a UTF8 string. */ class DBIStringConverter_UTF8: public DBIStringConverter { public: virtual char* convertString( const String& str, char* target, int &bufsize ) const; }; extern DBIStringConverter_UTF8 DBIStringConverter_UTF8_impl; /** Utility string converter functor for InBind class. Returns a string converted into a WCHART string. */ class DBIStringConverter_WCHAR: public DBIStringConverter { public: virtual char* convertString( const String& str, char* target, int &bufsize ) const; }; extern DBIStringConverter_WCHAR DBIStringConverter_WCHAR_impl; /** Helper class to bind item into local database value. This class is used to turn a Falcon item into a C item representation which is used by the vast majority of SQL engines to bind input variables. Creating and populating the final SQL-dependant bind structure is up to the engine, but this class simplifies the rutinary operations of turning falcon strings, integers, objects and other elements into acceptable representations for the final engine. To convert the item from a Falcon timestamp object into a DB-engine timestamp item, this class uses the DBIBind::convertTime virtual function that must be provided by the engine re-implementations. */ class DBIBindItem: public BaseAlloc { public: static const int bufsize = 128; DBIBindItem(); virtual ~DBIBindItem(); typedef enum tag_datatype { t_nil, t_bool, t_int, t_double, t_string, t_time, t_buffer } datatype; void set(const Item& value, const DBITimeConverter& tc=DBITimeConverter_ISO_impl, const DBIStringConverter& sc=DBIStringConverter_UTF8_impl ); void clear(); /** Returns the type of this item. */ datatype type() const { return m_type; } /** Return a void pointer to the stored data. */ void* data() { return &m_cdata.v_int64; } void* databuffer() { if( m_type == t_string || m_type == t_buffer || m_type == t_time ) return m_cdata.v_buffer; return &m_cdata.v_int64; } bool isNil(); bool asBool() const { return m_cdata.v_bool; } int64 asInteger() const { return m_cdata.v_int64; } double asDouble() const { return m_cdata.v_double; } const char* asString() const { return m_cdata.v_string; } void* asBuffer() const { return m_cdata.v_buffer; } int asStringLen() const { return m_buflen; } bool* asBoolPtr() { return &m_cdata.v_bool; } int64* asIntegerPtr() { return &m_cdata.v_int64; } double* asDoublePtr() { return &m_cdata.v_double; } /** Gets the user buffer. Returns 128 bytes of preallocated memory in this object, usually separated by the rest. Can be used to store transformation of the main data type, for example, local SQL engine renderings of the generic timestamp format. */ char* userbuffer() { return m_buffer; } /** Returns the inner buffer lenght. Valid only in case of strings and timestamps. * @return size of the data in the buffer. */ int length() const { return m_buflen; } private: datatype m_type; // Local buffer we use for long int and buffers. typedef union tag_cdata { bool v_bool; double v_double; int64 v_int64; char* v_string; void* v_buffer; } cdata; cdata m_cdata; // local buffer that can be used for several reasons. char m_buffer[bufsize]; int m_buflen; }; /** Base abstract class for DBI input bindings. Engines must reimplement this class to provide the needed callbacks to create their own bind variables. The base class creates DBIBindItems generating appropriate memory locations where to store the bound input (falcon-to-engine) variables. The subclasses will receive callbacks when a new binding begins and when a binding item had a relevant change (i.e. it's memory footprint has changed), so that the change can be reflected in the engine specific binding variables. The binding fails if a previous binding was already performed, and a new binding is tried with different types or with an array of different size. In that case, an appropriate DBI error is raised. */ class DBIInBind: public BaseAlloc { public: /** Creates a input binding. * * Some engines (e.g. sqlite) bind the input buffer BY VALUE; this * requires a complete rebind at each step. * * @param bAlwaysChange rebind at each step. */ DBIInBind( bool bAlwaysChange = false ); virtual ~DBIInBind(); virtual void bind( const ItemArray& arr, const DBITimeConverter& tc=DBITimeConverter_ISO_impl, const DBIStringConverter& sc=DBIStringConverter_UTF8_impl ); /*# Bind to 0 parameters */ void unbind(); /** Called back when the binding is initialized. * * @param size The number of the variables that should be allocated. */ virtual void onFirstBinding( int size ) = 0; /** Called when an item had a relevant change, requiring reset of the underlying variables. * * Use the number passed as parameter to get the relevant item in the m_ibinds array. * On first call of bind(), this method will be called for every item. * * @param num Number of the item. */ virtual void onItemChanged( int num ) = 0; /** Return true if we're processing the items for the fist time. */ bool isFirstLoop() const { return m_size == 0; } protected: DBIBindItem* m_ibind; bool m_bAlwaysChange; int m_size; }; } #endif /* end of dbi_bind.h */ modules/native/dbi/include/falcon/dbi_outbind.h000066400000000000000000000141731176363201700221060ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_outbind.h Database Interface Helper for general C-to-Falcon variable binding ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 17 May 2010 22:32:39 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_DBI_OUTBIND_H_ #define FALCON_DBI_OUTBIND_H_ #include #include #include namespace Falcon { class Item; class GarbageLock; class TimeStamp; class String; class ItemArray; /** Helper class to store (variable) memory for output binds. Each item may be used mainly in two ways: - A total memory may be allocated with alloc(). The amount of allocated memory can then be accessed with allocated() and the memory can be retrieved with memory(); - Otherwise, more blocks can be allocated via allocBlock(), and once read, their actual size can be set via setBlockSize(). When all the data has been read, the blocks can be turned into a linear memory area using consolidate(), which fills the field returned by memory() and allocated(). Notice that the object is created with memory being allocated to a static buffer of 16 bytes (large enough to store many native datatypes). Using memory() before using alloc() will give access to this area, so basic datatypes doesn't need extra allocation. In this case, getMemory() would return 0; so, if you need to get the memory even if small, use allocate() before getMemory(). */ class DBIOutBind: public BaseAlloc { public: DBIOutBind(); ~DBIOutBind(); /** Allocates a new block of given size. The caller may then write up to size bytes in the returned block. The returned block should never be used outside the scope of the calling function (unless You Know What You're Doing). After a succesfull data read, the block should be passed to setBlockSize, to indicate the amount of bytes really stored in the block. After all the blocks have been read, use consolidate() to create a buffer large enough to store all the data and destroy all the blocks used for temporary reads. Never destroy the blocks manually. Use consolidate instead. The destructor will clean existing blocks, if they are left hanging. @param size The amount of bytes that can be stored in the block. @return a newly allocated block. */ void* allocBlock( unsigned size ); /** Indicates how many bytes are really available in a block. * * This method doesn't change the block allocation. Instead, it indicates * how many useful bytes are in the block, and changes the global block size * count. * * @param block The block to be sized. * @param size The size of the block. */ void setBlockSize( void* block, unsigned size ); /** Moves all the block read up to date into the memory buffer. This method destroys all the blocks allocated with allocBlock() after having copied their contents linearly to a final block, which is stored in the main buffer of this class. The total size of the allocated data can be subsequently retrieved using allocated(), while the returned buffer can be accessed later on via the memory() method. If another memory buffer was previously allocated via alloc() or via a former consolidate() it is deleted. */ void* consolidate(); /** Allocate some dynamic memory to be used by this item. Previously allocated memory will be relocated. The allocated size will be returned by allocated(). \return the newly allocated area. */ void* alloc( unsigned size ); /** Ensure that at least size bytes are available. This method ensures that the memory in this object can store at least size bytes. The main difference with alloc() is that memory is not relocated if enough data can be stored in the previously allocated memory. Also, the method will automatically call consolidate if needed. \return the area suitable for the allocation */ void* reserve( unsigned size ); /** Returns the amount of allocated memory. At creation, it reports the size of the default buffer. After alloc() is called, it reports the size set by alloc(). After consolidate() is called, it reports the full size of the read blocks. \return Size of the memory buffer returned by memory(). */ unsigned allocated() const { return m_allocated; } /** Returns the memory buffer held buy this object. At creation, this is set to the internal default buffer. After alloc(), this is the same pointer returned by alloc(). After consolidate(), this is the same pointer returned by consolidate(). If the memory doesn't point to the default creation buffer, getMemory() empties this buffer. After calling getMemory(), the buffer will be 0. */ void* memory() { return m_memory; } /** Gets the allocated memory. Both memory() and allocated() will be set to zero; the caller becomes the owner of the allocated memory and must free it via memFree(). If there isn't any memory allocated via alloc() or consolidate(), the method returns 0. */ void* getMemory(); /** Extra memory space where to store extra data. Many engines require extra allocation space to receive output informations from the database queries. Length and is_null state are the most common output data which requires a local storage where the engine places them. The base class doesn't define them; engines may overload this class, and use this structure as they prefer. */ private: static const int bufsize = 16; char m_stdBuffer[ bufsize ]; unsigned m_allocated; unsigned m_allBlockSizes; void* m_memory; void* m_headBlock; void* m_tailBlock; }; } #endif /* end of dbi_outbind.h */ modules/native/dbi/include/falcon/dbi_params.h000066400000000000000000000212441176363201700217220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_params.h Database Interface - Generic settings/parameters parser for DBI. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 16 May 2010 00:16:00 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_DBI_PARAMS_H_ #define FALCON_DBI_PARAMS_H_ #include #include #include #include #include namespace Falcon { /** An utility used by many drivers to break connection strings. Base class for more specific */ class DBIParams: public BaseAlloc { protected: /** A Specification of a connection parameter. The parameter is a pair of a parameter name (as "uid" or "pwd") and of the output parameter, that is a pointer to a string that will be filled with the parameter name. */ class Param { public: Param( const String& name, String& output ): m_name( name ), m_output( output ), m_szOutput( 0 ), m_cstrOut(0), m_pNext(0) {} Param( const String& name, String& output, const char** szOutput ): m_name( name ), m_output( output ), m_szOutput( szOutput ), m_cstrOut(0), m_pNext(0) {} ~Param(); /** Parses an input string. * @param value A string in format = or = * @return True if the name matches (case insensitive) the name of this parameter. */ bool parse( const String& value ); String m_name; String &m_output; const char** m_szOutput; AutoCString* m_cstrOut; Param* m_pNext; }; Param* m_pFirst; bool parsePart( const String& strPart ); DBIParams(); public: virtual ~DBIParams(); /** Function adding a parse parameter */ virtual void addParameter( const String& name, String& value ); /** Function adding a parse parameter and its c-string value */ virtual void addParameter( const String& name, String& value, const char** szValue ); /** Parse the parameter=value string */ virtual bool parse( const String& connStr ); /** Utility to check for boolean values */ static bool checkBoolean( const String& pvalue, bool &boolVar ); }; /** Parameter parser for the settings string. The setting string is used at DBIConnect, Connection.setOptions and Connection.tropen; they determine the behavior of db fetches, and may be implemented differently by different database drivers. Setting options are: - prefetch: number of records to be pre-fetched at each query. The value may be "all" to wholly fetch queries locally, "none" to prefetch none or an arbitrary number of rows to be read from the server. By default, it's "all". - autocommit: Performs a transaction commit after each sql command. Can be "on" or "off"; it's "off" by default. - cursor: Number of records returned by a query that should trigger the creation of a server side cursor. Can be "none" to prevent creation of server side cursor (the default) "all" to always create a cursor or an arbitrary number to create a server side cursor only if the query returns at least the indicated number of rows. - strings: If "on", all the values are returned as a string. Can be useful if the engine provides this mode natively and if the recordset is needed just for dump on an output device. Using this option in this case will reduce unneeded transformations into Falcon data and then into the external representationss After a complete local prefetch, all the records are moved to the client, so it's possible to issue another query returning a different recordset. If moving all the returned rows to the client is not feasible, but it's still necessary to fetch rows from a query and performing other queries based on the retrieved results, then it's necessary to create a cursor on the server side. Many SQL engines allow to specify to open the cursor directly on the SQL statement, but this option is given to provide the user with the ability to access this feature without using engine-specific SQL. Notice that server-side cursor and complete local prefetch are alternative methods to make the recordset consistent across different queries in the same transaction, so it's pretty useless to use both of them at the same time. This class transforms the given options in C variables that can easily be read by the drivers. Also, drivers can add their own option parsing code by overriding the DBIConnection::parseOptions method. */ class DBISettingParams: public DBIParams { private: String m_sCursor; String m_sAutocommit; String m_sPrefetch; String m_sFetchStrings; static const bool defaultAutocommit = true; static const int defaultCursor = -1; static const int defaultPrefetch = -1; static const bool defaultFetchStrings = false; public: DBISettingParams(); DBISettingParams( const DBISettingParams & other ); virtual ~DBISettingParams(); /** Specific parse analizying the options */ virtual bool parse( const String& connStr ); /** True if the transaction should be autocommit, false otherwise. */ bool m_bAutocommit; /** Cursor invocation treshold. * Will be -1 if cursor should be never used, 0 if always used, * a positive number for a given treshold. */ int64 m_nCursorThreshold; /** Number of rows to be pre-fetched after queries. Will be -1 if recordset must be completely prefetched, 0 to disable pre-fetching and a positive number to ask for prefetching of that many rows. */ int64 m_nPrefetch; /** True if the transaction should be autocommit, false otherwise. */ bool m_bFetchStrings; }; /** An utility used by many drivers to break connection strings. This is just an utility class that a driver may use to break connection strings into parameters for its DB system. A driver is free not to use it if, for example, it thinks it should pass the string as-is to the underlying DB system. The format of the string respects ODBC standards ("parameter=value;..."), so the connection stings for systems that don't rely on that to initialize the connections (as, for example SQLlite and MySQL) looks like ODBC connection, giving a flavor of portability of the connection string across different system. The utility works like this: a set of parameters are declared via the method addParameter(); other than the parameter name, this method allows to declare a parameter value, that is, a locally defined string where the parameter value will be placed if found, and optionally a C zero terminated string where to put the AutoCString result (converting the string in utf-8), in case the driver needs this feature. If the required parameter is not found in the parsed string, the string is left empty, and the C ZTstring, if provided, is set to a null pointer. If it's found, but empty, then the string is set to a pair of double quotes (""), and the c ZTString, if provided, is set to an empty string. To ensure a minimal coherence across different drivers, and to reduce the effort of the implementers, a minimal set of common parameters are added by the constructor of this class. The variables where the parameter values are placed are provided by the class as well. Namely: - uid= The user id for the connection (values placed in m_sUser and m_szUser). - pwd= Password for the connection (values placed in m_sPassword and m_szPassword). - db= Name of the DB to open (values in m_sDb and m_szDb). - host= Host where to perform the connection (values placed in m_sHost and m_szHost). - port= TCP Port where the server is listening (values in m_sPort and m_szPort). - create= set to "always" or "cond" to create the database or try to create it in case it doesn't exist. */ class DBIConnParams: public DBIParams { public: DBIConnParams( bool bNoDefaults = false ); virtual ~DBIConnParams(); // Base parameters known by all systems. String m_sUser; String m_sPassword; String m_sHost; String m_sPort; String m_sDb; String m_sCreate; const char* m_szUser; const char* m_szPassword; const char* m_szHost; const char* m_szPort; const char* m_szDb; const char* m_szCreate; }; } #endif /* dbi_params.h */ modules/native/dbi/include/falcon/dbi_recordset.h000066400000000000000000000060201176363201700224240ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_recordset.h Database Interface - SQL Recordset class ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 16 May 2010 00:09:13 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_DBI_RECORDSET_H_ #define FALCON_DBI_RECORDSET_H_ #include namespace Falcon { class DBIHandle; class Item; /** * Abstraction of recordset class. * * The recordset class is the minimal query access interface unit towards the database. * It represents a single database query with results. Through this class, query data * can be accessed. */ class DBIRecordset : public FalconData { public: DBIRecordset( DBIHandle *dbt ); virtual ~DBIRecordset(); /** Move to the next record * \throw DBIError* in case of error. * \return true on success, false on end of updates reached */ virtual bool fetchRow()=0; /** * Get the current row number. * * \return row index (0 based) or -1 for invalid row */ virtual int64 getRowIndex()=0; /** * Fetch the number of rows in the recordset or -1 if unknown */ virtual int64 getRowCount()=0; /** * Fetch the number of columns in the recordset */ virtual int getColumnCount()=0; /** * Fetch the row headers */ virtual bool getColumnName( int nCol, String& name )=0; /** Gets a value in the recordset. */ virtual bool getColumnValue( int nCol, Item& value )=0; /** Returns the full description of a field in the recordset. @note To be introduced in the next version. Returns a blessed dictionary which gives informations about a column in the recordset. The minimal information that every driver should return is: - "name" - the name of the field - "size" - Size of the field. 0 can be returned for numeric types or for blobs. - "type" - Basic SQL type of the field. - "full_type" - SQL type that can be used in a CREATE/ALTER table statement to recreate the field. - "native_type" - Native type ID for the engine. */ //virtual CoreDict* getColumnDescription( int nCol )=0; /** Gets a type in the recordset. */ //virtual dbi_status getColumnType( int nCol, dbi_type& type )=0; /** Skip the required amount of records from this position on. */ virtual bool discard( int64 ncount ) = 0; /** * Close the recordset */ virtual void close()=0; /** * Get the next recordset -- if any. * * Normally returns 0. Only certain engines support this feature. */ virtual DBIRecordset* getNext(); //========================================================= // Manage base class control. // virtual FalconData *clone() const; virtual void gcMark( uint32 ); protected: DBIHandle* m_dbh; }; } #endif /* end of dbi_recorset.h */ modules/native/dbi/include/falcon/dbi_refcount.h000077500000000000000000000017121176363201700222650ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_refcount.h SQLite3 driver main module interface ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_DBI_REFCOUNT_H #define FALCON_DBI_REFCOUNT_H namespace Falcon { template class DBIRefCounter { public: DBIRefCounter( const _T& handler ): m_Handler(handler), m_nRefCount(1) {} void incref() { m_nRefCount ++; } void decref() { if ( --m_nRefCount == 0 ) delete this; } const _T& handle() const { return m_Handler; } _T& handle() { return m_Handler; } protected: virtual ~DBIRefCounter() { } private: _T m_Handler; int m_nRefCount; }; } #endif /* end of dbi_refcount.h */ modules/native/dbi/include/falcon/dbi_stmt.h000066400000000000000000000056021176363201700214260ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: dbi_trans.h Database Interface - SQL Transaction class. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 16 May 2010 00:09:13 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_DBI_TRANS_H_ #define FALCON_DBI_TRANS_H_ #include #include namespace Falcon { class DBIHandle; class DBISettingParams; class DBIRecordset; class ItemArray; /** Abstraction of statement class. */ class DBIStatement: public FalconData { public: DBIStatement( DBIHandle *dbh ); virtual ~DBIStatement(); /** prepare/execute step2 * @param params the parameters. * */ virtual DBIRecordset* execute( ItemArray* params = 0 ) =0; virtual void reset()=0; /** Closes the transaction. * * By default, the engine must also close the transaction. */ virtual void close()=0; /** * Open a blob entity. * \param blobId the ID through which the field is known. * \param stauts a DBI error code (on error). * \return On success, an open stream to the blob, or 0 on failure. * The BlobID database-specific, and may also be a fully binary value (i.e. a 64 bit numeric ID). * It's responsibility of the driver to correctly decode the contents of the blob. */ //virtual DBIBlobStream *openBlob( const String &blobId, dbi_status &status )=0; /** * Create a new blob entity. * \param stauts a DBI error code (on error). * \param params Driver specific settings (i.e. blob subtype). * \param bBinary The caller will set this to true to override generic parameters. * \return On success, an open stream to the blob, or 0 on failure. * * The BlobID database-specific, and may also be a fully binary value (i.e. a 64 bit numeric ID). * It's responsibility of the driver to correctly decode the contents of the blob. * * If bBinary is true, the drivers should try to create a binary-oriented blob, while * if its false, they should use a text-oriented blob. Driver specific parameters must * override this setting, which must be ignored in case a specific setting is provided. */ //virtual DBIBlobStream *createBlob( dbi_status &status, const String ¶ms= "", bool bBinary = false )=0; /** Get the DBIHandle associated with this transaction. */ DBIHandle *getHandle() const { return m_dbh; } virtual void gcMark( uint32 ); virtual FalconData* clone() const; /** returns the count of rows affected by the last query() operation */ int64 affectedRows() const { return m_nLastAffected; } protected: DBIHandle *m_dbh; int64 m_nLastAffected; }; } #endif /* end of dbi_trans.h */ modules/native/dbi/include/falcon/srv/000077500000000000000000000000001176363201700202575ustar00rootroot00000000000000modules/native/dbi/include/falcon/srv/dbi_service.h000066400000000000000000000115331176363201700227110ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: dbi_service.h * * DBI service that DBI drivers inherit from and implement * ------------------------------------------------------------------- * Author: Giancarlo Niccolai and Jeremy Cowgar * Begin: Sun, 23 Dec 2007 19:22:38 +0100 * * ------------------------------------------------------------------- * (C) Copyright 2007-2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #ifndef DBI_SERVICE_H #define DBI_SERVICE_H #include #include #include #include #include namespace Falcon { class VMachine; class String; class ItemArray; class DBIStatement; class DBIHandle; typedef enum { dbit_string, dbit_integer, dbit_integer64, dbit_numeric, dbit_date, dbit_time, dbit_datetime, dbit_boolean, dbit_blob } dbi_type; #if 0 /** * Abstraction of blob stream class. * * The DBIBlobStream class holds a stream specialized in blob writing. * It just provides an extra BlobID entry and driver specific setting * parser that will be hooked by the drivers. */ class DBIBlobStream : public Stream { String m_blobID; String m_falconClassName; protected: DBIBlobStream(): Stream( t_membuf ), m_falconClassName( "DBIBlobStream") {} /** Sets a falcon specific class name that should wrap this subclass. */ DBIBlobStream( const String &className ): Stream( t_membuf ), m_falconClassName( className ) {} public: void setBlobID( const String &blobID ) { m_blobID = blobID; } const String &getBlobID() { return m_blobID; } /** Retiurns the Falcon class name that encapsulates this stream. If drivers don't want to bother creating a new Stream class for their own blobs, they can just use the default DBIBlobStream class declared in the base DBI. However, submodules may be willing to provide more functionalities on open blobs, in example, they may return informations about the blob size, fragments, type and so on. In that case, specific per-module blob class name may be returned; In case an encapsulation is needed by the script, the DBI module will ask the VM to instance the required blob class, and will use that to the Falcon::DBIBlobStream instance. */ virtual const String &getFalconClassName() const { return m_falconClassName; } }; #endif /** * Base class for database providers. * * Database provider services must derive from this class and subscribe * their module with different names. */ class DBIService: public Service { protected: DBIService( const String &name ): Service( name ) {} public: virtual ~DBIService() {} /** * Initialization hook. * * It gets called as soon as the service is loaded. It may be used by the * module to initialize global data. */ virtual void init()=0; /** * Initialization hook * * It gets called as soon as the service is loaded. It may be used by the * module to initialize global data. * * The function returns a DBIHandle because some driver may want to re-use * already existing DBI handles if they are capable to perform concurrent * operations and if the connection parameters are compatible with already * existing connections. * * \note add doc on connection parameters * \param parameters the connection parameters. * \return a configured DBIHandle or 0 on error. */ virtual DBIHandle *connect( const String ¶meters )=0; /** * Creates an instance of database handle for Falcon scripts. * * This function creates a core object getting the DBI database handler subclass * managed by the providing module. * * It can't fail. */ virtual CoreObject *makeInstance( VMachine *vm, DBIHandle *dbh ) = 0; }; /** * Service used to load DBI modules. * * This service is meant as an utility and is mainly used internally by the DBI module. * It resolves the module names, checks if they are currently loaded in the VM and, if not, * loads and links them. */ class DBILoader: public Service { protected: DBILoader( const String &name ): Service( name ) {} public: /** * Loads the required provider and returns the service it provides. * * If the service is already present in the VM, that service is returned instead. * The VM is used as the error handler of the loader that will load the modules, * so, in case of errors, the VM will be notified with a standard module loading * error. * * \return a DBIService instance or 0 */ virtual DBIService *loadDbProvider( VMachine *vm, const String &provName )=0; //virtual void escapeString( const String &value, String &escaped ) = 0; }; } #endif /* end of dbiservice.h */ modules/native/dbi/mysql/000077500000000000000000000000001176363201700157255ustar00rootroot00000000000000modules/native/dbi/mysql/CMakeLists.txt000066400000000000000000000047341176363201700204750ustar00rootroot00000000000000#################################################################### # The Falcon Programming language # # CMake configuration file for module mysql #################################################################### if(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) endif(COMMAND cmake_policy) option( FALCON_DBI_MYSQL_MY_RPATH "Append mysqlclient library location to install RPATH" ON) falcon_define_module( FALCON_MODULE mysql ) # Find MYSQL SET(MYSQL_INCLUDE_SEARCH_PATH ${MYSQL_INCLUDE_DIR} /usr/include/mysql /usr/local/include/mysql ) FIND_PATH(MYSQL_REAL_INCLUDE_DIR mysql.h ${MYSQL_INCLUDE_SEARCH_PATH}) # Find MySQL Client Library SET(MYSQL_LIB_SEARCH_PATH ${MYSQL_LIB_DIR} /usr/lib /usr/lib/mysql /usr/local/lib /usr/local/lib/mysql) SET(MYSQL_LIB_NAMES mysqlclient mysqlclient_r) FIND_LIBRARY(MYSQL_LIBRARY NAMES ${MYSQL_LIB_NAMES} PATHS ${MYSQL_LIB_SEARCH_PATH} ) IF (MYSQL_REAL_INCLUDE_DIR AND MYSQL_LIBRARY) INCLUDE_DIRECTORIES(BEFORE ${MYSQL_REAL_INCLUDE_DIR}) ELSE (MYSQL_REAL_INCLUDE_DIR AND MYSQL_LIBRARY) IF(NOT MYSQL_REAL_INCLUDE_DIR) MESSAGE(SEND_ERROR "MySQL include path was not found") MESSAGE(SEND_ERROR "Looked in: ${MYSQL_INCLUDE_SEARCH_PATH}") ENDIF (NOT MYSQL_REAL_INCLUDE_DIR) IF (NOT MYSQL_LIBRARY) MESSAGE(SEND_ERROR "MySQL client library was not found") MESSAGE(SEND_ERROR "Looked for: ${MYSQL_LIB_NAMES}") MESSAGE(SEND_ERROR "Looked in: ${MYSQL_LIB_SEARCH_PATH}") ENDIF (NOT MYSQL_LIBRARY) MESSAGE(FATAL_ERROR "Cannot build MySQL DBI module") ENDIF (MYSQL_REAL_INCLUDE_DIR AND MYSQL_LIBRARY) MESSAGE(STATUS "Found mysql.h in ${MYSQL_REAL_INCLUDE_DIR}") MESSAGE(STATUS "Found mysql client library, ${MYSQL_LIBRARY}") # Inclusion settings INCLUDE_DIRECTORIES(.) INCLUDE_DIRECTORIES(../include) INCLUDE_DIRECTORIES(../dbi) # Target ADD_LIBRARY( ${FALCON_MODULE} MODULE ${dbi_common_files} mysql.cpp mysql_ext.cpp mysql_mod.cpp ) # Link IF(MSVC) SET(MYSQL_OPT_LIBS wsock32) SET_TARGET_PROPERTIES(${FALCON_MODULE} PROPERTIES LINK_FLAGS "/NODEFAULTLIB:library") ENDIF(MSVC) TARGET_LINK_LIBRARIES(${FALCON_MODULE} falcon_engine ${MYSQL_OPT_LIBS} ${MYSQL_LIBRARY}) if(FALCON_DBI_MYSQL_MY_RPATH) get_filename_component(rpath "${MYSQL_LIBRARY}" PATH ) set_target_properties( ${FALCON_MODULE} PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_RPATH}:${rpath}" ) message( "DBI.Mysql Setting install rpath to ${CMAKE_INSTALL_RPATH}:${rpath}" ) endif() falcon_install_module2( ${FALCON_MODULE} dbi ) modules/native/dbi/mysql/mysql.cpp000066400000000000000000000035541176363201700176050ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: mysql.cpp * * Mysql driver main module * * This is BOTH a driver for the DBI interface AND a standalone * MySQL module. * ------------------------------------------------------------------- * Author: Jeremy Cowgar * Begin: Wed Jan 02 21:35:18 2008 * * ------------------------------------------------------------------- * (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include "mysql_mod.h" #include "version.h" #include "mysql_ext.h" // Instantiate the driver service Falcon::DBIServiceMySQL theMySQLService; /*# @module dbi.mysql MySQL driver module @brief DBI extension supporting MySQL Directly importable as @b dbi.mysql, it is usually loaded through the @a dbi module. */ // the main module FALCON_MODULE_DECL { // Module declaration Falcon::Module *self = new Falcon::Module(); self->name( "mysql" ); self->engineVersion( FALCON_VERSION_NUM ); self->version( VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION ); // first of all, we need to declare our dependency from the DBI module. self->addDepend( "dbi", "dbi", true, false ); // also, we declare a MySQL class, which derives from DBIHandler which // is in the DBI module. Falcon::Symbol *dbh_class = self->addExternalRef( "dbi.%Handle" ); // it's external dbh_class->imported( true ); Falcon::Symbol *mysql_class = self->addClass( "MySQL", Falcon::Ext::MySQL_init ); mysql_class->getClassDef()->addInheritance( new Falcon::InheritDef( dbh_class ) ); mysql_class->setWKS( true ); // we don't have extra functions for the dbhandler of mysql. If we had, // this would be the right place to store them. // service publication self->publishService( &theMySQLService ); // we're done return self; } /* end of mysql.cpp */ modules/native/dbi/mysql/mysql_ext.cpp000066400000000000000000000037751176363201700204720ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: mysql_ext.cpp * * MySQL Falcon extension interface * ------------------------------------------------------------------- * Author: Jeremy Cowgar * Begin: Wed Jan 02 21:35:18 2008 * * ------------------------------------------------------------------- * (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include #include "mysql_mod.h" #include "mysql_ext.h" /*# @beginmodule dbi.mysql */ namespace Falcon { namespace Ext { /*# @class MySQL @brief Direct interface to MySQL database. @param connect String containing connection parameters. The connect string uses the standard connection values: - uid: user id - pwd: password - db: database where to connect - host: host where to connect (defaults to localhost) - port: prot where to connect (defaults to mysql standard port) Other than that, mysql presents the following driver-specific parameters - socket: UNIX socket name for UNIX-socket based MySQL connections. */ FALCON_FUNC MySQL_init( VMachine *vm ) { Item *paramsI = vm->param(0); Item *i_tropts = vm->param(1); if ( paramsI == 0 || ! paramsI->isString() || ( i_tropts != 0 && ! i_tropts->isString() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S,[S]" ) ); } String *params = paramsI->asString(); DBIHandle *hand = 0; try { hand = theMySQLService.connect( *params ); if( i_tropts != 0 ) { hand->options( *i_tropts->asString() ); } // great, we have the database handler open. Now we must create a falcon object to store it. CoreObject *instance = theMySQLService.makeInstance( vm, hand ); vm->retval( instance ); } catch( DBIError* error ) { delete hand; throw error; } } } /* namespace Ext */ } /* namespace Falcon */ /* end of mysql_ext.cpp */ modules/native/dbi/mysql/mysql_ext.h000066400000000000000000000013131176363201700201210ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: mysql_ext.h * * MySQL Falcon extension interface * ------------------------------------------------------------------- * Author: Jeremy Cowgar * Begin: Wed Jan 02 21:35:18 2008 * * ------------------------------------------------------------------- * (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ /* #include #include */ #include #ifndef MYSQL_EXT_H #define MYSQL_EXT_H namespace Falcon { class VMachine; namespace Ext { FALCON_FUNC MySQL_init( VMachine *vm ); } } #endif /* MYSQL_EXT_H */ /* end of mysql_ext.h */ modules/native/dbi/mysql/mysql_mod.cpp000066400000000000000000001056211176363201700204420ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: mysql_mod.cpp * * MySQL Falcon service/driver * ------------------------------------------------------------------- * Author: Giancarlo Niccolai * Begin: Sat, 22 May 2010 14:44:49 +0200 * * ------------------------------------------------------------------- * (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include #include #include #include #include #include "mysql_mod.h" #include namespace Falcon { /****************************************************************************** * Private class used to convert timestamp to MySQL format. *****************************************************************************/ class DBITimeConverter_MYSQL_TIME: public DBITimeConverter { public: virtual void convertTime( TimeStamp* ts, void* buffer, int& bufsize ) const; } DBITimeConverter_MYSQL_TIME_impl; void DBITimeConverter_MYSQL_TIME::convertTime( TimeStamp* ts, void* buffer, int& bufsize ) const { fassert( ((unsigned)bufsize) >= sizeof( MYSQL_TIME ) ); MYSQL_TIME* mtime = (MYSQL_TIME*) buffer; mtime->year = (unsigned) ts->m_year; mtime->month = (unsigned) ts->m_month; mtime->day = (unsigned) ts->m_day; mtime->hour = (unsigned) ts->m_hour; mtime->minute = (unsigned) ts->m_minute; mtime->second = (unsigned) ts->m_second; mtime->second_part = (unsigned) ts->m_msec; mtime->neg = 0; bufsize = sizeof( MYSQL_TIME ); } /****************************************************************************** * (Input) bindings class *****************************************************************************/ MyDBIInBind::MyDBIInBind( MYSQL_STMT* stmt ): m_mybind(0), m_stmt( stmt ) {} MyDBIInBind::~MyDBIInBind() { memFree( m_mybind ); } void MyDBIInBind::onFirstBinding( int size ) { m_mybind = (MYSQL_BIND*) memAlloc( sizeof(MYSQL_BIND) * size ); memset( m_mybind, 0, sizeof(MYSQL_BIND) * size ); } void MyDBIInBind::onItemChanged( int num ) { DBIBindItem& item = m_ibind[num]; MYSQL_BIND& myitem = m_mybind[num]; switch( item.type() ) { // set to null case DBIBindItem::t_nil: myitem.buffer_type = MYSQL_TYPE_NULL; *((my_bool*) item.data()) = 1; break; case DBIBindItem::t_bool: myitem.buffer_type = MYSQL_TYPE_BIT; myitem.buffer = item.asBoolPtr(); myitem.buffer_length = 1; break; case DBIBindItem::t_int: myitem.buffer_type = MYSQL_TYPE_LONGLONG; myitem.buffer = item.asIntegerPtr(); myitem.buffer_length = sizeof( int64 ); break; case DBIBindItem::t_double: myitem.buffer_type = MYSQL_TYPE_DOUBLE; myitem.buffer = item.asDoublePtr(); myitem.buffer_length = sizeof( double ); break; case DBIBindItem::t_string: myitem.buffer_type = MYSQL_TYPE_STRING; myitem.buffer = (void*) item.asString(); myitem.buffer_length = item.asStringLen(); break; case DBIBindItem::t_buffer: myitem.buffer_type = MYSQL_TYPE_BLOB; // Blobs in prepared statements are treated strangely: // The first push is ok, but the other require either rebind or // send_long_data to be called on the statement. // It seems that stmt_param_bind calls send_long_data on its own to // circumvent a bug -- so, the next time YOU must call it in its place. if ( myitem.buffer != 0 ) { mysql_stmt_send_long_data( m_stmt, num, (const char*) item.asBuffer(), item.asStringLen() ); } myitem.buffer = item.asBuffer(); myitem.buffer_length = item.asStringLen(); break; case DBIBindItem::t_time: myitem.buffer_type = MYSQL_TYPE_TIMESTAMP; myitem.buffer = item.asBuffer(); myitem.buffer_length = sizeof( MYSQL_TIME ); break; } } /****************************************************************************** * Recordset class *****************************************************************************/ DBIRecordsetMySQL::DBIRecordsetMySQL( DBIHandleMySQL *dbh, MYSQL_RES *res, bool bCanSeek ) : DBIRecordset( dbh ), m_res( res ), m_bCanSeek( bCanSeek ) { m_row = -1; // BOF m_rowCount = -1; // default -- not known m_columnCount = mysql_num_fields( res ); m_fields = mysql_fetch_fields( res ); m_pConn = dbh->getConn(); m_pConn->incref(); } DBIRecordsetMySQL::~DBIRecordsetMySQL() { if ( m_res != 0 ) close(); } int DBIRecordsetMySQL::getColumnCount() { return m_columnCount; } bool DBIRecordsetMySQL::getColumnName( int nCol, String& name ) { if( nCol >=0 && nCol < m_columnCount ) { name.fromUTF8( m_fields[nCol].name ); return true; } return false; } int64 DBIRecordsetMySQL::getRowCount() { return m_rowCount; } int64 DBIRecordsetMySQL::getRowIndex() { return m_row; } void DBIRecordsetMySQL::close() { if ( m_res != 0 ) { mysql_free_result( m_res ); m_res = 0; m_pConn->decref(); } } /****************************************************************************** * Recordset class --- when using statements. *****************************************************************************/ DBIRecordsetMySQL_STMT::DBIRecordsetMySQL_STMT( DBIHandleMySQL *dbh, MYSQL_RES *res, MYSQLStmtHandle *pStmt, bool bCanSeek ) : DBIRecordsetMySQL( dbh, res, bCanSeek ), m_stmt( pStmt->handle() ), m_pStmt( pStmt ) { pStmt->incref(); } DBIRecordsetMySQL_STMT::DBIRecordsetMySQL_STMT( DBIHandleMySQL *dbh, MYSQL_RES *res, MYSQL_STMT *stmt, bool bCanSeek ) : DBIRecordsetMySQL( dbh, res, bCanSeek ), m_stmt( stmt ), m_pStmt( new MYSQLStmtHandle(stmt) ) { } void DBIRecordsetMySQL_STMT::init() { // bind the output values m_pMyBind = (MYSQL_BIND*) memAlloc( sizeof( MYSQL_BIND ) * m_columnCount ); memset( m_pMyBind, 0, sizeof( MYSQL_BIND ) * m_columnCount ); m_pOutBind = new MyDBIOutBind[ m_columnCount ]; // keep track of blobs: they must be zeroed before each fetch m_pBlobId = new int[m_columnCount]; m_nBlobCount = 0; for ( int c = 0; c < m_columnCount; c++ ) { // blob field? -- we need to know its length. MYSQL_FIELD& field = m_fields[c]; MyDBIOutBind& ob = m_pOutBind[c]; MYSQL_BIND& mb = m_pMyBind[c]; mb.buffer_type = field.type; // Accept to blob in sizes < 1024 if( field.type == MYSQL_TYPE_DATE || field.type == MYSQL_TYPE_TIME || field.type == MYSQL_TYPE_DATETIME || field.type == MYSQL_TYPE_TIMESTAMP || field.type == MYSQL_TYPE_NEWDATE ) { mb.buffer_length = sizeof( MYSQL_TIME ); mb.buffer = ob.reserve( mb.buffer_length ); } else if( field.length >= 1024 && ( field.type == MYSQL_TYPE_TINY_BLOB || field.type == MYSQL_TYPE_BLOB || field.type == MYSQL_TYPE_MEDIUM_BLOB || field.type == MYSQL_TYPE_LONG_BLOB ) ) { // if we have a large blob, fetch it separately m_pBlobId[m_nBlobCount++] = c; } else { mb.buffer_length = field.length + 1; mb.buffer = ob.reserve( field.length + 1 ); } mb.length = &ob.nLength; mb.is_null = &ob.bIsNull; } if( mysql_stmt_bind_result( m_stmt, m_pMyBind ) != 0 ) { static_cast(m_dbh)-> throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_BIND_MIX ); } m_rowCount = mysql_stmt_affected_rows( m_stmt ); } DBIRecordsetMySQL_STMT::~DBIRecordsetMySQL_STMT() { close(); memFree( m_pMyBind ); delete m_pOutBind; delete[] m_pBlobId; } bool DBIRecordsetMySQL_STMT::getColumnValue( int nCol, Item& value ) { if ( m_row == -1 || nCol < 0 || nCol >= m_columnCount ) { return false; } // if the field is nil, return nil if( *m_pMyBind[nCol].is_null ) { value.setNil(); return true; } unsigned long dlen = *m_pMyBind[nCol].length; MyDBIOutBind& outbind = m_pOutBind[nCol]; switch( m_fields[nCol].type ) { case MYSQL_TYPE_NULL: value.setNil(); break; case MYSQL_TYPE_TINY: value.setInteger( (*(char*) outbind.memory() ) ); break; case MYSQL_TYPE_YEAR: case MYSQL_TYPE_SHORT: value.setInteger( (*(short*) outbind.memory() ) ); break; case MYSQL_TYPE_INT24: case MYSQL_TYPE_LONG: case MYSQL_TYPE_ENUM: case MYSQL_TYPE_GEOMETRY: value.setInteger( (*(int32*) outbind.memory() ) ); break; case MYSQL_TYPE_LONGLONG: value.setInteger( (*(int64*) outbind.memory() ) ); break; case MYSQL_TYPE_FLOAT: value.setNumeric( (*(float*) outbind.memory() ) ); break; case MYSQL_TYPE_DOUBLE: value.setNumeric( (*(double*) outbind.memory() ) ); break; case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: { // encoding is utf-8, and values are in range < 127 String sv = (char*) outbind.memory(); double dv=0.0; sv.parseDouble(dv); value.setNumeric(dv); } break; case MYSQL_TYPE_DATE: case MYSQL_TYPE_TIME: case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_NEWDATE: { VMachine* vm = VMachine::getCurrent(); if( vm == 0 ) { return false; } MYSQL_TIME* mtime = (MYSQL_TIME*) outbind.memory(); TimeStamp* ts = new TimeStamp; ts->m_year = mtime->year; ts->m_month = mtime->month; ts->m_day = mtime->day; ts->m_hour = mtime->hour; ts->m_minute = mtime->minute; ts->m_second = mtime->second; ts->m_msec = mtime->second_part; CoreObject *ots = vm->findWKI("TimeStamp")->asClass()->createInstance(); ots->setUserData( ts ); value = ots; } break; // string types case MYSQL_TYPE_BIT: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: // text? if( m_fields[nCol].charsetnr == 63 ) // sic -- from manual { value = new MemBuf_1( (byte*) outbind.memory(), dlen ); } else { CoreString* res = new CoreString; if( dlen > 0 ) { //((char*) outbind.memory())[ dlen -1] = 0; res->fromUTF8( (char*) outbind.memory() ); } value = res; } break; case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: // read the missing memory -- and be sure to alloc if( dlen != 0 ) { outbind.alloc( dlen + 1 ); m_pMyBind[nCol].buffer_length = dlen+1; m_pMyBind[nCol].buffer = outbind.memory(); if( mysql_stmt_fetch_column( m_stmt, m_pMyBind + nCol, nCol, 0 ) != 0 ) { static_cast< DBIHandleMySQL *>(m_dbh) ->throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_FETCH ); } } // text? if( m_fields[nCol].charsetnr == 63 ) // sic -- from manual { // give ownership if( dlen == 0 ) { value = new MemBuf_1( 0, 0 ); } else { value = new MemBuf_1( (byte*) outbind.getMemory(), dlen, memFree ); } } else { if( dlen == 0 ) { value = new CoreString( "" ); } else { ((char*) outbind.memory())[ dlen ] = 0; CoreString* res = new CoreString; res->fromUTF8( (char*) outbind.memory() ); value = res; } } break; default: static_cast< DBIHandleMySQL *>(m_dbh) ->throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_UNHANDLED_TYPE ); } return true; } bool DBIRecordsetMySQL_STMT::discard( int64 ncount ) { if( m_res == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_RSET, __LINE__ ) ); // we have all the records. We may seek if( m_bCanSeek ) { mysql_stmt_data_seek( m_stmt, (uint64) ncount + (m_row == 0 ? 0 : m_row+1) ); } else { for ( int64 i = 0; i < ncount; ++i ) { int res = mysql_stmt_fetch( m_stmt ); if( res == MYSQL_NO_DATA ) return false; if( res == 1 ) { static_cast< DBIHandleMySQL *>(m_dbh) ->throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_UNHANDLED_TYPE ); } } } return true; } bool DBIRecordsetMySQL_STMT::fetchRow() { if( m_res == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_RSET, __LINE__ ) ); // first, zero excessively long blobs. for( int i = 0; i < m_nBlobCount; ++i ) { MYSQL_BIND& bind = m_pMyBind[ m_pBlobId[i] ]; bind.buffer_length = 0; *bind.length = 0; bind.buffer = 0; } // then do the real fetch int res = mysql_stmt_fetch( m_stmt ); if( res == 1 ) { // there's an error. static_cast< DBIHandleMySQL *>(m_dbh) ->throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_FETCH ); } else if ( res == MYSQL_NO_DATA ) { return false; } // we have the values m_row++; return true; } void DBIRecordsetMySQL_STMT::close() { DBIRecordsetMySQL::close(); if ( m_stmt != 0 ) { while( mysql_next_result( m_pConn->handle() ) == 0 ) { MYSQL_RES *res = mysql_use_result( m_pConn->handle() ); if( res != NULL ) { mysql_free_result( res ); } } m_stmt = 0; m_pStmt->decref(); } } DBIRecordset* DBIRecordsetMySQL_STMT::getNext() { DBIHandleMySQL* mysql = static_cast(m_dbh); if ( mysql_next_result( m_pConn->handle() ) == 0 ) { // We want a result recordset MYSQL_RES * meta = mysql_stmt_result_metadata( m_pStmt->handle() ); if( meta == 0 ) { //No, we have nothing to return. mysql->throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_FETCH ); } // ok. Do the user wanted all the result back? if( m_dbh->options()->m_nPrefetch < 0 ) { if( mysql_stmt_store_result( m_pStmt->handle() ) != 0 && mysql_errno( m_pConn->handle() ) != 0 ) { mysql_free_result( meta ); mysql->throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_FETCH ); } } DBIRecordsetMySQL_STMT* recset = new DBIRecordsetMySQL_STMT( mysql, meta, m_pStmt ); // -- may throw try { recset->init(); } catch( ... ) { delete recset; throw; } return recset; } return 0; } /****************************************************************************** * Recordset class --- when using query. *****************************************************************************/ DBIRecordsetMySQL_RES::DBIRecordsetMySQL_RES( DBIHandleMySQL *dbh, MYSQL_RES *res, bool bCanSeek ) : DBIRecordsetMySQL( dbh, res, bCanSeek ) { m_rowCount = mysql_num_rows( res ); // Only valid when using mysql_store_result instead of use_result } DBIRecordsetMySQL_RES::~DBIRecordsetMySQL_RES() { } bool DBIRecordsetMySQL_RES::getColumnValue( int nCol, Item& value ) { if ( m_row == -1 || nCol < 0 || nCol >= m_columnCount ) { return false; } const char* data = m_rowData[nCol]; if( data == 0 ) { value.setNil(); return true; } switch( m_fields[nCol].type ) { case MYSQL_TYPE_NULL: value.setNil(); break; case MYSQL_TYPE_TINY: case MYSQL_TYPE_YEAR: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_INT24: case MYSQL_TYPE_LONG: case MYSQL_TYPE_ENUM: case MYSQL_TYPE_GEOMETRY: case MYSQL_TYPE_LONGLONG: { int64 vn; String sv(data); sv.parseInt(vn); value = vn; } break; case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_NEWDECIMAL: { double vn; String sv(data); sv.parseDouble(vn); value = vn; } break; case MYSQL_TYPE_DATE: value = makeTimestamp( String(data) + " 00:00:00"); break; case MYSQL_TYPE_TIME: value = makeTimestamp( String( "0000-00-00 " ) + String(data) ); break; case MYSQL_TYPE_DATETIME: case MYSQL_TYPE_TIMESTAMP: case MYSQL_TYPE_NEWDATE: value = makeTimestamp( String(data) ); break; // string types case MYSQL_TYPE_BIT: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VARCHAR: case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: // text? if( m_fields[nCol].flags & BINARY_FLAG ) // sic -- from manual { unsigned long* lengths = mysql_fetch_lengths( m_res ); byte* mem = (byte*) memAlloc( lengths[nCol] ); memcpy( mem, data, lengths[nCol] ); value = new MemBuf_1( mem, lengths[nCol], memFree ); } else { CoreString* vs = new CoreString; vs->fromUTF8( data ); value = vs; } break; default: static_cast< DBIHandleMySQL *>(m_dbh) ->throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_UNHANDLED_TYPE ); } return true; } CoreObject* DBIRecordsetMySQL_RES::makeTimestamp( const String& str ) { VMachine* vm = VMachine::getCurrent(); if( vm == 0 ) { static_cast< DBIHandleMySQL *>(m_dbh) ->throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_UNHANDLED_TYPE ); } CoreObject *ots = vm->findWKI("TimeStamp")->asClass()->createInstance(); TimeStamp* ts = new TimeStamp; int64 ival; str.subString(0,4).parseInt(ival); ts->m_year = ival; str.subString(5,7).parseInt(ival); ts->m_month = ival; str.subString(8,10).parseInt(ival); ts->m_day = ival; str.subString(11,13).parseInt(ival); ts->m_hour = ival; str.subString(14,16).parseInt(ival); ts->m_minute = ival; str.subString(17).parseInt(ival); ts->m_second = ival; ts->m_msec = 0; ots->setUserData( ts ); return ots; } bool DBIRecordsetMySQL_RES::discard( int64 ncount ) { if( m_res == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_RSET, __LINE__ ) ); // we have all the records. We may seek if( m_dbh->options()->m_nPrefetch == -1 ) { m_row = ncount + (m_row == 0 ? 0 : m_row+1); mysql_data_seek( m_res, (uint64) m_row ); } else { for ( int64 i = 0; i < ncount; ++i ) { MYSQL_ROW row = mysql_fetch_row( m_res ); if( row == 0 ) { if ( mysql_errno( m_pConn->handle() ) != 0 ) { static_cast< DBIHandleMySQL *>(m_dbh) ->throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_UNHANDLED_TYPE ); } return false; } m_row++; } } return true; } bool DBIRecordsetMySQL_RES::fetchRow() { if( m_res == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_RSET, __LINE__ ) ); m_rowData = mysql_fetch_row( m_res ); if ( m_rowData == 0 ) return false; // we have the values m_row++; return true; } /****************************************************************************** * Recordset class --- when using query + direct string output. *****************************************************************************/ DBIRecordsetMySQL_RES_STR::DBIRecordsetMySQL_RES_STR( DBIHandleMySQL *dbh, MYSQL_RES *res, bool bCanSeek ) : DBIRecordsetMySQL_RES( dbh, res, bCanSeek ) { } DBIRecordsetMySQL_RES_STR::~DBIRecordsetMySQL_RES_STR() { } bool DBIRecordsetMySQL_RES_STR::getColumnValue( int nCol, Item& value ) { if ( m_row == -1 || nCol < 0 || nCol >= m_columnCount ) { return false; } const char* data = m_rowData[nCol]; if( data == 0 || m_fields[nCol].type == MYSQL_TYPE_NULL ) { value.setNil(); } else if( m_fields[nCol].charsetnr == 63 && IS_LONGDATA(m_fields[nCol].type ) ) // sic -- from manual { unsigned long* lengths = mysql_fetch_lengths( m_res ); byte* mem = (byte*) memAlloc( lengths[nCol] ); memcpy( mem, data, lengths[nCol] ); value = new MemBuf_1( mem, lengths[nCol], memFree ); } else { CoreString* vs = new CoreString; vs->fromUTF8( data ); value = vs; } return true; } /****************************************************************************** * Transaction class *****************************************************************************/ DBIStatementMySQL::DBIStatementMySQL( DBIHandleMySQL *dbh, MYSQL_STMT* stmt ): DBIStatement( dbh ), m_statement( stmt ), m_inBind(0), m_bBound( false ) { m_pConn = dbh->getConn(); m_pConn->incref(); m_pStmt = new MYSQLStmtHandle( stmt ); } DBIStatementMySQL::~DBIStatementMySQL() { close(); } DBIRecordset* DBIStatementMySQL::execute( ItemArray* params ) { if( m_statement == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_STMT, __LINE__ ) ); // should we bind with the statement? -- first time around? if ( ! m_bBound ) { m_bBound = true; // Do we have some parameter to bind? if( params == 0 ) { if( mysql_stmt_param_count( m_statement ) != 0 ) { getMySql()->throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_BIND_SIZE ); } } else { if( params->length() != mysql_stmt_param_count( m_statement ) ) { getMySql()->throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_BIND_SIZE ); } // params.lengh() == 0 is possible with totally static selects, // or other statements that will be run usually just once. // Inserts or other repetitive statements will have at least 1, so // this branch won't be repeatedly checked in the fast path. m_inBind = new MyDBIInBind( m_statement ); m_inBind->bind( *params, DBITimeConverter_MYSQL_TIME_impl ); if( mysql_stmt_bind_param( m_statement, m_inBind->mybindings() ) != 0 ) { getMySql()->throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_BIND_MIX ); } } } else { if ( params != 0 && m_inBind != 0 ) { m_inBind->bind( *params, DBITimeConverter_MYSQL_TIME_impl ); } else if ( m_inBind != 0 ) { // we had parameters, but now we have not. getMySql()->throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_BIND_SIZE ); } } if( mysql_stmt_execute( m_statement ) != 0 ) { getMySql()->throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_EXEC ); } // row count? m_nLastAffected = mysql_stmt_affected_rows( m_statement ); // do we have metadata? MYSQL_RES* meta = 0; DBIHandleMySQL* mysql = static_cast(m_dbh); // We want a result recordset meta = mysql_stmt_result_metadata( m_statement ); if( meta == 0 ) { //No, we have nothing to return. return 0; } else { // ok. Do the user wanted all the result back? if( mysql->options()->m_nPrefetch < 0 ) { if( mysql_stmt_store_result( m_statement ) != 0 ) { mysql_free_result( meta ); mysql->throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_FETCH ); } } DBIRecordsetMySQL_STMT* recset = new DBIRecordsetMySQL_STMT( mysql, meta, m_pStmt ); // -- may throw try { recset->init(); } catch( ... ) { delete recset; throw; } return recset; } } void DBIStatementMySQL::reset() { if( m_statement == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_STMT, __LINE__ ) ); if( mysql_stmt_reset( m_statement ) != 0 ) { getMySql()->throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_RESET ); } } void DBIStatementMySQL::close() { if ( m_statement != 0 ) { m_statement = 0; delete m_inBind; m_inBind = 0; m_pConn->decref(); m_pStmt->decref(); } } MYSQLStmtHandle::~MYSQLStmtHandle() { mysql_stmt_close( handle() ); } /****************************************************************************** * DB Handler class *****************************************************************************/ DBIHandleMySQL::~DBIHandleMySQL() { DBIHandleMySQL::close(); } void DBIHandleMySQL::options( const String& params ) { if( m_settings.parse( params ) ) { mysql_autocommit( m_conn, m_settings.m_bAutocommit ? 1 : 0); } else { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_OPTPARAMS, __LINE__ ) .extra( params ) ); } } const DBISettingParams* DBIHandleMySQL::options() const { return &m_settings; } DBIHandleMySQL::DBIHandleMySQL() { m_conn = NULL; } DBIHandleMySQL::DBIHandleMySQL( MYSQL *conn ) { m_conn = conn; m_pConn = new MYSQLHandle( conn ); // we'll be using UTF-8 charset mysql_set_character_set( m_conn, "utf8" ); mysql_autocommit( m_conn, m_settings.m_bAutocommit ? 1 : 0 ); } DBIRecordset *DBIHandleMySQL::query( const String &sql, ItemArray* params ) { if( m_conn == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); // do we want to fetch strings? if( ! options()->m_bFetchStrings ) { // if not, try to prepare and execute. // prepare and execute -- will create a new m_statement MYSQL_STMT* stmt = my_prepare( sql, true ); // If 0, it means that mysql doesn't support prepare for this query. if ( stmt != 0 ) { MYSQL_RES* meta = 0; DBIRecordsetMySQL_STMT* recset = 0; try { MyDBIInBind bindings(stmt); m_nLastAffected = my_execute( stmt, bindings, params ); // We want a result recordset meta = mysql_stmt_result_metadata( stmt ); if( meta == 0 ) { return 0; } // ok. Do the user wanted all the result back? if( m_settings.m_nPrefetch < 0 ) { if( mysql_stmt_store_result( stmt ) != 0 ) { throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_FETCH ); } } // -- may throw recset = new DBIRecordsetMySQL_STMT( this, meta, stmt ); recset->init(); return recset; } catch( ... ) { if( meta != 0 ) mysql_free_result( meta ); if( recset ) { delete recset; } else { mysql_stmt_close( stmt ); } throw; } } } // either we WANT to fetch strings, or we're FORCED by mysql // -- which may not support prepare/execute for this query. MYSQL *conn = m_conn; int result; if( params != 0) { String temp; sqlExpand( sql, temp, *params ); AutoCString asQuery( temp ); result = mysql_real_query( conn, asQuery.c_str(), asQuery.length() ); } else { AutoCString asQuery( sql ); result = mysql_real_query( conn, asQuery.c_str(), asQuery.length() ); } if( result != 0 ) { throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_QUERY ); } MYSQL_RES* rec = options()->m_nPrefetch < 0 ? mysql_store_result( conn ) : mysql_use_result( conn ); m_nLastAffected = mysql_affected_rows( conn ); if( rec == 0 ) { return 0; } return new DBIRecordsetMySQL_RES_STR( this, rec ); } MYSQL_STMT* DBIHandleMySQL::my_prepare( const String &query, bool bCanFallback ) { if( m_conn == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); MYSQL_STMT* stmt = mysql_stmt_init( m_conn ); if( stmt == 0 ) { throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_NOMEM ); } AutoCString cquery( query ); if( mysql_stmt_prepare( stmt, cquery.c_str(), cquery.length() ) != 0 ) { int result = mysql_errno( m_conn ); if ( result == 1295 && bCanFallback ) { // unsupported as prepared query return 0; } throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_QUERY ); } // prepare the attributes as suggested by our parameters. unsigned long setting = m_settings.m_nCursorThreshold == 0 ? CURSOR_TYPE_READ_ONLY : CURSOR_TYPE_NO_CURSOR; mysql_stmt_attr_set( stmt, STMT_ATTR_CURSOR_TYPE, &setting ); if( m_settings.m_nPrefetch > 0 ) { setting = (unsigned long) m_settings.m_nPrefetch; mysql_stmt_attr_set( stmt, STMT_ATTR_PREFETCH_ROWS, &setting ); } else if ( m_settings.m_nPrefetch == -1 ) { setting = (unsigned long) 0xFFFFFFFF; mysql_stmt_attr_set( stmt, STMT_ATTR_PREFETCH_ROWS, &setting ); } return stmt; } int64 DBIHandleMySQL::my_execute( MYSQL_STMT* stmt, MyDBIInBind& bindings, ItemArray* params ) { fassert( m_conn != 0 ); int count = mysql_stmt_param_count( stmt ); if( params == 0 || params->length() == 0 ) { if( count != 0 ) throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_BIND_SIZE ); } else { // Do we have some parameter to bind? if ( params->length() != count ) { throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_BIND_SIZE ); } bindings.bind( *params, DBITimeConverter_MYSQL_TIME_impl ); if( mysql_stmt_bind_param( stmt, bindings.mybindings() ) != 0 ) { throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_BIND_MIX ); } } if( mysql_stmt_execute( stmt ) != 0 ) { throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_EXEC ); } // row count? return mysql_stmt_affected_rows( stmt ); } DBIStatement* DBIHandleMySQL::prepare( const String &query ) { MYSQL_STMT* stmt = my_prepare( query ); return new DBIStatementMySQL( this, stmt ); } int64 DBIHandleMySQL::getLastInsertedId( const String& sequenceName ) { if( m_conn == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); return mysql_insert_id( m_conn ); } void DBIHandleMySQL::begin() { if( m_conn == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); if( mysql_query( m_conn, "BEGIN" ) != 0 ) { throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_TRANSACTION ); } } void DBIHandleMySQL::commit() { if( m_conn == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); if( mysql_query( m_conn, "COMMIT" ) != 0 ) { throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_TRANSACTION ); } } void DBIHandleMySQL::rollback() { if( m_conn == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); if( mysql_query( m_conn, "ROLLBACK" ) != 0 ) { throwError( __FILE__, __LINE__, FALCON_DBI_ERROR_TRANSACTION ); } } void DBIHandleMySQL::selectLimited( const String& query, int64 nBegin, int64 nCount, String& result ) { String sBegin, sCount; if ( nBegin > 0 ) { sBegin = " OFFSET "; sBegin.N( nBegin ); } if( nCount > 0 ) { sCount.N( nCount ); } result = "SELECT " + query; if( nCount != 0 || nBegin != 0 ) { result += " LIMIT " + sCount + sBegin; } } void DBIHandleMySQL::close() { if ( m_conn != NULL ) { mysql_query( m_conn, "COMMIT" ); m_pConn->decref(); m_conn = NULL; } } void DBIHandleMySQL::throwError( const char* file, int line, int code ) { fassert( m_conn != 0 ); const char *errorMessage = mysql_error( m_conn ); if ( errorMessage != NULL ) { String description; description.N( (int64) mysql_errno( m_conn ) ).A(": "); description.A( errorMessage ); throw new DBIError( ErrorParam( code, line ) .extra(description) .module( file ) ); } else { throw new DBIError( ErrorParam( code, line ) .module( file ) ); } } /****************************************************************************** * Main service class *****************************************************************************/ void DBIServiceMySQL::init() { } DBIHandle *DBIServiceMySQL::connect( const String ¶meters ) { MYSQL *conn = mysql_init( NULL ); if ( conn == NULL ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_NOMEM, __LINE__) ); } // Parse the connection string. DBIConnParams connParams; // add MySQL specific parameters String sSocket, sFlags; const char *szSocket = 0; connParams.addParameter( "socket", sSocket, &szSocket ); connParams.addParameter( "flags", sFlags ); if( ! connParams.parse( parameters ) ) { mysql_close( conn ); throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CONNPARAMS, __LINE__) .extra( parameters ) ); } long szFlags = CLIENT_MULTI_STATEMENTS|CLIENT_MULTI_RESULTS; // TODO parse flags if ( mysql_real_connect( conn, connParams.m_szHost, connParams.m_szUser, connParams.m_szPassword, connParams.m_szDb, connParams.m_szPort == 0 ? 0 : atoi( connParams.m_szPort ), szSocket, szFlags ) == NULL ) { int en = mysql_errno( conn ) == ER_BAD_DB_ERROR ? FALCON_DBI_ERROR_DB_NOTFOUND : FALCON_DBI_ERROR_CONNECT; String errorMessage = mysql_error( conn ); errorMessage.bufferize(); mysql_close( conn ); throw new DBIError( ErrorParam( en, __LINE__).extra( errorMessage ) ); } if( connParams.m_sCreate == "always" ) { String sDrop = "drop database IF EXIST " + connParams.m_sDb ; AutoCString asQuery( sDrop ); if( mysql_real_query( conn, asQuery.c_str(), asQuery.length() ) != 0 ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CONNECT_CREATE, __LINE__ )); } String sCreate = "create database " + connParams.m_sDb ; AutoCString asQuery2( sCreate ); if( mysql_real_query( conn, asQuery2.c_str(), asQuery2.length() ) != 0 ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CONNECT_CREATE, __LINE__ )); } } else if ( connParams.m_sCreate == "cond" ) { String sCreate = "create database if not exist " + connParams.m_sDb; AutoCString asQuery2( sCreate ); if( mysql_real_query( conn, asQuery2.c_str(), asQuery2.length() ) != 0 ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CONNECT_CREATE, __LINE__ )); } } else if( connParams.m_sCreate != "" ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CONNPARAMS, __LINE__) .extra( parameters ) ); } return new DBIHandleMySQL( conn ); } CoreObject *DBIServiceMySQL::makeInstance( VMachine *vm, DBIHandle *dbh ) { Item *cl = vm->findWKI( "MySQL" ); if ( cl == 0 || ! cl->isClass() ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_INVALID_DRIVER, __LINE__ ) ); } CoreObject *obj = cl->asClass()->createInstance(); obj->setUserData( dbh ); return obj; } } /* namespace Falcon */ /* end of mysql_mod.cpp */ modules/native/dbi/mysql/mysql_mod.h000066400000000000000000000126411176363201700201060ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: mysql_mod.h * * MySQL driver main module interface * ------------------------------------------------------------------- * Author: Giancarlo Niccolai * Begin: Sun, 23 May 2010 16:58:53 +0200 * * ------------------------------------------------------------------- * (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #ifndef DBI_MYSQL_H #define DBI_MYSQL_H #include #include #include namespace Falcon { class MYSQLHandle: public DBIRefCounter { public: MYSQLHandle( MYSQL* m ): DBIRefCounter( m ) {} virtual ~MYSQLHandle() { mysql_close( handle() ); } }; class MYSQLStmtHandle: public DBIRefCounter { public: MYSQLStmtHandle( MYSQL_STMT* m ): DBIRefCounter( m ) {} virtual ~MYSQLStmtHandle(); }; class MyDBIInBind: public DBIInBind { public: MyDBIInBind( MYSQL_STMT* stmt ); virtual ~MyDBIInBind(); virtual void onFirstBinding( int size ); virtual void onItemChanged( int num ); MYSQL_BIND* mybindings() const { return m_mybind; } private: MYSQL_BIND* m_mybind; MYSQL_STMT* m_stmt; }; class MyDBIOutBind: public DBIOutBind { public: MyDBIOutBind(): bIsNull( false ), nLength( 0 ) {} ~MyDBIOutBind() {} my_bool bIsNull; unsigned long nLength; }; class DBIHandleMySQL; class DBIRecordsetMySQL: public DBIRecordset { protected: int m_row; int m_rowCount; int m_columnCount; MYSQL_RES *m_res; MYSQL_FIELD* m_fields; bool m_bCanSeek; MYSQLHandle *m_pConn; public: DBIRecordsetMySQL( DBIHandleMySQL *dbt, MYSQL_RES *res, bool bCanSeek = false ); virtual ~DBIRecordsetMySQL(); virtual int64 getRowIndex(); virtual int64 getRowCount(); virtual int getColumnCount(); virtual bool getColumnName( int nCol, String& name ); virtual void close(); }; class DBIRecordsetMySQL_STMT: public DBIRecordsetMySQL { protected: MYSQL_STMT *m_stmt; MYSQLStmtHandle *m_pStmt; // Binding data MYSQL_BIND* m_pMyBind; MyDBIOutBind* m_pOutBind; // used to keep track of blobs that must be zeroed before fetch int* m_pBlobId; int m_nBlobCount; public: DBIRecordsetMySQL_STMT( DBIHandleMySQL *dbt, MYSQL_RES *res, MYSQLStmtHandle *pStmt, bool bCanSeek = false ); DBIRecordsetMySQL_STMT( DBIHandleMySQL *dbt, MYSQL_RES *res, MYSQL_STMT *stmt, bool bCanSeek = false ); virtual ~DBIRecordsetMySQL_STMT(); void init(); virtual bool fetchRow(); virtual bool getColumnValue( int nCol, Item& value ); virtual bool discard( int64 ncount ); virtual void close(); /** This cind of recordsets can generate a next recordset. */ virtual DBIRecordset* getNext(); }; class DBIRecordsetMySQL_RES : public DBIRecordsetMySQL { protected: MYSQL_ROW m_rowData; CoreObject* makeTimestamp( const String& str ); public: DBIRecordsetMySQL_RES( DBIHandleMySQL *dbt, MYSQL_RES *res, bool bCanSeek = false ); virtual ~DBIRecordsetMySQL_RES(); virtual bool fetchRow(); virtual bool getColumnValue( int nCol, Item& value ); virtual bool discard( int64 ncount ); }; class DBIRecordsetMySQL_RES_STR: public DBIRecordsetMySQL_RES { public: DBIRecordsetMySQL_RES_STR( DBIHandleMySQL *dbt, MYSQL_RES *res, bool bCanSeek = false ); virtual ~DBIRecordsetMySQL_RES_STR(); virtual bool getColumnValue( int nCol, Item& value ); }; class DBIHandleMySQL : public DBIHandle { protected: MYSQL *m_conn; MYSQLHandle *m_pConn; DBISettingParams m_settings; MYSQL_STMT* my_prepare( const String &query, bool bCanFallback = false ); int64 my_execute( MYSQL_STMT* stmt, MyDBIInBind& bindings, ItemArray* params ); public: DBIHandleMySQL(); DBIHandleMySQL( MYSQL *conn ); virtual ~DBIHandleMySQL(); virtual void options( const String& params ); virtual const DBISettingParams* options() const; virtual void close(); virtual DBIRecordset *query( const String &sql, ItemArray* params ); virtual DBIStatement* prepare( const String &query ); virtual int64 getLastInsertedId( const String& name = "" ); virtual void begin(); virtual void commit(); virtual void rollback(); virtual void selectLimited( const String& query, int64 nBegin, int64 nCount, String& result ); MYSQLHandle *getConn() { return m_pConn; } // Throws a DBI error, using the last error code and description. void throwError( const char* file, int line, int code ); }; class DBIStatementMySQL : public DBIStatement { protected: MYSQL_STMT* m_statement; MYSQLHandle* m_pConn; MYSQLStmtHandle *m_pStmt; MyDBIInBind* m_inBind; bool m_bBound; public: DBIStatementMySQL( DBIHandleMySQL *dbh, MYSQL_STMT* stmt ); virtual ~DBIStatementMySQL(); virtual DBIRecordset* execute( ItemArray* params ); virtual void reset(); virtual void close(); DBIHandleMySQL* getMySql() const { return static_cast( m_dbh ); } MYSQL_STMT* my_statement() const { return m_statement; } }; class DBIServiceMySQL : public DBIService { public: DBIServiceMySQL() : DBIService( "DBI_mysql" ) {} virtual void init(); virtual DBIHandle *connect( const String ¶meters ); virtual CoreObject *makeInstance( VMachine *vm, DBIHandle *dbh ); }; } extern Falcon::DBIServiceMySQL theMySQLService; #endif /* DBI_MYSQL_H */ /* end of mysql_mod.h */ modules/native/dbi/mysql/version.h000066400000000000000000000011171176363201700175630ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: version.h * * MySQL module version information * ------------------------------------------------------------------- * Author: Jeremy Cowgar * Begin: Wed Jan 02 21:35:18 2008 * * ------------------------------------------------------------------- * (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #ifndef VERSION_H #define VERSION_H #define VERSION_MAJOR 0 #define VERSION_MINOR 9 #define VERSION_REVISION 8 #endif /* end of version.h */ modules/native/dbi/odbc/000077500000000000000000000000001176363201700154675ustar00rootroot00000000000000modules/native/dbi/odbc/CMakeLists.txt000066400000000000000000000051071176363201700202320ustar00rootroot00000000000000#################################################################### # The Falcon Programming language # # CMake configuration file for module ODBC #################################################################### if(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) endif(COMMAND cmake_policy) FALCON_DEFINE_MODULE( FALCON_MODULE odbc ) if(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) endif(COMMAND cmake_policy) # Find ODBC SET(ODBC_INCLUDE_SEARCH_PATH ${ODBC_INCLUDE_DIR} "$ENV{ProgramFiles}/Microsoft Visual Studio 8/VC/PlatformSDK/include" "$ENV{ProgramFiles}/Microsoft Visual Studio 9/VC/PlatformSDK/include" "$ENV{ProgramFiles}/Microsoft SDKs/Windows/v7.0A/Include" "$ENV{ProgramFiles}/Microsoft Platform SDK/Include" ) FIND_PATH(ODBC_REAL_INCLUDE_DIR odbcss.h ${ODBC_INCLUDE_SEARCH_PATH}) # Find ODBC Client Library SET(ODBC_LIB_SEARCH_PATH ${ODBC_LIB_DIR} "$ENV{ProgramFiles}/Microsoft Visual Studio 8/VC/PlatformSDK/lib" "$ENV{ProgramFiles}/Microsoft Visual Studio 9/VC/PlatformSDK/lib" "$ENV{ProgramFiles}/Microsoft SDKs/Windows/v7.0A/Lib" "$ENV{ProgramFiles}/Microsoft Platform SDK/Lib" ) FIND_LIBRARY(ODBC_LIBRARY NAMES odbc32 PATHS ${ODBC_LIB_SEARCH_PATH} ) IF (ODBC_REAL_INCLUDE_DIR AND ODBC_LIBRARY) INCLUDE_DIRECTORIES(BEFORE ${ODBC_REAL_INCLUDE_DIR}) ELSE (ODBC_REAL_INCLUDE_DIR AND ODBC_LIBRARY) IF(NOT ODBC_REAL_INCLUDE_DIR) MESSAGE(SEND_ERROR "ODBC include path was not found") MESSAGE(SEND_ERROR "Looked in: ${ODBC_INCLUDE_SEARCH_PATH}") ENDIF (NOT ODBC_REAL_INCLUDE_DIR) IF (NOT ODBC_LIBRARY) MESSAGE(SEND_ERROR "ODBC client library was not found") MESSAGE(SEND_ERROR "Looked for: ${ODBC_LIB_NAMES}") MESSAGE(SEND_ERROR "Looked in: ${ODBC_LIB_SEARCH_PATH}") ENDIF (NOT ODBC_LIBRARY) MESSAGE(FATAL_ERROR "Cannot build ODBC DBI module") ENDIF (ODBC_REAL_INCLUDE_DIR AND ODBC_LIBRARY) # we know we have found it FIND_LIBRARY(ODBCCP_LIBRARY NAMES odbccp32 PATHS ${ODBC_LIB_SEARCH_PATH} ) MESSAGE(STATUS "Found ODBC.h in ${ODBC_REAL_INCLUDE_DIR}") MESSAGE(STATUS "Found ODBC client library, ${ODBC_LIBRARY}") # Inclusion settings INCLUDE_DIRECTORIES(.) INCLUDE_DIRECTORIES(../include) # Target ADD_LIBRARY( ${FALCON_MODULE} MODULE ${dbi_common_files} odbc.cpp odbc_ext.cpp odbc_srv.cpp odbc_mod.cpp odbc_ext.h odbc_mod.h ) # Link IF(MSVC) SET_TARGET_PROPERTIES(${FALCON_MODULE} PROPERTIES LINK_FLAGS "/NODEFAULTLIB:library") ENDIF(MSVC) TARGET_LINK_LIBRARIES( ${FALCON_MODULE} falcon_engine ${ODBC_OPT_LIBS} ${ODBC_LIBRARY} ${ODBCCP_LIBRARY}) FALCON_INSTALL_MODULE2( ${FALCON_MODULE} dbi ) modules/native/dbi/odbc/odbc.cpp000066400000000000000000000034471176363201700171120ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: odbc.cpp ODBC driver for DBI - main module This is BOTH a driver for the DBI interface AND a standalone ODBC module. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue Sep 30 17:00:00 2008 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "odbc_mod.h" #include "version.h" #include "odbc_ext.h" /*# @module dbi.odbc ODBC driver module. @brief DBI extension supporting ODBC connections. Directly importable as @b dbi.odbc, it is usually loaded through the @a dbi module. */ // Instantiate the driver service Falcon::DBIServiceODBC theODBCService; // the main module FALCON_MODULE_DECL { // Module declaration Falcon::Module *self = new Falcon::Module(); self->name( "odbc" ); self->engineVersion( FALCON_VERSION_NUM ); self->version( VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION ); // first of all, we need to declare our dependency from the DBI module. self->addDepend( "dbi", "dbi", true, false ); // also, we declare a ODBC class, which derives from DBIHandler which // is in the DBI module. Falcon::Symbol *dbh_class = self->addExternalRef( "dbi.%Handle" ); // it's external dbh_class->imported( true ); Falcon::Symbol *odbc_class = self->addClass( "ODBC", Falcon::Ext::ODBC_init ) ->addParam("connect")->addParam("options"); odbc_class->getClassDef()->addInheritance( new Falcon::InheritDef( dbh_class ) ); odbc_class->setWKS( true ); // service publication self->publishService( &theODBCService ); // we're done return self; } /* end of odbc.cpp */ modules/native/dbi/odbc/odbc_ext.cpp000066400000000000000000000047011176363201700177640ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: odbc_ext.cpp * * ODBC Falcon extension interface * ------------------------------------------------------------------- * Author: Giancarlo Niccolai * Begin: * * ------------------------------------------------------------------- * (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include #include "odbc_mod.h" #include "odbc_ext.h" /*# @beginmodule dbi.odbc */ namespace Falcon { namespace Ext { /*# @class ODBC @brief Interface to ODBC connections. @param connect String containing connection parameters. @optparam options Connection and query default options. The ODBC drivers have a limited ability to determine the underlying database types; for this reason, it's advisable to limit the usage of prepared statements, and rely on @b query, which performs safer verbatim parameter expansion. The @b connect string is directly passed to the ODBC driver for connection, so it must respect ODBC standards and specific extensions of the target database. Other than the base DBI class options, this class supports the following options: - bigint (on/off): By default, the ODBC drivers can't deal with int64 (64 bit integers) data. Setting this on, it is possible to send int64 data through prepared statements. */ FALCON_FUNC ODBC_init( VMachine *vm ) { Item *paramsI = vm->param(0); Item *i_connParams = vm->param(1); if ( paramsI == 0 || ! paramsI->isString() || ( i_connParams != 0 && ! i_connParams->isString() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S,[S]" ) ); } CoreObject *self = vm->self().asObject(); String connectErrorMessage; const String& params = i_connParams == 0 ? String("") : *i_connParams->asString(); DBIHandle *hand = 0; try { hand = theODBCService.connect( params ); if( i_connParams != 0 ) { hand->options( *i_connParams->asString() ); } // great, we have the database handler open. Now we must create a falcon object to store it. CoreObject *instance = theODBCService.makeInstance( vm, hand ); vm->retval( instance ); } catch( DBIError* error ) { delete hand; throw error; } } } /* namespace Ext */ } /* namespace Falcon */ /* end of odbc_ext.cpp */ modules/native/dbi/odbc/odbc_ext.h000066400000000000000000000012611176363201700174270ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: odbc_ext.h * * ODBC Falcon extension interface * ------------------------------------------------------------------- * Author: Giancarlo Niccolai * Begin: Tue Sep 30 17:00:00 2008 * * ------------------------------------------------------------------- * (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ /* #include #include */ #include #ifndef ODBC_EXT_H #define ODBC_EXT_H namespace Falcon { class VMachine; namespace Ext { FALCON_FUNC ODBC_init( VMachine *vm ); } } #endif /* ODBC_EXT_H */ modules/native/dbi/odbc/odbc_mod.cpp000066400000000000000000000671501176363201700177520ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: odbc_mod.cpp ODBC driver main module interface ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 23 May 2010 18:23:20 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "odbc_mod.h" #include #include #include #include #define ODBC_COLNAME_SIZE 512 namespace Falcon { /****************************************************************************** * Private class used to convert timestamp to MySQL format. *****************************************************************************/ class DBITimeConverter_ODBC_TIME: public DBITimeConverter { public: virtual void convertTime( TimeStamp* ts, void* buffer, int& bufsize ) const; } DBITimeConverter_ODBC_TIME_impl; void DBITimeConverter_ODBC_TIME::convertTime( TimeStamp* ts, void* buffer, int& bufsize ) const { fassert( ((unsigned)bufsize) >= sizeof( TIMESTAMP_STRUCT ) ); TIMESTAMP_STRUCT* mtime = (TIMESTAMP_STRUCT*) buffer; mtime->year = (unsigned) ts->m_year; mtime->month = (unsigned) ts->m_month; mtime->day = (unsigned) ts->m_day; mtime->hour = (unsigned) ts->m_hour; mtime->minute = (unsigned) ts->m_minute; mtime->second = (unsigned) ts->m_second; mtime->fraction = (unsigned) ts->m_msec*100000; bufsize = sizeof( TIMESTAMP_STRUCT ); } /****************************************************************************** * (Input) bindings class *****************************************************************************/ ODBCInBind::ODBCInBind( SQLHSTMT stmt, bool bigInt ): m_hStmt( stmt ), m_pLenInd( 0 ), m_pColInfo( 0 ), m_bUseBigInteger( bigInt ) {} ODBCInBind::~ODBCInBind() { // we don't own the handlers if ( m_pLenInd != 0 ) { memFree( m_pLenInd ); } /*if( m_pColInfo != 0 ) { delete[] m_pColInfo; } */ } void ODBCInBind::onFirstBinding( int size ) { m_pLenInd = (SQLLEN *) memAlloc( sizeof(SQLINTEGER) * size ); memset( m_pLenInd, 0, sizeof(SQLLEN ) * size ); //m_pColInfo = new ODBCColInfo[ size ]; } void ODBCInBind::onItemChanged( int num ) { DBIBindItem& item = m_ibind[num]; SQLLEN& pLenInd = m_pLenInd[num]; // fill the call variables with consistent defaults SQLSMALLINT InputOutputType = SQL_PARAM_INPUT; SQLSMALLINT ValueType = SQL_C_CHAR; SQLSMALLINT ParameterType = SQL_CHAR; SQLSMALLINT ColSize = 0; SQLLEN BufferLength = 0; SQLPOINTER ParameterValuePtr; switch( item.type() ) { // set to null case DBIBindItem::t_nil: pLenInd = SQL_NULL_DATA; ParameterValuePtr = 0; ColSize = 1; break; case DBIBindItem::t_bool: ValueType = SQL_C_BIT; ParameterType = SQL_BIT; ParameterValuePtr = (SQLPOINTER) item.asBoolPtr(); ColSize = 1; break; case DBIBindItem::t_int: if( m_bUseBigInteger ) { ValueType = SQL_C_SBIGINT; ParameterType = SQL_BIGINT; ParameterValuePtr = (SQLPOINTER) item.asIntegerPtr(); ColSize = 19; pLenInd = sizeof(int64); } else { ValueType = SQL_C_LONG; ParameterType = SQL_INTEGER; ParameterValuePtr = (SQLPOINTER) item.asIntegerPtr(); ColSize = 10; pLenInd = sizeof(long); } break; case DBIBindItem::t_double: ValueType = SQL_C_DOUBLE; ParameterType = SQL_DOUBLE; ParameterValuePtr = (SQLPOINTER) item.asDoublePtr(); ColSize = 15; pLenInd = sizeof(double); break; case DBIBindItem::t_string: ValueType = SQL_C_WCHAR; ParameterType = SQL_WCHAR; // String::toWideString is granted to ensure space and put extra '\0' at the end. // Use the extra incoming '\0' pLenInd = BufferLength = item.asStringLen()*sizeof(wchar_t); ParameterValuePtr = (SQLPOINTER) item.asString(); ColSize = (SQLSMALLINT) BufferLength; break; case DBIBindItem::t_buffer: ValueType = SQL_C_BINARY; ParameterType = SQL_BINARY; pLenInd = BufferLength = item.asStringLen(); ParameterValuePtr = (SQLPOINTER) item.asBuffer(); break; case DBIBindItem::t_time: ValueType = SQL_C_TIMESTAMP; ParameterType = SQL_TIMESTAMP; pLenInd = BufferLength = item.asStringLen(); ParameterValuePtr = (SQLPOINTER) item.asBuffer(); ColSize = (SQLSMALLINT) pLenInd; break; } SQLRETURN ret = SQLBindParameter( m_hStmt, num+1, InputOutputType, ValueType, ParameterType, ColSize, 0, ParameterValuePtr, BufferLength, &pLenInd); if( ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO ) { DBIHandleODBC::throwError( FALCON_DBI_ERROR_BIND_INTERNAL, SQL_HANDLE_STMT, m_hStmt, TRUE, false ); } } /****************************************************************************** * Recordset class *****************************************************************************/ DBIRecordsetODBC::DBIRecordsetODBC( DBIHandleODBC *dbh, int64 nRowCount, int32 nColCount, ODBCStatementHandler* h ) : DBIRecordset( dbh ), m_pStmt( h ), m_nRow( 0 ), m_nRowCount( nRowCount ), m_nColumnCount( nColCount ), m_pColInfo(0) { dbh->incConnRef(); m_conn = dbh->getConn(); h->incref(); m_bAsString = dbh->options()->m_bFetchStrings; } DBIRecordsetODBC::DBIRecordsetODBC( DBIHandleODBC *dbh, int64 nRowCount, int32 nColCount, SQLHSTMT hStmt ) : DBIRecordset( dbh ), m_pStmt( new ODBCStatementHandler( hStmt) ), m_nRow( 0 ), m_nRowCount( nRowCount ), m_nColumnCount( nColCount ), m_pColInfo(0) { dbh->incConnRef(); m_conn = dbh->getConn(); m_bAsString = dbh->options()->m_bFetchStrings; } DBIRecordsetODBC::~DBIRecordsetODBC() { close(); delete[] m_pColInfo; } int DBIRecordsetODBC::getColumnCount() { return m_nColumnCount; } int64 DBIRecordsetODBC::getRowIndex() { return m_nRow; } int64 DBIRecordsetODBC::getRowCount() { return m_nRowCount; } bool DBIRecordsetODBC::getColumnName( int nCol, String& name ) { if( m_pStmt == 0 ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_RSET, __LINE__ ) ); } // a good moment to fetch the column data if ( m_pColInfo == 0 ) { GetColumnInfo(); } if ( nCol < 0 || nCol >= m_nColumnCount ) { return false; } name = m_pColInfo[nCol].sName; return true; } void DBIRecordsetODBC::GetColumnInfo() { m_pColInfo = new ODBCColInfo[ m_nColumnCount ]; wchar_t ColumnName[ODBC_COLNAME_SIZE]; SQLSMALLINT NameLength; SQLHSTMT hStmt = m_pStmt->handle(); for ( SQLSMALLINT nCol = 0; nCol < m_nColumnCount; ++nCol ) { ODBCColInfo& current = m_pColInfo[nCol]; SQLRETURN ret = SQLDescribeColW( hStmt, nCol+1, ColumnName, ODBC_COLNAME_SIZE, &NameLength, ¤t.DataType, ¤t.ColumnSize, ¤t.DecimalDigits, ¤t.Nullable ); if ( NameLength+1 > ODBC_COLNAME_SIZE ) { NameLength = ODBC_COLNAME_SIZE - 1; } if( ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO ) { // Vital informations are not available; better bail out and close DBIError* dbie = new DBIError( ErrorParam( FALCON_DBI_ERROR_FETCH, __LINE__ ). extra( DBIHandleODBC::GetErrorMessage( SQL_HANDLE_STMT, hStmt, TRUE ) ) ); close(); throw dbie; // return } ColumnName[NameLength] = 0; current.sName = ColumnName; current.sName.bufferize(); } } bool DBIRecordsetODBC::fetchRow() { if( m_pStmt == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_RSET, __LINE__ ) ); SQLRETURN ret = SQLFetch( m_pStmt->handle() ); if ( ret == SQL_NO_DATA ) { // we're done return false; } // error? if( ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO ) { // throw but don't close DBIHandleODBC::throwError( FALCON_DBI_ERROR_FETCH, SQL_HANDLE_STMT, m_pStmt->handle(), TRUE, false ); // return } // more data incoming m_nRow++; return true; } bool DBIRecordsetODBC::discard( int64 ncount ) { while ( ncount > 0 ) { if( ! fetchRow() ) { return false; } --ncount; } return true; } void DBIRecordsetODBC::close() { if( m_pStmt != 0 ) { m_pStmt->decref(); m_conn->decref(); m_pStmt = 0; } } bool DBIRecordsetODBC::getColumnValue( int nCol, Item& value ) { if( m_pStmt == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_RSET, __LINE__ ) ); if ( nCol < 0 || nCol >= m_nColumnCount ) { return false; } // a good moment to fetch the column data if ( m_pColInfo == 0 ) { GetColumnInfo(); } SQLHSTMT hStmt = m_pStmt->handle(); ODBCColInfo& column = m_pColInfo[ nCol ]; SQLRETURN ret; int64 integer; double real; unsigned char uchar; SQLLEN ExpSize = 0, nSize = 0; // do a first call to determine null or memory requirements switch ( column.DataType ) { case SQL_CHAR: case SQL_VARCHAR: case SQL_LONGVARCHAR: case SQL_WCHAR: case SQL_WVARCHAR: case SQL_WLONGVARCHAR: ret = SQLGetData( hStmt, nCol+1, SQL_C_WCHAR, &uchar, 0, &ExpSize); if( ret != SQL_ERROR ) { if( ExpSize == SQL_NULL_DATA ) { value.setNil(); } else if( ExpSize == 0 ) { value = new CoreString(""); } else { // we must account for an extra '0' put in by ODBC uint32 alloc = ExpSize+sizeof(wchar_t); wchar_t *cStr = (wchar_t*) memAlloc( alloc ); ret = SQLGetData( hStmt, nCol+1, SQL_C_WCHAR, cStr, alloc, &nSize); // save the data even in case we had an error, or we'll leak CoreString* cs = new CoreString; uint32 size = ExpSize/sizeof(wchar_t); cs->adopt( cStr, size, alloc ); value = cs; } } break; case SQL_DECIMAL: case SQL_NUMERIC: case SQL_REAL: case SQL_FLOAT: case SQL_DOUBLE: if( m_bAsString ) { char buffer[32]; ret = SQLGetData( hStmt, nCol+1, SQL_C_CHAR, &buffer, sizeof(buffer), &ExpSize); if( ExpSize == SQL_NULL_DATA ) { value.setNil(); } else { value.setString( &(new CoreString)->bufferize( buffer ) ); } } else { ret = SQLGetData( hStmt, nCol+1, SQL_C_DOUBLE, &real, sizeof(real), &ExpSize); if( ExpSize == SQL_NULL_DATA ) { value.setNil(); } else { value.setNumeric( real ); } } break; case SQL_INTERVAL_MONTH: case SQL_INTERVAL_YEAR: case SQL_INTERVAL_DAY: case SQL_INTERVAL_YEAR_TO_MONTH: case SQL_INTERVAL_HOUR: case SQL_INTERVAL_MINUTE: case SQL_INTERVAL_SECOND: case SQL_INTERVAL_DAY_TO_HOUR: case SQL_INTERVAL_DAY_TO_MINUTE: case SQL_INTERVAL_DAY_TO_SECOND: case SQL_INTERVAL_HOUR_TO_MINUTE: case SQL_INTERVAL_HOUR_TO_SECOND: case SQL_INTERVAL_MINUTE_TO_SECOND: case SQL_GUID: case SQL_TINYINT: case SQL_SMALLINT: case SQL_INTEGER: case SQL_BIGINT: if( m_bAsString ) { char buffer[32]; ret = SQLGetData( hStmt, nCol+1, SQL_C_CHAR, &buffer, sizeof(buffer), &ExpSize); if( ExpSize == SQL_NULL_DATA ) { value.setNil(); } else { value.setString( &(new CoreString)->bufferize( buffer ) ); } } else { ret = SQLGetData( hStmt, nCol+1, SQL_C_SBIGINT, &integer, sizeof(integer), &ExpSize); if( ExpSize == SQL_NULL_DATA ) { value.setNil(); } else { value.setInteger( integer ); } } break; case SQL_BIT: ret = SQLGetData( hStmt, nCol+1, SQL_C_BIT, &uchar, sizeof(uchar), &ExpSize); if( ExpSize == SQL_NULL_DATA ) { value.setNil(); } else { if( m_bAsString ) { value.setString( new CoreString( uchar ? "true" : "false" ) ); } else { value.setBoolean( uchar ? true : false ); } } return true; case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: ret = SQLGetData( hStmt, nCol+1, SQL_C_BINARY, &uchar, 0, &ExpSize); if( ret != SQL_ERROR ) { if( ExpSize == SQL_NULL_DATA ) { value.setNil(); return true; } MemBuf* mb = new MemBuf_1( ExpSize ); ret = SQLGetData( hStmt, nCol+1, SQL_C_BINARY, mb->data(), ExpSize , &nSize); // save the data nevertheless value = mb; } break; case SQL_TYPE_DATE: case SQL_TYPE_TIME: case SQL_TYPE_TIMESTAMP: if( m_bAsString ) { char buffer[32]; ret = SQLGetData( hStmt, nCol+1, SQL_C_CHAR, &buffer, sizeof(buffer), &ExpSize); if( ExpSize == SQL_NULL_DATA ) { value.setNil(); } else { value.setString( &(new CoreString)->bufferize( buffer ) ); } } else { TIMESTAMP_STRUCT tstamp; ret = SQLGetData( hStmt, nCol+1, SQL_C_TYPE_TIMESTAMP, &tstamp, sizeof(tstamp) , &ExpSize); if( ExpSize == SQL_NULL_DATA ) { value.setNil(); } else if ( ret == SQL_SUCCESS || ret == SQL_SUCCESS_WITH_INFO ) { TimeStamp* ts = new TimeStamp; ts->m_year = tstamp.year; ts->m_month = tstamp.month; ts->m_day = tstamp.day; ts->m_hour = tstamp.hour; ts->m_minute = tstamp.minute; ts->m_second = tstamp.second; ts->m_msec = (int16) tstamp.fraction/100000; CoreObject *ots = VMachine::getCurrent()->findWKI("TimeStamp")->asClass()->createInstance(); ots->setUserData( ts ); value = ots; } } break; default: throw new DBIError( ErrorParam( FALCON_DBI_ERROR_UNHANDLED_TYPE, __LINE__ ) ); } if ( ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO ) { DBIHandleODBC::throwError( FALCON_DBI_ERROR_FETCH, SQL_HANDLE_STMT, hStmt, TRUE, false ); } return true; } /****************************************************************************** * DB Statement class *****************************************************************************/ DBIStatementODBC::DBIStatementODBC( DBIHandleODBC *dbh, SQLHSTMT hStmt ): DBIStatement( dbh ), m_inBind( hStmt, dbh->options()->m_bUseBigInt ), m_pStmt( new ODBCStatementHandler(hStmt) ) { dbh->incConnRef(); m_conn = dbh->getConn(); } DBIStatementODBC::~DBIStatementODBC() { close(); } DBIRecordset* DBIStatementODBC::execute( ItemArray* params ) { if( m_pStmt == 0 ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_STMT, __LINE__ ) ); } if( params != 0 ) { m_inBind.bind(*params, DBITimeConverter_ODBC_TIME_impl, DBIStringConverter_WCHAR_impl ); } SQLHSTMT hStmt = m_pStmt->handle(); SQLRETURN ret = SQLExecute( hStmt ); if ( ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO ) { DBIHandleODBC::throwError( FALCON_DBI_ERROR_FETCH, SQL_HANDLE_STMT, hStmt, TRUE, false ); } // Cont the rows SQLLEN nRowCount; RETCODE retcode = SQLRowCount( hStmt, &nRowCount ); if( retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO ) { DBIHandleODBC::throwError( FALCON_DBI_ERROR_QUERY, SQL_HANDLE_STMT, hStmt, TRUE, false ); // return } m_nLastAffected = (int64) nRowCount; // create the recordset SQLSMALLINT ColCount; retcode = SQLNumResultCols( hStmt, &ColCount); if( retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO ) { DBIHandleODBC::throwError( FALCON_DBI_ERROR_QUERY, SQL_HANDLE_STMT, hStmt, TRUE, false ); // return } // Do we have a recordset? if ( ColCount > 0 ) { // this may throw -- and in that case will close hStmt return new DBIRecordsetODBC( static_cast(m_dbh), nRowCount, ColCount, m_pStmt ); } else { return 0; } } void DBIStatementODBC::reset() { if( m_pStmt == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_STMT, __LINE__ ) ); } void DBIStatementODBC::close() { if( m_pStmt != 0 ) { m_pStmt->decref(); m_conn->decref(); m_pStmt = 0; } } /****************************************************************************** * DB Handler class *****************************************************************************/ DBIHandleODBC::DBIHandleODBC() { m_conn = NULL; m_bInTrans = false; } DBIHandleODBC::DBIHandleODBC( ODBCConn *conn ) { m_conn = conn; m_bInTrans = false; } DBIHandleODBC::~DBIHandleODBC( ) { close( ); } ODBCConn *DBIHandleODBC::getConnData() { if( m_conn == 0 ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); } return m_conn; } int64 DBIHandleODBC::getLastInsertedId( const String& sequenceName ) { ODBCConn *conn = getConnData(); // It's a trick, but it should work SQLHSTMT hStmt = openStatement( conn->m_hHdbc ); SQLRETURN ret = SQLExecDirectA( hStmt, (SQLCHAR*) "SELECT @@IDENTITY", SQL_NTS ); if( ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO ) { throwError( FALCON_DBI_ERROR_QUERY, SQL_HANDLE_STMT, hStmt, TRUE ); } // Cont the rows ret = SQLFetch( hStmt ); if( ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO ) { throwError( FALCON_DBI_ERROR_QUERY, SQL_HANDLE_STMT, hStmt, TRUE ); } int64 value; SQLLEN ind; ret = SQLGetData( hStmt, 1, SQL_C_SBIGINT, &value, sizeof(value), &ind ); if( ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO ) { throwError( FALCON_DBI_ERROR_QUERY, SQL_HANDLE_STMT, hStmt, TRUE ); } SQLFreeStmt( hStmt, SQL_CLOSE ); return value; } void DBIHandleODBC::close() { if( m_conn ) { m_conn->decref(); m_conn = 0; } } void DBIHandleODBC::options( const String& params ) { ODBCConn* conn = getConnData(); if( m_settings.parse( params ) ) { // To turn off the autocommit. SQLINTEGER commitValue = m_settings.m_bAutocommit ? SQL_AUTOCOMMIT_ON: SQL_AUTOCOMMIT_OFF; SQLSetConnectAttr( conn->m_hHdbc, SQL_AUTOCOMMIT, &commitValue, SQL_IS_INTEGER ); } else { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_OPTPARAMS, __LINE__ ) .extra( params ) ); } } const DBISettingParamsODBC* DBIHandleODBC::options() const { return &m_settings; } SQLHSTMT DBIHandleODBC::openStatement(SQLHDBC hHdbc) { SQLHSTMT hHstmt; SQLRETURN retcode = SQLAllocHandle( SQL_HANDLE_STMT, hHdbc, &hHstmt ); if( ( retcode != SQL_SUCCESS ) && ( retcode != SQL_SUCCESS_WITH_INFO ) ) { // don't close the db for this throwError( FALCON_DBI_ERROR_QUERY, SQL_HANDLE_DBC, hHdbc, TRUE, false ); } return hHstmt; } /* SQLHDESC DBIHandleODBC::getStatementDesc( SQLHSTMT hHstmt ) { ODBCConn *conn = ((DBIHandleODBC *) m_dbh)->getConn(); SQLHDESC hIpd; retcode = SQLGetStmtAttr( hHstmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0 ); if( ( retcode != SQL_SUCCESS ) && ( retcode != SQL_SUCCESS_WITH_INFO ) ) { // will close hHstmt throwError( FALCON_DBI_ERROR_QUERY, SQL_HANDLE_DBC, hHstmt, TRUE, true ); } return hIpd; } */ DBIRecordset *DBIHandleODBC::query( const String &sql, ItemArray* params ) { ODBCConn *conn = getConnData(); SQLHSTMT hStmt = openStatement( conn->m_hHdbc ); SQLRETURN ret; AutoWString asQuery( sql ); // call the query if( params == 0 ) { // -- no params -- easier. ret = SQLExecDirectW( hStmt, ( SQLWCHAR* )asQuery.w_str(), asQuery.length() ); } else { ret = SQLPrepareW( hStmt, (SQLWCHAR*) asQuery.w_str(), asQuery.length() ); if ( ret != SQL_ERROR ) { ODBCInBind inBind( hStmt, options()->m_bUseBigInt ); inBind.bind(*params, DBITimeConverter_ODBC_TIME_impl, DBIStringConverter_WCHAR_impl ); ret = SQLExecute( hStmt ); } } if( ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO ) { throwError( FALCON_DBI_ERROR_QUERY, SQL_HANDLE_STMT, hStmt, TRUE ); // return } // Cont the rows SQLLEN nRowCount; RETCODE retcode = SQLRowCount( hStmt, &nRowCount ); if( retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO ) { throwError( FALCON_DBI_ERROR_QUERY, SQL_HANDLE_STMT, hStmt, TRUE ); // return } m_nLastAffected = (int64) nRowCount; // create the recordset SQLSMALLINT ColCount; retcode = SQLNumResultCols( hStmt, &ColCount); if( retcode != SQL_SUCCESS && retcode != SQL_SUCCESS_WITH_INFO ) { throwError( FALCON_DBI_ERROR_QUERY, SQL_HANDLE_STMT, hStmt, TRUE ); // return } // Do we have a recordset? if ( ColCount > 0 ) { // this may throw -- and in that case will close hStmt return new DBIRecordsetODBC( this, nRowCount, ColCount, hStmt ); } else { return 0; } } DBIStatement* DBIHandleODBC::prepare( const String &query ) { ODBCConn *conn = getConnData(); SQLHSTMT hStmt = openStatement( conn->m_hHdbc ); AutoWString wQuery( query ); SQLRETURN ret = SQLPrepareW( hStmt, (SQLWCHAR*) wQuery.w_str(), wQuery.length() ); if ( ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO ) { throwError( FALCON_DBI_ERROR_QUERY, SQL_HANDLE_STMT, hStmt, TRUE ); } return new DBIStatementODBC( this, hStmt ); } void DBIHandleODBC::begin() { ODBCConn *conn = getConnData(); if( m_bInTrans ) { SQLRETURN srRet = SQLEndTran( SQL_HANDLE_DBC, conn->m_hHdbc, SQL_COMMIT ); if ( srRet != SQL_SUCCESS && srRet != SQL_SUCCESS_WITH_INFO ) { m_bInTrans = false; throwError( FALCON_DBI_ERROR_TRANSACTION, SQL_HANDLE_DBC, conn->m_hHdbc, TRUE, false ); } } m_bInTrans = true; } void DBIHandleODBC::commit() { ODBCConn *conn = getConnData(); if( m_bInTrans ) { SQLRETURN srRet = SQLEndTran( SQL_HANDLE_DBC, conn->m_hHdbc, SQL_COMMIT ); if ( srRet != SQL_SUCCESS && srRet != SQL_SUCCESS_WITH_INFO ) { m_bInTrans = false; throwError( FALCON_DBI_ERROR_TRANSACTION, SQL_HANDLE_DBC, conn->m_hHdbc, TRUE, false ); } } m_bInTrans = false; } void DBIHandleODBC::rollback() { ODBCConn *conn = getConnData(); if( m_bInTrans ) { SQLRETURN srRet = SQLEndTran( SQL_HANDLE_DBC, conn->m_hHdbc, SQL_ROLLBACK ); m_bInTrans = false; if ( srRet != SQL_SUCCESS && srRet != SQL_SUCCESS_WITH_INFO ) { throwError( FALCON_DBI_ERROR_TRANSACTION, SQL_HANDLE_DBC, conn->m_hHdbc, TRUE, false ); } } } void DBIHandleODBC::selectLimited( const String& query, int64 nBegin, int64 nCount, String& result ) { String sBegin, sCount; if ( nBegin > 0 ) { sBegin = " OFFSET "; sBegin.N( nBegin ); } if( nCount > 0 ) { sCount.N( nCount ); } result = "SELECT " + query; if( nCount != 0 || nBegin != 0 ) { result += " LIMIT " + sCount + sBegin; } } //============================================================ // Settings parameter parser //============================================================ DBISettingParamsODBC::DBISettingParamsODBC(): m_bUseBigInt( false ) { addParameter( "bigint", m_sUseBigint ); } DBISettingParamsODBC::DBISettingParamsODBC( const DBISettingParamsODBC & other): DBISettingParams(other), m_bUseBigInt( other.m_bUseBigInt ) { // we don't care about the parameter parsing during the copy. } DBISettingParamsODBC::~DBISettingParamsODBC() { } bool DBISettingParamsODBC::parse( const String& connStr ) { if( ! DBISettingParams::parse(connStr) ) { return false; } return checkBoolean( m_sUseBigint, m_bUseBigInt ); } //===================================================================== // Utilities //===================================================================== void DBIHandleODBC::throwError( int falconError, SQLSMALLINT plm_handle_type, SQLHANDLE plm_handle, int ConnInd, bool free ) { String err = GetErrorMessage( plm_handle_type, plm_handle, ConnInd ); if (free) { SQLFreeHandle( plm_handle_type, plm_handle ); } throw new DBIError( ErrorParam(falconError, __LINE__ ).extra(err) ); } String DBIHandleODBC::GetErrorMessage(SQLSMALLINT plm_handle_type, SQLHANDLE plm_handle, int ConnInd) { RETCODE plm_retcode = SQL_SUCCESS; UCHAR plm_szSqlState[MAXBUFLEN] = ""; UCHAR plm_szErrorMsg[MAXBUFLEN] = ""; SDWORD plm_pfNativeError = 0L; SWORD plm_pcbErrorMsg = 0; SQLSMALLINT plm_cRecNmbr = 1; SDWORD plm_SS_MsgState = 0, plm_SS_Severity = 0; SQLINTEGER plm_Rownumber = 0; USHORT plm_SS_Line; SQLSMALLINT plm_cbSS_Procname, plm_cbSS_Srvname; SQLCHAR plm_SS_Procname[ODBC_COLNAME_SIZE] ="", plm_SS_Srvname[ODBC_COLNAME_SIZE] = ""; String sRet = ""; char Convert[MAXBUFLEN]; while (plm_retcode != SQL_NO_DATA_FOUND) { plm_retcode = SQLGetDiagRec(plm_handle_type, plm_handle, plm_cRecNmbr, plm_szSqlState, &plm_pfNativeError, plm_szErrorMsg, MAXBUFLEN - 1, &plm_pcbErrorMsg); // Note that if the application has not yet made a // successful connection, the SQLGetDiagField // information has not yet been cached by ODBC // Driver Manager and these calls to SQLGetDiagField // will fail. if (plm_retcode != SQL_NO_DATA_FOUND) { if (ConnInd) { plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr, SQL_DIAG_ROW_NUMBER, &plm_Rownumber, SQL_IS_INTEGER, NULL); plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr, SQL_DIAG_SS_LINE, &plm_SS_Line, SQL_IS_INTEGER, NULL); plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr, SQL_DIAG_SS_MSGSTATE, &plm_SS_MsgState, SQL_IS_INTEGER, NULL); plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr, SQL_DIAG_SS_SEVERITY, &plm_SS_Severity, SQL_IS_INTEGER, NULL); plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr, SQL_DIAG_SS_PROCNAME, &plm_SS_Procname, sizeof(plm_SS_Procname), &plm_cbSS_Procname); plm_retcode = SQLGetDiagField( plm_handle_type, plm_handle, plm_cRecNmbr, SQL_DIAG_SS_SRVNAME, &plm_SS_Srvname, sizeof(plm_SS_Srvname), &plm_cbSS_Srvname); } sRet += "SqlState = " + String( ( char* )plm_szSqlState ) + ";"; sRet += "NativeError = " + String( _itoa( plm_pfNativeError, Convert, 10 ) ) + ";"; sRet += "ErrorMsg = " + String( ( char* )plm_szErrorMsg ) + ";"; sRet += "pcbErrorMsg = " + String( _itoa( plm_pcbErrorMsg, Convert, 10 ) ) + ";"; if (ConnInd) { sRet += "ODBCRowNumber = " + String( _itoa( plm_Rownumber, Convert, 10 ) ) + ";"; sRet += "SSrvrLine = " + String( _itoa( plm_Rownumber, Convert, 10 ) ) + ";"; sRet += "SSrvrMsgState = " + String( _itoa( plm_SS_MsgState, Convert, 10 ) ) + ";"; sRet += "SSrvrSeverity = " + String( _itoa( plm_SS_Severity, Convert, 10 ) ) + ";"; sRet += "SSrvrProcname = " + String( ( char* )plm_SS_Procname ) + ";"; sRet += "SSrvrSrvname = " + String( ( char* )plm_SS_Srvname ) + ";"; } } plm_cRecNmbr++; //Increment to next diagnostic record. } return sRet; } } /* end of odbc_mod.cpp */ modules/native/dbi/odbc/odbc_mod.h000066400000000000000000000127561176363201700174210ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: odbc_mod.h * * ODBC driver main module interface * ------------------------------------------------------------------- * Author: Giancarlo Niccolai * Begin: Tue Sep 30 17:00:00 2008 * * ------------------------------------------------------------------- * (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #ifndef FALCON_DBI_ODBC_H #define FALCON_DBI_ODBC_H #include #include #include namespace Falcon { const unsigned long MAXBUFLEN = 256; class DBIHandleODBC; class ODBCConn { public: SQLHENV m_hEnv; SQLHDBC m_hHdbc; ODBCConn() { m_hEnv = SQL_NULL_HENV; m_hHdbc = SQL_NULL_HDBC; m_refCount = 1; } ODBCConn( SQLHENV hEnv, SQLHDBC hHdbc ) { m_hEnv = hEnv; m_hHdbc = hHdbc; m_refCount = 1; } void incref() { m_refCount++; } void decref() { if ( --m_refCount == 0 ) delete this; } private: ~ODBCConn() { if( m_hHdbc != SQL_NULL_HDBC ) { SQLDisconnect( m_hHdbc ); SQLFreeHandle( SQL_HANDLE_DBC, m_hHdbc ); } if( m_hEnv != SQL_NULL_HENV) SQLFreeHandle(SQL_HANDLE_ENV, m_hEnv ); } int m_refCount; }; class ODBCInBind: public DBIInBind { public: ODBCInBind(SQLHSTMT hStmt, bool bBigInt ); virtual ~ODBCInBind(); virtual void onFirstBinding( int size ); virtual void onItemChanged( int num ); protected: class ODBCColInfo { public: SQLSMALLINT DataType; SQLULEN ColumnSize; SQLSMALLINT DecimalDigits; SQLSMALLINT Nullable; }; SQLHSTMT m_hStmt; SQLLEN* m_pLenInd; ODBCColInfo *m_pColInfo; bool m_bUseBigInteger; }; class ODBCStatementHandler: public DBIRefCounter { public: ODBCStatementHandler( SQLHSTMT hStmt ): DBIRefCounter( hStmt ) {} ~ODBCStatementHandler() { SQLFreeStmt( handle(), SQL_CLOSE ); } }; class DBIRecordsetODBC : public DBIRecordset { public: DBIRecordsetODBC( DBIHandleODBC *dbt, int64 nRowCount, int32 nColCount, ODBCStatementHandler* h ); DBIRecordsetODBC( DBIHandleODBC *dbt, int64 nRowCount, int32 nColCount, SQLHSTMT h ); virtual ~DBIRecordsetODBC(); virtual int64 getRowIndex(); virtual int64 getRowCount(); virtual int getColumnCount(); virtual bool getColumnName( int nCol, String& name ); virtual bool fetchRow(); virtual bool getColumnValue( int nCol, Item& value ); virtual bool discard( int64 ncount ); virtual void close(); SQLHSTMT handle() const { return m_pStmt->handle(); } protected: class ODBCColInfo { public: String sName; SQLSMALLINT DataType; SQLULEN ColumnSize; SQLSMALLINT DecimalDigits; SQLSMALLINT Nullable; }; ODBCStatementHandler* m_pStmt; int64 m_nRow; int64 m_nRowCount; int32 m_nColumnCount; ODBCColInfo* m_pColInfo; bool m_bAsString; ODBCConn *m_conn; void GetColumnInfo(); }; class DBIStatementODBC: public DBIStatement { public: DBIStatementODBC( DBIHandleODBC *dbh, SQLHSTMT h ); virtual ~DBIStatementODBC(); virtual DBIRecordset* execute( ItemArray* params = 0 ); virtual void reset(); virtual void close(); SQLHSTMT handle() const { return m_pStmt->handle(); } protected: ODBCInBind m_inBind; ODBCStatementHandler* m_pStmt; ODBCConn *m_conn; }; class DBISettingParamsODBC: public DBISettingParams { private: String m_sUseBigint; public: DBISettingParamsODBC(); DBISettingParamsODBC( const DBISettingParamsODBC & other ); virtual ~DBISettingParamsODBC(); /** Specific parse analizying the options */ virtual bool parse( const String& connStr ); /** True if we can use int64 on the underlying driver. */ bool m_bUseBigInt; }; class DBIHandleODBC : public DBIHandle { protected: ODBCConn* m_conn; DBISettingParamsODBC m_settings; bool m_bInTrans; /** Checks if the connection is open and throws otherwise */ ODBCConn *getConnData(); SQLHSTMT openStatement(SQLHDBC hdbc); SQLHDESC getStatementDesc( SQLHSTMT hHstmt ); public: DBIHandleODBC(); DBIHandleODBC( ODBCConn *conn ); virtual ~DBIHandleODBC(); virtual void options( const String& params ); virtual const DBISettingParamsODBC* options() const; virtual void close(); virtual DBIRecordset *query( const String &sql, ItemArray* params ); virtual DBIStatement* prepare( const String &query ); virtual int64 getLastInsertedId( const String& name = "" ); virtual void begin(); virtual void commit(); virtual void rollback(); virtual void selectLimited( const String& query, int64 nBegin, int64 nCount, String& result ); ODBCConn *getConn() { return m_conn; } /** Throws a DBI error wsrapping an ODBC error. */ static void throwError( int falconError, SQLSMALLINT plm_handle_type, SQLHANDLE plm_handle, int ConnInd, bool free = true ); /** Utility to get ODBC error description. */ static String GetErrorMessage(SQLSMALLINT plm_handle_type, SQLHANDLE plm_handle, int ConnInd); void incConnRef() { m_conn->incref(); } void decConnRef() { m_conn->decref(); } }; class DBIServiceODBC : public DBIService { public: DBIServiceODBC() : DBIService( "DBI_odbc" ) {} virtual void init(); virtual DBIHandle *connect( const String ¶meters ); virtual CoreObject *makeInstance( VMachine *vm, DBIHandle *dbh ); }; } extern Falcon::DBIServiceODBC theODBCService; #endif /* DBI_ODBC_H */ /* end of odbc.h */ modules/native/dbi/odbc/odbc_srv.cpp000066400000000000000000000071471176363201700200050ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: odbc_srv.cpp * * ODBC service/driver * ------------------------------------------------------------------- * Author: Giancarlo Niccolai * Begin: Wed Oct 13 09:44:00 2008 * * ------------------------------------------------------------------- * (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #define _WIN32_WINNT 0x0500 #if ! defined( _WIN32_WINNT ) || _WIN32_WINNT < 0x0403 #undef _WIN32_WINNT #define _WIN32_WINNT 0x0403 #endif #include #include #include #include #include "odbc_mod.h" #include #include #include namespace Falcon { /****************************************************************************** * Main service class *****************************************************************************/ void DBIServiceODBC::init() { } DBIHandle *DBIServiceODBC::connect( const String ¶meters ) { AutoCString asConnParams( parameters ); SQLHENV hEnv; SQLHDBC hHdbc; RETCODE retcode = SQLAllocHandle (SQL_HANDLE_ENV, NULL, &hEnv); if( ( retcode != SQL_SUCCESS_WITH_INFO ) && ( retcode != SQL_SUCCESS ) ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CONNECT, __LINE__) .extra( "Impossible to allocate the ODBC environment" )); } retcode = SQLSetEnvAttr( hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER) SQL_OV_ODBC3, SQL_IS_INTEGER ); if( ( retcode != SQL_SUCCESS_WITH_INFO ) && ( retcode != SQL_SUCCESS ) ) { SQLFreeHandle(SQL_HANDLE_ENV, hEnv ); throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CONNECT, __LINE__) .extra( "Impossible to notify ODBC that this is an ODBC 3.0 app." )); } // Allocate ODBC connection handle and connect. retcode = SQLAllocHandle( SQL_HANDLE_DBC, hEnv, &hHdbc ); if( ( retcode != SQL_SUCCESS_WITH_INFO ) && ( retcode != SQL_SUCCESS ) ) { SQLFreeHandle(SQL_HANDLE_ENV, hEnv ); throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CONNECT, __LINE__) .extra( "Impossible to allocate ODBC connection handle and connect." )); return NULL; } int nSec = 15; SQLSetConnectAttr( hHdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER)(&nSec), 0 ); SQLCHAR OutConnStr[MAXBUFLEN]; short OutConnStrLen = MAXBUFLEN; retcode = SQLDriverConnect( hHdbc, NULL, (SQLCHAR*)asConnParams.c_str(), asConnParams.length(), OutConnStr, MAXBUFLEN, &OutConnStrLen, SQL_DRIVER_NOPROMPT ); if( ( retcode != SQL_SUCCESS ) && ( retcode != SQL_SUCCESS_WITH_INFO ) ) { String errorMessage = String("SQLDriverConnect failed. Reason: ") + DBIHandleODBC::GetErrorMessage( SQL_HANDLE_DBC, hHdbc, FALSE ); SQLDisconnect( hHdbc ); SQLFreeHandle( SQL_HANDLE_DBC, hHdbc ); SQLFreeHandle( SQL_HANDLE_ENV, hEnv ); throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CONNECT, __LINE__) .extra( errorMessage )); return NULL; } ODBCConn* conn = new ODBCConn( hEnv, hHdbc ); return new DBIHandleODBC( conn ); } CoreObject *DBIServiceODBC::makeInstance( VMachine *vm, DBIHandle *dbh ) { Item *cl = vm->findWKI( "ODBC" ); if ( cl == 0 || ! cl->isClass() || cl->asClass()->symbol()->name() != "ODBC" ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_BASE, __LINE__ ) .desc( "ODBC DBI driver was not found" ) ); return 0; } CoreObject *obj = cl->asClass()->createInstance(); obj->setUserData( dbh ); return obj; } } /* namespace Falcon */ /* end of mysql_srv.cpp */ modules/native/dbi/odbc/version.h000066400000000000000000000011171176363201700173250ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: version.h * * MySQL module version information * ------------------------------------------------------------------- * Author: Jeremy Cowgar * Begin: Wed Jan 02 21:35:18 2008 * * ------------------------------------------------------------------- * (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #ifndef VERSION_H #define VERSION_H #define VERSION_MAJOR 0 #define VERSION_MINOR 9 #define VERSION_REVISION 8 #endif /* end of version.h */ modules/native/dbi/oracle/000077500000000000000000000000001176363201700160255ustar00rootroot00000000000000modules/native/dbi/oracle/CMakeLists.txt000066400000000000000000000056731176363201700206000ustar00rootroot00000000000000#################################################################### # The Falcon Programming language # # CMake configuration file for module Oracle #################################################################### if(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) endif(COMMAND cmake_policy) falcon_define_module( FALCON_MODULE oracle ) SET(ORACLE_HOME $ENV{ORACLE_HOME}) SET(ORACLE_INCLUDE_SEARCH_PATH ${ORACLE_INCLUDE_DIR} ${ORACLE_HOME}/sdk/include ${ORACLE_HOME}/xdk/include ${ORACLE_HOME}/OCI/include ${ORACLE_HOME}/sdk/lib/msvc/ ${ORACLE_HOME}/include /usr/local/include/oracle ) FIND_PATH( ORACLE_INCLUDE_DIR occi.h ${ORACLE_INCLUDE_SEARCH_PATH} ) SET(ORACLE_LIB_SEARCH_PATH ${ORACLE_LIB_DIR} ${ORACLE_HOME}/lib ${ORACLE_HOME}/OCI/lib/MSVC ${ORACLE_HOME} /usr/local/lib/oracle ) FIND_LIBRARY( ORACLE_LIBRARY_OCCI NAMES libocci occi ${ORACLE_LIB_SEARCH_PATH} ) FIND_LIBRARY( ORACLE_LIBRARY_CLNTSH NAMES libclntsh clntsh ${ORACLE_LIB_SEARCH_PATH} ) IF(APPLE) FIND_LIBRARY( ORACLE_LIBRARY_OCIEI NAMES libociei ociei ${ORACLE_LIB_SEARCH_PATH} ) ENDIF(APPLE) IF(ORACLE_INCLUDE_DIR AND ORACLE_LIBRARY_OCCI AND ORACLE_LIBRARY_CLNTSH) INCLUDE_DIRECTORIES(BEFORE ${ORACLE_INCLUDE_DIR}) ELSE(ORACLE_INCLUDE_DIR AND ORACLE_LIBRARY) IF(NOT ORACLE_INCLUDE_DIR) MESSAGE(SEND_ERROR "Oracle include path was not found") MESSAGE(SEND_ERROR "Looked in: ${ORACLE_INCLUDE_SEARCH_PATH}") ENDIF (NOT ORACLE_INCLUDE_DIR) IF (NOT ORACLE_LIBRARY_OCCI ) MESSAGE(SEND_ERROR "Oracle client library libocci was not found") MESSAGE(SEND_ERROR "Looked in: ${ORACLE_LIB_SEARCH_PATH}") ENDIF (NOT ORACLE_LIBRARY_OCCI) IF (NOT ORACLE_LIBRARY_CLNTSH ) MESSAGE(SEND_ERROR "Oracle client library libclntsh was not found") MESSAGE(SEND_ERROR "Looked in: ${ORACLE_LIB_SEARCH_PATH}") ENDIF (NOT ORACLE_LIBRARY_CLNTSH) IF (NOT ORACLE_LIBRARY_OCIEI AND APPLE) MESSAGE(SEND_ERROR "Oracle client library libociei was not found") MESSAGE(SEND_ERROR "Looked in: ${ORACLE_LIB_SEARCH_PATH}") ENDIF (NOT ORACLE_LIBRARY_OCIEI AND APPLE) MESSAGE(FATAL_ERROR "Cannot build Oracle DBI module") ENDIF(ORACLE_INCLUDE_DIR AND ORACLE_LIBRARY_OCCI AND ORACLE_LIBRARY_CLNTSH) MESSAGE(STATUS "Found occi.h in ${ORACLE_INCLUDE_DIR}") MESSAGE(STATUS "Found library clntsh ${ORACLE_LIBRARY_CLNTSH}") MESSAGE(STATUS "Found library occi ${ORACLE_LIBRARY_OCCI}") IF(APPLE) MESSAGE(STATUS "Found library ociei ${ORACLE_LIBRARY_OCIEI}") ENDIF(APPLE) # Inclusion settings INCLUDE_DIRECTORIES(${ORACLE_INCLUDE_DIR}) INCLUDE_DIRECTORIES(../include) INCLUDE_DIRECTORIES(../dbi) INCLUDE_DIRECTORIES(.) # Target ADD_LIBRARY( ${FALCON_MODULE} MODULE ${dbi_common_files} oracle.cpp oracle_ext.cpp oracle_mod.cpp ) # Link TARGET_LINK_LIBRARIES( ${FALCON_MODULE} falcon_engine ${ORACLE_LIBRARY} ) falcon_install_module2( ${FALCON_MODULE} dbi ) modules/native/dbi/oracle/oracle.cpp000066400000000000000000000033331176363201700200000ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: oracle.cpp * * Oracle driver main module * * This is BOTH a driver for the DBI interface AND a standalone * Oracle module. * ------------------------------------------------------------------- * Author: Steven Oliver * * ------------------------------------------------------------------- * (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include "oracle_mod.h" #include "version.h" #include "oracle_ext.h" // Instantiate the driver service Falcon::DBIServiceOracle theOracleService; /*--# << turn on when active @module dbi.oracle Oracle driver module @brief DBI extension supporting Oracle Directly importable as @b dbi.oracle, it is usually loaded through the @a dbi module. */ // the main module FALCON_MODULE_DECL { // Module declaration Falcon::Module *self = new Falcon::Module(); self->name( "oracle" ); self->engineVersion( FALCON_VERSION_NUM ); self->version( VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION ); // first of all, we need to declare our dependency from the DBI module. self->addDepend( "dbi", "dbi", true, false ); // also, we declare an Oracle class, which derives from DBIHandler which // is in the DBI module. Falcon::Symbol *dbh_class = self->addExternalRef( "dbi.%Handle" ); // it's external Falcon::Symbol *oracle_class = self->addClass( "Oracle", Falcon::Ext::Oracle_init ); oracle_class->getClassDef()->addInheritance( new Falcon::InheritDef( dbh_class ) ); oracle_class->setWKS( true ); // service publication self->publishService( &theOracleService ); // we're done return self; } /* end of oracle.cpp */ modules/native/dbi/oracle/oracle_ext.cpp000066400000000000000000000040241176363201700206560ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: oracle_ext.cpp * * Oracle Falcon extension interface * ------------------------------------------------------------------- * Author: Steven Oliver * * ------------------------------------------------------------------- * (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include #include "oracle_mod.h" #include "oracle_ext.h" /*--# << turn on when active @beginmodule dbi.oracle */ namespace Falcon { namespace Ext { /*--# << turn on when active @class Oracle @brief Direct interface to Oracle database. @param connect String containing connection parameters. The connect string uses the standard connection values: - username - password - database Oracle does not use ports in its connection string. That information is generally stored in your TNSNAMES.ora file. */ FALCON_FUNC Oracle_init( VMachine *vm ) { Item *paramsI = vm->param(0); Item *i_tropts = vm->param(1); if ( ! paramsI || ! paramsI->isString() || ( i_tropts && ! i_tropts->isString() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S,[S]" ) ); } String *params = paramsI->asString(); DBIHandle *hand = 0; try { //hand = theOracleService.connect( *params ); if( i_tropts != 0 ) { // hand->options( *i_tropts->asString() ); } //CoreObject *instance = theOracleService.makeInstance( vm, hand ); //vm->retval( instance ); } catch (...) { //delete hand; throw ; } } } /* namespace Ext */ } /* namespace Falcon */ /* end of oracle_ext.cpp */ modules/native/dbi/oracle/oracle_ext.h000066400000000000000000000012121176363201700203170ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: oracle_ext.h * * Oracle Falcon extension interface * ------------------------------------------------------------------- * Author: Steven Oliver * * ------------------------------------------------------------------- * (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include #ifndef ORACLE_EXT_H #define ORACLE_EXT_H namespace Falcon { class VMachine; namespace Ext { FALCON_FUNC Oracle_init( VMachine *vm ); } } #endif /* ORACLE_EXT_H */ /* end of oracle_ext.h */ modules/native/dbi/oracle/oracle_mod.cpp000066400000000000000000000111101176363201700206270ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: oracle_mod.cpp * * Oracle Falcon service/driver * ------------------------------------------------------------------- * Author: Steven Oliver * * ------------------------------------------------------------------- * (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include #include #include #include #include "oracle_mod.h" namespace Falcon { /****************************************************************************** * Transaction Class *****************************************************************************/ DBIStatementOracle::DBIStatementOracle( DBIHandleOracle *dbh, oracle::occi::Statement* stmt ): DBIStatement( dbh ), o_statement( stmt ), o_inBind(0), o_bBound( false ) { //o_pConn = dbh->getConn(); //o_pConn->incref(); //o_pStmt = new ORACLEStmtHandle( stmt ); } DBIStatementOracle::~DBIStatementOracle() { close(); } DBIRecordset* DBIStatementOracle::execute( ItemArray* params ) { if( o_statement == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_STMT, __LINE__ ) ); /* * TODO * */ return 0; } void DBIStatementOracle::close() { if ( o_statement != 0 ) { o_statement = 0; delete o_inBind; o_inBind = 0; //o_pConn->decref(); //o_pStmt->decref(); } } /****************************************************************************** * DB Handler Class *****************************************************************************/ DBIHandleOracle::~DBIHandleOracle() { DBIHandleOracle::close(); } void DBIHandleOracle::close() { if ( o_conn != NULL ) { //o_pConn->decref(); // Kill the connection then the environment o_env->terminateConnection( o_conn ); //oracle::occi::Environment::terminateEnvironment( o_env ); } } void DBIHandleOracle::commit() { if( o_conn == NULL ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); } } void DBIHandleOracle::rollback() { if( o_conn == NULL ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); } /****************************************************************************** * Main service class *****************************************************************************/ void DBIServiceOracle::init() { } DBIHandle *DBIServiceOracle::connect( const String ¶meters ) { oracle::occi::Connection *conn; oracle::occi::Environment *env; if ( conn = NULL ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_NOMEM, __LINE__) ); } DBIConnParams connParams; if( ! connParams.parse( parameters ) ) { //close( conn ); throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CONNPARAMS, __LINE__) .extra( parameters ) ); } /* env = oracle::occi::Environment::createEnvironment(); // We'll need at least a username, password, and db. conn = env->createConnection(connParams.o_szUser, connParams.o_szPassword, connParams.o_szConn); */ if ( conn == NULL ) { String errorMessage = oracle::occi::SQLException::getMessage(); errorMessage.bufferize(); //close( conn ); int en = mysql_errno( conn ) == ER_BAD_DB_ERROR ? FALCON_DBI_ERROR_DB_NOTFOUND : FALCON_DBI_ERROR_CONNECT; throw new DBIError( ErrorParam( en, __LINE__).extra( errorMessage ) ); } #if ( OCCI_MAJOR_VERSION > 9 ) env->setCacheSortedFlush( true ); #endif //return new DBIHandleOracle( conn ); } CoreObject *DBIServiceOracle::makeInstance( VMachine *vm, DBIHandle *dbh ) { Item *cl = vm->findWKI( "Oracle" ); if ( cl == 0 || ! cl->isClass() ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_INVALID_DRIVER, __LINE__ ) ); } CoreObject *obj = cl->asClass()->createInstance(); obj->setUserData( dbh ); return obj; } }/* namespace Falcon */ /* end of oracle_mod.cpp */ modules/native/dbi/oracle/oracle_mod.h000066400000000000000000000103001176363201700202740ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: oracle_mod.h * * Oracle driver main module interface * ------------------------------------------------------------------- * Author: Steven Oliver * * ------------------------------------------------------------------- * (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #ifndef DBI_ORACLE_H #define DBI_ORACLE_H #include #include #include namespace Falcon { class ORACLEHandle; class ORACLEStmtHandle: public DBIRefCounter { public: ORACLEStmtHandle( oracle::occi::Statement* o ): DBIRefCounter( o ) {} virtual ~ORACLEStmtHandle() { //mysql_stmt_close( handle() ); FIXME } }; class ODBIInBind: public DBIInBind { public: ODBIInBind( oracle::occi::Statement * stmt ); virtual ~ODBIInBind(); virtual void onFirstBinding( int size ); virtual void onItemChanged( int num ); //MYSQL_BIND* mybindings() const { return m_mybind; } FIXME private: //MYSQL_BIND* m_mybind; oracle::occi::Statement* o_stmt; }; class ODBIOutBind: public DBIOutBind { public: ODBIOutBind(): //bIsNull( false ), nLength( 0 ) {} ~ODBIOutBind() {} //o_bool bIsNull; unsigned long nLength; }; class DBIHandleOracle; class DBIRecordsetOracle : public DBIRecordset { protected: int o_row; int o_rowCount; int o_columnCount; bool m_bCanSeek; ORACLEHandle *o_pConn; public: virtual ~DBIRecordsetOracle(); virtual int64 getRowIndex(); virtual int64 getRowCount(); virtual int getColumnCount(); virtual bool getColumnName( int nCol, String& name ); virtual void close(); }; class DBIHandleOracle : public DBIHandle { protected: oracle::occi::Connection *o_conn; oracle::occi::Environment *o_env; DBISettingParams o_settings; public: DBIHandleOracle(); DBIHandleOracle( oracle::occi::Connection *conn ); virtual ~DBIHandleOracle(); //virtual void options( const String& params ); FIXME //virtual const DBISettingParams* options() const; virtual void close(); //virtual DBIRecordset *query( const String &sql, ItemArray* params ); FIXME //virtual DBIStatement* prepare( const String &query ); virtual void commit(); virtual void rollback(); //virtual void selectLimited( const String& query, int64 nBegin, int64 nCount, String& result ); FIXME }; class DBIStatementOracle : public DBIStatement { protected: oracle::occi::Statement* o_statement; ODBIInBind* o_inBind; bool o_bBound; public: DBIStatementOracle( DBIHandleOracle *dbh, oracle::occi::Statement* stmt ); virtual ~DBIStatementOracle(); virtual DBIRecordset* execute( ItemArray* params ); virtual void close(); oracle::occi::Statement* my_statement() const { return o_statement; } }; class DBIServiceOracle : public DBIService { public: DBIServiceOracle() : DBIService( "DBI_oracle" ) {} virtual void init(); virtual DBIHandle *connect( const String ¶meters ); virtual CoreObject *makeInstance( VMachine *vm, DBIHandle *dbh ); }; } extern Falcon::DBIServiceOracle theOracleService; #endif /* DBI_ORACLE_H */ /* end of oracle_mod.h */ modules/native/dbi/oracle/version.h000066400000000000000000000010551176363201700176640ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: version.h * * Oracle module version information * ------------------------------------------------------------------- * Author: Steven Oliver * * ------------------------------------------------------------------- * (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #ifndef VERSION_H #define VERSION_H #define VERSION_MAJOR 0 #define VERSION_MINOR 9 #define VERSION_REVISION 8 #endif /* end of version.h */ modules/native/dbi/pgsql/000077500000000000000000000000001176363201700157065ustar00rootroot00000000000000modules/native/dbi/pgsql/CMakeLists.txt000066400000000000000000000037301176363201700204510ustar00rootroot00000000000000#################################################################### # The Falcon Programming language # # CMake configuration file for module pgsql #################################################################### if(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) endif(COMMAND cmake_policy) FALCON_DEFINE_MODULE( FALCON_MODULE pgsql ) # Find PgSQL SET(PGSQL_INCLUDE_SEARCH_PATH ${PGSQL_INCLUDE_DIR} /usr/include/pgsql /usr/local/include/pgsql /usr/include/postgresql ) FIND_PATH(PGSQL_REAL_INCLUDE_DIR libpq-fe.h ${PGSQL_INCLUDE_SEARCH_PATH}) # Find PgSQL Client Library SET(PGSQL_LIB_SEARCH_PATH ${PGSQL_LIB_DIR} /usr/lib /usr/lib/pgsql /usr/local/lib/pgsql) SET(PGSQL_LIB_NAMES pq libpq) FIND_LIBRARY(PGSQL_LIBRARY NAMES ${PGSQL_LIB_NAMES} PATHS ${PGSQL_LIB_SEARCH_PATH} ) IF (PGSQL_REAL_INCLUDE_DIR AND PGSQL_LIBRARY) INCLUDE_DIRECTORIES(BEFORE ${PGSQL_REAL_INCLUDE_DIR}) ELSE(PGSQL_REAL_INCLUDE_DIR AND PGSQL_LIBRARY) IF(NOT PGSQL_REAL_INCLUDE_DIR) MESSAGE(SEND_ERROR "PgSQL include path was not found") MESSAGE(SEND_ERROR "Looked in: ${PGSQL_INCLUDE_SEARCH_PATH}") ENDIF(NOT PGSQL_REAL_INCLUDE_DIR) IF(NOT PGSQL_LIBRARY) MESSAGE(SEND_ERROR "PgSQL library was not found") MESSAGE(SEND_ERROR "Looked for: ${PGSQL_LIB_NAMES}") MESSAGE(SEND_ERROR "Looked in: ${PGSQL_LIB_SEARCH_PATH}") ENDIF(NOT PGSQL_LIBRARY) MESSAGE(FATAL_ERROR "Could not find PgSQL, cannot continue") ENDIF (PGSQL_REAL_INCLUDE_DIR AND PGSQL_LIBRARY) MESSAGE(STATUS "Found libpq-fe.h in ${PGSQL_REAL_INCLUDE_DIR}") MESSAGE(STATUS "Found pgsql client library, ${PGSQL_LIBRARY}") # Inclusion settings INCLUDE_DIRECTORIES(.) INCLUDE_DIRECTORIES(../include) # Target ADD_LIBRARY( ${FALCON_MODULE} MODULE ${dbi_common_files} pgsql_fm.cpp pgsql_ext.cpp pgsql_mod.cpp ) # Link IF(MSVC) SET(PGSQL_OPT_LIBS wsock32) ENDIF(MSVC) TARGET_LINK_LIBRARIES( ${FALCON_MODULE} falcon_engine ${PGSQL_OPT_LIBS} ${PGSQL_LIBRARY}) FALCON_INSTALL_MODULE2( ${FALCON_MODULE} dbi ) modules/native/dbi/pgsql/pgsql_ext.cpp000066400000000000000000000053671176363201700204330ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: pgsql_ext.cpp * * PgSQL Falcon service/driver * ------------------------------------------------------------------- * Author: Jeremy Cowgar, Stanislas Marquis * Begin: Sun Dec 23 21:54:42 2007 * * ------------------------------------------------------------------- * (C) Copyright 2007: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include #include "pgsql_ext.h" #include "pgsql_mod.h" /*# @beginmodule dbi.pgsql */ namespace Falcon { namespace Ext { /*# @class PgSQL @brief Direct interface to Postgre SQL database. @param connect String containing connection parameters. @optparam options String containing options The @b connect string is directly passed to the low level postgre driver. */ FALCON_FUNC PgSQL_init( VMachine* vm ) { Item* i_params = vm->param( 0 ); Item* i_opts = vm->param( 1 ); if ( !i_params || !i_params->isString() || ( i_opts != 0 && !i_opts->isString() ) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S,[S]" ) ); } String* params = i_params->asString(); DBIHandle* hand = 0; try { hand = thePgSQLService.connect( *params ); if ( i_opts != 0 ) hand->options( *i_opts->asString() ); // great, we have the database handler open. Now we must create a falcon object to store it. CoreObject* instance = thePgSQLService.makeInstance( vm, hand ); vm->retval( instance ); } catch ( DBIError* error ) { delete hand; throw error; } } /*# @method prepareNamed PgSQL @brief Prepares a PgSQL specific "named statement". @param name Name for the prepared statement @param query The query to prepare */ FALCON_FUNC PgSQL_prepareNamed( VMachine* vm ) { Item* i_name = vm->param( 0 ); Item* i_query = vm->param( 1 ); if ( !i_name || !i_name->isString() || !i_query || !i_query->isString() ) throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).extra( "S,S" ) ); DBIHandlePgSQL* dbh = static_cast( vm->self().asObjectSafe()->getUserData() ); fassert( dbh ); // names of stored procedures need to be lowercased String name = *i_name->asString(); name.lower(); DBIStatement* trans = dbh->prepareNamed( name, *i_query->asString() ); // snippet taken from dbi_ext.h - should be shared? Item *trclass = vm->findWKI( "%Statement" ); fassert( trclass != 0 && trclass->isClass() ); CoreObject *oth = trclass->asClass()->createInstance(); oth->setUserData( trans ); vm->retval( oth ); } } /* namespace Ext */ } /* namespace Falcon */ modules/native/dbi/pgsql/pgsql_ext.h000066400000000000000000000013401176363201700200630ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: pgsql_ext.h * * PgSQL Falcon service/driver * ------------------------------------------------------------------- * Author: Jeremy Cowgar, Stanislas Marquis * Begin: Sun Dec 23 21:54:42 2007 * * ------------------------------------------------------------------- * (C) Copyright 2007: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include #include #ifndef PGSQL_EXT_H #define PGSQL_EXT_H namespace Falcon { class VMachine; namespace Ext { FALCON_FUNC PgSQL_init( VMachine* vm ); FALCON_FUNC PgSQL_prepareNamed( VMachine* vm ); } // !Ext } // !Falcon #endif /* PGSQL_EXT_H */ modules/native/dbi/pgsql/pgsql_fm.cpp000066400000000000000000000040101176363201700202150ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: pgsql_fm.cpp * * PgSQL Falcon service/driver * ------------------------------------------------------------------- * Author: Jeremy Cowgar, Stanislas Marquis * Begin: Sun Dec 23 21:54:42 2007 * * ------------------------------------------------------------------- * (C) Copyright 2007: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include "pgsql_ext.h" #include "pgsql_mod.h" #include "version.h" /*# @module dbi.pgsql Postgre SQL database driver module @brief DBI extension supporting Postgre SQL database Directly importable as @b dbi.pgsql, it is usually loaded through the @a dbi module. */ // Instantiate the driver service Falcon::DBIServicePgSQL thePgSQLService; // the main module FALCON_MODULE_DECL { // Module declaration Falcon::Module *self = new Falcon::Module(); self->name( "pgsql" ); self->engineVersion( FALCON_VERSION_NUM ); self->version( VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION ); // first of all, we need to declare our dependency from the DBI module. self->addDepend( "dbi", "dbi", true, false ); // also, we declare a PgSQL class, which derives from DBIHandler which // is in the DBI module. Falcon::Symbol *dbh_class = self->addExternalRef( "dbi.%Handle" ); // it's external dbh_class->imported( true ); Falcon::Symbol *pgsql_class = self->addClass( "PgSQL", Falcon::Ext::PgSQL_init ); pgsql_class->getClassDef()->addInheritance( new Falcon::InheritDef( dbh_class ) ); pgsql_class->setWKS( true ); // we don't have extra functions for the dbhandler of mysql. If whe had, // this would be the right place to store them. // named prepared statements self->addClassMethod( pgsql_class, "prepareNamed", Falcon::Ext::PgSQL_prepareNamed ) .asSymbol()->addParam( "name" )->addParam( "query" ); // service publication self->publishService( &thePgSQLService ); // we're done return self; } modules/native/dbi/pgsql/pgsql_mod.cpp000066400000000000000000000423301176363201700204010ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: pgsql_mod.cpp * * PgSQL Falcon service/driver * ------------------------------------------------------------------- * Author: Jeremy Cowgar, Stanislas Marquis * Begin: Sun Dec 23 21:54:42 2007 * * ------------------------------------------------------------------- * (C) Copyright 2007: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #include #include #include #include "pgsql_mod.h" /* Oid - see catalog/pg_type.h */ #define PG_TYPE_BOOL 16 #define PG_TYPE_BYTEA 17 #define PG_TYPE_CHAR 18 #define PG_TYPE_NAME 19 #define PG_TYPE_INT8 20 #define PG_TYPE_INT2 21 #define PG_TYPE_INT2VECTOR 22 #define PG_TYPE_INT4 23 #define PG_TYPE_REGPROC 24 #define PG_TYPE_TEXT 25 #define PG_TYPE_OID 26 #define PG_TYPE_TID 27 #define PG_TYPE_XID 28 #define PG_TYPE_CID 29 #define PG_TYPE_OIDVECTOR 30 #define PG_TYPE_SET 32 #define PG_TYPE_CHAR2 409 #define PG_TYPE_CHAR4 410 #define PG_TYPE_CHAR8 411 #define PG_TYPE_POINT 600 #define PG_TYPE_LSEG 601 #define PG_TYPE_PATH 602 #define PG_TYPE_BOX 603 #define PG_TYPE_POLYGON 604 #define PG_TYPE_FILENAME 605 #define PG_TYPE_FLOAT4 700 #define PG_TYPE_FLOAT8 701 #define PG_TYPE_ABSTIME 702 #define PG_TYPE_RELTIME 703 #define PG_TYPE_TINTERVAL 704 #define PG_TYPE_UNKNOWN 705 #define PG_TYPE_MONEY 790 #define PG_TYPE_OIDINT2 810 #define PG_TYPE_OIDINT4 910 #define PG_TYPE_OIDNAME 911 #define PG_TYPE_BPCHAR 1042 #define PG_TYPE_VARCHAR 1043 #define PG_TYPE_DATE 1082 #define PG_TYPE_TIME 1083 /* w/o timezone */ #define PG_TYPE_TIMETZ 1266 /* with timezone */ #define PG_TYPE_TIMESTAMP 1114 /* w/o timezone */ #define PG_TYPE_TIMESTAMPTZ 1184 /* with timezone */ #define PG_TYPE_NUMERIC 1700 namespace Falcon { int32 dbi_pgsqlQuestionMarksToDollars( const String& input, String& output ) { output.reserve( input.size() + 32 ); output.size( 0 ); uint32 pos0 = 0; uint32 pos1 = input.find( "?" ); int32 i = 0; while ( pos1 != String::npos ) { output += input.subString( pos0, pos1 ); output.A( "$" ).N( ++i ); pos0 = pos1 + 1; pos1 = input.find( "?", pos0 ); } output += input.subString( pos0 ); return i; } /****************************************************************************** * Recordset class *****************************************************************************/ DBIRecordsetPgSQL::DBIRecordsetPgSQL( DBIHandlePgSQL* dbh, PGresult* res ) : DBIRecordset( dbh ), m_row( -1 ), m_res( res ), m_pConn( dbh->getConnRef() ) { m_rowCount = PQntuples( res ); m_columnCount = PQnfields( res ); m_pConn->incref(); } DBIRecordsetPgSQL::~DBIRecordsetPgSQL() { if ( m_res != NULL ) close(); } bool DBIRecordsetPgSQL::fetchRow() { return ++m_row < m_rowCount ? true : false; } bool DBIRecordsetPgSQL::discard( int64 ncount ) { for ( int64 i=0; i < ncount; ++i ) { if ( !fetchRow() ) return false; } return true; } int64 DBIRecordsetPgSQL::getRowIndex() { return m_row; } int64 DBIRecordsetPgSQL::getRowCount() { return m_rowCount; } int DBIRecordsetPgSQL::getColumnCount() { return m_columnCount; } bool DBIRecordsetPgSQL::getColumnName( int nCol, String& name ) { if ( nCol < 0 || nCol >= m_columnCount ) return false; name.bufferize( PQfname( m_res, nCol ) ); return true; } bool DBIRecordsetPgSQL::getColumnValue( int nCol, Item& value ) { if ( nCol < 0 || nCol >= m_columnCount ) return false; const char* v = PQgetvalue( m_res, m_row, nCol ); if ( *v == '\0' && PQgetisnull( m_res, m_row, nCol ) ) { value.setNil(); return true; } else if ( m_dbh->options()->m_bFetchStrings ) { String s( v ); s.bufferize(); value = s; return true; } switch ( PQftype( m_res, nCol ) ) { case PG_TYPE_BOOL: value.setBoolean( *v == 't' ? true : false ); break; case PG_TYPE_INT2: case PG_TYPE_INT4: case PG_TYPE_INT8: value.setInteger( atol( v ) ); break; case PG_TYPE_FLOAT4: case PG_TYPE_FLOAT8: case PG_TYPE_NUMERIC: value.setNumeric( atof( v ) ); break; case PG_TYPE_DATE: { VMachine* vm = VMachine::getCurrent(); if ( vm == 0 ) return false; String tv( v ); int64 year, month, day; tv.subString( 0, 4 ).parseInt( year ); tv.subString( 5, 7 ).parseInt( month ); tv.subString( 8, 10 ).parseInt( day ); TimeStamp* ts = new TimeStamp( year, month, day ); CoreObject* ots = vm->findWKI( "TimeStamp" )->asClass()->createInstance(); ots->setUserData( ts ); value = ots; break; } case PG_TYPE_TIME: case PG_TYPE_TIMETZ: // todo: handle tz { VMachine* vm = VMachine::getCurrent(); if ( vm == 0 ) return false; String tv( v ); int64 hour, minute, second; tv.subString( 0, 2 ).parseInt( hour ); tv.subString( 3, 5 ).parseInt( minute ); tv.subString( 6, 8 ).parseInt( second ); TimeStamp* ts = new TimeStamp( 0, 0, 0, hour, minute, second ); CoreObject* ots = vm->findWKI( "TimeStamp" )->asClass()->createInstance(); ots->setUserData( ts ); value = ots; break; } case PG_TYPE_TIMESTAMP: case PG_TYPE_TIMESTAMPTZ: // todo: handle tz { VMachine* vm = VMachine::getCurrent(); if ( vm == 0 ) return false; String tv( v ); int64 year, month, day, hour, minute, second; tv.subString( 0, 4 ).parseInt( year ); tv.subString( 5, 7 ).parseInt( month ); tv.subString( 8, 10 ).parseInt( day ); tv.subString( 11, 13 ).parseInt( hour ); tv.subString( 14, 16 ).parseInt( minute ); tv.subString( 17, 19 ).parseInt( second ); TimeStamp* ts = new TimeStamp( year, month, day, hour, minute, second ); CoreObject* ots = vm->findWKI( "TimeStamp" )->asClass()->createInstance(); ots->setUserData( ts ); value = ots; break; } case PG_TYPE_CHAR2: case PG_TYPE_CHAR4: case PG_TYPE_CHAR8: case PG_TYPE_TEXT: case PG_TYPE_VARCHAR: default: { String s( v ); s.bufferize(); value = s; break; } } return true; } void DBIRecordsetPgSQL::close() { if ( m_res != NULL ) { PQclear( m_res ); m_pConn->decref(); m_res = NULL; } } /* DBIStatementPgSQL class */ DBIStatementPgSQL::DBIStatementPgSQL( DBIHandlePgSQL* dbh ) : DBIStatement( dbh ), m_pConn( dbh->getConnRef() ) { m_pConn->incref(); } void DBIStatementPgSQL::init( const String& query, const String& name ) { fassert( name.length() ); m_name = name; String temp; m_nParams = dbi_pgsqlQuestionMarksToDollars( query, temp ); AutoCString zQuery( temp ); AutoCString zName( name ); PGresult* res = PQprepare( m_pConn->handle(), zName.c_str(), zQuery.c_str(), m_nParams, NULL ); if ( res == NULL || PQresultStatus( res ) != PGRES_COMMAND_OK ) { DBIHandlePgSQL::throwError( __FILE__, __LINE__, res ); } PQclear( res ); getExecString( m_nParams, name ); } DBIStatementPgSQL::~DBIStatementPgSQL() { close(); } void DBIStatementPgSQL::getExecString( uint32 nParams, const String& name ) { fassert( name.length() ); m_execString.reserve( 11 + name.length() + ( nParams * 2 ) ); m_execString.size( 0 ); m_execString = "EXECUTE " + name + "("; if ( nParams > 0 ) { m_execString.append( "?" ); for ( uint32 i=1; i < nParams; ++i ) m_execString.append( ",?" ); } m_execString.append( ");" ); } DBIRecordset* DBIStatementPgSQL::execute( ItemArray* params ) { String output; if ( (params == 0 && m_nParams != 0) || (params != 0 && (params->length() != m_nParams || !dbi_sqlExpand( m_execString, output, *params ) ) ) ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_BIND_SIZE, __LINE__ ) ); } AutoCString zQuery( output ); PGresult* res = PQexec( ((DBIHandlePgSQL*)m_dbh)->getConn(), zQuery.c_str() ); if ( res == 0 ) DBIHandlePgSQL::throwError( __FILE__, __LINE__, res ); ExecStatusType st = PQresultStatus( res ); // have we a resultset? if ( st == PGRES_TUPLES_OK ) { return new DBIRecordsetPgSQL( static_cast(m_dbh), res ); } else if ( st != PGRES_COMMAND_OK ) { DBIHandlePgSQL::throwError( __FILE__, __LINE__, res ); } // no result PQclear( res ); return 0; } void DBIStatementPgSQL::reset() { } void DBIStatementPgSQL::close() { // deallocate the stored procedure String query = "DEALLOCATE "; query += m_name; AutoCString zQuery( query ); PGresult* res = PQexec( ((DBIHandlePgSQL*)m_dbh)->getConn(), zQuery.c_str() ); if ( res != 0 ) PQclear( res ); if( m_pConn != 0 ) { m_pConn->decref(); m_pConn = 0; } } /****************************************************************************** * DB Handler class *****************************************************************************/ DBIHandlePgSQL::DBIHandlePgSQL( PGconn *conn ) : m_conn( conn ), m_bInTrans( false ), m_pConn( new PgSQLHandlerRef(conn) ) {} DBIHandlePgSQL::~DBIHandlePgSQL() { close(); } void DBIHandlePgSQL::close() { if ( m_conn != 0 ) { if ( m_bInTrans ) { PGresult* res = PQexec( m_conn, "COMMIT" ); m_bInTrans = false; if ( res != 0 ) PQclear( res ); } m_pConn->decref(); m_conn = 0; } } void DBIHandlePgSQL::options( const String& params ) { if ( !m_settings.parse( params ) ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_OPTPARAMS, __LINE__ ) .extra( params ) ); } } const DBISettingParams* DBIHandlePgSQL::options() const { return &m_settings; } void DBIHandlePgSQL::throwError( const char* file, int line, PGresult* res ) { fassert( res ); int code = (int) PQresultStatus( res ); const char* err = PQresultErrorMessage( res ); if ( err != NULL && err[0] != '\0' ) { String desc = err; desc.remove( desc.length() - 1, 1 ); // Get rid of newline desc.bufferize(); PQclear( res ); throw new DBIError( ErrorParam( code, line ) .extra( desc ) .module( file ) ); } else { PQclear( res ); throw new DBIError( ErrorParam( code, line ) .module( file ) ); } } void DBIHandlePgSQL::begin() { if ( m_conn == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); if ( m_bInTrans ) return; PGresult* res = PQexec( m_conn, "BEGIN" ); if ( res == 0 || PQresultStatus( res ) != PGRES_COMMAND_OK ) { throwError( __FILE__, __LINE__, res ); } m_bInTrans = true; PQclear( res ); } void DBIHandlePgSQL::commit() { if ( m_conn == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); if ( !m_bInTrans ) return; PGresult* res = PQexec( m_conn, "COMMIT" ); if ( res == 0 || PQresultStatus( res ) != PGRES_COMMAND_OK ) { throwError( __FILE__, __LINE__, res ); } m_bInTrans = false; PQclear( res ); } void DBIHandlePgSQL::rollback() { if ( m_conn == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); if ( !m_bInTrans ) return; PGresult* res = PQexec( m_conn, "ROLLBACK" ); if ( res == 0 || PQresultStatus( res ) != PGRES_COMMAND_OK ) { throwError( __FILE__, __LINE__, res ); } m_bInTrans = false; PQclear( res ); } PGresult* DBIHandlePgSQL::internal_exec( const String& sql, int64& affectedRows ) { fassert( m_conn ); AutoCString cStr( sql ); PGresult* res = PQexec( m_conn, cStr.c_str() ); if ( res == NULL ) throwError( __FILE__, __LINE__, res ); ExecStatusType st = PQresultStatus( res ); if ( st != PGRES_TUPLES_OK && st != PGRES_COMMAND_OK ) throwError( __FILE__, __LINE__, res ); const char* num = PQcmdTuples( res ); if ( num && num[0] != '\0' ) affectedRows = atoi( num ); else affectedRows = -1; return res; } DBIRecordset* DBIHandlePgSQL::query( const String &sql, ItemArray* params ) { if ( m_conn == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); PGresult* res = 0; if ( params != 0 && params->length() != 0 ) { String output; if ( !dbi_sqlExpand( sql, output, *params ) ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_QUERY, __LINE__ ) ); } res = internal_exec( output, m_nLastAffected ); } else { res = internal_exec( sql, m_nLastAffected ); } fassert( res != 0 ); ExecStatusType st = PQresultStatus( res ); // have we a resultset? if ( st == PGRES_TUPLES_OK ) { return new DBIRecordsetPgSQL( this, res ); } // no result fassert( st == PGRES_COMMAND_OK ); PQclear( res ); return 0; } DBIStatement* DBIHandlePgSQL::prepare( const String &query ) { if ( m_conn == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); DBIStatementPgSQL* stmt = new DBIStatementPgSQL( this ); // the statement may throw try { stmt->init( query ); return stmt; } catch( ... ) { delete stmt; throw; } } DBIStatement* DBIHandlePgSQL::prepareNamed( const String &name, const String& query ) { if ( m_conn == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); DBIStatementPgSQL* stmt = new DBIStatementPgSQL( this ); // the statement may throw try { stmt->init( query, name ); return stmt; } catch( ... ) { delete stmt; throw; } } int64 DBIHandlePgSQL::getLastInsertedId( const String& name ) { if ( m_conn == 0 ) throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CLOSED_DB, __LINE__ ) ); /* so... PQoidValue does it but takes a PGresult. We can retrieve a PGresult in case of prepared statements only... */ AutoCString nm( name ); PGresult* res = PQdescribePrepared( m_conn, nm.c_str() ); int oid = -1; if ( PQresultStatus( res ) != PGRES_COMMAND_OK ) goto finish; else oid = (int) PQoidValue( res ); finish: PQclear( res ); return oid; } void DBIHandlePgSQL::selectLimited( const String& query, int64 nBegin, int64 nCount, String& result ) { String sBegin, sCount; if( nCount > 0 ) sCount.A( " LIMIT " ).N( nCount ); if ( nBegin > 0 ) sBegin.A( " OFFSET " ).N( nBegin ); result = "SELECT " + query + sCount + sBegin; } /****************************************************************************** * Main service class *****************************************************************************/ void DBIServicePgSQL::init() { } DBIHandle *DBIServicePgSQL::connect( const String ¶meters ) { AutoCString connParams( parameters ); PGconn *conn = PQconnectdb( connParams.c_str () ); if ( conn == NULL ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_NOMEM, __LINE__ ) ); } if ( PQstatus( conn ) != CONNECTION_OK ) { String errorMessage = PQerrorMessage( conn ); errorMessage.remove( errorMessage.length() - 1, 1 ); // Get rid of newline errorMessage.bufferize(); PQfinish( conn ); throw new DBIError( ErrorParam( FALCON_DBI_ERROR_CONNECT, __LINE__ ) .extra( errorMessage ) ); } return new DBIHandlePgSQL( conn ); } CoreObject *DBIServicePgSQL::makeInstance( VMachine *vm, DBIHandle *dbh ) { Item *cl = vm->findWKI( "PgSQL" ); if ( cl == 0 || ! cl->isClass() || cl->asClass()->symbol()->name() != "PgSQL" ) { throw new DBIError( ErrorParam( FALCON_DBI_ERROR_INVALID_DRIVER, __LINE__ ) ); } CoreObject *obj = cl->asClass()->createInstance(); obj->setUserData( dbh ); return obj; } } /* namespace Falcon */ modules/native/dbi/pgsql/pgsql_mod.h000066400000000000000000000067411176363201700200540ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: pgsql_mod.h * * Pgsql driver main module interface * ------------------------------------------------------------------- * Author: Jeremy Cowgar, Stanislas Marquis * Begin: Sun Dec 23 21:36:20 2007 * * ------------------------------------------------------------------- * (C) Copyright 2007: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #ifndef PGSQL_MOD_H #define PGSQL_MOD_H #include #include #include namespace Falcon { class DBIHandlePgSQL; int32 dbi_pgsqlQuestionMarksToDollars( const String& input, String& output ); class PgSQLHandlerRef: public DBIRefCounter { public: PgSQLHandlerRef( PGconn* conn ): DBIRefCounter( conn ) {} ~PgSQLHandlerRef() { PQfinish( handle() ); } }; class DBIRecordsetPgSQL : public DBIRecordset { protected: int64 m_row; int64 m_rowCount; int m_columnCount; PGresult* m_res; PgSQLHandlerRef* m_pConn; public: DBIRecordsetPgSQL( DBIHandlePgSQL* dbh, PGresult* res ); virtual ~DBIRecordsetPgSQL(); virtual bool fetchRow(); virtual int64 getRowIndex(); virtual int64 getRowCount(); virtual int getColumnCount(); virtual bool getColumnName( int nCol, String& name ); virtual bool getColumnValue( int nCol, Item& value ); virtual bool discard( int64 ncount ); virtual void close(); }; class DBIStatementPgSQL : public DBIStatement { protected: uint32 m_nParams; String m_execString; String m_name; PgSQLHandlerRef* m_pConn; void getExecString( uint32 nParams, const String& name ); public: DBIStatementPgSQL( DBIHandlePgSQL* dbh ); virtual ~DBIStatementPgSQL(); virtual DBIRecordset* execute( ItemArray* params = 0 ); virtual void reset(); virtual void close(); void init( const String& query, const String& name="happy_falcon" ); }; class DBIHandlePgSQL : public DBIHandle { protected: PGconn* m_conn; bool m_bInTrans; DBISettingParams m_settings; PgSQLHandlerRef* m_pConn; public: DBIHandlePgSQL( PGconn* = 0 ); virtual ~DBIHandlePgSQL(); PGconn* getConn() { return m_conn; } virtual void options( const String& params ); virtual const DBISettingParams* options() const; virtual void begin(); virtual void commit(); virtual void rollback(); virtual DBIRecordset* query( const String &sql, ItemArray* params = 0 ); virtual DBIStatement* prepare( const String &query ); virtual DBIStatement* prepareNamed( const String &name, const String& query ); virtual int64 getLastInsertedId( const String& name = "" ); virtual void selectLimited( const String& query, int64 nBegin, int64 nCount, String& result ); virtual void close(); static void throwError( const char* file, int line, PGresult* res ); PGresult* internal_exec( const String& sql, int64& affectedRows ); PgSQLHandlerRef* getConnRef() const { return m_pConn; } }; class DBIServicePgSQL : public DBIService { public: DBIServicePgSQL() : DBIService( "DBI_pgsql" ) {} virtual void init(); virtual DBIHandle* connect( const String& parameters ); virtual CoreObject* makeInstance( VMachine* vm, DBIHandle* dbh ); }; } // !namespace Falcon extern Falcon::DBIServicePgSQL thePgSQLService; #endif /* PGSQL_MOD_H */ modules/native/dbi/pgsql/version.h000066400000000000000000000011171176363201700175440ustar00rootroot00000000000000/* * FALCON - The Falcon Programming Language. * FILE: version.h * * PgSQL module version information * ------------------------------------------------------------------- * Author: Jeremy Cowgar * Begin: Sun Dec 23 22:05:54 2007 * * ------------------------------------------------------------------- * (C) Copyright 2007: the FALCON developers (see list in AUTHORS file) * * See LICENSE file for licensing details. */ #ifndef VERSION_H #define VERSION_H #define VERSION_MAJOR 0 #define VERSION_MINOR 9 #define VERSION_REVISION 8 #endif /* end of version.h */ modules/native/dbi/sqlite3/000077500000000000000000000000001176363201700161445ustar00rootroot00000000000000modules/native/dbi/sqlite3/CMakeLists.txt000066400000000000000000000033231176363201700207050ustar00rootroot00000000000000#################################################################### # The Falcon Programming language # # CMake configuration file for module sqlite3 #################################################################### if(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) endif(COMMAND cmake_policy) if( WIN32 OR APPLE ) set(_sqlite_default ON) else( WIN32 OR APPLE ) set(_sqlite_default OFF) endif( WIN32 OR APPLE) option(FALCON_DBI_WITH_INTERNAL_SQLITE "Use internal sqlite sources" ${_sqlite_default}) falcon_define_module( FALCON_MODULE sqlite3 ) # Inclusion settings include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include ) set(SRC_FILES sqlite3_fm.cpp sqlite3_mod.cpp sqlite3_ext.cpp sqlite3_srv.cpp ) set(HDR_FILES #Headers for IDE sqlite3_ext.h sqlite3_mod.h version.h ) if(FALCON_DBI_WITH_INTERNAL_SQLITE) list(APPEND SRC_FILES sqlite3.c) list(APPEND HDR_FILES sqlite3.h) set(sqlite_definitions SQLITE_THREADSAFE=1 SQLITE_ENABLE_FTS3 SQLITE_SECURE_DELETE SQLITE_ENABLE_MEMORY_MANAGEMENT # SQLITE_ENABLE_COLUMN_METADATA # adds API that is not wrapped right now ) set_source_files_properties( sqlite.c PROPERTIES COMPILE_DEFINITIONS "${sqlite_definitions}" ) else() find_package(Sqlite REQUIRED) include_directories(${Sqlite_INCLUDE_DIRS}) endif() # Target add_library( ${FALCON_MODULE} MODULE ${dbi_common_files} ${SRC_FILES} ${HDR_FILES} ) #Link target_link_libraries(${FALCON_MODULE} falcon_engine ${Sqlite_LIBRARIES} ) if(NOT WIN32) #Non-win32 builds require pthreads target_link_libraries(${FALCON_MODULE} pthread) endif() falcon_install_module2( ${FALCON_MODULE} dbi ) modules/native/dbi/sqlite3/sqlite3.c000066400000000000000000167677501176363201700177310ustar00rootroot00000000000000/****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite ** version 3.6.23.1. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a one translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements ** of 5% are more are commonly seen when SQLite is compiled as a single ** translation unit. ** ** This file is all you need to compile SQLite. To use SQLite in other ** programs, you need this file and the "sqlite3.h" header file that defines ** the programming interface to the SQLite library. (If you do not have ** the "sqlite3.h" header file at hand, you will find a copy embedded within ** the text of this file. Search for "Begin file sqlite3.h" to find the start ** of the embedded sqlite3.h header file.) Additional code files may be needed ** if you want a wrapper to interface SQLite with your choice of programming ** language. The code for the "sqlite3" command-line shell is also in a ** separate file. This file contains only code for the core SQLite library. */ #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1 #ifndef SQLITE_PRIVATE # define SQLITE_PRIVATE static #endif #ifndef SQLITE_API # define SQLITE_API #endif /************** Begin file sqliteInt.h ***************************************/ /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** Internal interface definitions for SQLite. ** */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ /* ** These #defines should enable >2GB file support on POSIX if the ** underlying operating system supports it. If the OS lacks ** large file support, or if the OS is windows, these should be no-ops. ** ** Ticket #2739: The _LARGEFILE_SOURCE macro must appear before any ** system #includes. Hence, this block of code must be the very first ** code in all source files. ** ** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch ** on the compiler command line. This is necessary if you are compiling ** on a recent machine (ex: Red Hat 7.2) but you want your code to work ** on an older machine (ex: Red Hat 6.0). If you compile on Red Hat 7.2 ** without this option, LFS is enable. But LFS does not exist in the kernel ** in Red Hat 6.0, so the code won't work. Hence, for maximum binary ** portability you should omit LFS. ** ** Similar is true for Mac OS X. LFS is only supported on Mac OS X 9 and later. */ #ifndef SQLITE_DISABLE_LFS # define _LARGE_FILE 1 # ifndef _FILE_OFFSET_BITS # define _FILE_OFFSET_BITS 64 # endif # define _LARGEFILE_SOURCE 1 #endif /* ** Include the configuration header output by 'configure' if we're using the ** autoconf-based build */ #ifdef _HAVE_SQLITE_CONFIG_H #include "config.h" #endif /************** Include sqliteLimit.h in the middle of sqliteInt.h ***********/ /************** Begin file sqliteLimit.h *************************************/ /* ** 2007 May 7 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This file defines various limits of what SQLite can process. */ /* ** The maximum length of a TEXT or BLOB in bytes. This also ** limits the size of a row in a table or index. ** ** The hard limit is the ability of a 32-bit signed integer ** to count the size: 2^31-1 or 2147483647. */ #ifndef SQLITE_MAX_LENGTH # define SQLITE_MAX_LENGTH 1000000000 #endif /* ** This is the maximum number of ** ** * Columns in a table ** * Columns in an index ** * Columns in a view ** * Terms in the SET clause of an UPDATE statement ** * Terms in the result set of a SELECT statement ** * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement. ** * Terms in the VALUES clause of an INSERT statement ** ** The hard upper limit here is 32676. Most database people will ** tell you that in a well-normalized database, you usually should ** not have more than a dozen or so columns in any table. And if ** that is the case, there is no point in having more than a few ** dozen values in any of the other situations described above. */ #ifndef SQLITE_MAX_COLUMN # define SQLITE_MAX_COLUMN 2000 #endif /* ** The maximum length of a single SQL statement in bytes. ** ** It used to be the case that setting this value to zero would ** turn the limit off. That is no longer true. It is not possible ** to turn this limit off. */ #ifndef SQLITE_MAX_SQL_LENGTH # define SQLITE_MAX_SQL_LENGTH 1000000000 #endif /* ** The maximum depth of an expression tree. This is limited to ** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might ** want to place more severe limits on the complexity of an ** expression. ** ** A value of 0 used to mean that the limit was not enforced. ** But that is no longer true. The limit is now strictly enforced ** at all times. */ #ifndef SQLITE_MAX_EXPR_DEPTH # define SQLITE_MAX_EXPR_DEPTH 1000 #endif /* ** The maximum number of terms in a compound SELECT statement. ** The code generator for compound SELECT statements does one ** level of recursion for each term. A stack overflow can result ** if the number of terms is too large. In practice, most SQL ** never has more than 3 or 4 terms. Use a value of 0 to disable ** any limit on the number of terms in a compount SELECT. */ #ifndef SQLITE_MAX_COMPOUND_SELECT # define SQLITE_MAX_COMPOUND_SELECT 500 #endif /* ** The maximum number of opcodes in a VDBE program. ** Not currently enforced. */ #ifndef SQLITE_MAX_VDBE_OP # define SQLITE_MAX_VDBE_OP 25000 #endif /* ** The maximum number of arguments to an SQL function. */ #ifndef SQLITE_MAX_FUNCTION_ARG # define SQLITE_MAX_FUNCTION_ARG 127 #endif /* ** The maximum number of in-memory pages to use for the main database ** table and for temporary tables. The SQLITE_DEFAULT_CACHE_SIZE */ #ifndef SQLITE_DEFAULT_CACHE_SIZE # define SQLITE_DEFAULT_CACHE_SIZE 2000 #endif #ifndef SQLITE_DEFAULT_TEMP_CACHE_SIZE # define SQLITE_DEFAULT_TEMP_CACHE_SIZE 500 #endif /* ** The maximum number of attached databases. This must be between 0 ** and 30. The upper bound on 30 is because a 32-bit integer bitmap ** is used internally to track attached databases. */ #ifndef SQLITE_MAX_ATTACHED # define SQLITE_MAX_ATTACHED 10 #endif /* ** The maximum value of a ?nnn wildcard that the parser will accept. */ #ifndef SQLITE_MAX_VARIABLE_NUMBER # define SQLITE_MAX_VARIABLE_NUMBER 999 #endif /* Maximum page size. The upper bound on this value is 32768. This a limit ** imposed by the necessity of storing the value in a 2-byte unsigned integer ** and the fact that the page size must be a power of 2. ** ** If this limit is changed, then the compiled library is technically ** incompatible with an SQLite library compiled with a different limit. If ** a process operating on a database with a page-size of 65536 bytes ** crashes, then an instance of SQLite compiled with the default page-size ** limit will not be able to rollback the aborted transaction. This could ** lead to database corruption. */ #ifndef SQLITE_MAX_PAGE_SIZE # define SQLITE_MAX_PAGE_SIZE 32768 #endif /* ** The default size of a database page. */ #ifndef SQLITE_DEFAULT_PAGE_SIZE # define SQLITE_DEFAULT_PAGE_SIZE 1024 #endif #if SQLITE_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE # undef SQLITE_DEFAULT_PAGE_SIZE # define SQLITE_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE #endif /* ** Ordinarily, if no value is explicitly provided, SQLite creates databases ** with page size SQLITE_DEFAULT_PAGE_SIZE. However, based on certain ** device characteristics (sector-size and atomic write() support), ** SQLite may choose a larger value. This constant is the maximum value ** SQLite will choose on its own. */ #ifndef SQLITE_MAX_DEFAULT_PAGE_SIZE # define SQLITE_MAX_DEFAULT_PAGE_SIZE 8192 #endif #if SQLITE_MAX_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE # undef SQLITE_MAX_DEFAULT_PAGE_SIZE # define SQLITE_MAX_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE #endif /* ** Maximum number of pages in one database file. ** ** This is really just the default value for the max_page_count pragma. ** This value can be lowered (or raised) at run-time using that the ** max_page_count macro. */ #ifndef SQLITE_MAX_PAGE_COUNT # define SQLITE_MAX_PAGE_COUNT 1073741823 #endif /* ** Maximum length (in bytes) of the pattern in a LIKE or GLOB ** operator. */ #ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH # define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000 #endif /* ** Maximum depth of recursion for triggers. ** ** A value of 1 means that a trigger program will not be able to itself ** fire any triggers. A value of 0 means that no trigger programs at all ** may be executed. */ #ifndef SQLITE_MAX_TRIGGER_DEPTH # define SQLITE_MAX_TRIGGER_DEPTH 1000 #endif /************** End of sqliteLimit.h *****************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /* Disable nuisance warnings on Borland compilers */ #if defined(__BORLANDC__) #pragma warn -rch /* unreachable code */ #pragma warn -ccc /* Condition is always true or false */ #pragma warn -aus /* Assigned value is never used */ #pragma warn -csu /* Comparing signed and unsigned */ #pragma warn -spa /* Suspicious pointer arithmetic */ #endif /* Needed for various definitions... */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif /* ** Include standard header files as necessary */ #ifdef HAVE_STDINT_H #include #endif #ifdef HAVE_INTTYPES_H #include #endif /* ** The number of samples of an index that SQLite takes in order to ** construct a histogram of the table content when running ANALYZE ** and with SQLITE_ENABLE_STAT2 */ #define SQLITE_INDEX_SAMPLES 10 /* ** The following macros are used to cast pointers to integers and ** integers to pointers. The way you do this varies from one compiler ** to the next, so we have developed the following set of #if statements ** to generate appropriate macros for a wide range of compilers. ** ** The correct "ANSI" way to do this is to use the intptr_t type. ** Unfortunately, that typedef is not available on all compilers, or ** if it is available, it requires an #include of specific headers ** that very from one machine to the next. ** ** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on ** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)). ** So we have to define the macros in different ways depending on the ** compiler. */ #if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */ # define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X)) # define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X)) #elif !defined(__GNUC__) /* Works for compilers other than LLVM */ # define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X]) # define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) #elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */ # define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X)) # define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X)) #else /* Generates a warning - but it always works */ # define SQLITE_INT_TO_PTR(X) ((void*)(X)) # define SQLITE_PTR_TO_INT(X) ((int)(X)) #endif /* ** The SQLITE_THREADSAFE macro must be defined as either 0 or 1. ** Older versions of SQLite used an optional THREADSAFE macro. ** We support that for legacy */ #if !defined(SQLITE_THREADSAFE) #if defined(THREADSAFE) # define SQLITE_THREADSAFE THREADSAFE #else # define SQLITE_THREADSAFE 1 #endif #endif /* ** The SQLITE_DEFAULT_MEMSTATUS macro must be defined as either 0 or 1. ** It determines whether or not the features related to ** SQLITE_CONFIG_MEMSTATUS are available by default or not. This value can ** be overridden at runtime using the sqlite3_config() API. */ #if !defined(SQLITE_DEFAULT_MEMSTATUS) # define SQLITE_DEFAULT_MEMSTATUS 1 #endif /* ** Exactly one of the following macros must be defined in order to ** specify which memory allocation subsystem to use. ** ** SQLITE_SYSTEM_MALLOC // Use normal system malloc() ** SQLITE_MEMDEBUG // Debugging version of system malloc() ** ** (Historical note: There used to be several other options, but we've ** pared it down to just these two.) ** ** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as ** the default. */ #if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_MEMDEBUG)>1 # error "At most one of the following compile-time configuration options\ is allows: SQLITE_SYSTEM_MALLOC, SQLITE_MEMDEBUG" #endif #if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_MEMDEBUG)==0 # define SQLITE_SYSTEM_MALLOC 1 #endif /* ** If SQLITE_MALLOC_SOFT_LIMIT is not zero, then try to keep the ** sizes of memory allocations below this value where possible. */ #if !defined(SQLITE_MALLOC_SOFT_LIMIT) # define SQLITE_MALLOC_SOFT_LIMIT 1024 #endif /* ** We need to define _XOPEN_SOURCE as follows in order to enable ** recursive mutexes on most Unix systems. But Mac OS X is different. ** The _XOPEN_SOURCE define causes problems for Mac OS X we are told, ** so it is omitted there. See ticket #2673. ** ** Later we learn that _XOPEN_SOURCE is poorly or incorrectly ** implemented on some systems. So we avoid defining it at all ** if it is already defined or if it is unneeded because we are ** not doing a threadsafe build. Ticket #2681. ** ** See also ticket #2741. */ #if !defined(_XOPEN_SOURCE) && !defined(__DARWIN__) && !defined(__APPLE__) && SQLITE_THREADSAFE # define _XOPEN_SOURCE 500 /* Needed to enable pthread recursive mutexes */ #endif /* ** The TCL headers are only needed when compiling the TCL bindings. */ #if defined(SQLITE_TCL) || defined(TCLSH) # include #endif /* ** Many people are failing to set -DNDEBUG=1 when compiling SQLite. ** Setting NDEBUG makes the code smaller and run faster. So the following ** lines are added to automatically set NDEBUG unless the -DSQLITE_DEBUG=1 ** option is set. Thus NDEBUG becomes an opt-in rather than an opt-out ** feature. */ #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) # define NDEBUG 1 #endif /* ** The testcase() macro is used to aid in coverage testing. When ** doing coverage testing, the condition inside the argument to ** testcase() must be evaluated both true and false in order to ** get full branch coverage. The testcase() macro is inserted ** to help ensure adequate test coverage in places where simple ** condition/decision coverage is inadequate. For example, testcase() ** can be used to make sure boundary values are tested. For ** bitmask tests, testcase() can be used to make sure each bit ** is significant and used at least once. On switch statements ** where multiple cases go to the same block of code, testcase() ** can insure that all cases are evaluated. ** */ #ifdef SQLITE_COVERAGE_TEST SQLITE_PRIVATE void sqlite3Coverage(int); # define testcase(X) if( X ){ sqlite3Coverage(__LINE__); } #else # define testcase(X) #endif /* ** The TESTONLY macro is used to enclose variable declarations or ** other bits of code that are needed to support the arguments ** within testcase() and assert() macros. */ #if !defined(NDEBUG) || defined(SQLITE_COVERAGE_TEST) # define TESTONLY(X) X #else # define TESTONLY(X) #endif /* ** Sometimes we need a small amount of code such as a variable initialization ** to setup for a later assert() statement. We do not want this code to ** appear when assert() is disabled. The following macro is therefore ** used to contain that setup code. The "VVA" acronym stands for ** "Verification, Validation, and Accreditation". In other words, the ** code within VVA_ONLY() will only run during verification processes. */ #ifndef NDEBUG # define VVA_ONLY(X) X #else # define VVA_ONLY(X) #endif /* ** The ALWAYS and NEVER macros surround boolean expressions which ** are intended to always be true or false, respectively. Such ** expressions could be omitted from the code completely. But they ** are included in a few cases in order to enhance the resilience ** of SQLite to unexpected behavior - to make the code "self-healing" ** or "ductile" rather than being "brittle" and crashing at the first ** hint of unplanned behavior. ** ** In other words, ALWAYS and NEVER are added for defensive code. ** ** When doing coverage testing ALWAYS and NEVER are hard-coded to ** be true and false so that the unreachable code then specify will ** not be counted as untested code. */ #if defined(SQLITE_COVERAGE_TEST) # define ALWAYS(X) (1) # define NEVER(X) (0) #elif !defined(NDEBUG) # define ALWAYS(X) ((X)?1:(assert(0),0)) # define NEVER(X) ((X)?(assert(0),1):0) #else # define ALWAYS(X) (X) # define NEVER(X) (X) #endif /* ** The macro unlikely() is a hint that surrounds a boolean ** expression that is usually false. Macro likely() surrounds ** a boolean expression that is usually true. GCC is able to ** use these hints to generate better code, sometimes. */ #if defined(__GNUC__) && 0 # define likely(X) __builtin_expect((X),1) # define unlikely(X) __builtin_expect((X),0) #else # define likely(X) !!(X) # define unlikely(X) !!(X) #endif /************** Include sqlite3.h in the middle of sqliteInt.h ***************/ /************** Begin file sqlite3.h *****************************************/ /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This header file defines the interface that the SQLite library ** presents to client programs. If a C-function, structure, datatype, ** or constant definition does not appear in this file, then it is ** not a published API of SQLite, is subject to change without ** notice, and should not be referenced by programs that use SQLite. ** ** Some of the definitions that are in this file are marked as ** "experimental". Experimental interfaces are normally new ** features recently added to SQLite. We do not anticipate changes ** to experimental interfaces but reserve the right to make minor changes ** if experience from use "in the wild" suggest such changes are prudent. ** ** The official C-language API documentation for SQLite is derived ** from comments in this file. This file is the authoritative source ** on how SQLite interfaces are suppose to operate. ** ** The name of this file under configuration management is "sqlite.h.in". ** The makefile makes some minor changes to this file (such as inserting ** the version number) and changes its name to "sqlite3.h" as ** part of the build process. */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ #include /* Needed for the definition of va_list */ /* ** Make sure we can call this stuff from C++. */ #if 0 extern "C" { #endif /* ** Add the ability to override 'extern' */ #ifndef SQLITE_EXTERN # define SQLITE_EXTERN extern #endif #ifndef SQLITE_API # define SQLITE_API #endif /* ** These no-op macros are used in front of interfaces to mark those ** interfaces as either deprecated or experimental. New applications ** should not use deprecated interfaces - they are support for backwards ** compatibility only. Application writers should be aware that ** experimental interfaces are subject to change in point releases. ** ** These macros used to resolve to various kinds of compiler magic that ** would generate warning messages when they were used. But that ** compiler magic ended up generating such a flurry of bug reports ** that we have taken it all out and gone back to using simple ** noop macros. */ #define SQLITE_DEPRECATED #define SQLITE_EXPERIMENTAL /* ** Ensure these symbols were not defined by some previous header file. */ #ifdef SQLITE_VERSION # undef SQLITE_VERSION #endif #ifdef SQLITE_VERSION_NUMBER # undef SQLITE_VERSION_NUMBER #endif /* ** CAPI3REF: Compile-Time Library Version Numbers ** ** ^(The [SQLITE_VERSION] C preprocessor macro in the sqlite3.h header ** evaluates to a string literal that is the SQLite version in the ** format "X.Y.Z" where X is the major version number (always 3 for ** SQLite3) and Y is the minor version number and Z is the release number.)^ ** ^(The [SQLITE_VERSION_NUMBER] C preprocessor macro resolves to an integer ** with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same ** numbers used in [SQLITE_VERSION].)^ ** The SQLITE_VERSION_NUMBER for any given release of SQLite will also ** be larger than the release from which it is derived. Either Y will ** be held constant and Z will be incremented or else Y will be incremented ** and Z will be reset to zero. ** ** Since version 3.6.18, SQLite source code has been stored in the ** Fossil configuration management ** system. ^The SQLITE_SOURCE_ID macro evalutes to ** a string which identifies a particular check-in of SQLite ** within its configuration management system. ^The SQLITE_SOURCE_ID ** string contains the date and time of the check-in (UTC) and an SHA1 ** hash of the entire source tree. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.6.23.1" #define SQLITE_VERSION_NUMBER 3006023 #define SQLITE_SOURCE_ID "2010-03-26 22:28:06 b078b588d617e07886ad156e9f54ade6d823568e" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version, sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros ** but are associated with the library instead of the header file. ^(Cautious ** programmers might include assert() statements in their application to ** verify that values returned by these interfaces match the macros in ** the header, and thus insure that the application is ** compiled with matching library and header files. ** **
    ** assert( sqlite3_libversion_number()==SQLITE_VERSION_NUMBER );
    ** assert( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)==0 );
    ** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 );
    ** 
    )^ ** ** ^The sqlite3_version[] string constant contains the text of [SQLITE_VERSION] ** macro. ^The sqlite3_libversion() function returns a pointer to the ** to the sqlite3_version[] string constant. The sqlite3_libversion() ** function is provided for use in DLLs since DLL users usually do not have ** direct access to string constants within the DLL. ^The ** sqlite3_libversion_number() function returns an integer equal to ** [SQLITE_VERSION_NUMBER]. ^The sqlite3_sourceid() function returns ** a pointer to a string constant whose value is the same as the ** [SQLITE_SOURCE_ID] C preprocessor macro. ** ** See also: [sqlite_version()] and [sqlite_source_id()]. */ SQLITE_API const char sqlite3_version[] = SQLITE_VERSION; SQLITE_API const char *sqlite3_libversion(void); SQLITE_API const char *sqlite3_sourceid(void); SQLITE_API int sqlite3_libversion_number(void); #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS /* ** CAPI3REF: Run-Time Library Compilation Options Diagnostics ** ** ^The sqlite3_compileoption_used() function returns 0 or 1 ** indicating whether the specified option was defined at ** compile time. ^The SQLITE_ prefix may be omitted from the ** option name passed to sqlite3_compileoption_used(). ** ** ^The sqlite3_compileoption_get() function allows interating ** over the list of options that were defined at compile time by ** returning the N-th compile time option string. ^If N is out of range, ** sqlite3_compileoption_get() returns a NULL pointer. ^The SQLITE_ ** prefix is omitted from any strings returned by ** sqlite3_compileoption_get(). ** ** ^Support for the diagnostic functions sqlite3_compileoption_used() ** and sqlite3_compileoption_get() may be omitted by specifing the ** [SQLITE_OMIT_COMPILEOPTION_DIAGS] option at compile time. ** ** See also: SQL functions [sqlite_compileoption_used()] and ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ SQLITE_API int sqlite3_compileoption_used(const char *zOptName); SQLITE_API const char *sqlite3_compileoption_get(int N); #endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */ /* ** CAPI3REF: Test To See If The Library Is Threadsafe ** ** ^The sqlite3_threadsafe() function returns zero if and only if ** SQLite was compiled mutexing code omitted due to the ** [SQLITE_THREADSAFE] compile-time option being set to 0. ** ** SQLite can be compiled with or without mutexes. When ** the [SQLITE_THREADSAFE] C preprocessor macro is 1 or 2, mutexes ** are enabled and SQLite is threadsafe. When the ** [SQLITE_THREADSAFE] macro is 0, ** the mutexes are omitted. Without the mutexes, it is not safe ** to use SQLite concurrently from more than one thread. ** ** Enabling mutexes incurs a measurable performance penalty. ** So if speed is of utmost importance, it makes sense to disable ** the mutexes. But for maximum safety, mutexes should be enabled. ** ^The default behavior is for mutexes to be enabled. ** ** This interface can be used by an application to make sure that the ** version of SQLite that it is linking against was compiled with ** the desired setting of the [SQLITE_THREADSAFE] macro. ** ** This interface only reports on the compile-time mutex setting ** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with ** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but ** can be fully or partially disabled using a call to [sqlite3_config()] ** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], ** or [SQLITE_CONFIG_MUTEX]. ^(The return value of the ** sqlite3_threadsafe() function shows only the compile-time setting of ** thread safety, not any run-time changes to that setting made by ** sqlite3_config(). In other words, the return value from sqlite3_threadsafe() ** is unchanged by calls to sqlite3_config().)^ ** ** See the [threading mode] documentation for additional information. */ SQLITE_API int sqlite3_threadsafe(void); /* ** CAPI3REF: Database Connection Handle ** KEYWORDS: {database connection} {database connections} ** ** Each open SQLite database is represented by a pointer to an instance of ** the opaque structure named "sqlite3". It is useful to think of an sqlite3 ** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and ** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()] ** is its destructor. There are many other interfaces (such as ** [sqlite3_prepare_v2()], [sqlite3_create_function()], and ** [sqlite3_busy_timeout()] to name but three) that are methods on an ** sqlite3 object. */ typedef struct sqlite3 sqlite3; /* ** CAPI3REF: 64-Bit Integer Types ** KEYWORDS: sqlite_int64 sqlite_uint64 ** ** Because there is no cross-platform way to specify 64-bit integer types ** SQLite includes typedefs for 64-bit signed and unsigned integers. ** ** The sqlite3_int64 and sqlite3_uint64 are the preferred type definitions. ** The sqlite_int64 and sqlite_uint64 types are supported for backwards ** compatibility only. ** ** ^The sqlite3_int64 and sqlite_int64 types can store integer values ** between -9223372036854775808 and +9223372036854775807 inclusive. ^The ** sqlite3_uint64 and sqlite_uint64 types can store integer values ** between 0 and +18446744073709551615 inclusive. */ #ifdef SQLITE_INT64_TYPE typedef SQLITE_INT64_TYPE sqlite_int64; typedef unsigned SQLITE_INT64_TYPE sqlite_uint64; #elif defined(_MSC_VER) || defined(__BORLANDC__) typedef __int64 sqlite_int64; typedef unsigned __int64 sqlite_uint64; #else typedef long long int sqlite_int64; typedef unsigned long long int sqlite_uint64; #endif typedef sqlite_int64 sqlite3_int64; typedef sqlite_uint64 sqlite3_uint64; /* ** If compiling for a processor that lacks floating point support, ** substitute integer for floating-point. */ #ifdef SQLITE_OMIT_FLOATING_POINT # define double sqlite3_int64 #endif /* ** CAPI3REF: Closing A Database Connection ** ** ^The sqlite3_close() routine is the destructor for the [sqlite3] object. ** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is ** successfullly destroyed and all associated resources are deallocated. ** ** Applications must [sqlite3_finalize | finalize] all [prepared statements] ** and [sqlite3_blob_close | close] all [BLOB handles] associated with ** the [sqlite3] object prior to attempting to close the object. ^If ** sqlite3_close() is called on a [database connection] that still has ** outstanding [prepared statements] or [BLOB handles], then it returns ** SQLITE_BUSY. ** ** ^If [sqlite3_close()] is invoked while a transaction is open, ** the transaction is automatically rolled back. ** ** The C parameter to [sqlite3_close(C)] must be either a NULL ** pointer or an [sqlite3] object pointer obtained ** from [sqlite3_open()], [sqlite3_open16()], or ** [sqlite3_open_v2()], and not previously closed. ** ^Calling sqlite3_close() with a NULL pointer argument is a ** harmless no-op. */ SQLITE_API int sqlite3_close(sqlite3 *); /* ** The type for a callback function. ** This is legacy and deprecated. It is included for historical ** compatibility and is not documented. */ typedef int (*sqlite3_callback)(void*,int,char**, char**); /* ** CAPI3REF: One-Step Query Execution Interface ** ** The sqlite3_exec() interface is a convenience wrapper around ** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()], ** that allows an application to run multiple statements of SQL ** without having to use a lot of C code. ** ** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded, ** semicolon-separate SQL statements passed into its 2nd argument, ** in the context of the [database connection] passed in as its 1st ** argument. ^If the callback function of the 3rd argument to ** sqlite3_exec() is not NULL, then it is invoked for each result row ** coming out of the evaluated SQL statements. ^The 4th argument to ** to sqlite3_exec() is relayed through to the 1st argument of each ** callback invocation. ^If the callback pointer to sqlite3_exec() ** is NULL, then no callback is ever invoked and result rows are ** ignored. ** ** ^If an error occurs while evaluating the SQL statements passed into ** sqlite3_exec(), then execution of the current statement stops and ** subsequent statements are skipped. ^If the 5th parameter to sqlite3_exec() ** is not NULL then any error message is written into memory obtained ** from [sqlite3_malloc()] and passed back through the 5th parameter. ** To avoid memory leaks, the application should invoke [sqlite3_free()] ** on error message strings returned through the 5th parameter of ** of sqlite3_exec() after the error message string is no longer needed. ** ^If the 5th parameter to sqlite3_exec() is not NULL and no errors ** occur, then sqlite3_exec() sets the pointer in its 5th parameter to ** NULL before returning. ** ** ^If an sqlite3_exec() callback returns non-zero, the sqlite3_exec() ** routine returns SQLITE_ABORT without invoking the callback again and ** without running any subsequent SQL statements. ** ** ^The 2nd argument to the sqlite3_exec() callback function is the ** number of columns in the result. ^The 3rd argument to the sqlite3_exec() ** callback is an array of pointers to strings obtained as if from ** [sqlite3_column_text()], one for each column. ^If an element of a ** result row is NULL then the corresponding string pointer for the ** sqlite3_exec() callback is a NULL pointer. ^The 4th argument to the ** sqlite3_exec() callback is an array of pointers to strings where each ** entry represents the name of corresponding result column as obtained ** from [sqlite3_column_name()]. ** ** ^If the 2nd parameter to sqlite3_exec() is a NULL pointer, a pointer ** to an empty string, or a pointer that contains only whitespace and/or ** SQL comments, then no SQL statements are evaluated and the database ** is not changed. ** ** Restrictions: ** **
      **
    • The application must insure that the 1st parameter to sqlite3_exec() ** is a valid and open [database connection]. **
    • The application must not close [database connection] specified by ** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. **
    • The application must not modify the SQL statement text passed into ** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. **
    */ SQLITE_API int sqlite3_exec( sqlite3*, /* An open database */ const char *sql, /* SQL to be evaluated */ int (*callback)(void*,int,char**,char**), /* Callback function */ void *, /* 1st argument to callback */ char **errmsg /* Error msg written here */ ); /* ** CAPI3REF: Result Codes ** KEYWORDS: SQLITE_OK {error code} {error codes} ** KEYWORDS: {result code} {result codes} ** ** Many SQLite functions return an integer result code from the set shown ** here in order to indicates success or failure. ** ** New error codes may be added in future versions of SQLite. ** ** See also: [SQLITE_IOERR_READ | extended result codes] */ #define SQLITE_OK 0 /* Successful result */ /* beginning-of-error-codes */ #define SQLITE_ERROR 1 /* SQL error or missing database */ #define SQLITE_INTERNAL 2 /* Internal logic error in SQLite */ #define SQLITE_PERM 3 /* Access permission denied */ #define SQLITE_ABORT 4 /* Callback routine requested an abort */ #define SQLITE_BUSY 5 /* The database file is locked */ #define SQLITE_LOCKED 6 /* A table in the database is locked */ #define SQLITE_NOMEM 7 /* A malloc() failed */ #define SQLITE_READONLY 8 /* Attempt to write a readonly database */ #define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/ #define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ #define SQLITE_CORRUPT 11 /* The database disk image is malformed */ #define SQLITE_NOTFOUND 12 /* NOT USED. Table or record not found */ #define SQLITE_FULL 13 /* Insertion failed because database is full */ #define SQLITE_CANTOPEN 14 /* Unable to open the database file */ #define SQLITE_PROTOCOL 15 /* NOT USED. Database lock protocol error */ #define SQLITE_EMPTY 16 /* Database is empty */ #define SQLITE_SCHEMA 17 /* The database schema changed */ #define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */ #define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */ #define SQLITE_MISMATCH 20 /* Data type mismatch */ #define SQLITE_MISUSE 21 /* Library used incorrectly */ #define SQLITE_NOLFS 22 /* Uses OS features not supported on host */ #define SQLITE_AUTH 23 /* Authorization denied */ #define SQLITE_FORMAT 24 /* Auxiliary database format error */ #define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */ #define SQLITE_NOTADB 26 /* File opened that is not a database file */ #define SQLITE_ROW 100 /* sqlite3_step() has another row ready */ #define SQLITE_DONE 101 /* sqlite3_step() has finished executing */ /* end-of-error-codes */ /* ** CAPI3REF: Extended Result Codes ** KEYWORDS: {extended error code} {extended error codes} ** KEYWORDS: {extended result code} {extended result codes} ** ** In its default configuration, SQLite API routines return one of 26 integer ** [SQLITE_OK | result codes]. However, experience has shown that many of ** these result codes are too coarse-grained. They do not provide as ** much information about problems as programmers might like. In an effort to ** address this, newer versions of SQLite (version 3.3.8 and later) include ** support for additional result codes that provide more detailed information ** about errors. The extended result codes are enabled or disabled ** on a per database connection basis using the ** [sqlite3_extended_result_codes()] API. ** ** Some of the available extended result codes are listed here. ** One may expect the number of extended result codes will be expand ** over time. Software that uses extended result codes should expect ** to see new result codes in future releases of SQLite. ** ** The SQLITE_OK result code will never be extended. It will always ** be exactly zero. */ #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8)) #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8)) #define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8)) #define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8)) #define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8)) #define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6<<8)) #define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7<<8)) #define SQLITE_IOERR_UNLOCK (SQLITE_IOERR | (8<<8)) #define SQLITE_IOERR_RDLOCK (SQLITE_IOERR | (9<<8)) #define SQLITE_IOERR_DELETE (SQLITE_IOERR | (10<<8)) #define SQLITE_IOERR_BLOCKED (SQLITE_IOERR | (11<<8)) #define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12<<8)) #define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13<<8)) #define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14<<8)) #define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15<<8)) #define SQLITE_IOERR_CLOSE (SQLITE_IOERR | (16<<8)) #define SQLITE_IOERR_DIR_CLOSE (SQLITE_IOERR | (17<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8) ) /* ** CAPI3REF: Flags For File Open Operations ** ** These bit values are intended for use in the ** 3rd parameter to the [sqlite3_open_v2()] interface and ** in the 4th parameter to the xOpen method of the ** [sqlite3_vfs] object. */ #define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_CREATE 0x00000004 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_DELETEONCLOSE 0x00000008 /* VFS only */ #define SQLITE_OPEN_EXCLUSIVE 0x00000010 /* VFS only */ #define SQLITE_OPEN_AUTOPROXY 0x00000020 /* VFS only */ #define SQLITE_OPEN_MAIN_DB 0x00000100 /* VFS only */ #define SQLITE_OPEN_TEMP_DB 0x00000200 /* VFS only */ #define SQLITE_OPEN_TRANSIENT_DB 0x00000400 /* VFS only */ #define SQLITE_OPEN_MAIN_JOURNAL 0x00000800 /* VFS only */ #define SQLITE_OPEN_TEMP_JOURNAL 0x00001000 /* VFS only */ #define SQLITE_OPEN_SUBJOURNAL 0x00002000 /* VFS only */ #define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */ #define SQLITE_OPEN_NOMUTEX 0x00008000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ /* ** CAPI3REF: Device Characteristics ** ** The xDeviceCapabilities method of the [sqlite3_io_methods] ** object returns an integer which is a vector of the these ** bit values expressing I/O characteristics of the mass storage ** device that holds the file that the [sqlite3_io_methods] ** refers to. ** ** The SQLITE_IOCAP_ATOMIC property means that all writes of ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values ** mean that writes of blocks that are nnn bytes in size and ** are aligned to an address which is an integer multiple of ** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means ** that when data is appended to a file, the data is appended ** first then the size of the file is extended, never the other ** way around. The SQLITE_IOCAP_SEQUENTIAL property means that ** information is written to disk in the same order as calls ** to xWrite(). */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 #define SQLITE_IOCAP_ATOMIC1K 0x00000004 #define SQLITE_IOCAP_ATOMIC2K 0x00000008 #define SQLITE_IOCAP_ATOMIC4K 0x00000010 #define SQLITE_IOCAP_ATOMIC8K 0x00000020 #define SQLITE_IOCAP_ATOMIC16K 0x00000040 #define SQLITE_IOCAP_ATOMIC32K 0x00000080 #define SQLITE_IOCAP_ATOMIC64K 0x00000100 #define SQLITE_IOCAP_SAFE_APPEND 0x00000200 #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. */ #define SQLITE_LOCK_NONE 0 #define SQLITE_LOCK_SHARED 1 #define SQLITE_LOCK_RESERVED 2 #define SQLITE_LOCK_PENDING 3 #define SQLITE_LOCK_EXCLUSIVE 4 /* ** CAPI3REF: Synchronization Type Flags ** ** When SQLite invokes the xSync() method of an ** [sqlite3_io_methods] object it uses a combination of ** these integer values as the second argument. ** ** When the SQLITE_SYNC_DATAONLY flag is used, it means that the ** sync operation only needs to flush data to mass storage. Inode ** information need not be flushed. If the lower four bits of the flag ** equal SQLITE_SYNC_NORMAL, that means to use normal fsync() semantics. ** If the lower four bits equal SQLITE_SYNC_FULL, that means ** to use Mac OS X style fullsync instead of fsync(). */ #define SQLITE_SYNC_NORMAL 0x00002 #define SQLITE_SYNC_FULL 0x00003 #define SQLITE_SYNC_DATAONLY 0x00010 /* ** CAPI3REF: OS Interface Open File Handle ** ** An [sqlite3_file] object represents an open file in the ** [sqlite3_vfs | OS interface layer]. Individual OS interface ** implementations will ** want to subclass this object by appending additional fields ** for their own use. The pMethods entry is a pointer to an ** [sqlite3_io_methods] object that defines methods for performing ** I/O operations on the open file. */ typedef struct sqlite3_file sqlite3_file; struct sqlite3_file { const struct sqlite3_io_methods *pMethods; /* Methods for an open file */ }; /* ** CAPI3REF: OS Interface File Virtual Methods Object ** ** Every file opened by the [sqlite3_vfs] xOpen method populates an ** [sqlite3_file] object (or, more commonly, a subclass of the ** [sqlite3_file] object) with a pointer to an instance of this object. ** This object defines the methods used to perform various operations ** against the open file represented by the [sqlite3_file] object. ** ** If the xOpen method sets the sqlite3_file.pMethods element ** to a non-NULL pointer, then the sqlite3_io_methods.xClose method ** may be invoked even if the xOpen reported that it failed. The ** only way to prevent a call to xClose following a failed xOpen ** is for the xOpen to set the sqlite3_file.pMethods element to NULL. ** ** The flags argument to xSync may be one of [SQLITE_SYNC_NORMAL] or ** [SQLITE_SYNC_FULL]. The first choice is the normal fsync(). ** The second choice is a Mac OS X style fullsync. The [SQLITE_SYNC_DATAONLY] ** flag may be ORed in to indicate that only the data of the file ** and not its inode needs to be synced. ** ** The integer values to xLock() and xUnlock() are one of **
      **
    • [SQLITE_LOCK_NONE], **
    • [SQLITE_LOCK_SHARED], **
    • [SQLITE_LOCK_RESERVED], **
    • [SQLITE_LOCK_PENDING], or **
    • [SQLITE_LOCK_EXCLUSIVE]. **
    ** xLock() increases the lock. xUnlock() decreases the lock. ** The xCheckReservedLock() method checks whether any database connection, ** either in this process or in some other process, is holding a RESERVED, ** PENDING, or EXCLUSIVE lock on the file. It returns true ** if such a lock exists and false otherwise. ** ** The xFileControl() method is a generic interface that allows custom ** VFS implementations to directly control an open file using the ** [sqlite3_file_control()] interface. The second "op" argument is an ** integer opcode. The third argument is a generic pointer intended to ** point to a structure that may contain arguments or space in which to ** write return values. Potential uses for xFileControl() might be ** functions to enable blocking locks with timeouts, to change the ** locking strategy (for example to use dot-file locks), to inquire ** about the status of a lock, or to break stale locks. The SQLite ** core reserves all opcodes less than 100 for its own use. ** A [SQLITE_FCNTL_LOCKSTATE | list of opcodes] less than 100 is available. ** Applications that define a custom xFileControl method should use opcodes ** greater than 100 to avoid conflicts. ** ** The xSectorSize() method returns the sector size of the ** device that underlies the file. The sector size is the ** minimum write that can be performed without disturbing ** other bytes in the file. The xDeviceCharacteristics() ** method returns a bit vector describing behaviors of the ** underlying device: ** **
      **
    • [SQLITE_IOCAP_ATOMIC] **
    • [SQLITE_IOCAP_ATOMIC512] **
    • [SQLITE_IOCAP_ATOMIC1K] **
    • [SQLITE_IOCAP_ATOMIC2K] **
    • [SQLITE_IOCAP_ATOMIC4K] **
    • [SQLITE_IOCAP_ATOMIC8K] **
    • [SQLITE_IOCAP_ATOMIC16K] **
    • [SQLITE_IOCAP_ATOMIC32K] **
    • [SQLITE_IOCAP_ATOMIC64K] **
    • [SQLITE_IOCAP_SAFE_APPEND] **
    • [SQLITE_IOCAP_SEQUENTIAL] **
    ** ** The SQLITE_IOCAP_ATOMIC property means that all writes of ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values ** mean that writes of blocks that are nnn bytes in size and ** are aligned to an address which is an integer multiple of ** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means ** that when data is appended to a file, the data is appended ** first then the size of the file is extended, never the other ** way around. The SQLITE_IOCAP_SEQUENTIAL property means that ** information is written to disk in the same order as calls ** to xWrite(). ** ** If xRead() returns SQLITE_IOERR_SHORT_READ it must also fill ** in the unread portions of the buffer with zeros. A VFS that ** fails to zero-fill short reads might seem to work. However, ** failure to zero-fill short reads will eventually lead to ** database corruption. */ typedef struct sqlite3_io_methods sqlite3_io_methods; struct sqlite3_io_methods { int iVersion; int (*xClose)(sqlite3_file*); int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite3_int64 iOfst); int (*xTruncate)(sqlite3_file*, sqlite3_int64 size); int (*xSync)(sqlite3_file*, int flags); int (*xFileSize)(sqlite3_file*, sqlite3_int64 *pSize); int (*xLock)(sqlite3_file*, int); int (*xUnlock)(sqlite3_file*, int); int (*xCheckReservedLock)(sqlite3_file*, int *pResOut); int (*xFileControl)(sqlite3_file*, int op, void *pArg); int (*xSectorSize)(sqlite3_file*); int (*xDeviceCharacteristics)(sqlite3_file*); /* Additional methods may be added in future releases */ }; /* ** CAPI3REF: Standard File Control Opcodes ** ** These integer constants are opcodes for the xFileControl method ** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()] ** interface. ** ** The [SQLITE_FCNTL_LOCKSTATE] opcode is used for debugging. This ** opcode causes the xFileControl method to write the current state of ** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED], ** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE]) ** into an integer that the pArg argument points to. This capability ** is used during testing and only needs to be supported when SQLITE_TEST ** is defined. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_SET_LOCKPROXYFILE 3 #define SQLITE_LAST_ERRNO 4 /* ** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks ** at the internal representation of an [sqlite3_mutex]. It only ** deals with pointers to the [sqlite3_mutex] object. ** ** Mutexes are created using [sqlite3_mutex_alloc()]. */ typedef struct sqlite3_mutex sqlite3_mutex; /* ** CAPI3REF: OS Interface Object ** ** An instance of the sqlite3_vfs object defines the interface between ** the SQLite core and the underlying operating system. The "vfs" ** in the name of the object stands for "virtual file system". ** ** The value of the iVersion field is initially 1 but may be larger in ** future versions of SQLite. Additional fields may be appended to this ** object when the iVersion value is increased. Note that the structure ** of the sqlite3_vfs object changes in the transaction between ** SQLite version 3.5.9 and 3.6.0 and yet the iVersion field was not ** modified. ** ** The szOsFile field is the size of the subclassed [sqlite3_file] ** structure used by this VFS. mxPathname is the maximum length of ** a pathname in this VFS. ** ** Registered sqlite3_vfs objects are kept on a linked list formed by ** the pNext pointer. The [sqlite3_vfs_register()] ** and [sqlite3_vfs_unregister()] interfaces manage this list ** in a thread-safe way. The [sqlite3_vfs_find()] interface ** searches the list. Neither the application code nor the VFS ** implementation should use the pNext pointer. ** ** The pNext field is the only field in the sqlite3_vfs ** structure that SQLite will ever modify. SQLite will only access ** or modify this field while holding a particular static mutex. ** The application should never modify anything within the sqlite3_vfs ** object once the object has been registered. ** ** The zName field holds the name of the VFS module. The name must ** be unique across all VFS modules. ** ** SQLite will guarantee that the zFilename parameter to xOpen ** is either a NULL pointer or string obtained ** from xFullPathname(). SQLite further guarantees that ** the string will be valid and unchanged until xClose() is ** called. Because of the previous sentence, ** the [sqlite3_file] can safely store a pointer to the ** filename if it needs to remember the filename for some reason. ** If the zFilename parameter is xOpen is a NULL pointer then xOpen ** must invent its own temporary name for the file. Whenever the ** xFilename parameter is NULL it will also be the case that the ** flags parameter will include [SQLITE_OPEN_DELETEONCLOSE]. ** ** The flags argument to xOpen() includes all bits set in ** the flags argument to [sqlite3_open_v2()]. Or if [sqlite3_open()] ** or [sqlite3_open16()] is used, then flags includes at least ** [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]. ** If xOpen() opens a file read-only then it sets *pOutFlags to ** include [SQLITE_OPEN_READONLY]. Other bits in *pOutFlags may be set. ** ** SQLite will also add one of the following flags to the xOpen() ** call, depending on the object being opened: ** **
      **
    • [SQLITE_OPEN_MAIN_DB] **
    • [SQLITE_OPEN_MAIN_JOURNAL] **
    • [SQLITE_OPEN_TEMP_DB] **
    • [SQLITE_OPEN_TEMP_JOURNAL] **
    • [SQLITE_OPEN_TRANSIENT_DB] **
    • [SQLITE_OPEN_SUBJOURNAL] **
    • [SQLITE_OPEN_MASTER_JOURNAL] **
    ** ** The file I/O implementation can use the object type flags to ** change the way it deals with files. For example, an application ** that does not care about crash recovery or rollback might make ** the open of a journal file a no-op. Writes to this journal would ** also be no-ops, and any attempt to read the journal would return ** SQLITE_IOERR. Or the implementation might recognize that a database ** file will be doing page-aligned sector reads and writes in a random ** order and set up its I/O subsystem accordingly. ** ** SQLite might also add one of the following flags to the xOpen method: ** **
      **
    • [SQLITE_OPEN_DELETEONCLOSE] **
    • [SQLITE_OPEN_EXCLUSIVE] **
    ** ** The [SQLITE_OPEN_DELETEONCLOSE] flag means the file should be ** deleted when it is closed. The [SQLITE_OPEN_DELETEONCLOSE] ** will be set for TEMP databases, journals and for subjournals. ** ** The [SQLITE_OPEN_EXCLUSIVE] flag is always used in conjunction ** with the [SQLITE_OPEN_CREATE] flag, which are both directly ** analogous to the O_EXCL and O_CREAT flags of the POSIX open() ** API. The SQLITE_OPEN_EXCLUSIVE flag, when paired with the ** SQLITE_OPEN_CREATE, is used to indicate that file should always ** be created, and that it is an error if it already exists. ** It is not used to indicate the file should be opened ** for exclusive access. ** ** At least szOsFile bytes of memory are allocated by SQLite ** to hold the [sqlite3_file] structure passed as the third ** argument to xOpen. The xOpen method does not have to ** allocate the structure; it should just fill it in. Note that ** the xOpen method must set the sqlite3_file.pMethods to either ** a valid [sqlite3_io_methods] object or to NULL. xOpen must do ** this even if the open fails. SQLite expects that the sqlite3_file.pMethods ** element will be valid after xOpen returns regardless of the success ** or failure of the xOpen call. ** ** The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS] ** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to ** test whether a file is readable and writable, or [SQLITE_ACCESS_READ] ** to test whether a file is at least readable. The file can be a ** directory. ** ** SQLite will always allocate at least mxPathname+1 bytes for the ** output buffer xFullPathname. The exact size of the output buffer ** is also passed as a parameter to both methods. If the output buffer ** is not large enough, [SQLITE_CANTOPEN] should be returned. Since this is ** handled as a fatal error by SQLite, vfs implementations should endeavor ** to prevent this by setting mxPathname to a sufficiently large value. ** ** The xRandomness(), xSleep(), and xCurrentTime() interfaces ** are not strictly a part of the filesystem, but they are ** included in the VFS structure for completeness. ** The xRandomness() function attempts to return nBytes bytes ** of good-quality randomness into zOut. The return value is ** the actual number of bytes of randomness obtained. ** The xSleep() method causes the calling thread to sleep for at ** least the number of microseconds given. The xCurrentTime() ** method returns a Julian Day Number for the current date and time. ** */ typedef struct sqlite3_vfs sqlite3_vfs; struct sqlite3_vfs { int iVersion; /* Structure version number */ int szOsFile; /* Size of subclassed sqlite3_file */ int mxPathname; /* Maximum file pathname length */ sqlite3_vfs *pNext; /* Next registered VFS */ const char *zName; /* Name of this virtual file system */ void *pAppData; /* Pointer to application-specific data */ int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*, int flags, int *pOutFlags); int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir); int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut); int (*xFullPathname)(sqlite3_vfs*, const char *zName, int nOut, char *zOut); void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename); void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg); void (*(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol))(void); void (*xDlClose)(sqlite3_vfs*, void*); int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut); int (*xSleep)(sqlite3_vfs*, int microseconds); int (*xCurrentTime)(sqlite3_vfs*, double*); int (*xGetLastError)(sqlite3_vfs*, int, char *); /* New fields may be appended in figure versions. The iVersion ** value will increment whenever this happens. */ }; /* ** CAPI3REF: Flags for the xAccess VFS method ** ** These integer constants can be used as the third parameter to ** the xAccess method of an [sqlite3_vfs] object. They determine ** what kind of permissions the xAccess method is looking for. ** With SQLITE_ACCESS_EXISTS, the xAccess method ** simply checks whether the file exists. ** With SQLITE_ACCESS_READWRITE, the xAccess method ** checks whether the file is both readable and writable. ** With SQLITE_ACCESS_READ, the xAccess method ** checks whether the file is readable. */ #define SQLITE_ACCESS_EXISTS 0 #define SQLITE_ACCESS_READWRITE 1 #define SQLITE_ACCESS_READ 2 /* ** CAPI3REF: Initialize The SQLite Library ** ** ^The sqlite3_initialize() routine initializes the ** SQLite library. ^The sqlite3_shutdown() routine ** deallocates any resources that were allocated by sqlite3_initialize(). ** These routines are designed to aid in process initialization and ** shutdown on embedded systems. Workstation applications using ** SQLite normally do not need to invoke either of these routines. ** ** A call to sqlite3_initialize() is an "effective" call if it is ** the first time sqlite3_initialize() is invoked during the lifetime of ** the process, or if it is the first time sqlite3_initialize() is invoked ** following a call to sqlite3_shutdown(). ^(Only an effective call ** of sqlite3_initialize() does any initialization. All other calls ** are harmless no-ops.)^ ** ** A call to sqlite3_shutdown() is an "effective" call if it is the first ** call to sqlite3_shutdown() since the last sqlite3_initialize(). ^(Only ** an effective call to sqlite3_shutdown() does any deinitialization. ** All other valid calls to sqlite3_shutdown() are harmless no-ops.)^ ** ** The sqlite3_initialize() interface is threadsafe, but sqlite3_shutdown() ** is not. The sqlite3_shutdown() interface must only be called from a ** single thread. All open [database connections] must be closed and all ** other SQLite resources must be deallocated prior to invoking ** sqlite3_shutdown(). ** ** Among other things, ^sqlite3_initialize() will invoke ** sqlite3_os_init(). Similarly, ^sqlite3_shutdown() ** will invoke sqlite3_os_end(). ** ** ^The sqlite3_initialize() routine returns [SQLITE_OK] on success. ** ^If for some reason, sqlite3_initialize() is unable to initialize ** the library (perhaps it is unable to allocate a needed resource such ** as a mutex) it returns an [error code] other than [SQLITE_OK]. ** ** ^The sqlite3_initialize() routine is called internally by many other ** SQLite interfaces so that an application usually does not need to ** invoke sqlite3_initialize() directly. For example, [sqlite3_open()] ** calls sqlite3_initialize() so the SQLite library will be automatically ** initialized when [sqlite3_open()] is called if it has not be initialized ** already. ^However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT] ** compile-time option, then the automatic calls to sqlite3_initialize() ** are omitted and the application must call sqlite3_initialize() directly ** prior to using any other SQLite interface. For maximum portability, ** it is recommended that applications always invoke sqlite3_initialize() ** directly prior to using any other SQLite interface. Future releases ** of SQLite may require this. In other words, the behavior exhibited ** when SQLite is compiled with [SQLITE_OMIT_AUTOINIT] might become the ** default behavior in some future release of SQLite. ** ** The sqlite3_os_init() routine does operating-system specific ** initialization of the SQLite library. The sqlite3_os_end() ** routine undoes the effect of sqlite3_os_init(). Typical tasks ** performed by these routines include allocation or deallocation ** of static resources, initialization of global variables, ** setting up a default [sqlite3_vfs] module, or setting up ** a default configuration using [sqlite3_config()]. ** ** The application should never invoke either sqlite3_os_init() ** or sqlite3_os_end() directly. The application should only invoke ** sqlite3_initialize() and sqlite3_shutdown(). The sqlite3_os_init() ** interface is called automatically by sqlite3_initialize() and ** sqlite3_os_end() is called by sqlite3_shutdown(). Appropriate ** implementations for sqlite3_os_init() and sqlite3_os_end() ** are built into SQLite when it is compiled for Unix, Windows, or OS/2. ** When [custom builds | built for other platforms] ** (using the [SQLITE_OS_OTHER=1] compile-time ** option) the application must supply a suitable implementation for ** sqlite3_os_init() and sqlite3_os_end(). An application-supplied ** implementation of sqlite3_os_init() or sqlite3_os_end() ** must return [SQLITE_OK] on success and some other [error code] upon ** failure. */ SQLITE_API int sqlite3_initialize(void); SQLITE_API int sqlite3_shutdown(void); SQLITE_API int sqlite3_os_init(void); SQLITE_API int sqlite3_os_end(void); /* ** CAPI3REF: Configuring The SQLite Library ** ** The sqlite3_config() interface is used to make global configuration ** changes to SQLite in order to tune SQLite to the specific needs of ** the application. The default configuration is recommended for most ** applications and so this routine is usually not necessary. It is ** provided to support rare applications with unusual needs. ** ** The sqlite3_config() interface is not threadsafe. The application ** must insure that no other SQLite interfaces are invoked by other ** threads while sqlite3_config() is running. Furthermore, sqlite3_config() ** may only be invoked prior to library initialization using ** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. ** ^If sqlite3_config() is called after [sqlite3_initialize()] and before ** [sqlite3_shutdown()] then it will return SQLITE_MISUSE. ** Note, however, that ^sqlite3_config() can be called as part of the ** implementation of an application-defined [sqlite3_os_init()]. ** ** The first argument to sqlite3_config() is an integer ** [SQLITE_CONFIG_SINGLETHREAD | configuration option] that determines ** what property of SQLite is to be configured. Subsequent arguments ** vary depending on the [SQLITE_CONFIG_SINGLETHREAD | configuration option] ** in the first argument. ** ** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. ** ^If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_config(int, ...); /* ** CAPI3REF: Configure database connections ** EXPERIMENTAL ** ** The sqlite3_db_config() interface is used to make configuration ** changes to a [database connection]. The interface is similar to ** [sqlite3_config()] except that the changes apply to a single ** [database connection] (specified in the first argument). The ** sqlite3_db_config() interface should only be used immediately after ** the database connection is created using [sqlite3_open()], ** [sqlite3_open16()], or [sqlite3_open_v2()]. ** ** The second argument to sqlite3_db_config(D,V,...) is the ** configuration verb - an integer code that indicates what ** aspect of the [database connection] is being configured. ** The only choice for this value is [SQLITE_DBCONFIG_LOOKASIDE]. ** New verbs are likely to be added in future releases of SQLite. ** Additional arguments depend on the verb. ** ** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if ** the call is considered successful. */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_db_config(sqlite3*, int op, ...); /* ** CAPI3REF: Memory Allocation Routines ** EXPERIMENTAL ** ** An instance of this object defines the interface between SQLite ** and low-level memory allocation routines. ** ** This object is used in only one place in the SQLite interface. ** A pointer to an instance of this object is the argument to ** [sqlite3_config()] when the configuration option is ** [SQLITE_CONFIG_MALLOC] or [SQLITE_CONFIG_GETMALLOC]. ** By creating an instance of this object ** and passing it to [sqlite3_config]([SQLITE_CONFIG_MALLOC]) ** during configuration, an application can specify an alternative ** memory allocation subsystem for SQLite to use for all of its ** dynamic memory needs. ** ** Note that SQLite comes with several [built-in memory allocators] ** that are perfectly adequate for the overwhelming majority of applications ** and that this object is only useful to a tiny minority of applications ** with specialized memory allocation requirements. This object is ** also used during testing of SQLite in order to specify an alternative ** memory allocator that simulates memory out-of-memory conditions in ** order to verify that SQLite recovers gracefully from such ** conditions. ** ** The xMalloc and xFree methods must work like the ** malloc() and free() functions from the standard C library. ** The xRealloc method must work like realloc() from the standard C library ** with the exception that if the second argument to xRealloc is zero, ** xRealloc must be a no-op - it must not perform any allocation or ** deallocation. ^SQLite guarantees that the second argument to ** xRealloc is always a value returned by a prior call to xRoundup. ** And so in cases where xRoundup always returns a positive number, ** xRealloc can perform exactly as the standard library realloc() and ** still be in compliance with this specification. ** ** xSize should return the allocated size of a memory allocation ** previously obtained from xMalloc or xRealloc. The allocated size ** is always at least as big as the requested size but may be larger. ** ** The xRoundup method returns what would be the allocated size of ** a memory allocation given a particular requested size. Most memory ** allocators round up memory allocations at least to the next multiple ** of 8. Some allocators round up to a larger multiple or to a power of 2. ** Every memory allocation request coming in through [sqlite3_malloc()] ** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0, ** that causes the corresponding memory allocation to fail. ** ** The xInit method initializes the memory allocator. (For example, ** it might allocate any require mutexes or initialize internal data ** structures. The xShutdown method is invoked (indirectly) by ** [sqlite3_shutdown()] and should deallocate any resources acquired ** by xInit. The pAppData pointer is used as the only parameter to ** xInit and xShutdown. ** ** SQLite holds the [SQLITE_MUTEX_STATIC_MASTER] mutex when it invokes ** the xInit method, so the xInit method need not be threadsafe. The ** xShutdown method is only called from [sqlite3_shutdown()] so it does ** not need to be threadsafe either. For all other methods, SQLite ** holds the [SQLITE_MUTEX_STATIC_MEM] mutex as long as the ** [SQLITE_CONFIG_MEMSTATUS] configuration option is turned on (which ** it is by default) and so the methods are automatically serialized. ** However, if [SQLITE_CONFIG_MEMSTATUS] is disabled, then the other ** methods must be threadsafe or else make their own arrangements for ** serialization. ** ** SQLite will never invoke xInit() more than once without an intervening ** call to xShutdown(). */ typedef struct sqlite3_mem_methods sqlite3_mem_methods; struct sqlite3_mem_methods { void *(*xMalloc)(int); /* Memory allocation function */ void (*xFree)(void*); /* Free a prior allocation */ void *(*xRealloc)(void*,int); /* Resize an allocation */ int (*xSize)(void*); /* Return the size of an allocation */ int (*xRoundup)(int); /* Round up request size to allocation size */ int (*xInit)(void*); /* Initialize the memory allocator */ void (*xShutdown)(void*); /* Deinitialize the memory allocator */ void *pAppData; /* Argument to xInit() and xShutdown() */ }; /* ** CAPI3REF: Configuration Options ** EXPERIMENTAL ** ** These constants are the available integer configuration options that ** can be passed as the first argument to the [sqlite3_config()] interface. ** ** New configuration options may be added in future releases of SQLite. ** Existing configuration options might be discontinued. Applications ** should check the return code from [sqlite3_config()] to make sure that ** the call worked. The [sqlite3_config()] interface will return a ** non-zero [error code] if a discontinued or unsupported configuration option ** is invoked. ** **
    **
    SQLITE_CONFIG_SINGLETHREAD
    **
    There are no arguments to this option. ^This option sets the ** [threading mode] to Single-thread. In other words, it disables ** all mutexing and puts SQLite into a mode where it can only be used ** by a single thread. ^If SQLite is compiled with ** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then ** it is not possible to change the [threading mode] from its default ** value of Single-thread and so [sqlite3_config()] will return ** [SQLITE_ERROR] if called with the SQLITE_CONFIG_SINGLETHREAD ** configuration option.
    ** **
    SQLITE_CONFIG_MULTITHREAD
    **
    There are no arguments to this option. ^This option sets the ** [threading mode] to Multi-thread. In other words, it disables ** mutexing on [database connection] and [prepared statement] objects. ** The application is responsible for serializing access to ** [database connections] and [prepared statements]. But other mutexes ** are enabled so that SQLite will be safe to use in a multi-threaded ** environment as long as no two threads attempt to use the same ** [database connection] at the same time. ^If SQLite is compiled with ** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then ** it is not possible to set the Multi-thread [threading mode] and ** [sqlite3_config()] will return [SQLITE_ERROR] if called with the ** SQLITE_CONFIG_MULTITHREAD configuration option.
    ** **
    SQLITE_CONFIG_SERIALIZED
    **
    There are no arguments to this option. ^This option sets the ** [threading mode] to Serialized. In other words, this option enables ** all mutexes including the recursive ** mutexes on [database connection] and [prepared statement] objects. ** In this mode (which is the default when SQLite is compiled with ** [SQLITE_THREADSAFE=1]) the SQLite library will itself serialize access ** to [database connections] and [prepared statements] so that the ** application is free to use the same [database connection] or the ** same [prepared statement] in different threads at the same time. ** ^If SQLite is compiled with ** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then ** it is not possible to set the Serialized [threading mode] and ** [sqlite3_config()] will return [SQLITE_ERROR] if called with the ** SQLITE_CONFIG_SERIALIZED configuration option.
    ** **
    SQLITE_CONFIG_MALLOC
    **
    ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mem_methods] structure. The argument specifies ** alternative low-level memory allocation routines to be used in place of ** the memory allocation routines built into SQLite.)^ ^SQLite makes ** its own private copy of the content of the [sqlite3_mem_methods] structure ** before the [sqlite3_config()] call returns.
    ** **
    SQLITE_CONFIG_GETMALLOC
    **
    ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mem_methods] structure. The [sqlite3_mem_methods] ** structure is filled with the currently defined memory allocation routines.)^ ** This option can be used to overload the default memory allocation ** routines with a wrapper that simulations memory allocation failure or ** tracks memory usage, for example.
    ** **
    SQLITE_CONFIG_MEMSTATUS
    **
    ^This option takes single argument of type int, interpreted as a ** boolean, which enables or disables the collection of memory allocation ** statistics. ^(When memory allocation statistics are disabled, the ** following SQLite interfaces become non-operational: **
      **
    • [sqlite3_memory_used()] **
    • [sqlite3_memory_highwater()] **
    • [sqlite3_soft_heap_limit()] **
    • [sqlite3_status()] **
    )^ ** ^Memory allocation statistics are enabled by default unless SQLite is ** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory ** allocation statistics are disabled by default. **
    ** **
    SQLITE_CONFIG_SCRATCH
    **
    ^This option specifies a static memory buffer that SQLite can use for ** scratch memory. There are three arguments: A pointer an 8-byte ** aligned memory buffer from which the scrach allocations will be ** drawn, the size of each scratch allocation (sz), ** and the maximum number of scratch allocations (N). The sz ** argument must be a multiple of 16. The sz parameter should be a few bytes ** larger than the actual scratch space required due to internal overhead. ** The first argument must be a pointer to an 8-byte aligned buffer ** of at least sz*N bytes of memory. ** ^SQLite will use no more than one scratch buffer per thread. So ** N should be set to the expected maximum number of threads. ^SQLite will ** never require a scratch buffer that is more than 6 times the database ** page size. ^If SQLite needs needs additional scratch memory beyond ** what is provided by this configuration option, then ** [sqlite3_malloc()] will be used to obtain the memory needed.
    ** **
    SQLITE_CONFIG_PAGECACHE
    **
    ^This option specifies a static memory buffer that SQLite can use for ** the database page cache with the default page cache implemenation. ** This configuration should not be used if an application-define page ** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option. ** There are three arguments to this option: A pointer to 8-byte aligned ** memory, the size of each page buffer (sz), and the number of pages (N). ** The sz argument should be the size of the largest database page ** (a power of two between 512 and 32768) plus a little extra for each ** page header. ^The page header size is 20 to 40 bytes depending on ** the host architecture. ^It is harmless, apart from the wasted memory, ** to make sz a little too large. The first ** argument should point to an allocation of at least sz*N bytes of memory. ** ^SQLite will use the memory provided by the first argument to satisfy its ** memory needs for the first N pages that it adds to cache. ^If additional ** page cache memory is needed beyond what is provided by this option, then ** SQLite goes to [sqlite3_malloc()] for the additional storage space. ** ^The implementation might use one or more of the N buffers to hold ** memory accounting information. The pointer in the first argument must ** be aligned to an 8-byte boundary or subsequent behavior of SQLite ** will be undefined.
    ** **
    SQLITE_CONFIG_HEAP
    **
    ^This option specifies a static memory buffer that SQLite will use ** for all of its dynamic memory allocation needs beyond those provided ** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE]. ** There are three arguments: An 8-byte aligned pointer to the memory, ** the number of bytes in the memory buffer, and the minimum allocation size. ** ^If the first pointer (the memory pointer) is NULL, then SQLite reverts ** to using its default memory allocator (the system malloc() implementation), ** undoing any prior invocation of [SQLITE_CONFIG_MALLOC]. ^If the ** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory ** allocator is engaged to handle all of SQLites memory allocation needs. ** The first pointer (the memory pointer) must be aligned to an 8-byte ** boundary or subsequent behavior of SQLite will be undefined.
    ** **
    SQLITE_CONFIG_MUTEX
    **
    ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mutex_methods] structure. The argument specifies ** alternative low-level mutex routines to be used in place ** the mutex routines built into SQLite.)^ ^SQLite makes a copy of the ** content of the [sqlite3_mutex_methods] structure before the call to ** [sqlite3_config()] returns. ^If SQLite is compiled with ** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then ** the entire mutexing subsystem is omitted from the build and hence calls to ** [sqlite3_config()] with the SQLITE_CONFIG_MUTEX configuration option will ** return [SQLITE_ERROR].
    ** **
    SQLITE_CONFIG_GETMUTEX
    **
    ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mutex_methods] structure. The ** [sqlite3_mutex_methods] ** structure is filled with the currently defined mutex routines.)^ ** This option can be used to overload the default mutex allocation ** routines with a wrapper used to track mutex usage for performance ** profiling or testing, for example. ^If SQLite is compiled with ** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then ** the entire mutexing subsystem is omitted from the build and hence calls to ** [sqlite3_config()] with the SQLITE_CONFIG_GETMUTEX configuration option will ** return [SQLITE_ERROR].
    ** **
    SQLITE_CONFIG_LOOKASIDE
    **
    ^(This option takes two arguments that determine the default ** memory allocation for the lookaside memory allocator on each ** [database connection]. The first argument is the ** size of each lookaside buffer slot and the second is the number of ** slots allocated to each database connection.)^ ^(This option sets the ** default lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE] ** verb to [sqlite3_db_config()] can be used to change the lookaside ** configuration on individual connections.)^
    ** **
    SQLITE_CONFIG_PCACHE
    **
    ^(This option takes a single argument which is a pointer to ** an [sqlite3_pcache_methods] object. This object specifies the interface ** to a custom page cache implementation.)^ ^SQLite makes a copy of the ** object and uses it for page cache memory allocations.
    ** **
    SQLITE_CONFIG_GETPCACHE
    **
    ^(This option takes a single argument which is a pointer to an ** [sqlite3_pcache_methods] object. SQLite copies of the current ** page cache implementation into that object.)^
    ** **
    */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_SCRATCH 6 /* void*, int sz, int N */ #define SQLITE_CONFIG_PAGECACHE 7 /* void*, int sz, int N */ #define SQLITE_CONFIG_HEAP 8 /* void*, int nByte, int min */ #define SQLITE_CONFIG_MEMSTATUS 9 /* boolean */ #define SQLITE_CONFIG_MUTEX 10 /* sqlite3_mutex_methods* */ #define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */ /* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ #define SQLITE_CONFIG_LOOKASIDE 13 /* int int */ #define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */ #define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */ #define SQLITE_CONFIG_LOG 16 /* xFunc, void* */ /* ** CAPI3REF: Configuration Options ** EXPERIMENTAL ** ** These constants are the available integer configuration options that ** can be passed as the second argument to the [sqlite3_db_config()] interface. ** ** New configuration options may be added in future releases of SQLite. ** Existing configuration options might be discontinued. Applications ** should check the return code from [sqlite3_db_config()] to make sure that ** the call worked. ^The [sqlite3_db_config()] interface will return a ** non-zero [error code] if a discontinued or unsupported configuration option ** is invoked. ** **
    **
    SQLITE_DBCONFIG_LOOKASIDE
    **
    ^This option takes three additional arguments that determine the ** [lookaside memory allocator] configuration for the [database connection]. ** ^The first argument (the third parameter to [sqlite3_db_config()] is a ** pointer to an memory buffer to use for lookaside memory. ** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb ** may be NULL in which case SQLite will allocate the ** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the ** size of each lookaside buffer slot. ^The third argument is the number of ** slots. The size of the buffer in the first argument must be greater than ** or equal to the product of the second and third arguments. The buffer ** must be aligned to an 8-byte boundary. ^If the second argument to ** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally ** rounded down to the next smaller ** multiple of 8. See also: [SQLITE_CONFIG_LOOKASIDE]
    ** **
    */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes ** ** ^The sqlite3_extended_result_codes() routine enables or disables the ** [extended result codes] feature of SQLite. ^The extended result ** codes are disabled by default for historical compatibility. */ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); /* ** CAPI3REF: Last Insert Rowid ** ** ^Each entry in an SQLite table has a unique 64-bit signed ** integer key called the [ROWID | "rowid"]. ^The rowid is always available ** as an undeclared column named ROWID, OID, or _ROWID_ as long as those ** names are not also used by explicitly declared columns. ^If ** the table has a column of type [INTEGER PRIMARY KEY] then that column ** is another alias for the rowid. ** ** ^This routine returns the [rowid] of the most recent ** successful [INSERT] into the database from the [database connection] ** in the first argument. ^If no successful [INSERT]s ** have ever occurred on that database connection, zero is returned. ** ** ^(If an [INSERT] occurs within a trigger, then the [rowid] of the inserted ** row is returned by this routine as long as the trigger is running. ** But once the trigger terminates, the value returned by this routine ** reverts to the last value inserted before the trigger fired.)^ ** ** ^An [INSERT] that fails due to a constraint violation is not a ** successful [INSERT] and does not change the value returned by this ** routine. ^Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK, ** and INSERT OR ABORT make no changes to the return value of this ** routine when their insertion fails. ^(When INSERT OR REPLACE ** encounters a constraint violation, it does not fail. The ** INSERT continues to completion after deleting rows that caused ** the constraint problem so INSERT OR REPLACE will always change ** the return value of this interface.)^ ** ** ^For the purposes of this routine, an [INSERT] is considered to ** be successful even if it is subsequently rolled back. ** ** This function is accessible to SQL statements via the ** [last_insert_rowid() SQL function]. ** ** If a separate thread performs a new [INSERT] on the same ** database connection while the [sqlite3_last_insert_rowid()] ** function is running and thus changes the last insert [rowid], ** then the value returned by [sqlite3_last_insert_rowid()] is ** unpredictable and might not equal either the old or the new ** last insert [rowid]. */ SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); /* ** CAPI3REF: Count The Number Of Rows Modified ** ** ^This function returns the number of database rows that were changed ** or inserted or deleted by the most recently completed SQL statement ** on the [database connection] specified by the first parameter. ** ^(Only changes that are directly specified by the [INSERT], [UPDATE], ** or [DELETE] statement are counted. Auxiliary changes caused by ** triggers or [foreign key actions] are not counted.)^ Use the ** [sqlite3_total_changes()] function to find the total number of changes ** including changes caused by triggers and foreign key actions. ** ** ^Changes to a view that are simulated by an [INSTEAD OF trigger] ** are not counted. Only real table changes are counted. ** ** ^(A "row change" is a change to a single row of a single table ** caused by an INSERT, DELETE, or UPDATE statement. Rows that ** are changed as side effects of [REPLACE] constraint resolution, ** rollback, ABORT processing, [DROP TABLE], or by any other ** mechanisms do not count as direct row changes.)^ ** ** A "trigger context" is a scope of execution that begins and ** ends with the script of a [CREATE TRIGGER | trigger]. ** Most SQL statements are ** evaluated outside of any trigger. This is the "top level" ** trigger context. If a trigger fires from the top level, a ** new trigger context is entered for the duration of that one ** trigger. Subtriggers create subcontexts for their duration. ** ** ^Calling [sqlite3_exec()] or [sqlite3_step()] recursively does ** not create a new trigger context. ** ** ^This function returns the number of direct row changes in the ** most recent INSERT, UPDATE, or DELETE statement within the same ** trigger context. ** ** ^Thus, when called from the top level, this function returns the ** number of changes in the most recent INSERT, UPDATE, or DELETE ** that also occurred at the top level. ^(Within the body of a trigger, ** the sqlite3_changes() interface can be called to find the number of ** changes in the most recently completed INSERT, UPDATE, or DELETE ** statement within the body of the same trigger. ** However, the number returned does not include changes ** caused by subtriggers since those have their own context.)^ ** ** See also the [sqlite3_total_changes()] interface, the ** [count_changes pragma], and the [changes() SQL function]. ** ** If a separate thread makes changes on the same database connection ** while [sqlite3_changes()] is running then the value returned ** is unpredictable and not meaningful. */ SQLITE_API int sqlite3_changes(sqlite3*); /* ** CAPI3REF: Total Number Of Rows Modified ** ** ^This function returns the number of row changes caused by [INSERT], ** [UPDATE] or [DELETE] statements since the [database connection] was opened. ** ^(The count returned by sqlite3_total_changes() includes all changes ** from all [CREATE TRIGGER | trigger] contexts and changes made by ** [foreign key actions]. However, ** the count does not include changes used to implement [REPLACE] constraints, ** do rollbacks or ABORT processing, or [DROP TABLE] processing. The ** count does not include rows of views that fire an [INSTEAD OF trigger], ** though if the INSTEAD OF trigger makes changes of its own, those changes ** are counted.)^ ** ^The sqlite3_total_changes() function counts the changes as soon as ** the statement that makes them is completed (when the statement handle ** is passed to [sqlite3_reset()] or [sqlite3_finalize()]). ** ** See also the [sqlite3_changes()] interface, the ** [count_changes pragma], and the [total_changes() SQL function]. ** ** If a separate thread makes changes on the same database connection ** while [sqlite3_total_changes()] is running then the value ** returned is unpredictable and not meaningful. */ SQLITE_API int sqlite3_total_changes(sqlite3*); /* ** CAPI3REF: Interrupt A Long-Running Query ** ** ^This function causes any pending database operation to abort and ** return at its earliest opportunity. This routine is typically ** called in response to a user action such as pressing "Cancel" ** or Ctrl-C where the user wants a long query operation to halt ** immediately. ** ** ^It is safe to call this routine from a thread different from the ** thread that is currently running the database operation. But it ** is not safe to call this routine with a [database connection] that ** is closed or might close before sqlite3_interrupt() returns. ** ** ^If an SQL operation is very nearly finished at the time when ** sqlite3_interrupt() is called, then it might not have an opportunity ** to be interrupted and might continue to completion. ** ** ^An SQL operation that is interrupted will return [SQLITE_INTERRUPT]. ** ^If the interrupted SQL operation is an INSERT, UPDATE, or DELETE ** that is inside an explicit transaction, then the entire transaction ** will be rolled back automatically. ** ** ^The sqlite3_interrupt(D) call is in effect until all currently running ** SQL statements on [database connection] D complete. ^Any new SQL statements ** that are started after the sqlite3_interrupt() call and before the ** running statements reaches zero are interrupted as if they had been ** running prior to the sqlite3_interrupt() call. ^New SQL statements ** that are started after the running statement count reaches zero are ** not effected by the sqlite3_interrupt(). ** ^A call to sqlite3_interrupt(D) that occurs when there are no running ** SQL statements is a no-op and has no effect on SQL statements ** that are started after the sqlite3_interrupt() call returns. ** ** If the database connection closes while [sqlite3_interrupt()] ** is running then bad things will likely happen. */ SQLITE_API void sqlite3_interrupt(sqlite3*); /* ** CAPI3REF: Determine If An SQL Statement Is Complete ** ** These routines are useful during command-line input to determine if the ** currently entered text seems to form a complete SQL statement or ** if additional input is needed before sending the text into ** SQLite for parsing. ^These routines return 1 if the input string ** appears to be a complete SQL statement. ^A statement is judged to be ** complete if it ends with a semicolon token and is not a prefix of a ** well-formed CREATE TRIGGER statement. ^Semicolons that are embedded within ** string literals or quoted identifier names or comments are not ** independent tokens (they are part of the token in which they are ** embedded) and thus do not count as a statement terminator. ^Whitespace ** and comments that follow the final semicolon are ignored. ** ** ^These routines return 0 if the statement is incomplete. ^If a ** memory allocation fails, then SQLITE_NOMEM is returned. ** ** ^These routines do not parse the SQL statements thus ** will not detect syntactically incorrect SQL. ** ** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior ** to invoking sqlite3_complete16() then sqlite3_initialize() is invoked ** automatically by sqlite3_complete16(). If that initialization fails, ** then the return value from sqlite3_complete16() will be non-zero ** regardless of whether or not the input SQL is complete.)^ ** ** The input to [sqlite3_complete()] must be a zero-terminated ** UTF-8 string. ** ** The input to [sqlite3_complete16()] must be a zero-terminated ** UTF-16 string in native byte order. */ SQLITE_API int sqlite3_complete(const char *sql); SQLITE_API int sqlite3_complete16(const void *sql); /* ** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors ** ** ^This routine sets a callback function that might be invoked whenever ** an attempt is made to open a database table that another thread ** or process has locked. ** ** ^If the busy callback is NULL, then [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] ** is returned immediately upon encountering the lock. ^If the busy callback ** is not NULL, then the callback might be invoked with two arguments. ** ** ^The first argument to the busy handler is a copy of the void* pointer which ** is the third argument to sqlite3_busy_handler(). ^The second argument to ** the busy handler callback is the number of times that the busy handler has ** been invoked for this locking event. ^If the ** busy callback returns 0, then no additional attempts are made to ** access the database and [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] is returned. ** ^If the callback returns non-zero, then another attempt ** is made to open the database for reading and the cycle repeats. ** ** The presence of a busy handler does not guarantee that it will be invoked ** when there is lock contention. ^If SQLite determines that invoking the busy ** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY] ** or [SQLITE_IOERR_BLOCKED] instead of invoking the busy handler. ** Consider a scenario where one process is holding a read lock that ** it is trying to promote to a reserved lock and ** a second process is holding a reserved lock that it is trying ** to promote to an exclusive lock. The first process cannot proceed ** because it is blocked by the second and the second process cannot ** proceed because it is blocked by the first. If both processes ** invoke the busy handlers, neither will make any progress. Therefore, ** SQLite returns [SQLITE_BUSY] for the first process, hoping that this ** will induce the first process to release its read lock and allow ** the second process to proceed. ** ** ^The default busy callback is NULL. ** ** ^The [SQLITE_BUSY] error is converted to [SQLITE_IOERR_BLOCKED] ** when SQLite is in the middle of a large transaction where all the ** changes will not fit into the in-memory cache. SQLite will ** already hold a RESERVED lock on the database file, but it needs ** to promote this lock to EXCLUSIVE so that it can spill cache ** pages into the database file without harm to concurrent ** readers. ^If it is unable to promote the lock, then the in-memory ** cache will be left in an inconsistent state and so the error ** code is promoted from the relatively benign [SQLITE_BUSY] to ** the more severe [SQLITE_IOERR_BLOCKED]. ^This error code promotion ** forces an automatic rollback of the changes. See the ** ** CorruptionFollowingBusyError wiki page for a discussion of why ** this is important. ** ** ^(There can only be a single busy handler defined for each ** [database connection]. Setting a new busy handler clears any ** previously set handler.)^ ^Note that calling [sqlite3_busy_timeout()] ** will also set or clear the busy handler. ** ** The busy callback should not take any actions which modify the ** database connection that invoked the busy handler. Any such actions ** result in undefined behavior. ** ** A busy handler must not close the database connection ** or [prepared statement] that invoked the busy handler. */ SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); /* ** CAPI3REF: Set A Busy Timeout ** ** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps ** for a specified amount of time when a table is locked. ^The handler ** will sleep multiple times until at least "ms" milliseconds of sleeping ** have accumulated. ^After at least "ms" milliseconds of sleeping, ** the handler returns 0 which causes [sqlite3_step()] to return ** [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED]. ** ** ^Calling this routine with an argument less than or equal to zero ** turns off all busy handlers. ** ** ^(There can only be a single busy handler for a particular ** [database connection] any any given moment. If another busy handler ** was defined (using [sqlite3_busy_handler()]) prior to calling ** this routine, that other busy handler is cleared.)^ */ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); /* ** CAPI3REF: Convenience Routines For Running Queries ** ** Definition: A result table is memory data structure created by the ** [sqlite3_get_table()] interface. A result table records the ** complete query results from one or more queries. ** ** The table conceptually has a number of rows and columns. But ** these numbers are not part of the result table itself. These ** numbers are obtained separately. Let N be the number of rows ** and M be the number of columns. ** ** A result table is an array of pointers to zero-terminated UTF-8 strings. ** There are (N+1)*M elements in the array. The first M pointers point ** to zero-terminated strings that contain the names of the columns. ** The remaining entries all point to query results. NULL values result ** in NULL pointers. All other values are in their UTF-8 zero-terminated ** string representation as returned by [sqlite3_column_text()]. ** ** A result table might consist of one or more memory allocations. ** It is not safe to pass a result table directly to [sqlite3_free()]. ** A result table should be deallocated using [sqlite3_free_table()]. ** ** As an example of the result table format, suppose a query result ** is as follows: ** **
    **        Name        | Age
    **        -----------------------
    **        Alice       | 43
    **        Bob         | 28
    **        Cindy       | 21
    ** 
    ** ** There are two column (M==2) and three rows (N==3). Thus the ** result table has 8 entries. Suppose the result table is stored ** in an array names azResult. Then azResult holds this content: ** **
    **        azResult[0] = "Name";
    **        azResult[1] = "Age";
    **        azResult[2] = "Alice";
    **        azResult[3] = "43";
    **        azResult[4] = "Bob";
    **        azResult[5] = "28";
    **        azResult[6] = "Cindy";
    **        azResult[7] = "21";
    ** 
    ** ** ^The sqlite3_get_table() function evaluates one or more ** semicolon-separated SQL statements in the zero-terminated UTF-8 ** string of its 2nd parameter and returns a result table to the ** pointer given in its 3rd parameter. ** ** After the application has finished with the result from sqlite3_get_table(), ** it should pass the result table pointer to sqlite3_free_table() in order to ** release the memory that was malloced. Because of the way the ** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling ** function must not try to call [sqlite3_free()] directly. Only ** [sqlite3_free_table()] is able to release the memory properly and safely. ** ** ^(The sqlite3_get_table() interface is implemented as a wrapper around ** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access ** to any internal data structures of SQLite. It uses only the public ** interface defined here. As a consequence, errors that occur in the ** wrapper layer outside of the internal [sqlite3_exec()] call are not ** reflected in subsequent calls to [sqlite3_errcode()] or ** [sqlite3_errmsg()].)^ */ SQLITE_API int sqlite3_get_table( sqlite3 *db, /* An open database */ const char *zSql, /* SQL to be evaluated */ char ***pazResult, /* Results of the query */ int *pnRow, /* Number of result rows written here */ int *pnColumn, /* Number of result columns written here */ char **pzErrmsg /* Error msg written here */ ); SQLITE_API void sqlite3_free_table(char **result); /* ** CAPI3REF: Formatted String Printing Functions ** ** These routines are work-alikes of the "printf()" family of functions ** from the standard C library. ** ** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their ** results into memory obtained from [sqlite3_malloc()]. ** The strings returned by these two routines should be ** released by [sqlite3_free()]. ^Both routines return a ** NULL pointer if [sqlite3_malloc()] is unable to allocate enough ** memory to hold the resulting string. ** ** ^(In sqlite3_snprintf() routine is similar to "snprintf()" from ** the standard C library. The result is written into the ** buffer supplied as the second parameter whose size is given by ** the first parameter. Note that the order of the ** first two parameters is reversed from snprintf().)^ This is an ** historical accident that cannot be fixed without breaking ** backwards compatibility. ^(Note also that sqlite3_snprintf() ** returns a pointer to its buffer instead of the number of ** characters actually written into the buffer.)^ We admit that ** the number of characters written would be a more useful return ** value but we cannot change the implementation of sqlite3_snprintf() ** now without breaking compatibility. ** ** ^As long as the buffer size is greater than zero, sqlite3_snprintf() ** guarantees that the buffer is always zero-terminated. ^The first ** parameter "n" is the total size of the buffer, including space for ** the zero terminator. So the longest string that can be completely ** written will be n-1 characters. ** ** These routines all implement some additional formatting ** options that are useful for constructing SQL statements. ** All of the usual printf() formatting options apply. In addition, there ** is are "%q", "%Q", and "%z" options. ** ** ^(The %q option works like %s in that it substitutes a null-terminated ** string from the argument list. But %q also doubles every '\'' character. ** %q is designed for use inside a string literal.)^ By doubling each '\'' ** character it escapes that character and allows it to be inserted into ** the string. ** ** For example, assume the string variable zText contains text as follows: ** **
    **  char *zText = "It's a happy day!";
    ** 
    ** ** One can use this text in an SQL statement as follows: ** **
    **  char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES('%q')", zText);
    **  sqlite3_exec(db, zSQL, 0, 0, 0);
    **  sqlite3_free(zSQL);
    ** 
    ** ** Because the %q format string is used, the '\'' character in zText ** is escaped and the SQL generated is as follows: ** **
    **  INSERT INTO table1 VALUES('It''s a happy day!')
    ** 
    ** ** This is correct. Had we used %s instead of %q, the generated SQL ** would have looked like this: ** **
    **  INSERT INTO table1 VALUES('It's a happy day!');
    ** 
    ** ** This second example is an SQL syntax error. As a general rule you should ** always use %q instead of %s when inserting text into a string literal. ** ** ^(The %Q option works like %q except it also adds single quotes around ** the outside of the total string. Additionally, if the parameter in the ** argument list is a NULL pointer, %Q substitutes the text "NULL" (without ** single quotes).)^ So, for example, one could say: ** **
    **  char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText);
    **  sqlite3_exec(db, zSQL, 0, 0, 0);
    **  sqlite3_free(zSQL);
    ** 
    ** ** The code above will render a correct SQL statement in the zSQL ** variable even if the zText variable is a NULL pointer. ** ** ^(The "%z" formatting option works like "%s" but with the ** addition that after the string has been read and copied into ** the result, [sqlite3_free()] is called on the input string.)^ */ SQLITE_API char *sqlite3_mprintf(const char*,...); SQLITE_API char *sqlite3_vmprintf(const char*, va_list); SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...); /* ** CAPI3REF: Memory Allocation Subsystem ** ** The SQLite core uses these three routines for all of its own ** internal memory allocation needs. "Core" in the previous sentence ** does not include operating-system specific VFS implementation. The ** Windows VFS uses native malloc() and free() for some operations. ** ** ^The sqlite3_malloc() routine returns a pointer to a block ** of memory at least N bytes in length, where N is the parameter. ** ^If sqlite3_malloc() is unable to obtain sufficient free ** memory, it returns a NULL pointer. ^If the parameter N to ** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns ** a NULL pointer. ** ** ^Calling sqlite3_free() with a pointer previously returned ** by sqlite3_malloc() or sqlite3_realloc() releases that memory so ** that it might be reused. ^The sqlite3_free() routine is ** a no-op if is called with a NULL pointer. Passing a NULL pointer ** to sqlite3_free() is harmless. After being freed, memory ** should neither be read nor written. Even reading previously freed ** memory might result in a segmentation fault or other severe error. ** Memory corruption, a segmentation fault, or other severe error ** might result if sqlite3_free() is called with a non-NULL pointer that ** was not obtained from sqlite3_malloc() or sqlite3_realloc(). ** ** ^(The sqlite3_realloc() interface attempts to resize a ** prior memory allocation to be at least N bytes, where N is the ** second parameter. The memory allocation to be resized is the first ** parameter.)^ ^ If the first parameter to sqlite3_realloc() ** is a NULL pointer then its behavior is identical to calling ** sqlite3_malloc(N) where N is the second parameter to sqlite3_realloc(). ** ^If the second parameter to sqlite3_realloc() is zero or ** negative then the behavior is exactly the same as calling ** sqlite3_free(P) where P is the first parameter to sqlite3_realloc(). ** ^sqlite3_realloc() returns a pointer to a memory allocation ** of at least N bytes in size or NULL if sufficient memory is unavailable. ** ^If M is the size of the prior allocation, then min(N,M) bytes ** of the prior allocation are copied into the beginning of buffer returned ** by sqlite3_realloc() and the prior allocation is freed. ** ^If sqlite3_realloc() returns NULL, then the prior allocation ** is not freed. ** ** ^The memory returned by sqlite3_malloc() and sqlite3_realloc() ** is always aligned to at least an 8 byte boundary. ** ** In SQLite version 3.5.0 and 3.5.1, it was possible to define ** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in ** implementation of these routines to be omitted. That capability ** is no longer provided. Only built-in memory allocators can be used. ** ** The Windows OS interface layer calls ** the system malloc() and free() directly when converting ** filenames between the UTF-8 encoding used by SQLite ** and whatever filename encoding is used by the particular Windows ** installation. Memory allocation errors are detected, but ** they are reported back as [SQLITE_CANTOPEN] or ** [SQLITE_IOERR] rather than [SQLITE_NOMEM]. ** ** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()] ** must be either NULL or else pointers obtained from a prior ** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have ** not yet been released. ** ** The application must not read or write any part of ** a block of memory after it has been released using ** [sqlite3_free()] or [sqlite3_realloc()]. */ SQLITE_API void *sqlite3_malloc(int); SQLITE_API void *sqlite3_realloc(void*, int); SQLITE_API void sqlite3_free(void*); /* ** CAPI3REF: Memory Allocator Statistics ** ** SQLite provides these two interfaces for reporting on the status ** of the [sqlite3_malloc()], [sqlite3_free()], and [sqlite3_realloc()] ** routines, which form the built-in memory allocation subsystem. ** ** ^The [sqlite3_memory_used()] routine returns the number of bytes ** of memory currently outstanding (malloced but not freed). ** ^The [sqlite3_memory_highwater()] routine returns the maximum ** value of [sqlite3_memory_used()] since the high-water mark ** was last reset. ^The values returned by [sqlite3_memory_used()] and ** [sqlite3_memory_highwater()] include any overhead ** added by SQLite in its implementation of [sqlite3_malloc()], ** but not overhead added by the any underlying system library ** routines that [sqlite3_malloc()] may call. ** ** ^The memory high-water mark is reset to the current value of ** [sqlite3_memory_used()] if and only if the parameter to ** [sqlite3_memory_highwater()] is true. ^The value returned ** by [sqlite3_memory_highwater(1)] is the high-water mark ** prior to the reset. */ SQLITE_API sqlite3_int64 sqlite3_memory_used(void); SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag); /* ** CAPI3REF: Pseudo-Random Number Generator ** ** SQLite contains a high-quality pseudo-random number generator (PRNG) used to ** select random [ROWID | ROWIDs] when inserting new records into a table that ** already uses the largest possible [ROWID]. The PRNG is also used for ** the build-in random() and randomblob() SQL functions. This interface allows ** applications to access the same PRNG for other purposes. ** ** ^A call to this routine stores N bytes of randomness into buffer P. ** ** ^The first time this routine is invoked (either internally or by ** the application) the PRNG is seeded using randomness obtained ** from the xRandomness method of the default [sqlite3_vfs] object. ** ^On all subsequent invocations, the pseudo-randomness is generated ** internally and without recourse to the [sqlite3_vfs] xRandomness ** method. */ SQLITE_API void sqlite3_randomness(int N, void *P); /* ** CAPI3REF: Compile-Time Authorization Callbacks ** ** ^This routine registers a authorizer callback with a particular ** [database connection], supplied in the first argument. ** ^The authorizer callback is invoked as SQL statements are being compiled ** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()], ** [sqlite3_prepare16()] and [sqlite3_prepare16_v2()]. ^At various ** points during the compilation process, as logic is being created ** to perform various actions, the authorizer callback is invoked to ** see if those actions are allowed. ^The authorizer callback should ** return [SQLITE_OK] to allow the action, [SQLITE_IGNORE] to disallow the ** specific action but allow the SQL statement to continue to be ** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be ** rejected with an error. ^If the authorizer callback returns ** any value other than [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY] ** then the [sqlite3_prepare_v2()] or equivalent call that triggered ** the authorizer will fail with an error message. ** ** When the callback returns [SQLITE_OK], that means the operation ** requested is ok. ^When the callback returns [SQLITE_DENY], the ** [sqlite3_prepare_v2()] or equivalent call that triggered the ** authorizer will fail with an error message explaining that ** access is denied. ** ** ^The first parameter to the authorizer callback is a copy of the third ** parameter to the sqlite3_set_authorizer() interface. ^The second parameter ** to the callback is an integer [SQLITE_COPY | action code] that specifies ** the particular action to be authorized. ^The third through sixth parameters ** to the callback are zero-terminated strings that contain additional ** details about the action to be authorized. ** ** ^If the action code is [SQLITE_READ] ** and the callback returns [SQLITE_IGNORE] then the ** [prepared statement] statement is constructed to substitute ** a NULL value in place of the table column that would have ** been read if [SQLITE_OK] had been returned. The [SQLITE_IGNORE] ** return can be used to deny an untrusted user access to individual ** columns of a table. ** ^If the action code is [SQLITE_DELETE] and the callback returns ** [SQLITE_IGNORE] then the [DELETE] operation proceeds but the ** [truncate optimization] is disabled and all rows are deleted individually. ** ** An authorizer is used when [sqlite3_prepare | preparing] ** SQL statements from an untrusted source, to ensure that the SQL statements ** do not try to access data they are not allowed to see, or that they do not ** try to execute malicious statements that damage the database. For ** example, an application may allow a user to enter arbitrary ** SQL queries for evaluation by a database. But the application does ** not want the user to be able to make arbitrary changes to the ** database. An authorizer could then be put in place while the ** user-entered SQL is being [sqlite3_prepare | prepared] that ** disallows everything except [SELECT] statements. ** ** Applications that need to process SQL from untrusted sources ** might also consider lowering resource limits using [sqlite3_limit()] ** and limiting database size using the [max_page_count] [PRAGMA] ** in addition to using an authorizer. ** ** ^(Only a single authorizer can be in place on a database connection ** at a time. Each call to sqlite3_set_authorizer overrides the ** previous call.)^ ^Disable the authorizer by installing a NULL callback. ** The authorizer is disabled by default. ** ** The authorizer callback must not do anything that will modify ** the database connection that invoked the authorizer callback. ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** ** ^When [sqlite3_prepare_v2()] is used to prepare a statement, the ** statement might be re-prepared during [sqlite3_step()] due to a ** schema change. Hence, the application should ensure that the ** correct authorizer callback remains in place during the [sqlite3_step()]. ** ** ^Note that the authorizer callback is invoked only during ** [sqlite3_prepare()] or its variants. Authorization is not ** performed during statement evaluation in [sqlite3_step()], unless ** as stated in the previous paragraph, sqlite3_step() invokes ** sqlite3_prepare_v2() to reprepare a statement after a schema change. */ SQLITE_API int sqlite3_set_authorizer( sqlite3*, int (*xAuth)(void*,int,const char*,const char*,const char*,const char*), void *pUserData ); /* ** CAPI3REF: Authorizer Return Codes ** ** The [sqlite3_set_authorizer | authorizer callback function] must ** return either [SQLITE_OK] or one of these two constants in order ** to signal SQLite whether or not the action is permitted. See the ** [sqlite3_set_authorizer | authorizer documentation] for additional ** information. */ #define SQLITE_DENY 1 /* Abort the SQL statement with an error */ #define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */ /* ** CAPI3REF: Authorizer Action Codes ** ** The [sqlite3_set_authorizer()] interface registers a callback function ** that is invoked to authorize certain SQL statement actions. The ** second parameter to the callback is an integer code that specifies ** what action is being authorized. These are the integer action codes that ** the authorizer callback may be passed. ** ** These action code values signify what kind of operation is to be ** authorized. The 3rd and 4th parameters to the authorization ** callback function will be parameters or NULL depending on which of these ** codes is used as the second parameter. ^(The 5th parameter to the ** authorizer callback is the name of the database ("main", "temp", ** etc.) if applicable.)^ ^The 6th parameter to the authorizer callback ** is the name of the inner-most trigger or view that is responsible for ** the access attempt or NULL if this access attempt is directly from ** top-level SQL code. */ /******************************************* 3rd ************ 4th ***********/ #define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */ #define SQLITE_CREATE_TABLE 2 /* Table Name NULL */ #define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */ #define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */ #define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */ #define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */ #define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */ #define SQLITE_CREATE_VIEW 8 /* View Name NULL */ #define SQLITE_DELETE 9 /* Table Name NULL */ #define SQLITE_DROP_INDEX 10 /* Index Name Table Name */ #define SQLITE_DROP_TABLE 11 /* Table Name NULL */ #define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */ #define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */ #define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */ #define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */ #define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */ #define SQLITE_DROP_VIEW 17 /* View Name NULL */ #define SQLITE_INSERT 18 /* Table Name NULL */ #define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */ #define SQLITE_READ 20 /* Table Name Column Name */ #define SQLITE_SELECT 21 /* NULL NULL */ #define SQLITE_TRANSACTION 22 /* Operation NULL */ #define SQLITE_UPDATE 23 /* Table Name Column Name */ #define SQLITE_ATTACH 24 /* Filename NULL */ #define SQLITE_DETACH 25 /* Database Name NULL */ #define SQLITE_ALTER_TABLE 26 /* Database Name Table Name */ #define SQLITE_REINDEX 27 /* Index Name NULL */ #define SQLITE_ANALYZE 28 /* Table Name NULL */ #define SQLITE_CREATE_VTABLE 29 /* Table Name Module Name */ #define SQLITE_DROP_VTABLE 30 /* Table Name Module Name */ #define SQLITE_FUNCTION 31 /* NULL Function Name */ #define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */ #define SQLITE_COPY 0 /* No longer used */ /* ** CAPI3REF: Tracing And Profiling Functions ** EXPERIMENTAL ** ** These routines register callback functions that can be used for ** tracing and profiling the execution of SQL statements. ** ** ^The callback function registered by sqlite3_trace() is invoked at ** various times when an SQL statement is being run by [sqlite3_step()]. ** ^The sqlite3_trace() callback is invoked with a UTF-8 rendering of the ** SQL statement text as the statement first begins executing. ** ^(Additional sqlite3_trace() callbacks might occur ** as each triggered subprogram is entered. The callbacks for triggers ** contain a UTF-8 SQL comment that identifies the trigger.)^ ** ** ^The callback function registered by sqlite3_profile() is invoked ** as each SQL statement finishes. ^The profile callback contains ** the original statement text and an estimate of wall-clock time ** of how long that statement took to run. */ SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*, void(*xProfile)(void*,const char*,sqlite3_uint64), void*); /* ** CAPI3REF: Query Progress Callbacks ** ** ^This routine configures a callback function - the ** progress callback - that is invoked periodically during long ** running calls to [sqlite3_exec()], [sqlite3_step()] and ** [sqlite3_get_table()]. An example use for this ** interface is to keep a GUI updated during a large query. ** ** ^If the progress callback returns non-zero, the operation is ** interrupted. This feature can be used to implement a ** "Cancel" button on a GUI progress dialog box. ** ** The progress handler must not do anything that will modify ** the database connection that invoked the progress handler. ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** */ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*); /* ** CAPI3REF: Opening A New Database Connection ** ** ^These routines open an SQLite database file whose name is given by the ** filename argument. ^The filename argument is interpreted as UTF-8 for ** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte ** order for sqlite3_open16(). ^(A [database connection] handle is usually ** returned in *ppDb, even if an error occurs. The only exception is that ** if SQLite is unable to allocate memory to hold the [sqlite3] object, ** a NULL will be written into *ppDb instead of a pointer to the [sqlite3] ** object.)^ ^(If the database is opened (and/or created) successfully, then ** [SQLITE_OK] is returned. Otherwise an [error code] is returned.)^ ^The ** [sqlite3_errmsg()] or [sqlite3_errmsg16()] routines can be used to obtain ** an English language description of the error following a failure of any ** of the sqlite3_open() routines. ** ** ^The default encoding for the database will be UTF-8 if ** sqlite3_open() or sqlite3_open_v2() is called and ** UTF-16 in the native byte order if sqlite3_open16() is used. ** ** Whether or not an error occurs when it is opened, resources ** associated with the [database connection] handle should be released by ** passing it to [sqlite3_close()] when it is no longer required. ** ** The sqlite3_open_v2() interface works like sqlite3_open() ** except that it accepts two additional parameters for additional control ** over the new database connection. ^(The flags parameter to ** sqlite3_open_v2() can take one of ** the following three values, optionally combined with the ** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE], ** and/or [SQLITE_OPEN_PRIVATECACHE] flags:)^ ** **
    ** ^(
    [SQLITE_OPEN_READONLY]
    **
    The database is opened in read-only mode. If the database does not ** already exist, an error is returned.
    )^ ** ** ^(
    [SQLITE_OPEN_READWRITE]
    **
    The database is opened for reading and writing if possible, or reading ** only if the file is write protected by the operating system. In either ** case the database must already exist, otherwise an error is returned.
    )^ ** ** ^(
    [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
    **
    The database is opened for reading and writing, and is creates it if ** it does not already exist. This is the behavior that is always used for ** sqlite3_open() and sqlite3_open16().
    )^ **
    ** ** If the 3rd parameter to sqlite3_open_v2() is not one of the ** combinations shown above or one of the combinations shown above combined ** with the [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], ** [SQLITE_OPEN_SHAREDCACHE] and/or [SQLITE_OPEN_SHAREDCACHE] flags, ** then the behavior is undefined. ** ** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection ** opens in the multi-thread [threading mode] as long as the single-thread ** mode has not been set at compile-time or start-time. ^If the ** [SQLITE_OPEN_FULLMUTEX] flag is set then the database connection opens ** in the serialized [threading mode] unless single-thread was ** previously selected at compile-time or start-time. ** ^The [SQLITE_OPEN_SHAREDCACHE] flag causes the database connection to be ** eligible to use [shared cache mode], regardless of whether or not shared ** cache is enabled using [sqlite3_enable_shared_cache()]. ^The ** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not ** participate in [shared cache mode] even if it is enabled. ** ** ^If the filename is ":memory:", then a private, temporary in-memory database ** is created for the connection. ^This in-memory database will vanish when ** the database connection is closed. Future versions of SQLite might ** make use of additional special filenames that begin with the ":" character. ** It is recommended that when a database filename actually does begin with ** a ":" character you should prefix the filename with a pathname such as ** "./" to avoid ambiguity. ** ** ^If the filename is an empty string, then a private, temporary ** on-disk database will be created. ^This private database will be ** automatically deleted as soon as the database connection is closed. ** ** ^The fourth parameter to sqlite3_open_v2() is the name of the ** [sqlite3_vfs] object that defines the operating system interface that ** the new database connection should use. ^If the fourth parameter is ** a NULL pointer then the default [sqlite3_vfs] object is used. ** ** Note to Windows users: The encoding used for the filename argument ** of sqlite3_open() and sqlite3_open_v2() must be UTF-8, not whatever ** codepage is currently defined. Filenames containing international ** characters must be converted to UTF-8 prior to passing them into ** sqlite3_open() or sqlite3_open_v2(). */ SQLITE_API int sqlite3_open( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); SQLITE_API int sqlite3_open16( const void *filename, /* Database filename (UTF-16) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); SQLITE_API int sqlite3_open_v2( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb, /* OUT: SQLite db handle */ int flags, /* Flags */ const char *zVfs /* Name of VFS module to use */ ); /* ** CAPI3REF: Error Codes And Messages ** ** ^The sqlite3_errcode() interface returns the numeric [result code] or ** [extended result code] for the most recent failed sqlite3_* API call ** associated with a [database connection]. If a prior API call failed ** but the most recent API call succeeded, the return value from ** sqlite3_errcode() is undefined. ^The sqlite3_extended_errcode() ** interface is the same except that it always returns the ** [extended result code] even when extended result codes are ** disabled. ** ** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language ** text that describes the error, as either UTF-8 or UTF-16 respectively. ** ^(Memory to hold the error message string is managed internally. ** The application does not need to worry about freeing the result. ** However, the error string might be overwritten or deallocated by ** subsequent calls to other SQLite interface functions.)^ ** ** When the serialized [threading mode] is in use, it might be the ** case that a second error occurs on a separate thread in between ** the time of the first error and the call to these interfaces. ** When that happens, the second error will be reported since these ** interfaces always report the most recent result. To avoid ** this, each thread can obtain exclusive use of the [database connection] D ** by invoking [sqlite3_mutex_enter]([sqlite3_db_mutex](D)) before beginning ** to use D and invoking [sqlite3_mutex_leave]([sqlite3_db_mutex](D)) after ** all calls to the interfaces listed here are completed. ** ** If an interface fails with SQLITE_MISUSE, that means the interface ** was invoked incorrectly by the application. In that case, the ** error code and message may or may not be set. */ SQLITE_API int sqlite3_errcode(sqlite3 *db); SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); SQLITE_API const char *sqlite3_errmsg(sqlite3*); SQLITE_API const void *sqlite3_errmsg16(sqlite3*); /* ** CAPI3REF: SQL Statement Object ** KEYWORDS: {prepared statement} {prepared statements} ** ** An instance of this object represents a single SQL statement. ** This object is variously known as a "prepared statement" or a ** "compiled SQL statement" or simply as a "statement". ** ** The life of a statement object goes something like this: ** **
      **
    1. Create the object using [sqlite3_prepare_v2()] or a related ** function. **
    2. Bind values to [host parameters] using the sqlite3_bind_*() ** interfaces. **
    3. Run the SQL by calling [sqlite3_step()] one or more times. **
    4. Reset the statement using [sqlite3_reset()] then go back ** to step 2. Do this zero or more times. **
    5. Destroy the object using [sqlite3_finalize()]. **
    ** ** Refer to documentation on individual methods above for additional ** information. */ typedef struct sqlite3_stmt sqlite3_stmt; /* ** CAPI3REF: Run-time Limits ** ** ^(This interface allows the size of various constructs to be limited ** on a connection by connection basis. The first parameter is the ** [database connection] whose limit is to be set or queried. The ** second parameter is one of the [limit categories] that define a ** class of constructs to be size limited. The third parameter is the ** new limit for that construct. The function returns the old limit.)^ ** ** ^If the new limit is a negative number, the limit is unchanged. ** ^(For the limit category of SQLITE_LIMIT_XYZ there is a ** [limits | hard upper bound] ** set by a compile-time C preprocessor macro named ** [limits | SQLITE_MAX_XYZ]. ** (The "_LIMIT_" in the name is changed to "_MAX_".))^ ** ^Attempts to increase a limit above its hard upper bound are ** silently truncated to the hard upper bound. ** ** Run-time limits are intended for use in applications that manage ** both their own internal database and also databases that are controlled ** by untrusted external sources. An example application might be a ** web browser that has its own databases for storing history and ** separate databases controlled by JavaScript applications downloaded ** off the Internet. The internal databases can be given the ** large, default limits. Databases managed by external sources can ** be given much smaller limits designed to prevent a denial of service ** attack. Developers might also want to use the [sqlite3_set_authorizer()] ** interface to further control untrusted SQL. The size of the database ** created by an untrusted script can be contained using the ** [max_page_count] [PRAGMA]. ** ** New run-time limit categories may be added in future releases. */ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); /* ** CAPI3REF: Run-Time Limit Categories ** KEYWORDS: {limit category} {*limit categories} ** ** These constants define various performance limits ** that can be lowered at run-time using [sqlite3_limit()]. ** The synopsis of the meanings of the various limits is shown below. ** Additional information is available at [limits | Limits in SQLite]. ** **
    ** ^(
    SQLITE_LIMIT_LENGTH
    **
    The maximum size of any string or BLOB or table row.
    )^ ** ** ^(
    SQLITE_LIMIT_SQL_LENGTH
    **
    The maximum length of an SQL statement, in bytes.
    )^ ** ** ^(
    SQLITE_LIMIT_COLUMN
    **
    The maximum number of columns in a table definition or in the ** result set of a [SELECT] or the maximum number of columns in an index ** or in an ORDER BY or GROUP BY clause.
    )^ ** ** ^(
    SQLITE_LIMIT_EXPR_DEPTH
    **
    The maximum depth of the parse tree on any expression.
    )^ ** ** ^(
    SQLITE_LIMIT_COMPOUND_SELECT
    **
    The maximum number of terms in a compound SELECT statement.
    )^ ** ** ^(
    SQLITE_LIMIT_VDBE_OP
    **
    The maximum number of instructions in a virtual machine program ** used to implement an SQL statement.
    )^ ** ** ^(
    SQLITE_LIMIT_FUNCTION_ARG
    **
    The maximum number of arguments on a function.
    )^ ** ** ^(
    SQLITE_LIMIT_ATTACHED
    **
    The maximum number of [ATTACH | attached databases].)^
    ** ** ^(
    SQLITE_LIMIT_LIKE_PATTERN_LENGTH
    **
    The maximum length of the pattern argument to the [LIKE] or ** [GLOB] operators.
    )^ ** ** ^(
    SQLITE_LIMIT_VARIABLE_NUMBER
    **
    The maximum number of variables in an SQL statement that can ** be bound.
    )^ ** ** ^(
    SQLITE_LIMIT_TRIGGER_DEPTH
    **
    The maximum depth of recursion for triggers.
    )^ **
    */ #define SQLITE_LIMIT_LENGTH 0 #define SQLITE_LIMIT_SQL_LENGTH 1 #define SQLITE_LIMIT_COLUMN 2 #define SQLITE_LIMIT_EXPR_DEPTH 3 #define SQLITE_LIMIT_COMPOUND_SELECT 4 #define SQLITE_LIMIT_VDBE_OP 5 #define SQLITE_LIMIT_FUNCTION_ARG 6 #define SQLITE_LIMIT_ATTACHED 7 #define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 #define SQLITE_LIMIT_VARIABLE_NUMBER 9 #define SQLITE_LIMIT_TRIGGER_DEPTH 10 /* ** CAPI3REF: Compiling An SQL Statement ** KEYWORDS: {SQL statement compiler} ** ** To execute an SQL query, it must first be compiled into a byte-code ** program using one of these routines. ** ** The first argument, "db", is a [database connection] obtained from a ** prior successful call to [sqlite3_open()], [sqlite3_open_v2()] or ** [sqlite3_open16()]. The database connection must not have been closed. ** ** The second argument, "zSql", is the statement to be compiled, encoded ** as either UTF-8 or UTF-16. The sqlite3_prepare() and sqlite3_prepare_v2() ** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2() ** use UTF-16. ** ** ^If the nByte argument is less than zero, then zSql is read up to the ** first zero terminator. ^If nByte is non-negative, then it is the maximum ** number of bytes read from zSql. ^When nByte is non-negative, the ** zSql string ends at either the first '\000' or '\u0000' character or ** the nByte-th byte, whichever comes first. If the caller knows ** that the supplied string is nul-terminated, then there is a small ** performance advantage to be gained by passing an nByte parameter that ** is equal to the number of bytes in the input string including ** the nul-terminator bytes. ** ** ^If pzTail is not NULL then *pzTail is made to point to the first byte ** past the end of the first SQL statement in zSql. These routines only ** compile the first statement in zSql, so *pzTail is left pointing to ** what remains uncompiled. ** ** ^*ppStmt is left pointing to a compiled [prepared statement] that can be ** executed using [sqlite3_step()]. ^If there is an error, *ppStmt is set ** to NULL. ^If the input text contains no SQL (if the input is an empty ** string or a comment) then *ppStmt is set to NULL. ** The calling procedure is responsible for deleting the compiled ** SQL statement using [sqlite3_finalize()] after it has finished with it. ** ppStmt may not be NULL. ** ** ^On success, the sqlite3_prepare() family of routines return [SQLITE_OK]; ** otherwise an [error code] is returned. ** ** The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are ** recommended for all new programs. The two older interfaces are retained ** for backwards compatibility, but their use is discouraged. ** ^In the "v2" interfaces, the prepared statement ** that is returned (the [sqlite3_stmt] object) contains a copy of the ** original SQL text. This causes the [sqlite3_step()] interface to ** behave differently in three ways: ** **
      **
    1. ** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it ** always used to do, [sqlite3_step()] will automatically recompile the SQL ** statement and try to run it again. ^If the schema has changed in ** a way that makes the statement no longer valid, [sqlite3_step()] will still ** return [SQLITE_SCHEMA]. But unlike the legacy behavior, [SQLITE_SCHEMA] is ** now a fatal error. Calling [sqlite3_prepare_v2()] again will not make the ** error go away. Note: use [sqlite3_errmsg()] to find the text ** of the parsing error that results in an [SQLITE_SCHEMA] return. **
    2. ** **
    3. ** ^When an error occurs, [sqlite3_step()] will return one of the detailed ** [error codes] or [extended error codes]. ^The legacy behavior was that ** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code ** and the application would have to make a second call to [sqlite3_reset()] ** in order to find the underlying cause of the problem. With the "v2" prepare ** interfaces, the underlying reason for the error is returned immediately. **
    4. ** **
    5. ** ^If the value of a [parameter | host parameter] in the WHERE clause might ** change the query plan for a statement, then the statement may be ** automatically recompiled (as if there had been a schema change) on the first ** [sqlite3_step()] call following any change to the ** [sqlite3_bind_text | bindings] of the [parameter]. **
    6. **
    */ SQLITE_API int sqlite3_prepare( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); SQLITE_API int sqlite3_prepare_v2( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); SQLITE_API int sqlite3_prepare16( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const void **pzTail /* OUT: Pointer to unused portion of zSql */ ); SQLITE_API int sqlite3_prepare16_v2( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const void **pzTail /* OUT: Pointer to unused portion of zSql */ ); /* ** CAPI3REF: Retrieving Statement SQL ** ** ^This interface can be used to retrieve a saved copy of the original ** SQL text used to create a [prepared statement] if that statement was ** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. */ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); /* ** CAPI3REF: Dynamically Typed Value Object ** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} ** ** SQLite uses the sqlite3_value object to represent all values ** that can be stored in a database table. SQLite uses dynamic typing ** for the values it stores. ^Values stored in sqlite3_value objects ** can be integers, floating point values, strings, BLOBs, or NULL. ** ** An sqlite3_value object may be either "protected" or "unprotected". ** Some interfaces require a protected sqlite3_value. Other interfaces ** will accept either a protected or an unprotected sqlite3_value. ** Every interface that accepts sqlite3_value arguments specifies ** whether or not it requires a protected sqlite3_value. ** ** The terms "protected" and "unprotected" refer to whether or not ** a mutex is held. A internal mutex is held for a protected ** sqlite3_value object but no mutex is held for an unprotected ** sqlite3_value object. If SQLite is compiled to be single-threaded ** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) ** or if SQLite is run in one of reduced mutex modes ** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD] ** then there is no distinction between protected and unprotected ** sqlite3_value objects and they can be used interchangeably. However, ** for maximum code portability it is recommended that applications ** still make the distinction between between protected and unprotected ** sqlite3_value objects even when not strictly required. ** ** ^The sqlite3_value objects that are passed as parameters into the ** implementation of [application-defined SQL functions] are protected. ** ^The sqlite3_value object returned by ** [sqlite3_column_value()] is unprotected. ** Unprotected sqlite3_value objects may only be used with ** [sqlite3_result_value()] and [sqlite3_bind_value()]. ** The [sqlite3_value_blob | sqlite3_value_type()] family of ** interfaces require protected sqlite3_value objects. */ typedef struct Mem sqlite3_value; /* ** CAPI3REF: SQL Function Context Object ** ** The context in which an SQL function executes is stored in an ** sqlite3_context object. ^A pointer to an sqlite3_context object ** is always first parameter to [application-defined SQL functions]. ** The application-defined SQL function implementation will pass this ** pointer through into calls to [sqlite3_result_int | sqlite3_result()], ** [sqlite3_aggregate_context()], [sqlite3_user_data()], ** [sqlite3_context_db_handle()], [sqlite3_get_auxdata()], ** and/or [sqlite3_set_auxdata()]. */ typedef struct sqlite3_context sqlite3_context; /* ** CAPI3REF: Binding Values To Prepared Statements ** KEYWORDS: {host parameter} {host parameters} {host parameter name} ** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding} ** ** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants, ** literals may be replaced by a [parameter] that matches one of following ** templates: ** **
      **
    • ? **
    • ?NNN **
    • :VVV **
    • @VVV **
    • $VVV **
    ** ** In the templates above, NNN represents an integer literal, ** and VVV represents an alphanumeric identifer.)^ ^The values of these ** parameters (also called "host parameter names" or "SQL parameters") ** can be set using the sqlite3_bind_*() routines defined here. ** ** ^The first argument to the sqlite3_bind_*() routines is always ** a pointer to the [sqlite3_stmt] object returned from ** [sqlite3_prepare_v2()] or its variants. ** ** ^The second argument is the index of the SQL parameter to be set. ** ^The leftmost SQL parameter has an index of 1. ^When the same named ** SQL parameter is used more than once, second and subsequent ** occurrences have the same index as the first occurrence. ** ^The index for named parameters can be looked up using the ** [sqlite3_bind_parameter_index()] API if desired. ^The index ** for "?NNN" parameters is the value of NNN. ** ^The NNN value must be between 1 and the [sqlite3_limit()] ** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999). ** ** ^The third argument is the value to bind to the parameter. ** ** ^(In those routines that have a fourth argument, its value is the ** number of bytes in the parameter. To be clear: the value is the ** number of bytes in the value, not the number of characters.)^ ** ^If the fourth parameter is negative, the length of the string is ** the number of bytes up to the first zero terminator. ** ** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and ** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or ** string after SQLite has finished with it. ^If the fifth argument is ** the special value [SQLITE_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. ** ^If the fifth argument has the value [SQLITE_TRANSIENT], then ** SQLite makes its own private copy of the data immediately, before ** the sqlite3_bind_*() routine returns. ** ** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that ** is filled with zeroes. ^A zeroblob uses a fixed amount of memory ** (just an integer to hold its size) while it is being processed. ** Zeroblobs are intended to serve as placeholders for BLOBs whose ** content is later written using ** [sqlite3_blob_open | incremental BLOB I/O] routines. ** ^A negative value for the zeroblob results in a zero-length BLOB. ** ** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer ** for the [prepared statement] or with a prepared statement for which ** [sqlite3_step()] has been called more recently than [sqlite3_reset()], ** then the call will return [SQLITE_MISUSE]. If any sqlite3_bind_() ** routine is passed a [prepared statement] that has been finalized, the ** result is undefined and probably harmful. ** ** ^Bindings are not cleared by the [sqlite3_reset()] routine. ** ^Unbound parameters are interpreted as NULL. ** ** ^The sqlite3_bind_* routines return [SQLITE_OK] on success or an ** [error code] if anything goes wrong. ** ^[SQLITE_RANGE] is returned if the parameter ** index is out of range. ^[SQLITE_NOMEM] is returned if malloc() fails. ** ** See also: [sqlite3_bind_parameter_count()], ** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()]. */ SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double); SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int); SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int); SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*)); SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); /* ** CAPI3REF: Number Of SQL Parameters ** ** ^This routine can be used to find the number of [SQL parameters] ** in a [prepared statement]. SQL parameters are tokens of the ** form "?", "?NNN", ":AAA", "$AAA", or "@AAA" that serve as ** placeholders for values that are [sqlite3_bind_blob | bound] ** to the parameters at a later time. ** ** ^(This routine actually returns the index of the largest (rightmost) ** parameter. For all forms except ?NNN, this will correspond to the ** number of unique parameters. If parameters of the ?NNN form are used, ** there may be gaps in the list.)^ ** ** See also: [sqlite3_bind_blob|sqlite3_bind()], ** [sqlite3_bind_parameter_name()], and ** [sqlite3_bind_parameter_index()]. */ SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); /* ** CAPI3REF: Name Of A Host Parameter ** ** ^The sqlite3_bind_parameter_name(P,N) interface returns ** the name of the N-th [SQL parameter] in the [prepared statement] P. ** ^(SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA" ** have a name which is the string "?NNN" or ":AAA" or "@AAA" or "$AAA" ** respectively. ** In other words, the initial ":" or "$" or "@" or "?" ** is included as part of the name.)^ ** ^Parameters of the form "?" without a following integer have no name ** and are referred to as "nameless" or "anonymous parameters". ** ** ^The first host parameter has an index of 1, not 0. ** ** ^If the value N is out of range or if the N-th parameter is ** nameless, then NULL is returned. ^The returned string is ** always in UTF-8 encoding even if the named parameter was ** originally specified as UTF-16 in [sqlite3_prepare16()] or ** [sqlite3_prepare16_v2()]. ** ** See also: [sqlite3_bind_blob|sqlite3_bind()], ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. */ SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); /* ** CAPI3REF: Index Of A Parameter With A Given Name ** ** ^Return the index of an SQL parameter given its name. ^The ** index value returned is suitable for use as the second ** parameter to [sqlite3_bind_blob|sqlite3_bind()]. ^A zero ** is returned if no matching parameter is found. ^The parameter ** name must be given in UTF-8 even if the original statement ** was prepared from UTF-16 text using [sqlite3_prepare16_v2()]. ** ** See also: [sqlite3_bind_blob|sqlite3_bind()], ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. */ SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); /* ** CAPI3REF: Reset All Bindings On A Prepared Statement ** ** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset ** the [sqlite3_bind_blob | bindings] on a [prepared statement]. ** ^Use this routine to reset all host parameters to NULL. */ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); /* ** CAPI3REF: Number Of Columns In A Result Set ** ** ^Return the number of columns in the result set returned by the ** [prepared statement]. ^This routine returns 0 if pStmt is an SQL ** statement that does not return data (for example an [UPDATE]). */ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Column Names In A Result Set ** ** ^These routines return the name assigned to a particular column ** in the result set of a [SELECT] statement. ^The sqlite3_column_name() ** interface returns a pointer to a zero-terminated UTF-8 string ** and sqlite3_column_name16() returns a pointer to a zero-terminated ** UTF-16 string. ^The first parameter is the [prepared statement] ** that implements the [SELECT] statement. ^The second parameter is the ** column number. ^The leftmost column is number 0. ** ** ^The returned string pointer is valid until either the [prepared statement] ** is destroyed by [sqlite3_finalize()] or until the next call to ** sqlite3_column_name() or sqlite3_column_name16() on the same column. ** ** ^If sqlite3_malloc() fails during the processing of either routine ** (for example during a conversion from UTF-8 to UTF-16) then a ** NULL pointer is returned. ** ** ^The name of a result column is the value of the "AS" clause for ** that column, if there is an AS clause. If there is no AS clause ** then the name of the column is unspecified and may change from ** one release of SQLite to the next. */ SQLITE_API const char *sqlite3_column_name(sqlite3_stmt*, int N); SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); /* ** CAPI3REF: Source Of Data In A Query Result ** ** ^These routines provide a means to determine the database, table, and ** table column that is the origin of a particular result column in ** [SELECT] statement. ** ^The name of the database or table or column can be returned as ** either a UTF-8 or UTF-16 string. ^The _database_ routines return ** the database name, the _table_ routines return the table name, and ** the origin_ routines return the column name. ** ^The returned string is valid until the [prepared statement] is destroyed ** using [sqlite3_finalize()] or until the same information is requested ** again in a different encoding. ** ** ^The names returned are the original un-aliased names of the ** database, table, and column. ** ** ^The first argument to these interfaces is a [prepared statement]. ** ^These functions return information about the Nth result column returned by ** the statement, where N is the second function argument. ** ^The left-most column is column 0 for these routines. ** ** ^If the Nth column returned by the statement is an expression or ** subquery and is not a column value, then all of these functions return ** NULL. ^These routine might also return NULL if a memory allocation error ** occurs. ^Otherwise, they return the name of the attached database, table, ** or column that query result column was extracted from. ** ** ^As with all other SQLite APIs, those whose names end with "16" return ** UTF-16 encoded strings and the other functions return UTF-8. ** ** ^These APIs are only available if the library was compiled with the ** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol. ** ** If two or more threads call one or more of these routines against the same ** prepared statement and column at the same time then the results are ** undefined. ** ** If two or more threads call one or more ** [sqlite3_column_database_name | column metadata interfaces] ** for the same [prepared statement] and result column ** at the same time then the results are undefined. */ SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt*,int); SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*,int); SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*,int); SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*,int); SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*,int); SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); /* ** CAPI3REF: Declared Datatype Of A Query Result ** ** ^(The first parameter is a [prepared statement]. ** If this statement is a [SELECT] statement and the Nth column of the ** returned result set of that [SELECT] is a table column (not an ** expression or subquery) then the declared type of the table ** column is returned.)^ ^If the Nth column of the result set is an ** expression or subquery, then a NULL pointer is returned. ** ^The returned string is always UTF-8 encoded. ** ** ^(For example, given the database schema: ** ** CREATE TABLE t1(c1 VARIANT); ** ** and the following statement to be compiled: ** ** SELECT c1 + 1, c1 FROM t1; ** ** this routine would return the string "VARIANT" for the second result ** column (i==1), and a NULL pointer for the first result column (i==0).)^ ** ** ^SQLite uses dynamic run-time typing. ^So just because a column ** is declared to contain a particular type does not mean that the ** data stored in that column is of the declared type. SQLite is ** strongly typed, but the typing is dynamic not static. ^Type ** is associated with individual values, not with the containers ** used to hold those values. */ SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt*,int); SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); /* ** CAPI3REF: Evaluate An SQL Statement ** ** After a [prepared statement] has been prepared using either ** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy ** interfaces [sqlite3_prepare()] or [sqlite3_prepare16()], this function ** must be called one or more times to evaluate the statement. ** ** The details of the behavior of the sqlite3_step() interface depend ** on whether the statement was prepared using the newer "v2" interface ** [sqlite3_prepare_v2()] and [sqlite3_prepare16_v2()] or the older legacy ** interface [sqlite3_prepare()] and [sqlite3_prepare16()]. The use of the ** new "v2" interface is recommended for new applications but the legacy ** interface will continue to be supported. ** ** ^In the legacy interface, the return value will be either [SQLITE_BUSY], ** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE]. ** ^With the "v2" interface, any of the other [result codes] or ** [extended result codes] might be returned as well. ** ** ^[SQLITE_BUSY] means that the database engine was unable to acquire the ** database locks it needs to do its job. ^If the statement is a [COMMIT] ** or occurs outside of an explicit transaction, then you can retry the ** statement. If the statement is not a [COMMIT] and occurs within a ** explicit transaction then you should rollback the transaction before ** continuing. ** ** ^[SQLITE_DONE] means that the statement has finished executing ** successfully. sqlite3_step() should not be called again on this virtual ** machine without first calling [sqlite3_reset()] to reset the virtual ** machine back to its initial state. ** ** ^If the SQL statement being executed returns any data, then [SQLITE_ROW] ** is returned each time a new row of data is ready for processing by the ** caller. The values may be accessed using the [column access functions]. ** sqlite3_step() is called again to retrieve the next row of data. ** ** ^[SQLITE_ERROR] means that a run-time error (such as a constraint ** violation) has occurred. sqlite3_step() should not be called again on ** the VM. More information may be found by calling [sqlite3_errmsg()]. ** ^With the legacy interface, a more specific error code (for example, ** [SQLITE_INTERRUPT], [SQLITE_SCHEMA], [SQLITE_CORRUPT], and so forth) ** can be obtained by calling [sqlite3_reset()] on the ** [prepared statement]. ^In the "v2" interface, ** the more specific error code is returned directly by sqlite3_step(). ** ** [SQLITE_MISUSE] means that the this routine was called inappropriately. ** Perhaps it was called on a [prepared statement] that has ** already been [sqlite3_finalize | finalized] or on one that had ** previously returned [SQLITE_ERROR] or [SQLITE_DONE]. Or it could ** be the case that the same database connection is being used by two or ** more threads at the same moment in time. ** ** Goofy Interface Alert: In the legacy interface, the sqlite3_step() ** API always returns a generic error code, [SQLITE_ERROR], following any ** error other than [SQLITE_BUSY] and [SQLITE_MISUSE]. You must call ** [sqlite3_reset()] or [sqlite3_finalize()] in order to find one of the ** specific [error codes] that better describes the error. ** We admit that this is a goofy design. The problem has been fixed ** with the "v2" interface. If you prepare all of your SQL statements ** using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] instead ** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces, ** then the more specific [error codes] are returned directly ** by sqlite3_step(). The use of the "v2" interface is recommended. */ SQLITE_API int sqlite3_step(sqlite3_stmt*); /* ** CAPI3REF: Number of columns in a result set ** ** ^The sqlite3_data_count(P) the number of columns in the ** of the result set of [prepared statement] P. */ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); /* ** CAPI3REF: Fundamental Datatypes ** KEYWORDS: SQLITE_TEXT ** ** ^(Every value in SQLite has one of five fundamental datatypes: ** **
      **
    • 64-bit signed integer **
    • 64-bit IEEE floating point number **
    • string **
    • BLOB **
    • NULL **
    )^ ** ** These constants are codes for each of those types. ** ** Note that the SQLITE_TEXT constant was also used in SQLite version 2 ** for a completely different meaning. Software that links against both ** SQLite version 2 and SQLite version 3 should use SQLITE3_TEXT, not ** SQLITE_TEXT. */ #define SQLITE_INTEGER 1 #define SQLITE_FLOAT 2 #define SQLITE_BLOB 4 #define SQLITE_NULL 5 #ifdef SQLITE_TEXT # undef SQLITE_TEXT #else # define SQLITE_TEXT 3 #endif #define SQLITE3_TEXT 3 /* ** CAPI3REF: Result Values From A Query ** KEYWORDS: {column access functions} ** ** These routines form the "result set" interface. ** ** ^These routines return information about a single column of the current ** result row of a query. ^In every case the first argument is a pointer ** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*] ** that was returned from [sqlite3_prepare_v2()] or one of its variants) ** and the second argument is the index of the column for which information ** should be returned. ^The leftmost column of the result set has the index 0. ** ^The number of columns in the result can be determined using ** [sqlite3_column_count()]. ** ** If the SQL statement does not currently point to a valid row, or if the ** column index is out of range, the result is undefined. ** These routines may only be called when the most recent call to ** [sqlite3_step()] has returned [SQLITE_ROW] and neither ** [sqlite3_reset()] nor [sqlite3_finalize()] have been called subsequently. ** If any of these routines are called after [sqlite3_reset()] or ** [sqlite3_finalize()] or after [sqlite3_step()] has returned ** something other than [SQLITE_ROW], the results are undefined. ** If [sqlite3_step()] or [sqlite3_reset()] or [sqlite3_finalize()] ** are called from a different thread while any of these routines ** are pending, then the results are undefined. ** ** ^The sqlite3_column_type() routine returns the ** [SQLITE_INTEGER | datatype code] for the initial data type ** of the result column. ^The returned value is one of [SQLITE_INTEGER], ** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL]. The value ** returned by sqlite3_column_type() is only meaningful if no type ** conversions have occurred as described below. After a type conversion, ** the value returned by sqlite3_column_type() is undefined. Future ** versions of SQLite may change the behavior of sqlite3_column_type() ** following a type conversion. ** ** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() ** routine returns the number of bytes in that BLOB or string. ** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts ** the string to UTF-8 and then returns the number of bytes. ** ^If the result is a numeric value then sqlite3_column_bytes() uses ** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns ** the number of bytes in that string. ** ^The value returned does not include the zero terminator at the end ** of the string. ^For clarity: the value returned is the number of ** bytes in the string, not the number of characters. ** ** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(), ** even empty strings, are always zero terminated. ^The return ** value from sqlite3_column_blob() for a zero-length BLOB is an arbitrary ** pointer, possibly even a NULL pointer. ** ** ^The sqlite3_column_bytes16() routine is similar to sqlite3_column_bytes() ** but leaves the result in UTF-16 in native byte order instead of UTF-8. ** ^The zero terminator is not included in this count. ** ** ^The object returned by [sqlite3_column_value()] is an ** [unprotected sqlite3_value] object. An unprotected sqlite3_value object ** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()]. ** If the [unprotected sqlite3_value] object returned by ** [sqlite3_column_value()] is used in any other way, including calls ** to routines like [sqlite3_value_int()], [sqlite3_value_text()], ** or [sqlite3_value_bytes()], then the behavior is undefined. ** ** These routines attempt to convert the value where appropriate. ^For ** example, if the internal representation is FLOAT and a text result ** is requested, [sqlite3_snprintf()] is used internally to perform the ** conversion automatically. ^(The following table details the conversions ** that are applied: ** **
    ** **
    Internal
    Type
    Requested
    Type
    Conversion ** **
    NULL INTEGER Result is 0 **
    NULL FLOAT Result is 0.0 **
    NULL TEXT Result is NULL pointer **
    NULL BLOB Result is NULL pointer **
    INTEGER FLOAT Convert from integer to float **
    INTEGER TEXT ASCII rendering of the integer **
    INTEGER BLOB Same as INTEGER->TEXT **
    FLOAT INTEGER Convert from float to integer **
    FLOAT TEXT ASCII rendering of the float **
    FLOAT BLOB Same as FLOAT->TEXT **
    TEXT INTEGER Use atoi() **
    TEXT FLOAT Use atof() **
    TEXT BLOB No change **
    BLOB INTEGER Convert to TEXT then use atoi() **
    BLOB FLOAT Convert to TEXT then use atof() **
    BLOB TEXT Add a zero terminator if needed **
    **
    )^ ** ** The table above makes reference to standard C library functions atoi() ** and atof(). SQLite does not really use these functions. It has its ** own equivalent internal routines. The atoi() and atof() names are ** used in the table for brevity and because they are familiar to most ** C programmers. ** ** ^Note that when type conversions occur, pointers returned by prior ** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or ** sqlite3_column_text16() may be invalidated. ** ^(Type conversions and pointer invalidations might occur ** in the following cases: ** **
      **
    • The initial content is a BLOB and sqlite3_column_text() or ** sqlite3_column_text16() is called. A zero-terminator might ** need to be added to the string.
    • **
    • The initial content is UTF-8 text and sqlite3_column_bytes16() or ** sqlite3_column_text16() is called. The content must be converted ** to UTF-16.
    • **
    • The initial content is UTF-16 text and sqlite3_column_bytes() or ** sqlite3_column_text() is called. The content must be converted ** to UTF-8.
    • **
    )^ ** ** ^Conversions between UTF-16be and UTF-16le are always done in place and do ** not invalidate a prior pointer, though of course the content of the buffer ** that the prior pointer points to will have been modified. Other kinds ** of conversion are done in place when it is possible, but sometimes they ** are not possible and in those cases prior pointers are invalidated. ** ** ^(The safest and easiest to remember policy is to invoke these routines ** in one of the following ways: ** **
      **
    • sqlite3_column_text() followed by sqlite3_column_bytes()
    • **
    • sqlite3_column_blob() followed by sqlite3_column_bytes()
    • **
    • sqlite3_column_text16() followed by sqlite3_column_bytes16()
    • **
    )^ ** ** In other words, you should call sqlite3_column_text(), ** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result ** into the desired format, then invoke sqlite3_column_bytes() or ** sqlite3_column_bytes16() to find the size of the result. Do not mix calls ** to sqlite3_column_text() or sqlite3_column_blob() with calls to ** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() ** with calls to sqlite3_column_bytes(). ** ** ^The pointers returned are valid until a type conversion occurs as ** described above, or until [sqlite3_step()] or [sqlite3_reset()] or ** [sqlite3_finalize()] is called. ^The memory space used to hold strings ** and BLOBs is freed automatically. Do not pass the pointers returned ** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into ** [sqlite3_free()]. ** ** ^(If a memory allocation error occurs during the evaluation of any ** of these routines, a default value is returned. The default value ** is either the integer 0, the floating point number 0.0, or a NULL ** pointer. Subsequent calls to [sqlite3_errcode()] will return ** [SQLITE_NOMEM].)^ */ SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol); SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol); SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol); SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); /* ** CAPI3REF: Destroy A Prepared Statement Object ** ** ^The sqlite3_finalize() function is called to delete a [prepared statement]. ** ^If the statement was executed successfully or not executed at all, then ** SQLITE_OK is returned. ^If execution of the statement failed then an ** [error code] or [extended error code] is returned. ** ** ^This routine can be called at any point during the execution of the ** [prepared statement]. ^If the virtual machine has not ** completed execution when this routine is called, that is like ** encountering an error or an [sqlite3_interrupt | interrupt]. ** ^Incomplete updates may be rolled back and transactions canceled, ** depending on the circumstances, and the ** [error code] returned will be [SQLITE_ABORT]. */ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); /* ** CAPI3REF: Reset A Prepared Statement Object ** ** The sqlite3_reset() function is called to reset a [prepared statement] ** object back to its initial state, ready to be re-executed. ** ^Any SQL statement variables that had values bound to them using ** the [sqlite3_bind_blob | sqlite3_bind_*() API] retain their values. ** Use [sqlite3_clear_bindings()] to reset the bindings. ** ** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S ** back to the beginning of its program. ** ** ^If the most recent call to [sqlite3_step(S)] for the ** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], ** or if [sqlite3_step(S)] has never before been called on S, ** then [sqlite3_reset(S)] returns [SQLITE_OK]. ** ** ^If the most recent call to [sqlite3_step(S)] for the ** [prepared statement] S indicated an error, then ** [sqlite3_reset(S)] returns an appropriate [error code]. ** ** ^The [sqlite3_reset(S)] interface does not change the values ** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. */ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); /* ** CAPI3REF: Create Or Redefine SQL Functions ** KEYWORDS: {function creation routines} ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} ** ** ^These two functions (collectively known as "function creation routines") ** are used to add SQL functions or aggregates or to redefine the behavior ** of existing SQL functions or aggregates. The only difference between the ** two is that the second parameter, the name of the (scalar) function or ** aggregate, is encoded in UTF-8 for sqlite3_create_function() and UTF-16 ** for sqlite3_create_function16(). ** ** ^The first parameter is the [database connection] to which the SQL ** function is to be added. ^If an application uses more than one database ** connection then application-defined SQL functions must be added ** to each database connection separately. ** ** The second parameter is the name of the SQL function to be created or ** redefined. ^The length of the name is limited to 255 bytes, exclusive of ** the zero-terminator. Note that the name length limit is in bytes, not ** characters. ^Any attempt to create a function with a longer name ** will result in [SQLITE_ERROR] being returned. ** ** ^The third parameter (nArg) ** is the number of arguments that the SQL function or ** aggregate takes. ^If this parameter is -1, then the SQL function or ** aggregate may take any number of arguments between 0 and the limit ** set by [sqlite3_limit]([SQLITE_LIMIT_FUNCTION_ARG]). If the third ** parameter is less than -1 or greater than 127 then the behavior is ** undefined. ** ** The fourth parameter, eTextRep, specifies what ** [SQLITE_UTF8 | text encoding] this SQL function prefers for ** its parameters. Any SQL function implementation should be able to work ** work with UTF-8, UTF-16le, or UTF-16be. But some implementations may be ** more efficient with one encoding than another. ^An application may ** invoke sqlite3_create_function() or sqlite3_create_function16() multiple ** times with the same function but with different values of eTextRep. ** ^When multiple implementations of the same function are available, SQLite ** will pick the one that involves the least amount of data conversion. ** If there is only a single implementation which does not care what text ** encoding is used, then the fourth argument should be [SQLITE_ANY]. ** ** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** function can gain access to this pointer using [sqlite3_user_data()].)^ ** ** The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are ** pointers to C-language functions that implement the SQL function or ** aggregate. ^A scalar SQL function requires an implementation of the xFunc ** callback only; NULL pointers should be passed as the xStep and xFinal ** parameters. ^An aggregate SQL function requires an implementation of xStep ** and xFinal and NULL should be passed for xFunc. ^To delete an existing ** SQL function or aggregate, pass NULL for all three function callbacks. ** ** ^It is permitted to register multiple implementations of the same ** functions with the same name but with either differing numbers of ** arguments or differing preferred text encodings. ^SQLite will use ** the implementation that most closely matches the way in which the ** SQL function is used. ^A function implementation with a non-negative ** nArg parameter is a better match than a function implementation with ** a negative nArg. ^A function where the preferred text encoding ** matches the database encoding is a better ** match than a function where the encoding is different. ** ^A function where the encoding difference is between UTF16le and UTF16be ** is a closer match than a function where the encoding difference is ** between UTF8 and UTF16. ** ** ^Built-in functions may be overloaded by new application-defined functions. ** ^The first application-defined function with a given name overrides all ** built-in functions in the same [database connection] with the same name. ** ^Subsequent application-defined functions of the same name only override ** prior application-defined functions that are an exact match for the ** number of parameters and preferred encoding. ** ** ^An application-defined function is permitted to call other ** SQLite interfaces. However, such calls must not ** close the database connection nor finalize or reset the prepared ** statement in which the function is running. */ SQLITE_API int sqlite3_create_function( sqlite3 *db, const char *zFunctionName, int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); SQLITE_API int sqlite3_create_function16( sqlite3 *db, const void *zFunctionName, int nArg, int eTextRep, void *pApp, void (*xFunc)(sqlite3_context*,int,sqlite3_value**), void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); /* ** CAPI3REF: Text Encodings ** ** These constant define integer codes that represent the various ** text encodings supported by SQLite. */ #define SQLITE_UTF8 1 #define SQLITE_UTF16LE 2 #define SQLITE_UTF16BE 3 #define SQLITE_UTF16 4 /* Use native byte order */ #define SQLITE_ANY 5 /* sqlite3_create_function only */ #define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */ /* ** CAPI3REF: Deprecated Functions ** DEPRECATED ** ** These functions are [deprecated]. In order to maintain ** backwards compatibility with older code, these functions continue ** to be supported. However, new applications should avoid ** the use of these functions. To help encourage people to avoid ** using these functions, we are not going to tell you what they do. */ #ifndef SQLITE_OMIT_DEPRECATED SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*); SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*); SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void); SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void); SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void*,sqlite3_int64); #endif /* ** CAPI3REF: Obtaining SQL Function Parameter Values ** ** The C-language implementation of SQL functions and aggregates uses ** this set of interface routines to access the parameter values on ** the function or aggregate. ** ** The xFunc (for scalar functions) or xStep (for aggregates) parameters ** to [sqlite3_create_function()] and [sqlite3_create_function16()] ** define callbacks that implement the SQL functions and aggregates. ** The 4th parameter to these callbacks is an array of pointers to ** [protected sqlite3_value] objects. There is one [sqlite3_value] object for ** each parameter to the SQL function. These routines are used to ** extract values from the [sqlite3_value] objects. ** ** These routines work only with [protected sqlite3_value] objects. ** Any attempt to use these routines on an [unprotected sqlite3_value] ** object results in undefined behavior. ** ** ^These routines work just like the corresponding [column access functions] ** except that these routines take a single [protected sqlite3_value] object ** pointer instead of a [sqlite3_stmt*] pointer and an integer column number. ** ** ^The sqlite3_value_text16() interface extracts a UTF-16 string ** in the native byte-order of the host machine. ^The ** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces ** extract UTF-16 strings as big-endian and little-endian respectively. ** ** ^(The sqlite3_value_numeric_type() interface attempts to apply ** numeric affinity to the value. This means that an attempt is ** made to convert the value to an integer or floating point. If ** such a conversion is possible without loss of information (in other ** words, if the value is a string that looks like a number) ** then the conversion is performed. Otherwise no conversion occurs. ** The [SQLITE_INTEGER | datatype] after conversion is returned.)^ ** ** Please pay particular attention to the fact that the pointer returned ** from [sqlite3_value_blob()], [sqlite3_value_text()], or ** [sqlite3_value_text16()] can be invalidated by a subsequent call to ** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], ** or [sqlite3_value_text16()]. ** ** These routines must be called from the same thread as ** the SQL function that supplied the [sqlite3_value*] parameters. */ SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); SQLITE_API int sqlite3_value_bytes(sqlite3_value*); SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); SQLITE_API double sqlite3_value_double(sqlite3_value*); SQLITE_API int sqlite3_value_int(sqlite3_value*); SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); /* ** CAPI3REF: Obtain Aggregate Function Context ** ** Implementions of aggregate SQL functions use this ** routine to allocate memory for storing their state. ** ** ^The first time the sqlite3_aggregate_context(C,N) routine is called ** for a particular aggregate function, SQLite ** allocates N of memory, zeroes out that memory, and returns a pointer ** to the new memory. ^On second and subsequent calls to ** sqlite3_aggregate_context() for the same aggregate function instance, ** the same buffer is returned. Sqlite3_aggregate_context() is normally ** called once for each invocation of the xStep callback and then one ** last time when the xFinal callback is invoked. ^(When no rows match ** an aggregate query, the xStep() callback of the aggregate function ** implementation is never called and xFinal() is called exactly once. ** In those cases, sqlite3_aggregate_context() might be called for the ** first time from within xFinal().)^ ** ** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer if N is ** less than or equal to zero or if a memory allocate error occurs. ** ** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is ** determined by the N parameter on first successful call. Changing the ** value of N in subsequent call to sqlite3_aggregate_context() within ** the same aggregate function instance will not resize the memory ** allocation.)^ ** ** ^SQLite automatically frees the memory allocated by ** sqlite3_aggregate_context() when the aggregate query concludes. ** ** The first parameter must be a copy of the ** [sqlite3_context | SQL function context] that is the first parameter ** to the xStep or xFinal callback routine that implements the aggregate ** function. ** ** This routine must be called from the same thread in which ** the aggregate SQL function is running. */ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); /* ** CAPI3REF: User Data For Functions ** ** ^The sqlite3_user_data() interface returns a copy of ** the pointer that was the pUserData parameter (the 5th parameter) ** of the [sqlite3_create_function()] ** and [sqlite3_create_function16()] routines that originally ** registered the application defined function. ** ** This routine must be called from the same thread in which ** the application-defined function is running. */ SQLITE_API void *sqlite3_user_data(sqlite3_context*); /* ** CAPI3REF: Database Connection For Functions ** ** ^The sqlite3_context_db_handle() interface returns a copy of ** the pointer to the [database connection] (the 1st parameter) ** of the [sqlite3_create_function()] ** and [sqlite3_create_function16()] routines that originally ** registered the application defined function. */ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); /* ** CAPI3REF: Function Auxiliary Data ** ** The following two functions may be used by scalar SQL functions to ** associate metadata with argument values. If the same value is passed to ** multiple invocations of the same SQL function during query execution, under ** some circumstances the associated metadata may be preserved. This may ** be used, for example, to add a regular-expression matching scalar ** function. The compiled version of the regular expression is stored as ** metadata associated with the SQL value passed as the regular expression ** pattern. The compiled regular expression can be reused on multiple ** invocations of the same function so that the original pattern string ** does not need to be recompiled on each invocation. ** ** ^The sqlite3_get_auxdata() interface returns a pointer to the metadata ** associated by the sqlite3_set_auxdata() function with the Nth argument ** value to the application-defined function. ^If no metadata has been ever ** been set for the Nth argument of the function, or if the corresponding ** function parameter has changed since the meta-data was set, ** then sqlite3_get_auxdata() returns a NULL pointer. ** ** ^The sqlite3_set_auxdata() interface saves the metadata ** pointed to by its 3rd parameter as the metadata for the N-th ** argument of the application-defined function. Subsequent ** calls to sqlite3_get_auxdata() might return this data, if it has ** not been destroyed. ** ^If it is not NULL, SQLite will invoke the destructor ** function given by the 4th parameter to sqlite3_set_auxdata() on ** the metadata when the corresponding function parameter changes ** or when the SQL statement completes, whichever comes first. ** ** SQLite is free to call the destructor and drop metadata on any ** parameter of any function at any time. ^The only guarantee is that ** the destructor will be called before the metadata is dropped. ** ** ^(In practice, metadata is preserved between function calls for ** expressions that are constant at compile time. This includes literal ** values and [parameters].)^ ** ** These routines must be called from the same thread in which ** the SQL function is running. */ SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); /* ** CAPI3REF: Constants Defining Special Destructor Behavior ** ** These are special values for the destructor that is passed in as the ** final argument to routines like [sqlite3_result_blob()]. ^If the destructor ** argument is SQLITE_STATIC, it means that the content pointer is constant ** and will never change. It does not need to be destroyed. ^The ** SQLITE_TRANSIENT value means that the content will likely change in ** the near future and that SQLite should make its own private copy of ** the content before returning. ** ** The typedef is necessary to work around problems in certain ** C++ compilers. See ticket #2191. */ typedef void (*sqlite3_destructor_type)(void*); #define SQLITE_STATIC ((sqlite3_destructor_type)0) #define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1) /* ** CAPI3REF: Setting The Result Of An SQL Function ** ** These routines are used by the xFunc or xFinal callbacks that ** implement SQL functions and aggregates. See ** [sqlite3_create_function()] and [sqlite3_create_function16()] ** for additional information. ** ** These functions work very much like the [parameter binding] family of ** functions used to bind values to host parameters in prepared statements. ** Refer to the [SQL parameter] documentation for additional information. ** ** ^The sqlite3_result_blob() interface sets the result from ** an application-defined function to be the BLOB whose content is pointed ** to by the second parameter and which is N bytes long where N is the ** third parameter. ** ** ^The sqlite3_result_zeroblob() interfaces set the result of ** the application-defined function to be a BLOB containing all zero ** bytes and N bytes in size, where N is the value of the 2nd parameter. ** ** ^The sqlite3_result_double() interface sets the result from ** an application-defined function to be a floating point value specified ** by its 2nd argument. ** ** ^The sqlite3_result_error() and sqlite3_result_error16() functions ** cause the implemented SQL function to throw an exception. ** ^SQLite uses the string pointed to by the ** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() ** as the text of an error message. ^SQLite interprets the error ** message string from sqlite3_result_error() as UTF-8. ^SQLite ** interprets the string from sqlite3_result_error16() as UTF-16 in native ** byte order. ^If the third parameter to sqlite3_result_error() ** or sqlite3_result_error16() is negative then SQLite takes as the error ** message all text up through the first zero character. ** ^If the third parameter to sqlite3_result_error() or ** sqlite3_result_error16() is non-negative then SQLite takes that many ** bytes (not characters) from the 2nd parameter as the error message. ** ^The sqlite3_result_error() and sqlite3_result_error16() ** routines make a private copy of the error message text before ** they return. Hence, the calling function can deallocate or ** modify the text after they return without harm. ** ^The sqlite3_result_error_code() function changes the error code ** returned by SQLite as a result of an error in a function. ^By default, ** the error code is SQLITE_ERROR. ^A subsequent call to sqlite3_result_error() ** or sqlite3_result_error16() resets the error code to SQLITE_ERROR. ** ** ^The sqlite3_result_toobig() interface causes SQLite to throw an error ** indicating that a string or BLOB is too long to represent. ** ** ^The sqlite3_result_nomem() interface causes SQLite to throw an error ** indicating that a memory allocation failed. ** ** ^The sqlite3_result_int() interface sets the return value ** of the application-defined function to be the 32-bit signed integer ** value given in the 2nd argument. ** ^The sqlite3_result_int64() interface sets the return value ** of the application-defined function to be the 64-bit signed integer ** value given in the 2nd argument. ** ** ^The sqlite3_result_null() interface sets the return value ** of the application-defined function to be NULL. ** ** ^The sqlite3_result_text(), sqlite3_result_text16(), ** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces ** set the return value of the application-defined function to be ** a text string which is represented as UTF-8, UTF-16 native byte order, ** UTF-16 little endian, or UTF-16 big endian, respectively. ** ^SQLite takes the text result from the application from ** the 2nd parameter of the sqlite3_result_text* interfaces. ** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** is negative, then SQLite takes result text from the 2nd parameter ** through the first zero character. ** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** is non-negative, then as many bytes (not characters) of the text ** pointed to by the 2nd parameter are taken as the application-defined ** function result. ** ^If the 4th parameter to the sqlite3_result_text* interfaces ** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that ** function as the destructor on the text or BLOB result when it has ** finished using that result. ** ^If the 4th parameter to the sqlite3_result_text* interfaces or to ** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite ** assumes that the text or BLOB result is in constant space and does not ** copy the content of the parameter nor call a destructor on the content ** when it has finished using that result. ** ^If the 4th parameter to the sqlite3_result_text* interfaces ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT ** then SQLite makes a copy of the result into space obtained from ** from [sqlite3_malloc()] before it returns. ** ** ^The sqlite3_result_value() interface sets the result of ** the application-defined function to be a copy the ** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The ** sqlite3_result_value() interface makes a copy of the [sqlite3_value] ** so that the [sqlite3_value] specified in the parameter may change or ** be deallocated after sqlite3_result_value() returns without harm. ** ^A [protected sqlite3_value] object may always be used where an ** [unprotected sqlite3_value] object is required, so either ** kind of [sqlite3_value] object can be used with this interface. ** ** If these routines are called from within the different thread ** than the one containing the application-defined function that received ** the [sqlite3_context] pointer, the results are undefined. */ SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); SQLITE_API void sqlite3_result_double(sqlite3_context*, double); SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int); SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int); SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*); SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); SQLITE_API void sqlite3_result_int(sqlite3_context*, int); SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); SQLITE_API void sqlite3_result_null(sqlite3_context*); SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); /* ** CAPI3REF: Define New Collating Sequences ** ** These functions are used to add new collation sequences to the ** [database connection] specified as the first argument. ** ** ^The name of the new collation sequence is specified as a UTF-8 string ** for sqlite3_create_collation() and sqlite3_create_collation_v2() ** and a UTF-16 string for sqlite3_create_collation16(). ^In all cases ** the name is passed as the second function argument. ** ** ^The third argument may be one of the constants [SQLITE_UTF8], ** [SQLITE_UTF16LE], or [SQLITE_UTF16BE], indicating that the user-supplied ** routine expects to be passed pointers to strings encoded using UTF-8, ** UTF-16 little-endian, or UTF-16 big-endian, respectively. ^The ** third argument might also be [SQLITE_UTF16] to indicate that the routine ** expects pointers to be UTF-16 strings in the native byte order, or the ** argument can be [SQLITE_UTF16_ALIGNED] if the ** the routine expects pointers to 16-bit word aligned strings ** of UTF-16 in the native byte order. ** ** A pointer to the user supplied routine must be passed as the fifth ** argument. ^If it is NULL, this is the same as deleting the collation ** sequence (so that SQLite cannot call it anymore). ** ^Each time the application supplied function is invoked, it is passed ** as its first parameter a copy of the void* passed as the fourth argument ** to sqlite3_create_collation() or sqlite3_create_collation16(). ** ** ^The remaining arguments to the application-supplied routine are two strings, ** each represented by a (length, data) pair and encoded in the encoding ** that was passed as the third argument when the collation sequence was ** registered. The application defined collation routine should ** return negative, zero or positive if the first string is less than, ** equal to, or greater than the second string. i.e. (STRING1 - STRING2). ** ** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() ** except that it takes an extra argument which is a destructor for ** the collation. ^The destructor is called when the collation is ** destroyed and is passed a copy of the fourth parameter void* pointer ** of the sqlite3_create_collation_v2(). ** ^Collations are destroyed when they are overridden by later calls to the ** collation creation functions or when the [database connection] is closed ** using [sqlite3_close()]. ** ** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. */ SQLITE_API int sqlite3_create_collation( sqlite3*, const char *zName, int eTextRep, void*, int(*xCompare)(void*,int,const void*,int,const void*) ); SQLITE_API int sqlite3_create_collation_v2( sqlite3*, const char *zName, int eTextRep, void*, int(*xCompare)(void*,int,const void*,int,const void*), void(*xDestroy)(void*) ); SQLITE_API int sqlite3_create_collation16( sqlite3*, const void *zName, int eTextRep, void*, int(*xCompare)(void*,int,const void*,int,const void*) ); /* ** CAPI3REF: Collation Needed Callbacks ** ** ^To avoid having to register all collation sequences before a database ** can be used, a single callback function may be registered with the ** [database connection] to be invoked whenever an undefined collation ** sequence is required. ** ** ^If the function is registered using the sqlite3_collation_needed() API, ** then it is passed the names of undefined collation sequences as strings ** encoded in UTF-8. ^If sqlite3_collation_needed16() is used, ** the names are passed as UTF-16 in machine native byte order. ** ^A call to either function replaces the existing collation-needed callback. ** ** ^(When the callback is invoked, the first argument passed is a copy ** of the second argument to sqlite3_collation_needed() or ** sqlite3_collation_needed16(). The second argument is the database ** connection. The third argument is one of [SQLITE_UTF8], [SQLITE_UTF16BE], ** or [SQLITE_UTF16LE], indicating the most desirable form of the collation ** sequence function required. The fourth parameter is the name of the ** required collation sequence.)^ ** ** The callback function should register the desired collation using ** [sqlite3_create_collation()], [sqlite3_create_collation16()], or ** [sqlite3_create_collation_v2()]. */ SQLITE_API int sqlite3_collation_needed( sqlite3*, void*, void(*)(void*,sqlite3*,int eTextRep,const char*) ); SQLITE_API int sqlite3_collation_needed16( sqlite3*, void*, void(*)(void*,sqlite3*,int eTextRep,const void*) ); #if SQLITE_HAS_CODEC /* ** Specify the key for an encrypted database. This routine should be ** called right after sqlite3_open(). ** ** The code to implement this API is not available in the public release ** of SQLite. */ SQLITE_API int sqlite3_key( sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The key */ ); /* ** Change the key on an open database. If the current database is not ** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the ** database is decrypted. ** ** The code to implement this API is not available in the public release ** of SQLite. */ SQLITE_API int sqlite3_rekey( sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The new key */ ); /* ** Specify the activation key for a SEE database. Unless ** activated, none of the SEE routines will work. */ SQLITE_API void sqlite3_activate_see( const char *zPassPhrase /* Activation phrase */ ); #endif #ifdef SQLITE_ENABLE_CEROD /* ** Specify the activation key for a CEROD database. Unless ** activated, none of the CEROD routines will work. */ SQLITE_API void sqlite3_activate_cerod( const char *zPassPhrase /* Activation phrase */ ); #endif /* ** CAPI3REF: Suspend Execution For A Short Time ** ** ^The sqlite3_sleep() function causes the current thread to suspend execution ** for at least a number of milliseconds specified in its parameter. ** ** ^If the operating system does not support sleep requests with ** millisecond time resolution, then the time will be rounded up to ** the nearest second. ^The number of milliseconds of sleep actually ** requested from the operating system is returned. ** ** ^SQLite implements this interface by calling the xSleep() ** method of the default [sqlite3_vfs] object. */ SQLITE_API int sqlite3_sleep(int); /* ** CAPI3REF: Name Of The Folder Holding Temporary Files ** ** ^(If this global variable is made to point to a string which is ** the name of a folder (a.k.a. directory), then all temporary files ** created by SQLite when using a built-in [sqlite3_vfs | VFS] ** will be placed in that directory.)^ ^If this variable ** is a NULL pointer, then SQLite performs a search for an appropriate ** temporary file directory. ** ** It is not safe to read or modify this variable in more than one ** thread at a time. It is not safe to read or modify this variable ** if a [database connection] is being used at the same time in a separate ** thread. ** It is intended that this variable be set once ** as part of process initialization and before any SQLite interface ** routines have been called and that this variable remain unchanged ** thereafter. ** ** ^The [temp_store_directory pragma] may modify this variable and cause ** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, ** the [temp_store_directory pragma] always assumes that any string ** that this variable points to is held in memory obtained from ** [sqlite3_malloc] and the pragma may attempt to free that memory ** using [sqlite3_free]. ** Hence, if this variable is modified directly, either it should be ** made NULL or made to point to memory obtained from [sqlite3_malloc] ** or else the use of the [temp_store_directory pragma] should be avoided. */ SQLITE_API char *sqlite3_temp_directory; /* ** CAPI3REF: Test For Auto-Commit Mode ** KEYWORDS: {autocommit mode} ** ** ^The sqlite3_get_autocommit() interface returns non-zero or ** zero if the given database connection is or is not in autocommit mode, ** respectively. ^Autocommit mode is on by default. ** ^Autocommit mode is disabled by a [BEGIN] statement. ** ^Autocommit mode is re-enabled by a [COMMIT] or [ROLLBACK]. ** ** If certain kinds of errors occur on a statement within a multi-statement ** transaction (errors including [SQLITE_FULL], [SQLITE_IOERR], ** [SQLITE_NOMEM], [SQLITE_BUSY], and [SQLITE_INTERRUPT]) then the ** transaction might be rolled back automatically. The only way to ** find out whether SQLite automatically rolled back the transaction after ** an error is to use this function. ** ** If another thread changes the autocommit status of the database ** connection while this routine is running, then the return value ** is undefined. */ SQLITE_API int sqlite3_get_autocommit(sqlite3*); /* ** CAPI3REF: Find The Database Handle Of A Prepared Statement ** ** ^The sqlite3_db_handle interface returns the [database connection] handle ** to which a [prepared statement] belongs. ^The [database connection] ** returned by sqlite3_db_handle is the same [database connection] ** that was the first argument ** to the [sqlite3_prepare_v2()] call (or its variants) that was used to ** create the statement in the first place. */ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); /* ** CAPI3REF: Find the next prepared statement ** ** ^This interface returns a pointer to the next [prepared statement] after ** pStmt associated with the [database connection] pDb. ^If pStmt is NULL ** then this interface returns a pointer to the first prepared statement ** associated with the database connection pDb. ^If no prepared statement ** satisfies the conditions of this routine, it returns NULL. ** ** The [database connection] pointer D in a call to ** [sqlite3_next_stmt(D,S)] must refer to an open database ** connection and in particular must not be a NULL pointer. */ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); /* ** CAPI3REF: Commit And Rollback Notification Callbacks ** ** ^The sqlite3_commit_hook() interface registers a callback ** function to be invoked whenever a transaction is [COMMIT | committed]. ** ^Any callback set by a previous call to sqlite3_commit_hook() ** for the same database connection is overridden. ** ^The sqlite3_rollback_hook() interface registers a callback ** function to be invoked whenever a transaction is [ROLLBACK | rolled back]. ** ^Any callback set by a previous call to sqlite3_rollback_hook() ** for the same database connection is overridden. ** ^The pArg argument is passed through to the callback. ** ^If the callback on a commit hook function returns non-zero, ** then the commit is converted into a rollback. ** ** ^The sqlite3_commit_hook(D,C,P) and sqlite3_rollback_hook(D,C,P) functions ** return the P argument from the previous call of the same function ** on the same [database connection] D, or NULL for ** the first call for each function on D. ** ** The callback implementation must not do anything that will modify ** the database connection that invoked the callback. Any actions ** to modify the database connection must be deferred until after the ** completion of the [sqlite3_step()] call that triggered the commit ** or rollback hook in the first place. ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** ** ^Registering a NULL function disables the callback. ** ** ^When the commit hook callback routine returns zero, the [COMMIT] ** operation is allowed to continue normally. ^If the commit hook ** returns non-zero, then the [COMMIT] is converted into a [ROLLBACK]. ** ^The rollback hook is invoked on a rollback that results from a commit ** hook returning non-zero, just as it would be with any other rollback. ** ** ^For the purposes of this API, a transaction is said to have been ** rolled back if an explicit "ROLLBACK" statement is executed, or ** an error or constraint causes an implicit rollback to occur. ** ^The rollback callback is not invoked if a transaction is ** automatically rolled back because the database connection is closed. ** ^The rollback callback is not invoked if a transaction is ** rolled back because a commit callback returned non-zero. ** ** See also the [sqlite3_update_hook()] interface. */ SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); /* ** CAPI3REF: Data Change Notification Callbacks ** ** ^The sqlite3_update_hook() interface registers a callback function ** with the [database connection] identified by the first argument ** to be invoked whenever a row is updated, inserted or deleted. ** ^Any callback set by a previous call to this function ** for the same database connection is overridden. ** ** ^The second argument is a pointer to the function to invoke when a ** row is updated, inserted or deleted. ** ^The first argument to the callback is a copy of the third argument ** to sqlite3_update_hook(). ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], ** or [SQLITE_UPDATE], depending on the operation that caused the callback ** to be invoked. ** ^The third and fourth arguments to the callback contain pointers to the ** database and table name containing the affected row. ** ^The final callback parameter is the [rowid] of the row. ** ^In the case of an update, this is the [rowid] after the update takes place. ** ** ^(The update hook is not invoked when internal system tables are ** modified (i.e. sqlite_master and sqlite_sequence).)^ ** ** ^In the current implementation, the update hook ** is not invoked when duplication rows are deleted because of an ** [ON CONFLICT | ON CONFLICT REPLACE] clause. ^Nor is the update hook ** invoked when rows are deleted using the [truncate optimization]. ** The exceptions defined in this paragraph might change in a future ** release of SQLite. ** ** The update hook implementation must not do anything that will modify ** the database connection that invoked the update hook. Any actions ** to modify the database connection must be deferred until after the ** completion of the [sqlite3_step()] call that triggered the update hook. ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** ** ^The sqlite3_update_hook(D,C,P) function ** returns the P argument from the previous call ** on the same [database connection] D, or NULL for ** the first call on D. ** ** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()] ** interfaces. */ SQLITE_API void *sqlite3_update_hook( sqlite3*, void(*)(void *,int ,char const *,char const *,sqlite3_int64), void* ); /* ** CAPI3REF: Enable Or Disable Shared Pager Cache ** KEYWORDS: {shared cache} ** ** ^(This routine enables or disables the sharing of the database cache ** and schema data structures between [database connection | connections] ** to the same database. Sharing is enabled if the argument is true ** and disabled if the argument is false.)^ ** ** ^Cache sharing is enabled and disabled for an entire process. ** This is a change as of SQLite version 3.5.0. In prior versions of SQLite, ** sharing was enabled or disabled for each thread separately. ** ** ^(The cache sharing mode set by this interface effects all subsequent ** calls to [sqlite3_open()], [sqlite3_open_v2()], and [sqlite3_open16()]. ** Existing database connections continue use the sharing mode ** that was in effect at the time they were opened.)^ ** ** ^(This routine returns [SQLITE_OK] if shared cache was enabled or disabled ** successfully. An [error code] is returned otherwise.)^ ** ** ^Shared cache is disabled by default. But this might change in ** future releases of SQLite. Applications that care about shared ** cache setting should set it explicitly. ** ** See Also: [SQLite Shared-Cache Mode] */ SQLITE_API int sqlite3_enable_shared_cache(int); /* ** CAPI3REF: Attempt To Free Heap Memory ** ** ^The sqlite3_release_memory() interface attempts to free N bytes ** of heap memory by deallocating non-essential memory allocations ** held by the database library. Memory used to cache database ** pages to improve performance is an example of non-essential memory. ** ^sqlite3_release_memory() returns the number of bytes actually freed, ** which might be more or less than the amount requested. */ SQLITE_API int sqlite3_release_memory(int); /* ** CAPI3REF: Impose A Limit On Heap Size ** ** ^The sqlite3_soft_heap_limit() interface places a "soft" limit ** on the amount of heap memory that may be allocated by SQLite. ** ^If an internal allocation is requested that would exceed the ** soft heap limit, [sqlite3_release_memory()] is invoked one or ** more times to free up some space before the allocation is performed. ** ** ^The limit is called "soft" because if [sqlite3_release_memory()] ** cannot free sufficient memory to prevent the limit from being exceeded, ** the memory is allocated anyway and the current operation proceeds. ** ** ^A negative or zero value for N means that there is no soft heap limit and ** [sqlite3_release_memory()] will only be called when memory is exhausted. ** ^The default value for the soft heap limit is zero. ** ** ^(SQLite makes a best effort to honor the soft heap limit. ** But if the soft heap limit cannot be honored, execution will ** continue without error or notification.)^ This is why the limit is ** called a "soft" limit. It is advisory only. ** ** Prior to SQLite version 3.5.0, this routine only constrained the memory ** allocated by a single thread - the same thread in which this routine ** runs. Beginning with SQLite version 3.5.0, the soft heap limit is ** applied to all threads. The value specified for the soft heap limit ** is an upper bound on the total memory allocation for all threads. In ** version 3.5.0 there is no mechanism for limiting the heap usage for ** individual threads. */ SQLITE_API void sqlite3_soft_heap_limit(int); /* ** CAPI3REF: Extract Metadata About A Column Of A Table ** ** ^This routine returns metadata about a specific column of a specific ** database table accessible using the [database connection] handle ** passed as the first function argument. ** ** ^The column is identified by the second, third and fourth parameters to ** this function. ^The second parameter is either the name of the database ** (i.e. "main", "temp", or an attached database) containing the specified ** table or NULL. ^If it is NULL, then all attached databases are searched ** for the table using the same algorithm used by the database engine to ** resolve unqualified table references. ** ** ^The third and fourth parameters to this function are the table and column ** name of the desired column, respectively. Neither of these parameters ** may be NULL. ** ** ^Metadata is returned by writing to the memory locations passed as the 5th ** and subsequent parameters to this function. ^Any of these arguments may be ** NULL, in which case the corresponding element of metadata is omitted. ** ** ^(
    ** **
    Parameter Output
    Type
    Description ** **
    5th const char* Data type **
    6th const char* Name of default collation sequence **
    7th int True if column has a NOT NULL constraint **
    8th int True if column is part of the PRIMARY KEY **
    9th int True if column is [AUTOINCREMENT] **
    **
    )^ ** ** ^The memory pointed to by the character pointers returned for the ** declaration type and collation sequence is valid only until the next ** call to any SQLite API function. ** ** ^If the specified table is actually a view, an [error code] is returned. ** ** ^If the specified column is "rowid", "oid" or "_rowid_" and an ** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output ** parameters are set for the explicitly declared column. ^(If there is no ** explicitly declared [INTEGER PRIMARY KEY] column, then the output ** parameters are set as follows: ** **
    **     data type: "INTEGER"
    **     collation sequence: "BINARY"
    **     not null: 0
    **     primary key: 1
    **     auto increment: 0
    ** 
    )^ ** ** ^(This function may load one or more schemas from database files. If an ** error occurs during this process, or if the requested table or column ** cannot be found, an [error code] is returned and an error message left ** in the [database connection] (to be retrieved using sqlite3_errmsg()).)^ ** ** ^This API is only available if the library was compiled with the ** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined. */ SQLITE_API int sqlite3_table_column_metadata( sqlite3 *db, /* Connection handle */ const char *zDbName, /* Database name or NULL */ const char *zTableName, /* Table name */ const char *zColumnName, /* Column name */ char const **pzDataType, /* OUTPUT: Declared data type */ char const **pzCollSeq, /* OUTPUT: Collation sequence name */ int *pNotNull, /* OUTPUT: True if NOT NULL constraint exists */ int *pPrimaryKey, /* OUTPUT: True if column part of PK */ int *pAutoinc /* OUTPUT: True if column is auto-increment */ ); /* ** CAPI3REF: Load An Extension ** ** ^This interface loads an SQLite extension library from the named file. ** ** ^The sqlite3_load_extension() interface attempts to load an ** SQLite extension library contained in the file zFile. ** ** ^The entry point is zProc. ** ^zProc may be 0, in which case the name of the entry point ** defaults to "sqlite3_extension_init". ** ^The sqlite3_load_extension() interface returns ** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. ** ^If an error occurs and pzErrMsg is not 0, then the ** [sqlite3_load_extension()] interface shall attempt to ** fill *pzErrMsg with error message text stored in memory ** obtained from [sqlite3_malloc()]. The calling function ** should free this memory by calling [sqlite3_free()]. ** ** ^Extension loading must be enabled using ** [sqlite3_enable_load_extension()] prior to calling this API, ** otherwise an error will be returned. ** ** See also the [load_extension() SQL function]. */ SQLITE_API int sqlite3_load_extension( sqlite3 *db, /* Load the extension into this database connection */ const char *zFile, /* Name of the shared library containing extension */ const char *zProc, /* Entry point. Derived from zFile if 0 */ char **pzErrMsg /* Put error message here if not 0 */ ); /* ** CAPI3REF: Enable Or Disable Extension Loading ** ** ^So as not to open security holes in older applications that are ** unprepared to deal with extension loading, and as a means of disabling ** extension loading while evaluating user-entered SQL, the following API ** is provided to turn the [sqlite3_load_extension()] mechanism on and off. ** ** ^Extension loading is off by default. See ticket #1863. ** ^Call the sqlite3_enable_load_extension() routine with onoff==1 ** to turn extension loading on and call it with onoff==0 to turn ** it back off again. */ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); /* ** CAPI3REF: Automatically Load An Extensions ** ** ^This API can be invoked at program startup in order to register ** one or more statically linked extensions that will be available ** to all new [database connections]. ** ** ^(This routine stores a pointer to the extension entry point ** in an array that is obtained from [sqlite3_malloc()]. That memory ** is deallocated by [sqlite3_reset_auto_extension()].)^ ** ** ^This function registers an extension entry point that is ** automatically invoked whenever a new [database connection] ** is opened using [sqlite3_open()], [sqlite3_open16()], ** or [sqlite3_open_v2()]. ** ^Duplicate extensions are detected so calling this routine ** multiple times with the same extension is harmless. ** ^Automatic extensions apply across all threads. */ SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); /* ** CAPI3REF: Reset Automatic Extension Loading ** ** ^(This function disables all previously registered automatic ** extensions. It undoes the effect of all prior ** [sqlite3_auto_extension()] calls.)^ ** ** ^This function disables automatic extensions in all threads. */ SQLITE_API void sqlite3_reset_auto_extension(void); /* ****** EXPERIMENTAL - subject to change without notice ************** ** ** The interface to the virtual-table mechanism is currently considered ** to be experimental. The interface might change in incompatible ways. ** If this is a problem for you, do not use the interface at this time. ** ** When the virtual-table mechanism stabilizes, we will declare the ** interface fixed, support it indefinitely, and remove this comment. */ /* ** Structures used by the virtual table interface */ typedef struct sqlite3_vtab sqlite3_vtab; typedef struct sqlite3_index_info sqlite3_index_info; typedef struct sqlite3_vtab_cursor sqlite3_vtab_cursor; typedef struct sqlite3_module sqlite3_module; /* ** CAPI3REF: Virtual Table Object ** KEYWORDS: sqlite3_module {virtual table module} ** EXPERIMENTAL ** ** This structure, sometimes called a a "virtual table module", ** defines the implementation of a [virtual tables]. ** This structure consists mostly of methods for the module. ** ** ^A virtual table module is created by filling in a persistent ** instance of this structure and passing a pointer to that instance ** to [sqlite3_create_module()] or [sqlite3_create_module_v2()]. ** ^The registration remains valid until it is replaced by a different ** module or until the [database connection] closes. The content ** of this structure must not change while it is registered with ** any database connection. */ struct sqlite3_module { int iVersion; int (*xCreate)(sqlite3*, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char**); int (*xConnect)(sqlite3*, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char**); int (*xBestIndex)(sqlite3_vtab *pVTab, sqlite3_index_info*); int (*xDisconnect)(sqlite3_vtab *pVTab); int (*xDestroy)(sqlite3_vtab *pVTab); int (*xOpen)(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor); int (*xClose)(sqlite3_vtab_cursor*); int (*xFilter)(sqlite3_vtab_cursor*, int idxNum, const char *idxStr, int argc, sqlite3_value **argv); int (*xNext)(sqlite3_vtab_cursor*); int (*xEof)(sqlite3_vtab_cursor*); int (*xColumn)(sqlite3_vtab_cursor*, sqlite3_context*, int); int (*xRowid)(sqlite3_vtab_cursor*, sqlite3_int64 *pRowid); int (*xUpdate)(sqlite3_vtab *, int, sqlite3_value **, sqlite3_int64 *); int (*xBegin)(sqlite3_vtab *pVTab); int (*xSync)(sqlite3_vtab *pVTab); int (*xCommit)(sqlite3_vtab *pVTab); int (*xRollback)(sqlite3_vtab *pVTab); int (*xFindFunction)(sqlite3_vtab *pVtab, int nArg, const char *zName, void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), void **ppArg); int (*xRename)(sqlite3_vtab *pVtab, const char *zNew); }; /* ** CAPI3REF: Virtual Table Indexing Information ** KEYWORDS: sqlite3_index_info ** EXPERIMENTAL ** ** The sqlite3_index_info structure and its substructures is used to ** pass information into and receive the reply from the [xBestIndex] ** method of a [virtual table module]. The fields under **Inputs** are the ** inputs to xBestIndex and are read-only. xBestIndex inserts its ** results into the **Outputs** fields. ** ** ^(The aConstraint[] array records WHERE clause constraints of the form: ** **
    column OP expr
    ** ** where OP is =, <, <=, >, or >=.)^ ^(The particular operator is ** stored in aConstraint[].op.)^ ^(The index of the column is stored in ** aConstraint[].iColumn.)^ ^(aConstraint[].usable is TRUE if the ** expr on the right-hand side can be evaluated (and thus the constraint ** is usable) and false if it cannot.)^ ** ** ^The optimizer automatically inverts terms of the form "expr OP column" ** and makes other simplifications to the WHERE clause in an attempt to ** get as many WHERE clause terms into the form shown above as possible. ** ^The aConstraint[] array only reports WHERE clause terms that are ** relevant to the particular virtual table being queried. ** ** ^Information about the ORDER BY clause is stored in aOrderBy[]. ** ^Each term of aOrderBy records a column of the ORDER BY clause. ** ** The [xBestIndex] method must fill aConstraintUsage[] with information ** about what parameters to pass to xFilter. ^If argvIndex>0 then ** the right-hand side of the corresponding aConstraint[] is evaluated ** and becomes the argvIndex-th entry in argv. ^(If aConstraintUsage[].omit ** is true, then the constraint is assumed to be fully handled by the ** virtual table and is not checked again by SQLite.)^ ** ** ^The idxNum and idxPtr values are recorded and passed into the ** [xFilter] method. ** ^[sqlite3_free()] is used to free idxPtr if and only if ** needToFreeIdxPtr is true. ** ** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in ** the correct order to satisfy the ORDER BY clause so that no separate ** sorting step is required. ** ** ^The estimatedCost value is an estimate of the cost of doing the ** particular lookup. A full scan of a table with N entries should have ** a cost of N. A binary search of a table of N entries should have a ** cost of approximately log(N). */ struct sqlite3_index_info { /* Inputs */ int nConstraint; /* Number of entries in aConstraint */ struct sqlite3_index_constraint { int iColumn; /* Column on left-hand side of constraint */ unsigned char op; /* Constraint operator */ unsigned char usable; /* True if this constraint is usable */ int iTermOffset; /* Used internally - xBestIndex should ignore */ } *aConstraint; /* Table of WHERE clause constraints */ int nOrderBy; /* Number of terms in the ORDER BY clause */ struct sqlite3_index_orderby { int iColumn; /* Column number */ unsigned char desc; /* True for DESC. False for ASC. */ } *aOrderBy; /* The ORDER BY clause */ /* Outputs */ struct sqlite3_index_constraint_usage { int argvIndex; /* if >0, constraint is part of argv to xFilter */ unsigned char omit; /* Do not code a test for this constraint */ } *aConstraintUsage; int idxNum; /* Number used to identify the index */ char *idxStr; /* String, possibly obtained from sqlite3_malloc */ int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */ int orderByConsumed; /* True if output is already ordered */ double estimatedCost; /* Estimated cost of using this index */ }; #define SQLITE_INDEX_CONSTRAINT_EQ 2 #define SQLITE_INDEX_CONSTRAINT_GT 4 #define SQLITE_INDEX_CONSTRAINT_LE 8 #define SQLITE_INDEX_CONSTRAINT_LT 16 #define SQLITE_INDEX_CONSTRAINT_GE 32 #define SQLITE_INDEX_CONSTRAINT_MATCH 64 /* ** CAPI3REF: Register A Virtual Table Implementation ** EXPERIMENTAL ** ** ^These routines are used to register a new [virtual table module] name. ** ^Module names must be registered before ** creating a new [virtual table] using the module and before using a ** preexisting [virtual table] for the module. ** ** ^The module name is registered on the [database connection] specified ** by the first parameter. ^The name of the module is given by the ** second parameter. ^The third parameter is a pointer to ** the implementation of the [virtual table module]. ^The fourth ** parameter is an arbitrary client data pointer that is passed through ** into the [xCreate] and [xConnect] methods of the virtual table module ** when a new virtual table is be being created or reinitialized. ** ** ^The sqlite3_create_module_v2() interface has a fifth parameter which ** is a pointer to a destructor for the pClientData. ^SQLite will ** invoke the destructor function (if it is not NULL) when SQLite ** no longer needs the pClientData pointer. ^The sqlite3_create_module() ** interface is equivalent to sqlite3_create_module_v2() with a NULL ** destructor. */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_create_module( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ void *pClientData /* Client data for xCreate/xConnect */ ); SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_create_module_v2( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ void *pClientData, /* Client data for xCreate/xConnect */ void(*xDestroy)(void*) /* Module destructor function */ ); /* ** CAPI3REF: Virtual Table Instance Object ** KEYWORDS: sqlite3_vtab ** EXPERIMENTAL ** ** Every [virtual table module] implementation uses a subclass ** of this object to describe a particular instance ** of the [virtual table]. Each subclass will ** be tailored to the specific needs of the module implementation. ** The purpose of this superclass is to define certain fields that are ** common to all module implementations. ** ** ^Virtual tables methods can set an error message by assigning a ** string obtained from [sqlite3_mprintf()] to zErrMsg. The method should ** take care that any prior string is freed by a call to [sqlite3_free()] ** prior to assigning a new string to zErrMsg. ^After the error message ** is delivered up to the client application, the string will be automatically ** freed by sqlite3_free() and the zErrMsg field will be zeroed. */ struct sqlite3_vtab { const sqlite3_module *pModule; /* The module for this virtual table */ int nRef; /* NO LONGER USED */ char *zErrMsg; /* Error message from sqlite3_mprintf() */ /* Virtual table implementations will typically add additional fields */ }; /* ** CAPI3REF: Virtual Table Cursor Object ** KEYWORDS: sqlite3_vtab_cursor {virtual table cursor} ** EXPERIMENTAL ** ** Every [virtual table module] implementation uses a subclass of the ** following structure to describe cursors that point into the ** [virtual table] and are used ** to loop through the virtual table. Cursors are created using the ** [sqlite3_module.xOpen | xOpen] method of the module and are destroyed ** by the [sqlite3_module.xClose | xClose] method. Cursors are used ** by the [xFilter], [xNext], [xEof], [xColumn], and [xRowid] methods ** of the module. Each module implementation will define ** the content of a cursor structure to suit its own needs. ** ** This superclass exists in order to define fields of the cursor that ** are common to all implementations. */ struct sqlite3_vtab_cursor { sqlite3_vtab *pVtab; /* Virtual table of this cursor */ /* Virtual table implementations will typically add additional fields */ }; /* ** CAPI3REF: Declare The Schema Of A Virtual Table ** EXPERIMENTAL ** ** ^The [xCreate] and [xConnect] methods of a ** [virtual table module] call this interface ** to declare the format (the names and datatypes of the columns) of ** the virtual tables they implement. */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_declare_vtab(sqlite3*, const char *zSQL); /* ** CAPI3REF: Overload A Function For A Virtual Table ** EXPERIMENTAL ** ** ^(Virtual tables can provide alternative implementations of functions ** using the [xFindFunction] method of the [virtual table module]. ** But global versions of those functions ** must exist in order to be overloaded.)^ ** ** ^(This API makes sure a global version of a function with a particular ** name and number of parameters exists. If no such function exists ** before this API is called, a new function is created.)^ ^The implementation ** of the new function always causes an exception to be thrown. So ** the new function is not good for anything by itself. Its only ** purpose is to be a placeholder function that can be overloaded ** by a [virtual table]. */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); /* ** The interface to the virtual-table mechanism defined above (back up ** to a comment remarkably similar to this one) is currently considered ** to be experimental. The interface might change in incompatible ways. ** If this is a problem for you, do not use the interface at this time. ** ** When the virtual-table mechanism stabilizes, we will declare the ** interface fixed, support it indefinitely, and remove this comment. ** ****** EXPERIMENTAL - subject to change without notice ************** */ /* ** CAPI3REF: A Handle To An Open BLOB ** KEYWORDS: {BLOB handle} {BLOB handles} ** ** An instance of this object represents an open BLOB on which ** [sqlite3_blob_open | incremental BLOB I/O] can be performed. ** ^Objects of this type are created by [sqlite3_blob_open()] ** and destroyed by [sqlite3_blob_close()]. ** ^The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces ** can be used to read or write small subsections of the BLOB. ** ^The [sqlite3_blob_bytes()] interface returns the size of the BLOB in bytes. */ typedef struct sqlite3_blob sqlite3_blob; /* ** CAPI3REF: Open A BLOB For Incremental I/O ** ** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located ** in row iRow, column zColumn, table zTable in database zDb; ** in other words, the same BLOB that would be selected by: ** **
    **     SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
    ** 
    )^ ** ** ^If the flags parameter is non-zero, then the BLOB is opened for read ** and write access. ^If it is zero, the BLOB is opened for read access. ** ^It is not possible to open a column that is part of an index or primary ** key for writing. ^If [foreign key constraints] are enabled, it is ** not possible to open a column that is part of a [child key] for writing. ** ** ^Note that the database name is not the filename that contains ** the database but rather the symbolic name of the database that ** appears after the AS keyword when the database is connected using [ATTACH]. ** ^For the main database file, the database name is "main". ** ^For TEMP tables, the database name is "temp". ** ** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is written ** to *ppBlob. Otherwise an [error code] is returned and *ppBlob is set ** to be a null pointer.)^ ** ^This function sets the [database connection] error code and message ** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()] and related ** functions. ^Note that the *ppBlob variable is always initialized in a ** way that makes it safe to invoke [sqlite3_blob_close()] on *ppBlob ** regardless of the success or failure of this routine. ** ** ^(If the row that a BLOB handle points to is modified by an ** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects ** then the BLOB handle is marked as "expired". ** This is true if any column of the row is changed, even a column ** other than the one the BLOB handle is open on.)^ ** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for ** a expired BLOB handle fail with an return code of [SQLITE_ABORT]. ** ^(Changes written into a BLOB prior to the BLOB expiring are not ** rolled back by the expiration of the BLOB. Such changes will eventually ** commit if the transaction continues to completion.)^ ** ** ^Use the [sqlite3_blob_bytes()] interface to determine the size of ** the opened blob. ^The size of a blob may not be changed by this ** interface. Use the [UPDATE] SQL command to change the size of a ** blob. ** ** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces ** and the built-in [zeroblob] SQL function can be used, if desired, ** to create an empty, zero-filled blob in which to read or write using ** this interface. ** ** To avoid a resource leak, every open [BLOB handle] should eventually ** be released by a call to [sqlite3_blob_close()]. */ SQLITE_API int sqlite3_blob_open( sqlite3*, const char *zDb, const char *zTable, const char *zColumn, sqlite3_int64 iRow, int flags, sqlite3_blob **ppBlob ); /* ** CAPI3REF: Close A BLOB Handle ** ** ^Closes an open [BLOB handle]. ** ** ^Closing a BLOB shall cause the current transaction to commit ** if there are no other BLOBs, no pending prepared statements, and the ** database connection is in [autocommit mode]. ** ^If any writes were made to the BLOB, they might be held in cache ** until the close operation if they will fit. ** ** ^(Closing the BLOB often forces the changes ** out to disk and so if any I/O errors occur, they will likely occur ** at the time when the BLOB is closed. Any errors that occur during ** closing are reported as a non-zero return value.)^ ** ** ^(The BLOB is closed unconditionally. Even if this routine returns ** an error code, the BLOB is still closed.)^ ** ** ^Calling this routine with a null pointer (such as would be returned ** by a failed call to [sqlite3_blob_open()]) is a harmless no-op. */ SQLITE_API int sqlite3_blob_close(sqlite3_blob *); /* ** CAPI3REF: Return The Size Of An Open BLOB ** ** ^Returns the size in bytes of the BLOB accessible via the ** successfully opened [BLOB handle] in its only argument. ^The ** incremental blob I/O routines can only read or overwriting existing ** blob content; they cannot change the size of a blob. ** ** This routine only works on a [BLOB handle] which has been created ** by a prior successful call to [sqlite3_blob_open()] and which has not ** been closed by [sqlite3_blob_close()]. Passing any other pointer in ** to this routine results in undefined and probably undesirable behavior. */ SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); /* ** CAPI3REF: Read Data From A BLOB Incrementally ** ** ^(This function is used to read data from an open [BLOB handle] into a ** caller-supplied buffer. N bytes of data are copied into buffer Z ** from the open BLOB, starting at offset iOffset.)^ ** ** ^If offset iOffset is less than N bytes from the end of the BLOB, ** [SQLITE_ERROR] is returned and no data is read. ^If N or iOffset is ** less than zero, [SQLITE_ERROR] is returned and no data is read. ** ^The size of the blob (and hence the maximum value of N+iOffset) ** can be determined using the [sqlite3_blob_bytes()] interface. ** ** ^An attempt to read from an expired [BLOB handle] fails with an ** error code of [SQLITE_ABORT]. ** ** ^(On success, sqlite3_blob_read() returns SQLITE_OK. ** Otherwise, an [error code] or an [extended error code] is returned.)^ ** ** This routine only works on a [BLOB handle] which has been created ** by a prior successful call to [sqlite3_blob_open()] and which has not ** been closed by [sqlite3_blob_close()]. Passing any other pointer in ** to this routine results in undefined and probably undesirable behavior. ** ** See also: [sqlite3_blob_write()]. */ SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); /* ** CAPI3REF: Write Data Into A BLOB Incrementally ** ** ^This function is used to write data into an open [BLOB handle] from a ** caller-supplied buffer. ^N bytes of data are copied from the buffer Z ** into the open BLOB, starting at offset iOffset. ** ** ^If the [BLOB handle] passed as the first argument was not opened for ** writing (the flags parameter to [sqlite3_blob_open()] was zero), ** this function returns [SQLITE_READONLY]. ** ** ^This function may only modify the contents of the BLOB; it is ** not possible to increase the size of a BLOB using this API. ** ^If offset iOffset is less than N bytes from the end of the BLOB, ** [SQLITE_ERROR] is returned and no data is written. ^If N is ** less than zero [SQLITE_ERROR] is returned and no data is written. ** The size of the BLOB (and hence the maximum value of N+iOffset) ** can be determined using the [sqlite3_blob_bytes()] interface. ** ** ^An attempt to write to an expired [BLOB handle] fails with an ** error code of [SQLITE_ABORT]. ^Writes to the BLOB that occurred ** before the [BLOB handle] expired are not rolled back by the ** expiration of the handle, though of course those changes might ** have been overwritten by the statement that expired the BLOB handle ** or by other independent statements. ** ** ^(On success, sqlite3_blob_write() returns SQLITE_OK. ** Otherwise, an [error code] or an [extended error code] is returned.)^ ** ** This routine only works on a [BLOB handle] which has been created ** by a prior successful call to [sqlite3_blob_open()] and which has not ** been closed by [sqlite3_blob_close()]. Passing any other pointer in ** to this routine results in undefined and probably undesirable behavior. ** ** See also: [sqlite3_blob_read()]. */ SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); /* ** CAPI3REF: Virtual File System Objects ** ** A virtual filesystem (VFS) is an [sqlite3_vfs] object ** that SQLite uses to interact ** with the underlying operating system. Most SQLite builds come with a ** single default VFS that is appropriate for the host computer. ** New VFSes can be registered and existing VFSes can be unregistered. ** The following interfaces are provided. ** ** ^The sqlite3_vfs_find() interface returns a pointer to a VFS given its name. ** ^Names are case sensitive. ** ^Names are zero-terminated UTF-8 strings. ** ^If there is no match, a NULL pointer is returned. ** ^If zVfsName is NULL then the default VFS is returned. ** ** ^New VFSes are registered with sqlite3_vfs_register(). ** ^Each new VFS becomes the default VFS if the makeDflt flag is set. ** ^The same VFS can be registered multiple times without injury. ** ^To make an existing VFS into the default VFS, register it again ** with the makeDflt flag set. If two different VFSes with the ** same name are registered, the behavior is undefined. If a ** VFS is registered with a name that is NULL or an empty string, ** then the behavior is undefined. ** ** ^Unregister a VFS with the sqlite3_vfs_unregister() interface. ** ^(If the default VFS is unregistered, another VFS is chosen as ** the default. The choice for the new VFS is arbitrary.)^ */ SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); SQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); /* ** CAPI3REF: Mutexes ** ** The SQLite core uses these routines for thread ** synchronization. Though they are intended for internal ** use by SQLite, code that links against SQLite is ** permitted to use any of these routines. ** ** The SQLite source code contains multiple implementations ** of these mutex routines. An appropriate implementation ** is selected automatically at compile-time. ^(The following ** implementations are available in the SQLite core: ** **
      **
    • SQLITE_MUTEX_OS2 **
    • SQLITE_MUTEX_PTHREAD **
    • SQLITE_MUTEX_W32 **
    • SQLITE_MUTEX_NOOP **
    )^ ** ** ^The SQLITE_MUTEX_NOOP implementation is a set of routines ** that does no real locking and is appropriate for use in ** a single-threaded application. ^The SQLITE_MUTEX_OS2, ** SQLITE_MUTEX_PTHREAD, and SQLITE_MUTEX_W32 implementations ** are appropriate for use on OS/2, Unix, and Windows. ** ** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor ** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex ** implementation is included with the library. In this case the ** application must supply a custom mutex implementation using the ** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function ** before calling sqlite3_initialize() or any other public sqlite3_ ** function that calls sqlite3_initialize().)^ ** ** ^The sqlite3_mutex_alloc() routine allocates a new ** mutex and returns a pointer to it. ^If it returns NULL ** that means that a mutex could not be allocated. ^SQLite ** will unwind its stack and return an error. ^(The argument ** to sqlite3_mutex_alloc() is one of these integer constants: ** **
      **
    • SQLITE_MUTEX_FAST **
    • SQLITE_MUTEX_RECURSIVE **
    • SQLITE_MUTEX_STATIC_MASTER **
    • SQLITE_MUTEX_STATIC_MEM **
    • SQLITE_MUTEX_STATIC_MEM2 **
    • SQLITE_MUTEX_STATIC_PRNG **
    • SQLITE_MUTEX_STATIC_LRU **
    • SQLITE_MUTEX_STATIC_LRU2 **
    )^ ** ** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) ** cause sqlite3_mutex_alloc() to create ** a new mutex. ^The new mutex is recursive when SQLITE_MUTEX_RECURSIVE ** is used but not necessarily so when SQLITE_MUTEX_FAST is used. ** The mutex implementation does not need to make a distinction ** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does ** not want to. ^SQLite will only request a recursive mutex in ** cases where it really needs one. ^If a faster non-recursive mutex ** implementation is available on the host platform, the mutex subsystem ** might return such a mutex in response to SQLITE_MUTEX_FAST. ** ** ^The other allowed parameters to sqlite3_mutex_alloc() (anything other ** than SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) each return ** a pointer to a static preexisting mutex. ^Six static mutexes are ** used by the current version of SQLite. Future versions of SQLite ** may add additional static mutexes. Static mutexes are for internal ** use by SQLite only. Applications that use SQLite mutexes should ** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or ** SQLITE_MUTEX_RECURSIVE. ** ** ^Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST ** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() ** returns a different mutex on every call. ^But for the static ** mutex types, the same mutex is returned on every call that has ** the same type number. ** ** ^The sqlite3_mutex_free() routine deallocates a previously ** allocated dynamic mutex. ^SQLite is careful to deallocate every ** dynamic mutex that it allocates. The dynamic mutexes must not be in ** use when they are deallocated. Attempting to deallocate a static ** mutex results in undefined behavior. ^SQLite never deallocates ** a static mutex. ** ** ^The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt ** to enter a mutex. ^If another thread is already within the mutex, ** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return ** SQLITE_BUSY. ^The sqlite3_mutex_try() interface returns [SQLITE_OK] ** upon successful entry. ^(Mutexes created using ** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread. ** In such cases the, ** mutex must be exited an equal number of times before another thread ** can enter.)^ ^(If the same thread tries to enter any other ** kind of mutex more than once, the behavior is undefined. ** SQLite will never exhibit ** such behavior in its own use of mutexes.)^ ** ** ^(Some systems (for example, Windows 95) do not support the operation ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() ** will always return SQLITE_BUSY. The SQLite core only ever uses ** sqlite3_mutex_try() as an optimization so this is acceptable behavior.)^ ** ** ^The sqlite3_mutex_leave() routine exits a mutex that was ** previously entered by the same thread. ^(The behavior ** is undefined if the mutex is not currently entered by the ** calling thread or is not currently allocated. SQLite will ** never do either.)^ ** ** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or ** sqlite3_mutex_leave() is a NULL pointer, then all three routines ** behave as no-ops. ** ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. */ SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*); SQLITE_API int sqlite3_mutex_try(sqlite3_mutex*); SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); /* ** CAPI3REF: Mutex Methods Object ** EXPERIMENTAL ** ** An instance of this structure defines the low-level routines ** used to allocate and use mutexes. ** ** Usually, the default mutex implementations provided by SQLite are ** sufficient, however the user has the option of substituting a custom ** implementation for specialized deployments or systems for which SQLite ** does not provide a suitable implementation. In this case, the user ** creates and populates an instance of this structure to pass ** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option. ** Additionally, an instance of this structure can be used as an ** output variable when querying the system for the current mutex ** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. ** ** ^The xMutexInit method defined by this structure is invoked as ** part of system initialization by the sqlite3_initialize() function. ** ^The xMutexInit routine is calle by SQLite exactly once for each ** effective call to [sqlite3_initialize()]. ** ** ^The xMutexEnd method defined by this structure is invoked as ** part of system shutdown by the sqlite3_shutdown() function. The ** implementation of this method is expected to release all outstanding ** resources obtained by the mutex methods implementation, especially ** those obtained by the xMutexInit method. ^The xMutexEnd() ** interface is invoked exactly once for each call to [sqlite3_shutdown()]. ** ** ^(The remaining seven methods defined by this structure (xMutexAlloc, ** xMutexFree, xMutexEnter, xMutexTry, xMutexLeave, xMutexHeld and ** xMutexNotheld) implement the following interfaces (respectively): ** **
      **
    • [sqlite3_mutex_alloc()]
    • **
    • [sqlite3_mutex_free()]
    • **
    • [sqlite3_mutex_enter()]
    • **
    • [sqlite3_mutex_try()]
    • **
    • [sqlite3_mutex_leave()]
    • **
    • [sqlite3_mutex_held()]
    • **
    • [sqlite3_mutex_notheld()]
    • **
    )^ ** ** The only difference is that the public sqlite3_XXX functions enumerated ** above silently ignore any invocations that pass a NULL pointer instead ** of a valid mutex handle. The implementations of the methods defined ** by this structure are not required to handle this case, the results ** of passing a NULL pointer instead of a valid mutex handle are undefined ** (i.e. it is acceptable to provide an implementation that segfaults if ** it is passed a NULL pointer). ** ** The xMutexInit() method must be threadsafe. ^It must be harmless to ** invoke xMutexInit() mutiple times within the same process and without ** intervening calls to xMutexEnd(). Second and subsequent calls to ** xMutexInit() must be no-ops. ** ** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()] ** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory ** allocation for a static mutex. ^However xMutexAlloc() may use SQLite ** memory allocation for a fast or recursive mutex. ** ** ^SQLite will invoke the xMutexEnd() method when [sqlite3_shutdown()] is ** called, but only if the prior call to xMutexInit returned SQLITE_OK. ** If xMutexInit fails in any way, it is expected to clean up after itself ** prior to returning. */ typedef struct sqlite3_mutex_methods sqlite3_mutex_methods; struct sqlite3_mutex_methods { int (*xMutexInit)(void); int (*xMutexEnd)(void); sqlite3_mutex *(*xMutexAlloc)(int); void (*xMutexFree)(sqlite3_mutex *); void (*xMutexEnter)(sqlite3_mutex *); int (*xMutexTry)(sqlite3_mutex *); void (*xMutexLeave)(sqlite3_mutex *); int (*xMutexHeld)(sqlite3_mutex *); int (*xMutexNotheld)(sqlite3_mutex *); }; /* ** CAPI3REF: Mutex Verification Routines ** ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines ** are intended for use inside assert() statements. ^The SQLite core ** never uses these routines except inside an assert() and applications ** are advised to follow the lead of the core. ^The SQLite core only ** provides implementations for these routines when it is compiled ** with the SQLITE_DEBUG flag. ^External mutex implementations ** are only required to provide these routines if SQLITE_DEBUG is ** defined and if NDEBUG is not defined. ** ** ^These routines should return true if the mutex in their argument ** is held or not held, respectively, by the calling thread. ** ** ^The implementation is not required to provided versions of these ** routines that actually work. If the implementation does not provide working ** versions of these routines, it should at least provide stubs that always ** return true so that one does not get spurious assertion failures. ** ** ^If the argument to sqlite3_mutex_held() is a NULL pointer then ** the routine should return 1. This seems counter-intuitive since ** clearly the mutex cannot be held if it does not exist. But the ** the reason the mutex does not exist is because the build is not ** using mutexes. And we do not want the assert() containing the ** call to sqlite3_mutex_held() to fail, so a non-zero return is ** the appropriate thing to do. ^The sqlite3_mutex_notheld() ** interface should also return 1 when given a NULL pointer. */ #ifndef NDEBUG SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); #endif /* ** CAPI3REF: Mutex Types ** ** The [sqlite3_mutex_alloc()] interface takes a single argument ** which is one of these integer constants. ** ** The set of static mutexes may change from one SQLite release to the ** next. Applications that override the built-in mutex logic must be ** prepared to accommodate additional static mutexes. */ #define SQLITE_MUTEX_FAST 0 #define SQLITE_MUTEX_RECURSIVE 1 #define SQLITE_MUTEX_STATIC_MASTER 2 #define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */ #define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */ #define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */ #define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */ #define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */ #define SQLITE_MUTEX_STATIC_LRU2 7 /* lru page list */ /* ** CAPI3REF: Retrieve the mutex for a database connection ** ** ^This interface returns a pointer the [sqlite3_mutex] object that ** serializes access to the [database connection] given in the argument ** when the [threading mode] is Serialized. ** ^If the [threading mode] is Single-thread or Multi-thread then this ** routine returns a NULL pointer. */ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); /* ** CAPI3REF: Low-Level Control Of Database Files ** ** ^The [sqlite3_file_control()] interface makes a direct call to the ** xFileControl method for the [sqlite3_io_methods] object associated ** with a particular database identified by the second argument. ^The ** name of the database "main" for the main database or "temp" for the ** TEMP database, or the name that appears after the AS keyword for ** databases that are added using the [ATTACH] SQL command. ** ^A NULL pointer can be used in place of "main" to refer to the ** main database file. ** ^The third and fourth parameters to this routine ** are passed directly through to the second and third parameters of ** the xFileControl method. ^The return value of the xFileControl ** method becomes the return value of this routine. ** ** ^If the second parameter (zDbName) does not match the name of any ** open database file, then SQLITE_ERROR is returned. ^This error ** code is not remembered and will not be recalled by [sqlite3_errcode()] ** or [sqlite3_errmsg()]. The underlying xFileControl method might ** also return SQLITE_ERROR. There is no way to distinguish between ** an incorrect zDbName and an SQLITE_ERROR return from the underlying ** xFileControl method. ** ** See also: [SQLITE_FCNTL_LOCKSTATE] */ SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); /* ** CAPI3REF: Testing Interface ** ** ^The sqlite3_test_control() interface is used to read out internal ** state of SQLite and to inject faults into SQLite for testing ** purposes. ^The first parameter is an operation code that determines ** the number, meaning, and operation of all subsequent parameters. ** ** This interface is not for use by applications. It exists solely ** for verifying the correct operation of the SQLite library. Depending ** on how the SQLite library is compiled, this interface might not exist. ** ** The details of the operation codes, their meanings, the parameters ** they take, and what they do are all subject to change without notice. ** Unlike most of the SQLite API, this function is not guaranteed to ** operate consistently from one release to the next. */ SQLITE_API int sqlite3_test_control(int op, ...); /* ** CAPI3REF: Testing Interface Operation Codes ** ** These constants are the valid operation code parameters used ** as the first argument to [sqlite3_test_control()]. ** ** These parameters and their meanings are subject to change ** without notice. These values are for testing purposes only. ** Applications should not use any of these parameters or the ** [sqlite3_test_control()] interface. */ #define SQLITE_TESTCTRL_FIRST 5 #define SQLITE_TESTCTRL_PRNG_SAVE 5 #define SQLITE_TESTCTRL_PRNG_RESTORE 6 #define SQLITE_TESTCTRL_PRNG_RESET 7 #define SQLITE_TESTCTRL_BITVEC_TEST 8 #define SQLITE_TESTCTRL_FAULT_INSTALL 9 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 #define SQLITE_TESTCTRL_LAST 16 /* ** CAPI3REF: SQLite Runtime Status ** EXPERIMENTAL ** ** ^This interface is used to retrieve runtime status information ** about the preformance of SQLite, and optionally to reset various ** highwater marks. ^The first argument is an integer code for ** the specific parameter to measure. ^(Recognized integer codes ** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^ ** ^The current value of the parameter is returned into *pCurrent. ** ^The highest recorded value is returned in *pHighwater. ^If the ** resetFlag is true, then the highest record value is reset after ** *pHighwater is written. ^(Some parameters do not record the highest ** value. For those parameters ** nothing is written into *pHighwater and the resetFlag is ignored.)^ ** ^(Other parameters record only the highwater mark and not the current ** value. For these latter parameters nothing is written into *pCurrent.)^ ** ** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a ** non-zero [error code] on failure. ** ** This routine is threadsafe but is not atomic. This routine can be ** called while other threads are running the same or different SQLite ** interfaces. However the values returned in *pCurrent and ** *pHighwater reflect the status of SQLite at different points in time ** and it is possible that another thread might change the parameter ** in between the times when *pCurrent and *pHighwater are written. ** ** See also: [sqlite3_db_status()] */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); /* ** CAPI3REF: Status Parameters ** EXPERIMENTAL ** ** These integer constants designate various run-time status parameters ** that can be returned by [sqlite3_status()]. ** **
    ** ^(
    SQLITE_STATUS_MEMORY_USED
    **
    This parameter is the current amount of memory checked out ** using [sqlite3_malloc()], either directly or indirectly. The ** figure includes calls made to [sqlite3_malloc()] by the application ** and internal memory usage by the SQLite library. Scratch memory ** controlled by [SQLITE_CONFIG_SCRATCH] and auxiliary page-cache ** memory controlled by [SQLITE_CONFIG_PAGECACHE] is not included in ** this parameter. The amount returned is the sum of the allocation ** sizes as reported by the xSize method in [sqlite3_mem_methods].
    )^ ** ** ^(
    SQLITE_STATUS_MALLOC_SIZE
    **
    This parameter records the largest memory allocation request ** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their ** internal equivalents). Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. ** The value written into the *pCurrent parameter is undefined.
    )^ ** ** ^(
    SQLITE_STATUS_PAGECACHE_USED
    **
    This parameter returns the number of pages used out of the ** [pagecache memory allocator] that was configured using ** [SQLITE_CONFIG_PAGECACHE]. The ** value returned is in pages, not in bytes.
    )^ ** ** ^(
    SQLITE_STATUS_PAGECACHE_OVERFLOW
    **
    This parameter returns the number of bytes of page cache ** allocation which could not be statisfied by the [SQLITE_CONFIG_PAGECACHE] ** buffer and where forced to overflow to [sqlite3_malloc()]. The ** returned value includes allocations that overflowed because they ** where too large (they were larger than the "sz" parameter to ** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because ** no space was left in the page cache.
    )^ ** ** ^(
    SQLITE_STATUS_PAGECACHE_SIZE
    **
    This parameter records the largest memory allocation request ** handed to [pagecache memory allocator]. Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. ** The value written into the *pCurrent parameter is undefined.
    )^ ** ** ^(
    SQLITE_STATUS_SCRATCH_USED
    **
    This parameter returns the number of allocations used out of the ** [scratch memory allocator] configured using ** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not ** in bytes. Since a single thread may only have one scratch allocation ** outstanding at time, this parameter also reports the number of threads ** using scratch memory at the same time.
    )^ ** ** ^(
    SQLITE_STATUS_SCRATCH_OVERFLOW
    **
    This parameter returns the number of bytes of scratch memory ** allocation which could not be statisfied by the [SQLITE_CONFIG_SCRATCH] ** buffer and where forced to overflow to [sqlite3_malloc()]. The values ** returned include overflows because the requested allocation was too ** larger (that is, because the requested allocation was larger than the ** "sz" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer ** slots were available. **
    )^ ** ** ^(
    SQLITE_STATUS_SCRATCH_SIZE
    **
    This parameter records the largest memory allocation request ** handed to [scratch memory allocator]. Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. ** The value written into the *pCurrent parameter is undefined.
    )^ ** ** ^(
    SQLITE_STATUS_PARSER_STACK
    **
    This parameter records the deepest parser stack. It is only ** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].
    )^ **
    ** ** New status parameters may be added from time to time. */ #define SQLITE_STATUS_MEMORY_USED 0 #define SQLITE_STATUS_PAGECACHE_USED 1 #define SQLITE_STATUS_PAGECACHE_OVERFLOW 2 #define SQLITE_STATUS_SCRATCH_USED 3 #define SQLITE_STATUS_SCRATCH_OVERFLOW 4 #define SQLITE_STATUS_MALLOC_SIZE 5 #define SQLITE_STATUS_PARSER_STACK 6 #define SQLITE_STATUS_PAGECACHE_SIZE 7 #define SQLITE_STATUS_SCRATCH_SIZE 8 /* ** CAPI3REF: Database Connection Status ** EXPERIMENTAL ** ** ^This interface is used to retrieve runtime status information ** about a single [database connection]. ^The first argument is the ** database connection object to be interrogated. ^The second argument ** is the parameter to interrogate. ^Currently, the only allowed value ** for the second parameter is [SQLITE_DBSTATUS_LOOKASIDE_USED]. ** Additional options will likely appear in future releases of SQLite. ** ** ^The current value of the requested parameter is written into *pCur ** and the highest instantaneous value is written into *pHiwtr. ^If ** the resetFlg is true, then the highest instantaneous value is ** reset back down to the current value. ** ** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); /* ** CAPI3REF: Status Parameters for database connections ** EXPERIMENTAL ** ** These constants are the available integer "verbs" that can be passed as ** the second argument to the [sqlite3_db_status()] interface. ** ** New verbs may be added in future releases of SQLite. Existing verbs ** might be discontinued. Applications should check the return code from ** [sqlite3_db_status()] to make sure that the call worked. ** The [sqlite3_db_status()] interface will return a non-zero error code ** if a discontinued or unsupported verb is invoked. ** **
    ** ^(
    SQLITE_DBSTATUS_LOOKASIDE_USED
    **
    This parameter returns the number of lookaside memory slots currently ** checked out.
    )^ **
    */ #define SQLITE_DBSTATUS_LOOKASIDE_USED 0 /* ** CAPI3REF: Prepared Statement Status ** EXPERIMENTAL ** ** ^(Each prepared statement maintains various ** [SQLITE_STMTSTATUS_SORT | counters] that measure the number ** of times it has performed specific operations.)^ These counters can ** be used to monitor the performance characteristics of the prepared ** statements. For example, if the number of table steps greatly exceeds ** the number of table searches or result rows, that would tend to indicate ** that the prepared statement is using a full table scan rather than ** an index. ** ** ^(This interface is used to retrieve and reset counter values from ** a [prepared statement]. The first argument is the prepared statement ** object to be interrogated. The second argument ** is an integer code for a specific [SQLITE_STMTSTATUS_SORT | counter] ** to be interrogated.)^ ** ^The current value of the requested counter is returned. ** ^If the resetFlg is true, then the counter is reset to zero after this ** interface call returns. ** ** See also: [sqlite3_status()] and [sqlite3_db_status()]. */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); /* ** CAPI3REF: Status Parameters for prepared statements ** EXPERIMENTAL ** ** These preprocessor macros define integer codes that name counter ** values associated with the [sqlite3_stmt_status()] interface. ** The meanings of the various counters are as follows: ** **
    **
    SQLITE_STMTSTATUS_FULLSCAN_STEP
    **
    ^This is the number of times that SQLite has stepped forward in ** a table as part of a full table scan. Large numbers for this counter ** may indicate opportunities for performance improvement through ** careful use of indices.
    ** **
    SQLITE_STMTSTATUS_SORT
    **
    ^This is the number of sort operations that have occurred. ** A non-zero value in this counter may indicate an opportunity to ** improvement performance through careful use of indices.
    ** **
    */ #define SQLITE_STMTSTATUS_FULLSCAN_STEP 1 #define SQLITE_STMTSTATUS_SORT 2 /* ** CAPI3REF: Custom Page Cache Object ** EXPERIMENTAL ** ** The sqlite3_pcache type is opaque. It is implemented by ** the pluggable module. The SQLite core has no knowledge of ** its size or internal structure and never deals with the ** sqlite3_pcache object except by holding and passing pointers ** to the object. ** ** See [sqlite3_pcache_methods] for additional information. */ typedef struct sqlite3_pcache sqlite3_pcache; /* ** CAPI3REF: Application Defined Page Cache. ** KEYWORDS: {page cache} ** EXPERIMENTAL ** ** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can ** register an alternative page cache implementation by passing in an ** instance of the sqlite3_pcache_methods structure.)^ The majority of the ** heap memory used by SQLite is used by the page cache to cache data read ** from, or ready to be written to, the database file. By implementing a ** custom page cache using this API, an application can control more ** precisely the amount of memory consumed by SQLite, the way in which ** that memory is allocated and released, and the policies used to ** determine exactly which parts of a database file are cached and for ** how long. ** ** ^(The contents of the sqlite3_pcache_methods structure are copied to an ** internal buffer by SQLite within the call to [sqlite3_config]. Hence ** the application may discard the parameter after the call to ** [sqlite3_config()] returns.)^ ** ** ^The xInit() method is called once for each call to [sqlite3_initialize()] ** (usually only once during the lifetime of the process). ^(The xInit() ** method is passed a copy of the sqlite3_pcache_methods.pArg value.)^ ** ^The xInit() method can set up up global structures and/or any mutexes ** required by the custom page cache implementation. ** ** ^The xShutdown() method is called from within [sqlite3_shutdown()], ** if the application invokes this API. It can be used to clean up ** any outstanding resources before process shutdown, if required. ** ** ^SQLite holds a [SQLITE_MUTEX_RECURSIVE] mutex when it invokes ** the xInit method, so the xInit method need not be threadsafe. ^The ** xShutdown method is only called from [sqlite3_shutdown()] so it does ** not need to be threadsafe either. All other methods must be threadsafe ** in multithreaded applications. ** ** ^SQLite will never invoke xInit() more than once without an intervening ** call to xShutdown(). ** ** ^The xCreate() method is used to construct a new cache instance. SQLite ** will typically create one cache instance for each open database file, ** though this is not guaranteed. ^The ** first parameter, szPage, is the size in bytes of the pages that must ** be allocated by the cache. ^szPage will not be a power of two. ^szPage ** will the page size of the database file that is to be cached plus an ** increment (here called "R") of about 100 or 200. ^SQLite will use the ** extra R bytes on each page to store metadata about the underlying ** database page on disk. The value of R depends ** on the SQLite version, the target platform, and how SQLite was compiled. ** ^R is constant for a particular build of SQLite. ^The second argument to ** xCreate(), bPurgeable, is true if the cache being created will ** be used to cache database pages of a file stored on disk, or ** false if it is used for an in-memory database. ^The cache implementation ** does not have to do anything special based with the value of bPurgeable; ** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will ** never invoke xUnpin() except to deliberately delete a page. ** ^In other words, a cache created with bPurgeable set to false will ** never contain any unpinned pages. ** ** ^(The xCachesize() method may be called at any time by SQLite to set the ** suggested maximum cache-size (number of pages stored by) the cache ** instance passed as the first argument. This is the value configured using ** the SQLite "[PRAGMA cache_size]" command.)^ ^As with the bPurgeable ** parameter, the implementation is not required to do anything with this ** value; it is advisory only. ** ** ^The xPagecount() method should return the number of pages currently ** stored in the cache. ** ** ^The xFetch() method is used to fetch a page and return a pointer to it. ** ^A 'page', in this context, is a buffer of szPage bytes aligned at an ** 8-byte boundary. ^The page to be fetched is determined by the key. ^The ** mimimum key value is 1. After it has been retrieved using xFetch, the page ** is considered to be "pinned". ** ** ^If the requested page is already in the page cache, then the page cache ** implementation must return a pointer to the page buffer with its content ** intact. ^(If the requested page is not already in the cache, then the ** behavior of the cache implementation is determined by the value of the ** createFlag parameter passed to xFetch, according to the following table: ** ** **
    createFlag Behaviour when page is not already in cache **
    0 Do not allocate a new page. Return NULL. **
    1 Allocate a new page if it easy and convenient to do so. ** Otherwise return NULL. **
    2 Make every effort to allocate a new page. Only return ** NULL if allocating a new page is effectively impossible. **
    )^ ** ** SQLite will normally invoke xFetch() with a createFlag of 0 or 1. If ** a call to xFetch() with createFlag==1 returns NULL, then SQLite will ** attempt to unpin one or more cache pages by spilling the content of ** pinned pages to disk and synching the operating system disk cache. After ** attempting to unpin pages, the xFetch() method will be invoked again with ** a createFlag of 2. ** ** ^xUnpin() is called by SQLite with a pointer to a currently pinned page ** as its second argument. ^(If the third parameter, discard, is non-zero, ** then the page should be evicted from the cache. In this case SQLite ** assumes that the next time the page is retrieved from the cache using ** the xFetch() method, it will be zeroed.)^ ^If the discard parameter is ** zero, then the page is considered to be unpinned. ^The cache implementation ** may choose to evict unpinned pages at any time. ** ** ^(The cache is not required to perform any reference counting. A single ** call to xUnpin() unpins the page regardless of the number of prior calls ** to xFetch().)^ ** ** ^The xRekey() method is used to change the key value associated with the ** page passed as the second argument from oldKey to newKey. ^If the cache ** previously contains an entry associated with newKey, it should be ** discarded. ^Any prior cache entry associated with newKey is guaranteed not ** to be pinned. ** ** ^When SQLite calls the xTruncate() method, the cache must discard all ** existing cache entries with page numbers (keys) greater than or equal ** to the value of the iLimit parameter passed to xTruncate(). ^If any ** of these pages are pinned, they are implicitly unpinned, meaning that ** they can be safely discarded. ** ** ^The xDestroy() method is used to delete a cache allocated by xCreate(). ** All resources associated with the specified cache should be freed. ^After ** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] ** handle invalid, and will not use it with any other sqlite3_pcache_methods ** functions. */ typedef struct sqlite3_pcache_methods sqlite3_pcache_methods; struct sqlite3_pcache_methods { void *pArg; int (*xInit)(void*); void (*xShutdown)(void*); sqlite3_pcache *(*xCreate)(int szPage, int bPurgeable); void (*xCachesize)(sqlite3_pcache*, int nCachesize); int (*xPagecount)(sqlite3_pcache*); void *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); void (*xUnpin)(sqlite3_pcache*, void*, int discard); void (*xRekey)(sqlite3_pcache*, void*, unsigned oldKey, unsigned newKey); void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); void (*xDestroy)(sqlite3_pcache*); }; /* ** CAPI3REF: Online Backup Object ** EXPERIMENTAL ** ** The sqlite3_backup object records state information about an ongoing ** online backup operation. ^The sqlite3_backup object is created by ** a call to [sqlite3_backup_init()] and is destroyed by a call to ** [sqlite3_backup_finish()]. ** ** See Also: [Using the SQLite Online Backup API] */ typedef struct sqlite3_backup sqlite3_backup; /* ** CAPI3REF: Online Backup API. ** EXPERIMENTAL ** ** The backup API copies the content of one database into another. ** It is useful either for creating backups of databases or ** for copying in-memory databases to or from persistent files. ** ** See Also: [Using the SQLite Online Backup API] ** ** ^Exclusive access is required to the destination database for the ** duration of the operation. ^However the source database is only ** read-locked while it is actually being read; it is not locked ** continuously for the entire backup operation. ^Thus, the backup may be ** performed on a live source database without preventing other users from ** reading or writing to the source database while the backup is underway. ** ** ^(To perform a backup operation: **
      **
    1. sqlite3_backup_init() is called once to initialize the ** backup, **
    2. sqlite3_backup_step() is called one or more times to transfer ** the data between the two databases, and finally **
    3. sqlite3_backup_finish() is called to release all resources ** associated with the backup operation. **
    )^ ** There should be exactly one call to sqlite3_backup_finish() for each ** successful call to sqlite3_backup_init(). ** ** sqlite3_backup_init() ** ** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the ** [database connection] associated with the destination database ** and the database name, respectively. ** ^The database name is "main" for the main database, "temp" for the ** temporary database, or the name specified after the AS keyword in ** an [ATTACH] statement for an attached database. ** ^The S and M arguments passed to ** sqlite3_backup_init(D,N,S,M) identify the [database connection] ** and database name of the source database, respectively. ** ^The source and destination [database connections] (parameters S and D) ** must be different or else sqlite3_backup_init(D,N,S,M) will file with ** an error. ** ** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is ** returned and an error code and error message are store3d in the ** destination [database connection] D. ** ^The error code and message for the failed call to sqlite3_backup_init() ** can be retrieved using the [sqlite3_errcode()], [sqlite3_errmsg()], and/or ** [sqlite3_errmsg16()] functions. ** ^A successful call to sqlite3_backup_init() returns a pointer to an ** [sqlite3_backup] object. ** ^The [sqlite3_backup] object may be used with the sqlite3_backup_step() and ** sqlite3_backup_finish() functions to perform the specified backup ** operation. ** ** sqlite3_backup_step() ** ** ^Function sqlite3_backup_step(B,N) will copy up to N pages between ** the source and destination databases specified by [sqlite3_backup] object B. ** ^If N is negative, all remaining source pages are copied. ** ^If sqlite3_backup_step(B,N) successfully copies N pages and there ** are still more pages to be copied, then the function resturns [SQLITE_OK]. ** ^If sqlite3_backup_step(B,N) successfully finishes copying all pages ** from source to destination, then it returns [SQLITE_DONE]. ** ^If an error occurs while running sqlite3_backup_step(B,N), ** then an [error code] is returned. ^As well as [SQLITE_OK] and ** [SQLITE_DONE], a call to sqlite3_backup_step() may return [SQLITE_READONLY], ** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an ** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code. ** ** ^The sqlite3_backup_step() might return [SQLITE_READONLY] if the destination ** database was opened read-only or if ** the destination is an in-memory database with a different page size ** from the source database. ** ** ^If sqlite3_backup_step() cannot obtain a required file-system lock, then ** the [sqlite3_busy_handler | busy-handler function] ** is invoked (if one is specified). ^If the ** busy-handler returns non-zero before the lock is available, then ** [SQLITE_BUSY] is returned to the caller. ^In this case the call to ** sqlite3_backup_step() can be retried later. ^If the source ** [database connection] ** is being used to write to the source database when sqlite3_backup_step() ** is called, then [SQLITE_LOCKED] is returned immediately. ^Again, in this ** case the call to sqlite3_backup_step() can be retried later on. ^(If ** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX], [SQLITE_NOMEM], or ** [SQLITE_READONLY] is returned, then ** there is no point in retrying the call to sqlite3_backup_step(). These ** errors are considered fatal.)^ The application must accept ** that the backup operation has failed and pass the backup operation handle ** to the sqlite3_backup_finish() to release associated resources. ** ** ^The first call to sqlite3_backup_step() obtains an exclusive lock ** on the destination file. ^The exclusive lock is not released until either ** sqlite3_backup_finish() is called or the backup operation is complete ** and sqlite3_backup_step() returns [SQLITE_DONE]. ^Every call to ** sqlite3_backup_step() obtains a [shared lock] on the source database that ** lasts for the duration of the sqlite3_backup_step() call. ** ^Because the source database is not locked between calls to ** sqlite3_backup_step(), the source database may be modified mid-way ** through the backup process. ^If the source database is modified by an ** external process or via a database connection other than the one being ** used by the backup operation, then the backup will be automatically ** restarted by the next call to sqlite3_backup_step(). ^If the source ** database is modified by the using the same database connection as is used ** by the backup operation, then the backup database is automatically ** updated at the same time. ** ** sqlite3_backup_finish() ** ** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the ** application wishes to abandon the backup operation, the application ** should destroy the [sqlite3_backup] by passing it to sqlite3_backup_finish(). ** ^The sqlite3_backup_finish() interfaces releases all ** resources associated with the [sqlite3_backup] object. ** ^If sqlite3_backup_step() has not yet returned [SQLITE_DONE], then any ** active write-transaction on the destination database is rolled back. ** The [sqlite3_backup] object is invalid ** and may not be used following a call to sqlite3_backup_finish(). ** ** ^The value returned by sqlite3_backup_finish is [SQLITE_OK] if no ** sqlite3_backup_step() errors occurred, regardless or whether or not ** sqlite3_backup_step() completed. ** ^If an out-of-memory condition or IO error occurred during any prior ** sqlite3_backup_step() call on the same [sqlite3_backup] object, then ** sqlite3_backup_finish() returns the corresponding [error code]. ** ** ^A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step() ** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** ** sqlite3_backup_remaining(), sqlite3_backup_pagecount() ** ** ^Each call to sqlite3_backup_step() sets two values inside ** the [sqlite3_backup] object: the number of pages still to be backed ** up and the total number of pages in the source databae file. ** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces ** retrieve these two values, respectively. ** ** ^The values returned by these functions are only updated by ** sqlite3_backup_step(). ^If the source database is modified during a backup ** operation, then the values are not updated to account for any extra ** pages that need to be updated or the size of the source database file ** changing. ** ** Concurrent Usage of Database Handles ** ** ^The source [database connection] may be used by the application for other ** purposes while a backup operation is underway or being initialized. ** ^If SQLite is compiled and configured to support threadsafe database ** connections, then the source database connection may be used concurrently ** from within other threads. ** ** However, the application must guarantee that the destination ** [database connection] is not passed to any other API (by any thread) after ** sqlite3_backup_init() is called and before the corresponding call to ** sqlite3_backup_finish(). SQLite does not currently check to see ** if the application incorrectly accesses the destination [database connection] ** and so no error code is reported, but the operations may malfunction ** nevertheless. Use of the destination database connection while a ** backup is in progress might also also cause a mutex deadlock. ** ** If running in [shared cache mode], the application must ** guarantee that the shared cache used by the destination database ** is not accessed while the backup is running. In practice this means ** that the application must guarantee that the disk file being ** backed up to is not accessed by any connection within the process, ** not just the specific connection that was passed to sqlite3_backup_init(). ** ** The [sqlite3_backup] object itself is partially threadsafe. Multiple ** threads may safely make multiple concurrent calls to sqlite3_backup_step(). ** However, the sqlite3_backup_remaining() and sqlite3_backup_pagecount() ** APIs are not strictly speaking threadsafe. If they are invoked at the ** same time as another thread is invoking sqlite3_backup_step() it is ** possible that they return invalid values. */ SQLITE_API sqlite3_backup *sqlite3_backup_init( sqlite3 *pDest, /* Destination database handle */ const char *zDestName, /* Destination database name */ sqlite3 *pSource, /* Source database handle */ const char *zSourceName /* Source database name */ ); SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage); SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p); SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p); SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); /* ** CAPI3REF: Unlock Notification ** EXPERIMENTAL ** ** ^When running in shared-cache mode, a database operation may fail with ** an [SQLITE_LOCKED] error if the required locks on the shared-cache or ** individual tables within the shared-cache cannot be obtained. See ** [SQLite Shared-Cache Mode] for a description of shared-cache locking. ** ^This API may be used to register a callback that SQLite will invoke ** when the connection currently holding the required lock relinquishes it. ** ^This API is only available if the library was compiled with the ** [SQLITE_ENABLE_UNLOCK_NOTIFY] C-preprocessor symbol defined. ** ** See Also: [Using the SQLite Unlock Notification Feature]. ** ** ^Shared-cache locks are released when a database connection concludes ** its current transaction, either by committing it or rolling it back. ** ** ^When a connection (known as the blocked connection) fails to obtain a ** shared-cache lock and SQLITE_LOCKED is returned to the caller, the ** identity of the database connection (the blocking connection) that ** has locked the required resource is stored internally. ^After an ** application receives an SQLITE_LOCKED error, it may call the ** sqlite3_unlock_notify() method with the blocked connection handle as ** the first argument to register for a callback that will be invoked ** when the blocking connections current transaction is concluded. ^The ** callback is invoked from within the [sqlite3_step] or [sqlite3_close] ** call that concludes the blocking connections transaction. ** ** ^(If sqlite3_unlock_notify() is called in a multi-threaded application, ** there is a chance that the blocking connection will have already ** concluded its transaction by the time sqlite3_unlock_notify() is invoked. ** If this happens, then the specified callback is invoked immediately, ** from within the call to sqlite3_unlock_notify().)^ ** ** ^If the blocked connection is attempting to obtain a write-lock on a ** shared-cache table, and more than one other connection currently holds ** a read-lock on the same table, then SQLite arbitrarily selects one of ** the other connections to use as the blocking connection. ** ** ^(There may be at most one unlock-notify callback registered by a ** blocked connection. If sqlite3_unlock_notify() is called when the ** blocked connection already has a registered unlock-notify callback, ** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing ** unlock-notify callback is cancelled. ^The blocked connections ** unlock-notify callback may also be canceled by closing the blocked ** connection using [sqlite3_close()]. ** ** The unlock-notify callback is not reentrant. If an application invokes ** any sqlite3_xxx API functions from within an unlock-notify callback, a ** crash or deadlock may be the result. ** ** ^Unless deadlock is detected (see below), sqlite3_unlock_notify() always ** returns SQLITE_OK. ** ** Callback Invocation Details ** ** When an unlock-notify callback is registered, the application provides a ** single void* pointer that is passed to the callback when it is invoked. ** However, the signature of the callback function allows SQLite to pass ** it an array of void* context pointers. The first argument passed to ** an unlock-notify callback is a pointer to an array of void* pointers, ** and the second is the number of entries in the array. ** ** When a blocking connections transaction is concluded, there may be ** more than one blocked connection that has registered for an unlock-notify ** callback. ^If two or more such blocked connections have specified the ** same callback function, then instead of invoking the callback function ** multiple times, it is invoked once with the set of void* context pointers ** specified by the blocked connections bundled together into an array. ** This gives the application an opportunity to prioritize any actions ** related to the set of unblocked database connections. ** ** Deadlock Detection ** ** Assuming that after registering for an unlock-notify callback a ** database waits for the callback to be issued before taking any further ** action (a reasonable assumption), then using this API may cause the ** application to deadlock. For example, if connection X is waiting for ** connection Y's transaction to be concluded, and similarly connection ** Y is waiting on connection X's transaction, then neither connection ** will proceed and the system may remain deadlocked indefinitely. ** ** To avoid this scenario, the sqlite3_unlock_notify() performs deadlock ** detection. ^If a given call to sqlite3_unlock_notify() would put the ** system in a deadlocked state, then SQLITE_LOCKED is returned and no ** unlock-notify callback is registered. The system is said to be in ** a deadlocked state if connection A has registered for an unlock-notify ** callback on the conclusion of connection B's transaction, and connection ** B has itself registered for an unlock-notify callback when connection ** A's transaction is concluded. ^Indirect deadlock is also detected, so ** the system is also considered to be deadlocked if connection B has ** registered for an unlock-notify callback on the conclusion of connection ** C's transaction, where connection C is waiting on connection A. ^Any ** number of levels of indirection are allowed. ** ** The "DROP TABLE" Exception ** ** When a call to [sqlite3_step()] returns SQLITE_LOCKED, it is almost ** always appropriate to call sqlite3_unlock_notify(). There is however, ** one exception. When executing a "DROP TABLE" or "DROP INDEX" statement, ** SQLite checks if there are any currently executing SELECT statements ** that belong to the same connection. If there are, SQLITE_LOCKED is ** returned. In this case there is no "blocking connection", so invoking ** sqlite3_unlock_notify() results in the unlock-notify callback being ** invoked immediately. If the application then re-attempts the "DROP TABLE" ** or "DROP INDEX" query, an infinite loop might be the result. ** ** One way around this problem is to check the extended error code returned ** by an sqlite3_step() call. ^(If there is a blocking connection, then the ** extended error code is set to SQLITE_LOCKED_SHAREDCACHE. Otherwise, in ** the special "DROP TABLE/INDEX" case, the extended error code is just ** SQLITE_LOCKED.)^ */ SQLITE_API int sqlite3_unlock_notify( sqlite3 *pBlocked, /* Waiting connection */ void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ void *pNotifyArg /* Argument to pass to xNotify */ ); /* ** CAPI3REF: String Comparison ** EXPERIMENTAL ** ** ^The [sqlite3_strnicmp()] API allows applications and extensions to ** compare the contents of two buffers containing UTF-8 strings in a ** case-indendent fashion, using the same definition of case independence ** that SQLite uses internally when comparing identifiers. */ SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); /* ** CAPI3REF: Error Logging Interface ** EXPERIMENTAL ** ** ^The [sqlite3_log()] interface writes a message into the error log ** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()]. ** ^If logging is enabled, the zFormat string and subsequent arguments are ** passed through to [sqlite3_vmprintf()] to generate the final output string. ** ** The sqlite3_log() interface is intended for use by extensions such as ** virtual tables, collating functions, and SQL functions. While there is ** nothing to prevent an application from calling sqlite3_log(), doing so ** is considered bad form. ** ** The zFormat string must not be NULL. ** ** To avoid deadlocks and other threading problems, the sqlite3_log() routine ** will not use dynamically allocated memory. The log message is stored in ** a fixed-length buffer on the stack. If the log message is longer than ** a few hundred characters, it will be truncated to the length of the ** buffer. */ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...); /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double #endif #if 0 } /* End of the 'extern "C"' block */ #endif #endif /************** End of sqlite3.h *********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /************** Include hash.h in the middle of sqliteInt.h ******************/ /************** Begin file hash.h ********************************************/ /* ** 2001 September 22 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This is the header file for the generic hash-table implemenation ** used in SQLite. */ #ifndef _SQLITE_HASH_H_ #define _SQLITE_HASH_H_ /* Forward declarations of structures. */ typedef struct Hash Hash; typedef struct HashElem HashElem; /* A complete hash table is an instance of the following structure. ** The internals of this structure are intended to be opaque -- client ** code should not attempt to access or modify the fields of this structure ** directly. Change this structure only by using the routines below. ** However, some of the "procedures" and "functions" for modifying and ** accessing this structure are really macros, so we can't really make ** this structure opaque. ** ** All elements of the hash table are on a single doubly-linked list. ** Hash.first points to the head of this list. ** ** There are Hash.htsize buckets. Each bucket points to a spot in ** the global doubly-linked list. The contents of the bucket are the ** element pointed to plus the next _ht.count-1 elements in the list. ** ** Hash.htsize and Hash.ht may be zero. In that case lookup is done ** by a linear search of the global list. For small tables, the ** Hash.ht table is never allocated because if there are few elements ** in the table, it is faster to do a linear search than to manage ** the hash table. */ struct Hash { unsigned int htsize; /* Number of buckets in the hash table */ unsigned int count; /* Number of entries in this table */ HashElem *first; /* The first element of the array */ struct _ht { /* the hash table */ int count; /* Number of entries with this hash */ HashElem *chain; /* Pointer to first entry with this hash */ } *ht; }; /* Each element in the hash table is an instance of the following ** structure. All elements are stored on a single doubly-linked list. ** ** Again, this structure is intended to be opaque, but it can't really ** be opaque because it is used by macros. */ struct HashElem { HashElem *next, *prev; /* Next and previous elements in the table */ void *data; /* Data associated with this element */ const char *pKey; int nKey; /* Key associated with this element */ }; /* ** Access routines. To delete, insert a NULL pointer. */ SQLITE_PRIVATE void sqlite3HashInit(Hash*); SQLITE_PRIVATE void *sqlite3HashInsert(Hash*, const char *pKey, int nKey, void *pData); SQLITE_PRIVATE void *sqlite3HashFind(const Hash*, const char *pKey, int nKey); SQLITE_PRIVATE void sqlite3HashClear(Hash*); /* ** Macros for looping over all elements of a hash table. The idiom is ** like this: ** ** Hash h; ** HashElem *p; ** ... ** for(p=sqliteHashFirst(&h); p; p=sqliteHashNext(p)){ ** SomeStructure *pData = sqliteHashData(p); ** // do something with pData ** } */ #define sqliteHashFirst(H) ((H)->first) #define sqliteHashNext(E) ((E)->next) #define sqliteHashData(E) ((E)->data) /* #define sqliteHashKey(E) ((E)->pKey) // NOT USED */ /* #define sqliteHashKeysize(E) ((E)->nKey) // NOT USED */ /* ** Number of entries in a hash table */ /* #define sqliteHashCount(H) ((H)->count) // NOT USED */ #endif /* _SQLITE_HASH_H_ */ /************** End of hash.h ************************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /************** Include parse.h in the middle of sqliteInt.h *****************/ /************** Begin file parse.h *******************************************/ #define TK_SEMI 1 #define TK_EXPLAIN 2 #define TK_QUERY 3 #define TK_PLAN 4 #define TK_BEGIN 5 #define TK_TRANSACTION 6 #define TK_DEFERRED 7 #define TK_IMMEDIATE 8 #define TK_EXCLUSIVE 9 #define TK_COMMIT 10 #define TK_END 11 #define TK_ROLLBACK 12 #define TK_SAVEPOINT 13 #define TK_RELEASE 14 #define TK_TO 15 #define TK_TABLE 16 #define TK_CREATE 17 #define TK_IF 18 #define TK_NOT 19 #define TK_EXISTS 20 #define TK_TEMP 21 #define TK_LP 22 #define TK_RP 23 #define TK_AS 24 #define TK_COMMA 25 #define TK_ID 26 #define TK_INDEXED 27 #define TK_ABORT 28 #define TK_ACTION 29 #define TK_AFTER 30 #define TK_ANALYZE 31 #define TK_ASC 32 #define TK_ATTACH 33 #define TK_BEFORE 34 #define TK_BY 35 #define TK_CASCADE 36 #define TK_CAST 37 #define TK_COLUMNKW 38 #define TK_CONFLICT 39 #define TK_DATABASE 40 #define TK_DESC 41 #define TK_DETACH 42 #define TK_EACH 43 #define TK_FAIL 44 #define TK_FOR 45 #define TK_IGNORE 46 #define TK_INITIALLY 47 #define TK_INSTEAD 48 #define TK_LIKE_KW 49 #define TK_MATCH 50 #define TK_NO 51 #define TK_KEY 52 #define TK_OF 53 #define TK_OFFSET 54 #define TK_PRAGMA 55 #define TK_RAISE 56 #define TK_REPLACE 57 #define TK_RESTRICT 58 #define TK_ROW 59 #define TK_TRIGGER 60 #define TK_VACUUM 61 #define TK_VIEW 62 #define TK_VIRTUAL 63 #define TK_REINDEX 64 #define TK_RENAME 65 #define TK_CTIME_KW 66 #define TK_ANY 67 #define TK_OR 68 #define TK_AND 69 #define TK_IS 70 #define TK_BETWEEN 71 #define TK_IN 72 #define TK_ISNULL 73 #define TK_NOTNULL 74 #define TK_NE 75 #define TK_EQ 76 #define TK_GT 77 #define TK_LE 78 #define TK_LT 79 #define TK_GE 80 #define TK_ESCAPE 81 #define TK_BITAND 82 #define TK_BITOR 83 #define TK_LSHIFT 84 #define TK_RSHIFT 85 #define TK_PLUS 86 #define TK_MINUS 87 #define TK_STAR 88 #define TK_SLASH 89 #define TK_REM 90 #define TK_CONCAT 91 #define TK_COLLATE 92 #define TK_BITNOT 93 #define TK_STRING 94 #define TK_JOIN_KW 95 #define TK_CONSTRAINT 96 #define TK_DEFAULT 97 #define TK_NULL 98 #define TK_PRIMARY 99 #define TK_UNIQUE 100 #define TK_CHECK 101 #define TK_REFERENCES 102 #define TK_AUTOINCR 103 #define TK_ON 104 #define TK_INSERT 105 #define TK_DELETE 106 #define TK_UPDATE 107 #define TK_SET 108 #define TK_DEFERRABLE 109 #define TK_FOREIGN 110 #define TK_DROP 111 #define TK_UNION 112 #define TK_ALL 113 #define TK_EXCEPT 114 #define TK_INTERSECT 115 #define TK_SELECT 116 #define TK_DISTINCT 117 #define TK_DOT 118 #define TK_FROM 119 #define TK_JOIN 120 #define TK_USING 121 #define TK_ORDER 122 #define TK_GROUP 123 #define TK_HAVING 124 #define TK_LIMIT 125 #define TK_WHERE 126 #define TK_INTO 127 #define TK_VALUES 128 #define TK_INTEGER 129 #define TK_FLOAT 130 #define TK_BLOB 131 #define TK_REGISTER 132 #define TK_VARIABLE 133 #define TK_CASE 134 #define TK_WHEN 135 #define TK_THEN 136 #define TK_ELSE 137 #define TK_INDEX 138 #define TK_ALTER 139 #define TK_ADD 140 #define TK_TO_TEXT 141 #define TK_TO_BLOB 142 #define TK_TO_NUMERIC 143 #define TK_TO_INT 144 #define TK_TO_REAL 145 #define TK_ISNOT 146 #define TK_END_OF_FILE 147 #define TK_ILLEGAL 148 #define TK_SPACE 149 #define TK_UNCLOSED_STRING 150 #define TK_FUNCTION 151 #define TK_COLUMN 152 #define TK_AGG_FUNCTION 153 #define TK_AGG_COLUMN 154 #define TK_CONST_FUNC 155 #define TK_UMINUS 156 #define TK_UPLUS 157 /************** End of parse.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ #include #include #include #include #include /* ** If compiling for a processor that lacks floating point support, ** substitute integer for floating-point */ #ifdef SQLITE_OMIT_FLOATING_POINT # define double sqlite_int64 # define LONGDOUBLE_TYPE sqlite_int64 # ifndef SQLITE_BIG_DBL # define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) # endif # define SQLITE_OMIT_DATETIME_FUNCS 1 # define SQLITE_OMIT_TRACE 1 # undef SQLITE_MIXED_ENDIAN_64BIT_FLOAT # undef SQLITE_HAVE_ISNAN #endif #ifndef SQLITE_BIG_DBL # define SQLITE_BIG_DBL (1e99) #endif /* ** OMIT_TEMPDB is set to 1 if SQLITE_OMIT_TEMPDB is defined, or 0 ** afterward. Having this macro allows us to cause the C compiler ** to omit code used by TEMP tables without messy #ifndef statements. */ #ifdef SQLITE_OMIT_TEMPDB #define OMIT_TEMPDB 1 #else #define OMIT_TEMPDB 0 #endif /* ** The "file format" number is an integer that is incremented whenever ** the VDBE-level file format changes. The following macros define the ** the default file format for new databases and the maximum file format ** that the library can read. */ #define SQLITE_MAX_FILE_FORMAT 4 #ifndef SQLITE_DEFAULT_FILE_FORMAT # define SQLITE_DEFAULT_FILE_FORMAT 1 #endif /* ** Determine whether triggers are recursive by default. This can be ** changed at run-time using a pragma. */ #ifndef SQLITE_DEFAULT_RECURSIVE_TRIGGERS # define SQLITE_DEFAULT_RECURSIVE_TRIGGERS 0 #endif /* ** Provide a default value for SQLITE_TEMP_STORE in case it is not specified ** on the command-line */ #ifndef SQLITE_TEMP_STORE # define SQLITE_TEMP_STORE 1 #endif /* ** GCC does not define the offsetof() macro so we'll have to do it ** ourselves. */ #ifndef offsetof #define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD)) #endif /* ** Check to see if this machine uses EBCDIC. (Yes, believe it or ** not, there are still machines out there that use EBCDIC.) */ #if 'A' == '\301' # define SQLITE_EBCDIC 1 #else # define SQLITE_ASCII 1 #endif /* ** Integers of known sizes. These typedefs might change for architectures ** where the sizes very. Preprocessor macros are available so that the ** types can be conveniently redefined at compile-type. Like this: ** ** cc '-DUINTPTR_TYPE=long long int' ... */ #ifndef UINT32_TYPE # ifdef HAVE_UINT32_T # define UINT32_TYPE uint32_t # else # define UINT32_TYPE unsigned int # endif #endif #ifndef UINT16_TYPE # ifdef HAVE_UINT16_T # define UINT16_TYPE uint16_t # else # define UINT16_TYPE unsigned short int # endif #endif #ifndef INT16_TYPE # ifdef HAVE_INT16_T # define INT16_TYPE int16_t # else # define INT16_TYPE short int # endif #endif #ifndef UINT8_TYPE # ifdef HAVE_UINT8_T # define UINT8_TYPE uint8_t # else # define UINT8_TYPE unsigned char # endif #endif #ifndef INT8_TYPE # ifdef HAVE_INT8_T # define INT8_TYPE int8_t # else # define INT8_TYPE signed char # endif #endif #ifndef LONGDOUBLE_TYPE # define LONGDOUBLE_TYPE long double #endif typedef sqlite_int64 i64; /* 8-byte signed integer */ typedef sqlite_uint64 u64; /* 8-byte unsigned integer */ typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ typedef INT16_TYPE i16; /* 2-byte signed integer */ typedef UINT8_TYPE u8; /* 1-byte unsigned integer */ typedef INT8_TYPE i8; /* 1-byte signed integer */ /* ** SQLITE_MAX_U32 is a u64 constant that is the maximum u64 value ** that can be stored in a u32 without loss of data. The value ** is 0x00000000ffffffff. But because of quirks of some compilers, we ** have to specify the value in the less intuitive manner shown: */ #define SQLITE_MAX_U32 ((((u64)1)<<32)-1) /* ** Macros to determine whether the machine is big or little endian, ** evaluated at runtime. */ #ifdef SQLITE_AMALGAMATION SQLITE_PRIVATE const int sqlite3one = 1; #else SQLITE_PRIVATE const int sqlite3one; #endif #if defined(i386) || defined(__i386__) || defined(_M_IX86)\ || defined(__x86_64) || defined(__x86_64__) # define SQLITE_BIGENDIAN 0 # define SQLITE_LITTLEENDIAN 1 # define SQLITE_UTF16NATIVE SQLITE_UTF16LE #else # define SQLITE_BIGENDIAN (*(char *)(&sqlite3one)==0) # define SQLITE_LITTLEENDIAN (*(char *)(&sqlite3one)==1) # define SQLITE_UTF16NATIVE (SQLITE_BIGENDIAN?SQLITE_UTF16BE:SQLITE_UTF16LE) #endif /* ** Constants for the largest and smallest possible 64-bit signed integers. ** These macros are designed to work correctly on both 32-bit and 64-bit ** compilers. */ #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) /* ** Round up a number to the next larger multiple of 8. This is used ** to force 8-byte alignment on 64-bit architectures. */ #define ROUND8(x) (((x)+7)&~7) /* ** Round down to the nearest multiple of 8 */ #define ROUNDDOWN8(x) ((x)&~7) /* ** Assert that the pointer X is aligned to an 8-byte boundary. This ** macro is used only within assert() to verify that the code gets ** all alignment restrictions correct. ** ** Except, if SQLITE_4_BYTE_ALIGNED_MALLOC is defined, then the ** underlying malloc() implemention might return us 4-byte aligned ** pointers. In that case, only verify 4-byte alignment. */ #ifdef SQLITE_4_BYTE_ALIGNED_MALLOC # define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&3)==0) #else # define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&7)==0) #endif /* ** An instance of the following structure is used to store the busy-handler ** callback for a given sqlite handle. ** ** The sqlite.busyHandler member of the sqlite struct contains the busy ** callback for the database handle. Each pager opened via the sqlite ** handle is passed a pointer to sqlite.busyHandler. The busy-handler ** callback is currently invoked only from within pager.c. */ typedef struct BusyHandler BusyHandler; struct BusyHandler { int (*xFunc)(void *,int); /* The busy callback */ void *pArg; /* First arg to busy callback */ int nBusy; /* Incremented with each busy call */ }; /* ** Name of the master database table. The master database table ** is a special table that holds the names and attributes of all ** user tables and indices. */ #define MASTER_NAME "sqlite_master" #define TEMP_MASTER_NAME "sqlite_temp_master" /* ** The root-page of the master database table. */ #define MASTER_ROOT 1 /* ** The name of the schema table. */ #define SCHEMA_TABLE(x) ((!OMIT_TEMPDB)&&(x==1)?TEMP_MASTER_NAME:MASTER_NAME) /* ** A convenience macro that returns the number of elements in ** an array. */ #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0]))) /* ** The following value as a destructor means to use sqlite3DbFree(). ** This is an internal extension to SQLITE_STATIC and SQLITE_TRANSIENT. */ #define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3DbFree) /* ** When SQLITE_OMIT_WSD is defined, it means that the target platform does ** not support Writable Static Data (WSD) such as global and static variables. ** All variables must either be on the stack or dynamically allocated from ** the heap. When WSD is unsupported, the variable declarations scattered ** throughout the SQLite code must become constants instead. The SQLITE_WSD ** macro is used for this purpose. And instead of referencing the variable ** directly, we use its constant as a key to lookup the run-time allocated ** buffer that holds real variable. The constant is also the initializer ** for the run-time allocated buffer. ** ** In the usual case where WSD is supported, the SQLITE_WSD and GLOBAL ** macros become no-ops and have zero performance impact. */ #ifdef SQLITE_OMIT_WSD #define SQLITE_WSD const #define GLOBAL(t,v) (*(t*)sqlite3_wsd_find((void*)&(v), sizeof(v))) #define sqlite3GlobalConfig GLOBAL(struct Sqlite3Config, sqlite3Config) SQLITE_API int sqlite3_wsd_init(int N, int J); SQLITE_API void *sqlite3_wsd_find(void *K, int L); #else #define SQLITE_WSD #define GLOBAL(t,v) v #define sqlite3GlobalConfig sqlite3Config #endif /* ** The following macros are used to suppress compiler warnings and to ** make it clear to human readers when a function parameter is deliberately ** left unused within the body of a function. This usually happens when ** a function is called via a function pointer. For example the ** implementation of an SQL aggregate step callback may not use the ** parameter indicating the number of arguments passed to the aggregate, ** if it knows that this is enforced elsewhere. ** ** When a function parameter is not used at all within the body of a function, ** it is generally named "NotUsed" or "NotUsed2" to make things even clearer. ** However, these macros may also be used to suppress warnings related to ** parameters that may or may not be used depending on compilation options. ** For example those parameters only used in assert() statements. In these ** cases the parameters are named as per the usual conventions. */ #define UNUSED_PARAMETER(x) (void)(x) #define UNUSED_PARAMETER2(x,y) UNUSED_PARAMETER(x),UNUSED_PARAMETER(y) /* ** Forward references to structures */ typedef struct AggInfo AggInfo; typedef struct AuthContext AuthContext; typedef struct AutoincInfo AutoincInfo; typedef struct Bitvec Bitvec; typedef struct CollSeq CollSeq; typedef struct Column Column; typedef struct Db Db; typedef struct Schema Schema; typedef struct Expr Expr; typedef struct ExprList ExprList; typedef struct ExprSpan ExprSpan; typedef struct FKey FKey; typedef struct FuncDef FuncDef; typedef struct FuncDefHash FuncDefHash; typedef struct IdList IdList; typedef struct Index Index; typedef struct IndexSample IndexSample; typedef struct KeyClass KeyClass; typedef struct KeyInfo KeyInfo; typedef struct Lookaside Lookaside; typedef struct LookasideSlot LookasideSlot; typedef struct Module Module; typedef struct NameContext NameContext; typedef struct Parse Parse; typedef struct RowSet RowSet; typedef struct Savepoint Savepoint; typedef struct Select Select; typedef struct SrcList SrcList; typedef struct StrAccum StrAccum; typedef struct Table Table; typedef struct TableLock TableLock; typedef struct Token Token; typedef struct Trigger Trigger; typedef struct TriggerPrg TriggerPrg; typedef struct TriggerStep TriggerStep; typedef struct UnpackedRecord UnpackedRecord; typedef struct VTable VTable; typedef struct Walker Walker; typedef struct WherePlan WherePlan; typedef struct WhereInfo WhereInfo; typedef struct WhereLevel WhereLevel; /* ** Defer sourcing vdbe.h and btree.h until after the "u8" and ** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque ** pointer types (i.e. FuncDef) defined above. */ /************** Include btree.h in the middle of sqliteInt.h *****************/ /************** Begin file btree.h *******************************************/ /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This header file defines the interface that the sqlite B-Tree file ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. */ #ifndef _BTREE_H_ #define _BTREE_H_ /* TODO: This definition is just included so other modules compile. It ** needs to be revisited. */ #define SQLITE_N_BTREE_META 10 /* ** If defined as non-zero, auto-vacuum is enabled by default. Otherwise ** it must be turned on for each database using "PRAGMA auto_vacuum = 1". */ #ifndef SQLITE_DEFAULT_AUTOVACUUM #define SQLITE_DEFAULT_AUTOVACUUM 0 #endif #define BTREE_AUTOVACUUM_NONE 0 /* Do not do auto-vacuum */ #define BTREE_AUTOVACUUM_FULL 1 /* Do full auto-vacuum */ #define BTREE_AUTOVACUUM_INCR 2 /* Incremental vacuum */ /* ** Forward declarations of structure */ typedef struct Btree Btree; typedef struct BtCursor BtCursor; typedef struct BtShared BtShared; typedef struct BtreeMutexArray BtreeMutexArray; /* ** This structure records all of the Btrees that need to hold ** a mutex before we enter sqlite3VdbeExec(). The Btrees are ** are placed in aBtree[] in order of aBtree[]->pBt. That way, ** we can always lock and unlock them all quickly. */ struct BtreeMutexArray { int nMutex; Btree *aBtree[SQLITE_MAX_ATTACHED+1]; }; SQLITE_PRIVATE int sqlite3BtreeOpen( const char *zFilename, /* Name of database file to open */ sqlite3 *db, /* Associated database connection */ Btree **ppBtree, /* Return open Btree* here */ int flags, /* Flags */ int vfsFlags /* Flags passed through to VFS open */ ); /* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the ** following values. ** ** NOTE: These values must match the corresponding PAGER_ values in ** pager.h. */ #define BTREE_OMIT_JOURNAL 1 /* Do not use journal. No argument */ #define BTREE_NO_READLOCK 2 /* Omit readlocks on readonly files */ #define BTREE_MEMORY 4 /* In-memory DB. No argument */ #define BTREE_READONLY 8 /* Open the database in read-only mode */ #define BTREE_READWRITE 16 /* Open for both reading and writing */ #define BTREE_CREATE 32 /* Create the database if it does not exist */ SQLITE_PRIVATE int sqlite3BtreeClose(Btree*); SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeSetSafetyLevel(Btree*,int,int); SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree*); SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix); SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*); SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree*); SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int); SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *); SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster); SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*); SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*); SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*); SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*,int); SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, int*, int flags); SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree*); SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*); SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree*); SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *pBtree); SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock); SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *, int, int); SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *); SQLITE_PRIVATE const char *sqlite3BtreeGetJournalname(Btree *); SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *, Btree *); SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *); /* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR ** of the following flags: */ #define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */ #define BTREE_ZERODATA 2 /* Table has keys only - no data */ #define BTREE_LEAFDATA 4 /* Data stored in leaves only. Implies INTKEY */ SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree*, int, int*); SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, int*); SQLITE_PRIVATE void sqlite3BtreeTripAllCursors(Btree*, int); SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue); SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); /* ** The second parameter to sqlite3BtreeGetMeta or sqlite3BtreeUpdateMeta ** should be one of the following values. The integer values are assigned ** to constants so that the offset of the corresponding field in an ** SQLite database header may be found using the following formula: ** ** offset = 36 + (idx * 4) ** ** For example, the free-page-count field is located at byte offset 36 of ** the database file header. The incr-vacuum-flag field is located at ** byte offset 64 (== 36+4*7). */ #define BTREE_FREE_PAGE_COUNT 0 #define BTREE_SCHEMA_VERSION 1 #define BTREE_FILE_FORMAT 2 #define BTREE_DEFAULT_CACHE_SIZE 3 #define BTREE_LARGEST_ROOT_PAGE 4 #define BTREE_TEXT_ENCODING 5 #define BTREE_USER_VERSION 6 #define BTREE_INCR_VACUUM 7 SQLITE_PRIVATE int sqlite3BtreeCursor( Btree*, /* BTree containing table to open */ int iTable, /* Index of root page */ int wrFlag, /* 1 for writing. 0 for read-only */ struct KeyInfo*, /* First argument to compare function */ BtCursor *pCursor /* Space to write cursor structure */ ); SQLITE_PRIVATE int sqlite3BtreeCursorSize(void); SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*); SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor*); SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( BtCursor*, UnpackedRecord *pUnKey, i64 intKey, int bias, int *pRes ); SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor*, int*); SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*); SQLITE_PRIVATE int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey, const void *pData, int nData, int nZero, int bias, int seekResult); SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor*, int *pRes); SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor*, int *pRes); SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor*, int *pRes); SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor*); SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor*, int *pRes); SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor*, i64 *pSize); SQLITE_PRIVATE int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt); SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt); SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor*, u32 *pSize); SQLITE_PRIVATE int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*); SQLITE_PRIVATE void sqlite3BtreeSetCachedRowid(BtCursor*, sqlite3_int64); SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor*); SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*); SQLITE_PRIVATE void sqlite3BtreeCacheOverflow(BtCursor *); SQLITE_PRIVATE void sqlite3BtreeClearCursor(BtCursor *); #ifndef NDEBUG SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*); #endif #ifndef SQLITE_OMIT_BTREECOUNT SQLITE_PRIVATE int sqlite3BtreeCount(BtCursor *, i64 *); #endif #ifdef SQLITE_TEST SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int); SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*); #endif /* ** If we are not using shared cache, then there is no need to ** use mutexes to access the BtShared structures. So make the ** Enter and Leave procedures no-ops. */ #ifndef SQLITE_OMIT_SHARED_CACHE SQLITE_PRIVATE void sqlite3BtreeEnter(Btree*); SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3*); #else # define sqlite3BtreeEnter(X) # define sqlite3BtreeEnterAll(X) #endif #if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE SQLITE_PRIVATE void sqlite3BtreeLeave(Btree*); SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*); SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor*); SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3*); SQLITE_PRIVATE void sqlite3BtreeMutexArrayEnter(BtreeMutexArray*); SQLITE_PRIVATE void sqlite3BtreeMutexArrayLeave(BtreeMutexArray*); SQLITE_PRIVATE void sqlite3BtreeMutexArrayInsert(BtreeMutexArray*, Btree*); #ifndef NDEBUG /* These routines are used inside assert() statements only. */ SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree*); SQLITE_PRIVATE int sqlite3BtreeHoldsAllMutexes(sqlite3*); #endif #else # define sqlite3BtreeLeave(X) # define sqlite3BtreeEnterCursor(X) # define sqlite3BtreeLeaveCursor(X) # define sqlite3BtreeLeaveAll(X) # define sqlite3BtreeMutexArrayEnter(X) # define sqlite3BtreeMutexArrayLeave(X) # define sqlite3BtreeMutexArrayInsert(X,Y) # define sqlite3BtreeHoldsMutex(X) 1 # define sqlite3BtreeHoldsAllMutexes(X) 1 #endif #endif /* _BTREE_H_ */ /************** End of btree.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /************** Include vdbe.h in the middle of sqliteInt.h ******************/ /************** Begin file vdbe.h ********************************************/ /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** Header file for the Virtual DataBase Engine (VDBE) ** ** This header defines the interface to the virtual database engine ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ /* ** A single VDBE is an opaque structure named "Vdbe". Only routines ** in the source file sqliteVdbe.c are allowed to see the insides ** of this structure. */ typedef struct Vdbe Vdbe; /* ** The names of the following types declared in vdbeInt.h are required ** for the VdbeOp definition. */ typedef struct VdbeFunc VdbeFunc; typedef struct Mem Mem; typedef struct SubProgram SubProgram; /* ** A single instruction of the virtual machine has an opcode ** and as many as three operands. The instruction is recorded ** as an instance of the following structure: */ struct VdbeOp { u8 opcode; /* What operation to perform */ signed char p4type; /* One of the P4_xxx constants for p4 */ u8 opflags; /* Mask of the OPFLG_* flags in opcodes.h */ u8 p5; /* Fifth parameter is an unsigned character */ int p1; /* First operand */ int p2; /* Second parameter (often the jump destination) */ int p3; /* The third parameter */ union { /* fourth parameter */ int i; /* Integer value if p4type==P4_INT32 */ void *p; /* Generic pointer */ char *z; /* Pointer to data for string (char array) types */ i64 *pI64; /* Used when p4type is P4_INT64 */ double *pReal; /* Used when p4type is P4_REAL */ FuncDef *pFunc; /* Used when p4type is P4_FUNCDEF */ VdbeFunc *pVdbeFunc; /* Used when p4type is P4_VDBEFUNC */ CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */ Mem *pMem; /* Used when p4type is P4_MEM */ VTable *pVtab; /* Used when p4type is P4_VTAB */ KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ int *ai; /* Used when p4type is P4_INTARRAY */ SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ } p4; #ifdef SQLITE_DEBUG char *zComment; /* Comment to improve readability */ #endif #ifdef VDBE_PROFILE int cnt; /* Number of times this instruction was executed */ u64 cycles; /* Total time spent executing this instruction */ #endif }; typedef struct VdbeOp VdbeOp; /* ** A sub-routine used to implement a trigger program. */ struct SubProgram { VdbeOp *aOp; /* Array of opcodes for sub-program */ int nOp; /* Elements in aOp[] */ int nMem; /* Number of memory cells required */ int nCsr; /* Number of cursors required */ int nRef; /* Number of pointers to this structure */ void *token; /* id that may be used to recursive triggers */ }; /* ** A smaller version of VdbeOp used for the VdbeAddOpList() function because ** it takes up less space. */ struct VdbeOpList { u8 opcode; /* What operation to perform */ signed char p1; /* First operand */ signed char p2; /* Second parameter (often the jump destination) */ signed char p3; /* Third parameter */ }; typedef struct VdbeOpList VdbeOpList; /* ** Allowed values of VdbeOp.p4type */ #define P4_NOTUSED 0 /* The P4 parameter is not used */ #define P4_DYNAMIC (-1) /* Pointer to a string obtained from sqliteMalloc() */ #define P4_STATIC (-2) /* Pointer to a static string */ #define P4_COLLSEQ (-4) /* P4 is a pointer to a CollSeq structure */ #define P4_FUNCDEF (-5) /* P4 is a pointer to a FuncDef structure */ #define P4_KEYINFO (-6) /* P4 is a pointer to a KeyInfo structure */ #define P4_VDBEFUNC (-7) /* P4 is a pointer to a VdbeFunc structure */ #define P4_MEM (-8) /* P4 is a pointer to a Mem* structure */ #define P4_TRANSIENT (-9) /* P4 is a pointer to a transient string */ #define P4_VTAB (-10) /* P4 is a pointer to an sqlite3_vtab structure */ #define P4_MPRINTF (-11) /* P4 is a string obtained from sqlite3_mprintf() */ #define P4_REAL (-12) /* P4 is a 64-bit floating point value */ #define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ #define P4_INT32 (-14) /* P4 is a 32-bit signed integer */ #define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ #define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */ /* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure ** is made. That copy is freed when the Vdbe is finalized. But if the ** argument is P4_KEYINFO_HANDOFF, the passed in pointer is used. It still ** gets freed when the Vdbe is finalized so it still should be obtained ** from a single sqliteMalloc(). But no copy is made and the calling ** function should *not* try to free the KeyInfo. */ #define P4_KEYINFO_HANDOFF (-16) #define P4_KEYINFO_STATIC (-17) /* ** The Vdbe.aColName array contains 5n Mem structures, where n is the ** number of columns of data returned by the statement. */ #define COLNAME_NAME 0 #define COLNAME_DECLTYPE 1 #define COLNAME_DATABASE 2 #define COLNAME_TABLE 3 #define COLNAME_COLUMN 4 #ifdef SQLITE_ENABLE_COLUMN_METADATA # define COLNAME_N 5 /* Number of COLNAME_xxx symbols */ #else # ifdef SQLITE_OMIT_DECLTYPE # define COLNAME_N 1 /* Store only the name */ # else # define COLNAME_N 2 /* Store the name and decltype */ # endif #endif /* ** The following macro converts a relative address in the p2 field ** of a VdbeOp structure into a negative number so that ** sqlite3VdbeAddOpList() knows that the address is relative. Calling ** the macro again restores the address. */ #define ADDR(X) (-1-(X)) /* ** The makefile scans the vdbe.c source file and creates the "opcodes.h" ** header file that defines a number for each opcode used by the VDBE. */ /************** Include opcodes.h in the middle of vdbe.h ********************/ /************** Begin file opcodes.h *****************************************/ /* Automatically generated. Do not edit */ /* See the mkopcodeh.awk script for details */ #define OP_Goto 1 #define OP_Gosub 2 #define OP_Return 3 #define OP_Yield 4 #define OP_HaltIfNull 5 #define OP_Halt 6 #define OP_Integer 7 #define OP_Int64 8 #define OP_Real 130 /* same as TK_FLOAT */ #define OP_String8 94 /* same as TK_STRING */ #define OP_String 9 #define OP_Null 10 #define OP_Blob 11 #define OP_Variable 12 #define OP_Move 13 #define OP_Copy 14 #define OP_SCopy 15 #define OP_ResultRow 16 #define OP_Concat 91 /* same as TK_CONCAT */ #define OP_Add 86 /* same as TK_PLUS */ #define OP_Subtract 87 /* same as TK_MINUS */ #define OP_Multiply 88 /* same as TK_STAR */ #define OP_Divide 89 /* same as TK_SLASH */ #define OP_Remainder 90 /* same as TK_REM */ #define OP_CollSeq 17 #define OP_Function 18 #define OP_BitAnd 82 /* same as TK_BITAND */ #define OP_BitOr 83 /* same as TK_BITOR */ #define OP_ShiftLeft 84 /* same as TK_LSHIFT */ #define OP_ShiftRight 85 /* same as TK_RSHIFT */ #define OP_AddImm 20 #define OP_MustBeInt 21 #define OP_RealAffinity 22 #define OP_ToText 141 /* same as TK_TO_TEXT */ #define OP_ToBlob 142 /* same as TK_TO_BLOB */ #define OP_ToNumeric 143 /* same as TK_TO_NUMERIC*/ #define OP_ToInt 144 /* same as TK_TO_INT */ #define OP_ToReal 145 /* same as TK_TO_REAL */ #define OP_Eq 76 /* same as TK_EQ */ #define OP_Ne 75 /* same as TK_NE */ #define OP_Lt 79 /* same as TK_LT */ #define OP_Le 78 /* same as TK_LE */ #define OP_Gt 77 /* same as TK_GT */ #define OP_Ge 80 /* same as TK_GE */ #define OP_Permutation 23 #define OP_Compare 24 #define OP_Jump 25 #define OP_And 69 /* same as TK_AND */ #define OP_Or 68 /* same as TK_OR */ #define OP_Not 19 /* same as TK_NOT */ #define OP_BitNot 93 /* same as TK_BITNOT */ #define OP_If 26 #define OP_IfNot 27 #define OP_IsNull 73 /* same as TK_ISNULL */ #define OP_NotNull 74 /* same as TK_NOTNULL */ #define OP_Column 28 #define OP_Affinity 29 #define OP_MakeRecord 30 #define OP_Count 31 #define OP_Savepoint 32 #define OP_AutoCommit 33 #define OP_Transaction 34 #define OP_ReadCookie 35 #define OP_SetCookie 36 #define OP_VerifyCookie 37 #define OP_OpenRead 38 #define OP_OpenWrite 39 #define OP_OpenEphemeral 40 #define OP_OpenPseudo 41 #define OP_Close 42 #define OP_SeekLt 43 #define OP_SeekLe 44 #define OP_SeekGe 45 #define OP_SeekGt 46 #define OP_Seek 47 #define OP_NotFound 48 #define OP_Found 49 #define OP_IsUnique 50 #define OP_NotExists 51 #define OP_Sequence 52 #define OP_NewRowid 53 #define OP_Insert 54 #define OP_InsertInt 55 #define OP_Delete 56 #define OP_ResetCount 57 #define OP_RowKey 58 #define OP_RowData 59 #define OP_Rowid 60 #define OP_NullRow 61 #define OP_Last 62 #define OP_Sort 63 #define OP_Rewind 64 #define OP_Prev 65 #define OP_Next 66 #define OP_IdxInsert 67 #define OP_IdxDelete 70 #define OP_IdxRowid 71 #define OP_IdxLT 72 #define OP_IdxGE 81 #define OP_Destroy 92 #define OP_Clear 95 #define OP_CreateIndex 96 #define OP_CreateTable 97 #define OP_ParseSchema 98 #define OP_LoadAnalysis 99 #define OP_DropTable 100 #define OP_DropIndex 101 #define OP_DropTrigger 102 #define OP_IntegrityCk 103 #define OP_RowSetAdd 104 #define OP_RowSetRead 105 #define OP_RowSetTest 106 #define OP_Program 107 #define OP_Param 108 #define OP_FkCounter 109 #define OP_FkIfZero 110 #define OP_MemMax 111 #define OP_IfPos 112 #define OP_IfNeg 113 #define OP_IfZero 114 #define OP_AggStep 115 #define OP_AggFinal 116 #define OP_Vacuum 117 #define OP_IncrVacuum 118 #define OP_Expire 119 #define OP_TableLock 120 #define OP_VBegin 121 #define OP_VCreate 122 #define OP_VDestroy 123 #define OP_VOpen 124 #define OP_VFilter 125 #define OP_VColumn 126 #define OP_VNext 127 #define OP_VRename 128 #define OP_VUpdate 129 #define OP_Pagecount 131 #define OP_Trace 132 #define OP_Noop 133 #define OP_Explain 134 /* The following opcode values are never used */ #define OP_NotUsed_135 135 #define OP_NotUsed_136 136 #define OP_NotUsed_137 137 #define OP_NotUsed_138 138 #define OP_NotUsed_139 139 #define OP_NotUsed_140 140 /* Properties such as "out2" or "jump" that are specified in ** comments following the "case" for each opcode in the vdbe.c ** are encoded into bitvectors as follows: */ #define OPFLG_JUMP 0x0001 /* jump: P2 holds jmp target */ #define OPFLG_OUT2_PRERELEASE 0x0002 /* out2-prerelease: */ #define OPFLG_IN1 0x0004 /* in1: P1 is an input */ #define OPFLG_IN2 0x0008 /* in2: P2 is an input */ #define OPFLG_IN3 0x0010 /* in3: P3 is an input */ #define OPFLG_OUT2 0x0020 /* out2: P2 is an output */ #define OPFLG_OUT3 0x0040 /* out3: P3 is an output */ #define OPFLG_INITIALIZER {\ /* 0 */ 0x00, 0x01, 0x05, 0x04, 0x04, 0x10, 0x00, 0x02,\ /* 8 */ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x24, 0x24,\ /* 16 */ 0x00, 0x00, 0x00, 0x24, 0x04, 0x05, 0x04, 0x00,\ /* 24 */ 0x00, 0x01, 0x05, 0x05, 0x00, 0x00, 0x00, 0x02,\ /* 32 */ 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00,\ /* 40 */ 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x08,\ /* 48 */ 0x11, 0x11, 0x11, 0x11, 0x02, 0x02, 0x00, 0x00,\ /* 56 */ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x01,\ /* 64 */ 0x01, 0x01, 0x01, 0x08, 0x4c, 0x4c, 0x00, 0x02,\ /* 72 */ 0x01, 0x05, 0x05, 0x15, 0x15, 0x15, 0x15, 0x15,\ /* 80 */ 0x15, 0x01, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,\ /* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x02, 0x24, 0x02, 0x00,\ /* 96 */ 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ /* 104 */ 0x0c, 0x45, 0x15, 0x01, 0x02, 0x00, 0x01, 0x08,\ /* 112 */ 0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,\ /* 120 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,\ /* 128 */ 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,\ /* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04,\ /* 144 */ 0x04, 0x04,} /************** End of opcodes.h *********************************************/ /************** Continuing where we left off in vdbe.h ***********************/ /* ** Prototypes for the VDBE interface. See comments on the implementation ** for a description of what each of these routines does. */ SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3*); SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int); SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp); SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1); SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2); SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3); SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u8 P5); SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr); SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr, int N); SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int); SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int,int); SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe*, int); SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe*); #ifdef SQLITE_DEBUG SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *, int); SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe*,FILE*); #endif SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe*); SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeSetNumCols(Vdbe*,int); SQLITE_PRIVATE int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, void(*)(void*)); SQLITE_PRIVATE void sqlite3VdbeCountChanges(Vdbe*); SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int); SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe*,Vdbe*); SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*); SQLITE_PRIVATE void sqlite3VdbeProgramDelete(sqlite3 *, SubProgram *, int); SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetValue(Vdbe*, int, u8); SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe*, int); #ifndef SQLITE_OMIT_TRACE SQLITE_PRIVATE char *sqlite3VdbeExpandSql(Vdbe*, const char*); #endif SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,char*,int); SQLITE_PRIVATE void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord*); SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); #ifndef NDEBUG SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe*, const char*, ...); # define VdbeComment(X) sqlite3VdbeComment X SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...); # define VdbeNoopComment(X) sqlite3VdbeNoopComment X #else # define VdbeComment(X) # define VdbeNoopComment(X) #endif #endif /************** End of vdbe.h ************************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /************** Include pager.h in the middle of sqliteInt.h *****************/ /************** Begin file pager.h *******************************************/ /* ** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This header file defines the interface that the sqlite page cache ** subsystem. The page cache subsystem reads and writes a file a page ** at a time and provides a journal for rollback. */ #ifndef _PAGER_H_ #define _PAGER_H_ /* ** Default maximum size for persistent journal files. A negative ** value means no limit. This value may be overridden using the ** sqlite3PagerJournalSizeLimit() API. See also "PRAGMA journal_size_limit". */ #ifndef SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT #define SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT -1 #endif /* ** The type used to represent a page number. The first page in a file ** is called page 1. 0 is used to represent "not a page". */ typedef u32 Pgno; /* ** Each open file is managed by a separate instance of the "Pager" structure. */ typedef struct Pager Pager; /* ** Handle type for pages. */ typedef struct PgHdr DbPage; /* ** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is ** reserved for working around a windows/posix incompatibility). It is ** used in the journal to signify that the remainder of the journal file ** is devoted to storing a master journal name - there are no more pages to ** roll back. See comments for function writeMasterJournal() in pager.c ** for details. */ #define PAGER_MJ_PGNO(x) ((Pgno)((PENDING_BYTE/((x)->pageSize))+1)) /* ** Allowed values for the flags parameter to sqlite3PagerOpen(). ** ** NOTE: These values must match the corresponding BTREE_ values in btree.h. */ #define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */ #define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */ /* ** Valid values for the second argument to sqlite3PagerLockingMode(). */ #define PAGER_LOCKINGMODE_QUERY -1 #define PAGER_LOCKINGMODE_NORMAL 0 #define PAGER_LOCKINGMODE_EXCLUSIVE 1 /* ** Valid values for the second argument to sqlite3PagerJournalMode(). */ #define PAGER_JOURNALMODE_QUERY -1 #define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */ #define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ #define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ #define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ /* ** The remainder of this file contains the declarations of the functions ** that make up the Pager sub-system API. See source code comments for ** a detailed description of each routine. */ /* Open and close a Pager connection. */ SQLITE_PRIVATE int sqlite3PagerOpen( sqlite3_vfs*, Pager **ppPager, const char*, int, int, int, void(*)(DbPage*) ); SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); /* Functions used to configure a Pager object. */ SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u16*, int); SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int); SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int); SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int); SQLITE_PRIVATE int sqlite3PagerJournalMode(Pager *, int); SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *, i64); SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager*); /* Functions used to obtain and release page references. */ SQLITE_PRIVATE int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag); #define sqlite3PagerGet(A,B,C) sqlite3PagerAcquire(A,B,C,0) SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno); SQLITE_PRIVATE void sqlite3PagerRef(DbPage*); SQLITE_PRIVATE void sqlite3PagerUnref(DbPage*); /* Operations on page references. */ SQLITE_PRIVATE int sqlite3PagerWrite(DbPage*); SQLITE_PRIVATE void sqlite3PagerDontWrite(DbPage*); SQLITE_PRIVATE int sqlite3PagerMovepage(Pager*,DbPage*,Pgno,int); SQLITE_PRIVATE int sqlite3PagerPageRefcount(DbPage*); SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *); SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *); /* Functions used to manage pager transactions and savepoints. */ SQLITE_PRIVATE int sqlite3PagerPagecount(Pager*, int*); SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int); SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int); SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*); SQLITE_PRIVATE int sqlite3PagerRollback(Pager*); SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n); SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager); /* Functions used to query pager state and configuration. */ SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*); SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*); SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*); SQLITE_PRIVATE const sqlite3_vfs *sqlite3PagerVfs(Pager*); SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*); SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*); SQLITE_PRIVATE int sqlite3PagerNosync(Pager*); SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*); SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*); /* Functions used to truncate the database file. */ SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno); /* Functions to support testing and debugging. */ #if !defined(NDEBUG) || defined(SQLITE_TEST) SQLITE_PRIVATE Pgno sqlite3PagerPagenumber(DbPage*); SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage*); #endif #ifdef SQLITE_TEST SQLITE_PRIVATE int *sqlite3PagerStats(Pager*); SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*); void disable_simulated_io_errors(void); void enable_simulated_io_errors(void); #else # define disable_simulated_io_errors() # define enable_simulated_io_errors() #endif #endif /* _PAGER_H_ */ /************** End of pager.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /************** Include pcache.h in the middle of sqliteInt.h ****************/ /************** Begin file pcache.h ******************************************/ /* ** 2008 August 05 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This header file defines the interface that the sqlite page cache ** subsystem. */ #ifndef _PCACHE_H_ typedef struct PgHdr PgHdr; typedef struct PCache PCache; /* ** Every page in the cache is controlled by an instance of the following ** structure. */ struct PgHdr { void *pData; /* Content of this page */ void *pExtra; /* Extra content */ PgHdr *pDirty; /* Transient list of dirty pages */ Pgno pgno; /* Page number for this page */ Pager *pPager; /* The pager this page is part of */ #ifdef SQLITE_CHECK_PAGES u32 pageHash; /* Hash of page content */ #endif u16 flags; /* PGHDR flags defined below */ /********************************************************************** ** Elements above are public. All that follows is private to pcache.c ** and should not be accessed by other modules. */ i16 nRef; /* Number of users of this page */ PCache *pCache; /* Cache that owns this page */ PgHdr *pDirtyNext; /* Next element in list of dirty pages */ PgHdr *pDirtyPrev; /* Previous element in list of dirty pages */ }; /* Bit values for PgHdr.flags */ #define PGHDR_DIRTY 0x002 /* Page has changed */ #define PGHDR_NEED_SYNC 0x004 /* Fsync the rollback journal before ** writing this page to the database */ #define PGHDR_NEED_READ 0x008 /* Content is unread */ #define PGHDR_REUSE_UNLIKELY 0x010 /* A hint that reuse is unlikely */ #define PGHDR_DONT_WRITE 0x020 /* Do not write content to disk */ /* Initialize and shutdown the page cache subsystem */ SQLITE_PRIVATE int sqlite3PcacheInitialize(void); SQLITE_PRIVATE void sqlite3PcacheShutdown(void); /* Page cache buffer management: ** These routines implement SQLITE_CONFIG_PAGECACHE. */ SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *, int sz, int n); /* Create a new pager cache. ** Under memory stress, invoke xStress to try to make pages clean. ** Only clean and unpinned pages can be reclaimed. */ SQLITE_PRIVATE void sqlite3PcacheOpen( int szPage, /* Size of every page */ int szExtra, /* Extra space associated with each page */ int bPurgeable, /* True if pages are on backing store */ int (*xStress)(void*, PgHdr*), /* Call to try to make pages clean */ void *pStress, /* Argument to xStress */ PCache *pToInit /* Preallocated space for the PCache */ ); /* Modify the page-size after the cache has been created. */ SQLITE_PRIVATE void sqlite3PcacheSetPageSize(PCache *, int); /* Return the size in bytes of a PCache object. Used to preallocate ** storage space. */ SQLITE_PRIVATE int sqlite3PcacheSize(void); /* One release per successful fetch. Page is pinned until released. ** Reference counted. */ SQLITE_PRIVATE int sqlite3PcacheFetch(PCache*, Pgno, int createFlag, PgHdr**); SQLITE_PRIVATE void sqlite3PcacheRelease(PgHdr*); SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr*); /* Remove page from cache */ SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr*); /* Make sure page is marked dirty */ SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr*); /* Mark a single page as clean */ SQLITE_PRIVATE void sqlite3PcacheCleanAll(PCache*); /* Mark all dirty list pages as clean */ /* Change a page number. Used by incr-vacuum. */ SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr*, Pgno); /* Remove all pages with pgno>x. Reset the cache if x==0 */ SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache*, Pgno x); /* Get a list of all dirty pages in the cache, sorted by page number */ SQLITE_PRIVATE PgHdr *sqlite3PcacheDirtyList(PCache*); /* Reset and close the cache object */ SQLITE_PRIVATE void sqlite3PcacheClose(PCache*); /* Clear flags from pages of the page cache */ SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *); /* Discard the contents of the cache */ SQLITE_PRIVATE void sqlite3PcacheClear(PCache*); /* Return the total number of outstanding page references */ SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache*); /* Increment the reference count of an existing page */ SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr*); SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr*); /* Return the total number of pages stored in the cache */ SQLITE_PRIVATE int sqlite3PcachePagecount(PCache*); #if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) /* Iterate through all dirty pages currently stored in the cache. This ** interface is only available if SQLITE_CHECK_PAGES is defined when the ** library is built. */ SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)); #endif /* Set and get the suggested cache-size for the specified pager-cache. ** ** If no global maximum is configured, then the system attempts to limit ** the total number of pages cached by purgeable pager-caches to the sum ** of the suggested cache-sizes. */ SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *, int); #ifdef SQLITE_TEST SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *); #endif #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT /* Try to return memory used by the pcache module to the main memory heap */ SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int); #endif #ifdef SQLITE_TEST SQLITE_PRIVATE void sqlite3PcacheStats(int*,int*,int*,int*); #endif SQLITE_PRIVATE void sqlite3PCacheSetDefault(void); #endif /* _PCACHE_H_ */ /************** End of pcache.h **********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /************** Include os.h in the middle of sqliteInt.h ********************/ /************** Begin file os.h **********************************************/ /* ** 2001 September 16 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ****************************************************************************** ** ** This header file (together with is companion C source-code file ** "os.c") attempt to abstract the underlying operating system so that ** the SQLite library will work on both POSIX and windows systems. ** ** This header file is #include-ed by sqliteInt.h and thus ends up ** being included by every source file. */ #ifndef _SQLITE_OS_H_ #define _SQLITE_OS_H_ /* ** Figure out if we are dealing with Unix, Windows, or some other ** operating system. After the following block of preprocess macros, ** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, SQLITE_OS_OS2, and SQLITE_OS_OTHER ** will defined to either 1 or 0. One of the four will be 1. The other ** three will be 0. */ #if defined(SQLITE_OS_OTHER) # if SQLITE_OS_OTHER==1 # undef SQLITE_OS_UNIX # define SQLITE_OS_UNIX 0 # undef SQLITE_OS_WIN # define SQLITE_OS_WIN 0 # undef SQLITE_OS_OS2 # define SQLITE_OS_OS2 0 # else # undef SQLITE_OS_OTHER # endif #endif #if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER) # define SQLITE_OS_OTHER 0 # ifndef SQLITE_OS_WIN # if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__) # define SQLITE_OS_WIN 1 # define SQLITE_OS_UNIX 0 # define SQLITE_OS_OS2 0 # elif defined(__EMX__) || defined(_OS2) || defined(OS2) || defined(_OS2_) || defined(__OS2__) # define SQLITE_OS_WIN 0 # define SQLITE_OS_UNIX 0 # define SQLITE_OS_OS2 1 # else # define SQLITE_OS_WIN 0 # define SQLITE_OS_UNIX 1 # define SQLITE_OS_OS2 0 # endif # else # define SQLITE_OS_UNIX 0 # define SQLITE_OS_OS2 0 # endif #else # ifndef SQLITE_OS_WIN # define SQLITE_OS_WIN 0 # endif #endif /* ** Determine if we are dealing with WindowsCE - which has a much ** reduced API. */ #if defined(_WIN32_WCE) # define SQLITE_OS_WINCE 1 #else # define SQLITE_OS_WINCE 0 #endif /* ** Define the maximum size of a temporary filename */ #if SQLITE_OS_WIN # include # define SQLITE_TEMPNAME_SIZE (MAX_PATH+50) #elif SQLITE_OS_OS2 # if (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 3) && defined(OS2_HIGH_MEMORY) # include /* has to be included before os2.h for linking to work */ # endif # define INCL_DOSDATETIME # define INCL_DOSFILEMGR # define INCL_DOSERRORS # define INCL_DOSMISC # define INCL_DOSPROCESS # define INCL_DOSMODULEMGR # define INCL_DOSSEMAPHORES # include # include # define SQLITE_TEMPNAME_SIZE (CCHMAXPATHCOMP) #else # define SQLITE_TEMPNAME_SIZE 200 #endif /* If the SET_FULLSYNC macro is not defined above, then make it ** a no-op */ #ifndef SET_FULLSYNC # define SET_FULLSYNC(x,y) #endif /* ** The default size of a disk sector */ #ifndef SQLITE_DEFAULT_SECTOR_SIZE # define SQLITE_DEFAULT_SECTOR_SIZE 512 #endif /* ** Temporary files are named starting with this prefix followed by 16 random ** alphanumeric characters, and no file extension. They are stored in the ** OS's standard temporary file directory, and are deleted prior to exit. ** If sqlite is being embedded in another program, you may wish to change the ** prefix to reflect your program's name, so that if your program exits ** prematurely, old temporary files can be easily identified. This can be done ** using -DSQLITE_TEMP_FILE_PREFIX=myprefix_ on the compiler command line. ** ** 2006-10-31: The default prefix used to be "sqlite_". But then ** Mcafee started using SQLite in their anti-virus product and it ** started putting files with the "sqlite" name in the c:/temp folder. ** This annoyed many windows users. Those users would then do a ** Google search for "sqlite", find the telephone numbers of the ** developers and call to wake them up at night and complain. ** For this reason, the default name prefix is changed to be "sqlite" ** spelled backwards. So the temp files are still identified, but ** anybody smart enough to figure out the code is also likely smart ** enough to know that calling the developer will not help get rid ** of the file. */ #ifndef SQLITE_TEMP_FILE_PREFIX # define SQLITE_TEMP_FILE_PREFIX "etilqs_" #endif /* ** The following values may be passed as the second argument to ** sqlite3OsLock(). The various locks exhibit the following semantics: ** ** SHARED: Any number of processes may hold a SHARED lock simultaneously. ** RESERVED: A single process may hold a RESERVED lock on a file at ** any time. Other processes may hold and obtain new SHARED locks. ** PENDING: A single process may hold a PENDING lock on a file at ** any one time. Existing SHARED locks may persist, but no new ** SHARED locks may be obtained by other processes. ** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks. ** ** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a ** process that requests an EXCLUSIVE lock may actually obtain a PENDING ** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to ** sqlite3OsLock(). */ #define NO_LOCK 0 #define SHARED_LOCK 1 #define RESERVED_LOCK 2 #define PENDING_LOCK 3 #define EXCLUSIVE_LOCK 4 /* ** File Locking Notes: (Mostly about windows but also some info for Unix) ** ** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because ** those functions are not available. So we use only LockFile() and ** UnlockFile(). ** ** LockFile() prevents not just writing but also reading by other processes. ** A SHARED_LOCK is obtained by locking a single randomly-chosen ** byte out of a specific range of bytes. The lock byte is obtained at ** random so two separate readers can probably access the file at the ** same time, unless they are unlucky and choose the same lock byte. ** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range. ** There can only be one writer. A RESERVED_LOCK is obtained by locking ** a single byte of the file that is designated as the reserved lock byte. ** A PENDING_LOCK is obtained by locking a designated byte different from ** the RESERVED_LOCK byte. ** ** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available, ** which means we can use reader/writer locks. When reader/writer locks ** are used, the lock is placed on the same range of bytes that is used ** for probabilistic locking in Win95/98/ME. Hence, the locking scheme ** will support two or more Win95 readers or two or more WinNT readers. ** But a single Win95 reader will lock out all WinNT readers and a single ** WinNT reader will lock out all other Win95 readers. ** ** The following #defines specify the range of bytes used for locking. ** SHARED_SIZE is the number of bytes available in the pool from which ** a random byte is selected for a shared lock. The pool of bytes for ** shared locks begins at SHARED_FIRST. ** ** The same locking strategy and ** byte ranges are used for Unix. This leaves open the possiblity of having ** clients on win95, winNT, and unix all talking to the same shared file ** and all locking correctly. To do so would require that samba (or whatever ** tool is being used for file sharing) implements locks correctly between ** windows and unix. I'm guessing that isn't likely to happen, but by ** using the same locking range we are at least open to the possibility. ** ** Locking in windows is manditory. For this reason, we cannot store ** actual data in the bytes used for locking. The pager never allocates ** the pages involved in locking therefore. SHARED_SIZE is selected so ** that all locks will fit on a single page even at the minimum page size. ** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE ** is set high so that we don't have to allocate an unused page except ** for very large databases. But one should test the page skipping logic ** by setting PENDING_BYTE low and running the entire regression suite. ** ** Changing the value of PENDING_BYTE results in a subtly incompatible ** file format. Depending on how it is changed, you might not notice ** the incompatibility right away, even running a full regression test. ** The default location of PENDING_BYTE is the first byte past the ** 1GB boundary. ** */ #define PENDING_BYTE sqlite3PendingByte #define RESERVED_BYTE (PENDING_BYTE+1) #define SHARED_FIRST (PENDING_BYTE+2) #define SHARED_SIZE 510 /* ** Wrapper around OS specific sqlite3_os_init() function. */ SQLITE_PRIVATE int sqlite3OsInit(void); /* ** Functions for accessing sqlite3_file methods */ SQLITE_PRIVATE int sqlite3OsClose(sqlite3_file*); SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset); SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset); SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file*, i64 size); SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file*, int); SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file*, i64 *pSize); SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int); SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int); SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut); SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*); #define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id); SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id); /* ** Functions for accessing sqlite3_vfs methods */ SQLITE_PRIVATE int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *); SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *, const char *, int); SQLITE_PRIVATE int sqlite3OsAccess(sqlite3_vfs *, const char *, int, int *pResOut); SQLITE_PRIVATE int sqlite3OsFullPathname(sqlite3_vfs *, const char *, int, char *); #ifndef SQLITE_OMIT_LOAD_EXTENSION SQLITE_PRIVATE void *sqlite3OsDlOpen(sqlite3_vfs *, const char *); SQLITE_PRIVATE void sqlite3OsDlError(sqlite3_vfs *, int, char *); SQLITE_PRIVATE void (*sqlite3OsDlSym(sqlite3_vfs *, void *, const char *))(void); SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *); #endif /* SQLITE_OMIT_LOAD_EXTENSION */ SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *); SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int); SQLITE_PRIVATE int sqlite3OsCurrentTime(sqlite3_vfs *, double*); /* ** Convenience functions for opening and closing files using ** sqlite3_malloc() to obtain space for the file-handle structure. */ SQLITE_PRIVATE int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*); SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *); #endif /* _SQLITE_OS_H_ */ /************** End of os.h **************************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /************** Include mutex.h in the middle of sqliteInt.h *****************/ /************** Begin file mutex.h *******************************************/ /* ** 2007 August 28 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This file contains the common header for all mutex implementations. ** The sqliteInt.h header #includes this file so that it is available ** to all source files. We break it out in an effort to keep the code ** better organized. ** ** NOTE: source files should *not* #include this header file directly. ** Source files should #include the sqliteInt.h file and let that file ** include this one indirectly. */ /* ** Figure out what version of the code to use. The choices are ** ** SQLITE_MUTEX_OMIT No mutex logic. Not even stubs. The ** mutexes implemention cannot be overridden ** at start-time. ** ** SQLITE_MUTEX_NOOP For single-threaded applications. No ** mutual exclusion is provided. But this ** implementation can be overridden at ** start-time. ** ** SQLITE_MUTEX_PTHREADS For multi-threaded applications on Unix. ** ** SQLITE_MUTEX_W32 For multi-threaded applications on Win32. ** ** SQLITE_MUTEX_OS2 For multi-threaded applications on OS/2. */ #if !SQLITE_THREADSAFE # define SQLITE_MUTEX_OMIT #endif #if SQLITE_THREADSAFE && !defined(SQLITE_MUTEX_NOOP) # if SQLITE_OS_UNIX # define SQLITE_MUTEX_PTHREADS # elif SQLITE_OS_WIN # define SQLITE_MUTEX_W32 # elif SQLITE_OS_OS2 # define SQLITE_MUTEX_OS2 # else # define SQLITE_MUTEX_NOOP # endif #endif #ifdef SQLITE_MUTEX_OMIT /* ** If this is a no-op implementation, implement everything as macros. */ #define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8) #define sqlite3_mutex_free(X) #define sqlite3_mutex_enter(X) #define sqlite3_mutex_try(X) SQLITE_OK #define sqlite3_mutex_leave(X) #define sqlite3_mutex_held(X) 1 #define sqlite3_mutex_notheld(X) 1 #define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8) #define sqlite3MutexInit() SQLITE_OK #define sqlite3MutexEnd() #endif /* defined(SQLITE_MUTEX_OMIT) */ /************** End of mutex.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /* ** Each database file to be accessed by the system is an instance ** of the following structure. There are normally two of these structures ** in the sqlite.aDb[] array. aDb[0] is the main database file and ** aDb[1] is the database file used to hold temporary tables. Additional ** databases may be attached. */ struct Db { char *zName; /* Name of this database */ Btree *pBt; /* The B*Tree structure for this database file */ u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */ u8 safety_level; /* How aggressive at syncing data to disk */ Schema *pSchema; /* Pointer to database schema (possibly shared) */ }; /* ** An instance of the following structure stores a database schema. ** ** If there are no virtual tables configured in this schema, the ** Schema.db variable is set to NULL. After the first virtual table ** has been added, it is set to point to the database connection ** used to create the connection. Once a virtual table has been ** added to the Schema structure and the Schema.db variable populated, ** only that database connection may use the Schema to prepare ** statements. */ struct Schema { int schema_cookie; /* Database schema version number for this file */ Hash tblHash; /* All tables indexed by name */ Hash idxHash; /* All (named) indices indexed by name */ Hash trigHash; /* All triggers indexed by name */ Hash fkeyHash; /* All foreign keys by referenced table name */ Table *pSeqTab; /* The sqlite_sequence table used by AUTOINCREMENT */ u8 file_format; /* Schema format version for this file */ u8 enc; /* Text encoding used by this database */ u16 flags; /* Flags associated with this schema */ int cache_size; /* Number of pages to use in the cache */ #ifndef SQLITE_OMIT_VIRTUALTABLE sqlite3 *db; /* "Owner" connection. See comment above */ #endif }; /* ** These macros can be used to test, set, or clear bits in the ** Db.pSchema->flags field. */ #define DbHasProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))==(P)) #define DbHasAnyProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))!=0) #define DbSetProperty(D,I,P) (D)->aDb[I].pSchema->flags|=(P) #define DbClearProperty(D,I,P) (D)->aDb[I].pSchema->flags&=~(P) /* ** Allowed values for the DB.pSchema->flags field. ** ** The DB_SchemaLoaded flag is set after the database schema has been ** read into internal hash tables. ** ** DB_UnresetViews means that one or more views have column names that ** have been filled out. If the schema changes, these column names might ** changes and so the view will need to be reset. */ #define DB_SchemaLoaded 0x0001 /* The schema has been loaded */ #define DB_UnresetViews 0x0002 /* Some views have defined column names */ #define DB_Empty 0x0004 /* The file is empty (length 0 bytes) */ /* ** The number of different kinds of things that can be limited ** using the sqlite3_limit() interface. */ #define SQLITE_N_LIMIT (SQLITE_LIMIT_TRIGGER_DEPTH+1) /* ** Lookaside malloc is a set of fixed-size buffers that can be used ** to satisfy small transient memory allocation requests for objects ** associated with a particular database connection. The use of ** lookaside malloc provides a significant performance enhancement ** (approx 10%) by avoiding numerous malloc/free requests while parsing ** SQL statements. ** ** The Lookaside structure holds configuration information about the ** lookaside malloc subsystem. Each available memory allocation in ** the lookaside subsystem is stored on a linked list of LookasideSlot ** objects. ** ** Lookaside allocations are only allowed for objects that are associated ** with a particular database connection. Hence, schema information cannot ** be stored in lookaside because in shared cache mode the schema information ** is shared by multiple database connections. Therefore, while parsing ** schema information, the Lookaside.bEnabled flag is cleared so that ** lookaside allocations are not used to construct the schema objects. */ struct Lookaside { u16 sz; /* Size of each buffer in bytes */ u8 bEnabled; /* False to disable new lookaside allocations */ u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */ int nOut; /* Number of buffers currently checked out */ int mxOut; /* Highwater mark for nOut */ LookasideSlot *pFree; /* List of available buffers */ void *pStart; /* First byte of available memory space */ void *pEnd; /* First byte past end of available space */ }; struct LookasideSlot { LookasideSlot *pNext; /* Next buffer in the list of free buffers */ }; /* ** A hash table for function definitions. ** ** Hash each FuncDef structure into one of the FuncDefHash.a[] slots. ** Collisions are on the FuncDef.pHash chain. */ struct FuncDefHash { FuncDef *a[23]; /* Hash table for functions */ }; /* ** Each database connection is an instance of the following structure. ** ** The sqlite.lastRowid records the last insert rowid generated by an ** insert statement. Inserts on views do not affect its value. Each ** trigger has its own context, so that lastRowid can be updated inside ** triggers as usual. The previous value will be restored once the trigger ** exits. Upon entering a before or instead of trigger, lastRowid is no ** longer (since after version 2.8.12) reset to -1. ** ** The sqlite.nChange does not count changes within triggers and keeps no ** context. It is reset at start of sqlite3_exec. ** The sqlite.lsChange represents the number of changes made by the last ** insert, update, or delete statement. It remains constant throughout the ** length of a statement and is then updated by OP_SetCounts. It keeps a ** context stack just like lastRowid so that the count of changes ** within a trigger is not seen outside the trigger. Changes to views do not ** affect the value of lsChange. ** The sqlite.csChange keeps track of the number of current changes (since ** the last statement) and is used to update sqlite_lsChange. ** ** The member variables sqlite.errCode, sqlite.zErrMsg and sqlite.zErrMsg16 ** store the most recent error code and, if applicable, string. The ** internal function sqlite3Error() is used to set these variables ** consistently. */ struct sqlite3 { sqlite3_vfs *pVfs; /* OS Interface */ int nDb; /* Number of backends currently in use */ Db *aDb; /* All backends */ int flags; /* Miscellaneous flags. See below */ int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ int errCode; /* Most recent error code (SQLITE_*) */ int errMask; /* & result codes with this before returning */ u8 autoCommit; /* The auto-commit flag. */ u8 temp_store; /* 1: file 2: memory 0: default */ u8 mallocFailed; /* True if we have seen a malloc failure */ u8 dfltLockMode; /* Default locking-mode for attached dbs */ u8 dfltJournalMode; /* Default journal mode for attached dbs */ signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ u8 suppressErr; /* Do not issue error messages if true */ int nextPagesize; /* Pagesize after VACUUM if >0 */ int nTable; /* Number of tables in the database */ CollSeq *pDfltColl; /* The default collating sequence (BINARY) */ i64 lastRowid; /* ROWID of most recent insert (see above) */ u32 magic; /* Magic number for detect library misuse */ int nChange; /* Value returned by sqlite3_changes() */ int nTotalChange; /* Value returned by sqlite3_total_changes() */ sqlite3_mutex *mutex; /* Connection mutex */ int aLimit[SQLITE_N_LIMIT]; /* Limits */ struct sqlite3InitInfo { /* Information used during initialization */ int iDb; /* When back is being initialized */ int newTnum; /* Rootpage of table being initialized */ u8 busy; /* TRUE if currently initializing */ u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ } init; int nExtension; /* Number of loaded extensions */ void **aExtension; /* Array of shared library handles */ struct Vdbe *pVdbe; /* List of active virtual machines */ int activeVdbeCnt; /* Number of VDBEs currently executing */ int writeVdbeCnt; /* Number of active VDBEs that are writing */ void (*xTrace)(void*,const char*); /* Trace function */ void *pTraceArg; /* Argument to the trace function */ void (*xProfile)(void*,const char*,u64); /* Profiling function */ void *pProfileArg; /* Argument to profile function */ void *pCommitArg; /* Argument to xCommitCallback() */ int (*xCommitCallback)(void*); /* Invoked at every commit. */ void *pRollbackArg; /* Argument to xRollbackCallback() */ void (*xRollbackCallback)(void*); /* Invoked at every commit. */ void *pUpdateArg; void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64); void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*); void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*); void *pCollNeededArg; sqlite3_value *pErr; /* Most recent error message */ char *zErrMsg; /* Most recent error message (UTF-8 encoded) */ char *zErrMsg16; /* Most recent error message (UTF-16 encoded) */ union { volatile int isInterrupted; /* True if sqlite3_interrupt has been called */ double notUsed1; /* Spacer */ } u1; Lookaside lookaside; /* Lookaside malloc configuration */ #ifndef SQLITE_OMIT_AUTHORIZATION int (*xAuth)(void*,int,const char*,const char*,const char*,const char*); /* Access authorization function */ void *pAuthArg; /* 1st argument to the access auth function */ #endif #ifndef SQLITE_OMIT_PROGRESS_CALLBACK int (*xProgress)(void *); /* The progress callback */ void *pProgressArg; /* Argument to the progress callback */ int nProgressOps; /* Number of opcodes for progress callback */ #endif #ifndef SQLITE_OMIT_VIRTUALTABLE Hash aModule; /* populated by sqlite3_create_module() */ Table *pVTab; /* vtab with active Connect/Create method */ VTable **aVTrans; /* Virtual tables with open transactions */ int nVTrans; /* Allocated size of aVTrans */ VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */ #endif FuncDefHash aFunc; /* Hash table of connection functions */ Hash aCollSeq; /* All collating sequences */ BusyHandler busyHandler; /* Busy callback */ int busyTimeout; /* Busy handler timeout, in msec */ Db aDbStatic[2]; /* Static space for the 2 default backends */ Savepoint *pSavepoint; /* List of active savepoints */ int nSavepoint; /* Number of non-transaction savepoints */ int nStatement; /* Number of nested statement-transactions */ u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */ i64 nDeferredCons; /* Net deferred constraints this transaction. */ #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY /* The following variables are all protected by the STATIC_MASTER ** mutex, not by sqlite3.mutex. They are used by code in notify.c. ** ** When X.pUnlockConnection==Y, that means that X is waiting for Y to ** unlock so that it can proceed. ** ** When X.pBlockingConnection==Y, that means that something that X tried ** tried to do recently failed with an SQLITE_LOCKED error due to locks ** held by Y. */ sqlite3 *pBlockingConnection; /* Connection that caused SQLITE_LOCKED */ sqlite3 *pUnlockConnection; /* Connection to watch for unlock */ void *pUnlockArg; /* Argument to xUnlockNotify */ void (*xUnlockNotify)(void **, int); /* Unlock notify callback */ sqlite3 *pNextBlocked; /* Next in list of all blocked connections */ #endif }; /* ** A macro to discover the encoding of a database. */ #define ENC(db) ((db)->aDb[0].pSchema->enc) /* ** Possible values for the sqlite3.flags. */ #define SQLITE_VdbeTrace 0x00000100 /* True to trace VDBE execution */ #define SQLITE_InternChanges 0x00000200 /* Uncommitted Hash table changes */ #define SQLITE_FullColNames 0x00000400 /* Show full column names on SELECT */ #define SQLITE_ShortColNames 0x00000800 /* Show short columns names */ #define SQLITE_CountRows 0x00001000 /* Count rows changed by INSERT, */ /* DELETE, or UPDATE and return */ /* the count using a callback. */ #define SQLITE_NullCallback 0x00002000 /* Invoke the callback once if the */ /* result set is empty */ #define SQLITE_SqlTrace 0x00004000 /* Debug print SQL as it executes */ #define SQLITE_VdbeListing 0x00008000 /* Debug listings of VDBE programs */ #define SQLITE_WriteSchema 0x00010000 /* OK to update SQLITE_MASTER */ #define SQLITE_NoReadlock 0x00020000 /* Readlocks are omitted when ** accessing read-only databases */ #define SQLITE_IgnoreChecks 0x00040000 /* Do not enforce check constraints */ #define SQLITE_ReadUncommitted 0x0080000 /* For shared-cache mode */ #define SQLITE_LegacyFileFmt 0x00100000 /* Create new databases in format 1 */ #define SQLITE_FullFSync 0x00200000 /* Use full fsync on the backend */ #define SQLITE_LoadExtension 0x00400000 /* Enable load_extension */ #define SQLITE_RecoveryMode 0x00800000 /* Ignore schema errors */ #define SQLITE_ReverseOrder 0x01000000 /* Reverse unordered SELECTs */ #define SQLITE_RecTriggers 0x02000000 /* Enable recursive triggers */ #define SQLITE_ForeignKeys 0x04000000 /* Enforce foreign key constraints */ /* ** Bits of the sqlite3.flags field that are used by the ** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface. ** These must be the low-order bits of the flags field. */ #define SQLITE_QueryFlattener 0x01 /* Disable query flattening */ #define SQLITE_ColumnCache 0x02 /* Disable the column cache */ #define SQLITE_IndexSort 0x04 /* Disable indexes for sorting */ #define SQLITE_IndexSearch 0x08 /* Disable indexes for searching */ #define SQLITE_IndexCover 0x10 /* Disable index covering table */ #define SQLITE_OptMask 0x1f /* Mask of all disablable opts */ /* ** Possible values for the sqlite.magic field. ** The numbers are obtained at random and have no special meaning, other ** than being distinct from one another. */ #define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */ #define SQLITE_MAGIC_CLOSED 0x9f3c2d33 /* Database is closed */ #define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */ #define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */ #define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */ /* ** Each SQL function is defined by an instance of the following ** structure. A pointer to this structure is stored in the sqlite.aFunc ** hash table. When multiple functions have the same name, the hash table ** points to a linked list of these structures. */ struct FuncDef { i16 nArg; /* Number of arguments. -1 means unlimited */ u8 iPrefEnc; /* Preferred text encoding (SQLITE_UTF8, 16LE, 16BE) */ u8 flags; /* Some combination of SQLITE_FUNC_* */ void *pUserData; /* User data parameter */ FuncDef *pNext; /* Next function with same name */ void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */ void (*xStep)(sqlite3_context*,int,sqlite3_value**); /* Aggregate step */ void (*xFinalize)(sqlite3_context*); /* Aggregate finalizer */ char *zName; /* SQL name of the function. */ FuncDef *pHash; /* Next with a different name but the same hash */ }; /* ** Possible values for FuncDef.flags */ #define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */ #define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */ #define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */ #define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */ #define SQLITE_FUNC_PRIVATE 0x10 /* Allowed for internal use only */ #define SQLITE_FUNC_COUNT 0x20 /* Built-in count(*) aggregate */ #define SQLITE_FUNC_COALESCE 0x40 /* Built-in coalesce() or ifnull() function */ /* ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are ** used to create the initializers for the FuncDef structures. ** ** FUNCTION(zName, nArg, iArg, bNC, xFunc) ** Used to create a scalar function definition of a function zName ** implemented by C function xFunc that accepts nArg arguments. The ** value passed as iArg is cast to a (void*) and made available ** as the user-data (sqlite3_user_data()) for the function. If ** argument bNC is true, then the SQLITE_FUNC_NEEDCOLL flag is set. ** ** AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal) ** Used to create an aggregate function definition implemented by ** the C functions xStep and xFinal. The first four parameters ** are interpreted in the same way as the first 4 parameters to ** FUNCTION(). ** ** LIKEFUNC(zName, nArg, pArg, flags) ** Used to create a scalar function definition of a function zName ** that accepts nArg arguments and is implemented by a call to C ** function likeFunc. Argument pArg is cast to a (void *) and made ** available as the function user-data (sqlite3_user_data()). The ** FuncDef.flags variable is set to the value passed as the flags ** parameter. */ #define FUNCTION(zName, nArg, iArg, bNC, xFunc) \ {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0} #define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \ {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \ pArg, 0, xFunc, 0, 0, #zName, 0} #define LIKEFUNC(zName, nArg, arg, flags) \ {nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0} #define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \ {nArg, SQLITE_UTF8, nc*SQLITE_FUNC_NEEDCOLL, \ SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0} /* ** All current savepoints are stored in a linked list starting at ** sqlite3.pSavepoint. The first element in the list is the most recently ** opened savepoint. Savepoints are added to the list by the vdbe ** OP_Savepoint instruction. */ struct Savepoint { char *zName; /* Savepoint name (nul-terminated) */ i64 nDeferredCons; /* Number of deferred fk violations */ Savepoint *pNext; /* Parent savepoint (if any) */ }; /* ** The following are used as the second parameter to sqlite3Savepoint(), ** and as the P1 argument to the OP_Savepoint instruction. */ #define SAVEPOINT_BEGIN 0 #define SAVEPOINT_RELEASE 1 #define SAVEPOINT_ROLLBACK 2 /* ** Each SQLite module (virtual table definition) is defined by an ** instance of the following structure, stored in the sqlite3.aModule ** hash table. */ struct Module { const sqlite3_module *pModule; /* Callback pointers */ const char *zName; /* Name passed to create_module() */ void *pAux; /* pAux passed to create_module() */ void (*xDestroy)(void *); /* Module destructor function */ }; /* ** information about each column of an SQL table is held in an instance ** of this structure. */ struct Column { char *zName; /* Name of this column */ Expr *pDflt; /* Default value of this column */ char *zDflt; /* Original text of the default value */ char *zType; /* Data type for this column */ char *zColl; /* Collating sequence. If NULL, use the default */ u8 notNull; /* True if there is a NOT NULL constraint */ u8 isPrimKey; /* True if this column is part of the PRIMARY KEY */ char affinity; /* One of the SQLITE_AFF_... values */ #ifndef SQLITE_OMIT_VIRTUALTABLE u8 isHidden; /* True if this column is 'hidden' */ #endif }; /* ** A "Collating Sequence" is defined by an instance of the following ** structure. Conceptually, a collating sequence consists of a name and ** a comparison routine that defines the order of that sequence. ** ** There may two separate implementations of the collation function, one ** that processes text in UTF-8 encoding (CollSeq.xCmp) and another that ** processes text encoded in UTF-16 (CollSeq.xCmp16), using the machine ** native byte order. When a collation sequence is invoked, SQLite selects ** the version that will require the least expensive encoding ** translations, if any. ** ** The CollSeq.pUser member variable is an extra parameter that passed in ** as the first argument to the UTF-8 comparison function, xCmp. ** CollSeq.pUser16 is the equivalent for the UTF-16 comparison function, ** xCmp16. ** ** If both CollSeq.xCmp and CollSeq.xCmp16 are NULL, it means that the ** collating sequence is undefined. Indices built on an undefined ** collating sequence may not be read or written. */ struct CollSeq { char *zName; /* Name of the collating sequence, UTF-8 encoded */ u8 enc; /* Text encoding handled by xCmp() */ u8 type; /* One of the SQLITE_COLL_... values below */ void *pUser; /* First argument to xCmp() */ int (*xCmp)(void*,int, const void*, int, const void*); void (*xDel)(void*); /* Destructor for pUser */ }; /* ** Allowed values of CollSeq.type: */ #define SQLITE_COLL_BINARY 1 /* The default memcmp() collating sequence */ #define SQLITE_COLL_NOCASE 2 /* The built-in NOCASE collating sequence */ #define SQLITE_COLL_REVERSE 3 /* The built-in REVERSE collating sequence */ #define SQLITE_COLL_USER 0 /* Any other user-defined collating sequence */ /* ** A sort order can be either ASC or DESC. */ #define SQLITE_SO_ASC 0 /* Sort in ascending order */ #define SQLITE_SO_DESC 1 /* Sort in ascending order */ /* ** Column affinity types. ** ** These used to have mnemonic name like 'i' for SQLITE_AFF_INTEGER and ** 't' for SQLITE_AFF_TEXT. But we can save a little space and improve ** the speed a little by numbering the values consecutively. ** ** But rather than start with 0 or 1, we begin with 'a'. That way, ** when multiple affinity types are concatenated into a string and ** used as the P4 operand, they will be more readable. ** ** Note also that the numeric types are grouped together so that testing ** for a numeric type is a single comparison. */ #define SQLITE_AFF_TEXT 'a' #define SQLITE_AFF_NONE 'b' #define SQLITE_AFF_NUMERIC 'c' #define SQLITE_AFF_INTEGER 'd' #define SQLITE_AFF_REAL 'e' #define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC) /* ** The SQLITE_AFF_MASK values masks off the significant bits of an ** affinity value. */ #define SQLITE_AFF_MASK 0x67 /* ** Additional bit values that can be ORed with an affinity without ** changing the affinity. */ #define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */ #define SQLITE_STOREP2 0x10 /* Store result in reg[P2] rather than jump */ #define SQLITE_NULLEQ 0x80 /* NULL=NULL */ /* ** An object of this type is created for each virtual table present in ** the database schema. ** ** If the database schema is shared, then there is one instance of this ** structure for each database connection (sqlite3*) that uses the shared ** schema. This is because each database connection requires its own unique ** instance of the sqlite3_vtab* handle used to access the virtual table ** implementation. sqlite3_vtab* handles can not be shared between ** database connections, even when the rest of the in-memory database ** schema is shared, as the implementation often stores the database ** connection handle passed to it via the xConnect() or xCreate() method ** during initialization internally. This database connection handle may ** then used by the virtual table implementation to access real tables ** within the database. So that they appear as part of the callers ** transaction, these accesses need to be made via the same database ** connection as that used to execute SQL operations on the virtual table. ** ** All VTable objects that correspond to a single table in a shared ** database schema are initially stored in a linked-list pointed to by ** the Table.pVTable member variable of the corresponding Table object. ** When an sqlite3_prepare() operation is required to access the virtual ** table, it searches the list for the VTable that corresponds to the ** database connection doing the preparing so as to use the correct ** sqlite3_vtab* handle in the compiled query. ** ** When an in-memory Table object is deleted (for example when the ** schema is being reloaded for some reason), the VTable objects are not ** deleted and the sqlite3_vtab* handles are not xDisconnect()ed ** immediately. Instead, they are moved from the Table.pVTable list to ** another linked list headed by the sqlite3.pDisconnect member of the ** corresponding sqlite3 structure. They are then deleted/xDisconnected ** next time a statement is prepared using said sqlite3*. This is done ** to avoid deadlock issues involving multiple sqlite3.mutex mutexes. ** Refer to comments above function sqlite3VtabUnlockList() for an ** explanation as to why it is safe to add an entry to an sqlite3.pDisconnect ** list without holding the corresponding sqlite3.mutex mutex. ** ** The memory for objects of this type is always allocated by ** sqlite3DbMalloc(), using the connection handle stored in VTable.db as ** the first argument. */ struct VTable { sqlite3 *db; /* Database connection associated with this table */ Module *pMod; /* Pointer to module implementation */ sqlite3_vtab *pVtab; /* Pointer to vtab instance */ int nRef; /* Number of pointers to this structure */ VTable *pNext; /* Next in linked list (see above) */ }; /* ** Each SQL table is represented in memory by an instance of the ** following structure. ** ** Table.zName is the name of the table. The case of the original ** CREATE TABLE statement is stored, but case is not significant for ** comparisons. ** ** Table.nCol is the number of columns in this table. Table.aCol is a ** pointer to an array of Column structures, one for each column. ** ** If the table has an INTEGER PRIMARY KEY, then Table.iPKey is the index of ** the column that is that key. Otherwise Table.iPKey is negative. Note ** that the datatype of the PRIMARY KEY must be INTEGER for this field to ** be set. An INTEGER PRIMARY KEY is used as the rowid for each row of ** the table. If a table has no INTEGER PRIMARY KEY, then a random rowid ** is generated for each row of the table. TF_HasPrimaryKey is set if ** the table has any PRIMARY KEY, INTEGER or otherwise. ** ** Table.tnum is the page number for the root BTree page of the table in the ** database file. If Table.iDb is the index of the database table backend ** in sqlite.aDb[]. 0 is for the main database and 1 is for the file that ** holds temporary tables and indices. If TF_Ephemeral is set ** then the table is stored in a file that is automatically deleted ** when the VDBE cursor to the table is closed. In this case Table.tnum ** refers VDBE cursor number that holds the table open, not to the root ** page number. Transient tables are used to hold the results of a ** sub-query that appears instead of a real table name in the FROM clause ** of a SELECT statement. */ struct Table { sqlite3 *dbMem; /* DB connection used for lookaside allocations. */ char *zName; /* Name of the table or view */ int iPKey; /* If not negative, use aCol[iPKey] as the primary key */ int nCol; /* Number of columns in this table */ Column *aCol; /* Information about each column */ Index *pIndex; /* List of SQL indexes on this table. */ int tnum; /* Root BTree node for this table (see note above) */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ u16 nRef; /* Number of pointers to this Table */ u8 tabFlags; /* Mask of TF_* values */ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ FKey *pFKey; /* Linked list of all foreign keys in this table */ char *zColAff; /* String defining the affinity of each column */ #ifndef SQLITE_OMIT_CHECK Expr *pCheck; /* The AND of all CHECK constraints */ #endif #ifndef SQLITE_OMIT_ALTERTABLE int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ #endif #ifndef SQLITE_OMIT_VIRTUALTABLE VTable *pVTable; /* List of VTable objects. */ int nModuleArg; /* Number of arguments to the module */ char **azModuleArg; /* Text of all module args. [0] is module name */ #endif Trigger *pTrigger; /* List of triggers stored in pSchema */ Schema *pSchema; /* Schema that contains this table */ Table *pNextZombie; /* Next on the Parse.pZombieTab list */ }; /* ** Allowed values for Tabe.tabFlags. */ #define TF_Readonly 0x01 /* Read-only system table */ #define TF_Ephemeral 0x02 /* An ephemeral table */ #define TF_HasPrimaryKey 0x04 /* Table has a primary key */ #define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */ #define TF_Virtual 0x10 /* Is a virtual table */ #define TF_NeedMetadata 0x20 /* aCol[].zType and aCol[].pColl missing */ /* ** Test to see whether or not a table is a virtual table. This is ** done as a macro so that it will be optimized out when virtual ** table support is omitted from the build. */ #ifndef SQLITE_OMIT_VIRTUALTABLE # define IsVirtual(X) (((X)->tabFlags & TF_Virtual)!=0) # define IsHiddenColumn(X) ((X)->isHidden) #else # define IsVirtual(X) 0 # define IsHiddenColumn(X) 0 #endif /* ** Each foreign key constraint is an instance of the following structure. ** ** A foreign key is associated with two tables. The "from" table is ** the table that contains the REFERENCES clause that creates the foreign ** key. The "to" table is the table that is named in the REFERENCES clause. ** Consider this example: ** ** CREATE TABLE ex1( ** a INTEGER PRIMARY KEY, ** b INTEGER CONSTRAINT fk1 REFERENCES ex2(x) ** ); ** ** For foreign key "fk1", the from-table is "ex1" and the to-table is "ex2". ** ** Each REFERENCES clause generates an instance of the following structure ** which is attached to the from-table. The to-table need not exist when ** the from-table is created. The existence of the to-table is not checked. */ struct FKey { Table *pFrom; /* Table containing the REFERENCES clause (aka: Child) */ FKey *pNextFrom; /* Next foreign key in pFrom */ char *zTo; /* Name of table that the key points to (aka: Parent) */ FKey *pNextTo; /* Next foreign key on table named zTo */ FKey *pPrevTo; /* Previous foreign key on table named zTo */ int nCol; /* Number of columns in this key */ /* EV: R-30323-21917 */ u8 isDeferred; /* True if constraint checking is deferred till COMMIT */ u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */ Trigger *apTrigger[2]; /* Triggers for aAction[] actions */ struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ int iFrom; /* Index of column in pFrom */ char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */ } aCol[1]; /* One entry for each of nCol column s */ }; /* ** SQLite supports many different ways to resolve a constraint ** error. ROLLBACK processing means that a constraint violation ** causes the operation in process to fail and for the current transaction ** to be rolled back. ABORT processing means the operation in process ** fails and any prior changes from that one operation are backed out, ** but the transaction is not rolled back. FAIL processing means that ** the operation in progress stops and returns an error code. But prior ** changes due to the same operation are not backed out and no rollback ** occurs. IGNORE means that the particular row that caused the constraint ** error is not inserted or updated. Processing continues and no error ** is returned. REPLACE means that preexisting database rows that caused ** a UNIQUE constraint violation are removed so that the new insert or ** update can proceed. Processing continues and no error is reported. ** ** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys. ** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the ** same as ROLLBACK for DEFERRED keys. SETNULL means that the foreign ** key is set to NULL. CASCADE means that a DELETE or UPDATE of the ** referenced table row is propagated into the row that holds the ** foreign key. ** ** The following symbolic values are used to record which type ** of action to take. */ #define OE_None 0 /* There is no constraint to check */ #define OE_Rollback 1 /* Fail the operation and rollback the transaction */ #define OE_Abort 2 /* Back out changes but do no rollback transaction */ #define OE_Fail 3 /* Stop the operation but leave all prior changes */ #define OE_Ignore 4 /* Ignore the error. Do not do the INSERT or UPDATE */ #define OE_Replace 5 /* Delete existing record, then do INSERT or UPDATE */ #define OE_Restrict 6 /* OE_Abort for IMMEDIATE, OE_Rollback for DEFERRED */ #define OE_SetNull 7 /* Set the foreign key value to NULL */ #define OE_SetDflt 8 /* Set the foreign key value to its default */ #define OE_Cascade 9 /* Cascade the changes */ #define OE_Default 99 /* Do whatever the default action is */ /* ** An instance of the following structure is passed as the first ** argument to sqlite3VdbeKeyCompare and is used to control the ** comparison of the two index keys. */ struct KeyInfo { sqlite3 *db; /* The database connection */ u8 enc; /* Text encoding - one of the TEXT_Utf* values */ u16 nField; /* Number of entries in aColl[] */ u8 *aSortOrder; /* If defined an aSortOrder[i] is true, sort DESC */ CollSeq *aColl[1]; /* Collating sequence for each term of the key */ }; /* ** An instance of the following structure holds information about a ** single index record that has already been parsed out into individual ** values. ** ** A record is an object that contains one or more fields of data. ** Records are used to store the content of a table row and to store ** the key of an index. A blob encoding of a record is created by ** the OP_MakeRecord opcode of the VDBE and is disassembled by the ** OP_Column opcode. ** ** This structure holds a record that has already been disassembled ** into its constituent fields. */ struct UnpackedRecord { KeyInfo *pKeyInfo; /* Collation and sort-order information */ u16 nField; /* Number of entries in apMem[] */ u16 flags; /* Boolean settings. UNPACKED_... below */ i64 rowid; /* Used by UNPACKED_PREFIX_SEARCH */ Mem *aMem; /* Values */ }; /* ** Allowed values of UnpackedRecord.flags */ #define UNPACKED_NEED_FREE 0x0001 /* Memory is from sqlite3Malloc() */ #define UNPACKED_NEED_DESTROY 0x0002 /* apMem[]s should all be destroyed */ #define UNPACKED_IGNORE_ROWID 0x0004 /* Ignore trailing rowid on key1 */ #define UNPACKED_INCRKEY 0x0008 /* Make this key an epsilon larger */ #define UNPACKED_PREFIX_MATCH 0x0010 /* A prefix match is considered OK */ #define UNPACKED_PREFIX_SEARCH 0x0020 /* A prefix match is considered OK */ /* ** Each SQL index is represented in memory by an ** instance of the following structure. ** ** The columns of the table that are to be indexed are described ** by the aiColumn[] field of this structure. For example, suppose ** we have the following table and index: ** ** CREATE TABLE Ex1(c1 int, c2 int, c3 text); ** CREATE INDEX Ex2 ON Ex1(c3,c1); ** ** In the Table structure describing Ex1, nCol==3 because there are ** three columns in the table. In the Index structure describing ** Ex2, nColumn==2 since 2 of the 3 columns of Ex1 are indexed. ** The value of aiColumn is {2, 0}. aiColumn[0]==2 because the ** first column to be indexed (c3) has an index of 2 in Ex1.aCol[]. ** The second column to be indexed (c1) has an index of 0 in ** Ex1.aCol[], hence Ex2.aiColumn[1]==0. ** ** The Index.onError field determines whether or not the indexed columns ** must be unique and what to do if they are not. When Index.onError=OE_None, ** it means this is not a unique index. Otherwise it is a unique index ** and the value of Index.onError indicate the which conflict resolution ** algorithm to employ whenever an attempt is made to insert a non-unique ** element. */ struct Index { char *zName; /* Name of this index */ int nColumn; /* Number of columns in the table used by this index */ int *aiColumn; /* Which columns are used by this index. 1st is 0 */ unsigned *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ Table *pTable; /* The SQL table being indexed */ int tnum; /* Page containing root of this index in database file */ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ char *zColAff; /* String defining the affinity of each column */ Index *pNext; /* The next index associated with the same table */ Schema *pSchema; /* Schema containing this index */ u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ char **azColl; /* Array of collation sequence names for index */ IndexSample *aSample; /* Array of SQLITE_INDEX_SAMPLES samples */ }; /* ** Each sample stored in the sqlite_stat2 table is represented in memory ** using a structure of this type. */ struct IndexSample { union { char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */ double r; /* Value if eType is SQLITE_FLOAT or SQLITE_INTEGER */ } u; u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */ u8 nByte; /* Size in byte of text or blob. */ }; /* ** Each token coming out of the lexer is an instance of ** this structure. Tokens are also used as part of an expression. ** ** Note if Token.z==0 then Token.dyn and Token.n are undefined and ** may contain random values. Do not make any assumptions about Token.dyn ** and Token.n when Token.z==0. */ struct Token { const char *z; /* Text of the token. Not NULL-terminated! */ unsigned int n; /* Number of characters in this token */ }; /* ** An instance of this structure contains information needed to generate ** code for a SELECT that contains aggregate functions. ** ** If Expr.op==TK_AGG_COLUMN or TK_AGG_FUNCTION then Expr.pAggInfo is a ** pointer to this structure. The Expr.iColumn field is the index in ** AggInfo.aCol[] or AggInfo.aFunc[] of information needed to generate ** code for that node. ** ** AggInfo.pGroupBy and AggInfo.aFunc.pExpr point to fields within the ** original Select structure that describes the SELECT statement. These ** fields do not need to be freed when deallocating the AggInfo structure. */ struct AggInfo { u8 directMode; /* Direct rendering mode means take data directly ** from source tables rather than from accumulators */ u8 useSortingIdx; /* In direct mode, reference the sorting index rather ** than the source table */ int sortingIdx; /* Cursor number of the sorting index */ ExprList *pGroupBy; /* The group by clause */ int nSortingColumn; /* Number of columns in the sorting index */ struct AggInfo_col { /* For each column used in source tables */ Table *pTab; /* Source table */ int iTable; /* Cursor number of the source table */ int iColumn; /* Column number within the source table */ int iSorterColumn; /* Column number in the sorting index */ int iMem; /* Memory location that acts as accumulator */ Expr *pExpr; /* The original expression */ } *aCol; int nColumn; /* Number of used entries in aCol[] */ int nColumnAlloc; /* Number of slots allocated for aCol[] */ int nAccumulator; /* Number of columns that show through to the output. ** Additional columns are used only as parameters to ** aggregate functions */ struct AggInfo_func { /* For each aggregate function */ Expr *pExpr; /* Expression encoding the function */ FuncDef *pFunc; /* The aggregate function implementation */ int iMem; /* Memory location that acts as accumulator */ int iDistinct; /* Ephemeral table used to enforce DISTINCT */ } *aFunc; int nFunc; /* Number of entries in aFunc[] */ int nFuncAlloc; /* Number of slots allocated for aFunc[] */ }; /* ** The datatype ynVar is a signed integer, either 16-bit or 32-bit. ** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater ** than 32767 we have to make it 32-bit. 16-bit is preferred because ** it uses less memory in the Expr object, which is a big memory user ** in systems with lots of prepared statements. And few applications ** need more than about 10 or 20 variables. But some extreme users want ** to have prepared statements with over 32767 variables, and for them ** the option is available (at compile-time). */ #if SQLITE_MAX_VARIABLE_NUMBER<=32767 typedef i16 ynVar; #else typedef int ynVar; #endif /* ** Each node of an expression in the parse tree is an instance ** of this structure. ** ** Expr.op is the opcode. The integer parser token codes are reused ** as opcodes here. For example, the parser defines TK_GE to be an integer ** code representing the ">=" operator. This same integer code is reused ** to represent the greater-than-or-equal-to operator in the expression ** tree. ** ** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB, ** or TK_STRING), then Expr.token contains the text of the SQL literal. If ** the expression is a variable (TK_VARIABLE), then Expr.token contains the ** variable name. Finally, if the expression is an SQL function (TK_FUNCTION), ** then Expr.token contains the name of the function. ** ** Expr.pRight and Expr.pLeft are the left and right subexpressions of a ** binary operator. Either or both may be NULL. ** ** Expr.x.pList is a list of arguments if the expression is an SQL function, ** a CASE expression or an IN expression of the form " IN (, ...)". ** Expr.x.pSelect is used if the expression is a sub-select or an expression of ** the form " IN (SELECT ...)". If the EP_xIsSelect bit is set in the ** Expr.flags mask, then Expr.x.pSelect is valid. Otherwise, Expr.x.pList is ** valid. ** ** An expression of the form ID or ID.ID refers to a column in a table. ** For such expressions, Expr.op is set to TK_COLUMN and Expr.iTable is ** the integer cursor number of a VDBE cursor pointing to that table and ** Expr.iColumn is the column number for the specific column. If the ** expression is used as a result in an aggregate SELECT, then the ** value is also stored in the Expr.iAgg column in the aggregate so that ** it can be accessed after all aggregates are computed. ** ** If the expression is an unbound variable marker (a question mark ** character '?' in the original SQL) then the Expr.iTable holds the index ** number for that variable. ** ** If the expression is a subquery then Expr.iColumn holds an integer ** register number containing the result of the subquery. If the ** subquery gives a constant result, then iTable is -1. If the subquery ** gives a different answer at different times during statement processing ** then iTable is the address of a subroutine that computes the subquery. ** ** If the Expr is of type OP_Column, and the table it is selecting from ** is a disk table or the "old.*" pseudo-table, then pTab points to the ** corresponding table definition. ** ** ALLOCATION NOTES: ** ** Expr objects can use a lot of memory space in database schema. To ** help reduce memory requirements, sometimes an Expr object will be ** truncated. And to reduce the number of memory allocations, sometimes ** two or more Expr objects will be stored in a single memory allocation, ** together with Expr.zToken strings. ** ** If the EP_Reduced and EP_TokenOnly flags are set when ** an Expr object is truncated. When EP_Reduced is set, then all ** the child Expr objects in the Expr.pLeft and Expr.pRight subtrees ** are contained within the same memory allocation. Note, however, that ** the subtrees in Expr.x.pList or Expr.x.pSelect are always separately ** allocated, regardless of whether or not EP_Reduced is set. */ struct Expr { u8 op; /* Operation performed by this node */ char affinity; /* The affinity of the column or 0 if not a column */ u16 flags; /* Various flags. EP_* See below */ union { char *zToken; /* Token value. Zero terminated and dequoted */ int iValue; /* Integer value if EP_IntValue */ } u; /* If the EP_TokenOnly flag is set in the Expr.flags mask, then no ** space is allocated for the fields below this point. An attempt to ** access them will result in a segfault or malfunction. *********************************************************************/ Expr *pLeft; /* Left subnode */ Expr *pRight; /* Right subnode */ union { ExprList *pList; /* Function arguments or in " IN ( IN (" name="Name"/> Occupation: Gender: >'checked="yes"'?>/> Male     >'checked="yes"'?>/> Female Likes: >'checked="yes"'?>/> Sport     >'checked="yes"'?>/> Reading     >'checked="yes"'?>/> Music Modes (Exp. one="One", two="Two"): one: > Request.posts["Modes"]["one"] ?>"/>     two: > Request.posts["Modes"]["two"] ?>"/> three: > Request.posts["Modes"]["three"] ?>"/> @endcode Of course, a so verbose and "handmade" HTML form management and crafting is not what usually done in well planned server side applications (that use specific libraries to render the HTML forms and fill them with incoming data), but this is meant to be a sample of what's should be done "behind the scenes". The form action uses the "Request.uri" field to send the fields in the form to the same script initially loaded. To simplify the code, a set of default values are stored in the @b posts fields to be used in the code in the form rendering part in case they're not there. The @b name attribute of the @b input field is turned into a key in the @b posts dictionary of the @b Request object. Values are directly decoded and transformed into strings before they reach the script. Adding a "[]" (square brackets) pair to a field name, all the values associated with that field are stored in an array. In the above example, we have the "Likes" field created with a set of interrelated checkboxes, which value is filled with 0 or more strings, each being an option in the checkbox list. @note It is possible to create arrays of values also in GET fields, appending the "[]" to the variable names in the URI query part. When something is placed within the square brackets, for example "[some_name]", all the values associated with that field are stored in a dictionary, under the key represented by the given name. In the above example, we have the "Modes" field created with a set of interrelated text vields, which value is filled with 0 or more strings, each being stored under the "Modes" dictionary, and associated with the string key value indicated in the field name. For instance, will cause the value associated to this input to be provided to the receiver script under Request.post["Modes"]["one"]. @note Similarly, the same can be done for GET fields, appending a "[...]" descriptor to a variable name in the URI query part. Consider using the @a Request.getField method to access a field that may come from GET or POST fields. This method can also provide a simple default in case the field is not given. For example, a variable that is vital for your script may be read like this: @code // Read a "work_mode" variable, and if not sent default it to "normal" work_mode = Request.getField( "work_mode", "normal" ) @endcode @section upload_control Upload control The WOPI system allows to retrieve and manage uploaded files through a class called @a Uploaded. Each field of a file upload enable form is turned into an @a Uploaded instance in the corresponding post entry of the @a Request entity. In example, this form: @code

    Form field:

    Input file:

    @endcode will cause the Request.posts dictionary to contain a string entry for the "Afield" key, and an @a Uploaded instance for TheFile. A typical inspect() result on the Request.posts dictionary may be like the following: @code Dict[2]{ "Afield" => "aaaa" "TheFile" => Object of class Uploaded { data => Nil error => Nil filename => "kusamakura.pdf" mimeType => "application/x-filler" open => Ext. Function Uploaded.open read => Ext. Function Uploaded.read size => int(472396) storage => "/tmp/8sO5pvU6ZoxEY1Qn" store => Ext. Function Uploaded.store } "sub_btn" => "Send" } @endcode @note Notice the @b accept-charset parameter of the @b form tag. As MIME doesn't specify an encoding standard for multipart data, the WOPI back end modules assumes that all the fields in multipart posted forms (except the file entries) are encoded in utf-8. Please, use the accept-charset="utf-8" in every multipart form when sending data to Falcon modules. @subsection upload_control_config Configuration options The upload control is sensible to three parameters that can be configured by the front-ends. Different front-ends have different defaults, and different means to set this parameters; however, the meaning of the parameters stays unchanged across all the frontends: - @b Maximum size for uploads: it's the maximum upload size allowed, in kilobytes (1024 bytes). The upload size is applied globally to the incoming form data; it's the total size of the uploaded files plus the overhead for headers and multipart boundaries. If the size of the incoming data is above this limit, the script receive a notify in the ":error" @a Request.posts dictionary, explaining the problem. The script can then notify the problem to the user. - @b Maximum size for uploads stored in memory: Limit for direct-to-memory upload; see below. - @b UploadDir: Where to place temporary files for uploads. This must be a directory present on the system and writable by the web server process. This directory is used also by @a Request.tempFile to create temporary files. If the server cannot write on the target directory when needed, an ":error" entry is returned in the @a Request.posts fields, describing the problem. @subsection upload_recv Upload receiving mode WOPI front-ends serve the uploaded files in two ways: - Reading them in memory and serving them as a MemBuf. - Saving them in a temporary file and allowing the scripts to access it at a later time. If a file is smaller than what set in the maximum size for memory-stored uploads, as set in the specific front-end configuration, it is directed to the @b Uploaded.data field and its whole contents are stored in a MemBuf. If it's larger, it is stored in a temporary file whose complete path is written as a string in the @b Uploaded.storage property of the incoming data. To have every upload saved in a temporary file, set the maximum memory-upload size to zero, to have everything in memory without disk storage set it to the same value as the maximum allowed upload size. The @a Uploaded class has a set of methods that are useful to treat both upload modes the same way at script level; in other words, it is possible to use this methods to write scripts working just the same if the uploaded data is stored in memory or in a temporary file. They are namely: - @a Uploaded.store - saves the uploaded data to a specified file or directory. In case the uploaded file was stored in a temporary file, a file rename is tried first, and a full copy is performed if not possible (i.e. if source and destination are on different devices). - @a Uploaded.open - opens a stream (file or memory-based) towards the uploaded data; the stream can be used to seek, read or modify the uploaded data. - @a Uploaded.read - reads the temporary file into a memory buffer (placing it in the Uploaded.data property), doing nothing if Uploaded.data is already in place. Temporary files are destroyed when the script terminates, so they don't waste system resources and the script doesn't need to take care of their deletion. @section wopi_model_cookies Cookie control Cookies are variables that can be stored on the remote browser, and that it will be sent back by the browser when contacting the site. Cookies are read and decoded in the @a Request.cookies dictionary, similarly to what happens with @a Request.gets and @a Request.posts. Also, the @a Request.getField method can be used to access cookie fields as well. Cookies are set via the @a Reply.setCookie method. They can be bound with a specific sub-part of the site, or deemed to be "forgotten" by the remote client after a given time, or at a specific point in future. The complete list of parameters is described in the @a Reply.setCookie method documentation; what we want to show here is how to use cookies in the flow of your web-based application. Suppose that you want to use cookies to identify the remote user and remember how many times your page has been visited. The following script shows how to properly set cookies, verify their value and eventually remove them. @code Cookie test

    Cookie test

    Welcome, my master!

    You have been around times.

    Do you want to logout or to reload this page?

    ?

    Logout performed. Goodbye my master!

    Wrong use rid/password. Please try again.

    Bad move.You're trying to force the system with unauthorized cookies.

    User:
    Password:
    @endcode Notice the first part: in case you're using an FTD script, it's necessary to open immediately a processor block (major/question-mark) to prevent any output to be generated prior we get an occasion to set cookies. Then, cookies are read through @a Request.getField. Later on we see that we have set the same names for get fields ("mode=logout"), post fields (user/pwd) and cookies. This is totally arbitrary, and you may prefer to keep those separated so that you can determine if a field has been previously set as a cookie or is coming from a login form. However, at times is useful to abstract the source of a field, and just consider its value, no matter how the remote client sent it to us. In this way, we can have scripts sending logout requests via standard http links, and sending us login requests via forms, as it happens here. Be careful about checking authorization cookies: as you can see, the script here repeats the check on the login cookies at each step @section wopi_model_session Sessions Sessions are conceptually similar to cookies with one mayor difference. They are stored server-side. A variable in the Request scoping (cookie, post or get) contains an unique session ID that refers to a dictionary of key-value pairs stored server side. As the "session data" is just a standard Falcon dictionary, every kind of data can be used both for keys and values. When the script terminates, the data is stored in a safe place (depending on the configuration options and on the front-end mechanism, it is usually serialized to a temporary file). The default behavior consists in storing the session id in a cookie named "SID", but it is possible to change the name of the variable storing the session ID changing the value of the @a Request.sidField property, and prevent the system to automatically generate the cookie changing the value of the @a Request.autoSession field to false. In this case, the scripts are required to keep track of the SID variable by hand, and to pass it around (for example, storing it in the @a Request.gets field and using @a Request.fwdGet method to create a query element to be attached to intra-site links). As the @a Request.getSession method will try to create a new session if a valid SID is not provided anywhere as an input variable, the @a Request.hasSession method can be used to check if we can suppose that a session was already open (or if consistent, the script may just check for the presence of previously stored data in the dictionary returned by @a Request.getSession). Many sites prefer to create a session for any incoming visitor, adding valid user data to the session when the remote client is validated; in this case, just using @a Request.getSession settles the problem, as you can count on the data to be empty for new visitors, filled with generic data for visitors still not authenticated and having a valid user authentication field when the user has logged in. Other sites prefer to associate session data only to authenticated users; in this second, more complex case, it is advisable to use the @a Request.hasSession method to determine if the remote user has tried to open a session in its previous contacts. Also, in this cases, the site will want to close the session (removing the remote cookie and freeing locally allocated resources) when the remote user explicitly logs out. This second, more complete and complex approach is shown in the following example, in which we substitute the cookie concepts seen in the previous section with session-based processing: @code Cookie test

    Session test

    Welcome, my master!

    You have been around times.

    Do you want to logout or to reload this page?

    Logout performed. Goodbye my master!

    . Please try again.

    User:
    Password:
    @endcode See how the session approach does not require the password to be stored anywhere, nor the authentication process to be repeated. @note To prevent man-in-the-middle attacks that may be possible if the attacker sniffs the SID cookie or variable passing by, or if he guess by brute force a SID being still active, it is advisable to record the @a Request.remote_ip field that was detected at session creation in the session dictionary, and check if it's consistent with following requests. Sessions will be automatically closed, and their data destroyed, after a certain timeout has expired. Each call to @a Request.getSession will reset the timeout; same happens each time a script having called @a Request.getSession terminates. So, as long as the remote user keeps visiting the site, its session stays open. The site may provide some automatic refresh strategy (even AJAX based) that may "ping" the session as long as the browser has an open page on the side, if they wish to. @note The timeout can be set through the target front-end configuration. @subsection wopi_model_named_session Named sessions The anonymous session support is meant to create a single set of data which is associated with a visitor of a site. However, it is also possible to specify sessions having a specific name; this is useful when receiving an ID from a remote site, or a central service which distributes the data to the users associating them with an unique key. The function @a Request.startSession accepts a string parameter that can be used to get the same session at a later time. The same parameter can then be passed to @a Request.getSession and @a Request.closeSession to retreive the same data during another step of the process and to get rid of that data. @section wopi_pdata Persistent local data. Some web applications run directly through persistent processes, as as those served through the FastCGI, the apache module or the falhttpd front-ends, may find useful to initialize some data just once, as they are called the first time under their execution context (be it a system process or a separate thread), and then reused indefinitely until the execution context stays valid. This is the case of database connections: opening and closing a database connection each time a web application is invoked may be an overkill. The same connection object may be shared by all the web application invoked by the same web server execution space (process or thread). As the data needs not to be serialized to other processes or applications, and stays local and private for the currently running process or thread, any falcon item can be sored in this way, even if it doesn't support sharing or serialization. Persistent data can be created through the @a Wopi.setPData method, and then accessed through and @a Wopi.getPData. @code class MyClass file = nil init file = IOStream( "some file" ) end end Wopi.setPData( "The common MyClass", MyClass() ) // later on ... inst = Wopi.getPData( "The common MyClass" ) inst.file.writeString( "Hello world!" ) @endcode The @a Wopi.getPData method has an extra parameter that can be used to create the object on the fly if it has still not been initialized for this process: @code // open the file or reuse it... inst = Wopi.getPData( "The common MyClass", {=> return MyClass()} ) // continue to use the already open file... inst.file.writeString( "Hello world!" ) @endcode Persistent data methods can be used also from single-process web applications, (CGI based front-ends), with the effect that the web application will see them not initialized at every invocation. @section wopi_appdata Application-wide data. Application-wide data is to be considered an asset that must stay valid for a whole application without any time limit. More applications can produce private (but application-widely visible) data on the same site, and it's even possible to exchange data across different applications. Application data is not meant for configuration; it is possible to store relatively static configuration data into configuration files or separate variable-declaring scripts. Web application-wide data is meant for data that may change across rapidly, or on a per-user basis,which is meaningless to store on more-secure and organize persistent storages, as databases. Typical usages for application-wide data are: - Stats on currently logged in users. - Page counters. - Sudden warnings (messages to be displayed to any visitor). Falcon WOPI provides a default (unnamed) application data store, and the ability to create application specific stores, each named under the desired application. The data is stored as a dictionary (which may be empty if the data was never modified since its creation), and can be accessed via the @a Wopi.getAppData method. The method can be fed with an optional string, which represent the name of the application willing to access its data. For example: @code const appname = "My_Web_Application" data = Wopi.getAppData( appname ) // ... use data @endcode @note The application name shouldn't contain spaces nor slashes: on some system, it would cause an error when trying to store the data on semi-persistent or persistent resources. The querying script owns a personal copy of the data that can be changed at will. Concurrent changes of the same application data won't be visible in different scripts until one of the data holder decides to write its copy to the application space via the @a Wopi.setData. This method accepts an optional application name, that @i should match the application name used on the @a Wopi.getData call (this is not a requirement). @code const appname = "My Web Application" // ... using data Wopi.setAppData( data, appname ) @endcode @note The item fed into Wopi.setAppData doesn't need to be created by @a Wopi.getAppData method. To propagate the new status of the application wide data, the script must call @a Wopi.setAppData. If the data can be safely written on the system and propagated to other scripts, the function returns true; otherwise, it returns false and the contents in the given item are atomically refreshed with the new contents. To have this atomically modified data back in the calling program, the parameter must have been passed as a reference, so that the old data can be discarded and the new data can be used instead. In short, the usage pattern for the application wide that is the following: @code const appname = "MyApp" data = Wopi.getAppData( appname ) // operate on data while not Wopi.setAppData( $data, appname ) // reapply changes to the modified data, if necessary end @endcode For example, suppose that you need to increment a counter indicating the visited pages: @code data = Wopi.getData() loop if data == nil data = [ "counter" => 1 ] else data["counter"] ++ end end counter.setData( $data ) @endcode In the above example, there is always the possibility that some other script increments the count while we're trying to store its new value. This method ensures that the data is still valid and we're the only having changed it between the moment in which they are first queried and when they are finally stored back to the common repository. @note The stored data can be any arbitrary Falcon data, provided that it supports serialization. In case of error (I/O on the external persistent storage or serialization/deserialization errors) an exception is raised. Errors that are considered temporary or transient won't generate exception raising; the scripts can consider the raised exception as a definitive failure that will prevent to operate correctly with the designed application-wide data. */ /*# @page falcgi_iso Isomorphism of CGI based sites. @inmodule WOPI WOPI provides a coherent interface across different data providers, including CGI and CGI-based front ends (as, for example, FastCGI and the CGI module). However, what WOPI can do is to expose the same interface to access web information and functionalities to communicate with the remote client. It cannot change the model through which the dynamic page is served by the web server. The CGI model has been designed to provide single entry point, relatively complex applications generating dynamically the whole content of the replied page. They are meant to be monolithic web applications taking care of a whole aspect of the dynamic site by themselves. Opposely, active server-side pages model has been designed with the idea of providing multiple simple entry points in the web-based applications, each being mainly a "view" entity being backed up, at best, by a central engine made of "common functions". WOPI cannot completely overcome the differences between this two philosophical approaches to web based application programming; it limits to offer a consistent interface no matter what model you prefer to chose. However, shaping down the web-based application so that it ignores this differences, it is possible to ensure that it runs seamlessly under both CGI oriented front-ends and server-side active pages oriented front-ends. And the result is also relatively elegant, so it's worth having a look. @section falcgi_iso_entry One entry point The secret of this is offering a single entry point to the application, where all the decision about what elements to be loaded will be taken. The structure of a site thought with this idea may be the following: @code html_docs/ ... cgi-bin/ index.exe index.fal pages/ default.ftd first.ftd second.ftd third.ftd ,... @endcode The @b index.exe program would be the CGI front-end (it may be omitted in case we're using the cgi_fm module from within the index.fal script). Our index.fal may look like this: @code #!/usr/bin/falcon -pcgi //^^ this is optional, and if available, it allows to work without the binary front-end. // shows the real page if "p" notin Requests.gets errorDocument( "Sorry, page ID not provided" ) end // try to load it at this point. p = Requests.gets["p"] try include( "pages/" + p + ".ftd" ) catch in error errorDocument( @"Sorry, cannot load required page \"$p\"", error ) end // End of main program. // expose an utility function to help creating links to ourself: function makeLink( page ) return Request.uri + "?p=" + page end // A simple utility function errorDocument( text, error ) >' Error!

    Error!

    ' > text, '

    ' if error > "

    Error:

    ", error,toString(), "

    " end > "" end // export the utility functions so pages can use them export makeLink, errorDocument @endcode In this way, it doesn't matter if the application is served through a CGI interface or through a web-based interface. As long as every part of the web application respects the directive of indicating other pages to be loaded by adding a @b p field to the query of the link, instead of trying to link it directly, the page will be served through the main script, which will be loaded just the same under a CGI front-end or under an active server page based front-end. Actually, many well-designed active server page based web applications adopt this approach just to simplify the organization of their site and to centralize all the setup operations, performing them before the element that takes care of a certain step (that is, a specific "page") is even loaded. It's a "configuration push" model, rather a configuration "pull", where each page must take care of loading the configuration and setting up the environment (for example, checking for a valid user to have logged in and filling the variables accordingly). This "push" design has been found superior by many web designer, so the need to use it to make CGI based and server-active-page based WOPI application seamlessly isomorphic should not be considered a limitation; rather, the ability to pick a simpler, direct model to treat the two kind of web applications differently is to be considered an extended feature of WOPI, which may be useful or not. */ modules/native/wopi/docs/wopi_modules.fd000066400000000000000000000330421176363201700207530ustar00rootroot00000000000000/*# @page wopi_modules Building modular applications @inmodule WOPI For how simple a web application may be, it will usually be composed of several pages meant to work coordinately. Although coordination can happen also through data exchange (for example through databases), it is often necessary to share common code or show repeated page elements even when the remote client access different elements of a site. This chapter shows how to use Falcon inter-module facilities to build more complex and structured web based applications. @note Module loading and importing works as described in the Falcon Survival Guide, and is very sensible to the settings of your Falcon installation and WOPI load path configuration option. @section wopi_modules_lib Function libraries The most common kind of shared code consists in functions, object, classes or other functional code that is used across different modules. Falcon modular structure allows to create more complex applications binding different modules into one unique application space via the @b load directive, or to access remote code in a user-provider relationship via the @b import/from directive. Suppose you want to make some functions or classes from the following simple library: @code // simplelib.fal class HtmlHeader( title, desc ) title = title desc = desc function render() // Notice: we're returning a string that may or may not be used // by the target script. return @" $(self.title) " end end function closeHtml() // Notice: we'll be directly printing this output // and it will be composed in the body of the final page > "" end @endcode You can import the code from a page like the following: @code head.render() ?>

    A page with some function coming from outside

    @endcode Of course, in this trivial case, using @b import to confine the imported symbols in a namespace is an overkill, but you may prefer this for stylistic reasons. However, when the library is highly coupled with your web-based application, it is totally consistent to use @b load instead, to form a unique monolithic applications out of the separate module you want to merge. In some cases, it is actually the best solution, as we're explaining in the next section. @section wopi_modules_conf Site-wide configuration Although it is possible to configure a web based application using some data file or via the Configuration module in Feathers, it is simpler to store the global configuration for a site in a common module directly loaded by all the elements of the web application. For example, storing the web site and web master information, database connection information and so on may be a matter of writing: @code // GLOBAL CONFIG (config.fal) WEBMASTER = "me@this_site.com" DOMAIN= "http://www.this_site.com" //... export @endcode And then, @code

    ...

    Any problem with this page? -- write me at

    @endcode @subsection wopi_modules_conf_values Global environment values At times, it is useful for some web-based application to store some globally visible value in the "global environment". For example, it is useful to set a global variable, known to all the modules of the application, the ID of a logged-in user. The simplest (but not necessarily the best) solution goes along the following lines: @code // in a "common" module user = nil export user // ... in another module load common > "

    You are ", user // in a "modifying" module load common import user user = "Logged in user" @endcode The @b import directive is here used to say that the @i user variable is @b not declared in the @i modifying module, but rather declared elsewhere and just modified here. @note This example can be seamlessly extended to FTD modules. There isn't any difference between normal falcon scripts and FTD "active pages" from a inter-module communication point of view. @subsection wopi_modules_conf_objects Global objects However, cleaner ways to handle this kind of settings are message programming and object oriented programming. Exporting an object makes it immediately visible to all the loaders, so it's not necessary to force its import. For example: @code // in a "common" module: object User name = nil rights = nil // other values ... end export User // in another module load common > "

    You are ", User.name // and then, in the "modifying module load common User.name = "logged in user" @endcode Through objects, it is possible to create global "entities" that all your application is bound to know and that have global visibility, but encapsulated in a rigid OOP class. @subsection wopi_modules_conf_msgs Application messages Finally, message oriented programming provides the most flexible way to pass data around foreign modules. For example, you may raise an @b assertion when a user logs in, and register callbacks on other modules (or just check for the assertion): @code // in any module... user = nil subscribe( "user", displayUser ) function displayUser( name ) > "

    You are ", name end // in the module checking for the login assert( "user", "I am logged in!" ) // in another module > "You are ", getAssert( "user" ) @endcode And all of this without any need for direct reciprocal @b load or @b import. Of course, as messages are logically asynchronous, if you use message programming constructs to propagate the knowledge about the state of the web-based application, it is necessary to carefully design the flux of the messages so that the modules are loaded only when the assertions are known to be performed, or otherwise to be sure that the output is performed in the expected order. In other words, a callback on global assertion shouldn't generate any output in a web-based application, but communicate (maybe via another assertion) what's the output that it would like to perform. Then, the main script would take care of performing consistent output gathering all the generated pieces and putting them in the correct layout of the final page. This can be easily done, for example, with a "summary" document like the following: @code ...

    Summary for

    ... @endcode or, yet again, it may be possible for the main module to create an object containing the asserted elements, or store them in exported variables. To finely control WHEN to include a submodule, and if the submodule is a FTD script, when to generate its output, the @b include function comes into help. @section wopi_modules_include Including elements Falcon standard @b include function can be used to control the output generated by FTD modules, or by standard modules that write the output from their main code. For example, the following page includes two different modules, a normal Falcon module and a FTD at determined positions in the file: @code

    Inclusion test

    Here we include an FTD:



    And here, a standard falcon module:



    @endcode The @b parent variable may have been created in any other module that this main FTD happened to load. Once loaded, the global exported variables will be presented to the included files as well, so we can access them directly in the following FTD script: @code

    Hello.
    My duty is that of showing you the parent variable:

    @endcode And in the following FAL script: @code // This is inc_fal.fal >> "

    The variable is: ", parent > "

    " @endcode @note It's advisable not to rely on the extension of modules to distinguish between them. For example, in this case we have saved the inc_fal and inc_ftd modules using two different file names. Using the same filename for both would make impossible to load them by name, and may cause confusion when saving the pre-compiled fam file. @section wopi_model_common_index Applications based on a single entry point Many web applications are designed to provide a single entry point, and to load a page or a script depending on a part of the URI (usually, a GET field describing what's the page that should be loaded). This can be achieved checking a field in the @a Request.gets array, and then using @b include to load a determined element, as described in the previous section. The common file (usually, index.fal) has then the ability to prepare the data for the final page or script to handle it, and to setup some visual framing (html headers, page headers and footers, common menus etc.) on its initiative prior loading the final script. For a simple example, see @a falcgi_iso, where this method is described in greater detail. @subsection wopi_model_common_index_db Page database Complicating the things a little, some meta-informations about the pages may be stored in a separate location. For example, it may be possible to associate a page-id to some information about the page. For example: @code // pagedb.fal class Page( title, descr, keywords, inclusor ) title = title descr = descr keywords = keywords inclusor = inclusor end function makeDB() return [ "main" => Page( "Welcome", "This is the main page", "main, page", .[ include pagedb/main.ftd ] ), "second" => Page( "Hello again", "This is the second page", "second, page", .[ include pagedb/second.fal ] ) ] end export @endcode This "pagedb" describes a very simple site. Notice that we didn't limit to name the page that should be loaded in case the given page-id is provided; we put in a whole function (as a callable array). More complex scheme are possible, as, for example, specifying a page to be loaded with an overridable default function. Now, suppose that the main script is something like: @code // index.fal load pagedb // p is the page id if "p" notin Request.gets // default to main Requests.gets["p"] = "main" end // ignore the errors for simplicity page = makeDB()[ Requests.gets["p"] ] // create a header; for simplicity we just use the title field > @"$(page.title)" // now the real page: // an arbitrary common header > "

    Here we start


    " // create the document (see below) page.inclusor() // then the footer > "

    And that was the page

    " @endcode So, we can call directly the inclusor function to fill our page content. In this way, for example, it is possible set some function reading the page content from a database as the page generator, and that would be totally transparent to the main script. @subsection wopi_model_common_index_more Further suggestions There are endless possibilities to create different web-application frameworks, and it would be rather impossible to list them all. However, it is useful to spend a couple of extra words on the possibility to use the @b compiler module in Feathers instead of the @b include function to load the included pages. One of the possibilities that are opened by this option is the ability to read the module attributes prior to executing it's "__main__" symbol. So the page may say something of itself, like ... @code // included.fal // attributes controlling the layout: mysite_title: "Title for this document" mysite_css: "another_non_default_css.css" mysite_keywords: "a, sort, of, page" function setup() // the loader will call this to prepare the page // may be the right place to load some config or get some cookie end function render() // the loader will call this to render the page > "

    Render!

    " > "

    And here we go..." end @endcode Writing an index.ftd or index.fal loading this page via the @b compiler module, checking the module attributes and calling the function it exposes is relatively trivial (and described in the compiler module reference). This kind of dynamic setup may obviate the need for a page database as we have seen in the previous section, as all the dynamic informations on how to setup and render a page can be stored in the page itself. It's centralized vs. distributed approach, and there are pro and cons in both of them, so picking one or the other (or none of them, and going for direct page loading) depends on the structure of your applications and on the results you want to achieve. */ modules/native/wopi/extra_docs/000077500000000000000000000000001176363201700171335ustar00rootroot00000000000000modules/native/wopi/extra_docs/Linbin-README000066400000000000000000000007671176363201700212360ustar00rootroot00000000000000 Falcon - WOPI 1.0 GNU/Linux binary package This binary package contains the WOPI front-ends compiled for LINUX. They are placed in the following locations: sbin/falhttpd - Stand Alone web server. lib/apache2/modules/mod_apache - WOPI module for Apache2. lib/falcon/wopi/cgi-bin/falcgi - WOPI CGI module front-end. lib/falcon/wopi/cgi-bin/ffalcgi - WOPI FastCGI front-end. lib/falcon/cgi_fm.so - WOPI CGI module for scripts. modules/native/wopi/extra_docs/VS8-README.txt000066400000000000000000000022001176363201700212410ustar00rootroot00000000000000 FALCON WOPI 1.0 MS-Windows pre-built files For Falcon built with Visual Studio 8 This package contains 2 executable programs, falhttpd and falcgi, that are respectively the Falcon WOPI stand-alone web server and the CGI front-end. Their usage is explained in the HTML documentation you can find under the docs/ directory. The programs doesn't need to be installed, as long as the Falcon libraries and the Visual Studio Runtime can be readily found (a normal Falcon installation will ensure that). They can be used directly from any location you launch them. Also, the cgi_fm.dll file is a Falcon module that should be copied in the Falcon binary directory (where falcon.exe resides), and that can be loaded by WOPI scripts willing to be used as CGI applications from web servers. More details on configuration and usage of this items is explained in the HTML documentation that you can find under the docs directory. modules/native/wopi/extra_docs/VS9-README.txt000066400000000000000000000022001176363201700212420ustar00rootroot00000000000000 FALCON WOPI 1.0 MS-Windows pre-built files For Falcon built with Visual Studio 9 This package contains 2 executable programs, falhttpd and falcgi, that are respectively the Falcon WOPI stand-alone web server and the CGI front-end. Their usage is explained in the HTML documentation you can find under the docs/ directory. The programs doesn't need to be installed, as long as the Falcon libraries and the Visual Studio Runtime can be readily found (a normal Falcon installation will ensure that). They can be used directly from any location you launch them. Also, the cgi_fm.dll file is a Falcon module that should be copied in the Falcon binary directory (where falcon.exe resides), and that can be loaded by WOPI scripts willing to be used as CGI applications from web servers. More details on configuration and usage of this items is explained in the HTML documentation that you can find under the docs directory. modules/native/wopi/falcgi/000077500000000000000000000000001176363201700162255ustar00rootroot00000000000000modules/native/wopi/falcgi/CMakeLists.txt000066400000000000000000000020451176363201700207660ustar00rootroot00000000000000###################################################################### # CMake file for fcgi # MESSAGE( "Creating the Falcon CGI driver" ) ####################################################################### # Targets # # Inclusion settings include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include ${Falcon_INCLUDE_DIRS} ) # Sources files the library is built from. set(SRC_FILES cgi_options.cpp cgi_reply.cpp cgi_request.cpp falcgi.cpp falcgi_make_streams.cpp falcgi_perform.cpp ) # These are actually not needed by cmake to build. But if omitted they won't be # listed in the virtual file tree of Visual Studio. set(HDR_FILES cgi_make_streams.h cgi_options.h cgi_reply.h cgi_request.h falcgi_perform.h ) ADD_EXECUTABLE( falcgi ${SRC_FILES} ${WOPI_SOURCES} ${HDR_FILES} # optional but useful, see comment above. ) TARGET_LINK_LIBRARIES( falcgi falcon_engine ${SYSLIBS} ) #APR library added in ld flags INSTALL(TARGETS falcgi RUNTIME DESTINATION ${FALCON_WOPI_CGI_INSTALL_DIR} ) modules/native/wopi/falcgi/cgi_make_streams.h000066400000000000000000000013461176363201700216770ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: cgi_make_streams.h Falcon CGI program driver - common declaration for stream provider. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Feb 2010 17:35:14 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef CGI_MAKE_STREAMS_H_ #define CGI_MAKE_STREAMS_H_ #include Falcon::Stream* makeOutputStream(); Falcon::Stream* makeInputStream(); Falcon::Stream* makeErrorStream(); #endif /* CGI_MAKE_STREAMS_H_ */ /* end of cgi_make_streams.h */ modules/native/wopi/falcgi/cgi_options.cpp000066400000000000000000000020331176363201700212440ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: cgi_options.cpp Falcon CGI program driver - Options for the CGI module. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 21 Feb 2010 13:22:44 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "cgi_options.h" #include #include CGIOptions::CGIOptions(): m_smgr( 0 ) { // provide some defaults m_maxUpload = 20000000; m_maxMemUpload = 5000; #ifdef FALCON_SYSTEM_WIN m_sUploadPath = "/C:/TEMP"; #else m_sUploadPath = "/tmp"; #endif } CGIOptions::~CGIOptions() { } bool CGIOptions::init( int argc, char* argv[] ) { if( argc == 0 ) return false; m_sScritpName.bufferize( argv[0] ); Falcon::Path ps( m_sScritpName ); m_sMainScript = ps.getFile(); return true; } /* end of cgi_options.cpp */ modules/native/wopi/falcgi/cgi_options.h000066400000000000000000000017011176363201700207120ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: cgi_options.h Falcon CGI program driver - Options for the CGI module. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 21 Feb 2010 13:22:44 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef CGI_OPTIONS_H_ #define CGI_OPTIONS_H_ #include #include class CGIOptions { public: CGIOptions(); virtual ~CGIOptions(); bool init( int argc, char* argv[] ); Falcon::int64 m_maxUpload; Falcon::int64 m_maxMemUpload; Falcon::String m_sUploadPath; Falcon::String m_sScritpName; Falcon::String m_sMainScript; Falcon::WOPI::SessionManager* m_smgr; }; #endif /* CGI_OPTIONS_H_ */ /* end of cgi_options.h */ modules/native/wopi/falcgi/cgi_reply.cpp000066400000000000000000000025151176363201700207110ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: cgi_reply.cpp Falcon CGI program driver - cgi-based reply specialization. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 21 Feb 2010 12:19:38 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include CGIReply::CGIReply( const Falcon::CoreClass* cls ): Reply( cls ), m_output( 0 ) { } CGIReply::~CGIReply() { } void CGIReply::init( ) { } void CGIReply::startCommit() { Falcon::String sRep; /*sRep.A("HTTP/1.1 ").N( m_nStatus ).A(" ").A(m_sReason); m_output->writeString( sRep + "\r\n" );*/ m_bHeadersSent = true; } Falcon::Stream* CGIReply::makeOutputStream() { m_output = ::makeOutputStream(); return m_output; } void CGIReply::commitHeader( const Falcon::String& name, const Falcon::String& value ) { m_output->writeString( name + ": " + value + "\r\n" ); } void CGIReply::endCommit() { m_output->writeString( "\r\n" ); m_output->flush(); } Falcon::CoreObject* CGIReply::factory( const Falcon::CoreClass* cls, void* , bool ) { return new CGIReply( cls ); } /* end of cgi_reply.cpp */ modules/native/wopi/falcgi/cgi_reply.h000066400000000000000000000021251176363201700203530ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: cgi_reply.h Falcon CGI program driver - cgi-based reply specialization. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 21 Feb 2010 12:19:38 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef CGI_REPLY_H_ #define CGI_REPLY_H_ #include #include class CGIReply: public Falcon::WOPI::Reply { public: CGIReply( const Falcon::CoreClass* cls ); virtual ~CGIReply(); void init(); static Falcon::CoreObject* factory( const Falcon::CoreClass* cls, void* ud, bool bDeser ); protected: virtual void startCommit(); virtual Falcon::Stream* makeOutputStream(); virtual void commitHeader( const Falcon::String& name, const Falcon::String& value ); virtual void endCommit(); private: Falcon::Stream* m_output; }; #endif /* CGI_REPLY_H_ */ /* end of cgi_reply.h */ modules/native/wopi/falcgi/cgi_request.cpp000066400000000000000000000143711176363201700212510ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: cgi_request.cpp Falcon CGI program driver - cgi-based request specialization. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 21 Feb 2010 12:19:38 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "cgi_request.h" #include "cgi_reply.h" #include #include #include #include #include #include #include #include CGIRequest::CGIRequest( const Falcon::CoreClass* cls ): CoreRequest( cls ), m_cration_time(0), m_post_length(0), m_vmOwner(0) { m_provider = "CGI"; } CGIRequest::~CGIRequest() { // Actually, it does nothing if sessions have already been released. if( m_sm != 0 && m_base != 0 ) { m_sm->releaseSessions( m_base->sessionToken() ); } } void CGIRequest::init( Falcon::Stream* input, Falcon::CoreClass* upld_cls, Falcon::WOPI::Reply* r, Falcon::WOPI::SessionManager* sm ) { CoreRequest::init( upld_cls, r, sm ); // First; suck all the environment variables that we need. Falcon::Sys::_enumerateEnvironment( &handleEnvStr, this ); // a bit of post-processing if ( m_base->parsedUri().port() == "443" || m_base->parsedUri().port() == "https" ) { m_base->parsedUri().scheme("https"); } else { m_base->parsedUri().scheme("http"); } m_base->m_request_time = 0; m_base->m_bytes_sent = 0; if ( m_base->m_method == "POST" ) { // on error, proper fields are set. m_base->parseBody( input ); processMultiPartBody(); } } void CGIRequest::handleEnvStr( const Falcon::String& key, const Falcon::String& value, void *data ) { // First; suck all the environment variables that we need. CGIRequest* self = (CGIRequest*) data; Falcon::WOPI::Request* r = self->base(); // Is this an header transformed in an env-var? if( key.startsWith("HTTP_") ) { self->addHeaderFromEnv( key, value ); } else if( key == "AUTH_TYPE" ) { r->m_ap_auth_type = value; } else if( key == "CONTENT_TYPE" ) { self->m_post_type = r->m_content_type = value; r->m_MainPart.addHeader( "Content-Type", value ); } else if( key == "CONTENT_LENGTH" ) { Falcon::int64 tgt; value.parseInt(tgt); r->m_content_length = (int) tgt; self->m_post_length = (int) tgt; } else if( key == "DOCUMENT_ROOT" ) { // .... } else if( key == "GATEWAY_INTERFACE" ) { // .... } else if( key == "PATH_INFO" ) { r->m_path_info = value; } else if ( key == "QUERY_STRING" ) { // it's part of the REQUEST_URI } else if( key == "REMOTE_ADDR" ) { r->m_remote_ip = value; } else if( key == "REMOTE_PORT" ) { //... not implemented } else if( key == "REMOTE_USER" ) { r->m_user = value; } else if( key == "REQUEST_METHOD" ) { r->m_method = value; } else if( key == "REQUEST_URI" ) { r->setURI( value ); } else if( key == "SCRIPT_FILENAME" ) { r->m_filename = value; } else if( key == "SCRIPT_NAME" ) { r->parsedUri().path( value ); } else if( key == "SERVER_ADDR" ) { // .... } else if( key == "SERVER_ADMIN" ) { // .... } else if( key == "SERVER_NAME" ) { r->parsedUri().host( value ); } else if( key == "SERVER_PORT" ) { r->parsedUri().port( value ); } else if( key == "SERVER_PROTOCOL" ) { r->m_protocol = value; } else if( key == "SERVER_SIGNATURE" ) { // .... } else if( key == "SERVER_SOFTWARE" ) { // .... } } void CGIRequest::addHeaderFromEnv( const Falcon::String& key, const Falcon::String& value ) { // discard "http_" Falcon::String sKey( key, 5 ); // ... and transform the rest in "Content-Type" format bool bUpper = true; Falcon::uint32 len = sKey.length(); for( Falcon::uint32 i = 0; i < len; ++i ) { if( sKey[i] == '_' ) { sKey.setCharAt(i, '-'); bUpper = true; } else if ( bUpper ) { sKey.setCharAt(i, toupper( sKey[i] ) ); bUpper = false; } else { sKey.setCharAt(i, tolower( sKey[i] ) ); } } // Ok, we can now add the thing to the dict m_base->headers()->put( new Falcon::CoreString( sKey ), new Falcon::CoreString( value ) ); if ( key == "HTTP_COOKIE" ) { Falcon::uint32 pos = 0; Falcon::uint32 pos1 = value.find(";"); while( true ) { Falcon::WOPI::Utils::parseQueryEntry( value.subString(pos,pos1), m_base->cookies()->items() ); if( pos1 == Falcon::String::npos ) break; pos = pos1+1; pos1 = value.find(";", pos ); } } else if ( key == "HTTP_CONTENT_TYPE" ) { m_base->m_content_type = value; } else if ( key == "HTTP_CONTENT_ENCODING" ) { m_base->m_content_encoding = value; } } void CGIRequest::PostInitPrepare( Falcon::VMachine* vmowner ) { m_bPostInit = true; m_vmOwner = vmowner; } void CGIRequest::postInit() { // ok, we can now pass to init. Falcon::Item* i_upld = m_vmOwner->findGlobalItem( "Uploaded" ); fassert( i_upld != 0 ); Falcon::Item* i_reply = m_vmOwner->findGlobalItem( "Reply" ); fassert( i_reply != 0 ); // configure using the main script Falcon::LiveModule* ms = m_vmOwner->mainModule(); Falcon::WOPI::FileSessionManager* fsm = new Falcon::WOPI::FileSessionManager(""); if( ms != 0 ) { fsm->configFromModule( ms->module() ); if( fsm->timeout() == 0 ) fsm->timeout(600); fsm->startup(); } CGIRequest::init( m_vmOwner->stdIn(), i_upld->asClass(), Falcon::dyncast(i_reply->asObject()), fsm ); if( ms != 0 ) { configFromModule( ms->module() ); } m_base->startedAt( m_cration_time ); } Falcon::CoreObject* CGIRequest::factory( const Falcon::CoreClass* cls, void* , bool ) { return new CGIRequest( cls ); } /* end of cgi_request.cpp */ modules/native/wopi/falcgi/cgi_request.h000066400000000000000000000030261176363201700207110ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: cgi_request.h Falcon CGI program driver - cgi-based request specialization. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 21 Feb 2010 12:19:38 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef CGI_REQUEST_H_ #define CGI_REQUEST_H_ #include class CGIRequest: public Falcon::WOPI::CoreRequest { public: CGIRequest( const Falcon::CoreClass* cls ); virtual ~CGIRequest(); void init( Falcon::Stream* input, Falcon::CoreClass* upld_cls, Falcon::WOPI::Reply* r, Falcon::WOPI::SessionManager* sm ); static Falcon::CoreObject* factory( const Falcon::CoreClass* cls, void* ud, bool bDeser ); void PostInitPrepare( Falcon::VMachine* vmowner ); virtual void postInit(); Falcon::numeric m_cration_time; private: static void handleEnvStr( const Falcon::String& key, const Falcon::String& value, void *data ); void addHeaderFromEnv( const Falcon::String& key, const Falcon::String& value ); void readPostFields( Falcon::Stream* input ); void readMultipartFields( Falcon::Stream* input ); bool readSinglePart( const Falcon::String& sBoundary, Falcon::Stream* input ); int m_post_length; Falcon::String m_post_type; Falcon::VMachine* m_vmOwner; }; #endif /* CGI_REQUEST_H_ */ /* end of cgi_request.h */ modules/native/wopi/falcgi/falcgi.cpp000066400000000000000000000033731176363201700201640ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falcgi.cpp Falcon CGI program driver - main file. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Feb 2010 17:35:14 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include "cgi_options.h" #include "cgi_request.h" #include "cgi_reply.h" #include "falcgi_perform.h" static void report_temp_file_error( const Falcon::String& fileName, void* data ) { Falcon::Stream* serr = (Falcon::Stream*) data; serr->writeString( "ERROR: Cannot remove temp file " + fileName +"\r\n"); serr->flush(); } int main( int argc, char* argv[] ) { // we need to re-randomize based on our pid + time, // or we may end up creating the same temp files. Falcon::WOPI::Utils::xrandomize(); // start the engine Falcon::Engine::Init(); CGIOptions cgiopt; if ( cgiopt.init( argc, argv ) ) { void *tempFileList = perform( cgiopt, argc, argv ); // perform complete GC to reset open states (i.e. open file handles). Falcon::memPool->performGC(); // Free the temp files if( tempFileList != 0 ) { Falcon::Stream* errstream = new Falcon::TranscoderUTF8( Falcon::stdErrorStream() ); Falcon::WOPI::Request::removeTempFiles( tempFileList, errstream, report_temp_file_error ); delete errstream; } } // clear the manager delete cgiopt.m_smgr; // engine down. Falcon::Engine::Shutdown(); return 0; } modules/native/wopi/falcgi/falcgi_make_streams.cpp000066400000000000000000000014771176363201700227220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falcgi_make_streams.cpp Falcon CGI program driver - CGI specific stream provider. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Feb 2010 17:35:14 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include Falcon::Stream* makeOutputStream() { return new Falcon::StdOutStream; } Falcon::Stream* makeInputStream() { return new Falcon::StdInStream; } Falcon::Stream* makeErrorStream() { return Falcon::stdErrorStream(); } /* end of falcgi_make_streams.cpp */ modules/native/wopi/falcgi/falcgi_perform.cpp000066400000000000000000000113001176363201700217030ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: cgi_perform.cpp Falcon CGI program driver - part running a single script. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Feb 2010 17:35:14 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include #include #include #include #include void configure_loader( CGIOptions& opts, Falcon::ModuleLoader& ml ) { // TODO: use options Falcon::String lp; ml.addFalconPath(); if( Falcon::Sys::_getEnv( "FALCON_LOAD_PATH", lp ) && lp != "" ) { ml.addSearchPath( lp ); } Falcon::Engine::setSearchPath( ml.getSearchPath() ); ml.sourceEncoding("utf-8"); Falcon::Engine::setEncodings( "utf-8", "utf-8" ); } void* perform( CGIOptions& options, int argc, char* argv[] ) { double nStartedAt = Falcon::Sys::_seconds(); // setup the virtual machine Falcon::VMachineWrapper vm; Falcon::ModuleLoader ml("."); configure_loader( options, ml ); Falcon::Stream* input = makeInputStream(); // ok, we're in the business. // Prepare the class vm->link( Falcon::core_module_init() ); vm->link( Falcon::WOPI::wopi_module_init(CGIRequest::factory, CGIReply::factory ) ); Falcon::Item* i_req = vm->findGlobalItem( "Request" ); fassert( i_req != 0 ); Falcon::Item* i_upld = vm->findGlobalItem( "Uploaded" ); fassert( i_upld != 0 ); Falcon::Item* i_reply = vm->findGlobalItem( "Reply" ); fassert( i_reply != 0 ); Falcon::Item* i_wopi = vm->findGlobalItem( "Wopi" ); fassert( i_wopi != 0 ); CGIRequest* request = Falcon::dyncast( i_req->asObject() ); Falcon::WOPI::Request *base = 0; Falcon::uint32 nSessionToken = 0; // And the reply here CGIReply* reply = Falcon::dyncast( i_reply->asObject() ); reply->init(); vm->stdOut( new Falcon::WOPI::ReplyStream( reply ) ); vm->stdErr( new Falcon::WOPI::ReplyStream( reply ) ); // change the input (however, it's useless) -- but so the vm takes care of freeing it. vm->stdIn( input ); vm->appSearchPath( ml.getSearchPath() ); // From here, we may start to do wrong. try { Falcon::Module* main = ml.loadName(options.m_sMainScript); bool cfgSessionManager = false;; if( options.m_smgr == 0 ) { cfgSessionManager = true; options.m_smgr = new Falcon::WOPI::FileSessionManager( "" ); } // init must be called before the module can be configured. request->init( input, i_upld->asClass(), reply, options.m_smgr ); // as it creates the base. base = request->base(); base->startedAt( nStartedAt ); base->setMaxMemUpload( options.m_maxMemUpload ); if( options.m_sUploadPath != "" ) base->setUploadPath( options.m_sUploadPath ); nSessionToken = base->sessionToken(); request->configFromModule( main ); Falcon::dyncast(i_wopi->asObject())->configFromModule( main ); if( cfgSessionManager ) { options.m_smgr->configFromModule( main ); if( options.m_smgr->timeout() == 0 ) options.m_smgr->timeout(600); options.m_smgr->startup(); } Falcon::Path mainPath( options.m_sMainScript ); // try to change, but ignore failure if ( mainPath.getFullLocation().size() != 0) { Falcon::int32 chdirError; Falcon::Sys::fal_chdir( mainPath.getFullLocation(), chdirError ); } // having parsed the request, we can start. Falcon::Runtime rt( &ml ); rt.addModule( main ); vm->link( &rt ); vm->launch(); } catch( Falcon::Error* e ) { Falcon::Stream* err = Falcon::stdErrorStream(); // Write to the log ... err->writeString("FALCON ERROR:\r\n" + e->toString() ); err->flush(); reply->commit(); //delete err; // ...and to the document vm->stdOut()->writeString("FALCON ERROR:\r\n" + e->toString()+"\r\n" ); e->decref(); } vm->stdOut()->close(); vm->stdErr()->close(); if( options.m_smgr != 0 && nSessionToken != 0 ) { options.m_smgr->releaseSessions( nSessionToken ); } return base != 0 ? base->getTempFiles() : 0; // here the VM is destroyed. } /* end of cgi_perform.cpp */ modules/native/wopi/falcgi/falcgi_perform.h000066400000000000000000000011731176363201700213570ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: cgi_perform.h Falcon CGI program driver - part running a single script. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Feb 2010 17:35:14 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef CGI_PERFORM_H_ #define CGI_PERFORM_H_ void* perform( CGIOptions& options, int argc, char* argv[] ); #endif /* CGI_PERFORM_H_ */ /* end of cgi_perform.h */ modules/native/wopi/falhttpd/000077500000000000000000000000001176363201700166065ustar00rootroot00000000000000modules/native/wopi/falhttpd/CMakeLists.txt000066400000000000000000000030331176363201700213450ustar00rootroot00000000000000###################################################################### # CMake file for fcgi # MESSAGE( "Creating the Falcon micro HTTP Daemon" ) ####################################################################### # Targets # # Inclusion settings include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include ${Falcon_INCLUDE_DIRS} ) # Sources files the library is built from. set(SRC_FILES falhttpd_client.cpp falhttpd.cpp falhttpd_dirhandler.cpp falhttpd_filehandler.cpp falhttpd_istream.cpp falhttpd_options.cpp falhttpd_ostream.cpp falhttpd_reply.cpp falhttpd_rh.cpp falhttpd_scripthandler.cpp ) # These are actually not needed by cmake to build. But if omitted they won't be # listed in the virtual file tree of Visual Studio. set(HDR_FILES falhttpd_client.h falhttpd_dirhandler.h falhttpd_filehandler.h falhttpd.h falhttpd_istream.h falhttpd_options.h falhttpd_ostream.h falhttpd_reply.h falhttpd_rh.h falhttpd_scripthandler.h ${WOPI_HEADERS} ) ADD_EXECUTABLE( falhttpd ${SRC_FILES} ${WOPI_SOURCES} ${HDR_FILES} # optional but useful, see comment above. ) if(UNIX OR MAC) set(SYS_SPECIFIC socket_sys_unix.cpp) # On Solaris, we need also socket lib if( "${CMAKE_SYSTEM_NAME}" STREQUAL "SunOS" ) set(_syslibs /lib/libsocket.so /lib/libnsl.so ) endif() else() set(_syslibs ws2_32 ) endif() TARGET_LINK_LIBRARIES( falhttpd falcon_engine ${_syslibs} ) INSTALL(TARGETS falhttpd RUNTIME DESTINATION bin ) modules/native/wopi/falhttpd/falhttpd.cpp000066400000000000000000000202031176363201700211150ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd.cpp Micro HTTPD server providing Falcon scripts on the web. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 23 Feb 2010 22:09:02 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "falhttpd.h" #include "falhttpd_client.h" #include "falhttpd_reply.h" #include #include #include #include #include #ifndef FALCON_SYSTEM_WIN #include #include /* inet_ntoa() to format IP address */ #include /* in_addr structure */ #include #else #include #define socklen_t int #endif FalhttpdApp* FalhttpdApp::m_theApp = 0; FalhttpdApp::FalhttpdApp(): m_logModule(0), m_log(0) { if ( m_theApp != 0 ) { throw std::runtime_error( "FalhttpdApp is not singleton"); } m_theApp = this; // start the engine Falcon::Engine::Init(); Falcon::Engine::cacheModules( true ); // let's create also a useful general module loader. m_loader = new Falcon::ModuleLoader("."); m_loader->addFalconPath(); Falcon::String io_encoding; m_ss = Falcon::stdOutputStream(); m_coreModule = Falcon::core_module_init(); m_wopiModule = Falcon::WOPI::wopi_module_init( &WOPI::CoreRequest::factory, &FalhttpdReply::factory ); } FalhttpdApp::~FalhttpdApp() { if ( m_log != 0 ) { // we did get initialized m_log->log(LOGLEVEL_INFO, "Log closed." ); m_log->decref(); } if( m_logModule != 0 ) { m_logModule->decref(); } m_wopiModule->decref(); m_coreModule->decref(); delete m_ss; if( m_nSocket != 0 ) { #ifdef _WIN32 closesocket( m_nSocket ); WSACleanup(); #else ::close( m_nSocket ); #endif } Falcon::Engine::Shutdown(); } bool FalhttpdApp::init( int argc, char* argv[] ) { // configure command line if( ! m_hopts.init( argc, argv ) ) { usage(); return false; } if( m_hopts.m_bHelp ) { usage(); } if( ! readyServices() ) return false; if( m_hopts.m_loadPath.size() ) { m_loader->setSearchPath( m_hopts.m_loadPath ); } return readyNet(); } bool FalhttpdApp::readyServices() { // we'll be throwing an error if not available. m_logModule = m_loader->loadName("logging"); Falcon::LogService* ls = (Falcon::LogService*) m_logModule->getService(LOGSERVICE_NAME); fassert( ls != 0 ); // create our application log area. m_log = ls->makeLogArea( "HTTP" ); readyLog( ls ); m_log->log( LOGLEVEL_INFO, "Falcon HTTPD server started..." ); if( m_hopts.m_configFile.size() != 0 ) { // Ok, time to get the config (we don't need to cache it) Falcon::Module* cfgmod = m_loader->loadName( "confparser" ); Falcon::ConfigFileService* cfs = (Falcon::ConfigFileService*) cfgmod->getService( CONFIGFILESERVICE_NAME ); fassert( cfs != 0 ); if( ! cfs->initialize( m_hopts.m_configFile, "utf-8" ) || ! cfs->load() ) { m_log->log( LOGLEVEL_WARN, "Cannot read init file " + m_hopts.m_configFile + ": " + cfs->errorMessage() ); } else { m_hopts.loadIni( m_log, cfs ); } // Not working because of static destructor. We need to upgrate core. cfs->clearMainSection(); cfgmod->decref(); } return true; } bool FalhttpdApp::readyNet() { struct sockaddr_in sa; #ifdef _WIN32 WORD wVersionRequested = MAKEWORD(2, 1); WSADATA WSAData; if( WSAStartup( wVersionRequested, &WSAData ) != 0 ) { m_hopts.m_sErrorDesc = "Cannot initialize Winsock 2.1"; return false; } #endif ::memset(&sa, 0, sizeof(struct sockaddr_in) ); Falcon::AutoCString myname( m_hopts.m_sIface ); sa.sin_family = AF_INET; sa.sin_addr.s_addr = inet_addr( myname.c_str() ); sa.sin_port = htons( m_hopts.m_nPort ); if (( m_nSocket = ::socket( sa.sin_family, SOCK_STREAM, 0) ) < 0) { m_hopts.m_sErrorDesc = "Cannot create the listening socket ("; m_hopts.m_sErrorDesc.N( (Falcon::int64) m_nSocket ).A(")"); m_nSocket = 0; return false; } int val = 1; ::setsockopt(m_nSocket, SOL_SOCKET, SO_REUSEADDR, (const char*) &val, sizeof(val)); if ( ::bind( m_nSocket, (struct sockaddr*) &sa, sizeof(struct sockaddr_in) ) < 0) { m_hopts.m_sErrorDesc = "Can't bind to local address " + m_hopts.m_sIface + ":"; m_hopts.m_sErrorDesc.N( m_hopts.m_nPort ); return false; } ::listen( m_nSocket, 10 ); return true; } void FalhttpdApp::readyLog( Falcon::LogService* ls ) { // and a console channel logging everything up to info level if( ! m_hopts.m_bQuiet ) { Falcon::LogChannel* chnConsole = ls->makeChnStream( Falcon::stdOutputStream(), "[%S %L|%a] %m", m_hopts.m_logLevel ); m_log->addChannel( chnConsole ); chnConsole->decref(); } if( m_hopts.m_bSysLog ) { Falcon::LogChannel* chnSyslog = ls->makeChnSyslog( "FHTTPD", "[%S %L|%a] %m", 0, m_hopts.m_logLevel ); m_log->addChannel( chnSyslog ); chnSyslog->decref(); } if( m_hopts.m_sLogFiles.size() != 0 ) { // on files, log everything Falcon::LogChannel* chnStream = ls->makeChnlFiles( m_hopts.m_sLogFiles ); m_log->addChannel( chnStream ); chnStream->decref(); } } void FalhttpdApp::usage() { m_ss->writeString( " Usage: \n" " falhttpd [options]\n\n" " Options:\n" " -? This help\n" " -A

    Directory where to place persistent application data.\n" " -C Load this configuration file\n" " -D Log debug level informations to the given file or path\n" " -h Sets this directory as site HTDOCS root\n" " -i Listen on the named interface (defaults to 0.0.0.0 - all)\n" " -l Log level (0 min, 5 max)\n" " -L Set this as the falcon load path\n" " -p Listen on required port (deaults to 80)\n" " -q Be quiet (don't log on console)\n" " -S Do not log on syslog\n" " -t Set session timeout (defaults to 30)\n" " -T Use this as temporary path\n\n" ); } int FalhttpdApp::run() { // accept. while( true ) { sockaddr_in inAddr; socklen_t inLen = sizeof(inAddr); SOCKET sIncoming = ::accept( m_nSocket, (struct sockaddr*) &inAddr, &inLen ); logi( "Incoming client" ); if( sIncoming == 0 ) { // we're done break; } char host[256]; char serv[32]; Falcon::String sRemote; int error = ::getnameinfo( (struct sockaddr*) &inAddr, inLen, host, 255, serv, 31, NI_NUMERICHOST | NI_NUMERICSERV ); if( error == 0 ) { sRemote = host; sRemote += ":"; sRemote += serv; } else { sRemote = "unknown"; } FalhttpdClient* cli = new FalhttpdClient( m_hopts, m_log, sIncoming, sRemote ); cli->serve(); delete cli; } return 0; } //============================================================================= // int main( int argc, char* argv[] ) { FalhttpdApp theApp; try { if( theApp.init( argc, argv ) ) { return theApp.run(); } else { theApp.m_ss->writeString( Falcon::String( "falhttpd: Cannot intialize application.\n") + Falcon::String( "falhttpd: ") + theApp.m_hopts.m_sErrorDesc +"\n\n" ); } } catch( Falcon::Error* e ) { // Run catches its own error. Errors here can happen only during initialization theApp.m_ss->writeString( "Error during initialization: \n" ); theApp.m_ss->writeString( e->toString() + "\n" ); theApp.m_ss->flush(); e->decref(); } return -1; } /* end of falhttpd.cpp */ modules/native/wopi/falhttpd/falhttpd.h000066400000000000000000000037371176363201700205770ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd.h Micro HTTPD server providing Falcon scripts on the web. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 23 Feb 2010 22:09:02 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_HTTPD_SERVER_H_ #define FALCON_HTTPD_SERVER_H_ #ifdef _WIN32 #define _WIN32_WINNT 0x0403 #include #include #else #include #define SOCKET int #endif #include #include "falhttpd_options.h" #include #include class FalhttpdApp { public: FalhttpdApp(); ~FalhttpdApp(); bool init( int argc, char* argv[] ); bool readyServices(); bool readyNet(); void usage(); int run(); inline void logf( const Falcon::String& l ) { m_log->log( LOGLEVEL_FATAL, l ); } inline void loge( const Falcon::String& l ) { m_log->log( LOGLEVEL_ERROR, l ); } inline void logw( const Falcon::String& l ) { m_log->log( LOGLEVEL_WARN, l ); } inline void logi( const Falcon::String& l ) { m_log->log( LOGLEVEL_INFO, l ); } inline void logd( const Falcon::String& l ) { m_log->log( LOGLEVEL_DEBUG, l ); } Falcon::Module* core() const { return m_coreModule; } Falcon::Module* wopi() const { return m_wopiModule; } Falcon::ModuleLoader* loader() const { return m_loader; } //! Sevice stream Falcon::Stream* m_ss; FalhttpOptions m_hopts; static FalhttpdApp* get() { return m_theApp; } private: void readyLog( Falcon::LogService* ); Falcon::Module* m_logModule; Falcon::LogArea* m_log; Falcon::ModuleLoader* m_loader; Falcon::Module* m_coreModule; Falcon::Module* m_wopiModule; SOCKET m_nSocket; static FalhttpdApp* m_theApp; }; #endif /* end of falhttpd.h */ modules/native/wopi/falhttpd/falhttpd.ini000066400000000000000000000012501176363201700211130ustar00rootroot00000000000000;===================================== ; SAMPLE Initialization parameters for ; falcon mircro httpd server ; Sets the temporary directory TempDir= /tmp ; Listening on this port Port Port=8080 ; Load path for Falcon LoadPath=. ; place where the site is located (relative to start directory) HomeDir = public_html/ ; Where to store peristent data ; Disable to store persistend data in memory ; PersistentDataDir = ;============================ ; Mime mapping configuration ; mime.text.html= "*.html;*.htm" mime.text.css= *.css mime.text.javascript= *.js mime.image.jpeg= "*.jpg;*.jpeg" mime.image.png= *.png mime.image.tiff= *.tiff mime.image.gif= *.gif mime.text.plain=* modules/native/wopi/falhttpd/falhttpd_client.cpp000066400000000000000000000211671176363201700224650ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_client.cpp Micro HTTPD server providing Falcon scripts on the web. Servicing single client. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 24 Feb 2010 20:10:45 +0100 s ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #define MAX_HEADER_SIZE 100000 #include "falhttpd.h" #include "falhttpd_client.h" #include "falhttpd_istream.h" #include "falhttpd_rh.h" #ifndef _WIN32 #include #include /* inet_ntoa() to format IP address */ #include /* in_addr structure */ #include #else // #pragma comment(lib, "wininet.lib") #endif #include #include #include #include FalhttpdClient::FalhttpdClient( const FalhttpOptions& options, LogArea* l, SOCKET s, const String& remName ): m_log( l ), m_nSocket( s ), m_sRemote( remName ), m_cBuffer( 0 ), m_sSize( 0 ), m_bComplete( false ), m_options( options ) { m_log->log( LOGLEVEL_INFO, "Incoming client from "+ m_sRemote ); m_cBuffer = (char*) memAlloc( DEFAULT_BUFFER_SIZE ); m_pSessionManager = options.m_pSessionManager; } FalhttpdClient::~FalhttpdClient() { close(); } void FalhttpdClient::close() { if( m_nSocket != 0 ) { #ifdef FALCON_SYSTEM_WIN ::shutdown( m_nSocket, SD_BOTH ); ::closesocket( m_nSocket ); #else ::shutdown( m_nSocket, SHUT_RDWR ); ::close( m_nSocket ); #endif m_log->log( LOGLEVEL_INFO, "Client "+ m_sRemote + " gone" ); m_nSocket = 0; } } void FalhttpdClient::serve() { m_log->log( LOGLEVEL_DEBUG, "Serving client "+ m_sRemote ); // get the header String sHeader; StreamBuffer si( new SocketInputStream( m_nSocket ) ); uint32 chr; while ( ! sHeader.endsWith("\r\n") && si.get(chr) ) { // do nothing sHeader.append( chr ); } if ( ! sHeader.endsWith("\r\n") ) { m_log->log( LOGLEVEL_WARN, "Client "+ m_sRemote + " has sent an invalid header." ); return; } // remove trailing \r\n sHeader.remove( sHeader.length()-2, 2 ); // parse the header; must be in format GET/POST/HEAD 'uri' HTTP/1.x uint32 p1, p2; p1 = sHeader.find( " " ); p2 = sHeader.rfind( " " ); if ( p1 == p2 ) { sHeader.trim(); m_log->log( LOGLEVEL_WARN, "Client "+ m_sRemote + " has sent an invalid header: " + sHeader ); return; } String sRequest = sHeader.subString( 0, p1 ); String sUri = sHeader.subString( p1+1, p2 ); String sProto = sHeader.subString( p2+1 ); // a bit of formal control int type = sRequest == "GET" ? 1 : ( sRequest == "POST" ? 2 : ( sRequest == "HEAD" ? 3 : 0 ) ); if ( type == 0 ) { m_log->log( LOGLEVEL_WARN, "Client "+ m_sRemote + " has sent an invalid type: " + sHeader ); replyError( 400, "Invalid requests type" ); return; } URI uri( sUri ); if( ! uri.isValid() ) { m_log->log( LOGLEVEL_WARN, "Client "+ m_sRemote + " has sent an invalid URI: " + sHeader ); replyError( 400, "Invalid invalid uri" ); return; } if( sProto != "HTTP/1.0" && sProto != "HTTP/1.1" ) { m_log->log( LOGLEVEL_WARN, "Client "+ m_sRemote + " has sent an invalid Protocol: " + sHeader ); replyError( 505 ); return; } // ok, we got a valid header -- proceed in serving the request. serveRequest( sRequest, sUri, sProto, &si ); } void FalhttpdClient::serveRequest( const String& sRequest, const String& sUri, const String& sProto, Stream* si ) { m_log->log( LOGLEVEL_INFO, "Serving request from "+ m_sRemote + ": " + sRequest + " " + sUri + " " + sProto ); // first, read the headers. WOPI::Request* req = new WOPI::Request; req->startedAt( Sys::_seconds() ); if( ! req->parse( si ) ) { replyError( 400, req->partHandler().error() ); delete req; return; } req->setURI( sUri ); req->m_remote_ip = m_sRemote; String sFile = req->parsedUri().path(); m_log->log( LOGLEVEL_DEBUG, "Remapping file "+ sFile ); // Time to re-map the file if ( ! m_options.remap( sFile ) ) { m_log->log( LOGLEVEL_WARN, "Not found file "+ sFile ); replyError( 404 ); } else { m_log->log( LOGLEVEL_DEBUG, "File remapped as "+ sFile ); req->m_filename = sFile; // and finally process the request through the appropriate request handler. FalhttpdRequestHandler* rh = m_options.getHandler( sFile, this ); rh->serve( req ); delete rh; } delete req; m_log->log( LOGLEVEL_INFO, "Served client "+ m_sRemote ); } void FalhttpdClient::replyError( int errorID, const String& explain ) { String sErrorDesc = codeDesc( errorID ); String sReply; String sError; sReply.A("HTTP/1.1 ").N( errorID ).A( " " + sErrorDesc + "\r\n"); sError.N(errorID ).A( ": " + sErrorDesc ); // for now, we create the docuemnt here. // TODO Read the error document from a file. String sErrorDoc = "\n" "\r\n" "Error " +sError + "\n" "\n" "\n" "

    " + sError + "

    \n"; if( explain.size() != 0 ) { sErrorDoc += "

    This abnormal condition has been encountered while receiving and processing Your request:

    \n"; sErrorDoc += "

    " + explain + "

    \n"; } else { sErrorDoc += "

    An error of type " + sError + " has been detected while parsing your request.

    \n"; } sErrorDoc += getServerSignature(); sErrorDoc += "\n\n"; AutoCString content( sErrorDoc ); TimeStamp now; now.currentTime(); sReply += "Date: " + now.toRFC2822() + "\r\n"; sReply.A( "Content-Length: ").N( (int64) content.length() ).A("\r\n"); sReply += "Content-Type: text/html; charset=utf-8\r\n\r\n"; m_log->log( LOGLEVEL_INFO, "Sending ERROR reply to client " + m_sRemote + ": " + sError ); sendData( sReply ); sendData( content.c_str(), content.length() ); } String FalhttpdClient::getServerSignature() { //TODO Make this configurable String sString = "

    \n

    Falcon HTTPD simple server.

    \n"; return sString; } String FalhttpdClient::codeDesc( int errorID ) { switch( errorID ) { // continue codes case 100: return "Continue"; case 101: return "Switching Protocols"; // Success codes case 200: return "OK"; case 201: return "Created"; case 202: return "Accepted"; case 203: return "Non-Authoritative Information"; case 204: return "No Content"; case 205: return "Reset Content"; case 206: return "Partial Content"; // Redirection Codes case 300: return "Multiple Choices"; case 301: return "Moved Permanently"; case 302: return "Found"; case 303: return "See Other"; case 304: return "Not Modified"; case 305: return "Use Proxy"; case 307: return "Temporary Redirect"; // Client Error Codes case 400: return "Bad Request"; case 401: return "Unauthorized"; case 402: return "Payment Required"; case 403: return "Forbidden"; case 404: return "Not Found"; case 405: return "Method Not Allowed"; case 406: return "Not Acceptable"; case 407: return "Proxy Authentication Required"; case 408: return "Request Timeout"; case 409: return "Conflict"; case 410: return "Gone"; case 411: return "Length Required"; case 412: return "Precondition Failed"; case 413: return "Request Entity Too Large"; case 414: return "Request-URI Too Large"; case 415: return "Unsupported Media Type"; case 416: return "Requested Range Not Satisfiable"; case 417: return "Expectation Failed"; // Server Error Codes case 500: return "Internal Server Error"; case 501: return "Not Implemented"; case 502: return "Bad Gateway"; case 503: return "Service Unavailable"; case 504: return "Gateway Timeout"; case 505: return "HTTP Version not supported"; } return "Unknown code"; } void FalhttpdClient::sendData( const String& sReply ) { sendData( sReply.getRawStorage(), sReply.size() ); } void FalhttpdClient::sendData( const void* data, uint32 size ) { uint32 sent = 0; int res = 0; const char* bdata = (const char* ) data; while( sent < size ) { res = ::send( m_nSocket, bdata + sent, size - sent, 0 ); if( res < 0 ) { m_log->log( LOGLEVEL_WARN, "Client "+ m_sRemote + " had a send error." ); return; } sent += res; } } /* falhttpd_socket.cpp */ modules/native/wopi/falhttpd/falhttpd_client.h000066400000000000000000000033301176363201700221220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_client.cpp Micro HTTPD server providing Falcon scripts on the web. Servicing single client. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 24 Feb 2010 20:10:45 +0100 s ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALHTTPD_CLIENT_H_ #define FALHTTPD_CLIENT_H_ #include "falhttpd.h" #include #include #define DEFAULT_BUFFER_SIZE 4096 using namespace Falcon; class FalhttpdClient { public: FalhttpdClient( const FalhttpOptions& opts, LogArea* l, SOCKET s, const String& remName ); ~FalhttpdClient(); void close(); void serve(); void replyError( int errorID, const String& explain="" ); String codeDesc( int errorID ); String getServerSignature(); void sendData( const String& sReply ); void sendData( const void* data, uint32 size ); const FalhttpOptions& options() const { return m_options; } Falcon::LogArea* log() const { return m_log; } SOCKET socket() const { return m_nSocket; } Falcon::WOPI::SessionManager* smgr() const { return m_pSessionManager; } private: void serveRequest( const String& sRequest, const String& sUri, const String& sProto, Stream* si ); LogArea* m_log; SOCKET m_nSocket; String m_sRemote; char* m_cBuffer; uint32 m_sSize; bool m_bComplete; const FalhttpOptions& m_options; Falcon::WOPI::SessionManager* m_pSessionManager; }; #endif /* FALHTTPD_CLIENT_H_ */ /* falhttpd_client.h */ modules/native/wopi/falhttpd/falhttpd_dirhandler.cpp000066400000000000000000000044341176363201700233210ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_dirhandler.cpp Micro HTTPD server providing Falcon scripts on the web. Request handler processing access to directories. Mimetype is text/html; charset=utf-8 ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Mar 2010 12:16:03 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "falhttpd.h" #include "falhttpd_dirhandler.h" #include "falhttpd_client.h" #include DirHandler::DirHandler( const Falcon::String& sFile, FalhttpdClient* cli ): FalhttpdRequestHandler( sFile, cli ) { } DirHandler::~DirHandler() { } void DirHandler::serve( Falcon::WOPI::Request* req ) { // open the directory Falcon::int32 error; Falcon::DirEntry *de = Falcon::Sys::fal_openDir( m_sFile, error ); if( de == 0 ) { m_client->log()->log( LOGLEVEL_WARN, "Can't open directory " + m_sFile ); m_client->replyError( 403 ); return; } Falcon::String thisDir = m_sFile.subString( m_client->options().m_homedir.length() ); if( thisDir.endsWith("/") && thisDir.length() > 1 ) { thisDir.remove(thisDir.length()-1, 1); } Falcon::String title = "Index of "+ thisDir + "" "\n

    Index of "+ thisDir + "

    \n

    "; m_client->sendData( title ); Falcon::String fname; while ( de->read(fname) ) { if ( fname == "." ) continue; Falcon::String loc; if( fname == ".." ) { if( thisDir == "/" ) continue; Falcon::uint32 pos = thisDir.rfind("/"); if ( pos == Falcon::String::npos ) loc = "/"; else loc = thisDir.subString(0,pos) + "/"; } else if (thisDir != "/" ) { loc = thisDir +"/"; } else { loc = thisDir; } Falcon::String entry = "
    " + fname + "\n"; m_client->sendData( entry ); } m_client->sendData( "\n\n" ); delete de; } /* end of falhttpd_dirhandler.cpp */ modules/native/wopi/falhttpd/falhttpd_dirhandler.h000066400000000000000000000016431176363201700227650ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_scripthandler.h Micro HTTPD server providing Falcon scripts on the web. Request handler processing access to directories. Mimetype is text/html; charset=utf-8 ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Mar 2010 12:18:29 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALHTTPD_DIRHANDLER_H_ #define FALHTTPD_DIRHANDLER_H_ #include "falhttpd.h" #include "falhttpd_rh.h" class DirHandler: public FalhttpdRequestHandler { public: DirHandler( const Falcon::String& sFile, FalhttpdClient* client ); virtual ~DirHandler(); virtual void serve( Falcon::WOPI::Request* req ); }; #endif /* falhttpd_dirhandler.h */ modules/native/wopi/falhttpd/falhttpd_filehandler.cpp000066400000000000000000000043411176363201700234570ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_filehandler.cpp Micro HTTPD server providing Falcon scripts on the web. Handler for requests of files. This also determines the MIME type of served files. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Mar 2010 12:16:03 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "falhttpd_rh.h" #include "falhttpd_filehandler.h" #include "falhttpd_client.h" FileHandler::FileHandler( const Falcon::String& sFile, FalhttpdClient* cli ): FalhttpdRequestHandler( sFile, cli ) { } FileHandler::~FileHandler() { } void FileHandler::serve( Falcon::WOPI::Request* req ) { String sMimeType; // Get the document -- for now a very simple thing if ( ! m_client->options().findMimeType( m_sFile, sMimeType ) ) sMimeType = "unknown"; // Send the file Falcon::FileStat stats; if( ! Falcon::Sys::fal_stats( m_sFile, stats ) ) { m_client->replyError( 403 ); return; } FileStream fs; if( ! fs.open( m_sFile ) ) { m_client->log()->log( LOGLEVEL_WARN, "Can't open file "+ m_sFile ); m_client->replyError( 403 ); return; } m_client->log()->log( LOGLEVEL_INFO, "Sending file "+ m_sFile ); // ok we can serve the file String sReply = "HTTP/1.1 200 OK\r\n"; TimeStamp now; now.currentTime(); sReply += "Content-Type: " + sMimeType + "; charset=" + m_client->options().m_sTextEncoding + "\r\n"; sReply += "Date: " + now.toRFC2822() + "\r\n"; sReply += "Last-Modified: " + stats.m_mtime->toRFC2822() + "\r\n"; sReply += "\r\n"; // content length not strictly necessary now m_client->sendData( sReply ); char buffer[4096]; int len = fs.read( buffer, 4096 ); while( len > 0 ) { m_client->sendData( buffer, len ); len = fs.read( buffer, 4096 ); } if ( len < 0 ) { // error! m_client->log()->log( LOGLEVEL_WARN, "Error while reading file "+ m_sFile ); m_client->replyError( 403 ); } } /* end of falhttpd_filehandler.cpp */ modules/native/wopi/falhttpd/falhttpd_filehandler.h000066400000000000000000000015201176363201700231200ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_filehandler.h Micro HTTPD server providing Falcon scripts on the web. Handler(s) for requests. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Mar 2010 12:18:29 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALHTTPD_FILEHANDLER_H_ #define FALHTTPD_FILEHANDLER_H_ #include "falhttpd_rh.h" class FileHandler: public FalhttpdRequestHandler { public: FileHandler( const Falcon::String& sFile, FalhttpdClient* client ); virtual ~FileHandler(); virtual void serve( Falcon::WOPI::Request* req ); }; #endif /* falhttpd_filehandler.h */ modules/native/wopi/falhttpd/falhttpd_istream.cpp000066400000000000000000000032121176363201700226420ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_istream.cpp Micro HTTPD server providing Falcon scripts on the web. Servicing single client. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 28 Feb 2010 23:03:18 +0100 s ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "falhttpd_istream.h" #include "falhttpd_istream.h" #ifndef FALCON_SYSTEM_WIN #include #include #endif SocketInputStream::SocketInputStream( SOCKET s ): Stream(t_network), m_socket(s), m_nLastError(0) { m_status = t_open; } SocketInputStream::~SocketInputStream() { // don't do anything } SocketInputStream* SocketInputStream::clone() const { // uncloneable; return 0; } Falcon::int32 SocketInputStream::read( void *buffer, Falcon::int32 size ) { int rin = ::recv( m_socket, (char*) buffer, size, 0 ); if( rin == 0 ) { status(t_eof); } else if( rin < 0 ) { status(t_error); #ifdef FALCON_SYSTEM_WINDOWS m_nLastError = (Falcon::int64) ::WSAGetLastError(); #else m_nLastError = (Falcon::int64) errno; #endif return -1; } return (Falcon::int32) rin; } bool SocketInputStream::get( Falcon::uint32 &chr ) { // not efficient, but we're not actually going to use this. Falcon::byte b; return this->read( &b, 1 ) == 1; } Falcon::int64 SocketInputStream::lastError() const { return m_nLastError; } /* end of falhttpd_istream.cpp */ modules/native/wopi/falhttpd/falhttpd_istream.h000066400000000000000000000020131176363201700223050ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_istream.h Micro HTTPD server providing Falcon scripts on the web. Servicing single client. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 28 Feb 2010 23:03:18 +0100 s ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALHTTPD_ISTREAM_H_ #define FALHTTPD_ISTREAM_H_ #include "falhttpd.h" #include class SocketInputStream: public Falcon::Stream { public: SocketInputStream( SOCKET s ); virtual ~SocketInputStream(); virtual SocketInputStream* clone() const; virtual Falcon::int32 read( void *buffer, Falcon::int32 size ); virtual bool get( Falcon::uint32 &chr ); virtual Falcon::int64 lastError() const; private: SOCKET m_socket; Falcon::int64 m_nLastError; }; #endif /* falhttpd_istream.h */ modules/native/wopi/falhttpd/falhttpd_options.cpp000066400000000000000000000274161176363201700227050ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_options.cpp Micro HTTPD server providing Falcon scripts on the web. Implementation of option file ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 24 Feb 2010 20:10:45 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "falhttpd.h" #include "falhttpd_options.h" #include #include #include #include #include "falhttpd_rh.h" #include "falhttpd_filehandler.h" #include "falhttpd_scripthandler.h" #include "falhttpd_dirhandler.h" #include //TODO: set from .h FalhttpOptions::FalhttpOptions(): m_loadPath( "." ), m_sIface( "0.0.0.0" ), m_nPort(80), m_logLevel( 3 ), m_bQuiet( false ), m_bHelp( false ), m_bSysLog( true ), m_bAllowDir( true ) { m_maxUpload = 200000; m_maxMemUpload = 5000; m_sUploadPath = "/tmp"; m_sTextEncoding = "utf-8"; m_sSourceEncoding = "utf-8"; m_pSessionManager = new Falcon::WOPI::MemSessionManager; m_pSessionManager->timeout(30); setIndexFile( "index.ftd;index.fal;index.html;index.htm" ); } FalhttpOptions::~FalhttpOptions() { } bool FalhttpOptions::init( int argc, char* argv[] ) { Falcon::String res; Falcon::String logLevel; Falcon::String sPort, sTimeout; // first thing, pre-configure falcon load path. if ( Falcon::Sys::_getEnv("FALCON_LOAD_PATH", res ) ) m_loadPath = res; // then read the parameters Falcon::String* pParam = 0; for( int i = 1; i < argc; ++i ) { char* param = argv[i]; if ( pParam != 0 ) { pParam->bufferize(param); pParam = 0; continue; } if( param[0] == '-' ) { switch( param[1] ) { case 'A': pParam = &m_sAppDataDir; break; case 'C': pParam = &m_configFile; break; case 'D': pParam = &m_sLogFiles; break; case 'h': pParam = &m_homedir; break; case 'l': pParam = &logLevel; break; case 'i': pParam = &m_sIface; break; case 'L': pParam = &m_loadPath; break; case 'p': pParam = &sPort; break; case 'q': m_bQuiet = true; break; case 'S': m_bSysLog = false; break; case 'T': pParam = &m_sUploadPath; case 't': pParam = &sTimeout; case '?': m_bHelp = true; break; default: m_sErrorDesc = "Invalid command "; m_sErrorDesc += param; return false; } } else { if ( m_configFile.size() ) { m_sErrorDesc = "Give just one config file"; return false; } m_configFile.bufferize( param ); } } if( pParam != 0 ) { m_sErrorDesc = "Missing mandatory parameter for command "; m_sErrorDesc += *pParam; return false; } // log level post processing Falcon::int64 ll; if( logLevel.size() != 0 ) { if( logLevel.size() != 0 && logLevel.parseInt( ll ) ) m_logLevel = (int) ll; } // port address post processing if( sPort.size() != 0 ) { if( sPort.parseInt( ll ) ) { m_nPort = (int) ll; } else { m_sErrorDesc = "Invalid value for -p " + sPort; return false; } } // port address post processing if( sTimeout.size() != 0 ) { if( sTimeout.parseInt( ll ) ) { m_nTimeout = (int) ll; m_pSessionManager->timeout(m_nTimeout); } else { m_sErrorDesc = "Invalid value for -t " + sTimeout; return false; } } Falcon::Engine::setEncodings( m_sSourceEncoding, m_sSourceEncoding ); return true; } void FalhttpOptions::setIndexFile( const Falcon::String& fName ) { m_lIndexFiles.clear(); Falcon::uint32 pos1 = fName.find( ";" ); Falcon::uint32 pos = 0; while( pos1 != Falcon::String::npos ) { m_lIndexFiles.push_back( fName.subString( pos, pos1 ) ); pos = pos1 + 1; pos1 = fName.find( ";", pos ); } m_lIndexFiles.push_back( fName.subString( pos ) ); m_sIndexFile = fName; } bool FalhttpOptions::remap( Falcon::String& sFname ) const { // first, let's handle redirection as-is. std::list::const_iterator ired = m_lRedirects.begin(); while( ired != m_lRedirects.end() ) { if( sFname.startsWith(ired->m_sPath) ) { // we found it. sFname = ired->m_sScript; return true; } ++ired; } // find the file. Falcon::Path path( sFname ); path.setFullLocation( m_homedir + "/" + path.getFullLocation() ); sFname = path.get(); Falcon::FileStat stats; if( ! Falcon::Sys::fal_stats( sFname, stats ) ) { return false; } else { // resolve links while( stats.m_type == Falcon::FileStat::t_link ) { Falcon::String nname; if( ! Falcon::Sys::fal_readlink( sFname, nname ) ) return false; if( nname.startsWith("/") ) sFname = nname; else { Falcon::uint32 pos = sFname.rfind("/"); sFname = sFname.subString( 0, pos ) + "/" + nname; } if( ! Falcon::Sys::fal_stats( sFname, stats ) ) { return false; } } // Is the file a directory? if( stats.m_type == Falcon::FileStat::t_dir ) { path.setFullLocation( sFname ); // Do we have index files to try? std::list::const_iterator fni = m_lIndexFiles.begin(); while( fni != m_lIndexFiles.end() ) { path.setFilename( *fni ); if( Falcon::Sys::fal_stats( path.get(), stats ) ) { break; } ++fni; } // return it as a directory? if( fni == m_lIndexFiles.end() ) { sFname = path.getFullLocation() + "/"; } else { sFname = path.get(); } } } return true; } void FalhttpOptions::loadIni( Falcon::LogArea* log, Falcon::ConfigFileService* cfs ) { Falcon::String sPort; Falcon::String sIndex; cfs->getValue( "HomeDir", m_homedir ); cfs->getValue( "LoadPath", m_loadPath ); cfs->getValue( "TempDir", m_sUploadPath ); cfs->getValue( "Interface", m_sIface ); cfs->getValue( "PersistentDataDir", m_sAppDataDir ); if( cfs->getValue( "Port", sPort ) ) { Falcon::int64 nPort; if( sPort.parseInt( nPort ) ) m_nPort = (int) nPort; } if( cfs->getValue( "SessionTimeout", sPort ) ) { Falcon::int64 nTimeout; if( sPort.parseInt( nTimeout ) ) { m_nTimeout = (int) nTimeout; m_pSessionManager->timeout( m_nTimeout ); } } Falcon::String sBoolVal; if( cfs->getValue( "AllowDir", sBoolVal ) ) { m_bAllowDir = checkBool(sBoolVal); } if( cfs->getValue( "IndexFile", sIndex ) ) { setIndexFile( sIndex ); } parseMimeTypes( log, cfs ); parseRedirects( log, cfs ); } void FalhttpOptions::parseMimeTypes( Falcon::LogArea* log, Falcon::ConfigFileService* cfs ) { Falcon::String sKey; if ( cfs->getFirstKey( "mime", sKey ) ) { addMimeType( log, cfs, sKey ); while( cfs->getNextKey( sKey ) ) { addMimeType( log, cfs, sKey ); } } else { // add some sensible default. m_lMimeTypes.push_back( MimeType( "text/html", "*.html;*.htm" ) ); m_lMimeTypes.push_back( MimeType( "text/css", "*.css" ) ); m_lMimeTypes.push_back( MimeType( "text/javascript", "*.js" ) ); m_lMimeTypes.push_back( MimeType( "image/png", "*.png" ) ); m_lMimeTypes.push_back( MimeType( "image/gif", "*.gif" ) ); m_lMimeTypes.push_back( MimeType( "image/jpg", "*.jpg;*.jpeg" ) ); m_lMimeTypes.push_back( MimeType( "image/tiff", "*.tif;*.tiff" ) ); m_lMimeTypes.push_back( MimeType( "text/plain", "*" ) ); } } void FalhttpOptions::parseRedirects( Falcon::LogArea* log, Falcon::ConfigFileService* cfs ) { Falcon::String sKey; if ( cfs->getFirstKey( "vpath", sKey ) ) { addRedirect( log, cfs, sKey ); while( cfs->getNextKey( sKey ) ) { addRedirect( log, cfs, sKey ); } } } void FalhttpOptions::addMimeType( Falcon::LogArea* log, Falcon::ConfigFileService* cfs, const Falcon::String& sKey ) { Falcon::String sValue; if( cfs->getValue( sKey, sValue ) ) { log->log( LOGLEVEL_INFO, "Parsing mime type " + sKey + " = " + sValue ); Falcon::String sType = sKey.subString(5); // discard "mime." Falcon::uint32 pos=0; while( (pos = sType.find( ".", pos ) ) != Falcon::String::npos ) { sType.setCharAt(pos, '/' ); } if ( sValue == "*" ) { m_lMimeTypes.push_back( MimeType( sType, sValue ) ); } else { m_lMimeTypes.push_front( MimeType( sType, sValue ) ); } } else { log->log( LOGLEVEL_WARN, "No mime type associated with " + sKey ); } } void FalhttpOptions::addRedirect( Falcon::LogArea* log, Falcon::ConfigFileService* cfs, const Falcon::String& sKey ) { Falcon::String sValue; if( cfs->getValue( sKey, sValue ) ) { Falcon::String sPath = sKey.subString(8); // discard "redirect" (we need the starting ".") Falcon::uint32 pos=0; while( (pos = sPath.find( ".", pos ) ) != Falcon::String::npos ) { sPath.setCharAt(pos, '/' ); } // remove the * so that we get the whole path if( sPath.endsWith("*") ) { sPath.remove(sPath.length()-1,1); m_lRedirects.push_back( Redirect( sPath, sValue ) ); } else { sPath += "/"; m_lRedirects.push_front( Redirect( sPath, sValue ) ); } log->log( LOGLEVEL_INFO, "Adding redirect script handler "+ sValue + " for queries in " + sPath ); } else { log->log( LOGLEVEL_WARN, "No script associated with redirect " + sKey ); } } FalhttpOptions::MimeType::MimeType( const Falcon::String& sType, const Falcon::String& sValue ): m_def(sType) { Falcon::uint32 pos = 0, pos1; do { pos1 = sValue.find( ";", pos ); Falcon::String sPart = sValue.subString( pos, pos1 ); sPart.trim(); m_lWildcards.push_back(sPart); pos = pos1+1; } while( pos1 != Falcon::String::npos ); } bool FalhttpOptions::MimeType::match( const Falcon::String& sFname ) const { std::list::const_iterator pos = m_lWildcards.begin(); while( pos != m_lWildcards.end() ) { if( sFname.wildcardMatch( *pos ) ) return true; ++pos; } return false; } bool FalhttpOptions::findMimeType( const Falcon::String& sFname, Falcon::String& type ) const { std::list::const_iterator melem = m_lMimeTypes.begin(); while( melem != m_lMimeTypes.end() ) { const MimeType& mtype = *melem; if( mtype.match( sFname ) ) { type = mtype.m_def; return true; } ++melem; } return false; } bool FalhttpOptions::checkBool( const Falcon::String& s ) { Falcon::String upper = s; upper.upper(); return (upper == "T") || (upper == "TRUE") || (upper == "1") || (upper == "ON"); } FalhttpdRequestHandler* FalhttpOptions::getHandler( const Falcon::String& sFile, FalhttpdClient* cli ) const { if ( sFile.endsWith("/") && m_bAllowDir ) { return new DirHandler( sFile, cli ); } else if( sFile.endsWith(".fal") || sFile.endsWith(".fam") || sFile.endsWith(".ftd") ) { return new ScriptHandler( sFile, cli ); } return new FileHandler( sFile, cli ); } /* end of falhttpd_options.cpp */ modules/native/wopi/falhttpd/falhttpd_options.h000066400000000000000000000062441176363201700223460ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_options.h Micro HTTPD server providing Falcon scripts on the web. Implementation of option file ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 24 Feb 2010 20:10:45 +0100 s ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_HTTPD_OPTIONS_H_ #define FALCON_HTTPD_OPTIONS_H_ #ifdef _WIN32 #include #include #endif #include #include #include #include #include class FalhttpdClient; class FalhttpdRequestHandler; class FalhttpOptions { public: FalhttpOptions(); ~FalhttpOptions(); bool init( int argc, char* argv[] ); void setIndexFile( const Falcon::String& fName ); //bool loadConfig( const String& cfgFile ); /** Load an INI file. */ void loadIni( Falcon::LogArea* si, Falcon::ConfigFileService* cfs ); /** Change the file to be loaded. */ bool remap( Falcon::String& sFname ) const; /** Determines the mime type of a file being loaded. */ bool findMimeType( const Falcon::String& fname, Falcon::String& mtype ) const; /** Gets a registered request handler for the given file. */ FalhttpdRequestHandler* getHandler( const Falcon::String& sFile, FalhttpdClient* cli ) const; Falcon::int64 m_maxUpload; Falcon::int64 m_maxMemUpload; Falcon::String m_sUploadPath; Falcon::String m_loadPath; Falcon::String m_homedir; Falcon::String m_configFile; Falcon::String m_sErrorDesc; Falcon::String m_sLogFiles; Falcon::String m_sIface; Falcon::String m_sIndexFile; Falcon::String m_sTextEncoding; Falcon::String m_sSourceEncoding; Falcon::String m_sAppDataDir; std::list m_lIndexFiles; int m_nPort; int m_logLevel; int m_nTimeout; bool m_bQuiet; bool m_bHelp; bool m_bSysLog; bool m_bAllowDir; Falcon::WOPI::SessionManager* m_pSessionManager; private: bool checkBool( const Falcon::String& b ); void parseMimeTypes( Falcon::LogArea* log, Falcon::ConfigFileService* cfs ); void addMimeType( Falcon::LogArea* log, Falcon::ConfigFileService* cfs, const Falcon::String& key ); void parseRedirects( Falcon::LogArea* log, Falcon::ConfigFileService* cfs ); void addRedirect( Falcon::LogArea* log, Falcon::ConfigFileService* cfs, const Falcon::String& key ); class MimeType { public: Falcon::String m_def; std::list m_lWildcards; MimeType( const Falcon::String &def, const Falcon::String& wcard ); bool match( const Falcon::String &name ) const; }; class Redirect { public: Falcon::String m_sPath; Falcon::String m_sScript; Redirect( const Falcon::String& p, const Falcon::String& s ): m_sPath( p ), m_sScript( s ) {} }; std::list m_lMimeTypes; std::list< Redirect > m_lRedirects; }; #endif /* end of falhttpd_options.h */ modules/native/wopi/falhttpd/falhttpd_ostream.cpp000066400000000000000000000031421176363201700226520ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_ostream.h Micro HTTPD server providing Falcon scripts on the web. Output stream with reply-header control. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 06 Mar 2010 20:48:45 +0100 s ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "falhttpd_ostream.h" #include "falhttpd_reply.h" class FalhttpdReply; FalhttpdOutputStream::FalhttpdOutputStream( SOCKET s ): Stream(t_network), m_socket( s ) { m_status = t_open; } FalhttpdOutputStream::~FalhttpdOutputStream() { // do nothing } FalhttpdOutputStream* FalhttpdOutputStream::clone() const { return new FalhttpdOutputStream( m_socket ); } bool FalhttpdOutputStream::get( Falcon::uint32 &chr ) { chr = 0; return false; } Falcon::int32 FalhttpdOutputStream::write( const void *buffer, Falcon::int32 size ) { int sent = 0; const char* cbuf = (const char*) buffer; while( sent < size ) { int res = ::send( m_socket, cbuf + sent, size - sent, 0 ); if( res < 0 ) { return res; } sent += res; m_lastMoved = sent; } return sent; } bool FalhttpdOutputStream::put( Falcon::uint32 chr ) { Falcon::byte b = (Falcon::byte) chr; return ::send( m_socket, (const char*) &b, 1, 0 ) == 1; } Falcon::int64 FalhttpdOutputStream::lastError() const { return m_nLastError; } /* falhttpd_ostream.cpp */ modules/native/wopi/falhttpd/falhttpd_ostream.h000066400000000000000000000021551176363201700223220ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_ostream.h Micro HTTPD server providing Falcon scripts on the web. Stream reading input from a TCP socket. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 06 Mar 2010 20:48:45 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALHTTPD_OSTREAM_H_ #define FALHTTPD_OSTREAM_H_ #include "falhttpd.h" #include class FalhttpdReply; class FalhttpdOutputStream: public Falcon::Stream { public: FalhttpdOutputStream( SOCKET s ); virtual ~FalhttpdOutputStream(); virtual FalhttpdOutputStream* clone() const; virtual Falcon::int32 write( const void *buffer, Falcon::int32 size ); virtual bool put( Falcon::uint32 chr ); virtual Falcon::int64 lastError() const; virtual bool get( Falcon::uint32 &chr ); private: SOCKET m_socket; Falcon::int64 m_nLastError; }; #endif /* falhttpd_ostream.h */ modules/native/wopi/falhttpd/falhttpd_reply.cpp000066400000000000000000000032201176363201700223300ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_reply.cpp Micro HTTPD server providing Falcon scripts on the web. Reply object specific for the Falcon micro HTTPD server. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 06 Mar 2010 21:31:37 +0100 s ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "falhttpd_reply.h" #include "falhttpd_ostream.h" FalhttpdReply::FalhttpdReply( const Falcon::CoreClass* cls ): Reply( cls ) { } FalhttpdReply::~FalhttpdReply() { } void FalhttpdReply::init( SOCKET s ) { m_socket = s; } void FalhttpdReply::startCommit() { m_headers.A("HTTP/1.1 ").N( m_nStatus ).A(" ").A( m_sReason ).A("\r\n"); } Falcon::Stream* FalhttpdReply::makeOutputStream() { return new Falcon::StreamBuffer( new FalhttpdOutputStream( m_socket ) ); } void FalhttpdReply::commitHeader( const Falcon::String& hname, const Falcon::String& hvalue ) { m_headers += hname + ": " + hvalue + "\r\n"; } void FalhttpdReply::endCommit() { send( m_headers + "\r\n" ); } void FalhttpdReply::send( const Falcon::String& s ) { Falcon::uint32 sent = 0; while( sent < s.size() ) { int res = ::send( m_socket, (const char*) s.getRawStorage() + sent, s.size() - sent, 0 ); if( res < 0 ) return; sent += res; } } Falcon::CoreObject* FalhttpdReply::factory( const Falcon::CoreClass* cls, void* ud, bool bDeser ) { return new FalhttpdReply( cls ); } /* falhttpd_reply.cpp */ modules/native/wopi/falhttpd/falhttpd_reply.h000066400000000000000000000023001176363201700217730ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_reply.h Micro HTTPD server providing Falcon scripts on the web. Reply object specific for the Falcon micro HTTPD server. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 06 Mar 2010 21:31:37 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALHTTPD_REPLY_H_ #define FALHTTPD_REPLY_H_ #include "falhttpd.h" #include class FalhttpdReply: public Falcon::WOPI::Reply { public: FalhttpdReply( const Falcon::CoreClass* cls ); ~FalhttpdReply(); void init( SOCKET s ); Falcon::Stream* makeOutputStream(); virtual void startCommit(); virtual void commitHeader( const Falcon::String& hname, const Falcon::String& hvalue ); virtual void endCommit(); static Falcon::CoreObject* factory( const Falcon::CoreClass* cls, void* ud, bool bDeser ); private: void send( const Falcon::String& s ); SOCKET m_socket; Falcon::String m_headers; }; #endif /* falhttpd_reply.h */ modules/native/wopi/falhttpd/falhttpd_rh.cpp000066400000000000000000000014221176363201700216100ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_rh.cpp Micro HTTPD server providing Falcon scripts on the web. Handler(s) for requests. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 19 Mar 2010 05:13:03 -0700 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "falhttpd_rh.h" #include "falhttpd_client.h" FalhttpdRequestHandler::FalhttpdRequestHandler( const Falcon::String& sFile, FalhttpdClient* cli ): m_sFile( sFile ), m_client( cli ) { } FalhttpdRequestHandler::~FalhttpdRequestHandler() { } /* end of falhttpd_rh.cpp */ modules/native/wopi/falhttpd/falhttpd_rh.h000066400000000000000000000020421176363201700212540ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_rh.h Micro HTTPD server providing Falcon scripts on the web. Abstract interface for request handlers. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 19 Mar 2010 05:13:03 -0700 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALHTTPD_RH_H_ #define FALHTTPD_RH_H_ #include "falhttpd.h" #include class FalhttpdClient; class FalhttpdRequestHandler { public: FalhttpdRequestHandler( const Falcon::String& sFile, FalhttpdClient* client ); virtual ~FalhttpdRequestHandler(); virtual void serve( Falcon::WOPI::Request* req ) = 0; const Falcon::String& errorDesc() const { return m_sErrorDesc; } protected: Falcon::String m_sFile; Falcon::String m_sErrorDesc; FalhttpdClient* m_client; }; #endif /* falhttpd_rh.h */ modules/native/wopi/falhttpd/falhttpd_scripthandler.cpp000066400000000000000000000112201176363201700240360ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_scripthandler.cpp Micro HTTPD server providing Falcon scripts on the web. Request handler processing a script. Mimetype is not determined (must be set by the script), but it defaults to text/html; charset=utf-8 ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Mar 2010 12:18:29 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include "falhttpd_scripthandler.h" #include "falhttpd_reply.h" #include "falhttpd_ostream.h" #include "falhttpd_client.h" #include "falhttpd.h" #include #include #include ScriptHandler::ScriptHandler( const Falcon::String& sFile, FalhttpdClient* cli ): FalhttpdRequestHandler( sFile, cli ) { } ScriptHandler::~ScriptHandler() { } static void report_temp_file_error( const Falcon::String& fileName, void* data ) { Falcon::LogArea* serr = (Falcon::LogArea*) data; serr->log( LOGLEVEL_WARN, "Cannot remove temp file " + fileName ); } void ScriptHandler::serve( Falcon::WOPI::Request* req ) { Falcon::String cwd; Falcon::int32 status; Falcon::Sys::fal_getcwd( cwd, status ); FalhttpdApp* app = FalhttpdApp::get(); // find the file. Falcon::ModuleLoader ml( *app->loader() ); ml.sourceEncoding( m_client->options().m_sSourceEncoding ); Falcon::Engine::setEncodings( m_client->options().m_sSourceEncoding, m_client->options().m_sTextEncoding ); if( m_client->options().m_loadPath.size() == 0 ) ml.addFalconPath(); else ml.addSearchPath( m_client->options().m_loadPath ); Falcon::Path path( m_sFile ); ml.addDirectoryFront( path.getFullLocation() ); // broadcast the dir to other elements Falcon::Engine::setSearchPath( ml.getSearchPath() ); void *tempFileList = 0; { Falcon::Runtime rt(&ml); Falcon::VMachineWrapper vm; // we should have no trouble here vm->link( app->core() ); vm->link( app->wopi() ); // Now let's init the request and reply objects Falcon::Item* i_req = vm->findGlobalItem( "Request" ); Falcon::Item* i_rep = vm->findGlobalItem( "Reply" ); Falcon::Item* i_cupt = vm->findGlobalItem( "Uploaded" ); Falcon::Item* i_wopi = vm->findGlobalItem( "Wopi" ); fassert( i_req->isObject() && i_rep->isObject() ); fassert( i_wopi != 0 && i_wopi->isObject() ); FalhttpdReply* rep = dyncast(i_rep->asObject()); rep->init( m_client->socket() ); Falcon::WOPI::CoreRequest* creq = dyncast(i_req->asObject()); creq->init(i_cupt->asClass(), rep, m_client->smgr(), req ); creq->processMultiPartBody(); creq->provider("HTTPD"); uint32 sTokenId = dyncast(i_req->asObject())->base()->sessionToken(); vm->stdOut( new Falcon::WOPI::ReplyStream( rep ) ); vm->stdErr( new Falcon::WOPI::ReplyStream( rep ) ); Falcon::Item* i_sn = vm->findGlobalItem( "scriptName" ); Falcon::Item* i_sp = vm->findGlobalItem( "scriptPath" ); *i_sn = new CoreString( path.getFullLocation() ); *i_sp = new CoreString( path.getFilename() ); if( m_client->options().m_sAppDataDir != "" ) dyncast( i_wopi->asObject() )->wopi()->dataLocation( m_client->options().m_sAppDataDir ); try { // prepare the reply rt.loadFile( path.get() ); vm->link( &rt ); Falcon::Sys::fal_chdir( path.getFullLocation(), status ); m_client->log()->log( LOGLEVEL_INFO, "Serving script "+ path.get() ); vm->launch(); // be sure that the output is committed, or output may be 0 below at close() rep->commit(); } catch( Error* err ) { rep->commit(); String s = err->toString(); rep->output()->writeString( s ); rep->output()->flush(); m_client->log()->log( LOGLEVEL_WARN, "Script "+ path.get()+ " terminated with error: " + s ); err->decref(); } tempFileList = req->getTempFiles(); m_client->smgr()->releaseSessions(sTokenId); rep->output()->close(); } // End of scope of the VM Falcon::Sys::fal_chdir( cwd, status ); // Free the temp files if( tempFileList != 0 ) { Falcon::WOPI::Request::removeTempFiles( tempFileList, m_client->log(), report_temp_file_error ); } } /* falhttpd_scripthandler.cpp */ modules/native/wopi/falhttpd/falhttpd_scripthandler.h000066400000000000000000000017531176363201700235150ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falhttpd_scripthandler.h Micro HTTPD server providing Falcon scripts on the web. Request handler processing a script. Mimetype is not determined (must be set by the script), but it defaults to text/html; charset=utf-8 ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Mar 2010 12:18:29 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALHTTPD_SCRIPTHANDLER_H_ #define FALHTTPD_SCRIPTHANDLER_H_ #include "falhttpd.h" #include "falhttpd_rh.h" class ScriptHandler: public FalhttpdRequestHandler { public: ScriptHandler( const Falcon::String& sFile, FalhttpdClient* client ); virtual ~ScriptHandler(); virtual void serve( Falcon::WOPI::Request* req ); }; #endif /* falhttpd_scripthandler.h */ modules/native/wopi/ffalcgi/000077500000000000000000000000001176363201700163735ustar00rootroot00000000000000modules/native/wopi/ffalcgi/CMakeLists.txt000066400000000000000000000041511176363201700211340ustar00rootroot00000000000000###################################################################### # CMake file for fcgi # MESSAGE( "Creating the Falcon FastCGI driver" ) ###################################################################### # Search FastCGI SET( FASTCGI_INCLUDE_SEARCH_PATH ${FASTCGI_INCLUDE_DIR} /opt/include /usr/local/include /usr/include/ ) SET( FASTCGI_LIB_SEARCH_PATH ${FASTCGI_LIB_DIR} /opt/lib /usr/local/lib /usr/lib /opt/lib64 /usr/local/lib64 /usr/lib64 ) FIND_PATH(FASTCGI_REAL_INCLUDE_DIR fastcgi.h PATHS ${FASTCGI_INCLUDE_SEARCH_PATH} PATH_SUFFIXES fastcgi) FIND_PATH(FASTCGI_REAL_LIB_DIR NAMES libfcgi.a libfcgi.so PATHS ${FASTCGI_LIB_SEARCH_PATH}) if(NOT FASTCGI_INCLUDE_SEARCH_PATH) Message(FATAL_ERROR "Can't find FastCGI include headers." ) endif() if(NOT FASTCGI_REAL_LIB_DIR) Message(FATAL_ERROR "Can't find FastCGI libraries." ) endif() INCLUDE_DIRECTORIES("${FASTCGI_REAL_INCLUDE_DIR}") link_directories( "${FASTCGI_REAL_LIB_DIR}" ) Message( "Found FastCGI at ${FASTCGI_REAL_INCLUDE_DIR}" ) ###################################################################### # Targets # # Inclusion settings include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/falcgi ${Falcon_INCLUDE_DIRS} ) set(SRC_FILES ../falcgi/cgi_options.cpp ../falcgi/cgi_reply.cpp ../falcgi/cgi_request.cpp ../falcgi/falcgi_perform.cpp ffalcgi.cpp ffalcgi_make_streams.cpp ) # These are actually not needed by cmake to build. But if omitted they won't be # listed in the virtual file tree of Visual Studio. set(HDR_FILES ../falcgi/cgi_make_streams.h ../falcgi/cgi_options.h ../falcgi/cgi_reply.h ../falcgi/cgi_request.h ../falcgi/falcgi_perform.h ) #INCLUDE_DIRECTORIES("../falcgi") ADD_EXECUTABLE( ffalcgi ${SRC_FILES} ${WOPI_SOURCES} ${HDR_FILES} # optional but useful, see comment above. ) if(WIN32) set( LIBFCGI libfcgi ) else() set( LIBFCGI fcgi ) endif() TARGET_LINK_LIBRARIES( ffalcgi falcon_engine ${LIBFCGI} ) #APR library added in ld flags INSTALL(TARGETS ffalcgi RUNTIME DESTINATION ${FALCON_WOPI_CGI_INSTALL_DIR} ) modules/native/wopi/ffalcgi/ffalcgi.cpp000066400000000000000000000034561176363201700205020ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falcgi.cpp Falcon CGI program driver - main file. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Feb 2010 17:35:14 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include //We're not an application program; instead, we're using the FCGI api directly #define NO_FCGI_DEFINES #include #include #include #include #include #include #include #include #include #include static void report_temp_file_error( const Falcon::String& fileName, void* data ) { Falcon::AutoCString cstr(fileName); printf( "ERROR: Cannot remove temp file %s\r\n", cstr.c_str() ); } int main( int argc, char* argv[] ) { // we need to re-randomize based on our pid + time, // or we may end up creating the same temp files. Falcon::WOPI::Utils::xrandomize(); // start the engine Falcon::Engine::Init(); CGIOptions cgiopt; if ( cgiopt.init( argc, argv ) ) { while( FCGI_Accept() >= 0 ) { void *tempFileList = perform( cgiopt, argc, argv ); // perform complete GC to reset open states (i.e. open file handles). Falcon::memPool->performGC(); // Free the temp files if( tempFileList != 0 ) { Falcon::WOPI::Request::removeTempFiles( tempFileList, 0, report_temp_file_error ); } } } delete cgiopt.m_smgr; // engine down. Falcon::Engine::Shutdown(); return 0; } modules/native/wopi/ffalcgi/ffalcgi_make_streams.cpp000066400000000000000000000042661176363201700232350ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: falcgi_make_streams.cpp Falcon CGI program driver - CGI specific stream provider. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Feb 2010 17:35:14 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include class FFCGIStream: public Falcon::Stream { public: FFCGIStream( FILE* tgt ); virtual ~FFCGIStream(); virtual Falcon::int32 write( const void* data, Falcon::int32 size ); virtual Falcon::int32 read( void* data, Falcon::int32 size ); virtual bool put( Falcon::uint32 chr ); virtual bool get( Falcon::uint32& chr ); virtual bool close(); virtual Falcon::int64 lastError() const; virtual bool flush(); private: FILE* m_target; }; FFCGIStream::FFCGIStream( FILE* tgt ): Stream(t_stream), m_target(tgt) { } FFCGIStream::~FFCGIStream() { } Falcon::int32 FFCGIStream::write( const void* data, Falcon::int32 size ) { return fwrite( const_cast(data), 1, size, m_target ); } Falcon::int32 FFCGIStream::read( void* data, Falcon::int32 size ) { return fread( data, 1, size, m_target ); } bool FFCGIStream::put( Falcon::uint32 chr ) { return fputc( chr, m_target ) >= 0 ; } bool FFCGIStream::get( Falcon::uint32& chr ) { int ichr = fgetc( m_target ); chr = (Falcon::uint32) ichr; return ichr >= 0; } bool FFCGIStream::close() { // we can't close this streams return true; } Falcon::int64 FFCGIStream::lastError() const { return (Falcon::int64) errno; } bool FFCGIStream::flush() { return fflush( m_target ) == 0 ; } Falcon::Stream* makeOutputStream() { Falcon::Stream* test = new FFCGIStream( stdout ); return test; } Falcon::Stream* makeInputStream() { return new FFCGIStream( stdin ); } Falcon::Stream* makeErrorStream() { return new FFCGIStream( stderr ); } /* end of falcgi_make_streams.cpp */ modules/native/wopi/include/000077500000000000000000000000001176363201700164235ustar00rootroot00000000000000modules/native/wopi/include/CMakeLists.txt000066400000000000000000000004231176363201700211620ustar00rootroot00000000000000file(GLOB _headers *.h) unset(WOPI_HEADERS) foreach(item ${_headers}) list(APPEND WOPI_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/${item}) endforeach(item) set(WOPI_HEADERS ${WOPI_HEADERS} PARENT_SCOPE) install(FILES ${WOPI_HEADERS} DESTINATION ${Falcon_INC_DIR}/falcon/wopi)modules/native/wopi/include/falcon/000077500000000000000000000000001176363201700176655ustar00rootroot00000000000000modules/native/wopi/include/falcon/wopi/000077500000000000000000000000001176363201700206435ustar00rootroot00000000000000modules/native/wopi/include/falcon/wopi/error_ext.h000066400000000000000000000024331176363201700230270ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: error_h.cpp Error for WOPI exceptions. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 28 Mar 2010 17:12:01 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef WOPI_ERROR_H #define WOPI_ERROR_H #include #include #ifndef FALCON_ERROR_WOPI_BASE #define FALCON_ERROR_WOPI_BASE 2400 #endif #define FALCON_ERROR_WOPI_SESS_IO (FALCON_ERROR_WOPI_BASE + 0 ) #define FALCON_ERROR_WOPI_SESS_EXPIRED (FALCON_ERROR_WOPI_BASE + 1 ) #define FALCON_ERROR_WOPI_APPDATA_SER (FALCON_ERROR_WOPI_BASE + 2 ) #define FALCON_ERROR_WOPI_APPDATA_DESER (FALCON_ERROR_WOPI_BASE + 3 ) #define FALCON_ERROR_WOPI_SESS_INVALID_ID (FALCON_ERROR_WOPI_BASE + 4 ) namespace Falcon { namespace WOPI { class WopiError: public ::Falcon::Error { public: WopiError(): Error( "WopiError" ) {} WopiError( const ErrorParam ¶ms ): Error( "WopiError", params ) {} }; void InitErrorClass( Module* self ); } } #endif /* end of error_ext.h */ modules/native/wopi/include/falcon/wopi/file_sm.h000066400000000000000000000024461176363201700224400ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: file_sm.h Falcon Web Oriented Programming Interface File based session manager. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 27 Mar 2010 15:00:17 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef _FILE_SESSION_MANAGER_H #define _FILE_SESSION_MANAGER_H #include namespace Falcon { namespace WOPI { /** File based session data. */ class FileSessionData: public SessionData { public: FileSessionData( const String& SID, const String& tmpDir ); virtual ~FileSessionData(); virtual bool resume(); virtual bool store(); virtual bool dispose(); private: String m_tmpDir; }; /** File based session manager. */ class FileSessionManager: public SessionManager { public: FileSessionManager( const String& tmpDir ); virtual ~FileSessionManager(); virtual void startup(); virtual void configFromModule( const Module* mod ); protected: virtual SessionData* createSession( const String& sSID ); private: String m_tmpDir; }; } } #endif /* end of file_sm.h */ modules/native/wopi/include/falcon/wopi/fwdata.h000066400000000000000000000021321176363201700222600ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: fwdata.h Falcon Web Oriented Programming Interface Framework data manager and framework data. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 19 Apr 2010 20:51:59 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_WOPI_FWDATA_H_ #define FALCON_WOPI_FWDATA_H_ #include #include #include #include #include namespace Falcon { namespace WOPI { //============================================================ // Global framework data. // class FWData: public CoreObject { public: FWData(); }; class FWDataManager: public BaseAlloc { public: FWDataManager(); FWDataManager( const String& persistLocation ); private: Mutex m_mtx; typedef std::map< String, FWData > DataMap; DataMap m_mData; }; } } /* end of fwdata.h */ modules/native/wopi/include/falcon/wopi/mem_sm.h000066400000000000000000000026211176363201700222720ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: mem_sm.h Falcon Web Oriented Programming Interface Memory based session manager. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 13 Mar 2010 10:25:11 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef _MEM_SESSION_MANAGER_H #define _MEM_SESSION_MANAGER_H #include namespace Falcon { namespace WOPI { /** Memory based session data. This session data doesn't provide any form of persistency. */ class MemSessionData: public SessionData { public: MemSessionData( const String& SID ); virtual ~MemSessionData(); virtual bool resume(); virtual bool store(); virtual bool dispose(); }; /** Memory based session manager. This session store all the data about session just in memory. Client code restart will clear all the sessions (without any information about the fact that existing sessions are invalidated). */ class MemSessionManager: public SessionManager { public: MemSessionManager(); virtual ~MemSessionManager(); virtual void startup(); protected: virtual SessionData* createSession( const String& sSID ); }; } } #endif /* end of mem_sm.h */ modules/native/wopi/include/falcon/wopi/parthandler.h000066400000000000000000000160121176363201700233200ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: parthandler.h Web Oriented Programming Interface RFC1867 compliant upload handler. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 26 Feb 2010 20:29:47 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef _FALCON_WOPI_PARTHANDLER_H_ #define _FALCON_WOPI_PARTHANDLER_H_ #include #include #include #include namespace Falcon { class StringStream; class MemBuf; namespace WOPI { class Request; class HeaderValue { public: typedef std::deque ValueList; typedef std::map ParamMap; HeaderValue(); HeaderValue( const String& value ); HeaderValue( const HeaderValue& other ); virtual ~HeaderValue(); bool hasValue() const { return ! m_lValues.empty(); } const String& value() const { return m_lValues.front(); } const ValueList& values() const { return m_lValues; } ValueList& values() { return m_lValues; } const ParamMap& parameters() const { return m_mParameters; } ParamMap& parameters() { return m_mParameters; } bool parseValue( const String& v ); const String& rawValue() const { return m_sRawValue; } void setRawValue( const String& value ); private: bool parseValuePart( const String& v ); bool parseParamPart( const String& v ); ValueList m_lValues; ParamMap m_mParameters; String m_sRawValue; }; /** RFC1867 compliant upload handler. */ class PartHandler { public: typedef std::map HeaderMap; PartHandler(); PartHandler( const String& sBoundary ); virtual ~PartHandler(); bool parse( Stream* input ) { bool dummy; return parse( input, dummy ); } bool parse( Stream* input, bool& isLast ); //! Return true if this part is complete bool isComplete() const { return m_nReceived == m_nPartSize; } //! Returns the declared content lenght of this part. int64 contentLenght() const { return m_nPartSize; } //! Returns the name of the part. const String& name() const { return m_sPartName; } //! Returns the original filename of an uploaded file const String& filename() const { return m_sPartFileName; } //! Returns the content type of this section const String& contentType() const { return m_sPartContentType; } //! Returns the temporary file where this part has been saved. const String& tempFile() const { return m_sTempFile; } PartHandler* next() const { return m_pNextPart; } PartHandler* child() const { return m_pSubPart; } //! Adds an header bool addHeader( const String& key, const String& value ); //! Returns true if we're saving on a memory stream. bool isMemory() const { return m_str_stream != 0; } //! Parses just the header before the body bool parseHeader( Stream* input ); /** Parse the body of the request part. \note The upload stream MUST have been already opened or the function will assert. \param input The stream. \param isLast will be set to true if this is the last part of a multipart request. \return true on success, false on I/O failure or malformed boundary. */ bool parseBody( Stream* input, bool& isLast ); /* Returns the memory data in a target string. If the part was loaded into memory, the memory data is directly transfered to this string without any copy. Otherwise, nothing is done and the function returns false. */ bool getMemoryData( String& target ); /* Returns the memory data in a target memory buffer. If the part was loaded into memory, the memory data is directly transfered to this MB without any copy. Otherwise, nothing is done and the function returns false. */ bool getMemoryData( MemBuf& target ); const HeaderMap& headers() const { return m_mHeaders; } HeaderMap& headers() { return m_mHeaders; } //! Returns a brief description of what was wrong with last decoding. const String& error() const { return m_sError; } int64 uploadedSize() const { return m_nReceived; } bool startUpload(); void closeUpload(); void startMemoryUpload(); bool startFileUpload(); void setOwner( Request* owner ) { m_owner = owner; } /** Sets the boundary from outside. Useful if the header are parsed elsewhere. */ void setBoundary( const String& b ) { m_sBoundary = b; } //! Create a new buffer (that will be passed down to other elements in the chain) void initBuffer(); bool uploadsInMemory() const { return m_bUseMemoryUpload; } //! Activates memory storage of uploaded data. void uploadsInMemory( bool b ) { m_bUseMemoryUpload = b; } //! True if this part is meant to upload a file, false if it's a standard field. bool isFile() const { return m_bPartIsFile; } void setBodySize( int64 size ) { m_nBodyLeft = size; } private: class PartHandlerBuffer { public: enum e_constants { buffer_size = 8192 }; PartHandlerBuffer( int64* pToMax ); ~PartHandlerBuffer(); uint32 m_nBufPos; uint32 m_nBufSize; byte* m_buffer; bool full() { return m_nBufSize == buffer_size; } bool empty() { return m_nBufSize == 0; } //! Flushes 0->bufPos, and moves m_nBufPos->m_nBufSize -> 0 void flush( Stream* out = 0 ); //! Flushes the size (all) void allFlush( Stream* out = 0 ) { m_nBufPos = m_nBufSize; flush(out); } //! Searches this string in the buffer. uint32 find( const String& str ); //! refills buffer bool fill( Stream* input ); //! gets a string from the buffer (without allocating it). void grab( String& target, int posEnd ); void grabMore( String& target, int size ); bool hasMore( int count, Stream* input, Stream* output = 0 ); private: int64* m_nDataLeft; }; PartHandlerBuffer* m_pBuffer; bool m_bOwnBuffer; void passSetting( PartHandler* child ); //! Searches for the boundary and store the data in m_stream bool scanForBound( const String& boundary, Stream* input, bool& isLast ); bool parseHeaderField( const String& line ); bool parsePrologue( Stream* input ); bool parseMultipartBody( Stream* input ); HeaderMap m_mHeaders; String m_sPartName; String m_sPartFileName; String m_sPartContentType; String m_sTempFile; String m_sError; String m_sBoundaryBuffer; String m_sBoundary; String m_sEnclosingBoundary; int64 m_nPartSize; // Pointer to the total size expected to be received. int64* m_pToBodyLeft; int64 m_nBodyLeft; int64 m_nReceived; Stream* m_stream; // for memory uploads and data fields StringStream* m_str_stream; PartHandler *m_pNextPart; PartHandler *m_pSubPart; Request* m_owner; bool m_bPartIsFile; bool m_bUseMemoryUpload; }; } } #endif /* PARTHANDLER_H_ */ /* end of parthandler.h */ modules/native/wopi/include/falcon/wopi/reply.h000066400000000000000000000121711176363201700221510ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: reply.h Web Oriented Programming Interface Object encapsulating reply. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 18 Feb 2010 14:22:38 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_WOPI_REPLY #define FALCON_WOPI_REPLY #include #include #include #define FALCON_WOPI_DEFAULT_REPLY_STATUS 200 #define FALCON_WOPI_DEFAULT_REPLY_REASON "Ok" namespace Falcon { namespace WOPI { class CookieParams { public: CookieParams(); CookieParams& value( const String& val ) { m_value = val; m_bValueGiven = true; return *this; } CookieParams& path( const String& val ) { m_path = val; return *this; } CookieParams& domain( const String& val ) { m_domain = val; return *this; } CookieParams& expire_string( const String& val ) { m_expire_string = val; return *this; } CookieParams& version( int v ) { m_version = v; return *this; } CookieParams& max_age( int v ) { m_max_age = v; return *this; } CookieParams& secure( bool v ) { m_secure = v; return *this; } CookieParams& httpOnly( bool v ) { m_httpOnly = v; return *this; } CookieParams& expire( TimeStamp* v ) { m_expire = v; return *this; } String m_value; String m_path; String m_domain; TimeStamp* m_expire; String m_expire_string; int m_max_age; int m_version; bool m_secure; bool m_httpOnly; bool m_bValueGiven; }; /** Class encapsulating an HTTP reply. Final users must re-implement: - startCommit() to send the first response reply. - commitHeader() that will receive every header and. - endCommit() when all the headers are sent. */ class Reply: public CoreObject { public: Reply( const CoreClass* base ); virtual ~Reply(); /** Sets a cookie. Use a variable-parameter idiom provider called CookieParams to configure the contents of this cookie. \return true if the cookie can be set, false if the headers are sent * */ bool setCookie( const String& cname, const CookieParams& params ); /** Clears a cookie. Removes this cookie from the remote browser. */ void clearCookie( const String& cname ); /** Sets the required header to the given value. */ bool setHeader( const String& fname, const String& value ); /** Removes the required header. \return true if the header is found, false if the headers have been sent or if the header is not present. */ bool removeHeader( const String& fname ); /** Gets the value of the required header. \return true if the header name is found, false otherwise. */ bool getHeader( const String& fname, String& value ); //! Helper to set the content-type bool setContentType( const String& type ); //! Helper to set the content-type bool setContentType( const String& type, const String& subtype ); //! Helper to set the content-type and charset elements. bool setContentType( const String& type, const String& subtype, const String& encoding ); //! Helper to redirect a page bool setRedirect( const String& url, uint32 timeout=0 ); //! Gets all the headers as a dictionary. CoreDict* getHeaders(); //! Send to the final stream (to be re-implemented by the final renderer). virtual bool commit(); //! true if we have perfomed commit bool isCommited() const { return m_bHeadersSent; } /** Output stream used by this reply. It is created by the commit process invoking makeOutputStream(). In case a specific transcoding is required (i.e. the content-type + encoding is set), the stream is wrapped with the correct transcoder after makeOutputStream() is invoked. The stream is property of this reply and is destroyed with it. The VM only knows ReplyStream instances, which are not owning this reply nor the final stream. However, a close on one of VMachine::stdErr() or VMachine::stdOut() will be reflected here. */ Stream* output() const { return m_output; } // Overrides virtual CoreObject *clone() const; virtual bool setProperty( const String &prop, const Item &value ); virtual bool getProperty( const String &prop, Item &value ) const; virtual void gcMark( uint32 mark ); protected: //! Invoked when the commit operation is about to begin. virtual void startCommit() = 0; //! Invoked right before startCommit to create an output stream. virtual Stream* makeOutputStream() = 0; //! Invoked to finalize an header after makeOutputStream. virtual void commitHeader( const String& hname, const String& hvalue ) = 0; //! Invoked when all the headers are generated. virtual void endCommit() = 0; // dictionaries Utils::StringMap m_mHeaders; Utils::StringMap m_mCookies; int32 m_nStatus; String m_sReason; String m_sEncoding; bool m_bHeadersSent; bool m_bDefaultContent; Stream* m_output; }; } } #endif /* end of reply.h */ modules/native/wopi/include/falcon/wopi/reply_ext.h000066400000000000000000000013331176363201700230270ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: request_ext.cpp Web Oriented Programming Interface Request class script interface ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 14 Feb 2010 13:27:55 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef _FALCON_WOPI_REPLY_EXT_H #define _FALCON_WOPI_REPLY_EXT_H #include namespace Falcon { namespace WOPI { void InitReplyClass( Module* m, ObjectFactory cff, ext_func_t init_func = 0 ); } } #endif /* end of reply_ext.h */ modules/native/wopi/include/falcon/wopi/replystream.h000066400000000000000000000034321176363201700233650ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: replystream.h Falcon WOPI - Web Oriented Programming Interface This is a dummy stream sensing for the first output operation and then invoking the Reply for commit. The stream is then destroyed and the first write operation is invoked on the real stream. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Mar 2010 16:47:02 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef REPLY_STREAM_H #define REPLY_STREAM_H #include namespace Falcon { namespace WOPI { class Reply; /** This is a dummy stream sensing for the first output operation and then invoking the Reply for commit. The stream is then destroyed and the first write operation is invoked on the real stream. */ class ReplyStream: public Stream { public: ReplyStream( Reply* rep ); ReplyStream( const ReplyStream& other ); ~ReplyStream(); // We don't really need to implement all of those; // as we want to reimplement output streams, we'll just // set "unsupported" where we don't want to provide support. bool writeString( const String &source, uint32 begin=0, uint32 end = csh::npos ); virtual bool close(); virtual int32 write( const void *buffer, int32 size ); virtual int32 writeAvailable( int, const Sys::SystemData* ); virtual int64 lastError() const; virtual bool put( uint32 chr ); virtual bool get( uint32 &chr ); virtual Stream *clone() const; // Flushes the stream. virtual bool flush(); private: Reply* m_rep; }; } } #endif /* end of replystream.h */ modules/native/wopi/include/falcon/wopi/request.h000066400000000000000000000215171176363201700225120ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: request.h Web Oriented Programming Interface Object encapsulating requests. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 13 Feb 2010 12:29:23 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_WOPI_REQUEST #define FALCON_WOPI_REQUEST #define DEFAULT_SESSION_FIELD "SID" #include #include #include #include #include #include namespace Falcon { namespace WOPI { #define FALCON_WOPI_MAXMEMUPLOAD_ATTRIB "wopi_maxMemUpload" #define FALCON_WOPI_TEMPDIR_ATTRIB "wopi_tempDir" class SessionManager; class CoreRequest; class Reply; class Request: public BaseAlloc { public: Request(); virtual ~Request(); //========================================================= // Main operations // //! Do a complete parse of the whole input (headrs and body) bool parse( Stream* input ); //! parse the header part. bool parseHeader( Stream* input ); //! Parses the body. bool parseBody( Stream* input ); /** Create a generic usage temporary file. */ Stream* makeTempFile( String& fname, int64& le ); //========================================================= // Overridable // virtual const String& getTempPath() const { return m_sTempPath; } virtual int64 getMemoryUpload() const { return m_nMaxMemUpload; } //========================================================= bool getField( const String& fname, String& value ) const; bool getField( const String& fname, Item& value ) const; bool getFieldOrArray( const String& fname, Item& value ) const { if ( ! getField( fname, value ) ) return getField(fname + "[]", value ); return true; } void fwdGet( String& fwd, bool all=false ) const; void fwdPost( String& fwd, bool all=false ) const; const String& getSessionFieldName() const { return m_sSessionField; } void setSessionFieldName( const String& name ) { m_sSessionField = name; } /** Token created by the session manager for this requet. */ uint32 sessionToken() const { return m_nSessionToken; } void sessionToken( uint32 st ) { m_nSessionToken = st; } bool setURI( const String& uri ); const URI& parsedUri() const { return m_uri; } URI& parsedUri() { return m_uri; } const String& getUri() const { return m_sUri; } bool addPartData( byte* data, int length ); bool parsePartHeaderLine( const String& line ); const PartHandler& partHandler() const { return m_MainPart; } PartHandler& partHandler() { return m_MainPart; } //! Sets the maximum size that for uploading a part. void setMaxMemUpload( int64 mm ) { m_nMaxMemUpload = mm; } //! Sets the location for temporary files. void setUploadPath( const String& path ) { m_sTempPath = path; } /** Adds a temporary file. The VM tries to delete all the temporary files during its destructor. On failure, it ingores the problem and logs an error in Apache. */ void addTempFile( const Falcon::String &fname ); /** Gets the list of temporary files. Before the VM is destroyed, this should be taken out so that it is then possible to get rid of the files. Using removeTempFiles after the VM has been destroyed ensures that all the streams open by the VM are closed (as this is done during the GC step). \return an opaque pointer to an internal structure. */ void* getTempFiles() const { return m_tempFiles; } /** Removes from the disk a list of temporary files. Using removeTempFiles after the VM has been destroyed ensures that all the streams open by the VM are closed (as this is done during the GC step). \param head The valued returned from getTempFiles() before the VM was destroyed. \param data Opaque pointer passed as extra data to the error_func (can be 0 if not used). \param error_func callback that will be invoked in some file can't be deleted. */ static void removeTempFiles( void* head, void* data, void (*error_func)(const String& msg, void* data) ); void startedAt( Falcon::numeric t ) { m_startedAt = t; } Falcon::numeric startedAt() const { return m_startedAt; } CoreDict* gets() const { return m_gets; } CoreDict* posts() const { return m_posts; } CoreDict* cookies() const { return m_cookies; } CoreDict* headers() const { return m_headers; } // Generic request informations String m_protocol; String m_method; String m_location; String m_filename; String m_path_info; String m_args; String m_remote_ip; // Authorization fields String m_ap_auth_type; String m_user; // Post-request parse fields. String m_content_type; String m_content_encoding; PartHandler m_MainPart; // quantitative informations int64 m_request_time; int64 m_bytes_sent; int64 m_content_length; String m_sUri; protected: void forward( const ItemDict& main, const ItemDict& aux, String& fwd, bool all ) const; // generate the headers when first requested. void makeHeaders(); // Create the headers in a canonical form //CoreDict* makeCanonicalHeaders(); // dictionaries CoreDict* m_gets; CoreDict* m_posts; CoreDict* m_cookies; CoreDict* m_headers; String m_sSessionField; URI m_uri; class TempFileEntry { public: Falcon::String m_entry; TempFileEntry* m_next; TempFileEntry( const Falcon::String &fname ): m_entry( fname ), m_next(0) {} }; // Used to remember which files to delete at end. TempFileEntry *m_tempFiles; String m_sTempPath; int64 m_nMaxMemUpload; uint32 m_nSessionToken; Falcon::numeric m_startedAt; friend class CoreRequest; GarbageLock* m_lockGets; GarbageLock* m_lockPosts; GarbageLock* m_lockHeaders; GarbageLock* m_lockCookies; }; /** Script side Request wrapper. This class implements a Request object as seen by the Falcon script side. It's mainly a wrapper for Request class with some utilities for the falcon scripts using it. A Request can be created elsewhere and passed to this class via its init() method, or this class will create its own Request at init(). */ class CoreRequest: public CoreObject { public: CoreRequest( const CoreClass* base ); virtual ~CoreRequest(); /** Construct this object after the initial creation. This allows to build the object after it has been pre-created by the virtual machine. @param upd The coreclass serving as the generator */ void init( CoreClass* upd, Reply* reply, SessionManager* sm, Request* r=0 ); Request* base() const { return m_base; } /** Override this to be called back at first usage. Also, set m_bPostInit to true; in this way, this virtual function will be called back the first time setProperty or getProperty is called on this object. */ virtual void postInit() {} /** Adds uploaded parts and process multipart fields if the request has a multipart body. This should ALWAYS be called after the process is fully parsed to translate each parts into script-available objects. \return true If the request has a multipart body. */ virtual bool processMultiPartBody(); /** Configure this request using the given module. Uses the given module attributes to configure: - The temporary upload path (cgi_tempDir). - The maximum upload did size (cgi_maxMemUpload). */ void configFromModule( const Module* mod ); //===================================================== // Utilities for script // /** Get the session manager. */ SessionManager* smgr() const { return m_sm; } //===================================================== // Overrides from CoreObject // virtual CoreObject *clone() const; virtual bool setProperty( const String &prop, const Item &value ); virtual bool getProperty( const String &prop, Item &value ) const; virtual void gcMark( uint32 mark ); static CoreObject* factory( const Falcon::CoreClass* cls, void* ud, bool bDeser ); Reply* reply() const { return m_reply; } bool autoSession() const { return m_bAutoSession; } void provider( const String& s ) { m_provider = s; } protected: //! Creates an uploaded element (in the post fields) out of the data in partHandler void addUploaded( PartHandler* ph, const String& prefix = "" ); String m_provider; SessionManager* m_sm; CoreClass* m_upld_c; bool m_bPostInit; Request* m_base; Reply* m_reply; bool m_bAutoSession; }; } } #endif /* end of request.h */ modules/native/wopi/include/falcon/wopi/request_ext.h000066400000000000000000000013431176363201700233650ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: request_ext.cpp Web Oriented Programming Interface Request class script interface ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 14 Feb 2010 13:27:55 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef _FALCON_WOPI_REQUEST_EXT_H #define _FALCON_WOPI_REQUEST_EXT_H #include namespace Falcon { namespace WOPI { void InitRequestClass( Module* m, ObjectFactory cff, ext_func_t init_func = 0 ); } } #endif /* end of request_ext.h */ modules/native/wopi/include/falcon/wopi/session_manager.h000066400000000000000000000205421176363201700241740ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: session_manager.h Falcon Web Oriented Programming Interface Session manager. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 11 Feb 2010 23:17:11 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef _SESSION_MANAGER_H #define _SESSION_MANAGER_H #define FALCON_WOPI_SESSION_TO_ATTRIB "wopi_sessionTO" #include #include #include namespace Falcon { namespace WOPI { /** Abstract class representing a single session data. Subclasses should reimplement the methods assign, release and dispose to manage system resources related with this session data. */ class SessionData { public: SessionData( const String& SID ); virtual ~SessionData(); /** Resumes a suspended session. Suppose a user code restart (i.e. after a server HUP, or the new invocation of a CGI script). The user may ask for a session item via a HTTP query which is not currently existing in the session map but that is available on some persistent storage. Resume is invoked after a getSession has not found this session object in the session map, and thus has created it. It may return false if the resume fails, or if this particular subclass of SessionData is not resume-able (i.e. if it's fully memory based, not providing any persistency). This method must also have the acquire semantic (possibly calling it). */ virtual bool resume() = 0; /** Stores a session to persistent media. */ virtual bool store() = 0; /** Disposes this session. This is call right before the destructor to allow the session to try to perform system operations that may fail. Subclasses should implement this function so that all the system resources allocated to manage this session are removed. */ virtual bool dispose() = 0; /** Assigns the session to a certain assignee. An assigned session never expires and cannot be assigned to another request (getSession in the session manager shall fail). Subclasses must remember to update the m_assignee field. \note This should be called in the session lock context. */ void assign( uint32 assignee ) { m_assignee = assignee; } /** Releases a session. \note This should be called in the session lock context. */ void release() { m_assignee = 0; } /** Indicates that this session has been used now. This marks the last time this session is used, preventing early expiration. */ void touch(); void setError( int e ); void setError( const String& edesc, int e = 0 ); //==================================================================== // accessors CoreDict* data() const { return m_dataLock.item().asDict(); } bool isAssigned() const { return m_assignee != 0; } uint32 assigned() const { return m_assignee; } /** Returns the last time (relative to Falcon::Sys::_seconds) this session has been used. */ numeric lastTouched() const { return m_touchTime; } const String& errorDesc() const { return m_errorDesc; } int lastError() const { return m_lastError; } const String& sID() const { return m_sID; } bool isInvalid() const { return m_bInvalid; } void setInvalid() { m_bInvalid = true; } /** The weak reference to this class */ class WeakRef { public: WeakRef( SessionData* r ): m_ref(r), m_bDropped(false) { } void onDestroy() { if( m_bDropped ) delete this; else m_ref = 0; } SessionData* get() const { return m_ref; } void dropped() { if ( m_ref == 0 ) delete this; else m_bDropped = true; } private: SessionData* m_ref; bool m_bDropped; }; /** Gets a weak reference to this class. The reference pointer is turned into 0 when the object is destroyed, or otherwise invalidated. */ WeakRef* getWeakRef(); /** Called during invalidation or destruction to clear weak references. Actually, it should be called when the session manager is locked. */ void clearRefs(); protected: GarbageLock m_dataLock; uint32 m_assignee; private: numeric m_touchTime; int m_lastError; String m_errorDesc; String m_sID; bool m_bInvalid; mutable std::list m_reflist; }; /** Repository for session variables. */ class SessionManager { public: SessionManager(); virtual ~SessionManager(); /** Gets an existing session for a particular user. If the required session doesn't exist, a new session is created. On first call, the token shall be zero. An unique session token is then given, and can be used to ask for new sessions opened in the same context. When the users of the session created in this context is terminated, the session token can be used to close all the related sessions. \note If the session doesn't exist, getSession tries to create it via createSession() and then SessionData::resume(). \param sSID Session identificator. \parma token A session owner identifier. \return A core dictionary containing the session data. */ SessionData* getSession( const String& sSID, uint32 token ); /** Starts a new session. This method is similar to getSession, but it creates a new session, without calling SessionData::resume() */ SessionData* startSession( uint32 token ); /** Tries to start a new session. * This version allows an external source to decide the ID of the new session. It is useful in case of unique IDs being sent during multiple step authentication schemes as OAUTH. */ SessionData* startSession( uint32 token, const Falcon::String &sSID ); /** Clears all the sessions that have been opened by the given user. Call this when the session user associated with this token doesn't exist anymore. All the released sessions are first touched, then stored and finally released. \note This function must be called in the same thread of expireOldSessions to avoid concurrent expiration & storage (there can't be any mutex protection against this. A refcount protection may prevent crashes, but may cause dirty files and memory leaks to be left on the ground). */ bool releaseSessions( uint32 token ); /** Creates an unique session token. */ uint32 getSessionToken(); /** Closes a session explicitly, freeing its data. */ bool closeSession( const String& sSID, uint32 token ); void timeout( uint32 to ) { m_nSessionTimeout = to; } uint32 timeout() const { return m_nSessionTimeout; } /** Prepare first execution. To be called after a complete configuration. */ virtual void startup() = 0; virtual void configFromModule( const Module* mod ); protected: /** Creates a session adequate for this session manager. Must be fast. */ virtual SessionData* createSession( const String& sSID ) = 0; /** Creates a unique ID and returning atomically a garbage lock. This also creates an empty session record in the SessionMap. */ SessionData* createUniqueId( String& sSID ); private: /** Delete sessions that are expired in time. \note This function must be called in the same thread of releaseSessions() to avoid concurrent expiration & storage (there can't be any mutex protection against this. A refcount protection may prevent crashes, but may cause dirty files and memory leaks to be left on the ground). */ void expireOldSessions(); typedef std::map SessionMap; typedef std::list SessionList; typedef std::map SessionUserMap; typedef std::multimap ExpirationMap; //Rightful owner of sessions SessionMap m_smap; // Owning weakrefs SessionUserMap m_susers; ExpirationMap m_expmap; mutable Falcon::Mutex m_mtx; uint32 m_nLastToken; uint32 m_nSessionTimeout; }; } } #endif /* end of session_manager.h */ modules/native/wopi/include/falcon/wopi/sharedmem.h000066400000000000000000000135421176363201700227660ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: sharedmem.h Stream on a shared disk file. TODO: Move this file in the main engine in the next version. Interprocess shared-memory object. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 24 Apr 2010 12:12:10 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef _FALCON_SHAREDMEM_H_ #define _FALCON_SHAREDMEM_H_ #include #include #include #include namespace Falcon { class SharedMemPrivate; /** Shared memory abstraction. This class allows a closed, black-box shared memory area to exchange data between Falcon applications. The data in the shared memory cannot be accessed directly by the applications. Instead, it must be read or written atomically to some process-specific memory, and then accessed from there. Even if generating extra reads and writes at each passage, this method avoids that programming errors in one application (which may be a script) block the others. The local memory where the shared memory must be copied is accessed in form of a Stream. The stream interface is extremely efficient for memory based operation, and has already all the needed policies for dynamic memory management, and read-write size recording. The shared memory can be backed on a system file or just be created transient. @TODO write about the read-commit process. */ class SharedMem: public BaseAlloc { public: /** Creates a non-backed up shared memory object. @throws Falcon::IoError if the memory object cannot be created. */ SharedMem( const String &name ); /** Creates a shared memory object backed up on the given system file. @throws Falcon::IoError if the memory object cannot be created, or if the file cannot be created. If the file exists when the object is created, the lastVersion() of this object is updated so that the first read is known to be aligned. Backup policies are left to the underlying O/S. */ SharedMem( const String &name, const String &filename ); /** Frees shared memory resources. */ ~SharedMem(); /** Reads the content of the shared memory in the stream. If the stream is a memory based stream, this translates quite directly in a raw memcopy, with correct allocation strategies put in place for you. You can also use this method to write the contents of the shared memory in a different kind of stream, but be aware that the shared memory is held (read-lock) for the whole time needed for the copy. So, it's often preferable to read in a memory based stream and then transfer its content to a file. The content is read starting from the current position in the stream, and the file pointer is left past the full read position. You may want to truncate the file after the read is complete. The read will refresh the version reported by lastVersion(). If it is detected that the version didn't change, read returns false and nothing is done, unless bAlwaysRead is true. In that case, the read is performed even if the current version of the memory is detected to be the same as in the previous read. \param target The stream where to read the memory data. \param bAlwaysRead set to true to read the data even if the memory is still aligned. \return false if the data was not read (because of a mis-alignment in versions). */ bool read( Stream* target, bool bAlwaysRead = false ); /** Writes the contents of the target stream on the shared memory. If the stream is a memory based stream, this translates quite directly in a raw memcopy, with correct allocation strategies put in place for you. The commit takes into account the lastVersion() value. If the current version in the shared memory has been changed, the function returns false. If the bReread parameter is true, the target stream is read atomically, and lastVersion() is updated to the current version. To prevent the contents of the stream to be overwritten, pass bReread = false. In case of successful commit, the current version of the shared memory and the lastVersion() reported by this object are updated. \param source The stream where the source for the storage is located. \param bReread if true (default), will overwrite the contents of source. \param size The size of the data to be commited. \return true if the commit was possible, false if the version was changed in the meanwhile. */ bool commit( Stream* source, int32 size, bool bReread = true ); /** Return the current version of the memory. This method is meant to minimize operations in case of mis-alignments. It reads the current version of the shared memory. In case it is not the same as lastVersion(), the caller can safely assume that a commit() would fail, and avoid prepare the data to be stored. If it is the same as lastVersion(), then a commit MAY succeed, but it may still fail, so the caller need tobe prepared to this eventuality. */ uint32 currentVersion() const; /** Returns the latest version that has been read or committed. Initially, the shared memory has version 1, and the local view of the memory has the same version. */ uint32 lastVersion() const { return m_version; } private: // Private D-pointer SharedMemPrivate* d; uint32 m_version; void internal_build( const String &name, const String &filename ); void init(); void close(); void internal_read( Stream* target ); }; } #endif /* _FALCON_SHAREDMEM_H_ */ /* end of sharedmem.h */ modules/native/wopi/include/falcon/wopi/uploaded_ext.h000066400000000000000000000013041176363201700234670ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: uploaded_ext.h Web Oriented Programming Interface Object encapsulating requests. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 23 Apr 2010 11:24:16 -0700 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef WOPI_UPLOADED_EXT_H #define WOPI_UPLOADED_EXT_H #include #include namespace Falcon { namespace WOPI { void InitUploadedClass( Module* m ); } } #endif /* end of uploaded_ext.h */ modules/native/wopi/include/falcon/wopi/utils.h000066400000000000000000000026071176363201700221610ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: utils.h Web Oriented Programming Interface (WOPI) Utilities. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 13 Feb 2010 14:10:56 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_WOPI_UTILS_H_ #define FALCON_WOPI_UTILS_H_ #include #include #include #include namespace Falcon { class CoreDict; class CoreObject; namespace WOPI { namespace Utils { typedef std::map StringMap; void fieldsToUriQuery( const ItemDict& fields, String& target ); CoreObject* makeURI( const URI& uri ); void parseQuery( const String &query, ItemDict& dict ); void parseQueryEntry( const String &query, ItemDict& dict ); void addQueryVariable( const String &key, const Item& value, ItemDict& dict ); bool parseHeaderEntry( const String &line, String& key, String& value ); void makeRandomFilename( String& target, int size ); void unescapeQuotes( Falcon::String &str ); void dictAsInputFields( String& fwd, const ItemDict& items ); void htmlEscape( const String& source, String& fwd ); void xrandomize(); } } } #endif /* UTILS_H_ */ /* end of utils.h */ modules/native/wopi/include/falcon/wopi/version.h000066400000000000000000000012031176363201700224750ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: version.h Falcon WOPI - Web Oriented Programming Interface Version definition. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2007-07-28 18:55:17 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef VERSION_H #define VERSION_H #define VERSION_MAJOR 1 #define VERSION_MINOR 2 #define VERSION_REVISION 0 #define VERSION_DESC "1.2" #endif /* end of version.h */ modules/native/wopi/include/falcon/wopi/wopi.h000066400000000000000000000044561176363201700220030ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: wopi.h Falcon Web Oriented Programming Interface. Global WOPI application objects. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 25 Apr 2010 17:02:10 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef _FALCON_WOPI_WOPI_H_ #define _FALCON_WOPI_WOPI_H_ #include #include #include #include #include #include #define FALCON_WOPI_PDATADIR_ATTRIB "wopi_pdataDir" #include namespace Falcon { namespace WOPI { class Wopi: public BaseAlloc { public: Wopi(); virtual ~Wopi(); bool setData( Item& data, const String& appName, bool atomicUpdate ); bool getData( Item& data, const String& appName ); bool setPersistent( const String& id, const Item& data ); bool getPeristent( const String& id, Item& data ) const; void dataLocation( const String& loc ); String dataLocation(); private: void inner_readData( SharedMem* shmem, Item& data ); SharedMem* inner_create_appData( const String& appName ); Mutex m_mtx; typedef std::map AppDataMap; /** Persistent data map. We have one of theese per thread. */ typedef std::map PDataMap; AppDataMap m_admap; String m_sAppDataLoc; /** Persistent data. */ ThreadSpecific m_pdata; static void pdata_deletor( void* ); }; /** Main WOPI object. Central object of the WOPI system. */ class CoreWopi: public CoreObject { public: CoreWopi( const CoreClass* parent ); virtual ~CoreWopi(); virtual CoreObject *clone() const; virtual bool setProperty( const String &prop, const Item &value ); virtual bool getProperty( const String &prop, Item &value ) const; void configFromModule( const Module* mod ); static CoreObject* factory( const CoreClass *cls, void *user_data, bool bDeserializing ); Wopi* wopi() const { return m_wopi; } void setWopi( Wopi* w ) { m_wopi = w; } private: Wopi* m_wopi; }; } } #endif /* _FALCON_WOPI_WOPI_H_ */ /* end of wopi.h */ modules/native/wopi/include/falcon/wopi/wopi_ext.h000066400000000000000000000013761176363201700226610ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: wopi_ext.h Falcon Web Oriented Programming Interface. Main module generator ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Feb 2010 16:19:57 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #ifndef FALCON_WOPI_EXT_H #define FALCON_WOPI_EXT_H #include namespace Falcon{ namespace WOPI { Falcon::Module *wopi_module_init( ObjectFactory rqf, ObjectFactory rpf, ext_func_t rq_init_func=0, ext_func_t rp_init_func=0 ); } } #endif /* end of wopi_ext.h */ modules/native/wopi/src/000077500000000000000000000000001176363201700155675ustar00rootroot00000000000000modules/native/wopi/src/CMakeLists.txt000066400000000000000000000016361176363201700203350ustar00rootroot00000000000000###################################################################### # CMake file for falcon WOPI sources # ####################################################################### # Targets # # Inclusion settings include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include ${Falcon_INCLUDE_DIRS} ) if(WIN32) set( _files_sys sharedmem_win.cpp ) else() set( _files_sys sharedmem_posix.cpp ) endif() set(_files ${_files_sys} error_ext.cpp file_sm.cpp mem_sm.cpp parthandler.cpp reply.cpp reply_ext.cpp replystream.cpp request.cpp request_ext.cpp session_manager.cpp utils.cpp uploaded_ext.cpp wopi.cpp wopi_ext.cpp ) unset(WOPI_SOURCES) foreach(item ${_files}) list(APPEND WOPI_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/${item}) endforeach(item) # Sources files the library is built from. set(WOPI_SOURCES ${WOPI_SOURCES} PARENT_SCOPE) modules/native/wopi/src/error_ext.cpp000066400000000000000000000043771176363201700203170ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: error_ext.cpp Error for WOPI exceptions. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 28 Mar 2010 17:12:01 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include namespace Falcon { namespace WOPI { /*# @beginmodule WOPI */ /*# @class WopiError @brief Error generated by wopi in case of failures. @optparam code A numeric error code. @optparam description A textual description of the error code. @optparam extra A descriptive message explaining the error conditions. @from Error code, description, extra Possible error codes are: - WopiError.SessionFailed - Failed to restore a session. - WopiError.SessionInvalid - Invalid session ID. */ void WopiError_init ( ::Falcon::VMachine *vm ) { CoreObject *einst = vm->self().asObject(); if( einst->getUserData() == 0 ) einst->setUserData( new WopiError ); ::Falcon::core::Error_init( vm ); } void InitErrorClass( Module* self ) { // create a singleton instance of %Request class Falcon::Symbol *error_class = self->addExternalRef( "Error" ); // it's external Falcon::Symbol *c_wopi_error = self->addClass( "WopiError", &WopiError_init ); c_wopi_error->getClassDef()->addInheritance( new Falcon::InheritDef( error_class ) ); self->addClassProperty( c_wopi_error, "SessionError" ) .setInteger( FALCON_ERROR_WOPI_SESS_IO ); self->addClassProperty( c_wopi_error, "SessionExpired" ) .setInteger( FALCON_ERROR_WOPI_SESS_EXPIRED ); self->addClassProperty( c_wopi_error, "AppdataSer" ) .setInteger( FALCON_ERROR_WOPI_APPDATA_SER ); self->addClassProperty( c_wopi_error, "AppdataDeser" ) .setInteger( FALCON_ERROR_WOPI_APPDATA_DESER ); self->addClassProperty( c_wopi_error, "InvalidSessionID" ) .setInteger( FALCON_ERROR_WOPI_SESS_INVALID_ID ); } } } /* end of error_ext.cpp */ modules/native/wopi/src/file_sm.cpp000066400000000000000000000101111176363201700177030ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: file_sm.cpp Falcon Web Oriented Programming Interface File based session manager. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 27 Mar 2010 15:04:36 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #define FALCON_SESSION_DATA_EXT ".fsd" namespace Falcon { namespace WOPI { //============================================================ // File based session data // FileSessionData::FileSessionData( const String& SID, const String& tmpDir ): SessionData( SID ), m_tmpDir( tmpDir ) { } FileSessionData::~FileSessionData() { } bool FileSessionData::resume() { String sFile = m_tmpDir + "/" + sID() + FALCON_SESSION_DATA_EXT; FileStat fst; if ( ! Sys::fal_stats( sFile, fst ) ) { // is this a new session, or is the directory plainly wrong? if ( (! Sys::fal_stats( m_tmpDir, fst )) || fst.m_type == FileStat::t_normal ) { setError( "Not a directory " + m_tmpDir ); return false; } // it's a new session! return false; } else { // deserialize from this file. FileStream fs; if ( ! fs.open( sFile, FileStream::e_omReadOnly ) ) { setError( "Can't open session file " + sFile ); return false; } // don't use a VM to deserialize if( m_dataLock.item().deserialize( &fs, VMachine::getCurrent() ) != Item::sc_ok ) { setError( "Deserialization failed from " + sFile ); // but let the user see the error. return false; } } return true; } bool FileSessionData::store() { FileStream fs; String sFile = m_tmpDir + "/" + sID() + FALCON_SESSION_DATA_EXT; // try to create the file. if ( ! fs.create( sFile, FileStream::e_aUserWrite | FileStream::e_aUserRead ) ) { setError( "Serialization to file failed: " + sFile ); return false; } return m_dataLock.item().serialize( &fs ) == Item::sc_ok; } bool FileSessionData::dispose() { String sFile = m_tmpDir + "/" + sID() + FALCON_SESSION_DATA_EXT; int32 fsStatus; return Sys::fal_unlink( sFile, fsStatus ); } //============================================================ // File based session manager // FileSessionManager::FileSessionManager( const String& tmpDir ): m_tmpDir( tmpDir ) { } void FileSessionManager::startup() { // delete all the expired sessions. if( timeout() == 0 ) return; int32 err; DirEntry* entry = Sys::fal_openDir( m_tmpDir, err ); if( entry == 0 ) return; String name; TimeStamp now; now.currentTime(); while( entry->read( name ) ) { if ( name.endsWith( FALCON_SESSION_DATA_EXT ) ) { String sFullName = m_tmpDir + "/" + name; FileStat st; if( Sys::fal_stats( sFullName, st ) ) { st.m_mtime->distance( now ); if( (st.m_mtime->m_day * 3600*24 + st.m_mtime->m_hour * 3600 + st.m_mtime->m_minute * 60 + st.m_mtime->m_second ) > (int) timeout() ) { Sys::fal_unlink( sFullName, err ); } } } } } FileSessionManager::~FileSessionManager() { } void FileSessionManager::configFromModule( const Module* mod ) { AttribMap* attribs = mod->attributes(); if( attribs == 0 ) { return; } VarDef* value = attribs->findAttrib( FALCON_WOPI_TEMPDIR_ATTRIB ); if( value != 0 && value->isString() ) { m_tmpDir.bufferize( *value->asString() ); } SessionManager::configFromModule( mod ); } SessionData* FileSessionManager::createSession( const Falcon::String& sSID ) { return new FileSessionData( sSID, m_tmpDir ); } } } /* end of file_sm.cpp */ modules/native/wopi/src/fwdata.cpp000066400000000000000000000015251176363201700175440ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: fwdata.cpp Falcon Web Oriented Programming Interface Framework data manager and framework data. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 19 Apr 2010 20:51:59 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #define FALCON_SESSION_DATA_EXT ".fsd" namespace Falcon { namespace WOPI { //============================================================ // File based session data // FWDataManager::FWDataManager( const String& SID, const String& tmpDir ): SessionData( SID ), m_tmpDir( tmpDir ) { } } } /* end of fwdata.cpp */ modules/native/wopi/src/mem_sm.cpp000066400000000000000000000025151176363201700175530ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: mem_sm.cpp Falcon Web Oriented Programming Interface Memory based session manager. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 13 Mar 2010 10:25:11 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include namespace Falcon { namespace WOPI { //============================================================ // Memory based session data // MemSessionData::MemSessionData( const String& SID ): SessionData( SID ) { } MemSessionData::~MemSessionData() { } bool MemSessionData::resume() { return true; } bool MemSessionData::store() { // nothing to do return true; } bool MemSessionData::dispose() { // nothing to do return true; } //============================================================ // Memory based session manager // MemSessionManager::MemSessionManager() { } MemSessionManager::~MemSessionManager() { } void MemSessionManager::startup() { // nothing to do } SessionData* MemSessionManager::createSession( const Falcon::String& sSID ) { return new MemSessionData( sSID ); } } } /* end of mem_sm.cpp */ modules/native/wopi/src/parthandler.cpp000066400000000000000000000454741176363201700206150ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: parthandler.cpp Web Oriented Programming Interface RFC1867 compliant upload handler. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 26 Feb 2010 20:29:47 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include //#define TRACE( fmt, ... ) fprintf( stderr, "%d: " fmt "\n", __LINE__, __VA_ARGS__ ); fflush(stderr); #define TRACE( fmt, ... ) namespace Falcon { namespace WOPI { HeaderValue::HeaderValue() { } HeaderValue::HeaderValue( const String& value ) { parseValuePart( value ); } HeaderValue::HeaderValue( const HeaderValue& other ): m_lValues( other.m_lValues ), m_mParameters( other.m_mParameters ) { } HeaderValue::~HeaderValue() { } bool HeaderValue::parseValue( const String& v ) { TRACE( "parseValue: %s", v.getRawStorage() ); m_sRawValue = v; uint32 pos=0, pos1=0; // get the value pos = v.find(";"); if( pos == String::npos ) { // eventually, parse it also as parameters // --- but don't complain if it's not in the format V=N parseParamPart( v ); return parseValuePart( v ); } // else if( ! parseValuePart( v.subString(0,pos) ) ) return false; // eventually, parse it also as parameters // --- but don't complain if it's not in the format V=N parseParamPart( v.subString(0,pos) ); // now parse all the parameters ++pos; while( (pos1 = v.find(";", pos) ) != String::npos ) { if( ! parseParamPart( v.subString(pos, pos1) ) ) return false; pos = pos1+1; } // the last parameter return parseParamPart( v.subString(pos) ); } bool HeaderValue::parseValuePart( const String& v ) { TRACE( "parseValuePart: %s", v.getRawStorage() ); uint32 pos=0, pos1=0; // now parse all the parameters while( (pos1 = v.find(",", pos) ) != String::npos ) { m_lValues.push_back( URI::URLDecode( v.subString(pos, pos1) ) ); m_lValues.back().trim(); pos = pos1+1; } m_lValues.push_back( URI::URLDecode( v.subString(pos) ) ); m_lValues.back().trim(); return true; } bool HeaderValue::parseParamPart( const String& v ) { TRACE( "parseParamPart: %s", v.getRawStorage() ); uint32 pos = v.find( "=" ); if( pos == String::npos ) return false; String sKey = v.subString(0,pos); sKey.trim(); String sValue = v.subString(pos+1); sValue.trim(); if( sValue.startsWith("\"") && sValue.endsWith("\"") ) { sValue = sValue.subString(1,sValue.length()-1); } // prevent misunderstading effectively empty values if( sValue.size() == 0 ) { m_mParameters[sKey] = ""; return true; } // Is URI encoded? String sValFinal = URI::URLDecode( sValue ); if( sValFinal.size() == 0 ) { // if it's not URI encoded, then it must be UTF-8 encoded // also, no valid utf-8 encoding can be uri encoded, so... String sValDec; sValue.c_ize(); sValDec.fromUTF8( (char*) sValue.getRawStorage() ); m_mParameters[sKey] = sValDec; } else { m_mParameters[sKey] = sValFinal; } return true; } void HeaderValue::setRawValue( const String& v ) { TRACE( "setRawValue: %s", v.getRawStorage() ); m_sRawValue = v; m_lValues.push_front( v ); } //====================================================== // PartHandler::PartHandler(): m_pBuffer( 0 ), m_bOwnBuffer( false ), m_nPartSize(-1), // Initially, think we're the main part. m_pToBodyLeft(&m_nBodyLeft), m_nBodyLeft(1), m_nReceived(0), m_stream(0), m_str_stream(0), m_pNextPart( 0 ), m_pSubPart( 0 ), m_owner( 0 ), m_bPartIsFile( false ), m_bUseMemoryUpload( false ) { m_nBodyLeft <<= 62; } PartHandler::PartHandler( const String& sBound ): m_pBuffer( 0 ), m_bOwnBuffer( false ), m_sEnclosingBoundary( sBound ), m_nPartSize(-1), m_pToBodyLeft(&m_nBodyLeft), m_nBodyLeft(1), m_nReceived(0), m_stream(0), m_str_stream(0), m_pNextPart( 0 ), m_pSubPart( 0 ), m_owner( 0 ), m_bPartIsFile( false ), m_bUseMemoryUpload( false ) { m_nBodyLeft <<= 62; } PartHandler::~PartHandler() { delete m_stream; delete m_pNextPart; delete m_pSubPart; if( m_bOwnBuffer ) delete m_pBuffer; } // Reads a single field in the request void PartHandler::startMemoryUpload() { m_stream = m_str_stream = new StringStream; } bool PartHandler::startFileUpload() { int64 le; Stream* fs = m_owner->makeTempFile( m_sTempFile, le ); if( fs == 0 ) { m_sError = "Can't create temporary stream " + m_sTempFile; m_sError.A(" (").N(le).A(")"); return false; } // was this stream first opened in memory? if( m_str_stream != 0 ) { // we must discharge all on disk. int wr, written = 0; while( (written != (int) m_str_stream->tell()) && ( wr = fs->write( m_str_stream->data()+written, (int) m_str_stream->tell() )) > 0 ) { written += wr; } delete m_str_stream; m_str_stream = 0; } m_stream = fs; return true; } void PartHandler::closeUpload() { TRACE( "PartHandler closing upload%s", "" ); m_nPartSize = m_nReceived = m_stream->tell(); if( m_str_stream == 0 && m_stream != 0 ) m_stream->close(); } bool PartHandler::getMemoryData( String& target ) { if( m_str_stream != 0 ) { m_str_stream->closeToString( target ); delete m_str_stream; m_str_stream = 0; m_stream = 0; return true; } return false; } bool PartHandler::getMemoryData( MemBuf& target ) { if( m_str_stream != 0 ) { int64 pos = m_str_stream->tell(); byte* data = m_str_stream->closeToBuffer(); target.setData( data, (uint32) pos, memFree ); delete m_str_stream; m_str_stream = 0; m_stream = 0; return true; } return false; } bool PartHandler::startUpload() { TRACE( "PartHandler starting upload%s", "" ); if ( m_bUseMemoryUpload || ! m_bPartIsFile ) { startMemoryUpload(); } else { return startFileUpload() != 0; } return true; } bool PartHandler::parse( Stream* input, bool& isLast ) { TRACE( "Parsing...%s", "" ); // read the header and the body if ( ! parseHeader( input ) ) return false; // time to open the upload if ( ! startUpload() ) { return false; } bool v = parseBody( input, isLast ); closeUpload(); return v; } bool PartHandler::parseBody( Stream* input, bool& isLast ) { fassert( m_stream != 0 ); // If we don't have it, create the buffer to be passed down. if( m_pBuffer == 0 ) initBuffer(); m_nBodyLeft -= m_pBuffer->m_nBufSize - m_pBuffer->m_nBufPos; // is this a mulitpart element? if( m_sBoundary.size() != 0 ) { if ( ! parsePrologue( input ) || ! parseMultipartBody( input ) ) { return false; } // are we part of a bigger multipart element? if ( m_sEnclosingBoundary.size() != 0 ) { return scanForBound( "\r\n--"+m_sEnclosingBoundary, input, isLast ); } } // are we part of a bigger multipart element? else if ( m_sEnclosingBoundary.size() != 0 ) { return scanForBound( "\r\n--"+m_sEnclosingBoundary, input, isLast ); } else { // Save previous data, if any m_pBuffer->allFlush(m_stream); // just get the (rest of the) body while( *m_pToBodyLeft > 0 && (! input->eof()) ) { m_pBuffer->fill( input ); m_pBuffer->allFlush( m_stream ); if( ! ( input->good() && m_stream->good() ) ) { m_sError = "I/O Error while reading the post body"; return false; } } isLast = true; } return true; } bool PartHandler::parsePrologue( Stream* input ) { // we must scan to the first body bool bIsLast; // When processed through web servers, the prologue is removed. // shouldn't be the last, or we have no multipart if (! scanForBound( "--"+m_sBoundary, input, bIsLast ) || bIsLast ) { m_sError = "Can't find the initial boundary; " + m_sError; return false; } return true; } bool PartHandler::parseMultipartBody( Stream* input ) { PartHandler* child = new PartHandler( m_sBoundary ); m_pSubPart = child; passSetting( child ); bool bResult; bool bIsLast; while( (bResult = child->parse( input, bIsLast ) ) && ! bIsLast ) { child->m_pNextPart = new PartHandler( m_sBoundary ); child = child->m_pNextPart; passSetting( child ); } if( ! bResult ) m_sError = child->m_sError; return bResult; } bool PartHandler::scanForBound( const String& boundary, Stream* input, bool& isLast ) { TRACE( "ScanForBound... %s", boundary.getRawStorage() ); // the boundary is valid only if followed by 2 characters, either \r\n or -- uint32 boundSize = boundary.size()+2; m_pBuffer->fill( input ); uint32 nBoundPos = m_pBuffer->find( boundary ); while( nBoundPos == String::npos ) { // end of the game? if( *m_pToBodyLeft == 0 || input->eof() ) { m_sError = "Malformed part (missing boundary)"; return false; } // Do we have too much data in? // -- of course, we don't want to commit if we don't have enough data // even for the boundary. if( m_pBuffer->m_nBufPos + boundSize < m_pBuffer->m_nBufSize ) { // commit the extra data and reduce the buffer size. m_pBuffer->m_nBufPos = m_pBuffer->m_nBufSize - boundSize; m_pBuffer->flush( m_stream ); } // get new data in. m_pBuffer->fill( input ); // find again nBoundPos = m_pBuffer->find( boundary ); } // We found a match. Is this the last? m_pBuffer->m_nBufPos = nBoundPos; if( ! m_pBuffer->hasMore( boundary.size() + 2, input, m_stream ) ) { m_sError = "Malformed part (missing ending)"; return false; } m_pBuffer->flush( m_stream ); String sRealBound; m_pBuffer->grabMore( sRealBound, boundary.size() + 2 ); if( sRealBound.endsWith( "--" ) ) { // was the last part -- but is it the flux last element? if( m_pBuffer->hasMore( boundary.size() + 4, input, m_stream ) ) { m_pBuffer->grabMore( sRealBound, boundary.size() + 4 ); if( ! sRealBound.endsWith( "\r\n" ) ) { m_sError = "Malformed last part (missing trailing CRLF)"; return false; } m_pBuffer->m_nBufPos += boundary.size() + 4; } isLast = true; } else { if( ! sRealBound.endsWith( "\r\n" ) ) { m_sError = "Malformed part (missing trailing CRLF)"; return false; } m_pBuffer->m_nBufPos += boundary.size() + 2; isLast = false; } //remove the boundary -- Necessary? m_pBuffer->flush(); return true; } bool PartHandler::parseHeader( Stream* input ) { TRACE( "Parse header...%s", "" ); // if we're not given a buffer, create it now if ( m_pBuffer == 0 ) initBuffer(); m_pBuffer->fill( input ); uint32 pos; while( true ) { pos = m_pBuffer->find( "\r\n" ); // No more headers? -- get new data if( pos == String::npos ) { m_pBuffer->flush(); // was the flush operation useless -- can't we make new space? if( m_pBuffer->full() ) { m_sError = "Single header entity too large"; return false; } // hit eof in the previous loops? if( *m_pToBodyLeft == 0 || input->eof() ) { m_sError = "Unterminated header"; return false; } m_pBuffer->fill( input ); // read error? if( ! input->good() ) { m_sError = "I/O Error while reading from input stream"; return false; } // Re-perform the search continue; } // are we done? if( pos == 0 ) { // discard read headers m_pBuffer->m_nBufPos += 2; break; } // parse this field String sLineBuf; m_pBuffer->grabMore( sLineBuf, pos ); if( ! parseHeaderField( sLineBuf ) ) { return false; } // update the buffer pos m_pBuffer->m_nBufPos += pos + 2; } // flush discarding parsed headers. m_pBuffer->flush(); // Maybe not necessary here return true; } bool PartHandler::parseHeaderField( const String& line ) { TRACE( "Parse field...%s", "" ); String sKey, sValue; if( ! Utils::parseHeaderEntry( line, sKey, sValue ) ) { m_sError = "Failed to parse header: " + line; return false; } return addHeader( sKey, sValue ); } bool PartHandler::addHeader( const String& sKeyu, const String& sValue ) { TRACE( "Adding header: %s: %s", sKeyu.getRawStorage(), sValue.getRawStorage() ); // probably this is an overkill String sKey = URI::URLDecode( sKeyu ); HeaderMap::iterator iadd = m_mHeaders.find( sKey ); if( iadd == m_mHeaders.end() ) { iadd = m_mHeaders.insert( HeaderMap::value_type( sKey, HeaderValue() ) ).first; } HeaderValue& hv = iadd->second; // we can accept, but not canonicize, invalid parameters bool bValid = hv.parseValue( sValue ); if ( ! bValid ) { hv.setRawValue( sValue ); } // a bit of checks if ( sKey.compareIgnoreCase( "Content-Disposition" ) == 0 ) { if( ! bValid ) { m_sError = "Failed to parse header value: " + sValue; return false; } HeaderValue::ParamMap::const_iterator ni = hv.parameters().find( "name" ); if ( ni != hv.parameters().end() ) { m_sPartName = ni->second; } else { // can't be a well formed content-disposition m_sError = "Invalid Content-Disposition"; return false; } ni = hv.parameters().find( "filename" ); if ( ni != hv.parameters().end() ) { // The filename may be empty if the field wasn't sent. m_sPartFileName = ni->second; m_bPartIsFile = true; } } else if ( sKey.compareIgnoreCase( "Content-Type" ) == 0 ) { if( ! bValid ) { m_sError = "Failed to parse header value: " + sValue; return false; } if( ! hv.hasValue() ) { m_sError = "Invalid Content-Type"; return false; } m_sPartContentType = hv.value(); if( m_sPartContentType.startsWith("multipart/") ) { m_sBoundary = hv.parameters()["boundary"]; TRACE( "found boundary header: %s", m_sBoundary.getRawStorage() ); if( m_sBoundary.size() == 0 ) { m_sError = "Missing boundary in multipart Content-Type"; return false; } } } else if ( sKey.compareIgnoreCase( "Content-Length" ) == 0 ) { if( ! hv.hasValue() ) { m_sError = "Invalid Content-Length"; return false; } if (! hv.value().parseInt( m_nPartSize ) ) { m_sError = "Invalid Content-Length"; return false; } } return true; } //============================================================ // Buffer management // void PartHandler::initBuffer() { if( m_pBuffer != 0 ) return; m_bOwnBuffer = true; m_pBuffer = new PartHandlerBuffer( m_pToBodyLeft ); } void PartHandler::passSetting( PartHandler* child ) { child->m_bUseMemoryUpload = m_bUseMemoryUpload; child->m_owner = m_owner; child->m_pBuffer = m_pBuffer; child->m_bOwnBuffer = false; // Pass the same pointer of the owner child->m_pToBodyLeft = m_pToBodyLeft; } PartHandler::PartHandlerBuffer::PartHandlerBuffer( int64* imax ): m_nBufPos(0), m_nBufSize(0), m_nDataLeft( imax ) { m_buffer = (byte*) memAlloc( buffer_size ); } PartHandler::PartHandlerBuffer::~PartHandlerBuffer() { memFree( m_buffer ); } void PartHandler::PartHandlerBuffer::flush( Stream* output ) { if( m_nBufPos > 0 ) { if( output != 0 ) output->write( m_buffer, m_nBufPos ); // Is there something left in the buffer? if( m_nBufPos < m_nBufSize ) { // Roll the buffer. // -- are the areas overlapping? if( m_nBufSize - m_nBufPos < m_nBufPos ) { // no? -- use a plain memcpy memcpy( m_buffer, m_buffer + m_nBufPos, m_nBufSize - m_nBufPos ); } else { // yes? -- use a slower memmove memmove( m_buffer, m_buffer + m_nBufPos, m_nBufSize - m_nBufPos ); } } if( m_nBufPos <= m_nBufSize ) m_nBufSize = m_nBufSize - m_nBufPos; else m_nBufSize = 0; m_nBufPos = 0; } } uint32 PartHandler::PartHandlerBuffer::find( const String& str ) { TRACE( "FIND: finding %s", str.getRawStorage() ); if( m_nBufPos >= m_nBufSize ) return String::npos; // do not create a new string, just scan in the buffer. String temp; temp.adopt( (char*) m_buffer + m_nBufPos, m_nBufSize - m_nBufPos, 0 ); return temp.find( str ); } bool PartHandler::PartHandlerBuffer::fill( Stream* input ) { TRACE( "PartHandlerBuffer Filling: %d", (int) m_nDataLeft ); if ( *m_nDataLeft > 0 && (! input->eof()) ) { int32 toRead = buffer_size - m_nBufSize; if( toRead > *m_nDataLeft ) { toRead = (int32) *m_nDataLeft; } int32 size = input->read( m_buffer+m_nBufSize, toRead ); TRACE( "PartHandlerBuffer Grabbed: %d", size ); if( size <= 0 ) return false; m_nBufSize += size; *m_nDataLeft -= size; return true; } return false; } void PartHandler::PartHandlerBuffer::grab( String& target, int posEnd ) { TRACE( "Grab: %d", posEnd ); target.adopt( (char*) m_buffer + m_nBufPos, posEnd - m_nBufPos, 0 ); } void PartHandler::PartHandlerBuffer::grabMore( String& target, int size ) { TRACE( "GrabMore: %d", size ); target.adopt( (char*) m_buffer + m_nBufPos, size, 0 ); } bool PartHandler::PartHandlerBuffer::hasMore( int count, Stream* input, Stream* output ) { TRACE( "Has more...%d+%d < %d", m_nBufPos, count, m_nBufSize ); if( (m_nBufPos + count) < m_nBufSize ) { return true; } if( (m_nBufPos + count) > buffer_size ) flush(output); // NOTE: we're under the hypotesis that buffer_size >> count bool largeEnough; do{ fill( input ); largeEnough = (m_nBufPos + count) < m_nBufSize; } while ( !largeEnough && !input->eof() ); return largeEnough; } } } /* end of parthandler.cpp */ modules/native/wopi/src/reply.cpp000066400000000000000000000162531176363201700174350ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: reply.cpp Web Oriented Programming Interface Object encapsulating reply. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 19 Feb 2010 19:30:26 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include namespace Falcon { namespace WOPI { CookieParams::CookieParams(): m_expire(0), m_max_age(-1), m_version(1), m_secure(false), m_httpOnly( false ), m_bValueGiven( false ) { } Reply::Reply( const CoreClass* base ): CoreObject( base ), m_nStatus( FALCON_WOPI_DEFAULT_REPLY_STATUS ), m_sReason( FALCON_WOPI_DEFAULT_REPLY_REASON ), m_bHeadersSent( false ) { // prepare default values setContentType( "text/html; charset=utf-8" ); setHeader( "Cache-Control", "no-cache" ); // and THEN tell we're using the defaults. m_bDefaultContent = true; } Reply::~Reply() { } void Reply::clearCookie( const String& cname ) { String sCookie; Falcon::URI::URLEncode( cname, sCookie ); sCookie.append( "=;Max-Age=0;expires=0" ); m_mCookies[ cname ] = sCookie; } bool Reply::setCookie( const String& cname, const CookieParams &p ) { String sCookie; Falcon::URI::URLEncode( cname, sCookie ); sCookie.append( "=" ); // if value is not a string, stringify it if ( p.m_bValueGiven ) { Falcon::String temp; //URLEncode will encode also quotes, so that quoted values are safe. Falcon::URI::URLEncode( p.m_value, temp ); sCookie += temp; } // Expire part if ( p.m_expire != 0 ) { String sDummy; p.m_expire->toRFC2822( sDummy ); sCookie += "; expires=" + sDummy; } else if ( p.m_expire_string.size() != 0 ) { sCookie += "; expires=" + p.m_expire_string; } else if( p.m_max_age >= 0 ) { sCookie.A("; Max-Age=").N( p.m_max_age ); } // path part if ( p.m_path.size() != 0 ) { sCookie += "; Path=" + p.m_path; } if ( p.m_domain.size() != 0 ) { sCookie += "; Domain=" + p.m_domain; } if ( p.m_version != 0 ) { sCookie.A( "; Version=" ).N( p.m_version ); } if ( p.m_secure ) { sCookie += "; Secure"; } if ( p.m_httpOnly ) { sCookie += "; httponly"; } m_mCookies[ cname ] = sCookie; return true; } bool Reply::setHeader( const String& fname, const String& value ) { if( m_bHeadersSent ) return false; // Find the header. Utils::StringMap::iterator ifield = m_mHeaders.find( fname ); if( ifield != m_mHeaders.end() ) { ifield->second = value; } else { m_mHeaders[ fname ] = value; } return true; } bool Reply::removeHeader( const String& fname ) { if( m_bHeadersSent ) return false; // Find the header. Utils::StringMap::iterator ifield = m_mHeaders.find( fname ); if( ifield != m_mHeaders.end() ) { m_mHeaders.erase( ifield ); return true; } return false; } bool Reply::getHeader( const String& fname, String& value ) { // Find the header. Utils::StringMap::iterator ifield = m_mHeaders.find( fname ); if( ifield != m_mHeaders.end() ) { value = ifield->second; return true; } return false; } CoreDict* Reply::getHeaders() { LinearDict* ld = new LinearDict( m_mHeaders.size() ); Utils::StringMap::iterator ifield = m_mHeaders.begin(); while( ifield != m_mHeaders.end() ) { ld->put( new CoreString( ifield->first ), new CoreString( ifield->second ) ); ++ifield; } return new CoreDict( ld ); } bool Reply::setContentType( const String& type ) { if( m_bHeadersSent ) return false; m_bDefaultContent = false; setHeader( "Content-Type", type ); // do the type includes an encoding? uint32 pos = type.find( "charset" ); if( pos != String::npos ) { uint32 p1 = type.find("=", pos); if( p1 != String::npos ) { uint32 p2 = type.find(";", p1 ); // ok also if p2 is npos m_sEncoding = type.subString( p1+1, p2 ); m_sEncoding.trim(); return true; } } // else default the encoding to C m_sEncoding = "C"; return true; } bool Reply::setContentType( const String& type, const String& subtype ) { if( m_bHeadersSent ) return false; m_bDefaultContent = false; setHeader( "Content-Type", type + "/" + subtype ); m_sEncoding = "C"; return true; } bool Reply::setContentType( const String& type, const String& subtype, const String& encoding ) { if( m_bHeadersSent ) return false; m_bDefaultContent = false; setHeader( "Content-Type", type + "/" + subtype + "; charset=" + encoding ); m_sEncoding = encoding; return true; } bool Reply::setRedirect( const String& url, uint32 timeout ) { if( m_bHeadersSent ) return false; String dest; dest.N( (int64)timeout ).A( "; url=" ).A( url ); setHeader( "Refresh", dest ); return true; } bool Reply::commit() { // already sent -- reuturn false. if ( m_bHeadersSent ) return false; // headers must be sent without transcoding // and some reply child class use m_output to send them. m_output = makeOutputStream(); // send the response line. startCommit(); // prepare the headers Utils::StringMap::const_iterator ic = m_mHeaders.begin(); while( ic != m_mHeaders.end() ) { //else -- raise error? commitHeader( ic->first, ic->second ); ++ic; } ic = m_mCookies.begin(); while( ic != m_mCookies.end() ) { commitHeader( "Set-Cookie", ic->second ); ++ic; } endCommit(); m_bHeadersSent = true; // Apply the transcoding to the body if( m_sEncoding != "C" ) { Stream* out = TranscoderFactory( m_sEncoding, m_output, true ); if( out == 0 ) { // throw in case the transcoder can't be found throw new CodeError( ErrorParam( e_unknown_encoding, __LINE__ ) .extra( m_sEncoding ) ); } m_output = out; } return true; } //======================================================= // CoreObject Overrides // CoreObject *Reply::clone() const { // uncloneable (it's a singleton) return 0; } bool Reply::setProperty( const String &prop, const Item &value ) { // only read-only properties if( prop == "status" ) { m_nStatus = (int) value.forceInteger(); } else if( prop == "reason" ) { m_sReason = *value.asString(); } else { readOnlyError( prop ); } return true; } bool Reply::getProperty( const String &prop, Item &value ) const { if( prop == "status" ) { value = (int64) m_nStatus; } else if( prop == "reason" ) { value = m_sReason; } else if( prop == "isSent" ) { value.setBoolean( m_bHeadersSent ); } else { return defaultProperty( prop, value ); } return false; } void Reply::gcMark( uint32 ) { } } } /* end of reply.cpp */ modules/native/wopi/src/reply_ext.cpp000066400000000000000000000345451176363201700203210ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: reply_ext.cpp Web Oriented Programming Interface Reply class script interface ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 19 Feb 2010 22:09:02 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include /*# @beginmodule WOPI */ namespace Falcon { namespace WOPI { static void internal_capitalize_name( String& name ) { // capitalize the first letter name.setCharAt( 0, toupper( name[0] ) ); // and all the letters after a "-" uint32 pos = name.find( "-" ); // we know name is long at least 1 while( pos < name.length()-1 ) { name.setCharAt( pos+1, toupper( name[pos+1] ) ); pos = name.find( "-", pos + 1 ); } } /*# @object Reply @brief Reflects the reply that this script has sent (or will send). This object contains the header that the system is going to send back to the remote client as soon as the first output (through the ">" operator or through print functions) is performed. If output has already been performed, then the header becomes read-only, and the @b Reply.sent field becomes true. Falcon template files willing to change the output header should escape to Falcon immediately, giving the escape sequence as their very first character, as unescaped text is immediately sent to the upstream server. A suitable default header with "+200 OK", no cache pragmas and text/html in utf-8 encoding is provided by default. @prop status Numeric HTTP reply code. For example, 200 (meaning OK). @prop reason Descriptive http reply reason. In the "+200 OK" standard reply, it's the "OK" part. @prop sent True when the header has been delivered, false if the header is still unsent. */ /*# @method commit Reply @brief Sends immediately the header and pending data. Usually, the integration module sends the header upstream at a reasonable moment, but some scripts may prefer to send the header sooner; in example, this is useful if the script is just generating a reply that consists in an HTTP header without data, or to start a keep-alive HTTP/1.1 conversation. */ FALCON_FUNC Reply_commit( VMachine *vm ) { Reply* r = dyncast( vm->self().asObject() ); vm->retval( r->commit() ); } /*# @method setCookie Reply @brief sets a cookie that will be received next time that a script is called. @param name Name of the Cookie or complete cookie specification in a dictionary. @optparam value Value for the cookie (eventually truned into a string). @optparam expires Expiration date (a TimeStamp or an ISO or RFC2822 formatted string), or maximum life in seconds (an integer). @optparam path Cookie path validity @optparam domain Domain for which the cookie is valid. @optparam secure True to specify that the cookie can be sent only in https. @optparam httpOnly If true, this cookie will be invisible to client side scripts, and will be only sent to the server. This facility allows to store variables on the remote system (usually a web browser), which will send them back each time it connects again. The cookies sent through this function will be received in the @b cookies member of subquesent call to this or other scripts. Parameters to be left blank can be skipped using @b nil; however, it may be useful to use the named parameter convention. For example: @code Reply.setCookie( "cookie1", "value1", nil, nil, ".mydomain.com", false, true ) // but probably better Reply.setCookie( name|"cookie1", value|"value1", domain|".mydomain.com", httpOnly|true ) @endcode Only the @b name parameter is mandatory. @note Cookies must be set before output is sent to the upstream server. */ FALCON_FUNC Reply_setCookie( VMachine *vm ) { String sDummy; String sCookie; Item *i_name = vm->param(0); Item *i_value = vm->param(1); Item *i_expire = vm->param(2); Item *i_path = vm->param(3); Item *i_domain = vm->param(4); Item *i_secure = vm->param(5); Item *i_httpOnly = vm->param(6); CookieParams cp; if ( i_name == 0 || ! (i_name->isString() ) ) { goto invalid; } // if value is not a string, stringify it if ( i_value != 0 ) { String temp; if ( i_value->isString() ) { cp.value( *i_value->asString() ); } else if( ! i_value->isNil() ) { vm->itemToString( cp.m_value, i_value ); cp.m_bValueGiven = true; } } // Expire part if ( i_expire != 0 ) { if ( i_expire->isOrdinal() ) { cp.m_max_age = (int32) i_expire->forceInteger(); } // a bit of sanitization; if we have an expire, we must ensure it's in ISO or RFC2822 format. else if ( i_expire->isObject() && i_expire->asObject()->derivedFrom( "TimeStamp" ) ) { cp.m_expire = (TimeStamp *) i_expire->asObject()->getUserData(); } else if ( i_expire->isString() ) { TimeStamp tsDummy; if ( ! TimeStamp::fromRFC2822( tsDummy, *i_expire->asString() ) ) goto invalid; cp.m_expire_string = *i_expire->asString(); } else if ( ! i_expire->isNil() ) goto invalid; } // path part if ( i_path != 0 ) { if ( i_path->isString() ) { cp.m_path = *i_path->asString(); } else if ( ! i_path->isNil() ) goto invalid; } if ( i_domain != 0 ) { if ( i_domain->isString() ) { cp.m_domain = *i_domain->asString(); } else if ( ! i_domain->isNil() ) goto invalid; } if ( i_secure != 0 && i_secure->isTrue() ) { cp.m_secure = true; } if ( i_httpOnly != 0 && i_httpOnly->isTrue() ) { cp.m_httpOnly = true; } // great, we have it. { Reply* r = dyncast( vm->self().asObject() ); r->setCookie( *i_name->asString(), cp ); } return; invalid: throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).extra( "S|D,[X,TimeStamp|S,S,S,B,B]" ) ); } /*# @method clearCookie Reply @brief Remove given cookie. @param name The cookie to be removed. This function explicitly tries to clear the cookie from the remote client cache. The cookie value is @b not removed from the @a Request.cookies array. */ FALCON_FUNC Reply_clearCookie( VMachine *vm ) { String sCookie; Item *i_name = vm->param(0); if( i_name == 0 || ! i_name->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).extra( "S" ) ); } Reply* r = dyncast( vm->self().asObject() ); r->clearCookie( *i_name->asString() ); } /*# @method redirect Reply @brief Helper creating a Refresh (redirection) header. @optparam uri Where to redirect the page. @optparam timeout Number of seconds before refresh takes place. This function creates a well-formed "Refresh" header in the reply, which can: - Reload the page immediately or after a timeout - Ask for redirection to another page immediately or after a timeout. @note As this method mangles the output headers, it must be called before any output is sent to the client. Otherwise, it will be ignored. */ FALCON_FUNC Reply_redirect( VMachine *vm ) { Reply* r = dyncast( vm->self().asObject() ); Item* i_url = vm->param(0); Item* i_timeout = vm->param(1); if( (i_url != 0 && !( i_url->isNil() || i_url->isString() )) || (i_timeout != 0 && ! ( i_timeout->isNil() || i_timeout->isOrdinal() )) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "[S],[N]" ) ); } int iTimeout = (int)(i_timeout == 0 || i_timeout->isNil() ? 0: i_timeout->forceInteger()); if( i_url == 0 || i_url->isNil() ) r->setRedirect( "", iTimeout ); else r->setRedirect( *i_url->asString(), iTimeout ); } /*# @method setHeader Reply @brief Adds or remove a reply header. @param name The header to be created or removed. @optparam value If given and not nil, the value to be stored in the header; if not given, or nil, the header will be removed. This sets the given header to the required value. The @b value parameter can be of any type; if it's not a string, a standard conversion to string will be attempted. In case @b value is not given or is nil, the header is removed. The header @b name is automatically capitalized at the beginning and after each '-' symbol. @note Pay attention to the fact that the maps in which headers are stored are case-sensitive. A case mismatch may cause an undesired duplication of the header. */ FALCON_FUNC Reply_setHeader( VMachine *vm ) { Reply* r = dyncast( vm->self().asObject() ); Item* i_name = vm->param(0); Item* i_value = vm->param(1); if( i_name == 0 || ! i_name->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S,[X]" ) ); } String name = *i_name->asString(); if ( name.size() == 0 ) { return; } internal_capitalize_name( name ); // should we delete this string? if( i_value == 0 || i_value->isNil() ) { r->removeHeader( *i_name->asString() ); } else if ( ! i_value->isString() ) { r->setHeader( name, *i_value->asString() ); } else { String value; vm->itemToString( value , i_value ); r->setHeader( name, value ); } } /*# @method getHeader Reply @brief Retrieves the value of a given header. @param name The name of the header to be queried. @return If the header is set, its value as a string; false otherwise. The header @b name is automatically capitalized at the beginning and after each '-' symbol. @note Pay attention to the fact that the maps in which headers are stored are case-sensitive. */ FALCON_FUNC Reply_getHeader( VMachine *vm ) { Reply* r = dyncast( vm->self().asObject() ); Item* i_name = vm->param(0); if( i_name == 0 || ! i_name->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S,[X]" ) ); } String name = *i_name->asString(); if ( name.size() == 0 ) { return; } internal_capitalize_name( name ); String value; if( r->getHeader( name, value ) ) { vm->retval( new CoreString( value ) ); } else vm->retnil(); } /*# @method getHeaders Reply @brief returns the list of headers that will be sent (or have been sent). @return A dictionary of strings containing all the headers prepared for being sent. This method returns a map containing all the headers that the WOPI system is going to send, or that have been sent if output is already started. The map is a snapshot of the current status of the Reply. Headers can be manipulated only through @a Reply.setHeader. Any change to this structure won't be reflected on the actual reply headers, and similarly, any change in the Reply headers will not be reflected into this value. */ FALCON_FUNC Reply_getHeaders( VMachine *vm ) { vm->retval( dyncast( vm->self().asObject() )->getHeaders() ); } /*# @method ctype Reply @brief Helper creating a Content-Type header. @param type Main MIME type of the data that shall be sent to output. @optparam subtype MIME Subtype that shall be sent to the output. @optparam charset MIME Charset encoding of the output (if text). Creates a Content-Type: <type>/<subtype>; charset=<charset> field in the headers. It's just a shortcut. @note Many functions in the module suppose that the output will be utf-8. */ FALCON_FUNC Reply_ctype( VMachine *vm ) { Reply* r = dyncast( vm->self().asObject() ); Item* i_type = vm->param(0); Item* i_subtype = vm->param(1); Item* i_charset = vm->param(2); if( (i_type == 0 || ! i_type->isString() ) || ( i_subtype != 0 && !(i_subtype->isString() || i_subtype->isNil() )) || ( i_charset != 0 && !(i_charset->isString() || i_charset->isNil() )) ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra( "S,[S],[S]" ) ); } if( i_charset == 0 || i_charset->isNil() ) { if( i_subtype == 0 || i_subtype->isNil() ) r->setContentType( *i_type->asString() ); else r->setContentType( *i_type->asString(),*i_subtype->asString() ); } else { r->setContentType( *i_type->asString(), i_subtype!= 0 && i_subtype->isString() ? *i_subtype->asString():"", *i_charset->asString() ); } } void InitReplyClass( Module* self, ObjectFactory cff, ext_func_t init_func ) { // create a singleton instance of %Reply class Symbol *c_reply_o = self->addSingleton( "Reply", init_func ); Falcon::Symbol *c_reply = c_reply_o->getInstance(); c_reply->getClassDef()->factory( cff ); // we don't make it WKS; let it to be exchangeable with another object. self->addClassProperty( c_reply, "status" ); self->addClassProperty( c_reply, "reason" ); self->addClassProperty( c_reply, "isSent" ); self->addClassMethod( c_reply, "commit", &Reply_commit ); self->addClassMethod( c_reply, "setHeader", &Reply_setHeader ).asSymbol() ->addParam( "name" )->addParam( "value" ); self->addClassMethod( c_reply, "getHeader", &Reply_getHeader ).asSymbol() ->addParam( "name" ); self->addClassMethod( c_reply, "getHeaders", &Reply_getHeaders ); self->addClassMethod( c_reply, "redirect", &Reply_redirect ).asSymbol() ->addParam( "url" )->addParam( "timeout" ); self->addClassMethod( c_reply, "ctype", &Reply_ctype ).asSymbol() ->addParam( "type" )->addParam( "subtype" )->addParam( "charset" ); self->addClassMethod( c_reply, "setCookie", &Reply_setCookie ).asSymbol() ->addParam( "name" )->addParam( "value" )->addParam( "expires" ) ->addParam( "path" )->addParam( "domain" )->addParam( "secure" ) ->addParam( "httpOnly" ); self->addClassMethod( c_reply, "clearCookie", &Reply_clearCookie ).asSymbol() ->addParam( "name" ); } } } /* end of reply_ext.cpp */ modules/native/wopi/src/replystream.cpp000066400000000000000000000042571176363201700206520ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: replystream.cpp Falcon WOPI - Web Oriented Programming Interface This is a dummy stream sensing for the first output operation and then invoking the Reply for commit. The stream is then destroyed and the first write operation is invoked on the real stream. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Mar 2010 16:47:02 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include namespace Falcon { namespace WOPI { ReplyStream::ReplyStream( Reply* rep ): Stream( t_proxy ), m_rep( rep ) { } ReplyStream::ReplyStream( const ReplyStream& other ): Stream( other ), m_rep( other.m_rep ) { } ReplyStream::~ReplyStream() { } bool ReplyStream::writeString( const String &source, uint32 begin, uint32 end ) { if ( ! m_rep->isCommited() ) m_rep->commit(); return m_rep->output()->writeString( source, begin, end ); } bool ReplyStream::close() { if ( ! m_rep->isCommited() ) m_rep->commit(); return m_rep->output()->close(); } int32 ReplyStream::write( const void *buffer, int32 size ) { if ( ! m_rep->isCommited() ) m_rep->commit(); return m_rep->output()->write( buffer, size ); } int ReplyStream::writeAvailable( int n, const Sys::SystemData* sd ) { return m_rep->output() ? 1 : m_rep->output()->writeAvailable( n, sd ); } int64 ReplyStream::lastError() const { return m_rep->output()->lastError(); } bool ReplyStream::put( uint32 chr ) { if ( ! m_rep->isCommited() ) m_rep->commit(); return m_rep->output()->put( chr ); } bool ReplyStream::get( uint32 &chr ) { m_status = t_unsupported; return false; } Stream *ReplyStream::clone() const { return new ReplyStream( *this ); } // Flushes the stream. bool ReplyStream::flush() { if ( ! m_rep->isCommited() ) m_rep->commit(); return m_rep->output()->flush(); } } } /* end of replystream.cpp */ modules/native/wopi/src/request.cpp000066400000000000000000000362671176363201700200010ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: request.cpp Web Oriented Programming Interface Object encapsulating requests. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 13 Feb 2010 12:29:23 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include // for memcpy #include #include //#define TRACE( fmt, ... ) fprintf( stderr, "%d: " fmt "\n", __LINE__, __VA_ARGS__ ); fflush(stderr); #define TRACE( fmt, ... ) namespace Falcon { namespace WOPI { //================================================================== // Request Request::Request(): m_request_time( 0 ), m_bytes_sent( 0 ), m_content_length( -1 ), // protected m_gets( new CoreDict( new LinearDict ) ), m_posts( new CoreDict( new LinearDict ) ), m_cookies( new CoreDict( new LinearDict ) ), m_headers( new CoreDict( new LinearDict ) ), m_sSessionField( DEFAULT_SESSION_FIELD ), m_tempFiles( 0 ), m_nMaxMemUpload( 1024 ), m_startedAt(0.0) { //just provide a silly default #ifdef _WIN32 m_sTempPath = "/C:/temp"; #else m_sTempPath = "/tmp"; #endif m_lockGets = new GarbageLock(); m_lockPosts = new GarbageLock(); m_lockCookies = new GarbageLock(); m_lockHeaders = new GarbageLock(); m_lockGets->item().setDict( m_gets ); m_lockPosts->item().setDict( m_posts ); m_lockCookies->item().setDict( m_cookies ); m_lockHeaders->item().setDict( m_headers ); m_MainPart.setOwner( this ); } Request::~Request() { delete m_lockGets; delete m_lockPosts; delete m_lockCookies; delete m_lockHeaders; } bool Request::parse( Stream* input ) { TRACE( "Parsing request %s","" ); if( ! parseHeader( input ) ) return false; TRACE( "Parsing request found content length %d", (int)m_content_length ); if( m_content_length > 0 ) return parseBody( input ); return true; } bool Request::parseHeader( Stream* input ) { TRACE( "Parsing headers%s", "" ); if ( ! m_MainPart.parseHeader( input ) ) { m_posts->put( SafeItem(new CoreString(":error")), SafeItem(new CoreString( m_MainPart.error() )) ); return false; } // get the content type and encoding PartHandler::HeaderMap::const_iterator ci = m_MainPart.headers().find( "Content-Type" ); TRACE( "Parsing headers %s", "" ); if( ci != m_MainPart.headers().end() ) { m_content_type = ci->second.rawValue(); // default the content-length to 0 in case of non multipart data. if( m_content_type.find( "multipart/") != 0 ) m_content_length = 0; } ci = m_MainPart.headers().find( "Content-Encoding" ); if( ci != m_MainPart.headers().end() ) { m_content_encoding = ci->second.rawValue(); } ci = m_MainPart.headers().find( "Content-Length" ); if( ci != m_MainPart.headers().end() ) { ci->second.rawValue().parseInt( m_content_length ); m_MainPart.setBodySize( m_content_length ); } // parse the cookies. ci = m_MainPart.headers().find( "Cookie" ); if( ci != m_MainPart.headers().end() ) { HeaderValue::ParamMap::const_iterator pi = ci->second.parameters().begin(); while( pi != ci->second.parameters().end() ) { m_cookies->put( SafeItem( new CoreString( pi->first) ), SafeItem( new CoreString(pi->second)) ); ++pi; } } ci = m_MainPart.headers().begin(); while( ci != m_MainPart.headers().end() ) { m_headers->put( SafeItem( new CoreString( ci->first ) ), SafeItem( new CoreString( ci->second.rawValue() )) ); ++ci; } return true; } bool Request::parseBody( Stream* input ) { TRACE( "Parsing body%s", "" ); // prepare the POST data receive area m_MainPart.startMemoryUpload(); //fprintf( stderr, "Content length: %d / %d\n", (int) m_content_length, (int) m_nMaxMemUpload ); // Inform the part if it can use memory uploads for their subparts. if ( m_content_length != -1 && (m_nMaxMemUpload > 0 && m_content_length < m_nMaxMemUpload) ) { // This tell the children of the main part NOT TO create a temporary file // when they receive a file upload (the default). // Standard form fields are still received in memory. m_MainPart.uploadsInMemory( true ); } // For prudence, bool bDummy = false; if ( ! m_MainPart.parseBody( input, bDummy ) ) { m_posts->put( SafeItem(new CoreString(":error")), SafeItem(new CoreString( m_MainPart.error() )) ); return false; } // shouldn't be necessary as we started the upload in memory mode. m_MainPart.closeUpload(); TRACE( "Parsing body -- starting parsing parts%s", "" ); // it's a singlepart or multipart? PartHandler* child = m_MainPart.child(); if( child == 0 ) { // parse the post fields String post_data; m_MainPart.getMemoryData( post_data ); Falcon::WOPI::Utils::parseQuery( post_data, m_posts->items() ); } /* else { while( child != 0 ) { addUploaded( child ); child = child->next(); } } */ return true; } Stream* Request::makeTempFile( String& fname, int64& le ) { Path fpath; fpath.setFullLocation( getTempPath() ); // try 3 times int tries = 0; while( tries < 3 ) { String fname_try; Utils::makeRandomFilename( fname_try, 12 ); fpath.setFile( fname_try ); fname = fpath.get(); // try to create the file Falcon::FileStream* tgFile = new Falcon::FileStream(); if ( tgFile->create( fname, Falcon::BaseFileStream::e_aUserRead | Falcon::BaseFileStream::e_aUserWrite ) ) { // save the tempfile name addTempFile( fname ); le = 0; return tgFile; } le = tgFile->lastError(); delete tgFile; ++tries; } // no way, we really failed. return 0; } bool Request::getField( const String& fname, String& value ) const { Item temp; if( ! getField( fname, temp ) || ! temp.isString() ) return false; value = *temp.asString(); return true; } bool Request::getField( const String& fname, Item& value ) const { Item* res; // try in gets. if( (res = m_gets->find( fname )) == 0 ) { if( (res = m_posts->find(fname) ) == 0 ) { if( (res = m_cookies->find(fname) ) == 0 ) { return false; } } } value = *res; return true; } void Request::fwdGet( String& fwd, bool all ) const { forward( m_gets->items(), m_posts->items(), fwd, all ); } void Request::fwdPost( String& fwd, bool all ) const { Utils::dictAsInputFields( fwd, m_posts->items() ); if( all ) Utils::dictAsInputFields( fwd, m_gets->items() ); } void Request::forward( const ItemDict& main, const ItemDict& aux, String& fwd, bool all ) const { fwd.size(0); Utils::fieldsToUriQuery( main, fwd ); if( all ) { Utils::fieldsToUriQuery( aux, fwd ); } } bool Request::setURI( const String& uri ) { if( m_uri.parse( uri, false ) ) { m_sUri = uri; if ( m_uri.query().size() != 0 ) { Utils::parseQuery( m_uri.query(), m_gets->items() ); } m_location = m_uri.path(); return true; } return false; } void Request::addTempFile( const Falcon::String &fname ) { TempFileEntry* tfe = new TempFileEntry( fname ); tfe->m_next = m_tempFiles; m_tempFiles = tfe; } void Request::removeTempFiles( void* head, void* data, void (*error_func)(const String&, void*) ) { TempFileEntry *tfe = (TempFileEntry*) head; while( tfe != 0 ) { TempFileEntry *tfe_next = tfe->m_next; Falcon::int32 status; if ( ! Falcon::Sys::fal_unlink( tfe->m_entry, status ) ) { if ( error_func != 0 ) { String error = String("Cannot remove temporary file (").N(status).A( tfe->m_entry ); error_func( error, data ); } } delete tfe; tfe = tfe_next; } } //================================================================ // Falcon interface. // CoreRequest::CoreRequest( const CoreClass* base ): CoreObject( base ), m_sm(0), m_upld_c( 0 ), m_bPostInit( false ), m_base(0), m_reply(0), m_bAutoSession(true) { } void CoreRequest::init( CoreClass* upld_c, Reply* reply, SessionManager *sm, Request* r ) { m_upld_c = upld_c; m_sm = sm; if( r == 0 ) r = new Request; m_base = r; m_reply = reply; m_base->sessionToken( sm->getSessionToken() ); } CoreRequest::~CoreRequest() { delete m_base; } void CoreRequest::addUploaded( PartHandler* ph, const String& prefix ) { String key = prefix.size() == 0 ? ph->name(): prefix + "." + ph->name(); if( ph->isFile() ) { // an empty file field? if( ph->filename().size() == 0 ) { // puts a nil item. Falcon::WOPI::Utils::addQueryVariable( key, Item(), m_base->m_posts->items() ); } else { // configure the part CoreObject* upld = m_upld_c->createInstance(); upld->setProperty( "mimeType", SafeItem( new CoreString(ph->contentType())) ); upld->setProperty( "filename", SafeItem( new CoreString(ph->filename())) ); upld->setProperty( "size", ph->uploadedSize() ); if( ph->error().size() != 0 ) { // was there an error? upld->setProperty( "error", SafeItem( new CoreString(ph->error())) ); } else { // no? -- store the data or the temporary file name. if ( ph->isMemory() ) { MemBuf* mb = new MemBuf_1(0); ph->getMemoryData( *mb ); upld->setProperty( "data", SafeItem(mb) ); } else { upld->setProperty( "storage", SafeItem( new CoreString(ph->tempFile())) ); } } // It may take some time before we can reach the vm, // so it's better to be sure we're not marked. Falcon::WOPI::Utils::addQueryVariable( key, SafeItem(upld), m_base->m_posts->items() ); } } else { // We have just to add this field. if( ph->isMemory() ) { String temp; CoreString* value = new CoreString; ph->getMemoryData( temp ); temp.c_ize(); value->fromUTF8( (char *) temp.getRawStorage() ); // It may take some time before we can reach the vm, // so it's better to be sure we're not marked. Falcon::WOPI::Utils::addQueryVariable( key, SafeItem(value), m_base->m_posts->items() ); } // else, don't know what to do } PartHandler* child = ph->child(); while( child != 0 ) { addUploaded( child, key ); child = child->next(); } } bool CoreRequest::processMultiPartBody() { PartHandler* child = m_base->m_MainPart.child(); if( child == 0 ) { return false; } while( child != 0 ) { addUploaded( child ); child = child->next(); } return true; } void CoreRequest::configFromModule( const Module* mod ) { AttribMap* attribs = mod->attributes(); if( attribs == 0 ) { return; } VarDef* value = attribs->findAttrib( FALCON_WOPI_MAXMEMUPLOAD_ATTRIB ); if( value != 0 && value->isInteger() ) { m_base->m_nMaxMemUpload = value->asInteger(); } value = attribs->findAttrib( FALCON_WOPI_TEMPDIR_ATTRIB ); if( value != 0 && value->isString() ) { m_base->m_sTempPath.bufferize( *value->asString() ); } } //================================================================ // Override // CoreObject *CoreRequest::clone() const { // request object is not clone able. return 0; } bool CoreRequest::setProperty( const String &prop, const Item &value ) { if ( m_bPostInit ) { postInit(); m_bPostInit = false; } if( prop == "sidField" ) { if( value.isString() ) m_base->m_sSessionField.bufferize(*value.asString()); else throw new ParamError( ErrorParam( e_inv_params, __LINE__ ) .extra("sidField") ); } else if ( prop == "autoSession" ) { m_bAutoSession = value.isTrue(); } else { readOnlyError( prop ); } return true; } bool CoreRequest::getProperty( const String &prop, Item &value ) const { if ( m_bPostInit ) { const_cast(this)->postInit(); const_cast(this)->m_bPostInit = false; } if( prop == "gets" ) { value = m_base->m_gets; } else if( prop == "posts" ) { value = m_base->m_posts; } else if( prop == "cookies" ) { value = m_base->m_cookies; } else if( prop == "headers" ) { value = m_base->m_headers; } else if( prop == "parsed_uri" ) { value = Utils::makeURI( m_base->m_uri ); } else if( prop == "protocol" ) { value = m_base->m_protocol; } else if( prop == "request_time" ) { value = m_base->m_request_time; } else if( prop == "bytes_sent" ) { value = m_base->m_bytes_sent; } else if( prop == "content_length" ) { value = m_base->m_content_length; } else if( prop == "method" ) { value = m_base->m_method; } else if( prop == "content_type" ) { value = m_base->m_content_type; } else if( prop == "content_encoding" ) { value = m_base->m_content_encoding; } else if( prop == "ap_auth_type" ) { value = m_base->m_ap_auth_type; } else if( prop == "user" ) { value = m_base->m_user; } else if( prop == "location" ) { value = m_base->m_location; } else if( prop == "uri" ) { value = m_base->m_sUri; } else if( prop == "filename" ) { value = m_base->m_filename; } else if( prop == "path_info" ) { value = m_base->m_path_info; } else if( prop == "args" ) { value = m_base->m_args; } else if( prop == "remote_ip" ) { value = m_base->m_remote_ip; } else if( prop == "sidField" ) { value = m_base->m_sSessionField; } else if( prop == "startedAt" ) { value = m_base->startedAt(); } else if( prop == "provider" ) { value = m_provider; } else if( prop == "autoSession" ) { value.setBoolean( m_bAutoSession ); } else { return defaultProperty( prop, value ); } return true; } void CoreRequest::gcMark( uint32 mark ) { /* They are now locked. if( m_base != 0 ) { if( m_base->m_gets != 0 ) m_base->m_gets->gcMark( mark ); if( m_base->m_posts != 0 ) m_base->m_posts->gcMark( mark ); if( m_base->m_cookies != 0 ) m_base->m_cookies->gcMark( mark ); if( m_base->m_headers != 0 ) m_base->m_headers->gcMark( mark ); } */ } CoreObject* CoreRequest::factory( const Falcon::CoreClass* cls, void* ud, bool bDeser ) { return new CoreRequest( cls ); } } } /* end of request.cpp */ modules/native/wopi/src/request_ext.cpp000066400000000000000000000505441176363201700206530ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: request_ext.cpp Web Oriented Programming Interface Request class script interface ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 14 Feb 2010 13:27:55 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include /*# @beginmodule WOPI */ namespace Falcon { namespace WOPI { /*# @object Request @brief Main web server interface object. Object Request contains the informations that are retreived from the web server, and allows to exchange data with that. In forms and gets, If the field name in the request ends with "[]", then the entry in the gets dictionary is an array containing all the values posted under the same field name @prop request_time Number of microseconds since 00:00:00 january 1, 1970 UTC. Format int64. @prop method Original request method. Can be "GET", "POST", or HTTP methods. @prop bytes_sent Body byte count, for easy access. Format int64 @prop content_type The Content-Type for the current request. @prop content_encoding Encoding through which the data was received. @prop content_length Full length of uploaded data, including MIME multipart headers. Will be -1 if unknown, and 0 if the request has only an HTTP request header and no body data. @prop user If an apache authentication check was made, this gets set to the user name. @prop ap_auth_type If an apache authentication check was made, this gets set to the auth type. @prop uri The complete URI as it was sent in the request (including the query elements). @prop location The portion of the URI indicating the "location" of the desired file or directory. @prop parsed_uri The uri, already stored in a URI class instance. @prop filename The filename on disk corresponding to this response. @prop path_info The PATH_INFO extracted from this request. @prop args The QUERY_ARGS extracted from this request. @prop remote_ip The IP address where the request has originated. @prop sidField The name of the field used to carry the Session ID. @prop provider Name of the subsystem that is providing this WOPI interface. @prop autoSession Set to true (default) to have the request ID field automatically written in the cookies of the reply when getSession() is invoked. To manage manually the session cookie (or send the SID field via post/get forwarding) set this to false. @prop headers Original request headers (in a dictionary). @prop cookies Dictionary containing the cookies set in the request. */ /*# @property startedAt Request @brief Thehe value of seconds() when the script was started. This method returns a relative time taken when the web server integration system gives control to Falcon, before that the script is actually loaded and started. This formula: @code elapsed = seconds() - Request.startedAt @endcode gives the time elapsed in processing the script up to that line, including the time to setup the VM and start the execution. */ /*# @property gets Request @brief Fields received in the GET request method. If the current script is invoked by a query containing query fields in the URI, this property contains the a dictionary with the paris of key/values contained in the query. Fields whose name end with "[]" are translated into arrays and their values is stored in the order tey are found in the query. In example, if the page is loaded through a form containing the following fields: @code


    User id:
    Hobby:
    Hobby:
    Hobby:
    @endcode myscript.fal will receive the following fields in gets: @code > Request.gets["id"] // will be the user id inspect( Request.gets["hobbies"] ) // will be an array @endcode Get fields can be generated directly through a query. A link to a falcon script followed by "?" and an URL encode query will be translated into a GET request, and @b Request.gets fields will receive the specified values. If a web page contains the following code: @code @endcode then, myscript.fal will receive the "id" value and the array specified by hobbies in the "hobbies" key of the @b Request.gets property. */ /*# @property posts Request @brief Fields received in the POST method. If the current script is invoked through a form declared as having a post method, it will receive the values of the form fields. Fields whose name end with "[]" are translated into arrays and their values is stored in the order tey are found in the query. In example, if the page is loaded through a form containing the following fields: @code

    User id:
    Hobby:
    Hobby:
    Hobby:
    @endcode myscript.fal will receive the following fields in gets: @code > Request.posts["id"] // will be the user id inspect( Request.posts["hobbies"] ) // will be an array @endcode A script may receive both @b gets and @b posts fields if the */ /*# @method getField Request @brief Retreives a query field from either @b Request.gets or @b Request.posts. @param field The field name to be found. @optparam defval Default value to be returned if the field is not found. @return A cookie, POST or GET field value (as a string). @raise AccessError, if no default value is given and field is not found. In certain cases, it is useful to retreive a query field no matter if it comes from a cookie, the POST part or the GET part of the query. This method searches first the @b Request.gets, then @b Request.posts fields and finally the @b Request.cookies. If the field is not found, the given @b default value is returned; if that parameter is not specified and the field is not found, an AccessError is raised. */ FALCON_FUNC Request_getfield( VMachine *vm ) { Item *i_key = vm->param( 0 ); if ( i_key == 0 || ! i_key->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).extra( "S,[X]" ) ); return; } CoreRequest *self = dyncast( vm->self().asObject() ); Item res; if( ! self->base()->getField( *i_key->asString(), vm->regA() ) ) { // should we raise or return null on error? bool bRaise = vm->paramCount() == 1; // nothing; should we raise something? if ( bRaise ) { throw new AccessError( ErrorParam( e_arracc, __LINE__ ) .extra( "getField" ) ); return; } //else, return the default parameter. vm->retval( *vm->param(1) ); } // else, the result is already in regA } /*# @method fwdGet Request @brief Forwards the request, creating a suitable query string for a target URI. @optparam all If true, add also the POST fields. @return An URI encoded string that can be directly used in further get requests. This method simplifies the task to create callback to the page that is being processed when it's necessary to forward all the fields received to the new request. If the @b all parameter is true, also the fields passed as POST fields will be forwared through this method. @note As the get and post fields are not read-only, it is possible to change their contents in this object and then call this method to introduce exceptions in forwarding the request. */ FALCON_FUNC Request_fwdGet( VMachine *vm ) { CoreRequest *self = dyncast( vm->self().asObject() ); Item *i_all = vm->param( 0 ); CoreString *res = new CoreString; self->base()->fwdGet( *res, i_all != 0 && i_all->isTrue() ); vm->retval( res ); } /*# @method fwdPost Request @brief Forwards the request, creating a set of hidden input fields. @optparam all If true, add also the GET fields. @return A string containing pre-encoded http hidden input fields. This method simplifies the task to create callback to the page that is being processed when it's necessary to forward all the fields received to the new request. If the @b all parameter is true, also the fields passed as GET fields will be forwared through this method. @note As the get and post fields are not read-only, it is possible to change their contents in this object and then call this method to introduce exceptions in forwarding the request. */ FALCON_FUNC Request_fwdPost( VMachine *vm ) { CoreRequest *self = dyncast( vm->self().asObject() ); Item *i_all = vm->param( 0 ); CoreString *res = new CoreString; self->base()->fwdPost( *res, i_all != 0 && i_all->isTrue() ); vm->retval( res ); } /*# @method getSession Request @brief Create a new session or returns the session data associated with this session. @optparam sid Explicit session ID synthesized by the script. @return A blessed dictionary that can be used to store session data. @raise WopiError If the session cannot be restored or is expired. Use the return code to determine what happened. This method creates a new session, eventually using an externally provided session ID. If not provided, the session ID is found in cookies or other sources as indicated by the auto-session settings. If a @b sid is provided, the owner is responsible for its creation and maintenance. Possible errors that can be thrown are: - WopiError.SessionFailed - Failed to restore a session. - WopiError.SessionInvalid - Invalid session ID. */ FALCON_FUNC Request_getSession( VMachine *vm ) { CoreRequest *self = dyncast( vm->self().asObject() ); Item *i_sid = vm->param( 0 ); if ( i_sid != 0 && ! i_sid->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).extra( "[S]" ) ); return; } // Get the field name const String& fieldName = self->base()->getSessionFieldName(); Item i_sid_v; bool success; if( i_sid != 0 ) { i_sid_v = *i_sid; success = true; } else { success = self->base()->getField( fieldName, i_sid_v ) && i_sid_v.isString(); } // Get the session id, if it's there. WOPI::SessionData* sd = 0; if( success && *i_sid_v.asString() != "" ) { sd = self->smgr()->getSession( *i_sid_v.asString(), self->base()->sessionToken() ); if( sd == 0 ) { throw new WopiError( ErrorParam( FALCON_ERROR_WOPI_SESS_EXPIRED, __LINE__ ) .desc( "Invalid or unexisting session" ) .extra("getSession:" + *i_sid_v.asString() ) ); } else if ( sd->isInvalid() ) { String desc = sd->errorDesc(); delete sd; // eventually kill the session id if( self->autoSession() ) { self->reply()->setCookie( fieldName, CookieParams() ); } throw new WopiError( ErrorParam( FALCON_ERROR_WOPI_SESS_IO, __LINE__ ) .desc( "Failed to restore the required session" ) .extra( desc ) ); } } else { if( i_sid != 0 ) { sd = self->smgr()->startSession( self->base()->sessionToken(), *i_sid->asString() ); if( sd == 0 ) { throw new WopiError( ErrorParam( FALCON_ERROR_WOPI_SESS_INVALID_ID, __LINE__ ) .desc( "Duplicated Session ID")); } } else { sd = self->smgr()->startSession( self->base()->sessionToken() ); } } // ok; now, should we replicate the session on the cookies? if( self->autoSession() && i_sid == 0 ) { TimeStamp expirets; CookieParams cpar; cpar.value( sd->sID() ); if ( self->smgr()->timeout() > 0 ) { expirets.currentTime(); expirets.changeTimezone(tz_UTC); expirets.add(0,0,0,self->smgr()->timeout()); cpar.expire( &expirets ); } self->reply()->setCookie( fieldName, cpar ); } vm->retval( sd->data() ); } /*# @method startSession Request @brief Create a new session. @optparam sid Explicit session ID synthesized by the script. @return A blessed dictionary that can be used to store session data. @raise WopiError If the session cannot be created. Use the return code to determine what happened. This method creates a new session, using an explicit session ID. The session ID is not automatically saved in cookies nor propagated by any other means. Possible errors that can be thrown are: - WopiError.SessionFailed - Failed create the session (session already existing) */ FALCON_FUNC Request_startSession( VMachine *vm ) { CoreRequest *self = dyncast( vm->self().asObject() ); Item *i_sid = vm->param( 0 ); if ( i_sid == 0 || ! i_sid->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).extra( "[S]" ) ); return; } // Get the field name SessionData* sd = self->smgr()->startSession( self->base()->sessionToken(), *i_sid->asString() ); if( sd == 0 ) { throw new WopiError( ErrorParam( FALCON_ERROR_WOPI_SESS_INVALID_ID, __LINE__ ) .desc( "Duplicated Session ID")); } vm->retval( sd->data() ); } /*# @method closeSession Request @optparam sid Optional explicit SID to be closed @brief Closes a currently open session. If the current script is associated with an open session, the active session is closed. In case the session control keeps track of the session ID using a cookie, this cookie is automatically removed. In case the current script is not associated with a session, this method does nothing. */ FALCON_FUNC Request_closeSession( VMachine *vm ) { CoreRequest *self = dyncast( vm->self().asObject() ); Item *i_sid_param = vm->param( 0 ); if ( i_sid_param != 0 && ! i_sid_param->isString() ) { throw new ParamError( ErrorParam( e_inv_params, __LINE__ ).extra( "[S]" ) ); return; } // explicit SID to be closed? if ( i_sid_param != 0 ) { self->smgr()->closeSession( *i_sid_param->asString(), self->base()->sessionToken() ); // ok, we're done return; } // Get the field name const String& fieldName = self->base()->getSessionFieldName(); Item i_sid; if( self->base()->getField( fieldName, i_sid ) && i_sid.isString() && *i_sid.asString() != "" ) { self->smgr()->closeSession( *i_sid.asString(), self->base()->sessionToken() ); // ok; In case of autosession, close the cookie. if( self->autoSession() ) { self->reply()->clearCookie( fieldName ); self->base()->cookies()->remove( fieldName ); } } } /*# @method hasSession Request @brief Checks if the current script is hosting an open session. @return True if this script supposes that it has an open session. This function checks this script has been provided with a seemingly valid session id; in other words, it checks if @a Request.getSession() will try to open an existing session or if it will create a new session. @a Request.getSession() may fail even if this function returns true, in case the session ID that this script is provided with is invalid or expired, or if an I/O error occurs during session restore. */ FALCON_FUNC Request_hasSession( VMachine *vm ) { CoreRequest *self = dyncast( vm->self().asObject() ); // Get the field name const String& fieldName = self->base()->getSessionFieldName(); Item i_sid; bool res = self->base()->getField( fieldName, i_sid ) && i_sid.isString() && *i_sid.asString() != ""; vm->regA().setBoolean(res); } /*# @method tempFile Request @brief Creates a temporary file. @optparam name If given and passed by reference, it will receive the complete file name. @return A Falcon stream. @raise IoError on open or write error. Temporary streams are automatically deleted when the the script terminates. In case the script want to get the name of the file where the temporary data is stored (i.e. to copy or move it elsewhere after having completed the updates), the parameter @b name needs to be passed by reference, and it will receive the filename. @note The temporary files are stored in the directory specified by the parameter UploadDir in the falcon.ini file. */ FALCON_FUNC Request_tempFile( Falcon::VMachine *vm ) { CoreRequest* request = Falcon::dyncast(vm->self().asObject()); String fname; int64 le; Stream* tgFile = request->base()->makeTempFile( fname, le ); if( tgFile == 0 ) { throw new Falcon::IoError( Falcon::ErrorParam( Falcon::e_io_error, __LINE__ ) .extra( fname ) .sysError( (uint32) le ) ); } Item* i_name = vm->param(0); // create the stream. Falcon::Item *stream_cls = vm->findWKI( "Stream" ); fassert( stream_cls != 0 ); fassert( stream_cls->isClass() ); Falcon::CoreObject *oret = stream_cls->asClass()->createInstance(); oret->setUserData( tgFile ); vm->retval( oret ); // eventually report the filename if ( i_name != 0 && vm->isParamByRef(0) ) { Falcon::CoreString* gs = new Falcon::CoreString( fname ); gs->bufferize(); *i_name = gs; } } void InitRequestClass( Module* self, ObjectFactory cff, ext_func_t init_func ) { // create a singleton instance of %Request class Falcon::Symbol *c_request_o = self->addSingleton( "Request", init_func ); Falcon::Symbol *c_request = c_request_o->getInstance(); c_request->getClassDef()->factory( cff ); self->addClassProperty( c_request, "gets" ); self->addClassProperty( c_request, "posts" ); self->addClassProperty( c_request, "cookies" ); self->addClassProperty( c_request, "headers" ); self->addClassProperty( c_request, "parsed_uri" ); self->addClassProperty( c_request, "autoSession" ); // Fields from apache request_rec self->addClassProperty( c_request, "protocol" ); self->addClassProperty( c_request, "request_time" ); self->addClassProperty( c_request, "method" ); self->addClassProperty( c_request, "bytes_sent" ); self->addClassProperty( c_request, "content_type" ); self->addClassProperty( c_request, "content_encoding" ); self->addClassProperty( c_request, "content_length" ); self->addClassProperty( c_request, "ap_auth_type" ); self->addClassProperty( c_request, "user" ); self->addClassProperty( c_request, "uri" ); self->addClassProperty( c_request, "location" ); self->addClassProperty( c_request, "filename" ); self->addClassProperty( c_request, "path_info" ); self->addClassProperty( c_request, "args" ); self->addClassProperty( c_request, "remote_ip" ); self->addClassProperty( c_request, "sidField" ); self->addClassProperty( c_request, "startedAt" ); self->addClassProperty( c_request, "provider" ); self->addClassMethod( c_request, "getField", &Request_getfield ).asSymbol() ->addParam( "field" )->addParam( "defval" ); self->addClassMethod( c_request, "fwdGet", &Request_fwdGet ).asSymbol() ->addParam( "all" ); self->addClassMethod( c_request, "fwdPost", &Request_fwdPost ).asSymbol() ->addParam( "all" ); self->addClassMethod( c_request, "startSession", &Request_startSession ).asSymbol() ->addParam("sid"); self->addClassMethod( c_request, "getSession", &Request_getSession ).asSymbol() ->addParam("sid"); self->addClassMethod( c_request, "closeSession", &Request_closeSession ); self->addClassMethod( c_request, "hasSession", &Request_hasSession ); self->addClassMethod( c_request, "tempFile", &Request_tempFile ).asSymbol() ->addParam( "name" ); } } } /* end of request_ext.cpp */ modules/native/wopi/src/session_manager.cpp000066400000000000000000000205731176363201700214570ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: session_manager.cpp Falcon Web Oriented Programming Interface Session manager. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 11 Feb 2010 23:17:11 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include namespace Falcon { namespace WOPI { //======================================================================= // Main falcon // SessionData::SessionData( const String& SID ): m_lastError( 0 ), m_sID( SID ), m_bInvalid( false ) { m_dataLock.item() = SafeItem( new CoreDict( new LinearDict ) ); m_dataLock.item().asDict()->bless(true); m_dataLock.item().asDict()->put( SafeItem( new CoreString( "SID" )), SafeItem(new CoreString(sID())) ); } SessionData::~SessionData() { clearRefs(); } void SessionData::touch() { m_touchTime = Sys::_seconds(); } void SessionData::setError( int e ) { m_errorDesc = ""; m_lastError = e; } void SessionData::setError( const String& edesc, int e ) { m_errorDesc = edesc; m_lastError = e; } SessionData::WeakRef* SessionData::getWeakRef() { WeakRef* wr = new WeakRef( this ); m_reflist.push_back( wr ); return wr; } void SessionData::clearRefs() { std::list::iterator iter = m_reflist.begin(); while( iter != m_reflist.end() ) { (*iter)->onDestroy(); ++iter; } m_reflist.clear(); } //======================================================================= // Main session manager // SessionManager::SessionManager(): m_nLastToken(0), m_nSessionTimeout(0) { } SessionManager::~SessionManager() { m_mtx.lock(); SessionMap::iterator iter = m_smap.begin(); while( iter != m_smap.end() ) { SessionData* sd = iter->second; delete sd; ++iter; } m_mtx.unlock(); } static void _init_srand() { static int done = 0; if (! done ) { done = 1; srand( Sys::_getpid() + (time(0)%32000)*32000); } } SessionData* SessionManager::getSession( const Falcon::String& sSID, uint32 token ) { SessionData* sd = 0; bool bCreated; // Should we start a new thread for this? if( timeout() > 0 ) expireOldSessions(); m_mtx.lock(); SessionMap::iterator iter = m_smap.find( sSID ); if( iter != m_smap.end() ) { sd = iter->second; if ( sd == 0 || sd->isAssigned() ) { m_mtx.unlock(); return 0; } sd->assign( token ); // We must manipulate m_susers in the lock to prevent concurrent update // from other threads. m_susers[token].push_back( sd->getWeakRef() ); // now that the session is assigned, we are free to manipulate it outside the lock. m_mtx.unlock(); bCreated = false; } else { // create the session (fast) sd = createSession( sSID ); // assign to our maps m_smap[sSID] = sd; m_susers[token].push_back( sd->getWeakRef() ); // assign the session sd->assign( token ); // try to resume after unlock m_mtx.unlock(); bCreated = true; } // can we resume this session? if( ! sd->resume() ) { // all useless work. m_mtx.lock(); m_smap.erase( sSID ); sd->clearRefs(); m_mtx.unlock(); //If the session was created, we should have done it. if( ! bCreated ) { sd->setInvalid(); } else { delete sd; sd = 0; } } return sd; } SessionData* SessionManager::startSession( uint32 token ) { SessionData* sd = 0; Falcon::String sSID; // No one can possibly use this SD as no one can know it. sd = createUniqueId( sSID ); m_mtx.lock(); m_susers[token].push_back( sd->getWeakRef() ); // assign the session sd->assign( token ); m_mtx.unlock(); return sd; } SessionData* SessionManager::startSession( uint32 token, const Falcon::String &sSID ) { SessionData* sd = 0; m_mtx.lock(); if ( m_smap.find( sSID ) == m_smap.end() ) { sd = createSession(sSID); m_smap[ sSID ] = sd; m_susers[token].push_back( sd->getWeakRef() ); // assign the session sd->assign( token ); } m_mtx.unlock(); // else, it's still 0 return sd; } // release all the sessions associated with this token bool SessionManager::releaseSessions( uint32 token ) { m_mtx.lock(); // do we have the session? SessionUserMap::iterator pos = m_susers.find( token ); if( pos != m_susers.end() ) { // copy the list of sessions to be closed, so that we can work on it. SessionList lCopy = pos->second; m_susers.erase( pos ); m_mtx.unlock(); SessionList::iterator iter = lCopy.begin(); while( iter != lCopy.end() ) { SessionData::WeakRef* wsd = *iter; SessionData* sd = wsd->get(); // Still a valid reference? if( sd != 0 ) { // store on persistent media sd->store(); // mark as used now sd->touch(); // make available for other requests m_mtx.lock(); if( timeout() > 0 ) { m_expmap.insert( ExpirationMap::value_type( sd->lastTouched() + timeout(), sd->getWeakRef() ) ); } sd->release(); m_mtx.unlock(); } wsd->dropped(); ++iter; } } else { m_mtx.unlock(); } return true; } bool SessionManager::closeSession( const String& sSID, uint32 token ) { m_mtx.lock(); SessionMap::iterator iter = m_smap.find( sSID ); if( iter != m_smap.end() ) { SessionData* sd = iter->second; m_smap.erase( sSID ); sd->clearRefs(); m_mtx.unlock(); sd->dispose(); delete sd; return true; } m_mtx.unlock(); return false; } void SessionManager::expireOldSessions() { numeric now = Sys::_seconds(); std::deque expiredSessions; m_mtx.lock(); while( ! m_expmap.empty() && m_expmap.begin()->first < now ) { SessionData::WeakRef* wsd = m_expmap.begin()->second; SessionData* sd = wsd->get(); // Is the session still alive? if ( sd != 0 && sd->lastTouched() + timeout() < now ) { // the data is dead, so we remove it now from the available map m_smap.erase( sd->sID() ); // prevents others (and ourselves) to use it again sd->clearRefs(); // and we push it aside for later clearing expiredSessions.push_back( sd ); } // also, take it away from our expired data m_expmap.erase( m_expmap.begin() ); } m_mtx.unlock(); // now we can destroy the expired sessions std::deque::iterator elem = expiredSessions.begin(); while( elem != expiredSessions.end() ) { SessionData* sd = *elem; sd->dispose(); delete sd; ++elem; } } SessionData* SessionManager::createUniqueId( Falcon::String& sSID ) { static const char* alpha="abcdefghjkilmnopqrstuvwxyzABCDEFGHJKILMNOPQRSTUVWXYZ0123456789"; SessionData* sd = 0; _init_srand(); bool found = false; while( ! found ) { //sSID.N( rand() ).N(rand()).N(rand()); for( int nCount = 0; nCount < 16; nCount++ ) { sSID += alpha[ rand() % 62 ]; } m_mtx.lock(); if ( m_smap.find( sSID ) == m_smap.end() ) { found = true; sd = createSession(sSID); m_smap[ sSID ] = sd; } else { // try again sSID.size(0); } m_mtx.unlock(); } return sd; } uint32 SessionManager::getSessionToken() { m_mtx.lock(); uint32 ret = ++m_nLastToken; // prevent roll-over error if ( ret == 0 ) ret = ++m_nLastToken; m_mtx.unlock(); return ret; } void SessionManager::configFromModule( const Module* mod ) { AttribMap* attribs = mod->attributes(); if( attribs == 0 ) { return; } VarDef* value = attribs->findAttrib( FALCON_WOPI_SESSION_TO_ATTRIB ); if( value != 0 && value->isInteger() ) { m_nSessionTimeout = (uint32) value->asInteger(); } } } } /* end of session_manager.cpp */ modules/native/wopi/src/sharedmem_posix.cpp000066400000000000000000000243121176363201700214640ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: sharedmem_posix.cpp Shared memory mapped object. TODO: Move this file in the main engine in the next version. Interprocess shared-memory object. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 24 Apr 2010 12:12:10 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ // To make SUNC happy #define _POSIX_C_SOURCE 3 #include #include #include #include #include #include #include #include #include #include #include #include #define SEM_PREFIX "/WPS_" #define APP_PREFIX "/WPM_" namespace Falcon { // data in the initial part of the buffer typedef struct tag_BufferData { uint32 version; int32 size; } BufferData; class SharedMemPrivate { public: SharedMemPrivate(): bSemReady(false), shmfd(0), filefd(0), bd(0) {} bool bSemReady; sem_t *sema; int shmfd; int filefd; // Memory mapped data BufferData* bd; }; SharedMem::SharedMem( const String &name ): d( new SharedMemPrivate ), m_version(0) { internal_build( name, "" ); } SharedMem::SharedMem( const String &name, const String &filename ): d( new SharedMemPrivate ), m_version(0) { internal_build( name, filename ); } void SharedMem::internal_build( const String &name, const String &filename ) { String sSemName = SEM_PREFIX; sSemName += name; try { AutoCString csn( sSemName ); // create the semaphore, with an initial value of 0 // so it is initially takeable if( (d->sema = sem_open( csn.c_str(), O_CREAT, 0666, 1 )) == SEM_FAILED ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra( "sem_open " + sSemName ) .sysError( errno ) ); } d->bSemReady = true; //sem_unlink( csn.c_str() ); // ok, get the semaphore. if ( sem_wait( d->sema ) != 0 ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra("sem_wait") .sysError( errno ) ); } // we're the owners of the memory. But, is it new or does it exists? int fd = 0; // the descriptor to map. if( filename != "" ) { // try to map the file AutoCString cfname( filename ); d->filefd = open( cfname.c_str(), O_CREAT | O_RDWR, 0666 ); if( d->filefd <= 0 ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra("open "+ filename ) .sysError( errno ) ); } fd = d->filefd; } else { String sMemName = APP_PREFIX + name; AutoCString cMemName( sMemName ); d->shmfd = shm_open( cMemName.c_str(), O_CREAT | O_RDWR, 0666 ); if( d->shmfd <= 0 ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra("shm_open "+ sMemName ) .sysError( errno ) ); } //shm_unlink( cMemName.c_str() ); fd = d->shmfd; } // eventually prepare the first buffer init(); // ok, let's run. sem_post( d->sema ); } catch( ... ) { if( d->bSemReady ) { sem_post( d->sema ); } close(); delete d; throw; } } SharedMem::~SharedMem() { close(); delete d; } void SharedMem::init() { int fd = d->shmfd <= 0 ? d->filefd : d->shmfd; // ok, we have our stream. See if it needs initialization. off_t pos = lseek( fd, 0, SEEK_END ); if( pos < 0 ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra("lseek" ) .sysError( errno ) ); } // Yes? -- add space. if( pos == 0 ) { if ( ftruncate( fd, sizeof(BufferData) ) != 0 ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra("ftruncate" ) .sysError( errno ) ); } } d->bd = (BufferData*) mmap( 0, sizeof(BufferData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 ); if( d->bd == MAP_FAILED ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra("mmap" ) .sysError( errno ) ); } // if the file wasn't empty, we're done if ( pos != 0 ) { return; } // real initialization d->bd->size = 0; d->bd->version = 0; if( msync( d->bd, sizeof(BufferData), MS_ASYNC ) != 0 ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra("msync" ) .sysError( errno ) ); } } void SharedMem::close() { if( d->bd != MAP_FAILED ) { munmap( d->bd, sizeof(BufferData) ); } // we're in trouble. if( d->bSemReady ) { sem_close( d->sema ); d->bSemReady = false; } if( d->filefd > 0 ) { ::close( d->filefd ); d->filefd = 0; } if( d->shmfd > 0 ) { ::close( d->shmfd ); d->shmfd = 0; } } bool SharedMem::read( Stream* target, bool bAlwaysRead ) { // acquire adequate memory mapping. if( sem_wait( d->sema ) != 0 ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra("sem_wait" ) .sysError( errno ) ); } // be sure we have the right data in msync( d->bd, sizeof(BufferData), MS_SYNC ); // are we aligned? if( m_version != d->bd->version && ! bAlwaysRead ) { sem_post( d->sema ); return false; } // align try { internal_read( target ); sem_post( d->sema ); } catch( ... ) { sem_post( d->sema ); throw; } return true; } bool SharedMem::commit( Stream* source, int32 size, bool bReread ) { // acquire adequate memory mapping. if( sem_wait( d->sema ) != 0 ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra("sem_wait" ) .sysError( errno ) ); } // synchronize the version msync( d->bd, sizeof(BufferData), MS_SYNC ); // are we aligned? if( m_version != d->bd->version ) { // ops, we have a problem. if( bReread ) { // ok, time to update the data. try { internal_read( source ); } catch( ... ) { sem_post( d->sema ); throw; } } sem_post( d->sema ); return false; } // write the new data. int fd = d->shmfd <= 0 ? d->filefd : d->shmfd; if ( ftruncate( fd, size + sizeof(BufferData) ) != 0 ) { sem_post( d->sema ); throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra( String("ftruncate to ").N(size).A( " bytes" ) ) .sysError( errno ) ); } // map the rest of the file void* data = mmap( 0, size + sizeof(BufferData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 ); if( data == MAP_FAILED ) { sem_post( d->sema ); throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra( String("mmap ").N( (int32) size).A(" bytes") ) .sysError( errno ) ); } try { // ok, read the data from our stream (in the final buffer). byte* bdata = ((byte*) data) + sizeof(BufferData); int32 written = 0; while( written < size ) { int32 rin = source->read( bdata + written, size - written ); if( rin > 0 ) { written += rin; } else { // end of stream? if ( rin == 0 ) { size = written; break; } throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra( String("reading from stream") ) .sysError( source->lastError() ) ); } } // update the version m_version++; if( m_version == 0 ) m_version = 1; d->bd->version = m_version; d->bd->size = size; // sync all the buffers, infos and data msync( d->bd, size + sizeof(BufferData), MS_ASYNC ); munmap( data, size ); sem_post( d->sema ); } catch( ... ) { munmap( data, size ); sem_post( d->sema ); throw; } return true; } void SharedMem::internal_read( Stream* target ) { m_version = d->bd->version; int32 size = d->bd->size; int fd = d->shmfd <= 0 ? d->filefd : d->shmfd; // map the rest of the file void* data = mmap( 0, size + sizeof(BufferData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 ); if( data == MAP_FAILED ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra( String("mmap ").N( (int32) size).A(" bytes") ) .sysError( errno ) ); } try { byte* bdata = ((byte*) data) + sizeof(BufferData); int32 written = 0; while( written < size ) { int32 rin = target->write( bdata + written, size - written ); if( rin > 0 ) { written += rin; } else { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra( String("writing to stream") ) .sysError( target->lastError() ) ); } } munmap( data, size ); } catch( ... ) { munmap( data, size ); throw; } } uint32 SharedMem::currentVersion() const { msync( d->bd, sizeof(BufferData), MS_SYNC ); return d->bd->version; } } /* end of sharedmem_posix.cpp */ modules/native/wopi/src/sharedmem_win.cpp000066400000000000000000000251001176363201700211130ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: sharedmem_win.cpp Shared memory mapped object. TODO: Move this file in the main engine in the next version. Interprocess shared-memory object. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 24 Apr 2010 12:12:10 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #define SEM_PREFIX "WOPI_SEM_" #define APP_PREFIX "WOPI_MEM_" namespace Falcon { // data in the initial part of the buffer typedef struct tag_BufferData { uint32 version; int32 size; } BufferData; class SharedMemPrivate { public: SharedMemPrivate(): mtx(INVALID_HANDLE_VALUE), hFile(INVALID_HANDLE_VALUE), hMemory(INVALID_HANDLE_VALUE), bd(0) {} HANDLE mtx; HANDLE hFile; HANDLE hMemory; // Temporary buffer data BufferData* bd; String sMemName; void EnterSession() { if( WaitForSingleObject( this->mtx, INFINITE ) != WAIT_OBJECT_0 ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra("WaitForSingleObject" ) .sysError( GetLastError() ) ); } // be sure we have the right data in this->bd = (BufferData*) MapViewOfFile( this->hMemory, FILE_MAP_WRITE, 0, 0, 0 ); if( this->bd == 0 ) { ReleaseMutex( this->mtx ); throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra("MapViewOfFile" ) .sysError( GetLastError() ) ); } } void ExitSession() { UnmapViewOfFile( this->bd ); this->bd = 0; ReleaseMutex( this->mtx ); } }; SharedMem::SharedMem( const String &name ): d( new SharedMemPrivate ), m_version(0) { internal_build( name, "" ); } SharedMem::SharedMem( const String &name, const String &filename ): d( new SharedMemPrivate ), m_version(0) { internal_build( name, filename ); } void SharedMem::internal_build( const String &name, const String &filename ) { String sSemName = SEM_PREFIX + name; AutoWString csn( sSemName ); // Are we the first around? bool bFirst = false; DWORD dwLastError; try { // try to create the mutex, and take ownership. d->mtx = CreateMutexW( 0, TRUE, csn.w_str() ); if ( d->mtx == INVALID_HANDLE_VALUE ) { if ( (dwLastError = GetLastError()) == ERROR_ALREADY_EXISTS ) { // great, the mutex (and the rest) already exists. d->mtx = OpenMutexW( SYNCHRONIZE, FALSE, csn.w_str() ); if( d->mtx == INVALID_HANDLE_VALUE ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra( "OpenMutex " + sSemName ) .sysError( GetLastError() ) ); } } else { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra( "CreateMutex " + sSemName ) .sysError( dwLastError ) ); } } else { if( GetLastError() != ERROR_ALREADY_EXISTS ) bFirst = true; } HANDLE handle; // we're the owners of the memory. But, is it new or does it exists? if( filename != "" ) { // try to map the file Path winName(filename); AutoWString wfname( winName.getWinFormat() ); d->hFile = CreateFileW( wfname.w_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if( d->hFile == INVALID_HANDLE_VALUE ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra("CreateFile "+ filename ) .sysError( GetLastError() ) ); } handle = d->hFile; } else { handle = INVALID_HANDLE_VALUE; } // create the file mapping. d->sMemName = APP_PREFIX + name; AutoWString wMemName( d->sMemName ); d->hMemory = CreateFileMappingW( handle, 0, PAGE_READWRITE, 0, sizeof( BufferData ), wMemName.w_str() ); if( d->hMemory == INVALID_HANDLE_VALUE ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra("CreateFileMapping "+ d->sMemName ) .sysError( GetLastError() ) ); } // ok, let's run -- if we're the first, we should release the mutex if( bFirst ) { init(); ReleaseMutex( d->mtx ); } } catch( ... ) { if( bFirst ) { ReleaseMutex( d->mtx ); } close(); delete d; throw; } } SharedMem::~SharedMem() { close(); delete d; } void SharedMem::init() { // real initialization BufferData* bd = (BufferData*) MapViewOfFile( d->hMemory, FILE_MAP_WRITE, 0, 0, sizeof( BufferData ) ); if( bd == NULL ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra("MapViewOfFile" ) .sysError( GetLastError() ) ); } if( bd->version == 0 ) { bd->size = 0; } UnmapViewOfFile( bd ); } void SharedMem::close() { if ( d->bd != NULL ) { UnmapViewOfFile( d->bd ); d->bd = NULL; } if( d->hMemory != INVALID_HANDLE_VALUE ) { CloseHandle( d->hMemory ); d->hMemory = INVALID_HANDLE_VALUE; } if( d->hFile != INVALID_HANDLE_VALUE ) { CloseHandle( d->hFile ); d->hFile = INVALID_HANDLE_VALUE; } if( d->mtx != INVALID_HANDLE_VALUE ) { CloseHandle( d->mtx ); d->mtx = INVALID_HANDLE_VALUE; } } bool SharedMem::read( Stream* target, bool bAlwaysRead ) { d->EnterSession(); // are we aligned? if( m_version != d->bd->version && ! bAlwaysRead ) { d->ExitSession(); return false; } // align try { internal_read( target ); d->ExitSession(); } catch( ... ) { d->ExitSession(); throw; } return true; } bool SharedMem::commit( Stream* source, int32 size, bool bReread ) { // acquire adequate memory mapping. d->EnterSession(); if( d->bd == NULL ) { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra("MapViewOfFile" ) .sysError( GetLastError() ) ); } // are we aligned? if( m_version != d->bd->version ) { // ops, we have a problem. if( bReread ) { // ok, time to update the data. try { internal_read( source ); } catch( ... ) { d->ExitSession(); throw; } } d->ExitSession(); return false; } if( d->hFile != INVALID_HANDLE_VALUE ) { // try to resize the file SetFilePointer( d->hFile, size + sizeof(BufferData), 0, FILE_BEGIN ); SetEndOfFile( d->hFile ); } // write the new data -- changing the file view AutoWString wMemName( d->sMemName ); HANDLE hView = CreateFileMappingW( d->hFile, 0, PAGE_READWRITE, 0, size + sizeof(BufferData), wMemName.w_str() ); if ( hView == INVALID_HANDLE_VALUE ) { d->ExitSession(); throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra( String("CreateFileMappingW to ").N(size).A( " bytes" ) ) .sysError( GetLastError() ) ); } UnmapViewOfFile( d->bd ); d->bd = 0; void* data = MapViewOfFile( d->hMemory, FILE_MAP_WRITE, 0, 0, 0 ); if( data == NULL ) { CloseHandle( hView ); ReleaseMutex( d->mtx ); throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra( String("MapViewOfFile ").N( (int32) size).A(" bytes") ) .sysError( GetLastError() ) ); } try { // ok, read the data from our stream (in the final buffer). byte* bdata = ((byte*) data) + sizeof(BufferData); int32 written = 0; while( written < size ) { int32 rin = source->read( bdata + written, size - written ); if( rin > 0 ) { written += rin; } else { // end of stream? if ( rin == 0 ) { size = written; break; } throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra( String("reading from stream") ) .sysError( (int32) source->lastError() ) ); } } // update the version m_version++; if( m_version == 0 ) m_version = 1; BufferData* bd = (BufferData*) data; bd->version = m_version; bd->size = size; // sync all the buffers, infos and data UnmapViewOfFile( data ); // change the old view with the new one CloseHandle( d->hMemory ); d->hMemory = hView; ReleaseMutex( d->mtx ); } catch( ... ) { UnmapViewOfFile( data ); CloseHandle( hView ); ReleaseMutex( d->mtx ); throw; } return true; } void SharedMem::internal_read( Stream* target ) { m_version = d->bd->version; int32 size = d->bd->size; // map the rest of the file byte* bdata = (byte*) (d->bd + 1); int32 written = 0; while( written < size ) { int32 rin = target->write( bdata + written, size - written ); if( rin > 0 ) { written += rin; } else { throw new IoError( ErrorParam( e_io_error, __LINE__ ) .extra( String("writing to stream") ) .sysError( (int32) target->lastError() ) ); } } } uint32 SharedMem::currentVersion() const { d->EnterSession(); uint32 version = d->bd->version; d->ExitSession(); return version; } } /* end of sharedmem_win.cpp */ modules/native/wopi/src/uploaded_ext.cpp000066400000000000000000000330301176363201700207470ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: uploaded_ext.cpp Web Oriented Programming Interface Wrapper for uploaded data files. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 23 Apr 2010 11:24:16 -0700 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include /*# @beginmodule WOPI */ namespace Falcon { namespace WOPI { //============================================ // Uploaded class - class managing uploads. //============================================ /*# @class Uploaded @brief Entity storing uploaded file data. Forms containing uploadable files are returned in the @a Request.posts dictionary. In those forms, the entries coresponding to uploaded files are stored as instances of this class. The class has support to access the temporary storage where the uploaded file has been placed, to read it in memory or to access the memory where the system has stored it. For more informations read the @a upload_control entry. @prop size The size of the uploaded file, in bytes. @prop mimeType The declared mime type of the uploaded data. This is Determined by the remote browser uploading the file, so it cannot be trusted. @prop filename The name of the original file before upload. Can be used as an hint of the name it should be given locally, or for extra confirmation of the mime type. @prop storage Complete path to an accessible file in the local filesystem. This is where the server has temporarily stored the file. It may be destroeyd as soon as the script is closed. If the file was impored directly in memory, this field will be nil. @prop data Complete content of the file, as a byte-sized MemBuf. If the server was setup to store the file in memory as it is received, this field will be valorized with a MemBuf containing all the data in the file. If this option is not enabled, the property will be nil. */ /*# @method read Uploaded @brief Reads an uploaded file from the temporary storage into memory. @return True if the file was actually read, false if it was already stored in memory. @raise IoError on read error. @raise TypeError if the storage property is not a valid filename. If the uploaded file coresponding to this entry was stored in a temporary local file (in the @b storage property), this method reads it in a wide-enough MemBuf and stores it in the @b data property. It is possible to use this method to make sure that the whole file is in the @b data property after a size check. @note The server may prevent this operation to be completed if the file is too large. Where in doubt, prefer @b Uploaded.open, which has the same semantic but that is more flexible and resource-aware. */ FALCON_FUNC Uploaded_read( Falcon::VMachine *vm ) { Falcon::CoreObject *self = vm->self().asObject(); Falcon::Item i_data; if ( self->getProperty( "data", i_data ) && ( i_data.isMemBuf() || i_data.isString() ) ) { vm->regA().setBoolean( false ); return; } Falcon::Item i_storage; if ( ! self->getProperty( "storage", i_storage ) || ! i_storage.isString() ) { // invalid storage? throw new Falcon::TypeError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ). extra( ".storage" ) ); } Falcon::FileStream fs; if ( ! fs.open( *i_storage.asString(), Falcon::BaseFileStream::e_omReadOnly, Falcon::BaseFileStream::e_smShareRead ) ) { throw new Falcon::IoError( Falcon::ErrorParam( Falcon::e_io_error, __LINE__ ). sysError( (uint32) fs.lastError() ) ); } Falcon::int64 filesize = fs.seekEnd(0); Falcon::MemBuf *mb = new Falcon::MemBuf_1( (uint32) filesize ); fs.seekBegin( 0 ); Falcon::int64 readIn = 0; while ( readIn < filesize ) { Falcon::int32 len = fs.read( mb->data() + readIn, (int32)( filesize - readIn ) ); if ( len < 0 ) { throw new Falcon::IoError( Falcon::ErrorParam( Falcon::e_io_error, __LINE__ ). sysError( (uint32) fs.lastError() ) ); // anyhow, try to close to avoid system leaks. fs.close(); } readIn += len; } self->setProperty( "data", mb ); if ( ! fs.close() ) { throw new Falcon::IoError( Falcon::ErrorParam( Falcon::e_io_error, __LINE__ ). sysError( (uint32) fs.lastError() ) ); } vm->regA().setBoolean( true ); } /*# @method store Uploaded @brief Stores the uploaded data into a file. @param path The location where to store the file, or an open stream. @raise IoError on read or write error. @raise TypeError if the storage property is not a valid filename. If @b data is filled, this method saves its contents into the file indicated by the @b path parameter; if it was stored to a temporary file, a system file move is tried, if it fails, a file copy is tried, and the origin file is removed after the copy is succesful. On failure, a relevant IoError is raised, but the operation doesn't fail in case the original file cannot be deleted. @note This method can be also used to move or copy an arbitrary file by storing a path directly in the @b storage property. */ FALCON_FUNC Uploaded_store( Falcon::VMachine *vm ) { Falcon::Item *ip_path = vm->param(0); if ( ip_path == 0 || ( ! ip_path->isString() && ! ip_path->isOfClass( "Stream" ) ) ) { throw new Falcon::ParamError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ) .extra( "S|Stream" ) ); } Falcon::CoreObject *self = vm->self().asObject(); //=============================================== // Do we store from data? // Falcon::Item i_data; if ( self->getProperty( "data", i_data ) && ( i_data.isMemBuf() || i_data.isString() ) ) { Falcon::FileStream* tgFile = 0; Falcon::Stream *tgStream; // try to save to ip_path; if ( ip_path->isString() ) { // try to open the file. tgFile = new Falcon::FileStream; if ( ! tgFile->create( *ip_path->asString(), Falcon::BaseFileStream::e_aUserRead | Falcon::BaseFileStream::e_aUserWrite ) ) { Falcon::int64 le = tgFile->lastError(); delete tgFile; throw new Falcon::IoError( Falcon::ErrorParam( Falcon::e_io_error, __LINE__ ). sysError( (uint32) le ) ); } tgStream = tgFile; } else tgStream = static_cast( ip_path->asObject()->getUserData() ); Falcon::int64 written = 0; Falcon::uint32 len = i_data.isString() ? i_data.asString()->size() : i_data.asMemBuf()->size(); Falcon::byte* data = i_data.isString() ? i_data.asString()->getRawStorage() : i_data.asMemBuf()->data(); int wrt = 0; while ( written < len ) { wrt = tgStream->write( data + written, (uint32)( len - written ) ); if ( wrt < 0 ) { Falcon::int64 le = tgStream->lastError(); delete tgFile; throw new Falcon::IoError( Falcon::ErrorParam( Falcon::e_io_error, __LINE__ ). sysError( (uint32) le ) ); } written += wrt; } if ( ! tgStream->close() ) { Falcon::int64 le = tgStream->lastError(); delete tgFile; // ok also if 0 throw new Falcon::IoError( Falcon::ErrorParam( Falcon::e_io_error, __LINE__ ) .sysError( (uint32)le ) ); } // success return; } // ============================================= // Do we store from storage? Falcon::Item i_storage; if ( ! self->getProperty( "storage", i_storage ) || ! i_storage.isString() ) { // invalid storage? throw new Falcon::TypeError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ) .extra( ".storage" ) ); } const Falcon::String &fname = *i_storage.asString(); // First, try to rename the file. if ( ip_path->isString() ) { Falcon::int32 status; // fname -> dest if ( Falcon::Sys::fal_move( fname, *ip_path->asString(), status ) ) return; // great, we did it } Falcon::FileStream infile; if ( ! infile.open( fname, Falcon::BaseFileStream::e_omReadOnly ) || ! infile.good() ) { throw new Falcon::IoError( Falcon::ErrorParam( Falcon::e_io_error, __LINE__ ). sysError( (uint32) infile.lastError() ) ); } // no way? -- try to copy it. Falcon::FileStream* tgFile = 0; Falcon::Stream *tgStream; // try to save to ip_path; if ( ip_path->isString() ) { // try to open the file. tgFile = new Falcon::FileStream(); if ( ! tgFile->create( *ip_path->asString(), Falcon::BaseFileStream::e_aUserRead | Falcon::BaseFileStream::e_aUserWrite ) ) { Falcon::int64 le = tgFile->lastError(); delete tgFile; throw new Falcon::IoError( Falcon::ErrorParam( Falcon::e_io_error, __LINE__ ). sysError( (uint32) le ) ); } tgStream = tgFile; } else tgStream = static_cast( ip_path->asObject()->getUserData() ); Falcon::byte buffer[2048]; int wrt = 0; while ( ! infile.eof() ) { wrt = infile.read( buffer, 2048 ); if ( wrt < 0 ) { delete tgFile; throw new Falcon::IoError( Falcon::ErrorParam( Falcon::e_io_error, __LINE__ ). sysError( (uint32) infile.lastError() ) ); } wrt = tgStream->write( buffer, wrt ); if ( wrt < 0 ) { Falcon::int64 le = tgStream->lastError(); delete tgFile; throw new Falcon::IoError( Falcon::ErrorParam( Falcon::e_io_error, __LINE__ ). sysError( (uint32) le ) ); } } // silently try to unlink the source file infile.close(); Falcon::int32 status; Falcon::Sys::fal_unlink( fname, status ); // do not raise again if in error if ( ! tgStream->close() ) { Falcon::int64 le = tgStream->lastError(); delete tgFile; throw new Falcon::IoError( Falcon::ErrorParam( Falcon::e_io_error, __LINE__ ) .sysError( (uint32) le ) ); } delete tgFile; } /*# @method open Uploaded @brief Opens a read-only Falcon Stream pointing to the uploaed file. @return A Falcon stream. @raise IoError on open or write error. @raise TypeError if the storage property is not a valid filename. If @b data is filled, this method creates a memory read-only StringStream accessing the data as a file. If it was stored in a temporary file named as reported by the @b storage property, that file is open in read-only/shared mode. This method allows to obtain a valid readable stream no matter if the uploaded file was cached in memory or temporarily stored to disk. */ FALCON_FUNC Uploaded_open( Falcon::VMachine *vm ) { Falcon::CoreObject *self = vm->self().asObject(); Falcon::Item i_data; // try to read the data. Falcon::Stream *ret = 0; if ( self->getProperty( "data", i_data ) ) { if ( i_data.isMemBuf() ) { ret = new Falcon::StringStream(); Falcon::MemBuf *mb = i_data.asMemBuf(); ret->write( mb->data(), mb->size() ); ret->seekBegin(0); } else if ( i_data.isString() ) { ret = new Falcon::StringStream( *i_data.asString() ); } } // try to load from storage if we didn't create a stream. if ( ret == 0 ) { Falcon::Item i_storage; if ( ! self->getProperty( "storage", i_storage ) || ! i_storage.isString() ) { // invalid storage? throw new Falcon::TypeError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ) .extra( ".storage" ) ); } Falcon::FileStream *temp = new Falcon::FileStream(); if ( ! temp->open( *i_storage.asString(), Falcon::BaseFileStream::e_omReadOnly ) ) { Falcon::int64 le = temp->lastError(); delete temp; throw new Falcon::IoError( Falcon::ErrorParam( Falcon::e_io_error, __LINE__ ) .sysError( (uint32) le ) ); } ret = temp; } // create the stream. Falcon::Item *stream_cls = vm->findWKI( "Stream" ); fassert( stream_cls != 0 ); fassert( stream_cls->isClass() ); Falcon::CoreObject *oret = stream_cls->asClass()->createInstance(); oret->setUserData( ret ); vm->retval( oret ); } void InitUploadedClass( Module* self ) { // Create a class for uploaded files Falcon::Symbol *c_upfile = self->addClass( "Uploaded" ); c_upfile->setWKS( true ); // we don't need an object manager as we don't have internal data. self->addClassProperty( c_upfile, "size" ); self->addClassProperty( c_upfile, "mimeType" ); self->addClassProperty( c_upfile, "filename" ); self->addClassProperty( c_upfile, "storage" ); self->addClassProperty( c_upfile, "data" ); self->addClassProperty( c_upfile, "error" ); self->addClassMethod( c_upfile, "read", &Uploaded_read ); self->addClassMethod( c_upfile, "open", &Uploaded_open ); self->addClassMethod( c_upfile, "store", &Uploaded_store ).asSymbol() ->addParam( "path" ); } } } /* end of uploaded_ext.cpp */ modules/native/wopi/src/utils.cpp000066400000000000000000000201131176363201700174300ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: utils.cpp Web Oriented Programming Interface (WOPI) Utilities. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 13 Feb 2010 14:10:56 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include #include #include #ifdef FALCON_SYSTEM_WIN #include #include #else #include #endif namespace Falcon { namespace WOPI { namespace Utils { void fieldsToUriQuery( const ItemDict& fields, String& target ) { Iterator iter( const_cast(&fields) ); while( iter.hasCurrent() ) { if( target.size() != 0 ) target += "&"; String name, value; if( iter.getCurrentKey().isString() ) { URI::URLEncode( *iter.getCurrentKey().asString(), name ); if( iter.getCurrent().isString() ) { URI::URLEncode( *iter.getCurrent().asString(), value ); target += name + "=" + value; } else if( iter.getCurrent().isArray() ) { CoreArray* arr = iter.getCurrent().asArray(); for( uint32 i = 0; i < arr->length(); ++i ) { Item& str = arr->at(i); if( str.isString() ) { URI::URLEncode( *str.asString(), value ); target += name + "[]=" + value; } } } // else, just drop the value } iter.next(); } } void dictAsInputFields( String& fwd, const ItemDict& items ) { Falcon::Iterator iter( const_cast(&items) ); while ( iter.hasCurrent() ) { const Falcon::Item &key = iter.getCurrentKey(); const Falcon::Item &value = iter.getCurrent(); if ( key.isString() && value.isString() ) { fwd += "\n"; } iter.next(); } } void htmlEscape( const String& str, String& fwd ) { for ( Falcon::uint32 i = 0; i < str.length(); i++ ) { Falcon::uint32 chr = str[i]; switch ( chr ) { case '<': fwd.append( "<" ); break; case '>': fwd.append( ">" ); break; case '"': fwd.append( """ ); break; case '&': fwd.append( "&" ); break; default: fwd.append( chr ); } } } CoreObject* makeURI( const URI& uri ) { VMachine* vm = VMachine::getCurrent(); Item* i_uric = vm->findGlobalItem("URI"); fassert( i_uric != 0 ); fassert( i_uric->isClass() ); return i_uric->asClass()->createInstance( new URI(uri), false ); } void parseQuery( const String &query, ItemDict& dict ) { // query is a set of zero or more & separated utf-8 url-encoded strings. // we must first find the & chars String section, key, value; uint32 pos1, pos2; pos1 = 0; pos2 = query.find( "&" ); while ( pos1 != String::npos ) { // get the substring section = query.subString( pos1, pos2 ); parseQueryEntry( section, dict ); // else, the record was malformed // What to do? if ( pos2 != String::npos ) { pos1 = pos2 + 1; pos2 = query.find( "&", pos1 ); } else break; } } void parseQueryEntry( const String &query, ItemDict& dict ) { bool proceed = false; String key; CoreString& value = *(new CoreString); // get the = uint32 poseq = query.find( "=" ); if( poseq != String::npos ) { if ( URI::URLDecode( query.subString( 0, poseq ), key ) && URI::URLDecode( query.subString( poseq + 1 ), value ) ) { proceed = true; } } else { if( URI::URLDecode( query, key ) ) { proceed = true; value = ""; } } key.trim(); if ( proceed ) { value.bufferize(); addQueryVariable( key, &value, dict ); } } void addQueryVariable( const String &key, const Item& value, ItemDict& dict ) { // is this a dictionary? if( key.endsWith("[]") ) { String short_key = key.subString(0, key.length()-2); // create an array Item *arr = dict.find( short_key ); if ( arr != 0 ) { if ( ! arr->isArray() ) { Item* temp = arr; *arr = new CoreArray; arr->asArray()->append( *temp ); } arr->asArray()->append( value ); } else { CoreArray *carr = new CoreArray; carr->append( value ); dict.put( new CoreString( short_key ), carr ); } } else if ( key.endsWith("]") ) { // must be a dictionary entry. uint32 pos = key.find("["); if( pos == 0 || pos == String::npos ) { // ignore and go on dict.put( new CoreString(key), value ); } else { String genPart = key.subString( 0, pos ); String specPart = key.subString( pos+1, key.length()-1 ); specPart.trim(); if( (specPart[0] == '"' && specPart[specPart.length()-1] == '"') || (specPart[0] == '\'' && specPart[specPart.length()-1] == '\'') ) { specPart = specPart.subString(1,specPart.length()-1); } // else, create a dictionary Item *arr = dict.find( genPart ); ItemDict* keyDict; if( arr == 0 ) { keyDict = new LinearDict; dict.put( new CoreString( genPart), Item(new CoreDict(keyDict)) ); } else { if( ! arr->isDict() ) { keyDict = new LinearDict; keyDict->put( Item(), *arr ); dict.put( new CoreString(genPart), Item(new CoreDict(keyDict)) ); } else { keyDict = &arr->asDict()->items(); } } keyDict->put( new CoreString(specPart), value ); } } else { dict.put( new CoreString(key), value ); } } bool parseHeaderEntry( const String &line, String& key, String& value ) { /*String l = sline; l.c_ize(); String line; line.fromUTF8( (char*) l.getRawStorage() );*/ uint32 pos = line.find( ":" ); if( pos != String::npos ) { key = line.subString( 0, pos ); if( line.endsWith("\r\n") ) { value = line.subString( pos+1, line.length()-2 ); } else { value = line.subString( pos+1 ); } unescapeQuotes( value ); return true; } return false; } void makeRandomFilename( String& target, int size ) { static const char* alphabeth= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; target.reserve( size ); for ( int i = 0; i < size; i ++ ) { target.append( alphabeth[rand() % 63] ); } } void unescapeQuotes( Falcon::String &str ) { str.trim(); Falcon::uint32 len = str.length(); if ( len > 1 ) { if ( str.getCharAt(0) == '"' && str.getCharAt(len-1) == '"' ) { str.remove(0,1); str.remove( len-2, 1 ); len -= 2; Falcon::uint32 i = 0; while ( i + 1 < len ) { if ( str.getCharAt(i) == '\\' && str.getCharAt(i+1) == '"' ) { str.remove( i, 1 ); } i++; } } } } void xrandomize() { uint32 pid; #ifdef FALCON_SYSTEM_WIN pid = (uint32) GetCurrentProcessId(); #else pid = (uint32) getpid(); #endif // time randomization is not enough // as we may be called in the same seconds from different processes. srand( (unsigned)time( NULL ) + pid ); } } } } /* end of utils.h */ modules/native/wopi/src/wopi.cpp000066400000000000000000000172751176363201700172650ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: wopi.cpp Falcon Web Oriented Programming Interface. Global WOPI application objects. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 25 Apr 2010 17:02:10 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include namespace Falcon { namespace WOPI { Wopi::Wopi(): m_pdata( Wopi::pdata_deletor ) { } Wopi::~Wopi() { AppDataMap::iterator iter = m_admap.begin(); while( iter != m_admap.end() ) { delete iter->second; ++iter; } } void Wopi::pdata_deletor( void* data ) { // maybe an overkill, but... if ( data == 0 ) return; PDataMap* pm = (PDataMap*) data; PDataMap::iterator iter = pm->begin(); while( iter != pm->end() ) { delete iter->second; ++iter; } delete pm; } void Wopi::dataLocation( const String& loc ) { m_mtx.lock(); m_sAppDataLoc = loc; m_mtx.unlock(); } String Wopi::dataLocation() { m_mtx.lock(); String ret = m_sAppDataLoc; m_mtx.unlock(); return ret; } bool Wopi::setData( Item& data, const String& appName, bool atomicUpdate ) { SharedMem* pAppMem = 0; // do we have the required appname data? m_mtx.lock(); AppDataMap::const_iterator pos = m_admap.find( appName ); if( pos == m_admap.end() ) { m_mtx.unlock(); pAppMem = inner_create_appData( appName ); } else { pAppMem = pos->second; m_mtx.unlock(); } // we can deal with the shared memory in an unlocked region, of course. if ( pAppMem->currentVersion() != pAppMem->lastVersion() ) { // we already know we're out of sync. if( atomicUpdate ) inner_readData( pAppMem, data ); return false; } // ok, try and serialize the data. StringStream source; Item::e_sercode sc = data.serialize( &source, false ); if( sc != Item::sc_ok ) { throw new WopiError( ErrorParam( FALCON_ERROR_WOPI_APPDATA_SER, __LINE__ ) .desc( "Error during Serialization of application data") .extra( String("type ").N( (int) sc ) ) ); } // great, the data is serialized; try to get it out of the door. int32 datalen = (int32) source.tell(); source.seekBegin(0); bool bSuccess = pAppMem->commit( &source, datalen, atomicUpdate ); // did we succeed? if( ! bSuccess ) { // No; we wasted the serialization. However, are now required to // deserialize the item? if( atomicUpdate ) { // ... and, is there anything to be de-serialized? if( source.tell() != 0 ) { source.seekBegin(0); Item::e_sercode sc = data.deserialize( &source, VMachine::getCurrent() ); if( sc != Item::sc_ok ) { throw new WopiError( ErrorParam( FALCON_ERROR_WOPI_APPDATA_DESER, __LINE__ ) .desc( "Error during de-serialization of application data") .extra( String("type ").N( (int) sc ) ) ); } } else { data.setNil(); } } } return bSuccess; } bool Wopi::getData( Item& data, const String& appName ) { SharedMem* shmem = 0; // do we have the required appname data? m_mtx.lock(); AppDataMap::const_iterator pos = m_admap.find( appName ); if( pos == m_admap.end() ) { m_mtx.unlock(); shmem = inner_create_appData( appName ); } else { shmem = pos->second; m_mtx.unlock(); } // we can deal with the shared memory in an unlocked region, of course. inner_readData( shmem, data ); return true; } SharedMem* Wopi::inner_create_appData( const String& appName ) { SharedMem* pAppMem = 0; // a new application data. Maybe. String sAppName = appName == "" ? "DFLT_" : "N_" + appName; // shall we get a backed up application data? if( m_sAppDataLoc != "" ) { String sAppLoc = appName == "" ? m_sAppDataLoc + "/_WOPI_DEFAULT_DATA" : m_sAppDataLoc + "/" + appName + ".fdt"; pAppMem = new SharedMem( sAppName, sAppLoc ); } else { pAppMem = new SharedMem( sAppName ); } // ok; but someone may have added the shared mem in the meanwhile. // If it's so, ok, np. Just discard our copy. m_mtx.lock(); AppDataMap::const_iterator pos = m_admap.find( appName ); if( pos == m_admap.end() ) { m_admap[ appName ] = pAppMem; m_mtx.unlock(); } else { SharedMem* pOld = pAppMem; pAppMem = pos->second; m_mtx.unlock(); delete pOld; } return pAppMem; } void Wopi::inner_readData( SharedMem* shmem, Item& data ) { StringStream target; shmem->read( &target, true ); if ( target.tell() == 0 ) { // nothing to be deserialized. data.setNil(); return; } target.seekBegin(0); Item::e_sercode sc = data.deserialize( &target, VMachine::getCurrent() ); if( sc != Item::sc_ok ) { throw new WopiError( ErrorParam( FALCON_ERROR_WOPI_APPDATA_DESER, __LINE__ ) .desc( "Error during de-serialization of application data") .extra( String("type ").N( (int) sc ) ) ); } } bool Wopi::setPersistent( const String& id, const Item& data ) { // get the thread-specific data map PDataMap* pm = (PDataMap*) m_pdata.get(); // we don't have it? if( pm == 0 ) { pm = new PDataMap; m_pdata.set( pm ); } // search the key PDataMap::iterator iter = pm->find( id ); // already around? if( iter != pm->end() ) { GarbageLock* gl = iter->second; // if data is NIL, we can destroy the entry if( data.isNil() ) { delete gl; pm->erase(iter); } else { gl->item() = data; } return false; } else if ( ! data.isNil() ) { (*pm)[id] = new GarbageLock( data ); } return true; } bool Wopi::getPeristent( const String& id, Item& data ) const { // get the thread-specific data map PDataMap* pm = (PDataMap*) m_pdata.get(); // we don't have it? if( pm == 0 ) { // then we can hardly have the key return false; } // search the key PDataMap::iterator iter = pm->find( id ); // already around? if( iter != pm->end() ) { GarbageLock* gl = iter->second; data = gl->item(); return true; } // didn't find it return false; } //======================================================================== // CoreWopi // CoreWopi::CoreWopi( const CoreClass* parent ): CoreObject( parent ), m_wopi( 0 ) { } CoreWopi::~CoreWopi() { } CoreObject *CoreWopi::clone() const { return 0; } bool CoreWopi::setProperty( const String &prop, const Item &value ) { readOnlyError( prop ); return false; } bool CoreWopi::getProperty( const String &prop, Item &value ) const { return defaultProperty( prop, value ); } void CoreWopi::configFromModule( const Module* mod ) { AttribMap* attribs = mod->attributes(); if( attribs == 0 ) { return; } VarDef* value = attribs->findAttrib( FALCON_WOPI_PDATADIR_ATTRIB ); if( value != 0 && value->isString() ) { wopi()->dataLocation( *value->asString() ); } } CoreObject* CoreWopi::factory( const CoreClass *cls, void *, bool ) { return new CoreWopi( cls ); } } } /* end of wopi.cpp */ modules/native/wopi/src/wopi_ext.cpp000066400000000000000000000504561176363201700201430ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. FILE: wopi_ext.h Web Oriented Programming Interface Object encapsulating requests. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2007-07-28 18:55:17 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include /*# @beginmodule WOPI */ namespace Falcon { namespace WOPI { static void internal_htmlEscape_stream( const Falcon::String &str, Falcon::Stream *out ) { for ( Falcon::uint32 i = 0; i < str.length(); i++ ) { Falcon::uint32 chr = str[i]; switch ( chr ) { case '<': out->writeString( "<" ); break; case '>': out->writeString( ">" ); break; case '"': out->writeString( """ ); break; case '&': out->writeString( "&" ); break; default: out->put( chr ); } } } /*# @global scriptName @brief Physical name of the loaded main script. This is the name of the script that has been loaded and served through the web server request. It may be different from the file name that has been actually requested, as the web server may perform several mappings. scriptPath + "/" + scriptName is granted to give the complete file path where the script has been loaded. */ /*# @global scriptPath @brief Physical path of the loaded main script. This is the directory where the script that has been served through the web server request has been loaded from. It may be different from the path requested, as the web server may perform several path mappings. scriptPath + "/" + scriptName is granted to give the complete file path where the script has been loaded. This information can be useful to the scripts to know the base directory of their main code, and use that to perform relative operations on the filesystem. @note scriptPath won't be changed through submodules; it stays the same all through one execution. */ /*# @object Wopi @brief General framework data and functions This object allows to access to general purpose functions exposed by the framework, and to global framework settings. A few utility function exposed by the Wopi model are directly exported to the main namespace for convenience and because they are widely used. Every other functionality is exposed through @a Request, @a Reply or @a Wopi. */ static void Wopi_init( Falcon::VMachine* vm ) { static Wopi wopi; dyncast(vm->self().asObject())->setWopi( &wopi ); } /*# @method getAppData Wopi @brief Gets global, persistent web application data. @optparam app Application name for the specific data. This method restores application-wide data set through the @a Wopi.setData method. The optional parameter @b app may be specified to indicate a different applicaiton name under which the data are to be saved and restored. See @a wopi_appdata for further details. */ static void Wopi_getAppData( Falcon::VMachine *vm ) { // Get name of the file. Falcon::Item *i_app = vm->param( 0 ); // parameter sanity check. if ( i_app != 0 && ! (i_app->isString() ||i_app->isNil()) ) { throw new Falcon::ParamError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ). extra( "[S]" ) ); } Wopi* self = dyncast( vm->self().asObject() )->wopi(); bool success = self->getData( vm->regA(), i_app == 0 || i_app->isNil() ? "" : *i_app->asString() ); if( !success ) vm->retnil(); } /*# @method setAppData Wopi @brief Sets global, persistent web application data. @param data The data to be saved (in a dictionary) @optparam app Application name for the specific data. @return True if the synchronization with the application data was successful, false in case the data was already changed. This method saves application-wide data, that can be retrieved elsewhere to have persistent data. The optional parameter @b app may be specified to indicate a different applicaiton name under which the data are to be saved and restored. @note Important: be sure that the data to be saved can be safely and directly serialized. The function may return false if the synchronization failed. If you want to atomically receive the new contents of the data, pass it by reference. See @a wopi_appdata for further details. */ static void Wopi_setAppData( Falcon::VMachine *vm ) { // Get name of the file. Falcon::Item *i_data = vm->param( 0 ); Falcon::Item *i_app = vm->param( 1 ); // parameter sanity check. if ( i_data == 0 || (i_app != 0 && ! (i_app->isString() ||i_app->isNil())) ) { throw new Falcon::ParamError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ). extra( "D,[S]" ) ); } Wopi* self = dyncast( vm->self().asObject() )->wopi(); vm->regA().setBoolean( self->setData( *i_data, i_app == 0 || i_app->isNil() ? "" : *i_app->asString(), vm->isParamByRef(0) ) ); } static bool Wopi_getPData_NEXT( Falcon::VMachine *vm ) { fassert( vm->param(0)->isString() ); // we're in the frame where we're the self! Wopi* self = dyncast( vm->self().asObject() )->wopi(); if( ! vm->regA().isNil() ) { self->setPersistent( *vm->param(0)->asString(), vm->regA() ); } return false; // we're done, A can be returned. } /*# @method getPData Wopi @brief Gets local per-thread persistent data @param id Unique ID for persistent data. @optparam func Function to create the data if it's not ready. @return The previously saved item, or nil if not found. This method restores process or thread specific persistent data that was previously saved, possibly during another execution and in another virtual machine, via @a Wopi.setPData. An optional @b func parameter is called in case the data under the given @b id is not found; the return value of that function is then stored in the persistent data slot, as if @a Wopi.setPData was called with the same @b id to save the data, and is then returned to the caller of this method. See @a wopi_pdata for further details. */ static void Wopi_getPData( Falcon::VMachine *vm ) { // Get the ID of the persistent data Falcon::Item *i_id = vm->param( 0 ); Falcon::Item *i_func = vm->param( 1 ); // parameter sanity check. if ( i_id == 0 || ! i_id->isString() || ( i_func != 0 && ! i_func->isCallable() ) ) { throw new Falcon::ParamError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ). extra( "S,[C]" ) ); } Wopi* self = dyncast( vm->self().asObject() )->wopi(); bool success = self->getPeristent( *i_id->asString(), vm->regA() ); // Not found? if( ! success ) { // should we initialize the data? if ( i_func != 0 ) { // callback ourself in our same frame: we need self and param(0) vm->returnHandler( Wopi_getPData_NEXT ); // then prepare the new farme vm->callFrame( *i_func, 0 ); } else { vm->regA().setNil(); // just in case } } // otherwise, all ok } /*# @method setPData Wopi @brief Gets local per-thread persistent data. @param id Unique ID for persistent data. @param item A data to be stored persistently. This method saves process or thread specific data, to be retrieved, eventually by another virtual machine at a later execution under the same thread or process. Persistent data is identified by an unique ID. An application can present different persistent data naming them differently. @note The id must be a valid string, including an empty string. So, "" can be used as a valid persistent key. Setting the item to nil effectively removes the entry from the persistent storage. However, the data will be reclaimed (and finalized) during the next garbage collection loop. See @a wopi_pdata for further details. */ static void Wopi_setPData( Falcon::VMachine *vm ) { // Get name of the file. Falcon::Item *i_id = vm->param( 0 ); Falcon::Item *i_item = vm->param( 1 ); // parameter sanity check. if ( i_id == 0 || ! i_id->isString() || i_item == 0 ) { throw new Falcon::ParamError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ). extra( "S,X" ) ); } Wopi* self = dyncast( vm->self().asObject() )->wopi(); self->setPersistent( *i_id->asString(), *i_item ); } /*# @method sendTemplate Wopi @brief Configures template file and possibly sends it to the remote end. @param stream A Falcon stream opened for reading (or a memory string stream). @optparam tpd Data for template conversion. @optparam inMemory Work in memory and return the result instead sending it. @return The configured contents of the file if @b inMemory is true. @raise IoError on error reading the file. This function reads a text as-is (in binary mode) and flushes its contents to the remote side stream. If a dictionary is set as template conversion data, the data in the file is converted so that strings between a pair of '%' symbols are expanded in the text coresponding to the key in the dictionary. In example, if this is a template file: @code My name is %name%, pleased to meet you! @endcode The @b %name% configurable text may be changed into "John Smith" throught the following call: @code sendTemplate( InputStream("mytemplate.txt"), ["name" => "John Smith"] ) @endcode If a configurable text is not found in the @b tpd dictionary, it is removed. The specal sequence '%%' may be used to write a single '%'. @note Maximum lenght of template configurable strings is 64. */ static void Wopi_sendTemplate( Falcon::VMachine *vm ) { // Get name of the file. Falcon::Item *i_file = vm->param( 0 ); Falcon::Item *i_dict = vm->param( 1 ); Falcon::Item *i_inMemory = vm->param( 2 ); // parameter sanity check. if ( i_file == 0 || ! i_file->isObject() || !i_file->asObject()->derivedFrom( "Stream" ) || ( i_dict != 0 && ! ( i_dict->isDict() || i_dict->isNil() ) ) ) { throw new Falcon::ParamError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ). extra( "Stream,[D,X]" ) ); } bool bWorkInMem = i_inMemory == 0 ? false : i_inMemory->isTrue(); Falcon::Stream *outStream; if ( bWorkInMem ) { outStream = new Falcon::StringStream; } else { outStream = vm->stdOut(); } Falcon::CoreDict *dataDict = i_dict == 0 || i_dict->isNil() ? 0 : i_dict->asDict(); Falcon::Stream *inStream = (Falcon::Stream *)i_file->asObject()->getUserData(); Falcon::String text(1024+96); // spare a bit of space for extra templating. while ( ! inStream->eof() ) { if ( ! inStream->readString( text, 1024 ) ) { delete outStream; throw new Falcon::IoError( Falcon::ErrorParam( Falcon::e_io_error, __LINE__ ). sysError( (uint32) inStream->lastError() ) ); } // scan for templating if ( dataDict != 0 ) { Falcon::uint32 pos0=0, pos1, pos2; pos1 = text.find( "%" ); while ( pos1 != Falcon::String::npos ) { // verify the other position is within 64 chars pos2 = text.find( "%", pos1+1); // did we broke the limit of the read data? while ( pos2 == Falcon::String::npos && text.length()-pos1 < 64 ) { Falcon::uint32 c; if ( ! inStream->get( c ) ) { delete outStream; throw new Falcon::IoError( Falcon::ErrorParam( Falcon::e_io_error, __LINE__ ). sysError( (uint32) inStream->lastError() ) ); } text += c; if ( c == '%' ) pos2 = text.length(); } // ok; now, if we have found it, fine, else just drop it. if ( pos2 != Falcon::String::npos ) { // write the other part of the text outStream->writeString( text, pos0, pos1 ); if ( pos2 == pos1 + 1 ) outStream->writeString( "%" ); else { // find the key. Falcon::String key = text.subString( pos1+1, pos2 ); Falcon::Item *i_value = dataDict->find( &key ); // write something only if found if ( i_value != 0 ) { if ( i_value->isString() ) { outStream->writeString( *i_value->asString() ); } else { Falcon::String temp; vm->itemToString( temp, i_value ); outStream->writeString( temp ); } } } // search next variable pos0 = pos2 + 1; pos1 = text.find( "%", pos0 ); } else { // just write everything that's left. outStream->writeString( text, pos0 ); pos1 = pos2; // will exit loop } } // write the last part outStream->writeString( text, pos0 ); } // if not using the dictionary else { outStream->writeString( text ); } } if ( bWorkInMem ) { Falcon::CoreString *gs = new Falcon::CoreString; static_cast(outStream)->closeToString(*gs); vm->retval( gs ); delete outStream; } else { outStream->flush(); } } /*# @method parseQuery Wopi @brief Explodes a query string into a dictionary. @param qstring A string in query string format to be parsed. @return A dictionary containing the keys and values in the query string. */ FALCON_FUNC Wopi_parseQuery( Falcon::VMachine *vm ) { Falcon::Item *i_qstring = vm->param(0); Falcon::Item *i_output = vm->param(1); // return an empty string if nil was given as parameter. if ( i_qstring == 0 || ! i_qstring->isString() ) { throw new Falcon::ParamError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ). extra( "S" ) ); } LinearDict* dict = new LinearDict; Utils::parseQuery( *i_qstring->asString(), *dict ); vm->retval(new CoreDict(dict)); } /*# @method makeQuery Wopi @brief Implodes a dictionary of key/value pairs into a query string. @param dict A dictionary containing the data to be transformed into query. @return A valid query string. @note The items in the dictionary are not stringified, they are just turned into strings with the basic toString() method. Object.toString() overrides won't be respected. */ FALCON_FUNC Wopi_makeQuery( Falcon::VMachine *vm ) { Falcon::Item *i_dict = vm->param(0); // return an empty string if nil was given as parameter. if ( i_dict == 0 || ! i_dict->isDict() ) { throw new Falcon::ParamError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ). extra( "D" ) ); } CoreString* cs = new CoreString(); vm->retval( cs ); CoreDict* dict = i_dict->asDict(); ItemDict& idict = dict->items(); Iterator iter(&idict); bool bFilled = false; while( iter.hasCurrent() ) { if (bFilled ) { cs->append('&'); } const Item& key = iter.getCurrentKey(); const Item& value = iter.getCurrent(); String skey; key.toString( skey ); String svalue; value.toString( svalue ); cs->append( URI::URLEncode( skey ) ); cs->append( '=' ); cs->append( URI::URLEncode( svalue ) ); iter.next(); bFilled = true; } } /*# @function htmlEscape @brief Escapes a string converting HTML special characters. @param string The string to be converted. @optparam output A stream where to place the output. @return A converted string, or nil if writing to a string. This function converts special HTML characters to HTML sequences: - '<' is converted to '&lt;' - '>' is converted to '&gt;' - '&' is converted to '&amp;' - '"' is converted to '&quot;' If an @b output stream is specified, then the output is sent there instead being returned in a new string. If the output of this function must be sent directly to output, a true value can be passed to send data directly to the VM output stream (usually linked with web server data stream). This will spare memory and CPU. */ FALCON_FUNC htmlEscape( Falcon::VMachine *vm ) { Falcon::Item *i_str = vm->param(0); Falcon::Item *i_output = vm->param(1); // return an empty string if nil was given as parameter. if ( i_str != 0 && i_str->isNil() ) { vm->retval( new Falcon::CoreString ); return; } if ( i_str == 0 || ! i_str->isString() ) { throw new Falcon::ParamError( Falcon::ErrorParam( Falcon::e_inv_params, __LINE__ ). extra( "S,[Stream|X]" ) ); } if ( i_output != 0 ) { if ( i_output->isObject() && i_output->asObject()->derivedFrom( "Stream" ) ) internal_htmlEscape_stream( *i_str->asString(), (Falcon::Stream *) i_output->asObject()->getUserData() ); else internal_htmlEscape_stream( *i_str->asString(), i_output->isTrue() ? vm->stdOut() : vm->stdErr() ); return; } const Falcon::String& str = *i_str->asString(); Falcon::CoreString* encoded = new Falcon::CoreString( str.size() ); Falcon::uint32 len = str.length(); for ( Falcon::uint32 i = 0; i < len; i++ ) { Falcon::uint32 chr = str[i]; switch ( chr ) { case '<': encoded->append( "<" ); break; case '>': encoded->append( ">" ); break; case '"': encoded->append( """ ); break; case '&': encoded->append( "&" ); break; default: encoded->append( chr ); } } vm->retval( encoded ); } //============================================ // Module initialization //============================================ Falcon::Module * wopi_module_init( ObjectFactory rqf, ObjectFactory rpf, ext_func_t rq_init_func, ext_func_t rp_init_func ) { // initialize the module Falcon::Module *self = new Falcon::Module(); self->name( "WOPI" ); self->engineVersion( FALCON_VERSION_NUM ); self->version( VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION ); InitRequestClass( self, rqf, rq_init_func ); InitReplyClass( self, rpf, rp_init_func ); InitErrorClass( self ); InitUploadedClass( self ); // create a singleton instance of %Wopi class Falcon::Symbol *c_wopi_o = self->addSingleton( "Wopi", &Wopi_init ); Falcon::Symbol *c_wopi = c_wopi_o->getInstance(); c_wopi->getClassDef()->factory( CoreWopi::factory ); self->addClassMethod( c_wopi, "getAppData", &Wopi_getAppData ).asSymbol() ->addParam( "app" ); self->addClassMethod( c_wopi, "setAppData", &Wopi_setAppData ).asSymbol() ->addParam( "data" )->addParam( "app" ); self->addClassMethod( c_wopi, "getPData", &Wopi_getPData ).asSymbol() ->addParam( "id" )->addParam("func"); self->addClassMethod( c_wopi, "setPData", &Wopi_setPData ).asSymbol() ->addParam( "id" )->addParam( "func" ); self->addClassMethod( c_wopi, "sendTemplate", &Wopi_sendTemplate ).asSymbol() ->addParam( "stream" )->addParam( "tpd" )->addParam( "inMemory" ); self->addClassMethod( c_wopi, "parseQuery", &Wopi_parseQuery ).asSymbol() ->addParam( "qstring" ); self->addClassMethod( c_wopi, "makeQuery", &Wopi_makeQuery ).asSymbol() ->addParam( "dict" ); // Generic functions. self->addExtFunc( "htmlEscape", htmlEscape ) ->addParam( "string" )->addParam( "output" ); return self; } } } /* end of wopi_ext.cpp */ tests/000077500000000000000000000000001176363201700122265ustar00rootroot00000000000000tests/CMakeLists.txt000066400000000000000000000002671176363201700147730ustar00rootroot00000000000000add_subdirectory(core/testsuite) #ok testsuite should run, but should we install it? if( FALCON_INSTALL_TESTS ) install(DIRECTORY . DESTINATION ${Falcon_SHARE_DIR}/tests ) endif()tests/core/000077500000000000000000000000001176363201700131565ustar00rootroot00000000000000tests/core/benchmarks/000077500000000000000000000000001176363201700152735ustar00rootroot00000000000000tests/core/benchmarks/falcon/000077500000000000000000000000001176363201700165355ustar00rootroot00000000000000tests/core/benchmarks/falcon/readline.fal000066400000000000000000000021351176363201700210050ustar00rootroot00000000000000/* FALCON - Benchmarks FILE: readline.fal Read repeated lines. Measures the count of characters and lines read froma stream in text oriented operations. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Mon, 02 Feb 2009 19:58:29 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ // Config if args.len() == 0 > "Please, give a file from which to read a massive number of lines." return 1 end ins = IOStream( args[0] ) line = strBuffer( 100000 ) count = 0 chars = 0 time = seconds() while not ins.eof() ins.readLine( line ) ++count chars += line.len() end endtime = seconds() diff = endtime - time if diff == 0 > "Sorry, file too small to take timings." return 1 end > @ "Total elapsed time: $(diff:.3)" meanl = count / diff meanc = chars / diff > @"Total lines: $count ($chars characters)" > @"Means: $(meanl:.3) l/sec ($(meanc:.3) char/sec)" > "Done." return 0 tests/core/benchmarks/falcon/sieve.fal000066400000000000000000000024341176363201700203370ustar00rootroot00000000000000/* FALCON - Benchmarks FILE: sieve.fal Timed Sieve of Eratosthenes. Measures performance how many computational loops are performed in a given amount of time. See also "sieve_mt.fal" ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ // Config const SIZE = 8092 const BATCH = 100 const TIME = 30 flags = arrayBuffer( SIZE+1 ) //========================= // The sieve //========================= function sieve() global count flags.fill(0) for i in [1:SIZE+1] if flags[i]: continue prime = 2 * i + 1 start = prime + i for k in [start:SIZE+1:prime] flags[k] = 1 end end count += i end //========================= // Main code //========================= count = 0 cycles = 0 t = seconds() //while seconds() < t + TIME // Test for TIME (30) seconds for iter in [0:BATCH] sieve() end cycles += BATCH //end t = seconds() - t f = Format( ".3" ) > f.format( t ), " seconds." > count, " sieves." > f.format(count / t), " sieves per second" tests/core/benchmarks/falcon/sieve_mt.fal000066400000000000000000000037531176363201700210440ustar00rootroot00000000000000/* FALCON - Benchmarks FILE: sieve_mt.fal Multithreaded Sieve of Eratosthenes. Measures performance linearity in pure parallel calculation tasks by summing up the computational cycles performed by each thread in a given amount of time. Requires the threading module. Provide the number of desired threads as a parameter (defaults to 1). See also "sieve.fal" ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load threading // Config const SIZE = 5000 const BATCH = 20 const TIME = 30 //========================= // The sieve //========================= function sieve() count = 0 flags = arrayBuffer( SIZE ) for i in [0:SIZE] if flags[i]: continue prime = 2 * i + 1 start = prime + i for k in [start:SIZE:prime] flags[k] = true end count++ end return count end //========================= // The thread main routine //========================= function threaded() cycles = 0 t = seconds() while seconds() < t + TIME // test for TIME (30) seconds for iter in [0:BATCH] p = sieve() end cycles += BATCH end // return to join() the number of completed loops return cycles end //========================= // Main code //========================= // get the argument if args.len() tcount = int(args[0]) if tcount < 1: tcount = 1 else tcount = 1 end // creates nameless threads threads = arrayBuffer( tcount ) t = seconds() for i in [0:tcount] threads[i] = Threading.start( threaded ) end // wait for the threads to finish count = 0 for i in [0:tcount] count += threads[i].join() end t = seconds() - t // results f = Format( ".3" ) > f.format(count / t), " sieves per second" tests/core/longtests/000077500000000000000000000000001176363201700152005ustar00rootroot00000000000000tests/core/longtests/README000066400000000000000000000015411176363201700160610ustar00rootroot00000000000000This directory contains the memory/cpu intensive regression tests for this Falcon version. The scripts here are not stand-alone, and can't be normally called with "falcon", as they integrate with some utility provided by "faltest". A complete and working installation of falcon can be tested by entering in this directory and launching "faltest" from the command line. If the bottom line reports an error, then something is broken. The faltest manual in the "Command Line Tools" guide (available with "man faltest" on Linux and downloadable from http://falconpl.org) illustrates how to use faltest to have more accurate error reports. In case of this preliminary test failing, please send a complete report generated with "faltest -v" applied on the failing tests to the Committe through our "contacts" page at http://falconpl.org/index.ftd?page_id=contactstests/core/longtests/arrChange.fal000066400000000000000000000016041176363201700175570ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 5b * Category: array * Subcategory: * Short: Array change single item * Description: * Performance test on 1000000 Array acesses. * [/Description] ****************************************************************************/ loops = 5000000 * timeFactor() each = int(loops/10) array = ["a string", ["an", "array"], 10, 11, 12.5, [13:15], 18] range = [2:4] arrins = [ 20, 30, 40 ] // getting time time = seconds() for i in [ 0 : loops ] array[4] = "hello" if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/longtests/arrChangeRng.fal000066400000000000000000000016461176363201700202340ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 5d * Category: array * Subcategory: * Short: Array change range * Description: * Performance test on 1000000 Array change range (array resize). * [/Description] ****************************************************************************/ loops = 2000000 * timeFactor() each = int(loops/10) s = ["a string", ["an", "array"], 10, 11, 12.5, [13:15], 18] range = [2:4] arrins = [ 20, 30, 40 ] // getting time time = seconds() for i in [ 0 : loops ] array = s.clone() array[range] = arrins if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/longtests/arrSubstRng.fal000066400000000000000000000016311176363201700201410ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 5c * Category: array * Subcategory: * Short: Array change range * Description: * Performance test on 1000000 Array change range (no array resize). * [/Description] ****************************************************************************/ loops = 13000000 * timeFactor() each = int(loops/10) array = ["a string", ["an", "array"], 10, 11, 12.5, [13:15], 18] range = [2:5] arrins = [ 20, 30, 40 ] // getting time time = seconds() for i in [ 0 : loops ] array[range] = arrins if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/longtests/arrayAccess.fal000066400000000000000000000015461176363201700201320ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * * ID: 5a * Category: array * Subcategory: * Short: Array access * Description: * Performance test on 1000000 Array acesses. * [/Description] ****************************************************************************/ loops = 4000000 * timeFactor() each = int(loops/10) array = ["a string", ["an", "array"], 10, 11, 12.5, [13:15], 18] range = [2:4] // getting time time = seconds() for i in [ 0 : loops ] result = array[range] if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/longtests/dictFind.fal000066400000000000000000000023511176363201700174110ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 6e * Category: dict * Subcategory: * Short: Find in normal dictionary * Description: * Performance test on repeated finds in small paged dictionaries. * [/Description] ****************************************************************************/ loops = 100000 * timeFactor() each = int(loops/10) // creating a default key function makeKey() static letter_a = ord('A') key = " " end for num in [ 0 : key.len() ] key[num] = random( 0, 25 ) + letter_a end return key end // creating the base dictionary dict = [=>] for i in [ 0 : 100 ] dict[ makeKey() ] = random() end // getting time time = seconds() // looping for insertion for i in [ 0 : loops ] // repeating the findings v = makeKey() in dict if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] makeKey() if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive // we have produced 20 operations per loop timings( time, loops ) /* end of file */ tests/core/longtests/dictPageFind.fal000066400000000000000000000023561176363201700202130ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 6f * Category: dict * Subcategory: * Short: Find in paged dictionary * Description: * Performance test on repeated finds in small paged dictionaries. * [/Description] ****************************************************************************/ loops = 100000 * timeFactor() each = int(loops/10) // creating a default key function makeKey() static letter_a = ord('A') key = " " end for num in [ 0 : key.len() ] key[num] = random( 0, 25 ) + letter_a end return key end // creating the base dictionary dict = PageDict() for i in [ 0 : 100 ] dict[ makeKey() ] = random() end // getting time time = seconds() // looping for insertion for i in [ 0 : loops ] // repeating the findings v = makeKey() in dict if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] makeKey() if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive // we have produced 20 operations per loop timings( time, loops ) /* end of file */ tests/core/longtests/func_sieve.fal000066400000000000000000000024761176363201700200230ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 7b * Category: functional * Subcategory: * Short: Benchmark on repeated functional calls * Description: * Performing a CPU killing funcional sieve of erathostenes with forced * regeneration of the functional sequence to stress the garbage * collector. Don't take this as a good example of fast code, it is meant * to disrupt the GC as much as possible. * * The performance of this script can be checked against 7a to test for * fast creation and setup of the functional loop. * [/Description] ****************************************************************************/ const NUM = 100 loops = NUM * timeFactor() each = int(loops/10) // getting time time = seconds() function main(limit) global count flags = MemBuf(8193,4) count = 0 try for num in [1:limit+1] for i in [1: 8193]: flags[i] = num for i in [2:8193] if flags[i] .[times ,[i+i:8193:i] .[ .[ function( x,y ); x[y] = 0; end flags &1 ] .[ { x => ++x } $count ] ] ]() end end alive( num / limit*100 ) end catch inspect( flags ) failure( "ops" ) end end main(loops) time = seconds() - time timings( time, count ) tests/core/longtests/func_sieve_fast.fal000066400000000000000000000022141176363201700210260ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 7a * Category: functional * Subcategory: * Short: Benchmark on repeated functional calls * Description: * Performing a CPU killing funcional sieve of erathostenes reusing the * same functional sequence again and again. Notice that with correct * optimization, this test sould run as 7b. * [/Description] ****************************************************************************/ const NUM = 100 loops = NUM * timeFactor() each = int(loops/10) // getting time time = seconds() function main(limit) global count flags = MemBuf(8193,4) count = 0 the_param = nil the_loop = .[times $the_param .[ .[ function( x,y ); x[y] = 0; end flags &1 ] .[ { x => ++x } $count ] ] ] for num in [1:limit+1] for i in [1: 8193]: flags[i] = num for i in [2:8193] if flags[i] the_param = [ i+i: 8193: i ] the_loop() end end alive( num / limit*100 ) end end main(loops) time = seconds() - time timings( time, count ) tests/core/longtests/funccall.fal000066400000000000000000000016111176363201700174520ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 2a * Category: calls * Subcategory: * Short: Benchmark on function calls * Description: * Performing repeated function calls and returns. * This test calls a function without parameters. * * [/Description] ****************************************************************************/ loops = 50000000 * timeFactor() each = int(loops/10) function toBeCalled() end // getting time time = seconds() for i in [ 0 : loops ] // perform the call toBeCalled() if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/longtests/funccallpar.fal000066400000000000000000000017521176363201700201630ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 2b * Category: calls * Subcategory: * Short: Benchmark on function calls with params * Description: * Performing repeated function calls and returns. * This test calls a function without parameters. * * Here we add 5 parameters. * [/Description] ****************************************************************************/ loops = 12000000 * timeFactor() each = int(loops/10) function toBeCalled( first, second, third, fourth, fifth ) end // getting time time = seconds() for i in [ 0 : loops ] // perform the call toBeCalled( i, 1, "A", 2, 3 ) if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/longtests/funcdeep.fal000066400000000000000000000020151176363201700174530ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 2g * Category: calls * Subcategory: deep * Short: Benchmark on deep calls (100) * Description: * Performing repeated function calls and returns, lengthening the * vm stack. This test performs 100 deep calls. * [/Description] ****************************************************************************/ const depth = 100 loops = (13000000/depth) * timeFactor() each = int(loops/10) function deepCall( counter, a, b, c, d ) if counter < depth deepCall( counter + 1 ) end end // getting time time = seconds() for i in [ 0 : loops ] // perform the call deepCall( 0 ) if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops * depth ) /* end of file */ tests/core/longtests/funcdeep2.fal000066400000000000000000000020201176363201700175310ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 2h * Category: calls * Subcategory: deep * Short: Benchmark on deep calls (1000) * Description: * Performing repeated function calls and returns, lengthening the * vm stack. This test performs 1000 deep calls. * [/Description] ****************************************************************************/ const depth = 1000 loops = (13000000/depth) * timeFactor() each = int(loops/10) function deepCall( counter, a, b, c, d ) if counter < depth deepCall( counter + 1 ) end end // getting time time = seconds() for i in [ 0 : loops ] // perform the call deepCall( 0 ) if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops * depth ) /* end of file */ tests/core/longtests/funcdeep3.fal000066400000000000000000000020171176363201700175400ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 2j * Category: calls * Subcategory: deep * Short: Benchmark on deep calls (10000) * Description: * Performing repeated function calls and returns, lengthening the * vm stack. This test performs 100 deep calls. * [/Description] ****************************************************************************/ const depth = 10000 loops = (13000000/depth) * timeFactor() each = int(loops/10) function deepCall( counter, a, b, c, d ) if counter < depth deepCall( counter + 1 ) end end // getting time time = seconds() for i in [ 0 : loops ] // perform the call deepCall( 0 ) if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [0 : loops] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops * depth ) /* end of file */ tests/core/longtests/gc.fal000066400000000000000000000030121176363201700162510ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite - benchmarks / lengthy tests * * ID: 100 * Category: GC * Subcategory: * Short: Basic garbage collecting. * Description: * This test performs one million loops in which an array filled with strings * and created-on-the-fly data. The array is repetedly dropped, so the GC * collector must destroy it, or the memory of the machine running this test * will soon crash. * * The test takes approx 10 seconds to complete on a 2.4GHrz machine. * [/Description] * ****************************************************************************/ loops = 500000 * timeFactor() each = int(loops/60) function nonStatic() return "a nonstatic" end // getting time time = seconds() static_string = "Static string" for i in [ 0 : loops ] var = [ 1, 2, strBuffer(1000), "This is " + nonStatic() +" string", 5 ] var1 = [ i, var ] var2 = [ i, var1 ] if i % each == 0 alive( i/loops*100 ) GC.perform(true) end var2 = 0 end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive // have we lost a static string? if static_string != "Static" + " " + "string" failure( "GC killed a static string." ) end if var[3] != "This is a nonstatic string" failure( "GC killed the content of the array." ) end timings( time, loops ) success() /* end of file */ tests/core/longtests/massDict.fal000066400000000000000000000021411176363201700174310ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 6a * Category: dict * Subcategory: * Short: Massive dictionary * Description: * Performance test on 100000 dictionary insertions. * [/Description] ****************************************************************************/ loops = 50000 * timeFactor() each = int(loops/10) dict = [ => ] // getting time time = seconds() // creating a default key function makeKey() static letter_a = ord('A') key = " " end for num in [ 0 : key.len() ] key[num] = random( 0, 25 ) + letter_a end return key end // looping for insertion for i in [ 0 : loops ] key = makeKey() dict[ key ] = random() if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] makeKey( key ); random() if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) success() /* end of file */ tests/core/longtests/massPageDict.fal000066400000000000000000000021571176363201700202350ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 6b * Category: dict * Subcategory: * Short: Massive page dictionary * Description: * Performance test on 100000 paged dictionary insertions. * [/Description] ****************************************************************************/ loops = 50000 * timeFactor() each = int(loops/10) dict = PageDict() // getting time time = seconds() // creating a default key function makeKey() static letter_a = ord('A') key = " " end for num in [ 0 : key.len() ] key[num] = random( 0, 25 ) + letter_a end return key end // looping for insertion for i in [ 0 : loops ] key = makeKey() dict[ key ] = random() if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [0 : loops ] makeKey( key ); random() if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) success() /* end of file */ tests/core/longtests/methodCall.fal000066400000000000000000000022021176363201700177340ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * * ID: 2c * Category: calls * Subcategory: methods * Short: Benchmark on method calls * Description: * Performing repeated method calls and returns. * This test calls a function without parameters. * * [/Description] ****************************************************************************/ loops = 7000000 * timeFactor() each = int(loops/10) object item prop1 = nil prop2 = nil prop3 = nil prop4 = nil prop5 = nil prop6 = nil function toBeCalled() end function not1() return 0 end function not2() return 0 end function not3() return 0 end end // getting time time = seconds() for i in [ 0 : loops ] // perform the call item.toBeCalled() if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/longtests/methodCallPar.fal000066400000000000000000000022611176363201700204040ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 2d * Category: calls * Subcategory: * Short: Benchmark on method calls with params * Description: * Performing repeated method calls and returns. * This test calls uses 5 parameters. * * [/Description] ****************************************************************************/ loops = 5000000 * timeFactor() each = int(loops/10) object item prop1 = nil prop2 = nil prop3 = nil prop4 = nil prop5 = nil prop6 = nil function toBeCalled( param1, param2, param3, param4, param5 ) end function not1() return 0 end function not2() return 0 end function not3() return 0 end end // getting time time = seconds() for i in [ 0 : loops ] // perform the call item.toBeCalled( i, 1, "A", 2, 3 ) if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/longtests/methodCallSmall.fal000066400000000000000000000016601176363201700207340ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 2e * Category: calls * Subcategory: * Short: Benchmark on mimimal method calls * Description: * Performing repeated method calls and returns. * The item in which the method is called is very small. * * [/Description] ****************************************************************************/ loops = 10000000 * timeFactor() each = int(loops/10) object item function toBeCalled() end end // getting time time = seconds() for i in [ 0 : loops ] // perform the call item.toBeCalled() if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/longtests/methodCallSmallPar.fal000066400000000000000000000020341176363201700213730ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 2f * Category: calls * Subcategory: * Short: Benchmark on mimimal method calls with params * Description: * Performing repeated method calls and returns. * The item in which the method is called is very small. * This test calls uses 5 parameters. * * [/Description] ****************************************************************************/ loops = 7000000 * timeFactor() each = int(loops/10) object item function toBeCalled( param1, param2, param3, param4, param5 ) end end // getting time time = seconds() for i in [ 0 : loops ] // perform the call item.toBeCalled( i, 1, "A", 2, 3 ) if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/longtests/smallDict.fal000066400000000000000000000024671176363201700176110ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 6c * Category: dict * Subcategory: * Short: Small dictionary * Description: * Performance test on repeated insertions in small dictionaries. * [/Description] ****************************************************************************/ loops = 10000 * timeFactor() each = int(loops/10) function testInsert() dict = [ => ] for i in [ 0 : 20 ] dict[ makeKey() ] = random() end end function testEmpty() dict = [ => ] for i in [ 0 : 20 ] makeKey(); random() end end // creating a default key function makeKey() static letter_a = ord('A') key = " " end for num in [ 0 : key.len() ] key[num] = random( 0, 25 ) + letter_a end return key end // getting time time = seconds() // looping for insertion for i in [ 0 : loops ] testInsert() if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] testEmpty() if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive // we have produced 100 operations per loop timings( time, loops * 20 ) success() /* end of file */ tests/core/longtests/smallPage.fal000066400000000000000000000025121176363201700175710ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 6d * Category: dict * Subcategory: * Short: Small paged dictionary * Description: * Performance test on repeated insertions in small paged dictionaries. * [/Description] ****************************************************************************/ loops = 10000 * timeFactor() each = int(loops/10) function testInsert() dict = PageDict() for i in [ 0 : 20 ] dict[ makeKey() ] = random() end end function testEmpty() dict = PageDict() for i in [ 0 : 20 ] makeKey(); random() end end // creating a default key function makeKey() static letter_a = ord('A') key = " " end for num in [ 0 : key.len() ] key[num] = random( 0, 25 ) + letter_a end return key end // getting time time = seconds() // looping for insertion for i in [ 0 : loops ] testInsert() if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] testEmpty() if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive // we have produced 20 operations per loop timings( time, loops * 20 ) success() /* end of file */ tests/core/longtests/strAccess.fal000066400000000000000000000015211176363201700176150ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 3a * Category: strings * Subcategory: * Short: String access * Description: * Performance test on 10000000 string accesses. * [/Description] ****************************************************************************/ loops = 2000000 * timeFactor() each = int(loops/10) str = "the string that will be accessed" range = [5:10] // getting time time = seconds() for i in [ 0 : loops ] result = str[range] if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/longtests/strAssign.fal000066400000000000000000000015511176363201700176430ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 3e * Category: strings * Subcategory: * Short: String assign * Description: * Performance test on 10000000 string assignments. * [/Description] ****************************************************************************/ loops = 10000000 * timeFactor() each = int(loops/10) str = "the string that will be accessed" str7= "abc" // we'll change three chars // getting time time = seconds() for i in [ 0 : loops ] str[5] = str7 if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/longtests/strAssignRng.fal000066400000000000000000000015751176363201700203200ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 3d * Category: strings * Subcategory: * Short: String range assign * Description: * Performance test on 100000 string range assignments. * [/Description] ****************************************************************************/ loops = 2500000 * timeFactor() each = int(loops/10) str7= "abc" // we'll change three chars // getting time r = [5:7] time = seconds() for i in [ 0 : loops ] str = "the string that will be accessed" str[r] = str7 if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/longtests/strBinAccess.fal000066400000000000000000000015101176363201700202440ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 3c * Category: strings * Subcategory: * Short: String Binary access * Description: * Performance test on 10000000 string accesses. * [/Description] ****************************************************************************/ loops = 10000000 * timeFactor() each = int(loops/10) str = "the string that will be accessed" // getting time time = seconds() for i in [ 0 : loops ] result = str[ *5] if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/longtests/strCat.fal000066400000000000000000000015041176363201700171240ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 3b * Category: strings * Subcategory: * Short: String concatenation * Description: * Performance test on 10000000 string concatenations. * [/Description] ****************************************************************************/ loops = 2000000 * timeFactor() each = int(loops/10) part = "second" str = "first " // getting time time = seconds() for i in [ 0 : loops ] result = str + part if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/longtests/vmloop.fal000066400000000000000000000020261176363201700172000ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 1a * Category: vm * Subcategory: * Short: Benchmark on pure VM loop * Description: * Performing a repeated set of mean operations in a tight loop * Mean VM loop time will be determined by dividing the total time by the * total count of VM opcodes. * * [/Description] ****************************************************************************/ GC.enable(false) const opcodes = 8 // the if is executed half of the times loops = 15000000 * timeFactor() each = int(loops/10) // getting time time = seconds() for i in [ 0 : loops ] // setting a variable loopVar1 = i // multiplying a variable loopVar2 = loopVar1 * i // doing a check if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time timeAlive = seconds() for i in [ 0 : loops /each ] alive( i ) end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, opcodes * loops ) /* end of file */ tests/core/longtests/vmloopref.fal000066400000000000000000000020751176363201700177010ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 1b * Category: vm * Subcategory: * Short: Benchmark on pure VM loop - with ref * Description: * Performing a repeated set of mean operations in a tight loop * Mean VM loop time will be determined by dividing the total time by the * total count of VM opcodes. * * This test uses reference variables. * * [/Description] ****************************************************************************/ const opcodes = 8 // the if is executed half of the times loops = 5500000 * timeFactor() each = int(loops/10) // getting time time = seconds() for i in [ 0 : loops ] // setting a variable loopVar1 = $i // multiplying a variable loopVar2 = loopVar1 * i // doing a check if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time timeAlive = seconds() for i in [ 0 : loops/each ] alive( i ) end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, opcodes * loops ) /* end of file */ tests/core/longtests/wStrAssign.fal000066400000000000000000000015561176363201700177770ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 4a * Category: strings * Subcategory: * Short: Wide String assign * Description: * Performance test on 10000000 string assignments. * [/Description] ****************************************************************************/ loops = 10000000 * timeFactor() each = int(loops/10) str = "the string that will be accessed" str7= "abc" // we'll change three chars // getting time time = seconds() for i in [ 0 : loops ] str[5] = str7 if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/longtests/wStrAssignRng.fal000066400000000000000000000016101176363201700204350ustar00rootroot00000000000000/**************************************************************************** * Falcon direct benchmarks * * ID: 4b * Category: strings * Subcategory: * Short: Wide String range assign * Description: * Performance test on 100000 string range assignments. * [/Description] ****************************************************************************/ loops = 2500000 * timeFactor() each = int(loops/10) str7= "abc" // we'll change three chars // getting time r = [5:7] time = seconds() for i in [ 0 : loops ] str = "\x23ffthe string that will be accessed" str[r] = str7 if i % each == 0 alive( i/loops*100 ) end end // taking end time time = seconds() - time // subtract alive time timeAlive = seconds() for i in [ 0 : loops ] if i % each == 0 alive( i/loops*100 ) end end timeAlive = seconds() - timeAlive time -= timeAlive timings( time, loops ) /* end of file */ tests/core/samples/000077500000000000000000000000001176363201700146225ustar00rootroot00000000000000tests/core/samples/args.fal000066400000000000000000000015551176363201700162500ustar00rootroot00000000000000#!falcon /* FALCON - Samples FILE: args.fal How to access script args Provide several command line paramters and see the results. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ printl( "The Falcon Programming Language - samples. " ) printl( " Arguments Sample. This script will list all the arguments that it has received." ) printl() print( "This program has been called with ", len( args ), " argument" ) if len( args != 1 ) printl( "s" ) end printl() for arg in args printl( "Argument: ", arg ) end if len( args ) != 0: printl() printl( "Done." ) /* end */ tests/core/samples/argsParse.fal000066400000000000000000000065101176363201700172370ustar00rootroot00000000000000#!falcon /* FALCON - Samples FILE: argsParer.fal Demonstration of the CmdlineParser facility Launch with the parameter -? ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ object MyParser from CmdlineParser freeParams = [] opt_d = nil opt_l = nil opt_m = nil sw_k = 0 sw_n = 0 used = nil function onOption( option ) self.used = true switch option case "?", "help" self.usage() case "d", "m", "l", "long" self.expectValue() case "k" self.sw_k = 1 case "n" self.sw_n = 1 case "v", "version" printl( "argsParse.fal - version 1.0" ); case "z", "sleep" self.terminate() default self.unrecognized( option ) end end function onValue( option, value ) switch option case "d" self.opt_d = value case "m" self.opt_m = value case "l", "long" self.opt_l = value end // can't be anything else, as this function call must // be authorized from onOption end function onFree( param ) self.used = true self.freeParams += param end function onSwitchOff( sw ) self.used = true switch sw case "k" self.sw_k = 0 case "n" self.sw_n = 0 default self.unrecognized( sw ) end end function unrecognized( option ) printl( "Unrecognized option \"", option, "\"\n" ); self.usage() exit(0) end function usage() if not self.used printl( "Provide some parameters to test this script.\n" ) end printl(' -?,--help Prints this help -d Sets the D option -m Sets the M option -l Sets the L option --long Long version of -l -k K switch on -n N switch on -z,--sleep Ignore the rest of the command Single char option can be "chained" i.e. -knd . When chaining more than one option requiring a parameter, only the last one will actually be considered. Switches can be turend off by appendig a "-" after them. Words eneterd without being prepended by ''-'', or a ''-'' by itself, are considered "free options", and will be listed separately. A "--" will be interpreted as end of parse marker, and everything given after that mark will be passed as free options.' ) end function dump() if self.opt_d: printl( "D option: ", self.opt_d ) if self.opt_l: printl( "L option: ", self.opt_l ) if self.opt_m: printl( "M option: ", self.opt_m ) printl( "K switch ", self.sw_k ? "ON" : "OFF" ) printl( "N switch ", self.sw_n ? "ON" : "OFF" ) for elem in self.freeParams forfirst: >> "Free options: " >> elem formiddle: >> ", " forlast: > "." end end end if not MyParser.parse() or not MyParser.used MyParser.usage() exit(0) end printl( "Option status: " ) MyParser.dump() /* end */ tests/core/samples/attributes.fal000066400000000000000000000021711176363201700174750ustar00rootroot00000000000000/* Falcon Samples. Show how attributes can make a program nicer. */ name: "The Falcon Programming Language - Properties." author: "Giancarlo Niccolai." purpose: "Show off attribute powers." function test() purpose: "Provide something to test" > "Hello from test" end class MyTestClass purpose: "A class with some private attrib" persistent: "alpha" alpha = 0 beta = 1 function aMethod() author: "Giancarlo Niccolai" purpose: "Show attributes also in methods." return "Hello from method" end end > ' Test showing symbol attributes (falcon equivalent for annotation) We will now show how to retreive compile-time attributes associated with symbols. ' > "Attributes of this program: " for key, value in attributes(): > " ", key, ": ", value > > "Attributes of function test: " for key, value in test.attributes(): > " ", key, ": ", value > > "Attributes of class MyTestClass: " for key, value in MyTestClass.attributes(): > " ", key, ": ", value > > "Attributes of method aMethod: " for key, value in MyTestClass.aMethod.attributes(): > " ", key, ": ", value > > "Done." tests/core/samples/bcastAnonym.fal000066400000000000000000000046051176363201700175710ustar00rootroot00000000000000/* FALCON - Samples FILE: bcastAnonym.fal Shows the usage of anonymous slots and "event" broadcasting. Events are special messages which can be sent on a slot regardless of their name. Specific handlers can be registered with them through the "register" method, while generic marshalling is still possible via standard on_ object callback automatic marshalling. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ //================================================ // DataBay // class DataBay _data = [] function on_SetData( data ) self._data += data return true end function on_GetData() consume() return self._data end // generic _on_event catching everything function __on_event( event ) printl( @"Received an unhandled event of type \"$event\": ", passvp().describe() ) end end // generic callback function setDataMarker( data ) > "Received: ", data end //================================================== > "=================== ***** ========================" > "Falcon event marshalling test" > "=================== ***** ========================" > > "This test verifies the event semantics for message marshalling" > "The arguments you type on the command line will be repeated through an event;" > "Then, another event will ask a storage item class to retreive the data." if args.len() == 0 > >"No arguments provided; please, write something on the command line." return 1 end db = DataBay() // register to an anonymous slot anonym_slot = VMSlot() anonym_slot.subscribe( db ) // also, adds a specific event listener anonym_slot.register( "SetData", setDataMarker ) // Send a specific event for data in args: anonym_slot.send( "SetData", data ) // transmit by name >> "Arguments replied: " stored = anonym_slot.send( "GetData" ) for string in stored >> string formiddle: >>", " forlast: >"." end // transmit an arbitrary event for test (.[ anonym_slot.send "ArbitraryEvent" ] + stored )() // verify that a broadcast on an anonymous slot doesn't excite marashalling anonym_slot.broadcast() > > "Complete." tests/core/samples/bcastCB.fal000066400000000000000000000045441176363201700166160ustar00rootroot00000000000000/* FALCON - Samples FILE: bcastCB.fal Display how to fill delayed event execution lists. See the instructions in the print statements. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ //====================================== // Generic listener //================================================== // Our command subscriber // id: ordinal number // cmd: command to activate us. class CmdManager( id, cmd ) id = id cmd = cmd init subscribe( "declare", self ) end function activation( extra ) >> "Manager ", self.id, " activated on '", self.cmd, "'" if extra != nil >> " (on param ", extra, ")" end > end // commands is the required commands. // sublist is the subscription list. // extra is just an extra parameter that we'll play back in case of activation. function on_declare( commands, sublist ) // should we react? if self.cmd in commands sublist.push( [self.activation, arrayFind( commands, self.cmd )] ) end end end //================================================== commands = [ "red", "blue", "green", "white", "black", "orange" ] > "=================== ***** ========================" > "Falcon delayed event reply test" > "=================== ***** ========================" > > "This test shows how lists of delayed activies can be built through " > "message programming." > "Write some \"commands\" on the command line." > "They can be one or more of the following:" for c in commands: >> c, " " > > "Those command will summon some repliers, that will activate a delayed" > "routine when a \"subscription phase\" is complete." if args.len() == 0 > >"No arguments provided; please, write some 'command' on the command line." return 1 end id = 0 // warning; in real life you should store them somewhere. for c in commands CmdManager( id++, c ) end //ok, we can transmit sublist = List() broadcast( "declare", args, sublist ) > "Activations: " > "=============" for delayedActivation in sublist delayedActivation() end >"=================================" >"Done." tests/core/samples/bcastRet.fal000066400000000000000000000030571176363201700170620ustar00rootroot00000000000000/* FALCON - Samples FILE: bcastRet.fal Test for event marshalling and data returning. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ //================================================ // DataBay // class DataBay _data = [] function on_SetData( data ) self._data += data return true end function on_GetData( data ) consume() return self._data end end //================================================== > "=================== ***** ========================" > "Falcon event marshalling test" > "=================== ***** ========================" > > "This test verifies the event semantics for message marshalling" > "The arguments you type on the command line will be repeated through an event;" > "Then, another event will ask a storage item class to retreive the data." if args.len() == 0 > >"No arguments provided; please, write something on the command line." return 1 end db = DataBay() // register by name subscribe( "GetData", db ) // register by item slot = getSlot( "SetData" ) slot.subscribe( db ) // transmit by item for data in args: slot.broadcast( data ) // transmit by name >> "Arguments replied: " for string in broadcast( "GetData" ) >> string formiddle: >>", " forlast: >"." end > > "Complete." tests/core/samples/callbacks.fal000066400000000000000000000047231176363201700172330ustar00rootroot00000000000000/* FALCON - Samples FILE: callbacks.fal Show how to create event-driven callabacks. The main loop accepts a sequence of words; the first word is interpreted as an event which is broadcasted through the "scope" attribute. Three listener are instantiated, and they handle scope with through marshall callback facility. Incoming messages are then sent to the on[Message] handler. This classes interpret the test, pass and remove messages. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 25 Dec 2007 13:36:39 +0100 ------------------------------------------------------------------- (C) Copyright 2007: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ class Manager( name ) name = name // let's subscribe at creation init subscribe( "scope", self ) end function on_scope( msg ) try self.getProperty( "on_" + msg[0] )(msg[1]) catch AccessError > "Malformed command" end end // Just say the parameter and block processing function on_test( p1 ) > self.name, ": onTest - ", p1 end // Just say the parameter but allow broadcast over scope function on_pass( p1 ) > self.name, ": onPass - ", p1 end // say the parameter, block broadcast but remove ourself function on_remove( p1 ) > self.name, ": onRemove - ", p1 unsubscribe( "scope", self ) consume() end end //===================================================== // Main script // managers = map( Manager, [ "one", "two", "three" ] ) > "Event handlers demonstration." > > "Input an event followed by an optional parameter." > "The event is broadcasted via the \"scope\" attribute to three objects." > "The marshallCB function translates the first word into a callback" > "in the objects. Known handled events are:" > " test x - calls the onTest method on the first available handler" > " pass x - calls the onPass method on all the handlers" > " remove x - calls the onRemove method on the first handler, " > " which removes himself and won't be called again." > > " Write an empty linie to terminate." // accepts the first line >> "> "; line = input() while line // splits the line in words msg = strSplit( line, " " ) // boradcasts the message through the 'scope' parameter broadcast( "scope", msg ) >> "> "; line = input() end /* end of file */ tests/core/samples/closeStreams.fal000066400000000000000000000041161176363201700177540ustar00rootroot00000000000000/* Falcon Samples. Control process streams from script. This script demonstrates the usage of the basic directory API. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 25 Dec 2007 13:36:39 +0100 ------------------------------------------------------------------- (C) Copyright 2007: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ > "Control process streams from script." > > " This test Shows how to correctly get a standard stream and close it." > "This is necessary when scripts are meant to work with other piped processes." > "So, you may try to pipe this script's output through a program, or" > "redirect it to a file; the output will be closed, making the piped program" > "to terminate, then a small sleep will be issued to show that this script" > "is still running..." > > " In example, call with:" > > " $ falcon closeStreams.fal | grep -c \"issued\"" > > " This will cause grep to print \"2\" after the close line indicated below." > // Let's use stdErr to show parallel errStream = stdErr() //Let's get the standard output stream = stdOut() stream.writeText( "This text is sent through standard output proxy.\n" ) stream.close() > "Closing the standard output proxy won't close the process stream." // sleep to show we're still inside the pipe. errStream.writeText( "I just closed a standard stream proxy; piped apps should be still alive.\n" ) for i in [3 : 0] sleep( 1 ) errStream.writeText( @ "Sleeping for $i seconds more...\n" ) end //Now let's close it seriously // Use \r\n because output is not translated anymore. stream = stdOutRaw() stream.writeText( "Sent through standard output physical stream.\r\n" ) // piped programs will stop now. errStream.writeText( "Now I close the stream; piped apps shoudld exit now:\n"+ "====================================================\n" ) stream.close() for i in [3 : 0] sleep( 1 ) errStream.writeText( @ "Sleeping for $i seconds more...\n" ) end errStream.writeText( "Done.\n" ) tests/core/samples/continuation.fal000066400000000000000000000026011176363201700200170ustar00rootroot00000000000000/* Falcon Samples. Control process streams from script. Shows working principles of continuations. Call passing a first item that is iteratively searched througout the other items passed on the command line. Communication goes back and forth on both sides of the continuation: The slave function communicates the position where the item is found, and the master caller communicates how many items have been found up to date. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 22 Sep 2010 16:04:48 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ if args.len() < 2 > "Usage: continuation.fal item_to_find item1 item2 ... itemN" return 1 end iterFind = Continuation( { c, elem, array => for n in [0: array.len()] if array[n] == elem found(c,n) end end }) pos = iterFind(args[0], args[1:] ) count = 0 while pos >= 0 > "Found a '", args[0], "' at pos ", pos pos = iterFind(++count) end > "Complete" return 0 //======================================= function found( c, value ) foundUpToDate = c(value) >> "Found ", foundUpToDate, " element" if foundUpToDate > 1 >> "s" end > "." end tests/core/samples/corosub.fal000066400000000000000000000022411176363201700167610ustar00rootroot00000000000000/* Falcon Samples. Coroutine and message subscription. Normally, 'broadcast' function works in the context of the caller. */ const number = 5 function handler( id, data ) > @ "Handler $id called with $data" end function coro( id, sem ) subscribe( "msg", .[handler id ] ) wtime = random( 500 )/100 // 0...5 seconds > @ "Coroutine $id started, sleeping $(wtime:.3) second" sleep( wtime ) > @ "Coroutine $id terminated" sem.post() end function sender() secs = random( 500 )/100 // 0...5 seconds > @ "Sender started, sleeping $(secs:.3) second" sleep( secs ) broadcast( "msg", "Hello world" ) end function waiter( wsem ) wtime = seconds() while not wsem.wait(0.1) wsec = seconds() - wtime >> @"Counting: $(wsec:.2)\r" end > > "Counter done" end //===================================== counter = 0 semaphores = [] for i = 1 to number sem = Semaphore() launch coro( i, sem ) semaphores += sem end launch sender() complete = Semaphore() launch waiter( complete ) // wait all the coroutines for sem in semaphores sem.wait() end > "All the handlers terminated" complete.post() > "Main done." tests/core/samples/coroutine.fal000066400000000000000000000043331176363201700173200ustar00rootroot00000000000000/* Falcon Samples. Coroutine / light parallelism test This script creates eight coroutines from objects (actually from methods, as Falcon accepts only function calls as semantically correct coroutines), and one control coroutine that shows the status of the other coroutines. Both the main coroutine and the updater are notified of relevant events via two different semaphores. The script uses willfully a combination of global variables, parameter passing and object access to test and show the different approaches. */ //================================================== // Global variables ended = 0 //================================================= // Updater thread // function updater( waiter, coroutineArray ) while true // print the number of ended arrays print( "\rE:", ended ) // print data on all the coroutines for elem in coroutineArray print( @" c$(elem.id)=$(elem.value:3)" ) end print( "\r" ) //wait for new updates if ended < 8 waiter.wait() else // a break would do too here. yieldOut() end end end //=============================================== // coroutine class // class coroutine( param, su, se ) id = param sem_updates = su sem_ended = se value = 0 // ======================= // Thread method // (could have any name) function run( param ) for i in [1 : 101] self.value = i sleep( 0.01 * self.id ) self.sem_updates.post() end // access rw the global variable global ended ended ++ // warn both listening threads self.sem_updates.post() self.sem_ended.post() end end //======================================================== // Main program // our semaphores end_semaphore = Semaphore() upd_semaphore = Semaphore() // we save the coroutine objects here coro_array = [] for i in [0 : 8] coro = coroutine( i, upd_semaphore, end_semaphore ) coro_array += coro launch coro.run( i ) end // Functions can be started normally launch updater( upd_semaphore, coro_array ) // wait for all the threads to end while ended < 8 end_semaphore.wait() end printl() printl( "Done" ) tests/core/samples/corowait.fal000066400000000000000000000040041176363201700171330ustar00rootroot00000000000000/* Falcon Samples. Coroutine / wait timeout This sample shows how to wait for a semaphore to be signaled with a given timeout. The waiter function waits either to be waken up or 1 second. The main loop signals the semaphore every 0.5, 0.6, 0.5 and so on secods, up to 2.0 seconds. The first times, the waiter is always woken up, but when the signal interval corsses one second, it begins to signal both timeouts and wakeups. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 25 Dec 2007 13:36:39 +0100 ------------------------------------------------------------------- (C) Copyright 2007: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ //================================================== // Global variables //=============================================== // coroutine function // function waiter( sem, done ) initial = seconds() loop val = sem.wait( 1.0 ) if done == -1: break alive = seconds() - initial initial = seconds() if val >> @"\rAwaken! After:$(alive:4.2)s Done:", done else >> @"\rTIMEOUT! After:$(alive:4.2)s Done:", done end end > end //======================================================== // Main program > \ " This sample shows how to wait for a semaphore to be signaled with a given timeout. The waiter function waits either to be waken up or 1 second. The main loop signals the semaphore every 0.5, 0.6, 0.7 secods and so on, up to 2.0 seconds. The first times, the waiter is always woken up, but when the signal interval corsses one second, it begins to signal both timeouts and wakeups. " // our semaphores sem = Semaphore() // wait for all the threads to end done = 0 // Functions can be started normally launch waiter( sem, $done ) wait = 0.5 while done < 15 sleep( wait ) wait += 0.1 sem.post() done++ end done = -1 sem.post() > > "Done" tests/core/samples/def_params.fal000066400000000000000000000020241176363201700174050ustar00rootroot00000000000000/* FALCON - Samples FILE: def_params.fal Shows the default parameter idiom. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ > "Test for default parameters" > "Will pring a string prefixed by \">>>\" below" > "---------------------------------------------" > // Let's say we have a function with two parameters, the second one // being optional. // -- In example, this prints p1 eventually prefixed by prefix, if given. function prompt_print( p1, prefix ) if prefix > prefix, p1 else > p1 end end // now we create a version of the function that gives a value for prefix... xprint =.[prompt_print prefix|">>> "] // ... and use it. if args.len() == 0 xprint( "Hello world" ) else for elem in args: xprint( elem ) end tests/core/samples/dirlist.fal000066400000000000000000000023661176363201700167670ustar00rootroot00000000000000/* Falcon Samples. Directory listing. This script demonstrates the usage of the basic directory API. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 25 Dec 2007 13:36:39 +0100 ------------------------------------------------------------------- (C) Copyright 2007: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ //================================================== // Print the current directory printl( "Current directory: ", dirCurrent() ) dir = len( args ) == 0 ? "." : args[0] printl( "Directory list for: ", dir ) try dirHandle = Directory( dir ) elem = dirHandle.read() stats = FileStat() while elem != nil fname = dir + "/" + elem if stats.read( fname ) >> @ "($stats.ftype) $(elem:40) $(stats.size:r10) " + stats.mtime.toString() if stats.ftype == stats.LINK > " -> ", dirReadLink( fname ) else > end else printl( "Failure getting stats for: ", dir + "/" + elem ) end elem = dirHandle.read() end dirHandle.close() catch in error > "Error: ============= " > error end printl() printl( "Done" ) tests/core/samples/fib.fal000066400000000000000000000020571176363201700160520ustar00rootroot00000000000000/* FALCON - Samples FILE: fib.fal A classical fibonacci example. Provide a command-line parameter to determine the width of the fib. Default is 30. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ // Usign strict directive, we must declare variables with def. directive strict = on function fib( n ) if n < 2 return n else return fib(n-1) + fib(n-2) end end /* Uses a default fib value */ /* args is provided by the VM */ if len( args ) > 0 try def reach = int(args[0]) catch > "Please, give a numeric argument." return 0 end else def reach = 30 end > "Calculating FIBONACCI number for ", reach def clock = seconds() print( "Fib(", reach, ")=" ) printl( fib( reach ) ) printl( "Elapsed time: ", (seconds() - clock ) ) /* end */ tests/core/samples/filecopy.fal000066400000000000000000000041221176363201700171170ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - Samples FILE: filecopy.fal This samples shows how to perform a file copy with Falcon. This sample also show the usage of the scriptName variable and of the args vector. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ if len( args ) != 2 printl( "Usage: ", scriptName, " " ) exit(1) end printl( "Copying ", args[0], " into ", args[1] ) try // try opening the input stream. source = InputStream( args[0] ) // then try opening the output stream dest = OutputStream( args[1] ) // let's see how much we have to copy; a seek to end will do inSize = source.seekEnd(0) source.seek(0) // first, read a block initializing a suitable variable. block = source.grab( 1024 ) readSize = len(block) // continue until the last read returns 0 (and this sets the EOF flag). while not source.eof() // write the block. We don't need to specify the lenght... dest.write( block ) // because, as long as the block is a string, read will set its size correctly. readSize += source.read( block ) // a bit of bells and whistles gauge( inSize, readSize ) end source.close() dest.close() catch in err printl( "Can't complete the required operation." ) printl( err.toString() ) if err.systemError: printl( "System error. ", err.getSysErrorDesc() ) end printl() printl("Done.") exit(0) /********************************** * A bit of a gauge **********************************/ function gauge( max, current ) static lastRatio = -1 end if max == 0: return ratio = 60 * (current/max) if lastRatio == int(ratio) return else lastRatio = int(ratio) end gauge = strReplicate( "#", ratio ) prc = current/max*100 print(@"[$(gauge:60)] $(prc:6.2r)%\r") end /* end of filecopy.fal */ tests/core/samples/filecopymb.fal000066400000000000000000000041621176363201700174420ustar00rootroot00000000000000#!/usr/bin/falcons /* FALCON - Samples FILE: filecopymb.fal This script works as filecopy.fal, but it uses Memory Buffers (MemBuf) instead of strings to move files around. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ if len( args ) != 2 printl( "Usage: ", scriptName, " " ) exit(1) end printl( "Copying ", args[0], " into ", args[1] ) try // try opening the input stream. source = InputStream( args[0] ) // then try opening the output stream dest = OutputStream( args[1] ) // let's see how much we have to copy; a seek to end will do inSize = source.seekEnd(0) readSize = 0 source.seek(0) // first, read a block initializing a suitable variable. block = MemBuf( 1024 ) // continue until the last read returns 0 (and this sets the EOF flag). while not source.eof() block.rewind() count = source.read( block ) // write the block. As memblocks are fixed length, we do NEED to // specify it dest.write( block, count ) // because, as long as the block is a string, read will set its size correctly. readSize += count // a bit of bells and whistles gauge( inSize, readSize ) end source.close() dest.close() catch in err printl( "Can't complete the required operation." ) printl( err.toString() ) if err.systemError: printl( "System error. ", err.getSysErrorDesc() ) end printl() printl("Done.") exit(0) /********************************** * A bit of a gauge **********************************/ function gauge( max, current ) static lastRatio = -1 end if max == 0: return ratio = 60 * (current/max) if lastRatio == int(ratio) return else lastRatio = int(ratio) end gauge = strReplicate( "#", ratio ) prc = current/max*100 print(@"[$(gauge:60)] $(prc:6.2r)%\r") end /* end of filecopymb.fal */ tests/core/samples/gcdemo.fal000066400000000000000000000017251176363201700165510ustar00rootroot00000000000000#!/usr/bin/env falcon /* FALCON - Samples FILE: gcdemo.fal Sample showing script controlled garbage collection. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ first = "" GC.enable( true ) //GC.adjust( GC.ADJ_LOOSE ) function makeGC() global first first = [ "alpha".clone(), "beta", 2, 3 , 4 ].clone() for i = 1 to 100000 a = [ "alpha".clone(), "beta", 2, 3 , 4 ].clone() end end for i = 1 to 10 makeGC() > @"$i ====================== " //GC.perform(false) //sleep(0.01) > @"Used memory: $(GC.usedMem:11rg3,)" > @"Live items: $(GC.items:11rg3,)" > @"Normal level: $(GC.th_normal:11rg3,)" > @"Active level: $(GC.th_active:11rg3,)" end /* end of file */ tests/core/samples/hello.ftd000066400000000000000000000006331176363201700164260ustar00rootroot00000000000000 0 ? args[0] : "No argument" ?> <?=title?> 0 ?>

    You said something:

    You said nothing

    > "

    ", param, "

    " end ?> tests/core/samples/intl.fal000066400000000000000000000003231176363201700162520ustar00rootroot00000000000000/* Falcon Samples. Shows internationalization */ directive lang="en_US" > i"Hello world!" var = "value" > @i"The value of var is $(var)" > "This string will stay as in the original" > i"Goodbye world!" tests/core/samples/intl.fr_FR.ftt000066400000000000000000000006761176363201700173150ustar00rootroot00000000000000 Hello world! Bonjour a tout le monde! The value of var is $(var) Le valeur de var est $(var) Goodbye world! Adieu, monde! tests/core/samples/intl.ftr000066400000000000000000000005071176363201700163070ustar00rootroot00000000000000TLTABfr_FRit_IT\ja_JPBonjour a tout le monde!Le valeur de var est $(var) Adieu, monde! Salve, mondo!Il valore di var è $(var) Addio, mondo!こんにちは、世界よ!Varの値は $(var) です。さようなら、世界よ!tests/core/samples/intl.it_IT.ftt000066400000000000000000000006621176363201700173220ustar00rootroot00000000000000 Hello world! Salve, mondo! The value of var is $(var) Il valore di var è $(var) Goodbye world! Addio, mondo! tests/core/samples/intl.ja_JP.ftt000066400000000000000000000007271176363201700172770ustar00rootroot00000000000000 Hello world! こんにちは、世界よ! The value of var is $(var) Varの値は $(var) です。 Goodbye world! さようなら、世界よ! tests/core/samples/life.fal000066400000000000000000000063561176363201700162370ustar00rootroot00000000000000/***************************************************** The life game */ ALIVE=ord("O") DEAD=ord("-") // strange, doing write = stdOut().write crashes after a few calls to write() write = stdOut().write class Life( width, height ) w = width h = height thisgen = nil nextgen = nil // we need an array of rows... init self.thisgen = arrayBuffer( height+1 ) self.nextgen = arrayBuffer( height+1 ) for row in [1: height+1] self.thisgen[row] = arrayBuffer( width+1 ) self.nextgen[row] = arrayBuffer( width+1 ) for col in [1:width+1] self.thisgen[row][col] = 0 self.nextgen[row][col] = 0 end end end function spawn( shape, left, top ) for y in [0: shape.h] for x in [0: shape.w] self.thisgen[top+y][left+x] = shape[y*shape.w+x] end end end // run the CA and produce the next generation function evolve() static w = self.w end next = self.nextgen data = self.thisgen ym1,y=self.h-1,self.h for yp1 in [1:self.h+1] xm1,x=w-1,w nextRow = next[y] for xp1 in [1:w+1] rm1 = data[ym1] ry = data[y] rp1 = data[yp1] sum = rm1[xm1] + rm1[x] + rm1[xp1] + \ ry[xm1] + ry[xp1] + \ rp1[xm1] + rp1[x] + rp1[xp1] nextRow[x] = (sum==2) and ry[x] ? 1 : sum==3 ? 1 : 0 xm1,x = x,xp1 end ym1,y = y,yp1 end // exchange data. self.nextgen = self.thisgen self.thisgen = next end // Display this generation function draw() static out = strReplicate( " ", self.h*(self.w+1) ) // Write all at once height = self.h+1 width = self.w end p = 0 for y in [1:height] row = self.thisgen[y] for x in [0:width] if row[x+1] out[p+x] = ALIVE else out[p+x] = DEAD end end p += self.w out[p] = 10 p++ end write( out ) end end /************************************************** Shapes that we may want to use as tests They are arrays with w and h bindings. **************************************************/ HEART = [1,0,1,1,0,1,1,1,1]; HEART.w = 3; HEART.h = 3 GLIDER = [0,0,1,1,0,1,0,1,1]; GLIDER.w = 3; GLIDER.h = 3 EXPLODE = [0,1,0,1,1,1,1,0,1,0,1,0]; EXPLODE.w = 3; EXPLODE.h = 4 FISH = [0,1,1,1,1,1,0,0,0,1,0,0,0,0,1,1,0,0,1,0]; FISH.w = 5; FISH.h = 4 BUTTERFLY = [1,0,0,0,1,0,1,1,1,0,1,0,0,0,1,1,0,1,0,1,1,0,0,0,1] BUTTERFLY.w = 5; BUTTERFLY.h = 5 /**************************************************** * Main routine ****************************************************/ // create some life life = Life( 40, 20 ) // some spawning /*life.spawn(GLIDER, 2, 2) life.spawn(EXPLODE, 40, 10) life.spawn(FISH, 10, 14)*/ life.spawn(GLIDER, 5,4) life.spawn(EXPLODE, 25,10) life.spawn(FISH, 4,12) // run until break gen = 0 write("\x1b[2J") // ANSI clear screen while gen < 2000 life.evolve() write("\x1b[H") // ANSI home cursor life.draw() write( "Life - generation " + ++gen ) //sleep(0.05) // slight delay end printl() tests/core/samples/membuf.fal000066400000000000000000000031111176363201700165550ustar00rootroot00000000000000/* FALCON - Samples FILE: membuf.fal Minimal membuffer operations Transforms a string given as parameter into a membuffer. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 18 Mar 2008 00:34:06 +0100 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ const defString = "A default string to test memory buffers" if args.len() == 0 or args[0].len() == 0 > "No arguments provided, using the default string:" > "'", defString, "'" str = defString else str = args[0] > "Using: '", str, "'" end memory = MemBuf( str.len() ) for i in [0 : str.len()] memory[i] = str[*i] end > "Transfer complete; dumping the memory buffer:" fmt = Format( "2xp0" ) >> "[" for i in [0: memory.len()] >> fmt.format( memory[i] ), ", " end > "]" /* temporary removed // you can create the very same membuffer using strToMemBuf memory = strToMemBuf( str ) > "Through for/in:" // again for byte in memory.len() forfirst: >> "[" >> fmt.format( byte ) formiddle: >> ", " forlast: > "]." end // and again > "Through iterator:" iter = memory.first() >> "[" while iter.hasCurrent() >> fmt.format( iter.value() ), ", " iter.next() end > "]" > "Changing all the chars:" // then you can change a character and have a string back... for byte in memory if byte != 0x20 // unicode space .= 65 // unicode 'A' end end > strFromMemBuf( memory ) > "Complete." */ /* end */ tests/core/samples/random.fal000066400000000000000000000030471176363201700165720ustar00rootroot00000000000000#!/usr/local/bin/falcon /* FALCON - Samples FILE: random.fal A little showdown of random capabilities. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ const loops = 100000 const elements = 10 // let's generate a good seed seed = int(seconds() * 1000) printl( "Random functions showdown." ) printl( "Setting random seed to ", seed ) randomSeed( seed ) printl( "Filling ", elements, " elements with ", loops, " random values" ) // we'll fill an array of 10 elements with 10000 instances... array = [] for i in [0 : elements] array += 0 end for i in [ 0 : loops] array[ random( elements-1 ) ] += 1 if i % 100 == 0 print( "Processing: ", (i / loops)*100, "% \r" ) end end printl( "Results: ") showResults( array ) printl( "Randomizing the array with random grab." ) array = randomGrab( array, elements ) printl( "Results: ") showResults( array ) printl() printl("Done.") exit(0) function showResults( array ) sum = 0 for i in [ 0 : elements] print( @"$(i:2):" ) val = array[ i - 1 ] point = val / loops * elements * elements for j in [0 : point] print( "*" ) end printl( " ", val ) sum += val end printl( "Sum: ", sum ) end /* end of process.fal */ tests/core/samples/serialTest.fal000066400000000000000000000037161176363201700174340ustar00rootroot00000000000000#!falcon /* FALCON - Samples FILE: serialTest.fal Serializer and deserializer for file streams. Use parameter -o to write an output serialized file Use parameter -i to read a previously serialized file ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ object obj a = 1.3 b = [ "c" => "d" ] function test() printl( "Test!!" ) end end printl( "Falcon sample programs." ) printl( "Serialization sample." ) printl( "-------------------------" ) if len( args ) < 2 printl( "specify '-i/-o ' on the command line" ) return end if args[0] != "-o" and args[0] != "-i" printl( "Specify either -i or -o on the command line" ) return end if args[0] == "-o" printl( "Writing to file ", args[1], "\n" ) array = [ "Hello world", 1, nil, 1.3, [3:-1], [3:], [1, 2, 3], ["a"=>1, "b"=>2, "c"=>3 ], obj, "last" ] try stream = OutputStream( args[1] ) for elem in array elem.serialize( stream ) end stream.close() catch in err printl( "Error while writing!" ) printl( err.toString() ) end else printl( "Reading from file ", args[1], "\n" ) try stream = InputStream( args[1] ) count = 1 while true item = deserialize( stream ) if stream.eof() break end print( "Deserialized ", count, ": " ) inspect( item ) count ++ end stream.close() catch in err if stream.eof() > "Stream hit EOF." else printl( "Error while reading!" ) printl( err.toString() ) end end end printl( "-------------------------" ) printl( "Test complete." ) tests/core/samples/stateful.fal000066400000000000000000000063151176363201700171420ustar00rootroot00000000000000/* FALCON - Samples FILE: stateful.fal Test showing a bit of mangling in enter-leave of states. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 15 Nov 2009 12:49:14 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ class Stateful init > "Stateful.init" end function stateless() > "This is the same" end function neverSeen() > "Never seen" end function _common_enter( orig, leaveval ) > "Entering \"", self.getState(), @"\" coming from \"$orig\"." > "Parameter from leave: ", leaveval return random(100) end function _common_leave( orig ) > "Leaving \"", self.getState(), @"\" to go in \"$orig\"." return random(100) end [init] function __enter( orig, lval ): return self._common_enter( orig, lval ) function __leave( dest ): return self._common_leave( dest ) function neverSeen() > "never seen - From init" end function f1() > "F1 from init" end function f2() > "F2 from init" end end [first] function __enter( orig, lval ): return self._common_enter( orig, lval ) function __leave( dest ): return self._common_leave( dest ) function neverSeen() > "never seen - From first" end function f1() > "F1 from first" end function f2() > "F2 from first" end end [second] function __enter( orig, lval ): return self._common_enter( orig, lval ) function __leave( dest ): return self._common_leave( dest ) function neverSeen() > "never seen - From second" end function f1() > "F1 from second" end function f3() > "F3 from second" end end end class Stateful_sub from Stateful init > "Stateful_sub.init" end [first] function f1() > "F1 from first -- in Stateful_sub" end end [third] function f1() > "F1 from third" end end end > ' === Stateful classes test === This test just show how class states can be used to control which set of function is temporarily in charte in one instance, and how to detect state changes via __enter and __leave special functions. Also, notice that the subclass initialized in the last part of the test gets completely constructed (via init) and THEN the __enter in the init state of the base class is called. The __enter special method in the __init state is actually an handler for the base class to control subclasses instantation. ' > "Press ENTER to continue...", input() > "Hello from stateful" s = Stateful() > "Stateful created" s.stateless() > "S now in ", s.getState() s.f1() v = s.setState( s.second ) > "Set State return value: ", v > "S now in ", s.getState() s.f3() v = s.setState( "first" ) > "Set State return value: ", v > "S now in ", s.getState() s.neverSeen() s = Stateful_sub() s.setState( "first" ) s.f1() s.setState( "second" ) s.f1() s.setState( "third" ) s.f1() tests/core/samples/stdStreams.fal000066400000000000000000000016131176363201700174400ustar00rootroot00000000000000/* Falcon Samples. Standard I/O streams test. This just checks for standard VM streams to work correctly by reversing the input stream to the output stream (line by line) and then writing the number of parsed line to the standard error. */ > ' Test for ''standard input stream'' Enter some text line; it will be printed in reverse order to confirm it has been read. After entering some line, close the stream with CTRL+D on UNIX and CTRL+Z (then enter) on MS-Windows. The count of read lines will be printed.' sInput = stdIn() sOutput = stdOut() sError = stdErr() count = 0 line = "" while not sInput.eof() if sInput.readLine( line, 512 ) == 0: continue if line.len() == 1 sOutput.write( line ) else sOutput.write( line[-1:0] ) end sOutput.write( "\n" ) count++ end sError.write( @"\nParsed $count lines\n\n" ) /* End of stdStreams.fal */ tests/core/samples/timecalc.fal000066400000000000000000000030101176363201700170610ustar00rootroot00000000000000/* FALCON - Samples FILE: timecalc.fal This test is meant to show overload (toString) and accessor functionalities in classes instances. ------------------------------------------------------------------- Author: Giancarlo Niccolai, Maik Beckmann Begin: Sun, 11 Oct 2009 20:41:46 +0200 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ class Time _time = nil // in seconds function __set_hours(var) self._time = var * 3600 end function __get_hours(var) return self._time / 3600 end function __set_minutes(var) self._time = var * 60 end function __get_minutes(var) return self._time / 60 end function __set_seconds(var) self._time = var end function __get_seconds(var) return self._time end function toString() mins = int(self.minutes) % 60 secs = self.seconds % 60 return @"$(self.hours:.0)h, $(mins)m, $(secs)s" end end > "Time calculator." if args.len() == 0 > "Please, call with a number optionally followed by 'h' (hours) or 'm' (minutes)" > "Numbers without suffix will be interpreted as seconds" return 0 end try time = Time() n = numeric( args[0] ) if args[0][-1] == 'h' time.hours = n elif args[0][-1] == 'm' time.minutes = n else time.seconds = n end > args[0], " = ", time catch TypeError > "Wrong number format \"", args[0], "\"" end tests/core/samples/timestamp.fal000066400000000000000000000016631176363201700173170ustar00rootroot00000000000000/* FALCON - Samples FILE: timestamp.fal Shows a bit of date handling. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ ts = TimeStamp() ts.currentTime() printl( "Default time representation: ", ts.toString() ) printl( "Strfmt string representation: ", ts.toString( "%X %x" ) ) printl( "Waiting 0.35" ) sleep( 0.35 ) tsnew = TimeStamp() tsnew.currentTime() printl( tsnew.toString() ) printl( "Comparing old -> new : ", ts.compare( tsnew ) ) printl( "Comparing new -> old : ", tsnew.compare( ts ) ) printl( "Comparing new -> new : ", tsnew.compare( tsnew ) ) tsdiff = TimeStamp( tsnew ) tsdiff.distance( ts ) printl( "Difference: ", tsdiff.toString() ) /* end */ tests/core/samples/transcoder.fal000066400000000000000000000033011176363201700174470ustar00rootroot00000000000000/* FALCON - Samples FILE: transcoder.fal Transcodes an input file from a certain encoding to another. This code is currently highly experimental. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ------------------------------------------------------------------- (C) Copyright 2006: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ if len( args ) != 4 printl( "usage: ", scriptName, " " ) printl( "Knwon encodings: "+ "\tutf-8\t\tUnicode UTF-8\n"+ "\tutf-16\t\tUnicode UTF-16 (endian detecting)\n"+ "\tutf-16BE\tUnicode UTF-16 Big Endian\n"+ "\tutf-16LE\tUnicode UTF-16 Little Endian\n"+ "\tiso8859-\twhere 1..15: ISO tables\n"+ "\tcp1252\t\tMS-Windows code page LATIN\n"+ "\tgbk\t\tGBK encoding for modern chinese\n"+ "\tC\t\tDirect untranslated byte encoding\n" ) printl( "\n...Local system encoding: ", getSystemEncoding(), "\n" ) return 0 end try input = InputStream( args[0] ) output = OutputStream( args[2] ) catch in error printl( "Error while opening streams: ", error.toString() ) return 1 end try input.setEncoding( args[1] ) catch printl( "Unknown encoding ", args[1] ) return 1 end try output.setEncoding( args[3] ) catch printl( "Unknown encoding ", args[3] ) return 1 end try buffer = "" while( input.readText( buffer, 1024 ) != 0 ) output.writeText( buffer ) end input.close() output.close() catch in error printl( "Error while transcoding streams: ", error.toString() ) return 1 end printl( "Operation complete." ) return 0 /* end of transcoder.fal */ tests/core/samples/utf16.fal000066400000000000000000000033361176363201700162600ustar00rootroot00000000000000#!/usr/local/bin/falcon -E utf-16 /* FALCON - Samples FILE: utf16.fal This sample is just a nice Falcon program saved in utf16 encoding. Compile this with "-E utf-16". ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ //================================ // function konnichi wa, "hello"! function S00D0a0o0( $P1 ) printl( "$Po0 0", $P1, " 0 `0c0_0." ) end //================================== // Main program // if len( args ) == 0 printl( "T00000000}k0UOK0fD0f0 NU0D00m0G0" ) printl( "Sorry, may you please write something on the command line?" ) return 0 end for s^ in args S00D0a0o0( s^ ) end /* end of utf16.fal */ tests/core/samples/vminfo.fal000066400000000000000000000012141176363201700166020ustar00rootroot00000000000000/* FALCON - Samples FILE: vminfo.fal Reports VM and eventually module informations. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ > "VM Info - Prints Falcon Virtual Machine informations." > major, minor, revision = vmVersionInfo() > @ "Falcon Virtual Machine $(major).$(minor).$(revision)." > "Version description: ", vmVersionName() > "System type: ", vmSystemType() tests/core/samples/wordcount.fal000066400000000000000000000022111176363201700173260ustar00rootroot00000000000000/* FALCON - Samples FILE: wordcount.fal Counts the words found in a file through the tokenizer interface. Give a file as a parameter or use stdin ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 22 Mar 2009 20:24:01 +0100 ------------------------------------------------------------------- (C) Copyright 2009: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ if args.len() > 0 try stream = InputStream( args[0] ) catch IoError in error > "Sorry, can't open file ", args[0] > error return 1 end else stream = stdIn() end t = Tokenizer( seps|" ;,|!.:\"'?\\/*+\r\n)", options|Tokenizer.groupsep ) t.parse( stream ) words = [=>] for token in t if token in words ++words[token] else words[token] = 1 end end > "========================================================" > "Results" > "========================================================" for w,n in words > @"$w: $n" end > "========================================================" > "Done." /* end of file */ tests/core/testsuite/000077500000000000000000000000001176363201700152075ustar00rootroot00000000000000tests/core/testsuite/0a.fal000066400000000000000000000010371176363201700161740ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 0a * Category: smoke * Subcategory: * Short: Minimal smoke test * Description: * Minimal test to check the operativity of the VM, compiler, linker and * unit test package. * * This just stores 0 in the return field of the virtual machine. * [/Description] * * // To be implemented. Expected failure step & expected error ****************************************************************************/ exit( 0 ) /* End of file */ tests/core/testsuite/0b.fal000066400000000000000000000007601176363201700161770ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 0b * Category: smoke * Subcategory: * Short: String smoke test * Description: * Minimal test to check the operativity of the VM, compiler, linker and * unit test package. * * This just stores a string in the return field of the virtual machine. * [/Description] ****************************************************************************/ exit( "Hello world" ) /* End of file */ tests/core/testsuite/0c.fal000066400000000000000000000007551176363201700162040ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 0c * Category: smoke * Subcategory: * Short: Smoke test on arrays * Description: * Minimal test to check the operativity of the VM, compiler, linker and * unit test package. * * This just stores an array in the return field of the virtual machine. * [/Description] ****************************************************************************/ exit( 1, 2, 3 ) /* End of file */ tests/core/testsuite/0d.fal000066400000000000000000000007551176363201700162050ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 0d * Category: smoke * Subcategory: * Short: Smoke test on ranges * Description: * Minimal test to check the operativity of the VM, compiler, linker and * unit test package. * * This just stores a range in the return field of the virtual machine. * [/Description] ****************************************************************************/ exit( [ 1: 2 ] ) /* End of file */ tests/core/testsuite/0e.fal000066400000000000000000000010201176363201700161700ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 0e * Category: smoke * Subcategory: * Short: Smoke test on dictionaries * Description: * Minimal test to check the operativity of the VM, compiler, linker and * unit test package. * * This just stores a dictionary in the return field of the virtual machine. * [/Description] ****************************************************************************/ exit( [ "1" => "hello", 2 => "world" ] ) /* End of file */ tests/core/testsuite/0f.fal000066400000000000000000000007621176363201700162050ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 0f * Category: smoke * Subcategory: * Short: Smoke test on numeric * Description: * Minimal test to check the operativity of the VM, compiler, linker and * unit test package. * * This just stores a numeric in the return field of the virtual machine. * [/Description] * ****************************************************************************/ exit( 12.30e21 ) /* End of file */ tests/core/testsuite/0g.fal000066400000000000000000000010771176363201700162060ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 0g * Category: smoke * Subcategory: * Short: Smoke test on encoding * Description: * Minimal test to check the operativity of the VM, compiler, linker and * unit test package. * * An UTF-8 direct string is delivered to the VM. * [/Description] * ****************************************************************************/ // Saying: "People, this is a Japanese utf-8 string" exit( "皆さん、これは日本語のutf-8ストリング" ) /* End of file */ tests/core/testsuite/CMakeLists.txt000066400000000000000000000022411176363201700177460ustar00rootroot00000000000000set(categories RTL basic classes comp expression expressions fbom function functional functions gc iterators lambda loops macro membuf messages meta methods modloader poop prototype reference rtl select smoke statements states switch tabular template types ) # clean this directory from artefacts of former faltest runs file(GLOB _fams "*.fam") if(_fams) file(REMOVE ${_fams}) endif(_fams) # start memcheck ignore list file(WRITE ${CMAKE_BINARY_DIR}/CTestCustom.cmake "set(CTEST_CUSTOM_MEMCHECK_IGNORE\n") add_test(testsuite_all ${CMAKE_COMMAND} -P test_driver.cmake) file(APPEND ${CMAKE_BINARY_DIR}/CTestCustom.cmake " testsuite_all\n") foreach(category ${categories}) add_test(testsuite_category_${category} ${CMAKE_COMMAND} -Dtest_category=${category} -P test_driver.cmake) file(APPEND ${CMAKE_BINARY_DIR}/CTestCustom.cmake " testsuite_category_${category}\n") endforeach(category) # wrap memcheck ignore list up file(APPEND ${CMAKE_BINARY_DIR}/CTestCustom.cmake ")") configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/test_driver.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/test_driver.cmake @ONLY ) tests/core/testsuite/Import spaced/000077500000000000000000000000001176363201700177015ustar00rootroot00000000000000tests/core/testsuite/Import spaced/imported.fal000066400000000000000000000000551176363201700222100ustar00rootroot00000000000000function test1() return "From test1" end tests/core/testsuite/README000066400000000000000000000014761176363201700160770ustar00rootroot00000000000000This directory contains the main CORE regression tests. The scripts here are not stand-alone, and can't be normally called with "falcon", as they integrate with some utility provided by "faltest". A complete and working installation of falcon can be tested by entering in this directory and launching "faltest" from the command line. If the bottom line reports an error, then something is broken. The faltest manual in the "Command Line Tools" guide (available with "man faltest" on Linux and downloadable from http://falconpl.org) illustrates how to use faltest to have more accurate error reports. In case of this preliminary test failing, please send a complete report generated with "faltest -v" applied on the failing tests to the Committe through our "contacts" page at http://falconpl.org/index.ftd?page_id=contactstests/core/testsuite/arr_change.fal000066400000000000000000000131011176363201700177600ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 11k * Category: types * Subcategory: array * Short: Ranged substitutions. * Description: * Verify that substitutions performed on arrays with ranges are correct. * This test only checks for positive ranges. * [/Description] * ****************************************************************************/ array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] // array change 1 item array[3:4] = "alpha" if len( array ) != 8 : failure( "item - len" ) if array[2] != 2 or array[4] != 4: failure( "item - surround" ) if array[3] != "alpha": failure( "item - item" ) //========================================= // Substitute with one item // // array change more items - middle array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[3:6] = "alpha" if len( array ) != 6 : failure( "more items - len" ) if array[2] != 2 or array[4] != 6: failure( "more items - surround" ) if array[3] != "alpha": failure( "more items - item" ) array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[5:3] = "alpha" if len( array ) != 6: failure( "more items reverse - len" ) if array[2] != 2 or array[4] != 6: failure( "more items reverse - surround" ) if array[3] != "alpha": failure( "more items reverse - item" ) // array change more items - begin array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[0:3] = "alpha" if len( array ) != 6 : failure( "more items (begin) - len" ) if array[1] != 3: failure( "more items (begin) - surround" ) if array[0] != "alpha": failure( "more items (begin) - item" ) array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[2:0] = "alpha" if len( array ) != 6 : failure( "more items (begin) reverse - len" ) if array[1] != 3: failure( "more items (begin) reverse - surround" ) if array[0] != "alpha": failure( "more items (begin) reverse - item" ) // array change more items - end array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[6:] = "alpha" if len( array ) != 7 : failure( "more items (end) - len" ) if array[5] != 5: failure( "more items (end) - surround" ) if array[6] != "alpha": failure( "more items (end) - item" ) //========================================= // Substitute with a shorter array // // in the middle array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[2:6] = ["a","b"] if len( array ) != 6 : failure( "smaller array - len" ) if array[1] != 1 or array[4] != 6: failure( "smaller array - surround" ) if array[2] != "a" or array[3] != "b": failure( "smaller array - items" ) array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[5:2] = ["a","b"] if len( array ) != 6 : failure( "smaller array reverse - len" ) if array[1] != 1 or array[4] != 6: failure( "smaller array reverse - surround" ) if array[2] != "a" or array[3] != "b": failure( "smaller array reverse - items" ) // at begin the middle array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[0:4] = ["a","b"] if len( array ) != 6 : failure( "smaller array (begin) - len" ) if array[2] != 4: failure( "smaller array (begin) - surround" ) if array[0] != "a" or array[1] != "b": failure( "smaller array (begin) - items" ) array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[3:0] = ["a","b"] if len( array ) != 6 : failure( "smaller array (begin) reverse - len" ) if array[2] != 4: failure( "smaller array (begin) reverse - surround" ) if array[0] != "a" or array[1] != "b": failure( "smaller array (begin) reverse - items" ) // at begin the middle array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[4:] = ["a","b"] if len( array ) != 6 : failure( "smaller array (end) - len" ) if array[3] != 3: failure( "smaller array (end) - surround" ) if array[4] != "a" or array[5] != "b": failure( "smaller array (end) - items" ) //========================================= // Substitute with a larger array // // in the middle array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[2:4] = ["a","b","c"] if len( array ) != 9 : failure( "larger array - len" ) if array[1] != 1 or array[6] != 5: failure( "Range larger smaller array - surround" ) if array[2] != "a" or array[4] != "c": failure( "larger array - items" ) array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[3:2] = ["a","b","c"] if len( array ) != 9 : failure( "larger array reverse - len" ) if array[1] != 1 or array[6] != 5: failure( "Range larger smaller array reverse - surround" ) if array[2] != "a" or array[4] != "c": failure( "larger array reverse - items" ) // at begin the middle array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[0:2] = ["a","b","c"] if len( array ) != 9 : failure( "larger array (begin) - len" ) if array[3] != 2: failure( "larger array (begin) - surround" ) if array[0] != "a" or array[2] != "c": failure( "larger array (begin) - items" ) array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[1:0] = ["a","b","c"] if len( array ) != 9 : failure( "larger array (begin) reverse - len" ) if array[3] != 2: failure( "larger array (begin) reverse - surround" ) if array[0] != "a" or array[2] != "c": failure( "larger array (begin) reverse - items" ) // at begin the middle array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[6:] = ["a","b","c"] if len( array ) != 9 : failure( "larger array (end) - len" ) if array[5] != 5: failure( "larger array (end) - surround" ) if array[6] != "a" or array[8] != "c": failure( "larger array (end) - items" ) success() /* End of file */ tests/core/testsuite/arr_change_neg.fal000066400000000000000000000131421176363201700206160ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 11l * Category: types * Subcategory: array * Short: Ranged substitutions negative. * Description: * Verify that substitutions performed on arrays with ranges are correct. * This test only checks for negative ranges. * [/Description] * ****************************************************************************/ array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] // array change 1 item array[-5:-4] = "alpha" if len( array ) != 8 : failure( "item - len" ) if array[2] != 2 or array[4] != 4: failure( "item - surround" ) if array[3] != "alpha": failure( "item - item" ) //========================================= // Substitute with one item // // array change more items - middle array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[-5:-2] = "alpha" if len( array ) != 6 : failure( "more items - len" ) if array[2] != 2 or array[4] != 6: failure( "more items - surround" ) if array[3] != "alpha": failure( "more items - item" ) array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[-3:-5] = "alpha" if len( array ) != 6: failure( "more items reverse - len" ) if array[2] != 2 or array[4] != 6: failure( "more items reverse - surround" ) if array[3] != "alpha": failure( "more items reverse - item" ) // array change more items - begin array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[0:-5] = "alpha" if len( array ) != 6 : failure( "more items (begin) - len" ) if array[1] != 3: failure( "more items (begin) - surround" ) if array[0] != "alpha": failure( "more items (begin) - item" ) array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[-6:0] = "alpha" if len( array ) != 6 : failure( "more items (begin) reverse - len" ) if array[1] != 3: failure( "more items (begin) reverse - surround" ) if array[0] != "alpha": failure( "more items (begin) reverse - item" ) // array change more items - end array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[-2:] = "alpha" if len( array ) != 7 : failure( "more items (end) - len" ) if array[5] != 5: failure( "more items (end) - surround" ) if array[6] != "alpha": failure( "more items (end) - item" ) //========================================= // Substitute with a shorter array // // in the middle array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[-6:-2] = ["a","b"] if len( array ) != 6 : failure( "smaller array - len" ) if array[1] != 1 or array[4] != 6: failure( "smaller array - surround" ) if array[2] != "a" or array[3] != "b": failure( "smaller array - items" ) array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[-3:-6] = ["a","b"] if len( array ) != 6 : failure( "smaller array reverse - len" ) if array[1] != 1 or array[4] != 6: failure( "smaller array reverse - surround" ) if array[2] != "a" or array[3] != "b": failure( "smaller array reverse - items" ) // at begin the middle array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[0:-4] = ["a","b"] if len( array ) != 6 : failure( "smaller array (begin) - len" ) if array[2] != 4: failure( "smaller array (begin) - surround" ) if array[0] != "a" or array[1] != "b": failure( "smaller array (begin) - items" ) array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[-5:0] = ["a","b"] if len( array ) != 6 : failure( "smaller array (begin) reverse - len" ) if array[2] != 4: failure( "smaller array (begin) reverse - surround" ) if array[0] != "a" or array[1] != "b": failure( "smaller array (begin) reverse - items" ) // at begin the middle array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[-4:] = ["a","b"] if len( array ) != 6 : failure( "smaller array (end) - len" ) if array[3] != 3: failure( "smaller array (end) - surround" ) if array[4] != "a" or array[5] != "b": failure( "smaller array (end) - items" ) //========================================= // Substitute with a larger array // // in the middle array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[-6:-4] = ["a","b","c"] if len( array ) != 9 : failure( "larger array - len" ) if array[1] != 1 or array[6] != 5: failure( "Range larger smaller array - surround" ) if array[2] != "a" or array[4] != "c": failure( "larger array - items" ) array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[-5:-6] = ["a","b","c"] if len( array ) != 9 : failure( "larger array reverse - len" ) if array[1] != 1 or array[6] != 5: failure( "Range larger smaller array reverse - surround" ) if array[2] != "a" or array[4] != "c": failure( "larger array reverse - items" ) // at begin the middle array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[0:-6] = ["a","b","c"] if len( array ) != 9 : failure( "larger array (begin) - len" ) if array[3] != 2: failure( "larger array (begin) - surround" ) if array[0] != "a" or array[2] != "c": failure( "larger array (begin) - items" ) array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[-7:0] = ["a","b","c"] if len( array ) != 9 : failure( "larger array (begin) reverse - len" ) if array[3] != 2: failure( "larger array (begin) reverse - surround" ) if array[0] != "a" or array[2] != "c": failure( "larger array (begin) reverse - items" ) // at begin the middle array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] array[-2:] = ["a","b","c"] if len( array ) != 9 : failure( "larger array (end) - len" ) if array[5] != 5: failure( "larger array (end) - surround" ) if array[6] != "a" or array[8] != "c": failure( "larger array (end) - items" ) success() /* End of file */ tests/core/testsuite/arr_comp.fal000066400000000000000000000044511176363201700175010ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 121a * Category: comp * Subcategory: array * Short: Comprehension on arrays * Description: * Comprehension is an expression or expression set generating * lists or sets. * * Falcon comprehension is quite articulated, as it is composed of * .comp( , [] ) * * where is the target sequence, is a generating * item (range, source sequence or generator function) and filter may * transform the data in the item on the fly or discard it. * * [/Description] * ****************************************************************************/ // direct comprehension a = [].comp( .[ 'a' 'b' 'c' ] ) if a.len() != 3: failure( "Direct comprehension, size" ) if a[0] != 'a': failure( "Direct comprehension, 'a'" ) if a[1] != 'b': failure( "Direct comprehension, 'b'" ) if a[2] != 'c': failure( "Direct comprehension, 'c'" ) // indirect a = [].comp( ['a','b','c'], { letter=> ord(letter) } ) if a.len() != 3: failure( "Indirect comprehension, size" ) if a[0] != ord('a'): failure( "Indirect comprehension, 'a'" ) if a[1] != ord('b'): failure( "Indirect comprehension, 'b'" ) if a[2] != ord('c'): failure( "Indirect comprehension, 'c'" ) // indirect a = [].comp( ['a','b','Z','c'], { letter=> letter > 'Z' ? ord(letter): oob(1) } ) if a.len() != 3: failure( "Skipping 'Z'" ) if a[0] != ord('a'): failure( "Skipping 'Z', 'a'" ) if a[1] != ord('b'): failure( "Skipping 'Z', 'b'" ) if a[2] != ord('c'): failure( "Skipping 'Z', 'c'" ) // ranged comprehension a = [].comp( [ ord('a'): ord('d')], { ln => chr(ln) } ) if a.len() != 3: failure( "Ranged comprehension, size" ) if a[0] != 'a': failure( "Ranged comprehension, 'a'" ) if a[1] != 'b': failure( "Ranged comprehension, 'b'" ) if a[2] != 'c': failure( "Ranged comprehension, 'c'" ) // ranged comprehension function gengen() val = 0 gen = function() v = val val += 1 if val == 4: return oob(0) return v end return gen end a = [].comp( gengen(), { ln => 'a' / ln } ) if a.len() != 3: failure( "Generator comprehension, size" ) if a[0] != 'a': failure( "Generator comprehension, 'a'" ) if a[1] != 'b': failure( "Generator comprehension, 'b'" ) if a[2] != 'c': failure( "Generator comprehension, 'c'" ) success() tests/core/testsuite/array.fal000066400000000000000000000055451176363201700170220ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 11a * Category: types * Subcategory: array * Short: Basic array * Description: * Checks for correctness of basic array operations. * Using only integers in this test * [/Description] * ****************************************************************************/ // assignment test - include a compilation test array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] // len test if len( array ) != 8: failure( "Len core function" ) if len( [] ) != 0: failure( "Len of empty array" ) // minimal access if array[2] != 2: failure( "Smoke test" ) // array update array[2] = 100 if array[2] != 100: failure( "Minimal assign" ) // range access subarr = array[3:4] if subarr[0] != 3: failure( "Range access 1" ) if len( subarr ) != 1: failure( "Range access 1 - len" ) subarr = array[3:5] if subarr[0] != 3 or subarr[1] != 4: failure( "Range access 2" ) if len( subarr ) != 2: failure( "Range access 2 - len" ) // array reversal subarr = array[5:3] if subarr[1] != 4: failure( "Range reversal" ) if len( subarr ) != 3: failure( "Range reversal - len" ) // array deletion array[6:7] = [] if array[6] != 7: failure( "Range deletion" ) if len( array ) != 7: failure( "Range deletion - len" ) // array insertion array[6:6] = 6 if array[6] != 6:failure( "Range insertion item" ) if array[5] != 5 or array[7] != 7: failure( "Range insertion surrounding" ) if len( array ) != 8: failure( "Range insertion item - len" ) array[6:6] = [1, 2, 3] if array[7] != 2: failure( "Range insertion array" ) if len( array ) != 11: failure( "Range insertion array - len" ) // array copying array1 = [1, 2, 3] array2 = [0, 0, 0] array2.copyFrom(0, array1, 0) array1[0] = 9 array1[1] = 9 array1[2] = 9 if array2[0] != 1 or array2[1] != 2 or array2[2] != 3: failure( "Array copying" ) // array changes are in another test ( arr_change.fal -- 11k) // array concatenation test = [1,2] arr2 = test + [3,4] + [5,6] if arr2[2] != 3: failure( "Concatenation" ) if arr2[4] != 5: failure( "Concatenation (second)" ) if len( arr2 ) != 6: failure( "Concatenation (len)" ) if len( test ) != 2: failure( "Invariance in concatenation" ) // array addition arr2 += [5,6] if arr2[4] != 5: failure( "Addition" ) // in operator if not 2 in arr2: failure( "In operator - positive" ) if 2 notin arr2: failure( "Notin operator - negative" ) if 10 in arr2: failure( "In operator - negative" ) if not 10 notin arr2: failure( "Notin operator - negative" ) // resize with a negative integer. arr2.resize(-1) // as 0 if arr2.len() > 0: failure( "Resize negative" ) // unpacking array = [1,2] a,b = array if a != 1 or b != 2: failure( "unpacking" ) // should raise an AccessError throw = false try a,b,c = array catch AccessError throw = true end if not throw: failure( "invalid unpack statement" ) success() /* End of file */ tests/core/testsuite/array_call.fal000066400000000000000000000031621176363201700200060ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 60b * Category: functional * Subcategory: execarr * Short: Array call * Description: * Test for callable arrays, base of falcon functional programming. * [/Description] * ****************************************************************************/ function theCall( p1, p2, p3, p4 ) global gp1, gp2, gp3, gp4 gp1 = p1 gp2 = p2 gp3 = p3 gp4 = p4 end function reset() global gp1, gp2, gp3, gp4 gp1 = nil gp2 = nil gp3 = nil gp4 = nil end // is callable test if isCallable( [1,2,3] ): failure( "Callable negative" ) if not isCallable( [theCall,2,3] ): failure( "Callable positive 1" ) call = [theCall] if not isCallable( call ): failure( "Callable positive 2" ) // call array pure reset() [theCall, 1, 2, 3, 4]() if gp1 != 1 or gp2 != 2 or gp3 != 3 or gp4 != 4 failure( "Direct call pure" ) end reset() call = [theCall, 1, 2, 3, 4] call() if gp1 != 1 or gp2 != 2 or gp3 != 3 or gp4 != 4 failure( "Indirect call pure" ) end // call array spurious reset() [theCall, 1, 2](3,4) if gp1 != 1 or gp2 != 2 or gp3 != 3 or gp4 != 4 failure( "Direct call spurious" ) end reset() call = [theCall, 1, 2 ] call(3,4) if gp1 != 1 or gp2 != 2 or gp3 != 3 or gp4 != 4 failure( "Indirect call spurious" ) end // call array impure reset() [theCall](1,2,3,4) if gp1 != 1 or gp2 != 2 or gp3 != 3 or gp4 != 4 failure( "Direct call impure" ) end reset() call = [theCall] call(1,2,3,4) if gp1 != 1 or gp2 != 2 or gp3 != 3 or gp4 != 4 failure( "Indirect call impure" ) end success() /* end of file */ tests/core/testsuite/array_call1.fal000066400000000000000000000032301176363201700200630ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 60c * Category: functional * Subcategory: execarr * Short: Array OOP call * Description: * Test for callable arrays, base of falcon functional programming. * Here we use method instead of functions. * [/Description] * ****************************************************************************/ object theObject p1 = nil p2 = nil p3 = nil p4 = nil function theCall( p1, p2, p3, p4 ) self.p1 = p1 self.p2 = p2 self.p3 = p3 self.p4 = p4 end function reset() self.p1 = nil self.p2 = nil self.p3 = nil self.p4 = nil end function check() if self.p1 != 1 or self.p2 != 2 or self.p3 != 3 or self.p4 != 4 return false end return true end end // call array pure theObject.reset() [theObject.theCall, 1, 2, 3, 4]() if not theObject.check() failure( "Direct call pure" ) end theObject.reset() call = [theObject.theCall, 1, 2, 3, 4] call() if not theObject.check() failure( "Indirect call pure" ) end // call array spurious theObject.reset() [theObject.theCall, 1, 2](3,4) if not theObject.check() failure( "Direct call spurious" ) end theObject.reset() call = [theObject.theCall, 1, 2 ] call(3,4) if not theObject.check() failure( "Indirect call spurious" ) end // call array impure theObject.reset() [theObject.theCall](1,2,3,4) if not theObject.check() failure( "Direct call impure" ) end theObject.reset() call = [theObject.theCall] call(1,2,3,4) if not theObject.check() failure( "Indirect call impure" ) end success() /* end of file */ tests/core/testsuite/array_call_err.fal000066400000000000000000000017211176363201700206550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 60d * Category: functional * Subcategory: execarr * Short: Array call error detection * Description: * Test for callable arrays, base of falcon functional programming. * Check for error detection while calling arrays. * [/Description] * ****************************************************************************/ try [1,2,3,4]() failure("Direct uncallable" ) catch TypeError // ok catch in error failure( "Direct uncallable: " + error.toString() ) end try c = [1,2,3,4] c() failure("Indirect uncallable" ) catch TypeError // ok catch in error failure( "Indirect uncallable: " + error.toString() ) end function test() raise "Something" end try c = [test] test() failure("Expicit raise uncaught" ) catch StringType // ok catch in error failure( "Expicit raise: " + error.toString() ) end success() /* End of file */ tests/core/testsuite/array_mass.fal000066400000000000000000000746751176363201700200570ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 11g * Category: types * Subcategory: array * Short: Massive array * Author: Jeremy Cowgar * Description: * Checks for massive array creation using VM operators. * This test may either crash or work. * [/Description] * ****************************************************************************/ // start array on line 20 so that we can count the items array = [ \ [ nil, "ST", nil ], [ nil, "BHT", nil ], [ nil, "REF", "87" ], [ "1000A", "NM1", "41" ], [ "1000A", "PER", "IC" ], [ "1000B", "NM1", "40" ], [ "2000A", "HL", nil ], [ "2000A", "PRV", "BI" ], [ "2000A", "PRV", "PT" ], [ "2000A", "CUR", "85" ], [ "2010AA", "NM1", "85" ], [ "2010AA", "N3", nil ], [ "2010AA", "N4", nil ], [ "2010AA", "REF", "0B" ], [ "2010AA", "REF", "1A" ], [ "2010AA", "REF", "1B" ], [ "2010AA", "REF", "1C" ], [ "2010AA", "REF", "1D" ], [ "2010AA", "REF", "1G" ], [ "2010AA", "REF", "1H" ], [ "2010AA", "REF", "1J" ], [ "2010AA", "REF", "B3" ], [ "2010AA", "REF", "BQ" ], [ "2010AA", "REF", "EI" ], [ "2010AA", "REF", "FH" ], [ "2010AA", "REF", "G2" ], [ "2010AA", "REF", "G5" ], [ "2010AA", "REF", "LU" ], [ "2010AA", "REF", "SY" ], [ "2010AA", "REF", "X5" ], [ "2010AA", "REF", "06" ], [ "2010AA", "REF", "8U" ], [ "2010AA", "REF", "EM" ], [ "2010AA", "REF", "IJ" ], [ "2010AA", "REF", "LU" ], [ "2010AA", "REF", "RB" ], [ "2010AA", "REF", "ST" ], [ "2010AA", "REF", "TT" ], [ "2010AA", "PER", "IC" ], [ "2010AB", "NM1", "87" ], [ "2010AB", "N3", nil ], [ "2010AB", "N4", nil ], [ "2010AB", "REF", "0B" ], [ "2010AB", "REF", "1A" ], [ "2010AB", "REF", "1B" ], [ "2010AB", "REF", "1C" ], [ "2010AB", "REF", "1D" ], [ "2010AB", "REF", "1G" ], [ "2010AB", "REF", "1H" ], [ "2010AB", "REF", "1J" ], [ "2010AB", "REF", "B3" ], [ "2010AB", "REF", "BQ" ], [ "2010AB", "REF", "EI" ], [ "2010AB", "REF", "FH" ], [ "2010AB", "REF", "G2" ], [ "2010AB", "REF", "G5" ], [ "2010AB", "REF", "LU" ], [ "2010AB", "REF", "SY" ], [ "2010AB", "REF", "X5" ], [ "2000B", "HL", nil ], [ "2000B", "SBR", nil ], [ "2000B", "PAT", nil ], [ "2010BA", "NM1", "IL" ], [ "2010BA", "N3", nil ], [ "2010BA", "N4", nil ], [ "2010BA", "DMG", "D8" ], [ "2010BA", "REF", "1W" ], [ "2010BA", "REF", "23" ], [ "2010BA", "REF", "IG" ], [ "2010BA", "REF", "SY" ], [ "2010BA", "REF", "Y4" ], [ "2010BB", "NM1", "AO" ], [ "2010BB", "REF", "AB" ], [ "2010BB", "REF", "BB" ], [ "2010BC", "NM1", "PR" ], [ "2010BC", "N3", nil ], [ "2010BC", "N4", nil ], [ "2010BC", "REF", "2U" ], [ "2010BC", "REF", "FY" ], [ "2010BC", "REF", "NF" ], [ "2010BC", "REF", "TJ" ], [ "2010BD", "NM1", "QD" ], [ "2010BD", "N3", nil ], [ "2010BD", "N4", nil ], [ "2000C", "HL", nil ], [ "2000C", "PAT", nil ], [ "2010CA", "NM1", "QC" ], [ "2010CA", "N3", nil ], [ "2010CA", "N4", nil ], [ "2010CA", "DMG", "D8" ], [ "2010CA", "REF", "1W" ], [ "2010CA", "REF", "23" ], [ "2010CA", "REF", "IG" ], [ "2010CA", "REF", "SY" ], [ "2010CA", "REF", "Y4" ], [ "2300", "CLM", nil ], [ "2300", "DTP", "096" ], [ "2300", "DTP", "434" ], [ "2300", "DTP", "435" ], [ "2300", "CL1", nil ], [ "2300", "PWK", "AS" ], [ "2300", "PWK", "B2" ], [ "2300", "PWK", "B3" ], [ "2300", "PWK", "B4" ], [ "2300", "PWK", "CT" ], [ "2300", "PWK", "DA" ], [ "2300", "PWK", "DG" ], [ "2300", "PWK", "DS" ], [ "2300", "PWK", "EB" ], [ "2300", "PWK", "MT" ], [ "2300", "PWK", "NN" ], [ "2300", "PWK", "OB" ], [ "2300", "PWK", "OZ" ], [ "2300", "PWK", "PN" ], [ "2300", "PWK", "PO" ], [ "2300", "PWK", "PZ" ], [ "2300", "PWK", "RB" ], [ "2300", "PWK", "RR" ], [ "2300", "PWK", "RT" ], [ "2300", "CN1", "01" ], [ "2300", "CN1", "02" ], [ "2300", "CN1", "03" ], [ "2300", "CN1", "04" ], [ "2300", "CN1", "05" ], [ "2300", "CN1", "06" ], [ "2300", "CN1", "09" ], [ "2300", "AMT", "C5" ], [ "2300", "AMT", "F3" ], [ "2300", "AMT", "F5" ], [ "2300", "AMT", "MA" ], [ "2300", "REF", "9C" ], [ "2300", "REF", "9A" ], [ "2300", "REF", "D9" ], [ "2300", "REF", "DD" ], [ "2300", "REF", "F8" ], [ "2300", "REF", "LX" ], [ "2300", "REF", "4N" ], [ "2300", "REF", "G4" ], [ "2300", "REF", "9F" ], [ "2300", "REF", "G1" ], [ "2300", "REF", "EA" ], [ "2300", "REF", "P4" ], [ "2300", "K3", nil ], [ "2300", "NTE", "ALG" ], [ "2300", "NTE", "DCP" ], [ "2300", "NTE", "DGN" ], [ "2300", "NTE", "DME" ], [ "2300", "NTE", "MED" ], [ "2300", "NTE", "NTR" ], [ "2300", "NTE", "ODT" ], [ "2300", "NTE", "RHB" ], [ "2300", "NTE", "RLH" ], [ "2300", "NTE", "RNH" ], [ "2300", "NTE", "SET" ], [ "2300", "NTE", "SFM" ], [ "2300", "NTE", "SPT" ], [ "2300", "NTE", "UPI" ], [ "2300", "NTE", "ADD" ], [ "2300", "CR6", "1" ], [ "2300", "CR6", "2" ], [ "2300", "CR6", "3" ], [ "2300", "CR6", "4" ], [ "2300", "CR6", "5" ], [ "2300", "CR6", "6" ], [ "2300", "CR6", "7" ], [ "2300", "CR6", "8" ], [ "2300", "CRC", "75" ], [ "2300", "CRC", "76" ], [ "2300", "CRC", "77" ], [ "2300", "HI", nil ], [ "2300", "QTY", "CA" ], [ "2300", "QTY", "CD" ], [ "2300", "QTY", "LA" ], [ "2300", "QTY", "NA" ], [ "2300", "HCP", nil ], [ "2305", "CR7", "AI" ], [ "2305", "CR7", "MS" ], [ "2305", "CR7", "OT" ], [ "2305", "CR7", "PT" ], [ "2305", "CR7", "SN" ], [ "2305", "CR7", "ST" ], [ "2305", "HSD", "VS" ], [ "2310A", "NM1", "71" ], [ "2310A", "PRV", "AT" ], [ "2310A", "PRV", "SU" ], [ "2310A", "REF", "0B" ], [ "2310A", "REF", "1A" ], [ "2310A", "REF", "1B" ], [ "2310A", "REF", "1C" ], [ "2310A", "REF", "1D" ], [ "2310A", "REF", "1G" ], [ "2310A", "REF", "1H" ], [ "2310A", "REF", "EI" ], [ "2310A", "REF", "G2" ], [ "2310A", "REF", "LU" ], [ "2310A", "REF", "N5" ], [ "2310A", "REF", "SY" ], [ "2310A", "REF", "X5" ], [ "2310B", "NM1", "72" ], [ "2310B", "PRV", "OP" ], [ "2310B", "REF", "0B" ], [ "2310B", "REF", "1A" ], [ "2310B", "REF", "1B" ], [ "2310B", "REF", "1C" ], [ "2310B", "REF", "1D" ], [ "2310B", "REF", "1G" ], [ "2310B", "REF", "1H" ], [ "2310B", "REF", "EI" ], [ "2310B", "REF", "G2" ], [ "2310B", "REF", "LU" ], [ "2310B", "REF", "N5" ], [ "2310B", "REF", "SY" ], [ "2310B", "REF", "X5" ], [ "2310C", "NM1", "73" ], [ "2310C", "PRV", "OT" ], [ "2310C", "PRV", "PE" ], [ "2310C", "REF", "0B" ], [ "2310C", "REF", "1A" ], [ "2310C", "REF", "1B" ], [ "2310C", "REF", "1C" ], [ "2310C", "REF", "1D" ], [ "2310C", "REF", "1G" ], [ "2310C", "REF", "1H" ], [ "2310C", "REF", "EI" ], [ "2310C", "REF", "G2" ], [ "2310C", "REF", "LU" ], [ "2310C", "REF", "N5" ], [ "2310C", "REF", "SY" ], [ "2310C", "REF", "X5" ], [ "2310D", "NM1", "DN" ], [ "2310D", "NM1", "P3" ], [ "2310D", "PRV", "RF" ], [ "2310D", "REF", "0B" ], [ "2310D", "REF", "1A" ], [ "2310D", "REF", "1B" ], [ "2310D", "REF", "1C" ], [ "2310D", "REF", "1D" ], [ "2310D", "REF", "1G" ], [ "2310D", "REF", "B3" ], [ "2310D", "REF", "BQ" ], [ "2310D", "REF", "EI" ], [ "2310D", "REF", "G2" ], [ "2310D", "REF", "LU" ], [ "2310D", "REF", "N5" ], [ "2310D", "REF", "SY" ], [ "2310D", "REF", "X5" ], [ "2310E", "NM1", "FA" ], [ "2310E", "PRV", "RP" ], [ "2310E", "N3", nil ], [ "2310E", "N4", nil ], [ "2310E", "REF", "0B" ], [ "2310E", "REF", "1A" ], [ "2310E", "REF", "1B" ], [ "2310E", "REF", "1C" ], [ "2310E", "REF", "1D" ], [ "2310E", "REF", "1G" ], [ "2310E", "REF", "1H" ], [ "2310E", "REF", "1J" ], [ "2310E", "REF", "EI" ], [ "2310E", "REF", "FH" ], [ "2310E", "REF", "G2" ], [ "2310E", "REF", "G5" ], [ "2310E", "REF", "LU" ], [ "2310E", "REF", "N5" ], [ "2310E", "REF", "X5" ], [ "2320", "SBR", nil ], [ "2320", "CAS", "CO" ], [ "2320", "CAS", "CR" ], [ "2320", "CAS", "OA" ], [ "2320", "CAS", "PI" ], [ "2320", "CAS", "PR" ], [ "2320", "AMT", "C4" ], [ "2320", "AMT", "T3" ], [ "2320", "AMT", "N1" ], [ "2320", "AMT", "KF" ], [ "2320", "AMT", "PG" ], [ "2320", "AMT", "AA" ], [ "2320", "AMT", "B1" ], [ "2320", "AMT", "A8" ], [ "2320", "AMT", "YT" ], [ "2320", "DMG", "D8" ], [ "2320", "OI", nil ], [ "2320", "MIA", nil ], [ "2320", "MOA", nil ], [ "2330A", "NM1", "IL" ], [ "2330A", "N3", nil ], [ "2330A", "N4", nil ], [ "2330A", "REF", "1W" ], [ "2330A", "REF", "23" ], [ "2330A", "REF", "IG" ], [ "2330A", "REF", "SY" ], [ "2330B", "NM1", "PR" ], [ "2330B", "N3", nil ], [ "2330B", "N4", nil ], [ "2330B", "DTP", "573" ], [ "2330B", "REF", "2U" ], [ "2330B", "REF", "F8" ], [ "2330B", "REF", "FY" ], [ "2330B", "REF", "NF" ], [ "2330B", "REF", "TJ" ], [ "2330B", "REF", "9F" ], [ "2330B", "REF", "G1" ], [ "2330C", "NM1", "QC" ], [ "2330C", "REF", "1W" ], [ "2330C", "REF", "IG" ], [ "2330C", "REF", "SY" ], [ "2330D", "NM1", "71" ], [ "2330D", "REF", "1A" ], [ "2330D", "REF", "1B" ], [ "2330D", "REF", "1C" ], [ "2330D", "REF", "1D" ], [ "2330D", "REF", "1G" ], [ "2330D", "REF", "1H" ], [ "2330D", "REF", "EI" ], [ "2330D", "REF", "G2" ], [ "2330D", "REF", "LU" ], [ "2330D", "REF", "N5" ], [ "2330E", "NM1", "72" ], [ "2330E", "REF", "1A" ], [ "2330E", "REF", "1B" ], [ "2330E", "REF", "1C" ], [ "2330E", "REF", "1D" ], [ "2330E", "REF", "1G" ], [ "2330E", "REF", "1H" ], [ "2330E", "REF", "EI" ], [ "2330E", "REF", "G2" ], [ "2330E", "REF", "LU" ], [ "2330E", "REF", "N5" ], [ "2330F", "NM1", "73" ], [ "2330F", "REF", "1A" ], [ "2330F", "REF", "1B" ], [ "2330F", "REF", "1C" ], [ "2330F", "REF", "1D" ], [ "2330F", "REF", "1G" ], [ "2330F", "REF", "1H" ], [ "2330F", "REF", "EI" ], [ "2330F", "REF", "G2" ], [ "2330F", "REF", "LU" ], [ "2330F", "REF", "N5" ], [ "2330F", "REF", "SY" ], [ "2330G", "NM1", "DN" ], [ "2330G", "REF", "1A" ], [ "2330G", "REF", "1B" ], [ "2330G", "REF", "1C" ], [ "2330G", "REF", "1D" ], [ "2330G", "REF", "1G" ], [ "2330G", "REF", "B3" ], [ "2330G", "REF", "BQ" ], [ "2330G", "REF", "EI" ], [ "2330G", "REF", "G2" ], [ "2330G", "REF", "LU" ], [ "2330G", "REF", "N5" ], [ "2330G", "REF", "SY" ], [ "2330G", "REF", "X5" ], [ "2330H", "NM1", "FA" ], [ "2330H", "REF", "1B" ], [ "2330H", "REF", "1C" ], [ "2330H", "REF", "1D" ], [ "2330H", "REF", "EI" ], [ "2330H", "REF", "G2" ], [ "2330H", "REF", "LU" ], [ "2330H", "REF", "N5" ], [ "2400", "LX", nil ], [ "2400", "SV1", nil ], [ "2400", "SV2", nil ], [ "2400", "SV3", nil ], [ "2400", "TOO", nil ], [ "2400", "SV4", nil ], [ "2400", "SV5", nil ], [ "2400", "SV6", nil ], [ "2400", "SV7", nil ], [ "2400", "HI", nil ], [ "2400", "PWK", "AS" ], [ "2400", "PWK", "B2" ], [ "2400", "PWK", "B3" ], [ "2400", "PWK", "B4" ], [ "2400", "PWK", "CT" ], [ "2400", "PWK", "DA" ], [ "2400", "PWK", "DG" ], [ "2400", "PWK", "DS" ], [ "2400", "PWK", "EB" ], [ "2400", "PWK", "MT" ], [ "2400", "PWK", "NN" ], [ "2400", "PWK", "OB" ], [ "2400", "PWK", "OZ" ], [ "2400", "PWK", "PN" ], [ "2400", "PWK", "PO" ], [ "2400", "PWK", "PZ" ], [ "2400", "PWK", "RB" ], [ "2400", "PWK", "RR" ], [ "2400", "PWK", "RT" ], [ "2400", "CR1", nil ], [ "2400", "CR2", nil ], [ "2400", "CR3", nil ], [ "2400", "CR4", nil ], [ "2400", "CR5", nil ], [ "2400", "CRC", nil ], [ "2400", "DTP", "472" ], [ "2400", "DTP", "866" ], [ "2400", "QTY", nil ], [ "2400", "MEA", nil ], [ "2400", "CN1", nil ], [ "2400", "REF", nil ], [ "2400", "AMT", "GT" ], [ "2400", "AMT", "N8" ], [ "2400", "K3", nil ], [ "2400", "NTE", nil ], [ "2400", "PS1", nil ], [ "2400", "IMM", nil ], [ "2400", "HSD", nil ], [ "2400", "HCP", nil ], [ "2410", "LIN", nil ], [ "2410", "CTP", nil ], [ "2410", "REF", "XZ" ], [ "2420A", "NM1", "71" ], [ "2420A", "PRV", "AT" ], [ "2420A", "REF", "0B" ], [ "2420A", "REF", "1A" ], [ "2420A", "REF", "1B" ], [ "2420A", "REF", "1C" ], [ "2420A", "REF", "1D" ], [ "2420A", "REF", "1G" ], [ "2420A", "REF", "1H" ], [ "2420A", "REF", "EI" ], [ "2420A", "REF", "G2" ], [ "2420A", "REF", "LU" ], [ "2420A", "REF", "N5" ], [ "2420A", "REF", "SY" ], [ "2420A", "REF", "X5" ], [ "2420B", "NM1", "72" ], [ "2420B", "PEV", "OP" ], [ "2420B", "REF", "0B" ], [ "2420B", "REF", "1A" ], [ "2420B", "REF", "1B" ], [ "2420B", "REF", "1C" ], [ "2420B", "REF", "1D" ], [ "2420B", "REF", "1G" ], [ "2420B", "REF", "1H" ], [ "2420B", "REF", "EI" ], [ "2420B", "REF", "G2" ], [ "2420B", "REF", "LU" ], [ "2420B", "REF", "N5" ], [ "2420B", "REF", "SY" ], [ "2420B", "REF", "X5" ], [ "2420C", "NM1", "73" ], [ "2420C", "PRV", "OT" ], [ "2420C", "PRV", "PE" ], [ "2420C", "REF", "0B" ], [ "2420C", "REF", "1A" ], [ "2420C", "REF", "1B" ], [ "2420C", "REF", "1C" ], [ "2420C", "REF", "1D" ], [ "2420C", "REF", "1G" ], [ "2420C", "REF", "1H" ], [ "2420C", "REF", "EI" ], [ "2420C", "REF", "G2" ], [ "2420C", "REF", "LU" ], [ "2420C", "REF", "N5" ], [ "2420C", "REF", "SY" ], [ "2420C", "REF", "X5" ], [ "2420D", "NM1", "DN" ], [ "2420D", "PRV", "RF" ], [ "2420D", "REF", "0B" ], [ "2420D", "REF", "1A" ], [ "2420D", "REF", "1B" ], [ "2420D", "REF", "1C" ], [ "2420D", "REF", "1D" ], [ "2420D", "REF", "1G" ], [ "2420D", "REF", "B3" ], [ "2420D", "REF", "BQ" ], [ "2420D", "REF", "EI" ], [ "2420D", "REF", "G2" ], [ "2420D", "REF", "LU" ], [ "2420D", "REF", "N5" ], [ "2420D", "REF", "SY" ], [ "2420D", "REF", "X5" ], [ "2430", "SVD", nil ], [ "2430", "CAS", "CO" ], [ "2430", "CAS", "CR" ], [ "2430", "CAS", "OA" ], [ "2430", "CAS", "PI" ], [ "2430", "CAS", "PR" ], [ "2430", "DTP", "573" ], [ nil, "SE", nil ] ] // starting on line 510 array2 = [ \ [ nil, "ST", nil ], [ nil, "BHT", nil ], [ nil, "REF", "87" ], [ "1000A", "NM1", "41" ], [ "1000A", "N2", nil ], [ "1000A", "PER", "IC" ], [ "1000B", "NM1", "40" ], [ "1000B", "N2", nil ], [ "2000A", "HL", nil ], [ "2000A", "PRV", "BI" ], [ "2000A", "PRV", "PT" ], [ "2000A", "CUR", "85" ], [ "2010AA", "NM1", "85" ], [ "2010AA", "N2", nil ], [ "2010AA", "N3", nil ], [ "2010AA", "N4", nil ], [ "2010AA", "REF", "0B" ], [ "2010AA", "REF", "1A" ], [ "2010AA", "REF", "1B" ], [ "2010AA", "REF", "1C" ], [ "2010AA", "REF", "1D" ], [ "2010AA", "REF", "1G" ], [ "2010AA", "REF", "1H" ], [ "2010AA", "REF", "1J" ], [ "2010AA", "REF", "B3" ], [ "2010AA", "REF", "BQ" ], [ "2010AA", "REF", "EI" ], [ "2010AA", "REF", "FH" ], [ "2010AA", "REF", "G2" ], [ "2010AA", "REF", "G5" ], [ "2010AA", "REF", "LU" ], [ "2010AA", "REF", "SY" ], [ "2010AA", "REF", "U3" ], [ "2010AA", "REF", "X5" ], [ "2010AA", "REF", "06" ], [ "2010AA", "REF", "8U" ], [ "2010AA", "REF", "EM" ], [ "2010AA", "REF", "IJ" ], [ "2010AA", "REF", "LU" ], [ "2010AA", "REF", "RB" ], [ "2010AA", "REF", "ST" ], [ "2010AA", "REF", "TT" ], [ "2010AA", "PER", "IC" ], [ "2010AB", "NM1", "87" ], [ "2010AB", "N2", nil ], [ "2010AB", "N3", nil ], [ "2010AB", "N4", nil ], [ "2010AB", "REF", "0B" ], [ "2010AB", "REF", "1A" ], [ "2010AB", "REF", "1B" ], [ "2010AB", "REF", "1C" ], [ "2010AB", "REF", "1D" ], [ "2010AB", "REF", "1G" ], [ "2010AB", "REF", "1H" ], [ "2010AB", "REF", "1J" ], [ "2010AB", "REF", "B3" ], [ "2010AB", "REF", "BQ" ], [ "2010AB", "REF", "EI" ], [ "2010AB", "REF", "FH" ], [ "2010AB", "REF", "G2" ], [ "2010AB", "REF", "G5" ], [ "2010AB", "REF", "LU" ], [ "2010AB", "REF", "SK" ], [ "2010AB", "REF", "U3" ], [ "2010AB", "REF", "X5" ], [ "2000B", "HL", nil ], [ "2000B", "SBR", "P" ], [ "2000B", "SBR", "S" ], [ "2000B", "SBR", "T" ], [ "2000B", "PAT", nil ], [ "2010BA", "NM1", "IL" ], [ "2010BA", "N2", nil ], [ "2010BA", "N3", nil ], [ "2010BA", "N4", nil ], [ "2010BA", "DMG", nil ], [ "2010BA", "REF", "1W" ], [ "2010BA", "REF", "23" ], [ "2010BA", "REF", "IG" ], [ "2010BA", "REF", "SY" ], [ "2010BA", "REF", "Y4" ], [ "2010BB", "NM1", "PR" ], [ "2010BB", "N2", nil ], [ "2010BB", "N3", nil ], [ "2010BB", "N4", nil ], [ "2010BB", "REF", "2U" ], [ "2010BB", "REF", "FY" ], [ "2010BB", "REF", "NF" ], [ "2010BB", "REF", "TJ" ], [ "2010BC", "NM1", "QD" ], [ "2010BC", "N2", nil ], [ "2010BC", "N3", nil ], [ "2010BC", "N4", nil ], [ "2010BD", "NM1", "AO" ], [ "2010BD", "N2", nil ], [ "2010BD", "REF", "AB" ], [ "2010BD", "REF", "BB" ], [ "2000C", "HL", nil ], [ "2000C", "PAT", nil ], [ "2010CA", "NM1", "QC" ], [ "2010CA", "N2", nil ], [ "2010CA", "N3", nil ], [ "2010CA", "N4", nil ], [ "2010CA", "DMG", nil ], [ "2010CA", "REF", "1W" ], [ "2010CA", "REF", "23" ], [ "2010CA", "REF", "IG" ], [ "2010CA", "REF", "SY" ], [ "2010CA", "REF", "Y4" ], [ "2300", "CLM", nil ], [ "2300", "DTP", "938" ], [ "2300", "DTP", "454" ], [ "2300", "DTP", "330" ], [ "2300", "DTP", "304" ], [ "2300", "DTP", "431" ], [ "2300", "DTP", "453" ], [ "2300", "DTP", "438" ], [ "2300", "DTP", "439" ], [ "2300", "DTP", "484" ], [ "2300", "DTP", "455" ], [ "2300", "DTP", "ABC" ], [ "2300", "DTP", "471" ], [ "2300", "DTP", "360" ], [ "2300", "DTP", "361" ], [ "2300", "DTP", "297" ], [ "2300", "DTP", "296" ], [ "2300", "DTP", "435" ], [ "2300", "DTP", "098" ], [ "2300", "DTP", "090" ], [ "2300", "DTP", "091" ], [ "2300", "PWK", "77" ], [ "2300", "PWK", "AS" ], [ "2300", "PWK", "B2" ], [ "2300", "PWK", "B3" ], [ "2300", "PWK", "B4" ], [ "2300", "PWK", "CT" ], [ "2300", "PWK", "DA" ], [ "2300", "PWK", "DG" ], [ "2300", "PWK", "DS" ], [ "2300", "PWK", "EB" ], [ "2300", "PWK", "MT" ], [ "2300", "PWK", "MN" ], [ "2300", "PWK", "OB" ], [ "2300", "PWK", "OZ" ], [ "2300", "PWK", "PN" ], [ "2300", "PWK", "PO" ], [ "2300", "PWK", "PZ" ], [ "2300", "PWK", "RB" ], [ "2300", "PWK", "RR" ], [ "2300", "PWK", "RT" ], [ "2300", "CN1", "02" ], [ "2300", "CN1", "03" ], [ "2300", "CN1", "04" ], [ "2300", "CN1", "05" ], [ "2300", "CN1", "06" ], [ "2300", "CN1", "09" ], [ "2300", "AMT", "MA" ], [ "2300", "AMT", "F5" ], [ "2300", "AMT", "NE" ], [ "2300", "REF", "4N" ], [ "2300", "REF", "F5" ], [ "2300", "REF", "EW" ], [ "2300", "REF", "9F" ], [ "2300", "REF", "G1" ], [ "2300", "REF", "F8" ], [ "2300", "REF", "X4" ], [ "2300", "REF", "9A" ], [ "2300", "REF", "9C" ], [ "2300", "REF", "LX" ], [ "2300", "REF", "D9" ], [ "2300", "REF", "1S" ], [ "2300", "REF", "EA" ], [ "2300", "REF", "P4" ], [ "2300", "K3", nil ], [ "2300", "NTE", "ADD" ], [ "2300", "NTE", "CER" ], [ "2300", "NTE", "DCP" ], [ "2300", "NTE", "DGN" ], [ "2300", "NTE", "PMT" ], [ "2300", "NTE", "TPO" ], [ "2300", "CR1", "LB" ], [ "2300", "CR2", "CR2" ], [ "2300", "CRC", "07" ], [ "2300", "CRC", "E1" ], [ "2300", "CRC", "E2" ], [ "2300", "CRC", "E3" ], [ "2300", "CRC", "75" ], [ "2300", "CRC", "ZZ" ], [ "2300", "HI", nil ], [ "2300", "HCP", nil ], [ "2305", "CR7", "AI" ], [ "2305", "CR7", "MS" ], [ "2305", "CR7", "OT" ], [ "2305", "CR7", "PT" ], [ "2305", "CR7", "SN" ], [ "2305", "CR7", "ST" ], [ "2305", "HSD", "VS" ], [ "2310A", "NM1", "DN" ], [ "2310A", "NM1", "P3" ], [ "2310A", "PRV", "RF" ], [ "2310A", "N2", nil ], [ "2310A", "REF", "0B" ], [ "2310A", "REF", "1B" ], [ "2310A", "REF", "1C" ], [ "2310A", "REF", "1D" ], [ "2310A", "REF", "1G" ], [ "2310A", "REF", "1H" ], [ "2310A", "REF", "EI" ], [ "2310A", "REF", "G2" ], [ "2310A", "REF", "LU" ], [ "2310A", "REF", "N5" ], [ "2310A", "REF", "SY" ], [ "2310A", "REF", "X5" ], [ "2310B", "NM1", "82" ], [ "2310B", "PRV", "PE" ], [ "2310B", "N2", nil ], [ "2310B", "REF", "0B" ], [ "2310B", "REF", "1B" ], [ "2310B", "REF", "1C" ], [ "2310B", "REF", "1D" ], [ "2310B", "REF", "1G" ], [ "2310B", "REF", "1H" ], [ "2310B", "REF", "EI" ], [ "2310B", "REF", "G2" ], [ "2310B", "REF", "LU" ], [ "2310B", "REF", "N5" ], [ "2310B", "REF", "SY" ], [ "2310B", "REF", "X5" ], [ "2310C", "NM1", "QB" ], [ "2310C", "REF", "0B" ], [ "2310C", "REF", "1A" ], [ "2310C", "REF", "1B" ], [ "2310C", "REF", "1C" ], [ "2310C", "REF", "1D" ], [ "2310C", "REF", "1G" ], [ "2310C", "REF", "1H" ], [ "2310C", "REF", "E1" ], [ "2310C", "REF", "G2" ], [ "2310C", "REF", "LU" ], [ "2310C", "REF", "N5" ], [ "2310C", "REF", "SY" ], [ "2310C", "REF", "U3" ], [ "2310C", "REF", "X5" ], [ "2310D", "NM1", "77" ], [ "2310D", "NM1", "FA" ], [ "2310D", "NM1", "LI" ], [ "2310D", "NM1", "TL" ], [ "2310D", "N2", nil ], [ "2310D", "N3", nil ], [ "2310D", "N4", nil ], [ "2310D", "REF", "0B" ], [ "2310D", "REF", "1A" ], [ "2310D", "REF", "1B" ], [ "2310D", "REF", "1C" ], [ "2310D", "REF", "1D" ], [ "2310D", "REF", "1G" ], [ "2310D", "REF", "1H" ], [ "2310D", "REF", "G2" ], [ "2310D", "REF", "LU" ], [ "2310D", "REF", "N5" ], [ "2310D", "REF", "TJ" ], [ "2310D", "REF", "X4" ], [ "2310D", "REF", "X5" ], [ "2310E", "NM1", "DQ" ], [ "2310E", "N2", nil ], [ "2310E", "REF", "0B" ], [ "2310E", "REF", "1B" ], [ "2310E", "REF", "1C" ], [ "2310E", "REF", "1D" ], [ "2310E", "REF", "1G" ], [ "2310E", "REF", "1H" ], [ "2310E", "REF", "EI" ], [ "2310E", "REF", "G2" ], [ "2310E", "REF", "LU" ], [ "2310E", "REF", "N5" ], [ "2310E", "REF", "SY" ], [ "2310E", "REF", "X5" ], [ "2320", "SBR", "P" ], [ "2320", "SBR", "S" ], [ "2320", "SBR", "T" ], [ "2320", "CAS", "CO" ], [ "2320", "CAS", "CR" ], [ "2320", "CAS", "OA" ], [ "2320", "CAS", "PI" ], [ "2320", "CAS", "PR" ], [ "2320", "AMT", "D" ], [ "2320", "AMT", "AAE" ], [ "2320", "AMT", "B6" ], [ "2320", "AMT", "F2" ], [ "2320", "AMT", "AU" ], [ "2320", "AMT", "D8" ], [ "2320", "AMT", "DY" ], [ "2320", "AMT", "F5" ], [ "2320", "AMT", "T" ], [ "2320", "AMT", "T2" ], [ "2320", "DMG", "D8" ], [ "2320", "OI", nil ], [ "2320", "MOA", nil ], [ "2330A", "NM1", "IL" ], [ "2330A", "N2", nil ], [ "2330A", "N3", nil ], [ "2330A", "N4", nil ], [ "2330A", "REF", "1W" ], [ "2330A", "REF", "23" ], [ "2330A", "REF", "IG" ], [ "2330A", "REF", "SY" ], [ "2330B", "NM1", "PR" ], [ "2330B", "N2", nil ], [ "2330B", "PER", "IC" ], [ "2330B", "DTP", "573" ], [ "2330B", "REF", "2U" ], [ "2330B", "REF", "F8" ], [ "2330B", "REF", "FY" ], [ "2330B", "REF", "NF" ], [ "2330B", "REF", "TJ" ], [ "2330B", "REF", "9F" ], [ "2330B", "REF", "G1" ], [ "2330B", "REF", "T4" ], [ "2330C", "NM1", "QC" ], [ "2330C", "REF", "1W" ], [ "2330C", "REF", "23" ], [ "2330C", "REF", "IG" ], [ "2330C", "REF", "SY" ], [ "2330D", "NM1", "DN" ], [ "2330D", "NM1", "P3" ], [ "2330D", "REF", "1B" ], [ "2330D", "REF", "1C" ], [ "2330D", "REF", "1D" ], [ "2330D", "REF", "EI" ], [ "2330D", "REF", "G2" ], [ "2330D", "REF", "LU" ], [ "2330D", "REF", "N5" ], [ "2330E", "NM1", "82" ], [ "2330E", "REF", "1B" ], [ "2330E", "REF", "1C" ], [ "2330E", "REF", "1D" ], [ "2330E", "REF", "EI" ], [ "2330E", "REF", "G2" ], [ "2330E", "REF", "LU" ], [ "2330E", "REF", "N5" ], [ "2330F", "NM1", "QB" ], [ "2330F", "REF", "1B" ], [ "2330F", "REF", "1C" ], [ "2330F", "REF", "1D" ], [ "2330F", "REF", "EI" ], [ "2330F", "REF", "G2" ], [ "2330F", "REF", "LU" ], [ "2330F", "REF", "N5" ], [ "2330G", "NM1", "77" ], [ "2330G", "NM1", "FA" ], [ "2330G", "NM1", "LI" ], [ "2330G", "NM1", "TL" ], [ "2330G", "REF", "1A" ], [ "2330G", "REF", "1B" ], [ "2330G", "REF", "1C" ], [ "2330G", "REF", "1D" ], [ "2330G", "REF", "G2" ], [ "2330G", "REF", "LU" ], [ "2330G", "REF", "N5" ], [ "2330H", "NM1", "DQ" ], [ "2330H", "REF", "1B" ], [ "2330H", "REF", "1C" ], [ "2330H", "REF", "1D" ], [ "2330H", "REF", "EI" ], [ "2330H", "REF", "G2" ], [ "2330H", "REF", "N5" ], [ "2400", "LX", nil ], [ "2400", "SV1", nil ], [ "2400", "SV4", nil ], [ "2400", "PWK", "CT" ], [ "2400", "CR1", "LB" ], [ "2400", "CR2", nil ], [ "2400", "CR3", "I" ], [ "2400", "CR3", "R" ], [ "2400", "CR3", "S" ], [ "2400", "CR5", "I" ], [ "2400", "CR5", "R" ], [ "2400", "CR5", "S" ], [ "2400", "CRC", "07" ], [ "2400", "CRC", "70" ], [ "2400", "CRC", "09" ], [ "2400", "CRC", "11" ], [ "2400", "DTP", "472" ], [ "2400", "DTP", "607" ], [ "2400", "DTP", "374" ], [ "2400", "DTP", "330" ], [ "2400", "DTP", "374" ], [ "2400", "DTP", "461" ], [ "2400", "DTP", "938" ], [ "2400", "DTP", "304" ], [ "2400", "DTP", "738" ], [ "2400", "DTP", "739" ], [ "2400", "DTP", "119" ], [ "2400", "DTP", "480" ], [ "2400", "DTP", "481" ], [ "2400", "DTP", "011" ], [ "2400", "DTP", "431" ], [ "2400", "DTP", "455" ], [ "2400", "DTP", "453" ], [ "2400", "DTP", "454" ], [ "2400", "DTP", "438" ], [ "2400", "QTY", "BF" ], [ "2400", "QTY", "EC" ], [ "2400", "QTY", "EM" ], [ "2400", "QTY", "HM" ], [ "2400", "QTY", "HO" ], [ "2400", "QTY", "HP" ], [ "2400", "QTY", "P3" ], [ "2400", "QTY", "P4" ], [ "2400", "QTY", "P5" ], [ "2400", "QTY", "SG" ], [ "2400", "MEA", "OG" ], [ "2400", "MEA", "TR" ], [ "2400", "CN1", "01" ], [ "2400", "CN1", "02" ], [ "2400", "CN1", "03" ], [ "2400", "CN1", "04" ], [ "2400", "CN1", "05" ], [ "2400", "CN1", "06" ], [ "2400", "CN1", "09" ], [ "2400", "REF", "9B" ], [ "2400", "REF", "9D" ], [ "2400", "REF", "9F" ], [ "2400", "REF", "G1" ], [ "2400", "REF", "6R" ], [ "2400", "REF", "EW" ], [ "2400", "REF", "X4" ], [ "2400", "REF", "F4" ], [ "2400", "REF", "BT" ], [ "2400", "REF", "1S" ], [ "2400", "REF", "TP" ], [ "2400", "REF", "OZ" ], [ "2400", "REF", "VP" ], [ "2400", "AMT", "T" ], [ "2400", "AMT", "AAE" ], [ "2400", "AMT", "F4" ], [ "2400", "K3", nil ], [ "2400", "NTE", "ADD" ], [ "2400", "NTE", "DCP" ], [ "2400", "NTE", "PMT" ], [ "2400", "NTE", "TPO" ], [ "2400", "PS1", "PS1" ], [ "2400", "HSD", "VS" ], [ "2400", "HCP", nil ], [ "2410", "LIN", nil ], [ "2410", "CTP", nil ], [ "2410", "REF", "XZ" ], [ "2420A", "NM1", "82" ], [ "2420A", "PRV", "PE" ], [ "2420A", "N2", nil ], [ "2420A", "REF", "0B" ], [ "2420A", "REF", "1B" ], [ "2420A", "REF", "1C" ], [ "2420A", "REF", "1D" ], [ "2420A", "REF", "1G" ], [ "2420A", "REF", "1H" ], [ "2420A", "REF", "EI" ], [ "2420A", "REF", "G2" ], [ "2420A", "REF", "LU" ], [ "2420A", "REF", "N5" ], [ "2420A", "REF", "SY" ], [ "2420A", "REF", "X5" ], [ "2420B", "NM1", "QB" ], [ "2420B", "REF", "0B" ], [ "2420B", "REF", "1A" ], [ "2420B", "REF", "1B" ], [ "2420B", "REF", "1C" ], [ "2420B", "REF", "1D" ], [ "2420B", "REF", "1G" ], [ "2420B", "REF", "1H" ], [ "2420B", "REF", "EI" ], [ "2420B", "REF", "G2" ], [ "2420B", "REF", "LU" ], [ "2420B", "REF", "N5" ], [ "2420B", "REF", "SY" ], [ "2420B", "REF", "U3" ], [ "2420B", "REF", "X5" ], [ "2420C", "NM1", "77" ], [ "2420C", "NM1", "FA" ], [ "2420C", "NM1", "LI" ], [ "2420C", "NM1", "TL" ], [ "2420C", "N2", nil ], [ "2420C", "N3", nil ], [ "2420C", "N4", nil ], [ "2420C", "REF", "0B" ], [ "2420C", "REF", "1A" ], [ "2420C", "REF", "1B" ], [ "2420C", "REF", "1C" ], [ "2420C", "REF", "1D" ], [ "2420C", "REF", "1G" ], [ "2420C", "REF", "1H" ], [ "2420C", "REF", "G2" ], [ "2420C", "REF", "LU" ], [ "2420C", "REF", "N5" ], [ "2420C", "REF", "TJ" ], [ "2420C", "REF", "X4" ], [ "2420C", "REF", "X5" ], [ "2420D", "NM1", "DQ" ], [ "2420D", "N2", nil ], [ "2420D", "REF", "0B" ], [ "2420D", "REF", "1B" ], [ "2420D", "REF", "1C" ], [ "2420D", "REF", "1D" ], [ "2420D", "REF", "1G" ], [ "2420D", "REF", "1H" ], [ "2420D", "REF", "EI" ], [ "2420D", "REF", "G2" ], [ "2420D", "REF", "LU" ], [ "2420D", "REF", "N5" ], [ "2420D", "REF", "SY" ], [ "2420D", "REF", "X5" ], [ "2420E", "NM1", "DK" ], [ "2420E", "N2", nil ], [ "2420E", "N3", nil ], [ "2420E", "N4", nil ], [ "2420E", "REF", "0B" ], [ "2420E", "REF", "1B" ], [ "2420E", "REF", "1C" ], [ "2420E", "REF", "1D" ], [ "2420E", "REF", "1G" ], [ "2420E", "REF", "1H" ], [ "2420E", "REF", "EI" ], [ "2420E", "REF", "G2" ], [ "2420E", "REF", "LU" ], [ "2420E", "REF", "N5" ], [ "2420E", "REF", "SY" ], [ "2420E", "REF", "X5" ], [ "2420E", "PER", "IC" ], [ "2420F", "NM1", "DN" ], [ "2420F", "NM1", "P3" ], [ "2420F", "PRV", "RF" ], [ "2420F", "N2", nil ], [ "2420F", "REF", "0B" ], [ "2420F", "REF", "1B" ], [ "2420F", "REF", "1C" ], [ "2420F", "REF", "1D" ], [ "2420F", "REF", "1G" ], [ "2420F", "REF", "1H" ], [ "2420F", "REF", "EI" ], [ "2420F", "REF", "G2" ], [ "2420F", "REF", "LU" ], [ "2420F", "REF", "N5" ], [ "2420F", "REF", "SY" ], [ "2420F", "REF", "X5" ], [ "2420G", "NM1", "PR" ], [ "2420G", "REF", "9F" ], [ "2420G", "REF", "G1" ], [ "2430", "SVD", nil ], [ "2430", "CAS", nil ], [ "2430", "CAS", nil ], [ "2430", "CAS", nil ], [ "2430", "CAS", nil ], [ "2430", "CAS", nil ], [ "2430", "DTP", "573" ], [ "2440", "LQ", "AS" ], [ "2440", "LQ", "UT" ], [ "2440", "FRM", nil ], [ nil, "SE", nil ] ] // if we get here, the array creation worked. if array.len() != 484: failure( "Array 1 - size" ) if array[-1][1] != "SE": failure( "Array 1 - Last element" ) if array[-2][0] != "2430": failure( "Array 1 - Second last element 1" ) if array[-2][1] != "DTP": failure( "Array 1 - Second last element 2" ) if array[0][1] != "ST": failure( "Array 1 - First element" ) if array[300][0] != "2330B": failure( "Array 1 - Middle element" ) if array2.len() != 557: failure( "Array 2 - size" ) if array2[-1][1] != "SE": failure( "Array 2 - Last element" ) if array2[-2][0] != "2440": failure( "Array 2 - Second last element 1" ) if array2[-2][1] != "FRM": failure( "Array 2 - Second last element 2" ) if array2[0][1] != "ST": failure( "Array 2 - First element" ) if array2[300][1] != "N4": failure( "Array 2 - Middle element" ) success() /* End of file */ tests/core/testsuite/array_sub.fal000066400000000000000000000027151176363201700176670ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 11h * Category: types * Subcategory: array * Short: Subtract array * Description: * Checks working of subtract operator on arrays * [/Description] * ****************************************************************************/ // assignment test - include a compilation test arr = [ 2, 0, 1, "alpha", "beta" ] // remove an item arr1 = arr - "beta" if arr1.len() != 4: failure( "remove one item - size" ) if "beta" in arr1 : failure( "remove one item - content" ) // remove an array arr2 = arr - [2, "alpha"] if arr2.len() != 3: failure( "remove array - size" ) if 2 in arr2 or "alpha" in arr2: failure( "remove array - content" ) // invariance if 2 notin arr or "alpha" notin arr or "beta" notin arr failure( "remove invariance" ) end // now for self operators arr_copy = arr arr -= "beta" if arr.len() != 4: failure( "self remove one item - size" ) if "beta" in arr : failure( "self remove one item - content" ) if "beta" in arr_copy: failure( "self remove - consistency" ) arr -= "beta" if arr.len() != 4: failure( "self remove one item again - size" ) if "beta" in arr : failure( "self remove one item again - content" ) // remove an array arr -= [2, "alpha", "beta" ] // try also to remove an item removed if arr.len() != 2: failure( "self remove array - size" ) if 2 in arr or "alpha" in arr: failure( "self remove array - content" ) success() /* End of file */ tests/core/testsuite/arrayexpand.fal000066400000000000000000000025471176363201700202210ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 11f * Category: types * Subcategory: array * Short: Array unpack * Description: * Checks array unpacking * Using only integers in this test * [/Description] * ****************************************************************************/ // assignment test - include a compilation test function returnArray() var1, var2, var3 = ["a",2, [3:4]] if var1 != "a": failure( "first item direct" ) if var2 != 2: failure( "second item direct" ) if var3 != [3:4]: failure( "third item direct" ) array = ["b",3, [3:4]] var1, var2, var3 = array if var1 != "b": failure( "first item semi-direct" ) if var2 != 3: failure( "second item semi-direct" ) if var3 != [3:4]: failure( "third item semi-direct" ) return ["c",4, [3:4]] end function receiveArray() var1, var2, var3 = returnArray() if var1 != "c": failure( "first item indirect" ) if var2 != 4: failure( "second item indirect" ) if var3 != [3:4]: failure( "third item indirect" ) end try a = ['a', 'b' ] var1, var2, var3 = a failure( "Exception in delayed unpacking not raised - 2" ) catch end try a = ['a', 'b', 'c', 'd'] var1, var2, var3 = a failure( "Exception in delayed unpacking not raised - 2" ) catch end receiveArray() success() /* end of arrayexpand.fal */ tests/core/testsuite/arrcall_deep.fal000066400000000000000000000014521176363201700203120ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 60f * Category: functional * Subcategory: execarr * Short: Executable arrays deep * Description: * Deep functional calls using arrays. * [/Description] * ****************************************************************************/ function f(a,b) global first, second first = a second = b end function recall( func, a, b ) return func(a,b) end deep = .[recall f] // deep 1 - calling function of function deep( "first", "second" ) if first != "first" or second != "second": failure( "deep func" ) // deep2 - calling an array from an array deep = .[recall .[f]] deep( "first", "second" ) if first != "first" or second != "second": failure( "deep array" ) success() /* End of file */ tests/core/testsuite/arrstrings.fal000066400000000000000000000021141176363201700200670ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 11i * Category: types * Subcategory: array * Short: String copy in arrays * Description: * Checks if deep array assignment actually copy the string by value. * [/Description] * ****************************************************************************/ array = [nil] // setting string = "0000" array[0] = string string[0] = "1" if array[0] != "0000": failure( "Array write access" ) // self addition string = "0000" array_copy = array array += string string[0] = "1" if array[1] != "0000": failure( "array self append" ) if "0000" notin array_copy: failure( "Self append consistency" ) // normal addition string = "0000" array = ["0"] + string string[0] = "1" if array[1] != "0000": failure( "array + string" ) // array range string = "0000" array[0:0] = string string[0] = "1" if array[0] != "0000": failure( "array range insert" ) // array range string = "0000" array[0:1] = string string[0] = "1" if array[0] != "0000": failure( "array range change" ) success() /* End of file */ tests/core/testsuite/arrsubs.fal000066400000000000000000000050161176363201700173560ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 11j * Category: types * Subcategory: array * Short: Ranged subscripts. * Description: * Check ranged subscription access to array. * This check is complete of positive and negative ranged tests. * [/Description] * ****************************************************************************/ array = [ 0, 1, 2, 3, 4, 5, 6, 7 ] // get an empty range a1 = array[0:0] if len( a1 ) != 0: failure( "Get Empty range" ) // Get whole open range. a1 = array[0:] if len( a1 ) != 8: failure( "Get Full range - len" ) for i in [0:len(a1)] if a1[i] != array[i]: failure( "Get Full range - item " + i ) end // direct range -- 1 item a1 = array[2:3] if len( a1 ) != 1: failure( "Get 1 item - len" ) if a1[0] != 2: failure( "Get 1 item - value" ) // direct range -- more items a1 = array[2:5] if len( a1 ) != 3: failure( "Get more elements - len" ) if a1[0] != 2 or a1[-1] != 4: failure( "Get more elements - value" ) // Reverse range -- full a1 = array[-1:0] if len( a1 ) != 8: failure( "Reverse range full - len" ) for i in [0:len(a1)] if a1[i] != array[len(a1)-i-1]: failure( "Reverse range full - item " + i ) end // Reverse range -- partial a1 = array[5:4] if len( a1 ) != 2: failure( "Reverse range - len" ) if a1[0] != array[5] or a1[1] != array[4]: failure( "Reverse range - items" ) // Direct range with negative index a1 = array[-5:-4] if len( a1 ) != 1: failure( "Dirct range neg - len(1)" ) if a1[0] != array[-5]: failure( "Dirct range - items(1)" ) // Direct range with negative index a1 = array[-5:-3] if len( a1 ) != 2: failure( "Dirct range neg - len(2)" ) if a1[0] != array[-5] or a1[1] != array[-4]: failure( "Dirct range - items(2)" ) // Reverse range with negative index a1 = array[-3:-5] if len( a1 ) != 3: failure( "Reverse range neg - len(3)" ) if a1[0] != array[-3] or a1[2] != array[-5]: failure( "Dirct range - items(3)" ) // Reverse range open a1 = array[-5:] if len( a1 ) != 5: failure( "Reverse range open - len" ) if a1[4] != 7 or a1[0] != 3: failure( "Reverse range open - items" ) // Reverse range open -- empty a1 = array[-1:] if len( a1 ) != 1: failure( "Reverse range open last" ) if a1[0] != 7: failure( "Reverse range open last - item" ) a1 = array[-5:-5] if len( a1 ) != 0: failure( "Negative empty" ) a1 = array[3:-5] if len( a1 ) != 0: failure( "Negative empty x:-x" ) a1 = array[-5:3] if len( a1 ) != 0: failure( "Negative empty -x:x" ) success() /* End of file */ tests/core/testsuite/assert.fal000066400000000000000000000016171176363201700172010ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 38f * Category: messages * Subcategory: assert * Short: Assertions * Description: * Checks working of assertions. * [/Description] * ****************************************************************************/ function handler( data ) if data[0] != "Hello world": failure( "Data content" ) data += "handler" end // let's create the message message = ["Hello world"] assert( "msg", message ) // assert once. subscribe( "msg", handler ) if "handler" notin message: failure( "Assertion not fired" ) // Reassert message = ["Hello world"] assert( "msg", message ) if "handler" notin message: failure( "Assertion not back-fired" ) unsubscribe( "msg", handler ) // retract retract( "msg" ) subscribe( "msg", handler ) // ^^ would cause an error if something is wrong success() /* end of file */ tests/core/testsuite/assert_rep.fal000066400000000000000000000022031176363201700200370ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 38h * Category: messages * Subcategory: assert * Short: Repeated assertion * Description: * Verifies what happens on re-assertion and on broadcasts over existing * assertions. * [/Description] * ****************************************************************************/ control = 0 result = "untouched" function tfunc( data ) global control, result if data != control: failure( "Didn't receive expected data" ) result = data end // we're clean now subscribe( "test", tfunc ) if result != "untouched": failure( "Changed on subscription" ) control = 100 assert( "test", 100 ) if result != 100: failure( "Not called on first assertion" ) control = 200 assert( "test", 200 ) if result != 200: failure( "Not called on second assertion" ) control = "onbkast" broadcast( "test", control ) if result != control: failure( "Not called on broadcast" ) retract( "test" ) control = "onbkast-after" broadcast( "test", control ) if result != control: failure( "Not called on broadcast after retract" ) success() /* end of file */ tests/core/testsuite/assert_slot.fal000066400000000000000000000017171176363201700202430ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 38g * Category: messages * Subcategory: assert * Short: Assertions on VMSlots * Description: * Checks working of assertions done on vmslots * [/Description] * ****************************************************************************/ function handler( data ) if data[0] != "Hello world": failure( "Data content" ) data += "handler" end // let's create the message message = ["Hello world"] slot = VMSlot( "msg" ) slot.assert( message ) // assert once. slot.subscribe( handler ) if "handler" notin message: failure( "Assertion not fired" ) // Reassert message = ["Hello world"] slot.assert( message ) if "handler" notin message: failure( "Assertion not back-fired" ) slot.unsubscribe( handler ) // retract slot.retract() message = ["Hello world"] slot.subscribe( handler ) // ^^ would cause an error if something is wrong success() /* end of file */ tests/core/testsuite/autoadd.fal000066400000000000000000000034101176363201700173120ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 30a * Category: expression * Subcategory: autoassign * Short: Deep autoassign add * Description: * Test on complex types for autoassign add. * [/Description] * ****************************************************************************/ deeparr = [ 0 ] array = [ deeparr ] array[0][0] += 1 if deeparr[0] != 1: failure( "Deep array simple" ) array[0][0] += testReflect( 1 ) if deeparr[0] != 2: failure( "Deep array complex" ) object deepobj property = 0 end object one property = deepobj end one.property.property += 1 if deepobj.property != 1: failure( "Deep obj simple" ) one.property.property += testReflect( 1 ) if deepobj.property != 2: failure( "Deep obj complex" ) array = [ one ] one.property = 0 array[0].property += 1 if one.property != 1: failure( "Deep array->object simple." ) array[0].property += testReflect( 1 ) if one.property != 2: failure( "Deep array->object complex." ) one.property = array array[0] = 0 one.property[0] += 1 if array[0] != 1: failure( "Deep object->array simple." ) one.property[0] += testReflect( 1 ) if array[0] != 2: failure( "Deep object->array complex." ) object auto prop = 0 proparr = [ 0 ] function test() self.prop += 1 self.proparr[0] += 1 end function testCpx() self.prop += testReflect( 1 ) self.proparr[0] += testReflect( 1 ) end end auto.test() if auto.prop != 1: failure( "Deep object self assign simple." ) if auto.proparr[0] != 1: failure( "Deep object self to array assign simple." ) auto.testCpx() if auto.prop != 2: failure( "Deep object self assign complex." ) if auto.proparr[0] != 2: failure( "Deep object self to array assign complex." ) success() /* End of file */ tests/core/testsuite/autoand.fal000066400000000000000000000036351176363201700173350ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 30f * Category: expression * Subcategory: autoassign * Short: Deep autoassign and * Description: * Test on complex types for autoassign and * [/Description] * ****************************************************************************/ deeparr = [ 0xffff ] array = [ deeparr ] array[0][0] &= 0xff0f if deeparr[0] != 0xff0f: failure( "Deep array simple" ) array[0][0] &= testReflect( 0xf0f0 ) if deeparr[0] != 0xf000: failure( "Deep array complex" ) object deepobj property = 0xffff end object one property = deepobj end one.property.property &= 0xff0f if deepobj.property != 0xff0f: failure( "Deep obj simple" ) one.property.property &= testReflect( 0xf0f0 ) if deepobj.property != 0xf000: failure( "Deep obj complex" ) array = [ one ] one.property = 0xffff array[0].property &= 0xff0f if one.property != 0xff0f: failure( "Deep array->object simple." ) array[0].property &= testReflect( 0xf0f0 ) if one.property != 0xf000: failure( "Deep array->object complex." ) one.property = array array[0] = 0xffff one.property[0] &= 0xff0f if array[0] != 0xff0f: failure( "Deep object->array simple." ) one.property[0] &= testReflect( 0xf0f0 ) if array[0] != 0xf000: failure( "Deep object->array complex." ) object auto prop = 0xffff proparr = [ 0xffff ] function test() self.prop &= 0xff0f self.proparr[0] &= 0xff0f end function testCpx() self.prop &= testReflect( 0xf0f0 ) self.proparr[0] &= testReflect( 0xf0f0 ) end end auto.test() if auto.prop != 0xff0f: failure( "Deep object self assign simple." ) if auto.proparr[0] != 0xff0f: failure( "Deep object self to array assign simple." ) auto.testCpx() if auto.prop != 0xf000: failure( "Deep object self assign complex." ) if auto.proparr[0] != 0xf000: failure( "Deep object self to array assign complex." ) success() /* End of file */ tests/core/testsuite/autoarr.fal000066400000000000000000000024431176363201700173530ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 11e * Category: types * Subcategory: array * Short: Complex array autoops * Description: * Verify the correctness of complex array access and auto ops * Using only integers in this test * [/Description] * ****************************************************************************/ flag = 0 function changingVal( val ) global flag if flag: return val + 1 flag++ return val end // assignment test - include a compilation test array = [ 10,10,10,10,10,10 ] array[0]++ if array[0] != 11: failure( "auto inc base" ) if ++array[1] != 11: failure( "auto inc pre" ) if array[1] != 11: failure( "auto inc pre - effect" ) if array[2]++ != 10: failure( "auto inc post - effect" ) if array[2] != 11: failure( "auto inc post - effect" ) // complex array[ changingVal(3) ]++ flag = 0 if array[changingVal(3)] != 11: failure( "auto inc complex" ) flag = 0 if ++array[changingVal(4)] != 11: failure( "auto inc complex pre" ) flag = 0 if array[changingVal(4)] != 11: failure( "auto inc complex pre - effect" ) flag = 0 if array[changingVal(5)]++ != 10: failure( "auto inc complex post" ) flag = 0 if array[changingVal(5)] != 11: failure( "auto inc complex post - effect" ) success() /* End of file */ tests/core/testsuite/autodec.fal000066400000000000000000000045611176363201700173250ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 31b * Category: expression * Subcategory: incdec * Short: Deep autodecrement * Description: * Test on complex types for auto decrement * [/Description] * ****************************************************************************/ deeparr = [ 2 ] array = [ deeparr ] if array[0][0]-- != 2: failure( "Array postfix value retrival" ) if deeparr[0] != 1: failure( "Array postfix effect" ) if --array[0][0] != 0: failure( "Array prefix value retrival" ) if deeparr[0] != 0: failure( "Array prefix effect" ) object deepobj property = 2 end object one property = deepobj end if one.property.property-- != 2: failure( "Deep obj postfix simple retrival" ) if deepobj.property != 1: failure( "Deep obj postfix effect" ) if --one.property.property != 0: failure( "Deep obj prefix simple retrival" ) if deepobj.property != 0: failure( "Deep obj prefix effect" ) array = [ one ] one.property = 2 if array[0].property -- != 2: failure( "Deep array->object postfix retrival." ) if one.property != 1: failure( "Deep array->object postfix effext." ) if -- array[0].property != 0: failure( "Deep array->object prefix retrival." ) if one.property != 0: failure( "Deep array->object prefix effext." ) one.property = array array[0] = 2 if one.property[0] -- != 2: failure( "Deep object->array postfix retrival." ) if array[0] != 1: failure( "Deep object->array postfix effext." ) if -- one.property[0] != 0: failure( "Deep object->array prefix retrival." ) if array[0] != 0: failure( "Deep object->array prefix effext." ) object auto prop = 2 proparr = [ 2 ] function testpost() if self.prop-- != 2: failure( "Deep object self postfix retrival." ) if self.proparr[0]-- != 2: failure( "Deep object self to array postfix retrival." ) end function testpre() if --self.prop != 0: failure( "Deep object self prefix retrival." ) if --self.proparr[0] != 0: failure( "Deep object self to array prefix retrival." ) end end auto.testpost() if auto.prop != 1: failure( "Deep object self postfix effect." ) if auto.proparr[0] != 1: failure( "Deep object self to array postfix effect." ) auto.testpre() if auto.prop != 0: failure( "Deep object self prefix effect." ) if auto.proparr[0] != 0: failure( "Deep object self to array prefix effect." ) success() /* End of file */ tests/core/testsuite/autodiv.fal000066400000000000000000000034621176363201700173530ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 30d * Category: expression * Subcategory: autoassign * Short: Deep autoassign divide * Description: * Test on complex types for autoassign divide. * [/Description] * ****************************************************************************/ deeparr = [ 50 ] array = [ deeparr ] array[0][0] /= 10 if deeparr[0] != 5.0: failure( "Deep array simple" ) array[0][0] /= testReflect( 2 ) if deeparr[0] != 2.5: failure( "Deep array complex" ) object deepobj property = 50 end object one property = deepobj end one.property.property /= 10 if deepobj.property != 5.0: failure( "Deep obj simple" ) one.property.property /= testReflect( 2 ) if deepobj.property != 2.5: failure( "Deep obj complex" ) array = [ one ] one.property = 50 array[0].property /= 10 if one.property != 5.0: failure( "Deep array->object simple." ) array[0].property /= testReflect( 2 ) if one.property != 2.5: failure( "Deep array->object complex." ) one.property = array array[0] = 50 one.property[0] /= 10 if array[0] != 5.0: failure( "Deep object->array simple." ) one.property[0] /= testReflect( 2 ) if array[0] != 2.5: failure( "Deep object->array complex." ) object auto prop = 50 proparr = [ 50 ] function test() self.prop /= 10 self.proparr[0] /= 10 end function testCpx() self.prop /= testReflect( 2 ) self.proparr[0] /= testReflect( 2 ) end end auto.test() if auto.prop != 5.0: failure( "Deep object self assign simple." ) if auto.proparr[0] != 5.0: failure( "Deep object self to array assign simple." ) auto.testCpx() if auto.prop != 2.5: failure( "Deep object self assign complex." ) if auto.proparr[0] != 2.5: failure( "Deep object self to array assign complex." ) success() /* End of file */ tests/core/testsuite/autoinc.fal000066400000000000000000000045611176363201700173430ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 31a * Category: expression * Subcategory: incdec * Short: Deep autoincrement * Description: * Test on complex types for auto increment * [/Description] * ****************************************************************************/ deeparr = [ 0 ] array = [ deeparr ] if array[0][0]++ != 0: failure( "Array postfix value retrival" ) if deeparr[0] != 1: failure( "Array postfix effect" ) if ++array[0][0] != 2: failure( "Array prefix value retrival" ) if deeparr[0] != 2: failure( "Array prefix effect" ) object deepobj property = 0 end object one property = deepobj end if one.property.property++ != 0: failure( "Deep obj postfix simple retrival" ) if deepobj.property != 1: failure( "Deep obj postfix effect" ) if ++one.property.property != 2: failure( "Deep obj prefix simple retrival" ) if deepobj.property != 2: failure( "Deep obj prefix effect" ) array = [ one ] one.property = 0 if array[0].property ++ != 0: failure( "Deep array->object postfix retrival." ) if one.property != 1: failure( "Deep array->object postfix effext." ) if ++ array[0].property != 2: failure( "Deep array->object prefix retrival." ) if one.property != 2: failure( "Deep array->object prefix effext." ) one.property = array array[0] = 0 if one.property[0] ++ != 0: failure( "Deep object->array postfix retrival." ) if array[0] != 1: failure( "Deep object->array postfix effext." ) if ++ one.property[0] != 2: failure( "Deep object->array prefix retrival." ) if array[0] != 2: failure( "Deep object->array prefix effext." ) object auto prop = 0 proparr = [ 0 ] function testpost() if self.prop++ != 0: failure( "Deep object self postfix retrival." ) if self.proparr[0]++ != 0: failure( "Deep object self to array postfix retrival." ) end function testpre() if ++self.prop != 2: failure( "Deep object self prefix retrival." ) if ++self.proparr[0] != 2: failure( "Deep object self to array prefix retrival." ) end end auto.testpost() if auto.prop != 1: failure( "Deep object self postfix effect." ) if auto.proparr[0] != 1: failure( "Deep object self to array postfix effect." ) auto.testpre() if auto.prop != 2: failure( "Deep object self prefix effect." ) if auto.proparr[0] != 2: failure( "Deep object self to array prefix effect." ) success() /* End of file */ tests/core/testsuite/automod.fal000066400000000000000000000034461176363201700173520ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 30e * Category: expression * Subcategory: autoassign * Short: Deep autoassign module * Description: * Test on complex types for autoassign module. * [/Description] * ****************************************************************************/ deeparr = [ 103 ] array = [ deeparr ] array[0][0] %= 100 if deeparr[0] != 3: failure( "Deep array simple" ) array[0][0] %= testReflect( 2 ) if deeparr[0] != 1: failure( "Deep array complex" ) object deepobj property = 103 end object one property = deepobj end one.property.property %= 100 if deepobj.property != 3: failure( "Deep obj simple" ) one.property.property %= testReflect( 2 ) if deepobj.property != 1: failure( "Deep obj complex" ) array = [ one ] one.property = 103 array[0].property %= 100 if one.property != 3: failure( "Deep array->object simple." ) array[0].property %= testReflect( 2 ) if one.property != 1: failure( "Deep array->object complex." ) one.property = array array[0] = 103 one.property[0] %= 100 if array[0] != 3: failure( "Deep object->array simple." ) one.property[0] %= testReflect( 2 ) if array[0] != 1: failure( "Deep object->array complex." ) object auto prop = 103 proparr = [ 103 ] function test() self.prop %= 100 self.proparr[0] %= 100 end function testCpx() self.prop %= testReflect( 2 ) self.proparr[0] %= testReflect( 2 ) end end auto.test() if auto.prop != 3: failure( "Deep object self assign simple." ) if auto.proparr[0] != 3: failure( "Deep object self to array assign simple." ) auto.testCpx() if auto.prop != 1: failure( "Deep object self assign complex." ) if auto.proparr[0] != 1: failure( "Deep object self to array assign complex." ) success() /* End of file */ tests/core/testsuite/automul.fal000066400000000000000000000034361176363201700173670ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 30c * Category: expression * Subcategory: autoassign * Short: Deep autoassign multiply * Description: * Test on complex types for autoassign multiply. * [/Description] * ****************************************************************************/ deeparr = [ 2 ] array = [ deeparr ] array[0][0] *= 2 if deeparr[0] != 4: failure( "Deep array simple" ) array[0][0] *= testReflect( -2 ) if deeparr[0] != -8: failure( "Deep array complex" ) object deepobj property = 2 end object one property = deepobj end one.property.property *= 2 if deepobj.property != 4: failure( "Deep obj simple" ) one.property.property *= testReflect( -2 ) if deepobj.property != -8: failure( "Deep obj complex" ) array = [ one ] one.property = 2 array[0].property *= 2 if one.property != 4: failure( "Deep array->object simple." ) array[0].property *= testReflect( -2 ) if one.property != -8: failure( "Deep array->object complex." ) one.property = array array[0] = 2 one.property[0] *= 2 if array[0] != 4: failure( "Deep object->array simple." ) one.property[0] *= testReflect( -2 ) if array[0] != -8: failure( "Deep object->array complex." ) object auto prop = 2 proparr = [ 2 ] function test() self.prop *= 2 self.proparr[0] *= 2 end function testCpx() self.prop *= testReflect( -2 ) self.proparr[0] *= testReflect( -2 ) end end auto.test() if auto.prop != 4: failure( "Deep object self assign simple." ) if auto.proparr[0] != 4: failure( "Deep object self to array assign simple." ) auto.testCpx() if auto.prop != -8: failure( "Deep object self assign complex." ) if auto.proparr[0] != -8: failure( "Deep object self to array assign complex." ) success() /* End of file */ tests/core/testsuite/autoor.fal000066400000000000000000000036331176363201700172110ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 30g * Category: expression * Subcategory: autoassign * Short: Deep autoassign or * Description: * Test on complex types for autoassign or * [/Description] * ****************************************************************************/ deeparr = [ 0x0f00 ] array = [ deeparr ] array[0][0] |= 0x01f0 if deeparr[0] != 0x0ff0: failure( "Deep array simple" ) array[0][0] |= testReflect( 0xf0f0 ) if deeparr[0] != 0xfff0: failure( "Deep array complex" ) object deepobj property = 0x0f00 end object one property = deepobj end one.property.property |= 0x01f0 if deepobj.property != 0x0ff0: failure( "Deep obj simple" ) one.property.property |= testReflect( 0xf0f0 ) if deepobj.property != 0xfff0: failure( "Deep obj complex" ) array = [ one ] one.property = 0x0f00 array[0].property |= 0x01f0 if one.property != 0x0ff0: failure( "Deep array->object simple." ) array[0].property |= testReflect( 0xf0f0 ) if one.property != 0xfff0: failure( "Deep array->object complex." ) one.property = array array[0] = 0x0f00 one.property[0] |= 0x01f0 if array[0] != 0x0ff0: failure( "Deep object->array simple." ) one.property[0] |= testReflect( 0xf0f0 ) if array[0] != 0xfff0: failure( "Deep object->array complex." ) object auto prop = 0x0f00 proparr = [ 0x0f00 ] function test() self.prop |= 0x01f0 self.proparr[0] |= 0x01f0 end function testCpx() self.prop |= testReflect( 0xf0f0 ) self.proparr[0] |= testReflect( 0xf0f0 ) end end auto.test() if auto.prop != 0x0ff0: failure( "Deep object self assign simple." ) if auto.proparr[0] != 0x0ff0: failure( "Deep object self to array assign simple." ) auto.testCpx() if auto.prop != 0xfff0: failure( "Deep object self assign complex." ) if auto.proparr[0] != 0xfff0: failure( "Deep object self to array assign complex." ) success() /* End of file */ tests/core/testsuite/autopow.fal000066400000000000000000000015341176363201700173740ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 30i * Category: expression * Subcategory: autoassign * Short: Deep autoassign power * Description: * Test on complex types for autoassign power * [/Description] * ****************************************************************************/ deeparr = [ 100 ] array = [ deeparr ] array[0][0] **= 2 if deeparr[0] != 10000.0: failure( "Deep array simple" ) array[0][0] **= testReflect( 1 / 2 ) if deeparr[0] != 100.0: failure( "Deep array complex" ) object deepobj property = 100 end object one property = deepobj end one.property.property **= 2 if deepobj.property != 10000.0: failure( "Deep obj simple" ) one.property.property **= testReflect( 1 / 2 ) if deepobj.property != 100.0: failure( "Deep obj complex" ) success() /* End of file */ tests/core/testsuite/autoshl.fal000066400000000000000000000036011176363201700173520ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 30j * Category: expression * Subcategory: autoassign * Short: Deep autoassign shift left * Description: * Test on complex types for autoassign shift left * [/Description] * ****************************************************************************/ deeparr = [ 0x0f00 ] array = [ deeparr ] array[0][0] <<= 4 if deeparr[0] != 0xf000: failure( "Deep array simple" ) array[0][0] <<= testReflect( 4 ) if deeparr[0] != 0xf0000: failure( "Deep array complex" ) object deepobj property = 0x0f00 end object one property = deepobj end one.property.property <<= 4 if deepobj.property != 0xf000: failure( "Deep obj simple" ) one.property.property <<= testReflect( 4 ) if deepobj.property != 0xf0000: failure( "Deep obj complex" ) array = [ one ] one.property = 0x0f00 array[0].property <<= 4 if one.property != 0xf000: failure( "Deep array->object simple." ) array[0].property <<= testReflect( 4 ) if one.property != 0xf0000: failure( "Deep array->object complex." ) one.property = array array[0] = 0x0f00 one.property[0] <<= 4 if array[0] != 0xf000: failure( "Deep object->array simple." ) one.property[0] <<= testReflect( 4 ) if array[0] != 0xf0000: failure( "Deep object->array complex." ) object auto prop = 0x0f00 proparr = [ 0x0f00 ] function test() self.prop <<= 4 self.proparr[0] <<= 4 end function testCpx() self.prop <<= testReflect( 4 ) self.proparr[0] <<= testReflect( 4 ) end end auto.test() if auto.prop != 0xf000: failure( "Deep object self assign simple." ) if auto.proparr[0] != 0xf000: failure( "Deep object self to array assign simple." ) auto.testCpx() if auto.prop != 0xf0000: failure( "Deep object self assign complex." ) if auto.proparr[0] != 0xf0000: failure( "Deep object self to array assign complex." ) success() /* End of file */ tests/core/testsuite/autoshr.fal000066400000000000000000000035751176363201700173720ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 30k * Category: expression * Subcategory: autoassign * Short: Deep autoassign shift right * Description: * Test on complex types for autoassign shift right * [/Description] * ****************************************************************************/ deeparr = [ 0x0f00 ] array = [ deeparr ] array[0][0] >>= 4 if deeparr[0] != 0x00f0: failure( "Deep array simple" ) array[0][0] >>= testReflect( 4 ) if deeparr[0] != 0x000f: failure( "Deep array complex" ) object deepobj property = 0x0f00 end object one property = deepobj end one.property.property >>= 4 if deepobj.property != 0x00f0: failure( "Deep obj simple" ) one.property.property >>= testReflect( 4 ) if deepobj.property != 0x000f: failure( "Deep obj complex" ) array = [ one ] one.property = 0x0f00 array[0].property >>= 4 if one.property != 0x00f0: failure( "Deep array->object simple." ) array[0].property >>= testReflect( 4 ) if one.property != 0x000f: failure( "Deep array->object complex." ) one.property = array array[0] = 0x0f00 one.property[0] >>= 4 if array[0] != 0x00f0: failure( "Deep object->array simple." ) one.property[0] >>= testReflect( 4 ) if array[0] != 0x000f: failure( "Deep object->array complex." ) object auto prop = 0x0f00 proparr = [ 0x0f00 ] function test() self.prop >>= 4 self.proparr[0] >>= 4 end function testCpx() self.prop >>= testReflect( 4 ) self.proparr[0] >>= testReflect( 4 ) end end auto.test() if auto.prop != 0x00f0: failure( "Deep object self assign simple." ) if auto.proparr[0] != 0x00f0: failure( "Deep object self to array assign simple." ) auto.testCpx() if auto.prop != 0x000f: failure( "Deep object self assign complex." ) if auto.proparr[0] != 0x000f: failure( "Deep object self to array assign complex." ) success() /* End of file */ tests/core/testsuite/autosub.fal000066400000000000000000000034101176363201700173530ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 30b * Category: expression * Subcategory: autoassign * Short: Deep autoassign sub * Description: * Test on complex types for autoassign sub. * [/Description] * ****************************************************************************/ deeparr = [ 2 ] array = [ deeparr ] array[0][0] -= 1 if deeparr[0] != 1: failure( "Deep array simple" ) array[0][0] -= testReflect( 1 ) if deeparr[0] != 0: failure( "Deep array complex" ) object deepobj property = 2 end object one property = deepobj end one.property.property -= 1 if deepobj.property != 1: failure( "Deep obj simple" ) one.property.property -= testReflect( 1 ) if deepobj.property != 0: failure( "Deep obj complex" ) array = [ one ] one.property = 2 array[0].property -= 1 if one.property != 1: failure( "Deep array->object simple." ) array[0].property -= testReflect( 1 ) if one.property != 0: failure( "Deep array->object complex." ) one.property = array array[0] = 2 one.property[0] -= 1 if array[0] != 1: failure( "Deep object->array simple." ) one.property[0] -= testReflect( 1 ) if array[0] != 0: failure( "Deep object->array complex." ) object auto prop = 2 proparr = [ 2 ] function test() self.prop -= 1 self.proparr[0] -= 1 end function testCpx() self.prop -= testReflect( 1 ) self.proparr[0] -= testReflect( 1 ) end end auto.test() if auto.prop != 1: failure( "Deep object self assign simple." ) if auto.proparr[0] != 1: failure( "Deep object self to array assign simple." ) auto.testCpx() if auto.prop != 0: failure( "Deep object self assign complex." ) if auto.proparr[0] != 0: failure( "Deep object self to array assign complex." ) success() /* End of file */ tests/core/testsuite/autoxor.fal000066400000000000000000000036351176363201700174030ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 30h * Category: expression * Subcategory: autoassign * Short: Deep autoassign xor * Description: * Test on complex types for autoassign xor * [/Description] * ****************************************************************************/ deeparr = [ 0x0f00 ] array = [ deeparr ] array[0][0] ^= 0x01f0 if deeparr[0] != 0x0Ef0: failure( "Deep array simple" ) array[0][0] ^= testReflect( 0xf0f0 ) if deeparr[0] != 0xfE00: failure( "Deep array complex" ) object deepobj property = 0x0f00 end object one property = deepobj end one.property.property ^= 0x01f0 if deepobj.property != 0x0Ef0: failure( "Deep obj simple" ) one.property.property ^= testReflect( 0xf0f0 ) if deepobj.property != 0xfE00: failure( "Deep obj complex" ) array = [ one ] one.property = 0x0f00 array[0].property ^= 0x01f0 if one.property != 0x0Ef0: failure( "Deep array->object simple." ) array[0].property ^= testReflect( 0xf0f0 ) if one.property != 0xfE00: failure( "Deep array->object complex." ) one.property = array array[0] = 0x0f00 one.property[0] ^= 0x01f0 if array[0] != 0x0Ef0: failure( "Deep object->array simple." ) one.property[0] ^= testReflect( 0xf0f0 ) if array[0] != 0xfE00: failure( "Deep object->array complex." ) object auto prop = 0x0f00 proparr = [ 0x0f00 ] function test() self.prop ^= 0x01f0 self.proparr[0] ^= 0x01f0 end function testCpx() self.prop ^= testReflect( 0xf0f0 ) self.proparr[0] ^= testReflect( 0xf0f0 ) end end auto.test() if auto.prop != 0x0Ef0: failure( "Deep object self assign simple." ) if auto.proparr[0] != 0x0Ef0: failure( "Deep object self to array assign simple." ) auto.testCpx() if auto.prop != 0xfE00: failure( "Deep object self assign complex." ) if auto.proparr[0] != 0xfE00: failure( "Deep object self to array assign complex." ) success() /* End of file */ tests/core/testsuite/backslash.fal000066400000000000000000000010261176363201700176250ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 1j * Category: basic * Subcategory: structure * Short: Backslash * Description: * Checks for backslash parsing. * This tests checks both for valid "\" and fake (useless) "\" * [/Description] * ****************************************************************************/ sel = 1 if \ sel success() else // useless backslash failure( "Wrong " + "branch" \ ) end failure( "If skipped" ) /* End of file */ tests/core/testsuite/base64.fal000066400000000000000000000025321176363201700167610ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 150a * Category: Encoding * Subcategory: Base64 * Short: Test for base64 encoding * Description: * Checks that base64 is working. * [/Description] * ****************************************************************************/ if Base64.encode( "Hello" ) != "SGVsbG8=": failure("Basic encode 1") if Base64.encode( "Hellod" ) != "SGVsbG9k": failure("Basic encode 2") if Base64.encode( "Hellodx" ) != "SGVsbG9keA==": failure("Basic encode 3") if Base64.encode("" ) != "" : failure("Basic encode empty") if Base64.decode( Base64.encode( "Hello" ) )!= "Hello": failure("Basic decode 1") if Base64.decode( Base64.encode( "Hellod" ) )!= "Hellod": failure("Basic decode 2") if Base64.decode( Base64.encode( "Hellodx" ) )!= "Hellodx": failure("Basic decode 3") if Base64.decode( Base64.encode( "" ) )!= "": failure("Basic decode empty") if Base64.decode( Base64.encode( "私生エンコードする" ) )!= "私生エンコードする": failure("Intl. encode 1") if Base64.decode( Base64.encode( "私生エンコードしてる" ) )!= "私生エンコードしてる": failure("Intl. encode 2") if Base64.decode( Base64.encode( "私生エンコードしている" ) )!= "私生エンコードしている": failure("Intl. encode 3") success() /* End of file */ tests/core/testsuite/base64bin.fal000066400000000000000000000012701176363201700174500ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 150b * Category: Encoding * Subcategory: Base64 * Short: Check binary data in Base64 * Description: * Checks that base64 is working -- with binary data. * [/Description] * ****************************************************************************/ mb1 = Base64.decmb( "SGVsbG8=" ) mb2 = Base64.decmb( "SGVsbG9k" ) mb3 = Base64.decmb( "SGVsbG9keA==" ) if Base64.encode( mb1 ) != "SGVsbG8=": failure("Basic encode 1") if Base64.encode( mb2 ) != "SGVsbG9k": failure("Basic encode 2") if Base64.encode( mb3 ) != "SGVsbG9keA==": failure("Basic encode 3") success() /* End of file */ tests/core/testsuite/basicif.fal000066400000000000000000000010651176363201700172750ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 1g * Category: basic * Subcategory: conditionals * Short: If-else negative. * Description: * Checks for a minimal if/then/else to be correctly evaluated. * This is the negative test, that is, with the if that should * fail and enter the else branch. * [/Description] * ****************************************************************************/ sel = 0 if sel failure( "Wrong branch" ) else success() end failure( "If skipped" ) /* End of file */ tests/core/testsuite/binary.fal000066400000000000000000000013011176363201700171520ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 2d * Category: expressions * Subcategory: * Short: Binary expressions * Description: * Tests for various binary expressions * [/Description] * ****************************************************************************/ a = 0xFF00 b = 0x0FF0 if a && b != 0x0F00: failure( "AND" ) if a || b != 0xFFF0: failure( "OR" ) if a ^^ b != 0xF0F0: failure( "XOR" ) if ~a != 0xFFFFFFFFFFFF00FF: failure( "NOT" ) if a << 1 != 0x1FE00: failure( "SHL 1" ) if a >> 1 != 0x7F80: failure( "SHR 1" ) if a << 8 != 0xFF0000: failure( "SHL 8" ) if a >> 8 != 0xFF: failure( "SHR 8" ) success() /* End of file */ tests/core/testsuite/binary_assign.fal000066400000000000000000000013621176363201700205250ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 2e * Category: expressions * Subcategory: * Short: Binary assignment expressions * Description: * Tests for various binary expressions (assigmnent version) * [/Description] * ****************************************************************************/ a = 0xFF00 b = 0x0FF0 c = a c &= b if c != 0x0F00: failure( "AND" ) c = a c |= b if c != 0xFFF0: failure( "OR" ) c = a c ^= b if c != 0xF0F0: failure( "XOR" ) c = a c <<= 1 if c != 0x1FE00: failure( "SHL 1" ) c = a c >>= 1 if c != 0x7F80: failure( "SHR 1" ) c = a c <<= 8 if c != 0xFF0000: failure( "SHL 8" ) c = a c >>= 8 if c != 0xFF: failure( "SHR 8" ) success() /* End of file */ tests/core/testsuite/caller.fal000066400000000000000000000012561176363201700171410ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 13f * Category: functions * Subcategory: * Short: Function caller * Description: * This test calls a function that in turns calls another function * that calls the original function with a different argument. It * verifies that fself.caller is working properly. * [/Description] * ****************************************************************************/ function test1() return fself.caller()(3) end function test( param ) if 1 == param return test1() else return 2 end end if test(1) != 2: failure( "fself.caller" ) success() /* End of file */ tests/core/testsuite/class.fal000066400000000000000000000026441176363201700170060ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 21a * Category: types * Subcategory: classes * Short: Basic classes * Description: * Basic classes and instantation. * [/Description] * ****************************************************************************/ class zero prop_one = nil prop_two = nil end ze = zero() if ze.prop_one != nil: failure( "Can't access basic property" ) ze.prop_two = "changed" if ze.prop_two != "changed": failure( "Can't change basic property") class basic prop_nil = nil prop_num = 0 prop_str = "Nothing" prop_array = ["An array", 2, 3] prop_opt = nil init if paramCount() > 0 self.prop_opt = paramNumber( 0 ) end end function multiply( param ) return param * self.prop_num end function dummy() failure( "Missing overload" ) end end elem = basic( 10 ) if elem.prop_nil != nil: failure( "Basic property / 2" ) if len( elem.prop_array ) != 3: failure( "Automatic initializer" ) if elem.prop_opt != 10: failure( "Explicit initializer" ) elem.prop_num = 2 if elem.multiply( 3 ) != 6: failure( "Method call" ) temp = elem.multiply if temp( 3 ) != 6: failure("Basic indirect method call" ) function test( str ) return str + self.prop_array[0] end elem.dummy = test if elem.dummy( "head ") != "head An array": failure( "Method overloading" ) success() /* End of file */ tests/core/testsuite/class_as_param.fal000066400000000000000000000015751176363201700206530ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 21f * Category: types * Subcategory: classes * Short: Object as parameters * Description: * An object is passes as a parameter function and then accessed. * [/Description] * ****************************************************************************/ function func( obj1, obj2 ) try obj1.prop = 1 catch failure( "Can't store in obj1" ) end try var = obj2.prop catch failure( "Can't access obj2 (reference)" ) end try obj1.method() catch failure( "Can't call method in obj1" ) end try obj2.method() catch failure( "Can't call method in obj2 (reference)" ) end end class cls prop = 0 function method() end end obj1 = cls() obj2 = cls() func( obj1, $obj2 ) success() /* End of file */ tests/core/testsuite/class_gc.fal000066400000000000000000000021241176363201700174500ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 21o * Category: types * Subcategory: classes * Short: Class and GC * Description: * Performs several GC loops while mangling with classes (especially init). * [/Description] * ****************************************************************************/ class basic prop_nil = nil prop_num = 0 prop_str = "Nothing" prop_array = ["An array", 2, 3] prop_opt = nil prop_test = "" init GC.perform( true ) if paramCount() > 0 self.prop_opt = paramNumber( 0 ) end for i = 1 to 10 GC.perform( true ) self.prop_test += strReplicate( "a", 256 ) end end function multiply( param ) return param * self.prop_num end function dummy() failure( "Missing overload" ) end end elem = basic( 10 ) if elem.prop_nil != nil: failure( "Basic property / 2" ) if len( elem.prop_array ) != 3: failure( "Automatic initializer" ) if elem.prop_opt != 10: failure( "Explicit initializer" ) success() /* End of file */ tests/core/testsuite/class_in.fal000066400000000000000000000014511176363201700174670ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 21j * Category: types * Subcategory: classes * Short: Operator "in" for classess * Description: * The in operator, for classes, works as the provides operator, but it * check for strings instead of symbols. * [/Description] * ****************************************************************************/ class test prop_one = 0 prop_two = "something" function method() end end zero = test() if "prop_one" notin zero: failure( "Property check via in -- 1" ) if "prop_two" notin zero: failure( "Property check via in -- 2" ) if "method" notin zero: failure( "Property check via in -- 3" ) if "not here" in zero: failure( "Property check, negative" ) success() /* End of file */ tests/core/testsuite/class_method.fal000066400000000000000000000024631176363201700203450ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 28i * Category: classes * Subcategory: subclasses * Short: Class Method Coherence * Description: * In this test, we check for correct assignment and usage of * "class methods". Class methods are pair of objects and inner * class. They are generated by asking for a subclass property in * an object, and once dereferenced, they must create a method using * the base class function and the original object. Also, calling * a class method must act as calling the base class itself (i.e. * creating an instance of the base class). * Finally, the test also checks for the "." operator not to create * a class method for a property not being a base class for the given * object. * [/Description] * ****************************************************************************/ class A function test(): return "A" end class B from A prop = nil function test(): return "B" end b = B() if b.A.typeId() != ClassMethodType: failure( "type id") if b.test() != "B": failure( "derived method") if b.A.test() != "A": failure( "base method") c = b.A() if c.baseClass() != A: failure( "base class instance" ) b.prop = TimeStamp if b.prop.typeId() != ClassType: failure( "Isomorphism of non bases" ) success() tests/core/testsuite/class_setter_getter.fal000066400000000000000000000016721176363201700217460ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 22j * Category: types * Subcategory: classes * Short: Property accessor * Description: * Tests for a complete accessor to work on a virtual property. * * [/Description] * ****************************************************************************/ class test _private_var = "Initial" count = 0 function __set_prop( value ) ++self.count self._private_var = value end function __get_prop() ++self.count return self._private_var end end t = test() if t.prop != "Initial": failure( "Initial value" ) if t.count != 1: failure( "First access" ) if (t.prop = "NewValue") != "NewValue": failure( "Assign return value" ) if t.count != 2: failure( "Second access" ) if t.prop != "NewValue": failure( "Retrieval of new value" ) if t.count != 3: failure( "Third access" ) success() /* end of file */ tests/core/testsuite/class_setter_getter_err.fal000066400000000000000000000023141176363201700226100ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 22k * Category: types * Subcategory: classes * Short: Property accessor errors * Description: * Checks for error raisal in case of dynamic errors in accessing * properties masked by accessors. * * [/Description] * ****************************************************************************/ class ReadOnly function __get_prop() return "A value" end end class WriteOnly function __set_prop( value ) return value end end class AccessItself function __set_prop( value ) self.prop = value end function __get_prop() return self.prop end end ro = ReadOnly() if ro.prop != "A value": failure( "Read only value" ) try ro.prop = "hello" failure( "Read-only write allowed" ) catch AccessError end wo = WriteOnly() wo.prop = "A value" // would raise on failure. try x = wo.prop failure( "Write-only read allowed" ) catch AccessError end ai = AccessItself() try ai.prop = "some value" failure( "Access itself -- write" ) catch AccessError end try x = ai.prop failure( "Access itself -- read" ) catch AccessError end success() /* end of file */ tests/core/testsuite/class_static.fal000066400000000000000000000022171176363201700203510ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 21l * Category: types * Subcategory: classes * Short: Class static property access. * Description: * Strings in class templates are shared among all the objects * generated from that class. Changes in one object must alter * only the local string, leaving the template string alone. * [/Description] * ****************************************************************************/ class test // A static property static property = 0 static ptrue = true static pfalse = false function stc_func() test.property++ end end // smoke if test.property != 0: failure( "Basic static access" ) if test.pfalse: failure( "Basic static access - bool false" ) if not test.ptrue: failure( "Basic static access - bool true" ) test.stc_func() test.stc_func() test.stc_func() test.stc_func() if test.property != 4: failure( "Repeated call" ) test.property = "Hello world" try test.stc_func() failure( "Autoincrement of strings..." ) catch end if test.property != "Hello world" failure( "String assignment" ) end /* End of file */ tests/core/testsuite/class_tplinv.fal000066400000000000000000000031451176363201700203770ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 21k * Category: types * Subcategory: classes * Short: Class template invariancy * Description: * Strings in class templates are shared among all the objects * generated from that class. Changes in one object must alter * only the local string, leaving the template string alone. * [/Description] * ****************************************************************************/ class test // generate a template with a string property = "something" function alter( char, pos ) self.property[pos] = char end function withStatic() // in mehtods, static variables affect // ALL the methods in every object static test = 0 end if test == 0 test = 1 return 0 end return test end end t1 = test() t2 = test() // direct change t1.property[0] = "z" if t1.property != "zomething": failure( "Direct change" ) if t2.property != "something": failure( "Invariance (direct)" ) // alter via method t1.alter( "a", 0 ) if t1.property != "aomething": failure( "Indirect change" ) if t2.property != "something": failure( "Invariance (indirect)" ) // static variance t1.withStatic() t1.withStatic() // also t2 static variable must have been affected. if t2.withStatic() != 1: failure( "Static variance" ) // re-use the template t3 = test() if t3.property != "something": failure( "Template invariance" ) // t3 must come ALIVE with the static value set. if t3.withStatic() != 1: failure( "Template static variance" ) success() /* End of file */ tests/core/testsuite/closedeep.fal000066400000000000000000000021561176363201700176420ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 60g * Category: functional * Subcategory: closure * Short: Functional closure * Description: * Test for second level closures. This checks for being able to close * and modify remote parent local data. * [/Description] * ****************************************************************************/ function makeSharedDeep() locvar = 0 make_get = function() return {=> c = locvar; return c } end make_set = function() return {a=> locvar = a} end return [make_get(), make_set()] end function makePrivateDeep() locvar = 0 make_get = function() return {=>locvar} end make_set = innerfunc() return {a=> locvar = a} end return [make_get(), make_set()] end get, set = makeSharedDeep() set( "Hello" ) if get() != "Hello": failure( "Share deep" ) get, set = makeSharedDeep() if get() != 0: failure( "Contamination of local" ) get, set = makePrivateDeep() set( "Hello" ) if get() != 0: failure( "Inner func insolation failed" ) success() /* end of file */ tests/core/testsuite/closure.fal000066400000000000000000000014471176363201700173550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 18g * Category: function * Subcategory: closure * Short: Closures expressions * Description: * Basic test for closures (functions referencing outer vars) * [/Description] * ****************************************************************************/ function closing( a, b ) c = a * b return \ function( n ) return c * n end end function closing_nest( a, b ) c = a * b return \ function( n ) return c * \ (function ( k ); return k * n; end)(2) end end func = closing( 5, 2 ) if func( 5 ) != 50: failure( "Simple closure" ) func = closing_nest( 3, 2 ) if func( 5 ) != 60: failure( "Nested closure" ) success() /* End of file */ tests/core/testsuite/closure_lambda.fal000066400000000000000000000013161176363201700206500ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 18h * Category: lambda * Subcategory: closure * Short: Closures lambda * Description: * Basic test for closures (functions referencing outer vars) * [/Description] * ****************************************************************************/ function closing( a, b ) c = a * b return {n => c*n} end function closing_nest( a, b ) c = a * b return \ function( n ) return c * {k => k*n}(2) end end func = closing( 5, 2 ) if func( 5 ) != 50: failure( "Simple closure" ) func = closing_nest( 3, 2 ) if func( 5 ) != 60: failure( "Nested closure" ) success() /* End of file */ tests/core/testsuite/closure_outer.fal000066400000000000000000000012661176363201700205720ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 18j * Category: function * Subcategory: closure * Short: Outer closures * Description: * Test for closures referencing deeper/outer values. * [/Description] * ****************************************************************************/ gl = 10 function closing( a, b ) c = a * b return \ (function ( s ); c; // import C return { n => gl + s + c + n }; end )(a) end func = closing( 2, 2 ) if func( 1 ) != 17: failure( "Closing with 10" ) gl = 20 func = closing( 2, 2 ) if func( 1 ) != 27: failure( "Closing with 10" ) success() /* End of file */ tests/core/testsuite/complex_pagedict.fal000066400000000000000000000015051176363201700212030ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 12d * Category: types * Subcategory: dictionary * Short: Advanced paged dictionary * Description: * Checks complex items in paged dictionaries * [/Description] * ****************************************************************************/ // assignment test - include a compilation test three = "three" dict = PageDict() dict += [ "one" => [1,2], "two" => [], three => testReflect( [ "one" => 1 ] ) ] // minimal access if dict[ "one" ][1] != 2: failure( "Minimal access" ) // Changing items dict[three]["one"] = "first" dict1 = dict[three] if dict1["one"] != "first" : failure( "Deep assignment" ) //in/notin if three notin dict: failure( "Notin operator - variable search" ) success() /* End of file */ tests/core/testsuite/complexarray.fal000066400000000000000000000014171176363201700204040ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 11b * Category: types * Subcategory: array * Short: Advanced array * Description: * Checks subarrays and other complex operations. * [/Description] * ****************************************************************************/ // assignment test - include a compilation test array = [ 0, "one", [2:3], [0,"one",2] ] // minimal access if array[1] != "one": failure( "Access" ) // two level access if array[3][1] != "one": failure( "Deep access" ) // subitem disanabiguation if array[3][1][0] != "o": failure( "Subitem disambiguation" ) // changing a subitem array[3][1:2] = [1, "two", 3] if array[3][3] != 3: failure( "Subitem changing" ) success() /* End of file */ tests/core/testsuite/complexdict.fal000066400000000000000000000014461176363201700202130ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 12b * Category: types * Subcategory: dictionary * Short: Advanced dictionary * Description: * Checks complex items in dictionaries * [/Description] * ****************************************************************************/ // assignment test - include a compilation test three = "three" dict = [ "one" => [1,2], "two" => [], three => testReflect( [ "one" => 1 ] ) ] // minimal access if dict[ "one" ][1] != 2: failure( "Minimal access" ) // Changing items dict[three]["one"] = "first" dict1 = dict[three] if dict1["one"] != "first" : failure( "Deep assignment" ) //in/notin if three notin dict: failure( "Notin operator - variable search" ) success() /* End of file */ tests/core/testsuite/complexexpr.fal000066400000000000000000000033741176363201700202500ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 2b * Category: expression * Subcategory: * Short: Complex expression * Description: * As test 2a, this test checks for expressions to work, but it uses complex * sub expression items, in this case function calls that are found in the * testsuite module, to check for complex item resolution (i.e. loading an * expression item in A and then checking it). * * This leverages on conditionals. * [/Description] * ****************************************************************************/ // basic expr a = 1 a = a == testReflect( 2 ) if a: failure( "Assignment disambiguation 1 - simple/complex" ) a = 1 a = testReflect( a ) == 2 if a: failure( "Assignment disambiguation 2 - complex/simple" ) a = 1 a = testReflect( a ) == testReflect( 2 ) if a: failure( "Assignment disambiguation 3 - complex/complex" ) // basic math a = 3 + testReflect( 4 ) * 2 - 1 * testReflect( 3 ) * ( 2 + 1 ) if a != 2: failure( "Math priortiy" ) // connector expr b = testReflect( 1 ) and 0 or testReflect( 1 ) and 2 if not b: failure( "Connector expression 1 complex/simple mix" ) b = 0 b = 1 and testReflect( 0 ) or 1 and testReflect( 2 ) if not b: failure( "Connector expression 2 complex/simple mix" ) b = 0 b = testReflect( 1 ) and testReflect( 0 ) or testReflect( 1 ) and testReflect( 2 ) if not b: failure( "Connector expression 3 all complex" ) b = 0 testReflect( b ) or testReflect( 1 ) and testReflect( 0 ) or (b = testReflect( 2 )) if b != 2: failure( "Connector expression 4: connectors and let, all complex") b = testReflect( 0 ) or testReflect( 1 ) and not testReflect( 3 ) or testReflect( 1 ) if not b: failure( "Connector expression 5: not operator") success() /* End of file */ tests/core/testsuite/complexswitch.fal000066400000000000000000000036771176363201700206010ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 17e * Category: switch * Subcategory: * Short: Complex switch * Description: * Uses variable switch entries * [/Description] * ****************************************************************************/ glob1 = 0 glob2 = printl glob3 = "hello" glob4 = "world" function call_sw( param ) switch param case nil return 0 case "one", 1 to 2, 5 return 1 case glob1 return 2 case glob2, glob3 return 3 case glob4, "two", 6, 10 to 12 return 4 default return 10 end end if call_sw( nil ) != 0: failure("case 0") if call_sw( 1 ) != 1: failure("case 1 / 1") if call_sw( "one" ) != 1: failure("case 1 / 2") if call_sw( 2 ) != 1: failure("case 1 / 3") if call_sw( 5 ) != 1: failure("case 1 / 4") if call_sw( glob1 ) != 2: failure( "case 2 / 1" ) if call_sw( 0 ) != 2: failure( "case 2 / 2" ) glob1 = "g1" if call_sw( glob1 ) != 2: failure( "case 2 / 3" ) if call_sw( "g1" ) != 2: failure( "case 2 / 4" ) if call_sw( glob2 ) != 3: failure( "case 3 / 1" ) if call_sw( printl ) != 3: failure( "case 3 / 2" ) glob2 = "g2" if call_sw( glob2 ) != 3: failure( "case 3 / 3" ) if call_sw( "g2" ) != 3: failure( "case 3 / 4" ) if call_sw( glob3 ) != 3: failure( "case 3 / 5" ) if call_sw( "hello" ) != 3: failure( "case 3 / 6" ) glob2 = "g3" if call_sw( glob3 ) != 3: failure( "case 3 / 7" ) if call_sw( "g3" ) != 3: failure( "case 3 / 8" ) if call_sw( glob4 ) != 4: failure( "case 4 / 1" ) if call_sw( "world" ) != 4: failure( "case 4 / 2" ) if call_sw( "two" ) != 4: failure( "case 4 / 3" ) if call_sw( 6 ) != 4: failure( "case 4 / 4" ) if call_sw( 11 ) != 4: failure( "case 4 / 5" ) if call_sw( 16 ) != 10: failure( "default / 1" ) if call_sw( "none" ) != 10: failure( "default / 2" ) glob4 = "none" if call_sw( "world" ) != 10: failure( "default / 3" ) success() /* End of file */ tests/core/testsuite/connectors.fal000066400000000000000000000017311176363201700200520ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 1f * Category: basic * Subcategory: connectors * Short: Connectors and logical expressions * Description: * An important test, as connectors are often needed in tests. * The let operator is needed, so the test 1e must be passed * * [/Description] * ****************************************************************************/ a = 1 b = 0 // expected b = 1 (a == 1) and ( b = b + 1 ) or failure("A point") // expected b = 2 a == 2 or ( b = b + 1) // expected b = 3 (a == 1 and (b = b + 1)) // expected, everything as it is now a == 1 and a != 2 or failure( "B point" ) // b = 4 not a == 2 and (b = b + 1) or failure( "C point" ) // a true a = (1 or 0) and (0 or 1) if not a failure( "D point" ) elif b != 4 failure( "E point" ) end a = 0 if not (a = 1 + 1 ) failure( "F point" ) end a = 1 if (a = 0) failure( "G point" ) end success() /* End of file */ tests/core/testsuite/continuations.fal000066400000000000000000000016241176363201700205730ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 140a * Category: rtl * Subcategory: continuation * Short: Continuations * Description: * Checks for correct working of continuations. * [/Description] * ****************************************************************************/ function contd( values, cc ) value = "" for i in [0:values.len()] value += values[i] cc( [i,value] ) end return nil end cc = Continuation( contd ) while (elem = cc( .['a' 'b' 'c' 'd' 'e'] )) t, v = elem switch t case 0 if v != 'a': failure( "Loop 1" ) case 1 if v != 'ab': failure( "Loop 2" ) case 2 if v != 'abc': failure( "Loop 3" ) case 3 if v != 'abcd': failure( "Loop 4" ) case 4 if v != 'abcde': failure( "Loop 5" ) end end success() /* End of file */ tests/core/testsuite/core_callable.fal000066400000000000000000000023001176363201700204350ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 40b * Category: rtl * Subcategory: core * Short: isCallable test * Description: * Tests for predefined constants and isCallable core module function. * [/Description] * ****************************************************************************/ class testClass end object testObj function testMethod() end end function func() end if isCallable( nil ) : failure( "Nil type" ) if isCallable( 1 ) : failure( "Integer type" ) if isCallable( 1.1 ) : failure( "Numberic type" ) if isCallable( [1:2] ) : failure( "Range type" ) if not isCallable( func ) : failure( "Function type" ) if isCallable( "hello" ) : failure( "String type" ) if isCallable( [1,2] ) : failure( "Array type" ) if isCallable( [1 => 2 ] ) : failure( "Dictionary type" ) if isCallable( testObj ) : failure( "Object type / automatic" ) if isCallable( testClass() ) : failure( "Object type / created" ) if not isCallable( testClass ) : failure( "Class type" ) if not isCallable( testObj.testMethod ) : failure( "Method type" ) if not isCallable( failure ) : failure( "External (library) function type" ) success() /* End of file */ tests/core/testsuite/core_complex.fal000066400000000000000000000024541176363201700203570ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 131a * Category: rtl * Subcategory: core * Short: Complex() test * Description: * Basic tests for Complex() class. * [/Description] * ****************************************************************************/ if Complex( ) != Complex( ): failure( "Complex( )" ) if Complex( 1 ).real != 1: failure( "Complex( 1 )" ) if Complex( 0, -5 ).imag != -5: failure( "Complex( 0, -5)" ) if Complex( 0.3, 1.3 ).real != 0.3: failure( "Complex( 0.3 )" ) if Complex( 1234567891234,0 ).real != 1234567891234: failure( "Complex( 1234567891234, 0 )" ) if Complex( 0,-1234567891234 ).imag != -1234567891234: failure( "Complex( 0,-1234567891234 )" ) if Complex( 1, 0 ).abs() != 1.0: failure( "Complex( 1, 0 ) " ) if Complex( 0, -1 ).abs() != 1.0: failure( "Complex( 0, -1 ) " ) if Complex( 2, 3 ).toString() != "2 , 3i": failure( "Complex().toString()" ) if Complex( 2, 3 ) + Complex(1,2) != Complex(3,5): failure( "Complex() + operator" ) if Complex( 2, 3 ) - Complex(1,2) != Complex(1,1): failure( "Complex() - operator" ) if Complex( 2, 3 ) * Complex(1,2) != Complex(-4,7): failure( "Complex() * operator" ) if Complex( 2, 3 ) / Complex(1,2) != Complex(1.6,-0.2): failure( "Complex() / operator" ) success() /* End of file */ tests/core/testsuite/core_complex_err.fal000066400000000000000000000017021176363201700212220ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 131b * Category: rtl * Subcategory: core * Short: Complex() error test * Description: * Tests on error detection in complex class- * [/Description] * ****************************************************************************/ // Check if the constructor detects invalid parameters. try Complex( 'a', 0 ) failure( "Wrong first parameter not detected" ) catch ParamError // ok end // Check if the constructor detects invalid parameters. try Complex( 0, 'b' ) failure( "Wrong second parameter not detected" ) catch ParamError // ok end // Check if we generate a math error on divisions by zero. try divisor = Complex( 0, 0 ) d = Complex( 2, 3 ) n = d / divisor // an error in the code should make this test to crash. failure( "division by zero not detected" ) catch MathError // ok end success() /* End of file */ tests/core/testsuite/core_getProperty.fal000066400000000000000000000063721176363201700212370ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 40g * Category: rtl * Subcategory: core * Short: getProperty * Description: * The core function getProperty allows to access an object property * by name. * [/Description] * ****************************************************************************/ class testClass prop1 = "value1" function func1() return "func1" end end object testObj prop2 = "value2" function testMethod() return "testMethod" end end cls = testClass() array = [] array.prop1 = "orig value" array.mth = function(); return "testMethod"; end bd = bless([ "prop1" => "orig value", "mth" => {=>return "testMethod"} ] ) try prop1 = getProperty( cls, "prop1" ) if prop1 != "value1": failure( "Get property on class" ) catch failure( "Property access on class" ) end try method = getProperty( cls, "func1" ) if not isCallable( method ): failure( "Method retrival on class" ) value = method() if value != "func1": failure( "Method intdirect call on class" ) catch failure( "Method access on class" ) end try prop2 = getProperty( testObj, "prop2" ) if prop2 != "value2": failure( "Get property on object" ) catch failure( "Property access on object" ) end try method2 = getProperty( testObj, "testMethod" ) if not isCallable( method2 ): failure( "Method retrival on object" ) value2 = method2() if value2 != "testMethod": failure( "Method intdirect call on object" ) catch failure( "Method access on object" ) end //====================================================== try prop1 = getProperty( array, "prop1" ) if prop1 != "orig value": failure( "Get property on array" ) catch failure( "Property access on array" ) end try mth = getProperty( array, "mth" ) if not isCallable( mth ): failure( "Method retrival on array" ) value2 = mth() if value2 != "testMethod": failure( "Method intdirect call on array" ) catch failure( "Method access on array" ) end //====================================================== try prop1 = getProperty( bd, "prop1" ) if prop1 != "orig value": failure( "Get property on dict" ) catch failure( "Property access on dict" ) end try mth = getProperty( bd, "mth" ) if not isCallable( mth ): failure( "Method retrival on dict" ) value2 = mth() if value2 != "testMethod": failure( "Method intdirect call on dict" ) catch failure( "Method access on dict" ) end //====================================================== // negative tests. try getProperty( cls, "not here!!" ) failure( "Exception not raised for classes" ) catch in error if error.code != 33: failure( "Wrong exception raised for classes" ) end try getProperty( testObj, "not here!!" ) failure( "Exception not raised for object" ) catch in error if error.code != 33: failure( "Wrong exception raised for object" ) end try getProperty( array, "not here!!" ) failure( "Exception not raised for array" ) catch in error if error.code != 33: failure( "Wrong exception raised for array" ) end try getProperty( bd, "not here!!" ) failure( "Exception not raised for dict" ) catch in error if error.code != 33: failure( "Wrong exception raised for dict" ) end success() /* End of file */ tests/core/testsuite/core_int.fal000066400000000000000000000023241176363201700174760ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 40d * Category: rtl * Subcategory: core * Short: int() test * Description: * Test for int() conversion function. * [/Description] * ****************************************************************************/ if int( 0 ) != 0: failure( "int( 0 )" ) if int( 1 ) != 1: failure( "int( 1 )" ) if int( -1 ) != -1: failure( "int( -1 )" ) if int( 0.3 ) != 0: failure( "int( 0.3 )" ) if int( 15.2 ) != 15: failure( "int( 15.2 )" ) if int( -15.2 ) != -15: failure( "int( -15.2 )" ) if int( 1234567891234 ) != 1234567891234: failure( "int( 1234567891234 )" ) if int( -1234567891234 ) != -1234567891234: failure( "int( -1234567891234 )" ) if int( "0" ) != 0: failure( "int( \"0\" )" ) if int( "1" ) != 1: failure( "int( \"1\" )" ) if int( "-1" ) != -1: failure( "int( \"-1\" )" ) if int( "0.3" ) != 0: failure( "int( \"0.3\" )" ) if int( "15.2" ) != 15: failure( "int( \"15.2\" )" ) if int( "-15.2" ) != -15: failure( "int( \"-15.2\" )" ) if int( "1234567891234" ) != 1234567891234: failure( "int( \"1234567891234\" )" ) if int( "-1234567891234" ) != -1234567891234: failure( "int( \"-1234567891234\" )" ) success() /* End of file */ tests/core/testsuite/core_numeric.fal000066400000000000000000000025461176363201700203540ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 40i * Category: rtl * Subcategory: core * Short: numeric() test * Description: * Test for numeric() conversion function. * [/Description] * ****************************************************************************/ if numeric( 0 ) != 0: failure( "numeric( 0 )" ) if numeric( 1 ) != 1: failure( "numeric( 1 )" ) if numeric( -1 ) != -1: failure( "numeric( -1 )" ) if numeric( 0.3 ) != 0.3: failure( "numeric( 0.3 )" ) if numeric( 15.2 ) != 15.2: failure( "numeric( 15.2 )" ) if numeric( -15.2 ) != -15.2: failure( "numeric( -15.2 )" ) if numeric( 1234567891234 ) != 1234567891234: failure( "numeric( 1234567891234 )" ) if numeric( -1234567891234 ) != -1234567891234: failure( "numeric( -1234567891234 )" ) if numeric( "0" ) != 0: failure( "numeric( \"0\" )" ) if numeric( "1" ) != 1: failure( "numeric( \"1\" )" ) if numeric( "-1" ) != -1: failure( "numeric( \"-1\" )" ) if numeric( "0.3" ) != 0.3: failure( "numeric( \"0.3\" )" ) if numeric( "15.2" ) != 15.2: failure( "numeric( \"15.2\" )" ) if numeric( "-15.2" ) != -15.2: failure( "numeric( \"-15.2\" )" ) if numeric( "1234567891234" ) != 1234567891234: failure( "numeric( \"1234567891234\" )" ) if numeric( "-1234567891234" ) != -1234567891234: failure( "numeric( \"-1234567891234\" )" ) success() /* End of file */ tests/core/testsuite/core_objToString.fal000066400000000000000000000030161176363201700211470ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 40f * Category: rtl * Subcategory: core * Short: object toString * Description: * Test for automatic calling of toString methods on objects. * Also checks for basic application of formats and personalized object formats. * [/Description] * ****************************************************************************/ class first prop = "first" function toString() return self.prop end end class second from first prop1 = "second" function toString() return self.prop1 + ":" + self.prop end end object obj1 from second function toString() return "obj1:" + second.toString() end end object obj2 from second prop = "obj2" end object obj3 function toString() return "obj3" end end object obj4 from second function toString( format ) if format return "obj4" + ":" + first.toString() + "/" + format else return "obj4" + ":" + first.toString() end end end inst1 = first() inst2 = second() if toString( inst1 ) != "first": failure( "First instance" ) if toString( inst2 ) != "second:first": failure( "Second instance" ) if toString( obj1 ) != "obj1:second:first": failure( "Obj1" ) if toString( obj2 ) != "second:obj2": failure( "Obj2" ) if toString( obj3 ) != "obj3": failure( "Obj3" ) if toString( obj4 ) != "obj4:first": failure( "Obj4" ) if toString( obj4, "20rp.|fmt!!" ) != "....obj4:first/fmt!!": failure( "Obj4 + fmt" ) success() /* End of file */ tests/core/testsuite/core_ordchr.fal000066400000000000000000000012601176363201700201630ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 40c * Category: rtl * Subcategory: core * Short: chr/ord test * Description: * Test for character-to-integer conversion functions. * [/Description] * ****************************************************************************/ if ord( "a" ) != ord( "b" ) - 1: failure("ord") if chr( 10 ) != "\n": failure( "chr" ) if ord( chr( 10 ) ) != 10: failure( "ord(chr)" ) if chr( ord( "\n" ) ) != "\n": failure( "chr(ord)" ) try ord( 10 ) failure( "ord(10): error not raised" ) end try chr( "a" ) failure( "chr(\"a\"): error not raised" ) end success() /* End of file */ tests/core/testsuite/core_setProperty.fal000066400000000000000000000035401176363201700212450ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 40h * Category: rtl * Subcategory: core * Short: setProperty * Description: * The core function setProperty allows to change an object property * by name. * [/Description] * ****************************************************************************/ class testClass prop1 = "value1" end object testObj prop2 = "value2" end cls = testClass() array = [] array.prop1 = "orig value" bd = bless([ "prop1" => "orig value"] ) try prop1 = setProperty( cls, "prop1", "new value" ) if cls.prop1 != "new value": failure( "Set property on class instance" ) catch failure( "Property access on class" ) end try prop2 = setProperty( testObj, "prop2", "new value" ) if testObj.prop2 != "new value": failure( "Set property on object" ) catch failure( "Property access on object" ) end try setProperty( bd, "prop1", "new value" ) if bd.prop1 != "new value": failure( "Set property on dictionary" ) catch failure( "Property access dictionary" ) end try setProperty( array, "prop1", "new value" ) if array.prop1 != "new value": failure( "Set property on array" ) catch failure( "Property access array" ) end // Negative tests. try setProperty( cls, "not here!!", "arrg" ) failure( "Exception not raised for classes" ) catch in error if error.code != 33: failure( "Wrong exception raised for classes" ) end try setProperty( testObj, "not here!!", "arrg" ) failure( "Exception not raised for object" ) catch in error if error.code != 33: failure( "Wrong exception raised for object" ) end // arrays can have new property set try setProperty( array, "prop2", "new value" ) if array.prop2 != "new value": failure( "Set new property on array" ) catch failure( "Property addition array" ) end success() /* End of file */ tests/core/testsuite/core_tostring.fal000066400000000000000000000025001176363201700205510ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 40e * Category: rtl * Subcategory: core * Short: toString * Description: * Test for conversion into strings * [/Description] * ****************************************************************************/ if toString( nil ) != "Nil": failure( "toString( nil )" ) if toString( "a string" ) != "a string": failure( "isomorphism" ) if toString( 0 ) != "0": failure( "toString( 0 )" ) if toString( 15 ) != "15": failure( "toString( 15 )" ) if toString( -15 ) != "-15": failure( "toString( -15 )" ) if toString( 129319293194124 ) != "129319293194124": failure( "toString( 129319293194124 )" ) if toString( -129319293194124 ) != "-129319293194124": failure( "toString( -129319293194124 )" ) if toString( 12.31234, ".5" ) != "12.31234": failure( "toString( 12.31234, 5 )" ) if toString( 12.31234, ".2" ) != "12.31": failure( "toString( 12.31234, 2 )" ) if toString( -12.31234, ".5" ) != "-12.31234": failure( "toString( -12.31234, 5 )" ) if toString( -12.31234, ".2" ) != "-12.31": failure( "toString( -12.31234, 2 )" ) if toString( [1:2] ) != "[1:2]": failure( "toString( [1:2] )" ) if toString( [-1:2] ) != "[-1:2]": failure( "toString( [-1:2] )" ) if toString( [1:] ) != "[1:]": failure( "toString( [1:] )" ) success() /* End of file */ tests/core/testsuite/core_typeof.fal000066400000000000000000000023331176363201700202120ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 40a * Category: rtl * Subcategory: core * Short: TypeOf test * Description: * Tests for predefined constants and typeOf core module function. * [/Description] * ****************************************************************************/ class testClass end object testObj function testMethod() end end function func() end if typeOf( nil ) != NilType: failure( "Nil type" ) if typeOf( 1 ) != NumericType: failure( "Integer (NumericType) type" ) if typeOf( 1.1 ) != NumericType: failure( "Numberic type" ) if typeOf( [1:2] ) != RangeType: failure( "Range type" ) if typeOf( func ) != FunctionType: failure( "Function type" ) if typeOf( "hello" ) != StringType: failure( "String type" ) if typeOf( [1,2] ) != ArrayType: failure( "Array type" ) if typeOf( [1 => 2 ] ) != DictionaryType: failure( "Dictionary type" ) if typeOf( testObj ) != ObjectType: failure( "Object type / automatic" ) if typeOf( testClass() ) != ObjectType: failure( "Object type / created" ) if typeOf( testClass ) != ClassType: failure( "Class type" ) if typeOf( testObj.testMethod ) != MethodType: failure( "Method type" ) success() /* End of file */ tests/core/testsuite/coro_yout.fal000066400000000000000000000007721176363201700177230ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 23c * Category: statements * Subcategory: launch * Short: Coroutine with yieldOut * Description: * Checks for yieldOut to actually quit the machine. * [/Description] * ****************************************************************************/ function coro1() coro2() failure( "Yieldout didn't work" ) end function coro2() yieldOut() end launch coro1() success() /* End of file */ tests/core/testsuite/corobase.fal000066400000000000000000000006311176363201700174700ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 23a * Category: statements * Subcategory: launch * Short: Minimal coroutines * Description: * Coroutines smoke test * [/Description] * ****************************************************************************/ function coroutine() return 0 end launch coroutine() success() /* End of file */ tests/core/testsuite/coromass.fal000066400000000000000000000041331176363201700175220ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 23d * Category: statements * Subcategory: launch * Short: Massive coroutines * Description: * Check if coroutines are exited correctly after many loops. * [/Description] * ****************************************************************************/ const coroCount = 4 const loopCount = 10000 //================================================== // Global variables ended = 0 counted = 0 //================================================= // Updater thread // function updater( waiter, coroutineArray ) global counted while true //wait for new updates if ended < coroCount waiter.wait() counted++ else // a break would do too here. yieldOut() end end end //=============================================== // coroutine class // class coroutine( param, su, se ) id = param sem_updates = su sem_ended = se value = 0 // ======================= // Thread method // (could have any name) function run( param ) for i = 1 to loopCount self.value = i self.sem_updates.post() end // access rw the global variable global ended ended ++ // warn both listening threads self.sem_updates.post() self.sem_ended.post() end end //======================================================== // Main program // our semaphores end_semaphore = Semaphore() upd_semaphore = Semaphore() // we save the coroutine objects here coro_array = [] for i in [0 : coroCount] coro = coroutine( i, upd_semaphore, end_semaphore ) coro_array += coro launch coro.run( i ) end // Functions can be started normally launch updater( upd_semaphore, coro_array ) // wait for all the threads to end while ended < coroCount end_semaphore.wait() end if ended != coroCount: failure( "Ended count" ) if counted == 0: failure( "Updater never called" ) for coro in coro_array if coro.value != loopCount: failure( "Count for coroutine " + coro.id ) end success() /* end of file */ tests/core/testsuite/coroutine.fal000066400000000000000000000014561176363201700177100ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 23b * Category: statements * Subcategory: launch * Short: Basic coroutines * Description: * Coroutines basic test. This test can either pass or hang the VM. * [/Description] * ****************************************************************************/ function coroutine( semaphore, count ) // force swapping, as we are waiting on the semaphore semaphore.wait() // we don't want to be interrupted now beginCritical() count ++ // not necessary, as we are going to be done, but... endCritical() end semaphore = Semaphore() counter = 0 for i in [0:7] launch coroutine( semaphore, $counter ) end semaphore.post( 7 ) while counter != 7 yield() end success() /* End of file */ tests/core/testsuite/decrement.fal000066400000000000000000000023611176363201700176430ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 5b * Category: expression * Subcategory: incdec * Short: Decrement * Description: * Testing decrement operator under various conditions. * [/Description] * ****************************************************************************/ a = 2 a-- if a != 1: failure( "Basic decrement postfix" ) --a if a != 0: failure( "Basic decrement prefix" ) a = 1 b = --a + 1 // should be 1 if b != 1: failure( "Prefix value retrival in expression -- 1" ) if a != 0: failure( "Prefix effect -- 1" ) a = 1 b = a-- + 1 if b != 2: failure( "Postfix value retrival in expression -- 1" ) if a != 0: failure( "Postfix effect -- 1" ) a = 1 b = 2 * --a +1 if b != 1: failure( "Prefix value retrival in expression -- 2" ) if a != 0: failure( "Prefix effect -- 2" ) a = 1 b = 2 * a-- +1 if b != 3: failure( "Postfix value retrival in expression -- 2" ) if a != 0: failure( "Postfix effect -- 2" ) a = 1 if --a != 0: failure( "Prefix value retrival in conditional" ) if a != 0: failure( "Prefix effect in conditional" ) a = 1 if a-- != 1: failure( "Postfix value retrival in conditional" ) if a != 0: failure( "Postfix effect in conditional" ) success() /* End of file */ tests/core/testsuite/deep_unpack.fal000066400000000000000000000010041176363201700201440ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 33e * Category: expression * Subcategory: deep * Short: Deep indirect unpack * Description: * Tests if n,m = v works also when complex expressions are involved. * [/Description] * ****************************************************************************/ object a b = [0,0] end v = [1,2] a.b[0], a.b[1] = v if a.b[0] != 1 or a.b[1] != 2 failure( "Deep unpack" ) end success() /* end of file */ tests/core/testsuite/deeparr.fal000066400000000000000000000017721176363201700173240ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 11c * Category: types * Subcategory: array * Short: Deep array * Description: * Verify the correctness of deep array access * Using only integers in this test * [/Description] * ****************************************************************************/ // assignment test - include a compilation test array = [ [ [1,2], [3,4] ], [ [5, 6], [7, 8] ], [ [9, 10], [11, 12] ] ] arr2 = [ [1,2], [2,1] ] if array[1][1][1] != 8: failure( "Deep access" ) array[2][0][1] = 100 if array[2][0][1] != 100: failure( "Deep assign" ) if array[ arr2[1][0] ][1][0] != 11: failure( "Deep complex access" ) array[ arr2[1][0] ][1][0] = 100 if array[ arr2[1][0] ][ 10 - 9 ][ 0 * 1 ] != 100: failure( "Deep complex assign" ) elem = array[ arr2[1][0] ] [1] if elem[0] != 100: failure( "Array loading access" ) elem[1] = 110 if array[ arr2[1][0] ][1][1] != 110: failure( "Array loading assign" ) success() /* End of file */ tests/core/testsuite/deepauto.fal000066400000000000000000000017551176363201700175110ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 31d * Category: expression * Subcategory: deep * Short: Deep released expression * Description: * This test is meant to check for correct release of the expression context * in case of expressions that require LD[P|V]R opcode ( load property or * vector by reference). This reference value is teporary stored in A, but * it must be reset at expression end or next assignment to A may actually cause * the referenced value to change. * [/Description] * ****************************************************************************/ object zero prop_array = [] end zero.prop_array += 1 a_variable1 = "name" if zero.prop_array.typeId() != ArrayType: failure( "Property conformance") vector = [[0]] vector[0][0] += 1 a_variable2 = "name" if vector[0][0] != 1: failure( "Array conformance") if a_variable1 != a_variable2: failure( "Assignment conformance" ) success() /* End of file */ tests/core/testsuite/deepclass.fal000066400000000000000000000025611176363201700176420ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 21b * Category: types * Subcategory: classes * Short: Deep class access * Description: * This script tests for deep class access and class/vector access mix. * [/Description] * ****************************************************************************/ class zero zero_one = nil zero_two = nil end class one one_one = nil one_two = nil end class two two_one = nil two_two = nil end class three three_one = nil three_two = nil end o_zero = zero() o_one = one() o_two = two() o_three = three() o_zero.zero_one = o_one o_one.one_one = o_two o_two.two_one = o_three o_three.three_one = "Final" if o_zero.zero_one.one_one.two_one.three_one != "Final": failure( "Deep access" ) o_zero.zero_one.one_one.two_one.three_one = "Changed" if o_zero.zero_one.one_one.two_one.three_one != "Changed": failure( "Deep modify" ) if o_three.three_one != "Changed": failure( "Deep modify reference" ) o_zero.zero_two = [ o_one, o_two, o_three] if o_zero.zero_two[ 1 ].two_one.three_one != "Changed": failure( "Deep access w/array" ) o_zero.zero_two[ 1 ].two_one.three_one = "Again" if o_zero.zero_two[ 1 ].two_one.three_one != "Again": failure( "Deep modify w/array" ) if o_three.three_one != "Again": failure( "Deep modify ref w/array" ) success() /* End of file */ tests/core/testsuite/deepclass_arr.fal000066400000000000000000000014661176363201700205110ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 21n * Category: types * Subcategory: classes * Short: Deep loop store * Description: * Test for deep storage and retrival of complex data * [/Description] * ****************************************************************************/ class Test p = nil end // prepare the classes classes = [] for i in [0:10]: classes += Test() // get the instances and fill them for i in [0:10] classes[i].p = [ i, "" + i, "Test " + toString( i ) ] end // test it for i in [0:10] if classes[i].p[0] != i: failure( "First element on " + i ) if classes[i].p[1] != ""+i: failure( "Second element on " + i ) if classes[i].p[2] != "Test " + i: failure( "Third element on " + i ) end success() /* End of file */ tests/core/testsuite/dict_comp.fal000066400000000000000000000050671176363201700176440ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 121b * Category: comp * Subcategory: dict * Short: Comprehension on dictionaries * Description: * Comprehension is an expression or expression set generating * lists or sets. * * Falcon comprehension is quite articulated, as it is composed of * .comp( , [] ) * * where is the target sequence, is a generating * item (range, source sequence or generator function) and filter may * transform the data in the item on the fly or discard it. * * This series of test is aimed to test comprehension on dictionaries. * [/Description] * ****************************************************************************/ // direct comprehension a = [=>].comp( .[ .['c' 1] .['b' 2] .['a' 3] ] ) if a.len() != 3: failure( "Direct comprehension, size" ) if a['a'] != 3: failure( "Direct comprehension, 'a'" ) if a['b'] != 2: failure( "Direct comprehension, 'b'" ) if a['c'] != 1: failure( "Direct comprehension, 'c'" ) // indirect a = [=>].comp( ['c','b','a'], { letter=> [letter, ord(letter)] } ) if a.len() != 3: failure( "Indirect comprehension, size" ) if a['a'] != ord('a'): failure( "Indirect comprehension, 'a'" ) if a['b'] != ord('b'): failure( "Indirect comprehension, 'b'" ) if a['c'] != ord('c'): failure( "Indirect comprehension, 'c'" ) // indirect a = [=>].comp( ['c','b',0,'a'], { letter=> letter == 0 ? oob(1) : [letter, ord(letter)] } ) if a.len() != 3: failure( "Indirect comprehension, size" ) if a['a'] != ord('a'): failure( "Indirect comprehension, 'a'" ) if a['b'] != ord('b'): failure( "Indirect comprehension, 'b'" ) if a['c'] != ord('c'): failure( "Indirect comprehension, 'c'" ) // ranged comprehension a = [=>].comp( [ ord('a'): ord('d')], { ln => [chr(ln), ln] } ) if a.len() != 3: failure( "Ranged comprehension, size" ) if a['a'] != ord('a'): failure( "Ranged comprehension, 'a'" ) if a['b'] != ord('b'): failure( "Ranged comprehension, 'b'" ) if a['c'] != ord('c'): failure( "Ranged comprehension, 'c'" ) // ranged comprehension function gengen() val = ord('a') gen = function() v = val val += 1 if chr(val) == 'e': return oob(0) return v end return gen end a = [=>].comp( gengen(), { ln => [chr(ln), ln] } ) if a.len() != 3: failure( "Generator comprehension, size" ) if a['a'] != ord('a'): failure( "Generator comprehension, 'a'" ) if a['b'] != ord('b'): failure( "Generator comprehension, 'b'" ) if a['c'] != ord('c'): failure( "Generator comprehension, 'c'" ) success() /* end of file */ tests/core/testsuite/dict_pageSub.fal000066400000000000000000000030141176363201700202620ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 12f * Category: types * Subcategory: dictionary * Short: Subtract page dictionary * Description: * Checks working of subtract operator on dictionaries (using page dictionaries ) * [/Description] * ****************************************************************************/ // assignment test - include a compilation test dict = PageDict() dict[2] = "two" dict[0] = "two" dict[1] = "two" dict["alpha"] = 100 dict["beta"] = 200 // remove an item dict1 = dict - "beta" if dict1.len() != 4: failure( "remove one item - size" ) if "beta" in dict1 : failure( "remove one item - content" ) // remove an array dict2 = dict - [2, "alpha"] if dict2.len() != 3: failure( "remove array - size" ) if 2 in dict2 or "alpha" in dict2: failure( "remove array - content" ) // invariance if 2 notin dict or "alpha" notin dict or "beta" notin dict failure( "remove invariance" ) end // now for self operators dict -= "beta" if dict.len() != 4: failure( "self remove one item - size" ) if "beta" in dict : failure( "self remove one item - content" ) dict -= "beta" if dict.len() != 4: failure( "self remove one item again - size" ) if "beta" in dict : failure( "self remove one item again - content" ) // remove an array dict -= [2, "alpha", "beta" ] // try also to remove an item removed if dict.len() != 2: failure( "self remove array - size" ) if 2 in dict or "alpha" in dict: failure( "self remove array - content" ) success() /* End of file */ tests/core/testsuite/dict_sub.fal000066400000000000000000000027211176363201700174710ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 12e * Category: types * Subcategory: dictionary * Short: Subtract dictionary * Description: * Checks working of subtract operator on dictionaries * [/Description] * ****************************************************************************/ // assignment test - include a compilation test dict = [ 2 => "two", 0 => "zero", 1 => "one", "alpha" => 100, "beta" => 200 ] // remove an item dict1 = dict - "beta" if dict1.len() != 4: failure( "remove one item - size" ) if "beta" in dict1 : failure( "remove one item - content" ) // remove an array dict2 = dict - [2, "alpha"] if dict2.len() != 3: failure( "remove array - size" ) if 2 in dict2 or "alpha" in dict2: failure( "remove array - content" ) // invariance if 2 notin dict or "alpha" notin dict or "beta" notin dict failure( "remove invariance" ) end // now for self operators dict -= "beta" if dict.len() != 4: failure( "self remove one item - size" ) if "beta" in dict : failure( "self remove one item - content" ) dict -= "beta" if dict.len() != 4: failure( "self remove one item again - size" ) if "beta" in dict : failure( "self remove one item again - content" ) // remove an array dict -= [2, "alpha", "beta" ] // try also to remove an item removed if dict.len() != 2: failure( "self remove array - size" ) if 2 in dict or "alpha" in dict: failure( "self remove array - content" ) success() /* End of file */ tests/core/testsuite/dictionary.fal000066400000000000000000000023011176363201700200340ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 12a * Category: types * Subcategory: dictionary * Short: Basic dictionary * Description: * Checks the basic dictionary functionalities with integers * [/Description] * ****************************************************************************/ // assignment test - include a compilation test dict = [ 2 => "two", 0 => "zero", 1 => "one" ] // minimal access if dict[0] != "zero": failure( "Minimal access" ) // Changing items dict[1] = "first" if dict[1] != "first": failure( "Assignment" ) // item addition dict = dict + [ 3 => "three" ] if dict[3] != "three": failure( "Addition" ) // item change and addition dict += [ 3 => "third", 4 => "four" ] if dict[3] != "third" or dict[4] != "four": failure( "Addition/modify" ) //in/notin if not (1 in dict): failure( "In operator - positive" ) if "not present" in dict: failure( "In operator - negative" ) if 1 notin dict: failure( "Notin operator - negative" ) if not "not present" notin dict: failure( "Notin operator - positive" ) if len( dict ) != 5: failure( "Len core function" ) if dict.len() != 5: failure( "Len FBOM" ) success() /* End of file */ tests/core/testsuite/dictloop.fal000066400000000000000000000041061176363201700175110ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 15g * Category: loops * Subcategory: forin * Short: For in dictionary * Description: * Checks for loops on specially built dictionaries: with 0 to 3 elements. * This test checks for in loops without for first/for last clauses * [/Description] * ****************************************************************************/ // Empty dictionary dict = [=>] keys = [] values = [] try for key, value in dict keys += key values += value end catch in error failure( "Empty dict: " + error.toString() ) end // 1 element dictionary dict = [ 1=> "a"] keys = [] values = [] try for key, value in dict keys += key values += value end catch in error failure( "1 element dict: " + error.toString() ) end if len( keys ) != 1: failure( "1 element dict: key retrival" ) if len( values ) != 1: failure( "1 element dict: value retrival" ) // 2 elements dictionary dict = [ 1 => "a", 2 => "b"] keys = [] values = [] try for key, value in dict keys += key values += value end catch in error failure( "2 elements dict: " + error.toString() ) end if len( keys ) != 2: failure( "2 elements dict: key retrival" ) if len( values ) != 2: failure( "2 elements dict: value retrival" ) // 3 elements dictionary dict = [ 1 => "a", 2 => "b", 3 => "c" ] keys = [] values = [] try for key, value in dict keys += key values += value end catch in error failure( "3 elements dict: " + error.toString() ) end if len( keys ) != 3: failure( "3 elements dict: key retrival" ) if len( values ) != 3: failure( "3 elements dict: value retrival" ) // 4 elements dictionary dict = [ 1 => "a", 2 => "b", 3 => "c", 4 => "d" ] keys = [] values = [] try for key, value in dict keys += key values += value end catch in error failure( "4 elements dict: " + error.toString() ) end if len( keys ) != 4: failure( "4 elements dict: key retrival" ) if len( values ) != 4: failure( "4 elements dict: value retrival" ) success() /* End of file */ tests/core/testsuite/dictloop2.fal000077500000000000000000000051371176363201700176030ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 15h * Category: loops * Subcategory: forin * Short: For in dictionary/2 * Description: * Checks for loops on specially built dictionaries: with 0 to 3 elements. * Checks forfirst clause. * [/Description] * ****************************************************************************/ // Empty dictionary dict = [=>] keys = [] values = [] keys_first = [] values_first = [] try for key, value in dict keys += key values += value forfirst keys_first += key values_first += value end end catch in error failure( "Empty dict: " + error.toString() ) end // 1 element dictionary dict = [ 1 => "a"] keys = [] values = [] keys_first = [] values_first = [] try for key, value in dict keys += key values += value forfirst keys_first += key values_first += value end end catch in error failure( "1 element dict: " + error.toString() ) end if len( keys ) != 1: failure( "1 element dict: key retrival" ) if len( values ) != 1: failure( "1 element dict: value retrival" ) if len( keys_first ) != 1: failure( "1 element dict for first: key retrival" ) if len( values_first ) != 1: failure( "1 element dict for first: value retrival" ) // 2 elements dictionary dict = [ 1 => "a", 2 => "b"] keys = [] values = [] keys_first = [] values_first = [] try for key, value in dict keys += key values += value forfirst keys_first += key values_first += value end end catch in error failure( "2 elements dict: " + error.toString() ) end if len( keys ) != 2: failure( "2 elements dict: key retrival" ) if len( values ) != 2: failure( "2 elements dict: value retrival" ) if len( keys_first ) != 1: failure( "2 element dict for first: key retrival" ) if len( values_first ) != 1: failure( "2 element dict for first: value retrival" ) // 3 elements dictionary dict = [ 1 => "a", 2 => "b", 3 => "c" ] keys = [] values = [] keys_first = [] values_first = [] try for key, value in dict keys += key values += value forfirst keys_first += key values_first += value end end catch in error failure( "3 elements dict: " + error.toString() ) end if len( keys ) != 3: failure( "3 elements dict: key retrival" ) if len( values ) != 3: failure( "3 elements dict: value retrival" ) if len( keys_first ) != 1: failure( "3 element dict for first: key retrival" ) if len( values_first ) != 1: failure( "3 element dict for first: value retrival" ) success() /* End of file */ tests/core/testsuite/dictloop3.fal000066400000000000000000000063101176363201700175730ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 15i * Category: loops * Subcategory: forin * Short: For in dictionary/3 * Description: * Checks for loops on specially built dictionaries: with 0 to 3 elements. * Checks forlast clause. * [/Description] * ****************************************************************************/ // Empty dictionary dict = [=>] keys = [] values = [] keys_last = [] values_last = [] try for key, value in dict keys += key values += value forlast keys_last += key values_last += value end end catch in error failure( "Empty dict: " + error.toString() ) end // 1 element dictionary dict = [ 1 => "a"] keys = [] values = [] keys_last = [] values_last = [] try for key, value in dict keys += key values += value forlast keys_last += key values_last += value end end catch in error failure( "1 element dict: " + error.toString() ) end if len( keys ) != 1: failure( "1 element dict: key retrival" ) if len( values ) != 1: failure( "1 element dict: value retrival" ) if len( keys_last ) != 1: failure( "1 element dict for last: key retrival" ) if len( values_last ) != 1: failure( "1 element dict for last: value retrival" ) // 2 elements dictionary dict = [ 1 => "a", 2 => "b"] keys = [] values = [] keys_last = [] values_last = [] try for key, value in dict keys += key values += value forlast keys_last += key values_last += value end end catch in error failure( "2 elements dict: " + error.toString() ) end if len( keys ) != 2: failure( "2 elements dict: key retrival" ) if len( values ) != 2: failure( "2 elements dict: value retrival" ) if len( keys_last ) != 1: failure( "2 element dict for last: key retrival" ) if len( values_last ) != 1: failure( "2 element dict for last: value retrival" ) // 3 elements dictionary dict = [ 1 => "a", 2 => "b", 3 => "c" ] keys = [] values = [] keys_last = [] values_last = [] try for key, value in dict keys += key values += value forlast keys_last += key values_last += value end end catch in error failure( "3 elements dict: " + error.toString() ) end if len( keys ) != 3: failure( "3 elements dict: key retrival" ) if len( values ) != 3: failure( "3 elements dict: value retrival" ) if len( keys_last ) != 1: failure( "3 element dict for last: key retrival" ) if len( values_last ) != 1: failure( "3 element dict for last: value retrival" ) // 4 elements dictionary dict = [ 1 => "a", 2 => "b", 3 => "c", 4 => "d" ] keys = [] values = [] keys_last = [] values_last = [] try for key, value in dict keys += key values += value forlast keys_last += key values_last += value end end catch in error failure( "4 elements dict: " + error.toString() ) end if len( keys ) != 4: failure( "4 elements dict: key retrival" ) if len( values ) != 4: failure( "4 elements dict: value retrival" ) if len( keys_last ) != 1: failure( "4 element dict for last: key retrival" ) if len( values_last ) != 1: failure( "4 element dict for last: value retrival" ) success() /* End of file */ tests/core/testsuite/dictloop4.fal000066400000000000000000000130631176363201700175770ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 15j * Category: loops * Subcategory: forin * Short: For in dictionary/4 * Description: * Checks for loops on specially built dictionaries: with 0 to 3 elements. * Checks forfirst and forlast clause. * [/Description] * ****************************************************************************/ // Empty dictionary dict = [=>] keys = [] values = [] keys_first = [] values_first = [] keys_last = [] values_last = [] try for key, value in dict keys += key values += value forfirst keys_first += key values_first += value end forlast keys_last += key values_last += value end end catch in error failure( "Empty dict: " + error.toString() ) end // 1 element dictionary dict = [ 1 => "a"] keys = [] values = [] keys_first = [] values_first = [] keys_last = [] values_last = [] keys_middle = [] values_middle = [] try for key, value in dict keys += key forfirst keys_first += key values_first += value end values += value forlast keys_last += key values_last += value end formiddle keys_middle += key values_middle += value end end catch in error failure( "1 element dict: " + error.toString() ) end if len( keys ) != 1: failure( "1 element dict: key retrival" ) if len( values ) != 1: failure( "1 element dict: value retrival" ) if len( keys_first ) != 1: failure( "1 element dict for first: key retrival" ) if len( values_first ) != 1: failure( "1 element dict for first: value retrival" ) if len( keys_last ) != 1: failure( "1 element dict for last: key retrival" ) if len( values_last ) != 1: failure( "1 element dict for last: value retrival" ) if len( keys_middle ) != 0: failure( "1 element dict for middle: key retrival" ) if len( values_middle ) != 0: failure( "1 element dict for middle: value retrival" ) // 2 elements dictionary dict = [ 1 => "a", 2 => "b"] keys = [] values = [] keys_first = [] values_first = [] keys_last = [] values_last = [] keys_middle = [] values_middle = [] try for key, value in dict keys += key values += value forfirst keys_first += key values_first += value end forlast keys_last += key values_last += value end formiddle keys_middle += key values_middle += value end end catch in error failure( "2 elements dict: " + error.toString() ) end if len( keys ) != 2: failure( "2 elements dict: key retrival" ) if len( values ) != 2: failure( "2 elements dict: value retrival" ) if len( keys_first ) != 1: failure( "2 element dict for first: key retrival" ) if len( values_first ) != 1: failure( "2 element dict for first: value retrival" ) if len( keys_last ) != 1: failure( "2 element dict for last: key retrival" ) if len( values_last ) != 1: failure( "2 element dict for last: value retrival" ) if len( keys_middle ) != 1: failure( "2 element dict for middle: key retrival" ) if len( values_middle ) != 1: failure( "2 element dict for middle: value retrival" ) // 3 elements dictionary dict = [ 1 => "a", 2 => "b", 3 => "c" ] keys = [] values = [] keys_first = [] values_first = [] keys_last = [] values_last = [] keys_middle = [] values_middle = [] try for key, value in dict keys += key values += value forfirst keys_first += key values_first += value end forlast keys_last += key values_last += value end formiddle keys_middle += key values_middle += value end end catch in error failure( "3 elements dict: " + error.toString() ) end if len( keys ) != 3: failure( "3 elements dict: key retrival" ) if len( values ) != 3: failure( "3 elements dict: value retrival" ) if len( keys_first ) != 1: failure( "3 element dict for first: key retrival" ) if len( values_first ) != 1: failure( "3 element dict for first: value retrival" ) if len( keys_last ) != 1: failure( "3 element dict for last: key retrival" ) if len( values_last ) != 1: failure( "3 element dict for last: value retrival" ) if len( keys_middle ) != 2: failure( "3 element dict for last: key retrival" ) if len( values_middle ) != 2: failure( "3 element dict for last: value retrival" ) // 4 elements dictionary dict = [ 1 => "a", 2 => "b", 3 => "c", 4 => "d" ] keys = [] values = [] keys_first = [] values_first = [] keys_last = [] values_last = [] keys_middle = [] values_middle = [] try for key, value in dict keys += key values += value forfirst keys_first += key values_first += value end forlast keys_last += key values_last += value end formiddle keys_middle += key values_middle += value end end catch in error failure( "4 elements dict: " + error.toString() ) end if len( keys ) != 4: failure( "4 elements dict: key retrival" ) if len( values ) != 4: failure( "4 elements dict: value retrival" ) if len( keys_first ) != 1: failure( "4 element dict for first: key retrival" ) if len( values_first ) != 1: failure( "4 element dict for first: value retrival" ) if len( keys_last ) != 1: failure( "4 element dict for last: key retrival" ) if len( values_last ) != 1: failure( "4 element dict for last: value retrival" ) if len( keys_middle ) != 3: failure( "4 element dict for last: key retrival" ) if len( values_middle ) != 3: failure( "4 element dict for last: value retrival" ) success() /* End of file */ tests/core/testsuite/dictloop5.fal000066400000000000000000000023451176363201700176010ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 15l * Category: loops * Subcategory: forin * Short: For in dictionary/5 * Description: * Checks for loops in dicitionary when setting and dropping elements. * [/Description] * ****************************************************************************/ // Empty dictionary dict = [ "A"=>1, "B" => 2, "C" => 3, "D" => 4 ] for key, value in dict try if key == "C" // focefully generate an exception printl( "first param", "second", "third", 9*nil ) end catch continue dropping end end /* if dict.len() != 3: failure( "third element removal - size" ) dict = [ "A"=>1, "B" => 2, "C" => 3, "D" => 4 ] for key, value in dict try if key == "D" // focefully generate an exception printl( "first param", "second", "third", 9 * nil ) end catch continue dropping end end if dict.len() != 3: failure( "last element removal - size" ) dict = [ "A"=>1 ] for key, value in dict try if key == "A" // focefully generate an exception printl( "first param", "second", "third", 9*nil ) end catch continue dropping end end if dict.len() != 0: failure( "removal of single element" ) */ success() /* End of file */ tests/core/testsuite/dictstrings.fal000066400000000000000000000012161176363201700202300ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 12g * Category: types * Subcategory: dictionaries * Short: String copy in dictionaries * Description: * Checks if deep dictionary assignment actually copy the string by value. * [/Description] * ****************************************************************************/ dict = [=>] // write string = "0000" dict["a"] = string string[0] = "1" if dict["a"] != "0000": failure( "dict write access" ) // insertion string = "0000" dict["b"] = string string[0] = "1" if dict["b"] != "0000": failure( "dict insertion" ) success() /* End of file */ tests/core/testsuite/dirtest.fal000066400000000000000000000051261176363201700173550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 110 * Category: rtl * Subcategory: dir * Short: Directory mangling * Description: * Testing the directory access api. * This file creates a directory (dirtest.dir), accesses it, * and tries to read back the files, and to create and remove some more. * [/Description] * ****************************************************************************/ // eventually remove the directory try dirRemove( "dirtest.dir" ) end // get current dir curdir = dirCurrent() filelen = [=>] filelen2 = [=>] try dirMake( "dirtest.dir" ) dirChange( "dirtest.dir" ) file = OutputStream( "one" ) file.write( "some data\n" ) filelen[ "one" ] = file.tell() file.close() file = OutputStream( "two" ) file.write( "some other data\n" ) filelen[ "two" ] = file.tell() file.close() file = OutputStream( "three" ) file.write( "some other data yet\n" ) filelen[ "three" ] = file.tell() file.close() file = OutputStream( "four" ) file.write( "some other data yet still\n" ) filelen[ "four" ] = file.tell() file.close() dir = Directory( "." ) element = dir.read() stats = FileStat() while element if stats.read( element ) filelen2[ element ] = stats.size else failure( "Can't retrieve stats for " + element ) end if element == "." or element == ".." if fileType( element ) != FileStat.DIR failure( "Filed to recognize a directory" ) end else if fileType( element ) != FileStat.NORMAL failure( "Filed to recognize a file" ) end end element = dir.read() end dir.close() catch in error failure( "Exception rised: " + error.toString() ) end if "one" notin filelen2: failure( "one not retreived" ) if "two" notin filelen2: failure( "two not retreived" ) if "three" notin filelen2: failure( "thre not retreived" ) if "four" notin filelen2: failure( "four not retreived" ) if filelen["one"] != filelen2["one"]: failure( "wrong size 1" ) if filelen["two"] != filelen2["two"]: failure( "wrong size 2" ) if filelen["three"] != filelen2["three"]: failure( "wrong size 3" ) if filelen["four"] != filelen2["four"]: failure( "wrong size 4" ) // directory removal try for key, val in filelen fileRemove( key ) end dirChange( ".." ) if dirCurrent() != curdir: failure( "Did not return to previous dir" ) dirRemove( "dirtest.dir" ) catch in error failure( "Dir removal, Exception rised: " + error.toString() ) end success() /* End of file */ tests/core/testsuite/discarddec.fal000066400000000000000000000011011176363201700177510ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 5d * Category: expression * Subcategory: incdec * Short: Fake autoincrement * Description: * This test checks the effect on autoincrement on discardable/transient data, * as function returns. * [/Description] * ****************************************************************************/ if testReflect( 1 ) -- != 1: failure( "Fake increment discard (postfix)." ) if -- testReflect( 1 ) != 0: failure( "Fake increment usage (prefix)." ) success() /* End of file */ tests/core/testsuite/discardinc.fal000066400000000000000000000011011176363201700177670ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 5c * Category: expression * Subcategory: incdec * Short: Fake autoincrement * Description: * This test checks the effect on autoincrement on discardable/transient data, * as function returns. * [/Description] * ****************************************************************************/ if testReflect( 1 ) ++ != 1: failure( "Fake increment discard (postfix)." ) if ++ testReflect( 1 ) != 2: failure( "Fake increment usage (prefix)." ) success() /* End of file */ tests/core/testsuite/empty_loops.fal000066400000000000000000000020771176363201700202530ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 15p * Category: loops * Subcategory: empty * Short: Empty loops * Description: * Cheks the while and forin loops to be performed also when empty. * * [/Description] * ****************************************************************************/ // Test for open range tc1 = false tc2 = false function testCall1() global tc1 tc1 = true return false end function testCall2() global tc2 tc2 = true return [1,2,3] end //========================= // test for an empty while // while testCall1() end if not tc1: failure( "Empty while" ) //========================== // test for an empty for/in // for i in testCall2() end if not tc2: failure( "Empty for/in" ) //========================== // Now test empty forlast // empty forfirst and formiddle just do nothing // res = "" for i in [1,2,3] forlast; end res += i.toString() formiddle: res += ";" forfirst; end end if res != "1;2;3": failure( "Empty forlast&forfirst" ) /* End of file */ tests/core/testsuite/eval.fal000066400000000000000000000021751176363201700166270ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 61a * Category: functional * Subcategory: eval * Short: Eval function * Description: * This test checks eval function, which starts evaluation context. * [/Description] * ****************************************************************************/ function vector_eq( v1, v2 ) if v1.len() != v2.len(): return false for i in [0:v1.len()] if v1[i] != v2[i]: return false end return true end // identity eval if eval( 0 ) != 0: failure( "eval(0)" ) if eval( "a" ) != "a": failure( "eval(a)" ) if not vector_eq( eval( [1,2,3] ), [1,2,3] ) : failure( "eval flat array" ) function produce( a ) return a + 1 end // single result if eval( [produce, 1] ) != 2: failure( "Single eval" ) // multiple result if not vector_eq( eval( [[produce, 1], [produce,2] ] ), [2,3] ) failure( "multiple eval" ) end // recursive result if not vector_eq( eval( [\ [produce, [produce, 1]],\ [produce, [produce, 2]]\ ] ), [3,4] ) failure( "recursive eval" ) end success() /* end of file */ tests/core/testsuite/extlambda.fal000066400000000000000000000015001176363201700176300ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 18f * Category: lambda * Subcategory: * Short: Lambda with external * Description: * Verifies that lambda expressions manage correctly undefined symbols. * [/Description] * ****************************************************************************/ function ret_lambda( sel ) if sel var = {param=> sin(param)} else var = {p1, p2=> p1 + {a, b => a( b ) }( sin, p2 ) } end return var end // select sin(x) vsin = ret_lambda( true ) if vsin( 3 ) != sin(3): failure( "Simple external" ) // select f(a,b)=a + sin(b) // tests also for callable function return direct call // and compilation issues. if ret_lambda(false)( 5,3 ) != 5 + sin(3): failure( "Nested external" ) success() /* End of file */ tests/core/testsuite/fastif.fal000066400000000000000000000013431176363201700171500ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 1c * Category: basic * Subcategory: basic * Short: Fast if conditional * Description: * This completes the test on basic conditional by testing also more complex * grammar, as i.e. the short and fast if statements. This is test must * be passed in order to grant the quality of the unit test. * [/Description] * ****************************************************************************/ a = 1 b = (a == 1 and a != 0) ? 4 : 5 if b != 4: failure( "First sequence (positive) failed" ) if b == 4: c = a == 0 ? 10 : 11 if c == 11 success() else failure( "Second sequence (negative) failed" ) end /* End of file */ tests/core/testsuite/fbom.fal000066400000000000000000000153161176363201700166240ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 36a * Category: fbom * Subcategory: * Short: Falcon Basic Object Model * Description: * This script tests for Falcon basic object model. Tests on all the types * are performed. * [/Description] * ****************************************************************************/ // Definitions class alpha fprop = nil end class beta sprop = nil end class mixed from alpha, beta function func() end end object obj from mixed end string = "A string" array = [ 1, 2, 3 ] dictionary = [ 1 => 1, 2 => 2, 3 => 3 ] integer = 1 float = 1.3 range = [1:2] inst = mixed() method = inst.func aNil = nil // Test 1: types if aNil.typeId() != NilType: failure( "NilType" ) if integer.typeId() != NumericType: failure( "NumericType -- from int" ) if float.typeId() != NumericType: failure( "NumericType" ) if range.typeId() != RangeType: failure( "RangeType" ) if string.typeId() != StringType: failure( "StringType" ) if array.typeId() != ArrayType: failure( "ArrayType" ) if dictionary.typeId() != DictionaryType: failure( "DictType" ) if printl.typeId() != FunctionType: failure( "FunctionType" ) if alpha.typeId() != ClassType: failure( "ClassType" ) if obj.typeId() != ObjectType: failure( "ObjectType" ) if method.typeId() != MethodType: failure( "MethodType" ) // Test 2: toString if aNil.toString() != "Nil": failure( "Nil to string" ) if integer.toString() != "1": failure( "Integer to string" ) if float.toString() != "1.3": failure( "Numeric to string" ) if range.toString() != "[1:2]": failure( "Range to string" ) if string.toString() != "A string": failure( "String to string" ) if array.toString() != "Array": failure( "Array to string" ) if dictionary.toString() != "Dictionary": failure( "Dictionary to string" ) if printl.toString() != "Function printl": failure( "Function to string" ) if alpha.toString() != "Class alpha": failure( "Class to string" ) if obj.toString() != "Object from %obj": failure( "Object to string" ) if method.toString() != "Method (Object from mixed).mixed.func": failure( "Method to string" ) // Test 3: len if aNil.len() != 0: failure( "Nil len" ) if integer.len() != 0: failure( "Integer len" ) if float.len() != 0: failure( "Numeric len" ) if range.len() != 3: failure( "Range len" ) if string.len() != 8: failure( "String len" ) if array.len() != 3: failure( "Array len" ) if dictionary.len() != 3: failure( "Dictionary len" ) if printl.len() != 0: failure( "Function len" ) if alpha.len() != 0: failure( "Class len" ) if obj.len() != 0: failure( "Object len" ) if method.len() != 0: failure( "Method len" ) // Test 4: first try aNil.first() failure( "Nil have first" ) end try integer.first() failure( "Integer have first" ) end try string.first() failure( "String have first" ) end try float.first() failure( "Float have first" ) end try printl.first() failure( "Function have first" ) end try alpha.first() failure( "Class have first" ) end try obj.first() failure( "Obj have first" ) end try method.first() failure( "Method have first" ) end if array.first() != Iterator( array ): failure( "Array first" ) if dictionary.first() != Iterator( dictionary ): failure( "Dictionary first" ) // Test 4: last try aNil.last() failure( "Nil have last" ) end try integer.last() failure( "Integer have last" ) end try string.last() failure( "String have last" ) end try float.last() failure( "Float have last" ) end try printl.last() failure( "Function have last" ) end try alpha.last() failure( "Class have last" ) end try obj.last() failure( "Obj have last" ) end try method.last() failure( "Method have last" ) end if array.last() != Iterator( array, -1 ): failure( "Array last" ) if dictionary.last() != Iterator( dictionary, -1 ): failure( "Dictionary last" ) //test 5: compare bigInt = 1500 if bigInt.compare( integer ) <= 0: failure( "Compare verse <=" ) if integer.compare( bigInt ) >= 0: failure( "Compare verse >=" ) bigInt = integer if integer.compare( bigInt ) != 0: failure( "Compare verse == first" ) if bigInt.compare( integer ) != 0: failure( "Compare verse == beta" ) // todo: more extensive tests in a separate file //test 7: className if aNil.className() != nil: failure( "Nil className" ) if integer.className() != nil: failure( "Integer className" ) if float.className() != nil: failure( "Numeric className" ) if range.className() != nil: failure( "Range className" ) if string.className() != nil: failure( "String className" ) if array.className() != nil: failure( "Array className" ) if dictionary.className() != nil: failure( "Dictionary className" ) if printl.className() != nil: failure( "Function className" ) if alpha.className() != "alpha": failure( "Class className" ) if obj.className() != "%obj": failure( "Object className" ) if inst.className() != "mixed": failure( "Instance className" ) if method.className() != nil: failure( "Method className" ) //test 8: className if aNil.baseClass() != nil: failure( "Nil baseClass" ) if integer.baseClass() != nil: failure( "Integer baseClass" ) if float.baseClass() != nil: failure( "Numeric baseClass" ) if range.baseClass() != nil: failure( "Range baseClass" ) if string.baseClass() != nil: failure( "String baseClass" ) if array.baseClass() != nil: failure( "Array baseClass" ) if dictionary.baseClass() != nil: failure( "Dictionary baseClass" ) if printl.baseClass() != nil: failure( "Function baseClass" ) if alpha.baseClass() != nil: failure( "Class baseClass" ) if obj.baseClass() == nil: failure( "Object baseClass" ) if inst.baseClass() != mixed: failure( "Instance baseClass" ) if method.baseClass() != nil: failure( "Method baseClass" ) //test 9: derived from if obj.derivedFrom( "impossible" ): failure( "Negative derived from" ) if not obj.derivedFrom( "%obj" ): failure( "Obj derived from %obj" ) if not obj.derivedFrom( "mixed" ): failure( "Obj derived from mixed" ) if not obj.derivedFrom( "beta" ): failure( "Obj derived from beta" ) if not obj.derivedFrom( "alpha" ): failure( "Obj derived from alpha" ) //test 10: trim try aNil.trim() failure( "Nil has trim" ) end try integer.trim() failure( "Integer has trim" ) end try float.trim() failure( "Float has trim" ) end try range.trim() failure( "Range has trim" ) end try string.trim() catch failure( "String does not have trim" ) end try array.trim() failure( "Array has trim" ) end try dictionary.trim() failure( "Dictionary has trim" ) end try printl.trim() failure( "Function has trim" ) end try alpha.trim() failure( "Class has trim" ) end try obj.trim() failure( "Object has trim" ) end try inst.trim() failure( "Instance has trim" ) end try method.trim() failure( "Method has trim" ) end success() /* End of file */ tests/core/testsuite/fbom_clone.fal000066400000000000000000000024441176363201700200020ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 36b * Category: fbom * Subcategory: clone * Short: fbom clone * Description: * Checking for item copying using fbom. * [/Description] ****************************************************************************/ // string on test string = "Hello world" string += "." smod = string.clone() if smod != "Hello world.": failure( "String copy" ) string += "Modify" if smod != "Hello world.": failure( "String copy -- insolation" ) // array copy astr = "a" array = [ astr, "b", "c" ] amod = array.clone() if len( amod ) != 3: failure( "Array copy" ) array += "element" if len( amod ) != 3: failure( "Array copy -- insolation" ) // string invariancy test amod[0][0] = "b" if astr != "a": failure( "Invariancy test 1" ) if array[0] != "a": failure( "Invariancy test 2" ) // dictionary copy dict = [ 1 => 1, 2 => 2, 3 => 3 ] dmod = dict.clone() if dmod[2] != 2: failure( "Dict copy" ) dict[2] = "modified" if dmod[2] != 2: failure( "Dict copy -- insolation" ) // object copy object test prop1 = "one" prop2 = "two" end tmod = test.clone() if tmod.prop1 != "one": failure( "Object copy" ) test.prop1 = "changed" if tmod.prop1 != "one": failure( "Object copy -- insolation" ) success() /* End of file */ tests/core/testsuite/fbom_frontback.fal000066400000000000000000000032771176363201700206600ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 36c * Category: fbom * Subcategory: * Short: FBOM front/back * Description: * Tests for front/back items in series. * [/Description] * ****************************************************************************/ // Strings str = "Hello" if str.front() != "H": failure( "String front" ) if str.back() != "o": failure( "String back" ) // numeric version if str.front(1, false, true) != ord("H"): failure( "String front numeric" ) if str.back(1, false, true) != ord("o"): failure( "String back numeric" ) // removal count = 0 cfrvector = ["H","e","l","l","o"] while str if str.front(1,true) != cfrvector[count++]: failure( "String removal" ) end // Arrays array = [ 1, 2, 3, 4 ] cfrvector = array.clone() if array.front() != 1: failure( "Array front" ) if array.back() != 4: failure( "Array back" ) // removal count = 0 while array if array.front(true) != cfrvector[count++]: failure( "Array removal" ) end // Dictionaries dict = [ "A"=>1, "B"=>2, "C"=>3] if dict.front() != 1: failure( "Dict front" ) if dict.back() != 3: failure( "Dict back" ) // test with keys if dict.front(false, true) != "A": failure( "Dict key front" ) if dict.back(false, true) != "C": failure( "Dict key back" ) // removal count = 0 cfrvector = [1,2,3] while dict if dict.front(true) != cfrvector[count++]: failure( "Dict removal" ) end // Membuf mb = MemBuf(5) mb[0] = 1 mb[-1] = 2 if mb.front() != 1: failure( "Membuf front" ) if mb.back() != 2: failure( "Membuf back" ) // error /* try err = "" k = err.front() failure( "Error on empty string not raised" ) catch AccessError end */ success() /* End of file */ tests/core/testsuite/fbom_getprop.fal000066400000000000000000000063721176363201700203660ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 36d * Category: fbom * Subcategory: * Short: FBOM.getProperty * Description: * The FBOM getProperty method allows to access an object property * by name. * [/Description] * ****************************************************************************/ class testClass prop1 = "value1" function func1() return "func1" end end object testObj prop2 = "value2" function testMethod() return "testMethod" end end cls = testClass() array = [] array.prop1 = "orig value" array.mth = function(); return "testMethod"; end bd = bless([ "prop1" => "orig value", "mth" => {=>return "testMethod"} ] ) try prop1 = cls.getProperty( "prop1" ) if prop1 != "value1": failure( "Get property on class" ) catch failure( "Property access on class" ) end try method = cls.getProperty( "func1" ) if not isCallable( method ): failure( "Method retrival on class" ) value = method() if value != "func1": failure( "Method intdirect call on class" ) catch failure( "Method access on class" ) end try prop2 = testObj.getProperty( "prop2" ) if prop2 != "value2": failure( "Get property on object" ) catch failure( "Property access on object" ) end try method2 = testObj.getProperty( "testMethod" ) if not isCallable( method2 ): failure( "Method retrival on object" ) value2 = method2() if value2 != "testMethod": failure( "Method intdirect call on object" ) catch failure( "Method access on object" ) end //====================================================== try prop1 = array.getProperty( "prop1" ) if prop1 != "orig value": failure( "Get property on array" ) catch failure( "Property access on array" ) end try mth = array.getProperty( "mth" ) if not isCallable( mth ): failure( "Method retrival on array" ) value2 = mth() if value2 != "testMethod": failure( "Method intdirect call on array" ) catch failure( "Method access on array" ) end //====================================================== try prop1 = bd.getProperty( "prop1" ) if prop1 != "orig value": failure( "Get property on dict" ) catch failure( "Property access on dict" ) end try mth = bd.getProperty( "mth" ) if not isCallable( mth ): failure( "Method retrival on dict" ) value2 = mth() if value2 != "testMethod": failure( "Method intdirect call on dict" ) catch failure( "Method access on dict" ) end //====================================================== // negative tests. try cls.getProperty( "not here!!" ) failure( "Exception not raised for classes" ) catch in error if error.code != 33: failure( "Wrong exception raised for classes" ) end try testObj.getProperty( "not here!!" ) failure( "Exception not raised for object" ) catch in error if error.code != 33: failure( "Wrong exception raised for object" ) end try array.getProperty( "not here!!" ) failure( "Exception not raised for array" ) catch in error if error.code != 33: failure( "Wrong exception raised for array" ) end try bd.getProperty( "not here!!" ) failure( "Exception not raised for dict" ) catch in error if error.code != 33: failure( "Wrong exception raised for dict" ) end success() /* End of file */ tests/core/testsuite/fbom_setprop.fal000066400000000000000000000040161176363201700203730ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 36e * Category: fbom * Subcategory: * Short: FBOM.setProperty * Description: * The core function setProperty allows to change an object property * by name. * [/Description] * ****************************************************************************/ class testClass prop1 = "value1" end object testObj prop2 = "value2" end cls = testClass() array = [] array.prop1 = "orig value" bd = bless([ "prop1" => "orig value"] ) try cls.setProperty( "prop1", "new value" ) if cls.prop1 != "new value": failure( "Set property on class instance" ) catch failure( "Property access on class" ) end try testObj.setProperty( "prop2", "new value" ) if testObj.prop2 != "new value": failure( "Set property on object" ) catch failure( "Property access on object" ) end try bd.setProperty( "prop1", "new value" ) if bd.prop1 != "new value": failure( "Set property on dictionary" ) catch failure( "Property access dictionary" ) end try array.setProperty( "prop1", "new value" ) if array.prop1 != "new value": failure( "Set property on array" ) catch failure( "Property access array" ) end // Negative tests. try cls.setProperty( "not here!!", "arrg" ) failure( "Exception not raised for classes" ) catch in error if error.code != 33: failure( "Wrong exception raised for classes" ) end try testObj.setProperty( "not here!!", "arrg" ) failure( "Exception not raised for object" ) catch in error if error.code != 33: failure( "Wrong exception raised for object" ) end try bd.setProperty( "prop2", "new value" ) failure( "Exception not raised for ditctionary" ) catch in error if error.code != 33: failure( "Wrong exception raised for dictionary" ) end // arrays can have new property set try array.setProperty( "prop2", "new value" ) if array.prop2 != "new value": failure( "Set new property on array" ) catch failure( "Property addition array" ) end success() /* End of file */ tests/core/testsuite/files.fal000066400000000000000000000034361176363201700170030ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 109a * Category: rtl * Subcategory: file * Short: Common file operations. * Description: * Test on common files operations. * [/Description] * ****************************************************************************/ const filename = "109a.test" try file = IOStream( filename, 0644, FILE_SHARE ); catch in error failure( "File creation: " + error.toString() ) end try if file.tell() != 0: failure( "Tell at 0" ) if file.write( "Hello world" ) != 11: failure( "Writing" ) if file.tell() != 11: failure( "Tell at end" ) // write a bit more, needed for readLine file.write( "\nNext line 1\r\nNext line 2\nNext line 3" ); fileLen = file.tell() file.seek( 0 ) if file.tell() != 0: failure( "Tell after seek" ) if file.grab( 11 ) != "Hello world": failure( "Read back" ) file.grabLine() // discards the first \n if file.grabLine() != "Next line 1" or \ file.grabLine() != "Next line 2" or \ file.grabLine() != "Next line 3" failure( "read line" ) end if not file.eof(): failure( "Should be at eof" ) file.seek( 0 ) if file.eof(): failure( "Eof reset" ) file.seekEnd( 0 ) if file.tell() != fileLen: failure( "Return to end position" ) if file.eof(): failure( "early eof" ) if file.grab( 100 ) != "": failure( "read at end of file" ) if not file.eof(): failure( "Eof not detected." ) // seekCur test. file.seek( 11 ) file.seekCur( -5 ) if file.grab( 5 ) != "world": failure( "seekCur" ) file.close() catch in error failure( "File operations: " + error.toString() ) end try fileRemove( filename ) catch in error failure( "File removal: " + error.toString() ) end success() /* End of file */ tests/core/testsuite/files2.fal000066400000000000000000000046731176363201700170710ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 109b * Category: rtl * Subcategory: file * Short: Space-saving file operations * Description: * Test on common files operations. * This test is analgue to 109a, but it uses the space-saving vesrion if the * file functions, that can reuse string buffers. * [/Description] * ****************************************************************************/ const filename = "109b.test" const bufsize = 128 buffer = "" try file = IOStream( filename, 0644, FILE_SHARE ); catch in error failure( "File creation: " + error.toString() ) end try if file.tell() != 0: failure( "Tell at 0" ) if file.write( "Hello world" ) != 11: failure( "Writing" ) if file.tell() != 11: failure( "Tell at end" ) // write a bit more, needed for readLine file.write( "\nNext line 1\r\nNext line 2\nNext line 3" ); fileLen = file.tell() file.seek( 0 ) if file.tell() != 0: failure( "Tell after seek" ) if file.read( buffer, 11 ) != 11 or buffer != "Hello world": failure( "Read back" ) file.readLine( buffer, bufsize ) // discards the first \n if not file.readLine( buffer, bufsize ): failure( "readline 1" ) if buffer != "Next line 1": failure( "readline 1 - content" ) if not file.readLine( buffer, bufsize ): failure( "readline 2" ) if buffer != "Next line 2": failure( "readline 2 - content " ) if not file.readLine( buffer, bufsize ): failure( "readline 3" ) if buffer != "Next line 3": failure( "readline 3 - content " ) if not file.eof(): failure( "Should be at eof" ) file.seek( 0 ) if file.eof(): failure( "Eof reset" ) file.seekEnd( 0 ) if file.tell() != fileLen: failure( "Return to end position" ) if file.eof(): failure( "early eof" ) if file.read( buffer, bufsize ) != 0: failure( "read at end of file" ) if buffer != "": failure( "Buffer not reset" ) if not file.eof(): failure( "Eof not detected." ) // seekCur test. file.seek( 11 ) file.seekCur( -5 ) if file.read( buffer, 5 ) != 5 or buffer != "world": failure( "seekCur" ) file.close() catch in error if error.fsError failure( "File operations: " + error.toString() + "\n" + systemErrorDescription(error.fsError) ) else failure( "File operations: " + error.toString() ) end end try fileRemove( filename ) catch in error failure( "File removal: " + error.toString() ) end success() /* End of file */ tests/core/testsuite/files3.fal000066400000000000000000000047671176363201700170760ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 109c * Category: rtl * Subcategory: file * Short: Space-saving file operations 2 * Description: * Test on common files operations. * This test is analgue to 109a, but it uses the space-saving vesrion if the * file functions, that can reuse string buffers. With respect to 109b, * the space is pre-allocated, and the single functions use the pre-allocated * size as a limit for the reads. * [/Description] * ****************************************************************************/ const filename = "109c.test" buffer = strBuffer( 128 ) // buffer is "", but it has 128 allocated bytes. if buffer != "": failure( "Buffer allocation" ) try file = IOStream( filename, 0644, FILE_SHARE ); catch in error failure( "File creation: " + error.toString() ) end try if file.tell() != 0: failure( "Tell at 0" ) if file.write( "Hello world" ) != 11: failure( "Writing" ) if file.tell() != 11: failure( "Tell at end" ) // write a bit more, needed for readLine file.write( "\nNext line 1\r\nNext line 2\nNext line 3" ); fileLen = file.tell() file.seek( 0 ) if file.tell() != 0: failure( "Tell after seek" ) if file.read( buffer, 11 ) != 11 or buffer != "Hello world": failure( "Read back" ) file.readLine( buffer ) // discards the first \n if not file.readLine( buffer ): failure( "readline 1" ) if buffer != "Next line 1": failure( "readline 1 - content" ) if not file.readLine( buffer ): failure( "readline 2" ) if buffer != "Next line 2": failure( "readline 2 - content " ) if not file.readLine( buffer ): failure( "readline 3" ) if buffer != "Next line 3": failure( "readline 3 - content " ) if not file.eof(): failure( "Should be at eof" ) file.seek( 0 ) if file.eof(): failure( "Eof reset" ) file.seekEnd( 0 ) if file.tell() != fileLen: failure( "Return to end position" ) if file.eof(): failure( "early eof" ) if file.read( buffer ) != 0: failure( "read at end of file" ) if buffer != "": failure( "Buffer not reset" ) if not file.eof(): failure( "Eof not detected." ) // seekCur test. file.seek( 11 ) file.seekCur( -5 ) // 5 spaces. buffer = strBuffer(5) if file.read( buffer ) != 5 or buffer != "world": failure( "seekCur" ) file.close() catch in error failure( "File operations: " + error.toString() ) end try fileRemove( filename ) catch in error failure( "File removal: " + error.toString() ) end success() tests/core/testsuite/fnamesplit.fal000066400000000000000000000042671176363201700200460ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 111 * Category: rtl * Subcategory: dir * Short: File name splitting. * Description: * This test tries to split a file name in costituients * [/Description] * ****************************************************************************/ disk, path, file, ext = fileNameSplit( "C:/a/path/to/a/file.ext" ) if disk != "C": failure( "Disk or server spec" ) if path != "/a/path/to/a": failure( "Path spec" ) if file != "file": failure( "File spec" ) if ext != "ext": failure( "Extension spec" ) // mount it back complete = fileNameMerge( disk, path, file, ext ) if complete != "/C:/a/path/to/a/file.ext": failure( "Mouting back" ) // several combi disk, path, file, ext = fileNameSplit( "/a/path/to/a/file.ext" ) if disk != "": failure( "Disk or server spec - 2" ) if path != "/a/path/to/a": failure( "Path spec - 2" ) if file != "file": failure( "File spec - 2" ) if ext != "ext": failure( "Extension spec - 2" ) complete = fileNameMerge( disk, path, file, ext ) if complete != "/a/path/to/a/file.ext": failure( "Mouting back - 2" ) disk, path, file, ext = fileNameSplit( "file.ext" ) if disk != "": failure( "Disk or server spec - 3" ) if path != "": failure( "Path spec - 3" ) if file != "file": failure( "File spec - 3" ) if ext != "ext": failure( "Extension spec - 3" ) complete = fileNameMerge( disk, path, file, ext ) if complete != "file.ext": failure( "Mouting back - 3" ) disk, path, file, ext = fileNameSplit( "C:file.ext" ) if disk != "C": failure( "Disk or server spec - 4" ) if path != "": failure( "Path spec - 4" ) if file != "file": failure( "File spec - 4" ) if ext != "ext": failure( "Extension spec - 4" ) complete = fileNameMerge( disk, path, file, ext ) if complete != "/C:file.ext": failure( "Mouting back - 4" ) disk, path, file, ext = fileNameSplit( "C:/a/path/" ) if disk != "C": failure( "Disk or server spec - 5" ) if path != "/a/path": failure( "Path spec - 5" ) complete = fileNameMerge( disk, path ) if complete != "/C:/a/path": failure( "Mouting back - 5" ) complete = fileNameMerge( nil, path ) if complete != "/a/path": failure( "Mouting back - 6" ) success() /* End of file */ tests/core/testsuite/for_to.fal000066400000000000000000000027721176363201700171730ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 16a * Category: loops * Subcategory: for * Short: For/to * Description: * For/to is in Falcon back again since 0.8.14. It is just a candy * grammar for a standard for/in loop ending at the final element, * inclusive. So, we'll do some very basic working tests. * [/Description] ****************************************************************************/ a = 0 for i = 1 to 5 a += (i+1) // so we're sure we're not doing 0 end if a != 2+3+4+5+6: failure( "for/to bounds - up" ) a = 0 for i = -1 to -5 a += (i-1) // so we're sure we're not doing 0 end if a != -2-3-4-5-6: failure( "for/to bounds - down" ) // check the step a = 0 for i = 1 to 5 , 2 a += i end if a != 1+3+5: failure( "for/to bounds step - 1" ) // check the step, but missing the last one a = 0 for i = 1 to 6 , 2 a += i end if a != 1+3+5: failure( "for/to bounds step - 2" ) // for -5 to -4 => should do both a = 0 for i = -5 to -4 a += i end if a != -5-4: failure( "For up in negative range" ) // for 0 - 0 a = 1 for i = 0 to 0 a = i end if a!= 0: failure( "For/to with start == end" ) a = 1 for i = -1 to -2, 1 a = i end if a!= 1: failure( "Empty for/to, increasing" ) a = 1 for i = 10 to 11, -1 a = i end if a!= 1: failure( "Empty for/to, decreasing" ) a = 1 for i = 10 to 10, -1 a = i end if a!= 1: failure( "Empty for/to, decreasing (2)" ) // all the other tests are performed by for/in success() tests/core/testsuite/foreach_try.fal000066400000000000000000000041471176363201700202060ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 20f * Category: statements * Subcategory: try * Short: Try/catch in foreach * Description: * Tests for ecception rising and catching in foreach loops * [/Description] * ****************************************************************************/ vector = [ [1,2], [3, 4], [5, 6], 7, // this will generate our error [8, 9] ] //=========================================================================== // This function won't return cleanly if try doesn't clean the foreach stack. // function tryToReturn() try for v1, v2 in vector if typeOf( v1 ) != NumericType: failure( "Unpacking TRAV - ret " ) end catch if v2 != 6 // last catched value failure( "Catch from unpack ret" ) end end end //==================== //=========================================================================== // This function will make the stack dirty if not cleanly popped // function tryToRaise() for v1, v2 in vector if typeOf( v1 ) != NumericType: failure( "Unpacking TRAV - ret " ) end end //==================== for subvect in vector try v1, v2 = subvect if typeOf( v1 ) != NumericType: failure( "Unpacking UNPS " ) catch if subvect != 7 failure( "Catch in unps" ) end end end try for v1, v2 in vector if typeOf( v1 ) != NumericType: failure( "Unpacking TRAV " ) end catch if v2 != 6 // last catched value failure( "Catch from unpack" ) end end // if we didn't remove the stack in tryToRaise(), we'd had an error here for subvect in vector try tryToRaise() end end // Try to see if we clean the foreach stack after try tryToReturn() // This will raise an error in TRAN if local stack isnt cleaned. for subvect in vector try for v1, v2 in vector if typeOf( v1 ) != NumericType: failure( "Unpacking TRAV - inner " ) end catch if v2 != 6 // last catched value failure( "Catch from unpack - inner" ) end end end success() tests/core/testsuite/forin.fal000066400000000000000000000020451176363201700170110ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 15a * Category: loops * Subcategory: forin * Short: Forin simple * Description: * Cheks the forin loop, in its simplest incarnation * [/Description] * ****************************************************************************/ array = [ "a", "b", "c", "d", "e", "f" ] body = [] for var in array body += var end if len( body ) != len( array ) or body[5] != array[5] failure( "Simple forin" ) end body = [] for var in array body += var forfirst:first = var end if first != "a" or body[1] != "b" failure( "forin - forfirst" ) end body = [] for var in array body += var forlast last = var end end if last != "f" or body[-1] != "f" failure( "forin - forlast" ) end body = [] first = 0 last = 0 for var in array body += var forfirst : first = var forlast : last = var end if first != "a" or last != "f" or body[1] != "b" or body[-1] != "f" failure( "forin - forfirst - forlast" ) end success() /* End of file */ tests/core/testsuite/forin_dict.fal000066400000000000000000000017531176363201700200210ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 15m * Category: loops * Subcategory: forin * Short: Forin dictionary complete * Description: * Tests forfirst, forlast, formiddle, continue dropping and assignments * in dictionaries all in one step. * [/Description] * ****************************************************************************/ dict = [ "alpha" => 1, "beta" => 2, "gamma" => 3, "delta" => 4, "eta" => 5 ] result = "" for key, value in dict forfirst: result += "f:" if value % 2 == 1 continue dropping end result += key + "=" + value.toString() .= "Changed" formiddle: result += "," forlast: result += "." end if result != "f:beta=2,delta=4,": failure( "In looping" ) if dict.len() != 2: failure( "In dropping" ) for key, value in dict if value != "Changed": failure( "In touching key: " + key ) end /* end of file */ tests/core/testsuite/forin_forall.fal000066400000000000000000000055601176363201700203550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 15f * Category: loops * Subcategory: forin * Short: Forfirst and Forlast special * Description: * Cheks the forin loop under particular condition. * Here we check the forfirst and forlast clauses * Checks are performed for: * - element = nil * - empty set * - one, two, three and four element sets * [/Description] * ****************************************************************************/ array = nil result = [] result_first = [] result_last = [] try for var in array result += var forfirst result_first += var end forlast result_last += var end end catch failure( "Empty set generates error" ) end if len( result ) != 0: failure( "Empty set" ) if len( result_first ) != 0: failure( "Empty set - forfirst" ) if len( result_last ) != 0: failure( "Empty set - forlast" ) array = [1] result = [] result_first = [] result_last = [] result_middle = [] for var in array result += var formiddle: result_middle += var forfirst: result_first += var forlast: result_last += var end if len( result ) != 1: failure( "1 element set" ) if len( result_first ) != 1: failure( "1 element set - forfirst" ) if len( result_last ) != 1: failure( "1 element set - forlast" ) if len( result_middle ) != 0: failure( "1 element set - formiddle" ) array = [ 1, 2 ] result = [] result_first = [] result_last = [] result_middle = [] for var in array forfirst: result_first += var forlast: result_last += var result += var formiddle result_middle += var end end if len( result ) != 2: failure( "2 element set" ) if len( result_first ) != 1: failure( "2 element set - forfirst" ) if len( result_last ) != 1: failure( "2 element set - forlast" ) if len( result_middle ) != 1: failure( "2 element set - formiddle" ) array = [ 1, 2, 3 ] result = [] result_first = [] result_last = [] result_middle = [] for var in array forfirst result_first += var end result += var forlast: result_last += var formiddle: result_middle += var end if len( result ) != 3: failure( "3 element set" ) if len( result_first ) != 1: failure( "3 element set - forfirst" ) if len( result_last ) != 1: failure( "3 element set - forlast" ) if len( result_middle ) != 2: failure( "3 element set - formiddle" ) array = [ 1, 2, 3, 4 ] result = [] result_first = [] result_last = [] result_middle = [] for var in array forfirst result_first += var end forlast: result_last += var formiddle result_middle += var end result += var end if len( result ) != 4: failure( "4 element set" ) if len( result_first ) != 1: failure( "4 element set - forfirst" ) if len( result_last ) != 1: failure( "4 element set - forlast" ) if len( result_middle ) != 3: failure( "4 element set - formiddle" ) success() /* End of file */ tests/core/testsuite/forin_forfirst.fal000066400000000000000000000031541176363201700207310ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 15d * Category: loops * Subcategory: forin * Short: Forfirst special * Description: * Cheks the forin loop under particular condition. * Here we check the forfirst clause * Checks are performed for: * - element = nil * - empty set * - one, two and three element sets * [/Description] * ****************************************************************************/ array = nil result = [] result_first = [] try for var in array result += var forfirst result_first += var end end catch failure( "Empty set generates error" ) end if len( result ) != 0: failure( "Empty set" ) if len( result_first ) != 0: failure( "Empty set - forfirst" ) array = [1] result = [] result_first = [] for var in array result += var forfirst result_first += var continue // skipping the main block end end if len( result ) != 0: failure( "1 element set" ) if len( result_first ) != 1: failure( "1 element set - forfirst" ) array = [ 1, 2 ] result = [] result_first = [] for var in array result += var forfirst result_first += var continue // skipping the main block end end if len( result ) != 1: failure( "2 element set" ) if len( result_first ) != 1: failure( "2 element set - forfirst" ) array = [ 1, 2, 3 ] result = [] result_first = [] for var in array result += var forfirst result_first += var continue end end if len( result ) != 2: failure( "3 element set" ) if len( result_first ) != 1: failure( "3 element set - forfirst" ) success() /* End of file */ tests/core/testsuite/forin_forlast.fal000066400000000000000000000027571176363201700205550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 15e * Category: loops * Subcategory: forin * Short: Forlast special * Description: * Cheks the forin loop under particular condition. * Here we check the forlast clause * Checks are performed for: * - element = nil * - empty set * - one, two and three element sets * [/Description] * ****************************************************************************/ array = nil result = [] result_last = [] try for var in array result += var forlast result_last += var end end catch failure( "Empty set generates error" ) end if len( result ) != 0: failure( "Empty set" ) if len( result_last ) != 0: failure( "Empty set - forlast" ) array = [1] result = [] result_last = [] for var in array forlast:result_last += var result += var end if len( result ) != 1: failure( "1 element set" ) if len( result_last ) != 1: failure( "1 element set - forlast" ) array = [ 1, 2 ] result = [] result_last = [] for var in array result += var forlast:result_last += var end if len( result ) != 2: failure( "2 element set" ) if len( result_last ) != 1: failure( "2 element set - forlast" ) array = [ 1, 2, 3 ] result = [] result_last = [] for var in array result += var forlast result_last += var end end if len( result ) != 3: failure( "3 element set" ) if len( result_last ) != 1: failure( "3 element set - forlast" ) success() /* End of file */ tests/core/testsuite/forin_generator.fal000066400000000000000000000016071176363201700210620ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 75a * Category: functional * Subcategory: generators * Short: Forin generators * Description: * Tests for/in loop applied to generators. * [/Description] * ****************************************************************************/ // A generator function function gen_func( reset ) static counter = 1 end if reset counter = 1 return end if counter == 5: return oob(0) return counter ++ end sum = 0 for value in gen_func sum += value end if sum != 1+2+3+4: failure( "Simple loop" ) // reset the generator function gen_func( true ) str = "" for value in gen_func forfirst: str += "Begin: " str += value formiddle: str += ", " forlast: str += "." end if str != "Begin: 1, 2, 3, 4.": failure( "Complete loop" ) success() /* end of file */ tests/core/testsuite/forin_genobj.fal000066400000000000000000000015661176363201700203440ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 75b * Category: functional * Subcategory: generators * Short: Forin object generators * Description: * Tests for/in loop applied to generators (as objects). * [/Description] * ****************************************************************************/ // A generator object object genobj counter = 1 function __call() if self.counter == 5: return oob(0) return self.counter ++ end end sum = 0 for value in genobj sum += value end if sum != 1+2+3+4: failure( "Simple loop" ) // reset the generator function genobj.counter = 1 str = "" for value in genobj forfirst: str += "Begin: " str += value formiddle: str += ", " forlast: str += "." end if str != "Begin: 1, 2, 3, 4.": failure( "Complete loop" ) success() /* end of file */ tests/core/testsuite/forin_range.fal000066400000000000000000000060341176363201700201670ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 15n * Category: loops * Subcategory: forin * Short: Forin ranges * Description: * Cheks the forin loop used on ranges. * * [/Description] * ****************************************************************************/ // Test for open range result = 0 count = 0 // must never enter for c in [10:] result += c count++ end if result != 0: failure( "Open range - value" ) if count != 0: failure( "Open range - times" ) //=================================================== // Test for empty range, positive result = 0 count = 0 for c in [10:10] result += c count++ end if result != 0: failure( "Empty range - value" ) if count != 0: failure( "Empty range - times" ) //=================================================== // Test for empty range, negative result = 0 count = 0 for c in [-10:-10] result += c count++ end if result != 0: failure( "Empty range neg - value" ) if count != 0: failure( "Empty range neg - times" ) //=================================================== // Test for positive range, upwards result = 0 count = 0 for c in [1:5] result += c count++ end if result != 1+2+3+4: failure( "Filled range, positive up - value" ) if count != 4: failure( "Filled range, positive up - times" ) //=================================================== // Test for positive range, downwards result = 0 count = 0 for c in [5:1] result += c count++ end if result != 1+2+3+4+5: failure( "Filled range, positive down - value" ) if count != 5: failure( "Filled range, positive down - times" ) //=================================================== // Test for negative range, upwards result = 0 count = 0 for c in [-5:-1] result += c count++ end if result != -5 -4 -3 -2: failure( "Filled range, negative up - value" ) if count != 4: failure( "Filled range, negative up - times" ) //=================================================== // Test for negative range, downwards result = 0 count = 0 for c in [-1:-5] result += c count++ end if result != -5 -4 -3 -2 -1: failure( "Filled range, negative down - value" ) if count != 5: failure( "Filled range, negative down - times" ) //=================================================== // Test for mixed range, upwards result = 0 count = 0 for c in [-1: 4] result += c count++ end if result != -1 +1+2+3 : failure( "Filled range, mixed up - value" ) if count != 5: failure( "Filled range, mixed up - times" ) //=================================================== // Test for mixed range, downwards result = 0 count = 0 for c in [4: -1] result += c count++ end if result != -1 +1+2+3+4 : failure( "Filled range, mixed down - value" ) if count != 6: failure( "Filled range, mixed down - times" ) //=================================================== // Test for negative empty loop result = 0 count = 0 for c in [5: 5:-1] result += c count++ end if result != 5: failure( "Empty downto range - value" ) if count != 1: failure( "Empty downto range - times" ) success() /* End of file */ tests/core/testsuite/forin_range_blocks.fal000066400000000000000000000074571176363201700215360ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 15o * Category: loops * Subcategory: forin * Short: Forin ranges blocks * Description: * Cheks the forin loop used on ranges with forfirst, * forlast and formiddle clauses. * * [/Description] * ****************************************************************************/ // Test for open range result = 0 count = 0 vals_first = [] vals_last = [] vals_middle = [] // Must never enter for c in [10:] forfirst: vals_first += c result += c count++ formiddle: vals_middle += c forlast: vals_last += c end if result != 0: failure( "Open range - value" ) if count != 0: failure( "Open range - times" ) if vals_first.len() != 0: failure( "Open range - forfirst" ) if vals_middle.len() != 0: failure( "Open range - formiddle" ) if vals_last.len() != 0: failure( "Open range - forlast" ) //=================================================== // Test for empty range, positive result = 0 count = 0 vals_first = [] vals_last = [] vals_middle = [] for c in [10:10] forfirst: vals_first += c result += c count++ formiddle: vals_middle += c forlast: vals_last += c end if result != 0: failure( "Empty range - value" ) if count != 0: failure( "Empty range - times" ) if vals_first.len() != 0: failure( "Empty range - forfirst" ) if vals_middle.len() != 0: failure( "Empty range - formiddle" ) if vals_last.len() != 0: failure( "Empty range - forlast" ) //=================================================== // Test for filled range, upwards result = 0 count = 0 vals_first = [] vals_last = [] vals_middle = [] for c in [1:5] forfirst: vals_first += c result += c count++ formiddle: vals_middle += c forlast: vals_last += c end if result != 1+2+3+4: failure( "Upward range - value" ) if count != 4: failure( "Upward range - times" ) if vals_first.len() != 1: failure( "Upward range - forfirst" ) if vals_middle.len() != 3: failure( "Upward range - formiddle" ) if vals_last.len() != 1: failure( "Upward range - forlast" ) //=================================================== // Test for filled range, downward result = 0 count = 0 vals_first = [] vals_last = [] vals_middle = [] for c in [5:1] forfirst: vals_first += c result += c count++ formiddle: vals_middle += c forlast: vals_last += c end if result != 1+2+3+4+5: failure( "Downward range - value" ) if count != 5: failure( "Downward range - times" ) if vals_first.len() != 1: failure( "Downward range - forfirst" ) if vals_middle.len() != 4: failure( "Downward range - formiddle" ) if vals_last.len() != 1: failure( "Downward range - forlast" ) //=================================================== // Test for filled range, one result = 0 count = 0 vals_first = [] vals_last = [] vals_middle = [] for c in [1:2] forfirst: vals_first += c result += c count++ formiddle: vals_middle += c forlast: vals_last += c end if result != 1: failure( "Upward unique range - value" ) if count != 1: failure( "Upward unique range - times" ) if vals_first.len() != 1: failure( "Upward unique range - forfirst" ) if vals_middle.len() != 0: failure( "Upward unique range - formiddle" ) if vals_last.len() != 1: failure( "Upward unique range - forlast" ) //=================================================== // Test for filled range, two, down result = 0 count = 0 vals_first = [] vals_last = [] vals_middle = [] for c in [2:1] forfirst: vals_first += c result += c count++ formiddle: vals_middle += c forlast: vals_last += c end if result != 1+2: failure( "Downward double range - value" ) if count != 2: failure( "Downward double range - times" ) if vals_first.len() != 1: failure( "Downward double range - forfirst" ) if vals_middle.len() != 1: failure( "Downward double range - formiddle" ) if vals_last.len() != 1: failure( "Downward double range - forlast" ) success() /* End of file */ tests/core/testsuite/forin_setItem.fal000066400000000000000000000013541176363201700205050ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 15k * Category: loops * Subcategory: forin * Short: dot.assign * Description: * Changes the content of a forin loop. * [/Description] * ****************************************************************************/ // Test in array array = [ "a", "b", "c", "d", "e", "f" ] for var in array if var == "c" .= "Z" end end if array[2] != "Z": failure( "Setting item in array" ) // Test in dictionary dict = [ 1=>"a", 2=>"b", 3=>"c", 4=>"d", 5=>"e", 6=>"f" ] for key, var in dict if var == "c" .= "Z" end end if dict[ 3 ] != "Z": failure( "Setting item in dictionary" ) success() /* End of file */ tests/core/testsuite/forin_short.fal000066400000000000000000000011441176363201700202270ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 15c * Category: loops * Subcategory: forin * Short: Forin short grammar. * Description: * Cheks the forin loop, in its simplest incarnation; * here the shortened grammar (colon instead of eol) is checked. * [/Description] * ****************************************************************************/ array = [ "a", "b", "c", "d", "e", "f" ] body = [] for var in array: body += var if len( body ) != len( array ) or body[5] != array[5] failure( "Simple forin" ) end success() /* End of file */ tests/core/testsuite/forin_special.fal000066400000000000000000000015571176363201700205200ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 165c * Category: loops * Subcategory: forin * Short: Forin special * Description: * Cheks the forin loop under particular condition. * Checks are performed for: * - element = nil * - empty set * - one element * - two elements * [/Description] * ****************************************************************************/ array = nil result = [] try for var in array result += var end catch failure( "Empty set generates error" ) end if len( result ) != 0: failure( "Empty set" ) array = [1] result = [] for var in array result += var end if len( result ) != 1: failure( "1 element set" ) array = [ 1, 2 ] result = [] for var in array result += var end if len( result ) != 2: failure( "2 element set" ) success() /* End of file */ tests/core/testsuite/forin_table.fal000066400000000000000000000014211176363201700201550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 81f * Category: tabular * Subcategory: forin * Short: Table with forin * Description: * Tests if a table can be accessed as a sequence in a for/in loop. * [/Description] * ****************************************************************************/ function genFunc( ) return self.a end t = Table( ["a", "b", "c"], [1, "one", genFunc], [2, "two", genFunc], [3, "three", genFunc] ) col1 = 0 col2 = "" col3 = 0 for row in t col1 += row.a col2 += row.b col3 += row.c() GC.perform( true ) end if col1 != 6: failure( "col1 - value" ) if col2 != "onetwothree": failure( "col2 - value" ) if col3 != 6: failure( "col3 - value" ) success() /* end of file */ tests/core/testsuite/forincomplex.fal000066400000000000000000000016041176363201700204010ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 15b * Category: loops * Subcategory: forin * Short: Forin complex * Description: * Cheks the forin loop, using complex effects... * [/Description] * ****************************************************************************/ function provider() return [ ["a", "b", 1 ], ["c", "d", 2], ["e", "f", 3] ] end array1 = [] array2 = [] array3 = [] for var1, var2, var3 in provider() array1 += var1 array2 += var2 array3 += var3 end if array1[2] != "e" or array2[2] != "f" or array3[2] != 3 failure( "Forin with array unpack" ) end array1 = [] array2 = [] for key, value in [ "k1" => "v1", "k2" => "v2" ] array1 += key array2 += value end success() if array1[1] != "k2" or array2[1] != "v2" failure( "Forin with dictionary unpack" ) end success() /* End of file */ tests/core/testsuite/format_base.fal000066400000000000000000000054731176363201700201660ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 41a * Category: rtl * Subcategory: core * Short: Basic numeric format * Description: * Checks for format operations being correctly performed. * [/Description] * ****************************************************************************/ val = 0.45292123 if @"$val:.0" != "0": failure( "Positive (0.) trim" ) if @"$val:.2" != "0.45": failure( "Positive (0.) round down" ) if @"$val:.3" != "0.453": failure( "Positive (0.) round up" ) val = -0.45292123 if @"$val:.0" != "0": failure( "Negative (0.) trim" ) if @"$val:.2" != "-0.45": failure( "Negative (0.) round down" ) if @"$val:.3" != "-0.453": failure( "Negative (0.) round up" ) val = 0.9 if @"$val:.0" != "1": failure( "Positive (0.9) trim" ) if @"$val:.1" != "0.9": failure( "Positive (0.9) fix" ) if @"$val:.2" != "0.90": failure( "Positive (0.9) exceed" ) val = -0.9 if @"$val:.0" != "-1": failure( "Negative (0.9) trim" ) if @"$val:.1" != "-0.9": failure( "Negative (0.9) fix" ) if @"$val:.2" != "-0.90": failure( "Negative (0.9) exceed" ) val = 0.009 if @"$val:.0" != "0": failure( "Positive (0.009) trim" ) if @"$val:.1" != "0.0": failure( "Positive (0.009) fix" ) if @"$val:.2" != "0.01": failure( "Positive (0.009) exceed" ) val = -0.009 if @"$val:.0" != "0": failure( "Negative (0.009) trim" ) if @"$val:.1" != "-0.0": failure( "Negative (0.009) fix" ) if @"$val:.2" != "-0.01": failure( "Negative (0.009) exceed" ) val = 120.45292123 if @"$val:0" != "120": failure( "Zero len (120)" ) if @"$val:.0" != "120": failure( "Positive (120.) trim" ) if @"$val:.2" != "120.45": failure( "Positive (120.) round down" ) if @"$val:.3" != "120.453": failure( "Positive (120.) round up" ) val = -120.45292123 if @"$val:0" != "-120": failure( "Zero len (-120)" ) if @"$val:.0" != "-120": failure( "Negative (120.) trim" ) if @"$val:.2" != "-120.45": failure( "Negative (120.) round down" ) if @"$val:.3" != "-120.453": failure( "Negative (120.) round up" ) val = 120.9 if @"$val:.0" != "121": failure( "Positive (120.9) trim" ) if @"$val:.1" != "120.9": failure( "Positive (120.9) fix" ) if @"$val:.2" != "120.90": failure( "Positive (120.9) exceed" ) val = -120.9 if @"$val:.0" != "-121": failure( "Negative (120.9) trim" ) if @"$val:.1" != "-120.9": failure( "Negative (120.9) fix" ) if @"$val:.2" != "-120.90": failure( "Negative (120.9) exceed" ) val = 120.009 if @"$val:.0" != "120": failure( "Positive (120.009) trim" ) if @"$val:.1" != "120.0": failure( "Positive (120.009) fix" ) if @"$val:.2" != "120.01": failure( "Positive (120.009) exceed" ) val = -120.009 if @"$val:.0" != "-120": failure( "Negative (120.009) trim" ) if @"$val:.1" != "-120.0": failure( "Negative (120.009) fix" ) if @"$val:.2" != "-120.01": failure( "Negative (120.009) exceed" ) success() /* End of file */ tests/core/testsuite/func_allany.fal000066400000000000000000000055231176363201700201730ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 62a * Category: functional * Subcategory: constructs * Short: All and any * Description: * Checks for correct working of functional constructs "all" and "any". * [/Description] * ****************************************************************************/ test_calls = 0 test_3_calls = 0 function test( a ) global test_calls test_calls++ return a == 0 end function test_3( alpha, beta, gamma ) global test_3_calls test_3_calls++ return alpha == "alpha" and beta == "beta" and gamma == "gamma" end // basic if all( [] ): failure( "Empty all" ) if any( [] ): failure( "Empty any" ) if allp(): failure( "Empty allp" ) if anyp(): failure( "Empty anyp" ) // only immediate, positive if not all( [1, "test", 3.24] ): failure( "positive immediate all" ) if not allp( 1, "test", 3.24 ): failure( "positive immediate allp" ) if not any( [1, "test", 3.24] ): failure( "positive immediate any" ) if not anyp( 1, "test", 3.24 ): failure( "positive immediate anyp" ) // only immediate, negative if all( [1, "", 3.24] ): failure( "negative immediate all" ) if allp( 1, 0, 3.24 ): failure( "negative immediate allp" ) if any( [nil, "", 0] ): failure( "negative immediate any" ) if anyp( nil, "", 0 ): failure( "negative immediate anyp" ) // mixed, positive if not all( [1, [test, 0], 3.24] ): failure( "positive mixed all" ) if not allp( 1, [test, 0], 3.24 ): failure( "positive mixed allp" ) if not any( [0, [test, 0], 3.24] ): failure( "positive mixed any" ) if not anyp( 0, [test, 0], nil ): failure( "positive mixed anyp" ) // mixed, negative if all( [1, [test, 1], 3.24] ): failure( "negative mixed all" ) if allp( 1, [test, 1], 3.24 ): failure( "negative mixed allp" ) if any( [nil, [test, 1], 0] ): failure( "negative mixed any" ) if anyp( nil, [test, 1], 0 ): failure( "negative mixed anyp" ) // pure, positive if not all( [[test_3, "alpha", "beta", "gamma"], [test, 0]] ): failure( "positive pure all" ) if not allp( [test_3, "alpha", "beta", "gamma"], [test, 0] ): failure( "positive pure allp" ) if not any( [[test_3, "alpha", "beta", "gamma"], [test, 1]] ): failure( "positive pure any" ) if not anyp( [test_3, "alpha", "x", "gamma"], [test, 0] ): failure( "positive pure anyp" ) // pure, negative if all( [[test_3, "alpha", "beta", "gamma"], [test, 1]] ): failure( "negative pure all" ) if allp( [test_3, "alpha", "x", "gamma"], [test, 0] ): failure( "negative pure allp" ) if any( [[test_3, "alpha", "x", "gamma"], [test, 1]] ): failure( "negative pure any" ) if anyp( [test_3, "alpha", "x", "gamma"], [test, 1] ): failure( "negative pure anyp" ) // check for correct calls if test_3_calls != 8: failure( "test_3 call count" ) // as anyp is shortcircuited, we should have 14 calls if test_calls != 14: failure( "test call count" ) success() /* End of file */ tests/core/testsuite/func_brigade.fal000066400000000000000000000021171176363201700203040ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 63b * Category: functional * Subcategory: constructs * Short: Brigade * Description: * Checks if the "brigade" function is correctly working. * [/Description] * ****************************************************************************/ function mean( array ) value = 0 for elem in array: value += elem return value / len( array ) end function dbl( array ) for i in [0:len(array)]: array[i] *= 2 end doubleMean = .[ brigade .[ dbl mean ]] if doubleMean( [1,2,3] ) != 4: failure( "Simple brigade" ) sum = 0 looper = .[brigade .[ { val, text => sum += val; return 0} // or even change them via out-of-band { val, text => oob( [val+1, "Changed"] ) } // and ask repetition via out-of-band // or return something at the end. { val, text => val < 10 ? oob(1): "Completed." } ] ] final = looper( 1, "Original" ) if sum != 1+2+3+4+5+6+7+8+9: failure( "looping" ) if final != "Completed.": failure( "return value" ) success() tests/core/testsuite/func_cascade.fal000066400000000000000000000032451176363201700202750ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 63a * Category: functional * Subcategory: constructs * Short: Cascade * Description: * Checks for correct working of functional construct "cascade". * [/Description] * ****************************************************************************/ function square( x ) return x * x end function sqrt( x ) return x ** 0.5 end // empty cascade if cascade([]) != nil: failure( "Empty cascade" ) // straight cascade cfunc = [ square, sqrt, int ] if cascade( cfunc, 2 ) != 2: failure( "Fake abs 1" ) if cascade( cfunc, -2 ) != 2: failure( "Fake abs 2" ) // reductive cascade function test( a, b, c ) return a * b - c end cfunc[0:0] = test if cascade( cfunc, 2, 1, 4 ) != 2: failure( "Fake abs 3" ) // function declining processing saved1 = 0 saved2 = 0 saved3 = 0 function testdecl( p1, p2, p3 ) global saved1, saved2, saved3 saved1 = p1 saved2 = p2 saved3 = p3 return oob() end // in the middle... cfunc = [ test, testdecl, square, sqrt, int ] if cascade( cfunc, 2, 1, 4 ) != 2: failure( "decline in the middle" ) if saved1 != -2 or saved2 != nil or saved3 != nil failure( "decline call in the middle" ) end // at begin cfunc = [ testdecl, test, square, sqrt, int ] if cascade( cfunc, 2, 1, 4 ) != 2: failure( "decline at begin" ) if saved1 != 2 or saved2 != 1 or saved3 != 4 failure( "decline call at begin" ) end // at end cfunc = [ test, square, sqrt, int, testdecl ] if cascade( cfunc, -2, 1, -4 ) != 2: failure( "decline at end" ) if saved1 != 2 or saved2 != nil or saved3 != nil failure( "decline call at end" ) end success() /* End of file */ tests/core/testsuite/func_clos.fal000066400000000000000000000025451176363201700176540ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 60e * Category: functional * Subcategory: closure * Short: Functional closure * Description: * Closure application test. * [/Description] * ****************************************************************************/ function token(t) f = function (s) //printl("t: ",t," s: ",s) try if t.len() != 3: failure( "Closed value size" ) return s[ 0 : t.len() ] catch Error in error failure( "Param access 1" ) end end return f end // the same, but here the parameter is not directly used in an expression. function token1(t) f = function (s) try // the trick in this line is that we use undefined "t" twice in a line. // the compiler must be able to assign the undefined symbol only the first time // and use the created symbol the second time. return t == s[ 0 : t.len() ] catch Error in error failure( "Param access 2" ) end end return f end t = token( "foo" ) if t( "barfoo" ) != "bar": failure( "Closure result 1" ) t = token1( "foo" ) if not t( "foo" ) : failure( "Closure result 2 (double undefined on one line)" ) if t( "bar" ) : failure( "Closure result 3" ) success() /* end of file */ tests/core/testsuite/func_dolist.fal000066400000000000000000000025531176363201700202110ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 71a * Category: functional * Subcategory: constructs * Short: dolist * Description: * Checks the construct dolist. * [/Description] * ****************************************************************************/ function reset() global positive, negative positive = 0 negative = 0 end function test( a ) global positive if a >= 0 positive++ else negative ++ end return true end a = {a=>sin(a)} reset() dolist( test, [] ) if positive != 0 or negative != 0: failure( "empty list" ) reset() dolist( test, [1] ) if positive != 1 or negative != 0: failure( "one element flat list" ) reset() dolist( test, [ [ {a=>-a}, 1] ] ) if positive != 0 or negative != 1: failure( "one element eval list" ) // first, test with empty list reset() dolist( test, [1, 2, 3, -1] ) if positive != 3 or negative != 1: failure( "flat array" ) // second, test evaluation reset() dolist( test, [1, 2, [{a => a + 1}, 2],\ [{a=> -a}, 5] ] ) if positive != 3 or negative != 1: failure( "evaluation" ) // third: deep evaluation reset() dolist( test, [ [{a=> a+2}, 1], 2, [{a => a + 1}, 1], [{a=> -a}, 5] ] ) if positive != 3 or negative != 1: failure( "deep evaluation" ) success() /* End of file */ tests/core/testsuite/func_dolist_break.fal000066400000000000000000000010461176363201700213510ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 71c * Category: functional * Subcategory: constructs * Short: dolist break * Description: * Check limited dolist when returning false from test. * [/Description] * ****************************************************************************/ value = 0 function test( a ) global value value += a if a > 3: return oob( nil ) end dolist( test, [1,2,3,4,5,6] ) if value != 10: failure( "not interrupted" ) success() /* End of file */ tests/core/testsuite/func_dolist_err.fal000066400000000000000000000011321176363201700210510ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 71b * Category: functional * Subcategory: constructs * Short: dolist errors * Description: * Checks the construct dolist for error detection * [/Description] * ****************************************************************************/ function dummy() end try dolist( 0, [dummy, "no"] ) failure( "noncallable filter" ) end try dolist( dummy ) failure( "no second param" ) end try dolist( dummy, 0 ) failure( "second param non-array" ) end success() /* End of file */ tests/core/testsuite/func_filter.fal000066400000000000000000000012711176363201700201740ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 66a * Category: functional * Subcategory: mappings * Short: Functional filter * Description: * Checks for correct working of functional construct "filter". * [/Description] * ****************************************************************************/ function filter_func( f ) return f % 2 != 0 end // empty mapping if filter( filter_func, [] ).len() != 0: failure( "Empty set" ) fd = filter( filter_func, [1,2,3,4] ) if fd.len() != 2 failure( "Full filtering - size" ) end if fd[0] != 1 or fd[1] != 3 failure( "Full filtering - content" ) end success() /* End of file */ tests/core/testsuite/func_floop.fal000066400000000000000000000013411176363201700200240ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 72a * Category: functional * Subcategory: constructs * Short: floop * Description: * Checks the floop construct. This loop may eiter crash, loop * forever or succeed. * [/Description] * ****************************************************************************/ i=0 sum = 0 .[floop .[ .[{ x=> x = x+1} $i] // increase i .[iff .[{x=>x==5} $i] oob(1) ] // continue if i == 5 .[{x,y=> x = x + y} $sum $i] // sum every number .[iff .[{x=>x>=10} $i] oob(0) ] // break if i >= 10 ]]() // check against sum of first 10 numbers minus 5 if sum != 1+2+3+4+6+7+8+9+10: failure( "test" ) success() /* End of file */ tests/core/testsuite/func_iff.fal000066400000000000000000000036441176363201700174610ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 64a * Category: functional * Subcategory: constructs * Short: Functional if * Description: * Checks for correct working of functional construct "iff". * [/Description] * ****************************************************************************/ // base if if iff( 1, "yes" ) != "yes": failure( "Base iff positive, w/o else" ) if iff( 1, "yes", "no" ) != "yes": failure( "Base iff positive, with else" ) if iff( 0, "yes" ) != nil: failure( "Base iff negative, w/o else" ) if iff( 0, "yes", "no" ) != "no": failure( "Base iff negative, with else" ) // with callable check test_count = 0 function test( a ) global test_count test_count++ return a != 0 end if iff( [test, 1], "yes" ) != "yes": failure( "Callable check positive, w/o else" ) if iff( [test, 1], "yes", "no" ) != "yes": failure( "Callable check iff positive, with else" ) if iff( [test, 0], "yes" ) != nil: failure( "Callable check iff negative, w/o else" ) if iff( [test, 0], "yes", "no" ) != "no": failure( "Callable check iff negative, with else" ) if test_count != 4: failure( "Test function call count" ) // with callable check and callable results yes_count = 0 no_count = 0 function ret_yes( v ) global yes_count, no_count if v == "yes" yes_count++ else no_count++ end return v end ry = [ ret_yes, "yes" ] rn = [ ret_yes, "no" ] if iff( [test, 1], ry ) != "yes": failure( "All callable iff positive, w/o else" ) if iff( [test, 1], ry, rn ) != "yes": failure( "All callable iff positive, with else" ) if iff( [test, 0], ry ) != nil: failure( "All callable iff negative, w/o else" ) if iff( [test, 0], ry, rn ) != "no": failure( "All callable iff negative, with else" ) if yes_count != 2: failure( "Evaluation of positive branch count" ) if no_count != 1: failure( "Evaluation of negative branch count" ) success() /* End of file */ tests/core/testsuite/func_map.fal000066400000000000000000000016201176363201700174620ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 65a * Category: functional * Subcategory: mappings * Short: Functional mapping * Description: * Checks for correct working of functional construct "map". * [/Description] * ****************************************************************************/ function mapping_func( f ) return f * f end function mapping_func1( f ) if f < 0: return oob() return f * f end // empty mapping if map( mapping_func, [] ).len() != 0: failure( "Empty set" ) mapped = map( mapping_func, [1,2,3,4] ) if mapped[0] != 1 or mapped[1] != 4 or mapped[2] != 9 or mapped[3] != 16 failure( "Full mapping" ) end mapped = map( mapping_func1, [-1,2,-3,4] ) if mapped.len() != 2: failure( "Partial map size" ) if mapped[0] != 4 or mapped[1] != 16 failure( "Partial map content" ) end success() /* End of file */ tests/core/testsuite/func_max.fal000066400000000000000000000015201176363201700174710ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 70a * Category: functional * Subcategory: minmax * Short: max * Description: * Checks for correct working of functional construct "max". * [/Description] * ****************************************************************************/ function map_func( a ) if a < 0: raise nil return a * a end // empty mapping if max() != nil: failure( "Empty set" ) if max( 1 ) != 1 failure( "One element" ) end if max( 3, 1 ) != 3 failure( "Two elements" ) end object test function compare( a ) if typeOf( a ) != NumericType: return nil if a > 0: return -1 if a < 0: return 1 return 0 end end if max( -3, -2, test ) != test failure( "Elements with compare overload" ) end success() /* End of file */ tests/core/testsuite/func_min.fal000066400000000000000000000014371176363201700174760ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 69a * Category: functional * Subcategory: minmax * Short: min * Description: * Checks for correct working of functional construct "min". * [/Description] * ****************************************************************************/ function map_func( a ) if a < 0: raise nil return a * a end // empty mapping if min() != nil: failure( "Empty set" ) if min( 1 ) != 1 failure( "One element" ) end if min( 3, 1 ) != 1 failure( "Two elements" ) end object test function compare( a ) if typeOf( a ) != NumericType: return nil return 0 - a end end if min( 3, 2, test ) != test failure( "Elements with compare overload" ) end success() /* End of file */ tests/core/testsuite/func_reduce.fal000066400000000000000000000014211176363201700201530ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 67a * Category: functional * Subcategory: mappings * Short: reduce * Description: * Checks for correct working of functional construct "reduce". * [/Description] * ****************************************************************************/ function reduce_func( a, b ) return a + b end // empty mapping if reduce( reduce_func, [] ) != nil: failure( "Empty set" ) if reduce( reduce_func, [], 0 ) != 0: failure( "Empty set with initial value" ) if reduce( reduce_func, [1,2,3,4] ) != 10 failure( "Full reducing without initial value" ) end if reduce( reduce_func, [1,2,3,4],-1 ) != 9 failure( "Full reducing with initial value" ) end success() /* End of file */ tests/core/testsuite/func_xmap.fal000066400000000000000000000024701176363201700176560ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 68a * Category: functional * Subcategory: mappings * Short: xmap * Description: * Checks for correct working of functional construct "xmap". * [/Description] * ****************************************************************************/ function map_func( a ) if a < 0: return oob() return a * a end // empty mapping if xmap( map_func, [] ).len() != 0: failure( "Empty set" ) fp = xmap( map_func, [1,-2,3,-4] ) if fp.len() != 2 failure( "XMap selection count" ) end if fp[0] != 1 or fp[1] != 9 failure( "Full reducing without initial value" ) end //=================================================== // a bit more complex // function mapper( a ) if a == 3: return oob() return a * 2 end function retval( name ) switch name case "uno" return 1 case "due" return 2 end end function join( a, b ) return a + b end arr = [ [retval, [join, "u","no"]],[retval, [join, "d","ue"]], [join, 2, 1],4 ] arr1 = xmap( mapper, arr ) if arr1.len() != 3: failure( "Functional eval, returned array size" ) if arr1[0] != 2: failure("Functional eval 1" ) if arr1[1] != 4: failure("Functional eval 2" ) if arr1[2] != 8: failure("Functional eval 3" ) success() /* End of file */ tests/core/testsuite/funcbase.fal000066400000000000000000000007331176363201700174640ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 13a * Category: functions * Subcategory: * Short: Basic function * Description: * Minimal function call test. If this isn't passed, many things won't be * passed too. * [/Description] * ****************************************************************************/ function test() return 1 end if test() != 1: failure( "Basic test" ) success() /* End of file */ tests/core/testsuite/funcexpr.fal000066400000000000000000000017511176363201700175310ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 32 * Category: expression * Subcategory: complex * Short: Functions as expressions * Description: * Tests for usage of functions as expression elements * [/Description] * ****************************************************************************/ function testArray() return [1,2,3] end class testClass value = "hello" end function testObject() return testClass() end function testString() return "Hello" end if testArray()[1] != 2: failure( "Access as array" ) testRet = testArray()[1:3] if testRet[0] != 2 or testRet[1] != 3: failure( "Range access" ) if testString()[ * 0 ] != ord( "H" ): failure( "Integer string accessor" ) if testString()[* 0 ] != ord( "H" ): failure( "Integer string accessor 2" ) if testString()[*0] != ord( "H" ): failure( "Integer string accessor 3" ) if testObject().value != "hello": failure( "Access as items" ) success() /* End of file */ tests/core/testsuite/funcinvpar.fal000066400000000000000000000010321176363201700200420ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 13o * Category: functions * Subcategory: * Short: Parameter invariance * Description: * String parameters must be pushed by value. * [/Description] * ****************************************************************************/ function test( a ) b = a b[0] = "X" if a == "Xello": failure( "Local modification" ) end data = "Hello" test( data ) if data != "Hello": failure( "Invariance" ) success() /* End of file */ tests/core/testsuite/funcnoparams.fal000066400000000000000000000010341176363201700203650ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 13n * Category: functions * Subcategory: * Short: Function without parameters * Description: * For unbeliveable as it may seems, all the other tests passesd with a bug in the * complire that didn't check for functions not having paramters. * [/Description] * ****************************************************************************/ function test( ) success() end test() failure( "Function not called" ) /* End of file */ tests/core/testsuite/funcparams.fal000066400000000000000000000013031176363201700200270ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 13m * Category: functions * Subcategory: * Short: Function paramter ordering * Description: * For unbeliveable as it may seems, all the other tests passesd with a bug in the * assembly generator that mixed up the order of the parameters... alphabetically! * [/Description] * ****************************************************************************/ function test( zeta, gamma, alpha ) if zeta != 1: failure( "First parameter mixed" ) if gamma != 2: failure( "Second parameter mixed" ) if alpha != 3: failure( "Third parameter mixed" ) end test( 1, 2, 3 ) success() /* End of file */ tests/core/testsuite/function.fal000066400000000000000000000014131176363201700175170ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 13b * Category: functions * Subcategory: * Short: Double function call * Description: * This test calls a function that in turns calls another function. * This is the minimal test to check for correct stack unwinding at * return, as first-level function from main entry point may return * correctly, while functions called from function may not. * [/Description] * ****************************************************************************/ function test1( param ) return param * 2 end function test( param ) return test1( param ) * test1( param -1 ) end var = 4 if test( var ) != var * 2 * (var-1) * 2: failure( "Deep function" ) success() /* End of file */ tests/core/testsuite/functional.fal000066400000000000000000000041041176363201700200340ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 60a * Category: functional * Subcategory: execarr * Short: Executable arrays * Description: * This is the base of functional programming in Falcon. This test * checks correct working of functional programming basic constructs: * executable arrays. * [/Description] * ****************************************************************************/ function test( a ) return a == 0 end function test2( a, b ) return a == "f" and b == 10 end function test_3( alpha, beta, gamma ) return alpha == "alpha" and beta == "beta" and gamma == "gamma" end // first, check for correct detection of isCallable if isCallable( [ 1, 2, 3 ]): failure( "isCallable negative" ) if not isCallable( [test, 2, 3] ): failure( "isCallable positive" ) // immediate call if not [test]( 0 ): failure( "immediate call 1" ) if [test]( 1 ): failure( "immediate call 2" ) // transient call tc = [test] if not tc( 0 ): failure( "transient call 1" ) if tc( 1 ): failure( "transient call 2" ) // transient 2 params // transient call tc = [test2] if not tc( "f", 10 ): failure( "transient 2 params call 1" ) if tc( "f", 9 ): failure( "transient 2 params call 2" ) if tc( "a", 10 ): failure( "transient 2 params call 3" ) // semideferred call if not [test, 0](): failure( "semideferred call 1" ) if [test, 1](): failure( "semideferred call 2" ) // deferred call tc = [test, 0] if not tc(): failure( "deferred call 1" ) tc = [test, 1] if tc(): failure( "deferred call 2" ) // deferred 2 params tc = [test2, "f", 10] if not tc(): failure( "deferred 2 params call" ) // mixed calls // semideferred mixed if not [test_3, "alpha", "beta"]( "gamma" ): failure( "semideferred mixed 1" ) if [test_3, "alpha", "beta"]( "dif" ): failure( "semideferred mixed 2" ) if [test_3, "alpha", "beta"](): failure( "semideferred mixed 3" ) tc = [test_3, "alpha", "beta"] if not tc( "gamma" ): failure( "deferred mixed 1" ) if tc(): failure( "deferred mixed 2" ) if tc( "other" ): failure( "deferred mixed 3" ) success() /* End of file */ tests/core/testsuite/future_eval.fal000066400000000000000000000022321176363201700202130ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 73b * Category: functional * Subcategory: fbind * Short: Future bindings in eval * Description: * Evaluation and future bindings require a slighly different management. * Testing them here. * [/Description] * ****************************************************************************/ function makepair( zp, vp ) return [zp, vp] end // First check that everything is fine. a = .[makepair vp|"b" zp|"a" ] ret = a() if ret[0] != "a" or ret[1] != "b": failure( "Smoke test" ) // Let's try an evaluation. ret = eval(a) if ret[0] != "a" or ret[1] != "b": failure( "eval basic" ) v = nil b = .[let $v a] eval( b ) if v[0] != "a" or v[1] != "b": failure( "eval through let" ) // More indirect evaluation vp = lbind( "vp", "b" ) zp = lbind( "zp", "a" ) a = .[makepair vp zp ] b = .[let $v a] eval( b ) if v[0] != "a" or v[1] != "b": failure( "eval deep - precalc" ) // More indirect evaluation v = 0 a = .[makepair .[lbind "vp", "b"] .[lbind "zp", "a"] ] b = .[let $v a] eval( b ) if v[0] != "a" or v[1] != "b": failure( "eval deep" ) success() /* end of file */ tests/core/testsuite/futurebind.fal000066400000000000000000000046471176363201700200550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 73a * Category: functional * Subcategory: fbind * Short: Basic future binding * Description: * Checking named parameters and their behavior in positional calls. * [/Description] * ****************************************************************************/ function test( z, a, b ) global gpar1, gpar2, gpar3 gpar1 = z gpar2 = a gpar3 = b end function reset() global gpar1, gpar2, gpar3 gpar1 = nil gpar2 = nil gpar3 = nil end x = lbind( "a", "Hello" ) test( x ) if gpar1 != nil or gpar2 != "Hello" or gpar3 != nil failure( "Single named parameter" ) end reset() test( x, "Yo!" ) if gpar1 != "Yo!" or gpar2 != "Hello" or gpar3 != nil failure( "Named+positional reverse" ) end reset() test( "Yo!", x ) if gpar1 != "Yo!" or gpar2 != "Hello" or gpar3 != nil failure( "Named+positional direct" ) end reset() test( "Yo!", "Yo! again", x ) if gpar1 != "Yo!" or gpar2 != "Hello" or gpar3 != nil failure( "Named+positional overwrite" ) end reset() test( x, "Yo!", "Yo! again", "end" ) if gpar1 != "Yo!" or gpar2 != "Hello" or gpar3 != "end" failure( "Named+positional overwrite + other" ) end // now same thing with an array. reset() [test, x, "Yo!" ]() if gpar1 != "Yo!" or gpar2 != "Hello" or gpar3 != nil failure( "[A] Named+positional reverse" ) end reset() [test, "Yo!", x ]() if gpar1 != "Yo!" or gpar2 != "Hello" or gpar3 != nil failure( "[A] Named+positional direct" ) end reset() [test, "Yo!", "Yo! again", x ]() if gpar1 != "Yo!" or gpar2 != "Hello" or gpar3 != nil failure( "[A] Named+positional overwrite" ) end reset() [test, x, "Yo!", "Yo! again", "end" ]() if gpar1 != "Yo!" or gpar2 != "Hello" or gpar3 != "end" failure( "[A] Named+positional overwrite + other" ) end // and with partially cached arrays reset() [test, x ]( "Yo!") if gpar1 != "Yo!" or gpar2 != "Hello" or gpar3 != nil failure( "[A2] Named+positional reverse" ) end reset() [test, "Yo!" ](x) if gpar1 != "Yo!" or gpar2 != "Hello" or gpar3 != nil failure( "[A2] Named+positional direct" ) end reset() [test, "Yo!" ]("Yo! again", x) if gpar1 != "Yo!" or gpar2 != "Hello" or gpar3 != nil failure( "[A2] Named+positional overwrite" ) end reset() [test, x, "Yo!" ]("Yo! again", "end") if gpar1 != "Yo!" or gpar2 != "Hello" or gpar3 != "end" failure( "[A2] Named+positional overwrite + other" ) end success() tests/core/testsuite/futurebind_err.fal000066400000000000000000000020511176363201700207100ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 73c * Category: functional * Subcategory: fbind * Short: future binding errors * Description: * [/Description] * ****************************************************************************/ function f( x, y ) local = nil end try f( local|10 ) failure( "Local variable passing not detected" ) catch end a = .[ f local|10 ] try a() failure( "Local variable passing not detected" ) catch end try eval( a ) failure( "Local variable passing not detected ( eval )" ) catch end v = nil try b = .[let $v a] eval( b ) failure( "Local variable passing not detected ( eval through let )" ) catch end try local = lbind( "local", 10 ) a = .[ f local ] b = .[ let $v a] eval( b ) failure( "Local variable passing not detected ( eval deep - precalc)" ) catch end try a = .[ f .[lbind "local", 10] ] b = .[let $v a] eval( b ) failure( "Local variable passing not detected ( eval deep )" ) catch end success() tests/core/testsuite/gcArray.fal000066400000000000000000000053161176363201700172700ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 51c * Category: gc * Subcategory: array * Short: Garbage collection - arrays * Description: * Test for correct accounting of garbage collection when arrays are * involved * [/Description] * ****************************************************************************/ print() // create a reusable frame // Record initial memory requirements GC.perform( true ) aliveMem = GC.aliveMem items = GC.items usedMem = GC.usedMem //==================================== // TEST 1 // drop a static array array = [ 1, 2, 3, 4] array = nil // ...and empty the A register a = 0*1 GC.perform( true ) if aliveMem != GC.aliveMem: failure( "dropped - unmatching allocated memory" ) if usedMem != GC.usedMem: failure( "dropped - unmatching used memory" ) if items != GC.items: failure( "dropped - unmatching live items items" ) //==================================== // TEST 2 // drop an array of arrays array = [ 1, 2, 3, [3,4], [5,6] ] // Empty the array... array = nil // ...and empty the A register a = 0*1 GC.perform( true ) if aliveMem != GC.aliveMem: failure( "drop array of array - unmatching allocated memory" ) if usedMem != GC.usedMem: failure( "drop array of array - unmatching used memory" ) if items != GC.items: failure( "drop array of array - unmatching live items items" ) //==================================== // TEST 3 // Insert an item array = [ 1, 2, 3, 4 ] array[2:2] = 10 // here we have an insertion // Empty the array... array = nil // ...and empty the A register a = 0*1 GC.perform( true ) if aliveMem != GC.aliveMem: failure( "insert - unmatching allocated memory" ) if usedMem != GC.usedMem: failure( "insert - unmatching used memory" ) if items != GC.items: failure( "insert - unmatching live items items" ) //==================================== // TEST 4 // Remove an item array = [ 1, 2, 3, 4 ] array[3:4] = [] // here we have a removal // Empty the array... array = nil // ...and empty the A register a = 0*1 GC.perform( true ) if aliveMem != GC.aliveMem: failure( "append - unmatching allocated memory" ) if usedMem != GC.usedMem: failure( "append - unmatching used memory" ) if items != GC.items: failure( "append - unmatching live items items" ) //==================================== // TEST 5 // append item array = [ 1, 2, 3, 4 ] array += [4,5,6] // Empty the array... array = nil // ...and empty the A register a = 0*1 GC.perform( true ) if aliveMem != GC.aliveMem: failure( "append self - unmatching allocated memory" ) if usedMem != GC.usedMem: failure( "append self - unmatching used memory" ) if items != GC.items: failure( "append self - unmatching live items items" ) success() /* End of file */ tests/core/testsuite/gcBase.fal000066400000000000000000000014461176363201700170640ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 51a * Category: gc * Subcategory: * Short: Garbage collection - basic * Description: * Test for correct accounting of garbage collection when no items are * involved. * [/Description] * ****************************************************************************/ print() // create a reusable frame // Record initial memory requirements GC.perform( true ) aliveMem = GC.aliveMem items = GC.items usedMem = GC.usedMem // perform GC.perform( true ) if aliveMem != GC.aliveMem: failure( "empty - unmatching allocated memory" ) if usedMem != GC.usedMem: failure( "empty - unmatching used memory" ) if items != GC.items: failure( "empty - unmatching live items items" ) success() /* End of file */ tests/core/testsuite/gcDict.fal000066400000000000000000000047161176363201700171000ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 51d * Category: gc * Subcategory: dictionary * Short: Garbage collection - dictionaries * Description: * Test for correct accounting of garbage collection when dictionaries are * involved * [/Description] * ****************************************************************************/ // ensure the main stack is lage enough a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17] a = nil // Record initial memory requirements GC.perform( true ) aliveMem = GC.aliveMem items = GC.items usedMem = GC.usedMem //==================================== // TEST 1 // drop a static dictionary dict = [ 1=>1, 2=>2, 3=>3, 4=>4 ] dict = nil // ...and empty the A register a = 0*1 GC.perform( true ) if aliveMem != GC.aliveMem: failure( "dropped - unmatching allocated memory" ) if usedMem != GC.usedMem: failure( "dropped - unmatching used memory" ) if items != GC.items: failure( "dropped - unmatching live items items" ) //==================================== // TEST 2 // drop an array of arrays //goDeep(10) // create 10 reusable frames dict = [ 1=>1, 2=>2, 4=>[3=>3, 4=>4], 5=>[5=>5, 6=>6] ] // Empty the array... dict = nil // ...and empty the A register a = 0*1 GC.perform( true ) if aliveMem != GC.aliveMem: failure( "drop dict of dict - unmatching allocated memory" ) if usedMem != GC.usedMem: failure( "drop dict of dict - unmatching used memory" ) if items != GC.items: failure( "drop dict of dict - unmatching live items items" ) //==================================== // TEST 3 // Insert an item dict = [ 1=>1, 2=>2, 3=>3, 4=>4 ] dict[ 100 ] = 30 // here we have an insertion // Empty the array... dict = nil // ...and empty the A register a = 0*1 GC.perform( true ) if aliveMem != GC.aliveMem: failure( "insert - unmatching allocated memory" ) if usedMem != GC.usedMem: failure( "insert - unmatching used memory" ) if items != GC.items: failure( "insert - unmatching live items items" ) //==================================== // TEST 4 // Remove an item dict = [ 1=>1, 2=>2, 3=>3, 4=>4 ] dictRemove( dict, 2 ) // here we have an insertion // Empty the array... dict = nil // ...and empty the A register a = 0*1 GC.perform( true ) if aliveMem != GC.aliveMem: failure( "append - unmatching allocated memory" ) if usedMem != GC.usedMem: failure( "append - unmatching used memory" ) if items != GC.items: failure( "append - unmatching live items items" ) success() /* End of file */ tests/core/testsuite/gcReflect.fal000066400000000000000000000015551176363201700175770ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 51g * Category: gc * Subcategory: reflection * Short: Garbage collection - reflection * Description: * Tests if reflection is correctly considerin garbage collection (i.e. * discarding destroyed data). * The test may crash if something goes wrong. * [/Description] * ****************************************************************************/ // we create a reflective class, like... x = TypeError( 100, "Hello" ) r = "one" r *=100 // then we do reflection x.message = r GC.perform( true ) sleep(0.01) f = x.toString() // At this point, a copy of r is in A, and is going to be disposed. if "oneone" notin x.message: failure( "Data destroyed" ) r= nil GC.perform( true ) f = x.toString() if "oneone" notin x.message: failure( "Data destroyed" ) success() tests/core/testsuite/gcStrings.fal000066400000000000000000000062771176363201700176520ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 51b * Category: gc * Subcategory: strings * Short: Garbage collection - strings * Description: * Test for correct accounting of garbage collection when strings are * involved * [/Description] * ****************************************************************************/ print() // create a reusable frame // clean memory -- serialzation may dirty the gc. GC.perform( true ) aliveMem = GC.aliveMem items = GC.items usedMem = GC.usedMem // test0 : keep a reference to a methodized static string. // gc may crash if not properly handled ref = "a static string".len //==================================== // TEST 1 // drop a static string str = "Hello world".clone() str += " world" str = nil GC.perform( true ) if aliveMem != GC.aliveMem: failure( "dropped string - unmatching allocated memory" ) if usedMem != GC.usedMem: failure( "dropped string - unmatching used memory" ) if items != GC.items: failure( "dropped string - unmatching live items items" ) //==================================== // TEST 2 // string manipulation - insert string. // modifying a static string creates a new buffer string, so the allocation size changes. strres = "Hello " strres[4:5] = " world" GC.perform( true ) aliveMem = GC.aliveMem usedMem = GC.usedMem items = GC.items str = strres.clone() str1 = " great " str[5:6] = str1 // here we have an insertion // Empty the strings... str = nil str1 = nil // ...and empty the A register a = 0*1 GC.perform( true ) if aliveMem != GC.aliveMem: failure( "insertion - unmatching allocated memory" ) if usedMem != GC.usedMem: failure( "insertion - unmatching used memory" ) if items != GC.items: failure( "insertion - unmatching live items items" ) //==================================== // TEST 3 // string manipulation - cut down string str = strres.clone() + " world" str[5:12] = "" // here we have a deletion // Empty the strings... str = nil // ...and empty the A register a = 0*1 GC.perform( true ) if aliveMem != GC.aliveMem: failure( "cut - unmatching allocated memory" ) if usedMem != GC.usedMem: failure( "cut - unmatching used memory" ) if items != GC.items: failure( "cut - unmatching live items items" ) //==================================== // TEST 4 // string manipulation - append string str = strres.clone() str += "world" // here we have an addition // Empty the strings... str = nil // ...and empty the A register a = 0*1 GC.perform( true ) if aliveMem != GC.aliveMem: failure( "append - unmatching allocated memory" ) if usedMem != GC.usedMem: failure( "append - unmatching used memory" ) if items != GC.items: failure( "append - unmatching live items items" ) //==================================== // TEST 5 // string change type - append string str = strres.clone() str += 921 // here we have an addition of an unicode high // Empty the strings... str = nil // ...and empty the A register a = 0*1 GC.perform( true ) if aliveMem != GC.aliveMem: failure( "unicode - unmatching allocated memory" ) if usedMem != GC.usedMem: failure( "unicode - unmatching used memory" ) if items != GC.items: failure( "unicode - unmatching live items items" ) success() /* End of file */ tests/core/testsuite/gc_module_class.fal000066400000000000000000000014201176363201700210130ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 51f * Category: gc * Subcategory: class * Short: Garbage collection - intermodule classes * Description: * This test checks that GC works correctly across subclass * instantation on different modules. It uses gc_module_class_1.fal * [/Description] * ****************************************************************************/ load .gc_module_class_1 print() // create a reusable frame class Child from Parent init self.changed = "" self.data = "" GC.perform(true) end end if parentInstance.changed !="original": failure( "Creation of original instsance" ) c = Child() if c.changed !="": failure( "Creation of original instsance" ) success() tests/core/testsuite/gc_module_class_1.fal000066400000000000000000000012001176363201700212270ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: gc * Subcategory: class * Short: Garbage collection - intermodule classes * Description: * This is the module part of the 51f test * [/Description] * ****************************************************************************/ load .gc_module_class_1 class Parent changed = "" data = nil init self.changed = "original" self.data = "1"*10000 GC.perform(true) self.data = "" GC.perform(true) end end // create an instance right now. parentInstance = Parent() exporttests/core/testsuite/getassert.fal000066400000000000000000000023721176363201700177000ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 38i * Category: messages * Subcategory: assert * Short: getAssert * Description: * Checks working of the getAssert function. * [/Description] * ****************************************************************************/ assert( "test", "assertval" ) if getAssert( "test" ) != "assertval": failure( "existing assert" ) // Tests on existing slot retract( "test" ) // Tests on non existing slots try getAssert( "test" ) failure( "Didn't raise message error on slot wihtout asserts" ) catch MessageError // it's ok catch in another failure( "Wrong exception raised in slot wihtout asserts" ) end val = getAssert( "test", "a default" ) if val != "a default": failure( "failing default on slot wihtout asserts" ) //========================================= // Tests on non existing slots try getAssert( "non-exist" ) failure( "Didn't raise message error on non-existing assert" ) catch MessageError // it's ok catch in another failure( "Wrong exception raised in non-existing assert" ) end val = getAssert( "non-exist", "a default" ) if val != "a default": failure( "failing default on non-existing assert" ) success() /* end of file */ tests/core/testsuite/getassert_slot.fal000066400000000000000000000015671176363201700207460ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 38l * Category: messages * Subcategory: assert * Short: getAssert on slots * Description: * Checks working of the getAssert method on slots * [/Description] * ****************************************************************************/ slot = VMSlot( "test" ) slot.assert( "assertval" ) if slot.getAssert() != "assertval": failure( "existing assert" ) // Tests on existing slot slot.retract() // Tests on non existing slots try slot.getAssert() failure( "Didn't raise message error on slot wihtout asserts" ) catch MessageError // it's ok catch in another failure( "Wrong exception raised in slot wihtout asserts" ) end val = slot.getAssert( "a default" ) if val != "a default": failure( "failing default on slot wihtout asserts" ) success() /* end of file */ tests/core/testsuite/ifelif.fal000066400000000000000000000013771176363201700171410ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 1d * Category: basic * Subcategory: * Short: if-elif-else nesting * Description: * This is quite an important test too, as many other tests suppos that * if/else/elif works correctly. * * Thist test also perform a minimal nidification test in conditionals * [/Description] * ****************************************************************************/ a = 1 if a != 1 failure( "if !=" ) elif a > 2 failure( "if >" ) elif a < 0 failure( "if <" ) else if a >= 2 failure( "if >=" ) elif a = 1 success() else failure( "false negative on ==" ) end end failure( "Impossible escape from branch" ) /* End of file */ tests/core/testsuite/ifelseposi.fal000066400000000000000000000010531176363201700200340ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 1h * Category: basic * Subcategory: conditionals * Short: If-else positive * Description: * Checks for a minimal if/then/else to be correctly evaluated. * This is the positive test, that is, with the if that should * enter the main branch. * [/Description] * ****************************************************************************/ sel = 1 if sel success() else failure( "Wrong branch" ) end failure( "If skipped" ) /* End of file */ tests/core/testsuite/import_alias.fal000066400000000000000000000012201176363201700203510ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 53b * Category: modloader * Subcategory: import * Short: Mixed import/from test * Description: * Test for importin the same symbol from a normal sub test with alias. * [/Description] * ****************************************************************************/ // named import import test1 from import_sub_1 // aliased import import test1 from import_sub_1 in alias if import_sub_1.test1() != "Hello world" failure( "Non aliased test" ) end if alias.test1() != "Hello world" failure( "Aliased test" ) end success() /* End of file */ tests/core/testsuite/import_as.fal000066400000000000000000000012101176363201700176620ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 53e * Category: modloader * Subcategory: import * Short: Import as * Description: * this tests the import/alias modality * [/Description] * ****************************************************************************/ // named import import test1 from import_sub_2 as first // aliased import import test1 from import_sub_3 as second if first() != "From sub2" failure( "import test1 from import_sub_2 = first" ) end if second() != "From sub3" failure( "import test1 from import_sub_3 = second" ) end success() /* End of file */ tests/core/testsuite/import_double.fal000066400000000000000000000014011176363201700205330ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 53c * Category: modloader * Subcategory: import * Short: Export discarding import/from test * Description: * Testing export request discarding. This program imports the same * exported symbol from import_sub_2 and import_sub_3. * [/Description] * ****************************************************************************/ // named import import test1 from import_sub_2 in first // aliased import import test1 from import_sub_3 in second if first.test1() != "From sub2" failure( "import test1 from import_sub_2 = first" ) end if second.test1() != "From sub3" failure( "import test1 from import_sub_3 = second" ) end success() /* End of file */ tests/core/testsuite/import_self.fal000066400000000000000000000010611176363201700202140ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 53d * Category: modloader * Subcategory: import * Short: Import from self-named module * Description: * Tests for the module loader being able to load a module named after self. * [/Description] * ****************************************************************************/ // named with self import test1 from self.imported if imported.test1() != "From test1" failure( "import test1 from import_self = first" ) end success() /* End of file */ tests/core/testsuite/import_self/000077500000000000000000000000001176363201700175325ustar00rootroot00000000000000tests/core/testsuite/import_self/imported.fal000066400000000000000000000000551176363201700220410ustar00rootroot00000000000000function test1() return "From test1" end tests/core/testsuite/import_simple.fal000066400000000000000000000010321176363201700205520ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 53a * Category: modloader * Subcategory: import * Short: Basic import/from test * Description: * This is a minimal test trying to import symbols by name from a submodule. * [/Description] * ****************************************************************************/ // named import import test1 from import_sub_1 if import_sub_1.test1() != "Hello world" failure( "Module import failed" ) end success() /* End of file */ tests/core/testsuite/import_spaced.fal000066400000000000000000000013251176363201700205250ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 53f * Category: modloader * Subcategory: import * Short: Import from spaced subdir. * Description: * Subdirectories with spaces are escaped to "n+m" in URI format traveling in * the modloader data. The DLL loader must back-escape it (or the module * loader must unescape it) before system request is tried. * * [/Description] * ****************************************************************************/ // named with self import test1 from "Import spaced/imported.fal" in imported if imported.test1() != "From test1" failure( "import test1 from import_self = first" ) end success() /* End of file */ tests/core/testsuite/import_sub_1.fal000066400000000000000000000006551176363201700203040ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: -- * Category: modloader * Subcategory: import * Short: Basic import/from test (component) * Description: * Submodule used by import_simple and other tests. * [/Description] * ****************************************************************************/ function test1() return "Hello world" end /* End of file */ tests/core/testsuite/import_sub_2.fal000066400000000000000000000006431176363201700203020ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: -- * Category: modloader * Subcategory: import * Short: Basic import/from test (component) * Description: * Submodule used by import_* test. * [/Description] * ****************************************************************************/ function test1() return "From sub2" end export /* End of file */ tests/core/testsuite/import_sub_3.fal000066400000000000000000000006431176363201700203030ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: -- * Category: modloader * Subcategory: import * Short: Basic import/from test (component) * Description: * Submodule used by import_* test. * [/Description] * ****************************************************************************/ function test1() return "From sub3" end export /* End of file */ tests/core/testsuite/increment.fal000066400000000000000000000024011176363201700176540ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 5a * Category: expression * Subcategory: incdec * Short: Increment * Description: * Testing increment operator under various conditions. * [/Description] * ****************************************************************************/ a = 0 a++ if a != 1: failure( "Basic increment postfix" ) ++a if a != 2: failure( "Basic increment prefix" ) a = 1 b = ++a + 1 // should be 3 if b != 3: failure( "Prefix value retrival in expression -- 1" ) if a != 2: failure( "Prefix effect -- 1" ) a = 1 b = a++ + 1 // should be 2 if b != 2: failure( "Postfix value retrival in expression -- 1" ) if a != 2: failure( "Postfix effect -- 1" ) a = 1 b = 2 * ++a +1 if b != 5: failure( "Prefix value retrival in expression -- 2" ) if a != 2: failure( "Prefix effect -- 2" ) a = 1 b = 2 * a++ +1 if b != 3: failure( "Postfix value retrival in expression -- 2" ) if a != 2: failure( "Postfix effect -- 2" ) a = 1 if ++a != 2: failure( "Prefix value retrival in conditional" ) if a != 2: failure( "Prefix effect in conditional" ) a = 1 if a++ != 1: failure( "Postfix value retrival in conditional" ) if a != 2: failure( "Postfix effect in conditional" ) success() /* End of file */ tests/core/testsuite/indirect.fal000066400000000000000000000026531176363201700175020ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 33a * Category: expression * Subcategory: indirect * Short: basic indirect * Description: * Test string-to-symbol indirect access, basic functionalities. * [/Description] * ****************************************************************************/ item = "Hello" str = "item" // immediate test if # "item" != "Hello": failure( "Basic indirect immediate" ) if # "it" + "em" != "Hello": failure( "Basic indirect immediate/precedence" ) if #str != "Hello": failure( "Basic indirect string" ) item = [ "one", "two", "three" ] str = "item[0]" if #str != "one": failure( "Array indirect 1" ) str = " item [ 2 ] " if #str != "three": failure( "Array indirect 2" ) object test prop = "Hi!" end if # "test.prop" != "Hi!": failure( "object indirect" ) test.prop = [ "one", "two", "three" ] if # "test.prop[0]" != "one": failure( "object indirect + array access 1 " ) if # "test.prop [ 2 ]" != "three": failure( "object indirect + array access 2" ) item = [ 0, test, 1 ] if # "item[ 1 ]. prop [0] " != "one": failure( "array access + object indirect" ) // test failure try a = # "item[ 1 " failure( "Negative test 1" ) end try a = # "item. " failure( "Negative test 2" ) end try a = # "unexisting" failure( "Negative test 3" ) end try a = # "item[ 1 ]. prop [0] asdfa" failure( "Negative test 4" ) end success() /* End of file */ tests/core/testsuite/indirect_deep.fal000066400000000000000000000021761176363201700204770ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 33c * Category: expression * Subcategory: indirect * Short: deep indirect * Description: * Test string-to-symbol indirect access, checking for recursive indirections. * [/Description] * ****************************************************************************/ item = [ "one", "two", "three" ] object access prop = [ 0, 1, 2 ] prop1 = [ "one" => 1, "two" => 2, "three" => 3 ] function mth() return "method" end function mth1() return self.prop1["three"] end end if # "item[ access.prop[1] ]" != "two": failure( "Deep indirect 1" ) if # "access.prop1[ item[2] ]" != 3: failure( "Deep indirect 2" ) deeparr = [ [1, "one"], [2, "two"], [3, "three"] ] if # "deeparr[ access.prop[1] ][1]" != "two": failure( "Deep indirect 3" ) if # "deeparr[ access.prop1[ item[1] ] ][0]" != 3: failure( "Deep indirect 4" ) // let's try with a method if (#"access.mth")() != "method": failure( "Deep indirect on methods" ) if (#"access.mth1")() != 3: failure("Deep indirect on methods - self" ) success() /* End of file */ tests/core/testsuite/indirect_dict.fal000066400000000000000000000013051176363201700204760ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 33b * Category: expression * Subcategory: indirect * Short: dictionary indirect * Description: * Test for indirections on dictionaries. * [/Description] * ****************************************************************************/ item = [ "a" => "one", '"b"' => "two", 0 => "three" ] str = "item[ 'a' ]" if #str != "one": failure( "Dictionary indirect 1" ) str = "item[ '\"b\"' ]" if #str != "two": failure( "Dictionary indirect 2" ) object test prop = "Hi!" end item["a"] = test if # "item[ 'a' ]. prop" != "Hi!": failure( "dictionary access + object indirect" ) success() /* End of file */ tests/core/testsuite/indirect_local.fal000066400000000000000000000016471176363201700206560ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 33d * Category: expression * Subcategory: indirect * Short: mixed symtab indirect * Description: * Test string-to-symbol indirect access, checking complex interchange local/global * interactions * [/Description] * ****************************************************************************/ function perform( deeparr ) item = [ "one", "two", "three" ] if # "item[ access.prop[1] ]" != "two": failure( "Deep indirect 1" ) if # "access.prop1[ item[2] ]" != 3: failure( "Deep indirect 2" ) if # "deeparr[ access.prop[1] ][1]" != "two": failure( "Deep indirect 3" ) if # "deeparr[ access.prop1[ item[1] ] ][0]" != 3: failure( "Deep indirect 4" ) end object access prop = [ 0, 1, 2 ] prop1 = [ "one" => 1, "two" => 2, "three" => 3 ] end perform( [ [1, "one"], [2, "two"], [3, "three"] ] ) success() /* End of file */ tests/core/testsuite/inheritance.fal000066400000000000000000000034731176363201700201730ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 21c * Category: types * Subcategory: classes * Short: Inheritance test * Description: * Tests the inheritance constructs. * [/Description] * ****************************************************************************/ class base_one(p1,p2) p_one = 1 p_two = 2 p_three = nil init self.p_three = p1 + p2 end function basic() return 1 end end class base_two( p1, p2 ) p_one = 3 p_two = 4 p_three = nil init self.p_three = p1 + p2 end function basic() return 2 end end // simple inheritance tst class inh_one from base_one( 1, 2 ) p_four = nil init self.p_four = self.p_three end function basic() return 3 end end // basic inheritance verify inh = inh_one() if inh.basic() != 3: failure( "Basic overload" ) if inh.p_one != 1: failure( "Inherited initialization" ) if inh.p_three != 3: failure( "Inherithed parametric initialization" ) if inh.p_four != 3: failure( "Inheritance constructor order" ) // inherited access verify if inh.base_one.basic() != 1: failure( "Base class method access" ) //--------------------------------------------------------- //double inheritance class inh_two(p1) from base_one( p1, 2 ), base_two( 5, p1 ) p_four = nil init self.p_four = p1 end function basic() return 3 end end inh = inh_two( 3 ) // sequence is one, two, so: if inh.p_one != 3: failure( "Basic double inherit order" ) if inh.p_three != 8: failure( "Basic double init order" ) if inh.basic() != 3: failure( "Basic double overload" ) if inh.base_one.basic() != 1: failure( "Double inh. base method access 1" ) if inh.base_two.basic() != 2: failure( "Double inh. base method access 2" ) success() /* End of file */ tests/core/testsuite/initfirst.fal000066400000000000000000000027611176363201700177140ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 22d * Category: types * Subcategory: objects * Short: Init on first use * Description: * Tests for init on first use sample. * The object declaration and alphabetical order ensure that the "center" * object is initialized before one and after another of the two other * objects. This test is succesful if the main routine can correctly * access the properties that are initialized in this tangled way. * [/Description] * ****************************************************************************/ object alpha sum = "" init // Reading first.list via the accessor for i in [ len( center.getList() )-1 : 0 : -1] // we have called the accessor, so the list is initalized self.sum += center.list[i] end end end object center list = nil init // forces initialization if this has not still happened self.getList() end // accessor... function getList() // ... using init on first use idiom. if self.list == nil: self.list = ["a", "b", "c"] return self.list end end object omega sum = nil init // Reading first.list via the accessor self.sum = "" for elem in center.getList() self.sum += elem end end end if alpha.sum != "cba": failure( "Alpha init" ) if omega.sum != "abc": failure( "Omega init" ) if center.list[0] != "a": failure( "Center init" ) success() /* End of file */ tests/core/testsuite/intl_substring.fal000066400000000000000000000014371176363201700207460ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 120a * Category: rtl * Subcategory: internat * Short: Substring international * Description: * Check if ranged access works with international strings. * [/Description] * ****************************************************************************/ // check working on international strings. cats = "Falcon, Gedit,Check , Ubuntu ,\xf20" if cats[0:6] != "Falcon": failure( "International sub[0:6]" ) if cats[14:19] != "Check": failure( "International sub[14:20]" ) if cats[5:0] != "noclaF": failure( "International sub[5:0]" ) if cats[-9:-3] != "Ubuntu": failure( "International [-10:-3]" ) if cats[-1:-5] != "\xf20, ut": failure( "International [-10:-3]" ) success() /* end of file */ tests/core/testsuite/intl_trim.fal000066400000000000000000000013711176363201700176760ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 120b * Category: rtl * Subcategory: internat * Short: Trim international * Description: * Checks if trim works correctly with wide strings. * [/Description] * ****************************************************************************/ // check working on international strings. cats = strSplit("Falcon, Gedit,Check , Ubuntu ,\xf20", ",") if cats[0].trim() != "Falcon": failure( "International trim - none" ) if cats[1].trim() != "Gedit": failure( "International trim - front" ) if cats[2].trim() != "Check": failure( "International trim - back" ) if cats[3].trim() != "Ubuntu": failure( "International trim - both" ) success() /* end of file */ tests/core/testsuite/iter_array.fal000066400000000000000000000021751176363201700200410ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 35a * Category: iterators * Subcategory: * Short: Iterators on arrays * Description: * Tests for iterator usage on arrays. * [/Description] * ****************************************************************************/ array = [1, 2, 3, 4] // test 1: forward iteration it = Iterator( array ) dest = 0 while it.hasCurrent() dest += it.value() it.next() end if dest != 10: failure( "Forward iteration" ) // test 2: backward iteration it = Iterator( array, -1 ) dest = [] while it.hasCurrent() dest += it.value() it.prev() end if dest[-1] != array[0]: failure( "Reverse iteration" ) // test 3: item change it = Iterator( array ) while it.hasCurrent() if it.value() == 4 it.value( 10 ) end it.next() end if array[-1] != 10: failure( "Item change" ) // test 4: item remove it = Iterator( array ) while it.hasCurrent() if it.value() == 2 it.erase() else it.next() end end if array[0] != 1 and array[1] != 3: failure( "Item erase" ) if array.len() != 3: failure( "Item erase -- len" ) success() /* End of file */ tests/core/testsuite/iter_dict.fal000066400000000000000000000025551176363201700176500ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 35b * Category: iterators * Subcategory: * Short: Iterators on dictionaries * Description: * Tests for iterator usage on dictionarys. * [/Description] * ****************************************************************************/ dictionary = [ "a"=> 1, "z" => 2, "c"=>3, "k"=>4 ] // test 1: forward iteration it = Iterator( dictionary ) dest = "" destNum = 0 while it.hasCurrent() dest += it.key() destNum += it.value() it.next() end if destNum != 10: failure( "Forward iteration -- value" ) if dest != "ackz": failure( "Forward iteration -- key" ) // test 2: backward iteration it = Iterator( dictionary, -1 ) dest = "" destNum = 0 loop dest += it.key() destNum += it.value() end not it.prev() if destNum != 10: failure( "Reverse iteration -- value" ) if dest != "zkca": failure( "Reverse iteration -- key" ) // test 3: item change it = Iterator( dictionary ) while it.hasCurrent() if it.key() == "k" it.value( 10 ) end it.next() end if dictionary["k"] != 10: failure( "Item change" ) // test 4: item remove it = Iterator( dictionary ) while it.hasCurrent() if it.key() == "k" it.erase() else it.next() end end if "k" in dictionary: failure( "Item erase" ) if dictionary.len() != 3: failure( "Item erase -- len" ) success() /* End of file */ tests/core/testsuite/lambda.fal000066400000000000000000000010051176363201700171070ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 18a * Category: lambda * Subcategory: * Short: Lambda expressions * Description: * Basic test for lambda expressions * [/Description] * ****************************************************************************/ var = {a, b => a * b} if var( 2, 3) != 6: failure( "Lambda assingment" ) if 1 + {a, b => a * b}( 3, 2 ) + 2 != 9 failure( "Lambda in expression parsing" ) end success() /* End of file */ tests/core/testsuite/lastline.fal000066400000000000000000000010051176363201700175020ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 1k * Category: basic * Subcategory: parser * Short: Last line * Description: * Check for correct addition of a fake EOL if the file is not * terminating with an EOL. Of course, we'll have to drop * closing comment for once. * [/Description] * ****************************************************************************/ if 1 success() // Ok, as we want to test for the compiler ability to find "end" end tests/core/testsuite/let.fal000066400000000000000000000011111176363201700164510ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 70b * Category: functional * Subcategory: constructs * Short: let * Description: * Tests for let functional operator to work correctly * [/Description] * ****************************************************************************/ v = nil let( $v, 0 ) if v != 0: failure( "direct let" ) v = nil let( $v, .[random 1 10] ) if v == nil: failure( "indirect let" ) v = nil eval( .[ let $v .[random 1 10] ] ) if v == nil: failure( "evaluated let" ) success() /* end of file */ tests/core/testsuite/lettest.fal000066400000000000000000000007051176363201700173610ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 1e * Category: basic * Subcategory: connectors * Short: Let operator test * Description: * This is a prerequisite for the conditional tests * [/Description] * ****************************************************************************/ a = 1 a == 1 and (b = 1) if a == 1 if b == 1 success() end end failure() /* End of file */ tests/core/testsuite/lex_binary.fal000066400000000000000000000015541176363201700200340ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 40j * Category: types * Subcategory: binaries * Short: Binary test * Description: * Checks for correctness of binary sintaxis as r'0[bB][01_]+' * [/Description] * ****************************************************************************/ // '0b' prefix if 0b1111 != 15: failure( "0b1111" ) if 0b11_11 != 15: failure( "0b11_11" ) if 0b_1111 != 15: failure( "0b_1111" ) if 0b1111_ != 15: failure( "0b1111_" ) if 0b_1_1_1_1_ != 15: failure( "0b_1_1_1_1_" ) if 0b__1111 != 15: failure( "0b__1111" ) // '0B' prefix if 0B1111 != 15: failure( "0B1111" ) if 0B_1_1_1_1_ != 15: failure( "0B_1_1_1_1_" ) if 0B__1111 != 15: failure( "0B__1111" ) if 0b11111010 != 250: failure( "0b11111010" ) if 0b_1001_0110 != 150: failure( "0b_1001_0110" ) success() /* End of file */ tests/core/testsuite/lex_hexadecimal.fal000066400000000000000000000017331176363201700210130ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 40k * Category: types * Subcategory: hexadecimals * Short: Hexadecimal test * Description: * Checks for correctness of hexadecimal sintaxis as r'0[xX][a-fA-F0-9_]+' * [/Description] * ****************************************************************************/ //'0x' prefix if 0x110DB != 69851: failure( "0x110DB" ) if 0x1_10DB != 69851: failure( "0x1_10DB" ) if 0x_110DB != 69851: failure( "0x_110DB" ) if 0x110DB_ != 69851: failure( "0x110DB_" ) if 0x_1_1_0_D_B_ != 69851: failure( "0x_1_1_0_D_B_" ) if 0x__110DB != 69851: failure( "0x__110DB" ) // '0X' prefix if 0X110DB != 69851: failure( "0X110DB" ) if 0X_1_1_0_D_B_ != 69851: failure( "0X_1_1_0_D_B_" ) if 0X__110DB != 69851: failure( "0X__110DB" ) if 0x110db != 69851: failure( "0x110db" ) if 0x110dB != 69851: failure( "0x110dB" ) if 0x_ABC_DEF5 != 180150005: failure( "0x_ABC_DEF5" ) success() /* End of file */ tests/core/testsuite/lex_octal.fal000066400000000000000000000021371176363201700176500ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 40l * Category: types * Subcategory: octals * Short: Octal test * Description: * Checks for correctness of octal sintaxis as r'0[cC]?[0-7]+[0-7_]*' * [/Description] * ****************************************************************************/ // '0c' prefix if 0c44242 != 18594: failure( "0c44242" ) if 0c44_242 != 18594: failure( "0c44_242" ) if 0c_44242 != 18594: failure( "0c_44242" ) if 0c44242_ != 18594: failure( "0c44242_" ) if 0c_4_4_2_4_2_ != 18594: failure( "0c_4_4_2_4_2_" ) if 0c__44242 != 18594: failure( "0c__44242" ) // '0C' prefix if 0C44242 != 18594: failure( "0C44242" ) if 0C_4_4_2_4_2_ != 18594: failure( "0C_4_4_2_4_2_" ) if 0C__44242 != 18594: failure( "0C__44242" ) // '0' prefix if 044242 != 18594: failure( "044242" ) if 04_4_2_4_2_ != 18594: failure( "04_4_2_4_2_" ) if 04_4242_ != 18594: failure( "044242" ) if 0c_601_630 != 197528: failure( "0c_601_630" ) if 02165303 != 584387: failure( "02165303" ) if 02165307 != 584391: failure( "02165307" ) success() /* End of file */ tests/core/testsuite/list.fal000066400000000000000000000114201176363201700166440ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 116a * Category: rtl * Subcategory: list * Short: Basic list * Description: * We test the RTL list class basic functionalities * * [/Description] * ****************************************************************************/ // empty list = List() if list.len() != 0: failure( "Empty constructor len" ) if not list.empty(): failure( "Empty constructor empty" ) list.push( "a" ) if list.len() != 1: failure( "After push len" ) if list.empty(): failure( "After push empty" ) if list.front() != "a": failure( "After push front" ) if list.back() != "a": failure( "After push back" ) list.pushFront( "i" ) if list.len() != 2: failure( "After pushFront len" ) if list.empty(): failure( "After pushFront empty" ) if list.front() != "i": failure( "After pushFront front" ) if list.back() != "a": failure( "After pushFront back" ) list.push( "z" ) if list.len() != 3: failure( "After push 2 len" ) if list.empty(): failure( "After push 2 empty" ) if list.front() != "i": failure( "After push 2 front" ) if list.back() != "z": failure( "After push 2 back" ) // let's start pops elem = list.pop() if elem != "z": failure( "After pop 1 element" ) if list.len() != 2: failure( "After pop 1 len" ) if list.empty(): failure( "After pop 1 empty" ) if list.front() != "i": failure( "After pop 1 front" ) if list.back() != "a": failure( "After pop 1 back" ) elem = list.pop() if elem != "a": failure( "After pop 2 element" ) if list.len() != 1: failure( "After pop 2 len" ) if list.empty(): failure( "After pop 2 empty" ) if list.front() != "i": failure( "After pop 2 front" ) if list.back() != "i": failure( "After pop 2 back" ) elem = list.pop() if elem != "i": failure( "After pop 3 element" ) if list.len() != 0: failure( "After pop 3 len" ) if not list.empty(): failure( "After pop 3 empty" ) // test again with reverse order. list.push( "z" ) list.push( "a" ) list.push( "i" ) // let's start pops elem = list.popFront() if elem != "z": failure( "After popFront 1 element" ) if list.len() != 2: failure( "After popFront 1 len" ) if list.empty(): failure( "After popFront 1 empty" ) if list.front() != "a": failure( "After popFront 1 front" ) if list.back() != "i": failure( "After popFront 1 back" ) elem = list.popFront() if elem != "a": failure( "After popFront 2 element" ) if list.len() != 1: failure( "After popFront 2 len" ) if list.empty(): failure( "After popFront 2 empty" ) if list.front() != "i": failure( "After popFront 2 front" ) if list.back() != "i": failure( "After popFront 2 back" ) elem = list.popFront() if elem != "i": failure( "After popFront 3 element" ) if list.len() != 0: failure( "After popFront 3 len" ) if not list.empty(): failure( "After popFront 3 empty" ) // test again, mixed order. list.push( "z" ) list.push( "a" ) list.push( "i" ) elem = list.popFront() if elem != "z": failure( "After pop-mix 1 element" ) if list.len() != 2: failure( "After pop-mix 1 len" ) if list.empty(): failure( "After pop-mix 1 empty" ) if list.front() != "a": failure( "After pop-mix 1 front" ) if list.back() != "i": failure( "After pop-mix 1 back" ) elem = list.pop() if elem != "i": failure( "After pop-mix 2 element" ) if list.len() != 1: failure( "After pop-mix 2 len" ) if list.empty(): failure( "After pop-mix 2 empty" ) if list.front() != "a": failure( "After pop-mix 2 front" ) if list.back() != "a": failure( "After pop-mix 2 back" ) elem = list.popFront() if elem != "a": failure( "After pop-mix 3 element" ) if list.len() != 0: failure( "After pop-mix 3 len" ) if not list.empty(): failure( "After pop-mix 3 empty" ) // test again, mixed order. list.pushFront( "z" ) list.pushFront( "a" ) list.pushFront( "i" ) elem = list.pop() if elem != "z": failure( "After pop-remix 1 element" ) if list.len() != 2: failure( "After pop-remixremix 1 len" ) if list.empty(): failure( "After pop-remix 1 empty" ) if list.front() != "i": failure( "After pop-remix 1 front" ) if list.back() != "a": failure( "After pop-remix 1 back" ) elem = list.popFront() if elem != "i": failure( "After pop-remix 2 element" ) if list.len() != 1: failure( "After pop-remix 2 len" ) if list.empty(): failure( "After pop-remix 2 empty" ) if list.front() != "a": failure( "After pop-remix 2 front" ) if list.back() != "a": failure( "After pop-remix 2 back" ) elem = list.pop() if elem != "a": failure( "After pop-remix 3 element" ) if list.len() != 0: failure( "After pop-remix 3 len" ) if not list.empty(): failure( "After pop-remix 3 empty" ) // list constructor //list = List( 1, 2, 3, "a", "b", "c" ) //if list.len() != 6: failure( "Nonempty constructor len" ) // test for clear for i in [ 1 : 10 ] list.push(i) end list.clear() if list.len() != 0: failure( "After clear len" ) if not list.empty(): failure( "After clear empty" ) success() /* End of file */ tests/core/testsuite/list_forin.fal000066400000000000000000000045071176363201700200510ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 116j * Category: rtl * Subcategory: list * Short: List for/in * Description: * Test for exceptions list working in for/in loops. * * [/Description] * ****************************************************************************/ // Empty list list = List() count = 0 for item in list count ++ end if count != 0: failure( "Empty list" ) // list parse list = List( "a", "b", "c" ) cfr = [ "a", "b", "c" ] count = 0 for elem in list if elem != cfr[count] failure( "Traversal at " + toString( count ) ) end ++count end if count != 3: failure( "Traverasal count 1" ) // and again. count = 0 for elem in list if elem != cfr[count] failure( "Re-traversal at " + toString( count ) ) end ++count end if count != 3: failure( "Traverasal count 2" ) // Traversal with forfirst/forlast // and again. count = 0 for elem in list forfirst if elem != "a": failure( "ForFirst" ) end ++count forlast if elem != "c": failure( "ForLast" ) end end if count != 3: failure( "Forfirst/forlast count" ) // Traversal with forfirst/forlast/formiddle // and again. elements = "" for elem in list forfirst elements = ">" end elements += elem formiddle elements += ", " end forlast elements += "." end end if elements != ">a, b, c." failure( "Complete for/in" ) end // Setting an item. for elem in list if elem == "c" .= "z" end end if list.back() != "z": failure( "Setting an item" ) // deleting an item compose = "" for elem in list if elem == "b" continue dropping end compose += elem end if compose != "az": failure( "Rest of delete" ) if list.len() != 2: failure( "Size after delete" ) compose = "" iter = list.first() while iter.hasCurrent() compose += iter.value() iter.next() end if compose != "az": failure( "Rest of delete (recheck)" ) compose = "" for elem in list compose += elem end if compose != "az": failure( "Rest of delete (recheck 2)" ) // delete of last element for item in list if item == "z" continue dropping end end if list.front() != "a" or list.back() != "a" failure( "Continue dropping on last" ) end // check with fordot - on the same line for elem in list: .= "d" success() /* End of file */ tests/core/testsuite/list_gc.fal000066400000000000000000000027021176363201700173200ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 116c * Category: rtl * Subcategory: list * Short: List memory * Description: * We test the RTL List to correctly mark, save and dispose * items being stored in them. * * [/Description] * ****************************************************************************/ class tester( initparam ) name = initparam end print() // create a reusable frame for i in [1 : 11] end // initialize references GC.perform( true ) aliveMem = GC.aliveMem items = GC.items usedMem = GC.usedMem // create a list list = List() // change gc mark GC.perform( true ) for i in [1 : 11] list.push( tester( toString(i) ) ) end // a recursive list list.pushFront( list ) // we have no reference of the original items, but they are stored deep in the list. // so, if we destroy them, we'll crash here: GC.perform( true ) for i in [ 10 : 1 ] elem = list.pop() if int( elem.name ) != i failure( "coherency of stored item " + toString(i) ) end end // Now the items can be collected; verify that we clean the memory: list = nil elem = nil // clear the A register a = 2 * 1 // be sure to kill both marks GC.perform( true ) GC.perform( true ) if aliveMem != GC.aliveMem: failure( "unmatching allocated memory" ) if usedMem != GC.usedMem: failure( "unmatching used memory" ) if items != GC.items: failure( "unmatching live items items" ) success() /* End of file */ tests/core/testsuite/list_init.fal000066400000000000000000000026721176363201700177000ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 116b * Category: rtl * Subcategory: list * Short: Initialized list * Description: * We test the RTL List for consistence when being externally * initialized. * * [/Description] * ****************************************************************************/ // empty list = List( "a", "b", "c" ) if list.len() != 3: failure( "init size" ) if list.empty(): failure( "init empty" ) if list.front() != "a": failure( "init front" ) if list.back() != "c": failure( "init back" ) // insert and remove back list.push( "z" ) if list.back() != "z": failure( "back after push" ) elem = list.pop() if elem != "z": failure( "pop back item" ) if list.back() != "c": failure( "back after push and pop" ) elem = list.pop() if elem != "c": failure( "back item after push and pop" ) // insert and remove front list.pushFront( "z" ) if list.front() != "z": failure( "front after pushFront" ) elem = list.popFront() if elem != "z": failure( "pop front item" ) if list.front() != "a": failure( "front after push and pop" ) elem = list.popFront() if elem != "a": failure( "front item after push and pop" ) if list.front() != "b": failure( "Remained element front" ) if list.back() != "b": failure( "Remained element back" ) list.popFront() if list.len() != 0: failure( "empty list size" ) if not list.empty(): failure( "empty list empty" ) success() /* End of file */ tests/core/testsuite/list_iter.fal000066400000000000000000000030601176363201700176700ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 116e * Category: rtl * Subcategory: list * Short: List iterators basic * Description: * We test the RTL List iterators; here we test for basic operations, * that are forward and backward scans. * [/Description] * ****************************************************************************/ list = List( "a", "b", "c" ) // forward processing iter = list.first() count = 0 while iter.hasCurrent() switch count case 0 if iter.value() != "a": failure( "forward 0" ) case 1 if iter.value() != "b": failure( "forward 1" ) case 2 if iter.value() != "c": failure( "forward 2" ) end count ++ iter.next() end if count != 3: failure( "forward count" ) if list.len() != 3: failure( "forward invariance size" ) if list.front() != "a": failure( "forward invariance front" ) if list.back() != "c": failure( "forward invariance back" ) // backward processing iter = list.last() count = 0 while iter.hasCurrent() switch count case 0 if iter.value() != "c": failure( "backward 0" ) case 1 if iter.value() != "b": failure( "backward 1" ) case 2 if iter.value() != "a": failure( "backward 2" ) end count ++ iter.prev() end if count != 3: failure( "forward count" ) if list.len() != 3: failure( "forward invariance size" ) if list.front() != "a": failure( "forward invariance front" ) if list.back() != "c": failure( "forward invariance back" ) success() /* End of file */ tests/core/testsuite/list_iter2.fal000066400000000000000000000026101176363201700177520ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 116f * Category: rtl * Subcategory: list * Short: List iterators insert * Description: * We test the RTL List iterators; here we test for advanced operations, * namely insertion at various positions. * [/Description] * ****************************************************************************/ list = List() // insertion in an empty list iter = list.first() iter.insert( "a" ) if list.len() != 1 or list.front() != "a": failure( "insert in empty" ) // head insertion list = List( "a", "b", "c" ) iter = list.first() iter.insert( "z" ) if list.len() != 4 or list.front() != "z": failure( "head insertion" ) // tail nsertion iter = list.last() iter.next() iter.insert( "y" ) if list.len() != 5 or list.back() != "y": failure( "head insertion" ) // middle insertion iter = list.first() iter.next() iter.insert( "w" ) list.popFront() if list.front() != "w": failure( "Insertion in the middle" ) // iterator validity iter = list.first() iter2 = list.first() iter.next() iter2.next() // record the data that should be here. data = iter.value() iter.insert( "n" ) // is the old data still there? if iter2.value() != data: failure( "Iterator consistent validity" ) // ... and the new data under current iterator? if iter.value() != "n": failure( "Iterator self-validity" ) success() /* End of file */ tests/core/testsuite/list_iter3.fal000066400000000000000000000036521176363201700177620ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 116g * Category: rtl * Subcategory: list * Short: List iterators erase * Description: * We test the RTL List iterators; here we test for advanced operations, * namely erase at various positions. * [/Description] * ****************************************************************************/ list = List() // erase in an empty list iter = list.first() try iter.erase() failure( "Invalid exception not raised" ) end if list.len() != 0: failure( "erase empty" ) // head erase list = List( "a", "b", "c" ) iter = list.first() iter.erase() if list.len() != 2 or list.front() != "b": failure( "head erase" ) if iter.value() != "b": failure( "Iterator status after erase" ) // tail erase list = List( "a", "b", "c" ) iter = list.last() iter.erase() if list.len() != 2 or list.back() != "b": failure( "tail erase" ) if iter.hasCurrent(): failure( "Invalidation of tail erase" ) // middle erase list = List( "a", "b", "c" ) iter = list.first() iter.next() iter.erase() if list.front() != "a": failure( "Head after erase middle" ) if list.back() != "c": failure( "Tail after erase middle" ) if iter.value() != "c": failure( "Tail after erase middle" ) // iterator validity // we prepare a set of iterators so that // a b c // 0 1+3 2 // Then we remove B via 1; 0 and 2 must still be valid, // 1 must be valid and moved to C and 3 must be invalid list = List( "a", "b", "c" ) iter0 = list.first() iter1 = list.first() iter1.next() iter2 = list.last() iter3 = list.last() iter3.prev() // a first pre-check if iter1.value() != iter3.value(): failure( "erase invalidation pre" ) iter1.erase() if iter3.hasCurrent(): failure( "iter3 not invalid" ) if iter0.value() != "a": failure( "iter0 moved" ) if iter2.value() != "c": failure( "iter2 moved" ) if iter1.value() != "c": failure( "iter1 not moved" ) success() /* End of file */ tests/core/testsuite/list_iter4.fal000066400000000000000000000013021176363201700177510ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 116h * Category: rtl * Subcategory: list * Short: List iterators API * Description: * This test verifies that lists can be fully accessed through CORE * iterator API. * [/Description] * ****************************************************************************/ list = List( "a", "b", "c" ) iter = Iterator( list ) if iter.value() != "a": failure( "Head iterator" ) iter.next() iter.erase() if iter.value() != "c": failure( "Erase" ) iter.insert( "z" ) if iter.value() != "z": failure( "Insert" ) iter.next() if iter.value() != "c": failure( "Next after insert" ) success() /* End of file */ tests/core/testsuite/list_raise.fal000066400000000000000000000016321176363201700200330ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 116d * Category: rtl * Subcategory: list * Short: List errors * Description: * Test for exceptions regardings lists being raised when needed. * * [/Description] * ****************************************************************************/ list = List() try list.pop() failure( "Popping empty list" ) end try list.popFront() failure( "Popping front empty list" ) end try list.back() failure( "Tacking back from empty list" ) end try list.front() failure( "Taking front from empty list" ) end // try insert with a non coherent iterator thing = [1=>2] iter = Iterator( thing ) try list.insert( iter, nil ) failure( "Insert with invalid iterator" ) end iter = list.first() try list.erase( iter ) failure( "Erase with invalid iterator" ) end success() /* End of file */ tests/core/testsuite/listexpand.fal000066400000000000000000000022151176363201700200460ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 11d * Category: types * Subcategory: array * Short: List expansion * Description: * This test checks the correct interpretation of expressions as * a, b, c = 1, 2, 3 * Using only integers in this test * [/Description] * ****************************************************************************/ // basic test a, b, c = 1, 2, 3 if a != 1 or b !=2 or c != 3: failure( "Basic expansion -- 1" ) a, b, c = [1, 2, 3] if a != 1 or b !=2 or c != 3: failure( "Basic expansion -- 2" ) array = 4, 5, 6 e, f, g = array if e != 4 or f != 5 or g != 6: failure( "Translated expansion" ) a, b, c = testReflect( [6, 7, 8] ) if a != 6 or b != 7 or c != 8: failure( "Returned value expansion" ) a, b, c = [1, 2], 3, 4 if len( a ) != 2 or b != 3 or c != 4: failure( "Complex expansion" ) a, b, c = testReflect( [ [1, 2], 3, 4 ] ) if len( a ) != 2 or b != 3 or c != 4: failure( "Complex return value expansion" ) array = [1, 4], 5, 6 a, b, c = array if len( a ) != 2 or b != 5 or c != 6: failure( "Complex translated value expansion" ) success() /* End of file */ tests/core/testsuite/loop_module_a.fal000066400000000000000000000007051176363201700205130ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: -- * Category: modloader * Subcategory: * Short: Circular loads. * Description: * Used from test 50e (loop_module_main.fal) * [/Description] * ****************************************************************************/ load loop_module_b class CModA pa = CModB() function value() return "Value from A" end end export CModA tests/core/testsuite/loop_module_b.fal000066400000000000000000000006571176363201700205220ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: -- * Category: modloader * Subcategory: * Short: Circular loads. * Description: * Used from test 50e (loop_module_main.fal) * [/Description] * ****************************************************************************/ load loop_module_a class CModB pb = CModA.value() prop = "Property from b" end export CModB tests/core/testsuite/loop_module_main.fal000066400000000000000000000015451176363201700212220ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 50e * Category: modloader * Subcategory: * Short: Circular loads. * Description: * Tests for the VM to be able to link circular loads. * Uses loop_module_a and loop_module_b submodules, linking each other. * [/Description] * ****************************************************************************/ load loop_module_a load loop_module_b ia = CModA() ib = CModB() if ib.prop != "Property from b": failure( "Direct init" ) if ib.pb != "Value from A": failure( "Init via recursive symbol" ) if not ia.pa.derivedFrom( CModB ): failure( "Recursive constructor" ) if ia.pa.prop != "Property from b" : failure( "Recursive constructor direct init" ) if ia.pa.pb != "Value from A": failure( "Recursive constructor init via recursive symbol" ) success() tests/core/testsuite/macro.fal000066400000000000000000000007031176363201700167740ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 91a * Category: macro * Subcategory: * Short: Macro smoke * Description: * Minimal macro compiler smoke test. * [/Description] * ****************************************************************************/ macro mmin( a, b ) (($a<$b ? $a : $b)) if \\mmin( 3, 4 ) != 3: failure( "Min1" ) if \\mmin( 4, 3 ) != 3: failure( "Min2" ) success() tests/core/testsuite/macroacc.fal000066400000000000000000000010511176363201700174400ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 91c * Category: macro * Subcategory: * Short: Macro access * Description: * An even more serious access test. * [/Description] * ****************************************************************************/ macro access1( a, b ) ("$(a).$b") macro access2( a, b ) ($(a).$b) object test prop = "Hello" end if \\access1( test , prop ) != "test.prop": failure( "Trim" ) if \\access2( test , prop ) != "Hello": failure( "Access" ) success() tests/core/testsuite/macroclass.fal000066400000000000000000000010511176363201700200170ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 91b * Category: macro * Subcategory: * Short: Macro class * Description: * A serious macro definition test. * [/Description] * ****************************************************************************/ macro makeClass( name ) ( class $name( value ) prop = value end ) \\makeClass( MyClass ) try x = MyClass( "val" ) catch failure( "Class generation" ) end if x.prop != "val": failure( "Instance generation" ) success() tests/core/testsuite/macroesc.fal000066400000000000000000000006771176363201700175010ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 91d * Category: macro * Subcategory: * Short: Macro escape * Description: * Checks for escapes (string escapes) correctly respected in macro calls. * [/Description] * ****************************************************************************/ macro t( a, b ) ($(a)($b)) if \\t( len , "Hello" ) != 5: failure( "Escaping" ) success() tests/core/testsuite/master_1.fal000066400000000000000000000010531176363201700174050ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 50a * Category: modloader * Subcategory: * Short: Basic cascade compilation test. * Description: * This program loads the slave_1 module, which is in the same directory * as a source file. Then it calls a function that is in that module. * [/Description] * ****************************************************************************/ load slave_1 if not fromSlave() failure( "didn't load slave module" ) end success() /* End of file */ tests/core/testsuite/master_2.fal000066400000000000000000000017001176363201700174050ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 50b * Category: modloader * Subcategory: * Short: Multiple cascade compilation. * Description: * The structure of this compilation test is a bit more complex. * We require the loading of two modules, each of which loads other * two modules. One of this two modules loads again one of the modules * we load. * [/Description] * ****************************************************************************/ load slave_1 load slave_2 load slave_3 if not fromSlave(): failure( "didn't load slave_1 module" ) if not fromSlave_2(): failure( "didn't load slave_2 module" ) if not fromSlave_3(): failure( "didn't load slave_3 module" ) if not fromSlave_4(): failure( "didn't load slave_4 module" ) if not fromSlave_5(): failure( "didn't load slave_5 module" ) if not fromSlave_6(): failure( "didn't load slave_6 module" ) success() /* End of file */ tests/core/testsuite/master_3.fal000066400000000000000000000020511176363201700174060ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 50c * Category: modloader * Subcategory: * Short: Link time initialization. * Description: * The structure of this compilation test is a bit more complex. * We require the loading of two modules, each of which loads other * two modules. One of this two modules loads again one of the modules * we load. * [/Description] * ****************************************************************************/ load slave_alpha load slave_beta if world.items != 3: failure( "world initialization" ) world.elements[0].setVal( "first" ) world.elements[1].setVal( "second" ) world.elements[2].setVal( "third" ) if alpha.val == nil: failure( "child reference - 1" ) if beta.val == nil: failure( "child reference - 2" ) if delta.val == nil: failure( "child reference - 3" ) if alpha.val != "first": failure( "link order - 1" ) if delta.val != "second": failure( "link order - 2" ) if beta.val != "third": failure( "link order - 3" ) success() /* End of file */ tests/core/testsuite/master_4.fal000066400000000000000000000011051176363201700174060ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 50d * Category: modloader * Subcategory: * Short: Globals across modules * Description: * This test checks for global data to be passed across modules, * and for private data to stay private. * [/Description] * ****************************************************************************/ load slave_glob import glob // import, not necessary if glob != "From Slave": failure( "Slave module initialization" ) glob = "Changed" TestGlob() success() /* End of file */ tests/core/testsuite/membuf_bit.fal000066400000000000000000000022461176363201700200100ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 118a * Category: membuf * Subcategory: * Short: Membuf byte ops * Description: * Checks for memory buffer operations with the new NIO-like interface. * [/Description] * ****************************************************************************/ // create a nice membuffer mb = MemBuf(16) mb.put( 65 ).put( 66 ).put( 67 ) if mb.position() != 3: failure( "Basic put" ) mb.mark() mb.put( 68 ).put( 69 ).put( 70 ) mb.reset() if mb.position() != 3: failure( "Reset put" ) if mb.get() != 68: failure( "Get value 1" ) if mb.get() != 69: failure( "Get value 2" ) if mb.get() != 70: failure( "Get value 3" ) mb.reset() if mb.position() != 3: failure( "Reset put 2" ) // let's flip mb.flip() // position -> limit, 0 -> position if mb.remaining() != 3: failure( "Flip" ) // and now a bit of compact mb.position( mb.limit() ) mb.limit( 6 ) mb.compact() if mb.position() != 3: failure( "compact - 1" ) if mb.remaining() != 13: failure( "compact - 2" ) // See if compact worked if mb[0] != 68 or mb[1] != 69 or mb[2] != 70 failure( "compact - 3 " ) end success() /* end of file */ tests/core/testsuite/membuf_bit_rw.fal000066400000000000000000000026011176363201700205130ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 118b * Category: membuf * Subcategory: * Short: Membuf reads * Description: * Checks for memory buffer operations with the new NIO-like interface. * [/Description] * ****************************************************************************/ // create a nice membuffer mb = MemBuf(8) // emulate a variable search in a stream. // we'll parse this string. orig = "Hello world, we need some spaces" sstr = StringStream( orig ) words = [] loop word = "" readin = sstr.read( mb ) if readin > mb.position(): failure( "Position not moved after read" ) mb.flip() // ok, start reading mb.mark() // provides a default mark while mb.remaining() char = mb.get() // word complete? if char == 0x20 words += word word = "" // mark if we find a partial word mb.mark() mark = true else word %= char end end // we have a partial word mb.reset() // return to the previous mark mb.compact() // prepare to read some more. end sstr.eof() if word != "": words += word cfr = strSplit( orig, " " ) if cfr.len() != words.len() failure( "Failure (len)" ) end for i in [0:cfr.len()] if words[i] != cfr[i] failure( @@"Word at $i is $$words[$i]") end end success() /* end of file */ tests/core/testsuite/membuf_range.fal000066400000000000000000000017461176363201700203320ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 118c * Category: membuf * Subcategory: range * Short: Memory buffer ranged accessors. * Description: * Checks if the range accessor correctly creates memory buffer slices. * [/Description] * ****************************************************************************/ mb = MemBuf( 10 ) for i in [0:10]: mb[i] = i + 0x60 m2 = mb[:] if m2.len() != 10: failure( "Clone size" ) m2.describe() != "MB(10,1) [60 61 62 63 64 65 66 67 68 69 ]" and failure( "[:]" ) m2[2:5].describe() != "MB(3,1) [62 63 64 ]" and failure( "[2:5]" ) m2[2:2].describe() != "MB(0,1) []" and failure( "[2:2]" ) m2[3:-1].describe() != "MB(6,1) [63 64 65 66 67 68 ]" and failure( "[3:-1]" ) m2[7:3].describe() != "MB(5,1) [67 66 65 64 63 ]" and failure( "[7:3]" ) m2[1:5:2].describe() != "MB(2,1) [61 63 ]" and failure( "[1:5:2]" ) m2[-2:3:-2].describe() != "MB(3,1) [68 66 64 ]" and failure( "[-2:3:-2]" ) success() tests/core/testsuite/metacomp.fal000066400000000000000000000006121176363201700174770ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 90a * Category: meta * Subcategory: * Short: Meta smoke * Description: * Smoketest for meta compiler. * [/Description] * ****************************************************************************/ \[ >> "variable" \] = 100 if variable != 100: failure( "Not compiled" ) success() tests/core/testsuite/metaextern.fal000066400000000000000000000011741176363201700200520ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 90d * Category: meta * Subcategory: * Short: Meta extern * Description: * Loading meta compilation fragments from external modules. * [/Description] * ****************************************************************************/ \[ // loading inside a meta-block loads a meta library load metaextern_module \] value = 250 if \[ >> var \] != 250 failure( "Basic load" ) end // calling a meta function from extern code. \[ MetaExternCode() \] if meta_value != "Assigned" failure( "Function load" ) end success() tests/core/testsuite/metaextern_module.fal000066400000000000000000000007111176363201700214130ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: --- * Category: meta * Subcategory: * Short: Meta extern * Description: * Part of the metaextern (90d) test. * This file acts as a macro library for metaextern.fal * [/Description] * ****************************************************************************/ var = "value" function MetaExternCode() >> 'meta_value = "Assigned"' end export tests/core/testsuite/metafrag.fal000066400000000000000000000010431176363201700174570ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 90c * Category: meta * Subcategory: * Short: Meta fragments * Description: * Tests for fragmented (incremental) metacompiler usage. * [/Description] * ****************************************************************************/ object first second = "value" end \[ var = "first" \] if \[ >>var \] != first failure( "First test" ) end \[ var += ".second" \] if \[ >> var \] != "value" failure( "Second test" ) end success() tests/core/testsuite/metatest.fal000066400000000000000000000010661176363201700175240ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 90b * Category: meta * Subcategory: * Short: Metacompiler * Description: * Minimal meta compiler test with some functions * [/Description] * ****************************************************************************/ \[ function factorial( number ) if number == 1: return 1 return factorial( number - 1 ) * number end // print the row. > "var = ", factorial( 5 ) \] if var != (5*4*3*2) failure( "Different" ) end success() tests/core/testsuite/method_static.fal000066400000000000000000000014611176363201700205240ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 21m * Category: types * Subcategory: classes * Short: Static external call prevention. * Description: * External functions always take for granted that the self object * is containing Falcon object. Calling a method directly from a * class causes the class to be passed as self, thus breaking this * convention. * [/Description] * ****************************************************************************/ try x = Stream.read catch in e // non-static methods from classes are cacheable... failure( "Caugth error " + e ) end // ... but they are not callable. try str = "some value" x( str ) failure( "Called non-static method." ) catch in e end success() /* End of file */ tests/core/testsuite/mincond.fal000066400000000000000000000017521176363201700173270ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 1a * Category: basic * Subcategory: conditionals * Short: Minimal conditionals * Description: * The tests in "basic" category are mandatory, as they test minimal * functionality that falcon scripts must support to test for themselves * to be right. * * Testsuite scripts will only use non-complex checks to evaluate the * status of a script, so conditionals on basic expressions must be * safe. * * This is the minimal test that must be passed, along with the smoke test, * to grant the validity of all the other tests. * [/Description] * ****************************************************************************/ a = 1 if a != 1 failure( "if !=" ) end if a > 2 failure( "if >" ) end if a < 0 failure( "if <" ) end if a >= 2 failure( "if >=" ) end if a <= 0 failure( "if <=" ) end if a == 1 success() end failure( "false negative on ==" ) /* End of file */ tests/core/testsuite/module_class_base.fal000066400000000000000000000013341176363201700213400ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: modloader * Subcategory: class * Short: Class properties across modules, base module * Description: * This is a slave module to be loaded by module_class_main (ID 52a). * It is never directly executed. * [/Description] * ****************************************************************************/ class Base( gamma, beta, alpha ) gamma = gamma beta = beta alpha = alpha static lastId = 0 id = nil init if alpha == nil alpha = "Nilled" end self.id = ++self.lastId end function overload() return "Base" end end export /* End of file */ tests/core/testsuite/module_class_main.fal000066400000000000000000000025151176363201700213540ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 52a * Category: modloader * Subcategory: class * Short: Class properties across modules * Description: * This tests checks for base class initialization and correct class * property initialization across modules. * [/Description] * ****************************************************************************/ load module_class_middle class Top( tau, beta, alpha ) from Middle( "Top", beta, alpha ) tau = tau function overload() return "Top" end end top = Top( "first", "second", "third" ) if top.Base.typeId() != ClassMethodType: failure( "Base class - type" ) if top.Middle.typeId() != ClassMethodType: failure( "Middle class - type" ) if top.alpha != "second": failure( "Top - alpha" ) if top.beta != "third": failure( "Top - beta" ) if top.gamma != "Middle": failure( "Top - gamma" ) if top.sigma != "Top": failure( "Top - sigma" ) if top.tau != "first": failure( "Top - tau" ) if top.id != 1: failure( "Top - id" ) if top.lastId != 1: failure( "Top - lastId (ref)" ) if top.overload() != "Top": failure( "Top - overloaded function" ) if top.Middle.overload() != "Middle": failure( "Top - overloaded via Middle" ) if top.Base.overload() != "Base": failure( "Top - overloaded via Base" ) success() /* End of file */ tests/core/testsuite/module_class_middle.fal000066400000000000000000000013771176363201700216730ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: modloader * Subcategory: class * Short: Class properties across modules, middle module * Description: * This is a slave module to be loaded by module_class_main (ID 52a). * It is never directly executed. * This test tries also to load module_class_base, which should be already * in place. * [/Description] * ****************************************************************************/ load module_class_base // Notice alpha and beta in reverse order class Middle( sigma, beta, alpha ) from Base( "Middle", alpha, beta ) sigma = sigma function overload() return "Middle" end end export /* End of file */ tests/core/testsuite/module_obj_base.fal000066400000000000000000000011051176363201700210010ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 52b * Category: modloader * Subcategory: object * Short: Checking correct implicit import of objects. * Description: * This test assigns a property to an object imported from a module * and checks if the other property is invariant. * [/Description] * ****************************************************************************/ load module_obj_middle MyObject.alpha = 500 if MyObject.beta != "Original string": failure( "invariancy" ) success() /* End of file */ tests/core/testsuite/module_obj_middle.fal000066400000000000000000000007011176363201700213260ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: modloader * Subcategory: object * Short: Checking correct implicit import of objects. * Description: * Central part of test 52b * [/Description] * ****************************************************************************/ object MyObject alpha = 0 beta = "Original string" end export MyObject /* End of file */ tests/core/testsuite/morecond.fal000066400000000000000000000015431176363201700175040ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 1b * Category: basic * Subcategory: conditionals * Short: More conditionals * Description: * This completes the test on basic conditional by testing also more complex * grammar, as i.e. the short if statement. This is test must * be passed in order to grant the quality of the unit test. * [/Description] * ****************************************************************************/ a = 1 if a != 1: failure( "if !=" ) if a > 2: failure( "if >" ) if a < 0: failure( "if <" ) if a >= 2: failure( "if >=" ) if a <= 0: failure( "if <=" ) // conditional on wide integers a = 600851475143 if a != 600851475143: failure( "if != wide" ) if a < 1: failure( "if < wide" ) if a == 600851475143: success() failure( "false negative on ==" ) /* End of file */ tests/core/testsuite/msg_basic.fal000066400000000000000000000044011176363201700176210ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 38a * Category: messages * Subcategory: * Short: Basic message * Description: * Test for subscribe and broadcast functions. * [/Description] * ****************************************************************************/ function handler1( z, y ) global h1c // check for unordered parameters too h1c = [z, y] end function handler2( value1, value2 ) global h2c h2c = [value1, value2] end object hobj function on_msg( value1, value2 ) global hmth hmth = [value1, value2] end end // Test1: see if a call passes through all subscribe( "msg", handler1 ) subscribe( "msg", handler2 ) subscribe( "msg", hobj ) broadcast( "msg", 'first', 'second' ) if h1c.len() != 2: failure( "Forming of handler1 - 1" ) if h2c.len() != 2: failure( "Forming of handler2 - 1" ) if hmth.len() != 2: failure( "Forming of handler3 - 1" ) if h1c[0] != 'first' or h1c[1] != 'second': failure( "Content of handler1 - 1" ) if h2c[0] != 'first' or h2c[1] != 'second': failure( "Content of handler2 - 1" ) if hmth[0] != 'first' or hmth[1] != 'second': failure( "Content of handler3 - 1" ) // Test2: check for unsubscription h1c = nil h2c = nil hmth = nil unsubscribe( "msg", handler1 ) unsubscribe( "msg", handler2 ) // allow the hobj to stay to see we did the call. broadcast( "msg", 'first', 'second' ) if hmth.len() != 2: failure( "Element still subscribed not called after unsub" ) if h1c != nil: failure( "Unsubscribed handler1 called" ) if h2c != nil: failure( "Unsubscribed handler2 called" ) // Test3: complete unsubscription hmth = nil unsubscribe( "msg", hobj ) broadcast( "msg", 'first', 'second' ) if hmth != nil : failure( "Single handler called after unsubscription" ) // Test4: see if we can force the object handler to handle other messages. subscribe( "other", hobj.on_msg ) broadcast( "other", 'first', 'second' ) if hmth[0] != 'first' or hmth[1] != 'second': failure( "Content of handler3 - 1" ) // Test5: see if we raise a failure in case of unhandled message in objects subscribe( "another", hobj ) try broadcast( "another", 'first', 'second' ) failure( "Error not raised for missing handler" ) catch AccessError // it's ok end success() /* End of file */ tests/core/testsuite/msg_coop.fal000066400000000000000000000022211176363201700174760ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 38d * Category: messages * Subcategory: * Short: Message cooperation * Description: * Check the cooperation on messages (parameter modify, message stealing) * [/Description] * ****************************************************************************/ function handler1( data ) // mark us data += "handler1" // declare the data done if marked if "marker" in data: consume() end function handler2( data ) // mark us data += "handler2" // declare the data done if marked if "marker" in data: consume() end // Subscribe the handlers subscribe( "msg", handler1 ) subscribe( "msg", handler2 ) // do a first broadcast with the data and see if they're in data = [] broadcast( "msg", data ) if "handler1" notin data: failure( "First handler not in - 1" ) if "handler2" notin data: failure( "Second handler not in - 1" ) // Then, tell our handlers to rip the data from the other. data = ["marker"] broadcast( "msg", data ) if "handler1" in data and "handler2" in data failure( "Signal not consumed" ) end success() /* end of file */ tests/core/testsuite/msg_gc.fal000066400000000000000000000022501176363201700171310ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 38e * Category: messages * Subcategory: GC * Short: GC & msgs * Description: * Verifies survival of message handlers and data across garbage collection. * [/Description] * ****************************************************************************/ function handler1( data ) // mark us data += "handler1" // destroy the data data = nil // perform GC GC.perform( true ) end function handler2( data ) // mark us data += "handler2" // destroy the data data = nil // perform GC GC.perform( true ) end // Subscribe the handlers subscribe( "msg", handler1 ) subscribe( "msg", handler2 ) //now fre the handler symbols so that their function can be reclaimed handler1 = nil handler2 = nil // do a first broadcast with the data and see if they're in data = [] broadcast( "msg", data ) if "handler1" notin data: failure( "First handler not in - 1" ) if "handler2" notin data: failure( "Second handler not in - 1" ) // again, but this time use volatile data broadcast( "msg", [] ) // if we were wrong we would crash here. success() /* end of file */ tests/core/testsuite/msg_prio.fal000066400000000000000000000021601176363201700175110ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 38k * Category: messages * Subcategory: * Short: Message priority * Description: * Check that unsubscriptions during broadcasts are working properly. * [/Description] * ****************************************************************************/ control = 0 result = "untouched" class subscriber( id, prio ) id = id init subscribe( "test", self.manager, prio ) end function manager( coop, block ) coop += self.id if block: consume() end end subs = [] subs += subscriber( 0 ) subs += subscriber( 1 ) subs += subscriber( 2, true ) // test for unfiltered message coop = .[] broadcast( "test", coop ) if coop.len() != 3: failure( "Cooperation on broadcast" ) if coop[0] != 2: failure( "High priority" ) if coop[1] != 0: failure( "Normal priority" ) if coop[2] != 1: failure( "Low priority" ) // test for blocked message coop = .[] broadcast( "test", coop, true ) if coop.len() != 1: failure( "Blocking msg" ) if coop[0] != 2: failure( "Blocker id" ) success() /* end of file */ tests/core/testsuite/msg_slot.fal000066400000000000000000000055321176363201700175270ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 38b * Category: messages * Subcategory: slots * Short: Basic slots * Description: * Same as 38a, but using the VMSlot instance interface instead. * [/Description] * ****************************************************************************/ function handler1( z, y ) global h1c // check for unordered parameters too h1c = [z, y] end function handler2( value1, value2 ) global h2c h2c = [value1, value2] end object hobj function on_msg( value1, value2 ) global hmth hmth = [value1, value2] end end // Test1: see if a call passes through all msg = getSlot( "msg", true ) msg.subscribe( handler1 ) msg.subscribe( handler2 ) msg.subscribe( hobj ) msg.broadcast( 'first', 'second' ) if h1c.len() != 2: failure( "Forming of handler1 - 1" ) if h2c.len() != 2: failure( "Forming of handler2 - 1" ) if hmth.len() != 2: failure( "Forming of handler3 - 1" ) if h1c[0] != 'first' or h1c[1] != 'second': failure( "Content of handler1 - 1" ) if h2c[0] != 'first' or h2c[1] != 'second': failure( "Content of handler2 - 1" ) if hmth[0] != 'first' or hmth[1] != 'second': failure( "Content of handler3 - 1" ) // Test1.1 -- verify the slot is not re-created if reget. msg = getSlot( "msg", true ) h1c = nil h2c = nil hmth = nil msg.broadcast( 'first', 'second' ) if h1c[0] != 'first' or h1c[1] != 'second': failure( "Content of handler1 - 2" ) if h2c[0] != 'first' or h2c[1] != 'second': failure( "Content of handler2 - 2" ) if hmth[0] != 'first' or hmth[1] != 'second': failure( "Content of handler3 - 2" ) // Test1.2 -- verify the slot is not re-created if using VMSlot. msg = VMSlot( "msg" ) h1c = nil h2c = nil hmth = nil msg.broadcast( 'first', 'second' ) if h1c[0] != 'first' or h1c[1] != 'second': failure( "Content of handler1 - 3" ) if h2c[0] != 'first' or h2c[1] != 'second': failure( "Content of handler2 - 3" ) if hmth[0] != 'first' or hmth[1] != 'second': failure( "Content of handler3 - 3" ) // Test2: check for unsubscription h1c = nil h2c = nil hmth = nil msg.unsubscribe( handler1 ) msg.unsubscribe( handler2 ) // allow the hobj to stay to see we did the call. msg.broadcast( 'first', 'second' ) if hmth.len() != 2: failure( "Element still subscribed not called after unsub" ) if h1c != nil: failure( "Unsubscribed handler1 called" ) if h2c != nil: failure( "Unsubscribed handler2 called" ) // Test3: complete unsubscription hmth = nil msg.unsubscribe( hobj ) msg.broadcast( 'first', 'second' ) if hmth != nil : failure( "Single handler called after unsubscription" ) // Test5: see if we raise a failure in case of unhandled message in objects msg = VMSlot( "another" ) msg.subscribe( hobj ) try msg.broadcast( 'first', 'second' ) failure( "Error not raised for missing handler" ) catch AccessError // it's ok end success() /* End of file */ tests/core/testsuite/msg_slot2.fal000066400000000000000000000030201176363201700175770ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 38c * Category: messages * Subcategory: * Short: Slots + messages * Description: * Checks if functions and VMSlot are completely interoperable. * [/Description] * ****************************************************************************/ function handler1( z, y ) global h1c // check for unordered parameters too h1c = [z, y] end function handler2( value1, value2 ) global h2c h2c = [value1, value2] end object hobj function on_msg( value1, value2 ) global hmth hmth = [value1, value2] end end // Test1: see if a call passes through all msg = VMSlot( "msg" ) msg.subscribe( handler1 ) msg.subscribe( handler2 ) msg.subscribe( hobj ) // Test1 -- Check broadcast on the message name broadcast( "msg", 'first', 'second' ) if h1c[0] != 'first' or h1c[1] != 'second': failure( "Content of handler1 - 3" ) if h2c[0] != 'first' or h2c[1] != 'second': failure( "Content of handler2 - 3" ) if hmth[0] != 'first' or hmth[1] != 'second': failure( "Content of handler3 - 3" ) // Test2: check for unsubscription h1c = nil h2c = nil hmth = nil msg.unsubscribe( handler1 ) unsubscribe( "msg", handler2 ) // allow the hobj to stay to see we did the call. broadcast( "msg", 'first', 'second' ) if hmth.len() != 2: failure( "Element still subscribed not called after unsub" ) if h1c != nil: failure( "Unsubscribed handler1 called" ) if h2c != nil: failure( "Unsubscribed handler2 called" ) success() /* End of file */ tests/core/testsuite/mth_array.fal000066400000000000000000000013741176363201700176660ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 27b * Category: methods * Subcategory: * Short: Method array * Description: * Check if methods created through arrays can still be seen as arrays. * [/Description] * ****************************************************************************/ // bom methods class Test a = [{x,y=> y = x}, "new value" ] end t = Test() if t.a.typeId() != MethodType: failure( "Not a method" ) val = nil t.a( $val ) if val != "new value": failure( "Method call" ) if t.a[1] != "new value": failure( "Method as array - read access") m= t.a m[1] = "changed" t.a( $val ) if val != "changed": failure( "Method as array - write access") success() /* End of file */ tests/core/testsuite/mth_array_add.fal000066400000000000000000000013531176363201700204730ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 27c * Category: methods * Subcategory: * Short: Method array add/sub * Description: * Check if methods created through arrays can still be seen as arrays. * [/Description] * ****************************************************************************/ // bom methods class Test a = [{x,y=> y = x}, "new value" ] end t = Test() if t.a.typeId() != MethodType: failure( "Not a method" ) val = nil t.a( $val ) if val != "new value": failure( "Method call" ) t.a += "Item" if t.a[2] != "Item": failure( "Method-array addition" ) m = t.a if (m - ["Item"]).len() != 2: failure( "Method-array removal" ) success() /* End of file */ tests/core/testsuite/mth_array_base.fal000066400000000000000000000013771176363201700206630ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 27d * Category: methods * Subcategory: * Short: Method array add/sub * Description: * Check if methods created through arrays can still be seen as arrays. * [/Description] * ****************************************************************************/ // bom methods class Test a = [{x,y=> y = x}, "new value" ] end t = Test() if t.a.base().typeId() != ArrayType: failure( "base method" ) // see if we can also access the item iteself via the base method t.a += "Item" if t.a[2] != "Item": failure( "Method-array addition" ) t.a.base() -= "Item" if t.a.base().len() != 2: failure( "Method-array base extraction removal" ) success() /* End of file */ tests/core/testsuite/mth_array_nm.fal000066400000000000000000000013171176363201700203550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 27e * Category: methods * Subcategory: * Short: Non-method arrays * Description: * Check if methods created through arrays can still be seen as arrays. * [/Description] * ****************************************************************************/ // bom methods class Test a = arrayNM( [{x,y=> y = x}, "new value" ] ) b = [printl, print].NM() end t = Test() if t.a.typeId() != ArrayType: failure( "arrayNM" ) if t.b.typeId() != ArrayType: failure( "self.NM" ) if t.a[1] != "new value": failure( "arrayNM - content" ) if t.b[1] != print: failure( "self.NM - content" ) success() /* End of file */ tests/core/testsuite/mth_source.fal000066400000000000000000000014101176363201700200370ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 27a * Category: methods * Subcategory: * Short: Method ops * Description: * Method persistency and operations. * [/Description] * ****************************************************************************/ // bom methods x = 1.toString if x.typeId() != MethodType: failure( "BOM Method type" ) if x() != "1": failure( "BOM method base" ) if x.source() != 1: failure( "BOM method source" ) // normal methods object AnObj function some() return "Some" end end x = AnObj.some if x.typeId() != MethodType: failure( "Method type" ) if x() != "Some": failure( "Method base" ) if x.source() != AnObj: failure( "Method source" ) success() /* End of file */ tests/core/testsuite/nameless_complex.fal000066400000000000000000000016641176363201700212400ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 18e * Category: function * Subcategory: nameless * Short: Complex nameless function * Description: * Testing lamdas with static blocks * [/Description] * ****************************************************************************/ function ret_function() var = function( param ) static values = [1,2,3,4] val_id = 0 end // prologue: use state only if != 0 if param == 0 return values[ val_id ] elif param < 0 return -1 end next = values[val_id++] if val_id == len( values ): val_id = 0 return next end return var end var = ret_function() // check prologue if var(-1) != -1: failure( "Prologue execution" ) // enter start var(0) var(1) var(1) var(1) var(1) if var(0) != 1: failure( "Static advancement" ) success() /* End of file */ tests/core/testsuite/nameless_func.fal000066400000000000000000000007121176363201700205150ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 18c * Category: function * Subcategory: nameless * Short: function functions * Description: * Basic test for function functions * [/Description] * ****************************************************************************/ var = function( a, b ) return a * b end if var( 2, 3) != 6: failure( "function assingment" ) success() /* End of file */ tests/core/testsuite/nameless_nested.fal000066400000000000000000000017521176363201700210510ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 18d * Category: function * Subcategory: nameless * Short: Nested nameless functions * Description: * Nested function functions and functions returning functions * [/Description] * ****************************************************************************/ function ret_function( sel ) if sel var = function( param ) var = function( a, b ) return a * b end return param * var( 2, 3 ) end else var = function( param ) var = function( a, b ) return a + b end return param + var( 4, 5 ) end end return var end var = ret_function( 0 ) if var( 10 ) != 10 + 4 + 5: failure( "Point A" ) // tests also for callable function return direct call // and compilation issues. if ret_function(1)( 5 ) != 5 * 2 * 3: failure( "Point B" ) success() /* End of file */ tests/core/testsuite/nestlambda.fal000066400000000000000000000014101176363201700200010ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 18b * Category: lambda * Subcategory: * Short: Nested expressions * Description: * Nested lambda functions and functions returning expressions * [/Description] * ****************************************************************************/ function ret_lambda( sel ) if sel var = { param=> param * { a, b => a * b}( 2, 3 )} else var = { param=> param + { a, b => a + b}( 4, 5 )} end return var end var = ret_lambda( 0 ) if var( 10 ) != 10 + 4 + 5: failure( "Point A" ) // tests also for callable function return direct call // and compilation issues. if ret_lambda(1)( 5 ) != 5 * 2 * 3: failure( "Point B" ) success() /* End of file */ tests/core/testsuite/obj_comp.fal000066400000000000000000000016641176363201700174720ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 121c * Category: comp * Subcategory: * Short: Custom comprehension * Description: * Test on custom comprehension. * * [/Description] * ****************************************************************************/ class MetaArray array = [] function append( data ) self.array.add( data ) end end ma = MetaArray() ma.comp( [0:3], {x=> x*2} ) if ma.array.len() != 3: failure( "1 - Size" ) if ma.array[0] != 0: failure( "1 - Zero" ) if ma.array[1] != 2: failure( "1 - One" ) if ma.array[2] != 4: failure( "1 - Two" ) // testing also OOB working ma = MetaArray() ma.comp( [0:10], {x=> x%2 == 0 ? x*2: oob( x < 5 ? 1:0 ) } ) if ma.array.len() != 3: failure( "2 - Size" ) if ma.array[0] != 0: failure( "2 - Zero" ) if ma.array[1] != 4: failure( "2 - One" ) if ma.array[2] != 8: failure( "2 - Two" ) success() /* end of file */ tests/core/testsuite/obj_init_params.fal000066400000000000000000000124101176363201700210310ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 22f * Category: types * Subcategory: objects * Short: Initialization parameters * Description: * Checks for correct assignment of initialization variables * when number of parameters is different from what expected. * [/Description] * ****************************************************************************/ class Base( gamma, alpha, beta ) gamma = gamma alpha = alpha beta = beta init if self.beta == nil self.beta = "nilled" end end end class Derived( beta, alpha, sigma ) from Base( "Derived", beta, alpha ) sigma = sigma end class Derived2( beta, sigma, tau ) from Derived( "Derived2", beta ) tau = tau sigma = sigma end //============================================ // Test for base class //============================================ oBase = Base( "one" ) if oBase.gamma != "one": failure( "Base - given property" ) if oBase.alpha != nil: failure( "Base - missing property" ) if oBase.beta != "nilled": failure( "Base - nilled property" ) oBase_two = Base( "two", "three" ) if oBase_two.gamma != "two": failure( "Base, two params - first property" ) if oBase_two.alpha != "three": failure( "Base, two params - second property" ) if oBase_two.beta != "nilled": failure( "Base, two params - nilled property" ) oBase_three = Base( "four", "five", "six" ) if oBase_three.gamma != "four": failure( "Base, three params - first property" ) if oBase_three.alpha != "five": failure( "Base, three params - second property" ) if oBase_three.beta != "six": failure( "Base, three params - third property" ) //============================================ // Test for derived class //============================================ oDerived = Derived() if oDerived.gamma != "Derived": failure( "Derived - zero params - inherithed property" ) if oDerived.alpha != nil: failure( "Derived - zero params - missing property" ) if oDerived.beta != "nilled": failure( "Derived - zero params - nilled property" ) if oDerived.sigma != nil: failure( "Derived - zero params - extra private property" ) oDerived_one = Derived( "two" ) if oDerived_one.gamma != "Derived": failure( "Derived - one param - inherithed property" ) if oDerived_one.alpha != "two": failure( "Derived - one param - given property" ) if oDerived_one.beta != "nilled": failure( "Derived - one param - nilled property" ) if oDerived_one.sigma != nil: failure( "Derived - one param - nilled property" ) oDerived_two = Derived( "three", "four" ) if oDerived_two.gamma != "Derived": failure( "Derived, two params - inherithed property" ) if oDerived_two.alpha != "three": failure( "Derived, two params - given property" ) if oDerived_two.beta != "four": failure( "Derived, two params - third property" ) if oDerived.sigma != nil: failure( "Derived, two params - class property" ) oDerived_three = Derived( "three", "four", "five" ) if oDerived_three.gamma != "Derived": failure( "Derived, three params - inherithed property" ) if oDerived_three.alpha != "three": failure( "Derived, three params - given property" ) if oDerived_three.beta != "four": failure( "Derived , three params - third property" ) if oDerived_three.sigma != "five": failure( "Derived , three params - class property" ) //============================================ // Test for derived 2 class //============================================ oDerived2 = Derived2() if oDerived2.gamma != "Derived": failure( "Derived2 - zero params - base inherited property" ) if oDerived2.alpha != "Derived2": failure( "Derived2 - zero params - inherithed property" ) if oDerived2.beta != "nilled": failure( "Derived2 - zero params - nilled property" ) if oDerived2.sigma != nil: failure( "Derived2 - zero params - Derived property" ) if oDerived2.tau != nil: failure( "Derived2 - zero params - class property" ) oDerived2_one = Derived2( "one" ) if oDerived2_one.gamma != "Derived": failure( "Derived2 - one params - base inherited property" ) if oDerived2_one.alpha != "Derived2": failure( "Derived2 - one params - inherithed property" ) if oDerived2_one.beta != "one": failure( "Derived2 - one params - nilled property" ) if oDerived2_one.sigma != nil: failure( "Derived2 - one params - Derived property" ) if oDerived2_one.tau != nil: failure( "Derived2 - one params - class property" ) oDerived2_two = Derived2( "one", "two" ) if oDerived2_two.gamma != "Derived": failure( "Derived2 - two params - base inherited property" ) if oDerived2_two.alpha != "Derived2": failure( "Derived2 - two params - inherithed property" ) if oDerived2_two.beta != "one": failure( "Derived2 - two params - nilled property" ) if oDerived2_two.sigma != "two": failure( "Derived2 - two params - Derived property" ) if oDerived2_two.tau != nil: failure( "Derived2 - two params - class property" ) oDerived2_three = Derived2( "one", "two", "three" ) if oDerived2_three.gamma != "Derived": failure( "Derived2 - three params - base inherited property" ) if oDerived2_three.alpha != "Derived2": failure( "Derived2 - three params - inherithed property" ) if oDerived2_three.beta != "one": failure( "Derived2 - three params - nilled property" ) if oDerived2_three.sigma != "two": failure( "Derived2 - three params - Derived property" ) if oDerived2_three.tau != "three": failure( "Derived2 - three params - class property" ) success() /* End of file */ tests/core/testsuite/obj_nest.fal000066400000000000000000000014261176363201700175010ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 22g * Category: types * Subcategory: objects * Short: Expressions in from * Description: * Test for corerect generation and usage of expressions and * forward references during from clause calls. * [/Description] * ****************************************************************************/ load obj_nest_base object fseq1 from Base( .[ 1 fseq2 3 ] ) end object fseq2 function test() return 100 end end try call = fseq1.param[1] catch AccessError failure( "Expression generation during init" ) end try if call.test() != 100 failure( "Call value" ) end catch AccessError failure( "Expression deep generation" ) end success() /* end of test */ tests/core/testsuite/obj_nest_base.fal000066400000000000000000000005571176363201700204770ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: -- * Category: types * Subcategory: objects * Short: Expressions in from * Description: * Component for test 22g * [/Description] * ****************************************************************************/ class Base( param ) param = param end export tests/core/testsuite/objclone.fal000066400000000000000000000014111176363201700174630ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 22h * Category: types * Subcategory: objects * Short: Cloning objects * Description: * Checks for cloning objects and value persistency. * [/Description] * ****************************************************************************/ object Test a = 100 b = 200 c = 300 end t1 = Test.clone() if t1.a != 100 or t1.b != 200 or t1.c != 300 failure( "Cloning unchanged" ) end Test.a = "one" t2 = Test.clone( Test ) if t2.a != "one" or t2.b != 200 or t2.c != 300 failure( "Cloning changed" ) end if t1.a != 100 failure( "Cloning persistency" ) end Test.a = "two" if t2.a != "one" failure( "Cloning insolation" ) end success() /* End of file */ tests/core/testsuite/object.fal000066400000000000000000000013371176363201700171450ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 22a * Category: types * Subcategory: objects * Short: Basic object * Description: * Object basic features * [/Description] * ****************************************************************************/ function func() return [1,2] end object zero prop_one = nil prop_two = func() init self.prop_one = "hello" end function method() return self.prop_one + " world" end end if zero.prop_one != "hello": failure( "Initializer" ) if len( zero.prop_two ) != 2: failure( "Automatic complex initializer" ) if zero.method() != "hello world": failure( "Method call" ) success() /* End of file */ tests/core/testsuite/object_in.fal000066400000000000000000000014321176363201700176270ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 22e * Category: types * Subcategory: objects * Short: Operator "in" for objects * Description: * The in operator, for objects, works as the provides operator, but it * check for strings instead of symbols. * [/Description] * ****************************************************************************/ object zero prop_one = 0 prop_two = "something" function method() end end if "prop_one" notin zero: failure( "Property check via in -- 1" ) if "prop_two" notin zero: failure( "Property check via in -- 2" ) if "method" notin zero: failure( "Property check via in -- 3" ) if "not here" in zero: failure( "Property check, negative" ) success() /* End of file */ tests/core/testsuite/objinherit.fal000066400000000000000000000023231176363201700200300ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 22b * Category: types * Subcategory: objects * Short: Multiple inheritance object * Description: * Object that inherits from multiple classes. * * [/Description] * ****************************************************************************/ class one pone = nil init self.pone = [1,2,3] end function method_one() return 0 end end class two ptwo = ["a"=>1, "b"=>2, "C"=>3 ] function method_two() return 0 end end object zero from one, two function method_one() return "redefined" end function method_three() if self.method_two() != 0: failure("Internal method call") if self.one.method_one() != 0: failure( "Self access class base" ) return 0 end end if zero.method_three() != 0: failure( "Declared method" ) if len( zero.pone ) != 3: failure( "Inheritance/init block" ) if zero.ptwo["a"] != 1: failure( "Inheritance/ auto block" ) if zero.method_one() != "redefined": failure("Overolading" ) if zero.method_two() != 0: failure( "Direct inherited call" ) if zero.one.method_one() != 0: failure( "Base access" ) success() /* End of file */ tests/core/testsuite/oob.fal000066400000000000000000000026751176363201700164640ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 37a * Category: rtl * Subcategory: core * Short: Basic OOB * Description: * Check for item OOB set, reset and status. * TODO: Move this as 4a when OOB operators are done. * [/Description] ****************************************************************************/ item = oob( 100 ) if item != 100: failure( "Oob invariancy 1" ) if not isoob( item ): failure( "Oob status" ) item = deoob( item ) if item != 100: failure( "Oob invariancy 2" ) if isoob( item ): failure( "Oob reset" ) arr = [1, 2, oob(3), 4] if arr[2] != 3: failure( "Oob deep invariancy 1" ) if not isoob( arr[2] ): failure( "Oob deep status" ) arr[2] = deoob( arr[2] ) if arr[2] != 3: failure( "Oob deep invariancy 2" ) if isoob( arr[2] ): failure( "Oob deep reset" ) arr[2] = oob(3) item = arr[2] last = item if last != 3: failure( "Oob transitive invariancy 1" ) if not isoob( last ): failure( "Oob transitive status" ) item = deoob( arr[2] ) if last != 3: failure( "Oob self invariancy 2" ) if not isoob( last ): failure( "Oob self status invariancy" ) if not isoob( arr[2] ): failure( "Oob maitanin" ) if isoob( item ): failure( "Oob remove" ) if item != arr[2]: failure( "Equality of different oob stated data" ) if not isoob( oob( "String" ) ): failure( "Oob immediate string" ) l = oob( "String" ) if not isoob(l): failure( "Oob copied string" ) success() /* End of file */ tests/core/testsuite/oob_call.fal000066400000000000000000000016651176363201700174550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 37b * Category: rtl * Subcategory: core * Short: Advanced OOB * Description: * Check for item OOB across function calls. * TODO: Move this as 4c when OOB operators are done. * [/Description] ****************************************************************************/ function deep( value ) if value == 0 return "A non oob value" elif value == 1 return oob("A oob value") end return deep( value - 2 ) end if isoob( deep( 0 ) ): failure( "false positive immediate" ) if not isoob( deep( 1 ) ): failure( "false negative immediate" ) if isoob( deep( 2 ) ): failure( "false positive, 1 step" ) if not isoob( deep( 3 ) ): failure( "false negative, 1 step" ) if isoob( deep( 10 ) ): failure( "false positive, 5 steps" ) if not isoob( deep( 11 ) ): failure( "false negative, 5 steps" ) success() /* End of file */ tests/core/testsuite/oob_call2.fal000066400000000000000000000020651176363201700175320ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 37c * Category: rtl * Subcategory: core * Short: Parametric OOB * Description: * Check for item OOB across function calls, * when passed as parameter. * TODO: Move this as 4c when OOB operators are done. * [/Description] ****************************************************************************/ function deep( value, oobData ) if value == 0 return oobData end return deep( value - 1, isoob( oobData ) ? deoob( oobData ) : oob( oobData ) ) end if isoob( deep( 0, "some data" ) ): failure( "false positive immediate" ) if not isoob( deep( 1, "some data" ) ): failure( "false negative immediate" ) if isoob( deep( 2, "some data" ) ): failure( "false positive, 1 step" ) if not isoob( deep( 3, "some data" ) ): failure( "false negative, 1 step" ) if isoob( deep( 10, "some data" ) ): failure( "false positive, 5 steps" ) if not isoob( deep( 11, "some data" ) ): failure( "false negative, 5 steps" ) success() /* End of file */ tests/core/testsuite/opeval.fal000066400000000000000000000025611176363201700171650ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 74a * Category: functional * Subcategory: operator * Short: Eval operator * Description: * Tests the "!" for eval operations. * [/Description] * ****************************************************************************/ // values must ve returned as is. test = 100 if ^* test != 100: failure( "Value 1" ) test = "Hello" if ^* test != "Hello": failure( "Value 2" ) // same for array. No copy involved test = [1,2,3] n = ^* test if n.len() != 3: failure( "Non callable Arrays" ) test[1] = 'a' if n[1] != 2: failure( "Array non-persistency" ) object test1 prop = nil end t = ^*test1 if not (t provides prop): failure( "Deep objects" ) test1.prop = "something" if t.prop != "something": failure( "Deep objects persistency" ) //========================================== // Test for functions. // function retSomething( param ) if param: return param return "Empty" end if ^* retSomething != "Empty": failure( "Direct call" ) if ^* .[retSomething "Other Param" ] != "Other Param" failure( "Minimal eval" ) end x = .[retSomething .[retSomething .[ retSomething "param" ]]] if ^* x != "param" failure( "Full eval" ) end // a failed functional evaluation may mess up the return stack. ^*x ^*x if ^*x != "param" failure( "Repeated full eval" ) end success() tests/core/testsuite/override.fal000066400000000000000000000057461176363201700175260ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 28a * Category: classes * Subcategory: override * Short: Basic override * Description: * Tests basic operator overrides on classes. * [/Description] * ****************************************************************************/ class Over numval = 0 array = [] function __add( addend ) if addend provides numval self.numval += addend.numval elif addend.typeId() == NumericType self.numval += addend else raise Error( 10001, "Wrong operands for add__", addend ) end return self end function __sub( addend ) if addend provides numval self.numval -= addend.numval elif addend.typeId() == NumericType self.numval -= addend else raise Error( 10001, "Wrong operands for sub__", addend ) end return self end function __mul( addend ) if addend provides numval self.numval *= addend.numval elif addend.typeId() == NumericType self.numval *= addend else raise Error( 10001, "Wrong operands for mul__", addend ) end return self end function __div( addend ) if addend provides numval self.numval /= addend.numval elif addend.typeId() == NumericType self.numval /= addend else raise Error( 10001, "Wrong operands for div__", addend ) end return self end function __mod( addend ) if addend provides numval self.numval %= addend.numval elif addend.typeId() == NumericType self.numval %= addend else raise Error( 10001, "Wrong operands for mod__", addend ) end return self end function __pow( addend ) if addend provides numval self.numval **= addend.numval elif addend.typeId() == NumericType self.numval **= addend else raise Error( 10001, "Wrong operands for mod__", addend ) end return self end function __neg() return -self.numval end function __inc() return ++self.numval end function __dec() return --self.numval end function __incpost() return self.numval ++ end function __decpost() return self.numval-- end end o = Over() o += 10 if o.numval != 10: failure( "Plus override" ) o -= 1 if o.numval != 9: failure( "Minus override" ) o *= 2 if o.numval != 18: failure( "Times override" ) o /= 2 if o.numval != 9: failure( "Div override" ) o %= 4 if o.numval != 1: failure( "Mod override" ) o.numval = 2 o = o ** 3 if o.numval != 8: failure( "Pow override" ) if -o != -8: failure( "Neg override" ) if ++o != 9: failure( "Inc override" ) if --o != 8: failure( "Dec override" ) if o++ != 8: failure( "Inc override post - 1" ) if o.numval != 9: failure( "Inc override post - 2" ) if o-- != 9: failure( "Dec override post - 1" ) if o.numval != 8: failure( "Dec override post - 2" ) success() tests/core/testsuite/override_call.fal000066400000000000000000000011041176363201700205010ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 28c * Category: classes * Subcategory: override * Short: Call override * Description: * Tests call operator overrides on classes. * [/Description] * ****************************************************************************/ class Over() inner = nil function __call( data ) self.inner = data return data.len() end end x = Over() if x( "Hello" ) != 5: failure( "Override return" ) if x.inner != "Hello": failure( "Override setting" ) success() tests/core/testsuite/override_cmp.fal000066400000000000000000000047761176363201700203670ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 28b * Category: classes * Subcategory: override * Short: Compare override * Description: * Tests comparation overrides on classes. * [/Description] * ****************************************************************************/ globalCompare = compareByNum class Over( name ) name = name numval = 0 array = [] function compare( addend ) return globalCompare( self, addend ) end end function compareByNum( o1, o2 ) if o2 provides numval return o1.numval - o2.numval elif o2.typeId() == NumericType return o1.numval - o2 end return nil // let the VM to order us. end function compareByName( o1, o2 ) if o2 provides name return o1.name.compare( o2.name ) elif o2.typeId() == StringType return o1.name.compare( o2 ) end return nil // let the VM to order us. end globalCompare = compareByNum // compare on numbers o = Over( "zero" ) if o > 1: failure( "o > 1" ) if not o < 1: failure( "o < 1" ) if o >= 1: failure( "o >= 1" ) if not o <= 1: failure( "o <= 1" ) if o == 1: failure( "o == 1" ) if not o != 1: failure( "o != 1" ) if o > 0: failure( "o > 0" ) if o < 0: failure( "o < 0" ) if not o >= 0: failure( "o >= 0" ) if not o <= 0: failure( "o <= 0" ) if not o == 0: failure( "o == 0" ) if o != 0: failure( "o != 0" ) o1 = Over( "one" ) o1.numval = 1 if o > o1: failure( "o > o1" ) if not o < o1: failure( "o < o1" ) if o >= o1: failure( "o >= o1" ) if not o <= o1: failure( "o <= o1" ) if o == o1: failure( "o == o1" ) if not o != o1: failure( "o != o1" ) // Check for VM ordering in dicts // names and creation mixed to force randomic order in VM o_five = Over( "five" ) o_five.numval = 5 o_two = Over( "two" ) o_two.numval = 2 o_four = Over( "four" ) o_four.numval = 4 o_three = Over( "three" ) o_three.numval = 3 dict = [ o1 => 1, o => 0, o_five => 5, o_four => 4, o_three => 3, o_two => 2 ] res = [] for k,v in dict res += k end if res[0] != o or \ res[1] != o1 or \ res[2] != o_two or \ res[3] != o_three or \ res[4] != o_four or \ res[5] != o_five failure( "Numeric ordering" ) end // Now by name globalCompare = compareByName dict = [ o1 => 1, o => 0, o_five => 5, o_four => 4, o_three => 3, o_two => 2 ] res = [] for k,v in dict res += k end if res[0] != o_five or \ res[1] != o_four or \ res[2] != o1 or \ res[3] != o_three or \ res[4] != o_two or \ res[5] != o failure( "Name ordering" ) end success() tests/core/testsuite/override_poop.fal000066400000000000000000000062241176363201700205530ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 28e * Category: poop * Subcategory: override * Short: Basic override * Description: * Tests basic operator overrides on classes. * [/Description] * ****************************************************************************/ GC.enable( false ) o = bless([ "numval" => 0, "array" => [], "__add" => function( addend ) if addend provides numval self.numval += addend.numval elif addend.typeId() == NumericType self.numval += addend else raise Error( 10001, "Wrong operands for add__", addend ) end return self end, "__sub" => function( addend ) if addend provides numval self.numval -= addend.numval elif addend.typeId() == NumericType self.numval -= addend else raise Error( 10001, "Wrong operands for sub__", addend ) end return self end, "__mul" => function( addend ) if addend provides numval self.numval *= addend.numval elif addend.typeId() == NumericType self.numval *= addend else raise Error( 10001, "Wrong operands for mul__", addend ) end return self end, "__div" => function( addend ) if addend provides numval self.numval /= addend.numval elif addend.typeId() == NumericType self.numval /= addend else raise Error( 10001, "Wrong operands for div__", addend ) end return self end, "__mod" => function( addend ) if addend provides numval self.numval %= addend.numval elif addend.typeId() == NumericType self.numval %= addend else raise Error( 10001, "Wrong operands for mod__", addend ) end return self end, "__pow" => function( addend ) if addend provides numval self.numval **= addend.numval elif addend.typeId() == NumericType self.numval **= addend else raise Error( 10001, "Wrong operands for mod__", addend ) end return self end, "__neg" => function() return -self.numval end, "__inc" => function() return ++self.numval end, "__dec" => function() return --self.numval end, "__incpost" => function() return self.numval++ end, "__decpost" => function() return self.numval-- end ]) o += 10 if o.numval != 10: failure( "Plus override" ) o -= 1 if o.numval != 9: failure( "Minus override" ) o *= 2 if o.numval != 18: failure( "Times override" ) o /= 2 if o.numval != 9: failure( "Div override" ) o %= 4 if o.numval != 1: failure( "Mod override" ) o.numval = 2 o = o ** 3 if o.numval != 8: failure( "Pow override" ) if -o != -8: failure( "Neg override" ) // prefix op will have the VM return the item itself // no matter what we return from the method if ++o != 9: failure( "Inc override" ) if --o != 8: failure( "Dec override" ) if o++ != 8: failure( "Inc override post - 1" ) if o.numval != 9: failure( "Inc override post - 2" ) if o-- != 9: failure( "Dec override post - 1" ) if o.numval != 8: failure( "Dec override post - 2" ) success() tests/core/testsuite/override_poop_call.fal000066400000000000000000000011051176363201700215370ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 28f * Category: poop * Subcategory: override * Short: Poop call override * Description: * Tests call operator overrides on classes. * [/Description] * ****************************************************************************/ x = bless([ "inner" => nil, "__call" => function( data ); self.inner = data; return data.len(); end ]) if x( "Hello" ) != 5: failure( "Override return" ) if x.inner != "Hello": failure( "Override setting" ) success() tests/core/testsuite/override_poop_cmp.fal000066400000000000000000000051141176363201700214070ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 28g * Category: poop * Subcategory: override * Short: Poop compare override * Description: * Tests comparation overrides on classes. * [/Description] * ****************************************************************************/ globalCompare = compareByNum o = bless([ "name" => nil, "numval" => 0, "array" => [], "compare" => { addend => return globalCompare( self, addend ); } ]) function compareByNum( o1, o2 ) if o2 provides numval return o1.numval - o2.numval elif o2.typeId() == NumericType return o1.numval - o2 end return nil // let the VM to order us. end function compareByName( o1, o2 ) if o2 provides name return o1.name.compare( o2.name ) elif o2.typeId() == StringType return o1.name.compare( o2 ) end return nil // let the VM to order us. end globalCompare = compareByNum // compare on numbers o.name = "zero" if o > 1: failure( "o > 1" ) if not o < 1: failure( "o < 1" ) if o >= 1: failure( "o >= 1" ) if not o <= 1: failure( "o <= 1" ) if o == 1: failure( "o == 1" ) if not o != 1: failure( "o != 1" ) if o > 0: failure( "o > 0" ) if o < 0: failure( "o < 0" ) if not o >= 0: failure( "o >= 0" ) if not o <= 0: failure( "o <= 0" ) if not o == 0: failure( "o == 0" ) if o != 0: failure( "o != 0" ) o1 = o.clone() o1.name = "one" o1.numval = 1 if o > o1: failure( "o > o1" ) if not o < o1: failure( "o < o1" ) if o >= o1: failure( "o >= o1" ) if not o <= o1: failure( "o <= o1" ) if o == o1: failure( "o == o1" ) if not o != o1: failure( "o != o1" ) // Check for VM ordering in dicts // names and creation mixed to force randomic order in VM o_five = o.clone() o_five.name = "five" o_five.numval = 5 o_two = o.clone() o_two.name = "two" o_two.numval = 2 o_four = o.clone() o_four.name = "four" o_four.numval = 4 o_three = o.clone() o_three.name = "three" o_three.numval = 3 dict = [ o1 => 1, o => 0, o_five => 5, o_four => 4, o_three => 3, o_two => 2 ] res = [] for k,v in dict res += k end if res[0] != o or \ res[1] != o1 or \ res[2] != o_two or \ res[3] != o_three or \ res[4] != o_four or \ res[5] != o_five failure( "Numeric ordering" ) end // Now by name globalCompare = compareByName dict = [ o1 => 1, o => 0, o_five => 5, o_four => 4, o_three => 3, o_two => 2 ] res = [] for k,v in dict res += k end if res[0] != o_five or \ res[1] != o_four or \ res[2] != o1 or \ res[3] != o_three or \ res[4] != o_two or \ res[5] != o failure( "Name ordering" ) end success() tests/core/testsuite/override_poop_square.fal000066400000000000000000000020041176363201700221230ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 28h * Category: poop * Subcategory: override * Short: POOP square accessor override * Description: * Checks for overriding the [N] accessor * [/Description] * ****************************************************************************/ x = bless([ "inner" => [nil, nil, nil], "__setIndex" => function( pos, data ); if typeOf( pos ) == StringType; self.set( pos, data ); else; (self.inner)[pos] = data; end; return data; end, "__getIndex" => function( pos ); if typeOf( pos ) == StringType; return self.get( pos ); else; return self.inner[pos]; end; end ]) if (x[0] = 10) != 10: failure( "Override return" ) if x[0] != 10: failure( "Override get" ) if x[1] != nil: failure( "Override untouched" ) // test if we can add strings x["hello"] = "world" if x["hello"] != "world": failure( "set/get in [] accessor") success() tests/core/testsuite/override_square.fal000066400000000000000000000013101176363201700210650ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 28d * Category: classes * Subcategory: override * Short: Square accessor override * Description: * Checks for overriding the [N] accessor * [/Description] * ****************************************************************************/ class Over() inner = [nil, nil, nil] function __setIndex( pos, data ) self.inner[pos] = data return data end function __getIndex( pos ) return self.inner[pos] end end x = Over() if (x[0] = 10) != 10: failure( "Override return" ) if x[0] != 10: failure( "Override get" ) if x[1] != nil: failure( "Override untouched" ) success() tests/core/testsuite/pagedict.fal000066400000000000000000000023511176363201700174540ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 12c * Category: types * Subcategory: dictionary * Short: Basic paged dictionary * Description: * Checks the basic paged dictionary functionalities with integers * [/Description] * ****************************************************************************/ // assignment test - include a compilation test dict = PageDict() dict[ 2 ] = "two" dict[ 0 ] = "zero" dict[ 1 ] = "one" // minimal access if dict[0] != "zero": failure( "Minimal access" ) // Changing items dict[1] = "first" if dict[1] != "first": failure( "Assignment" ) // item addition dict = dict + [ 3 => "three" ] if dict[3] != "three": failure( "Addition" ) // item change and addition dict += [ 3 => "third", 4 => "four" ] if dict[3] != "third" or dict[4] != "four": failure( "Addition/modify" ) //in/notin if not (1 in dict): failure( "In operator - positive" ) if "not present" in dict: failure( "In operator - negative" ) if 1 notin dict: failure( "Notin operator - negative" ) if not "not present" notin dict: failure( "Notin operator - positive" ) if len( dict ) != 5: failure( "Len core function" ) if dict.len() != 5: failure( "Len FBOM" ) success() /* End of file */ tests/core/testsuite/passbyref.fal000066400000000000000000000020531176363201700176710ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 13l * Category: functions * Subcategory: * Short: Variable parameter call * Description: * Checks calling by reference, both in fixed and variable convention. * [/Description] * ****************************************************************************/ function test_fixed( param1, param2 ) param1 = "changed" param2 = "changed" end function test_variable() for i in [0: paramCount()] if paramIsRef( i ) paramSet( i, "changed" ) end end end var1 = "original" var2 = "original" test_fixed( var1, $var2 ) if var1 != "original": failure( "Fixed parameter pollution -- fixed convention" ) if var2 != "changed": failure( "Variable parameter unchanged -- fixed convention" ) var2 = "original" test_variable( var1, $var2 ) if var1 != "original": failure( "Fixed parameter pollution -- variable convention" ) if var2 != "changed": failure( "Variable parameter unchanged -- variable convention" ) success() /* End of file */ tests/core/testsuite/passvp.fal000066400000000000000000000057251176363201700172200ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 13p * Category: functions * Subcategory: varpar * Short: Checks for the passvp function. * Description: * The passvp function retreives the parameters exceeding the official ones * or eventually calls a function receiving those parameters. * [/Description] * ****************************************************************************/ function testpars() ret = "" for i in [0:paramCount()] ret += parameter(i) formiddle: ret+="." end return ret end function first_a() a = 0 // create a non-param symbol return passvp() end function first_p(): return passvp( testpars ) function second_a( one ): return passvp() function second_p( one ) a = 0 // create a non-param symbol return passvp( testpars ) end function third_a( one, two ) n = 0 return passvp() end function third_p( one, two ): return passvp( testpars ) function extra() n = 0 return passvp( .[testpars "begin" ] ) end // no formal parameters if first_a() != []: failure( "first_a()" ) if first_p() != "": failure( "first_p()" ) if first_a( 'one' ) != ["one"]: failure( "first_a('one')" ) if first_p( 'one' ) != "one": failure( "first_p('one')" ) if first_a( 'one', 'two' ) != [ 'one', 'two' ]: failure( "first_a( 'one', 'two' )" ) if first_p( 'one', 'two' ) != "one.two": failure( "first_p( 'one', 'two' )" ) if first_a( 'one', 'two', 'three' ) != ['one', 'two', 'three']: failure( "first_a('one', 'two', 'three')" ) if first_p( 'one', 'two', 'three' ) != "one.two.three": failure( "first_p( 'one', 'two', 'three' )" ) // one formal parameters if second_a() != []: failure( "second_a()" ) if second_p() != "": failure( "second_p()" ) if second_a( 'one' ) != []: failure( "second_a('one')" ) if second_p( 'one' ) != "": failure( "second_p('one')" ) if second_a( 'one', 'two' ) != [ 'two' ]: failure( "second_a( 'one', 'two' )" ) if second_p( 'one', 'two' ) != "two": failure( "second_p( 'one', 'two' )" ) if second_a( 'one', 'two', 'three' ) != ['two', 'three']: failure( "second_a('one', 'two', 'three')" ) if second_p( 'one', 'two', 'three' ) != "two.three": failure( "second_p( 'one', 'two', 'three' )" ) // two formal parameters if third_a() != []: failure( "third_a()" ) if third_p() != "": failure( "third_p()" ) if third_a( 'one' ) != []: failure( "third_a('one')" ) if third_p( 'one' ) != "": failure( "third_p('one')" ) if third_a( 'one', 'two' ) != []: failure( "third_a( 'one', 'two' )" ) if third_p( 'one', 'two' ) != "": failure( "third_p( 'one', 'two' )" ) if third_a( 'one', 'two', 'three' ) != ['three']: failure( "third_a('one', 'two', 'three')" ) if third_p( 'one', 'two', 'three' ) != "three": failure( "third_p( 'one', 'two', 'three' )" ) // other kind of callable if extra( 'one', 'two', 'three' ) != "begin.one.two.three": failure( "extra" ) // error test try {=> passvp( "hello" )}() failure( "Passvp didn't check the parameter" ) catch ParamError in e end success() /* End of file */ tests/core/testsuite/passvp_meth.fal000066400000000000000000000056601176363201700202330ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 13q * Category: functions * Subcategory: varpar * Short: Checks for the passvp function - on methods. * Description: * The passvp function retreives the parameters exceeding the official ones * or eventually calls a function receiving those parameters. * This test checks if everything is ok also on methods. * [/Description] * ****************************************************************************/ function testpars() ret = "" for i in [0:paramCount()] ret += parameter(i) formiddle: ret+="." end return ret end object tobj function first_a() a = 0 // create a non-param symbol return passvp() end function first_p() a = 0 // create a non-param symbol b = 0 return passvp( testpars ) end function second_a( one ): return passvp() function second_p( one ): return passvp( testpars ) function third_a( one, two ): return passvp() function third_p( one, two ): return passvp( testpars ) end // no formal parameters if tobj.first_a() != []: failure( "first_a()" ) if tobj.first_p() != "": failure( "first_p()" ) if tobj.first_a( 'one' ) != ["one"]: failure( "first_a('one')" ) if tobj.first_p( 'one' ) != "one": failure( "first_p('one')" ) if tobj.first_a( 'one', 'two' ) != [ 'one', 'two' ]: failure( "first_a( 'one', 'two' )" ) if tobj.first_p( 'one', 'two' ) != "one.two": failure( "first_p( 'one', 'two' )" ) if tobj.first_a( 'one', 'two', 'three' ) != ['one', 'two', 'three']: failure( "first_a('one', 'two', 'three')" ) if tobj.first_p( 'one', 'two', 'three' ) != "one.two.three": failure( "first_p( 'one', 'two', 'three' )" ) // one formal parameters if tobj.second_a() != []: failure( "second_a()" ) if tobj.second_p() != "": failure( "second_p()" ) if tobj.second_a( 'one' ) != []: failure( "second_a('one')" ) if tobj.second_p( 'one' ) != "": failure( "second_p('one')" ) if tobj.second_a( 'one', 'two' ) != [ 'two' ]: failure( "second_a( 'one', 'two' )" ) if tobj.second_p( 'one', 'two' ) != "two": failure( "second_p( 'one', 'two' )" ) if tobj.second_a( 'one', 'two', 'three' ) != ['two', 'three']: failure( "second_a('one', 'two', 'three')" ) if tobj.second_p( 'one', 'two', 'three' ) != "two.three": failure( "second_p( 'one', 'two', 'three' )" ) // two formal parameters if tobj.third_a() != []: failure( "third_a()" ) if tobj.third_p() != "": failure( "third_p()" ) if tobj.third_a( 'one' ) != []: failure( "third_a('one')" ) if tobj.third_p( 'one' ) != "": failure( "third_p('one')" ) if tobj.third_a( 'one', 'two' ) != []: failure( "third_a( 'one', 'two' )" ) if tobj.third_p( 'one', 'two' ) != "": failure( "third_p( 'one', 'two' )" ) if tobj.third_a( 'one', 'two', 'three' ) != ['three']: failure( "third_a('one', 'two', 'three')" ) if tobj.third_p( 'one', 'two', 'three' ) != "three": failure( "third_p( 'one', 'two', 'three' )" ) success() /* End of file */ tests/core/testsuite/path_int.fal000066400000000000000000000025471176363201700175110ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 117c * Category: rtl * Subcategory: URI * Short: String width mix in URIs * Description: * This test checks if URI and PATH classes can handle mixed width * international character sequences. * [/Description] ****************************************************************************/ // Wide->small u = URI( "/国際/filename.dat", false ) p = Path( u.path ) p.file = p.file + "_fm" p.extension = "ext" u.path = p.path if URI.decode( u.uri ) != "/国際/filename_fm.ext": failure( "small in wide" ) // shortening p.location = "/t" u.path = p.path if URI.decode( u.uri ) != "/t/filename_fm.ext": failure( "reducing" ) // re-lenghtening p.location = "/国際" u.path = p.path if URI.decode( u.uri ) != "/国際/filename_fm.ext": failure( "re-enlarging" ) // Small -> wide (shortening) u = URI( "/test_with_a_long_path/filename.dat" ) p = Path( u.path ) p.location = "/国際" p.file = p.file + "_fm" p.extension = "ext" u.path = p.path if URI.decode( u.uri ) != "/国際/filename_fm.ext": failure( "enlarging reduced" ) // SMall->wide - lengthening u = URI( "/t/filename.dat" ) p = Path( u.path ) p.location = "/国際" p.file = p.file + "_fm" p.extension = "ext" u.path = p.path if URI.decode( u.uri ) != "/国際/filename_fm.ext": failure( "enlarging wide" ) tests/core/testsuite/pathtest.fal000066400000000000000000000030361176363201700175310ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 117b * Category: rtl * Subcategory: Path * Short: Test for path parsing. * Description: * This test checks the functionalities of path class. * [/Description] * ****************************************************************************/ path = Path( ["C:", "/path/to/", "myfile", "ext"] ) if path.path != "/C:/path/to/myfile.ext": failure( "Initial setup" ) if path.unit != "C": failure( "Initial unit" ) if path.location != "/path/to": failure( "Location" ) if path.file != "myfile": failure( "Initial file" ) if path.extension != "ext": failure( "Initial extension" ) if path.filename != "myfile.ext": failure( "Initial filename" ) // Change the whole path path.path = "C1:/path/to1/myfile1.ext1" if path.unit != "C1": failure( "Chagned unit" ) if path.location != "/path/to1": failure( "Chagned Location" ) if path.file != "myfile1": failure( "Chagned file" ) if path.extension != "ext1": failure( "Chagned extension" ) if path.filename != "myfile1.ext1": failure( "Chagned filename" ) path.path = "C:/path/to/myfile.ext" path.unit = "" if path.path != "/path/to/myfile.ext": failure( "Nilled unit" ) path.location = "" if path.path != "myfile.ext": failure( "Nilled location" ) path.extension = "" if path.path != "myfile": failure( "Nilled extension" ) path.filename = "Test.txt" if path.file != "Test": failure( "Filename into file" ) if path.extension != "txt": failure( "Filename into extension" ) success() /* end of uritest.fal */ tests/core/testsuite/powexpr.fal000066400000000000000000000012541176363201700174010ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 2c * Category: expression * Subcategory: * Short: Power operator * Description: * Tests for power operator. * This leverages on conditionals. * [/Description] * ****************************************************************************/ // basic expr a = 5 ** 2 if a != 25.0: failure( "Simple power" ) a = a ** ( 1 / 2 ) if a != 5.0: failure( "Simple radix" ) try a = 0**-1 // just to dirty math errno and see if we reset it. catch MathError end try a = 5**2 // see if errno is clear catch failure( "Power error recovering" ) end success() /* End of file */ tests/core/testsuite/prototype_oop_1.fal000066400000000000000000000020761176363201700210420ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 26a * Category: prototype * Subcategory: dict * Short: Prototype oop * Description: * Checking prototype OOP on blessed linear dictionaries. * [/Description] * ****************************************************************************/ // Smoke test: compilation v = bless( [ "prop" => 0, "add" => function( val ); self.prop += val; end, "sub" => function( val ); self["prop"] -= val; end, 1 => 0 ]) // Cover some basics first if not v provides prop: failure( "Provides property" ) if not v provides add: failure( "Provides method" ) if "prop" notin v or v["prop"] != 0 failure( "Dict access" ) end if v.prop != 0: failure( "Dot access" ) v["prop"] = 1 if "prop" notin v or v["prop"] != 1 failure( "Dict write" ) end v.prop = 0 if v.prop != 0: failure( "Dot write" ) // and now some call v.add( 2 ) if v.prop != 2: failure( "self with dot access" ) v.sub( 1 ) if v.prop != 1: failure( "self with dict access" ) success() /* End of file */ tests/core/testsuite/prototype_oop_2.fal000066400000000000000000000021031176363201700210320ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 26b * Category: prototype * Subcategory: pagedict * Short: Prototype oop - page dict * Description: * Checking prototype OOP on blessed page dictionaries. * [/Description] * ****************************************************************************/ // Smoke test: compilation v = bless( PageDict() ) v["prop"] = 0 v["add"] = function( val ); self.prop += val; end v["sub"] = function( val ) self["prop"] -= val end // Cover some basics first if not v provides prop: failure( "Provides property" ) if not v provides add: failure( "Provides method" ) if "prop" notin v or v["prop"] != 0 failure( "Dict access" ) end if v.prop != 0: failure( "Dot access" ) v["prop"] = 1 if "prop" notin v or v["prop"] != 1 failure( "Dict write" ) end v.prop = 0 if v.prop != 0: failure( "Dot write" ) // and now some call v.add( 2 ) if v.prop != 2: failure( "self with dot access" ) v.sub( 1 ) if v.prop != 1: failure( "self with dict access" ) success() /* End of file */ tests/core/testsuite/prototype_oop_3.fal000066400000000000000000000016251176363201700210430ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 26c * Category: prototype * Subcategory: dict * Short: Prototype copy oop * Description: * Checking prototype OOP on blessed linear dictionaries. In particular, * this test checks for copying and inter-copy safety of prototypes. * [/Description] * ****************************************************************************/ // Smoke test: compilation v = bless( [ "prop" => 0, "add" => function( val ); self.prop += val; end, "sub" => function( val ); self["prop"] -= val; end, 1 => 0 ]) // our copy c = v.clone() if c.prop != 0: failure( "Clone" ) v.add( 2 ) c.sub( 2 ) if v.prop != 2: failure( "Source insolation - value" ) if c.prop != -2: failure( "Dest insolation - value" ) v["another"] = 1 if c provides another: failure( "Content insolation" ) success() /* End of file */ tests/core/testsuite/prototype_oop_4.fal000066400000000000000000000016321176363201700210420ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 26d * Category: prototype * Subcategory: pagedict * Short: Prototype copy oop - page dict * Description: * Checking prototype OOP on blessed page dictionaries. In particular, * this test checks for copying and inter-copy safety of prototypes. * [/Description] * ****************************************************************************/ // Smoke test: compilation v = bless( PageDict() ) v["prop"] = 0 v["add"] = function( val ); self.prop += val; end v["sub"] = function( val ) self["prop"] -= val end // our copy c = v.clone() if c.prop != 0: failure( "Clone" ) v.add( 2 ) c.sub( 2 ) if v.prop != 2: failure( "Source insolation - value" ) if c.prop != -2: failure( "Dest insolation - value" ) v["another"] = 1 if c provides another: failure( "Content insolation" ) success() /* End of file */ tests/core/testsuite/prototype_oop_array_1.fal000066400000000000000000000016631176363201700222410ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 26f * Category: prototype * Subcategory: array * Short: Prototype oop array * Description: * Checking prototype OOP on arrays. * [/Description] * ****************************************************************************/ // Smoke test: compilation v = [0] v.prop = 0 v.add = function( val ); self.prop += val; end v.sub = function( val ) self.prop -= val self[0] -= val end // Cover some basics first if not v provides prop: failure( "Provides property" ) if not v provides add: failure( "Provides method" ) if v.prop != 0: failure( "Dot access" ) v.prop = 1 if v.prop != 1: failure( "Dot write" ) // and now some call v.add( 2 ) if v.prop != 3: failure( "Self with dot access" ) v.sub( 1 ) if v.prop != 2: failure( "Self with array access" ) if v[0] != -1: failure( "Self as array" ) success() /* End of file */ tests/core/testsuite/prototype_oop_array_2.fal000066400000000000000000000014261176363201700222370ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 26g * Category: prototype * Subcategory: array * Short: Prototype copy oop - array * Description: * Checking prototype OOP arrays. In particular, * this test checks for copying and inter-copy safety of prototypes. * [/Description] * ****************************************************************************/ // Smoke test: compilation v = [] v.prop = 0 v.add = function( val ); self.prop += val; end v.sub = function( val ) self.prop -= val end // our copy c = v.clone() if c.prop != 0: failure( "Clone" ) v.add( 2 ) c.sub( 2 ) if v.prop != 2: failure( "Source insolation - value" ) if c.prop != -2: failure( "Dest insolation - value" ) success() /* End of file */ tests/core/testsuite/prototype_oop_array_3.fal000066400000000000000000000021621176363201700222360ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 26h * Category: prototype * Subcategory: array * Short: Prototype indirect arrays * Description: * Checking OOP indirect operations on array based prototyped objects. * * In 0.8.x version, VM has a special code used to complie string expansion * and indirect operators which mimics the full compiler, but is much more * compact and faster. However, this duplicates code, at least in * functionalities, and that must be tested separately for arrays and * dictionary bindings. * ****************************************************************************/ v = [] v.prop = 0 v.add = function( val ); self.prop += val; end // copy try s = @"$v.prop" if s != "0" failure( "Stex value" ) end catch failure( "Stex compilation" ) end try s = #"v.prop" if s != 0 failure( "INDI value" ) end catch failure( "INDI compilation" ) end mth = #"v.add" try mth(2) if v.prop != 2 failure( "INDI call" ) end catch failure( "INDI exception on call" ) end success() /* End of file */ tests/core/testsuite/prototype_oop_serial.fal000066400000000000000000000015651176363201700221630ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 26j * Category: prototype * Subcategory: pagedict * Short: Pagedict serialize * Description: * Serialize blessed page dictionaries. * [/Description] * ****************************************************************************/ // Smoke test: compilation v = bless( PageDict() ) v["prop"] = 0 v["add"] = function( val ); self.prop += val; end v["sub"] = function( val ) self["prop"] -= val end stream = StringStream() v.serialize( stream ) stream.seek( 0 ) // our copy c = deserialize( stream ) if c.prop != 0: failure( "Clone" ) v.add( 2 ) c.sub( 2 ) if v.prop != 2: failure( "Source insolation - value" ) if c.prop != -2: failure( "Dest insolation - value" ) v["another"] = 1 if c provides another: failure( "Content insolation" ) success() /* End of file */ tests/core/testsuite/prototype_stex.fal000066400000000000000000000023751176363201700210120ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 26e * Category: prototype * Subcategory: dict * Short: Prototype indirect * Description: * Checking OOP indirect operations on dictionary based prototyped objects. * * In 0.8.x version, VM has a special code used to complie string expansion * and indirect operators which mimics the full compiler, but is much more * compact and faster. However, this duplicates code, at least in * functionalities, and that must be tested separately for arrays and * dictionary bindings. * [/Description] * ****************************************************************************/ // Smoke test: compilation v = bless( [ "prop" => 0, "add" => function( val ); self.prop += val; end, "sub" => function( val ); self["prop"] -= val; end, 1 => 0 ]) // copy try s = @"$v.prop" if s != "0" failure( "Stex value" ) end catch failure( "Stex compilation" ) end try s = #"v.prop" if s != 0 failure( "INDI value" ) end catch failure( "INDI compilation" ) end mth = #"v.add" try mth(2) if v.prop != 2 failure( "INDI call" ) end catch in e failure( "INDI exception on call" ) end success() /* End of file */ tests/core/testsuite/range_err.fal000066400000000000000000000007101176363201700176350ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 41b * Category: types * Subcategory: Range * Short: Range error checks * Description: * Checks for error in the generation of ranges. * [/Description] ****************************************************************************/ expr = "a" try a = [0:expr:1] failure( "Error not caught" ) catch TypeError end success() /* end of test */ tests/core/testsuite/recurse_lambda.fal000066400000000000000000000010731176363201700206440ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 18j * Category: lambda * Subcategory: * Short: Recursive lambdas * Description: * Test for the 'fself' keyword to dig into current lambdas. * [/Description] * ****************************************************************************/ if {x=> x>1 ? x+fself(x-1) : 1 }(5) != 15: failure( "Compact version" ) if { x => if x > 1: return x + fself(x-1) return 1 }(5) != 15 failure( "Expanded version" ) end success() /* End of file */ tests/core/testsuite/recursion.fal000066400000000000000000000011151176363201700177020ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 13c * Category: functions * Subcategory: * Short: Recursion * Description: * Minimal function call test. If this isn't passed, many things won't be * passed too. * [/Description] * ****************************************************************************/ function recurse( param ) if param == 1 return 1 end return param + recurse( param - 1 ) end // sum the first 10 numbers if recurse( 10 ) != 55: failure( "Recursion count" ) success() /* End of file */ tests/core/testsuite/reference.fal000066400000000000000000000007311176363201700176320ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 3a * Category: reference * Subcategory: * Short: References * Description: * Reference access and assignment * [/Description] * ****************************************************************************/ var = 0 ref = $var ref = 2 if var != 2: failure( "Reference assign" ) ref = $0 ref = 1 if var != 2: failure( "Reference unlink" ) success() /* End of file */ tests/core/testsuite/reflect.fal000066400000000000000000000017301176363201700173200ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 117a * Category: rtl * Subcategory: reflection * Short: Auto-reflectivity * Description: * Checks correct update of reflective properties with auto-operations * [/Description] * ****************************************************************************/ // create a reflective property timestamp = TimeStamp() timestamp.day = 5 timestamp.day++ if timestamp.day != 6: failure( "++" ) timestamp.day-- if timestamp.day != 5: failure( "--" ) timestamp.day+=5 if timestamp.day != 10: failure( "+=" ) timestamp.day-=2 if timestamp.day != 8: failure( "-=" ) timestamp.day*=2 if timestamp.day != 16: failure( "*=" ) timestamp.day/=2 if timestamp.day != 8: failure( "/=" ) timestamp.day|=1 if timestamp.day != 9: failure( "|=" ) timestamp.day&=8 if timestamp.day != 8: failure( "&=" ) // there are another couple of them, but we trust them. success() /* End of file */ tests/core/testsuite/rng_as_array.fal000066400000000000000000000021011176363201700203340ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 11m * Category: types * Subcategory: range * Short: Range as array * Description: * Test for array access on range variables. * [/Description] * ****************************************************************************/ // a range range = [1:3] // basic test, we need this to certify everything works if range != [1:3]: failure( "Range comparation" ) // test for range access if range[0] != 1: failure( "Range access begin" ) if range[1] != 3: failure( "Range access end" ) if range[-2] != 3: failure( "Range access -end" ) if range[-1] != 0: failure( "Range access -step" ) // test for range write range[0] = 4 if range != [4:3]: failure( "range set begin" ) range[1] = 4 if range != [4:4]: failure( "range set end" ) range[-1] = 5 if range != [4:4:5]: failure( "range set -end" ) // opening the range range[2] = nil if range != [4:]: failure( "range opening" ) range = [1:2] range[-1] = nil if range != [1:]: failure( "range -opening" ) success() /* End of file */ tests/core/testsuite/rtlAbs.fal000066400000000000000000000013001176363201700171140ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 113g * Category: rtl * Subcategory: math * Short: abs * Description: * Checks for correct working of abs() function. * [/Description] ****************************************************************************/ // Integer numbers if abs( 1 ) != 1: failure( "abs integer positive" ) if abs( -1 ) != 1: failure( "abs integer negative" ) if abs( 0 ) != 0: failure( "abs zero" ) // positive if abs( 1.5 ) != 1.5: failure( "abs on float" ) if abs( -1.5 ) != 1.5: failure( "abs on float neg" ) try floor( "alpha" ) failure( "Error not risen for parameters" ) end success() /* End of file */ tests/core/testsuite/rtlArrayBase.fal000066400000000000000000000031001176363201700202600ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 102a * Category: rtl * Subcategory: array * Short: Array substitute ops * Description: * Test for RTL functions operating on arrays as the VM. * [/Description] * ****************************************************************************/ array = [ "x", 2, 3, 4 ] arrayIns( array, 2, "a" ) if len( array ) != 5: failure( "arrayIns resizing" ) if array[2] != "a": failure( "arrayIns insertion" ) arrayAdd( array, "a" ) if len( array ) != 6: failure( "arrayAdd resizing" ) if array[5] != "a": failure( "arrayAdd insertion" ) arrayDel( array, "x" ) if len( array ) != 5: failure( "arrayDel resizing" ) if array[0] != 2: failure( "arrayDel removal" ) arrayResize( array, 2 ) if len( array ) != 2: failure( "arrayResize down" ) arrayResize( array, 10 ) try if array[9] != nil: failure( "arrayResize clearing" ) array[9] = 9 if array[9] != 9: failure( "arrayResize write/read" ) catch failure( "Resized array access" ) end try x = arrayBuffer( 10 ) if x.len() != 10: failure( "Array buffer basic (length)" ) for i in [0:x.len()] if x[i] != nil: failure( "Array buffer basic" ) end catch AccessError failure( "Array buffer basic (access)" ) end try x = arrayBuffer( 10, "default" ) if x.len() != 10: failure( "Array buffer default (length)" ) for i in [0:x.len()] if x[i] != "default": failure( "Array buffer default" ) end catch AccessError failure( "Array buffer default (access)" ) end success() /* End of file */ tests/core/testsuite/rtlArrayDelAll.fal000066400000000000000000000012161176363201700205510ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 102j * Category: rtl * Subcategory: array * Short: ArrayDelAll * Description: * Test for RTL functions operating on arrays as the VM. * This tests the arrayDelAll() function * [/Description] * ****************************************************************************/ array = [ "x", 2, 3, "x", "x", "a", "b", 4 ] arrayDelAll( array, "x" ) if len( array ) != 5: failure( "arrayDelAll resizing" ) if array[0] != 2: failure( "arrayDelAll removal - 1" ) if array[2] != "a": failure( "arrayDelAll removal - 2" ) success() /* End of file */ tests/core/testsuite/rtlArrayFill.fal000066400000000000000000000014141176363201700203020ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 102d * Category: rtl * Subcategory: array * Short: Array fill * Description: * Test for array filling routines. * [/Description] * ****************************************************************************/ array = [ 1, 2, 3, 4, 5 ] arrayFill( array, 100 ) if array.len() != 5: failure( "fill length" ) for n in array if n != 100: failure( "Fill content" ) end array.fill( "hello" ) if array.len() != 5: failure( "fill length 2" ) for n in array if n != "hello": failure( "Fill content 2" ) end array[2][0] = "H" if array[2] != "Hello": failure( "Modify after fill" ) if array[3] != "hello": failure( "Insolation after fill" ) success() /* End of file */ tests/core/testsuite/rtlArrayFind.fal000066400000000000000000000016541176363201700203020ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 102b * Category: rtl * Subcategory: array * Short: Array find * Description: * Searching function for arrays. * [/Description] * ****************************************************************************/ array = [ 1, 2, "alpha", 3, "beta", 4 ] pos = arrayFind( array, "alpha" ) if pos != 2: failure( "arrayFind unconditional positive" ) pos = arrayFind( array, 50 ) if pos != -1: failure( "arrayFind unconditional negative" ) pos = arrayFind( array, "alpha", 2 ) if pos != 2: failure( "arrayFind from positive" ) pos = arrayFind( array, "alpha", 1, 4 ) if pos != 2: failure( "arrayFind from, to positive" ) pos = arrayFind( array, "alpha", 3 ) if pos != -1: failure( "arrayFind from negative" ) pos = arrayFind( array, "alpha", 3, 5 ) if pos != -1: failure( "arrayFind from, to negative" ) success() /* End of file */ tests/core/testsuite/rtlArrayMerge.fal000066400000000000000000000026041176363201700204550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 102h * Category: rtl * Subcategory: array * Short: Array merging * Description: * test for function merge array. * [/Description] * ****************************************************************************/ array = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] arr1 = [ "a", "b", "c" ] arr2 = clone( array ) arrayMerge( arr2, arr1 ) if len( arr2 ) != 13 or arr2[0] != 1 or arr2[10] != "a" failure( "arrayMerge as append" ) end arr2 = clone( array ) arrayMerge( arr2, arr1, 5 ) if len( arr2 ) != 13 or arr2[0] != 1 or arr2[5] != "a" or arr2[8] != 6 failure( "arrayMerge as insert" ) end arr2 = clone( array ) arrayMerge( arr2, arr1, 5, 1 ) if len( arr2 ) != 12 or arr2[0] != 1 or arr2[5] != "b" or arr2[7] != 6 failure( "arrayMerge as partial insert" ) end arr2 = clone( array ) arrayMerge( arr2, arr1, 5, 1, 2 ) if len( arr2 ) != 11 or arr2[0] != 1 or arr2[5] != "b" or arr2[6] != 6 failure( "arrayMerge as partial insert 2" ) end arr2 = clone( array ) arrayMerge( arr2, arr1, 0, 0, 2 ) if len( arr2 ) != 12 or arr2[0] != "a" or arr2[2] != 1 failure( "arrayMerge as head insert" ) end arr2 = clone( array ) arrayMerge( arr2, arr1, 1, 2, 0 ) if len( arr2 ) != 13 or arr2[0] != 1 or arr2[1] != "c" or arr2[4] != 2 failure( "arrayMerge reverse insert" ) end success() /* End of file */ tests/core/testsuite/rtlArrayRemove.fal000066400000000000000000000013741176363201700206560ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 102g * Category: rtl * Subcategory: array * Short: Array remove * Description: * Test for array item removal using rtl functions * [/Description] * ****************************************************************************/ array = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] // ranged remove arrayRemove( array, 5, 7 ) if len( array ) != 8: failure ( "Basic removal" ) if array[4] != 5: failure( "too early" ) if array[5] != 8: failure( "incorrect removal" ) // point remove arrayRemove( array, 0 ) if len( array ) != 7: failure ( "Basic removal -- single item" ) if array[0] != 2: failure( "incorrect removal -- single item" ) success() /* End of file */ tests/core/testsuite/rtlArrayScan.fal000066400000000000000000000021241176363201700202770ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 102c * Category: rtl * Subcategory: array * Short: Array scan * Description: * Searching function for arrays. * [/Description] * ****************************************************************************/ function func( element ) if element == "alpha" return 1 end return 0 end function func1( element ) if element == "000000" return 1 end return 0 end array = [ 1, 2, "alpha", 3, "beta", 4 ] pos = arrayScan( array, func ) if pos != 2: failure( "arrayScan unconditional positive" ) pos = arrayScan( array, func1 ) if pos != -1: failure( "arrayScan unconditional negative" ) pos = arrayScan( array, func, 2 ) if pos != 2: failure( "arrayScan from positive" ) pos = arrayScan( array, func, 1, 4 ) if pos != 2: failure( "arrayScan from, to positive" ) pos = arrayScan( array, func, 3 ) if pos != -1: failure( "arrayScan from negative" ) pos = arrayScan( array, func, 3, 5 ) if pos != -1: failure( "arrayScan from, to negative" ) success() /* End of file */ tests/core/testsuite/rtlArraySort.fal000066400000000000000000000020231176363201700203400ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 103a * Category: rtl * Subcategory: array * Short: Array sort basic * Description: * Test for sorting using the default FALCON language ordering algorithm. * [/Description] * ****************************************************************************/ array = [ 1, 5, 8, 4, 9, 4, 3, 2, 1, 2, 1, 2, 7, 4, 3, 2, 9, 10 ] arraySort( array ) old = 0 for element in array if element < old: failure( "Sort integers" ) old = element end array = [ "Zeta", "ypsilon", "Alfa", "beta", "GAMMA", "tetha", "TETHA", "omicron", "Psi", "fi"] arraySort( array ) old = 0 for element in array if element < old: failure( "Sort string" ) old = element end array = [ "Zeta", "ypsilon", 5, 1, "Alfa", "beta", 8.2, "GAMMA", 4.1, "tetha", "TETHA", 6, "omicron", "Psi", "fi" ] arraySort( array ) old = 0 for element in array if element < old: failure( "Sort mixed" ) old = element end success() /* End of file */ tests/core/testsuite/rtlArraySort2.fal000066400000000000000000000022461176363201700204310ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 103b * Category: rtl * Subcategory: array * Short: Array sort advanced * Description: * Test for sort using a custom sorting function. * [/Description] * ****************************************************************************/ function reverseSort( a, b ) if a > b return -1 elif a < b return 1 end return 0 end array = [ 1, 5, 8, 4, 9, 4, 3, 2, 1, 2, 1, 2, 7, 4, 3, 2, 9, 10 ] arraySort( array, reverseSort ) old = 1000 for element in array if element > old: failure( "Sort integers" ) old = element end array = [ "Zeta", "ypsilon", "Alfa", "beta", "GAMMA", "tetha", "TETHA", "omicron", "Psi", "fi"] arraySort( array, reverseSort ) old = "zzzzzzz" for element in array if element > old: failure( "Sort string" ) old = element end array = [ "Zeta", "ypsilon", 5, 1, "Alfa", "beta", 8.2, "GAMMA", 4.1, "tetha", "TETHA", 6, "omicron", "Psi", "fi" ] arraySort( array, reverseSort ) old = "zzzzzzz" for element in array if element > old: failure( "Sort mixed" ) old = element end success() /* End of file */ tests/core/testsuite/rtlC.fal000066400000000000000000000012761176363201700166050ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 113g * Category: rtl * Subcategory: math * Short: C * Description: * Checks for correct working of C( n, r ) (combination) function. * [/Description] ****************************************************************************/ // Integer numbers if C( 5, 1 ) != 5: failure( "C(5, 1) incorrectly evaluated" ) if C( 5, 2 ) != 10: failure( "C(5, 2) incorrectly evaluated" ) if C( 5, 3 ) != 10: failure( "C(5, 3) incorrectly evaluated" ) if C( 5, 4 ) != 5: failure( "C(5, 4) incorrectly evaluated" ) if C( 5, 5 ) != 1: failure( "C(5, 5) incorrectly evaluated" ) success() /* End of file */ tests/core/testsuite/rtlCeil.fal000066400000000000000000000035371176363201700173010ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 113c * Category: rtl * Subcategory: math * Short: ceil * Description: * Checks for correct working of ceil() function. * [/Description] ****************************************************************************/ // Integer numbers if ceil( 1 ) != 1: failure( "ceil integer positive" ) if ceil( -1 ) != -1: failure( "ceil integer negative" ) if ceil( 0 ) != 0: failure( "ceil zero" ) // real ceils // positive if int( ceil( 1.0 ) ) != 1: failure( "ceil on .0 (int)" ) if fract( ceil( 1.0 ) ) != 0: failure( "ceil on .0 (fract)" ) if int( ceil( 1.4999 ) ) != 2: failure( "ceil positive down (int)" ) if fract( ceil( 1.49999999 ) ) != 0: failure( "ceil positive down (fract)" ) if int( ceil( 1.5 ) ) != 2: failure( "ceil positive edge (int)" ) if fract( ceil( 1.5 ) ) != 0: failure( "ceil positive edge (fract)" ) if int( ceil( 1.99999999 ) ) != 2: failure( "ceil positive up (int)" ) if fract( ceil( 1.99999999 ) ) != 0: failure( "ceil positive up (fract)" ) // negative if int( ceil( -1.0 ) ) != -1: failure( "ceil on .0 (int)" ) if fract( ceil( -1.0 ) ) != 0: failure( "ceil on .0 (fract)" ) if int( ceil( -1.4999 ) ) != -1: failure( "ceil negative down (int)" ) if fract( ceil( -1.49999999 ) ) != 0: failure( "ceil negative down (fract)" ) if int( ceil( -1.5 ) ) != -1: failure( "ceil negative edge (int)" ) if fract( ceil( -1.5 ) ) != 0: failure( "ceil negative edge (fract)" ) if int( ceil( -1.99999999 ) ) != -1: failure( "ceil negative up (int)" ) if fract( ceil( -1.99999999 ) ) != 0: failure( "ceil negative up (fract)" ) // zero if int( ceil( 0.0 ) ) != 0: failure( "ceil on 0.0 (int)" ) if fract( ceil( 0.0 ) ) != 0: failure( "ceil on 0.0 (fract)" ) // error try ceil( "alpha" ) failure( "Error not risen for parameters" ) end success() /* End of file */ tests/core/testsuite/rtlDictArr.fal000066400000000000000000000016431176363201700177510ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 104c * Category: rtl * Subcategory: dictionary * Short: Dictionary to array * Description: * This script tests the function that estracts arrays from * a dictionary. * This script uses arraySort, that therefore must work. * [/Description] * ****************************************************************************/ dict = [ "A" => 1, "b" => 2, "C" => 3, "d" => 4, 3 => "three", 1 => "one", 2 => "two" ] // dict keys/values extraction keys = dictKeys( dict ) if len( keys ) != len( dict ): failure( "Keys extraction base" ) values = dictValues( dict ) if len( values ) != len( dict ): failure( "Value extraction base" ) // dict keys/values test i = 0 for key, value in dict if key != keys[i]: failure( "Nth. key" ) if value != values[i]: failure( "Nth. value" ) i++ end success() /* End of file */ tests/core/testsuite/rtlDictBase.fal000066400000000000000000000022441176363201700200750ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 104a * Category: rtl * Subcategory: dictionary * Short: Dictionary traversal * Description: * This is the basic test for RTL dictionary functions: * dictionary traversal. * [/Description] * ****************************************************************************/ dict = [ "A" => 1, "b" => 2, "C" => 3, "d" => 4, 3 => "three", 1 => "one", 2 => "two" ] if len( dict ) != 7: failure( "len()" ) key = "d" dict[key] = "changed" if dictGet( dict, key ) != "changed": failure( "Access via dictGet" ) // get the iterator if dictFind( dict, "none" ) != nil: failure( "Negative dictFind" ) iter = dictFind( dict, "A" ) if iter.value() != 1: failure( "Positive dictFind" ) // Find the nearest iterator, and find one exat iter = dictBest( dict, "A" ) if iter.key() != "A": failure( "Dict best hitting" ) if isoob(iter): failure( "Dict best OOB indicator" ) // Find the nearest iterator, find nearest iter = dictBest( dict, 5 ) if iter.key() != "A": failure( "Dict best, near hit" ) if not isoob(iter): failure( "Dict best near hit OOB indicator" ) success() /* End of file */ tests/core/testsuite/rtlDictDop.fal000066400000000000000000000026671176363201700177560ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 104e * Category: RTL * Subcategory: dictionaries * Short: Dictionary.dop method. * Description: * Checks the dop method for various modalities. * [/Description] * ****************************************************************************/ dict = [=>] val = dict.dop( "value", 10 ) if val != 10: failure( "Simple dop return - 1") if "value" notin dict: failure( "Simple dop value creation" ) if dict["value"] != 10: failure( "Simple dop default setvalue" ) // now that the value is 10, it must not be changed val = dict.dop( "value", 11 ) if val != 10: failure( "Simple dop return - 2") if dict["value"] != 10: failure( "Simple dop non default setvalue" ) users = dict.dop( "users", [], { v => v += "one" } ) if users != ["one"]: failure( "Complex dop return value - 1" ) if "users" notin dict: failure( "Complex dop value creation" ) if dict["users"] != ["one"]: failure( "Complex dop default setvalue" ) users = dict.dop( "users", [], { v => v += "two" } ) if users != ["one", "two"]: failure( "Complex dop return value - 2" ) if dict["users"] != ["one", "two"]: failure( "Complex dop non default setvalue" ) // some error test try dict.dop( "a" ) failure( "Too few parameters not catched" ) catch ParamError end try dict.dop( "v", "k", "x" ) failure( "Non callable parameter not catched" ) catch ParamError end success() /* End of file */ tests/core/testsuite/rtlDictFill.fal000066400000000000000000000014561176363201700201150ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 104b * Category: rtl * Subcategory: dictionary * Short: Dictionary fill * Description: * Test for dictionary filling routines. * [/Description] * ****************************************************************************/ dict = [ 1=>"a", 2=>"b", 3=>"c", 4=>"d", 5=>"e" ] dictFill( dict, 100 ) if dict.len() != 5: failure( "fill length" ) for k,n in dict if n != 100: failure( "Fill content" ) end dict.fill( "hello" ) if dict.len() != 5: failure( "fill length 2" ) for k,n in dict if n != "hello": failure( "Fill content 2" ) end dict[2][0] = "H" if dict[2] != "Hello": failure( "Modify after fill" ) if dict[3] != "hello": failure( "Insolation after fill" ) success() /* End of file */ tests/core/testsuite/rtlDictRemove.fal000066400000000000000000000016561176363201700204660ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 104d * Category: rtl * Subcategory: dictionary * Short: Remove from dictionary * Description: * This script tests the function that remove elements from dictionaries. * [/Description] * ****************************************************************************/ // removal by key dict = [ "A" => 1, "b" => 2, "C" => 3, "d" => 4, 3 => "three", 1 => "one", 2 => "two" ] length = len( dict ) if not dictRemove( dict, "d" ): failure( "Removal key" ) if length - 1 != len( dict ): failure( "removal size - 4" ) if dictGet( dict, "d" ) != nil: failure( "found removed key" ) // negative remove by key if dictRemove( dict, "not present" ): failure( "Negative remove success") if length -1 != len( dict ): failure( "Negative remove activity" ) dictClear( dict ) if dict.len() > 0: failure( "Remove all" ) success() /* End of file */ tests/core/testsuite/rtlEnviron.fal000066400000000000000000000016661176363201700200460ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 115 * Category: rtl * Subcategory: environ * Short: Environment vars * Description: * Testing setenv/getenv vars. * Faltests sets a "FALCON_TESTING=1" for convenience. We'll expect this. * This also tests for setenv() and unsetenv() * [/Description] * ****************************************************************************/ if getenv( "FALCON_TESTING" ) != "1" failure( "FALCON_TESTING process envvar" ); end try setenv( "THIS_TEST_115", "A Value" ) catch failure( "envvar setting" ) end if getenv( "THIS_TEST_115" ) != "A Value" failure( "envvar get after setting" ) end if getenv( "Can't possibly exist an envvar like this =" ) != nil failure( "negative getenv()" ) end unsetenv( "THIS_TEST_115" ) if getenv( "THIS_TEST_115" ) != nil failure( "negative getenv() after unsetenv()" ) end success() /* End of file */ tests/core/testsuite/rtlFact.fal000066400000000000000000000010141176363201700172660ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 113f * Category: rtl * Subcategory: math * Short: Fact * Description: * Checks for correct working of fact( n ) (factorial) function. * [/Description] ****************************************************************************/ // Integer numbers if fact( 5 ) != 120: failure( "fact(5) incorrectly evaluated" ) if fact( 10 ) != 3628800: failure( "fact(10) incorrectly evaluated" ) success() /* End of file */ tests/core/testsuite/rtlFint.fal000066400000000000000000000036011176363201700173150ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 113e * Category: rtl * Subcategory: math * Short: fint * Description: * Checks for correct working of fint() (float integer) function. * [/Description] ****************************************************************************/ // Integer numbers if fint( 1 ) != 1: failure( "fint integer positive" ) if fint( -1 ) != -1: failure( "fint integer negative" ) if fint( 0 ) != 0: failure( "fint zero" ) // real fints // positive if int( fint( 1.0 ) ) != 1: failure( "fint on .0 (int)" ) if fract( fint( 1.0 ) ) != 0: failure( "fint on .0 (fract)" ) if int( fint( 1.4999 ) ) != 1: failure( "fint positive down (int)" ) if fract( fint( 1.49999999 ) ) != 0: failure( "fint positive down (fract)" ) if int( fint( 1.5 ) ) != 1: failure( "fint positive edge (int)" ) if fract( fint( 1.5 ) ) != 0: failure( "fint positive edge (fract)" ) if int( fint( 1.99999999 ) ) != 1: failure( "fint positive up (int)" ) if fract( fint( 1.99999999 ) ) != 0: failure( "fint positive up (fract)" ) // negative if int( fint( -1.0 ) ) != -1: failure( "fint on negative .0 (int)" ) if fract( fint( -1.0 ) ) != 0: failure( "fint on negative .0 (fract)" ) if int( fint( -1.4999 ) ) != -1: failure( "fint negative down (int)" ) if fract( fint( -1.49999999 ) ) != 0: failure( "fint negative down (fract)" ) if int( fint( -1.5 ) ) != -1: failure( "fint negative edge (int)" ) if fract( fint( -1.5 ) ) != 0: failure( "fint negative edge (fract)" ) if int( fint( -1.99999999 ) ) != -1: failure( "fint negative up (int)" ) if fract( fint( -1.99999999 ) ) != 0: failure( "fint negative up (fract)" ) // zero if int( fint( 0.0 ) ) != 0: failure( "fint on 0.0 (int)" ) if fract( fint( 0.0 ) ) != 0: failure( "fint on 0.0 (fract)" ) // error try fint( "alpha" ) failure( "Error not risen for parameters" ) end success() /* End of file */ tests/core/testsuite/rtlFloor.fal000066400000000000000000000036371176363201700175070ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 113d * Category: rtl * Subcategory: math * Short: floor * Description: * Checks for correct working of floor() function. * [/Description] ****************************************************************************/ // Integer numbers if floor( 1 ) != 1: failure( "floor integer positive" ) if floor( -1 ) != -1: failure( "floor integer negative" ) if floor( 0 ) != 0: failure( "floor zero" ) // real floors // positive if int( floor( 1.0 ) ) != 1: failure( "floor on .0 (int)" ) if fract( floor( 1.0 ) ) != 0: failure( "floor on .0 (fract)" ) if int( floor( 1.4999 ) ) != 1: failure( "floor positive down (int)" ) if fract( floor( 1.49999999 ) ) != 0: failure( "floor positive down (fract)" ) if int( floor( 1.5 ) ) != 1: failure( "floor positive edge (int)" ) if fract( floor( 1.5 ) ) != 0: failure( "floor positive edge (fract)" ) if int( floor( 1.99999999 ) ) != 1: failure( "floor positive up (int)" ) if fract( floor( 1.99999999 ) ) != 0: failure( "floor positive up (fract)" ) // negative if int( floor( -1.0 ) ) != -1: failure( "floor on negative .0 (int)" ) if fract( floor( -1.0 ) ) != 0: failure( "floor on negative .0 (fract)" ) if int( floor( -1.4999 ) ) != -2: failure( "floor negative down (int)" ) if fract( floor( -1.49999999 ) ) != 0: failure( "floor negative down (fract)" ) if int( floor( -1.5 ) ) != -2: failure( "floor negative edge (int)" ) if fract( floor( -1.5 ) ) != 0: failure( "floor negative edge (fract)" ) if int( floor( -1.99999999 ) ) != -2: failure( "floor negative up (int)" ) if fract( floor( -1.99999999 ) ) != 0: failure( "floor negative up (fract)" ) // zero if int( floor( 0.0 ) ) != 0: failure( "floor on 0.0 (int)" ) if fract( floor( 0.0 ) ) != 0: failure( "floor on 0.0 (fract)" ) // error try floor( "alpha" ) failure( "Error not risen for parameters" ) end success() /* End of file */ tests/core/testsuite/rtlFract.fal000066400000000000000000000013221176363201700174520ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 113a * Category: rtl * Subcategory: math * Short: Fractional part * Description: * Extracts the fractional from a floating point number. * [/Description] ****************************************************************************/ // integer fract if fract( 1 ) != 0: failure( "integer fract" ) // we must round the fract part, because it is imprecise if int( fract( 1.2312 ) * 10000 ) != 2312: failure( "correct fract" ) if int( fract( -1.6342 ) * 10000 ) != -6342: failure( "Negative fract" ) // error try fract( "alpha" ) failure( "Error not risen for parameters" ) end success() /* End of file */ tests/core/testsuite/rtlItemCopy.fal000066400000000000000000000021771176363201700201550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 105 * Category: rtl * Subcategory: * Short: item copying * Description: * Checking for item copying. * [/Description] ****************************************************************************/ // string on test string = "Hello world" string += "." smod = clone( string ) if smod != "Hello world.": failure( "String copy" ) string += "Modify" if smod != "Hello world.": failure( "String copy -- insolation" ) // array copy array = [ "a", "b", "c" ] amod = clone( array ) if len( amod ) != 3: failure( "Array copy" ) array += "element" if len( amod ) != 3: failure( "Array copy -- insolation" ) // dictionary copy dict = [ 1 => 1, 2 => 2, 3 => 3 ] dmod = clone( dict ) if dmod[2] != 2: failure( "Dict copy" ) dict[2] = "modified" if dmod[2] != 2: failure( "Dict copy -- insolation" ) // object copy object test prop1 = "one" prop2 = "two" end tmod = clone( test ) if tmod.prop1 != "one": failure( "Object copy" ) test.prop1 = "changed" if tmod.prop1 != "one": failure( "Object copy -- insolation" ) success() /* End of file */ tests/core/testsuite/rtlPow.fal000066400000000000000000000022321176363201700171610ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 113f * Category: rtl * Subcategory: math * Short: pow * Description: * Test for pow function. * [/Description] ****************************************************************************/ // standard pows if pow( 1, 0 ) != 1: failure( "pow(1,0)" ) if pow( 1, 1 ) != 1: failure( "pow(1,1)" ) if pow( 2, 0 ) != 1: failure( "pow(2,0)" ) if pow( 2, 1 ) != 2: failure( "pow(2,1)" ) if pow( 2, 2 ) != 4: failure( "pow(2,2)" ) if pow( 100, 1/2 ) != 10: failure( "pow(100,1/2)" ) if pow( -2, 2 ) != 4: failure( "pow(-2,2)" ) if pow( -2, 3 ) != -8: failure( "pow(-2,3)" ) if pow( 10, -1 ) != 1/10: failure( "pow(10,-1)" ) // some error check try pow( 2 ) failure( "Too few params error not raised" ) catch ParamError in e end try pow( 2, "a" ) failure( "Param type error not raised - second" ) catch ParamError in e end try pow( "a", 2 ) failure( "Param type error not raised - first" ) catch ParamError in e end // now some domain error try pow( -2, 0.5 ) failure( "Domain error not raised" ) catch MathError in e end success() /* End of file */ tests/core/testsuite/rtlRandom.fal000066400000000000000000000031461176363201700176410ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 112a * Category: rtl * Subcategory: math * Short: Random * Description: * Of course, random functions are not that much suitable for automatized * testsuite. Nevertheless, there are some random function whose result is * partially deterministic: pick and walk function must do some things * all the times (select some items... without crashing). * [/Description] ****************************************************************************/ // seed randomSeed( seconds() ) // random range numeric = random() if numeric < 0.0 or numeric >= 1.0: failure( "Random in [0, 1[ interval" ) // random integer integer = random( 2 ) if integer != 0 and integer != 1 and integer != 2: failure( "Integer 0 to 2" ) // random ranged integer integer = random( -1, 1 ) if integer != -1 and integer != 0 and integer != 1: failure( "Integer -1 to 1" ) // random ranged integer reverse integer = random( 1, -1 ) if integer != -1 and integer != 0 and integer != 1: failure( "Integer 1 to -1" ) // random pick, 2 items: pick = random( "a", 2 ) if pick != "a" and pick != 2: failure( "Pick 'a' or 2" ) // random pick 3 items pick = random( 1, 2, 3 ) if pick != 1 and pick != 2 and pick != 3 : failure( "Pick 1, 2 or 3" ) // explicit pick pick = randomChoice( 9, "a" ) if pick != 9 and pick != "a": failure( "random choice 9 or 'a'" ) // now some error: try random( "a" ) failure( "Error not raised for random(\"a\")" ) end try randomChoice() failure( "Error not raised for randomChoice()" ) end success() /* End of file */ tests/core/testsuite/rtlRandomGrab.fal000066400000000000000000000024461176363201700204370ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 112d * Category: rtl * Subcategory: math * Short: Random grab * Description: * Of course, random functions are not that much suitable for automatized * testsuite. Nevertheless, there are some random function whose result is * partially deterministic: pick and walk function must do some things * all the times (select some items... without crashing). * [/Description] ****************************************************************************/ // seed randomSeed( seconds() ) // random pick array = [ "a", "b", "c" ] item = randomGrab( array ) if len( item ) != 1 or len( array ) != 2 failure( "randomGrab" ) end array = [ "a", "b", "c" ] item = randomGrab( array, 2 ) if len( item ) != 2 or len( array ) != 1 failure( "randomWalk 2 items" ) end array = [ "a", "b", "c" ] item = randomGrab( array, 3 ) if len( item ) != 3 or len( array ) != 0 failure( "randomGrab 3 items" ) end // random grab canNOT create an array greater than the source array = [ "a", "b", "c" ] item = randomGrab( array, 4 ) if len( item ) != 3 or len( array ) != 0 failure( "randomWalk 4 items" ) end // errors try randomGrab() failure( "Empty random walk not raised" ) end success() /* End of file */ tests/core/testsuite/rtlRandomPick.fal000066400000000000000000000017401176363201700204460ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 112b * Category: rtl * Subcategory: math * Short: Random pick * Description: * Of course, random functions are not that much suitable for automatized * testsuite. Nevertheless, there are some random function whose result is * partially deterministic: pick and walk function must do some things * all the times (select some items... without crashing). * [/Description] ****************************************************************************/ // seed randomSeed( seconds() ) // random pick item = randomPick( [ "a", "b", "c" ] ) if item != "a" and item != "b" and item != "c" failure( "randomPick" ) end // error try randomPick() failure( "Empty random pick not raised" ) end try randomPick( "a" ) failure( "Non array random pick not raised" ) end try randomPick( [] ) failure( "Empty array random pick not raised" ) end success() /* End of file */ tests/core/testsuite/rtlRandomWalk.fal000066400000000000000000000022001176363201700204460ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 112c * Category: rtl * Subcategory: math * Short: Random walk * Description: * Of course, random functions are not that much suitable for automatized * testsuite. Nevertheless, there are some random function whose result is * partially deterministic: pick and walk function must do some things * all the times (select some items... without crashing). * [/Description] ****************************************************************************/ // seed randomSeed( seconds() ) // random pick array = [ "a", "b", "c" ] item = randomWalk( array ) if len( item ) != 1 failure( "randomWalk" ) end item = randomWalk( array, 2 ) if len( item ) != 2 failure( "randomWalk 2 items" ) end item = randomWalk( array, 3 ) if len( item ) != 3 failure( "randomWalk 3 items" ) end // random walk can create an array greater than the source item = randomWalk( array, 4 ) if len( item ) != 4 failure( "randomWalk 4 items" ) end // errors try randomWalk() failure( "Empty random walk not raised" ) end success() /* End of file */ tests/core/testsuite/rtlRound.fal000066400000000000000000000036271176363201700175140ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 113b * Category: rtl * Subcategory: math * Short: Number rounding * Description: * Checks for correct working of round() function. * [/Description] ****************************************************************************/ // Integer numbers if round( 1 ) != 1: failure( "round integer positive" ) if round( -1 ) != -1: failure( "round integer negative" ) if round( 0 ) != 0: failure( "round zero" ) // real rounds // positive if int( round( 1.0 ) ) != 1: failure( "round on .0 (int)" ) if fract( round( 1.0 ) ) != 0: failure( "round on .0 (fract)" ) if int( round( 1.4999 ) ) != 1: failure( "round positive down (int)" ) if fract( round( 1.49999999 ) ) != 0: failure( "round positive down (fract)" ) if int( round( 1.5 ) ) != 2: failure( "round positive edge (int)" ) if fract( round( 1.5 ) ) != 0: failure( "round positive edge (fract)" ) if int( round( 1.99999999 ) ) != 2: failure( "round positive up (int)" ) if fract( round( 1.99999999 ) ) != 0: failure( "round positive up (fract)" ) // negative if int( round( -1.0 ) ) != -1: failure( "round on .0 (int)" ) if fract( round( -1.0 ) ) != 0: failure( "round on .0 (fract)" ) if int( round( -1.4999 ) ) != -1: failure( "round positive down (int)" ) if fract( round( -1.49999999 ) ) != 0: failure( "round positive down (fract)" ) if int( round( -1.5 ) ) != -2: failure( "round positive edge (int)" ) if fract( round( -1.5 ) ) != 0: failure( "round positive edge (fract)" ) if int( round( -1.99999999 ) ) != -2: failure( "round positive up (int)" ) if fract( round( -1.99999999 ) ) != 0: failure( "round positive up (fract)" ) // zero if int( round( 0.0 ) ) != 0: failure( "round on 0.0 (int)" ) if fract( round( 0.0 ) ) != 0: failure( "round on 0.0 (fract)" ) // error try round( "alpha" ) failure( "Error not risen for parameters" ) end success() /* End of file */ tests/core/testsuite/rtlStrFill.fal000066400000000000000000000021341176363201700177740ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101i * Category: rtl * Subcategory: string * Short: strFill * Description: * Test on RTL string functions. * Check for strFill. * * [/Description] * ****************************************************************************/ string = "abcdefgh" strFill( string, 65 ) if string != "AAAAAAAA": failure( "single char numeric" ) strFill( string, "B" ) if string != "BBBBBBBB": failure( "single char string" ) strFill( string, "DE" ) if string != "DEDEDEDE": failure( "Multiple char pair" ) strFill( string, "GHI" ) if string != "GHIGHIGH": failure( "Multiple char impair" ) strFill( string, "国" ) if string != "国国国国国国国国": failure( "Wide char single" ) strFill( string, "国際" ) if string != "国際国際国際国際": failure( "Wide char multiple pair" ) strFill( string, "国際な" ) if string != "国際な国際な国際": failure( "Wide char multiple impair" ) strFill( string, 65 ) if string != "AAAAAAAA": failure( "Back to single char" ) success() /* End of file */ tests/core/testsuite/scoping.fal000066400000000000000000000022721176363201700173400ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 13d * Category: functions * Subcategory: * Short: Global/local variables * Description: * Check functionality of scoping. * [/Description] * ****************************************************************************/ value = "Global" function use_private() value = "private" if value != "private": failure( "Private access - use_private" ) end function read_global() if value != "Global": failure( "Global read - read_global" ) value = "private" if value != "private": failure( "Private access - read_global" ) end function use_global() if value != "Global": failure( "Global read - use_global" ) value = "private" if value != "private": failure( "Private access - use_global" ) global value if value != "Global": failure( "global keyword - use_global" ) value = "used" // test for grammar. global a, b end use_private() if value != "Global": failure( "Global scoping pollution 1" ) read_global() if value != "Global": failure( "Global scoping pollution 2" ) use_global() if value != "used": failure( "Not writing to global" ) success() /* End of file */ tests/core/testsuite/select.fal000066400000000000000000000024501176363201700171530ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 24a * Category: select * Subcategory: * Short: Mixed select * Description: * Checks repeatedly a select in a function call * [/Description] * ****************************************************************************/ function call_select( param ) select param case NumericType return "number" case StringType: return "string" case Test2: return "class test2" case Test: return "class test" case Instance: return "instance obj" default: return "something else" end end // Just a class test class Test prop = 0 end // Just a class test2 class Test2 prop = 0 end object Instance prop = 0 end if call_select( 1 ) != "number": failure( "Select on integers" ) if call_select( 1.5 ) != "number": failure( "Select on numbers" ) if call_select( "a string" ) != "string": failure( "Select on strings" ) if call_select( Instance ) != "instance obj": failure( "select on objects" ) itm = Test() if call_select( itm ) != "class test": failure( "select on classes 1" ) itm = Test2() if call_select( itm ) != "class test2": failure( "select on classes 2" ) if call_select( [3:2] ) != "something else": failure( "select default" ) success() /* End of file */ tests/core/testsuite/self_autosum.fal000066400000000000000000000012131176363201700203760ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 21g * Category: types * Subcategory: classes * Short: Self sum * Description: * Test for autooperators on sef. * [/Description] * ****************************************************************************/ class test sum = "" init list = [ "a", "b", "c", "d", "e" ] sum = "" for elem in list // we have called the accessor, so the list is initalized self.sum += elem end end end item = test() if item.sum != "abcde": failure( "Summing up self with self" ) success() /* End of file */ tests/core/testsuite/serialize.fal000066400000000000000000000030051176363201700176600ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 107a * Category: rtl * Subcategory: serialization * Short: Serialization * Description: * Testing serialization/deserialization of standard types. * [/Description] * ****************************************************************************/ // we need a stream stream = StringStream() serialize( "Hello world", stream ) serialize( 1, stream ) serialize( nil, stream ) serialize( 1.3, stream ) serialize( [3:-1], stream ) serialize( [3:], stream ) serialize( true, stream ) serialize( false, stream ) serialize( "last", stream ) stream.seek(0) try item = nil count = 0 while item != "last" item = deserialize( stream ) count ++ if count == 1 and item != "Hello world": failure( "first item" ) if count == 2 and item != 1: failure( "second item" ) if count == 3 and item != nil: failure( "third item" ) if count == 4 and item != 1.3: failure( "fourth item" ) if count == 5 and item != [3:-1]: failure( "fifth item" ) if count == 6 and item != [3:]: failure( "sixth item" ) if count == 7 and not item: failure( "seventh item" ) if count == 8 and item != false: failure( "eight item" ) end catch failure( "deserialization failed" ) end if count != 9: failure( "count of items" ) try stream.grab(1) if not stream.eof(): failure( "Stream was not at eof" ) catch failure( "Standard read after deserialization" ) end success() /* End of file */ tests/core/testsuite/serialize_array.fal000066400000000000000000000021651176363201700210640ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 107b * Category: rtl * Subcategory: serialization * Short: Array serialization * Description: * Serialization test on arrays. * [/Description] * ****************************************************************************/ // we need a stream stream = StringStream() array1 = [ "Hello", "world", "an item", 1, 2.2, nil, [2:3] ] array2 = [ "a", "nested", array1, "array" ] serialize( array1, stream ) serialize( array2, stream ) stream.seek(0) try item1 = deserialize( stream ) if len( array1 ) != len( item1 ): failure( "Basic" ) for i in [ 0 : len( array1 ) ] if array1[i] != item1[i]: failure( "Deep item retrival" ) end item2 = deserialize( stream ) if len( array2 ) != len( item2 ): failure( "Second deserialization" ) item3 = item2[2] if len( array1 ) != len( item3 ): failure( "Deep deserialization" ) for i in [ 0 : len( array1 ) ] if array1[i] != item3[i]: failure( "Deep item retrival 2" ) end catch failure( "Deserialization failed" ) end success() /* End of file */ tests/core/testsuite/serialize_array_fbom.fal000066400000000000000000000021651176363201700220670ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 107f * Category: fbom * Subcategory: serialization * Short: Array serialization * Description: * Serialization test on arrays. * [/Description] * ****************************************************************************/ // we need a stream stream = StringStream() array1 = [ "Hello", "world", "an item", 1, 2.2, nil, [2:3] ] array2 = [ "a", "nested", array1, "array" ] array1.serialize( stream ) array2.serialize( stream ) stream.seek(0) try item1 = deserialize( stream ) if len( array1 ) != len( item1 ): failure( "Basic" ) for i in [ 0 : len( array1 ) ] if array1[i] != item1[i]: failure( "Deep item retrival" ) end item2 = deserialize( stream ) if len( array2 ) != len( item2 ): failure( "Second deserialization" ) item3 = item2[2] if len( array1 ) != len( item3 ): failure( "Deep deserialization" ) for i in [ 0 : len( array1 ) ] if array1[i] != item3[i]: failure( "Deep item retrival 2" ) end catch failure( "Deserialization failed" ) end success() /* End of file */ tests/core/testsuite/serialize_dict.fal000066400000000000000000000022711176363201700206670ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 107c * Category: rtl * Subcategory: serialization * Short: Dictionary serialization * Description: * Serialization test on dictionaries. * This test does the same things as 107b, on arrays, to make thigns a bit * more complex for the serialize function; but it doesn't test the * outcomes that have been already tested before. * [/Description] * ****************************************************************************/ // we need a stream stream = StringStream() array1 = [ "Hello", "world", "an item", 1, 2.2, nil, [2:3] ] array2 = [ "a", "nested", array1, "array" ] dict = [ "elem1" => array1, "elem2" => array2 ] serialize( array1, stream ) serialize( dict, stream ) serialize( array2, stream ) stream.seek(0) try item1 = deserialize( stream ) item2 = deserialize( stream ) item3 = deserialize( stream ) res1 = item2[ "elem1" ] if len( array1 ) != len( res1 ): failure( "Basic" ) for i in [ 0 : len( array1 ) ] if array1[i] != res1[i]: failure( "Deep item retrival" ) end catch failure( "Deserialization failed" ) end success() /* End of file */ tests/core/testsuite/serialize_dict_fbom.fal000066400000000000000000000023341176363201700216720ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 107g * Category: fbom * Subcategory: serialization * Short: Dictionary serialization * Description: * Serialization test on dictionaries. * This test does the same things as 107b, on arrays, to make thigns a bit * more complex for the serialize function; but it doesn't test the * outcomes that have been already tested before. * [/Description] * ****************************************************************************/ // we need a stream stream = StringStream() array1 = [ "Hello", "world", "an item", 1, 2.2, nil, [2:3] ] array2 = [ "a", "nested", array1, "array" ] dict = [ "elem1" => array1, "elem2" => array2 ] array1.serialize( stream ) dict.serialize( stream ) array2.serialize( stream ) stream.seek(0) item1 = nil item2 = nil item3 = nil try item1 = deserialize( stream ) item2 = deserialize( stream ) item3 = deserialize( stream ) res1 = item2[ "elem1" ] if len( array1 ) != len( res1 ): failure( "Basic" ) for i in [ 0 : len( array1 ) ] if array1[i] != res1[i]: failure( "Deep item retrival" ) end catch failure( "Deserialization failed" ) end success() /* End of file */ tests/core/testsuite/serialize_fbom.fal000066400000000000000000000032041176363201700206640ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 107e * Category: fbom * Subcategory: serialization * Short: Serialization * Description: * Testing serialization/deserialization of standard types. * [/Description] * ****************************************************************************/ // we need a stream stream = StringStream() item = "Hello world" item.serialize( stream ) item = 1 item.serialize( stream ) item = nil item.serialize( stream ) item = 1.3 item.serialize( stream ) item = [3:-1] item.serialize( stream ) item = [3:] item.serialize( stream ) item = true item.serialize( stream ) item = false item.serialize( stream ) item = "last" item.serialize( stream ) stream.seek(0) try item = nil count = 0 while item != "last" item = deserialize( stream ) count ++ if count == 1 and item != "Hello world": failure( "first item" ) if count == 2 and item != 1: failure( "second item" ) if count == 3 and item != nil: failure( "third item" ) if count == 4 and item != 1.3: failure( "fourth item" ) if count == 5 and item != [3:-1]: failure( "fifth item" ) if count == 6 and item != [3:]: failure( "sixth item" ) if count == 7 and item != true: failure( "seventh item" ) if count == 8 and item != false: failure( "eight item" ) end catch in e failure( "deserialization failed: " + e.toString() ) end if count != 9: failure( "count of items" ) try stream.grab(1) if not stream.eof(): failure( "Stream was not at eof" ) catch failure( "Standard read after deserialization" ) end success() /* End of file */ tests/core/testsuite/serialize_obj.fal000066400000000000000000000032261176363201700205170ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 107d * Category: rtl * Subcategory: serialization * Short: Object serialization * Description: * Serialization test on dictionaries. * This test demonstrates the initialization of objects, by also checking * execution of the init routine. * [/Description] * ****************************************************************************/ // we need a stream stream = StringStream() class tester( p1, p2 ) prop1 = p1 prop2 = p2 init global counter counter ++ end function check( ck1, ck2 ) if ck1 != self.prop1: failure( "tester: Check on prop1" ) if len( ck2 ) != len( self.prop2 ): failure( "tester: Check on prop2" ) end end object testerObj prop1 = "a" prop2 = "b" init global counter1 if counter1 == nil: counter1 = 0 counter1 ++ end function check( ck1, ck2 ) if ck1 != self.prop1: failure( "testerObj: Check on prop1" ) if ck2 != self.prop2: failure( "testerObj: Check on prop2" ) end end counter = 0 counter1 = 0 array = [1,2] t1 = tester( "first", array ) serialize( t1, stream ) serialize( testerObj, stream ) stream.seek(0) try item1 = deserialize( stream ) item2 = deserialize( stream ) // the deserialization should not call the initializer (?) //if counter != 2: failure( "class item has not been re-created" ) //if counter1 != 2: failure( "object item has not been re-created" ) item1.check( "first", array ) item2.check( "a", "b" ) catch in e failure( "Deserialization failed: " + e.toString() ) end success() /* End of file */ tests/core/testsuite/serialize_obj_fbom.fal000066400000000000000000000032671176363201700215270ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 107h * Category: fbom * Subcategory: serialization * Short: Object serialization * Description: * Serialization test on dictionaries. * This test demonstrates the initialization of objects, by also checking * execution of the init routine. * [/Description] * ****************************************************************************/ // we need a stream stream = StringStream() class tester( p1, p2 ) prop1 = p1 prop2 = p2 init global counter counter ++ end function check( ck1, ck2 ) if ck1 != self.prop1: failure( "tester: Check on prop1" ) if len( ck2 ) != len( self.prop2 ): failure( "tester: Check on prop2" ) end end object testerObj prop1 = "a" prop2 = "b" init global counter1 if counter1 == nil: counter1 = 0 counter1 ++ end function check( ck1, ck2 ) if ck1 != self.prop1: failure( "testerObj: Check on prop1" ) if ck2 != self.prop2: failure( "testerObj: Check on prop2" ) end end counter = 0 counter1 = 0 array = [1,2] t1 = tester( "first", array ) t1.serialize( stream ) testerObj.serialize( stream ) stream.seek(0) item1 = nil item2 = nil try item1 = deserialize( stream ) item2 = deserialize( stream ) // the deserialization should not call the initializer (?) //if counter != 2: failure( "class item has not been re-created" ) //if counter1 != 2: failure( "object item has not been re-created" ) item1.check( "first", array ) item2.check( "a", "b" ) catch in e printl( e.toString() ) failure( "Deserialization failed" ) end success() /* End of file */ tests/core/testsuite/set.fal000066400000000000000000000012521176363201700164660ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 19a * Category: types * Subcategory: set * Short: Basic set * Description: * Checks the basic set functionalities with integers * [/Description] * ****************************************************************************/ set = Set(1,2,3) /* not yet implemented. // in if not (1 in set): failure( "In operator - positive" ) if 4 in set: failure( "In operator - negative" ) // notin if 1 notin set: failure( "Notin operator - negative" ) if not 4 notin set: failure( "Notin operator - positive" ) */ if set.len() != 3: failure( "Len FBOM" ) success() /* End of file */tests/core/testsuite/simpleexpr.fal000066400000000000000000000015521176363201700200660ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 2a * Category: expression * Subcategory: * Short: Basic expression * Description: * This test is meant to check for expressions (on simple items) to work. * This leverages on conditionals. * [/Description] * ****************************************************************************/ // basic expr a = 1 a = a == 2 if a: failure( "Assignment disambiguation 1" ) a = 1 a = a == 2 or 0 if a: failure( "Assignment disambiguation" ) // basic math a = 3 + 4 * 2 - 1 * 3 * ( 2 + 1 ) if a != 2: failure( "Math priortiy" ) b = 1 and 0 or 1 and 2 if not b: failure( "Connector expression 1" ) b = 0 b or 1 and 0 or (b = 2) if b != 2: failure( "Connector expression 2") b = 0 or 1 and not 3 or 1 if not b: failure( "Connector expression 3") success() /* End of file */ tests/core/testsuite/slave_1.fal000066400000000000000000000007501176363201700172270ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: modloader * Subcategory: * Short: Basic cascade compilation test, slave module * Description: * This is a slave module to be loaded by master_1 (ID 50a). It is never * directly executed. * [/Description] * ****************************************************************************/ function fromSlave() return 1 end export fromSlave /* End of file */ tests/core/testsuite/slave_2.fal000066400000000000000000000011611176363201700172250ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: modloader * Subcategory: * Short: Advanced cascade compilation test, slave module * Description: * This is a slave module to be loaded by master_2 (ID 50b). It is never * directly executed. * This test tries also to load slave_1, which should be already in place. * [/Description] * ****************************************************************************/ load slave_1 load slave_3 load slave_4 function fromSlave_2() return fromSlave_3() end export fromSlave_2 /* End of file */ tests/core/testsuite/slave_3.fal000066400000000000000000000010341176363201700172250ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: modloader * Subcategory: * Short: Advanced cascade compilation test, slave module * Description: * This is a slave module to be loaded by master_2 (ID 50b). It is never * directly executed. * [/Description] * ****************************************************************************/ load slave_5 load slave_6 function fromSlave_3() return fromSlave_5() or fromSlave_6() end export /* End of file */ tests/core/testsuite/slave_4.fal000066400000000000000000000007441176363201700172350ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: modloader * Subcategory: * Short: Advanced cascade compilation test, slave module * Description: * This is a slave module to be loaded by master_2 (ID 50b). It is never * directly executed. * [/Description] * ****************************************************************************/ function fromSlave_4() return 1 end export /* End of file */ tests/core/testsuite/slave_5.fal000066400000000000000000000007441176363201700172360ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: modloader * Subcategory: * Short: Advanced cascade compilation test, slave module * Description: * This is a slave module to be loaded by master_2 (ID 50b). It is never * directly executed. * [/Description] * ****************************************************************************/ function fromSlave_5() return 1 end export /* End of file */ tests/core/testsuite/slave_6.fal000066400000000000000000000007441176363201700172370ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: modloader * Subcategory: * Short: Advanced cascade compilation test, slave module * Description: * This is a slave module to be loaded by master_2 (ID 50b). It is never * directly executed. * [/Description] * ****************************************************************************/ function fromSlave_6() return 1 end export /* End of file */ tests/core/testsuite/slave_alpha.fal000066400000000000000000000013241176363201700201520ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: modloader * Subcategory: * Short: Link time init, slave. * Description: * This is a slave module to be loaded by master_3 (ID 50c). It is never * directly executed. * [/Description] * ****************************************************************************/ load slave_world object alpha from alpha_class val = nil // init should be inherithed from alpha_class end object delta from alpha_class val = nil init // just to check for return from other step if self notin world.elements: failure( "inherited initialization" ) end end export /* End of file */ tests/core/testsuite/slave_beta.fal000066400000000000000000000011171176363201700200000ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: modloader * Subcategory: * Short: Advanced cascade compilation test, slave module * Description: * This is a slave module to be loaded by master_2 (ID 50b). It is never * directly executed. * [/Description] * ****************************************************************************/ load slave_world object beta val = nil init world.add( self ) end function setVal( param ) self.val = param end end export /* End of file */ tests/core/testsuite/slave_glob.fal000066400000000000000000000010041176363201700200030ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: modloader * Subcategory: * Short: Globals across modules, slave * Description: * Provides a pre-initialized global variable for the master. * [/Description] * ****************************************************************************/ glob = "From Slave" function TestGlob() if glob != "Changed": failure( "Global change not reflected" ) end export TestGlob, glob /* End of file */ tests/core/testsuite/slave_world.fal000066400000000000000000000014411176363201700202140ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: modloader * Subcategory: * Short: Advanced cascade compilation test, slave module * Description: * This is a slave module to be loaded by master_2 (ID 50b). It is never * directly executed. * [/Description] * ****************************************************************************/ object world elements = nil items = nil function add( elem ) if self.elements == nil self.elements = [] self.items = 0 end self.elements += elem self.items++ end end class alpha_class val = 0 init world.add( self ) end function setVal( param ) self.val = param end end export /* End of file */ tests/core/testsuite/ss_stdout.fal000066400000000000000000000013771176363201700177320ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 106b * Category: rtl * Subcategory: streams * Short: Stdout on string stream * Description: * Testing the string stream. * This checks for the string stream to respect stream copy semantic. * We'll redirect the output steream to string stream, and then check * if it is still accessible. * [/Description] * ****************************************************************************/ // empty string stream stream = StringStream() // store it as stdout old = stdOut( stream ) // write to stdout >> "Hello world" // reset stdout stdOut( old ) if stream.closeToString() != "Hello world" failure( "not working" ) end success() /* End of file */ tests/core/testsuite/state_basic.fal000066400000000000000000000021641176363201700201570ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 39a * Category: states * Subcategory: * Short: Basic states * Description: * Checks basic state functionality (switching to and from states). * [/Description] * ****************************************************************************/ class Stateful function stateless() return "stateless" end function f1() return "f1-stateless" end [one] function f1(): return "f1-one" function f2(): return "f2-one" end [two] function f1(): return "f1-two" function f2(): return "f2-two" end end s = Stateful() if s.stateless() != "stateless": failure( "0:stateless" ) if s.f1() != "f1-stateless": failure( "0:f1-stateless" ) s.setState( s.one ) if s.stateless() != "stateless": failure( "stateless-one" ) if s.f1() != "f1-one": failure( "f1-one" ) if s.f2() != "f2-one": failure( "f1-one" ) s.setState( s.two ) if s.stateless() != "stateless": failure( "stateless-two" ) if s.f1() != "f1-two": failure( "f1-two" ) if s.f2() != "f2-two": failure( "f1-two" ) success() tests/core/testsuite/state_error.fal000066400000000000000000000013041176363201700202220ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 39e * Category: states * Subcategory: * Short: State error * Description: * Checks detection of errors when applying states. * [/Description] * ****************************************************************************/ class Stateful [one] end [two] end end class Stateless end s = Stateful() // applying an unexisting state try s.setState( "three" ) failure( "unexisting state applied" ) catch end // applying an existing state to a stateless class s = Stateless() try s.setState( Stateful.one ) failure( "Sate applied to stateless instance" ) catch end success() tests/core/testsuite/state_inherit.fal000066400000000000000000000035541176363201700205440ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 39b * Category: states * Subcategory: * Short: State inheritance * Description: * Checks correct working of inheritance when states are involved. * This test doesn't check for basic state functionalities (39a) * [/Description] * ****************************************************************************/ class Base [one] function f1(): return "f1-one-base" function f2(): return "f2-one-base" end [two] function f1(): return "f1-two-base" function f2(): return "f2-two-base" end end class DerivedOne from Base [one] function f1(): return "f1-one-d1" function f2(): return "f2-one-d1" end // inherit state two [three] function f1(): return "f1-three-d1" function f2(): return "f2-three-d1" end end class DerivedTwo from Base [one] function f1(): return "f1-one-d2" // clears f2 end [two] // clears state two end end // we already tested basic d1 = DerivedOne() d1.setState( d1.one ) if d1.f1() != "f1-one-d1": failure( "f1-one-d1" ) if d1.f2() != "f2-one-d1": failure( "f2-one-d1" ) d1.setState( d1.two ) if d1.f1() != "f1-two-base": failure( "f1-two-base" ) if d1.f2() != "f2-two-base": failure( "f2-two-base" ) d1.setState( d1.three ) if d1.f1() != "f1-three-d1": failure( "f1-three-d1" ) if d1.f2() != "f2-three-d1": failure( "f2-three-d1" ) d2 = DerivedTwo() d2.f2 = "occupied" d2.setState( d2.one ) if d2.f1() != "f1-one-d2": failure( "f1-one-d2" ) if d2.f2 != "occupied": failure( "clearing function not applied" ) d2.f1 = "occupied" d2.setState( d2.two ) // the state is empty, so nothing should be done if d2.f1 != "occupied": failure( "clearing state not applied - 1" ) if d2.f2 != "occupied": failure( "clearing state not applied - 2" ) success() tests/core/testsuite/state_init.fal000066400000000000000000000013761176363201700200450ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 39d * Category: states * Subcategory: * Short: Init state * Description: * Checks correct working of init state. * [/Description] * ****************************************************************************/ class Base element = 0 init self.element = "started" end [init] function __enter(): self.element = "touched" function f1(): return "from init" end end class Derived from Base init self.element = "changed" end end d1 = Derived() if d1.f1() != "from init": failure( "Init state not applied" ) if d1.element != "touched": failure( "Init state not applied after instance init" ) success() tests/core/testsuite/state_module.fal000066400000000000000000000014221176363201700203570ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 39e * Category: states * Subcategory: * Short: Intermodule state * Description: * Checks if states work across modules. * [/Description] * ****************************************************************************/ load .state_module_1 class Child from Parent end class Child_init from Parent test = 0 init self.test = "init" self.value = "init" end end // Try of a test without init. c = Child() if c.value != "enter": failure( "Initless __enter not followed" ) c = Child_init() if c.test != "init": failure( "Initful init block not executed" ) if c.value != "enter": failure( "Initful __enter not followed" ) success() /* end of file */ tests/core/testsuite/state_module_1.fal000066400000000000000000000010431176363201700205760ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: states * Subcategory: * Short: Intermodule state * Description: * Checks if states work across modules -- This is the second part (secondary * module. * [/Description] * ****************************************************************************/ export class Parent value = 0 function _setup() self.value = "enter" end [init] function __enter() self._setup() end end end tests/core/testsuite/state_trans.fal000066400000000000000000000020001176363201700202120ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 39c * Category: states * Subcategory: * Short: State transitions * Description: * Check for correct application of enter and leave methods. * This test doesn't check for basic state functionalities (39a) * [/Description] * ****************************************************************************/ class Stateful [one] function __leave( newstate ): return @"leaving to ($newstate)" end [two] function __enter( oldstate, leave ): return @"coming from ($oldstate) - ($leave)" end [three] end end s = Stateful() if s.setState( "one" ) != nil: failure( "No transition" ) if s.setState( s.three ) != "leaving to (three)": failure( "leave transition" ) if s.setState( s.two ) != "coming from (three) - (Nil)": failure( "enter transition" ) s.setState( s.one ) if s.setState( s.two ) != "coming from (one) - (leaving to (two))": failure( "enter - leave transition" ) success() tests/core/testsuite/static.fal000066400000000000000000000015431176363201700171650ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 13e * Category: functions * Subcategory: * Short: Static block * Description: * Tests static block execution and static variable access and modify * [/Description] * ****************************************************************************/ function use_static() static var1 = 1 return 0 end var1++ return var1 end function use_static_param( param ) static var1 = param return 0 end return var1 end if use_static() != 0: failure( "Static code execution 1" ) if use_static() != 2: failure( "Static local access" ) if use_static_param( "Static" ) != 0: failure( "Static code execution 2" ) if use_static_param( "Anything" ) != "Static": failure( "Parameter access from static" ) success() /* End of file */ tests/core/testsuite/strCase.fal000066400000000000000000000026411176363201700173020ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101h * Category: rtl * Subcategory: string * Short: Case tests * Description: * Test on RTL string functions * [/Description] * ****************************************************************************/ string = "This Is A String;?123" if strUpper( string ) != "THIS IS A STRING;?123": failure( "strUpper" ) if strLower( string ) != "this is a string;?123": failure( "strLower" ) if strCmpIgnoreCase( "aBC", "AbC" ) != 0: failure( "strCmpIgnoreCase 1" ) if strCmpIgnoreCase( "AbC", "AbC" ) != 0: failure( "strCmpIgnoreCase 2" ) if strCmpIgnoreCase( "abc", "ABC" ) != 0: failure( "strCmpIgnoreCase 3" ) if strCmpIgnoreCase( "aBC", "ABc" ) != 0: failure( "strCmpIgnoreCase 4" ) if strCmpIgnoreCase( "a1BC", "B1Bc" ) != -1: failure( "strCmpIgnoreCase 5" ) if strCmpIgnoreCase( "A1BC", "b1Bc" ) != -1: failure( "strCmpIgnoreCase 6" ) if strCmpIgnoreCase( "Z1BC", "a1Bc" ) != 1: failure( "strCmpIgnoreCase 7" ) if strCmpIgnoreCase( "z1BC", "A1Bc" ) != 1: failure( "strCmpIgnoreCase 8" ) if strCmpIgnoreCase( "aB1Cd", "aB1c" ) != 1: failure( "strCmpIgnoreCase 9" ) if strCmpIgnoreCase( "Ab1Cd", "Ab1C" ) != 1: failure( "strCmpIgnoreCase 10" ) if strCmpIgnoreCase( "aBC", "aBcd" ) != -1: failure( "strCmpIgnoreCase 11" ) if strCmpIgnoreCase( "AbC", "AbCd" ) != -1: failure( "strCmpIgnoreCase 12" ) success() /* End of file */ tests/core/testsuite/strEndsWith.fal000066400000000000000000000076211176363201700201570ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101r * Category: rtl * Subcategory: string * Short: EndsWith * Description: * Testing the endsWith functions and method. * [/Description] * ****************************************************************************/ //================================================= // Starts with - positive cases. // if not strEndsWith( "abc", "c" ): failure( "Minimal match endsWith func" ) if not strEndsWith( "abc", "bc" ): failure( "Double match endsWith func" ) if not strEndsWith( "abc", "abc" ): failure( "Complete match endsWith func" ) if not strEndsWith( "abc", "" ): failure( "Zero match endsWith func" ) if not strEndsWith( "", "" ): failure( "Double zero match endsWith func" ) //================================================= // Starts with - negative cases. // if strEndsWith( "", "a" ): failure( "False positive 1 endsWith func" ) if strEndsWith( "bcd", "a" ): failure( "False positive 2 endsWith func" ) if strEndsWith( "bacd", "a" ): failure( "False positive 3 endsWith func" ) if strEndsWith( "bacd", "abcd" ): failure( "False positive 4 endsWith func" ) if strEndsWith( "abcd", "abcde" ): failure( "False positive 5 endsWith func" ) //================================================= // Starts with - mixed case positive cases. // if not strEndsWith( "abc", "C", true ): failure( "Minimal match mixed 1 endsWith func" ) if not strEndsWith( "Abc", "c", true ): failure( "Minimal match mixed 2 endsWith func" ) if not strEndsWith( "AbC", "bC", true ): failure( "Double match mixed endsWith func" ) if not strEndsWith( "aBc", "AbC", true ): failure( "Complete match mixed endsWith func" ) if not strEndsWith( "abc", "", true ): failure( "Zero match endsWith func" ) if not strEndsWith( "", "", true ): failure( "Double zero match endsWith func" ) //================================================= // METHOD Starts with - positive cases. //================================================= //================================================= // Starts with - positive cases. // if not "abc".endsWith( "c" ): failure( "Minimal match endsWith method" ) if not "abc".endsWith( "bc" ): failure( "Double match endsWith method" ) if not "abc".endsWith( "abc" ): failure( "Complete match endsWith method" ) if not "abc".endsWith( "" ): failure( "Zero match endsWith method" ) if not "".endsWith( "" ): failure( "Double zero match endsWith method" ) //================================================= // Starts with - negative cases. // if "".endsWith( "a" ): failure( "False positive 1 endsWith method" ) if "bcd".endsWith( "a" ): failure( "False positive 2 endsWith method" ) if "bacd".endsWith( "a" ): failure( "False positive 3 endsWith method" ) if "bacd".endsWith( "abcd" ): failure( "False positive 4 endsWith method" ) if "abcd".endsWith( "abcde" ): failure( "False positive 5 endsWith method" ) //================================================= // Starts with - mixed case positive cases. // if not "abc".endsWith( "C", true ): failure( "Minimal match mixed 1 endsWith method" ) if not "abc".endsWith( "c", true ): failure( "Minimal match mixed 2 endsWith method" ) if not "abc".endsWith( "bC", true ): failure( "Double match mixed endsWith method" ) if not "abc".endsWith( "AbC", true ): failure( "Complete match mixed endsWith method" ) if not "abc".endsWith( "", true ): failure( "Zero match endsWith method" ) if not "".endsWith( "", true ): failure( "Double zero match endsWith method" ) //================================================= // Parameter error control // try strEndsWith( "abc", 88, true ) failure( "Error not rised for param error in function 1" ) catch ParamError end try strEndsWith( 88, "abc", true ) failure( "Error not rised for param error in function 2" ) catch ParamError end try "abc".endsWith( 88, true ) failure( "Error not rised for param error in method" ) catch ParamError end success() tests/core/testsuite/strFind.fal000066400000000000000000000042771176363201700173160ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101c * Category: rtl * Subcategory: string * Short: strFind * Description: * Test on RTL string functions * [/Description] * ****************************************************************************/ string = "this is a string" pos = strFind( string, "a" ) if pos != 8: failure( "strFind, single char" ) pos = strFind( string, "a str" ) if pos != 8: failure( "strFind, string" ) pos = strFind( string, "s", 6 ) if pos != 6: failure( "strFind, char from" ) pos = strFind( string, "s", 6, 12 ) if pos != 6: failure( "strFind, char from-to" ) pos = strFind( string, "str", 5, 13 ) if pos != 10: failure( "strFind, string from-to" ) pos = strFind( string, "s", 13 ) if pos != -1: failure( "strFind, negative" ) pos = strBackFind( string, "t" ) if pos != 11: failure( "strBackFind, single char" ) pos = strBackFind( string, "a str" ) if pos != 8: failure( "strBackFind, string" ) pos = strBackFind( string, "s", 12 ) if pos != -1: failure( "strBackFind, char from" ) pos = strBackFind( string, "s", 6, 12 ) if pos != 10: failure( "strBackFind, char from-to" ) pos = strBackFind( string, "asdf" ) if pos != -1: failure( "strBackFind, negative" ) //=========================================== // Methodic version // pos = string.find( "a" ) if pos != 8: failure( "string.find, single char" ) pos = string.find( "a str" ) if pos != 8: failure( "string.find, string" ) pos = string.find( "s", 6 ) if pos != 6: failure( "string.find, char from" ) pos = string.find( "s", 6, 12 ) if pos != 6: failure( "string.find, char from-to" ) pos = string.find( "str", 5, 13 ) if pos != 10: failure( "string.find, string from-to" ) pos = string.find( "s", 13 ) if pos != -1: failure( "string.find, negative" ) pos = string.rfind( "t" ) if pos != 11: failure( "string.rfind, single char" ) pos = string.rfind( "a str" ) if pos != 8: failure( "string.rfind, string" ) pos = string.rfind( "s", 12 ) if pos != -1: failure( "string.rfind, char from" ) pos = string.rfind( "s", 6, 12 ) if pos != 10: failure( "string.rfind, char from-to" ) pos = string.rfind( "asdf" ) if pos != -1: failure( "string.rfind, negative" ) success() /* End of file */ tests/core/testsuite/strHead.fal000066400000000000000000000010251176363201700172630ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101d * Category: rtl * Subcategory: string * Short: strHead and strTail * Description: * Test on RTL string functions * [/Description] * ****************************************************************************/ string = "this is a string" substr = strFront( string, 4 ) if substr != "this": failure( "strHead" ) substr = strBack( string, 6 ) if substr != "string": failure( "strTail" ) success() /* End of file */ tests/core/testsuite/strJoin.fal000066400000000000000000000021021176363201700173160ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101o * Category: rtl * Subcategory: string * Short: join on strings method * Description: * Test on String.join method. * [/Description] * ****************************************************************************/ if "".join() != "": failure( "Full empty join" ) if ",".join() !="": failure( "Void join" ) if "".join("a word") != "a word": failure( "Join single word" ) if "".join( 1 ) != "1": failure( "Join toString" ) if "x".join( 640, 480 ) != "640x480": failure( "Join with a string" ) if "".join( 640, 480 ) != "640480": failure( "Join without strings" ) if " tok ".join( "single" ) != "single": failure( "Join single token" ) if " tok ".join( "alpha", 1, 2, "beta" ) != \ "alpha tok 1 tok 2 tok beta": failure( "join token" ) // Nmultibyte if ",".join( "安心", "しました" ) != "安心,しました": failure( "Join multi mixed" ) if "、".join( "安心", "しました" ) != "安心、しました": failure( "Join multi pure" ) success() /* End of file */ tests/core/testsuite/strMerge.fal000066400000000000000000000022331176363201700174630ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101b * Category: rtl * Subcategory: string * Short: strMerge * Description: * Test on RTL string functions. * Testing here strMerge functionalities. * [/Description] * ****************************************************************************/ strarr = [ "this", "is", "a", "string" ] strres = strMerge( strarr ) if strres != "thisisastring": failure( "strMerge single param" ) strres = strMerge( strarr, "," ) if strres != "this,is,a,string": failure( "strMerge" ) strres = strMerge( strarr, ",", 3 ) if strres != "this,is,a": failure( "strMerge limited" ) strres = strMerge( strarr, ",", 1 ) if strres != "this": failure( "strMerge limited to 1" ) strres = strMerge( strarr, ",", 0 ) if strres != "": failure( "strMerge limited to 0" ) strres = strMerge( strarr, ",", 10 ) if strres != "this,is,a,string": failure( "strMerge overlimited" ) strres = strMerge( [] ) if strres != "": failure( "strMerge with empty array" ) strres = strMerge( [], ",", 10 ) if strres != "": failure( "strMerge with empty array overlimited" ) success() /* End of file */ tests/core/testsuite/strMerge2.fal000066400000000000000000000021541176363201700175470ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101p * Category: rtl * Subcategory: string * Short: merge method on strings * Description: * Test on String.merge method. * [/Description] * ****************************************************************************/ if "".merge([]) != "": failure( "Full empty merge" ) if ",".merge([]) !="": failure( "Void merge" ) if "".merge(["a word"]) != "a word": failure( "merge single word" ) if "".merge( [1] ) != "1": failure( "merge toString" ) if "x".merge( [640, 480] ) != "640x480": failure( "merge with a string" ) if "".merge( [640, 480] ) != "640480": failure( "merge without strings" ) if " tok ".merge( ["single"] ) != "single": failure( "merge single token" ) if " tok ".merge( ["alpha", 1, 2, "beta"] ) != \ "alpha tok 1 tok 2 tok beta": failure( "merge token" ) // Multibyte if ",".merge( ["安心", "しました"] ) != "安心,しました": failure( "merge multi mixed" ) if "、".merge( ["安心", "しました"] ) != "安心、しました": failure( "merge multi pure" ) success() /* End of file */ tests/core/testsuite/strReplace.fal000066400000000000000000000015711176363201700200030ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101f * Category: rtl * Subcategory: string * Short: strReplace * Description: * Test on RTL string functions * [/Description] * ****************************************************************************/ string = "element with this" strrep = strReplace( string, "e", "a" ) if strrep != "alamant with this": failure( "strReplace, single with single" ) strrep = strReplace( string, "e", "al" ) if strrep != "allalmalnt with this": failure( "strReplace, single with string" ) strrep = strReplace( string, "th", "x" ) if strrep != "element wix xis": failure( "strReplace, string with single" ) strrep = strReplace( string, "th", "'another long'" ) if strrep != "element wi'another long' 'another long'is": failure( "strReplace, string with string" ) success() /* End of file */ tests/core/testsuite/strReplicate.fal000066400000000000000000000010571176363201700203370ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101g * Category: rtl * Subcategory: string * Short: strReplicate * Description: * Test on RTL string functions * string replicate * [/Description] * ****************************************************************************/ strrep = strReplicate( "a", 5 ) if strrep != "aaaaa": failure( "strReplicate single char" ) strrep = strReplicate( "abc ", 3 ) if strrep != "abc abc abc ": failure( "strReplicate string" ) success() /* End of file */ tests/core/testsuite/strSplit.fal000066400000000000000000000120001176363201700175100ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101a * Category: rtl * Subcategory: string * Short: strSplit * Description: * Test on RTL string functions. * Testing here strSplit function capability * [/Description] * ****************************************************************************/ string = "this is a string" strarr = strSplit( string, " " ) if len(strarr) != 4: failure( "Strsplit base" ) if strarr[0] != "this": failure( "Strsplit part 0" ) if strarr[1] != "is": failure( "Strsplit part 1" ) if strarr[2] != "a": failure( "Strsplit part 2" ) if strarr[3] != "string": failure( "Strsplit parts 3" ) strarr2 = strSplit( string, " ", 2 ) if len( strarr2 ) != 2: failure( "Limited split" ) strarr3 = strSplit( string, " ", 100 ) if len( strarr3 ) != 4: failure( "Overlimited split - size" ) if strarr3[0] != "this": failure( "Overlimited split - content 0" ) if strarr3[1] != "is": failure( "Overlimited split - content 1" ) if strarr3[2] != "a": failure( "Overlimited split - content 2" ) if strarr3[3] != "string": failure( "Overlimited split - content 3" ) strarr4 = strSplit( string, "zka" ) if len( strarr4 ) != 1: failure( "Negative split - size" ) if strarr4[0] != string: failure( "Negative split - content" ) strarr5 = strSplit( "fields;;here", ";" ) if len( strarr5 ) != 3: failure( "Adjacent fields considered - lenght" ) if strarr5[0] != "fields": failure( "Adjacent fields considered - first" ) if strarr5[1] != "": failure( "Adjacent fields considered - second" ) if strarr5[2] != "here": failure( "Adjacent fields considered - third" ) strarr6 = strSplit( ":", ":" ) if len( strarr6 ) != 2: failure( "Positive empty split" ) if strarr6[0] != "" or strarr6[1] != "": failure( "Positive empty split - content" ) strarr6 = strSplitTrimmed( "fields here", " " ) if len( strarr6 ) != 2: failure( "Adjacent fields ignored - lenght" ) if strarr6[0] != "fields": failure( "Adjacent fields ignored - first" ) if strarr6[1] != "here": failure( "Adjacent fields ignored - second" ) strarr7 = strSplit( "final;one;c", ";" ) if strarr7[0] != "final": failure( "Final one char - first" ) if strarr7[1] != "one": failure( "Final one char - second" ) if strarr7[2] != "c": failure( "Final one char - third" ) strarr7 = strSplitTrimmed( "final;one;c", ";" ) if strarr7[0] != "final": failure( "Final one char (trimmed) - first" ) if strarr7[1] != "one": failure( "Final one char (trimmed) - second" ) if strarr7[2] != "c": failure( "Final one char (trimmed) - third" ) strarr7 = strSplit( "final;one;", ";" ) if len( strarr7 ) != 3: failure( "Final field missing - size" ) if strarr7[0] != "final": failure( "Final field missing - first" ) if strarr7[1] != "one": failure( "Final field missing - second" ) if strarr7[2] != "": failure( "Final field missing - empty (third)" ) strarr7 = strSplitTrimmed( "final;one;", ";" ) if len( strarr7 ) != 3: failure( "Final field missing (trimmed) - size" ) if strarr7[0] != "final": failure( "Final field missing (trimmed) - first" ) if strarr7[1] != "one": failure( "Final field missing (trimmed) - second" ) if strarr7[2] != "": failure( "Final field missing (trimmed) - empty (third)" ) strarr7 = strSplit( "final;one;;", ";" ) if len( strarr7 ) != 4: failure( "Two final field missing - size" ) if strarr7[0] != "final": failure( "Two final missing - first" ) if strarr7[1] != "one": failure( "Two final missing - second" ) if strarr7[2] != "": failure( "Two final missing - empty (third)" ) if strarr7[3] != "": failure( "Two final missing - empty (fourth)" ) strarr7 = strSplitTrimmed( "final;one;;", ";" ) if len( strarr7 ) != 3: failure( "Two final field missing (trimmed) - size" ) if strarr7[0] != "final": failure( "Two final field missing (trimmed) - first" ) if strarr7[1] != "one": failure( "Two final field missing (trimmed) - second" ) if strarr7[2] != "": failure( "Two final field missing (trimmed) - empty (third)" ) //split empty strings strarr7 = strSplit( "", ";" ) if len( strarr7 ) != 1: failure( "empty split - size" ) if strarr7[0] != "": failure( "empty split - content" ) strarr7 = strSplitTrimmed( "", ";" ) if len( strarr7 ) != 1: failure( "empty split (trimmed) - size" ) if strarr7[0] != "": failure( "empty split (trimmed) - content" ) //Shorter split strarr7 = strSplit( "aaa", ";;;;;;" ) if len( strarr7 ) != 1: failure( "shorter split - size" ) if strarr7[0] != "aaa": failure( "shorter split - content" ) strarr7 = strSplitTrimmed( "aaa", ";;;;;;" ) if len( strarr7 ) != 1: failure( "shorter split (trimmed) - size" ) if strarr7[0] != "aaa": failure( "shorter split (trimmed) - content" ) strarr7 = strSplitTrimmed( "*****abc*****def*****ghi*****", "*" ) // != array works only since falcon 0.9.6 if strarr7 != ["", "abc", "def", "ghi", ""]: failure( "split trimmed with multiple trims") strarr7 = strSplitTrimmed( "a*****abc*****def*****ghi*****di*di1", "*" ) // != array works only since falcon 0.9.6 if strarr7 != ["a", "abc", "def", "ghi", "di", "di1"]: failure( "split trimmed with multiple trims - ending") success() /* End of file */ tests/core/testsuite/strStartsWith.fal000066400000000000000000000100321176363201700205340ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101q * Category: rtl * Subcategory: string * Short: StartsWith * Description: * Testing the startsWith function and method. * [/Description] * ****************************************************************************/ //================================================= // Starts with - positive cases. // if not strStartsWith( "abc", "a" ): failure( "Minimal match startsWith func" ) if not strStartsWith( "abc", "ab" ): failure( "Double match startsWith func" ) if not strStartsWith( "abc", "abc" ): failure( "Complete match startsWith func" ) if not strStartsWith( "abc", "" ): failure( "Zero match startsWith func" ) if not strStartsWith( "", "" ): failure( "Double zero match startsWith func" ) //================================================= // Starts with - negative cases. // if strStartsWith( "", "a" ): failure( "False positive 1 startsWith func" ) if strStartsWith( "bcd", "a" ): failure( "False positive 2 startsWith func" ) if strStartsWith( "bacd", "a" ): failure( "False positive 3 startsWith func" ) if strStartsWith( "bacd", "abcd" ): failure( "False positive 4 startsWith func" ) if strStartsWith( "abcd", "abcde" ): failure( "False positive 5 startsWith func" ) //================================================= // Starts with - mixed case positive cases. // if not strStartsWith( "abc", "A", true ): failure( "Minimal match mixed 1 startsWith func" ) if not strStartsWith( "Abc", "a", true ): failure( "Minimal match mixed 2 startsWith func" ) if not strStartsWith( "Abc", "aB", true ): failure( "Double match mixed startsWith func" ) if not strStartsWith( "aBc", "AbC", true ): failure( "Complete match mixed startsWith func" ) if not strStartsWith( "abc", "", true ): failure( "Zero match startsWith func" ) if not strStartsWith( "", "", true ): failure( "Double zero match startsWith func" ) //================================================= // METHOD Starts with - positive cases. //================================================= //================================================= // Starts with - positive cases. // if not "abc".startsWith( "a" ): failure( "Minimal match startsWith method" ) if not "abc".startsWith( "ab" ): failure( "Double match startsWith method" ) if not "abc".startsWith( "abc" ): failure( "Complete match startsWith method" ) if not "abc".startsWith( "" ): failure( "Zero match startsWith method" ) if not "".startsWith( "" ): failure( "Double zero match startsWith method" ) //================================================= // Starts with - negative cases. // if "".startsWith( "a" ): failure( "False positive 1 startsWith method" ) if "bcd".startsWith( "a" ): failure( "False positive 2 startsWith method" ) if "bacd".startsWith( "a" ): failure( "False positive 3 startsWith method" ) if "bacd".startsWith( "abcd" ): failure( "False positive 4 startsWith method" ) if "abcd".startsWith( "abcde" ): failure( "False positive 5 startsWith method" ) //================================================= // Starts with - mixed case positive cases. // if not "abc".startsWith( "A", true ): failure( "Minimal match mixed 1 startsWith method" ) if not "abc".startsWith( "a", true ): failure( "Minimal match mixed 2 startsWith method" ) if not "abc".startsWith( "aB", true ): failure( "Double match mixed startsWith method" ) if not "abc".startsWith( "AbC", true ): failure( "Complete match mixed startsWith method" ) if not "abc".startsWith( "", true ): failure( "Zero match startsWith method" ) if not "".startsWith( "", true ): failure( "Double zero match startsWith method" ) //================================================= // Parameter error control // try strStartsWith( "abc", 88, true ) failure( "Error not rised for param error in function 1" ) catch ParamError end try strStartsWith( 88, "abc", true ) failure( "Error not rised for param error in function 2" ) catch ParamError end try "abc".startsWith( 88, true ) failure( "Error not rised for param error in method" ) catch ParamError end success() tests/core/testsuite/strTrim.fal000066400000000000000000000042221176363201700173370ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101e * Category: rtl * Subcategory: string * Short: Trim * Description: * Test on RTL string functions * [/Description] * ****************************************************************************/ spaced = " a spaced string " nonspaced = "non spaced" if strBackTrim( spaced ) != " a spaced string": failure( "strBackTrim" ) if strFrontTrim( spaced ) != "a spaced string ": failure( "strFrontTrim" ) if strTrim( spaced ) != "a spaced string": failure( "strTrim" ) if spaced.rtrim() != " a spaced string": failure( ".rtrim" ) if spaced.ftrim() != "a spaced string ": failure( ".ftrim" ) if spaced.trim() != "a spaced string": failure( ".trim" ) spaced = " \t a spaced string \t " if strBackTrim( spaced ) != " \t a spaced string": failure( "strBackTrim 2" ) if strFrontTrim( spaced ) != "a spaced string \t ": failure( "strFrontTrim 2" ) if strTrim( spaced ) != "a spaced string": failure( "strTrim 2" ) if spaced.rtrim() != " \t a spaced string": failure( ".rtrim 2" ) if spaced.ftrim() != "a spaced string \t ": failure( ".ftrim 2" ) if spaced.trim() != "a spaced string": failure( ".trim 2" ) if strBackTrim( nonspaced ) != "non spaced": failure( "strBackTrim nonspaced" ) if strFrontTrim( nonspaced ) != "non spaced": failure( "strFrontTrim nonspaced" ) if strTrim( nonspaced ) != "non spaced": failure( "strTrim nonspaced" ) if nonspaced.rtrim() != "non spaced": failure( ".rtrim nonspaced" ) if nonspaced.ftrim() != "non spaced": failure( ".ftrim nonspaced" ) if nonspaced.trim() != "non spaced": failure( ".trim nonspaced" ) other = "54321 hello world 12345" if strBackTrim( other, "12345 " ) != "54321 hello world": failure( "strBackTrim other" ) if strFrontTrim( other, "12345 ") != "hello world 12345": failure( "strFrontTrim other" ) if strTrim( other, "12345 " ) != "hello world": failure( "strTrim other" ) if other.rtrim( "12345 " ) != "54321 hello world": failure( "other.rtrim" ) if other.ftrim( "12345 ") != "hello world 12345": failure( "other.ftrim" ) if other.trim( "12345 " ) != "hello world": failure( "other.trim" ) success() /* End of file */ tests/core/testsuite/strWideMerge.fal000066400000000000000000000021741176363201700203000ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101b * Category: rtl * Subcategory: string * Short: strMerge - Wide string * Description: * Test on RTL string functions. * This test strMerge when operating on international/wide strings. * * [/Description] * ****************************************************************************/ strarr = [ "これは", "国際な", "string", "ですよ!" ] strres = strMerge( strarr ) if strres != "これは国際なstringですよ!": failure( "strMerge single param" ) strres = strMerge( strarr, "," ) if strres != "これは,国際な,string,ですよ!": failure( "strMerge" ) strres = strMerge( strarr, ",", 3 ) if strres != "これは,国際な,string": failure( "strMerge limited" ) strres = strMerge( strarr, ",", 1 ) if strres != "これは": failure( "strMerge limited to 1" ) strres = strMerge( strarr, ",", 0 ) if strres != "": failure( "strMerge limited to 0" ) strres = strMerge( strarr, ",", 10 ) if strres != "これは,国際な,string,ですよ!": failure( "strMerge overlimited" ) success() /* End of file */ tests/core/testsuite/strWideReplace.fal000066400000000000000000000020311176363201700206040ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101j * Category: rtl * Subcategory: string * Short: strReplace wide * Description: * Test on RTL string functions - replace string in wide characters. * [/Description] * ****************************************************************************/ string = "これは internationalなストリング" strrep = strReplace( string, "ス", "ワ" ) if strrep != "これは internationalなワトリング": failure( "strReplace, single with single" ) strrep = strReplace( string, "ス", "ワヲ" ) if strrep != "これは internationalなワヲトリング": failure( "strReplace, single with string" ) strrep = strReplace( string, "na", "x" ) if strrep != "これは interxtioxlなストリング": failure( "strReplace, string with single" ) strrep = strReplace( string, "これ", "'another long'" ) if strrep != "'another long'は internationalなストリング": failure( "strReplace, string with string" ) success() /* End of file */ tests/core/testsuite/strWideReplicate.fal000066400000000000000000000011631176363201700211460ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101k * Category: rtl * Subcategory: string * Short: strReplicate wide * Description: * Test on RTL string functions - string replicate, wide characters * [/Description] * ****************************************************************************/ strrep = strReplicate( "安", 5 ) if strrep != "安安安安安": failure( "strReplicate single char" ) strrep = strReplicate( "安心した ", 3 ) if strrep != "安心した 安心した 安心した ": failure( "strReplicate string" ) success() /* End of file */ tests/core/testsuite/strWideSplit.fal000066400000000000000000000035031176363201700203310ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101m * Category: rtl * Subcategory: string * Short: strSplit Wide * Description: * Test on RTL string functions - strSplit with wide strings * [/Description] * ****************************************************************************/ string = "これは 国際な string ですよ!" strarr = strSplit( string, " " ) if len(strarr) != 4: failure( "Strsplit base" ) if strarr[0] != "これは": failure( "Strsplit part 0" ) if strarr[1] != "国際な": failure( "Strsplit part 1" ) // notice; this will compare a wide string with a short one! if strarr[2] != "string": failure( "Strsplit part 2 - or long/short comparation" ) if strarr[3] != "ですよ!": failure( "Strsplit part 3" ) strarr2 = strSplit( string, " ", 2 ) if len( strarr2 ) != 2: failure( "Limited split" ) if strarr[0] != "これは": failure( "Limited Strsplit part 0" ) if strarr[1] != "国際な": failure( "Limited Strsplit part 1" ) strarr3 = strSplit( string, " ", 100 ) if len( strarr3 ) != 4: failure( "Overlimited split - size" ) if strarr3[0] != "これは": failure( "Overlimited split - content 0" ) if strarr3[1] != "国際な": failure( "Overlimited split - content 1" ) if strarr3[2] != "string": failure( "Overlimited split - content 2" ) if strarr3[3] != "ですよ!": failure( "Overlimited split - content 3" ) strarr3 = strSplit( string, "国際な" ) if len( strarr3 ) != 2: failure( "Complex split - size" ) if strarr3[0] != "これは ": failure( "Complex split - content 0" ) if strarr3[1] != " string ですよ!": failure( "Complex split - content 1" ) strarr4 = strSplit( string, "zka" ) if len( strarr4 ) != 1: failure( "Negative split - size" ) if strarr4[0] != string: failure( "Negative split - content" ) success() /* End of file */ tests/core/testsuite/strWildcardMatch.fal000066400000000000000000000012531176363201700211330ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 101n * Category: rtl * Subcategory: string * Short: strWildcardMatch * Description: * Test on RTL string functions * [/Description] * ****************************************************************************/ string = "this is a string" if not strWildcardMatch( string, "this is*" ): failure( "match end" ) if not strWildcardMatch( string, "*a string" ): failure( "match begining" ) if not strWildcardMatch( string, "this*string" ): failure( "match middle" ) if strWildcardMatch( ".svn", "*.fal" ): failure( "partial match then fail" ) success() /* End of file */ tests/core/testsuite/stream_grabline.fal000066400000000000000000000031001176363201700210230ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 130b * Category: rtl * Subcategory: stream * Short: Grabline compliance. * Description: * Grabline is a tricky function. This test cheks if it: * 1) correctly reads empty lines. * 2) correctly returns the last line when it meets an EOF. * 3) does not return an extra line when meeting an EOF right after EOL. * 4) returns an oob(0) at end as expected. * [/Description] * ****************************************************************************/ //===================== // Test on last line truncated // s = StringStream( ' First line Third line') lines = [] while (line = s.grabLine()) != 0 lines += line end // was the last line an oob(0) ? if not isoob( line ): failure( "Last return not oob" ) if lines.len() != 3: failure( "EOF on the last line - count" ) if lines[0] != "First line": failure( "Content, first line" ) if lines[1] != "": failure( "Content, empty line" ) if lines[2] != "Third line": failure( "Content, last line" ) //===================== // Test on EOL+EOF // s = StringStream( ' First line Third line ') lines = [] while (line = s.grabLine()) != 0 lines += line end // was the last line an oob(0) ? if not isoob( line ): failure( "EOL+EOF - Last return not oob" ) if lines.len() != 3: failure( "EOL+EOF - count" ) if lines[0] != "First line": failure( "EOL+EOF - Content, first line" ) if lines[1] != "": failure( "EOL+EOF - Content, empty line" ) if lines[2] != "Third line": failure( "EOL+EOF - Content, last line" ) /* end of test */ tests/core/testsuite/stream_readline.fal000066400000000000000000000023041176363201700210300ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 130a * Category: rtl * Subcategory: stream * Short: Readline compliance. * Description: * Readline is a tricky function. This test cheks if it: * 1) correctly reads empty lines. * 2) correctly returns the last line when it meets an EOF. * 3) does not return an extra line when meeting an EOF right after EOL * [/Description] * ****************************************************************************/ line = "" //================================= // Test for empty line // s = StringStream( ' First line Third line') count = 0 while s.readLine( line, 1024 ) if ++count == 2 if line != "": failure( "Empty line" ) end end if count != 3: failure( "last line" ) //================================= // Test for EOL + EOF // s = StringStream( ' First line Third line ') lines = [] while s.readLine( line, 1024 ) lines += line end if lines.len() != 3: failure( "EOL + EOF" ) if lines[0] != "First line": failure( "Content, first line" ) if lines[1] != "": failure( "Content, empty line" ) if lines[2] != "Third line": failure( "Content, last line" ) success() /* end of test */ tests/core/testsuite/string_change.fal000066400000000000000000000116131176363201700205100ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 10g * Category: types * Subcategory: strings * Short: String ranged substitutions. * Description: * Verify that substitutions performed on string with ranges are correct. * This test only checks for positive ranges. * [/Description] * ****************************************************************************/ string = "01234567" // string change 1 item string[3:4] = "a" if len( string ) != 8 : failure( "item - len" ) if string[2] != "2" or string[4] != "4": failure( "item - surround" ) if string[3] != "a": failure( "item - item" ) //========================================= // Substitute with one item // // string change more items - middle string = "01234567" string[3:6] = "a" if len( string ) != 6 : failure( "more items - len" ) if string[2] != "2" or string[4] != "6": failure( "more items - surround" ) if string[3] != "a": failure( "more items - item" ) string = "01234567" string[5:3] = "a" if len( string ) != 6: failure( "more items reverse - len" ) if string[2] != "2" or string[4] != "6": failure( "more items reverse - surround" ) if string[3] != "a": failure( "more items reverse - item" ) // string change more items - begin string = "01234567" string[0:3] = "a" if len( string ) != 6 : failure( "more items (begin) - len" ) if string[1] != "3": failure( "more items (begin) - surround" ) if string[0] != "a": failure( "more items (begin) - item" ) string = "01234567" string[2:0] = "a" if len( string ) != 6 : failure( "more items (begin) reverse - len" ) if string[1] != "3": failure( "more items (begin) reverse - surround" ) if string[0] != "a": failure( "more items (begin) reverse - item" ) // string change more items - end string = "01234567" string[6:] = "a" if len( string ) != 7 : failure( "more items (end) - len" ) if string[5] != "5": failure( "more items (end) - surround" ) if string[6] != "a": failure( "more items (end) - item" ) //========================================= // Substitute with a shorter string // // in the middle string = "01234567" string[2:6] = "ab" if len( string ) != 6 : failure( "smaller string - len" ) if string[1] != "1" or string[4] != "6": failure( "smaller string - surround" ) if string[2] != "a" or string[3] != "b": failure( "smaller string - items" ) string = "01234567" string[5:2] = "ab" if len( string ) != 6 : failure( "smaller string reverse - len" ) if string[1] != "1" or string[4] != "6": failure( "smaller string reverse - surround" ) if string[2] != "a" or string[3] != "b": failure( "smaller string reverse - items" ) // at begin the middle string = "01234567" string[0:4] = "ab" if len( string ) != 6 : failure( "smaller string (begin) - len" ) if string[2] != "4": failure( "smaller string (begin) - surround" ) if string[0] != "a" or string[1] != "b": failure( "smaller string (begin) - items" ) string = "01234567" string[3:0] = "ab" if len( string ) != 6 : failure( "smaller string (begin) reverse - len" ) if string[2] != "4": failure( "smaller string (begin) reverse - surround" ) if string[0] != "a" or string[1] != "b": failure( "smaller string (begin) reverse - items" ) // at begin the middle string = "01234567" string[4:] = "ab" if len( string ) != 6 : failure( "smaller string (end) - len" ) if string[3] != "3": failure( "smaller string (end) - surround" ) if string[4] != "a" or string[5] != "b": failure( "smaller string (end) - items" ) //========================================= // Substitute with a larger string // // in the middle string = "01234567" string[2:4] = "abc" if len( string ) != 9 : failure( "larger string - len" ) if string[1] != "1" or string[6] != "5": failure( "Range larger smaller string - surround" ) if string[2] != "a" or string[4] != "c": failure( "larger string - items" ) string = "01234567" string[3:2] = "abc" if len( string ) != 9 : failure( "larger string reverse - len" ) if string[1] != "1" or string[6] != "5": failure( "Range larger smaller string reverse - surround" ) if string[2] != "a" or string[4] != "c": failure( "larger string reverse - items" ) // at begin the middle string = "01234567" string[0:2] = "abc" if len( string ) != 9 : failure( "larger string (begin) - len" ) if string[3] != "2": failure( "larger string (begin) - surround" ) if string[0] != "a" or string[2] != "c": failure( "larger string (begin) - items" ) string = "01234567" string[1:0] = "abc" if len( string ) != 9 : failure( "larger string (begin) reverse - len" ) if string[3] != "2": failure( "larger string (begin) reverse - surround" ) if string[0] != "a" or string[2] != "c": failure( "larger string (begin) reverse - items" ) // at begin the middle string = "01234567" string[6:] = "abc" if len( string ) != 9 : failure( "larger string (end) - len" ) if string[5] != "5": failure( "larger string (end) - surround" ) if string[6] != "a" or string[8] != "c": failure( "larger string (end) - items" ) success() /* End of file */ tests/core/testsuite/string_change_neg.fal000066400000000000000000000116531176363201700213450ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 10h * Category: types * Subcategory: strings * Short: String negative ranged substitutions. * Description: * Verify that substitutions performed on string with ranges are correct. * This test only checks for negative ranges. * [/Description] * ****************************************************************************/ string = "01234567" // string change 1 item string[-5:-4] = "a" if len( string ) != 8 : failure( "item - len" ) if string[2] != "2" or string[4] != "4": failure( "item - surround" ) if string[3] != "a": failure( "item - item" ) //========================================= // Substitute with one item // // string change more items - middle string = "01234567" string[-5:-2] = "a" if len( string ) != 6 : failure( "more items - len" ) if string[2] != "2" or string[4] != "6": failure( "more items - surround" ) if string[3] != "a": failure( "more items - item" ) string = "01234567" string[-3:-5] = "a" if len( string ) != 6: failure( "more items reverse - len" ) if string[2] != "2" or string[4] != "6": failure( "more items reverse - surround" ) if string[3] != "a": failure( "more items reverse - item" ) // string change more items - begin string = "01234567" string[0:-5] = "a" if len( string ) != 6 : failure( "more items (begin) - len" ) if string[1] != "3": failure( "more items (begin) - surround" ) if string[0] != "a": failure( "more items (begin) - item" ) string = "01234567" string[-6:0] = "a" if len( string ) != 6 : failure( "more items (begin) reverse - len" ) if string[1] != "3": failure( "more items (begin) reverse - surround" ) if string[0] != "a": failure( "more items (begin) reverse - item" ) // string change more items - end string = "01234567" string[-2:] = "a" if len( string ) != 7 : failure( "more items (end) - len" ) if string[5] != "5": failure( "more items (end) - surround" ) if string[6] != "a": failure( "more items (end) - item" ) //========================================= // Substitute with a shorter string // // in the middle string = "01234567" string[-6:-2] = "ab" if len( string ) != 6 : failure( "smaller string - len" ) if string[1] != "1" or string[4] != "6": failure( "smaller string - surround" ) if string[2] != "a" or string[3] != "b": failure( "smaller string - items" ) string = "01234567" string[-3:-6] = "ab" if len( string ) != 6 : failure( "smaller string reverse - len" ) if string[1] != "1" or string[4] != "6": failure( "smaller string reverse - surround" ) if string[2] != "a" or string[3] != "b": failure( "smaller string reverse - items" ) // at begin the middle string = "01234567" string[0:-4] = "ab" if len( string ) != 6 : failure( "smaller string (begin) - len" ) if string[2] != "4": failure( "smaller string (begin) - surround" ) if string[0] != "a" or string[1] != "b": failure( "smaller string (begin) - items" ) string = "01234567" string[-5:0] = "ab" if len( string ) != 6 : failure( "smaller string (begin) reverse - len" ) if string[2] != "4": failure( "smaller string (begin) reverse - surround" ) if string[0] != "a" or string[1] != "b": failure( "smaller string (begin) reverse - items" ) // at begin the middle string = "01234567" string[-4:] = "ab" if len( string ) != 6 : failure( "smaller string (end) - len" ) if string[3] != "3": failure( "smaller string (end) - surround" ) if string[4] != "a" or string[5] != "b": failure( "smaller string (end) - items" ) //========================================= // Substitute with a larger string // // in the middle string = "01234567" string[-6:-4] = "abc" if len( string ) != 9 : failure( "larger string - len" ) if string[1] != "1" or string[6] != "5": failure( "Range larger smaller string - surround" ) if string[2] != "a" or string[4] != "c": failure( "larger string - items" ) string = "01234567" string[-5:-6] = "abc" if len( string ) != 9 : failure( "larger string reverse - len" ) if string[1] != "1" or string[6] != "5": failure( "Range larger smaller string reverse - surround" ) if string[2] != "a" or string[4] != "c": failure( "larger string reverse - items" ) // at begin the middle string = "01234567" string[0:-6] = "abc" if len( string ) != 9 : failure( "larger string (begin) - len" ) if string[3] != "2": failure( "larger string (begin) - surround" ) if string[0] != "a" or string[2] != "c": failure( "larger string (begin) - items" ) string = "01234567" string[-7:0] = "abc" if len( string ) != 9 : failure( "larger string (begin) reverse - len" ) if string[3] != "2": failure( "larger string (begin) reverse - surround" ) if string[0] != "a" or string[2] != "c": failure( "larger string (begin) reverse - items" ) // at begin the middle string = "01234567" string[-2:] = "abc" if len( string ) != 9 : failure( "larger string (end) - len" ) if string[5] != "5": failure( "larger string (end) - surround" ) if string[6] != "a" or string[8] != "c": failure( "larger string (end) - items" ) success() /* End of file */ tests/core/testsuite/string_rng.fal000066400000000000000000000051101176363201700200440ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 10f * Category: types * Subcategory: strings * Short: Ranged string access. * Description: * Check ranged subscription access to strings. * This check is complete of positive and negative ranged tests. * [/Description] * ****************************************************************************/ string = "01234567" // get an empty range str = string[0:0] if len( str ) != 0: failure( "Get Empty range" ) // Get whole open range. str = string[0:] if len( str ) != 8: failure( "Get Full range - len" ) for i in [0:len(str)] if str[i] != string[i]: failure( "Get Full range - item " + i ) end // direct range -- 1 item str = string[2:3] if len( str ) != 1: failure( "Get 1 item - len" ) if str[0] != "2": failure( "Get 1 item - value" ) // direct range -- more items str = string[2:5] if len( str ) != 3: failure( "Get more elements - len" ) if str[0] != "2" or str[-1] != "4": failure( "Get more elements - value" ) // Reverse range -- full str = string[-1:0] if len( str ) != 8: failure( "Reverse range full - len" ) for i in [0:len(str)] if str[i] != string[len(str)-i-1]: failure( "Reverse range full - item " + i ) end // Reverse range -- partial str = string[5:4] if len( str ) != 2: failure( "Reverse range - len" ) if str[0] != string[5] or str[1] != string[4]: failure( "Reverse range - items" ) // Direct range with negative index str = string[-5:-4] if len( str ) != 1: failure( "Direct range neg - len(1)" ) if str[0] != string[-5]: failure( "Dircet range - items(1)" ) // Direct range with negative index str = string[-5:-3] if len( str ) != 2: failure( "Dirct range neg - len(2)" ) if str[0] != string[-5] or str[1] != string[-4]: failure( "Dirct range - items(2)" ) // Reverse range with negative index str = string[-3:-5] if len( str ) != 3: failure( "Reverse range neg - len(3)" ) if str[0] != string[-3] or str[2] != string[-5]: failure( "Dirct range - items(3)" ) // Reverse range open str = string[-5:] if len( str ) != 5: failure( "Reverse range open - len" ) if str[4] != "7" or str[0] != "3": failure( "Reverse range open - items" ) // Reverse range open -- empty str = string[-1:] if len( str ) != 1: failure( "Reverse range open last" ) if str[0] != "7": failure( "Reverse range open last - item" ) // negative empty str = string[-5:-5] if len( str ) != 0: failure( "Negative empty" ) str = string[3:-5] if len( str ) != 0: failure( "Negative empty x:-x" ) str = string[-5:3] if len( str ) != 0: failure( "Negative empty -x:x" ) success() /* End of file */ tests/core/testsuite/string_wide.fal000066400000000000000000000011461176363201700202130ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 10e * Category: types * Subcategory: strings * Short: String widening. * Description: * Tries to widen some strings from 1 to 2 bytes representation. * * [/Description] * ****************************************************************************/ string = "abc" string[1:2] = "あばたとは、天然痘が治った後、皮膚に残る小さなくぼみ。" if string != "aあばたとは、天然痘が治った後、皮膚に残る小さなくぼみ。c" failure( "insertion" ) end success() tests/core/testsuite/stringexp.fal000066400000000000000000000015211176363201700177150ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 10d * Category: types * Subcategory: strings * Short: String expansion * Description: * Verify correcntess of string expansion system. * This is only a basic grammar test, more sophisticated tests are later on. * * [/Description] * ****************************************************************************/ var1 = "one" var2 = "two" var3 = 10 var4 = 19.388 string = "Hello $var1;" if @string != "Hello one;": failure( "First test" ) string = "Hello >$var2:5r<" if @string != "Hello > two<": failure( "Second test" ) string = "Hello >$(var3:5r)<" if @string != "Hello > 10<": failure( "Numeric test" ) string = "Hello >$(var4:6.2rd,)<" if @string != "Hello > 19,39<": failure( "Numeric comma test" ) success() /* End of file */ tests/core/testsuite/stringexp_fmt.fal000066400000000000000000000011511176363201700205620ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 34b * Category: expression * Subcategory: strex * Short: String format * Description: * Test for string expansion and format applying. * Internally, string expansion uses indirect access; so what we do here * is just checking that parsing and expansion works. * [/Description] * ****************************************************************************/ object test function toString( fmt ) if fmt != "Test Format": failure( "Test format" ) end end success() /* End of file */ tests/core/testsuite/stringexp_ind.fal000066400000000000000000000040561176363201700205550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 34a * Category: expression * Subcategory: strex * Short: String expansion * Description: * Test for basic string expansion. * Internally, string expansion uses indirect access; so what we do here * is just checking that parsing and expansion works. * [/Description] * ****************************************************************************/ item = "Hello" // immediate test if @ "$(item)" != "Hello": failure( "Basic strex immediate" ) if @ "$it" + "em" != "Hello": failure( "Basic strex immediate/precedence" ) if @ "an expanded string: $item" != "an expanded string: Hello": failure( "Basic strex tail" ) if @ "$item an expanded string" != "Hello an expanded string": failure( "Basic strex head" ) if @ "$item an expanded string $item" != "Hello an expanded string Hello": failure( "Basic strex head" ) if @ "$item an $item expanded string $item" != "Hello an Hello expanded string Hello": failure( "Basic strex triple" ) if @ "$(item) an $(item) expanded string $(item): $(item)" != \ "Hello an Hello expanded string Hello: Hello" failure( "Basic strex parenthethized" ) end // ========================================================= item = [ "one", "two", "three" ] if @ "$item[0]" != "one": failure( "Array strex 0" ) if @ "$( item [ 2 ] )" != "three": failure( "Array strex internal trim." ) object test prop = "Hi!" end if @ "$test.prop" != "Hi!": failure( "Strex object" ) if @ "$(test. prop)" != "Hi!": failure( "Strex object parenthetized" ) test.prop = [ "one", "two", "three" ] if @ "$test.prop[0]" != "one": failure( "Strex object + array access 0" ) if @ "$( test. prop [ 2 ] )" != "three": failure( "Strex parenthethized object + array access 2" ) if @ ">$( test. prop [ 0 ] )<>$( test. prop [ 2 ] )<" != ">one<>three<": failure( "Double Strex parenthethized object + array access" ) item = [ 0, test, 1 ] if @ "$( item[ 1 ]. prop [0] )" != "one": failure( "Strex array access + object indirect" ) success() /* End of file */ tests/core/testsuite/stringinv.fal000066400000000000000000000007751176363201700177270ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 10a * Category: types * Subcategory: strings * Short: String invariant * Description: * Checks for string to stay invariant after their modify. * [/Description] * ****************************************************************************/ string = "Hello world" string2 = string[0:] s1 = string s1[5] = "-" if string != string2: failure( "Original string destroyed" ) success() /* End of file */ tests/core/testsuite/strings.fal000066400000000000000000000046061176363201700173720ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 10b * Category: types * Subcategory: strings * Short: String test * Description: * Checks for correctness of basic string operations. * * This leverages on conditionals. * [/Description] * ****************************************************************************/ string = "Hello world" // Access test if string[0] != "H": failure( "Access test" ) if string[-1] != "d": failure( "Access reverse test" ) // range access if string[0:5] != "Hello": failure( "Range access positive definite" ) if string[6:] != "world": failure( "Range access positive high-open" ) if string[-1:6] != "dlrow": failure( "Range access Neagative definite" ) if string[-5:] != "world": failure( "Range access negative open" ) if string[-1:0] != "dlrow olleH": failure( "Range access complete reverse" ) // Assigment test string[5] = "-" if string != "Hello-world": failure( "Assignment test 1:1" ) string[5:6] = " - " if string != "Hello - world": failure( "Range assigment - enlarge" ) string[5:8] = "" if string != "Helloworld": failure( "Range assigment - remove" ) string[5:5] = " " if string != "Hello world": failure( "Range assigment - insertion" ) // concatenation test string = string[0:6] + "old" + string[5:] if string != "Hello old world": failure( "Concatenation 1" ) string += "." if string != "Hello old world.": failure( "Auto-sum" ) string[0:5] = "Good" + "bye" if string != "Goodbye old world.": failure( "Static string concatenation" ) string[8:] = "cruel" + string[7:] if string != "Goodbye cruel old world.": failure( "Concatenation 2" ) // indirect range test. range = [0:7] if string[ range ] != "Goodbye": failure( "Indirect range." ) // len test if len( string ) != 24: failure( "Len core function" ) // compilationtest for multiline string string = " A string Spawning On many lines." if string != "A string Spawning On many lines." failure( "Multiline string" ) end // compilationtest for multiline string 2 string = " A string Spawning On many lines. " if string != "A string Spawning On many lines." failure( "Multiline string 2" ) end // compilationtest for multiline string 3 string = " A string \"spawning\" On many lines. " if string != "A string \"spawning\" On many lines." failure( "Multiline string 3" ) end success() /* End of file */ tests/core/testsuite/stringslit.fal000066400000000000000000000020241176363201700200730ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 10c * Category: types * Subcategory: strings * Short: Lit string test * Description: * Checks for compilation of literal strings. * * This leverages on conditionals. * [/Description] * ****************************************************************************/ string1 = 'Hello c:\world' string = 'Hello ''world''' if string1 != "Hello c:\\world": failure( "backslash parsed" ) if string != "Hello 'world'": failure( "sequence '' not recognized" ) lits = 'A \\ literal '' string\' // the \' closes nevertheless if lits != "A \\\\ literal ' string\\" failure( "literal string" ) end string = ' A ''string'' "Spawning" On many lines.' if string != "A 'string'\n \"Spawning\"\nOn many lines." failure( "Multiline literal string" ) end string = ' A ''string'' "Spawning" On many lines. ' if string != "A 'string'\n \"Spawning\"\nOn many lines.\n" failure( "Multiline literal string" ) end success() /* End of file */ tests/core/testsuite/stringstream.fal000066400000000000000000000020501176363201700204120ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 106a * Category: rtl * Subcategory: streams * Short: String stream * Description: * Testing the string stream. * We need it now because next is the serialization test. * This is just a miminal test; a deeper test must be performed * together with the file system. This test is just needed to * ensure that serialization has a minimal support. * [/Description] * ****************************************************************************/ // empty string stream stream = StringStream() stream.write( "Hello" ) // test1 -- try to read from bottom if stream.grab(1) != "": failure( "Read from bottom" ) if not stream.eof(): failure( "Eof not set when read from bottom" ) // test2 -- read back stream.seek(0) str = stream.grab( 5 ) if str != "Hello": failure( "Read Back" ) // test3 -- preallocated string stream stream = StringStream( "Hello wolrd" ) str = "" while not stream.eof() str += stream.grab(1) end success() /* End of file */ tests/core/testsuite/struct_comma.fal000066400000000000000000000007551176363201700204020ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 1i * Category: basic * Subcategory: structure * Short: Semicomma * Description: * Checks for semicomma parsing. * This tests checks both for valid ";" and fake (useless) ";" * [/Description] * ****************************************************************************/ sel = 1 if sel ; success(); else failure( "Wrong branch" ) end ; failure( "If skipped" ) /* End of file */ tests/core/testsuite/swichbound.fal000066400000000000000000000014021176363201700200350ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 17f * Category: switch * Subcategory: * Short: Outbounded switch * Description: * Switch that should go out of bounds, and so crash; * Also, using complex value instead of simple one, so size of table * changes. * [/Description] * ****************************************************************************/ val = 2 // lower switch testReflect(val) case 5 failure( "selected 5" ) case 10 failure( "selected 10" ) default value = 3 end // higher val = 100 switch testReflect(val) case 5 failure( "selected 5" ) case 10 failure( "selected 10" ) default val = 101 end success() /* End of file */ tests/core/testsuite/switch1.fal000066400000000000000000000015441176363201700172610ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 17a * Category: switch * Subcategory: * Short: Basic switch * Description: * Minimal switch test. * This test is perfomed only on nil, default and integers. * * As switch is a quite complex statement, we test it in different files. * [/Description] * ****************************************************************************/ a = 3 /* Leave a space below the switch; It is needed to test for switch context change. */ switch a case -1 failure( "Wrong case: - one" ) case 1 failure( "Wrong case: one" ) case 2 failure( "Wrong case: two" ) case 3 success() case 4 failure( "Wrong case: four" ) default failure( "Wrong case: default" ) end failure( "Switch skipped" ) /* End of file */ tests/core/testsuite/switch2.fal000066400000000000000000000013041176363201700172540ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 17b * Category: switch * Subcategory: * Short: Nil switch * Description: * The switch item is a NIL object * As switch is a quite complex statement, we test it in different files. * [/Description] * ****************************************************************************/ a = nil switch a case nil success() case 1 failure( "Wrong case: one" ) case 2 failure( "Wrong case: two" ) case 3 failure( "Wrong case: three" ) case 4 failure( "Wrong case: four" ) default failure( "Default" ) end failure( "Switch skipped" ) /* End of file */ tests/core/testsuite/switch3.fal000066400000000000000000000012321176363201700172550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 17c * Category: switch * Subcategory: * Short: Default switch * Description: * Checks the basic default switch by selecting an outside value * [/Description] * ****************************************************************************/ a = 10 switch a case nil failure( "Nil case" ) case 1 failure( "Wrong case: one" ) case 2 failure( "Wrong case: two" ) case 3 failure( "Wrong case: three" ) case 4 failure( "Wrong case: four" ) default success() end failure( "Switch skipped" ) /* End of file */ tests/core/testsuite/switch_overload.fal000066400000000000000000000035531176363201700210750ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 24b * Category: switch * Subcategory: objects * Short: Switch with overloads * Description: * Object items in this switch overloads the compare() BOM. * [/Description] * ****************************************************************************/ object first function compare( test ) if test == 1: return 0 // ask to use normal criterion return nil end end object second function compare( test ) if test == "It's me!!": return 0 // ask to use normal criterion return nil end end function call_sw( param ) switch param case nil return 0 case 2, 3 return 2 case 4 to 8 return 3 case 10 to 11, 15 to 16 return 4 case 12, 17 to 18 return 5 case "one" return 6 case "two", "three" return 7 case first return 100 case second return 101 default return -1 end end // First check if the switch still works right if call_sw( nil ) != 0: failure("case 0") if call_sw( 2 ) != 2: failure("case 2 / 1") if call_sw( 4 ) != 3: failure("case 3 / 1") if call_sw( 8 ) != 3: failure("case 3 / 3") if call_sw( "one" ) != 6: failure("case 6") if call_sw( "two" ) != 7: failure("case 7 / 1") if call_sw( 0 ) != -1: failure("case default / 1") if call_sw( "none" ) != -1: failure("case default / 2") if call_sw( 13 ) != -1: failure("case default / 3") // then try this specific thing. // first check for object identity if call_sw( first ) != 100: failure("case first/identity") if call_sw( second ) != 101: failure("case second/identity") // then check object compare() overload if call_sw( 1 ) != 100: failure("case first / overload") if call_sw( "It's me!!" ) != 101: failure("case second / overload") success() /* End of file */ tests/core/testsuite/switchmix.fal000066400000000000000000000037111176363201700177140ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 17d * Category: switch * Subcategory: * Short: Mixed switch * Description: * Checks repeatedly a switch in a function call * [/Description] * ****************************************************************************/ function call_sw( param ) switch param case nil return 0 case 1 return 1 case 2, 3 return 2 case 4 to 8 return 3 case 10 to 11, 15 to 16 return 4 case 12, 17 to 18 return 5 case "one" return 6 case "two", "three" return 7 case "four", 20 return 8 case "five", 21 to 23, 25 return 9 default return 10 end end if call_sw( nil ) != 0: failure("case 0") if call_sw( 1 ) != 1: failure("case 1") if call_sw( 2 ) != 2: failure("case 2 / 1") if call_sw( 3 ) != 2: failure("case 2 / 2") if call_sw( 4 ) != 3: failure("case 3 / 1") if call_sw( 5 ) != 3: failure("case 3 / 2") if call_sw( 8 ) != 3: failure("case 3 / 3") if call_sw( 10 ) != 4: failure("case 4 / 1") if call_sw( 11 ) != 4: failure("case 4 / 2") if call_sw( 15 ) != 4: failure("case 4 / 3") if call_sw( 16 ) != 4: failure("case 4 / 4") if call_sw( 12 ) != 5: failure("case 5 / 1") if call_sw( 17 ) != 5: failure("case 5 / 2") if call_sw( 18 ) != 5: failure("case 5 / 3") if call_sw( "one" ) != 6: failure("case 6") if call_sw( "two" ) != 7: failure("case 7 / 1") if call_sw( "three" ) != 7: failure("case 7 / 2") if call_sw( "four" ) != 8: failure("case 8 / 1") if call_sw( "five" ) != 9: failure("case 9 / 1") if call_sw( 21 ) != 9: failure("case 9 / 2") if call_sw( 23 ) != 9: failure("case 9 / 3") if call_sw( 25 ) != 9: failure("case 9 / 4") if call_sw( 0 ) != 10: failure("case default / 1") if call_sw( "none" ) != 10: failure("case default / 2") if call_sw( 13 ) != 10: failure("case default / 3") success() /* End of file */ tests/core/testsuite/switchsmall.fal000066400000000000000000000043201176363201700202240ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 17e * Category: switch * Subcategory: * Short: Small switch * Description: * Small switches caused VM crash. * [/Description] * ****************************************************************************/ val = 2 // empty switch switch val end // single item switch, negative switch val case 1 failure( "single item switch, negative" ) end // single item switch, nil switch val case nil failure( "single item switch, nil" ) end // single item positive switch val case 2 val = 3 end if val != 3: failure( "single item positive" ) // single item switch + default negative switch val case 1 failure( "single item switch + default negative" ) default val = 2 end if val != 2: failure( "not poitive -- single item switch + default negative" ) // single item switch, nil + default switch val case nil failure( "single item switch, nil + default" ) default val = 3 end if val != 3: failure( "not poitive -- single item switch, nil + default" ) // single item positive + default switch val case 3 val = 2 default failure( "defaulted -- single item positive + default" ) end if val != 2: failure( "not poitive -- single item positive + default" ) // double item switch + default negative switch val case 0 failure( "double item switch + default negative - 0" ) case 1 failure( "double item switch + default negative - 1" ) default val = 2 end if val != 2: failure( "not poitive -- double item switch + default negative" ) // double item switch, nil + default switch val case nil failure( "double item switch, nil + default" ) case 1 failure( "double item switch + default negative - 1" ) default val = 3 end if val != 3: failure( "not poitive -- double item switch, nil + default" ) // double item positive + default switch val case 1 failure( "double item switch + default positive - 1" ) case 3 val = 2 default failure( "defaulted -- double item positive + default" ) end if val != 2: failure( "not poitive -- double item positive + default" ) success() /* End of file */ tests/core/testsuite/table_bidding.fal000066400000000000000000000031671176363201700204510ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 81d * Category: tabular * Subcategory: functional * Short: Table choice * Description: * Checks the Find method of Table class. * [/Description] * ****************************************************************************/ table = Table( // by default, the bidder bids the value (lambda x => x) [ bidder|offer_same, "value", "name", "prize" ], [ offer_few, 200, "First", nil ], [ nil, 100, "Second", nil ], [ offer_much, 50, "Third", nil ], [ nil, 400, "Fourth", nil ] ) function offer_same() self.prize = self.value return self.prize end function offer_few() self.prize = self.value / 3 return self.prize end function offer_much() self.prize = self.value *3 return self.prize end if table.bidding( 'bidder', 'name' ) != "Fourth" failure( "Step 1 - A" ) end if table.get(3).prize != 400 failure( "Modification on step 1 - A" ) end if table.bidding( 'bidder' ).name != "Fourth" failure( "Step 1 - B" ) end if table.bidding( 'bidder', rows|[0:2] ).name != "Second" failure( "Step 1 - C" ) end //============================================== table.get(3)[0] = offer_few if table.bidding( 'bidder', 'name' ) != "Third" failure( "Step 2 - A" ) end if table.get(2).prize != 150 failure( "Modification on step 2 - A" ) end if table.bidding( 'bidder' ).name != "Third" failure( "Step 2 - B" ) end if table.bidding( 'bidder', rows|[0:3] ).name != "Third" failure( "Step 1 - C" ) end success() tests/core/testsuite/table_choice.fal000066400000000000000000000023651176363201700203020ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 81c * Category: tabular * Subcategory: functional * Short: Table choice * Description: * Checks the Find method of Table class. * [/Description] * ****************************************************************************/ table = Table( [ "value", "name" ], [ 200, "First" ], [ 100, "Second" ], [ 50, "Third" ], [ 400, "Fourth" ] ) function lastBeFirst( array ) // cache for faster oeration static pos = array.table().columnPos( 'value' ) end // ... and then use the cached value return 10000000 - array[pos] end if table.choice( lastBeFirst, 'name' ) != "Third" failure( "Selection of the smaller" ) end if table.choice( {x => x.value} ).name != "Fourth" failure( "Selection of the highest" ) end if table.choice(lastBeFirst, rows|[0:2]).name != "Second" failure( "Ranged selection" ) end // select 100. if table.choice( {x => x.value == 100 ? oob(100) : 1000} ).name != "Second" failure( "Interrupted successful selection" ) end // aborted selection // select 100. if table.choice( {x => x.value == 100 ? oob(nil) : 1000} ) != nil failure( "Interrupted negative selection" ) end success() tests/core/testsuite/table_cols.fal000066400000000000000000000103721176363201700200050ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 80f * Category: tabular * Subcategory: basic * Short: Column juggling. * Description: * Test for insertion, change and removal of columns into tables. * [/Description] * ****************************************************************************/ // minimal table table = Table( [ custId|'a test data' , "name" ], [ "cust1", "Frank Capra" ], [ "cust2", "Sam Wise" ], [ "cust3", "My Cousin" ], [ "cust4", "Unknown Person" ] ) // basic column data access & change if table.order() != 2: failure( "order" ) if table.columnPos( 'name' ) != 1: failure( "columnPos" ) if table.columnData( 'custId' ) != 'a test data' failure( "columnData - string" ) end if table.columnData( 0 ) != 'a test data' failure( "columnData - int" ) end //====================================== // Column changing // table.setColumn( "name", "newname" ) if table.columnPos( 'newname' ) != 1: failure( "rename column" ) table.setColumn( 1, coldata|'some data in col 1' ) if table.columnData( 'newname' ) != 'some data in col 1' failure( "change column data" ) end //====================================== // Column insertion // table.insertColumn( 0, "prepend", "added col", 10 ) if table.order() != 3 failure( "Prepend order" ) end if table.columnPos( 'prepend' ) != 0 failure( "Prepend - name" ) end if table.columnData( 0 ) != "added col" failure( "Prepend - data" ) end if table.get(0).prepend != 10 failure( "Prepend value defaulting" ) end //=========================== // now append table.insertColumn( 100, "append", "added col 2" ) if table.order() != 4 failure( "Append order" ) end if table.columnPos( 'append' ) != 3 failure( "Append - name" ) end if table.columnData( 3 ) != "added col 2" failure( "Append - data" ) end if table.get(1)[3] != nil failure( "Append (non) defaulting" ) end //============================================= // now in the middle table.insertColumn( "newname", "insert", dflt|20 ) if table.order() != 5 failure( "Insert order" ) end if table.columnPos( 'insert' ) != table.columnPos( 'newname' ) - 1 failure( "Insert - position" ) end if table.columnData( 'insert' ) != nil failure( "Insert - (non) data" ) end // Insert is not a great name for a property... if table.get(2)[ table.columnPos( 'insert' ) ] != 20 failure( "Insert - defaulting" ) end //====================================== // Column removing // table.removeColumn( "newname" ) if table.order() != 4 failure( "Remove order" ) end if table.columnPos( "newname" ) != -1 failure( "Remove - not removing..." ) end if table.columnPos( "append" ) != 3 or \ table.columnPos( "insert" ) != 2 failure( "Remove - Not squishing columns" ) end if table.get(0).len() != 4 failure( "Remove not resizing rows" ) end if table.get(3)[3] != nil or \ table.get(2)[2] != 20 failure( "Remove - not squishing rows" ) end //======================================= // remove extremes table.removeColumn( 0 ) table.removeColumn( -1 ) if table.order() != 2 failure( "Remove extremes order" ) end if table.columnPos( 'custId' ) != 0 or \ table.columnPos( 'insert' ) != 1 failure( "Remove extremes not squeezing..." ) end if table.columnData( 'custId' ) != 'a test data' or \ table.columnData( 'insert' ) != nil failure( "Remove not squeezing column data..." ) end if table.get(0).len() != 2 failure( "Remove extremes not resizing rows" ) end if table.get(0)[0] != 'cust1' or \ table.get(2)[1] != 20 failure( "Remove - not squishing rows" ) end //====================================== // empty the table table.removeColumn( 0 ) table.removeColumn( 0 ) if table.order() != 0 failure( "Empty table order" ) end if table.get(0).len() != 0 failure( "Empty table array length" ) end //=============================================== // and reinsert again table.insertColumn( 0, "readded", "readded data", 40 ) if table.order() != 1 failure( "Readd order" ) end if table.getHeader(0) != "readded" failure( "Readd header" ) end if table.columnData(0) != "readded data" failure( "Readd data" ) end if table.get(0).len() != 1 failure( "Readd row length" ) end if table.get(1)[0] != 40 failure( "Readd row - data" ) end success() tests/core/testsuite/table_find.fal000066400000000000000000000022461176363201700177660ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 81b * Category: tabular * Subcategory: functional * Short: Table find * Description: * Checks the find method of Table class. * [/Description] * ****************************************************************************/ // minimal table customers = Table( [ custId|nil, "name", xval| defmethod ], [ "cust1", "Frank Capra", "plain value" ], [ "cust2", "Sam Wise", func ], [ "cust3", "My Cousin", method ], [ "cust4", "Unknown Person", nil ] ) function func() return "plain return" end function method() return self.name end function defmethod() return self.custId end if customers.find("custId", "cust3").name != "My Cousin" failure( "Find row" ) end if customers.find("custId", "cust3", "name") != "My Cousin" failure( "Find item" ) end // by number if customers.find(0, "cust3").name != "My Cousin" failure( "Find row - number" ) end if customers.find(0, "cust3", "name") != "My Cousin" failure( "Find item - number" ) end if customers.find(0, "cust3", 1) != "My Cousin" failure( "Find item - number 2" ) end success() tests/core/testsuite/table_insrem.fal000066400000000000000000000051551176363201700203450ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 80e * Category: tabular * Subcategory: basic * Short: Row juggling. * Description: * Test for row insertion and removal of rows into tables. * [/Description] * ****************************************************************************/ // minimal table table = Table( [ custId|nil, "name" ], [ "cust1", "Frank Capra" ], [ "cust2", "Sam Wise" ], [ "cust3", "My Cousin" ], [ "cust4", "Unknown Person" ] ) // try to insert a new row. table.insert( 2, ["newrow", "Some data"] ) if table.len() != 5: failure( "Lenght 0" ) if table.get(2)[0] != "newrow": failure( "Insert pos 0" ) if table.get(3)[0] != "cust3": failure( "Moved pos 0" ) if table.get(1)[0] != "cust2": failure( "Unmoved pos 0" ) // try to remove a row table.remove( 2 ) if table.len() != 4: failure( "Lenght 1" ) if table.get(2)[0] != "cust3": failure( "Moved pos 1" ) if table.get(1)[0] != "cust2": failure( "Unmoved pos 1" ) // try to delete the last row table.remove( 3 ) if table.len() != 3: failure( "Lenght 2" ) if table.get(2)[0] != "cust3": failure( "Taken back pos 2" ) // try to remove the first row. table.remove(0) if table.len() != 2: failure( "Lenght 3" ) if table.get(1)[0] != "cust3": failure( "Taken back pos 3" ) // empty the table table.remove(0) table.remove(0) if table.len() != 0: failure( "Emptying" ) // reinsert some element table.insert( 0, [ "cust4", "Unknown Person" ] ) if table.len() != 1: failure( "Refilling" ) if table.get(0)[0] != "cust4": failure( "Refilling data" ) // prepend table.insert( 0, [ "prepended", "one" ] ) if table.len() != 2: failure( "Prepend len" ) if table.get(0)[0] != "prepended": failure( "Prepend data" ) // postpend table.insert( -1, [ "postpended", "two" ] ) if table.len() != 3: failure( "Postpend len" ) if table.get(0)[0] != "prepended": failure( "Postpend data" ) //============================= // try to raise some error try table.insert( -1, [ "postpended", "two", "three" ] ) failure( "Error not raised for array size != order 1" ) catch ParamError in p catch in p failure( "Wrong error raised for array size != order 1" ) end try table.insert( -1, [ "postpended" ] ) failure( "Error not raised for array size != order 2" ) catch ParamError in p catch in p failure( "Wrong error raised for array size != order 2" ) end // empty table table.remove(0) table.remove(0) table.remove(0) try table.remove(0) failure( "Error not raised remove on table empty" ) catch AccessError in e catch in p failure( "Wrong error raised for remove on table empty" ) end success() tests/core/testsuite/table_minimal.fal000066400000000000000000000034151176363201700204730ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 80a * Category: tabular * Subcategory: basic * Short: Table class * Description: * This test just creates a minimal Table() and checks for * essential functions to be available and working. It's * the smoke test for tabular programming. * [/Description] * ****************************************************************************/ // minimal table customers = Table( [ custId|nil, "name", "quantity", discount|{x => 2*x} ] ) if customers.order() != 4: failure( "Table order" ) // Check for positional retreival if customers.getHeader( 0 ) != "custId" failure( "getHeader - future binding header" ) end if customers.getHeader( 1 ) != "name" failure( "getHeader - string binding header" ) end // positional retrival with coldata if customers.getColData( 0 ) != nil failure( "getColData - nil item" ) end if customers.getColData( 3 ).typeId() != FunctionType failure( "getColData - function item (1)" ) end if customers.getColData( 3 )(100) != 200 failure( "getColData - function item (2)" ) end // non positional header = customers.getHeader() if header.len() != 4 failure( "getHeader - size" ) end if header[0] != "custId" or \ header[1] != "name" or \ header[2] != "quantity" or \ header[3] != "discount" failure( "getHeader - content" ) end coldata = customers.getColData() if coldata.len() != 4 failure( "getColData - size" ) end if coldata[0] != nil or \ coldata[1] != nil or \ coldata[2] != nil or \ coldata[3].typeId() != FunctionType failure( "getColData - content" ) end if coldata[3](100) != 200 failure( "getColData - function item" ) end success() /* end of file */ tests/core/testsuite/table_oop.fal000066400000000000000000000034501176363201700176410ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 81a * Category: tabular * Subcategory: oop * Short: Table as class * Description: * Check methods on tables. * [/Description] * ****************************************************************************/ // minimal table customers = Table( [ custId|nil, "name", xval| defmethod ], [ "cust1", "Frank Capra", "plain value" ], [ "cust2", "Sam Wise", func ], [ "cust3", "My Cousin", method ], [ "cust4", "Unknown Person", nil ] ) function func() return "plain return" end function method() return self.name end function defmethod() return self.custId end a0 = customers.get(0) if a0.xval != "plain value": failure( "plain value" ) a1 = customers.get(1) if a1.xval() != "plain return": failure( "simple function" ) a2 = customers.get(2) if a2.xval() != "My Cousin": failure( "method call" ) a3 = customers.get(3) if a3.xval() != "cust4": failure( "default method call" ) // repeat using the property directly if customers.get(0, "xval") != "plain value" failure( "plain value - colname" ) end if customers.get(1, "xval")() != "plain return" failure( "simple function - colname" ) end if customers.get(2, "xval")() != "My Cousin" failure( "method call - colname" ) end if customers.get(3, "xval")() != "cust4" failure( "default method call - colname" ) end // repeat using the property directly, but by number if customers.get(0, 2) != "plain value" failure( "plain value - colnumber" ) end if customers.get(1, 2)() != "plain return" failure( "simple function - colnumber" ) end if customers.get(2, 2)() != "My Cousin" failure( "method call - colnumber" ) end if customers.get(3, 2)() != "cust4" failure( "default method call - colnumber" ) end success() tests/core/testsuite/table_pages.fal000066400000000000000000000046661176363201700201550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 81e * Category: tabular * Subcategory: pages * Short: Table pages * Description: * Checking page insertion, removal and switching. * [/Description] * ****************************************************************************/ // minimal table table = Table( [ custId|nil, "name" ], [ "cust1", "Frank Capra" ], [ "cust2", "Sam Wise" ], [ "cust3", "My Cousin" ], [ "cust4", "Unknown Person" ] ) if table.pageCount() != 1: failure( "Basic page count" ) // insert an empty page table.insertPage() if table.pageCount() != 2: failure( "Empty append - size" ) if table.curPage() != 0: failure( "Empty append - curPage" ) table.setPage(1) if table.curPage() != 1: failure( "Empty append - setPage" ) if table.len() != 0: failure( "Empty append - page len" ) // append some data table.insertPage( data| [[ "p2 cust1", "Frank Zappa" ], [ "p2 cust2", "Sam unwise" ]] ) if table.pageCount() != 3: failure( "Filled append - size" ) if table.curPage() != 1: failure( "Filled append - curPage" ) table.setPage(2) if table.curPage() != 2: failure( "Filled append - setPage" ) if table.len() != 2: failure( "Filled append - page len" ) if table.get(1)[0] != "p2 cust2" or table.get(0)[1] != "Frank Zappa" failure( "Filled append - data" ) end // positional insert table.insertPage( 1, [[ "p1 cust1", "Frank Zappa1" ], [ "p1 cust2", "Sam unwise1" ]] ) if table.pageCount() != 4: failure( "Insert - size" ) if table.curPage() != 3: failure( "Insert - relocation" ) table.setPage(1) if table.get(1)[0] != "p1 cust2" or table.get(0)[1] != "Frank Zappa1" failure( "Insert - data" ) end table.removePage(2) if table.pageCount() != 3: failure( "Remove - size" ) if table.curPage() != 1: failure( "Remove - const pos" ) table.removePage(0) if table.pageCount() != 2: failure( "Remove - size 2" ) if table.curPage() != 0: failure( "Remove - move pos" ) if table.get(1)[0] != "p1 cust2" or table.get(0)[1] != "Frank Zappa1" failure( "Remove - const data" ) end table.setPage(1) table.removePage(1) if table.pageCount() != 1: failure( "Remove self" ) if table.curPage() != 0: failure( "Remove self - move to zero" ) if table.get(1)[0] != "p1 cust2" or table.get(0)[1] != "Frank Zappa1" failure( "Remove self - const data" ) end // excessive removal try table.removePage(0) failure( "Remove - error not raised" ) end success() tests/core/testsuite/table_sequence.fal000066400000000000000000000020651176363201700206550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 80b * Category: tabular * Subcategory: basic * Short: Table as sequence * Description: * This test checks if the current page in a table can * properly accessed as a sequence of vectors. * [/Description] * ****************************************************************************/ // minimal table customers = Table( [ custId|nil, "name" ], [ "cust1", "Frank Capra" ], [ "cust2", "Sam Wise" ], [ "cust3", "My Cousin" ], [ "cust4", "Unknown Person" ] ) cfrTable = [ [ "cust1", "Frank Capra" ], [ "cust2", "Sam Wise" ], [ "cust3", "My Cousin" ], [ "cust4", "Unknown Person" ] ] count = 0 for row in customers if count > cfrTable.len() failure( "Loop termination" ) end cfr = cfrTable[count] if row[0] != cfr[0]: failure( @"Content in row $count, col 0" ) if row[1] != cfr[1]: failure( @"Content in row $count, col 1" ) count++ end if count != cfrTable.len() failure( "Check too short" ) end tests/core/testsuite/table_sequence2.fal000066400000000000000000000023671176363201700207440ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 80c * Category: tabular * Subcategory: basic * Short: Table as sequence 2 * Description: * Other than checking if this table can be seen as a sequence, * this test also verifies that the sequence can be properly * alterated with .= and continue dropping. * [/Description] * ****************************************************************************/ // minimal table customers = Table( [ custId|nil, "name" ], [ "cust1", "Frank Capra" ], [ "cust2", "Sam Wise" ], [ "cust3", "My Cousin" ], [ "cust4", "Unknown Person" ] ) cfrTable = [ [ "cust1", "Frank Capra" ], [ "cust3", "My Cousin" ], [ "stranger", "me" ] ] count = 0 for row in customers if count == 1 count++ continue dropping elif count == 3 .= [ "stranger", "me" ] end count ++ end count = 0 for row in customers if count > cfrTable.len() failure( "Loop termination" ) end cfr = cfrTable[count] if row[0] != cfr[0]: failure( @"Content in row $count, col 0" ) if row[1] != cfr[1]: failure( @"Content in row $count, col 1" ) count++ end if count != cfrTable.len() failure( "Check too short" ) end tests/core/testsuite/table_sequence3.fal000066400000000000000000000022061176363201700207350ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 80d * Category: tabular * Subcategory: basic * Short: Table as sequence extreme * Description: * Check that the extremities of the forloop are correctly altered. * [/Description] * ****************************************************************************/ // minimal table customers = Table( [ custId|nil, "name" ], [ "cust1", "Frank Capra" ], [ "cust2", "Sam Wise" ], [ "cust3", "My Cousin" ], [ "cust4", "Unknown Person" ] ) cfrTable = [ [ "cust2", "Sam Wise" ], [ "cust3", "My Cousin" ] ] count = 0 for row in customers if count == 0 count ++ continue dropping end count ++ forlast l = customers.last() l.erase() end end count = 0 for row in customers if count > cfrTable.len() failure( "Loop termination" ) end cfr = cfrTable[count] if row[0] != cfr[0]: failure( @"Content in row $count, col 0" ) if row[1] != cfr[1]: failure( @"Content in row $count, col 1" ) count++ end if count != cfrTable.len() failure( "Check too short" ) end tests/core/testsuite/template.txt000066400000000000000000000006401176363201700175630ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * CVS-Id: $Id: template.txt,v 1.1.1.1 2006/10/08 15:05:43 gian Exp $ * * ID: 0a * Category: template * Subcategory: subtemplate * Short: A short description * Description: * * [/Description] * ****************************************************************************/ // Some Falcon code here /* End of file */ tests/core/testsuite/test_driver.cmake.in000066400000000000000000000012011176363201700211420ustar00rootroot00000000000000find_program(faltest_EXECUTABLE NAMES faltest HINTS "@CMAKE_INSTALL_PREFIX@/bin" NO_DEFAULT_PATH ) if(NOT faltest_EXECUTABLE) message(FATAL_ERROR "fatest executable not found") endif() if(test_category) set(cmd ${faltest_EXECUTABLE} -v -c ${test_category}) else() set(cmd ${faltest_EXECUTABLE} -v ) endif() execute_process( COMMAND ${cmd} WORKING_DIRECTORY @CMAKE_CURRENT_SOURCE_DIR@ RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_VARIABLE err ) if(res) message("faltest return value: ${res}") message("faltest stderr: ${err}") message("faltest stdout: ${out}") message(SEND_ERROR "test(s) failed") endif() tests/core/testsuite/time_clone.fal000066400000000000000000000023671176363201700200210ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 108c * Category: rtl * Subcategory: TimeStamp * Short: Timestamp clone * Description: * Duplicates a timestap * [/Description] ****************************************************************************/ // create a date for today today = CurrentTime() // create a date for tomorrow, copying it from today... tomorrow = today.clone() // first, see if they are equal if today.compare( tomorrow ) != 0: failure( "Clone equality" ) // relational ops now call compare if available if today != tomorrow: failure( "Clone equality" ) // change one and see insolation if today.hour == 22 tomorrow.hour = 23 else tomorrow.hour = 22 end if today.hour == tomorrow.hour: failure( "Clone insolation" ) // Try the same thing with item copy tomorrow = clone( today ) // first, see if they are equal if today.compare( tomorrow ) != 0: failure( "ItemCopy equality" ) // relational ops now call compare if available if today != tomorrow: failure( "ItemCopy equality" ) // change one and see insolation if today.hour == 22 tomorrow.hour = 23 else tomorrow.hour = 22 end if today.hour == tomorrow.hour: failure( "ItemCopy insolation" ) success() /* End of file */ tests/core/testsuite/timestamp.fal000066400000000000000000000114671176363201700177070ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 108a * Category: RTL * Subcategory: TimeStamp * Short: Test on timestamps * Description: * First test for RTL object and classes, checks the timestamp * class to work correctly. * [/Description] ****************************************************************************/ // create a date for today today = CurrentTime() // create a date for tomorrow, copying it from today... tomorrow = TimeStamp( today ) // first, see if they are equal if today.compare( tomorrow ) != 0: failure( "Same date not equal" ) // set a reference date tomorrow.year = 2006 tomorrow.month = 3 tomorrow.day = 31 today = TimeStamp( tomorrow ) // add a day aday = TimeStamp() aday.day = 1 tomorrow.add( aday ) if tomorrow.day != 1 or tomorrow.month != 4 or tomorrow.year != 2006 failure( "addition" ) end if tomorrow.compare( today ) != 1: failure( "Compare one" ) if today.compare( tomorrow ) != -1: failure( "Compare two" ) result = TimeStamp( tomorrow ) result.distance( today ) if result.day != -1: failure( "Subtraction" ) // check for a leap year. someyear = TimeStamp() someyear.year = 1997 if someyear.isLeapYear(): failure( "leap year -- false positive" ) someyear.year = 1996 if not someyear.isLeapYear(): failure( "leap year detection" ) // check for date correctness. origin = TimeStamp() //setting january the first 2006 origin.year = 2006 origin.month = 1 origin.day = 1 // it was a sunday if origin.dayOfWeek() != 6: failure( "Day of week calculation -- 1" ) // adding 30 days we should be at january the 31th aday.day = 30 origin.add( aday ) if origin.day != 31 or origin.month != 1: failure( "adding 30 days" ) // adding a day we roll on february (which was wednesday aday.day = 1 origin.add( aday ) if origin.month != 2 or origin.day != 1: failure( "Roll on 2006-2-1" ) if origin.dayOfWeek() != 2: failure( "Day of week calculation -- 2" ) // adding 28 days we roll on march the 1th, a wednesday too aday.day = 28 origin.add( aday ) if origin.month != 3 or origin.day != 1: failure( "Roll on 2006-3-1" ) if origin.dayOfWeek() != 2: failure( "Day of week calculation -- 3" ) // adding 60 we roll on april 30 aday.day = 60 origin.add( aday ) if origin.month != 4 or origin.day != 30: failure( "Roll on 2006-4-30" ) if origin.dayOfWeek() != 6: failure( "Day of week calculation -- 4" ) // roll by hours: aday.day = 0 aday.hour = 26 aday.minute = 62 aday.second = 131 aday.msec = 2250 origin.add( aday ) if origin.month != 5 or \ origin.day != 1 or \ origin.hour != 3 or \ origin.minute != 4 or \ origin.second != 13 or \ origin.msec != 250 failure( "Hour/day roll" ) end // roll the year aday.hour = 0 aday.minute = 0 aday.second = 0 aday.msec = 0 aday.day = 365 origin.add( aday ) if origin.year != 2007 or \ origin.month != 5 or \ origin.day != 1 failure( "Year roll" ) end // now way back: aday.day = -365 aday.hour = -26 aday.minute = -62 aday.second = -131 aday.msec = -2250 origin.add( aday ) if origin.year != 2006 or \ origin.month != 4 or \ origin.day != 30 or \ origin.hour != 0 or \ origin.minute != 0 or \ origin.second != 0 or \ origin.msec != 0 failure( "Hour/day back roll" ) end // and the distance? april_28 = TimeStamp() april_28.year = 2006 april_28.month = 4 april_28.day = 28 april_28.hour = 21 april_28.minute = 25 // aday is 30 april at midnight aday = TimeStamp( origin ) aday.distance( april_28 ) // the distance between 30 april and 28 april 21:25 (back in time) // is 2 hours, 35 minutes and one full day if aday.month != 0 or \ aday.day != -1 or \ aday.hour != 2 or \ aday.minute != 35 or \ aday.second != 0 or \ aday.msec != 0 failure( "Date distance 1 " ) end may_2 = TimeStamp() may_2.year = 2006 may_2.month = 5 may_2.day = 2 may_2.hour = 6 may_2.minute = 10 // aday is 30 april at midnight aday = TimeStamp( origin ) aday.distance( may_2 ) // the distance between 30 april 0:0 and 2 may 6:10 am // is 6 hours and, 10 minutes 2 full days if aday.month != 0 or \ aday.day != 2 or \ aday.hour != 6 or \ aday.minute != 10 or \ aday.second != 0 or \ aday.msec != 0 failure( "Date distance 2" ) end april_29 = TimeStamp() april_29.year, april_29.month, april_29.day , april_29.hour, april_29.minute = \ 2006, 4, 29, 15, 45 // aday is 30 april at midnight aday = TimeStamp( origin ) aday.distance( april_29 ) // the distance between 30 april 0:0 and 2 may 6:10 am // is 6 hours and, 10 minutes 2 full days if aday.month != 0 or \ aday.day != 0 or \ aday.hour != -8 or \ aday.minute != 15 or \ aday.second != 0 or \ aday.msec != 0 failure( "Date distance 3" ) end // test VM comparation if aday == nil: failure( "Timestamp considered == nil" ) if aday != aday: failure( "Timestamp considered != to itself" ) success() /* End of file */ tests/core/testsuite/trybreak.fal000066400000000000000000000034621176363201700175230ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 20d * Category: statements * Subcategory: try * Short: Try/catch with breaks * Description: * Tests for ecception rising and catching. * This tests correct compilation stack unwinding out of loops. * Here the break (ptry count) is checked * After this test series, exception rising and catching can be used in * testsuite as a part of the test checks. * [/Description] * ****************************************************************************/ // first, while inside try i = 0 try while i < 10 if i == 5: break i++ end end if i != 5: failure( "First break" ) // then, while inside try with a catch before try value = 0 while i < 10 break end raise 154 failure( "Impossible position" ) catch in e value = e == 154 end if not value: failure( "First catch not correctly performed" ) // now a try/catch inside a while i = 0 while i < 10 try value = 0 raise 154 catch in e value = e == 154 end break end if not value: failure( "Second catch not correctly performed" ) // now a break inside a try inside a while // now a try/catch inside a while i = 0 while i < 10 try value = 0 break end end // now a break inside a try/catch inside a while // now a try/catch inside a while i = 0 while i < 10 try value = 0 break raise 0 catch in e value = 1 end end if value: failure( "Break didn't stop raise" ) // now a break inside a catch inside a while // now a try/catch inside a while i = 0 while i < 10 try value = 0 if i == 5: raise 134 catch in e value = e == 134 break end i++ end if i != 5 or value != true: failure( "Break in catch" ) success() /* End of file */ tests/core/testsuite/trycatch.fal000066400000000000000000000017501176363201700175170ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 20a * Category: statements * Subcategory: try * Short: Basic try and catch * Description: * Tests for ecception rising and catching. * After this test series, exception rising and catching can be used in * testsuite as a part of the test checks. * [/Description] * ****************************************************************************/ // basic try try a = 1 + "a" failure( "Exception not raised / try" ) end // basic try catch try a = 1 + nil failure( "Exception not raised / trycatch" ) catch a = 1 end if a != 1: failure( "Exception not catched /trycatch" ) // basic try catch with value try a = 1 + "a" failure( "Exception not raised / trycatch + value" ) catch in e a = 2 end if a != 2: failure( "Exception not catched /trycatch + value" ) try raise "test" catch in e a = e end if a != "test": failure( "raise" ) success() /* End of file */ tests/core/testsuite/trycatch_deep.fal000066400000000000000000000014041176363201700205100ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 20h * Category: statements * Subcategory: try * Short: Deep try * Description: * This test checks for try/catch to work across deep frame stack calls. * [/Description] * ****************************************************************************/ function try_catch( param ) try raiser( param ) catch in e return e end return "failed" end function raiser( param ) GC.perform(true) if param == 0 a = nil a += 1 end raiser( param - 1) end if try_catch( 1 ).code != 17: failure( "One" ) if try_catch( 5 ).code != 17: failure( "Five" ) if try_catch( 10 ).code != 17: failure( "Ten" ) success() /* End of file */ tests/core/testsuite/trycatch_loop.fal000066400000000000000000000015661176363201700205550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 20b * Category: statements * Subcategory: try * Short: Try/catch in loops * Description: * Tests for ecception rising and catching. * This tests correct compilation stack unwinding out of loops * After this test series, exception rising and catching can be used in * testsuite as a part of the test checks. * [/Description] * ****************************************************************************/ i = 0 while i < 10 try if i == 2 i++ continue failure( "Continue in try/catchs" ) end if i == 8 or i == 9 a = nil + 1 end catch if i == 8 ++i continue else break end end ++i end if i != 9: failure( "Break in try/catchs" ) success() /* End of file */ tests/core/testsuite/trycatch_mod1.fal000066400000000000000000000016111176363201700204330ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: * Subcategory: * Short: try catch test / slave module 1 * Description: * This slave test will be loaded form trycatch_module.fal * It loads trycatch_mod2 in turn. * [/Description] * ****************************************************************************/ load trycatch_mod2 function raiseInMod1() raise "Hello from module 1" end function callInMod1() try raiseInMod2() catch NumericType in err failure( "Type catch - mod1 - 1" ) catch StringType in err if err != "Hello from module 2" failure( "Raise content - mod1 - 1" ) end catch Error failure( "Type catch - mod1 - 2" ) catch in res1 failure( "Type catch - mod1 - 3" ) end end function callInMod1_2() raiseInMod2() end export /* End of file */ tests/core/testsuite/trycatch_mod2.fal000066400000000000000000000010041176363201700204300ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: --- * Category: * Subcategory: * Short: try catch test / slave module 2 * Description: * This slave test will be loaded form trycatch_mod1.fal * It is used to raise exceptions that will be catched * in parent modules. * [/Description] * ****************************************************************************/ function raiseInMod2() raise "Hello from module 2" end export /* End of file */ tests/core/testsuite/trycatch_module.fal000066400000000000000000000022771176363201700210710ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 20g * Category: statements * Subcategory: try * Short: Inter-module try/catch * Description: * Tests for inter module raise and catch. Module context must be changed * and set up coherently on raisal, or the test will crash the vm. * [/Description] * ****************************************************************************/ load trycatch_mod1 try raiseInMod1() catch NumericType in err failure( "Type catch - top - 1" ) catch StringType in err if err != "Hello from module 1" failure( "Raise content - 1" ) end catch Error failure( "Type catch - top - 2" ) catch in res1 failure( "Type catch - top - 3" ) end // this raises an error in module 2 which is catched in module 1 callInMod1() try // this call a function in module 1 which calls a function in module 2 // which raises a string callInMod1_2() catch NumericType in err failure( "Type catch - top - 4" ) catch StringType in err if err != "Hello from module 2" failure( "Raise content - 2" ) end catch Error failure( "Type catch - top - 6" ) catch in res1 failure( "Type catch - top - 7" ) end success() /* End of file */ tests/core/testsuite/trycatch_ret.fal000066400000000000000000000015311176363201700203660ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 20c * Category: statements * Subcategory: try * Short: Try/catch in functions * Description: * Tests for ecception rising and catching. * This tests correct return stack unwinding in case of try/catch * After this test series, exception rising and catching can be used in * testsuite as a part of the test checks. * [/Description] * ****************************************************************************/ function try_catch( param ) try if param == 1: return 5 if param == 2: raise 10 catch in e return e end return param end if try_catch( 1 ) != 5: failure( "Return in try" ) if try_catch( 2 ) !=10: failure( "Return in catch" ) if try_catch( 3 ) != 3: failure( "Passthrough" ) success() /* End of file */ tests/core/testsuite/trycatch_typed.fal000066400000000000000000000046171176363201700207310ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 20e * Category: statements * Subcategory: try * Short: Typed try/catch * Description: * Tests for typed exception rising and catching. * [/Description] * ****************************************************************************/ // basic try catch try a = 1 + nil failure( "Exception not raised / trycatch" ) catch TypeError a = 1 catch failure( "Specific exception not caught" ) end if a != 1: failure( "Specific error branch not executed" ) // single integer try raise 1 catch NumericType a = 2 end if a != 2: failure( "Raise int / 1" ) // single integer, single type try raise 1 catch NumericType a = 3 catch StringType a = 2 end if a != 3: failure( "Raise int / 2" ) try raise 1 a = 1 catch NumericType, StringType a = 4 catch TypeError a = 2 end if a != 4: failure( "Raise int / 3" ) try raise 1 a = 1 catch NumericType, StringType a = 5 catch TypeError, Error a = 2 end if a != 5: failure( "Raise int / 4" ) try raise 1 a = 1 catch NumericType, StringType a = 6 catch TypeError, Error a = 2 catch a = 0 end if a != 6: failure( "Raise / complete" ) // capture raise try raise "message" a = 1 catch NumericType in err a = 2 catch StringType in err if err != "message": failure( "catch in / caught value" ) a = 4 catch Error in err a = 0 catch in err a = -1 end if a != 4: failure( "Raise / catch in 1" ) // capture a subclass in order a = 0 try a = 9 + nil catch NumericType in err a = 1 catch TypeError a = 10 catch Error a = 2 catch a = 3 end if a != 10: failure( "Catch order / 1" ) // capture a subclass in reverse order a = 0 try a = 9 + nil catch NumericType in err a = 1 catch Error a = 10 catch TypeError a = 1 catch a = 3 end if a != 10: failure( "Catch order / 2" ) // capture an item object test val = "hello" end a = 0 try raise test catch NumericType in err a = 1 catch TypeError a = 2 catch Error a = 3 catch test in res if res.val != "hello": failure( "Raised item" ) a = 10 catch a = 3 end if a != 10: failure( "Catch raised item" ) // catch into generic a = 0 try raise test catch NumericType in err a = 1 catch TypeError a = 2 catch Error a = 3 catch in res1 if res1.val != "hello": failure( "Raised item in generic" ) a = 10 end if a != 10: failure( "Catch raised item in generic" ) success() /* End of file */ tests/core/testsuite/ts_distance.fal000066400000000000000000000024521176363201700201760ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 108d * Category: RTL * Subcategory: TimeStamp * Short: Timestamp distance * Author: Jeremy Cowgar * Description: * Verify correct date distance calculation. * [/Description] ****************************************************************************/ function ts(year, month, day) ts = TimeStamp() ts.year = year ts.month = month ts.day = day return ts end function d(year, month, day, shouldBe) ts = TimeStamp() ts.year = 2008 ts.month = 1 ts.day = 5 then = TimeStamp() then.year = year then.month = month then.day = day ts.distance( then ) then2 = TimeStamp() then2.year = year then2.month = month then2.day = day offBy = ts.day - shouldBe return offBy == 0 end d( 1975, 1, 5, -12053 ) or failure( "1975/1/5" ) d( 1982, 1, 5, -9496 ) or failure( "1982/1/5" ) d( 1923, 1, 5, -31046 ) or failure( "1923/1/5" ) d( 2000, 1, 5, -2922 ) or failure( "2000/1/5" ) d( 2007, 5, 12, -238 ) or failure( "2007/5/12" ) d( 2007, 12, 12, -24 ) or failure( "2007/12/12" ) d( 2008, 2, 5, 31 ) or failure( "2008/2/5" ) d( 2008, 12, 5, 335 ) or failure( "2008/12/5" ) d( 2009, 12, 5, 700 ) or failure( "2009/12/5" ) success() /* end of test */ tests/core/testsuite/ts_tostring.fal000066400000000000000000000017121176363201700202530ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 108b * Category: RTL * Subcategory: TimeStamp * Short: Timestamp string fmt * Description: * Tests some timestamp to string conversion. * [/Description] ****************************************************************************/ // test standard tostring april_30 = TimeStamp() april_30.year = 2006 april_30.month = 4 april_30.day = 28 april_30.hour = 1 april_30.minute = 2 april_30.second = 3 april_30.msec = 9 if april_30.toString() != "2006-04-28 01:02:03.009" failure( "Default toString with fillers 1" ) end april_30.msec = 99 if april_30.toString() != "2006-04-28 01:02:03.099" failure( "Default toString with fillers 1" ) end april_30.hour = 11 april_30.minute = 12 april_30.second = 13 april_30.msec = 999 if april_30.toString() != "2006-04-28 11:12:13.999" failure( "Default toString without fillers" ) end success() /* End of file */ tests/core/testsuite/unsubscribe.fal000066400000000000000000000032111176363201700202140ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 38j * Category: messages * Subcategory: * Short: Controlled unsubscription * Description: * Check that unsubscriptions during broadcasts are working properly. * [/Description] * ****************************************************************************/ control = 0 result = "untouched" class subscriber( id ) id = id init subscribe( "test", self.manager ) end function manager( id, coop ) if id == self.id unsubscribe( "test", self.manager ) coop[self.id] = -1 else coop[self.id] = 1 end end end subs = [] for i in [0:3]: subs += subscriber( i ) coop = .[0 0 0] broadcast( "test", 1, coop ) if coop[0] != 1: failure( "First broadcast; receival 0" ) if coop[1] != -1: failure( "First broadcast; receival 1" ) if coop[2] != 1: failure( "First broadcast; receival 2" ) coop = .[0 0 0] broadcast( "test", 2, coop ) if coop[0] != 1: failure( "Second broadcast; receival 0" ) if coop[1] != 0: failure( "Second broadcast; receival 1" ) if coop[2] != -1: failure( "Second broadcast; receival 2" ) coop = .[0 0 0] broadcast( "test", 0, coop ) if coop[0] != -1: failure( "Third broadcast; receival 0" ) if coop[1] != 0: failure( "Third broadcast; receival 1" ) if coop[2] != 0: failure( "Third broadcast; receival 2" ) coop = .[0 0 0] broadcast( "test", 1, coop ) if coop[0] != 0: failure( "Fourth broadcast; receival 0" ) if coop[1] != 0: failure( "Fourth broadcast; receival 1" ) if coop[2] != 0: failure( "Fourth broadcast; receival 2" ) success() /* end of file */ tests/core/testsuite/uritest.fal000066400000000000000000000064141176363201700173770ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 117a * Category: rtl * Subcategory: URI * Short: URI support. * Description: * This test verifies conformity of URI class to standards. * [/Description] * ****************************************************************************/ uri = URI( "http://me:pwd@www.falconpl.org:80/path/to/index.ftd?field=value#frag" ) if uri.scheme != "http": failure( "Initial scheme" ) if uri.userInfo != "me:pwd": failure( "Initial userinfo" ) if uri.host != "www.falconpl.org": failure( "Initial host" ) if uri.port != "80": failure( "Initial port" ) if uri.path != "/path/to/index.ftd": failure( "Initial path" ) if uri.query != "field=value": failure( "Initial query" ) if uri.fragment != "frag": failure( "Initial fragment" ) if uri.uri != "http://me:pwd@www.falconpl.org:80/path/to/index.ftd?field=value#frag" failure( "Initial complete uri" ) end // Change the whole uri uri.uri = "http1://me:pwd1@www.falconpl.org1:801/path/to/index.ftd1?field=value1#frag1" if uri.scheme != "http1": failure( "Changed scheme" ) if uri.userInfo != "me:pwd1": failure( "Changed userinfo" ) if uri.host != "www.falconpl.org1": failure( "Changed host" ) if uri.port != "801": failure( "Changed port" ) if uri.path != "/path/to/index.ftd1": failure( "Changed path" ) if uri.query != "field=value1": failure( "Changed query" ) if uri.fragment != "frag1": failure( "Changed fragment" ) if uri.uri != "http1://me:pwd1@www.falconpl.org1:801/path/to/index.ftd1?field=value1#frag1" failure( "Changed complete uri" ) end uri.scheme = "http" if uri.uri != "http://me:pwd1@www.falconpl.org1:801/path/to/index.ftd1?field=value1#frag1" failure( "Scheme change" ) end uri.userInfo = "me:pwd" if uri.uri != "http://me:pwd@www.falconpl.org1:801/path/to/index.ftd1?field=value1#frag1" failure( "Userinfo change" ) end uri.host = "www.falconpl.org" if uri.uri != "http://me:pwd@www.falconpl.org:801/path/to/index.ftd1?field=value1#frag1" failure( "Host change" ) end uri.port = "80" if uri.uri != "http://me:pwd@www.falconpl.org:80/path/to/index.ftd1?field=value1#frag1" failure( "Port change" ) end uri.path = "/path/to/index.ftd" if uri.uri != "http://me:pwd@www.falconpl.org:80/path/to/index.ftd?field=value1#frag1" failure( "Path change" ) end uri.query = "field=value" if uri.uri != "http://me:pwd@www.falconpl.org:80/path/to/index.ftd?field=value#frag1" failure( "Query change" ) end uri.fragment = "frag" if uri.uri != "http://me:pwd@www.falconpl.org:80/path/to/index.ftd?field=value#frag" failure( "Fragment change" ) end uri.fragment = "" if uri.uri != "http://me:pwd@www.falconpl.org:80/path/to/index.ftd?field=value" failure( "Fragment nilling" ) end uri.path = "" if uri.uri != "http://me:pwd@www.falconpl.org:80?field=value" failure( "Path nilling" ) end uri.scheme = "" if uri.uri != "me:pwd@www.falconpl.org:80?field=value" failure( "Scheme nilling" ) end uri.userInfo = "" if uri.uri != "www.falconpl.org:80?field=value" failure( "Userinfo nilling" ) end uri.port = "" if uri.uri != "www.falconpl.org?field=value" failure( "Port nilling" ) end uri.query = "" if uri.uri != "www.falconpl.org" failure( "Query nilling" ) end // TODO: check fields and encoding. success() /* end of uritest.fal */ tests/core/testsuite/varclass.fal000066400000000000000000000017431176363201700175160ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 21h * Category: types * Subcategory: classes * Short: Class variables * Description: * A class is treated as a variable and then returned from a function. * [/Description] * ****************************************************************************/ class base propbase = 0 end class derived from base propderived = 0 end class derived1( var1, var2 ) from base propderived1 = var1 propbase = var2 end function generate( param ) if param == 0 return derived else return derived1 end end cls1 = generate( 0 ) cls2 = generate( 1 ) obj1 = cls1() if obj1.propderived != 0: failure( "derived initialization" ) if obj1.propbase != 0: failure( "base initialization" ) obj2 = cls2( 1, 2 ) if obj2.propderived1 != 1: failure( "second derived initialization" ) if obj2.propbase != 2: failure( "second base initialization" ) success() /* End of file */ tests/core/testsuite/varparams.fal000066400000000000000000000024511176363201700176710ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 13k * Category: functions * Subcategory: * Short: Variable parameter call * Description: * This test checks for the variable params conventions. * [/Description] * ****************************************************************************/ function test_call_two( param1, param2, param3 ) if param1 == nil or param2 == nil or param3 != nil failure( "parameter assigment convention (fill less)" ) end end function test_call_four( param1, param2, param3 ) if param1 == nil or param2 == nil or param3 == nil failure( "parameter assigment convention (fill more)" ) end end function test_call_var( param1, param2 ) for i in [ 0 : paramCount() ] if i == 0 and paramNumber( i ) != param1 failure( "Parameter access via paramNumber() and symbol -- 1" ) end if i == 1 and paramNumber( i ) != param2 failure( "Parameter access via paramNumber() and symbol -- 2" ) end end // return the last parameter if paramCount() > 0 return paramNumber( paramCount() - 1 ) end end test_call_two( 1, 2 ) test_call_four( 1, 2, 3, 4 ) if test_call_var( 0, 1, 2, 3 ) != 3 failure( "ParameterNumber return" ) end success() /* End of file */ tests/core/testsuite/while.fal000066400000000000000000000012271176363201700170050ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 14a * Category: loops * Subcategory: while * Short: Basic while * Description: * Checks for while, break and continue to work correctly * [/Description] * ****************************************************************************/ a = 10 while a > 0: --a if a != 0: failure( "Minimal while" ) a = 10 while a > 0 if a == 3 break end --a end if a != 3: failure( "break" ) a = 10 while a > 0 if a == 3 --a continue failure( "continue" ) end --a end if a != 0: failure( "while" ) success() /* End of file */ tests/core/testsuite/whilenest.fal000066400000000000000000000011541176363201700176760ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 14b * Category: loops * Subcategory: while * Short: Nested while * Description: * Checks for nested whiles * [/Description] * ****************************************************************************/ a = 10 c = 0 // minimal nested while while a > 0 a -- b = 10 while b > 0 b -- end if b != 0: failure( "Nested exit" ) c ++ if c > 10: failure( "Nested repetition" ) end if a != 0: failure( "Loop condition - 1" ) if c != 10: failure( "Loop execution - 1" ) success() /* End of file */ tests/feathers/000077500000000000000000000000001176363201700140275ustar00rootroot00000000000000tests/feathers/samples/000077500000000000000000000000001176363201700154735ustar00rootroot00000000000000tests/feathers/samples/bufext_test.fal000066400000000000000000000365521176363201700205260ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - Samples FILE: bufext_test.fal Bufext module tests This file contains simple and stress-tests for the buffers provided by the bufext module. ------------------------------------------------------------------- Author: Maximilian Malek Begin: Thu, 25 Oct 2010 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load bufext GC.adjust(GC.ADJ_STRICT) // we will produce a lot of garbge randomSeed(8736275764) // make results consistent success = true function EXPECT(actual, expected, str) global success if(actual != expected) > "FAIL: Expected: '", expected, "', actual: '", actual, "' <-- ", str success = false end end function EXPECTDIFF(actual, expected, diff, str) global success if(abs(actual - expected) > diff) > "FAIL: Expected: '", expected, "', actual: '", actual, "' <-- ", str success = false end end EXPECT(BitBuf.derivedFrom(ByteBuf), true, "BitBuf from ByteBuf") EXPECT(ByteBufLittleEndian.derivedFrom(ByteBuf), true, "ByteBufLittleEndian from ByteBuf") EXPECT(ByteBufNativeEndian.derivedFrom(ByteBuf), true, "ByteBufNativeEndian from ByteBuf") EXPECT(ByteBufBigEndian.derivedFrom(ByteBuf), true, "ByteBufBigEndian from ByteBuf") EXPECT(ByteBufReverseEndian.derivedFrom(ByteBuf), true, "ByteBufReverseEndian from ByteBuf") bb = BitBuf(3) // wrong initial size (internal array type is uint32 or uint64) EXPECT(bb.capacity() % 4, 0, "BitBuf capacity alignment") fa = [1,2,3] bb.write(fa) for i = 0 to len(fa) - 1 EXPECT(bb.r64(), fa[i], "Basic BitBuf 64 bit read/write") end EXPECT(BitBuf.bitsForInt(2), 2, "required bit count") EXPECT(BitBuf.bitsForInt(7), 3, "required bit count") EXPECT(BitBuf.bitsForInt(8), 4, "required bit count") EXPECT(BitBuf.bitsForInt(255), 8, "required bit count") EXPECT(BitBuf.bitsForInt(1023), 10, "required bit count") EXPECT(BitBuf.bitsForInt(1024), 11, "required bit count") EXPECT(BitBuf.bitsForInt(0xFFFFFFFFFFFF), 48, "required bit count") bb = ByteBuf() bb.w8(200, -50).w16(40000, -20000).w32(3000000000, -1000000000) EXPECT(bb.r8(), 200, "Unsigned 8 bit read") EXPECT(bb.r8(true), -50, "Signed 8 bit read") EXPECT(bb.r16(), 40000, "Unsigned 16 bit read") EXPECT(bb.r16(true), -20000, "Signed 16 bit read") EXPECT(bb.r32(), 3000000000, "Unsigned 32 bit read") EXPECT(bb.r32(true), -1000000000, "Signed 32 bit read") // if errors occur here, this is possibly because of wrong compiler optimization d1 = ByteBuf().wd(5.55555).toString() d2 = ByteBuf().write(5.55555).toString() d3 = BitBuf().wd(5.55555).toString() d4 = BitBuf().write(5.55555).toString() EXPECT(d2, d1, "ByteBuf numeric wd() equal to write()") EXPECT(d3, d4, "BitBuf numeric wd() equal to write()") EXPECT(d3, d1, "ByteBuf numeric wd() equal to BitBuf wd()") EXPECT(d4, d2, "ByteBuf numeric write() equal to BitBuf write()") bb = BitBuf() fa = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0] cmp = { a,b => abs(a-b) < 0.0000001 } for f in fa: bb.wf(f) for i = 0 to len(fa) - 1 f = bb.rf() ck = ByteBufNativeEndian(8).wf(fa[i]) fc = ck.rf() EXPECTDIFF(f, fc, 0.0000001, "BitBuf vs ByteBuf numeric precision") end function bitstr(bb) s = "" bb.rposBits(0) i = 0 while i < bb.sizeBits() bit = bb.rb() s += (bit ? "1" : "0") i++ end return s end bb = ByteBufLittleEndian() bb.w8(8).w16(16).w32(32).w64(64) EXPECT(bb.toString(), "081000200000004000000000000000", "ByteBufLittleEndian byte order") bb = ByteBufBigEndian() bb.w8(8).w16(16).w32(32).w64(64) EXPECT(bb.toString(), "080010000000200000000000000040", "ByteBufBigEndian byte order") EXPECT(bitstr(BitBuf().wb(1,1,1,0,0,0,0,1)), "11100001", "BitBuf bool 1-bit write") EXPECT(bitstr(BitBuf().bitCount(3).writeBits(0)), "000", "BitBuf fixed-bit write (1)") EXPECT(bitstr(BitBuf().bitCount(3).writeBits(0,1)), "000100", "BitBuf fixed-bit write (2)") EXPECT(bitstr(BitBuf().bitCount(3).writeBits(0,1,2)), "000100010", "BitBuf fixed-bit write (3)") EXPECT(bitstr(BitBuf().bitCount(3).writeBits(0,1,2,3)), "000100010110", "BitBuf fixed-bit write (4)") EXPECT(bitstr(BitBuf().bitCount(3).writeBits(0,1,2,3,4)), "000100010110001", "BitBuf fixed-bit write (5)") EXPECT(bitstr(BitBuf().bitCount(3).writeBits(0,1,2,3,4,5)), "000100010110001101", "BitBuf fixed-bit write (6)") EXPECT(bitstr(BitBuf().bitCount(3).writeBits(0,1,2,3,4,5,6)), "000100010110001101011", "BitBuf fixed-bit write (7)") EXPECT(bitstr(BitBuf().bitCount(3).writeBits(0,1,2,3,4,5,6,7)), "000100010110001101011111", "BitBuf fixed-bit write (8)") bb = ByteBuf().write("abc").writeNoNT("ABC") EXPECT(bb.readString(), "abc", "ByteBuf null-terminated string") EXPECT(bb.readString(), "ABC", "ByteBuf not null-terminated string at end of buf") EXPECT(bb.toString(), "61626300414243", "ByteBuf string, hex") bb = ByteBuf().writeNoNT("abc", "ABC").write("-") EXPECT(bb.readString(), "abcABC-", "ByteBuf not null-terminated string continous read") bb = ByteBuf(8) bb.growable(false) bb.w32(1,2) try bb.w8(3) EXPECT(false, true, "Not-growable buffer write error not raised") end mb = MemBuf(16, 1).fill(0xFF) bc = ByteBufLittleEndian(mb) // copy ba = ByteBufLittleEndian(mb, true) // adapt ba[0] = 0x04 ba[1] = 0x0A ba[5] = 0 EXPECT(mb.get(), 4, "ByteBuf adapt 1") EXPECT(bc[0], 0xFF, "ByteBuf copy 1") EXPECT(mb.get(), 10, "ByteBuf adapt 2") EXPECT(bc[1], 0xFF, "ByteBuf copy 2") EXPECT(ba.r16(), 0x0A04, "ByteBuf adapt read 1") EXPECT(bc.r16(), 0xFFFF, "ByteBuf copy read 1") // pointers + direct memory, reading bb = ByteBufLittleEndian() bb.resize(50) ba = ByteBufLittleEndian() ba.w32(0x12345678).write("extra space string------------!").w16(0xFDFE) ba.readPtr(bb.ptr(), ba.size()) EXPECT(bb.r32(), 0x12345678, "ByteBuf readPtr 1") bb.readString() EXPECT(bb.r16(), 0xFDFE, "ByteBuf readPtr 2") // pointers + direct memoty, writing ba = ByteBuf() ba.w16(0xFFFF).w8(1).w32(0x33445566).w8(0xDD).w16(0xEEEE) bb = ByteBuf() bb.writePtr(ba.ptr() + 3, 5) // skip first 3 bytes and last 2 byte EXPECT(bb.r32(), 0x33445566, "writePtr 1") EXPECT(bb.r8(), 0xDD, "writePtr 2") EXPECT(bb.readable(), 0, "writePtr bytecount") bc = ByteBufReverseEndian() bc.writePtr(ba.ptr() + 3, 5) // same as above EXPECT(bc.r32(), 0x66554433, "writePtr 1 reversed endian") EXPECT(bc.r8(), 0xDD, "writePtr 2 reversed endian") // read to membuf mb = MemBuf(5, 4) // 5x int32 mb[0] = 0x01020304 mb.position(1) bb = ByteBufLittleEndian().w32(5,6,7,8,9) v = bb.readToBuf(mb, 100) EXPECT(v, 16, "readToBuf(LE, WS 4 MB) limit") EXPECT(mb[0], 0x01020304, "readToBuf(LE, WS 4 MB) position()") EXPECT(mb[4], 8, "readToBuf(LE, WS 4 MB) end") mb = MemBuf(5, 4) // 5x int32 mb[0] = 0x01020304 mb.position(1) bb = ByteBufBigEndian().w32(5,6,7,8,9) v = bb.readToBuf(mb, 100) EXPECT(v, 16, "readToBuf(BE, WS 4 MB) limit") EXPECT(mb[0], 0x01020304, "readToBuf(BE, WS 4 MB) position()") EXPECT(mb[4], 8, "readToBuf(BE, WS 4 MB) end") mb = MemBuf(5, 1) // 5x int8 mb[0] = 0x22 mb.position(1) bb = ByteBufLittleEndian().w8(5,6,7,8,9) v = bb.readToBuf(mb) // no 2nd arg here intentionally EXPECT(v, 4, "readToBuf(WS 1 MB) limit") EXPECT(mb[0], 0x22, "readToBuf(WS 1 MB) position()") EXPECT(mb[4], 8, "readToBuf(WS 1 MB) end") bb = ByteBufLittleEndian().w8(2,3,4,5,6).writeNoNT("abcdefghijklmnopqrs") ng = ByteBufLittleEndian().growable(false).resize(15) ng.w8(0xFD, 0xFE) v = bb.readToBuf(ng, 3) // just 3 bytes EXPECT(v, 3, "readToBuf 3 bytes") EXPECT(ng[0], 0xFD, "readToBuf to ByteBuf read 3, wrongly overwritten") EXPECT(ng[4], 4, "readToBuf to ByteBuf read 3, append") v = bb.readToBuf(ng) // (ng is 5 bytes now) ... append the rest, up to the limit EXPECT(v, 10, "readToBuf up to limit") EXPECT(ng[4], 4, "readToBuf limit, unchanged prev val") EXPECT(ng[14], ord("h"), "readToBuf limit, at limit") ng.growable(true) v = bb.readToBuf(ng) // (ng is 15 bytes now) ... append the rest, all remaining EXPECT(v, 11, "readToBuf up to limit") EXPECT(ng[14], ord("h"), "readToBuf fully, unchanged prev val") EXPECT(ng[14+11], ord("s"), "readToBuf fully, unchanged prev val") EXPECT(ng.size(), 2+5+19, "readToBuf mb final size") EXPECT(bb.readable(), 0, "readToBuf src buf empty") // memory region shifting test (right by 1 bit) bb = BitBuf().w8(1,2,4,6,10,0x20,1) //> bitstr(bb); bb.rpos(0) // for clarification, uncomment this line EXPECT(bb.rb(), true, "BitBuf mem region Rshift init") // this is the 1, and shifts the frame t = ByteBufLittleEndian() bb.readToBuf(t) EXPECT(t.r8(), 0, "BitBuf mem region Rshift 1") EXPECT(t.r8(), 1, "BitBuf mem region Rshift 2") EXPECT(t.r8(), 2, "BitBuf mem region Rshift 3") EXPECT(t.r8(), 3, "BitBuf mem region Rshift 4") EXPECT(t.r8(), 5, "BitBuf mem region Rshift 5") EXPECT(t.r8(), 0x10 + 0x80, "BitBuf mem region Rshift 6") // (0x20 >> 1) + (1 shifted to right once, into byte beeing read) EXPECT(bb.readableBits(), 7, "BitBuf mem region Rshift 1 bit less") bb.wb(true) // 1 extra bit, will be highest bb.readToBuf(t) EXPECT(t.r8(), 0x80, "BitBuf mem region Rshift extra bit") // memory region shifting test (left by 1 bit) bb = BitBuf().wb(false).w8(1,2,4,6,10,0x20) //> bitstr(bb); bb.rposBits(0) // for clarification, uncomment this line t = ByteBufLittleEndian() bb.readToBuf(t) EXPECT(t.r8(), 2, "BitBuf mem region Lshift 1") EXPECT(t.r8(), 4, "BitBuf mem region Lshift 2") EXPECT(t.r8(), 8, "BitBuf mem region Lshift 3") EXPECT(t.r8(), 12, "BitBuf mem region Lshift 4") EXPECT(t.r8(), 20, "BitBuf mem region Lshift 5") EXPECT(t.r8(), 0x40, "BitBuf mem region Lshift 6") EXPECT(bb.readableBits(), 1, "BitBuf mem region Lshift last bit") bb.bitCount(7).writeBits(0) bb.readToBuf(t) EXPECT(t.r8(), 0, "BitBuf mem region Lshift extra bits") // toMemBuf tests bb = ByteBuf(100).w32(0xFF0000FF) for i = 0 to 90: bb.w8(i) // fill up a bit mb = bb.toMemBuf() // default behavior: adopt EXPECT(mb.ptr(), bb.ptr(), "toMemBuf adopt ptr equality") cp = bb.toMemBuf(true) // copy; ptrs must be different if cp.ptr() == bb.ptr() success = false > "toMemBuf copy ptr must be different" end p = bb.ptr() bb.write("this enlarges the buffer and forces reallocation") // now mb is invalid; it still uses the old pointer prior to re-allocation if bb.ptr() == p success = false > "buffer enlarged, but not reallocated" end EXPECT(mb.ptr(), p, "toMemBuf adopt ptr after reallocation") // must be same old (but now invalid) ptr EXPECT(ByteBuf(cp,true).r32(), 0xFF0000FF, "toMemBuf re-adopt read (copy)") > " === DEPENDENCY TEST vs. GC ===" // ByteBuf dependency smoke test (still required bufs may not be garbaged) mb = MemBuf(50000, 4).fill(0xFFFFFFFF) // a large object, interesting for the GC mb.put(42) b = ByteBufNativeEndian(mb, true) mb = nil for i = 0 to 500 EXPECT(b.r32(), 42, "ByteBuf recursive adapt dependency test") b = ByteBufNativeEndian(b, true).growable(false) // adapt end GC.perform(true) // if dependencies are not set up correctly, this may cause multi-free of the internal memory, and segfault x = b.r32() // if mb was garbaged, this will likely segfault EXPECT(x, 42, "ByteBuf recursive adapt dependency test, final") > " === STACK OVERFLOW TEST vs. GC ===" b = ByteBufNativeEndian(b, true).growable(false) // adapt for i = 0 to 100000 EXPECT(b.r32(), 42, "ByteBuf recursive adapt GC stack overflow test") b = ByteBufNativeEndian(b, true).growable(false) // adapt end GC.perform(true) // if a long dependency chain was built up, this must not cause GC stack overflow x = b.r32() // if mb was garbaged, this will likely segfault EXPECT(x, 42, "ByteBuf recursive adapt stack overflow test, final") b = nil if success > " === BASIC TESTS SUCCESSFUL ===" else > " === BASIC TESTS FAILED ===" exit() end // stress test below function genRString() lim = random(20) s = strBuffer(lim + 1) while lim lim-- s %= random(97, 112) end s.charSize(1) return s end typenames = [ "bool" ,"i8 " ,"i16 " ,"i32 " ,"i64 " ,"flt " ,"dbl " ,"str " ] writefunc = [ { b,v => b.wb(v) } ,{ b,v => b.w8(v) } ,{ b,v => b.w16(v) } ,{ b,v => b.w32(v) } ,{ b,v => b.w64(v) } ,{ b,v => b.wf(v) } ,{ b,v => b.wd(v) } ,{ b,v => b.write(v) } ] readfunc = [ { b => b.rb() } ,{ b => b.r8() } ,{ b => b.r16() } ,{ b => b.r32() } ,{ b => b.r64() } ,{ b => b.rf() } ,{ b => b.rd() } ,{ b => b.readString(1, 50) } ] genfunc = [ { => random(1) ? true : false } ,[random, 0, 255] ,[random, 0, 65535] ,[random, 0, 4294967295] ,[random, 0, 1152921504606846976] ,{ => random() * 100} ,{ => random() * 1000} , genRString ] cmpfunc = [ { a, b => a == b } ,{ a, b => a == b } ,{ a, b => a == b } ,{ a, b => a == b } ,{ a, b => a == b } ,{ a, b => abs(a-b) < 0.00001 } // float precision is never so good ,{ a, b, strict => strict ? (a == b) : (abs(a-b) < 0.000000001) } ,{ a, b => a == b } ] function buftest(bb, strict) lim = random(1500) + 1 vals = arrayBuffer(lim) ps = arrayBuffer(lim) x = len(writefunc) - 1 i = 0 while i < lim p = random(x) item = genfunc[p]() vals[i] = item ps[i] = p tn = typenames[p] writefunc[p](bb, item) i++ end i = 0 while i < lim item = vals[i] p = ps[i] r = readfunc[p](bb) tn = typenames[p] if not cmpfunc[p](item,r,strict) > "\n\n", bb.toString() > @ "ERROR! ty: '$tn', i = $i, p = $p, should be: '$item', is: '$r'" return false end i++ end if bb.readable() > "Readable, should not be: wpos = ", bb.wpos(), ", rpos = ", bb.rpos(), ", capacity = ", bb.capacity(), ", size = ", bb.size(), ", readable = ", bb.readable() if(bb provides sizeBits) // is BitBuf > "Readable, should not be: wposBits = ", bb.wposBits(), ", rpos = ", bb.rposBits(), ", sizeBits = ", bb.sizeBits(), ", readableBits = ", bb.readableBits() end return false end return true end > " === GENERIC STRESS TEST ===" lim = 500 bufs = [ [BitBuf, false], [ByteBuf, false], [ByteBufNativeEndian, true], [ByteBufLittleEndian, false], [ByteBufBigEndian, false], [ByteBufReverseEndian, false] ] for cons, strict in bufs for i = 0 to lim >> "\r[", cons, "] ", round(i / lim * 100), " % " if not buftest(cons(), strict) > "*TEST ERROR*" success = false break end end > " - OK!" end > " === BITBUF SEQUENCE STRESS TEST ===" lim = 200 for i = 1 to 63 // inclusive - cause all different possibilities for shifted patterns // we use the same random seed for each pattern, so the data are always the same, just shifted by different byte amounts randomSeed(873876698769) for j = 0 to lim >> "\r[BitBuf+", i, "] ", round(j / lim * 100), " % " bb = BitBuf() // this will have the whole buf shifted by i bytes bb.bitCount(i) bb.writeBits(0x0123456789abcdef) junk = bb.readBits() if not buftest(bb, false) > "*TEST ERROR*" success = false break end end > " - OK!" end if success > "==== TEST GOOD ====" else > "==== TEST FAILED ====" end tests/feathers/samples/bytebuf_socket_wrapper.fal000066400000000000000000000025351176363201700227340ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - Samples FILE: bytebuf_socket_wrapper.fal Bufext module example This file provides an example about how to send binary data over a socket easily. For a quick test, use netcat as echo server: 'nc -l -p 9909' ------------------------------------------------------------------- Author: Maximilian Malek Begin: Thu, 1 Nov 2010 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load bufext load socket host = "localhost" port = "9909" socket = TCPSocket() socket.setTimeout(-1) > "Waiting for connection..." if not socket.connect(host, port) > "Can't connect to ", host, ":", port, ", exiting" exit() end bs = ByteBufSocketWrapper(socket) // send some random binary data bs.write("#1", "Hello", "world!\n", 3.141596) bs.w32(0x616263FF).write("\n\n") bs.send() bs.write("#2").w8(1,2,3,4).w16(1234, 9999) bs.writeNoNT("test", "end", "#]") bs.send() > "Data sent!" bs.close() class ByteBufSocketWrapper(s) from ByteBuf sock = nil init self.sock = s end function send() self.sock.send(self.toMemBuf()) self.reset() // set read/write positions and size to 0 end function close(): self.sock.close() end tests/feathers/samples/chatClient.fal000066400000000000000000000047501176363201700202430ustar00rootroot00000000000000#!/usr/local/bin/falcon /* FALCON - Samples FILE: chatClient.fal A simple client meant to connect to a chatServer. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2006-05-11 12:35 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load socket /******************************** A simple utility *********************************/ class ClientTCPSocket( address, port ) from TCPSocket( address, port ) data = "" function _readline() readIn = "" self.recv( readIn, 1024 ) return readIn end function readline() readIn = "" self.recv( readIn, 1024 ) return readIn end end /******************************** Main program *********************************/ // Send a login to the server if len( args ) != 3 printl( "Usage: chatClient.fal
    " ) exit(1) end try socket = ClientTCPSocket() socket.setTimeout( 2000 ) socket.connect( args[0], args[1] ) // get Server hailing data = socket.readline() if data == nil: raise Error( 0, "Timeout in server response" ) printl( "Server reply: ", data ) printl( "Sending your nickname." ) socket.send( "[IAM_]" + args[2] +"\r\n" ) data = socket.readline() if data == nil: raise Error( 0, "Timeout in server response" ) printl( "Server reply: ", data ) if "accepted" notin data exit( 1 ) end catch in error printl( "Error in login sequence. ") printl( error.toString() ) socket.dispose() exit( 0 ) end printl( "Write something to send a message to all" ) printl( "Write nick: mesasge to senda a private message" ) printl( "Press ctrl + C to end" ) istream = stdIn() print( "> " ) while true if istream.readAvailable(0) message = input() print( "> " ) if ":" in message message = "[PRIV]" + message + "\r\n" else message = "[WALL]" + message + "\r\n" end try socket.send( message ) catch in error printl( "Error while sending message: ", error.toString() ) end elif socket.readAvailable(0) inline = socket.readline() if not inline printl( "Server died" ) socket.dispose() exit(0) end printl( "\r", inline ) print( "> " ) else sleep( 0.01 ) end end /* end of chatClient.fal */ tests/feathers/samples/chatServer.fal000066400000000000000000000166511176363201700202760ustar00rootroot00000000000000#!/usr/local/bin/falcon /* FALCON - Samples FILE: chatServer.fal A simple server for chatClient.fal samples. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2006-05-11 12:35 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load socket /******************************** This class stores outgoing messages *********************************/ class CliMessage( cli, msg ) client = cli message = msg end /******************************** This class register clients. *********************************/ class Client( com_) name = "unknown" com = com_ idleTime = 0 messages = [] msgIn = "" function alive() self.idleTime = seconds() end /** Sends a mesasge. */ function send( msg ) sent = self.com.send( msg ) // all the message has been sent? -- if no, resend while sent < len( msg ) msg = msg[sent+1:] sent = self.com.send( msg ) end end end /******************************** output functions *********************************/ warn = [ printl, "WARN: " ] function messageReceived( idFrom, idTo ) print( "MSG ", idFrom, "->", idTo, ": " ) params = [] for i in [2 : paramCount()] print( paramNumber(i) ) end printl() end /******************************** Manage Logins *********************************/ function manageLogins() global clients global waitingClients cliData = strBuffer(256) for cli in waitingClients try if cli.com.readAvailable(0) size = cli.com.recv( cliData ) if size == 0 // client closed cli.com.dispose() continue dropping else if cliData[0:6] == "[IAM_]" cliId = cliData[6:-2] if cliId in clients cli.messages += CliMessage( nil, "[SERV]ID Already present." ) else // and accept the new one cli.messages += CliMessage( nil, "[SERV]ID " + cliId + " accepted." ) // notify all the clients names = "" for name, tgcli in clients // tell the client about the others tgcli.messages += CliMessage( nil, "[SERV]Client \"" + cliId + "\" has joined." ) names += name formiddle: names += ", " forlast: names += "." end cli.messages += CliMessage( nil, "[SERV]Nick list: "+ names ) // accept in. cli.name = cliId clients[ cliId ] = cli warn( "client ", cliId, " logged in.") // remove from waiting clients continue dropping end else cli.messages += CliMessage( nil, "[SERV]You must send IAM." ) end end end catch in error warn( "Error while receiving client login: ", error.toString() ) end end end /******************************** Manage incoming data *********************************/ function manageIncoming() global clients cliData = strBuffer(1500) gone = [] for name, cli in clients com = cli.com try if com.readAvailable(0) size = com.recv( cliData ) if size == 0 // he's gone warn( "Client ", name, " closed connection" ) gone += name com.dispose() else cli.msgIn += cliData if not parseMessage( cli ) gone += name com.close() else cli.alive() end end end catch in error warn( "Error while reading from client: ", error.toString() ) gone += name com.dispose() end end // remove gone elements for name in gone dictRemove( clients, name ) end // and notify clients for name,cli in clients for name in gone cli.messages += CliMessage( nil, "[SERV]Client \"" + name + "\" is gone." ) end end end /******************************** Manage outgoing data *********************************/ function manageOutgoing() global clients // and notify clients for name, cli in clients if len( cli.messages ) != 0 and cli.com.writeAvailable(0) error = 0 for climsg in cli.messages try cli.send( climsg.message + "\r\n" ) > "Sending ", climsg.message catch in error warn( "Error while writing to client: ", error.toString() ) cli.com.dispose() error = 1 break end end // empty messages cli.messages = [] // drop the client on error if error: continue dropping end end end /******************************** Manage Incoming Messages *********************************/ function parseMessage( cli ) global clients // complete message? message = cli.msgIn pos = strFind( message, "\r\n" ) // still nothing if pos == -1 if len( message ) > 10000: return false return true end // ok, we have a complete line. if pos == len(message) - 2 cli.msgIn = "" else cli.msgIn = message[pos + 2:] end message = message[0:pos] // let's see what the client want to do. switch( message[0:6] ) case "[PONG]" return true case "[WALL]" //send = message[8:] for name, clitg in clients if cli.name != clitg.name clitg.messages += CliMessage( cli, message ) end end case "[PRIV]" // get the person to which the msg should go. try pos = strFind( message, ":" ) username = message[6:pos] clitg = clients[ username ] // may throw if pos != len( message ): clitg.messages += CliMessage( cli, "<" + cli.name + ">" + message[pos+1:] ) catch cli.messages += CliMessage( cli, "[SERV]Target client does not exist / malformed message." ) end case "[BYE_]" //close return false default printl( "Default" ) clitg = clients[ username ] // may throw clitg.messages += CliMessage( cli, "[SERV]Malformed message" ) end return true end /******************************** Main program *********************************/ // create and bind the server server = TCPServer() if len( args ) == 1 server.bind( args[0] ) elif len( args ) == 2 server.bind( args[0], args[1] ) else printl( "Usage: chatServer.fal address port" ) printl( " or chatServer.fal port" ) exit(1) end // registered clients clients = [=>] // clients waiting to be logged in. waitingClients = [] while true client = server.accept(0) if client != nil printl( "New connection from ", client.getHost(), ":", client.getService() ); client.send( "[SERV]Hello from server.\r\n" ); clientData = Client( client ) waitingClients += clientData end // first manage login attemps manageLogins() manageIncoming() manageOutgoing() // sleep a bit sleep( 0.10 ) end tests/feathers/samples/custom_hash.fal000066400000000000000000000123741176363201700205030ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - Samples FILE: custom_hash.fal Calculate Custom hash of a file This file provides an example for a rather simple hash algoritm, and how to implement a custom hash by overloding the HashBase class. (Take special note on comments marked with ** ) It is not supposed to be strong or collision resistant, just serve as an example. Do not try to hash large files with this, as it is very slow! (30 seconds for a 500kB file on a Core2 with 2.26 GHz) ------------------------------------------------------------------- Author: Maximilian Malek Begin: 16 Apr 2010 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load hash class MyHash from HashBase state = nil // internal state pos = 0 // internal state position shift = 0 // internal shift counter outp = nil // the result from finalize() will be put here halfsize = 0 // always bytes() / 2 // ** the init block sets up the internal state and fills the buffer with its initial data, if required init self.halfsize = self.bytes() / 2 self.state = MemBuf(self.halfsize, 4) self.state.fill(0xdeadc0de) // prevent memory from beeing un-initialized for i = 1 to self.halfsize - 1 x = self.state[i - 1] self.state[i] = x ^^ crc32int(x) end end // ** bytes() -- tells the module how long the result MemBuf will be. // ** must be overloaded and return a constant integer. // Note: for this algorithm, the byte length can be chosen freely, as long as it is >= 4 and an even number. function bytes(): return 24 // ** must be overloaded and return a MemBuf with wordSize 1 and length equal to bytes() // ** this method is also internally used to retrieve the data for toInt() and toString() function toMemBuf(): return self.outp // ** data that are put into to update() are internally converted to one or more MemBufs, // ** which will be passed to process() by the module. // ** most of the hash function logic takes place here. function process(buf) lim = len(buf) - 1 while buf.remaining() c = buf.get() m = c % 32 for round = 0 to 5 A = self.state[self.pos] newpos = (self.pos + 1) % self.halfsize B = self.state[newpos] C = self.rotr32(B, m) ^^ A B = self.rotl32(A, 1) A = C ^^ ((c ^^ round) << self.shift++); self.state[self.pos] = A self.state[newpos] = B self.shift %= 24 // byte can be max 0xFF, can shift max. 24 bits without losing data self.pos = newpos end end end // ** finalize() is called by the module directly before toString(), toInt(), or toMemBuf(). // ** This method is intended to finish up unprocessed data, or apply last transformations to the internal state, // ** and to make the actual hash digest available to the module. // ** There is no special checking needed to prevent finalize() getting called multiple times, // ** because this is checked by the module, which will allow it // ** to be called only ONCE, and also take care that the update() methods // ** can't be called anymore. function finalize() // must be declared, as it is invoked by the module when the hash digest is requested b = self.bytes() self.outp = MemBuf(b, 1) self.outp.fill(0) // prevent memory from beeing un-initialized k = 0 for round = 0 to 4 for i = 0 to self.halfsize - 1 x = self.state[i] for j = 0 to 8 self.outp[k++] ^= (x >> (j * 4)) && 0xFF k %= b end end end end // helpers: bit rotate left and right function rotl32(v, i): return ((v << i) || (v >> (31 - i))) && 0xFFFFFFFF function rotr32(v, i): return ((v >> i) || (v << (31 - i))) && 0xFFFFFFFF end // helper function for int->int crc32 calculation function crc32int(i) c = CRC32() c.updateInt(i, 4) return c.toInt() end // the code below is copied from md5.fal, // except that MD5Hash has been replaced with MyHash if args.len() == 0 > "Usage: Pass a filename as parameter to calculate a hash value (example algorithm)" else filename = args[0] bufsize = 65536 // 64 kB block size try file = InputStream(filename) catch > "Error opening file: ", filename return end buf = MemBuf(bufsize, 1) hash = MyHash() while not file.eof() bytes = file.read(buf, bufsize) // read the file in blocks // prepare buffer for hashing: buf.position(0) // from the start... buf.limit(bytes) // .. up to the amount of bytes read (if the block was < bufsize) hash.update(buf) // hash the block buf.rewind() // reset position for next read end > hash // output as hex string end tests/feathers/samples/echoproc.fal000066400000000000000000000025111176363201700177600ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - Samples FILE: echoproc.fal Simple process wrapper; this is similar to procload, but doesn't use the system shell to load the process. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load process if args.len() == 0 > "Please, specify the process to be launched. Use a single string.\n" + " Example: \n" + " echoproc.fal \"ls -l /usr/lib\"\n" return 1 end try > "Starting ... \"", args[0], "\"" > "---------------------------------------------------------------" proc = Process( args[0], PROCESS_MERGE_AUX || PROCESS_SINK_INPUT ) procout = proc.getOutput() while (result = proc.value()) if procout.readAvailable( 0.1 ) >> procout.grab( 1024 ) end end > > "---------------------------------------------------------------" > "Exit value: ", result > "Test complete" catch in error > "Catched error during processing!!:\n", error > "------------------------------------" > "Terminating with error" exit(1) end exit(0) /* end of echoproc.fal */ tests/feathers/samples/hashtest.fal000066400000000000000000000444771176363201700200220ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - Samples FILE: hashtest.fal Hash module tests Every hash should produce the same results for the same input data, on all platforms. This script tests the correctness of the hashes output, and checks for errors that may occur because of weird input or misuse. A benchmark function is included. ------------------------------------------------------------------- Author: Maximilian Malek Begin: Thu, 25 Mar 2010 02:46:10 +0100 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ BENCHMARK = false // set this to true to run a benchmark if the tests finish successfully load hash function chsplit(str) s = len(str) if not s: return nil arr = arrayBuffer(s) for i = 0 to s - 1: arr[i] = str[i] return arr end function expect(what, expected, desc) ok = true counter = 0 for it in what counter++ if(it == expected) > "[", desc, ": ", counter, "] success." else > "[", desc, ": ", counter, "] FAIL! Expected: ", expected, ", got: ", it ok = false end end return ok end // alg: hash class constructor, items: list or array of items to be hashed (preferrably strings or membufs) function myhash(alg, items) h = alg() for i in items: h.update(i) return h.toString() end // only hashes with an output size <= 8 bytes support toInt() function hashInt(alg, items) h = alg() for i in items: h.update(i) return h.toInt() end function hashBuf(alg, items) h = alg() for i in items: h.update(i) return h.toMemBuf() end function li2buf(s, li) pos = 0 mb = MemBuf(len(li), s) for e in li: mb[pos++] = e return mb end function bufcmp(a,b) la = len(a) if (la != len(b)) or (a.wordSize() != b.wordSize()): return false for i = 0 to la - 1: if a[i] != b[i]: return false return true end function test_hashes() // verification table table = [ "" => [ [CRC32, "CRC32", "00000000"], [Adler32, "Adler32", "00000001"], [SHA1Hash, "SHA1", "da39a3ee5e6b4b0d3255bfef95601890afd80709"], [SHA224Hash, "SHA224", "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"], [SHA256Hash, "SHA256", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"], [SHA384Hash, "SHA384", "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"], [SHA512Hash, "SHA512", "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"], [MD2Hash, "MD2", "8350e5a3e24c153df2275c9f80692773"], [MD4Hash, "MD4", "31d6cfe0d16ae931b73c59d7e0c089c0"], [MD5Hash, "MD5", "d41d8cd98f00b204e9800998ecf8427e"], [WhirlpoolHash, "Whirlpool", "19fa61d75522a4669b44e39c1d2e1726c530232130d407f89afee0964997f7a73e83be698b288febcf88e3e03c4f0757ea8964e59b63d93708b138cc42a66eb3"], [TigerHash, "Tiger", "3293ac630c13f0245f92bbb1766e16167a4e58492dde73f3"], [RIPEMD160Hash, "RIPEMD160", "9c1185a5c5e9fc54612808977ee8f548b2258d31"] ], "abc" => [ [CRC32, "CRC32", "352441c2"], [Adler32, "Adler32", "024d0127"], [SHA1Hash, "SHA1", "a9993e364706816aba3e25717850c26c9cd0d89d"], [SHA224Hash, "SHA224", "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7"], [SHA256Hash, "SHA256", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"], [SHA384Hash, "SHA384", "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"], [SHA512Hash, "SHA512", "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"], [MD2Hash, "MD2", "da853b0d3f88d99b30283a69e6ded6bb"], [MD4Hash, "MD4", "a448017aaf21d8525fc10ae87aa6729d"], [MD5Hash, "MD5", "900150983cd24fb0d6963f7d28e17f72"], [WhirlpoolHash, "Whirlpool", "4e2448a4c6f486bb16b6562c73b4020bf3043e3a731bce721ae1b303d97e6d4c7181eebdb6c57e277d0e34957114cbd6c797fc9d95d8b582d225292076d4eef5"], [TigerHash, "Tiger", "2aab1484e8c158f2bfb8c5ff41b57a525129131c957b5f93"], [RIPEMD160Hash, "RIPEMD160", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"] ], "The quick brown fox jumps over the lazy dog" => [ [CRC32, "CRC32", "414fa339"], [Adler32, "Adler32", "5bdc0fda"], [SHA1Hash, "SHA1", "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"], [SHA224Hash, "SHA224", "730e109bd7a8a32b1cb9d9a09aa2325d2430587ddbc0c38bad911525"], [SHA256Hash, "SHA256", "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"], [SHA384Hash, "SHA384", "ca737f1014a48f4c0b6dd43cb177b0afd9e5169367544c494011e3317dbf9a509cb1e5dc1e85a941bbee3d7f2afbc9b1"], [SHA512Hash, "SHA512", "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6"], [MD2Hash, "MD2", "03d85a0d629d2c442e987525319fc471"], [MD4Hash, "MD4", "1bee69a46ba811185c194762abaeae90"], [MD5Hash, "MD5", "9e107d9d372bb6826bd81d3542a419d6"], [WhirlpoolHash, "Whirlpool", "b97de512e91e3828b40d2b0fdce9ceb3c4a71f9bea8d88e75c4fa854df36725fd2b52eb6544edcacd6f8beddfea403cb55ae31f03ad62a5ef54e42ee82c3fb35"], [TigerHash, "Tiger", "6d12a41e72e644f017b6f0e2f7b44c6285f06dd5d2c5b075"], [RIPEMD160Hash, "RIPEMD160", "37f332f68db77bd9d7edd4969571ad671cf9dd3b"] ] ] supp = getSupportedHashes() > "Supported hashes:" for s in supp: > " * ", s good = true // check against table values for idx, reslist in table > "--- Test for: '", idx, "' ---" for e in reslist alg, desc, result = e a = myhash(alg, [idx]) // test with only 1 arg (1x UpdateData() call) b = myhash(alg, chsplit(idx)) // hash each char seperately (many UpdateData() calls) c = myhash(alg, [idx.toMemBuf()]) // convert to MemBuf if not expect([a,b,c], result, desc): good = false end end > "--- Checking toInt() methods ---" txt = "The quick brown fox jumps over the lazy dog" if not expect([hashInt(CRC32, [txt])], 1095738169, "CRC32"): good = false if not expect([hashInt(Adler32, [txt])], 1541148634, "Adler32"): good = false if not expect([hashInt(NopHash2Bytes, [nil])], 1, "NopHash (2 bytes endian test)"): good = false if not expect([hashInt(NopHash16Bytes, [nil])], 1, "NopHash (16 bytes endian test)"): good = false > "--- Checking for correct MemBuf output ---" sha1buf_expected = li2buf(1, .[0xA9 0x99 0x3E 0x36 0x47 0x06 0x81 0x6A 0xBA 0x3E 0x25 0x71 0x78 0x50 0xC2 0x6C 0x9C 0xD0 0xD8 0x9D]) sha1buf = hashBuf(SHA1Hash, ["abc"]) if not bufcmp(sha1buf, sha1buf_expected) > "[SHA1] FAIL!" >> "Expected: "; inspect(sha1buf_expected) >> "Got: "; inspect(sha1buf) good = false else > "MemBuf output success." end > "--- Checking membuf input ---" expected = "d702662cf2d55246776f96c51917ea27d60ec93b" // the memory layout below is in little endian mb1 = myhash(SHA1Hash, [li2buf(1, .[0x61 0x62 0x63 0x64 0x41 0x42 0x43 0x44])]) mb2 = myhash(SHA1Hash, [li2buf(2, .[0x6261 0x6463 0x4241 0x4443])]) mb3 = myhash(SHA1Hash, [li2buf(3, .[0x636261 0x424164]), li2buf(2, .[0x4443])]) mb4 = myhash(SHA1Hash, [li2buf(4, .[0x64636261 0x44434241])]) if not expect([myhash(SHA1Hash, ["abcdABCD"])], expected, "SHA1 string"): good = false if not expect([mb1,mb2,mb3,mb4], expected, "SHA1 MemBuf"): good = false > "--- Checking multi-byte string input ---" expected = "f930a2079c45dd97608325d73a24335cefa1846e" str = "abc" str.charSize(4) if not expect([myhash(SHA1Hash, [str])], expected, "SHA1 multibyte string"): good = false if not expect([myhash(SHA1Hash, [str.toMemBuf()])], expected, "SHA1 multibyte string (as MemBuf)"): good = false > "--- Checking direct hash hashing ---" expected = "d702662cf2d55246776f96c51917ea27d60ec93b" sha = SHA1Hash() sha.update("abcdABCD") if not expect([myhash(SHA1Hash, [sha])], myhash(SHA1Hash, [sha.toMemBuf()]), "SHA1 hash"): good = false > "--- Checking sequence hashing ---" expected = "d702662cf2d55246776f96c51917ea27d60ec93b" arr = [[[["ab"], ["c", "d"], [ ["AB", ["C", "D"] ] ] ]]] li = List("ab", "cd", "A", List("B", "CD")) dict = [ 1 => "ab", 2 => "cd", 3 => [ 1 => "AB", 2 => "CD"] ] // ordered dictionary dictu = [ 2 => "cd", 1 => "ab", 3 => [ 2 => "CD", 1 => "AB"] ] // rely on correct ordering by dict mixed = [ 1 => "a", 2 => List("b", ["c", "d", li2buf(1, .[0x41 0x42 0x43 0x44])])] if not expect([myhash(SHA1Hash, [arr])], expected, "SHA1 array"): good = false if not expect([myhash(SHA1Hash, [li])], expected, "SHA1 list"): good = false if not expect([myhash(SHA1Hash, [dict])], expected, "SHA1 dict"): good = false if not expect([myhash(SHA1Hash, [dictu])], expected, "SHA1 dict2"): good = false if not expect([myhash(SHA1Hash, [mixed])], expected, "SHA1 mixed"): good = false > "--- Checking references as input ---" expected = "d702662cf2d55246776f96c51917ea27d60ec93b" val = "abcdABCD" ref1 = $val ref2 = $ref1 if not expect([myhash(SHA1Hash, [ref1])], expected, "SHA1 ref"): good = false if not expect([myhash(SHA1Hash, [ref2])], expected, "SHA1 double-ref"): good = false > "--- Checking updateInt() ---" expected = "d702662cf2d55246776f96c51917ea27d60ec93b" sha = SHA1Hash() sha.update().update().update() // this should not throw sha.updateInt(0x61, 1).updateInt(0x62, 1).updateInt(0x6463, 2).updateInt(0x44434241, 4) // little endian if not expect([sha.toString()], expected, "SHA1 int8/int16/int32"): good = false sha = SHA1Hash() sha.updateInt(0x4443424164636261, 8) // little endian if not expect([sha.toString()], expected, "SHA1 int64"): good = false > "--- Checking for Nil ---" expected = "da39a3ee5e6b4b0d3255bfef95601890afd80709" if not expect([myhash(SHA1Hash, [nil])], expected, "SHA1 nil"): good = false if not expect([myhash(SHA1Hash, [nil, [1 => nil], [[nil]], List(nil,[nil, nil])])], expected, "SHA1 nil seq"): good = false > "--- Checking overloadable base for misbehavior ---" hb = HashBase() try t = hb.bits() // bytes() not overloaded, must throw good = false > "FAIL! HashBase.bits() didn't raise an error" catch > "Not overloaded bytes() did throw, good." end try t = hb.update("abc") // not overloaded, must throw good = false > "FAIL! HashBase.update() didn't raise an error" catch > "Not overloaded update() did throw, good." end try t = hb.toMemBuf() // not overloaded, must throw good = false > "FAIL! HashBase.toMemBuf() didn't raise an error" catch > "Not overloaded toMemBuf() did throw, good." end > "--- Checking custom overloaded function" cx = Checksum16() // class defined below if not expect([cx.bits()], 16, "Checksum16 bits"): good = false cx.update("abcd", "ABCD") out1 = cx.toString() out2 = cx.toString() // out1 and out2 must be equal, otherwise there are internal problems if not expect([out1, out2], "cb2d", "Checksum16 double toString()"): good = false > "--- Checking for wrong return types in overloaded classes ---" try myhash(Checksum16Wrong1, "abc") // must throw good = false > "FAIL! wrong toMemBuf() return type accepted" catch > "Wrong toMemBuf() return type did throw, good." end try myhash(Checksum16Wrong2, "abc") // must throw good = false > "FAIL! wrong toMemBuf() word size accepted" catch > "Wrong toMemBuf() word size did throw, good." end try myhash(Checksum16Wrong3, "abc") // must throw good = false > "FAIL! wrong toMemBuf() byte length accepted" catch > "Wrong toMemBuf() byte length did throw, good." end try cx = Checksum16Wrong4() > cx.bits() // must throw good = false > "FAIL! wrong bytes() return type accepted" catch > "Wrong bytes() return type did throw, good." end try cx = Checksum16Wrong5() > cx.bits() // must throw good = false > "FAIL! bytes() returning 0 accepted" catch > "bytes() returning 0 did throw, good." end > "--- Checking for stack overflow detection ---" try cx = Checksum16() cy = Checksum16Wrong6() cx.update(cy) good = false > "FAIL! undetected stack overflow?!" // not sure if we can even reach this point, rather the VM would blow up in smoke at update() catch > "update() stack overflow detected, good." end > "--- Checking convenience functions & makeHash() ---" v1 = sha1("abc") // have to check only one, as the others work the same internally if(v1 != "a9993e364706816aba3e25717850c26c9cd0d89d") > "FAIL! sha1('abc') returned '", v1, "', shoudld be 'a9993e364706816aba3e25717850c26c9cd0d89d'" good = false end for e in getSupportedHashes() v1 = makeHash(e).update("abc").toString() v2 = hash(false, e, "a", ["b", "c"]) tmph = makeHash(e); tmph.update("a"); f = { ha => ha.update("b") } v3 = hash(false, f(tmph) , "c") if(v1 != v2 or v1 != v3) > "FAIL! one of [", e, "] convenience function returned wrong output!" good = false else > e, " good." end end > "--- Checking hash(true, ...) - raw MemBuf output ---" sha1buf_expected = li2buf(1, .[0xA9 0x99 0x3E 0x36 0x47 0x06 0x81 0x6A 0xBA 0x3E 0x25 0x71 0x78 0x50 0xC2 0x6C 0x9C 0xD0 0xD8 0x9D]) sha1buf = hash(true, SHA1Hash, "abc") if not bufcmp(sha1buf, sha1buf_expected) > "[SHA1] FAIL!" >> "Expected: "; inspect(sha1buf_expected) >> "Got: "; inspect(sha1buf) good = false else > "hash(true, ...) raw MemBuf output success." end > "--- Checking hash() with custom overloaded class ---" ch16 = Checksum16() v1 = ch16.update("abc").toString() v2 = hash(false, Checksum16, "abc") if(v1 != v2) > "FAIL! hash() returned wrong results! Should be: ", v1, ", was ", v2 good = false else > "result: ", v1, ", seems good" end > "--- Test complete ---" return good end // own class for a simple checksum, to test HashBase class overloading // ** process(buf), finalize() -- have to be defined // ** toMemBuf(), bytes() -- have to be overloaded class Checksum16 from HashBase val = 0xa374cf9e // state variable, for this checksum an int is sufficient, for hashes a membuf would be a better choice retbuf = nil // our result after finalize() function bytes(): return 2 function toMemBuf(): return self.retbuf function process(buf) lim = len(buf) - 1 for i = 0 to lim self.val <<= 1 self.val ^= buf.get() end end function finalize() a = (self.val >> 24) && 0xFF b = (self.val >> 16) && 0xFF c = (self.val >> 8) && 0xFF d = self.val && 0xFF self.val = 0 self.retbuf = li2buf(1, [a^^c, b^^d]) end end // wrong return type class Checksum16Wrong1 from Checksum16 function toMemBuf(): return [1,2,3] end // wrong MemBuf size class Checksum16Wrong2 from Checksum16 function toMemBuf(): return MemBuf(2,2) end // wrong byte count class Checksum16Wrong3 from Checksum16 function toMemBuf(): return MemBuf(8,1) end // bytes() not a number class Checksum16Wrong4 from Checksum16 function bytes(): return [1,2,3] end // bytes() == 0 class Checksum16Wrong5 from Checksum16 function bytes(): return 0 end // toMemBuf() returning self will cause infinite recursion/stack overflow // this is because everything returned by toMemBuf() will be put into update() again, // and this goes on and on for this class, because toMemBuf() is always provided... class Checksum16Wrong6 from Checksum16 function toMemBuf(): return self end class NopHash2Bytes from HashBase retbuf = nil function bytes(): return 2 function toMemBuf(): return self.retbuf function process(buf) end function finalize() self.retbuf = MemBuf(2,1) self.retbuf.fill(0) self.retbuf[0] = 1 end end class NopHash16Bytes from HashBase retbuf = nil function bytes(): return 16 function toMemBuf(): return self.retbuf function process(buf) end function finalize() self.retbuf = MemBuf(16,1) self.retbuf.fill(0) self.retbuf[0] = 1 end end if test_hashes() > "Hash test completed successfully" if BENCHMARK > "\nBenchmarking...100000 runs for each algorithm and string length" alg = .[Checksum16 CRC32 Adler32 SHA1Hash MD2Hash MD5Hash RIPEMD160Hash TigerHash SHA512Hash WhirlpoolHash] func = [ .[{ x => x.toMemBuf() } "Short MemBuf"], .[{ x => x.toString() } "Short strings"], .[{ x => x.toString() * 100 } "Long strings"] ] for f,desc in func > "\n* ", desc for a in alg astr = toString(a) s = nil for j = 0 to 100 >> astr, ": ", j, "%\r" for i = 0 to 100 h = a() h.update(s) s = f(h) // use output as new input end end > "" end end > "Benchmark complete." end else > "Hash test FAILED!" end tests/feathers/samples/iniparser.fal000066400000000000000000000154601176363201700201610ustar00rootroot00000000000000#!/usr/bin/env falcon /* FALCON - Samples FILE: iniparser.fal Provide a file .ini as argument. Whithout other arguments, this program will dump the contents of the file. With an argument, it will search the key in the main section and display its value If the argument is in format "+key=value" it will be ADDED. If the argument is in format "o=value" it will be SET. With "-key" the given key will be removed. Providing also an argument in square brackets, as "[section]" the argument will be applied to the given section. "-[section]" will delete the given section. Provide "encoding=..." to change default encoding ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 3 2004 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load confparser printl( "The Falcon Programming Language - samples." ) if len(args) == 0 printl( ' Usage: Provide a file .ini as argument. Whithout other arguments, this program will dump the contents of the file. With a second argument, it will search the key in the main section and display its value. If the argument is in format ".key" the category will be shown. If the argument is in format "key=value" it will be SET. If the argument is in format "+key=value" it will be ADDED. With "-key" the given key will be removed. With "-.key" the given category will be removed. Providing also an argument in square brackets, as "[section]" the argument will be applied to the given section. "-[section]" will delete the given section. Provide "encoding=..." to change default encoding.' ) return 0 end // search for an encoding encoding = getSystemEncoding() section = nil for elem in args if "encoding=" in elem encoding = elem[9:] printl( "Setting encoding to ", encoding ) end if elem[0] == "[" and elem[-1] == "]" section = elem[1:-1] printl( @"Operating on section \"$section\"" ) end end parser = ConfParser( args[0], encoding ) printl( "Loading ini file..." ) parser.read() if len( args ) == 1 or len( args ) == 2 and section func = section ? dumpSection : dumpIni func( parser, section ) return 0 end // we have something more specific to do... for elem in args forfirst // do nothing - discard the first print() continue end if "encoding=" in elem continue end if elem[0] == "[" and elem[-1] == "]" continue end if "=" in elem //an assignment? assignIni( parser, elem, section ) modified = 1 continue end if elem[0] == "-" //try // to catch string index outbounds. // removal switch elem[1] case "[" if not parser.removeSection( elem[2:-1] ) printl ("Section ", elem[1:], " not found." ) else printl ("Section ", elem[1:], " deleted." ) modified = 1 end case "." deleteIniCat( parser, elem[2:], section ) modified = 1 default deleteIni( parser, elem[1:], section ) modified = 1 end //end continue end if elem[0] == "." elem = elem[1:] values = parser.getCategory( elem, section ) if values printl(";----------------------------") printl( "Dumping category ", elem, ":\n" ) if section: print( " in section [", section, "]" ); dumpCategory( values ) else print( "Category ", elem, " not found" ); if section: print( " in section [", section, "]" ); printl() end continue end // print the required element if section value = parser.get( elem, section ) print( "In section ", section, ":" ) else value = parser.get( elem ) end if not value printl( "Key ", elem, " not found." ) else print( elem, "=" ) dumpValue( value ) end end if modified printl( "Saving the modified file" ) parser.write() end //================================================ // Function dumping the file //================================================ function dumpIni( parser ) sect_list = parser.getSections() // first the main section printl( "; Dumping the main section: " ) sect_dict = parser.getDictionary() for key, value in sect_dict print( key, "=" ) dumpValue( value ) end printl( ";-----------------------\n" ) // then the other sections for sect in sect_list dumpSection( parser, sect ) end end function dumpSection( parser, sect ) printl( "; Dumping section [", sect, "]:" ) sect_dict = parser.getDictionary( sect ) for key, value in sect_dict print( key, "=" ) dumpValue( value ) end printl( ";-----------------------\n" ) end function dumpCategory( values ) for key, value in values print( key, "=" ) dumpValue( value ) end printl( ";-----------------------\n" ) end function dumpValue( value ) if typeOf( value ) = ArrayType dumpArray( value ) else printl( value ) end end function dumpArray( array ) print( "[" ) for elem in array print( elem ) formiddle: print( "," ) forlast end end printl( "]" ) end //================================================ // Managing ini assignment (add/set) //================================================ function assignIni( parser, elem, section ) if elem[0] == "+" elem = elem[1:] funcName = "add" func = parser.add else funcName = "add" func = parser.set end key, value = strSplit( elem, "=", 2 ) print( "Applying ", funcName, " to ", key, "=", value ) if section print( " in section [", section , "]" ) func( key, value, section ) else func( key, value ) end printl() end //================================================ // Managing ini deletion //================================================ function deleteIni( parser, key, section ) if section print( "Removing key ", key, " in section [", section , "]" ) parser.remove( key, section ) else print( "Removing key ", key, " from main section." ) parser.remove( key ) end printl() end //================================================ // Managing ini deletion of categories //================================================ function deleteIniCat( parser, key, section ) if section print( "Removing category ", key, " in section [", section , "]" ) else print( "Removing key ", key, " from main section." ) end parser.removeCategory( key, section ) printl() end /* end */ tests/feathers/samples/jsonize.fal000066400000000000000000000057461176363201700176540ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - Samples FILE: jsoinze.fal Read a Falcon module and output one object as json format. Usage: jsonize.fal [] ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load compiler load json if args.len() != 2 and args.len() != 3 > ' JSONize -- render as json data an arbitrary falcon object. USAGE: jsonize.fal [init] PARAMETERS: : module (source, fam or binary) where to find the object to be turned into a json representation : Object or class to be jsonized. [init]: Optionally, provide an expression to be passed as class init. The jsonized object will be an instance created through the expression @ "$cls($init)", where cls is the name of the class and init is the expression you provide here. ' return 1 end cls_init = args.len() == 3 ? args[2] : "" c = Compiler() c.addFalconPath() try m = c.loadFile( args[0] ) obj = m.get( args[1] ) select obj case ObjectType jsonize( obj ) case ClassType try maker = c.compile( "tempmod", @ ' load compiler c = Compiler(); c.addFalconPath() m = c.loadFile( "$args[0]" ) cls = m.get( "$args[1]" ) try obj = cls($cls_init) catch in err error = err end ' ) catch SyntaxError in e // the only sintax error we have here can be in $cls_init // we can't have other errors as we have tried the same ops before > @"Syntax error in provided init expression <<$cls_init>>" return 1 end maker.get( "__main__" )() // any error caused by instantation is in "error" if (error = maker.get( "error" )) > "Instantation of required class caused the following error: " > error return 1 end // ready to go jsonize( maker.get( "obj" ) ) default > @"The symbol \"$args[1]\" is not an object nor a class in module $args[0]" return 1 end catch IoError in e > @"Can't find module $args[0]: ", e.toString() return 1 catch SyntaxError in e > @"Module $args[0] can't be loaded: ", e.toString() return 1 catch CodeError in e > @"Module $args[0] can't be loaded: ", e.toString() return 1 end // done and fine return 0 function jsonize( obj ) > "JSON Output: " s = StringStream() JSONencode( obj, s, pretty|true, readable|true ) > s.closeToString() > end tests/feathers/samples/md5.fal000066400000000000000000000023571176363201700166530ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - Samples FILE: md5.fal Calculate MD5 checksum of a file Hint: Check the output of this script against `md5sum -b` ------------------------------------------------------------------- Author: Maximilian Malek Begin: 16 Apr 2010 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load hash if args.len() == 0 > "Usage: Pass a filename as parameter to calculate its MD5 hash" else filename = args[0] bufsize = 65536 // 64 kB block size try file = InputStream(filename) catch > "Error opening file: ", filename return end buf = MemBuf(bufsize, 1) hash = MD5Hash() while not file.eof() bytes = file.read(buf, bufsize) // read the file in blocks // prepare buffer for hashing: buf.position(0) // from the start... buf.limit(bytes) // .. up to the amount of bytes read (if the block was < bufsize) hash.update(buf) // hash the block buf.rewind() // reset position for next read end > hash // output as hex string end tests/feathers/samples/pread.fal000066400000000000000000000020561176363201700172550ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - Samples FILE: pread.fal Even simpler process wrapper. Useful when you want read the output of some process. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load process if args.len() == 0 > "Please, specify the process to be launched. Use a single string.\n" + " Example: \n" + " pread.fal \"ls -l /usr/lib\"\n" return 1 end try data = pread( args[0] ) > "---------------------------------------------------------------" > data > "---------------------------------------------------------------" > "Test complete" catch in error > "Catched error during processing!!:\n", error > "------------------------------------" > "Terminating with error" exit(1) end exit(0) /* end of echoproc.fal */ tests/feathers/samples/proclist.fal000066400000000000000000000017241176363201700200220ustar00rootroot00000000000000/* Falcon Samples. Process enumerator This script lists the processes in your system. */ load process //================================================== // Main program penum = ProcessEnum() printl( "Name PID Parent Command line" ) printl( "------------------ ---------- ---------- ------------------------------------" ) while penum.next() display( penum ) end penum.close() function display( pe ) l = len( pe.name ) if l > 18 print( pe.name[0:13] ) else print( pe.name ) while l < 18: l++ and print( " " ) end print (" ") pid = toString( pe.pid ) l = len( pid ) if l > 8 print( pid[0:8] ) else print( pid ) while l < 8: l++ and print( " " ) end print (" ") pid = toString( pe.parentPid ) l = len( pid ) if l > 8 print( pid ) else print( pid ) while l < 8: l++ and print( " " ) end print (" ") printl( pe.cmdLine ) end tests/feathers/samples/procload.fal000077500000000000000000000022601176363201700177650ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - Samples FILE: procload.fal This samples shows minimal process execution control. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load process if len( args ) < 1 printl( "Usage: ", scriptName, " [params]" ) exit(1) end try proc = Process( args, PROCESS_MERGE_AUX || PROCESS_SINK_INPUT ) procout = proc.getOutput() strout = "" // until the process is done, read what it has to say while (result = proc.value() ) == -1 if procout.readAvailable( 0.1 ) strout += procout.grab( 1024 ) end end if result printl( "Process ", args[0], " exited with error ", result ) else printl( "Process ended correctly. Here is the result:" ) printl( strout ) end catch in err printl( "Can't complete the required operation." ) printl( err.toString() ) end printl() printl("Done.") exit(0) /* end of process.fal */ tests/feathers/samples/reflex_comp.fal000066400000000000000000000020321176363201700204570ustar00rootroot00000000000000/* FALCON - Samples FILE: reflex_comp.fal Simple reflexive compiler test. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2006-05-11 12:35 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load compiler // create the compiler c = Compiler() // try to compile a source string str = ' function func( a ) > "Hello from compiled source: ", a end > "The main part of the module" ' try // First param is the module logical name. sourceMod = c.compile( "aString", str ) // in case of compilation error, we had an error and we bailed out // load the symbol func from our module... func = sourceMod.get( "func" ) // and execute it... func( "test param" ) // execute directly the main module, notice the () at eol sourceMod.get( "__main__" )() catch CodeError in e > "Had an error: ", e end tests/feathers/samples/reflex_comp_external.fal000066400000000000000000000002541176363201700223650ustar00rootroot00000000000000function func( a ) > 'Hello from compiled source: ', a end function retString() return "A string from the remote module" end > 'The main part of the loaded module' tests/feathers/samples/reflex_comp_load.fal000066400000000000000000000020741176363201700214640ustar00rootroot00000000000000/* FALCON - Samples FILE: reflex_comp_load.fal Simple reflexive compiler test; loading an external resource. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2006-05-11 12:35 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load compiler // create the compiler c = Compiler() c.sourceEncoding = "utf-8" // try to compile a source string try // First param is the module logical name. sourceMod = c.loadFile( "reflex_comp_external.fal" ) // in case of compilation error, we had an error and we bailed out // load the symbol func from our module... func = sourceMod.get( "func" ) // and execute it... func( "test param" ) > "... hello from loader..." > // execute directly the main module, notice the () at eol sourceMod.get( "__main__" )() > > "... goodbye from loader..." catch CodeError in e > "Had an error: ", e end tests/feathers/samples/reflex_comp_reload.fal000066400000000000000000000026541176363201700220170ustar00rootroot00000000000000/* FALCON - Samples FILE: reflex_comp_load.fal Simple reflexive compiler test; loading an external resource. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2006-05-11 12:35 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load compiler // create the compiler c = Compiler() c.sourceEncoding = "utf-8" c.launchAtLink = true // try to compile a source string try // First param is the module logical name. > "Loading AND LANUCHING reflex_comp_external" sourceMod = c.loadFile( "reflex_comp_external.fal" ) > "Loaded with name \"", sourceMod.name, "\"" > // in case of compilation error, we had an error and we bailed out // load the symbol func from our module... func = sourceMod.get( "func" ) // and execute it... func( "test param" ) > "... hello BACK from loader..." > "... reloading ..." > // reload it, but under another name sourceMod2 = c.loadFile( "reflex_comp_external.fal", "self.aliased" ) > "Loaded with name \"", sourceMod2.name, "\"" > // load the symbol func from our module... func = sourceMod2.get( "func" ) // and execute it... func( "test param" ) > > "... goodbye from loader..." catch CodeError in e > "Had an error: ", e end tests/feathers/samples/reflex_comp_unload.fal000066400000000000000000000026201176363201700220240ustar00rootroot00000000000000/* FALCON - Samples FILE: reflex_comp_unload.fal Loads an external resource and check that it can be unloaded. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2006-05-11 12:35 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load compiler // create the compiler c = Compiler() c.sourceEncoding = "utf-8" // try to compile a source string try // First param is the module logical name. sourceMod = c.loadFile( "reflex_comp_external.fal" ) // in case of compilation error, we had an error and we bailed out // load the symbol func from our module... func = sourceMod.get( "func" ) // and execute it... func( "test param" ) > "... hello from loader..." > str = sourceMod.get( "retString" )() > "The remote string: ", str // and now, unload sourceMod.unload() return // as we have references to them, we should still be able to access it GC.perform(true) func( "Again from main" ) > "The remote string again: ", str // and now discharge the module func = nil GC.perform(true) // but we should have cached the string in the local VM > "Final check on the string: ", str catch CodeError in e > "Had an error: ", e end tests/feathers/samples/regexTest.fal000066400000000000000000000022761176363201700201400ustar00rootroot00000000000000/* Falcon Samples. Regular expression test This script accepts a pattern and a string to match, and it prints the matches it is able to perform. */ load regex //================================================== // Main program if len( args ) < 2 printl( "Usage: ", scriptName, " [replacer]" ) return 1 end // let's try to compile the pattern try re = Regex( args[0] ) > "--------------------------------------" > "Using PCRE version ", re.version() > "--------------------------------------" catch in error > error return 1 end try // let's try the match if re.match( args[1] ) printl( "Pattern matches!" ) printl( "Matched substring: ", args[1][ re.captured(0) ] ) for i in [ 1 : re.capturedCount() ] printl( "Captured ", i,": ", args[1][ re.captured(i) ] ) end // should we substitute the found string? if len( args ) >= 3 replaced = re.replaceAll( args[1], args[2] ) printl( "Replaced string: ", replaced ) end else printl( "Sorry, match failed!" ) end return 0 catch in error > "Error during matches: " > error.message return 1 end /* end of regexTest.fal */ tests/feathers/samples/ssl_client_test.fal000066400000000000000000000010251176363201700213530ustar00rootroot00000000000000/* Client SSL socket. */ import from socket s = socket.TCPSocket() s.connect( 'www.openssl.org', 443 ) while not s.isConnected(): sleep(1) > "==== Connected!" //s.sslConfig( false, socket.SSLv3, 'keyd.crt', 'keyd.key' ) s.sslConfig( false, socket.SSLv3 ) s.sslConnect() > "==== SSL Connected!" i = s.send( "GET /\r\n\r\n" ) > @ "==== $(i) bytes written!" buf='' txt='' while (i = s.recv(buf,4098)) == 4098 txt += buf end if i != 0: txt += buf > buf s.sslClear() s.close() exit() /* vim: set ai et sw=3 ts=3 sts=3: */ tests/feathers/samples/test.ini000066400000000000000000000006061176363201700171550ustar00rootroot00000000000000; Test ini file, just to have a base to test iniparser.fal mainkey = main value cat.key1 = value 1 with cat cat.key2 = value 2 with cat [section] sectkey = section value cat.key1 = category in section 1 value cat.key2 = category in section 2 value [other_section] sectkey = other section value cat.key1 = category in other section 1 value cat.key2 = category in other section 2 value tests/feathers/samples/udpserver.fal000066400000000000000000000034221176363201700201770ustar00rootroot00000000000000/* Falcon Samples. UDP transmission. This scripts just waits for UDP packets from clients. */ load socket if len( args ) < 1 printl( "Usage: udpserver.fal [all]" ) printl( " may be a numeric port." ) printl( " listen on all interfaces." ) exit( 0 ) end service = args[0] if args.len() == 2 and args[1] == "all" interfaces = ["0.0.0.0"] else // we'll listen on all the local interfaces. interfaces = resolveAddress( getHostName() ) if len( interfaces ) == 0 printl( "Fatal: no interfaces." ) end end ifdone = [] sockets = [] for item in interfaces // avoid registering duplicates. if item notin ifdone try sockets += UDPSocket( item, service ) ifdone += item printl( "Listening on ", item, ":", service ) catch in error printl( "Warning: while listening on ", item, ": ", error.toString() ) end end end if len( sockets ) == 0 printl( "Fatal: can't listen on any interface." ) exit( 0 ) end while true for i in [ 0 : len( sockets ) ] socket = sockets[i] if socket.readAvailable( 10 ) data = socket.recv(1600) parseMessage( data, socket ) end end sleep(0.01) end function parseMessage( message, socket ) if strFind( message, "WAY" ) == 0 printl( "Received a where are you request from ", socket.remote, ":", socket.remoteService ) // we must send a message telling where we are. socket.sendTo( socket.remote, socket.remoteService, "IAM:" + socket.getHost() ) return end printl( socket.remote, ":", socket.remoteService, "> ", message ) socket.sendTo( socket.remote, socket.remoteService, "ACK:" + toString( len( message ) ) ) end /* end of udpserver.fal */ tests/feathers/samples/udptest.fal000066400000000000000000000027501176363201700176530ustar00rootroot00000000000000/* Falcon Samples. UDP transmission. This script broadcast a request for the "udpserver.fal", and once found it sends the messages you type to the server. */ load socket // send a discovery packet here. if len( args ) != 2 printl( "Usage: udptest " ) printl( " may be a broadcast address" ) exit( 0 ) end server = args[0] port = args[1] // ------------------------------------------- // Discovering service // print( "Discovering service" ) socket = UDPSocket( "0.0.0.0" ) socket.broadcast() socket.sendTo( server, port, "WAY" ) socket.setTimeout( 100 ) data = "" count = 0 while count < 50 and data == "" socket.recv( data, 1600 ) print( "." ) ++count end printl() if not data printl( "Server reply wait timed out." ) exit( 0 ) end //---------------------------------------------- // Message parsing. // if strFind( data, "IAM:" ) != 0 printl( "Server did not send a valid reply" ) exit( 0 ) end target = data[4:] //---------------------------------------------- // Main loop // printl( "Write \"quit\" to terminate" ) istream = stdIn() print( "> " ) while true if socket.readAvailable(10) data = socket.recv( 1600 ) printl() printl( "Server said: ", data ) print( "> " ) end if istream.readAvailable(0) msg = istream.read( 1024 ) if msg[0:4] == "quit" break end socket.sendTo( target, port, msg ) print( "> " ) end end /* end of udptest.fal */ tests/feathers/samples/vminfo.fal000066400000000000000000000032231176363201700174550ustar00rootroot00000000000000/* FALCON - Samples FILE: vminfo.fal Reports VM and eventually module informations. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: mer nov 3 2004 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load compiler > "VM Info - Prints Falcon Virtual Machine and module informations." > " To get informations on a module, pass its complete path as a paramter." > " Can get informations on sources, .fam and DLL/SO modules." > major, minor, revision = vmVersionInfo() > @ "Falcon Virtual Machine $(major).$(minor).$(revision)." > "Version description: ", vmVersionName() > "System type: ", vmSystemType() if args.len() != 0 comp = Compiler() comp.saveModules = false try mod = comp.loadModule( args[0] ) > @ "Module $(mod.name) (at $(mod.path))" major, minor, revision = mod.moduleVersion() if major or minor or revision > @ "Module version $(major).$(minor).$(revision)." else > "Module version information not available." end major, minor, revision = mod.engineVersion() > @ "Compiled with Falcon Virtual Machine $(major).$(minor).$(revision)." catch SyntaxError in error > @ "Source module $args[0] couldn't be compiled due to syntax errors:" for e in error.subErrors >> e end catch CodeError in error > @"Module $args[0] couldn't be loaded in the virtual machine:" > error catch in error > @ "Generic error while loading the module $(args[0])." > error end end /* end of vminfo.fal */ tests/feathers/samples/xml_surface.fal000066400000000000000000000025551176363201700204760ustar00rootroot00000000000000/* Falcon Samples. XML loader and surface scraper. This script loads an XML file and shows its "surface". */ load mxml if len(args) != 1 > "Please, specify an XML file name." exit(1) end // let's cache the standard output to provide it as a serialization device stdout = stdOut() try // read the XML document doc = MXMLDocument() doc.read( args[0] ) // Get the topmost node - it must exist, so we get it's child top = doc.top().firstChild() while top != nil // skip if this is a tag node; topmost tag node is the root if top.nodeType() == MXMLType.tag continue end > strReplicate( "-", 60 ) >> "Topmost node: " top.write( stdout, MXMLStyle.INDENT || MXMLStyle.TAB ) > top = top.nextSibling() end // now get the root's node child, if any root = doc.root() if root != nil dispNode( root, "Root node" ) child = root.firstChild() while child != nil dispNode( child, "Child node" ) child = child.nextSibling() end end catch MXMLError in error > "Couldn't read or parse the XML document: " > error exit( 1 ) end function dispNode( node, prefix ) > strReplicate( "-", 60 ) > prefix, ": ", node.name() attribs = node.getAttribs() for k,v in attribs > " \"", k, "\" = \"", v, "\"" end end tests/feathers/samples/xml_synth.fal000066400000000000000000000026451176363201700202130ustar00rootroot00000000000000/* Falcon Samples. XML generator. This script loads an XML file and shows its "surface". */ load mxml // creating some sample nodes. // you can create them also without documents nodeComment = MXMLNode( MXMLType.comment, "-no name-", "A comment that we'll place on top of XML" ) // let's create some items nodes = [] for i in [1: 11] node = MXMLNode( MXMLType.tag, "Item" ) node.setAttribute( "id", toString( i ) ) nodes += node end // We can create the document either before or after the nodes. doc = MXMLDocument() // add our topmost comment doc.top().addBelow( nodeComment ) // add our items below root. root = doc.root() root.name( "The_xml_document" ) for node in nodes root.addBelow( node ) end // Get a random node and add also some data to it nodes[4].data( "Also some data for this node" ) // Or if things gets nastier, you may wish to use explicit data nodes // we create here 'some data other data again some data' node = nodes[5] node.addBelow( MXMLNode( MXMLType.data, "", "Some normal data for this node" ) ) node.addBelow( MXMLNode( MXMLType.tag, "b", "bold text" ) ) node.addBelow( MXMLNode( MXMLType.data, "", "Other normal data." ) ) // Time to show. > "XML Test -- writing a document" > "======================================================" doc.style( MXMLStyle.INDENT || MXMLStyle.TAB ) doc.serialize( stdOut() ) > "======================================================" > "Done" tests/feathers/samples/zlibCompUncomp.fal000066400000000000000000000010361176363201700211200ustar00rootroot00000000000000load zlib > "Using zlib version: ", ZLib.getVersion() original = "Mary had a little lamb, it's fleece was white as snow." > "Uncompressed: " > " ", original // creating a compressed memory buffer comped = ZLib.compress( original ) // Retreiving the string in the uncompressed membuf > "Compressed then uncompressed:" > " ", strFromMemBuf( ZLib.uncompress( comped ) ) // Use string specific functions. comped = ZLib.compressText( original ) > "Compressed then uncompressed (text oriented):" > " ", ZLib.uncompressText( comped ) tests/feathers/samples/zlibIntlStr.fal000066400000000000000000000005041176363201700204360ustar00rootroot00000000000000load zlib > "Using zlib version: ", ZLib.getVersion() original = "これは国際のストリングだよ!!!" > "Uncompressed: " > " ", original comped = ZLib.compressText( original ) // Retreiving the string in the uncompressed string > "Compressed then uncompressed:" > " ", ZLib.uncompressText( comped ) tests/feathers/samples_mt/000077500000000000000000000000001176363201700161735ustar00rootroot00000000000000tests/feathers/samples_mt/th_basic.fal000066400000000000000000000020231176363201700204300ustar00rootroot00000000000000/* FALCON - Samples FILE: th_basic.fal Minimal threading sample. This test just launches 4 threads, that will run and mess a bit your output, and joins to them to wait for their end. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load threading const threadCount = 4 class MyThread(id) from Thread id = id function run() for i in [1:10000] > "Thread ", self.id, ": ", i end end end t = arrayBuffer(threadCount) for i in [0:t.len()] t[i] = MyThread( "ID" + i.toString() ) t[i].start() end // now that we started the threads, join them. while t.len() terminated = Threading.vwait( t ) > "Terminated thread ", t[terminated].id t[terminated:terminated+1] = [] end > "Main thread complete." tests/feathers/samples_mt/th_counter.fal000066400000000000000000000067671176363201700210510ustar00rootroot00000000000000/* FALCON - Samples FILE: th_counter.fal Synchronization counter sample. This sample shows how to use a synchronization counter (semaphore) in both its incarantions of "barrier with a counter" and "event signaler". The main thread creates five threads which will compete on a counter; once acquired the counter, they will just do some lengty work, and when done they'll post back on another counter that is waited by the main thread. Just, the first time the main thread will set the counter to one, the second to two, the third to three and so on. This means that only some of the threads will be able to proceed and complete that step of computation. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 08 Jun 2008 21:50:26 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load threading const threadCount = 4 const loops = 250 // ID: a numeric ID (in the panel) // counterIn: semaphore that must be acquired to have the right to write // counterDone: semaphore that must be signaled to tell the main thread we're done. // panel: a shared panel to write on. class MyThread( id, counterIn, counterDone, panel ) from Thread id = id counterIn = counterIn counterDone = counterDone panel = panel function run() loop // wait for the counter self.wait( self.counterIn ) // termination condition if self.panel[0] > loops: break // start counting like mad for i in [0:loops] self.panel[ self.id ] = i sleep( 0.01 ) end self.panel[ self.id ] = 0 // don't release the counter; the main thread will. self.counterDone.post() // declare we're done. end end end class ControlThread( panel ) from Thread panel = panel stopreq = false function display() for n in [0: self.panel.len()] forfirst: >> "\r", strReplicate( " ", 70 ), "\r" >> n, ": " if self.panel[n] == 0 || self.panel[n] > loops >> "_" else >> self.panel[n] end formiddle: >> "; " end end function run() loop try self.wait( 0.05 ) self.display() catch InterruptedError // one last cleanup display self.display() break end end end end //====================================================== // Main thread // let's go. panel = MemBuf( threadCount + 1, 4 ) for i in [0:panel.len()]: panel[i] = 0 counterIn = SyncCounter(0) counterDone = SyncCounter(0) ths = [] for i in [0:threadCount] thread = MyThread( i + 1, counterIn, counterDone, panel ) thread.start() ths += thread end // start our control thread to see what's going on tcnt = ControlThread( panel ) tcnt.start() // and now, our increasing post loop: for count in [0: threadCount] // let the threads to go panel[0] = count + 1 counterIn.post( count + 1 ) // and wait for them to be done finished = 0 while finished <= count Threading.wait( counterDone ) finished++ end end // avoid breaking last output tcnt.stop() Threading.wait(tcnt) // ask thread termination panel[0] = loops + 1 counterIn.post( threadCount + 1 ) for th in ths th.stop() Threading.wait( th ) end > > "Main thread complete." tests/feathers/samples_mt/th_errors.fal000066400000000000000000000041771176363201700206770ustar00rootroot00000000000000/* FALCON - Samples FILE: th_errors.fal Managing errors in threads. This program lances some threads. One will always complete with success, returning an object. The others will destroy themselves. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load threading const threadCount = 2 class MyThreadOk(id) from Thread id = id function run() str = "" randomSeed( seconds() ) sleep( random()*2 ) for i in [ord('A'): ord( 'A' ) + 26] str *= i end return str end end class MyThreadFail(id) from Thread id = id function run() randomSeed( seconds() ) sleep( random() * 3 ) raise "Ops, raised an error!" end end class MyThreadBadFail(id) from Thread id = id function run() randomSeed( seconds() ) sleep( random() * 3 ) self.id[400] = 10 // will surely generate an array access error. end end threads = [] for i in [0:threadCount] threads += MyThreadOk( "ID" + i.toString() ) end for i in [threadCount:threadCount*2] threads += MyThreadFail( "ID" + i.toString() ) end for i in [threadCount*2:threadCount*3] threads += MyThreadBadFail( "ID" + i.toString() ) end // start all the threads for t in threads t.start() end // wait for all them. start = seconds() fmt = Format( ".3" ) while threads thnum = Threading.vwait( threads, 0.1 ) >> "Elapsed: ", fmt.format(seconds() - start), "\r" // have been really signaled? if ( thnum >= 0 ) >> " \r" // clear the row // extract the thread that has been terminated th = threads[thnum] threads[thnum:thnum + 1] = [] > "Terminated thread ", th.id, \ ( th.hadError() ? " with error: " + th.getError().heading() : " with return: " + th.getReturn().toString() ) end end > "Main thread complete." tests/feathers/samples_mt/th_event.fal000066400000000000000000000034121176363201700204730ustar00rootroot00000000000000/* FALCON - Samples FILE: th_event.fal Auto reset event test for interthread coordination. This test shows how to perform a stop-and-go sequence using events. Each thread is waken up by the previous one and wakes up the next when its computation is done. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load threading const threadCount = 5 const countTo = 10000 class MyThread(id, prevEvent, nextEvent) from Thread id = id prevEvent = prevEvent nextEvent = nextEvent isLast = false function run() for count in [0: countTo+1] // Wait for the previous thread to be done self.wait( self.prevEvent ) >> self.id, ": ", count, "; " // and eventually go back if self.isLast: >> "\r" // wake up the next thread self.nextEvent.set() end end end // create the needed events evts = arrayBuffer( threadCount ) for i in [0:evts.len()] evts[i] = Event() end evts += evts[0] // create the circular buffer. // ... and assign them to our arrays t = arrayBuffer(threadCount) for i in [0:t.len()] // assign the i, i+1 (circular) to the events. t[i] = MyThread( "ID" + i.toString(), evts[i], evts[ i + 1 ] ) end // In form the last thread that he's the last t[-1].isLast = true // start all the threads for thread in t thread.start() end // start the game evts[0].set() // now that we started the threads, join them. for thread in t Threading.wait( thread ) end > > "Main thread complete." tests/feathers/samples_mt/th_event2.fal000066400000000000000000000033441176363201700205610ustar00rootroot00000000000000/* FALCON - Samples FILE: th_event.fal Auto reset event test for interthread coordination. This test is identical to th_event.fal, but it uses callable arrays to configure the run method of the Thread class instead deriving it. Functional programming is quite powerful and compact when properly applied... ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load threading const threadCount = 5 const countTo = 10000 function threadRunner(id, prevEvent, nextEvent, isLast ) for count in [0: countTo+1] // Wait for the previous thread to be done self.wait( prevEvent ) >> id, ": ", count, "; " // and eventually go back if isLast: >> "\r" // wake up the next thread nextEvent.set() end end // create the needed events evts = arrayBuffer( threadCount ) for i in [0:evts.len()] evts[i] = Event() end evts += evts[0] // create the circular buffer. // ... and assign them to our arrays t = arrayBuffer(threadCount) for i in [0:t.len()] // assign the i, i+1 (circular) to the events. t[i] = Thread() formiddle: t[i].run = [ threadRunner, "ID" + toString(i), evts[i], evts[ i + 1 ] ] forlast: t[i].run = [ threadRunner, "ID" + toString(i), evts[i], evts[ i + 1 ], true ] end // start all the threads for thread in t thread.start() end // start the game evts[0].set() // now that we started the threads, join them. for thread in t Threading.wait( thread ) end > > "Main thread complete." tests/feathers/samples_mt/th_grant.fal000066400000000000000000000036671176363201700205010ustar00rootroot00000000000000/* FALCON - Samples FILE: th_grant.fal Minimal threading sample, with interlocked screen access. This test just launches 4 threads, and instead letting them to mess the output, it uses Grants to coordinated acess to a shared resource (the screen). ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load threading const threadCount = 8 class MyThread(id, screenGrant, quit ) from Thread( "Waiter " + id ) id = id screenGrant = screenGrant quit = quit function run() i = 0 loop i++ obj = self.wait( self.quit, self.screenGrant ) if obj == self.quit: break >> "Thread ", self.id, ": ", i, "\r" obj.release() end self.wait( self.screenGrant ) > self, " terminated with: ", i self.screenGrant.release() end end class MainThread( screenGrant ) from Thread( "Control" ) screenGrant = screenGrant function run() for i in [0:3] obj = self.wait( self.screenGrant ) >> " CTRL: ", i, "\r" obj.release() sleep( 1 ) end obj = self.wait( self.screenGrant ) > " CTRL: Terminated." obj.release() end end sGrant = Grant() bQuit = Barrier() t = arrayBuffer(threadCount) for i in [0:t.len()] t[i] = MyThread( "ID" + i.toString(), sGrant, bQuit ) t[i].start() end // wait for the controller thread to terminate main = MainThread( sGrant ) main.start() Threading.wait( main ) // tell the other threads to quit. bQuit.open() // now that we told the threads to quit, join them. for thread in t Threading.wait( thread ) end > > "Main thread complete." tests/feathers/samples_mt/th_ids.fal000066400000000000000000000027211176363201700201330ustar00rootroot00000000000000/* FALCON - Samples FILE: th_ids.fal Tests for threads being launched through the threading interaface, and verifies their ID. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load threading const threadCount = 8 function thMain( barrier, queue ) queue.push( Threading.getCurrentID() ) Threading.wait( barrier ) queue.push( Threading.getCurrentID() ) end //=========================================== // threads = [=>] mbar = Barrier() mq = SyncQueue() for i in [0:threadCount] th = Threading.start( .[ thMain mbar mq ] ) threads[ th.getThreadID() ] = th end // fait for all the threds to be started started = 0 while started < threadCount Threading.wait( mq ) id = mq.popFront() mq.release() threads[id] = started started++ end > Threading.getCurrentID(), " -> Launched threads: " for id, th in threads > id, ": ", th.toString() end > "Waiting for thread termination" mbar.open() terminated = 0 while terminated < threadCount Threading.wait( mq ) id = mq.popFront() mq.release() threads[id] = terminated terminated++ end > "Termination order:" for id, terminated in threads > id, ": ", terminated end > "Main thread complete." tests/feathers/samples_mt/th_join.fal000066400000000000000000000045131176363201700203140ustar00rootroot00000000000000/* FALCON - Samples FILE: th_join.fal This test shows how to automatize checks on thread termination with join. Join works as waiting for a thread, but it returns the return value of the joined thread, or it throws a ThreadError if the joinied thread terminated with an error. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load threading const threadCount = 2 class MyThreadOk(id) from Thread id = id function run() str = "" sleep( random() * 2 ) for i in [ord('A'): ord( 'A' ) + 26] str *= i end return str end end class MyThreadFail(id) from Thread id = id function run() sleep( random() * 3 ) raise "Ops, raised an error!" end end class MyThreadBadFail(id) from Thread id = id function run() sleep( random() * 3 ) self.id[400] = 10 // will surely generate an array access error. end end class MyDetacher(id) from Thread id = id function run() sleep( random() * 3 ) self.detach() end end //=========================================== // Main function // threads = [] for i in [0:threadCount] threads += MyThreadOk( "ID" + i.toString() ) end for i in [threadCount:threadCount*2] threads += MyThreadFail( "ID" + i.toString() ) end for i in [threadCount*2:threadCount*3] threads += MyThreadBadFail( "ID" + i.toString() ) end for i in [threadCount*3:threadCount*4] threads += MyDetacher( "ID" + i.toString() ) end // start all the threads for t in threads t.start() end // wait for all them. for t in threads try output = t.join() > "Thread ", t.id, " terminated: ", output catch ThreadError in error se = error.subErrors[0] > "Thread ", t.id, " had an error: ", se.heading() catch JoinError in error > "Thread ", t.id, " has been detached." catch InterruptedError in error > "Interrupted while waiting for the threads... quitting" break catch in error > "Error for the threads: ", error > "...quitting." break end end > "Main thread complete." tests/feathers/samples_mt/th_join2.fal000066400000000000000000000051161176363201700203760ustar00rootroot00000000000000/* FALCON - Samples FILE: th_join2.fal This test combines waits on multiple threads to join automation. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load threading const threadCount = 2 class MyThreadOk(id) from Thread id = id function run() str = "" sleep( random() * 2 ) for i in [ord('A'): ord( 'A' ) + 26] str *= i end return str end end class MyThreadFail(id) from Thread id = id function run() sleep( random() * 3 ) raise "Ops, raised an error!" end end class MyThreadBadFail(id) from Thread id = id function run() sleep( random() * 3 ) self.id[400] = 10 // will surely generate an array access error. end end class MyDetacher(id) from Thread id = id function run() sleep( random() * 3 ) self.detach() end end //=========================================== // Main function // threads = [] for i in [0:threadCount] threads += MyThreadOk( "ID" + i.toString() ) end for i in [threadCount:threadCount*2] threads += MyThreadFail( "ID" + i.toString() ) end for i in [threadCount*2:threadCount*3] threads += MyThreadBadFail( "ID" + i.toString() ) end for i in [threadCount*3:threadCount*4] threads += MyDetacher( "ID" + i.toString() ) end // start all the threads for t in threads t.start() end // wait for all them. start = seconds() fmt = Format( ".3" ) while threads thnum = Threading.vwait( threads, 0.1 ) >> "Elapsed: ", fmt.format(seconds() - start), "\r" // have been really signaled? if ( thnum >= 0 ) >> " \r" // clear the row // extract the thread that has been terminated th = threads[thnum] threads[thnum:thnum + 1] = [] // join the thread performJoin( th ) end end function performJoin( t ) try output = t.join() > "Thread ", t.id, " terminated: ", output catch ThreadError in error se = error.subErrors[0] > "Thread ", t.id, " had an error: ", se.heading() catch JoinError in error > "Thread ", t.id, " has been detached." catch InterruptedError in error > "Interrupted while waiting for the threads... quitting" catch in error > "Error for the threads: ", error > "...quitting." end end > "Main thread complete." tests/feathers/samples_mt/th_queue.fal000066400000000000000000000072241176363201700205030ustar00rootroot00000000000000/* FALCON - Samples FILE: th_queue.fal Queue test. In this test we pass data to worker threads, and they pass data back to a main inspector thread. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load threading const threadCount = 4 const countTo = 10000 const INTERVAL = 0.2 const TEST_TIME = 10.0 const BURST_SIZE = 2000 class Inspector( evts, quit ) from Thread evts = evts quit = quit function run() // set up initial time started = seconds() // a storage to be displayed status = [=>] // publish every INTERVAL seconds nextPublish = started + INTERVAL // a useful formatter fmt = Format( ".3" ) loop // Wait for the previous thread to be done waited = self.wait( self.quit, self.evts, 0.2 ) // have we been notified? if waited == self.evts id, count = waited.popFront() if id in status status[ id ] += count else status[ id ] = count end waited.release() // or should we quit elif waited == self.quit break end // In case of timeout, see if we should publish now = seconds() if now >= nextPublish nextPublish += INTERVAL >> "\rAt ", fmt.format( now - started ), ": " for id, count in status >> id, ": ", count formiddle: >>"; " end end // loop and wait again end end end class WorkerThread(id, dataQueue, notifQueue, quit ) from Thread id = id dataQueue = dataQueue notifQueue = notifQueue quit = quit function run() done = 0 loop // Wait for the previous thread to be done waited = self.wait( self.quit, self.dataQueue ) // are we told to quit? if waited == self.quit: break // otherwise, process data. data = waited.popFront() // ...that we can release waited.release() // should be a string -- do something long (hopefully) count = 0 for i in [0:len( data ) ] for j in [0:len(data)] for k in [0:len(data)] count ^= data[*k] end end end // and notify the inspector self.notifQueue.push( [self.id, 1] ) end end end // Main thread notifQueue = SyncQueue() dataQueue = SyncQueue() quit = Barrier() // create the inspector inspth = Inspector( notifQueue, quit ) inspth.start() // ... create some consumer t = arrayBuffer(threadCount) for i in [0:t.len()] // assign the i, i+1 (circular) to the events. t[i] = WorkerThread( "ID" + i.toString(), dataQueue, notifQueue, quit ) t[i].start() end // Ok, produce some data for all. started = seconds() while seconds() - started < TEST_TIME // how many data to produce? 1-100 burst = random() * BURST_SIZE letter = random() * 26 + ord('A') for i in [0: burst] size = random() * 10 + 10 dataQueue.push( strReplicate( chr(letter), size ) ) end notifQueue.push( [ "Main", int( burst ) ] ) // sleep a bit sleep( random() * 0.5 ) end quit.open() // now that we started the threads, join them. for thread in t Threading.wait( thread ) end // and join inspector too Threading.wait( inspth ) > > "Main thread complete." tests/feathers/samples_mt/th_self.fal000066400000000000000000000030031176363201700202770ustar00rootroot00000000000000/* FALCON - Samples FILE: th_ids.fal Tests for threads being launched through the threading interaface, and verifies their ID. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load threading const threadCount = 8 function thMain( barrier, queue ) th = Threading.getCurrent() queue.push( th.getThreadID() ) th.wait( barrier, 4 ) queue.push( th.getThreadID() ) end //=========================================== // threads = [=>] mbar = Barrier() mq = SyncQueue() mainThread = Threading.getCurrent() for i in [0:threadCount] th = Threading.start( .[ thMain mbar mq ] ) threads[ th.getThreadID() ] = th end // fait for all the threds to be started started = 0 while started < threadCount mainThread.wait( mq ) id = mq.popFront() mq.release() threads[id] = started started++ end > mainThread.getThreadID(), " -> Launched threads: " for id, th in threads > id, ": ", th.toString() end > "Waiting for thread termination" mbar.open() terminated = 0 while terminated < threadCount mainThread.wait( mq ) id = mq.popFront() mq.release() threads[id] = terminated terminated++ end > "Termination order:" for id, terminated in threads > id, ": ", terminated end > "Main thread complete." tests/feathers/samples_mt/th_sharedbuf.fal000066400000000000000000000016571176363201700213260ustar00rootroot00000000000000/* FALCON - Samples FILE: th_sharedbuf.fal Uses a shared memory buffer to send back and forth data. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 27 Apr 2008 17:33:55 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load threading class MyThread( buffer ) from Thread buffer = buffer function run() for i in [1:1000000] for n in [0:self.buffer.len()] self.buffer[n] = i end end end end buffer = MemBuf( 5, 4 ) th = MyThread( buffer ) for n in [0:buffer.len()] buffer[n] = 0 end th.start() while not th.terminated() for n in [0:buffer.len()] >> buffer[n] formiddle: >> ", " forlast: >> ".\r" end end > > "Main thread complete." tests/feathers/samples_mt/th_stop.fal000066400000000000000000000023461176363201700203440ustar00rootroot00000000000000/* FALCON - Samples FILE: th_stop.fal Basic stop test ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load threading class MyThread() from Thread function run() > "Say something or the thread will be terminated in 5 seconds" >> "> " inf = stdIn() if inf.readAvailable() // but wait for 10 > "You said '", inf.grabLine(), "'" end end end function func_interrupter( thread ) sleep( 5 ) thread.stop() end //=========================================== // thread = MyThread() interrupter = Thread() interrupter.run = [func_interrupter, thread] // start and wait for 5 seconds thread.start() // interrupter interrupter.start() // who will win? Threading.wait( thread ) // now we don't care; just be sure to stop them both > if thread.hadError() > "Thread was interrupted." else > "Thread completed succesfully" interrupter.stop() Threading.wait( interrupter ) end > "Main thread complete." tests/feathers/samples_mt/th_waitIntr.fal000066400000000000000000000022721176363201700211560ustar00rootroot00000000000000/* FALCON - Samples FILE: th_errors.fal Managing errors in threads. This program lances some threads. One will always complete with success, returning an object. The others will destroy themselves. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 13 Apr 2008 23:26:44 +0200 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load threading const threadCount = 2 class MyThread(quit) from Thread quit = quit function run() str = "" loop try self.wait( self.quit, 5 ) // wait for self.quit in 5 seconds. > "This time, I go for good." break catch InterruptedError in e > "Interrupted!; but I will live on!" end end end end quit = Barrier() thread = MyThread( quit ) > "Starting interruptable thread" thread.start() for i in [0:5] sleep( 0.5 ) > "...Stopping..." thread.stop() end // now clear it really quit.open() Threading.wait( thread ) > "Main thread complete." tests/feathers/testsuite/000077500000000000000000000000001176363201700160605ustar00rootroot00000000000000tests/feathers/testsuite/AB.fal000066400000000000000000000005411176363201700170260ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: -- * Category: threading * Subcategory: * Short: Runtime re-creation * Description: * See tests 51a and 51b * [/Description] * **************************************************************************/ function ab() return 0 end export tests/feathers/testsuite/CMakeLists.txt000066400000000000000000000016151176363201700206230ustar00rootroot00000000000000set(categories reflexive regex zlib ) # clean this directory from artefacts of former faltest runs file(GLOB _fams "*.fam") if(_fams) file(REMOVE ${_fams}) endif(_fams) # start memcheck ignore list file(WRITE ${CMAKE_BINARY_DIR}/CTestCustom.cmake "set(CTEST_CUSTOM_MEMCHECK_IGNORE\n") add_test(testsuite_all ${CMAKE_COMMAND} -P test_driver.cmake) file(APPEND ${CMAKE_BINARY_DIR}/CTestCustom.cmake " testsuite_all\n") foreach(category ${categories}) add_test(testsuite_category_${category} ${CMAKE_COMMAND} -Dtest_category=${category} -P test_driver.cmake) file(APPEND ${CMAKE_BINARY_DIR}/CTestCustom.cmake " testsuite_category_${category}\n") endforeach(category) # wrap memcheck ignore list up file(APPEND ${CMAKE_BINARY_DIR}/CTestCustom.cmake ")") configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/test_driver.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/test_driver.cmake @ONLY ) tests/feathers/testsuite/ath.fal000066400000000000000000000017661176363201700173320ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 51a * Category: threading * Subcategory: * Short: Runtime re-creation (1) * Description: * The threading module must create a vm similar to the original one * when starting a new thread. To do that, it creates a new runtime * filling it with the modules coming from the calling VM, and links * it in a new VM. * * This test checks that dependency from core and from threading is * correctly restacked. It consists of two scripts, with names at the * opposite spectrum of the module map, so that if correct order is not * granted, a link error will arise. * * In this first test, the name of the calling module is the highest. * [/Description] * **************************************************************************/ load threading load AB load zb class Th from Thread function run() ab() zb() return 0 end end t = Th() t.start() t.join() success() tests/feathers/testsuite/ath2.fal000066400000000000000000000016501176363201700174040ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 51c * Category: threading * Subcategory: * Short: Runtime re-creation (1) * Description: * The threading module must create a vm similar to the original one * when starting a new thread. To do that, it creates a new runtime * filling it with the modules coming from the calling VM, and links * it in a new VM. * * This test checks that dependency from core and from threading is * correctly restacked. It consists of two scripts, with names at the * opposite spectrum of the module map, so that if correct order is not * granted, a link error will arise. * * In this first test, the name of the calling module is the highest. * [/Description] * **************************************************************************/ load threading load AB load zb Threading.start({=> return 0}).join() success() tests/feathers/testsuite/bufext_basic.fal000066400000000000000000000246461176363201700212160ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 60a * Category: bufext * Subcategory: * Short: Bufext basic functionality * Description: * Basic tests for the bufext module and all provided classes, * checking for correct memory access, endianity, * and ensuring correct operation of all methods. * (If some tests fail due to float rounding, try compiler settings * that increase floating point precision) * [/Description] **************************************************************************/ load bufext GC.adjust(GC.ADJ_STRICT) // we will produce a lot of garbge randomSeed(8736275764) // make results consistent function EXPECT(actual, expected, str) if(actual != expected) failure("Expected: '" + expected + "', actual: '" + actual + "' <-- " + str) end end function EXPECTDIFF(actual, expected, diff, str) if(abs(actual - expected) > diff) failure("Expected: '" + expected + "', actual: '" + actual + "' <-- " + str) end end EXPECT(BitBuf.derivedFrom(ByteBuf), true, "BitBuf from ByteBuf") EXPECT(ByteBufLittleEndian.derivedFrom(ByteBuf), true, "ByteBufLittleEndian from ByteBuf") EXPECT(ByteBufNativeEndian.derivedFrom(ByteBuf), true, "ByteBufNativeEndian from ByteBuf") EXPECT(ByteBufBigEndian.derivedFrom(ByteBuf), true, "ByteBufBigEndian from ByteBuf") EXPECT(ByteBufReverseEndian.derivedFrom(ByteBuf), true, "ByteBufReverseEndian from ByteBuf") bb = BitBuf(3) // wrong initial size (internal array type is uint32 or uint64) EXPECT(bb.capacity() % 4, 0, "BitBuf capacity alignment") fa = [1,2,3] bb.write(fa) for i = 0 to len(fa) - 1 EXPECT(bb.r64(), fa[i], "Basic BitBuf 64 bit read/write") end EXPECT(BitBuf.bitsForInt(2), 2, "required bit count") EXPECT(BitBuf.bitsForInt(7), 3, "required bit count") EXPECT(BitBuf.bitsForInt(8), 4, "required bit count") EXPECT(BitBuf.bitsForInt(255), 8, "required bit count") EXPECT(BitBuf.bitsForInt(1023), 10, "required bit count") EXPECT(BitBuf.bitsForInt(1024), 11, "required bit count") EXPECT(BitBuf.bitsForInt(0xFFFFFFFFFFFF), 48, "required bit count") bb = ByteBuf() bb.w8(200, -50).w16(40000, -20000).w32(3000000000, -1000000000) EXPECT(bb.r8(), 200, "Unsigned 8 bit read") EXPECT(bb.r8(true), -50, "Signed 8 bit read") EXPECT(bb.r16(), 40000, "Unsigned 16 bit read") EXPECT(bb.r16(true), -20000, "Signed 16 bit read") EXPECT(bb.r32(), 3000000000, "Unsigned 32 bit read") EXPECT(bb.r32(true), -1000000000, "Signed 32 bit read") // if errors occur here, this is possibly because of wrong compiler optimization d1 = ByteBuf().wd(5.55555).toString() d2 = ByteBuf().write(5.55555).toString() d3 = BitBuf().wd(5.55555).toString() d4 = BitBuf().write(5.55555).toString() EXPECT(d2, d1, "ByteBuf numeric wd() equal to write() [GCC 4.5.x -O3 bug?]") EXPECT(d3, d4, "BitBuf numeric wd() equal to write() [GCC 4.5.x -O3 bug?]") EXPECT(d3, d1, "ByteBuf numeric wd() equal to BitBuf wd() [GCC 4.5.x -O3 bug?]") EXPECT(d4, d2, "ByteBuf numeric write() equal to BitBuf write() [GCC 4.5.x -O3 bug?]") bb = BitBuf() fa = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0] cmp = { a,b => abs(a-b) < 0.0000001 } for f in fa: bb.wf(f) for i = 0 to len(fa) - 1 f = bb.rf() ck = ByteBufNativeEndian(8).wf(fa[i]) fc = ck.rf() EXPECTDIFF(f, fc, 0.0000001, "BitBuf vs ByteBuf numeric precision") end function bitstr(bb) s = "" bb.rposBits(0) i = 0 while i < bb.sizeBits() bit = bb.rb() s += (bit ? "1" : "0") i++ end return s end bb = ByteBufLittleEndian() bb.w8(8).w16(16).w32(32).w64(64) EXPECT(bb.toString(), "081000200000004000000000000000", "ByteBufLittleEndian byte order") bb = ByteBufBigEndian() bb.w8(8).w16(16).w32(32).w64(64) EXPECT(bb.toString(), "080010000000200000000000000040", "ByteBufBigEndian byte order") EXPECT(bitstr(BitBuf().wb(1,1,1,0,0,0,0,1)), "11100001", "BitBuf bool 1-bit write") EXPECT(bitstr(BitBuf().bitCount(3).writeBits(0)), "000", "BitBuf fixed-bit write (1)") EXPECT(bitstr(BitBuf().bitCount(3).writeBits(0,1)), "000100", "BitBuf fixed-bit write (2)") EXPECT(bitstr(BitBuf().bitCount(3).writeBits(0,1,2)), "000100010", "BitBuf fixed-bit write (3)") EXPECT(bitstr(BitBuf().bitCount(3).writeBits(0,1,2,3)), "000100010110", "BitBuf fixed-bit write (4)") EXPECT(bitstr(BitBuf().bitCount(3).writeBits(0,1,2,3,4)), "000100010110001", "BitBuf fixed-bit write (5)") EXPECT(bitstr(BitBuf().bitCount(3).writeBits(0,1,2,3,4,5)), "000100010110001101", "BitBuf fixed-bit write (6)") EXPECT(bitstr(BitBuf().bitCount(3).writeBits(0,1,2,3,4,5,6)), "000100010110001101011", "BitBuf fixed-bit write (7)") EXPECT(bitstr(BitBuf().bitCount(3).writeBits(0,1,2,3,4,5,6,7)), "000100010110001101011111", "BitBuf fixed-bit write (8)") bb = ByteBuf().write("abc").writeNoNT("ABC") EXPECT(bb.readString(), "abc", "ByteBuf null-terminated string") EXPECT(bb.readString(), "ABC", "ByteBuf not null-terminated string at end of buf") EXPECT(bb.toString(), "61626300414243", "ByteBuf string, hex") bb = ByteBuf().writeNoNT("abc", "ABC").write("-") EXPECT(bb.readString(), "abcABC-", "ByteBuf not null-terminated string continous read") bb = ByteBuf(8) bb.growable(false) bb.w32(1,2) try bb.w8(3) failure("Not-growable buffer write error not raised") end mb = MemBuf(16, 1).fill(0xFF) bc = ByteBufLittleEndian(mb) // copy ba = ByteBufLittleEndian(mb, true) // adapt ba[0] = 0x04 ba[1] = 0x0A ba[5] = 0 EXPECT(mb.get(), 4, "ByteBuf adapt 1") EXPECT(bc[0], 0xFF, "ByteBuf copy 1") EXPECT(mb.get(), 10, "ByteBuf adapt 2") EXPECT(bc[1], 0xFF, "ByteBuf copy 2") EXPECT(ba.r16(), 0x0A04, "ByteBuf adapt read 1") EXPECT(bc.r16(), 0xFFFF, "ByteBuf copy read 1") // pointers + direct memory, reading bb = ByteBufLittleEndian() bb.resize(50) ba = ByteBufLittleEndian() ba.w32(0x12345678).write("extra space string------------!").w16(0xFDFE) ba.readPtr(bb.ptr(), ba.size()) EXPECT(bb.r32(), 0x12345678, "ByteBuf readPtr 1") bb.readString() EXPECT(bb.r16(), 0xFDFE, "ByteBuf readPtr 2") // pointers + direct memoty, writing ba = ByteBuf() ba.w16(0xFFFF).w8(1).w32(0x33445566).w8(0xDD).w16(0xEEEE) bb = ByteBuf() bb.writePtr(ba.ptr() + 3, 5) // skip first 3 bytes and last 2 byte EXPECT(bb.r32(), 0x33445566, "writePtr 1") EXPECT(bb.r8(), 0xDD, "writePtr 2") EXPECT(bb.readable(), 0, "writePtr bytecount") bc = ByteBufReverseEndian() bc.writePtr(ba.ptr() + 3, 5) // same as above EXPECT(bc.r32(), 0x66554433, "writePtr 1 reversed endian") EXPECT(bc.r8(), 0xDD, "writePtr 2 reversed endian") // read to membuf mb = MemBuf(5, 4) // 5x int32 mb[0] = 0x01020304 mb.position(1) bb = ByteBufLittleEndian().w32(5,6,7,8,9) v = bb.readToBuf(mb, 100) EXPECT(v, 16, "readToBuf(LE, WS 4 MB) limit") EXPECT(mb[0], 0x01020304, "readToBuf(LE, WS 4 MB) position()") EXPECT(mb[4], 8, "readToBuf(LE, WS 4 MB) end") mb = MemBuf(5, 4) // 5x int32 mb[0] = 0x01020304 mb.position(1) bb = ByteBufBigEndian().w32(5,6,7,8,9) v = bb.readToBuf(mb, 100) EXPECT(v, 16, "readToBuf(BE, WS 4 MB) limit") EXPECT(mb[0], 0x01020304, "readToBuf(BE, WS 4 MB) position()") EXPECT(mb[4], 8, "readToBuf(BE, WS 4 MB) end") mb = MemBuf(5, 1) // 5x int8 mb[0] = 0x22 mb.position(1) bb = ByteBufLittleEndian().w8(5,6,7,8,9) v = bb.readToBuf(mb) // no 2nd arg here intentionally EXPECT(v, 4, "readToBuf(WS 1 MB) limit") EXPECT(mb[0], 0x22, "readToBuf(WS 1 MB) position()") EXPECT(mb[4], 8, "readToBuf(WS 1 MB) end") bb = ByteBufLittleEndian().w8(2,3,4,5,6).writeNoNT("abcdefghijklmnopqrs") ng = ByteBufLittleEndian().growable(false).resize(15) ng.w8(0xFD, 0xFE) v = bb.readToBuf(ng, 3) // just 3 bytes EXPECT(v, 3, "readToBuf 3 bytes") EXPECT(ng[0], 0xFD, "readToBuf to ByteBuf read 3, wrongly overwritten") EXPECT(ng[4], 4, "readToBuf to ByteBuf read 3, append") v = bb.readToBuf(ng) // (ng is 5 bytes now) ... append the rest, up to the limit EXPECT(v, 10, "readToBuf up to limit") EXPECT(ng[4], 4, "readToBuf limit, unchanged prev val") EXPECT(ng[14], ord("h"), "readToBuf limit, at limit") ng.growable(true) v = bb.readToBuf(ng) // (ng is 15 bytes now) ... append the rest, all remaining EXPECT(v, 11, "readToBuf up to limit") EXPECT(ng[14], ord("h"), "readToBuf fully, unchanged prev val") EXPECT(ng[14+11], ord("s"), "readToBuf fully, unchanged prev val") EXPECT(ng.size(), 2+5+19, "readToBuf mb final size") EXPECT(bb.readable(), 0, "readToBuf src buf empty") // memory region shifting test (right by 1 bit) bb = BitBuf().w8(1,2,4,6,10,0x20,1) //> bitstr(bb); bb.rpos(0) // for clarification, uncomment this line EXPECT(bb.rb(), true, "BitBuf mem region Rshift init") // this is the 1, and shifts the frame t = ByteBufLittleEndian() bb.readToBuf(t) EXPECT(t.r8(), 0, "BitBuf mem region Rshift 1") EXPECT(t.r8(), 1, "BitBuf mem region Rshift 2") EXPECT(t.r8(), 2, "BitBuf mem region Rshift 3") EXPECT(t.r8(), 3, "BitBuf mem region Rshift 4") EXPECT(t.r8(), 5, "BitBuf mem region Rshift 5") EXPECT(t.r8(), 0x10 + 0x80, "BitBuf mem region Rshift 6") // (0x20 >> 1) + (1 shifted to right once, into byte beeing read) EXPECT(bb.readableBits(), 7, "BitBuf mem region Rshift 1 bit less") bb.wb(true) // 1 extra bit, will be highest bb.readToBuf(t) EXPECT(t.r8(), 0x80, "BitBuf mem region Rshift extra bit") // memory region shifting test (left by 1 bit) bb = BitBuf().wb(false).w8(1,2,4,6,10,0x20) //> bitstr(bb); bb.rposBits(0) // for clarification, uncomment this line t = ByteBufLittleEndian() bb.readToBuf(t) EXPECT(t.r8(), 2, "BitBuf mem region Lshift 1") EXPECT(t.r8(), 4, "BitBuf mem region Lshift 2") EXPECT(t.r8(), 8, "BitBuf mem region Lshift 3") EXPECT(t.r8(), 12, "BitBuf mem region Lshift 4") EXPECT(t.r8(), 20, "BitBuf mem region Lshift 5") EXPECT(t.r8(), 0x40, "BitBuf mem region Lshift 6") EXPECT(bb.readableBits(), 1, "BitBuf mem region Lshift last bit") bb.bitCount(7).writeBits(0) bb.readToBuf(t) EXPECT(t.r8(), 0, "BitBuf mem region Lshift extra bits") // toMemBuf tests bb = ByteBuf(100).w32(0xFF0000FF) for i = 0 to 90: bb.w8(i) // fill up a bit mb = bb.toMemBuf() // default behavior: adopt EXPECT(mb.ptr(), bb.ptr(), "toMemBuf adopt ptr equality") cp = bb.toMemBuf(true) // copy; ptrs must be different if cp.ptr() == bb.ptr() failure("toMemBuf copy ptr must be different") end p = bb.ptr() bb.write("this enlarges the buffer and forces reallocation") if bb.ptr() == p // check if it was really re-allocated failure("buffer enlarged, but not reallocated") end // now mb is invalid; it still uses the old pointer prior to re-allocation EXPECT(mb.ptr(), p, "toMemBuf adopt ptr after reallocation") // must be same old (but now invalid) ptr EXPECT(ByteBuf(cp,true).r32(), 0xFF0000FF, "toMemBuf re-adopt read (copy)") success() tests/feathers/testsuite/bufext_gc.fal000066400000000000000000000033271176363201700205170ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 60b * Category: Bufext * Subcategory: * Short: bufext vs. GC * Description: * The GC should not free buffers still in use, including memory adopted * by other buffers. * This test also tests a possible GC stack overflow if the internal * adoption hierarchy is not set up correctly. * [/Description] **************************************************************************/ load bufext function EXPECT(actual, expected, str) if(actual != expected) failure("Expected: '" + expected + "', actual: '" + actual + "' <-- " + str) end end // ByteBuf dependency smoke test (still required bufs may not be garbaged) mb = MemBuf(50000, 4).fill(0xFFFFFFFF) // a large object, interesting for the GC mb.put(42) b = ByteBufNativeEndian(mb, true) mb = nil for i = 0 to 500 EXPECT(b.r32(), 42, "ByteBuf recursive adapt dependency test") b = ByteBufNativeEndian(b, true).growable(false) // adapt end GC.perform(true) // if dependencies are not set up correctly, this may cause multi-free of the internal memory, and segfault x = b.r32() // if mb was garbaged, this will likely segfault EXPECT(x, 42, "ByteBuf recursive adapt dependency test, final") b = ByteBufNativeEndian(b, true).growable(false) // adapt for i = 0 to 100000 EXPECT(b.r32(), 42, "ByteBuf recursive adapt GC stack overflow test") b = ByteBufNativeEndian(b, true).growable(false) // adapt end GC.perform(true) // if a long dependency chain was built up, this must not cause GC stack overflow x = b.r32() // if mb was garbaged, this will likely segfault EXPECT(x, 42, "ByteBuf recursive adapt stack overflow test, final") b = nil success() tests/feathers/testsuite/bufext_long.fal000066400000000000000000000110661176363201700210640ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 61a * Category: bufext * Subcategory: * Short: Bufext stress test / random data * Description: * Stress test for the buffer classes provided by the bufext module, * filling buffers with random data and verifying that reading afterwards * will return the same data that have been written to them. * (If some tests fail due to float rounding, try compiler settings * that increase floating point precision) * [/Description] **************************************************************************/ load bufext GC.adjust(GC.ADJ_STRICT) // we will produce a lot of garbge randomSeed(8736275764) // make results consistent function EXPECT(actual, expected, str) if(actual != expected) failure("Expected: '" + expected + "', actual: '" + actual + "' <-- " + str) end end function EXPECTDIFF(actual, expected, diff, str) if(abs(actual - expected) > diff) failure("Expected: '" + expected + "', actual: '" + actual + "' <-- " + str) end end function genRString() lim = random(20) s = strBuffer(lim + 1) while lim lim-- s %= random(97, 112) end s.charSize(1) return s end typenames = [ "bool" ,"i8 " ,"i16 " ,"i32 " ,"i64 " ,"flt " ,"dbl " ,"str " ] writefunc = [ { b,v => b.wb(v) } ,{ b,v => b.w8(v) } ,{ b,v => b.w16(v) } ,{ b,v => b.w32(v) } ,{ b,v => b.w64(v) } ,{ b,v => b.wf(v) } ,{ b,v => b.wd(v) } ,{ b,v => b.write(v) } ] readfunc = [ { b => b.rb() } ,{ b => b.r8() } ,{ b => b.r16() } ,{ b => b.r32() } ,{ b => b.r64() } ,{ b => b.rf() } ,{ b => b.rd() } ,{ b => b.readString(1, 50) } ] genfunc = [ { => random(1) ? true : false } ,[random, 0, 255] ,[random, 0, 65535] ,[random, 0, 4294967295] ,[random, 0, 1152921504606846976] ,{ => random() * 100} ,{ => random() * 1000} , genRString ] cmpfunc = [ { a, b => a == b } ,{ a, b => a == b } ,{ a, b => a == b } ,{ a, b => a == b } ,{ a, b => a == b } ,{ a, b => abs(a-b) < 0.00001 } // float precision is never so good ,{ a, b, strict => strict ? (a == b) : (abs(a-b) < 0.000000001) } ,{ a, b => a == b } ] function buftest(bb, strict) lim = random(1500) + 1 vals = arrayBuffer(lim) ps = arrayBuffer(lim) x = len(writefunc) - 1 i = 0 while i < lim p = random(x) item = genfunc[p]() vals[i] = item ps[i] = p tn = typenames[p] writefunc[p](bb, item) i++ end i = 0 while i < lim item = vals[i] p = ps[i] r = readfunc[p](bb) tn = typenames[p] if not cmpfunc[p](item,r,strict) failure(@ "[" + bb.className() + "] ERROR! ty: '$tn', i = $i, p = $p, should be: '$item', is: '$r'") end i++ end if bb.readable() if(bb provides sizeBits) // is BitBuf failure("Readable, should not be: wposBits = " + bb.wposBits() + ", rpos = " + bb.rposBits() + ", sizeBits = " + bb.sizeBits() + ", readableBits = " + bb.readableBits()) else failure("Readable, should not be: wpos = " + bb.wpos() + ", rpos = " + bb.rpos() + ", capacity = " + bb.capacity() + ", size = " + bb.size() + ", readable = " + bb.readable()) end end end normalruns = 500 bitbufruns = 200 maxruns = normalruns + (bitbufruns * 64) runcount = 0 bufs = [ [BitBuf, false], [ByteBuf, false], [ByteBufNativeEndian, true], [ByteBufLittleEndian, false], [ByteBufBigEndian, false], [ByteBufReverseEndian, false] ] for cons, strict in bufs for i = 0 to normalruns alive(runcount / maxruns * 100) buftest(cons(), strict) ++runcount end end for i = 1 to 63 // inclusive - cause all different possibilities for shifted patterns // we use the same random seed for each pattern, so the data are always the same, just shifted by different byte amounts randomSeed(873876698769) alive(runcount / maxruns * 100) for j = 0 to bitbufruns bb = BitBuf() // this will have the whole buf shifted by i bytes bb.bitCount(i) bb.writeBits(0x0123456789abcdef) junk = bb.readBits() buftest(bb, false) ++runcount end end success() tests/feathers/testsuite/json_dec2.fal000066400000000000000000000014311176363201700204110ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 40b * Category: json * Subcategory: * Short: Json decode * Description: * Try to decode a pretty twisty * [/Description] * ****************************************************************************/ load json mytest = '{"test_obj":{test_v1:"zero",test_v2:null},test_val:true,test_arr:[0,1,false],"ta2":["a","b"],"c":"d"}' try v = JSONdecode( mytest ) catch JSONError failure( "Can't decode the string" ) end if "test_obj" notin v: failure( "toplevel" ) test_obj = v["test_obj"] if test_obj["test_v2"] != nil: failure( "Null value" ) if v["test_val"] != true: failure( "flat value" ) if v["test_arr"][2] != false: failure( "final false" ) success() /* End of file */ tests/feathers/testsuite/json_decode.fal000066400000000000000000000073751176363201700210340ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 40a * Category: json * Subcategory: * Short: Json decode * Description: * Try to decode a pretty complex json data. * [/Description] * ****************************************************************************/ import from json mytest = " { fsm:{ name:'Vending Machine', states:{ state:[ { name:'start', transition:[ { input:'nickel', next:'five' }, { input:'dime', next:'ten' }, { input:'quarter', next:'start', action:'dispense' } ] }, { name:'five', transition:[ { input:'nickel', next:'ten' }, { input:'dime', next:'fifteen' }, { input:'quarter', next:'start', action:'dispense' } ] }, { name:'ten', transition:[ { input:'nickel', next:'fifteen' }, { input:'dime', next:'twenty' }, { input:'quarter', next:'start', action:'dispense' } ] }, { name:'fifteen', transition:[ { input:'nickel', next:'twenty' }, { input:'dime', next:'start', action:'dispense' }, { input:'quarter', next:'start', action:'dispense' } ] }, { name:'twenty', transition:[ { input:'nickel', next:'start', action:'dispense' }, { input:'dime', next:'start', action:'dispense' }, { input:'quarter', next:'start', action:'dispense' } ] } ] } } } " try v = json.JSONdecode( mytest ) catch failure( "Can't decode the string" ) end if "fsm" notin v: failure( "toplevel" ) fsm = v["fsm"] if fsm["name"] != "Vending Machine": failure( "second level" ) if fsm["states"]["state"].len() != 5: failure( "Third level" ) success() /* End of file */ tests/feathers/testsuite/math_extra.fal000066400000000000000000000014031176363201700206760ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 70 * Category: math_extra * Subcategory: * Short: * Description: * Make sure the functions are returning sane results. * [/Description] * **************************************************************************/ load math_extra if cosh(1) != 1.54308: failure( "cosh" ) if sinh(1) != 1.1752: failure( "sinh" ) if tanh(1) != 0.761594: failure( "tanh" ) if acosh(1) != 0: failure( "acosh" ) if asinh(1) != 0.881374: failure( "asinh" ) if atanh(0.5) != 0.549306: failure( "atanh" ) if sec(1) != 1.85082: failure( "sec" ) if csc(1) != 1.1884: failure( "csc" ) if cotan(1) != 0.642093: failure( "cotan" ) if lambda(2,4) != 256: failure( "lambda" ) success() tests/feathers/testsuite/modunload.fal000066400000000000000000000037331176363201700205340ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 20d * Category: reflexive * Subcategory: * Short: Basic reflexive compiler * Description: * Loads the functions and data in reflexcomp_2.fal, accessing them * through VM export facility. Several tests are perfomed to ensure * that the module is correctly cleaned after unload. In case of * errors, it is easy that this test may crash falcon. * [/Description] * ****************************************************************************/ load compiler comp = Compiler() disk, modPath, fname, ext = fileNameSplit( scriptPath ) comp.path = modPath comp.saveModules = false // record the garbage status. GC.perform( true ) mem = GC.usedMem items = GC.items // try once mod = comp.loadByName( "reflexcomp_2" ) // get the function symbol te = mod.get( "testExport" ) if te( 1, 2, 3 ) != 6: failure( "Exported function" ) // save the string in the module. str = mod.get( "returnString" )() if str != "A string from the module" failure( "Can't get string from the module" ) end // and create an instance. instance = mod.get("TestCls")() if instance.mth() != "a string" failure( "Can't get the method from the module" ) end // check if we have a correct report of updated memory. if GC.usedMem == mem or items == GC.items failure( "Memory increase after load not reported" ) end other_string = instance.mth() return // unload mod.unload() // We still hold the method, and the string must still exist. GC.perform(true) if str != "A string from the module" failure( "Remote string killed after unload" ) end if other_string != "a string" failure( "Returned string killed after unload" ) end // discharge the module mod = te = str = instance = nil GC.perform(true) // have we some leak? /* if GC.usedMem != mem or items != GC.items > "Mem: ", GC.usedMem - mem > "Items: ", GC.items - items failure( "Leaks after clean unload" ) end */ success() /* end of modunload.fal */ tests/feathers/testsuite/mtbasic_det.fal000066400000000000000000000010661176363201700210250ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 50b * Category: threading * Subcategory: * Short: Basic threading (detach). * Description: * Checks that a single thread runs and is then detached. * [/Description] * **************************************************************************/ load threading class Test from Thread function run() self.detach() sleep(0.001) end end Test().start() // ten times the detached thread sleep(0.01) success() tests/feathers/testsuite/mtbasic_join.fal000066400000000000000000000010771176363201700212120ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 50a * Category: threading * Subcategory: * Short: Basic threading (detach). * Description: * Checks that a single thread runs and is then detached. * [/Description] * **************************************************************************/ load threading class Test from Thread function run() return "A basic value" end end t = Test() t.start() val = t.join() if not "A basic value" == val: failure( "Join return" ) success() tests/feathers/testsuite/processEnum.fal000066400000000000000000000013361176363201700210520ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 62a * Category: process * Subcategory: * Short: ProcessEnums is tested to list faltest itself * Description: * * [/Description] * ****************************************************************************/ load process processIterator = ProcessEnum() found = false while processIterator.next() name = processIterator.name pid = processIterator.pid parentPid = processIterator.parentPid cmdLine = processIterator.cmdLine if name.startsWith("faltest"): foundName = true if "faltest" in cmdLine: foundCmd = true end if not (foundName and foundCmd): failure("ProcessEnum didn't enlist faltest itself") success() tests/feathers/testsuite/process_run.fal000066400000000000000000000044001176363201700211040ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 60a * Category: process * Subcategory: * Short: Makes a simple directory listing in every possible way. * Description: * * [/Description] * ****************************************************************************/ load process if vmSystemType() == "WIN" command_str = "cmd /C dir" command_array = ["cmd", "/C", "dir"] else // POSIX command_str = "sh --version" command_array = ["sh", "--version"] end /* system */ try > "system(command_str)" system(command_str) catch in e > e failure( "system failed on command string" ) end // try > "system(command_str)" system(command_array) catch in e > e failure( "system failed on command array" ) end /* systemCall */ try > "systemCall(command_str)" systemCall(command_str) catch in e > e failure( "systemCall failed on command string" ) end // try systemCall(command_array) catch in e > e failure( "systemCall failed on command array" ) end /* pread */ try > "pread(command_str)" output = pread(command_str) if output == "": raise "not output" > output catch in e > e failure( "pread failed on command string" ) end // try > "pread(command_array)" output = pread(command_array) > output catch in e > e failure( "pread failed on command array" ) end /* Process TODO: all combinations of FLAGS */ try > "Process(command_str)" process = Process(command_str) pout = process.getOutput() while ( ret = process.value() ) if pout.readAvailable( 0.1 ): > pout.grab(1024) end > "ret=", ret if ret: raise "fail" catch in e > e failure( "Process failed on command string" ) end // try > "Process(command_array)" process = Process(command_array) pout = process.getOutput() while ( ret = process.value() ) if pout.readAvailable( 0.1 ): > pout.grab(1024) end > "ret=", ret if ret: raise "fail" catch in e > e failure( "Process failed on command string" ) end // /* // exec // */ // try // exec(command_str, true) // catch in e // > e // failure( "exec failed on command string" ) // end // try // exec(command_array, true) // catch in e // > e // failure( "exec failed on command array" ) // end success() tests/feathers/testsuite/reflexcomp.fal000066400000000000000000000050701176363201700207120ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 20a * Category: reflexive * Subcategory: * Short: Basic reflexive compiler * Description: * Loads the functions reflexcomp_1.fal twice sending and * getting some data. * [/Description] * ****************************************************************************/ load compiler class TestClass function func( param ) return "Loader class func " + param.toString() end end object TestObj function func( param ) return "Loader obj func " + param.toString() end end function testFunc( param ) return "Loader func " + param.toString() end comp = Compiler() disk, modPath, fname, ext = fileNameSplit( scriptPath ) comp.path = modPath comp.saveModules = false // try once mod1 = comp.loadByName( "reflexcomp_1" ) mod2 = comp.loadFile( modPath + "/reflexcomp_1.fal" ) test( mod1, "Module1 - first try" ) test( mod2, "Module2 - first try" ) // try again mod1 = comp.loadByName( "reflexcomp_1" ) mod2 = comp.loadFile( modPath + "/reflexcomp_1.fal" ) test( mod1, "Module1 - second try" ) test( mod2, "Module2 - second try" ) function test( module, reason ) res = module.get( "__main__" )() if res != "main": failure( reason + " + main" ) res = module.get( "func" )( "val1" ) if res != "func val1": failure( reason + " - func" ) obj = module.get( "obj" ) if obj.func( "val1" ) != "obj val1": failure( reason + " - obj func" ) if obj.initData != "Initialized": failure( reason + " - init obj" ) // now inject some data. module.set( "global_var", "set" ) if module.get( "global_var" ) != "set": failure( reason + " - global value" ) if module.get( "func_global" )( "param" ) != "func_global set param" failure( reason + " - global from func" ) end module.set( "global_sym", testFunc ) res = module.get( "func_global_sym" )( "param" ) if res != "Loader func param": failure( reason + " - injected function" ) module.set( "global_sym", TestObj ) res = module.get( "func_global_obj" )( "param" ) if res != "Loader obj func param": failure( reason + " - injected object" ) module.set( "global_sym", TestClass ) res = module.get( "func_global_class" )( "param" ) if res != "Loader class func param": failure( reason + " - injected class" ) // try to import an ojbect, locally inject the symbol and call the // remote method. obj.remote = testFunc if obj.callRemote( "param" ) != "Loader func param" failure( reason + " - injected remote method" ) end end success() /* End of file */ tests/feathers/testsuite/reflexcomp_1.fal000066400000000000000000000030021176363201700211230ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: -- * Category: reflexive * Subcategory: * Short: Basic reflexive compiler * Description: * Loaded by reflexcomp as a module * getting some data. * [/Description] * ****************************************************************************/ // Some globals global_var = nil global_sym = nil // The object that will be used by the loader object obj initData = nil remote = nil init // this code must be executed at module loading self.initData = "Initialized" end function func( param ) return "obj " + param.toString() end function callRemote( param ) return self.remote( param ) end end // The functions that will be called by the loader function func( param ) return "func " + param.toString() end function func_global( param ) return "func_global " + global_var + " " + param end function func_global_sym( param ) // global_sym is now a function. Call it. return global_sym( param ) end function func_global_obj( param ) // now the global item is an object return global_sym.func( param ) end function func_global_class( param ) // now global_sym should be a class. Instance it... instance = global_sym() // and call a method return instance.func( param ) end //================================== // main code -- must return "main" marr = [ 'm', 'a', 'i', 'n' ] ret = "" for c in marr ret += c end return ret /* End of file */ tests/feathers/testsuite/reflexcomp_2.fal000066400000000000000000000011441176363201700211310ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: -- * Category: reflexive * Subcategory: * Short: Basic reflexive compiler * Description: * Loaded by modunload as a module * [/Description] * ****************************************************************************/ function testExport( one, two, three ) return one + two + three end exported = "a string" function returnString() return "A string from the module" end class TestCls function mth() return "a string" end end export testExport, exported, TestCls /* End of file */ tests/feathers/testsuite/reflexcomp_err.fal000066400000000000000000000023111176363201700215550ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 20c * Category: reflexive * Subcategory: * Short: Reflexive compiler errors * Description: * Checks that errors in reflexive compilations are correctly reported. * [/Description] * ****************************************************************************/ load compiler comp = Compiler() // test 1 - syntax error in compilation str = ' array = [1,2,3 4] val = 0 for e in array val += e end return e' try mod = comp.compile( "myPersonalModule", str ) failure( "Compilation error not raised" ) end // Test 2 - trying to load unexisting modules try mod = comp.loadByName( "an impossible module" ) failure( "Load (by name) error not raised" ) end try mod = comp.loadFile( "an impossible module" ) failure( "Load module error not raised" ) end // Test 3 -- unlink order disk, modPath, fname, ext = fileNameSplit( scriptPath ) comp.path = modPath // try to unload a module loaded after another mod = comp.compile( "testmod", "test = 0" ) mod1 = comp.loadByName( "reflexcomp_1" ) test_func = mod1.get( "func" ) if not mod.unload(): failure( "free unload not respected" ) mod1.unload() success() tests/feathers/testsuite/reflexcomp_str.fal000066400000000000000000000012231176363201700215760ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * * ID: 20b * Category: reflexive * Subcategory: * Short: Internal reflexive compiler * Description: * Compiles a string on the fly. As the main features are checked in 54a, * only basic functionality needs to be checked. * [/Description] * ****************************************************************************/ load compiler str = ' array = [1, 2,3,4] val = 0 for e in array val += e end return val' comp = Compiler() mod = comp.compile( "my_module", str ) if mod.get( "__main__" )() != 10: failure( "Compilation result" ) success() tests/feathers/testsuite/regexGrab.fal000066400000000000000000000022061176363201700204520ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 30c * Category: regex * Subcategory: * Short: Regex grab * Description: * Checks grab method in regular expressions. * We also take the occasion to check for capturedCount() * [/Description] * ****************************************************************************/ load regex r = Regex( "a.b" ) if r.capturedCount() != 0: failure("Captured count - 0.") if r.grab( "akb" )[0] != "akb": failure( "Grab all" ) if r.grab( "akbn" )[0] != "akb": failure( "Grab begin" ) if r.grab( "nakb" )[0] != "akb": failure( "Grab emd" ) if r.grab( "ancazbcanb" )[0] != "azb": failure( "Grab inside" ) if r.grab( "zzz" ): failure( "False positive" ) // now try with some captured expression r = Regex( "(..)/(..)/(....)" ) g = r.grab( "ab12/02/2001cd" ) if r.capturedCount() != 4: failure("Captured count - 1.") if g.len() != 4: failure( "multigrab - size" ) if g[0] != "12/02/2001": failure( "multigrab - all" ) if g[1] != "12": failure( "multigrab - 1" ) if g[2] != "02": failure( "multigrab - 2" ) if g[3] != "2001": failure( "multigrab - 3" ) success() tests/feathers/testsuite/regexOp.fal000066400000000000000000000013371176363201700201610ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 30b * Category: regex * Subcategory: * Short: Comprare override * Description: * Checks for == operator to work through compare() method. * [/Description] * ****************************************************************************/ load regex r = Regex( "a.b" ) if r.compare( "abb" ) != 0: failure( "Method, false negative" ) if r.match( "zzz" ) == 0: failure( "Method, false positive" ) if r != "abb": failure( "operator, false negative" ) if r == "zzz": failure( "operator, false positive" ) switch "abb" case r c = "ok" default c = "fail" end if c != "ok": failure( "switch comparison" ) success() tests/feathers/testsuite/regexSmoke.fal000066400000000000000000000013201176363201700206510ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 30a * Category: regex * Subcategory: * Short: Smoke regex * Description: * Checks minimal working of the regex module. * Notice: Tests in "regex" category are not meant to check the correctness * of the regular expression syntax and constructs; those are delegated to * the underlying PCRE library. This tests are * [/Description] * ****************************************************************************/ load regex r = Regex( "a.b" ) if not r.version(): failure( "missing version" ) if not r.match( "abb" ): failure( "False negative" ) if r.match( "zzz" ): failure( "False positive" ) success() tests/feathers/testsuite/test_driver.cmake.in000066400000000000000000000012011176363201700220130ustar00rootroot00000000000000find_program(faltest_EXECUTABLE NAMES faltest HINTS "@CMAKE_INSTALL_PREFIX@/bin" NO_DEFAULT_PATH ) if(NOT faltest_EXECUTABLE) message(FATAL_ERROR "fatest executable not found") endif() if(test_category) set(cmd ${faltest_EXECUTABLE} -v -c ${test_category}) else() set(cmd ${faltest_EXECUTABLE} -v ) endif() execute_process( COMMAND ${cmd} WORKING_DIRECTORY @CMAKE_CURRENT_SOURCE_DIR@ RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_VARIABLE err ) if(res) message("faltest return value: ${res}") message("faltest stderr: ${err}") message("faltest stdout: ${out}") message(SEND_ERROR "test(s) failed") endif() tests/feathers/testsuite/zb.fal000066400000000000000000000005371176363201700171640ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: -- * Category: threading * Subcategory: * Short: Runtime re-creation * Description: * See tests 51a and 51b * [/Description] * **************************************************************************/ function zb() return 0 end export tests/feathers/testsuite/zlibCompIntString.fal000066400000000000000000000012231176363201700221630ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 10c * Category: zlib * Subcategory: * Short: Compress and Uncompress string data containing internationalized strings. * Description: * * [/Description] * ****************************************************************************/ load zlib original = "これは国際のストリングですよ.どうぞ、大切にして、コムプレッソをしてくれ下さい。" z = ZLib() compressed = z.compressText( original ) uncompressed = z.uncompressText( compressed ) if original != uncompressed: failure( "original != uncompressed" ) success() tests/feathers/testsuite/zlibCompString.fal000066400000000000000000000010551176363201700215130ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 10b * Category: zlib * Subcategory: * Short: Compress and Uncompress string data * Description: * * [/Description] * ****************************************************************************/ load zlib original = "Mary had a little lamb, it's fleece was white as snow." z = ZLib() compressed = z.compressText( original ) uncompressed = z.uncompressText( compressed ) if original != uncompressed: failure( "original != uncompressed" ) success() tests/feathers/testsuite/zlibCompUncomp.fal000066400000000000000000000040331176363201700215050ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 10a * Category: zlib * Subcategory: * Short: Compress and Uncompress binary data * Description: * Creates a some binary buffers, compress and uncompress them. * [/Description] * ****************************************************************************/ load zlib // some functions to fill the buffers function blank( buffer, pos ) buffer[pos] = 0 end function modPos( buffer, pos ) buffer[pos] = pos % 256 end function flatRnd( spread, buffer, pos ) buffer[pos] = 256 / 2 + int((0.5 - random()) * spread) end buffers = [] buffers += [[MemBuf( 256 ), blank]] buffers += [[MemBuf( 512 ), modPos]] buffers += [[MemBuf( 1024 ), modPos]] buffers += [[MemBuf( 2048 ), [flatRnd, 10] ]] buffers += [[MemBuf( 4096 ), [flatRnd, 100] ]] // some oddly shaped buffer buffers += [[MemBuf( 129 ), modPos]] buffers += [[MemBuf( 1200 ), modPos]] buffers += [[MemBuf( 3121 ), blank]] buffers += [[MemBuf( 3121 ), [flatRnd,100] ]] // fill the buffers for buffer, algo in buffers for pos in [0:buffer.len()] algo(buffer, pos) end end // compress them z = ZLib() compbufs = [] for i in [0:buffers.len()] try compbufs += z.compress( buffers[i][0] ) catch ZLibError in err failure( "Buffer " + i + " failed compression:\n" + err ) end end // uncompress the buffers and see if they are the same. function compareBuffers( b1, b2 ) if b1.len() != b2.len() return -1 end for pos in [0:b1.len()] if b1[pos] != b2[pos] return pos end end return nil end for i in [0:buffers.len()] try unc = z.uncompress( compbufs[i] ) catch ZLibError in err failure( "Buffer " + i + " failed de-compression:\n" + err ) end res = compareBuffers( buffers[i][0], unc ) if res != nil if res == -1 failure( "Buffer " + i + " different in size" ) else failure( "Buffer " + i + " different at position " + res ) end end end success() tests/feathers/testsuite/zlibErrors.fal000066400000000000000000000035131176363201700207030ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 10f * Category: zlib * Subcategory: error * Short: ZLib errors. * Description: * Checks that correct errors are raised when zlib is misused (and that we * don't crash if buffers are messed up). * [/Description] * ****************************************************************************/ load zlib // first, a simple param error check z = ZLib() try z.compress(0) failure( "Parameter error not raised - 1" ) catch ParamError end try z.uncompress(nil) failure( "Parameter error not raised - 2" ) catch ParamError end try z.compressText( 1.2 ) failure( "Parameter error not raised - 3" ) catch ParamError end try z.uncompressText( nil ) failure( "Parameter error not raised - 4" ) catch ParamError end // then, try to uncompress something wrong try z.uncompressText( "Hello, this is an uncompressed text" ) failure( "Uncompressing a random string" ) catch ZLibError in e //inspect( e ) end // then, try to uncompress something wrong try z.uncompress( MemBuf( 4096 ) ) failure( "Uncompressing a random memory buffer" ) catch ZLibError in e //inspect( e ) end // and finally, compress some data and mess it up data = z.compress( MemBuf( 4096 ) ) data[10] = 99 try z.uncompress( data ) failure( "Uncompressing a corrupted buffer" ) catch ZLibError in e //inspect( e ) end data = z.compressText( "Hello, this is an uncompressed text" ) data[0] = 10 try z.uncompressText( data ) failure( "Uncompressing a corrupted string (length)" ) catch ZLibError in e //inspect( e ) end data = z.compressText( "Hello, this is an uncompressed text" ) data[6] = 10 try z.uncompressText( data ) failure( "Uncompressing a corrupted string (data)" ) catch ZLibError in e //inspect( e ) end success() tests/feathers/testsuite/zlibLargeStrings.fal000066400000000000000000000044021176363201700220310ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 10d * Category: zlib * Subcategory: * Short: Zlib on large strings * Description: * Compress and Uncompress rather large and variegated string data. * [/Description] * ****************************************************************************/ load zlib // some functions to fill the buffers function blank( buffer, pos ) buffer[pos] = 0 end function modPos( buffer, pos ) buffer[pos] = pos % 256 end function flatRnd( spread, buffer, pos ) buffer[pos] = 256 / 2 + int((0.5 - random()) * spread) end function flatInt( spread, buffer, pos ) buffer[pos] = 5600 + int((0.5 - random()) * spread) end buffers = [] buffers += [[strReplicate( ' ', 256 ), blank]] buffers += [[strReplicate( ' ', 512 ), modPos]] buffers += [[strReplicate( ' ', 1024 ), modPos]] buffers += [[strReplicate( ' ', 2048 ), [flatRnd, 10] ]] buffers += [[strReplicate( ' ', 4096 ), [flatRnd, 100] ]] // some oddly shaped buffer buffers += [[strReplicate( ' ', 129 ), modPos]] buffers += [[strReplicate( ' ', 1200 ), modPos]] buffers += [[strReplicate( ' ', 3121 ), blank]] // also an international string buffers += [[strReplicate( ' ', 3121 ), [flatInt,1000] ]] // fill the buffers for buffer, algo in buffers for pos in [0:buffer.len()] algo(buffer, pos) end end // compress them z = ZLib() compbufs = [] for i in [0:buffers.len()] try compbufs += z.compressText( buffers[i][0] ) catch ZLibError in err failure( "Buffer " + i + " failed compression:\n" + err ) end end // uncompress the buffers and see if they are the same. function compareBuffers( b1, b2 ) if b1.len() != b2.len() return -1 end for pos in [0:b1.len()] if b1[pos] != b2[pos] return pos end end return nil end for i in [0:buffers.len()] try unc = z.uncompressText( compbufs[i] ) catch ZLibError in err failure( "Buffer " + i + " failed de-compression:\n" + err ) end res = compareBuffers( buffers[i][0], unc ) if res != nil if res == -1 failure( "Buffer " + i + " different in size" ) else failure( "Buffer " + i + " different at position " + res ) end end end success() tests/feathers/testsuite/zlibStringFromBuf.fal000066400000000000000000000045361176363201700221640ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 10e * Category: zlib * Subcategory: * Short: ZLib strigns/buffer mixed. * Description: * This test verifies that string data converted to binary data via * a simple compress can be turned back into a string via strFromMemBuf. * [/Description] * ****************************************************************************/ load zlib // some functions to fill the buffers function blank( buffer, pos ) buffer[pos] = 0 end function modPos( buffer, pos ) buffer[pos] = pos % 256 end function flatRnd( spread, buffer, pos ) buffer[pos] = 256 / 2 + int((0.5 - random()) * spread) end function flatInt( spread, buffer, pos ) buffer[pos] = 5600 + int((0.5 - random()) * spread) end buffers = [] buffers += [[strReplicate( ' ', 256 ), blank]] buffers += [[strReplicate( ' ', 512 ), modPos]] buffers += [[strReplicate( ' ', 1024 ), modPos]] buffers += [[strReplicate( ' ', 2048 ), [flatRnd, 10] ]] buffers += [[strReplicate( ' ', 4096 ), [flatRnd, 100] ]] // some oddly shaped buffer buffers += [[strReplicate( ' ', 129 ), modPos]] buffers += [[strReplicate( ' ', 1200 ), modPos]] buffers += [[strReplicate( ' ', 3121 ), blank]] // strFromMemBuf doesn't work on international strings unless // external hints are provided. // fill the buffers for buffer, algo in buffers for pos in [0:buffer.len()] algo(buffer, pos) end end // compress them z = ZLib() compbufs = [] for i in [0:buffers.len()] try compbufs += z.compress( buffers[i][0] ) catch ZLibError in err failure( "Buffer " + i + " failed compression:\n" + err ) end end // uncompress the buffers and see if they are the same. function compareBuffers( b1, b2 ) if b1.len() != b2.len() return -1 end for pos in [0:b1.len()] if b1[pos] != b2[pos] return pos end end return nil end for i in [0:buffers.len()] try unc = strFromMemBuf( z.uncompress( compbufs[i] ) ) catch ZLibError in err failure( "Buffer " + i + " failed de-compression:\n" + err ) end res = compareBuffers( buffers[i][0], unc ) if res != nil if res == -1 failure( "Buffer " + i + " different in size" ) else failure( "Buffer " + i + " different at position " + res ) end end end success() tests/feathers/testsuite/zth.fal000066400000000000000000000017661176363201700173630ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 51b * Category: threading * Subcategory: * Short: Runtime re-creation (2) * Description: * The threading module must create a vm similar to the original one * when starting a new thread. To do that, it creates a new runtime * filling it with the modules coming from the calling VM, and links * it in a new VM. * * This test checks that dependency from core and from threading is * correctly restacked. It consists of two scripts, with names at the * opposite spectrum of the module map, so that if correct order is not * granted, a link error will arise. * * In this second test, the name of the calling module is the lowest. * [/Description] * **************************************************************************/ load threading load AB load zb class Th from Thread function run() ab() zb() return 0 end end t = Th() t.start() t.join() success() tests/feathers/testsuite/zth2.fal000066400000000000000000000016501176363201700174350ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite * * ID: 51d * Category: threading * Subcategory: * Short: Runtime re-creation (1) * Description: * The threading module must create a vm similar to the original one * when starting a new thread. To do that, it creates a new runtime * filling it with the modules coming from the calling VM, and links * it in a new VM. * * This test checks that dependency from core and from threading is * correctly restacked. It consists of two scripts, with names at the * opposite spectrum of the module map, so that if correct order is not * granted, a link error will arise. * * In this first test, the name of the calling module is the highest. * [/Description] * **************************************************************************/ load threading load AB load zb Threading.start({=> return 0}).join() success() tests/frameworks/000077500000000000000000000000001176363201700144065ustar00rootroot00000000000000tests/frameworks/nest/000077500000000000000000000000001176363201700153575ustar00rootroot00000000000000tests/frameworks/nest/nest/000077500000000000000000000000001176363201700163305ustar00rootroot00000000000000tests/frameworks/nest/nest/ajax/000077500000000000000000000000001176363201700172535ustar00rootroot00000000000000tests/frameworks/nest/nest/ajax/AddTableRecord.fal000066400000000000000000000007401176363201700225370ustar00rootroot00000000000000function AddTableRecord( req ) data = Wopi.getAppData("table-app") record = [ "name" => req["name"], "sname" => req["sname"], "age" => req["age"] ] while true if data id = data[-1]["id"] + 1 else id = 1 end record["id"] = id data.add( record ) if Wopi.setAppData(data, "table-app"): break data = Wopi.getAppData("table-app") end return ["updated" => id] end tests/frameworks/nest/nest/ajax/DeleteTableRecord.fal000066400000000000000000000004601176363201700232500ustar00rootroot00000000000000function DeleteTableRecord( req ) id = int(req["id"]) data = Wopi.getAppData("table-app") number = arrayScan( data, { v => v["id"] == id} ) if number >= 0 data.remove(number) Wopi.setAppData(data, "table-app") return ["removed" => id] end return ["unknown" => id] end tests/frameworks/nest/nest/ajax/GetTableRecord.fal000066400000000000000000000003451176363201700225670ustar00rootroot00000000000000function GetTableRecord( req ) id = int(req["id"]) data = Wopi.getAppData("table-app") number = arrayScan( data, { v => v["id"] == id} ) if number >= 0 return data[number] end return ["unknown" => id] end tests/frameworks/nest/nest/ajax/TableMultiDelete.fal000066400000000000000000000006371176363201700231320ustar00rootroot00000000000000function TableMultiDelete( req ) count = 0 while true data = Wopi.getAppData("table-app") for item in req id = int(item["key"]) number = arrayScan( data, { v => v["id"] == id} ) if number >= 0 data.remove(number) ++count end end if Wopi.setAppData(data, "table-app"): break end return ["removed" => count] end tests/frameworks/nest/nest/ajax/UpdateTableRecord.fal000066400000000000000000000006421176363201700232720ustar00rootroot00000000000000function UpdateTableRecord( req ) id = int(req["id"]) data = Wopi.getAppData("table-app") number = arrayScan( data, { v => v["id"] == id} ) if number >= 0 record = data[number] record["name"] = req["name"] record["sname"] = req["sname"] record["age"] = req["age"] Wopi.setAppData(data, "table-app") return ["updated" => id] end return ["unknown" => id] end tests/frameworks/nest/nest/ajax/hello.fal000066400000000000000000000003171176363201700210430ustar00rootroot00000000000000function hello( req ) text = ["text" => "Hello world!", "p0" => "p0" in req ? req["p0"] : "" ] if "Cookie" in Request.headers text["Cookie"] = Request.headers["Cookie"] end return text end tests/frameworks/nest/nest/frame.ftd000066400000000000000000000006171176363201700201250ustar00rootroot00000000000000 Nest test site - Ajax

    Return Home



    Return Home

    tests/frameworks/nest/nest/hooks/000077500000000000000000000000001176363201700174535ustar00rootroot00000000000000tests/frameworks/nest/nest/hooks/TableCheckUser/000077500000000000000000000000001176363201700222775ustar00rootroot00000000000000tests/frameworks/nest/nest/hooks/TableCheckUser/config.fal000066400000000000000000000003671176363201700242360ustar00rootroot00000000000000import User from nest.hooks.TableCheckUser as User users = [ "user" => User( 1, "1234", Nest.AL.USER, "Simple User" ), "staff" => User( 2, "1234", Nest.AL.STAFF, "Staff User" ), "user" => User( 3, "1234", Nest.AL.ADMIN, "Admin User" ) ] tests/frameworks/nest/nest/hooks/check_user.fal000066400000000000000000000006461176363201700222600ustar00rootroot00000000000000 class check_user function invoke( userid, password, remember ) if password == "1234" switch userid case 'user': res = [1, Nest.AL.USER, "A User"] case 'staff': res = [2, Nest.AL.STAFF, "A Staff member"] case 'admin': res = [3, Nest.AL.ADMIN, "An admin"] end end if res: Nest.loggedIn( res[0], res[1], res[2], true ) return res end end tests/frameworks/nest/nest/hooks/handle_form.fal000066400000000000000000000021351176363201700224160ustar00rootroot00000000000000class handle_form function invoke( form, values ) res = "" for k, v in values["params"] res += k + " = " + htmlEscape(v.describe()) + "\n" end return [ [ "message" => "set", "id" => "formResult", "property" => "innerHTML", "value" => res ], form.msgInvoke( "setFields", [ "enter_text" => "Forcefully reset", "choice" => ["one","three"], "pick" => "beta", "selmulti" => ["two", "four"] ] ) ] /* [ "message" => "set", "id" => "formResult", "property" => "innerHTML", "value" => res ], [ "message" => "set", "id" => "tableform.enter_text", "property" => "value", "value" => "Forcefully reset" ], [ "message" => "set", "id" => "tableform.choice", "property" => "value", "value" => ["one","three"] ], [ "message" => "set", "id" => "tableform.pick", "property" => "value", "value" => "beta" ], [ "message" => "set", "id" => "tableform.selmulti", "property" => "value", "value" => ["two", "four"] ] ] */ end end tests/frameworks/nest/nest/mod/000077500000000000000000000000001176363201700171075ustar00rootroot00000000000000tests/frameworks/nest/nest/mod/MyPlugin.fal000066400000000000000000000005111176363201700213340ustar00rootroot00000000000000 import MenuItem from nest.widgets.MenuItem as MenuItem import Link from nest.widgets.Link as Link class MyPlugin init mi = MenuItem( "pluginItem", Link( "idl1", "http://www.falconpl.org", "Go to falcon" ), Nest.resLink("icon_choice1.png") ) Nest.registerMenuItem( "plugins", mi ) end end tests/frameworks/nest/nest/pages/000077500000000000000000000000001176363201700174275ustar00rootroot00000000000000tests/frameworks/nest/nest/pages/ajax-cookie.ftd000066400000000000000000000015331176363201700223220ustar00rootroot00000000000000

    Test for cookie persistency across Java.

    Checking for cookie persistency via ajax: .

    In this test, we check wheter the AJAX "hello" funciton, that we have in the site, is able to get the cookie we created in this script and show it (it should be "Test value").

    This is the code:

    nest.js.button( "Button", "Nest.ajax('hello', {}, getCookie)" )
    
    nest.js.script( "
          function getCookie( obj ) {
                alert('Cookie data via ajax was: ' + obj.Cookie );
          }" )
    

    tests/frameworks/nest/nest/pages/basic-ajax.ftd000066400000000000000000000013601176363201700221300ustar00rootroot00000000000000

    Test of basic AJAX function call and return.

    AJAX basic function test: .

    In this example, Nest generates an AJAX call to a "hello" AJAX function we have on the site. The Nest.ajax method instructs javascript to invoke a "onHello" callback with the results got via the "hello" call.

    This is the code:

    nest.js.button( "Button", "Nest.ajax('hello', {'p0':'A parameter'}, onHello)" )
    
    tests/frameworks/nest/nest/pages/hashmaker.ftd000066400000000000000000000006201176363201700220670ustar00rootroot00000000000000

    Custom AJAX enabled widget test

    This test just renders a "HashMaker" widget, which extends directly the base Nest.widget class, and implements multiple components. Transformations of the text into a hash is done via an automatic AJAX call.

    The invocation code is just:

    Nest.widget( "HashMaker" )("hm").render()
    

    tests/frameworks/nest/nest/pages/home.ftd000066400000000000000000000034201176363201700210550ustar00rootroot00000000000000

    Nest widgets subsystem test.

    Generic Nest/AJAX and JS tests.

    Widget specific tests

    tests/frameworks/nest/nest/pages/jsscript.ftd000066400000000000000000000007201176363201700217660ustar00rootroot00000000000000

    Simple JS script generator function.

    JS button utility function test: .


    Source code:

    nest.js.script( "
       function say_hi() {
             alert('saying hi!');
       }" )
    

    tests/frameworks/nest/nest/pages/wd-ajaxform.ftd000066400000000000000000000112221176363201700223430ustar00rootroot00000000000000

    Nest Widget test: AJAX enabled form

    " function( source, msg, value ) { this.innerHTML = value.length; }" ] th = Nest.widget( "TheNameIsAvail" )( "the_other_name" ) form.addChild( th ) form.addChild( Nest.widget( "RichPassword" )("my_password", "Enter password", "Repeat password") ) form.addChild( wid ) form.addChild( widtext ) form.setConfirmReset( "Sure you want to reset?" ) class MyCBSet from CheckBoxSet( "choice", ["one:First choice$", "two:Second choice$", "three:Third choice$"]) function setRenderValue( val ) self.CheckBoxSet.setRenderValue( val ) if self.value.len() < 2 self.fieldInfo = "Please, select at least 2 items!" end end end cbox = MyCBSet() cbox.label = "Pick one or more..." form.addChild( cbox ) rbox = Nest.widget( "RadioButtonSet" )( "pick", ["alpha:Alpha", "beta:Beta", "gamma:Gamma"]) rbox.label = "Hello" form.addChild( rbox ) selbox = Nest.widget( "Select" )( "selopt", [ ": -- Pick one plz --", "one:Option One", "two:Option Two", "three:Option Three"], "Pick one" ) mselbox = Nest.widget( "Select" )( "selmulti", ["one:Option One", "two:Option Two", "three:Option Three", "four:Option four" ], "Pick many", -1 ) form.addChild( selbox ) form.addChild( mselbox ) datesel = Nest.widget( "DateSelector" )( "datesel" ) form.addChild( datesel ) // bottom line... contSub = Nest.widget( "Container" )( "sub" ) contSub.addChild( Nest.widget("Button")("sb", "Send data"). makeSendFormAJAX( form, "confirm('Really want to send?')" ) ) contSub.addChild( Nest.widget("Reset")("rb", "Reset data") ) form.addChild( contSub ) ?>

    Form entry displayed here:

       ... when submitted, data will be shown here ... 
    

    This test is used to check for Nest TableForm widget and several form-related widget. Submitting the form causes the data to be re-loaded into the widgets, so the correct behavior is that of seeing the same data that was inserted before sending the form. Moreover, some data is forcefully changed or updated upon ajax request retreival.

    Soruce code:

       form = Nest.widget( "TableForm" )("tableform")
    
       wid = Nest.widget( "ActiveTextArea" )("enter_text", "Enter text", 3, 30)
       widtext = Nest.widget("Text")("counter","0")
       widtext.addStyle( "color:red; font-weight:bold" )
       widtext.jsListeners = [
          wid => " function( source, msg, value ) { this.innerHTML = value.length; }"
          ]
    
       th = Nest.widget( "TheNameIsAvail" )( "the_other_name" )
       form.addChild( th )
       form.addChild( Nest.widget( "RichPassword" )("my_password", "Enter password", "Repeat password") )
       form.addChild( wid )
       form.addChild( widtext )
       form.setConfirmReset( "Sure you want to reset?" )
    
       class MyCBSet from CheckBoxSet( "choice",
                   ["one:First choice$", "two:Second choice$", "three:Third choice$"])
    
          function setRenderValue( val )
             self.CheckBoxSet.setRenderValue( val )
             if self.value.len() < 2
                self.fieldInfo = "Please, select at least 2 items!"
             end
          end
       end
       cbox = MyCBSet()
    
       cbox.label = "Pick one or more..."
       form.addChild( cbox )
    
       rbox = Nest.widget( "RadioButtonSet" )( "pick",
                   ["alpha:Alpha", "beta:Beta", "gamma:Gamma"])
       rbox.label = "Hello"
       form.addChild( rbox )
    
       selbox = Nest.widget( "Select" )( "selopt",
                   [ ": -- Pick one plz --", "one:Option One", "two:Option Two", "three:Option Three"],
                   "Pick one" )
    
       mselbox = Nest.widget( "Select" )( "selmulti",
                   ["one:Option One", "two:Option Two",
                    "three:Option Three",
                    "four:Option four" ],
                    "Pick many", -1 )
       form.addChild( selbox )
       form.addChild( mselbox )
    
       datesel = Nest.widget( "DateSelector" )( "datesel" )
       form.addChild( datesel )
    
       // bottom line...
       contSub = Nest.widget( "Container" )( "sub" )
       contSub.addChild( Nest.widget("Button")("sb", "Send data").
             makeSendFormAJAX( form, "confirm('Really want to send?')" ) )
       contSub.addChild( Nest.widget("Reset")("rb", "Reset data") )
       form.addChild( contSub )
    

    tests/frameworks/nest/nest/pages/wd-ajaxpane.ftd000066400000000000000000000020331176363201700223230ustar00rootroot00000000000000

    Nest Widget test: AJAX pane

    tap.render() ?>

    The above pane can be used to load dynamic AJAX content by pressing one of the wollowing buttons:

    • (Note: this page doesn't exist)
    • (Note: this page doesn't exist)
    tests/frameworks/nest/nest/pages/wd-ajaxpart.ftd000066400000000000000000000016601176363201700223530ustar00rootroot00000000000000

    Nest Widget test: AJAX part

    tap1.render() ?>

    tap2.render() > "

    ", btn2.render(), "

    " ?>

    The above test shows two AJAX enable "parts", that is, expandible widgts that are shown inline in a page. The first area is auto-expandible (it can be directly clicked). The second area is expandible by clicking the button below it.

    The second part shows how the AJAX routine of the part can set a specific size for the opened widget.

    tests/frameworks/nest/nest/pages/wd-calendar.ftd000066400000000000000000000026571176363201700223210ustar00rootroot00000000000000

    Nest Widget test: Calendar

    A calendar with a Nest listener waiting for messages.

    Selected date:


    A popup calendar.

    Selected date:


    An ajax enabled calendar. The ajax routine returns some busy days at the beginning of the months. The selected date will be changed by the AJAX routine so that the day is always the 1st.

    Also, notice that this calendar starts from "monday".

    Selected date:

    tests/frameworks/nest/nest/pages/wd-infobox.ftd000066400000000000000000000025171176363201700222070ustar00rootroot00000000000000

    Nest Widget test: InfoBox

    This is an InfoBox!

    InfoBox is a relatively simple and self contained wiget, that can be used to create hovering boxes near an arbitrarily placed image on the page.

    They use the Nest FX module, which has support for javascript transitions and visual effects.

    InfoBox is actually a composite widget, witjh an icon widget being parent of the real box.

    The box Nest widget can be accessed at Falcon level via the infobox property, while at Javascript level it can be accesseed via Nest.i('parentid.infobox'), where 'parentid' is the id given to the InfoBox main widget.

    " ibox = Nest.widget( "InfoBox" )("ibox", "Basic Infos", text) ibox2 = Nest.widget( "InfoBox" )("ibox2", "Basic Infos -- at the end", text) ?>

    Click to get more info...

    space...

    space...

    space...

    space...

    space...

    space...

    space...

    space...

    space...

    space...

    space...

    space...

    space...

    space...

    space...

    space...

    space...

    space...

    space...

    Click to get more at the end of the page

    tests/frameworks/nest/nest/pages/wd-listform.ftd000066400000000000000000000025011176363201700223730ustar00rootroot00000000000000

    Nest Widget test: ListForm

    " function( source, msg, value ) { this.innerHTML = value.length; }" ] th = Nest.widget( "TheNameIsAvail" )( "the_other_name" ) form.addChild( th ) form.addChild( Nest.widget( "RichPassword" )("my_password", "Enter password", "Repeat password") ) form.addChild( wid ) form.addChild( widtext ) ?>

    This test shows how to create a form out of multiple widgets. Source is as follows:

       form = Nest.widget( "ListForm" )("testform", "#")
    
       wid = Nest.widget( "ActiveTextArea" )("enter_text", "Enter text", 3, 30)
       widtext = Nest.widget("Text")("counter","0")
       widtext.addStyle( "color:red; font-weight:bold" )
       widtext.jsListeners = [
          wid => " function( source, msg, value ) { this.innerHTML = value.length; }"
          ]
    
       th = Nest.widget( "TheNameIsAvail" )( "the_other_name" )
       form.addChild( th )
       form.addChild( Nest.widget( "RichPassword" )("my_password", "Enter password", "Repeat password") )
       form.addChild( wid )
       form.addChild( widtext )
    

    tests/frameworks/nest/nest/pages/wd-loginmask.ftd000066400000000000000000000037261176363201700225320ustar00rootroot00000000000000

    Nest Widget test: LoginMask

    The page is composed of a "LoginMask" and a simple "Text" that receives a message from the mask containing the data of the logged in users.

    This example uses a hook function on "check_user", which is dynamically loaded when the login mask needs to know if the user/password pair is valid. Alternatively, it is possible to sub-class the LoginMask class. In both cases, the server-side part is repsonsible to record the login status in the Nest session variables.

    The following mask accepts the users "user", "staff" and "admin", all with password "1234"

    "display:none"] ok.jsListeners = [ wid => " function( w, m, d ){ if ( d == null ) { this.style.display = 'none'; } else { this.style.display = ''; this.innerHTML = 'Logged in as ' + d.name + ' \"' + d.data + '\" (level ' + d.level + ')'; } }" ] > wid.render() ?>

    Login status:


    Source code:

       import Link from nest.widgets.Link as Link
       import Text from nest.widgets.Text as Text
    
       wid = Nest.widget( "LoginMask" )("mask" )
       wid.recover = Link( "recover", "#", "Forgot my password" )
    
       ok = Text( "onLoginDone", "" )
       ok.props = ["style" => "display:none"]
       ok.jsListeners = [ wid => "
             function( w, m, d ){
                if ( d == null )  { this.style.display = 'none'; }
                else {
                   this.style.display = '';
                   this.innerHTML = 'Logged in as ' + d.name +
                      ' \"' + d.data + '\" (level ' + d.level + ')';
                }
             }"
          ]
    
       > wid.render()
    

    tests/frameworks/nest/nest/pages/wd-menu.ftd000066400000000000000000000071171176363201700215100ustar00rootroot00000000000000

    Nest Widget test: Menu

    Example of horizontal menu:

    'submenu'] ) submenu3 = Menu( "sub_menu3" ) submenu3.addChildren( MenuItem( "itm10", "First sub sub choice", rl("icon_choice1.png") ), MenuItem( "itm11", "Second sub sub choice", rl("icon_choice2.png") ) ) submenu3.set( ["class"=>'submenu'] ) submenu2 = Menu( "sub_menu2" ) submenu2.addChildren( MenuItem( "itm7", "Third sub choice", rl("icon_choice7.png"), submenu3, "|->" ), MenuItem( "itm8", "Fourth sub choice", rl("icon_choice8.png") ) ) submenu2.set( ["class"=>'submenu'] ) menu = Menu( "mymenu", false, "|" ) menu.addChildren( MenuItem( "itm1", Link( "idl1", "#", "First link" ), rl("icon_choice1.png") ), MenuItem( "itm2", "Submenu", rl("icon_choice2.png"), submenu, "|->" ), MenuItem( "itm3", Link( "idl2", "#", "Second link" ), rl("icon_choice3.png"), submenu2, Image( "imgl", rl("icon_submenu.png") ) ), MenuItem( "itm4", Link( "idl3", "#", "Third link" ), rl("icon_choice4.png") ) ) > menu.render() ?>

    Example of vertical menu:

    'submenu'] ) submenu3 = Menu( "sub_menu3" ) submenu3.addChildren( MenuItem( "itm10", "First sub sub choice", rl("icon_choice1.png") ), MenuItem( "itm11", "Second sub sub choice", rl("icon_choice2.png") ) ) submenu3.set( ["class"=>'submenu'] ) submenu2 = Menu( "sub_menu2" ) submenu2.addChildren( MenuItem( "itm7", "Third sub choice", rl("icon_choice7.png"), submenu3, "|->" ), MenuItem( "itm8", "Fourth sub choice", rl("icon_choice8.png") ) ) submenu2.set( ["class"=>'submenu'] ) menu = Menu( "mymenu2", true ) menu.addChildren( MenuItem( "itm1", Link( "idl1", "#", "First link" ), rl("icon_choice1.png") ), MenuItem( "itm2", "Submenu", rl("icon_choice2.png"), submenu, "|->" ), MenuItem( "itm3", Link( "idl2", "#", "Second link" ), rl("icon_choice3.png"), submenu2, Image( "imgl", rl("icon_submenu.png") ) ), MenuItem( "itm4", Link( "idl3", "#", "Third link" ), rl("icon_choice4.png") ) ) > menu.render() ?>

    Example of a menu with plugin ability:

    'yes'] ), "this page with plugin" ) ?>

    If you click on , a new menu item will be dynamically added to the plugin menu:

    tests/frameworks/nest/nest/pages/wd-miniblog.ftd000066400000000000000000000034661176363201700223470ustar00rootroot00000000000000

    Small embedded blog test

    This site has just demonstrative purposes, it's by no mean complete. It's purpose is that to demonstrate how to feed data in a form directly through a DataProvider class. In this case, we're using a DBI data provider, which links a form with SQL tables provided by a DBI interface.

    connect_form.render() ?> "display:none"]) form.jsListeners += [ tadder => "function(){ this.clearFields(); this.style.display = ''; }", table => "function( sender, msg, params ){ if( msg == 'upd') Nest.widgetAJAX( 'BlogForm', this.id, msg, null, params ); }", deletor => "function( sender, msg, params ){ Nest.widgetAJAX( 'BlogForm', this.id, 'del', null, params ); }" ] form.mode.jsListeners += [ tadder => "function(){ this.value = 'add'; }" ] form.confirm.jsListeners += [ tadder => "function(){ this.innerHTML='Add record'; }" ] > form.render() ?>tests/frameworks/nest/nest/pages/wd-nameisavail.ftd000066400000000000000000000007601176363201700230320ustar00rootroot00000000000000

    Nest Widget test: NameIsAvail

    Names taken on the server are: ", ".merge( namewid.takenNames ) ?>

    Please, notice that the names must be at least characters long.

    Check for availability:

    This is a test of the NameIsAvail widget, which can be subclassed to specify a precise AJAX behavior. The site has a "TheNameIsAvail" subclass that emulates a small DB.

    tests/frameworks/nest/nest/pages/wd-pickshelf.ftd000066400000000000000000000072611176363201700225140ustar00rootroot00000000000000

    Nest Widget test: Pick Shelf

    A PickShelf is a graphic large menu where items (mostly PickItem instances) can be inserted and selected by the user.

    tests/frameworks/nest/nest/pages/wd-popup.ftd000066400000000000000000000055071176363201700217100ustar00rootroot00000000000000

    Nest Widget test: Popup Windows

    This is a test for some popup windows.

    Notice that if the text spawns in more space, the window is automatically made scrolable.

    More text...

    More text..

    More text..

    More text..

    Text complete.

    " ) wnd.container.addChild( text ) wnd.jsListeners[btn1] = "function(){ this.show(); }" > wnd.render() ?>

    Click to open an absolute window.


    wnd.render() ?>

    Click to open a window centered in the screen, and with a fade transition.


    wnd.render() ?>

    Click to open a window fixed on the scrren.


    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    This is a bit of text to allow scrolling...

    End of scroll text

    tests/frameworks/nest/nest/pages/wd-richpassword.ftd000066400000000000000000000004101176363201700232410ustar00rootroot00000000000000

    Nest Widget test: RichPassword

    Source code:

    Nest.widget( "RichPassword" )("my_password", "Enter password", "Repeat password").render()
    

    tests/frameworks/nest/nest/pages/wd-showcase.ftd000066400000000000000000000052161176363201700223560ustar00rootroot00000000000000

    Nest Widget test: Showcase

    The showcase widget is a rotating display.

    First show item

    This is a show item. You can put actually any text here.

    To demonstrate click ability, a click returns to main page

    " ).addStyle( "position: absolute; top:0; width:450px; display:inline-block;color:#FFFFFF" ) image0 = Image('image0', "/images/first_panel.jpg").addStyle( "position: absolute; top:0;" ) page0 = Widget('page0').set(["onclick"=>"window.location='"+Nest.pageLink('home')+"'"]) page0.addChildren( image0, text0) text1 = Text('text1', "

    Second Item

    This is a show item. You can put actually any text here.

    To demonstrate click ability, a click returns to main page

    " ).set(["class"=>"present_text"]).addStyle( "position: absolute; top:0; width:450px; display:inline-block;color:#FFFFFF" ) image1 = Image('image1', "/images/second_panel.jpg").addStyle( "position: absolute; top:0;" ) page1 = Widget('page1').set(["onclick"=>"window.location='"+Nest.pageLink('home')+"'"]) page1.addChildren(image1, text1) text2 = Text('text2', "

    Third item

    This is a show item. You can put actually any text here.

    To demonstrate click ability, a click returns to main page

    " ).set(["class"=>"present_text"]).addStyle( "position: absolute; top:0; width:450px; display:inline-block;color:#FFFFFF" ) image2 = Image('image2', "/images/third_panel.jpg").addStyle( "position: absolute; top:0;" ) page2 = Widget('page2').set(["onclick"=>"window.location='"+Nest.pageLink('home')+"'"]) page2.addChildren( image2, text2 ) ?>

    Example with mode 0: slide pushing the previous image.

    cs.render() ?>

    Example with mode 1: slide over the previous image.

    cs.render() ?>

    Example with mode 2: Cross fade images.

    cs.render() ?> tests/frameworks/nest/nest/pages/wd-table.ftd000066400000000000000000000115331176363201700216300ustar00rootroot00000000000000

    Nest Widget test: Table

    "display:none"] jsListeners = [ table => " function( source, msg, value ) { if(msg == 'del') { if( window.confirm('delete: ' + value.comment) ) { Nest.ajax( 'DeleteTableRecord', {id:value.key}, function(){Nest.i('tabtest').refresh()} ); } } }" ] end tdeletor = TableDeletor() //================================================================= // Updater // class TableUpdater from widget.Widget( "tabtest.updater" ) props = ['style'=>"display:none"] jsListeners = [ table => " function( source, msg, value ) { if(msg == 'upd') { this.fetchData(value.key); } }" ] jsMethods = [ "fetchData" => " function( id ){ Nest.i('tabtest.updater').style.display='none'; Nest.ajax( 'GetTableRecord', {\"id\":id}, function(v){Nest.i('tabtest.updater').refresh(v)} ); }", "newData" => " function(){ Nest.i('tabtest.updater.id').value = -1; Nest.i('tabtest.updater.name').value = ''; Nest.i('tabtest.updater.surname').value = ''; Nest.i('tabtest.updater.age').value = ''; Nest.i('tabtest.updater').style.display=''; }", "refresh" => " function( data ) { Nest.i('tabtest.updater.id').value = data.id; Nest.i('tabtest.updater.name').value = data.name; Nest.i('tabtest.updater.surname').value = data.sname; Nest.i('tabtest.updater.age').value = data.age; Nest.i('tabtest.updater').style.display = ''; }", "update_done" => " function(){ var dict = { id: Nest.i('tabtest.updater.id').value, name: Nest.i('tabtest.updater.name').value, sname: Nest.i('tabtest.updater.surname').value, age: Nest.i('tabtest.updater.age').value }; Nest.i('tabtest.updater').style.display='none'; var func = 'UpdateTableRecord'; if( dict.id < 0 ) { func = 'AddTableRecord'; } Nest.ajax( func, dict, function(){Nest.i('tabtest').refresh()} ); }" ] init self.addChild( Hidden( 'id' ) ) self.addChild( InputText( 'name', "Name:" ) ) self.addChild( InputText( 'surname', "Surname:" ) ) self.addChild( InputText( 'age', "Age:" ) ) updbtn = Button( 'update', "Update" ) updbtn.props = [ "onclick" => "Nest.i('tabtest.updater').update_done()" ] fgtbtn = Button( 'forget', "Forget it" ) fgtbtn.props = [ "onclick" => "Nest.i('tabtest.updater').style.display='none'" ] self.addChild( updbtn ) self.addChild( fgtbtn ) end end tupd = TableUpdater() //================================================================= // Multiple deletor // tmdeletor = Button( 'tabtest.multideletor', "Delete selected" ) tmdeletor.props =[ "onclick" => "Nest.i('tabtest').withSelected('sel', function( vals ){Nest.i('tabtest.multideletor').remove(vals);})" ] tmdeletor.jsMethods = [ "remove" => " function(vals) { if (vals.length == 0) { alert( 'No selected item!' ); return; } var text = ''; for( var item in vals ) { if ( text != '' ) text = text + ', '; text = text + vals[item].comment; } if( window.confirm( 'Delete '+ text +'?' ) ) { Nest.ajax('TableMultiDelete', vals, function( vals ){ alert(JSON.stringify(vals));Nest.i('tabtest').refresh();}); } }" ] //================================================================= // Adder // tadder = Button( 'tabtest.adder', "Add" ) tadder.props =[ "onclick" => "Nest.i('tabtest.updater').newData()" ] ?>
    tests/frameworks/nest/nest/pages/wd-tableform.ftd000066400000000000000000000122571176363201700225200ustar00rootroot00000000000000

    Nest Widget test: TableForm

    " function( source, msg, value ) { this.innerHTML = value.length; }" ] th = Nest.widget( "TheNameIsAvail" )( "the_other_name" ) form.addChild( th ) form.addChild( Nest.widget("InputText")('genin', "Generic input", "Write something" ) ) form.addChild( Nest.widget( "RichPassword" )("my_password", "Enter password", "Repeat password") ) form.addChild( wid ) form.addChild( widtext ) form.setConfirmReset( "Sure you want to reset?" ) form.setConfirmSubmit( "Sure you want to submit?" ) class MyCBSet from CheckBoxSet( "choice", ["one:First choice$", "two:Second choice$", "three:Third choice$"]) function setRenderValue( val ) self.CheckBoxSet.setRenderValue( val ) if self.value.len() < 2 self.fieldInfo = "Please, select at least 2 items!" end end end cbox = MyCBSet() cbox.label = "Pick one or more..." form.addChild( cbox ) rbox = Nest.widget( "RadioButtonSet" )( "pick", ["one:Alpha", "two:Beta", "three:Gamma"]) rbox.label = "Hello" form.addChild( rbox ) selbox = Nest.widget( "Select" )( "selopt", [ ": -- Pick one plz --", "one:Option One", "two:Option Two", "three:Option Three"], "Pick one" ) mselbox = Nest.widget( "Select" )( "selmulti", ["one:Option One", "two:Option Two", "three:Option Three", "four:Option four" ], "Pick many", -1 ) form.addChild( selbox ) form.addChild( mselbox ) datesel = Nest.widget( "DateSelector" )( "datesel" ) form.addChild( datesel ) // bottom line... contSub = Nest.widget( "Container" )( "sub" ) contSub.addChild( Nest.widget("Submit")("sb", "Send data") ) contSub.addChild( Nest.widget("Reset")("rb", "Reset data") ) form.addChild( contSub ) if "tableform.hasForm" in Request.posts > "Receiving form data from a post" form.routeValues( Request.posts ) if not rbox.getValue() rbox.fieldInfo= "Please, set one!" end datesel.fieldInfo = "You entered " + datesel.getValue() end ?>

    This test is used to check for Nest TableForm widget and several form-related widget. Submitting the form causes the data to be re-loaded into the widgets, so the correct behavior is that of seeing the same data that was inserted before sending the form.

    Soruce code:

       form = Nest.widget( "TableForm" )("tableform", "#")
       form.addHidden( "hasForm", "true" )
    
       wid = Nest.widget( "ActiveTextArea" )("enter_text", "Enter text", 3, 30)
       widtext = Nest.widget("Text")("counter","0")
       widtext.addStyle( "color:red; font-weight:bold" )
       widtext.jsListeners = [
          wid => " function( source, msg, value ) { this.innerHTML = value.length; }"
          ]
    
       th = Nest.widget( "TheNameIsAvail" )( "the_other_name" )
       form.addChild( th )
       form.addChild( Nest.widget( "RichPassword" )("my_password", "Enter password", "Repeat password") )
       form.addChild( wid )
       form.addChild( widtext )
       form.setConfirmReset( "Sure you want to reset?" )
       form.setConfirmSubmit( "Sure you want to submit?" )
    
       class MyCBSet from CheckBoxSet( "choice",
                   ["one:First choice$", "two:Second choice$", "three:Third choice$"])
    
          function setRenderValue( val )
             self.CheckBoxSet.setRenderValue( val )
             if self.value.len() < 2
                self.fieldInfo = "<b>Please, select at least 2 items!</b>"
             end
          end
       end
       cbox = MyCBSet()
    
       cbox.label = "Pick one or more..."
       form.addChild( cbox )
    
       rbox = Nest.widget( "RadioButtonSet" )( "pick",
                   ["one:Alpha", "two:Beta", "three:Gamma"])
       rbox.label = "Hello"
       form.addChild( rbox )
    
       selbox = Nest.widget( "Select" )( "selopt",
                   [ ": -- Pick one plz --", "one:Option One", "two:Option Two", "three:Option Three"],
                   "Pick one" )
    
       mselbox = Nest.widget( "Select" )( "selmulti",
                   ["one:Option One", "two:Option Two",
                    "three:Option Three",
                    "four:Option four" ],
                    "Pick many", -1 )
       form.addChild( selbox )
       form.addChild( mselbox )
    
       datesel = Nest.widget( "DateSelector" )( "datesel" )
       form.addChild( datesel )
    
       // bottom line...
       contSub = Nest.widget( "Container" )( "sub" )
       contSub.addChild( Nest.widget("Submit")("sb", "Send data") )
       contSub.addChild( Nest.widget("Reset")("rb", "Reset data") )
       form.addChild( contSub )
    
       if "tableform.hasForm" in Request.posts
          > "Receiving form data from a post"
          form.routeValues( Request.posts )
          if not rbox.getValue()
             rbox.fieldInfo= "<b>Please, set one!</b>"
          end
          datesel.fieldInfo = "You entered " + datesel.getValue()
       end
    

    tests/frameworks/nest/nest/pages/widmsg.ftd000066400000000000000000000021531176363201700214210ustar00rootroot00000000000000

    Basic widgets with local page messaging

    "function( source, msg, value ) { this.innerHTML = value.length; }" ] ?>

    Lenght of the text:

    In this test, we use a custom ActiveTextArea widget on the site, which just sends a message to the "listeners" at each change. Then, we create a local widget out of the standard "Text" widget. To have this text widget to listen for messages generated by the active text area, it is just necessary to add it to the dictionary of jsListeners:

     wid = Nest.widget( "ActiveTextArea" )("enter_text", "Enter text", 3, 30)
    
       widtext = Nest.widget("Text")("counter","0")
       widtext.addStyle( "color:red; font-weight:bold" )
       widtext.jsListeners = [
          wid => "function( source, msg, value ) { this.innerHTML = value.length; }"
          ]
    

    tests/frameworks/nest/nest/res/000077500000000000000000000000001176363201700171215ustar00rootroot00000000000000tests/frameworks/nest/nest/res/icon_choice1.png000066400000000000000000000011641176363201700221540ustar00rootroot00000000000000PNG  IHDR sBIT|d pHYsxx\2tEXtSoftwarewww.inkscape.org<IDAT8Փka?wy/glSclU!(DT WG!\uuRKЩRmEh\z:zgZ_x|r1r<+NR.7v\vg^26xE#agxs#eGmLrkΣ9kVXqT]yP~$KP0 6đl3PG)Ngcܺ<ξLz˰H7_`Oz_ E=N<>SRsƯPixfOT<_> m3O=lR^/pCf$_ae7o_`j$f6uڪ&N\2UH[Z7Fm\J;$ZYiPk '\P65^ j8};Z_o_IAՃGDnv?MF3q GJqqb]{ʥTע1r<K!b c ]IENDB`tests/frameworks/nest/nest/res/icon_choice2.png000066400000000000000000000015361176363201700221600ustar00rootroot00000000000000PNG  IHDR sBIT|d pHYsbb<tEXtSoftwarewww.inkscape.org<IDAT8[HQ:f[&]|h)#,*"߂.FAPAAeP= )Q*J ,c꺻Μ]g``w$Eς x&"gz*IW1!T=SҰagd36xN-ekn[YdȲH}8'1$)u7L}Cߞ3a3+C^)ӥF2҃{?`]}5|xlk6uX| ЎrKJJa͕Vd[0wKs,R/0;h@s;ܼȥ-k.E e>mni<Q|xŇ?5!"Gzg2}lMQb$<}.RKk gy%F4D`Zs O^o $hNcrUUR٪zcFW8OנN]]7W+U+y+y0M~pNqZDh+s@ܴ5*]1'3yIʊüt+ mfHARY+NKh7>_0z %w mpB֦;h]_L;gME)[BG`q/LVWTvG uCY0XEM0߄=q@ *,+tPj3_U zkFГ>yq_21n|E|*t4*AYVq_O9TS3(cl"ߞ&pX䃓FHW62{yxonޒf^43x6gZ6D:dD{KiK\^<'//S3{}>".mA [i1&('ΦԛgYH?._Z.\۶ ӴddtJ:HtL8$ 1oe¥%~EB =XEG8zfwͥAg'iMSX^IENDB`tests/frameworks/nest/nest/res/icon_choice4.png000066400000000000000000000013571176363201700221630ustar00rootroot00000000000000PNG  IHDR sBIT|d pHYsbb<tEXtSoftwarewww.inkscape.org<lIDAT8KHTas/R+ѩ$ FD- MDth."jM"56"*q$93sƌ'ufo>|G.YTG6)Dv{\4Ʉ!3KB٢<%eJfCtJ; *[cDakP`(WؔȺČeL|' T_vr~Yz>%4A@R p'7/ mkM5b ZNdzxt<Bzʻ= lV`PŒ!L`!ٗHm\΋ 2ed1ǐ߬㟆t>;NfNw#\ \XP_r #-Bo1ќGYIãB/A$ <Ԋ ۜxIWcS"%! ?z%/_hFƼkyHZj ɸwI=L (2TW̯ ly0UL4w 1&0;4Gz\2i3a|~i58СQI4, ݺ(di`Jtg,^@im5J;hIENDB`tests/frameworks/nest/nest/res/icon_choice5.png000066400000000000000000000015751176363201700221660ustar00rootroot00000000000000PNG  IHDR sBIT|d pHYsbb<tEXtSoftwarewww.inkscape.org<IDAT8_hSg'9I J:H必wʫ[5C|}i͉i"2-*Fwl榁݄xF8 nGHupMӰAm$z4`_#7p̢x=g>xO?p6vXq F{{5`6`gT̋%<4AiX**eW|mw~:Km\s(?k%jewŁ+QpC euAR{_UAɇ#G(,͓~ŻĉKurQ# -\8N*/ugU o;}Qhqcڵ,I}#`. 8Zp] Z@kˢ@:fbLvT@zܘbWxSN )_ݖK  N5˹_Q.YeXiq&c_-4W#;mD[܆1Wd϶[@Dor,ɵQ9pgǐNd>#\@UY'uzh19G YU1,l:Io ++IENDB`tests/frameworks/nest/nest/res/icon_choice6.png000066400000000000000000000017101176363201700221560ustar00rootroot00000000000000PNG  IHDR sBIT|d pHYsbb<tEXtSoftwarewww.inkscape.org<EIDAT8]h[e'=Gk[;]بluݤn:;T v1` Xa"Q/kcmmRv-MLɒs^/’ދyx%:REeZNHtۑ %Ʀ4=d5 ++sC dW%ػ>GKC525=ߢ\_IGUpSWUﻗI:m6=s71+U6'_]{ )`/nkV?p4ni\flj%>;ɮ dj:`Ӣ%ax*`woCiMJsx0 )/fDs\R2DWN8Z>fTJb>`F@*3V:N2|6hbi݊ i_]R6RIENDB`tests/frameworks/nest/nest/res/icon_choice7.png000066400000000000000000000013101176363201700221530ustar00rootroot00000000000000PNG  IHDR sBIT|d pHYsbb<tEXtSoftwarewww.inkscape.org<EIDAT8OHq?w[:j4`x)t(s"bvx輠KJ20$XRLE.q:km=}~ϿWbJa|Z?$mI$ MxޖxtTu0?yF~nezy\8Y4UwF6{d,Q@OUO+fn)O^ v{?ü kҰ*#WMu} YV\s픕NR)1quP0iw*&sOva249zW1fƾB#CM6}4Y1՚F0WWn"+kW+]O8O̓v\%-,q˟'X'Ez\tS ]mky볓Bo*K0"@Rx"ƙM&ʏh"W-,Y`s4G\4&<ӹNN_ɪ8(WDqhj[}M21J3/^wfXr=]loÿVmAqbS 2'Ohwf)e{WLOĆֈ;,a;-~>.]0>OVcbtu{eYTNhILNyt=!cR9Cw (e6I @w"z:L:Mv \$UVޞ*I&S$}NdFsQZDh(ߙu=?1m1&MP62 " bݒѳk=Ec C2h44M˚?U"[v"HB5`L;Fԛ"=6*g$B2\Ge|a^/oV?RY]MpoY>nX++1V<{8/@KIENDB`tests/frameworks/nest/nest/res/icon_submenu.png000066400000000000000000000013631176363201700223200ustar00rootroot00000000000000PNG  IHDR sBIT|d pHYsbb<tEXtSoftwarewww.inkscape.org<pIDAT8]Ha}tk@eQҌfAPY!]D Aw]] EueAEiRܜs:}t19y~x@Y$%H%=/ ȑ@UXJl=e|+cO̦t1vh 8_1ɒfS9fS9snw泋!!fcY7w< :|ڮE\%!Ncr niK% K8}AH*(pRf\pbwicFW/>1"\rfH \p{ extra["year"], "month" => extra["month"], "busyDays" => [1, 3, 5] ] ) ] elif msg == 'selectDay' return [ self.msgInvoke( 'AJAXselectDay', [ "year" => extra["year"], "month" => extra["month"], "day" => 1 ] ) ] end return [=>] end end tests/frameworks/nest/nest/widgets/BlogForm.fal000066400000000000000000000053211176363201700221720ustar00rootroot00000000000000import from dbi in dbi import DBIDataProvider from nest.dbidp as DBIDataProvider import NestError from nest.error as NestError import Button from nest.widgets.Button as Button import TableForm from nest.widgets.TableForm as TableForm import ActiveTextArea from nest.widgets.ActiveTextArea as ActiveTextArea import InputText from nest.widgets.InputText as InputText import Hidden from nest.widgets.Hidden as Hidden class BlogForm( id ) from TableForm( id ) mode = Hidden('mode') idblog = Hidden('id') title = InputText('title', 'Title').set( ["size"=>60] ) ctx = ActiveTextArea('content', 'Content', 5, 60 ) tags = InputText('tags', 'Tags' ) confirm = Button('confirm', 'Update' ) init self.idblog.datafield = 'idblog' self.title.datafield = 'Title' self.ctx.datafield = 'Content' self.tags.datafield = 'Tags' self.confirm.makeSendFormAJAX( self ) self.addChildren( self.mode, self.idblog, self.title, self.ctx, self.tags, self.confirm ) end function AJAX( params ) //connect to the database dbh = dbi.connect( Nest.sessionData["miniblog.connstr"] ) dp = DBIDataProvider( dbh, "blogtable", "idblog" ) data = params["params"] if params["msg"] == 'upd' return self.startUpdate( dp, data["key"] ) elif params["msg"] == 'del' return self.performDelete( dp, data["key"] ) end idblog = data[self.id + '.id'] // save the data in ourselves self.routeValues( data ) // apply if data[self.id + ".mode"] == 'upd' dp.openSet( idblog ) elif data[self.id + ".mode"] == 'add' self.routeValues( data ) self.idblog.datafield = nil // let the database to create an id dp.openNew() else raise NestError( NestError.other, "Mode not valid", data[self.id + ".mode"] ) end self.setData( dp ) dp.apply() return [ ["message"=>"invoke", "id"=>"blogtable", "method"=>"refresh", "params"=>""], self.msgSetStyle( "display", "none" ) ] end function startUpdate( dp, key ) dp.openGet( key, self ) self.setData( dp ) dp.apply() self.getData(dp) self.mode.setValue( "upd" ) return [ self.msgSetStyle( "display", "" ), self.confirm.msgSetProperty( "innerHTML", "Update" ), self.msgSetAllFields() ] end function performDelete( dp, key ) dp.openDelete( key ) dp.apply() return [ ["message"=>"invoke", "id"=>"blogtable", "method"=>"refresh", "params"=>""] ] end end tests/frameworks/nest/nest/widgets/BlogTable.fal000066400000000000000000000042231176363201700223160ustar00rootroot00000000000000import Table from nest.widgets.Table in widgets import from nest.widgets.action in action import from dbi in dbi /* Specialized table for Nest-widgets test site */ class BlogTable( id, itemsPerPage, itemCount, firstItem, orderColumn ) from \ widgets.Table( id, itemsPerPage, itemCount, firstItem, orderColumn ) orderBy = "" function makeColumns() //self.addCheckboxColumn( "sel", "Select", ["idblog"], ["Title"] ) self.columsFromHeadings( ["idblog|ID", "Title|Title", "Date|Date", "Content|Content", "Tags|Tags" ] ) self.addActionColumn( "Actions", ["idblog"], ["Title"], .[ action.ActionButton("Update", self, "upd" ) action.ActionLink("Delete", self, "del" ) ]) end function setDataView() itemsPerPage = self.itemsPerPage firstItem = self.firstItem // todo -- filter flt = self.filter // filter table if flt // todo -- filter end // order the table. if self.orderColumn != 0 col = self.orderColumn if col < 0 self.orderBy = "order by " + self.headers[-col-1].fieldName + " desc" else self.orderBy = "order by " + self.headers[col-1].fieldName end else self.orderBy = "" end if "miniblog.connstr" in Nest.sessionData self.makeData() end end function makeData( ) dbh = dbi.connect( Nest.sessionData["miniblog.connstr"] ) // get the table size rset = dbh.query( "select count(*) from blogtable" ) self.itemCount = int(rset.fetch([])[0]) rset.close() // get the table portion in this size. rset = dbh.query( dbh.lselect( " * from blogtable " + self.orderBy, self.firstItem, self.itemsPerPage ) ) sdata = [=>] self.data = [] while rset.fetch( sdata ) content = sdata["Content"] if content.len() > 40: content = content[0:40] self.data += [ "idblog"=>sdata["idblog"], "Title"=>sdata["Title"], "Date" => sdata["Date"], "Content"=>content, "Tags"=>sdata["Tags"] ] end rset.close() end end tests/frameworks/nest/nest/widgets/ConnectForm.fal000066400000000000000000000036131176363201700227020ustar00rootroot00000000000000 import from dbi in dbi import TableForm from nest.widgets.TableForm as TableForm import InputText from nest.widgets.InputText as InputText import Button from nest.widgets.Button as Button class ConnectForm( id ) from TableForm( id ) conn = InputText( "conn" ) btn = Button( "send", "Connect" ) init if "miniblog.connstr" in Nest.sessionData connect = Nest.sessionData["miniblog.connstr"] else connect = vmSystemType() == "WIN" ? \ "sqlite3:db=C:\\Temp\\test-blog.db" : \ "sqlite3:db=/tmp/test-blog.db" end self.conn.setValue( connect ) self.conn.set( ["size"=>60] ) self.btn.makeSendFormAJAX(self) self.addChild( self.conn ) self.addChild( self.btn ) end function AJAX( params ) data = params["params"] self.routeValues( data ) connstr = self.conn.getValue() dbh = dbi.connect( connstr + ";create=cond" ) // if the connection was succesful, save it Nest.sessionData["miniblog.connstr"] = connstr // eventually, setup the database try dbr = dbh.query("select count(*) from blogtable") dbr.close() catch in e // ok, we must create it dbh.query(" create table blogtable( idblog INTEGER PRIMARY key AUTOINCREMENT, Title TEXT NOT NULL, Content TEXT NOT NULL, Tags TEXT, Date String(20) NOT NULL DEFAULT CURRENT_TIMESTAMP ); ") end dbh.close() // hide us and show the table entry msg1 = self.msgSetStyle( "display", "none" ) msg2 = ["message" => "set_style", "id" => 'datatable-div', "property" => 'display', "value" => "" ] msg3 = ["message" => "invoke", "id" => 'blogtable', "method" => 'refresh', "param" => "" ] return [msg1, msg2, msg3] end end tests/frameworks/nest/nest/widgets/HashMaker.fal000066400000000000000000000041741176363201700223330ustar00rootroot00000000000000import from nest.widgets.widget in wid import from hash in hash class HashInput from wid.Widget( "area" ) tag = "textarea" isSelfClosing = false props = ["rows"=>5, "cols"=>20, "name"=>"text" ] init // TODO: Remove this when we have automatic parentship self.addClassInParentship( HashInput ) end end class HashButton from wid.Widget( "button" ) tag = "input" props = ["type"=>"button", "value"=>"Calc SHA", "onclick" => "this.excite()" ] jsMethods = ["excite" => .[self.makeAjaxMsg "excite" .[wid.JSP("/area","../area/value")]] ] // a custom message handler ajaxMessages = [ 'buttonChange' => "function(obj) { this.value = obj.value; }" ] init // TODO: Remove this when we have automatic parentship self.addClassInParentship( HashButton ) end end class HashText from wid.Widget( "sha" ) init // TODO: Remove this when we have automatic parentship self.addClassInParentship( HashText ) end end class HashMaker( id ) from wid.Widget( id ) ta = HashInput() tb = HashButton() outputText = HashText() isAJAXHost = true init self.addChild( self.ta ) self.addChild( self.tb ) self.addChild( self.outputText ) // TODO: Remove this when we have automatic parentship self.addClassInParentship( HashMaker ) end function AJAX( params ) value = params["params"]["area"] sha = hash.sha256( value ) return [ self.outputText.msgSetProperty( "innerHTML", sha ), self.ta.msgSetProperty("value", value + "\n--Checked--" ), // and a custom message wid.Widget.msgGeneric( "buttonChange", ["value" => "click again..."] ) ] end //# Renders the contents of this widget. function renderContent() rend = '' rend +='\n" rend +='\n" rend +='\n" rend += "
    Enter text here:' + self.ta.render() + "
    Click when done:' + self.tb.render() + "
    See SHA hash:' + self.outputText.render() + "
    \n" return rend end end tests/frameworks/nest/nest/widgets/NameTable.fal000066400000000000000000000052771176363201700223250ustar00rootroot00000000000000import Table from nest.widgets.Table in widgets import from nest.widgets.action in action /* A function to create some random table data, which stays persistent in the server */ function makeTableData() names = [ "Ada", "Charles", "John", "Edda", "Mary", "Martin", "Steve", "Steven", "Dennis", "Dart", "Luke", "Daisy", "Anthony", "Adam", "Alan", "William", "Bill"] snames = .[ "Smith" "Brown" "Lee" "York" "Gates" "Howlings" "Turing" "Lovelace" "Babbage" "Duke" "Martin" "Summers" "Gates" "Wall" "Jobs" "Saders" "Wellby"] data = [] for i = 1 to 100 data += [ 'id' => i, 'name'=> randomPick( names ), 'sname' => randomPick( snames ), 'age' => random( 20, 70 ) ] end while not Wopi.setAppData( data, "table-app" ) end return data end /* Specialized table for Nest-widgets test site */ class NameTable( id, itemsPerPage, itemCount, firstItem, orderColumn ) from \ widgets.Table( id, itemsPerPage, itemCount, firstItem, orderColumn ) function makeColumns() self.addCheckboxColumn( "sel", "Select", ["id"], ["name","sname"] ) self.columsFromHeadings( ["id|ID", "name|Name", "sname|Surname", "age|Age"] ) self.addActionColumn( "Actions", ["id"], ["name","sname"], .[ action.ActionButton("Update", self, "upd" ) action.ActionLink("Delete", self, "del" ) ]) end function setDataView() itemsPerPage = self.itemsPerPage firstItem = self.firstItem flt = self.filter data = Wopi.getAppData( "table-app" ) if not data data = makeTableData() end // filter table if flt if " " in flt surname, name = map( {v=>strLower(v)}, flt.split( " ", 2 )) else surname = strLower(flt) end data = filter( { val => if not strLower(val['sname']).startsWith(surname): return false if name and not strLower(val['name']).startsWith( name ): return false return true }, data) end // order the table. if self.orderColumn != 0 orderCol = self.headers[ abs(self.orderColumn )-1 ] fieldName = orderCol.fieldName funcSort = self.orderColumn > 0 ? \ { v1, v2 => v1[fieldName] > v2[fieldName] } : \ { v1, v2 => v1[fieldName] < v2[fieldName] } arraySort( data, funcSort ) end self.firstItem = firstItem self.itemCount = data.len() lastItem = self.sanitizeItemRange() if self.firstItem == lastItem self.data = [] else self.data = data[self.firstItem : lastItem] end end end tests/frameworks/nest/nest/widgets/TestAjaxPane.fal000066400000000000000000000023731176363201700230160ustar00rootroot00000000000000import AjaxPane from nest.widgets.AjaxPane as AjaxPane class TestAjaxPane(id) from AjaxPane(id, "

    Simulating some wait...

    ") isAJAXHost = true paneData = [ 'first' => "

    This is the first page

    This is the AJAX content of the first page.

    ", 'second' => {=> sleep(1) if 'TestAjaxPane' notin Nest.sessionData data = 1 Nest.sessionData['TestAjaxPane'] = 1 else data = Nest.sessionData['TestAjaxPane'] Nest.sessionData['TestAjaxPane'] = data + 1 end extra = "

    " for n = 1 to data extra+= @"Extra text to activate scrolling... $n
    " end extra += "

    " return @"

    This is the second page

    This page has been generated dynamically $(data) times.

    " + extra } ] function renderContent() return "

    Still nothing here...

    " end function onUnknownPage( page ) if page and page[0] == 't' return @"

    TextAjaxPane (overriding AjaxPane): page \"$(page)\" not found.

    " else return AjaxPane.onUnknownPage( page ) end end end tests/frameworks/nest/nest/widgets/TestAjaxPart1.fal000066400000000000000000000011651176363201700231200ustar00rootroot00000000000000import AjaxPart from nest.widgets.AjaxPart as AjaxPart import Button from nest.widgets.Button as Button import Text from nest.widgets.Text as Text class TestAjaxPart1(id) from AjaxPart(id, Text('closed', "Try to open me..."), nil, "

    Simulating some wait...

    ", true) isAJAXHost = true init btn = Button('_.btn', "Close me...", nil, @" Nest.i('$(id)').close()" ) self.paneData['open'] = {=> sleep(1) return "

    This is the first opened data.

    " + btn.render() + "

    " } end end tests/frameworks/nest/nest/widgets/TestAjaxPart2.fal000066400000000000000000000017331176363201700231220ustar00rootroot00000000000000import AjaxPart from nest.widgets.AjaxPart as AjaxPart import Button from nest.widgets.Button as Button import Text from nest.widgets.Text as Text class TestAjaxPart2(id) from AjaxPart(id, Text('closed', "You got to click the button below to open this part..."), nil, "

    Simulating some wait...

    " ) isAJAXHost = true init btn = Button('_.btn', "Close me...", nil, @" Nest.i('$(id)').close()" ) self.paneData['open'] = {=> sleep(1) return [ 350, 170, "

    This is the second opened data.

    This will also resize the frame...

    A bit of space to show this...

    A bit of space to show this...

    A bit of space to show this...

    A bit of space to show this...

    A bit of space to show this...

    " + btn.render() + "

    "] } end end tests/frameworks/nest/nest/widgets/TheNameIsAvail.fal000066400000000000000000000005741176363201700232620ustar00rootroot00000000000000 import from nest.widgets.NameWithAvail in na class TheNameIsAvail(id) from na.NameWithAvail( id?id:"the_name", "Enter a name", 'Type a name' ) minSize = 4 takenNames = [ "jack", "john", "pierre", "luca", "greta", "gian", "stan" ] function checkAvail( name ) return name notin self.takenNames end endtests/frameworks/nest/site/000077500000000000000000000000001176363201700163235ustar00rootroot00000000000000tests/frameworks/nest/site/images/000077500000000000000000000000001176363201700175705ustar00rootroot00000000000000tests/frameworks/nest/site/images/first_panel.jpg000066400000000000000000002006641176363201700226100ustar00rootroot00000000000000JFIFExifII* z(1 2iCanonCanon EOS 10DGIMP 2.6.82012:04:25 16:02:55"'d0221    0100, 2005:07:27 15:33:282005:07:27 15:33:28p@BJR(ZeHHJFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?졆$sjםPӦ]:u qֲI94򛍧І\at5pM@YD&%@oν9fzbMsO⧸E,$NKeD}EopU^?*59dhhQ wp ʖ¶ 8{[kspnpAW=9z%#UKC<C/-j핂J&R=sP6hda*\8t["%R6q$OU`cu[e# <Me]<l}+;sJ8/ԜsT %jz=9(-T-'$s*mb:$ ։م芦Db/8Q9C1XY[.31 [e+6r㚇XK:V>ݴG C6}kɭJrq= ,웃sZ_5+LYcy3)]_Z0st8̬J9IJ%e 8OW*XEյ3E (!+{㯵pZڞ-Գ29O0y+n# wug=nzJ-<<2 Lu=?`j:%Aa` Mt9,aY Π?,K4!X 1REw#$31Q[4l$}qȨkG]ܤN?iwVVS;sGy0'J+.P]S5b6Erddn>MV0rzTWNN;S`Y.(c-چՅV-2ES0\<VilC3 .OAtaxPpA }jt{f>w@[nZ`K3(s֨`Ѐʹ_z8i,03E_DfaݞP1si*29GGm9F!ujYı1%=3N" r<8Ɲwх"3J!a@Te$G!T qb`;7g뺼mbBw?+J8zܶu7~(,"'EsZI%x;ɏp3Ӯ++M𮞃uVʨ5!FP`+Kw~l/bx$ ykyK|?!ҸuϨJa{+2C~{ Gѧ ?<Սfc 15ȶz=v׹;)۩c$$ 'dӀ`q};מ#osta3W?ʦKBzo1p:}j;Qx9 *YLJPhWkinJې)GӠ8ư,.8+ԚӋRsm2GDmP{{W=:~w[[Y6a!̫vj1= [#t> swNOSwA=*G>jc\چ\g^CB ?דHt۸"IR-҈iMo 1OEU+ԇ5t\dκk)#D2dv()&+vǛEƌQy>y5K;$q/EUepTu2EVYܪ.p0XⲖێs. p͜իb}nn}GClS%7:cidڍЉ8oJӻ#i|쏗jx֦N72XK` }*qq[HT%MoYJ89?RIJ`P&9#Ij4Og +lo]*@B mẆ1Vo$kb?&K2}+1r}I48!²&Yc]~\;p¶h%y|TrxϭrWbK # qE6]bK(ĸP*ZM2nrx;O| -]}D9ث1-ВŲGOkRw--L/csE+RNC#=;vP,J$%#˳pN94BtI<篭p:cӊ v8&ջ8w`1{4ש"8ֶ;Yd觚#Uer۶h1JLb36RYlL .t6\`乑Rقܜ ̞[^;i?z A u"b^'Vy&f SmZYb)瞴QQ[Kajlm9aԌdq)" +&lO#mCiee߫˂$rH?qU-Lq,D ' 9>̒x%/`g# =po7;)0$|zĺ N~".!*)TOF F4?)`cU\3_XӄRƏ(+m5tY,ƅGB%nN"_ cy9ufyǮ+XUaX?8{[zfjѶ8k1p8`D#U9$q&Y,i7nbc=XDG@{urFbz-hh \7b -F==k2XlxǷz{I9=*Y b[ SAri 3ȦD<ơKx;5% T[]Hm򶝾tTvWy>rrՁФ_3QիzA!2XTh1ZFC#H0To1>a]>f-˕ЖPqJc`cQNWZw5(JWQf?=kE67d0! dctt@{; s<9&[Ue]ā}5oTgH^ ٛp ]N+Hp9ԟz\YXZeiQ{W8,5$dbI99$L!ʀ|#sҒ).H`oP*UdI(u7*`uU808OkAqt%ҟ,|)s|߶km" FXd`u­\]O,H6I~xime$rrI3ϸLK;ytX6:`ʲ^IvQ{T%}ɔzSFL˪'>{ˋ;#$ kp7|A8cWtZLhJ188#N2QGk}a:yH8 ۿ_𹶟ĻIc"rbAt,8^gkM-Ks{glpvy# =>SIXyz^Elbt!=w SF kCV`p}4?jtdHY8$5裒uRR[# qVFܿ6;_5LԅP:Z|kGȣ;Iw^31w!i61OONF*H4B8ֹ7\ EMOĤߗXCt5s|ʥrI+EBi&0rJp:J;qE(<BF@kEoP5oNyU>"I13L0HqEt(ڴc'n7A#5b;f)NqFP1^Gg2,y'+]vIrdY?:(p?V9 cAME V[?P0CDɒ?)f>dN֝0S 9_Bݙ-l9#([ [http://ns.adobe.com/xap/1.0/ 1/4 2/1 13/1 7400879/1000000 3 2005-07-27T15:33:28+10:00 -1/1 4/1 2 25/1 0221 2005-07-27T15:33:28+10:00 3072 2048 1 36864,40960,40961,37121,37122,40962,40963,37510,40964,36867,36868,33434,33437,34850,34852,34855,34856,37377,37378,37379,37380,37381,37382,37383,37384,37385,37386,37396,41483,41484,41486,41487,41488,41492,41493,41495,41728,41729,41730,41985,41986,41987,41988,41989,41990,41991,41992,41993,41994,41995,41996,42016,0,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,20,22,23,24,25,26,27,28,30;4882212CBD8F07E5122A96203B73FD91 100 0230103391 17.0-40.0 mm 1.0.0 17/1 40/1 0/0 0/0 Canon Canon EOS 10D 3072 2048 2400000/10000 2400000/10000 2 1 256,257,258,259,262,274,277,284,530,531,282,283,296,301,318,319,529,532,306,270,271,272,305,315,33432;41012AFEEDCB655C32CAA0AB51740E55 2009-10-13T20:04:48+01:00 Adobe Photoshop Lightroom 2005-07-27T15:33:28+10:00 2009-10-13T20:04:48+01:00 1.0 Custom 4950 +4 +0.35 0 +79 +52 +2 25 0 25 0 0 0 0 0 0 0 0 0 0 0 +52 0 37 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 25 50 75 +1.0 25 0 False Medium Contrast 2231ACF7D78962DE989502D03559415E ACR 2.4 True False True 0, 0 32, 22 64, 56 128, 128 192, 196 255, 255 image/jpeg 3 sRGB IEC61966-2.1 xmp.iid:1FB720472BB8DE11B71AEB8BFDE48F8C xmp.did:1FB720472BB8DE11B71AEB8BFDE48F8C XICC_PROFILE HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)KmC   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((,F!1A"Qaq2#B3R$bCr%54DSUc4!1AQ"2aq#B3Rb ??){AVQQXJ~ޘ8~^ѭK[yc"ɧX,,C.ό`=gu"3$hЕ |qkȲ W[%ff]Iۮf_L Gx. I7! o5ʨuxet6 vdžFw%+$0+&ұ" yqr-h0+Gze0X%*>D-1i'ͣ6EIU^6*hGz`4o͸5W٤X./O3bmfrm:|8#,v:3HsUy/H$@{r7$o qؗPT F1#5]"{6T 5[EN%K؛{_<`=hrHq~q.mE43]E{,t"w [`_}aɴ\!TG,P{@7ȿA{~GkGli55ɹa}EǒYtbYQEUT ,/"9erRnMG:DclM!I!U87oA?c\}Ҥ!6z3ֆI1,m#v 1"3`?Ƣ*T2+/n;. Vbnbdz,ѣMcS=?'hĠ\"@ ]%1t.Y~bO8W)J,(奓vjpJm&ǣʮmQ"OJ ۜ}qMT7N&0 !1LΌV4t"{T C叚JjU"&n>vW.)E3fijU^DP]ַ͹L]j(k+i+hU_ #bAP"bw&2hWI_ 1^ex;}x@BiY̎3F҆(Αɵl vWZVk{$ " ,A_[beݻ6&vZEyELT;-ed;|,RF(W)zyWzOʷ$ͺyqNMZ%p t*I6&{qbl3Zʨ$Bܖ6k 7Ÿi"-Z3$Gf] liK6pxY;{錱JۉwH +k(%T]ВYiQQ傜Ry^Y KWHKn 38`OKh 7i9h(ͧO%Cm4]S $hZ$SOPUvI6`p/iS:l#U& W/:)1HM l/ifE:mlC Bu 6nd2o˜{U.8j8ˀf n }q\h %pK};~D_U^Gc%R6 [5kBMN**Ƅ9 jBIT-z(c: fCw$\\x@k|zfrhSh j!lk;Lp[TJLA&gT7@x-}}ʬw`w%60NJcD..!UE0pW5uNaGvđ omt=i*i|ێ?F4J%$J:ڙ#/}Dtnz覶EW?;?^hq54z&:YXIF[1.*Z:@V؟}&u1T@^v [nqeylU&X)7y#Il#A񹷭,r 0;כpk1[^R#[v&E*MHPȈ.|e5,kFIPB7;Z\4'ʑ}*\Db lO_mUU:((M|?|_cHD>d,o7bFh͠l( $*yBesL6 B_/\&y)%'鿐tӞBS0M*Piۍʛy&?OfX#Y$SGi5Ϧ:]}GpV{,-OSÇ i*I?S}lԵ=h4Hēq խv<3Wk>=? tf-,1> ɗE뎚1emQV@ӜgIl 읕,llKXÒl6aXIRA|R},*|'OJ\q(ؙr/kq8'a5t6]K!@Ab6✚ŵr/ͱ'BJ|55 $mn1y8Lh5X&TC؋xE_;-hTSH'. O[q=qn~+ YB"{-Qobe%*jjX4.[I !?6H95BH໓R{6РYPǍ6&pҢ,Ncd1 ͨ{.9Dw@0Fp/ӌ+eY,\5 Xvo˜)%&_#wN ֻ1_#k;3? j|K:AӤm{x=z~iW4z"@c;o T4R^/Ő]!kKJ! !dVkrmV)TU{ܡwк_%OS7EC* kF<^%Z"E[[p9' :!!Nc(PsIѢ_ UF/.OWV9lor& si*3"_<ն7,h`[2]=P`P9X3cǧA[YNLQHՊ dͷ>bp䌾 hNaEUKUAaHbxrt \߃؛R;M]YzB%UXIU%>xPRJ̑@R9y_1q y))"mn} *SRBi]eV6koFWfQ̈)RSVu1I&Lfeݵn19 mk.1%*j$pħP︽LvE$hm[$P *-}&UBgyPL\略8V:-^a,FR(A&8y#dK,_RQ|J; 'ET|JKi R/{uߦHAe}Ԉi܌P>o+[> '.e$R~4]XtZi- DCJ ]mr/so/ jYdre܋nO}IYE=37̀_[C @E6 6Wp-m$EF f$}8ŷ s "ށ$ l[..Đ]#}YZ\|(ʆEV+hc'qv;y n1R( ku:}0tMFcB^8N[yok};ŖPlǠñve+ V`BH [yS(2QQS{rRwQKN|lSr[@ߦt,v5 ]V&}'GTe5|zY_n=qe]+6 df2؎wHI1=6Bsʻ-G{9k_25.QFm+ ~ݚs&++i?_a+]DW]LJ ]8!ې"5LQ:atA,} _)0z-52Ueu2B*)j!\=خ#~&RuGD#;9hc(35+Ehpڍ8QsfFUB@m}acWq遇A0CیD]<[2 GEDZ&`uLC>=$::N'ؖB6{xC,GUf&pmW"uFH`7$ymgvTgoϜnc^ӫ>U?XuPllnߛ[kdɤ3N]0夑nܐmk(3V`wQrͩ/yl:tUYwŘnC,A{ylL$rHj!8}pmǶ8Qtrki"B@b5©[>#%DTZjJU"Im=_ұl#"f*|NcW]6'p,-Nv@SHtm±P?)mr7B{?{\_`XبÎyՒHrGƊ+j"j#y=Iό]^pM Lap_1oo(q7h1jvT x -YC_kI?_ 0TI.R9"0"_vBe(S5PmnGnf׶{FH-̗ Z90i)I2DFh|7TVcW3F,YIq-yyc#^kV)?R<Isͽ6rǎOobnʯJr+M,>@>!j@0[m~1]3e:i(&%$[Ǒ6k{cQtLv},8ZjȘŮ[|m?fMQb|C@J2JIguXgbc E2TFtצ&I v2ΫbXJWvNt cQURw7NISPLZI(e@&3zdXУDZc_ac.`ȎILJ{1[vak)+GOcvDM1*.EŽ0T6,8uq$IV9rd6RnvoH( G$Ce |ȤI nicGU `䌠~m<^օMQAI2>Ӧ8eNSV uibǺsϗK}vT2_eg%+MzcF eUJrH59$u1QXV 4^Y<5lQKMR5w*Mg=^6)8D[zH"G0n`,n}NO{%UeH{ Q߹6#< DRRz /?{cOT] $ЕkO pGxpu>dͪf[NaEk$. )cKs'gHVzE@mť]"DY{ hrműf,nY鹸 l<'V]%PC|DriV$aR`$8^5ewW`_,M4I k.t8BSxPǟ`CA@ڧXSq*WT`M\aWbIB縉F- eT:!WJ;6=l}.$JM՚u "3"klh.Ж!e&1MA=>R3T!P~Tl=\g~4 snK/ E#&"vVfm& t1e4B`mqÞ9@uS3ЊB|<ֺDcʻUoTe󨤃78<Ԫbw;ybO3JN(]}5ƗF9@Qs X0kI| M٬gwbI)ؒXRuWkr/fUC14 Tƫ뱹V>QR44w!ݎ\NXUfQ yD2iZ:@}'qN+ 2ijpGKb0gj )Bmrm(DJ <R;ׁiFBy+H"Q~Vcؐ8鉖ODl㥡~1qy\$~ >1f;OWIV5b626ۜc6SȮ6b/ŏ{cVg3zr- Ajk4Q} fe%\k}qK;EY-|B`PYw">ksB7;\a&hBə® ީ |"bGVM Yqʐ"A!?_mW_2t#@Mq|0W"i.zLuϋ#i[>(iһU|dәF _`˞N' d!0_qnn=qGM~aj2 u$m<iu_(V"$-˩ C:\zInKY-碕׳=/Le $]Ň}1C ,W]cy+ ̷n3Xi(9)q|,Q$rhs9(R9cxQ`Oͧc3Fwsgc޽_kmՎr[ZiG ARm4+E޲\I/aӹ_96Km=}oxpw{a"*zڌI+/*fbKBQU)`-ffzl O>oq\c%AmBq{)hd4X^k]6`/c댪2i%UFBKlA6rzmB*.@ص<;~CL.CKXtWH[ $ ktĴ))FZ2-b {5cLw+a10:@Ǘ#ȋZ_vh\=ë*scQӯ]NSA! P - R)P<3b@Y$*xm{x{DaDVu%"[mmrHrb UĬUXH+~|ۧLe"T9T.@}8wBI4>.jZXgXe1q}#>+[ e| X)$B E$Itn},}qZeע4D/$u7|D Q4{u-}4Ac L C, 7 02!Hp 1R7}KGiUԏ>`aqeȒeUK,T$ڭlx–<о92jh4 nXԜXJ1E$7/,0.4Wl;"guǩzv:͑WsLocI[3GB- ʋɖPȊZaѶܟx8?ݪ%|\fA`IյXЍc^hKNqcv'9ӋE QR63 E<6<7dAFfuU#4,%6$nm qe4JSB@nw9ڔcf{dh;/xTmNؑ&Idd,6*Fh3<1*Gt_eT%j_i8ψ}WJn?=]R4Q$tuYZiUJK7\NXg*d+JZ37ҿʣ0ONw6PTcdrdSqa}=S-Xm3۴*5=R#-Cl>[ ?RR81P[}zE>DYMl& >#XŠ^jT$I1ny岊"Oqa{[Oqdz.*q4Viu >"a9 s/'0Rx.{n8TsRJ.U\B $b=JqW Uգ xzW'Lg7KkA(DvLqw;5XHuÄm ]&+Ql.[э Dz6;?Ǧ4PZ"ƀ& ;Ţ~vQ1kBdY\J"3G{lkrx\e6W.KKUhA<^dۃrw6<UT4tذ}7$}w9 X"w l-:]C+F"s4,hą H$Uq;R@O55FQ,Y@|\^qV>A1D^bF*H|yf(=D#֨ }H|mJ9&ִ"hea4]+j`z|{_93."j35~0?sݻ;ڔ^.rbGdad5>ZMMHG- VCUdơ cÖHi?@M+τ/rl6Ǥ}6O$Q[Hl,HQN.І/h֞1;(eVau^ $uzy.Qb.7|D5ApQ#`&Ybpn,J^_ldCGT 5Vi*yu;}UC4?e&[*H`Jɗ˱4&t?QB>Ofp>!"XbvSG&CVNT)./.'GœIBҍ!4[|s9-9_]D`&-vቸj4*7ϩZ2Ķ}Mߧ~x,1=ѡV,aT\}:b2J~EX+sk@+|?ѭQ׭.e,scaTW: ˴t豗" ~T[0Ϧ<,rVƟ&:\.WH!'Azoalz0g7[R?+XlǟU;SE1Kɰ ^q`Nq7)y+b>g4[M&[X;7Qﲯgَg4Sڸ3Yuq6e.*[r ).ul ʼn8K9d5s`n 6 ͹1ʣnƀ咣Rk%x QK;rX.`'UAjT п>i%[$lZ|9R&ѢZ<:Ŭ:^޽wɡLP%JڥL2Ȍq,dǎz-ǷAܻ&8LY_qԱ׊nZAJ Z@Q''`<5MaEGٜQ$rfA |ê)U %̥ZjΣVڟPG26$f3UgÚh%ԓa> C::*D{X嵹0J$eh{8e|ۏ8M |R`wͫEZ)ecَ;sY[wY/~Krw?(\u/}$f󺺹ZX(rI>ŤA9og3Lꆝ;S[adK%6a(cUyj($Fm:~t%Qd9?F/0C:9FA6Y%N*B5 &D+[q/s UL0*t>XmHi@vn_|[G ·5Qb^0'}q,B VuGqszDAѱ86s7\,Ў#@-eƾmU*Bg<Ԏ r96d;4NU-+cJf <[첧_v.۟e<1ufk8hbG0v#uFm#o3mfN4!3GExDn+} i$ҤV_Xo]cEdyRq\O %3͔ e'ZY@ OFN.hE7hA![X-V|~VZ3SjFج"8etنq<摞K^OSP cP7uI?ðP&;[xb!Xgeս _L)]So8`4$~BI\Zj(Ńu|o*Q{T:*MPS- 1)BF'N&]ESs.m0} ';2.xK7SwS+ ˲Eoz2dXw"q7?ԬQF~}Ko q't -ZEK.}ko'1}JN\n׮IP$d)Q"@:!Z-˝!f 3ҲzܝL`<>X uKpɻwUSw_3 ;_}YP"Kt&sj 5U7pr~ߧ4( 9DW 4JJcNn>["~*xE S{ݏ+lC-X$wͽ8Ǖ';v}eVx 6䆶m[N5H+N"qfNUY$ [ۓC4I6$ZsF[,+@X إva\ۋ[c99&lϫ~YgMus\-=@qQwRWK$7RmlwpSԋU_UT" aR8#-s^X (ƪ6ƋQ(5M-CJ4XЬuSFRIO'qIP6E7<-a}XʚM$m_Ԟ& ݍx4VTXzybx gJˆBPT2سXf6S-# ,/9\J~ЋuaX2AЌP#UʶeP\1HZ%>Htɿ8sbT8iG(Z=G48+"qk1SQS 'V<nNZ"Sk2? *m &E؇?.uB-@ ۭ,w,c17mbc}.\Z˥)m$O[!#d6T VzOEE %`I?5H(LZ׽ŏ C %YY>l-Ӌb%0h"6C„!`J>n+wޞR)j&FAtoJ(Zě*rmb97$0jXS;1u q timO} KxA16=mjSHa1TU&p-7',r d V?~XDL,mē/.)*24o!]U 呴~xҥ[bھe4!f6VkE<i7 wGK| mQQ%k~ wQVy0 0 b8zI R7";rQ\&2wB*\ HtbFuh0d"HB{Z\0]C=bja[ᾆ3A`3z[fg]c13si-БXN% ( @>qe_ mcn4A}q6ж,ҵ%jR|Od[y{O2r:M*P|)+nwIEH\$z,F^qҔN3̝fab=7aWȺwۜZEPܓ}Aewc+n#¥ erF ʋ0 ،TWN!(67v6Q?aQVX3LӬZ6pOt歙4jbJp.קOUt3THceMbyUeizM4d k>Qӯ%'Z4=JX)i"鶛 cb)f3AQFaWXYdtq~wI`2jKRg'Jcn1-Ae9PRSlMS96ukoź| j!RQv6b7cH$r -IƗ݂p-zhx4R 0R*ERc|vbP+>g"kĝ>{)Y<=AY (ЪsHqvk;GSQ TƎ6S z88<e!{em&eM[cXdXrjT[4$-sJ޸3%XŐi{Q4LABi=Nci&;dO7e,+UZO,\lwǝ)$ `EPdxMaqo<l5^xJmnG[4Hkn;IR|<3jbvT<3HŜXѽ`7xWwJvrL754VX6'~1SҠk"׉PI_N8*F,iHf4{;2@[vfNXL AX4J3S^qR: ּXXr ><4 I[%LUSTK$2P`@E[93 I,GR:8@r>wk)CheQWST[UuƔhje#k[c%.l#l~ ',FE%JwsW*jjh\ *:=`ot; x!JlH[|bn9%ly ܗcU'O6i*Mǰ?lBm&@R;J2+%_gr{)FK-kb_ctGokcBƭU\YN&1F Z'@q-;!S8 q$Sxa- 5s 1☱6 8YX>⩼\HųuK##roA53%3o78I(YuF JgV'uEKcIᨡOJݓx'G4I-)&sfqФem #Y|!z9+LL>F,wc]^EWK"Te51GIO@CXw>C]r{%Rk"jC+ـ[ܶނ!Cٚ@,HVE܋ >=6M#p:j|ţJg.F%mmفSke &yw7!_}l6c,p.uq{kXL$جȨ52iƣ+Sw{my2xbYm)JڧfDZYR6 _9+fu 2Oy,K0PoϾ-BKUp3IRU*h GqǺB1df2@ߝ14l@utSFwS!Wēqc{-1K#ݱ7Bf."c R\G{) UcmئUU$1!A`FTCaUg942E1H*|y7>4gٺ$L)`W+sniOE sf0lJ9/l.b$̨Yb%rlmpsފoC|,'sDLbYZ^dRı ܆E7jW[fOс$/# lnƘkhٮ° #rcT3=8E'F+2w؛oe=ΆցYhiTw.¯닃A[S[hj-t4~`m{zക[Bӫy !Km nXybfAL*s(Rrq&Ӣu `U"`P91~nֈhLpOI牑HeZm bQB- o,iO3K15trq6+3mv]IR``nxݍT8ߊ;bT*b9N̛v.#GuǞzm焤4qha}/9t[vQYr&[4* _P<Ϳ &]qH,(S0^KZaC R+C aX`7c, bwm;O lKr/ 葙R8 4TK{v7bYĴXJSJQ0,vJ(,}\tƛCPT$U/6$bm>dQ8ppG9瓓e)YV9Ta@=p~+7 Zhxwht3Jk,;:;xұ$ o\;b#S,C!,y4Jts2A{B\ UfU"`o0E%%gIYt Q/I@FqϿ^8Ɖ~e[Y`,f;ἴW!SVw1MRɾ6[ܐ-m7\>j$W+X/W$ 8H$v&_Br fHXӦ:ܢ ab,N9Wd F Z/$ c\U"E4# ֶ7f ELUc U [p6==~y'4g9gz"!"{nXϳ=S#f8'>Aȳ1O@Fsӟ}+4Dpثu>닕z3gtپs0Uv ؓm)''XQ*)wL$2bĝ[qX2TK0M=[߾]#=t IƱXk3E$v lz(ct G&`'̍ fSs F{F}12)V4!剙,hd> F:r$a)1溜)Dd#ݷ '5`FazQ$`]C1QI),Ϊo) E1QEvr-n~JR&1N3dk}qz1)XQ*CT\l7 Ao:H`!t(1ikA߳)Mj]"ppln5i3S U,#nBA6&19fSUm;?\u(&t/3*F`w}%ѭ$T]- `vaS&)'2 ϖ-I11M6[l;(&ZP9c9Q:z:hIE,Xo9K`%~R۷a((6QE}N*6ۛq@x̱k_`Q"5$2%@u8 3ZHV2f * UnSP;07HOF) A{O/N̯bʔŽ=1('׽?pۡMҰMNIH=DmsS4X-;4:70ݛX eE!hg_ ) ,2 ۼ JW8$ZF*fnS_Mm/D5Bƽe)b%C[(7^ EcH{YP|8B˹ X U$~dS1UyQ*3G @6ۧ.h+9!BʣڃX~(z4Z.J -jxb  /QU+(2"6fekſ| *;%S[ 522[vEWdnU4&QڑV;n6ա4Q,n&Ò1 =8K#GkVѓm W,l0:fC"ng0HBZȣ>&FcOSc5 &&u䴌ySYMGHیeh(:Fk4jn&:g _o/" du2JĮ!1>UHg|B$0$=LJ EFҌCm?SR eEb ( ?3clPIZ/uDS`mN.9=zt+{"h[U 1FR]C~2 ݔ#0__qB/5U?BIqQ@UXYT.l ة<Xy8M(:bdS a,-t6EH%Ua彮m~jrSfV4o,ZY5pZ_XoD ],4Q.1+{BWc|ަ6HˎA8&f^%d"liI) r@vłe* DYlI\0/s,iX SBձձx@[b m4Mwg'UV{ZegoAnsRD\0PPoF'ֿ=Io!=ߠ XYꀒ6:m[c svkdfRQ~McK&M9:H;>ǵe = pclh | XZ |p0 M|V }6t\,\.*&TD$jPŀ"uDž.o*|e.5rtZSe-4(ڴ&mԀ=}:W[GlOѸ&}fVZY+^x ,H+ :flyq}juӓ3Y~mP^FԽ[s\sRbZZ_䇴 _UtPb&4s@܀: N o$pAʶ=[v<6rTIʛ{;^ƾ9%l$\xcAKS հqѓ%A5ݾ&xV1Jm0*qmjW?{6'? }G@݅v)DjHwi:-` [g42v R< I$Ayd=Hm&SSbT3D-=8y&W>C(VcŁΰc:1= I=#,.J >lc&rJ+]jU:lF:8\]Ѳp# 2.7V4y^U_YQ 1)2^&97%|tƕFkL8^Z()~ovŧe*ʺ moX^ָqLʉ5IWl;eæEbGWd+OxdEriASH[% oL ςq(.-%D2M'rqG 5DH3m6>Ÿc4iE.UN"wv&}[(MVchʝm1ϛ$%0H)Dm'n|ܟb$tҙ$ӡMyzceT{z=X~:i]0{H-~_,TDI4)[Ϯ6qI3XhB-Ʈ]k(@UqFk%2ϨlA*xbE^)^GP>9) S4ļ^C? -qMYQ.z/+<m9JOdW^27x])#k9;klm_OX%>ydծY4Em[`6K'YI jFa!su O-Cm:Hf"N,ف\maP3K.^@m6cBXp:@W ܜ64Ma'5K.Y㦛5lD #!] siY$%bX1E=B1ƽ^C_} $iZJ}::RG'|s/6cѪf,ŠIiR=Eqq( ɜŕQG +=U:zbFn:y]rњ|wEV ؠ.H7' M ZY撒Hc0܍yon+&l"!<4Ҭm1w25EŁʏ~4D Z}h*l§5x .@ y#k(9*W7Y\R44Uf*&z K!K1MbGAO/~*]Cfm^O8[RAXntw -r/ws쾎.45>VQ3Dvg6`eGpvx뵹bQ˖e1$l\Sk-dݿ-˧4WSP)KZ0T5ECOi9م JW3j>[ ގA^2up9!hŌ2.be᱕qIG94u4%U :LT#ͨ46ñnH=-Õ,HɐDœ)t3h^)2?7)f]s ;pE&tRӨ1*8$ }7'ւ{M!-=Lܮybv0n<*:!;k Ư_T( $1ӡv ?2xS7fvJ˂]UkZڛcsDP*>C*HV++KBZH{[ (96S=Uۓ˓Ѝ]NK*^=8M1[t_S6k* 1 .Ԥ ܲ_Ls<]!a a}}n؆zw,|,LNs/BArFR:㍱ɏ\}TM; tSI!6Ʊɴe(ŗ`FZE.e ۞=`NDq-2&[\ߺk^ya tl"LS@0dd}$:s팸*B 3t ~CI#/JJ2ױӱ;y{0,;Xk)3ՕeҬ Y& Nk[q;:c7ѽI#Sǀ88۫cuG6OѩGdٜa%/u܂ zq1vb$eB"Eskō{Φ' >+ЛQ"m8`V{c nvP`8 0 clD'p&UDt{^X$:^ucRNʄ~.~ + ȳ (t|~,#eUw#ifnqE,OF(7VmL*jl)d,bOpAq燓Ikה$\(Jp#UܑG~qUj=dsJ6J9|C̦%$=͸+yD27#78ޘӵu%-hѓ&"1rA d_24WTI0I,ܦ2}fKE4$hJn5om6,%Gl8c˄5ZS m|tbt[Zٝ\žaEbܿG~^XՋmt8i[Q HvmƟm!$-3VSd9UMA}:DX ycid-ίEgbˤ4tĐ H žmnv$yŭd}2JJ**:ˡTͷOB^vsE&o@3D%NJtŵF3V,u<Г!d'kB&Y$:fe( yRlƱBqobo8quT_WOAC A46W8{ "tS}- _QʕㅶR_l5H]*e?,#!GñQ$Ē41%{o3OE$Gav%(,*i4a%]|LAe~l>1}R QcHaiWcEj4稜€/-bSag(;1ȴԳN;b\[V[zX+o%֊ rP%#y#!#kQ*}ن4 h Cؗmv·H?A89^M,kB6,|$Ѡjy&4_Ekcw pz iG`M(N$S"Dzc'=N56|W^˗lol7V sTJAٺl:8b=Pog>hXW2 mpԟ,_y[> $NbK23/2C~&3[Fꅁ{cvXb,A_P3<177Њ\j[># UVD+mL⽗/yjLO;IK+}!I6Pbm[ s nU+ap:;,?5Xlg$q'ǯ1p)gB<:ܐizC#TT31`7ߋ^ROc:VDt2c 6`-ټ|yS(Oa&Z MiK3qm\mqO9:TRI=M#=41H3.($釡S)˿V4ړn/{b\oCZ:9Lt4e|W*@Bl1zfRQ I&:tN:,„2: Tas>vnR{umue4W"-Z 'Nb<})Ňc0RHy@#ͰB&^St,C}!%TmHj?i3 eP0;=QO!6[/*J!A|>"4&s J0Ȅ!m1[["q]u{9'+TP59MΐIc q=9ŝ&.eKMKIQEH)Gr-r96ǕZi$oٜK\LA-;|DHT_sajYmkynBN Qi(J&^ǁ:e&cf Ǵj[pvf$|_x8ы|59nR#$_ x #rt>޴a)~'W F!h$ 3q&q#MvsJL+C"Гeru\,ppk$VhdنYI Z#-܂Z~zg\T~ԓUWE]\8YQe.t]cQO:hQ.&zw$]Qkls{ojQ(i5R"x^CuҢ[؞:*<"g۶hԫVA"7+e6YZZ'VsjXUޚXI= 5|\/gä9wklD%PYv C nf.wh*` B:ghdPNs H;Xp.18|v©+xi 0$1rEnEc`I[b؀O]Iw2KERKi5A^{rovBk*e;t,o3̓7'66:cN~ggj=R~sCK9>*uBi:v F#~F_fvϵYeSûWp[@,CUV c;/SVԵrTvquFwEkgmޭmqp=.wۦ97R`O1tsdfXsN19Of1|\)Def-zxJ2Cn=H\(V;1e2V]T4`Ź1Qg<^h .lyCe̱^1[̪>WMap5i: A6Xԁm L%{[0qxDKݸXm}[?CvA[eUk6[߮&ql噟dX*0itBvEl@l:zN؊|)54ѿ$:$2zxJi™&+5wɕT",od8T^^0N a+-hH݀^p`I)2Xe~X6GU(JZo`bdA.j2ygѬT$;@l%b"u_;`o3u˫)$RI IrvdΕ&*nceXFtpA]/|Mw]$i ,E|~JmK[E0h!Qssb :}qZ2rmh !ͦ00g}$pMԌseeFi sD-(AE[cKi]*eWxYmMsaK ٤ Y˗eѥI;k߁#{s+&)Nv$ѭoJY"RY%f*w`v8ֻ/(h6v=ҒUP'PNBv'*RW\k;3KYC&VT,ûDeaK} 6Ye(^ܭ޵\ƒ8'Dw<16 [\׆I`7.C%NWO$%"VlGOk o.TLj͜5I!*!hb"$5rVlT;W2)9\!b]AVcCo' /UٰK(s(û{o' GG)nL42Sǫ'O}GscW}UOdcjY)m#[ưQV/dm3YfF?bK%LYzi)K.c1鱾q<4Z2P &]Mʏ"ATbnIqM1gXg`EngWR!u:w%][;^VĢлʩ{7WdQ9F{~ lqȲӀ-1vY)&yQIRSX gcudyUUMtS &BV]s5˩h%,&suUll=p A<40¦gT\9H,S[fp#̒1RR6nyLCY:DgwPbR $9As,.>MмPVi ä)IPO֙,Ys#-;+]Kk$vP.mqk>7KtY,ƵF[u rvsǙsqMnZk{/+3HirW:¤yI9[1nGgU>q9IZH#pv\m& 4tar<7d"T9a X ~Л9v96k5hM?w"I5*u\$ߦqyCqwSdɗg\-uI2wlBMʼnm'7'~\LJrm=$?eY(HGyRHN((䃺a3y%Jʐ1}iG6dlC#~Ψ xm3G/P8&ƅE@c}'AUaIP'G?P*J<9;gGI3u~eEyzr/ RAգR5tM* _DQL(2i|.UN}.PaF~mhǶKsNA eQ" F Ÿ5$g4lQ91?wBB mC}WW-)eih*%fGJI.+)o.~vi2/G91F/^e7٠{LEh䖕\)5<6bqs{ du7+^qƗ3djj:cj%S"w,NŸ۝Gc|K iȔ;m*-׸C/+٘1i;CcU#H,ST:ʑ;mԃأN?LACɣ ߀!܂l,\o,8< OzLp7$Ӌ {Dz"om@?0_.яk(j'pFě?,[=g)eL9{X7p<6"qhMQTw04ƥ*"H @0;2+H?̰I5>a"yjM>X[A[Ā &sÎrٜSس=THM@yQ r ۛFqՇ*KC2nUٕuR`~Suk[~էk%Urnfd pZIwodmsO#xc aHhi(yXݞPi;7ƫv_j;=GiS&Kj` {9 {0oC5]_yf 2E4YwLTqʧj&z$So' BrX%Gj脬jᘂT'au[%^q*Šɣ3Geʿ %zm䧙$<29gUT+T )?(یtH:V H'0)Za;.7'+Q5%<̝Cr*}I4il?ol\U|9!0yF) ܍>bD=+`IcYBʣHNگȱ9xܷE㛎'2R[$5<Im{_qog5jY"'7(N[70? EKD. E*_Rq)V3hh2j٢j%aA~$c@:e;ik`@tG8W+59?ks*=Yl$*CgN܋lH&k_k 6ԙeEE(7]E^IaQ)KI@=Z@v TspB;c }-+i;I-F{5`T@YUklqb< Zc y8e"GՙWOQMe]`Y7܃{ lqqh YdTC<YCk6]@۞9OYDg&gQKYSiAhr[b:J.)HzfY*GɅEn=zHi_nh`%2 D`O)Rְgm(oY9tam.kmb0dV,-Q7[x쨺zf" )+Ȓ0c 걵{}1&ʞ7Sj(MLk_o[a]J2V@uy{8ZX?u y06{>h;UMpFM$?͍"̳Yj^ToxE>o>T]e.)]dނM#Sںik OL`ҁ0mH ?HB0,jˢU%Au[ʨ/_ӣgHF-p-"Mwj9=ލt٤P% aHn:?^ĐlTHSL1iV17ta.A+9whahі%%4Đ>>$ZD~:ΨhsH w0Uw|I./bo{_OZ.h]QS&UK3 r>!oOݱ~@"Y3"_8\A;$+JI3 Rub<o}Ǟ AqО SHiU{5,1[.MA Lq}4YUszZXkx;˾;_ʕ N n *|)I15LfIv ;G =ͻ/JgkczjFbq({;Z9 J4˖ْ1:j "Y6nq\dg=*jjur(ˤzwbpѬg4ᦍ5Ѧ*Dd\. 4Y*w $cREkG8dYMU]S,I:lM$nFLڍGlvٌE4;Np5l,}:.iP0$3, n@t_nfrFndCE[xwS^N "qhDVh]$fR@偤") " B׃a#jRcU=2~h[ YLbxd$?-8v@X(RA>aF iH#:~j‰DCQuzG&e}]ct ݸ1%bHP}dVw :}>9 ATr~+3̱ 7>\Ќ7URFl-#|V)&r֎B;EM$q_LFl˫ v ]A7e76kXlzK좔~?UT%$>u'|9.UPc@^_"Vd4JF67e2: lNq\c*;ΊXy72dZg\ t:CD6z7v7" Y-:j}@XLXfSTPԗAI;uN<-ʕ*k1OUGּW4 6ܝ$ܝ*XswQ~Zq@.1ه#?m}*Tci[:q6SBotas|LmUw^'le3 ,sc$#o7N(*zuF4"5 Vv2ᇳY.TLDOXYүJmJzr" < KQ&jXQ#wsAK ¿m?A43f m)xF4|PU=eLSwkܘQ>d>mWK.s:fz5Xk/}Gn1Dfof2Ң$^4U2 7cZEq7kk t%u՘UzQ:ЮfBO; ,EkX~&bxu.䝭[p. xKf q>vvyTtF&b soSj=GyHI'~Gst\CLKࠫ5qREJ$8EH){`;4(Vu#cM#}E9 ]"w.2E:t'ke0IŃE9:#GƐHv9j tests/frameworks/nest/site/images/second_panel.jpg000066400000000000000000000721451176363201700227350ustar00rootroot00000000000000JFIF``C   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((,"E!1A"Qaq2#B$3Rbr%4CSDc1!1"AQ2aqBR#3b ?Viۭ`z릘Z3:VbVi$B8ެH^اHxԅޛH+pEDzk4՚|tӤ+*SjMf(ЬVk1Z4LVcY>tRfYʀN*VFWYNfM *f*µDVif=(,⦐Y^*1ڬǝf(i ڳf*1SH 1PVF*i%bf*44YFM$TbF)ti4bl1V⠭+lj7&IVߥAV5n+1Hʫ4ZM_kg-{XoUa'I#r?!YXWIU*tV@\hzVhH:jtEHNS+( IZ%HMFjBT5 BViu {$>vjtQ2U:j5)N>5sޯS(f.;SMf*U( tQEf45j>;ԠYNUsڰ'j(WiD85j5*P qU|Uj1RSU*4tJj VBe8]N(U~Ԯ425oVAN*1W޳O $Vj A\JS(ZR $YLk; K^C!+,OBw#=+}m<[/p䩗6j=_u|/5̞n gL"TBҵh'`ޙ8Z=ԅyf|N¼S&yT7u%:I eqOk1v>l2zdeSۗ _=*DtHCŵz3@z*tQB<)ЀYEry{ mDrϐ&~ &sSʊ ְNJ(P^_N?M #?ډSҊ|06Eq]=4vm ֈ7 D9{mDj zQZ+4ftmYE6ޣz|*A*tyQ:3YP;  +՜ XS4W/g/ʡtTh~*^b*IPs+4BT . QA]@)D?g.4zThuڵ1T 0O1QEhѶ( fJ>k4[$l3e Ye0Ù!3`WT]LΠzj4zWKui=Qm5KΕ_}í-~aI4M+]Y)=JFlxTdŒUX_TA|װn?DnQv+[+O]:G߭`|aٻaj [$*?ĸ2S7ړA!ug;9ރP̓lc;fحm0[Jڻ%=ɮ[J;6)7BIkn d}ڇ<2tۑ+^ypW cEZ-pGj,7Rݜ|evQaYKߟـGJ :$ł~5/J4 $W!QB#V+nV)-G""Σw$Cjkr:Qܞb#ڠǝۥg+ҍD{垽OTRhJ^G|aM`h}k~{`bmXbqڎh_Y4#lX!ڍV+ң/z^ ߥ`lVr#Vrw;QPҳJ?vڣjXyf!s Z3M_W %a%K u,rX>ς&1Ahx8}"%C&0 ^7pc^/S PτdD3YF@6ԏ}\ Ud]8w0}qC%Y/J0ːl$dQu5D`@LfqiKO$ _>k0l'Vznk^-ynO(fy0l6O4mΔj_jU,,T bLG+ܰ%@x>gz NӔ+)Ih`*[Mm2܃(A[iA@6sR ;ySNGO B+n6oޚ{`oBg#Y#ҧOjkT ykg=EHVfyNG|}jDuT ښr;bA(g MDl*==GڍШqҧMMBЫ66}bTauO5~ZvFk !M'5j:O$fiTa B޳M}Y6dr~CMM+ 늚)Vri$kBGRi'pEcf*5h- }X2{G %r۳z"Z?ҠҠǵG בp)uxاCq ĽX@Fc6ca\Zrzq/t#;ࣉXOi!i!W*ulXc|F2G_Q%RImad%pro:Q'鯯Ua%Wl6_I4H&gVB^znz7/7Y"m⎾lE[fХd'H@; g_ chYpM.v$86Auu#IonT6!N n|0>=WN7o([XcFD渄Qd2 *B0Ii``a7RF 䍀?=&EU-nekmyv!>SI O ]C=Fϝ|)0gh/,w8۬GBBmߔ}ۇ<2H>c15lE7#isA-W7H*4R&R9֎Yպ3'CRƴ֦̽_^"  ] ߸Iq;HU:uҨp-*}ҟP'ʣݽ)ȶj }tm[mҧݰzd(ߥgzSmO*:BOww'Swa }بmݼaގP};_1STš*w|k%ҧNͯcu=JwWrp2Oj"E!@/#[pU6R|_N-T)=geNTzΒ0O'I&L 9%8y+po&=*RK LjY-bY؜vzsAg̈ D8ڜ6p<~{W}kÕTU2;c\~-Y-U.UV@ r  g8\}l -l,x|ZAC*Gf:G 0+Fd v+gNNQTԜy8Բ0w>B,7zc'EzԆ+mVYlXwdyQqfdRÀdz[q,Qm'gqq|IhMj$1'7}^ӶUJoF\l兽Dً/ק֬orgIឍo/@ҳbpЅ$KdqEyu - 9ۯSoYrP\ngm cҀ:*kٺmڐj.V2rku|kbӂ &nkǦ=R;㷩9*E:ucqo{P.< A;-HeHȚEa5n|%$AֽH`~>tyЩ05c$[,KoxP7F>}rx/bG7:~q;9*,K;"sBnyn,T2/_,آs^/9^唞I0i^8#W% PI ˾3[XiJ4 6-`{~4,0q+i嵳,Y;H ;>=Ȥ8SLly49yB`F|WEemo1gj`JIow#EUBB@'>S%%#"EPFtYBhYTMz<>`EsIR\S;Kms JxEy<A{j_% q^OtRzЁ9Wq)l7{N1\>)j\,$o鍷UR4?q3VJ^1`K2s s]H/ĐOґFO j1گ窴n?d93'k]I$MU$Z,grHJ׏^B!M0~95q^<{pΈ$eGS8ŖA[Iii{Ic F+-?zl1^cp{E$`*itI#8"]{I|)B7AخOi[,ցN|,[2Sl戆I>b*Uf䭼"OKRSȫ[Vre3I#' Ѱ:>]OڷZˈ^{vb + PߐZ#}қUsFGt?Q)^D M.s+ALʨCGqU1U|ȘhP-O*qQom Xc*;#SYˍǝv'py0g .ǏGp۲;FZ5EԎFEmHpI w퍗wRp1Ƭm!x|q*r5xgs垃<B/-\C5`Ny uY[$%68'=57pLVfU }ݥUэʟPY( #IԸ=0TZ$مfm8y21&((fXI@H ;Qܞ x}v7@uokv|cU`>'>tإVh帿t=nbm>:AG>\C$ C#lb3_kinyE}@q櫆C H0`i9S}R8K [d7qB @q%v\R(B˅ά<]߭t2Z#B7ַ(mai]cPYː'?IuJEDNK˶Ҹ}3>84IhL=w)6VM,ujUyVIi|1p}N_QϞT-oo? D$ cY/\vszu cInQDc}szb]{o| )?J45JeOc^@9@vހoH2$i+n=7?.d .fcj}5#ղ=lBz$jmjCys @;݆}w] ^ë#.CƯlsXMu2հ;+FV K{uRylQo3q .3qU)u;e1cP"689х wy(.jX!NoOZ;hF9E'C~UDL[#;*ƒ$EL3 %LqԐ5pVy5pj͵iđ,w~L hFNӉ됩##8R#sDDri0N=皺9 + 1ota{Y;h`J|q߶M##cJ |5rj=?kSBeԇtLxŴyU'qnO&_>#o_J>׍<a)$-Ls{YoUG4zm yw$1/vuʍPڹ0:li$tNd\ɝT~'<ɮeu%}6sqUn&IK!Rq$;CqMswXgs `ov@{(c Uٲ(;^㚨3%r8>bq+{kb rIsk^MpVU#Kp<Ƕ>)oy(K4SF\tv֚Nw Wˮ#snecz4hȓK7I'7,w ڄ`wî1#!1 F0,m׶ow8A6]s&21got>:FG%efʝ,I} {8kNsLn5 *"iROa0I#={J{Q#E0]}O™tZ8:{9.@6O>-*;l m \Uvܞ`?w%@B=v|m1i:eQzb4=+`wa:f$|,3ґpf8軹w$ҦO_Aӭs0{Ot pqm*.:MFNp0*Tg`O^/B)M$hęNbn;ĥ29V8Iڒ?N}t~U[k5DoJuа:0I/{%>NJٍ́7) 9x囖Ș;`ou7)Ubuc:|q_*Jx<.NBsUN4|}P k$"] y4@*'n/خ]|uS{)RCI.ڸ+y{k!Y4: :t>=B/Cɩ*>UJ@εf@?ڣDҲVRpWZЩA;Шh`Oy*݅lchR9jeW[j8 ONMA}J|K'YņÖce2mCscm޺mM7  >yKOݲFm"`FuriVf5!OT)pmäHۡIdzzS 익#Jd-3H :4׉$o(]F9e_bˡ.B؝ܡ䅊|Ǫ+=̲H1F0>V}pH.!WTl m&O4Ö&P aU\-xs$',(Έdչrz[*iYbI'=rvQOWCm7-m+c:뇬qbj s;w|UN@"eb H%d3Jf|m# Y-rN2ȃަn n/ql#g%ʰNU->Tk8P-[3sVkľ;順 <۹b}S4ӌ{mp|2FzP^G1E K>i;8׵>,J<砮Zڞ#"\2v?:۸vđVؑ\?Hm\0o|LaeijϮWbY,zX~_26 v"3ZE <"uVڍ-&&" ^=.IJT\v`1.&5&98ٷr?JY37"3 p,]:%pܭβJEr8ԁP1f[;]q/$K==\PqkE3 H$y @6u$]T^ĤoB} ]Y2F? "݅|ܑ :Um Qʺ7|NC|@h,Θd1ƻ\dUSh)QGq{y8t  vTZ@ >bMr"ro67j@rz!zȸkӫ4.2sCho:oΌ{. nЭixQSY3ԈMlHc6xuD6徝w*Vk#N`0D-1Va01QδcETo960zK!!9&t[hrsf9ɤEb%.W59XTF .FCǗΥPܭ.tO*SA'YPI(SkEĉ/ȯC}+(1PX`+`esOOE$@6;PAVK,cհ J=)-JSzjAntDG2;GSMء)n1,kqĞHHoh" ڈ#"ǧF=ظT6!Su#3g|;R8ñqҏhTEoZ0cnVi% ="{{!fL2WLc'wUaq(-@.ovzQ1%-3BchSpGSA@X*Q o4kp[i#LʑA?SC޶`w8z*>^6"qFA܊K UB('4 bjᅤ'^-7V=0{{\9 I 1ǩvH%Kԫg.vOVXE^A=rIFM u|քvUHCv"/ .`wIp/ד.JHeF3,ȋÂppʬʢG sg[x%đ{夋B,koR$_'犡= ?ҕnVE 9v!W]/ɐo,m+P 5|cKؚk'fp|>B`P |)vi,S)Up͖U\HFh/'KN9`_.n!N u\oF_w`UCW#y.!,COJ^HFpB B3)چXA;U2 {h{u8'֯e}W&Gq/I=d XN9\|NJߠ!DZ$|KB8]m` ~UE r:47a&+p´ּͤfDcn8?j꽪=F$S*uU]滝% rG3ub~ fJOutXCjU=iN=EZtV+t$ջ;0ʰNnX>k2f<>^|+C#sڷ~ ++LVT $ 7TU0QF(+grTxXFvZm1| o7(cslV2. q^\FH?޶KY%M+V R@P@4`yCL.N{(rɖe(jgWءNۨ܏?Jemcds!qVA'JYöEcL~h¶r1)-ݤ%ǃ}E(hc)HQwM).I8݂M/cI>Tuܡ޺8H_hlI qY$EVeY6Ɂ?z-dRJuǏҼj8$G8aWlzRG|=J!q7 ` uUKl $Oe}<|[yJؿixbR|JNq_n?}}%Ŭ?DgQKkсUpJO(,4=:ӞG,ajTgիso+$H'x&<-I.E«&wsAZI(uR ` <t#T,F75/I PfEUf mNw@!IԞ[|,j/dyRe)<0`7qֹGE#f< Vn\baqlIcO`CzY}dw'eA :3XVtomẍ́A"3Y2MZ=TΜ\M<Vd%5A4iC p٫#vx{>&,[$zA =Ἆ? @kV2H9lt5VFq\uE,- sn_P,· 6 Y/iT3TQBT=Ma&ѦrJo|Q[8[p?>oeNY H#H\5|&nt[1#8 akؽ"[bNB7UW?ɥ'и6K8ۇJj?s#1][93qĮ##%?"VY8E#ٺPNEǠ`yXaui4B[Jr@MwBY^Hmd"{Ic<dYհ|wJ&YQ s#wO,RȌUdeG]}'i|7+n7056ʤoKH5nu70d9c޶1"zɚK$_:";QeΌl{U{-V#p7}&@:Oҋh8!8=$TZ]t|x#~r#c|.۶Z6[P1436hFIqR}qX֭U;#4ZO-4򬫵k)h{ 1㝿1SF24IgOmT+٣Gtsd\z<^/7&rv'P!=ǡL6U?8?j98 8W*n3- "poGEYc$w(0hC=yI2"&Gaү͊NZǑ%Iy1<~doxV|2@pl2NNkWH*y*O~+{H;hc4Uc@, hK> }hUl❥,1ψ8&V.m!,9 F{|QޮЊ]t4zES>~WAÿYEngsɦrNsn5z*?Tp[v ᱰVoZ!lKZ[pF߮ءM5:&` \yV`I+7;Zj:ngi9߭aۭM83?:?|oyb޶G@5yu8?fca1c7JF65}y5pL1H×'pH!MvY)Y6bg8IdeS7#nbwQ ^֤&zgZ~~_\19v^q ;f" md'Ht,#;N9J#}~N Ib쎭QU|"*l(VweWs,Q3.4x:z"h@P@~9Ypr"_ mjOc{5uC ،%A3=ߴY%YӨ H35xo4/_m-.(ch#|}KvF4\mǯ&N,Iץ'6Fq+esВ{M󯹣&} ;K02z;sZNJmEDMcc\8SBoȸjM<ۭ{GZB5jʼnϧS>?nLrooa jbUD*] ގLqު6pWb:~n{#{Bniv ~/q9ؖ\q - )~.Ts,9'ȏ!)wmvbuLQQ{lUi-o V[H  s R]Dnq׹:eF؎󮫍/sn\Y%܅#8$'u|)zy>rg oH^BI-Z4#+VG* EqrpǥLx^Sm,M3E$dN>i{ 416;z׊k֎U)\0qc+d3{"=#P{o7[(dDz[ҩB|uiq"A,!N>ԧ{-T-uBWbx~t<~2q!7pH)puA6qctt+9c!4$\Y+p@ݹ~uVѥ쌌4LD{>OWk\r&6Ԡpj}S+(.%0Iĩezp Lhe[knxhR6u3jq-035p;p9OSֺR18BO^NP=7ھ؞ )0qQ2\&ɃZ[D:SrG̖>q>n>Pڞ&P,n5$vV*cpFsV;>M@O)|:c{Џ CBv=km b+;3d y_M/I,-O6;vA>U{s(֡qVA¯lK9=ܿw{9m)=ܣ2J.V}}ku+". `"0ʯc]osÐ=؉Zv]7)~f7 /zEJQOe8<ƽnB,燾J0p it;chqb3n=w}}ȥ>Ҟsi}>tN{Oj{xyXF_>?, )'eYr#폝wl!p"6XWH$I084؋ɭh ESsh=ź9Ci:tʀ@m$zuR &M.JF2t,̱bEdujwγ )fEۘΑoO`Rjnɛ[ȫ, LE,cP2,抾1m6LrF;v"U$.2X] IS? 9Q\>NI#J~9\̣(s:GS؊XEv"|[,0`0A^7پkU!R+w?)=Z8T- ?*-5rNLҬ-Tll;;H$(ƣn}u*2qjSPiS9'v,e7,-+ [Ҝ\p|<ϋA]9~{zWx(&&s Hҁ gkHp`mJ65Fl]7Z?x|em-X($si8.#dQ;`ޗkP%u3{W'{aq[ ޹$,qW_vig0A&8u89ws+q5ŧ o3&ɳ?=ZGm("rqϒeUCf65#X!H$Z7[lm(ꭊ!Ocڽgw{Wi vGs*㇋V *R\BeCN4Ioa#qVE(Cmce+fCn$N }Gv4?{WO'CC 1xSH$.B뎻Y8V4{;X.,طzcGs!,#I?]su#(^feGt#o?kHĬ]Naޞs[Swo2I#SK` lG r9եc; zhWnfŒ$D/q~췳fD:`V|rRZKݝOTl øI.Ua`Cv+totmpp aVJK:}ꖸ=sKZ}.4r5kOҨ{:T[dw2lx^qCr@ߥ2[8{-wi;g|wӨ"^yR%}եf h&9O]5oEO#|$SD$G$#~5H;`(B̼̖qI &˳c:u[;rrz(n:IxrH1$yՒlg r:+8*襈-Th i4EmIc+G!PdҩX6Xaڲ, ܆El|,H (ά`|(k91k @ O1u wfCJ9D $h?M鷳q\{r*ݜntRU]Hc4\ ]{V,[glM(3ڵum\,Y6#[ n[ dl6.%K9ynR8A7ڮ[#BIő-O_v_w, {>dH$,vܚx6M1 kkź̉ QWf_*)M98Vh-jedQ,qIjV #Y%l2HDnJIӃnd{Rhڜp$"Ʀp? +pl;$C4*3a΃XβPBO-7yHWk+"SwY4:w.o2K|nÇꐓ KHTi}=0~t#lr$ß(c[k*s%!|_Pv@Z>/"Jl84d6ȚIFZ0޽+7%-XJ#sΖJJh)$Mc1^{? ^p<6@UpD!8fC=;*ǎ\qkRCn-x- 4˞4IݧL#_:ys֓ڥ[Fӫ6 IE׏"9i]E,͑3'5{!33΋=G;c|vr؞N&ё{Hwv\Lv nEsp>3nT=qN7=srsҹ'j^)^9->3 d;ils[oq̓xs Q$ý.~#njHUS0Oޫ72jYpzoXVzU .)r?QlŔnz~TViNҪyЯ'j(CJOm&2hfjP4_$3\"z X]KdvUY.JVvtxGkcepˌv_htLJ gzYåa*/PGm,5%Y;_ruqLb+qQ&=GJs H 6̾x#v#.k&{՚jv'4ڥ,SǮn>FYӏ -cKO9Ge|nکkd`Db2:鞔N5bVMz3EN;z*}69~[&RrKT/%LfN\uO@z|MrƢa `L38q.EHUˮAm<Duȹ20 ȵrn)DK.M"\!4Z&+<9#Cܳ$zAt*ʗ#E[2[KcV%+rL♀H=vQ5hw]O9XG@1(Fb=|\I%εPȽKUygaĦlQG TAi2T&Hl9l)]5C+#Eqndi0/-z0@rrGcCy,[+h92Ek#eCwyumSH!8sXtnF}\7g =qw.al=㻚il"\#D6Y4lw?$mcB E|-ߥvKk bc_^3Xew|.>!ؑglKr@8hgw's'INI*]$3YdM`I HP zzQ5%$. +֖}.J.h^ ={/xU @O$ՀUA}wN'{çRӇ^XmTRV|bnYaBlumnouƶ\in"$S6J;xdL|1"\,WZ`0@884 H[Bæt #ۤm6ˌ :sǭOJK>(nYǃd` Rw5ܯw񈣑Tԓϥ`c ⠑rmJ|)<~ e7m0ZĝcjjP0H3XgA-1X:].M m2-sgfI_hjHa1GT$SIE4݃D/yxWmm6|9 cRɦg{D$lJ䶶=Is.x=crAsI*W@17MȹBsaitc,u.~i/'u?8,As5<"ϼ2T'}CbOޯc[Yc8΢O*(m-%!@Kвɖ'7B(fp:k&2|!A a D4{r5GR (jeդdg5 նexZt1 N2?:ǯoEpݫ@&N7#}? )4!r3SU{=2@\\W y-QmLeWpTz0rq#D@0DƬ#}Cv-8%Nt3EbWvr2BX|N8$#s2oM\PaV=M&<4U[ UxVGڞ98OQƑ11ƞՀJsà[*3)0&:s}jQu/FJoNq;NHW#bGQQMs)2#<8VA(HAm;p=vyT^1Z_1Gp1'UĀ42/@v?o‘$YA#/ۆtu78.e~p5H8ӽg{#$8coO,i3yVbڶ9򃔬]c"7%ն?Hu522dr9A {0z_\` )I%K2]3;ii?eO8XEq&f:@5hߘ5mҹPaã9l||5(c昜Ʈ#coUϩIcxNYp~_q'#>tV(\<@nb*FqQ vaV9gdp=MHtĭQ/=G֙oJ hJGOXziG)o(Fm=+0-p)i@m@>oتi$4,#_z 0)% Z?s$] ,\2q:іJmS cj~ʗ7ԣc3T(' l|.9>Ub7Ur~uƦ%'m}ig&%3e_on<@~UVu,$)qc[\$[Yv$Hd#FNA`i2UhHAߍenLCZm+!R  7uPq̙@P=qrɬ[9UYL.83FWQ{2({ i5džڭKr e1cPL DQq)p@LOH -U1ˑs t?cl<Kq!Iei]hcR1#;di .=;_x܀ rS.;;}9-5yaq„ bg'?:keſ 7,UYw=USPtD.Ѧ 88 g]BP x_?i.Bﶰ vmI$Qd1wL(52h 4Ю"j=5 "6!r$\ja/|`PY |Оg8O"[鍩-EjA;a>y5rΦ1Vb'8OPjyc.TGGCTSBq,+rrr:Y"*T1Ӷ~[x6'Ȍ+8w9lۭGMᷚ^IPX:|m[@gՒrw?`VVђ}KLJn@~>tjbۤ7xU$Rq|pM4: ƑJKK&4C-bjQv[ݎo$=W,{h?Q`IDPlߵ_5_YM6D`Q-˲Z1Oeiy@U$@PoIo$/-@΅_Ѷ^$@G1 P$AyO&Z`u[p7ǖZMsHNNuc,ˮ 6ݔ~^Ma-Ue펇8U{t0!'C'e]ԫz5C+e(E9wj٠bę9ʲ[ΐN 7eȭɝfL AfQX#g9\cGE 4Iy_"v.╟ TKd~3Rؐ@=hym圫Iv'zbʥY\k25[_|`~*Zلb[嚩m`pY ynZ{d*o`>p{owonXl>c@Q7#Us^7 b.ehUtIއR\# <⤐v2f$mQ#\j u¹@띺k{dmFslm5 TO\?O#o4b >KfN1oj;rI ;5U+ w횢yee9g> ;oˁ ؉Rw`t|Z\Aj&Vgq*9mY $5&n[3+eEo&3ӄu˹ fѬm:F=D???ʐqeZ۸bn3_QԒ[ELomv8VvU_9s=:UwfYL@rNo: luОŎҮ~DbFyO8rb$KR.QP[ep5`aim}sH2 ?}Xe cc}TAiUVث1ԌJ&u wB g$`օI~'wtH{cTFwFǘ?Y54GeMQ|#~PHfȠ>ۖ-r2>[ʹ$5܁ޝ-%jZ{VWPBWd'VpF~gj%h=zw߯X9de(A =~՗LHӠC##/\Kp@3:UNϿjm}<{;U[4peKHv :V$&Tszˠ9p ~⌈)Mv !,-4cEe^ş ޙZAg5k,l ga,m۵)X9R)9S$Fס?ԧ+2 t%KȪQCNDHxP@=m,4G,u`;zUVڍJ(|.랧`3ɢ,E1g|됝wگTObd sdnVC! 6cYR5GI#HFm-Ӂhu-\メ~J=tV'~ҧ(XjԢ[0( İo$ӓx[<\&IB3POv/_fKLSye'HR"1wE$;Z\gӢ i$dY&jTbQ#h"+"2MGKh畂Ǒ(iCng20Llu48caH C[4ۺy۸ʟ1%lYIHN^,GN:x$9e?JKfpwFG{22btavR[ BQz8G#e @rMUH"F7 ]\4w29.N٩Ogm[ ڋk }L&,N ~ujdtpzuϞp7>m @FN'A="qK$ݠbsFOLl̞ȝ7vTI.0 JPg>LcҢv2C)ª%[b_Ƨm~#i^6%I7 TM.lb*pI[ )FE7@P\\ K+( ~@StNFrjy]!syіvldL)=غ/V+H?:K)wSe6VK#;cuEFwsE邶Xmm*AfY9$P0ǧM,T30z4Jw |#.8+!Ė Ů]Kt"ʩM*22qT^ڧzO.[Iۦjrp}z;i8>;мKugQD e$3u=>>˜{69 t!ӫ99횩H^N 2v dT:¯i$Ϭ&#nOi$S0FIJpC?槇m@Onfq\d cq諛KcIf(3/r3G֛(X:ҫ=qu:V jeq,RX!2! %II0K9g*W;n 6e~>IFW,K?:1p3HIeAeY A=Y;eU944e7 D_+IX!.Bu07@4XeD|r(HU%媎Cҵ|J҉tBS1D'ylZ[Ir/0̤aH*"*a2 'F{0[Hb'|~:ɦUYb_;IWy:jW+!5ة92*dӥϗVD C$i-&MȀӫAҬx! I Qm+iх!3ү1$r~ZP$ަ,u*Xe;uyy԰D*@ L䬒' BV$mUhf `yV:8% sZ;hi9WK$,T`*Aqڍ9D`o,KG,xF&|QjUN]O[`f,"Hʕ c@z]ݤBH`^QUڒYn1ERlcpgP| s9ߧj[3HDj\Și#YjBUkኅ]y o\Q0Oŏ֑J{ @:Ca<A[cEINw;Dl bImQilVcbI!r}+*rgtests/frameworks/nest/site/images/third_panel.jpg000066400000000000000000000314551176363201700225730ustar00rootroot00000000000000JFIFC   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((,":!1AQa"q2#BRrS$34Cb&!1A"Qaq2R ?XT+EDZ`䅑n4@TDe +`*l,t`"4vTЏ,z).6_;=#TQg9gTS+D<0 b;L-l&=5D^;,tR#FlCGd}.=@6-#he";5[tw@[}Gk'70۸ݙc@}f{cP7L460}MDD2 {ݒhٛGc)[YxL1D El}b0@Klіs9Qah7dai$vX(HVl37k?-V->oGzmJHsJ|,^- )}cH;؋:1udG 6hSrN.f0V/5` hAgU4@56?yIU A3zKcUwaXBt5]'FmQ4s|!nwod|h.o}NHi.߲tH$x]i"# "DqeY#>˷,42rAQ6Ú",jxIM >oa{[4s.Y 5 F%! F&H9GEQQ3gkB#Ȃ9d-46 lpd<2$.h'%{'t͒{bt2ƃT܄(OGR4\=FYum3ødW% G{~S ҩ4{MG.qITT3/_PriK:hwJ;66@'otk]VRN=#8U7U'ն(GF  nA9Ú7#śYuV68A6i9 sBpF 9P_ (/#n{&eB/f/$y"'[GdQ I;%P}uRZNQC͚,RŃ<$ꉦ8sIW7 H$TV%.Zn n2pBxDH9Ǩx * "! mX[h$,BI]8 m?;Vd>ȁ{{nCv4@k U!w/ I{i%8݃4 Φ\mdt<5F|LY7WVCkr;I7GgadK#Ua ef6Gi~LF v@=a5;w1&4ܝuDhć$6oVL7:xK.}PvuXDkw[m׿tkW(vr2{"s@Pm '#+ETw9ZgRcx]4y|$]}VlW פMoEX Yqu"SyB'l6c*>IᱫbbZ B/Y;hE\&&z ^lmHo^MܹLl3 f_(Šԃ#Kd&͔7?}|tՌHw<Yo%!sA|5lِJ;dbI0s id?L9I%8$9$4NUSEhy|rWQ_2x\cf v;Y cYIlX{c8s.tR<27$U^Ⱥ \_̂ft]c|ѾcédǮ2i<]qz8$F8z (l=7(dG4pϑ  ){EhەLoQdUw:NmITU@Zjgٶ& .e6vЁ]3 c5$@~S0~o)?54e4c>Nׇ>x;{<"K pRaJPXlO&:S~/){8+Hi@l̐_cWM {Zކ7fOsAxU?!dgHnPlX Ax.Oȣ\,a6@I)# l(7 $E#y!8x %mBZ@k?RS%%h+d|ߺJL?.ƾASH,S F!u"kfm +n F }V}M<Gpc3ʰpݑ>jgOWlGFd,Jz.ǑMx߂-7 wH`ޞ>A Ƌ=W|>X"fMryioz]MfV0kdŵ9Kc([WsCn& 4Edw |\;}ix,wI=OkOtM陠,kM4G]LpH Y_dG 2Iqc_b?"-c`6'DCBڗDX[3ci$gbI+gvG?O5FFmh(g|W8atVrC.$teuZv1<Ko?e>wEƇk*4ᝩuvɡ[zl~!֒'5̊I\݌t[g4d ܈|\IIʪ@eI0MU*+$`8w3[wQE߷IoЛdRX*v<л^"'4/Z%6@ F^#-a)p[]U~=WSZ,Ւp+Դ!kczYu՟ShM [cКYj0t=Sf(l*v?h#EWb }MR>ND|a;\]@`Ͽ8vtB13Y }}.ކXdxuZ!sZ]Ob0ޜIvGJlO4zY}[~|W ]$q<=qe#Z|_J淨1@;|7K&4̔[{-] u9T=p,5Q~ݢ7Mkzu^hE4=јӁʇLUVvFʪBOKAw'>n"sH}j[)`!- :A"4jaVQ%#vh~B̌vk!.4tzE 2KLC oIel376:v jGWw๝;s_jf"ւq3\n6O7MKEX+[@( 煰3jj(4њLGUPZ#F6Sӹ8s v(M c~\wev{]vMB-B%.65 ߲Q87qhz6amG"@-#"*9e,Oa.a {b"RE2(vU ]P9Dy4ZWWl@d)i#J΢N@ )]ܚJL3&vJLF Rc/(>%ug A g$uv:DqCqQBsI%`*"K9e9(d1"[,>!ynͩ±y̸F!SQܠXhettT OC 4M |@q]]#4E)O6(pG΍t^ѵїͪ+m;%tìb; 7 |A[7WƩ>&LLmK,]m>N0oKxd Z [({LVLGԺ bil ڷ hF?wfrڱey5i F:Poxg(]^s+?#_cd-觷NTlJ?L6\9 _k1h/.0?{~>RH.v( ɢ㚬$kkc,osq;i^\qeI47L]N\u-M 55zS.L(>s#_]ŒW@yQJ좴\'df3 >h&I7 zAX20P.O83۔ˑ04p𙍖I-·35&:ݹ(' P(}!/%mE >Q^|]!@^n}^Fȏ0yτ Y6QT 29 iN|,ZG(P]{%%<4 9?FW؄iqvMUULI݁y#;9&86spu289P˓]CqF% XqPQKTQ+%a5г>+'%DD:7j* 1,C EqFQrB3 ,L1;z'g\;~BEт|{tuYMl6.Lž]_d)3g`W1.65I~V,:҄y?pi[6GHKbnqJ`7kbL1Sk]}1c{Qu_N #A61k을R5)z=Lmǧ2ۣ_C'WȺH \Y][JCF2wn7w|~K:ep9)&,z/I<-CdoFBeVlP7/BZZMzU 6UU&uT="%[ r'DZpMa;>kG>sk7MiZ(X#*<~}4k"D@ (:9[8pwM }G̀ܭ4V$Wzle_W8iL M 47,?yr1Ɛ }y%0 T{)Y8K73 d_[] TY "K<e!J¬i7-)쨔U(n:e')>PS@쒙=86v쑙 "BN4m'+w>Ȳ|fwawLlVwRUG~9OHδsVpJ4nvdY+KtBV Q+.2zY*Y%uj-҈"]C%eB)NUQL3$nDS5pp#~. G#mr qm& S!r&Y ؚ+ dΣ ONis!pˡ13i`s[gwJgHd$B::(͓\ ' xk&$,ܧ쁏eZkl  _S0<$ZEo9SKj֟NX;%5-k o7[FWI&79GUBlM!fVh왕-s N.]X{z{!9 ΌEtVyvU8ٵ(F#qcF 5i 􎆌El,5[)Ӎ55˦Se@?L?){!ɽx ?c5H9J {@3'z@Y2Zv+19znRr 'EvYBK$߄}orC KvW4W?R2͂?) ]ܧ=#7|j =#; jqkON3?u17U $ ms}RwQi-\ɺé©Qhݜ8<䢉X%[(%2㕒i(Y%f]"bEVT]r(+T+ !d" Z *Xb>V0ךY $.co ۞ˡ.PD-{~wc*v:7@h?94tnqKI0NQw@x 9y}SSt3􎗴f$tuQ?\o7Lk5DIq=-Wy9MSh;l;`E;ճ+{=Xp TƷHv0böJ8DkQ \d 5t ~vӻ#пv֝pWoB2d6RzYxDNX#|F#KrdcD->9N\|KD[Ft>8֢2ot=8]?pԕ2> sc%vTy{+=vt}kn\3 ^NQ̿Ξ#B]cozKBL/ 6M[ VyXe۶RwXXDzۅ`H&i{ep/!x@dY* 4:A7 *nKH8O:64G)i@=fHbao0)akK;fV:2I'j5 |Pt.#?Me\6t pF{y _7aq!1#%˘\7>cR,s3.> t6=y%>^^>˝nsq5WLrfO1vCF?qRdi?y岁_?#W#~yln6«)-xshS3ăCzvBrϢpPmDyod+.<+Ï'ч,%QYVU.lUGdBwQeߨy-i *"S@Vm+h ¢!Q@ V)3 b__mP?1_!w '4.gE#\A $"I;kFicHx Bʷ8!5brciɈ!s:ӻFc\%` h63c?oeՓ\ӡir*ˀ)Mf8h2-gb%gC,y.Ib'̳}NNVۮ^0H:XNCsT]Ϋucb=ih1&0]PmM<^;䀹Z-T@,QH<7luNZ+,@A#y@)h&1=^C/͡|3nK+dXz赢py49 IE ZCy3ILoWW&즟VK4t]WH1"f(efN94 YFwf2K_FQ<0x<9kG|b'_t8CUyr6^|=.A4 N/Dz9Qk xM x~vܮiƽv a[qi8kB@NGs㋣V֟S߃YVH wi܏|fhw?Y/9?XY!>4%G( րNJiuLz*mS k ixY?v/^C%5ZPDH9ᄹp=&PFVY¾%VI(vrZ 4;;gg^{lڎ62-+t41΢l5yص.,8Saح.9'^n]xXLjC  wͅ;ҹ^'!+1";}CqC(ek$MX(XyTJWX6G+,PF(D'*("BKwVƶւK-NFyP*sB컱,Cg|l(2/z e)Bi>ˡErb~wN&`VSjuqᒝm5¯pk?0 \V> '63\A4f$[x:,%G[M+at2uzp`+?Fs燑h>ꅡ=ډ[#AkS@0/逫8= k-k,곋-It9q?۟t 1ςO&^ȃTM%<"e%AYϺt?>ώؤ3&tdy3$m%GA2B(O3tmFJ٤l}X/47ぞˌH@=HnT;/\OtR6F*"Zn9x7mJ|!.'s{߷q~L\@aA;_)'yݑ=>.ۮ!\A#)K^Etoh'>g< 9;R9pvH7}Yso1J Wcԛ #LA<^O\:㝏ɜhw[Fƫb\7Ƀ'?s҉@jI( =CYRN撯sLgGQz⎱q+/tW(k _GMm Ӱͫc5U ^sV !C;LE;,BKR2UJpQXp"',F\r?J#!<)E =:QE.Q"% QE1nԴCqa3-!fK*cu[ a wZ\|  {;P[]$v|Z Gk`Ms-h(gXq.$*0޷Uc'ݝwywAsqv=Pd۰sճf<.Mt k`zDdc oOG$?[)@,6^YqkmbCFwP|J[6B㉀(aߔ<\5CoÈ0w\T:Iz:׼niY5=$ pd:h"WӺK&cy?egZޑFĿ}$y\{bIcs]X?1i`Qk}d/C;f'-p4&gPk@?tJM{Q0 f^NՑRk_.kdlӳñWyDϚs+nv Cf(ǃI ,vkFIτ"+>JuzDL {&R!@~h {!O*'t||+y)8rF~#M -x^}MLkQ/K};h@Y-R }GJ-0u>'ױPpT+'e_ @[akaH?7 Z@20nZԨQZ)Ej,bre,4zZ"VV=*4=Y+r`jK7nUAFVyZ3"X-%jWHݫ ldtu4W=\h0ثㄣ^8CMrvZ2ϔwjqCZN/t1ِ/!l?V/pic+@LU]m{E]4ڇjyC6xsMO/8 6Vm8ݶ9z]V)<.眆s)p`9o~)|)nGmkfoP_(Uh.NꕃoZDV RcBV0uK$?>Ou\7e>;EUQ^V]XS!DC6rkV+➋D҇dԎ^VEj.Q@EErcNmBXœg®TQcl+G"C @5l9YV-n Cd Mue ~3d@#8T9nu yYu>VF NZ b4N95kp"=$ ZA]'QN@,%q"c-F%1mҊ-7v+q|0kO9]vv[춂};)#~)`gQ]%ckʄ]GUA_uH,q(Q]8Z+PP,b+iR9**VV1ʅEZQc"(k("(,bR@ckMR{4MMBNWsVI6k67&^^r`pR2wV݂Qa6(e.vSAdOMٷlMυ/ll*6`wV]K7;JY*VQ]D(EXĵQtRNT+ʥ1QEETQE1QEEQc,bS"9XQNTcE cE8SE EjP XłJZ-XQSuû,J\(1ڊX˻QRu2ԼQ`vY*N];] Xe)jSRV6ɕ N9( ,pJ9]1SC%cʥ;)""r+)ʅcE ,b( E(tests/frameworks/nest/site/index.fal000066400000000000000000000010061176363201700201130ustar00rootroot00000000000000/* FALCON - Nest AJAX test site. */ load nest load test_site_styles Nest.site_nest_dir = "../nest" // Uncomment to have debug infos. //Nest.debug = true //Nest.log_level = 3 // Stylesheet // Nest.stylesheets += "some_style.css" Nest.auth_level = Nest.AL.NONE // framing functions Nest.frame = .[include Nest.site_nest_dir + "/frame.ftd" ] /* Nest.onStartup = function() // Login and language are usually checked here or in the frame. end */ // We can start processing Nest request Nest.route() tests/frameworks/nest/site/test_site_styles.fal000066400000000000000000000241711176363201700224220ustar00rootroot00000000000000import Style from nest.widgets.style as Style rl = Nest.resLink("grad_metal_1.png") mtl2 = Nest.resLink("grad_metal_2.png") Style( "Input Background", nil, @' background: url("$(rl)") 30px repeat-x; ' ).applyToClass( "InputText" ) Style( "Input Background", nil, @' background: white; ' ).applyToClass( "ActiveTextArea" ) Style( "Base Highlight", nil, ' outline: none; transition: all 0.25s ease-in-out; -webkit-transition: all 0.25s ease-in-out; -moz-transition: all 0.25s ease-in-out; border-radius:3px; -webkit-border-radius:3px; -moz-border-radius:3px; ' ).applyToClass( "InputText" ).applyToClass( "HashInput" ) Style( "Blue Highlight", nil, ' border: 1px solid rgba(30,30,160, 0.3); ' ).applyToClass( "InputText" ).applyToClass( "HashInput" ) Style( "Blue Highlight focus", 'focus', ' box-shadow: 0 0 5px rgba(0, 0, 255, 1); -webkit-box-shadow: 0 0 5px rgba(0, 0, 255, 1); -moz-box-shadow: 0 0 5px rgba(0, 0, 255, 1); border:1px solid rgba(0,0,255, 0.8); ' ).applyToClass( "InputText" ).applyToClass( "HashInput" ) Style( "Button", nil, @' border:1px solid rgba(0,0,128, 1); background: url("$(mtl2)") repeat-x; background-size: 1px 40px; transition: all 0.25s ease-in-out; -webkit-transition: all 0.25s ease-in-out; -moz-transition: all 0.25s ease-in-out; border-radius:5px; -webkit-border-radius:5px; -moz-border-radius:5px; ' ).applyToClass( "Button" ) Style( "Button with mouse over", 'hover', @' box-shadow: 0 0 3px rgba(128, 128, 255, 1); -webkit-box-shadow: 0 0 3px rgba(128, 128, 255, 1); -moz-box-shadow: 0 0 3px rgba(128, 128, 255, 1); ' ).applyToClass( "Button" ) Style( "Red Highlight focus", 'focus', ' box-shadow: 0 0 5px rgba(255, 0, 0, 1); -webkit-box-shadow: 0 0 5px rgba(255, 0, 0, 1); -moz-box-shadow: 0 0 5px rgba(255, 0, 0, 1); border:1px solid rgba(255,0,0, 0.8); ' ).applyToClass( "Password" ) Style( "Button style", 'focus', ' box-shadow: 0 0 5px rgba(255, 0, 0, 1); -webkit-box-shadow: 0 0 5px rgba(255, 0, 0, 1); -moz-box-shadow: 0 0 5px rgba(255, 0, 0, 1); border:1px solid rgba(255,0,0, 0.8); ' ).applyToClass( "Password" ) Style( "Bordered box with shade", nil, ' border: 1px solid; padding: 5px; border-radius: 15px 15px; -webkit-border-radius: 15px 15px; -moz-border-radius: 15px 15px; box-shadow: 3px 3px 2px rgba(50, 50, 50, 0.8); -webkit-box-shadow: 3px 3px 2px rgba(50, 50, 50, 0.8); -moz-box-shadow: 3px 3px 2px rgba(50, 50, 50, 0.8); ' ).applyToClass("LoginMask") //============================================================= // Calendar styles // Style( "Calendar outline", nil, ' border: 1px solid; padding: 5px; background: #f9F9FF; width: 259px; border-radius: 15px 15px; -webkit-border-radius: 15px 15px; -moz-border-radius: 15px 15px; box-shadow: 2px 2px 10px rgba(50, 50, 90, 0.8); -webkit-box-shadow: 2px 2px 10px rgba(50, 50, 90, 0.8); -moz-box-shadow: 2px 2px 10px rgba(50, 50, 90, 0.8); ' ).applyToClass("Calendar") daystyle = ' border: 1px; padding: 0; margin: 1px; text-align: center; vertical-align: middle; background: #e0e0ff; cursor: pointer; cursor: hand; border-radius: 3px 3px; -webkit-border-radius: 3px 3px; -moz-border-radius: 3px 3px; ' Style( "Calendar days 2", nil, daystyle, 'span.day' ).applyToClass("Calendar") Style( "Calendar days 3", nil, daystyle+"background: #FaFaFa;", 'span.noday' ).applyToClass("Calendar") Style( "Calendar days", nil, ' background: #a0a0ef; ', 'span.day:active' ).applyToClass("Calendar") Style( "Calendar days -- today", nil, daystyle + ' color: #602020; background: #e0c0f0; font-weight: bold; ', 'span.today' ).applyToClass("Calendar") Style( "Calendar days -- today", nil, ' background:#a0a0ef; ', 'span.today:active' ).applyToClass("Calendar") Style( "Calendar days -- busy day", nil, daystyle + ' background:#e0c0f0; ', 'span.busyday' ).applyToClass("Calendar") Style( "Calendar days -- busy day -- active", nil, ' background:#a0a0ef; ', 'span.busyday:active' ).applyToClass("Calendar") Style( "Calendar intestation", nil, ' border: 1px; border-bottom: 3px solid #6060a0; padding: 0px; margin: 1px; text-align: center; vertical-align: middle; background: #e0c0ff; border-radius: 3px 3px; -webkit-border-radius: 3px 3px; -moz-border-radius: 3px 3px; ', 'span.intest' ).applyToClass("Calendar") Style( "Calendar Buttons", nil, ' width: 30px; height: 20px; margin: 1px; font-face: arial, sans; font-size: 10px; border: 1px solid; background: #9090B0; ', 'button' ).applyToClass("Calendar") Style( "Calendar Buttons -- close", nil, ' border-top: 0; border-bottom-left-radius: 6px; border-bottom-right-radius: 6px; -webkit-border-radius-bottomleft: 6px; -webkit-border-radius-bottomright: 6px; -moz-border-radius-bottomleft: 6px; -moz-border-radius-bottomright: 6px; background: #b0b0b0; ', 'button.closebtn' ).applyToClass("Calendar") Style( "Calendar Buttons -- close active", "active", ' background: #909090; ', 'button.closebtn:active' ).applyToClass("Calendar") Style( "Calendar Buttons hover", nil, ' background: #A0A0F0; ', 'button:hover' ).applyToClass("Calendar") Style( "Calendar Buttons active", "active", ' background: #8080C0; ', 'button:active' ).applyToClass("Calendar") Style( "Calendar current date", nil, ' font-weight: bold; font-size:16px; text-align: center; display: inline-block; width: 120px; margin-left: auto; margin-right: auto; ', 'span.curdate' ).applyToClass("Calendar") //============================================================= // Info box // Style( "InfoBox style", nil, ' background: #F0F0FF; border: 1px solid; border-radius: 5px 5px; -webkit-border-radius: 5px 5px; -moz-border-radius: 5px 5px; box-shadow: 3px 3px 6px rgba(50, 50, 90, 0.8); -webkit-box-shadow: 3px 3px 6px rgba(50, 50, 90, 0.8); -moz-box-shadow: 3px 3px 6px rgba(50, 50, 90, 0.8); ' ).applyToClass("_InfoBoxText") Style( "InfoBox Title bar", nil, ' background: #202060; color: #FFFFFF; font-family: arial, sans-serif; font-weight: bold; padding: 2px; ', 'div.titlebar' ).applyToClass("_InfoBoxText") Style( "InfoBox Title bar", nil, ' padding:5px; ', 'div.contentbox' ).applyToClass("_InfoBoxText") //============================================================= // Popup windowsWindow // Style( "Popup", nil, ' background: #F8F8FF; border:2px solid #335; border-radius: 3px 3px; -webkit-border-radius: 3px 3px; -moz-border-radius: 3px 3px; box-shadow: 3px 3px 12px rgba(30, 30, 50, 0.6); -webkit-box-shadow: 3px 3px 12px rgba(30, 30, 50, 0.6); -moz-box-shadow: 3px 3px 12px rgba(30, 30, 50, 0.6); ' ).applyToClass("Popup") Style( "Popup container", nil, ' padding: 6px ', "div.container" ).applyToClass("Popup") Style( "Window Title bar", nil, ' background: #202080; color: #FFFFFF; font-family: arial, sans-serif; font-weight: bold; font-size:11pt; ' ).applyToClass("TitleBar") Style( "Icon in title bar", nil, ' cursor: hand; cursor: pointer; ', 'img' ).applyToClass("TitleBar") Style( "Text inside the titlebar", nil, ' padding:6px; text-shadow: 0.1em 0.1em 0.2em rgba(200,200,200, 0.8); ', 'span.titletext' ).applyToClass("TitleBar") //============================================================= // Menus // menu_style = ' text-transform:uppercase; font-size:12px; font-weight:bold; font-family:Helvetica,Arial,Verdana,sans-serif; margin:0;padding:5px; -moz-border-radius:5px; -webkit-border-radius:5px; border-radius:5px; -moz-box-shadow:3.5px 3.5px 5px #000000; -webkit-box-shadow:3.5px 3.5px 5px #000000; box-shadow:3.5px 3.5px 5px #000000; border-width:2px; border-radius:6px; -moz-border-radius:6px; -webkit-border-radius:6px; border-color:#EEE; ' Style( "Outer look of the menus", nil, menu_style + ' background-color:#503030;' ).applyToClass("Menu") Style( "Outer look of the menus", nil, ' height:35px;', '.nest_horizontal_menu' ).applyToClass("Menu") Style( "Text in menus", nil, ' transition: color 1s, background-color 1s; -moz-transition: color 1s, background-color 1s; /* Firefox 4 */ -webkit-transition: color 1s, background-color 1s; /* Safari and Chrome */ -o-transition: color 1s, background-color 1s; /* Opera */ padding: 4px; color: #E0E0E0; vertical-align:middle', ["li.first_item", "li.item", "li.last_item"] ).applyToClass("Menu") Style( "Icons", nil, ' vertical-align: middle ', "img" ).applyToClass("Menu") Style( "Link Text in menus", nil, ' color: #E0E0E0; transition: color 1s; -moz-transition: color 1s; /* Firefox 4 */ -webkit-transition: color 1s; /* Safari and Chrome */ -o-transition: color 1s; /* Opera */ ', "li > a" ).applyToClass("Menu") Style( "Baloon on menu items", nil, ' background-color:#F0E0E0; color: #101010; border-color:#FFF; border-width:2px; border-radius:3px; -moz-border-radius:3px; -webkit-border-radius:3px; ', ["li.first_item:hover", "li.item:hover", "li.last_item:hover"] ).applyToClass("Menu") Style( "Baloon on menu items", nil, ' padding:10px; ', "li.separator" ).applyToClass("Menu") Style( "Baloon on menu items on links", nil, ' color: #101010; ', "li:hover > a" ).applyToClass("Menu") Style( "Outer look of the sub-menus", nil, menu_style + ' background-color:#402020; width: 200px;' , 'ul' ).applyToClass("Menu") Style( "Transition from submenus", nil, ' opacity: 0; height: 0; display: block; transition: opacity 0s; -moz-transition: opacity 0s; -webkit-transition: opacity 0s; -o-transition: opacity 0s; ' , 'li> ul' ).applyToClass("Menu") Style( "Transition to submenus", nil, ' height:auto; -transition-duration: 1s; -moz-transition-duration: 1s; -webkit-transition-duration: 1s; -o-transition-duration: 1s; opacity: 0.92; ' , 'li:hover > ul' ).applyToClass("Menu") tests/modules/000077500000000000000000000000001176363201700136765ustar00rootroot00000000000000tests/modules/net/000077500000000000000000000000001176363201700144645ustar00rootroot00000000000000tests/modules/net/logo.png000066400000000000000000000636141176363201700161440ustar00rootroot00000000000000PNG  IHDRkĶFstEXtSoftwareAdobe ImageReadyqe<g.IDATx`\W>}un˖"YJB $z[~vv , %v! Hbҫ;v,ɖ^ڽ{ߛF!mKƒf̼w{Nm:0OE,xW3UL1s`JG8Kؔ͟QUՕM6o.`.&hՂ ۙ<&:4]/HH>FPUy8ETkIG ]<À rd^Y=0^dsɩYwBzԚ^5Rf隲Sm'sǯ@!hbr0mE> L2V]Y82:WpԚ%ܯNnMMDAmg4EjktMn}9[ Z&13Z!H`(d+Uc pr4%JlsWXBcH7aXk0,X y5f4ck֔h0%/dZ>[GkMz5Z6=+8'H@b SF0hQ(2;ugd%"rXMlkY,!EN'@ N~ 'r,?3xܮ7dMȒYŴ؛2Ѿj!+C= M`d _e}[Q one )h{h?CT`42H<",(+rt"kpruk=c* T8|T2330YAݝ[=? Y1PtuWyƕr7 piSc1m_vSad舘6;Viĕs P`wɮM?A,400"hJ)m!`FaA#6~* >BepFawվ/p9#B zN|TRB):Ngqͬ=!^67[~;~+"x+i:󉇅ed 9L,W/'&>ؒ̏Є+0 b(4PO=؜Cgs=`(*qT)a9YuWƫq0R#*^G;MT z[8k92rʷ׀ѝ:'0)eسG-^ `Fsv8'ӕkR^VcvZ"Be 4QgTi%I66,{$KFT΀3*n`mJw aW(}{îX؁HtЪ=.' x"xqLI;|/R m^g E)$j07gLS2C`1IhO<&ߍ,q5x7zF00ͼ2 %ȁi(@Zr!kKW2X̃ 3168FɅ nI)#{341"66]WeA*1y[NLgofz8y5a%):Bʼn`b*`slQ|)%^@m9E% >1W tsuLDPَLW[k*ɓ`RC#>mo o)XvCLM'ԧw W+Ewxl#\FNcTU9 A&9~'ɬ<ҕ"Cq552+dl~7铡{t͛ZC}zl7>x;0Y ;Ry_6F_#Q'A[ıcߵFqOLj|uE僶іwޱ  rLBOj%S %n'0*=upvw+7y=X Ib̩:(T-4:<1ǭ㭟3{fшwWHn:J1T3$@G^dž,<w$%` tR?~6}eX`POP5%%"TǕѡW]P{͗هG!mħX0 ˲ i_\,OPqbUC\3Տ}CzN'5jh$& _&i'#*tU݃TB(0! h>BWyz[qC ҉6y׌ha/:%(8ue٭_]jϷ|qg^z׭7}|uIVvuw3iyhǯ9x FA 8NZŚڜP,] _PlRu rF4|TƸrbPJ"Ly> Je8@7s9K%~7.@7gr˒mZ~MSb v7q󭱌vQrNj<}G.q]2lcMwJeaDubY|'0ʌF0ȸ8cPEy:g{Ʉ|E[ y]l 9r0HU&@lH,,2#IWy1Ȁ(08 RMhr}̊?fv>˙5Ԕ/ٽoߒOټRO]{=/Z?O^lhkK?Y0P0usRTXQf E) Js`JAqqjL(!򃀁!Te6rb֑(ymyCh8"ӕSNdq*N8r*zTФ9YP\K}N \"Rm4Su e|y] olT}OVk]bΟV^rz!\d> e:~ffC?P(!9F.NcS2F`NDFՀ7U!aĝfc-}}Mer9%y  աgЊpeU ܜq&b׽HRi$Wnc!^-+Y|=i7[3пS l׃E3ddUT{S5i&L? $N.F( &r#}:2M}B.A hod>(X6oGǤ :8~}c#=7gp^5MLL01'aĄ\"vn/:~=3;#oq4w{әGC3N~FrF*& ,MF&'CbM*e@UK̕ kc;RLu[J\-ǘ.ɌGjmeϦD`%,-=Z`wf߱ETP%CX/B`,d>ZZ[[Rw\j:sR_͒oxU[)_uke*zk)oƇ9n:t`x:*~ԍ¢i1: U"sG I czL$+ d!ulpž;9E^7#Hɇ#IeÍ ~TW584Qqn xuL z1ik߰諏=#3* 6ݪ:&\.Y6ծSD5(w7*!coz<s|D`ָ8F j%01>ҧfV_|%*qyvҿw6qnݎZJ"mz~kXη|Ugą|%[bE%D g=`<#8y+ͪ~,%+{JkG5]YH `eq߅> KYy  zfv|Z]s o1P?'pŬ7zc]YQex\2 rDYxN555^pʧ6oOO VKv]ʼn1` ~wX R%9b " a$$ʔAN2:Zl/O[W,ARpz5o/bR㤕V@G5A֬ދލ;vpY/t%>L~9|KQ?q^h(O%NTVC}9t%! ?x(ǖ ;'j+Y>"4Ւ2\sodCco \Ec (e{.Mg4 CJbq4ݛ Agt yEKwfƼC]xp/fxgFPuɣ7nYKKKVuٝ5 "(k]#-^,z8S3 +ϧ 2MBz>&XGey6AA:O|ΒlJi.:PVP2 Lr d޽ |'V\3c Z3obzo8]_ew y.nf3 _GoS`VƚŖi&X;>oeLAb6Ռf}Zz<^K˲ TUs;̈́jyXK -畬ֆ;=z=kPP"/:8?A `.;#>'(|5:3ª//@jDr7owWOK*F8k?tUT68|{e MBJz:a(y`^Yu+x=D̍n]/z˻ßM*H?H+|H]%%%8VܙP 5'<XۑK%+3,c]S3Wº:|.ʹbG 6e\K!ZzeugXdÀgqp>ԒD~v`_Ι:ғ5 o͖ܾ};:ҦtɦXئ"VI1td* \Y-3˚i_ɀQUAc | 'UF5yi2]0^IV-bljCZuӧ,K.4Bkߩ +uG(*e8 rRӌePu={Jgc1Bx"tolI+xNLj%Q|ea,-^fh{iqc1`MNFdvZ2^<6TaZɤjI"W29)+fc@+e+T FfsD.aF*$ٝsTD%\;Te7\œ|=-*tCǰ:t=i7b3]Q*jwz3R䍞V׵J+PS"+_zJVQhO;KPK=S;4QMH""Ar/_8-R ɚog@3}#;?HD-76p 7ԲAvå\vAN>/i A "*QTgv>]u{ZSU;Aʪqw!k*(3}w=5 6bb` pZ.FeD:lJhmxzعkf} S-gYC2Bɍr,dŢ3Ggj3eZo Ma2k*R2&WԎOEEłhECS&Cǣt@O X-Șl;Ե\(KLZ%%Fg֊25~10e6\`o|nkղX^UbfKy ĆPCSK Lc+K'o@-|A%Ԋ$FFD9pX''f~ͦ N֟ew B]NxuFg2aB.\6}yĞ~-4; 02i8D{ 5d@C"ZXt.O" IT&E"'߯҉ 2o Et}fp9Ӏl 9s2DQB3;+eBidU<l0U |z`%/͠`c !BUDjŌ {1bo 0KTo2:dFo5my-1aJ+k;(h[qV_(c%zifv_LN?r`.Eס>%i F! h6X*ц7&Ş;\d}y=bncN)l /u:|uמ5:+N8ZT`ԃ%abPp8ؼ1 A1tu=K3&Ep;1 }Kftٕ?U[2^9iz`st7ɣ,[^JL$2ȓ!G5FIYV;!KHxy-"}`9l p 3K@^+K#Oq(lXzs\x9՜:9]wСFRA(|V}iXĊ=,gn@̅|ImdAYh;pzo渚p> z4!/' F`  wwvt,o'`<Ҙ)(&>Vr()W)fa(%YAfTE'yu?D"njLUk,RdhEN+^mY/Nڧ9.O"ZJl.4Yc~g'b(%9O>`Baݺu ,5CpC7Ѱ7Y+V0%T INR&m^bko8给qy75= -..>MbH=?tUi<C(n1knngGN`m~F6i!%4̫1VЈƛO$ct?NiYjݣ<3T64Y^׍-Bb΍l`4'9'3o`E6F:IgW~n~|!BlhS߈p֙p8lXv\rgݯLm2 L cE1>_/(?a N-;$/KFQ$v  IsͯY| /"sS5Nx03C k׮]rqZ43,j?~ LaTaĿbX7;yi(eҝc/{h ;ޝG|:X^yqQM̰pqˁ8zTҰH,5- 9NB6Q5Bр 35=ty_+ߘo8s{KX/- [ E@gJn<8( A 9T8FIt`jye権6FZP+Z7:{Ӎu7Vv*3t%pƭYMm(0l6;؞'J4Bf%k_+54`*sD;'i&"ր5r IQh!CJ(Hj@\Nwau:@ETxl5D-AaM,`˻2{>;F8]])Q>jȕ[h{d|6/[bTv0AC->RBұ8F-X$t?a$#R#F۲ 6qᘛ&2aQAb ٢6L!zx%֍e9+OrQs$>u3"5pVA X۟t|Wd]F$MM]&҆ã5TUHkgjWĭ{NKAOF,]ȀeK,ys bo͠ OPY/BFDV*aFc^@7AX+PMoy|ZPkS2x|'9/N"?hѼ/NU]Z# O}r_j^3Zr_gW*0㲊sͶ~ۉSƬUbYs8uP 8Ҍ=gY@ ?)  c,m PYnT%իoɄS R͝_6lXꔐҕنSF0Z5CUI}fl㵟٦vS}uh8t#w5-_A{C;DE5VPr͆Jc\Wp01Ud=Gs5LgTLy>29{D4' ѵFJC8 Pic(1|"34mfhvIU+m㡒{fK&c0ذSl X@U^Jx۶] g8DAb+s1n'V+% =;[ˇfg2W=u5lM?'J+P`%T}|˓7 S=_6&Z@8iUZ$ʸgĩ:|%`u4}~4<[A V]}\o .ݏ|{Z89l&4/h[KJ@< dR& )QE`_e혨#`iBRpfbEgHd鬊# E7_5XO#݊fsh%+ jjNmJˍ^Ԏ6JqzC EXG#mCm5qWF"wj˶ʲ`֟?[›&V"P$%P#N(la๠ ,NԺYr~%k0%sCv-8Su۷/KOwUg33!VE#G 4f xN3j[,*˭7޿@\Jd`ZQ!,t(,(04|6҇~<$C~cˍ=-mЯ d#ZsLʯǟD-B_(:|NT㜊µj`.G>Uw0-#dX#2imóz[~va\F(W_qss"]2Ikϓ.Mٟd;~Đ6j@A9Vue 68hO?*-CxU#$(^ZVX(ޗc9;pSERlŎ~Gf#N+:m e>"OGDInˮ|MV3Opp惎ͬ6\p'̀ th> H,+W[X.&=r:_g]ǎ774#3tJIz4Q8sklުѽZeEݚ=ּ—=֭]k|Uٳ gəgny+LQ f~#VsuU}-DžCtd4- R.96W.wkv w `.5#*vBX}Nu2g2FJmG-/\*vd4{6{,:۞SG$ % ĴE|.wXAs8iKL1LRo>w? @yYݟWѲM?٪$T:./3*1ݸCB1ITGW?|iS񓊽K7ZGnxBlc=0#x/{~hMپQIѥ8s}\XXom< "sTrh.`)œ^bm7,ڸ6 4fc󖩔%InFXcivbo e|1E1:2Oۮ##*+yE1%a >WeU@`Kthsi{"T;;ƺQb2yVG lCHR+6vSmy;rU wG?emΑƪx~8c؅^ƆIϷu :otcWltw{?4TL3iZ_ 5?QCh,46x2\Άƽ{si'*Z&h ǰdCFuWfUjJ500mXGv[4HK[ENmznN䘎f9*]P5ke9OSwߩ]& 7~΁-ŦV 7lvqϣ0k$;bdr}_;w7,x>79KqDkm !ƒ3IS%8'Oͬ.\ȪUԡKo꟥ᕀy:LJg*ZLL3ggGkLAxX"CRĦ=p5}Cª?uKouSLO9Wlޡvfdn)sd.,]tn%`c6R˟RΩx!5T3(jhѝz2А#ˆ_GjF遪 7 XhV:bYOoUmVXSksV(<8Xs'jk$vZ`ZYa{cϤ} +H(`~'gf=mF`FaU~O^q{jcO_陙PkYFJ($g& =D4sҍ: z Acs4^ ;B4ILKu9B!KB4᯼щ5غ{Pl9ͺ-()C{JW:صٵWp#Us6xz} ?< mTbeY̊}&UlP9k(.f6;9_OmNMt~~<ʧky-,qMGT908yx?4р](U7.;:)9^kJ؈.9ޕ2jQX.޽OO p<7vuf2Ǻ6PKO <Dp®]zJ`-m4M ĹD[`.FyGb@̊8!8SXylSEW_HypB:_vZoB2|./fܞ-3ZDfr Ҷ2irT 736r2 (GglD N,f! 8mŸ7k >)0Ӕ} Zo {wY&esVc wSN_g3n2ɴsC1&X-6%sj᠀'f4|)c#W ( qCEX^}͇O츨* ,c8pz?fYIGdt7P$0Q"Ҭ#FTi 7O3;) hVXƎT;dD-猝r7h?wWsW~O,y!]3}섣 o "|h8S}0Ωmm{Ju<3qW~ Un4Y%X12YX &Уv,LAg  E۬di>~ٓs`o/|wAt2}싢r,BbE^32C&Ɉ6TٜszG1r 1 ,@fi=ӺOVqwГX4Վu7tah20 PW\W~#C~wvms=2:Lw9 F^:;UYDTJ:5^{A9ݨBKNa'oO#+\*|]wL"UtS_v顃_ 77~ȳWqW+%:?SuJc i{CDT]kL佇gZ#sN~g%@ntՠBQDh`;}l6 >g-Ȫ9PaIc jd#EaQ RĘ7?sH˳ ^P-Y _"͐mVVoP$[xx9KAWoϜ"5ц0 V'E#ԨǤ6 [""InBҧĪwAIӌ&,ά<"8&<,HcB"DoFh咨y te !@<]_萲jWO^s@:Y_o/{j,p8Hk|i]y}BKܳǣ`bx2k)ά@nŬ( ւ{cQ/3bu [`jϤ\^ 1[ܙ;8HaB[`g&7]Df=mLE%Ԝ~A ayTRh!s81uXS;bg}ڍlnn\]/ 2 sTr<JY}%8>{hZAh1pK/͚K'o:XY:ɁO8y ̓#vb$[>щ}—Pl @,5fC2k"'3,̽4*Hy2\b9ġ-M`A W^yv[)=sfMLzX5Mt/y{&eZ_P",_ He>8"3l|hz%b:BKv}U\ZFVeH?gvbWPՕO!8y=8R@+@&trq {2NC ۆ}3D#F`az3^ˀ #Qm"UZF@tcߑY!Paòԙ3W_BR|+ %/Z@PzgkS]$5\B$lz<*Xw" +9/ctkSC';QO빈We%2TSB* %"- H#%f. k$G)O}d~)-)eSUOajԨ/i5:^+7-TTf=Wg@',!+F\]GTGy5ziϛ Yt%ɏJaO%AYs94y5Y i%f4Al5_0\h]X]nKǏ6Q,3%0=SLȼNJ[S)NsY +P ]$TGư.YO2˟%bSjmb*2YѪZ7xS2eu7QpbEތg5g\Ay}R; FF$.O8NykfYC%–x+XE#UM35Uc $.SnJ[ó4Cۼ8d{|ǘRRbȒ~T W9"镀5EJQEWD({O|׺w4Xr8qO8r] +y%bgQ  RdӳKPZ-vޢ* ZzDBG5]^jtl3|3*PIQiE ⤅I*r0JAƔ7zqc߱T/hhjX9Ss۬Zp%2&,U{f7{O&3VRUX <}# ~ih0JU>.:Ryw#M@ǹF^%qbFc;wqEPIh4J2@-DlœQF!dDD$qU(F#E2ʞU n@̖n0래[L_nH, BJ>t^8{/AKdBڳ2)h|L;qff&kBHdTe)YHK}A7!E/"( HF'kIJpJ]@U/xk{vh&X+X`q%6+:7m>vܤ9G{YRiAXHvOF$C`܆ՕZa}-qXFCD"7 $4e4.@l[z4{2N0V,!ӁgW٬鞾MZXɠM?3 JnVHl Ŧ vdz'MlCִn姍O0!] YŌHw*ީ)rHvp.62 ^]C-bySH iH֕FowwT{)tzYv7{UZh}Hk(t@֪Plt r2J1%9x@X " :;Ғ yJ[n޼SKq!,&ܚ ={RHub"@% 'uՙUTS9=%dGcT[|R˜=%3u,\^ί4#c?=8sw>~?I $EQdيX[:IӎN:m3x6-ȆUFE!0,G=,rjۢ@B q@!Rs#|p} ZTCΘE=]l)t#|9Fc1~b?XaMqٔl]Z6Rr186c!6`+Yv=W؊" I-EM$3, s@$`-Ըžbm4'^RY 7m? G7G-;ȗkR9,KueΔVk 2`f+D.hlб`4 vyvGnxHBI,ԷFN>,0N)3f0%A ԊF1tTz.3rA"MO#Bkn՚ 8Df/G %ES7ý3R=z<+RB_,&:mFݚqMms#Qt#\C3TH[]{: DY/ЖC1֢jk˭$Iv*Y@CeS\-bQ Cw4%`ozޛúy1h+]V`FiL-q}^!c!MiJYM5;AFZD;\QqIztR$T_S,OtZh+irZ'1rn74o|%Ug`Q5ofA^J ^U".?yhaaJQIGWj}e]t8(he.nnxcٞx13Q_oQ*eof£E<,a$`S&vd;oi8"sT7m<:p O!~TN*Q׺b JQ$"L9)E~'JRaECF*D;_W晀MS+fHC60>. 2 (Eh `u /-`OwO2KP $'ˑ9s3wJns6޻J(.]ˌ ECnu%Ɉ6KNݴ뽓 ujOkm xVHsx8wMF,FoE_h7"(JL5ɌWs[U1C iχrY1sbZ-Lb]68cKCd`MMHiߖqt!V@!̚j9t5͚02ٜ@ta]3~vs_paZ mrvq9F mJz :m7 L|eɳsA\'>c_ Zj;2pcDu3ZuiEOʣ6N|Ӛwp?Lʩ.\CAxI6-M'ҙJ;c0𳘘 k>$M 5a&\86l;)xק eujraSƃBd[̲#߮_(`/*l'jIP;뚭{g9\#VH~'q#ʐ#Qg"[a p_xREyeV|P)"b~XFɦZ ]l)/BX@l>Sina.=ߍ C\) t3Ykd[}B>3p:x]šw+-+3ql:hXN.`tU ^ ~$m>VL @ҭ'1 Cw:;&&&@kj(|Tp.>ނ EᄘXLFMK8OC)eVX[;ɵq٤M_gBc9ji4e ݳ# mz0V񡅷B#ޔz5 kڎrm)8;vrAjΣ7(a|sFY*gZJ55w dp yoTû+r |Hw\19B@6kM3JTmVI,Ͼ 9`d2B)ՄTO7%+i sJ@Բ@&A''M"IXX@IQio1Ȧ^79>uO\~_%;h&qtwdfF#&f]vs}'?ºOXܕ! y'6`;ox"pe|*UH໼ӻ~}hi6/@PTf,⮖ĖU%۾Īgj#'SojMrƁDL妧['&ad@l'%4-C/<73/}+{0jG6$$Zg-?=4ͼF~'LR 2]OOFcLN|d2wӣgϑV)FJ Nndw2݀@hOoZVc4{dC )-tABцVfb MW"ߘ GH)a)0Z.-wDdvZEQ;J(IŁ~a鯾̾R3TKA;@41-Zlop̿phc O\,s-^3uo4h#p܃05 I:fƾExWaM~ ]_bɊxlBk :uM x$b|`7jդ?;߉&bL r2"uFdnu`;vkeRol7RNF#A$rjB k즃O o&ok6|1UJPNtHͺ5:j \>Y9%,QeP;F&G{>IR& :׍\6JJOZlɂ7;3FMc2}s7N^xٳrx$7VQq >SQ߻.oyAN]oƓPH3P!B5/SF cۼns^oOhƪ1WB78lje^5qB~)ucoƱ0jcA`Z3hK!0mHa2337^ skl)j&"؄jX-~כ~+;7_im W!\ З:'M',Ve,-LXhz Fo:z7Oլ^I7`0@0hۀ!w{콾BV4 #!ۚX7ZO! ( :hV4 `5[̶}^cQdJ&֚Be'&('/*yԭ\SཉvX^oqbwFl+7}3AXHU%l '~ ɏhʼn t̬1zs=4R.u9'Fi,1>?~83+reS1WDvE#ϧ> &oLn9dG665k1-Zx3/~0ZR:Zdf,oIp9U1e>|-0TeJC',V*0@HKu4SLZδA-AsN֑ˬx^59=HMejH=-9l聚桧\+LOb &ٞc{ʹg_ /:ۀ l1KYݨvV 1FEr&}}~~[x=o16}j=-1TXydQOȑw @@=vhUn6LL$;n(Ȭ1t9mhBFLXجWO YzKYiKx-Ԍ}Nqb6GٱXgtmE6)ڲڴ m.*Oj! xg#$2oISiی~4qc9{S<$/l*wtnJz 2r-4uX.BqSV;3Kj[9d>~)Sifmc)fߐepf-_ZJe)  `<ѿ*'Vɶ1tKa@gSz2/q,R[ü<&6#8 ӂ6et4OR D>u{CSdۉZ򉳏.iK7CwŒ|Yd\Bg|MJAr&BVҒ;DR/E(_AP^뒂=ӿܷ[y~mBv놅;mݮ$ (y@%>s 2_{Z/5e /e>>[H/}m'ŏƴ{5ֱq(3H!ծ.V!|YDܻ2ZQMӵEiIQw`m, JrG2,P23G''O<:{ r秮/2⃁o؏xKa0{Hzc<qG/~?lpAe*ղQX|zyVfc5vEYIEȞ]X 4Dg<>'!Eжp j UF Ph"BˬPJA]%[]Ү? f^CXӪJ3v:kK 2Ӌ vrqܴ t/QXb`U*})yED Slt=̙S'c̓3IPp2cۀin#>~-:Q&GI @io=s߀ "I8 | BwAݼ+ - uI^n&,@j "Cd׺mwTOFt$?f2B1U, FI&_Apri 45`e +} rvrnxomsh(]㭮Sy2"F MpǿUAhs8*)8>p~ȔJ LPR~wzJ1o*؞cq ^m\ q|5gw6 ?ڕ4=3ضY?Q%v_\= 0Xa:AwS@ٸmC>r> Iu M7n8*reE<ޫe`4U:L%0 apeQe1JyR]T}Hu\5;S˽E2A o;0AU,AHu ~?}5 SN;'fIENDB`tests/modules/net/read_a_box.fal000066400000000000000000000023271176363201700172370ustar00rootroot00000000000000/* FALCON - Samples FILE: read_a_box.fal Read a mail from an imap folder Usage - imap_server_adders user pass folder [msgid] ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Wed, 19 Jan 2011 14:22:39 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load net.imap if args.len() < 4 > "Usage: read_a_box.fal [msgid]" exit(0) end server = args[0] user = args[1] pass = args[2] folder = args[3] msgid = args.len() > 4 ? args[4] : nil if ":" in server port = server[:server.find(":") +1] else port = "143" end // have 5 secs timeout remote = IMAP( server, port, 5000 ) remote.trace = printl > ":: CONNECTING" remote.connect() > ":: Logging in as ", user remote.login( user, pass ) > ":: Selecting folder ", folder remote.sel( folder, true ) if msgid msg = remote.getBody( msgid ) > "=" *77 > msg > "="*77 else > ":: Getting the list of messages" > remote.search().describe() end > ":: Quitting" remote.logout() > "::Done" tests/modules/net/send_a_mail.fal000066400000000000000000000025761176363201700174150ustar00rootroot00000000000000/* FALCON - MIME Parser FILE: send_a_mail.fal Test for the SMTP sender. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 21 Nov 2010 15:22:29 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from web.mime in mime import SMTP from net.smtp as SMTP if args.len() != 3 > "Usage: send_a_mail " end mail = mime.Part() mail.setMultipart( mime.Multipart.mixed ) mail.headers["From"] = "test@falconpl.org" mail.headers["To"] = args[2] mail.headers["Subject"] = "A test mail" textPart = mime.Part() textPart.setText( ' Hello, this is a test mail sent by the "send_a_mail" program. The text is automatically encoded as quoted-printable and the content type is automatically set to text/plain. Also, the text is turned into utf-8 before sending; we''ll add here some accented characters to test this feature. "àèìòù" (a, e, i, o, u) The Falcon logo is also attached. ') mail.addPart( textPart ) mail.attachFile( "./logo.png", "image/png" ) mail.attachFile( "./send_a_mail.fal", "text/plain" ) > "Starting mail transfer" server = SMTP( args[0], args[1] ) server.trace = printl server.send( mail ) > "Mail transfer complete" tests/modules/parser/000077500000000000000000000000001176363201700151725ustar00rootroot00000000000000tests/modules/parser/samples/000077500000000000000000000000001176363201700166365ustar00rootroot00000000000000tests/modules/parser/samples/creole_test_cases.txt000066400000000000000000000057471176363201700231020ustar00rootroot00000000000000= Top-level heading (1) == This a test for creole 0.1 (2) === This is a Subheading (3) ==== Subsub (4) ===== Subsubsub (5) The ending equal signs should not be displayed: = Top-level heading (1) = == This a test for creole 0.1 (2) == === This is a Subheading (3) === ==== Subsub (4) ==== ===== Subsubsub (5) ===== You can make things **bold** or //italic// or **//both//** or //**both**//. Character formatting extends across line breaks: **bold, this is still bold. This line deliberately does not end in star-star. Not bold. Character formatting does not cross paragraph boundaries. You can use [[internal links]] or [[http://www.wikicreole.org|external links]], give the link a [[internal links|different]] name. Here's another sentence: This wisdom is taken from [[Ward Cunningham's]] [[http://www.c2.com/doc/wikisym/WikiSym2006.pdf|Presentation at the Wikisym 06]]. Here's a external link without a description: [[http://www.wikicreole.org]] Be careful that italic links are rendered properly: //[[http://my.book.example/|My Book Title]]// Free links without braces should be rendered as well, like http://www.wikicreole.org/ and http://www.wikicreole.org/users/~example. Creole1.0 specifies that http://bar and ftp://bar should not render italic, something like foo://bar should render as italic. You can use this to draw a line to separate the page: ---- You can use lists, start it at the first column for now, please... unnumbered lists are like * item a * item b * **bold item c** blank space is also permitted before lists like: * item a * item b * item c ** item c.a or you can number them # [[item 1]] # item 2 # // italic item 3 // ## item 3.1 ## item 3.2 up to five levels * 1 ** 2 *** 3 **** 4 ***** 5 * You can have multiline list items * this is a second multiline list item You can use nowiki syntax if you would like do stuff like this: {{{ Guitar Chord C: ||---|---|---| ||-0-|---|---| ||---|---|---| ||---|-0-|---| ||---|---|-0-| ||---|---|---| }}} You can also use it inline nowiki {{{ in a sentence }}} like this. = Escapes = Normal Link: http://wikicreole.org/ - now same link, but escaped: ~http://wikicreole.org/ Normal asterisks: ~**not bold~** a tilde alone: ~ a tilde escapes itself: ~~xxx === Creole 0.2 === This should be a flower with the ALT text "this is a flower" if your wiki supports ALT text on images: {{Red-Flower.jpg|here is a red flower}} === Creole 0.4 === Tables are done like this: |=header col1|=header col2| |col1|col2| |you |can | |also |align\\ it. | You can format an address by simply forcing linebreaks: My contact dates:\\ Pone: xyz\\ Fax: +45\\ Mobile: abc === Creole 0.5 === |= Header title |= Another header title | | {{{ //not italic text// }}} | {{{ **not bold text** }}} | | //italic text// | ** bold text ** | === Creole 1.0 === If interwiki links are setup in your wiki, this links to the WikiCreole page about Creole 1.0 test cases: [[WikiCreole:Creole1.0TestCases]]. tests/modules/parser/samples/creole_test_ext.txt000066400000000000000000000070761176363201700226010ustar00rootroot00000000000000= Top-level heading (1) = == This a test for creole 0.1 (2)== === This is a Subheading (3)== ==== Subsub (4)==== ===== Subsubsub (5)===== The ending equal signs should not be displayed: = Top-level heading (1) == This a test for creole 0.1 (2) === This is a Subheading (3) ==== Subsub (4) ===== Subsubsub (5) You can make things **bold** or //italic// or **//both//** or //**both**//. Character formatting extends across line breaks: **bold, this is still bold. This line deliberately does not end in star-star. Not bold. Character formatting does not cross paragraph boundaries. You can use [[internal links]] or [[http://www.wikicreole.org|external links]], give the link a [[internal links|different]] name. Here's another sentence: This wisdom is taken from [[Ward Cunningham's]] [[http://www.c2.com/doc/wikisym/WikiSym2006.pdf|Presentation at the Wikisym 06]]. Here's a external link without a description: [[http://www.wikicreole.org]] Be careful that italic links are rendered properly: //[[http://my.book.example/|My Book Title]]// Free links without braces should be rendered as well, like http://www.wikicreole.org/ and http://www.wikicreole.org/users/~example. Creole1.0 specifies that http://bar and ftp://bar should not render italic, something like foo://bar should render as italic. You can use this to draw a line to separate the page: ---- +++++++ http://falconpl.org You can cite from a foreign site; this will last **not only** for the current paragraph. Citation can spawn more paragraphs, but they can't contain block elements. However you can: # add lists in blockquotes # Or close them right away Of course, you can't close a block quote while inside a list. At least, you should add an empty line after a list, or the blockquote will be confused. +++++++++++++ ++ Or you can add a blockquote without a cite. ++ You can use lists, start it at the first column for now, please... unnumbered lists are like * item a * item b * **bold item c** blank space is also permitted before lists like: * item a * item b * item c ** item c.a or you can number them # [[item 1]] # item 2 ** And then start a list ** of sub items # // italic item 3 // ## item 3.1 ## item 3.2 *** and again sub items ## item 3.3 # And always return to top numbering up to five levels * 1 ** 2 *** 3 **** 4 ***** 5 * You can have multiline list items * this is a second multiline list item You can use nowiki syntax if you would like do stuff like this: {{{ Guitar Chord C: ||---|---|---| ||-0-|---|---| ||---|---|---| ||---|-0-|---| ||---|---|-0-| ||---|---|---| }}} You can also use it inline nowiki {{{ in a sentence }}} like this. = Escapes = Normal Link: http://wikicreole.org/ - now same link, but escaped: ~http://wikicreole.org/ Normal asterisks: ~**not bold~** a tilde alone: ~ a tilde escapes itself: ~~xxx === Creole 0.2 === This should be a flower with the ALT text "this is a flower" if your wiki supports ALT text on images: {{Red-Flower.jpg|here is a red flower}} === Creole 0.4 === Tables are done like this: |=header col1|=header col2| |col1|col2| |you |can | |also |align\\ it. | You can format an address by simply forcing linebreaks: My contact dates:\\ Pone: xyz\\ Fax: +45\\ Mobile: abc === Creole 0.5 === |= Header title |= Another header title | | {{{ //not italic text// }}} | {{{ **not bold text** }}} | | //italic text// | ** bold text ** | === Creole 1.0 === If interwiki links are setup in your wiki, this links to the WikiCreole page about Creole 1.0 test cases: [[WikiCreole:Creole1.0TestCases]]. tests/modules/parser/samples/creole_to_html.fal000066400000000000000000000016501176363201700223230ustar00rootroot00000000000000/* FALCON - Generic Parser FILE: creole_to_html.fal Read and parses creole WIKI data and outputs an html generated out of it. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 30 Aug 2008 09:42:22 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from parser.creole import from parser.render.html in html if args.len() == 0 > "Please, indicate a file in creole wiki text format on the command line" return 1 end is = InputStream(args[0]) // let's say it's utf-8... is.setEncoding("utf-8") ctx = parser.creole.Context() p = parser.creole.Parser() p.initParser( "start" ) line = "" while is.readLine( line, 120000 ) p.parseLine( line, ctx ) end is.close() > html.Renderer().render( ctx ) tests/modules/parser/samples/creole_to_tree.fal000066400000000000000000000024311176363201700223140ustar00rootroot00000000000000/* FALCON - Generic Parser FILE: creole_to_tree.fal Read and parses creole WIKI data, displaying the layout of the parsed context tree. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 30 Aug 2010 09:42:22 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from parser.genparser import from parser.creole function traverse( node, level ) > "CTX"+level+": " + node.type + (node provides level ? " lvl: "+ node.level : "" ) if node provides infos > node.infos.describe() end if node.content == nil or node.content.typeId() == StringType > node.content else for item in node.content traverse( item, level + 1 ) end end end if args.len() == 0 > "Please, indicate a file in creole wiki text format on the command line" return 1 end is = InputStream(args[0]) // let's say it's utf-8... is.setEncoding("utf-8") ctx = parser.genparser.Context() p = parser.creole.Parser() p.initParser( "start" ) line = "" while is.readLine( line, 120000 ) p.parseLine( line, ctx ) end is.close() traverse( ctx.topNode, 0 ) tests/modules/parser/samples/faldoc_test.fal000066400000000000000000000025771176363201700216240ustar00rootroot00000000000000/* FALCON - Generic Parser FILE: faldoc_test.fal Read and parses a faldoc document, or a falcon file with faldoc comments, and displaes the found entities. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 25 Sep 2010 14:53:06 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from parser.genparser import from parser.faldoc.falsrc import from parser.faldoc.generic function traverse( node, level ) > "CTX"+level+": " + node.type + (node provides level ? " lvl: "+ node.level : "" ) if node provides infos > node.infos.describe() end if node.content > node.content end n = node.firstChild while n traverse( n, level + 1 ) n = n.next end end if args.len() == 0 > "Please, indicate a Faldoc source on the command line" return 1 end is = InputStream(args[0]) // let's say it's utf-8... is.setEncoding("utf-8") ctx = parser.genparser.Context() if args[0].endsWith(".fal") p = parser.faldoc.falsrc.Parser() else p = parser.faldoc.generic.Parser() end p.trace = true p.initParser( "start" ) line = "" while is.readLine( line, 120000 ) p.parseLine( line, ctx ) end is.close() traverse( ctx.topNode, 0 ) tests/modules/parser/samples/faltxt_test.fal000066400000000000000000000024201176363201700216610ustar00rootroot00000000000000/* FALCON - Generic Parser FILE: faltxt_test.fal Read and parses a faldoc document, or a falcon file with faldoc comments, and displaes the found entities. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 25 Sep 2010 14:53:06 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from parser.genparser import from parser.faldoc.txt function traverse( node, level ) > "CTX"+level+": " + node.type + (node provides level ? " lvl: "+ node.level : "" ) if node provides infos > node.infos.describe() end if node.content > node.content end n = node.firstChild while n traverse( n, level + 1 ) n = n.next end end if args.len() == 0 > "Please, indicate a Faldoc TEXT source on the command line" return 1 end is = InputStream(args[0]) // let's say it's utf-8... is.setEncoding("utf-8") ctx = parser.genparser.Context() p = parser.faldoc.txt.Parser() p.trace = true p.initParser( "start" ) line = "" while is.readLine( line, 120000 ) p.parseLine( line, ctx ) end is.close() traverse( ctx.topNode, 0 ) tests/modules/qrcode/000077500000000000000000000000001176363201700151535ustar00rootroot00000000000000tests/modules/qrcode/basetest.fal000066400000000000000000000012021176363201700174440ustar00rootroot00000000000000/* FALCON - Samples FILE: basetest.fal Base test for QR code generator. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 25 Dec 2010 16:32:16 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from img.qrcode in qr if args.len() != 2 > "Please, specify output and 'text' on the command line" return -1 end img = qr.Maker().encodeText(args[1], qr.ECL.L) img.savePng(args[0], 3, 2) > "Complete" return 0 tests/modules/web/000077500000000000000000000000001176363201700144535ustar00rootroot00000000000000tests/modules/web/ajax/000077500000000000000000000000001176363201700153765ustar00rootroot00000000000000tests/modules/web/ajax/ServerTime.fal000066400000000000000000000010771176363201700201540ustar00rootroot00000000000000import from web.ajax class ServerTime(dry) from web.ajax.Function(dry) //zipReply = true errorMap = [ 1 => i"Timezone must be an integer" ] parameters = .[ web.ajax.Param( "tzdisp", check|{ v, f => try return int(v) catch in e raise f.error( 1 ) end }) ] function run( tzdisp ) ts = CurrentTime() if tzdisp tsadd = TimeStamp() tsadd.minute = tzdisp ts.add( tsadd ) end return [ "time" => ts.toString() ] end end ServerTime()tests/modules/web/ajax/library_js.fal000066400000000000000000000001561176363201700202240ustar00rootroot00000000000000import Generator from web.ajax.library as Generator Generator( "/", "./", .[ "ServerTime"] ).generate()tests/modules/web/ajax/servertime.html000066400000000000000000000022611176363201700204520ustar00rootroot00000000000000 ServerTime test

    Falcon - Ajax test

    Server time

    Click on the button to have the request updated via ajax.

    Optional displacement:
    Here the result will appare here
    Problems may appare here
    tests/modules/web/htmaker/000077500000000000000000000000001176363201700161065ustar00rootroot00000000000000tests/modules/web/htmaker/form.fal000066400000000000000000000031271176363201700175400ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. Hyper Text Maker - XHTML generator FILE: form.fal XHTML generator -- form test ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 11 Jun 2010 21:42:11 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from htmaker doc = htmaker.XHTMLFrame() doc.html.add( htmaker.Head().add( htmaker.Title( "Here is the title" ) ), htmaker.Body().set( ["onload" => "launchme()"]).add( htmaker.H1( "Form test" ), htmaker.P( "We're making some form test here." ), htmaker.Form( "receiver.fal", "post" ).add( htmaker.P().add( htmaker.HiddenInput( "hidden_field", "hidden value" ), htmaker.Label( "User name" ).add( htmaker.TextInput( "UserName" ).id( "uname_id" ) ), htmaker.Select( "optval" ).add( htmaker.OptGroup( "First group" ).add( htmaker.Option("First option", "op1" ), htmaker.Option("Second Option", "op2" ).selected() ), htmaker.OptGroup( "Second group" ).add( htmaker.Option("First option", "op3" ), htmaker.Option("Second Option", "op4" ).selected() ) ), htmaker.SubmitInput( "Send", "Send the data" ) ) ), htmaker.HR(), htmaker.P( "Here, we're done!" ), htmaker.ValidHtmlMark() ) ) doc.render() tests/modules/web/htmaker/included.xhtml000066400000000000000000000002371176363201700207550ustar00rootroot00000000000000

    An included fragment.

    It's not hard to create included fragment, just use the Include class.

    tests/modules/web/htmaker/included_fal.fal000066400000000000000000000014261176363201700212060ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. Hyper Text Maker - XHTML generator FILE: included_fal.fal Show how to include the result of a falcon script in an xhtml document. This is the included script generating stuff to be included ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 11 Jun 2010 21:42:11 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ printl( '

    An included fragment -- from a Falcon script.

    It''s not hard to create included fragment, just use the Include class.

    ') tests/modules/web/htmaker/inclusor.fal000066400000000000000000000016021176363201700204270ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. Hyper Text Maker - XHTML generator FILE: inclusor.fal Mimimal XHTML Generator using an included file. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 11 Jun 2010 21:42:11 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from htmaker Reply.ctype( "text","xhtml" ) doc = htmaker.XHTMLFrame() doc.html.add( htmaker.Head().add( htmaker.Title( "Here is the title" ) ), htmaker.Body().set( ["onload" => "launchme()"]).add( htmaker.P("Trying to include some text here:"), htmaker.Include( "included.xhtml" ), htmaker.P("Inclusion complete"), htmaker.ValidHtmlMark() ) ) doc.render() tests/modules/web/htmaker/inclusor_fal.fal000066400000000000000000000016121176363201700212520ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. Hyper Text Maker - XHTML generator FILE: inclusor_fal.fal Show how to include the result of a falcon script in an xhtml document. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 11 Jun 2010 21:42:11 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from htmaker doc = htmaker.XHTMLFrame() doc.html.add( htmaker.Head().add( htmaker.Title( "Here is the title" ) ), htmaker.Body().set( ["onload" => "launchme()"]).add( htmaker.P("Trying to include some text here:"), htmaker.IncludeFalcon( "included_fal.fal" ), htmaker.P("Inclusion complete"), htmaker.ValidHtmlMark() ) ) doc.render() tests/modules/web/htmaker/minimal.fal000066400000000000000000000020471176363201700202230ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. Hyper Text Maker - XHTML generator FILE: minimal.fal Mimimal XHTML generator ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 11 Jun 2010 21:42:11 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from htmaker doc = htmaker.XHTMLFrame() doc.html.add( htmaker.Head().add( htmaker.Title( "HTMaker minimal test" ), htmaker.Meta( "desription", "A sample test for Falcon module htmaker" ) ), htmaker.Body().set( ["onload" => "launchme()"]).add( htmaker.P( ).add( "some text.", "some other text.", "And still some text." ).set ( { a => a.style = "my_style" a.onmouseover = "dothis" a.id = "thispara" } ), htmaker.ValidHtmlMark() ) ) doc.render() tests/modules/web/htmaker/script.fal000066400000000000000000000022561176363201700201030ustar00rootroot00000000000000/* FALCON - The Falcon Programming Language. Hyper Text Maker - XHTML generator FILE: script.fal XHTML generator -- script test ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Fri, 11 Jun 2010 21:42:11 +0200 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from htmaker > "="*78 > "XHTML" > "="*78 doc = htmaker.XHTMLFrame() // Testing an XHTML script doc.html.add( htmaker.Head().add( htmaker.Title( "Here is the title" ) ), htmaker.Body().set( ["onload" => "test()"]).add( htmaker.Script(data|"function test() { alert('ok'); }" ) ) ) doc.render() htmaker.OutStream.stream.flush() > "="*78 > "HTML Transitional" > "="*78 doc = htmaker.TransitionalFrame() // Testing an XHTML script doc.html.add( htmaker.Head().add( htmaker.Title( "Here is the title" ) ), htmaker.Body().set( ["onload" => "test()"]).add( htmaker.Script(data|"function test() { alert('ok'); }" ) ) ) doc.render() htmaker.OutStream.stream.flush() tests/modules/web/mime/000077500000000000000000000000001176363201700154025ustar00rootroot00000000000000tests/modules/web/mime/mimeMaker.fal000066400000000000000000000024461176363201700200030ustar00rootroot00000000000000/* FALCON - MIME Parser FILE: mimeMaker.fal Test for Multipurpose Internet Mail Extensions parser. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 21 Nov 2010 15:22:29 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from web.mime in mime simpleMail = mime.Part( ["Content-Type" => ["" => "text/plain", "charset" => "utf-8"], "Subject" => "Hello world", "From" => "me", "To" => "you" ], transcodeTo( "Hello. This is the body of the MIME message.\n", "utf-8" ) ) > simpleMail > "=" * 70 subPart = mime.Part( ["Content-Type" => "text/html"], "

    While this is html

    " ) complexMail = mime.Part( [ "Subject" => "More complex", "From" => "me", "To" => "you" ] ).setMultipart( mime.Multipart.alternative ).addPart( mime.Part( ["Content-type" => "text/plain"], "And this is plain text") ).addPart( subPart ) stdOut().write(complexMail.toString() ) > "=" * 70 subPart.attachData( "Hello; this will be seen as an attached file", "text/plain", "data.txt", "Your data" ) > complexMail tests/modules/web/mime/mimeParser.fal000066400000000000000000000015451176363201700201770ustar00rootroot00000000000000/* FALCON - MIME Parser FILE: mimeParser.fal Test for Multipurpose Internet Mail Extensions parser. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 21 Nov 2010 15:22:29 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from web.mime in mime mail = mime.parse(stdIn()) showMail( mail, 0 ) function showMail( part, level ) > "=" * 70 for k,v in part.headers > " "*(level*3) + "HEADER: " +k, ": " + v.describe() end > "BODY:" > part.body > "=" * 70 parts = part.parts if parts > "Parts at level " + level for p in parts showMail( p, level+1 ) end end end tests/modules/web/mime/mime_test.fal000066400000000000000000000047721176363201700200660ustar00rootroot00000000000000import from web.mime in mime msg = ' Return-Path: Delivered-To: testing@chaosorigin.com Received: from spool.mail.gandi.net (mspool1-d.mgt.gandi.net [10.0.21.131]) by nmboxes15-d.mgt.gandi.net (Postfix) with ESMTP id C2818FD9BB for ; Tue, 14 Dec 2010 18:58:35 +0100 (CET) Received: from mfilter8-d.gandi.net (mfilter8-d.gandi.net [217.70.178.34]) by spool.mail.gandi.net (Postfix) with ESMTP id BE95D25D31C for ; Tue, 14 Dec 2010 18:58:35 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at mfilter8-d.mgt.gandi.net Received: from spool.mail.gandi.net ([10.0.21.131]) by mfilter8-d.gandi.net (mfilter8-d.gandi.net [217.70.178.34]) (amavisd-new, port 10024) with ESMTP id 7z7rghrLDhbp for ; Tue, 14 Dec 2010 18:58:34 +0100 (CET) Received: from mail-iw0-f176.google.com (mail-iw0-f176.google.com [209.85.214.176]) by spool.mail.gandi.net (Postfix) with ESMTP id 3BC5B25D31D for ; Tue, 14 Dec 2010 18:58:31 +0100 (CET) Received: by iwn2 with SMTP id 2so1086087iwn.21 for ; Tue, 14 Dec 2010 09:58:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:received:date:message-id :subject:from:to:content-type; bh=kGHp7R0RtvHHDG+ecNgiIiKPXcWejy2HEAyqm1ZSxQI=; b=AES1/nTiXrsw8UQPV4FRMoqSllXHDxunrAMon43yI1kBU+yjEkTIcj9PJV6MmF5b7r ayATvrVjrFuj4hO2MlaVbi2s0KUpO5zEJQwkRnlhUCTZ6pZkV0+KOHBgnCu6nHH5ArVQ BLn9/9JQ9QglUBJ9srjyqB0b9yCRsB8PBRDeo= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:date:message-id:subject:from:to:content-type; b=Y/mqYj5HQJei76uxuN+qsHdavAeA/6VHrVwd0GXpXnutBnM8EcRnWUoWvNgqg1RURW ABbB814PpbzLKTUewRgopNCqN9e+AYTvvdDMbjW77U+hrpLmtjbJgSpfXC6fdQZFWZAt Xb6m8vnUh6c9DndDT3L1qErb6pQxMfrItOjWA= MIME-Version: 1.0 Received: by 10.231.35.8 with SMTP id n8mr3644588ibd.123.1292349510212; Tue, 14 Dec 2010 09:58:30 -0800 (PST) Received: by 10.231.153.7 with HTTP; Tue, 14 Dec 2010 09:58:30 -0800 (PST) Date: Tue, 14 Dec 2010 18:58:30 +0100 Message-ID: Subject: Test IMAP From: Stanislas Marquis To: testing@chaosorigin.com Content-Type: text/plain; charset=ISO-8859-1 Hello test imap.' part = mime.parseString( msg ) inspect( part ) /* vi: set ai et sts=3 sw=3: */ tests/modules/web/oauth/000077500000000000000000000000001176363201700155735ustar00rootroot00000000000000tests/modules/web/oauth/oauth_access.fal000066400000000000000000000056451176363201700207320ustar00rootroot00000000000000/* FALCON - Samples FILE: oauth_access.fal Second step of OAUTH request. This test uses a token file generated by oauth_request.fal and exchange an OAuth request token with an OAuth access token, that can then be used to send authorized requests. The input file is updated with the access token, and can then be used by oauth_call.fal. OAuth mandates that the request token must be invalid after this operation, so it's totally uesless to keep the old file (the data in that would be discarded by the remote OAuth server no matter the result of the operation on this side). ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 25 Dec 2010 16:32:16 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from web.oauth in oauth import from curl if args.len() < 2 or args.len() > 3 > "Usage: oauth_access.fal
    []" > ' This test uses a token file generated by oauth_request.fal and exchange an OAuth request token with an OAuth access token, that can then be used to send authorized requests. The input file is updated with the access token, and can then be used by oauth_call.fal. OAuth mandates that the request token must be invalid after this operation, so it''s totally uesless to keep the old file (the data in that would be discarded by the remote OAuth server no matter the result of the operation on this side). If not given, the token file name defaults to token.oauth. ' return end verifier = args[0] address = args[1] file = args.len() > 2 ? args[2] : "token.oauth" > "Opening file ", file try fin = InputStream( file ) cust_id = fin.grabLine() cust_secret = fin.grabLine() token = fin.grabLine() secret = fin.grabLine() fin.close() > "Read request token: ", token client = oauth.Client( cust_id, cust_secret ) xtok = oauth.Token(token, secret) xtok.verifier = verifier > "Exchanging the token..." access_tok = client.getToken( address, nil, xtok ) > "Exchange complete; access token: ", access_tok.token extra = access_tok.getExtraData() if extra > "="*70 > "Extra data in token: " extra.do( {k,v => >k,": ",v}) > "="*70 end > "Writing access token file: ", file fout = OutputStream( file ) fout.writeText( cust_id + "\n" ) fout.writeText( cust_secret + "\n" ) fout.writeText( access_tok.token + "\n" ) fout.writeText( access_tok.secret + "\n" ) fout.close() > "Done." catch IoError in err > "I/O Error while reading or writing files." > err catch curl.CurlError in err > "Network error while contacting the remote server." > err catch oauth.ProtoError in err > "OAuth protocol error." > err end tests/modules/web/oauth/oauth_call.fal000066400000000000000000000036131176363201700203750ustar00rootroot00000000000000/* FALCON - Samples FILE: oauth_call.fal Third step of OAUTH request. Calls a certain remote web API protected through an OAuth access token. Prior to use this script, it's necessary to obtain an access token from the remote OAUTH service. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 25 Dec 2010 16:32:16 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from web.oauth in oauth import from curl if args.len() < 1 or args.len() > 3 > "Usage: oauth_request.fal
    [] [post data]" > ' Calls a certain remote web API protected through an OAuth access token. Prior to use this script, it''s necessary to obtain an access token from the remote OAUTH service. If not given, the token file name defaults to token.oauth. ' return end address = args[0] file = args.len() > 1 ? args[1] : "token.oauth" post = args.len() > 2 ? oauth.Client.parseQS(args[2]) : "" > "Opening file ", file try fin = InputStream( file ) cust_id = fin.grabLine() cust_secret = fin.grabLine() token = fin.grabLine() secret = fin.grabLine() fin.close() > "Read access token: ", token client = oauth.Client( cust_id, cust_secret, post ? oauth.Via.POST : oauth.Via.GET ) xtok = oauth.Token(token, secret) > "Calling required API" result = client.callAPI( xtok, address, post ) if result > "Result: " > "="*70 > result > "="*70 end > "Done." catch IoError in err > "I/O Error while reading or writing files." > err catch curl.CurlError in err > "Network error while contacting the remote server." > err catch oauth.ProtoError in err > "OAuth protocol error." > err end tests/modules/web/oauth/oauth_request.fal000066400000000000000000000047411176363201700211550ustar00rootroot00000000000000/* FALCON - Samples FILE: oauth_request.fal First step of OAUTH request. This test connects to the required address and asks for a request token. After the token is obtained, it must be fed in some Web-API for authentication (i.e. inputed directly in a browser). Once authenticated, you can use oauth_access.fal to exchange the request token with an access token, that can finally be used to call OAuth APIs via oauth_api.fal. This scripts writes an output file called "token.oauth" that is then used by the other scripts as the input file. You can change the name after it''s written, or add it on the command line. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 25 Dec 2010 16:32:16 +0100 ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ import from web.oauth in oauth import from curl if args.len() < 3 > "Usage oauth_request.fal
    [output]" > ' This test connects to the required address and asks for a request token. After the token is obtained, it must be fed in some Web-API for authentication (i.e. inputed directly in a browser). Once authenticated, you can use oauth_access.fal to exchange the request token with an access token, that can finally be used to call OAuth APIs via oauth_api.fal. This scripts writes an output file called "token.oauth" that is then used by the other scripts as the input file. You can change the name after it''s written, or add it on the command line. ' return end try > "Asking for request token:" r = oauth.Client( args[0], args[1] ) //r.mode = oauth.Via.GET //r.use_header = oauth.UseHeader.FULL tok = r.getToken( args[2] ) outfile_name = args.len() >= 4 ? args[3] : "token.oauth" > "Token received, writing to ", outfile_name of = OutputStream( outfile_name ) of.writeText( args[0] + "\n" ) of.writeText( args[1] + "\n" ) of.writeText( tok.token + "\n" ) of.writeText( tok.secret + "\n" ) of.close() > "Success" > "Token: ", tok.token > "Done" catch IoError in err > "I/O Error while reading or writing files." > err catch curl.CurlError in err > "Network error while contacting the remote server." > err catch oauth.ProtoError in err > "OAuth protocol error." > err end tests/native/000077500000000000000000000000001176363201700135145ustar00rootroot00000000000000tests/native/curl/000077500000000000000000000000001176363201700144615ustar00rootroot00000000000000tests/native/curl/get_cb.fal000066400000000000000000000017241176363201700163740ustar00rootroot00000000000000/* FALCON PROGRAMMING LANGUAGE CURL library binding - Samples get_cb.fal - Shows how to send incoming data to a callback USAGE: get_cb.fal */ import from curl if args.len() != 1 > "USAGE: get_cb.fal " > return 1 end try h = curl.Handle( args[0] ) // we want to receive the data as a stream. h.setOutCallback( receiver.callback ) > "Starting transfer..." h.exec() > "Transfer complete." h.cleanup() > "Received content (just some parts...)" receiver.describe() return 0 catch curl.CurlError in e > "ERROR!" > "Received the follwing error:" > e return 1 end object receiver str = "" function callback( data ) > "Receiving data in ", data.len(), " bytes..." self.str += data end function describe() if self.str.len() > 80 > self.str[0:40], " ..." > "..." > "... ", self.str[-40:] else > self.str end end endtests/native/curl/get_dload.fal000066400000000000000000000014101176363201700170630ustar00rootroot00000000000000/* FALCON PROGRAMMING LANGUAGE CURL library binding - Samples get_dload.fal - Uses the dload function to get a remote address on a stream. USAGE: get_stream.fal */ import from curl if args.len() != 2 and args.len() != 1 > "USAGE: get_dload.fal []" > return 1 end try if args.len() == 2 os = OutputStream( args[1] ) > "Starting transfer; writing on file ", args[1] end data = curl.dload( args[0], os ) > "Transfer complete." if os os.close() else > data end return 0 catch curl.CurlError in e > "ERROR!" > "Received the follwing error:" > e return 1 catch IoError in e > "Error in creating the output file: " > e return 1 end tests/native/curl/get_multiple.fal000066400000000000000000000023721176363201700176430ustar00rootroot00000000000000/* FALCON PROGRAMMING LANGUAGE CURL library binding - Samples get_multiple.fal - Captures multiple files. Uses a loop to determine end of multiple transfers. USAGE: get_multiple.fal ... */ import from curl if args.len() < 1 > "USAGE: get_multiple.fal ... " > return 1 end // A place where to store the handles class Storer( uri, id ) file = nil uri = uri handle = nil id = id init self.handle = curl.Handle( uri ) self.handle.setOutCallback( self.onData ) self.file = OutputStream( @"get_multiple_dump_$(id:r2p0)" ) end function onData( data ) self.file.write( data ) end end handles = [] multi = curl.Multi() try for i in [0:args.len()] st = Storer( args[i], i ) handles += st multi.add( st.handle ) end > "Starting transfer..." loop v = multi.perform() >> v, ": " for h in handles >> h.id, "(", h.handle.getInfo(curl.INFO.SIZE_DOWNLOAD) >> ")" formiddle: >>", " end >> "\r" sleep(0.1) end v == 0 > > "Transfer complete." return 0 catch curl.CurlError in e > "ERROR!" > "Received the follwing error:" > e return 1 end tests/native/curl/get_stdout.fal000066400000000000000000000007711176363201700173330ustar00rootroot00000000000000/* FALCON PROGRAMMING LANGUAGE CURL library binding - Samples get_stdout.fal - Shows how to dump a remote source to stdout. USAGE: get_stdout.fal */ import from curl if args.len() != 1 > "USAGE: get_stdout.fal " > return 1 end try h = curl.Handle( args[0] ) // by default, it's so -- but we repeat for clearness h.setOutConsole() h.exec() return 0 catch curl.CurlError in e > "ERROR!" > "Received the follwing error:" > e return 1 end tests/native/curl/get_stream.fal000066400000000000000000000013331176363201700172770ustar00rootroot00000000000000/* FALCON PROGRAMMING LANGUAGE CURL library binding - Samples get_stream.fal - Shows how to dump remote data to a stream. USAGE: get_stream.fal */ import from curl if args.len() != 2 > "USAGE: get_stream.fal " > return 1 end try os = OutputStream( args[1] ) h = curl.Handle( args[0] ) // we want to receive the data as a stream. h.setOutStream( os ) > "Starting transfer; writing on file ", args[1] h.exec() > "Transfer complete." os.close() h.cleanup() return 0 catch curl.CurlError in e > "ERROR!" > "Received the follwing error:" > e return 1 catch IoError in e > "Error in creating the output file: " > e return 1 end tests/native/curl/get_string.fal000066400000000000000000000011171176363201700173120ustar00rootroot00000000000000/* FALCON PROGRAMMING LANGUAGE CURL library binding - Samples get_string.fal - Shows how to capture remote data in a string. USAGE: get_string.fal */ import from curl if args.len() != 1 > "USAGE: get_string.fal " > return 1 end try h = curl.Handle( args[0] ) // we want to receive the data as a string h.setOutString() > "Starting transfer..." h.exec() > "Transfer complete. Writing the content: " > h.getData() return 0 catch curl.CurlError in e > "ERROR!" > "Received the follwing error:" > e return 1 end tests/native/curl/index.html000066400000000000000000000506231176363201700164640ustar00rootroot00000000000000 The Falcon programming language
    logo
    The Falcon Programming Language
    A fast, easy and powerful programming language
    Location: Home page

    Falcon is ...

    ...an Open Source, simple, fast and powerful programming language, easy to learn and to feel comfortable with, and a scripting engine ready to empower mission-critical multithreaded applications.

    Falcon provides six integrated programming paradigms: procedural, object oriented, prototype oriented, functional, tabular and message oriented. And you don't have to master all of them; you just need to pick the ingredients you prefer, and let the code to follow your inspiration.

    Falcon is like...
    // International class; name and street
    class 国際( なまえ, Straße )
       // set class name and street address
       नाम = なまえ
       شَارِع   =  Straße
       // Say who am I!
       function 言え!()
         >@"I am $(self.नाम) from ",self.شَارِع
       end
    end
    // all the people of the world!
    民族 = [ 国際( "高田 Friederich", "台湾" ),
       国際( "Smith Σωκράτης", "Cantù" ),
       国際( "Stanisław Lec", "południow" ) ]
    
    for garçon in 民族: garçon.言え!()
    
    Get started
    Join now!
    Start a project

    Partners

    SunOS Open Source community distro. They help Falcon and many other Open Source projects in moving to the Sun platforms.
    Kross scripting framework for KDE. Kross integrates Falcon and other scripting languages into KDE4; they are helping us in developing better bindings for foreign applications.
    AuroraUX - Open source Solaris distro that has chosen Falcon as main scripting language.
    Appcelerator's Titanium - An application development framework that uses HTML for GUI and scripting engines to manage the application logic.

    News

    Stateful objects in upcoming 0.9.6
    Posted by jonnymind on 2009-11-15
    Our development team has just committed the stateful object patch. Read about this new feature...
    Released version 0.9.4.4
    Posted by jonnymind on 2009-10-19
    A new bugfix release is ready. Go and get it!
    Falcon ranking up on Tiobe
    Posted by jonnymind on 2009-10-13
    It's the first time Falcon enters Tiobe Programming Community ranking of the TOP 50 languages in the world.
    Upcoming version 0.9.4.4
    Posted by jonnymind on 2009-10-11
    The new version is being readied in this hours. It's a maintenance version, with several relevant bugixes and, again, another small surprise...
    Eagle bugfix released
    Posted by jonnymind on 2009-09-22
    Version 0.9.4.2 - Eagle (bugfix) has been released. And it brings a couple of surprises.
    Falcon in Titanium
    Posted by jonnymind on 2009-09-19
    The Falcon Programming Language is currently being added to the list of supported language in the Titanium application framework.
    Falcon 0.9.4.2 on the go
    Posted by jonnymind on 2009-09-13
    We are preparing a bugfix release, with a couple of nice surprises.
    LibGD binding released
    Posted by jonnymind on 2009-08-30
    A long awaited feature is now available: LibGD version 2, the coolest web-oriented image manipulation library is now supported by a Falcon Module.
    Important notice on Threading
    Posted by jonnymind on 2009-08-22
    Recently, a couple of users reported a problem with a simple threading based program. On this regard, please read this notice.
    Eagle released
    Posted by jonnymind on 2009-08-11
    Falcon 0.9.4 (Eagle) has been released.

    Elapsed time: 0.039 secs. (VM time 0.030 secs.)
    tests/native/curl/send.fal000066400000000000000000000044661176363201700161100ustar00rootroot00000000000000/* FALCON PROGRAMMING LANGUAGE CURL library binding - Samples send.fal - Shows how to send a file to a remote server USAGE: send.fal [-u ] [-p ] [-r ] [-?] */ import from curl try Options.parse() if Options.url == nil or Options.file == nil Options.usage() exit(0) end catch StringType exit(1) end try is = InputStream( Options.file ) h = curl.Handle( URI.encode(Options.url) ) // we want to receive the data as a stream. h.setInStream( is ) h.setOption( curl.CURLOPT.UPLOAD, true ) if Options.user: h.setOption( curl.CURLOPT.USERNAME, Options.user ) if Options.password: h.setOption( curl.CURLOPT.PASSWORD, Options.password ) // if we want to rename the file after it is copied, use FTP quote if Options.newname uri = URI( Options.url ) quoted = [ "RNFR " + uri.path, "RNTO " + Options.newname ] h.setOption( curl.CURLOPT.POSTQUOTE, quoted ) end > "Starting transfer; reading file ", args[1] h.exec() > "Transfer complete." is.close() h.cleanup() return 0 catch curl.CurlError in e > "ERROR!" > "Received the follwing error:" > e return 1 catch IoError in e > "Error in creating the output file: " > e return 1 end object Options from CmdlineParser url = nil file = nil newname = nil user = nil password = nil function onOption( option ) switch option case "?", "help" self.usage() case "u", "p", "r" self.expectValue() default self.unrecognized( option ) end end function onValue( option, value ) switch option case "p" self.password = value case "u" self.user = value case "r" self.newname = value end end function onFree( param ) if self.url == nil self.url = param elif self.file == nil self.file = param else self.usage() raise "broken" end end function unrecognized( option ) > "Unrecognized option: ", option self.usage() raise "broken" end function usage() > "USAGE: send.fal [-u ] [-p ] [-?] " > end end tests/native/dbi/000077500000000000000000000000001176363201700142525ustar00rootroot00000000000000tests/native/dbi/tests/000077500000000000000000000000001176363201700154145ustar00rootroot00000000000000tests/native/dbi/tests/samples/000077500000000000000000000000001176363201700170605ustar00rootroot00000000000000tests/native/dbi/tests/samples/dbi_odbc.fal000066400000000000000000000025501176363201700212730ustar00rootroot00000000000000/* FALCON - DBI - Samples FILE: dbi_odbc.fal New DBI smoke test -- ODBC connection Create a DNS named "falcondbi" prior running this test. Then, it should simply run out of the box :-) ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) */ import from dbi db = dbi.connect( "odbc:dsn=falcondbi" ) db.query( "drop table test_table" ) db.query( "create table test_table( name char(20), intprice integer, price double, descr varchar(60), today timestamp )" ) db.query( "insert into test_table(name, intprice, price, descr, today) values(?,?,?,?,?)", 'hello world', 100, 12.13, "Ciao!", CurrentTime() ) stmt = db.prepare( "insert into test_table(name, intprice, price, descr, today ) values(?,?,?,?,?)" ) vals = .[ .["first" nil nil nil nil ] .[ "second" 150 nil "A nice text -è-!" nil ] .[ "third" 10 1234.41234 "Again a text" nil ] .[ "fourth" 11 123.2 "still a text" CurrentTime() ] ] count = 0 for value in vals (.[stmt.execute ] + value)() end db.commit() > "Done step 1" rec = db.query( "select * from test_table" ) while data = rec.fetch([=>]) for k, v in data > k, " = >", v.toString().trim(), "< / ", v.len() end > "===" end tests/native/dbi/tests/samples/ndbi.fal000066400000000000000000000021111176363201700204530ustar00rootroot00000000000000/* FALCON - DBI - Samples FILE: ndbi.fal New DBI smoke test. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) */ import from dbi dbh = dbi.DBIConnect( " mysql: uid=falcon; db=testdb") dbh.options("strings=on;prefetch=none") dbh.query("BEGIN") //dbh.query( " delete from test_table" ) dbh.query( " insert into test_table( f_chr, f_str, f_text, f_blob, f_int, f_double, f_dec ) values (?,?,?, ?,?,?, ?)", "Hello", "World", "here", "Some binary data", 100, 23.4, "123.2" ) stmt = dbh.prepare( " insert into test_table( f_chr, f_str, f_text, f_blob, f_int, f_double, f_dec ) values (?,?,?, ?,?,?, ?)") stmt.execute( "Hello2", "World2", "here2", "Some binary data2", 101, 24.4, "123.22" ) r = dbh.query( "select * from test_table" ) dict = r.fetch( [=>] ) inspect( dict ) r.do( { n => > n.describe() }, [] ) dbh.perform( "rollback" ) dbh.close() tests/native/dbi/tests/samples/ndbi2.fal000066400000000000000000000012001176363201700205330ustar00rootroot00000000000000/* FALCON - DBI - Samples FILE: ndbi2.fal New DBI smoke test. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) */ load dbi dbh = DBIConnect( " mysql: uid=root; pwd=my%pc; db=TicketDB") r = dbh.call( "sp_component_insert(?, ?)", "Un'altro modulo", "Un'altra descrizione") > "RETURNED: " + (v = r.fetch([=>])).describe() dbh.query( "select * from tModules" ).do( {d=> >d.describe()}, [=>] ) dbh.close() tests/native/dbi/tests/samples/pgsql.fal000066400000000000000000000037251176363201700207010ustar00rootroot00000000000000/* FALCON - DBI - Samples FILE: ndbi.fal DBI/PgSQL test sample ------------------------------------------------------------------- Author: Stanislais Marquis Begin: ------------------------------------------------------------------- (C) Copyright 2010: the FALCON developers (see list in AUTHORS file) */ import from dbi // Your connection info _connInfo = "pgsql:dbname=testdb user=testuser password=testpassword" // Some queries to execute _dropTableStr = ' DROP TABLE IF EXISTS FalconTestTable;' _createTableStr = ' CREATE TABLE FalconTestTable ( idx serial primary key, name varchar(128) not null, amount numeric not null default 0, comment text default null );' _insertRowStr = ' INSERT INTO FalconTestTable ( name, amount, comment ) VALUES ( ?,?,? );' _rows = [ [ "Someone 1", 123, "Some comment." ], [ "Someone 2", 321, "Another one." ], [ "Someone 3", 111, nil ] ] _rows_2 = [ [ "Someone 4", 222.22, "Hello postgres!" ], [ "Someone 5", 333.33, "Nice amount" ], [ "Someone 6", 0, "Poor guy" ] ] _selectAllStr = 'SELECT * FROM FalconTestTable' // Let's test > "-- Connecting..." dbh = dbi.connect( _connInfo ) > "-- Drop table..." dbh.query( _dropTableStr ) > "-- Create table..." dbh.query( _createTableStr ) > "-- Prepare a statement and insert 3 rows..." stmt = dbh.prepare( _insertRowStr ) i = 0 for row in _rows > i++ stmt.execute( row[0], row[1], row[2] ) end // This is a pgsql driver specific function. > "-- Prepare a named statement and insert 3 rows..." stmt = dbh.prepareNamed( "StoredProc", _insertRowStr ) for row in _rows_2 > i++ stmt.execute( row[0], row[1], row[2] ) end > "-- Select all..." res = dbh.query( _selectAllStr ) i = 0 while i < res.getRowCount() row = res.fetch() j = 0 for colName in res.getColumnNames() >> @"$(colName)= $(row[j]) | " ++j end >> "\n" ++i end > "-- Finally drop table..." dbh.query( _dropTableStr ) tests/native/dbi/tests/testsuite/000077500000000000000000000000001176363201700174455ustar00rootroot00000000000000tests/native/dbi/tests/testsuite/config.fal000066400000000000000000000001571176363201700214010ustar00rootroot00000000000000 mysql_conn_str = " mysql: host=localhost; uid=faltest; pwd=faltest; db=testsuite" tests/native/dbi/tests/testsuite/mysql_binblob.fal000066400000000000000000000023161176363201700227670ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 24a * Category: mysql * Subcategory: * Short: MySQL record insertion with binary data, and checj * Description: * Insert one position * -- USES the table created by the first test * [/Description] * ****************************************************************************/ import from dbi import mysql_conn_str from config try conn = dbi.connect( config.mysql_conn_str ) mb = MemBuf(256) for i = 0 to 255: mb[i] = i conn.query(" insert into TestTable( vint, fixchar, vchar, tblob, bblob, number, ts ) values( ?, ?, ?, ?, ?, ?, ? )", 100, "fixchar", "varchar", "New data for text blob", mb, 24.45, CurrentTime() ) conn.commit() blob = conn.query( "select bblob from TestTable where vint = 100" ).fetch([])[0]; if blob.typeId() != MemBufType: failure( "Type -- should be a membuf" ) if blob.len() != 256: failure( "Blob length " + blob.len() ) for i = 0 to 255 if blob[i] != mb[i]: failure( "Different content in blob: " + i ) end conn.close() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/mysql_create.fal000066400000000000000000000026521176363201700226260ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 20a * Category: mysql * Subcategory: * Short: MySQL table creation * Description: * Tests table creation in MySQL. The table is then used in all the MySQL * category to perform further tests, so this script should be called before * the other tests are performed * [/Description] * ****************************************************************************/ import from dbi import mysql_conn_str from config try conn = dbi.connect( config.mysql_conn_str ) // Creating a table with ALL the basic datatypes known by the engine. // Interpolated datatypes are not necessary in our tests. conn.query(" create table TestTable( kv INTEGER PRIMARY KEY AUTO_INCREMENT, vint INTEGER, fixchar char(25), vchar varchar(25), tblob TEXT, bblob BLOB, number REAL, ts TimeStamp ) ENGINE=InnoDB" ) conn.query(" create procedure getData( IN v_first integer, IN v_last integer ) BEGIN select kv, vint, fixchar, vchar, tblob, bblob, number, ts from TestTable where vint >= v_first and vint < v_last; END ") conn.commit() conn.close() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/mysql_drop.fal000066400000000000000000000013341176363201700223230ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 29 * Category: mysql * Subcategory: * Short: MySQL drop table * Description: * Drops the sqlite test table, resetting the test status. * -- USES the table created by the first test * [/Description] * ****************************************************************************/ import from dbi import mysql_conn_str from config try GC.perform(true) conn = dbi.connect( config.mysql_conn_str ) conn.query("drop table TestTable") conn.query("drop procedure getData") conn.commit() conn.close() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/mysql_insert_one.fal000066400000000000000000000015101176363201700235200ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 20b * Category: mysql * Subcategory: * Short: MySQL direct record insertion. * Description: * Inserts a single record via a complete SQL statement. * -- USES the table created by the first test * [/Description] * ****************************************************************************/ import from dbi import mysql_conn_str from config try conn = dbi.connect( config.mysql_conn_str ) conn.query(" insert into TestTable( vint, fixchar, vchar, tblob, bblob, number, ts ) values( 1, 'Fixed char', 'var char', 'A textual blob', 'A binary blob', 101.243, Now() ) ") conn.commit(); conn.close(); success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/mysql_insert_one_pos.fal000066400000000000000000000015601176363201700244060ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 20c * Category: mysql * Subcategory: * Short: MySQL indirect record insertion * Description: * Insert one position * -- USES the table created by the first test * [/Description] * ****************************************************************************/ import from dbi import mysql_conn_str from config try conn = dbi.connect( config.mysql_conn_str ) mb = MemBuf(10) for i = 0 to 9: mb[i] = i+65 conn.query(" insert into TestTable( vint, fixchar, vchar, tblob, bblob, number, ts ) values( ?, ?, ?, ?, ?, ?, ? )", 2, "fixchar", "varchar", "New data for text blob", mb, 24.45, CurrentTime() ) conn.commit() conn.close() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/mysql_insert_pstat.fal000066400000000000000000000021161176363201700240750ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 20d * Category: mysql * Subcategory: * Short: MySQL prepared statement * Description: * Inserts multiple records using a prepared statement. * -- USES the table created by the first test * [/Description] * ****************************************************************************/ import from dbi import mysql_conn_str from config data = [] for i in [0:10] mb = MemBuf(10) for j = 0 to 9: mb[j] = (j+i+65) t = CurrentTime() t.second = i data += [[ i+10, "fix char " + i, "varchar " + i, "Text blob " + i, mb, 24589.21345/(i+1), t ]] end try conn = dbi.connect( config.mysql_conn_str ) stmt = conn.prepare(" insert into TestTable( vint, fixchar, vchar, tblob, bblob, number, ts ) values( ?, ?, ?, ?, ?, ?, ? )" ) for elem in data (.[ stmt.execute ] + elem)() end stmt.close() conn.commit() conn.close() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/mysql_lastkey.fal000066400000000000000000000017301176363201700230330ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 23b * Category: mysql * Subcategory: * Short: MySQL automatic ID insertion * Description: * Verifies that the system is able to detect the last ID generated in the * current transaction. * [/Description] * ****************************************************************************/ import from dbi import mysql_conn_str from config try conn = dbi.connect( config.mysql_conn_str, "autocommit=off" ) conn.begin() conn.query( " insert into TestTable( tblob, number) values( 'The desired value', 10.3 )" ) rs = conn.query( "select tblob from TestTable where kv=?", conn.getLastID() ) if rs.fetch([])[0] != 'The desired value' failure( "Fetched another record" ) end rs.close() conn.commit() conn.close() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/mysql_query_call.fal000066400000000000000000000035371176363201700235260ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 21d * Category: mysql * Subcategory: * Short: MySQL query on procedure * Description: * Queries the recordset of a stored procedure. * -- USES the table created by the first test and the data from test 20d * [/Description] * ****************************************************************************/ import from dbi import mysql_conn_str from config data = [] for i in [0:10] mb = MemBuf(10) for j = 0 to 9: mb[j] = (j+i+65) t = CurrentTime() t.second = i data += [[ 0, i+10, "fix char " + i, "varchar " + i, "Text blob " + i, mb, 24589.21345/(i+1), t ]] end try conn = dbi.connect( config.mysql_conn_str ) rs = conn.query( "call getData(?, ?)", 10, 20 ) if rs.getColumnCount() != 8 failure( "Can't get columns: " + rs.getColumnCount() ) end if rs.getRowCount() != 10 failure( "Can't get row count: " + rs.getRowCount() ) end // fetch array arr = [] count = 0 while rs.fetch( arr ) if arr[1] != data[count][1] or arr[2] != data[count][2] or \ arr[3] != data[count][3] or arr[6] != data[count][6] or \ arr[7].year != data[count][7].year failure( "Consistency check at step " + count ) end ++count end if count != 10 failure( "Fetched records " + count ) end rs.close() // try to query again rs = conn.query( "select * from TestTable" ) if rs.getColumnCount() == 0 failure( "Can't re-seek the table" ) end // fetch all count = 0 while rs.fetch( arr ) ++count end if count != rs.getRowCount() failure( "Problems fetching after call" ) end conn.close() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/mysql_query_multiple.fal000066400000000000000000000037171176363201700244460ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 21c * Category: mysql * Subcategory: * Short: MySQL query multiple * Description: * Performs a simple query that should return 10 records * -- USES the table created by the first test and the data from test 20d * [/Description] * ****************************************************************************/ import from dbi import mysql_conn_str from config data = [] for i in [0:10] mb = MemBuf(10) for j = 0 to 9: mb[j] = (j+i+65) t = CurrentTime() t.second = i data += [[ 0, i+10, "fix char " + i, "varchar " + i, "Text blob " + i, mb, 24589.21345/(i+1), t ]] end try conn = dbi.connect( config.mysql_conn_str ) rs = conn.query( " select kv, vint, fixchar, vchar, tblob, bblob, number, ts from TestTable where vint >= ? and vint < ?", 10, 20 ) if rs.getColumnCount() != 8 failure( "Can't get columns: " + rs.getColumnCount() ) end if rs.getRowCount() != 10 failure( "Can't get row count: " + rs.getRowCount() ) end // fetch array arr = [] count = 0 while rs.fetch( arr ) if arr[1] != data[count][1] or arr[2] != data[count][2] or \ arr[3] != data[count][3] or arr[6] != data[count][6] or \ arr[7].year != data[count][7].year failure( "Consistency check at step " + count ) end ++count end if count != 10 failure( "Fetched records " + count ) end rs.close() // try to query again rs = conn.query( "select * from TestTable" ) if rs.getColumnCount() == 0 failure( "Can't re-seek the table" ) end // fetch all count = 0 while rs.fetch( arr ) ++count end if count != rs.getRowCount() failure( "Problems fetching after select" ) end rs.close() conn.close() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/mysql_query_opt.fal000066400000000000000000000025571176363201700234160ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 22a * Category: mysql * Subcategory: * Short: MySQL query with option * Description: * Performs a simple query with special options for fetching only strings * -- USES the table created by the first test and the data fromd test 10b * [/Description] * ****************************************************************************/ import from dbi import mysql_conn_str from config try conn = dbi.connect( config.mysql_conn_str, "strings=on" ) rs = conn.query("select kv, tblob, number, ts from TestTable where kv = 1") if rs.getColumnCount() != 4 failure( "Can't get columns: " + rs.getColumnCount() ) end // sqlite doesn't return the count of rows if rs.getRowCount() != 1 failure( "Can't get row count: " + rs.getRowCount() ) end // fetch array -- with all strings arr = rs.fetch( [] ) if arr.len() != 4: failure( "Fetched columns number" ) if arr[0] != "1": failure( "Value of field 1" ) if arr[1] != 'A textual blob': failure( "Value of field 2" ) if arr[2] != "101.243": failure( "Value of field 3" ) if arr[3].len() != 19 or arr[3][0] != "2": failure( "Value of field 4" ) rs.close() conn.close() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/mysql_query_simple.fal000066400000000000000000000037511176363201700241020ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 21b * Category: mysql * Subcategory: * Short: MySQL query simple * Description: * Performs a simple query that should return exactly one record * -- USES the table created by the first test and the data from test 20b * [/Description] * ****************************************************************************/ import from dbi import mysql_conn_str from config try conn = dbi.connect( config.mysql_conn_str ) rs = conn.query(" select kv, vint, fixchar, vchar, tblob, bblob, number, ts from TestTable where kv = 1") if rs.getColumnCount() != 8 failure( "Can't get columns: " + rs.getColumnCount() ) end if rs.getRowCount() != 1 failure( "Can't get row count: " + rs.getRowCount() ) end // fetch array arr = rs.fetch( [] ) if arr.len() != 8: failure( "Fetched columns number" ) if arr[0] != 1: failure( "Value of field 1" ) if arr[1] != 1: failure( "Value of field 2" ) if arr[2] != 'Fixed char': failure( "Value of field 3" ) if arr[3] != 'var char': failure( "Value of field 4" ) if arr[4] != 'A textual blob': failure( "Value of field 5" ) if arr[5].typeId() != MemBufType: failure( "Value of field 6" ) if arr[6] != 101.243: failure( "Value of field 7" ) if not arr[7].derivedFrom("TimeStamp"): failure( "Value of field 8" ) // same thing with a dictionary rs = conn.query("select * from TestTable where kv = 1") dict = rs.fetch( [=>] ) if dict.len() != 8: failure( "Fetched columns number - dict" ) if dict['kv'] != 1: failure( "Dict - Value of field 1" ) if dict['tblob'] != 'A textual blob': failure( "Dict - Value of field 2" ) if dict['bblob'].typeId() != MemBufType: failure( "Dict - Value of field 3" ) if dict['number'] != 101.243: failure( "Dict - Value of field 4" ) success() GC.perform(true) catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/mysql_query_simple_neg.fal000066400000000000000000000020201176363201700247170ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 21a * Category: mysql * Subcategory: * Short: MySQL query simple -- negative * Description: * Performs a simple query that shouldn't return any record * -- USES the table created by the first test and the data from test 10b * [/Description] * ****************************************************************************/ import from dbi import mysql_conn_str from config try conn = dbi.connect( config.mysql_conn_str ) rs = conn.query("select * from TestTable where kv = -1") if rs.getColumnCount() != 8 failure( "Can't get columns: " + rs.getColumnCount() ) end // We expect 0 records if rs.getRowCount() != 0 failure( "Can't get row count: " + rs.getRowCount() ) end // fetch array arr = rs.fetch( [] ) if arr != nil: failure( "Received data" ) rs.close() conn.close() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/mysql_query_table.fal000066400000000000000000000026271176363201700237010ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: -- * Category: mysql * Subcategory: * Short: MySQL query table * Description: * Checks fetching all the data on a table. * -- USES the table created by the first test and the data from test 20d * [/Description] * ****************************************************************************/ import from dbi import mysql_conn_str from config data = [] for i in [0:10] mb = MemBuf(10) for j = 0 to 9: mb[j] = (j+i+65) t = CurrentTime() t.second = i data += [[ 0, i+10, "fix char " + i, "varchar " + i, "Text blob " + i, mb, 24589.21345/(i+1), t ]] end try conn = dbi.connect( config.mysql_conn_str ) rs = conn.query( " select kv, vint, fixchar, vchar, tblob, bblob, number, ts from TestTable where vint >= ? and vint < ?", 10, 20 ) // fetch array table = rs.fetch( Table() ) count = 0 for arr in table if arr[1] != data[count][1] or arr[2] != data[count][2] or \ arr[3] != data[count][3] or arr[6] != data[count][6] or \ arr[7].year != data[count][7].year failure( "Consistency check at step " + count ) end ++count end if count != 10 failure( "Fetched records " + count ) end success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/mysql_rollback.fal000066400000000000000000000015471176363201700231560ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 23a * Category: mysql * Subcategory: * Short: Checks connection rollback * Description: * Verifies that an insert is correctly rolled back. * [/Description] * ****************************************************************************/ import from dbi import mysql_conn_str from config try conn = dbi.connect( config.mysql_conn_str, "autocommit=off" ) conn.begin() conn.query( " insert into TestTable( kv, tblob, number) values( 100, 'A rolled back value', 10.3 )" ) conn.rollback() rs = conn.query( "select * from TestTable where kv = 100" ) if rs.fetch([]) != nil failure( "Some data was returned" ) end success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/sqlite_create.fal000066400000000000000000000017611176363201700227620ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 10a * Category: sqlite * Subcategory: * Short: SQLite table creation * Description: * Tests table creation in SQLite. The table is then used in all the sqlite * category to perform further tests, so this script should be called before * the other tests are performed * [/Description] * ****************************************************************************/ import from dbi try conn = dbi.connect( "sqlite3:db=testsuite.db;create=always" ) // Creating a table with ALL the basic datatypes known by the engine. // Interpolated datatypes are not necessary in our tests. conn.query(" create table TestTable( key INTEGER PRIMARY KEY, tblob TEXT, bblob BLOB, number REAL) ") conn.commit() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/sqlite_drop.fal000066400000000000000000000012311176363201700224530ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 19 * Category: sqlite * Subcategory: * Short: SQLite drop table * Description: * Drops the sqlite test table, resetting the test status. * -- USES the table created by the first test * [/Description] * ****************************************************************************/ import from dbi try GC.perform(true) conn = dbi.connect( "sqlite3:db=testsuite.db" ) conn.query("drop table TestTable") conn.commit() conn.close() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/sqlite_insert_one.fal000066400000000000000000000013651176363201700236640ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 10b * Category: sqlite * Subcategory: * Short: SQLite direct record insertion. * Description: * Inserts a single record via a complete SQL statement. * -- USES the table created by the first test * [/Description] * ****************************************************************************/ import from dbi try conn = dbi.connect( "sqlite3:db=testsuite.db" ) conn.query(" insert into TestTable( key, tblob, bblob, number ) values( 1, 'A textual blob', 'A binary blob', 101.243 ) ") conn.commit(); conn.close(); success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/sqlite_insert_one_pos.fal000066400000000000000000000014631176363201700245440ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 10c * Category: sqlite * Subcategory: * Short: SQLite indirect record insertion * Description: * Inserts a single using positional markers. * -- USES the table created by the first test * [/Description] * ****************************************************************************/ import from dbi try conn = dbi.connect( "sqlite3:db=testsuite.db" ) mb = MemBuf(10) for i = 0 to 9: mb[i] = i+65 conn.query(" insert into TestTable( key, tblob, bblob, number ) values( ?, ?, ?, ? )", 2, "New data for text blob", mb, 24.45 ) conn.commit() conn.close() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/sqlite_insert_pstat.fal000066400000000000000000000016031176363201700242310ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 10d * Category: sqlite * Subcategory: * Short: SQLite prepared statement * Description: * Inserts multiple records using a prepared statement. * -- USES the table created by the first test * [/Description] * ****************************************************************************/ import from dbi data = [] for i in [0:10] mb = MemBuf(10) for j = 0 to 9: mb[j] = (j+i+65) data.add([ i+10, "Text blob " + i, mb, 24589.21345/(i+1)]) end try conn = dbi.connect( "sqlite3:db=testsuite.db" ) stmt = conn.prepare(" insert into TestTable( key, tblob, bblob, number ) values( ?, ?, ?, ? )") for elem in data (.[ stmt.execute ] + elem)() end success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/sqlite_lastkey.fal000066400000000000000000000017031176363201700231670ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 13b * Category: sqlite * Subcategory: * Short: Checks automatic ID insertion * Description: * Verifies that the system is able to detect the last ID generated in the * current transaction. * [/Description] * ****************************************************************************/ import from dbi try GC.perform(true) // enable strings on conn = dbi.connect( "sqlite3:db=testsuite.db", "autocommit=off" ) conn.begin() conn.query( " insert into TestTable( tblob, number) values( 'The desired value', 10.3 )" ) rs = conn.query( "select tblob from testtable where key=?", conn.getLastID() ) if rs.fetch([])[0] != 'The desired value' failure( "Fetched another record" ) end success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/sqlite_query_multiple.fal000066400000000000000000000027661176363201700246050ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 11c * Category: sqlite * Subcategory: * Short: SQLite query multiple * Description: * Performs a simple query that should return 10 records * -- USES the table created by the first test and the data from test 10d * [/Description] * ****************************************************************************/ import from dbi data = [] for i in [0:10] mb = MemBuf(10) for j = 0 to 9: mb[j] = (j+i+65) data += [[ i+10, "Text blob " + i, mb, 24589.21345/(i+1)]] end try conn = dbi.connect( "sqlite3:db=testsuite.db" ) rs = conn.query( "select key, tblob, bblob, number from TestTable where key >= ? and key < ?", 10, 20 ) if rs.getColumnCount() != 4 failure( "Can't get columns: " + rs.getColumnCount() ) end // sqlite doesn't return the count of rows if rs.getRowCount() != -1 failure( "Can't get row count: " + rs.getRowCount() ) end // fetch array arr = [] count = 0 while rs.fetch( arr ) // Sqlite will return binary blobs as strings if arr[0] != data[count][0] or arr[1] != data[count][1] or \ arr[3] != data[count][3] failure( "Consistency check at step " + count ) end ++count end if count != 10 failure( "Fetched records " + count ) end rs.close() conn.close() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/sqlite_query_opt.fal000066400000000000000000000026201176363201700235410ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 12a * Category: sqlite * Subcategory: * Short: SQLite query with option * Description: * Performs a simple query with special options for * -- USES the table created by the first test and the data fromd test 10b * [/Description] * ****************************************************************************/ import from dbi try // enable strings on conn = dbi.connect( "sqlite3:db=testsuite.db", "strings=on" ) // rs = conn.query("select * from TestTable where key = ?", 1) rs = conn.query("select * from TestTable where tblob = ?", 'A textual blob') if rs.getColumnCount() != 4 failure( "Can't get columns: " + rs.getColumnCount() ) end // sqlite doesn't return the count of rows if rs.getRowCount() != -1 failure( "Can't get row count: " + rs.getRowCount() ) end // fetch array -- with all strings arr = rs.fetch( [] ) if arr.len() != 4: failure( "Fetched columns number" ) if arr[0] != "1": failure( "Value of field 1" ) if arr[1] != 'A textual blob': failure( "Value of field 2" ) if arr[2] != 'A binary blob': failure( "Value of field 3" ) if arr[3] != "101.243": failure( "Value of field 4" ) rs.close() conn.close() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/sqlite_query_simple.fal000066400000000000000000000032701176363201700242320ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 11b * Category: sqlite * Subcategory: * Short: SQLite query simple * Description: * Performs a simple query that should return exactly one record * -- USES the table created by the first test and the data from test 10b * [/Description] * ****************************************************************************/ import from dbi try conn = dbi.connect( "sqlite3:db=testsuite.db" ) rs = conn.query("select * from TestTable where key = 1") if rs.getColumnCount() != 4 failure( "Can't get columns: " + rs.getColumnCount() ) end // sqlite doesn't return the count of rows if rs.getRowCount() != -1 failure( "Can't get row count: " + rs.getRowCount() ) end // fetch array arr = rs.fetch( [] ) if arr.len() != 4: failure( "Fetched columns number" ) if arr[0] != 1: failure( "Value of field 1" ) if arr[1] != 'A textual blob': failure( "Value of field 2" ) if arr[2] != 'A binary blob': failure( "Value of field 3" ) if arr[3] != 101.243: failure( "Value of field 4" ) // same thing with a dictionary rs = conn.query("select * from TestTable where key = 1") dict = rs.fetch( [=>] ) if dict.len() != 4: failure( "Fetched columns number - dict" ) if dict['key'] != 1: failure( "Value of field 1" ) if dict['tblob'] != 'A textual blob': failure( "Value of field 2" ) if dict['bblob'] != 'A binary blob': failure( "Value of field 3" ) if dict['number'] != 101.243: failure( "Value of field 4" ) rs.close() conn.close() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/sqlite_query_simple_neg.fal000066400000000000000000000020121176363201700250540ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 11a * Category: sqlite * Subcategory: * Short: SQLite query simple -- negative * Description: * Performs a simple query that shouldn't return any record * -- USES the table created by the first test and the data from test 10b * [/Description] * ****************************************************************************/ import from dbi try conn = dbi.connect( "sqlite3:db=testsuite.db" ) rs = conn.query("select * from TestTable where key = -1") if rs.getColumnCount() != 4 failure( "Can't get columns: " + rs.getColumnCount() ) end // sqlite doesn't return the count of rows if rs.getRowCount() != -1 failure( "Can't get row count: " + rs.getRowCount() ) end // fetch array arr = rs.fetch( [] ) if arr != nil: failure( "Received data" ) rs.close() conn.close() success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/sqlite_query_table.fal000066400000000000000000000024111176363201700240240ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: -- * Category: sqlite * Subcategory: * Short: SQLite read table. * Description: * Creates a table with a signle fetch * -- USES the table created by the first test and the data from test 10d * -- Currently not used (table fetch disabled till next version). * [/Description] * ****************************************************************************/ import from dbi data = [] for i in [0:10] mb = MemBuf(10) for j = 0 to 9: mb[j] = (j+i+65) data += [[ i+10, "Text blob " + i, mb, 24589.21345/(i+1)]] end try conn = dbi.connect( "sqlite3:db=testsuite.db" ) rs = conn.query("select key, tblob, bblob, number from TestTable where key >= 10 and key < 20") // fetch table table = rs.fetch( Table() ) count = 0 for arr in table // Sqlite will return binary blobs as strings if arr[0] != data[count][0] or arr[1] != data[count][1] or \ arr[3] != data[count][3] failure( "Consistency check at step " + count ) end ++count end if count != 10 failure( "Fetched records " + count ) end success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/sqlite_rollback.fal000066400000000000000000000015521176363201700233060ustar00rootroot00000000000000/**************************************************************************** * Falcon test suite -- DBI tests * * * ID: 13a * Category: sqlite * Subcategory: * Short: Checks connection rollback * Description: * Verifies that an insert is correctly rolled back. * [/Description] * ****************************************************************************/ import from dbi try // enable strings on conn = dbi.connect( "sqlite3:db=testsuite.db", "autocommit=off" ) conn.begin() conn.query( " insert into TestTable( key, tblob, number) values( 100, 'A rolled back value', 10.3 )" ) conn.rollback() rs = conn.query( "select * from TestTable where key = 100" ) if rs.fetch([]) != nil failure( "Some data was returned" ) end success() catch dbi.DBIError in error failure( "Received a DBI error: " + error ) end tests/native/dbi/tests/testsuite/testsuite_mysql.sql000066400000000000000000000014211176363201700234420ustar00rootroot00000000000000/******************************************************* * Falcon DBI - testsuite * * * Script creating the testsuite database and the faltest * user in the MySQL database for the DBI testsuite. * * Run with * $ mysql -u root -p < testsuite_mysql.sql * */ create database IF NOT EXISTS testsuite character set utf8; use testsuite; DROP PROCEDURE IF EXISTS add_user_if_not_exist; DELIMITER $$ CREATE PROCEDURE add_user_if_not_exist() BEGIN DECLARE foo BIGINT DEFAULT 0 ; SELECT COUNT(*) INTO foo FROM `mysql`.`user` WHERE `User` = 'faltest' ; IF foo = 0 THEN CREATE USER faltest IDENTIFIED BY 'faltest'; GRANT all on * to faltest; END IF; END ;$$ DELIMITER ; CALL add_user_if_not_exist() ; DROP PROCEDURE IF EXISTS add_user_if_not_exist; tests/native/dbus/000077500000000000000000000000001176363201700144515ustar00rootroot00000000000000tests/native/dbus/samples/000077500000000000000000000000001176363201700161155ustar00rootroot00000000000000tests/native/dbus/samples/dbus_receive_signal.fal000066400000000000000000000031451176363201700226000ustar00rootroot00000000000000/* FALCON - Samples FILE: dbus_receive_signal.fal A minimal example that catch the signals sent from dbus_signal.fal ------------------------------------------------------------------- Author: Enrico Lumetti Begin: Sun, 22 Aug 2010 14:52 +0200 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load dbus // the two functions are called when the dispatcher catch the respective signal function Test() > "Test signal received!" end function Testp( params ) > "Testp signal received with args:" inspect( params ) end try // Get the connection conn = DBus() // Add a match for signals from opt.Falcon.tests.dbus_signal conn.addMatch( "type='signal',interface='opt.Falcon.tests.dbus_signal'" ) // We add a filter on signals Test and Testp and set an handler to each of them. // During the dispatching, the function will check the interface and the name // of the signal and will call the respective handler. conn.addFilter( "opt.Falcon.tests.dbus_signal", "Test", Test, true ); conn.addFilter( "opt.Falcon.tests.dbus_signal", "Testp", Testp, true ) > "Test started." // start the parallel dispatching conn.startDispatch() stdin = stdIn() loop end stdin.readAvailable( 0.5 ) // if the user press a key, stop the loop stdin.grabLine() // just flush the input conn.stopDispatch() > "Test complete" catch DBusError in e > "Test failed. DBus subsystem reported the following error: " > > e end tests/native/dbus/samples/dbus_signal.fal000066400000000000000000000021251176363201700210730ustar00rootroot00000000000000/* FALCON - Samples FILE: dcop_signal.fal Show a minimal DCOP sample signaling an object. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sat, 20 Dec 2008 15:36:59 +0100 ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load dbus try conn = DBus() // First without parameters > "Sending a parameterless signal on /opt/Falcon/tests/dbus_signal - Test()" conn.signal( "/opt/Falcon/tests/dbus_signal", "opt.Falcon.tests.dbus_signal", "Test" ) // And then with some params > "Sending a signal" > "Sending a parameter signal on /opt/Falcon/tests/dbus_signal - Testp(1, 2.5, \"Hello world\")" conn.signal( "/opt/Falcon/tests/dbus_signal", "opt.Falcon.tests.dbus_signal", "Testp", 1, 2.5, "Hello world" ) > "Test complete" catch DBusError in e > "Test failed. DBus subsystem reported the following error: " > > e end tests/native/dynlib/000077500000000000000000000000001176363201700147755ustar00rootroot00000000000000tests/native/dynlib/CMakeLists.txt000066400000000000000000000013301176363201700175320ustar00rootroot00000000000000#################################################################### # The Falcon Programming Language # # CMake configuration file for dynlib - TESTS #################################################################### cmake_minimum_required(VERSION 2.6) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${falcon_INCLUDE_DIRS} ) add_library( test_dynlib MODULE testsrc/test_dynlib.c ) # dynlib goes in the testsuite source.# this is because we need it for our # tests. set_target_properties(test_dynlib PROPERTIES PREFIX "" LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/testsuite ) if(APPLE) set_target_properties(test_dynlib PROPERTIES SUFFIX ".dylib" ) endif() add_subdirectory(testsuite) tests/native/dynlib/samples/000077500000000000000000000000001176363201700164415ustar00rootroot00000000000000tests/native/dynlib/samples/testdialog.fal000066400000000000000000000027121176363201700212660ustar00rootroot00000000000000/* FALCON - Samples for the DynLib dynamic library FILE: testdialog.fal Test a file open box under MS-Windows (using comdlg32). ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2006-05-11 12:35 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load dynlib lib = DynLib( "c:/windows/system32/comdlg32" ) // let's use the 8-bit string version; we could do also with 16 bit, but requeres a couple of tricks opendlg = lib.get( "int GetOpenFileNameA( void* data )" ) data = MemBuf( 100, 1 ) // approx sise of the OPENFILENAME structure memSet( data, 0, 100 ) // zero all the data fname = MemBuf(1024,1) // a string to get the filename fname[0] = 0 setStruct( data, 0, 4, 22*4 ) // SIZE: 88 bytes - needs to be precise setStruct( data, 12, 4, stringToPtr( "All Files\x0*.*\x0TextFiles\x0*.txt; *.cfg\x0\x0" ) ) // File mask setStruct( data, 24, 4, 1 ) // first string selected setStruct( data, 28, 4, memBufToPtr( fname ) ) // place to store the filename setStruct( data, 32, 4, 1024 ) // size of the filename setStruct( data, 52, 4, 0x8000 ) // Flags // perform the call if opendlg( data ) limitMembuf( fname ) // transform in a zero terminated string > "Selected file: ", strFromMemBuf( fname ) else > "No file selected." end tests/native/dynlib/samples/testgtk.fal000066400000000000000000000020151176363201700206100ustar00rootroot00000000000000/* FALCON - Samples for the DynLib dynamic library FILE: testgtk.fal Test a message box under UNIX with GTK libraries. Also tests protected items. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2006-05-11 12:35 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load dynlib l = DynLib( "/usr/lib/libgtk-x11-2.0.so" ) gtkInit = l.get( "void gtk_init(int argc, char* argv[] )" ) gtkDialogWithButtons = l.get( "struct GTKWidget* gtk_dialog_new_with_buttons( const char* str, int x, int y, ... )", "void gtk_widget_destroy( struct GTKWidget* w )" ) gtkDialogRun = l.get( "int gtk_dialog_run ( struct GTKWidget* w )" ) gtkInit( 0, 0 ) w = gtkDialogWithButtons( "Hello world", 0, 1, "Ok", 1, "Cancel", 2, "何か言った?", 3, 0 ) n = gtkDialogRun( w ) > "Dialog result: ", n tests/native/dynlib/samples/testmsgbox.fal000066400000000000000000000013761176363201700213330ustar00rootroot00000000000000/* FALCON - Samples for the DynLib dynamic library FILE: testmsgbox.fal Test a message box under MS-Windows. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2006-05-11 12:35 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load dynlib l = DynLib( "user32.dll" ) k = DynLib( "kernel32.dll" ) MessageBox = l.get( "int MessageBoxA( void* wnd, char* text, char* caption, unsigned int type )" ) GetLastError = k.get( "int GetLastError()" ) v = MessageBox( 0, "Hello world!", "Hello", 4 || 32 ) > "Ops: ", v, " le: ", GetLastError() tests/native/dynlib/samples/testmsgbox2.fal000066400000000000000000000014561176363201700214140ustar00rootroot00000000000000/* FALCON - Samples for the DynLib dynamic library FILE: testmsgbox2.fal Test a message box (wide characters) under MS-Windows. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: 2006-05-11 12:35 ------------------------------------------------------------------- (C) Copyright 2004: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load dynlib l = DynLib( "user32.dll" ) k = DynLib( "kernel32.dll" ) MessageBox = l.get( "int MessageBoxW( void* wnd, wchar_t* text, wchar_t* caption, unsigned int type)" ) GetLastError = k.get( "int GetLastError()" ) n = 4 || 32 v = MessageBox( 0, "こんにちは、世界よ!", "Hello", $n ) > "Ops: ", v, " le: ", GetLastError() tests/native/dynlib/testsrc/000077500000000000000000000000001176363201700164645ustar00rootroot00000000000000tests/native/dynlib/testsrc/test_dynlib.c000066400000000000000000000101551176363201700211520ustar00rootroot00000000000000/* The Falcon Programming Language FILE: test_dynlib.c Direct dynamic library interface for Falcon C library exporting some test functions that can be called and prototyped from Falcon. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 13 Nov 2008 19:25:55 +0100 ------------------------------------------------------------------- (C) Copyright 2008: The Falcon Comittee See the LICENSE file distributed with this package for licensing details. */ #include #include #include #ifdef _MSC_VER #define int64 __int64 #define uint64 unsigned __int64 #define EXPORT __declspec(dllexport) #else #define int64 long long #define uint64 unsigned long long #define EXPORT #endif //===================================================== // Basic int/float calls // int EXPORT call_p0_ri() { return 100; } int EXPORT call_p3i_ri( int a, int b, int c ) { return a + b + c; } int EXPORT call_p3u_ri( unsigned int a, unsigned int b, unsigned int c ) { return (int)(a + b + c); } unsigned int EXPORT call_p3u_ru( unsigned int a, unsigned int b, unsigned int c ) { return a + b + c; } int64 EXPORT call_p3i_rl( int a, int b, int c ) { int64 test = 1; test <<= 32; return a + b + c + test; } int64 EXPORT call_p3l_rl( int64 a, int64 b, int64 c ) { int64 test = 1; test <<= 32; return a + b + c + test; } float EXPORT call_pfdi_rf( float x, double y, int c ) { return (float) y + x + c; } double EXPORT call_pfdi_rd( float x, double y, int c ) { return y + x + c; } //=========================================== // String data // static char *sz_data = "Hello world"; EXPORT const char* call_rsz() { return sz_data; } int EXPORT call_psz_ri_check( const char *data ) { const char *p1 = data; const char *p2 = sz_data; while( *p1 == *p2 && *p1 != 0 ) { ++p1; ++p2; } if (*p1 == 0 ) return 0; return *p1 > *p2 ? 1 : -1; } //=========================================== // Wide string data // static wchar_t *wz_data = L"Hello world"; EXPORT const wchar_t* call_rwz() { return wz_data; } int EXPORT call_pwz_ri_check( const wchar_t *data ) { const wchar_t *p1 = data; const wchar_t *p2 = wz_data; while( *p1 == *p2 && *p1 != 0 ) { ++p1; ++p2; } if (*p1 == 0 ) return 0; return *p1 > *p2 ? 1 : -1; } //=========================================== // useful to check the content of memory // uint64 EXPORT checksum( const unsigned char *data, unsigned int size ) { int64 ret = 0; int bitmask = 1 << sizeof( uint64 ); while ( size > 0 ) { int64 val = (int64) *data; ret += val << bitmask; if ( bitmask == 0 ) bitmask = 1 << sizeof( uint64 ); else bitmask--; ++data; size --; } return ret; } //=========================================== // Param byref tests -- int // void EXPORT call_piiRi( int a, int b, int *sum ) { *sum = a + b; } void EXPORT call_piiRu( int a, int b, unsigned int *sum ) { *sum = a + b; } void EXPORT call_piiRl( int a, int b, int64 *sum ) { int64 v = 1; v <<= 32; *sum = a + b; *sum += v; } //=========================================== // Param byref tests -- strings // void EXPORT call_pRsz( char **sz ) { *sz = sz_data; } void EXPORT call_pRwz( wchar_t **wz ) { *wz = wz_data; } //=========================================== // Structures // struct dynlib_s { int size; char* name; int count; }; EXPORT struct dynlib_s* dynlib_s_make( const char *initval ) { struct dynlib_s *s = (struct dynlib_s*) malloc( sizeof( struct dynlib_s) ); s->size = strlen( initval ); s->name = (char*) malloc( sizeof( s->size + 1) ); memcpy( s->name, initval, s->size + 1 ); s->count = 0; return s; } EXPORT int dynlib_s_count( struct dynlib_s* s, int count ) { int res = s->count; s->count += count; return res; } EXPORT const char* dynlib_s_name( struct dynlib_s* s ) { return s->name; } EXPORT void dynlib_s_free( struct dynlib_s* s ) { free( s->name ); free( s ); } tests/native/dynlib/testsuite/000077500000000000000000000000001176363201700170265ustar00rootroot00000000000000tests/native/dynlib/testsuite/CMakeLists.txt000066400000000000000000000016361176363201700215740ustar00rootroot00000000000000set(categories smoke unguarded guarded byptr opaque ) # clean this directory from artefacts of former faltest runs file(GLOB _fams "*.fam") if(_fams) file(REMOVE ${_fams}) endif(_fams) # start memcheck ignore list file(WRITE ${CMAKE_BINARY_DIR}/CTestCustom.cmake "set(CTEST_CUSTOM_MEMCHECK_IGNORE\n") add_test(testsuite_all ${CMAKE_COMMAND} -P test_driver.cmake) file(APPEND ${CMAKE_BINARY_DIR}/CTestCustom.cmake " testsuite_all\n") foreach(category ${categories}) add_test(testsuite_category_${category} ${CMAKE_COMMAND} -Dtest_category=${category} -P test_driver.cmake) file(APPEND ${CMAKE_BINARY_DIR}/CTestCustom.cmake " testsuite_category_${category}\n") endforeach(category) # wrap memcheck ignore list up file(APPEND ${CMAKE_BINARY_DIR}/CTestCustom.cmake ")") configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/test_driver.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/test_driver.cmake @ONLY ) tests/native/dynlib/testsuite/README000066400000000000000000000011311176363201700177020ustar00rootroot00000000000000 DynLib - Dynamic Library Loader for Falcon Automated test suite This test suite works as usual with faltest. All the tests in this directory require the test_dynlib.xxx (where xxx is your platform DLL/so/dylib extension) to be created and copied manually here or installed. To build test_dynlib.xxx from test_dynlib.c, use -DWITH_TESTS=ON from cmake command line. Install targets generated by cmake will copy the resulting binary dynamic library into this directory, but if you don't want to run the install targets, just copy it manually. tests/native/dynlib/testsuite/byptr_error.fal000066400000000000000000000011731176363201700220650ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 3c * Category: byptr * Subcategory: * Short: Byptr errors * Description: * Checks error detection of data passed by pointer. * [/Description] * ****************************************************************************/ load dynlib try // setup l = DynLib( "./test_dynlib." + dynExt() ) // String pointer f = l.get( "void call_pRsz( char** ptr )" ) try f(0) failure( "Int" ) end success() catch DynLibError in e failure( e.toString() ) end tests/native/dynlib/testsuite/byptr_int.fal000066400000000000000000000017361176363201700215330ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 3a * Category: byptr * Subcategory: * Short: Byptr integers * Description: * Checks correct return via pointer of integer types. * [/Description] * ****************************************************************************/ load dynlib try // setup l = DynLib( "./test_dynlib." + dynExt() ) // Integer pointer f = l.get( "void call_piiRi(int p1, int p2, int* ret )" ) v = 0 f( 10, 5, $v ) if v != 15: failure( "Int ptr (1)" ) f( -10, 5, $v ) if v != -5: failure( "Int ptr (2)" ) f = l.get( "void call_piiRu( int p1, int p2, unsigned int* ret )" ) f( 10, 5, $v ) if v != 15: failure( "Uint ptr" ) f = l.get( "void call_piiRl( int p1, int p2, long long* ret )" ) f( 1, 5, $v ) if v != 1 << 32 + 6: failure( "int64 ptr" ) success() catch DynLibError in e failure( e.toString() ) end tests/native/dynlib/testsuite/byptr_strings.fal000066400000000000000000000014031176363201700224210ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 3b * Category: byptr * Subcategory: * Short: Byptr strings * Description: * Checks correct return via pointer of string types. * [/Description] * ****************************************************************************/ load dynlib try // setup l = DynLib( "./test_dynlib." + dynExt() ) // Integer pointer f = l.get( "void call_pRsz( const char** v )" ) s = "" f( $s ) if s != "Hello world": failure( "$S" ) f = l.get( "void call_pRwz( const wchar_t** v )" ) s = "" f( $s ) if s != "Hello world": failure( "$W" ) success() catch DynLibError in e failure( e.toString() ) end tests/native/dynlib/testsuite/cksum.fal000066400000000000000000000014271176363201700206400ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: -- * * Checksum library matching the checksum process in test_dynlib.c * ****************************************************************************/ function checksum( data ) ret = 0; bitmask = 1 << 8; size = data.len(); converter = data.typeId() == StringType ? \ { n => data[*n] }: \ { n => data[n] } pos = 0 while pos < size ret += converter(pos) << bitmask; if bitmask == 0 bitmask = 1 << 8; else bitmask--; end ++pos; end return ret end function checksum_32( data ) ck = checksum( data ) ck32 = ck && 0xFFFFFFFF return ck32 end export tests/native/dynlib/testsuite/errdetect.fal000066400000000000000000000027411176363201700214770ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 0e * Category: smoke * Subcategory: * Short: Error detection * Description: * Basic error detection. * [/Description] * ****************************************************************************/ load dynlib try // setup l = DynLib( "./test_dynlib." + dynExt() ) // check an unexisting function via query f = l.query( "void Yo()" ) if f != nil: failure( "Found unexisting function" ) // check an unexsting function via get try f = l.get( "void Yo()" ) failure( "Error not raised by get on unexisting function" ) catch DynLibError in e end // checks invalid return val try f = l.get( "asdr call_p0_ri()" ) failure( "Invalid retval not detected" ) catch ParseError in e end // checks invalid param try f = l.get( "int call_p0_ri( asdr name )" ) failure( "Invalid param not detected" ) catch ParseError in e end // checks invalid param try f = l.get( "int call_p0_ri( int )" ) failure( "Invalid param not detected" ) catch ParseError in e end // checks parameter after ... try f = l.get( "int call_p0_ri( void* data, ..., int next)" ) failure( "Parameter after ... not detected" ) catch ParseError in e end // type checks in other tests. success() catch DynLibError in e failure( e.toString() ) end tests/native/dynlib/testsuite/guarderr.fal000066400000000000000000000033661176363201700213350ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 2d * Category: guarded * Subcategory: * Short: Error check on guarded params * Description: * Checks if the correct errors are raised when the guarded parameters do not * match with the actual ones. * [/Description] * ****************************************************************************/ load dynlib load cksum try // setup l = DynLib( "./test_dynlib." + dynExt() ) // use a parameterless call to avoid problems in case of messups // No parameters f = l.get( "int call_p0_ri()" ) try f( "p" ) failure( "Empty params" ) catch ParamError in e end // a parameter with no call f = l.get( "int call_p0_ri( int p1 )" ) try f() failure( "Single param not given" ) catch ParamError in e end try f( 10, 20 ) failure( "Too many params" ) catch ParamError in e end // type checks try f( "s" ) failure( "Int type not filtering (1)" ) catch ParamError in e end // type checks try f( [10:20] ) failure( "Int type not filtering (2)" ) catch ParamError in e end f = l.get( "int call_p0_ri(const char* p)" ) try f( false ) failure( "String type not filtering (1)" ) catch ParamError in e end try f( 10 ) failure( "String type not filtering (2)" ) catch ParamError in e end try f( 10.5 ) failure( "String type not filtering (3)" ) catch ParamError in e end try f( nil ) failure( "String type not filtering (4)" ) catch ParamError in e end success() catch DynLibError in e failure( e.toString() ) end tests/native/dynlib/testsuite/guardint.fal000066400000000000000000000031761176363201700213360ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 2a * Category: guarded * Subcategory: * Short: Guarded integers * Description: * Verifies working with various types of integer parameters. * [/Description] * ****************************************************************************/ load dynlib try // setup l = DynLib( "./test_dynlib." + dynExt() ) // Parameterless f = l.get( "int call_p0_ri()" ) if f() != 100: failure( "call_p0_ri" ) // int f = l.get( "int call_p3i_ri( int one, int two, int three )" ) if f( 1, 2, 3 ) != 6: failure( "call_p3i_ri (1)" ) if f( 1, -2, 3 ) != 2: failure( "call_p3i_ri (2)" ) // unsigned f = l.get( "int call_p3u_ri( unsigned int one, unsigned int two, unsigned int three )" ) if f( 1, 2, 3 ) != 6: failure( "call_p3u_ri (1)" ) if f( 0x80000000, 1, 2 ) != -0x7ffffffd: failure( "call_p3u_ri (2)" ) f = l.get( "unsigned int call_p3u_ri( unsigned int one, unsigned int two, unsigned int three )" ) if f( 1, 2, 3 ) != 6: failure( "call_p3u_ru (1)" ) if f( 0x80000000, 1, 2 ) != 0x80000003: failure( "call_p3u_ru (2)" ) // long f = l.get( "long long call_p3i_ri( int one, int two, int three )" ) if f( 1, 2, 3 ) != 0x100000006: failure( "call_p3u_rl (1)" ) if f( -1, -1, -1 ) != 0xFFFFFFFD: failure( "call_p3u_rl (2)" ) f = l.get( "long long call_p3i_ri( long long one, long long two, long long three )" ) if f( 1, 0x100000000, 0x100000000 ) != 0x300000001: failure( "call_p3l_rl (1)" ) success() catch DynLibError in e failure( e.toString() ) end tests/native/dynlib/testsuite/guardmb.fal000066400000000000000000000014161176363201700211350ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 2c * Category: guarded * Subcategory: * Short: Guarded membuffers * Description: * Verifies working with various types of membuffer parameters. * [/Description] * ****************************************************************************/ load dynlib load cksum try // setup l = DynLib( "./test_dynlib." + dynExt() ) check = l.get( "long long checksum( void* data, int size )" ) mb = MemBuf( 40 ) for i in [0:mb.len()] mb[i] = i*2+20 end r1 = checksum( mb ) r2 = check( mb, mb.len() ) if r1 != r2: failure( "Check failed" ) success() catch DynLibError in e failure( e.toString() ) end tests/native/dynlib/testsuite/guardstring.fal000066400000000000000000000027421176363201700220500ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 2b * Category: guarded * Subcategory: * Short: Guarded strings * Description: * Verifies working with various types of strings parameters. * [/Description] * ****************************************************************************/ load dynlib try // setup l = DynLib( "./test_dynlib." + dynExt() ) // plain string rsz = l.get( "const char* call_rsz()" ) check = l.get( "int call_psz_ri_check( const char* chr )" ) the_string = rsz() if the_string != "Hello world" failure( "S return" ) end if check( the_string ) != 0 failure( "S check" ) end if check( "Hello world" ) != 0 failure( "S similar" ) end if check( "Walla" ) != 1 failure( "S greather" ) end if check( "All" ) != -1 failure( "S smaller" ) end // wchar_t string rsz = l.get( "const wchar_t* call_rwz()" ) check = l.get( "int call_pwz_ri_check( const wchar_t* chr )" ) the_string = rsz() if the_string != "Hello world" failure( "W return" ) end if check( the_string ) != 0 failure( "W check" ) end if check( "Hello world" ) != 0 failure( "W similar" ) end if check( "Walla" ) != 1 failure( "W greather" ) end if check( "All" ) != -1 failure( "W smaller" ) end success() catch DynLibError in e failure( e.toString() ) end tests/native/dynlib/testsuite/guessguard.fal000066400000000000000000000021641176363201700216660ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 1d * Category: unguarded * Subcategory: * Short: Unguessable parameters * Description: * Checks if the library correctly raises error for unknown/unguessable * parameters. * [/Description] * ****************************************************************************/ load dynlib class cls function f() end end try // setup l = DynLib( "./test_dynlib." + dynExt() ) // use a parameterless function so we won't crash on mistakes. f = l.get( "void call_p0_ri(...)" ) try f( [1:2] ) failure( "Unguessable not guarded: range" ) catch ParamError in e end try f( cls ) failure( "Unguessable not guarded: class" ) catch ParamError in e end try f( cls() ) failure( "Unguessable not guarded: object" ) catch ParamError in e end try f( cls().f ) failure( "Unguessable not guarded: method" ) catch ParamError in e end success() catch DynLibError in e failure( e.toString() ) end tests/native/dynlib/testsuite/ints.fal000066400000000000000000000014321176363201700204670ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 1a * Category: unguarded * Subcategory: int * Short: Unguarded integer operations * Description: * Try some call on unguarded integers * [/Description] * ****************************************************************************/ load dynlib try // setup l = DynLib( "./test_dynlib." + dynExt() ) f = l.get( "int call_p3i_ri(...)" ) r = f( 1, 2, 3 ) if r != 6: failure( "int sum" ) r = f( 1, -2, 3 ) if r != 2: failure( "int sum with negative" ) f = l.get( "int call_p3u_ri(...)" ) r = f( 1, 2, 3 ) if r != 6: failure( "unsigned int sum" ) success() catch DynLibError in e failure( e.toString() ) end tests/native/dynlib/testsuite/membuf.fal000066400000000000000000000017131176363201700207670ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 1c * Category: unguarded * Subcategory: membufs * Short: Unguarded membuf operations * Description: * Checks for membufs to be correctly passed in unguarded mode. * [/Description] * ****************************************************************************/ load dynlib load cksum try // setup l = DynLib( "./test_dynlib." + dynExt() ) f = l.get( "void* checksum(...)" ) mb = MemBuf( 40 ) for i in [0:mb.len()] mb[i] = i*2+20 end r1 = f( mb, mb.len() ) // we didn't set a return type, so we got to use the 32 bit // version of our checksum r2 = checksum_32( mb ) // but on 64 bit system, we'll receive a void* integer so... r3 = checksum( mb ) if r1 != r2 and r1 != r3: failure( "Pass membuf failed" ) success() catch DynLibError in e failure( e.toString() ) end tests/native/dynlib/testsuite/minimal.fal000066400000000000000000000014401176363201700211370ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 0b * Category: smoke * Subcategory: * Short: Minimal test * Description: * Gets an unguarded routine, calls it and see if the result is coherent. * [/Description] * ****************************************************************************/ load dynlib try l = DynLib( "./test_dynlib." + dynExt() ) f = l.get( "int call_p0_ri()" ) if f() != 100: failure( "Invalid unguarded return value" ) // try also with query. f = l.query( "int call_p0_ri()" ) if f == nil: failure( "Queried function not found" ) if f() != 100: failure( "Query - Invalid unguarded return value" ) success() catch DynLibError in e failure( e ) end tests/native/dynlib/testsuite/opaque.fal000066400000000000000000000016141176363201700210060ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 4a * Category: opaque * Subcategory: * Short: Opaque types * Description: * Checks for correct usage of opaque types. * [/Description] * ****************************************************************************/ load dynlib try // setup l = DynLib( "./test_dynlib." + dynExt() ) make = l.get( "struct dynlib_s* dynlib_s_make( const char* name )", "void dynlib_s_free( struct dynlib_s* s )" ) test = l.get( "const char* dynlib_s_name(struct dynlib_s* item)" ) data = make( "Myself" ) if not data.derivedFrom( DynOpaque ) failure( "Data not opaqued" ) end if test( data ) != "Myself" failure( "Data not correctly used" ) end success() catch DynLibError in e failure( e.toString() ) end tests/native/dynlib/testsuite/opaque_error.fal000066400000000000000000000016211176363201700222150ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 4b * Category: opaque * Subcategory: * Short: Opaque error * Description: * Checks for errors in using opaque data. * [/Description] * ****************************************************************************/ load dynlib try // setup l = DynLib( "./test_dynlib." + dynExt() ) // String pointer make = l.get( "struct dynlib_s* dynlib_s_make( const char* name )", "void dynlib_s_free( struct dynlib_s* s )" ) test = l.get( "const char* dynlib_s_name(struct dynlib_s* item)" ) data = make( "Test" ) try test( 0 ) failure( "Not filtering an integer" ) end try test( "data" ) failure( "Not filtering a string" ) end success() catch DynLibError in e failure( e.toString() ) end tests/native/dynlib/testsuite/pseudoclass.fal000066400000000000000000000016711176363201700220440ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 0d * Category: smoke * Subcategory: * Short: Pseudoclass * Description: * Checks correct identification, content and protection of foreign * pseudoclasses. * [/Description] * ****************************************************************************/ load dynlib try // setup l = DynLib( "./test_dynlib." + dynExt() ) f = l.get( "struct PData* call_p0_ri()" ) // this creates a pseudo class pdata = f() // what is pdata? if pdata.baseClass() != DynOpaque failure( "pseudo class creation" ) end if pdata.type != "PData" failure( "pseudo class value" ) end // has pdata the right content? if pdata.ptr != 100 failure( "pseudo class content" ) end // correct call in other tests. success() catch DynLibError in e failure( e ) end tests/native/dynlib/testsuite/smoke.fal000066400000000000000000000010571176363201700206330ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 0a * Category: smoke * Subcategory: * Short: Smoke test * Description: * Just loading the library and see if it's all right. * [/Description] * ****************************************************************************/ load dynlib try l = DynLib( "./test_dynlib." + dynExt() ) success() catch DynLibError in e failure( "Can't load the test dynamic library needed for the test suite: " + e ) end tests/native/dynlib/testsuite/strings.fal000066400000000000000000000017121176363201700212040ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 1b * Category: unguarded * Subcategory: strings * Short: Unguarded strings operations * Description: * Checks for strings to be correctly passed in unguarded mode. * [/Description] * ****************************************************************************/ load dynlib load cksum try // setup l = DynLib( "./test_dynlib." + dynExt() ) f = l.get( "void* checksum(...)" ) string = "Hello world, this is a checksummed string!" r1 = f( string, string.len() ) // we didn't set a return type, so we got to use the 32 bit // version of our checksum r2 = checksum_32( string ) // but on 64 bit system, we'll receive a void* integer so... r3 = checksum( string ) if r1 != r2 and r1 != r3: failure( "Pass string failed" ) success() catch DynLibError in e failure( e.toString() ) end tests/native/dynlib/testsuite/test_driver.cmake.in000066400000000000000000000012011176363201700227610ustar00rootroot00000000000000find_program(faltest_EXECUTABLE NAMES faltest HINTS "@CMAKE_INSTALL_PREFIX@/bin" NO_DEFAULT_PATH ) if(NOT faltest_EXECUTABLE) message(FATAL_ERROR "fatest executable not found") endif() if(test_category) set(cmd ${faltest_EXECUTABLE} -v -c ${test_category}) else() set(cmd ${faltest_EXECUTABLE} -v ) endif() execute_process( COMMAND ${cmd} WORKING_DIRECTORY @CMAKE_CURRENT_SOURCE_DIR@ RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_VARIABLE err ) if(res) message("faltest return value: ${res}") message("faltest stderr: ${err}") message("faltest stdout: ${out}") message(SEND_ERROR "test(s) failed") endif() tests/native/dynlib/testsuite/tostring.fal000066400000000000000000000066401176363201700213710ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 0c * Category: smoke * Subcategory: * Short: String conversion * Description: * Verifies if the parameter parsing and reverse conversion of the function * description are working. * [/Description] * ****************************************************************************/ load dynlib try // setup l = DynLib( "./test_dynlib." + dynExt() ) f = l.get( "void call_p0_ri()" ) // Basic check if f.toString() != "void call_p0_ri()": failure( "void call_p0_ri()" ) // mean check f = l.get( "int call_p0_ri()" ) if f.toString() != "int call_p0_ri()": failure( "int call_p0_ri()" ) f = l.get( "int call_p0_ri(...)" ) if f.toString() != "int call_p0_ri(...)": failure( "int call_p0_ri(...)" ) // a bit more complex f = l.get( "int call_p0_ri(void* data)" ) if f.toString() != "int call_p0_ri(void* data)": failure( "int call_p0_ri(void* data)" ) f = l.get( "long call_p0_ri(unsigned long p)" ) if f.toString() != "long call_p0_ri(unsigned long p)": failure( "long call_p0_ri(unsigned long p)" ) f = l.get( "short call_p0_ri(unsigned short p)" ) if f.toString() != "short call_p0_ri(unsigned short p)": failure( "short call_p0_ri(unsigned short p)" ) f = l.get( "short call_p0_ri(unsigned short p)" ) if f.toString() != "short call_p0_ri(unsigned short p)": failure( "short call_p0_ri(unsigned short p)" ) f = l.get( "void* call_p0_ri(char p)" ) if f.toString() != "void* call_p0_ri(char p)": failure( "void* call_p0_ri(char p)" ) f = l.get( "void** call_p0_ri(unsigned char p)" ) if f.toString() != "void** call_p0_ri(unsigned char p)": failure( "void** call_p0_ri(unsigned char p)" ) f = l.get( "void** call_p0_ri(const char* p)" ) if f.toString() != "void** call_p0_ri(const char* p)": failure( "void** call_p0_ri(const char* p)" ) // with structures f = l.get( "struct test* call_p0_ri(int* p)" ) if f.toString() != "struct test* call_p0_ri(int* p)": failure( "struct test* call_p0_ri(int* p)" ) f = l.get( "union test* call_p0_ri(int* p)" ) if f.toString() != "union test* call_p0_ri(int* p)": failure( "union test* call_p0_ri(int* p)" ) f = l.get( "enum test call_p0_ri(int* p)" ) if f.toString() != "enum test call_p0_ri(int* p)": failure( "enum test call_p0_ri(int* p)" ) f = l.get( "void call_p0_ri(struct test* the_struct)" ) if f.toString() != "void call_p0_ri(struct test* the_struct)": failure( "void call_p0_ri(struct test* the_struct)" ) f = l.get( "void call_p0_ri(union test* the_struct)" ) if f.toString() != "void call_p0_ri(union test* the_struct)": failure( "void call_p0_ri(union test* the_struct)" ) f = l.get( "void call_p0_ri(enum test the_struct)" ) if f.toString() != "void call_p0_ri(enum test the_struct)": failure( "void call_p0_ri(enum test the_struct)" ) // multiple parameters f = l.get( "void call_p0_ri(void* data, const char* string, int size)" ) // we have to see this normalized if f.toString() != "void call_p0_ri(void* data, const char* string, int size)": failure( "void call_p0_ri(void* data, const char* string, int size)" ) // multiple params with ... f = l.get( "void call_p0_ri(int size, ...)" ) if f.toString() != "void call_p0_ri(int size, ...)": failure( "void call_p0_ri(int size, ...)" ) success() catch DynLibError in e failure( e ) end tests/native/dynlib/testsuite/varparams.fal000066400000000000000000000015641176363201700215140ustar00rootroot00000000000000/**************************************************************************** * DynLib - Falcon dynamic library loader module - test suite * * ID: 2e * Category: guarded * Subcategory: * Short: Variable params specifier * Description: * Check for the variable parameters specifier to actually change the parser * into unguarded mode. * [/Description] * ****************************************************************************/ load dynlib load cksum try // setup l = DynLib( "./test_dynlib." + dynExt() ) catch DynLibError in e failure( e.toString() ) end // use a parameterless call to avoid problems in case of messups f = l.get( "int call_p0_ri( ... )" ) // empty test f() // some params test f( 1, 2, 3 ) f = l.get( "int call_p0_ri( int p1, const char* s1, ... ) " ) //minimal call f( 1, "hello" ) // some more params f( 1, "hello", "another" ) success() tests/native/gd2/000077500000000000000000000000001176363201700141705ustar00rootroot00000000000000tests/native/gd2/samples/000077500000000000000000000000001176363201700156345ustar00rootroot00000000000000tests/native/gd2/samples/gd_drawing.fal000066400000000000000000000014061176363201700204260ustar00rootroot00000000000000load gd2 try img = GdImage( 200, 200 ) white = img.ColorAllocate( 0xff, 0xff, 0xff ) red = img.ColorAllocate( 240, 10, 20 ) blue = img.ColorAllocate( 10, 10, 90 ) img.Line( 0, 0, 200, 200, red ) img.Line( 200, 0, 0, 200, blue ) img.FilledArc( 100, 100, 10, 8, 0, 360, red, gdArc ) img.FilledRectangle( 95, 46, 105, 54, red ) img.FilledRectangle( 95, 146, 105, 154, blue ) img.FilledRectangle( 45, 96, 55, 104, red ) img.FilledRectangle( 145, 96, 155, 104, blue ) > "Writing image gdTest_paint.png" out = OutputStream( "gdTest_paint.png" ) img.Png( out ) out.close() > "Write complete" catch IoError in e > "Can't create image gdTest_paint.png" > e catch GdError in e > "Error in creating the image." > e end tests/native/gd2/samples/gd_load.fal000066400000000000000000000006441176363201700177150ustar00rootroot00000000000000load gd2 if args.len() == 0 imgname = "logo.png" else imgname = args[0] end > "Loading PNG image ", imgname try fin = InputStream( imgname ) img = GdImage.CreateFromPng( fin ) fin.close() > "Loaded PNG image" > "Image size: ", img.SX(), "x", img.SY() catch IoError in e > "Can't load image ", imgname > e catch GdError in e > "Error in decoding file ", imgname, " as a PNG." > e end tests/native/gd2/samples/gd_save.fal000066400000000000000000000011401176363201700177240ustar00rootroot00000000000000load gd2 if args.len() == 0 imgname = "logo.png" else imgname = args[0] end > "Loading PNG image ", imgname try fin = InputStream( imgname ) img = GdImage.CreateFromPng( fin ) fin.close() > "Loaded PNG image" > "Image size: ", img.SX(), "x", img.SY() > "Saving jpeg image" uri = URI( imgname ) p = Path(uri.path) p.extension = "jpg" uri.path = p.path out = OutputStream( uri.uri ) img.Jpeg( out, 85 ) out.close() catch IoError in e > "Can't load image ", imgname > e catch GdError in e > "Error in decoding file ", imgname, " as a PNG." > e end tests/native/gd2/samples/gd_string.fal000066400000000000000000000010361176363201700203000ustar00rootroot00000000000000load gd2 name = "gdTest_string.png" try img = GdImage( 200, 200 ) blue = img.ColorAllocate( 10, 10, 90 ) white = img.ColorAllocate( 0xff, 0xff, 0xff ) font = gdFontGetMediumBold() for i = 1 to 10 img.String( font, 5, i * 15 + 2, "Hello " + i + " times.", white ) end > "Writing image "+ name out = OutputStream( name ) img.Png( out ) out.close() > "Write complete" catch IoError in e > "Can't create image " + name > e catch GdError in e > "Error in creating the image." > e end tests/native/gd2/samples/gd_stringft.fal000066400000000000000000000026071176363201700206370ustar00rootroot00000000000000load gd2 name = "gdTest_stringFT.png" fonts = [ "Times New Roman", "Times", "Arial", "FreeSans", "Sans", "System", // falback to a safe font. "./FreeSansBoldOblique.ttf" ] // if we have an arg, add it on top if args.len() > 0: fonts[0:0] = args[0] for font in fonts try brect = GdImage.StringFT( 0, font, 14, 0, 0, 0, "Hello 0 times." ) > "Using font \"", font, "\"" break catch GdError in e // no good, try another font > "Can't find font \"", font, "\"..." end end if not brect > "Please, specify a full path to a valid TTF font on the command line." > "Can't find any suitable font. Terminating" return 0 end try /* create an image big enough for the string plus a little whitespace */ w = brect[2]-brect[6] + 6; h = (brect[3]-brect[7])*5 + 16; img = GdImage.CreateTrueColor( w, h ) blue = img.ColorResolve( 10, 10, 90 ) white = img.ColorResolve( 0xff, 0xff, 0xff ) img.FilledRectangle( 0, 0, img.SX(), img.SY(), blue ) for i = 1 to 5 img.StringFT( white, font, 14, 0, 3, i *(brect[3]-brect[7]+2) +1, "Hello " + i + " times." ) end > "Writing image "+ name out = OutputStream( name ) img.Png( out ) out.close() > "Write complete" catch IoError in e > "Can't create image " + name > e catch GdError in e > "Error in creating the image." > e end tests/native/gd2/samples/logo.png000066400000000000000000000636141176363201700173140ustar00rootroot00000000000000PNG  IHDRkĶFstEXtSoftwareAdobe ImageReadyqe<g.IDATx`\W>}un˖"YJB $z[~vv , %v! Hbҫ;v,ɖ^ڽ{ߛF!mKƒf̼w{Nm:0OE,xW3UL1s`JG8Kؔ͟QUՕM6o.`.&hՂ ۙ<&:4]/HH>FPUy8ETkIG ]<À rd^Y=0^dsɩYwBzԚ^5Rf隲Sm'sǯ@!hbr0mE> L2V]Y82:WpԚ%ܯNnMMDAmg4EjktMn}9[ Z&13Z!H`(d+Uc pr4%JlsWXBcH7aXk0,X y5f4ck֔h0%/dZ>[GkMz5Z6=+8'H@b SF0hQ(2;ugd%"rXMlkY,!EN'@ N~ 'r,?3xܮ7dMȒYŴ؛2Ѿj!+C= M`d _e}[Q one )h{h?CT`42H<",(+rt"kpruk=c* T8|T2330YAݝ[=? Y1PtuWyƕr7 piSc1m_vSad舘6;Viĕs P`wɮM?A,400"hJ)m!`FaA#6~* >BepFawվ/p9#B zN|TRB):Ngqͬ=!^67[~;~+"x+i:󉇅ed 9L,W/'&>ؒ̏Є+0 b(4PO=؜Cgs=`(*qT)a9YuWƫq0R#*^G;MT z[8k92rʷ׀ѝ:'0)eسG-^ `Fsv8'ӕkR^VcvZ"Be 4QgTi%I66,{$KFT΀3*n`mJw aW(}{îX؁HtЪ=.' x"xqLI;|/R m^g E)$j07gLS2C`1IhO<&ߍ,q5x7zF00ͼ2 %ȁi(@Zr!kKW2X̃ 3168FɅ nI)#{341"66]WeA*1y[NLgofz8y5a%):Bʼn`b*`slQ|)%^@m9E% >1W tsuLDPَLW[k*ɓ`RC#>mo o)XvCLM'ԧw W+Ewxl#\FNcTU9 A&9~'ɬ<ҕ"Cq552+dl~7铡{t͛ZC}zl7>x;0Y ;Ry_6F_#Q'A[ıcߵFqOLj|uE僶іwޱ  rLBOj%S %n'0*=upvw+7y=X Ib̩:(T-4:<1ǭ㭟3{fшwWHn:J1T3$@G^dž,<w$%` tR?~6}eX`POP5%%"TǕѡW]P{͗هG!mħX0 ˲ i_\,OPqbUC\3Տ}CzN'5jh$& _&i'#*tU݃TB(0! h>BWyz[qC ҉6y׌ha/:%(8ue٭_]jϷ|qg^z׭7}|uIVvuw3iyhǯ9x FA 8NZŚڜP,] _PlRu rF4|TƸrbPJ"Ly> Je8@7s9K%~7.@7gr˒mZ~MSb v7q󭱌vQrNj<}G.q]2lcMwJeaDubY|'0ʌF0ȸ8cPEy:g{Ʉ|E[ y]l 9r0HU&@lH,,2#IWy1Ȁ(08 RMhr}̊?fv>˙5Ԕ/ٽoߒOټRO]{=/Z?O^lhkK?Y0P0usRTXQf E) Js`JAqqjL(!򃀁!Te6rb֑(ymyCh8"ӕSNdq*N8r*zTФ9YP\K}N \"Rm4Su e|y] olT}OVk]bΟV^rz!\d> e:~ffC?P(!9F.NcS2F`NDFՀ7U!aĝfc-}}Mer9%y  աgЊpeU ܜq&b׽HRi$Wnc!^-+Y|=i7[3пS l׃E3ddUT{S5i&L? $N.F( &r#}:2M}B.A hod>(X6oGǤ :8~}c#=7gp^5MLL01'aĄ\"vn/:~=3;#oq4w{әGC3N~FrF*& ,MF&'CbM*e@UK̕ kc;RLu[J\-ǘ.ɌGjmeϦD`%,-=Z`wf߱ETP%CX/B`,d>ZZ[[Rw\j:sR_͒oxU[)_uke*zk)oƇ9n:t`x:*~ԍ¢i1: U"sG I czL$+ d!ulpž;9E^7#Hɇ#IeÍ ~TW584Qqn xuL z1ik߰諏=#3* 6ݪ:&\.Y6ծSD5(w7*!coz<s|D`ָ8F j%01>ҧfV_|%*qyvҿw6qnݎZJ"mz~kXη|Ugą|%[bE%D g=`<#8y+ͪ~,%+{JkG5]YH `eq߅> KYy  zfv|Z]s o1P?'pŬ7zc]YQex\2 rDYxN555^pʧ6oOO VKv]ʼn1` ~wX R%9b " a$$ʔAN2:Zl/O[W,ARpz5o/bR㤕V@G5A֬ދލ;vpY/t%>L~9|KQ?q^h(O%NTVC}9t%! ?x(ǖ ;'j+Y>"4Ւ2\sodCco \Ec (e{.Mg4 CJbq4ݛ Agt yEKwfƼC]xp/fxgFPuɣ7nYKKKVuٝ5 "(k]#-^,z8S3 +ϧ 2MBz>&XGey6AA:O|ΒlJi.:PVP2 Lr d޽ |'V\3c Z3obzo8]_ew y.nf3 _GoS`VƚŖi&X;>oeLAb6Ռf}Zz<^K˲ TUs;̈́jyXK -畬ֆ;=z=kPP"/:8?A `.;#>'(|5:3ª//@jDr7owWOK*F8k?tUT68|{e MBJz:a(y`^Yu+x=D̍n]/z˻ßM*H?H+|H]%%%8VܙP 5'<XۑK%+3,c]S3Wº:|.ʹbG 6e\K!ZzeugXdÀgqp>ԒD~v`_Ι:ғ5 o͖ܾ};:ҦtɦXئ"VI1td* \Y-3˚i_ɀQUAc | 'UF5yi2]0^IV-bljCZuӧ,K.4Bkߩ +uG(*e8 rRӌePu={Jgc1Bx"tolI+xNLj%Q|ea,-^fh{iqc1`MNFdvZ2^<6TaZɤjI"W29)+fc@+e+T FfsD.aF*$ٝsTD%\;Te7\œ|=-*tCǰ:t=i7b3]Q*jwz3R䍞V׵J+PS"+_zJVQhO;KPK=S;4QMH""Ar/_8-R ɚog@3}#;?HD-76p 7ԲAvå\vAN>/i A "*QTgv>]u{ZSU;Aʪqw!k*(3}w=5 6bb` pZ.FeD:lJhmxzعkf} S-gYC2Bɍr,dŢ3Ggj3eZo Ma2k*R2&WԎOEEłhECS&Cǣt@O X-Șl;Ե\(KLZ%%Fg֊25~10e6\`o|nkղX^UbfKy ĆPCSK Lc+K'o@-|A%Ԋ$FFD9pX''f~ͦ N֟ew B]NxuFg2aB.\6}yĞ~-4; 02i8D{ 5d@C"ZXt.O" IT&E"'߯҉ 2o Et}fp9Ӏl 9s2DQB3;+eBidU<l0U |z`%/͠`c !BUDjŌ {1bo 0KTo2:dFo5my-1aJ+k;(h[qV_(c%zifv_LN?r`.Eס>%i F! h6X*ц7&Ş;\d}y=bncN)l /u:|uמ5:+N8ZT`ԃ%abPp8ؼ1 A1tu=K3&Ep;1 }Kftٕ?U[2^9iz`st7ɣ,[^JL$2ȓ!G5FIYV;!KHxy-"}`9l p 3K@^+K#Oq(lXzs\x9՜:9]wСFRA(|V}iXĊ=,gn@̅|ImdAYh;pzo渚p> z4!/' F`  wwvt,o'`<Ҙ)(&>Vr()W)fa(%YAfTE'yu?D"njLUk,RdhEN+^mY/Nڧ9.O"ZJl.4Yc~g'b(%9O>`Baݺu ,5CpC7Ѱ7Y+V0%T INR&m^bko8给qy75= -..>MbH=?tUi<C(n1knngGN`m~F6i!%4̫1VЈƛO$ct?NiYjݣ<3T64Y^׍-Bb΍l`4'9'3o`E6F:IgW~n~|!BlhS߈p֙p8lXv\rgݯLm2 L cE1>_/(?a N-;$/KFQ$v  IsͯY| /"sS5Nx03C k׮]rqZ43,j?~ LaTaĿbX7;yi(eҝc/{h ;ޝG|:X^yqQM̰pqˁ8zTҰH,5- 9NB6Q5Bр 35=ty_+ߘo8s{KX/- [ E@gJn<8( A 9T8FIt`jye権6FZP+Z7:{Ӎu7Vv*3t%pƭYMm(0l6;؞'J4Bf%k_+54`*sD;'i&"ր5r IQh!CJ(Hj@\Nwau:@ETxl5D-AaM,`˻2{>;F8]])Q>jȕ[h{d|6/[bTv0AC->RBұ8F-X$t?a$#R#F۲ 6qᘛ&2aQAb ٢6L!zx%֍e9+OrQs$>u3"5pVA X۟t|Wd]F$MM]&҆ã5TUHkgjWĭ{NKAOF,]ȀeK,ys bo͠ OPY/BFDV*aFc^@7AX+PMoy|ZPkS2x|'9/N"?hѼ/NU]Z# O}r_j^3Zr_gW*0㲊sͶ~ۉSƬUbYs8uP 8Ҍ=gY@ ?)  c,m PYnT%իoɄS R͝_6lXꔐҕنSF0Z5CUI}fl㵟٦vS}uh8t#w5-_A{C;DE5VPr͆Jc\Wp01Ud=Gs5LgTLy>29{D4' ѵFJC8 Pic(1|"34mfhvIU+m㡒{fK&c0ذSl X@U^Jx۶] g8DAb+s1n'V+% =;[ˇfg2W=u5lM?'J+P`%T}|˓7 S=_6&Z@8iUZ$ʸgĩ:|%`u4}~4<[A V]}\o .ݏ|{Z89l&4/h[KJ@< dR& )QE`_e혨#`iBRpfbEgHd鬊# E7_5XO#݊fsh%+ jjNmJˍ^Ԏ6JqzC EXG#mCm5qWF"wj˶ʲ`֟?[›&V"P$%P#N(la๠ ,NԺYr~%k0%sCv-8Su۷/KOwUg33!VE#G 4f xN3j[,*˭7޿@\Jd`ZQ!,t(,(04|6҇~<$C~cˍ=-mЯ d#ZsLʯǟD-B_(:|NT㜊µj`.G>Uw0-#dX#2imóz[~va\F(W_qss"]2Ikϓ.Mٟd;~Đ6j@A9Vue 68hO?*-CxU#$(^ZVX(ޗc9;pSERlŎ~Gf#N+:m e>"OGDInˮ|MV3Opp惎ͬ6\p'̀ th> H,+W[X.&=r:_g]ǎ774#3tJIz4Q8sklުѽZeEݚ=ּ—=֭]k|Uٳ gəgny+LQ f~#VsuU}-DžCtd4- R.96W.wkv w `.5#*vBX}Nu2g2FJmG-/\*vd4{6{,:۞SG$ % ĴE|.wXAs8iKL1LRo>w? @yYݟWѲM?٪$T:./3*1ݸCB1ITGW?|iS񓊽K7ZGnxBlc=0#x/{~hMپQIѥ8s}\XXom< "sTrh.`)œ^bm7,ڸ6 4fc󖩔%InFXcivbo e|1E1:2Oۮ##*+yE1%a >WeU@`Kthsi{"T;;ƺQb2yVG lCHR+6vSmy;rU wG?emΑƪx~8c؅^ƆIϷu :otcWltw{?4TL3iZ_ 5?QCh,46x2\Άƽ{si'*Z&h ǰdCFuWfUjJ500mXGv[4HK[ENmznN䘎f9*]P5ke9OSwߩ]& 7~΁-ŦV 7lvqϣ0k$;bdr}_;w7,x>79KqDkm !ƒ3IS%8'Oͬ.\ȪUԡKo꟥ᕀy:LJg*ZLL3ggGkLAxX"CRĦ=p5}Cª?uKouSLO9Wlޡvfdn)sd.,]tn%`c6R˟RΩx!5T3(jhѝz2А#ˆ_GjF遪 7 XhV:bYOoUmVXSksV(<8Xs'jk$vZ`ZYa{cϤ} +H(`~'gf=mF`FaU~O^q{jcO_陙PkYFJ($g& =D4sҍ: z Acs4^ ;B4ILKu9B!KB4᯼щ5غ{Pl9ͺ-()C{JW:صٵWp#Us6xz} ?< mTbeY̊}&UlP9k(.f6;9_OmNMt~~<ʧky-,qMGT908yx?4р](U7.;:)9^kJ؈.9ޕ2jQX.޽OO p<7vuf2Ǻ6PKO <Dp®]zJ`-m4M ĹD[`.FyGb@̊8!8SXylSEW_HypB:_vZoB2|./fܞ-3ZDfr Ҷ2irT 736r2 (GglD N,f! 8mŸ7k >)0Ӕ} Zo {wY&esVc wSN_g3n2ɴsC1&X-6%sj᠀'f4|)c#W ( qCEX^}͇O츨* ,c8pz?fYIGdt7P$0Q"Ҭ#FTi 7O3;) hVXƎT;dD-猝r7h?wWsW~O,y!]3}섣 o "|h8S}0Ωmm{Ju<3qW~ Un4Y%X12YX &Уv,LAg  E۬di>~ٓs`o/|wAt2}싢r,BbE^32C&Ɉ6TٜszG1r 1 ,@fi=ӺOVqwГX4Վu7tah20 PW\W~#C~wvms=2:Lw9 F^:;UYDTJ:5^{A9ݨBKNa'oO#+\*|]wL"UtS_v顃_ 77~ȳWqW+%:?SuJc i{CDT]kL佇gZ#sN~g%@ntՠBQDh`;}l6 >g-Ȫ9PaIc jd#EaQ RĘ7?sH˳ ^P-Y _"͐mVVoP$[xx9KAWoϜ"5ц0 V'E#ԨǤ6 [""InBҧĪwAIӌ&,ά<"8&<,HcB"DoFh咨y te !@<]_萲jWO^s@:Y_o/{j,p8Hk|i]y}BKܳǣ`bx2k)ά@nŬ( ւ{cQ/3bu [`jϤ\^ 1[ܙ;8HaB[`g&7]Df=mLE%Ԝ~A ayTRh!s81uXS;bg}ڍlnn\]/ 2 sTr<JY}%8>{hZAh1pK/͚K'o:XY:ɁO8y ̓#vb$[>щ}—Pl @,5fC2k"'3,̽4*Hy2\b9ġ-M`A W^yv[)=sfMLzX5Mt/y{&eZ_P",_ He>8"3l|hz%b:BKv}U\ZFVeH?gvbWPՕO!8y=8R@+@&trq {2NC ۆ}3D#F`az3^ˀ #Qm"UZF@tcߑY!Paòԙ3W_BR|+ %/Z@PzgkS]$5\B$lz<*Xw" +9/ctkSC';QO빈We%2TSB* %"- H#%f. k$G)O}d~)-)eSUOajԨ/i5:^+7-TTf=Wg@',!+F\]GTGy5ziϛ Yt%ɏJaO%AYs94y5Y i%f4Al5_0\h]X]nKǏ6Q,3%0=SLȼNJ[S)NsY +P ]$TGư.YO2˟%bSjmb*2YѪZ7xS2eu7QpbEތg5g\Ay}R; FF$.O8NykfYC%–x+XE#UM35Uc $.SnJ[ó4Cۼ8d{|ǘRRbȒ~T W9"镀5EJQEWD({O|׺w4Xr8qO8r] +y%bgQ  RdӳKPZ-vޢ* ZzDBG5]^jtl3|3*PIQiE ⤅I*r0JAƔ7zqc߱T/hhjX9Ss۬Zp%2&,U{f7{O&3VRUX <}# ~ih0JU>.:Ryw#M@ǹF^%qbFc;wqEPIh4J2@-DlœQF!dDD$qU(F#E2ʞU n@̖n0래[L_nH, BJ>t^8{/AKdBڳ2)h|L;qff&kBHdTe)YHK}A7!E/"( HF'kIJpJ]@U/xk{vh&X+X`q%6+:7m>vܤ9G{YRiAXHvOF$C`܆ՕZa}-qXFCD"7 $4e4.@l[z4{2N0V,!ӁgW٬鞾MZXɠM?3 JnVHl Ŧ vdz'MlCִn姍O0!] YŌHw*ީ)rHvp.62 ^]C-bySH iH֕FowwT{)tzYv7{UZh}Hk(t@֪Plt r2J1%9x@X " :;Ғ yJ[n޼SKq!,&ܚ ={RHub"@% 'uՙUTS9=%dGcT[|R˜=%3u,\^ί4#c?=8sw>~?I $EQdيX[:IӎN:m3x6-ȆUFE!0,G=,rjۢ@B q@!Rs#|p} ZTCΘE=]l)t#|9Fc1~b?XaMqٔl]Z6Rr186c!6`+Yv=W؊" I-EM$3, s@$`-Ըžbm4'^RY 7m? G7G-;ȗkR9,KueΔVk 2`f+D.hlб`4 vyvGnxHBI,ԷFN>,0N)3f0%A ԊF1tTz.3rA"MO#Bkn՚ 8Df/G %ES7ý3R=z<+RB_,&:mFݚqMms#Qt#\C3TH[]{: DY/ЖC1֢jk˭$Iv*Y@CeS\-bQ Cw4%`ozޛúy1h+]V`FiL-q}^!c!MiJYM5;AFZD;\QqIztR$T_S,OtZh+irZ'1rn74o|%Ug`Q5ofA^J ^U".?yhaaJQIGWj}e]t8(he.nnxcٞx13Q_oQ*eof£E<,a$`S&vd;oi8"sT7m<:p O!~TN*Q׺b JQ$"L9)E~'JRaECF*D;_W晀MS+fHC60>. 2 (Eh `u /-`OwO2KP $'ˑ9s3wJns6޻J(.]ˌ ECnu%Ɉ6KNݴ뽓 ujOkm xVHsx8wMF,FoE_h7"(JL5ɌWs[U1C iχrY1sbZ-Lb]68cKCd`MMHiߖqt!V@!̚j9t5͚02ٜ@ta]3~vs_paZ mrvq9F mJz :m7 L|eɳsA\'>c_ Zj;2pcDu3ZuiEOʣ6N|Ӛwp?Lʩ.\CAxI6-M'ҙJ;c0𳘘 k>$M 5a&\86l;)xק eujraSƃBd[̲#߮_(`/*l'jIP;뚭{g9\#VH~'q#ʐ#Qg"[a p_xREyeV|P)"b~XFɦZ ]l)/BX@l>Sina.=ߍ C\) t3Ykd[}B>3p:x]šw+-+3ql:hXN.`tU ^ ~$m>VL @ҭ'1 Cw:;&&&@kj(|Tp.>ނ EᄘXLFMK8OC)eVX[;ɵq٤M_gBc9ji4e ݳ# mz0V񡅷B#ޔz5 kڎrm)8;vrAjΣ7(a|sFY*gZJ55w dp yoTû+r |Hw\19B@6kM3JTmVI,Ͼ 9`d2B)ՄTO7%+i sJ@Բ@&A''M"IXX@IQio1Ȧ^79>uO\~_%;h&qtwdfF#&f]vs}'?ºOXܕ! y'6`;ox"pe|*UH໼ӻ~}hi6/@PTf,⮖ĖU%۾Īgj#'SojMrƁDL妧['&ad@l'%4-C/<73/}+{0jG6$$Zg-?=4ͼF~'LR 2]OOFcLN|d2wӣgϑV)FJ Nndw2݀@hOoZVc4{dC )-tABцVfb MW"ߘ GH)a)0Z.-wDdvZEQ;J(IŁ~a鯾̾R3TKA;@41-Zlop̿phc O\,s-^3uo4h#p܃05 I:fƾExWaM~ ]_bɊxlBk :uM x$b|`7jդ?;߉&bL r2"uFdnu`;vkeRol7RNF#A$rjB k즃O o&ok6|1UJPNtHͺ5:j \>Y9%,QeP;F&G{>IR& :׍\6JJOZlɂ7;3FMc2}s7N^xٳrx$7VQq >SQ߻.oyAN]oƓPH3P!B5/SF cۼns^oOhƪ1WB78lje^5qB~)ucoƱ0jcA`Z3hK!0mHa2337^ skl)j&"؄jX-~כ~+;7_im W!\ З:'M',Ve,-LXhz Fo:z7Oլ^I7`0@0hۀ!w{콾BV4 #!ۚX7ZO! ( :hV4 `5[̶}^cQdJ&֚Be'&('/*yԭ\SཉvX^oqbwFl+7}3AXHU%l '~ ɏhʼn t̬1zs=4R.u9'Fi,1>?~83+reS1WDvE#ϧ> &oLn9dG665k1-Zx3/~0ZR:Zdf,oIp9U1e>|-0TeJC',V*0@HKu4SLZδA-AsN֑ˬx^59=HMejH=-9l聚桧\+LOb &ٞc{ʹg_ /:ۀ l1KYݨvV 1FEr&}}~~[x=o16}j=-1TXydQOȑw @@=vhUn6LL$;n(Ȭ1t9mhBFLXجWO YzKYiKx-Ԍ}Nqb6GٱXgtmE6)ڲڴ m.*Oj! xg#$2oISiی~4qc9{S<$/l*wtnJz 2r-4uX.BqSV;3Kj[9d>~)Sifmc)fߐepf-_ZJe)  `<ѿ*'Vɶ1tKa@gSz2/q,R[ü<&6#8 ӂ6et4OR D>u{CSdۉZ򉳏.iK7CwŒ|Yd\Bg|MJAr&BVҒ;DR/E(_AP^뒂=ӿܷ[y~mBv놅;mݮ$ (y@%>s 2_{Z/5e /e>>[H/}m'ŏƴ{5ֱq(3H!ծ.V!|YDܻ2ZQMӵEiIQw`m, JrG2,P23G''O<:{ r秮/2⃁o؏xKa0{Hzc<qG/~?lpAe*ղQX|zyVfc5vEYIEȞ]X 4Dg<>'!Eжp j UF Ph"BˬPJA]%[]Ү? f^CXӪJ3v:kK 2Ӌ vrqܴ t/QXb`U*})yED Slt=̙S'c̓3IPp2cۀin#>~-:Q&GI @io=s߀ "I8 | BwAݼ+ - uI^n&,@j "Cd׺mwTOFt$?f2B1U, FI&_Apri 45`e +} rvrnxomsh(]㭮Sy2"F MpǿUAhs8*)8>p~ȔJ LPR~wzJ1o*؞cq ^m\ q|5gw6 ?ڕ4=3ضY?Q%v_\= 0Xa:AwS@ٸmC>r> Iu M7n8*reE<ޫe`4U:L%0 apeQe1JyR]T}Hu\5;S˽E2A o;0AU,AHu ~?}5 SN;'fIENDB`tests/native/gtk/000077500000000000000000000000001176363201700143015ustar00rootroot00000000000000tests/native/gtk/ex000_basic_usage.fal000066400000000000000000000027411176363201700201520ustar00rootroot00000000000000/* Falcon-Gtk Example 000 ====================== This is a very simple application demonstrating the basic usage of Falcon-Gtk. */ // Load the module. load gtk /* Initialize gtk by creating a 'main' object. This is mandatory for ALL gtk applications to run correctly! */ m = GtkMain( args ) /* Create a custom window by subclassing GtkWindow. */ class MyWindow from GtkWindow // a delete callback to quit the main loop. function on_delete_event() m.quit() return false end // a button-clicked callback that prints something. function on_clicked() > "Yay!" end // a constructor. init // set window title with 'set_title' method... self.set_title( "Falcon" ) // ... or set window title using a generic property setter. //self.set_property( "title", "Falcon" ); // connect 'delete-event' to the class 'on_delete_event' method. self.signal_delete_event().connect( self ) // make a vertical box vbox = GtkVBox() self.add( vbox ) // add a label lbl = GtkLabel( "Hello World" ) vbox.add( lbl ) // add a button btn = GtkButton.new_with_label( "Falcon!" ) // connect the button-clicked signal to the class 'on_clicked' method. btn.signal_clicked().connect( self ) vbox.add( btn ) end end // Initialize the window. w = MyWindow() // Show the window and run the main loop. m.run( w ) tests/native/gtk/ex001_GtkEventBox.fal000066400000000000000000000014401176363201700201010ustar00rootroot00000000000000/* Falcon-GTK Example 001 ====================== Demonstrating GtkEventBox, providing GtkImage a button-press-event. */ load gtk m = GtkMain() class MyWindow from GtkWindow init self.signal_delete_event().connect( self ) img = GtkImage.new_from_stock( GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_DIALOG ) box = GtkEventBox() box.add( img ) box.signal_button_press_event().connect( self ) self.add( box ) end function on_button_press_event( event ) // prints the coordinates of the mouse pointer > event.x, " ", event.y // (return true to block the event) return false end function on_delete_event() m.quit() return false end end w = MyWindow() m.run( w ) tests/native/gtk/ex002_GtkTable.fal000066400000000000000000000021471176363201700174040ustar00rootroot00000000000000/* Falcon-GTK Example 002 ====================== Demonstrating use of GtkTable. */ load gtk m = GtkMain() w = GtkWindow() // a callback for the window to quit the main loop function _quit() m.quit() return false end w.signal_delete_event().connect( _quit ) /* Sublcass GtkButton. */ class MyButton( row, col ) from GtkButton _row = 0 _col = 0 init self._row = row self._col = col self.set_label( @ "$row-$col" ) self.signal_clicked().connect( self ) end function on_clicked() > "Clicked ", self._row, " ", self._col end end /* Subclass GtkTable (and create a table of MyButton). */ class MyTable( numrows, numcols ) from GtkTable init self.resize( numrows, numcols ) i = 0 while i < numrows y = 0 while y < numcols b = MyButton( i, y ) self.attach_defaults( b, y, y+1, i, i+1 ) y += 1 end i += 1 end end end // Create a table of 25 buttons. tab = MyTable( 5, 5 ) w.add( tab ) m.run( w ) tests/native/gtk/ex003_GtkSpinButton_IP_Pinger.fal000066400000000000000000000021721176363201700223550ustar00rootroot00000000000000/* Falcon-GTK Example 003 ====================== Little application to 'ping' some IP address (unfinished!), demonstrating the use of a GtkSpinButton. (Credits to billykater.) */ load gtk m = GtkMain( args ) class MyWindow from GtkWindow button = nil spinner = [] function on_delete_event() m.quit() return false end function on_value_changed() ip = [] for i in [0:4] ip += self.spinner[i].get_value() end self.button.set_label( "Ping " + ".".merge( ip ) ) end init self.set_property( "title", "IP Pinger" ) self.signal_delete_event().connect( self ) box = GtkVBox() self.add( box ) hb = GtkHBox() for i in [0:4] spinner = GtkSpinButton( GtkAdjustment( 0.0, 0.0, 255.0, 1.0, 1.0, 0.0 ), 1, 0 ) spinner.signal_value_changed().connect( self ) self.spinner += spinner hb.add( spinner ) end box.add( hb ) self.button = GtkButton( "Test" ) box.add( self.button ) end end w = MyWindow() m.run( w ) tests/native/gtk/ex004_Stock_Items.fal000066400000000000000000000066661176363201700201470ustar00rootroot00000000000000/* Falcon-GTK Example 004 ====================== An application showing all the Gtk stock items. */ load gtk _main = GtkMain() stockItems = [ GTK_STOCK_ABOUT, GTK_STOCK_ADD, GTK_STOCK_APPLY, GTK_STOCK_BOLD, GTK_STOCK_CANCEL, GTK_STOCK_CAPS_LOCK_WARNING, GTK_STOCK_CDROM, GTK_STOCK_CLEAR, GTK_STOCK_CLOSE, GTK_STOCK_COLOR_PICKER, GTK_STOCK_CONVERT, GTK_STOCK_CONNECT, GTK_STOCK_COPY, GTK_STOCK_CUT, GTK_STOCK_DELETE, GTK_STOCK_DIALOG_AUTHENTICATION, GTK_STOCK_DIALOG_ERROR, GTK_STOCK_DIALOG_INFO, GTK_STOCK_DIALOG_QUESTION, GTK_STOCK_DIALOG_WARNING, GTK_STOCK_DIRECTORY, GTK_STOCK_DISCARD, GTK_STOCK_DISCONNECT, GTK_STOCK_DND, GTK_STOCK_DND_MULTIPLE, GTK_STOCK_EDIT, GTK_STOCK_EXECUTE, GTK_STOCK_FILE, GTK_STOCK_FIND, GTK_STOCK_FIND_AND_REPLACE, GTK_STOCK_FLOPPY, GTK_STOCK_FULLSCREEN, GTK_STOCK_GOTO_BOTTOM, GTK_STOCK_GOTO_FIRST, GTK_STOCK_GOTO_LAST, GTK_STOCK_GOTO_TOP, GTK_STOCK_GO_BACK, GTK_STOCK_GO_DOWN, GTK_STOCK_GO_FORWARD, GTK_STOCK_GO_UP, GTK_STOCK_HARDDISK, GTK_STOCK_HELP, GTK_STOCK_HOME, GTK_STOCK_INDENT, GTK_STOCK_INDEX, GTK_STOCK_INFO, GTK_STOCK_ITALIC, GTK_STOCK_JUMP_TO, GTK_STOCK_JUSTIFY_CENTER, GTK_STOCK_JUSTIFY_FILL, GTK_STOCK_JUSTIFY_LEFT, GTK_STOCK_JUSTIFY_RIGHT, GTK_STOCK_LEAVE_FULLSCREEN, GTK_STOCK_MEDIA_FORWARD, GTK_STOCK_MEDIA_NEXT, GTK_STOCK_MEDIA_PAUSE, GTK_STOCK_MEDIA_PLAY, GTK_STOCK_MEDIA_PREVIOUS, GTK_STOCK_MEDIA_RECORD, GTK_STOCK_MEDIA_REWIND, GTK_STOCK_MEDIA_STOP, GTK_STOCK_MISSING_IMAGE, GTK_STOCK_NETWORK, GTK_STOCK_NEW, GTK_STOCK_NO, GTK_STOCK_OK, GTK_STOCK_OPEN, GTK_STOCK_ORIENTATION_LANDSCAPE, GTK_STOCK_ORIENTATION_PORTRAIT, GTK_STOCK_ORIENTATION_REVERSE_LANDSCAPE, GTK_STOCK_ORIENTATION_REVERSE_PORTRAIT, GTK_STOCK_PAGE_SETUP, GTK_STOCK_PASTE, GTK_STOCK_PREFERENCES, GTK_STOCK_PRINT, GTK_STOCK_PRINT_ERROR, GTK_STOCK_PRINT_PAUSED, GTK_STOCK_PRINT_PREVIEW, GTK_STOCK_PRINT_REPORT, GTK_STOCK_PRINT_WARNING, GTK_STOCK_PROPERTIES, GTK_STOCK_QUIT, GTK_STOCK_REDO, GTK_STOCK_REFRESH, GTK_STOCK_REMOVE, GTK_STOCK_REVERT_TO_SAVED, GTK_STOCK_SAVE, GTK_STOCK_SAVE_AS, GTK_STOCK_SELECT_ALL, GTK_STOCK_SELECT_COLOR, GTK_STOCK_SELECT_FONT, GTK_STOCK_SORT_ASCENDING, GTK_STOCK_SORT_DESCENDING, GTK_STOCK_SPELL_CHECK, GTK_STOCK_STOP, GTK_STOCK_STRIKETHROUGH, GTK_STOCK_UNDELETE, GTK_STOCK_UNDERLINE, GTK_STOCK_UNDO, GTK_STOCK_UNINDENT, GTK_STOCK_YES, GTK_STOCK_ZOOM_100, GTK_STOCK_ZOOM_FIT, GTK_STOCK_ZOOM_IN, GTK_STOCK_ZOOM_OUT ] class MyTable from GtkTable init self.resize( 11, 11 ) i = 0; y = 0 for stokid in stockItems img = GtkImage.new_from_stock( stokid, GTK_ICON_SIZE_LARGE_TOOLBAR ) self.attach_defaults( img, y, y+1, i, i+1 ) if i == 10 i = 0 y += 1 else i += 1 end end end end class MyWindow from GtkWindow init self.set_title( "Stock Items" ) self.signal_delete_event().connect( self ) tab = MyTable() self.add( tab ) end function on_delete_event() _main.quit() return false end end w = MyWindow() _main.run( w ) tests/native/gtk/ex005_GtkTextView.fal000066400000000000000000000007431176363201700201370ustar00rootroot00000000000000/* Falcon-GTK Example 005 ====================== Quickly demonstrating GtkTextView... */ load gtk _main = GtkMain() class MyWindow from GtkWindow init self.signal_delete_event().connect( self ) buf = GtkTextBuffer() buf.set_text( "Falcon", -1 ) view = GtkTextView.new_with_buffer( buf ) self.add( view ) end function on_delete_event() _main.quit() return false end end _main.run( MyWindow() ) tests/native/hpdf/000077500000000000000000000000001176363201700144355ustar00rootroot00000000000000tests/native/hpdf/samples/000077500000000000000000000000001176363201700161015ustar00rootroot00000000000000tests/native/hpdf/samples/arc_demo.fal000066400000000000000000000022101176363201700203310ustar00rootroot00000000000000import from hpdf import printGrid from .grid_sheet as printGrid pdf = hpdf.Doc() // add a new page object page = pdf.addPage() page.setHeight(220) page.setWidth(200) printGrid(pdf, page) // draw pie chart // A: 45% Red // B: 25% Blue // C: 15% green // D: other yellow // A page.setRGBFill(1.0, 0, 0) page.moveTo(100, 100) page.lineTo(100, 180) page.arc(100, 100, 80, 0, 360 * 0.45) pos = page.getCurrentPos() page.lineTo(100, 100) page.fill() // B page.setRGBFill(0, 0, 1.0) page.moveTo(100, 100) page.lineTo(pos.x, pos.y) page.arc(100, 100, 80, 360 * 0.45, 360 * 0.7) pos = page.getCurrentPos() page.lineTo(100, 100) page.fill() // C page.setRGBFill(0, 1.0, 0) page.moveTo(100, 100) page.lineTo(pos.x, pos.y) page.arc(100, 100, 80, 360 * 0.7, 360 * 0.85) pos = page.getCurrentPos() page.lineTo(100, 100) page.fill() // D page.setRGBFill(1.0, 1.0, 0) page.moveTo(100, 100) page.lineTo(pos.x, pos.y) page.arc(100, 100, 80, 360 * 0.85, 360) pos = page.getCurrentPos() page.lineTo(100, 100) page.fill() // draw center circle page.setGrayStroke(0) page.setGrayFill(1) page.circle(100, 100, 30) page.fill() pdf.saveToFile(scriptName + ".pdf") tests/native/hpdf/samples/character_map.fal000066400000000000000000000072711176363201700213650ustar00rootroot00000000000000import from hpdf function drawPage(pdf, page, title_font, font, h_byte, l_byte) PAGE_WIDTH = 420 CELL_HEIGHT = 20 CELL_WIDTH = 20 l_byte = int(l_byte / 16) * 16 h_count = 16 - (l_byte / 16) page_height = 40 + 40 + (h_count + 1) * CELL_HEIGHT page.setHeight(page_height) page.setWidth(PAGE_WIDTH) page.setFontAndSize(title_font, 10) ypos = h_count + 1 loop y = (ypos) * CELL_HEIGHT + 40; page.moveTo(40, y) page.lineTo(380, y) page.stroke() if ypos < h_count buf = chr(16 - ypos - 1) if ord(buf) < 10 buf = buf/ord('0') else buf = buf/(ord('A') - 10) end w = page.textWidth(buf) page.beginText() page.moveTextPos(40 + (20 - w) / 2, y + 5) page.showText(buf) page.endText() end if ypos == 0: break ypos-- end for xpos = 0 to 17 y = (h_count + 1) * CELL_HEIGHT + 40 x = xpos * CELL_WIDTH + 40 page.moveTo(x, 40) page.lineTo(x, y) page.stroke() if xpos > 0 and xpos <= 16 buf = chr(xpos - 1) if ord(buf) < 10 buf = buf/ord('0') else buf = buf/(ord('A') - 10) end w = page.textWidth(buf) page.beginText() page.moveTextPos(x + (20 - w) / 2, h_count * CELL_HEIGHT + 45) page.showText(buf) page.endText() end end page.setFontAndSize(font, 15) ypos = h_count loop y = (ypos - 1) * CELL_HEIGHT + 45 for xpos in [0:16] x = xpos * CELL_WIDTH + 40 + CELL_WIDTH buf = " " buf[0] = h_byte buf[1] = (16 - ypos) * 16 + xpos w = page.textWidth(buf) if w > 0 page.beginText() page.moveTextPos(x + (20 - w) / 2, y) page.showText(buf) page.endText() end end if ypos == 0: break ypos-- end end if args.len() < 2 > "usage: character_map " exit(1) end pdf = hpdf.Doc() /* configure pdf-document (showing outline, compression enabled) */ pdf.setPageMode(hpdf.PageMode.USE_OUTLINE) pdf.setCompressionMode(hpdf.COMP_ALL) pdf.setPagesConfiguration(10) pdf.useJPEncodings() pdf.useJPFonts() pdf.useKREncodings() pdf.useKRFonts() pdf.useCNSEncodings() pdf.useCNSFonts() pdf.useCNTEncodings() pdf.useCNTFonts() encoder = pdf.getEncoder(args[0]) if encoder.getType() != hpdf.EncoderType.DOUBLE_BYTE >"error: ", args[0], " is not cmap-encoder" end font = pdf.getFont(args[1], args[0]) min_l = 255 min_h = 256 max_l = 0 max_h = 0 flg = arrayBuffer(256) flg.fill(0) for i in [0:256] for j in [20:256] buf = " " code = i * 256 + j buf[0] = i buf[1] = j btype = encoder.getByteType(buf, 0) unicode = encoder.getUnicode(code) if btype == hpdf.ByteType.LEAD and unicode != 0x25A1 if min_l > j: min_l = j if max_l < j: max_l = j if min_h > i: min_h = i if max_h < i: max_h = i flg[i] = 1 end end end > @ "min_h=$(min_h:04X) max_h=$(max_h:04X) min_l=$(min_l:04X) max_l=$(max_l:04X)" /* create outline root. */ root = pdf.createOutline(nil, args[0]) root.setOpened(true) for i in [0:256] if flg[i] page = pdf.addPage() title_font = pdf.getFont("Helvetica") rstart = i * 256 + min_l rend = i * 256 + max_l buf = @ "0x$(rstart:04X)-0x$(rend:04X)" outline = pdf.createOutline(root, buf) dst = page.createDestination() outline.setDestination(dst) drawPage(pdf, page, title_font, font, i, min_l) buf = @ "$(args[0]) ($(args[1])) 0x$(rstart:04X)-0x$(rend:04X)" page.setFontAndSize(title_font, 10) page.beginText() page.moveTextPos(40, page.getHeight() - 35) page.showText(buf) page.endText() end end pdf.saveToFile(scriptName + ".pdf") tests/native/hpdf/samples/encoding_list.fal000066400000000000000000000061131176363201700214070ustar00rootroot00000000000000import from hpdf PAGE_WIDTH = 420 PAGE_HEIGHT = 400 CELL_WIDTH = 20 CELL_HEIGHT = 20 CELL_HEADER = 10 > "WARNING: This example doesn't work correctly yet!" > "The falcon string encoding seems to interfere, " > "so MemBuf support has to be added to hpdf text output functions." function drawGraph(page) // Draw 16 X 15 cells // Draw vertical line page.setLineWidth(0.5) for i = 0 to 17 x = i * CELL_WIDTH + 40; page.moveTo(x, PAGE_HEIGHT - 60) page.lineTo(x, 40) page.stroke() if i > 0 and i <= 16 page.beginText() page.moveTextPos(x + 5, PAGE_HEIGHT - 75); num = i - 1 buf = @"$(num:X)" page.showText(buf); page.endText() end end // Draw horizontal lines for i = 0 to 15 y = i * CELL_HEIGHT + 40 page.moveTo(40, y) page.lineTo(PAGE_WIDTH - 40, y) page.stroke() if i < 14 page.beginText() page.moveTextPos(45, y + 5) num = 15 - i buf = @"$(num:X)" page.showText(buf) page.endText() end end end function drawFonts(page) page.beginText() // Draw all character from 0x20 to 0xFF to the canvas for i in [1:17] for j in [1:17] y = PAGE_HEIGHT - 55 - ((i - 1) * CELL_HEIGHT); x = j * CELL_WIDTH + 50; shift = (i - 1) * 16 + (j - 1) buf = chr(0)/shift if ord(buf) >= 32 d = x - page.textWidth(buf) / 2 page.textOut(d, y, buf) > shift > buf end end end page.endText() end encodings = [ "StandardEncoding" // "MacRomanEncoding", // "WinAnsiEncoding", // "ISO8859-2", // "ISO8859-3", // "ISO8859-4", // "ISO8859-5", // "ISO8859-9", // "ISO8859-10", // "ISO8859-13", // "ISO8859-14", // "ISO8859-15", // "ISO8859-16", // "CP1250", // "CP1251", // "CP1252", // "CP1254", // "CP1257", // "KOI8-R", // "Symbol-Set", // "ZapfDingbats-Set" ] pdf = hpdf.Doc() // set compression mode pdf.setCompressionMode(hpdf.COMP_ALL) // Set page mode to use outlines pdf.setPageMode( hpdf.PageMode.USE_OUTLINE) // get default font font = pdf.getFont("Helvetica") // load font object font_name = pdf.loadType1FontFromFile("type1/a010013l.afm", "type1/a010013l.pfb") // create outline root root = pdf.createOutline( nil, "Encoding list", nil) root.setOpened(true) for encoding in encodings page = pdf.addPage() page.setWidth(PAGE_WIDTH) page.setHeight(PAGE_HEIGHT) outline = pdf.createOutline(root, encoding) dst = page.createDestination() dst.setXYZ(0, page.getHeight(), 1) outline.setDestination(dst) page.setFontAndSize(font, 15) drawGraph(page) page.beginText() page.setFontAndSize(font, 20) page.moveTextPos(40, PAGE_HEIGHT - 50) page.showText(encoding) page.showText(" Encoding"); page.endText() if encoding=="Symbol-Set" font2 = pdf.getFont("Symbol") elif encoding == "ZapfDingbats-Set" font2 = pdf.getFont("ZapfDingbats"); else font2 = pdf.getFont(font_name, encoding) end page.setFontAndSize(font2, 14) drawFonts(page) end // save the document to a file pdf.saveToFile(scriptName + ".pdf")tests/native/hpdf/samples/encryption.fal000066400000000000000000000011271176363201700207600ustar00rootroot00000000000000import from hpdf import printGrid from .grid_sheet as printGrid text = "This is an encrypt document example."; owner_passwd = "owner"; user_passwd = "user"; pdf = hpdf.Doc() // get default font font = pdf.getFont("Helvetica") page = pdf.addPage() page.setSize(hpdf.PageSize.B5, hpdf.PageDirection.LANDSCAPE) page.beginText() page.setFontAndSize(font, 20) tw = page.textWidth(text) page.moveTextPos( (page.getWidth() - tw) / 2, (page.getHeight() - 20) / 2) page.showText(text) page.endText() pdf.setPassword(owner_passwd, user_passwd) pdf.saveToFile(scriptName + ".pdf") tests/native/hpdf/samples/font_demo.fal000066400000000000000000000031161176363201700205400ustar00rootroot00000000000000import from hpdf fontList = [ "Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique", "Helvetica", "Helvetica-Bold", "Helvetica-Oblique", "Helvetica-BoldOblique", "Times-Roman", "Times-Bold", "Times-Italic", "Times-BoldItalic", "Symbol", "ZapfDingbats" ] page_title = "FontDemo"; pdf = hpdf.Doc() page = pdf.addPage(); height = page.getHeight(); width = page.getWidth(); /* Print the lines of the page. */ page.setLineWidth(1); > 50, " ", 50, " ", width - 100, " ", height - 110 page.rectangle(50, 50, width - 100, height - 110); page.stroke(); // Print the title of the page (with positioning center) def_font = pdf.getFont("Helvetica"); page.setFontAndSize(def_font, 24); tw = page.textWidth(page_title); page.beginText(); page.textOut((width - tw) / 2, height - 50, page_title); page.endText(); /* output subtitle. */ page.beginText(); page.setFontAndSize(def_font, 16); page.textOut(60, height - 80, ""); page.endText(); page.beginText(); page.moveTextPos(60, height - 105); for i in [0:fontList.len()] samp_text = "abcdefgABCDEFG12345!#$%&+-@?"; font = pdf.getFont(fontList[i]); /* print a label of text */ page.setFontAndSize(def_font, 9); page.showText(fontList[i]); page.moveTextPos(0, -18); /* print a sample text. */ page.setFontAndSize(font, 20); page.showText(samp_text); page.moveTextPos(0, -20); end page.endText(); pdf.saveToFile(scriptName + ".pdf") tests/native/hpdf/samples/grid_sheet.fal000066400000000000000000000037461176363201700207140ustar00rootroot00000000000000import from hpdf function printGrid(pdf, page) height = page.getHeight() width = page.getWidth() font = pdf.getFont("Helvetica") page.setFontAndSize(font, 5) page.setGrayFill(0.5) page.setGrayStroke(0.8) /* Draw horizontal lines */ y = 0; while y < height if y % 10 == 0 page.setLineWidth(0.5) else if page.getLineWidth()!= 0.25: page.setLineWidth(0.25) end page.moveTo(0, y) page.lineTo(width, y) page.stroke() if y % 10 == 0 and y > 0 page.setGrayStroke(0.5) page.moveTo(0, y) page.lineTo(5, y) page.stroke() page.setGrayStroke(0.8) end y += 5 end /* Draw virtical lines */ x = 0; while x < width if x % 10 == 0 page.setLineWidth(0.5) else if page.getLineWidth()!= 0.25: page.setLineWidth(0.25) end page.moveTo(x, 0) page.lineTo(x, height) page.stroke() if x % 50 == 0 and x > 0 page.setGrayStroke(0.5) page.moveTo(x, 0) page.lineTo(x, 5) page.stroke() page.moveTo(x, height) page.lineTo(x, height - 5) page.stroke() page.setGrayStroke(0.8) end x += 5 end /* Draw horizontal text */ y = 0; while y < height if y % 10 == 0 and y > 0 page.beginText() page.moveTextPos(5, y - 2) buf = @ "$(y)" page.showText(buf) page.endText() end y += 5 end /* Draw virtical text */ x = 0; while ( x < width) if x % 50 == 0 and x > 0 page.beginText() page.moveTextPos(x, 5) buf = @ "$(x)" page.showText(buf) page.endText() page.beginText() page.moveTextPos(x, height - 10) page.showText(buf) page.endText() end x += 5 end page.setGrayFill(0) page.setGrayStroke(0) end if vmIsMain() pdf = hpdf.Doc() /* add a new page object. */ page = pdf.addPage() page.setHeight(600) page.setWidth(400) printGrid(pdf, page) pdf.saveToFile(scriptName + ".pdf") endtests/native/hpdf/samples/image_demo.fal000066400000000000000000000061511176363201700206560ustar00rootroot00000000000000import from hpdf function showDescription(page, x, y, text) page.moveTo(x, y - 10) page.lineTo(x, y + 10) page.moveTo(x - 10, y) page.lineTo(x + 10, y) page.stroke() page.setFontAndSize(page.getCurrentFont(), 8) page.setRGBFill(0, 0, 0) page.beginText() buf = "(x=" + x + ",y=" + y + ")" page.moveTextPos(x - page.textWidth(buf) - 5, y - 10) page.showText(buf) page.endText() page.beginText() page.moveTextPos(x - 20, y - 25) page.showText(text) page.endText() end pdf = hpdf.Doc() pdf.setCompressionMode(hpdf.COMP_ALL) /* create default-font */ font = pdf.getFont("Helvetica") /* add a new page object. */ page = pdf.addPage() page.setWidth(550) page.setHeight(500) dst = page.createDestination() dst.setXYZ(0, page.getHeight(), 1) pdf.setOpenAction(dst) page.beginText() page.setFontAndSize(font, 20) page.moveTextPos(220, page.getHeight() - 70) page.showText("ImageDemo") page.endText() /* load image file. */ image = pdf.loadPngImageFromFile("pngsuite/basn3p02.png") /* image1 is masked by image2. */ image1 = pdf.loadPngImageFromFile("pngsuite/basn3p02.png") /* image2 is a mask image. */ image2 = pdf.loadPngImageFromFile("pngsuite/basn0g01.png") /* image3 is a RGB-color image. we use this image for color-mask * demo. */ image3 = pdf.loadPngImageFromFile("pngsuite/maskimage.png") iw = image.getWidth() ih = image.getHeight() page.setLineWidth(0.5) x = 100 y = page.getHeight() - 150 /* Draw image to the canvas. (normal-mode with actual size.)*/ page.drawImage(image, x, y, iw, ih) showDescription(page, x, y, "Actual Size") x += 150 /* Scalling image (X direction) */ page.drawImage(image, x, y, iw * 1.5, ih) showDescription(page, x, y, "Scalling image (X direction)") x += 150 /* Scalling image (Y direction). */ page.drawImage(image, x, y, iw, ih * 1.5) showDescription(page, x, y, "Scalling image (Y direction)") x = 100 y -= 120 /* Skewing image. */ angle1 = 10 angle2 = 20 rad1 = angle1 / 180 * PI rad2 = angle2 / 180 * PI page.gSave() page.concat(iw, tan(rad1) * iw, tan(rad2) * ih, ih, x, y) page.executeXObject(image) page.gRestore() showDescription(page, x, y, "Skewing image") x += 150 /* Rotating image */ angle = 30 /* rotation of 30 degrees. */ rad = angle / 180 * PI /* Calcurate the radian value. */ page.gSave() page.concat(iw * cos(rad), iw * sin(rad), ih * -sin(rad), ih * cos(rad), x, y) page.executeXObject(image) page.gRestore() showDescription(page, x, y, "Rotating image") x += 150; /* draw masked image. */ /* Set image2 to the mask image of image1 */ image1.setMaskImage(image2) page.setRGBFill(0, 0, 0) page.beginText() page.moveTextPos(x - 6, y + 14) page.showText("MASKMASK") page.endText() page.drawImage(image1, x - 3, y - 3, iw + 6, ih + 6) showDescription(page, x, y, "masked image") x = 100; y -= 120; /* color mask. */ page.setRGBFill(0, 0, 0) page.beginText() page.moveTextPos(x - 6, y + 14) page.showText("MASKMASK") page.endText() image3.setColorMask (0, 255, 0, 0, 0, 255) page.drawImage(image3, x, y, iw, ih) showDescription(page, x, y, "Color Mask") /* save the document to a file */ pdf.saveToFile(scriptName + ".pdf") tests/native/hpdf/samples/images/000077500000000000000000000000001176363201700173465ustar00rootroot00000000000000tests/native/hpdf/samples/images/gray.jpg000066400000000000000000000662021176363201700210200ustar00rootroot00000000000000WANGZJFIF,,C      @ }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz?M(}<*hHn# 7].UB3+_Jx#j'Kh7X#X ]15o)5Y`MIkV8%Hl0/Y N +,$dc7k4FO܂$g@dv, oLsJ쯱qd/lu ?zT3ܬ9ҡNceF9pJ7:q²5s PYpF?V9uyaK͆Cq̲JC8#؍20+o5JSuBNA] P~P^gd4i7[&_1T : O_(ZQpg>ʱ, )ft'}0{Sa+(fܟb<灸  w |9蚦텴'70{-{;Gùt]ZX;BvL0[,[*rϋ-Cp nˑ:į\xG _,p^  r$9=둸rʘ pZ$zM' ZyX@;ziNF3sM).b*2};R<aP-1'4@!u*㒣{~1] ?ƫܬʴjۂȥ$2ft ^99zrˆL',4I* W4\.܍}jM.5Fvw.3_zc߅SH=ZjveƓ${Jɸgz #@ w8 %D xnj_t?vK)-kW K'1p3@ zt> `|kq>+8mqyXЈpp{}'5^!/l%c#j^А6lN\Wfwv`7|y%Xѷ=~V%j$sl@:U!]w3zaBpx e.t`3vHL OS2'_oQ7.\Z Vcu  ^׾)x͎jVpBXKga$qx.@B1|/ ޴Pi0FdDI34COZx2Zun[BIsq\@P[G|z M%LɸG$ztӞt٬##O09 DC%Ng:MK"Q=*So\Z L7䎛A1jƁp ߱`#l8/$-r]َqt5j\Q)mě~S =OS*ÿq,z:TpڵR4k?NҚ;#X5w1I-x '{qkF~gm>(| ,}1SК6$\p7 \VYxs_Hգ̖TBӱq >u$#u,4%`c :IRyLcr`#9{=elu__ZT)"8ÃrFMr zqr]x?67UBx$gA2-}r=n1;F>PO__X VeeH#?yOpkɯt>,+2 ؕYJ,*.2kj#$0gp[?xvW[dV1Gk7m9D2~`9{'B}4ZxWSԦ^r,I8UAd?^Fo6}66M/"FF7nf$>Mo xVyor\@sg8S'eP8>}-e0RNsQZpIw% LC秧Ҩ4[,lK{O͜|~uӼ;Ga}4w"1;6 Gc%zwŽJ5-.i.^B``\YTHeSY6Zr&$_$;r Q~5a6iSz`ЊtEڳ,rW8r;یV) }Oj|!K2Zk%ĩ3nP۷סz/iM]C%8)1팦G5 4kƚ '88ʌo0nx\lI~wnŘٷNyɯ]$iD-,ctf9=w2<1м ltJ-E$_ʒ fjdRl ǿt*m#Mde2w:'(_87Om<䌌q. Bn&yn"FH?{>]j[a V>˪:B8rTrp?6WT,GW?ΥռEj6n&%w2ɵv ^xoz,5Qvڮ$p!G9q^_Px-j]NeuO!u8 0.OUk~O=gF%y%eMT4Jwv#I %'Mb́y$1ԞI`48b`:]&sayhIAD6yk{ft#*?)%+qߑWX'/z5}F RBCv1xr(𮇣]jeՖPkxuBPGsDsJl82=k=.Co2\`[|D:`v'oO~|Vjv)^K\ETLd }e=*b$t' P(vNt}O_²2Jttcӎ+ULUbXl}sU][;+i.oe!" *|nqPJl $?aRi4| dےl_/Zn而ɮrGVWs~]%d$ޡ ď@9p1 cokVϊt?K[M42+q{ %;g?5ƫ[I<֢ +gqs>VR&W,@' I\CcktMq}4|cž忂&QotсMG.f?; 5gP/$n[2+rmۆpHl}X` C: k"@l 5Mee0`qxf=Is=k 7qаs\Rsw+1I$Yn'95Zh&XCd)cP=B.2,=qƥBܣM#y1FɆ›k&Eg k4rmsL݆*֔WE+m'*a> MTa2`чg^AG/N细&him rNu݀N?M迳_/-ougtf7DsH'0:ҧ zgb`TGUֶ<9AE+{OSXGHnͲRA{{[Oٍ? Ԯ`ʙ1v8ǮqC>_spEhP^ }+sjI-֘7QHUE sߝkh<[;[[3m,spgbNI,ǯs׫?<[b݈ 2Wy',^m{vaC?ʘ~VY$0]A*ea#6g@! A?n*]FW!ÁYj &նd__h~kXo*؎) dH0qFR+:֧isі "r0;zc8!I>Hb#ZP3 YP-'}+[J-%ǖ <7,,qH Wxc:3<.Ixt ԜԾ-nҫ$2'ֺ_1ZHi|\v!ץp$ί >8ʨ$#뚆j}Ylo]N\]gVKh mϩ$e$} ~$ҤhA)gT΁eKp$H8'{~U8K*De'qKQk46BHEWAg{`Ƞzt# "|5zv/9Q122AxsAԼWc&i ȬHAڅ81|:o|kv7' 2E>?^}J{RCRNfu+Kuu`YUPyBGH2Ao<$!f@<_OJ[i~Y,K̽;Ǔ@01׫1e eV@[yJ##cs5ꗞ!4m-r2mоT?p7É ̨첧R@SGo~8gMZXLAܧs=lNN[nJӯV/%{ky8.YO^?׊`8ap9c5{27X4'`SڪMsm+@):Tfmoqj:88 wWq]HU.b*FvRz^M5d"HFGr1Onk$mٕXX$Y1,jY~RdסzB#+FQn6FKxwE(vc$/= iHn1A rz#{wcxVt'<9V]Y !ebt$$'n;ʹNY8ʌUs+.r;<vB ֓< 1s?θe!19?R.%,ӊ wQoMyo;FF2;Fzr*8Zar Nm7X$>=玝OәfGU Fn^1סkV:u8D>^S ( ^\?P*2,!h\ nf[JsGpnB<ǾjaH1޼^qH35~휫H3 nF8Sw4zFܴ&g9r ?OrV ctR@ߞ:p{o3-/ 3Ͻ%A$-'JR[Up(~;$1WWPfeC ec=;Sԋ(F @< `z8!; 6ՑYs͐1'<ێWqo`cIff?1=ҾA! 2 uN{t%w*I^7IIn=3ۭmk&m=fKp7!(Q@woEp*GÂH'9$~_#m$ާ!IʐH׌+QT=`{ g [w̪0INA'O KqiS#~WgNQqp\arGF6db/ |:ԵoX]W1|R;fyq^ ]"7=g#syǩ~h7yt3y^8`כXUn$*XVxG|8޺ E8TbNGև&.OOz¦,6di}>^|>2hr\1 #~ARy}kAz̐d^c2ntBI8bKAh}H'?^\Fkj:Ia2,qx \}}K8Mhq S'=+d_kalR189uZI\09=TOBӢr9f9In(*;䯖#ޮT-ys30zwO+('@28j/|G\[qbcR1\ďVf^ ocq?U\g3+ 0Кx ,簒C@nC÷׊(iRˈ&ERBH>+/IG]4#$`398ܸ|2Z;O)V̅NU'I\IwyovKOE8gַխ r@I܄x,u|W@O_C]TM0Yw9fbW= {aV NIsss~%bw %@>o>9Y$={TX 2E*6盚&{iYO0~o`q85xZ0ª+ُʒd|#s_/z _QER)Vm5+ޡ$0[݇m`0I叩4ٵ/X˟F3jW-z(>=# |Y8; ᵷ|MA:Xc$ T1ws~jZf5ܟD /:џ<{Xt6G".&'ih'ǧvhKhAXq{yⶾ&;u_NA둓obv%]rcgߒY8E1ưÂqc`ImM?=A.K&|Ȃ9%V CHc5꺭o/b|3^-8nVS@ůx>T\i: +! 8V=yd⏄ο?MtZmDAޤO')i[_oĒC,H#V|POxt8穯>?IyH(ʼhc%9_~um9eb0:}kxj=+7wzVr w3|çr:f[>:1Zr<"L|sg礰*9 7(('#³u}e.dBYY@p:`>#7Ғi-D(#$ǾsVÿj8V >W3s0GO\c=j<{oi6dG($qLE{^o44K@^r' H`r>'Tzߤx[C|4~cj3ElKIf*wgy=?yJ Zӵ]6K !$be|cO<i.%>D,ci X{? ;>4DMTah$}!,JO!t"?Z>dUY nvtQ| =Z[ʏ r2J#yn'UD74la?Zռ{ʞM+FzH9SĖ~!5K&^DVdcx>kwmNPjWA yc$8< [idr3ïqz>Xv&؏`cq?xt{Ԙ݆7pI+.d"ۼ `Azs)<<Bqi甦q1䜑׮AW[2HmwC,qSz0nqc܎#WhO'$ckf)ReP #={+դ2ve'vXp9;w>Fv2@c/#O׾g{Q$O#:$tuϽ|wqĺl0g2Y!hB+<[&^OanI3Jn*q-+3%7`q }zυm|QxKĒ.3##y6~*VZx|Ie{(Ö<+=WEi-Z7J/ʥ:I8둟?oRkH%esg{xSMe EHY.Hqڻ; iW0]Xڃl2pdOόu ~4cH] @xd݂`u>0;V}[G༱MFؖ#.3pې0 Sg&&MޫdA9ڗOӴMy6ϵmż)켆8~i_z=naVvS3 ]D6i7X!F[%PMŤ1LA϶: /zh0p28>.E#4``ܼ(EA 8$ZĿz!kmdypdZa&3w^F;ךKlI!A+*[#w=ƚLK)7TRvVEJ6#,BnD/t|q {׏kRo1*UF̀3>k>kl2Gz>ԟ-951T[;Kskl)|\ m `܃T/es|~HY7>; K žr3^A qp|i@=Ls$qL22 ~QY7[,It–cPA?,-ܒ8"8-Iw8O֯a-x'PH1=<ӚOI9z![}^ɮ8Mr9Ϸ# |ɠzAnvl8z<,n-%8}8?/iI4ZKlcU-?gJj[ ;/$ x<ۑf$SrTIDvrOZzmw'1UX-#`U;R`PNKllqvNڬpWSXn8WKū\8XXFyNj>"%fq35Gq{eC+ݸ6kfb~캯wejC Ly 9s_{)s ZT'Mq?΃>dcʱ_2kj('#ڵ<)C51O^*"5Aڬ@>a1''?u>Y3@O^q>jcb"ЧnN8V(5R|9AQt rĐgkHPHHq7NG$^ L!>aYyHF; sn[h `(ǯ^:zчVfRO=ᥚX :1p+>Z?G/i8ޱ &,mA{^1pM젞cSҾ)^+oZMj(*Tx{C-Ώ ,/ecppcGz~$ү%u0J6 rG|5! ݲ$fkI11#ss|iphXe{ϭ{{I$sZ{[W;|C^{axZ;G$z0yx5C:3'Y#R=۟dڵ쑣PtUG+2Hx>&<*8vO\q}6?JJ,̀=JI}/zFHr0^a*;_C(`U&h́Yc;WB""5'#zwȫq4LPGvVn!| ZA+Ӵ;o Us2H׶+Qiu1KvH=1Uݤv,QbFJ-sϾ@+3Q $!NpǕ0k5 \n s}Oq Ȧ; ebHn93ZOYټo0b>\I#=>@Ѕ̩8Z\ ~ӬXdcP?y{h ڨsҪ&8 MQN4qFbW0H{+#zY]V"&Szݗ\sm#ڬ[y#ֽᜑAE>+u MFrĖr8߷Q*ͻO- TNBPё &]ej:Ėrd'!F`t"kk;8?1c\}zBx[D\0$0_>x$e2Hw8ӊG93FA#3=f',-UNG1z>{[iLENDuGiRccy~W1'w Y̫Rtt898'.ϟlga" Nɾe]U bSN9P˳(bU]j6HN:֤ʌ=g+=oxCoզJU$gfWXp3?NzWnۆqkXydiObĆXS׎׹kA*LP׵r~"V'$;0G_̌MAFP?+ܼ70 ,@3 z9AF AAA9qTCV,X$ָOisB3&fV&N9#p$4>c}I県r^WZU#Ib'-1ֺYePz^^$@VNΣv|}qZv1]Y"oA n2n}7zb *G+L1'u-W[dž]ydP%5ܸ-Ws-U,r >LsUZ#mp@>S ^RK1ۊ6Y!'p@PV.(Ӗh\ttc I?9 f5̬FʬG4H žM}cx~#kGj4RʠlnV9c޾Eu.S{apAUsaآ6v1>}%x|X4nc\v F?^k_'=3*veU~V%"nFYy6Fq'CYq}x2&郟Z]-6T>9&hycdXdq F3A#wH`^:$3*FT=$ca"Kod-Jl +t^9϶3ym%弍<)z,:NZ8471sۊnPKg.dib9#~vYU3(V^ gb.r1vPo+"ݖ!apHdM#o=r=Sl$H:+GVp{9#c#skpXlЏid0'fy'ߟƈ"56&TRI4Ԉoe!2BDI6pO8՛#nWY.gv$W2G^}o^:> m& n pV '$ݖsm 7R[i,q<~FWd6Z3\JDpL/AȾ)x2M[횒Eh\@X;Gd|yHr!fdU{-%'giGP(, G^?zĕb,힘lO~$%`gsvAWu[ J'vqqQ7q n4la'0 rXcZRn],1W$래3:3ϸ>9$1Fxޗu{o w gd/$rN}:{%Б%r<QRz}|RD2$OkOqd?0qdqV>ѷ|"1gBڗ/j)rFFGO4Rp9c 7UrsRL(H2-I3GCʸnVTq#3|GS ]I A03O>KTBXWbȗpw.85i$f+\g< p.Ы| UbYM#fNYp *z/x~,ң'EÀB;Al}Yam:mQ$W1_]mCao/r3 W)MONnQi B23:C 3_(UWh,N2X=b&ֹX#I`P$qx'5_C[R[I (dl;$3ooZoEH7˖'<F3:]6cHʆF͜t)k4k@$@X܋1rge=0GLW!7٥  ##qxkbH-4d }y]}cIT[p׮>p8#k#JB 'srk[9M^n `I˼qS=ݝt'SND;]ڻ*0UzaGs{ԏMv[IhRvH dVjc¦}5l`h$<^?tEn-(A?1vիceunjnmU;J?έ,q͒b8 OzRi$W>cߝW$3>`er:gxַ+ɜ "zJtܵ)'Lgz{U>[{([\F|c}D6 isjʈ GBrqw>ko2H20i9[w p8T|luHH&TɛЀ2>] 23O$Cn8m9-ٸ䑟Ng&_)u-:N8::C:Fm3ۃ׶skt<V;1AXC/~f <~kSP%c8`$3c;Qp' 91_0\;2CqHC).vZ ev=*!Kdy<6|g<{92$w6J1}(CJ&I(qqxo7hX̌ 8>*ݝV"TH9ZX(e&%— 秠WKI !T rm!qdqW[ G󴑒;94 '2鍡TKzc$Qq#/ xMiXY"-33bCяQ>r:7$,+ncR6܄27z{ִNHfXV<֭Zr8 >Rxb AHYq:}yYE y"@B1O^뻉m!syH#tVaw'#;q&$z'8=[ϔ}ga$k讬E+.V=j巕4N#Fy-g/%koL dkWP!q8${VswU-LaS@;梽&Pこ~~qq`gOOŲc8䎇E_Z34,噦udX8@۽g^uQ)aV-2>*1ujݵG,O1. $5DHޅ "brGPje@B\Ff9յiV7"E}K*TrǀqҾYG؁`zG~?_X:PzֹvYV_/k3w=ZKR}xx8YR; /|G+}JȵPyN0n׽+OMu-ms8&OrI?^82)gpsۯji"Wp!x'_υEgbvUE pcn<1=9 F^R*յܪp!c-wD N'w@;W3}ʷY1*6e1!G:gm5VF)m` 9?R[mn- Hd/;SԚV;Iy\ y-M鏯Z[^yH.Jg8zgjTU%: |:am1㫍.++4K 2 X#QNJONndx #v+SQ)l.r%9n̾=NhEE I$\lt#%Xd:subVB+)Q?Qxm>)$UV .:?:ĖiK eTt8>;z> xi8f?uؒ Uqܕ5s4lfEU bOLVl/bW v(VFt#y9MA/\U\85W#ُ&2#Z˶# X֭JmVHJn?w ׂx< [VC=̪ )=O59(Y#,$95kkveI><*|:y.3?Ъn≮-`X&(p:<{6W~BGxft.T+=k߈^W8|q_!'=;~uk{itDLyV)#8=:ԗ+D ,}=j9d{J'#{n~O4stL[z<sq~ i]ђH7xMZQk w(#Uk >kK|1GPϩ1PRB}[/ilwI*f/bLׯ=ahYkRWq*e $݀1~U$3N~XЋ)#V$0$g'Z6i T6SvKǺ\3N%ەe8ΰ>[ M\ܴ,T2w}pzzόm<ٯ˻7z~8WL.@*8,8=kWT7.Kcq22KrUb10Ĥ61~wD'"7mc:OҴm2{MeDkt]|ܖZ4&yy;2F-9=8>!x $<{Q&e1Yy7 1!:v9sjk 7>\Spq ~t5D3( G<}4L`1 z~U諥 2Y9BG\==$@U $c>Ak$g hE,]^[)S(T=:ӎXnhwKyf)v28SSCn[k_S`4":͵QleVbYPs?w0 .}?+WԀT|0i3ȩc:NSYe+R9p7'0Gku)}*ɗgFq}xwr\+nQ( 2;s'ۃn|.^A"Gw9|dSOc{Ɨ/wKJNKGP8*As*^2<*EO0S}lUűpv}(;EXP6 ~bNs۹ұ]|$c;g8>nml#9Qy9y 8?Nhp#%18=IjAAT%8>c/Cp 16uysOB*1 A ߮sQ^]J+J< d1^?,f^(FSی*ؗ3;| ̀O͞|uj+0謰pFIyfpΑHmǶ2sFVEGg/^{$hٙלJK2*Os_ϹG_S )mIe$hW 0Xai%ː#?~5\bOSƛgzP÷V=v3 ,{Њn(l7 utests/native/hpdf/samples/images/rgb.jpg000066400000000000000000000776131176363201700206400ustar00rootroot00000000000000WANGZJFIF,,C     C   @! }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?Mҳut "|RC V3*'J>(D|ll}hdx`H˩ ˕|{⥟}JMRXmh$\5V8%Hl0I<->sǕ(z8RC( m5QR]9I g;8'#߃Һ?xU񅥻sjy"8YdrNv]Fl|1oRfKh m uz>yZJ' 1򓑹3X=;ܳ* #LW^$}dJ}<en /ɆKٙpGA#W+4I{O+RL׵}:3oW$F ~P3ߠǥjHŷ'WP]>r: t*F nkDǗkP ̬@B?: U#,g՟?2n̊񌰩]%q먠M6II"dW# gHǠTtD=XY(o-{-SM;{6A#3j8N2taԙB".#튵f+mt. CG}p(Bݔx$x!VhY3cZk= %6O~U"6Bj3N[^3xK{X ksU4LuYXgh [8q,?ըw]W̶9{KsWf#c 5>7JHN泷S J86~SrWZW?J3hp4U(W8_߆qZIĐeǁvLxR̦!#wL`g]zTpI=~MNV$a( <)q\HW#Z1PEr~Bl"{~f'hP1mطPKLNJ, ; yÚjx|X[H#ho뚓n](7S֚͢VӴzeYbܰ',S'$`HZ7GKSv>^?^:FIՃFٳ//;{~f DXہN1a6HƳ,#;9M' ZyTgH y&Yk܀ǯ6dS+2TdN?kBZ5t^eyo #eڡ[y`s})VL ܖ By䯗I9I.r!}inrĶM΁c nqzr˵9dg(}{e$8yV9"^nFy?Z{l?JK*a }_{|c߅OH5ZjveƓIJ=QJɸgzs:)ޝG5Tq1@v V2UPE.pKzڱn>4x5P#`bg @<>:O4*I)%\OCg5/<6N~>?kedK]a)ݼޅ@y8gDa[wǯjܒ*2hHQi-fB3IǧN}9V/fPdՒ9'3C$GyG!]>U% Ji%V% #ߎ3)8h9)-m/5guo#; v$;݉O|s]NFIu2opJUHorO^NDo,GVu2;Fp3N ] ):Nq} $`c$W# 9>n9~֞/Ԭu$g= SQfZFңW\ }}~z՟5U/n%TBӷ!v8Lf44d>m|B>]w("ҵlDhKv ʃˌg&PZK9eyLcrH#9^+vzj1VFuVuҡI>qr3\\9TlOXE^(ڄbQ*ƌ~ߘ(';IQ7Okï>u-KƧsM31‚,I8U ⽋Wx\KPLK&hWd|gv1',-?x|h |\7Ӄp->)FN; SgePA;kcd}7R,ʘTm{Tzֳ>]_tFS 'w9h旖{<Sg.x]G_:]Ȭ Dnf1;ndL-{HžIu=.y.ZB``\YTHecVMQܬI.9܌Ǩs)h",&^947 ,19}OửCLmQ]28ϩ֔w3q0E%A۷=*tv9~^y<~^S ?/˟jzm)18ݽ~ ssJmIF-ͤ<1nA#%29)F*5QAM"ZqKrTcQw?hxĶze2-_!سxvu95N_ r $I<tf9>W_x3xCԢRIVT6Ke+o)+١Qoqw?It-G{-ϕzO8Q&q>"{m>0ѶvۓO9##u5&b> .gc}}뛺p>`F~aӵC}j#GHTGO*98W)MՋ?*T5vf2.=KVv5ViA >; vodh&R8֪)-M;Pĥ%{rrav ^oZ,5_y`[;fh} >:`g)k{_px=j]NUuO!u8 0.OUoľOMcF%y%eMT4JCv#'$+`y#2+3`1,Nq+;by t{EJ &Clgɭ ?Qmq>pv{T{c Ph=4=6k)-`|raJ';_h//EwpGU*~RKW"M\S| &urAѯu]nHCc2.3Q~\kOt-UZXj ohA*1@=jO٤D48rK2JD,f#'A0*_% 59GcC}=%hq}O_ҨudkdZ4t٣]%DS+3# ҰiHFpFG~9汚v4t:.:T+0#qG`_ǥ . ,^"8OcޟJ"cm=ߴnKN5tP[Ȫ˕\ ڼ"RZ$1% G *)[$v;/-ZmCUE̞VN;2Z#=Аd0V5$-Nzb/C'&0oc,v{{V< ,)hS)Jm(H@Gz:<+-TUlH?,"ѻ\fҽVL̖p҈Q2(Tdt' OJkAiP@KT$Td۱: ]؜<9=j[&Wdp6 TM] c9 1x9YbfBǜjR ĺCxgVd7777p%O$;7=w|DrIЦhulPKJD#NQNs: wF\G+5h,u=3τũK Hhleld}ky8HbRE1)r8UU2`mm \M-20@_֨JK}6c[$SҬ\_KX&*N1˔\dJ}]1]VdW;sEMѬQ3$!f.-7  J+ aJ˞3Y#Ԭ2$Ҡ9@qבڴ&2u[VOV\УnYdCqU'ʈZLR1U?0m#B׮KHHR)$.$2/嵸6u`x< QDFYAA>r^}6Tcfݵ ㏮* \('w%UJb&jr<| c$ܐ+bxz8Z=V[! #*p0s&$+)\UVBJ1+ uhEM;a(\q coj&Fߊuiz~=̗Vpai|8{`V}cŀ0z#&Yz]Ѭ.u]jL0 2}}}-N _QvۊH m\eke4UrA8Rxl<+}?kWSr$Թ-!JRv$~[H[3  X"*!gv9282r3#q!zVDž=kz4nn'2" unp}+uNJriwZs[]@[IbJl>9Z2o3汧)ouF]$5uzcrQj/gczV76;:g,SsEmt0e/wDӁ*gM|dGn۹RM+O_լ4e+fڪF NjoK=sy#\~r2@8ҰѶu&Uok:y%֣phiIi'-ݸg wdX7a c]թ]c4$nm~P?6ZDp0<Vh[: GޠP-۬6;8~:9qqZWsb#u9>͹i$iG0v=9 zv5]q8S̚ۻ%BܡC s! ;6GM[\g',Kf1MIܵu$ऒ[IĊò>C ]M;:$~`Ug$usK1ho6rKԽӼyᅚ8Z[v°\w`x7K3Y4Y !C1Ò :9g=<%*mI֗ʩv&Du@0{Z½Nr)\0RjĶƛslm2Fy3،ׂY wrнTɎFoq\- 3oyMHK(?P>Ruom[9pn+t鈓WZTW}=jd%n{ s׎x}T!FOovÃZ?ķv-;5YFy8 TgFk#{>p[[$N21˼o!Im嶅-;~5$zet]^M6[O1Usl\ gkW>Ga"4Q>@=ʸ=?QRά>4y 4=>rșqùAAHnRdo |IGsN)kJ:-I/-*R8[%qXM8HAK#s6)#8$zd`Y!Jmw8dƦk#BXIv5vY7-rz|q^G/s9rݍQXct'pH8Lu])j`|Yp3$I)1r^xu|CNS%r3NY:[ I\',ŋeGڒI"r$9 ˜)# &< 1s?3:唳8K SLDq)`6=a:=;Z!e~>Q#! zpH u5ݮKށk,!pV>g#ZޫCkFodcZ|Ǒ#0P$*_$^OwZe;ձ@138\.{p:"8YEpqrx5RJ {ڱu.uFRNm?NeMA7+tzcD֭.4sqN^y줣3;uwT^HsqT9YdB`$s9:q]H洋w \Ǔrk=0:ƌw,}7 @0OnÝCB dP 98Ry4񮍾` 1s `?ҹ'rdB];>xO/jdåАGS<Z-Mlr<[?+}'JSm ?gyN&(2+2gsNԺ\EydJ0l89N;yu$:mrmYK <On9_#*Xr r;<j(ϏDerLarI|I#xoIkl $hP7FM ?LP3H&yъP85nZ$_U:d0 r 7Kx̯NBpH8~|;uOiipmagA pp|ʣd2{f?&g;Ӎ91#=\uq+6M9V8JGF6_g/Kԯ%Nc"9ԎrWvzueKMZ#ؼ).AzLn{ GSbSW~$||5bo|=of `r3~բGM qsװ=s*anGY%NNcZ;h6M(c.2rNܓ\;ZX5=bNI+RdRvc's1X^&-LeӾ:-O2ZޤS\ݔ1/@2:7 yP0yhwRG]f8X\i;1 ss*'n3w?zտeVڷrh;i{8 T<7.TD&ܬ|a|o-*p˓~Z6$Vuc`okyqv8 9:m$wQ$ ѰkZ4ghq%o<;#|Vi:pFq9SL95ې֓\9/*)k˔eNnV2? 6c!.]H 9+Sy_1Y gڌ6zŌt{{MR}>Yl1p_tm9geR"!@m~[ˊŮ^4#,`S98ܸ|.xcJAʱ8\N#"\SVK+A'ni6$<N3I:S̉ ?#uz"htr81+ӎ߇ Hzul;99'Q8;߉m1$W r)(('9>墎R/"7>9Y$={TX =EU#םsz&{if) qkv `*!G˂;|qRWC忙ދyC~S `-ǘ,eUJY'FNsMפAo[{v\F7ncMu8soELyRk.~uz+s\F[;icIPȑ>`pd@nn4 ߆-/#g~cFp $qʆ1=Hnp< VLt.O"Pr{nkӷa;FßG-cV^6:/p0A8䎹p'ҽ{Bլ.Q#d'~=ɫOWAjYWj]R3~R:=+Rio%I^1x(5Pn1i!ĸx'FOkM[muV\8Z]ޓwQ<ۙ l+*ޘvS}ϒCNSz+u ^YF.ƋY`y\>2ѭr~Λ׸?*K__ %u9>>jsMIܜM:i4MۺsJM+2-gU5[_x^gl[pݳxۉ`䁃&:xBo5>[?)47T*OL Ey֍' qvhXo5okpsz<μ;dk-bbHeDhϜ|>qS_K|*I[ UʼhcoNW&sg8i/c m&`FՕ9Ḵ Zl&Tc ;ҳu%-NO h91[Er<"L|sn'gq5~]6x "rsּϚL['.Df\# t~p!ƒNI-!A =D)tv׬h;/R;Թmg`LpG㫥ഋ˴Mƹ8ԓԟLVr.^r/lԺ-w#c_pPsבgڼXTi## {i\p2"EG+Yq p#=>/=|A8#?H+푈±yk0q~җeue\Is$50v-ZDB2z}9\vF2#_%[`ޝwdN H{y$1Rܐ[<szcmavqJ0\d=S{ٙnẗ́Ag dz> xYm qs%g-@bB9TM&#Q#.u TYte{#b3@Q+xs z͝෎[fQᱍs@=1]ѧzF"\]xR{HhRCD2!L_3<h߆來5+OXor64u*<ʱ0}la{E挏 .񧇅䖺 }1syqH/ُO ,]2hsk%`V71#TD2XCym<3j7? h7Ko5c%3wgyC<Qg58}&L~0xT10 8ؓW]gExF_T:,o ZƸ(w<<(N1+x |z$uk#NWmw,q%׈,4YOw"sfZ|FPim7kXإ VY:9%AV5o %kWZxeT^BN2r3ܖMҳZG ]_gx?1[6w<3YCdDiK>ՕIS;; W0]Oڃl dxױ^ggq ̆;,d޼ʡAM Q2w4Pu)ǩ><ӵg54xr [Y$m";Fw1x;xQ^E__x↉a;igX cw)Wp289BN ьmbOKM|R&Y#r'nbVv_-Twz淨Wj{#hmT<9~55=_Ǿ5ݥ#GsIי*pG NLAیu$.!HGhr02;3uGTrNɔ&]@!WӚ5_-6؈dGj@uUjwadl9[ìio-ܨ&by?: ríGɴHJ| ƽXs$ϔʫnޥmK}3C 0GoB" pX')" b0z ^ ikMFHdP@q+Ǯ'y&F|cST`{VjV;_m{;y )%_? sE:em\ǫ{jqRq9|vyŭ$Z\-q~CzԨ[vِ?{9?/֢墳{-Pgu s$qǩ9omxnkcOvӒjͣ5*,+F_,m8 Aҹ#ufz`Ss޷GcÊpY| {?ӽzf$ 4 9Dtɔ]HL:7eps(+a",*.R}AVrўW[$q<9yI<sק׎k7ZĖ!BE"*=<Nk3mY#\A$9zBqֽ],qi<=À$W.S +-n p7f!88k׭!xXa,ھӬ B  LQ  ǦG'$`dcgZq@NKVh3f5de-!y9<~^{Ij^鷱"(;9+VGxX9ӯExβ+@;U!2W'?u-NuY3@O:lYbr"Ф;rq+I`ۦIJ8`{G\uY%W@B7˜)9\ٶcuHtPHHq7^G$^ La>aYyHF; EXoJLV@H򌟯^:zыVY+8SG<}yW^ ,A+d&q/^'^_nnQC` ❥Xvw7WeNsιp#02<e$~p>h|@Dh<]Np%9}ׯ*Q<\۱b"b7ſBwt=g쑪NDz|V]/Z[B|yjd~U(M'_էTnq4:PLNj?ԀgҶ4~2pP{1NJ'M%{FZ>>)^+o0ZMj(*Tp2z`W>.tNEnwr? ɺ8FcEGo%e le9#ffjSj6zp}N:g+>P6۴FsǮ?/ҸLnc\ds+5s`QNo[̓ M Bq 22}EYKU3mɂ71:c\VgDY}y\b>z@$^I+Oi-9o ~#ze#`dxHy10mG|a<*b}96M_IdƎOʧXҧjZ[Ѯ! ̮)`g?ϥDt_팓 \՞a.fЮc(>Y#nsi"DqTJVL)JGΐWn]Hr0קzi1G#ʹV\5jJoCԡEAyhUpO@H=~6_H 7u8 s\5#cO޷B]1bWAn<{+~2emUqN>@=NLyt|sY 1 $zr{pcI:n#}{duG%l Q3/ _RHϸwzZ39lQ^ L'lsجx 獁Us:Rp?LWCVW1J'k۵yw{c󬫋HX3*-X9 :Ǧ|GB '8n?S^I:ɕy ;P={wwGqfHBXAoL֬vo)[![\'V31VT+}kx1xKO8ڰ#P?mMFБ+j?y neA9NФ^E ؋P g$obŲƫ4d>k:eF`yϘЌ`Udr"e<\kmXݔșےGYd.a9՞H^ze9'u K#Xz~ibLm?֢e}b>P=kht_Bt6~S ;v9*xTۉ䳐E n%$QYNZYrD)2%v]y/"܏XBIXđ-.,q)bGnV~fǞOcƩ EOǽc7ddr$ЏڷNjt_Z6>` }ӌd0z&<[1ǩ$g׉l]?K1UMvᗟztE7,쪫L0C|~cy'aduis՟گ. M'>H1cwr=O*;^!iL@1'vᖒ:Oj1Qym 511Ə8V 9g>\ٽ7ecp48,wutѬ} 5{>{Bcc˵Z(s7"u36_Y֕ld-sPy!ݘ.I9M=bS,_rgnOsݴ<ʫ[F-r˅ 1x{W%+ɥi"yHs`=ǭtNYhesB8 OϡQ * >O^854IhT HNA8Arjb8KB#Auޗp~3{n-D?y fV&OpjnN1>,@ yr^6lWai֗zi31$ c1ֺk$`(!Udc9듁=sS-VSv<cWO&ܫnQYir;ot98 F(䚓mR+5YΜ.*4.YpڤoK91 :+RRĪ-nnO駇&$v4o`K"V?NIwy- -u>վǬtT#u]t. Q2dS ${Ҿ_^[&m#dcyzə!38UMQ2K%N+]3#`w2o8R8&Wr.&DEcΕ 6U'<AnYx}18qV׭[SZ,-+[P'U>K7<{g5ᚎ],˿+pH䞼c\_7}0[뢆8!HAw#j;=: TZx|in^nT9 :?yNKʨ`Hm^Jl;>bpHmJ*#v?&r #iOE;9$nRWZ-x7%4kF{on?jIc5Y6h^Az.J,n9zRL4Y FgE''U$w,''4Ltئ Xc$p3Ƙtd#b9 Ѥj]ڑzװs׏IGeW ^'9sS;4uPi~ Ջګ% 1QҰuKD$yu\`z٧ϠG_btK^We9_/-.#<~] 2$1"90 YGoNʹV#dp'98czb|{u!ܑc <9s\6k(˱F@< 㩮ȴsu;'bd㌪"F0;`ǎ&O,7;~nu\ ߽s'k3ɒ4h$ā7ʹH=9>&ӵb~.>TGF9'8:KSqhLXy"܄!sO#7s Iu!HRꠀA rNKxΓqu"OapOOZ= rXA~x,рaBq5{PMԅN>V_~FCN-jS t A#'?zѱMfe;8Dھ{EЕ"YZg,_ϭK&'11:i1I6 ߮ c99*ŪR1թ\vA14HfUl&})yYN Rx\SJYKQխOd*NI 9ڳ(vz\Rr i/E1\<t:I]"Rgeݱ u?7mNE]7q@ű` uz:K|㯦i)9ZB v+6XCW rpAZlތ*uC`j2HUX q}g056su2aj)}tӢ҅Q_9|.0>by7ϫEV". `H#猂2FA} ;BNu'Cw# 9Q{,KdhTi ?9צz2χQKb,힘lO47) o3J ZD+HAl-qT.9?c=8o:Go33$%h~W8 1ab{I4chwI6fW$Ldz̷>CP!0?3bz m! Vݕ^H8<3N}:E%Б%.fTp3Ԟsٕ֔=(IoңGpOO剴yD_dU}O{/iNݎP`aT7wS BY ^pq~tҳ*M)wmxVShN,x5Q$AKʅ'䓎8<zT<[CAIk, L0NHJоֱvv@SUKs;ŷ{\i7ԑ!U2p8[3Hk[?\x+9u$, n*Y/,/vG?*rpԣ۩(ՙHf1vI')&P~CÙZ- 2;{sU{*I c-IML,;+t.Yݛ[3L啔^=E:InZF '׵ eQgO\1kk߆ ^xV' T#ۚhwa_Oc^QVi cer5hO ZL72>SWI[|.YVal ?`m~1 RJ:(#8IGp6'r:UJi4`\9Ϡ'ˢ=(3,P%8sFxpzgjPK9TEYY~egcT0c/mξE*}޹jc.Td֨A=LI,1)#j3DIrw:p ǡRN[J߃Lc]PKelLztS's)@Nkғ❜[|a ; ; u=XVL_ݦ-11Y#SHۺH.sA,&[=s~ QGC\Em!]^ vc8ӧ)|t-.H4^.v(C\zv{{VȴՌI'az>n"YF:!IjG,ކ2S]>-僕 qzj׊<iH<2 9JǛ[G^@T"#kps⤐ 2;G@ |dYh[?{ж6'{I'>lFE| OON}qαICya'_/V}頖b<_-qn iؘEE4ol!u F8n?^Ȫ3eC:jnZJ[oM5bHWBNAqpb!$0ct^ff1#a*z^;gc+<*9lWi⸐:p=2V@''RFrNP} PܑI|zkx)8@AaL̕IibgcLi=MTv1h2vG?Zȸ8Y]c@%Hs׽MduԖ9)A~U\4 ]/p9zRoy{^kD԰K"bM}sհjq[]H.l)f= -H#T$lzI3?'w)霜eْ'4g'.7`?5kMvz,!d;6`OQN c]pӌvS[u5KwN^~lj<y]Xm[+",x%d^{@ϫcrl*BӒcs]ޟkyNCĔn7tVU2Utl<[?3J`}%;jLa%֠8&s *'I`9%ch\Q-j֦g[Ma)tힹ晫]"'.@U9s>ZIAJwQM5B""0ǘX?C 1 lI2Ҷn(2#lVI)]c=qOW(ZIoY 9jМmC ۀdAҵu qez&ctih#nnάIb:pzqިOz-D6na8k5g̓ }mڪF1^^][Y1T17:jMXi^]j;K`pq&VW;FDAP/YÆ!h NFrynEīh U Trǀq+w2jO^䳱/g ~뱾PdBӷi{IKheUf;Yp7zZKfxnb2pp<v+Tȩ.J&7Z4]wZJ(|7'8pǽ#OMuMmu 8&OrI?^8ʜsv{@w&xgx5fy"GpL8gqY60J%;#.fs{qǹNO;ys|]V+F6mC{eXB)|ǡ'qZ1wWDΣyomPe==@'8=|x;PEݟ<~GI\Vwqf}lVK:,E8,Hr9q|X43FןIJ;ӛQw?>u viW<$xf)|=wwpl"dgB1# 94<;sz^*LnOߚQ4$kAIvi>V`c릤,rSi潶$˶# X֭mD21 tV _Cw4E]B٭Y ż̪ )@9k2se},H\dkD;w5-m\ނ'),6|:-%@«H[`g= vUF:O2Lٛxx=zt,һ$dt=<ik"o@եye_Ps@$U,.''Q?皅9;X}R9RcR=\`qmܵ-$$=ah'$ݍ jM/PIL@MmO@N$>V:+g240[jd9bqmJJGњm^ 6#ֹ?iD4l'K*͜q}sYO[4zÛayaK{f\|đ={?\|gfeD.8Ͷ8N<3h}^6.@ r~d|~*)u,N&FI~A F2cN'd|| ݷ9V>z:OҴ].:QG_5䒲rdDir@&%FKd$s[ې1m۲G:g%}NWQӎփPI9<{SP,΢۹N6SH>'c.6ֻ#XٷRJ'ןޯmrGjcY0yx=? tQ؅y9F#׷g6fg id q>Ak($g hE,u<בޝJPwYIbJ#@p1'ɧԐH`,Sdqӎ'ם7bK<JW# pv=,&*cx^m;2mњy(9xlsU㺆ЕT=jCj`">Z\Ʋ*d䎣#ӿVissjU.&𼞘'Vg=gyE"1J[b$˳c#<N %b.z an~udT#ۺf8º̀)ۥw_ |f'PHNwH9N'[y)Fޔۑ'/_Ş 'Ҥӥ!% #88*AUkMC8Tc韨ؼŮ%m& w3#~GoI\,@PXƁa@,lj6vr9p~<] 6zu gC#%F## h tHQ όpNO~^䤣&J80@^r[Rx= /I\!{y1ZXM9YIEs1 #c}q֖9eWV*6qE'w*jw$RlsޣSF5d$;$zwg{jrE",26팜+@ ?u}@c뷧]_$fPIH33 $s򮍯bVvEV|Osc]OV^9oLB 4aROrs kaŤ.BNF]X|"y7"ugbؓ6y #?x7Ɂ0ڽ October 1999 tests/native/hpdf/samples/pngsuite/basn0g01.png000066400000000000000000000002441176363201700217600ustar00rootroot00000000000000PNG  IHDR [GYgAMA1_[IDATx-̱ 0 J z4o< aEQ/ҤlΩ%SS4W!K&=Bs%%^ڲoj0i.)ano0eI//IENDB`tests/native/hpdf/samples/pngsuite/basn0g02.png000066400000000000000000000001501176363201700217550ustar00rootroot00000000000000PNG  IHDR =gAMA1_IDATxc`]0PS3 cI IENDB`tests/native/hpdf/samples/pngsuite/basn0g04.png000066400000000000000000000002211176363201700217560ustar00rootroot00000000000000PNG  IHDR )gAMA1_HIDATxc``TR26vq MK+/g CA*wLrPV#ݽT3r%GAIENDB`tests/native/hpdf/samples/pngsuite/basn0g08.png000066400000000000000000000002121176363201700217620ustar00rootroot00000000000000PNG  IHDR V%(gAMA1_AIDATxcd`$ȳ )?`y00gdy\ q10edPq5YIENDB`tests/native/hpdf/samples/pngsuite/basn0g16.png000066400000000000000000000002471176363201700217710ustar00rootroot00000000000000PNG  IHDR kgAMA1_^IDATx1 0 CQ9[ܠ({2*ُ?8Wc:`݂@B&@=2 -hL`?oO8K_+IENDB`tests/native/hpdf/samples/pngsuite/basn2c08.png000066400000000000000000000002211176363201700217600ustar00rootroot00000000000000PNG  IHDR gAMA1_HIDATx 0 @r;D++ ; }Lx@J„(t8#@pw^@KIENDB`tests/native/hpdf/samples/pngsuite/basn2c16.png000066400000000000000000000004561176363201700217710ustar00rootroot00000000000000PNG  IHDR 1gAMA1_IDATxՖ 0DA~&fzE=֠B>/drs~='3_Zwt(p3p]`_yt?t(C\l52L̩g I@g.`(`g Dg&((`60Y`zl(P49܀:{z*zȟmt3ΞAO3B^IENDB`tests/native/hpdf/samples/pngsuite/basn3p01.png000066400000000000000000000001601176363201700217710ustar00rootroot00000000000000PNG  IHDR IgAMA1_PLTE""fl&IDATxc4?fYIENDB`tests/native/hpdf/samples/pngsuite/basn3p02.png000066400000000000000000000002221176363201700217710ustar00rootroot00000000000000PNG  IHDR ggAMA1_sBIT|.w PLTEe?+"IDATxc0,| =IꉎIENDB`tests/native/hpdf/samples/pngsuite/basn3p04.png000066400000000000000000000003301176363201700217730ustar00rootroot00000000000000PNG  IHDR TggAMA1_sBITw-PLTE""fwDDҰIGIDATxc =sfժrcwfd C+(H*ŅTݻq@*)#MK#G{7}IENDB`tests/native/hpdf/samples/pngsuite/basn3p08.png000066400000000000000000000024061176363201700220050ustar00rootroot00000000000000PNG  IHDR DgAMA1_PLTE"Dww :w""""Uffff"DDUU"DDUU3DDff3D"ffD33U*D˺[""f2Ucw:DDkfBkܺ33sJw{"w332fDwJf""UUDff3UwwDwffD"w"3333cU3{UUUUf܃wwUUww""DD3ԪU*U˴f3BSD̙"Sww333Ĉwff""UU"DD[wfwws33wD""U"f 3bIDATx GHd+;3 ekXegа**4h lޣY2W%syiHCL*;8"KE6sx'HK?Y9sģ>1~!'B 0IENDB`tests/native/hpdf/samples/pngsuite/basn4a08.png000066400000000000000000000001761176363201700217710ustar00rootroot00000000000000PNG  IHDR sgAMA1_5IDATxch41",(,?a0T1`4GÀ*hP* }IENDB`tests/native/hpdf/samples/pngsuite/basn4a16.png000066400000000000000000000042361176363201700217710ustar00rootroot00000000000000PNG  IHDR n<gAMA1_UIDATxŗ_h[cHuRGT(8B-P.%4BiKaw]HɐXf%$zmiJWGc9w~t5O>>`eaX=`Ԃ±8G?]e _^X.wGO3wd3 gZú|'fef[Z!p&ކuh`c:oȇtri^g)X]UVZ l-5C`T$u!Z??p߅Cgj@dƱt-YY˅ae}';Ȁ~B@3 x'͌B ^SA#nr2r:QfGR`fAdv <@_"AÃևðqXZ3S:܀{Ճ< ';B 1/s,ZYߣ` `pg=U<@#뿃נ/`6,њ̀U]轏@`7&kc{ ? %:4o6JވaKGaɄ ? @= !e/sVVCpA5AhΠ.Ak%,?%!V[?hv@[/  !~Q`ZJHV@ס, \Z-tVKuVai *(a0ס  =i0Vކ.a{hQf\5 |ee8 l9QF haeXC9wnA D`6_Bh^SygLgv]~dA_ lwSR; I[VF&;5l :F$g 8h=ox yot| 'Q2 ,44c94O!1DӲͷ+3߲M:$m+?=*|~5{Wb zC`W݄辥< Cu㰈"j Q}8p|$;>.;/؏a 4/@ н[/E})x6훏m^=t)y#!ËGT/So SMO]]#7*gǏl!qXkdQa59G2 5e5$l (+畸2ۥ_$`Wb?@0 9E{Bk80/ƲVRZYxYf\ Nv{k By_@SaJcKjT𔥞cVE^>[v]7R>܉\i@)]SBBy$Ғh('eϭia;cҹ; МkX]HM%I%ŎUz]„Xj"22CO+h ɴ )oF\ܼ27 h+' wCQRY\-4-dA.Hax4,@ᜲ$o{ dJ;v,& C˧T$3ӔgVWsϓ<yhB# FP!v Vޔl X$p]y]\._s=,l7Gd){&TLK(}Z)h+V %@{gd"iiqૢ]Z^<y wPg+ЮB;  s%/ڟ gU<=DPw?({!l7d̀t:9-xIb z 4thFk2*<#~WQ\ 4eύpNf.J,OAdžN 6lf-ȌcGR3*%dE((\0){n$KJ3`38`ek+-w53&7yiIq4u-⋋=wTK\*< e7 M@x޶"&nܛ9f5|>IEtSqEUvvhƫ ^R\TjVz=XtodɎap~~ p}c ׍{3lvB}ކ|9:^TyJz5%WkFƫ%X go|h7#aL$7u)7ݬP?*>WU4^Y+/WZO?{1+^ |{ w u'0oh5HM@3~ݸ7sf7k.7e-/NK?RxZ s'@/eAY@ [?p| HQtu yfKϼQ-ѪHM@3~ݸ7sf7k.ԇ _|IV?4ħځN,'[3kXf|>o$_NY%an }MEFj0&fqo攛n\~|[~|!~ݻj%VyN&#āHFq@ ҊU;2t`@2zKoT۵3=-nj7#aL$7Vlvkeݻμ K@pR (#8grRKX4L;V݌14׍{3\67aZi.3o~`i޻ {pi 3 =)opQqdv%)3{>5XaXiH4ь̤+[h7#aL$׀fiZV!_ Cɽ?x~;w1[o zDE3ؓI~]Rl~%yZ+1S5ISZj݈VF˜H.~xVv: N]mx⁍/43}Ȉ c'ݛ l/Ib*aS1Q&1%?3j݈VD\9߭;wR'3j=K3Y ҕx{vi 1\[SΦx>lOcJF ޒ4ŨuWGjO9hn$83*}}§Rw~;@4k7syv|aE%aL>  j0&3gƫˁȽ_BAqޓ=vA&folnon4|6jd.dndoGq?eí۵IENDB`tests/native/hpdf/samples/pngsuite/maskimage.png000066400000000000000000000002251176363201700224020ustar00rootroot00000000000000PNG  IHDR \IDATxUA 0e`-  n 0HFz[=E2SB-zA^`_4/IENDB`tests/native/hpdf/samples/raw_image_demo.fal000066400000000000000000000041521176363201700215260ustar00rootroot00000000000000import from hpdf tmp = [ 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xf0, 0xf3, 0xf3, 0xff, 0xe0, 0xf3, 0xf3, 0xff, 0xc0, 0xf3, 0xf3, 0xff, 0x80, 0xf3, 0x33, 0xff, 0x00, 0xf3, 0x33, 0xfe, 0x00, 0xf3, 0x33, 0xfc, 0x00, 0xf8, 0x07, 0xf8, 0x00, 0xf8, 0x07, 0xf0, 0x00, 0xfc, 0xcf, 0xe0, 0x00, 0xfc, 0xcf, 0xc0, 0x00, 0xff, 0xff, 0x80, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x00, 0xff, 0xf8, 0x0f, 0xe0, 0xff, 0xf0, 0x0f, 0xe0, 0xff, 0xe0, 0x0c, 0x30, 0xff, 0xc0, 0x0c, 0x30, 0xff, 0x80, 0x0f, 0xe0, 0xff, 0x00, 0x0f, 0xe0, 0xfe, 0x00, 0x0c, 0x30, 0xfc, 0x00, 0x0c, 0x30, 0xf8, 0x00, 0x0f, 0xe0, 0xf0, 0x00, 0x0f, 0xe0, 0xe0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] rawImageData = MemBuf(tmp.len()) for i in [0:tmp.len()]: rawImageData[i] = tmp[i] pdf = hpdf.Doc() pdf.setCompressionMode(hpdf.COMP_ALL) // create default-font font = pdf.getFont("Helvetica") // add a new page object page = pdf.addPage() page.setWidth(172) page.setHeight(80) page.beginText() page.setFontAndSize(font, 20) page.moveTextPos(220, page.getHeight() - 70) page.showText("RawImageDemo") page.endText() // load RGB raw-image file image = pdf.loadRawImageFromFile("rawimage/32_32_rgb.dat", 32, 32, hpdf.ColorSpace.DEVICE_RGB) x = 20 y = 20 // Draw image to the canvas. (normal-mode with actual size.) page.drawImage(image, x, y, 32, 32); // load GrayScale raw-image file image = pdf.loadRawImageFromFile("rawimage/32_32_gray.dat", 32, 32, hpdf.ColorSpace.DEVICE_GRAY) x = 70 y = 20 // Draw image to the canvas. (normal-mode with actual size.) page.drawImage(image, x, y, 32, 32); // load GrayScale raw-image (1bit) file from memory. image = pdf.loadRawImageFromMem(rawImageData, 32, 32, hpdf.ColorSpace.DEVICE_GRAY, 1); x = 120 y = 20 // Draw image to the canvas. (normal-mode with actual size.) page.drawImage(image, x, y, 32, 32) // save the document to a file pdf.saveToFile(scriptName + ".pdf") tests/native/hpdf/samples/rawimage/000077500000000000000000000000001176363201700176755ustar00rootroot00000000000000tests/native/hpdf/samples/rawimage/32_32_2color.dat000066400000000000000000000002001176363201700223670ustar00rootroot00000000000000333 0 0 0 0tests/native/hpdf/samples/rawimage/32_32_gray.dat000066400000000000000000000020001176363201700221310ustar00rootroot00000000000000 $-6?HQZclu~պ &/8AJS\enwϴ (1:CLU^gpyɮ!*3GPYbkt}ؽ %.7@IR[dmvҷ '09BKT]fox̱ )2;DMV_hqzƫ"+4=FOXajs|$-6?HQZclu~պ&/8AJS\enwϴ~(1:CLU^gpyɮx!*3GPYbkt}ؽl%.7@IR[dmvҷf'09BKT]fox̱{` )2;DMV_hqzƫuZ"+4=FOXajs|oT$-6?HQZclu~պiN&/8AJS\enwϴ~cH(1:CLU^gpyɮx]B*3GPYbkt}ؽlQ6.7@IR[dmvҷfK009BKT]fox̱{`E*2;DMV_hqzƫuZ?$4=FOXajs|oT96?HQZclu~պiN38AJS\enwϴ~cH-:CLU^gpyɮx]B' GPYbkt}ؽlQ6tests/native/hpdf/samples/rawimage/32_32_rgb.dat000066400000000000000000000060001176363201700217450ustar00rootroot00000000000000{skcZRJB91)!{skcZRJB91)!{skcZRJB91)!{skcZRJB91)!{skcZRJB91)!!{skcZRJB91)!!){skcZRJB91)!!)1{skcZRJB91)!!)19޽ֽνƽ{skcZRJB91)!!)19B޵ֵεƵ{skcZRJB91)!!)19BJޭ֭έƭ{skcZRJB91!))!19BJRޥ֥Υƥ{skcZRJB9!1))1!9BJRZޜ֜ΜƜ{skcZRJB!9)11)9!BJRZcޔ֔ΔƔ{skcZRJ!B)9119)B!JRZckތ֌Όƌ{skcZR!J)B1991B)J!RZcksބք΄Ƅ{skcZ!R)J1B99B1J)R!Zcks{{{{{{{{{{{{{{{{{{{s{k{c{!Z{)R{1J{9B{B9{J1{R){Z!{c{k{s{{{ssssssssssssssss{sssks!cs)Zs1Rs9JsBBsJ9sR1sZ)sc!sksss{sskkkkkkkkkkkkkkkk{ksk!kk)ck1Zk9RkBJkJBkR9kZ1kc)kk!ksk{kkkcccccccccccccccc{c!sc)kc1cc9ZcBRcJJcRBcZ9cc1ck)cs!c{ccccZZZZZZZZZZZZZZZZ!{Z)sZ1kZ9cZBZZJRZRJZZBZc9Zk1Zs)Z{!ZZZZZRRRRRRRRRRRRRRR!R){R1sR9kRBcRJZRRRRZJRcBRk9Rs1R{)R!RRRRRJJJJJJJJJJJJJJ!J)J1{J9sJBkJJcJRZJZRJcJJkBJs9J{1J)J!JJJJJBBBBBBBBBBBBB!B)B1B9{BBsBJkBRcBZZBcRBkJBsBB{9B1B)B!BBBBB999999999999!9)91999B{9Js9Rk9Zc9cZ9kR9sJ9{B99919)9!9999911111111111!1)11191B1J{1Rs1Zk1cc1kZ1sR1{J1B19111)1!11111))))))))))!)))1)9)B)J)R{)Zs)ck)kc)sZ){R)J)B)9)1)))!)))))!!!!!!!!!!!)!1!9!B!J!R!Z{!cs!kk!sc!{Z!R!J!B!9!1!)!!!!!!!!)19BJRZc{kssk{cZRJB91)!!)19BJRZck{ss{kcZRJB91)!!)19BJRZcks{{skcZRJB91)!!)19BJRZcks{{skcZRJB91)!tests/native/hpdf/samples/text_annotation.fal000066400000000000000000000046071176363201700220120ustar00rootroot00000000000000import from hpdf rect1 = [50, 350, 150, 400] rect2 = [210, 350, 350, 400] rect3 = [50, 250, 150, 300] rect4 = [210, 250, 350, 300] rect5 = [50, 150, 150, 200] rect6 = [210, 150, 350, 200] rect7 = [50, 50, 150, 100] rect8 = [210, 50, 350, 100] pdf = hpdf.Doc() font = pdf.getFont("Times-Roman", "WinAnsiEncoding") page = pdf.addPage() page.setWidth(400) page.setHeight(500) page.beginText() page.setFontAndSize(font, 16) page.moveTextPos(130, 450) page.showText("Annotation Demo") page.endText() annot = page.createTextAnnot(rect1, "Annotation with Comment Icon. \n This annotation set to be opened initially.") annot.setIcon(hpdf.AnnotIcon.COMMENT); annot.setOpened(true) annot = page.createTextAnnot(rect2, "Annotation with Key Icon") annot.setIcon(hpdf.AnnotIcon.PARAGRAPH); annot = page.createTextAnnot(rect3, "Annotation with Note Icon") annot.setIcon(hpdf.AnnotIcon.NOTE) annot = page.createTextAnnot(rect4, "Annotation with Help Icon") annot.setIcon(hpdf.AnnotIcon.HELP); annot = page.createTextAnnot(rect5, "Annotation with NewParagraph Icon") annot.setIcon(hpdf.AnnotIcon.NEW_PARAGRAPH) annot = page.createTextAnnot(rect6, "Annotation with Paragraph Icon") annot.setIcon(hpdf.AnnotIcon.PARAGRAPH) annot = page.createTextAnnot(rect7, "Annotation with Insert Icon") annot.setIcon(hpdf.AnnotIcon.INSERT) encoding = pdf.getEncoder("ISO8859-2") page.createTextAnnot(rect8, "Annotation with ISO8859 text ÓÔÕÖרÙ", encoding); page.setFontAndSize(font, 11) page.beginText() page.moveTextPos(rect1[0] + 35, rect1[3] - 20) page.showText("Comment Icon.") page.endText() page.beginText() page.moveTextPos(rect2[0] + 35, rect2[3] - 20) page.showText("Key Icon") page.endText() page.beginText() page.moveTextPos(rect3[0] + 35, rect3[3] - 20) page.showText("Note Icon.") page.endText() page.beginText() page.moveTextPos(rect4[0] + 35, rect4[3] - 20) page.showText("Help Icon") page.endText() page.beginText() page.moveTextPos(rect5[0] + 35, rect5[3] - 20) page.showText("NewParagraph Icon") page.endText() page.beginText() page.moveTextPos(rect6[0] + 35, rect6[3] - 20) page.showText("Paragraph Icon") page.endText() page.beginText() page.moveTextPos(rect7[0] + 35, rect7[3] - 20) page.showText("Insert Icon") page.endText() page.beginText() page.moveTextPos(rect8[0] + 35, rect8[3] - 20) page.showText("Text Icon(ISO8859-2 text)") page.endText() // save the document to a file pdf.saveToFile(scriptName + ".pdf")tests/native/hpdf/samples/text_demo.fal000066400000000000000000000140171176363201700205600ustar00rootroot00000000000000import from hpdf function showStripePattern(page, x, y) iy = 0 while (iy < 50) page.setRGBStroke(0.0, 0.0, 0.5) page.setLineWidth(1) page.moveTo(x, y + iy) page.lineTo(x + page.textWidth("ABCabc123"),y + iy) page.stroke() iy += 3 end page.setLineWidth(2.5) end function showDescription (page, x, y, text) fsize = page.getCurrentFontSize() font = page.getCurrentFont() c = page.getRGBFill() page.beginText() page.setRGBFill(0, 0, 0) page.setTextRenderingMode(hpdf.TextRenderingMode.FILL) page.setFontAndSize(font, 10) page.textOut(x, y - 12, text) page.endText() page.setFontAndSize(font, fsize) page.setRGBFill(c.r, c.g, c.b) end page_title = "Text Demo"; samp_text = "abcdefgABCDEFG123!#$%&+-@?"; samp_text2 = "The quick brown fox jumps over the lazy dog."; pdf = hpdf.Doc() pdf.setCompressionMode(hpdf.COMP_ALL); /* create default-font */ font = pdf.getFont("Helvetica") // add a new page object. page = pdf.addPage() // print the title of the page (with positioning center). page.setFontAndSize(font, 24) tw = page.textWidth(page_title) page.beginText() page.textOut((page.getWidth() - tw) / 2, page.getHeight () - 50, page_title) page.endText() page.beginText() page.moveTextPos(60, page.getHeight() - 60) /* * font size */ fsize = 8; while (fsize < 60) // set style and size of font. page.setFontAndSize(font, int(fsize)) // set the position of the text. page.moveTextPos(0, -5 - fsize) // measure the number of characters which included in the page. tlen = page.measureText(samp_text, page.getWidth() - 120, false) page.showText(samp_text[0:tlen]) /* print the description. */ page.moveTextPos(0, -10); page.setFontAndSize(font, 8); page.showText("Fontsize=" + fsize); fsize *= 1.5 end /* * font color */ page.setFontAndSize(font, 8) page.moveTextPos(0, -30) page.showText("Font color") page.setFontAndSize(font, 18) page.moveTextPos(0, -20) textLen = len(samp_text) rg = function (i) r = i / textLen g = 1 - r ret = r,g return ret end for i in [0:textLen] r,g = rg(i) buf = samp_text[i:i+1] page.setRGBFill(r, g, 0) page.showText(buf) end page.moveTextPos(0, -25) for i in [0:textLen] r,b = rg(i) buf = samp_text[i:i+1] page.setRGBFill(r, 0, b) page.showText(buf) end page.moveTextPos(0, -25) for i in [0:textLen] g,b = rg(i) buf = samp_text[i:i+1] page.setRGBFill(0, g, b) page.showText(buf) end page.endText() ypos = 450 /* * Font rendering mode */ page.setFontAndSize(font, 32) page.setRGBFill(0.5, 0.5, 0) page.setLineWidth(1.5) // PDF_FILL showDescription(page, 60, ypos, "RenderingMode=PDF_FILL") page.setTextRenderingMode(hpdf.TextRenderingMode.FILL) page.beginText() page.textOut(60, ypos, "ABCabc123") page.endText() /*PDF_STROKE */ showDescription(page, 60, ypos - 50, "RenderingMode=PDF_STROKE") page.setTextRenderingMode(hpdf.TextRenderingMode.STROKE) page.beginText() page.textOut(60, ypos - 50, "ABCabc123") page.endText() // PDF_FILL_THEN_STROKE showDescription(page, 60, ypos - 100, "RenderingMode=PDF_FILL_THEN_STROKE"); page.setTextRenderingMode(hpdf.TextRenderingMode.FILL_THEN_STROKE) page.beginText() page.textOut(60, ypos - 100, "ABCabc123") page.endText() // PDF_FILL_CLIPPING showDescription(page, 60, ypos - 150, "RenderingMode=PDF_FILL_CLIPPING") page.gSave() page.setTextRenderingMode(hpdf.TextRenderingMode.FILL_CLIPPING) page.beginText() page.textOut(60, ypos - 150, "ABCabc123") page.endText() showStripePattern(page, 60, ypos - 150) page.gRestore() // PDF_STROKE_CLIPPING showDescription(page, 60, ypos - 200, "RenderingMode=PDF_STROKE_CLIPPING") page.gSave() page.setTextRenderingMode(hpdf.TextRenderingMode.STROKE_CLIPPING) page.beginText() page.textOut(60, ypos - 200, "ABCabc123") page.endText() showStripePattern(page, 60, ypos - 200) page.gRestore() // PDF_FILL_STROKE_CLIPPING showDescription(page, 60, ypos - 250, "RenderingMode=PDF_FILL_STROKE_CLIPPING"); page.gSave() page.setTextRenderingMode(hpdf.TextRenderingMode.FILL_STROKE_CLIPPING) page.beginText() page.textOut(60, ypos - 250, "ABCabc123") page.endText() showStripePattern(page, 60, ypos - 250) page.gRestore() // Reset text attributes page.setTextRenderingMode(hpdf.TextRenderingMode.FILL) page.setRGBFill(0, 0, 0) page.setFontAndSize(font, 30) /* * Rotating text */ angle1 = 30 // A rotation of 30 degrees. rad1 = angle1 / 180 * PI // Calcurate the radian value. showDescription(page, 320, ypos - 60, "Rotating text") page.beginText() page.setTextMatrix(cos(rad1), sin(rad1), -sin(rad1), cos(rad1), 330, ypos - 60); page.showText("ABCabc123"); page.endText(); /* * Skewing text. */ showDescription(page, 320, ypos - 120, "Skewing text"); page.beginText(); angle1 = 10 angle2 = 20 rad1 = angle1 / 180 * PI rad2 = angle2 / 180 * PI page.setTextMatrix(1, tan(rad1), tan(rad2), 1, 320, ypos - 120) page.showText("ABCabc123") page.endText() /* * scaling text (X direction) */ showDescription(page, 320, ypos - 175, "Scaling text (X direction)") page.beginText() page.setTextMatrix(1.5, 0, 0, 1, 320, ypos - 175) page.showText("ABCabc12") page.endText() /* * scaling text (Y direction) */ showDescription(page, 320, ypos - 250, "Scaling text (Y direction)") page.beginText() page.setTextMatrix(1, 0, 0, 2, 320, ypos - 250) page.showText("ABCabc123") page.endText() /* * char spacing, word spacing */ showDescription(page, 60, 140, "char-spacing 0") showDescription(page, 60, 100, "char-spacing 1.5") showDescription(page, 60, 60, "char-spacing 1.5, word-spacing 2.5") page.setFontAndSize(font, 20) page.setRGBFill(0.1, 0.3, 0.1) // char-spacing 0 page.beginText(); page.textOut(60, 140, samp_text2) page.endText() // char-spacing 1.5 page.setCharSpace(1.5); page.beginText() page.textOut(60, 100, samp_text2) page.endText() // char-spacing 1.5, word-spacing 3.5 page.setWordSpace(2.5) page.beginText() page.textOut(60, 60, samp_text2) page.endText() // save the document to a file pdf.saveToFile(scriptName + ".pdf") tests/native/hpdf/samples/text_demo2.fal000066400000000000000000000065121176363201700206430ustar00rootroot00000000000000import from hpdf import printGrid from .grid_sheet as printGrid samp_text = "The quick brown fox jumps over the lazy dog. " pdf = hpdf.Doc() page = pdf.addPage() page.setSize(hpdf.PageSize.A5, hpdf.PageDirection.PORTRAIT) printGrid(pdf, page) height = page.getHeight() font = pdf.getFont("Helvetica") page.setTextLeading(20) // HPDF_TALIGN_LEFT left = 25 top = 545 right = 200 bottom = top - 40 page.rectangle(left, bottom, right - left, top - bottom) page.stroke() page.beginText() page.setFontAndSize(font, 10) page.textOut(left, top + 3, "HPDF_TALIGN_LEFT") page.setFontAndSize(font, 13) page.textRect(left, top, right, bottom, samp_text, hpdf.TextAlignment.LEFT) page.endText() // HPDF_TALIGN_RIGHT left = 220 right = 395 page.rectangle(left, bottom, right - left, top - bottom) page.stroke() page.beginText() page.setFontAndSize(font, 10) page.textOut(left, top + 3, "HPDF_TALIGN_RIGHT") page.setFontAndSize(font, 13) page.textRect(left, top, right, bottom, samp_text, hpdf.TextAlignment.RIGHT) page.endText() // HPDF_TALIGN_CENTER left = 25 top = 475 right = 200 bottom = top - 40 page.rectangle(left, bottom, right - left, top - bottom) page.stroke() page.beginText() page.setFontAndSize(font, 10) page.textOut(left, top + 3, "HPDF_TALIGN_CENTER") page.setFontAndSize(font, 13) page.textRect(left, top, right, bottom, samp_text, hpdf.TextAlignment.CENTER) page.endText() // HPDF_TALIGN_JUSTIFY left = 220 right = 395 page.rectangle(left, bottom, right - left, top - bottom) page.stroke() page.beginText() page.setFontAndSize(font, 10) page.textOut(left, top + 3, "HPDF_TALIGN_JUSTIFY") page.setFontAndSize(font, 13) page.textRect(left, top, right, bottom, samp_text, hpdf.TextAlignment.JUSTIFY) page.endText() // Skewed coordinate system angle1 = 5.0 angle2 = 10.0 rad1 = angle1 / 180 * PI rad2 = angle2 / 180 * PI page.gSave() page.concat(1, tan(rad1), tan(rad2), 1, 25, 350) left = 0 top = 40 right = 175 bottom = 0 page.rectangle(left, bottom, right - left, top - bottom) page.stroke() page.beginText() page.setFontAndSize(font, 10) page.textOut(left, top + 3, "Skewed coordinate system") page.setFontAndSize(font, 13) page.textRect(left, top, right, bottom, samp_text, hpdf.TextAlignment.LEFT) page.endText() page.gRestore() // Rotated coordinate system page.gSave() angle1 = 5.0 rad1 = angle1 / 180 * PI page.concat(cos(rad1), sin(rad1), -sin(rad1), cos(rad1), 220, 350) left = 0 top = 40 right = 175 bottom = 0 page.rectangle(left, bottom, right - left, top - bottom) page.stroke() page.beginText() page.setFontAndSize(font, 10) page.textOut(left, top + 3, "Rotated coordinate system") page.setFontAndSize(font, 13) page.textRect(left, top, right, bottom, samp_text, hpdf.TextAlignment.LEFT) page.endText() page.gRestore() // text along a circle page.setGrayStroke(0) page.circle(210, 190, 145) page.circle(210, 190, 113) page.stroke() angle1 = 360.0 / samp_text.len() angle2 = 180.0 page.beginText() font = pdf.getFont("Courier-Bold") page.setFontAndSize(font, 30) i = 0 while i < samp_text.len() rad1 = (angle2 - 90) / 180 * PI rad2 = angle2 / 180 * PI x = 210.0 + cos(rad2) * 122 y = 190.0 + sin(rad2) * 122 page.setTextMatrix(cos(rad1), sin(rad1), -sin(rad1), cos(rad1), x, y) page.showText(samp_text[i:i+1]) angle2 -= angle1 i += 1 end page.endText() pdf.saveToFile(scriptName + ".pdf") tests/native/hpdf/samples/ttfont/000077500000000000000000000000001176363201700174175ustar00rootroot00000000000000tests/native/hpdf/samples/ttfont/PenguinAttack.ttf000066400000000000000000002113241176363201700226760ustar00rootroot00000000000000 LTSHaR"$OS/2poDVVDMXdDk cmapwo>LPcvt 8 \fpgm2Msfbgasp  glyf^v phdmx\head\,6hhea!fd$hmtx2$kernloca9*vmaxp3 nameZposto (prep} D _<cc(v ,*cv  Q33f@@PYRS@ R QA?y]46Pcg[0RGAQ_W'W4S=YA]KW\a=F*-GBQ.JQEjEQqO.WIWaUMUO\`  ^bC{ILN"\SuOA9M>I1W_oK4~/nS* SS(cQU9 7]0BMBMmq;coKOq;KK"I *KY1"0I(IUUqUq;U oeqUB4^~..YW88{#L>.S;]bIRBK[[[Q0?.I&A8888A_A_A_A_hhVhhVSrVrVrVrVrVDEQCC{{up?[VME$IS&[[Y9A^c^c^c^cf;SI5IH.q3q3I1CSO$5MQ CoQ[qU;4fvY]jatk@;/+LL;LDLLD;LL;;73&/DL;;L;;;;;                                                                                                                                                                                                                                                                                                                        !!           !           %%          % !       *)   ##     ")# %   $  .-    '" &$      %-!& ) !  # '   21 !"  *%#)!  '   (1 $* -!  $ #& + #65! $$ ! ! - ( &-#" *   +5 ', 0#  &! &(".  !&:9#&' " ## #! !1 !" +")/&%! -     /9 *!1 !!4%!!!!  !!!!!) $ (+%1 ""$  !)CB(-"  -" # '&)(%$*' "&8&&(#&%2'/7,*& #4  $"%% $# 6B 1#%8&&< +!&&&&$$$$ $%%%%%&&&&&0$) /2*9'()%%&/KJ"#-! 2&$"$!#2##"& $'$# #++--)(/+&*?*+,&**8,5>1/+" ':  )&) )  )'  **""C" $"""1%****(((( )******++++!!!5"")# !. 58 !/@,! " ,."!*) *4^& FYXgheZJKLMOQ@R !"#$PGHIAB%&'()*+,-./0123546789:;<=>TSUVWj? CDENonsutvxwy{kzdalp_`]^[\qrcbim|f~}8X@/:@OQZfz~1BSax~~    " & 0" 0;APR[g{1AR`x}~    & 0" mb3GL;9ޫpXt||FYXgheZJKLMOQ@RPGHIAB54TSUVWj?CDEN{zdlmnostuvwxyp[\c]^b0V@/:@OQZfz~1BSax~~    " & 0" 0;APR[g{1AR`x}~    & 0" mb3GL;9ޫVrzzFYXgheZJKLMOQ@RPGHIAB54TSUVWj?CDEN{zdlmnostuvwxyp[\c]^b,K PXYD _^-, EiD`-,*!-, F%FRX#Y Id F had%F hadRX#eY/ SXi TX!@Yi TX!@eYY:-, F%FRX#Y F jad%F jadRX#Y/-,K &PXQXD@DY!! EPXD!YY-, EiD` E}iD`-,*-,K &SX@Y &SX#!#Y &SX#!#Y &SX#!@#Y &SX%EPX#!#!%E#!#!Y!YD- ,KSXED!!Y-++ E}iD*,)<QE7&+A&6FVfv ]A]EX+/+>YEX!/!>Y!+9+ A ]A  ( 8 H X h x ]!A'7GWgw ]A]01676.'&32>7#".54>32.'Q#(% $I_*;^B#8Y>3bN4( Bb{A[g75e];{d@)@NJ=;!%A1Ag>9rZ8YEX/>Y:+90132"#465.54>32#"&'>732>.#"32>7(#$(  493 =c<YEXP/P>YZ_+Z+?{+?+?DиZeܸPoAoo'o7oGoWogowoooooo ]Aoo] A]A(8HXhx ]01%".54>32>32#".'>76.>32#".54>326.#"32>54.#".7>54.'&3GLH8#GuO+T'&0=),[I-=F".   %#.A&$ )C+71 JIB1(?R.$C4 3Voxx30p^?Af}<:sV+ *4+J7!%6"HT 4AD@;#%VTM<#%AW2,`P35J/X 2JdB\\.3'.EQE.!* +60 !NX_a^+,#  9T7>nZG17W>5M3*F4%,/9-&KnH.5-8)901B[D3`K/IyZ0R<#];=f>/?/>и/A&6FVfv ]A]и и /? ܺ* 99A99]A 99)999I9Y9i9y999999 ]%/EX/>YEX/>YEX / >YA]A(8HXhx ]%9*%901""#6'6>32#".'2>37>54.><3 494  (U)*t9e[*.RC(E7' )-)     ._%04:A 65P^Bd=$9I%>39S^'KsG6%Z%IU+I<+<&UI901>32&#"'>.>'..54>32#".7Q)8%#5' ! JA     #,7=4$ )!6Ug10'  &,."9$RE-/RpH#A8-Gi}`9"08Z (($32 $/#%)0*O<$!473)%1c(ExwWd+WAWW&W6WFWVWfWvWWWWWW ]AWW]is+i\+\Fsi901>32&'.'&>76.7>&'.>'..54>32#".7~)8%#6' %5DUf<<4( $2(L?, "$" $)!020)&!   #+8=4$ )!6Tg10'/?#."9$RE-1Tqu-ⶂ? 3@HL&7/ ';& 0A?;+]** &13Z (($32 $/#%)0*O<$! TK5%1[(B8-+8A88&868F8V8f8v888888 ]A88]%и%/EX/>YEX(/(>YEX-/->Y01>.'.'"#6'6>7>74=G& :IN=$[a\ )?3,)-)!,";HL!  494  (K(  NKA"3$!PK=     L_b!URA  ED4:A } ALJ0)/01>32&#"'>.0)8%#3#   @8      $QE./SqHq_ Kvm?"08G!_EV+E/:+/$+AEE&E6EFEVEfEvEEEEEE ]AEE]VE9A::]A ::):9:I:Y:i:y:::::: ] :/9V9A$$]A $$)$9$I$Y$i$y$$$$$$ ]4V95V9JV9aEX/>YEX4/4>YEXJ/J>YEXL/L>YEXP/P>Y01>32>>#>54&'.#>54&'.&#"'>54.G)9%(6%jpk&ITYRE    IJE   JJE  JA    9$RE-:362?;.; +L61flq< #0*>?;SPMIMJXg #0*>?;SPNINKKZ#B:.Gi>?`9"08A!Dͺ++A++]A ++)+9+I+Y+i+y++++++ ]+9+'и'/+)к0+9FEX/>YEX0/0>YEX2/2>YEX6/6>Y01>32>#>54&'.&#"'>.A)9%0:"[eh]J   ckb JA    9$RE-6/%-,J31flq< #0*>?;SPKIDCO]]^#B:.Hi|`9"08QJ7K/L/KEиE/A&6FVfv ]A]L;ܸA]A )9IYiy ]EXJ/J>YEX@/@>Y6+6Jܸ@ A  ' 7 G W g w ]A ]0132>54.#">7#".54>32#".7>7B`=AeG=`B# 1#4.% ! '6? ( "/6:?kM,>lQ]q>%и>/EX/>Y 7+ -A--'-7-G-W-g-w------ ]A--]01>.54>32.54>.#"32>54.'"&_ @iNPZ6^~H%RF.2KZ''  4'+R@(1UvE%3!   )_Fsm=mR/.fs_h6-J64H#!$($'LqKGc:2AHI!6KK?WuM(,+I"+IA&6FVfv ]A]A""]A "")"9"I"Y"i"y"""""" ]3и3/I8и8/IFиF/EX8/8>YEX=/=>YEXB/B>YEXD/D>YEXF/F>Y '+ 01"32>7.#".'>32#".54>4.523 #"'.NmD3XD.& '  @"))AQ(Mf<>g9 04/ )*)((6ABf~<4nZ9 +  !$%16J.9hZYpD-9-N8 W%0(+(#ܸи/A&6FVfv ]A]и/EX#/#>YEX(/(>Y016>7>."#>.p(M)9@pR !%" @Ve50  4;4    !'VB>M!!YEX?/?>Y A  ' 7 G W g w ]A ],A,,]A,,(,8,H,X,h,x,,,,,, ]?KAKK'K7KGKWKgKwKKKKKK ]AKK]01%.32>54.54>326.#"#".54>32j ;@6+GR $YN5.LadaL.6Rd.=mV9 #&$ 3I-#@3-J_c_J-Bdv4EHD5 #8G%&J8u#-$/ :/&.7UA=T3?aC!B5! 6)"(!9XB>X9+:&%7%3E%=D+и/A&6FVfv ]A] и // + и/и/AиA/DиD/013654'>7.#>7..'>'.>7=4c3 BAED  CC3 (TF0*HZZP$& 0]-npqv!_sE $'$ HrU5 7E  "A(%LD8$ @^zYYK GEX / >YEX/>YEX/>Y>A>>'>7>G>W>g>w>>>>>> ]A>>]016"#67.67654>7>'>76.7>74.)M(  /5. 1?M/XvJ$ AJOE5 h ")&8X?EdE*  ljVff@57*-Mhtx8##JE'(:HID+,'$ 2nCY01%267>7>#".'67)S5 6>7 $'+16?G))G>61+'$ 8>6 4T9??]xa::ax]??KHwEX/>YEX/>YA'7GWgw ]A]201%267>7>#".'#".'6732>7>.3t(S5 7>7 %'+07>G)61-,06)G>70+($ 7>6 4S)$  %B9??]xa:8M--M8:ax]??$GfB I^gU66Q`[LWa5%EX/>YEX,/,>Y01..'&'>7'.'3>7. 1AK' 2eWA .4. /9A$%B:0 /4. BXf2'L@2 Y?5*5ZOI% +V_mB4ZPJ$$KQ[5Cn`V,%IOZ5\<@FP-\olA#/1/%+%+01#".>32.32>7.'7>7lI3=WxRPm; %R?#E8' &+'  $' *?);ZC0"6  EiQ 1hJJooJ%:*,BKA+4Wpxw2d QUU%Y[6 WYa+.JEX/>YEX/>YEX!/!>Y9+90167$7..67>7&aMM 8`WONN*[6t  Qx;h &3 6WLIQ`=  )  *) N~~[Fg7h/i/hdиd/A&6FVfv ]A]iPܸA]A )9IYiy ])dP9H/EXZ/Z>YEX\/\>Y A  ' 7 G W g w ]A ])\H901>54.'&#"5>77#"&'&54>7>32#".'.547"  .B/9R5$3 *  )  0&B)0-M=-  *6E)3TA/#'#0yFR9*cV<\e^q*>I 032 5   ,*!$VPA -DQ$Wo6qle+&E<. 7Tb*hsw-', /EX/>Y 9 901">73#6#".54>7 !.   #-* '3!(2JX+v(' /<3& G[M A+ 7*+7N+NA]A )9IYiy ]A  & 6 F V f v ]A ]A**]A **)*9*I*Y*i*y****** ]XAN9]EX/>YEX/>YX+XI+I%<+%01.#".5467>7>54.#"32>54&'>7#".54>7>32$7quU[ 1uuo-:eK*3Rj65`J+2D(83$,3"<,5Vo:9r[8"6D!8{@MrFOV /()0  ( 7?F'3wUGsQ,'He=(J9",?)$9#5? ?cE%/Qm>+RG:! 1\Roз< B"a?$+? W+ AWW]A WW)W9WIWYWiWyWWWWWW ]IW 9I/AII]A II)I9IIIYIiIyIIIIII ]A??&?6?F?V?f?v?????? ]A??]a$9cEX/>Y\+):+)DADD'D7DGDWDgDwDDDDDD ]ADD]01>3#".54>32.'>.#"32>54.54>54.#"UOcq8@rW32LXM54DHE7!BkHJ`73MZ'=8- &)& ,6+0EO EkH&9VdV96P_P6%O-&IiC/H7*#'&&*0:F+RvL$7aK'<)$07= -$'%2-CO!Y?!+?L+Lи/и/и/LNиLPиP/L[и[/01.'>7.#".7>7>.#"32>7#".54>323232765454'3>7)T+(L)HFOe *! +S# 7M/&B1 8I*A@ *R*w-gid*+gkh,$s:==/.5.(' @?"-/T@%4F'OB *)**& Jh$O6+Oи/Oи/Oи/O!A66]A 66)696I6Y6i6y666666 ]Ojb/h/EX&/&>Ye +e1@+1T+T9;&b9&JAJJ'J7JGJWJgJwJJJJJJ ]AJJ]01.#">32#"&'.67>32.#"32>54.#"#"'>73267H MVX*6m9  BHJ#==#=V3B5"&7=5Z@$%GhCZtC( + $ Q_*A:<+0+0<09A]A )9IYiy ]A&6FVfv ]A]&ܸ0CA/EX5/5>Y+++5A95 A  ' 7 G W g w ]A ]0132>56.#".54>32#".54>7P^  'N{ZEkK'*Me6$>/!$!&"/+*Jg=_].0][Ny\?(>{|Ty^Y6 +6M2901>7>7.#"7>547#".54>;32672MzM*O&AH3_R?  $)% -;"70 - dh=x6 "+(]]VB'c7g6  )%2' )0)SC+8V;"C3  *3Yu +uM6+M*W+* aAaa'a7aGaWagawaaaaaa ]Aaa]01>54.#"#".54>32#".546732>54.#"32>54.'.54>32/!!8I(+N:#&<+)`_XD(:hKBf?'He?)PH=-#;M+ ?2#    %3 ,EU(!=/1Pe45]D'6Xo93lZ:>av7:rX7.="N"&,3I.+8)'):FQ`n?Li>1WvE<|e?1?ED+F3)>*+U   &*R@("=U3=`C#*H_5@gSBY5 +5++01%>7>.#"32>54&'>7#".54>32P^ 'N{ZEkK'*Me6$>/!$!&"/+*Jg=_].0][Ny\?(>{|sTy^Y01>32#".>32#".O !!'  ""'%!&$& &# .vTSEX / >YEX/>YEX/>YEX/>YC 9 JAJJ]AJJ(J8JHJXJhJxJJJJJJ ]013>32"'.>'67#".>76.'&.!&-9H[rFP*0EX3JYd2 (0:D(3WI;. )٧cH}òF&Q-Q0()*<$ 7U:*FY]ZG-70/WY˜(W=Lr ]+ A]]]A ]])]9]I]Y]i]y]]]]]] ]tEXi/i>YEXn/n>YF+F+<++016>#".54>32.'>.#"32>'.'.5467>76."#6n(^(Nl=+F\3+NyJ5aX%XYR?&)F\3%I?.'+&*):# 9NY(>a=gP+]ZQ?&0Wt9 6=6  7 4YV=R:) ?]~QV_3 -;G)7cJ,/AIO' D>*"7H%5D'(KkB[yN, &5E,Sk>ЊMXZIFT7+C +CA ]A  ) 9 I Y i y ]A&6FVfv ]A] $и$/CVEX>/>>YEX0/0>YH+>A]A(8HXhx ]0A'7GWgw ]A]0132>7.#"32>723'.54>32#".7>7L4ZB"C:. DkFOzY=% 6X}VKpR7# +.+ %FatC\bC" @`~_XT2Uq@BHF8"!5B#$?0 /#GoL'T^d/IoX< -`PNl?:eM6O3 '+G<4We{M%EXD/D>YEXI/I>Y016 >7>.'.>7'.67>'"#6n(^(l׶ $BbC!MJ@' "ABF'?CI&;U,AQZWKNuQ0 Eis 6=6  7Ő ^w7.#">7&'6 '>&'.54>7&>'.'TMTFFN _Utl  *qs\ -ReqiZ("/:  5%U^[D! Zzm   :ys  ;ZXM  6/2+*E5& 6>4# $(%    ):&1-  U;K$+EX/>Y01>7.#"#6 '>&'.54>7&>'.'TMRFEQ   *qs\ -RfpiZ("/:  6%U]ZC" Zym   :=:ZXL  6/2UD5& 6>4# %)%    ):&1-  ODD 8+ D+DA  & 6 F V f v ]A ]D9/A]A )9IYiy ]*ܸFEX?/?>YEX1/1>Y1?9?A]A(8HXhx ]1A'7GWgw ]A]01.#"32>76.'>32#".54>32(OvOP~_C) !8Y|TVd:-Neif'"[cf,Lg<+Lgw@YdD$#Dfa^LmUh:4ZyG3d>YEX / >YEXD/D>YEXI/I>Y016 "#>7.>'67.7>6%6 "#6(^( 6=6 :KdH>8.=0 *-( 8MRP< [l(^( 6=5  /)KKP.TX*,8> + (Q2 ;V88R6Su@ -fQMXZ`$=%EX/>YEX / >Y016 "#6v(^( 6=5  7MXZ 9B-+- 9+ # 9A--&-6-F-V-f-v------ ]A--]A99]A 99)999I9Y9i9y999999 ]D/01'".54>32&'>&'&7>&54.'*Q}],i[=(E\56F&"q" @6#2IS":K, jl1N<]?*Q?'"6B@833, .?.B( Kk6"rh^=D8@+8A88&868F8V8f8v888888 ]A88]ܸ8EX'/'>YEX,/,>YEX;/;>YEX@/@>Y016>7>'3..'.'"#6t(^(HE<Nm@ 8jZ!CCC (Z[X%1UG8+ 4;5 $.8!IMN" 6=5  7 iP]z   Tiwzw4,ilhWA όMXZb47;+A&6FVfv ]A]и/9и9///EX4/4>Y0123 >7>.".#>.#"".7n+T+ Gte]bnD %# $'$ -CU30=3$M>=w}P 7̝  ,;B7!"08'WF( @Ye\J$'8pCxNx +xgj+gST+Sjg9"TS9Axx&x6xFxVxfxvxxxxxx ]Axx] S9EX/>YEX/>YEX*/*>YEX:/:>YEXS/S>YEXg/g>YEXi/i>Y:9":9*FAFF]AFF(F8FHFXFhFxFFFFFF ]]A]]]A]](]8]H]X]h]x]]]]]] ]sк:901#".54>32>32>7>32.76.'"#.#"+4>.#">&'9'4%5#(CY0HO' .YEX / >YEX/>Y0:+0 JAJJ]AJJ(J8JHJXJhJxJJJJJJ ]0VиV/01263>>..'>32#".7>.6`*Z* ?Th>WlH., '=,!* " 76(E3-B+BaE*%)$@xaGkO5    ;:w>6\D'-Pm@NA._P82<3!$!#(#(:#;L) E8$;`z}u+Kn>?g>TJPZXLBDSmT/U/TNиN/A&6FVfv ]A]UDܸA]A )9IYiy ]EX=/=>YEXI/I>Y A  ' 7 G W g w ]A ]=A]A(8HXhx ]0132>54.#"32>74'23".54>32#".'&>7W ) %;RkCGjM1  4H_<72% %=3$ "%" *@PRO=% '8H-UiK1B}qpR$9F#1{IT~\4:`~=A{]7/ERE/*;@" .5(?@EJJ;%YEX]/]>YEXe/e>Y A  ' 7 G W g w ]A ]XI9I(A((]A((((8(H(X(h(x(((((( ]6XI9SXI9013267.67>54.#"32>7.54>32.'6.'#".'&>7Y* %;RkC<]%+VJ;r ;M+'  4H_<72% %)'$"A$>RURA( '8H-TiK1D?8dB &*% &7";YpR$9F#1{IT~\4)# $.;&.$ 1v||6A{]7/ERE/!++(8!/4(@@EJJ;%Cngs6\HJX,+, 4+ A,,&,6,F,V,f,v,,,,,, ]A,,]A44]A 44)494I4Y4i4y444444 ]<иYEXA/A>YEXF/F>Y/+/9A99]A99(989H9X9h9x999999 ]<и32#".54632.32>'.#""#6r0aba/3utjQ11Qirs2YEXd/d>YEXi/i>Y-?9\A\\]A\\(\8\H\X\h\x\\\\\\ ]01>32>54..'>#".'.54>7>54.#""#6j3YX^9ZO-KagdR9"@SPC!   6#0$ FF8#$37D}`9-RtF5j6 6=6  7 !UsJgF+$%Y][( &$<:0!1#  =Wh6)TD+&=LME&1%!*<.")  !=cNVrE ǎMXZOO]^/_/^ и /_Gܺ G9A]A )9IYiy ] 9A99&969F9V9f9v999999 ]A99]EX%/%>YEXL/L>YX+XܸLA'7GWgw ]A]%4A44]A44(484H4X4h4x444444 ]01%6.#"32>54.'.54>764.#"#".54>32)D1DUc7P]3"8E$0z~y^9IwKH}Y ,0, +PuI\W>"6EF?1lh^F*XV&be`K.:QV3hJ*:$+M=/?&EnP.F5&#,YEX?/?>Y#+# + #ܺ#92#97#901.#"32>&'7#".54>323267.'"#6'4j9BoK"SN%*&,*%%>U0-dV8&@RVV#=i`\bj?P8$'# YK# 6<6  (E9\JWW+<++M+MA++&+6+F+V+f+v++++++ ]A++]AMM&M6MFMVMfMvMMMMMM ]AMM]2<M9EXC/C>YEX/>YEX/>YEX / >YC9C&A&&]A&&(&8&H&X&h&x&&&&&& ]2C9016 "#>7#".54>7>.#">&'7.54>327>76)]) 5=6 SmWUoB'1"&!?0 q1<>3 !-9C%=I$  ,7=X;# -*T,,H4A?11Ocd\ ~BL`p;MlHPF +F AFF&F6FFFVFfFvFFFFFF ]AFF]EX/>YEX8/8>Y-8901#".54>327> 7 #".'&7>.'(#'&?-+>)BkT@1$ ,15898840$($ &D@<;9<>BI(0A/#"'7M7.!%   XB#+!8T`'%K=&I|ĸxAJuyN L}ù{HWǤ3&;G;  bg~S.I+.y+ܺI9A..&.6.F.V.f.v...... ]A..]8I9eI9fI9tиt/Ayy]A yy)y9yIyYyiyyyyyyyy ]EX / >YEX/>Y\A\\'\7\G\W\g\w\\\\\\ ]A\\]01#".'#".>7>.'.7>.'>7#".54>>.'77>54.54>7bZL"("*QxNYmA# -Ea@PnF$  #$575)   !$" ,@)$C3"9L*kF !=11J8%    .HcA$6#&-&*I9 kN_Z=nT2CuZQyK-Mfqvo_!2cbb`^/&0*;"  7:7,9S5,ZG-N~>MlP0[ڹ ǔP Y A ]A  ( 8 H X h x ]-L297L29AL29GL29017>7.67#".54>76>77.'Cyne0+NHA=8"),^ ,8B# -E04ZRJGE$/aeh6&+'6v~A5q|J1]|o;1ddc0:NU_5&59'/#,9&;(4IP(WI03ZyFSF#($rlrLj<OnS91o?R@/A/8ܸA]A )9IYiy ]@и/A&6FVfv ]A])89./EX"/">YEX/>YEX;/;>Y" A ]A  ( 8 H X h x ]).93.901!67.67#".54>76>7"Y +PJD@:#(-^;=8B# -E04\SMIF$0^`e6&,'5}~5 6=6)Ta7&59&/#,:MP3JO(WI01XwESF$($woĒW3BnI-F+-A--&-6-F-V-f-v------ ]A--]EXK/K>YEXQ/Q>YEXN/N>YEX/>YEX/>YEX/>YA'7GWgw ]A]N!ܸK&A&&]A&&(&8&H&X&h&x&&&&&& ]KAAAA]AAA(A8AHAXAhAxAAAAAA ][A[['[7[G[W[g[w[[[[[[ ]A[[]`A``'`7`G`W`g`w`````` ]A``]01%>32#"&#".5454767".#">7#".54>73267 23267>54&'!3A#,(;&fkPXaZYEX?/?>YA'7GWgw ]A]5A]A(8HXhx ]0167>54.#"3267.54>#".54>&_)_)   !3%EvU1(@R+'4  ''ZK2.FR%H~^6ZPNi@ ?KK6!IHB1:cGKqM'$($ ! #I36J.7g_sf./Rl>mK/EX/>Y017>32#".K !"& J& &# ~bEOc+d/e/Aܸ A ]A  ) 9 I Y i y ]d7и7/A&6FVfv ]A]EXYEX_/_>Y2+<A]A(8HXhx ]01%4&>7>76.#"32>54.'&>7#".54>76'>32#".#, J@*U5;cIKh=#9HIF ""':uph-!@HS37dL.%AZ6,%! *7<*>)"8J'FyY3/ZR5RF=>D* :GL=% &# n}// и /Xܸ1A11]A 11)191I1Y1i1y111111 ]и/1.и./ uAuu&u6uFuVufuvuuuuuu ]Auu]n+$+S6+S+++01%.54>2&'&'#".>32>54.#">72.54>32>7>.#">467.#"67&54WպP_y;)?Td9F%>CDkEGfD~<,Kf9'28/"! "&& 6DI=';[m1Tl?  0>)$+cӆ8}zpV4EyǸ >;*C)#SG>j" ^v5 D|iI{9G +  DgxgD%-CiH%)  &17(3M22]R;DK&.*!  9'^õvD7ZwosC)gD#(+@KB-!#*H=2/ / 29 29! 293 29D 290167.7>'.5>76.'&>7. 07-A/,686VP9/A> $E[]W(aV<-70 -K_22_K-`(IHHMU0)*  ):(Apln@,)"d 3A?nlqA(:) )*0UMHHI(#<5++4=SV 4 +и/ //EX/>Y01".# "! Vfv|(L=/@/@9@9,@9-@9A@9014>7.54>'.77&'>76.~-J`22`J- 07,Y01 "&#& '>32#". *  !"'1   & &# 9u V//901%.'>54&'>7mm?B?u6utn- -lst6ySmjQy7o^ ' /// /ܸܸи/01$7&$$7&$;33      0uV//9017'6$7&$'7p@A@muyQjmSy6tsl- -ntuMq+A&6FVfv ]A]//9901.54>7&7Q`C**C`Q4YG6%%6GX5iGGiu.g_^°g.uMu+A]A )9IYiy ]//9901'>54.7S5XH6%%6HX5Q`C**C`u.g^_g.uiGGiV(?9_/ /EX/>YEX6/6>Y6969#69+69.6901>7.'.'>7.'>7>.'23  &QRP% f:.5;9+V-<[%-Nq KRV* ZA  00=2VON*%#& % $FC? '`*2K$ G8?2F +RRT,;9#s+и/ܸ и ///#+ + и и /#и/ 0134&'7267.'#>7";jMnJdeK   KifJghK    KfgMP1|EX/>Y + A]A(8HXhx ]#к- 901232>7#".'.#".'>?*>2*/6>&9H(@MV-*>1*06>&9H(@MVP%+(08 #VK2%+) 07 #VK2Kn,)/EX/>YEX / >Y01>&''".5>32'>5467 &  ! .#7O2 6<5  % %.32`I( !!  O-A)3/EX/>YEX / >Y01>&''".5>32'>5467>32#".  '   .#7O2 !!' 6<5 # %.32`I( !!  D%!&$;9f#// //и/0132>7.#";Mv78vNMv87wNf   }\ / /01d "$! J_}Y //01>7!%" cǃpvuI +и/A&6FVfv ]A]и/////016 '67.67.M t`BO OB`  #&"  qz  "&#  *+и/A]A )9IYiy ] и /!////01&.576'&4>77 s`BN  NC`s  #&"    "&#  <=I /// 901.'6753&' "%!XQ`QX !$" >EED.13H ////0132$7&,1tttu    -Q\/-/EX/>YEXQ/Q>YK+8R+8K и /и/ и/ и/ Uܸи/Rи/8ܸ8и/8и/R'ܸ*и85и5/=и=/R@и@/UCиC/FиF/KNиN/UWиRZиZ/01>7&'>7'>7'>727>7>7.'>7.'7>7.(  'vu?~A  >z>AA  %(% %BB  %)$ %8l8x<    B v^a}v] oLMof߆cfa  ML  a{/NNNMI / /// //01".#6&'".#6&'   "    # *TSR'MZ*TSR'MZI / / /01".#6&'   " *TSR'MZ-/01>7672&#".'.54>.d ' ! /"7O2+ 6<5  % %.32`I( !"  -/01>&''".5>32'>5467  ' ! .#7O2J 6;5 & %/22`I( !!  l-[ /B/01>7672&#".'.54>.>7672&#".'.54>.d ' ! /"7O2R '  ! /"7O2+ 6<5  % %.32`I( !"   6<5  % %.32`I( !"  l,Z /A/01>&''".5>32'>5467%>&''".5>32'>5467 &  ! .#7O2  ' ! .#7O2J 6;5 & %/22`I( !!   6;5 & %/22`I( !!  USfQ;5f#/ /// и/0132>7.#";:rrȨ::rrɨ:f   Uv';EX/>YEX#/#>YEX7/7>YA'7GWgw ]A]и-017>32#".%>32#".%>32#".U ""'4 ""'3 ""'J& &# & &# & &#  ^3 eOU9Y&MQ CBB] +]N(+N2D+2++A]A )9IYiy ]ܺ% 9: 9ADD]A DD)D9DIDYDiDyDDDDDD ]ANN&N6NFNVNfNvNNNNNN ]ANN]U 9A]]&]6]F]V]f]v]]]]]] ]A]]]A]A )9IYiy ]EX-/->YEX/>Yt+tj+%-9:-9-IAII]AII(I8IHIXIhIxIIIIII ]U-9bAbb'b7bGbWbgbwbbbbbb ]Abb]01.54>32##".54>7.54>32.'732>54.#"67&32>7.#"32>'>7#".54>32>76.'&32>78Q^P53N\*9[@#,RsH deWSAf}.H26Uh1.jkdO2 1$ H;' 4?#=*0Q<"4Sh4;X:{|>.ahGuZA-gTyO&6V=+B*3.3 )9$(CY03P;% 7Ne=NjA;gR*"')=6.!"4A 9K, ;4B<+ "6$ /P*EX/>Y+A]A(8HXhx ] 9&9012>7#".#".#>'')G.&/ /9@!$4,(.8'&/ +/9@P(*&- F=)(0(&+G=*^-YgzZN+Zm+{+и/A{{]A {{){9{I{Y{i{y{{{{{{ ]{ ܸ{ии/и/$и$/Amm]A mm)m9mImYmimymmmmmm ]m4и4/N;и;/mEиE/mTиT/mYиY/AZZ&Z6ZFZVZfZvZZZZZZ ]AZZ]m_и_/mbиmoиo/mqиq/и/и/Y/)/,///m4+mYcܸTܸи/cи/4$и$/E)Y9mи/01..'"&#"&#>7.54>325.'.54>;.'.'#"6545.#"%4.'>C{a? (,) "7J-5X#>.;`z? )) &[]WC)-HZ,).1*J:bH(Nv>2Sk9/,*#6L4# s`+K&5!&()+/5%:>-C1" vppgr.}6lxں1+'+':g+:]U+]A&6FVfv ]A]A''&'6'F'V'f'v'''''' ]A'']'ܸи]FAUU]A UU)U9UIUYUiUyUUUUUU ]Agg]A gg)g9gIgYgigygggggg ]]zp/x/EXb/b>Y ,+ bAAAA'A7AGAWAgAwAAAAAA ]AAA]0132>5454.'&".#4632#".54>732>54.'&".#4632#".54>7 &*  ! A>7,(AR**P=%u %+   ! A=7,(AR**O=%c "$!  7f> +12))?M$3&!39j}+I_3?jM++Lg;"AAA!8e> +12))?M$93&!39k}+I_3?jM++Lg;"AAA!_.}S6lx1+'+':g+:]U+]|+|+A&6FVfv ]A]A''&'6'F'V'f'v'''''' ]A'']'ܸA::&:6:F:V:f:v:::::: ]A::]]FAUU]A UU)U9UIUYUiUyUUUUUU ]ܸA]A )9IYiy ]A]A )9IYiy ]p/x/EXb/b>YEX/>Y ,+ bAAAA'A7AGAWAgAwAAAAAA ]AAA]0132>5454.'&".#4632#".54>732>54.'&".#4632#".54>732>5454.'&".#4632#".54>7 &*  ! A>7,(AR**P=%u %+   ! A=7,(AR**O=%c "$! b %+  ! A>6,(@S**O=% 7f> +12))?M$3&!39j}+I_3?jM++Lg;"AAA!8e> +12))?M$93&!39k}+I_3?jM++Lg;"AAA!_8e> +12))?M$3&!39k}+I_3?jM++Lg;"AAA!Y V /EX/>Y01.'3hD7BI$VNy)&=3-W V "/EX / >Y  901.'>7$IB7Di3-3=&(zN8&% j)8&% k{q& j&.kLDк4)+4>+A&6FVfv ]A]и>ܸ>и/>!и!/A44&464F4V4f4v444444 ]A44]j)9m)9EXg/g>YEXm/m>YEX$/$>Y;A;;';7;G;W;g;w;;;;;; ]A;;]!$;9gDADD]ADD(D8DHDXDhDxDDDDDD ]jgD9$g901>7.#">7&'465#".'&>732676'.#"32>74'23".54>324&5>&'.54>7&>'.'TMTFFN _Utl2rApR$9F#E ) %;RkCSv( %cA72% %=3$ "%" *@PRO=% '8H-9a**qs\ -ReqiZ("/:  5%U^[D! [zm   :yt  ; "&ngs6)1{IT~\4NA5@/ERE/*;@" .5(?@EJJ;%    6/2+*E5& 5?4# $(%    ):&1,  . sOt/u/tи/A&6FVfv ]A]и/и/uYܺ5Y9IкNY9OY9dY9YjAjj]A jj)j9jIjYjijyjjjjjj ]EX / >YEX^/^>YEX`/`>YEXd/d>Y!D+!01>7.#>.'&4&'7&>32.>7"2>7.#">7>32&#"'>54. -T)  0(>Cy`-^M2"1(<$ '"    $0:3I0;CB(#5' JA    7- O_9 Aa|L * iX"?`9"08;bYc/d/cAиA/:и:/dTܺ:T9A]A )9IYiy ]:T9и/и/A2A22&262F2V2f2v222222 ]A22]'к/:T9AHиH/TVEX/>YEX9/9>YEX]/]>YEX_/_>YM"+M01>'#.>7"2674&'.#">7.#>.'&4&'7&>32&#"K   );# (!    ]63I/;CA?{7  0(?Cy`5q+((  ?9bU*63(6   87I|[ "E"O_9 Aa|M ) i”X3+*{JICq_ ]x&'fUvI&jB &kK& fS[& j[& kx[& frQub'///#/01>32#".%>32#".Q "!'W ""'%!&$%!&$?Jt>W;+WA;;]A ;;);9;I;Y;i;y;;;;;; ]vh/EX / >Y8\+8!&+!и/!и/h 9 A]A(8HXhx ]!и/)ܸ8cиc/Rܺ.cR9icR9)qиq/&tиt/01.54>32.'&267.#>32654.'3.>32#.#"'>76&'?Er6/VxI3ZK<".7@N' SrlN96O]c--PNR0@1"1!#4< +E.6O4#7B/NEABG*/rwr0@JF7 "9~MBGSa6 5C#K)D1Hly08p7   M|4 $ -/#H;=4&0+  7KTG/2M]*@W6 / 6O-U`/ //01>7.'$IB78BI$f$"%)B&B-3=&&=3-q4,&"X<&///01>7.'&B)$##f$IB87BI$YA]A(8HXhx ]01>32#".'732>54&#"A#4B""C3 5D$!@3"R;(( ;+# .E-.F-.F.+B- 35$,; (d3Oc"d/e/dAиA/ A  & 6 F V f v ]A ]e7ܸA]A )9IYiy ]_/EXY2+2<A'7GWgw ]A]0132>54.#".7>32.54>7>.7#".54>32/$, J@* ;U48[@# +1'%' #5! .BO)=V4;cIKh=#8HJD !"'5:uph-!@HS36eL.%AZ6,% ! *7<*>)"8J'GxY2/ZR5RF=>D* :HK=% &# 8 &% |+8&% fu8&% zd8k&% ~C'_!&) j_!&) k!_!&) |_!V&) z9B&- jV&- k&- |_&- zxBSd&2 fVL&3 jVL&3 kVL&3 |VL&3 fmVLg&3 zJD:C[kl/m/lи/A&6FVfv ]A]и/m8ܺ8989@89NANN]A NN)N9NINYNiNyNNNNNN ]+/C/EX%/%>YEX=/=>YC+9C+9@C+9GAGG'G7GGGWGgGwGGGGGG ]AGG]%eAee]Aee(e8eHeXehexeeeeee ]01>7&'&>767.54>32>7#"&'32>54.'2>?.#"8N\$9F#E ) #)XN A>6( '8H-4Z(  "%!%3I/B}qN;'y*qKGjM1 )-C`3253=F*72% =3f3dgs6)1{Iz]S =2EJJ;%:#D"5Pl40-X-6=:`~=:|{r0 6]s */ERE/Eh&9 jQt&9kCf&9 |CfV&9 zw9{q& |{q]& zt@uk&~~pI!nfo++ A]A )9IYiy ]9ao9kиk/иܸEX^/^>YEXf/f>YEX#/#>Y6r+6и/#A'7GWgw ]A]#f9yиy/-ܸfܸAиA/a#f90132>'..54>'.'#".>32>54.#">72.54>32>3.#">76..'&1)VL(XI-(54 %`  4NZN4;`{@ JH@LROPwO&)OtMD~<,Kf9'370!! "&% 6DI<';[l1e88IV.WzN'=:*C)#RGC>52K3'J<*Ad>6H((9!+:<-?cD" &(ASWSA(&-CiH%)  &17(3M2IA4'@f~>(+@KB-  5G*UC+2A[&& |M5&zGE& jI!%/01>32&#"'>.I)8%#5' ! JA    9$RE-/RpH#A8-Gi}`9"08S& k%&& | f& zI[& |[]& z@9)=@/9/EX%/%>Y++ и /0132>7.#">32#".>32#".9dHHdcHHd} "!'  !"& j "$"   \& &#% &# AtV:HRS/T/Sи/A&6FVfv ]A]T/ܺ/9;A;;]A ;;);9;I;Y;i;y;;;;;; ]>/9N/9$/:/EX / >YEX)/)>YEX/>YEX4/4>Y  ܺ:$9>:$94DADD'D7DGDWDgDwDDDDDD ]ADD]N:$901>7.7>7>7&>32>7#"&'4&'32>">7&`2>ElQ>m0,+aZM0=`B#"8+;Y901672634>32#".Z * '"!     #& &5K+c+;R+;"+A&6FVfv ]A]A""]A "")"9"I"Y"i"y"""""" ],9A;;&;6;F;V;f;v;;;;;; ]A;;]eEX / >Y'+@M+@W6+W A'7GWgw ]A]012#".54>32>54.#"%676.'&32>7#".54>32.'U|Z3jVwU0T>Ȋoʙ[ZpȂ@$(% $J_*:^B#8Y>3aN5( Bb{A\f75e];{d@)@OJ=*Op^c+Pr]zjhʠcRz{̓Q[W %A0Ag>:rZ8YEX\/\>YEX/>YEX/>YV4+V{d+{9 ܸ*A**'*7*G*W*g*w****** ]A**]Y9nкz9 01%.54>'.'#".7>732>54.#">7#".54>32>3!32>'..#"4N[N4;`{@s,6`]q>%C8J^@e~>  *gX<6G((7*UD*%?T/. 36˺1+'+'A&6FVfv ]A]ܸA]A )9IYiy ]'8"/6/ ,+ 0132>5454.'&".#4632#".54>7 &*  ! A>7,(AR**P=% 7f> +12))?M$3&!39j}+I_3?jM++Lg;"AAA!31V"޺+A&6FVfv ]A]и/и/ ܸи/и/"//EX/>Y + +и/ и01267.'".#"'3.' JdfK  # HdfJV   )# odg37V9N.3+.3и/3и/3 и /3A..&.6.F.V.f.v...... ]A..].и/.и/3!ܸи/!,и36и6/./3/EX/>Y$)+$ +  + $и/$и/ и и/и$!и!/,ܸ6и)9и9/0136'"'3.'7267.'267.#".#>7"9dJHdfJ{ JdfKKiiL # HbR odg      vz I=MW!F+FAFF&F6FFFVFfFvFFFFFF ]AFF]/=/EX / >YEX7/7>Y*A**'*7*G*W*g*w****** ]A**] AAAA]AAA(A8AHAXAhAxAAAAAA ]I=901>7.54>32>7#"'32>7#"&'.'&>6&'6I-X+*B-5e]#L#-W* "$! 6h11?5O.-'-W*-3bN4( Bb{A#?&L#2;^B# %4CT!" PQIW`3R}L fjdc"[7->]] B..d'$?99  1oj+Rg+RR9A&6FVfv ]A]Agg]A gg)g9gIgYgigygggggg ])gR98и8/9gR9RDиD/RGиG/g]и]/gdиd/./EX"/">YEXU/U>YEXZ/Z>YaO+aGR+G<A+<и/<и/" A ]A  ( 8 H X h x ])Z.93Z.9<9и9/DܸaJиJ/R]иGdиDgиAjиj/01.67#".54>76>7>7.'>7.'"#>7'>7"7YD)LFA<8#(-^;=8B# -E04\SMIF$0^`e6&,'5}~5E^`GE]]E  6=6  D[]EE^Q|[3&59&/#,:MP3JO(WI01XwESF$($wo   3b0  GABHo0c2XfEX/>YEX_/_>YEXd/d>YEM+EBиB/MTиT/MqиE013> 3232>&'.54>7&>'.'32>7.>7&'>54&'.>'67#".>76>5. #*3;DMU^h8*09%1BCw& &H\fbV("/:  6%NQN8 Mfvqa8W%%AFP38XMFJS4 _Utl"4AD@+P:'48* "$! "CY+RXJnCT][&/RB2`YQE6$!uqA 9?2UE5& 6>4# $(%    ):&1-   :ys  ;.>;,8\I5!(?R0()*=$ 6U:IopK GAW^Xkk϶(CP'<&+&9) /EX/>Y 90136#"&'>732654&#"#"&'5HE2 5C"*Q #-+ o&G0.C,)- 4&*&  SMF&' O^C&<5e{|/}/ܸqAqq]A qq)q9qIqYqiqyqqqqqq ]ии/q-и-/|CиC/9и9/CfAff&f6fFfVfffvffffff ]Aff]>и>/f\и\/( +(HW+H01#".54>32'.#"32>76.'.54>7.54>326.#">54.'.'*9)6Eh{7*rgH#:J''M:"$($-HU!%[Q9$?R-'PKA1#+)"8Vg0@sY: $'" 5M0#C3#9K(9wc?"8H'0\&#$%>Q-#J"%-Di2&USuK":bI-E/$=P,/!*(8J-3[H*B0" !.E]@%<853>K*QmB%MxT.\J.1S?*<* )L},/=' #`77D+ _Q7  //01>32#".Q':J*!NB-1FL)I:'2R: :R32Q9";PCM+A&6FVfv ]A]и/IEX / >Y!D+!01.#>.'&4&'7&>32.>7"2>7.#">?{7  0(?Cy`-^M2"1(<# (!    $0:3I/;CA"E"O_9 Aa|M ) i”X"YEX/>Y5 +5(A(('(7(G(W(g(w(((((( ]A((]49:A::]A::(:8:H:X:h:x:::::: ]01%.54>'.54>!32>'..#"4N[N4;`{@bX)3b\XyN(('8K0(XI-(55 &^3K33T?* h4++:<,?cD"EsVNsE@f~>%FOO?(6H((7*UD+%@T/[QFZк>+>A]A )9IYiy ]\EX9/9>YT+TM+9A]A(8HXhx ]01.'#".>32>54.#">72.54>32'467.#"67&54,-E?BDPwO&)OtMD~<,Le:'270!" "&% 6DI<';[l1Tm? &, >;*C(#RG>k"' #  (ASWSA(&-CiH%)  &17(3M22]R;CK'%4 5D#(+@KB-!#USfQ]{eߺb+bи/bи/A&6FVfv ]A] EXZ/Z>YEX_/_>Y+и/и/ и /ܸbиb/eиe/016'6>7.>7>.'.>7'.67>'"#67a>g0 (^(0agsCCshb2l׶ $BbC!MJ@' "ABF'?CI&;U,@QZWKNuQ0 Eis 6=6 .e</̜    ^wY 9 901>7.'7>7#".>76.'>.7>&'.'?j05q7Te9tJMz7VX #>[|QS[4 FpNF|^< &3"*4K]1NU(^^Je? lN4mD#<,8b%U6)D"dEtU2;bwY?`ov6"JE9$r}]/ =Vlv| }/k5& }47Q+A&6FVfv ]A]и/O9O/ܸиEиE/OH//EX@/@>Y@9H@90123>7>7>.".#>.#"".767'>74n+T+ ;zKdE Gte]bnD %# $'$ -CU30=3$M>=w}P  +[6@Lz67(\: 7[.ʟ  ,;B7!"08'WF( @Ye\J$' H*[+I#.;m *+ A  & 6 F V f v ]A ]и/*1и1//01>32>7&#"'>7'>76.()8%-9#5lDZ?  @8  3k?@V?    $QE.K~[#T3 3Q*'q_ 8[xK#R3Z1T)\J"08 @gSbp.Ur'.'.'.'&54>76.67>327>7&>7>7>7>7>.7>2.'.'675.>766&'.'.'.''.'.'&>7&''676&'.'.'.'#"%67>7>7.'.'.'.'&>7>'.#">7&'74>>'.>56.'7>326'.'#2636.#">7.>4632##"7.327#.676'.- !&'!"J% %'#      '%! =Ub+8A!  #*.,  "!#:4055+ &Z[X#+E S.    4;<  ( #   1/+ V"@  '%   !%%%I#" %*+ %.5 %# #$% ;71 /;8 (( =>> AA?   1% L  %"   #&" &) ;         A `   32/  &)% .:-.0):FIF3D$?P^3"= #?<:(`gi1%%  &++    EE ++#HE  9&%M%&  $<  #LOQ* HKC$/13 %&)  +)2/,"%((    +  '*  ! 0"=+Q, '  $$    3 (    o=71&$.2 B " ""/0   '7  z:     D,nR`x*N  !"j"#\$$&:'(<)*,-F-//11245578`899::P::;*;<<==j=>>6>X>?L??@A A,AtABDBBCCCCCFFHRIL&LNLzLLLLNpOPQQ QQ$Q0Q[J[V[b[n[]8__``a`brde(e~eeffghiijjk,k8kDkPk\khl@AB Cjimqpxzy}bcefghklnrsutvw{|~doIHD.null     !"# $ %!&"'#($)%*&+',(-(.)/*0+1,2-3.4/5060718293:4;5<6=7>8?8@9A:B;C<D=E>F?G@H@IAJBKCLDMENFOGPHQHRISJTKULVMWNXOYPZP[Q\R]S^T_U`VaWbXcXdYeZf[g\h]i^j_k`l`manbocpdqerfsgthuhviwjxkylzm{n|o}p~pqrstuvwxxyz{|}~tests/native/hpdf/samples/ttfont_demo.fal000066400000000000000000000034521176363201700211130ustar00rootroot00000000000000import from hpdf SAMP_TXT = "The quick brown fox jumps over the lazy dog." if args.len() < 1 > 'usage: ttfont_demo.fal [path to font file] -E(embedding font).' exit(1) end font_name = args[0] flg = nil if args.len() > 1: flg = args[1] pdf = hpdf.Doc() pdf.setCompressionMode(hpdf.COMP_ALL) // Add a new page object page = pdf.addPage() title_font = pdf.getFont("Helvetica") if (flg == "-E") embed = true else embed = false end detail_font_name = pdf.loadTTFontFromFile(font_name, embed) > font_name, ' load OK.[', detail_font_name, ']' detail_font = pdf.getFont(detail_font_name) page.setFontAndSize(title_font, 10) page.beginText() // Move the position of the text to top of the page page.moveTextPos(10, 190) page.showText(detail_font_name) if embed: page.showText("(Embedded Subset)") page.setFontAndSize(detail_font, 15) page.moveTextPos(10, -20) page.showText("abcdefghijklmnopqrstuvwxyz") page.moveTextPos(0, -20) page.showText("ABCDEFGHIJKLMNOPQRSTUVWXYZ") page.moveTextPos(0, -20) page.showText("1234567890") page.moveTextPos(0, -20) page.setFontAndSize(detail_font, 10) page.showText(SAMP_TXT) page.moveTextPos(0, -18) page.setFontAndSize(detail_font, 16) page.showText(SAMP_TXT) page.moveTextPos(0, -27) page.setFontAndSize(detail_font, 23) page.showText(SAMP_TXT) page.moveTextPos(0, -36) page.setFontAndSize(detail_font, 30) page.showText(SAMP_TXT) page.moveTextPos(0, -36) tw = page.textWidth(SAMP_TXT) page_height = 210 page_width = tw + 40 page.setWidth(page_width) page.setHeight(page_height) // Finish to print text page.endText() page.setLineWidth(0.5) page.moveTo(10, page_height - 25) page.lineTo(page_width - 10, page_height - 25) page.stroke() page.moveTo(10, page_height - 85) page.lineTo(page_width - 10, page_height - 85) page.stroke() pdf.saveToFile(scriptName + ".pdf") tests/native/hpdf/samples/type1/000077500000000000000000000000001176363201700171435ustar00rootroot00000000000000tests/native/hpdf/samples/type1/COPYING000066400000000000000000000431101176363201700201750ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. tests/native/hpdf/samples/type1/README000066400000000000000000000045311176363201700200260ustar00rootroot00000000000000This is release 1.0.7pre22 of Valek Filippov's improved versions of the URW type 1 font collection, repackaged for distribution with Ghostscript. Cyrillized free URW fonts. These fonts were made from the free URW fonts distributed with ghostcript. There are NO changes in the latin part of them (I hope). Cyrillic glyphs were added by copying suitable latin ones and painting oulines of unique cyrillic glyphs in same style as the others. For all modification pfaedit was used. The license for result is (of course) same as for original fonts, i.e. GPL with an exception that you can put these fonts in your own non-GPLed documents. (Looks like LGPL from my point of view =). The "sources" of these fonts in the native pfaedit format are available at ftp://ftp.gnome.ru/fonts/sources The great font editor pfaedit is available at http://pfaedit.sf.net. That page also includes some links to fonts created by George Williams -- the author of pfaedit. Acknowledgements: I would like to thank George Williams, the pfaedit's author and developer. He is the most bug-reporter/feature-requester friendly developer I ever saw in my not so short life. At some moment in the future I must write a book about him: "George Williams and my best experience in bug-reporting." George also greatly helped me bug-hunting these fonts, explained to me some very important things about fonts and font design, quickly adopted pfaedit to my needs (or pointed me to The Right Place in documentation where I found better way of doing things). I would like to thank Alexey Novodvorsky (aka AEN), who pointed me to pfaedit and George Williams' fonts, explained The Task to me. He is also one of the main participators in the development of Sysiphus -- free repository of free software. I didn't loose my time for compiling/installing and supporting my linux box coz I used the result of Sysiphus developers' works. I would like to thank Sergey Vlasov, who tested these fonts and reported about bugs. Also he help me to make some bug-reports to George about pfaedit bugs. I would like Dmitry 40in, who did big QA for some font outlines, drawn some glyphs, and explain some The Truths for me. I would like to thank Vlad Harchev (aka hvv), who proofread this text for me. Also I have to thank RMS for GPL and URW for releasing the fonts under it. Thank you very much! Valek Filippov frob@df.ru (C)opyLeft 2001 tests/native/hpdf/samples/type1/a010013l.afm000066400000000000000000001235211176363201700206750ustar00rootroot00000000000000StartFontMetrics 2.0 Comment Generated by pfaedit Comment Creation Date: Thu Dec 26 23:03:13 2002 FontName URWGothicL-Book FullName URW Gothic L Book FamilyName URW Gothic L Weight Book Notice (Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development; Cyrillic glyphs added by Valek Filippov (C) 2001-2002) ItalicAngle 0 IsFixedPitch false UnderlinePosition -96 UnderlineThickness 58 Version 1.06 EncodingScheme AdobeStandardEncoding FontBBox -115 -240 1151 1009 CapHeight 739 XHeight 547 Ascender 739 Descender -192 StartCharMetrics 560 C 0 ; WX 277 ; N .notdef ; B 0 0 0 0 ; C 32 ; WX 277 ; N space ; B 0 0 0 0 ; C 33 ; WX 295 ; N exclam ; B 111 0 185 739 ; C 34 ; WX 309 ; N quotedbl ; B 74 513 237 739 ; C 35 ; WX 554 ; N numbersign ; B 5 0 549 739 ; C 36 ; WX 554 ; N dollar ; B 74 -56 479 810 ; C 37 ; WX 775 ; N percent ; B 13 -13 752 749 ; C 38 ; WX 757 ; N ampersand ; B 57 -13 735 752 ; C 39 ; WX 351 ; N quoteright ; B 91 547 253 739 ; C 40 ; WX 369 ; N parenleft ; B 57 -125 335 739 ; C 41 ; WX 369 ; N parenright ; B 34 -125 312 739 ; C 42 ; WX 425 ; N asterisk ; B 64 468 360 752 ; C 43 ; WX 606 ; N plus ; B 51 49 555 557 ; C 44 ; WX 277 ; N comma ; B 91 -67 253 126 ; C 45 ; WX 332 ; N hyphen ; B 31 233 302 296 ; C 46 ; WX 277 ; N period ; B 101 0 175 126 ; C 47 ; WX 437 ; N slash ; B 40 -128 397 739 ; C 48 ; WX 554 ; N zero ; B 30 -13 525 752 ; C 49 ; WX 554 ; N one ; B 155 0 354 739 ; C 50 ; WX 554 ; N two ; B 33 0 506 752 ; C 51 ; WX 554 ; N three ; B 33 -13 504 752 ; C 52 ; WX 554 ; N four ; B 12 0 527 739 ; C 53 ; WX 554 ; N five ; B 26 -13 528 739 ; C 54 ; WX 554 ; N six ; B 25 -13 529 739 ; C 55 ; WX 554 ; N seven ; B 64 0 490 739 ; C 56 ; WX 554 ; N eight ; B 42 -13 512 752 ; C 57 ; WX 554 ; N nine ; B 25 0 529 752 ; C 58 ; WX 277 ; N colon ; B 101 0 175 507 ; C 59 ; WX 277 ; N semicolon ; B 41 -67 203 507 ; C 60 ; WX 606 ; N less ; B 49 37 558 565 ; C 61 ; WX 606 ; N equal ; B 51 164 555 436 ; C 62 ; WX 606 ; N greater ; B 48 37 557 565 ; C 63 ; WX 591 ; N question ; B 65 0 525 752 ; C 64 ; WX 867 ; N at ; B 71 -13 797 752 ; C 65 ; WX 740 ; N A ; B 11 0 729 739 ; C 66 ; WX 574 ; N B ; B 76 0 544 739 ; C 67 ; WX 813 ; N C ; B 44 -13 770 752 ; C 68 ; WX 744 ; N D ; B 76 0 700 739 ; C 69 ; WX 536 ; N E ; B 76 0 480 739 ; C 70 ; WX 485 ; N F ; B 76 0 451 739 ; C 71 ; WX 872 ; N G ; B 44 -13 831 752 ; C 72 ; WX 683 ; N H ; B 76 0 607 739 ; C 73 ; WX 226 ; N I ; B 76 0 150 739 ; L J IJ ; C 74 ; WX 482 ; N J ; B 11 -14 406 739 ; C 75 ; WX 591 ; N K ; B 76 0 585 739 ; C 76 ; WX 462 ; N L ; B 76 0 456 739 ; C 77 ; WX 919 ; N M ; B 76 0 843 739 ; C 78 ; WX 740 ; N N ; B 76 0 664 739 ; C 79 ; WX 869 ; N O ; B 44 -13 825 752 ; C 80 ; WX 592 ; N P ; B 76 0 565 739 ; C 81 ; WX 871 ; N Q ; B 44 -13 839 752 ; C 82 ; WX 607 ; N R ; B 76 0 577 739 ; C 83 ; WX 498 ; N S ; B 22 -13 476 752 ; C 84 ; WX 426 ; N T ; B 7 0 419 739 ; C 85 ; WX 655 ; N U ; B 76 -13 579 739 ; C 86 ; WX 702 ; N V ; B 10 0 692 739 ; C 87 ; WX 960 ; N W ; B 10 0 950 739 ; C 88 ; WX 609 ; N X ; B 8 0 601 739 ; C 89 ; WX 592 ; N Y ; B 2 0 590 739 ; C 90 ; WX 480 ; N Z ; B 17 0 474 739 ; C 91 ; WX 351 ; N bracketleft ; B 112 -125 313 739 ; C 92 ; WX 605 ; N backslash ; B 84 0 464 739 ; C 93 ; WX 351 ; N bracketright ; B 38 -125 239 739 ; C 94 ; WX 606 ; N asciicircum ; B 18 180 588 739 ; C 95 ; WX 500 ; N underscore ; B 0 -125 500 -67 ; C 96 ; WX 351 ; N quoteleft ; B 98 547 260 739 ; C 97 ; WX 683 ; N a ; B 42 -13 620 560 ; C 98 ; WX 682 ; N b ; B 63 -13 640 739 ; C 99 ; WX 647 ; N c ; B 42 -13 608 560 ; C 100 ; WX 685 ; N d ; B 42 -13 622 739 ; C 101 ; WX 650 ; N e ; B 42 -13 611 560 ; C 102 ; WX 314 ; N f ; B 16 0 311 739 ; L l fl ; L i fi ; C 103 ; WX 673 ; N g ; B 42 -215 610 560 ; C 104 ; WX 610 ; N h ; B 63 0 547 739 ; C 105 ; WX 200 ; N i ; B 63 0 137 739 ; L j ij ; C 106 ; WX 203 ; N j ; B -45 -192 140 739 ; C 107 ; WX 502 ; N k ; B 63 0 497 739 ; C 108 ; WX 200 ; N l ; B 63 0 137 739 ; C 109 ; WX 938 ; N m ; B 63 0 875 561 ; C 110 ; WX 610 ; N n ; B 63 0 547 560 ; C 111 ; WX 655 ; N o ; B 42 -13 613 560 ; C 112 ; WX 682 ; N p ; B 63 -192 640 560 ; C 113 ; WX 682 ; N q ; B 42 -192 619 560 ; C 114 ; WX 301 ; N r ; B 63 0 292 560 ; C 115 ; WX 388 ; N s ; B 18 -13 361 560 ; C 116 ; WX 339 ; N t ; B 12 0 327 739 ; C 117 ; WX 608 ; N u ; B 63 -13 545 547 ; C 118 ; WX 554 ; N v ; B 8 0 546 547 ; C 119 ; WX 831 ; N w ; B 7 0 824 547 ; C 120 ; WX 480 ; N x ; B 8 0 472 547 ; C 121 ; WX 536 ; N y ; B 9 -192 527 547 ; C 122 ; WX 425 ; N z ; B 10 0 415 547 ; C 123 ; WX 351 ; N braceleft ; B 37 -126 271 739 ; C 124 ; WX 672 ; N bar ; B 304 0 368 739 ; C 125 ; WX 351 ; N braceright ; B 38 -126 269 739 ; C 126 ; WX 606 ; N asciitilde ; B 51 210 555 390 ; C 161 ; WX 295 ; N exclamdown ; B 111 -192 185 547 ; C 162 ; WX 554 ; N cent ; B 45 62 509 707 ; C 163 ; WX 554 ; N sterling ; B 18 0 592 752 ; C 164 ; WX 166 ; N fraction ; B -115 0 281 739 ; C 165 ; WX 554 ; N yen ; B 0 0 589 739 ; C 166 ; WX 554 ; N florin ; B 18 -153 546 818 ; C 167 ; WX 615 ; N section ; B 87 -141 529 752 ; C 168 ; WX 554 ; N currency ; B 26 50 528 553 ; C 169 ; WX 198 ; N quotesingle ; B 74 513 124 739 ; C 170 ; WX 502 ; N quotedblleft ; B 96 547 406 739 ; C 171 ; WX 425 ; N guillemotleft ; B 40 99 385 499 ; C 172 ; WX 251 ; N guilsinglleft ; B 40 99 211 499 ; C 173 ; WX 251 ; N guilsinglright ; B 40 99 211 499 ; C 174 ; WX 487 ; N fi ; B 13 0 424 739 ; C 175 ; WX 485 ; N fl ; B 13 0 422 739 ; C 177 ; WX 500 ; N endash ; B 0 233 500 296 ; C 178 ; WX 553 ; N dagger ; B 59 -133 494 739 ; C 179 ; WX 553 ; N daggerdbl ; B 59 -133 494 739 ; C 180 ; WX 277 ; N periodcentered ; B 101 229 175 355 ; C 182 ; WX 564 ; N paragraph ; B 37 -133 491 739 ; C 183 ; WX 606 ; N bullet ; B 118 118 488 488 ; C 184 ; WX 354 ; N quotesinglbase ; B 89 -67 251 126 ; C 185 ; WX 502 ; N quotedblbase ; B 89 -67 398 126 ; C 186 ; WX 484 ; N quotedblright ; B 87 547 393 739 ; C 187 ; WX 425 ; N guillemotright ; B 40 99 385 499 ; C 188 ; WX 1000 ; N ellipsis ; B 130 0 870 126 ; C 189 ; WX 1174 ; N perthousand ; B 13 -13 1151 749 ; C 191 ; WX 591 ; N questiondown ; B 66 -205 526 547 ; C 193 ; WX 378 ; N grave ; B 74 610 304 776 ; C 194 ; WX 375 ; N acute ; B 74 610 301 782 ; C 195 ; WX 502 ; N circumflex ; B 74 623 428 749 ; C 196 ; WX 439 ; N tilde ; B 75 633 365 728 ; C 197 ; WX 485 ; N macron ; B 74 643 411 707 ; C 198 ; WX 453 ; N breve ; B 74 623 378 743 ; C 199 ; WX 222 ; N dotaccent ; B 74 623 148 749 ; C 200 ; WX 369 ; N dieresis ; B 74 623 295 749 ; C 202 ; WX 332 ; N ring ; B 74 610 258 794 ; C 203 ; WX 324 ; N cedilla ; B 74 -213 250 0 ; C 205 ; WX 552 ; N hungarumlaut ; B 74 610 479 780 ; C 206 ; WX 302 ; N ogonek ; B 74 -191 228 0 ; C 207 ; WX 502 ; N caron ; B 74 623 428 749 ; C 208 ; WX 1000 ; N emdash ; B 0 233 1000 296 ; C 225 ; WX 992 ; N AE ; B 11 0 936 739 ; C 227 ; WX 369 ; N ordfeminine ; B 31 319 339 745 ; C 232 ; WX 517 ; N Lslash ; B 26 0 502 739 ; C 233 ; WX 868 ; N Oslash ; B 44 -83 824 821 ; C 234 ; WX 1194 ; N OE ; B 44 -13 1138 752 ; C 235 ; WX 369 ; N ordmasculine ; B 32 319 337 745 ; C 241 ; WX 1157 ; N ae ; B 42 -13 1118 560 ; C 245 ; WX 200 ; N dotlessi ; B 63 0 137 547 ; C 248 ; WX 300 ; N lslash ; B 36 0 270 739 ; C 249 ; WX 653 ; N oslash ; B 42 -39 611 594 ; C 250 ; WX 1137 ; N oe ; B 42 -13 1098 560 ; C 251 ; WX 554 ; N germandbls ; B 59 -13 524 752 ; C -1 ; WX 740 ; N Adieresis ; B 11 0 729 931 ; C -1 ; WX 740 ; N Aacute ; B 11 0 729 964 ; C -1 ; WX 740 ; N Agrave ; B 11 0 729 958 ; C -1 ; WX 740 ; N Acircumflex ; B 11 0 729 931 ; C -1 ; WX 740 ; N Abreve ; B 11 0 729 925 ; C -1 ; WX 740 ; N Atilde ; B 11 0 729 910 ; C -1 ; WX 740 ; N Aring ; B 11 0 729 976 ; C -1 ; WX 740 ; N Aogonek ; B 11 -191 760 739 ; C -1 ; WX 813 ; N Ccedilla ; B 44 -240 770 752 ; C -1 ; WX 813 ; N Cacute ; B 44 -13 770 964 ; C -1 ; WX 813 ; N Ccaron ; B 44 -13 770 931 ; C -1 ; WX 744 ; N Dcaron ; B 76 0 700 931 ; C -1 ; WX 536 ; N Edieresis ; B 76 0 480 931 ; C -1 ; WX 536 ; N Eacute ; B 76 0 480 964 ; C -1 ; WX 536 ; N Egrave ; B 76 0 480 958 ; C -1 ; WX 536 ; N Ecircumflex ; B 76 0 480 931 ; C -1 ; WX 536 ; N Ecaron ; B 76 0 480 931 ; C -1 ; WX 536 ; N Edotaccent ; B 76 0 480 931 ; C -1 ; WX 536 ; N Eogonek ; B 76 -191 511 739 ; C -1 ; WX 872 ; N Gbreve ; B 44 -13 831 925 ; C -1 ; WX 226 ; N Idieresis ; B 3 0 224 931 ; C -1 ; WX 226 ; N Iacute ; B -1 0 226 964 ; C -1 ; WX 226 ; N Igrave ; B -2 0 228 958 ; C -1 ; WX 226 ; N Icircumflex ; B -64 0 290 931 ; C -1 ; WX 226 ; N Idotaccent ; B 76 0 150 931 ; C -1 ; WX 462 ; N Lacute ; B 76 0 456 964 ; C -1 ; WX 462 ; N Lcaron ; B 76 0 456 739 ; C -1 ; WX 740 ; N Nacute ; B 76 0 664 964 ; C -1 ; WX 740 ; N Ncaron ; B 76 0 664 931 ; C -1 ; WX 740 ; N Ntilde ; B 76 0 664 910 ; C -1 ; WX 869 ; N Odieresis ; B 44 -13 825 931 ; C -1 ; WX 869 ; N Oacute ; B 44 -13 825 964 ; C -1 ; WX 869 ; N Ograve ; B 44 -13 825 958 ; C -1 ; WX 869 ; N Ocircumflex ; B 44 -13 825 931 ; C -1 ; WX 869 ; N Otilde ; B 44 -13 825 910 ; C -1 ; WX 869 ; N Ohungarumlaut ; B 44 -13 825 962 ; C -1 ; WX 607 ; N Racute ; B 76 0 577 964 ; C -1 ; WX 607 ; N Rcaron ; B 76 0 577 931 ; C -1 ; WX 498 ; N Sacute ; B 22 -13 476 964 ; C -1 ; WX 498 ; N Scaron ; B 22 -13 476 931 ; C -1 ; WX 498 ; N Scedilla ; B 22 -213 476 752 ; C -1 ; WX 426 ; N Tcaron ; B 7 0 419 931 ; C -1 ; WX 655 ; N Udieresis ; B 76 -13 579 931 ; C -1 ; WX 655 ; N Uacute ; B 76 -13 579 964 ; C -1 ; WX 655 ; N Ugrave ; B 76 -13 579 958 ; C -1 ; WX 655 ; N Ucircumflex ; B 76 -13 579 931 ; C -1 ; WX 655 ; N Uring ; B 76 -13 579 976 ; C -1 ; WX 655 ; N Uhungarumlaut ; B 76 -13 579 962 ; C -1 ; WX 592 ; N Yacute ; B 2 0 590 964 ; C -1 ; WX 480 ; N Zacute ; B 17 0 474 964 ; C -1 ; WX 480 ; N Zcaron ; B 17 0 474 931 ; C -1 ; WX 480 ; N Zdotaccent ; B 17 0 474 931 ; C -1 ; WX 740 ; N Amacron ; B 11 0 729 889 ; C -1 ; WX 426 ; N Tcommaaccent ; B 7 -225 419 739 ; C -1 ; WX 592 ; N Ydieresis ; B 2 0 590 931 ; C -1 ; WX 536 ; N Emacron ; B 76 0 480 889 ; C -1 ; WX 226 ; N Imacron ; B -56 0 281 889 ; C -1 ; WX 226 ; N Iogonek ; B 27 -191 181 739 ; C -1 ; WX 591 ; N Kcommaaccent ; B 76 -225 585 739 ; C -1 ; WX 462 ; N Lcommaaccent ; B 76 -225 456 739 ; C -1 ; WX 740 ; N Ncommaaccent ; B 76 -225 664 739 ; C -1 ; WX 869 ; N Omacron ; B 44 -13 825 889 ; C -1 ; WX 607 ; N Rcommaaccent ; B 76 -225 577 739 ; C -1 ; WX 872 ; N Gcommaaccent ; B 44 -225 831 752 ; C -1 ; WX 655 ; N Umacron ; B 76 -13 579 889 ; C -1 ; WX 655 ; N Uogonek ; B 76 -191 579 739 ; C -1 ; WX 683 ; N adieresis ; B 42 -13 620 749 ; C -1 ; WX 683 ; N aacute ; B 42 -13 620 782 ; C -1 ; WX 683 ; N agrave ; B 42 -13 620 776 ; C -1 ; WX 683 ; N acircumflex ; B 42 -13 620 749 ; C -1 ; WX 683 ; N abreve ; B 42 -13 620 743 ; C -1 ; WX 683 ; N atilde ; B 42 -13 620 728 ; C -1 ; WX 683 ; N aring ; B 42 -13 620 794 ; C -1 ; WX 683 ; N aogonek ; B 42 -191 651 560 ; C -1 ; WX 647 ; N cacute ; B 42 -13 608 782 ; C -1 ; WX 647 ; N ccaron ; B 42 -13 608 749 ; C -1 ; WX 647 ; N ccedilla ; B 42 -224 608 560 ; C -1 ; WX 725 ; N dcaron ; B 42 -13 765 739 ; C -1 ; WX 650 ; N edieresis ; B 42 -13 611 749 ; C -1 ; WX 650 ; N eacute ; B 42 -13 611 782 ; C -1 ; WX 650 ; N egrave ; B 42 -13 611 776 ; C -1 ; WX 650 ; N ecircumflex ; B 42 -13 611 749 ; C -1 ; WX 650 ; N ecaron ; B 42 -13 611 749 ; C -1 ; WX 650 ; N edotaccent ; B 42 -13 611 749 ; C -1 ; WX 650 ; N eogonek ; B 42 -191 611 560 ; C -1 ; WX 673 ; N gbreve ; B 42 -215 610 743 ; C -1 ; WX 200 ; N idieresis ; B -10 0 211 749 ; C -1 ; WX 200 ; N iacute ; B -13 0 214 782 ; C -1 ; WX 200 ; N igrave ; B -15 0 215 776 ; C -1 ; WX 200 ; N icircumflex ; B -77 0 277 749 ; C -1 ; WX 200 ; N lacute ; B -14 0 213 964 ; C -1 ; WX 245 ; N lcaron ; B 63 0 285 739 ; C -1 ; WX 610 ; N nacute ; B 63 0 547 782 ; C -1 ; WX 610 ; N ncaron ; B 63 0 547 749 ; C -1 ; WX 610 ; N ntilde ; B 63 0 547 728 ; C -1 ; WX 655 ; N odieresis ; B 42 -13 613 749 ; C -1 ; WX 655 ; N oacute ; B 42 -13 613 782 ; C -1 ; WX 655 ; N ograve ; B 42 -13 613 776 ; C -1 ; WX 655 ; N ocircumflex ; B 42 -13 613 749 ; C -1 ; WX 655 ; N otilde ; B 42 -13 613 728 ; C -1 ; WX 655 ; N ohungarumlaut ; B 42 -13 613 780 ; C -1 ; WX 301 ; N racute ; B 63 0 324 782 ; C -1 ; WX 388 ; N sacute ; B 18 -13 361 782 ; C -1 ; WX 388 ; N scaron ; B 17 -13 371 749 ; C -1 ; WX 388 ; N scommaaccent ; B 18 -225 361 560 ; C -1 ; WX 339 ; N tcaron ; B 12 0 379 739 ; C -1 ; WX 608 ; N udieresis ; B 63 -13 545 749 ; C -1 ; WX 608 ; N uacute ; B 63 -13 545 782 ; C -1 ; WX 608 ; N ugrave ; B 63 -13 545 776 ; C -1 ; WX 608 ; N ucircumflex ; B 63 -13 545 749 ; C -1 ; WX 608 ; N uring ; B 63 -13 545 794 ; C -1 ; WX 608 ; N uhungarumlaut ; B 63 -13 547 780 ; C -1 ; WX 536 ; N yacute ; B 9 -192 527 782 ; C -1 ; WX 425 ; N zacute ; B 10 0 415 782 ; C -1 ; WX 425 ; N zcaron ; B 10 0 415 749 ; C -1 ; WX 425 ; N zdotaccent ; B 10 0 415 749 ; C -1 ; WX 536 ; N ydieresis ; B 9 -192 527 749 ; C -1 ; WX 339 ; N tcommaaccent ; B 12 -225 327 739 ; C -1 ; WX 683 ; N amacron ; B 42 -13 620 707 ; C -1 ; WX 650 ; N emacron ; B 42 -13 611 707 ; C -1 ; WX 200 ; N imacron ; B -69 0 268 707 ; C -1 ; WX 502 ; N kcommaaccent ; B 63 -225 497 739 ; C -1 ; WX 200 ; N lcommaaccent ; B 26 -225 148 739 ; C -1 ; WX 610 ; N ncommaaccent ; B 63 -225 547 560 ; C -1 ; WX 655 ; N omacron ; B 42 -13 613 707 ; C -1 ; WX 301 ; N rcommaaccent ; B 63 -225 292 560 ; C -1 ; WX 608 ; N umacron ; B 63 -13 545 707 ; C -1 ; WX 608 ; N uogonek ; B 63 -191 576 547 ; C -1 ; WX 301 ; N rcaron ; B 10 0 364 749 ; C -1 ; WX 388 ; N scedilla ; B 17 -213 361 560 ; C -1 ; WX 673 ; N gcommaaccent ; B 42 -215 610 790 ; C -1 ; WX 200 ; N iogonek ; B 14 -191 168 739 ; C -1 ; WX 498 ; N Scommaaccent ; B 22 -225 476 752 ; C -1 ; WX 790 ; N Eth ; B 26 0 746 739 ; C -1 ; WX 790 ; N Dcroat ; B 26 0 746 739 ; C -1 ; WX 592 ; N Thorn ; B 76 0 565 739 ; C -1 ; WX 685 ; N dcroat ; B 42 -13 685 739 ; C -1 ; WX 655 ; N eth ; B 42 -13 613 756 ; C -1 ; WX 682 ; N thorn ; B 63 -192 640 739 ; C -1 ; WX 554 ; N Euro ; B -32 -13 573 752 ; C -1 ; WX 332 ; N onesuperior ; B 98 288 233 739 ; C -1 ; WX 332 ; N twosuperior ; B 12 288 320 747 ; C -1 ; WX 332 ; N threesuperior ; B 13 280 319 747 ; C -1 ; WX 400 ; N degree ; B 50 380 350 686 ; C -1 ; WX 606 ; N minus ; B 51 267 555 341 ; C -1 ; WX 606 ; N multiply ; B 51 48 555 551 ; C -1 ; WX 606 ; N divide ; B 51 112 555 496 ; C -1 ; WX 1000 ; N trademark ; B 63 340 938 739 ; C -1 ; WX 606 ; N plusminus ; B 51 0 555 585 ; C -1 ; WX 831 ; N onehalf ; B 96 0 814 739 ; C -1 ; WX 831 ; N onequarter ; B 101 0 803 739 ; C -1 ; WX 831 ; N threequarters ; B 13 0 803 747 ; C -1 ; WX 351 ; N commaaccent ; B 101 -225 223 -53 ; C -1 ; WX 747 ; N copyright ; B -9 -13 755 752 ; C -1 ; WX 747 ; N registered ; B -9 -13 755 752 ; C -1 ; WX 494 ; N lozenge ; B 18 0 466 740 ; C -1 ; WX 612 ; N uni2206 ; B 6 0 608 688 ; C -1 ; WX 549 ; N notequal ; B 25 42 529 558 ; C -1 ; WX 549 ; N radical ; B 10 -35 515 913 ; C -1 ; WX 549 ; N lessequal ; B 26 0 530 646 ; C -1 ; WX 549 ; N greaterequal ; B 26 0 530 646 ; C -1 ; WX 606 ; N logicalnot ; B 51 148 555 436 ; C -1 ; WX 713 ; N summation ; B 14 -123 695 752 ; C -1 ; WX 494 ; N partialdiff ; B 26 -10 462 753 ; C -1 ; WX 672 ; N brokenbar ; B 304 0 368 739 ; C -1 ; WX 608 ; N mu ; B 63 -192 545 547 ; C -1 ; WX 740 ; N afii10017 ; B 11 0 729 739 ; C -1 ; WX 571 ; N afii10018 ; B 76 0 544 739 ; C -1 ; WX 571 ; N afii10019 ; B 76 0 544 739 ; C -1 ; WX 487 ; N afii10020 ; B 76 0 480 739 ; C -1 ; WX 848 ; N afii10021 ; B 66 -126 781 739 ; C -1 ; WX 536 ; N afii10022 ; B 76 0 480 739 ; C -1 ; WX 536 ; N afii10023 ; B 76 0 480 924 ; C -1 ; WX 974 ; N afii10024 ; B 45 0 934 739 ; C -1 ; WX 665 ; N afii10025 ; B 58 -13 622 752 ; C -1 ; WX 663 ; N afii10026 ; B 76 0 587 739 ; C -1 ; WX 663 ; N afii10027 ; B 76 0 587 919 ; C -1 ; WX 591 ; N afii10028 ; B 76 0 570 739 ; C -1 ; WX 702 ; N afii10029 ; B 35 0 665 739 ; C -1 ; WX 919 ; N afii10030 ; B 106 0 813 739 ; C -1 ; WX 683 ; N afii10031 ; B 76 0 607 739 ; C -1 ; WX 869 ; N afii10032 ; B 44 -13 825 752 ; C -1 ; WX 683 ; N afii10033 ; B 76 0 607 739 ; C -1 ; WX 592 ; N afii10034 ; B 76 0 565 739 ; C -1 ; WX 813 ; N afii10035 ; B 44 -13 770 752 ; C -1 ; WX 466 ; N afii10036 ; B 7 0 459 739 ; C -1 ; WX 596 ; N afii10037 ; B 4 0 592 739 ; C -1 ; WX 825 ; N afii10038 ; B 47 -42 785 794 ; C -1 ; WX 609 ; N afii10039 ; B 8 0 601 739 ; C -1 ; WX 712 ; N afii10040 ; B 76 -140 668 739 ; C -1 ; WX 635 ; N afii10041 ; B 66 0 559 741 ; C -1 ; WX 964 ; N afii10042 ; B 76 0 888 739 ; C -1 ; WX 983 ; N afii10043 ; B 76 -140 949 739 ; C -1 ; WX 710 ; N afii10044 ; B 6 0 683 739 ; C -1 ; WX 742 ; N afii10045 ; B 76 0 666 739 ; C -1 ; WX 571 ; N afii10046 ; B 76 0 544 739 ; C -1 ; WX 814 ; N afii10047 ; B 44 -13 770 752 ; C -1 ; WX 1081 ; N afii10048 ; B 76 -13 1037 752 ; C -1 ; WX 604 ; N afii10049 ; B 27 0 528 739 ; C -1 ; WX 683 ; N afii10065 ; B 42 -13 620 560 ; C -1 ; WX 656 ; N afii10066 ; B 45 -13 618 784 ; C -1 ; WX 473 ; N afii10067 ; B 63 0 439 547 ; C -1 ; WX 369 ; N afii10068 ; B 63 0 364 547 ; C -1 ; WX 668 ; N afii10069 ; B 27 -120 641 547 ; C -1 ; WX 650 ; N afii10070 ; B 42 -13 611 560 ; C -1 ; WX 650 ; N afii10071 ; B 42 -13 611 749 ; C -1 ; WX 768 ; N afii10072 ; B 7 0 761 548 ; C -1 ; WX 458 ; N afii10073 ; B 27 -13 431 560 ; C -1 ; WX 608 ; N afii10074 ; B 63 0 545 548 ; C -1 ; WX 608 ; N afii10075 ; B 63 0 545 733 ; C -1 ; WX 477 ; N afii10076 ; B 63 0 467 549 ; C -1 ; WX 554 ; N afii10077 ; B 8 0 546 547 ; C -1 ; WX 690 ; N afii10078 ; B 63 0 627 549 ; C -1 ; WX 544 ; N afii10079 ; B 63 0 481 547 ; C -1 ; WX 655 ; N afii10080 ; B 42 -13 613 560 ; C -1 ; WX 549 ; N afii10081 ; B 63 0 486 547 ; C -1 ; WX 682 ; N afii10082 ; B 63 -192 640 560 ; C -1 ; WX 647 ; N afii10083 ; B 42 -13 608 560 ; C -1 ; WX 362 ; N afii10084 ; B 6 0 356 547 ; C -1 ; WX 536 ; N afii10085 ; B 9 -192 527 547 ; C -1 ; WX 728 ; N afii10086 ; B 42 -108 686 631 ; C -1 ; WX 480 ; N afii10087 ; B 8 0 472 547 ; C -1 ; WX 560 ; N afii10088 ; B 63 -129 533 547 ; C -1 ; WX 500 ; N afii10089 ; B 34 0 437 547 ; C -1 ; WX 822 ; N afii10090 ; B 63 0 759 548 ; C -1 ; WX 882 ; N afii10091 ; B 63 -129 825 547 ; C -1 ; WX 556 ; N afii10092 ; B 6 0 529 547 ; C -1 ; WX 620 ; N afii10093 ; B 63 0 567 547 ; C -1 ; WX 466 ; N afii10094 ; B 63 0 439 547 ; C -1 ; WX 650 ; N afii10095 ; B 42 -13 608 560 ; C -1 ; WX 853 ; N afii10096 ; B 63 -13 796 560 ; C -1 ; WX 502 ; N afii10097 ; B 35 0 439 547 ; C -1 ; WX 536 ; N uni0400 ; B 76 0 480 958 ; C -1 ; WX 426 ; N afii10051 ; B 7 0 419 739 ; C -1 ; WX 487 ; N afii10052 ; B 76 0 480 954 ; C -1 ; WX 814 ; N afii10053 ; B 44 -13 770 752 ; C -1 ; WX 498 ; N afii10054 ; B 22 -13 476 752 ; C -1 ; WX 226 ; N afii10055 ; B 76 0 150 739 ; C -1 ; WX 226 ; N afii10056 ; B 2 0 223 905 ; C -1 ; WX 482 ; N afii10057 ; B 11 -14 406 739 ; C -1 ; WX 906 ; N afii10058 ; B 11 -1 885 739 ; C -1 ; WX 957 ; N afii10059 ; B 76 0 910 739 ; C -1 ; WX 426 ; N afii10060 ; B 7 0 419 739 ; C -1 ; WX 591 ; N afii10061 ; B 76 0 585 954 ; C -1 ; WX 740 ; N uni040D ; B 76 0 664 1005 ; C -1 ; WX 596 ; N afii10062 ; B 4 0 592 906 ; C -1 ; WX 683 ; N afii10145 ; B 76 -140 607 739 ; C -1 ; WX 650 ; N uni0450 ; B 42 -13 611 813 ; C -1 ; WX 610 ; N afii10099 ; B 9 0 546 739 ; C -1 ; WX 369 ; N afii10100 ; B 63 0 364 788 ; C -1 ; WX 650 ; N afii10101 ; B 42 -13 608 560 ; C -1 ; WX 388 ; N afii10102 ; B 18 -13 361 560 ; C -1 ; WX 200 ; N afii10103 ; B 63 0 137 739 ; C -1 ; WX 200 ; N afii10104 ; B -10 0 211 749 ; C -1 ; WX 203 ; N afii10105 ; B -45 -192 140 739 ; C -1 ; WX 678 ; N afii10106 ; B 8 0 652 547 ; C -1 ; WX 694 ; N afii10107 ; B 63 0 664 547 ; C -1 ; WX 610 ; N afii10108 ; B 9 0 546 739 ; C -1 ; WX 502 ; N afii10109 ; B 63 0 497 788 ; C -1 ; WX 608 ; N uni045D ; B 63 0 545 760 ; C -1 ; WX 536 ; N afii10110 ; B 9 -192 527 733 ; C -1 ; WX 529 ; N afii10193 ; B 63 -140 466 547 ; C -1 ; WX 571 ; N uni048C ; B 4 0 544 739 ; C -1 ; WX 466 ; N uni048D ; B 3 0 439 547 ; C -1 ; WX 592 ; N uni048E ; B 76 0 565 739 ; C -1 ; WX 682 ; N uni048F ; B 63 -192 640 560 ; C -1 ; WX 487 ; N afii10050 ; B 76 0 480 805 ; C -1 ; WX 369 ; N afii10098 ; B 63 0 364 610 ; C -1 ; WX 487 ; N uni0492 ; B 10 0 480 739 ; C -1 ; WX 369 ; N uni0493 ; B 3 0 364 547 ; C -1 ; WX 487 ; N uni0494 ; B 76 0 480 739 ; C -1 ; WX 369 ; N uni0495 ; B 63 0 364 547 ; C -1 ; WX 974 ; N uni0496 ; B 10 0 964 739 ; C -1 ; WX 808 ; N uni0497 ; B 7 -129 807 549 ; C -1 ; WX 559 ; N uni0498 ; B 44 -8 515 752 ; C -1 ; WX 400 ; N uni0499 ; B 27 -13 373 560 ; C -1 ; WX 591 ; N uni049A ; B 76 0 585 739 ; C -1 ; WX 502 ; N uni049B ; B 63 0 497 549 ; C -1 ; WX 591 ; N uni049C ; B 76 0 585 739 ; C -1 ; WX 502 ; N uni049D ; B 63 0 497 549 ; C -1 ; WX 591 ; N uni049E ; B 6 0 585 739 ; C -1 ; WX 502 ; N uni049F ; B 4 0 497 549 ; C -1 ; WX 591 ; N uni04A0 ; B 76 0 585 739 ; C -1 ; WX 502 ; N uni04A1 ; B 63 0 497 549 ; C -1 ; WX 683 ; N uni04A2 ; B 76 0 607 739 ; C -1 ; WX 529 ; N uni04A3 ; B 63 0 466 547 ; C -1 ; WX 844 ; N uni04A4 ; B 76 0 819 739 ; C -1 ; WX 529 ; N uni04A5 ; B 63 0 466 547 ; C -1 ; WX 683 ; N uni04A6 ; B 76 0 607 739 ; C -1 ; WX 529 ; N uni04A7 ; B 63 0 466 547 ; C -1 ; WX 813 ; N uni04A8 ; B 44 -13 770 752 ; C -1 ; WX 647 ; N uni04A9 ; B 42 -13 608 560 ; C -1 ; WX 813 ; N uni04AA ; B 44 -13 770 752 ; C -1 ; WX 647 ; N uni04AB ; B 42 -13 608 560 ; C -1 ; WX 426 ; N uni04AC ; B 7 0 419 739 ; C -1 ; WX 362 ; N uni04AD ; B 6 0 356 547 ; C -1 ; WX 592 ; N uni04AE ; B 2 0 590 739 ; C -1 ; WX 554 ; N uni04AF ; B 8 -192 546 547 ; C -1 ; WX 592 ; N uni04B0 ; B 2 0 590 739 ; C -1 ; WX 554 ; N uni04B1 ; B 8 -192 546 547 ; C -1 ; WX 609 ; N uni04B2 ; B 8 0 601 739 ; C -1 ; WX 480 ; N uni04B3 ; B 8 0 472 547 ; C -1 ; WX 742 ; N uni04B4 ; B 76 -140 698 739 ; C -1 ; WX 590 ; N uni04B5 ; B 63 -129 563 547 ; C -1 ; WX 635 ; N uni04B6 ; B 66 0 559 741 ; C -1 ; WX 500 ; N uni04B7 ; B 34 0 437 547 ; C -1 ; WX 635 ; N uni04B8 ; B 66 1 559 742 ; C -1 ; WX 500 ; N uni04B9 ; B 34 1 437 548 ; C -1 ; WX 635 ; N uni04BA ; B 66 0 559 741 ; C -1 ; WX 500 ; N uni04BB ; B 34 0 437 547 ; C -1 ; WX 813 ; N uni04BC ; B 44 -13 770 752 ; C -1 ; WX 647 ; N uni04BD ; B 42 -13 608 560 ; C -1 ; WX 813 ; N uni04BE ; B 44 -13 770 752 ; C -1 ; WX 647 ; N uni04BF ; B 42 -13 608 560 ; C -1 ; WX 226 ; N uni04C0 ; B 76 0 150 739 ; C -1 ; WX 974 ; N uni04C1 ; B 10 0 964 959 ; C -1 ; WX 808 ; N uni04C2 ; B 7 0 801 767 ; C -1 ; WX 591 ; N uni04C3 ; B 76 0 585 739 ; C -1 ; WX 502 ; N uni04C4 ; B 63 0 497 549 ; C -1 ; WX 683 ; N uni04C7 ; B 76 0 607 739 ; C -1 ; WX 529 ; N uni04C8 ; B 63 0 466 547 ; C -1 ; WX 635 ; N uni04CB ; B 66 0 559 741 ; C -1 ; WX 500 ; N uni04CC ; B 34 0 437 547 ; C -1 ; WX 740 ; N uni04D0 ; B 11 0 729 912 ; C -1 ; WX 683 ; N uni04D1 ; B 42 -13 620 767 ; C -1 ; WX 740 ; N uni04D2 ; B 11 0 729 918 ; C -1 ; WX 683 ; N uni04D3 ; B 42 -13 620 773 ; C -1 ; WX 992 ; N uni04D4 ; B 11 0 936 739 ; C -1 ; WX 1157 ; N uni04D5 ; B 42 -13 1118 560 ; C -1 ; WX 536 ; N uni04D6 ; B 76 0 480 912 ; C -1 ; WX 650 ; N uni04D7 ; B 42 -13 611 767 ; C -1 ; WX 872 ; N uni04D8 ; B 44 -13 831 752 ; C -1 ; WX 647 ; N afii10846 ; B 27 -13 608 560 ; C -1 ; WX 872 ; N uni04DA ; B 44 -13 831 958 ; C -1 ; WX 647 ; N uni04DB ; B 27 -13 608 766 ; C -1 ; WX 974 ; N uni04DC ; B 10 0 964 965 ; C -1 ; WX 808 ; N uni04DD ; B 7 0 801 773 ; C -1 ; WX 559 ; N uni04DE ; B 44 -8 515 965 ; C -1 ; WX 400 ; N uni04DF ; B 27 -13 373 773 ; C -1 ; WX 559 ; N uni04E0 ; B 44 -8 515 752 ; C -1 ; WX 400 ; N uni04E1 ; B 27 -13 373 560 ; C -1 ; WX 740 ; N uni04E2 ; B 76 0 664 903 ; C -1 ; WX 608 ; N uni04E3 ; B 63 0 545 652 ; C -1 ; WX 740 ; N uni04E4 ; B 76 0 664 965 ; C -1 ; WX 608 ; N uni04E5 ; B 63 0 545 713 ; C -1 ; WX 869 ; N uni04E6 ; B 44 -13 825 965 ; C -1 ; WX 655 ; N uni04E7 ; B 42 -13 613 773 ; C -1 ; WX 869 ; N uni04E8 ; B 44 -13 825 752 ; C -1 ; WX 655 ; N uni04E9 ; B 42 -13 613 560 ; C -1 ; WX 869 ; N uni04EA ; B 44 -13 825 965 ; C -1 ; WX 655 ; N uni04EB ; B 42 -13 613 773 ; C -1 ; WX 814 ; N uni04EC ; B 44 -13 770 965 ; C -1 ; WX 650 ; N uni04ED ; B 42 -13 608 773 ; C -1 ; WX 596 ; N uni04EE ; B 4 0 592 903 ; C -1 ; WX 536 ; N uni04EF ; B 9 -192 527 711 ; C -1 ; WX 596 ; N uni04F0 ; B 4 0 592 965 ; C -1 ; WX 536 ; N uni04F1 ; B 9 -192 527 773 ; C -1 ; WX 596 ; N uni04F2 ; B 4 0 592 1009 ; C -1 ; WX 536 ; N uni04F3 ; B 9 -192 527 817 ; C -1 ; WX 635 ; N uni04F4 ; B 66 0 559 965 ; C -1 ; WX 500 ; N uni04F5 ; B 34 0 437 773 ; C -1 ; WX 778 ; N uni04F8 ; B 76 0 666 965 ; C -1 ; WX 635 ; N uni04F9 ; B 63 0 557 773 ; C -1 ; WX 813 ; N Ccircumflex ; B 44 -13 770 965 ; C -1 ; WX 647 ; N ccircumflex ; B 42 -13 608 773 ; C -1 ; WX 813 ; N Cdotaccent ; B 44 -13 770 965 ; C -1 ; WX 647 ; N cdotaccent ; B 42 -13 608 773 ; C -1 ; WX 536 ; N Ebreve ; B 76 0 480 959 ; C -1 ; WX 650 ; N ebreve ; B 42 -13 611 767 ; C -1 ; WX 872 ; N Gcircumflex ; B 44 -13 831 965 ; C -1 ; WX 673 ; N gcircumflex ; B 42 -215 610 773 ; C -1 ; WX 872 ; N Gdotaccent ; B 44 -13 831 965 ; C -1 ; WX 673 ; N gdotaccent ; B 42 -215 610 773 ; C -1 ; WX 683 ; N Hcircumflex ; B 76 0 607 965 ; C -1 ; WX 610 ; N hcircumflex ; B 63 0 547 965 ; C -1 ; WX 683 ; N Hbar ; B 12 0 674 739 ; C -1 ; WX 610 ; N hbar ; B 9 0 546 739 ; C -1 ; WX 226 ; N Itilde ; B -32 0 258 887 ; C -1 ; WX 200 ; N itilde ; B -45 0 245 742 ; C -1 ; WX 226 ; N Ibreve ; B -39 0 265 912 ; C -1 ; WX 200 ; N ibreve ; B -52 0 252 767 ; C -1 ; WX 686 ; N IJ ; B 76 -14 606 739 ; C -1 ; WX 330 ; N ij ; B 63 -192 266 739 ; C -1 ; WX 482 ; N Jcircumflex ; B 11 -14 546 965 ; C -1 ; WX 203 ; N jcircumflex ; B -74 -192 280 777 ; C -1 ; WX 502 ; N kgreenlandic ; B 63 0 497 549 ; C -1 ; WX 462 ; N Ldot ; B 76 0 456 739 ; C -1 ; WX 320 ; N ldot ; B 63 0 285 739 ; C -1 ; WX 610 ; N napostrophe ; B 63 0 547 560 ; C -1 ; WX 740 ; N Eng ; B 76 0 664 739 ; C -1 ; WX 610 ; N eng ; B 63 0 547 560 ; C -1 ; WX 869 ; N Obreve ; B 44 -13 825 959 ; C -1 ; WX 655 ; N obreve ; B 42 -13 613 767 ; C -1 ; WX 498 ; N Scircumflex ; B 22 -13 476 965 ; C -1 ; WX 388 ; N scircumflex ; B 18 -13 407 773 ; C -1 ; WX 426 ; N uni0162 ; B 7 -213 419 739 ; C -1 ; WX 339 ; N uni0163 ; B 12 -213 327 739 ; C -1 ; WX 426 ; N Tbar ; B 7 0 419 739 ; C -1 ; WX 339 ; N tbar ; B 12 0 327 739 ; C -1 ; WX 655 ; N Utilde ; B 76 -13 579 934 ; C -1 ; WX 608 ; N utilde ; B 63 -13 545 742 ; C -1 ; WX 655 ; N Ubreve ; B 76 -13 579 959 ; C -1 ; WX 608 ; N ubreve ; B 63 -13 545 767 ; C -1 ; WX 960 ; N Wcircumflex ; B 10 0 950 965 ; C -1 ; WX 831 ; N wcircumflex ; B 7 0 824 773 ; C -1 ; WX 592 ; N Ycircumflex ; B 2 0 590 965 ; C -1 ; WX 536 ; N ycircumflex ; B 9 -192 527 773 ; C -1 ; WX 314 ; N longs ; B 16 0 311 739 ; C -1 ; WX 1017 ; N afii61352 ; B 35 0 951 739 ; C -1 ; WX 768 ; N infinity ; B 42 130 720 520 ; EndCharMetrics StartKernData StartKernPairs 983 KPX quoteright y -21 KPX quoteright w -19 KPX quoteright v -21 KPX quoteright t -32 KPX quoteright s -27 KPX quoteright r -30 KPX quoteright period -80 KPX quoteright o -55 KPX quoteright d -56 KPX quoteright comma -100 KPX quoteright Aring -105 KPX quoteright Adieresis -105 KPX quoteright Aacute -105 KPX quoteright AE -183 KPX quoteright A -105 KPX comma quoteright -19 KPX comma quotedblright -17 KPX comma one -64 KPX hyphen Y -43 KPX hyphen W 4 KPX hyphen V -24 KPX hyphen T -11 KPX hyphen Aring 13 KPX hyphen Adieresis 13 KPX hyphen Aacute 13 KPX hyphen AE -21 KPX hyphen A 13 KPX period quoteright -58 KPX period quotedblright -56 KPX period one -100 KPX zero seven -30 KPX zero one -66 KPX zero four 22 KPX one zero -27 KPX one two -51 KPX one three -55 KPX one six -49 KPX one seven -88 KPX one period -58 KPX one one -133 KPX one nine -52 KPX one four -62 KPX one five -46 KPX one eight -46 KPX one comma -77 KPX two seven -35 KPX two one -81 KPX two four -19 KPX three seven -51 KPX three one -91 KPX three four 12 KPX four seven -35 KPX four one -81 KPX four four 24 KPX five seven -32 KPX five one -77 KPX five four 24 KPX six seven -64 KPX six one -94 KPX six four 24 KPX seven two -27 KPX seven three -42 KPX seven six -60 KPX seven seven -20 KPX seven period -115 KPX seven one -65 KPX seven four -81 KPX seven five -47 KPX seven eight -41 KPX seven comma -133 KPX seven colon -75 KPX eight seven -43 KPX eight one -84 KPX eight four 17 KPX nine seven -31 KPX nine one -77 KPX nine four -33 KPX A y -37 KPX A w -33 KPX A v -39 KPX A u -15 KPX A t -28 KPX A quoteright -92 KPX A quotedblright -90 KPX A q -21 KPX A period 13 KPX A o -20 KPX A hyphen 13 KPX A guilsinglleft -22 KPX A guillemotleft -22 KPX A g -23 KPX A e -21 KPX A d -21 KPX A comma -5 KPX A ccedilla -22 KPX A c -22 KPX A b 5 KPX A a -20 KPX A Y -77 KPX A W -47 KPX A V -72 KPX A Ugrave -29 KPX A Udieresis -29 KPX A Ucircumflex -29 KPX A Uacute -29 KPX A U -29 KPX A T -50 KPX A Q -38 KPX A Odieresis -38 KPX A O -38 KPX A G -38 KPX A Ccedilla -37 KPX A C -36 KPX B Y -26 KPX B W -2 KPX B V -24 KPX B Oslash 9 KPX B Ograve 6 KPX B Odieresis 6 KPX B Ocircumflex 6 KPX B Oacute 6 KPX B OE 7 KPX B O 6 KPX B Atilde -8 KPX B Aring -8 KPX B Adieresis -8 KPX B Acircumflex -8 KPX B Aacute -8 KPX B AE -21 KPX B A -8 KPX C Odieresis -18 KPX C Oacute -18 KPX C O -18 KPX C Aring -22 KPX C Adieresis -22 KPX C Aacute -22 KPX C AE -45 KPX C A -22 KPX D Y -44 KPX D X -29 KPX D W -14 KPX D V -36 KPX D T -40 KPX D J -5 KPX D Atilde -37 KPX D Aring -37 KPX D Agrave -37 KPX D Adieresis -37 KPX D Acircumflex -37 KPX D Aacute -37 KPX D A -37 KPX F u -11 KPX F r -11 KPX F period -80 KPX F oslash -5 KPX F oe -3 KPX F odieresis -3 KPX F oacute -3 KPX F o -3 KPX F j -7 KPX F i -5 KPX F hyphen 23 KPX F eacute -3 KPX F e -3 KPX F comma -98 KPX F aring -3 KPX F ae -3 KPX F adieresis -3 KPX F aacute -3 KPX F a -3 KPX F J -55 KPX F Atilde -56 KPX F Aring -56 KPX F Agrave -56 KPX F Adieresis -56 KPX F Acircumflex -56 KPX F Aacute -56 KPX F A -56 KPX G Y -48 KPX G W -22 KPX G V -43 KPX G T -45 KPX G Atilde -40 KPX G Aring -40 KPX G Agrave -40 KPX G Adieresis -40 KPX G Acircumflex -40 KPX G Aacute -40 KPX G AE -64 KPX G A -40 KPX J Aring -27 KPX J Adieresis -27 KPX J AE -39 KPX J A -27 KPX K y -44 KPX K udieresis -38 KPX K u -38 KPX K odieresis -44 KPX K oacute -44 KPX K o -44 KPX K hyphen -29 KPX K e -45 KPX K aring -44 KPX K ae -44 KPX K adieresis -44 KPX K a -44 KPX K T 37 KPX K S -16 KPX K Odieresis -38 KPX K Oacute -38 KPX K OE -37 KPX K O -38 KPX K G -38 KPX K C -36 KPX L y -59 KPX L udieresis -14 KPX L u -14 KPX L quoteright -116 KPX L quotedblright -114 KPX L hyphen -79 KPX L Y -88 KPX L W -59 KPX L V -100 KPX L Udieresis -25 KPX L U -25 KPX L T -47 KPX L S 7 KPX L Otilde -49 KPX L Ograve -49 KPX L Odieresis -49 KPX L Ocircumflex -49 KPX L Oacute -49 KPX L O -49 KPX L G -48 KPX L Ccedilla -49 KPX L C -46 KPX L Aring 34 KPX L Adieresis 34 KPX L Aacute 34 KPX L AE 34 KPX L A 34 KPX N udieresis 13 KPX N u 13 KPX N period 3 KPX N oslash 9 KPX N odieresis 8 KPX N oacute 8 KPX N o 8 KPX N eacute 7 KPX N e 7 KPX N comma -15 KPX N aring 8 KPX N ae 7 KPX N adieresis 8 KPX N aacute 8 KPX N a 8 KPX N Odieresis 9 KPX N Oacute 9 KPX N O 9 KPX N G 9 KPX N Ccedilla 11 KPX N C 10 KPX N AE -1 KPX O Y -46 KPX O X -31 KPX O W -16 KPX O V -38 KPX O T -42 KPX O Aring -39 KPX O Adieresis -39 KPX O Aacute -39 KPX O AE -68 KPX O A -39 KPX P period -106 KPX P oslash -19 KPX P oe -15 KPX P odieresis -15 KPX P oacute -15 KPX P o -15 KPX P hyphen -4 KPX P eacute -15 KPX P e -15 KPX P comma -125 KPX P aring -16 KPX P ae -16 KPX P adieresis -16 KPX P aacute -16 KPX P a -16 KPX P J -68 KPX P Aring -61 KPX P Adieresis -61 KPX P Aacute -61 KPX P AE -108 KPX P A -61 KPX R y 20 KPX R udieresis 7 KPX R uacute 7 KPX R u 7 KPX R oe -7 KPX R odieresis -7 KPX R oacute -7 KPX R o -7 KPX R hyphen 3 KPX R eacute -7 KPX R e -7 KPX R aring -7 KPX R ae -7 KPX R adieresis -7 KPX R aacute -7 KPX R a -7 KPX R Y -14 KPX R W -3 KPX R V -11 KPX R Udieresis 11 KPX R U 11 KPX R T -4 KPX R Odieresis 2 KPX R Oacute 2 KPX R OE 2 KPX R O 2 KPX R G 1 KPX R Ccedilla 3 KPX R C 2 KPX S t 17 KPX S Y -10 KPX S W 3 KPX S V -9 KPX S T 3 KPX S Aring -5 KPX S Adieresis -5 KPX S Aacute -5 KPX S AE -16 KPX S A -5 KPX T y -50 KPX T w -49 KPX T v -50 KPX T u -40 KPX T semicolon -62 KPX T s -32 KPX T r -33 KPX T period -47 KPX T oslash -45 KPX T o -48 KPX T j 6 KPX T i 8 KPX T hyphen -11 KPX T guilsinglleft -42 KPX T guillemotleft -42 KPX T g -48 KPX T e -48 KPX T comma -65 KPX T colon -53 KPX T c -49 KPX T ae -48 KPX T a -48 KPX T Y 38 KPX T W 35 KPX T V 34 KPX T Otilde -42 KPX T Oslash -38 KPX T Ograve -42 KPX T Odieresis -42 KPX T Ocircumflex -42 KPX T Oacute -42 KPX T OE -40 KPX T O -42 KPX T J -49 KPX T G -41 KPX T C -40 KPX T Atilde -50 KPX T Aring -50 KPX T Agrave -50 KPX T Adieresis -50 KPX T Acircumflex -50 KPX T Aacute -50 KPX T AE -51 KPX T A -50 KPX U r 13 KPX U period -15 KPX U p 13 KPX U n 13 KPX U m 13 KPX U comma -36 KPX U Atilde -30 KPX U Aring -30 KPX U Adieresis -30 KPX U Acircumflex -30 KPX U Aacute -30 KPX U AE -50 KPX U A -30 KPX V y -8 KPX V u -34 KPX V semicolon -70 KPX V r -32 KPX V period -92 KPX V oslash -58 KPX V o -59 KPX V i 6 KPX V hyphen -25 KPX V guilsinglleft -49 KPX V guillemotleft -49 KPX V g -59 KPX V e -59 KPX V comma -110 KPX V colon -60 KPX V ae -59 KPX V a -59 KPX V T 35 KPX V S -12 KPX V Otilde -39 KPX V Oslash -35 KPX V Ograve -39 KPX V Odieresis -39 KPX V Ocircumflex -39 KPX V Oacute -39 KPX V O -39 KPX V G -38 KPX V C -37 KPX V Atilde -72 KPX V Aring -72 KPX V Agrave -72 KPX V Adieresis -72 KPX V Acircumflex -72 KPX V Aacute -72 KPX V AE -118 KPX V A -72 KPX W y 4 KPX W u -21 KPX W semicolon -47 KPX W r -15 KPX W period -53 KPX W oslash -28 KPX W o -30 KPX W i 6 KPX W hyphen 3 KPX W guilsinglleft -21 KPX W guillemotleft -21 KPX W g -30 KPX W e -30 KPX W comma -71 KPX W colon -38 KPX W ae -30 KPX W a -30 KPX W T 35 KPX W Otilde -16 KPX W Oslash -12 KPX W Ograve -16 KPX W Odieresis -16 KPX W Ocircumflex -16 KPX W Oacute -16 KPX W O -16 KPX W G -15 KPX W C -14 KPX W Atilde -48 KPX W Aring -48 KPX W Agrave -48 KPX W Adieresis -48 KPX W Acircumflex -48 KPX W Aacute -48 KPX W AE -73 KPX W A -48 KPX X y -37 KPX X u -29 KPX X o -36 KPX X hyphen -17 KPX X e -37 KPX X a -36 KPX X Q -30 KPX X Odieresis -31 KPX X O -31 KPX X C -29 KPX Y v -14 KPX Y u -40 KPX Y semicolon -78 KPX Y period -89 KPX Y p -38 KPX Y oslash -66 KPX Y o -68 KPX Y i 10 KPX Y hyphen -45 KPX Y guilsinglleft -62 KPX Y guillemotleft -63 KPX Y g -68 KPX Y e -68 KPX Y comma -107 KPX Y colon -69 KPX Y ae -68 KPX Y a -68 KPX Y T 39 KPX Y S -13 KPX Y Otilde -46 KPX Y Oslash -42 KPX Y Ograve -46 KPX Y Odieresis -46 KPX Y Ocircumflex -46 KPX Y Oacute -46 KPX Y O -46 KPX Y G -46 KPX Y C -44 KPX Y Atilde -78 KPX Y Aring -78 KPX Y Agrave -78 KPX Y Adieresis -78 KPX Y Acircumflex -78 KPX Y Aacute -78 KPX Y AE -93 KPX Y A -78 KPX Z y -8 KPX Z v -8 KPX quoteleft Y 2 KPX quoteleft W 14 KPX quoteleft V 6 KPX quoteleft T -17 KPX quoteleft Aring -92 KPX quoteleft Adieresis -92 KPX quoteleft Aacute -92 KPX quoteleft AE -170 KPX quoteleft A -92 KPX a y 5 KPX a w 6 KPX a v 5 KPX a quoteright -7 KPX a j 14 KPX b y -23 KPX b w -19 KPX b v -25 KPX c k -4 KPX c h -4 KPX e y -23 KPX e x -20 KPX e w -20 KPX e v -24 KPX e t -23 KPX e quoteright -31 KPX f t 34 KPX f s 13 KPX f quoteright -5 KPX f oslash -24 KPX f oe -19 KPX f odieresis -19 KPX f oacute -19 KPX f o -19 KPX f l 9 KPX f j 7 KPX f i 9 KPX f f 32 KPX f eacute -19 KPX f e -19 KPX f aring -20 KPX f ae -21 KPX f adieresis -20 KPX f aacute -20 KPX f a -20 KPX g r 16 KPX g odieresis 7 KPX g oacute 7 KPX g l 16 KPX g eacute 7 KPX g e 7 KPX g aring 7 KPX g ae 7 KPX g adieresis 7 KPX g a 7 KPX h y -17 KPX h quoteright -30 KPX i j 13 KPX i T 8 KPX k udieresis 1 KPX k u 1 KPX k s -5 KPX k period -7 KPX k odieresis -28 KPX k oacute -28 KPX k o -28 KPX k hyphen -36 KPX k g -29 KPX k eacute -28 KPX k e -28 KPX k comma -21 KPX k aring -28 KPX k ae -28 KPX k adieresis -28 KPX k aacute -28 KPX k a -28 KPX l y 5 KPX l v 5 KPX m y -15 KPX m w -12 KPX m v -17 KPX m p 14 KPX n y -17 KPX n w -14 KPX n v -19 KPX n quoteright -30 KPX n p 13 KPX n T -41 KPX o y -21 KPX o x -18 KPX o w -18 KPX o v -24 KPX o t -22 KPX o quoteright -31 KPX o T -47 KPX p y -23 KPX p t -23 KPX q u 13 KPX q c 7 KPX r z 27 KPX r y 32 KPX r x 31 KPX r w 33 KPX r v 32 KPX r u 6 KPX r t 31 KPX r semicolon -24 KPX r s 8 KPX r r 6 KPX r quoteright -8 KPX r q -29 KPX r period -53 KPX r p 6 KPX r oslash -33 KPX r ograve -28 KPX r oe -27 KPX r odieresis -28 KPX r ocircumflex -28 KPX r oacute -28 KPX r o -28 KPX r n 6 KPX r m 6 KPX r l 6 KPX r k 6 KPX r j 4 KPX r i 6 KPX r hyphen -19 KPX r h 6 KPX r g -18 KPX r f 29 KPX r egrave -28 KPX r ecircumflex -28 KPX r eacute -28 KPX r e -28 KPX r d -29 KPX r comma -70 KPX r colon -12 KPX r ccedilla -25 KPX r c -30 KPX r aring -28 KPX r agrave -28 KPX r ae -28 KPX r adieresis -28 KPX r acircumflex -28 KPX r aacute -28 KPX r a -28 KPX s t 8 KPX s quoteright -16 KPX t semicolon -25 KPX t quoteright -9 KPX t odieresis -24 KPX t oacute -24 KPX t o -24 KPX t h 4 KPX t eacute -24 KPX t e -24 KPX t colon -13 KPX t aring -25 KPX t ae -25 KPX t adieresis -25 KPX t aacute -25 KPX t a -25 KPX t S 14 KPX u quoteright -7 KPX v semicolon -31 KPX v s -2 KPX v period -70 KPX v oslash -24 KPX v ograve -25 KPX v odieresis -25 KPX v oacute -25 KPX v o -25 KPX v l 6 KPX v g -21 KPX v egrave -25 KPX v ecircumflex -25 KPX v eacute -25 KPX v e -25 KPX v comma -87 KPX v colon -21 KPX v c -26 KPX v atilde -25 KPX v aring -25 KPX v agrave -25 KPX v ae -25 KPX v adieresis -25 KPX v acircumflex -25 KPX v aacute -25 KPX v a -25 KPX w semicolon -29 KPX w period -62 KPX w oslash -19 KPX w ograve -20 KPX w odieresis -20 KPX w oacute -20 KPX w o -20 KPX w l 7 KPX w hyphen 6 KPX w g -18 KPX w egrave -20 KPX w ecircumflex -20 KPX w eacute -20 KPX w e -20 KPX w comma -79 KPX w colon -19 KPX w c -21 KPX w atilde -20 KPX w aring -20 KPX w agrave -20 KPX w ae -20 KPX w adieresis -20 KPX w acircumflex -20 KPX w aacute -20 KPX w a -20 KPX x q -19 KPX x o -19 KPX x eacute -19 KPX x e -19 KPX x c -20 KPX x a -19 KPX y semicolon -31 KPX y s -3 KPX y period -72 KPX y oslash -25 KPX y ograve -26 KPX y odieresis -26 KPX y oacute -26 KPX y o -26 KPX y l 6 KPX y g -22 KPX y egrave -26 KPX y ecircumflex -26 KPX y eacute -26 KPX y e -26 KPX y comma -89 KPX y colon -21 KPX y c -27 KPX y atilde -26 KPX y aring -26 KPX y agrave -26 KPX y ae -26 KPX y adieresis -26 KPX y acircumflex -26 KPX y aacute -26 KPX y a -26 KPX quotedblleft W 12 KPX quotedblleft V 3 KPX quotedblleft T -19 KPX quotedblleft Aring -95 KPX quotedblleft Adieresis -95 KPX quotedblleft Aacute -95 KPX quotedblleft AE -172 KPX quotedblleft A -95 KPX guilsinglright Y -61 KPX guilsinglright W -21 KPX guilsinglright V -48 KPX guilsinglright T -41 KPX guilsinglright Aring -22 KPX guilsinglright Adieresis -22 KPX guilsinglright Aacute -22 KPX guilsinglright AE -60 KPX guilsinglright A -22 KPX quotedblbase Y -93 KPX quotedblbase W -57 KPX quotedblbase V -96 KPX quotedblbase T -52 KPX quotedblbase AE -8 KPX quotedblbase A 6 KPX quotedblright Y -4 KPX quotedblright W 7 KPX quotedblright T -5 KPX quotedblright Aring -101 KPX quotedblright Adieresis -101 KPX quotedblright Aacute -101 KPX quotedblright AE -179 KPX quotedblright A -101 KPX guillemotright Y -62 KPX guillemotright W -21 KPX guillemotright V -48 KPX guillemotright T -42 KPX guillemotright Aring -22 KPX guillemotright Adieresis -22 KPX guillemotright Aacute -22 KPX guillemotright AE -60 KPX guillemotright A -22 KPX Oslash A -36 KPX ae y -22 KPX ae w -20 KPX ae v -23 KPX Adieresis y -37 KPX Adieresis w -33 KPX Adieresis v -39 KPX Adieresis u -15 KPX Adieresis t -28 KPX Adieresis quoteright -92 KPX Adieresis quotedblright -90 KPX Adieresis q -21 KPX Adieresis period 13 KPX Adieresis o -20 KPX Adieresis hyphen 13 KPX Adieresis guilsinglleft -22 KPX Adieresis guillemotleft -22 KPX Adieresis g -23 KPX Adieresis d -21 KPX Adieresis comma -5 KPX Adieresis c -22 KPX Adieresis b 5 KPX Adieresis a -20 KPX Adieresis Y -77 KPX Adieresis W -47 KPX Adieresis V -72 KPX Adieresis U -29 KPX Adieresis T -50 KPX Adieresis Q -38 KPX Adieresis O -38 KPX Adieresis G -38 KPX Adieresis C -36 KPX Aacute y -37 KPX Aacute w -33 KPX Aacute v -39 KPX Aacute u -15 KPX Aacute t -28 KPX Aacute quoteright -92 KPX Aacute q -21 KPX Aacute period 13 KPX Aacute o -20 KPX Aacute hyphen 13 KPX Aacute guilsinglleft -22 KPX Aacute guillemotleft -22 KPX Aacute g -23 KPX Aacute e -21 KPX Aacute d -21 KPX Aacute comma -5 KPX Aacute c -22 KPX Aacute b 5 KPX Aacute a -20 KPX Aacute Y -77 KPX Aacute W -47 KPX Aacute V -72 KPX Aacute U -29 KPX Aacute T -50 KPX Aacute Q -38 KPX Aacute O -38 KPX Aacute G -38 KPX Aacute C -36 KPX Agrave period 13 KPX Agrave comma -5 KPX Agrave Y -77 KPX Agrave W -47 KPX Agrave V -72 KPX Agrave U -29 KPX Agrave T -50 KPX Agrave Q -38 KPX Agrave O -38 KPX Agrave G -38 KPX Agrave C -36 KPX Acircumflex period 13 KPX Acircumflex comma -5 KPX Acircumflex Y -77 KPX Acircumflex W -47 KPX Acircumflex V -72 KPX Acircumflex U -29 KPX Acircumflex T -50 KPX Acircumflex Q -38 KPX Acircumflex O -38 KPX Acircumflex G -38 KPX Acircumflex C -36 KPX Atilde period 13 KPX Atilde comma -5 KPX Atilde Y -77 KPX Atilde W -47 KPX Atilde V -72 KPX Atilde U -29 KPX Atilde T -50 KPX Atilde Q -38 KPX Atilde O -38 KPX Atilde G -38 KPX Atilde C -36 KPX Aring y -37 KPX Aring w -33 KPX Aring v -39 KPX Aring u -15 KPX Aring t -28 KPX Aring quoteright -92 KPX Aring quotedblright -90 KPX Aring q -21 KPX Aring period 13 KPX Aring o -20 KPX Aring hyphen 13 KPX Aring guilsinglleft -22 KPX Aring guillemotleft -22 KPX Aring g -23 KPX Aring e -21 KPX Aring d -21 KPX Aring comma -5 KPX Aring c -22 KPX Aring b 5 KPX Aring a -20 KPX Aring Y -77 KPX Aring W -47 KPX Aring V -72 KPX Aring U -29 KPX Aring T -50 KPX Aring Q -38 KPX Aring O -38 KPX Aring G -38 KPX Aring C -36 KPX Ccedilla A -22 KPX Odieresis Y -46 KPX Odieresis X -31 KPX Odieresis W -16 KPX Odieresis V -38 KPX Odieresis T -42 KPX Odieresis A -39 KPX Oacute Y -46 KPX Oacute W -16 KPX Oacute V -38 KPX Oacute T -42 KPX Oacute A -39 KPX Ograve Y -46 KPX Ograve V -38 KPX Ograve T -42 KPX Ocircumflex Y -46 KPX Ocircumflex V -38 KPX Ocircumflex T -42 KPX Otilde Y -46 KPX Otilde V -38 KPX Otilde T -42 KPX Udieresis r 13 KPX Udieresis period -15 KPX Udieresis p 13 KPX Udieresis n 13 KPX Udieresis m 13 KPX Udieresis comma -36 KPX Udieresis b 13 KPX Udieresis A -30 KPX Uacute r 13 KPX Uacute period -15 KPX Uacute p 13 KPX Uacute n 13 KPX Uacute m 13 KPX Uacute comma -36 KPX Uacute A -30 KPX Ugrave A -30 KPX Ucircumflex A -30 KPX adieresis y 5 KPX adieresis w 6 KPX adieresis v 5 KPX aacute y 5 KPX aacute w 6 KPX aacute v 5 KPX agrave y 5 KPX agrave w 6 KPX agrave v 5 KPX aring y 5 KPX aring w 6 KPX aring v 5 KPX eacute y -23 KPX eacute w -20 KPX eacute v -24 KPX ecircumflex y -23 KPX ecircumflex w -20 KPX ecircumflex v -24 KPX odieresis y -21 KPX odieresis x -18 KPX odieresis w -18 KPX odieresis v -24 KPX odieresis t -22 KPX oacute y -21 KPX oacute w -18 KPX oacute v -24 KPX ograve y -21 KPX ograve w -18 KPX ograve v -24 KPX ocircumflex t -22 EndKernPairs EndKernData EndFontMetrics tests/native/hpdf/samples/type1/a010013l.pfb000066400000000000000000002105061176363201700207010ustar00rootroot00000000000000%!PS-AdobeFont-1.0: URWGothicL-Book 1.06 %%Title: URWGothicL-Book %%CreationDate: Thu Dec 26 23:03:13 2002 %%Creator: frob %%DocumentSuppliedResources: font URWGothicL-Book % Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development; Cyri % Generated by PfaEdit 1.0 (http://pfaedit.sf.net/) %%EndComments FontDirectory/URWGothicL-Book known{/URWGothicL-Book findfont dup/UniqueID known{dup /UniqueID get 5019553 eq exch/FontType get 1 eq and}{pop false}ifelse {save true}{false}ifelse}{false}ifelse 11 dict begin /FontType 1 def /FontMatrix [0.001 0 0 0.001 0 0 ]readonly def /FontName /URWGothicL-Book def /FontBBox [-115 -240 1151 1009 ]readonly def /UniqueID 5019553 def /PaintType 0 def /FontInfo 10 dict dup begin /version (1.06) readonly def /Notice (Copyright \050URW\051++,Copyright 1999 by \050URW\051++ Design & Development; Cyrillic glyphs added by Valek Filippov \050C\051 2001-2002) readonly def /FullName (URW Gothic L Book) readonly def /FamilyName (URW Gothic L) readonly def /Weight (Book) readonly def /FSType 0 def /ItalicAngle 0 def /isFixedPitch false def /UnderlinePosition -96 def /UnderlineThickness 58 def end readonly def /Encoding StandardEncoding def currentdict end currentfile eexec D t?clZP'0*X \}<+}-˕~gj3u(zSw}ϰPQwa o&R_iKWH2()ȓͦ嶑;Ui b.:GxY%aw),;"w'pY$!$T L8~4x@7݈X(Wzyow|`ZH/mɤdtUjVM3j^ Yc4xmΕ͏;>[xt@<_ H+5DImK79Y(e_`16zolm1kcr ySg\PHȳZWǵvnY'u@%7oiT` zFܻJdR=CujL,L0'vd΃ ֞e 1;GӨ?P~Ka  Wud*"#r|rFm"8߬`.:7jrʗ@? Ʌa|`2Bm:h W!ˢ^CNT`2/?ڣ@jkGBxx0KB%G/UZj˙qtÃ}cmFQw%tW 0l$ڜBmF\ܦgX["093 컜PAAS9nP#?;3Wyg!/B =JQ(92? M~@ 73kw_1IģrI;CfL#1mB'~z[92PDC6?].Ĕ_F 5M*iAqdU0@Ճ#4eלY3V'-|2Dyv(>z@wտ•D+Yي6|7 {9nPܰ.#\;qMW1N$Jh^I InK;"Օ*GEPvNL&jd]R#,"(R͍v]>ZX*m0Ngt`^013lԟsĂ6^d`XbRuo:m0A]iS )x`6rd}\]Ţ pKS*qzv+`p*E yXL'U `X^y_|!uuPQG/9k.iS0򮐺b+V(8fvҗXFw&RہU3 v"nԜQV>ImˈÏڽVC JױY$!o'LlTO~ſN|r(Y- xOQ6DxcDm޵ë]ZʛHm k%FWAv ek0\CpՍ S ^W̆zGƷ:Nd+e%ff带qYð eC"> +ɗ;jk_P)V( ׺[R[VxM2GHw/~ekZݤ;m ::^ !'fv5von@ '_pg߷} jŜD~r]lm1Q{A>B!H/h>ϙ n&X7R&Ed, %R,8:?LuW^wP| RiciAdIM = Kev]'Ve6X~`{y]h ; s2 o{np,Bl ^̌oJhCC}HJLpB3^fnNVK\b.08gΝ#GLڔf+.^;vu# y h!J%8l|mSlhX-kWZPg#(*ϗX0lߚUȳkBb'_DE9prW4S޿|U*\}<:d^1KhĮYl ?4੉2׿!튵l3f@2K :!Z\-4f:BDѬq'Aow`t<5;]hgB)xnV$9ыop^ҬDʠqle mkj^GvF:%^Y x/`ϣCʪﱄ{F&Q7x;O>;YT`%̖ns5Ĥgqh5p 7ިQ*L#ZZxFe;&qR4c9oxO!&1yvL0%DFaE԰g}$_؟CKz 5]x."e:牗4L5j+vVGAkH_8ZUNQdy0R+DB6o5|k~e% wx@_:zNt2.:# X\웷5W!{>=s];wBC ytxz=,u6&b x:e+$VF 7:s.O(VJofyehOxz0usGJs5%y6vzGY 6y^czUq,R$ɲkD%z𛐃K;Є_!);s ϳ)X-au%0\c=@JwXx 4Fo;01 8k܉mxSk~Yݛ-)8)y,1c0 M!Z}r{yYf 2:kEH{(:+ڼ|8-AcV :aP@tjrԄm+a ^orhI@QVk/!^lٚ)53G7uz<H:m)8X<=qBQ6Vè rO2(YڽIw[dEm N(3h- z($ sw!el%$[܆|9 mNx–5(R%4@8栬ѯC9%m.NEaQ~9$:Fы򨲕>u5I] % !lT'f>-&3̉J3~& {gބSz9E|g3)-桚L, qNDJ4*lR.K[ߺAv&ݬFps[6aC t}=;xkQh-flVsȴsuv,X[3 5o_U#( s j`dI-;8aVC*sUއӺJ#RZPF45; H[T w9?)ER ~CzHp`e4QSWmD/טO=}dX\9v?>] !w'[֜7Hr nn>k$llڛ?pޱZ eaKLh]/ʉh$rќ٢xdT2p}&溄LkXzJ~6hĄAGE ~ \,Qrя[#<ȈҍIŴNdD\6,) uF~6n>⁘F#XA$fD?]Y6}۩iӗBYs;!ixcx8Rk_50sp8jbzn*ΣbN w|=sAeyRD77>;myOS*USg)E U"yGn6NHOlt8t[ e%دg,f-@D !&7jA:5?Ti.r'u-5=4oWæmתI{qAS slLƱxXg=GYa5XW-ټŮUsqw9ɓ&fQr,c+Qmh9Zps'Ekg^ym`̪sc2~I!Ő>&98\P-n7ݳI t)VgHl!E7 njQ)K)sz&Dox\0MaG"1D{AߋNiy.9Z&g\_-lZjUron*PHV̨+QئXv$K]jagSh:!.)20+ S\@wmB`I(Kϻj-opl$0aZF}j;w(OJrx038Hp^7ʇ97ZR4vJÁ+6'j\foJV0?fO:¥©xɨڢQ5fD4$ЏNܴ?ı锐hW3W +5졇#^*8.<пB !.K0Fȫo{< \z݊_゗"SrM.op@%Q䥥]/n`iScƒ;#r3ݎ 3'J|Sx3ɜ9oGپ"6)-uI8g*7g5Gi/.x3h(X* "y?ƐU2*jY{"b٢fK/ݾe(`9{>*&nx@j0bҍe~ܸg""};؃QgHR!65]<0ﷷh1a>z΅|Wv-. H^%Nn?CL>=wӿu: Z 񥋔s_;.,V:h X[wHjl\!|AB=S/ON_Ͱ Ӷ-zL AI&y.B{t+Ù`"JYS틑;Ofhj7FZ {qCӠbߏފ;G_4J# y٤t2b'[6g$Vvz ^d ^Gd g:6) !?sM$n,JOAZl9:$ȆJ0rH.d)"a#rX̂bBUVq]}uNYn@IQ)DI*ʬlLE0$Feps.ho^0Yѫ= *h FG" A!(%)/L"w䖎p=d5gT#JI"LIȰkѰF2yAF`X?-6Zk.l)ja=dldʨhbZRc0<#rAԂk%bV)ӏ!Lƥ}_AOVJEvs_=dR+=vQK %`!ztk-螺/:abOP_0/$TV-/e&4Fh2!;`} 4 $e184S1޴D#SO^%`@)0N % 7md5tbp>}+u"p8^=VBa9{N$˂ `2 97m 0}վ.wՀ!Bx=)F#Ƨ HoGy C@:;۪ߩ4T/ǡ2^E~dR 'DJ3"Gmu &H'rKF?&Xbv)%$rb Y4I_nZ1b K;9ӟj2Gf..i!.a'*b(RG /w#ځ hHoy sEu5e GHZ/|ƫ5@^pi=hV>DY}aF^?`K'seiO teb#4>8u`F_FA3%#qGNGĀ7%Ն-+1ګĽc^ϻ Qd&X.>ixͱϘzb/+.jb=pP6v2CRXTz-os#2Eol<t5PH1Yaڥ1m@_*Ԁc/ϸ -=iH?TWiA!h ZESG~\K ؇M a p3|vU&Ue%i[&n-#U:8+#7`̴IJ~HwοWlzmQ쀩p9r",ʡLg2I HďmDurXbn@P蔭Ԧ ։%'Mg>+T`L&%rVYBL}d0L #T_LUM]aPVv DK8=y94r=o@.C/8E&2@!kh>Yu1F*9WZBXRAr2O;}ȊrEc :#)euS8]ш޺y~G=s+ 9P=Le%AWyw+Vz3G/~En uJ;SJSnJ='t|+х碌C{^vJ1jmN|7bA`LgBQ'0[3tz& l>Z,Nxh7Q?q +)à-UlBEc^)D}kw'zDbb9Uk*е\QEU~-7 fG.R~HjpQ@͍!8+mD%X[yL5g`JY Ӛ4d'hP3G[ ns5gns lT@; DWhݩmŸ9uR&vrU_Kik6>^p懝Z6?((leN.͠ qu! _u)J/",'D͚չ ??ƈ<,K& 5*0J+0B:uŔ_eR}ֽn §j%`&JHBaNu3OhԮ10ufCn_rAeVd-0Z[!;`qFCeX_w@ Jz3FcZސ.͎ҩڢp:[#:?HgJ}i QP?&wd̑|[tN陙OR&UN^|Of$Mmc\4xD+JaWbX[4udHx9AwN-ygDi,GX΋_<쉞jI+;v DHzs tI'd&9g< <̻Ϋr͹h&zٺk/!rBz\3ސjx%Sg 7`'2w^jg^2ih{-^EA-1]dNVv.f6G;am?^LLX{-WcN2̐9Z!п8y,_,[R Qgl+d %IOR7)Uwd@`Hr~2G ixu,P*vV[Uw@)g;_w<4 SNq"[{P=5ރ%C6CzayCCg{dD !HK ٥,; *:xgכwᰎ=Q<-꣕m;.ɩљ.h <g< B=+N%2kb/`E}^ǓkE tzHSJN,:hwjfu)G]"&DqIDNէD9R&wC+. ;O75qVUUڏ|^~/Aok*^=θܩ>=h[GVe&]R7OY?O [+ ~VV%~mb 1Ջ I[b4ُ2.LSF4{x ^S;,CӶg86Hr@V:R̃uxy\(-}Tpy4^.)oz?@Lڇan R=<`e; "b\ׁ(FxAvY&ND>ѓQ,ӆ0\@WU/>mǹ"3+M,P*yhR p}[ʦ<x8b_KC9Y֦Ϊͧ"~n+2Uqv=HlvN "~k9 \fy_,jܒꊈ2DRnO}tꐬ-#З#;&t9VB(<JF0mUPRG#RY}‹rMA@Ai?=lgHcTrx i`@ޔךeKoh\(o/ ?Z6;/bD %%(cɤ釵uPhHVY,%\.0s|*JJ(]W+z?!`ibD+@XcS\/z'?Yek:9+?QJVrj2hYM ګSͬvԟ-ˑ8EO,=ian'/(|J+Wh& bڨgx C=%(QND9f:H\!23E؆Ji [a:cD F%XKZƗ'l/yql&@>i4,LXPFkRt{a0ͨ8.Fq@ie] /(c/Ʈ]w6Q_WH9k2yg<G U!DSiQq.&o8@TpNaQ5Ov%9_q5V*M*]Z7lt]TRL<uí%5a8YTR];8Zv(8|ICN_§:t+ھxg6S{E 9fu]w-µUr")4J6mā O\dže62HXp=*) sqӬyaQtKE!y}{뜓Z%-ݥS6ݽ'h]u] qh32]uGpwaw|t+w{iY>ϲIjkYN~UĜ~)ۊ]~P4l CibE19`CUQuuɘrSKyTjHGjbͬ,_!Xpgj|b"m̬;k5̭fj5zyf>'KI.[Mbb>Pl loI vtero.apup"mt=@&^ p Ӟ`;k:<(z,}A3C^ŷn"f 0zS2 -~c.i(4>jJR!ƪafȺ R FT[պWV{-r @+ n1zfǩST)qZBeFmu㲶j}^OӚۆS8nArULcN֧[ݮ[&/Ė'tE^TK h(,W] epӌ}P8R-{`x50}lQmfA"[{.=Qӓ@vn Zvhs/6-8 :eN\:ţD丑|\}jMsĵ1fk/BIc }q30-禓lxqu08.9ʾlS+%U9&_ {>Q,q̾gt Bon:t7vbˣa<~m :6s)]Osndc *)R:^Jn$I0'^Jmsp?|EekZe 0[Os,t,$Z$fز\Fdx=p@ɚܧfe_t?F{ػzȏMUeH jG ;W73tpro܅wuح74rXHME}cN8IQ73$yH<~z'\rr7ny8K0Jk _8ω>rnGΏz)`gQ=l fski9^P0*Jܟ9*_e:*ڣm)/ Zs0X/ Q%Ӻ[_oUrd,!K]UW8&I|U y>[^LjH#ȼ (ew:c9:Q C*QX~Swx|\1* bp[MNHW(0dž*Gj Mk7Yg Mmm&*+L4\!HapQ9"[Z}7i*#H oY`_@O7/yI630slc/KN\kC_2}y] j.ȑx V$m[l<<w~Q]j7e+;V-Z|F*)2x "*t48dr^վM&O6V&7%Ki^Y}4e/g 9?4k);DӚ-7k68+m`~kڔ?Ϳ#9]:}ut!:Bs]AӗBUv\knXmK5 =DM VFȕKȢ֝3 g(-ߧ)+#{P1/V]Oҙq*StgKKBIX@Fi6Q2@Hsa6yp"׫Ԏ82gʩOLA'5Ƿu0Eg{`8B8Y?ÞT"+]e{ H1#{!*yt4Q{E{X-S2puDW{6g":6ӜAr7yvrLN-p(ckOpem롽AQ\4E qOβҏC#G^fw- ̃/yX QkE]\8T-p̣ac53f&"Ct6^~3^JQQc1$h-YH)O"b8Lï;pQ+ PN,՜sF; ̞7lo b[}ܤUC3 TZ` @o}{-U_RVotڙVu{}Vc+"P6mҘŷ)U).Ƥ F8%tN&Ë=x|Jag ~2Z0ޠoxf˩G[C[Fv+Ri'ѲVG[ -,n'^4ݸC 6cW$v[J?ks݅"V4+"4ŗ2f\  {ejze=_PTH#5rڝ]1F^tɝ光PLlS(}+V=\HgVrE) ˾'zfLɳ2]&mׇ͠"{($H9/CDfe@mXFߡ,${4{cEź+ce[Bk ii<4g8g(adhzse|tWNom {v$2~=o%Wq}l29WZjn^ә&OX4U!(n/iy_};7DTGiazf,6`ȧsfzA;%y`})<$4<.IZs@ҫ+ۖD, _4~# b\X=Lo{(ZS 5in pOڶ<,ѺgmG_p޹*1,1ֿLOU^]xs;|ZRy'ča)OB,mZvvEj'-SmY40aeq%^o**v2}s R]?} 鈔BUGDZdrI)\n)$\89. )y~9^y*Vxb?_ Yx۴9O^!X@CH2* 0ǶhŪ6Ss Wtv6ewOryr=V0{}t*} ܒs{$e[؝Stk?ZuDXoJ~"TʣVcIu\4t:2<–R"Z5NJ='.,O9ke}c9?#QtD$,v5ӫ* gf #i=x'Q*/If\ō!N<`q˝ ;j{!2)OuE[ČajMU H/Ѷ!?PΨrY\Gp(|![8~=3 sg=PB OqFy<PSsHSb!o4<4r~4m:/U_W/|~WnP>s(!ʓ2n(Qn8]BF)p>ۄN)>5=Xx١[r;Os ICDԠDp"ǣљN[sHTf)}2ҕmxLOEFȻ*7vOYC^.-`cc*ڬ+lnރ"l"a<>-BR|C616nL"0hƒD"SIWrF%S\ZY-JLjڔk_N+m.ٹ*f ҊqzRX8 wN]i8NTXή` ڊo*0n}̩RC蜈c:6+w]Dۦopz썤I^tP0ٙ&8ip)R@5pƓJ^a>MFݼ`4ig;h GF+{x# HЃ)(zf('/-wuhpj7 ֞Y**Cm7",ðGsq$y0ml!S cW{~ u 'Z܊DU <dSdPIcLq/E/af u@fH}fVA(ؕV={D Ew^mE26w4ʞ22b.He'y1g0dQOMOu Z:7go.EqU.WK ɉKda-9µ@kOn˹e&eBZvV-  F2Tx_F QU./:th5 G7j G3۾e,I;?zIca+n_|HaJXت]N ?2/c%hW^Ux-)M)!~W6Ņ8d g pWk<+O9{<@ta r'3͏.FR)c‚rNT?Hx)ןrA ^8I"y#FeIGD彺Ie F;RcGl%S4i vfey? ;k(02֋4^>C= ] {̰whFFI/ Ɍ}\ͳ-Rh BbZױ뗅.?ΊŻi[ogRF;]8纆`g"~X?YLL`$[ާa(6i&?1Wv9@RQ׾{9jh# ycJdN UK HaY&I 6z^g ^:@uđFͪۍ<>.\}Fd_"%nе w  R#eJe&R݆RE3jАQ >Xi3i&vIqdEml,Zjn"JCق Jׂ/`?~hhO`b'"#cPo\[1#͢,.l``?C1>2v!?l@p]ͻqFO4@YZ|;7LfM@f[pǽؒ6,([#^rw} o w6nS*C53hW2քU%,c y{mm5w攑dwBQBz~Sӷ"(>4{@%PpAn`D+djBmCع}zȧPQ}lcP?@TD'mP0,T1I)!cG*슎QTM{\ !mCfP*{IJ0 Ѹc L={s%oV 88NH<|'QA0xR-_,$b>Yԃ +ļP8̇66l~ďUt/5R,_33'qqb _}tn  ;SMG9#,Ӈ?XK֠n 6u\s=GLf'A~֕>”Md*dD__ M\% d.!#<\2 S%&gYD֯erÆ8"XV(it i9cmL~}CsR B5>,YƋi9 Tk+qN"@6ZpycUPeƅ@G1O<_/[a"Z0 i-&*qt ={C)AEm|k~0H~uҐ$3=33!2^ߓ)L2jLEkO//vx ["#@iB#@ ѝQٛHFǚ녡^bCkXBg q5+sn:MOf)_+7W ;g0[Q)^'N;ZIBr6,<8!"3VU)Aа)k/;Dc#0>ԙ$J9JG~Yd@HKW;=iAxa4dpS1 cGcsNd]G9LWuRkchsU--{O c7H~Sf$Lc|qh ׇj :[ UvY-4'eUy0vFUGH@ ah~1D 0nd!~OAɣ"0d=%?a,ŶPa1%EZ.+ϊ{I5GkiO?2û1uΫVo %(`Q/$H@ga9-"3Fȧ,[T4(@L0?Lïqmv:E:$7 zǺ\>@jƊkeC `:cHfy0_+,Z;8k uPhU;h.ͩdNf!ZGjܵf{m9؝R tD+ 'aȬPs*d.Q5,q}uAf`#᠃?)aсt2ǠPsL$GR=J='ϯ5?y 䚬#Xr(w"Df} !}J6a{Kǃ⍃:@pRX^h=FT;CY+MrONs?TȲ wtذo7j1aKܧͧ7}G9 @{k"ySt=-6q A{.pUODFSFZ;nk/3䲡1?͙%A?I]Fz8NF1snPΠOT A)98eGykʙBe`_:0g:\`&;;wE\/|Y+^B]IHOH՞>(j?*f{ 3Wۢm/ƍT&Lɋ-d@|g,E*za6s &v%d8ޢgi8>Hf?/Zp+`% `&gŶ:% B!Niuehk_ӞeGsDlF n^zWsKIՁB5?Ɗ9K?g;{F/TD)t E |t4=,)lQԍLt(=ϥs{EqER gXvr U5C[j8˘X*'Pz$h]>1l~VP |5$ V̑9q iU) c2]ϩDl⚻m Yw"{''zRȷM-U,bwC`V]]D'F O⿅9*& 57kX;Pqr!y[ym6c>6oUjt~,i] 79V̇xa}u^LVOG"Scu^k3 {+X˜V}X,}XM$ n!Pk WleqNUl!=,>(}q Al{0@5Y`% ;ǻ"!x3m,xYɕ|7%]-c 814j;k:'kW}"ԑ qXt{n]Nbu^fjЈaE-)cor%xXI3@4m[D BKn)5ǁ(,|2ca.9B׈ps@~fj_ 88^18cE7:9Qkq @w2 ;{ғπG8?hgaJb4ohU^1ԻJL~_'W z o5XQmQp!3CyataI *j|ۥŬ&usg HcKro:Yqj";1=ltU,Pӽ;6}B fa@wp+[L&YC?p Y1A('+lY[sN9y-?DDI7#F}Bؓ6WWdoCkMv|l}cj)n S5S1G`N>\?gŰlVˆMWq sjN8Yػf}Qm1t&QSBɜ]KŴů ZsG&G?~5*ADXhhl,63d}Q i¬r\EknWg}/]bk&D}zо},ɉ0`h*f0Z+Vm̟vz8|(ōY*!𦅯(5!ͪ}4m!7 ܛ}'.w?Ћ8Xwȟs }9Hw\O? #KFSjBIniGLԅ H R£6s;aN;5onp9Űv[: "7f/@L$;1t&WL9D;#@FU' 䇍~yB`=ixvf0٤I\~-;nLd>w~ .9;=K!9 MloECZ:J"IG!b2̲Eh((lJ-Oi=K[}lpP AlÜˍ})%|[@6̆% .:R,p'VI ?P͔pF-iH.2{ĬUD\ﵾޘe7aE֍Xw{_b*͹F~%z[Y^l}Q"~ K- ̬ZE!&(;PKGWjNY׺amD$D'zi6vn'F=Y7yQK/)5&Ym^`49Wr(Wa/71^ =7NUK R+ 5XO{B"Uo_#;: z )`l򑴿8\PK1;:(Bճ gͰ#i!AKD|evE Pcb 167J_S7v\r6 ~ò#glLo;#)qyObm6ar=ʛ$/Y3Qϻ$v^^2B'#'cR k-^7V(!ω  BtY|ZWԉ|.I+Z{[/eYYvZa(طv5/.~fv@N%$٫ϝ3Ҏ~^Xc(̙=|QLTwפE@|F)9 {[v}D:@ w 8w^]ivԶ4@,RzD[f2{H}wuď3h-WٙLv"sٻPʼn t7ҋ@幑@!S!Y7sSooo6 .=0|tBܤxI]zn2`Az#bpެ()ӂ b>+b%%6 k# wIrWH]"f 2z/*]I`2#IV*a N%c$,=FBf! v1c >۟Fw}S}]gT>KQ\I=9K%8#e`3yZ#~z^'OgTcv+Q4I]R^" z &qAm bXe6C` KH`8|XzhDXY{ 4>yW[ \8duk/L.3pOy#[ݴjĦ=&cQ\LUGtE 6N^,?>>aؼA }\:u^X?4FY%MBkĊ;FÿSwstxx? ¢xzK[klM.ݷd]{Ż [ Oy{5w7C7b -pMRnM& ;G}% ~}+"a'UqPfCJFmwŘ>LcY%D#_G/NȨh8z,!8e^ Ozԇ{ՖktԮd~QTZT Y}ݔ߬4?*88j<*"q&"a4mR4غ0er"G>EK>Q`[4cr$\tIus9- "/H)({9l[C~"ܑ(tS΍>j{E݀ )N>eb}DstE 3 .ĉi6q.tFPb*J)1A@8W&>a$穊81"kCaْg[ntjc1OT/%05 !=o/9z ^uD%㍼j;?Hu"PߠwwSby6zS.?%>&oT_$ra~T%2 ѨKhSS?T962eѕ=8?L[V)`#78<2A\ʂ&077/ qV!jn=\>=1V /;HL"Wg'tα/gݠhMX-w&%`S!Km42'B!{+ 5wpu|4V/Qu ) >γzr (օ,z-T. cy?Neʄh|ƌr ^WJFKlk Z&dDMx5B J8͖;8l3dI} N:#~ 9rI} Rb'j Ɠ*1)YBDR"-iGxi_ft3Is߭F*ciI*~hL OQVd~*HA5 8ʨlJ)lЋc=;p3(N*!֗n%r2!nJP0 PI R팵MޟLn*q/Cvž$` PWQ+Y8y|K-xm2.Jg 77L-_`srOT %ў9fM M=3fs DBb Yz',+AgNC`K뽹 \IH)2N=4n~qޘ;O=:ߍ~s}+P,Q!g&ȗ#0`0[q(KʐtsC'r [k!ÀOz(KlC\EexgE AKb&9o Z+vYCEnCl.Z| yot ?/$ˎhYa -Gr?z2Wzx3B>R.zA4eu9CQ(5KR~[Ud/pÇN>_x-d3pn?d˯ZY<%52jZ(8q9*&NApbJj9 DtٍJUgO 4:&cz<eoȕ+s%e5$^iUɘ߮tg14\M^lsEd}LWаQC WrWo%]W 2:CE"eZHOGgLDW|Rg"j(x4 x R"FUYh٘o#ny.3`i _Y+J(Xhiӭi.>Kڳ: 9  "qڥQױ\;g$TuY0l坪tMqWLnWhii7B`#@iB/|XPjcPdHQ1. !;ڸe2{i|\F/ʁ_!1A>0J3h0#nP ka'W5DvM߯=ky(n$9<ԑ0pRcWXRUq8ɍ|SZvfq]o!X'ڴ„h_Xg3l쟢fu,[E`ߚ2 յ  }aO3c:BqYNzyvNĎt[}v>؀*|?!UQ /F=?Zm~ ApZ>9 e<J5[u.ˮh#,aV}ᴃöN0\c{CztbE)mk:vaEޚ0@5t6~ɕMi3V) ;"oV/lN2q/z0 +kzSRΛ*'Do"nYcwbYlȬE9̭Ԑ0 ԩL|1Ԥ獝/Xh9X%)9pRic Ҙ#s!vmzC*L1˔ia ncpU_Lw[U : ~t N-7u_gM9"gVd׭ȌٮLw^8 Xba $ۧcIRŠG؍+Β*GrZ/G~4kOL1˭TO(3vYk'{JK!͇.il &kO'QZK8 ID/(җ-[b ,Z_ 㘁{Jl bzvcRoٿZEGΑxQӊ1(/hNxZD&fw.]f45QM~,i{'LޛW4NT$' >KqKTBMQMa1/ OP׶!sch+ȔL$&UgT+)1@Z6f-NzF5<+{ vSp%D}/ۋۏپѪ֦@ isܠRh @ #[lFЯ^P=ůpwAZ˰"jBq1Z>š@Gs@$> 2Z7]>qC%*% [@A19kjf)7>ZtR̟L2l*ɴ yl=4zo;]]^1ݓ{3s7DL4A%:mu+HpV%]-'<9!eKR`bA|malԠ޽0:csoe fh x | `]m8OhGVGh'iFw~# KDK*0Z17 WFHء#iI@=y-ϔU8sfnSn+x`7=Gt5"L^'x.N"@15Jz5N[rYIACߒ_J[GhTe ^|Ow\ͧU0:aUŅv*Xkj\^RR-nxJ}hS] ^ؓQrxIޚ]Az%1eN35dwE)N(MKl!5V@6t{$ h-rlVN=ͨ,q |heƷ |;Rh B<4qRm1F ˲"+V5UYx8u)I={_ؙ* ҅OyvtƄdSDm-B eL~g# HTK YIaFAZ"y:&t2׆Ih(tx:*=[bVra -6s m^\Jj4\@Ox/z`)9(4f xqz`&kx 304vdɑߖIǻk^hCM66U 'XAQl" p{`J9?NUY{*S:x> aiRmk\*`a[ȯ(tnՁ"%L+vB&zθcm\\2!l>bԪ̤}L_z{2qs"w0׬]a* Ea M& ˀ{"TtP^8]X,[n>%Iͦ""9Bo ayҡ.J@&L( P E`D!B;&0 \/%zFkjǻYf>!XmBgE/$n—.]S@W=CÓTbL{j{E݂f"%ݛY2{Ha+|#gH0t)W{n =/ ! K'iL%GlijV-S?vl_Kםlbd!20fXEDG^yqhh~F iv X E`.9(xB?qy \۝GD˓mϼ~;ENɞחr:(YqZ/_.?y᫠qnҰBeoB2lm- ~mLH;( :j E0n 4ѬHB/ojavL#sk-\eQzzu%̑%hVelZ.0_M]`'(~/\N V6}$ׂ@2ڢ(vդcz^Ox-l EBU5:_rmOO`!I]M=mg6M#u!ܳMdBgje>8fq* Oc1 Rꡦ0nEŲ{،b]izOM@~_4 MP YO֗H4աÓ$쏏^ ѵթ˺Ɛwhx AaF}]DRFm8*ӧ{|8"Ļ=?㔞S]s\,Zk%YVU`9q%Ԙ]?./ WC遈B!цL #^-2O6.M8H~SfdgF8\awȄ䴻n7m{f<$'slE"8J¸힮6D_YJ3;l/smh{$_X?Y܂oa*͟+hI[%)s._rLv]JGš|ޞL5+̊`vp9FO"lopM4zJbvPʶ3>]:rge,9DvŶA6vhY{YWH(klf}:m>r,>;u&oÄJJǑv/0⦎u3iO`ldtaȕf*+V|$I]sLZ!ģ"֐+Em8tإ4 {h*O FL\ k^Rj%Gm=  @ <0aRYT\C._Ѧzs{xQ(Y!Y/DsU]%^$hT(L `0,hNrT7$(N&Y Ա_Ԭ:dMXIȃSk &YoÉUǘq;8f^]~`Mī{R|Z㴃r_f2 Iye1yx֥?aN>Z:/TA9 S΀sE=.? Tɶ&>*26|u!N;GΆnj$S%Gϑ¼hfslC!\. zL`yBw0A{ǣ\ы'׈=rW}1_M?wtN5d3MZ]ηzQl)YK7]T IҭL.LBD1z2#!̳FV)tH&{+wNуSf TljS ;9pnmK^MŚ)s91*o'3`o0*v^cTnsh~"@- H6j$[QM*GoƯGR:MS4t4VFq!Nn{5Zoܩ\[iǑӸq|p M53K''Y\bG>Wl:NS-@>ysN1WzdJRy8qQ/a$^YPJ{ђK c{⩚#=P7C8v3oNLъ{=ݡy48l-ødGt.H4ѯej- ZhQ#$2f3g3{ X s Ds/0lG7ap;UZu r0T|ne9ge-x i:uleޔ}o~}J46%mF4l%ח(V3Qa횼m(`B,;F:R*~Eχ_П`Ndǐ)eT+ W ݻg=euPD:Tw+3\)8Y&x$h')0d$ s&'K+epyzxDkXD꼍εǟZ?k4㎓QwKxGxk^p^5᱒~XJlgKjvxu s-va/Gw.wELj23Ƴܿ{'($2"JS3>Y*g'\őlmlosm 63QwVqdܧFQ]̛g-.d{l3`s%\ g'Xbu f o,_`B)C:?)Wza{MC 9q܎C8ml$3s8MKJVc@4.I[}Ebς$̃[c>!P˖~l$ΈYƟZ"fSm-mӡ?6tF(=kC %ZjVQ R9ua)ÛJ2,!VA':廩Wf xx0(xiJeoy30Us6PbF.0/'IE5*pZyk}P$M5v);:SbiH"&{YX[5 07Rh\ h%~q zsҰr%}*k-zZ=,;i178-o^ݫ% R?2\t,aY뭻v H@F>EYtK9!RVϝ!)o&&[~զ?OeI7!T(Gf "(̗zhv^usLbMa&q׵.7}+ᥟVV)u`bKYl-9κ=I^u '6V,\ØY]3 |FߩPt.^2-#뜙43GcqyK9n *a(bb'a^u1ts2"@5T~lBSRZ,Xf$韅.I' 1R!Ih_x>[,x`=/JP Ǒg4ԊhҤgS:_JQ瑅7:8B?xP}f '5i"^,rkO l;KHpYB_(QZ80NʳNfHEoF-^f r\N<Ś:oQ6ez^]xvs;Պ뀙2ش4ِY+&k9We܎$dRį/pP 7|J1` cY\i|^j @)'\4x(^6jcd ]YAے('/em~Bшy=Q7MM*-~ǽ~HV7~U_r~qy'0^`TP|g -ap&Ê.bu9mnnMRIjp1fh}llL!‡C}I=ӭ|Bꪒ0g ߵCgƈ`%3%B^jMhآ">(Oq8ܘ2eP*qƧSa(C't$˼wj)].h wyTO]ZU Vu>HyuĽ03w : GRF Ac} ;Bw[5j__Ͷ$ 3ugI[-K6-g}s_ߜAd1Hu6~z𴌕; EWd,үYuAqzc*wr11/'.OЍ~a@5Ɠ'ԅgRMs@Q<[$%Oyϖ#7~HVkrKL YE+@fNf`=uԥUMA5yY[PaP_uތpȪj/bE#b-ĠO@],mӬ5V U f{1]UяHR}+Z=/\d}M\TT tZ`: yn<,䦉Ӣ2 if.һ 3IvB"Ee^ jh b3,[f.d9OPWq?|~{ЧF@e0pҾ̐h5 :矨vWΘV"Dğo"R͛?\~b˂h=E|穂8~ t7H(}bTQD*.?n}S*C$<GdO[!1Y]Ԥ֙o $ecU/)DgQmvo,:8XĀnRRMoxPkwCB@&.Pj/dz< Me~򃀽j=WrO]X~/ủfCWtG<"#FH/-ٯ4Lѝ?<E9U`9cG7r5M/X"168fVk4mH!~"|Q)jsX%w[4%3]Yu_rk`qt@rƫv4^QF G۪ᇤkbs+IX,'7©lX <ͤhc@Pp_xZyi떭Y$dI'->Xf1?c='- F[5~.eo).1)PA/5m+9Jb\Eg6S*W$DQ̗3-&/X9>0B3؉nѣaٶ#h=9^+űqn*cV];':A P^gH l,`V(̈ٙȏG6*,vM[ĬhZL.&maR*h@Hf~<{R[i?[C#MXgAI__-kײSHa4v#S"NHm #|H|uӽ|JľT͔'S%qrXp;SN؁rtθG2g]/.̭)3>DY􌛸 贞q/v߿%Y^.kK83\j6pOndmwF%#9M&BEF 0Y RC=I!`%T(p{gVf_sa|\k PiE%jw6īqpNa!/90TԀ,YX+7xˉK=bЬJX/*[Ύ$@& |}!WTQ0ѮZ'Imن8(=J5`6dl@GRX::Tqj/G6eZjHVZ4_Gnq 0lօKMtUmf†JVAX5*2ِMu22$Gרv]ؼ{jrmKȧGi7`kpu,//m.$Y)M{ b*QR׃$z]0=cۼ!)s 6Ipxg"dRށѪ"0ze8.n'4<"}Ȼ:OҊ|t2F{Zfg Q!SepDȶﲙ9t `Yq h@­jO^}4+2G@a!jmE$ڽ." 8oꗗH\-vw rAGmC֢NʊwG$*f#"V/QNCEhS⫾EۥDwO)RSU!*}ٷVjhJ )4 28$Dgц fԞI%fY(q\V}Zg֚@D_ϐ#ˍʏgyK{OȃX*@Pro׫ ol0dȼd^-:(RUňdHoT t.HäM"5SX>+B}d?˯GM Ztf^UNkPXx?/G} Epǝ؂E#L`V^"t87_P EQmC$_Xãk] aZM P;X/`T:wCWv\P7RZlrѦ7*#'_b&HE3н xƂRʇfn'Վ  цP=Y<*,=ec?# .!|c?`l?y_wkwO6'ra0cI)8B@ {}F{4'1»sP:64)J>CTS"=jByԔYT+buV {ɜc9d=zPum!9U.ĥ3H.W GPIdPp;~.-_<)^r;]Z0>a4q8W'nJG +D3@ʮs{/:B3:BAj7MclTh;W}#XSrM-)wGa^Bv3)ZOW>zYiu2O M V2Hdi0O;4%RRBQJa9gfc,{,XdbsU K++R5y%oYjh3êr !8 4=筁ۧ?\GQ@/+_&0H+"Cǩy];m{W}oo{ 02F%>R7&g6E{Y N5跉otI&]ecyͪh'I / E#<0ղ "` fL'};U4mE6~tyV+TJT _>PMl;ĺ`xiSx)lEisյsӇSҪ݃ Zw4I7]g#4oܩyC3K^h.0A\q*I[hM`쨨"_+-PW[E|K -?`NH/wF'Xa/ w0}{(NZ}Dk$i0̟L>Nx"|OޥѮ ĥAKyфr55hmc+~zbJvEf(Fj`AyiP &*8S`{Sy `' #Ym-]%f#I(2ÓX"o; P(o˄S+UG/ɗ jEr={?c}#/c[W&KuvIC:Gg w-0O,-\ V˭ LRt%wU 7[㏍Db)vE1#Dd>TGr7-. O?@ 4bJ@o0l1Xe#vP5aU˥RGxy6E05Ml ӻ,/e|RjTf=DSt r}NSgůx1Vu1Y!\gBsk+i' 뤃YaWS4:Q~9vɉ{"p-S@(;T1>ch- rqcv;8~,\5~@M*P ½9Yiﬡ:#*n93g $]@A(wtզ9/~!BJZ͆^8ߍv_J>hp",տ2`V9ځSBx?K}ةȬƷ+"䰡VO 86L&c96+ #kfEL@/Й #F&A޲ߤ:#OmodMA!a͸C.3׸~(=~$**#p["|۹Wf9lV[+Pe*[7Q1$2ra4u ;27d ~# G8m Ĩ%z ie[=cD1b؇8HZ<_*)@/ Ö.< İXLRs "`U`D#ΐH,#f:}*fj٫5e۵v鳬}_al[ #:8_`'m~ڎ{1[uBYk鮰ㅂ|]wT@u 0[n}ԃPsӉc51Jė.?4"W&(u9cvlLQNv e]5DKQ%ZJ'+tQ1~KfPT2j65}V.w֋ktY+f9iaă3K5g:)fEY8pVHWnk-4T7M_|n˂ I6W \I+N1hQNfYOP`(3\r&4⟀.C'R"x"(V$fTj"ugE|vcQJdwp*js׷\Pe-ʉ'f ;ҧ+̯MP,H:ߖj^ɶ}u9Bk3׉y[#*%vq`,-`Rbh;j C@5.S}qm'%xf+ݘdkCMRM!"PXrLBWdv)MBѼʎ0̟gR^S(#bѫͽwl_ C$Dn=7]!l$i4rB,8ӧdH+ S֠7/+V.F(V8t{zv0YRH0AG{ _U5)w1 4G% ;h=[@Y]/4͖x,$ N.\IBG$3Ji@!QJ=<~ =TnmvdZv{5 k*AǧoWV l0-4}&$J.U|Ut. cU1#]/(gğnyO&2 ԑCx֍++6BT#ru8_.Q0ۭ~L}$ LI&R79q a2/.Q.۪ ?e\<B'Zw0,dPvmSAߺ^ߧ.|w>*ύUd_6sU{P;%w=&}ˎky+Y'*tVV/ n:gMg_]۵0l DL tݠ]b\Cjǟlr\ۘg"[zL|eAצ]oaSKrOkmgX-|^v,%! e/ɒFG5tF~䄮|J]/p_48 1`(`EPڦڃFdcf``TxNޜxJs'f'V 2@Aq2nt93*zF+wh7yHmߏUm5/w͋LNu<*m~SqT[4hw[3 d/+~|! I^G/zv=`JOSז#Zul\t+2LoW1Թ.jS0ٟ/ϲ2APTYFħ&VOeh%A:ewI fUZAGTsTKIoݍguqƮ'lq%+8p3?eOk V=[ XmYOO,^d=2 6b~V~﹫*ft. ԏ.@!Dz˹Bz!\ Vv<28X+;,V $Lb=+ߘ&lr#r\ LUCfU~n`>^$_^G=iD \HjH V"YᅽpLb:,u6L< jt8A6p1?4b/Cy }Y*B#P`] C&0'Sk͎9}ւCo+[&4+u8ZWHʂb*D6"$"Oe@:_&eQձ{}a |TRS)d:NQnAgl;dW/!S>'x..l~ѨPKbѝ68:ټh]昙>܍gL'bmiҜESIf Z,>eljk=\9L~;H /3 K+ &~=I-CJ@m-~xGyœ?zB@ ^Op^txm3`T7=JCf5fК,"MN:Loe7tJyu??ts^] Q%M'en[yˎ_/ ,fY>*dM&m\}'/GB"Ѓo]'3Đ'w nγ3W~aMN#h( p6xR7rli4^ 6)1!b2ױNC#ohM/y$ ~:NÚ;N6_T)JL0(Sh2Z? ܥrՆ_:~YbOxN=ݎfJqZYq$'Acs%lp;%5\åv ['__6JxN*aiNMΓn"#rM^Br&Nf_~M ©t U$su&2m 2IY0J ibm&}x2H3( -?|O}hIT$4,=ۄ'cV@aZIԂ̏G1sWL60Id JzKCVxWu>ۖ>o,`jRrGTd*,.kR$мsOQ`1*p󴙌p =.Y!^!DTuG[ab$Rf>?l*% >>Ib7E, 8|.G$//b Sx[&6s|w;v &婹_]{8 ,r& Vtfra*' ^.,#;LB.Ө0 UYJ_B(3>jfERc3~\#FUu̖-wSעȞrZ8I6n{|̫AFA$_+Qg9wOX t`ٻ漣,Q5K\0 Jd Yvt`+xrQ2;ոW,ZawJ("e2i\Ƣa8QlT+U)၌MnCfCRO;BHێGҪm}`sf_[Ҥl]J}u?tZ9J= L#< ט@Qo[9޶;kR-QHgfMc41vyTZ/t=|Vf:L}|XK6+TǨ.il{\Td\:fF=L{Ěr|DTT{>^H̘Aq=eKxo_=fӌCgcT$گ^_/x ,t8~):,٬%qCP[%O|&$촓ȩaK5B'H?D Q;CnčC1#t2%8HĻB<5̦9z}[32 rT(CT)I[3㣔 HwEi1Ma[oa5ژkӋ̕lgMmfЪ?#h&ܺ 9Ebrw{GgFp&)P)M}bKVJ{4B,bKbv`0f쥙 5R5_߽7v4we.jrp(VR#dm`Q>H|aFRÀMc.ɜ@m8a|3(cq OM% cTu,R8~ju|:չ7ti@xuS(H/4,ˡ n TۤB!ɘJg?e1h`?M4" % NѤݖ}ҴG2ĘS7Y}8b)69PydWsgQ T[v x{ka9<:Qz_O3kr&Ke46L#\|DJ&ðB'ʝK`];&RDoԝ!zz4۱銹ﮀXwH43k($s7VaC@ՔEbk.UTVU$]6ChvZ< ;#d ~`rV%]O-&@ bdPYu̚앜E|sF~ \unB$2!4cݽ5uz448-:ͫj{cPpMB$q~o0ӯwUg(3!aG,ln_8.ǎ&̭Ԍ?u1آ>Ve"y%GՆ4\F,3ZZ`ب͙e_ m^~kIf@MO=eaHʧMlIz`n;ǞQ $~9Հ`tك j9પYp45>dM T᫡!D,#ԛ15 ݕI94;Za 8A=VSzqC۬Oyƈ 3Q B򴛩m:pE\egҕ%0d 9Ȫa6M ZaW1j=qAk-B,dqTq% V2bdSmӺ4B?Oy'~V ](4+m%WHHh17R{U&]:Ŗ1 m 5& avAbu13rH M7 ~vZ ATABcGネ *=k(IIg!>XWDf/nܶ1&s]v&SOuIvjz/KPh *4+iVBwd@W&pS,3Ed@A!pp6Zܢ?.]?4-񈱬=\J-S+QlG?=CsO$NOp6 dڛeUhjRIή;bA]bSb8~MV\Ms~jYu{s_# qP8 DQ榴J6rlPdn ~i^F)WvgN_k$7y:}Kl9&KmgpB+%\0̷>(0bK84ĚG@ (z~lPU(Dr=)krqHMh݂i8TKQٗk%\wy$-3K0 uF"HZ7*\˵L6d +LE@ dW HEzxq7(ssqc9UpdA2*, !Үn$.VU6?:@77wށU2kms]z"FJN':zcW]99\=T.^24ߴddG1p 0SQ^9DO~z%23RBLvNVBh0w9F}px^fGNfuH<A}laʑhJ#A90/ >x[N 1Ko>ʧ5v|!:&V}rx>i̍"> ɍ gŽ;WQ/hľ+!mrJ~}zhgIxgNB/W+h)hJ:c&/K(:^Ī<(jh8}IlxwRIZ`VMקplL6U #C(YV=#YF00(%|a/kse:NR&ÙCLN:_]瑝*n2K_}t~EvRNŪ}q:]p.R, -yCtcZ*Mɏ6(hz:f$~D/BzdfW, cÜĕCяI ݻ98w {rQ$2tR@I`۹ʹɸ"^ň{"2p -X=M\YI8rdNn~rr'>hBaob,Ga37iPmeuBS=/Aօ# O{_sN?gF8C'yqe@SIDUE-<61#Q&XƩʥC#n8ffJ'[0u|i1ra7aURkFnm\@*:yWb't#@[.px]&BDNO׿;E>~*2 jgex=%ťt57x-j+}-},ۏ}򪽽OlLTG5:2@VG+ᲂG:krw 8ws6-GOߧyk 7s? ܽ{ʱ4=l4mi}!W?xsU oV]"m5_(1F1什*"f2g)s}QA]oc!їN q.Atʒvu.9nUŭ‰&Md`f"2>E]4*x( QR͸G(J)'0@ ,XP$#PקOrnjyefEe2~gX*JY~;/ s9[3~g/s+)c}=gg}u.bM.E3<%WeIE0~:hjq؆.:J'57=U>I& v< RAT+"K~@r|;$#.pn\gc)o'VL%`߫>qcaI3b3 :5Ak$}E,2؅CsO޶!u&:fͥU8q#͇¯b}Rj;ݡ_@\{y/m \<9.2?JAəЗv} K<] SͺRp;(0ˏTehͷVxx1C5Xg = qLRrHm:!g>+(,i( )X51<9A|i&zblHh^IFgj`{U:oɴѣhڟ KASd u$} 85 )7s&]ݵ2mp5w(fF̍kuz;SI8Lb W&Y7.nzw^ lI(&T҈lW h;.6S]B J<"d`QSKٛaߕM) w>`Q~߃4ʦ_nrz.$>@"o拍y=9sXa+ٳ!Pk$ y9~+8mO t!Qc ̑u%b8 OOZI/b=HUd1a9Z3bS)󠁽T49YKB >D# (Rme8jCNt{X}$=|8M|'sqDD`)֍^{5 }Ӝf qkKPudA}Y@]*?֠DnլA/3GPxJcL"G4C~gX{_3V]z:J~9vx(,XP*Xz^oygbKtZʛ+f7 3GY(FcR&َ\ <ʔ Qj%IvbOA1,BU9HKN?7~xbT/8/ANXx(V,#j0 oC$}z`R002[br I*I-Wg d㋨oatu؄@ 71&6/<:mg !?k7T ~ZI96pFھ[)j +?& "ǖ" rʩo]`1` -{(Ry.ϓP*Ʒ+ؐ _ZRzD&ӂJumBlX|i; ZVvr%r泌|@5eYAX:${p/:LDirdhD2mN:ߠ' :\dFy)/\Djfxe0K.I^aGOO`A6BvqM8>ooo:^i $(„MN" lF  Gg"KDi)ե:Ȏkyu[,%JGM#}G_y%.ReX(eķbnC= s꫄xԯxb|rW5ÏHE& la.4=Rt )%<_gǒn hW5 lW3&p:Mh,T%&Wpg/y TZ΂2MI-6b ؋aùa1J8U썼q`Ud]L3~t:frtJh.kWwɪ QCiiJ)uX%Z7{!'IR^H1 XIU5&DGiڇlfhыm?LtcЗWn_"-q>hA٧Gիrִ^-ŬGD6u$5ۅWvN02iebKAuTz'N\haC"/BNu;ms6l%xD{7i?'bՏ2gHz]\ۣxFI\ ]Dh0v.*䇗*`q)'Qdqd3@AĻԋ;Qxp E+mb=}6UE2?YlQd["A"{uYp + [YlY1t~.4Rej-!|Ex_2M"_U&wc+^`݀Do(?!`H8BGG7,z_M]MP 좂-O\#fP}vѵQ)SUCeE /J&g )m0_KiRbN8֜'wLj/F%ĝa=)3$x4 %U 9Z(JVPn|"bhr PC @ErgeDw0o"C.5V;A,KG=n"vBS63ZNv l5 x z/&M؁rܽ h`OrwfZej*fsOz8:10 ov+ߚB\tx,P(ZJFB]+IjCKJR:g^Q'P;l{3QƯ,䨔+A}e2<&"{+*`^"`0p6?Ajڰb znH0&aOQoAKcԮyRP@=Hp@$)@*9&EbҽӞ]#/)Ajb)/nw ODxV񅏛r=zou'hKVq36 c,Fvܴ*sZ(4^L\;0Z/ b1ا,q:OnI n2֢5La5'QkOБ#Wc:%UZݞEU!czyu$FtWk T!G?;H@fJӇ4s^|G)^-Xe >g(7!z'S#8]O{nm6zh%y3`~/<$r `yzq Z.J_0H(PaY $Qʪ}*9':1HY,Y̳7-#i-&pG 0miMv]1GzPHoę̑(IcPExiTLo{Sya^һi ,GDp3Y9n9Zv`[H/7~e1#\:.xQm1&ܓUp\a,mMWcNZGPH:WjDBBʹ:I)_DXתid>E?Dd뵒D^-n[;\qL[z+(SҞgk$a8lhYN­1J{X^:9:~=Zdj|+?N#?BSμ<[ sr96`VcHIQCgB3p!Y^W^58Nċ/j0;w3+\VͳnȕYe),#KG@ X7ճx403xx޹Tx.|ɫRٝAKGm`4VI) ]Zw7͋a51 I,3-_S6h |#l$8 mn i[&9a"MqXkWpA b08͹b!\8}n[@u*" 71tX~ pizY,\tdS%&1\Gt7Em_$~j Tv*nnKM2gݞ dJ2_% d hu%&wB<¼;W Р%.BAMs+M;hSx3lה3,i,YrJՆ4p`WPiT[+ H/CV$dRu]JхSdO&;( $鐡dppSE R5*!\s'ם4OJFv+bAAPq}yDͥmc^+ڢe(:[C=u=}嫚BQ 2' –A%sD|}t Xzb*C.g( yAǶw'7er=-XCm\2%4n<VMXM o_|QH+,<:ֱ6߃ai ?|n^sU.p{[ \ނg+M,n!qd Ͽ J;Ƭh*Ό! .h[j&+ȓwǾ PqqGuR0p L&Za_$}^%)GoP^aDEh KʪNuKJ5_LU&sU' S]ok.dZ8U_ReRR;k_{|\2DMhۑ~bv[8'(=$[( . nD\α:~-\!E5)NFһLL 2SAf N+37uig/s૝KҠ\_1w/Kt;"Ϩ^wTsh4k\| }+c/sȇ$EL""R\,W[/QOSk -# :p@D{)w9^(G~b\ؠSesҰ nN u:)N0X̜@wPp!&6V;jv㴝fKtB.-T hH&u#*g-PQ?Ex /we n_"BwC 9b,|%݂킷jϻ-wnph~ET9TGZhM|%ϊׂ5x)SޙV0!ָ5O?۵x#j)} }W+̝t3($è&G9N Q=@H>q:1 >N>3\ee |U @EHL4!X'[(W6Uԭ"Hq{#=e,@, Y?! ЎqȦXs )ou>Sd*6Dj/H2dˡ'OˢΙ'>`RT*h*Ro6m&"47fXxeV}₵ EkoܯC3| 3B|HϡsWbd;bmh>l̲t3%JԌI'0`A݂~0yRs Bޯ,Wnt T1ʲcZiA $P`3ųO5F/("+3ieRnl1^:L{:[ݬO@,Ae|`${6 Y2vRwI53# "0k "IM9Uvۘ2_·H Hk2GlbmE($ *uK+Xe$i ,%MH]"Nl{^[LOWj0oۺ+zP9N@ \G=V39)y=}&ɳaVu(=U3eJ :N, T䑇 a\ǹ^%'T7"?l z0}t1 Ӈ콛[$Q  0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 cleartomark {restore}iftests/native/hpdf/testsuite/000077500000000000000000000000001176363201700164665ustar00rootroot00000000000000tests/native/hpdf/testsuite/doc.fal000066400000000000000000000012211176363201700177130ustar00rootroot00000000000000/* * ID: 10a * Category: doc * Subcategory: * Short: smoke test * Description: * Test the HPDFDoc wrapper * [/Description] */ directive strict=on import from hpdf function test_create() def doc = hpdf.Doc() return doc end function test_addPage(doc) def page = doc.addPage() return page end function test_getFont(doc) // valid font def font = doc.getFont("Helvetica") def exceptionThrown = false try def font = doc.getFont("ThisIsAnInvalidFontName") catch exceptionThrown = true end if not exceptionThrown: failure("doc.getFont(\"Helvetic\") didn't throw") end def doc = test_create() test_getFont(doc) success() tests/native/hpdf/testsuite/import.fal000066400000000000000000000002601176363201700204620ustar00rootroot00000000000000/* * ID: 0a * Category: smoke * Subcategory: * Short: smoke test * Description: * Test whether the module can be imported. * [/Description] */ import from hpdf success() tests/native/hpdf/testsuite/page.fal000066400000000000000000000013571176363201700200740ustar00rootroot00000000000000/* * ID: 20a * Category: page * Subcategory: * Short: Test the HPDFPage wrapper */ directive strict=on import from hpdf function test_create(doc) def exceptionThrown = false try def standalonePage = hpdf.Page() catch exceptionThrown = true end if not exceptionThrown: failure("hpdf.Page() didn't throw") def page = doc.addPage() return page end function test_setFontAndSize(doc, page) def font = doc.getFont("Helvetica") page.setFontAndSize(font, 10) end def doc = hpdf.Doc() def page = test_create(doc) test_setFontAndSize(doc, page) def height = page.getHeight() page.setHeight(height) def width = page.getWidth() page.setWidth(width) def lineWidth = page.getLineWidth() page.setLineWidth(lineWidth) success()tests/native/mongodb/000077500000000000000000000000001176363201700151415ustar00rootroot00000000000000tests/native/mongodb/mongodb_test.fal000066400000000000000000000062441176363201700203170ustar00rootroot00000000000000/* Test sample for Falcon MongoDB module Need a running server on localhost:27017 */ import from mongo _n = 0 function Assert( x, msg ) ++_n if not x > @"*** TEST FAILURE ($(_n)) ***" exit( _n ) else > msg end end db = mongo.MongoDB( "olelew00tw00t", 4321 ) inspect( db ) // Getting host & port > "Host = " + db.host() > "Port = " + db.port() // connect failure try db.connect() catch mongo.MongoDBError > "Caught connection failure!" end // Setting host & port db.host( "127.0.0.1" ) db.port( 27017 ) > "Host = " + db.host() > "Port = " + db.port() // connect success db.connect() Assert( db.isConnected(), "Connected!" ) // drop collection if it exists db.dropCollection( "none", "test" ) // drop database db.dropDatabase( "none" ) // authentication failure Assert( not db.authenticate( "none", "shall", "pass" ), "user shall unknown to none!" ) // add user Assert( db.addUser( "none", "shall", "pass" ), "Added user shall to none!" ) // authentication success Assert( db.authenticate( "none", "shall", "pass" ), "shall authenticated!" ) // create an object id > "Creating object id!" oid = mongo.ObjectID() > oid // toString inspect( oid ) // create an empty bson object > "Creating empty bson object!" obj = mongo.BSON() inspect( obj.asDict() ) // create non-empty bson object > "Creating non-empty bson object!" ob = mongo.BSON( [ "key" => "value" ] ) inspect( ob.asDict() ) // append something > "Creating bson object!" curtime = TimeStamp() curtime.currentTime() mem = MemBuf( 3, 1 ) mem.put( 0x00 ).put( 0x01 ).put( 0x02 ) obj.genOID().append([ "key1" => 3, "key2" => 1.1, "key3" => true, "key4" => nil, "key5" => "hello", "key6" => [ 0,1,2 ], "key7" => [ "a"=>0, "b"=>1 ], "key8" => curtime, "key9" => mem ]) // hasKey and value Assert( obj.hasKey( "key4" ), "Has key4!" ) Assert( not obj.hasKey( "foo" ), "Has not key foo!" ) Assert( obj.value( "key4" ) == nil, "Key4 is nil!" ) Assert( obj.value( "key3" ) == true, "Key3 is true!" ) // Turn object back to dict inspect( obj.asDict() ) // Iterate over obj iter = mongo.BSONIter( obj ) while iter.next() > iter.key() + " => " + iter.value() end // insert object Assert( db.insert( "none.test", obj ), "Inserting bson object!" ) // reset object > "Resetting bson object!" obj.reset() // find one > "Find one object!" obj = db.findOne( "none.test" ); inspect( obj.asDict() ) > "Find one with query!" ob = db.findOne( "none.test", mongo.BSON(["key1"=>3]) ) inspect( ob.asDict() ) Assert( db.findOne( "none.test", mongo.BSON(["key1"=>2]) ) == nil, "Find one nothing!" ) // update > "Updating!" db.update( "none.test", mongo.BSON(["key1"=>3]), mongo.BSON(["$set"=>["key1"=>2],"$inc"=>["key2"=>1.2]]) ) Assert( db.findOne( "none.test", mongo.BSON(["key1"=>2]) ) != nil, "Find one updated!" ) // count Assert( db.count( "none", "test" ) == 1, "Count 1!" ) // find res = db.find( "none.test" ) Assert( res.len() == 1, "Find!" ) // remove > "Remove!" db.remove( "none.test", mongo.BSON(["key1"=>2]) ) Assert( db.count( "none", "test" ) == 0, "Count 0!" ) // disconnect db.disconnect() Assert( not db.isConnected(), "Disconnected!" ) > "TEST OK" tests/native/sdl/000077500000000000000000000000001176363201700142765ustar00rootroot00000000000000tests/native/sdl/FalconLogo1.bmp000066400000000000000000005346661176363201700171260ustar00rootroot00000000000000BM6(]  μvvvpppmmmlllkkklllnnnqqqwwwɱlpy]gQcIbEdBe@e@iAp@eBeCdE`I\O[xTYf\\\eeerrrչ|~`mLgAh=kp?q@pBkFeM]TXb]^^kkkѱfsKj>jo>kBhBdB_B\BYAXAZ@]@cAg?l@rAsAtCuDwEuImO`UX]```tttղZpAja=c;c;eT=O}>Q~@YBiDvGzI|KM|PlSZi\\\nnn޻Yr>lep?q?r@r?i?\>P{@TEjJ}ORUUuU]n\\\qqq̝bx@lp>q?q@rAsBuCvExErB_?OwC^N}VY\XvV[e___{{{຺{EnmmNdKc=lq>q>l>a@TEJXQQRddd~~~ڿuTVY\_V@NlRzfh`WZbddd껽Y{lq>q>r?r?s@s@r?e@Q}FGJXXXttt۸iXZ]_^EYIfce_V\gbbb뼿Tyr?r?s@sAtBuCwCq@YCGQTTTrrrZZ\^_LiDY]a[U\l```V~C=nNdHgq>r>r>p=k=e?[BTFKWQQR___sssĀL}DyFzG{H}J~EiBGTUUU}}}ڗVVWVMv?PuRSPxUW^kkkzq>r>r>r>r>r>r>r>r>s>s?s?s>i>YBLdMMN```zzzOFzG|I}JIzAQxKKKnnnӥUSSRJr@S}ONMoVWYttt용=or>r>r>r>r>r>s?r?s?t?s?s?s?s?s?s?t@s?h@TFIPXXXsss{J~G|I}JKC^FGJdddΨSPOOFkAXLJ}LeYYYEtr>r>r>r>s>s>s?s?sMcǐzVEx@u@t@t@t@uAu@o@YEHPXXXxxx^H|H}I~JEiCFN^^^ʥOMLKA_DdH{GxN\}```]r>r>r>r>s>s?s?s?t?t=kIKNnnnəsGz@uAuAuAuAvAp@UHHK___}I}H|H}I}FnAFQ\\\ʕKI}I|H{=SEoExDqRU]nnnr>r>r>s>s>s?s?t?t?t@t@t<`r>r>s>s>s?s?t?t?t@t@u@u@uAvAvAvAs@n@`BVFLZQQQbbbzzziDxBvBwBwCuAZGHJbbbVFzFzFzCnAFQ^^^fExDwDv@i?WBuArLY{bbbkr>s>s>s?s?t?t?t@u@u@uAvAvAvAwAwBwBwBwBwBwBu@hAVFJSUUUnnn^CxBwBwCwBgCHUYYY[EyEyEyBjCFNdddOCvBuBtr>r>s>s>s?t?t?t@u@u@uAvAvAwDrQyL|G{DyCyCxCyCyCyCxCxBsA^CIWRRRoooI|BwBwBwBoAKcSSS[CwCwCw@cEFHnnnCvAtAs@ppH\^^^`r>s>s>s>t?t?t?u@u@u@vAvAwBwBwP_Ͷכ†XEzDzDzDyDyDyCvA]DHPWWW{{{_BxBwBvBr@NpPPP}}}VBvBvBu>YKKK|||n@s?r?qp>kRT[xxxs>s>s>s>t?t?t?u@u@vAvAwBwBxCx>fEEG```uuuȼՒ_F{E{EzDzDyDsATIJKgggzCwBvBvAt@OuPPP~~~L}AuAt@t>NwTTTFx?q>p>p;Q@q=nG]```g=qAfJe=q=q=q=q=r=r=r=r=r=r>s>s>s>t>t?t?u?u@u@vAwAwBxBxCyCz?j?_B[CUGN_NPUYYYggg{{{DZsI~E{E{EzDyCfCGPZZZEyAvAuAr@NrSSSFx@s@s?mAGVccc?q>p>o;c@_=n>iTUX@r>rK\?o=q=q>s=r=r=r=r=r=s>s>s>s>t>t?t?u?u@v@vAwAwBxCyCzDzD{E{F|F}G}G|EvCgBYDJXPPQbbb|||mG|E{E{DzDqAKdRRR}}}EyAuAu@p@KdWWWAu?r?r>cNOQM|=pC>qCY?qs>s>s>t>t?t?u?u@v@vAwAxBxCyCzDzE{E|F}G~G~HIIIIIDmAUEGLWWWtttRF|E{EzDv@PwNNNyyyCwAu@t?mBHUdddi?rE@zUiĔ>pE=oKmUys>s>t>t>t?u?u?u@v@vAwAxByCyCzD{E|F|G}G~HIIJJKKKKKIzB[DGNWWWzzzmG|E{EzDx?RLLLxxxAv@t@s?fWX[QF?uCr浺Jx=mr>pJZ=r=r=r=s@w=s=s=s=s>s>t>t>t?u?u?u@v@vAwAxBxCyCzD{E|F}G~HIJJKKLMMMMMMLIyAUGHJdddG|E{EzDx?QNNN}}}{@tK@{_wܻ릻Lx:iH_eee=r@gHd=r=r=s=s=s=s=s=s>t>t>t>t>u?u?u?v@v@wAxBxByCzD{E|F}G~HIJKLLMNNNOOONNMLFmAFRWWWH}E{DzCv?NuUUU`BzHxus4^9YRRSO|=rF]Al=s=s=s=s=s=s=s>t>t>t>t>u?u?u?v@v@wAwAxByCzD{E|F}G~HIJKLMNOOPPPPPPOONMJ{@LiOOOzzzG|EzCyCtMWn°|(H(F:=EWWW=r>rIY=s=s=s=s=s=s=t>t>t>t>t>u>u?u?v@v@wAwAxByCzD{E|F}F~HIJKLMNOPQQRRRRQQPONNL@R}LLLyyyF|ODIu壦Ě{{{nnnlllkkkfgi0b.a!(8222RRRgggkkkkkkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllnnnwww=s>mH]=s=s=s=s=t=t>t>t>t>u>u>u?u?v?v@w@wAxByBzCzD{E|F~GHJKQobVSSRRQPNML?SMMM}}}rF|OۜnnMMg@@Y==V==V<>W>>W>>W>>W>>W>>W>>W>>W>>W>>W>>W>>W>>W>>W>>W>>W>>W>>W>>W>>W>>W>>W>>X>>X>>X>>X>>X>>X>>X>>X>>X>>X>>X>>X>>X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X??X@?X?@X@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@@Y@AY@AYAAYAAYAAYAAYAAYAAYAAYAAYAAYAAYAAYAAYAAZAAYAAZAAZAAZAAZAAZAAZAAZAAZAAZAAZAAZAAZAAZAAZAAZAAZAAZAAZAAZAAZAAZAAZAAZAAZBBZBBZBBZBBZBBZBBZBBZBBZBBZBBZBBZBBZBBZBBZBBZBBZBBZBBZBBZBBZBBZBBZBBZBBZBBZBB[BBZBB[BB[BB[BB[BB[BB[BB[BB[BB[BB[BB[BC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC[CC\CC[CD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\DD\EE\EE\EE\EE\EE\EE\EE\EE\EE\EE\EE]EE\EE]EE]EE]EE]EE]EE]EE]EE]EE]EE]EE]EE]EE]EE]EE]EE]EE]EE]EE]EE]EE]EE]EE]EE]EE]EF]FF]EF]FF]FF]FF]FF]FF]FF]FF]FF]FF]FF]FF]FF]FF]FF]FF]FF]FF]FF]FF]FF]FF]FF]FF]FF^FF^FF^FF^FF^FF^FF^FF^FF^FF^FF^FF^FF^GF^GF^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG^GG_GG_GG_HG_HG_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HH_HI_IH_II_II_II_II_II_II_II_II_II`II`II`II`II`II`II`II`II`II`II`II`II`II`II`II`II`II`II`II`II`II`II`II`II`II`II`II`II`II`IJ`IJ`JJ`JJ`JJ`JJ`JJ`JJ`JJ`JJ`JJ`JJ`JJ`JJ`JJ`JJ`JJ`JJ`JJ`JJ`JJ`JJ`JJaJJaJJaJJaJJaJJaJJaJJaJJaJJaJJaJJaJJaJJaJJaJJaJJaKJaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaKKaLLbMMcUU[ttt=sAfFg=s=s=t=t=t>t>t>t>u>u>u?u?v?v@w@wAxAxByCzD{E|F}G~HIQೳXSRQPOMK?OtWWW֋   ! !!!!!!!!"!"!"""""""#"###########$$$$$$$$$$$$%%%%%%%%%&&&&&&&&&&&&'''''''''''''((((((((((()))))))*)**********+++++++++++,+,,,,,,,,,,-------------........././/////////0000000000011111111111222222222323333333343334444444445455555555565666666666667777777778788888888898999999999:9::::::::;:::;;;;;;;;;<<<<<<<<<=<==========>>>>>>>>>>>>??????????@@@@@@@@@@@@@@AAAAAAAABAABBBBBBBBBCBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEEEEFEFFFFFFFFGGGGGGGGGGHGHGHHHHHHHIIHIIIIIIIIJIJJJJJJJJJKKKKKKKKKKKKKLLLLLLLLLMMMMMMMMMMMMNNNNNNNNNNNONOOOOOOOOOOPPPPPPPPPPPPPQQQQQQQQQRQRRRRRRRRSSSSSSSSSSSSTTTTTTTTTTTUUUUUUUUUUVUUVVVVVVVVVVWWWWWWWWWWWXXXXXXXXXYXYYYYYYYYYYYZZZZZZZZZZ[[[[[[[[[[[[[\\\\\\\\\]]]]]]]]]]]]]^^^^^^^^^^___________```````````a`aaaaaaaaabbbbbbbbbbbcbccccccccdcddddSS}llli=sD^Bk=t=t=t=t>t>t>u>u>u>u>u?v?v?v@w@wAxAyBzC{D{E}F~GIg_XוURQPNMJ}U_w     !!!!!!!!!!""!""""""""""###########$$$$$$$$$$%$%%%%%%%%&%&%&&&&&&&&&&''''''''''''(((((((()())))))))))**********+**+++++++++,,,,,,,,,,,--,------............./..////////00000000001011111111112122222222223333333333434444444444455555555556666666666777777777777788888888889999999999:9:::::::::;;;;;;;;;;;<;<<<<<<<<=<==========>=>>>>>>>>>>>??????????@@?@@@@@@@@AAAAAAAAAAABBBBBBBBBBCCCCCCCCCCCCDDDDDDDDDDDEEEEEEEEEEEFFFFFFFFFFFFGGGGGGGGGGGHHHHHHHHHHIHIIIIIIIIIJJJJJJJJJJJJKKKKKKKKKKKLKLLLLLLLLLLMMMMMMMMMMNNMNNNNNONOOOOOOOOOOOOOPPPPPPPPPQQQPQQQQQQQQRRRRRRRRRRRRSSSSSSSSSSTTTTTTTTTTTTUUUUUUUUUUUVVVVVVVVVVVWWWWWWWWWWWWXXXXXXXXXXYYYYYYYYYYZZZZZZZZZZZZ[[[[[[[[[[[[\\\\\\\\\]]]]]]]]]]]^]^^^^^^^^^^_^__________``````````aaaaaaaaaaaababbbbbbbbccccccccccccdddd^^xxxP=tG[?p=t=t>t>t>u>u>u>u>u>u>v?v?v?w@w@xAyByBzC{D|F}GHp`Seis]RPPML_  !!!!!!!!"!"!"""""""""#########$#$$$$$$$$%$%$%%%%%%%%%%%&&&&&&&&'&&&'''''''''(((((((((())())))))))*))**********+++++++,,+,,,,,,,,,,,,------------..........//////////000000000000101111111112122222222333333333334444444444455555555565556666666667777777777788888888888889999999999::9::::::::;;;;;;;;;;<;<<<<<<<<<<==========>=>>>>>>>>??>??????????@@@@@@@@@@@A@A@AAAAAAAABBBBBBBBBBCCCCCCCCCCDCDDDDDDDDDDEEEEEEEEEEFFFFFFFFFFFFGFGGGGGGGGGGGGHHHHHHHHHIIIIIIIIIIIJIJJJJJJJJKKKKKKKKKKKKLLLLLLLLLLMLMLMMMMMMMMNMNNNNNNNNNNOOOOOOOOPOPPPOPPPPPPQQQQQQQQQQQRQRRRRRRRRRSRSSSSSSSSTTSTTTTTTTTTUUUUUUUUUUUUVVVVVVVVVVVVWWWWWWWWXWWXXXXXXXXXXXYYYYYYYYYYZZZZZZZZZZZ[[[[[[[[[[[\\\\\\\\\\\\\]]]]]]]]]]^^^^^^^^^^____________`_``````````aaaaaaaaababbbbbbbbbbccccccccccdccddd||?u>uGY=s=t>t>u>u>u>u>u>u>v>v?v?v?w@w@xAxAyBzC{D|E}F~GgmXShhhYQNN`⺾   !!!!!!!!!!""""""""""############$$$$$$$$$$$$%%%%%%%%%%%%&&&&&&&&'&'&''''''('('(((((((()())))))))))*)**********++++++++,+,+,,,,,,,,,-----------.........././//////////0000000000111111111121122222222232233333333344444444444555555555556666666666777777777787788888888899899999999::::::::::::;;;;;;;;;;;;<<<<<<<<<<==========>=>>>>>>>>?>>??????????@@@@@@@@@@@A@AAAAAAAABBBABBBBBBBBCCCCCCCCCCCCDCDDDDDDDDDEEEEEEEEEEEFEFFFFFFFFFFGGGGGGGGGHGHHHHHHHHIIIIIIIIIIIIIIJJJJJJJJJJKKKKKKKKKKKLLLLLLLLLLLLMMMMMMMMMMNNNNNNNNNNNNOOOOOOOOPOPPPPPPPPQPPPQQQQQQQQQQRRRRRRRRRRRRSSSSSSSSSSTTTTTTTTTTTTUTUUUUUUUUVVVVVVVVVVVVVWWWWWWWWWXXXXXXXXXXYXYYYYYYYYYYZZZZZZZZZZ[Z[[[[[[[[[[[\\\\\\\\\\\]]]]]]]]]]]]^^^^^^^^____________`_````````a`a`aaaaaaaaaabbbbbbbbbbcccccccccccddddd>t>tGX=t>u>u>u>u>u>u>u>v>v>v?v?w?w@w@xAyAyBzC{D|E}F~SwʨYVdddRPf  ! !!!!!!!!!!!"""""""""""###########$$$$$$$$$$$$%%%%%%%%%&%%&&&&&&&&&'''''''''''('(((((((((())))))))))**********+*++++++++++,+,,,,,,,,-,---------............././///////00/00000000011111111211222222222323333333333434444444444555555555566666666667667777777777788888888889899999999::::::::::::;;;;;;;;;;<;<<<<<<<<<<=<==========>>>>>>>>>>???????????@?@@@@@@@@@AAAAAAAAAABABBBBBBBBCBCCCCCCCCCCDCDDDDDDDDEDDDEEEEEEEEEFEFFFFFFFFFFFGGGGGGGGGGHHHHHHHHHHHIIIIIIIIIIIIJJJJJJJJJJJKKKKKKKKKKLKLLLLLLLLMLLMMMMMMMMMMMNNNNNNNNNOOOOOOOOOOOPPPPPPPPPPPPQQQQQQQQQQQRRRRRRRRRSRSSSSSSSSSTTTTTTTTTTTUUUUUUUUUUUUVVVVVVVVVVVVWWWWWWWWWWWXXXXXXXXXXXYYYYYYYYYYZYZYZZZZZZZZ[[[[[[[[[[[[\\\\\\\\\\]]]]]]]]]]^^^^^^^^^^^^___________```````````aaaaaaaaaababbbbbbbbbcccccccccccdddddd=t>rGX>u>u>u>u>u>u>v>v>v>v?v?w?w?w@x@xAyAzBzC{D|E}Fz~YW^__zzzZo?d0X=Nij@{>rHZ>u>u>u>u>v>v>v>v>v>v?w?w?w?x@x@yAyAzB{C{D}E~Tn_ZP`SSSzzzńP|=rJ_FDI\>u>u>v>v>v>v>v>v>v>w?w?w?w@x@x@yAyBzB{C|D}DznrzyZVDRmMMNbbbuuu~~~zzzzzz{THK`ҿʹμͺ>u>qHZ>v>v>v>v>v>v>v>v>w?w?w?w?x@x@x@yAzBzB{C|D}GkʸaZZYTSkQeQtPnPiYߙO{;lK`xxxqqqoooqqqsssvvv{{{ӳtttpppppprrrtttxxx~~~ٹvvvpppoooqqqtttwww|||׷uuupppoooqqqtttwww}}}>v>tGX>v>v>v>v>v>w>w>w>w?w?w?x?x@x@y@yAzBzB{C|D}L`ʽzz{fmzZqVYZZZZYVWWUgKx;kN`ffGG==>>GGVV~eeriikeeeaaacccqqqدWW~BB<v>v>v>w>w>w>w>w?w?w?x?x?x@x@y@yAzB{B{C|D|MUgyyyjkk]eyUhPqP~TWXYYj莰ZXV`}dnjxFt;kS`ZZ00NNTTUQQQ^^^}}}飣77<u>w>w>w>w>w>w>w?w?x?x?x?x@y@y@yAzB{B{C|CwEFI^^^`dnXdPeKlJuKPQSTUVWaZV\Zq?n;j[cw%%??_GGGZZZ**EEILLLggg""CCRIII``` %%CCOJJJbbbX>wI^@r>w>w>w>w>w>w?x?x?x?x?x?y@y@y@zAzA{B{C|?oAbEtF}HJKLNOQRSTVsi볿9g:gchsWW>>MM99 33aFFFccc ++GGKK&&>>EOOOuuu&&$$CCMM00 ::NJJJlll&&DDLL,,;;KLLLooor>wEcCm>w>w>w>x>x?x?x?x?x?x?x?y@y@y@zAzA{B{C|>jCvFGHIJLMNPQR[ā2Z7_nnpmm§++::FQQQ|||Ȁ""܋BBB___Ǩ~~Õ>>BWWWǛ䄄Î@@@ZZZ>wBlGh>x>x>x?x?x?x?x?x?x?y?y?y@y@y@zAzA{B{C|>jBuEFGIJKLNOPm׵JZ'G5Pwwwٷүϸ۸ηͶйظӯݺ˩Ȳ''॥}} FFFiiiůŮЭãŴٶ̪ΰ˩TTkkşMM44TQQQͫŲdzӯqqIIӢjj**nKKKtttĮӻiiUUϡ``..cMMMxxxí>x?rJ_?x?x?x?x?x?x?x?y?y?y?y?y@y@z@zAzA{B{B|@pArEEMNIJKMO˛pppZZZ!0V6q6FmΞrrr]]]```{{{đkkk___mmmġmmmfffiiipppxxx||||||zzzyyy|||Пsss^^^bbb|||mmmddddddfffiiilllllliiibbbZZZXXX^^^tttiii]]]fffؾ~~~kkkbbb```dddhhhlllooosssyyyĠlllcccaaadddhhhkkknnnpppvvvǝsss^^^aaa|||đlll___mmmӢvvv___```zzz人ggg]]]kkkڸyyyhhhaaabbbeeeiiilllnnnrrryyyffiiii__\\njsssfffaaabbbeeehhh++>>F]]]ddd]]]oooֳvvvhhhcccdddgggjjjllllllggg___XXXYYYffflll]]]dddjjj]]]hhhܮcccbbbyyyttt___```zzz͜qqq^^^bbb弼hhh```ooorrr___```yyyĻmmm]]]ddd人ggg```XXEEYY``WWwwmmmcccaaacccfffbboHHHqqq潽hhhYYYXXX___jjjuuu{{{}}}}}}}}}~~~zzzjjjddddddgggjjjllllllhhh```YYYXXXbbb~~~rrr^^^aaa|||đlll___``w@@VV``ZZiipppddd```bbbeeehhhDDDfffұvvvgggbbbcccgggjjjmmmooottt}}}ɤpppeeeccceeeiiikkkmmmjjjddd\\\WWW\\\``DDZZbbYYnnoooddd```bbbfffffjEEEjjj״uuuhhhgggmmmtttzzz||||||yyyzzz?x?xL]?w?x?x?x?y?y?y?y?y?y?y@y@z@z@zAzA{B{B|Bx?mD~IZFHIJVOOCC11!!B+Z9@QRRDD22""DDRcccMM@@..00SSSzzzRR..##--==OOggxxttufffcccpppSSEE33""EEScccɎNN,, **EEaa11??WAA++00))##MMMnnnºKK** ++;;RRff~kkkbbb___jjjƒRR--))88MMaahhk```]]]fff}}}CC??11""DDScccNN@@..00SSS}}}VVFF44##BBWaaaKK==**++QQQyyy㼼{{BB''""..>>TTffxfff^^^^^^lllѶ||BB&&''33++//hTTT??7744''22yUUUߴrr==$$##33QQXX""""LLLSS3366,,KKKlllMM??--%%NNNrrrppII88''FFSXXh1166//""AAXaaaOOCC11!!IIKfffLL==++88oPP_,,22--""AAX```ll>>==.. KKKlllLL==**99kYYYaa44!!"",,99 CCDfffLL>>++"")),,........99șooAA''!!..KK__''HHJZZq2277//!!DDScccNN@@..00SSS{{{mm;;##!!))66::O]]]̮mm;;##$$22CCZZhhsddd^^^aaasssΜXX00 ''??]]>>55kXXX§jj88""!!**77>>I```ַqq<<$$((55FF\\rryylllbbbhhh~~~_?xHcCo?y?y?y?y?y?y?y?y?y?y@z@z@z@zAzA{A{B|B{=fD~E~EFHIf U$J@BF22gVVVIIIjjjNN  GG__eaaauuu33gVVVVV++v;;pAAJ```FFBBWW`TTTbbbXX 77QQcNNNWWWppw 22hVVVIIInnn!!..rTTTGGGkkk77 BBQQVOOO___88 NNN//JJJqqq**CCCMMt>>O^^^DDEdddYY11nRRV --sTTT 77\YYYJJN ..sTTThh>>O^^^!!NNNtttuu::S^^^쫫ˢ2277XUU\22hVVVIIIkkk&&--oUUU&& **LL~QQRRRRgggnnLLLsss""22dXXX##55YYy___iii?yCoJg?y?y?y?y?y?y?y?z?z@z@z@z@z@zA{A{A{B|B|=hD}D~EFG11 { 9GGG@@##NNN|||DDF^^^66""44**ee{{{@@$$NNN|||==FFN66]VVVwwKKx```vvv77 33~FFFYYY##NNN{{{DDFcccQQMMMxxx@@H```??\JJJcccttIIICC DDGfff 88VSS\ 00iUUU::R[[[""MMM LLLwww22((QQQFFGLLLppy00iUUUHHHgggNN..mVVV''66@@DDEEEEEEDD**xQQQ##NNN{{{DDF___``!!NNNyyytt !!DDMOOOoooPPFFFfff__%%QQQ~~~ ..44DDuuu@z@xN^?z?z?z?z?z?z@z@z@z@z@z@z@z@z@{A{A{A|B|?mBwD~E~Obb +MMMuuHHHppp3399XMMa&&^^dµLL؃vvHHHppp$$]]ѣwwDDD''NNN11 BBǤ䕕FFqqhh @@{{¡ᐐ<<//sJJJ--HHHooo5599YZZZGGGmmm44aWWWLLTTͣ|| ==PQQQTTͤ,, DDFVVt<gC}Xص!"&PPP DDDeeehh,,x==j==APPPmmm))TT DDDeeeYY))XXX<<??G!!III JJ\}}}||??ؼRRFFUttt DDE<< CCDeeejj,,yRRRCCCbbb66&&PPP}}}TTV@@IIITTU==O]]] //lTTTBBSjjj JJJ//HHHoooXXLLLvvvBBICCBBCbbb EEFhhhBB66_<>QQR``55]==u..y___JJGGGjjj''%%}FFF___{{{ll}}~@{AtNd@{@{@{@{@{@{@{@{@{@{@{@{@{@{A{A{A{A|B|?jCXuvx&&7QQQ::R[[[::d66EBBBUUUhhhvvv{{{}}}ppbb[[QQ}HHtPPdeee;;R\\\qq22iQQQ 44_00 DDELLLvvvllGGGlll3388\IIg::S[[[LLLuuu88WYYYkkIIIqqq..$$OOO~~~ DDE&&OOO{{{33cVVV!!!!MMMxxx CCEaaaEEFAACCCdddFFGjjj55::XOO`77XXXX>>K^^^yy((GGX88XYYYYYCCCdddll++{==k??I^^^ EEEhhh{{JJJrrr??==OYYYAAEOOm::S[[[FFQ44aUUU>>KPPk77YYYYJJJCC$$LLLttt##''HHW88UXXXAAI```&&===KKK___pppyyy|||{{~ee__VVLLwHHo]]^uuuHA{NbCuA{A{A{A{A{A{@{@{@{@{@{@{A{A{A{A{A|B|B{>JPPj;;OYYY??H___kk++{PPR))|PPP||| 22dTTTHHK)){QQQmm;;OYYYFFR22eUUU@@F___ EEGhhhtt))PPP}}}44_SSX --pRRRFFF%%NNNzzz22dUUV,,uRRR FFFRRxGGGgggeeHHH)){PPP}}} 77ZXXX// ))3399<<<<;;8800&&!!;;h```A|DrNhA}A|A|A|DDA|A|A|A|A|A{A{A{A{A{A|A|B|?lBCHkkk..JJJvvvMMIIIoooBBGFFOoooNN LLLvvvFFFccceeEEL11e::wAAH]]]ʦ~~99UVVVMMMIIInnnDDFeee__KKKttt<>M___JJJkkk55%%NNN IIInnnCCF&&GGGiii99%%NNN##KKKvvv??M[[_ @@MZZZ CCC!!IIIkkk))))zQQQ>>%%YYYEB}OdDwB}B}D]JA}A}A|A|A|A|A|A|A|A|A|A|A|B|>DDWMMMaaa}}}22GGr???SSSsss11==tOOO^^^yyy==H%%GGG11LLLZZZuuu ggg11""KKKVVVoooYYgaa::CHHHbbbTT33ttt==GUUU{{{>>>>>OOOlll 99pLLL]]]yyyhhl 88xLLL\\\www==L^^^!!@@@SSSqqqLLOSSShhh??HHH55 ==?MMMiii<<EEETTTmmmWWeii66LGGG```ii AAFOOOfff==AA|^^@@K```HH ==?LLLiii33GGsNN KKLQQQfffIIJJJrrrTT%%PPP~~~ LLXQQQccc`` CCFEE::CHHHbbbTT33__DD]NNNaaa|||jj EEEJJAAHaaa%%PPP66//NNNZZZttt 00g@@pFFWOOObbb~~~__FFFffo &&JJJVVVppphhhGDNhHuDDDCCCC~B~B}B}B}A|A|A|A|A|A|A|A|@tqLdwww/U55ZWWW <<=JJJ]]]nnnwwwxxxKK88,,YYZYYYbbbnnnvvvvvw&&\\44PDDDWWWlllzzzII__d]]]bbbmmmuuuxxxLL44m88 CCD&&YYeYYY___jjjssswwwiiفjj RRqYYY]]]hhhrrrwwwvvwuu##DDDPPPbbbpppxxxxxxKK8811[FFF]]]vvvZZ44^GGGVVVfffsssxxxvvvqqqppp""ZZ^YYY```kkktttxxxcc͋;;ZZcZZZ___jjjtttxxxkk66k\\\**99RIIIXXXhhhtttxxxuuuqqqppqBB]]^^^^fffqqqwwwwww JJLKK00iFFFTTTeeerrrxxxwww""FFpYYY]]]gggqqqwwwvvwmm CCDOOOaaapppwwwxxxBB44XXX\\\dddooovvvwww<<JJ99g]]]dd00jFFFTTTeeerrryyyxxx44QQ<<[[[[[[dddooovvvwwwCC KKMnnnJJJsss]]22__a]]]dddooovvvwww44BBSXXk##DDDPPPbbbpppxxxxxxKK88))[[]ZZZaaammmuuuxxxZZDDNZZl::e___PPPTT\\n\\\```jjjtttxxxbb&&QQX //[[\ZZZbbbmmmvvvxxxRRFFMgggCC SSkXXX]]]iiissswww\\%%FF|TeEEEEDDCCC~B~B}B}A}A|A|A|A|A{A{A{@p=DTSSSַs@r?d^^^f~=pBg|}}Lu7e''~OOO}}}))<<`VV[ddh^^wCC bb11^^jjphhnZZ{55ꌌ**//i<>>OOOeeevvv|||{{{xxxxxx^^44YYniikeesOO**HHhhjXXQQhhwllncctKKDD̶ NNhhyllnbbvJJ44lllLL ;;\\mjjldduJJ**NNiijLL88ccoosjjrYY//UUlpp{11UUphhljjrZZ22??ddmmqhhqXX.. 矟33$$MMuddlkko``{@@//]]mmsjjo^^z==||1166kkk11UUphhkjjp]]}::턄bb00[[kkriioZZ~;; YYhyyy FFFhhh,,^^nnvllq^^|::EEnnq''OOtffkkkoaayCC bb$$VVjjukkn^^yCCHH}qqq99mmm ccc}},,OOkk}nnqeeuMMfffII((XXjjtjjo]]z@@MMvuuuNNggwjjmccsKKDDĮ]GJrQoFFFEEDDCCB~B}B}B}A|A|A|A{A{A{A{@rr:RABDLXu@n=pFes;l=kHHHqqq[[$$ww@@\\==ܢ--̐..oTTT\\쓓**~~[[##ll44QQmm||ooocccVVlUUee 鰰22ɕ``YYggnnkk==jj11Ê!!EE[[ll ??O___JJ@@##OOII77Ɩ--텅77ߒ__YY렠KIQhLzHGGFEEDDCC~B~B}A}A|A|A|A{A{A{A{Aws>r>r=n=k=p=p=pRaꝩ>m;lczCC CCCeeeDD77>>xx?? MMMxxx]]풒쌌BB //MMPPS 퍍VV ..뺺cc 죣丸KK ffSSNN99턄33QQ [[ޟLL\\::oo,,44dWWWdd>>}}BBMM GGˉ##__펎))㸸EETTjj압KJUgKIHHGFEEDCC~B}B}A|A|A|A{A{A{A{AzAz>h>IcHIJ^^^yyyȻjrQiCe@l>p>t>s>s>r=r=q=q=q=p=pakﺿHr;lIo˪;;L[[[44hhYY6600__55bOOr{{QQ22VV**{{HHHmmmyyLL..%%KKRR22!!HH55PP!!33ff 0077 mmmm88!!GGppEE))++YYvvHH,,%%__..::~~ff00%%MMllCC''22ppuuSSNN~~JJ ==rrZZ55##JJ++WW((22aaee>>""==ss ㈈pp PP}}EE<f>P|BELOOO]]]jjjvvv}}}}}}vvvijlUeDj?t?u?u>t>t>s>s>r=r=r=q=pLdLq=potWy:ji=]@YBWCVDXD[C_@e?r?v?u?u>u>t>t>s>s>s=r=r=q=q;UGIOAj>l܉h:j8ejyú IIIkkk**''QQQ~~~55&&MMMppp::V[[[EE""KKKnnn**EEqq\\hhHHHkkkUQQrWvMLKIHGFEDCB~B}A|A|A{@{@z@z@z@y@y@y@y@y@x?x?x?x?w?w?w?v?v?v?u>u>u>t>t>t>s>s=r=r=r=q=r9X=hu>u>u>t>t>t>s>s=s=r=r=q=q=q=p=p>o臏y>nv>u>u>u>t>t>t>s>s=s=r=r=r=q=q=q=pu@cJLYYSSGGAA??""44::<<>>>>??CC牉77;;==>>????????????yy$$Ԙ8877##^^oo[[WTRrXtNLJIGFECC~B}A|A{@{@z@z?y?y?y?x?x?x?x?w?w?w?v>v>v>v>u>u>u>t>t>t>s=s=r=r=r=q=q=q=pw>v>v>v>u>u>u>t>t>t=s=s=s=r=r=r=q=q=q=pDr➤ܱ`z=nw>w>v>v>v>u>u>u>u>t>t=s=s=s=s=r=r=qu>p퍙ΛQrw>w>w>v>v>v>u>u>u>t>t=t=s=s=s=r=r=r=q=q>w>qӸ~Dmw>w>w>v>v>v>u>u>u>t>t=t=t=s=s=s=r=r=r=q=q@q톖½\s=mw>w>w>v>v>v>u>u>u>t>t=t=s=s=s=r=r=r=q=qGt䍙ozElw>w>w>v>v>v>u>u>u>t=t=t=s=s=s=r=r=r=qQvыtzMk=ow>w>w>v>v>v>u>u>u>t=t=t=s=s=s=r=r=r?p[qxxzhmzLh=nw>w>w>v>v>v>u>u>u>t=t=t=s=s=s=r>pIiY`rT_yEe=ow>w>w>v>v>v>u>u>u=t=t=t=rAnFgJ]LYwF`>i>q=qk=r=q=q@ra詺hE}?y?x?x?x?u?p@kAhChDfDgCf@f?h>l>q>s=s=r=r?sW쐩_By>w>w>v>v>vHK>u=t=t=s=s>sVwtests/native/sdl/LaunchMe000077500000000000000000000115751176363201700157310ustar00rootroot00000000000000#!/usr/bin/env sdl_falcon /* FALCON - SDL MODULE Samples FILE: sdl_events.fal Show minimal management of events. This small program also shows how Falcon coroutines and SDL events can interact. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 16 Mar 2008 21:15:18 +0100 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. In order to use this file in its compiled form, this source or part of it you have to read, understand and accept the conditions that are stated in the LICENSE file that comes boundled with this package. */ load sdlmixer load sdlttf load sdlimage //const prefix="/Volumes/Falcon SDL binding module/Resources/" const prefix="./" object MyEventManager quitRequest = Semaphore() surf1 = SDLRect( 0,20,640,80) surf2 = SDLRect( 0,90,640,150) subs = [ "sdl_MouseMotion", "sdl_Quit", "sdl_Expose", "UserEvent" ] init for event in self.subs: subscribe( event, self ) end function on_sdl_MouseMotion( state, x, y, xrel, yrel ) static saved = SDL.CreateRGBSurface( SDL.HWSURFACE, 640, 60, 32 ) global font, screen, color screen.BlitSurface( self.surf1, saved, nil ) end color.r, color.g, color.b = 20, 240, 240 saved.BlitSurface( nil, screen, self.surf1 ) dataImage = font.Render_Blended( @ "X=$x, Y=$y, XRel=$xrel, YRel=$yrel", color ) dataImage.BlitSurface( nil, screen, self.surf1 ) screen.UpdateRect() >> "X=",x, " Y=",y, " Xrel=", xrel, " YRel=",yrel, " \r" end function on_sdl_Quit( state, x, y, xrel, yrel ) > strReplicate( " ", 60 ) > "== Quit! ==" self.quitRequest.post(2) end function on_sdl_Expose() global screen screen.UpdateRect() end function on_UserEvent( code, item ) static saved = SDL.CreateRGBSurface( SDL.HWSURFACE, 640, 60, 32 ) global font, screen, color screen.BlitSurface( self.surf2, saved, nil ) end saved.BlitSurface( nil, screen, self.surf2 ) if item dataImage = font.Render_Blended( item.toString(), color ) dataImage.BlitSurface( nil, screen, self.surf2 ) end screen.UpdateRect() end end //============================================== // Coroutine to display a blinking cursor // function blinker( screen ) r = SDLRect( 280, 200, 80, 80 ) black = screen.MapRGBA( 0, 0, 0, 0 ) white = screen.MapRGBA( 255, 255, 255, 255 ) color = white time = 0 send = "Reset..." while not MyEventManager.quitRequest.wait( 0.5 ) color = (color == white ? black : white ) screen.FillRect( r, color ) screen.UpdateRect( r ) time ++ if time % 6 == 0 send = send ? "" : "Time ticks " + time broadcast( "UserEvent", 0, send ) end end end //============================================== // Main program // try autoSDL = SDL.InitAuto( SDL.INIT_VIDEO ) SDL.LoadBMP("falcon-ico.bmp").SetIcon() SDL.WM_SetCaption( "Falcon SDL Event Handler Test" ) screen = SDL.SetVideoMode( 640, 480 ) // draw a cloured band - first hardware try band = SDL.CreateRGBSurface( SDL.HWSURFACE, 640, 1, 32 ) catch SDLError > "Hardware surface creation failed, try with software creation." // then software -- let it fail if it fails band = SDL.CreateRGBSurface( SDL.SWSURFACE, 640, 1, 32 ) end launch blinker( screen ) // MUSIC ==================== MIX.OpenAudio( 44100, AUDIO.S16, 2, 4096 ) music = MIX.LoadMUS( prefix + "fading.ogg" ) music.Play( loops| -1, fadeIn| 6 ) // FONT ====================== autoFont = TTF.InitAuto() font = TTF.OpenFont( prefix + "Vera.ttf", 24) font.SetFontStyle( TTF.STYLE_BOLD || TTF.STYLE_ITALIC ) color = SDLColor( 20, 240, 240) color.r, color.g, color.b = 20, 240, 240 screen.UpdateRect() pixels = band.pixels for bid in [0:640] pixels[bid] = band.MapRGBA( 150, bid%128, (bid+128)%256, 250 ) end rect = SDLRect( 0,0,640,1) for y in [0:480] rect.y = y band.BlitSurface( nil, screen, rect ) end screen.UpdateRect() // make a solid rectangle // make a solid rectangle launch blinker( screen ) > "Trying to load a BMP image:\n" img = IMAGE.Load( prefix + "FalconLogo1.bmp" ) img.BlitSurface( nil, screen, SDLRect(0, 390, 640, 480 ) ) screen.UpdateRect() > "Success! - Now start moving the mouse." > "Click the X on the bar to quit the app." SDL.StartEvents() count = 0 while true if MyEventManager.quitRequest.wait(1): break >> count++, "\r" end SDL.StopEvents() > "Complete." catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/Vera.ttf000066400000000000000000002006141176363201700157150ustar00rootroot00000000000000OS/2_cpVPCLTъ^6cmaplXcvt 9fpgm&`gaspH glyf tA&~hdmx4!Hhead݄T6hheaEoL$hmtx Ǝ0kernRՙ-loca=maxpG:, nameټȵpostZ/prep; h::_:: dM0l   p t  &   Y &  &   c . 5 `  s 0 & {Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera SansBitstreamVeraSans-RomanRelease 1.10Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.comCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera SansBitstreamVeraSans-RomanRelease 1.10Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.com5fqu-J3T99NR7s`s3VV9s3D{o{RoHT3fs +b-{T#\q#H99`#fy```{w``b{{Rffw;{J/}oo5jo{-{T7fD)fs@%2%%A:B2SAS//2ݖ}ٻ֊A}G}G͖2ƅ%]%]@@%d%d%A2dA  d   A(]%]@%..%A  %d%@~}}~}}|d{T{%zyxw v utsrqponl!kjBjSih}gBfedcba:`^ ][ZYX YX WW2VUTUBTSSRQJQP ONMNMLKJKJIJI IH GFEDC-CBAK@?>=>=<=<; <@; :987876765 65 43 21 21 0/ 0 / .- .- ,2+*%+d*)*%)('%(A'%&% &% $#"!! d d BBBdB-B}d       -d@--d++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++, %Id@QX Y!-,%Id@QX Y!-,  P y PXY%%# P y PXY%-,KPX EDY!-,%E`D-,KSX%%EDY!!-,ED-ff@ /10!%!!fsr)5 @@ <2991/0K TX @ 878Y P ]%3#3#5qeM@1<20KTKT[X@878Y@0 @ P ` p ]#!#o$++`@1      91/<<<<<<<2220@   ]!! !3!!!!#!#!5!!5!T%Dh$ig8R>hggh`TifaabbNm!(/@U" '&( /)/))/B" ) *!#*- ) " & 0<<<1/299990KSX99Y"K TX0@00878YK TKT[KT[X000@878Y#.'5.546753.'>54&dijfod]SS\dtzq{---@A$*.U# jXV`OnZXhq) #'3@6$%&%&'$'B .$ &($4'!%   ! + 1 49912<0KSXY"K TK T[K T[KT[KT[K T[X4@44878Y"32654&'2#"&546"32654&%3#2#"&546WccWUccUVcbWWcd1Zܻۻa ۻۼ 0@      !         B  (('+'$ .  .'.'!!199999991/9990KSX99999999Y"2]@ " ) **&:4D ^YZ UZZY0g{ "-  ' (   2'') #**(/2; 49?2J LKFO2VZ Y UY\_2j i`2uy z 2229]]3267 >73#'#"5467.54632.#"[UԠ_I{;B h]hΆ02޸SUWDi;#QX?@Yr~YW׀c?}<$$/1oX3goB@ 10KTKT[X@878Y@ @P`p]#o+{ O@  29910KTX@878YKTX@878Y#&547{>;o @ <99103#654<:=JN@,       <2<2991<22990%#'%%73%g:r:g:PrPbybcy #@   <<1/<<0!!#!5!-Ө-Ӫ--@ 1073#ӤR@d10!!d1/073#B-@B/9910KSXY"3#m #@  10"32'2#"  P3343ssyzZ K@B  1/20KSXY"KTX  @878Y]7!5%3!!JeJsHHժJ@'B   91/20KSX9Y"KTKT[KT[X@878Y@2UVVzzvtvust]]%!!567>54&#"5>32Ls3aM_xzXE[w:mIwBC12\ps({@.    #)&  )99190KTKT[X)@))878Y@ daa d!]!"&'532654&+532654&#"5>32?^jTmǹSrsY %Đ%%12wps{$& Ѳ|d @   B    <291/<290KSXY"K TK T[X@878Y@* *HYiw+&+6NO O Vfuz ]] !33##!55^%3`du@#    190KTKT[X@878YKTX@878Y!!>32!"&'532654&#",X,$^hZkʭQTժ 10$& $X@$  "% " !%190@]]"32654&.#">32# !2 LL;kPL;y$&W]ybhc@B991/0KSXY"KTX@878Y@X9Hg]]!#!3V+ #/C@% '-'0 $*$ !0991990"32654&%&&54632#"$54632654&#"HŚV г "Əُattt$X@# %!"" %190@]]7532#"543 !"&2654&#"LK:lL>$& V\s[#@<21/073#3### %@  <2103#3#ӤR#٬@^M@*B$#29190KSXY" 5Ѧ`@ #<210!!!!^O@+B$#<9190KSXY"55//m$p@+$     &%99991/9990K TX%@%%878Yy z z ]%3##546?>54&#"5>32ſ8ZZ93lOa^gHZX/'eVY5^1YnFC98ŸLVV/5<4q L@2  L4307$7CM34( (+(I+*(I,=M<9912990K TK T[KT[KT[KT[XMMM@878Y@ NN/N?N]32654&#"#"&5463253>54&'&$#"3267#"$'&5476$32|{zy!orqp ˘s'6@   0210].# !267# !2'ffjzSb_^^_HHghG.@   2 99991/0`]3 !%! )5BhPa/w.,~ .@   21/0 ]!!!!!!9>ժF# )@ 21/0 ]!!!!#ZpPժH7s9@ 43 1990%!5!# !2&&# !26uu^opkSUmnHF_`%; ,@ 8  221/<20P ]3!3#!#"d+991/0KTX@878Y@ 0@P`]3#+f M@  9 991990KTX  @878Y@ 0 @ P ` ]3+53265M?nj @(B  291/<290KSXY"]@ ((764GFCUgvw    (+*66650 A@E@@@ b`hgwp  ,]q]q3! !#3wH1j%@ :1/0@ 0P]3!!_ժ @4  B    >  91/<290KSXY"p]@V   && & 45 i|{y   #,'( 4<VY ej vy ]]! !###-}-+3 y@B6 991/<2990KSXY" ]@068HGif FIWXeiy ]]!3!#j+s #@  310"32' ! ':xyLHH[[bb:@   ? 291/0@ ?_]32654&#%!2+#8/ϒs R@*  B     39991990KSX9Y""32#'# ! '? !#y;:xLHHab[T@5  B    ?  299991/<9990KSX9Y"@]@Bz%%%&'&&& 66FFhuuw]]#.+#! 32654&#A{>ٿJx~hb؍O'~@<    B %( "-"(9999190KSX99Y")])/)O)].#"!"&'532654&/.54$32Hs_wzj{r{i76vce+ٶ0/EF~n|-&J@@@1/20K TX@878Y@  @ p ]!!#!ժ+)K@   8A1299990KTX@878Y]332653! ˮ®u\*$h@'B91/290KSXY"P]@b*GGZ} *&&))% 833<<7HEEIIGYVfiizvvyyu)]]!3 3J+D {@I      B     91/<2290KSXY"]@  ($ >>4 0 LMB @ Yjkg ` {|      !   # $ %  <:5306 9 ? 0FFJ@E@BBB@@ D M @@XVY Pfgab```d d d wv{xwtyywpx   []]3 3 3# #D:9:9+=; ]@F      B    91/<290KSXY"K TK T[KT[X  @878Y@ '' 486 KX[fkww       &()&(' ) 54<;:;4 4 8 ? H O X _ eejjhiil l xyyx}  x   @]]3 3 # #su \Y+3{@(B@@ 91/290KSXY" ]@<5000F@@@QQQe &)78@ ghxp ]]3 3#f9\ @BB 991/0KSXY"K TK T[X @ 878Y@@ )&8HGH    / 59? GJO UYfio wx ]]!!!5!sP=g՚oXS@C210K TX@878YKTKT[X@878Y!#3!XB-@B/9910KSXY"#mo<@C<10KTKT[X@878Y!53#5oXޏ@ 91290##HHu-10!5f1@ D10K TKT[X@878Y #ofv{-{ %@'   #   E&22991/9990@n0000 0!0"?'@@@@ @!@"PPPP P!P"P'p' !"'''000 0!@@@ @!PPP P!``` `!ppp p! !]]"326=7#5#"&5463!54&#"5>32߬o?`TeZ3f{bsٴ)Lfa..'' 8@  G F221/0`]4&#"326>32#"&'#3姒:{{:/Rdaadq{?@  HE210@ ].#"3267#"!2NPƳPNM]-U5++++$$>:#qZ8@G E221/0`]3#5#"3232654&#":||ǧ^daDDaq{p@$   KE9190@)?p?????,// , ooooo ]q]!3267# 32.#" ͷjbck)^Z44*,8 Cė/p@     L<<991/22990K TX@878YKTX@878Y@P]#"!!##535463cM/ѹPhc/яNqVZ{ (J@#  &#' & G E)221/990`***]4&#"326!"&'5326=#"3253aQQR9||9=,*[cb::bcd4@  N  F21/<90`]#4&#"#3>32d||Bu\edy+@F<21/0@  @ P ` p ]3#3#`Vy D@   O  F<2991990@ @P`p]3+532653#F1iL`a( @)B F 291/<90KSXY" ]@_ ')+Vfgsw    ('(++@ h` ]q]33 ##%kǹi#y"F1/0@ @P`p]3#{"Z@&   PPF#291/<<<290@0$P$p$$$$$$$ ]>32#4&#"#4&#"#3>32)Erurw?yz|v\`gb|d{6@  N  F21/<90`]#4&#"#3>32d||Bu\`edqu{ J@  QE10@#?{{   {  {]"32654&'2#"s98V{>@ GF2210@ `]%#3>32#"&4&#"326s:{{8 daaqVZ{ >@   GE2210@ `]32654&#"#"3253#/s:||:/daDDadJ{0@    F21/90P].#"#3>32JI,:.˾`fco{'@<  S  SB %( R"E(9999190KSX99Y"']@m   . , , , ; ; ; ; $( ( *//*(() )!$'      '/)?)_))))))]]q.#"#"&'532654&/.54632NZb?ĥZlfae@f?((TT@I!*##55YQKP%$78@  F<<2991/<2990]!!;#"&5#53w{KsբN`>X`6@    NF21/290`]332653#5#"&||Cua{fc=`@'B91/290KSXY"K TX@878YKTKT[X@878Y@Hj{  &&)) 55::0FFIIFH@VVYYPffiigh`ut{{uz>]]3 3#=^^\`TV5` @IU U U U   B     91/<2290KSXY"K TKT[KT[KT[K T[X  @878YK TK T[KT[X @ 878Y@" 5 IIF @ [[U P nnf yy          %%#'!%""%' $ ! # 9669 0FHF@B@@@D D D @@VVVPQRRPS T U cdejejjjn a g ouuy}x}zzxy  { v } @/   y]]333# #V`jjj;y` Z@F      B   91/<290KSXY"K TKT[KT[KT[X  @878YKTX @ 878Y@   & =1 UWX f vzvt        )&% * :9746 9 0 IFE J @ YVYYWVYVV Y P o x  /]] # # 3 dkr))`HJq=V`@C        B     9129990KSX2Y"K TKT[X@878YKTX@878Y@     # 5 I O N Z Z j        '$$  )( % $ $ ' ** 755008 6 6 8 990A@@@@@@@@B E G II@TQQUPPVUVW W U U YYPffh ii`{xx   e]]+5326?3 3N|lLT3!;^^hzHTNlX` @B 2991/0KSXY"K TK T[X @ 878YKTX  @878Y@B&GI  + 690 @@E@@CWY_ ``f``b ]]!!!5!qjL}e`ۓ%$@4 %   !  % $  C %<<29999999199999990K TX%%%@878Y&]#"&=4&+5326=46;#"3>l==k>DV[noZVtsݓXX10#$@6%   #%#C %<2<9999999199999990K TX%@%%878YKTX%%%@878Y&]326=467.=4&+532;#"+FUZooZUF?l>>l?VWstݔ1#@  1990#"'&'&'&#"56632326ian ^Xbian ^V1OD;>MSOE<>LhN'$uhm !@T   !!  ! !!!B     !  VV!"2299999991/<9990KSXY" #]@  s P#f iu {yyv v!# ]]4&#"326!.54632#!#TY?@WX??Y!X=>sr?<҈_Z?YWA?XXN)sIsrFv)su''&-k'(u3^'1usN'2'u)N'8u{-f'DR{-f'DCR{-f'DR{-'DR{-7'DR{-'DRqu{'Fqf'Hqf'HCqf'Hq'Hof'f'C\f'F'd7'Qquf'Rsquf'RCsquf'Rsqu'Rsqu7'RsXf'X{Xf'XC{Xf'X{X'X{9; '@  YW Y <<1<203!!#!5!oo\]u=  @  Z[Z10"32654&'2#"&546PnnPPnoO@v+..ooPOmmOOp1.-rB#!Q@+     "  "<<<221<9990%&&'667#&73JDFHAMf fIX⸹)**'# 32!b`@!    <<1/2<2990K TX@878Y66].#"!!!!53#535632NL=ty-=))׏/я\= >@54&.#"#"&'532654/.5467.54632{?>?>S8alӃ\]>9̭IXW:fqր][;;ȦI.Z.L-[.K''PGZsweZ54m@''TLf{xf[1,pE3!   \ 104632#"&3~|}}||};9 %@]] 91290!###&&54$yfNݸ/@0-'!  **.  !' $'$-F099991/990@@'(     ! "&  : :!MM I!I"jj  ]]4632#"&'532654&/.5467.#"#:A9`@IPAtx;e\`Wqqs`/Q*%jd_[?T>7;[gp/8L`@6EBC?2H09JC 9 $HE301BKL?gwyVpMI`3D/IC@&=>:A$104G$ 7aD=0^* D^ J21/02#"$'&5476$"32676654&'&&&&#"3267#"&54632mmllmmmmllmm^^``^^⃄^]]^\^BB@zBCFInmmmmnnmmmmng^^^傁^^__^]⃅]^^! "'F >@!    b b cbc91<<2<<903#######5Jq7rqr/B^^sRf1@ D10K TKT[X@878Y3#fF)@dd1<20K TK T[X@878YK TK T[KT[KT[X@878YKTKT[X@878Y@````pppp]3#%3#^y'>@"     <291<2<<990!!!!!'7!5!7!}/H{};fըfӪH@9  B     <291/<0KSXY"]@gww  ]!!!!!!#!59=qժF՞f +@< +,  )&  *&& &,+,* # )#3,99999999199999990@*WZWU!je!{vu! FYVjddj(|svz( ]] 324&'.#"&5!27!"&''3>_'y=_''NOy;WfNPƀ[gX@CHp@CpDfbMKYg[KKX /@- !$'!!0 $*0999919990@     $$$   $$ $ ***///***55500055 5 :::???:::EEE@@@EE E JJJOOOJJJV !"&'()]]32654&#".#"326#"&54632>32#"&1TevYR1UfvYRF^_HDa^/XZie7XXjeߦ~᧯w .@     <2<21/<<0!!#!5!!!-Ө-}} T@.B $# <2291/90KSXY" 5!!@po V@/B$ # <<291/90KSXY"55!5AǪR@F  B     fe f e<2299991/2<2<290KSXY"K TX@878Y@(' ' ')((79  ]]!#!5!5'!5!3 3!!!c`Tþ{yT9{3{JD{3V` M@%  !   NF!2912<990"`""]3326533267#"&'#"&'#% )I#ER2bf*V H<9 NPOONNh-)b@'! '!* $$*9991990K TK T[KT[KT[KT[X*@**878Y>54&#"#"&54632#"&54324&#"32IH7$$0e՘ݢe WOmVPmmWKt,>bFأ[t}t{w; ]@    91990@0QVPZ spvupz  Z pp{ t  ]]!! !!5 7AJI3!wq@gg120!#!# }/#@1 " $ #" #h#$9999991/<229990K TX$$$@878Y@V             ##(]]#3267#"&5467!##"#>3!i/7.%7vy"Pµ)6< yJ\:1fd.xo@E}/%&@  & iji&1026732#"&'&&#"#"&546327j Pd@7*8  kOeD=!0 l9TA6?&#Hn!bSA8?Ss;)_@3(%%  * "(kl"k *22999199990!!#5#"&5463354&#"56632"32655P,]uu>DIE~bRhP{@p?Dq[[""CO@Mr`d.@  klk 9910!!2#"&546"32654&PXγгi~hi}|P{ݿܾsN@@"   mm  9991/<20%!5654#"!5!&5! Dz?1/aL"a*>w؍{o{3>@C'-%= 4%:.-*1 %?47&%7& =&-7"E?<9999912<<29990@0+0,0-0.0/00@+@,@-@.@/@0P+P,P-P.P/P0+0@@@@@@@@@??? ??0,0-0.0/@,@-@.@/P,P-P.P/ooo oo`,`-`.`/p,p-p.p/,-./]q].#">32!3267#"&'#"&5463!54&#"5>32"326=DJԄ ̷hddjMI؏`TeZ߬o0Z^Z55*,ywxx..''`f{bsٴ)H +@<+,&  )&  *&& &,+,* # #Q)E,22999999199999990@p(?-YVUV jf!{    { z{ {!"#$%{&%--&YVUZ(ifej(ztvz($$]] 32654&'.#".5327#"&'')gA\*g>}66]C_56`?`!*(Ou))Hn.Mw834OMx43N $@/  !# #%" " "!& %999919990KTKT[KT[X%%%@878Y@ ttttv]33267#"&546?>7>5#537ZZ:3mN`^gIYX0&DeWX5^1YnFC98ŸLVV/5<65 b@ <2991/0K TX @ 878YKTKT[KT[X  @878Y P ]#53#3+e^@ 10!#!^=} *@    91903##'%\sB}}`s-Pb;V#@@   B   !$  $912299990KSX29Y"K TX$$$@878Y.#"!!#"&'53267#5!>32&P,`r<::d/4a/am"?$Ɨ5dzɏ!!J;?@.9*-" *19" <-<<219999990#"'&'&'&#"56632326#"'&'&'&#"56632326ian ^Xbian ^Vgian ^Xbian ^VoNE;=LTNE;=KڲOE;=LSNE;=K`8@91/90@cmpxyvn]] !3!^DC?%# @I    B   o o n<2991<2990KSXY"55%-+#-+#RRH# @I  B   o op<<991<2990KSXY"5%5+-+-#^R^  ^R^   #@   1/<<220%3#%3#%3#hk'$uh^'$us^'2'us ;@   299991/220!!!!! !# !39OAg@AժF|pm|q{'3@1 . ("%4"1 K1 Q+E499912<2290@%?5_5p55555????? ooooo ]q].#"!3267#"&'#"32>32%"32654& H ̷jbdjQGьBN5Z44*,nmnm98olkp݇y/10!!yy/10!!ym '@   1<20#53#53ӤRӤR??m '@   1<203#%3#ӤRӤRլ@@@ 10#53ӤR?@ q103#ӤR՘?o )@ r <<103#3#!!oA#u"@91990  9%-=V'\^N'<su+@B10KSXY"3#-\^R#/@I -'! - -'!0 *$0* $ $(st*(s099999999919999999907'#"&''7&&5467'766324&#"326{r%$&(r;t=:x=q%%&&s7t@?s9q(&%%s>v:@t8s'%$|pprs#G@%Bon29190KSXY"5s-+#R#I@&Bop<9190KSXY"5+-#^R^  /J@(   L<2<2991/<22990K TX@878YKTX@878Y@0P]]#!##53546;#"3#JcM`/яNPhc/J@!    L<<991/<22990K TX@878YKTX@878Y@0P ]!#!"!!##53546JcM/ѹ{Phc/яN9;>@   Y W Y <<2<<2122220%!#!5!!5!3!!!oooo\\HF103#F@ 10%3#ӤR@m '@    1<20%3#%3#ӤRfӤR@@q L #'3?K@D$%&%&'$'B@ .(F4 :&$L%IC'1+C =  1 =I 7+ ! L9912<<2220KSXY"KTK T[K T[K T[K T[KT[XL@LL878Y"32654&'2#"&5462#"&546!3#"32654&2#"&546"32654&WddWUccUt%ZVcbWWcdWccWUccܻۻۻۼܻۻhm'$um'(uhk'$uN'(uk'(uk',/u`m',/uXN',/u;k',/usk'2'usm'2'usk'2'u)k'8u)m'8u)k'8uy` F1/0@ @P`p]3#`?f7@ u91290K TKT[X@878Y3#'#fJ7c@$   VwVv99991<<99990K TK T[X@878Y'.#"#>3232673#"&9! &$}f[&@%9! &$}f[&@Z7IR!7IRb+/10K TKT[X@878Y!!V)9H W@ VV1<0K TX@878YKTKT[KT[X@878Y332673#"&v aWV` v HKKJLDf,@ d10K TX@878Y3# _@ V xV10K TK T[X@878YK TK T[K T[X@878Y4&#"3267#"&54632X@AWWA@Xzssss?XW@AWX@sss#u@  ' 1/90!#"&'532654&'T76xv.W+"J/;<+->i0Y[ 0.W=fB@991<20K TKT[X@878Y3#3#߉fxLu @   '1/90!33267#"&546w-+76 >&Dzs5=X.. W]0i?f7@ u91<90K TKT[X@878Y373xu ?@   : y<<991/900P]3%!!'79Pw^Mo;jnH ^@  z z <<991/90KTX @ 878Y@ @ P ` sz p ]37#'7Ǹ}Lɸ{JZjXjm'6uof'V\m'=uXf']@ <210##    g@    2  y<291/220@(   ]]! )#53!!3 !iP`P5~.,qu('@^%{&%#${##{#({'(#&'('%$%(('"#" ! B('&%"! ## #)&' ! (%#" QE)999999919990KSXY"?*]@v%+("/#/$)%-&-'*(6%F%X X!` `!f"u u!u"%#%$&&&''(6$6%F$E%Z Z!b b!z{     {zzv v!x"**']].#"32654&#"5432''%'3%F2X)6 ~r4*!M!ü޼z&77kc\̑oabk'<su=Vf'\^ =@   ? 2291/0@ ?_]332+#32654&#'ђV>@ GF2210@ `]%#3>32#"&4&#"326s:{{8daa-10!!ת? @M    B   <291<290KSXY" '77w55v8vL57y5yy5 ,@   |]|| 12035733! c)t'+n^J@$}}B ~9190KSX2Y"!!56754&#"56632 "?XhU4zHM98rn81^BQ##{l0b(H@'    #)~&~ )999190#"&'532654&##532654&#"56632 \e9}F4wCmxolV^^ad_(fQI7Z`mR|yOFJLl?<:=svcE`''5 d?''5db''5 dsm'* uqVZH'JP', /uu'6ou{'Vs'k'&-uqf'Fs'm'&-uqf'Fq$J@$ "    GE%<<1/<20`&&&]!5!533##5#"3232654&#"F:||ǧN}}daDDad10!!dHF103#F1@: "+ /) 2+"!)#&  , & &*!/<29999999999122<20K TK T[K T[KT[KT[KT[X222@878Y@z  1Ti lnooooiko o!o"o#n$l%i'i-  !"#$%&'()*+,-2   USjg ]].#"!!!!3267#"#734&5465#7332[f A78 ʝf[Y`(77(6bbiZȻ{.# .{ZiHH"{/ #/{"G)@ dd1<20KTKT[X@878YKTK T[KT[X@878YKTKT[X@878YKTX@878Y@````pppp]3#%3#^ys@B10KSXY"K TX@878YKTX@878Y@ %%6FVjg //]]3#7Ju@!  VV 99991<2990K TX@878YKTX@878Y ]'.#"#4632326=3#"&9 $(}gV$=09" (}gT";9! 2-ev 3)dw @B10KSXY"K TX@878YKTX@878Y@*$$5CUU//]]#ę1w@ 91<90K TX@878YKTX@878YKTX@878Y@ //- ]3#'#Ӌ1@ 91290K TK T[K T[K T[X@878YKTX@878YKTX@878Y@ "  ]373Ӌ ? @   ] <291<290KTKT[KT[KT[K T[K T[X@878YKTKT[X@878Y@T /9IFYi       "5GK S[ e]] !33##5!55bf]my9 j@ VV120K TX@878YKTX@878YKTKT[X@878Y332673#"&v cSRav 6978w{zf103#  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~>: ~1BSax~ & 0 : !""""+"H"e%  0AR^x}  0 9 !""""+"H"`%^ChVjq_8 (Bbcdefghjikmlnoqprsutvwxzy{}|~f55q=3=dd?y}s)3s\\?uLsLsyD{={\{fqqq/q999qqJ+o#7=V;=3XyysLs{{{{{{fqqqqq9999qqqqq9\3 'sLfR#hd+/s`N{H?55=ZyyLss/q%%=V^33 / /9% qyy\\\\;LsLsLs9#LF+o{\3X3 q=55^5bb3sq\+osfqsfqqds 5?+   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~     sfthyphenperiodcenteredEuroc6459c6460c6461c6462c6463c6466c6467c6468c6469""""XO!nE~Le  R s  X : i  = z /Eu)pP@"m#{CwRw [ r !5!B!!!" ""#"0"="J"W"d"q"~"""""""""## ##'#4#A#N#[#h##$4$%3%S%&&'K''((X()_*%*\**+z+,D,,-P-..R./0A011!1P12H2z23F3p3p3}3334z44445595g55556[667C77888J999)969C9P9]9j9w99999999::{::;;^;;;<"<_<<<<<<=c>;>H>U>>>?a??@:@K@\@m@z@@@@@@@@A@AVAkBEBBC_CCDUDE*E?- x$%&')*K+-r./2934K57D9:;< =IQR&UYZ\bdg9xy&z&{&|&}&9 999 K$$$$$9$&$*$2$4$6$7a$8$9}$:$;$R,1\wD3$ R1s9R9sBT1ƜsB!1sB!RJƜsB!RsB!J)sB!B)B!J(B!BB!RB(!R!B)%R !RBRJ)BRJ)J %R))J!RJJ)TJ J)%RJ!J)8A'Ua BCVdR)-E"KFsPZr RͩR $1T2B BuL)-BrKsA3stG DfDBpxP S@bB.TX\]\@.!!A,pox N)*u \adhlptx||$%@DD4s !"#$ OggS{`3nXFAG% ]o>Ϗ!=fW2޶𵶃Ia* 2bP Mgϟ=vv28xtիW֭[޾֭[nݺu֭[9s9~߿}~9'IfWUs~߿}}}}}}}}}}}}}s9~~;ØL&8z\.ǯ_~ׯ_~].U5篯s9㻻_>wwwaa߿ah[$`+q 4[xPSgc>}N8-Ajzc3rP8j~x i؈3{ϻ+/s޶|;OĠ @Xx\v0G=v&̼ g+n[2kiĕEq 0sⅽ˙ᛉ]}Fg_3,ܟ>-ԗ߃Ozr0ʃQ2y ŮEߍ}yР|!k[l$rv0܅.%A:B](u!_B(@1rz^q7nG_m3f.BC9|[;Y^‡oik=@@.|EK˂*>bX_I˛3(}`}&pE|ml ]9=\2,svedH1 "AY,.eA IK^\5-T=\ExEL֎\H Rx-i}ۅآ7 # H @9=~j_S2M~0V ?_B$$.b:Wʕ+T;60\"d2 m  % "ԒU:rÒJVO儡m5|+M4=K3 U0"oK[s>F[0ge۶ p`. (@/Xtʶ]E_rPc5v蟕sy7u Dk`?fAn_K`?@73pfX݋` `Pxo$Y,Dut*$|d?[ZU6SeQ(f@G@BiuZBCS[5c.x{O0UQMDh﹟*f?kŰ~*@S~eFirkL-K~9C?[X6x9@ܜ(<e>|p V݀BN  d ~h>6DTʠ*rPml"-&S {vkGc<5U 4@vs|R.wl r&/*@ra<lE` 2i> gx,CZjEȵtϊp/`BP_R}py`B x5 #QG E<9TQxp9@TovD@X*֟7&]\F*"">2ۗawMopJF_;KPmX  ˚@%k@Pv@S>r.ۼ˟E+֧%"}:q^ @} `G@ؒ !WO-撱@e^v[Ph LJ:EDn("h¡*{4cX]yp)*?}5&L -Kuwˌ/WYr0 st>8J6`۵PXC)f CU= _ @ 0tZ4/xwuwP5I/Kk4X|5+A0J e_ "9KS <Dvk(*La 2Ԩs%.%:׍n_?Z_GsK9/J:IY{qm`sj[84 OhS[ύ . ^ #? s kenܒxҿ-oF>zo7( 4L|E`K &`**!ڇ_ |:h;yV+3vQŽ("_*JraKj#釪)Ne|\=2$/YWPy4=̔1CY/#`\4 ι8w7%(4dޗt hϟ$:%kI2}T;  H@]9 V> $v97Zh5a, Z + zݐr-.~ǐ<\J[4ʪkzʬa j;efVFd_O390 uΛ}$ p:{`T %%UVJAă\2ԯ4P }pWH!_&P^Ł P'5Go2DQ@U Zb+ל &#ծlZ Wg0/ⵏ`ݾ|Q^8_0Cp rD!l[DNd\4 a]]S(J,2_`} PFzZo8~ gM|jq AMj;M/ 0 9U@T} p9ݪL!ET* kQQ:vhmű=ʵ U"_ H$azuұpd`CETҾ6SiKV-xzi=xn⦄JC/O`VK@K?N" [WV=KkM{S~G{[Yt~ns-@nutPEeH'@XCTTZuuI_Xq-`1?<'k4*A K)\cC>4ddY^(OSPBvء)@_~mVy/!"R˛?͵Zn bnT|360L|w &L쿚  J}lSY3`ihc?`77"ksz\8~ƃP?l- Q}ceY֍b|1W8 ְ}WRr|jh(^0 ?[/_%RpovHCl^؅ TY%JnUů\ eMU` YC5p/;z  4%猵\}i4Ccx.? ;w a+Pk8G@&Z)hVEPӺi|kW,-TVYcs+.ZhY?Zi  L?w M#dC `}{ٲ,/0hz2d(sz OggS@`3IgR% ;EDG?=[ 'BEF<&c8~C[[$XuH E; 2=AX,4ڏ߹ HS6A'^x " U!S`</vK"gХ=XCC P ևqGTi` aouS$`C4@{c/X`_~H? >8FOA:.@_/P]WʽA@ہ7!i7AS~l`& !!$GEb*@<%uC<9ytktiDO+@w(l~均n-ŽOs_}hZ#Ug,. NN/g@pb"~%ݖ,øc#U[7&b%?iEJ]vہwӫ܈9]`oXo%z v vJSRUMr\Lrw,p`|7d/ݘ ::߷/w4}|c;ij{&s߯ '243;SC۸Gv?6h:i/Sngew-A $@Hz/L8ӪŚ!rdg  WUr=^Tsk^-rOfX9/ & !?a/ʡ`"!ˌP!j2x4PQ2#(UTq`k*.В23Bg2F+뢬tQLtp,pP?Xd9?M@ Y̦Syg9,0vs8_&}m}Kf9l6o7-4ۿ, uVuQN/{Q )xY }`Y3{9@܊pU$B54v'§ﻪ+G1n+%-{T /ܳ/e==;AKxm{W0t_Q?v*}1*fr}xtit~xT[mwN')s$vɔLf)?>8G}r^Q{U=9P~&j;`^Fspͷ(C, +oK=k[V{lsg/yOk:h&4|MH=ớΔI~u=ƾEw@s;~̧GuN}L~>1Ïz~W4߇ JL6%)[yCܳo-3cdWwjS?~ۮ|s~cURe<ߛX|)pP ]cՃOC FrPԮʺtEp 0*Ro K]rP< Ԇ#u*gZ:W{91*F18/N==9P/@ACM %BA { * ƘMgodr4.u;!Ji<}xwPyf b4jDUQ1hl~J͓uB%;kߙs%ܦVc}|=l /B_Sp[~[,STF: l)_*)iNg,005ar;.t\<nKW5| xnP| b=N9ͷAM $ $ukox"nvvO={A  P]&xdStdN'K.J](9w+Wi`ļۛRUzpGDјrWOߦq4 <,._}{kg  l@JBД"l-5=w\/P>~LGas?y>`_40^߽sp※ GL @A\+-nc7bNΈ A_{+O   zaA.5f~ 2PeȀKK[\EԽvȎ9>:w<~`^ :.^#0,c}9- HtjFOggS@`3eV !`AuB0ڔ3-)V#3חJ-Yr(p&`W py np5Ap70 x݋]jZVkر 걵`d9\ʸ@A{/*с1 oE&RKVIdhNݓO0㏿_u%'Vm6*PyN)X{]sy l?=@HR+ppi}߶PXB05fylJ%n /0_| \?0AOs vla n&a,٦:nlDu%HtA%uɐWɠzߣ/q`) ׿"tA}R |C9~^in8BujKnvA5Qpֽ}ܙ ]l?j[D A|mkPO w)UaQhVK0wtxunZ׃50`|bq\5@z @qnzfny5Oەീ@7zy ~ 0C)0@IVrјZ{o**O ;$b]H*T!" \xo<44(S/ `yw=SUڌ`_ ˿ 7 M~φ.F55.` @oh`wֹ 7o`è&D@wM5œH.Pfl?A` #ޅ!-@`ZJ!Bn^r ) e\Q[ևƨLS'Ϣ7?6hmg_׮ؼ9@5T>"篛:4zjyg3-77(D} _ua?xHW!ggrҹļ2OBDCo+ @3Ps',qze%NB*払+4u?LA JGfuFY˄ҽx_@׭|(F v᱀\#*D Y_@~N![ܜ ~:Aa>Mi~qdC@zR TA-+2ފ-\&i-$ RY_EYr8MU }c\"?(_ (IkпO8A6b1͜DBhZU/p3Jh͐w/^IN̰ 쯹ed/e \{P D:9JRj TTh@ c@ɩ@}2^c!^`lS|?J )$Nb 7M m~잡OY|= ?Ts/݃8AEr)A0сEPtl ep vO c)Eߋuf] 0=!{ZhZ>(~/蚈NЅU@:"VUvՇNN/mṭ$TBy@oPp>:0 `~i1n:<2YA҇S'zHRRxa[2! x-1yNO-\e UA@VPT)5"o:̯ 8~ s/a񆯚L5-_IFNTp ; GP/NPW(ܵ!ܵ׻Hަ }(@(ؒn "* krrx˖K Wt h,{د~/:b˔$5*ԺABiĸ?y6Eum-oNH6Xjo?ׇ"o/?0 .KJ 2 |L\# VH/)4W|Yͨ_ vfe4VQ^P*@r23̀1uYǢ[tɍ9+vhmJ@@ED?ppd8c1ٿކ*up>t`5?8X8wm˼xpd إu22 /%W%h4J @z, _)7L~'g>I&^@H=lAmKO`RN㠑0Qesw$5N~WxWp<<6(PhA@{;3Esyᯗ5p~s5 @΋_`: ƒ rpJ?>!l => e-Ҫ ys-H_/@(>-(VuYפmQ3؆=lفo0_*}B,1qBٔm՛Yo!'쾸}wytɐKRyi%6>L[`Pȏ; U[L`ܯ=L82MG0M [wvd$EMMΞ_q*$7Sΐ7L`oK 2HF2.\%)+ @KUJPHaa|m؞佭4%٢;J/ۍ3էDj/lo, kpm ISIv_v޾jU="" LaE4Ի*OfK?҂+÷m +Ǿ_>3fd{cp|=sq`<9lK7S/ i23iZTy g_y$n B꿈RZy/^_,IR>(ޚ-nJڞ'CXΉDTWWʤph7h{4@wvy ~1Fijy^\{u dY[9!} Pm2W (afڡ az1xt6i(~/_ FۑLTy^ ] @>$4|._4 "sE`|OB_6Tmo !@.޸'B @v!ڦ`s8x3Ưh}C{9% =Q, 1K j t4 /5(4fz(v<5Xxk .;XޡMP2P xe-Oq rZ×nVZH;?ga4|v #g FU"_ 6݀ JF /o@Q%4 z OggS@2`3h  +)yoY/uTbķwSVABQ(-MU}D޷=^:}`5c#Gg҄"e~]Z?){!kY Y9oA o@񋋖R+uy0L@.uLL`zЁjFyFGv%yL=TרּZ* ,@};@ MtɃ.twĚ軒0|`7sx%0GXgKdASIϺQD(r_UƄ@vvy+eҵ9>IS NV <,6u?vJkd?/4\59A=qE7/L+H-G:-H_  @6f˿66H6^wLOW] oR!bk3?!Af*H74< EU>PĿʬ*yr'lW_m1 A-Bz pq.@(,=[~{HI|CJGTWWP{8gʺS tsݮdY~8Q+lŀ{یh+ЫFVKɅ;89@TP?QQv~ s2m8K t ؔVEƴt8Hn=ƾ2 غAp] `kI% F ~N[|#})?I6')tpGL$ Xspu @ A*M$jFΏi8Ɓ._H$#?u >/-w]@̧<@JȀ2/vڜa>Ujf[>o'Zmc8^0<\atN jL"#  Sd7s@ ^u#v.[uS,ŮI;^}Μ1!}Šn }(׸*@,#N  "ѣ~,+&繖^{'B6|>@r2 ߻, @ R%$W35mybxNnm)7߿rag~`uRJ@>hg _~cL$c.@)IXU+@/_oKN~p`H3R >a/cԅ OiD'YLq[g%D:vzPo)"yb.0/s;8b }\)53i'_d+}%+By 7.c P{@l"zZ  ?Dۤ5J t-D_`p],h! 4%1-f %ÿKy:;bu-"._ 9V_(8PRA mQhVJ3L͔IdJ!22Ë@iNC~n=@M3hb;[>⏀BY!&Νs]yr'`&%@wʼBPPH**,-ꋁ*ғ\||T˟ :)ފ;űЕ^%N (ɂةV]Aqk C$\$)-sݱ7BSt{Y[ՠ埦 ^ω- Xq 2+  |6&wR[@c2 SǬwS$si2'`dYJR@U [~zK-ʎ uй (4x+*wY4)oCL'}kpXKMv.]PBIJ*B&gwP8QQ89}@-!fx*Z]OJ1w8Jq؜'^,> C0d,n|i֞^6p_ކ`V +i`gM2029c`sH%%|Y dV .(IŔ @%\tF|"Mhʷ>(+/6K:zeq&NBI}^CS "pz0.(!3U$퍫SMEc4L{Ѩb;_TfW&\- -xxX[ۼO(N0o3[luؚ1fuU==_c>NH@ϧӕ'jʄǙ@ (w?HҵljH*gUY%,$Q Zц^{_K`xޚ;ޛGԧ^\ @Tb"iRj|@ f7/hc;R2 !p!Ҿ+Wɮ_{Eט 0D|Yvξc ASa9p>_ 0-&;*/fο @/Pe{'8ޮ0Ց_g. MP$;FGdƗ|[@#W0!c` B0 2X l@zu+]^`(>pܦ:ȺF?,*KXR07M;؛_#?WUw6=q/ ~*-,F g hf_s^ZǨzLNiZ"jcklm; {Y <]DROe K~d~xAZ U/ ;gu#~BǠ!aVY,C+CUI]2O.0WN M4;,y|?!`jŠxvSAoj5:/ ֣ ˭ɼڦ[}`bo7_Y=kc:4_𶗙i&Q>s-x^`XOhDR,%$ p@|q9^5#,BJr֍V{l_8a:+< ɉx =6BqQ 98-gg {Y6q7ơޑmA!%m):7{/_|q>"'mʡd~@)GLP;qߒux$^hx.bi]RH Ry/))j )cOggSo`3=DCG' 5+>6:nhU_1߄kK|\6!6x)!?{y0jx}ʢn@b R]*m}(&/<=\V>;c!D]=O+6s|0w盔|j;Ў`-ǟ T70@r>G0 L  0fr$% -ض~0t z+TO@Ro=l{.A3S!(,9F>򱺭_Z!F x7a_~'0)ċCMu 4Ã3Qt[?٨zE? &x<E ȼxH'c( n+s?S@ H~30dg=km'l@Ǵ$6@O$% '% Qn ep&,~z5+ϐ~TW×!zBǍm5Ad" 0{6at~7kov6 ȵٷ1E 0n<{m+@_^) ,p>o|cȥy ̱7}.v;PHO"CO#{r~?1J};~~%0X98)oLO|cI+RU@۲w  $!ao߅ P0^z'~RI>T*Oln4r6<"X-&(0 'j){Ez$_x<{$|o7{wpzuN] 씥,Ae#y_@/9m8W s5`ޟ yiJ:dh'd"fʝ@5c26*ʳ Z |m D-*R6#TUSVhPH4>zOѴOjtR~"B7 NQ>O.(=@1bl!L~.Z) bu[eW^wt=.7@no9-0W֞lKOέgi~AZ ȸ}'w\|VqUo ɖWyɫ+g,w vdExdC C6yw#pD)|%Bg#@ʧCƝ~ \ %&Y @t>YvSH(f7~;ҲbjL4c:{O9|3C~"㷣߷q, Pn[WyH?ߣ[!#h5^:OGr]0ѯ}&Zgc=xGT /]jq8p2[0a- Pkx$tu^w:dӣYać)$7&y S7ZT!ed!S+i-7}T7,cǍF}zM;J go4lp~υ34z?kst{2 + c/'*>@*@*_ ^@k*vw&[cfCSըz0s`_/Ksp׉8P7sa C1 M繵O^ϞwJ7|UJ2ߍ);P agV2d|&~w)nɇy`~ফBNӄzM;eviiZ*Ky}2t ~7cmw.;=gf bເV+Z '0R(LJo_6t%ڈ"_P:'qY< oΟ8@PQBrO.hN&d(AV\_yIJҢǨT?L@r #MhoQ/Ɓj;k@khۉg0{VPdZ[N?&z;0^u@ ?#2nկdU*#+4* ;rf}A";YL_9^XKu(\pLO0U!vww%H?rQq^7V^'KXM7ʁ@QDm[B,s\p+)9pZNLto0^17)θ wuVko4z2#,pد/VP"Ĕo$?ԧ(> ߤQdԫO|p8![v^7% n-04`K/O Pxc}6v{.:V2b'aaq1I(ϖ˚^** j p䨿%0X^K~ +UxF ^ O阔I05wF7FT[+1_ Ğ# \pTR5|_gr܌O^/ߔ w- x:L\@' l@{)ŗ1N]*  *ovv&8Ϗ|jn o-+}(/goi `8|dg ;VSL>@ U.H,(z4l^ewavwBΉɣ_'Ns)R;" p_b",7P- 0S(BSv:՛⽶]&W~Q@6>;aX/d2г~.˝ZB8SB?g -_DD $g_g9{"wZ#(g`_H 7d]A7˾pB[ܹt `rOi|M@EEJi{e䯘%ΤVq*֯7~< 7nرTb;"0u;H4[]w'5 /Afq˞fO.<19 u[rr ER0b__gQlpU)#|Úy@Hk7XwX@GޙC y>ǁ]@DLlH^?|W:=mp4 ~bPޠ$HOggS`3:" 858FCHED,";C ݋QuMp1lQZ٥'Cvo[nM<߹O pQ.ā)[x;8=N>y@KNg\*P!Mƭ1ʄ2uS8P&6 O]d+nBٟ H?tvǹ镛|RŴr 1O=籁UD7cJy n9 <ߴXTU>eO)L]~%`'i|G[վp8 _FRR#!z݃8V\:pa-lz ?w+pu:u7pby<)2e dUlwDinE`/3nhGpQN݀ia{&y `7g Z}`5))϶a%~}K}T gB V(6=y՟E.OʵHBjB(C7PO7NYUP^J@,t2]^k:rI;3!drP_ ˲#꽣fN?fŖoܜƙ <AAc֝t$ ߍcNYn  syy]S<>'&ܦ^{1B/ @; mY,F@1 0`)DwK~yKЛDSqi>wM|OKmg6d~6ݻ=ZWW!2F{ЙvTmS~c_UվUk*-L{Cn޶G];'ۘG-g2"OaW|L׃#|Wޙq\BGj???ut7Lm/8۱dwz%Lx>)[=o㴪J^|\jM5D[m·ת?~8>+mV3G5c{-O8oW!إ<ᛵ_w9Ew{rc|@o_}hRcהn 3(a|&>:t×ʼT[Qo8Y:Slq?"[5nI97X-j!5gnYѯ~^[{W:6͚8MS@ܱ޾Xtu[c=/jIu#1׾tqW:OZ3:9G%vrd@tv <^QQQ/ߌʸ[+" rxdeuࡔ3L%͢> P\ݥxqaQ'`TnY>]ӻɗ(~fM +ED~ y뇿9pn,ˁx1"" 5"(":ư~?lΦ O0 gx`>!?%dKC:FwJƏmBr-͹K*CH'!+UsQtW`4`WOCo3Y3ApD̘$_qF7@ y9t9ુ me@" &lf0Z1vv]ݝj|sاۚY a?\a <]rм9Z,_ p/PP9Nl/d~_=~o;VxFȿnP(?O· w-_AXqӓ~ ꡲooެ~ h= ೩R ƕi΃z 0BdȽiԁ5S Zxz;v翗dhd~Z6lM FWƓxj-jf&e\Q~&̕׶a?71 WHo@Dﴂr ҄)EVrX\c3:UT/1v-GZ!ɗA XKg${x\4_lw_"UB^9ެP`!&}s=_ oQ  NÚŽ'P('n?W@@'F 0w%R0rp䘍|ts? -ZN}_ޘɯF÷m/@r?$ ܍@ J}x >!OVӿ5m?;0@ʹg @/+}g½7yH,/= i XK@>NWA0Cܯ =G2L7>[?i ި,4wa0j !  m'zn}^ /+^H&yqt9+wJ6HS ͚M\쁷ǙL+=4mWo̯`~% >)27`@<,!H}U=of!} gȵZg&e7އqUiiLHh d 6i'y*mnr?';*s:@NuUs&g2+qX)$H*U|c]?;4跓 C>EW(l"*+'4y.A^@9ܑH r{_0qp&h}cCA\;\(^S'9<4$SΏ7Tjq'4^b s_\Bd/͡S誊8DŽSwnlwgʋo,Ly*#<82 l$ Yl@P{5h'`ܨy34M:IA{x^%^`rӏsk&$JDqo-/$g H}0L( X!?l]'[_GqT%;+hc?۝Ha3 !pqe.mWPTcw۶e\f &N7$NhMӇI@w:Ht1W~4h@>4=eB K`@/âS na80L}:Xg3 '-^d}bHPɸnla!" F#Hw9-SeQ+@" 88><XĚhBTbme/M:VMcW̐C&V_E}*@R?ot>vM2)m8־UmNGʾtS8u尝m6ߡ|n*WHC-K*%TQ*T1+ymE j$*NWL_4&7gqXږ8Wҙ<;Ļ DŽX8pU!flY4ŧϜ|ʮk Mc=gN7o|~72 d !֜˔hgs %aȅ=%mXz]]xi+\Ҁugcs`O0(!b4PH~r _bPyI`8bӛTUTR2 8I|0މEOjN.7 HjSbW^%ԟd0$6g O砃b~s 16qTF xp$byikƽI=|y6dzb6w۝t{/%Qve 5߂m)@9;Uxe-#{8%[8o5h@d08ϧoaYMp}m=gEE4-aHTJ=+GºD a0` W1ldރ eCOc7Ms kԿc(A\Ĺ\KAVUʺ/M󿴥S_G 9(,|L2*3P{(~Xa(d( Z>;MrȍUH30$<+Ld cnhKq&Phf@|J PL\<!*?h}ʇx `g^wl4cCHl}"@pMF Wp:`>o|0M> 'oCat{Ԇ{G6u9W>KtPg]'?2 3`S>l{kh@(XA :ֿhdw8+= ; OdJ@gPd{+C>ؒA6Um?VR(zbT. J` HQlh)~OV3 ?3yaȚ[ L@Hg18(ΰ$Hx[aݭiaXBN *m>=%ƒx^?1z5<6`"_#Q.;o2zg `4 tptEts+|0 =-9ynN +H"_[P%)$JPTy@E9sE y:a]3ۣ^EwޘX]s˙V N0ω 52TYy|9K:*Q P1̔_?Ѥ~˸ rsh_k׏PJc]B Q C 89/q[24k]j# 0>3G;X<3nĔ<|!=HJ Yd[`i +RJw}@ó7kcB(G9`gfk[2y0BJF xYisJ/Q`} m)mQ(9^aj`4L^e⮼/[غb봽EQ6Mw*^eGl/4c3$bܯY-FLvbha;7C&Y(Pt`%<P7&TE*vIփm[R+韭Z~$ $e|?B>Vs= @!QʂUZ(!:~m)PunChnTcw S:ͰhZٶ 4kb_wӯC}Y02^U/&:ɞ{9OggS'`3 n[   Bޘ%VQf1r?>  Dm W; %PF2s-s~ȵ/z ,VU>{s2))` l-$\2OFatR~C!C{k;@KU,P ^Xan|E 2>)lO-ui%#|EϮl`@toB6 >%_Q%?Y+zP8W.>FAAОwA4>^*u<{LFGf0qjYt+g9yНͼޔ4˒ e@'=5r] 9 ]F~6Y E>5@i-בoІ9oƞ{ƧWpq@` Q]:f`IP"# AƲ^v/ dl@߽{ =Ja\-0L~yWD_[RK0N3vCJ|!vfO "\>@M~0x"Z*p~l  @'Jo|*p s W&i!0Zȼ2y^\|`$&L\ȰU)Z6P8ŝ ~aZ^>诜@n3@Ck/z0^^ ^.|DRTL:rzEUI%lSy7Qk+_fE//lr舿Ϲ(B%H;c @<(Dr 7Hqlʆ{IE<} o:?@nWBȔƟx ?͓Ł,@!:ZOΡ-B=|kwoy͚~Wv:V?@c;:Z9ߧexW'?$H6_# <1=h49O\vH!0 x_a5{GB wt3m@/D/=p=0hЌ.pg ږ!f;'mY*d~_?vC8XEZ+!avd^ vMg+P2JזZ`GµP^HΥw M564˥.|v d)A.66`rT1D{ U[!URXOxmɲdKѬGbaUR%4xHĆw{1Fk1ۿ~D>K@L`*8_3a `s=ʶdf ''Ex[ͭXR9J pwπRd9|dY/Y+7 "?g`n2wr.䲁i; v__{5 \R~l](%)-, EFebƽ2֧hekz [ 4~hßY$͡5~%[];`#V K'4T95PE us07A@SʔןZt>;xey_Jg Mk4og&G76Gq!$E @e QMyy-5ׁzPHo}ǓЗ ` cd#14eGm?ɲB&׏Q+Y%a6Aêm}Ǯ|! .x2{( ~ 0N/@TW JxP5 J*B6G؆o^{͛`ڞqo}-v2VKX|JM\@?Q:ri: N:wR2%iz >!ӄ2vۇd5`ߋhf}Y]_@TՕzO[$*+.N udA6 +4>HOb{AAj~E D_89 "nz[l a!}a-$h0_0A-iX<ߓpfĴ<*s72hy@ref`N~)*On =x_:4aP p&ןޱnFl@vn`=L ^?SRllcl<6UG@HxGHEٟ>l/ H%_a8 ;f-fH?`Z>. x h [m=F 9J?ڙw9XLk0Cfej @^HvLAkKb+}o/?Lt\%^n@ OȮ@+$qMm?Ĉ;CI[u'?97X;1 <@׳ .RșBr Cf}Ȑ=EMlSw6ƹx/*O F@g2Zxԏ%*V*"ɗuE铋4bXa0s hx p-Y ٦R$0WZ `wtۇW* V%Yrhɾ{Y9JM `HzV^syJC t@$lwsVJ8$ Oe2 1ykG^l,2 2(UI?$ŇSfC}%dɣ{mfH| n\0l@-WEAa\K~wgk\ dXه/]V< PxSWQ V 2Ҝg2Z)o2tC^f(}ˉ @+mr30ԡ@ē@ kZEG0OY?t+@`5 4(@7 sz=ae+|؈&;s U!>ɷR'Nܬ=GnZ@jc [2ty}[ޠ7'>]L.R~~&H~[H8)@˰-? zQc5@:^@|NQlaDB#z:W=; eo6s] `( gz{Ti~&ţiey6?4=K@%{cd3'l&>Onz,ENT'],@c~ s3r,&&ZKڋ2Rn=uE/Jol9%WIέ~gIi6 lڀ$aFI D]ȕTKx` EG>B,U+ >w61; KNnsG60Nw(*?we[~胆1菍6j]47p?,QZ0+m7lVbt'zchhPdBj#K'/ p٫Ѡ<|xҫuƊ{g`|YB_&o3=Ȁpl pO/:m HqR4Pd;HXr] \[< 0kh*@ouTu(@ƒ۟fo7@;k 9n Щ6j"@QyXAoIO#=C?nq.n:1\5 544sk9wPȑ 2@7꓃/9%@v^D뻘 ҳM -J0t `X)@4 ,!>?13]F#0k'9c+^Q{(΁ *~:p9:A7l$@V~.M;Q ~ //xhUy- O܂USrub *%[(-H;w>U_I|zLR736]xG 3v皟4 :u|suΥM>5= <Q5O(pB%}+"_2v܁I6 _[<}נr8v W<2:}tp|t[G_ۼ?U40 ~ x*o {Xi ~ؐ Fg0|urfKNʳdƉ?ϵn o;⁣PTxA1 +Wu@8mte#=M@@B[TC)ۚr$bWD"'7\XPAԮQ 7@@;;0`a>7ʟyJh8Z{>Z);Ɇ=u ~Ms;+D@`j@^=sӄ7yBnhϼL?"ֈǟ-́ ~(:Pi+@ȵ)rh* Z }a K[FږTT D+Xܠ"0H,DBww(dmJXla7TVvhѹ|A:3s@@@9uРiZn2J=ϟn\_MXMh{  4>m>ٖz﷓Mjv`o#N21ۏ 7N;Huz,3)pn߮;lrɯ-m3Q/([+uOo:2=2?}qo|ɿ71WP/,88PȀ^x%3TbN'yWO`_0) N8{sn`T/*޳m\z{`*yo->yy\T ;ZR8 ~?i_?k@{g1{qB@s_;`>n(L <T^tCcnܳw'.c0V# l|%` ' 6x_13i+a-2 6,O woWd&tsDٵaqsgPbvjXh^}9IZ ]ݟ--_w3~r;|z[$Mύg$ۻUǹw7CsY)0'p$-$o~D-pwy9V<7zuOc} U/#4YZKGKl c/]DWX 5NP||AS1E2}?Lӫ篢O7GbHem'z_Ђ%/Z gjr6ϯ})P]]|= "*ֻnix&85-_KζT(L@M5<_K{lce@ug%?azCƇGQ, X R_>~yd PgY-^\`ϘЅo ZyYP&Xߠ3Ãq\S3 |6he4ITMI}S;'Gp iШ%v~S䂒+؎=O~.sD( Z6 G(n .OA(:_ ܎F@PPy&`@ax ~hgi'=C 8 CpD<.u 1_";y$p~h<./T@c@7 AcjY~KZ "JZ졅%x=JnO&ZZ Ϯh Hí 쪪 2&sm~LoV_SD6m?|b-Ni->lvxLzbVÔd-7$b,`_4pU+w/`~&?lAT-I`f6^g0"l @vӇg2~;h# t!9rf+'ol2%v(D6ԋ2_FD39 ?<ڕ3hnZsk]^*=(L 6Zg>J/,> XK7 b>(Ó8sSl9>f pU[ >v3/þ݀009^BeAzB aC[ދ3vF@aCB9W~CZD E׿z:_\'%8kL @<>|4t?_ 41u)Xgi:%~D*OLGklSo6Np\E*>;u¤W`'`ȁ.d̈́4_k.^.( i̮(ʐƻ5'.7uE\tEnSrhj NK~ @Q@ eglK3F_=0hMppoR;L|^/p@_>uĽk髜~ q&M,y/}[@@0p`_8֜ x44@GKnhh6>uĽ4%Pĥ]F>}0h6{ `a"W{u..wV_!@w d%JS (ո p`4&/t 'B̐!k-(4^IhZa>_'voɩ%Lo ܛ{nt<5qaC8 QV^sV 2|o}:< ^Mĭ{RLhlHo[IT&OD7hZ)pxlp*t,malQI C<P @fLr!{I@F1uC˄!,;M GV@qRD@HP#߱MDy- ){J we`@o,N4_O@ڭzi OJ 1E|S̓L̫ N4Y}(`"HX+_9~yU%8ZwPr&NQ28caߜjWȵ̧EaC&bZxr'T'(`8ٰ2,s<>V( ZV>`@cc{,0?;_ .@| MUZyVP!_DFW)vrP\^h0@<_^JW\8. :VoJ53!N9 Ȅ![2@(oꤚpyXo QA%VzsG x,.@ a A-tx`RA+ (N j^79MG.8\J3 P Phy*a Yn=>6`~g蟋 `૿ |ފm#Y5D t[WD}Sb^ @Ť[4|NZʜЄ$:7[ZMnwr~5R(w'n )P 8pv_{OÅDɹCo4 5BϳnhS,|l?:iS/oG,@@d>OlHkPS h" /F `3hgo4ϵ`]P2 I@.Mp`*;Py41iP2k>ٮ@Wd:-Pmb)T^i|{x;O~o;VF?˝̂nWz_ f o@i3[j"dpTT2 H7@37HA7zGd4TĀXAg|O^&8@>0uB"ЫjF7vЋ \$ >Ȇ+Ye i%,`V(L`fBF" $ ž`29`jI輜Kz^1Sg',ǧ o3P:0!@ p/:: N )U˛ l 6}z J w&'uGhdцx_ 7SăuG'p qઉd̑di1smRTvȹYGK)dudփ셜3m@W]H ;@^]@|nH@Dkks.p͏oeKzq&Ž,ԯс BРfv`q<ק`4Q@l'W*fVjM+R$~3v 񻟰5[ `v!_0 *ARڥ3\Dw?B4A !${xi^xr@ޑgB;Ҭ  ᯢ*$*\hQ6=2ʝ2v?~Sט 8S iPq0l8;(ڥaS E0XO?>Q"OggS`3 &q 򀄧 @->=iH6+B:g7YZahq#|~d` ؠ "D ]tKMq'J@NGlܥ(@@AM4r~ʮ;o7RSVHM'@.o H4 5eh PF61E!^sfܳ%madX~z]s0%';TT66?њʉlVys6' 燋]v\2`uog} nwe𝿔d)\:{(`@Ϻhf- ? \qy{W0*҄<'P @S`L. LImo$~`{Lx>!~+ 4{'` qa't؝BW@vnuJ;U^VwU,{/s}} Ŀc*S .<=7]39v 2pݘo1۫2+y_ w :s̃@UJES + W]G΍sQٯajy5(&7Ik!/818+瀉@*@b4^cȑS7Nt]ުVgBmrU7s[%9CfnU'lLn?9 4? =@{>b|Y^(O|s @-bO]?I 25Ŀ:GCߊH6l\Rުj<<:72IOWnV$뫒Ms' ._ {<4IH@jf;k>yVZL|/4> ieswz@?\{z;}=|"}y 9';uyL#ͫi.۩/e]`_r9 0Sjbl"Gp M6Y1֤GF!ݴ.S6_{hx/qj$cuzu;?;zև! xvmюsvs5,x|[M6^7 Z?Ouam~&ٙΧ` Şl TJRc% Jj$>PR%5EC*D]O416M-wdKid*}C`mc^j;`OmL@!5;,?@p$ 6l4UY0d2Ѐи t|||Z)gd9qNaRUK/U;m@E.%[WYZg+ߺ< `/i(rXji)v<@}m h&1{=4Iwl;IvoƖl ,4Z`s8W`Mp~U!B0 "/RR-ز>ȅFqTJ1L-@@pq۪ϫ@ń#ҁ9-R] w'47 },Qr>9䷊3xOkX R2P{` (`^ymG=ݿ4&ho0Мv ;2̿30 _;;P q'Jm9Ŀ)cd@KK-[ OVQ4V3Cz6O,'22`F J4s&nKWд?:)Áyp@UWNO ,@蛋X䷓F{!&+LN{QiY$s LL_ b t0oo/cw sw~G@,oI~"EM /]U) o-ZI&ZMpSCOl9ynW.R/ǁ HeUP=gr v`YS^9U%+K;GX)-g5(3tuZU~|hS\ _$lhם |sq6>( En4E\RWkx~{!ό'^E4|{$:odS 'NyNh5ϐ~v}:18A,`O-ۆ lXڤ5>Eڅ~ 0+ju"w'1fVbܷ+B(M0/j h45̯s@6As10 %i@)G^uh);x3@myAdQ-n*/?f?|xs66l~8q_ |u# ?,Gw#[~# !,9,˒,>huPc-v9OY̯ŧ{r$R' 1j{ ɓW7fiOUb/P8Ï4`UcCp x^-/b_B |p`S>Vu\Y'/y -~26 UйgսB&Eg]|nh$?rM-aKmt5d` 8+dDٽf޵weFٵJ@:wSSvinSW9직pT~US<ߍ6JiJAsءBrt](:N6-;~%@.5nX3`,bSz+SEKXO<ݍ경we=@6b0Y~|ѻ ua=?*%`,Ϳm%{=[YzCGiY^ɾwZ*|2n^KkmD-;n{O//\?dp^4!ij_mw$MҒ>޷&Tڦ O{?M}^W&٭{&<ޑ]Uo[Ez@F%+ =l)yf{:i;Bʿm߿%F2tD{엖A>/k.]ݝ y=uWg׋ߵoOOj=[j|O>2t\=_uwbM:>0 &3zn{{5yc,g?Oq~ M/TIX"]sH_Thb iHًpH~bcBN: 쥫PfckSi^]o^9ao~U 庯[fSo~Z6g]qtr>euCR.7Y<9oğ dd @ϖR c%g=p^~8 e65=MoUB[qIx*E_?2! k; O ^$P`X{]>'C5mZ}G̉@*LDC~6(qA 'ܘд&:J^$jˣEjv5{pf):H}BRC) n(%c dDDD$' *ϪP_ f";ͧ :О 02e:ߛ)@&(P^ޘmP ~xh?Z 9ZRUjQ^ wPiP^m#e3Ӡ^XNlX\ l>И6?(0,/?7L[2@EY< bǺ#C_(n*?cD/"O},)O/:JإMTcog{-_:UD 6]ug]WZ]28K;6`=d٩NR_&_ t͂m4ι|nj>'=zH ;xv~>k3'T *r?L=6YpX@˪m{@OAB4;xO~+ p<ۍ.۸uiFp1;튈t4 绷V䣇p4u'~l{m8dt>|~+߈ w> Tߍ;oy^Wf"N).i{{jg't{k/> y[w9@8T߭x7 ߴZnƎb2?}sy|G^OGP?lɥnG_әەK0v_ٚWx\O-XoyXN1=4pLh P$|;/n+svs* r"k {SQU&4/׫J>uBhov(~P=?"5?==d/~Ps ]<^Q95q0bazgCKG=z?Vnɇ֮ipcfr!$%B= TH(OZU[_ M~lhT"Vί٬C_ &te[3@/K H_w `b|c@mIUp  Bb07 yV~C~-&;QJ.P0٫r2BDwOTU} (8'JrL+5QPZ'Tޟ gM࿻s_f%4l)Q= Ǜ|V |k16`?WQiV{9 j:&_AyVYh tt«5WaȗdžRzfPV <BPg{0d3׾f/,Ue@ȁ75Ra.*зDdK\S gYOq19?iw n` , ϷZX'@YA~ZfMX:)G1!ͅpk2? ߾Ce~~w% m_ j@HE8ck9J/<ɿf!P @Te' ?6KieCEhS:FCZ -L] 1cpn6n%X0N9?D @O @/ y #4@hU8@<ƒOggSW`3vYS&$~p9q-Y(t+/bU\?eud3c,@ܜ(<@% KtUp %IM963,c{{GNBtRETP' dPd(TĝPPt{K@ߨ*`u_%*+h؃IG(ZaCu{sx7`4gv]*Y` Ī<VŽB""\3 1=-{mځ"Uw,BJVOWj-t-8E=w;F} 6-<`N֥e. 0ow|]}ar!-Yv<P`d\0~o'>8wnk3]R|`UNHyZ  4K ħ.71 U@WoZm=~cG2xLT&,! J? H9E|2Z~6j& rDe}AkՑӫ+wwvj™zJ]lq̷<@[9r&&.o@ vK'~~@W 3SH}- \~54'Z\O3DC#z"xEdS/ͭ t)l;ࢭ/ ۰kX-J-FY1]9ْa{/adyzGn9 UADeu3Deo@_c/2^1bjpa(~khP]kILpHH !X[_I$V+Pb;X/d4>>mY!8B~%~G[#Wz"̣5( #QD_} Dβ=&l)fnΔ9= Ηw6_Lm6K g@Sϡ`YldK'0@(Bz ~FRR -"ӯP^ ⋔re%ǽEL:Awwjq x0:tBBA4+&g%tĺEL:s(?֙o{PW=@=xEqƶmm8/׆z-r%42ޡ aLeC6 Yi``_t$@j|a@V.L ƿ|o )$%`g&p ʭKlSP(rAPx2W[_ a:U\d?pcddOsxoY^%@.t)p.1 N!](κҍ0ExlmEoTvPkGrUUm:p 7ijC:| GުMي!p] ?b<_n+\noONāC4{%zCT+lj\ϴ0αW_<2eLX 8Fl+iC`z@蕛(T$:!ax֩څ cukzx 7K@H3 x/4u-00o@^ ).`~-#_.$QXl3U Z[nq )޺-;2FnjTϙ+ ֯%?&zxp__"0s[sEubٻoj CxUsST|*K3twS]{zoUZWE}P}*"6:@@,䆍9Ν8HK>(`fO/{b<L}|%=:g>a?4PH`,@OBV!0 l')Mnثbt:$6%MA ~$Y mrH` w_ {x6Q[^ep.b!¡Dbu xײI nݑHe)E@P?UsCBAfE PMڜlo_`Ч/ }F p_:~:q@VwHq6/tӟMgwUT{*3t!t`:Gw ` C/No{ۿ'>}bss@xT^T_, RW%U9x7a3`,-=Sem<*u+r%!*D"H^Ƚ/zȫ e_@l =UU1Q0m؛I{\CKLqn @ =oe%2vYǎu#` qO^3D!a@wȶw5 x*dq93=k`Bh dT>? G OggS@`30"!+6:7FEM 2`@O*F4pKWO{y4hVg|bG{g$L%VHr4_h%{<~n+7s<}/f @Y֨o %BiyX_oi *mB-ЕL)], ΈDZ%L |&$3Wo6@VXQIP՛[ ȏ~A d,d+)|h-+`(ns L# ޹3Ds* cr'rykM`!v"Δ<(t2 4[ 7p 3@\z='FMwM 7!GVlCP[+B0T*>"\FA~tM1e8*E_؇.è@u>l&?2=߃/(TIh}̀ ;= x!H`@,AN\d  _X:JҶK9amէicckG`on]0yiϘ;uO'`#2,v Aa!ɧTTJUvtK5VfgTZڭ3{ϐZ%mvfSY`>nmaRhVC$I ( Ht+߶7X]SDe}Dk}աHƯ?t+Q cx7^o׻1n5hfbY9:ɥZ/ } nfW<5RL_}] nunpA (_n/a4j cOϠ!SV&_56fL;u k*axV\|jC7\BM-@6_zڛAB/ hYuY;SoZU\;)(KQkvSuG:aަ,q<%[[_ oZg %[50A.9gx~g{_Yoo$*$Yh S lCBld K[_@|}^*N%͚d}$#+Mԋsvo;xKN7W(ʀѿi  W’?#g )ܙL4Wh[cGUyy F$v$M ~<n` _iRVN:czwRۭsS.>=L!Ao d+l?AX!g٠2e o9БW& &^"i0XwcM6 <"[5ȍ8 Q]8|~4G?R4[>*ܚM<4`؃[Hj󗆔veV{hx < %pxܨ W%k>}: @:w7Bk˨x`VZ o dzj:BD)X(@Z RZ5A<\B?]S;{"fiD#'/sU3c4[iޟ32s3CЀ ,  j̴SU2)[ߕ+%drLWDKVWb}HOk~<9%X/^0}X0"bO}֟TxYidrN3i) 0uA:\>A9{oIGf*:Τ:܄+ÁE/K:$spfy-,'on X|E?=4PnJ97l郍@@`Vh10(N֚h=0Sn1ZQ.b}!kj^i |{6k .<  q 'v׀) ky'TBsJEJy;?z{W~UJP)˫(@;3@>}2v hC7.iG dӌ(m?a8~@OӼ14?@Cyt@Р6o< nDh@3UX M0  5Y/(?+.䗒xbo>y)|ɾ / 0/Ax4/Xp@AЄUZP~Ͼ|9g|p?-@2sgVMCۋrX5Pa%E JȽ*;J#c1@W:yFVx[Ԁ߆ v3.s3O~.Ss /#5uU(ПAZAr^5=y$]VU_1CM~)bч]a24=aG^G =ғw) }ZMlIIYpLIQdSk  $OA3E|5)3E"T+F og싡.3 @ht}-؆gDK %e0lO { `I@ԙs6K"tf1ڗ`{o@ͧ(`ytd|LiG#/@C8(ajש;@֣$}5OB7e;ƃ%(롕tW1Y}vGٵsHd$Ё \~,HE $6d:C=E>CeY@{ U Ha1hP# p [ΣETKU Cw :Am"/@.$ϿP= 04(<ͿuEr`[` s;OggS`3.#   5::98>3>?L:0èE eܫ0"]k?ڌ w&k0WORcua<;;h|<9sA0Ag*@Oj]nf.#rTxv;Y #7ĺ,sJ!+ @3`"Zx(mk=O_g?B63fط_$6_OwI`Dp>ܐ>vݵe 6rBOqm#%C$v9C&oif*#tq yٱs(TP%›"pۧei9\',xҺA~ًgO! /;yza{6БۢP&ރw~ l|x`|6)jr @kVng`_-v X&hΆrN>2L{@d  t} "KʽWKK}FL_ 3LP{wʜ}oTӃ <1F Vd~ moZS;?07qyNk~@ otus~B3+@,{|ysYJtɢ;]vH .ι+p/bgY|JٔU!?0 x2ſVl/&@0+jr:]pO+7,rUÚi/V*C6.ο;4>@P=Q(Z7# B^3=@`=@շpv΍ ~sz#,B > @Q'@}D4w˖1c*o' 4vnfЖ\x8v0]wp=X@Aw+7@f{ y*XRmrn=][ :ܬܙdfn*&}@qs F83i62׵2ܖ@pb#/. 8pP 9@$@xE=&#.jz`gpBVS/\e ouB! r A>xhbY]Lr5wNؗ1-9 G:OBHL3~]P|cqgPT6ICQtDG֠->eW-Jƃ630.߼~| yn2PůmI +# 6Uߏq` ~F⁜ވb̰%\X@&?pzgnc~m;* Ue{ .Hdw8~#('gJ{0E!h{ @ :&(Xwz%4u( 4`eKs#?fr?4(?-xȒ'x0~_t(L3䣗 }c@?@؋ ,+og) IZX\ܾ`|{&F\ų t@i3_$ 86oGsxr.>@H4I.5c׾0u]&Np[PZ--@݅w?d`2 ᢄq#e]Ƚ L)@$I$U5}j<y30`$@ hptGO m_ZJ x/ @Qie=&&1IJ|1V,O迤Yi`\>bvo@6L!`~ }zզz 5< rwO _Þ[Ȑ7tO٤Z#w>+~G5&`"ý):o( OЁ@/xF;ǿBA?KiPTTT-UD/ 1@~RE!G,hgKvE+[UGn1EdQMa=7"w ~FNqUdCy}5V8o{>?Ck[R:׊{!+~)vny}/Iwkiׁ͸R]eÌaӌ;݊TQ-`|ܛvw0/p*&~|5NV3ɹ@Nu6X P|<to `8/E"R@ m@l<[EߛحM k桽>ӧ{y?߷TKTD[v?ߛJC 4 ɲ`ҫ{1^&xcf:TtwDw}Xa]$&\y4SS%^5?R,xތǼ{zXͼ 8{5O$i^꣭L;)nclm^8@}+nʹ}yOÍ,rc'-Len{w{+_b:2jyj1~ߋv·UỡD[6oEۛv&sEh _YsN8;\c ,If${]Kv/{]ߧo%5򥴔;#Y(<ٛ jj/=J< !d ޿|ιtm烏++W@*ђOdDwDT"(j-l~An8~?;7`LEδ`P`zlu@zig{UOV6 쌥#L^j:&FR +W`23h~`f#DX|@P '.: \XU%\ Klj@ƣ r*LeUhʇ`6XS7oH̓Y͏3  m[}7 -@]4P|V 05d[RNDlW /\[YHup?\ɧ VJ(P;*g%TOggS`3 E!  146=HF>|#J.> /y1Ld~ mcU  @Ή~9A[~qqpk`eW}a56_ @'Ht[w xc10]}2E -@x1e[N:9Ndx1K ɓG]Z cO!9~U<{W7s#>aG~~=Wjh =3LJ>@6(\VЕ &)0|Z!-Ү`2?O,>kl« (!,0Ξٱ#{5v]a`bJWޗ|-׺uمTz V5-w .g>ҨLJNw\"7ZƭM5@dF0Sz Ƹ_ WP O@~!}W%7`hc@TUQ --  t T}]q$J!\J!wJ`(r!p>q5htL rc4Le4e0K/Ky2}l&N @82;Tw Q`~Ϝ6&`Mظ^uaaym@NQF^!dʳ>go̯?&ni|wW=@M}.,d\BP@߅`Zh\(@,8~%?qSWSWE_s!A{A}]'̹vkr_;fwRgLTB/[Ƙb9+HC5FoyIc~  0ܷ~V<Ԕ': s o̹| @)"/`|1O][t_?/J?$zYGU  H*QJA iy@5C?)5#_2g&X; ny ܫ}%x[D|9 `sL9)*\6~G WĜ!(vOPE¹69Lh#pcvZ:_MV,3%]imZ;@g̱QX.Ckw  ~X&صMwC6[&)J)Ԟ.'wm pKc}q'[c -hg1V -%5YdBXp )<|Qy@ &v""FRAP@'išQǁ;2| Vu089X6zP Fw@z_( p <}š6MD,H# }TU~'ܐtJ$r?\ 2 9s sExx [[pV@{ywL21V;Pn `MX2@v+_gޟUxZye"N&;,ה:Mh=+ 6p7~u_)̹ݵwX]S9L?xYZ|UY P m OY@DcSR/h=ɏ\Ξ ӀY| b#^!k fȋwX.^)2?xZ?x@шT t{픜Tݕ7 Ձ F-w:3O~+8'e+=7~@߸A,& [j`8gN` _o7ll@% (UUOVƣS@$ȉ]1870@n4|r.pdc53$vnYIsRa>Og{-(}-}O;V-@J? 4t0!yND'/9x57Sٓ 7(@?0x ^{x_3Ko~% 1! @=(ȠU6hgHfڃW &%< o0g/@{H.ZYBZo 8j x`4PO;/N׻p^ތWd`淮G:fM*v<://>3wZ`ڰ=~P΀*{ۗP)^7 'PeT 2>q4wMFeupQ5T `~(@)ˇ<+ tw5`?xJs?0;8UEH@ʔrg'mo֯7V%gp02| UWUɾ}yP<-m3MHzXztپwi\ken@~;Oҁ/ iLqjAjI ~MPW%UTL}%f_]Amgn)u=yu_+ qb -vd˲<;>qruDz Mu}i#6`Nx>p\k+ wmqqzaЊc Xn>WDUUy}?n97yV dU_w%$Q+p5U5lס4|q ݩ^;U5-ﯞ[}~Ϸ-%8W'n Ğ.y-&Z2@' ma@R4oTQPt^A)ú+x>Kyf#0]#Oit-YhU~G,H golOggS=`3A5)2C86-*..!& @Wda,Es? +dE!4"~d? )&i-M#P$͏gc;s-\FdxTs,-P9Dw(ޒ)6Aks?P]gd{}C'L|j 5 S e_~M=j882.oHt]~Tg)`Yk)pV[NX;L sp_nJi=s ץă$& @yܗWd.j@ PF6!>s PZMJ[v ( 2] |jPAEz# URQ1ֺXPdaʪ0"rD>mKXg< uD53`((9;eQg (ȹ3hbK-h]9ktzv:i负Worڝ!u R7oC7sy W)>X@w=0~y/|s?H}ML 1=tUe޳b@'fS-R9 TΉ\s/G"04$Ds0R /g?.}ӷmEFyKw@&o7)$@L9|MtF@7UH^a| \ut MT<_Ah6?,@_L_S2F5ɞ6;뀁h[g"_=" ߗ @@z-F mZF i4sK0/^+ @¹́ۉ{; P9B8KkWg׭.aV3V,-|_`f]3^9^aRPE"GUP2+")GѬ weV @Vq6v@JV<Gq(H?#)/_H^ ƙ1-4n0_sѷH?@PxvDxӹY?S7*x:s0H4C&1 YT+@ݽR.scr&Pg9&(&L_OumnL = _ fVj*(wE (;O7n&vj*Ń+́i{ `Z"d+ %9{,]Rtd`y6 ~ f)]A}NhnA6sb`|9pM4qt[( h |sJzK_ђ.3y@\F/__ E$I<PZO7U2}PEVODB#@iz!`üZ܈R rۯ 2@!}90eȋ 0Sqdf(Wswz {߿o>1]3A: 3O&%#q׾ic[֗oxP;ex3#ʝ@rr@ rs|>@^%U@؁=@ *Fy.P@x9םN1[8Yݷ=J.KM'(X5PvˍB`6?\ }؛Mw>ـ91X,=K`_2$8CKMc>9ā r$}j BBdW3 898&sK)B\$R͘_z%w6-@(?:=_Gt՘P+ ӫI"f^Dq\~ڝBn􉻷o%e&a`9 K|ch2axti|k_?t?t`o@w,~%P6Zm lgzj"d_-qu};.a_hhy78spUo45EVQov0&+ h<7 m{Y򄝂|Հ̀uΧsk\\ZӕޓK.!w:JN[(ygA]D@ v"!n& d(0c6' }zA5X8W5@;x|- `p -QZA5 CT[ 41 1;D3#:WwyU~   (A/~'ZIQPaQDT5|r.GI*7G:ĉ[wfc{=?@k&ʎʂļ={ PZѮ|œ O/ZLϿh@<3` @(c7x 6Ͷ^@%˛_hNktn DDS4sp,Q3q>v"jUÊx,^M ' }өñ-UC=Z|" r*(?RTue4q-h=)@jj;?F3 ~\i}wAd%`h@ & e::wa OggSy`31f `6. D@^مk8 jȼN=ju_ j6hܙbv<Ӟv_8 W A!@Hu -Sq>&ȟs;#L[x(t;ЦOQC%ٱA|3Oqfɿ\|$ 7@{3o??y`ơ@?ׯ}22_`f Ww7F9 @Ȧ~U le΁ rWiҔsr`9x9xnp!f&E! ޕFΚ|<@W+TiND" O(/B(Uvv!f̯LTN i}g`_A` . @yZ[)C{>g |x \  $`A9`-e%}?&k[?}tFʰ]/(b"m>$q΃F{y EDYĎKF*yרt/@:xu-ϿS dZcPOU$U@TZVn?c~w]>`.B'`5k;V6я_3gHyW@Od @o6K|ʂI-Y _65NoOI 5n<8%́9q/nq7 Gl J Izv9@<vPU]DUE@L@'*1@CCQvLx}/(Mtk 1sgG7>)isWkhDGΟ}zq|،;3yG /%H-+/ 5'tyS+W< PdlcbKf?{9@9 @n| c "(BCr_Ki2)PzEJ蟧B2ƀ=V %gd r(_-**8πifOow0LSʱ?hP6~xО>uf=1Xlʿ7|ݒP׫ٕ2@B;.s(3:$@ -`zy 9P:sṕ8;΃?]DA$M /wҮF7)֯|$BwWN=xrrg75eE?La]Ty{ py Ci^?Цgk x35WW(8Z 0/FOs~aX^ ?r`*  ("G@ ~#Q t@JoEUI{x5!s!sELe<xox0)TIS98&}Ra)cyd¦#w,ລ1߇v|C|,(FR7.PMM31e5LИȅd8j@Qh,Z_ozw:WQ nQPc-XzR,x00] c>;QʬѾ %7SrbE@7{9p}Ń| @ 5!(\uٜ6Òs8f(E|ki^3']ޝPP>YPު @X ߭b;<_svfwk_`& `2_1ǽ{w. (C6>To$KT^ՠC gwPk\9'ƸNPd'0R@@4(= x{󭇞\r|&=݃ܙw]? @*cjȧ2޻!iw<qo(2\\3@3fi/;tp~ {?S`PzM4<6` . @dI 3ڗ-ӄz;b.zWAhGZ&g=YjXn1ho p 7\ʉ?]|﶑3X>7  ZA!@P>g5 w|.ln$[w(M-g{`+Ps $RKP"ח.|K--`@tZD@// )d[krh39-gI?[z( ?CN!dN>L.2/"'>~ŷ&?Pû |*͉Cbɢ,8 @cM~Ts0%Y gA ؉ڲΙ"){|o2'>iʬUK "0!G%‡J"9_st:V@eW-&&`Z{LVKp )p>n_߷hPRɏ̔C:N&P_O ɿ0~`+ ϘYC˶ CQ6-51!@+mNh T̂Z ́fv8ur~UHUnFllyOִebS7G]euZUwr `L>qw]>A_M<Uefg_=K۝B.}_Ƞ` @oi=tS@s5/=`߾XMwQz7 @*[ 4o?EU@[6Kђ0&`pU ޙ;7&dsTk3IuWY=֙kp669dL"MD=r7ouEH 1x?=#G ͭjE|π+3xnp?f], g3rqn;xс?,f07|'lO [d$ ge !_R\ ^OggS@`3l[\420/GFI%(3 hWU׹w%k$뮰rSHvFܜTs8b@ &َde~lҽςc܍vn @hwZm;FT&E@ntF^p#?Ly:+0|u8Y`:_&Z3<`1lyc0߉}Q @/UC/&0}={Qm ` }QPT)Z@&Ya eg5R-sLvβJb+GӞA AU@,P}}C9sI_OC欨ۘDsn e_L::=X7@Dg@>p׋]xGLf`O`zYn} Kk4PkZlۧ`͍0`@8̷ϒ @w8y7 JZڶq aeO'ͭ~B%+,Z${97;(}~J [C7:8:o5O{>>eՒ:"8>a/k NRɿV4h/P͏yJ66S1>7lhũg::W!o@  ʩi:O-ʈd0"YmL&jlCh9~)Bxg^>r0u˅s/9yD<@fq?쇿`;ߗ: Oks4A.eh9.Hm0}+yo0:<@Z T n3$BhؘUL|Ag g RԮ!zDc5mǎ9 @488r_UBqon?̭3hU_?`_ا:s?=Q斂I> o~ G@$@ҳ 7?( MA?`|XC O+_&4 -A o}Aӣ\ yYېy;>`tǫsNl΀ΰEEs=w}+w|>WBX[8 au2[~電Ȕ/:Wz鿷0s/ _w4`D`}Oo@!_IYw]IW]й @@WEfVTh &@U/(`~eW?MbN 㩱wCkݺH߀7Ńs!H(`xq}cW͡Dt#/6אc'm1kPyO`g8on/JgE _PlpɆ [;L 톪Nhj JKrA>FT}~e֟Ds,  I`#VFp\Sb7 $[ybr>gKACz3vONߨůh>Yer^|mA>NW ?^}8-$= @'w57n4^ oY"!_3gd@7|.@'f]T4E hul@ >zZ, |A{Z-yNB#+n.Ή&< v{:7&l9ﭽP&~[|g]v@ȝ_{ZuK|ny|Y?v!YOiȼZoS{`*7πLn/&:SbDq9 @.@`.3@aA?'0P/ OJWlNJVF> inyeq5ؐZ7(^M;&o:|0B@(ic9>C:NoLxύt0a%y~Sn?wt4s'kП[y0|폥h}}b{<4 1o8 0ɯ?#}C E* P";[C@0vU2nx{LbYn$$F,vhJҀhC3[BTբd*\Ȯf{NkRKmKΧ<}Vo szP[Spo^_ǧ 3d-EVo( PPnspqk ܠ73C @ omK.x T{ ?^ޫk>Pv%A96 #4,f 1We&.4Spm'+~F T=&ٻ=)u\CUrZ]lwQgT4p#H壄\lgl_0U'geO{ H^v04קRE{v}8 /Ϧ>f_k3}(K?_mKD'N6[˸u&ޯywu 6=|qˌ:OO]>/ncD*V \0\28S8`)sEV*qL+)w? n5#dת`N› 6  ޙe}]Ρ_h)*`X/1Y0!`ȉ, ~XWJ\k 8Q2!P&}P6I)-O [vjW!*q/e @] PLX pun͵2w!9>Q"UP&C"l>ZAHsV܁ݗ3Ý/wP7p p8n F^?@P^g~--KMrYfLh|Arr 22ىcb[~(bBO X^À7t|F|iڇOl$AȜ[;TCJs"r W W"&%X )8œ@@ KӰ/`OfA,nNwA?I9AcW>‹ J+p\>B5spbd߂_:cG @aɤR^t#?Ϟ z _i &Y?<ؾOG9`~ O ~6d%x= /^G8𘅙gFldE|AR޷B65;_Au@}u h<Hg ~6ADGևvL2+ yhgQP53\Ӈx c^)d[<_^;aoBm̂+TD* wNGpLd?~۰L9;\|[/La (٫&eiyf>J 6syM;UCdp[|x27 wK[J~8L+Dm+? 7/ 7j J&C*L`7WxxPG  ԟ=Ji'޸E+T?,݃6P#}5@5]cd/5`9?>7 h#&~` *p_d7J;E XDX@O3jb'me_ d=c7! {D^+O48{ˈZA#Yr'p_4yy`Jg@AO2mD1 $ ?e-]onQs DX*/K 69 Ǔ\BcUo|`}H-N)f'mss(d4s6ߺ>_i@4I<,v5R<Wz,=W LO= D@EuFA$To*t@Vџ>[_\Ol<|| ԯ=B@@ɕWFj4g+ KEߋ\R0`f7D Nw5uzzA |#1yģeNd6n 5J. ~B<+Pzo a " ì7mgsǟ*9΀*({~) |I0 7EWDKRIhZ-P"E?ڳZ?{mЎ?5H9*EhbfZ)BQ}N"'W n~|ϏS =[$Ȳ(*SQ:NzVtчfP9jX@O^w7􃯿 $37>ћG~+@ɥG4"A_/^F- N"odDXz_ (j#Ӏ)V@B- bU|cPL7пʻ&@KkX Wf3`dfȅF{SznNGUg@ $ArG*P~k0@hU!@@7 c;{0 Dൌc/gLϣLd9&h00$‚66ιvw Hձ> P~"&U/ ag@(*ô.S|] r>4U&v& )Y Ngrs2t^ 0@% Z`揺?D)`G 8r[ɥw('aAHXt) i p } @??-- ~OnG HEjB[gk/3O VU p0YUw Q&NόRj x@ P@'j-@px@\@[2Vn80V?/șӾ_  cXmw?Nc zV  ,ು&ȥO(7pw FAݏ=l؂N?2` ' #?,fW*XÏ oI=Aɇ>b" |P[]e ιs7_aۿP,N˖ gFjLKpVP;;rf$􇅛ar^ /fx0hP IgNiPI%W[2꯷dP r"AB.ېngk:Vc6'"VP{^AՌ={{;drykf(KFsA=B 4uuc>_uɼ^mC<L0_<ж޻M,@-d( @ÀS}^@qȀHQww ;{"@˻AvA1}/6+_.` 4JWru%`mGbw#ЊÏ N ^'y77`Y4@eODY@<誠?.}&f )| "浮?:WVdG^X1%PA(޸5 rKpXW od G‚8/p's `xxL';wf|9 h?brPB9~E8H g!@7>6תK63*8uwn{ޘm@.A :._< @Y+30'NNT@zn@zK7tw /_yyJo v.?"K;āoK >3-@wȸϾϔz+sB9Tۃ UiӣGPOȹ e.OB[ <ԲÀHycR>_R  ע0~NKDW|*1}M@_P q(805P r8ew E,HgeD 0~O @=1[8-Qiڴ's@@  &_O%3a O Lw=sH':Ghc@g[7T pMjN`sJc jRQBЃ -%, 8R;F$I}]AM#<UPEб2#0@%Er!,3^T/1AjغVjPO{ty ; M@=}16:O 1=.8? @ HM䒜raЀ _= Prۥ@lvį:ӯؓ `/n~ % Bt (Ch_AB dx;&AD#sbabb?Cq~?Fo ;5Qcխ︾?ڡ5aa織3uN&7?)L9HVm:B`΀hs a`W7#$|gx i`9Ȗ@R^':9ɧ%  ,[e^V}gW0p=ܽ#?}]\+`TWs\gB bۏM5~\tۛu*:;&p֯G;^rKs(@}d<ۧȃP|eX O_ܾ9+ڐ?ˏS?t2sh÷"wUnXq ;|@~C-^|w~ۿ"_ r]DۍȰEen֎Vï'q K:S1#?0;|~af?}Q;|z%uXe]ɢMܔ}Є ýNV+ڳ>Q=O9Pjc;C*1P#+e|]X?ʠwvi榇M3BSe@(2]"Kn? g:eS(n)s> t@&C}xruQ|Y@m.E寡`:{a0&rK{TJJU,} _W|oS0<"0A2JևBW,Ѻ-q 0}~^Aܕ7hx?nΙq .p 1ASWprMBouCw>9/;gB옌5 ^޿^BPZ'UZ/ ]OP:3py* ֯ {L0%ߤ`"W %)Wjpuh}Ѐf8W~E(7 94%sxNł[W F:P'td/=@i Ʒ, S |z  (!m%" xSp  ?R"+û%XeᒚJ)x՚ zvz;q5< P\.Y s?ClJOm3s C!@9%عJ2!qG>-fW(-=rK5ۖOK?x`:v>~ g:g@㱩u s ;@h,Gtg n c)b͒G.oYmIkKS\bEgD5r ]i0 "D})B0{1u;p0H}e.}IYk]h &*#G] [ǁ'A ^e X\mJ)Z7ޞ{ `_6@HmbOggSh`3` #$ 7888:5FFE3' >=qK -8`(4MTpکp nmW5_"xm'/r"67H e{<`+U:+vWA"+ tB| P>;\ds=a h~(gBk6 h+EQ@!|#Pcy 4R0 @ 5} _~N0`O@3U؀p )U]$zᥟ5]ֶ!gD1;wm)Jcn@QQG@sr /ۇBvZB^-'0}9dP( #%4 %%lho8?` m׆ d}ypN3εMun X},Mȋx *] ޸ZLAk`{_Z!tDY9ph9z5F 4;& };4t ,r[r.7(BxN":6'MQP WUo|2kIř~}>o47z@iW`%` ҿow*tDG~/PgG Se+[񜦼.'$?ʼAYceboӞ9ocB" F|)O1:=@5rT ¹X+ BÂjWhVwh=pޭ[Sw pл-Z 2:(` "?q@LL X_@q@,<2˾ !QlVEmgբ5n)w:G\kRQ @>GfL UT[Z<ɩ$z^Sଟ,bpټ# 656EpkO@y&B;J>`/ Oϊ|`@qiOr'͟rCb>L gs|g! onǎ-\^ϥg:7 grLO@07O-K8M Л%$OU HB/>`^$/~{S[GCdX]x ?.%O 66u:!Lk>P]I{3=f_ P=DeIL5&↷R{^p˫eoL0nY@ܖߺ@Ot`| u^980ط9m} 7_ ={<(͏므2 ?5ز b8u腃*t2إ/{gDotH ݙ 2<@\P-':vnK0ԄxL ^Ǽn Ȯ!3^EqG+NtoE ;Hx|xs%CJ 7EKyǗ1O0GaCH /~}oys{5@?ug~t5KR,߲ 2 o$@YP@P؅CUO-H/yoBn@9O?R?aO r*X/]PڹJxAPSQb褍*ȊI=ՁiX H>xzץ@O8 d6&CXBQ@UdTQau~ 3i4F\^\;B؀a_xvA@/[ \pխAkg|g6e{; ~04@U@$1Wd` zVk)~LO 7ѾNԾvG۽(~_p׀ 8Xw}<3xRVMŦ:*`&ܬKo0BڧF~ȅÞ9Ū^Z 9 뻟 pp K\`@+@JT'v%G4jg,\ R+P~wJWFvr)~eUx$1S".ϗATT%"jREA wTb ɭK4 `g'o7l` 2}V.P4OggS`3R  B K ^CR" ;E~fؚ֧Q`u7pNAy:0w,!ӄo.`י"y<p;ȡvCGwJ$Ñ=j> ;z4'KlHՎGssN>yISr,Rej\h nH5 .CPb ~a@`k Q΁>i@x^@F$c5  /o\ 4` 8AMy]pBFKwŏ TdlQ@X,lX;@WDsMm/撾wrKA<} nݢ<tD_^y5s6m(90?K@U `OH[/(^J, Z  UҠ^a{O\8{`{7o h:<`Z)EX*"J(5;|)Nl)K}zJ_{V`k4: 䤹~@[Ǎ@hBf~Ɣsi)=ko¿gcN<@v~te jw:Zy(@MbOsebs*~VyZ.}՛l'f%Ƙy `ߘfwz)Sդ\52򜞫>E jN*akJ~=ao{̯3 @|[<p@7S^R3} doQ_- HoBp4)h{fI-jeVk%C8ˮ4ڦno(jӆc4eB2c?2@QrDz,Ϗ1~6?/ܦ8HǛ_}LS4>5@pOh 9)~nA)mh S@/K6?ttI$'/zNk>%T[蘴Ew;BkW)r>_/Om9YZ(P(bD s%|¼(zN>gU'SR.vN$)kxҺɴPdp)^4 ߢiK3ɹWsCZ'T2w0F5(Sa:g7/>-ik㼜P+P_l [UF*)YЗ (,|1A>Ew|.b2}{l(-yD,CBlƬ}ָ kyofPoV<PΟx %ygj5`xi^(?| oΧmкi`oɇ%р̽I,?yc$Fga|'Rlq_*JHy-КeteY>b |h h6bC%r7hZ%il?o?[ YxmvDAu49Ӭ2SaIX#@ aox@}<(7@F@։()X+i6f0}{/zV }0a@CɉwLӇ*c-Ѵ@R%@+@>KU%%~UT>; F20D*q (%-_\-YR}AbΈ`AwͿsG nbm]@U ^O6e.(Mv,@`7`)tP˄'x&7^Jzpts76O`_mAHw?dpw? OW6Sl2O >|F&GfKZ%p$~љhV7&`@%{skhsz [wsl'}Do+Z<tPڮ(J~ 7U` G8H;.-p~o(%c ; 4\AoQ}7 4P?>LAR>@ߕ* ]Ez o` @x|,`?U]ܞD"j/x^pQ'hOl8r8)RP  Wb>S|ai)ퟨ[? H%tǀ#R2*獁 [2!@;[mLC@ise4O cy60l8/]ա/f/ JX+ 6%}WS|A m8z_=RejﺖWs0nW&qڵ*<'ܡs2$MG,/]}% 3Xg(%w;Or=8?oO0>! ڿ~ =6p?YV`n,* /0w\P`a @p9n!Þ`w359P=[V:20 8Ա9_M%\D 'E"Xzr\QΝ3`\ʘ7v]hݰla*%}2s1aݩmX[3X ~ PBfW7~ۡG3ߜ(~ /SVtiDla `s l@ >x%G@ҼCHp9*Qd2,~ngak˜\ >[Laʶu_|_~x޽77D-.J؎ܿ{8ȴ wӹ+C;LH*cJuX+k5 3@jد:0iv;Xu: nPeLh2&= H t .l,Ч F%N4G=JeՁӏ;>  S9s,Bj9㙠5vE/A֧֚׭!yȣQ(nN | d`J~ x=zw$splσ@n7SߓTo4: {>K7q``+_uSH~*00EOggS`3XV "101111/EDH=#  ~wU~BPoCp >wT <T avCjP4CzGci:aD9>n:dT wo݀ eV kd&r] \GZkG']hiþ}7&p$2My c)T H_#a9ER B V2_̯ín~1% ޮ  9W<:5xm7scX$!@ Mfgƒ'YM߿ @u/Vn~(|0sڴDZ3rc߫^ًlLJE)%zץ@Oq}pl T5ndZ`b @%@o `b5("A납7$mX%K&/ٷ r RYTTݣ^Uw+˰]>Ǽnq9(AJx;L߽(|BFU߮/sln4[t ߰osסMjVC2>VmUYB'^4[ffKfi(,嘃W+u62/L{ QFyv;tLgycm٨G-ɯ*<۽~ϖ~{wevӟ0~T!V< "n׎LߧU߭ǾTlӏy>utI7 M*k1,߇w·".zMlvF0VD |u9|>nG_Lݭ&^߽&l,? [tA*JKz%%:&dvW@_~g}n"5ӕ0,ӈ*릟 t<]>w4Lr0{$k"z**1 owXgdHVKɛwLڃuEgy4:3D.Tm59`i;0_i映S; :sߛpyX4|wȷY=˰P/C#>Auđ S#/: GI{p'ky6F~%g>=ejGG|hyA hUZ1?ݑ G|G+? Nj1KysEEm>]ڄ䦣+3\䕖E@̽{u6qCm\' >V`rU;4ݼ@HMGA[ KKT-7-@TsG-]v)LrPgOw8kXֵ`MlR 87e 32ۿ.FnzYi?<w0tt3O% N@BHNҭAh |% pm8J>s;j6L `t\ӆtmnP֌3^ү%@qo.۰|_ v<.иBě-BVHRHwl u.lcnU"l0[)q\F#򋾻3|pҖ@.-1;a-$1t-DP@;y@ O2wVPE@UH_Ch $ܜq` 3 ~+ z ^K= Zjiz (Q~@l0!g-4> p≮qXs7 +񇹻 oͳ@3B{lbإѶ싰:%2Hko@{ϙ--#w[ t em7 t DCED(۴xy~\>x0y{ur(pspN   ,|ן%& < ЀL~zuQ;JġYM6Cw WO~BH p@)oX.q*lmN LZJN@} [x@;=2]t!{z#VVJҌJN:DpDQ93 V@Y(V#Gd>( + /͐o0_] Om7h@ 2x~a֗J6> h^z3BˆM Ϭ ?޴ڙHFQ|KGhۿӎ8Spy8.9gL tRm%d?kxr>o옅$nE@Vh.^h?@Wz PD@dpʐN (T>$h[CC5XOggS!`3Ic3?  @z gx_n^zi&fVrь3/$ qqK"rEF;s%foYBX)H뭈SJY\8Ȭm-ٚt !:h:(@AJ& p}sס|7[q HAO%@taCЫf߾) ?kf/0蘭jn n~okYNB |A~j-tkP&5G7Q(~A_p@*̩XcT`U~(DtQ=ی yK-`9C4{ t|]]!<߻ݎX̽VTgT`lW֢Wtrׇs @fo6y aֶs; C_ :6+ Mx5+笖fbBgq?бrC%e߸`N 0sALф$4Ř[j5KM4(g7i\a@4OZ9|Y %Ld޵A@GsUvxd`~`[M.aüS;OAV۠po4L_w87=mj@>8x ?@WkMWh Rg:ɩ؈cZѐ~8"! wkPq *Ng6 CcMOkCM-vq' 塺N3$h6?1Lf='W]c<-r6 t nۜ!~>@$x4VNWLE 94 | m&0]fhP/'-=}2}x;PdhXdGˀ-o"ON**BGFQ|~83>z_L~WtduVO|ƴf"(zo 9j8 ۡuBPlM%1ipFK捶w7Е.$BYH nktTNX~9Ah{n,5_BI*[=Cޠl&sib)g:Ow @N@?&2Pr7 xrl+k:0`R\Ron.j-+2 >w-@+-fvR @\ 8. Mx;!HvyP*5܄,aA/ML >١HBD{j>hv({CnrGLBq$%O{dT_h l Mlq^I@a>r@agǬɐ,`| Nes6P @Q[ w[~ju'IKKgՑ1 dҫD&" 8Oci 1SZ'Ԑ;,ĸUYk_'nɩa6%P@)vOX!y!y Sy}R{nSKw^!ZdFrzN40ocX e*n w#hجo Wr_IPp4~'%I,uU>#R#V?p6 d {x4E{΍Jg@8aPǎMrJRV惎z CAn>j8ɞ$wʿv,i.5N A8{n^*^8L?_ϷVgliRZ[002Ak>w24goײ( :0 O~ 0(] [@7߼ZwG]ԡ\faٵ7:s40'l4AMB8Q,Ǡֻ!(Sw" hLi>Ƿ 0@\LU۽*w瓘i $3翻uB<|Zv(@ w!5@/r|>3 _w@ۯWNX0d$Hpxs_ d-WDO&%9jUO^ QcJ(-1"phuo6`+DA@DCߩo``$-@rt;]k+Kn/P%I-m8wPǵ4~|@gQ5{z~ Ё~v pZ _@_jp t߀}'@~*4@@zujm;폣.tg\2aӐ=Q0ngΖ8>`@EAABB u6#.!.?n-s1LA|t۸s6|/y<]%re{=ɇ'OosO\ ,7t@Hq _;6ٷo@qۯoSL%W% `7d˷ w``TJMj-l JzrC =%s&-z`:p9@ `0A~ ;}/q}@5afsjK62 /?FPB5#@UALPABXܼLW h5lƍ׭{8 2t̳?=0v8믷[j ,nMOt:HIj? _25a /P\50 `)tV-H~F BypJVy{y[J6񐗊Ǘ:G9Z: 4>5XO e|2/@WGOox[u,t}/,==?k}ox@@M];9Db9:-eЊ"_j4'm:Oks;Q2m%P=HB$TpCFܶTu}s\?~f$g;3$} 2_>0l<$Nsuy@9vqt-i|us6=sh Io?kPI$b HO~*AL|OggS@_`3D4 IEH5ުhѲiBKh:^.!s[H}`Qʨ aPT?ܤr"~jhrXXB¸Wx~k[TT<_X{daxϿ`D;tnq|y :|n=`kLػqo~~=c/r^|6!ER XeD? y 6VH+@<;eHX[-~ze+mM)QFѶl->hhKأm\@ A%,GMe.;ozW0M4/Xf#ybW}`,#c寺r-e P 0YNKHp4Q~zWz_nmWutZzm%?EY1 8lJ ")AanMyz ~X|=`  m-3i6ױgQ$ϔ1}"a90e¥'dh~b}yB @H7vi-@~;ޛCA ' &QA"%uE}2 p!+1Hbtzogq|f4CvzށZvٖ ;;2ңvȸy?;Ve[7, n0ό<N|8\`<<<M*K S_@0o;< pp< ` &@8i3X@ ^Z'ޖ'm oF!+N<$zUNӇ;&{"EB@кm!7lÐ ?] $VdU_|'R (}ܖ=Ĵ-~l(Ce1:}_D/Ԗl@i)p@omqC^.M ""nO  ؀@h /!50?sDP6c0wj%_1rJlVrxo2{e_4xsE̛QQļ|u .w>"Wz¹7o`,K2!A0b;u_ g$qzzh6MS`wAzYTUQ(=Oy7P8 )An2Oܩ3*ބ6i@_lhUq !`o8 4^jwYx3FGF NEZ$8¸lƚq{ ^}qg2k[+; KMg'9~IqU<ڐjo}xSq4C&fqwCy[p^τ0 ܄M|}eBl }" ꫠ(((8 P ~z5ৌ|)27݌| [1ϊ#o{36́o (śd7T[Tۆw^Ve-`v lg.m:Ww5 @~'g|Ǚ9#j c0=o2:/ MWBb@a?[~ -Ti d7^j;u'3gM)~969J2[q=M_A`IGaXD ɡq!vtYg͝< :% g'J/XNv$ݍ#oGk~6pf iD޼r`fjco@ 엝r|E@g4{yz!zn~hwkXӱ[GJ;ߩ}͸=1g&p1;}sw|3ۏ޳Ǯ鯜vhrK ? ,[@}g|YHH35A(!r_vIEj4|6=؆\۞v=or p7ԖLrl?oOL^ sK?< -MJ@@bПyt<}{5cq /m't1q<{?fwjOin~ ~rm-xOc?,k#r`1Z4*%j2Zx}rB%;'o<+HsevN."- Ir? =r 0s3~:O ?gq[ 'l8|{l}a }9>a `~.y$7r1:}r 0TnٺE_U~R|PS x%YĉG*? yV2=0!yb-.G=xt%S1B#;iQekCX9w6TE a.o}UܾM]7?z57˲0 x\?8zYnXSyUɋ2d7 p}[3y?M$;;:_4q'ߜ3 u7|0@-jTËp2O 6~_-(_$Kuy]ZkbC ){Ł}q'"SSS׿IӀlTWID[q DD͍aa||<,^m_w"Bo$_$+d;ewG35 Un[ò ɜ3R%Kp!tkZyE?P@TQjul=㜳s} RP 5l3uHZJ ԸoAiF^LoyZ4+#_q6dxcs_yΙs2mW 6ۇgzkLy)yG= 4o~M>\`in0Sc_!z+ˊŢ4 l,`e8VM xm=8) ?W >/Yvyʑ-È.@ ʌEH}Sټd=*'v`?l$hRh썇&JhPK2RmNtPvnRCSU@PM@?g3xqy}Χ/3'[ܸh:9ߵȿ)XYSۿI Շ>r^6_#lxVOggS@`3    zTOUz -\n2:qCWr^sN{'psnpj T3B45g;49`;4i">!/''t NT PM s`6y <þ8=ȷ^& /^sܹn7ɹ w?#~;?ƙU<6z@OM@#nn[~퍢|y(5΁oW4 w#Tkwڔ< xH2Mxq_ޑ7xqr4n.x) tPus_ 9h`Jfl P}(@eOg*nHY#O $y~ `~LݒE@Ke< e->l#2#uI2CnekJ8@:7i9LOzUrO|25۶)ji %_6os v=@ 2i>@( J'`HO]Ӏn2(02^kp NC?45!Cxӯpή\<|[``n~ `iy`ni~oF=e=u&GX_i?eΏ nWs9;v91B `SR#G'P2J n/ _\xܡnl^=h%rro }p}Whgi35?hyp.`|~3)`~ nf @gon ~[~MN20d ޙ=>\Gj5tZ;EYD;h&k<\M;.P\\A,I?MԎujX1g񝃂?;wOQT]4ZGEK>9^Z_Mmz~؟0i0[/_tN'30pX=&>@3nj _=>l)qh$/r6蓷9xy%vsZT@˜ۗ]7]bk ץ(޿P?͇'6_@xaPk' 3k2XS$ ѽg@OzSOgLF$` ( t6_@ϲW=wK o^+0_?g3Kd<@P#>ZX{"V;Gncga`H@ɐ#3s7:[P}u?U+eKW&lph[xBo8cq_?L5X^'zu~y m3 rpi /9CO~J&8 35 >?YX-.o?=  K4h9}ZgDBET *8 ߝ^n5psA PH,#Cj[ -G@0^ SIUX=|nGP=V-oa^@.?:_4͢~1nO 0(o50. HKl6ޙ#m_]fV_{~8hdۡab*[lN k\A W@kMn^7QºSÍs}$\o]ÔK^P3 SGЏnl OMigٔ-Z߇PTswwAt39dcfΏ}@%tGUCl|h%@n`g^&cd*ѻHke8e*!'oE;Sk 'J-3;||ss`;pNwS}nT ̷ui]5 65ѣ- ʾ@`6t5ЂlW܌tYS{!־3跧!VB«jOlQh7@0!oqڃgJM.f%j3 RJA~TT@TVQy^ֈzRG4[8WܷzYөߜ6?ި$*kyh{ e0W@hφπ3ݳ&(|Z@<@_aϺh86eG[6Ǒ2˔iO6DyV&C:)sj ܨ"whvLk. (mg*:ԝ 6N ,B|y#?yQM!s/7>g0mh0vO_&_? dxLӖ2@?-j@TƯ/4P<u0__}1>d88dJ>#y+7?+B-Ih40F@l x?@`R {Mׯs;OVխ5Ӎ)3ȁ[qXG-Vj,Ȣ8yO7{pwv}~fW6e`>Oa_8hʯ{dnp1@ji|{,j(XeiBHf4w&ʶ Qa^kRL\ ~Z5b[mMDMBaT0ޞ!qrs?0h J/7@%9?^"lC\ІsxyVS?4WmM ys;@(PVf8 V2~yGj5Y᠏J#8h' 5>~@lݖ9J#!B}hr!ssxd;ۥ, Ʈˑ A1ȂsyL/R)t >q^_y`7,|zL΀cN0ɀ,dyY7%?:@>8JSA l2`yGZw~p|Qـ8ѬnT*B`ҴΣ֪WXd={BJimy۴ N$CK0/\`{|9Pɴ\0p)`a )9JNu<3?ȾӑϦZ4Ou%@?YQt @Th( rh_OggS@`39i @yg|o&YX|%N2hKk_lS>wϓ֭3Ɩs#^@Ĥ w3@mFI=`E"otS!dv- e\ހ<\ȏt(7a9|&AYVPt9zanP7Z,k! o?8r% 7 EM#B]~yW.fҟthXN3Q 1}[j 6= qT@nڱؿuO9\p  @5hc8 3il/hWh$_p{|KY/ 98b ĉ=?O ud0ʥuPPUEify>`ES|3a*n><n ֝|in4TLmK;y.?+Y `_ <?1GFE1pO>iO Zi'`5 1n,-Ĥ)QyD@^;|E @6JIK\AsSl<\|tw/ @vq><ݿ|OLK N |74U_0 po&|7l*V~%wNR-hP (iOt%o/V +oD`,ado xtud BE2pcp܂,9w&Y@VƀYi "xwgY.z]8i{ʥ p"B L\:W@1 ?8޿ o~(9o=*V%`o +4\;YW|%1@J;P 1}86v%GB"DY㿣h/*'` oцy9]xb|΃~΅8&sη 07`q. _ 3nM/ȾȿMoM(4['` SYXgfd.̄BGwvDuhl"lYOޠUZ+TB +3hoẂ"dbe7MQFᴮ[ P6'j<|/}KmґU/ǾMnmR9}(p:5\=}y'O}50 yO7|V o}@ 0f"*s_ @L6IWl0eN%toċc׽,@c}Ď|Pq 63$e=Ǻ&0^Xc*ɼGe Aᷩمހ9<|l,XY5w<5*S |b]eY?۳X{OU!jBפgfеͽt\d-h?T_ݛ_|eG hoiLaBO*:U >MDT`*N7?A+MJ 륉 yz;0Og@?jQ`T J~ o*@[#aKR%`K2d4TA>9ewɾ UR%j"~_Aۓ&Ang%K*ZVP:eLi Nt x -po :mf@? kC>J//{ 'm96><0_=,n3A)η_l "}yvni/CB;7ecAp֎IJU"sIu^~iܬan2/m*L~i|k;oem:/};P03R&. +#NvsFe$._M>’MG ̐ŮoavlI=:(@OggS(`3 E>?8 eDO&r0 eoXXA^9"-plG~(RhحN#WUZ;&Tg>ӣ<0qNcZ ̜o.adr QhiIf|},& 0nwNPf2@2O;+.IyLkڏ (ȎSF6B'KwVүթ5|<;,ZS ht3p{@~oZ #0/xm$)TgU ROsUȽ :ډ$ _sYk.ZʇB|cmzדݟqeP*\ p3߹>7lh& 7'$&Vb/ZCS]#׎M-{xìP> p ${\aĄMON5??>/۶dHT\+Z-N]i޿^緿5^k}u<|:CKjTeUJH_ DE[nT{b~X xާv4+=_w I"Wz_ժ&Fq bWOLk'x y+PC !*z$[ssos7\t mvO:4N;.FrQxMp@s|&GSúg vSUc+nPseǏ=J|'3 8rٗme[nv;9pngB|l_h`I? Ok69wћSzv 1SPs?5&v}P{ʍ@<@.BI̸T~REuZˡ&P >T&Ria*+I{\2PZꑯwx,ZnUxϷTeu|)![w8,"T-~)HQ;_I1JLPܼ &Jo__Ow%`su@O9mOPVEsǃ 3SD2is= ˜f=n @x},C}nGSSQ@)Qi69Zdw>( t0'ׯ7 2}W:wϷ^Kogm'S$`% "*CT7J:d.0 eTc??DŽF$+]k_9 Ty^0` ۯI@}ZJ76ϗ*,FGr-9@},CƍTkp`Vo"8gqxLK5 h@VL}`8{@m>Pҫ_-@J6@7 HOA@g*~&2}Y(-ڥ?fNk[bZ/@1gM !~y/HJVI_U* d WB@ > _iv00]F6$^{j '/Ȁ+a \J͉wY75BO6dPoRw;3 G @v< @W-i;n? ȓS@$(ۍ;H. :yV N erh|'+6߱p~WI I*)Q< > 4J'@cD=b!/O ;5 <~ \(/ ~Cwl\!P{,i)SMd¤Z)/Y4M~mCyKb ŽtsV'-@PO};7ZȹO '@ʊ}"֡L Jsg0~aU@.Z%*7K @ ^ fh)~lps 8><n/?`s_E4T94:P/;7ſ2`~ (^t>,BT]Uc-f ( {4}d1 0=醫r?z Hm`Q < MGP* VQ>_qZϹ.!L7фÞ@|QY0Q$\`/ }lot ȡCv4#^uF#!5`Q@p_kyr)@vyT2Ve!e0SO R NZJ:R/K;_̀**mK 5 4`^ >5(siPٿPnڸRD ݩ&R\W a?e@\TZ҉n!lGE{%$(׾Hr6r: (,[_^ S s3t.QZho1qW@ 8`|poO9`4`ݝ/'7H@J$ IIP d>J) Dr'4$u,T\ 0ӯySpOӳ0Dw_* L]0c 3 9ܼwxN@&~[U>w&@8y/Rf#,G8%[Sݑx1`aoAZXI}\oz3A ( E** @@>|j. C36i,LdXnyB 쓓Ǚmq';|x;Yom6^ߏ@)ՔLjeS=?19W>G?^+z+{W׭@S^`ҏϷ#_D7=Z 0osr*<\`OZɍS|]4\rT?]9|?{|r,PT@T}Md0ےՀ@6~)ٿQUܷԹ1ЦߴmC`*swMþ`}}Vop|ӂƂw+ Pyb $/~?9O7 @.˹tShUJ-uKG`@D5Wޮx: _,[)<@/9B<׈r|1`k@TKP(9͎7eol`xW&s<9 SSOĻkpo%QϮBbxB/6(@)e Ȣd^Co)x \IC$P>@@nkuK~nqX^tR:wroC͆v[OLʸM YyG}^\NO{+ aN`1>}@lC]F|8ٖ(%)TTT$ @I4b%7Oud<Վx#bn; @)֠= ǘBMR\(F;'#ɭ:sؿJ hCv nx}rx dt4N*#߼6.mnZ#yLyr]E@=g3Q˾i- p{n vte P%%w~+UϩO JBK/,>9h)7 A& !+V /& p;vSTb )QDױ@@YsE xA[ON^a r6 r.}\fꞽu57|tNw(O|[xۿ`:d}*n5^?&8 :zy۟2ٗsw㏯_ ? %PI _ If.7pI&TGsN`e.n|okN6Os(qb!+giPImՓ DŽgv6o6V|j>(Ц7b:?{B`O;L;L.*M fNb w=m_j16 V>I?a rnPYF5ʸ?hrC4(Tᯏs4>]bx'P-8 f e:bc'@fο@f;%u@D>4٪$ZpˆPjyS j@~ysooL^ P3d>w^` 8|E# eX! ^9*^zIg* ~xZ 8"ˏZ0 L4h`N,4{l i|rўP3y(XӼw0y,祶֬d4;90x!jڛ(4}`L:;K/^`44 LL)U0@gD7~/ 赤(4F6B>9OOk.w9FٿէX eM-O>  `"1sp{6 _51@  _6xƞŮH:@(0@+$ it/0c_:rzp5@&SI4$忞dksofwx9U$`T )YR >)nTtϭCk]4sHЮQ 4`@C?nqrS(r#S\,ʿx~(nuaޥ Z!)ۜ80Cu axzqP[?Fzpumh/iVy㗿 s+(X>/@+ I.AݢG@T7~@HP0?oHSK @W!W!*d(| D@[Fx pbKxɩ"-!BF%_O 翵<@l+ ]R.U(b:64o`qIvy;EB;@ ?{tИRn,[ GT寖1 0Hl@v Ԕ8;0T1睗'c$rR<޲#@%α7s P.*fZۮHw\o@5f!6ۑ]ɒaz<@[|25g`?@+'h=zf{9.! {r˻~'h3mΔ ql~XmB?H7%UU (T|2LuN[|=/ İZQ L:ӥ p̡9/_2>fWp{_9}٤S\{41ie *kX b=ddOew>6m"8VZWu#$\veo`w펷( -'g6ڼ5˾}}&QƼM˓_|(N]+2m@WM= %x J SQuK_ >vR 81J]Wuq099os* M}pdS6A4蹃h޾+kTcLp⟐mTPB(zfD_XGY7b@s`e/3%Z( W|YNǞ /x+   @O0{7B"gn~:@m0>* @0r&NNr |@r!Be \6NpLkr'@V, `~Ni0 9!c[su y, `$t5o-,U @^GLC^d¾RLW!<dg;.WW-<06H{5#Ž{RL=Mo/r'/Q|Yh[`ڳefRF:=(Kh }wMsJ-yL/P@ 'Cdb)t591ݢ!m3 h${g;;>W~XnR&Wq =|)=)miK#hB?pMk%_7!y[߷O~ ̯l o6,û~3Uu)qx3@'0 lx.K_1-i(_+A;&PQ0Cl/(;&,މ=uxL|ЖSSgn{ ΦX9-~m -y LW1xQȉ4TBJN='nilP gl{ 0ۋ\fo8eK3<*@~-,) `s_& @30 !\*~O#N'|AșLY2qGKWH 0 q'x7/PCm P/b3AO]^֮ޟC .yW-?v2p @)z!!Z0bo8fȥR`俐Dhy`1dޙ;@ξo 6@ CW &!p^eܯ &fߑsj' E\wm<Fw> N S.`V*9*>xgx'M^*7 SwBe8>(r ,Η=-exPT^Omf} b`~n|ʯh6g?VQY0UF2 @X.-r6u%BOpih@c͈n4hun?u2g~mq;fђ@s 8j6X!c`A ;^  ~uįГIo$P+"OQ 7HΜ \I種@}pDy 8P&ʴ0gˍ?R/7h"WgP/g&VON[Jy>J5cxs @2)K}@Q;3i@y`'r? > 0>/2&` Bŏ쬪RTȨ&LG_X@\~u0A_\*#fP_i%n }[!PQU~(ˁEL2Ok@w%y՜ 9t[Ɵ:\eOg+77c-N~^g@^ ZnEp[fH}U,W(z(Ro[zVe.[Rhޙ0o:ehĭ},'h;߉+!+ ej# pF 6D'C3;^|ݵZ%>ռ6%o=s= q`>& {ςtz\ ̥w^Ysʇ @-Xm =8tc w*y /@O)&L+A(@Nb$>^PpNJp /dBAiy 4'.W-?u<;C; @7}$is dAƽhF~gd?86NCp !(9ʱq}Q//OQJOPԿU( @/VtVj3W*Om2(ovS/7.7'ùqcU w<^+k+c84֌;=|;z,w!$^A{%PjcƇfs 5@/OVnP)p*^NQ}3ߧ}@> .n-6ho&l|& $[c`x1lbRM0>^j5;7?^y ?S\gjW\&ivT@ܗLn Y-*?.Xʌئh ~m6;oT~;pթAȀ_>]s.@7ܜ2_{oA0rVX,] o${Rj}Nlm9ιwۢ}D,`@wm%_>m\nA 2D> D#dYFV h>>juwloBDMS\YN8- +N\\U0q!U!R}1nb%}w3^x9Mj.pyCnhˑhu4|X?ԁ5;UK-8ӒȬ.@<~9@U5Ϥ?c|LO6DA!@H@'I V #c 5OggS`3!6 ^zWiF3y: YMC3$[%>;e)&ơX6e%$U@k;mHtބ|Y vϸ;04YtriWȤ`1Qtma~rg@<íQp($T5TN,| 3Zldd0d^ٷ?O`}!\! ^Qm]lt $爲3uڴa 4>*v͖>jO8 ?k/SI?||LD}ZjN-!T!&dQEʲS}S>d,PQE!*`e`x 4t|A6L: &vuխ!f{_Ѓy.jٯ$oi2|! v%.{*mEkd(A1p^j=A os9'nZy`wmSn9N" ;bT<|Ϛoj\=b-dX|8izWv_p^?owM{3@O R՟_Ξp'ry ߼]4P7@ :,ib-G9oՑZeA4S;|ƭEgyzY Yeį6399ewZ`Ï+ɹLJN@+S5 C27WO 6Jj; &Zoܻe'ЎfBG5< ( |zh^swy~ }c6?'h:Oi;Z 80/gn&t9~a|m Cc,0="uϧW ϧo\FB'TO/{akҴuaӿ Ჺjvzu{TXPI/zX>|zs ,vO997=Kɀ ׳釳rwJc |k3OA7n@}is\2ߪ󽩤*S(' <q9bևI+[䖦Pnm}I)Y'b0YlJ?cͯ<.ag~ѱ8';A*B\`O2M^{[M;I78{}&&ѕaH|%ilaL^ҀkPoP]84ۗe_s!p 'Ӏp۩P4r2Y,YڝښL|ի9\EM5Z 8ZG@>Zu;)&! ;}Py襮c/7@7'Qt "]88/}y<߾Z׉Cg@Q :2Xl6ڛ(tx/"G3O:ۧ:;Lw uж~:8v1'0@jLo: ˧!@ {HG$L.+^=] ˝=j>C .knIu+43r{y{m~WXe ?F#@?Fk>+su{\Οo?o=@~߀=>6c?;K:E}ȮU@<,.2YT/YWXHlvԴ7=#jb0&6/Z5'.ſ.s \ݐ^}3  c_J-amp>Ib'(C&y fUwRitSI}?mU:[kGS o`z5;b-tJ )Uß`8pc;.*#48&uXC졛9 !dթƋd&'K^=-_fSwր<췥B1/`jS^ZӼۧ| =}8x{j%%@0|:_{:&< chk;=io @oK!cU)6`l,B-S_94V(~I-bBv- >z'ywl/?ڶS4%>C-U @1=tӻf׭AӍs2(z[wkloCP 0358 I9ӭidEaq ʧ-Z4 |~XU9<8_c>}'*ac_!5H76`!4j$_ cɆG6'#Q^Of?M.3А!#.tƾaK',C | @&!׉UWt26>~!}K>djm@vUO2nw7 0zya|.mgc__($|HK. f3;H67m1=k}v 1HbÒxAT!|#I n0E, @ ~5W,0 Gt.t6@JJ[hP݌sXNgdlq|}s)aBX kCAg{mpNZ m^s'u[/R Lj؆oDa9_kR}/$sO4M%VuR rşѷs0b,A[b#& ~0zW,PBZ .?((]`3|7ܙP*--d*B mNL5/:)k#@/>ئO{ C;@.iHN:ޙ0mYy8x1G\l`Ȫ89?oů[i-CpB8`~P3+HjP)v7P!-vٴw Ɣ>0Ow _Ac7yew8 0UXg>3Ked1FD;wxlm"bB|Lp&qH~޿aAˊ]棣|BzGBe N\E__u}τl6_8 3JJbjǰxmuS@<ceN;k>e^®7'$ *Wzz ڀ% _ՀT'W  OggS( `3"(45?=+  jwEag¢Ϊ#W:s7pڇ8Al}\E(}Z.7:W.jt?v w <yZ-}a~>ι k` 2*f? <|趺;鯭Amj?/ @[-{3m@/GT [R~ 8:MBtg2Y Z2*la ^j5;fo7wR+FD4_"&JV3Թyq}PM݋rt?|i}O MHN3~O; FG{;{@>&AwSsd54@M-Q,B"!-Ęowqgkr^)Ty١}ϰRMe@K v<;)| {݂}l>{>pun{zo VjKN7|^0hk Tͻ `a=PJѵi2鎀la}7j5 }4Y 3Yw77F3+!h LĞh;*B=9'lCrujnL0z@$I7~3_{( )}\w<6L)>n7d-;_?O- 0~0~N7}ec}V9M#}w ȯ [Kҗ@zl<ˆb//[K)dhYwl0/_ }n'֜}AQ_}˗6Pz'ItZ92DfHZ*VH"ci`bFUt`❅`K;pg o;6!g2qcY~&X=-cjAWډ|cBe)m>x=feSUL]`dK)#{-CM7M@rJ>z R8SDI֋K뗪? `! mw_+_ݖc,`,Av4`dO\Q9&3?i-P%%n H䠕8Ihkctw`K qiTD( G&Q9&OWl5 ?`b(w o[2lwt2'1@L =f:h6 \٦@M D<ϔݎ^`p~;K2@? @F4}s-d~cwE*0, B? @N ~&_ Rڏ PQZeG1%_:ʻ #x3 ?/}=_ZV-ͩU &0,csu*s75ex[`nz ( @(zgTOJjm'=4I(H\¼m%e6zp׀K~kZ%v<$ @}]$D[7K`v:gs@O3~< ` hL@_|Y8A657pJ.v``?-~`l> ߑaK'406cY" *PuEg `sodg"`yClX8T@5k 9$Cy@D^0S2dBX>cTP`ť4 NJ@]L&G[v{S;И_C;@vtO>v@Sdۿ1IHG}1m(d( ^ Q5d~KJxOF_Dt&]5ף>Xpns3 x^P|v!Zw.Y(5? BXUX<dש"xPq@TTU_-?.LLL}|}kMyu' 0~w6g$9ZS8t{zdZ `r@K @ _]2W\@ V6e^0Oǐ8f@4&$ӝ t' 8ouെ 7Ğ@ݔ+ޅ$\qg+;dz*@(cyP;",<@]ˆ(* @rAV4Dq6p SN^he6\ƪ@Phi:Ai(zz ?[ c!Ox>Plgy4@M S4FshEPHkwl8hm$Plm%WMs^/TEo5p}/o"EVVE!g ^[?z_)֣Pp)tNJ 3((vJ._ PގT=i2``Ћ{lZ`8P*)睜|~ E;& _ ePb=Q Ep$c>W3"qv"@7 h27__% ր ξVkPHH1hf;W5uP@@X{bO0~di ˹&Sr?@`D@uN@ HF4A'v!HAr&|`:۽ZN$V@|PX2?-d`+ߑ ~KIH^{)tzFls 4/d"ks'3Do6 jaEB"UoftaM@y@U h^#Vw 4F ('yJdWU h'{d <\t 50ǘ__K =jЗ_[K/2T?KY1腹1¸2\>?B.qK~@`'hkOUo>ߖs^-3pPo BB@Rv^("8-'~6Ԝs%h&Ϟ( u9~`.*xi}kb[4cm[vD~U,?@p0U7Lg_d W)Q l@>O0$5+U#5ٞOg;ʄu +^ }` xLV1<"P^ ZEmoXܹe %G0>߶=PYH-D4!5N okE$ۂP\4$طSt L|52<8@4?RU,Ⲃ68E-Z*HU2#5oe6 O@U:YW˹p@hRKt$Z k2'T_O@_wS~%-"pj0㸩mzVOh(}"\=\ gF\2w0Y_8Y=!H@K E$﹨< Y%rb5oy&bpPx;DRB <߲Z+k W osu%0?Fi=wZKۼ98,Xy{Vu@0@B 4Tdw>=D 8B ? * :VEթ[jтfO! 8( WHŷu:%@6fapW+Sп/Es`8-eO4f[&(ĬoɎ:/DFy\ w|W`ηlc C H\S&$B B%kŤěl{ί`95CZ@`   7ZoevD'+@\Q|(|ZZ x~}K"1 Ջytd>~If_ bwFb ׳>?xo X@ρ qW_L? T?os,yſ( WvT(|`yh=i2z/l_+H$ KL"_5 TA.1a?bӶ10>W <ȓV pL w_S@[oC B0K.Z\YC%߱`R`dHLlhV;Z >^w`zOa?t: V$p ~N1▌deZ"$mj=hϟ]=ę@,.@@Psdo$%M8qs$CD?IUNiѢs85"@7%`N+#;  ~U$L J5@"@&`ZH7d).c\ǂ!65/{?1 ( w(D+PZf Q@Z=X Jˆ( ӏUC8).ԈɑBDg44 ۿ J 8`X0mٶ`{0k!cmn0gHcH+= 5s߰[iow Q~7ėÍu0 oPPO;ұC=<=PgWf{e@)$@@&QеQdV^~(ɗ@\>Ksc"|rوƁvl8l_@_W5sjÏgy `})`SXqT#Tl)YpP((3P^FJg@7{. /$TPXp}Ʊ_h~{ +0Y|(3q \^Sݳ@3:=sN'_TS,sqza?oֿ/b6@\K0ʫzhf܅*`A@Mh`Gz@U6Zf?>(07jW"@_6~Kp!2tg`x^r?1a/5jeGEвsA͒?.޺"|p(AҼ0>v9U `ם:GfrJJ7piǭ Swi[X*7<g.-YDP`T %;Hz&0/@`~ݿ2Crnw_Tnze19ء4 ,ڋKld^%Jk8j&jgD#>\B5;ݮ;՞;w'x:^@~fk7Ϥ"pԲk*)wpOgLA _/?f2kO{<69@ z髨6xO n o@FFLp > $( !Bg6`~ !j(K~%_I^Y4ЪP}2Z7_(P{1b#nnO>hk?E#4>}} <`aό* KܸVFCξ PSzdB/tX@#2t(vZO>p{8󘘀/4cS\@bS۶2ʨYN>N{K?JRR8 зJН$lmp+x>#|)yVk3JӲS,ȍ]hȋ늀 /<= {Xj 7`+ nk:󖖓s`#G +侟d UCYaX 5yK;-@p~0_r 74_dqط4#H` ?#n 6 ǯX _ fojvx>#|IBE?1o Vj;.R?9B/92}i/?^e$h|S4\35 k1U L>yNjFc7ej9m}b>{eWʇk~<׉y]d`r;@(Hf)=T  pB~.Ǎ y{7c l! ?x%,X%hvh"$Mw\4-{SUB@qy Oљ{S$0["2XB1o7L3͹-2ry9}[E@ SpUΙ!x@yH,~=<\`TUti6Og.2$+I cI$z7ـ Kse؂dzv#P`h%XQd<伩U |AEޭĭ(D /au@.:pXX"* @Cfu;K/_TKi; k9Hy@ 9 2P8ٜ` @hJo.`v(`u[/0WS }aw.˿2\r $A5|VP h%? %{>H%8JDWܝ_}l;+7[~ q'z#< AbFVn|ۃP :ZpnlcW .-uީ'lԧ@ Z; ~j -LJ. H p@@5I0qwܦQcZN"#IF|$r(-+,Y p)`" $}Cp-QhVGUs0L2[I|*8WրғH )_$9\t{@}UG927l z{g !hz͹G %{ih`5S2l ?cp۾g- gUwo֥ 㛽A`k]>Jݒ^~O豅$c6F@z3_ &H]6@:]@2Q(ٛT7{6HpGJ:za'̹-4cAO !R{ _^+rXֻgG i>5x.≗:~<}a `U%'=]$o GP6|Wcp<cQsOU66|bmmOj &bZ4GXS쏵ʩ6la>N6?WN<q`C1X-Xp0{kȬ 2T$}쎧^pKhgΞv3,׀q*U_{-`^ ( a8; kBoim \aeY̨oX8QM oezs` 1Dhexwݳd׆[ ʙH:bωg(rte`O> 쌋W3U*+s (rܗ*f6s3hrs'ʄ*`nlН^<jwjH[AIx7h4J>ݒ %y!7e,P5 9|lslRT+JN;U*kW? pLLW/x%3tTӁ ̻f>|z %4AuPr BT1"-(\&K~d rem>pLrnރ8}a.A+ o = :`kqL[M|0X{5/(~ j!z>7 > &3Y`ʒꤘ|?mcWj8ߊJsqm.s9<1'( G$9 L6_H @V [ `mis4h{coH)жUYks_R$g{ҀNt @Dq<Q=,?0?~`k/ r (@,qx='ߟi\2:IJd%R@K@ F0?&92df" Ȣ #spŹ|l"?PS*Y,VT( I.h^̿:,x2#9rWuTO:5QQQa^TF->hi B f% x{?Q}5`|}M 9ǖUȯ*LZ/ @[ߒ 8m>3%a+;k:oR@ PS'ċjp1| `E4:p"La!NP@4(4QqP1aA!%ڞw}XG;|??sl??m }s揯xwyNM  *@Ȁ[ R %3{IAsr@_qMgcN >h`DWL'1l0t\B||1|<9 @W.tJ-Bb#"@ 7-PO8.Q7~ wl~rOgZ@g (L-kO%$93@Ld[_H/ iH՗J%s-"u{Ī? n%WOs"` T ?(|o p{Οi|lD aC @*҂ Tʘ v XVwOsw6}Ń+| `݊)Ę5hh2@? ,-ȁwHș`лR:!%Beo@=޵9Aɐgg=@ 6Wۜ$wсobSˋS=Es}|o*} `s6O%J ;aـy-U>N8OP{֧LYRzJ ǿ]0&9*b.@e"Xt &pp@ zU6){֗*Poa ,<>P *auh \EѦWaekShi6 ǐiMJӅzӃ w S+T  T:$Y`ȿ@2@6 lŀ >U?iw㋲VӞ@fLWdh& sEm4j6A`:"h4!f3/#]?>: %}d p2Ym= x@;qPw2W Nu~u`i[yx{2 2a` 9.Yƭ |b Wf`([2}(-3h BVF`@e ΘТЅV6hUO?k~BW66Cu+(* b2#U@Rw P2dbLV &ũ\݁ht KDՠu2:[!a/4/No{!Λ+tMvD off=:VZ+`^HKoX&G}yZ0=xW *T $ݯ0QJȨMhdž l|OggS `3&s< 53=7KKLN)(^W~OOa̿'ȝNr5"I/\ 89s8lUAgx8FܭN-2?6VU<v~ @UTTETrybn)@_ K_o ާ @7?a%ӛIٿ= Ə\?A~Vm,pi%&R \;. )ZW_L?!eef̡*׏&͛llc_O˔yzBonÝ[h%6$xa3_G|'%&l_~BkPdm>ڰFd>'Ur p3:m8[m'AXd Xao~@&ω1H""@2coK[}|G29` 8ڝ FWZEP gN `i`j@NJ~M=ГdgzFK 2G0y vU`|*/m@0z ILض~ `(Iܞ(\\iXsZJ &xjvXMܩ(fwe$mI 9(|8| *ʬ[?p|˙6/}?~f^)J(`|Krɘ0_A+v~CQƖ 0p#6c(<0g4 T~-zD4qP"+D ~@H|z<\Jd !Sc|n@{ x!n s 5X<ȿ07N@\ٰ̺Ro@;q}v;o_,Ԁd@(g_d `Rj׾n>؟j@`=30(@e2.-w Fq87do1@I~L9N "_Ȍ;HįOo_y7ܜT&|90Obn LUUh!?w.tx }7wX9S@P|+8tX!v DÊ٨*0+\Im_ x p4{hK$:tާ(uD;{&[vBUatƆ*f-&Q n v |nUI3yEsjXH?EC9X8 UU!Xqs4wX!q ˹ٙ9޸χxe_xw9gܽwؒ1_r p0s@4,>Z/~_74o fc* u @2P(  * u~i-WMW;3 }4cyB*Ļ5 ћּ[ˎ4pB5[ß8gZ<ܱg}m*soM=D@ng7Ŭql#' Vj\UV6_izFc vHMսa,Ji=3;>=}cBύ W{?6>K<|pt4_c  O2&\3{ [Dmm( x#r7͝Eq}ʄ{1Wßvom,L㆝qyՃYrp].c^!Wgec^<Կ~ι~wǖµ.<&~]vى]k6ar_nL_&:U2L«XK_DHmHݶk~u?o)^5f/1k/`f,<Xc89PVd}UW'ow"g!6? >@~;&m(tvE+}&Vo3ڮֲ}z;o;QQ@ r5)͎&V6I߇sWF0h,ϿFυ44T{(CT.P9M޶@ Xc _e,;BI1#T9NQ7V+59ĽjT\ ҫx5UV`M^Xg J!5`A1X÷w c8<(8Yl͆s-[ग़@>@ u=/>ky!x|XOW`,,Îڲ .0ȻHb?G M`a|&@_wWH|,xRXcd b wC•F@X}!(7I֞ateY(̅g^RZD" ""̯7W[`e`+<6 @{9E|5^sq`^P rz D%4~ӶڃVN;?B ހ.FC)rj%>w`j? {_'3QeZQ \[^Mؒ m!elm'ݢЀQ@ڝ zˡk[ ~@?OggSS `3'v9;*% +) ` 뫩 ש* ?ɯ' P> @-X7`^JBc)4FɘCh2WĔy937p`<\7(aY٣4خLԁ,!qRyQkWNr @bqlRJ2o8ma@S!0Xu~)M`X_/{A~:%ዾ-L)`tLK"&r95R+a|$' >[LeJ }]h*a:F*liއLX^TOݞv9 ᠿP߫ + xۿ >́W ' N@o_Ng>4f}c O:@'rT[̀K0tȔ/s?iP ;]@^@zi^6- RP(4o, <+AOx? `m<qٓx6 S@ZZ3kX&@aNd|^~ᇼSm @o O[pLLe4Ik]+m2 `Tn @#gR/ Qg^M@;Y跮,̓o_m+%<࿪W 4~u+* Yjevjoʢ4&F>¸j~<*O̿4`5@OiN 3RFQ!*| n_cp8%LJ Me 4 'c ~fsdGr`։^maJOT ?|73Y1Wrh0o&} 0糩hY^s MOlz­1?~7^eg HJ6L}J6}]i9'P. Ww0l q=+^AAebI*u.@ Hfk +|B{x>] T,=Px)UNVT+DYUvnD~u]|?ۏNO\`O > Emgo]S 14}aޞeVL @.,(^3!rL \QJvҸ eu{*1+1 }ABk)ϭkJcr½:0ޏ(# Prf 4.XwLn-  ;m 7s-=񀪋{ 8~wo>8-W#F7N&2UL K`;8]f3_,5i/U,2>  EI`=&d&*6FIHDq`P'(p݀XC,ӠցA@;Tq|d%Gd|,ōpIw^=<~ |WYp{Ų@g [r i7P úv*~ׁMt8l ?@y_u d l_ _@s^5@CRmS+G II(>L((Qx<+ts707PB@edjupu| CbtNTn !+| aES02 oڶ g~({ʃ+ >HL|}#0o|?(#w|k_*TTEY _u`: K0RJp:@.P@>|BSq,%MhJ"_ܤ`ņ p}v!`;@@KB?=oݬ`#@[`ԝ?Z >YE@Qxzvzp>e+M-^ P1āU泦; Tp.rvUlJpEU^(1 7EXiZb!`|\]GYz nڷ( # :Òz2yo o7>m/e؎w6)/9vMp1oc>6ohdɃ*r+9B;(鏣j/4@v, l`Su/&@To?,nlV>R81[_;.@/Z\JScWADMDĀ w:6 WW@@_];?8G`Ǜ@gŗsϦdh'}i@'==#0<7] 0ҡ o 5;Ohp+g oHpetO;x ,vB azqGkJyN1wrZ(P =x0pdx݄NLIȭup#4;e`o<=?ç_ʭ/4nGM64δ_"tYBsC5K'NG &@(0DzCyѢMׁb Wwk:_6g%/@ް,;^ sL }҂- 1QQ7 2NG.'_OAr λ˭" @J ε[VL4_~uc6>o 7(_ r{/rPJJ {fP_oP7?0d p^/F&?l~zuځN-RPdJ{ `s>1x&I4U~CːiAq&ճl8\Uu`ѡOto.>y5L䗔7: _ gZ5d-g~<P8L-ZyP$#a1  -`d?}%ƻ$,(|"U>z+hnAzC%J/مY0q;yq{3Ǝs\2SDaj9du@rH=n3edtZA_܅26Io<vR`{\1PZf/if`N57.^YgȔgZ y?/{BFG ln |@gz_7sJMe> .`d_ #m cz;s݂>3 ̶XP[a >eBl0I(Ijw{ɗ~d`2 *?"'؉s|8ޤ5MS1ޝ@o?n_I#(75݆~WrsoKk`u֧ϵx5*l.+|}vܸgu. @FUs| gc>GY)(j3\~&N,^isb!I>'eAÚt8jX #M@qdkx}R죯f;xԇӇ =&PуUyƃT\{\n}[˗5 of`_{#>o@k_wMj,^~=4 Зy8} Ff[|OS _@>QRBay#'A;,5\~7 (v2{dv{RN6P5XrNWܿ~(H,q?<hz|-p'1@&[@{E6wUeI whpq"namitn#gKOS >+@~'7hԀ;ũ3uXcP P Num _Ryt>EMKeO sn\5sc5smxg0&sjc|I%&-4Λ=Ϸ;| | l0_lT*ESBgTT.r2@l@<(~+'tepN }dE߶Z>d+<=>G [kϹm>Zb z<0>\ ϧ]c= ヱ1H#` 1|ʔANI+m-Sӗg 8x T< 0svL*"Mfw5䋓\N>TdT Z{LM;1(\~+ɋ2-_ 4‹7fSf3Ouc`S\k78hGoG pihط:?6nc cg9!9|~] 1|Ija {=@3С>{*^#7S{Ӿd0Ac;xtf ȧ?W|/o%0>sL>־[Q?_IǷߡziE BchA QAhfR >G|.SMWՂdkh!5E$`GK{F#[xG\$H?g}{۰;_1twRZ{;5:7Azw` ! F#G8* M4{qZ9 w32 ޣd@Ua?bު}WJn䥃m03kíx++ M/ <|l6Ǣ `?6g N0C @&x>Ai2|v?d 2OO`;YObo8@r1[~ugzohXJLAEidCB>.9nAp\=Y6qW2rxEDY{%W@U_-+ *KY`fzvͼ|PuLϟ`f`ZVɐa}W Z/w7a'p&h?ҖVsDU?@؎+`!Hy:.G| xuGY2"W:-HvOTnPsbKg4*N ^r59q,?7@mƸ.gjݞȭu~9vči[ So@k,:q /2k`SS?G&t =.φQ?8d۝̟xn`>[ܾ_^@ fm'S{~U?I{ri-$牚K@xx^|?̓}GU*K,oQv?ED+|9=lʡ/W-tz唣--L#Xm*dZ^5[;ŽŃ2`>y5}LpXV`+pBemdAfr!n2V+S>d&@} so(ݼZڮdp.b**T@$I*TUEGnkYilZ"ewz;V5M|靖}zط??-S3'*&ɽA.Z۳msj4.2N` ?@V W >HJUק2 ˊ 5"%kNҫ#$ϒ?{:( f`@ţӉJqNqJr4se&fJ/6)2ש;jC3@Kex ߗE7, `7.2cc@@==;n! ȀHI~]U2_0C2`ߐ>=/j%/}*k)`w@%vӀ68Z9h6g~1%zL-7jLW@6D.^ڦ܂Zs-͓SU1@c%p°&|mQ9V.hӋfſ@~b zۿA'}k~<&5y} O@`70` XV,g C yCBӻ*.G޴wG&دޤGT ?\1Ng3(h۫- "X{M( b vUN| p9@fFiV+ 5ٿK oƱWBz+wDžʯb/w=O}(x.6x'XuR_\Y,~+wǀnU \P֦J'=_<&W _(1 Zij&*ǂsn U"v+H#뙈sZ dߎό8.>ltb.,d`|ʊ7Wu9'v5|Vu how rJd0?6@\>R;q{i*c[;Q$_Rdw  d`]kxUl~53m6rZͯ(</d0 )$ ,NЀE񀒢"L` G@f~ o~XbZ;[B;p?p{iwBsnr߀χ1*@_썈 Ҋ(*uPzk6}<}yo3zω/@N-]_aʥwj#i?G,<,<ƙ% (t(%I%e @:vK%? x7 e}}<UIaNHqwwp2a~8c@k L]pЀ ֥j ><_>{ =7dG.c0pV/X{:AڅOIdqWIP+3s =u T g]pڠ<jV*6KU_wx}KG:Z ߤy {0F(h$ (:p5 ORXd6s+$m* Y]} [B e hg0lŖǿ{={ ~dyR#'QAe̔l/mGnv<ЈT(u(!4 ˡ Կ2,pߤ*cX2!L܏2GBH Ɩ#@>5gLPoVwɅg]UN >s 5DsrzV[!@rSo"ۊ4=8>(r;xZ6 ߎ-bG.,y|}Z1M*@k*/o¦Y`nq+Pڪ~2̽@֯G@V3|n@{Gh_l&"ccG*lxV ɥg)fg/j =J~GABIDNUP^zZpiϦTqث S@K*zgSW?۲ɮ KǝBjYXtϐx<Z'@M'p(?JO5hg_z܁OW~ P(qac0 <ݙ]姷$ZY7o eO ZTj-s+ԏIb1 _. pɥw^뾪*h[K_AegJBbn9& ^ PO'Fs L.N>uo!LW!Ds>jB%MS/Lmpދ7os.73 ͯJ{ހ 8O GJu3Ge ^;hN 2| p J3@w??i! ZcOGj`@e+h㭪w xv2g-6&?.0xsFϙNyA#tdpQd'q"ு2h~_  aB/K~?|YiwpYlΤ74PnWx ftX\c,&:~??`9'b4`zl _2x o.̅ BWl %T'm ٕwN eίZ˭"p᭄2@]@>n'Bуp JiȁaT ,&@et̛?F 3[/A)*a$D^9`*w\$spXz33h1c-kLύv9е .\R_ 5T_|OxFR (Py^ .ZLĊ7^>=g 5 F<"ɶL-Xd3&86<(OGÚ&N/O͌iy >{&&0 L y LwZo ^hLߝ&~] i_HjT@Oz"?reO ,  !vxtZ ;{`} X5" 'kx{'}SZ |%wߨ7 9՘ϕ;2phi_7&t>30J">W7@ǯ3&]6_n9P"RW /d d꟬eqBOd:>]3no)t&H(2(Zn_ۄ@3GG@ϖ7P W|;sw' Yؿޠzs5?{Zp|q2~AY\{;@s o\9Gg0d}\9!ݍA*/|?f0@=֟2OdžI-U~B |Anpk[ݗ8iDUH@VR"w&_BHACLۋ_}KJ;{?lpy*(P'=ܘא3L#O~NS~91?گ@2PSTn ');+6~,{$ed[>{R<<ŀ0ʟv+^XUF}AnMѡg$~Z@isl d!*v8t}~<"S*}q CZ[߉67ܘ8q>tZƭG sO@.7/ 5w0.}l!`~S E@.r|}@}(Z*ù1Q~a^JLS733G  73phO>[RsᬭW1A~sH !Pci? L|$#@Y_현!tpَT|!l,6ޙ5W f/%֑?4? |!2WR~kG6l̓=*Yfq>0OUAQ7fQR^9-{UPQa ^^p{ jYtÝ1s8S \(1V{lhV mՊ=(( @ټ:Z4|S\%~ >I J|S2Pz nkmB0̦{ޙgZOƥVWZx Wf)qom\6'\⼀P07*4=${(`$^u 6y,-i{@kӃ51?%3N9@ocls ~~ 9rO;2uLQp% @0@a RxSzPe d qX4IY;d35AOggSO `3+N}009BB5ܯh_# &I*/x/=MUUE9צd8y{}YJ {y9i%0J]g}Uc8aNy}yoِ5^tm! |ݲ7lL,߇gÒ8^5Bfd}L)PBU_@'G2 --@x_$I%ȋI3`mu{daC,ۂ8:η~OJbx"=b6Ai zN$G|FBK7}\ɴyyVlx};^s0@ƵW=*H4 v^DR="U*6*D*"2i؛4gjQ-fu7>iwZʢ"R#FNU_tz ~8pvYEUzyVZa `B' ![Yx`|ƿ+:Gl]$xtӷDtl ЪU$ cU篇 ˻/(RT޳DQSWGa&B9 ;Fәt yuW ƪ/BZU[7lXO\FY C" SF4*26iW(lg//oe]89ꠐ>eI7iN:<c1sX.z<ˋέkLC[` aοOtpe7$% lHB^'qfpP\8H Ɉ/`@˲)d . Yw,e"&x/0 @騗=F B "R IcssFX>xv"'g.wp{Q2lp0(ܑeW%|6/֥ ׂ>Ps 3~7_߇Z[]@.(u1@N @~c}5,v,=[vFwY MOy j5Y-/H`϶n3Niȫ"a\';vٙ){֩d*XjI-. w-Lkp>/8?6ωۗW8,!@9Yd 2AɿY]8_~?3S`.?i'g\1db.y(!QUUT) '(sh,G ~IWfo?'L~"$!i6CKhf<})djUUdRNyUZ􍷞HW-_(m(<}e&Skm3M;%fw -|9ߣ6: _0o 0gzH~ r~ @x$ OZHb%_kJdh9e@#UVbX +"VЪDBw;]NJU*w+#}AFYϝ\7zp')(]5!G֙9g\)ޏH&)l(ݓsƒo:3m:[ ^s4#vp `[1 O0(T_@RQI}Uk'sEUQV;덁9EIc~9̕"'V/Mxp3F?D5TU3So({xKkoAc )$\$i9׌lAw>(e-SX#Yy6 >)wVa/nJ[/'@]; (/R@3;J<ȄKrE@(R:4_;ɴjz=_G9wh!{ 3s]cהS0R()Ͻa  m t L~#s1 'B\!nuRzt$*E?b6\"djdN']P!Ćn7 Zb4>Wݤ)'At2d y[p UwcgŽ@2}Y؅[vI=^h2dt2 t] XRᇕi9mA{OO3\༎ona;s:{/lA?̗jB@ rud $ԽBrm4}X-`$Izd ˩C* PR!7UW,ҢڿҞj/D~ tm{{hЄAT[y(b'>N#2^n\A>Q;gjߦ1'n=o~;y3yJo7oC*W@/(gI  }< ;  ??L ė kڛu.#&.K ~EE heW4V  e398vnJq~@PǛ0]G: &K#5`g~ Pav]aVe۾;yR,gNo>M<;@/suM;x?,ZK5jߌ{{?C,%9' ?3@R 5/I~T8}1Qk2 ' #00 9F>E9﹎4u6CHHr+ ,ѝۛO'`D@Hs9Y4}s'`0U>4KdkL2l1+5KK~tyBypW>4ۭOf'ڿk>~8yy}- ۗO{HFXlA,)yT᧘'|nW^`46wҥv`9Lv>tnY9ڂC;o;gE=P̓>0$%]qoJ ?aB?@`|FAXj ӱ6G`Bhe L" $@d\??K{nd m3Oˏ vvٷ?~C`.Zsd5 @QTgh[ruOTW2Ǽ3g(JҳV֗]Q=ɺ ԓ_I}5 s/PaQnOggS `3,B#/40032>GF1.!0")&$4| |(c6Xjde k5,1^)gb",|ߥ&cYV kT/ R@ ^qq`uLYeMBrniS|d?p =<@x$. 2*{MazE;] =2\.IOwx̿ixg~|9!Dd/Q e@ۯ*<@dPLߍS6=U4͉~/-tmJ+V5D[v<ƱwwDVd4L{Z|p1?(^qDEqd{dgc Rz_ikߕ5T;4o;[w#N_ワ:T[Ov ߴQcMUkzb)PҐkD|KMFj+xy[VqrD ,U^o·k5Wjxz}냣|ñk/ 7$%pPL{8o؋{wUC:uCtg}Y"ukɝ}X[>W T0V2 :ޏ1 `F,*^y~cWTR~+caIg ``5x Re٭`+X?PO` Y0Xln>ҽ-3o ׻'tH%돊-{@W>*'#. $* ҟfVaMLd\8 {VZ4s.0NOx@_ `1;Kۂ+/~e`2{f!IP'y} ܠ hm(+pjs?`N0   7L iywذ<@ tgfUw'*@U24jVoTEPPZ#YJd[""7l%=g[cm;%_V' 0[2_1*_bGxe>E֒:4JLzQxAl㚶Xpظ;poy\~.4o.<n' &N1RG,#hq >;޵>CxmȾ4s<~T:&WtQƯ P Ղ i8Ԯ<_72LbeWTr7tD_,>Vɧdk@ RҢBjg!`3>%W"&E!%dq_zI?wxU [8o ^4`"~ lrKG2}2p5(,,Bu j0@_7((:vp`2s aAˍMZCJis@Χ5p>̿aB'`%s;Կ[~k0h215B'EHMD.o?""$kB"~@rl{ h. q>@=@H{cּY0`:=N ?LR[ o,0rx1dzv3E2@W-~rV`_YQy]2@@"HKPCqh,Q +wudFZhoeqC ɕ O ` ?Zs%W'EM/r! 5K8J@c" /iワ0y?t>w$̽Ӏw|Y <UI?U'kE3 *koR(sVJޞ_PO ޻$h%-P04dD`oXfȔ+:L[77L@N78]%0 {@Nʭ`0x [ވ%g &r#Ef'2\Tdϼ lP?UhV/ڻT`ۀpAMS~6/$a;ג o8!*K@B_2(w&@Jة"k @&7?tq y=T,ݫrz7 ~ /ME~~<~[@LOggS@ `3-42!"%977KFG:eo3;KaAM+NH41/*/@~z` 15рD[0wU`u ZzEk `$a*/W(d¿StHlWq |R 0hJ ƛUf-9’dC ͕L3@o_U;0}m^'3j57^x{ Cn+‹Ob(* q\_@k)W} `)` ZiD2d{!}>nΙ"g_ي gd`Kd =-.e ɫQ @i ADX F_B}uM F^Fz7|_H,Hx 8 U<~@>hUgB:$ErVɽ^N4,)c@:7A'9 Xw-h@@ 91&O*E}s1hak @q;KϪ b] :` <=$ [.†*@Yv6\aI}O LQ0@9sv+АKhh%Gl!MX)iQMb/tQ? Yh}>_Hѽ_韍9%@A[LN8ApL@E83s9Bb%d9w((kJ@-3] ~6g=#_Ǟ+"TDDu_grHaGv%$BKP&ak> WR4gW'fSh{Y;mIuN 41} pE1Omh`֝NOWX% ,14^/[C-3)ܱ{yyyd ٚCu6:`tT#[8'ZJdg{f/kqΥA4X*g |M` }!Hu2 \S`_^< WUWT'f&cVRjs!@{sCL\~;*Ms? P:!/[s4LVY3?O*C~)P062ʽԎ: yy_Eg Ҽ/ۆ;k/ȣS pkPXϟ6@ioǠ^h f%`fЧ~7UG<&tB?w _ޤ9S' L8@so`,Pį`/ue 빿-wL ^/'TcD5qgX>dtfO(! Y?VJyw@XI l ya{Tc׆(6Ap%SE^gzgJt @fϿ<N@ww~ h[/5x~` uO~' T; sV+eC] ?|JJfh`k;h@m'T 4"^Chisq/ ` =+PrJ@N\ /`6i-6gn(Qtc^KeO@M.C#{3] 5y={D|e^+C@m 4  gZ,JS5{/C0_w1?0W |@L9E xsw5aPByͧ72ȪMzC|9ZYP' Po? d4^4E|?Q \;]So}"Ԧsr+Ft@`|}<̼i:u&6= ^گhޣ CTޗǡ! Lpdy>0a`+p'Pù8^+PVOy-;;HkPp@U5l2 E7c!L?mnhtќ׿@rV6AW 1 R`gU^̨uwv{=:k/ Z8`?h P[; h| '!B,}2z\t٫}Q VC}==2Mɛ}/x|35E@Ċ( |ȻF-abGl(Zv;洕 0Wp `= s `Sm  +-0+(M&s hŘ%޷f^}_<1g>AR)\(ֻܸ~`>^D BŪ$Z7EԮۜ- er9׻b'xa P3$`x<@VGI@AT@%u5c{1ge T)rC7:B,b}m@z+nڎ61b 习M.PTC08ʷQm|{Swݿv_*O_.t<51)P|M.-4SSG?_ku< vZc1 y'i>i eY'Wo-ۇ;o< @G{v~9|w .>/!,ۡipq0jF#g+5T|@x"ݶtůǧk,E/Ine?3Ǔ`#jl*P;_F& 1hj?,nw>'U}j^&]i+yciK'?s靅 *&l |2V}- CL5KFF&O%yϔ8W;xJLj +[=g 5/kYEQQTDѭ7Tv@&Npd.qyҖ-)}j]cs]`k եWcK+~?s w`?8:BkW\&;Kc4P__[`G7 i*/)+ܺμh *N&L(6*@/p sov=S37@h {Ux U@̨8ƕ2&zg?O?y"ka>i۝c2Пs! ;d2!.NETٱ7"`ww]G7J;ioL4WA(s%?n& |='q'PU1OggS@ `3.[!)14%(+ bI$w1i-گM%ЩU#?6 +tʕ@{޶esUO,FrMmb`^װQxjY99TwowgՖ @ɹgeϜjLr8۟(ouOoIƼd1wpqGǯq2ہ&>5Юiaɲjw Tl!XY_e#@ d> صsQ  (PUWmPyM- tڕꃶ=/jr/1(c3Wn6 ZC6YE>M)@35̊v-J.~Jء֜dOj Bl#X|ХdO)wV2rɯOƀf.2@5nTTĸSЏ Tꉢ*tiMV2=av^Pz.(d<Spa/@#W@[/J{]`@ +0Pz+_"v~\5h }9AT F5!]V >߀qspgo[\JpAEw*w |OD T=p~?.`VehM=gYj}p bMhuȝA,ae'{B y<ŊP + ѠI*^>n>`v@R7 g r4(o}zYrR|ЁfuoèdnW7HN*/.XMS' AGKxmzH\430c\}?c\.i<p67h+ vV@vT-@uPr<*x{|ח.'@(j%R.@I |:`W$ud9R @e=n!@e" mΫ/y;g@}My>6yc/_?stGe]~ w`% jZScHUd=BJA!eouJk#L<滹x.E/\n7p9|9mAL/)m 1&>!'= ?+~=ot@_T{TG3{^4Omj>!9z7 -j_6p-"@&BPʕnԋWԵw"_ d$"'Sѝ`8xx;g Nal W`6j!\W9<W<)(@=9+'P $ܪH`ISbҐ  ԮPVwC^==#yCEoMX|7s?R Pګ}?[㿌RkŶw' `Ub"޺ @)z!5u r(Lgp%/?ϋ׃qpG-"@,+ (4t,ۜϯ6dOZ/(G c V}! (-@{<UAY9y-/ȷm*=1sWg5` \VdsmfU$@^"H3ma@_fߴ,SkEho!Т޺Mp A˞L. ^y*/ 0pLw̹kx#7s [smrP'Pb -I ]}腐wB},W ]&ng; >,ځ|D} TU!o2"(|770 k@sO^YywSyc;_k 7o(ng*V Ju`İ}iW>&V&1gu% 0Shc g>7ܜ[ 7eLAYlp.iP͔g{@ypw;o @iМMr1^\?u`U(a_}0)6 w2 /Sgz`z` Op p顱[@ GlQ 5hN9`u+@ VZn@f ¶3xqK"qHƬMYw3G.X εtL@+K}GnЦtoZPx~@}o`{Be3?x/Gb1luhN?Z1Xš\HE%{*E.\Exwƕo9omw`U<܀1  J~`A=0%sGT8dqC| /ςG-U S[8s0C 8΁x8#+54´t2&z7^Q/bX]̌}<P@ӯ@٨Fr: [ : zT ji" k]S@)CPǴ{;h;qPO_~u |KHUP?~30"@ hIt**BtM/I眲Y  ן3sp  ^@ZӮ\YS@ǯK{hW2D }aC&< >k23޶-_c4W=@ǦŨ䃍>43 m/Pw=Ox6rR>e6HXXUY@?|e/ _ >m`I܏֭Rt|5 21qu|Ӳ`[ <5bC XcUڐ H])||T/ p>Jo \@h6@s@z>[ƭ052&`bШQO m>}D @/@?>ЩqYo}[7`bOggS@@ `3/!e    g/p>mpM{Uk==Jv-)}a!: Ntc<9"njSx~i&@I{}ځ8Ø6/? ܦ<@)# B-#/-irZU+$êQNǂF!00/tgD0 {T "FЀv(OosPʀ+ T[ѕ>|@b)ĒGh{O3,nbݗ v=?Tsӱ`M ._ƂlͿhLgNOk[@40> @_ ȐW  } x,@St ުGh{.f %KMRv~Q2!h]}pw1_\@ps 8< bF ?؀m.X/tIaRт@cLv8:Pj]@R`qkPR3d?Lg~wKnpwtv9%}A 2dd Z |'nY*7xG<)V{բ~QHixpNvৼw (l%t׶Yɐ{y]Ţ v12P ~٦h}w @)|" p6M~7K{}z^|u{nt褽&`~ǏX%.^  /Ϡ)@~7Bţ2jOmx{!]zK#0jo ֏xy p5Anp;@;v04=w:54Ғs(8Jc@?9`S[@s8 .*T4oDHf;9ϰvO` 4f~Ny+v! F5|3_??8<.< |  > > bt;eS]鳕 ;Wj 3n}@yJ^54'*@"F;E] 8[_PFe![/ CdyI 86A@Nyo`?{څVFn*gD;]qծ9[ 99}.E{(:p,bp2!Gu'qol*)+|v$Eۛ3]9򻶔|Gx<T  #t ;nu6dc.~~?nЀ| vW) Q\ٚva Ⱦ|h &b b$O6ԁ|z- _` T9} Y7` =nCLA'c s1H4.⇮< (SM_Jn%!} .  "Ɋ6}M\vVw&{9 uWa$OG][tZySXv~@ l @/;#P>wS}!$LJ@ Kkj-mG򪵲[ᄴJ; 7ǧx

    64o6h įU`T(:U+M\9&DBmO]9%6Es0; 0 96+ss\l*|WQ-AoVLiA޷yE. ?7;NrÜV h <1?' n0̓O;`[(@?])|G`o865IK~Zᗦe$Ղv_qU@F3u['\=wCt;yD^?:[O90~G *_nfh.:U:[\W5@1k:@ `0[[Q /o 6d4ޚMV^h(&5-*1 N %LIcOD}TP}?~iI~tNmÛuAZyoIZ!Mi(L%W|10g??0s[ =>L نП?<)OggSz `30  631AEzhH!&:6y8l]V@w (a=Ӊ&?XӞò-N Qd@}cp"H5etLz~Vc>9q[f}_}\clð=@ߐc %yNKƐqjɡ)50`1{ ~r2zi\e hgap0 lzց!NOhvU;\lg^xÙ?79$|@"~+nR6\VR}i?ŷ %φ 3ћ 馆) t o<@ vsg~oz1:~Mj3e uA C8͏Y>ZP)T6=( Z%1fp~jM=VK~ZF}lrиO؃rx4A~8B4.܅Ǥ0_N2N>Ko!I;2*Q7uplopd(jU(g*j{oeMLrV7^6c4JZzReXdȯ>D4Z nf?@ |rI @ h)`Y}GVA9~7~3 >7#5Ѐ @hR /iga @Zw<<LD)<P_  u`.TU}h*H }%oPmjYY_g"6#% w4'v߀G3.@Y˄9⽿ S3P2<_*z4 ,9G[l?T.kpɐsfmޛӅZsz9zͺQ=O`F0r2sx=6owd84^AR  o@^R, 40gP"N)|Zgh -fʈ xuWVt_d]?dsv“ @v*#uJZ >3J1|wu cS](&W$8> h3i<=/KOs3_h(nT-䛾xnmDI~M寷P}Ӿni+5 \wnBD2>홖vP4D 7j+wada 4~7hod OOVrj׌kYe= ݶ+*sL{6͟ʌc&WkND77~$1LUNfm!<}Μ|¯ z[Pwr2촅*CN|Ngicu`,{yV{mny IscG5pawlD3V]nBDJh*kKUɭG[j@f:e~50}x8nS"Olq^ (05=trpR]2 Ɵ>@o}e*ޏ pU#(\n ' ;_0 rP [Up+[)_l>%Ci&XP mY&,.x.EPreIdv[3Z'/< I .Dw ,hBMZ@t4υ>Û 7zacm߰h^,wu@a߭o| s@^!gC@`VJnV<@5;ޛFBLy'tWp6q@٪3p; ?Jx˾_hnItc &U.`{큔i9Z^zw {>e;-| ,U.%< _[w`?j  r^<@uNx yob 6TnD oD//OK^*5h@L c@z5;щe;J Z&&V9xow߀xq7P|ₙ5.֌'*ʗK?9C >nH?<u+6Qa]bo鮷   6[05R Pkо~cxsMN|@M!MBGPt~wDO!ҭWq_&0XP-Z#XÅAMwx9kNj}Fn{+d0lelwIG@`@ _cOggS `31    ,^n^5whZ'W(PI"بuyA8"(=P4 Fy.xaz*Ĥ%bz֬ueYV-9z9VAzJ((0UїHһih@@S K^~k65ɹKq{}뿏Y3[b8oB3 ngn=}'hоf_q*XfZ ܕJ`Y_Mֻ (^Gt5[$;TH0?fKR# :p/NЁgsoว7t"|v`4&>̙IFֿvzÛo/]="`Rzw29/}+%02Pf ;'@/DEFEO(!}3lM@^rrN_9/|GET=~}aJ7W8 9Ac8iv}3 (`|0U /_6Xp4<|?Atb54/| -;rv%,[&f3?Tm ˯~s,k   _@wMl^N5;?Fϭ>SqLRVr-&j$p}́Ɖ} q:hs:.gOnP]t'.zӻeW/Jh߲( ?@ zr ?ke|} X PPLaU=iOpw/ f|S4O{PƺXrWח)UK@2t+3ޖwdlYj Lk/\GB%ppCqP *9Y!& @NYwpjqMvs~iG=7\FSP[,hZ(,=Ks j 7 ?Ц \~n>,_ [gs{C7@?G (|! G)@[V#%ޙe#[SI,P>\gi:d/ H?@gܸisʴ$`@sJٽ%]nnA-etet BwZ(;y() .3DQ\aqn0LG5L Lͨ +lC˯W @J;)K,?2uL"Byәh>].˫`SVq8S@ ;bMta;ū33ss" ܤ My>C+W39'? )yn_j`bmrS@Zy}kB I@/ZZ`j h*ZǽpُPYt yGZ6';3b_ACV~_^Ғ_0;8sPGD;Nl^>"X yl=&b5ͻ>7yÒ)_)&`y\ ?y3lH~qJ@W@nUƭ ~ |Op3m|Pa]_70jO{K1~{:LG$ol-T8,R/0߷T@W8ȀPeGy#` W=ZS$87ax6q|0%L C1;hzttq^ >7/=:;O,i rΖRom_QnX?5ĭը_? ԯƏ 8c:6:h_lo @k88M;b-?কYWq3~'>cGqÍ;'c˲.W$ҵz62gh @<* uH \2O* -lB^yeWt~,Y LJp*Xw}6%i./ .\;'v/\|B[vkHuԘFMX֎.G˽Rr~zB)F@oLb`8*NƖ7aE@iMf=nD~JѣI/Zm9[.--m @RJ"((قέ,0g Zêg@)9ޟs{d2Ї ܚ  fjWx}< 8&$n4Y_$@ r/_=w@5vGP .CXeN'B@(}zlF[<~ {%җfƝ3`;[s]90{(Rٌ[ hyo8pz s/`#wMC. t^ owG5< Տ] 4l _[ [.E |ns`[^Igh S~FQ^>JwV'%sqrd!ا{Եey-q߇cg\:6 w6*CCmh1mv~IW7(B[hi4W;,Py> }4xq sDOlGl$Sgf: -?e۾˛19 %/eF2t!XU@t M y8p s.v^D Tjt=y`p`(m~(l d _A]@xBNz  ^YWLWb+5M˦2> Ve.lsN8[6*M&i9 =y mx_43mQ&y>p&n|Rޗ0n3t @ 8*}7/2P߹Un`r#}昑x `H}IATȴ6w`jIG?T1WZ PsxCcQ_pCn੝jN*Aa* -&{fbGy'JY-3 C79OHk>ϘhAk+&S= pE %RijzUpSϜY v7ݞh۷BiYcCy$1yv/׆dy}1ؑHUI=6@_8߿wTC76 Wl$ڱ(*߱H*ÕKۿ5ݣ)?ÙX* r([`zV+$ QW O]x}{9 tT ~V(Q@1ĿP=wdDGQI5٭I4xMPO8HL H6\/ tʷ/0l4l}7`o5Ua$`:wi->|B;6  IL##qmB5`_X tr%H5>7'eGN :=Нݰ^WCg~o h-JE.PLg U).(&?W4 />)uW& KR)7>!+T1G+=hqxy|>t4w4uK)RUФ4$\Ò+?{''[9( ez۳ @U$3#)Kz@ay4.`tz}>,&j @eJ;u v@_ G᯾c;xF_Ch|%4;·ҬOacpOd$"_J !΋mo;V>z'r('5)6ԣhJVlr/ƖN W:{8, Nɒ|V1 Bͷ{x# D5O'`<%4/M/5._&ps{H9p(@yg<b 6߭n p-KfjfV_m" l6~d 4JѡHiYʊ Lj#у),P='uVPB5NK32$9R6=\~'' Κ[}ߛx! ʐ]XtTAa6͍ߙ[E3ϑj;uk @ o x/ g7vpW=/C.a ҃(SILVm5C!Aewڟ9\Ahp B0o훒X B.#6@HE/9 6Ftx= .G\SvBD;IcBϴ&S@)ôh ޠ{N I{%^"8`ogLme:h}3~|y\lo`mv$P4P/$Q(삡ۆ38{.b w Bg'#)W>Ipҩ-Q8GJm'SUU@c6&=?2589oϗ;QRL$KR`K?h X<"[˸hj ާ?4 u `n3Njw~lu)Ͻ(9\̩1/cpwr/}_5Ғ?(|+BP`<ܾ`'@Y׎ OggS@8 `33ݮ!5MC(!   0ؤOiY:+ \ Xhv<{u88ba UEL9^\l<_+%ZAQ6'K<^=KA!Hd %JoteNxfؠ9Yw+y `#4@) tHTNƑVMm?>=&۟ܟzV-p aS7ŨfnIX@ _T[-''Wtb~pg8}hOw_~QTmEUEz !9罜rw;K-+%!vmyqE%SW~4Ə}703>7`fqaWhC]/;4sg2in_>c S >o0dͲPI7T<؎P-@`Xi1)#BӰg69kZڴt[Ԅw (@ Cs~ュ?r+ N׹hjyvhMgkŏA6|OOcl9z 8^%zw-A/ΚʫGK}3,mKVD;7}oD\_M^Ύek%^맏柽{VmI#U~mJU]n؈vpN`ڑ]gq˙&f&+.N;7>HIbfxWƽؘCer!jCyJ<*/m)BAVfr=\>ka7nV} bLYNf/kδSL=[oqK(`? п<蹴~ڡ/KFy $#ysBk(b5҃Ao٤cD]|VZYPIз^&>^م#]hQL/ Ym"v$wZA:9`Lăa)lAJY9i/@3E@|-9ЅPJVjr> ЂlUG,.8x[5['[iJ͐G Md % tѩm|`g_04LAP)Y.pxQ5rV ?`TVW hQ 4s"ayӋgf-wM%%~hRd"Ep%?Dϑ9(S;XkZ ޣt4Ci еe5OO7m_ 6ܵl;@y`v %0"(I(!Itlo ЀӸ3yjp>3e,1:)7 Ċp[Az ^ujsBtL/RY}a' W <㈂4`8s0܎ 8'6G `Bػ` ed  a+BP CPn:iJF;0,7Lx `:tJ-(bD];/LQ |LB3}j|؟~e Lmɥ4 } ]2pK !zT{ 0 <n, %QYXU3^T!Lǡp 6 Nݣ}OKaD+ 77TcR9Jw%b4 T(jt,SaЭ tD㢋>h:!P{ 1F)V5"`z[0 @;[pL+?o/o>oYZ`En s TU(.~-}1K|Ok N%rU`g>sB TR0p`0FQ3Mx(7;|=tgr[ң%riNt$DogOtn?Nc>J1Bln•۳wTM@1r'KYGxM?]/2PAz]v[ ߙtDbC貎9<# G&L7oL74 79h0~v3̇=M-`}-tR+HO1ʋ ?.^冶D3R9%j;XIYL0Ex~􏣽/hv4-Qk7"`^kFt<'\<-5 V*O/`H~/sDk𰮰/7Pw1k iU۽, #mZh1Ӡ\Nς_ \9XiIh@@Sz!9h!߹O@܍m '/;\o204>24/0~99]40={B\SsyӗiF4w`rO&?I@u^j `n޻6o%<y_%OggSv `34ŀJ$%   6CGF&P?>hLݝ/O?BYgx?~iNvlD{EU ʸYqqM_ |fs}G߂M"m}˷}Ew<ۛ.lsv/0^|lg_ݟsOV)oJaaE7DU6o\?B`JP\I^c[,^m!9GY.>K; q ~ٮpv9 C V cKX|[MƤ`aJ" k M ƹ[&9)MG;ы$t|? HLG0?5zwR[DY _?'i=`mJPh7l4D­j"DD1 }/|r2 ?J%X`S_>(Ӊ(@,B^_wnJ7@1ۆy"`bF6@:0^dZdzyj hN?}sw@M0*w/X|鷲/Phtb2Gs[ WRf'! _:GxŶ*FIv^Xж 2j:nJjEwu݁:@{5@t m: oIf,Կ @2,ToH P L>%WB2?-3IԽ!`uqM 8 0[Bo~s̖6B TI$j@Gr aFrʒa" ?^vs @ UFpcK8K~7fal)`z 57p.rX&Gy3 d/ J5744 eۥ܄ĺ0zs[U,@: w~ M:7|xӘ0kOLH-vz/G\H-Г K/_Ś.<-}%&R7.V@P~v% gyco[ N;;h0Y?4m='*vۃ'O0;  )#(]X׶ 1%?7A&4yPZR[@H&Ơ spr Mրˉ.qh@RC$ɤ }al\X @q@vK [*$iG{?OSW~6s|E(Cvwy1 ]d?׿`/7mr!]y>$o6  j]s53i> WjZ~}08`#/9~ ^f;WEHC ׹=|M67s[ wwB{w3@t.Yv*x{0$WS?Q;oghjN `U0wT-!pt x{2V  f2(ާeK8FKxR?#?$h<9n(|7lc* 部U6oa)y":U '9x9~9kRܮ25J&>2 {v[cZ 4=yQK'~S~|p{.X W@՗>W|6 !ޯPJ $i(-~@%4 ux}5vݠ6pBHcc&E)R!(}Ӂ`x c^,xK JX3:`QET(N|eohd~]19`ZYL8;Ȣ H5|" ~_wmN~ޱ _ 1iH IP}'*B%Q\gfV6;^1m2&@@D4}i#`y@t-]_JV!Ȍ[{}O}2hk5Ɂ~鿞I=ޡ찯qՏ9Nm۶ }cϙܔ3Y @73 a>|.|FkXp`io #okI8-PW!_?tY9@ <;qbtWkl t@˧卺i1y2I o ;4۽e-k&&eϽ؞8x!\?I|;9_ӟ[zh=EyD;#㦗R!4' w*xt?˨*;~uqnkwʝ4\߃2f{m[hiz\QtJMu{.6 OG>&z*zEB[+3UYnVso>!!Aʫ (#@ul?Si/yR}@8%Wk;Jh[Pz** oUP/Fy*+̘Ah7)ڭTlt% ҙુi`ff}ݩ@t|5@yK:Vh:?O| e;=a`OggS@ `35#.Bj=#'7@2` ~eyW 6ea"!~j `OԁQj4m<ĉ /j>]w+Ϲ<~85И{  @\#}T *獊JZjyGOK_ K@$)@tnkSTiV'Uo1L-eGZiX.' NUvR!Ʊf@M#f7TNL>yaA :Ѡ7}w\} P&?mu~Ưv37E QNZl{ w(=~5rBÆĒB5&$~hߔolO̕`~\#8s/,&4U_O=@AX})5d@sWz~=h fK8 -9Oڢ**h'G5tg6v3p>mcmL}Yyn&@ٞARS9v7༏ UU/;}' xx>5yd,9 Eb?eu.shpㆹu+'^+GOx`P{7ך ȴ\{Gt|i BkWd OX.:EjDngKIkJ2 G6PW/_=X?(ӷ3}(`Nw!- }0`kCv-}@~M!&Mǖ16-=NRi <əP5蠀g֝1@W7MQ[K`e1ޜ5{h53o%]^y @6;,/n Bp1<UP@>)QC=[ݕČ@V̸=a1S{8їz7Zl#$G쿼M|Ѱ@:.Ridd3f[$ tQ Gq#I;mY;ɿ ~2n={ gm-R6\|J(bwrjH]#`&$B[].Mq vn@P=@??cgLz ?k 9?l~5#"ф3Ÿё/@Zh>DA i:+ha_7GJo'SNB~ǣ TWXV"nqT9Aי=@+@z(T{P .7X3b}[}|& m@ko` @^5bs>giWRϕP$<$8&0*[؀[݀x~"Uɟ7uq-a18I }δ]r &puAvR>;-@i $Q`}>Z .h3@q;RF>@oy#߷^t 1TIJoZ-^ub$!:1(}Aۘ" aW`"77~`w<Ԃq(  &%A߮>I4FL"9\ :hz z@5:ɏ-wtL|)Dߨl'O"A~ m <'@;;@| >|xhX XR#gPy[8D@8v s8ܱS @bBPv9PZ\>KO46 B w^ z.9&RI`ETP\UW =s7/23clǯ/{NU{;-ܨ (&\G~' 7G#J^{ mz+5fͭ&>(Y5t(/J8xL nqAg\ SU~*PAs[b9fj+*.#v(>@P3-]rI)T(yw2-ܹ[ /3Н|u9=x\/xfyPy L8]@*&QO_O u+ w 7g@ @G0y5GD42;O˭PR(q/5IS.bC#'`;_|xs gN .x0w$ZO,5إkhLI.WcQًÄD _OĆ2ps'2989nSUr klϿ: B뜿Ȍ:&οE;[o W6 V:P y3It[Ϳ"iVH$O] ?w@ 5 844 *3[ⷣ04_xr/L|o`z<;HCs((ސ5)h z @ ** oNDaW?ǽcwmW۹Ь۫@ x) `G`^4Qw6y+DahaYj[|G e95 ,5IdarBz L77QVSuW@b*&@ Fdu/ @Y# 5Pg VP-@_@>~VA %OggS@ `36=P   ES|3y3b.V]n5j -U_0h w ?{';xB pB |ia M O;$2ַ~fdrzEE] urA2槱и@}A"h m'i_0j 8-@{~g8z?޿;4X]j]:i9 7TIXϑz("ʭ#f ʏ y 0+0OP yevh`j P6$4F$ؐD 0+g5-x-9S|WϻP|( }(1UHr[8Y]_CU@Н%R*@ۤf3)3 LOb/t\Pݗo Q?~.#w suYi53QH:#TPm娾;_!Y6my eؿ.xp88x(ĚPgO TBVQI;e@m/Нs!洔ȑ D_aX7yY~3T7iuq  -z>XJ\@.kؑugq/_;o@ə_Q+>Yo]{7@U (L@C|) P'yњ2Oc>Kψ(^E#@O$1['}`;SʃUi@_qhWu2@Os:C!\m 8L5A8Ez?B͇[,+bu0,BK]@8O/z ٶ?(0tc9Mo@#)p}@ByLhԙ>8~y@EF.V> [2¶1 ˛o0KpnMxp 8\T55xj9! lT! @O7tOobPTN_=/o"c,zڳu`u{<@?_2`ZCS ~#%DN(^÷{ o?X^ 8g3ݵd4 Zw+M@^5#T6~ .P4$%<a7/ ?]"O ȿv#,050;7 H4?5y Q > f \?ȷ/dHZt |D ??>z4IREn+@qzYrl,5[`$ nZu8WV/PCg@su-,HW%nnh蕨 Ovk2CS:Y7H ̀op9o*pl/A Bl ey1@&e C5? c^V^ Ҕm[\Hvh<<- ;  %v$էb 7o >~LZ 7Yk =n@8| @. 64@~3" s| ]dW_hf$P2,}a[|[G`_ nCx&]%WdF,E'bx}wcdWx^85M?Wɓ  d[mS)/#@b-\\Sף@ƜƇ 6_|]/ɝ0`%@Waxy~ *Z (8#L6.+ˍPD؏&| h}O >w1n`ɹjsPExs#efrZ P+gϢx 8c% PfXo2zv) "~Z pW,lv7nO#/ %F~@C toM ~#Q v{w"BU}Wێo< (x`/X F*nThA Հٯd0 e<%;O tZJ;@yzici TYd: ;^y(LvȠpܕ%l #D4H{? ?ppwopk svS?@ P U[TAe7g4W35s?Yj]>ő$^\:d]~& pn|ڲ]5ʑnir$f;5 x]!Wg-H3|Wm9fO@s;.Su)SVuUh:8~;eƿN?Lܸ}F`]@, @|<nb8@7>+>io.bݕ3$A@|bYp7 > b{a4J% Hd@ 0c@]ݙOb*> PP ʚ[Et@r npv[Cp _?+p(70yn<|`U`|l+PWp& 0 ȯ  >w [qcWO5CǬ?i0(p9`j8}iF $8Ќnj;s:{BsR{=5Wq)"p=ӝ@J&EeY)XEc7erN(E@O@%0Ќ/@@wHU_^  8OggS@8`37k &MB]5U^?P]5Y~a O7<  bMAigeydƥ|2p W,9|2g@~NɣTUJ`vym΋Z8Nq:DK:} v0p4/Ώ1T Xo_U*D^BP`~y5OmH2Wh`8Ÿ"fJ`==@@&a}NpsxLB X%hķP wM ˒(&5&mBƀq'  oBPwԯ,U%XC*mvui.Bk) @^yW3L{>AnRψvy@qR| o|r7k9\#!γ*6$SgH@QIt I1 x E;dPzh}P(-Sck wlX_Ӏ4ѽ@4N@7u`f>ͭX@D,eE?? ¹(>y5;1)OL;b>@^X>_,8qT/8 p!-q֢NS؊ؽyqXPhtR]6P~ (`]օ^@e;ܟhazO0{Tl,@Ci > z .!wtl ^y5ݽ+p d0GL4&{pB<A".z'a{B@+ Q[21x ɻ Ah/xrS @: d’ YW0g *8eP@^+@om[/uxjn㬽ڗ76VuaIH—E}mxO 2q#3FNO@^y5ϙOC\jQn_@Qs )O 5pzG M"| Ȁ'0IRNTcL]k3 o .<)@{0h ;i dL~'ƻ@R:L_0\40w{n^!Ip ZEQۀ Rg0R_^y5洧1V󿻗2 @\ %/߄f†΃ AsUE,6&`*L4N< lv<@ '7QO2@@@:!FUiu6ĭV~q|UL?l Dܸaf AwJЀbޝ ^ OM?-І<@KnCr'-X Zlj$9OȘ6[fF9 3@y:̠t f@i] 1| 75 NV_( _; _7 =.}@uA0+D`z䨪C։p4@ >iG6~#kZc*[0ƭKnc0 `6i E(@ aW|n'r49a8h^wx ËQNq $0,`3V(rSU$Pv.o@9'?0<Ǿaj|$ڑq yHR0c@I۬UJ%g24ig~۠j('OOz ~>/ =gL4&6 \π 'H9YTDq ,f`o4.(-C@zr3l F{>F2@h~j @Рt7\^q;^O=5`VU QMK8^- lBY'͝oM`DbV+X8#;/>n@^\@č@/Dހ@ș@`kyKӃ2 } P@!իA!g ge }g oN50`ʕ<PpQgO·-(oP3<Р^YW{IJ'([,|R@qV^|Ha>݀@| &p3Nʅе <1JtXl\m IM3-AMJ']֫G7m${? c[78x0o@Vd"b\e%k3Zא).G]UxV;|[ F )':""݃@* ,mp@nx['dS31h; ߊe5KDA ٯ 1,LF%L l^9M+a9kWI6#ӽ?u >$ۅx 2għlhx%@  4PĆ*L'y@fob2/0憷; LȧsNs0tB.Hx+|6PvQ`GwuG rܖ4z~͛=ߝTQR_}/@i5ı ()-@ho+zhWFZ{phq[ x gfpRUH%WCv=(D|x+x(aPX9mp +"i2  80Ѭbg =Thj F߾y :t"T(caئڂ+'D|HZT_ ~Z; bblOggS@x`38[1     3s| INj'QNFz- |B} ^sϽܸ |z ed" ,Ki)?kj5 N?@  yw QY;OJN(T9 ШjD0@n`|}{ *_ o@A(u=a8@\Q2d MbM hn~G[f?eT(_JRS<)0>|;} hs$0{q`+2$Tqn `֕m#>&wB{N Gi t7eǿ@Q dT  јxx0⍷8&@o0}@i t470 RUGib7@yP*}:bD&3 ,,5.Sg*Ԕk./zx$0W PUEј@"7!Mo+耶p5(@yq.`dɉT@U,x꒺8@axW.J\-9v66Ч;RۣOt ;xC=jI FZEXg|#!LC'J79 / ४>pg2 dNB[Q>N&A‘MG`-D,@-d=|E2Pp+-  )'%nѕ%`~1"lܿ_@AK|L |D.քz1Mv\jcmbci >v`> 3_cRmg* ?4 S>q46l*o 1Ȅi/yQ(j5I*'h M`t,m ȼ@ -)-ѷ8420wDhmV .7/ @EBf(4c/+am"uǡІ*mZZC EN2'/C^ G IlzE1> 'D@@=s`9pE@& h &XPߎGBYC2 < B`"ϝw n>@ⷴI s ㏦@i7M*J+Ԩ9I -+O6ЂGv$1ղ-#j ΁ -E8I0 ZEM z#{*<*> EUUо; =jc Hך! c3 An t=Z 69~)Osl?8~`UMrOȐ~[@?@T|*8\b㼴I7*h!10.#iۍI6`~ wX\z ?| >x?9o>M{'xc%L`*`2)tpF)<>R6Y'%@?>v̆q y/m '>a`@2PR8?`o)ү d* @Bzt3xKaq;F&@Z~e^fq ^ WP'qиL;]~XUo"9T O9 'PU!:ؠER\h$`ٳҿ!>K@n@痫<@tO P ];I K}y p~|3Ze%om UUvKe}L\r<֩ -kɹR^]$ʃKKW)0pJky_w8@ɀ ^b=w kǛ{7P?T_)0B= $Gƶgn;6?#|ĉJ"Șd~n>|<ϘPcȀMWF0ӓȌ;MƐL#_.T<*! \ oU!-/Te75d`e}4Bq@@` $_7 #B2W]C~V: 373i>ySk \7!@x]rE=miEN.P:_.@6BPSd(*) 1e549l`n] v4@ l q:l+=$2|1SJ>7~%B}؃17Ą(9N@a ~ WTy"3x$mHj \GI|-n*@y%@ # _نWUU!9\Gci9X%cv;N2xX+~߀B :Ig(?m o}Yd`1 ,@;Zenp|`@_@La[Wi^SnaKn ͹*F`.I@M9VX(bn64^ ̯w&<2Jh01gz¹ʉ-2H<mL" PhL{|^p Vbw4s -^>MoOa9A˯~ [5@f؀ `c3 ( y3>MvhVNm`@hDzn P4X)FG uiM yW"ڎ~S\khVM$QOggS@`39M=  /227:AIJ9~Wt 6( s;&yzN9dM . | _0hAƿ^8A~t= gvъv0B?9@xe9 u| >d AdY10t] Xl2_@D@|1XH1 ATcHBhjl F؀BxpSDn՜! ^OQLol+`x䋢8d_x/`~d8qHW.*ߒ"xjxֶ)o}G_I L_}UO̡ ,9w4tvۯֹ[`:~H ө3+YAR& P_wvR+{6upL#N]qJ2p瘲|p~w:-9m}DRHP^CoH7xiŹkP/*VU$dA;?/} L8_;B0ςU6krBpQZx(Th} $E>(mCm_y(X14)T \+HƯdK~RhNԞۇ3~;`*p:Rax}xg0 3._Ð,3 z8I) EpS p8~C$~Z 5TBB!W[T{'S1=:@[qZ(J:: dϢ=5\@XN;;\Wtmކ:#'\?%g(D<~a<zpQV* "6G6!1?iqNp>O4vf?=hO=‹Hw^Ѽ7` M>"i HԺ *@]y 6ے{er7aL~\¥kQps ,|^~@0(P ^ P eĭg i1MPl.PP~͚ƗN溛`2tt 0+TƑ`JD7&^wg.'X AdMy1 ܏߮>{yylȹxX&%F!PZFַ !e~q2M xqOhO16G 9זbR&Rk@eWZ?{0w`}`#]24[!tY&d2iO*g|n4Eǘ&YLل-AyMމV _Qh1zΜ2펦oݚMpԢA?%`IKFNaA^͖ >0p[=AKhevߟ))llfdkx7z!=>H}̋r>|kc/L2a\> tU(bz쌳7'6Ouؼ:OrKn^R)Aw_9_oCn0]qVoh+O% #s?}$p KY$@)ViPh,+J.G;*^ɍ4m5'Ip 6җk0p3=x9\g\G Ѽ Ki4A #&8464%0k@%K6~𚴸AU@wMQ@طA=&6=a~)ck6j7gFc7o2^޽@akk2;&f7;+q9W. ^lB",.OggS`3:O-# >MK> ug&~>4iZ ޘ5}w;݃. Z}$ɕ* o++P9Kk7RP9LTH {$2Q@N ǀ,FݡІ766+J $X־OZ044e3 rtԗǪ6@|tKA& 0<+, az9 e)m&ž%sbx+dX^'kzPꉐV"j[CԞ]Osvr ,R=;&m,p{8.U2|hJT > ^e{v ݃O-S,Jy]XNo$Pn,@3 W0?0W W,gUCIl-b  ӿsj xL^xe7b/-吪iu~oų}ȧW1, +ٛG3pհxv `>I*CZ e=|z,G2ڇ@H'@+%dY#yQD[@PY5"Be]x{%m#ZnS:S!S_23 oemK?I4/ܼqZ A~P]Jyq^,ޕC}] Hz @])K,o`:(A P!%"7ux03rэT> t}HUD>Oci~|&E9lu8Վ`3Нe dE.Gx, e]K0`,Bvš5qwA1/%r }^0~~m+sG 0DYfջ =7"o^mG0y ;9<@(8wͽ{w]cϫ3.0 P blZI:eiJ\8ǐ^@ԕG3eW< x+0FA@FÜmƗ,r踤-z *v . @ ?A@^xUE?3D@EUq!덃o0j{5}L6@<#`0ӽui 53 %@xi>R?=B h/ EՃf!opA_`uxwƚLf`˺p4Eh C$@@Aړ]ݜzc`<]~VZ+Ü~jg볧WULo.9w]3%ɫw7QӥW"DYGT쌸ĭ+ O 1}`Wƅ?K}M_Ml1'=L&qj?`wul=L/y}UL^4[=Cʳ5ȱG=?E^?~o@ޙW7*RBX(? w+ɲq󳡦^@7۝\."AKXӘϷ-IAlݯ{F_jFt3q)&cy~GXVafu=saw]ӿYr#+vuq{۾W__7( 4 v%|M!ڿh r^xTTT̥&{/VTTT9};EQnRKnl s;ܚ ^K*y+{{Wor)܃E[oo_[[? ]_g}-nH_GJ d,*W=QAcU>[F`$^Yvtk-=8xn&r EP Û< A4AUv 0K EeG (;PIrr|Sn똛́z\ -<ӐvA\(2O40_e_-\N" Ch{!G`}yնPYZH#y.p g.ƞ>!޴ ~wȗ5>K!t\`0'V1zK*IRavCyo|锌<Yw >xA0>S{^',*(\{qu2|]qpোdnz<0c@}67w L pu\^;jjJ@ #G>9fG\h,kVKg!yqWS^^U|@9t%a`A)"]͚}WSc\,Zr4E,q ('93zEKPUv2s"Q`TLJP۟џm˟~ 9㳿s L_rtN j?kt2`BUx@k ,nOggS@&`3;v.! :999=8IGK\A.8{Uv|!-?upgB''xpE`<܆-,W W xWlL+ k[^>9^E6&Rn=0.u_O=AMy @۟$&s)J!LQ<[꺀OZp3=4I @F?j?)<be1?ב=:{ y~"HK?%P_@zxWO@m!ۃtHW~rXI+ |m6)EVb%YWOt;"(sLݛ׹Gd{nisPj\Q92- =,p F_ D-/ 5t1l_.P'+T|j`Hq@%+x@( 6   @6,ϸ`:t|nߒ(ʊ@!{ ~&#wlN+je4`~^:dop鏟Aj͆"XЯ[l+NNmW }6p@mZQ@(P1? Y2wfP}h=-z~Y.J_GnyW.,raeh^r.\O$P' 0 X,GNnO?k ^=,'z/fO9siP`_<4|@#+L+ Ur)dȍ/G`; >t ҳ˓"p*^(GOW|V@rNȰ{dM@ dhͥq-%:(Wv \D@QzVJ"|haz= aw:d@@e|8ǖ߮fjgiw͏g-cO> Vt{ʾcu^Gt+n]JxY~󧎛('XWlc@S `e"t3j+~_ k@吂'9$T' P:nEm^UQC@X:v$I |s瀍R * h iۿhNo6;ph- [?.Cd%8HU_Fc;lqZ,>vUM 5ʗK< "ZPtx5 )$ /VooH>?{2@WoO P( P8%x0LAD}|ǎԛ>oު pt0Yvv{N^]ۻi`Ή̠y}I5HO6n]@;)`aGvp^p{V!@Ip"4 & mۋW^)@Nfk?2!1K\6Uׁ34-b Gu'[vΝ+ qE!~-t[XC=~Lԩ}ab_<@Diz&?//xG[-'2oQֺǧ,cп H5j+P(mCcc88ohnȄjek =\o@$ WQ-Z)i+=2}ќ<=xkR\R=0Ww j >ҷo[띂9_ߊY1^y 59Lpз,PVk {Sغz / !`]r>w:ko>x 7Bҷ{)lk$Sgo{oρx}b@-.GXO7O{{~? .[❿cd-SԷ8j-%,,^R4|#l{ <.j\:u)T( z'au":}\l|i@MT`Q>?FA(tq 1:0\v>'p>bxrjd~7?Ǿ ͏?s}x$g$~$a\ W39S&y1,|tjͨ>F}ׇj~V7: u8ueIV]>mG~s[w }1Iyh 0ry00w}*6/QKPaP]iQFlS/kvr(y7pW.G& g;] @fۆz!ojOWSádk! @p=նk՝={˻ˢ<'9?1;Ԧ6^)Γ'].g"/ct| Z/9oMso,9eb]1Iw@M SmSבe!MǷ)f@){$)i&loEoJ}ycםjM_@N{ `U}(w{0+ =n:\t#2 8;.2p;=ӺQ-g7Oq'|U@%Bh;/|Bmu!B]%L pv*jd rԎGQ-2<_fɆ\8Y@@QV(`d%@!>Z,߹^Zq?Kߠ{C,g>~C3jlN Ho~0 <8 ο5l0j$`B"Ѡ"(*j|ZdVw3p~O x]/<;f @PNv@E%kPQTe'('WGVtzhџw p"NvZum'iDcʂ. p^`[( |$`^*WJ}m :!Dҗ\=6;-/%vB"5z sNO aѠi˷ oL\L\Id;WF6Ö{ki*s}/ 8 |x:dmGXaeuy11 @@%Gd+*,ڨ" z `D%oi*{s5-pOggS@b`3<z pH/$0 ]l3 (Uy?:̵0 1ۄa>]lէs~1~A44*:W 0(]03Y .Z~u?<FpYs>x dr @<}!eTww|O ʐ qcd~8e r]սz " P*Ťr--}䴃>k,h%Ês?`O ~:ȵsF *6eNk0C(DI@N|w5z(x]^`ǷuH6>Yߜ̻!K BO] SgHQ nz,$/+ "hsATl'Ǩ"*"؛ A2^Əp{_>1V Nr<w@XH*](HBS-P̵0~71 :&a[Sm߰"o(Z@~ X+(xp >WLп[Wm\ `ug (ct__w\(2։Dz ЎU)9k,m$YR zYgEMOn7S$;G.Usn~E<]3g7 C|:(jGoh4 "`( fk0Zv_1\o-?HFf>PD}M ~ /A{s ^%@ \۞Ep|DŽoDBTGNwTC|{Rj/ꝼjz@(NZ9+%ߍ ԺtC؛Z;/;#7nLAAtEe'QPSX}KFǘ9`uSqu:oVpBRv `؀TE;V~0\[]5.Mۄ7;=@z xPGAV z ]160uN. $d5׵yZ<v d^|mp P~0Tr9r&7 k0>,Xdhڒ߲ }n_K NV*7ane^ mi ?pQ]d @Uo~ݳ )V;2wJoR**BGD= ./LJVpE_EpZ*Wpp  ͐4%+[])DIvs0j=d`.xJG23{_ػwZTgиy J_u'U.*pTiXȜ / >.`TX0 ePVV>6=ևWa>ń~l6W ΡT~hw% 0S ָ&^(ʊ c&\/YP~&2PPke @ۑ\Ï[XSmB  ]d "ʲXLe*LW |n :wϨu >,ƈ"9z09kZ%7){+p` :'!d @h˶}6_|a3LкNv"kr? FDc_ LD}L007"rpy,} LZw$`sd幍7V5YprzU"N d-deF!xqn I(B0y' "^4[m@QAP"KL؝ sGsL}F[+@J3Z5J:zg`hhZP& e晋g.exg֌ @/{L 0(+X}l vš`3 x+oOH^2-PT03#/Zvx1qx`cQDwU@5P1d`TUiavnnsۊ>zp2BHL@X{v@/< <Mo p]VuZ&MQ03wH^uIdC al %Q6:Ap,PkӈCA['B0P+z@_t92ɔN(gחUnK W1Ȟkm`@*w@|=NEDTVTPT^*bCp5XЙ&o BW%Ds6~♋hο/#e*KJOY?AV(`( %`b`}j>E1|J"@re;P)>~_sz )!P:J%\/;: `cgFG( (ꄊhblc-`G!ԗ}9>u.s{*GJQtC ^ #hʋe+MkV-/_'Cr?'p_E ;3 A8`ǻfHόުmO|tGLI.[uA)x/3E87#l :0_s( ǾupPy:av.r޾llEXV姊B$F< HzI! w>؄+da|uM-ۑ瘟vlƿoK"&?@ '$ /2>Ap,>K}1B్b,<Y>6w(d9sK4Mm""m-]!^iS9I {~cǧ%0@?R7cr9h}[)_jnA:aCRn 1WL[? كݢcsP@k⹯ M/L pX Z3~P~Vzni`2ز$y &8 k \CB;>es+B[61ݴ3"P7gp)?@&4(_ND.@YnpkNEz杓?< gB㙼R@Rɹ.dAT $S?bSJR^|A՝MjkV$"-G9@%\Zl [.7{_ymt߳~>qޡq-ևݏ+){W61b mlOggS@`3= $ Kc¢MA=UCudF; 3[>ڥ-ha ~nJs5DՇf3(<ſc"79(w: pZqf`Ym96F<Qz_F\mtaGRԗTOh-8D%1%4 /oPbS+vkgH֍neAoz h`;͏= Z>^sIzI  j7 ;4J{-UUYM٫d h|! (Vc> [^3C+2RZ'Ϧ.@uՉ5yiHh `§zv#(_5Ϗ^9u*x ߱gn@+`3<&䬀܁r} C1_ &~`֏ߐsfXDSXKO,CUs2x((XG_ @m`CÑցWcėz * y.%8 =0u*!awt71$'6UΐuPCWsxM!^}ɐ!%CLu<`Cm3yPgrDp P5-:ogLET"Y)*@C=' ,e1w;G;yKƟV -tK?ٴfszѺ+=}|XR9D E0:( 㧚GogLx $?L+-Ȅ\xRO N}P*a2υe"i(ꑕ0Lt^ҁz.z~{s=pH@*!ZZl]BR"ƶ%t$ `2` au4~'v&孍ܗun +*Ɵ=/W=Cg-(|kP,K 79Ljy::;&Ts:د<%S"o^e&~H@_Z7vۏ`h45@Yj]jK.0#Ex޸enmp "} Y( X*zgF!~0]~ 1+%嫵wtBHdh~Ku˔?50>( %`կ!q/l!ám,Qq)' @ m ë%  Z` @Q-AedXٽ  $t-gDe\;<xRoLP'874W_6Rd[@tG%dVQ  n`@eؠ@ >wiP.d?TS7_px(n_t`'>' q'%eXSX[u.uz>w)'O 8F1 d L&Z@Ǝ gFS dȌ+˥X5FhLn35 ,~3z7 4 zos֙K.9oSM_T TBء%t%XDU)c $RcLF`hc66Nb[;4k#!}*Wl5dYmZ ٍ=F?_\n .tBΕI׸;p)=;* IzPw_ŃB&^y/_AA߻}37; ,05^Сށ Ά߾e;ol`..^ nI` ,l6ؾ0ⱒ TwlMo!)5+ *W˟l=q'"zj%p}5Ǽ̧f`'Ĺ NX$ L0Id람ǀ";WJ7(NP`2pMA6s$^u=JtWG.2| 9OӀPNN2o`~?L7p}3-iefO%g:CZ#:Ld[ƅGZ$"W-6ne;fo; M!T7#j|{ERnjz*mxa} &r5sEU ^5A6l@k2[w Aw^;)M1/dCLZ-̤}d !h- FO*,&ܿE3@Qַ `dcTHi'\%_X5+iik[eh U!)kALD ^&=,"H2xj q|><\l6+@iJVsA;?p<Dq^o|c&lq{'{ JZL_Ň@Hf/vw@g&CՋ$qyxe`f`o' _T"IBHa e~ĺ`0n3ߒAD+^m%[I6 =~(DOl\*x @BNr:P;O|cO+k )HP~ۑ~IyL2|Ⱦi; G+(F_g"Igs2N,86Fc˿hPДܠd~]t.sWEYKT!R( =OggS@`3>k GG/ f@{iފ3F$ nq&~J{SDZ6Ps!dHLh!ѡ*#`7mPQ! ,~-t}&p[oA³s-r cx ̍mOZu|'C ,&>"Oo@ ,^F _A n[/-0v>(z 2 ^z#1Q-mѺ ڭ |? @p w,%C@(ta{aaP6JkC=Vלf_lG1b ΖK u^*9OOW) ҁ&gc :s qPp( ^KӃ@] }7$,b1F,0'X_ }>+ϐ,I(Lf Aj=< `$ . ,1)G!IiON>W6#@q!$ TQ314}ߵ-wS -L:Bkh@ w7 _fPCQcyi0)g?R@.@ך6Էcs"?J*`Q9xU@-^zgD_n\0OI4u!ژ0;7X* ^0hנ F, P8Y<~˱A-4Xk 9VtB~]2?!@x+A}H(]; @lmȞ8pl_9=a1_XP)W5ZZk 4qb_t}, Ϫl(T/Ī -H RGlz5'1CB'VhsNDiqg=kWsVk; `)1B!Oݫ 1a/*(n@/W sQ>WPAWKG<O=`6Y+O"펄XU\賸33Mˋ&c "=g[/Q&1c ?Oq }g}:Ythst4æwWWPC<sWLohbpPV֝Qδ,6:e3fbͼϵ6w7s+A+JZa۶q>l| Ҵ ?N$-$W6c$'Hk;.wDG61<%Іc;nӞ+[x?'混3 'wF[O?T;9WEw&ّrʔ4$manZ˅:^D7'z : VG$=ZZu"; kggy5\V/wO+gcvy7XXK?Nԟ:4i;M)nW1ϹVy#1y.9 p>!>x2PQQ1_5?** BoLjD]@EAkv0g:4w}wj60fH{k+'}Kd%`?d v+YspSRIΰ!ЪݰOw4?:qtLӉwpgvvNi\Z@<n9FB.B0d d־U "$4Th2x최{Ӹ0egYh/;@/3t@ 9m8t߶7,7+6`) ݭP\ ;Ys)i_3T?KBwelD+MqqKL+{w`̹nS eiO|`V9K hdty2QW 2~[KR}©Q'TC۲ϝpLs1}|@/ e/#_=#8as6yP f $J 6^ įeL0I6Ѵ5/%jQxpʹr.@>q@PPn?I~r>֡:It @o[f< d,;-ȭfW+7緗yUaG?VUoZqe]~[z+S&S{oPnY?wj?n8s?{u4H4b[o0m*D6 t| O+ ߤiFz/I'NPƠį „uqC7q*(dvlVg=XL6ttgNpe_ PZs} EUՃz(8WF3O8u)ǞZ|˰<}0$3fQtyeqF_ =_@ fh`}%c@ʻНW,V@?|ZhdhdH|1'C7fQה 'Yx npvly x nPȸ-Kؗvڜ9/h/z;VAXث M0׸[w̏>F޾u(tVw:@k/დ=#NwO` (w/jՏrrYd>\ R?6D!(W0Ofz `hoJՑxp!^poڽ( cL"unD]}(צ rCsG :޿>6Y9u+2yQfBWDyGaQ_v s߿?}7?gسkp8-VoOggS `3?{ !  BHEU/ "~  䯘̢)E +y!|M8ڶݸ]:p F _aV}UC@qW`壅u@9\.=LJeu5Y9 `fP{1ደf?[ti9|}?q0rPRo7v^)8jrkS`M\~W ^;ˈZP-% QbW#5E0O7 8XlSR h5l  _L2 k w G:ɇq;:  :a3wo9?~b_;# 9h}N&I~gYJ㵿4[l {p|qO ~|ă. ( ewD/]V؅gbᵙ}A).ÿq.bnp3fܕ'T+ zi5ٛIFPBkK2M̦Ch.2ou4=:@:JߑCg )X l   @E?6i%0@"_NCSrmD?S7SQs 78;P @Dky_K$NHi(j5 ,gQQlRTG-"Xm73q1C_~05 p3\ Kr4a콼 * dC+ @RdMϛh@>*3Lms1.(||.лmdyJ/[ @~~ !6GuX XCТ\ӬJ) 01\Ӄ>+=uo%_Tߤ/|l<-[Z`(<^6645[56?RvZݣKSw~jghxЧm4ۛEaG ;1GCUpg1[Q};l_F5+9*O2K ^-ʔW~7 \h?~r^gVĘͿ&4cۏц5~؟y $"Br]{keY/'7:w۷qho>WrLx糳Ngh.ȅ_DEEb6NEg#'UY㟯O#Ngo%"0~ ?Xh񯗛87yz+`ۍp(4U]k{ke$ âZϚVÓߋ&mL8rj@JIK-d[x0oJ!2MK['r^,@;(D1 z*y'?KaH8sWrvɆaɁc|;p ȑ)@lJɊJcy`_>f4%-Byg{8s:eSdҳkx-Dl6P܀C@\ W: `MB @꾞vWGs7T{ 25Y>l Rᧉ,{yq *&("Tvag9iG;s4L4Ja廀1Z=\h=i@D"/OU|ogaLB, pI~ECAJcG67*┾m[m韫U- aNܔ {.x qE0ĕ`|cЄK2E/i۷OJw0^?em,_n<>J`Kdvo(;_ #&6,ѴK>tlbwMABj$yG|Ff|ps= rPPQQf|i`s 0S{ :=a?<yΑrTެiЁl!zo>agr. I&8gWawi Y~:BTn-X@ya *`7sUp!kK2vu& - \;}41s(w-;C (rD [J:t 49Qՙ1[i,:,j<9/l (j9y~.soO?1٥\]3%(LER kE<VP?&@C53( opU`- !@.m\u^^r ѴW祏v 4, kVC]C@t~r~Bh_pL~8H+_ ٟDA ?C0m@o/ma٣uG$)350'm1 '> 7@w\-/0cl`G`_\ NMh@rISL´Z{eJ^5=>ߛ%|=<@=w ky( >wED@1 t?2t{fX8L 4Z㜁10,IElҺ9ҫlBTӔk_8 vO-!K`"EizğL ?% f38 oY3i։ZzK7ޜ`nJu ʹ^h;K!(@nPҸty|+2sJ@W;=\U-DPݽ:YRC}q}~ qo~k^>Aeq>nkEfK\̉g]٪'_)9ODyw V ZnBSab=|[]w5/I^۶\Zay68B{>u۬zz݂,#s277s_Wŗ sP`J@s >T@Ȇ o @@ V@-OggS``3@V   6_l{l)ТU(v+ܟ49Ko>, Lhon(l-O [x &Y5l ; s fxm )A25C(`\3-l{3@d%u`|m@7f N'/F/ ?~N|)>%{:8ɍ]ξgS8 ѝKI⇤S-o\}{cE7  Sc[Kq5^.W~c({&NŪ2Y KXP+AeK(//gn9i^;( @d\RliD@dj4g(I:u ozۗZ?d@6;0Q~%}q4J.ɽr϶SVM4ӅbPv1E@ls07^|&g30UW.`  |@"Ut4M?4$usu+1SUjvֱ^R3dyQ gk 0L#SB}& r75TEkQ{Q~J)تg톷Y}``! [`o;0#/@@,U%Տd}bw $Sɫ:!e&·܏o@Kk%\56'T`B @ޝ2MLX[-G"!gU)@>tmJ.S#*%ԏZd28UlZa:3R}8bkڔr*wNB^qūi@5$oFx `<;zoOw>*QI*Ϙf^ '|vy݅8 4\, @-g 7Ii\BFcz%0Șlw_Dy[%qWx_m7}SYd@֝E=dt])C>^ Z H긷S >cA.`yo j{]d\ i~!M~گfp{ 9/vuZ)?Nlጫ y=L%4$W$  @Q? &#^E;Y4nCpz-K|̕&Wg[T\tT[Pq (T\0FYʰnD?+oDU: 'O_ʩڿo@]q7{yߑ@?y@87^@QP9u^TÌʻfm f35nin(Ň 10c57 KJ0ׄ] Q|S->Gl4HhZK =9,`"MD/A.U1-~`Ry Zh(6 !Pu_ux%)v@P Rm?3ʉc0dO +H[pwՓږ^{9۳d Y~;옪\lAiU@`r6?q( 4>nͷض T` o_p xS.@f A eG* i9P]VQ%yc?u~!K/V^<"JB>h=߁nQ.` NE [o_$|] S+XIz(>[QRexKR< *~Z5Q\P}q8 p~ N9lK&SOӟoV?gTezDO_ȘNcU)~W1JuYь?>p$cbdvWz4n0 uC s`VSlpdM>&uc:筣{;~SɘTjNG6 ,Ec¢tRÌ1QySI3GçKڛ4",, > Y; 0ΈΏo>ƭo _1 `rX`Y3(|Wp>%*q9 Y2L09.QbH(@_x_lxp6B8Ugj){у45p_NNmȾ@[g}z3nB @qq[ PddIO.oQg_q_+Md jH`1 G_oo@06%cY|Jez#^wU$hYK@YBbԃ 88~-PK].? W8T j <8^z[>*Q{Sc!i+ǝ=b Ƅo 7ox%{WėpBDbmC܏;BZhę dډ'Ş d 8]s XL`{7[&~0f=? tn_wxп(ޔ3J@WMlF4V^ T!M^@ G Z~٥Op metꤖ࿓0Od֔hC 2%H?s&2 7@bNA%zLjvhyVv预 !ъ=~>ޛ>( ݰHϥ@t`6`K ?Ȏ#8H:=; (/t/E!GHZ<*gzϡU Xr70%ɚaFDm<0W&^s֮ -h?%6@\]1T$L%8ǝi=qB1߲( 3mr^v7+yCB&TI&}*n06xq>;[ɔ; ~%~Pymݭ^ WH 9iBK OCSRYQQdžd\+4m>n|hZğl0}ϏsR־!,ݮ9/K|́{ LLU}n .a'쪄\HQB#""ˁ}-qWs0PӺ) 4RNCWVu)!0pJ2>+j|`g P>$홎y6yd S 'W[ ÎBS30kZj(zpBT GC'~נјҧ`:s_YSJ6ſ8.FU)h::YsK!^ȵ?-m5b-'ߤ}x68ahGzΈ%Az\ֹ -wršl4mhun|~YSɜdC;Ǻr'? R_F+̼;BBW'f Ӻ1P=@v96'l0?=m{ZlpPRy =$JH}D29j,@SJ X O}Y>%?hʖ9JZDњ6;ŠwD( /`"6{|w1MJnX&۸|Wx߭rcCt7  V 'G68&sf7]_!2WCdϚ}}IXIu9{pP}'0u@@}Wl@5s V@w(yHRuU@϶ l ؘ4^o p@&\_=QvmABAlh g9A V=pD{%4Ȩv+s>xN]fRN.ĝ,@WBax[(8t'N6L JSk3૭g}QaA \P - vƧ)7ckPR+ 0|I%B?`P~*J2&.K?= ޘ1=ѴZHK*$b _o 4/{kN`9oUo(uTBzfj_YNF !,ihH\(d ~3<@(d+oaT?OAt3:pe [V|` UI"T`~0P\5}@gf%Dy&@iVXL@<ɝrcP"lzmYicraKc aлV5h san(\6YۿW\> @7tkGJ"UE^ V$ &]bxxU{xQfm{dƀ 0.gA+9h%QZal%uCi$$67x1cgO垇~] 㰈 TtPf}88աXX4T Kҡ7&}?Py_^h\10 @64&^0P@]l$ B}DTJ<${Gba+h?0WZa67O{)k$V6%`>0sh]k~`(12BTb}ޥqDmwfGv_Bܿ!a@c>M,Uv[+@| /|%l,(Kh?HﷸU]o@>Xd(AoևVTd4-8 @% aA-@@ozݻev%Rd-P=W 9b_V RJnC&W#/qx Q>he^@-GN4΋/Ǟs8t[r>v/} '@UER 0|oKXjY@H#fb[I6svxSyv;xCU } BP@HS+W[k]j^s_ @^Ƨ%N2d0>XWy5]v2J rt&; _?gr ; FQ֕7 }kN[jk$ ˒$@UBߒKŨRS-T#O8{ؾ(hWN<)lO礠~` ~ L0o^[0>Q^S!%ǬOd<ۻ2_=|j(Pw!CCy@ _d٩2d Exdqx~;T$٦M>`۟ u5KO>wRPp7c U![ ?@اv*{/N/p5 '$M{8e5'E`/NA6o4Jf|aldK*" o g1ަQn6at(c Bֺ4^LwU}TX|VA p)hg%C\;/Ͳ9΁!7[?d\}ZpU5@W?]ME=h⯨R}ّZ|z>h~A4m[EYoصC!0ݧH(`sݯb6T#vŝ>,۬?)1cko zhow~4ջޯظe@xhHXniWJ[7"|\1F>iV|/_U<7MAϯpvp餗V??K`Uvm.dCm.t7{:Y1IFf'q@iAЯ/P\+/} BA !3&aoNWpQ/Gїaؔ\8睃o@º|**/QQQQѓ@q"7e/o橕u`ZV&Zca*jur)F(}o7Kܺ_U[̯JrJs:.@RM*`l<> &wiHQ;$eYJ|XWA KăTց؜ rIZum#kNTX t  G-o@f C{P4ɣr y'E@Ejخ4+2A~Ή;ǴF7& YLi g;M כlZ/| [{ w|2Xe OggS@`3B!   BMJN32! ?<^e%$dό̧3XhZ7AhePH%?ANsw L`HcӶ!}HrT%A#?NJ"W=Ey"ˏ j@d0xfJn0QZym$&t<K&ߺ`-XП h*w~ oO!👥Oh p^A Q^iON:!@U`Fb?5v,xx).>r(R >rFaH}(ݏf$!Wu!T,"DCdam|;t1vӔtJː?;TĽ=P!_s7n[?c'0߼{n?n@41jMUhz EWZP]7&rOx6~w_P97c8YsT (-cb>I^٫nۯױs/;ef`c{P=I^DD9p|v=:!ُO*tx]|@oy 0wn?27mC_6# k:a1:10`т0+w+%5.#U {-  x0@c$.hjȴ%sK'EEi!D$m($D{W-|w>8xxx*רȐ@;tEp3_O,d`O v'7Ljnxkzۍy1o90g','^!|@ sԃv[stP00;=e8N p,4Ӯ[Lhn_. @" LUe @Whxg|>X|"x)@Q)}FQ [j6B=(j fp^K9w\)b½BD@ӯؽ : WsȭZɯe+8?`0;6{̓0OGpw(0ǵr?*{|Kuq L:hy.X:|v N\Fg8OKV P2uҵ[Z#X|{ߗj$nPt]>&b=o dFҎ[.*d6/ÉwvF@ff;d @ld0ӣgi0j6Q| .kk>طUo]6yLchupGw]\r Ax Wo@sc!෌!@ n&O( JW<= hYjK"U1@pxQE4"GDqل9lNޡ( 9 ӷ p]מ/Wh@ ^3=[X%1̫^=P=EpPc9ce"~(j.Pr{Z{oPa0{6@B ̠@QZ_ + l;vENDaxK LJ]7>/dΫ|0LJǿ}ހ<d(7Ҡ7wpϙWf254̼M@E [3د?B^&( ^ .Ls<bpꤴg^@*T Pn.* ׁrH~r^ qo~)]M@z PNO@aJ6g2H_߱m!= 6s]4 " e [%5JD_i?Pi΍ 3o'P-qo Q 1H>¿]TWY K.o.7Z#}) P@jXp_Qop!" JȠ7pG ?>n2/}@. [.! )-K(@:Lh!- l[DiFsNsZ0}m ȍ*3\}kOpu}KV ;;@VTH 6xU*;! ^-壧U?g`\+"lj^JNsU! E.)sJ;VڵU!P-2e AUTUTUMHJ;̣Mjmw@5-u@g_ץ&P{g4s G2 ZB2K7^ /z'sn{R(@tz  x}  $0}.drTVA Pa$b9&3:k\^RR6ŪrFGXrS~xxI ce=஀u?2 7.ߵ` /8A90I eDDJpߌ d=⦛> g @g4>P(W9&wT` 5{{jnPT((v*(qXFn' ta*'sݾr{iL`np;%@d<\p CKa[%o>:ܥmJk,/gL}s[{"J$^Xkx_ v<#wJ Zw @Sl@bhE3U;d?ضǩqGJG%d\<nm,2MVrpOS {"~(q ʳ XG(EKJ9I~ۖ)6 OggS@`3C`  Gc@y:6,\.VѳdGIf'拘l"#e>dn՘o: |R@ @-YŨ4V 0 [ Ϝ -6.PN{1kfqt@Ρ vj(SIֻN'@En^ n^jζ](`4#Ҟp ^Pxw NBxtŧF0:g%5WyqI1ASojCSb] / w \zW5D8W!< M z~v"V!nl@|3Ⱦf%=ȅRwYJBǀ|$Vt-9=#|U&qTe.׫O?c2+hY| އ0R*NGV$p]u Q|ha6n5,DQT@DEbGa`X-/"s6nK#8ۭg6`>^>3 N5 ,РKVql2ig; | u- [@s ~vVS,Ub'eF=˸[ZjCN-|jhLQ"3*:`oϘ,i3O0~,[Y^! `SKN_U$XٛLPqs+ +LB |rWɐdH " aϠylP<P~0I(E@dG=QxJόG2qsf:Vv  hyeZݡ }GnB١ l~ ^VZU},R0_QT&_M" ;W~>4̗lA+4 7|p*6MVC>Lœ(9[[e}&R$!puj,@sa7L;B٬CG{)<**8Qd|SICSdjt(\;zy1qL zں11d 9 1% 3H. +{;@ r0 9xǜ&95|`CU a)2$}F8}"R1X Ni%$9O-C0}W'P{w.E3՗N" *7|j7:K e8OnT'潉vNQy|>on6dy P *~#RBIV+&яf==s?Ow U^`}W[A`%*5]ĵࣅU>+( d/C_&>p;;= 8|_[ѯOEUi Mx7cr?EeXR!tB"X:C&^w(my5 Z{0G_^c(@B ^ʕ^ gew++P~5!" <sp^`7.휻 h@D uByX9>Q2|gj$i#еf bj"q$|l6t+^`bi8@Wp k MܐPYmjjebP|WۙJ$-#[ڮ`r '(`.#N"dT-*ƑZGoiwW Z9Tl$Q.|4O.KXx@ cR6Dg ]O Ⱦt 5@cPX *kA?lK֫;v]o OS4dc7l,пL~vϯ;5_~  'G_F~ 9A,J-8md+Yk(ր { v5 {0'SU4cr?6zAST$@;XV:>[K;=oiYTtd(]A*}4' oN/Ȣ諑&SX*e>{Q:l­?'7 ƙFm"-e}CB'2 mnЙtyL #DHOggS@T`3D, xASU4wymwۙ(I2'D2 \u .^s0?= _A@Snf6V>Rv|-Ś%P @~ "Dlٿ @_En/6}` as`=4@/foFiO;asZ9X3P7-aVg9D uZ6?%,_[dnLW /f1ћw ߠ m@<mLE.՝%mgop{Dϰ]P̐!ջ"cs_}.n`5iuOp6 {kг 0ƒ RRHm`W$ l>oFj 68Je72CS*`u[ ӹ P1iyN`g Y?-cǁ Irc^o9ڻˆjjD!p] ɝac(zjgdg/(w.:oߟM9חs ,i]>tT! ~NNoZ. rS ہ +ǿJZ#zmTOӇ+bu[🠆x;W)x:-DcΚ x9>Rr`gSQM4Nf}{?ܺ 䁇y6){(dPiIf@|@hvO{)zk#p!pYl&Nn^ 7ITڻt6 8`~WD4\o1{x=di q'5HVmUcuz.ZB\P/C1ά8djy;H:'Y_l}" 6-/ 0?]K 6j%(@5)aK._- (Pi{u}ؓЖ/pyle)Mm?~Bi~x\ 6;4 ^Ȇ[ @Ge#RX`,x@VDP*{ .]0COl8RZ{;0m#4'`~k0&]n`8T8z#Z.) Kiq׬x|N(@3Kz!Б}zxOK | dr VSGN_qfقoW?04w74љ,`8>OM7s'*PyFvjXz=z}5%̮4(޺t2`<])+&+ᠵS3NTm9=}u74pCEm5lYR{[D|b)wLO ijUT ),B 4~7A|?A/?} |jnTas>7\4xV|T?vEy5W2@M!@wi p N wצE;:>ͩ6{ 4 @4zGQT>s6nn h%nTYBȩRZYT($U%R1z ,$,A &mO 41=nyJ7TܯOXiws_As}&?4#w5:%%J`9Nx%=CnGP_PKЅsnhG 2Lr|FF/\b0~s>.ݶSްiiut+ļNz}s ~KP"8_ nbdK旲l"C|eyNHNs ޚ-?V-i+eǶ IŠ%sps^?`^:fWq0T:h>ߛV-uv?gJ]ʴ!M~+ |I揼- zK2@L0}=`} ?ݠaz. ww9a;߃Uz?П 6)JeGusf Ri^RxjodK۪zjtuWA߿ ɹp0%&r!`j'oN]\J.Aw|۷<eb)@pr`iOֿsZ*Y~~q_ (:ZU.;[`:lfA)Iqήe25a] %X2 "]kez- 'V[;Oo?yI߾̷x p0ǔ+~i,qӰ?7YK]|Jϫܚw㽧a}y LPjd\֦={ p^VT 6ç'`\|u<@C; `c>pS.|lcwO co=^qK=``OggS@`3E73/CKJ:  ZO ̷_ J?1}; J`" @ FeC";Lxh;.~yŸ!(P \%dMT\NOA{PYxaMyDkxK=Iۙ8@8_{'uVvDJ SOWAOV\`@z50@Z߈TC t${o?by @"SE@ R >iJ@%Wc=糠>dZUUNaT&ŋ0;Ugmd82ǜm>*itOӀyOĺ/( er)gS71}DFЧ)}y>?!Ҫ|in iN6 q,IE$(q|3szR~̾'qYZzp0qȬBЗ?ǵVzklpo tKI]"QL@D^rwXطhc/c}>ٷw=z hˤFN sBf<ۄۄ~oyq0. } \?16PU$HtdgO/ 'wd !#d6<׍zT<~jY f=S۞o5w4?4,L-khs2:ԋz:J25ЏykUDۭE\׿p}Es.RvXMoZԅ u>\o=ъ798=VlDyk^(C{TU9xL۝Mb]Uu;]-)wW( gXCЛyZ?M_4b~V~[OJQ<[awqPذ}w1;* a<8쪹/E@AOwe895k/<zh*.TKZ<^9b>֫a㗨ޓX{K--:!h< H }[栙 i_A @h|"04!L5ݟji7盞\~_ϦQȾJϽ9U=s'Yf2j("غQPTj+-$BiUey(L'N @'XWQ[/^g@ `2 k^e@i-p/ICE3\FHV:64^3X@k3pfWU5+@+((@HH.e/㶦7MpFkvS g o{,~e2%^z+ 6n\;=?=U@5*hϟ'QnesaihQgf֣ޛB{&͆?8ap xlq1`tŁAYJ@-bM>h]WϬS3塥lb j@ubY/!CV khZjna*8 ,C_=ug_ޯq?//"J)Ў9d2-yd4:8:(ry[}ӟj5f= M%9ljK=~+l8puvz.6t@_ >lH]B_jbƮT,bY/ ԗ>7 G)lNLE$Bn/VQRk%K Z˹-ZJW2vC^'TU1 9|.8!kQE&2W12RXG6=Nkudeaڵ=}Uч_pv{;5 l﹬$.;0: $6l`m;l lޚ]$6=ڝN0}xC?:sxҾpW0X Up,"Y8|5e&p` >zH36mmS<͓KG(,B]d y]5e* g 8,憾uvG[u/q.3{}}(15`\7.y?>^(6\ju9kpu_W"TP_g@lMlJeF.zvD·9v+3EZ~….L' ![j2Te IӺTK*!Y AdA@FPA}QDACGuK".d4:I7F錧AcE ^FqX%z-ĕ$`&XX h ~pb*B84$ƴ34oyOwo㣴0].S % fI)a\9?FTK@ч@U"qS3E}ؤjk`K8ׂ?69@/4@8Ci]kx27,lvmP) MF^V~3$%BE7{ߢ_07( ⁷V`J!S=gzmJ^l.gyt%0*^Eۣq#CYe ]7qge`lşESXQE2hDUX*%Ӻ?vpkcO3<%?<dlÜx2π7 M@@\>`> m2ـvԗNS=b}QO?Wăr^Zq+q*WU8߱jz\+|8˦nz%Cv n@nqz3mP\E`q$@UQ=YP@&ovʑVp,j4x.n ց5H~`C8@^?n8/N;PpV,Z,C2`8՘945.V Orn'Dٱ*`"@~{ 0q@<0uY*P4EyBOOUoU}[ߎ DYC %@N.=\J XPO,ҢBo?A cse~lX& @//@O @Vn9nW'?4H߿SP`c ``kO8 RgX-sA}BjUG욈mҵ< mIk,~Lk.k]1 :w<(i[ />o19;oټ|7@~w|໥]kjk6f\s6n 1yOggS@`3FN! _ohX_>UH>E@yP;/ tWO9/iN'@V@8oA9o<> gkjS$TP~/ MPwn{0;z-= a5{bNmErBCn2'@VJ4%p 0?|`'L0/0vmm |x7 hXxoPYK!CC7 ` ?z|= TlBYmI}O5HXy1z"z-/SK<&IMOB`M0~ЈK:@~2ltŠ̆&}. jY2`|l<];; ^澡OyDDBf@UJ5WF5Yȗ,v=Tp;76;?a9{xzai` O"J2Nv-#g``.|?!H_,C\>%M$U͈v [@^jyESzQBB ##]|!W#8%0Wh˄5![۾'&'΍Hx97:sIpJ's2d.we׹7J_aަ+ s:`fI ]s;o>(L=_ `Lun>7:"݀ m*J%U~'@w  0(* {-cXfWMRuIKR uuγf݆%AIɯɕkVslC(w~ { HEC"gؿUry< q~^$!GӧK#-WTRx& B]|q|#ښ7W8j#m!%:q^ڰlG"x\4a{_q/L+"b20OIMGI1_dB##>ރL >]d W2S+> $pΣeR܆Û/9ǝ7_C惗d6Fa44? p{`Ogd@m`;ʜ;3AzG|+(]Op0,7 g$zKSYāҪ9S&_O@\gՋ |gxp׹Θ]xKhW` o):>ol&s<'xA_q!{@/ޟ\򪜊 ָ& `ar4 xd˝9_L`S۫?r_x%!/kOy ?0>.{Ϲ:U ͗W)ߤdAViE,GHh+ƋFo&VrbiI$!=-vB{Gc5 njX:u԰`\BR1N8f|juP4+ G0!Ա} Pe@YVT@GT 6Jɋfsklgަ l_/@O7 ֽߛ; m:/DwND  P3f~Zlq<`?p@`D3.Xp^഍e-8b+ "@V9溏#MV8kiu{,x~Ö\Jfsۇ iId9*:!'v@D.t۝17sOÛ4u:33\~ ΃ ( )XJH! @&_:-`'#r-Z'lm% |bG ǧ#pMk0W`y 9 iN OL^j{d;c<۝(]POd~N<@?mTDu @71!?/3ȏP,1 \6`~g?}<`tmPW0o@﶐ Ӌe i. >ފ+bO G5jw R1ACHF#սz`&@o? ޗMov@1E%DHpc&qbFb#Rsd(`%z^@AOfKrgCdvǞ2"l*6@Z+nȵ|o=_oށ4R@}y;e>8ul/0nlWa@-@$#H^u'z'$K*@?Q}RNF$ڶe~ޯ?dzANd"GKhzWY˛ځ5wă?Z%'\@x5a^gdnOJ x(;s˿z+PLL 7ٗP? 8gqxy6>4Z9go(Mx>|VziWbO&ꛥIwH80}D>Y$(O+|_tro7 }s95 6!"A8US/V %C)qT3 O8`/th( g%vXg mr(M\`9 |a7>cLoNi@ЀyiKm"KOJiBLPĉ3Z~H ZOggS`3Gf  8JJA+$#O$Q ?^J _H_Il=Hȇ pų׿ q+)bsаϹ/ B2Nk3KMԞ`@ ? 31: ǟ+jJf-tMtNYL_7Sg S:s3@)P>- sil;% @~`)@P|27LK6N< ol7 7 H/ wL{ZjTI~E[? BAǺYFHp\5#L,pfܨa*!$06λ2)a@۹o2[߅ $-!P^C}eR3CmAe@DFTPUQ@uzzLY8')rK^r.@oZ`M ۷e42 ONꛥ.r 񟄂 [$Nb[wۘ{pL (8k]1(}6!PbyܟCԜ~t6 o)!{nyB=rQK(jx&aSނh0iWh6Nۡi`'u80rIV'7?2@WTφd5''VߠfRMW!r;^X&Zw@E1^w ں΂cxP`o CZ6o<KM @a{=19_'/K=P/*zXqly AοYt231.z`_ހϻ=P(O @~{_2tMP/|y_+ yQZ@cS1s(Whr. A{x%ybfH9ooFbZР!8ǦJ!@M]3.Ѽsb;n){hi<%;i#S]`ffR4{viK!u6pmt&e"19t~ wv($ΝBƓD*DjU$$cõ$O~R7 a:Z7DۍMփl,7>?ѶVA *`2rL;l7g@9xjX{J؈wպ:|pp'@]/Gh毆JI<ݛwbm>xj孽z }?@9 7_#[8x>_xstZXq,i J`)6^?%`},`qAK 7(a LQK sN4s^QlOs ن@WF =!u 䒗+>eqx1PYz3}0reUTTTT^DӸCe]ԩ{"^sܾisi<ӏIP L׿yXnKw{"˭e iM d/ȯ"t[Y3yB՚V*>@ N ^m+&jKB+xe=5G D&)==pNRbb_ f"J:!!]ӽ|LYu`/8|x?)TCB#a9m^>]^, \2t`vgT/CWnO JEz7P X)^*/`8SRM |4~sY3]WM%F>6EJm,=(w. sO:p;A^J:AwO Xv$hB Q!*ېQz\>}xZoe@h8@-E !Ư;Uй&QPd7UH ppM G5S@{( 9h| -/qO orC~%@j5P4ȟ)2('X4^m%#Q8Ev%\{zw`>wa57< .A0 e%?x9!KDzh;CYl%/rBc?MKkYwd@6_ OYpt]G.:f2ӪY5we8gρ˗=d5%6vtp ߶#)5] (/߼H !l vUiRJ0#_3t-/ {f$k?}/\ ց36h%|`L!z xm,DB|)d4 l'G~Ӓ!$B es(Q*^[@oq `si yV/8ǗR'6Os+wӁ9M*mw:{6/ @le~u5V ik)6=Ƌp yo!CS ƫ ϥǷ=po,A88NR8gk''ȳ!1>_4Ӟ=\3W"E{K@uy|  dQ~ȴݥTAxk|)@^o&&a޻aZl`?̇C$2>y+ow,n0t\:xymL|5tG`|b@^u-.+O[KI$t]h?#z0ŸEqS E F5`:/mk7МB q^> >S:Pr!pw2v?YyWLPFrʫECT,oQ@EpǜX芅ǎH/m-5><F4tOBɗ55T_dR#ޒZqe,bf TcjGq8ɠ 4%T1XCL74Ȫ5-0 mdK|e,LQONkdY]( K=!VGoo-Qu3@̋[iO@E$@=Z7E?N)sw4 +W5/;rJn?OggS;`3H{Q=$  ' 769:677<@ABqIPq~ɕ#)1\|F`-1e" A4)LM&z4}f 7|4ˁ.%`q?6xh!h]d_S?F!{g`]2!x\ !\YhŲ3|t_D*/^t2a3uX~wc=@/@P}{S(-oW%2@ܿ5@lpS@!~K p>-5zڽB3:%K+B Ҷ]ws'0]N!h\#(EΣajڤ}J@Se ezƐppVzǣ3lO0uS WF![0)й<ހV_n|N{G~%\Cnl忟lV)6`14X+7uYP ި-:=mG-P?Ô_pDӮ +Nˮ|6< 0-pvs7c ^tHټ2c`uլx-73&2O`<r(7ט?Prc}:LJ]@ /ܓS_Mgo_v }6yodU!65_f\E*~``^]q08E%b3,_A/EDb~dPQb!U>gJ/>'tVդA+,,M ewΏ̥РOnm8> $l3s(A;(4-SoB3ºO/PLf7U}v%cKJT~UlH0<Ь!l`@ ڸP O'ހ&vƹ+:84P 4eާZ?m!GB.nK\2Q}5:﷥%ujbPj76Gz?47Ss#ӿT0PaXg w8BZ):>OϏy1>ky -%|A||BN.7G@ҭX߿,-/@W.Z5>~NC%HC >hL iTR[ qb9dؼ;F)v=zNC@3LŐC%O덹ː)yK6+4zgӢZQ|gʠ;g@LL~.,Y:e^T`>A;9C3@'Cg @./+}][ApSN@͂~ev.BwzuF8_ rrFS e@tNtN{߀sS'~b&s}#Y}G+ԣDR÷'La{ߧGM0YԮe pr'}Q̤k@j (M4j s7g w͏-wXR1w7h-5djG@W `4?Y`zP6t t *+Ql>uN;AB>t:y,iz'V}I8_{+Aji}A  tی_KO9 c *wx UGcx骍5?@_v@U ryw,=64wϯ\:g _NLm:wF\lAh։TV+ ( cq@>ye-t5qK9NI5TURԬ>4t@ /5'vZYCmi<% A}oYzX!48wg= _CJe l?)_yk~v$4 ^+ݢ]4z)30~ҟpn,M@v7F yNCJ?@%|G7 *w_[~%l m@;<1 k!f>[/ /s0<3m& oWF60}N(t gsd7[D)pf`Aw1qA O K,UJ| H/WQ@ih'`L{<7uoev-#8;gxiosqֲQXqT<9e<ދ,D8 oN[<[6EݛY+y8A,S0矾&l毈~a,Uo5ݻ(wNn7=" kާwr}3GgA<هMF[엟Qhӣ~ arze\;;U| gRyO[SLVO;˥wRүnͥl>N~DvONLo6w8b+[D5Ǜ\g;~?*O})"y {-%>4ͯ%k_ ,$`LBw=lq2leawtAB dϤD\BrꋙE=PZ 5k. WsVN}赣9; Ɇ5 8 #p߄q\rP2dȿfH~U%[i},fOggSu`3ImBD sWZ `YWYm-|v_*`VIR|sSdOU )-^mEr昰Y執>Xa jx/@f ^bX) X9 gv0"X=r\9mi2v[!gԷn93K,OKi(w/祈F!RaI*侓vSiciп-и_D:Ie[8#߹)f琢0$$TV[ >9f=cU6i.Ɉרj)1#@@@L(:SW7v^ ZxEwkR@@~_`LS8BhKll?,UwpdOEcf@(lXl`L6w0/.ˣ5 M=he(@2 `6|G cIi TS)_ECE(>9#MˋKn'I4+( &'DB}x/^ѿ $X x2%9ئևcLPsl(`}$x ><]/HB!nS|΂Ci(RnlQ)a@''T/z;mQ(9ņ`!OT7wB0+WQqO2J(f m8V?"xR~kw@D_8:z{7p?jp` ozNN9UBZ[cN~M@4 C87;[RK gh@eﹽ]44`[ǞdgQ4ȔOk`,$ѯa?nT`9w7< QP3>_}QP? r,4>wJ%w1g"dWQvBF| P@h K,?,@U;T4 T`/d ðT: ?{ ̆kpdl{ Zp[(l @GG^u^SAN?EN/ΘJEE p$Ӎ_<} j@/+K(jR8 l~VeR|]ԕI1iZ*w8"A.O?~?f>T!q_0> @(؋`bsjFL>D_' y KljD(W&~4ݟa3Ā~üd독 @/aC CNr}S| f_`MJAV?A`0n"3:iTF, FLߍFh)K8uC,x8@ lHy:3uZ:lEK3ED<@R@W2i lL-}dt *nCfs߅%}Y;!9vo>{wP|YiR잯ou`_n| O+(ՌX%?zh~vF ~sXS0D|+*3}~A3*0P;tp(xp@ &WXC4!!r(/էt|w+x ,Y 0}wb[zWE~~z@ؑr]Xz5f~T)6M? 9yj _Tu?4@ ? NIOJU%e$-2 AF ^U"Q¬=~AYK7ԊTb\m;?.Cu5X? 5<}d*z^bߴl3`{XRޝS Z{v=6=.aZXATWoni_!M6$[[JM`?ﰏcq[oCVfphK^eeq$4ͺOU~Έ_;Ǽx/YЎ]d<s7xs 3d^@eS~bR|sv@S+9}([:;5y&X pm $T )w$%tK8i02Y6 R:rþұ% e> ~E@q]?> ;PoNwfgͷ UE@&L!JwոmtXdR p ] lݬz>Q: x^H@w=\{XhG~9GBZY<U`#ev Y}jJ#7<8h9WE@ȭK@`{.6 ,@;F?RC%l2 mIVJC CEBHrh?4x%+Zr- !wЧ7~>d6J Z/S5XwsS|6 Yq$BɮzE>1AѐLAwT(.՘?.] N4";}$<@<J" > yk2+q`W7I`%e~y 8ٺf'@-pN,M/zipյCK#'Ο:46LdЁpmW-\6@k>ng]WkL1 eGcSy֟_(N4 ßhi?jEO_}ݞZp׀;p9>œ"84*]j;gK닑#}}9qgzNwxޓ2m7dOG(^T c )iY^7?0@;ݫn(_ T/˂a5pWX#&۾JU%OggS@`3J4?!#%6232513BHJF_?E=?6&)%H7ŐYR3X`%}ɸtq˿x<eagWʫz߸Q/QA 4TV??dVE\WT g|]HwGo~Q AXv|9'syzأ3gv_Nx(~oXR-=ynW5uWʛ$ۑ({O{m\\h{28.`2η#zΧ Q@Ҏ/}' _ %M'Bȷz?춃Q1~Y.CcZ7 eΩg#%3{ O}j]\Ӣ)˻W\/bynnŌ~|ck ջy7pm-l8IGvxu~[kBmUiΉp%U#]ݽNcz9u*tfn>fȶ~Pi":~R<[_M|øٔIZݳ &M%"'13um|C-|j"pLcz 7i:0@m`w}#:9口b訲ڊ9V;.VP蟠kkE?@84?80 =o>Y$?κ 8x/2 J=eT*W^4@W/$R|x'c @^ \qkXB&+umiGT @Q@Q/QSQQM(;خڰ[*~^ ?ap|/ip]Oekϧu'Ii2н!0`jJvVBi3_$[i 0v.Y$d|в|& z}`.d LDbMy~:#UUF˄dE:/@`w?~W<8~b4@㿶0q zVU~-& 5I;g8@AВnbkk 07j98@r.`_"dQx  }u!`=߬@I%L5w-ݣFȀ\k^`*9O4 @3~9 0gDPXg^A0G UVZ1 _͘/FdlU_#tzxNJ{k+5w%ȴ.Z.ß +&sU@5Iwq$!D8fSG 5 (?3@o4s;  ?,0;)^w-@bObo>+ bI&k<-;˲}䭦{tJǛQ\Y0/3/9Vp˜ B/NjJy4_" |UPU9?!{oÇ "@!(09);B$+{ɉ b@et"/y EY6_|rG%&Ws,m76r_ڟ$Q CG _D*y/ O~I&3b"]vBLy6*ۊ\+&tib|Dǭ R*Lw (8X.rTsz@ vs g|<|B. ت+7 W;`be `:HBG|"wD E:+ @{|l "{χp]y-CGJuBpOo_IfAw&; 09&2C:Ru#$cܭѷQ+0tpL .OCaNut_XϹ *BK{z'|ଟ\qAd]n^Za& @~<5ه7 J@%yuAtx-߀;0=\ @^Ln@Zm`o¯2ˇf޾h`R)Vp  ~95]$&ߩND-a} L7s fc-sJpI/GJgfgepڒ2. )C eg4\Sn`ii|VX (:@T?3hΰџ ܡ4M74G?W:@ 4οp@}Bŀ`EV{dΝ `+>)=MԪzkd.~}KGgWAAs^x^{/Ǜ*q*@%H> 6q?5uv͏Q| 5@zvv^i`o v\= %0ٓSvZZ dVy$]H\lUQA(trJ`Frٍ`~NP@ÿ~u$n}p=6]1L*^)&ۜ5I3kjHY麺IDG'vh<~is}hXB QOV{^@TQ ucdvNqY@`n^m T%z2pL1VW0-R~g o?/|,?0OggS@`3KK1f .$! _ube:ؚk`i(&rd&\u\W-&?|3 g߾AOߜ o )b O'qW3R@k|r.-dqOJ%GnThPAgDRЬ<_Lͬbe1xn`Sy]h ̗_@^uķi^2>3B瀸Ul57`08,Lp |=^=)R51V?rVRFj%dwk?5w@!,_nPn}#Ef",`0,iZ,E?^?y P/4@4l^?@'AO`ܜV;71 _~nVHAR@~61*82Üs?gIII%|2JspŁsG Pv``3>`^|Φwq!_@mdhwlr"e*( "JAe.㈢JDeQַ?3R)r9]}dpt5 L~6@J\{y% Dzk6`h>` đe%:@w &w^_6> p@&`7M\{s0X<:7>#j RxM/>89 IKV^gne G_!zdj;$OwCWNE\CD"4CpeR_!҆.vL=)5kPC9X]@=@kOk}z<ƒx02 ~ST~35?MoVД(TJ!\A:Vsp=(H6`hCAc@9R,ؓ4Nh"q GL Pfe&9=ZJ ?| EB4;L@|[,/eFr(6g/5 ¦ZE ,FyA</ 7}6@js1< ?F@Uި+ ]M/S_*0  Ӧ z;mP)o58CL"Eppm¿cYu ($4IR&~> ԰Y m;ǘ6j@UQ=u@UE#A(O9?ã/̟'3AQ]@z6 Cog ugvOXe_ mx-Xu`  ^gZJ/ >#QuDIH2Da5~@%\;˃w ~իb.[.;X]\vWt <m|/,a/ @9}@G@[QFPBb}<>'"g _nL ì?U|Hɴmp>`d䊟XU@6-Hh<*^eW'^rwj(,<n=1%@( &ȄNSQs1 D+Gۤ[{.W-ReזOp @GpkioʕpIF&^t>@v`:Vz8@fz}P L[$ ƂTU?@?K4`j/p+x<`ge5ˇrf ϱ$?դ*`kh Cl$b[oe(X{ SgA S?u2˿ȥy_2X:3^*Ğ3oȼeom63ul>|&\&^s_(@ Xӏ_tjO kP Q;c@eg?Z{,@.sje&2߀Pn8W׉Mɣhc|/yڄPT lsc+M)^hOVC`sW3ǽXJ=Pxl>#"$c UANG:#c7|א`p`=@';8=cmHeI>4ZP@O1 V- QDH@43LhDk[C/?1{663 ;sp.8 c.#3ެ<|unkg^ed^ O(,sfOj`Gbh* @'=Otp~N09v Oqh2<$dBhi2 $px ۃ?60wE:LI$!OggS$`3LV)#   11/2CHH6_{^=}( VXޠELW`wmx|'`@%X8Z6LuJ'K?8)փ`uEag%w ?%wYh̢]XCwI{Nioڱ|ryg&`rL?(g/ _V^tj]@;J   I3dom(N$YTͫie?I ڇ_w,hEFWfKcP~DbhwpC9+%GQ5%r28TpϢW>_ CeyxR|5[$XznVT;Egn8/ˣ 8M?u -Ap/Gk3$-9 /Bpz6Ae:v@)ܹEa:L_P_k{7t~گ^&L{{/^\|= ȭ @]!VQ_??Pr{U|O{<0% q":yeG5[t]~&Z9| Q.Y&& Ϲ"c%;K=9 8AtwujxwkD^35/K; \^[33]ʫT 7p5O;CA6K_P%Z}N9( `90~` 翪 TT*C@-d>H,㢽Y@3. :oXqlf98vxr(OiLU@ Au?OG?$e' I Qa{8hz(\`N-l- Jr0п9o4LڴEs ѷI< 4n qیG @$Ki`lC_ua(K(7&n1 OLhQd-kϨې@Rƙ"YΊW[.(8DK9( P㔧e $$+܅*@O~ϗ|;#q|x lWK 2xUyP:}߮wƈ2p+o:y~ĀkØO=Fп>4V ! |}o}c~ Z x?)0@:X _J_03y`~ewbnj6ɅT_!BV, ֻs@E8C.8$P\{ӲE;-?^±_{ٙےvNdYmK'JJ/u!X0D i>[QDJbZ4ؘa ;{+mLRYdGu@ 'f@Nq<miP TCA _)?=h A'CK@Jƀ3#ObT<= 4^D h[ @6 xqe$ky;j98}%@υF9e rƀY5Jf9j_C…Vj&2 ,P@o7y$gO~QQfP^l {=?3m3G%Dh?>1uE?It#d* S!RVag2%B A=Z:X /剓~bӊە`C")t$B"LLxkTVv$EVSt|ܬ$@go@㸫Sa' M-R߅_{xf[ ~t߁v'zHd7~|l ÈnJCޠl#Pg*&Wm4Uӽo 9^=dQjN=Ť tLмL[|3tcTk+G=9|/zv|qVD[r}ooޚΤ&PסOПLۭ?*>}`B ?|^/gin*7D'5m-aLh8~}?ws`| \L{eӽq$}5q/F>L[;ꋾ /I/[{mZS%d=2,0 {* rS-pѳ>s8Go\wo4%%Z@75}Uw5ȺdzӞl"7SdZ &Pc>ܯ>l842P^dhkq]|I}O>DGUƷ{dF˩bh@t *d`k;͝U!ͥAۏ,L/^@`_/mЁ{eԗn+^̯t!v\7Aﷀ @PFrB@ŖOuLۡ ?>x:R2!ўȻ ~bF |4~hs R.^},_ {TY(tHt@r [p5AX?2\ pg~ ve>~% ]\2{I-/(4X6pL/Ё"8 hX4(Jzy5.'I saOX23r$JahC @s(O%L4ge9R$\C'74EZй`^`< k׷@>UtHi~c'6Y>=Bc@Xhny)⋗/sh}en&O0@WW@ /6:AŔe{ ̙!oҹ jȀ|pu|I=B/ a {]@{'Ȉ_ZQ}7j8&co30>~0z '@\I2@_eU>E+^T+[}o|AKˈ(:(mpQāO_<ɴ<.N}QKN7"6Zw]VU[ P'wLYÃ5O -N{@-MS|@aPc %!:VnLT _a/?'ȰȽ?}/2p Wj_GEGyor q?7?O|p<*?Ց a)h` @=`qh<.0x~ۺ _=rʽra24mt*0 NA{<>&uqi`xͫ>~.)شyZ@l9*_?]R Ρۂ?w0sTm~ sQP(_U:¯~#^+\$p1Kf>#/ 1~BV 5Ѐx]]x;ho%tBY?H<)G2'<1k u ,{堞W}+oAԅa4ԢUP:tN-- msO&^ase? */kP7>#d_MȕG[|pٷH}3Iߔ0g'鏸D=@Q]`{[ |xa&O$w_x=iObCԘK)<$P/7 K}Ys&/! \6evP]R9ti2T4*@eԾ֡|b^t?@>u]7p54` ?+w-G!jZ" AW jWhF* x9y&; i_$ڦnx.+l$,kKȀĞ| /{n=qg: ~lG. kF1ІPP0@5qXI>#V; a3~XP3@SM@f:*9G{ɕgBSYjZ"i RUD_ {}@u}e `}ٙ#@t`on_* _ATlerT)JJ*ᓫf0J]n: @ψ Pv~״! Оf@«} CfzyMYh>VTPL@'89wp|8?2oh?;`P@ ~RɥW7ڢ "[!(S/Nf#WQ-&~g0@m9ߣq;!00M $ZU|dVwo)I(A^pZX#xhS@Q+iRaD+ >.sߌ;:Jo hM|ͻ+pY? X%+ @%.Xȅgg]eZ:_+^ fAp1_5`EpXcvu/`8 0`@X` -/{l.)ޏIJ]2|]z2,-eC(n}WimDYs<  վ"U  N@.ͭ Nx1?3j@h `@[ 4H K WX6fO'dEy\1⪳lp/Nvp&"`E}\%(r<Qx9Ϻ=`4O %EicYO+v _j K"|> B~@4ݞ`$&P{Jl[' gf4o3Ϳy9|`_0_}>߇ {`OggS`3N.n   566DGF')"Yf(Pd;RQ5$\ P)܇qEc6nq".lBzPVǻa3*O9\5q( @ǽi~ w_aM n1 *; d _GZ +@@@&*QiIB"B @ug&vV0&6UVbF /> `R +@ \- ( "Ky"B{3 U#6 @i, ~8<pB@zyOq߆$!|8,նF32E@ZeCX ʟ/?4} Pdm l_*7C , öLg3Á UTIhW%b W˖ 66@ 6ס8NW*[4&`38| `›fSr{āAZIʈLbX.J)Ў?j~MX_zeʊL3"q !tlm(݆1'U  :76zK XU}-02`@>ߡ zV "H/;J 3Na8m2qLJ/ g>ׯ+5p.YxdxJ&1|[&h=dC|H| S}slhM>_kx51'ʗV`>sv?xI|'mڙgJ 7@Hꁂ[uGP08hւp_A l?b ꩞V7 W d]|{)"X9pm;1@ 9m7qtœ7w@ۜ}-$<$/[Pbǘ NM5ӏ8 k/0z rz۫shM'f&@) >||& $@'0RD@hStbp HQ?T8SN(/zpY5Epqu@3`69HCtWo[|Py1>:s@c %Cx/d ̯ ݈P/r7 ML ߿(E| k\ڮ?UwK>_P]g͟+]a7*ȵ[%V6%tL$/o :' *XZbO 0@"^1!TWKӱc!ϯF/i˫|(CdYє9&oO^ކ!miXkw|-^tf`_T<w`L0ԇF"Y6)@WFk?㜷{B?(J qb@@FhJ%HQ^WL(TS#ۛ*R]o!lm%Ӛrڟt;PsQsH$@ٛM!}iv+= iփtq]!OEĜ#$ݛ[|8W &H^^?~<նJm4{կȡz&wC{Bg*l;=g|v+Ş>޴aŞL (!h x "-yhd(  "3B.VIbTA|$ED_[V{b:u`/ N`h (|CoK?0:Q e.R) md$*|cP[&- P7@lv ~ʚE+74510J7И+ϙgG b9s?ׁ֥Dm&[.k@Nr.oC!=|M<; S=olNFATQpǀ3.U,)"/^ܰd0G3Zȩ׼.ƯO>/ %G.YIE_Xo x  Ac$( @gGs%{J @n׽ @n$`5nv$iE }ڱs V~a!P:>d܁!ɿ h[}+Ea_J`}~XOmп _F GmP`Ca_@}R.j%C.+E W>xO 9 t/#52n7 "`n$H!FuǞ%1?@yIK!.pP =g#wK m@`]t-@;),i0 '7NDP6VWU]!Cf} ~~Ttt8I|:Ls~qs ӑxg/-PM^`޸=bI`K| w?p6T v,X#J=0 @>=dt7ʏ%x__Y PYl(5w>sK`"P{D"/?%:j{o+<o;deU0}Sh ? xajG24n_~K[7 `=٭7IZ6 ޸e55r!#]̶}I5 A`Z&z97pW'q/}Pܘ>!"4VGFWTA;yk 0n3I HW@_:7%aKJItdwy~@<@O ק3f20߹8`Zl8@|޸95/j۷T.nqxSL @ӞC0D,4C2Ox/Cz@-G2ˆ%O:mI $sb9>AreиZ89׳FW6!&Birvx5gO\3Ш⯝ :{5Oit4ЗW%u͆=mJWۧ--\}R^*@pS 9{N  C$ 6G͂IhR!e$ ^?@wP^/v] ߓo3Oo+qm O62SS&m~/}L<ε? @Yg >\:cv ?3@lz:`+ݍP+`0x0C~Pk!|4@#5'r Ip׺&}vXp^1(Ps+pĄ !h=Bk@kx9Ƣ `51QMD^rVx)Xsޤ꺑pg[AF C)նF &:=7@=<~( V\`{~lL4m^4:3$[WAhX_2WK4mW~e3-nip甥,JBh`omcT iu>lD"@ӡW¬qЙĎ tz(rPvi\@[KW&2cQaն7ـ8cX`UWGv_ 2РO [Ǐ~=6% +d|)(lx ` 6=M[vܨ<٭"Ne6iw_-SS+-}*n>ǚkAԏW]\ݝE[eZ pIG(*WSAޏ;4v>y(TTTߝ".Gkݝ}Μ?#K MV/ Qh*Z_T݃MnțZm~ C9W?lnOazb,[Wc揄`TݣWx>S] ~VA\]&UguN|>+z0d»^ T{V&\5qaz S@r I) O u]}PM͵ۆlڸ5Ɍ[j5EHZᒼ=h21Bq?{䋯ׂ:<KkO@$P"D4f+}T4"=判+er {@Vb&Wӗ" \W tLgnw`Y7y. >qh4rize9i!c61r]C7 HwA]|MTrgcpcY%@CD*=J@-x-su.'ًPOЉ!%`mmX}rI "#f \ډ .(ѹAVSZ xMGslӠ`ЮךƉX'Q! @v\(b.\Z>KȇF2p̏^{ujirŵ:x0 $_?E yCn d+㟥BU33"@N].B ,> Fm*,Khl :/Ov@w+Ip! zEu ^EšKN9@%&~bd:52PrV\{|o &C"붥ȡ)tﹷ܍:zg" (:7 "j BWцVIiRpM{ϴ^~· OggS `3Pƽ! !:8DFC7!&Nl6_VQ+75hqes5nL{ ;j/@ :!^gQ\T.}퉏RWQ TNv&@ it&ބbput[/GG.,!_% GnA|Z>T^f=P*D B^tԙkf$93#2 Ç(SS;_B3;7ps[\_ņ/_^ ֵM-~ᷙ ћOA05Ozx> Y@m"}9u`u:e"k(f#'MZ( ZQ\s &EYg! ͋zE"8^@hﳞ=E zϏahߞ @~:@:K+P6`=ohؿy2}>w BW@~W@@g¶=QZ'[Xh]FV?햌ɩ'+>`Z%qg& 1X}xcBQ 5hֲ" &cAV(@?e +T cZuܶ^>l[ Diz) `;&w93_@wW ,HJ &K:h|)ijtUDf_^b?HEyAXρL S ` 8pUDe\"]k5;h󩱐=!`]+ޮK)96>@=IO OWK]{APQ1dGMS[֒K;4,lHoV96h0~m\㛩u2@_L_f8ʢDD |@ g6r dOod]"gaڬ6R/@2:˜S%2׬3vWX5V|(apkw(88N"j\uA+PP@LRuC{Ɛ8HB_l}~/ śS>ӕBBit%F 9gGD@YAw[z =n\5׭$W,@+>@Nrց[V_ +WP0 t['s@c9 YU2o `6x?g}qڳ $7ȿ^l[|foP)g^ɅG*ڦ,yd?S t ]1DL S0s`">hU>vJ\@* ȅzM7%u^D} uF8J&KVGXj >$>NNZ(&is9ƪ (@s@=T@t^2\}8C 8?syqocq=?x8pH~zi d@>AU_^l nmcw,a]|%f/$EA07h@WEq`m`>n 8zKƛwfm}엣ʫϤۆ.Z, d9|I^)ߛC{mW`&sdޓ+YlDD究Bzz}pgr{l bl{_ݴ0lHC PE0 ?FƯlL?vҾJMJdA/?<ЀykpnG'ЈHU!h~pi WYq c 3{86xNz^vgd0?>xmc&_kecyz7E;ɳh˥ J- .7a1pg%_[7#y'2`KS"e~Uy*#P`~ص?i{kTvWr*P{ <t[q9v!EoX/6 8ufP(8 +h;l7 RS]6h+KGMLUb3( \kK{o`9ӟ>*俞JHi4ά_#R_&/l@ @^U{ Bo07q8ql@%C$'_Eh0@l-qy/m<DLC=TY}~w;xc~r>ȱ@f.N #Pr_~.>ƶ}$`J! x" .ȁ آKzOI,@*OggS@E`3QMA#  534136DFB86'%&oIR|E U}e݉~Tuܰ;?fQFyrusi|; |$ă9 (y)<2boշ5$r7|Ie x G ]"`F'xr@綔 R&XtI+@~=Ah˘Qדj@$0nJ&;,xe~A`=_,~!Is|@X$P__F%? f u$=vmU;6S98xx_('unpjw2lW+P> NTFOiw+"FDPŐ_N'8w7 @/;p3ߖ }02`v@ak- L^x'[3@x> @2o#_0З} `pgd_kt_O $(zaOs~*M `῍*'\ԏ_nRemhVv|27\_u ?'DZ9NkNC!@Ԋۡ<^\> \p}v$p@?^2|9^ @~XBoJ,?hxPޗU@v!RY"Y.n2@9# #>o1ozs&@>M` 'BZrWywپːlώu /o2㿥К P* {LNo@ӉE@G^FqRk$O`f`=~?0MS M Sg @5H _X80%3_@n79?]mFP^|7͡9E8pRNQnP>ݚ4䛔_@vT(@O}vSK @)ؽ - [ *(@ȉ0GLJz;zq[@g7iLRC׷|s{Z2,)_~V@}|j.@ɟ`&6  $[oMKy0VDDVڇ TB|Rl),K0)Ӯ\h̵5S {P]'3#d]˺o[(% ,ݪ;VUpddkN(U*Yq߷ Ԡ/r/J̴W+ybg`ڔ^\9 PSrO;9C `m `?3Xe $Un>[o ΛJ0{Ec߻E __ 8UD{|3mg?2ЛS/6D[RG3D'~._#ɡܒOx>kg3 n{Ͽ'RSl),Ȱ}sWP }<2lnS3xT<Kv}m;L@nߓrmJ.+ $;w׫ ޵א@x1"lozC~94jd>(ۧUg%v:g7w\qU}^8ImmzN|m+UW<ݝnF[7sf6˛WL}Uzŗ VYT߭9 b^)go݅w4ls ġ5>-Yϯm9!ܑרzvj%0.}WkdmkTݿ]9)5X@ uph:/18_,AMC37nDL/e䣨\ubh/(KC m%2j&ppR U^y ϧ<B6}l|MEPsl^оoPzz|8kvI c >JP:x 6UT^.c,/JRI0 , W_.{*7x+7h5 zsGNÔF!gh$^W*Oh\}!;f?܀Xw U k0pJN`ЦĆɞ(zd`(0 0顺W,P2Wr)+m~ۦ}OfR2L $* (X7rj&e x dn珗{C~jr0wU Mc`@ ~s Tnh,?ٵ +K { `5tBg,kIM+)A溪4q|X~ z3'Ui r-yp'*5g]vYe 05h| ǎ_~W3_&B`tcPqٞ) \VU@فטּ r&ι r4_ ^Ea9|Y@|@;9z@|@3ꯟ@@o$5@gI0-ZKHM\+-dm-xA Ϳx~ `s 7.֝_Ux~*g7}KP ?'RmOh]@UE͛{ 툶9hP(o1Kk2' f 2k& w8O?d@tO{3 ?r.^>$I XbBwIBØģPZFj"^oAyrqgy8(oV(G&{I ah )ǟm"S4 S,>Plm6 _ QP@fF% 9jPٌD׀c,{S[KΖk7>9>V<w |g , ?[[ɅOm@F*on[MKQdx5 -FLd6cϢtW y pـWx1@{0(xPТnAgvk4aotQf3w]i9w@ W>y p5]P#H.tSTEQQǯ/Nz@?pΞZy~ З'7xo @f@ K@?)~]3-#iqHD R`iƟ?L] yWs s}8%4},AMȃOZroFXYݑRhҲ|sBhP<~ a/er}ukNOd` s+ }Q"$%v#|a@T Ls @4k%w[OggS@`3R} `'@/jBblG@R$5ZCOt " Oc$s'XHDxܪL}Ļ0 jB!d9y Rհ ƃr7 /箴@#c@lwUZh 3ԅ@$"!Q<Wf`ZPr ฽@qm:$^&6?&po20zB8 ? €6H[)I ~j;a@/"'+@Wed^ s\>}0 ' \:ǃ n5`.1i&tӼ7\ਠtr< ^Vǰ= گB._; [&m_%tEA8Vִi`s5qd?z߮2 n^܊h[Z!m+PJGۊemQ_)(?lnA` O/lc X@6?6=xr)pn^u3B[B "g) ;Zsp/pPG[v5`8J0 j !~:MF՜w t KL٦IQ9)4ow>|'@N@.hzMBL~x)u g$kaǝK*Q`iG@5ؐj4;8 6 ~m|@Xi~k+|H^5%ѺA;I@(eɚ,wȤ?׃0Ww ͍K\sn?45 PA 1ZW8^@NѾ. 1=Eˡ!]-,@hߧwڗ/y&૯\Tf/"3Э<${ 7;a[pp>:?snthGwG3`R~ 䏂J_>>z#B[wPɗ#WްK0|EΝ@ @+# "X`+h#(Rz9HWf~:,͂~zS(^IJ@Yw/4{̛I wt-YM0ޚ5_W@8'xfr Pzi3mD0XtW( Hu~z3H-gݧWrF{AJ7$ 9Pܸ`N =xCw` A~}Zsiط% srOK)\v3hA asm.0{p߆ dѿ)@ű@bM9~zי"Wkl(U/@c.iVkdSC7^^8o*` xR">U)E5^m'T釓~՛'ybdB6.~QўW|/8A-US4!Qh\V }lv{‚㧗L m)@[d6 {xND@aKj xffZ:܀ !w`1/R3[Gx:  m3`B2PwX}% ~uWL &|9i[C{ޜ?QD(G8nؼi,ᰩb'د-@no`8-xr{? yŦ꘬ȅě d?3UEg$lJC!u:w9ԁ;e(y0|7_ _9w%/4ʖim6ZQ8j#5?> 3@6' i~qn:7X`< l*PN`R?7FdJU +U$/Kj&dJ8LP0Q @oc`mLd\4~8zIƒ'ypo9 :_2on 4y@4Rh9c\  # H/<&~zu;b6puf5*@z2Nd틸ሿUAɡo)aNԄ>!|X٫Bj09 T o6 O{zh-M8/ ht3t=6/aM<|}Owuhh_@ c~pOA& L 0e_paP @߯*}C+釓3+, ]iDl.G{Ps9l QD&ŞsTBNe Aw~TU\<wB~x|+rw@^hS @cWY'97csKΦ_`Zy[UZX[lӳ(cOۇǜ1r8fj %@*PXeBOggS@`3SKrC~ZWhFҠmb%y(PSH1G^6n8*(9} L  ' & OkȦ/d |`a'{hi| `q3lO\R oH*M10﨟I&o> ~v׆7Ёr :fJ ZxM=DO lMc@UP8-WdKƓ'Tо+0B$�4/ιA ef%^`)@YD nLW)~kY 2fj@ SVbv#/@K _ K'-W3?Hl{kIhZow&@ Ёi42 sT:7a._yT} |5|mZ$` Z-'%4~M$ %SPDHsQ`B@{Qb7SD5B'NCJ&ShP@ED(<z3~(:烉N/'\}~OX|)srͿgf^;@(^T6ۜwFL:Sstfuw@Zm*8AC~#f6u^5 @.6EEv8}Q]ЀB9HvƮkQ3e߲׻>@絉Rfĩ(<`z նr)vzge|;';pK8wh;̻ӁO4uAkF@+,Y(9{$l621kުuֹӄ|z3ޖc#WÈ LteGit @7|+ǒo8X٨APEto|+cw/Қ:P2v?z*=wzc~SQY./g2ҿ|f`>r`0aƆpݼNxgmLx7۸loPU^KW峥Мoq em~ZG:l-ͨW}&j7O {7}x/j7&cc BȾQI>zwUS3x_~I{?MgiN @tf0Z~w9-{4RqKL󿷖s8 ;%s@<}P7p{|xܐ_h~A{gA=_@,2c_\R(@JU@M ,>e;ߤb5o$^1Hq/%߻;c [ ƆæmPt߰PVU42t!T=dnﳶttRڋ$@2TL@y<,$bMC.?A ` >s{\[6qʹ@~sjaoZ`%PŠw4&wڟ*@gn @K`1pw y+`oYg-G$XMD|A~̰+fű]}$ʼn^]1>X5='ٙuONcs}]iJ!d`g9,|v@z`>Zo (Kc Hk`WI vT]@@^Wj616d/HP$˔^ٸ{hc,ks 6_)) }{x7:e) hePhhv~ 奕aAy"YU/7@0m^b4@Ohu^{[ޠ)~́-g arꞩ^1>/e^YzmM$$iؐj5;/GǤɫ~^;'6 2a.2 >[rD<{UāG+)xz,alϼԓ7Je۝{YC"/x1,ưM/Ϡv NVB ?J-p޽ϱ#0?ƹͽ8pn?U/X>Ajob13G> @?FD1Rb0o(hqeY=Z`i  v9Py] YTBo'g |m 6 "d{+j sgzKC.W0as7 zk9n5aj? O<~rמTNh,<3IEH? 6 @01@_>eOxWpfv^)+ފ=W!36·\@h] h|[9 HVbLyfX%lt 6gܶ*( ;p KyyZ6gX#zo@~ 0~NLt oi C;pc . o(ۏt0f65^_~0s<>P(}(ϝ}q VDRWPU@*Пx D%0 i%،G]^'>=_ _M$mj5w7o?2 ]ͣ{`jm>W% @5xq:E`\Ev @ݽF(B{b iph-'CRpFV'{ /zO_&\|h5 PO ) 5Cp68dWlOggS@`3T_C";487FE  0Vi%O4gࠟ43Dš]κњpȜG@Fx&핆ܗA|ppPJ7݄.PJ[rr'^}KR~, sn_,+덱nf59ٿ9N~Kh;пڞ # r=6sk\[b@V ML>9d&|mH t*ZN^W *_WhS() PZTeEٍ5-?s߿A>ƿ_gq'{*Z%%T=ﭣ_\[KλD\px֗J?S~njMV=x$ջ~Ov#ai1x.74kc Q؊/ Ż ,̿Mv{ŧ;\Dޮ r`I؜! /T#7^eo3ZE+ڄ6}_F[.y~oKG^9е1Tݓmwo7*/{X&VV@XҔ>ʹ ˜׿cݾ~4 9x5M2Aն.A0vf#S91gxG U sV@1SS-؆@PT:5OޭTp[+޴{+e:t:@>LX@}{|z06`z}4Μ p@W~[?sgpg:oE$A4x ΢rL[FPUDAND9#Lƾ9"(~GP=c{3A)Eӎ :tج-И<?{YS:O 7SN@+/ߘ}@= 3 70IJ7 U-z$+,r(Lގ yN]3)R By|S)H'p+gZr4T:h iZe^ {6_79J zd0w@_|joO:A_ ^]*4cTf 4~>#n6 {? jn^W;@i\be aIП^m  ɄA4 q|;K T:Ch~a/qn%q}>+q\ᬷMϿgvitSg)4oՁ V{2# \"-~+F&Vuo?1RL`Xt^5L[}@"@@*w_3*x> (. D EkF񮣲6)7T@}wTAIm+2xk?c4j5~]MFY?v1P4d 7g6Tk @s R;) c?~ @ze? =Zkt*Pm@>eܭ} q/ DbޟTػS34I䬜~Ms--| 8a|ÚE}n{ |6?|zo 7$8icfhڭO { =Y@Mg n~lzy>My@^)@ڿ 6[e=m?:nRϴ+_1cF}X۹ƗĆ (˜ܸ5| hQ( @e]N@i$d1(kr?_#.L ~8~T "ȔWTawE:plt||vg|^0ɹϫ/ ( pthO'sHa$ݣ4hڮTOG#3NZvz>\t:p~"gȅ_ME0s?x>gn3Wh|wHH ا9))A wcb2|`͚ A<#uV;~/9븡ݎw=ٷ"d K ȦBu4qU]?# U@^.tF@$A8d8m T> L?1oh/7(ƫy= };f&=(mPcT@pO7p}MPt^ Z ^ e#m]dnAEeE>F|()kOܨp@m@s6mڂP-`|a]""7`5?%a6$~GigdA+ȄAO+-`XWs4``! @6)jo?;"kKOl=.ǶP@UtAQairi/σ=:cqD[|ū}~Ic3|~/Tߦ&+1ߜLЉ 8,` 4'M1@ މg42+ĝ/cٞ$-`oXڜ{.~d+BiWR$fc*\'Y a0mUDA@Uọ:|ۇ>F,#Zwh}oΣ}j/3 llEzN4$ Pwz-c2's]0 `@,(@~mWi`0OggS@B`3UF 5D@A@~gkfhT;7fA:Z!Hu]yWN2.u Ў957x2w4 @4_Fj,\7~Nǵ+Q'9FQ~B.۽" Tm 6J9ys^6q% tk ρg9O@27h1c_@,+w75 FLc buމ;,AWf YVbA+-]v<sp0x+/W2瘸zx%gsHK@Ά e@@*tm* mZV5ʃ) y+,3Ul2˔^~HXZƜ'1@)L 0Y͍zlirc|{r%]U#ms"r4,(I"M@0q }/qyϷ^L8S8e`dr枟_PγFSo-O PubЧ zE8v~ _mSKo AM@%~yg4"{q!@|PI^[==TilP`4A(pRw}䴯__i?%]rNJir]N:.G 07nwk3:xn%m?zۭ̺e?K@UW}G J0lSvvmCy;qh@?eg( T?T|1o]G<@ !@+XR.re*M^.7Kx}'Tި꼀* *NoZ頥;q5~bmkT:V&?Oi0$zmW :\;zW1ݘo?jYYB {Fs4xWb~@߈wb%޸~lk'H] L}Bm˹5BW&p*+!EF"?:*ۅry]+z>9 \y1A0L 71 T~U'NYZ8 `2,`ҿd`hc6\s`  ~yO|%)}ԝ`s' loyn^NxPEZp =Om faB"r5;=9ۇ̩b; c'` |=æ 8ci6po,?1 @ Te@<2 @y y3Ǟ02 `8:7@`[^yO$;iޠ%bAWX/A%w5IOl/mˍaxIRE<5=̧'(ڳVlP8)Q0F?my-M{im"~*2i ;p & M?^ sCn  U0_7 $PeCz@A@*l +`P~y0oG,SI A_d+hybg?jH(B`6w^V3=SMDX]_鑀LEuP?3#hjO oǝC$t ~\r/O 3ހ_^*|@?;㝹3/>@'|@ktv4Z:_n} dK`z% 8%_ - on}mnp[ ]J(@ lP~iOj% UXߩxsJӷ"2j Oz>^ W1EH&^*T@Wc_h# a\ (,0^<_XHw! 3Ca t6{q1qnـ?3a`z7O? ]/d_Y qǧ((i5wb&MZ+Hklkrh27PPT!Hi!ҕA: OrWwėl'^ټ* wa p%_?|p.kx` o޼_ O_~`ay#_;w8SeO`7s / {o_?XJ&iq:<>m(Y;( ԯ*1+K9UUd @/u:\@[[uBR#}\ee63ϝxݹK'qySߖ8,@Q'~<&]934 {}M9P|N&k 1nW3L<'gY^ $m6ߵ7IlG*h`LSv+٩JLDZ~p^3:c GL=rvu{vG]V-`5R5N,oϳtMkڵ4ٛ/loy՗GvWD[y #怠e// Gf~uN D1-߻uv\@79$A@xdjq|^F#BhQ3 bϒDy{O;&b%.L 8NߠN$E`&W9pQP@Ms^ט{0$ "KU9sJ1{+QJnw̋wP,ۮ ;E$Kٰ|~ycD7EETTJ*ecmqyV<ܚiNZ*{J`WހN9Y@R/$!4a_@t.%FdN-w - 9r9+&./{Ă47\#Xo`4~`V ̡PkTDfD;9Z =ƱȰ,@ >TfFPu%~52V9,;$*%n3B' lSC {pƛOv$67[Vÿ6 OggSx`3V5p" :IHH+$ 325588CFB3 >ٯHB572fWqCȓ( ֛"P@x&@ 7Lpp@0Kc]ٻ]0~ O /L T`L:yx0L[eu4-B \:Fk}l)h@&<=g#dr˄.FE`,[VEF )U2='ЎQ7N[2>m nlj X3NoUZH@AXV7@O:~֟T)iQ7 c%ć7Vj^/`R@Eh7ᔯA͞f>R3d[A\]~ G=OBr0|OWeX={n58< æ_/ s0Kƫ>,!j\@u<x?S `kF( 8%Wr&Q"^nۚ=8FVj)b.NX-'GnorAL[wb]M>tA1y0 s0LlTcv? #( f%Ֆm1a\RɵBv+ 018M|+W-kk ]:*N7L6/UƯA @& |LJ9^/+,_5 i:(㞀6Jc4oXtOrh 'Fh|g L{@fI0<}hq#G5@p/ m eP_Ļ^-6R⨑`yDl=?Hgn70PY~ ? X^oxvLVԄ@kzybPdAwό!t9 E>=6a] C+Y[ 5+*bΝ}0aO4@F˼m# o10Mnﭜ:Z&/nn 7?J2Tތ'k6itka{n*T1ӽ +L7PK\`\x 0AJф"PtQXŅ+Q+O<Z^B2+P{pM@NJn)@I[kMPe!@DM*z!__"%z*}G sAg2WysFo GPW$aȡ^GCݺG1ȼ=I?[j?4h`"4vkklSf`jXu҄S!@n4NSJe\/s2Ь * ;&ug~>]6K@oDD^- "hr'` NTXO9еt:4ZqE0~tmf:O$sA!fs6CF)oћ~4-_AeZoa.Q q8DQ*L՗lxg;Pp `'1x30L#U, *!:lZ_@@J m6b-Pr~:<@- gΚd aU@v)(*g^,관o|6O/8 m],s&${wzlU.^)=2_߄"glP_2T߭^|#vh&ưV ~pؼ[.mE%?JaLrUִ.\@4uk瓙sX:OHOR&\ߝ-֢ uQScϮ޵$Oc3Qz0(DyM}NWP\8Ty;.PuWxZ޲Dv̿7[2+^<4(o~}YQm(oB7XTߝ_Wn-0խ&|׷ɰfCW/P?(D;֝wlw$ٱ)hWxV@Ka~=׼FQ8bPρT;ew̗e^R-sڼSn`J>r`Í < LߛeUɼ3sC8vUov}ͦ.b2mC93H ~ %VZ)߾&; y/JmBx?XP|'m98|,QB@RU02_5 ~9沗Ĝ4-R,k_PU*_M&$*T6J7'h zkzi}7Xj [zRׯހu\ )qM &C_W4 0?W8t8W@Qyh xޘ@CHr(Xfݟ8,W+ *U@eop]-y6I%`q$3ֳ%b* hOggS`3WAw)>5DYZ)&۸OT"7|YpY_W?w`t*M8˷A5ViJ9 Pv@чxW-;?W8cRj:eV.\zs1~o0=h[N2*;, 9ο@75`- O")&Wj6-EiT9WbhҐĕ Codp5\޹Pb\^9@CNH@ SuudL'\V]w!w~"` @Q?k>2HBЫ/@ mHXːr&PO_(Dl<5ƶ q GOj7b՟d}|~)&2U MQj!"૨*D!ْ(0׻!\-^\s0kr '0}#x{:V D:Pz#c@F\@h' e 7n8mt*ӈ(螊( 0nĸ=?UGm ݿq[4>P4- yWP/e||;7 / >)rHq V1Fb)$`/a̡w^/ n :p7kx xS4>`} &U{ HTPW'`!M.,|.h嗷((v̡B푘}4IjY aon@m9"J@|l`h;r/d~廷K r|^)FW$u(Mg^ܔOvηf㭱cbG` uKPhw kz |pol 'pBȽݩx :7(*­0FG:9NB7@ é"@Mm/W,#;_ U<<|wK b=wP!ҟ460@_9RZi@ޟ{3jOM7`7UGGbF_)l7QDBs^Znj*0@y Jp'goavo v:aP xtȂ0PQ@h Z|g10~9BfP Q]P?aL{[ n/GJJ] T]dt/|E+P/P?`޾~soF$ 5/`-7׺U)ُЃըDRO<4$y8& 8 џU Jǿgk780Ӏs\~A"ub-=<o@en.tonD}=s N—X'(B)AnfŃI='H|dhs< 0!XWb[v}qO%? 7k~+Ə@YG|1 J2$@Zo1/y|u h*`'@ B|SA5ᦙ<,Yz 1pz< ? .?9U@@WklUT@TbWSfh8O_A< KW g?َB@靏 ?>zj~;ރBLiD_d? fUxA^(l3{'tC _cI |kJWvV-~ '/rrIb/l嶷 ,^P 9' ؄ `b`ȭ?a5Zg:Q[;(Pf>_GrR MKk&d~+=$V,W[`"ux @63ĽyYW@a<%=15(g tQ̇;N:wx p".i\%k ,dsQEEo7mVhڏ#_ ڭ׬̜ym0 a݃;8H=8BlHX,  P){b5eiB)S#3qДBM96\\s=+~S^Kx~k@(ia"MmIKntn|rr<>C&rմ cƸ(^1}E=&w.\狳&g`OsC2M6QtkOiFAQ0x_W>`PxBZ.D-PLz`2FDOf6D%-SLYlZGFRw%8 pU•g(/3cUc8Fu?A<<"0\Ym8 ŏ. 6^x}9@d$ @ < ?(bϭ;ՙzr+&? pt s*t`3} -$Yb % Pw}OX-B;/@?)` _K6>9WɆI"(Z)D W~ACИQ%pڷOo480_>7M$Q Ad׫QrD(Ԍ-NJ7 $Ksye&W?kVLw޸"bYTPJ(>ںxC敖z/x^@ofy1tޗj qm(/$R]bA.@ҽ6X@@9oP+4 }/hpp77T$7 E^p^ `U'N@8mrek8gL-s k`) -=e }Klwz%׫%Kc|aއ2x#kfX֡%60|?>9F*q| `r _OQF# pnw.2%f@v|!eWug5eiZ W(S2P^1| {,MFjN9/Y:D}elxo6ڗ_5( Prhg/'EzfwR+Olܟ~ -l.+J@@(R@C>9ƹ*3ޢ1A>;a7mșgGLl_opsWPvby 4'neL͵R2ٹo=v,W7r_ r4 /N)PMq^PT)6Vm\ |4pJ]`?h_`/j%]'mxo~@'8.~a50-Xgd/`K^`9F;x'%egMS;}&3 Wo 6@̡(Ox栤1̒LH /܀?Jp 'N8 !CU1O|#J=(+S@ T/o@ԟRƗwUE{$ qDhaid;?/:0<yỆe2aj`}8Sޜ"0e25i kJ`?oq ~> &m|A]sqo}_O4 ߀3l,,+X=5wTϏI}|.d@7| p۹(m ຃G2oRFH5- &`6uQf 3Av\2`y9Y_Q'@ ]<@[ol Sձ]M)o=J|kO3,B ͝Wd3 ȕN>Rn<̿zumy36W˿J $M.S6Ai .-q~nTPPx+*m A) x2.!nx-%@e|apa*a r0TnZЏ)@(A5 |q]9,j`,l/R/0KG a__whW34@s(dߕU߃fܜ$S1 3?'#QsG V9{'cŦ4WC{{p $7滆sW"2]zsB(|s@{5#P9Y@VDpc탡|l8ۗewC:h>-ο%` ^_ohL_x>W,SvSoZ9Kr"L~L9*t_Nxdo LHġu.pIC)?UPy )eg@ -26޽>?67ٰj |a` D;|ˠѣtw@ݬB߿2]MM4:zgq'u]}1`襊rH&Dߝc]/h;M=gSPv]eq R l<;e=Fo޾&~pJ^!~MvT{|N_C)Q]ƚgש_}mfl82hı\Ϧ]{r?eߝ\gDv{w뵷} Sg+1x>ӏ?3gGzsTKY80p-Lݭϣk qV KUkuvKm1gNOw+NagL٭ףҽ_;PY)β; n;'QFKS92vǹg$LycD_wcswPo_l~Zߥ@>0L 6Vluu֝;y:9NA ]cv]oxgzXukv~F~P ;?ypL9)k[tle g$(8!{(n &fLoLB0>af_wN 轹oCWga%䶻Jns-Ȁhӻ҆65kV {@nUDcEEQs2S>ZsGi_c;qZ/Pv>tk>>*~OCu?;DhZԁCWXNQJ0>,dx`-Pj$GTJ7@:fG\ցc4Vw%1uV*b_t{2Ï}Aa/O| V60VSJ6[R(yᣕĿ,m~02u|s`^D7BbW{`U5( j]0 */PGH ܃sh*qv8S?Dp uH8D?cd)^~ ǿpQ ef`1tI )C ٧J  Zb~VK7? yP0ه_~p_ NXpl )"LjHtys~eI{><29A)" *qP>^۳HQ=&n~e# v\]@5mV%DNPVN+@Yu`@8|OggS#`3YuiǸG4!/# s3C Y coy  @+ hmj79PY@zoz!Xqi&X;5ph>_% @ M 2.+$L`NǦt KĹO ]e2ޗd_ rS=tE1_?ءŻDn[ζVV`Ts 85 n.0pG \ Mʰ/C -7M}dd@~ %bN؍0x?ܨ\8$/#83b_|>M<ĺU9_o``;X+` c0 .ƬV~&sP!nX@[Y@ 9]B[*"Â|̢A's {X# _i*;WqN|gk`-EX\\O0WuBTYVhuB~zC&'B@סԖPA{{]=?46ĩǛ44_k&` o $a Nwi) ޠ!Wuv!t7T<@SڃO'_ eO\it  }*vQMw0eRh?LN h lƩ&%,f17@L77 &`+1&Љgf2>fGkT{dlJVwW grpd SCȖ!8ڹ*dX:&:@6ZbVqPVAvI|+rSaJnR,@{oՏ_H ?^+3"N0^ Rwm?|V1?75ocsntPǯ`y +`/h / hՀL`5u";)JH-/N:l~~΃@s @  *5:QկfS!AEuZvr<1ț<Qa tl( .5f\MU)rA? -bNԦT2ԁa3]!|j~ ds{0 oz`@dOVF~qɶ  2|7>2'3ׂ@[> '?kD9 fzoT;c{4 L߽irO\dAЍC/o6zL @f#jwU?XZ1 f!ZmV>q\I~O? X)qk`VCEb0 6}uTRkD6RCL P{]Vz sMSzy Q@P-늂I&A5#E[[2 %S_)P%X,@jw ۃ/X@?7V 1Lx PĝltmD_L@N{B:#N1d{K'PPI}$>g -/E^ +wv"? hg "i#hoˆvpE8 pUBCR08 Pڽ( / pƧ4B~=ћuFi1d h+D'3@{ <θm/;Om\Ypa[ M&ުLE"4&7c_)7^LβT-Vg%w(ap_{&O;?-9٘@-7,UFAWD, J m3Z=`ۙHKaɡ>Ph3@V980fJ8hq|1`tJgx6d F 7 =^MZ:WU wh?j3K}#\0_: Z&L\ ~ 0{T_΄$TKp"C 8`$CP| }@@Anx=L'`po@ٛзX9Hi&c0$8x<#}9xPW%j/e d^Y>@*/Vud$o2uň_X>)1ӊBRo~ؗ/xHQ Tn< x沗ݴ0 p0P@Ӗ tQe3}K`~6 T7 4e( @"@@ Qk>>:Y,*>u;Re n*(U?c#b@qD D. ?/o '77`0!@Aҽa'Gl7v .'=1D NoLлnXz%ﴭ>x(NTb, `;j]nYj62± fWv:=W, !Hy Й]сD+uX 0og` a1 ( ~+" K+%6bb*Ž|i{ +:AD ^#0A`X%Q9wfp]PPJFY= czUk*@1!PZf"7~^`[}aU9gk_-k6ʫ~B_S' d0/0l,?08l>`yz@6P>>Oڟ_i`L| eAiWzD)ry! ߹)_?3bA `; L\P2IE}4bquL\/n\pzX N7ۅìGtW6# [_8Eݣ8%p~7>Ll8bm!9xdz#^&l ״:ה)ʎzυ Rɟ2 K'p>U9j0& [> SU`5tCS 4 3@@0?a8w[OdD#1.Կa~-|Pp厃~[~VJ hN &| Nɱp߶ko  @-"B *(xdiC (ehۢ]S ^(F_uߌՆ` $[?` 3 .ONO_Y^H#il@_٣]@l'çD-Nڃmw@ v.3Ww1y? @ޙeWftQ!H c$ŕ+-/S/Џx5@ܡ<8bǿ`ua' 96voJqbv6`- ԣvp$3t])Cx u'8 ]F*Bq, .=mI|a$~x{x狷|w@9dƿ 4*ڰ_KȻX>l whͨ矖Yw2b68[7 uEppc 0 9{!hRsXp@* =ɳ;SS %̀K`ٗJFmc>iEi] xhYjSZz )pz(h-\.7ol6g0Mö,U|r(ߧz^\X_`1+d@މWDkvu>%"^I@dAYJ/G`wT4=%.Qii'A h2(8x :a@gkmPM_j#l^.t#X$t}sS050@/TM ϤSrdZN7*r[@So -nU`\ tQjhA$ y+7_L0.#e2b^ )Ήݸ oxKQ_GP[$Ds0&Շg{6-}~| |Sn=8ȟ'-4XWʉci Β!əMϿ;d Zy~ C}oj824@>|BӯJH>|2z;EyQ"X/V~`7U OkEv~44e|_иY4OΕY,!aZ;ƽy]q5gnR+OK= 3l&@OjЦ f!;Ojݦ*_+"T%(F, *i\qp]a o-akF@@=%mKwX0 0*p2BvJBnG6Y>xeyXUb)+nn{j}~ vHj>[;gJFsELdH]>ᇬ @; E*J{IJxHEI5WL#?Q/Ct3 ̜P~y|?hP>X~L o [ L䧣Mo5/М'/9ue $/Q-b(d7[jLG⸻z&>v\DuαU#p}{{Pz=2Ժ?R9y*p} 1 -F($I=!5( c!wzWLkN NTĄW@TKlSh_}_aHpFXP_3poCS]ͬcB1ۘ =ouV>/c1P |žgN x<"LByI[z?`;ƳPiQ>Q xq&Z\08_ $-*B Pې㒖%Khlzwd4v[#Q(c{.@d~@?~fP xQ7`tP뚆=9j/ڡ;c sH*π._iivr; (Q%-|8X-ޓ]cO'ݶ2gtM>9:3d/QU YЉ\6 67kHڜ~@`ViO7m,7vy{ atǓu}+?F< ^0a_`9l &D@D78+)Dso֋֟i*<qV@qTTG^[S> ,@kjP9}e޿qz;7ROK 1 7ۭ\`H-R@:ۀ@OggS`3[4q"9679JG; !!  !-fڼ,UMU>xf,0gcӏ> T[5> l;d^4ތgxA,`#u%20 8 b}-4ٻ̖x+;.[$X8>,MOIqu<ۥ^ްg^hSK%oΌT%7mOl< W.ϝ9P{~:|wf4M~Ӄ7/_t_L OQu? "w] 7}xâgr<Ϻz89M(V3SmL8[~uzK$?vVD ux4o4sn0@oWߍi L˷tIw𑏼İmEz@Nۼ+a |Қ?mL|= @΢#=%wGDQcveZd]sPG-v?vu Pp̩ ybѣdzLw[Re~3q ?@($.9VGЪ)s=K\-ziuu׹I%sx#Pt7` o7 |0SpNoqBO}>fB0Z.gogLPJ`}ӑw"5x%($-?>ZS[@(a!LFy><gL>{O0t."DUEE@T Eit>~=wYTo  ]l  f%&mi`6ԄnE+FJVf>yX@[4 [P|n ^7NzlGfQ[Xt x(! =Qƺ~ٞzjzv(׋dp3t ϕW/H @)SY@)|Osos@:VU_vbT/ȻiP ye ۟Byt!-3ښ;"eB"b`?"SZ j;|A  ׷h6EJ<o`? n:0((,[HSy.$N =&:_߇~%Pz I ^zGljC%>%7e`9pV nQm}"UT3K `\ dv-?>> UI,C-KCH3F!/8UǞ} h\o`(R A't:!%l4~4rV̞s1U| cҵ;KwzY(Es2L[|o6`g PJ^6> DUj.JMMrO(ghyy9`@zGeK¾8 VT_wEkO-%e"]eX}٠o Wb#8+:&48hݸa6ݼp1!ilw\{Ih׈iCk:Lc HJ0ІZ07{P4hnhm+YB{ M.0E'6 -]2_ t5zQtLv_. ` :?.Hu +6~X>N[w  3Y;0/dfC[5Ef ڴjJ|:^_&>~w ɠ ʫ_+AaW^Y6/W~@ ߍ ցЋk3V9N|?ƙgi 0D4n@sG2҄lH=[c~+*)HhXp?K:.Ǯ@-0^372ǾC(-M-ƄmԷ( @:@ \s9y3:(N  sX26TPx+6-2'PpBFx4emRI*a"wֈpI6n\Yg64J74E&b|{~oso [,Ӡ7S3|mwd` bf<̌Cx`_p95ٰ/E/C%Y~w}<3 c h:#p):Pc\ُWߘ0  w@[ H7\5Pȯ~g O 娛g mFGCrٴk[P@gk_zg7:giU@@Pf^ZQ.}E}Y Wك@m x 9^@TM`/ E8̧_(M|5sBr~e43(s4ylJ  Ï0t zz U=>h+Z } >C/6g@3a4َ8X`%Y9fjpAM <, NObv7ouQqbd/, g}aQa`0"B6D_X7iz^U pq2P~2wP Pu- i9@?KA suPe'_xFXB m t:ZH0EGDkt%.2 W8 #}8K0"XD=M=p( h1s0>tPnw|#WUnkm`?n{0-V?m􌕳 /w(7  r0 Dd 169 Ƃ}{2n{|p [_Ȇg 0?9t#/+U@Np234_y\W_qB|gK=@0;ٞɢ(Kc$WPynwB9 Lmӆm7`XOyO}OHj&Noee̹f\[ z OggS`3\&   V[>  ZWK(i34-Iekh_JR\v y_s`5<7$_X5(C}8Y}ʱ7x}JzcYt5djRZ}f,=cȤ_<>vWTLPEͥ15ocǟ{ |Lw`k f9 R_~ |6}C}~-V>`I{䒹w/b&b 9Pjɷ*@L*ꁵQ[DFGƼrxI(n":JHWxlU@MyQ@Mө: ?/"^܌|˲n6uo:eCb@h};f T @V M%?,-H:J( +W(^ u/U(Ҁeρ /o뀒I*L^lg37EK&T"dK?,ݲ)2|/^O<~>YqtAA% ; bN :m:e7`i0up۟z @ )'@W(4%b,!-nɁϰ')p}aVBf(r![#>i\/6݉K1sPS&M@"+ 9`1tT73L>l{ ܾ{/%w%wL?0;.[_Nm`~S$]JFwE@}ݐ,{22j7!SZ)`>:d=hn_ GplZ:g@ia{sa-t@_zK\qno`A &x "=7U TW܄<2S: RqQ<"fxOiB(v UV1 *q@iS;Zc$? $0k¿s@Tt`(} @n`@/#` bD 5Qkcd'#>`$`?',E %+"o\C@O4_1(_G\;$ߖ8~C`YW1:4E#hl ցu.J 6&J@DǷHnUu"\VE[ 2n oG3eτ PnJ:|5  r?PTt@=_|ׯ7(m]Zo?0gB@D-H 1~CؤD VAXF`7- |kxKpn_8"@d9f;a=Ǚ߇@ZJޢmJ8 |<{.3p aYY! &iրܞx'2ok}<?-r5pUß/Gρ,"f4 ޸cUH3X%r6%mmsx x%/x* Z\?oK8㰡{3_ ham eN&Ԝ̹P1 T% %'Ϛj[n;+.pZUZ&g݄ N"{`Np/*1羾I5gۏsRJ& Q37 ?T)(Og|濘n ~/ Є-sW>XyE't k$^l %/`U z}CMEw|4җmށW=է9#>r[t5NFg g g@`|M0z.m1wO@ ۙ{|azq@.O-ڢ=["ۀ ߤ2c$9B(-ʅB/oA׭v%OM0E& W2 le|| P菤j9{3 3WB0>%@T1j`'(_oQnt錁 8mX@ ;~ Ke,V(~EƆ11*& ^cILW2K zު[,wk 'ĉZ^wo ϕP(@Ḿ~O ` TEo7"!4zdG.Nteg_0=3ei PԆ:J x!L4_d1iJS7[O?$0w, ?38e @Y,,Z>a+ ߱aByJ}bpgd= BU?׌` } 0x[ww[s\5@My$PH?QѸӘO~A.3?20Y/r.fZTؗ/I]@ 9(@LA:|S2]~O`0KUL7 a@ 4p 84?t;Qh`6Vm ,:kp&c`Oc} p,(򧐲Ynz ?~.'%c ձcu{tW@CJ~ L  6~YUөGF}DLkb0msր>w xw2 pn؋N~ןB p80OggS"`3]m|Z  3DFJ^E:`1U8mB>ݽWc q+xG ,05@@c4z+xI5'?+-F(]8aEDž8BI S -qC_AP\GJƧ`Os+P hpgbK?t|g_Tbcp>Ecݘ,Б*9Ϡ N)/ةvloWWЀ>{* U l!p4 jёdia}u 0)S@uس-j[P䀘lO) لl ӎ@iC`+z6(U<>kVD.G6P$"᧒h?hC{OsWLP2O44  )}ܟ Q:#^E#r$rGpW%Dq{m!k|1p jpW9p0[5hdJr}3\gPng^Bo̘;+KQJ?d+d__{r[.p:Fpncn A@|~6cR>]sIo}n#o0TH`?Gw–,u;,٧o->Q/͵0|G~}.s @ڶ_J ξrW1 NKv{>(@/s =XS o'lʖ1"qX >x?I4r̓8ުPL;eKHdXS@܀zK _VB^zIJm&#@¸a2ᅘg^/ ;ޓ=@o i%s $]U(Ԓkܜ)-6@?h}`^@_u|~O`(t׿5ȴ"`7 Tm8U~x῱GH QgDT/d芻37:&2X7 ,'*B2%TC-P )탞 +YR2]A5ߘxzN 5|\}70&yw`GO32;[G@تrƷu:R D0{ ˸ ^VI?ND!:MØq` 4>XŹ JZAJZq(]뮱 qyW{?sY g \']~*u)gڵCn5xz 3v7?wSs(}g9&"%*&V'~ 4LrG(;R2dh)@.CP ^,^W1_o ]@6 ݿr7Lr3-LBl8ӎҀ ~XeOFR%?I(N*$bN@~?'u.8oθo@KUU@FIGӺ0 -}t =ʨvv @ \ۧRCw!Yz~}O<"&~Mz'Mu5p>[,C d,k^LU S8Zꟴb T'[ >hw2 ZSEhP|od Ǐ%Blw[Gdr߀K@)] Ax>_91$+ouͽ">G0589ɐ8{OOJ`(d%=$"v'&n~ so7ڿ t <5{U@a4cPV]cdw{5\1DN^ ^8G2I8oreb#i _I)"B_ۉȉ_ o@qT \춹[ϻWdN)BҠW(Cپ J[7SE@R; FIioȚΦa? 8nwn$oWp:C7N9WF?@^AKVXWTtSt 13)Tn4&TP7WrOZSl=܃L?Z |ᘒl֒ED s߀ce[*$si0'{ߌ~McdMHvpWjz6- (hwɡ Їkw:uy8z @S`jh|zV(R ̈́Gk Կ5Z΋E+4#nwr>O ,a H`(8Jm]RNAh %FaGshE 㜁|5R o 0a3P Ov jP ] 43{ &9P(S2$i< !06߃,tb׾RT}@noN)` eZ[?yH Or?m޾Xur$|${^LD|؁B|ߋYy|ٜky}tXhy'ف<= U,U7+ڮ/7Gk@cXMNK?Gs]LA@oWۡdKϿTsS׃y8@6{c5FLUxzqpۧ>lW @OggS@Y`3^5wGB*&! La+M00\<1LL߈ ͻUVWFw{_ ay He|Z9VqWW\uh$g5f<g%<;=T׭@b1ā L79 *U?+:p 'Gn~(S]+;@sE~Vzhǿ9Y4?P@QLa.;aw=V3j]yrgU8ϰΗ*Wf]nyqW۽*4#"AmqUmU:!aoN=@d;::/p+~;4 7x<g4(_mъJ޿$s۬yuHg c5wjpH պC[(/%!8%&-Hܮ%& + 7WCWEf(,xX=`y|> `ky_ (@{zto*QTGP@E ;FETQE,D*SU"'[[r, ۗyP/%i7hjϘ`4`pf ^ %xzBrϜވU9WX\;""E@`50P0> !ht,lۋob\MKX#xN hz)\E{ H`XopVQ&-3B+g`rt4!È }H㗊z(D1!!SQݷ?8 D_ !6m7/=% ?P(sϴqEr&γK^mE?xr,M`AJ< `0?8}ހ oN'BJD@ `ߴ)<:-P=uR P~ @>gy2|9\oQ@ߚUT[TB8ȘK%`>@0//x_^_m$YV# 8WJ ~@o0} 8ߝav-iG[;  (泟-h~qdλnm@v"`%V< K  ,6`)/U @Dp/@TzΔs-)W3(7G&(OWxV)}DhX0 \2LABAr(<֛cp`(@H`C>?06[{^Zjst'շ@({ V9'ћ˟TǙ=uy^?d8X4x~P0 qF"X4Mh>|…FPP@BTW0.s5,.TC!x ;%R.@-/2v43dD# +Q(*ndOr H]˝i`_{ 2mV!!h  NcYv F$Jz0Xۣ$!_4^ǚw ZR6)k0Bxhx&* h|x(EMo(pe`Ѐ&&81郕=ao}i?<Nϩm}Wn z(z4AYSbukbIfݗxxwoN0xzWH ϟƴÃ!MSԆ ~Us38׏#*[uX+OנNO<3p o`N10=]7@7? ١ _NM[ad27!<;.[ @㢲@5:KHƚ_wb&m ºvf("ZzK Z׏ :a~3i3оaʭOiIa3P p~FۉWegzUU_9-{/?2Fi G9π@Bto~l >sɘo<.6+5xwOU4߼E8D/A?`\`@i&.r72ETXvL΀X;_䅇rQJu(%K;kV}?S8v@TY)LD`Sʅ2&0dǙ){W@u0_:nWu9@c(7[, @`+U}䙉ȸdS Ȃ [J!= /;8z.B;L c8158?hB RZ2:qfpo!h,$D Mc _~VAgoIﰧX)?UtN]m@~@2 x8ص9$,8?\[٤eLP@}hJ`h\N*]H8 E(*jYRU@Dѷ#Buj9ƞ0_TfSz0 Я1`J S=|w`޷[o'|vyɷ욈& ۗj#\~ q[%,0Qt W 7! hjso.|m Ol / @ {* +P" E@ND௯"Kzuٿgq>'vO]0|ؿ6;L @KX`??6&mCh%?] ~sCtoH~w5WjpK[@Y.T yBw " "uǎ o3A8Ow_Zrj/nx`F_}hl嶂(8%?/qp\?ϵT9PX>s\pă7M`Ep*ăM0CUS!\9^qvӝ SG-di|Z>[ riswvTPvܼ.ߊ_KZ2?[ߠ+`ֻO Q,~ pO xlrOpB *^?@<p %8p7&hઁ a1@0 WX.B"d1ۑ_f_gBqۄ-C^ 0n_+d&!S``eȖ"!zq^*`  m!'[>p\@I݇M0\yL`_THU%6~rpTRVgCo;W ru~O`V?̏>`@-Ӧ@h((^Ur.0t e/Or-f=LTI~m_ 017o߈WX,߻j "\ӕ؟D bֿ^#/h:>+*r7/%d(W^@b7Q_Z&1v2@\7T%~`40 >ǑGdqTeΎ7J Ԉ urJЮv\zL? k*(~g%{2,GVnø>r<$@{4A98EpZSx"N VAR]TOr;P0cZwR9tY@Tv^$ 8sDg9_UDgq~>C\ݻ1q0O2ly|%[og ׻~~@oxT.k:*MշA o_ٶD ӛĥ6lzX-gFWS?3{wLJ{殣%C'.dWE܉;6 ^VT 0ظMWQ]9T=0HU̅%ڈȱ@|<ݱ.Rs]hBȸ"fQ\1yΞ,혭Cټ1U eY=:ؐ~DdsI;u]hp:WAEPe2tDT/GF ο}?-Ӭ9|8aSooޙ%3/FTq9+}c p{n "Ld\~1@@OpfP 6d5W V%IrhIC8]|ޝ UW@@QNU:|籩dU_g=&sW0o`/9r~z1߫D>ښ6͹s;T~ -}W U@O 乂#@<Ewx6~`vąr;+[ !?p+y,#?z*1!!@K6 ɻUaLL i%sr!/<`np , P&G!l^ )Q q3)@,=^+[R7ڭsnx7t`n~av{CC0f!z?@=V0+ @vā0#8Xڸ)]I̮MOy\0>/*Ε'~xb`"> ţTU&g{|T Cs> 0-PdB0 *dvuK}}RQ@Ն X~f;N|۫ 5 8FK =z`}<{P4UFROggS@`3`v  6aDiQc喊o^6$8 p&i߸`؀sa`:`ἂߺdf!XS7$ X_(ÉPhS?N߰EP*/66^_Mo-C3)"xqysF`5`*j-ߘOsM._4ڴ~߳ m^^"b^0 ?Xizc(~55BKQ f=2By187</@ s..hd ȉZ+1Z# X GO{eϞ 6N#C 'J߲Tr\<# wm2zpJ>BАyN@t@&w0 G^ہNn>v(Fwxrs{;~oߩ P``!x nݠ`L'<@Tѵ?O[Gj ~`y'Y<. @8<\ЏZ- c(մmd(_&( lb,f 4%{͐%t W`lv : p8>W >]2>=Q %%IN/SHE_(OQʛq `_^0`8%0w7`i'T }~u =48~+n۔ 4y68s6|@.\:J|uJ(-pvȎFt!!B9Ui\e}%ҁ5֟ĸyj~y|| =vH gȯO 'M>=B[#RgW*X\-JbgYdFVF'y]` sjpq"zh:i0M .n`) Ȧn7^r8D{s APg"Yj+UT:";\ݮ;6t^M6Lϋf@4Xg`h܍Ow) pny]ڕg @ !Xp7q/8wqAg ֝n 0w50hL' KJq6LwTpZ-@YB/<9]HK7N2@YfH<@ThC=YuU˵rsChcS_ZpWjpOFo5^ڙ=U8mc{. Y`~CY~%Vpw^=B[sDNnA,T2_( c{r +,Γ-sP"[uW: [~5tɯOQ{gZpBvz   ݧЎ$@B$26=Um* y~ќ5o f@ǼPrV:>&8XpWNڪY@cHp e<>#BRus+g6*T*Q'-AK'y _ ^`ݷsp,*?J 4и$oy=ʐ..CZ@v?Pۏw n c @WfD8 ~åw./ &f?,k/NZd^#cR4Q2[U?_П 4hú/j4zz|{9nsR_X=聖-SibTRj 7J"&{'>(!bm.Ǧ#`.g"3F04#D6i R޸_UU8Qx˵B`=uFaOwO?p{so^(z>E~[aӹag jIƉ^]/ؗj8`Y`,h d&oyK)h7[}ia n<ۑ6=ABT$B|IͣP8;OO-+ƭu<0|w>`5y~D]XoogqOS@E0;gt_@7/VuZE: qYB|Х<< g7,^c1~BH^x8Vԇ qDS.aC5Pڇr)55{@Q#Cgo%'뜠ѫغHtRaR6(Dkex >X|kZ$#͹v ȹ58_j\_b<-:35 ߃GL=w 9$z3`[= uue[K0XV*h? ЎE"E]0=ā;`QKDXy7pSx[75o,`*79wig[V(o\忷C8<8g'-.$r8D@4@rTC@T#A|@$~xL@w@GlV&.31׹+kT_gJ@~h@:r0 ]_p} 6*Y~@p}bN*ĕ,5lN,c; <~{'P2-7ΗjwF$+,<ݑ&\L@ue,Ю(nra9!oտ1`>o8Wy2y@6 SzX {*R:pWH{%=2P~zRbWr}ߗY Ђ[oOggS@`3am   z@5ϫ?j 9 327%hb],hY`9x@ @$9don{LK%ýĬc r" *ƨ"Oy|dv64ȥ4 p@0z2Pcg߻j3h-N=h{X- ߇@@I}CM#C KRһ'(@z#B5rԇb%^ !TXQ iQCfA$A»L 2!{",sr\,&W|g7ܿ8ր_Ӽu_OX߇p3@ky9>tp TP `-jU hM>je+-&l}(VsD5T"H2I.~WOqCzx* P>O'^/Zo+U$rF%ЏӗiR|iY.ٍTxpe\qS6`Lk/y t YS |i iE {ʙb]3f rdxۆ H?_UX2(Y U8/Ͳe#y3ĄX>kBېӦJnq }{Foƶ@;cO9#αN!=ܫ;ʣĆ xGa1QJ / Z'2} Ӏ>.zjxq_{iE$~P ׷_ 04cxt?."SMy,?ygTR`=~yZh 7*c-?7 l~j+nF׉+kM$2=~1 (Sz@1G7OeD.cC.۰ >l9Pr3K*=с[@տ"" 0źi Ƌ6֘*yӡeMwиhF?}u@( uM@WS7 / 4DE H-@$4Ш槂17} $$^jeցftVH~g:-0xJ)-.c9P9@ 5OvI BOG͵7\ lKY՞<N_鯿~?&3z7p l>56~<:18~{ y!4i;o!iU̙ 9j(O䜹s'М9(`$u֛?WА-@|ʍacvJ%Ѱ@r'*"+)?c;{},ߡ5M;L:7CΑ|K5U/^PI:P`_ٮ ? w.:%<%̗[סZ_ f>3!| d2i=1'S}gybA8A[ACku Ρw^ 5ץ>/YJZimO!Oji)]pҸ̂ؿle4:Mz ]T:N+^nAe6`JlH@Z`O-$*ғ?ʁ x?6JC~N oor}F;$% ~4.@Vz~j#:[8U?K(Ǎ0د Xƙ%8Un=1fiYSh/1GUSD{0g:{7osxc3p؛yr/!9z}~ԯ۾o8ξTzȍǪk_֏E=|7}>iswj0: 7(0X xNo[w>6 $ВplBouxlhd6PU4ު5v&CǖaU̳X;l n>cA %Fi$N(/yĽV^VcۜCNZar > -@ILzZ|P>Mq { np4`C}skQ @s {9s/ q~U.!k׉ZNG[?Yw⁑w\*%QRy*cAjW(&[ S`=MqVS|fGq?&鞔Dcңq@!96?7$XGMf3052a+hԃWK~Nk(9n. ߮{ [֭;?}6:e53d ZOi'o 8KU>חS =|Zmc8ˎ@645N@-ygj6[䠖bΛ9ɦT't VюR l\yIV j>E8'UV&y9ìBՎ*( LEO+qnWZ£TP?PZQ-mQG=,3z ##t:97չd?5oS{' '޷{Qg@=ğy/WBj? d/olhٶU^0^;ї!Ϋ?XjBg@ʩNw`􎵚vm) g .~P J#lL\s]WL<4 ȑL4>FEUY]oS_7ǯӧϑNc8( Y?ݸu# sm}x|8={3~Ƚ@fT:9`?ф4gz:h`28`ׁ+њ$b~q?e'a"2ibXOw'8[2D\kc_^v!NRD`qx̗I]$2(xxa-[ƭ6ަ=Iޚ.w}!q5 ^ȜCL+_X3O_ 3eݾO9g4 М_c#DT(B%m ,Va FM`0 .iV`@,~+1+Yb,2l soacc=@ESU }8țxH̗nY˳4XYK(k%0DFQD ;7\Jo9nWn~\[3\(/c9&8AN/ۼ8u&]m$_,s  ɫ~ `V ҃-@gØQ"O@8d_ dOggS@J`3bJ   :8DG;7'޹Gb6>I_ vp瞎%On쥍J;Txg&o&߿*ܟ~G?ɤ9?/=^4uw=zߧ ̿ws|G=C;<@w%xo>N x;w&9ܳ2 { ƟF34;Hh P{/`6؟b$;d DS*A%E YD 6I&(@Sm^Wj5ViEwX;'Q^FQiDPr 7T8;ͧ !xY~!V&X◦6l}qߐGdxy< v=DV߯u(N/Mx۴?@oLo >id<_}\:Qo ( }X!B7Wg()RAE Ɔ d[4S^Wٔ+~^) kwӆ+qI68ϦH z"ew Fo6JXI!ZCQ1np+i/}?sW{hc`ru/76_@֠-p/|υ`#gzhs5ܜ'@^ ;H`Evո^PuyJ1P0 @8zQMM^GlJgՐiV!p%y"z>/Z`<\c=OK{)l( mu"@v )6K)ʜdw8x<(q1VPJ(&(N₩^ P*Di0~$E~3Wۇg@G/ȴL^& 6@|"1z`O4Q{h{M'0-Q%Pɯ3q +C"ghୌ] ӷGj-?>l7SzN~z lEqzр>\$,+D1Ҥ ([Z!}ʯw=%ǪUڰޯ\Sv|c o7?O_2 C x|8ֽ%@n~3f*w˥MJ؜|'+έ^dx<" 𡠔H"`i}N$P@WjBg|6#4h啯ĥ+;G/q(ޞXﰁWNE"sBX6nfGF^到.l@DZ;o<{6+;'N\c$̷zg1&<\CL7p&`Zo,^!2ǹ@E.r<@ߘy;@ l`K/12CڊyjaYe(TNЇn_nQ/1TjRQD{d=Iޚ,ڢ~8a?g`Nġga76 \׫#ډ+ac ٿ|7z{69Uf9$=o˾w5z@BL-EF-a_qW^"V9|?ph6[_jaW"N_S*Z%%A*{5-Ǣt (v`^ΉK]% F4A/7>| 5r˼'*"n&ީ7ŗO9K*:Xo>Wn>vT+uZw,gچG_ C@r.RYqK P2?gAoolXfaK ҏ\~0w5{^>XPhj:Tv^eܙ:}GN9@ H̓Id-@<N,/(g6 u.2Ư NKAA@Zv 0/X\e)Rn/wM =rh^x r;pV}k` 3L!,/z*"T@Ԏ’hn*u$ e;;6>m[@~34/1V:5 |<#1 X|&tm- > e)zvR>5hJ_$>ZS- bN0`9o]l ~`UUqU4W9L%QVJwb~g)]9B _((?i? (DW b:QX%'$* z;ǨW?zodrMi@W^3g@slԖΦ_53+}l#r+.rkT+ \٥-Oɛ^>Z |8ơaab}p?fCepIK[J/<}x(tx) S49h?LW vpk)=Uc.|[}?o׮*P}`Җ>^^X 0!M ^ڥ+k_ZtoaXXےmE^ gbжsp1[L!^OX1Xsj9I )y1n{V=8xHCug˅Q`jk5! jcY_2˯rik7)4@)Oz@`[s& 2P //$CYMf g/tfO@G ^e;s5w]> gjҖddGئ&N'vn`4b} `:'%%e (z0)c)%K ~ҵdtC (N42!A9.5sysX(Dk6mo4Gp^* @w=%d}w}oit%߇L6ύHOW-sM B=%-"^f}?"4Aeg,Z5?٬ԏTKR /i'AwJssƂ}o88o g,J0#˕BzIcγƫ7]s=>c)@ K{__mAΐ?h@4pPΔ?@8מ`.~l+_PT<|/OggS@`3cЕ  d`>^O杨l 3_3P%o XQB jkC(/XLE,wKSFR)}6R]?u UL9zz]uN'$ 'ALm#{/^NspG1.|)f2f5 @~Y)M@~'}ƪu_U}vUV,C&pkct:6q;zPm2-o𯿏-=в%XbUi揁ZŇەPAǑn8'}3ZѦoƍ\zаw~+@o/X n7@k_+gh pۯgDC/E(@S6 @*B JG GS@6O~KɥT ɥ=p)ۉ&>N(֞D0ftrX 9~;\@6I/@cn!M_o=%S #Pr$(@(+te{sTwq:I~N|}aK&,|C '4(olrc<h.y8WN ]`n9?_J索`=%2 z*@K@PW0ٕOV9{W*8E6#d"kjRh$p[@i%<&RR ( q^lաP#n?ɽjЮ`)a~/a ?g`b - `Q7dPvGy9c}~&hxa:@4z;ߚyaS{_:2d7m @Q0H`Г[  @@6T w eq*LU_ 4 _GȜ%hdBB$zvR;ZPŝ/|XOldOV#Ydxx"ʹ;ܠh7\kyq?}} ?)w631MTOwd=` 7-iX0 @`*PAֿm7P6luWJbx;zeOzѮؚ9<;u@F{nG6HOs4cr8RbTdo7dsș1"aMUZJHO9 ܎t:7605B̐ ;y>^0_t{}8mbh!򿾿*:|[Ji1M`*P* V`504pJQog.Joo_[`^ 8tZ"ڧ{D Q$2.,hhs DkٔЁ6%Hv{}6m)?EϽ{Δ5ϝwϢiEhx/O?B(Ϲ6I1@p@׿ 1LsooeO0pB#8%ʇu}}3{@s`^Ʌٗ. Wj֓h?8OphbvKiRcE79$4 oܶ@5&tgm L fp HwQea*Ө/Pr6P_|~@.?{ W4Ͽ2@}W@p}78OA@R8z   #l=2 6 2@~OVvzL_za~|g@O\,gÁB@@yOvd_1gtb}d~Wӧ 綅u~x@q'.]lQ?^lwOwL~޷q{6W&?je ~ޡzsRL8~@OpZ 2_ &^MZD>+P |vwE=?xg/zn$6`lp<?wO:佪T Ht)lVl$q% bpsaVЎhwNKe/nʬJ\BZS;}F۶VUE*8Q96ڻ؍ DBjF|?_p 1pu.;9s#> ?}j0]p `z{OB@#`jhTng?0རbl)*UOD"{[@q6   p! P wzƥx\ 􂷧4x*;x=KLPUQEsq@ 5{X{h\ e@oZa!Jtʯ5C;s=`~]Y-C5wyؿ hQ^?>la*=0@R U/Ib SW%Xr[k#YFIH5 } OggS`3dUpn 145.3ABF@]l@ywNKK_,OZSA7"uK{H 5'vnG رRCͩEu["֢eCBO >o;{)Q':'NSn:#u/(=Ɔn04qT_߾E0}ˊ}c; A X@߿RPEs c,vqx8) P`+xO eWㅯ0eait+c8g{U2. ,C z?2Qn'] (^ x[ DW(J*TU0:=Z+Z9Fng s~i5wf2^Ѫ_xiBƌ yyr{X(I1W \i:VbgUٛsF;kUlT7y>?Ý$Zo\ohG_/@^hыC66 Ayj }|zЀ.^ ׷9ǹ + _UӉIMuO-$ș@DK "@8+"8Ȇh R~iʯ'i/hWfK<o x n8"MyS3M-v뾇%:Vܦ 5z> K4 U(s&o_hBag|Z}쯿@lVoA#&:܀CF]{60kM%6? @?|<Pd1l8jtu4~\6B8wOlh9263HGeRi5﬙_J竂|&3SVm׀6E/ @nh25IY$(r7W@< ґ̺,|ƸS/)zuï|#2%Un`G~ݬ/?o ןO0ɕA  `.!KX2撯 `g// 1Z&`^IO j&N?!Srp=N4 I-uJ&Iލۉ SveXwOouWgZl`v3053 )s!`~m?ӡZ8 @鯽0@ʿm )38_$t |nmX# @۴D\9؃3(^Y̙K?dՑ?Xlry v8͘MŪ@.IЁ߿K2/m-Ɍyh>xju27X2{w tΘ7pƧYw xӡS9u n3 uy~0 N@5y8u>SKOU*/њݳ<,v.S֟<8(,^8<> ~)uwL?@T?-|/Df=Ҏ2G}}.@erEU hqXxs2Ѵk= (co`l  ɊK_o}n |->V:z w`̧߰gPT17-Z?(QǾ@$UQEULÔe+p1v {a _-Z@*C~9wHMF*V[[۽}J춍aI "4?8NMCB[C.&(-ycא'N/ etfk2@(>CO8`}T Z_:u2<@P~ @|ۅfO莬ʓי rhb +2HhZd[OgfC.r^)Wi%)~O |!`䭰Ov{B,WlV&t`ҝB[{NI %fV\|c Ƽ\$ǽ_ p~|l2X΀ѿM|9j/4A(m9q8HT ZcP_@.$' c$W90p!_`cȖ)"S耫Ga)Hr(̻֯(?Xbw֞<㞂3"8Ǟ7my E9W x*ed]>g՘ױUCw琽x< D]%Puudm ߟ|ۿ?PZr@~f8>W '̳Gj6]Z' gTQ] U>ź0"s\C &jSWHyX5.1 v`@?eg4?8ʯ'qG {+&nb䆪@.q )ζȥ$m13 ?~JŠ S M27ko7) ΤX9!Ͽs+ M wÐi*I@ p ۖ<?x? `!ҡ:!5%I-\ ޿^X]'u 8vs#1 8245W,%SJ"qpIrbnbeUxw+潊NS<=P|hюDN.H%z!;KouƘl5R#yy %P2!&$:mok՚({7520{ܜ_&n}u5_|VN0;~ŤMnEO8 o oj[SXvB (H* ͛bI)2i{Eu' )sL1X?;|-ҩ\Eױ.@[gs:UW0s[<݆>ovVl~[2܁9nx.U/m\=[5 o> Nܟ6 XF/[=1eTH5'f'ݴ-Y0{|N)VbL@`$W׻叹?G:Qbu'8v 4zw/KhuTze$iiwrnvĨ"U_$ѭWTg!y.NAsZ<=;5"Y 2{}嚼z9dO>g},ӭ/wqlo'# Ɓ_9;fDo˟ѭ7h3=j9Q86,:Rkw<-M8] vs7)i8oix3ggy_>T&$Dqoq\Y&yl:C}0_)4̇(pd z|7Ѕ3Czq83 ,J -`"pǿqWk% ְ#K`!n5 6(fE.2vه\i6N r.SHr5P_~e??qnhV2Бw(t\ ] %#?ud{{)ΖyZX~yLzUO.wܔŘP8q8__`'oe>hBr@HoJOggS@`3ew4,8 - +*'% Czv&I{z]h[0/1eoc' B޹E֒Z8b'KB/ L$e 2D?0Wop>ۉL( ~`cӓhmXC~;=W.@%C~.1%\^;ڵ;>( (7[PQӹ>5K"]\ K#;<4 'Ӽɲ?|_ݷ?B&L%RSe=m=( 2 ֐/>JLu_oA| Qo޹ʚ.dpb kAPld"^5xsƍ4Pv+{lW w{@rQ8J+$VF+gO%7r^ ?~~yȿ/>\/}l2~@{ {M | l@*"B*j(l=uz^ʽ9߾+?a  <@d#=7}i-]V_G=#J0 ?!D/pnK܀&"j8sL0 )$>4z0ϳ 0%Ͽ`{-Ng[x9 F q B2nI.H&ML sghυ=vw`Uw`? m?B hߚ_X*yP~l Prdj ިU2GZ<15H0$|B׫H(|k?KNp5oaP4QOě 0@BĔSTʙ HV9@KiG o?rg=sƇ}r{P`CX~z %=%e# DvA Bwṩ]DPPd4Օ$&g6o`#sz+<~؟~_ X2H_ ^#H*kK9̅ׄOJ蓄Oح߽>+e{`  o `^?8^ BC@^P%$$׊/ w@.!4x0 *K3:g;2Ɵz YwTPʠ#ÁeMv 哯3{{`<-\@s|dX 6TG>ykKhkB`Dr[Г4?0?_ p#_4-75|"Z* S'0J!s!oAT d;ϞJpOk= }e>i Ng(ݎdOdWP\ p0"jC߲Uc6{mc|@G=n&_m{TW`~=p [@BGq@x>%*.2DKMAŲfI;- 0*}[W1G>`. (Uu@DL*;/Y^8*#(x:B!e`Y: ?xh3@n@tB@A -"WHXEpY{z;ѝg5 o^}`z gP@`Y $W6@G>yE5*42hBnJ,Ȣ80Wwo߽7\| Oo PO Řoc{$0D Jn[' 0Z;d~v7@wMjB\[*D %pv䝨jDvD,}:  }z9сpPZg+ oU-@=O 7 Xo@h%GK= xKi4ki:-~@r2p`6(+<g{* N{+&k܄|61IQ NBn-;2'z`*-?$@ X@nZ(s @.(ͪLF] hhDUP#-=WJ' _t\@ps*2`^WQo +胞J¯?HIE#C%ϸ(P]P.|;3?x ߀u' ;\ P~YF& ԏyدd&[ho^d| 0oVc_ #Ch>XUWOႥHTTiEc 2,ʫJv,ȻgLH_@qWO (=c95 <~]i $($M@%*x V}@EE矔0Qw~¿1@^E)@][G^y >w- &]N> }A^(gL\J%$IJ泆'iB"H+Ъ7x@0*p8Qxx\ P8=DN`pBQFv;B@ds֏B>UW:g+M5O? 1DϽ(BrĚyv ?{ ~+so.8>7xp5=u_7;`r i'q_wY@m|}f  _w%d`77>+mg Rᱯb*/?;`;q2pϷ `n{&z @[O-!@ 6>UWlR볕2ib%e#{[ `EoPzກ]| X?_uhPDŽc$5Hhnzo`Ү]=m(3coK*ss| ҆[0| 3k F[,NY Kd}]!I(G*i88@?_:_sQl(SA T_ H'7UWO'K*zTd{X !@l'g3~a8\}_@3qc h,Al J%ۚm7[3c0>]i200#o* *lF kF}يg||;d\剿?:@x5|d|xi0=?B% u>duH.wc>"E՟! } `M<`ͯo<@g%n>}*;?#ixH vm/@|ڪpeO(`;@ݘ z z 6N4wT5$!#2,Ie:+Y.pq0M!B Ư_0ܝ~ p8w`~ W2?3Sψ x傏孡Cbs8! yp z[$ z!R`ے\+1G;r ;xsWiUrgW&㟤O9~VPʧ3LKD!UsW k*H?/TU{VtGF ĢM L/@GP+V L& (*jwD@ݴul?~iCeBY}ysVonĝMs.S{xՉ@s/id(;! owY:qya. @I}w`x@m@=ec&@ 8,`?S3ږUU.HճX 6y@+[ܾw ׄcTmOEesxem@w J(W8`[:m  +| E PU=Ubˑ %n֮Y5i_x~c.mGzD|=c=ezWB?gybX~L}/fm~gϜu9x^B[Y9'e7A$k}@gCKPPaXEP93E9) kCsc“ ~|u (޽7@ڿf .gh+dc$`~S6'| s1( B $ǒ/ 1=b4Yݬs6g}/TۦgzIOggS@s`3glY6R1&&&$) :ٵ)f1f#A(Rz_X-׭vlc C,Q=)@\vMV 3t_궳lrin\so{+/.iRJv d) '8.EW%xqm ` ҵႰ-GwkPjߧ$} cu`+ߎ >4L;n~sw{NB%_vp~ <- Ҡ/0?sK`V ,{ \% Th(g*.& 6X@[] _/"OОsSs<+8$4!+(i6ZɒO{+>ݪz(tpXxUO`\`U1@OO@a/~\' t"LsО7\0`p@rg:`?}:w>6}xz 7 @賯O _I_ J`(X1jЁup mO3V0::$)~a _?q(x.cЉ`? Є .&bIHP 30ɧeWxT?}`е-hfkBf[>&dP1uZoR!BuK.jwqnEkhoP,cmCse'|, mh @>6 lп%hB ;lZ  - u`c,3~M4qKSx #'+[xw ?%(a\ap0a~]"+XZ-a縣6hsX>ړ-3{=9gŽ\Pk6]9DvW@Y.@9U` Z"uqnJmjk:0 /Z@&Yx `Ot%W6bjsVXo 5ځ S/$ouPL* T%:Gn 8_?xx@}!V,$XZ-躺m OBE/~% ('-~r?<ݹ_.+]x@Io ,&<{7W:4^`Qz0Sf ezۯW2 d_>Y``Hγ V>> 5jB iWZ[YR & J\(Wi]`#7@<!gc> @F/;}x^πKs:C\teIXt{XQ}Q}|!h²"qST0Vk"qnDTO[8KZq4 Hpz`c ƥP[se仾UU5 F4_ /4GujBJ殅9Sp85< 27[@T@\&$) %ep_FRc|]ڋR.}WJ6iWwP|[nPr;-E6x \UGLh:<3 4@w~T+ P_l<̎J fYW@ Lqĥ]=ω-U+_l2Գ]{{@ŹO+AO|pPl`B}St/ AP~ǧJW-M}njX4/Z2G@-5Ve7 g"\ۼzWGBڞ,~댜*| NLWsw/~%VP`ى^Q4PQ|.|Cl0~8ҷ,51^5msDţ\{GZ/9aSxt?%F5x7xȶbCd[Ϙٹ6a)<}sgfSP{X,d@  1(x PJngͫ>}0xyqW2fO_~țJk^h'Nn?}hI 'kE=֎? ,f v [~5Br N6麵U&1HNݮ{!x&8'|+ַ s0Åx@A!zl2D9SX0q}. ᩋCXd/k d:__]C|S\C={X<p @N/XPS`:= J Uπm[[O# e@P  uĔ~t[4薸BN(a&^Xçxg}.p(uSxVRǃi&fMו3y(~@9Qx ?K &H͵6 KJ*<:pk:Ǚ}^f PrtOOe:F`y@!V?]$Π o _0^`M{[iCl5U /Vy29@r͠u3}܀+ [Ⱦ/sSx v4j"Ea;qơv\ʄNk;e`d%RWrh |ص v|34F>~p6k򀂩z7߹ oY)ŲlViI<M.0`2y-PoPm`J^rYJ@r(Sdθ8509/xnpύ` 7,f,M 3]MgM Y3@ξ?G.@,.C(/U!f r$RjN8(H BteC$@n( o|=Z N0}a6UMXH!7ڿ ~)> ު-hiS\e]6+}a,97opqցLDdԸ9o MͰ;iʔ8;R%Ljȗg-@p?=L)F)P|q|`'ݪ!ZH\* xsf7|> r~8_˦9RyH `^XPh2.k$@ W^cQ`OggS@`3h(f  w V G%&ޚMhNG1Ԭ{}K+%Wll RC?aa]s#Ѐ\9\` :iTe'];(j/[|f:`BmX[xMeh5+#~_Fj% W`L_̹GpB9Y~F hgV$y $t{8Z>j w\BɼP^:,dPUDUn] @-45ξ|3ܡ?Im SVmm3/7xS%)__փ=mZC@[?]+ ci6g(p4ފ`=e([|J%E!XYDYDHςsq @s߀r+Ip76  a1\~!ڲp~ Ĕ m[Z@Y^X}hKx݆][^TF78s0Y)R}& a_f&2u{vzoSӘ?1@^w)W9e`+"~ '@ >g-_Z9*$F0p۷. O_ U*pa.LM5? Uh14o_jhx9}GAЖ ed{ "*Jsʛ֧J-}m蔻w6 5)  1jŠ1ZǴ  IxW +@@{th#t T:e SJ7c}qsVxnp+Ayr.56* 4n -#Hɷ/hK=LBI@KFޱP<аK  z-Rtۓd@v-(e!'&hj76z879q>_pv^9q|m [Pj|2x pKG"e;eeQfieCڋ8s \c9#ơAdI-{=ZQf./.=,+T[~&/F\y7e@0[{/]h:-4$?3OZۆpG m'&q [Bho?h  @Fjp{t8=Z혮jHҮ'-lvz{硆8q+#ʛw qnpB$euvL^L!rݚr}h:B?_Gxj>tQ.o~ twm^Z dt0r hbXj7_?:lq|Wu>*tsZp( dMW @)xuZ [@p; ^D,x\@nmmz d]5G'm)U]"&~}Y`oh#Ѐi] $B(F4=dEc/]^i9o-!$iv ,Ґs0m +\iSKM?vǶ_s-1y)~~ӹ@Md9 W  RO> "` *biZKm ک5{FR](h"$0KĞC0 8nvG0 Pe%;S`?ԃ߃l\p[@٫x (@qdv)@( EӺ(=s(p(}om3}797qhqa@?:8=@X/߶ /HHG<WG:|fUU KRdr֍ZEajN`?(}G& ?4kPwC s 1aUv1| T>tS⧩RVBo=XyzWi>'Tڟ"p.18`Ώ=}{|7 de(F\ԙ0DU<?4,xah\HcK %j%-MAVLj1DH֮YA$MgA=Y7)P6{mā᪯&oA\pH'Ds{+Pg<7,s.K*[{[o$x-/#U@ܢ.E@Tc1{[$fpS59W܌p;?n22{Sw :Q9^΁i:O;G5dk')4}R?;d jk=@"I` I?R$6C(WR¿(z-V3=Z;āLi6vr~ (9 a#*j8Oďrĥ?9g?Cvs<i?NޯP<:HKr|ctI< JΞ3=0_8 ;3l9iۏߞr|(M^XFF }ېi~*,`",`{ zB)5gO"k6N6/Aݫ $\C@bdmgPZ00q)oLiUr=JɝWsr)e@p@V,a3a- 퀖Kp?۷KL@}K/dz .(Ö{.S .PF7i@ 231~|Eg |1` o UzMeƙCN~9ݓ^m> 51g'60H[H%^zWˋ어T]Շm u 9]+6aP=rbyro6D)gkhtco3zy j7?u ~|>"S Ggr^\f@.0o}m:ȝ/5TP/H x@ zM`7NwJTG?BMh# 6[جC2́R>@A!a !@CNj|$mF/_xjG<~̳F@/-l*b }R@@箔 2Rh3)([)(#si=95h+߿8l܁} \x>hf  ZSincohn`=+~y賖mJ%TY %60OggS@`3iBi~   GH%08z5qft@Zg4>$7zO}b sLAA"(k|h6P蓓^c_ߕY>_0}g.e)Ot:+EϞ?3Tff> !\` 4ZGoQ\H Pmzf* ^÷;= vl` &A@% ɗkzB1d1T-~z5ցiQPZ% '(́X{ 70+B8馳ei%m/cBBYа: Q譞Gm7Ή8/6!YI(s[u5xtN3`uԄqzR)931}M h>JmZ@i0-<  hOג s-$0Kd @g>@ 2O3jM3r.GpkQ Cr js8ΉTPj.K"m[~uZuīģRP`H(o)m8$;P|7~y+N ?3 ͋>79Sr_ O_3>?SƇemd=im9 |x4d('BU^Z5je^(?"=5cP@ 9ZhjMї֫ j`տ Q9m QË&,F:4>6h &t8G3,ww@d :sr 1;,خN#W)}wmٓgP0>V_}Cou(_*3^gweSkMwLKB0yȭ{o@k& 3 $症!E6 Ri0,+@ԗG20`  ~Z5;j(-~]Ge|ȻZlk1#Br -׌=H?ְ\wZHxEyN۔oK|CuѾNޢЁnQmNstiy ޝ[@r1 < i "=' -a IXc ӪЁ0Br VY%Z0sA :M^þfTC~bʗZ{t" +i6zs8n_m7cX>NwܱL3{L: Z+*|WƱun9s58krczA_A|Dȯ⿗nøGUU*^թm/(ELvM[hb|e~lYc`i"(O~|KnK| <۶\^#owGCL^_ƈ>NG6#y%\>b:|7ʫ:ٵglyZUgjI"N Q #.Qă't% w]+J:ldL%} ԧDB`teh^6hwL #*** AsKPqYm}['s\@dִ['4\<_?f @}/}~ eK(~- |-p6| Xqp? U ^ +e)#^MQJ2QFg N v%ᆿ@ZDžewWo)w8F~=|p1z d-=@I[; @9nOQsT/f*( ߳U@!o0?zs{g*h籥)O^Jh}qO9PV:u Ӌe1cˌ:#P=0;hސ;<4vbgrrSmMiw̺eG*6? O0>$JebzRG7_Aos\Оu PS@KU=;2B.rNe2}}B "7?ʜ 2Mӏ?C)os:\ſ-8{فVEOgww`> Ti23D` Pw5\W Y u(l>eW|6.ԯh%vG otÍ>C6x@2LԌԏq;(Sr(@PQr2MPv1l{2oex?y?N[LXm4yr-g:rs%; (П,Ÿ`}v;K30sa |[HO`oǀ!x9f/YOggS@0`3j    @Ud~w)&?PGByG[h?9=G̑W> ;b p(ހ3vB˔Chgx kKgHO<*~ɾP/irZm /'斞}T/-m&]k\ZׄNs),th@pl=gqϭ%/J*~LN&JUK Ҟo  X/=hwD/mt^㕴@Ahu.<DhB @yr#EٖӪWVkeJQzm>x(m a9m7m ;ɔN} >^ 럫 *\mWfR@tF׏ Sqe?YX_u5Ouŋsl`A_Ȗes@?8p ;r5;LWitWzp@:Uwo_0O=xX` Dj<^Gf%˥_JgBfy*u^@ BVZWK?/*H|_2/=%8镛vSO # E;97,>lxQA`ہ=V,Owp`L=j|+WP/Y\C4ީgXѲiJ+0h/BiERs#u;[in)~Dh*@$ɨ[S;pV!6z@&Wr%>wrwt0n2=m vݙc֯ܥcDZP^q3-{P=E\K g^MԧWȏ 0Tb t ApXv)3_^tG3zZ@s2 qaP!d 0U?[)t-9-v UpӀC%G;9hxxRZi? bpybEeJW<HP'C2~;bEvQ3ړ N\MG2{w&ڀ7'*9nh@:P P7>5_+pmﯺv/жi[( l @oJ.:05 W (>(s3D;wUxy^ )Ke 1@$ kZK 3@?[} tD2 =6@~~J=쭔Y )Y0` 3ڛM[|gR燱JHQmW Mǜ"&JѶ˾@ũgTM%J(%z硿Pt^}S(t@!_C_z< m2?:p{g-32?Wph.+? ן<ټOg _G K&$x %>o`놰+eh{2 Wt-?@,n'^Gr%0:x#a9k${L~>ziaW[']Wς뉃>bZ@v:ȥt!gp;* Uzk=|y: Ɵ=r8OO4.JN˲ @dڭȺP/L a`De BkM!4 x Pɥgɡ?'Š =G~.7sp狜v,g^&#Bqw>r4K8=Ҍ"o PM!jP)،2ˀQTg z/ ;W2g?&_ g#br[{^? 5:{$@[0#޾T7dux}ϣPD\I`874 W|!R3f}eL')h՟-'.PcnL,3 (xJjbBt?n5S TD9NvSI~7d׮?v ϋ}s'_n gn<^D|r{& / r͟ @_:gSu: Nw@~v@w=/"yW򵲃oBϧ8~)hމeG\$! ^V'SG#B+Cp@=}Lzjښznx4(RwfLA@p۱! \f"|{r aVV1@Ҡ6S©ƦγDec^>@]ʉ 4=MAG2pNXj}`p^:Ԏի*XEҺ/w ̛@ ~>@$FGlcϬB3/v%`#$՘&:ęJ7Lր16M,D7rk7[oEZn$zn`k3?p`S+dZETLP ߶ r0GlY0h}4p? TufϿ? WF)(|{-cQQ(`ީܯl~[ _cIuW҇XKKvƍu8=x9ՇA ݡ-kϨ߳%u56!қ/d+}&x'PJ txTP󗓋T9 _D} lښ'{k{?0@*:׍M ߑ Hyҳ @ ܲ;JtHdywh gI tUF~Gf Jt]!{< ei ,g:3AEPwqw!ӽ_2-g?@yzo]z_L^f%O z i;DEw3h6p'í%(fh?WÜ7]8@{-|+kltӀۀl@41On ﰖFc~8"zсGo7[<7*8osbt! Ad;k>8<9t7U\qsfeWV8+J4x`|XR /eɺB7<-/  U Zc\~P@;yЧ|jY_ _9 kI5 #-GZadUx (A $6ީe{iɋTZy'ݞ$|e8D6axtAs 3Tӳz(P=nzc'I=㳪珁T.|!=.ve{qۭЁbJH9l3[J{YI \c~dh`}OggS@p`3k1    `~ywb|X{r)&{ƺ+⸈íG+8^ WቕP.YyIO϶ehا{ h/T(G!L%w8 tU%ULUDUW*8r ~[)14'|]T@i+Vnϝ p~aiW0|<OTӁ @$ 5wD{3b`0}d*I }}A(\jJ#އ Γ 709MNWoɱZ"%3;ƍW7o92ߢp=A{w }A K~9kX05#~/yЅlMO9:iǷ״9u;@pR8|nyP@^W.F ~Vp}LEc/ Bp tTlВCiokyODO5(+-+P^YGqa ȉMNik5r>w tԇ W}Ҳ.rBɀf.@~f`(A/9ǻ:H|u9 Ih 8o[ş,t0S!.Cuß`,6 fAn(nYh9 YeW kg]?Y?>`p#  5qTzPuYHheo<:Kóݯ8G#m;_,RĖ)\ʼnWBW\NW)pKtg2T ,K[ @at:l0z_s_%I(Mw,Xm`=a,A)!y>>igWk+ڗF5hѮ8zݍ̸Q|X <9t(B H}5gQt%۵5{:ȼ iŢam}ؓ]X] D@Ӱv~d^_?{-.  xUy[48|5~4X^1@Z29M۶ > Т5'n xH&ep1<J`gH֟ ՅPJ#5o_zq 0<w 8@M9Vx4ӏO鹃7:'<}J>k.3 FK;=nȔB׿ހ T|:OF6`zOٲqks?+M4zY{^in|`:$X>23 o1,}.3 *TiO v!pyH=m8HF.+=yqxa x 48YA̷RnBWԋ!!\X;Yzc|  (niÅ鲒T$ŢE_pm)yyQw{J-;[Z|xTS*S_Nea⁶]fw* EUD@py{wq@{PO[%.'%]_¶X m/eՂivamCHwz(%?ў$"_ oZv>Uրu8%γT.ˡdž G Z̏tUdWUܫ8q3ql>xF BeI1^d2%=<CvU&?P) Ld`v)<P20%?Ƿ?2po?^Oϰ%Gǖ *|ŝIK1a6(eO"%F'T~̮+xSdƣUuqK v66TD:ɮ#? zaQYLt(>m3@[XXQQ@z]fc.3sPxƟ:~ߗ,bUt@A:_[d2ggH_~@0RUT@+]f%OP%h7%ßd:[KXw =(y89־g(^ˎX(rT}2vu2g7Y˝WKogh***ͪ@ 8Ype8{8 eg5~4ny 61& _Jp@id`uޫ[k̯Oy]@v} ŷ L?f@&k%_~/ UD!g/"7 l%p @kb:/me(@ca܀ tt}1_wč3:V<zwnU ,}d[ l`9 Y~+x.*+YH p<t)~%3b^Y Bڊ#,iHNVW8J] y~ns !$\"㝭0reJ(/d}mvQ&o)2`7๋s )/ /(ӕ @YƏ@XFSUUA]Ucj(Ⱦ*0@p ȵ~4O޹/b2߯!%y Gl6/ _(Pp 6MI/gZBgK$^N>D ${x)]  +An  {uSt_/ߣw;8K\  #wy M0r١?z6 _v8DT` M1l9[aJ5p~=.4km Jw c>spL` Y)D$7TV. /o|(4~%Ld 7غ'b)ND| x5 oAx&qw ǣ怂RArTh]9l2O@(ݟ ] xcv~ 2BxL_Lp_9`Gз6C&5dɩJ] `>^xG/|CuW[` 7@?%OtS lP_*4~%g/E"\9U-4qVO&/ ՟퇁0]P'vӀXÏrwBCS%t(@)[j xlh&M DӏBa/E0X|w >tO ik YOo@~P_)4eH[>%mi&A]*"L2@@5$^v6h?`yXݾ8I~ Z.WoBN*e|iJd XWa5-Z 'g:7*`:l&@obuN |@_<POdK 1bdןP3T HUÏR\ s_5}?|1uT|Ej~5`}:E > Ľ(pI0 ?M~lޓ̏͌rOF>տښ.$8j'Q{%pʹj6A oڝ&jͣ37 zZA(%tCm﹇&[1GNM@K(  t2|gT =z&}+&UoZKX4r{j?d"o }p]@E<) >Fϱ4 X% kB @?.fsཇRM}>7cc4i?HTRXN.aƀr8%}5=2@h;wвӝ 8L/TvXtR ۭ7pKR`({x7]#x0 -O`27 %9 U~סo d%˨"ֹ\iDh#!@})QRH4)ۭ%2T)g v͔#{ޖN_y~Ky(8wlǾ=E_@mh+ΝM lg9_ Lf/26ԁ@g o5Î TKOggS`3mNm"*596JGIA)H4xs le~smw4f^Ǫt ș/$05 vQ}؃ %8Tmvl* FMѪ=c֧BCS^x<Ъ_2=t6 BR,i0yVVho } ߿g` >ig3p{& $Ԋ>t##X[ |L' ?*=~V|prD\WJxΙKv 4jgo璴 [~ڕj/`|ׅ>F-k O8V7p" .׏*x=[G=7Y{YƏT%:/2 x25]`z Y"WN~]o|=xs`{81#&00/:>!p `^mB >`&wRϥAZhGtй]V< k`ټY-TGpP;[E"]u3$^~(/~<.L/˟Ʊ .)bƦ!ft__@4qD‰'P2 8yO / 4H^otk0o4@f _޺3yзP _0A!5 H@Aq=`Fg%&ma1$CN$e |X>pubRq7pi \AbVk㿽7 )IT  YA[rzp?h3w ,@55r@5{M2r $r"z"25 Jl`ox:Z"ޖ1 Ƣ q0@@X5ZQ9lK¤Lja+gkI<ǒ_= 0=?|jj>ؖ@G1[xĹ/p+~6 8(a h&$ wxzspS.4p!*WS xB@vvfʃS@qx]_C5\f0pP:)`&#(΂@9IxaSzxuZ8Fsp<x_u e@_ac`f/}U(TGm7lIB|!`ۅvzKuevP96igU󗰮h Ҿh>Ky>X^}t X<~ /~-#xnڹo@YVN 6* PKzSW"{a'|o2lX @fLYd =o>v Pjh*`t@ݺ / p=Ygf0_%{/2s~{dH |3fs*@)UiZhMW%rgcXh;IG yӿݷ z&^ǂC; 4{ C<P^$}X\7 7uCDVԒc*79mx^:h<  y[4  _m~ʒ >6rxiczeh>{0P݋ W.Ptn ΛsJ1-)_GȕP['~ZO;Ŏ ij#Dm׾jRU`(iN7u#ߙјJPj"/̿%gmc@#Ȱ*XFʃ@oRynO6zEw2>`c l5nV o{}#J;miZ _{" MaK!~G%.mIțE?Nq |zFSXVIUl| @~7< "̣pFa>浼pp!SF/c 3DGyV")ưX?í*9_GƟ_Spk~npnTxpV$pbW ;\U5kռ~+Y-nGE3ԁJ *͓klۢpQA-e7rY^$IU%(\>=}'V< JosgBb/gR:]Uxkʱe߳g_2Kͪߡln0U 7c-{l4Jz[Lm/H* `J`d07褪~ROQ T-Sx~hR[ 9f2$UQT2opr7"xKBB<[},/Da{t^ 8oV~?$;5Ձe؍ܣ~ųqxo^{e=w{{/\}::(_o.Ddy2q?xE@0ꣿ}sUd:{O}U8޵?VO L[oax\_>Ԇ-UL/ej<7|Vw)2#ҷ- c4?̈́=}!ԟ'%=7[9PY/k7𺝮>i-9] \&r|p[׽^YdpJ9Kٶ={>0 |d<-iӸ%cm`=vW(d鿷pQQm(j0 UòػՎv=|[;b2 `hճ06p`}@ !? mG^2N@Ce!?%E/ @}0>܆6Հ (' G֬*cjߏyd"v:d7ȗ]#dr ur'srYTSS#?:#/o\,t@* 9( 4Pv͜Oqh(Cvsy0XR˗|O %#rSNadA\5< X;"h kOE,WtiVom 0N!Up:yv|(fk !@j;"9g䳥q@!t]Y5^ޯ^F2M^ĮADU :>rztw8#UD{5B@b1ztT`֫C;aW?00?{PEL.OԾOggS`3n|=.*-%0+#%!%'%QDHB^}|ŖJJ/loh@uހ2`p׵ 6-.JLك8ĭq_ kʸE8Pl\c}} C=w7_ȓzw7ZmshM%dsFe۷3lW<-;QnwR iK'_/[VLTUi >|gPf87ܝ0So7/ۜlf1ؽet÷n4>W\M_>[B@6^_PGw$ f%`@KP 6- Wo9xv9\8[\/(/g0CPG0&c s.vK^VK)~0>ǣDѩ;\g?5}<-h ]0Gʒ=aB{ʜL/o}8:iR:_Ҍ g@}[TTh@%?k s=&R[-'/!$ ;~pCv΅xj01`nR cVܸ60 - \ 82X3\ 0p]@? \$pxA!,Ji}YmRO*(k*"m e88]}H{~OЉ" k }</@~oN }e(  aVceR ox+p~f=KB. )q[kh w^cܹ_׵ߞ?  _Ѐ= Cj,<^㶶6# όP x,r'u x<浝Ik@k9I}H8/x+}=Y'I 8+?8HfM0?K 1a`Tw p=LFAd[%lX䯟 T>-VS*BCN ZWpZCH[]` ?_=\?`F{u} \  @sZ5\}r[ N 9#-'E;g8|;/\9'eg8[[n3{Յj }HgS0oDtZИ|z о f̛z`9_4Y(c@.$' (eG v52@KjB _I=W0+yO./]8> qc'} Ѯ9!hWuZmΡU-SZ3 M JsY-u^~ r[ l? @ eYY]UѾ0W9n;~".W%R5o fs`HIu~ܿ83(`~ր* ?_ Ԕ ב@CG0nB#{sz< ]19dx_32H&t^f=( gȒy|y}h 0}np~ 0JGgZ*?ۧ >^g +G2dJQЮ#`~/P{9Ә; xO|$47Bܪ|̉ 2alsƏ2p!'6xB =~DUD\Uo-g"*ah2n\8rNܚg° S & _`on o o5yp.=]`l^0__` <~+h$೫gF]IN),% @;p& 0+ySO^9Yql_{ٲ1bG6ݑP@{:՛ةo>4CNAC_x}F Toג N9o @Ns3_@{@ܿ 造ς3;| nWzS`3 ~YOggS]`3oŤ| !   02wMc$$e6Og-P?TJg쀹 ; }O 7緘͏F69@nx~A$y LI?APfr.  ԧ)]ξ2K6K2@vnK ϋ*gU a~ [ghO;~b`nAoej P ^&Ch@v|"\3' l)mNVWp%A&W+dh?W3(7Osmi_ ; 8pr |D /,^?h@ƳcZ}:@ &-yX!< y~_`< 8u@T@O{(B ֧KC nڇ k3 @~U_e$`0 ~U4KK_k%6g;mZe32$վDb'fҠ x^ǰ+.6|?xLӉ|&0w@! [Jw'{DZµWF&Ffpd-i(g 뻎|oZԶĵ)QE2p&+sO6^0gcgy{;R (g)9~2H+/ RZ 50"r+g2J_T6.֏];-NSi:[@4wn`SDDVoVR| 򐁞û4Z@)%CzB%FB ,dW\~˒<Pz?xϔ]AJ "SY D$%;L?3o6ǿ)>s7Z>8_XBXz Ҡw 6w IdI<$湸_ |ʙv˩s`Um1Y+uשM~r/| CX0˰iKnykӱ- MT@9\@ FRC?yqyr_-f20߻348  R;}KT`}V_bۅm@jA TW ( 6'Y)M*Y]m ?կG* o[e"O]:w7:A@."qE( 9xq@ 32sC%@[P5@y|r>[|! 邪M*@V |Y/_n>m~3 ? љ?V[ W \,  T 'Y7ԯ#e2+wMGx/&IN?D{ t7-9#y9o70+y6%<(W0N]_W/y,(}L YB&$~.(,7 W˿;r4؎>\Ͽ@oMw=pEQm@/̛sß:@<__anrޠgV$(~ORT |B3PPywImDb)D[E͛M8xwġq'́Ɓ5@32RD!֒LܪK)|:UȼGv:{to ܾm W#{Eu@$oT6wPA zL"@5@_t?^ н*|$W_ pg  U!:w0.OR".;eWH|qU`S (Ypx SYGUԂ/+=9N5hn|`w8ov_1Ko[d@e m>eOiBڍq_5x[Ïu1=^* [}Jsp(JPp`D(9^Q$&Mڅ'H([3c Gs0W~$̣y Busf#O4mWIHwK_% @/N)hVFv. 8O]8` 2MxjJѣI@ lϟ)Ci+@ &f@ns2+cm C{LOoR@`F:ɯ047 @n >o$6/v6^ٕOH[[U Z Wqy$i@GxCǻ?ϞL 24Tǫw=+.+eQS]@G{ _.B뙝3h`ˤg4 bw-!upzhO9os1qMG:P* PWP X2`[rfOggS`3p\E]    Co^5OM!#)hk,ZHPʗի9;?uY.77;_7;~4L| 0c>>KS rT HZ{ڮ;$(jxB2lUϚG!,@R6/)0Gȶ 3_p5@ET`+<(/3@*v|X\`o@Exl|·Hw " C| ;~5w2aڕ'4V)D"cQzh8 0l4y#95XTK}'Ⱥ%t {p+Ο`G̑.쏀F;T!\[%L7+р^c֊KM8Z jsB|ӅWS8NZˌ`67 ,;ln:DV oо_:c[?VOg< `a27-̉kM!"́0~uwMj#Xg *OqU(jN?:c=c?E,#~u(B *ho"Y5^:.z^h8 WT BI} .Jo W oO 8'p( P@e@%p+MW %@Z h~W”9EZ"q%H}ŗ|nM8 R ~%El ی,tu@ol.@ԓkx?P7g/PT a@7̻ ;C8 r *lj?gvKO 06-2v YVBd@"|)"gPP@6Or2TUZZՁ_t݀ޢ"64vEz]BwХ/!" {|5(0Dfei桐?w4S}!R{hG|4 1 "@cCYp^n <770 0[/7m}G @n`W82 `%F%vȃ4TT G>W5e+R"rQ?kPA6p GJT`blU0؄Pb)D0n͓+U~6 v<𵷍6n (* {- @X% @.z"I.۩XaF  gW~x60^?6'K'iVH_,cX'v^.BRHd&G @ޘWv:O^BF9uӇIP |pnYZHp nw~ @ށAbs`^>o!>r.\HLpu^@01GlԫM@_M) ?^نVln@͐6^oaʌ)e Zr*Uz#As@xiv_́WQ945U!s6(G%xhfB!\a@PPplZbݙ}(;7\4= Dpp_q00 Zvu@y!=zCm`))K.-iL2* Zw!ݵᔒ~l^m ;Pベ?j!@w@9'xsPDY@+|@- w Z%4; ~CЋy;q:jSM|[/ ~~P\_:@xpEo  UE@a:CHFL%h͉#Y=Y&)BV]g!6SF?_ z0j ~d(Qs$ c(>L5ǖZ~Ѕb _?h1۝v1HQX!H+m&7@^i5+R=N H$Ь+ZNn~pg7`p j&U&vn!In`bLuN!u*+dJXAY?Y+˻ &/N5Z@ ɸ0b$0m CGnX~ _@ F]0P\]n3EP#ީ8upO/ȥ}`cOs: B 06r9/ G^B+$,%<`$OggS`3q~M=    8N#G XwXNj3h 8Fyi*g g sWbAفK" 0G ]3J- {^AreSX(gCA4]ux?z!DYz?ΝAzg* y =i.z_/" @p&0E5j׶yJȀ_ Lg!W I50LYm?ZU|&@icp ?gۀ?j `>Ha ğ=9\(dook22+t@? =Ȑ!^}f]eH9|ѩٰlUBj19P&@c[\iX>KqʿHB*a-wA`&; dH4(CJd VǕQ%|pΥt`=_1 醢h{ UU (.|J*6%Vɀ/h(|Ī@M `z|8(@e14e\<ʼ*2 sx{h- )@?{}(؛H#s/!}!s~i¡$cB \4@9u໔,#$JђBChDy~}A~p nW1PD·*vے U uZ#+Po3> ыdĖU._ez^ny;\%@ȸtAU 5 'b=ۅ?oO}Lo ;9tDxw%l06(\Yރw8E%#bؚ j{L% 9uUbC!-C\6pW|pd= 8`rA_3;UUK @Ȝ}B$Le4z !/jBhp|wnŠuڅhF|nů=?p8ԪZ7phy0c# 7 }+͋WH,a#%9~(tveҭ01x̐2Dr9u೤RCo#YK⣁T}͚AQsƋk/_Z@XO>X"5h#VBU05YeoMxaڤm9pzl .'}X'-@n//C(7Rjm ^cȸ`?~:XW_=`eֺ< WY )~Uކ\_XO5K.FgkuKJr+Ѝ1,9u#[|Ґ۟%F|%~&gKs&qM CUE;@%2i!U"%1OJe!L_X38?mo#2񹏎 ߦpyRoS_A%pUw<0dzC\:۽@'ܔ=6ZYAAmz{$3#"?0 ,%8]e ?4Uc)Cc9 '",kV\.(:`t !$‡lxXJFdJF!<ȶ:qr>xY ~F.@^? {^'^P$Ys p9su.0l,z-kcA?0&TFF>Tdo8q8MOS|HB$?'Dj~/o k ?R֠pUULt Q| {-Nciϧc`m{G2{vςcmcoǏn ?Ng}^ 9nȐǿ``6 1De6y)0!z}vNfF7eJb I0@8g++ UR@)Q l?(>z^fW<J#P}g*`eHj2AB ;lv;2 z O?A(oPO  |{`h; VOPJ\#fN[F_ HHD2#d$!n$4t-FC_|Z#Hq$Up\!7@8u$= _-B5 @/D_pCX1 P\%ݐsb-Au$e`i29O `nx~^!O/w;aȤbfjHMnY? x|Uk e~0a [4ewD ]E [cvQ˪H+7*}Kψ_!IDw7Jpm-hڟc1[(uJu bgIGU>*x56pXyJ%b &5THct50O (5Sb LWZ@<믁S [Yom-e%GK12HB^x,|8 #Y,}9/y}ܳi}!ZDo-V][`'{,Mp!:3de (JOMqH*?>H.HO /X<%9_tUh~٨ph@O*HC:QnbPdso/\v(/JVK2F$0uOJ dj2|po@TpC\Fl6fx~/ps2tlm״f26B2$f &K9Cj l0(1D> |O(3yLs1x$րgy9@ _.&'*@Y'CAku>{ c@P4CxZ @m~0[! ';k@I^Vyc^:B@6ɢSW `y44 P>N4R/2J] [2XLc5N|z #pavC|SFST)1- Ʋ#(2WCA~B~p .u|Ns.(:6`钡!cd#aC\& NfȜLw ']_}L[%4xdZ*D55-!Z/W!O 1 H1J B -x 3Kaly(ఋ͌rlOz{?WOv;`+SI snOW߁`l: e-0 :[GN,{y~:#H͹CgDŽy4,,L4Se¢E[ ( -쳱JEDa|#(Ay>s8< pCj7QEw ECBc'wc@t dd唄2d qMٗ !{'a_/Boy.{Ė DS ߀K7w/7]`c˔2a.PZk !-Kq{{ݓ> f],@HZy/'"}iclZhV܊ Ms2,6 46|B>(g }6A#olX(X TUE@. 5iu5as 8&@~fR<|Pt؃"eHY5EP Ww'hS s < {$pm= x)mT՟jE%edtb8*_7#oS.~%@wЬߙuJMs e =fI5OYv⋨I`|$5 N\3ݠdL^Wp A]ڣEYx @} Ki '¶\ۇrB(<БsڮĈv6 gxZ~"pcu[뿟;(Qg'0_b_ Y-x+Z-_?En\GƁ1; CirPwLSC?f)΂߱D8E%P 8 gNˆ󖠱 UP7*d¬Y :"`*) ,繥P:mB})=ԅ璉3j!:k/:{>̯[08|/Ҿ {n_ @n|-ItsV.;_vEilL WH5X0xdlw !@;UX=m@t-Ac VU!FCx 0:}tB >no,#Cs&mhh Hoȶ((l~,ECA:od O&G b%A\NXckJ VUB!+Zd_&|>.X~jgϰ Ya^S ~= hx qc 7,  ^Hl"<~36di@*D!^z`>,2_J+U*X+"L}ZS7k8;x?<4j<@d,[%We߯ Н 8,W(,O鈯 ql/*i>y~@$n@3QC,p UUA Ty/O.(o^Y<.;ç)`;Bnߌe _rR !  a>Ư,2/88? @x{N'7³6@]q/57ؕIhu81f( ~ zk#1 pga> 0YGTeѥ`rNi"/N{EOp(VU`@Q4f>ȾVN-@kM78022@. Sn֐N_ǫm]3`{8͟k 1@/ⓡLϥq 4`G `KHS+"ͥ;/sNͬHΰ {J,JS&;+<δ0vМ3 ^,RCL6r@ i5}x܀jq(PUU!"P֞G{Yu?%IA*N|` =[v]gLDbxuDn4g xmڇrA>G b.u̲|Ga1 0bM͏?{bwM$]ŏz*sQF&.]-OggSU`3s.}" 1135;QGD>*'*$!! 4^lWHLV 8K_!qs:"F-PUU!OּϦp62ùh__- [ ӻr)@_o!7rN; w>}> Is m&t6+Ni w ?bͥn ׿k 62$urٛu;5C;gf6ްt(Яr4)LmDwpa S0~G⠎id%E_TpTT``UUUQ$)c_}]&V Q5>"Ƒ b~5gퟧo`yS_?1~B U?u3 $5oarRNQFA䝇b5&]F''a_#3^nʥ8 v4ܵ+*$% '?[ן%{eԐPE@ KX}{96R$eЙ5^3&~s* uwz~aӋ9(cQ$(?Ƀf[M=U%/d/88?Xv&uy5R\\z{ѿ㹊ROw0m>OwJ+掌z@I, l4rL|Μ{zz6;w;Ph9ĶAd £h?LxܗQD{Za^ط&t>׫_>?>{9}=!m+/|gyDpEm&\gTb)k>Zlo0pa0/]nss~U]~q  L"<LxM j͕}o"Wl_nT~կ?[|?Eȵ%?okYc4uP2kfY(/a0*kzW7i7=G@ N|8U}  %xvvOЯ\$~I. ] 3mOvEN^Ag} 'ʥ }7VQ/Qz,P60 iX`_"HHZoSOڨy@o]@'yht(@Z @z<lA#k/~EorwZP fDOȩM"~@ V|y?b9yPy`?.My + PF˄Om!_j5M:p͐ X@?(,@div~ v k[E.֣(d%`Ơ $ {^i׹( F@hixv`su(@{ߍF7*(pn=@?(v,;o~LZCS_,a&- GNMr[/?x5^ ϊg8 ~j halYQhZ#+lswW5/( *2p~/?K*"'("]] 4WjYph2m˩ڲPl~ @dwoVy l9->%#] KBG{NWhçOxouEKI4 `>FO'p,R<b A[s\P^Sft ǺR<bO jH`w<`An\hx6vI=n6Dӻ/[ ^o8 }Wl2Fo7> &Gd$!I0>jк7[t?DOX珫18 ? v5f 0& [=h ꄗO|g,Zz@2@( rD"r0S!WjHخEN %6`GQK7gdpI%D@xm2,C4ŪN0:?|(߭' XԿ 9rOH8ֳ'Mw|/%Z[GcvuUoc* E-*qSQL%z5O^]iGx! `[4f (zJEpWxZX "۶q/؈մB)1iK @wj|@? ƀAFx3fYC @џ3TM<ElOk(ؒW ^(/Z:+|g p/x _3L|*:_':u8~@@x=y`Pگ_}^ N>:wv#],*KFъ?VVWx O #1$->R0ND^8V| p)30mX| 2W 6?S@ ~k,PD~~Z|Ag7L?8FaPB6di `]u8(>4J*yLJ < w> fyHOS߉ vuݓh5UZA^@ֿor ̠sQt,6n U@DF7k)iZ|˙[-w\H< >z< g7#^A&g99}" \>MD07y{"`T`[m~. G Z?^ y `< IAJ_}0<ŷǬ?Tƒ5 60?FU0PM63[zO.QLd^#$zȏn!C8 <q u )AIkL+sf  VTLTxφC`_6 UOggS`3tQW#  475434F8 @R7 ~x&-)5 <#ӯъ7ż ?ߞ?}6_nj@ >?ͧՓ)V~=2+,-q'8>!;CTۿմ,jh!8Vl80tjPaȯ8(KW ?^ D\V=?#J?uWc/ ,o~*0qxvQ:|8_AO ϱ{Ge,>2I-t_z@X"M0Sw=[\  9v:@&'$4@@~U(^1pT~߽ p>Hw Fz5}~͈G | |4ŷFR|/`8!0gOP4LTv\Kx ] @B1QrP9:5@寱@VM|}ѝEW˃t X%{?6 7Lyȸ6@@ͷ_ R uY2^ ٨ݏ~̂Ǯ'OsY4jyb] xSk|y(3kீ5w>P!$`mf@$d;Oȃ]@4_<gᘴ K\]LxiDRr@.啴vi1-3[#Pӿ9Kd;A_XVf^הfXyJ6|siG]7?OLpc8czQ7e S (\V *^. PӎN|@W~2C)S@Eic+T@ Y>,{-^wl&+@X (U@c*S^ 3 Жw,WYX] ? @K/lXG\~3&Xo+%\7< , k{3@g/PAF}1@Ȱ*P/gDGS OsbFK({w! & `\ y/PCUho#0 Nsa+yb[)w];+ p[ E1 3D=ʳ0\@1'_m >@7<S ;PD ʸkp1"S ?cU$ll@IXt?'jWaQ֡6p9_I&}4s%O0[?`>krY > sg^}_ [W7xX? ԋx)(U4$$%I7Ted ?!̂ @r\׷ @`d'Kd˜[@Ը qmw`[NwM߮dػ! ap]Ƿ/! ou`\ TX"X~?p~8|Bu^0 f?NurZQ!P S @PNr޽oW_nA)8apoܰAع _ @A[_% м\qHo[/ ND}(P[\~u{7݀twn-9\s *P^0(ޑm>In~W+ `7ŧO "SЗ^'5z-ǁ;wr̍3P 2po鰣<ǯ&$@|aYe.Ƕ݄ZTuh~3րy? ncdPN `> : ;vS=pu)lޜ'B``(?}.;{[+ D <uo_tnp{)|*0* $ۗmem趻zg_+L5fgo*T, $5dﬥrt'w1km]]x+[;[Os}8Ι:GǁpSd|B;㯑۸}No0]7h B#OggS`3u]3G6%)  LǹOEi01nV 3dm}y{ G7?ok@V2{d0 -Ӄ]H@fMkE#KP UD4/o:[@(|R*tsμmL"ĶyD۵ͮm?Q,,@~ 0 ,"7$ (kK%~u֭KU#09(2LQڅk3&{/csW?^T9ES)N hE37cӐ0y `@ο 09R> N'aiX Pds24i$U_' _|q-s  ~`x1w M7a"@;+8Vl4|aјBс|^I̫pkL {IM?}-Ų= O\K7"*@Tp-:h< .@2Xז_~*4> 6k*A3Nݓbi E 0A7J ws7>Apq3I0!-E ј`I08p 0KN@ kPnoU%z748l[B/Ї.N*G_G`&C9Yt Y,amO;?^ݑ>a̍9CwT/mP7d,=9- =?@^MhW*S&CI}R.UB&ȑ ~~}s:+ȵ~ ?h݀ /t0!X.*UBs[8s) Ӧ9ؙt Ϝ2; @{k_w;{}{ ޳ @^hg:@vT}ί &(#XL1750}[G:9=U@'] ZQA47~)RYٍ'cmڞVн  l^7p ` aL1e8AĴ[8j N:۳ tމyehrma^wNe}U%OnX9ͦ Glg7祬zxBLFhDv"m|./p:Σ5uz)*2>-3xRVS>^] &mdc ?>e#H: Xn@*P8 "[QǽJiQ5%#.. pu x"~4׳ ~<sfOH?\[P]M_]@8f/qF QcZUdoW&O 970J$lՕx'`ko,X 8^m;҃PÇusKDkڣ1Y*$ F_\7~`ߕs9(Ts򔆅UEC#1fj4 9j(PFD@@*+@Bnל  ˔I +pt;aS!_@9w X (۞@ ޕ[6>Z/h5@C>Noʔ˭㯑U/4. ږ2bh ̟PtU`vG oJD"N3CptVV_2 p^vr/g_ {~푧w;kM&U懝[E8 cfy6uYܟ]q Р0@d|I 7 `sx1opz_p`omn*C9>mW a.ȉm$U^hA)t/X`%N5p#@$ I0!VjbkAfMQzD %܏>6M?s@hRUXaE ƞ)d *D7ޯ>ҥ:CY  Eos_pzS'YE({ ?>^5_l032 j[ܓ(P!pyC s SxQ4 _)4oz5Rauk &|P?^[/0+/zBDty<&E.G?/ܲRJx ˿7r ?gX4@)RsXρthD&!#҃R+NyEzRV{T^5C(^ pIи(0YL%YU-,1{Ln`s pL(׳h(Yn^Q_.@8O*]@'KV>\蠐چ\.3kDCAXe(@fȑy_|a[ 0!G*o[2 0 ʝ['s% >G`YL Z9Aom &c> mT*-V=`_<`Mv9"wΩ{Tـ3 ssDQ<Pm&5Kghˋ6! HtXP%~e@i6DWG@8A|lS;d #@{W7 UktRy *d`7`){t#/߉*ϭ jRl\)=. f\Q[h] 7XBQX+(0V_ @گD@޹;҃|[G~DF0.!}PQ 4fwxp#A(A*`L:LB)㍺a4.& tr_ @m\Wb _`xr5javR][5 vBsZ$݀APY^ IWI{^<bG`tui`BhGl0C`~'S=[:Vh(k=H b,Nn@ٸ(AnX=>= }[r9{>r(/:|h6cl:,w~ dؙxk]H- @6CvK4oT/rf'S,_<3o=߿փP0Bק oT^_?іVh@{-lOggS`3v.Ti>OJˠ,oOvn'b:SDk޽M$00o&>΁K>YG@s}Ɔa}`4D}=& ȼn S&?n?CVPQBm%lHpn~ ^F:7 'D\1WR~*Gª` =~zW,6iZ:;rӮc/>y_퍏.>'[> lrĴTABƋʼ< L9h Kqs@zE>J{0^zd?S—" `Ã'2Sx.PS˲İ(`L/3`뿹+4>H^ju'r[I\F"ğ+=Z!f/V?_6@nox@4U-dO*T窅vtө9٦~b'ziֵ9rM eR *ͷ+ipicgz[us1HY[Q7":Cߟғv_ |7{݀ x.O|}+? K^Z\'m.D`  Cz&E >(,Ysܷp\:_! N+ DY{Zl͒0%0ܼhLu苧@~K:lk n5g Yw)ˤ?>V4PdmZ[<up0@P%=X7 YW @_dd['ˡ-hJ)Դ:Y] /auTT]6 ȋ)~[o:5oDO]o[wD~ G7N9 7 j`* v9^;d9csS@z ^gN@ѫTEg^ZfI3\Y1Rk8jQ-t^~mqwH>SP:m&wSl:s>d(ƠgD4&p9e߆M{}Z)'޶~1`< %;a?~4]DPK 8\ NBx, +dAڒtӍSwk s~P[[߭CR\@@+_ wEJFdž-~9eğjO|w,b[\ T;/C@ }PJxbB9ހ =O>ͮC}6?7K^;wPw@ x¸|rpa x:P{nFfN{͸^p/w[P n=j$(*om!@TiYM9OVX6⠻ nz/-$h)6] A"dOCu > ljC%Tn|_LjAӵ^!" @p 4ۭeFodÿ``^l~_)@=BR@T:r}} ~9eWcOZ &VcNoB˕[1_-_)vc ~Cx=pؗܜM\Fxق [)Ox2JwI&]ڦ֐m ٙZ7;& x f׈T19ʟ {=M6|\ś[WR@/Ɓc) & 73&\'sѡ{u:{K2_7@P,dG @(=; PENAl vz-->}܉# So5 |_0 />1LcǮ̫w=*m @UY-OBd^:o'sַO!8Zʕ]brǟ#-_`s46o1 7h~ӔBRglvs?z }r;(B|zXS H<ښӓfe 9isHցuu@ϟvj>GOoszyP kZH0L=^R -@^:MODOZg_ bm3kͣ@)yH{6\~r!8tT%yo;_Ty(@Gd]l -xo94C.K!{ hm 7v?ZS  ٿCLh9Cc 01n6 *UHh,)5LMkو6XŬgbLuC'ψW7( / LA A_ٗwb#jcpY<}( l(=@^* [6sK 3&Nn5mƧ"6z 9ؕL_ٟJXq 6`ԿO*/T|4`(OggSY`3wL9{Zt$hXoʕ#1w FP{4mu \ L! ;au" /}͜! p ۷=R|/p6:(@ u>gw{2}c_6@ @˧e z4&@ .t(kG`2eb5_Q #y kEa?F7`{h7plP 0~ٴn pyv10`:2C{H WB~0D.x谴^Kɛٟ~eo p.nM$ =u47 ։;d$. +A*M0X'+yʕ<8[Cp6K"0.ׇ+* T!8$Ɔ_ُ5{nmy @ f;N<_r JPzl*fuZPi=jw}Mxe& m'L^@'j0l,5@;lI U!dž5֙KrDv!1++Uqg"a >(ijTAhn#CJ (KA@[l9gsk@qeqXq ?bspUq e`:p5@F%Qճ钐' P( k\!P/NKo?il({eP:20<6@OǷJV,RӭOgVE@LKNy$%A.䞁աӘJv357nvz|BgȖjhv;h#FBa@^Dh)lbտ ~|`6HA{FH뷏0A A T5"KDZ/\/D5)|tW̮|͞U2 SWҲ3/O`yn<~y[v;>3I^sj8?@=Shqkz6 8/H@B=jh  -`|%J F>xy?Ef,v0PS o`!$ttlG׈/c=7oix Ni[V u]8}23hmC& (|P [͘?1n F~<@;05 3>o@ X膖 {Yc  8%(@0 ߈rF4AC3c84?X пA\_ ߐMA"H[[LO|B˧/w]eqC;L)m|PL;x)\dn7{2r<~:ﷇ O`~E A0@Uh-n;VrD@-  GSh7V~< <@ W#>G\?m0bVSAB$ݍkoڰ$FCՑfAXgN} ̟B  (h`Z̉80x=}$n 7{?t Dԝ5u_ `lY-rȩo=w,1 Ȁ 54cyҚIa@8;" to\L|Y98Vܿ 0ES:#W !AP,;0;ӱ_tdt{vc΄ d x,%cR-@M=,]~KɖUo_ts{v6?g 4@6<WAfkL+_@?j_/ wW1WcO@ 5j7>"<:-< f됬0r"EGk`( U"Lo\eINw>{Je({DLB!! b}<>( >Q'7\C}W0 o_اL/o4> O2@[`0 } hc:bxcqv %6* A"7ssqS%~@"zCc޴X@^WD@u""zYGjr=âݧvAmѡ|uVr΋VWJzA@h3&hWzbP}>2e]2v8'8 4࿈rZ|TV7Y)=x6|;b-kV9L? dCA 4l;~X-.ɋJvZi3+ J{B"JAbrvOx *6@=_?Ow߹V3_9'  +@`y$ TIkR byݓc?Jt2!l082@^=$DHI֝i$ҷkv12❿6[| 5r񶌻Xs5{DZŠvDr xԤ^3*zz۟ P3+C5Wb0s#l2s+Ak $k} {ci׽㝥{@) @6OggS`3x-~2&sG$ ̕QC<~y+z؃P`́E4րYU@t돬:pzgo ䷟ +m ҖJy\(|Gӿ| JE ~M4oezZ&JĘtrZ0$R s"UR JU>]~kw͘6䟆=HKvHGe(%)I#|Ü#A#] 5GVCEBֈ3`eQAA!K}r,d^AXׂ @NN۝xy<.i Ca>|L}4w:g x O}B)T`y-fi3 $9@> \a͠S>tәI851aL: 2=zڲ&>0_^ـ"F6ƋѪѠ|A6\9^Ȭڶ]yI en r 嫀 @)y LH_ = }]@V_si,( l(PUd 32Q~#KX!%D~Ox:P6 &nfc3&(R&WB A W.dvN:ltxPd{[~87<*`ibZ>;9,ėpRN J=HTk{9DqZY2t֟A辔0&q# H<UB~`7gb)Ac +"3RYz'Ӛahߖ4>ί-N ,e.Kv`L~; -)% 5 3Stui!7`>tE1tR pju   8e_m"ی/V?Ȩ;͘6*42#6 d $N_ <4k6(hH 5YU!|*^[[wZ?R'_7߁6Ci%? :rT ﯷ3i[L|Jc9>-8`&tP sx5xp *$?DT OIJ^O5' -#K\,FgB3}\n6i$( Ȧ@;Ò:װo?|߀w (Ack*ɵXYC߫s q]sD6J[Ii?ST,(-Ud` jlXr ` !0u~Ap@+ `In4É2 ; 44B|(5] J˶ -A`y`&o@$|7 Z5RUc)1\Fh7" Wr(P{o?uL^'3/:Pa  7C_ú44*T`+۴nb'E&2]/..i,d4KX7@z>e xQvlAt6*rA05YUU!izPM\'? |CVt`P@0h+[~VTI*Tӏ޼=z7_8& f %ۏ>߈ xb)Cs܄aG +ߋ~W%'0@ ZJ[9=W?|?S/X= :Z< @O\_>@Ρ`3ڽr8\8l<>}L xs} l9 H X Ph_YdB%J|#H`kZՆ/c%hB6(6{A2(t?qcԑ%5)<Ԝjjyd3~qX#$u@  A[oEٜPm=g@^ y r(a2I:ήw>7ѻG}d^9桗@uG#>2TB|S G!BeY-4(#lQ~]\LAptnDWbEKjB3 N7D̍7A_@,H ZPU U+M;4=P?im33 /xᯀۂYu6xBm@ηVf 3F>~}} (@ T*X(p "K @8qtɨ9FNeG P?#nb`dV.{8sp7\$@tjxUU!AhND9jxΓcYx!WqaGN2c!gȀsЗyB?W/hevsʅ P;o0ou`< _.H"2-'#+|I0z(-tpLhحDl 4f 4a)HE#7OH78ДÞ Gw0=JqkBP51$ߚu'qqzήt -MD Ѥwy=hЎV |ȯwۑւ7i߶ 7 }@x\zf Cz(_ @mYF4sntV2ˁ٨-`LSDrV '@aw ,B=ynpK1qXbb}L0`"WUaQtyW~i<)y:43>lʹٲz7ad  :?uw| '03<g9ժ0/ I)g,SP20K?paE1gRE uozi,_o*%$&‰c.`ۭ8QnXg 9jAIPݼڪ }7OvU-6_/h K>P@_ꡫvmCeC`*B ؔoP'q,:93P!Ibk``{8軀]Xy(R_I߽ Wdw)%<ΜF.0n9O߰6>?P6(OF5ʐ퍞<0X@%J&F 9W ?c8Ի5oZ̷ <Y&ۑ2kf>=<g1(LD\xOwbzmQ~k {~ ` e{~סS/ |5"rB ېa3Wh^o(cͶ}`Ixqj@lQˇ\fkZ=$ol!uU`T鐸YPu4E7 ^6-FlD ~ػȡu4 ?vmD& @teŠRցa}<EJ@] \0,v&ƂCΕ2@)an2xJ r䰏ַ˗Lmxv;#w? c6@_(\wEq ;yj-䐁XZR[덛 w}_xb}q<, XX @b t~tJe1q QN}(v5]C%Mvӟ0'#ؔNF!M4?#^CP۝a'v}\Ͽ̛/Bquv`]z{7.&O=i+h2~I*f @AڤVßtͶ~{dpMbɥ׈Ԟ{¥ܘ'{\&2 e|dpr $t+..dt})=Uo=Q$ T< _p*8Z9b*3Ɏ2mt9p $eL'kdpJ3'b"%xɆ\U -kaq9^9J>ls_tk >!+@ %LK\far!ys4.@Kc:9o_O ?EunV 5* ꎝ$-~]c;|%lsj YaZxHa !83~R9byaR G|lx* Nm`+cZ {Cn@PoP׀aUUE@z0weG$Y uΞ"%ҮDΦ%p^7wp} -C~~>^ywAmo<[~)Xj%|뉠n ,+2oEN [c ےCv}ܪRII<2R(sʥ$A3YUK }@ؔB90M #dGD CQ Ęo9/PUA 0N>mFwY;b|?+АECe\P2 mʸ WO`Lg4P`z1gs2:z?7 ͪ 0twi党@"_XK v$! AnwQ Iqb5jfY/daᛞρ~d CG`S H",ڭcmy\ BSbL_@"%/C*QJl.1TqWYOx@~=_۴Tzk5yo}1r[/XGjUXUr *Y6IzTeYغesR2J;5P'!)]=f׉*ujݭo"i[48F 6 /Ta8uxl4x4P jjUU!@(/Tb8T;=5G )74Cr{^NiK:20]ۣ;wܯs ۍA{F~Pȗa)p|`Xz@O.l/Ia| .ͭ!oR3ϓI |]5 ؀dʿa9^Z'/XZ8f=L5"moi%5iZ!=:cή ]Q"Cyg@3xȏ8qN17n q;6TadZx $2[ܹ@dβ{>uvWߔ3w" Ivis͙A}ęYIs]v& 9>p7nB4 f҆ ^#H_7:{gt&h* UU }c{goݰ U\ݏ~ra~ ^/J3ir|>t4=6Hցˢݎ>_ o.TXjo 0nm&/`3!f]x5!_Pu%CN(oY\ hҚk kF0Ǥ)h75.q |`Amf5"2`<0?`I30X U A?oh{qN -!fz2т@G( {a}-휇D~~|MoM˂qya^ h>ȥ$)L-McO4u ku_yu`0~ Gzd7:ښ&t1^@h lju;p|!k _n@0=*Ll4^U!>{O³C_ ui@ڑ7y2uR\z]oj OL(}]<2:w8s<` 0xX\i P2O`Wd>繵ۏ916҄x/3?}{dzr9_hRXi Y:1Z& ^I,E%nj MdL_:c 5P(A4SUUE 4NY;tô}KbOvO#CPrCJ~b `Qr005p7|ypV(n.@La;ԙۭ7PS-ס*JWo ߛgб8lԹ֍&wg>Ȱ;:vDO_cwUniAsїFBh ޷@TpWJ۲e<%] zF >8:9KH4:#BUU B.єMȂ款L}<_NJXM珗zz eq^wLPTTyn@Z rECc `{9IP~my#?Թϑ܁+Tcbc0sc1ٌ76 OggS`3z,ܻ    r2d '=~UaMSoH]\;Jq? )Ҁh1쪪 e?ӛ^ptiC Al?+p k0Χ ="ʿOA4-N?T@)_qGR>HrUU]!5^7S;nxۼR (@sBsS3 3@F湏u۠V~=0ϔ<5JpL&`:>Ìi\̭/e2U$2$_2dsų=D &{N7s `(1 5k6t!Sg~ '1',aބ*K;O>~`U6@RԩBp*e~}|}/yB:*| %H?܌-g/4_l( U&K_@j:d t W7JV:d Ӿ@^>_1v_%ʿveGjXG2NN+{{!狘ur;b<ؗuq Dh`P~7h'M}MR\ス|<΁ q4Y:8k,׉p1r>]en4(QrHߵ C|oo@sCy} .Uҁd% Ƞ ``v&84I6ͫ  )n% M튓>ELwIaV^#y$cc59|U"Č كi@SO }LCJ8OYRVcoCMs^@R# tݡ`˜3k} k?s02{`?T~t( F{EV%L3)).@ *0pUR T{023Xm^~MpkTc6Ra0]~׳fo 2^CclBPdvmYWVfA~ۇb E 䪪B*Tz-~J!<޾DMvH7Tߧc|=P!4x|Ci~?{q@2949]@oG;ˇH7 sY&0`.TZ`; ©(:$YlH$r ծݼ(ձA|m9 d2izR7L| yу|\Pz% DԔBrke6EQJp7wBx@z"o,A(OR',HvIS6gK<:p̢kp?`p.d^M(pVVd";32G٭>ٗTbu;+ϧL:e!3P^J`0&yKa2 f+xƉ0(RA*zNsI%݊QӦ곥U;Z [tOQ= hD3av+%ô[ z[w(7 نӄg_> c 3&A̩ѵdG iv²уx~ʗ*ŋTĐ~ `Wz(Q,ہ&.2JQ{KiOXN !5ȼyv*%E%TUEa$f<>0?U/5`ZyAf|0@ s)_i`"ɷn>`^ʬznm2g d^@ʁlvpz]UEg+/ `DZlújZy,n0ehj-Ukr"pDŽ 6-?6G4?&r7WcZʅ727w`[@PFJ* r4X|H٦O7kipap#h+D1(w1 !3Ƿz< a8N~5;y>w?&PP4< 8PM? x\p_UڶLJ>oA؀n2(iqv-y8֚%M\aVB!{ `n%j ct7CUUלYћNY~tyOū>|@ά/xw! ˿z|ZorL>o>֋,߰X zmnz!`y e:6H&d2CQ671 n#@v ٴp`.G͡~igҚ}SJXUp@ܿBHk8v-o60$sM)X@2cBTU;ys{~{pRN4ådQJ[نm`+p'A Oi/ͦjQ9C@!37תnHh ,hY̐ ҙ;UE\m@FcU}Z^]~0)mXShBB$9(iV6m@.(wv[d"1\{o)@;8+bKv媪 !b O濒΃a+sϴ{z L&3Ϧ|?.nǜ4o` ߾]7c?'nm;@tr*-\=,) 'nQhGȩ1ĝvmi󿛬Jz \O[3hUsHGZ]>g2 pޅ9?puL1ewC%d*N7YdўR(Cg.5~1 CD1j*k*I֫*/j_~^&ߦV\t\(=͏?׾1֏C(yꐧ ɴݿ;.EuEϟas0rHƟtJOLX QyMM$t۶2 _`-p]!tI@%-Aqoi6ۭqrّ,suM0OggSY`3{v    d@dEaXlþS2߈iY[[AW o (1*5@$LCUUqnRkNY;.riv81>طO\,OYD *jע)BgzdުC,PΖ`l= @  .8P$,8lU4z Qр}_=R?Bf6@^cRCA밥1!c~!_#I?P| `UU!$K$m`>lf$<~Xdyh؉ a%ܯ[,ܹ>A[lg:pgN3ҡw%)=TnոO @S51l!s{: f(F4ޞ]鍠tdYll^<o l b]@+=* ӹZ Es fz~Ŋg~Z< t7 do%04H9 }Xv2 w`Cd,Jl1mIWn 2x'@%+gETRe{S)c HQ`xe,ާE<7Xui΋ 0 ^{X ((>P'{w(O'hͫ:Jl`j_{R]ȺBpΟ@ /d8ƭ6j;qsٜ1c;{$ ?JGk/֐F(d 4o 悉wO)ߊA#ͅNzgu(QfR⪪"!anM{nX^şL tTyg/HR xgfJ1=Mn9Czo?%ot,S1*޺6 V a @=*?~k / ysMލ@*\7lgl$ރkzh6z\|s+ː0t5o:(S2'w yn*}xCo-d A(*hW`-v;i`,Ed\NyPP{Jfy&n$PZ?|@O_p'_2C;F'@_@:ܾ0dbjf>ӫ;Pc 0=Hiѿ`WR) ԰ov_(:G ǯys{Q5f<<2&UrT  0 hx8(.d r[,֚sQ5~.E2CNPoEp2wJa)RBOD< ^ g(IGUUUsӚKY^WӸ rl5ks1Lr&O@Xp7Kc~EЁ2n@(=>#;=F?hLqK]Ú 2B,~7tsM͜?:sv=!l !~+̖S29` y[<?6_ne!7 y+|.(5v;@DI=.WhtzcGix7c= Q0 ]0LIO4[B9C&ȶ[$KH 6Uz^SRvַ"-pV.IoEJF܀H{9*`$MPUU!4RbV(7;h /{:9O+z Kd<tn'^;:l:T~vn UJ@,~,$U33>;ӯ$LwBOLYOߛ/L<4Y}ƾ>w+8ԖPGhޗd_FH&KJb8)poHGΡ7@FԈYBUU5&axx"5h}'_`鱕$n\(ZGK( >! 8J(a\ e (*@O ΁[k{spL'O50zX;q?r`A +%`x wr"\2 %[2ro+=Qb~_K^S]OzWZSl@dʏb5V |qF{Py Fds-E ]p*ЂXsFFRg~SA2cR tL:[/pN(Tcϧ3ٸ۸P `&ʬhYi =NXͧ}MEާHx*T }M?H&ɺ?ABKХhN_@-^v׳|EVlOggS`3|m    pu=lݠ6ޗd qZ61XR#o|`o%o3#C<تf9;&g[7zaf<1 å@Dxvm >fq 0Z[>5[=ܜ > P ,3TT42.: 3S CxPLV]\a[_ eƂFGe aw]h(‰~.رs3Fv;#|(_EX2(nsIlQoi ZR Ժ ~ |`S<9%, 'a߶_~ \kI螬,U@rSLO@H! `>l~lP̀"@ zTcS+.\dS1 Ad/ %@sIjQ$%cjC"n0ajΈ&RX81"5ٹS2=ReCϿQ>Y9k9‰o _]=C`Q(H:/*'TYὢ|ɢ&k\ť(-ۛ;366<#Q mK OV,حBmLւ#W!q7;1vuۆy{G.jM4~oM HK ҇!D 7=*L Vش홫m@ 5K?hSg~B0;ܼH߇qs'袕;.+kMN3PE5PfG5CN&_[uAUF/~%a!cn؀S@(nvןVZnחD{$R} \N dAa @ʷ h7ި%YH_(PUU@l͞؊ 6/2ld2(ԇK]$+qw<՞ uk,Zk݂z_4lQkϴow` VUi I&rM=(nlי8h:C;Iak+sC,d?210JGS?> -c#0|Y_-sl}yO#njvaլ+ @o nKRbIC 9PAlXڸ?*MS"0e3S{a񾡩 \{ 3 %S ЇԼgjp`9xfېgo{[P6P+G㍽pѐI 9mSLd_k*?޵%eK=N*T@, apY4[f6o6A(u K6!GC?)6λր/%i0._@Ɣ*މq}Bh`xd NP@1T0TUUF2B5M3{hN3%s= O+ @y0!D_xayU__L/{fmsl2x~'? \X_0j0zZɧ`.$LbՆ𺒙޽@g8Hhi`Svq5i:s`;@o;jX9PiLw_DfHP[PwqF*1t(+z<\WљqhTw!X3R̰-n_R'7~5PE [O<>}qr)%^XעZ*Aype~L}=]^ʮTPdң7`6Ch^LMe4S U\ y%M'Tg6CA>*m}`}6AvXjP& zX-l5}gՔҹ3t{7Qxiωyit 8p~oŸ3P~+F W(r]"쯑!|~%M'UBZtMYwҶ@z#+nO~ﷱ m1nV?YڟW oXBϫ݂7(:쩾hox\ۆ̤>W֌rKjgXy||s) IRZ&tK i,^EGVd @ _Ŋf"\RZI,&uA%& ލ%MGQPUU'ދ)y|҇G60Mq f]/;K臁] G*_6Npzm/QIo- /y'] BQ-/bK`@TU]۬?i2Mkse_U0!z⸊&@ke  dtďHEd po \-1/iBa?+/S Ϗ'w7 6ɽ q!UΏ$9<$s n)O<=zM`.s `I02I~kӺݽCizaB#obNkA:5G93@!` ; ]!3eīm+{d7t3_Mvuyl{f*^q;3b߅=$Fΐ&wjaۅ_<,vs(:(~{3sS"1䓉/|q Q lb֎oW\^!]ߎ?~iJ)xr'|}}m쾀^;?r.n(?YkGUקIĂUo dHB|Iz v{{@ur0-B*61jك$u&{~LK[ 0^oX6B%K1(1qOYxSUU0obĹom>`#O6JR~cg\8K8݇ /ކL] g07Y>?}C6Kt—q|cKuKHJ! ,HxeJBQW}yKU/%ϮlZ,8<]nHV,ԙ7L#іZwlϠ~oŚm3E tePEt"/c9tRS UUvZNmwּ\ wv uYju`Y-pG-3]R2`ႂXNwz' n%}USٿ8V |6ڕ]>T:S~k&JƀA6;~No(r,=%#\' 2-0H%sD<* )>_E36Ж0p $]v}\MbrtBUkI ]Xîق?f5ߵ Sm31~ŝr3o {" u@pyy6(} hfCU~d1Kr z%1R.M=<ÀiUri9s}ipl°ҲDނGrCz [7:a/`N; m&^dx&%K`Mp5,4! UU!~6dX7* Ul )uF* ӏە7<Jp\ok@}/Zۗ 2hUG`sS;_p0h`քl ~;^} OSI10T{d 3 8i*LR` QE܀m*oNjC޹F8h\ =d<:EA^)2hG([>dacpkƥ%KLaE+*rh q ;FgiBuCQ=yvA =z8,>}܇σDrrO 6m Uj,4`:3g`TߛÐO7SPWtfa(9̇a>7Kr7x0I_E-eYir}ؔ 4mZmW싊ey3*>W?F8z좇ݫLZkAحuH6@>Ŋ1B~)ɜͰBO ed&M ADj(`4f\껻n/#NN _/nlGҎ>:o80̇5 u6sz `z:sj*ʪ`dN"sGΦzyq7X %̶]d#AV}_WLP[@V۾ aZ6=rv>-MZ ([4c6r%$r0Ld:. 9"w5C#ObCN FqL==-?a~|4׿  C;=n搓Y9yݰ) f,'_Kq:;a1#b ᙢ+8P)kd%B E0PZnE[$h9̹ V qc3 {8dܷb=h̴ V>4t_DW2`023KBb8Nߝi4mKA.ޜۦo??I1 b< pȾYF߾t˗& k ,;d 5*JrxTjʂi__e:mUuޣ%osR6 h!)\^2BhPeA8 /y>9}WR&;p ޖŨ1R~){ hM`_LXdoݠ#*!j `)oJ clX)a{sW?%`=g9xؠmΙz⿟0\r}dnOBsv~>co¡0^̺'I(O.S,wz-{/!݅ 4Do%1 bYRZߊ!CIp K|QSUUU@sStw麱&_4]>ާV HpUmOg<t3Մ]]hHq64pccV6&6~Z<f9rz4/+ŷ$3Řjӊ~< ևYbQ8TUU`W/c[x k?)ˎR]r^dn5v0P'N7AFsrm6MtT=pYo:\0{yr`ZueOl`dV+K7Vmq9Śj-K2>kZ Cʖ;e%vTxk?AMۄmӌQAL3 /Ÿ3"֒V9kCӰهN=BSbv(%sR]*V3_BX١Si[۩Ǧַ}͍̓r<y C|l_8Po˛'*:\٩Ч?{?}RaGǙcۗS|V t8<~}4( *$W肟F- # M-ن[~jnF4 .[3\Bn8yU7p/b쮷8RXA foŨ3 >%Xu\F)[}6 YMWBBUUz bct? u-rF`ORJ倣qi]Zpk@7_8`?}Ӏ/7 4F>:6i΢ Ґ${~Hx&'<:Or|pkog4`I|~$I1m9ՈA`a|e72d8yf/7&Z[-|$$df`2fdoňeI)Vlh}Mg*:K*@08!4\:7Cs&ԁq: v--jtG歖ߊOGHUޕtUUYÚd'sIΐm_qkޛH7 a[ $y;+e9Dw[ d:!*n,F2]q}) hnQZ5ʷblɸAʒTl(>P$ʩPCu2J4TUBB@Kc8vq`\Bz 6dZv__>HrLj^q39ux>@o?h|J34ώrLM*~ Ul>IR]n᪤s7?yI¹M3 S/0$dfYFkD5N. |= jьT5EIfo'XMFE` ~b ]tܷbc5P<Өهu֢" @>&MNQ_Muw!aj A.pC ^/#7qaʙ`Zdht :`sXS=7Ԥy??y}YoĜQC;=g>{x=(vj`XOcb(FJQA`L|xXhe@ bLF4TE"b;^#gU-x<x`06omiީKw o@2-/1ᬙN衚BodA8o$ LNOscׯÏwc87j "UBoE-џG3&s_A\]Mo36ɀfcޕn%V$[qP8ʩPS8Τ*""Z37u SN'S9(*ǹ9<@`[eϧցf ^ÿtԆ+kji]f{.& : J:ta97W@yIJH UKm#Y.c S8%@kXzg%ŗoEL>7 ӭc㚶6+Q# 3ZA ޕd"ŊꐣLHhzc n B=he>2-Us禱D{6D\_/wnMRcL֪WTB kEY!9F`(3US)ѐ.2.] [_C.h)Qf` P;^9a0. Qd/ ԎF  bxG:J\mw56uv+o'S5g*GGQ= t>u9۱CM}9Do -^Fh:/jjҷrTؔ`QCCRXŊdCSށΝz50/ .xZ 5鞪A[NAfO?s߮DdoM{|bF[.9|z ~|J'Oy"GU5QkUo󦨦 rmu&!@ns o=0jh22?^OGuIw'N4k΀RTЫ"0d d`e[Q2&Db)u ~ 3psN\nfBbә[Cy0ɚN{=F{Gs~<{ȪiU<|}];Va(T.2rMVن -92[C`bʒ#zs,0hjA~XԌ2 UUUOoư! q~=|< X0M 9`3xS44wnXHc7?uDfnbi]xH5-8>ٻszwvxNNU盙U@SȰI4ZgFl/ dns 4|ᜅs]Gn6aP4"N5lN*aUUErwy)<3yhK#kho_qj OLP $o¾C1@2IkjɢB*8M<% Y#54pqtv3]U^c@)ѺIBhb4 l->mnF 7cEI jޤ聧(2x1=r{,F9㦩y:z&l!2\yF^"_Yyϱ_ͼ6XXƎ뗱\_Jncɶ-'i߂qmHA>J0 4c7[ _ ˔%gxx'Ⴧ<"^#১9Yff*wE)?=S}k. uhT4\w *9~K8? >yvsjհӮfrRe幒j65Jx=ϜJOJҦShOsyh{!V)ſvvd@26?_K#c`Xo=+:`gt`2JIfks~3 ~-'\Yr۟Cz[jS`b&77?5ؖB'5 VUUE(&!n8j@L9?$Sw/P2@-3?R߀23 v1cߝӐ= T3͓PΞ6u׿?bv?19)CMM|Zg&uJRI[Þ[RWUOߝ مr!Z*&/e,P6d] ~R%c#%W!v_~s&+3e8w92 RlC%o1G`;aF,0~\#?gV?3?Luv"q>|!lԼ1ަ~#13יIgF C"}i';ywX6d+7 Hց2LkgZ; )OFyaSTyYFY4_zZ\!i8g' ixh=[=xpa&5>g_y<#3W׍MJXIe$/:=_޻kq~x⾾mݸN8"@@P%ޕ+gXF 5t%wtN&22=΀_Ӭd)*$&bk|cyvWh\q@ l |]w[5ݟ7U(>L?93O Q5u9%W ] ,*>p\Ӛ7VY_Hma |>վ;oxuMvZ2Z. {Gs 4찃 g`e\Ǭ8t%wx~62\Y8o2ǀbaKFQ0TUUE] v'k޲d!S&[sLO huP-ȉ `7 O\{YdV>>?wjw.$|jY(\Of!gy]ݮlwvm(׸sTTPF_ YḟȖ-wE⬹#kq~u#UV09mǻLW"PMhQm\˭%A7 CONUUU4"g]9Y }N9Pa2VdO9?s`ؐo94O=O} stzF;+U$5d@4=U 9?*T}/ƧG_ܱJi:SUW"{mK0k/@ڿ|R꺹`1h7UrIr FWXRx:aɻ$:?J.;4`=("ꐍ{L8iuf08-́&z?>\S7pLHC wk,(;t35p&o:AEl9}Y5f_י&q}OXUdIsξMUޔɴ}7yL2 aużN,$6HgX閸kh\h}gUm l vV 1pr ޕ3b@WЍ@2\s%a=3B5Qr*ON?T E@yUDO^h\zM҈b61K^=]~N2O7u;.jf_ora!3&gQ'l5y** 鮁hWx3K-LG뷞$=޲SOq\Q}2š)#n4L{[ׇBk@`hΙa!WW/ig>VE ̌2 UUUQ/<|ZAן\afAS 9 <ǎj\MeG'wJLCuݝh_Ւ5Z넺> yI~bY)le:#l;9pziTsPlt 8a<|Γ;g~Nn>n{O~z}zkK ůȯ5!B?zZ,eW]{Ӻvw:Rv`*ЏLOggS`3PQ    qhjv&'|+r/dXJ ^0g45R媪 &Ϛ*4'x0o,h17/=e*>g„E> U<Ű˳)czi"녘OM6Qu)'+q5=߫ϑ㉫ @nH!A ߟfpbZy ۛܝC|g^jrzͼ/ƥɥ>.@3dҍudrn|}ͻ˿l `ʙi#~&Nx~S^\=k c' pc\ sOj8]@Cc>.lUVbM$Z৊ Z040|˄鬈.=ׯ#eU `nay 4%RUU!v)¸!WEZvy=j-zyGAo|ic{zw/a(?h8M&l4_,-r.'ヨs<- ![/~{+ -_WYP~5r-,cx6;e@~Áf?1mOz.y~b}»A4`Q|.%q 9|x#XS#,!{s}[^rIҷxw\2okM5kőj&g3=y4_C>%r 9qTw> oztӵD3_xTB3hy#pկP~Ir*>g)9͑+Uުz/Wm/v:,+Z=Ԏi^"Bw1LhUcdrXg;[ۚobb[024 |&6-J uT 9̯^O-I'mh29{}|$}:N^K$@YHu7>y15a4L1{ެb7ξx3κK͞%a3ӳgWqfφ;g`:$b0{y`iqu ы,'G}~2eGEɗIm? XOFG/ (p0Ȣ4f|&crK]pv$YS'X=3K& !p?rww40n3+6E _gz)u{F 0`,_:I4x==mrajWI]'gÙ<vdt}J*}g1t33^?QWyʮ"O30(+# i:[eI6,,V۶+d=H+NOK?G<{'mۡ~艬OOfHlخnj V|QBNd]03@2o\AldbUUUﭜ ƋB%]Srm{9ܴۗ5sk>>Le0u?U5US9ey oMQWwqءzU/է[_!S5 IL5g_nڴ f윜j?IsFSZ|r^صH{_ (9!6s\i k f|ǝ%9|:{%55xtffdH*@b0 &{o70'tΞ{L8ڻ>$@V[?9tx}BGur=*x2!=\59dމ4ʒ>]O:m.W FU,Xw.7ȯl s^{oN%̥OY<#zC'u]E3 YMV& 5:2`LqaHmo|.a-(a~Y |%3NPUcg7/o PgKg-~|h@vYձqV@м8=C_!}Hգkvzɵ ݇jg'xƻE*iw:RS!6gtGh,CZy6o795]Ow\AUr9Y |<䲋<Ιyzo߻t4nb]T56;<}l(ᚩcdI%&Y̝G23Ǔ6y#8scdj;,I$2^[cO>WW;Ѹ+[oAlj\EBC3OggS`3[P   !4nvBA:/ӯ`` cɏ2w(TH:TUUU5m?s[Z˭&/r{W&RU:a'Ÿ`pw|Ω~]ègHB~9n83}o YNy{׼ws>5[StK{zz潙IW T&JZ%cƘZ&ƙZTQv;~VEξ엒%Fd( P% pȐi>!]A_{M0֡33HUUU!,g\>JO{7FVW*J> |MlMgPYc2*O]:=0KdOM >JRfQ5UgyE*敫I$ˁ!߭o]Ôzg0}/*5_Q^Y>ڙb!aTlV$ZШ`0Ch`(|.'4H1{m0qozffFrUUH8ӌe۾_Y.|%rwg5ry=YUX&QFU Ǖuc<{N8ߺIgw6-`ag& \vu⺀^R~ zvoݠ|lm)fάS4@M|ܓ'ÚV5[cl5[Dվc w|0;d>f|ơ{ @G$XG *[=h{ek 2{_u!ꗯ 5}_6> dP[T=3KC5+ k~Btfrh x8L%B_ř _I}ʣbWC7vrq|_`"gN$V7W'B#J\@C;4A |W՘AJ>(?GHUUUp?VF+ߗn'y>%m?FŸ㼽r83^xbX׷c%)|JPtIهuN~'9g9nʤu29}O50=MCg3nUrH=T՝&']JԞ SO+-[duuW+uZ(Z]<=|7x8]|Hun~Fո6ر[WW:Zh~hAem7a0?`pW%8%*޽;ٜLUw|8ν[ <\Z_ӧr9;f%T̹E=;dNW1bz*/םCۚ?x3o*'䮞\ y>ʘfߟsfU:7vhdT#ْO3hEzwlYNám3E:?Y:ԇc; Lbp> 4 H" ޕ |C*=9)鏹`mnK~I9_y{xbǿ9Z̥-?O\#6o!|:enfhvM/׬yS4DsM3U<33CNq54pFIkڝa3d{G5֔]l[/=l@:}Y;}Am6{#InSHix#.?HZNjp4mڮCi m eo;ƒk, ؓQ\UUc]6]{[@'9:y[}=`Me5dA^*-~A6EX0Y{OZ54OѮ?tzzKmܙTЖ;eΏb6eQD1iXd>УH+8/e3X:hdϒ)UUUlsC;?[]jL 2Lڗ j=.s׫W I&-T"jcس('N՞ʳ{3L^3OTnadbqƉ}z}Ww̝{\sdٚԮM\+j`!/zIԆ)cͪ^tTxy>`6Po,N=DV"mqҞ}spuh@f\7n`/_LpIL:PU 0OG-}o\n?Wf~}׷3?W)K'lLYIOAnLOː77ӛd1͙j]dqvg4:PIsQeeN4:fwX)$lIIA*yme_ND!+aaӌ^cRD.!B9ɡȮ 4 ݠ8|\7"@K>8 :Q !PF,K0ƾL~yy/.Rm|+Qg9YC'blo?DoE1k79EͿ0Hj0$s$΁QԹ z1@wg75'!sH?oAu/E7 WlAy'U+3!_Ő79C}nFǒsj` >ƭ8hd|'P|\; kzRijFXUUB?Йgi0缞S |QgOV'?_}1TzNeLBPw($~uboݛJ[h檓e6x6aSкc܊@.ijPG!#|\TGBXyN ^7 |N'5iBy{O*&>Tcuqc<*WMd'Dkg!qAq ýD5u^J3UΧ/KlFuo2G}ִmÖFzP(w՚ہa7ʙOggS `3tĐ  m 9@>|_ |k#'g{f* aeop9[T +o0`YQ?->y܏猷YhՖG꩹(OM9ƾ}lbJ]u-;J֘N򭪢3RfwQSPS38voU~>puZT[IM#UH%һa//%Y]{?^BogKlۺb`ӆɪAwX:CҝCSu.NШjhZ?#P (|mЀ16~xF6h:2"VYXD˙ 餲d/r;ZT^=Lnz/rvtۘgkxx8>7d.'Eqtٳ_ϳ=pntwec2r5UNJ;& ;B @!ޕJ>&!^I1deDTUUU@Y.,{= [w2vn#yz?o'z8/ahy^܍էLA}eK}*F ȸ7Y rv: QϹA0YO%J,\0 ]I,B,},BRӣ⟳0)֯:Ҝ*Ct--Fk" !{N kc@Z>f|\/@c1gBx% gGIPU@qٺ75^K=bXYj3NB8OdeyY8;E aFtw$l$9n=&_>3VNw: [m GG>=Lg\(}!rA/(Q-ÙM,~զ vΰ |EWq`NgjG!WUUY}NJhr^\\ys/NE_[?/g,>=gkޢ0t#N&I^O: eSɛT9sO> kg O0fo/n `f]7j`)h3"Atr>q2"zLJ8Ը__uWU2/?l ó|=ܻNL^_5-9f&m9eMvu&+|WCv0ՇyNfbH/|ؗ/ڟDFȘ4㊒Z'uNt垚:R臼wxL{H=uA ^7`|%S@ XcDtR**TtnAy;p^P_~ɏܾ<:7s53qj= E\u>TON$C}=5S쇩S| סm'syjڛw;;yb:6ݣ,Ǯ׭[|+DT9+P[FϝG\8=^0?rWHSmز6(B! |\ XcۿH$ ޷ .=zF4Jk|;Ftܶ3 aޮ_Oǃ1S0Zfv/\_;Tv`ɬ{gz٤ _U}MZڬ[ؕl6nv #c$V$IDñxq]yx .^wo&p2,QͅL7x.enfi  P),? *\p=(3*VUa9Ϧ,ཌ++]@w=yطc~sݿ_ov=s|3L$:jӆ f%U=j-sV?LY!o|_Ʒ!| `~jpȤ3*֪"$m9DTN)ߌMn;}uU}[p;TY.4yh%"D![}f#m{Jdo9 E /HK 8s&;WaSg,hgޚĀNm3Ezt6+R0JFsjz#Somr!ȄJUkOGmjnGvFˤDnUmSt-$0g i |!>aXTɞ%35UUUA[5MnӳqI;#C$[ޟVf?/<4ja@NN0c1>?y{'#?u&+]YVWμ NDuу[3gs k df ΂ݹd5=U̜f{TΡ)_L.oŷH–-Ѝ$IX+{e^Yd>̵&ֱ?agZM,tj`ũC~p1kg !@ o7ha9~ NJQXUUvOW;߯ o\Q?bngNjJMG42ϫz\{5'E!kvWt2;O}Wc'nS8~lPL@vѐCv 4+8PSp#e5&n#t֛=\]_$+K%=ӷEeyCFkS_Xtg[j}L ?JkdL6;[eTi@|5\Ò{ x !ڗKB-t߃$oο$yٟYZfwo$BoI&.tH_ڕMdh(HZUWWI-]!hOɲ^Ԡm6fg}Hw3=J'mSK>SԕHMyHrwGX{Y~Oֻ.]~pkjotiu=jYB HR5D| P+w0p+84*ᓷg5*5WM05yL|!Χ~֏(.;chfs*Wr#by2ȹ:G})&k6L:Zh2SSLwx7 ~E{]i T&T;QBFGUqm./b8Mu8^ɈJs'~A|n7j5,>o@O*pg4UUUÏz׫'nKu|ٷt!]dԜH" /b W/&_UVTeW>qI*dei wAbG@rP3u˕'{N(>ZRn9)>)sk2P)JS9m9};+LRۑwJgz)JD&c> ׌ai%d;-?OȋH%[|#ۉ)  $uWV"1mC4|&*;@!oZ!tȲ16lh J@e| aٿq"NT6䪪"!wZt@5Ҟ@`H&&}i6sm=o^3o^_3q%?%ԏ۽t@̽a`5TgϿ8׬ղ1d$ڧC!m}?KMu:q׶gw-m ,y>3V'짥;RutQ5T1M.H&"1\ݐ87|l|;@XqL?WgP$ x8i:UUUӫ5}U0y~góoۗԼv_[GƸ'JSU`:4ԯIi=|Zw3K(I4}q__+[ % (Llpv:/ T+n#u5nM"8]sU1*6qp&^mϺUoez2n`\-Uzd&w<@jvfc4$F΀h>:i a9}@8#i:TUUUk$Ć- g^}̋&YC0ߕͼ=|?yF29 Ӿ;YǤTyfNK=y7s؜f\9Cnj|e\%li65 ߀A5=EjmO 7|4p/v(iNlj|e50 $AHL0P64|_:~BACQ!ЇޫnDZ\~s^kԸr~Skd5:Nlꁜa=i g34Cf>n_TF\9t- }pk-$dK[شnɵܮvեMJH*9JNU\u'&M#ùhh$fΎBNŪ4ɹin4f2u|ad[18]̈TUUo$wouj ~x۹J>_{p6/ڌzi ٮ_2=U}'ø2Uf(;_3wAPכp7wpA7}בs6hl߲9=ݵwK7y{U1=~BRW3*\Gn7}_NML #crUdDYf0K:;́d# mh|-ӷL(@~p #8%t$!"sj|zY- 8Þc\6ݬO8XrK}[6lBhs>`e|0\X򹝾= NOڨ(3%*!Wj{4<s0niϺX \/v=oX3Psܶ3sGe3}A:=6񯼨O~ՕO}1k&y" æKu~UYj*a=/_%jjr21H~Sq)XN/H29xMK!G\wpD,Á~1fqLjP|KWO5TUU+p@&͢>=>6baeK~Sei.\')Trqh /5UJ\$C콇{a(q}n6~HƑ嘙QuWNj|&|sNe@NivzK﷕#c ׊|oňtѰX՜.j&?4hrbTl & Q !g>|bXyxM_G'NXUU<KW:Wd@M@vt UQuX dKO.gM×Ǘq`Zл!{=/㮺#cvO Seax豏?+P'{IS×Nwde\Ҙnnsn6s]y'b!`=оno/_1=i]^&Ρ{5.7C'ͣ~ E]ӵJӱ \q?Y<>~k֏W$tV]׭Ñr#?[~MXVӅuoԊVW+rf"98e@`hQh`|-70&$pK> )gfF AfqdI?nM=O~g·[w+ 35C3y3:_-_(!DD]k e>>]o+fyLDNgLQS d,j"A"~{ y9^[3Z_%0ۺޭAٯpj-qg@rU袛L`m Phl| =؎_ 'Tjv3͢/+8gwQ[=5><ϋ͟Ᏹ{>c%x߿5&3ߔ{E?7qn@w?cBK&ӍdC$*dKvّ?!uVcad':գ$ZOY8!YcW+_ݮz'/Z*$;{$;P]m.%h3mȨs 8Bf|Xc;~ ÊvgRUUU\z7[y\ljg_A+9ﳙ8t5$MO;l9}4`êijY*K棵Jgg*Jx(d1WߓFLҌ,(jwzz*oO,)ݕzc/kGD/m1ֹ8@51ABXQǸ:$ e|1&W}72@0īE%UUUE59ubnOϗ'\mR[^gO4뿟2?ɼ=v82\iOaMNvYLǾi.1fX>}3}rYIf7t çΘ{HJٕ%52!~yv:>~YYSz>;ٺ>{=f%[SVnIvZ#*y߉nEp Q>ˢ POggS `3e.  f|\7j`Wy?<#3#: UUxL{9K'o~}_>s+9/u_A;u1?^ۿrs}fڔx(r((te:@IW֡*e }F!k+<}4䠂a&Glkz`FxL}/lj',:BZx]3٘`1UfА>g_7L# >8c +H]}$Fp3p:]:MӪ@IŝEu^ejO}ν~o2_daLwӽ2ao3␕=Y'd'a:svն˼oc\ԷR+hkeT;%4OjʚL\I4(&,d[_9#c?Hig_'+>vv{-\bu7ٛ!@1TstHlM0FQ>f|]/s+bpzR3Jr5TU6y(rҾn :lu$O:;QNjn J\L7>DSLսZk dI uν{Pt6Lt;xx6zꛖA?|6jvj*c0v~4 tC9= Y2Zo0! 9| |Pxe(3(i*w}Jdݻ]?c6y>}?]jޗzxH*V/{XzfWyցn@ŶiEjk<1|eUP)пz~[l/NItgp+*)ʉÑdKW iZIh'z3P,]X,~WoUgw// _1'2t~֝gPـi@| P5,Lo0@X3Lj\UU! Վo㽇.sVՖ <^Soc9[sf)$UnM }ď:K>4pNgwI"~2TSS41PdW&K1 ԖZ $\QĮ8'sd0٭+yHz?ֻ _0Xm_)f*ѡ;SiFJC _RB >f8cf|-087<=34UUUU$&51ތ/'>y9px5r|¥Mמ8a&+yi=;3Ω΄5zܼy9-͡)jآz5"% :}{&>"=3YrrM}bb/eCtq>{z_N[1:e*{5'm_z8Fm7l{c0ƅ}!f|l8 pvBGrUUDs( ?mKڲGQ׌nQrmǣ1tO>yr_A * %JDj(Sfޮdy:y}u^eym@ZĢ뉕hŰ׵#'+NϮjL$W/k4BЙܤRZ67@ld >|]/Е|l7CѓRB*(~l`%TNmWnZˢIV<CǏ9o>9{7Rc${Uq͹ Ά5V40UkW?B8:83O=-k-^;Vs?7l\ם^f+!!A|u^x{=Vx=Řz}@GyepÞ~fִt_8DC0dLG}T@|\%{ AyV5Xãgf&U%TU_^9AktɄW@)>]<Wx?6X悤-8ifEIfx! 7U&fzoOU!ofS=Üv~eU0d|>Xf|\/|xρ ГT**icb}\9i:iq`L˽;pz~S02}EysHuYxޮSdB,y ?c^sψ _yVeT7'סx|9rWE)dQ[#x<鿏Nx86/˝֒g|퍽`:!BLF0jQW| U  f|n a1~!x%EħBT*B 3cpV-?L^1Css|Q>۵=DZi vesuO:yB{&g> u|`5Nu=fFN076mWYw#UiC5.07:w?R D5LqEݳU'NG$IhzV[u}tޫ8m3cT]θnAdQD8"|\7%0J5LFfTUU󟽗e5$MyaӍmsv=5؞Fײk:!_w|{6 =sn:>}s}'H2ggrȄ>M>k+'/n=P4sASvWՕ,pݫBez/G{#J1vx46];f|ma ?J@{Τij*HwI;rGp1ϤHg6XoGq럷̧ᱟ tpdVDh>.ɡvޚGKw%T5XA3䘵_r#0/iW%~}:A,,K _=`Mcl|ѧEY|ۡoKĢo1,4afQ6!>83 UB3JBUU!$jle)4v2:Ym ˄zo{x^Ԝ3__qx6}Oac`~&:f҇LW 혛2|~d{áx޻6<5wn87tWe2ȲZ}K.w5֧wa [6yww~$r.&сj\9 HI3B@!H b |l78 avzxU\{dffRUUU-pV~'a]˵51}vһ}9;C_cn9S4{`f簇S:3iM1dfUࡳ(g @y<]4PNvlK7Y Awƿw9|InIֺC/ٝNv(A}$ne("[QB Sr42OggS `3W   l_7a (cpho(zfό UUU!h{N=d_"0K? N]?O~mj/=>,>/s(yӓlb y65wʾ1d tա3BR5ӭ OT]Y{tMßGq/Ú븞ǥb ?U;N{k8LvmMwiKY 7 M skC >|mIZ2޷ [GfRJUUUUث{-! KS"L{OR͐^瓱_}k\9#9S?jNWdwAOy l΅sì8 k߂C Z#E>1K`+C|{U~ǭG+u{]9ιNF=kwu5ϒ Ap`3_7aIf&OP]CUUv_Mۻ0䷿][u)ԙۧq*\ꤐ+96?鹇Is3\Oyvf2=zQ.,fW_SF\D,v 4P |P2 Sw2~̤PBUUNhm'xP-R71^o}_y-}{[0vΪ|ֽսtmD?W'kTnNffT jϮ9.'P]Ǯ9Dc* D䡵]E57d[i 5_,)2aeax=p1`cFӞߔ r3'cW~.t2&6 P˗’E,0.gff\UUEhҾ^J|!ہ㥨+@*^~;{8vm}^}izfîrr>tRSix7y1KKC{$@Cg99pZ9{.\LCӝ mzÜ+cK’plۖmםv+yc32<斌t $Ӆ/g . . ܐ3f|  luQRF ܞMgy`g:PZ/7ɬ4is?˸ϗa*vQI =Ŝ0& [q1U ɣj*yvffץV;&6_gaޙgsO~I>(ٗ#||}U᱉fiS7/9;!Л \'̈́\Fa@ 0|3> GEFQIUUUU]ɮq~`]<٫m|^K8g^ٟy[OWlydpPOӧZ<0Uutt蚂QOl>7GK}Nkr^W^on4N//OYOזN-IrPlC"`P lb8`v0|IK ī#MUUUNOiN0裸ұ;P8naY3/J9u̙7OHsxyM{~v 0㾺v$BalcΘI2uu2N6`Nvv4~$0 .yT<]Qg}"aY*z^,?N)~x?_^y.M 4e| A3tDUVUU!jז(zcLoQo<Ƒ=rqϸ-$}f{]杼M`6DsL 5m4oVsx:}c쑺>:ȵ _xv5$MdeUm)B *FʂDKYEOk]a<:6:p;9֙DN!eۂ!@|}_ '%NFIg&WUUFDN.䪿۶"89:y<<ϭl1y_ԋT}B]Yv5)M#(g׳u쾎3s*I29TV]Y[t_@U,0VflrO*Yͬݕ]Kr'2e /rrb=6amXkdN=nDotN/PXP .QSdÎF ||o .vG4tD*Rr&ڙ/cr/SC:OT>uǯs8Gv]ÿq^qVToyRg᥮^[W]fДmUU|>TpG?`6{El\="Q@QYk%I`dx$;}e\D"'[9~A騥ՠBǖBR@@|W 8p2KDS#R$a݉NVȦ/Ǫ~cڔ>8 \7k)Nq_ՙ(ƠU%;<~ysB)ߚnבC{ZS$C+꣛y}ɣ^qS"2Y*Q$}px$գ&_um$&5w\i9MvW|K)8e04>ǿLHWyLoI (dF45"`=o51:b/\7ᗣ^?JYڷ[ yN]tA%>=+9p ^:ƿ g% x;oPX8y̌2"$TU!P^>N-=4P; שЦ5^O/:sx}??ԭn{כEe1=8Y:It?s_6.ghx/ï5NccƟ횋SMAOSLڼT l`X’BɃ1huJo+^=:׏ג0jXpy8ڗwsZ'K3g f_7npKW$pI:PUU05wuJIB O4_idoLdPhѬhF=6OggS!!`335[  |G1$pK.8UgTjD(Jn`jakq߯%P;U_oc Ou9!ONs;L؟}>u'Gahj s r꺜V~s?w=y/t)h(o%nE-eGyO^Nlac ~[.enNs!V mޱ`j.A=.2ܚF>|^/ Wy_xԈJTUUEt~t-Q`v0k1y-9:fc\_y>$`TizwTfqswbߘΚo'c"OVt\ qtLc?&kN衚/i Izjuq+{u'}!_di3`@Cu^@!nj]-0ODwe l@ޕ|Gq&A081@xg#PUUue7& ؜`>[C'al1/yld|<^Q۵ =(״jEm[ 4M>-=dۙW~>=l YթzB2IxMdȮȣ`=s\SM;W1Rlmq'Z8'|콮zXqM:pL,F30 @D|\e`H|5^3(H%BUUY+5r\pPz~[{oO73mӧ7_ن4 =ūl@C=f!2(ޕr>OM[\ O\5["gb1ȶ+OuwYh!QsaɁOw;rM׿hLeKgɏs0P@׳{Qf ۼY~O$`ݦTiۿj-=y=ۙeMuAL^4_uޓc;;C.3]=~|]Ѷ`zj24ӆn~v0tӵǼIr2S$^ H\v^pLeG=8Go[ ##$;\\4q(Ov |0,>K~/kс4MGTU ?6v?|]/Hϒߗh`2TUŪ">%^!Ro;UտY_oۗ}f ?m?_:n<]/|iWnPdZcoƑuښLn*ov&~4_0tl^ HAVi> oW#c<܆>=ujL^TݙfXYr'pMߘdf'sbtD%t5,:+5,{YO."q?ԅ1} lN$}f'чnF^Ӌ\7v1.-s(6,YZf0ٱ^_&p?@>u8JR36Ӱ?5lw:ɷg]9NʎwdzIR;DϩV\S<ck9FI4pxv _[m-O~3/w2SUIu03I?v80Mk/|=~Bz9t;7AH2!0WC)_+6]T|k .ؔ ҉?@e+^?B8K~](PwQF`kWKչe ۜW邩ͼ^UVr9ۅׇOH]c!7\L_<wxo=8O5&.sjϟ>,}+;y1CGScC横2iO db'V_=Cձ8`al̔DF3M"&+. s [ob (^?BƒL h Z#j =iJ*$?$_'wa&]swweI_I{<>v2aȩ9}=FTsWtK2S#wib`hdsföW'L:k(vת^Mkk~$dʆ.xb(t/,α~7ˀfjRT{EODf`V S  ^.?wA) ΒO)` ? QDUUU\K!O4>b9,mg|>Soj+Lt-qH(N)_<6MdOdb׷yïc35QmO6Ś7 En<hiz}]ӓ*܍M {18%.Eg B5E  -C|PLK~n/`c8Ȥ3*_~.K-a te۽ 9gtWq>ԛ;f!Z2Oɂ.**9sH_y.*X L5lma+Y1_w9iu;}jv 8Ð]^$OO9>5ph>\̭3ՓogPk5KV}ބd驹1hǣI0]tT#tI}k0H((ᜳ1 k[0>MaF@4 |n7SAe0x#\Fff& +j|YitF̦r]Lb1O 6Vq_9 m hOuz=,;3P{:ΦlZ|uI% r N/ pw&K5Z*A8 ﹪ӝY/d>jm{m|ˑM9nQ`g8ݠldB87% %?/L ^UL:R!nmY[iWJYSyyu|9,|߷~`V3/w4{JP>f;[< [ {gMT}_VuѓYN1L$((MM5HI6H{+ :]ϻ sx8t`;gOggS"`3*%\Jdvo|o7M0ϹA .O 􌒚TUHԿBi ^ہ{?|?7<}NٗzKXps5*g;ysϜ7by{p?Qh`8pگ[؛#r{|Ӌ@zcС GL0n2|q`mC>ڮ_J@6k@{S(IәT*B?y϶[ `c߻|5K0cLOT]!g*58x^Zqj,knݐo]ב٣IUs0{WUU8.znG6I};7.x-m-. ֍P@@H |o7MK~\MS(:G&tRUUHÓwk{w]|:woәN^9|pK x5skIdނ&lz75574P>A;gM7Ms0dWƙ5ў>S;mHNzQ}IiX8MXN4-{ff( m >_&<`.d&Q*R!_~U6d8ƞoK.3GmZ3.x;oE=9bO#$H{ ɀ1Ȁ <7MJ~˛b8\dQBB}ğй(q;lo^sI5MM295BYe] ەK=/ZOzoCooth]Mei3\ *lN7ލ7ϼ73NjW3OOlH_ ԕ38sIm~/YJ5յpbCkVy<~{ܹe޸@(q6>_6:MN"(3џ'3ř޾￾? lNY@ p=D'<UZ,1ST#vs9^ݪӵyL~_!sL# 5 6@.o6Nb#IΤRABVShe_}p'Ys2z#3BsM=@=_.OCy&u$2틋 tv&9̙%ݮ*y8}wQ:Q/ֽ 7BVSԌ""8@`>L_60L_6Q&5TEB ս=/}{v9Wo.9oV})gg֝K4О7f&+ݤ!JZtN60_6(3*޽ǒy;^uD|zG}|Mq|w_8t0AY/Qsw9ې]5)ꜷL͕n }ɫF]L8a< ϨU5+hv&Quqpjݵ_ Q ց5!  %7?PmRJ"qɮ{nr\97s_?:اeL&@iVϹT_tj8}|95ŦsgydB TUQP (n,k;} QRa1@LJ*.ɤ  &LnHH""*iL*ڿɬ" ȫ̪J@OԨT dn,YtlԌJFujU*Hʼάɪ̛;*)f9DiQ'J uTj:VZ5uԉJCiUtests/native/sdl/falcon-ico.bmp000066400000000000000000000101061176363201700170060ustar00rootroot00000000000000BMFF8    &O8d;jt=m>oArEuDr.V6_;j=lo>o?qAsGxMSMx5^;i=l=lo?qAsDwI{MV^bX0Vq=lnBtI|OV[bdKr;q=o=ms?u>q>q>q>p>r@s@sBtDuEtTZ\^\Q2X=p>q=n=p=p=q>q>q>r>r>q=n=n?pAtCwFzH{HwSYXTR=c6bq>q>r>r?s?s?r?s?s@s@r@qFyH|J}I{MQNL~Hx=hq>r>r>r?s?t@t?s=n>o?rAtBvBvCuEvH|H|FwJ{J}GyEvAq7d=n=n>q>r>r>r?s?t@u@vAvBvBvCwBvBvAsCvCxCwBtFzEyBtEvDvAs@q8b1h;nq>r>r>s>s?t@uAvBwBw@qAuCwDyEzDyDvCwDxBvArDwBu?nBt?q?pt>s?t?u@vAwCyDzF|G}H}I}H|ExF{F{ExBtCwAu?qBu@x>n@v?s=n8esA>v>t?u@uAwBxD{F}IJLMMLI{G{F|DxBsC{A|Ar@y;k?qn;j=o=o>t>t?u?u@v@wByD{F}IKMOPQPOLFzGCy?onu>u?u?v@wAxCzE|GH~I~J~LOPRQNI|F}Au-O=ns?u?v?v@w@xByC{E}Ez=jTS/XAmRPJ~9b>mx>s?v?v?w@w@xBzD|E{>kVVKL=i>l=r?z>u?w?w?x@xAyB{C|Bw4,KCpMVYVTNGv>l>s>s>u?x?x@x@yAzB{C{AuF|JPSVVWVPEs>kt>u?y?y@y@yAzB{C{D|IKOPL ,Brv>t@z@z@z@zA{B{B{D{JIJH~=l;i>v?t@z@z@{A{A{B|B|CzE~E|5k&=so>p>q>r>p=o8e9dr=nt>s>r=p:i8a>tt>s>u?yu>t>s>r=p;it>s>qu>u>t=r=p=n=m=o;m6b/^>t?x?v>t>s@y?x=p=p=p "Success! - press enter" input() oldCursor.SetCursor() catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdl_drawfont.fal000066400000000000000000000036431176363201700174560ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MODULE Samples FILE: sdl_fontinfo.fal Shows loading and displaying of informations about fonts. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 25 Mar 2008 02:43:47 +0100 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. In order to use this file in its compiled form, this source or part of it you have to read, understand and accept the conditions that are stated in the LICENSE file that comes boundled with this package. */ load sdlttf try a = TTF.InitAuto() font = TTF.OpenFont( "Vera.ttf", 24) > "Success - in loading font" b = SDL.Init( SDL.INIT_VIDEO ) SDL.WM_SetCaption( "Falcon SDL paint test - 3" ) SDL.LoadBMP("falcon-ico.bmp").SetIcon() screen = SDL.SetVideoMode( 640, 480 ) // draw a cloured band - first hardware try band = SDL.CreateRGBSurface( SDL.HWSURFACE, 640, 1, 32 ) catch SDLError > "Hardware surface creation failed, try with software creation." // then software -- let it fail if it fails band = SDL.CreateRGBSurface( SDL.SWSURFACE, 640, 1, 32 ) end pixels = band.pixels for bid in [0:640] pixels[bid] = band.MapRGBA( 150, bid%128, (bid+128)%256, 250 ) end rect = SDLRect( 0,0,640,1) for y in [0:480] rect.y = y band.BlitSurface( nil, screen, rect ) end // Now draw our font... color = SDLColor( 20, 240, 240) font.SetFontStyle( TTF.STYLE_BOLD || TTF.STYLE_ITALIC ) dataImage = font.Render_Blended( "<<== Hello world! ==>", color ); dataImage.BlitSurface( nil, screen, SDLRect( 0,200,640,300) ) screen.UpdateRect() > "Success! - press enter" input() catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdl_events.fal000066400000000000000000000063211176363201700171320ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MODULE Samples FILE: sdl_events.fal Show minimal management of events. This small program also shows how Falcon coroutines and SDL events can interact. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 16 Mar 2008 21:15:18 +0100 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. In order to use this file in its compiled form, this source or part of it you have to read, understand and accept the conditions that are stated in the LICENSE file that comes boundled with this package. */ load sdl object MyEventManager quitRequest = Semaphore() subs = [ "sdl_MouseMotion", "sdl_Quit", "sdl_Expose", "UserEvent" ] init for event in self.subs: subscribe( event, self ) end function on_sdl_MouseMotion( xstate, x, y, xrel, yrel ) >> " X=",x, " Y=",y, " Xrel=", xrel, " YRel=",yrel, " \r" end function on_sdl_Quit() > strReplicate( " ", 60 ) > "== Quit! ==" broadcast( "UserEvent", 0, "I am quit" ) self.quitRequest.post(2) end function on_sdl_Expose() global screen screen.UpdateRect() end function on_UserEvent( code, item ) > item, strReplicate( " ", 60 ) end end //============================================== // Coroutine to display a blinking cursor // function blinker( screen ) r = SDLRect( 280, 200, 80, 80 ) black = screen.MapRGBA( 0, 0, 0, 0 ) white = screen.MapRGBA( 255, 255, 255, 255 ) color = white time = 0 while not MyEventManager.quitRequest.wait( 0.5 ) color = (color == white ? black : white ) screen.FillRect( r, color ) screen.UpdateRect( r ) time ++ if time > 8 time = 0 broadcast( "UserEvent", 0, "Reset..." ) end end end //============================================== // Main program // try a = SDL.InitAuto( SDL.INIT_VIDEO ) SDL.LoadBMP("falcon-ico.bmp").SetIcon() SDL.WM_SetCaption( "Falcon SDL Event Handler Test" ) screen = SDL.SetVideoMode( 640, 480 ) // draw a cloured band - first hardware try band = SDL.CreateRGBSurface( SDL.HWSURFACE, 640, 1, 32 ) catch SDLError > "Hardware surface creation failed, try with software creation." // then software -- let it fail if it fails band = SDL.CreateRGBSurface( SDL.SWSURFACE, 640, 1, 32 ) end launch blinker( screen ) pixels = band.pixels for bid in [0:640] pixels[bid] = band.MapRGBA( 150, bid%128, (bid+128)%256, 250 ) end rect = SDLRect( 0,0,640,1) for y in [0:480] rect.y = y band.BlitSurface( nil, screen, rect ) end screen.UpdateRect() // make a solid rectangle > "Success! - Now start moving the mouse." > "Click the X on the bar to quit the app." SDL.StartEvents() count = 0 while true if MyEventManager.quitRequest.wait(1): break >> count++, "\r" end SDL.StopEvents() > "Complete." catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdl_fontinfo.fal000066400000000000000000000033111176363201700174440ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MODULE Samples FILE: sdl_fontinfo.fal Shows loading and displaying of informations about fonts. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Tue, 25 Mar 2008 02:43:47 +0100 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. In order to use this file in its compiled form, this source or part of it you have to read, understand and accept the conditions that are stated in the LICENSE file that comes boundled with this package. */ load sdlttf try a = TTF.InitAuto() font = TTF.OpenFont( "Vera.ttf", 16) > "=========================================" > "Success - in loading font" > "Font family: ", font.FontFaceFamilyName() > "Font style: ", font.FontFaceStyleName() > "Ascent: ", font.FontAscent() > "Descent: ", font.FontDescent() > "Fixed? -- ", font.FontFaceIsFixedWidth() ? "Yes" : "No" > "Height: ", font.FontHeight() > "Line skip: ", font.FontLineSkip() metrics = TTFMetrics() if font.GlyphMetrics( ord("O"), metrics ) > "Metrics for 'O':", \ " maxx=",metrics.maxx, " maxy=", metrics.maxy,\ " minx=",metrics.minx, " miny=", metrics.miny,\ " ascent=",metrics.ascent else > "Metrics retrival failed" end if font.SizeText( "Hello world!", metrics ) > "Size of the string 'Hello World!: ", metrics.w, "x", metrics.h else > "Size retrival failed" end > > "Test complete." catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdl_info.fal000066400000000000000000000027251176363201700165650ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MODULE Samples FILE: sdl_info.fal Reads and displays video infos. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 16 Mar 2008 21:15:18 +0100 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. In order to use this file in its compiled form, this source or part of it you have to read, understand and accept the conditions that are stated in the LICENSE file that comes boundled with this package. */ load sdl try a = SDL.InitAuto( SDL.INIT_VIDEO ) vi = SDL.GetVideoInfo() > "Video informations:" inspect( vi ) > "===============================" > "Using driver: ", SDL.VideoDriverName() >> "Available modes: " ListModes( SDL.ListModes( nil, SDL.FULLSCREEN||SDL.HWSURFACE ) ) > "Preferred bit per pixels at 640x480: ", \ SDL.VideoModeOK( 640, 480, 8 ) //> "Captions: ", title, " / ", iconTitle > "===============================" catch in e > "Test failed: " > e end function ListModes( modes ) switch modes case nil > "none." case -1 > "all." default for item in modes >> item[0], "x", item[1] formiddle: >> ", " forlast: > "." end end end /* end of file */ tests/native/sdl/sdl_init.fal000066400000000000000000000016721176363201700165750ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MODULE Samples FILE: sdl_init.fal Minimal initialization and quit test. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 16 Mar 2008 21:15:18 +0100 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. In order to use this file in its compiled form, this source or part of it you have to read, understand and accept the conditions that are stated in the LICENSE file that comes boundled with this package. */ load sdl try a = SDL.InitAuto( SDL.INIT_VIDEO ) SDL.WM_SetCaption( "Falcon SDL window", "Falcon and SDL" ); screen = SDL.SetVideoMode( 640, 480 ) > "Success! - press enter" input() catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdl_logo.fal000066400000000000000000000020651176363201700165670ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MODULE Samples FILE: sdl_logo.fal Test for minimal imaging functions. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 16 Mar 2008 21:15:18 +0100 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. In order to use this file in its compiled form, this source or part of it you have to read, understand and accept the conditions that are stated in the LICENSE file that comes boundled with this package. */ load sdl try a = SDL.InitAuto( SDL.INIT_VIDEO ) SDL.LoadBMP("falcon-ico.bmp").SetIcon() SDL.WM_SetCaption( "Falcon logo!" ) screen = SDL.SetVideoMode( 640, 480 ) img = SDL.LoadBMP( "FalconLogo1.bmp" ) img.BlitSurface( nil, screen, nil ) screen.UpdateRect() > "Success! - press enter" input() catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdl_paint.fal000066400000000000000000000027611176363201700167450ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MODULE Samples FILE: sdl_paint.fal Shows direct painting on SDL surfaces. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 16 Mar 2008 21:15:18 +0100 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. In order to use this file in its compiled form, this source or part of it you have to read, understand and accept the conditions that are stated in the LICENSE file that comes boundled with this package. */ load sdl try a = SDL.InitAuto( SDL.INIT_VIDEO ) SDL.WM_SetCaption( "Falcon SDL paint test - 1" ) SDL.LoadBMP("falcon-ico.bmp").SetIcon() screen = SDL.SetVideoMode( 640, 480 ) // draw a cloured band band = MemBuf( 640, screen.format.BytesPerPixel ) for bid in [0:640] band[bid] = screen.MapRGBA( 150, bid%128, (bid+128)%256, 250 ) end pixels = screen.pixels screen.LockIfNeeded() for y in [0:480] for x in [0:640] n = x + y * 640 pixels[ n ] = band[x] end end screen.UnlockIfNeeded() // make a solid rectangle r = SDLRect( 280, 200, 80, 80 ) screen.FillRect( r, screen.MapRGBA( 255, 255, 255, 255 ) ) screen.UpdateRect() > "Success! - press enter" input() catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdl_paint2.fal000066400000000000000000000034451176363201700170270ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MODULE Samples FILE: sdl_paint.fal Shows direct painting on SDL surfaces. Does the same things as paint, but it uses a software surface to blit repeatedly the same buffer. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 16 Mar 2008 21:15:18 +0100 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. In order to use this file in its compiled form, this source or part of it you have to read, understand and accept the conditions that are stated in the LICENSE file that comes boundled with this package. */ load sdl try a = SDL.InitAuto( SDL.INIT_VIDEO ) SDL.LoadBMP("falcon-ico.bmp").SetIcon() SDL.WM_SetCaption( "Falcon SDL paint test - 2" ) screen = SDL.SetVideoMode( 640, 480 ) // draw a cloured band - first hardware try band = SDL.CreateRGBSurface( SDL.HWSURFACE, 640, 1, 32 ) catch SDLError > "Hardware surface creation failed, try with software creation." // then software -- let it fail if it fails band = SDL.CreateRGBSurface( SDL.SWSURFACE, 640, 1, 32 ) end pixels = band.pixels for bid in [0:640] pixels[bid] = band.MapRGBA( 150, bid%128, (bid+128)%256, 250 ) end rect = SDLRect( 0,0,640,1) for y in [0:480] > y rect.y = y band.BlitSurface( nil, screen, rect ) end // make a solid rectangle r = SDLRect( 280, 200, 80, 80 ) screen.FillRect( r, screen.MapRGBA( 255, 255, 255, 255 ) ) screen.UpdateRect() > "Success! - press enter" input() catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdl_paint3.fal000066400000000000000000000034651176363201700170320ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MODULE Samples FILE: sdl_paint.fal Shows direct painting on SDL surfaces. Does the same things as paint2, but it uses a prebuilt membuf that is then delivered to a surface. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Sun, 16 Mar 2008 21:15:18 +0100 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. In order to use this file in its compiled form, this source or part of it you have to read, understand and accept the conditions that are stated in the LICENSE file that comes boundled with this package. */ load sdl try a = SDL.InitAuto( SDL.INIT_VIDEO ) SDL.LoadBMP("falcon-ico.bmp").SetIcon() SDL.WM_SetCaption( "Falcon SDL paint test - 3" ) screen = SDL.SetVideoMode( 640, 480 ) // draw a cloured band format = screen.format bandPix = MemBuf( 640, format.BytesPerPixel ) for bid in [0:640] bandPix[bid] = screen.MapRGBA( 150, bid%128, (bid+128)%256, 250 ) end // try now to store our color band somewhere. - first hardware band = SDL.CreateRGBSurfaceFrom( bandPix, 640, 1, format.BitsPerPixel, format.Rmask, format.Gmask, format.Bmask, format.Amask ) // then copy the result on the screen rect = SDLRect( 0, 0, 640, 1 ) blitter = band.BlitSurface for y in [0:480] rect.y = y blitter( nil, screen, rect ) end // make a solid rectangle r = SDLRect( 280, 200, 80, 80 ) screen.FillRect( r, screen.MapRGBA( 255, 255, 255, 255 ) ) screen.UpdateRect() > "Success! - press enter" input() catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdl_testimages.fal000066400000000000000000000056551176363201700200040ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MODULE Samples FILE: sdl_testimages.fal Tests the various functions related to SDL image module ------------------------------------------------------------------- Author: Federico Baroni Begin: Wed, 1 Oct 2008 21:58:47 +0100 Last modified because: Tue 7 Oct 2008 23:06:03 - Test 03 added ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. In order to use this file in its compiled form, this source or part of it you have to read, understand and accept the conditions that are stated in the LICENSE file that comes boundled with this package. */ load sdlimage > "\nStarting SDL_image module testing procedure:\n" > "!------------------------------------- TEST 01 -------------------------------------------!" > "Wrong loading (5 different loads):\n" try > "1st call: no call parameter" IMAGE.Load (); catch in e >e end try > "\n2nd call: filename is '0'" IMAGE.Load ( 0 ); catch in e >e end try > "\n3rd call: filename is a number" IMAGE.Load ( 654684 ); catch in e >e end try > "\n4th call: filename is \"Name\"" IMAGE.Load ( "Name" ); catch in e >e end try > "\n5th call: filename is \"Name.jpg\"" IMAGE.Load ( "Name.jpg" ); catch in e >e end > "\nSuccess! - press enter" input() > "End of test 01.\n\n" try > "!------------------------------------- TEST 02 -------------------------------------------!" > "Setting an error:" message = "Very long error string for you!!!" > message IMAGE.SetError( message ) > "\nNow getting it back:" str = IMAGE.GetError() > str if ( str != message ) raise "Strings do not match!" end > "\nSuccess! - press enter" catch in e > "\nTest failed: " > e > "Press enter" end input() > "End of test 02.\n\n" try > "!------------------------------------- TEST 03 -------------------------------------------!" a = SDL.InitAuto( SDL.INIT_VIDEO ) SDL.LoadBMP("falcon-ico.bmp").SetIcon() SDL.WM_SetCaption( "Falcon logo!" ) screen = SDL.SetVideoMode( 640, 480 ) > "Trying to load a BMP image:\n" img = IMAGE.Load( "FalconLogo1.bmp" ) img.BlitSurface( nil, screen, nil ) screen.UpdateRect() > "Success! - press enter" catch in e > "Test failed: " > e > "Press enter" end input() > "End of test 03.\n\n" try > "!------------------------------------- TEST 04 -------------------------------------------!" > "Trying to load a BMP image using streams:\n" img_stream = InputStream( "FalconLogo1.bmp" ) img = IMAGE.Load( img_stream ) img.BlitSurface( nil, screen, SDLRect(0,200,640,300) ) screen.UpdateRect() > "Success! - press enter" catch in e > "Test failed: " > e > "Press enter" end input() > "End of test 04.\n\n" /* end of file */ tests/native/sdl/sdlgl_rotatingTexturedCube.fal000066400000000000000000000117331176363201700223270ustar00rootroot00000000000000import SDL from sdl as SDL import IMAGE from sdlimage as IMAGE import from sdlgl as gl import GL from sdlgl as GL import from sdlglu as glu import GLU from sdlglu as GLU width = 640 height = 480 screen = nil rotationx = 0 rotationy = 0 rotvelx = 0 rotvely = 0 texture = nil teximage = nil function InitGraphics() global screen global texture SDL.Init(SDL.INIT_VIDEO) screen = SDL.SetVideoMode(width,height,32,SDL.OPENGL) gl.Viewport(0,0,width,height) gl.MatrixMode(GL.PROJECTION) gl.LoadIdentity() glu.Perspective(45.0, width / height, 1.0, 100.0) gl.MatrixMode(GL.MODELVIEW) gl.SetAttribute(GL.RED_SIZE, 5) gl.SetAttribute(GL.GREEN_SIZE, 5) gl.SetAttribute(GL.BLUE_SIZE, 5) gl.SetAttribute(GL.DEPTH_SIZE, 16) gl.SetAttribute(GL.DOUBLEBUFFER, 1) teximage = IMAGE.Load("NeHe.bmp", "BMP") texture = gl.GenTextures(1) gl.BindTexture(GL.TEXTURE_2D, texture[0]) gl.TexImage2D(GL.TEXTURE_2D, 0, 3, teximage.w, teximage.h, 0, GL.RGB, GL.UNSIGNED_BYTE, teximage.pixels) gl.TexParameteri(GL.TEXTURE_2D,GL.TEXTURE_MIN_FILTER,GL.LINEAR) gl.TexParameteri(GL.TEXTURE_2D,GL.TEXTURE_MAG_FILTER,GL.LINEAR) teximage = nil gl.Enable(GL.TEXTURE_2D) gl.ShadeModel(GL.SMOOTH) gl.ClearColor(0, 0, 0, 1) gl.ClearDepth(1) gl.Enable(GL.DEPTH_TEST) gl.DepthFunc(GL.LEQUAL) gl.Hint(GL.PERSPECTIVE_CORRECTION_HINT, GL.NICEST) end function DrawGraphics() gl.Clear(GL.COLOR_BUFFER_BIT || GL.DEPTH_BUFFER_BIT); // Set location in front of camera gl.LoadIdentity() gl.Translate(0, 0, -10) rotationx += rotvelx rotationy += rotvely gl.Rotate(rotationx, 0,1,0) gl.Rotate(rotationy, 1,0,0) gl.BindTexture(GL.TEXTURE_2D, texture[0]); //Draw a Cube gl.Begin(GL.QUADS) // Front Face gl.TexCoord2d(0.0, 0.0); gl.Vertex3d(-1.0, -1.0, 1.0) // Bottom Left Of The Texture and Quad gl.TexCoord2d(1.0, 0.0); gl.Vertex3d( 1.0, -1.0, 1.0) // Bottom Right Of The Texture and Quad gl.TexCoord2d(1.0, 1.0); gl.Vertex3d( 1.0, 1.0, 1.0) // Top Right Of The Texture and Quad gl.TexCoord2d(0.0, 1.0); gl.Vertex3d(-1.0, 1.0, 1.0) // Top Left Of The Texture and Quad // Back Face gl.TexCoord2d(1.0, 0.0); gl.Vertex3d(-1.0, -1.0, -1.0) // Bottom Right Of The Texture and Quad gl.TexCoord2d(1.0, 1.0); gl.Vertex3d(-1.0, 1.0, -1.0) // Top Right Of The Texture and Quad gl.TexCoord2d(0.0, 1.0); gl.Vertex3d( 1.0, 1.0, -1.0) // Top Left Of The Texture and Quad gl.TexCoord2d(0.0, 0.0); gl.Vertex3d( 1.0, -1.0, -1.0) // Bottom Left Of The Texture and Quad // Top Face gl.TexCoord2d(0.0, 1.0); gl.Vertex3d(-1.0, 1.0, -1.0) // Top Left Of The Texture and Quad gl.TexCoord2d(0.0, 0.0); gl.Vertex3d(-1.0, 1.0, 1.0) // Bottom Left Of The Texture and Quad gl.TexCoord2d(1.0, 0.0); gl.Vertex3d( 1.0, 1.0, 1.0) // Bottom Right Of The Texture and Quad gl.TexCoord2d(1.0, 1.0); gl.Vertex3d( 1.0, 1.0, -1.0) // Top Right Of The Texture and Quad // Bottom Face gl.TexCoord2d(1.0, 1.0); gl.Vertex3d(-1.0, -1.0, -1.0) // Top Right Of The Texture and Quad gl.TexCoord2d(0.0, 1.0); gl.Vertex3d( 1.0, -1.0, -1.0) // Top Left Of The Texture and Quad gl.TexCoord2d(0.0, 0.0); gl.Vertex3d( 1.0, -1.0, 1.0) // Bottom Left Of The Texture and Quad gl.TexCoord2d(1.0, 0.0); gl.Vertex3d(-1.0, -1.0, 1.0) // Bottom Right Of The Texture and Quad // Right face gl.TexCoord2d(1.0, 0.0); gl.Vertex3d( 1.0, -1.0, -1.0) // Bottom Right Of The Texture and Quad gl.TexCoord2d(1.0, 1.0); gl.Vertex3d( 1.0, 1.0, -1.0) // Top Right Of The Texture and Quad gl.TexCoord2d(0.0, 1.0); gl.Vertex3d( 1.0, 1.0, 1.0) // Top Left Of The Texture and Quad gl.TexCoord2d(0.0, 0.0); gl.Vertex3d( 1.0, -1.0, 1.0) // Bottom Left Of The Texture and Quad // Left Face gl.TexCoord2d(0.0, 0.0); gl.Vertex3d(-1.0, -1.0, -1.0) // Bottom Left Of The Texture and Quad gl.TexCoord2d(1.0, 0.0); gl.Vertex3d(-1.0, -1.0, 1.0) // Bottom Right Of The Texture and Quad gl.TexCoord2d(1.0, 1.0); gl.Vertex3d(-1.0, 1.0, 1.0) // Top Right Of The Texture and Quad gl.TexCoord2d(0.0, 1.0); gl.Vertex3d(-1.0, 1.0, -1.0) // Top Left Of The Texture and Quad gl.End() // Show the frame gl.SwapBuffers() end object events events = ["sdl_Quit", "sdl_Expose", "sdl_KeyDown"] init for event in self.events: subscribe( event, self ) end function on_sdl_Quit( state, x, y, xrel, yrel ) > "== Quit! ==" exit(0) end function on_sdl_Expose() gl.SwapBuffers(); end function on_sdl_KeyDown(state, scancode, sym, mod, unicode) if sym == SDLK.ESCAPE > "== Quit! ==" exit(0) elif sym == SDLK.LEFT rotvelx -= 0.1 elif sym == SDLK.RIGHT rotvelx += 0.1 elif sym == SDLK.UP rotvely += 0.1 elif sym == SDLK.DOWN rotvely -= 0.1 end end end try InitGraphics() SDL.StartEvents() while true DrawGraphics() sleep(0.001) end SDL.StopEvents() catch in e > e end tests/native/sdl/sdlgl_rotatingplane.fal000066400000000000000000000037111176363201700210200ustar00rootroot00000000000000import SDL from sdl import from sdlgl as OGL import GL from sdlgl as GL import from sdlglu as OGLU import GLU from sdlglu as GLU import from sdlglext as OGLExt import GLExt from sdlglext as GLExt width = 640 height = 480 screen = nil rotation = 0 function InitGraphics() global screen SDL.Init(SDL.INIT_VIDEO) screen = SDL.SetVideoMode(width,height,32,SDL.OPENGL) OGL.Viewport(0,0,width,height) OGL.MatrixMode(GL.PROJECTION) OGL.LoadIdentity() OGLU.Perspective(45.0, width / height, 1.0, 100.0) OGL.MatrixMode(GL.MODELVIEW) OGL.SetAttribute(GL.RED_SIZE, 5) OGL.SetAttribute(GL.GREEN_SIZE, 5) OGL.SetAttribute(GL.BLUE_SIZE, 5) OGL.SetAttribute(GL.DEPTH_SIZE, 16) OGL.SetAttribute(GL.DOUBLEBUFFER, 1) OGL.ClearColor(0, 0, 0, 1) OGL.ClearDepth(1) OGL.Enable(GL.DEPTH_TEST) end function DrawGraphics() OGL.Clear(GL.COLOR_BUFFER_BIT || GL.DEPTH_BUFFER_BIT); // Set location in front of camera OGL.LoadIdentity() OGL.Translate(0, 0, -10) rotation += 0.2 OGL.Rotate(rotation, 1,1,1) // Draw a square OGL.Begin(GL.QUADS) OGL.Color3d(1, 0, 0) OGL.Vertex3d(-2, 2, 0) OGL.Color3d(1, 1, 0) OGL.Vertex3d(2, 2, 0) OGL.Color3d(0, 1, 0) OGL.Vertex3d(2, -2, 0) OGL.Color3d(0, 1, 1) OGL.Vertex3d(-2, -2, 0) OGL.End() // Show the frame OGL.SwapBuffers() end object events events = ["sdl_Quit", "sdl_Expose", "sdl_KeyDown"] init for event in self.events: subscribe( event, self ) end function on_sdl_Quit( state, x, y, xrel, yrel ) > "== Quit! ==" exit(0) end function on_sdl_Expose() OGL.SwapBuffers(); end function on_sdl_KeyDown(state, scancode, sym, mod, unicode) if sym == SDLK.ESCAPE > "== Quit! ==" exit(0) end end end try InitGraphics() SDL.StartEvents() while true DrawGraphics() sleep(0.001) end SDL.StopEvents() catch in e > e end tests/native/sdl/sdlimg_stream.fal000066400000000000000000000031751176363201700176220ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MODULE Samples FILE: sdl_testimages.fal Tests the various functions related to SDL image module ------------------------------------------------------------------- Author: Federico Baroni Begin: Wed, 1 Oct 2008 21:58:47 +0100 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. In order to use this file in its compiled form, this source or part of it you have to read, understand and accept the conditions that are stated in the LICENSE file that comes boundled with this package. */ load sdlimage > "\nStarting SDL_image module testing procedure:\n" > "Testing SDL_Image Load on Falcon Streams" try a = SDL.InitAuto( SDL.INIT_VIDEO ) SDL.LoadBMP("falcon-ico.bmp").SetIcon() SDL.WM_SetCaption( "Falcon logo!" ) screen = SDL.SetVideoMode( 640, 480 ) img_stream = InputStream( "FalconLogo1.bmp" ) > "Checking if the image is a bmp: ", IMAGE.isBMP( img_stream ) > "Checking if the image is a jpg: ", IMAGE.isJPG( img_stream ) > "Trying to load a BMP image:\n" img = IMAGE.Load( img_stream ) img.BlitSurface( nil, screen, nil ) screen.UpdateRect() img_stream.seek(0) > "Trying to load a BMP image with the hint:\n" img = IMAGE.Load( img_stream, "BMP" ) img.BlitSurface( nil, screen, SDLRect(0,200,640,300) ) screen.UpdateRect() > "Success! - press enter" catch in e > "Test failed: " > e > "Press enter" end input() > "End of test 03.\n\n" /* end of file */ tests/native/sdl/sdlmix_fadeout.fal000066400000000000000000000017641176363201700200010ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MIXER MODULE Samples FILE: sdlmix_pause.fal Test for sound channel pause/resume. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 09 Oct 2008 23:19:59 +0200 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load sdlmixer try a = SDL.InitAuto( SDL.INIT_AUDIO ) MIX.OpenAudio( 44100, AUDIO.S16, 2, 4096 ) MIX.AllocateChannels( 2 ) sound = MIX.LoadWAV( "fading.ogg" ) sound.Play( -1, -1 ) > "Success. Press enter to stop" > "Fadeout in 8 seconds (for 5 seconds)." count = 0 stdin = stdIn() loop count++ >> @"[$(count)]\r" if count %80 == 0: MIX.FadeOutChannel( 0, 5.0 ) end stdin.readAvailable(0.1) MIX.CloseAudio() catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdlmix_music.fal000066400000000000000000000031561176363201700174670ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MIXER MODULE Samples FILE: sdlmix_play.fal Generic music file support testing. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 09 Oct 2008 23:19:59 +0200 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load sdlmixer object listener init subscribe( "sdl_Quit", self ) end function on_sdl_Quit() > "Quitting" exit(0) end end try a = SDL.InitAuto( SDL.INIT_AUDIO || SDL.INIT_VIDEO ) MIX.OpenAudio( 44100, AUDIO.S16, 2, 4096 ) music = MIX.LoadMUS( "fading.ogg" ) > "Success. Press enter to stop" > "Fading in in 6 seconds" music.Play( fadeIn| 6 ) count = 0 stdin = stdIn() SDL.StartEvents() loop count++ >> @"[$(count)]\r" if count == 80 > "Pausing for two seconds:" MIX.PauseMusic() end if count == 90 > "Checing if music is paused (", \ MIX.PausedMusic(), ") and playing (", \ MIX.PlayingMusic(), ")" end if count == 100 > "Resuming music" MIX.ResumeMusic() end if count == 140 > "Fading out in 5 secs " MIX.FadeOutMusic( 5 ) end if count == 170 > "... but killing it now" MIX.HaltMusic() end end stdin.readAvailable(0.1) MIX.CloseAudio() catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdlmix_musicdone.fal000066400000000000000000000023421176363201700203310ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MIXER MODULE Samples FILE: sdlmix_play.fal Generic music file support testing. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 09 Oct 2008 23:19:59 +0200 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load sdlmixer object listener init subscribe( "sdl_MusicFinished", self ) subscribe( "on_sdl_Quit", self ) end function on_sdl_MusicFinished() > "Music finished." exit(0) end function on_sdl_Quit() > "Quitting" exit(0) end end try a = SDL.InitAuto( SDL.INIT_AUDIO || SDL.INIT_VIDEO ) MIX.OpenAudio( 44100, AUDIO.S16, 2, 4096 ) music = MIX.LoadMUS( "fading.ogg" ) > "Success. Press enter to stop" music.Play() MIX.HookMusicFinished( true ) count = 0 stdin = stdIn() SDL.StartEvents() loop count++ >> @"[$(count)]\r" end stdin.readAvailable(0.1) SDL.StopEvents() MIX.CloseAudio() catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdlmix_ondone.fal000066400000000000000000000027251176363201700176320ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MIXER MODULE Samples FILE: sdlmix_ondone.fal Checking callbacks on completions. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 09 Oct 2008 23:19:59 +0200 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load sdlmixer object listener init subscribe( "sdl_ChannelFinished", self ) subscribe( "sdl_Quit", self ) end function on_sdl_ChannelFinished( id ) > "Channel ", id, " finished playing. - Restarting." global sound sound.Play(0,-1) end function on_sdl_Quit() > "Quitting" exit(0) end end try a = SDL.InitAuto( SDL.INIT_AUDIO || SDL.INIT_VIDEO) MIX.OpenAudio( 44100, AUDIO.S16, 2, 4096 ) MIX.AllocateChannels( 2 ) sound = MIX.LoadWAV( "fading.ogg" ) sound.Play( -1, -1 ) MIX.ChannelFinished( true ) > "Success. Press enter to stop" > "Channel will stop after 10 seconds, and will then be restarted." count = 0 stdin = stdIn() loop count++ >> @"[$(count)]\r" if stdin.readAvailable(0.1): break sleep(0.1) if count %80 == 0: MIX.FadeOutChannel( 0, 2.0 ) SDL.PollEvent() end MIX.CloseAudio() catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdlmix_pause.fal000066400000000000000000000020701176363201700174560ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MIXER MODULE Samples FILE: sdlmix_pause.fal Test for sound channel pause/resume. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 09 Oct 2008 23:19:59 +0200 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load sdlmixer try a = SDL.InitAuto( SDL.INIT_AUDIO ) MIX.OpenAudio( 44100, AUDIO.S16, 2, 4096 ) MIX.AllocateChannels( 2 ) sound = MIX.LoadWAV( "fading.ogg" ) sound.Play( -1, -1 ) > "Success. Press enter to stop" > "Notice that we'll pause and resume the channel every 5 seconds" count = 0 stdin = stdIn() loop count++ >> @"[$(count)]\r" if stdin.readAvailable(0.1): break if count %50 == 0: MIX.Pause( 0 ) if count %60 == 0: MIX.Resume( 0 ) end MIX.CloseAudio() catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdlmix_play.fal000066400000000000000000000016301176363201700173070ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MIXER MODULE Samples FILE: sdlmix_play.fal Test for minimal sound play. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 09 Oct 2008 23:19:59 +0200 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load sdlmixer try a = SDL.InitAuto( SDL.INIT_AUDIO ) MIX.OpenAudio( 44100, AUDIO.S16, 2, 4096 ) MIX.AllocateChannels( 2 ) sound = MIX.LoadWAV( "fading.ogg" ) sound.Play( -1, -1 ) > "Success. Press enter to stop" count = 0 stdin = stdIn() loop count++ >> @"[$(count)]\r" end stdin.readAvailable(0.010) MIX.CloseAudio() catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdlmix_playecho.fal000066400000000000000000000021001176363201700201370ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MIXER MODULE Samples FILE: sdlmix_playecho.fal Test for play on multiple channels. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 09 Oct 2008 23:19:59 +0200 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load sdlmixer try a = SDL.InitAuto( SDL.INIT_AUDIO ) MIX.OpenAudio( 44100, AUDIO.S16, 2, 4096 ) MIX.AllocateChannels( 3 ) sound = MIX.LoadWAV( "fading.ogg" ) sound.Play( 0, -1 ) // perform a bit of echo MIX.Volume( 1, 60 ) MIX.Volume( 2, 40 ) sleep( 0.30 ) sound.Play( 1, -1 ) sleep( 0.30 ) sound.Play( 2, -1 ) > "Success. Press enter to stop" count = 0 stdin = stdIn() loop count++ >> @"[$(count)]\r" if stdin.readAvailable(0.010): break end MIX.CloseAudio() catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdlmix_playfadein.fal000066400000000000000000000017601176363201700204620ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MIXER MODULE Samples FILE: sdlmix_playfadein.fal Test for fade-in. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 09 Oct 2008 23:19:59 +0200 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load sdlmixer try a = SDL.InitAuto( SDL.INIT_AUDIO ) MIX.OpenAudio( 44100, AUDIO.S16, 2, 4096 ) MIX.AllocateChannels( 3 ) sound = MIX.LoadWAV( "fading.ogg" ) sound.Play( 0, -1, time|12, fadeIn|6 ) > "Success. Press enter to stop" > "Fadein: 6 secs - Notice that the music will stop after 12 seconds" count = 0 stdin = stdIn() loop count++ >> @"[$(count)]\r" if stdin.readAvailable(0.010): break end MIX.CloseAudio() catch in e > "Test failed: " > e end /* end of file */ tests/native/sdl/sdlmix_playtime.fal000066400000000000000000000017151176363201700201720ustar00rootroot00000000000000#!/usr/bin/falcon /* FALCON - SDL MIXER MODULE Samples FILE: sdlmix_time.fal Test for automatic sound cut. ------------------------------------------------------------------- Author: Giancarlo Niccolai Begin: Thu, 09 Oct 2008 23:19:59 +0200 Last modified because: ------------------------------------------------------------------- (C) Copyright 2008: the FALCON developers (see list in AUTHORS file) See LICENSE file for licensing details. */ load sdlmixer try a = SDL.InitAuto( SDL.INIT_AUDIO ) MIX.OpenAudio( 44100, AUDIO.S16, 2, 4096 ) MIX.AllocateChannels( 3 ) sound = MIX.LoadWAV( "fading.ogg" ) sound.Play( 0, -1, time|8.5 ) > "Success. Press enter to stop" > "Notice that the music will stop after 8.5 seconds" count = 0 stdin = stdIn() loop count++ >> @"[$(count)]\r" end stdin.readAvailable(0.010) MIX.CloseAudio() catch in e > "Test failed: " > e end /* end of file */ tests/native/wopi/000077500000000000000000000000001176363201700144725ustar00rootroot00000000000000tests/native/wopi/README000066400000000000000000000022021176363201700153460ustar00rootroot00000000000000 Falcon WOPI - Test site This test site contains a small web-based application that can run through the various WOPI front-ends. Although WOPI is largely seamless, you'll need to configure the various WOPI modules to point to this area, or to configure you web server to read this directory as a CGI or FastCGI directory. Also, you may be required to configure the web application essential data, as the sessions timeout or the location where to place temporary files, either changing the configuration parameters in the WOPI specific front-end configuration files or setting them as "wopi_" attributes in the index.fal file. Also, in case of using CGI or FastCGI front ends, remember to copy the binary CGI front end in the directory, and name it "index", "index.exe" or "index.cgi" depending on the operating system you're on. Also, the file copied file should be directly executable. In case you use the cgi_fm frontend, remember to make index.fal executable, if your system allows to, and to correctly configure the sh-bang directive at top of the file to point to the falcon command line interpreter. tests/native/wopi/imgs/000077500000000000000000000000001176363201700154315ustar00rootroot00000000000000tests/native/wopi/imgs/falcon-logo.png000066400000000000000000000577541176363201700203610ustar00rootroot00000000000000PNG  IHDRk\tEXtSoftwareAdobe ImageReadyqe<_IDATx}|\W3X.fYnq76 ! Xz,e HHRI$ےիGe͌4e icI3o{s={. ޤ !|Ekllͷ+*h83Reu5 0pjf ) cmJf>%VCS`TL6u7u `e ]DbgO*pVўahLQ18`t3S3"Flе\uZΚnR|lkPԟ/hQ= *d1+2=<2ĈCUr2Rë2O' b]8 1Vn>4[߸uo9anhg{_F"X-R$2d|5Se[ڈZfB2V͐cQVxr `ؼĆ*feE{zah$Z`gEzaoK,&Y[,&XfvѱGl٢{M|裳񤽨7KQҐk,P@` 0g K#C^5͓,^0FUc-i{t]sѡ$O:@bȰaB/ӌޭrɟQDO;&zUթi(P 1ϕ A[Z,߰w>aØ_`ɛ\/'obb!71Q@?Y] h4URx%-dو\ 2xXTW#-?{d`U-@9܊sʤIeN]٦J:PXKt: 33 nqr|ʕ>+U`C/튪jT{P,bS&4 Eq&و*+Ud>Hb3ɻ7G}0߇Tn0HaFϣy*8"̲c(>2NOWw޷qZT6rVzm( 96)h4 @|4 NߪmO94Sc+cHh}%. {_+:}e+~g;`GޙqöD 8l x<\TWa_E&g k[A` 2{ P" #wJ7|pwrfL@yYh a%0CRL!^0翹['N=&4DLK~ړT=|ԅ[n|`1뜡p0]b롲MRZi|=Nb%&0foM>3a4Y4c3k>4#a,OAs sg=8ᄩĐ[B ׾#L{ C1*'6 ((,(xƒB0Z/}er-H(pMw|>HTo[qx8\EΑ)D=&,qjb @ф /Y8eЗ,$E %tu͘++qU2]}hNgc<[w0R}(wxym*'Np7oVa%BroyʒYX6 15 jWLo99tAHAߐ M9;, MR^*)BdUlFN&1…+}nd'+ X`69x]/Ai]9.kQy#cxҬTa^NaC]w 2}`OToWя@)PcCS +ǵEpaP*D}Jamc0e4郰MAA 甜mW&s*{yͽ#U(ꫛ{njwV$G2IOii'P8SHQШ?7;8'@P8--M0Ea,'i&Uc>La x*4 yefWkwg}}=N8;<$DTgBSp'9:EQ 'o8$|V^:1I(*vo4ejxS4 yJ:Ő"8w}RxC#;LͩW{0^-^`pp~[̑rEaz͸/7+:Y&[(V v?έ&jEo)4(5Ϫ2O7D:D:q|B_=Y|/nė,wJyckv~R4pyD> Nc]DPl}0N>fڟjsO uz]Sː;k,Z}gֽF>&v|^.齏\tvcndmw{"o\_| ܈VŶ!Kvqوt~kb b.̙@┼^zXlB脂qz ~aGJ7 & 3 Ǩ2ҘNB~_q-T#9V=&e)0SŵR *i`N9}mFvOʇfvG'jZ} h:2so񍸿?^3a9:϶3f׿+xLз:鸱 6. s=nƴq1A#19\SYOoߵK?*/cFTy6 F8C9Us3:`M r|Bf"Pk Qwºd\yX>?>ɇƋ]CMMM?]~wYgz{нowZg`,H8[vGc}:o;&8 5ê%c`/*9%/n޲%r[Z<ܵ5OoEbVOS7~H%F%rtșh!o/P~黕]o:Bތ3o4m79kw5z͢8D}0:k*[PJmcd|HDlHs!1e.-a >,S+v_PKw/ vTǁӼS Xߺ#wr8ź`Gt˺!L)4lG4\&%R'WW?%'+#.rB]HP}8q:~ZeYpz-ff/diIqDUC8ŧ(jrgĬi}UW,];{A K6m y;NX9վw_=[ b-}Onstv-8 ƪ(C,mgAN?h!;;P#=ed0ѓ/uotWs<{?$:)'!B3͞4mɚT1zQ–hƓH A#" _r o+_e_Z%wܣomgC?0PV%;3sN1=;%R(px30]w(^[=5ũ1NTJTqODfrZ>?ӽhV&l慖w;‰G25%FAXl<&*CH/߲e3x3u+1Xv# cfZTg˖-`OEO+MIw,hW>3+-?}+2m k--$RqY%d<=" &"(H%PBrNJDBМb.5#bOU_ڷSUUh,S;W^@嵷i d' V>IBmaJvF={G9x@(GZTTuQ&mMdG,Κy'.'L Z^@F2P"/,dlk-gȟ0<b{^eUޡٜ#{q 3Wzܪmi~N9ۏ;_4ixdewY NdpzȷM.֠r2 ?B~z #;LDT %X 2_55[7R@+ 6R_sz%iYի_"Stk4~=wi힡[{]P|IG԰5htcNo߿"PTbԋ\2xe)1 Ο$~ %(%ZXUՏy r^/{=RL%%Z㱚(LY0qONm4OQ,?}ClVr=ǬHk:ZǦf@ ŖPϗ}k@.D(Fȑ>]+n~-^cSQfTY`cƮN_uAjo8nDZMH$U-gGCS MEtQ(DfW?3 N)m!d.,0fxY]ߴ~rw,w̮];rVk5k!?s\3}ahr h!m2{)uuq2{82"T=!.tb7WOPǵRw탚+:K剁 H]Q91=D!lInob U[Ɯ0qF|mmjmmQ5oz>CGƓIH[S9yay> L] TF.o`CӷM'z'Hj*'z(:w2cG `oa , k Ց9+;vGVR5b>mHRvh[P]_H}#UD> ȝda(h #߲>;G~jc[%O~ya W2RL%~TUqmM===i_yLɼ*Ru1ӷ{hU8\DI oa6\=D#\6k\ҼL +KFԏ$~SA8a&F:ﳴ=C|}\{@)y^:3s}ǴwAXR-42#nDώx+>: D(;a$CrӡځjbU=y27l<${rC8%Xni,VZ`z>/Z`O!Gk>F0˔.a0iљ2l9*[Q!ϽRFpyl-f_ ,I'фcV& X.b0g*loPr}8r{/j;_?/?963c9P"cjR ,V!Mgp6̰0N6 yx+1UT0&#˕BB"l qɠ6e@v^ǣy^F>_(I.}VVV߫X]eh [ Br,몋`Ñn2%6]RIL@vu h@S}dX10Ud %3AeYhBQ]}j/gsm'O9}K=9y,[UEc50tEi"Zɘ E.ZfLhl.e#5;"]&Irm!?Ha# Bb?9e@v倮9-{ڎb%nmLpm@ӱ!@7K)˿[L[ rfEFYx#5+5D1M 5[ƅ;"͢5f[b 9fV]R~ֶ&6#<$I OSR1@Q'k5Z[C$O]I :ۆScO?uuQu}2l-zLNy`eeRV`KUVډ5@LRȟZfn1kĬnfDd# . cr4˜,a[ZO$^rKwrZ[[!L);Y)RQ.h͎jh,Ե8j%;;W yݴx BP φ0/4]LfoE~1CzA3?1l:r`X-| ^ װC!O|&/L߼)؄DMډ LpiS6l0ICOFɳ>逶4dy{[eE+иzU8:q)Y% IaA(G\p]Ecd[\^RB&LF^1F>>D2xgd #s:P PZ>6nJe}w[{f~,`! _O4 ~vm&&kUOf81&d $םwnhxU}`y;R.ɒ36o˕/@^{~8Tf!#AF8h&"hT <ԡ.Y 66lf0螓,`K1@p q,7_.Kw͢1:$9q!HxB6_a9upӎ&0 iCB2J +l,fsc |Ex 4qHƛCSsHP$>x?ΆFD匊%otx1 0y RbZT6:Q>{= G@ `䝞oD5p, 8C:q8I 5iq/8.K*~l TT8F82F:bFfE^((Ć]\5.N>M3:G+ w0ajzg`s( ^ 0fC1D$P X]BïL %XҎ-c99Οirڱ%ܤٙCGPZpwYn߿LWQI6: Kg((RMYW&M<{wx7vEy9{ Aݚ}xb 9w$ "kƬ9a4oBoڸ"EpK':\Kf:JoJAS!zE~ϋ7<,7^j6\3 lH.c5' uӧQ)@!@RCC *FY & />Q`BEyOf//$1Iwp5_ S'X$dMZ 7IV3ꡌ'>2^s';g/?煇?rTh#bRXN5 󙼾~ G'1F0d0KbæCf#캝`n.)zCqBYdF"CAHa ! Ʋ;_3gcxpJeYv|>lZ=%YnU{ds}_?} Ie/U-}}} 67aщ_@5Rx6S|9ݔӦB? 2s:vn39xҼU׼ZhH{>)CќsԪ+Xz*7esZx `C \Ll2u0#rȐJ7t ԭ3|v2Wu$mDdmd}򳴨q66Fd mFѠ+&342f 7~Y텊L* ?ֱ`|wN7x~z,pw>ui )E󚮱xdfIAmnP&bnE:>WˤP^G0sbT\1,r!to>;Ύp4._Rֈ1X`Ygi1Г.: 8(,DdhJgE>Wm|~EBP OgvB;ۉ'kY[?O$5 .x}mrbb*ʊZDpJ̥֘8+4[rόaaX@xjyN5ZFC xiJBf065JⓇ{ĿH~lJR5TF AȚ41bE^ʼ{^#CCm7"Mvc2PPo0=ۗ]͍6F^KA4~l '&A^Y MF=yB$zrdf13XBI\&Zj):k`k'6~1t3Ř hO+A |~9 y4߬m(@ĕ[5;$6imj;^8s3eM ݮDcpIOkj_6~B\G7m.&5P 2b.늲$nu4#Rch'y쪹[nA’.M%xT"${ =6h[!-1(^Tc$3qęi;rgBA@贜򺭟)z:ly,?\,eN'bd5_}+jvC`'a#7jح_'#ffjvzƃh {udQg+ ͓/JVQwJcS3w`;^a0,1OH8LkQ}SV@q<i@9O+#91UڍRnJGTF S}$.S|o(i+~9_Uțh 4~l4>[k]>Guȣe`ft",[dm'~g@H~G"i!Cl A|b^fCP5Ӄufv{oKt`l AǥGC:L`S TyQpEf3]3rϿ+|, 2Xl 130k)xa4 6DD'Řes%b?hiٝהy)&k/ۿsZA56QѪkv[vlur`Ka0.Z`mZ 3G}"1iHQ9GaO]bh?B7}Yþ) )~,E~px C&] k~N#)ned?#S >˲nϹ=hVîj根<ˑ{] 2rI [̙ 1$8h\6!S&tot]c+ksui| e݇փx&LHpl(JHh_*`g!l\#5:ֵ#^pm0깹s}Sc_Wc2v b㛷}+ÑcZZe{`eFgZ'ӌeV*f֫]8pfWXlTD߻-Jnb 3=JI1-!m=t!@9\\ֶH:E4W#sY= c“~F-面5Q[N_J L}2`&qׅɾr@#q vLUVzO`z1bk=LUH HFYi+-]w;O:9xejo-Dz*xsi-c%ed1I oˮ1so[cm7)EUG߳g# Ywh\Wv̽4}F|G]XAPTjiQOaf5[$,"0.0xk 2j@}"շ B®_go>l}n5(L4)}230 ܜ̓KC XYW|!Z뮸.OܮFZ.fxjͿǯA]wCzwO}IH)u-9JsgE'C l=E톺FfP>J[6Ϳ900F3~Mد(iyS~!kܐ !C&#ak \R›)A @RZ8,PK ?p ̒O34t*5p:Ji0g/UiJϑ nwV̨\gx~Oè/*odQX۵4L=숯#Aڭ5J۰<Gg'j0XN>KvWU>a!i;~TswE`h_<oqFec[+MfcvLGG<\V0X[voܩKi e Lrny,:ufC!wgwϦ~Ws}g\p5RcNZtM ЄiߪK8X@eǍ s;MO}j|/`9\QC/ٹz[7e"dX5eu_qvuB')x>t>B=Oլ˯L]kwݿH]-A#³ 1x~/qL@|.S}NN4P#@@yM^p@G3+&V/8ԅl-k7Z(jLL7=|==/lf"5eJYJQp:O[no}i >%[dU Ezab*'^Umw̷Me?ګkC!ҭ E,>/scy\"Ii*FrjAYEWgaY+eP+zӧO[3}{ҟݪAh2T1t+x$w`?2Uo:y^xH%2aV~?fxo$hWh ;1ӧSFß`ƻ$% X ALoLgwݠ(gq6{:}W%`0&- P^?OZN ,3-1R};>_ɓSW=Y}zHW啢B R'ܳ1b9eO8^sj<3p:'Ȅfj* ^Nj`[['o:>,C+cnmL ޛo\,s_.ùHFB[,,xe{ +hc~g,0N)ubEY)s'GhK˝:: |WX;(/Wk4 "-=2]NvQҽ[oLO+hӇqmK̝ ,^[?ss4)n6ށ3n54a\ :0qܯS]w,g{k/|􃮞ArL/ mcO|2$\ybTXfl6c&+cM㛬8CޕZyr 3Mh9Ԩ%S\Y%%u5XhOڪ DAD^~Ú>ȁlbCrmRK~sߊotP=\4\B("a Ñ_-CV ~!m?|nvS=R3)}[W_rkqy|_ԗOm'̓˭\1c&E##; (\ЄxkΈk7 Q ݬu_u+w\ӛ@ϥ|XtNnzpg>2^͛ʶ1 XtݑQ@A9 olfs6-WN’ٗt9B`6Xކt`3:TqYSW04]'O~7Ojd5]ɡ|hi,[Z񎏷1yR]IZznK~bO|ZPB]L< E5Ceݞψch5avMP;6 ͶDj:Gs鴐Ōo d>ya) =Mk zYJ"*z{H;9daAcMm|SbcEŢVgugnwy;KfJ6c@r`r˯Ҽ :Sh|㮦_GFԮLƯEL0q''Y]Gt]Ak$p%Q/5^ZQI38B11F*Pi,銩a8+[~S_W%/ <D"(o#=2=F!81bj]Br= D'95RuYE-[w,ZA&ܶO#cW<cfD aw O~LYĉaUpMM])Yp5օW["˾?D%5 GLLX-Ku#9]O@෯ d{خ[S9' :NxEC2(S$]ibLӚ\-<{Y뙜`ߘzpek87 BCcaQ$3=A``y,z\]XQY_O57[?MTB MAQXUBsyՏ'V=wYHDfWYk`:{k}te7{_vRcɿWVxlN;q14^@}FI^+=s7'Ϻ Ȁ0E PtH|N96w䦉>#Q&8PsO0&8"r~\_Aa~)M|N],fnekJA,*vxOˣI%1xX^^teRHKgQWw O>)69ﮩ8gy. qiGYr"⟼=1x#K'Daf)8 ^'Zl,(s8Yf8#oUks1GvbsNbmY o6CYk3480!a螖F!!Er? _FF.fuOddK˺u^>B֨s -Obػ.`%"l/nPٰ<3Z]x2ÈC*t 6WВDW+ L[G&4Mb9ٰ;ֱ5W\2A28%jCLK0̂Bp*,Zh#,Oi%ͺ$UYaC "g=0/^*X7g ӻoZ6,\V3bMKVY8ŽCaEg[³و LJUqq" )A-c 6\cn$o#QW10Qˊ8LY}oK;^R5y=ZL4H 0#1 _p䘐]X (l}i b E==hmgPJY9f`&Gb3Sv1!!\3-^|B脖~D{bf3r?du#0Q*"AD,R8q3߀4J7;^[C9=aC k:~Ԛvv=d"}#)VD {qI,.vibYkYg5Ej+ w{kmfz"H־X,v68I1$0E *~QT?(Iq* ʆcljĖZ3־f˽{_wz$ʼnJ3ywι|g7ǿFDw]6܅G6xp.n47=^q8};2 HQ3 ),xEuaD Qg `({i∏"ݲe=aLQKzY )_3!LB5QTALW _IBqHv.i'W0crI8$|V YXi04U)D~fA'ΖJrjXrLTGąFZFnaErIMԺwW콟u؋X{?z0NPJ֖̈́Ww,FqVGqK_m#t2rr- 6\L8UY&rʝBjƎ#U4P%ŪƐn[)@,T¶DM[IӾCHF^ZB @܃GAm@uHE]Ԃd1DXjCA3;7O#={wu=$9J"wTVQKt"bQ*;ŨFՄ0Aa4 >+?Ms33k?z㙿)ΞYF6r@+BsRBCҙ2=n4ϻ*1Vk{ǧ$X˸NqX46%b* Ve$\(!0Y:&T^,'?G #H}D"Lmxx=pq4}v&qP@as'Lϸ+T/-}̟8 puZvdDΌ0^%#B0݌mu~itEeǣ-[5ͧ2qt7*y#XPn}[ `3o.:]5Rna[%֕bC(G1!X>*5!$ZIG~:KMy̙=`jޗ60v0'Lu#-/4 1Vԗ]^h.'s"zyBoLҮǧ6=dxhȗ 0F95ŵY+E\?muoo33` ay^:F0AI[-$"մ3] jhv<S{CyaNaBZY$YR'(&J`:C p&dQ^1$ię\M\L0ZGCk=b.+!nuDD e*pS,}tY!#̏ Zcy- j(yl}ǟ^qa{ʕnX&rfo޶`:Yp) {?pcׯ}&3gW+ Wv)A3igQ^YVdcZ 4=ﵧ@oѪUX-x's F ^~^BQ$m:5y$WNdI4qV`H|N>7n]9q6?t'&'AQ;N"A.\n ݪ1wxOuuZWZL]5֗y.㆙pR ZZ RpH!b1> }%)Zft&X &ҡsV7 źS.5(&N 4Mh;yzf^~vԼq}kL3JݪԷ=`w P̭2ip4*ԿuQ맨ћ>Fj/5+9pم]獞 n$Bը^8Bҿ6رcx){\I3F=y0PUQf.?pz>΃<}zرFO~m@{8]6W{uULѤA U{MS1yݾK7C+iq\P~l_gϾSO(` $͝8vsafh~z]>ҍ./W|mL+Q6[A'OAea"qLwoĿ}?Z4r%1=tC\(/K :{mw J,H=1"QgK)ӆB'؅+93OIgp p_`[ޢ"$ T]LkJj'+’fٔvJQB8;0,'SGZ2hxx&Yf#{i=H@GP?s0FMil(*X{jV hl|y _Zkq@Z.@nG|m3nq±_~?\YF T,$$7p;Jܦ7**j )h|zsӣu}߯=O`㯡\,,+Llk6ڟݍ[zA_N r:a.]_\zudՊM4OEbIOE4p^2C&HL.*OKZPuf^.zt|6\8^T =3yo+zg4+6Sj bUZJu"tSvi |$8 \bL&n|(\=~"8~j:{yq*jX(U>tqG~7-4!\~^ry ˍ6b͟%w"%չ)Y>IfdeJ˱Yv5'ecn9^;<jܫW:_2K=bqZ|`>K/'.1*hvZd $}[ʦ>(@J0D'H[8+sn%pfP 6#ig5jEXPСCn1M/|+}8V<|3=ًSEi–<a^yl&<]-/~&+Q %BHb*#6[I;)+[1WҚfb k?=D[;w3Zq$ n]),״KG2s Xeaf:9QMMmVyN) Al-@BfrrFonr{55YPJ'hyq=OVv=tfOuwL_MȈ30@HKa\,'%Vʘ&eUE :\8E 8!Q =_N0M(I;YYkőpq3ug)PNAb1Lgi6 9_(OZhWlB L¡ /i8 >c}a[_?+9sz_v wn!ۗ_z)5?Ϸ`9{YLmKr`S '-i4Ј0.#$ɠ 'ii%lt(9lWMg`EsjnᬦmKZ92go p`.`f >Bct[E}'c`\z筬]ɉcƉ`^}ƞΞ]u_u3BQ30 W$rp!D`փ.&lk~g] wXڲW}"S&bՉog%̴ S*}@ 0*'(n>drělU@VGia&q0h,Nbd2V?7yWQh, /Sķ r~wao}JVz?yxVg ] @5E+U@(Nc$^F0IhIw$QpZ`GNtrKʨ$GED9"!`dW81%MB \Y#C؞/sGsQ|IENDB`tests/native/wopi/index.fal000066400000000000000000000061541176363201700162730ustar00rootroot00000000000000#!/usr/bin/falcon -pcgi /***************************************************************************** WOPI testsuite. This script is configured and prepared so that it can be used under any WOPI front-end. It simulates a small web-based application loading applications. Read the README file for more informations on setup. *****************************************************************************/ // The defaults and the configuration are usually ok // but you may want to set your own: // wopi_tempDir: "/tmp" // shows the real page if "p" notin Request.gets page = "main" else page = Request.gets["p"] end // load pre-pages if fileType( "pages/pre-" + page + ".fal" ) == FileStat.NORMAL try pre_data = include( "pages/pre-" + page + ".fal" ) catch in error header("Error!") errorDocument( @"Sorry, required pre-requisite for required page \"$page\" cannot be loaded.", error ) footer() return end end header(page) // try to load it at this point. try include( "pages/" + page + ".ftd" ) catch in error errorDocument( @"Sorry, cannot load required page \"$page\"", error ) end footer(page) // End of main program. //---------------------------------------------------------------------------- // Internal utilities // function header( page ) >' Test page "' + page + ' "

    Falcon Web Oriented Programming Interface
    ' > '
    ' end function footer( page ) > '
    ' >'' end //---------------------------------------------------------------------------- // expose an utility function to help creating links to ourself: function makeLink( page ) return Request.location + "?p=" + page end // A simple utility function errorDocument( text, error ) > '

    Error!

    ' > text, '

    ' if error > "

    Error:

    ", error.toString(), "

    " end end // An utility used for simple checks on tests function testCheck( exp ) count = 0 > ' ' for k, v in exp v0 = v[0].describe() v1 = v[1].describe() >@' ' if v[0] == v[1] count++ >> "" else >> "" end > '' end >'
    FieldValueExpectedResult
    $k$v1$v0OKfail
    ' if count == exp.len() > '

    Test SUCCESS.

    ' else > '

    Test failed.

    ' end end // export the utility functions so pages can use them export makeLink, errorDocument, testCheck, pre_data tests/native/wopi/older_tests/000077500000000000000000000000001176363201700170215ustar00rootroot00000000000000tests/native/wopi/older_tests/cookie.ftd000066400000000000000000000011201176363201700207630ustar00rootroot00000000000000

    Cookie test

    You have called this script times. Click here to reset the count. tests/native/wopi/older_tests/fields.ftd000066400000000000000000000051211176363201700207650ustar00rootroot00000000000000

    This is a dinamic page created with Falcon!

    "Query is empty (no fields in GET)
    " else > "Get fields:
      " dispDict( Request.gets, "
    • " ) > "
    " end if Request.posts.len() == 0 > "Post is empty (no fields in POST)
    " else > "Post fields:
      " dispDict( Request.posts, "
    • " ) > "
    " end ?>

    Test field 1:
    Test field 2:
    Test field 3:
    "", param, "" end function makemeta( keywords, descr ) // this demonstrates escape nesting. // closing here falcon escaping ?> > '"' >> val formiddle: >> ', ' forlast: > '"' end ?>/> @"" end function makeheading( title ) printl( "

    ", title, "

    " ) > "Script complete path: ", scriptPath + "/" + scriptName end function dispDict( dict, itemPrefix ) for key, val in dict if itemPrefix: > itemPrefix > key, " = " localInspect( val ) > "
    \n" end end function localInspect( val ) select val case ArrayType for elem in val >> localInspect( elem ) forfirst: >> "[ " formiddle: >> ", " forlast: >> "]" end case DictionaryType for k,v in val >> localInspect( k ), "=>", localInspect( v ) forfirst: >> "[ " formiddle: >> ", " forlast: >> "]" end default >> val end end ?> tests/native/wopi/older_tests/fields2.ftd000066400000000000000000000051771176363201700210620ustar00rootroot00000000000000

    This is a dinamic page created with Falcon!

    This post sends also array values; the TestField key is sent as an array.

    "Query is empty (no fields in GET)
    " else > "Get fields:
      " dispDict( Request.gets, "
    • " ) > "
    " end if Request.posts.len() == 0 > "Post is empty (no fields in POST)
    " else > "Post fields:
      " dispDict( Request.posts, "
    • " ) > "
    " end ?>

    Test field 1:
    Test field 2:
    Test field 3:
    "", param, "" end function makemeta( keywords, descr ) // this demonstrates escape nesting. // closing here falcon escaping ?> > '"' >> val formiddle: >> ', ' forlast: > '"' end ?>/> @"" end function makeheading( title ) printl( "

    ", title, "

    " ) end function dispDict( dict, itemPrefix ) for key, val in dict if itemPrefix: > itemPrefix > key, " = " localInspect( val ) > "
    \n" end end function localInspect( val ) select val case ArrayType for elem in val localInspect( elem ) forfirst: >> "[ " formiddle: >> ", " forlast: >> "]" end case DictionaryType for k,v in val localInspect( k ) >> "=>" localInspect( v ) forfirst: >> "[ " formiddle: >> ", " forlast: >> "]" end default >> val end end ?> tests/native/wopi/older_tests/hello.ftd000066400000000000000000000001551176363201700206240ustar00rootroot00000000000000

    
    
    tests/native/wopi/older_tests/imgtest.fal000066400000000000000000000021401176363201700211560ustar00rootroot00000000000000// This test transmits an image as a jpg. // to make things simpler, we'll load the jpg from disk. // use the query field 'imgname' to retreive the desired image. imgname = Request.getField( "imgname", "testimg.jpg" ) // cache de default content type def_content_type = Reply.headers["Content-type"] try infile = InputStream( imgname ) // to output on the web server, use the stdOut outfile = stdOut() buffer = MemBuf( 1024 ) Reply.headers["Content-type"] = "image/jpg" // ok, transmit the file while not infile.eof() readin = infile.read( buffer ) outfile.write( buffer, readin ) end outfile.close() infile.close() catch IoError in error // we couldn't send the image... let's send an error. > dirCurrent() if not Reply.sent Reply.headers["Content-type"] = def_content_type > "

    Problem while loading the image

    " > @"

    Sorry, couldn't load the desired image in $imgname.\n" > "

    The system reported the following error:
    \n" > "

    ", error, "

    \n" end // otherwise there is nothin we can do. end tests/native/wopi/older_tests/inc_fal.fal000066400000000000000000000001021176363201700210710ustar00rootroot00000000000000// This is inc_fal.fal >> "

    The variable is: ", parent > "

    " tests/native/wopi/older_tests/inc_ftd.ftd000066400000000000000000000001701176363201700211240ustar00rootroot00000000000000

    Hello.
    My duty is that of showing you the parent variable:

    tests/native/wopi/older_tests/inclusor.ftd000066400000000000000000000004411176363201700213550ustar00rootroot00000000000000

    Inclusion test

    Here we include an FTD:



    And here, a standard falcon module:



    tests/native/wopi/older_tests/login-logout.ftd000066400000000000000000000050761176363201700221470ustar00rootroot00000000000000 Cookie test

    Cookie test

    Welcome, my master!

    You have been around times.

    Do you want to logout or to reload this page?

    ?

    Logout performed. Goodbye my master!

    Wrong userid/password. Please try again.

    Bad move.You're trying to force the system with unauthorized cookies.

    User:
    Password:
    tests/native/wopi/older_tests/login-sess.ftd000066400000000000000000000041651176363201700216110ustar00rootroot00000000000000 Cookie test

    Session test

    Welcome, my master!

    You have been around times.

    Do you want to logout or to reload this page?

    Logout performed. Goodbye my master!

    . Please try again.

    User:
    Password:
    tests/native/wopi/older_tests/receive_upload.ftd000066400000000000000000000026421176363201700225120ustar00rootroot00000000000000

    Request:

    
    
    "

    Open test:

    " stream = upld.open() data = stream.read( 1024 ) read_in = 0 for i in [0:data.len()] read_in += data[*i] end stream.close() > "

    Test complete; sum of first ", data.len(), " bytes was ", read_in case "req_read" > "

    Read test:

    " upld.read() data = upld.data read_in = 0 for i in [0: data.len() > 1024 ? 1024 : 0 ] read_in += data[i] // here data is a membuf end > "

    Test complete; sum of first ", data.len(), " bytes was ", read_in case "req_store" > "

    Store test -- using a temporary file to complete it

    " name = nil tempFile( $name ).close() upld.store( $name ) > "

    Stored to temporary file ", name // now try to read it. stream = InputStream( name ) data = stream.read( 1024 ) read_in = 0 for i in [0: data.len() ] read_in += data[*i] end stream.close() > "

    Test complete; sum of first ", data.len(), " bytes was ", read_in default if upld.data > "

    Data content:
    " > "

    "
             > strFromMemBuf( upld.data )
             > "
    " end > "

    Test complete" end ?> tests/native/wopi/older_tests/tempfile.ftd000066400000000000000000000004671176363201700213340ustar00rootroot00000000000000

    Temp file test

    writing:

    reading file

    Readed:

    Note: we're willfully leaving it open for the VM to dispose it. tests/native/wopi/older_tests/test.ftd000066400000000000000000000001321176363201700204730ustar00rootroot00000000000000 Ok!

    
    
    tests/native/wopi/older_tests/test.tpl000066400000000000000000000077031176363201700205300ustar00rootroot00000000000000

    Hello %template%!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!

    This is what %verb% when you use a template!



    Ok, now I am done, %template%!

    tests/native/wopi/older_tests/testimg.jpg000066400000000000000000000413571176363201700212110ustar00rootroot00000000000000JFIFHHC   C"  E!1AQ "2aq#RBbr3Sc$CD4'su F!1AQ"2aqB#Rbr$CSc %34s4 ?F ѣFH4hHIԉ1qIRֵܒ{ 2mvS(6F^+FߌAEGZ-SjjSvE2Ml ԖO|9!Ϡ>Z c S22I3S_l2#b)/БRH&R"b`Svʄ]o)JC S䆇^Q#QԽʨ -6eeS'g~l R [nmA{VS-Rwõ9 BXP8`䏇)ug߽GčGl]H&G$Ox|b6iqi J`\ Tz%TI_1BkK# Fsgo]–qUXořg]x^ʓȄv s9ì 8fϭrQ!4rniOB}pUY&NA6 w‰T}0nm)YIO2;Fo ~-*'n7!45H:%|$gO˿mhu*p8B7£0J6*iC0v#X4hѯb+hѣR$4hԉ5"AFp-g]WTç OH}$cʹJy"rLdJJ:e-F I'[ģQc#Vw*=FI=5q#Ɲ׸ȳ-ATQYR*P2HeJs5N|#UxLӚG%%rNyGR-P(xܚuzSq9~ڱZzua"ٺK"nnƗۍ+G=$N Vg[ĭkϽF#LF) CX8AGTOQ!.өԻv}1ܪ{e8K s |D̙̾ τCHg.Nbe7y&lHRI) wQPxyR-BHGu"6d-ߥU=R<.gyAJ88'8c{V1np8IJez䄸@@Gln"ϻ$SK|ȞrƬ0# JB4~KԱr߸ [kS"2Yj ^ Ԓ,B#4jvoRFNmɭ̊q|`z8 d뇍nwv~:eV+Z?(-$ԁG*VUe*Vm*m V›qJJz1ehS+Oe4ߍq}H+sk(pun۩ P e[nJލ4F4hԉ $ԉuPlvu\i8L<u$;|#7>ݼb4] x-SoTl $8yB@NO*Nmu6ʑ"uL#jT "S >i2#9N=ۺ\$ݲz,8dXXe@?xuCmd7Uu4+^uyHjvjxNDdӔvlI@PaqCl䐍zͫjUV_:ܛPQ<T)JL)Zrdg=s;$Γ]{>y:Tjwg]H)H)ܒaR^.%Ǚ'ujQO &:ݵU#VVO^T]nEgI=F+B˻odKO#|Pʔ{!#+Q NrHȓva *Z䞀06rI:5F#g= }^h%9G+֓B0GG8e{3eԜ@_O\\RO؍1xkن8.#W _Le)ΩkkP,|OMi{jRpFgh;ag`0E:@`a |g@S R=KPOP'[8VKOw{]jMKf.zUR)'6KdSy#Qv{=\{۸\V"U=RV۪?M 1EbJj/#7/apoN 4fG\1GSʭY=ƶ `}RM".k>KlO$wS~"G:9]`'C97, :N-6Șq.6vRHP>\ۖE[vj6vcu*c8ZEcUI`8XGkӿh RՊ{J@W*C8#Ȃ5{H_l4+Q%2ƔtU|1ec  j{5+\$Qx:UB $%8GR?M85dO;LqJem,!*H#B p5큅]1[4Ll8IZAII&Ă#aG+y.b,ݥ@.$OUe:*wC{kVR4Fn)p$a@pP|EX]ք[ $u*M^0ʕ g]TF@Z(fNuzܞk]?HOSkۯ,lΓJ$Hr3$ѾpMO|k{Z^j+vF2%Ö¹JzpFG9[yȜk21MJcԔx'[y]1.`s-\4bOLCcET7Ǘ⁚k5맼x=h;^Dr>V0hѣWHA7^gj7.D+  $||w s #b6l[e֔@6A rxKhՎfuVJ-G>z7&tܫ]3Ft5G+IQYZN-j=Ttͷڹj@JXV$8QYc!!)ymu⪪8))u2u6OBBS6Hirfmi o 44UYR1T>cղչwФk0>y:-4h5s ڜTd9;)rCPJB|ԥ($2FiKPB#!6JE;7&6^l得2²SirI,S#t!GU(l/;{ݨݽgSUU:]M>g(Vœ N)wʾ!+ő]μid3la4̤*qc>}9i3?eARrk)lGAW& 4h $4hԉ5"FՑv?p-enJ3ʬ- )Rr}h8 %).^9SJ w*IO?Fڡ*,iѤCqBuPF TЂ P^Zy`j5NX+npUpŒƉ<+X[e G;",Oeǵ=k]4{U%Q"#u$%nl/\ #IP)$l94<-#BߌSǀ=2u)u~_ꖵV 2haDaAB$EУX%=׀n4fV2J)DKRQR$I|qul6Rv'׿=sZ`)'R)FTuWXhѢDjW iCX=ݬ WZa8G%2 'ĒI.$~0xHm\3#rmZW(r ?JH'u6۷Tq"WTzgа)#\~\\ziaP]qe% Tx+-iy7/prJ+}^.ўT)ÍMsOP  aA싏-;a)F=vC~ /(yi. 'P;iKk{G¬WѤH&sSw5 ʳ,JiK$g/fl:cۺSqXU@Z͖2r|HBs)=TQLmOٳ[ SԞ2L= #naJRվ3cBH,`zxpP"*kzw6Uj]l/ԩ|$y]BAH#҄H8!GmםhO< JN|{T%TgZ<+n'H<TM5 Nw$$F6q=J7-KXWÏt$O^b-i\k_ɈU@DaºCn6HAR~2i&LKn|,ĹU0ޞ0جөђ*҂:%͒'|:f7-A5]\IH1"Z4jy1GbTGٓq%đGB UՃxK 4k 4jDF ѣFH4hѩ @?e[]FR"%FcUM'm'iq϶Mn ۩DK&*C2KתgSK?:,v1UJNd//opP"ǀ^p`W2:cFURTdG)ZeYѨ9nC)o̸Lע6Xe}TcoFXbSK,vW{mq8A4LS7"س|\I+<~7o/Z:zD$(ڈ-_\^wʝRT Xa͌kemQu!ݏ(#ϒ_9րI=u_>_3wyg?.%|q{FLe4m>W8aE_j,C0U=5D\jGJhqBCš!;2@i-yuЯ֮o? ;baYW\:gY}gVcI*Iҁ8P:-^_-z - ,\^%$n-ayl5jTٲH*@>-@ U\Tj7%:(R8uv`t7v=6٘ړ4)nɶ4-ա7+^v8^RGWj&"nʫG?l<N%`9ٕD48vAش wmWMqĄ ꨫ?/~$SibcW#b~ᬏT+~$`7ȤAʇ}~*:Дj%D]Ϸ|qY[)0d*ǞGL$)]ZFJ$~R}>#54'$5E$p=B&ko2‚J(v%&C^**T~:+[/Xo~E܍ g [jJ 'eŻ 51ۥQBB!$$dsnc+!a_"|է|Z,ցamě, `6HVԾl VeA 2m_|U{%wyOuVk3]WBBCN8)RB0+`LNXMGq\I:pl(bCٛ۠ ee6 X"\pF@ 4jDF մؑ×[(ӭd-  'W:5A[<{@hZԭ)Q_2]e FGEEL gC/9Ϯti~I椚8X7O+ fʽuUBѻmLWn2%u*m?cM#9| :k:x@ZlM;A;\Sttuo$6sVQP[l8 76_k!Wפ0Um f\t;ڞ.Drݷr2?'I褒Bt@lb a@6PꓡǘμF)@Z |'ȘFDZB񫴷cӮKuIs.A{2AWP1p!hʜ&V(ѭj7NrFyۭ#; G_FFbDҮYuˌH+hÄ+.61Q.A]`Mo4甓zRH?$vKW]d[C&*)s!'ݛIkkRp5`ܾH[a$"THԛÔWiWj5wfZ8VDHFsL5c;ijk޳)R! "mHGVE@s`aY6&j XyZNRAF7Lj;+bԚs.?@=N|_pԵs*'ѥc/NOOTd__?3<9_fL˿rK6luq"àn~$6O˪y!ҰF+yHml9cn-g8 eZ\)-CGx^mG=ƯL]2I)/_0*P^nљA )>RR| I[V^tj~o2G&rNR݂=HLCi4-'U 4!S MWBq=FqxUB(c#!`W/`1#hX7ݶ$ ȯNk N2+A NGAs/im>,Zf[ UT#Ō殺>#d3;jwғ/m0Vg(&}ԡZ90A~g~ *ށ`O }tHӇ*vHTU^ #Xf ;hε/ǴkWHp{<'q2d DsǴA /J4bIpE.4iK]P#'yt[#]~u_O.#eQ>C#+p#AbX`=93ʀzr䨒z&TmGzЪDUY%7~Gx ,nM7k4{/F yHH`u `_akߍ}q"J,}Wᮍ9щ@ctXp2`{{7]sNq!A0#2e ]=NNvۣ-ވ㛃QArCa+p9_ZNp9NzlV+؛vY\BH6Q)܀` 7&ԒEk7AJHq˅m{e FG~eÐ;&WPJVځJzk#@iT A&S* TUߘi\ϼ:f}ˀUW}RO~0rbBg ҄̽ͮ-Ya]4JjRQF"τ̶ :RRPBP'fUBtۧut(BOR8uγ=شiޑ.ūGnowv(JiAN2ִ #餞F\tYi6:1pyؾnJiU(Б6^ÇdBqM.׶Rqt a)=b9[wuf7T#ԋ 흉^& 2kLH`cyw]LT+q̅R ƎYZ> /@Lc=R Yy>E:M[HR q^RRb^xl -[$)sCWOE- ۊϸVJZI8K,48 9I:omߚ"Xԙ$/;|JKjrql{TqpPSym#6r+$Iy+Dw[G rD]TA_" 6 86ckE& LwK҇b#.nqMJ4ݻnyqj: (VINZS2).lD.o5mUA?*KQKI*HtH@>$4)QjxQO؏QFڍ+3+ꘪyX1lq!wS5I67,t7Q[e{- =sd5ZbO}c BFJ= \3#@mGf+ HԖ2VyYRd`t Nm7[?WV) M~9\ MJJ$\FܼIn=:ӆT͔g?2к}`a|H]RdX @O9/*P#kAGIh7\ދWXnMӯU=Q KYONyJHCc,h\&podN֫ߒ_( SL!)m #8ΘoeґĦrOy ui @2Cq"[ť<+ nmj鞊j)tqCk+/YUU$>;Q7x LyY:i%.ɰ]P)*,иC<@n;q۬=Pڢ5[SNXDvy JRH8)'nfoVmVPpo ْq*}M,8O{P-U6^u2ե\[ 'qMA쓧9.ոM- Z o %/,J{|-+ˠ Ma6ܓd~c2xRAɞZE=R; p;ukRDNo8oY K[*Rpˑ,~fz4[SZnQd6OקMuJQQA{mN[aʅ,!9rc84QV$y 9(r)'$&bD"pu'dj JqgS = Tu]aŴۉ%*I JG{{Qy~vlzErؕ%+> )Wĕ̌d 3;;;lwj({aw9IHСjRS!) צ13,:|\hhxT:_[N}ebIM0ZLJl?Ș>$+\V;C}C.$W#Ia~5Y. !B\8:0zZ^dԎtƮPzEKmVv#%&Ε$MiZIZqG JR:u8V(-*\Kv+j8Bq4iyj>J"UrhqB%%IQ̦Y6Nj[{y/Q M3ނGସdc=tO˼9g6 -k[Ccnp ,iiJe8ZA3A{Ckt7a3xV÷Kn^E;螀/:FJibjUԝG y/$~PJ2ų ޚ^Fȓ+Y15f&yꔑ-G-2}߶S-A^Hdr<3xst8* v݆9eW4|%/9ߢz)9Kjn 6\j -*PBVp+Y[H HXN]h7MZQG€cFU'9&\\m #Al$vN2I=ul}$X~^A*>Gf*ђn7XEV:@)&#drX͸HF~;LMJTnE\w pt-zz)cB+[V$fZi4-ZҜ%P:%c'HYs1<^/U;+بrHSϥ8tD`E=)lN(>~&<'I$J ۱oIi8f"GG7C^hyxUQ*$Ʒ9jVOӼ`djsVո@Vwd]_B)8i':+L«e7@R >Ԝ$h$38=dv P4}}UמS̴(' hWǢ6Y)4h 4hԉjŷZnTZݻQ"0A ~#8sܾ \:ї-_U4аR2G"P6(r:ڕIOQn,ٳ%F\&֗(:i ((uho|rY*i]Z?CLy?8^e֞MgPE4#UM6ӚÄ.kiKrZVUS֯U%n7|'qvNQJ[l-+J 9 9 UЂ'RgZݣ`BB$=FR.$Q.]H!!o)3\i8{S*|?"8_hzZ8BY몉X8:p;#iBHP8 ؏M@pl`܇(j{J>Qk߫4u l*8ju=FB7z-mTeA)$^r6^PKnZ;t.*EbP~ o( %2۾8ZO5sTr#ˇ!貚Z\iRB@ Χ)MȸQq:k ̻(Rvn$%VV@:ۜN썷[O_YpV"}{ j(AUN윗;We*ۮ2aJj"(@F)q%y?ՏLi.qWnZgJqU,t#403Z5/ce9o$_2/1+ؖٴwKWt!!1ܥX꛹VS-d/ˎŴj.t˥5WEhu!JmNJ%-Px*)T1S1IP-?D%?6^[l.m6Q&0O}4O"CAUb:Vm_^jݰVV ~ =9t @勺ᯀ[CkO=p/5c)J^pjH +O 3j6"Sp[keZscpVrJ}N|}-&;jQk&h9W>^؟[Feh7g3gUJ,y36G6u";~vSyʠјp] t AFE\l@)b YigHI=I$kq {*q-VjZ@S-nk?M!ѕaQpR~)FTqRa"ⷉgA_.ڑK m&yjԄhm[Z=麨iI[6ӓS=JeNG,1JPH/0Rw_;6]Q$IUj< gxw8wTꈶ'Ī֤%1–0ADh[K-J\j=~ 4:2J$TrI$I,VpSAJ~5^`.GyTrxT&Ar J>%JGԚm -. FI%)-4  kYQ*Q0hѣ_Q5"AFHs@Zep۳Z,J-O)' :@/>KlfNnVNRRRe|RI1鐼롍cLOM ƊI=<`W³ErڼmT,y,G-;hzHߘJ@i֚UPtrGL̇ +w5{3wZT޺sԻoVL,{r3ul_j>wWkIĥc^}< j7,@̹VۨJ:RC8ʴcȒE6;{6GG|Jwx=px/GF&LMhϡhZ9s$E%@(JO2H88Αàk| + UZW]9")rN1A% [;yxI?YLu+r΃_EU"~Cex<9=.e\p Z] t?F IƽGPunM{=ʸ6NPDNqȮTH@-}RYhZV `r1.RE?])vJИuu]-\6m\k*z[ rԟdYHyˮ4>/S؝ÎbOOWV|B~뒴#}6^qJ8Jzc-el Lr䅛 6I']gbUIM$+ MŁB<{e=v[_gLqmm/ŧZ+)L# 'P6ʧ= q#|kg@V>.oBu#6]T;? |hf17v%>mag̸::.\7d^ߨ=[Vk7LJ.34}*-sL`ꊭT)XȈAvRHĤ$ ? ڽau$|ۤP-+$yC~zpiç\BUvWƧrp o YqPB3fs'p.U(t\1V&M.DwQLN*WV =Kr*BK+kNK<J9#)Ӽ^ jrYV0=[0w\ueN&.eYۍDjQx d&2ұkZOQTFLUSi:6uQVf!<4_Ӡ!B}*LaQ"Rq 1eR (hѦ)Xh!^ZRɃF4hѩ?F ѣFH4hѩ | (=!5oBOXQU(J)GӊeGQ˪#]֣UWc.3;!agȁF+UBW5.8ϼ%6[銨i N+۠ 5\qr]k[WT&]ZJϨK.Gtc5[ώnJZzs\۳ iLw:4hoQ%R'^lAZ}-j?Ba U(J-Tc74gM3OSr>ZU]\uWrͧAxe @^3H ^Z4jjq۵',)ٺLM $h9sޙ{-Mu-Y3.ZRS'NmenS`#r%2R ?գF܁4h-uZ'x}qkv%Tti}UoQh av|EnqְEN}HPFm)ә`hѢ2+$bQ _>fj)'5G5"AFHhѣR$tests/native/wopi/older_tests/testtpl.ftd000066400000000000000000000002151176363201700212150ustar00rootroot00000000000000 "world", "verb" => "happens" ] ) ?> tests/native/wopi/older_tests/upload.ftd000066400000000000000000000012021176363201700207770ustar00rootroot00000000000000

    A:

    B:

    C:

    Just upload:
    Open Test:
    Read Test:
    Store Test:

    tests/native/wopi/pages/000077500000000000000000000000001176363201700155715ustar00rootroot00000000000000tests/native/wopi/pages/appdata.ftd000066400000000000000000000016221176363201700177030ustar00rootroot00000000000000

    Application data test

    This test checks for some application data to be correctly passed across different instances.

    The application data here will contain the total number of times that the page has been loaded and the last time, prior to this time, when it was loaded.

    This is the first time that this page is invoked (or very nearly the first time, if you're trying concurrent access).

    Try to reload the page, and you will see the data we talked about.

    0] ?>

    This page has been visited times.

    The last visit was on

    tests/native/wopi/pages/cookies.ftd000066400000000000000000000033451176363201700177310ustar00rootroot00000000000000

    Cookies test

    This test checks for cookie usage allwoing to perform an emulated login/logout sequence. Also, a cookie with a counter value is stored on the client to count for how many times this page has been loaded. Other than that, a cookie with arbitrary data is created (by default, using wide-string characters).

    The user is "master" and the password is "1234". You will need to click on the "reload" link below to see the international cookie.

    Welcome,

    You have been around times.

    You have the international cookie:

    Do you want to logout or to reload this page?

    Logout performed. Goodbye!

    Wrong userid/password. Please try again.

    Bad move.You're trying to force the system with unauthorized cookies.

    " method="POST"> User:
    Password:
    tests/native/wopi/pages/gets.ftd000066400000000000000000000025721176363201700172400ustar00rootroot00000000000000 [ ["one", "two", "three"], "alpha" in gets ? gets["alpha"] : nil], "beta" => ["xyz", "beta" in gets ? gets["beta"] : nil], "数" => ["百万円", "数" in gets ? gets["数"] : nil], "delta" => ["", "delta" in gets ? gets["delta"] : nil], "gamma" => ["30%", "gamma" in gets ? gets["gamma"] : nil] ] for k, v in gets if k notin exp exp[k] = [nil, v] end end count = 0 ?>

    GETS fields test

    Performing GETS test:

    To start the GET field test "gets", "delta" => "", "beta" => "xyz", "gamma" => "30%", "数" => "百万円" ] ) >> uri.uri + "&alpha[]=one&alpha[]=two&alpha[]=three" /*>> Request.uri + "?p=gets&delta=&beta=xyz&gamma=" + URI.encode("30%") + "&alpha[]=one&alpha[]=two&alpha[]=three&" + URI.encode( "数") + "=" + URI.encode("百万円")*/ ?>">click on this link

    tests/native/wopi/pages/main.ftd000066400000000000000000000015121176363201700172130ustar00rootroot00000000000000

    Available tests

    • Redirect test:
    • Get fields test:
    • Standard form processing test:
    • Simple upload processing test:
    • Complex upload processing test:
    • Cookies control test:
    • Session control test:
    • Session with explicit ID test:
    • Persistent data test:
    • Application wide data control:
    [Click here]' end ?>tests/native/wopi/pages/persistent.ftd000066400000000000000000000027661176363201700205030ustar00rootroot00000000000000

    Persistent data test

    Persistent data is data kept valid in the same process or thread where the same application is executed different times. This helps to minimize per-process initialization in those context where this makes sense (for example, connections towards databases that can be assigned to the calling process, instead of reopened each time a page is loaded).

    This test creates a counter that is updated when the same thread or process is re-eneterd from a new page. Reload the page to see it in action.

    Please, notice that the counter will be always re-initialized in CGI-based frameworks.

    Current process:
    Current thread:
    Stauts:

    To reset this process, ">click here.

    To reload this page, ">click here.

    tests/native/wopi/pages/pre-cookies.fal000066400000000000000000000027541176363201700205050ustar00rootroot00000000000000// get the mode mode = Request.getField( "mode", "" ) // read the user and password. user = Request.getField( "user", "" ) pwd = Request.getField( "pwd", "" ) switch mode case "login" // a very simple auth scheme: if user == "master" and pwd == "1234" // notice: cookie names == to form field names Reply.setCookie( "user", "master" ) Reply.setCookie( "pwd", "1234" ) Reply.setCookie( "times", 0 ) Reply.setCookie( "international", "国際クッキー" ) times_around = 0 else // clear the user variable so we know the user is invalid user = oob(nil) // oob to mark the failed login end case "logout" // clear our cookies // it's not very important to know if we're really logged in Reply.clearCookie( "user" ) Reply.clearCookie( "pwd" ) Reply.clearCookie( "times" ) Reply.clearCookie( "international" ) // also, clear the variable so the script knows we're not a user user = nil default // no mode? -- if we're logged in we must update the times counter. if user == "master" and pwd == "1234" // will throw if something goes wrong; and it's ok with us times_around = int(Request.cookies["times"]) Reply.setCookie( "times", ++times_around ) elif user != "" // AAARGH, a failed breackage attempt! user = oob("") // mark this situation end end return [user, times_around, mode] tests/native/wopi/pages/pre-redir_1.fal000066400000000000000000000004031176363201700203630ustar00rootroot00000000000000/***************************************************************************** WOPI testsuite. Redirection test -- pre-load part *****************************************************************************/ Reply.redirect( makeLink( "redir_2" ), 5 ) tests/native/wopi/pages/pre-sessions.fal000066400000000000000000000020431176363201700207060ustar00rootroot00000000000000// determine what we want to do. mode = Request.getField( "mode", "" ) switch mode case "login" // a very simple auth scheme: if Request.getField( "user", "" ) == "master" and Request.getField( "pwd", "" ) == "1234" // notice: cookie names == to form field names session = Request.getSession() session["user"] = "master" session["times"] = 0 session["international"] = "国際記録物" session["started"] = CurrentTime().toLongFormat() // All right. status = nil else status = "Login error" end case "logout" Request.closeSession() default // no mode? -- if we're logged in we must update the times counter. if Request.hasSession() try session = Request.getSession() catch WopiError in e // an error in reading the session means we're trying to cheat status = "Invalid or expired session ID: " + e.toString() end end end return [session, mode, status] tests/native/wopi/pages/pre-sessions_expl.fal000066400000000000000000000022161176363201700217400ustar00rootroot00000000000000// determine what we want to do. mode = Request.getField( "mode", "" ) switch mode case "login" // a very simple auth scheme: if Request.getField( "user", "" ) == "master" and Request.getField( "pwd", "" ) == "1234" // notice: cookie names == to form field names sid = toString(random()) session = Request.startSession(sid) session["user"] = "master" session["times"] = 0 session["international"] = "国際記録物" session["started"] = CurrentTime().toLongFormat() // All right. status = nil else status = "Login error" end case "logout" Request.closeSession( Request.gets["my_sid"] ) default // no mode? -- if we're logged in we must update the times counter. try if "my_sid" in Request.gets sid = Request.gets["my_sid"] session = Request.getSession(sid) end catch WopiError in e // an error in reading the session means we're trying to cheat status = "Invalid or expired session ID: " + e.toString() end end return [session, mode, status, sid] tests/native/wopi/pages/redir_1.ftd000066400000000000000000000002111176363201700176070ustar00rootroot00000000000000

    Redirection test

    Hello. You should be redirected ">here in 5 seconds from now.

    tests/native/wopi/pages/redir_2.ftd000066400000000000000000000001201176363201700176070ustar00rootroot00000000000000

    Redirection test - SUCCESS

    The redirection has been sucessful.

    tests/native/wopi/pages/sessions.ftd000066400000000000000000000033541176363201700201430ustar00rootroot00000000000000

    Sessions test

    This test checks for session usage allwoing to perform an emulated login/logout sequence. Also, two elements (one international string and a counter) are added to the session data. to be separately retreived.

    The user is "master" and the password is "1234".

    Welcome, !

    You have been around times.

    Session started at > now.toRFC2822() ?> (alive for > Format(".3").format( now.day * 14400 + now.hour * 3600 + now.minute * 60 + now.second + now.msec/1000) ?> seconds).

    You have the international session item:

    Do you want to logout or to reload this page?

    Logout performed. Goodbye!

    . Please try again.

    " method="POST"> User:
    Password:
    tests/native/wopi/pages/sessions_expl.ftd000066400000000000000000000034731176363201700211750ustar00rootroot00000000000000

    Explicitly named sessions test

    This test checks for session usage allwoing to perform an emulated login/logout sequence. Also, the session ID is not saved in cookies, but generated by this script, and propagated via simple GET filed in the my_sid variable.

    The user is "master" and the password is "1234".

    Welcome, !

    You have been around times.

    Session started at > now.toRFC2822() ?> (alive for > Format(".3").format( now.day * 14400 + now.hour * 3600 + now.minute * 60 + now.second + now.msec/1000) ?> seconds).

    You have the international session item:

    Do you want to logout or to reload this page?

    Logout performed. Goodbye!

    . Please try again.

    " method="POST"> User:
    Password:
    tests/native/wopi/pages/stdform.ftd000066400000000000000000000100121176363201700177400ustar00rootroot00000000000000 [ "John Doe", "Name" in posts ? posts["Name"] : nil], "Likes" => [ ["Reading", "Music"], "Likes" in posts ? posts["Likes"] : nil], "Occupation" => ["Student", "Occupation" in posts ? posts["Occupation"] : nil], "Gender" => ["M", "Gender" in posts ? posts["Gender"] : nil], "Modes" => [["one"=>"One", "two"=>"Two", "three"=>""], "Modes" in posts ? posts["Modes"] : nil], "入空" => ["国際値", "入空" in posts ? posts["入空"] : nil], "btn" => ["send", "btn" in posts ? posts["btn"] : nil] ] for k, v in posts if k notin exp exp[k] = [nil, v] end end ?>

    Standard form fields test

    For this test, you're required to enter some values in the fields. If the values matches those we suggest, then the test is considered "passed". You may try with different values and check them manually to try wider test ranges.

    Blank form:

    ] posts["入空"] = "国際値" ?>

    Test results

    Already filled form:

    Name (Exp. "John Doe"):" name="Name"/>
    Occupation (Exp. Student):
    Gender (Exp. "M"): >'checked="yes"'?>/> Male     >'checked="yes"'?>/> Female
    Likes (Exp. Reading + Music): >'checked="yes"'?>/> Sport     >'checked="yes"'?>/> Reading     >'checked="yes"'?>/> Music
    Modes (Exp. one="One", two="Two"): one: > posts["Modes"]["one"] ?>"/>     two: > posts["Modes"]["two"] ?>"/> three: > posts["Modes"]["three"] ?>"/>
    International (Exp. "国際値"):
    tests/native/wopi/pages/upload.ftd000066400000000000000000000031441176363201700175560ustar00rootroot00000000000000

    Simple upload test

    In this test, you're require to send a single file through the form. You should send this script (pages/upload.ftd) or the falcon-logo.png file stored in the imgs/ subdirectory. In this case, the result will be throughly checked to verify that everything went fine.

    You may send an arbitrary file as well; there won't be any formal check, but you may see if something went wrong through the summary that is drawn below.

    Error in upload:

    Received data:

    Filename:
    Mime type:
    Size:
    Interally stored:
    Validity check:

    Input form

    File:
    tests/native/wopi/pages/xupload.ftd000066400000000000000000000055361176363201700177550ustar00rootroot00000000000000

    Complex upload test

    In this test, you're require to send multiple fields file through the form.

    The default values ensure an automated check on the validity of the data. You may disable it using the given field. In case you want to use automated file upload check, the files to be sent are falcon-logo.png from the imgs/ subdir, and upload.ftd from the pages/ subdir.

    Received data:

    [ "yes", "yes" ], "Text" => ["Default text", "Text" in Request.posts ? Request.posts["Text"] : nil], "IText" => ["平準国際値", "IText" in Request.posts ? Request.posts["IText"] : nil], "FileA" => [ "falcon-logo.png", Request.posts["FileA"].filename ], "FileA size" => [ 24556, Request.posts["FileA"].size ], "FileB" => ["upload.ftd", Request.posts["FileB"].filename], "FileB size" => [ 1636, Request.posts["FileB"].size ] ] testCheck( exp ) else ?>
    Text:
    International Text:
    Filename A:
    Mime type A:
    Size A:
    Interally stored A:
    Filename B:
    Mime type B:
    Size B:
    Interally stored B:

    Input form

    File A:
    File B:
    Text field:
    Int. text field:
    Check validity: Yes    No
    tests/native/wopi/tests.css000066400000000000000000000013241176363201700163460ustar00rootroot00000000000000/* CSS For WOPI tests */ body { font-family: "Arial", "Sans"; font-style: "Italic"; border: 0; padding:0; } h1 { background: #E0FFE0; color: #001010; } div.header { background: 0xF0FFF0; border-bottom: solid 2px; padding-bottom: 5px; height: 135px; } div.middle { padding: 20px; margin:15px; clear: both; } div.footer { background: 0xF0FFF0; margin: 7px; padding-top: 20px; } p.footer { border-top: solid 1px; font-size: -1; } div.img { float: left; } div.headtext { vertical-align: middle; padding: 30px; margin-left:100px; font-size: 22pt; font-weight: bold; font-family: "Verdana", "sans"; font-style: italic; color: #B03030; } versioninfo.cmake000066400000000000000000000003561176363201700144330ustar00rootroot00000000000000set(FALCON_VERSION_MAJOR 0) set(FALCON_VERSION_MINOR 9) set(FALCON_VERSION_REVISION 6) set(FALCON_VERSION_PATCH 9) set(FALCON_VERSION_NAME "Chimera") set(FALCON_SONAME_VERSION 1) set(FALCON_SONAME_REVISION 22) set(FALCON_SONAME_AGE 2)